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