blob: 7588c346ee5e919cff7d884e49d7acd8a026fd9a [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Apache.Arrow.Types;
using Xunit;
namespace Apache.Arrow.Tests
{
public class ArrayBuilderTests
{
// TODO: Test various builder invariants (Append, AppendRange, Clear, Resize, Reserve, etc)
#if NET5_0_OR_GREATER
[Fact]
public void PrimitiveArrayBuildersProduceExpectedArray()
{
Test<sbyte, Int8Array, Int8Array.Builder>();
Test<short, Int16Array, Int16Array.Builder>();
Test<int, Int32Array, Int32Array.Builder>();
Test<long, Int64Array, Int64Array.Builder>();
Test<byte, UInt8Array, UInt8Array.Builder>();
Test<ushort, UInt16Array, UInt16Array.Builder>();
Test<uint, UInt32Array, UInt32Array.Builder>();
Test<ulong, UInt64Array, UInt64Array.Builder>();
Test<Half, HalfFloatArray, HalfFloatArray.Builder>();
Test<float, FloatArray, FloatArray.Builder>();
Test<double, DoubleArray, DoubleArray.Builder>();
TestArrayBuilder<Time32Array, Time32Array.Builder>(x => x.Append(10).Append(20).Append(30));
TestArrayBuilder<Time64Array, Time64Array.Builder>(x => x.Append(10).Append(20).Append(30));
static void Test<T, TArray, TBuilder>()
where T : struct, INumber<T>
where TArray : PrimitiveArray<T>
where TBuilder : PrimitiveArrayBuilder<T, TArray, TBuilder>, new() =>
TestArrayBuilder<TArray, TBuilder>(x => x.Append(T.CreateChecked(10)).Append(T.CreateChecked(20)).Append(T.CreateChecked(30)));
}
[Fact]
public void PrimitiveArrayBuildersProduceExpectedArrayWithNulls()
{
Test<sbyte, Int8Array, Int8Array.Builder>();
Test<short, Int16Array, Int16Array.Builder>();
Test<int, Int32Array, Int32Array.Builder>();
Test<long, Int64Array, Int64Array.Builder>();
Test<byte, UInt8Array, UInt8Array.Builder>();
Test<ushort, UInt16Array, UInt16Array.Builder>();
Test<uint, UInt32Array, UInt32Array.Builder>();
Test<ulong, UInt64Array, UInt64Array.Builder>();
Test<Half, HalfFloatArray, HalfFloatArray.Builder>();
Test<float, FloatArray, FloatArray.Builder>();
Test<double, DoubleArray, DoubleArray.Builder>();
TestArrayBuilder<Time32Array, Time32Array.Builder>(x => x.Append(123).AppendNull().AppendNull().Append(127), 4, 2, 0x9);
TestArrayBuilder<Time64Array, Time64Array.Builder>(x => x.Append(123).AppendNull().AppendNull().Append(127), 4, 2, 0x9);
static void Test<T, TArray, TBuilder>()
where T : struct, INumber<T>
where TArray : PrimitiveArray<T>
where TBuilder : PrimitiveArrayBuilder<T, TArray, TBuilder>, new() =>
TestArrayBuilder<TArray, TBuilder>(x => x.Append(T.CreateChecked(123)).AppendNull().AppendNull().Append(T.CreateChecked(127)), 4, 2, 0x09);
}
#endif
[Fact]
public void BooleanArrayBuilderProducersExpectedArray()
{
TestArrayBuilder<BooleanArray, BooleanArray.Builder>(x => x.Append(true).Append(false).Append(true));
TestArrayBuilder<BooleanArray, BooleanArray.Builder>(x => x.Append(true).AppendNull().Append(false).Append(true), 4, 1, 0x0D);
}
[Fact]
public void StringArrayBuilderHandlesNullsAndEmptyStrings()
{
var stringArray = TestArrayBuilder<StringArray, StringArray.Builder>(x => x.Append("123").Append(null).AppendNull().Append(string.Empty), 4, 2, 0x09);
Assert.Equal("123", stringArray.GetString(0));
Assert.Null(stringArray.GetString(1));
Assert.Null(stringArray.GetString(2));
Assert.Equal(string.Empty, stringArray.GetString(3));
}
[Fact]
public void ListArrayBuilder()
{
var listBuilder = new ListArray.Builder(StringType.Default);
var valueBuilder = listBuilder.ValueBuilder as StringArray.Builder;
Assert.NotNull(valueBuilder);
listBuilder.Append();
valueBuilder.Append("1");
listBuilder.AppendNull();
listBuilder.Append();
valueBuilder.Append("22").Append("33");
listBuilder.Append();
valueBuilder.Append("444").AppendNull().Append("555").Append("666");
var list = listBuilder.Build();
Assert.Equal(
new List<string> { "1" },
ConvertStringArrayToList(list.GetSlicedValues(0) as StringArray));
Assert.Null(list.GetSlicedValues(1));
Assert.Equal(
new List<string> { "22", "33" },
ConvertStringArrayToList(list.GetSlicedValues(2) as StringArray));
Assert.Equal(
new List<string> { "444", null, "555", "666" },
ConvertStringArrayToList(list.GetSlicedValues(3) as StringArray));
Assert.Throws<ArgumentOutOfRangeException>(() => list.GetValueLength(-1));
Assert.Throws<ArgumentOutOfRangeException>(() => list.GetValueLength(4));
listBuilder.Resize(2);
var truncatedList = listBuilder.Build();
Assert.Equal(
new List<string> { "22", "33", "444", null, "555", "666" },
ConvertStringArrayToList(truncatedList.GetSlicedValues(2) as StringArray));
Assert.Throws<ArgumentOutOfRangeException>(() => truncatedList.GetSlicedValues(-1));
Assert.Throws<ArgumentOutOfRangeException>(() => truncatedList.GetSlicedValues(3));
listBuilder.Clear();
var emptyList = listBuilder.Build();
Assert.Equal(0, emptyList.Length);
List<string> ConvertStringArrayToList(StringArray array)
{
var length = array.Length;
var resultList = new List<string>(length);
for (var index = 0; index < length; index++)
{
resultList.Add(array.GetString(index));
}
return resultList;
}
}
[Fact]
public void ListArrayBuilderValidityBuffer()
{
ListArray listArray = new ListArray.Builder(Int64Type.Default).Append().AppendNull().Build();
Assert.False(listArray.IsValid(2));
}
[Fact]
public void NestedListArrayBuilder()
{
var childListType = new ListType(Int64Type.Default);
var parentListBuilder = new ListArray.Builder((IArrowType)childListType);
var childListBuilder = parentListBuilder.ValueBuilder as ListArray.Builder;
Assert.NotNull(childListBuilder);
var valueBuilder = childListBuilder.ValueBuilder as Int64Array.Builder;
Assert.NotNull(valueBuilder);
parentListBuilder.Append();
childListBuilder.Append();
valueBuilder.Append(1);
childListBuilder.Append();
valueBuilder.Append(2).Append(3);
parentListBuilder.Append();
childListBuilder.Append();
valueBuilder.Append(4).Append(5).Append(6).Append(7);
parentListBuilder.Append();
childListBuilder.Append();
valueBuilder.Append(8).Append(9).Append(10).Append(11).Append(12);
var parentList = parentListBuilder.Build();
var childList1 = (ListArray)parentList.GetSlicedValues(0);
var childList2 = (ListArray)parentList.GetSlicedValues(1);
var childList3 = (ListArray)parentList.GetSlicedValues(2);
Assert.Equal(2, childList1.Length);
Assert.Equal(1, childList2.Length);
Assert.Equal(1, childList3.Length);
Assert.Equal(
new List<long?> { 1 },
((Int64Array)childList1.GetSlicedValues(0)).ToList());
Assert.Equal(
new List<long?> { 2, 3 },
((Int64Array)childList1.GetSlicedValues(1)).ToList());
Assert.Equal(
new List<long?> { 4, 5, 6, 7 },
((Int64Array)childList2.GetSlicedValues(0)).ToList());
Assert.Equal(
new List<long?> { 8, 9, 10, 11, 12 },
((Int64Array)childList3.GetSlicedValues(0)).ToList());
}
[Fact]
public void FixedSizeListArrayBuilder()
{
var listBuilder = new FixedSizeListArray.Builder(StringType.Default, 4);
var valueBuilder = listBuilder.ValueBuilder as StringArray.Builder;
Assert.NotNull(valueBuilder);
listBuilder.Append();
Assert.Throws<ArgumentOutOfRangeException>(listBuilder.Append);
valueBuilder.Append("1");
valueBuilder.Append("2");
valueBuilder.Append("3");
valueBuilder.Append("4");
listBuilder.AppendNull();
listBuilder.Append();
valueBuilder.Append("5").Append("6").Append("7").Append("8");
listBuilder.Append();
valueBuilder.Append("444").AppendNull().Append("555").Append("666");
var list = listBuilder.Build();
Assert.Equal(
new List<string> { "1", "2", "3", "4" },
ConvertStringArrayToList(list.GetSlicedValues(0) as StringArray));
Assert.Null(list.GetSlicedValues(1));
Assert.Equal(
new List<string> { "5", "6", "7", "8" },
ConvertStringArrayToList(list.GetSlicedValues(2) as StringArray));
Assert.Equal(
new List<string> { "444", null, "555", "666" },
ConvertStringArrayToList(list.GetSlicedValues(3) as StringArray));
Assert.Throws<ArgumentOutOfRangeException>(() => list.GetSlicedValues(-1));
Assert.Throws<ArgumentOutOfRangeException>(() => list.GetSlicedValues(4));
listBuilder.Resize(3);
var truncatedList = listBuilder.Build();
Assert.Equal(
new List<string> { "5", "6", "7", "8" },
ConvertStringArrayToList(truncatedList.GetSlicedValues(2) as StringArray));
Assert.Throws<ArgumentOutOfRangeException>(() => truncatedList.GetSlicedValues(-1));
Assert.Throws<ArgumentOutOfRangeException>(() => truncatedList.GetSlicedValues(3));
listBuilder.Clear();
var emptyList = listBuilder.Build();
Assert.Equal(0, emptyList.Length);
List<string> ConvertStringArrayToList(StringArray array)
{
var length = array.Length;
var resultList = new List<string>(length);
for (var index = 0; index < length; index++)
{
resultList.Add(array.GetString(index));
}
return resultList;
}
}
[Fact]
public void FixedSizeListArrayBuilderValidityBuffer()
{
FixedSizeListArray.Builder builder = new FixedSizeListArray.Builder(Int64Type.Default, 2).Append();
((Int64Array.Builder)builder.ValueBuilder).Append(1).Append(2);
FixedSizeListArray listArray = builder.AppendNull().Build();
Assert.False(listArray.IsValid(2));
}
[Fact]
public void NestedFixedSizeListArrayBuilder()
{
var childListType = new FixedSizeListType(Int64Type.Default, 2);
var parentListBuilder = new FixedSizeListArray.Builder(childListType, 3);
var childListBuilder = parentListBuilder.ValueBuilder as FixedSizeListArray.Builder;
Assert.NotNull(childListBuilder);
var valueBuilder = childListBuilder.ValueBuilder as Int64Array.Builder;
Assert.NotNull(valueBuilder);
parentListBuilder.Append();
childListBuilder.Append();
valueBuilder.Append(1);
valueBuilder.Append(2);
childListBuilder.Append();
valueBuilder.Append(3).Append(4);
childListBuilder.AppendNull();
parentListBuilder.Append();
childListBuilder.Append();
valueBuilder.Append(4).Append(5);
childListBuilder.Append();
valueBuilder.Append(6).Append(7);
childListBuilder.Append();
valueBuilder.Append(8).Append(9);
parentListBuilder.Append();
childListBuilder.Append();
valueBuilder.Append(10).Append(11);
childListBuilder.AppendNull();
childListBuilder.Append();
valueBuilder.Append(12).AppendNull();
var parentList = parentListBuilder.Build();
var childList1 = (FixedSizeListArray)parentList.GetSlicedValues(0);
var childList2 = (FixedSizeListArray)parentList.GetSlicedValues(1);
var childList3 = (FixedSizeListArray)parentList.GetSlicedValues(2);
Assert.Equal(3, childList1.Length);
Assert.Equal(3, childList2.Length);
Assert.Equal(3, childList3.Length);
Assert.Equal(
new List<long?> { 1, 2 },
((Int64Array)childList1.GetSlicedValues(0)).ToList());
Assert.Equal(
new List<long?> { 3, 4 },
((Int64Array)childList1.GetSlicedValues(1)).ToList());
Assert.Equal(
new List<long?> { 4, 5 },
((Int64Array)childList2.GetSlicedValues(0)).ToList());
Assert.Equal(
new List<long?> { 12, null },
((Int64Array)childList3.GetSlicedValues(2)).ToList(includeNulls: true));
}
public class TimestampArrayBuilder
{
[Fact]
public void ProducesExpectedArray()
{
var now = DateTimeOffset.UtcNow.ToLocalTime();
var timestampType = new TimestampType(TimeUnit.Nanosecond, TimeZoneInfo.Local);
var array = new TimestampArray.Builder(timestampType)
.Append(now)
.Build();
Assert.Equal(1, array.Length);
var value = array.GetTimestamp(0);
Assert.NotNull(value);
Assert.Equal(now, value.Value);
timestampType = new TimestampType(TimeUnit.Microsecond, TimeZoneInfo.Local);
array = new TimestampArray.Builder(timestampType)
.Append(now)
.Build();
Assert.Equal(1, array.Length);
value = array.GetTimestamp(0);
Assert.NotNull(value);
Assert.Equal(now.Truncate(TimeSpan.FromTicks(10)), value.Value);
timestampType = new TimestampType(TimeUnit.Millisecond, TimeZoneInfo.Local);
array = new TimestampArray.Builder(timestampType)
.Append(now)
.Build();
Assert.Equal(1, array.Length);
value = array.GetTimestamp(0);
Assert.NotNull(value);
Assert.Equal(now.Truncate(TimeSpan.FromTicks(TimeSpan.TicksPerMillisecond)), value.Value);
}
}
public class Time32ArrayBuilder
{
[Fact]
public void ProducesExpectedArray()
{
var time32Type = new Time32Type(TimeUnit.Second);
var array = new Time32Array.Builder(time32Type)
.Append(1)
.Build();
Assert.Equal(1, array.Length);
var valueSeconds = array.GetSeconds(0);
Assert.NotNull(valueSeconds);
Assert.Equal(1, valueSeconds.Value);
var valueMilliSeconds = array.GetMilliSeconds(0);
Assert.NotNull(valueMilliSeconds);
Assert.Equal(1_000, valueMilliSeconds.Value);
time32Type = new Time32Type(TimeUnit.Millisecond);
array = new Time32Array.Builder(time32Type)
.Append(1_000)
.Build();
Assert.Equal(1, array.Length);
valueSeconds = array.GetSeconds(0);
Assert.NotNull(valueSeconds);
Assert.Equal(1, valueSeconds.Value);
valueMilliSeconds = array.GetMilliSeconds(0);
Assert.NotNull(valueMilliSeconds);
Assert.Equal(1_000, valueMilliSeconds.Value);
}
}
public class Time64ArrayBuilder
{
[Fact]
public void ProducesExpectedArray()
{
var time64Type = new Time64Type(TimeUnit.Microsecond);
var array = new Time64Array.Builder(time64Type)
.Append(1_000_000)
.Build();
Assert.Equal(1, array.Length);
var valueMicroSeconds = array.GetMicroSeconds(0);
Assert.NotNull(valueMicroSeconds);
Assert.Equal(1_000_000, valueMicroSeconds.Value);
var valueNanoSeconds = array.GetNanoSeconds(0);
Assert.NotNull(valueNanoSeconds);
Assert.Equal(1_000_000_000, valueNanoSeconds.Value);
time64Type = new Time64Type(TimeUnit.Nanosecond);
array = new Time64Array.Builder(time64Type)
.Append(1_000_000_000)
.Build();
Assert.Equal(1, array.Length);
valueMicroSeconds = array.GetMicroSeconds(0);
Assert.NotNull(valueMicroSeconds);
Assert.Equal(1_000_000, valueMicroSeconds.Value);
valueNanoSeconds = array.GetNanoSeconds(0);
Assert.NotNull(valueNanoSeconds);
Assert.Equal(1_000_000_000, valueNanoSeconds.Value);
}
}
private static TArray TestArrayBuilder<TArray, TArrayBuilder>(Action<TArrayBuilder> action, int expectedLength = 3, int expectedNullCount = 0, int expectedNulls = 0)
where TArray : IArrowArray
where TArrayBuilder : IArrowArrayBuilder<TArray>, new()
{
var builder = new TArrayBuilder();
action(builder);
var array = builder.Build(default);
Assert.IsAssignableFrom<TArray>(array);
Assert.NotNull(array);
Assert.Equal(expectedLength, array.Length);
Assert.Equal(expectedNullCount, array.NullCount);
if (expectedNulls != 0)
{
Assert.True(array.Data.Buffers[0].Span.Slice(0, 1).SequenceEqual(new ReadOnlySpan<byte>(BitConverter.GetBytes(expectedNulls).Take(1).ToArray())));
}
return array;
}
}
}