• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 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 bp2build
16
17/*
18For shareable/common bp2build testing functionality and dumping ground for
19specific-but-shared functionality among tests in package
20*/
21
22import (
23	"fmt"
24	"strings"
25	"testing"
26
27	"android/soong/android"
28	"android/soong/android/allowlists"
29	"android/soong/bazel"
30)
31
32var (
33	// A default configuration for tests to not have to specify bp2build_available on top level targets.
34	bp2buildConfig = android.NewBp2BuildAllowlist().SetDefaultConfig(
35		allowlists.Bp2BuildConfig{
36			android.Bp2BuildTopLevel: allowlists.Bp2BuildDefaultTrueRecursively,
37		},
38	)
39
40	buildDir string
41)
42
43func checkError(t *testing.T, errs []error, expectedErr error) bool {
44	t.Helper()
45
46	if len(errs) != 1 {
47		return false
48	}
49	if strings.Contains(errs[0].Error(), expectedErr.Error()) {
50		return true
51	}
52
53	return false
54}
55
56func errored(t *testing.T, tc bp2buildTestCase, errs []error) bool {
57	t.Helper()
58	if tc.expectedErr != nil {
59		// Rely on checkErrors, as this test case is expected to have an error.
60		return false
61	}
62
63	if len(errs) > 0 {
64		for _, err := range errs {
65			t.Errorf("%s: %s", tc.description, err)
66		}
67		return true
68	}
69
70	// All good, continue execution.
71	return false
72}
73
74func runBp2BuildTestCaseSimple(t *testing.T, tc bp2buildTestCase) {
75	t.Helper()
76	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
77}
78
79type bp2buildTestCase struct {
80	description                string
81	moduleTypeUnderTest        string
82	moduleTypeUnderTestFactory android.ModuleFactory
83	blueprint                  string
84	expectedBazelTargets       []string
85	filesystem                 map[string]string
86	dir                        string
87	// An error with a string contained within the string of the expected error
88	expectedErr         error
89	unconvertedDepsMode unconvertedDepsMode
90}
91
92func runBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc bp2buildTestCase) {
93	t.Helper()
94	dir := "."
95	filesystem := make(map[string][]byte)
96	toParse := []string{
97		"Android.bp",
98	}
99	for f, content := range tc.filesystem {
100		if strings.HasSuffix(f, "Android.bp") {
101			toParse = append(toParse, f)
102		}
103		filesystem[f] = []byte(content)
104	}
105	config := android.TestConfig(buildDir, nil, tc.blueprint, filesystem)
106	ctx := android.NewTestContext(config)
107
108	registerModuleTypes(ctx)
109	ctx.RegisterModuleType(tc.moduleTypeUnderTest, tc.moduleTypeUnderTestFactory)
110	ctx.RegisterBp2BuildConfig(bp2buildConfig)
111	ctx.RegisterForBazelConversion()
112
113	_, parseErrs := ctx.ParseFileList(dir, toParse)
114	if errored(t, tc, parseErrs) {
115		return
116	}
117	_, resolveDepsErrs := ctx.ResolveDependencies(config)
118	if errored(t, tc, resolveDepsErrs) {
119		return
120	}
121
122	errs := append(parseErrs, resolveDepsErrs...)
123	if tc.expectedErr != nil && checkError(t, errs, tc.expectedErr) {
124		return
125	}
126
127	checkDir := dir
128	if tc.dir != "" {
129		checkDir = tc.dir
130	}
131	codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
132	codegenCtx.unconvertedDepMode = tc.unconvertedDepsMode
133	bazelTargets, errs := generateBazelTargetsForDir(codegenCtx, checkDir)
134	if tc.expectedErr != nil {
135		if checkError(t, errs, tc.expectedErr) {
136			return
137		} else {
138			t.Errorf("Expected error: %q, got: %q", tc.expectedErr, errs)
139		}
140	} else {
141		android.FailIfErrored(t, errs)
142	}
143	if actualCount, expectedCount := len(bazelTargets), len(tc.expectedBazelTargets); actualCount != expectedCount {
144		t.Errorf("%s: Expected %d bazel target (%s), got `%d`` (%s)",
145			tc.description, expectedCount, tc.expectedBazelTargets, actualCount, bazelTargets)
146	} else {
147		for i, target := range bazelTargets {
148			if w, g := tc.expectedBazelTargets[i], target.content; w != g {
149				t.Errorf(
150					"%s: Expected generated Bazel target to be `%s`, got `%s`",
151					tc.description, w, g)
152			}
153		}
154	}
155}
156
157type nestedProps struct {
158	Nested_prop *string
159}
160
161type EmbeddedProps struct {
162	Embedded_prop *string
163}
164
165type OtherEmbeddedProps struct {
166	Other_embedded_prop *string
167}
168
169type customProps struct {
170	EmbeddedProps
171	*OtherEmbeddedProps
172
173	Bool_prop     bool
174	Bool_ptr_prop *bool
175	// Ensure that properties tagged `blueprint:mutated` are omitted
176	Int_prop         int `blueprint:"mutated"`
177	Int64_ptr_prop   *int64
178	String_prop      string
179	String_ptr_prop  *string
180	String_list_prop []string
181
182	Nested_props     nestedProps
183	Nested_props_ptr *nestedProps
184
185	Arch_paths         []string `android:"path,arch_variant"`
186	Arch_paths_exclude []string `android:"path,arch_variant"`
187
188	// Prop used to indicate this conversion should be 1 module -> multiple targets
189	One_to_many_prop *bool
190}
191
192type customModule struct {
193	android.ModuleBase
194	android.BazelModuleBase
195
196	props customProps
197}
198
199// OutputFiles is needed because some instances of this module use dist with a
200// tag property which requires the module implements OutputFileProducer.
201func (m *customModule) OutputFiles(tag string) (android.Paths, error) {
202	return android.PathsForTesting("path" + tag), nil
203}
204
205func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
206	// nothing for now.
207}
208
209func customModuleFactoryBase() android.Module {
210	module := &customModule{}
211	module.AddProperties(&module.props)
212	android.InitBazelModule(module)
213	return module
214}
215
216func customModuleFactory() android.Module {
217	m := customModuleFactoryBase()
218	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibBoth)
219	return m
220}
221
222type testProps struct {
223	Test_prop struct {
224		Test_string_prop string
225	}
226}
227
228type customTestModule struct {
229	android.ModuleBase
230
231	props      customProps
232	test_props testProps
233}
234
235func (m *customTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
236	// nothing for now.
237}
238
239func customTestModuleFactoryBase() android.Module {
240	m := &customTestModule{}
241	m.AddProperties(&m.props)
242	m.AddProperties(&m.test_props)
243	return m
244}
245
246func customTestModuleFactory() android.Module {
247	m := customTestModuleFactoryBase()
248	android.InitAndroidModule(m)
249	return m
250}
251
252type customDefaultsModule struct {
253	android.ModuleBase
254	android.DefaultsModuleBase
255}
256
257func customDefaultsModuleFactoryBase() android.DefaultsModule {
258	module := &customDefaultsModule{}
259	module.AddProperties(&customProps{})
260	return module
261}
262
263func customDefaultsModuleFactoryBasic() android.Module {
264	return customDefaultsModuleFactoryBase()
265}
266
267func customDefaultsModuleFactory() android.Module {
268	m := customDefaultsModuleFactoryBase()
269	android.InitDefaultsModule(m)
270	return m
271}
272
273type EmbeddedAttr struct {
274	Embedded_attr *string
275}
276
277type OtherEmbeddedAttr struct {
278	Other_embedded_attr *string
279}
280
281type customBazelModuleAttributes struct {
282	EmbeddedAttr
283	*OtherEmbeddedAttr
284	String_ptr_prop  *string
285	String_list_prop []string
286	Arch_paths       bazel.LabelListAttribute
287}
288
289func (m *customModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
290	paths := bazel.LabelListAttribute{}
291
292	if p := m.props.One_to_many_prop; p != nil && *p {
293		customBp2buildOneToMany(ctx, m)
294		return
295	}
296
297	for axis, configToProps := range m.GetArchVariantProperties(ctx, &customProps{}) {
298		for config, props := range configToProps {
299			if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
300				paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrcExcludes(ctx, archProps.Arch_paths, archProps.Arch_paths_exclude))
301			}
302		}
303	}
304
305	paths.ResolveExcludes()
306
307	attrs := &customBazelModuleAttributes{
308		String_ptr_prop:  m.props.String_ptr_prop,
309		String_list_prop: m.props.String_list_prop,
310		Arch_paths:       paths,
311	}
312	attrs.Embedded_attr = m.props.Embedded_prop
313	if m.props.OtherEmbeddedProps != nil {
314		attrs.OtherEmbeddedAttr = &OtherEmbeddedAttr{Other_embedded_attr: m.props.OtherEmbeddedProps.Other_embedded_prop}
315	}
316
317	props := bazel.BazelTargetModuleProperties{
318		Rule_class: "custom",
319	}
320
321	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
322}
323
324// A bp2build mutator that uses load statements and creates a 1:M mapping from
325// module to target.
326func customBp2buildOneToMany(ctx android.TopDownMutatorContext, m *customModule) {
327
328	baseName := m.Name()
329	attrs := &customBazelModuleAttributes{}
330
331	myLibraryProps := bazel.BazelTargetModuleProperties{
332		Rule_class:        "my_library",
333		Bzl_load_location: "//build/bazel/rules:rules.bzl",
334	}
335	ctx.CreateBazelTargetModule(myLibraryProps, android.CommonAttributes{Name: baseName}, attrs)
336
337	protoLibraryProps := bazel.BazelTargetModuleProperties{
338		Rule_class:        "proto_library",
339		Bzl_load_location: "//build/bazel/rules:proto.bzl",
340	}
341	ctx.CreateBazelTargetModule(protoLibraryProps, android.CommonAttributes{Name: baseName + "_proto_library_deps"}, attrs)
342
343	myProtoLibraryProps := bazel.BazelTargetModuleProperties{
344		Rule_class:        "my_proto_library",
345		Bzl_load_location: "//build/bazel/rules:proto.bzl",
346	}
347	ctx.CreateBazelTargetModule(myProtoLibraryProps, android.CommonAttributes{Name: baseName + "_my_proto_library_deps"}, attrs)
348}
349
350// Helper method for tests to easily access the targets in a dir.
351func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) (BazelTargets, []error) {
352	// TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely
353	res, err := GenerateBazelTargets(codegenCtx, false)
354	return res.buildFileToTargets[dir], err
355}
356
357func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
358	ctx.RegisterModuleType("custom", customModuleFactory)
359	ctx.RegisterForBazelConversion()
360}
361
362func simpleModuleDoNotConvertBp2build(typ, name string) string {
363	return fmt.Sprintf(`
364%s {
365		name: "%s",
366		bazel_module: { bp2build_available: false },
367}`, typ, name)
368}
369
370type attrNameToString map[string]string
371
372func makeBazelTarget(typ, name string, attrs attrNameToString) string {
373	attrStrings := make([]string, 0, len(attrs)+1)
374	attrStrings = append(attrStrings, fmt.Sprintf(`    name = "%s",`, name))
375	for _, k := range android.SortedStringKeys(attrs) {
376		attrStrings = append(attrStrings, fmt.Sprintf("    %s = %s,", k, attrs[k]))
377	}
378	return fmt.Sprintf(`%s(
379%s
380)`, typ, strings.Join(attrStrings, "\n"))
381}
382