blob: df618c4ec3dc92e203b8a4f5ab38f8a917fdfa94 [file] [log] [blame]
Dan Willemsen9b587492017-07-10 22:13:00 -07001// Copyright 2017 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 build
16
17import (
18 "bytes"
19 "context"
Patrice Arruda13848222019-04-22 17:12:02 -070020 "fmt"
21 "io/ioutil"
22 "os"
23 "path/filepath"
Dan Willemsen9b587492017-07-10 22:13:00 -070024 "reflect"
25 "strings"
26 "testing"
27
28 "android/soong/ui/logger"
29)
30
31func testContext() Context {
32 return Context{&ContextImpl{
Dan Willemsenb82471a2018-05-17 16:37:09 -070033 Context: context.Background(),
34 Logger: logger.New(&bytes.Buffer{}),
Colin Cross097ed2a2019-06-08 21:48:58 -070035 Writer: &bytes.Buffer{},
Dan Willemsen9b587492017-07-10 22:13:00 -070036 }}
37}
38
39func TestConfigParseArgsJK(t *testing.T) {
40 ctx := testContext()
41
42 testCases := []struct {
43 args []string
44
45 parallel int
46 keepGoing int
47 remaining []string
48 }{
49 {nil, -1, -1, nil},
50
51 {[]string{"-j"}, -1, -1, nil},
52 {[]string{"-j1"}, 1, -1, nil},
53 {[]string{"-j1234"}, 1234, -1, nil},
54
55 {[]string{"-j", "1"}, 1, -1, nil},
56 {[]string{"-j", "1234"}, 1234, -1, nil},
57 {[]string{"-j", "1234", "abc"}, 1234, -1, []string{"abc"}},
58 {[]string{"-j", "abc"}, -1, -1, []string{"abc"}},
59 {[]string{"-j", "1abc"}, -1, -1, []string{"1abc"}},
60
61 {[]string{"-k"}, -1, 0, nil},
62 {[]string{"-k0"}, -1, 0, nil},
63 {[]string{"-k1"}, -1, 1, nil},
64 {[]string{"-k1234"}, -1, 1234, nil},
65
66 {[]string{"-k", "0"}, -1, 0, nil},
67 {[]string{"-k", "1"}, -1, 1, nil},
68 {[]string{"-k", "1234"}, -1, 1234, nil},
69 {[]string{"-k", "1234", "abc"}, -1, 1234, []string{"abc"}},
70 {[]string{"-k", "abc"}, -1, 0, []string{"abc"}},
71 {[]string{"-k", "1abc"}, -1, 0, []string{"1abc"}},
72
73 // TODO: These are supported in Make, should we support them?
74 //{[]string{"-kj"}, -1, 0},
75 //{[]string{"-kj8"}, 8, 0},
76
77 // -jk is not valid in Make
78 }
79
80 for _, tc := range testCases {
81 t.Run(strings.Join(tc.args, " "), func(t *testing.T) {
82 defer logger.Recover(func(err error) {
83 t.Fatal(err)
84 })
85
86 c := &configImpl{
87 parallel: -1,
88 keepGoing: -1,
89 }
90 c.parseArgs(ctx, tc.args)
91
92 if c.parallel != tc.parallel {
93 t.Errorf("for %q, parallel:\nwant: %d\n got: %d\n",
94 strings.Join(tc.args, " "),
95 tc.parallel, c.parallel)
96 }
97 if c.keepGoing != tc.keepGoing {
98 t.Errorf("for %q, keep going:\nwant: %d\n got: %d\n",
99 strings.Join(tc.args, " "),
100 tc.keepGoing, c.keepGoing)
101 }
102 if !reflect.DeepEqual(c.arguments, tc.remaining) {
103 t.Errorf("for %q, remaining arguments:\nwant: %q\n got: %q\n",
104 strings.Join(tc.args, " "),
105 tc.remaining, c.arguments)
106 }
107 })
108 }
109}
Dan Willemsen091525e2017-07-11 14:17:50 -0700110
111func TestConfigParseArgsVars(t *testing.T) {
112 ctx := testContext()
113
114 testCases := []struct {
115 env []string
116 args []string
117
118 expectedEnv []string
119 remaining []string
120 }{
121 {},
122 {
123 env: []string{"A=bc"},
124
125 expectedEnv: []string{"A=bc"},
126 },
127 {
128 args: []string{"abc"},
129
130 remaining: []string{"abc"},
131 },
132
133 {
134 args: []string{"A=bc"},
135
136 expectedEnv: []string{"A=bc"},
137 },
138 {
139 env: []string{"A=a"},
140 args: []string{"A=bc"},
141
142 expectedEnv: []string{"A=bc"},
143 },
144
145 {
146 env: []string{"A=a"},
147 args: []string{"A=", "=b"},
148
149 expectedEnv: []string{"A="},
150 remaining: []string{"=b"},
151 },
152 }
153
154 for _, tc := range testCases {
155 t.Run(strings.Join(tc.args, " "), func(t *testing.T) {
156 defer logger.Recover(func(err error) {
157 t.Fatal(err)
158 })
159
160 e := Environment(tc.env)
161 c := &configImpl{
162 environ: &e,
163 }
164 c.parseArgs(ctx, tc.args)
165
166 if !reflect.DeepEqual([]string(*c.environ), tc.expectedEnv) {
167 t.Errorf("for env=%q args=%q, environment:\nwant: %q\n got: %q\n",
168 tc.env, tc.args,
169 tc.expectedEnv, []string(*c.environ))
170 }
171 if !reflect.DeepEqual(c.arguments, tc.remaining) {
172 t.Errorf("for env=%q args=%q, remaining arguments:\nwant: %q\n got: %q\n",
173 tc.env, tc.args,
174 tc.remaining, c.arguments)
175 }
176 })
177 }
178}
Patrice Arruda13848222019-04-22 17:12:02 -0700179
180func TestConfigCheckTopDir(t *testing.T) {
181 ctx := testContext()
182 buildRootDir := filepath.Dir(srcDirFileCheck)
183 expectedErrStr := fmt.Sprintf("Current working directory must be the source tree. %q not found.", srcDirFileCheck)
184
185 tests := []struct {
186 // ********* Setup *********
187 // Test description.
188 description string
189
190 // ********* Action *********
191 // If set to true, the build root file is created.
192 rootBuildFile bool
193
194 // The current path where Soong is being executed.
195 path string
196
197 // ********* Validation *********
198 // Expecting error and validate the error string against expectedErrStr.
199 wantErr bool
200 }{{
201 description: "current directory is the root source tree",
202 rootBuildFile: true,
203 path: ".",
204 wantErr: false,
205 }, {
206 description: "one level deep in the source tree",
207 rootBuildFile: true,
208 path: "1",
209 wantErr: true,
210 }, {
211 description: "very deep in the source tree",
212 rootBuildFile: true,
213 path: "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/7",
214 wantErr: true,
215 }, {
216 description: "outside of source tree",
217 rootBuildFile: false,
218 path: "1/2/3/4/5",
219 wantErr: true,
220 }}
221
222 for _, tt := range tests {
223 t.Run(tt.description, func(t *testing.T) {
224 defer logger.Recover(func(err error) {
225 if !tt.wantErr {
226 t.Fatalf("Got unexpected error: %v", err)
227 }
228 if expectedErrStr != err.Error() {
229 t.Fatalf("expected %s, got %s", expectedErrStr, err.Error())
230 }
231 })
232
233 // Create the root source tree.
234 rootDir, err := ioutil.TempDir("", "")
235 if err != nil {
236 t.Fatal(err)
237 }
238 defer os.RemoveAll(rootDir)
239
240 // Create the build root file. This is to test if topDir returns an error if the build root
241 // file does not exist.
242 if tt.rootBuildFile {
243 dir := filepath.Join(rootDir, buildRootDir)
244 if err := os.MkdirAll(dir, 0755); err != nil {
245 t.Errorf("failed to create %s directory: %v", dir, err)
246 }
247 f := filepath.Join(rootDir, srcDirFileCheck)
248 if err := ioutil.WriteFile(f, []byte{}, 0644); err != nil {
249 t.Errorf("failed to create file %s: %v", f, err)
250 }
251 }
252
253 // Next block of code is to set the current directory.
254 dir := rootDir
255 if tt.path != "" {
256 dir = filepath.Join(dir, tt.path)
257 if err := os.MkdirAll(dir, 0755); err != nil {
258 t.Errorf("failed to create %s directory: %v", dir, err)
259 }
260 }
261 curDir, err := os.Getwd()
262 if err != nil {
263 t.Fatalf("failed to get the current directory: %v", err)
264 }
265 defer func() { os.Chdir(curDir) }()
266
267 if err := os.Chdir(dir); err != nil {
268 t.Fatalf("failed to change directory to %s: %v", dir, err)
269 }
270
271 checkTopDir(ctx)
272 })
273 }
274}
275
276func TestConfigConvertToTarget(t *testing.T) {
277 tests := []struct {
278 // ********* Setup *********
279 // Test description.
280 description string
281
282 // ********* Action *********
283 // The current directory where Soong is being executed.
284 dir string
285
286 // The current prefix string to be pre-appended to the target.
287 prefix string
288
289 // ********* Validation *********
290 // The expected target to be invoked in ninja.
291 expectedTarget string
292 }{{
293 description: "one level directory in source tree",
294 dir: "test1",
295 prefix: "MODULES-IN-",
296 expectedTarget: "MODULES-IN-test1",
297 }, {
298 description: "multiple level directories in source tree",
299 dir: "test1/test2/test3/test4",
300 prefix: "GET-INSTALL-PATH-IN-",
301 expectedTarget: "GET-INSTALL-PATH-IN-test1-test2-test3-test4",
302 }}
303 for _, tt := range tests {
304 t.Run(tt.description, func(t *testing.T) {
305 target := convertToTarget(tt.dir, tt.prefix)
306 if target != tt.expectedTarget {
307 t.Errorf("expected %s, got %s for target", tt.expectedTarget, target)
308 }
309 })
310 }
311}
312
313func setTop(t *testing.T, dir string) func() {
314 curDir, err := os.Getwd()
315 if err != nil {
316 t.Fatalf("failed to get current directory: %v", err)
317 }
318 if err := os.Chdir(dir); err != nil {
319 t.Fatalf("failed to change directory to top dir %s: %v", dir, err)
320 }
321 return func() { os.Chdir(curDir) }
322}
323
324func createBuildFiles(t *testing.T, topDir string, buildFiles []string) {
325 for _, buildFile := range buildFiles {
326 buildFile = filepath.Join(topDir, buildFile)
327 if err := ioutil.WriteFile(buildFile, []byte{}, 0644); err != nil {
328 t.Errorf("failed to create file %s: %v", buildFile, err)
329 }
330 }
331}
332
333func createDirectories(t *testing.T, topDir string, dirs []string) {
334 for _, dir := range dirs {
335 dir = filepath.Join(topDir, dir)
336 if err := os.MkdirAll(dir, 0755); err != nil {
337 t.Errorf("failed to create %s directory: %v", dir, err)
338 }
339 }
340}
341
342func TestConfigGetTargets(t *testing.T) {
343 ctx := testContext()
344 tests := []struct {
345 // ********* Setup *********
346 // Test description.
347 description string
348
349 // Directories that exist in the source tree.
350 dirsInTrees []string
351
352 // Build files that exists in the source tree.
353 buildFiles []string
354
355 // ********* Action *********
356 // Directories passed in to soong_ui.
357 dirs []string
358
359 // Current directory that the user executed the build action command.
360 curDir string
361
362 // ********* Validation *********
363 // Expected targets from the function.
364 expectedTargets []string
365
Patrice Arruda13848222019-04-22 17:12:02 -0700366 // Expecting error from running test case.
367 errStr string
368 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700369 description: "one target dir specified",
370 dirsInTrees: []string{"0/1/2/3"},
371 buildFiles: []string{"0/1/2/3/Android.bp"},
372 dirs: []string{"1/2/3"},
373 curDir: "0",
374 expectedTargets: []string{"MODULES-IN-0-1-2-3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700375 }, {
376 description: "one target dir specified, build file does not exist",
377 dirsInTrees: []string{"0/1/2/3"},
378 buildFiles: []string{},
379 dirs: []string{"1/2/3"},
380 curDir: "0",
381 errStr: "Build file not found for 0/1/2/3 directory",
382 }, {
383 description: "one target dir specified, invalid targets specified",
384 dirsInTrees: []string{"0/1/2/3"},
385 buildFiles: []string{},
386 dirs: []string{"1/2/3:t1:t2"},
387 curDir: "0",
388 errStr: "1/2/3:t1:t2 not in proper directory:target1,target2,... format (\":\" was specified more than once)",
389 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700390 description: "one target dir specified, no targets specified but has colon",
391 dirsInTrees: []string{"0/1/2/3"},
392 buildFiles: []string{"0/1/2/3/Android.bp"},
393 dirs: []string{"1/2/3:"},
394 curDir: "0",
395 expectedTargets: []string{"MODULES-IN-0-1-2-3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700396 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700397 description: "one target dir specified, two targets specified",
398 dirsInTrees: []string{"0/1/2/3"},
399 buildFiles: []string{"0/1/2/3/Android.bp"},
400 dirs: []string{"1/2/3:t1,t2"},
401 curDir: "0",
402 expectedTargets: []string{"t1", "t2"},
Patrice Arruda13848222019-04-22 17:12:02 -0700403 }, {
404 description: "one target dir specified, no targets and has a comma",
405 dirsInTrees: []string{"0/1/2/3"},
406 buildFiles: []string{"0/1/2/3/Android.bp"},
407 dirs: []string{"1/2/3:,"},
408 curDir: "0",
409 errStr: "0/1/2/3 not in proper directory:target1,target2,... format",
410 }, {
411 description: "one target dir specified, improper targets defined",
412 dirsInTrees: []string{"0/1/2/3"},
413 buildFiles: []string{"0/1/2/3/Android.bp"},
414 dirs: []string{"1/2/3:,t1"},
415 curDir: "0",
416 errStr: "0/1/2/3 not in proper directory:target1,target2,... format",
417 }, {
418 description: "one target dir specified, blank target",
419 dirsInTrees: []string{"0/1/2/3"},
420 buildFiles: []string{"0/1/2/3/Android.bp"},
421 dirs: []string{"1/2/3:t1,"},
422 curDir: "0",
423 errStr: "0/1/2/3 not in proper directory:target1,target2,... format",
424 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700425 description: "one target dir specified, many targets specified",
426 dirsInTrees: []string{"0/1/2/3"},
427 buildFiles: []string{"0/1/2/3/Android.bp"},
428 dirs: []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"},
429 curDir: "0",
430 expectedTargets: []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"},
Patrice Arruda13848222019-04-22 17:12:02 -0700431 }, {
432 description: "one target dir specified, one target specified, build file does not exist",
433 dirsInTrees: []string{"0/1/2/3"},
434 buildFiles: []string{},
435 dirs: []string{"1/2/3:t1"},
436 curDir: "0",
Patrice Arruda9450d0b2019-07-08 11:06:46 -0700437 errStr: "Couldn't locate a build file from 0/1/2/3 directory",
Patrice Arruda13848222019-04-22 17:12:02 -0700438 }, {
439 description: "one target dir specified, one target specified, build file not in target dir",
440 dirsInTrees: []string{"0/1/2/3"},
441 buildFiles: []string{"0/1/2/Android.mk"},
442 dirs: []string{"1/2/3:t1"},
443 curDir: "0",
444 errStr: "Couldn't locate a build file from 0/1/2/3 directory",
445 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700446 description: "one target dir specified, build file not in target dir",
447 dirsInTrees: []string{"0/1/2/3"},
448 buildFiles: []string{"0/1/2/Android.mk"},
449 dirs: []string{"1/2/3"},
450 curDir: "0",
451 expectedTargets: []string{"MODULES-IN-0-1-2"},
Patrice Arruda13848222019-04-22 17:12:02 -0700452 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700453 description: "multiple targets dir specified, targets specified",
454 dirsInTrees: []string{"0/1/2/3", "0/3/4"},
455 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
456 dirs: []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"},
457 curDir: "0",
458 expectedTargets: []string{"t1", "t2", "t3", "t4", "t5"},
Patrice Arruda13848222019-04-22 17:12:02 -0700459 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700460 description: "multiple targets dir specified, one directory has targets specified",
461 dirsInTrees: []string{"0/1/2/3", "0/3/4"},
462 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
463 dirs: []string{"1/2/3:t1,t2", "3/4"},
464 curDir: "0",
465 expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
Patrice Arruda13848222019-04-22 17:12:02 -0700466 }, {
467 description: "two dirs specified, only one dir exist",
468 dirsInTrees: []string{"0/1/2/3"},
469 buildFiles: []string{"0/1/2/3/Android.mk"},
470 dirs: []string{"1/2/3:t1", "3/4"},
471 curDir: "0",
472 errStr: "couldn't find directory 0/3/4",
473 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700474 description: "multiple targets dirs specified at root source tree",
475 dirsInTrees: []string{"0/1/2/3", "0/3/4"},
476 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
477 dirs: []string{"0/1/2/3:t1,t2", "0/3/4"},
478 curDir: ".",
479 expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
Patrice Arruda13848222019-04-22 17:12:02 -0700480 }, {
481 description: "no directories specified",
482 dirsInTrees: []string{"0/1/2/3", "0/3/4"},
483 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
484 dirs: []string{},
485 curDir: ".",
486 }}
487 for _, tt := range tests {
488 t.Run(tt.description, func(t *testing.T) {
489 defer logger.Recover(func(err error) {
490 if tt.errStr == "" {
491 t.Fatalf("Got unexpected error: %v", err)
492 }
493 if tt.errStr != err.Error() {
494 t.Errorf("expected %s, got %s", tt.errStr, err.Error())
495 }
496 })
497
498 // Create the root source tree.
499 topDir, err := ioutil.TempDir("", "")
500 if err != nil {
501 t.Fatalf("failed to create temp dir: %v", err)
502 }
503 defer os.RemoveAll(topDir)
504
505 createDirectories(t, topDir, tt.dirsInTrees)
506 createBuildFiles(t, topDir, tt.buildFiles)
507 r := setTop(t, topDir)
508 defer r()
509
Dan Willemsence41e942019-07-29 23:39:30 -0700510 targets := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-")
Patrice Arruda13848222019-04-22 17:12:02 -0700511 if !reflect.DeepEqual(targets, tt.expectedTargets) {
512 t.Errorf("expected %v, got %v for targets", tt.expectedTargets, targets)
513 }
Patrice Arruda13848222019-04-22 17:12:02 -0700514
515 // If the execution reached here and there was an expected error code, the unit test case failed.
516 if tt.errStr != "" {
517 t.Errorf("expecting error %s", tt.errStr)
518 }
519 })
520 }
521}
522
523func TestConfigFindBuildFile(t *testing.T) {
524 ctx := testContext()
525
526 tests := []struct {
527 // ********* Setup *********
528 // Test description.
529 description string
530
531 // Array of build files to create in dir.
532 buildFiles []string
533
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700534 // Directories that exist in the source tree.
535 dirsInTrees []string
536
Patrice Arruda13848222019-04-22 17:12:02 -0700537 // ********* Action *********
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700538 // The base directory is where findBuildFile is invoked.
Patrice Arruda13848222019-04-22 17:12:02 -0700539 dir string
540
541 // ********* Validation *********
542 // Expected build file path to find.
543 expectedBuildFile string
544 }{{
545 description: "build file exists at leaf directory",
546 buildFiles: []string{"1/2/3/Android.bp"},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700547 dirsInTrees: []string{"1/2/3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700548 dir: "1/2/3",
549 expectedBuildFile: "1/2/3/Android.mk",
550 }, {
551 description: "build file exists in all directory paths",
552 buildFiles: []string{"1/Android.mk", "1/2/Android.mk", "1/2/3/Android.mk"},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700553 dirsInTrees: []string{"1/2/3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700554 dir: "1/2/3",
555 expectedBuildFile: "1/2/3/Android.mk",
556 }, {
557 description: "build file does not exist in all directory paths",
558 buildFiles: []string{},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700559 dirsInTrees: []string{"1/2/3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700560 dir: "1/2/3",
561 expectedBuildFile: "",
562 }, {
563 description: "build file exists only at top directory",
564 buildFiles: []string{"Android.bp"},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700565 dirsInTrees: []string{"1/2/3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700566 dir: "1/2/3",
567 expectedBuildFile: "",
568 }, {
569 description: "build file exist in a subdirectory",
570 buildFiles: []string{"1/2/Android.bp"},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700571 dirsInTrees: []string{"1/2/3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700572 dir: "1/2/3",
573 expectedBuildFile: "1/2/Android.mk",
574 }, {
575 description: "build file exists in a subdirectory",
576 buildFiles: []string{"1/Android.mk"},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700577 dirsInTrees: []string{"1/2/3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700578 dir: "1/2/3",
579 expectedBuildFile: "1/Android.mk",
580 }, {
581 description: "top directory",
582 buildFiles: []string{"Android.bp"},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700583 dirsInTrees: []string{},
Patrice Arruda13848222019-04-22 17:12:02 -0700584 dir: ".",
585 expectedBuildFile: "",
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700586 }, {
587 description: "build file exists in subdirectory",
588 buildFiles: []string{"1/2/3/Android.bp", "1/2/4/Android.bp"},
589 dirsInTrees: []string{"1/2/3", "1/2/4"},
590 dir: "1/2",
591 expectedBuildFile: "1/2/Android.mk",
592 }, {
593 description: "build file exists in parent subdirectory",
594 buildFiles: []string{"1/5/Android.bp"},
595 dirsInTrees: []string{"1/2/3", "1/2/4", "1/5"},
596 dir: "1/2",
597 expectedBuildFile: "1/Android.mk",
598 }, {
599 description: "build file exists in deep parent's subdirectory.",
600 buildFiles: []string{"1/5/6/Android.bp"},
601 dirsInTrees: []string{"1/2/3", "1/2/4", "1/5/6", "1/5/7"},
602 dir: "1/2",
603 expectedBuildFile: "1/Android.mk",
Patrice Arruda13848222019-04-22 17:12:02 -0700604 }}
605
606 for _, tt := range tests {
607 t.Run(tt.description, func(t *testing.T) {
608 defer logger.Recover(func(err error) {
609 t.Fatalf("Got unexpected error: %v", err)
610 })
611
612 topDir, err := ioutil.TempDir("", "")
613 if err != nil {
614 t.Fatalf("failed to create temp dir: %v", err)
615 }
616 defer os.RemoveAll(topDir)
617
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700618 createDirectories(t, topDir, tt.dirsInTrees)
Patrice Arruda13848222019-04-22 17:12:02 -0700619 createBuildFiles(t, topDir, tt.buildFiles)
620
621 curDir, err := os.Getwd()
622 if err != nil {
623 t.Fatalf("Could not get working directory: %v", err)
624 }
625 defer func() { os.Chdir(curDir) }()
626 if err := os.Chdir(topDir); err != nil {
627 t.Fatalf("Could not change top dir to %s: %v", topDir, err)
628 }
629
630 buildFile := findBuildFile(ctx, tt.dir)
631 if buildFile != tt.expectedBuildFile {
632 t.Errorf("expected %q, got %q for build file", tt.expectedBuildFile, buildFile)
633 }
634 })
635 }
636}
637
638func TestConfigSplitArgs(t *testing.T) {
639 tests := []struct {
640 // ********* Setup *********
641 // Test description.
642 description string
643
644 // ********* Action *********
645 // Arguments passed in to soong_ui.
646 args []string
647
648 // ********* Validation *********
649 // Expected newArgs list after extracting the directories.
650 expectedNewArgs []string
651
652 // Expected directories
653 expectedDirs []string
654 }{{
655 description: "flags but no directories specified",
656 args: []string{"showcommands", "-j", "-k"},
657 expectedNewArgs: []string{"showcommands", "-j", "-k"},
658 expectedDirs: []string{},
659 }, {
660 description: "flags and one directory specified",
661 args: []string{"snod", "-j", "dir:target1,target2"},
662 expectedNewArgs: []string{"snod", "-j"},
663 expectedDirs: []string{"dir:target1,target2"},
664 }, {
665 description: "flags and directories specified",
666 args: []string{"dist", "-k", "dir1", "dir2:target1,target2"},
667 expectedNewArgs: []string{"dist", "-k"},
668 expectedDirs: []string{"dir1", "dir2:target1,target2"},
669 }, {
670 description: "only directories specified",
671 args: []string{"dir1", "dir2", "dir3:target1,target2"},
672 expectedNewArgs: []string{},
673 expectedDirs: []string{"dir1", "dir2", "dir3:target1,target2"},
674 }}
675 for _, tt := range tests {
676 t.Run(tt.description, func(t *testing.T) {
677 args, dirs := splitArgs(tt.args)
678 if !reflect.DeepEqual(tt.expectedNewArgs, args) {
679 t.Errorf("expected %v, got %v for arguments", tt.expectedNewArgs, args)
680 }
681 if !reflect.DeepEqual(tt.expectedDirs, dirs) {
682 t.Errorf("expected %v, got %v for directories", tt.expectedDirs, dirs)
683 }
684 })
685 }
686}
687
688type envVar struct {
689 name string
690 value string
691}
692
693type buildActionTestCase struct {
694 // ********* Setup *********
695 // Test description.
696 description string
697
698 // Directories that exist in the source tree.
699 dirsInTrees []string
700
701 // Build files that exists in the source tree.
702 buildFiles []string
703
Patrice Arrudababa9a92019-07-03 10:47:34 -0700704 // Create root symlink that points to topDir.
705 rootSymlink bool
706
Patrice Arruda13848222019-04-22 17:12:02 -0700707 // ********* Action *********
708 // Arguments passed in to soong_ui.
709 args []string
710
711 // Directory where the build action was invoked.
712 curDir string
713
714 // WITH_TIDY_ONLY environment variable specified.
715 tidyOnly string
716
717 // ********* Validation *********
718 // Expected arguments to be in Config instance.
719 expectedArgs []string
720
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700721 // Expecting error from running test case.
722 expectedErrStr string
Patrice Arruda13848222019-04-22 17:12:02 -0700723}
724
Dan Willemsence41e942019-07-29 23:39:30 -0700725func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction) {
Patrice Arruda13848222019-04-22 17:12:02 -0700726 ctx := testContext()
727
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700728 defer logger.Recover(func(err error) {
729 if tt.expectedErrStr == "" {
730 t.Fatalf("Got unexpected error: %v", err)
731 }
732 if tt.expectedErrStr != err.Error() {
733 t.Errorf("expected %s, got %s", tt.expectedErrStr, err.Error())
734 }
735 })
736
Patrice Arruda13848222019-04-22 17:12:02 -0700737 // Environment variables to set it to blank on every test case run.
738 resetEnvVars := []string{
Patrice Arruda13848222019-04-22 17:12:02 -0700739 "WITH_TIDY_ONLY",
740 }
741
742 for _, name := range resetEnvVars {
743 if err := os.Unsetenv(name); err != nil {
744 t.Fatalf("failed to unset environment variable %s: %v", name, err)
745 }
746 }
747 if tt.tidyOnly != "" {
748 if err := os.Setenv("WITH_TIDY_ONLY", tt.tidyOnly); err != nil {
749 t.Errorf("failed to set WITH_TIDY_ONLY to %s: %v", tt.tidyOnly, err)
750 }
751 }
752
753 // Create the root source tree.
754 topDir, err := ioutil.TempDir("", "")
755 if err != nil {
756 t.Fatalf("failed to create temp dir: %v", err)
757 }
758 defer os.RemoveAll(topDir)
759
760 createDirectories(t, topDir, tt.dirsInTrees)
761 createBuildFiles(t, topDir, tt.buildFiles)
762
Patrice Arrudababa9a92019-07-03 10:47:34 -0700763 if tt.rootSymlink {
764 // Create a secondary root source tree which points to the true root source tree.
765 symlinkTopDir, err := ioutil.TempDir("", "")
766 if err != nil {
767 t.Fatalf("failed to create symlink temp dir: %v", err)
768 }
769 defer os.RemoveAll(symlinkTopDir)
770
771 symlinkTopDir = filepath.Join(symlinkTopDir, "root")
772 err = os.Symlink(topDir, symlinkTopDir)
773 if err != nil {
774 t.Fatalf("failed to create symlink: %v", err)
775 }
776 topDir = symlinkTopDir
777 }
778
Patrice Arruda13848222019-04-22 17:12:02 -0700779 r := setTop(t, topDir)
780 defer r()
781
782 // The next block is to create the root build file.
783 rootBuildFileDir := filepath.Dir(srcDirFileCheck)
784 if err := os.MkdirAll(rootBuildFileDir, 0755); err != nil {
785 t.Fatalf("Failed to create %s directory: %v", rootBuildFileDir, err)
786 }
787
788 if err := ioutil.WriteFile(srcDirFileCheck, []byte{}, 0644); err != nil {
789 t.Fatalf("failed to create %s file: %v", srcDirFileCheck, err)
790 }
791
Dan Willemsence41e942019-07-29 23:39:30 -0700792 args := getConfigArgs(action, tt.curDir, ctx, tt.args)
Patrice Arruda13848222019-04-22 17:12:02 -0700793 if !reflect.DeepEqual(tt.expectedArgs, args) {
794 t.Fatalf("expected %v, got %v for config arguments", tt.expectedArgs, args)
795 }
796
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700797 // If the execution reached here and there was an expected error code, the unit test case failed.
798 if tt.expectedErrStr != "" {
799 t.Errorf("expecting error %s", tt.expectedErrStr)
800 }
Patrice Arruda13848222019-04-22 17:12:02 -0700801}
802
Patrice Arruda39282062019-06-20 16:35:12 -0700803func TestGetConfigArgsBuildModules(t *testing.T) {
804 tests := []buildActionTestCase{{
Dan Willemsence41e942019-07-29 23:39:30 -0700805 description: "normal execution from the root source tree directory",
806 dirsInTrees: []string{"0/1/2", "0/2", "0/3"},
807 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "0/3/Android.mk"},
808 args: []string{"-j", "fake_module", "fake_module2"},
809 curDir: ".",
810 tidyOnly: "",
811 expectedArgs: []string{"-j", "fake_module", "fake_module2"},
Patrice Arruda39282062019-06-20 16:35:12 -0700812 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700813 description: "normal execution in deep directory",
814 dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
815 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"},
816 args: []string{"-j", "fake_module", "fake_module2", "-k"},
817 curDir: "1/2/3/4/5/6/7/8/9",
818 tidyOnly: "",
819 expectedArgs: []string{"-j", "fake_module", "fake_module2", "-k"},
Patrice Arruda39282062019-06-20 16:35:12 -0700820 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700821 description: "normal execution in deep directory, no targets",
822 dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
823 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"},
824 args: []string{"-j", "-k"},
825 curDir: "1/2/3/4/5/6/7/8/9",
826 tidyOnly: "",
827 expectedArgs: []string{"-j", "-k"},
Patrice Arruda39282062019-06-20 16:35:12 -0700828 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700829 description: "normal execution in root source tree, no args",
830 dirsInTrees: []string{"0/1/2", "0/2", "0/3"},
831 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"},
832 args: []string{},
833 curDir: "0/2",
834 tidyOnly: "",
835 expectedArgs: []string{},
Patrice Arrudababa9a92019-07-03 10:47:34 -0700836 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700837 description: "normal execution in symlink root source tree, no args",
838 dirsInTrees: []string{"0/1/2", "0/2", "0/3"},
839 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"},
840 rootSymlink: true,
841 args: []string{},
842 curDir: "0/2",
843 tidyOnly: "",
844 expectedArgs: []string{},
Patrice Arruda39282062019-06-20 16:35:12 -0700845 }}
846 for _, tt := range tests {
847 t.Run("build action BUILD_MODULES with dependencies, "+tt.description, func(t *testing.T) {
Dan Willemsence41e942019-07-29 23:39:30 -0700848 testGetConfigArgs(t, tt, BUILD_MODULES)
Patrice Arruda13848222019-04-22 17:12:02 -0700849 })
850 }
851}
852
853func TestGetConfigArgsBuildModulesInDirectory(t *testing.T) {
854 tests := []buildActionTestCase{{
Dan Willemsence41e942019-07-29 23:39:30 -0700855 description: "normal execution in a directory",
856 dirsInTrees: []string{"0/1/2"},
857 buildFiles: []string{"0/1/2/Android.mk"},
858 args: []string{"fake-module"},
859 curDir: "0/1/2",
860 tidyOnly: "",
861 expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"},
Patrice Arruda13848222019-04-22 17:12:02 -0700862 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700863 description: "build file in parent directory",
864 dirsInTrees: []string{"0/1/2"},
865 buildFiles: []string{"0/1/Android.mk"},
866 args: []string{},
867 curDir: "0/1/2",
868 tidyOnly: "",
869 expectedArgs: []string{"MODULES-IN-0-1"},
Patrice Arruda13848222019-04-22 17:12:02 -0700870 },
871 {
Dan Willemsence41e942019-07-29 23:39:30 -0700872 description: "build file in parent directory, multiple module names passed in",
873 dirsInTrees: []string{"0/1/2"},
874 buildFiles: []string{"0/1/Android.mk"},
875 args: []string{"fake-module1", "fake-module2", "fake-module3"},
876 curDir: "0/1/2",
877 tidyOnly: "",
878 expectedArgs: []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"},
Patrice Arruda13848222019-04-22 17:12:02 -0700879 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700880 description: "build file in 2nd level parent directory",
881 dirsInTrees: []string{"0/1/2"},
882 buildFiles: []string{"0/Android.bp"},
883 args: []string{},
884 curDir: "0/1/2",
885 tidyOnly: "",
886 expectedArgs: []string{"MODULES-IN-0"},
Patrice Arruda13848222019-04-22 17:12:02 -0700887 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700888 description: "build action executed at root directory",
889 dirsInTrees: []string{},
890 buildFiles: []string{},
891 rootSymlink: false,
892 args: []string{},
893 curDir: ".",
894 tidyOnly: "",
895 expectedArgs: []string{},
Patrice Arrudababa9a92019-07-03 10:47:34 -0700896 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700897 description: "build action executed at root directory in symlink",
898 dirsInTrees: []string{},
899 buildFiles: []string{},
900 rootSymlink: true,
901 args: []string{},
902 curDir: ".",
903 tidyOnly: "",
904 expectedArgs: []string{},
Patrice Arruda13848222019-04-22 17:12:02 -0700905 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700906 description: "build file not found",
907 dirsInTrees: []string{"0/1/2"},
908 buildFiles: []string{},
909 args: []string{},
910 curDir: "0/1/2",
911 tidyOnly: "",
912 expectedArgs: []string{"MODULES-IN-0-1-2"},
913 expectedErrStr: "Build file not found for 0/1/2 directory",
Patrice Arruda13848222019-04-22 17:12:02 -0700914 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700915 description: "GET-INSTALL-PATH specified,",
916 dirsInTrees: []string{"0/1/2"},
917 buildFiles: []string{"0/1/Android.mk"},
918 args: []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"},
919 curDir: "0/1/2",
920 tidyOnly: "",
921 expectedArgs: []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"},
Patrice Arruda13848222019-04-22 17:12:02 -0700922 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700923 description: "tidy only environment variable specified,",
924 dirsInTrees: []string{"0/1/2"},
925 buildFiles: []string{"0/1/Android.mk"},
926 args: []string{"GET-INSTALL-PATH"},
927 curDir: "0/1/2",
928 tidyOnly: "true",
929 expectedArgs: []string{"tidy_only"},
Patrice Arruda13848222019-04-22 17:12:02 -0700930 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700931 description: "normal execution in root directory with args",
932 dirsInTrees: []string{},
933 buildFiles: []string{},
934 args: []string{"-j", "-k", "fake_module"},
935 curDir: "",
936 tidyOnly: "",
937 expectedArgs: []string{"-j", "-k", "fake_module"},
Patrice Arruda13848222019-04-22 17:12:02 -0700938 }}
939 for _, tt := range tests {
940 t.Run("build action BUILD_MODULES_IN_DIR, "+tt.description, func(t *testing.T) {
Dan Willemsence41e942019-07-29 23:39:30 -0700941 testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY)
Patrice Arruda13848222019-04-22 17:12:02 -0700942 })
943 }
944}
945
946func TestGetConfigArgsBuildModulesInDirectories(t *testing.T) {
947 tests := []buildActionTestCase{{
948 description: "normal execution in a directory",
949 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
950 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
951 args: []string{"3.1/", "3.2/", "3.3/"},
952 curDir: "0/1/2",
953 tidyOnly: "",
954 expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-2-3.3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700955 }, {
956 description: "GET-INSTALL-PATH specified",
957 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3"},
958 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/Android.bp"},
959 args: []string{"GET-INSTALL-PATH", "2/3.1/", "2/3.2", "3"},
960 curDir: "0/1",
961 tidyOnly: "",
962 expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1-2-3.1", "GET-INSTALL-PATH-IN-0-1-2-3.2", "GET-INSTALL-PATH-IN-0-1"},
Patrice Arruda13848222019-04-22 17:12:02 -0700963 }, {
964 description: "tidy only environment variable specified",
965 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
966 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
967 args: []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3"},
968 curDir: "0/1/2",
969 tidyOnly: "1",
970 expectedArgs: []string{"tidy_only"},
Patrice Arruda13848222019-04-22 17:12:02 -0700971 }, {
972 description: "normal execution from top dir directory",
973 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
974 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/3/Android.bp", "0/2/Android.bp"},
Patrice Arrudababa9a92019-07-03 10:47:34 -0700975 rootSymlink: false,
976 args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
977 curDir: ".",
978 tidyOnly: "",
979 expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-3", "MODULES-IN-0-2"},
Patrice Arrudababa9a92019-07-03 10:47:34 -0700980 }, {
981 description: "normal execution from top dir directory in symlink",
982 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
983 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/3/Android.bp", "0/2/Android.bp"},
984 rootSymlink: true,
Patrice Arruda13848222019-04-22 17:12:02 -0700985 args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
986 curDir: ".",
987 tidyOnly: "",
988 expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-3", "MODULES-IN-0-2"},
Patrice Arruda13848222019-04-22 17:12:02 -0700989 }}
990 for _, tt := range tests {
991 t.Run("build action BUILD_MODULES_IN_DIRS, "+tt.description, func(t *testing.T) {
Dan Willemsence41e942019-07-29 23:39:30 -0700992 testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES)
Patrice Arruda13848222019-04-22 17:12:02 -0700993 })
994 }
995}