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	"io/ioutil"
20	"os"
21	"strings"
22	"testing"
23)
24
25var buildDir string
26
27func TestBpContainsTestHostPropsThrowsError(t *testing.T) {
28	ctx, _ := createContextAndConfig(t, `
29    csuite_test {
30      name: "plan_name",
31      test_config_template: "test_config.xml.template",
32      data_native_bins: "bin"
33    }
34  `)
35
36	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
37
38	android.FailIfNoMatchingErrors(t, `unrecognized property`, errs)
39}
40
41func TestBpContainsManifestThrowsError(t *testing.T) {
42	ctx, _ := createContextAndConfig(t, `
43    csuite_test {
44      name: "plan_name",
45      test_config_template: "test_config.xml.template",
46      test_config: "AndroidTest.xml"
47    }
48  `)
49
50	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
51
52	android.FailIfNoMatchingErrors(t, `unrecognized property`, errs)
53}
54
55func TestBpMissingNameThrowsError(t *testing.T) {
56	ctx, _ := createContextAndConfig(t, `
57    csuite_test {
58      test_config_template: "test_config.xml.template"
59    }
60  `)
61
62	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
63
64	android.FailIfNoMatchingErrors(t, `'name' is missing`, errs)
65}
66
67func TestBpMissingTemplatePathThrowsError(t *testing.T) {
68	ctx, config := createContextAndConfig(t, `
69    csuite_test {
70      name: "plan_name",
71    }
72  `)
73
74	ctx.ParseBlueprintsFiles("Android.bp")
75	_, errs := ctx.PrepareBuildActions(config)
76
77	android.FailIfNoMatchingErrors(t, `'test_config_template' is missing`, errs)
78}
79
80func TestValidBpMissingPlanIncludeDoesNotThrowError(t *testing.T) {
81	ctx, config := createContextAndConfig(t, `
82    csuite_test {
83      name: "plan_name",
84      test_config_template: "test_config.xml.template"
85    }
86  `)
87
88	parseBpAndBuild(t, ctx, config)
89}
90
91func TestValidBpMissingPlanIncludeGeneratesPlanXmlWithoutPlaceholders(t *testing.T) {
92	ctx, config := createContextAndConfig(t, `
93    csuite_test {
94      name: "plan_name",
95      test_config_template: "test_config.xml.template"
96    }
97  `)
98
99	parseBpAndBuild(t, ctx, config)
100
101	module := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common")
102	content := android.ContentFromFileRuleForTests(t, module.Output("config/plan_name.xml"))
103	if strings.Contains(content, "{") || strings.Contains(content, "}") {
104		t.Errorf("The generated plan name contains a placeholder: %s", content)
105	}
106}
107
108func TestGeneratedTestPlanContainsPlanName(t *testing.T) {
109	ctx, config := createContextAndConfig(t, `
110    csuite_test {
111      name: "plan_name",
112      test_config_template: "test_config.xml.template"
113    }
114  `)
115
116	parseBpAndBuild(t, ctx, config)
117
118	module := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common")
119	content := android.ContentFromFileRuleForTests(t, module.Output("config/plan_name.xml"))
120	if !strings.Contains(content, "plan_name") {
121		t.Errorf("The plan name is missing from the generated plan: %s", content)
122	}
123}
124
125func TestGeneratedTestPlanContainsTemplatePath(t *testing.T) {
126	ctx, config := createContextAndConfig(t, `
127    csuite_test {
128      name: "plan_name",
129      test_config_template: "test_config.xml.template"
130    }
131  `)
132
133	parseBpAndBuild(t, ctx, config)
134
135	module := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common")
136	content := android.ContentFromFileRuleForTests(t, module.Output("config/plan_name.xml"))
137	if !strings.Contains(content, "config/plan_name.xml.template") {
138		t.Errorf("The template path is missing from the generated plan: %s", content)
139	}
140}
141
142func TestTemplateFileCopyRuleExists(t *testing.T) {
143	ctx, config := createContextAndConfig(t, `
144    csuite_test {
145      name: "plan_name",
146      test_config_template: "test_config.xml.template"
147    }
148  `)
149
150	parseBpAndBuild(t, ctx, config)
151
152	params := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common").Rule("CSuite")
153	assertFileCopyRuleExists(t, params, "test_config.xml.template", "config/plan_name.xml.template")
154}
155
156func TestGeneratedTestPlanContainsPlanInclude(t *testing.T) {
157	ctx, config := createContextAndConfig(t, `
158    csuite_test {
159      name: "plan_name",
160      test_config_template: "test_config.xml.template",
161      test_plan_include: "include.xml"
162    }
163  `)
164
165	parseBpAndBuild(t, ctx, config)
166
167	module := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common")
168	content := android.ContentFromFileRuleForTests(t, module.Output("config/plan_name.xml"))
169	if !strings.Contains(content, `"includes/plan_name.xml"`) {
170		t.Errorf("The plan include path is missing from the generated plan: %s", content)
171	}
172}
173
174func TestPlanIncludeFileCopyRuleExists(t *testing.T) {
175	ctx, config := createContextAndConfig(t, `
176    csuite_test {
177      name: "plan_name",
178      test_config_template: "test_config.xml.template",
179      test_plan_include: "include.xml"
180    }
181  `)
182
183	parseBpAndBuild(t, ctx, config)
184
185	params := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common").Rule("CSuite")
186	assertFileCopyRuleExists(t, params, "include.xml", "config/includes/plan_name.xml")
187}
188
189func TestMain(m *testing.M) {
190	run := func() int {
191		setUp()
192		defer tearDown()
193
194		return m.Run()
195	}
196
197	os.Exit(run())
198}
199
200func parseBpAndBuild(t *testing.T, ctx *android.TestContext, config android.Config) {
201	_, parsingErrs := ctx.ParseBlueprintsFiles("Android.bp")
202	_, buildErrs := ctx.PrepareBuildActions(config)
203
204	android.FailIfErrored(t, parsingErrs)
205	android.FailIfErrored(t, buildErrs)
206}
207
208func assertFileCopyRuleExists(t *testing.T, params android.TestingBuildParams, src string, dst string) {
209	assertPathsContains(t, getAllInputPaths(params), src)
210	assertWritablePathsContainsRel(t, getAllOutputPaths(params), dst)
211	if !strings.HasPrefix(params.RuleParams.Command, "cp") {
212		t.Errorf("'cp' command is missing.")
213	}
214}
215
216func assertPathsContains(t *testing.T, paths android.Paths, path string) {
217	for _, p := range paths {
218		if p.String() == path {
219			return
220		}
221	}
222	t.Errorf("Cannot find expected path %s", path)
223}
224
225func assertWritablePathsContainsRel(t *testing.T, paths android.WritablePaths, relPath string) {
226	for _, path := range paths {
227		if path.Rel() == relPath {
228			return
229		}
230	}
231	t.Errorf("Cannot find expected relative path %s", relPath)
232}
233
234func getAllOutputPaths(params android.TestingBuildParams) android.WritablePaths {
235	var paths []android.WritablePath
236	if params.Output != nil {
237		paths = append(paths, params.Output)
238	}
239	if params.ImplicitOutput != nil {
240		paths = append(paths, params.ImplicitOutput)
241	}
242	if params.SymlinkOutput != nil {
243		paths = append(paths, params.SymlinkOutput)
244	}
245	paths = append(paths, params.Outputs...)
246	paths = append(paths, params.ImplicitOutputs...)
247	paths = append(paths, params.SymlinkOutputs...)
248
249	return paths
250}
251
252func getAllInputPaths(params android.TestingBuildParams) android.Paths {
253	var paths []android.Path
254	if params.Input != nil {
255		paths = append(paths, params.Input)
256	}
257	if params.Implicit != nil {
258		paths = append(paths, params.Implicit)
259	}
260	paths = append(paths, params.Inputs...)
261	paths = append(paths, params.Implicits...)
262
263	return paths
264}
265
266func setUp() {
267	var err error
268	buildDir, err = ioutil.TempDir("", "soong_csuite_test")
269	if err != nil {
270		panic(err)
271	}
272}
273
274func tearDown() {
275	os.RemoveAll(buildDir)
276}
277
278func createContextAndConfig(t *testing.T, bp string) (*android.TestContext, android.Config) {
279	t.Helper()
280	config := android.TestArchConfig(buildDir, nil, bp, nil)
281	ctx := android.NewTestArchContext(config)
282	ctx.RegisterModuleType("csuite_test", CSuiteTestFactory)
283	ctx.Register()
284
285	return ctx, config
286}
287