1
16
17 package value
18
19 import (
20 "encoding/base64"
21 "encoding/json"
22 "fmt"
23 "reflect"
24 "sort"
25 "testing"
26 "time"
27 )
28
29 func MustReflect(i interface{}) Value {
30 if i == nil {
31 return NewValueInterface(nil)
32 }
33 v, err := wrapValueReflect(reflect.ValueOf(i), nil, nil)
34 if err != nil {
35 panic(err)
36 }
37 return v
38 }
39
40 func TestReflectPrimitives(t *testing.T) {
41 rv := MustReflect("string")
42 if !rv.IsString() {
43 t.Error("expected IsString to be true")
44 }
45 if rv.AsString() != "string" {
46 t.Errorf("expected rv.String to be 'string' but got %v", rv.Unstructured())
47 }
48
49 rv = MustReflect([]byte("string"))
50 if !rv.IsString() {
51 t.Error("expected IsString to be true")
52 }
53 if rv.IsList() {
54 t.Error("expected IsList to be false ([]byte is represented as a base64 encoded string)")
55 }
56 encoded := base64.StdEncoding.EncodeToString([]byte("string"))
57 if rv.AsString() != encoded {
58 t.Errorf("expected rv.String to be %v but got %v", []byte(encoded), rv.Unstructured())
59 }
60
61 rv = MustReflect(1)
62 if !rv.IsInt() {
63 t.Error("expected IsInt to be true")
64 }
65 if rv.AsInt() != 1 {
66 t.Errorf("expected rv.Int to be 1 but got %v", rv.Unstructured())
67 }
68
69 rv = MustReflect(uint32(3000000000))
70 if !rv.IsInt() {
71 t.Error("expected IsInt to be true")
72 }
73 if rv.AsInt() != 3000000000 {
74 t.Errorf("expected rv.Int to be 3000000000 but got %v", rv.Unstructured())
75 }
76
77 rv = MustReflect(1.5)
78 if !rv.IsFloat() {
79 t.Error("expected IsFloat to be true")
80 }
81 if rv.AsFloat() != 1.5 {
82 t.Errorf("expected rv.Float to be 1.1 but got %v", rv.Unstructured())
83 }
84
85 rv = MustReflect(true)
86 if !rv.IsBool() {
87 t.Error("expected IsBool to be true")
88 }
89 if rv.AsBool() != true {
90 t.Errorf("expected rv.Bool to be true but got %v", rv.Unstructured())
91 }
92
93 rv = MustReflect(nil)
94 if !rv.IsNull() {
95 t.Error("expected IsNull to be true")
96 }
97 }
98
99 type Convertable struct {
100 Value interface{}
101 }
102
103 func (t Convertable) MarshalJSON() ([]byte, error) {
104 return json.Marshal(t.Value)
105 }
106
107 func (t Convertable) UnmarshalJSON(data []byte) error {
108 return json.Unmarshal(data, &t.Value)
109 }
110
111 type PtrConvertable struct {
112 Value interface{}
113 }
114
115 func (t *PtrConvertable) MarshalJSON() ([]byte, error) {
116 return json.Marshal(t.Value)
117 }
118
119 func (t *PtrConvertable) UnmarshalJSON(data []byte) error {
120 return json.Unmarshal(data, &t.Value)
121 }
122
123 type StringConvertable struct {
124 Value string
125 }
126
127 func (t StringConvertable) MarshalJSON() ([]byte, error) {
128 return json.Marshal(t.Value)
129 }
130
131 func (t StringConvertable) ToUnstructured() (string, bool) {
132 return t.Value, true
133 }
134
135 type PtrStringConvertable struct {
136 Value string
137 }
138
139 func (t PtrStringConvertable) MarshalJSON() ([]byte, error) {
140 return json.Marshal(t.Value)
141 }
142
143 func (t *PtrStringConvertable) ToUnstructured() (string, bool) {
144 return t.Value, true
145 }
146
147 func TestReflectCustomStringConversion(t *testing.T) {
148 dateTime, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05+07:00")
149 if err != nil {
150 t.Fatal(err)
151 }
152 cases := []struct {
153 name string
154 convertable interface{}
155 expected interface{}
156 }{
157 {
158 name: "marshalable-struct",
159 convertable: Convertable{Value: "struct-test"},
160 expected: "struct-test",
161 },
162 {
163 name: "marshalable-pointer",
164 convertable: &PtrConvertable{Value: "pointer-test"},
165 expected: "pointer-test",
166 },
167 {
168 name: "pointer-to-marshalable-struct",
169 convertable: &Convertable{Value: "pointer-test"},
170 expected: "pointer-test",
171 },
172 {
173 name: "string-convertable-struct",
174 convertable: StringConvertable{Value: "struct-test"},
175 expected: "struct-test",
176 },
177 {
178 name: "string-convertable-pointer",
179 convertable: &PtrStringConvertable{Value: "struct-test"},
180 expected: "struct-test",
181 },
182 {
183 name: "pointer-to-string-convertable-struct",
184 convertable: &StringConvertable{Value: "pointer-test"},
185 expected: "pointer-test",
186 },
187 {
188 name: "time",
189 convertable: dateTime,
190 expected: "2006-01-02T15:04:05+07:00",
191 },
192 {
193 name: "nil-marshalable-struct",
194 convertable: Convertable{Value: nil},
195 expected: nil,
196 },
197 }
198 for _, tc := range cases {
199 t.Run(tc.name, func(t *testing.T) {
200 rv := MustReflect(tc.convertable)
201 if rv.Unstructured() != tc.expected {
202 t.Errorf("expected rv.String to be %v but got %s", tc.expected, rv.AsString())
203 }
204 })
205 }
206 }
207
208 func TestReflectPointers(t *testing.T) {
209 s := "string"
210 rv := MustReflect(&s)
211 if !rv.IsString() {
212 t.Error("expected IsString to be true")
213 }
214 if rv.AsString() != "string" {
215 t.Errorf("expected rv.String to be 'string' but got %s", rv.AsString())
216 }
217 }
218
219 type T struct {
220 I int64 `json:"int"`
221 }
222
223 type emptyStruct struct{}
224 type testBasicStruct struct {
225 I int64 `json:"int"`
226 S string
227 }
228 type testOmitStruct struct {
229 I int64 `json:"-"`
230 S string
231 }
232 type testInlineStruct struct {
233 Inline T `json:",inline"`
234 S string
235 }
236 type testOmitemptyStruct struct {
237 Noomit *string `json:"noomit"`
238 Omit *string `json:"omit,omitempty"`
239 }
240 type testEmbeddedStruct struct {
241 *testBasicStruct `json:",inline"`
242 }
243
244 func TestReflectStruct(t *testing.T) {
245 cases := []struct {
246 name string
247 val interface{}
248 expectedMap map[string]interface{}
249 expectedUnstructured interface{}
250 }{
251 {
252 name: "empty",
253 val: emptyStruct{},
254 expectedMap: map[string]interface{}{},
255 expectedUnstructured: map[string]interface{}{},
256 },
257 {
258 name: "basic",
259 val: testBasicStruct{I: 10, S: "string"},
260 expectedMap: map[string]interface{}{"int": int64(10), "S": "string"},
261 expectedUnstructured: map[string]interface{}{"int": int64(10), "S": "string"},
262 },
263 {
264 name: "pointerToBasic",
265 val: &testBasicStruct{I: 10, S: "string"},
266 expectedMap: map[string]interface{}{"int": int64(10), "S": "string"},
267 expectedUnstructured: map[string]interface{}{"int": int64(10), "S": "string"},
268 },
269 {
270 name: "omit",
271 val: testOmitStruct{I: 10, S: "string"},
272 expectedMap: map[string]interface{}{"S": "string"},
273 expectedUnstructured: map[string]interface{}{"S": "string"},
274 },
275 {
276 name: "inline",
277 val: &testInlineStruct{Inline: T{I: 10}, S: "string"},
278 expectedMap: map[string]interface{}{"int": int64(10), "S": "string"},
279 expectedUnstructured: map[string]interface{}{"int": int64(10), "S": "string"},
280 },
281 {
282 name: "omitempty",
283 val: testOmitemptyStruct{Noomit: nil, Omit: nil},
284 expectedMap: map[string]interface{}{"noomit": (*string)(nil)},
285 expectedUnstructured: map[string]interface{}{"noomit": nil},
286 },
287 {
288 name: "embedded",
289 val: testEmbeddedStruct{&testBasicStruct{I: 10, S: "string"}},
290 expectedMap: map[string]interface{}{"int": int64(10), "S": "string"},
291 expectedUnstructured: map[string]interface{}{"int": int64(10), "S": "string"},
292 },
293 }
294
295 for _, tc := range cases {
296 t.Run(tc.name, func(t *testing.T) {
297 rv := MustReflect(tc.val)
298 if !rv.IsMap() {
299 t.Error("expected IsMap to be true")
300 }
301 m := rv.AsMap()
302 if m.Length() != len(tc.expectedMap) {
303 t.Errorf("expected map to be of length %d but got %d", len(tc.expectedMap), m.Length())
304 }
305 iterateResult := map[string]interface{}{}
306 m.Iterate(func(s string, value Value) bool {
307 iterateResult[s] = value.(*valueReflect).Value.Interface()
308 return true
309 })
310 if !reflect.DeepEqual(iterateResult, tc.expectedMap) {
311 t.Errorf("expected iterate to produce %#v but got %#v", tc.expectedMap, iterateResult)
312 }
313
314 unstructured := rv.Unstructured()
315 if !reflect.DeepEqual(unstructured, tc.expectedUnstructured) {
316 t.Errorf("expected iterate to produce %#v but got %#v", tc.expectedUnstructured, unstructured)
317 }
318 })
319 }
320 }
321
322 type testMutateStruct struct {
323 I1 int64 `json:"key1,omitempty"`
324 S1 string `json:"key2,omitempty"`
325 S2 string `json:"key3,omitempty"`
326 S3 string `json:"key4,omitempty"`
327 }
328
329 func TestReflectStructMutate(t *testing.T) {
330 rv := MustReflect(&testMutateStruct{I1: 1, S1: "string1"})
331 if !rv.IsMap() {
332 t.Error("expected IsMap to be true")
333 }
334 m := rv.AsMap()
335 atKey1, ok := m.Get("key1")
336 if !ok {
337 t.Fatalf("expected map.Get(key1) to be 1 but got !ok")
338 }
339 if atKey1.AsInt() != 1 {
340 t.Fatalf("expected map.Get(key1) to be 1 but got: %v", atKey1)
341 }
342 m.Set("key1", NewValueInterface(int64(2)))
343 m.Delete("key2")
344 m.Delete("key3")
345 m.Set("key4", NewValueInterface("string4"))
346
347 expectedMap := map[string]interface{}{"key1": int64(2), "key4": "string4"}
348 unstructured := rv.Unstructured()
349 if !reflect.DeepEqual(unstructured, expectedMap) {
350 t.Errorf("expected %v but got: %v", expectedMap, unstructured)
351 }
352 }
353
354
355 func TestReflectMutateNestedStruct(t *testing.T) {
356 type field struct {
357 S string `json:"s,omitempty"`
358 }
359
360 cases := []struct {
361 fieldName string
362 root Value
363 lookupField func(root Value) Value
364 expectUpdated interface{}
365 expectDeleted interface{}
366 }{
367 {
368 fieldName: "field",
369 root: MustReflect(&struct {
370 Field field `json:"field,omitempty"`
371 }{
372 Field: field{S: "field"},
373 }),
374 lookupField: func(rv Value) Value {
375 field, _ := rv.AsMap().Get("field")
376 return field
377 },
378 expectUpdated: map[string]interface{}{
379 "field": map[string]interface{}{"s": "updatedValue"},
380 },
381 expectDeleted: map[string]interface{}{
382 "field": map[string]interface{}{},
383 },
384 },
385 {
386 fieldName: "map",
387 root: MustReflect(&struct {
388 Map map[string]field `json:"map,omitempty"`
389 }{
390 Map: map[string]field{"mapKey": {S: "mapItem"}},
391 }),
392 lookupField: func(rv Value) Value {
393 m, _ := rv.AsMap().Get("map")
394 mapItem, _ := m.AsMap().Get("mapKey")
395 return mapItem
396 },
397 expectUpdated: map[string]interface{}{
398 "map": map[string]interface{}{"mapKey": map[string]interface{}{"s": "updatedValue"}},
399 },
400 expectDeleted: map[string]interface{}{
401 "map": map[string]interface{}{"mapKey": map[string]interface{}{}},
402 },
403 },
404 {
405 fieldName: "mapiter",
406 root: MustReflect(&struct {
407 Mapiter map[string]field `json:"mapiter,omitempty"`
408 }{
409 Mapiter: map[string]field{"mapKey": {S: "mapItem"}},
410 }),
411 lookupField: func(rv Value) Value {
412 mapItem := &valueReflect{}
413 m, _ := rv.AsMap().Get("mapiter")
414 m.AsMap().Iterate(func(key string, value Value) bool {
415 if key == "mapKey" {
416 *mapItem = *value.(*valueReflect)
417 return false
418 }
419 return true
420 })
421 if !mapItem.Value.IsValid() {
422 t.Fatal("map item not found")
423 }
424 return mapItem
425 },
426 expectUpdated: map[string]interface{}{
427 "mapiter": map[string]interface{}{"mapKey": map[string]interface{}{"s": "updatedValue"}},
428 },
429 expectDeleted: map[string]interface{}{
430 "mapiter": map[string]interface{}{"mapKey": map[string]interface{}{}},
431 },
432 },
433 {
434 fieldName: "list",
435 root: MustReflect(&struct {
436 List []field `json:"list,omitempty"`
437 }{
438 List: []field{{S: "listItem"}},
439 }),
440 lookupField: func(rv Value) Value {
441 list, _ := rv.AsMap().Get("list")
442 return list.AsList().At(0)
443 },
444 expectUpdated: map[string]interface{}{
445 "list": []interface{}{map[string]interface{}{"s": "updatedValue"}},
446 },
447 expectDeleted: map[string]interface{}{
448 "list": []interface{}{map[string]interface{}{}},
449 },
450 },
451 {
452 fieldName: "mapOfMaps",
453 root: MustReflect(&struct {
454 MapOfMaps map[string]map[string]field `json:"mapOfMaps,omitempty"`
455 }{
456 MapOfMaps: map[string]map[string]field{"outer": {"inner": {S: "mapOfMapItem"}}},
457 }),
458 lookupField: func(rv Value) Value {
459 mapOfMaps, _ := rv.AsMap().Get("mapOfMaps")
460 innerMap, _ := mapOfMaps.AsMap().Get("outer")
461 mapOfMapsItem, _ := innerMap.AsMap().Get("inner")
462 return mapOfMapsItem
463 },
464 expectUpdated: map[string]interface{}{
465 "mapOfMaps": map[string]interface{}{"outer": map[string]interface{}{"inner": map[string]interface{}{"s": "updatedValue"}}},
466 },
467 expectDeleted: map[string]interface{}{
468 "mapOfMaps": map[string]interface{}{"outer": map[string]interface{}{"inner": map[string]interface{}{}}},
469 },
470 },
471 {
472 fieldName: "mapOfLists",
473 root: MustReflect(&struct {
474 MapOfLists map[string][]field `json:"mapOfLists,omitempty"`
475 }{
476 MapOfLists: map[string][]field{"outer": {{S: "mapOfListsItem"}}},
477 }),
478 lookupField: func(rv Value) Value {
479 mapOfLists, _ := rv.AsMap().Get("mapOfLists")
480 innerList, _ := mapOfLists.AsMap().Get("outer")
481 mapOfListsItem := innerList.AsList().At(0)
482 return mapOfListsItem
483 },
484
485 expectUpdated: map[string]interface{}{
486 "mapOfLists": map[string]interface{}{"outer": []interface{}{map[string]interface{}{"s": "updatedValue"}}},
487 },
488 expectDeleted: map[string]interface{}{
489 "mapOfLists": map[string]interface{}{"outer": []interface{}{map[string]interface{}{}}},
490 },
491 },
492 }
493
494 for _, tc := range cases {
495 t.Run(tc.fieldName, func(t *testing.T) {
496 root := tc.root
497 field := tc.lookupField(root)
498 field.AsMap().Set("s", NewValueInterface("updatedValue"))
499 unstructured := root.Unstructured()
500 if !reflect.DeepEqual(unstructured, tc.expectUpdated) {
501 t.Errorf("expected %v but got: %v", tc.expectUpdated, unstructured)
502 }
503
504 field.AsMap().Delete("s")
505 unstructured = root.Unstructured()
506 if !reflect.DeepEqual(unstructured, tc.expectDeleted) {
507 t.Errorf("expected %v but got: %v", tc.expectDeleted, unstructured)
508 }
509 })
510 }
511 }
512
513 func TestReflectMap(t *testing.T) {
514 cases := []struct {
515 name string
516 val interface{}
517 expectedMap map[string]interface{}
518 expectedUnstructured interface{}
519 length int
520 }{
521 {
522 name: "empty",
523 val: map[string]string{},
524 expectedMap: map[string]interface{}{},
525 expectedUnstructured: map[string]interface{}{},
526 length: 0,
527 },
528 {
529 name: "stringMap",
530 val: map[string]string{"key1": "value1", "key2": "value2"},
531 expectedMap: map[string]interface{}{"key1": "value1", "key2": "value2"},
532 expectedUnstructured: map[string]interface{}{"key1": "value1", "key2": "value2"},
533 length: 2,
534 },
535 {
536 name: "convertableMap",
537 val: map[string]Convertable{"key1": {"converted1"}, "key2": {"converted2"}},
538 expectedMap: map[string]interface{}{"key1": "converted1", "key2": "converted2"},
539 expectedUnstructured: map[string]interface{}{"key1": "converted1", "key2": "converted2"},
540 length: 2,
541 },
542 }
543
544 for _, tc := range cases {
545 t.Run(tc.name, func(t *testing.T) {
546 rv := MustReflect(tc.val)
547 if !rv.IsMap() {
548 t.Error("expected IsMap to be true")
549 }
550 m := rv.AsMap()
551 if m.Length() != tc.length {
552 t.Errorf("expected map to be of length %d but got %d", tc.length, m.Length())
553 }
554 iterateResult := map[string]interface{}{}
555 m.Iterate(func(s string, value Value) bool {
556 iterateResult[s] = value.AsString()
557 return true
558 })
559 if !reflect.DeepEqual(iterateResult, tc.expectedMap) {
560 t.Errorf("expected iterate to produce %#v but got %#v", tc.expectedMap, iterateResult)
561 }
562 unstructured := rv.Unstructured()
563 if !reflect.DeepEqual(unstructured, tc.expectedUnstructured) {
564 t.Errorf("expected iterate to produce %#v but got %#v", tc.expectedUnstructured, unstructured)
565 }
566 })
567 }
568 }
569
570 func TestReflectMapMutate(t *testing.T) {
571 rv := MustReflect(map[string]string{"key1": "value1", "key2": "value2"})
572 if !rv.IsMap() {
573 t.Error("expected IsMap to be true")
574 }
575 m := rv.AsMap()
576 atKey1, ok := m.Get("key1")
577 if !ok {
578 t.Errorf("expected map.Get(key1) to be 'value1' but got !ok")
579 }
580 if atKey1.AsString() != "value1" {
581 t.Errorf("expected map.Get(key1) to be 'value1' but got: %v", atKey1)
582 }
583 m.Set("key1", NewValueInterface("replacement"))
584 m.Delete("key2")
585 m.Delete("key3")
586 m.Set("key4", NewValueInterface("value4"))
587
588 expectedMap := map[string]interface{}{"key1": "replacement", "key4": "value4"}
589 unstructured := rv.Unstructured()
590 if !reflect.DeepEqual(unstructured, expectedMap) {
591 t.Errorf("expected %v but got: %v", expectedMap, unstructured)
592 }
593 }
594
595 func TestReflectList(t *testing.T) {
596 cases := []struct {
597 name string
598 val interface{}
599 expectedIterate []interface{}
600 expectedUnstructured interface{}
601 length int
602 }{
603 {
604 name: "empty",
605 val: []string{},
606 expectedIterate: []interface{}{},
607 expectedUnstructured: []interface{}{},
608 length: 0,
609 },
610 {
611 name: "stringList",
612 val: []string{"value1", "value2"},
613 expectedIterate: []interface{}{"value1", "value2"},
614 expectedUnstructured: []interface{}{"value1", "value2"},
615 length: 2,
616 },
617 {
618 name: "convertableList",
619 val: []Convertable{{"converted1"}, {"converted2"}},
620 expectedIterate: []interface{}{"converted1", "converted2"},
621 expectedUnstructured: []interface{}{"converted1", "converted2"},
622 length: 2,
623 },
624 }
625
626 for _, tc := range cases {
627 t.Run(tc.name, func(t *testing.T) {
628 rv := MustReflect(tc.val)
629 if !rv.IsList() {
630 t.Error("expected IsList to be true")
631 }
632 m := rv.AsList()
633 if m.Length() != tc.length {
634 t.Errorf("expected list to be of length %d but got %d", tc.length, m.Length())
635 }
636
637 l := m.Length()
638 iterateResult := make([]interface{}, l)
639 for i := 0; i < l; i++ {
640 iterateResult[i] = m.At(i).AsString()
641 }
642 if !reflect.DeepEqual(iterateResult, tc.expectedIterate) {
643 t.Errorf("expected iterate to produce %#v but got %#v", tc.expectedIterate, iterateResult)
644 }
645
646 iter := m.Range()
647 iterateResult = make([]interface{}, l)
648 for iter.Next() {
649 i, val := iter.Item()
650 iterateResult[i] = val.AsString()
651 }
652 if !reflect.DeepEqual(iterateResult, tc.expectedIterate) {
653 t.Errorf("expected iterate to produce %#v but got %#v", tc.expectedIterate, iterateResult)
654 }
655
656 unstructured := rv.Unstructured()
657 if !reflect.DeepEqual(unstructured, tc.expectedUnstructured) {
658 t.Errorf("expected iterate to produce %#v but got %#v", tc.expectedUnstructured, unstructured)
659 }
660 })
661 }
662 }
663
664 func TestReflectListAt(t *testing.T) {
665 rv := MustReflect([]string{"one", "two"})
666 if !rv.IsList() {
667 t.Error("expected IsList to be true")
668 }
669 list := rv.AsList()
670 atOne := list.At(1)
671 if atOne.AsString() != "two" {
672 t.Errorf("expected list.At(1) to be 'two' but got: %v", atOne)
673 }
674 }
675
676 func TestMapZip(t *testing.T) {
677 type entry struct {
678 key string
679 lhs, rhs interface{}
680 }
681
682 type s struct {
683
684 C string `json:"c,omitempty"`
685 B string `json:"b,omitempty"`
686 D string `json:"d,omitempty"`
687 A string `json:"a,omitempty"`
688 }
689 cases := []struct {
690 name string
691 lhs interface{}
692 rhs interface{}
693 expectedZipped []entry
694 }{
695 {
696 name: "structZip",
697 lhs: &s{A: "1", B: "3", C: "5"},
698 rhs: &s{A: "2", B: "4", D: "6"},
699 expectedZipped: []entry{
700 {"a", "1", "2"},
701 {"b", "3", "4"},
702 {"c", "5", interface{}(nil)},
703 {"d", interface{}(nil), "6"},
704 },
705 },
706 {
707 name: "mapZip",
708 lhs: &map[string]interface{}{"a": "1", "b": "3", "c": "5"},
709 rhs: &map[string]interface{}{"a": "2", "b": "4", "d": "6"},
710 expectedZipped: []entry{
711 {"a", "1", "2"},
712 {"b", "3", "4"},
713 {"c", "5", interface{}(nil)},
714 {"d", interface{}(nil), "6"},
715 },
716 },
717 }
718
719 for _, tc := range cases {
720 t.Run(tc.name, func(t *testing.T) {
721 lhs := MustReflect(tc.lhs)
722 rhs := MustReflect(tc.rhs)
723 for _, lhs := range []Value{lhs, NewValueInterface(lhs.Unstructured())} {
724 for _, rhs := range []Value{rhs, NewValueInterface(rhs.Unstructured())} {
725 t.Run(fmt.Sprintf("%s-%s", reflect.TypeOf(lhs).Elem().Name(), reflect.TypeOf(rhs).Elem().Name()), func(t *testing.T) {
726 for _, order := range []MapTraverseOrder{Unordered, LexicalKeyOrder} {
727 var zipped []entry
728 var name string
729 switch order {
730 case Unordered:
731 name = "Unordered"
732 case LexicalKeyOrder:
733 name = "LexicalKeyOrder"
734 }
735 t.Run(name, func(t *testing.T) {
736 MapZip(lhs.AsMap(), rhs.AsMap(), order, func(key string, lhs, rhs Value) bool {
737 var li, ri interface{}
738 if lhs != nil {
739 li = lhs.Unstructured()
740 }
741 if rhs != nil {
742 ri = rhs.Unstructured()
743 }
744 zipped = append(zipped, entry{key, li, ri})
745 return true
746 })
747 if order == Unordered {
748 sort.Slice(zipped, func(i, j int) bool {
749 return zipped[i].key < zipped[j].key
750 })
751 }
752 if !reflect.DeepEqual(zipped, tc.expectedZipped) {
753 t.Errorf("expected zip to produce:\n%#v\nbut got\n%#v", tc.expectedZipped, zipped)
754 }
755 })
756 }
757 })
758 }
759 }
760 })
761 }
762 }
763
View as plain text