• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2023 The Android Open Source Project
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 soong_wayland_protocol_codegen
16
17import (
18	"os"
19	"regexp"
20	"testing"
21
22	"android/soong/android"
23)
24
25func TestMain(m *testing.M) {
26	os.Exit(m.Run())
27}
28
29var prepareForCodeGenTest = android.GroupFixturePreparers(
30	android.PrepareForTestWithArchMutator,
31	android.PrepareForTestWithDefaults,
32	android.PrepareForTestWithFilegroup,
33	android.GroupFixturePreparers(
34		android.FixtureRegisterWithContext(registerCodeGenBuildComponents),
35	),
36	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
37		android.RegisterPrebuiltMutators(ctx)
38		ctx.RegisterModuleType("fake_android_host_tool", fakeAndroidHostToolFactory)
39	}),
40	android.FixtureMergeMockFs(android.MockFS{
41		"android_host_tool": nil,
42		"tool_src_file":     nil,
43		"tool_src_file_1":   nil,
44		"tool_src_file_2":   nil,
45		"src_file":          nil,
46		"src_file_1":        nil,
47		"src_file_2":        nil,
48	}),
49)
50
51func testCodeGenBp() string {
52	return `
53		fake_android_host_tool {
54			name: "host_tool",
55		}
56
57		filegroup {
58			name: "tool_single_source_file_filegroup",
59			srcs: [
60				"tool_src_file",
61			],
62		}
63
64		filegroup {
65			name: "tool_multi_source_files_filegroup",
66			srcs: [
67				"tool_src_file_1",
68				"tool_src_file_2",
69			],
70		}
71
72		filegroup {
73			name: "single_source_filegroup",
74			srcs: [
75				"src_file",
76			],
77		}
78
79		filegroup {
80			name: "multi_source_filegroup",
81			srcs: [
82				"src_file_1",
83				"src_file_2",
84			],
85		}
86
87		filegroup {
88			name: "empty_filegroup",
89		}
90	`
91}
92
93func TestWaylandCodeGen(t *testing.T) {
94	testcases := []struct {
95		name string
96		prop string
97
98		err   string
99		cmds  []string
100		files []string
101	}{
102		{
103			name: "single_source_with_host_tool",
104			prop: `
105				tools: ["host_tool"],
106				srcs: ["src_file"],
107				output: "prefix_$(in)_suffix",
108				cmd: "$(location host_tool) gen < $(in) > $(out)",
109			`,
110			cmds: []string{
111				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file > __SBOX_SANDBOX_DIR__/out/prefix_src_file_suffix'",
112			},
113			files: []string{
114				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_suffix",
115			},
116		},
117		{
118			name: "multi_source_with_host_tool",
119			prop: `
120				tools: ["host_tool"],
121				srcs: ["src_file_1", "src_file_2"],
122				output: "prefix_$(in)_suffix",
123				cmd: "$(location host_tool) gen < $(in) > $(out)",
124			`,
125			cmds: []string{
126				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file_1 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_1_suffix' && bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file_2 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_2_suffix'",
127			},
128			files: []string{
129				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_1_suffix",
130				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_2_suffix",
131			},
132		},
133		{
134			name: "single_source_filegroup_with_host_tool",
135			prop: `
136				tools: ["host_tool"],
137				srcs: [":single_source_filegroup"],
138				output: "prefix_$(in)_suffix",
139				cmd: "$(location host_tool) gen < $(in) > $(out)",
140			`,
141			cmds: []string{
142				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file > __SBOX_SANDBOX_DIR__/out/prefix_src_file_suffix'",
143			},
144			files: []string{
145				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_suffix",
146			},
147		},
148		{
149			name: "multi_source_filegroup_with_host_tool",
150			prop: `
151				tools: ["host_tool"],
152				srcs: [":multi_source_filegroup"],
153				output: "prefix_$(in)_suffix",
154				cmd: "$(location host_tool) gen < $(in) > $(out)",
155			`,
156			cmds: []string{
157				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file_1 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_1_suffix' && bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file_2 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_2_suffix'",
158			},
159			files: []string{
160				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_1_suffix",
161				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_2_suffix",
162			},
163		},
164		{
165			name: "single_source_with_single_tool_file",
166			prop: `
167				tool_files: ["tool_src_file"],
168				srcs: ["src_file"],
169				output: "prefix_$(in)_suffix",
170				cmd: "$(location tool_src_file) gen < $(in) > $(out)",
171			`,
172			cmds: []string{
173				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file gen < src_file > __SBOX_SANDBOX_DIR__/out/prefix_src_file_suffix'",
174			},
175			files: []string{
176				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_suffix",
177			},
178		},
179		{
180			name: "multi_source_with_single_tool_file",
181			prop: `
182				tool_files: ["tool_src_file"],
183				srcs: ["src_file_1", "src_file_2"],
184				output: "prefix_$(in)_suffix",
185				cmd: "$(location tool_src_file) gen < $(in) > $(out)",
186			`,
187			cmds: []string{
188				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file gen < src_file_1 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_1_suffix' && bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file gen < src_file_2 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_2_suffix'",
189			},
190			files: []string{
191				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_1_suffix",
192				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_2_suffix",
193			},
194		},
195		{
196			name: "single_source_filegroup_with_single_tool_file",
197			prop: `
198				tool_files: ["tool_src_file"],
199				srcs: [":single_source_filegroup"],
200				output: "prefix_$(in)_suffix",
201				cmd: "$(location tool_src_file) gen < $(in) > $(out)",
202			`,
203			cmds: []string{
204				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file gen < src_file > __SBOX_SANDBOX_DIR__/out/prefix_src_file_suffix'",
205			},
206			files: []string{
207				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_suffix",
208			},
209		},
210		{
211			name: "multi_source_filegroup_with_single_tool_file",
212			prop: `
213				tool_files: ["tool_src_file"],
214				srcs: [":multi_source_filegroup"],
215				output: "prefix_$(in)_suffix",
216				cmd: "$(location tool_src_file) gen < $(in) > $(out)",
217			`,
218			cmds: []string{
219				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file gen < src_file_1 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_1_suffix' && bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file gen < src_file_2 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_2_suffix'",
220			},
221			files: []string{
222				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_1_suffix",
223				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_2_suffix",
224			},
225		},
226		{
227			name: "multiple_tool_files",
228			prop: `
229				tool_files: ["tool_src_file_1", "tool_src_file_2"],
230				srcs: ["src_file"],
231				output: "prefix_$(in)_suffix",
232				cmd: "$(location tool_src_file_1) $(location tool_src_file_2) gen < $(in) > $(out)",
233			`,
234			cmds: []string{
235				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file_1 __SBOX_SANDBOX_DIR__/tools/src/tool_src_file_2 gen < src_file > __SBOX_SANDBOX_DIR__/out/prefix_src_file_suffix'",
236			},
237			files: []string{
238				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_suffix",
239			},
240		},
241		{
242			name: "output_template_explicit_base_only",
243			prop: `
244				tools: ["host_tool"],
245				srcs: ["txt/a/file.txt"],
246				output: "$(in:base)",
247				cmd: "$(location host_tool) gen < $(in) > $(out)",
248			`,
249			cmds: []string{
250				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < txt/a/file.txt > __SBOX_SANDBOX_DIR__/out/file'",
251			},
252			files: []string{
253				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/file",
254			},
255		},
256		{
257			name: "output_template_explicit_base_and_ext",
258			prop: `
259				tools: ["host_tool"],
260				srcs: ["txt/a/file.txt"],
261				output: "$(in:base.ext)",
262				cmd: "$(location host_tool) gen < $(in) > $(out)",
263			`,
264			cmds: []string{
265				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < txt/a/file.txt > __SBOX_SANDBOX_DIR__/out/file.txt'",
266			},
267			files: []string{
268				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/file.txt",
269			},
270		},
271		{
272			name: "output_template_explicit_path_and_base",
273			prop: `
274				tools: ["host_tool"],
275				srcs: ["txt/a/file.txt"],
276				output: "$(in:path/base)",
277				cmd: "$(location host_tool) gen < $(in) > $(out)",
278			`,
279			cmds: []string{
280				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < txt/a/file.txt > __SBOX_SANDBOX_DIR__/out/txt/a/file'",
281			},
282			files: []string{
283				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/txt/a/file",
284			},
285		},
286		{
287			name: "output_template_explicit_path_and_base_and_ext",
288			prop: `
289				tools: ["host_tool"],
290				srcs: ["txt/a/file.txt"],
291				output: "$(in:path/base.ext)",
292				cmd: "$(location host_tool) gen < $(in) > $(out)",
293			`,
294			cmds: []string{
295				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < txt/a/file.txt > __SBOX_SANDBOX_DIR__/out/txt/a/file.txt'",
296			},
297			files: []string{
298				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/txt/a/file.txt",
299			},
300		},
301		{
302			name: "single_source_file_does_not_need_distinct_outputs",
303			prop: `
304				tools: ["host_tool"],
305				srcs: ["src_file"],
306				output: "output",
307				cmd: "$(location host_tool) gen < $(in) > $(out)",
308			`,
309			cmds: []string{
310				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file > __SBOX_SANDBOX_DIR__/out/output'",
311			},
312			files: []string{
313				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/output",
314			},
315		},
316		{
317			name: "legacy_prefix_suffix",
318			prop: `
319				tools: ["host_tool"],
320				srcs: ["src_file"],
321				prefix: "legacy_prefix_",
322				suffix: "_legacy_suffix",
323				cmd: "$(location host_tool) gen < $(in) > $(out)",
324			`,
325			cmds: []string{
326				"bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file > __SBOX_SANDBOX_DIR__/out/legacy_prefix_src_file_legacy_suffix'",
327			},
328			files: []string{
329				"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/legacy_prefix_src_file_legacy_suffix",
330			},
331		},
332		{
333			name: "error_if_no_sources",
334			prop: `
335				tools: ["host_tool"],
336				cmd: "$(location host_tool) gen < $(in) > $(out)",
337			`,
338			err: "must have at least one source file",
339		},
340		{
341			name: "error_if_no_filegroup_sources",
342			prop: `
343				tools: ["host_tool"],
344				srcs: [":empty_filegroup"],
345				cmd: "$(location host_tool) gen < $(in) > $(out)",
346			`,
347			err: "must have at least one source file",
348		},
349		{
350			name: "error_if_in_outputs_are_not_distinct",
351			prop: `
352				tools: ["host_tool"],
353				tool_files: ["tool_src_file"],
354				srcs: ["src_file_1", "src_file_2"],
355				output: "not_unique",
356				cmd: "$(location)"
357			`,
358			err: "Android.bp:39:2: module \"codegen\": generation conflict: both 'src_file_1' and 'src_file_2' generate 'not_unique'",
359		},
360		{
361			name: "error_if_output_expansion_fails",
362			prop: `
363				tools: ["host_tool"],
364				tool_files: ["tool_src_file"],
365				srcs: ["src_file"],
366				output: "prefix_$(bad)_suffix",
367				cmd: "$(location)"
368			`,
369			err: "Android.bp:45:11: module \"codegen\": output: unknown variable '$(bad)'",
370		},
371		{
372			name: "error_if_cmd_expansion_fails",
373			prop: `
374				tools: ["host_tool"],
375				tool_files: ["tool_src_file"],
376				srcs: ["src_file"],
377				output: "prefix_$(in)_suffix",
378				cmd: "$(location bad_name)"
379			`,
380			err: "Android.bp:46:8: module \"codegen\": cmd: unknown location label \"bad_name\"",
381		},
382	}
383
384	for _, test := range testcases {
385		t.Run(test.name, func(t *testing.T) {
386			bp := "wayland_protocol_codegen {\n"
387			bp += `name: "codegen",` + "\n"
388			bp += test.prop
389			bp += "}\n"
390
391			var expectedErrors []string
392			if test.err != "" {
393				expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
394			}
395
396			result := prepareForCodeGenTest.
397				ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
398				RunTestWithBp(t, testCodeGenBp()+bp)
399
400			if expectedErrors != nil {
401				return
402			}
403
404			gen := result.Module("codegen", "").(*Module)
405			android.AssertDeepEquals(t, "cmd", test.cmds, gen.rawCommands)
406			android.AssertPathsRelativeToTopEquals(t, "files", test.files, gen.outputFiles)
407		})
408	}
409}
410
411func TestGenruleWithBazel(t *testing.T) {
412	bp := `
413	    wayland_protocol_codegen {
414				name: "mixed_codegen",
415				srcs: ["src_file"],
416				bazel_module: { label: "//example:bazel_codegen" },
417		}
418	`
419
420	result := android.GroupFixturePreparers(
421		prepareForCodeGenTest, android.FixtureModifyConfig(func(config android.Config) {
422			config.BazelContext = android.MockBazelContext{
423				OutputBaseDir: "outputbase",
424				LabelToOutputFiles: map[string][]string{
425					"//example:bazel_codegen": {"bazelone.txt", "bazeltwo.txt"}}}
426		})).RunTestWithBp(t, testCodeGenBp()+bp)
427
428	gen := result.Module("mixed_codegen", "").(*Module)
429
430	expectedOutputFiles := []string{"outputbase/execroot/__main__/bazelone.txt",
431		"outputbase/execroot/__main__/bazeltwo.txt"}
432	android.AssertDeepEquals(t, "output files", expectedOutputFiles, gen.outputFiles.Strings())
433	android.AssertDeepEquals(t, "output deps", expectedOutputFiles, gen.outputDeps.Strings())
434}
435
436func TestDefaults(t *testing.T) {
437	bp := `
438		wayland_protocol_codegen_defaults {
439			name: "gen_defaults1",
440			cmd: "cp $(in) $(out)",
441			output: "$(in).h",
442		}
443
444		wayland_protocol_codegen_defaults {
445			name: "gen_defaults2",
446			srcs: ["in1"],
447		}
448
449		wayland_protocol_codegen {
450			name: "codegen",
451			defaults: ["gen_defaults1", "gen_defaults2"],
452		}
453		`
454
455	result := prepareForCodeGenTest.RunTestWithBp(t, testCodeGenBp()+bp)
456
457	gen := result.Module("codegen", "").(*Module)
458
459	expectedCmd := "bash -c cp in1 __SBOX_SANDBOX_DIR__/out/in1.h"
460	android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0])
461
462	expectedSrcs := []string{"in1"}
463	android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.Srcs)
464
465	expectedFiles := []string{"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/in1.h"}
466	android.AssertPathsRelativeToTopEquals(t, "files", expectedFiles, gen.outputFiles)
467}
468
469type fakeAndroidHostTool struct {
470	android.ModuleBase
471	outputFile android.Path
472}
473
474func fakeAndroidHostToolFactory() android.Module {
475	module := &fakeAndroidHostTool{}
476	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
477	return module
478}
479
480func (t *fakeAndroidHostTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
481	t.outputFile = android.PathForTesting("out", ctx.ModuleName())
482}
483
484func (t *fakeAndroidHostTool) HostToolPath() android.OptionalPath {
485	return android.OptionalPathForPath(t.outputFile)
486}
487
488var _ android.HostToolProvider = (*fakeAndroidHostTool)(nil)
489