• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package proptools
16
17import (
18	"errors"
19	"fmt"
20	"reflect"
21	"strings"
22	"testing"
23)
24
25type appendPropertyTestCase struct {
26	name   string
27	dst    interface{}
28	src    interface{}
29	out    interface{}
30	order  Order // default is Append
31	filter ExtendPropertyFilterFunc
32	err    error
33}
34
35func appendPropertiesTestCases() []appendPropertyTestCase {
36	return []appendPropertyTestCase{
37		// Valid inputs
38
39		{
40			name: "Append bool",
41			dst: &struct{ B1, B2, B3, B4 bool }{
42				B1: true,
43				B2: false,
44				B3: true,
45				B4: false,
46			},
47			src: &struct{ B1, B2, B3, B4 bool }{
48				B1: true,
49				B2: true,
50				B3: false,
51				B4: false,
52			},
53			out: &struct{ B1, B2, B3, B4 bool }{
54				B1: true,
55				B2: true,
56				B3: true,
57				B4: false,
58			},
59		},
60		{
61			name: "Prepend bool",
62			dst: &struct{ B1, B2, B3, B4 bool }{
63				B1: true,
64				B2: false,
65				B3: true,
66				B4: false,
67			},
68			src: &struct{ B1, B2, B3, B4 bool }{
69				B1: true,
70				B2: true,
71				B3: false,
72				B4: false,
73			},
74			out: &struct{ B1, B2, B3, B4 bool }{
75				B1: true,
76				B2: true,
77				B3: true,
78				B4: false,
79			},
80			order: Prepend,
81		},
82		{
83			name: "Append strings",
84			dst: &struct{ S string }{
85				S: "string1",
86			},
87			src: &struct{ S string }{
88				S: "string2",
89			},
90			out: &struct{ S string }{
91				S: "string1string2",
92			},
93		},
94		{
95			name: "Prepend strings",
96			dst: &struct{ S string }{
97				S: "string1",
98			},
99			src: &struct{ S string }{
100				S: "string2",
101			},
102			out: &struct{ S string }{
103				S: "string2string1",
104			},
105			order: Prepend,
106		},
107		{
108			name: "Append pointer to bool",
109			dst: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
110				B1: BoolPtr(true),
111				B2: BoolPtr(false),
112				B3: nil,
113				B4: BoolPtr(true),
114				B5: BoolPtr(false),
115				B6: nil,
116				B7: BoolPtr(true),
117				B8: BoolPtr(false),
118				B9: nil,
119			},
120			src: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
121				B1: nil,
122				B2: nil,
123				B3: nil,
124				B4: BoolPtr(true),
125				B5: BoolPtr(true),
126				B6: BoolPtr(true),
127				B7: BoolPtr(false),
128				B8: BoolPtr(false),
129				B9: BoolPtr(false),
130			},
131			out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
132				B1: BoolPtr(true),
133				B2: BoolPtr(false),
134				B3: nil,
135				B4: BoolPtr(true),
136				B5: BoolPtr(true),
137				B6: BoolPtr(true),
138				B7: BoolPtr(false),
139				B8: BoolPtr(false),
140				B9: BoolPtr(false),
141			},
142		},
143		{
144			name: "Prepend pointer to bool",
145			dst: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
146				B1: BoolPtr(true),
147				B2: BoolPtr(false),
148				B3: nil,
149				B4: BoolPtr(true),
150				B5: BoolPtr(false),
151				B6: nil,
152				B7: BoolPtr(true),
153				B8: BoolPtr(false),
154				B9: nil,
155			},
156			src: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
157				B1: nil,
158				B2: nil,
159				B3: nil,
160				B4: BoolPtr(true),
161				B5: BoolPtr(true),
162				B6: BoolPtr(true),
163				B7: BoolPtr(false),
164				B8: BoolPtr(false),
165				B9: BoolPtr(false),
166			},
167			out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
168				B1: BoolPtr(true),
169				B2: BoolPtr(false),
170				B3: nil,
171				B4: BoolPtr(true),
172				B5: BoolPtr(false),
173				B6: BoolPtr(true),
174				B7: BoolPtr(true),
175				B8: BoolPtr(false),
176				B9: BoolPtr(false),
177			},
178			order: Prepend,
179		},
180		{
181			name: "Append pointer to integer",
182			dst: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{
183				I1: Int64Ptr(55),
184				I2: Int64Ptr(-3),
185				I3: nil,
186				I4: Int64Ptr(100),
187				I5: Int64Ptr(33),
188				I6: nil,
189				I7: Int64Ptr(77),
190				I8: Int64Ptr(0),
191				I9: nil,
192			},
193			src: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{
194				I1: nil,
195				I2: nil,
196				I3: nil,
197				I4: Int64Ptr(1),
198				I5: Int64Ptr(-2),
199				I6: Int64Ptr(8),
200				I7: Int64Ptr(9),
201				I8: Int64Ptr(10),
202				I9: Int64Ptr(11),
203			},
204			out: &struct{ I1, I2, I3, I4, I5, I6, I7, I8, I9 *int64 }{
205				I1: Int64Ptr(55),
206				I2: Int64Ptr(-3),
207				I3: nil,
208				I4: Int64Ptr(1),
209				I5: Int64Ptr(-2),
210				I6: Int64Ptr(8),
211				I7: Int64Ptr(9),
212				I8: Int64Ptr(10),
213				I9: Int64Ptr(11),
214			},
215		},
216		{
217			name: "Prepend pointer to integer",
218			dst: &struct{ I1, I2, I3 *int64 }{
219				I1: Int64Ptr(55),
220				I3: nil,
221			},
222			src: &struct{ I1, I2, I3 *int64 }{
223				I2: Int64Ptr(33),
224			},
225			out: &struct{ I1, I2, I3 *int64 }{
226				I1: Int64Ptr(55),
227				I2: Int64Ptr(33),
228				I3: nil,
229			},
230			order: Prepend,
231		},
232		{
233			name: "Append pointer to strings",
234			dst: &struct{ S1, S2, S3, S4 *string }{
235				S1: StringPtr("string1"),
236				S2: StringPtr("string2"),
237			},
238			src: &struct{ S1, S2, S3, S4 *string }{
239				S1: StringPtr("string3"),
240				S3: StringPtr("string4"),
241			},
242			out: &struct{ S1, S2, S3, S4 *string }{
243				S1: StringPtr("string3"),
244				S2: StringPtr("string2"),
245				S3: StringPtr("string4"),
246				S4: nil,
247			},
248		},
249		{
250			name: "Prepend pointer to strings",
251			dst: &struct{ S1, S2, S3, S4 *string }{
252				S1: StringPtr("string1"),
253				S2: StringPtr("string2"),
254			},
255			src: &struct{ S1, S2, S3, S4 *string }{
256				S1: StringPtr("string3"),
257				S3: StringPtr("string4"),
258			},
259			out: &struct{ S1, S2, S3, S4 *string }{
260				S1: StringPtr("string1"),
261				S2: StringPtr("string2"),
262				S3: StringPtr("string4"),
263				S4: nil,
264			},
265			order: Prepend,
266		},
267		{
268			name: "Append slice",
269			dst: &struct{ S []string }{
270				S: []string{"string1"},
271			},
272			src: &struct{ S []string }{
273				S: []string{"string2"},
274			},
275			out: &struct{ S []string }{
276				S: []string{"string1", "string2"},
277			},
278		},
279		{
280			name: "Prepend slice",
281			dst: &struct{ S []string }{
282				S: []string{"string1"},
283			},
284			src: &struct{ S []string }{
285				S: []string{"string2"},
286			},
287			out: &struct{ S []string }{
288				S: []string{"string2", "string1"},
289			},
290			order: Prepend,
291		},
292		{
293			name: "Replace slice",
294			dst: &struct{ S []string }{
295				S: []string{"string1"},
296			},
297			src: &struct{ S []string }{
298				S: []string{"string2"},
299			},
300			out: &struct{ S []string }{
301				S: []string{"string2"},
302			},
303			order: Replace,
304		},
305		{
306			name: "Append empty slice",
307			dst: &struct{ S1, S2 []string }{
308				S1: []string{"string1"},
309				S2: []string{},
310			},
311			src: &struct{ S1, S2 []string }{
312				S1: []string{},
313				S2: []string{"string2"},
314			},
315			out: &struct{ S1, S2 []string }{
316				S1: []string{"string1"},
317				S2: []string{"string2"},
318			},
319		},
320		{
321			name: "Prepend empty slice",
322			dst: &struct{ S1, S2 []string }{
323				S1: []string{"string1"},
324				S2: []string{},
325			},
326			src: &struct{ S1, S2 []string }{
327				S1: []string{},
328				S2: []string{"string2"},
329			},
330			out: &struct{ S1, S2 []string }{
331				S1: []string{"string1"},
332				S2: []string{"string2"},
333			},
334			order: Prepend,
335		},
336		{
337			name: "Replace empty slice",
338			dst: &struct{ S1, S2 []string }{
339				S1: []string{"string1"},
340				S2: []string{},
341			},
342			src: &struct{ S1, S2 []string }{
343				S1: []string{},
344				S2: []string{"string2"},
345			},
346			out: &struct{ S1, S2 []string }{
347				S1: []string{},
348				S2: []string{"string2"},
349			},
350			order: Replace,
351		},
352		{
353			name: "Append nil slice",
354			dst: &struct{ S1, S2, S3 []string }{
355				S1: []string{"string1"},
356			},
357			src: &struct{ S1, S2, S3 []string }{
358				S2: []string{"string2"},
359			},
360			out: &struct{ S1, S2, S3 []string }{
361				S1: []string{"string1"},
362				S2: []string{"string2"},
363				S3: nil,
364			},
365		},
366		{
367			name: "Prepend nil slice",
368			dst: &struct{ S1, S2, S3 []string }{
369				S1: []string{"string1"},
370			},
371			src: &struct{ S1, S2, S3 []string }{
372				S2: []string{"string2"},
373			},
374			out: &struct{ S1, S2, S3 []string }{
375				S1: []string{"string1"},
376				S2: []string{"string2"},
377				S3: nil,
378			},
379			order: Prepend,
380		},
381		{
382			name: "Replace nil slice",
383			dst: &struct{ S1, S2, S3 []string }{
384				S1: []string{"string1"},
385			},
386			src: &struct{ S1, S2, S3 []string }{
387				S2: []string{"string2"},
388			},
389			out: &struct{ S1, S2, S3 []string }{
390				S1: []string{"string1"},
391				S2: []string{"string2"},
392				S3: nil,
393			},
394			order: Replace,
395		},
396		{
397			name: "Replace embedded slice",
398			dst: &struct{ S *struct{ S1 []string } }{
399				S: &struct{ S1 []string }{
400					S1: []string{"string1"},
401				},
402			},
403			src: &struct{ S *struct{ S1 []string } }{
404				S: &struct{ S1 []string }{
405					S1: []string{"string2"},
406				},
407			},
408			out: &struct{ S *struct{ S1 []string } }{
409				S: &struct{ S1 []string }{
410					S1: []string{"string2"},
411				},
412			},
413			order: Replace,
414		},
415		{
416			name: "Append slice of structs",
417			dst: &struct{ S []struct{ F string } }{
418				S: []struct{ F string }{
419					{F: "foo"}, {F: "bar"},
420				},
421			},
422			src: &struct{ S []struct{ F string } }{
423				S: []struct{ F string }{
424					{F: "baz"},
425				},
426			},
427			out: &struct{ S []struct{ F string } }{
428				S: []struct{ F string }{
429					{F: "foo"}, {F: "bar"}, {F: "baz"},
430				},
431			},
432			order: Append,
433		},
434		{
435			name: "Prepend slice of structs",
436			dst: &struct{ S []struct{ F string } }{
437				S: []struct{ F string }{
438					{F: "foo"}, {F: "bar"},
439				},
440			},
441			src: &struct{ S []struct{ F string } }{
442				S: []struct{ F string }{
443					{F: "baz"},
444				},
445			},
446			out: &struct{ S []struct{ F string } }{
447				S: []struct{ F string }{
448					{F: "baz"}, {F: "foo"}, {F: "bar"},
449				},
450			},
451			order: Prepend,
452		},
453		{
454			name: "Append map",
455			dst: &struct{ S map[string]string }{
456				S: map[string]string{
457					"key0": "",
458					"key1": "dst_value1",
459					"key2": "dst_value2",
460				},
461			},
462			src: &struct{ S map[string]string }{
463				S: map[string]string{
464					"key0": "src_value0",
465					"key1": "src_value1",
466					"key3": "src_value3",
467				},
468			},
469			out: &struct{ S map[string]string }{
470				S: map[string]string{
471					"key0": "src_value0",
472					"key1": "src_value1",
473					"key2": "dst_value2",
474					"key3": "src_value3",
475				},
476			},
477			order: Append,
478		},
479		{
480			name: "Prepend map",
481			dst: &struct{ S map[string]string }{
482				S: map[string]string{
483					"key0": "",
484					"key1": "dst_value1",
485					"key2": "dst_value2",
486				},
487			},
488			src: &struct{ S map[string]string }{
489				S: map[string]string{
490					"key0": "src_value0",
491					"key1": "src_value1",
492					"key3": "src_value3",
493				},
494			},
495			out: &struct{ S map[string]string }{
496				S: map[string]string{
497					"key0": "",
498					"key1": "dst_value1",
499					"key2": "dst_value2",
500					"key3": "src_value3",
501				},
502			},
503			order: Prepend,
504		},
505		{
506			name: "Replace map",
507			dst: &struct{ S map[string]string }{
508				S: map[string]string{
509					"key0": "",
510					"key1": "dst_value1",
511					"key2": "dst_value2",
512				},
513			},
514			src: &struct{ S map[string]string }{
515				S: map[string]string{
516					"key0": "src_value0",
517					"key1": "src_value1",
518					"key3": "src_value3",
519				},
520			},
521			out: &struct{ S map[string]string }{
522				S: map[string]string{
523					"key0": "src_value0",
524					"key1": "src_value1",
525					"key3": "src_value3",
526				},
527			},
528			order: Replace,
529		},
530		{
531			name: "Append empty map",
532			dst: &struct{ S1, S2 map[string]string }{
533				S1: map[string]string{"key0": "dst_value0"},
534				S2: map[string]string{},
535			},
536			src: &struct{ S1, S2 map[string]string }{
537				S1: map[string]string{},
538				S2: map[string]string{"key0": "src_value0"},
539			},
540			out: &struct{ S1, S2 map[string]string }{
541				S1: map[string]string{"key0": "dst_value0"},
542				S2: map[string]string{"key0": "src_value0"},
543			},
544			order: Append,
545		},
546		{
547			name: "Prepend empty map",
548			dst: &struct{ S1, S2 map[string]string }{
549				S1: map[string]string{"key0": "dst_value0"},
550				S2: map[string]string{},
551			},
552			src: &struct{ S1, S2 map[string]string }{
553				S1: map[string]string{},
554				S2: map[string]string{"key0": "src_value0"},
555			},
556			out: &struct{ S1, S2 map[string]string }{
557				S1: map[string]string{"key0": "dst_value0"},
558				S2: map[string]string{"key0": "src_value0"},
559			},
560			order: Prepend,
561		},
562		{
563			name: "Replace empty map",
564			dst: &struct{ S1, S2 map[string]string }{
565				S1: map[string]string{"key0": "dst_value0"},
566				S2: map[string]string{},
567			},
568			src: &struct{ S1, S2 map[string]string }{
569				S1: map[string]string{},
570				S2: map[string]string{"key0": "src_value0"},
571			},
572			out: &struct{ S1, S2 map[string]string }{
573				S1: map[string]string{},
574				S2: map[string]string{"key0": "src_value0"},
575			},
576			order: Replace,
577		},
578		{
579			name: "Append nil map",
580			dst: &struct{ S1, S2, S3 map[string]string }{
581				S1: map[string]string{"key0": "dst_value0"},
582			},
583			src: &struct{ S1, S2, S3 map[string]string }{
584				S2: map[string]string{"key0": "src_value0"},
585			},
586			out: &struct{ S1, S2, S3 map[string]string }{
587				S1: map[string]string{"key0": "dst_value0"},
588				S2: map[string]string{"key0": "src_value0"},
589			},
590			order: Append,
591		},
592		{
593			name: "Prepend nil map",
594			dst: &struct{ S1, S2, S3 map[string]string }{
595				S1: map[string]string{"key0": "dst_value0"},
596			},
597			src: &struct{ S1, S2, S3 map[string]string }{
598				S2: map[string]string{"key0": "src_value0"},
599			},
600			out: &struct{ S1, S2, S3 map[string]string }{
601				S1: map[string]string{"key0": "dst_value0"},
602				S2: map[string]string{"key0": "src_value0"},
603			},
604			order: Prepend,
605		},
606		{
607			name: "Replace nil map",
608			dst: &struct{ S1, S2, S3 map[string]string }{
609				S1: map[string]string{"key0": "dst_value0"},
610			},
611			src: &struct{ S1, S2, S3 map[string]string }{
612				S2: map[string]string{"key0": "src_value0"},
613			},
614			out: &struct{ S1, S2, S3 map[string]string }{
615				S1: map[string]string{"key0": "dst_value0"},
616				S2: map[string]string{"key0": "src_value0"},
617				S3: nil,
618			},
619			order: Replace,
620		},
621		{
622			name: "Replace slice of structs",
623			dst: &struct{ S []struct{ F string } }{
624				S: []struct{ F string }{
625					{F: "foo"}, {F: "bar"},
626				},
627			},
628			src: &struct{ S []struct{ F string } }{
629				S: []struct{ F string }{
630					{F: "baz"},
631				},
632			},
633			out: &struct{ S []struct{ F string } }{
634				S: []struct{ F string }{
635					{F: "baz"},
636				},
637			},
638			order: Replace,
639		},
640		{
641			name: "Append pointer",
642			dst: &struct{ S *struct{ S string } }{
643				S: &struct{ S string }{
644					S: "string1",
645				},
646			},
647			src: &struct{ S *struct{ S string } }{
648				S: &struct{ S string }{
649					S: "string2",
650				},
651			},
652			out: &struct{ S *struct{ S string } }{
653				S: &struct{ S string }{
654					S: "string1string2",
655				},
656			},
657		},
658		{
659			name: "Prepend pointer",
660			dst: &struct{ S *struct{ S string } }{
661				S: &struct{ S string }{
662					S: "string1",
663				},
664			},
665			src: &struct{ S *struct{ S string } }{
666				S: &struct{ S string }{
667					S: "string2",
668				},
669			},
670			out: &struct{ S *struct{ S string } }{
671				S: &struct{ S string }{
672					S: "string2string1",
673				},
674			},
675			order: Prepend,
676		},
677		{
678			name: "Append interface",
679			dst: &struct{ S interface{} }{
680				S: &struct{ S string }{
681					S: "string1",
682				},
683			},
684			src: &struct{ S interface{} }{
685				S: &struct{ S string }{
686					S: "string2",
687				},
688			},
689			out: &struct{ S interface{} }{
690				S: &struct{ S string }{
691					S: "string1string2",
692				},
693			},
694		},
695		{
696			name: "Prepend interface",
697			dst: &struct{ S interface{} }{
698				S: &struct{ S string }{
699					S: "string1",
700				},
701			},
702			src: &struct{ S interface{} }{
703				S: &struct{ S string }{
704					S: "string2",
705				},
706			},
707			out: &struct{ S interface{} }{
708				S: &struct{ S string }{
709					S: "string2string1",
710				},
711			},
712			order: Prepend,
713		},
714		{
715			name: "Unexported field",
716			dst: &struct{ s string }{
717				s: "string1",
718			},
719			src: &struct{ s string }{
720				s: "string2",
721			},
722			out: &struct{ s string }{
723				s: "string1",
724			},
725		},
726		{
727			name: "Unexported field",
728			dst: &struct{ i *int64 }{
729				i: Int64Ptr(33),
730			},
731			src: &struct{ i *int64 }{
732				i: Int64Ptr(5),
733			},
734			out: &struct{ i *int64 }{
735				i: Int64Ptr(33),
736			},
737		},
738		{
739			name: "Empty struct",
740			dst:  &struct{}{},
741			src:  &struct{}{},
742			out:  &struct{}{},
743		},
744		{
745			name: "Interface nil",
746			dst: &struct{ S interface{} }{
747				S: nil,
748			},
749			src: &struct{ S interface{} }{
750				S: nil,
751			},
752			out: &struct{ S interface{} }{
753				S: nil,
754			},
755		},
756		{
757			name: "Pointer nil",
758			dst: &struct{ S *struct{} }{
759				S: nil,
760			},
761			src: &struct{ S *struct{} }{
762				S: nil,
763			},
764			out: &struct{ S *struct{} }{
765				S: nil,
766			},
767		},
768		{
769			name: "Anonymous struct",
770			dst: &struct {
771				EmbeddedStruct
772				Nested struct{ EmbeddedStruct }
773			}{
774				EmbeddedStruct: EmbeddedStruct{
775					S: "string1",
776					I: Int64Ptr(55),
777				},
778				Nested: struct{ EmbeddedStruct }{
779					EmbeddedStruct: EmbeddedStruct{
780						S: "string2",
781						I: Int64Ptr(-4),
782					},
783				},
784			},
785			src: &struct {
786				EmbeddedStruct
787				Nested struct{ EmbeddedStruct }
788			}{
789				EmbeddedStruct: EmbeddedStruct{
790					S: "string3",
791					I: Int64Ptr(66),
792				},
793				Nested: struct{ EmbeddedStruct }{
794					EmbeddedStruct: EmbeddedStruct{
795						S: "string4",
796						I: Int64Ptr(-8),
797					},
798				},
799			},
800			out: &struct {
801				EmbeddedStruct
802				Nested struct{ EmbeddedStruct }
803			}{
804				EmbeddedStruct: EmbeddedStruct{
805					S: "string1string3",
806					I: Int64Ptr(66),
807				},
808				Nested: struct{ EmbeddedStruct }{
809					EmbeddedStruct: EmbeddedStruct{
810						S: "string2string4",
811						I: Int64Ptr(-8),
812					},
813				},
814			},
815		},
816		{
817			name: "BlueprintEmbed struct",
818			dst: &struct {
819				BlueprintEmbed EmbeddedStruct
820				Nested         struct{ BlueprintEmbed EmbeddedStruct }
821			}{
822				BlueprintEmbed: EmbeddedStruct{
823					S: "string1",
824					I: Int64Ptr(55),
825				},
826				Nested: struct{ BlueprintEmbed EmbeddedStruct }{
827					BlueprintEmbed: EmbeddedStruct{
828						S: "string2",
829						I: Int64Ptr(-4),
830					},
831				},
832			},
833			src: &struct {
834				BlueprintEmbed EmbeddedStruct
835				Nested         struct{ BlueprintEmbed EmbeddedStruct }
836			}{
837				BlueprintEmbed: EmbeddedStruct{
838					S: "string3",
839					I: Int64Ptr(66),
840				},
841				Nested: struct{ BlueprintEmbed EmbeddedStruct }{
842					BlueprintEmbed: EmbeddedStruct{
843						S: "string4",
844						I: Int64Ptr(-8),
845					},
846				},
847			},
848			out: &struct {
849				BlueprintEmbed EmbeddedStruct
850				Nested         struct{ BlueprintEmbed EmbeddedStruct }
851			}{
852				BlueprintEmbed: EmbeddedStruct{
853					S: "string1string3",
854					I: Int64Ptr(66),
855				},
856				Nested: struct{ BlueprintEmbed EmbeddedStruct }{
857					BlueprintEmbed: EmbeddedStruct{
858						S: "string2string4",
859						I: Int64Ptr(-8),
860					},
861				},
862			},
863		},
864		{
865			name: "Anonymous interface",
866			dst: &struct {
867				EmbeddedInterface
868				Nested struct{ EmbeddedInterface }
869			}{
870				EmbeddedInterface: &struct {
871					S string
872					I *int64
873				}{
874					S: "string1",
875					I: Int64Ptr(-8),
876				},
877				Nested: struct{ EmbeddedInterface }{
878					EmbeddedInterface: &struct {
879						S string
880						I *int64
881					}{
882						S: "string2",
883						I: Int64Ptr(55),
884					},
885				},
886			},
887			src: &struct {
888				EmbeddedInterface
889				Nested struct{ EmbeddedInterface }
890			}{
891				EmbeddedInterface: &struct {
892					S string
893					I *int64
894				}{
895					S: "string3",
896					I: Int64Ptr(6),
897				},
898				Nested: struct{ EmbeddedInterface }{
899					EmbeddedInterface: &struct {
900						S string
901						I *int64
902					}{
903						S: "string4",
904						I: Int64Ptr(6),
905					},
906				},
907			},
908			out: &struct {
909				EmbeddedInterface
910				Nested struct{ EmbeddedInterface }
911			}{
912				EmbeddedInterface: &struct {
913					S string
914					I *int64
915				}{
916					S: "string1string3",
917					I: Int64Ptr(6),
918				},
919				Nested: struct{ EmbeddedInterface }{
920					EmbeddedInterface: &struct {
921						S string
922						I *int64
923					}{
924						S: "string2string4",
925						I: Int64Ptr(6),
926					},
927				},
928			},
929		},
930		{
931			name: "Nil pointer to a struct",
932			dst: &struct {
933				Nested *struct {
934					S string
935				}
936			}{},
937			src: &struct {
938				Nested *struct {
939					S string
940				}
941			}{
942				Nested: &struct {
943					S string
944				}{
945					S: "string",
946				},
947			},
948			out: &struct {
949				Nested *struct {
950					S string
951				}
952			}{
953				Nested: &struct {
954					S string
955				}{
956					S: "string",
957				},
958			},
959		},
960		{
961			name: "Nil pointer to a struct in an interface",
962			dst: &struct {
963				Nested interface{}
964			}{
965				Nested: (*struct{ S string })(nil),
966			},
967			src: &struct {
968				Nested interface{}
969			}{
970				Nested: &struct {
971					S string
972				}{
973					S: "string",
974				},
975			},
976			out: &struct {
977				Nested interface{}
978			}{
979				Nested: &struct {
980					S string
981				}{
982					S: "string",
983				},
984			},
985		},
986		{
987			name: "Interface src nil",
988			dst: &struct{ S interface{} }{
989				S: &struct{ S string }{
990					S: "string1",
991				},
992			},
993			src: &struct{ S interface{} }{
994				S: nil,
995			},
996			out: &struct{ S interface{} }{
997				S: &struct{ S string }{
998					S: "string1",
999				},
1000			},
1001		},
1002
1003		// Errors
1004
1005		{
1006			name: "Non-pointer dst",
1007			dst:  struct{}{},
1008			src:  &struct{}{},
1009			err:  errors.New("expected pointer to struct, got struct {}"),
1010			out:  struct{}{},
1011		},
1012		{
1013			name: "Non-pointer src",
1014			dst:  &struct{}{},
1015			src:  struct{}{},
1016			err:  errors.New("expected pointer to struct, got struct {}"),
1017			out:  &struct{}{},
1018		},
1019		{
1020			name: "Non-struct dst",
1021			dst:  &[]string{"bad"},
1022			src:  &struct{}{},
1023			err:  errors.New("expected pointer to struct, got *[]string"),
1024			out:  &[]string{"bad"},
1025		},
1026		{
1027			name: "Non-struct src",
1028			dst:  &struct{}{},
1029			src:  &[]string{"bad"},
1030			err:  errors.New("expected pointer to struct, got *[]string"),
1031			out:  &struct{}{},
1032		},
1033		{
1034			name: "Mismatched types",
1035			dst: &struct{ A string }{
1036				A: "string1",
1037			},
1038			src: &struct{ B string }{
1039				B: "string2",
1040			},
1041			out: &struct{ A string }{
1042				A: "string1",
1043			},
1044			err: errors.New("expected matching types for dst and src, got *struct { A string } and *struct { B string }"),
1045		},
1046		{
1047			name: "Unsupported kind",
1048			dst: &struct{ I int }{
1049				I: 1,
1050			},
1051			src: &struct{ I int }{
1052				I: 2,
1053			},
1054			out: &struct{ I int }{
1055				I: 1,
1056			},
1057			err: extendPropertyErrorf("i", "unsupported kind int"),
1058		},
1059		{
1060			name: "Unsupported kind",
1061			dst: &struct{ I int64 }{
1062				I: 1,
1063			},
1064			src: &struct{ I int64 }{
1065				I: 2,
1066			},
1067			out: &struct{ I int64 }{
1068				I: 1,
1069			},
1070			err: extendPropertyErrorf("i", "unsupported kind int64"),
1071		},
1072		{
1073			name: "Interface nilitude mismatch",
1074			dst: &struct{ S interface{} }{
1075				S: nil,
1076			},
1077			src: &struct{ S interface{} }{
1078				S: &struct{ S string }{
1079					S: "string1",
1080				},
1081			},
1082			out: &struct{ S interface{} }{
1083				S: nil,
1084			},
1085			err: extendPropertyErrorf("s", "nilitude mismatch"),
1086		},
1087		{
1088			name: "Interface type mismatch",
1089			dst: &struct{ S interface{} }{
1090				S: &struct{ A string }{
1091					A: "string1",
1092				},
1093			},
1094			src: &struct{ S interface{} }{
1095				S: &struct{ B string }{
1096					B: "string2",
1097				},
1098			},
1099			out: &struct{ S interface{} }{
1100				S: &struct{ A string }{
1101					A: "string1",
1102				},
1103			},
1104			err: extendPropertyErrorf("s", "mismatched types struct { A string } and struct { B string }"),
1105		},
1106		{
1107			name: "Interface not a pointer",
1108			dst: &struct{ S interface{} }{
1109				S: struct{ S string }{
1110					S: "string1",
1111				},
1112			},
1113			src: &struct{ S interface{} }{
1114				S: struct{ S string }{
1115					S: "string2",
1116				},
1117			},
1118			out: &struct{ S interface{} }{
1119				S: struct{ S string }{
1120					S: "string1",
1121				},
1122			},
1123			err: extendPropertyErrorf("s", "interface not a pointer"),
1124		},
1125		{
1126			name: "Pointer not a struct",
1127			dst: &struct{ S *[]string }{
1128				S: &[]string{"string1"},
1129			},
1130			src: &struct{ S *[]string }{
1131				S: &[]string{"string2"},
1132			},
1133			out: &struct{ S *[]string }{
1134				S: &[]string{"string1"},
1135			},
1136			err: extendPropertyErrorf("s", "pointer is a slice"),
1137		},
1138		{
1139			name: "Error in nested struct",
1140			dst: &struct{ S interface{} }{
1141				S: &struct{ I int }{
1142					I: 1,
1143				},
1144			},
1145			src: &struct{ S interface{} }{
1146				S: &struct{ I int }{
1147					I: 2,
1148				},
1149			},
1150			out: &struct{ S interface{} }{
1151				S: &struct{ I int }{
1152					I: 1,
1153				},
1154			},
1155			err: extendPropertyErrorf("s.i", "unsupported kind int"),
1156		},
1157
1158		// Filters
1159
1160		{
1161			name: "Filter true",
1162			dst: &struct{ S string }{
1163				S: "string1",
1164			},
1165			src: &struct{ S string }{
1166				S: "string2",
1167			},
1168			out: &struct{ S string }{
1169				S: "string1string2",
1170			},
1171			filter: func(property string,
1172				dstField, srcField reflect.StructField,
1173				dstValue, srcValue interface{}) (bool, error) {
1174				return true, nil
1175			},
1176		},
1177		{
1178			name: "Filter false",
1179			dst: &struct{ S string }{
1180				S: "string1",
1181			},
1182			src: &struct{ S string }{
1183				S: "string2",
1184			},
1185			out: &struct{ S string }{
1186				S: "string1",
1187			},
1188			filter: func(property string,
1189				dstField, srcField reflect.StructField,
1190				dstValue, srcValue interface{}) (bool, error) {
1191				return false, nil
1192			},
1193		},
1194		{
1195			name: "Filter check args",
1196			dst: &struct{ S string }{
1197				S: "string1",
1198			},
1199			src: &struct{ S string }{
1200				S: "string2",
1201			},
1202			out: &struct{ S string }{
1203				S: "string1string2",
1204			},
1205			filter: func(property string,
1206				dstField, srcField reflect.StructField,
1207				dstValue, srcValue interface{}) (bool, error) {
1208				return property == "s" &&
1209					dstField.Name == "S" && srcField.Name == "S" &&
1210					dstValue.(string) == "string1" && srcValue.(string) == "string2", nil
1211			},
1212		},
1213		{
1214			name: "Filter mutated",
1215			dst: &struct {
1216				S string `blueprint:"mutated"`
1217			}{
1218				S: "string1",
1219			},
1220			src: &struct {
1221				S string `blueprint:"mutated"`
1222			}{
1223				S: "string2",
1224			},
1225			out: &struct {
1226				S string `blueprint:"mutated"`
1227			}{
1228				S: "string1",
1229			},
1230		},
1231		{
1232			name: "Filter mutated",
1233			dst: &struct {
1234				S *int64 `blueprint:"mutated"`
1235			}{
1236				S: Int64Ptr(4),
1237			},
1238			src: &struct {
1239				S *int64 `blueprint:"mutated"`
1240			}{
1241				S: Int64Ptr(5),
1242			},
1243			out: &struct {
1244				S *int64 `blueprint:"mutated"`
1245			}{
1246				S: Int64Ptr(4),
1247			},
1248		},
1249		{
1250			name: "Filter error",
1251			dst: &struct{ S string }{
1252				S: "string1",
1253			},
1254			src: &struct{ S string }{
1255				S: "string2",
1256			},
1257			out: &struct{ S string }{
1258				S: "string1",
1259			},
1260			filter: func(property string,
1261				dstField, srcField reflect.StructField,
1262				dstValue, srcValue interface{}) (bool, error) {
1263				return true, fmt.Errorf("filter error")
1264			},
1265			err: extendPropertyErrorf("s", "filter error"),
1266		},
1267	}
1268}
1269
1270func TestAppendProperties(t *testing.T) {
1271	for _, testCase := range appendPropertiesTestCases() {
1272		t.Run(testCase.name, func(t *testing.T) {
1273
1274			got := testCase.dst
1275			var err error
1276			var testType string
1277
1278			switch testCase.order {
1279			case Append:
1280				testType = "append"
1281				err = AppendProperties(got, testCase.src, testCase.filter)
1282			case Prepend:
1283				testType = "prepend"
1284				err = PrependProperties(got, testCase.src, testCase.filter)
1285			case Replace:
1286				testType = "replace"
1287				err = ExtendProperties(got, testCase.src, testCase.filter, OrderReplace)
1288			}
1289
1290			check(t, testType, testCase.name, got, err, testCase.out, testCase.err)
1291		})
1292	}
1293}
1294
1295func TestExtendProperties(t *testing.T) {
1296	for _, testCase := range appendPropertiesTestCases() {
1297		t.Run(testCase.name, func(t *testing.T) {
1298
1299			got := testCase.dst
1300			var err error
1301			var testType string
1302
1303			order := func(property string,
1304				dstField, srcField reflect.StructField,
1305				dstValue, srcValue interface{}) (Order, error) {
1306				switch testCase.order {
1307				case Append:
1308					return Append, nil
1309				case Prepend:
1310					return Prepend, nil
1311				case Replace:
1312					return Replace, nil
1313				}
1314				return Append, errors.New("unknown order")
1315			}
1316
1317			switch testCase.order {
1318			case Append:
1319				testType = "prepend"
1320			case Prepend:
1321				testType = "append"
1322			case Replace:
1323				testType = "replace"
1324			}
1325
1326			err = ExtendProperties(got, testCase.src, testCase.filter, order)
1327
1328			check(t, testType, testCase.name, got, err, testCase.out, testCase.err)
1329		})
1330	}
1331}
1332
1333type appendMatchingPropertiesTestCase struct {
1334	name   string
1335	dst    []interface{}
1336	src    interface{}
1337	out    []interface{}
1338	order  Order // default is Append
1339	filter ExtendPropertyFilterFunc
1340	err    error
1341}
1342
1343func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
1344	return []appendMatchingPropertiesTestCase{
1345		{
1346			name: "Append strings",
1347			dst: []interface{}{&struct{ S string }{
1348				S: "string1",
1349			}},
1350			src: &struct{ S string }{
1351				S: "string2",
1352			},
1353			out: []interface{}{&struct{ S string }{
1354				S: "string1string2",
1355			}},
1356		},
1357		{
1358			name: "Prepend strings",
1359			dst: []interface{}{&struct{ S string }{
1360				S: "string1",
1361			}},
1362			src: &struct{ S string }{
1363				S: "string2",
1364			},
1365			out: []interface{}{&struct{ S string }{
1366				S: "string2string1",
1367			}},
1368			order: Prepend,
1369		},
1370		{
1371			name: "Append all",
1372			dst: []interface{}{
1373				&struct{ S, A string }{
1374					S: "string1",
1375				},
1376				&struct{ S, B string }{
1377					S: "string2",
1378				},
1379			},
1380			src: &struct{ S string }{
1381				S: "string3",
1382			},
1383			out: []interface{}{
1384				&struct{ S, A string }{
1385					S: "string1string3",
1386				},
1387				&struct{ S, B string }{
1388					S: "string2string3",
1389				},
1390			},
1391		},
1392		{
1393			name: "Append some",
1394			dst: []interface{}{
1395				&struct{ S, A string }{
1396					S: "string1",
1397				},
1398				&struct{ B string }{},
1399			},
1400			src: &struct{ S string }{
1401				S: "string2",
1402			},
1403			out: []interface{}{
1404				&struct{ S, A string }{
1405					S: "string1string2",
1406				},
1407				&struct{ B string }{},
1408			},
1409		},
1410		{
1411			name: "Append mismatched structs",
1412			dst: []interface{}{&struct{ S, A string }{
1413				S: "string1",
1414			}},
1415			src: &struct{ S string }{
1416				S: "string2",
1417			},
1418			out: []interface{}{&struct{ S, A string }{
1419				S: "string1string2",
1420			}},
1421		},
1422		{
1423			name: "Append mismatched pointer structs",
1424			dst: []interface{}{&struct{ S *struct{ S, A string } }{
1425				S: &struct{ S, A string }{
1426					S: "string1",
1427				},
1428			}},
1429			src: &struct{ S *struct{ S string } }{
1430				S: &struct{ S string }{
1431					S: "string2",
1432				},
1433			},
1434			out: []interface{}{&struct{ S *struct{ S, A string } }{
1435				S: &struct{ S, A string }{
1436					S: "string1string2",
1437				},
1438			}},
1439		},
1440		{
1441			name: "Append through mismatched types",
1442			dst: []interface{}{
1443				&struct{ B string }{},
1444				&struct{ S interface{} }{
1445					S: &struct{ S, A string }{
1446						S: "string1",
1447					},
1448				},
1449			},
1450			src: &struct{ S struct{ S string } }{
1451				S: struct{ S string }{
1452					S: "string2",
1453				},
1454			},
1455			out: []interface{}{
1456				&struct{ B string }{},
1457				&struct{ S interface{} }{
1458					S: &struct{ S, A string }{
1459						S: "string1string2",
1460					},
1461				},
1462			},
1463		},
1464		{
1465			name: "Append through mismatched types and nil",
1466			dst: []interface{}{
1467				&struct{ B string }{},
1468				&struct{ S interface{} }{
1469					S: (*struct{ S, A string })(nil),
1470				},
1471			},
1472			src: &struct{ S struct{ S string } }{
1473				S: struct{ S string }{
1474					S: "string2",
1475				},
1476			},
1477			out: []interface{}{
1478				&struct{ B string }{},
1479				&struct{ S interface{} }{
1480					S: &struct{ S, A string }{
1481						S: "string2",
1482					},
1483				},
1484			},
1485		},
1486		{
1487			name: "Append through multiple matches",
1488			dst: []interface{}{
1489				&struct {
1490					S struct{ S, A string }
1491				}{
1492					S: struct{ S, A string }{
1493						S: "string1",
1494					},
1495				},
1496				&struct {
1497					S struct{ S, B string }
1498				}{
1499					S: struct{ S, B string }{
1500						S: "string2",
1501					},
1502				},
1503			},
1504			src: &struct{ S struct{ B string } }{
1505				S: struct{ B string }{
1506					B: "string3",
1507				},
1508			},
1509			out: []interface{}{
1510				&struct {
1511					S struct{ S, A string }
1512				}{
1513					S: struct{ S, A string }{
1514						S: "string1",
1515					},
1516				},
1517				&struct {
1518					S struct{ S, B string }
1519				}{
1520					S: struct{ S, B string }{
1521						S: "string2",
1522						B: "string3",
1523					},
1524				},
1525			},
1526		},
1527		{
1528			name: "Append through embedded struct",
1529			dst: []interface{}{
1530				&struct{ B string }{},
1531				&struct{ EmbeddedStruct }{
1532					EmbeddedStruct: EmbeddedStruct{
1533						S: "string1",
1534					},
1535				},
1536			},
1537			src: &struct{ S string }{
1538				S: "string2",
1539			},
1540			out: []interface{}{
1541				&struct{ B string }{},
1542				&struct{ EmbeddedStruct }{
1543					EmbeddedStruct: EmbeddedStruct{
1544						S: "string1string2",
1545					},
1546				},
1547			},
1548		},
1549		{
1550			name: "Append through BlueprintEmbed struct",
1551			dst: []interface{}{
1552				&struct{ B string }{},
1553				&struct{ BlueprintEmbed EmbeddedStruct }{
1554					BlueprintEmbed: EmbeddedStruct{
1555						S: "string1",
1556					},
1557				},
1558			},
1559			src: &struct{ S string }{
1560				S: "string2",
1561			},
1562			out: []interface{}{
1563				&struct{ B string }{},
1564				&struct{ BlueprintEmbed EmbeddedStruct }{
1565					BlueprintEmbed: EmbeddedStruct{
1566						S: "string1string2",
1567					},
1568				},
1569			},
1570		},
1571		{
1572			name: "Append through embedded pointer to struct",
1573			dst: []interface{}{
1574				&struct{ B string }{},
1575				&struct{ *EmbeddedStruct }{
1576					EmbeddedStruct: &EmbeddedStruct{
1577						S: "string1",
1578					},
1579				},
1580			},
1581			src: &struct{ S string }{
1582				S: "string2",
1583			},
1584			out: []interface{}{
1585				&struct{ B string }{},
1586				&struct{ *EmbeddedStruct }{
1587					EmbeddedStruct: &EmbeddedStruct{
1588						S: "string1string2",
1589					},
1590				},
1591			},
1592		},
1593		{
1594			name: "Append through BlueprintEmbed pointer to struct",
1595			dst: []interface{}{
1596				&struct{ B string }{},
1597				&struct{ BlueprintEmbed *EmbeddedStruct }{
1598					BlueprintEmbed: &EmbeddedStruct{
1599						S: "string1",
1600					},
1601				},
1602			},
1603			src: &struct{ S string }{
1604				S: "string2",
1605			},
1606			out: []interface{}{
1607				&struct{ B string }{},
1608				&struct{ BlueprintEmbed *EmbeddedStruct }{
1609					BlueprintEmbed: &EmbeddedStruct{
1610						S: "string1string2",
1611					},
1612				},
1613			},
1614		},
1615		{
1616			name: "Append through embedded nil pointer to struct",
1617			dst: []interface{}{
1618				&struct{ B string }{},
1619				&struct{ *EmbeddedStruct }{},
1620			},
1621			src: &struct{ S string }{
1622				S: "string2",
1623			},
1624			out: []interface{}{
1625				&struct{ B string }{},
1626				&struct{ *EmbeddedStruct }{
1627					EmbeddedStruct: &EmbeddedStruct{
1628						S: "string2",
1629					},
1630				},
1631			},
1632		},
1633		{
1634			name: "Append through BlueprintEmbed nil pointer to struct",
1635			dst: []interface{}{
1636				&struct{ B string }{},
1637				&struct{ BlueprintEmbed *EmbeddedStruct }{},
1638			},
1639			src: &struct{ S string }{
1640				S: "string2",
1641			},
1642			out: []interface{}{
1643				&struct{ B string }{},
1644				&struct{ BlueprintEmbed *EmbeddedStruct }{
1645					BlueprintEmbed: &EmbeddedStruct{
1646						S: "string2",
1647					},
1648				},
1649			},
1650		},
1651
1652		// Errors
1653
1654		{
1655			name: "Non-pointer dst",
1656			dst:  []interface{}{struct{}{}},
1657			src:  &struct{}{},
1658			err:  errors.New("expected pointer to struct, got struct {}"),
1659			out:  []interface{}{struct{}{}},
1660		},
1661		{
1662			name: "Non-pointer src",
1663			dst:  []interface{}{&struct{}{}},
1664			src:  struct{}{},
1665			err:  errors.New("expected pointer to struct, got struct {}"),
1666			out:  []interface{}{&struct{}{}},
1667		},
1668		{
1669			name: "Non-struct dst",
1670			dst:  []interface{}{&[]string{"bad"}},
1671			src:  &struct{}{},
1672			err:  errors.New("expected pointer to struct, got *[]string"),
1673			out:  []interface{}{&[]string{"bad"}},
1674		},
1675		{
1676			name: "Non-struct src",
1677			dst:  []interface{}{&struct{}{}},
1678			src:  &[]string{"bad"},
1679			err:  errors.New("expected pointer to struct, got *[]string"),
1680			out:  []interface{}{&struct{}{}},
1681		},
1682		{
1683			name: "Append none",
1684			dst: []interface{}{
1685				&struct{ A string }{},
1686				&struct{ B string }{},
1687			},
1688			src: &struct{ S string }{
1689				S: "string1",
1690			},
1691			out: []interface{}{
1692				&struct{ A string }{},
1693				&struct{ B string }{},
1694			},
1695			err: extendPropertyErrorf("s", "failed to find property to extend"),
1696		},
1697		{
1698			name: "Append mismatched kinds",
1699			dst: []interface{}{
1700				&struct{ S string }{
1701					S: "string1",
1702				},
1703			},
1704			src: &struct{ S []string }{
1705				S: []string{"string2"},
1706			},
1707			out: []interface{}{
1708				&struct{ S string }{
1709					S: "string1",
1710				},
1711			},
1712			err: extendPropertyErrorf("s", "mismatched types string and []string"),
1713		},
1714		{
1715			name: "Append mismatched types",
1716			dst: []interface{}{
1717				&struct{ S []int }{
1718					S: []int{1},
1719				},
1720			},
1721			src: &struct{ S []string }{
1722				S: []string{"string2"},
1723			},
1724			out: []interface{}{
1725				&struct{ S []int }{
1726					S: []int{1},
1727				},
1728			},
1729			err: extendPropertyErrorf("s", "mismatched types []int and []string"),
1730		},
1731	}
1732}
1733
1734func TestAppendMatchingProperties(t *testing.T) {
1735	for _, testCase := range appendMatchingPropertiesTestCases() {
1736		t.Run(testCase.name, func(t *testing.T) {
1737
1738			got := testCase.dst
1739			var err error
1740			var testType string
1741
1742			switch testCase.order {
1743			case Append:
1744				testType = "append"
1745				err = AppendMatchingProperties(got, testCase.src, testCase.filter)
1746			case Prepend:
1747				testType = "prepend"
1748				err = PrependMatchingProperties(got, testCase.src, testCase.filter)
1749			case Replace:
1750				testType = "replace"
1751				err = ExtendMatchingProperties(got, testCase.src, testCase.filter, OrderReplace)
1752			}
1753
1754			check(t, testType, testCase.name, got, err, testCase.out, testCase.err)
1755		})
1756	}
1757}
1758
1759func TestExtendMatchingProperties(t *testing.T) {
1760	for _, testCase := range appendMatchingPropertiesTestCases() {
1761		t.Run(testCase.name, func(t *testing.T) {
1762
1763			got := testCase.dst
1764			var err error
1765			var testType string
1766
1767			order := func(property string,
1768				dstField, srcField reflect.StructField,
1769				dstValue, srcValue interface{}) (Order, error) {
1770				switch testCase.order {
1771				case Append:
1772					return Append, nil
1773				case Prepend:
1774					return Prepend, nil
1775				case Replace:
1776					return Replace, nil
1777				}
1778				return Append, errors.New("unknown order")
1779			}
1780
1781			switch testCase.order {
1782			case Append:
1783				testType = "prepend matching"
1784			case Prepend:
1785				testType = "append matching"
1786			case Replace:
1787				testType = "replace matching"
1788			}
1789
1790			err = ExtendMatchingProperties(got, testCase.src, testCase.filter, order)
1791
1792			check(t, testType, testCase.name, got, err, testCase.out, testCase.err)
1793		})
1794	}
1795}
1796
1797func check(t *testing.T, testType, testString string,
1798	got interface{}, err error,
1799	expected interface{}, expectedErr error) {
1800
1801	printedTestCase := false
1802	e := func(s string, expected, got interface{}) {
1803		if !printedTestCase {
1804			t.Errorf("test case %s: %s", testType, testString)
1805			printedTestCase = true
1806		}
1807		t.Errorf("incorrect %s", s)
1808		t.Errorf("  expected: %s", p(expected))
1809		t.Errorf("       got: %s", p(got))
1810	}
1811
1812	if err != nil {
1813		if expectedErr != nil {
1814			if err.Error() != expectedErr.Error() {
1815				e("unexpected error", expectedErr.Error(), err.Error())
1816			}
1817		} else {
1818			e("unexpected error", nil, err.Error())
1819		}
1820	} else {
1821		if expectedErr != nil {
1822			e("missing error", expectedErr, nil)
1823		}
1824	}
1825
1826	if !reflect.DeepEqual(expected, got) {
1827		e("output:", expected, got)
1828	}
1829}
1830
1831func p(in interface{}) string {
1832	if v, ok := in.([]interface{}); ok {
1833		s := make([]string, len(v))
1834		for i := range v {
1835			s[i] = fmt.Sprintf("%#v", v[i])
1836		}
1837		return "[" + strings.Join(s, ", ") + "]"
1838	} else {
1839		return fmt.Sprintf("%#v", in)
1840	}
1841}
1842