xref: /aosp_15_r20/build/soong/android/arch_test.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1// Copyright 2019 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 android
16
17import (
18	"reflect"
19	"runtime"
20	"testing"
21
22	"github.com/google/blueprint/proptools"
23)
24
25type Named struct {
26	A *string `android:"arch_variant"`
27	B *string
28}
29
30type NamedAllFiltered struct {
31	A *string
32}
33
34type NamedNoneFiltered struct {
35	A *string `android:"arch_variant"`
36}
37
38func TestFilterArchStruct(t *testing.T) {
39	tests := []struct {
40		name     string
41		in       interface{}
42		out      interface{}
43		filtered bool
44	}{
45		// Property tests
46		{
47			name: "basic",
48			in: &struct {
49				A *string `android:"arch_variant"`
50				B *string
51			}{},
52			out: &struct {
53				A *string
54			}{},
55			filtered: true,
56		},
57		{
58			name: "tags",
59			in: &struct {
60				A *string `android:"arch_variant"`
61				B *string `android:"arch_variant,path"`
62				C *string `android:"arch_variant,path,variant_prepend"`
63				D *string `android:"path,variant_prepend,arch_variant"`
64				E *string `android:"path"`
65				F *string
66			}{},
67			out: &struct {
68				A *string
69				B *string
70				C *string
71				D *string
72			}{},
73			filtered: true,
74		},
75		{
76			name: "all filtered",
77			in: &struct {
78				A *string
79			}{},
80			out:      nil,
81			filtered: true,
82		},
83		{
84			name: "none filtered",
85			in: &struct {
86				A *string `android:"arch_variant"`
87			}{},
88			out: &struct {
89				A *string `android:"arch_variant"`
90			}{},
91			filtered: false,
92		},
93
94		// Sub-struct tests
95		{
96			name: "substruct",
97			in: &struct {
98				A struct {
99					A *string `android:"arch_variant"`
100					B *string
101				} `android:"arch_variant"`
102			}{},
103			out: &struct {
104				A struct {
105					A *string
106				}
107			}{},
108			filtered: true,
109		},
110		{
111			name: "substruct all filtered",
112			in: &struct {
113				A struct {
114					A *string
115				} `android:"arch_variant"`
116			}{},
117			out:      nil,
118			filtered: true,
119		},
120		{
121			name: "substruct none filtered",
122			in: &struct {
123				A struct {
124					A *string `android:"arch_variant"`
125				} `android:"arch_variant"`
126			}{},
127			out: &struct {
128				A struct {
129					A *string `android:"arch_variant"`
130				} `android:"arch_variant"`
131			}{},
132			filtered: false,
133		},
134
135		// Named sub-struct tests
136		{
137			name: "named substruct",
138			in: &struct {
139				A Named `android:"arch_variant"`
140			}{},
141			out: &struct {
142				A struct {
143					A *string
144				}
145			}{},
146			filtered: true,
147		},
148		{
149			name: "substruct all filtered",
150			in: &struct {
151				A NamedAllFiltered `android:"arch_variant"`
152			}{},
153			out:      nil,
154			filtered: true,
155		},
156		{
157			name: "substruct none filtered",
158			in: &struct {
159				A NamedNoneFiltered `android:"arch_variant"`
160			}{},
161			out: &struct {
162				A NamedNoneFiltered `android:"arch_variant"`
163			}{},
164			filtered: false,
165		},
166
167		// Pointer to sub-struct tests
168		{
169			name: "pointer substruct",
170			in: &struct {
171				A *struct {
172					A *string `android:"arch_variant"`
173					B *string
174				} `android:"arch_variant"`
175			}{},
176			out: &struct {
177				A *struct {
178					A *string
179				}
180			}{},
181			filtered: true,
182		},
183		{
184			name: "pointer substruct all filtered",
185			in: &struct {
186				A *struct {
187					A *string
188				} `android:"arch_variant"`
189			}{},
190			out:      nil,
191			filtered: true,
192		},
193		{
194			name: "pointer substruct none filtered",
195			in: &struct {
196				A *struct {
197					A *string `android:"arch_variant"`
198				} `android:"arch_variant"`
199			}{},
200			out: &struct {
201				A *struct {
202					A *string `android:"arch_variant"`
203				} `android:"arch_variant"`
204			}{},
205			filtered: false,
206		},
207
208		// Pointer to named sub-struct tests
209		{
210			name: "pointer named substruct",
211			in: &struct {
212				A *Named `android:"arch_variant"`
213			}{},
214			out: &struct {
215				A *struct {
216					A *string
217				}
218			}{},
219			filtered: true,
220		},
221		{
222			name: "pointer substruct all filtered",
223			in: &struct {
224				A *NamedAllFiltered `android:"arch_variant"`
225			}{},
226			out:      nil,
227			filtered: true,
228		},
229		{
230			name: "pointer substruct none filtered",
231			in: &struct {
232				A *NamedNoneFiltered `android:"arch_variant"`
233			}{},
234			out: &struct {
235				A *NamedNoneFiltered `android:"arch_variant"`
236			}{},
237			filtered: false,
238		},
239	}
240
241	for _, test := range tests {
242		t.Run(test.name, func(t *testing.T) {
243			out, filtered := proptools.FilterPropertyStruct(reflect.TypeOf(test.in), filterArchStruct)
244			if filtered != test.filtered {
245				t.Errorf("expected filtered %v, got %v", test.filtered, filtered)
246			}
247			expected := reflect.TypeOf(test.out)
248			if out != expected {
249				t.Errorf("expected type %v, got %v", expected, out)
250			}
251		})
252	}
253}
254
255type archTestModule struct {
256	ModuleBase
257	props struct {
258		Deps []string
259	}
260}
261
262func (m *archTestMultiTargetsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
263}
264
265func (m *archTestMultiTargetsModule) DepsMutator(ctx BottomUpMutatorContext) {
266	ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
267}
268
269func archTestMultiTargetsModuleFactory() Module {
270	m := &archTestMultiTargetsModule{}
271	m.AddProperties(&m.props)
272	InitAndroidMultiTargetsArchModule(m, HostAndDeviceSupported, MultilibCommon)
273	return m
274}
275
276type archTestMultiTargetsModule struct {
277	ModuleBase
278	props struct {
279		Deps []string
280	}
281}
282
283func (m *archTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
284}
285
286func (m *archTestModule) DepsMutator(ctx BottomUpMutatorContext) {
287	ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
288}
289
290func archTestModuleFactory() Module {
291	m := &archTestModule{}
292	m.AddProperties(&m.props)
293	InitAndroidArchModule(m, HostAndDeviceSupported, MultilibBoth)
294	return m
295}
296
297var prepareForArchTest = GroupFixturePreparers(
298	PrepareForTestWithArchMutator,
299	FixtureRegisterWithContext(func(ctx RegistrationContext) {
300		ctx.RegisterModuleType("module", archTestModuleFactory)
301		ctx.RegisterModuleType("multi_targets_module", archTestMultiTargetsModuleFactory)
302	}),
303)
304
305func TestArchMutator(t *testing.T) {
306	var buildOSVariants []string
307	var buildOS64Variants []string
308	var buildOS32Variants []string
309	var buildOSCommonVariant string
310
311	switch runtime.GOOS {
312	case "linux":
313		buildOSVariants = []string{"linux_glibc_x86_64", "linux_glibc_x86"}
314		buildOS64Variants = []string{"linux_glibc_x86_64"}
315		buildOS32Variants = []string{"linux_glibc_x86"}
316		buildOSCommonVariant = "linux_glibc_common"
317	case "darwin":
318		buildOSVariants = []string{"darwin_x86_64"}
319		buildOS64Variants = []string{"darwin_x86_64"}
320		buildOS32Variants = nil
321		buildOSCommonVariant = "darwin_common"
322	}
323
324	bp := `
325		module {
326			name: "foo",
327		}
328
329		module {
330			name: "bar",
331			host_supported: true,
332		}
333
334		module {
335			name: "nohostcross",
336			host_supported: true,
337			host_cross_supported: false,
338		}
339
340		module {
341			name: "baz",
342			device_supported: false,
343		}
344
345		module {
346			name: "qux",
347			host_supported: true,
348			compile_multilib: "32",
349		}
350
351		module {
352			name: "first",
353			host_supported: true,
354			compile_multilib: "first",
355		}
356
357		multi_targets_module {
358			name: "multi_targets",
359			host_supported: true,
360		}
361	`
362
363	testCases := []struct {
364		name                string
365		preparer            FixturePreparer
366		fooVariants         []string
367		barVariants         []string
368		noHostCrossVariants []string
369		bazVariants         []string
370		quxVariants         []string
371		firstVariants       []string
372
373		multiTargetVariants    []string
374		multiTargetVariantsMap map[string][]string
375
376		goOS string
377	}{
378		{
379			name:                "normal",
380			preparer:            nil,
381			fooVariants:         []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
382			barVariants:         append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
383			noHostCrossVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
384			bazVariants:         nil,
385			quxVariants:         append(buildOS32Variants, "android_arm_armv7-a-neon"),
386			firstVariants:       append(buildOS64Variants, "android_arm64_armv8-a"),
387			multiTargetVariants: []string{buildOSCommonVariant, "android_common"},
388			multiTargetVariantsMap: map[string][]string{
389				buildOSCommonVariant: buildOS64Variants,
390				"android_common":     {"android_arm64_armv8-a"},
391			}},
392		{
393			name: "host-only",
394			preparer: FixtureModifyConfig(func(config Config) {
395				config.BuildOSTarget = Target{}
396				config.BuildOSCommonTarget = Target{}
397				config.Targets[Android] = nil
398			}),
399			fooVariants:         nil,
400			barVariants:         buildOSVariants,
401			noHostCrossVariants: buildOSVariants,
402			bazVariants:         nil,
403			quxVariants:         buildOS32Variants,
404			firstVariants:       buildOS64Variants,
405			multiTargetVariants: []string{buildOSCommonVariant},
406			multiTargetVariantsMap: map[string][]string{
407				buildOSCommonVariant: buildOS64Variants,
408			},
409		},
410		{
411			name: "same arch host and host cross",
412			preparer: FixtureModifyConfig(func(config Config) {
413				ModifyTestConfigForMusl(config)
414				modifyTestConfigForMuslArm64HostCross(config)
415			}),
416			fooVariants:         []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
417			barVariants:         []string{"linux_musl_x86_64", "linux_musl_arm64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"},
418			noHostCrossVariants: []string{"linux_musl_x86_64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"},
419			bazVariants:         nil,
420			quxVariants:         []string{"linux_musl_x86", "android_arm_armv7-a-neon"},
421			firstVariants:       []string{"linux_musl_x86_64", "linux_musl_arm64", "android_arm64_armv8-a"},
422			multiTargetVariants: []string{"linux_musl_common", "android_common"},
423			multiTargetVariantsMap: map[string][]string{
424				"linux_musl_common": {"linux_musl_x86_64"},
425				"android_common":    {"android_arm64_armv8-a"},
426			},
427			goOS: "linux",
428		},
429	}
430
431	enabledVariants := func(ctx *TestContext, name string) []string {
432		var ret []string
433		variants := ctx.ModuleVariantsForTests(name)
434		for _, variant := range variants {
435			m := ctx.ModuleForTests(name, variant)
436			if m.Module().Enabled(PanickingConfigAndErrorContext(ctx)) {
437				ret = append(ret, variant)
438			}
439		}
440		return ret
441	}
442
443	moduleMultiTargets := func(ctx *TestContext, name string, variant string) []string {
444		var ret []string
445		targets := ctx.ModuleForTests(name, variant).Module().MultiTargets()
446		for _, t := range targets {
447			ret = append(ret, t.String())
448		}
449		return ret
450	}
451
452	for _, tt := range testCases {
453		t.Run(tt.name, func(t *testing.T) {
454			if tt.goOS != "" && tt.goOS != runtime.GOOS {
455				t.Skipf("requries runtime.GOOS %s", tt.goOS)
456			}
457
458			result := GroupFixturePreparers(
459				prepareForArchTest,
460				// Test specific preparer
461				OptionalFixturePreparer(tt.preparer),
462				FixtureWithRootAndroidBp(bp),
463			).RunTest(t)
464			ctx := result.TestContext
465
466			if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) {
467				t.Errorf("want foo variants:\n%q\ngot:\n%q\n", w, g)
468			}
469
470			if g, w := enabledVariants(ctx, "bar"), tt.barVariants; !reflect.DeepEqual(w, g) {
471				t.Errorf("want bar variants:\n%q\ngot:\n%q\n", w, g)
472			}
473
474			if g, w := enabledVariants(ctx, "nohostcross"), tt.noHostCrossVariants; !reflect.DeepEqual(w, g) {
475				t.Errorf("want nohostcross variants:\n%q\ngot:\n%q\n", w, g)
476			}
477
478			if g, w := enabledVariants(ctx, "baz"), tt.bazVariants; !reflect.DeepEqual(w, g) {
479				t.Errorf("want baz variants:\n%q\ngot:\n%q\n", w, g)
480			}
481
482			if g, w := enabledVariants(ctx, "qux"), tt.quxVariants; !reflect.DeepEqual(w, g) {
483				t.Errorf("want qux variants:\n%q\ngot:\n%q\n", w, g)
484			}
485			if g, w := enabledVariants(ctx, "first"), tt.firstVariants; !reflect.DeepEqual(w, g) {
486				t.Errorf("want first variants:\n%q\ngot:\n%q\n", w, g)
487			}
488
489			if g, w := enabledVariants(ctx, "multi_targets"), tt.multiTargetVariants; !reflect.DeepEqual(w, g) {
490				t.Fatalf("want multi_target variants:\n%q\ngot:\n%q\n", w, g)
491			}
492
493			for _, variant := range tt.multiTargetVariants {
494				targets := moduleMultiTargets(ctx, "multi_targets", variant)
495				if g, w := targets, tt.multiTargetVariantsMap[variant]; !reflect.DeepEqual(w, g) {
496					t.Errorf("want ctx.MultiTarget() for %q:\n%q\ngot:\n%q\n", variant, w, g)
497				}
498			}
499		})
500	}
501}
502
503func TestArchMutatorNativeBridge(t *testing.T) {
504	bp := `
505		// This module is only enabled for x86.
506		module {
507			name: "foo",
508		}
509
510		// This module is enabled for x86 and arm (via native bridge).
511		module {
512			name: "bar",
513			native_bridge_supported: true,
514		}
515
516		// This module is enabled for arm (native_bridge) only.
517		module {
518			name: "baz",
519			native_bridge_supported: true,
520			enabled: false,
521			target: {
522				native_bridge: {
523					enabled: true,
524				}
525			}
526		}
527	`
528
529	testCases := []struct {
530		name        string
531		preparer    FixturePreparer
532		fooVariants []string
533		barVariants []string
534		bazVariants []string
535	}{
536		{
537			name:        "normal",
538			preparer:    nil,
539			fooVariants: []string{"android_x86_64_silvermont", "android_x86_silvermont"},
540			barVariants: []string{"android_x86_64_silvermont", "android_native_bridge_arm64_armv8-a", "android_x86_silvermont", "android_native_bridge_arm_armv7-a-neon"},
541			bazVariants: []string{"android_native_bridge_arm64_armv8-a", "android_native_bridge_arm_armv7-a-neon"},
542		},
543	}
544
545	enabledVariants := func(ctx *TestContext, name string) []string {
546		var ret []string
547		variants := ctx.ModuleVariantsForTests(name)
548		for _, variant := range variants {
549			m := ctx.ModuleForTests(name, variant)
550			if m.Module().Enabled(PanickingConfigAndErrorContext(ctx)) {
551				ret = append(ret, variant)
552			}
553		}
554		return ret
555	}
556
557	for _, tt := range testCases {
558		t.Run(tt.name, func(t *testing.T) {
559			result := GroupFixturePreparers(
560				prepareForArchTest,
561				// Test specific preparer
562				OptionalFixturePreparer(tt.preparer),
563				PrepareForNativeBridgeEnabled,
564				FixtureWithRootAndroidBp(bp),
565			).RunTest(t)
566
567			ctx := result.TestContext
568
569			if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) {
570				t.Errorf("want foo variants:\n%q\ngot:\n%q\n", w, g)
571			}
572
573			if g, w := enabledVariants(ctx, "bar"), tt.barVariants; !reflect.DeepEqual(w, g) {
574				t.Errorf("want bar variants:\n%q\ngot:\n%q\n", w, g)
575			}
576
577			if g, w := enabledVariants(ctx, "baz"), tt.bazVariants; !reflect.DeepEqual(w, g) {
578				t.Errorf("want qux variants:\n%q\ngot:\n%q\n", w, g)
579			}
580		})
581	}
582}
583
584type testArchPropertiesModule struct {
585	ModuleBase
586	properties struct {
587		A []string `android:"arch_variant"`
588	}
589}
590
591func (testArchPropertiesModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
592
593// Module property "a" does not have "variant_prepend" tag.
594// Expected variant property orders are based on this fact.
595func TestArchProperties(t *testing.T) {
596	bp := `
597		module {
598			name: "foo",
599			a: ["root"],
600			arch: {
601				arm: {
602					a:  ["arm"],
603				},
604				arm64: {
605					a:  ["arm64"],
606				},
607				riscv64: { a: ["riscv64"] },
608				x86: { a:  ["x86"] },
609				x86_64: { a:  ["x86_64"] },
610			},
611			multilib: {
612				lib32: { a:  ["lib32"] },
613				lib64: { a:  ["lib64"] },
614			},
615			target: {
616				bionic: { a:  ["bionic"] },
617				host: { a: ["host"] },
618				android: { a:  ["android"] },
619				glibc: { a:  ["glibc"] },
620				musl: { a:  ["musl"] },
621				linux_bionic: { a:  ["linux_bionic"] },
622				linux: { a:  ["linux"] },
623				host_linux: { a: ["host_linux"] },
624				linux_glibc: { a:  ["linux_glibc"] },
625				linux_musl: { a:  ["linux_musl"] },
626				windows: { a:  ["windows"], enabled: true },
627				darwin: { a:  ["darwin"] },
628				not_windows: { a:  ["not_windows"] },
629				android32: { a:  ["android32"] },
630				android64: { a:  ["android64"] },
631				android_arm: { a:  ["android_arm"] },
632				android_arm64: { a:  ["android_arm64"] },
633				linux_x86: { a:  ["linux_x86"] },
634				linux_x86_64: { a:  ["linux_x86_64"] },
635				linux_glibc_x86: { a:  ["linux_glibc_x86"] },
636				linux_glibc_x86_64: { a:  ["linux_glibc_x86_64"] },
637				linux_musl_x86: { a:  ["linux_musl_x86"] },
638				linux_musl_x86_64: { a:  ["linux_musl_x86_64"] },
639				darwin_x86_64: { a:  ["darwin_x86_64"] },
640				windows_x86: { a:  ["windows_x86"] },
641				windows_x86_64: { a:  ["windows_x86_64"] },
642			},
643		}
644	`
645
646	type result struct {
647		module   string
648		variant  string
649		property []string
650	}
651
652	testCases := []struct {
653		name     string
654		goOS     string
655		preparer FixturePreparer
656		results  []result
657	}{
658		{
659			name: "default",
660			results: []result{
661				{
662					module:   "foo",
663					variant:  "android_arm64_armv8-a",
664					property: []string{"root", "linux", "bionic", "android", "android64", "arm64", "lib64", "android_arm64"},
665				},
666				{
667					module:   "foo",
668					variant:  "android_arm_armv7-a-neon",
669					property: []string{"root", "linux", "bionic", "android", "android64", "arm", "lib32", "android_arm"},
670				},
671			},
672		},
673		{
674			name: "linux",
675			goOS: "linux",
676			results: []result{
677				{
678					module:   "foo",
679					variant:  "linux_glibc_x86_64",
680					property: []string{"root", "host", "linux", "host_linux", "glibc", "linux_glibc", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_glibc_x86_64"},
681				},
682				{
683					module:   "foo",
684					variant:  "linux_glibc_x86",
685					property: []string{"root", "host", "linux", "host_linux", "glibc", "linux_glibc", "not_windows", "x86", "lib32", "linux_x86", "linux_glibc_x86"},
686				},
687			},
688		},
689		{
690			name: "windows",
691			goOS: "linux",
692			preparer: FixtureModifyConfig(func(config Config) {
693				config.Targets[Windows] = []Target{
694					{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
695					{Windows, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", true},
696				}
697			}),
698			results: []result{
699				{
700					module:   "foo",
701					variant:  "windows_x86_64",
702					property: []string{"root", "host", "windows", "x86_64", "lib64", "windows_x86_64"},
703				},
704				{
705					module:   "foo",
706					variant:  "windows_x86",
707					property: []string{"root", "host", "windows", "x86", "lib32", "windows_x86"},
708				},
709			},
710		},
711		{
712			name:     "linux_musl",
713			goOS:     "linux",
714			preparer: FixtureModifyConfig(ModifyTestConfigForMusl),
715			results: []result{
716				{
717					module:   "foo",
718					variant:  "linux_musl_x86_64",
719					property: []string{"root", "host", "linux", "host_linux", "musl", "linux_musl", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_musl_x86_64"},
720				},
721				{
722					module:   "foo",
723					variant:  "linux_musl_x86",
724					property: []string{"root", "host", "linux", "host_linux", "musl", "linux_musl", "not_windows", "x86", "lib32", "linux_x86", "linux_musl_x86"},
725				},
726			},
727		},
728		{
729			name: "darwin",
730			goOS: "darwin",
731			results: []result{
732				{
733					module:   "foo",
734					variant:  "darwin_x86_64",
735					property: []string{"root", "host", "darwin", "not_windows", "x86_64", "lib64", "darwin_x86_64"},
736				},
737			},
738		},
739	}
740
741	for _, tt := range testCases {
742		t.Run(tt.name, func(t *testing.T) {
743			if tt.goOS != "" && tt.goOS != runtime.GOOS {
744				t.Skipf("test requires runtime.GOOS==%s, got %s", tt.goOS, runtime.GOOS)
745			}
746			result := GroupFixturePreparers(
747				PrepareForTestWithArchMutator,
748				OptionalFixturePreparer(tt.preparer),
749				FixtureRegisterWithContext(func(ctx RegistrationContext) {
750					ctx.RegisterModuleType("module", func() Module {
751						module := &testArchPropertiesModule{}
752						module.AddProperties(&module.properties)
753						InitAndroidArchModule(module, HostAndDeviceDefault, MultilibBoth)
754						return module
755					})
756				}),
757			).RunTestWithBp(t, bp)
758
759			for _, want := range tt.results {
760				t.Run(want.module+"_"+want.variant, func(t *testing.T) {
761					got := result.ModuleForTests(want.module, want.variant).Module().(*testArchPropertiesModule).properties.A
762					AssertArrayString(t, "arch mutator property", want.property, got)
763				})
764			}
765		})
766	}
767}
768