blob: 7b14c470381d3d09cbcfbbbb56eea5cd670b9eca [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"
Patrice Arruda219eef32020-06-01 17:29:30 +000029 "android/soong/ui/status"
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{}),
Colin Cross097ed2a2019-06-08 21:48:58 -070036 Writer: &bytes.Buffer{},
Patrice Arruda219eef32020-06-01 17:29:30 +000037 Status: &status.Status{},
Dan Willemsen9b587492017-07-10 22:13:00 -070038 }}
39}
40
41func TestConfigParseArgsJK(t *testing.T) {
42 ctx := testContext()
43
44 testCases := []struct {
45 args []string
46
47 parallel int
48 keepGoing int
49 remaining []string
50 }{
51 {nil, -1, -1, nil},
52
53 {[]string{"-j"}, -1, -1, nil},
54 {[]string{"-j1"}, 1, -1, nil},
55 {[]string{"-j1234"}, 1234, -1, nil},
56
57 {[]string{"-j", "1"}, 1, -1, nil},
58 {[]string{"-j", "1234"}, 1234, -1, nil},
59 {[]string{"-j", "1234", "abc"}, 1234, -1, []string{"abc"}},
60 {[]string{"-j", "abc"}, -1, -1, []string{"abc"}},
61 {[]string{"-j", "1abc"}, -1, -1, []string{"1abc"}},
62
63 {[]string{"-k"}, -1, 0, nil},
64 {[]string{"-k0"}, -1, 0, nil},
65 {[]string{"-k1"}, -1, 1, nil},
66 {[]string{"-k1234"}, -1, 1234, nil},
67
68 {[]string{"-k", "0"}, -1, 0, nil},
69 {[]string{"-k", "1"}, -1, 1, nil},
70 {[]string{"-k", "1234"}, -1, 1234, nil},
71 {[]string{"-k", "1234", "abc"}, -1, 1234, []string{"abc"}},
72 {[]string{"-k", "abc"}, -1, 0, []string{"abc"}},
73 {[]string{"-k", "1abc"}, -1, 0, []string{"1abc"}},
74
75 // TODO: These are supported in Make, should we support them?
76 //{[]string{"-kj"}, -1, 0},
77 //{[]string{"-kj8"}, 8, 0},
78
79 // -jk is not valid in Make
80 }
81
82 for _, tc := range testCases {
83 t.Run(strings.Join(tc.args, " "), func(t *testing.T) {
84 defer logger.Recover(func(err error) {
85 t.Fatal(err)
86 })
87
88 c := &configImpl{
89 parallel: -1,
90 keepGoing: -1,
91 }
92 c.parseArgs(ctx, tc.args)
93
94 if c.parallel != tc.parallel {
95 t.Errorf("for %q, parallel:\nwant: %d\n got: %d\n",
96 strings.Join(tc.args, " "),
97 tc.parallel, c.parallel)
98 }
99 if c.keepGoing != tc.keepGoing {
100 t.Errorf("for %q, keep going:\nwant: %d\n got: %d\n",
101 strings.Join(tc.args, " "),
102 tc.keepGoing, c.keepGoing)
103 }
104 if !reflect.DeepEqual(c.arguments, tc.remaining) {
105 t.Errorf("for %q, remaining arguments:\nwant: %q\n got: %q\n",
106 strings.Join(tc.args, " "),
107 tc.remaining, c.arguments)
108 }
109 })
110 }
111}
Dan Willemsen091525e2017-07-11 14:17:50 -0700112
113func TestConfigParseArgsVars(t *testing.T) {
114 ctx := testContext()
115
116 testCases := []struct {
117 env []string
118 args []string
119
120 expectedEnv []string
121 remaining []string
122 }{
123 {},
124 {
125 env: []string{"A=bc"},
126
127 expectedEnv: []string{"A=bc"},
128 },
129 {
130 args: []string{"abc"},
131
132 remaining: []string{"abc"},
133 },
134
135 {
136 args: []string{"A=bc"},
137
138 expectedEnv: []string{"A=bc"},
139 },
140 {
141 env: []string{"A=a"},
142 args: []string{"A=bc"},
143
144 expectedEnv: []string{"A=bc"},
145 },
146
147 {
148 env: []string{"A=a"},
149 args: []string{"A=", "=b"},
150
151 expectedEnv: []string{"A="},
152 remaining: []string{"=b"},
153 },
154 }
155
156 for _, tc := range testCases {
157 t.Run(strings.Join(tc.args, " "), func(t *testing.T) {
158 defer logger.Recover(func(err error) {
159 t.Fatal(err)
160 })
161
162 e := Environment(tc.env)
163 c := &configImpl{
164 environ: &e,
165 }
166 c.parseArgs(ctx, tc.args)
167
168 if !reflect.DeepEqual([]string(*c.environ), tc.expectedEnv) {
169 t.Errorf("for env=%q args=%q, environment:\nwant: %q\n got: %q\n",
170 tc.env, tc.args,
171 tc.expectedEnv, []string(*c.environ))
172 }
173 if !reflect.DeepEqual(c.arguments, tc.remaining) {
174 t.Errorf("for env=%q args=%q, remaining arguments:\nwant: %q\n got: %q\n",
175 tc.env, tc.args,
176 tc.remaining, c.arguments)
177 }
178 })
179 }
180}
Patrice Arruda13848222019-04-22 17:12:02 -0700181
182func TestConfigCheckTopDir(t *testing.T) {
183 ctx := testContext()
184 buildRootDir := filepath.Dir(srcDirFileCheck)
185 expectedErrStr := fmt.Sprintf("Current working directory must be the source tree. %q not found.", srcDirFileCheck)
186
187 tests := []struct {
188 // ********* Setup *********
189 // Test description.
190 description string
191
192 // ********* Action *********
193 // If set to true, the build root file is created.
194 rootBuildFile bool
195
196 // The current path where Soong is being executed.
197 path string
198
199 // ********* Validation *********
200 // Expecting error and validate the error string against expectedErrStr.
201 wantErr bool
202 }{{
203 description: "current directory is the root source tree",
204 rootBuildFile: true,
205 path: ".",
206 wantErr: false,
207 }, {
208 description: "one level deep in the source tree",
209 rootBuildFile: true,
210 path: "1",
211 wantErr: true,
212 }, {
213 description: "very deep in the source tree",
214 rootBuildFile: true,
215 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",
216 wantErr: true,
217 }, {
218 description: "outside of source tree",
219 rootBuildFile: false,
220 path: "1/2/3/4/5",
221 wantErr: true,
222 }}
223
224 for _, tt := range tests {
225 t.Run(tt.description, func(t *testing.T) {
226 defer logger.Recover(func(err error) {
227 if !tt.wantErr {
228 t.Fatalf("Got unexpected error: %v", err)
229 }
230 if expectedErrStr != err.Error() {
231 t.Fatalf("expected %s, got %s", expectedErrStr, err.Error())
232 }
233 })
234
235 // Create the root source tree.
236 rootDir, err := ioutil.TempDir("", "")
237 if err != nil {
238 t.Fatal(err)
239 }
240 defer os.RemoveAll(rootDir)
241
242 // Create the build root file. This is to test if topDir returns an error if the build root
243 // file does not exist.
244 if tt.rootBuildFile {
245 dir := filepath.Join(rootDir, buildRootDir)
246 if err := os.MkdirAll(dir, 0755); err != nil {
247 t.Errorf("failed to create %s directory: %v", dir, err)
248 }
249 f := filepath.Join(rootDir, srcDirFileCheck)
250 if err := ioutil.WriteFile(f, []byte{}, 0644); err != nil {
251 t.Errorf("failed to create file %s: %v", f, err)
252 }
253 }
254
255 // Next block of code is to set the current directory.
256 dir := rootDir
257 if tt.path != "" {
258 dir = filepath.Join(dir, tt.path)
259 if err := os.MkdirAll(dir, 0755); err != nil {
260 t.Errorf("failed to create %s directory: %v", dir, err)
261 }
262 }
263 curDir, err := os.Getwd()
264 if err != nil {
265 t.Fatalf("failed to get the current directory: %v", err)
266 }
267 defer func() { os.Chdir(curDir) }()
268
269 if err := os.Chdir(dir); err != nil {
270 t.Fatalf("failed to change directory to %s: %v", dir, err)
271 }
272
273 checkTopDir(ctx)
274 })
275 }
276}
277
278func TestConfigConvertToTarget(t *testing.T) {
279 tests := []struct {
280 // ********* Setup *********
281 // Test description.
282 description string
283
284 // ********* Action *********
285 // The current directory where Soong is being executed.
286 dir string
287
288 // The current prefix string to be pre-appended to the target.
289 prefix string
290
291 // ********* Validation *********
292 // The expected target to be invoked in ninja.
293 expectedTarget string
294 }{{
295 description: "one level directory in source tree",
296 dir: "test1",
297 prefix: "MODULES-IN-",
298 expectedTarget: "MODULES-IN-test1",
299 }, {
300 description: "multiple level directories in source tree",
301 dir: "test1/test2/test3/test4",
302 prefix: "GET-INSTALL-PATH-IN-",
303 expectedTarget: "GET-INSTALL-PATH-IN-test1-test2-test3-test4",
304 }}
305 for _, tt := range tests {
306 t.Run(tt.description, func(t *testing.T) {
307 target := convertToTarget(tt.dir, tt.prefix)
308 if target != tt.expectedTarget {
309 t.Errorf("expected %s, got %s for target", tt.expectedTarget, target)
310 }
311 })
312 }
313}
314
315func setTop(t *testing.T, dir string) func() {
316 curDir, err := os.Getwd()
317 if err != nil {
318 t.Fatalf("failed to get current directory: %v", err)
319 }
320 if err := os.Chdir(dir); err != nil {
321 t.Fatalf("failed to change directory to top dir %s: %v", dir, err)
322 }
323 return func() { os.Chdir(curDir) }
324}
325
326func createBuildFiles(t *testing.T, topDir string, buildFiles []string) {
327 for _, buildFile := range buildFiles {
328 buildFile = filepath.Join(topDir, buildFile)
329 if err := ioutil.WriteFile(buildFile, []byte{}, 0644); err != nil {
330 t.Errorf("failed to create file %s: %v", buildFile, err)
331 }
332 }
333}
334
335func createDirectories(t *testing.T, topDir string, dirs []string) {
336 for _, dir := range dirs {
337 dir = filepath.Join(topDir, dir)
338 if err := os.MkdirAll(dir, 0755); err != nil {
339 t.Errorf("failed to create %s directory: %v", dir, err)
340 }
341 }
342}
343
344func TestConfigGetTargets(t *testing.T) {
345 ctx := testContext()
346 tests := []struct {
347 // ********* Setup *********
348 // Test description.
349 description string
350
351 // Directories that exist in the source tree.
352 dirsInTrees []string
353
354 // Build files that exists in the source tree.
355 buildFiles []string
356
357 // ********* Action *********
358 // Directories passed in to soong_ui.
359 dirs []string
360
361 // Current directory that the user executed the build action command.
362 curDir string
363
364 // ********* Validation *********
365 // Expected targets from the function.
366 expectedTargets []string
367
Patrice Arruda13848222019-04-22 17:12:02 -0700368 // Expecting error from running test case.
369 errStr string
370 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700371 description: "one target dir specified",
372 dirsInTrees: []string{"0/1/2/3"},
373 buildFiles: []string{"0/1/2/3/Android.bp"},
374 dirs: []string{"1/2/3"},
375 curDir: "0",
376 expectedTargets: []string{"MODULES-IN-0-1-2-3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700377 }, {
378 description: "one target dir specified, build file does not exist",
379 dirsInTrees: []string{"0/1/2/3"},
380 buildFiles: []string{},
381 dirs: []string{"1/2/3"},
382 curDir: "0",
383 errStr: "Build file not found for 0/1/2/3 directory",
384 }, {
385 description: "one target dir specified, invalid targets specified",
386 dirsInTrees: []string{"0/1/2/3"},
387 buildFiles: []string{},
388 dirs: []string{"1/2/3:t1:t2"},
389 curDir: "0",
390 errStr: "1/2/3:t1:t2 not in proper directory:target1,target2,... format (\":\" was specified more than once)",
391 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700392 description: "one target dir specified, no targets specified but has colon",
393 dirsInTrees: []string{"0/1/2/3"},
394 buildFiles: []string{"0/1/2/3/Android.bp"},
395 dirs: []string{"1/2/3:"},
396 curDir: "0",
397 expectedTargets: []string{"MODULES-IN-0-1-2-3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700398 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700399 description: "one target dir specified, two targets specified",
400 dirsInTrees: []string{"0/1/2/3"},
401 buildFiles: []string{"0/1/2/3/Android.bp"},
402 dirs: []string{"1/2/3:t1,t2"},
403 curDir: "0",
404 expectedTargets: []string{"t1", "t2"},
Patrice Arruda13848222019-04-22 17:12:02 -0700405 }, {
406 description: "one target dir specified, no targets and has a comma",
407 dirsInTrees: []string{"0/1/2/3"},
408 buildFiles: []string{"0/1/2/3/Android.bp"},
409 dirs: []string{"1/2/3:,"},
410 curDir: "0",
411 errStr: "0/1/2/3 not in proper directory:target1,target2,... format",
412 }, {
413 description: "one target dir specified, improper targets defined",
414 dirsInTrees: []string{"0/1/2/3"},
415 buildFiles: []string{"0/1/2/3/Android.bp"},
416 dirs: []string{"1/2/3:,t1"},
417 curDir: "0",
418 errStr: "0/1/2/3 not in proper directory:target1,target2,... format",
419 }, {
420 description: "one target dir specified, blank target",
421 dirsInTrees: []string{"0/1/2/3"},
422 buildFiles: []string{"0/1/2/3/Android.bp"},
423 dirs: []string{"1/2/3:t1,"},
424 curDir: "0",
425 errStr: "0/1/2/3 not in proper directory:target1,target2,... format",
426 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700427 description: "one target dir specified, many targets specified",
428 dirsInTrees: []string{"0/1/2/3"},
429 buildFiles: []string{"0/1/2/3/Android.bp"},
430 dirs: []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"},
431 curDir: "0",
432 expectedTargets: []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"},
Patrice Arruda13848222019-04-22 17:12:02 -0700433 }, {
434 description: "one target dir specified, one target specified, build file does not exist",
435 dirsInTrees: []string{"0/1/2/3"},
436 buildFiles: []string{},
437 dirs: []string{"1/2/3:t1"},
438 curDir: "0",
Patrice Arruda9450d0b2019-07-08 11:06:46 -0700439 errStr: "Couldn't locate a build file from 0/1/2/3 directory",
Patrice Arruda13848222019-04-22 17:12:02 -0700440 }, {
441 description: "one target dir specified, one target specified, build file not in target dir",
442 dirsInTrees: []string{"0/1/2/3"},
443 buildFiles: []string{"0/1/2/Android.mk"},
444 dirs: []string{"1/2/3:t1"},
445 curDir: "0",
446 errStr: "Couldn't locate a build file from 0/1/2/3 directory",
447 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700448 description: "one target dir specified, build file not in target dir",
449 dirsInTrees: []string{"0/1/2/3"},
450 buildFiles: []string{"0/1/2/Android.mk"},
451 dirs: []string{"1/2/3"},
452 curDir: "0",
453 expectedTargets: []string{"MODULES-IN-0-1-2"},
Patrice Arruda13848222019-04-22 17:12:02 -0700454 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700455 description: "multiple targets dir specified, targets specified",
456 dirsInTrees: []string{"0/1/2/3", "0/3/4"},
457 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
458 dirs: []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"},
459 curDir: "0",
460 expectedTargets: []string{"t1", "t2", "t3", "t4", "t5"},
Patrice Arruda13848222019-04-22 17:12:02 -0700461 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700462 description: "multiple targets dir specified, one directory has 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"},
466 curDir: "0",
467 expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
Patrice Arruda13848222019-04-22 17:12:02 -0700468 }, {
469 description: "two dirs specified, only one dir exist",
470 dirsInTrees: []string{"0/1/2/3"},
471 buildFiles: []string{"0/1/2/3/Android.mk"},
472 dirs: []string{"1/2/3:t1", "3/4"},
473 curDir: "0",
474 errStr: "couldn't find directory 0/3/4",
475 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700476 description: "multiple targets dirs specified at root source tree",
477 dirsInTrees: []string{"0/1/2/3", "0/3/4"},
478 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
479 dirs: []string{"0/1/2/3:t1,t2", "0/3/4"},
480 curDir: ".",
481 expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
Patrice Arruda13848222019-04-22 17:12:02 -0700482 }, {
483 description: "no directories specified",
484 dirsInTrees: []string{"0/1/2/3", "0/3/4"},
485 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
486 dirs: []string{},
487 curDir: ".",
488 }}
489 for _, tt := range tests {
490 t.Run(tt.description, func(t *testing.T) {
491 defer logger.Recover(func(err error) {
492 if tt.errStr == "" {
493 t.Fatalf("Got unexpected error: %v", err)
494 }
495 if tt.errStr != err.Error() {
496 t.Errorf("expected %s, got %s", tt.errStr, err.Error())
497 }
498 })
499
500 // Create the root source tree.
501 topDir, err := ioutil.TempDir("", "")
502 if err != nil {
503 t.Fatalf("failed to create temp dir: %v", err)
504 }
505 defer os.RemoveAll(topDir)
506
507 createDirectories(t, topDir, tt.dirsInTrees)
508 createBuildFiles(t, topDir, tt.buildFiles)
509 r := setTop(t, topDir)
510 defer r()
511
Dan Willemsence41e942019-07-29 23:39:30 -0700512 targets := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-")
Patrice Arruda13848222019-04-22 17:12:02 -0700513 if !reflect.DeepEqual(targets, tt.expectedTargets) {
514 t.Errorf("expected %v, got %v for targets", tt.expectedTargets, targets)
515 }
Patrice Arruda13848222019-04-22 17:12:02 -0700516
517 // If the execution reached here and there was an expected error code, the unit test case failed.
518 if tt.errStr != "" {
519 t.Errorf("expecting error %s", tt.errStr)
520 }
521 })
522 }
523}
524
525func TestConfigFindBuildFile(t *testing.T) {
526 ctx := testContext()
527
528 tests := []struct {
529 // ********* Setup *********
530 // Test description.
531 description string
532
533 // Array of build files to create in dir.
534 buildFiles []string
535
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700536 // Directories that exist in the source tree.
537 dirsInTrees []string
538
Patrice Arruda13848222019-04-22 17:12:02 -0700539 // ********* Action *********
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700540 // The base directory is where findBuildFile is invoked.
Patrice Arruda13848222019-04-22 17:12:02 -0700541 dir string
542
543 // ********* Validation *********
544 // Expected build file path to find.
545 expectedBuildFile string
546 }{{
547 description: "build file exists at leaf directory",
548 buildFiles: []string{"1/2/3/Android.bp"},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700549 dirsInTrees: []string{"1/2/3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700550 dir: "1/2/3",
551 expectedBuildFile: "1/2/3/Android.mk",
552 }, {
553 description: "build file exists in all directory paths",
554 buildFiles: []string{"1/Android.mk", "1/2/Android.mk", "1/2/3/Android.mk"},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700555 dirsInTrees: []string{"1/2/3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700556 dir: "1/2/3",
557 expectedBuildFile: "1/2/3/Android.mk",
558 }, {
559 description: "build file does not exist in all directory paths",
560 buildFiles: []string{},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700561 dirsInTrees: []string{"1/2/3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700562 dir: "1/2/3",
563 expectedBuildFile: "",
564 }, {
565 description: "build file exists only at top directory",
566 buildFiles: []string{"Android.bp"},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700567 dirsInTrees: []string{"1/2/3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700568 dir: "1/2/3",
569 expectedBuildFile: "",
570 }, {
571 description: "build file exist in a subdirectory",
572 buildFiles: []string{"1/2/Android.bp"},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700573 dirsInTrees: []string{"1/2/3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700574 dir: "1/2/3",
575 expectedBuildFile: "1/2/Android.mk",
576 }, {
577 description: "build file exists in a subdirectory",
578 buildFiles: []string{"1/Android.mk"},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700579 dirsInTrees: []string{"1/2/3"},
Patrice Arruda13848222019-04-22 17:12:02 -0700580 dir: "1/2/3",
581 expectedBuildFile: "1/Android.mk",
582 }, {
583 description: "top directory",
584 buildFiles: []string{"Android.bp"},
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700585 dirsInTrees: []string{},
Patrice Arruda13848222019-04-22 17:12:02 -0700586 dir: ".",
587 expectedBuildFile: "",
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700588 }, {
589 description: "build file exists in subdirectory",
590 buildFiles: []string{"1/2/3/Android.bp", "1/2/4/Android.bp"},
591 dirsInTrees: []string{"1/2/3", "1/2/4"},
592 dir: "1/2",
593 expectedBuildFile: "1/2/Android.mk",
594 }, {
595 description: "build file exists in parent subdirectory",
596 buildFiles: []string{"1/5/Android.bp"},
597 dirsInTrees: []string{"1/2/3", "1/2/4", "1/5"},
598 dir: "1/2",
599 expectedBuildFile: "1/Android.mk",
600 }, {
601 description: "build file exists in deep parent's subdirectory.",
602 buildFiles: []string{"1/5/6/Android.bp"},
603 dirsInTrees: []string{"1/2/3", "1/2/4", "1/5/6", "1/5/7"},
604 dir: "1/2",
605 expectedBuildFile: "1/Android.mk",
Patrice Arruda13848222019-04-22 17:12:02 -0700606 }}
607
608 for _, tt := range tests {
609 t.Run(tt.description, func(t *testing.T) {
610 defer logger.Recover(func(err error) {
611 t.Fatalf("Got unexpected error: %v", err)
612 })
613
614 topDir, err := ioutil.TempDir("", "")
615 if err != nil {
616 t.Fatalf("failed to create temp dir: %v", err)
617 }
618 defer os.RemoveAll(topDir)
619
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700620 createDirectories(t, topDir, tt.dirsInTrees)
Patrice Arruda13848222019-04-22 17:12:02 -0700621 createBuildFiles(t, topDir, tt.buildFiles)
622
623 curDir, err := os.Getwd()
624 if err != nil {
625 t.Fatalf("Could not get working directory: %v", err)
626 }
627 defer func() { os.Chdir(curDir) }()
628 if err := os.Chdir(topDir); err != nil {
629 t.Fatalf("Could not change top dir to %s: %v", topDir, err)
630 }
631
632 buildFile := findBuildFile(ctx, tt.dir)
633 if buildFile != tt.expectedBuildFile {
634 t.Errorf("expected %q, got %q for build file", tt.expectedBuildFile, buildFile)
635 }
636 })
637 }
638}
639
640func TestConfigSplitArgs(t *testing.T) {
641 tests := []struct {
642 // ********* Setup *********
643 // Test description.
644 description string
645
646 // ********* Action *********
647 // Arguments passed in to soong_ui.
648 args []string
649
650 // ********* Validation *********
651 // Expected newArgs list after extracting the directories.
652 expectedNewArgs []string
653
654 // Expected directories
655 expectedDirs []string
656 }{{
657 description: "flags but no directories specified",
658 args: []string{"showcommands", "-j", "-k"},
659 expectedNewArgs: []string{"showcommands", "-j", "-k"},
660 expectedDirs: []string{},
661 }, {
662 description: "flags and one directory specified",
663 args: []string{"snod", "-j", "dir:target1,target2"},
664 expectedNewArgs: []string{"snod", "-j"},
665 expectedDirs: []string{"dir:target1,target2"},
666 }, {
667 description: "flags and directories specified",
668 args: []string{"dist", "-k", "dir1", "dir2:target1,target2"},
669 expectedNewArgs: []string{"dist", "-k"},
670 expectedDirs: []string{"dir1", "dir2:target1,target2"},
671 }, {
672 description: "only directories specified",
673 args: []string{"dir1", "dir2", "dir3:target1,target2"},
674 expectedNewArgs: []string{},
675 expectedDirs: []string{"dir1", "dir2", "dir3:target1,target2"},
676 }}
677 for _, tt := range tests {
678 t.Run(tt.description, func(t *testing.T) {
679 args, dirs := splitArgs(tt.args)
680 if !reflect.DeepEqual(tt.expectedNewArgs, args) {
681 t.Errorf("expected %v, got %v for arguments", tt.expectedNewArgs, args)
682 }
683 if !reflect.DeepEqual(tt.expectedDirs, dirs) {
684 t.Errorf("expected %v, got %v for directories", tt.expectedDirs, dirs)
685 }
686 })
687 }
688}
689
690type envVar struct {
691 name string
692 value string
693}
694
695type buildActionTestCase struct {
696 // ********* Setup *********
697 // Test description.
698 description string
699
700 // Directories that exist in the source tree.
701 dirsInTrees []string
702
703 // Build files that exists in the source tree.
704 buildFiles []string
705
Patrice Arrudababa9a92019-07-03 10:47:34 -0700706 // Create root symlink that points to topDir.
707 rootSymlink bool
708
Patrice Arruda13848222019-04-22 17:12:02 -0700709 // ********* Action *********
710 // Arguments passed in to soong_ui.
711 args []string
712
713 // Directory where the build action was invoked.
714 curDir string
715
716 // WITH_TIDY_ONLY environment variable specified.
717 tidyOnly string
718
719 // ********* Validation *********
720 // Expected arguments to be in Config instance.
721 expectedArgs []string
722
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700723 // Expecting error from running test case.
724 expectedErrStr string
Patrice Arruda13848222019-04-22 17:12:02 -0700725}
726
Dan Willemsence41e942019-07-29 23:39:30 -0700727func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction) {
Patrice Arruda13848222019-04-22 17:12:02 -0700728 ctx := testContext()
729
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700730 defer logger.Recover(func(err error) {
731 if tt.expectedErrStr == "" {
732 t.Fatalf("Got unexpected error: %v", err)
733 }
734 if tt.expectedErrStr != err.Error() {
735 t.Errorf("expected %s, got %s", tt.expectedErrStr, err.Error())
736 }
737 })
738
Patrice Arruda13848222019-04-22 17:12:02 -0700739 // Environment variables to set it to blank on every test case run.
740 resetEnvVars := []string{
Patrice Arruda13848222019-04-22 17:12:02 -0700741 "WITH_TIDY_ONLY",
742 }
743
744 for _, name := range resetEnvVars {
745 if err := os.Unsetenv(name); err != nil {
746 t.Fatalf("failed to unset environment variable %s: %v", name, err)
747 }
748 }
749 if tt.tidyOnly != "" {
750 if err := os.Setenv("WITH_TIDY_ONLY", tt.tidyOnly); err != nil {
751 t.Errorf("failed to set WITH_TIDY_ONLY to %s: %v", tt.tidyOnly, err)
752 }
753 }
754
755 // Create the root source tree.
756 topDir, err := ioutil.TempDir("", "")
757 if err != nil {
758 t.Fatalf("failed to create temp dir: %v", err)
759 }
760 defer os.RemoveAll(topDir)
761
762 createDirectories(t, topDir, tt.dirsInTrees)
763 createBuildFiles(t, topDir, tt.buildFiles)
764
Patrice Arrudababa9a92019-07-03 10:47:34 -0700765 if tt.rootSymlink {
766 // Create a secondary root source tree which points to the true root source tree.
767 symlinkTopDir, err := ioutil.TempDir("", "")
768 if err != nil {
769 t.Fatalf("failed to create symlink temp dir: %v", err)
770 }
771 defer os.RemoveAll(symlinkTopDir)
772
773 symlinkTopDir = filepath.Join(symlinkTopDir, "root")
774 err = os.Symlink(topDir, symlinkTopDir)
775 if err != nil {
776 t.Fatalf("failed to create symlink: %v", err)
777 }
778 topDir = symlinkTopDir
779 }
780
Patrice Arruda13848222019-04-22 17:12:02 -0700781 r := setTop(t, topDir)
782 defer r()
783
784 // The next block is to create the root build file.
785 rootBuildFileDir := filepath.Dir(srcDirFileCheck)
786 if err := os.MkdirAll(rootBuildFileDir, 0755); err != nil {
787 t.Fatalf("Failed to create %s directory: %v", rootBuildFileDir, err)
788 }
789
790 if err := ioutil.WriteFile(srcDirFileCheck, []byte{}, 0644); err != nil {
791 t.Fatalf("failed to create %s file: %v", srcDirFileCheck, err)
792 }
793
Dan Willemsence41e942019-07-29 23:39:30 -0700794 args := getConfigArgs(action, tt.curDir, ctx, tt.args)
Patrice Arruda13848222019-04-22 17:12:02 -0700795 if !reflect.DeepEqual(tt.expectedArgs, args) {
796 t.Fatalf("expected %v, got %v for config arguments", tt.expectedArgs, args)
797 }
798
Patrice Arruda0dcf27f2019-07-08 17:03:33 -0700799 // If the execution reached here and there was an expected error code, the unit test case failed.
800 if tt.expectedErrStr != "" {
801 t.Errorf("expecting error %s", tt.expectedErrStr)
802 }
Patrice Arruda13848222019-04-22 17:12:02 -0700803}
804
Patrice Arruda39282062019-06-20 16:35:12 -0700805func TestGetConfigArgsBuildModules(t *testing.T) {
806 tests := []buildActionTestCase{{
Dan Willemsence41e942019-07-29 23:39:30 -0700807 description: "normal execution from the root source tree directory",
808 dirsInTrees: []string{"0/1/2", "0/2", "0/3"},
809 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "0/3/Android.mk"},
810 args: []string{"-j", "fake_module", "fake_module2"},
811 curDir: ".",
812 tidyOnly: "",
813 expectedArgs: []string{"-j", "fake_module", "fake_module2"},
Patrice Arruda39282062019-06-20 16:35:12 -0700814 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700815 description: "normal execution in deep directory",
816 dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
817 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"},
818 args: []string{"-j", "fake_module", "fake_module2", "-k"},
819 curDir: "1/2/3/4/5/6/7/8/9",
820 tidyOnly: "",
821 expectedArgs: []string{"-j", "fake_module", "fake_module2", "-k"},
Patrice Arruda39282062019-06-20 16:35:12 -0700822 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700823 description: "normal execution in deep directory, no targets",
824 dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
825 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"},
826 args: []string{"-j", "-k"},
827 curDir: "1/2/3/4/5/6/7/8/9",
828 tidyOnly: "",
829 expectedArgs: []string{"-j", "-k"},
Patrice Arruda39282062019-06-20 16:35:12 -0700830 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700831 description: "normal execution in root source tree, no args",
832 dirsInTrees: []string{"0/1/2", "0/2", "0/3"},
833 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"},
834 args: []string{},
835 curDir: "0/2",
836 tidyOnly: "",
837 expectedArgs: []string{},
Patrice Arrudababa9a92019-07-03 10:47:34 -0700838 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700839 description: "normal execution in symlink root source tree, no args",
840 dirsInTrees: []string{"0/1/2", "0/2", "0/3"},
841 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"},
842 rootSymlink: true,
843 args: []string{},
844 curDir: "0/2",
845 tidyOnly: "",
846 expectedArgs: []string{},
Patrice Arruda39282062019-06-20 16:35:12 -0700847 }}
848 for _, tt := range tests {
849 t.Run("build action BUILD_MODULES with dependencies, "+tt.description, func(t *testing.T) {
Dan Willemsence41e942019-07-29 23:39:30 -0700850 testGetConfigArgs(t, tt, BUILD_MODULES)
Patrice Arruda13848222019-04-22 17:12:02 -0700851 })
852 }
853}
854
855func TestGetConfigArgsBuildModulesInDirectory(t *testing.T) {
856 tests := []buildActionTestCase{{
Dan Willemsence41e942019-07-29 23:39:30 -0700857 description: "normal execution in a directory",
858 dirsInTrees: []string{"0/1/2"},
859 buildFiles: []string{"0/1/2/Android.mk"},
860 args: []string{"fake-module"},
861 curDir: "0/1/2",
862 tidyOnly: "",
863 expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"},
Patrice Arruda13848222019-04-22 17:12:02 -0700864 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700865 description: "build file in parent directory",
866 dirsInTrees: []string{"0/1/2"},
867 buildFiles: []string{"0/1/Android.mk"},
868 args: []string{},
869 curDir: "0/1/2",
870 tidyOnly: "",
871 expectedArgs: []string{"MODULES-IN-0-1"},
Patrice Arruda13848222019-04-22 17:12:02 -0700872 },
873 {
Dan Willemsence41e942019-07-29 23:39:30 -0700874 description: "build file in parent directory, multiple module names passed in",
875 dirsInTrees: []string{"0/1/2"},
876 buildFiles: []string{"0/1/Android.mk"},
877 args: []string{"fake-module1", "fake-module2", "fake-module3"},
878 curDir: "0/1/2",
879 tidyOnly: "",
880 expectedArgs: []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"},
Patrice Arruda13848222019-04-22 17:12:02 -0700881 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700882 description: "build file in 2nd level parent directory",
883 dirsInTrees: []string{"0/1/2"},
884 buildFiles: []string{"0/Android.bp"},
885 args: []string{},
886 curDir: "0/1/2",
887 tidyOnly: "",
888 expectedArgs: []string{"MODULES-IN-0"},
Patrice Arruda13848222019-04-22 17:12:02 -0700889 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700890 description: "build action executed at root directory",
891 dirsInTrees: []string{},
892 buildFiles: []string{},
893 rootSymlink: false,
894 args: []string{},
895 curDir: ".",
896 tidyOnly: "",
897 expectedArgs: []string{},
Patrice Arrudababa9a92019-07-03 10:47:34 -0700898 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700899 description: "build action executed at root directory in symlink",
900 dirsInTrees: []string{},
901 buildFiles: []string{},
902 rootSymlink: true,
903 args: []string{},
904 curDir: ".",
905 tidyOnly: "",
906 expectedArgs: []string{},
Patrice Arruda13848222019-04-22 17:12:02 -0700907 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700908 description: "build file not found",
909 dirsInTrees: []string{"0/1/2"},
910 buildFiles: []string{},
911 args: []string{},
912 curDir: "0/1/2",
913 tidyOnly: "",
914 expectedArgs: []string{"MODULES-IN-0-1-2"},
915 expectedErrStr: "Build file not found for 0/1/2 directory",
Patrice Arruda13848222019-04-22 17:12:02 -0700916 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700917 description: "GET-INSTALL-PATH specified,",
918 dirsInTrees: []string{"0/1/2"},
919 buildFiles: []string{"0/1/Android.mk"},
920 args: []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"},
921 curDir: "0/1/2",
922 tidyOnly: "",
923 expectedArgs: []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"},
Patrice Arruda13848222019-04-22 17:12:02 -0700924 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700925 description: "tidy only environment variable specified,",
926 dirsInTrees: []string{"0/1/2"},
927 buildFiles: []string{"0/1/Android.mk"},
928 args: []string{"GET-INSTALL-PATH"},
929 curDir: "0/1/2",
930 tidyOnly: "true",
931 expectedArgs: []string{"tidy_only"},
Patrice Arruda13848222019-04-22 17:12:02 -0700932 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700933 description: "normal execution in root directory with args",
934 dirsInTrees: []string{},
935 buildFiles: []string{},
936 args: []string{"-j", "-k", "fake_module"},
937 curDir: "",
938 tidyOnly: "",
939 expectedArgs: []string{"-j", "-k", "fake_module"},
Patrice Arruda13848222019-04-22 17:12:02 -0700940 }}
941 for _, tt := range tests {
942 t.Run("build action BUILD_MODULES_IN_DIR, "+tt.description, func(t *testing.T) {
Dan Willemsence41e942019-07-29 23:39:30 -0700943 testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY)
Patrice Arruda13848222019-04-22 17:12:02 -0700944 })
945 }
946}
947
948func TestGetConfigArgsBuildModulesInDirectories(t *testing.T) {
949 tests := []buildActionTestCase{{
950 description: "normal execution in a directory",
951 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
952 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
953 args: []string{"3.1/", "3.2/", "3.3/"},
954 curDir: "0/1/2",
955 tidyOnly: "",
956 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 -0700957 }, {
958 description: "GET-INSTALL-PATH specified",
959 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3"},
960 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/Android.bp"},
961 args: []string{"GET-INSTALL-PATH", "2/3.1/", "2/3.2", "3"},
962 curDir: "0/1",
963 tidyOnly: "",
964 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 -0700965 }, {
966 description: "tidy only environment variable specified",
967 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
968 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
969 args: []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3"},
970 curDir: "0/1/2",
971 tidyOnly: "1",
972 expectedArgs: []string{"tidy_only"},
Patrice Arruda13848222019-04-22 17:12:02 -0700973 }, {
974 description: "normal execution from top dir directory",
975 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
976 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 -0700977 rootSymlink: false,
978 args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
979 curDir: ".",
980 tidyOnly: "",
981 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 -0700982 }, {
983 description: "normal execution from top dir directory in symlink",
984 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
985 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"},
986 rootSymlink: true,
Patrice Arruda13848222019-04-22 17:12:02 -0700987 args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
988 curDir: ".",
989 tidyOnly: "",
990 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 -0700991 }}
992 for _, tt := range tests {
993 t.Run("build action BUILD_MODULES_IN_DIRS, "+tt.description, func(t *testing.T) {
Dan Willemsence41e942019-07-29 23:39:30 -0700994 testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES)
Patrice Arruda13848222019-04-22 17:12:02 -0700995 })
996 }
997}