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 *archTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
263}
264
265func (m *archTestModule) DepsMutator(ctx BottomUpMutatorContext) {
266	ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
267}
268
269func archTestModuleFactory() Module {
270	m := &archTestModule{}
271	m.AddProperties(&m.props)
272	InitAndroidArchModule(m, HostAndDeviceSupported, MultilibBoth)
273	return m
274}
275
276func TestArchMutator(t *testing.T) {
277	var buildOSVariants []string
278	var buildOS32Variants []string
279	switch runtime.GOOS {
280	case "linux":
281		buildOSVariants = []string{"linux_glibc_x86_64", "linux_glibc_x86"}
282		buildOS32Variants = []string{"linux_glibc_x86"}
283	case "darwin":
284		buildOSVariants = []string{"darwin_x86_64"}
285		buildOS32Variants = nil
286	}
287
288	bp := `
289		module {
290			name: "foo",
291		}
292
293		module {
294			name: "bar",
295			host_supported: true,
296		}
297
298		module {
299			name: "baz",
300			device_supported: false,
301		}
302
303		module {
304			name: "qux",
305			host_supported: true,
306			compile_multilib: "32",
307		}
308	`
309
310	testCases := []struct {
311		name        string
312		config      func(Config)
313		fooVariants []string
314		barVariants []string
315		bazVariants []string
316		quxVariants []string
317	}{
318		{
319			name:        "normal",
320			config:      nil,
321			fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
322			barVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
323			bazVariants: nil,
324			quxVariants: append(buildOS32Variants, "android_arm_armv7-a-neon"),
325		},
326		{
327			name: "host-only",
328			config: func(config Config) {
329				config.BuildOSTarget = Target{}
330				config.BuildOSCommonTarget = Target{}
331				config.Targets[Android] = nil
332			},
333			fooVariants: nil,
334			barVariants: buildOSVariants,
335			bazVariants: nil,
336			quxVariants: buildOS32Variants,
337		},
338	}
339
340	enabledVariants := func(ctx *TestContext, name string) []string {
341		var ret []string
342		variants := ctx.ModuleVariantsForTests(name)
343		for _, variant := range variants {
344			m := ctx.ModuleForTests(name, variant)
345			if m.Module().Enabled() {
346				ret = append(ret, variant)
347			}
348		}
349		return ret
350	}
351
352	for _, tt := range testCases {
353		t.Run(tt.name, func(t *testing.T) {
354			config := TestArchConfig(buildDir, nil, bp, nil)
355
356			ctx := NewTestArchContext()
357			ctx.RegisterModuleType("module", archTestModuleFactory)
358			ctx.Register(config)
359			if tt.config != nil {
360				tt.config(config)
361			}
362
363			_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
364			FailIfErrored(t, errs)
365			_, errs = ctx.PrepareBuildActions(config)
366			FailIfErrored(t, errs)
367
368			if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) {
369				t.Errorf("want foo variants:\n%q\ngot:\n%q\n", w, g)
370			}
371
372			if g, w := enabledVariants(ctx, "bar"), tt.barVariants; !reflect.DeepEqual(w, g) {
373				t.Errorf("want bar variants:\n%q\ngot:\n%q\n", w, g)
374			}
375
376			if g, w := enabledVariants(ctx, "baz"), tt.bazVariants; !reflect.DeepEqual(w, g) {
377				t.Errorf("want baz variants:\n%q\ngot:\n%q\n", w, g)
378			}
379
380			if g, w := enabledVariants(ctx, "qux"), tt.quxVariants; !reflect.DeepEqual(w, g) {
381				t.Errorf("want qux variants:\n%q\ngot:\n%q\n", w, g)
382			}
383		})
384	}
385}
386
387func TestArchMutatorNativeBridge(t *testing.T) {
388	bp := `
389		// This module is only enabled for x86.
390		module {
391			name: "foo",
392		}
393
394		// This module is enabled for x86 and arm (via native bridge).
395		module {
396			name: "bar",
397			native_bridge_supported: true,
398		}
399
400		// This module is enabled for arm (native_bridge) only.
401		module {
402			name: "baz",
403			native_bridge_supported: true,
404			enabled: false,
405			target: {
406				native_bridge: {
407					enabled: true,
408				}
409			}
410		}
411	`
412
413	testCases := []struct {
414		name        string
415		config      func(Config)
416		fooVariants []string
417		barVariants []string
418		bazVariants []string
419	}{
420		{
421			name:        "normal",
422			config:      nil,
423			fooVariants: []string{"android_x86_64_silvermont", "android_x86_silvermont"},
424			barVariants: []string{"android_x86_64_silvermont", "android_native_bridge_arm64_armv8-a", "android_x86_silvermont", "android_native_bridge_arm_armv7-a-neon"},
425			bazVariants: []string{"android_native_bridge_arm64_armv8-a", "android_native_bridge_arm_armv7-a-neon"},
426		},
427	}
428
429	enabledVariants := func(ctx *TestContext, name string) []string {
430		var ret []string
431		variants := ctx.ModuleVariantsForTests(name)
432		for _, variant := range variants {
433			m := ctx.ModuleForTests(name, variant)
434			if m.Module().Enabled() {
435				ret = append(ret, variant)
436			}
437		}
438		return ret
439	}
440
441	for _, tt := range testCases {
442		t.Run(tt.name, func(t *testing.T) {
443			config := TestArchConfigNativeBridge(buildDir, nil, bp, nil)
444
445			ctx := NewTestArchContext()
446			ctx.RegisterModuleType("module", archTestModuleFactory)
447			ctx.Register(config)
448			if tt.config != nil {
449				tt.config(config)
450			}
451
452			_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
453			FailIfErrored(t, errs)
454			_, errs = ctx.PrepareBuildActions(config)
455			FailIfErrored(t, errs)
456
457			if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) {
458				t.Errorf("want foo variants:\n%q\ngot:\n%q\n", w, g)
459			}
460
461			if g, w := enabledVariants(ctx, "bar"), tt.barVariants; !reflect.DeepEqual(w, g) {
462				t.Errorf("want bar variants:\n%q\ngot:\n%q\n", w, g)
463			}
464
465			if g, w := enabledVariants(ctx, "baz"), tt.bazVariants; !reflect.DeepEqual(w, g) {
466				t.Errorf("want qux variants:\n%q\ngot:\n%q\n", w, g)
467			}
468		})
469	}
470}
471