1// Copyright 2020 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 csuite
16
17import (
18	"android/soong/android"
19	"android/soong/java"
20	"io/ioutil"
21	"os"
22	"strings"
23	"testing"
24)
25
26var buildDir string
27
28func TestBpContainsTestHostPropsThrowsError(t *testing.T) {
29	createContextAndConfigExpectingErrors(t, `
30		csuite_test {
31			name: "plan_name",
32			test_config_template: "config_template.xml",
33			data_native_bins: "bin"
34		}
35	`,
36		"unrecognized property",
37	)
38}
39
40func TestBpContainsManifestThrowsError(t *testing.T) {
41	createContextAndConfigExpectingErrors(t, `
42		csuite_test {
43			name: "plan_name",
44			test_config_template: "config_template.xml",
45			test_config: "AndroidTest.xml"
46		}
47	`,
48		"unrecognized property",
49	)
50}
51
52func TestBpMissingNameThrowsError(t *testing.T) {
53	createContextAndConfigExpectingErrors(t, `
54		csuite_test {
55			test_config_template: "config_template.xml"
56		}
57	`,
58		`'name' is missing`,
59	)
60}
61
62func TestBpMissingTemplatePathThrowsError(t *testing.T) {
63	createContextAndConfigExpectingErrors(t, `
64		csuite_test {
65			name: "plan_name",
66		}
67	`,
68		`'test_config_template' is missing`,
69	)
70}
71
72func TestBpTemplatePathUnexpectedFileExtensionThrowsError(t *testing.T) {
73	createContextAndConfigExpectingErrors(t, `
74		csuite_test {
75			name: "plan_name",
76			test_config_template: "config_template.xml.template"
77		}
78	`,
79		`Config template path should ends with .xml`,
80	)
81}
82
83func TestBpExtraTemplateUnexpectedFileExtensionThrowsError(t *testing.T) {
84	createContextAndConfigExpectingErrors(t, `
85		csuite_test {
86			name: "plan_name",
87			test_config_template: "config_template.xml",
88			extra_test_config_templates: ["another.xml.template"]
89		}
90	`,
91		`Config template path should ends with .xml`,
92	)
93}
94
95func TestBpValidExtraTemplateDoesNotThrowError(t *testing.T) {
96	createContextAndConfig(t, `
97		csuite_test {
98			name: "plan_name",
99			test_config_template: "config_template.xml",
100			extra_test_config_templates: ["another.xml"]
101		}
102	`)
103}
104
105func TestValidBpMissingPlanIncludeDoesNotThrowError(t *testing.T) {
106	createContextAndConfig(t, `
107		csuite_test {
108			name: "plan_name",
109			test_config_template: "config_template.xml"
110		}
111	`)
112}
113
114func TestValidBpMissingPlanIncludeGeneratesPlanXmlWithoutPlaceholders(t *testing.T) {
115	ctx, config := createContextAndConfig(t, `
116		csuite_test {
117			name: "plan_name",
118			test_config_template: "config_template.xml"
119		}
120	`)
121
122	module := ctx.ModuleForTests("plan_name", config.BuildOS.String()+"_common")
123	content := android.ContentFromFileRuleForTests(t, ctx, module.Output("config/plan_name.xml"))
124	if strings.Contains(content, "{") || strings.Contains(content, "}") {
125		t.Errorf("The generated plan name contains a placeholder: %s", content)
126	}
127}
128
129func TestGeneratedTestPlanContainsPlanName(t *testing.T) {
130	ctx, config := createContextAndConfig(t, `
131		csuite_test {
132			name: "plan_name",
133			test_config_template: "config_template.xml"
134		}
135	`)
136
137	module := ctx.ModuleForTests("plan_name", config.BuildOS.String()+"_common")
138	content := android.ContentFromFileRuleForTests(t, ctx, module.Output("config/plan_name.xml"))
139	if !strings.Contains(content, "plan_name") {
140		t.Errorf("The plan name is missing from the generated plan: %s", content)
141	}
142}
143
144func TestGeneratedTestPlanContainsTemplatePath(t *testing.T) {
145	ctx, config := createContextAndConfig(t, `
146		csuite_test {
147			name: "plan_name",
148			test_config_template: "config_template.xml"
149		}
150	`)
151
152	module := ctx.ModuleForTests("plan_name", config.BuildOS.String()+"_common")
153	content := android.ContentFromFileRuleForTests(t, ctx, module.Output("config/plan_name.xml"))
154	if !strings.Contains(content, "config/plan_name/config_template.xml.template") {
155		t.Errorf("The template path is missing from the generated plan: %s", content)
156	}
157}
158
159func TestGeneratedTestPlanContainsExtraTemplatePath(t *testing.T) {
160	ctx, config := createContextAndConfig(t, `
161		csuite_test {
162			name: "plan_name",
163			test_config_template: "config_template.xml",
164			extra_test_config_templates: ["extra.xml"]
165		}
166	`)
167
168	module := ctx.ModuleForTests("plan_name", config.BuildOS.String()+"_common")
169	content := android.ContentFromFileRuleForTests(t, ctx, module.Output("config/plan_name.xml"))
170	if !strings.Contains(content, "config/plan_name/extra.xml.template") {
171		t.Errorf("The extra template path is missing from the generated plan: %s", content)
172	}
173	if !strings.Contains(content, "extra-templates") {
174		t.Errorf("The extra-templates param is missing from the generated plan: %s", content)
175	}
176}
177
178func TestGeneratedTestPlanDoesNotContainExtraTemplatePath(t *testing.T) {
179	ctx, config := createContextAndConfig(t, `
180		csuite_test {
181			name: "plan_name",
182			test_config_template: "config_template.xml"
183		}
184	`)
185
186	module := ctx.ModuleForTests("plan_name", config.BuildOS.String()+"_common")
187	content := android.ContentFromFileRuleForTests(t, ctx, module.Output("config/plan_name.xml"))
188	if strings.Contains(content, "extra-templates") {
189		t.Errorf("The extra-templates param should not be included in the generated plan: %s", content)
190	}
191}
192
193func TestTemplateFileCopyRuleExists(t *testing.T) {
194	ctx, config := createContextAndConfig(t, `
195		csuite_test {
196			name: "plan_name",
197			test_config_template: "config_template.xml"
198		}
199	`)
200
201	params := ctx.ModuleForTests("plan_name", config.BuildOS.String()+"_common").Rule("CSuite")
202	assertFileCopyRuleExists(t, params, "config_template.xml", "config/plan_name/config_template.xml.template")
203}
204
205func TestExtraTemplateFileCopyRuleExists(t *testing.T) {
206	ctx, config := createContextAndConfig(t, `
207		csuite_test {
208			name: "plan_name",
209			test_config_template: "config_template.xml",
210			extra_test_config_templates: ["extra.xml"]
211		}
212	`)
213
214	params := ctx.ModuleForTests("plan_name", config.BuildOS.String()+"_common").Rule("CSuite")
215	assertFileCopyRuleExists(t, params, "config_template.xml", "config/plan_name/extra.xml.template")
216}
217
218func TestGeneratedTestPlanContainsPlanInclude(t *testing.T) {
219	ctx, config := createContextAndConfig(t, `
220		csuite_test {
221			name: "plan_name",
222			test_config_template: "config_template.xml",
223			test_plan_include: "include.xml"
224		}
225	`)
226
227	module := ctx.ModuleForTests("plan_name", config.BuildOS.String()+"_common")
228	content := android.ContentFromFileRuleForTests(t, ctx, module.Output("config/plan_name.xml"))
229	if !strings.Contains(content, `"includes/plan_name.xml"`) {
230		t.Errorf("The plan include path is missing from the generated plan: %s", content)
231	}
232}
233
234func TestPlanIncludeFileCopyRuleExists(t *testing.T) {
235	ctx, config := createContextAndConfig(t, `
236		csuite_test {
237			name: "plan_name",
238			test_config_template: "config_template.xml",
239			test_plan_include: "include.xml"
240		}
241	`)
242
243	params := ctx.ModuleForTests("plan_name", config.BuildOS.String()+"_common").Rule("CSuite")
244	assertFileCopyRuleExists(t, params, "include.xml", "config/includes/plan_name.xml")
245}
246
247func TestMain(m *testing.M) {
248	run := func() int {
249		setUp()
250		defer tearDown()
251
252		return m.Run()
253	}
254
255	os.Exit(run())
256}
257
258func assertFileCopyRuleExists(t *testing.T, params android.TestingBuildParams, src string, dst string) {
259	assertPathsContains(t, getAllInputPaths(params), src)
260	assertWritablePathsContainsRel(t, getAllOutputPaths(params), dst)
261	if !strings.HasPrefix(params.RuleParams.Command, "cp") {
262		t.Errorf("'cp' command is missing.")
263	}
264}
265
266func assertPathsContains(t *testing.T, paths android.Paths, path string) {
267	for _, p := range paths {
268		if p.String() == path {
269			return
270		}
271	}
272	t.Errorf("Cannot find expected path %s", path)
273}
274
275func assertWritablePathsContainsRel(t *testing.T, paths android.WritablePaths, relPath string) {
276	for _, path := range paths {
277		if path.Rel() == relPath {
278			return
279		}
280	}
281	t.Errorf("Cannot find expected relative path %s", relPath)
282}
283
284func getAllOutputPaths(params android.TestingBuildParams) android.WritablePaths {
285	var paths []android.WritablePath
286	if params.Output != nil {
287		paths = append(paths, params.Output)
288	}
289	if params.ImplicitOutput != nil {
290		paths = append(paths, params.ImplicitOutput)
291	}
292	paths = append(paths, params.Outputs...)
293	paths = append(paths, params.ImplicitOutputs...)
294
295	return paths
296}
297
298func getAllInputPaths(params android.TestingBuildParams) android.Paths {
299	var paths []android.Path
300	if params.Input != nil {
301		paths = append(paths, params.Input)
302	}
303	if params.Implicit != nil {
304		paths = append(paths, params.Implicit)
305	}
306	paths = append(paths, params.Inputs...)
307	paths = append(paths, params.Implicits...)
308
309	return paths
310}
311
312func setUp() {
313	var err error
314	buildDir, err = ioutil.TempDir("", "soong_csuite_test")
315	if err != nil {
316		panic(err)
317	}
318}
319
320func tearDown() {
321	os.RemoveAll(buildDir)
322}
323
324func createContextAndConfig(t *testing.T, bp string) (*android.TestContext, android.Config) {
325	return createContextAndConfigExpectingErrors(t, bp, "")
326}
327
328func createContextAndConfigExpectingErrors(t *testing.T, bp string, error string) (*android.TestContext, android.Config) {
329	t.Helper()
330
331	testPreparer := android.GroupFixturePreparers(
332		java.PrepareForTestWithJavaDefaultModules,
333		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
334			ctx.RegisterModuleType("csuite_test", CSuiteTestFactory)
335		}),
336		android.FixtureWithRootAndroidBp(bp),
337	)
338
339	if error != "" {
340		testPreparer = testPreparer.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(error))
341	}
342
343	result := testPreparer.RunTest(t)
344
345	return result.TestContext, result.Config
346}
347