blob: dede4d136ddd81c4920098b3440049ce7535b47d [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"
Dan Willemsenb82471a2018-05-17 16:37:09 -070029 "android/soong/ui/terminal"
Dan Willemsen9b587492017-07-10 22:13:00 -070030)
31
32func testContext() Context {
33 return Context{&ContextImpl{
Dan Willemsenb82471a2018-05-17 16:37:09 -070034 Context: context.Background(),
35 Logger: logger.New(&bytes.Buffer{}),
36 Writer: terminal.NewWriter(terminal.NewCustomStdio(&bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{})),
Dan Willemsen9b587492017-07-10 22:13:00 -070037 }}
38}
39
40func TestConfigParseArgsJK(t *testing.T) {
41 ctx := testContext()
42
43 testCases := []struct {
44 args []string
45
46 parallel int
47 keepGoing int
48 remaining []string
49 }{
50 {nil, -1, -1, nil},
51
52 {[]string{"-j"}, -1, -1, nil},
53 {[]string{"-j1"}, 1, -1, nil},
54 {[]string{"-j1234"}, 1234, -1, nil},
55
56 {[]string{"-j", "1"}, 1, -1, nil},
57 {[]string{"-j", "1234"}, 1234, -1, nil},
58 {[]string{"-j", "1234", "abc"}, 1234, -1, []string{"abc"}},
59 {[]string{"-j", "abc"}, -1, -1, []string{"abc"}},
60 {[]string{"-j", "1abc"}, -1, -1, []string{"1abc"}},
61
62 {[]string{"-k"}, -1, 0, nil},
63 {[]string{"-k0"}, -1, 0, nil},
64 {[]string{"-k1"}, -1, 1, nil},
65 {[]string{"-k1234"}, -1, 1234, nil},
66
67 {[]string{"-k", "0"}, -1, 0, nil},
68 {[]string{"-k", "1"}, -1, 1, nil},
69 {[]string{"-k", "1234"}, -1, 1234, nil},
70 {[]string{"-k", "1234", "abc"}, -1, 1234, []string{"abc"}},
71 {[]string{"-k", "abc"}, -1, 0, []string{"abc"}},
72 {[]string{"-k", "1abc"}, -1, 0, []string{"1abc"}},
73
74 // TODO: These are supported in Make, should we support them?
75 //{[]string{"-kj"}, -1, 0},
76 //{[]string{"-kj8"}, 8, 0},
77
78 // -jk is not valid in Make
79 }
80
81 for _, tc := range testCases {
82 t.Run(strings.Join(tc.args, " "), func(t *testing.T) {
83 defer logger.Recover(func(err error) {
84 t.Fatal(err)
85 })
86
87 c := &configImpl{
88 parallel: -1,
89 keepGoing: -1,
90 }
91 c.parseArgs(ctx, tc.args)
92
93 if c.parallel != tc.parallel {
94 t.Errorf("for %q, parallel:\nwant: %d\n got: %d\n",
95 strings.Join(tc.args, " "),
96 tc.parallel, c.parallel)
97 }
98 if c.keepGoing != tc.keepGoing {
99 t.Errorf("for %q, keep going:\nwant: %d\n got: %d\n",
100 strings.Join(tc.args, " "),
101 tc.keepGoing, c.keepGoing)
102 }
103 if !reflect.DeepEqual(c.arguments, tc.remaining) {
104 t.Errorf("for %q, remaining arguments:\nwant: %q\n got: %q\n",
105 strings.Join(tc.args, " "),
106 tc.remaining, c.arguments)
107 }
108 })
109 }
110}
Dan Willemsen091525e2017-07-11 14:17:50 -0700111
112func TestConfigParseArgsVars(t *testing.T) {
113 ctx := testContext()
114
115 testCases := []struct {
116 env []string
117 args []string
118
119 expectedEnv []string
120 remaining []string
121 }{
122 {},
123 {
124 env: []string{"A=bc"},
125
126 expectedEnv: []string{"A=bc"},
127 },
128 {
129 args: []string{"abc"},
130
131 remaining: []string{"abc"},
132 },
133
134 {
135 args: []string{"A=bc"},
136
137 expectedEnv: []string{"A=bc"},
138 },
139 {
140 env: []string{"A=a"},
141 args: []string{"A=bc"},
142
143 expectedEnv: []string{"A=bc"},
144 },
145
146 {
147 env: []string{"A=a"},
148 args: []string{"A=", "=b"},
149
150 expectedEnv: []string{"A="},
151 remaining: []string{"=b"},
152 },
153 }
154
155 for _, tc := range testCases {
156 t.Run(strings.Join(tc.args, " "), func(t *testing.T) {
157 defer logger.Recover(func(err error) {
158 t.Fatal(err)
159 })
160
161 e := Environment(tc.env)
162 c := &configImpl{
163 environ: &e,
164 }
165 c.parseArgs(ctx, tc.args)
166
167 if !reflect.DeepEqual([]string(*c.environ), tc.expectedEnv) {
168 t.Errorf("for env=%q args=%q, environment:\nwant: %q\n got: %q\n",
169 tc.env, tc.args,
170 tc.expectedEnv, []string(*c.environ))
171 }
172 if !reflect.DeepEqual(c.arguments, tc.remaining) {
173 t.Errorf("for env=%q args=%q, remaining arguments:\nwant: %q\n got: %q\n",
174 tc.env, tc.args,
175 tc.remaining, c.arguments)
176 }
177 })
178 }
179}
Patrice Arruda13848222019-04-22 17:12:02 -0700180
181func TestConfigCheckTopDir(t *testing.T) {
182 ctx := testContext()
183 buildRootDir := filepath.Dir(srcDirFileCheck)
184 expectedErrStr := fmt.Sprintf("Current working directory must be the source tree. %q not found.", srcDirFileCheck)
185
186 tests := []struct {
187 // ********* Setup *********
188 // Test description.
189 description string
190
191 // ********* Action *********
192 // If set to true, the build root file is created.
193 rootBuildFile bool
194
195 // The current path where Soong is being executed.
196 path string
197
198 // ********* Validation *********
199 // Expecting error and validate the error string against expectedErrStr.
200 wantErr bool
201 }{{
202 description: "current directory is the root source tree",
203 rootBuildFile: true,
204 path: ".",
205 wantErr: false,
206 }, {
207 description: "one level deep in the source tree",
208 rootBuildFile: true,
209 path: "1",
210 wantErr: true,
211 }, {
212 description: "very deep in the source tree",
213 rootBuildFile: true,
214 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",
215 wantErr: true,
216 }, {
217 description: "outside of source tree",
218 rootBuildFile: false,
219 path: "1/2/3/4/5",
220 wantErr: true,
221 }}
222
223 for _, tt := range tests {
224 t.Run(tt.description, func(t *testing.T) {
225 defer logger.Recover(func(err error) {
226 if !tt.wantErr {
227 t.Fatalf("Got unexpected error: %v", err)
228 }
229 if expectedErrStr != err.Error() {
230 t.Fatalf("expected %s, got %s", expectedErrStr, err.Error())
231 }
232 })
233
234 // Create the root source tree.
235 rootDir, err := ioutil.TempDir("", "")
236 if err != nil {
237 t.Fatal(err)
238 }
239 defer os.RemoveAll(rootDir)
240
241 // Create the build root file. This is to test if topDir returns an error if the build root
242 // file does not exist.
243 if tt.rootBuildFile {
244 dir := filepath.Join(rootDir, buildRootDir)
245 if err := os.MkdirAll(dir, 0755); err != nil {
246 t.Errorf("failed to create %s directory: %v", dir, err)
247 }
248 f := filepath.Join(rootDir, srcDirFileCheck)
249 if err := ioutil.WriteFile(f, []byte{}, 0644); err != nil {
250 t.Errorf("failed to create file %s: %v", f, err)
251 }
252 }
253
254 // Next block of code is to set the current directory.
255 dir := rootDir
256 if tt.path != "" {
257 dir = filepath.Join(dir, tt.path)
258 if err := os.MkdirAll(dir, 0755); err != nil {
259 t.Errorf("failed to create %s directory: %v", dir, err)
260 }
261 }
262 curDir, err := os.Getwd()
263 if err != nil {
264 t.Fatalf("failed to get the current directory: %v", err)
265 }
266 defer func() { os.Chdir(curDir) }()
267
268 if err := os.Chdir(dir); err != nil {
269 t.Fatalf("failed to change directory to %s: %v", dir, err)
270 }
271
272 checkTopDir(ctx)
273 })
274 }
275}
276
277func TestConfigConvertToTarget(t *testing.T) {
278 tests := []struct {
279 // ********* Setup *********
280 // Test description.
281 description string
282
283 // ********* Action *********
284 // The current directory where Soong is being executed.
285 dir string
286
287 // The current prefix string to be pre-appended to the target.
288 prefix string
289
290 // ********* Validation *********
291 // The expected target to be invoked in ninja.
292 expectedTarget string
293 }{{
294 description: "one level directory in source tree",
295 dir: "test1",
296 prefix: "MODULES-IN-",
297 expectedTarget: "MODULES-IN-test1",
298 }, {
299 description: "multiple level directories in source tree",
300 dir: "test1/test2/test3/test4",
301 prefix: "GET-INSTALL-PATH-IN-",
302 expectedTarget: "GET-INSTALL-PATH-IN-test1-test2-test3-test4",
303 }}
304 for _, tt := range tests {
305 t.Run(tt.description, func(t *testing.T) {
306 target := convertToTarget(tt.dir, tt.prefix)
307 if target != tt.expectedTarget {
308 t.Errorf("expected %s, got %s for target", tt.expectedTarget, target)
309 }
310 })
311 }
312}
313
314func setTop(t *testing.T, dir string) func() {
315 curDir, err := os.Getwd()
316 if err != nil {
317 t.Fatalf("failed to get current directory: %v", err)
318 }
319 if err := os.Chdir(dir); err != nil {
320 t.Fatalf("failed to change directory to top dir %s: %v", dir, err)
321 }
322 return func() { os.Chdir(curDir) }
323}
324
325func createBuildFiles(t *testing.T, topDir string, buildFiles []string) {
326 for _, buildFile := range buildFiles {
327 buildFile = filepath.Join(topDir, buildFile)
328 if err := ioutil.WriteFile(buildFile, []byte{}, 0644); err != nil {
329 t.Errorf("failed to create file %s: %v", buildFile, err)
330 }
331 }
332}
333
334func createDirectories(t *testing.T, topDir string, dirs []string) {
335 for _, dir := range dirs {
336 dir = filepath.Join(topDir, dir)
337 if err := os.MkdirAll(dir, 0755); err != nil {
338 t.Errorf("failed to create %s directory: %v", dir, err)
339 }
340 }
341}
342
343func TestConfigGetTargets(t *testing.T) {
344 ctx := testContext()
345 tests := []struct {
346 // ********* Setup *********
347 // Test description.
348 description string
349
350 // Directories that exist in the source tree.
351 dirsInTrees []string
352
353 // Build files that exists in the source tree.
354 buildFiles []string
355
356 // ********* Action *********
357 // Directories passed in to soong_ui.
358 dirs []string
359
360 // Current directory that the user executed the build action command.
361 curDir string
362
363 // ********* Validation *********
364 // Expected targets from the function.
365 expectedTargets []string
366
367 // Expected build from the build system.
368 expectedBuildFiles []string
369
370 // Expecting error from running test case.
371 errStr string
372 }{{
373 description: "one target dir specified",
374 dirsInTrees: []string{"0/1/2/3"},
375 buildFiles: []string{"0/1/2/3/Android.bp"},
376 dirs: []string{"1/2/3"},
377 curDir: "0",
378 expectedTargets: []string{"MODULES-IN-0-1-2-3"},
379 expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
380 }, {
381 description: "one target dir specified, build file does not exist",
382 dirsInTrees: []string{"0/1/2/3"},
383 buildFiles: []string{},
384 dirs: []string{"1/2/3"},
385 curDir: "0",
386 errStr: "Build file not found for 0/1/2/3 directory",
387 }, {
388 description: "one target dir specified, invalid targets specified",
389 dirsInTrees: []string{"0/1/2/3"},
390 buildFiles: []string{},
391 dirs: []string{"1/2/3:t1:t2"},
392 curDir: "0",
393 errStr: "1/2/3:t1:t2 not in proper directory:target1,target2,... format (\":\" was specified more than once)",
394 }, {
395 description: "one target dir specified, no targets specified but has colon",
396 dirsInTrees: []string{"0/1/2/3"},
397 buildFiles: []string{"0/1/2/3/Android.bp"},
398 dirs: []string{"1/2/3:"},
399 curDir: "0",
400 expectedTargets: []string{"MODULES-IN-0-1-2-3"},
401 expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
402 }, {
403 description: "one target dir specified, two targets specified",
404 dirsInTrees: []string{"0/1/2/3"},
405 buildFiles: []string{"0/1/2/3/Android.bp"},
406 dirs: []string{"1/2/3:t1,t2"},
407 curDir: "0",
408 expectedTargets: []string{"t1", "t2"},
409 expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
410 }, {
411 description: "one target dir specified, no targets and has a comma",
412 dirsInTrees: []string{"0/1/2/3"},
413 buildFiles: []string{"0/1/2/3/Android.bp"},
414 dirs: []string{"1/2/3:,"},
415 curDir: "0",
416 errStr: "0/1/2/3 not in proper directory:target1,target2,... format",
417 }, {
418 description: "one target dir specified, improper targets defined",
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 }, {
425 description: "one target dir specified, blank target",
426 dirsInTrees: []string{"0/1/2/3"},
427 buildFiles: []string{"0/1/2/3/Android.bp"},
428 dirs: []string{"1/2/3:t1,"},
429 curDir: "0",
430 errStr: "0/1/2/3 not in proper directory:target1,target2,... format",
431 }, {
432 description: "one target dir specified, many targets specified",
433 dirsInTrees: []string{"0/1/2/3"},
434 buildFiles: []string{"0/1/2/3/Android.bp"},
435 dirs: []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"},
436 curDir: "0",
437 expectedTargets: []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"},
438 expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
439 }, {
440 description: "one target dir specified, one target specified, build file does not exist",
441 dirsInTrees: []string{"0/1/2/3"},
442 buildFiles: []string{},
443 dirs: []string{"1/2/3:t1"},
444 curDir: "0",
445 errStr: "Build file not found for 0/1/2/3 directory",
446 }, {
447 description: "one target dir specified, one target specified, build file not in target dir",
448 dirsInTrees: []string{"0/1/2/3"},
449 buildFiles: []string{"0/1/2/Android.mk"},
450 dirs: []string{"1/2/3:t1"},
451 curDir: "0",
452 errStr: "Couldn't locate a build file from 0/1/2/3 directory",
453 }, {
454 description: "one target dir specified, build file not in target dir",
455 dirsInTrees: []string{"0/1/2/3"},
456 buildFiles: []string{"0/1/2/Android.mk"},
457 dirs: []string{"1/2/3"},
458 curDir: "0",
459 expectedTargets: []string{"MODULES-IN-0-1-2"},
460 expectedBuildFiles: []string{"0/1/2/Android.mk"},
461 }, {
462 description: "multiple targets dir specified, targets specified",
463 dirsInTrees: []string{"0/1/2/3", "0/3/4"},
464 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
465 dirs: []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"},
466 curDir: "0",
467 expectedTargets: []string{"t1", "t2", "t3", "t4", "t5"},
468 expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
469 }, {
470 description: "multiple targets dir specified, one directory has targets specified",
471 dirsInTrees: []string{"0/1/2/3", "0/3/4"},
472 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
473 dirs: []string{"1/2/3:t1,t2", "3/4"},
474 curDir: "0",
475 expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
476 expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
477 }, {
478 description: "two dirs specified, only one dir exist",
479 dirsInTrees: []string{"0/1/2/3"},
480 buildFiles: []string{"0/1/2/3/Android.mk"},
481 dirs: []string{"1/2/3:t1", "3/4"},
482 curDir: "0",
483 errStr: "couldn't find directory 0/3/4",
484 }, {
485 description: "multiple targets dirs specified at root source tree",
486 dirsInTrees: []string{"0/1/2/3", "0/3/4"},
487 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
488 dirs: []string{"0/1/2/3:t1,t2", "0/3/4"},
489 curDir: ".",
490 expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
491 expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
492 }, {
493 description: "no directories specified",
494 dirsInTrees: []string{"0/1/2/3", "0/3/4"},
495 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
496 dirs: []string{},
497 curDir: ".",
498 }}
499 for _, tt := range tests {
500 t.Run(tt.description, func(t *testing.T) {
501 defer logger.Recover(func(err error) {
502 if tt.errStr == "" {
503 t.Fatalf("Got unexpected error: %v", err)
504 }
505 if tt.errStr != err.Error() {
506 t.Errorf("expected %s, got %s", tt.errStr, err.Error())
507 }
508 })
509
510 // Create the root source tree.
511 topDir, err := ioutil.TempDir("", "")
512 if err != nil {
513 t.Fatalf("failed to create temp dir: %v", err)
514 }
515 defer os.RemoveAll(topDir)
516
517 createDirectories(t, topDir, tt.dirsInTrees)
518 createBuildFiles(t, topDir, tt.buildFiles)
519 r := setTop(t, topDir)
520 defer r()
521
522 targets, buildFiles := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-")
523 if !reflect.DeepEqual(targets, tt.expectedTargets) {
524 t.Errorf("expected %v, got %v for targets", tt.expectedTargets, targets)
525 }
526 if !reflect.DeepEqual(buildFiles, tt.expectedBuildFiles) {
527 t.Errorf("expected %v, got %v for build files", tt.expectedBuildFiles, buildFiles)
528 }
529
530 // If the execution reached here and there was an expected error code, the unit test case failed.
531 if tt.errStr != "" {
532 t.Errorf("expecting error %s", tt.errStr)
533 }
534 })
535 }
536}
537
538func TestConfigFindBuildFile(t *testing.T) {
539 ctx := testContext()
540
541 tests := []struct {
542 // ********* Setup *********
543 // Test description.
544 description string
545
546 // Array of build files to create in dir.
547 buildFiles []string
548
549 // ********* Action *********
550 // Directory to create, also the base directory is where findBuildFile is invoked.
551 dir string
552
553 // ********* Validation *********
554 // Expected build file path to find.
555 expectedBuildFile string
556 }{{
557 description: "build file exists at leaf directory",
558 buildFiles: []string{"1/2/3/Android.bp"},
559 dir: "1/2/3",
560 expectedBuildFile: "1/2/3/Android.mk",
561 }, {
562 description: "build file exists in all directory paths",
563 buildFiles: []string{"1/Android.mk", "1/2/Android.mk", "1/2/3/Android.mk"},
564 dir: "1/2/3",
565 expectedBuildFile: "1/2/3/Android.mk",
566 }, {
567 description: "build file does not exist in all directory paths",
568 buildFiles: []string{},
569 dir: "1/2/3",
570 expectedBuildFile: "",
571 }, {
572 description: "build file exists only at top directory",
573 buildFiles: []string{"Android.bp"},
574 dir: "1/2/3",
575 expectedBuildFile: "",
576 }, {
577 description: "build file exist in a subdirectory",
578 buildFiles: []string{"1/2/Android.bp"},
579 dir: "1/2/3",
580 expectedBuildFile: "1/2/Android.mk",
581 }, {
582 description: "build file exists in a subdirectory",
583 buildFiles: []string{"1/Android.mk"},
584 dir: "1/2/3",
585 expectedBuildFile: "1/Android.mk",
586 }, {
587 description: "top directory",
588 buildFiles: []string{"Android.bp"},
589 dir: ".",
590 expectedBuildFile: "",
591 }}
592
593 for _, tt := range tests {
594 t.Run(tt.description, func(t *testing.T) {
595 defer logger.Recover(func(err error) {
596 t.Fatalf("Got unexpected error: %v", err)
597 })
598
599 topDir, err := ioutil.TempDir("", "")
600 if err != nil {
601 t.Fatalf("failed to create temp dir: %v", err)
602 }
603 defer os.RemoveAll(topDir)
604
605 if tt.dir != "" {
606 createDirectories(t, topDir, []string{tt.dir})
607 }
608
609 createBuildFiles(t, topDir, tt.buildFiles)
610
611 curDir, err := os.Getwd()
612 if err != nil {
613 t.Fatalf("Could not get working directory: %v", err)
614 }
615 defer func() { os.Chdir(curDir) }()
616 if err := os.Chdir(topDir); err != nil {
617 t.Fatalf("Could not change top dir to %s: %v", topDir, err)
618 }
619
620 buildFile := findBuildFile(ctx, tt.dir)
621 if buildFile != tt.expectedBuildFile {
622 t.Errorf("expected %q, got %q for build file", tt.expectedBuildFile, buildFile)
623 }
624 })
625 }
626}
627
628func TestConfigSplitArgs(t *testing.T) {
629 tests := []struct {
630 // ********* Setup *********
631 // Test description.
632 description string
633
634 // ********* Action *********
635 // Arguments passed in to soong_ui.
636 args []string
637
638 // ********* Validation *********
639 // Expected newArgs list after extracting the directories.
640 expectedNewArgs []string
641
642 // Expected directories
643 expectedDirs []string
644 }{{
645 description: "flags but no directories specified",
646 args: []string{"showcommands", "-j", "-k"},
647 expectedNewArgs: []string{"showcommands", "-j", "-k"},
648 expectedDirs: []string{},
649 }, {
650 description: "flags and one directory specified",
651 args: []string{"snod", "-j", "dir:target1,target2"},
652 expectedNewArgs: []string{"snod", "-j"},
653 expectedDirs: []string{"dir:target1,target2"},
654 }, {
655 description: "flags and directories specified",
656 args: []string{"dist", "-k", "dir1", "dir2:target1,target2"},
657 expectedNewArgs: []string{"dist", "-k"},
658 expectedDirs: []string{"dir1", "dir2:target1,target2"},
659 }, {
660 description: "only directories specified",
661 args: []string{"dir1", "dir2", "dir3:target1,target2"},
662 expectedNewArgs: []string{},
663 expectedDirs: []string{"dir1", "dir2", "dir3:target1,target2"},
664 }}
665 for _, tt := range tests {
666 t.Run(tt.description, func(t *testing.T) {
667 args, dirs := splitArgs(tt.args)
668 if !reflect.DeepEqual(tt.expectedNewArgs, args) {
669 t.Errorf("expected %v, got %v for arguments", tt.expectedNewArgs, args)
670 }
671 if !reflect.DeepEqual(tt.expectedDirs, dirs) {
672 t.Errorf("expected %v, got %v for directories", tt.expectedDirs, dirs)
673 }
674 })
675 }
676}
677
678type envVar struct {
679 name string
680 value string
681}
682
683type buildActionTestCase struct {
684 // ********* Setup *********
685 // Test description.
686 description string
687
688 // Directories that exist in the source tree.
689 dirsInTrees []string
690
691 // Build files that exists in the source tree.
692 buildFiles []string
693
694 // ********* Action *********
695 // Arguments passed in to soong_ui.
696 args []string
697
698 // Directory where the build action was invoked.
699 curDir string
700
701 // WITH_TIDY_ONLY environment variable specified.
702 tidyOnly string
703
704 // ********* Validation *********
705 // Expected arguments to be in Config instance.
706 expectedArgs []string
707
708 // Expected environment variables to be set.
709 expectedEnvVars []envVar
710}
711
712func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction, buildDependencies bool) {
713 ctx := testContext()
714
715 // Environment variables to set it to blank on every test case run.
716 resetEnvVars := []string{
717 "ONE_SHOT_MAKEFILE",
718 "WITH_TIDY_ONLY",
719 }
720
721 for _, name := range resetEnvVars {
722 if err := os.Unsetenv(name); err != nil {
723 t.Fatalf("failed to unset environment variable %s: %v", name, err)
724 }
725 }
726 if tt.tidyOnly != "" {
727 if err := os.Setenv("WITH_TIDY_ONLY", tt.tidyOnly); err != nil {
728 t.Errorf("failed to set WITH_TIDY_ONLY to %s: %v", tt.tidyOnly, err)
729 }
730 }
731
732 // Create the root source tree.
733 topDir, err := ioutil.TempDir("", "")
734 if err != nil {
735 t.Fatalf("failed to create temp dir: %v", err)
736 }
737 defer os.RemoveAll(topDir)
738
739 createDirectories(t, topDir, tt.dirsInTrees)
740 createBuildFiles(t, topDir, tt.buildFiles)
741
742 r := setTop(t, topDir)
743 defer r()
744
745 // The next block is to create the root build file.
746 rootBuildFileDir := filepath.Dir(srcDirFileCheck)
747 if err := os.MkdirAll(rootBuildFileDir, 0755); err != nil {
748 t.Fatalf("Failed to create %s directory: %v", rootBuildFileDir, err)
749 }
750
751 if err := ioutil.WriteFile(srcDirFileCheck, []byte{}, 0644); err != nil {
752 t.Fatalf("failed to create %s file: %v", srcDirFileCheck, err)
753 }
754
755 args := getConfigArgs(action, tt.curDir, buildDependencies, ctx, tt.args)
756 if !reflect.DeepEqual(tt.expectedArgs, args) {
757 t.Fatalf("expected %v, got %v for config arguments", tt.expectedArgs, args)
758 }
759
760 for _, env := range tt.expectedEnvVars {
761 if val := os.Getenv(env.name); val != env.value {
762 t.Errorf("expecting %s, got %s for environment variable %s", env.value, val, env.name)
763 }
764 }
765}
766
767// TODO: Remove this test case once mm shell build command has been deprecated.
768func TestGetConfigArgsBuildModulesInDirecotoryNoDeps(t *testing.T) {
769 tests := []buildActionTestCase{{
770 description: "normal execution in a directory",
771 dirsInTrees: []string{"0/1/2"},
772 buildFiles: []string{"0/1/2/Android.mk"},
773 args: []string{"-j", "-k", "showcommands", "fake-module"},
774 curDir: "0/1/2",
775 tidyOnly: "",
776 expectedArgs: []string{"-j", "-k", "showcommands", "fake-module", "MODULES-IN-0-1-2"},
777 expectedEnvVars: []envVar{
778 envVar{
779 name: "ONE_SHOT_MAKEFILE",
780 value: "0/1/2/Android.mk"}},
781 }, {
782 description: "makefile in parent directory",
783 dirsInTrees: []string{"0/1/2"},
784 buildFiles: []string{"0/1/Android.mk"},
785 args: []string{},
786 curDir: "0/1/2",
787 tidyOnly: "",
788 expectedArgs: []string{"MODULES-IN-0-1"},
789 expectedEnvVars: []envVar{
790 envVar{
791 name: "ONE_SHOT_MAKEFILE",
792 value: "0/1/Android.mk"}},
793 }, {
794 description: "build file not found",
795 dirsInTrees: []string{"0/1/2"},
796 buildFiles: []string{},
797 args: []string{},
798 curDir: "0/1/2",
799 tidyOnly: "",
800 expectedArgs: []string{"MODULES-IN-0-1-2"},
801 expectedEnvVars: []envVar{
802 envVar{
803 name: "ONE_SHOT_MAKEFILE",
804 value: "0/1/2/Android.mk"}},
805 }, {
806 description: "build action executed at root directory",
807 dirsInTrees: []string{},
808 buildFiles: []string{},
809 args: []string{},
810 curDir: ".",
811 tidyOnly: "",
812 expectedArgs: []string{},
813 expectedEnvVars: []envVar{
814 envVar{
815 name: "ONE_SHOT_MAKEFILE",
816 value: ""}},
817 }, {
818 description: "GET-INSTALL-PATH specified,",
819 dirsInTrees: []string{"0/1/2"},
820 buildFiles: []string{"0/1/Android.mk"},
821 args: []string{"GET-INSTALL-PATH"},
822 curDir: "0/1/2",
823 tidyOnly: "",
824 expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1"},
825 expectedEnvVars: []envVar{
826 envVar{
827 name: "ONE_SHOT_MAKEFILE",
828 value: "0/1/Android.mk"}},
829 }, {
830 description: "tidy only environment variable specified,",
831 dirsInTrees: []string{"0/1/2"},
832 buildFiles: []string{"0/1/Android.mk"},
833 args: []string{"GET-INSTALL-PATH"},
834 curDir: "0/1/2",
835 tidyOnly: "true",
836 expectedArgs: []string{"tidy_only"},
837 expectedEnvVars: []envVar{
838 envVar{
839 name: "ONE_SHOT_MAKEFILE",
840 value: "0/1/Android.mk"}},
841 }}
842 for _, tt := range tests {
843 t.Run("build action BUILD_MODULES_IN_DIR without their dependencies, "+tt.description, func(t *testing.T) {
844 testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY, false)
845 })
846 }
847}
848
849func TestGetConfigArgsBuildModulesInDirectory(t *testing.T) {
850 tests := []buildActionTestCase{{
851 description: "normal execution in a directory",
852 dirsInTrees: []string{"0/1/2"},
853 buildFiles: []string{"0/1/2/Android.mk"},
854 args: []string{"fake-module"},
855 curDir: "0/1/2",
856 tidyOnly: "",
857 expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"},
858 expectedEnvVars: []envVar{},
859 }, {
860 description: "build file in parent directory",
861 dirsInTrees: []string{"0/1/2"},
862 buildFiles: []string{"0/1/Android.mk"},
863 args: []string{},
864 curDir: "0/1/2",
865 tidyOnly: "",
866 expectedArgs: []string{"MODULES-IN-0-1"},
867 expectedEnvVars: []envVar{},
868 },
869 {
870 description: "build file in parent directory, multiple module names passed in",
871 dirsInTrees: []string{"0/1/2"},
872 buildFiles: []string{"0/1/Android.mk"},
873 args: []string{"fake-module1", "fake-module2", "fake-module3"},
874 curDir: "0/1/2",
875 tidyOnly: "",
876 expectedArgs: []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"},
877 expectedEnvVars: []envVar{},
878 }, {
879 description: "build file in 2nd level parent directory",
880 dirsInTrees: []string{"0/1/2"},
881 buildFiles: []string{"0/Android.bp"},
882 args: []string{},
883 curDir: "0/1/2",
884 tidyOnly: "",
885 expectedArgs: []string{"MODULES-IN-0"},
886 expectedEnvVars: []envVar{},
887 }, {
888 description: "build action executed at root directory",
889 dirsInTrees: []string{},
890 buildFiles: []string{},
891 args: []string{},
892 curDir: ".",
893 tidyOnly: "",
894 expectedArgs: []string{},
895 expectedEnvVars: []envVar{},
896 }, {
897 description: "build file not found - no error is expected to return",
898 dirsInTrees: []string{"0/1/2"},
899 buildFiles: []string{},
900 args: []string{},
901 curDir: "0/1/2",
902 tidyOnly: "",
903 expectedArgs: []string{"MODULES-IN-0-1-2"},
904 expectedEnvVars: []envVar{},
905 }, {
906 description: "GET-INSTALL-PATH specified,",
907 dirsInTrees: []string{"0/1/2"},
908 buildFiles: []string{"0/1/Android.mk"},
909 args: []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"},
910 curDir: "0/1/2",
911 tidyOnly: "",
912 expectedArgs: []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"},
913 expectedEnvVars: []envVar{},
914 }, {
915 description: "tidy only environment variable specified,",
916 dirsInTrees: []string{"0/1/2"},
917 buildFiles: []string{"0/1/Android.mk"},
918 args: []string{"GET-INSTALL-PATH"},
919 curDir: "0/1/2",
920 tidyOnly: "true",
921 expectedArgs: []string{"tidy_only"},
922 expectedEnvVars: []envVar{},
923 }, {
924 description: "normal execution in root directory with args",
925 dirsInTrees: []string{},
926 buildFiles: []string{},
927 args: []string{"-j", "-k", "fake_module"},
928 curDir: "",
929 tidyOnly: "",
930 expectedArgs: []string{"-j", "-k", "fake_module"},
931 expectedEnvVars: []envVar{},
932 }}
933 for _, tt := range tests {
934 t.Run("build action BUILD_MODULES_IN_DIR, "+tt.description, func(t *testing.T) {
935 testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY, true)
936 })
937 }
938}
939
940// TODO: Remove this test case once mmm shell build command has been deprecated.
941func TestGetConfigArgsBuildModulesInDirectoriesNoDeps(t *testing.T) {
942 tests := []buildActionTestCase{{
943 description: "normal execution in a directory",
944 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
945 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
946 args: []string{"3.1/:t1,t2", "3.2/:t3,t4", "3.3/:t5,t6"},
947 curDir: "0/1/2",
948 tidyOnly: "",
949 expectedArgs: []string{"t1", "t2", "t3", "t4", "t5", "t6"},
950 expectedEnvVars: []envVar{
951 envVar{
952 name: "ONE_SHOT_MAKEFILE",
953 value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
954 }, {
955 description: "GET-INSTALL-PATH specified",
956 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
957 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
958 args: []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3/:t6"},
959 curDir: "0/1/2",
960 tidyOnly: "",
961 expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1-2-3.1", "GET-INSTALL-PATH-IN-0-1-2-3.2", "t6"},
962 expectedEnvVars: []envVar{
963 envVar{
964 name: "ONE_SHOT_MAKEFILE",
965 value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
966 }, {
967 description: "tidy only environment variable specified",
968 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
969 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
970 args: []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3/:t6"},
971 curDir: "0/1/2",
972 tidyOnly: "1",
973 expectedArgs: []string{"tidy_only"},
974 expectedEnvVars: []envVar{
975 envVar{
976 name: "ONE_SHOT_MAKEFILE",
977 value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
978 }, {
979 description: "normal execution from top dir directory",
980 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
981 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
982 args: []string{"0/1/2/3.1", "0/1/2/3.2/:t3,t4", "0/1/2/3.3/:t5,t6"},
983 curDir: ".",
984 tidyOnly: "",
985 expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "t3", "t4", "t5", "t6"},
986 expectedEnvVars: []envVar{
987 envVar{
988 name: "ONE_SHOT_MAKEFILE",
989 value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
990 }}
991 for _, tt := range tests {
992 t.Run("build action BUILD_MODULES_IN_DIRS_NO_DEPS, "+tt.description, func(t *testing.T) {
993 testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES, false)
994 })
995 }
996}
997
998func TestGetConfigArgsBuildModulesInDirectories(t *testing.T) {
999 tests := []buildActionTestCase{{
1000 description: "normal execution in a directory",
1001 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
1002 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
1003 args: []string{"3.1/", "3.2/", "3.3/"},
1004 curDir: "0/1/2",
1005 tidyOnly: "",
1006 expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-2-3.3"},
1007 expectedEnvVars: []envVar{
1008 envVar{
1009 name: "ONE_SHOT_MAKEFILE",
1010 value: ""}},
1011 }, {
1012 description: "GET-INSTALL-PATH specified",
1013 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3"},
1014 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/Android.bp"},
1015 args: []string{"GET-INSTALL-PATH", "2/3.1/", "2/3.2", "3"},
1016 curDir: "0/1",
1017 tidyOnly: "",
1018 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"},
1019 expectedEnvVars: []envVar{
1020 envVar{
1021 name: "ONE_SHOT_MAKEFILE",
1022 value: ""}},
1023 }, {
1024 description: "tidy only environment variable specified",
1025 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
1026 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
1027 args: []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3"},
1028 curDir: "0/1/2",
1029 tidyOnly: "1",
1030 expectedArgs: []string{"tidy_only"},
1031 expectedEnvVars: []envVar{
1032 envVar{
1033 name: "ONE_SHOT_MAKEFILE",
1034 value: ""}},
1035 }, {
1036 description: "normal execution from top dir directory",
1037 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
1038 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"},
1039 args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
1040 curDir: ".",
1041 tidyOnly: "",
1042 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"},
1043 expectedEnvVars: []envVar{
1044 envVar{
1045 name: "ONE_SHOT_MAKEFILE",
1046 value: ""}},
1047 }}
1048 for _, tt := range tests {
1049 t.Run("build action BUILD_MODULES_IN_DIRS, "+tt.description, func(t *testing.T) {
1050 testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES, true)
1051 })
1052 }
1053}