]>
Commit | Line | Data |
---|---|---|
1d09f67e TL |
1 | // Licensed to the Apache Software Foundation (ASF) under one or more |
2 | // contributor license agreements. See the NOTICE file distributed with | |
3 | // this work for additional information regarding copyright ownership. | |
4 | // The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | // (the "License"); you may not use this file except in compliance with | |
6 | // the License. You may obtain a copy of the License at | |
7 | // | |
8 | // http://www.apache.org/licenses/LICENSE-2.0 | |
9 | // | |
10 | // Unless required by applicable law or agreed to in writing, software | |
11 | // distributed under the License is distributed on an "AS IS" BASIS, | |
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | // See the License for the specific language governing permissions and | |
14 | // limitations under the License. | |
15 | ||
16 | using Apache.Arrow.Ipc; | |
17 | using Apache.Arrow.Types; | |
18 | using System; | |
19 | using System.Linq; | |
20 | using System.Threading.Tasks; | |
21 | using Apache.Arrow.Arrays; | |
22 | using Xunit; | |
23 | ||
24 | namespace Apache.Arrow.Tests | |
25 | { | |
26 | public static class ArrowReaderVerifier | |
27 | { | |
28 | public static void VerifyReader(ArrowStreamReader reader, RecordBatch originalBatch) | |
29 | { | |
30 | RecordBatch readBatch = reader.ReadNextRecordBatch(); | |
31 | CompareBatches(originalBatch, readBatch); | |
32 | ||
33 | // There should only be one batch - calling ReadNextRecordBatch again should return null. | |
34 | Assert.Null(reader.ReadNextRecordBatch()); | |
35 | Assert.Null(reader.ReadNextRecordBatch()); | |
36 | } | |
37 | ||
38 | public static async Task VerifyReaderAsync(ArrowStreamReader reader, RecordBatch originalBatch) | |
39 | { | |
40 | RecordBatch readBatch = await reader.ReadNextRecordBatchAsync(); | |
41 | CompareBatches(originalBatch, readBatch); | |
42 | ||
43 | // There should only be one batch - calling ReadNextRecordBatchAsync again should return null. | |
44 | Assert.Null(await reader.ReadNextRecordBatchAsync()); | |
45 | Assert.Null(await reader.ReadNextRecordBatchAsync()); | |
46 | } | |
47 | ||
48 | public static void CompareBatches(RecordBatch expectedBatch, RecordBatch actualBatch, bool strictCompare = true) | |
49 | { | |
50 | SchemaComparer.Compare(expectedBatch.Schema, actualBatch.Schema); | |
51 | Assert.Equal(expectedBatch.Length, actualBatch.Length); | |
52 | Assert.Equal(expectedBatch.ColumnCount, actualBatch.ColumnCount); | |
53 | ||
54 | for (int i = 0; i < expectedBatch.ColumnCount; i++) | |
55 | { | |
56 | IArrowArray expectedArray = expectedBatch.Arrays.ElementAt(i); | |
57 | IArrowArray actualArray = actualBatch.Arrays.ElementAt(i); | |
58 | ||
59 | CompareArrays(expectedArray, actualArray, strictCompare); | |
60 | } | |
61 | } | |
62 | ||
63 | public static void CompareArrays(IArrowArray expectedArray, IArrowArray actualArray, bool strictCompare = true) | |
64 | { | |
65 | actualArray.Accept(new ArrayComparer(expectedArray, strictCompare)); | |
66 | } | |
67 | ||
68 | private class ArrayComparer : | |
69 | IArrowArrayVisitor<Int8Array>, | |
70 | IArrowArrayVisitor<Int16Array>, | |
71 | IArrowArrayVisitor<Int32Array>, | |
72 | IArrowArrayVisitor<Int64Array>, | |
73 | IArrowArrayVisitor<UInt8Array>, | |
74 | IArrowArrayVisitor<UInt16Array>, | |
75 | IArrowArrayVisitor<UInt32Array>, | |
76 | IArrowArrayVisitor<UInt64Array>, | |
77 | IArrowArrayVisitor<FloatArray>, | |
78 | IArrowArrayVisitor<DoubleArray>, | |
79 | IArrowArrayVisitor<BooleanArray>, | |
80 | IArrowArrayVisitor<TimestampArray>, | |
81 | IArrowArrayVisitor<Date32Array>, | |
82 | IArrowArrayVisitor<Date64Array>, | |
83 | IArrowArrayVisitor<ListArray>, | |
84 | IArrowArrayVisitor<StringArray>, | |
85 | IArrowArrayVisitor<FixedSizeBinaryArray>, | |
86 | IArrowArrayVisitor<BinaryArray>, | |
87 | IArrowArrayVisitor<StructArray>, | |
88 | IArrowArrayVisitor<Decimal128Array>, | |
89 | IArrowArrayVisitor<Decimal256Array>, | |
90 | IArrowArrayVisitor<DictionaryArray> | |
91 | { | |
92 | private readonly IArrowArray _expectedArray; | |
93 | private readonly ArrayTypeComparer _arrayTypeComparer; | |
94 | private readonly bool _strictCompare; | |
95 | ||
96 | public ArrayComparer(IArrowArray expectedArray, bool strictCompare) | |
97 | { | |
98 | _expectedArray = expectedArray; | |
99 | _arrayTypeComparer = new ArrayTypeComparer(expectedArray.Data.DataType); | |
100 | _strictCompare = strictCompare; | |
101 | } | |
102 | ||
103 | public void Visit(Int8Array array) => CompareArrays(array); | |
104 | public void Visit(Int16Array array) => CompareArrays(array); | |
105 | public void Visit(Int32Array array) => CompareArrays(array); | |
106 | public void Visit(Int64Array array) => CompareArrays(array); | |
107 | public void Visit(UInt8Array array) => CompareArrays(array); | |
108 | public void Visit(UInt16Array array) => CompareArrays(array); | |
109 | public void Visit(UInt32Array array) => CompareArrays(array); | |
110 | public void Visit(UInt64Array array) => CompareArrays(array); | |
111 | public void Visit(FloatArray array) => CompareArrays(array); | |
112 | public void Visit(DoubleArray array) => CompareArrays(array); | |
113 | public void Visit(BooleanArray array) => CompareArrays(array); | |
114 | public void Visit(TimestampArray array) => CompareArrays(array); | |
115 | public void Visit(Date32Array array) => CompareArrays(array); | |
116 | public void Visit(Date64Array array) => CompareArrays(array); | |
117 | public void Visit(ListArray array) => CompareArrays(array); | |
118 | public void Visit(FixedSizeBinaryArray array) => CompareArrays(array); | |
119 | public void Visit(Decimal128Array array) => CompareArrays(array); | |
120 | public void Visit(Decimal256Array array) => CompareArrays(array); | |
121 | public void Visit(StringArray array) => CompareBinaryArrays<StringArray>(array); | |
122 | public void Visit(BinaryArray array) => CompareBinaryArrays<BinaryArray>(array); | |
123 | ||
124 | public void Visit(StructArray array) | |
125 | { | |
126 | Assert.IsAssignableFrom<StructArray>(_expectedArray); | |
127 | StructArray expectedArray = (StructArray)_expectedArray; | |
128 | ||
129 | Assert.Equal(expectedArray.Length, array.Length); | |
130 | Assert.Equal(expectedArray.NullCount, array.NullCount); | |
131 | Assert.Equal(expectedArray.Offset, array.Offset); | |
132 | Assert.Equal(expectedArray.Data.Children.Length, array.Data.Children.Length); | |
133 | Assert.Equal(expectedArray.Fields.Count, array.Fields.Count); | |
134 | ||
135 | for (int i = 0; i < array.Fields.Count; i++) | |
136 | { | |
137 | array.Fields[i].Accept(new ArrayComparer(expectedArray.Fields[i], _strictCompare)); | |
138 | } | |
139 | } | |
140 | ||
141 | public void Visit(DictionaryArray array) | |
142 | { | |
143 | Assert.IsAssignableFrom<DictionaryArray>(_expectedArray); | |
144 | DictionaryArray expectedArray = (DictionaryArray)_expectedArray; | |
145 | var indicesComparer = new ArrayComparer(expectedArray.Indices, _strictCompare); | |
146 | var dictionaryComparer = new ArrayComparer(expectedArray.Dictionary, _strictCompare); | |
147 | array.Indices.Accept(indicesComparer); | |
148 | array.Dictionary.Accept(dictionaryComparer); | |
149 | } | |
150 | ||
151 | public void Visit(IArrowArray array) => throw new NotImplementedException(); | |
152 | ||
153 | private void CompareBinaryArrays<T>(BinaryArray actualArray) | |
154 | where T : IArrowArray | |
155 | { | |
156 | Assert.IsAssignableFrom<T>(_expectedArray); | |
157 | Assert.IsAssignableFrom<T>(actualArray); | |
158 | ||
159 | var expectedArray = (BinaryArray)_expectedArray; | |
160 | ||
161 | actualArray.Data.DataType.Accept(_arrayTypeComparer); | |
162 | ||
163 | Assert.Equal(expectedArray.Length, actualArray.Length); | |
164 | Assert.Equal(expectedArray.NullCount, actualArray.NullCount); | |
165 | Assert.Equal(expectedArray.Offset, actualArray.Offset); | |
166 | ||
167 | CompareValidityBuffer(expectedArray.NullCount, _expectedArray.Length, expectedArray.NullBitmapBuffer, actualArray.NullBitmapBuffer); | |
168 | ||
169 | if (_strictCompare) | |
170 | { | |
171 | Assert.True(expectedArray.ValueOffsetsBuffer.Span.SequenceEqual(actualArray.ValueOffsetsBuffer.Span)); | |
172 | Assert.True(expectedArray.Values.Slice(0, expectedArray.Length).SequenceEqual(actualArray.Values.Slice(0, actualArray.Length))); | |
173 | } | |
174 | else | |
175 | { | |
176 | for (int i = 0; i < expectedArray.Length; i++) | |
177 | { | |
178 | Assert.True( | |
179 | expectedArray.GetBytes(i).SequenceEqual(actualArray.GetBytes(i)), | |
180 | $"BinaryArray values do not match at index {i}."); | |
181 | } | |
182 | } | |
183 | } | |
184 | ||
185 | private void CompareArrays(FixedSizeBinaryArray actualArray) | |
186 | { | |
187 | Assert.IsAssignableFrom<FixedSizeBinaryArray>(_expectedArray); | |
188 | Assert.IsAssignableFrom<FixedSizeBinaryArray>(actualArray); | |
189 | ||
190 | var expectedArray = (FixedSizeBinaryArray)_expectedArray; | |
191 | ||
192 | actualArray.Data.DataType.Accept(_arrayTypeComparer); | |
193 | ||
194 | Assert.Equal(expectedArray.Length, actualArray.Length); | |
195 | Assert.Equal(expectedArray.NullCount, actualArray.NullCount); | |
196 | Assert.Equal(expectedArray.Offset, actualArray.Offset); | |
197 | ||
198 | CompareValidityBuffer(expectedArray.NullCount, _expectedArray.Length, expectedArray.NullBitmapBuffer, actualArray.NullBitmapBuffer); | |
199 | ||
200 | if (_strictCompare) | |
201 | { | |
202 | Assert.True(expectedArray.ValueBuffer.Span.Slice(0, expectedArray.Length).SequenceEqual(actualArray.ValueBuffer.Span.Slice(0, actualArray.Length))); | |
203 | } | |
204 | else | |
205 | { | |
206 | for (int i = 0; i < expectedArray.Length; i++) | |
207 | { | |
208 | Assert.True( | |
209 | expectedArray.GetBytes(i).SequenceEqual(actualArray.GetBytes(i)), | |
210 | $"FixedSizeBinaryArray values do not match at index {i}."); | |
211 | } | |
212 | } | |
213 | } | |
214 | ||
215 | private void CompareArrays<T>(PrimitiveArray<T> actualArray) | |
216 | where T : struct, IEquatable<T> | |
217 | { | |
218 | Assert.IsAssignableFrom<PrimitiveArray<T>>(_expectedArray); | |
219 | PrimitiveArray<T> expectedArray = (PrimitiveArray<T>)_expectedArray; | |
220 | ||
221 | actualArray.Data.DataType.Accept(_arrayTypeComparer); | |
222 | ||
223 | Assert.Equal(expectedArray.Length, actualArray.Length); | |
224 | Assert.Equal(expectedArray.NullCount, actualArray.NullCount); | |
225 | Assert.Equal(expectedArray.Offset, actualArray.Offset); | |
226 | ||
227 | CompareValidityBuffer(expectedArray.NullCount, _expectedArray.Length, expectedArray.NullBitmapBuffer, actualArray.NullBitmapBuffer); | |
228 | ||
229 | if (_strictCompare) | |
230 | { | |
231 | Assert.True(expectedArray.Values.Slice(0, expectedArray.Length).SequenceEqual(actualArray.Values.Slice(0, actualArray.Length))); | |
232 | } | |
233 | else | |
234 | { | |
235 | for (int i = 0; i < expectedArray.Length; i++) | |
236 | { | |
237 | Assert.Equal(expectedArray.GetValue(i), actualArray.GetValue(i)); | |
238 | } | |
239 | } | |
240 | } | |
241 | ||
242 | private void CompareArrays(BooleanArray actualArray) | |
243 | { | |
244 | Assert.IsAssignableFrom<BooleanArray>(_expectedArray); | |
245 | BooleanArray expectedArray = (BooleanArray)_expectedArray; | |
246 | ||
247 | actualArray.Data.DataType.Accept(_arrayTypeComparer); | |
248 | ||
249 | Assert.Equal(expectedArray.Length, actualArray.Length); | |
250 | Assert.Equal(expectedArray.NullCount, actualArray.NullCount); | |
251 | Assert.Equal(expectedArray.Offset, actualArray.Offset); | |
252 | ||
253 | CompareValidityBuffer(expectedArray.NullCount, _expectedArray.Length, expectedArray.NullBitmapBuffer, actualArray.NullBitmapBuffer); | |
254 | ||
255 | if (_strictCompare) | |
256 | { | |
257 | int booleanByteCount = BitUtility.ByteCount(expectedArray.Length); | |
258 | Assert.True(expectedArray.Values.Slice(0, booleanByteCount).SequenceEqual(actualArray.Values.Slice(0, booleanByteCount))); | |
259 | } | |
260 | else | |
261 | { | |
262 | for (int i = 0; i < expectedArray.Length; i++) | |
263 | { | |
264 | Assert.Equal(expectedArray.GetValue(i), actualArray.GetValue(i)); | |
265 | } | |
266 | } | |
267 | } | |
268 | ||
269 | private void CompareArrays(ListArray actualArray) | |
270 | { | |
271 | Assert.IsAssignableFrom<ListArray>(_expectedArray); | |
272 | ListArray expectedArray = (ListArray)_expectedArray; | |
273 | ||
274 | actualArray.Data.DataType.Accept(_arrayTypeComparer); | |
275 | ||
276 | Assert.Equal(expectedArray.Length, actualArray.Length); | |
277 | Assert.Equal(expectedArray.NullCount, actualArray.NullCount); | |
278 | Assert.Equal(expectedArray.Offset, actualArray.Offset); | |
279 | ||
280 | CompareValidityBuffer(expectedArray.NullCount, _expectedArray.Length, expectedArray.NullBitmapBuffer, actualArray.NullBitmapBuffer); | |
281 | Assert.True(expectedArray.ValueOffsetsBuffer.Span.SequenceEqual(actualArray.ValueOffsetsBuffer.Span)); | |
282 | ||
283 | actualArray.Values.Accept(new ArrayComparer(expectedArray.Values, _strictCompare)); | |
284 | } | |
285 | ||
286 | private void CompareValidityBuffer(int nullCount, int arrayLength, ArrowBuffer expectedValidityBuffer, ArrowBuffer actualValidityBuffer) | |
287 | { | |
288 | if (_strictCompare) | |
289 | { | |
290 | Assert.True(expectedValidityBuffer.Span.SequenceEqual(actualValidityBuffer.Span)); | |
291 | } | |
292 | else if (nullCount != 0) | |
293 | { | |
294 | int validityBitmapByteCount = BitUtility.ByteCount(arrayLength); | |
295 | Assert.True( | |
296 | expectedValidityBuffer.Span.Slice(0, validityBitmapByteCount).SequenceEqual(actualValidityBuffer.Span.Slice(0, validityBitmapByteCount)), | |
297 | "Validity buffers do not match."); | |
298 | } | |
299 | } | |
300 | } | |
301 | } | |
302 | } |