• 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(dstField, srcField reflect.StructField) (bool, error) {
1172				return true, nil
1173			},
1174		},
1175		{
1176			name: "Filter false",
1177			dst: &struct{ S string }{
1178				S: "string1",
1179			},
1180			src: &struct{ S string }{
1181				S: "string2",
1182			},
1183			out: &struct{ S string }{
1184				S: "string1",
1185			},
1186			filter: func(dstField, srcField reflect.StructField) (bool, error) {
1187				return false, nil
1188			},
1189		},
1190		{
1191			name: "Filter check args",
1192			dst: &struct{ S string }{
1193				S: "string1",
1194			},
1195			src: &struct{ S string }{
1196				S: "string2",
1197			},
1198			out: &struct{ S string }{
1199				S: "string1string2",
1200			},
1201			filter: func(dstField, srcField reflect.StructField) (bool, error) {
1202				return dstField.Name == "S" && srcField.Name == "S", nil
1203			},
1204		},
1205		{
1206			name: "Filter mutated",
1207			dst: &struct {
1208				S string `blueprint:"mutated"`
1209			}{
1210				S: "string1",
1211			},
1212			src: &struct {
1213				S string `blueprint:"mutated"`
1214			}{
1215				S: "string2",
1216			},
1217			out: &struct {
1218				S string `blueprint:"mutated"`
1219			}{
1220				S: "string1",
1221			},
1222		},
1223		{
1224			name: "Filter mutated",
1225			dst: &struct {
1226				S *int64 `blueprint:"mutated"`
1227			}{
1228				S: Int64Ptr(4),
1229			},
1230			src: &struct {
1231				S *int64 `blueprint:"mutated"`
1232			}{
1233				S: Int64Ptr(5),
1234			},
1235			out: &struct {
1236				S *int64 `blueprint:"mutated"`
1237			}{
1238				S: Int64Ptr(4),
1239			},
1240		},
1241		{
1242			name: "Filter error",
1243			dst: &struct{ S string }{
1244				S: "string1",
1245			},
1246			src: &struct{ S string }{
1247				S: "string2",
1248			},
1249			out: &struct{ S string }{
1250				S: "string1",
1251			},
1252			filter: func(dstField, srcField reflect.StructField) (bool, error) {
1253				return true, fmt.Errorf("filter error")
1254			},
1255			err: extendPropertyErrorf("s", "filter error"),
1256		},
1257		{
1258			name: "Append configurable",
1259			dst: &struct{ S Configurable[[]string] }{
1260				S: Configurable[[]string]{
1261					inner: &configurableInner[[]string]{
1262						single: singleConfigurable[[]string]{
1263							conditions: []ConfigurableCondition{{
1264								functionName: "soong_config_variable",
1265								args: []string{
1266									"my_namespace",
1267									"foo",
1268								},
1269							}},
1270							cases: []ConfigurableCase[[]string]{{
1271								patterns: []ConfigurablePattern{{
1272									typ:         configurablePatternTypeString,
1273									stringValue: "a",
1274								}},
1275								value: &[]string{"1", "2"},
1276							}},
1277						},
1278					},
1279				},
1280			},
1281			src: &struct{ S Configurable[[]string] }{
1282				S: Configurable[[]string]{
1283					inner: &configurableInner[[]string]{
1284						single: singleConfigurable[[]string]{
1285							conditions: []ConfigurableCondition{{
1286								functionName: "release_variable",
1287								args: []string{
1288									"bar",
1289								},
1290							}},
1291							cases: []ConfigurableCase[[]string]{{
1292								patterns: []ConfigurablePattern{{
1293									typ:         configurablePatternTypeString,
1294									stringValue: "b",
1295								}},
1296								value: &[]string{"3", "4"},
1297							}},
1298						},
1299					},
1300				},
1301			},
1302			out: &struct{ S Configurable[[]string] }{
1303				S: Configurable[[]string]{
1304					inner: &configurableInner[[]string]{
1305						single: singleConfigurable[[]string]{
1306							conditions: []ConfigurableCondition{{
1307								functionName: "soong_config_variable",
1308								args: []string{
1309									"my_namespace",
1310									"foo",
1311								},
1312							}},
1313							cases: []ConfigurableCase[[]string]{{
1314								patterns: []ConfigurablePattern{{
1315									typ:         configurablePatternTypeString,
1316									stringValue: "a",
1317								}},
1318								value: &[]string{"1", "2"},
1319							}},
1320						},
1321						next: &configurableInner[[]string]{
1322							single: singleConfigurable[[]string]{
1323								conditions: []ConfigurableCondition{{
1324									functionName: "release_variable",
1325									args: []string{
1326										"bar",
1327									},
1328								}},
1329								cases: []ConfigurableCase[[]string]{{
1330									patterns: []ConfigurablePattern{{
1331										typ:         configurablePatternTypeString,
1332										stringValue: "b",
1333									}},
1334									value: &[]string{"3", "4"},
1335								}},
1336							},
1337						},
1338					},
1339				},
1340			},
1341		},
1342		{
1343			name:  "Prepend configurable",
1344			order: Prepend,
1345			dst: &struct{ S Configurable[[]string] }{
1346				S: Configurable[[]string]{
1347					inner: &configurableInner[[]string]{
1348						single: singleConfigurable[[]string]{
1349							conditions: []ConfigurableCondition{{
1350								functionName: "soong_config_variable",
1351								args: []string{
1352									"my_namespace",
1353									"foo",
1354								},
1355							}},
1356							cases: []ConfigurableCase[[]string]{{
1357								patterns: []ConfigurablePattern{{
1358									typ:         configurablePatternTypeString,
1359									stringValue: "a",
1360								}},
1361								value: &[]string{"1", "2"},
1362							}},
1363						},
1364					},
1365				},
1366			},
1367			src: &struct{ S Configurable[[]string] }{
1368				S: Configurable[[]string]{
1369					inner: &configurableInner[[]string]{
1370						single: singleConfigurable[[]string]{
1371							conditions: []ConfigurableCondition{{
1372								functionName: "release_variable",
1373								args: []string{
1374									"bar",
1375								},
1376							}},
1377							cases: []ConfigurableCase[[]string]{{
1378								patterns: []ConfigurablePattern{{
1379									typ:         configurablePatternTypeString,
1380									stringValue: "b",
1381								}},
1382								value: &[]string{"3", "4"},
1383							}},
1384						},
1385					},
1386				},
1387			},
1388			out: &struct{ S Configurable[[]string] }{
1389				S: Configurable[[]string]{
1390					inner: &configurableInner[[]string]{
1391						single: singleConfigurable[[]string]{
1392							conditions: []ConfigurableCondition{{
1393								functionName: "release_variable",
1394								args: []string{
1395									"bar",
1396								},
1397							}},
1398							cases: []ConfigurableCase[[]string]{{
1399								patterns: []ConfigurablePattern{{
1400									typ:         configurablePatternTypeString,
1401									stringValue: "b",
1402								}},
1403								value: &[]string{"3", "4"},
1404							}},
1405						},
1406						next: &configurableInner[[]string]{
1407							single: singleConfigurable[[]string]{
1408								conditions: []ConfigurableCondition{{
1409									functionName: "soong_config_variable",
1410									args: []string{
1411										"my_namespace",
1412										"foo",
1413									},
1414								}},
1415								cases: []ConfigurableCase[[]string]{{
1416									patterns: []ConfigurablePattern{{
1417										typ:         configurablePatternTypeString,
1418										stringValue: "a",
1419									}},
1420									value: &[]string{"1", "2"},
1421								}},
1422							},
1423						},
1424					},
1425				},
1426			},
1427		},
1428	}
1429}
1430
1431func TestAppendProperties(t *testing.T) {
1432	for _, testCase := range appendPropertiesTestCases() {
1433		t.Run(testCase.name, func(t *testing.T) {
1434
1435			got := testCase.dst
1436			var err error
1437			var testType string
1438
1439			switch testCase.order {
1440			case Append:
1441				testType = "append"
1442				err = AppendProperties(got, testCase.src, testCase.filter)
1443			case Prepend:
1444				testType = "prepend"
1445				err = PrependProperties(got, testCase.src, testCase.filter)
1446			case Replace:
1447				testType = "replace"
1448				err = ExtendProperties(got, testCase.src, testCase.filter, OrderReplace)
1449			}
1450
1451			check(t, testType, testCase.name, got, err, testCase.out, testCase.err)
1452		})
1453	}
1454}
1455
1456func TestExtendProperties(t *testing.T) {
1457	for _, testCase := range appendPropertiesTestCases() {
1458		t.Run(testCase.name, func(t *testing.T) {
1459
1460			got := testCase.dst
1461			var err error
1462			var testType string
1463
1464			order := func(dstField, srcField reflect.StructField) (Order, error) {
1465				switch testCase.order {
1466				case Append:
1467					return Append, nil
1468				case Prepend:
1469					return Prepend, nil
1470				case Replace:
1471					return Replace, nil
1472				}
1473				return Append, errors.New("unknown order")
1474			}
1475
1476			switch testCase.order {
1477			case Append:
1478				testType = "prepend"
1479			case Prepend:
1480				testType = "append"
1481			case Replace:
1482				testType = "replace"
1483			}
1484
1485			err = ExtendProperties(got, testCase.src, testCase.filter, order)
1486
1487			check(t, testType, testCase.name, got, err, testCase.out, testCase.err)
1488		})
1489	}
1490}
1491
1492type appendMatchingPropertiesTestCase struct {
1493	name   string
1494	dst    []interface{}
1495	src    interface{}
1496	out    []interface{}
1497	order  Order // default is Append
1498	filter ExtendPropertyFilterFunc
1499	err    error
1500}
1501
1502func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
1503	return []appendMatchingPropertiesTestCase{
1504		{
1505			name: "Append strings",
1506			dst: []interface{}{&struct{ S string }{
1507				S: "string1",
1508			}},
1509			src: &struct{ S string }{
1510				S: "string2",
1511			},
1512			out: []interface{}{&struct{ S string }{
1513				S: "string1string2",
1514			}},
1515		},
1516		{
1517			name: "Prepend strings",
1518			dst: []interface{}{&struct{ S string }{
1519				S: "string1",
1520			}},
1521			src: &struct{ S string }{
1522				S: "string2",
1523			},
1524			out: []interface{}{&struct{ S string }{
1525				S: "string2string1",
1526			}},
1527			order: Prepend,
1528		},
1529		{
1530			name: "Append all",
1531			dst: []interface{}{
1532				&struct{ S, A string }{
1533					S: "string1",
1534				},
1535				&struct{ S, B string }{
1536					S: "string2",
1537				},
1538			},
1539			src: &struct{ S string }{
1540				S: "string3",
1541			},
1542			out: []interface{}{
1543				&struct{ S, A string }{
1544					S: "string1string3",
1545				},
1546				&struct{ S, B string }{
1547					S: "string2string3",
1548				},
1549			},
1550		},
1551		{
1552			name: "Append some",
1553			dst: []interface{}{
1554				&struct{ S, A string }{
1555					S: "string1",
1556				},
1557				&struct{ B string }{},
1558			},
1559			src: &struct{ S string }{
1560				S: "string2",
1561			},
1562			out: []interface{}{
1563				&struct{ S, A string }{
1564					S: "string1string2",
1565				},
1566				&struct{ B string }{},
1567			},
1568		},
1569		{
1570			name: "Append mismatched structs",
1571			dst: []interface{}{&struct{ S, A string }{
1572				S: "string1",
1573			}},
1574			src: &struct{ S string }{
1575				S: "string2",
1576			},
1577			out: []interface{}{&struct{ S, A string }{
1578				S: "string1string2",
1579			}},
1580		},
1581		{
1582			name: "Append mismatched pointer structs",
1583			dst: []interface{}{&struct{ S *struct{ S, A string } }{
1584				S: &struct{ S, A string }{
1585					S: "string1",
1586				},
1587			}},
1588			src: &struct{ S *struct{ S string } }{
1589				S: &struct{ S string }{
1590					S: "string2",
1591				},
1592			},
1593			out: []interface{}{&struct{ S *struct{ S, A string } }{
1594				S: &struct{ S, A string }{
1595					S: "string1string2",
1596				},
1597			}},
1598		},
1599		{
1600			name: "Append through mismatched types",
1601			dst: []interface{}{
1602				&struct{ B string }{},
1603				&struct{ S interface{} }{
1604					S: &struct{ S, A string }{
1605						S: "string1",
1606					},
1607				},
1608			},
1609			src: &struct{ S struct{ S string } }{
1610				S: struct{ S string }{
1611					S: "string2",
1612				},
1613			},
1614			out: []interface{}{
1615				&struct{ B string }{},
1616				&struct{ S interface{} }{
1617					S: &struct{ S, A string }{
1618						S: "string1string2",
1619					},
1620				},
1621			},
1622		},
1623		{
1624			name: "Append through mismatched types and nil",
1625			dst: []interface{}{
1626				&struct{ B string }{},
1627				&struct{ S interface{} }{
1628					S: (*struct{ S, A string })(nil),
1629				},
1630			},
1631			src: &struct{ S struct{ S string } }{
1632				S: struct{ S string }{
1633					S: "string2",
1634				},
1635			},
1636			out: []interface{}{
1637				&struct{ B string }{},
1638				&struct{ S interface{} }{
1639					S: &struct{ S, A string }{
1640						S: "string2",
1641					},
1642				},
1643			},
1644		},
1645		{
1646			name: "Append through multiple matches",
1647			dst: []interface{}{
1648				&struct {
1649					S struct{ S, A string }
1650				}{
1651					S: struct{ S, A string }{
1652						S: "string1",
1653					},
1654				},
1655				&struct {
1656					S struct{ S, B string }
1657				}{
1658					S: struct{ S, B string }{
1659						S: "string2",
1660					},
1661				},
1662			},
1663			src: &struct{ S struct{ B string } }{
1664				S: struct{ B string }{
1665					B: "string3",
1666				},
1667			},
1668			out: []interface{}{
1669				&struct {
1670					S struct{ S, A string }
1671				}{
1672					S: struct{ S, A string }{
1673						S: "string1",
1674					},
1675				},
1676				&struct {
1677					S struct{ S, B string }
1678				}{
1679					S: struct{ S, B string }{
1680						S: "string2",
1681						B: "string3",
1682					},
1683				},
1684			},
1685		},
1686		{
1687			name: "Append through embedded struct",
1688			dst: []interface{}{
1689				&struct{ B string }{},
1690				&struct{ EmbeddedStruct }{
1691					EmbeddedStruct: EmbeddedStruct{
1692						S: "string1",
1693					},
1694				},
1695			},
1696			src: &struct{ S string }{
1697				S: "string2",
1698			},
1699			out: []interface{}{
1700				&struct{ B string }{},
1701				&struct{ EmbeddedStruct }{
1702					EmbeddedStruct: EmbeddedStruct{
1703						S: "string1string2",
1704					},
1705				},
1706			},
1707		},
1708		{
1709			name: "Append through BlueprintEmbed struct",
1710			dst: []interface{}{
1711				&struct{ B string }{},
1712				&struct{ BlueprintEmbed EmbeddedStruct }{
1713					BlueprintEmbed: EmbeddedStruct{
1714						S: "string1",
1715					},
1716				},
1717			},
1718			src: &struct{ S string }{
1719				S: "string2",
1720			},
1721			out: []interface{}{
1722				&struct{ B string }{},
1723				&struct{ BlueprintEmbed EmbeddedStruct }{
1724					BlueprintEmbed: EmbeddedStruct{
1725						S: "string1string2",
1726					},
1727				},
1728			},
1729		},
1730		{
1731			name: "Append through embedded pointer to struct",
1732			dst: []interface{}{
1733				&struct{ B string }{},
1734				&struct{ *EmbeddedStruct }{
1735					EmbeddedStruct: &EmbeddedStruct{
1736						S: "string1",
1737					},
1738				},
1739			},
1740			src: &struct{ S string }{
1741				S: "string2",
1742			},
1743			out: []interface{}{
1744				&struct{ B string }{},
1745				&struct{ *EmbeddedStruct }{
1746					EmbeddedStruct: &EmbeddedStruct{
1747						S: "string1string2",
1748					},
1749				},
1750			},
1751		},
1752		{
1753			name: "Append through BlueprintEmbed pointer to struct",
1754			dst: []interface{}{
1755				&struct{ B string }{},
1756				&struct{ BlueprintEmbed *EmbeddedStruct }{
1757					BlueprintEmbed: &EmbeddedStruct{
1758						S: "string1",
1759					},
1760				},
1761			},
1762			src: &struct{ S string }{
1763				S: "string2",
1764			},
1765			out: []interface{}{
1766				&struct{ B string }{},
1767				&struct{ BlueprintEmbed *EmbeddedStruct }{
1768					BlueprintEmbed: &EmbeddedStruct{
1769						S: "string1string2",
1770					},
1771				},
1772			},
1773		},
1774		{
1775			name: "Append through embedded nil pointer to struct",
1776			dst: []interface{}{
1777				&struct{ B string }{},
1778				&struct{ *EmbeddedStruct }{},
1779			},
1780			src: &struct{ S string }{
1781				S: "string2",
1782			},
1783			out: []interface{}{
1784				&struct{ B string }{},
1785				&struct{ *EmbeddedStruct }{
1786					EmbeddedStruct: &EmbeddedStruct{
1787						S: "string2",
1788					},
1789				},
1790			},
1791		},
1792		{
1793			name: "Append through BlueprintEmbed nil pointer to struct",
1794			dst: []interface{}{
1795				&struct{ B string }{},
1796				&struct{ BlueprintEmbed *EmbeddedStruct }{},
1797			},
1798			src: &struct{ S string }{
1799				S: "string2",
1800			},
1801			out: []interface{}{
1802				&struct{ B string }{},
1803				&struct{ BlueprintEmbed *EmbeddedStruct }{
1804					BlueprintEmbed: &EmbeddedStruct{
1805						S: "string2",
1806					},
1807				},
1808			},
1809		},
1810
1811		// Errors
1812
1813		{
1814			name: "Non-pointer dst",
1815			dst:  []interface{}{struct{}{}},
1816			src:  &struct{}{},
1817			err:  errors.New("expected pointer to struct, got struct {}"),
1818			out:  []interface{}{struct{}{}},
1819		},
1820		{
1821			name: "Non-pointer src",
1822			dst:  []interface{}{&struct{}{}},
1823			src:  struct{}{},
1824			err:  errors.New("expected pointer to struct, got struct {}"),
1825			out:  []interface{}{&struct{}{}},
1826		},
1827		{
1828			name: "Non-struct dst",
1829			dst:  []interface{}{&[]string{"bad"}},
1830			src:  &struct{}{},
1831			err:  errors.New("expected pointer to struct, got *[]string"),
1832			out:  []interface{}{&[]string{"bad"}},
1833		},
1834		{
1835			name: "Non-struct src",
1836			dst:  []interface{}{&struct{}{}},
1837			src:  &[]string{"bad"},
1838			err:  errors.New("expected pointer to struct, got *[]string"),
1839			out:  []interface{}{&struct{}{}},
1840		},
1841		{
1842			name: "Append none",
1843			dst: []interface{}{
1844				&struct{ A string }{},
1845				&struct{ B string }{},
1846			},
1847			src: &struct{ S string }{
1848				S: "string1",
1849			},
1850			out: []interface{}{
1851				&struct{ A string }{},
1852				&struct{ B string }{},
1853			},
1854			err: extendPropertyErrorf("s", "failed to find property to extend"),
1855		},
1856		{
1857			name: "Append mismatched kinds",
1858			dst: []interface{}{
1859				&struct{ S string }{
1860					S: "string1",
1861				},
1862			},
1863			src: &struct{ S []string }{
1864				S: []string{"string2"},
1865			},
1866			out: []interface{}{
1867				&struct{ S string }{
1868					S: "string1",
1869				},
1870			},
1871			err: extendPropertyErrorf("s", "mismatched types string and []string"),
1872		},
1873		{
1874			name: "Append mismatched types",
1875			dst: []interface{}{
1876				&struct{ S []int }{
1877					S: []int{1},
1878				},
1879			},
1880			src: &struct{ S []string }{
1881				S: []string{"string2"},
1882			},
1883			out: []interface{}{
1884				&struct{ S []int }{
1885					S: []int{1},
1886				},
1887			},
1888			err: extendPropertyErrorf("s", "mismatched types []int and []string"),
1889		},
1890		{
1891			name:  "Append *bool to Configurable[bool]",
1892			order: Append,
1893			dst: []interface{}{
1894				&struct{ S Configurable[bool] }{
1895					S: Configurable[bool]{
1896						inner: &configurableInner[bool]{
1897							single: singleConfigurable[bool]{
1898								conditions: []ConfigurableCondition{{
1899									functionName: "soong_config_variable",
1900									args: []string{
1901										"my_namespace",
1902										"foo",
1903									},
1904								}},
1905								cases: []ConfigurableCase[bool]{{
1906									patterns: []ConfigurablePattern{{
1907										typ:         configurablePatternTypeString,
1908										stringValue: "a",
1909									}},
1910									value: BoolPtr(true),
1911								}, {
1912									patterns: []ConfigurablePattern{{
1913										typ: configurablePatternTypeDefault,
1914									}},
1915									value: BoolPtr(false),
1916								}},
1917							},
1918						},
1919					},
1920				},
1921			},
1922			src: &struct{ S *bool }{
1923				S: BoolPtr(true),
1924			},
1925			out: []interface{}{
1926				&struct{ S Configurable[bool] }{
1927					S: Configurable[bool]{
1928						inner: &configurableInner[bool]{
1929							single: singleConfigurable[bool]{
1930								conditions: []ConfigurableCondition{{
1931									functionName: "soong_config_variable",
1932									args: []string{
1933										"my_namespace",
1934										"foo",
1935									},
1936								}},
1937								cases: []ConfigurableCase[bool]{{
1938									patterns: []ConfigurablePattern{{
1939										typ:         configurablePatternTypeString,
1940										stringValue: "a",
1941									}},
1942									value: BoolPtr(true),
1943								}, {
1944									patterns: []ConfigurablePattern{{
1945										typ: configurablePatternTypeDefault,
1946									}},
1947									value: BoolPtr(false),
1948								}},
1949							},
1950							next: &configurableInner[bool]{
1951								single: singleConfigurable[bool]{
1952									cases: []ConfigurableCase[bool]{{
1953										value: BoolPtr(true),
1954									}},
1955								},
1956							},
1957						},
1958					},
1959				},
1960			},
1961		},
1962		{
1963			name:  "Append bool to Configurable[bool]",
1964			order: Append,
1965			dst: []interface{}{
1966				&struct{ S Configurable[bool] }{
1967					S: Configurable[bool]{
1968						inner: &configurableInner[bool]{
1969							single: singleConfigurable[bool]{
1970								conditions: []ConfigurableCondition{{
1971									functionName: "soong_config_variable",
1972									args: []string{
1973										"my_namespace",
1974										"foo",
1975									},
1976								}},
1977								cases: []ConfigurableCase[bool]{{
1978									patterns: []ConfigurablePattern{{
1979										typ:         configurablePatternTypeString,
1980										stringValue: "a",
1981									}},
1982									value: BoolPtr(true),
1983								}, {
1984									patterns: []ConfigurablePattern{{
1985										typ: configurablePatternTypeDefault,
1986									}},
1987									value: BoolPtr(false),
1988								}},
1989							},
1990						},
1991					},
1992				},
1993			},
1994			src: &struct{ S bool }{
1995				S: true,
1996			},
1997			out: []interface{}{
1998				&struct{ S Configurable[bool] }{
1999					S: Configurable[bool]{
2000						inner: &configurableInner[bool]{
2001							single: singleConfigurable[bool]{
2002								conditions: []ConfigurableCondition{{
2003									functionName: "soong_config_variable",
2004									args: []string{
2005										"my_namespace",
2006										"foo",
2007									},
2008								}},
2009								cases: []ConfigurableCase[bool]{{
2010									patterns: []ConfigurablePattern{{
2011										typ:         configurablePatternTypeString,
2012										stringValue: "a",
2013									}},
2014									value: BoolPtr(true),
2015								}, {
2016									patterns: []ConfigurablePattern{{
2017										typ: configurablePatternTypeDefault,
2018									}},
2019									value: BoolPtr(false),
2020								}},
2021							},
2022							next: &configurableInner[bool]{
2023								single: singleConfigurable[bool]{
2024									cases: []ConfigurableCase[bool]{{
2025										value: BoolPtr(true),
2026									}},
2027								},
2028							},
2029						},
2030					},
2031				},
2032			},
2033		},
2034	}
2035}
2036
2037func TestAppendMatchingProperties(t *testing.T) {
2038	for _, testCase := range appendMatchingPropertiesTestCases() {
2039		t.Run(testCase.name, func(t *testing.T) {
2040
2041			got := testCase.dst
2042			var err error
2043			var testType string
2044
2045			switch testCase.order {
2046			case Append:
2047				testType = "append"
2048				err = AppendMatchingProperties(got, testCase.src, testCase.filter)
2049			case Prepend:
2050				testType = "prepend"
2051				err = PrependMatchingProperties(got, testCase.src, testCase.filter)
2052			case Replace:
2053				testType = "replace"
2054				err = ExtendMatchingProperties(got, testCase.src, testCase.filter, OrderReplace)
2055			}
2056
2057			check(t, testType, testCase.name, got, err, testCase.out, testCase.err)
2058		})
2059	}
2060}
2061
2062func TestExtendMatchingProperties(t *testing.T) {
2063	for _, testCase := range appendMatchingPropertiesTestCases() {
2064		t.Run(testCase.name, func(t *testing.T) {
2065
2066			got := testCase.dst
2067			var err error
2068			var testType string
2069
2070			order := func(dstField, srcField reflect.StructField) (Order, error) {
2071				switch testCase.order {
2072				case Append:
2073					return Append, nil
2074				case Prepend:
2075					return Prepend, nil
2076				case Replace:
2077					return Replace, nil
2078				}
2079				return Append, errors.New("unknown order")
2080			}
2081
2082			switch testCase.order {
2083			case Append:
2084				testType = "prepend matching"
2085			case Prepend:
2086				testType = "append matching"
2087			case Replace:
2088				testType = "replace matching"
2089			}
2090
2091			err = ExtendMatchingProperties(got, testCase.src, testCase.filter, order)
2092
2093			check(t, testType, testCase.name, got, err, testCase.out, testCase.err)
2094		})
2095	}
2096}
2097
2098func check(t *testing.T, testType, testString string,
2099	got interface{}, err error,
2100	expected interface{}, expectedErr error) {
2101
2102	printedTestCase := false
2103	e := func(s string, expected, got interface{}) {
2104		if !printedTestCase {
2105			t.Errorf("test case %s: %s", testType, testString)
2106			printedTestCase = true
2107		}
2108		t.Errorf("incorrect %s", s)
2109		t.Errorf("  expected: %s", p(expected))
2110		t.Errorf("       got: %s", p(got))
2111	}
2112
2113	if err != nil {
2114		if expectedErr != nil {
2115			if err.Error() != expectedErr.Error() {
2116				e("unexpected error", expectedErr.Error(), err.Error())
2117			}
2118		} else {
2119			e("unexpected error", nil, err.Error())
2120		}
2121	} else {
2122		if expectedErr != nil {
2123			e("missing error", expectedErr, nil)
2124		}
2125	}
2126
2127	if !reflect.DeepEqual(expected, got) {
2128		e("output:", expected, got)
2129	}
2130}
2131
2132func p(in interface{}) string {
2133	if v, ok := in.([]interface{}); ok {
2134		s := make([]string, len(v))
2135		for i := range v {
2136			s[i] = fmt.Sprintf("%#v", v[i])
2137		}
2138		return "[" + strings.Join(s, ", ") + "]"
2139	} else {
2140		return fmt.Sprintf("%#v", in)
2141	}
2142}
2143