blob: 0d690d428c80a790cdfe22edfd82da39667044a8 [file] [log] [blame]
Colin Cross2a076922018-10-04 23:28:25 -07001// Copyright 2018 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 genrule
16
17import (
18 "io/ioutil"
19 "os"
20 "strings"
21 "testing"
22
23 "android/soong/android"
24)
25
26var buildDir string
27
28func setUp() {
29 var err error
30 buildDir, err = ioutil.TempDir("", "soong_java_test")
31 if err != nil {
32 panic(err)
33 }
34}
35
36func tearDown() {
37 os.RemoveAll(buildDir)
38}
39
40func TestMain(m *testing.M) {
41 run := func() int {
42 setUp()
43 defer tearDown()
44
45 return m.Run()
46 }
47
48 os.Exit(run())
49}
50
51func testContext(config android.Config, bp string,
52 fs map[string][]byte) *android.TestContext {
53
54 ctx := android.NewTestArchContext()
55 ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
56 ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(GenRuleFactory))
57 ctx.RegisterModuleType("tool", android.ModuleFactoryAdaptor(toolFactory))
58 ctx.Register()
59
60 bp += `
61 tool {
62 name: "tool",
63 }
64
65 filegroup {
66 name: "tool_files",
67 srcs: [
68 "tool_file1",
69 "tool_file2",
70 ],
71 }
72
73 filegroup {
74 name: "1tool_file",
75 srcs: [
76 "tool_file1",
77 ],
78 }
79
80 filegroup {
81 name: "ins",
82 srcs: [
83 "in1",
84 "in2",
85 ],
86 }
87
88 filegroup {
89 name: "1in",
90 srcs: [
91 "in1",
92 ],
93 }
94
95 filegroup {
96 name: "empty",
97 }
98 `
99
100 mockFS := map[string][]byte{
101 "Android.bp": []byte(bp),
102 "tool": nil,
103 "tool_file1": nil,
104 "tool_file2": nil,
105 "in1": nil,
106 "in2": nil,
107 }
108
109 for k, v := range fs {
110 mockFS[k] = v
111 }
112
113 ctx.MockFileSystem(mockFS)
114
115 return ctx
116}
117
118func TestGenruleCmd(t *testing.T) {
119 testcases := []struct {
120 name string
121 prop string
122
123 err string
124 expect string
125 }{
126 {
127 name: "empty location tool",
128 prop: `
129 tools: ["tool"],
130 out: ["out"],
131 cmd: "$(location) > $(out)",
132 `,
133 expect: "out/tool > __SBOX_OUT_FILES__",
134 },
135 {
136 name: "empty location tool file",
137 prop: `
138 tool_files: ["tool_file1"],
139 out: ["out"],
140 cmd: "$(location) > $(out)",
141 `,
142 expect: "tool_file1 > __SBOX_OUT_FILES__",
143 },
144 {
145 name: "empty location tool file fg",
146 prop: `
147 tool_files: [":1tool_file"],
148 out: ["out"],
149 cmd: "$(location) > $(out)",
150 `,
151 expect: "tool_file1 > __SBOX_OUT_FILES__",
152 },
153 {
154 name: "empty location tool and tool file",
155 prop: `
156 tools: ["tool"],
157 tool_files: ["tool_file1"],
158 out: ["out"],
159 cmd: "$(location) > $(out)",
160 `,
161 expect: "out/tool > __SBOX_OUT_FILES__",
162 },
163 {
164 name: "tool",
165 prop: `
166 tools: ["tool"],
167 out: ["out"],
168 cmd: "$(location tool) > $(out)",
169 `,
170 expect: "out/tool > __SBOX_OUT_FILES__",
171 },
172 {
173 name: "tool file",
174 prop: `
175 tool_files: ["tool_file1"],
176 out: ["out"],
177 cmd: "$(location tool_file1) > $(out)",
178 `,
179 expect: "tool_file1 > __SBOX_OUT_FILES__",
180 },
181 {
182 name: "tool file fg",
183 prop: `
184 tool_files: [":1tool_file"],
185 out: ["out"],
186 cmd: "$(location tool_file1) > $(out)",
187 `,
188 expect: "tool_file1 > __SBOX_OUT_FILES__",
189 },
190 {
191 name: "tool files",
192 prop: `
193 tool_files: [":tool_files"],
194 out: ["out"],
195 cmd: "$(location tool_file1) $(location tool_file2) > $(out)",
196 `,
197 expect: "tool_file1 tool_file2 > __SBOX_OUT_FILES__",
198 },
199 {
200 name: "in1",
201 prop: `
202 srcs: ["in1"],
203 out: ["out"],
204 cmd: "cat $(in) > $(out)",
205 `,
206 expect: "cat ${in} > __SBOX_OUT_FILES__",
207 },
208 {
209 name: "in1 fg",
210 prop: `
211 srcs: [":1in"],
212 out: ["out"],
213 cmd: "cat $(in) > $(out)",
214 `,
215 expect: "cat ${in} > __SBOX_OUT_FILES__",
216 },
217 {
218 name: "ins",
219 prop: `
220 srcs: ["in1", "in2"],
221 out: ["out"],
222 cmd: "cat $(in) > $(out)",
223 `,
224 expect: "cat ${in} > __SBOX_OUT_FILES__",
225 },
226 {
227 name: "ins fg",
228 prop: `
229 srcs: [":ins"],
230 out: ["out"],
231 cmd: "cat $(in) > $(out)",
232 `,
233 expect: "cat ${in} > __SBOX_OUT_FILES__",
234 },
235 {
236 name: "outs",
237 prop: `
238 out: ["out", "out2"],
239 cmd: "echo foo > $(out)",
240 `,
241 expect: "echo foo > __SBOX_OUT_FILES__",
242 },
243 {
244 name: "depfile",
245 prop: `
246 out: ["out"],
247 depfile: true,
248 cmd: "echo foo > $(out) && touch $(depfile)",
249 `,
250 expect: "echo foo > __SBOX_OUT_FILES__ && touch __SBOX_DEPFILE__",
251 },
252 {
253 name: "gendir",
254 prop: `
255 out: ["out"],
256 cmd: "echo foo > $(genDir)/foo && cp $(genDir)/foo $(out)",
257 `,
258 expect: "echo foo > __SBOX_OUT_DIR__/foo && cp __SBOX_OUT_DIR__/foo __SBOX_OUT_FILES__",
259 },
260
261 {
262 name: "error empty location",
263 prop: `
264 out: ["out"],
265 cmd: "$(location) > $(out)",
266 `,
267 err: "at least one `tools` or `tool_files` is required if $(location) is used",
268 },
269 {
270 name: "error location",
271 prop: `
272 out: ["out"],
273 cmd: "echo foo > $(location missing)",
274 `,
275 err: `unknown location label "missing"`,
276 },
277 {
278 name: "error variable",
279 prop: `
280 out: ["out"],
281 srcs: ["in1"],
282 cmd: "echo $(foo) > $(out)",
283 `,
284 err: `unknown variable '$(foo)'`,
285 },
286 {
287 name: "error depfile",
288 prop: `
289 out: ["out"],
290 cmd: "echo foo > $(out) && touch $(depfile)",
291 `,
292 err: "$(depfile) used without depfile property",
293 },
294 {
295 name: "error no depfile",
296 prop: `
297 out: ["out"],
298 depfile: true,
299 cmd: "echo foo > $(out)",
300 `,
301 err: "specified depfile=true but did not include a reference to '${depfile}' in cmd",
302 },
303 {
304 name: "error no out",
305 prop: `
306 cmd: "echo foo > $(out)",
307 `,
308 err: "must have at least one output file",
309 },
310 }
311
312 for _, test := range testcases {
313 t.Run(test.name, func(t *testing.T) {
314 config := android.TestArchConfig(buildDir, nil)
315 bp := "genrule {\n"
316 bp += "name: \"gen\",\n"
317 bp += test.prop
318 bp += "}\n"
319
320 ctx := testContext(config, bp, nil)
321
322 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
323 if errs == nil {
324 _, errs = ctx.PrepareBuildActions(config)
325 }
326 if errs == nil && test.err != "" {
327 t.Fatalf("want error %q, got no error", test.err)
328 } else if errs != nil && test.err == "" {
329 android.FailIfErrored(t, errs)
330 } else if test.err != "" {
331 if len(errs) != 1 {
332 t.Errorf("want 1 error, got %d errors:", len(errs))
333 for _, err := range errs {
334 t.Errorf(" %s", err.Error())
335 }
336 t.FailNow()
337 }
338 if !strings.Contains(errs[0].Error(), test.err) {
339 t.Fatalf("want %q, got %q", test.err, errs[0].Error())
340 }
341 return
342 }
343
344 gen := ctx.ModuleForTests("gen", "").Module().(*Module)
345 if gen.rawCommand != "'"+test.expect+"'" {
346 t.Errorf("want %q, got %q", test.expect, gen.rawCommand)
347 }
348 })
349 }
350
351}
352
353type testTool struct {
354 android.ModuleBase
355 outputFile android.Path
356}
357
358func toolFactory() android.Module {
359 module := &testTool{}
360 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
361 return module
362}
363
364func (t *testTool) DepsMutator(ctx android.BottomUpMutatorContext) {}
365
366func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
367 t.outputFile = android.PathForTesting("out", ctx.ModuleName())
368}
369
370func (t *testTool) HostToolPath() android.OptionalPath {
371 return android.OptionalPathForPath(t.outputFile)
372}
373
374var _ HostToolProvider = (*testTool)(nil)