|  | // 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 Apache.Arrow.Memory; | 
|  | using Xunit; | 
|  |  | 
|  | namespace Apache.Arrow.Tests | 
|  | { | 
|  | public class BinaryArrayBuilderTests | 
|  | { | 
|  | private static readonly MemoryAllocator _allocator = new NativeMemoryAllocator(); | 
|  |  | 
|  | // Various example byte arrays for use in testing. | 
|  | private static readonly byte[] _exampleNull = null; | 
|  | private static readonly byte[] _exampleEmpty = { }; | 
|  | private static readonly byte[] _exampleNonEmpty1 = { 10, 20, 30, 40 }; | 
|  | private static readonly byte[] _exampleNonEmpty2 = { 50, 60, 70, 80 }; | 
|  | private static readonly byte[] _exampleNonEmpty3 = { 90 }; | 
|  |  | 
|  | // Base set of single bytes that may be used to append to a builder in testing. | 
|  | private static readonly byte[] _singleBytesToAppend = { 0, 123, 127, 255 }; | 
|  |  | 
|  | // Base set of byte arrays that may be used to append to a builder in testing. | 
|  | private static readonly byte[][] _byteArraysToAppend = | 
|  | { | 
|  | _exampleNull, | 
|  | _exampleEmpty, | 
|  | _exampleNonEmpty2, | 
|  | _exampleNonEmpty3, | 
|  | }; | 
|  |  | 
|  | // Base set of multiple byte arrays that may be used to append to a builder in testing. | 
|  | private static readonly byte[][][] _byteArrayArraysToAppend = | 
|  | { | 
|  | new byte[][] { }, | 
|  | new[] { _exampleNull }, | 
|  | new[] { _exampleEmpty }, | 
|  | new[] { _exampleNonEmpty2 }, | 
|  | new[] { _exampleNonEmpty2, _exampleNonEmpty3 }, | 
|  | new[] { _exampleNonEmpty2, _exampleEmpty, _exampleNull }, | 
|  | }; | 
|  |  | 
|  | // Base set of byte arrays that can be used as "initial contents" of any builder under test. | 
|  | private static readonly byte[][][] _initialContentsSet = | 
|  | { | 
|  | new byte[][] { }, | 
|  | new[] { _exampleNull }, | 
|  | new[] { _exampleEmpty }, | 
|  | new[] { _exampleNonEmpty1 }, | 
|  | new[] { _exampleNonEmpty1, _exampleNonEmpty3 }, | 
|  | new[] { _exampleNonEmpty1, _exampleEmpty, _exampleNull }, | 
|  | }; | 
|  |  | 
|  | public class Append | 
|  | { | 
|  | public static IEnumerable<object[]> _appendSingleByteTestData = | 
|  | from initialContents in _initialContentsSet | 
|  | from singleByte in _singleBytesToAppend | 
|  | select new object[] { initialContents, singleByte }; | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_appendSingleByteTestData))] | 
|  | public void AppendSingleByte(byte[][] initialContents, byte singleByte) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | if (initialContents.Length > 0) | 
|  | builder.AppendRange(initialContents); | 
|  | int initialLength = builder.Length; | 
|  | int expectedLength = initialLength + 1; | 
|  | var expectedArrayContents = initialContents.Append(new[] { singleByte }); | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.Append(singleByte); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(expectedLength, builder.Length); | 
|  | var actualArray = builder.Build(_allocator); | 
|  | AssertArrayContents(expectedArrayContents, actualArray); | 
|  | } | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_appendSingleByteTestData))] | 
|  | public void AppendSingleByteAfterClear(byte[][] initialContents, byte singleByte) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | if (initialContents.Length > 0) | 
|  | builder.AppendRange(initialContents); | 
|  | builder.Clear(); | 
|  | var expectedArrayContents = new[] { new[] { singleByte } }; | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.Append(singleByte); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(1, builder.Length); | 
|  | var actualArray = builder.Build(_allocator); | 
|  | AssertArrayContents(expectedArrayContents, actualArray); | 
|  | } | 
|  |  | 
|  | public static readonly IEnumerable<object[]> _appendNullTestData = | 
|  | from initialContents in _initialContentsSet | 
|  | select new object[] { initialContents }; | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_appendNullTestData))] | 
|  | public void AppendNull(byte[][] initialContents) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | if (initialContents.Length > 0) | 
|  | builder.AppendRange(initialContents); | 
|  | int initialLength = builder.Length; | 
|  | int expectedLength = initialLength + 1; | 
|  | var expectedArrayContents = initialContents.Append(null); | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.AppendNull(); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(expectedLength, builder.Length); | 
|  | var actualArray = builder.Build(_allocator); | 
|  | AssertArrayContents(expectedArrayContents, actualArray); | 
|  | } | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_appendNullTestData))] | 
|  | public void AppendNullAfterClear(byte[][] initialContents) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | if (initialContents.Length > 0) | 
|  | builder.AppendRange(initialContents); | 
|  | builder.Clear(); | 
|  | var expectedArrayContents = new byte[][] { null }; | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.AppendNull(); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(1, builder.Length); | 
|  | var actualArray = builder.Build(_allocator); | 
|  | AssertArrayContents(expectedArrayContents, actualArray); | 
|  | } | 
|  |  | 
|  | public static readonly IEnumerable<object[]> _appendNonNullByteArrayTestData = | 
|  | from initialContents in _initialContentsSet | 
|  | from bytes in _byteArraysToAppend | 
|  | where bytes != null | 
|  | select new object[] { initialContents, bytes }; | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_appendNonNullByteArrayTestData))] | 
|  | public void AppendReadOnlySpan(byte[][] initialContents, byte[] bytes) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | if (initialContents.Length > 0) | 
|  | builder.AppendRange(initialContents); | 
|  | int initialLength = builder.Length; | 
|  | var span = (ReadOnlySpan<byte>)bytes; | 
|  | int expectedLength = initialLength + 1; | 
|  | var expectedArrayContents = initialContents.Append(bytes); | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.Append(span); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(expectedLength, builder.Length); | 
|  | var actualArray = builder.Build(_allocator); | 
|  | AssertArrayContents(expectedArrayContents, actualArray); | 
|  | } | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_appendNonNullByteArrayTestData))] | 
|  | public void AppendReadOnlySpanAfterClear(byte[][] initialContents, byte[] bytes) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | if (initialContents.Length > 0) | 
|  | builder.AppendRange(initialContents); | 
|  | builder.Clear(); | 
|  | var span = (ReadOnlySpan<byte>)bytes; | 
|  | var expectedArrayContents = new[] { bytes }; | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.Append(span); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(1, builder.Length); | 
|  | var actualArray = builder.Build(_allocator); | 
|  | AssertArrayContents(expectedArrayContents, actualArray); | 
|  | } | 
|  |  | 
|  | public static readonly IEnumerable<object[]> _appendByteArrayTestData = | 
|  | from initialContents in _initialContentsSet | 
|  | from bytes in _byteArraysToAppend | 
|  | select new object[] { initialContents, bytes }; | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_appendByteArrayTestData))] | 
|  | public void AppendEnumerable(byte[][] initialContents, byte[] bytes) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | if (initialContents.Length > 0) | 
|  | builder.AppendRange(initialContents); | 
|  | int initialLength = builder.Length; | 
|  | int expectedLength = initialLength + 1; | 
|  | var enumerable = (IEnumerable<byte>)bytes; | 
|  | var expectedArrayContents = initialContents.Append(bytes); | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.Append(enumerable); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(expectedLength, builder.Length); | 
|  | var actualArray = builder.Build(_allocator); | 
|  | AssertArrayContents(expectedArrayContents, actualArray); | 
|  | } | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_appendByteArrayTestData))] | 
|  | public void AppendEnumerableAfterClear(byte[][] initialContents, byte[] bytes) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | if (initialContents.Length > 0) | 
|  | builder.AppendRange(initialContents); | 
|  | builder.Clear(); | 
|  | var enumerable = (IEnumerable<byte>)bytes; | 
|  | var expectedArrayContents = new[] { bytes }; | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.Append(enumerable); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(1, builder.Length); | 
|  | var actualArray = builder.Build(_allocator); | 
|  | AssertArrayContents(expectedArrayContents, actualArray); | 
|  | } | 
|  | } | 
|  |  | 
|  | public class AppendRange | 
|  | { | 
|  | public static readonly IEnumerable<object[]> _appendRangeSingleBytesTestData = | 
|  | from initialContents in _initialContentsSet | 
|  | select new object[] { initialContents, _singleBytesToAppend }; | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_appendRangeSingleBytesTestData))] | 
|  | public void AppendRangeSingleBytes(byte[][] initialContents, byte[] singleBytes) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | if (initialContents.Length > 0) | 
|  | builder.AppendRange(initialContents); | 
|  | int initialLength = builder.Length; | 
|  | int expectedNewLength = initialLength + singleBytes.Length; | 
|  | var expectedArrayContents = initialContents.Concat(singleBytes.Select(b => new[] { b })); | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.AppendRange(singleBytes); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(expectedNewLength, builder.Length); | 
|  | var actualArray = builder.Build(_allocator); | 
|  | AssertArrayContents(expectedArrayContents, actualArray); | 
|  |  | 
|  | } | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_appendRangeSingleBytesTestData))] | 
|  | public void AppendRangeSingleBytesAfterClear(byte[][] initialContents, byte[] singleBytes) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | if (initialContents.Length > 0) | 
|  | builder.AppendRange(initialContents); | 
|  | builder.Clear(); | 
|  | var expectedArrayContents = singleBytes.Select(b => new[] { b }); | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.AppendRange(singleBytes); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(singleBytes.Length, builder.Length); | 
|  | var actualArray = builder.Build(_allocator); | 
|  | AssertArrayContents(expectedArrayContents, actualArray); | 
|  | } | 
|  |  | 
|  | public static readonly IEnumerable<object[]> _appendRangeByteArraysTestData = | 
|  | from initialContents in _initialContentsSet | 
|  | from byteArrays in _byteArrayArraysToAppend | 
|  | select new object[] { initialContents, byteArrays }; | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_appendRangeByteArraysTestData))] | 
|  | public void AppendRangeArrays(byte[][] initialContents, byte[][] byteArrays) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | if (initialContents.Length > 0) | 
|  | builder.AppendRange(initialContents); | 
|  | int initialLength = builder.Length; | 
|  | int expectedNewLength = initialLength + byteArrays.Length; | 
|  | var expectedArrayContents = initialContents.Concat(byteArrays); | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.AppendRange(byteArrays); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(expectedNewLength, builder.Length); | 
|  | var actualArray = builder.Build(_allocator); | 
|  | AssertArrayContents(expectedArrayContents, actualArray); | 
|  | } | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_appendRangeByteArraysTestData))] | 
|  | public void AppendRangeArraysAfterClear(byte[][] initialContents, byte[][] byteArrays) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | if (initialContents.Length > 0) | 
|  | builder.AppendRange(initialContents); | 
|  | builder.Clear(); | 
|  | var expectedArrayContents = byteArrays; | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.AppendRange(byteArrays); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(byteArrays.Length, builder.Length); | 
|  | var actualArray = builder.Build(_allocator); | 
|  | AssertArrayContents(expectedArrayContents, actualArray); | 
|  | } | 
|  | } | 
|  |  | 
|  | public class Clear | 
|  | { | 
|  | [Fact] | 
|  | public void ClearEmpty() | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.Clear(); | 
|  |  | 
|  | // Assert | 
|  | Assert.NotNull(actualReturnValue); | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(0, builder.Length); | 
|  | var array = builder.Build(_allocator); | 
|  | Assert.Equal(0, array.Length); | 
|  | } | 
|  |  | 
|  | public static readonly IEnumerable<object[]> _testData = | 
|  | from byteArrays in _byteArrayArraysToAppend | 
|  | select new object[] { byteArrays }; | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_testData))] | 
|  | public void ClearNonEmpty(byte[][] byteArrays) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | builder.AppendRange(byteArrays); | 
|  |  | 
|  | // Act | 
|  | var actualReturnValue = builder.Clear(); | 
|  |  | 
|  | // Assert | 
|  | Assert.NotNull(actualReturnValue); | 
|  | Assert.Equal(builder, actualReturnValue); | 
|  | Assert.Equal(0, builder.Length); | 
|  | var array = builder.Build(_allocator); | 
|  | Assert.Equal(0, array.Length); | 
|  | } | 
|  | } | 
|  |  | 
|  | public class Build | 
|  | { | 
|  | [Fact] | 
|  | public void BuildImmediately() | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  |  | 
|  | // Act | 
|  | var array = builder.Build(_allocator); | 
|  |  | 
|  | // Assert | 
|  | Assert.Equal(0, array.Length); | 
|  | } | 
|  |  | 
|  | public static readonly IEnumerable<object[]> _testData = | 
|  | from ba1 in _initialContentsSet | 
|  | from ba2 in _byteArrayArraysToAppend | 
|  | select new object[] { ba1.Concat(ba2) }; | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_testData))] | 
|  | public void AppendThenBuild(byte[][] byteArrays) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | foreach (var byteArray in byteArrays) | 
|  | { | 
|  | // Test the type of byte array to ensure each Append() overload is exercised. | 
|  | if (byteArray == null) | 
|  | { | 
|  | builder.AppendNull(); | 
|  | } | 
|  | else if (byteArray.Length == 1) | 
|  | { | 
|  | builder.Append(byteArray[0]); | 
|  | } | 
|  | else | 
|  | { | 
|  | builder.Append((ReadOnlySpan<byte>)byteArray); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Act | 
|  | var array = builder.Build(_allocator); | 
|  |  | 
|  | // Assert | 
|  | AssertArrayContents(byteArrays, array); | 
|  | } | 
|  |  | 
|  | [Theory] | 
|  | [MemberData(nameof(_testData))] | 
|  | public void BuildMultipleTimes(byte[][] byteArrays) | 
|  | { | 
|  | // Arrange | 
|  | var builder = new BinaryArray.Builder(); | 
|  | builder.AppendRange(byteArrays); | 
|  | builder.Build(_allocator); | 
|  |  | 
|  | // Act | 
|  | var array = builder.Build(_allocator); | 
|  |  | 
|  | // Assert | 
|  | AssertArrayContents(byteArrays, array); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void AssertArrayContents(IEnumerable<byte[]> expectedContents, BinaryArray array) | 
|  | { | 
|  | var expectedContentsArr = expectedContents.ToArray(); | 
|  | Assert.Equal(expectedContentsArr.Length, array.Length); | 
|  | for (int i = 0; i < array.Length; i++) | 
|  | { | 
|  | var expectedArray = expectedContentsArr[i]; | 
|  | var actualArray = array.IsNull(i) ? null : array.GetBytes(i).ToArray(); | 
|  | Assert.Equal(expectedArray, actualArray); | 
|  | } | 
|  | } | 
|  | } | 
|  | } |