]>
Commit | Line | Data |
---|---|---|
1d09f67e TL |
1 | // Licensed to the Apache Software Foundation (ASF) under one |
2 | // or more contributor license agreements. See the NOTICE file | |
3 | // distributed with this work for additional information | |
4 | // regarding copyright ownership. The ASF licenses this file | |
5 | // to you under the Apache License, Version 2.0 (the | |
6 | // "License"); you may not use this file except in compliance | |
7 | // with the License. You may obtain a copy of the License at | |
8 | // | |
9 | // http://www.apache.org/licenses/LICENSE-2.0 | |
10 | // | |
11 | // Unless required by applicable law or agreed to in writing, software | |
12 | // distributed under the License is distributed on an "AS IS" BASIS, | |
13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | // See the License for the specific language governing permissions and | |
15 | // limitations under the License. | |
16 | ||
17 | package arrow | |
18 | ||
19 | import ( | |
20 | "fmt" | |
21 | "reflect" | |
22 | "testing" | |
23 | ) | |
24 | ||
25 | func TestMetadata(t *testing.T) { | |
26 | for _, tc := range []struct { | |
27 | md Metadata | |
28 | kvs map[string]string | |
29 | keys, values []string | |
30 | err string | |
31 | serialize string | |
32 | }{ | |
33 | { | |
34 | md: Metadata{ | |
35 | keys: []string{"k1", "k2"}, | |
36 | values: []string{"v1", "v2"}, | |
37 | }, | |
38 | keys: []string{"k1", "k2"}, | |
39 | values: []string{"v1", "v2"}, | |
40 | serialize: `["k1": "v1", "k2": "v2"]`, | |
41 | }, | |
42 | { | |
43 | md: Metadata{}, | |
44 | serialize: "[]", | |
45 | }, | |
46 | { | |
47 | md: Metadata{ | |
48 | keys: []string{"k1", "k2"}, | |
49 | values: []string{"v1", "v2"}, | |
50 | }, | |
51 | kvs: map[string]string{"k1": "v1", "k2": "v2"}, | |
52 | serialize: `["k1": "v1", "k2": "v2"]`, | |
53 | }, | |
54 | { | |
55 | md: Metadata{}, | |
56 | keys: []string{"k1", "k2", "k3"}, | |
57 | values: []string{"v1", "v2"}, | |
58 | err: "arrow: len mismatch", | |
59 | }, | |
60 | } { | |
61 | t.Run("", func(t *testing.T) { | |
62 | if tc.err != "" { | |
63 | defer func() { | |
64 | e := recover() | |
65 | if e == nil { | |
66 | t.Fatalf("expected a panic") | |
67 | } | |
68 | if got := e.(string); got != tc.err { | |
69 | t.Fatalf("invalid panic. got=%q, want=%q", got, tc.err) | |
70 | } | |
71 | }() | |
72 | } | |
73 | var md Metadata | |
74 | switch len(tc.kvs) { | |
75 | case 0: | |
76 | md = NewMetadata(tc.keys, tc.values) | |
77 | default: | |
78 | md = MetadataFrom(tc.kvs) | |
79 | } | |
80 | if got, want := md.Len(), len(tc.md.keys); !reflect.DeepEqual(got, want) { | |
81 | t.Fatalf("invalid len: got=%v, want=%v", got, want) | |
82 | } | |
83 | if got, want := md.Keys(), tc.md.keys; !reflect.DeepEqual(got, want) { | |
84 | t.Fatalf("invalid keys: got=%v, want=%v", got, want) | |
85 | } | |
86 | if got, want := md.Values(), tc.md.values; !reflect.DeepEqual(got, want) { | |
87 | t.Fatalf("invalid values: got=%v, want=%v", got, want) | |
88 | } | |
89 | if !reflect.DeepEqual(tc.md, md) { | |
90 | t.Fatalf("invalid md: got=%#v, want=%#v", md, tc.md) | |
91 | } | |
92 | clone := md.clone() | |
93 | if !reflect.DeepEqual(clone, md) { | |
94 | t.Fatalf("invalid clone: got=%#v, want=%#v", clone, md) | |
95 | } | |
96 | ||
97 | if got, want := tc.md.String(), tc.serialize; got != want { | |
98 | t.Fatalf("invalid stringer: got=%q, want=%q", got, want) | |
99 | } | |
100 | }) | |
101 | } | |
102 | ||
103 | t.Run("find-key", func(t *testing.T) { | |
104 | md := NewMetadata([]string{"k1", "k11"}, []string{"v1", "v11"}) | |
105 | ||
106 | if got, want := md.FindKey("k1"), 0; got != want { | |
107 | t.Fatalf("got=%d, want=%d", got, want) | |
108 | } | |
109 | ||
110 | if got, want := md.FindKey(""), -1; got != want { | |
111 | t.Fatalf("got=%d, want=%d", got, want) | |
112 | } | |
113 | ||
114 | if got, want := md.FindKey("k"), -1; got != want { | |
115 | t.Fatalf("got=%d, want=%d", got, want) | |
116 | } | |
117 | ||
118 | if got, want := md.FindKey(" "), -1; got != want { | |
119 | t.Fatalf("got=%d, want=%d", got, want) | |
120 | } | |
121 | ||
122 | if got, want := md.FindKey("k11"), 1; got != want { | |
123 | t.Fatalf("got=%d, want=%d", got, want) | |
124 | } | |
125 | ||
126 | if got, want := md.FindKey("k11 "), -1; got != want { | |
127 | t.Fatalf("got=%d, want=%d", got, want) | |
128 | } | |
129 | }) | |
130 | } | |
131 | ||
132 | func TestSchema(t *testing.T) { | |
133 | for _, tc := range []struct { | |
134 | fields []Field | |
135 | md *Metadata | |
136 | err error | |
137 | serialize string | |
138 | }{ | |
139 | { | |
140 | fields: []Field{ | |
141 | {Name: "f1", Type: PrimitiveTypes.Int32}, | |
142 | {Name: "f2", Type: PrimitiveTypes.Int64}, | |
143 | }, | |
144 | md: func() *Metadata { | |
145 | md := MetadataFrom(map[string]string{"k1": "v1", "k2": "v2"}) | |
146 | return &md | |
147 | }(), | |
148 | serialize: `schema: | |
149 | fields: 2 | |
150 | - f1: type=int32 | |
151 | - f2: type=int64 | |
152 | metadata: ["k1": "v1", "k2": "v2"]`, | |
153 | }, | |
154 | { | |
155 | fields: []Field{ | |
156 | {Name: "f1", Type: PrimitiveTypes.Int32}, | |
157 | {Name: "f2", Type: PrimitiveTypes.Int64}, | |
158 | }, | |
159 | md: nil, | |
160 | serialize: `schema: | |
161 | fields: 2 | |
162 | - f1: type=int32 | |
163 | - f2: type=int64`, | |
164 | }, | |
165 | { | |
166 | fields: []Field{ | |
167 | {Name: "f1", Type: PrimitiveTypes.Int32}, | |
168 | {Name: "f2", Type: nil}, | |
169 | }, | |
170 | md: nil, | |
171 | err: fmt.Errorf("arrow: field with nil DataType"), | |
172 | }, | |
173 | { | |
174 | fields: []Field{ | |
175 | {Name: "f1", Type: PrimitiveTypes.Int32}, | |
176 | {Name: "f2", Type: PrimitiveTypes.Int64}, | |
177 | {Name: "dup", Type: PrimitiveTypes.Int32}, // duplicate | |
178 | {Name: "dup", Type: PrimitiveTypes.Int64}, // duplicate | |
179 | }, | |
180 | md: nil, | |
181 | serialize: `schema: | |
182 | fields: 4 | |
183 | - f1: type=int32 | |
184 | - f2: type=int64 | |
185 | - dup: type=int32 | |
186 | - dup: type=int64`, | |
187 | }, | |
188 | } { | |
189 | t.Run("", func(t *testing.T) { | |
190 | if tc.err != nil { | |
191 | defer func() { | |
192 | e := recover() | |
193 | if e == nil { | |
194 | t.Fatalf("expected a panic %q", tc.err) | |
195 | } | |
196 | switch err := e.(type) { | |
197 | case string: | |
198 | if err != tc.err.Error() { | |
199 | t.Fatalf("invalid panic message. got=%q, want=%q", err, tc.err) | |
200 | } | |
201 | case error: | |
202 | if err.Error() != tc.err.Error() { | |
203 | t.Fatalf("invalid panic message. got=%q, want=%q", err, tc.err) | |
204 | } | |
205 | default: | |
206 | t.Fatalf("invalid type for panic message: %T (err=%v)", err, err) | |
207 | } | |
208 | }() | |
209 | } | |
210 | ||
211 | s := NewSchema(tc.fields, tc.md) | |
212 | ||
213 | if got, want := len(s.Fields()), len(tc.fields); got != want { | |
214 | t.Fatalf("invalid number of fields. got=%d, want=%d", got, want) | |
215 | } | |
216 | ||
217 | if got, want := s.Field(0), tc.fields[0]; !got.Equal(want) { | |
218 | t.Fatalf("invalid field: got=%#v, want=%#v", got, want) | |
219 | } | |
220 | ||
221 | if got, want := s.HasMetadata(), tc.md != nil; got != want { | |
222 | t.Fatalf("invalid metadata: got=%v, want=%v", got, want) | |
223 | } | |
224 | ||
225 | if tc.md != nil { | |
226 | if got, want := s.Metadata(), *tc.md; !reflect.DeepEqual(got, want) { | |
227 | t.Fatalf("invalid metadata: got=%#v, want=%#v", got, want) | |
228 | } | |
229 | } | |
230 | ||
231 | for _, tc := range []struct { | |
232 | name string | |
233 | ok bool | |
234 | fields []Field | |
235 | i []int | |
236 | }{ | |
237 | {"f1", true, []Field{tc.fields[0]}, []int{0}}, | |
238 | {"f2", true, []Field{tc.fields[1]}, []int{1}}, | |
239 | {"N/A", false, nil, nil}, | |
240 | } { | |
241 | t.Run(tc.name, func(t *testing.T) { | |
242 | got, ok := s.FieldsByName(tc.name) | |
243 | if ok != tc.ok { | |
244 | t.Fatalf("invalid field %q: got=%v, want=%v", tc.name, ok, tc.ok) | |
245 | } | |
246 | if i := s.FieldIndices(tc.name); !reflect.DeepEqual(i, tc.i) { | |
247 | t.Fatalf("invalid FieldIndices(%s): got=%v, want=%v\nfields: %v", tc.name, i, tc.i, s.fields) | |
248 | } | |
249 | if ok := s.HasField(tc.name); ok != tc.ok { | |
250 | t.Fatalf("invalid HasField(%s): got=%v, want=%v", tc.name, ok, tc.ok) | |
251 | } | |
252 | for i, field := range got { | |
253 | if !field.Equal(tc.fields[i]) { | |
254 | t.Fatalf("invalid field[%d]: got=%#v, want=%#v", i, field, tc.fields[i]) | |
255 | } | |
256 | } | |
257 | }) | |
258 | } | |
259 | ||
260 | if s.HasField("dup") { | |
261 | got := s.FieldIndices("dup") | |
262 | want := []int{2, 3} | |
263 | if !reflect.DeepEqual(got, want) { | |
264 | t.Fatalf("invalid duplicate fields: got=%v, want=%v", got, want) | |
265 | } | |
266 | } | |
267 | ||
268 | if got, want := s.String(), tc.serialize; got != want { | |
269 | t.Fatalf("invalid stringer: got=%q, want=%q", got, want) | |
270 | } | |
271 | }) | |
272 | } | |
273 | } | |
274 | ||
275 | func TestSchemaEqual(t *testing.T) { | |
276 | fields := []Field{ | |
277 | {Name: "f1", Type: PrimitiveTypes.Int32}, | |
278 | {Name: "f2", Type: PrimitiveTypes.Int64}, | |
279 | } | |
280 | md := func() *Metadata { | |
281 | md := MetadataFrom(map[string]string{"k1": "v1", "k2": "v2"}) | |
282 | return &md | |
283 | }() | |
284 | ||
285 | for _, tc := range []struct { | |
286 | a, b *Schema | |
287 | want bool | |
288 | }{ | |
289 | { | |
290 | a: nil, | |
291 | b: nil, | |
292 | want: true, | |
293 | }, | |
294 | { | |
295 | a: nil, | |
296 | b: NewSchema(nil, nil), | |
297 | want: false, | |
298 | }, | |
299 | { | |
300 | a: NewSchema(nil, nil), | |
301 | b: nil, | |
302 | want: false, | |
303 | }, | |
304 | { | |
305 | a: NewSchema(nil, nil), | |
306 | b: NewSchema(nil, nil), | |
307 | want: true, | |
308 | }, | |
309 | { | |
310 | a: NewSchema(fields, nil), | |
311 | b: NewSchema(fields, nil), | |
312 | want: true, | |
313 | }, | |
314 | { | |
315 | a: NewSchema(fields, md), | |
316 | b: NewSchema(fields, nil), | |
317 | want: true, | |
318 | }, | |
319 | { | |
320 | a: NewSchema(fields, md), | |
321 | b: NewSchema(fields, md), | |
322 | want: true, | |
323 | }, | |
324 | { | |
325 | a: NewSchema(fields[:1], md), | |
326 | b: NewSchema(fields, md), | |
327 | want: false, | |
328 | }, | |
329 | { | |
330 | a: NewSchema(fields, md), | |
331 | b: NewSchema([]Field{ | |
332 | {Name: "f1", Type: PrimitiveTypes.Int32}, | |
333 | {Name: "f2", Type: PrimitiveTypes.Int32}, | |
334 | }, md), | |
335 | want: false, | |
336 | }, | |
337 | { | |
338 | a: NewSchema(fields, md), | |
339 | b: NewSchema([]Field{ | |
340 | {Name: "f1", Type: PrimitiveTypes.Int32}, | |
341 | {Name: "fx", Type: PrimitiveTypes.Int64}, | |
342 | }, md), | |
343 | want: false, | |
344 | }, | |
345 | } { | |
346 | t.Run("", func(t *testing.T) { | |
347 | if !tc.a.Equal(tc.a) { | |
348 | t.Fatalf("a != a") | |
349 | } | |
350 | if !tc.b.Equal(tc.b) { | |
351 | t.Fatalf("b != b") | |
352 | } | |
353 | ab := tc.a.Equal(tc.b) | |
354 | if ab != tc.want { | |
355 | t.Fatalf("got=%v, want=%v", ab, tc.want) | |
356 | } | |
357 | ||
358 | ba := tc.b.Equal(tc.a) | |
359 | if ab != ba { | |
360 | t.Fatalf("ab != ba") | |
361 | } | |
362 | ||
363 | if (tc.a.Fingerprint() == tc.b.Fingerprint()) != tc.want { | |
364 | t.Fatalf("fingerprint: got=%v;%v, wanted=%v", tc.a.Fingerprint(), tc.b.Fingerprint(), tc.want) | |
365 | } | |
366 | }) | |
367 | } | |
368 | } |