// Copyright 2017 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package build import ( "bytes" "context" "fmt" "io/ioutil" "os" "path/filepath" "reflect" "strings" "testing" "android/soong/ui/logger" "android/soong/ui/status" ) func testContext() Context { return Context{&ContextImpl{ Context: context.Background(), Logger: logger.New(&bytes.Buffer{}), Writer: &bytes.Buffer{}, Status: &status.Status{}, }} } func TestConfigParseArgsJK(t *testing.T) { ctx := testContext() testCases := []struct { args []string parallel int keepGoing int remaining []string }{ {nil, -1, -1, nil}, {[]string{"-j"}, -1, -1, nil}, {[]string{"-j1"}, 1, -1, nil}, {[]string{"-j1234"}, 1234, -1, nil}, {[]string{"-j", "1"}, 1, -1, nil}, {[]string{"-j", "1234"}, 1234, -1, nil}, {[]string{"-j", "1234", "abc"}, 1234, -1, []string{"abc"}}, {[]string{"-j", "abc"}, -1, -1, []string{"abc"}}, {[]string{"-j", "1abc"}, -1, -1, []string{"1abc"}}, {[]string{"-k"}, -1, 0, nil}, {[]string{"-k0"}, -1, 0, nil}, {[]string{"-k1"}, -1, 1, nil}, {[]string{"-k1234"}, -1, 1234, nil}, {[]string{"-k", "0"}, -1, 0, nil}, {[]string{"-k", "1"}, -1, 1, nil}, {[]string{"-k", "1234"}, -1, 1234, nil}, {[]string{"-k", "1234", "abc"}, -1, 1234, []string{"abc"}}, {[]string{"-k", "abc"}, -1, 0, []string{"abc"}}, {[]string{"-k", "1abc"}, -1, 0, []string{"1abc"}}, // TODO: These are supported in Make, should we support them? //{[]string{"-kj"}, -1, 0}, //{[]string{"-kj8"}, 8, 0}, // -jk is not valid in Make } for _, tc := range testCases { t.Run(strings.Join(tc.args, " "), func(t *testing.T) { defer logger.Recover(func(err error) { t.Fatal(err) }) c := &configImpl{ parallel: -1, keepGoing: -1, } c.parseArgs(ctx, tc.args) if c.parallel != tc.parallel { t.Errorf("for %q, parallel:\nwant: %d\n got: %d\n", strings.Join(tc.args, " "), tc.parallel, c.parallel) } if c.keepGoing != tc.keepGoing { t.Errorf("for %q, keep going:\nwant: %d\n got: %d\n", strings.Join(tc.args, " "), tc.keepGoing, c.keepGoing) } if !reflect.DeepEqual(c.arguments, tc.remaining) { t.Errorf("for %q, remaining arguments:\nwant: %q\n got: %q\n", strings.Join(tc.args, " "), tc.remaining, c.arguments) } }) } } func TestConfigParseArgsVars(t *testing.T) { ctx := testContext() testCases := []struct { env []string args []string expectedEnv []string remaining []string }{ {}, { env: []string{"A=bc"}, expectedEnv: []string{"A=bc"}, }, { args: []string{"abc"}, remaining: []string{"abc"}, }, { args: []string{"A=bc"}, expectedEnv: []string{"A=bc"}, }, { env: []string{"A=a"}, args: []string{"A=bc"}, expectedEnv: []string{"A=bc"}, }, { env: []string{"A=a"}, args: []string{"A=", "=b"}, expectedEnv: []string{"A="}, remaining: []string{"=b"}, }, } for _, tc := range testCases { t.Run(strings.Join(tc.args, " "), func(t *testing.T) { defer logger.Recover(func(err error) { t.Fatal(err) }) e := Environment(tc.env) c := &configImpl{ environ: &e, } c.parseArgs(ctx, tc.args) if !reflect.DeepEqual([]string(*c.environ), tc.expectedEnv) { t.Errorf("for env=%q args=%q, environment:\nwant: %q\n got: %q\n", tc.env, tc.args, tc.expectedEnv, []string(*c.environ)) } if !reflect.DeepEqual(c.arguments, tc.remaining) { t.Errorf("for env=%q args=%q, remaining arguments:\nwant: %q\n got: %q\n", tc.env, tc.args, tc.remaining, c.arguments) } }) } } func TestConfigCheckTopDir(t *testing.T) { ctx := testContext() buildRootDir := filepath.Dir(srcDirFileCheck) expectedErrStr := fmt.Sprintf("Current working directory must be the source tree. %q not found.", srcDirFileCheck) tests := []struct { // ********* Setup ********* // Test description. description string // ********* Action ********* // If set to true, the build root file is created. rootBuildFile bool // The current path where Soong is being executed. path string // ********* Validation ********* // Expecting error and validate the error string against expectedErrStr. wantErr bool }{{ description: "current directory is the root source tree", rootBuildFile: true, path: ".", wantErr: false, }, { description: "one level deep in the source tree", rootBuildFile: true, path: "1", wantErr: true, }, { description: "very deep in the source tree", rootBuildFile: true, 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", wantErr: true, }, { description: "outside of source tree", rootBuildFile: false, path: "1/2/3/4/5", wantErr: true, }} for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { defer logger.Recover(func(err error) { if !tt.wantErr { t.Fatalf("Got unexpected error: %v", err) } if expectedErrStr != err.Error() { t.Fatalf("expected %s, got %s", expectedErrStr, err.Error()) } }) // Create the root source tree. rootDir, err := ioutil.TempDir("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(rootDir) // Create the build root file. This is to test if topDir returns an error if the build root // file does not exist. if tt.rootBuildFile { dir := filepath.Join(rootDir, buildRootDir) if err := os.MkdirAll(dir, 0755); err != nil { t.Errorf("failed to create %s directory: %v", dir, err) } f := filepath.Join(rootDir, srcDirFileCheck) if err := ioutil.WriteFile(f, []byte{}, 0644); err != nil { t.Errorf("failed to create file %s: %v", f, err) } } // Next block of code is to set the current directory. dir := rootDir if tt.path != "" { dir = filepath.Join(dir, tt.path) if err := os.MkdirAll(dir, 0755); err != nil { t.Errorf("failed to create %s directory: %v", dir, err) } } curDir, err := os.Getwd() if err != nil { t.Fatalf("failed to get the current directory: %v", err) } defer func() { os.Chdir(curDir) }() if err := os.Chdir(dir); err != nil { t.Fatalf("failed to change directory to %s: %v", dir, err) } checkTopDir(ctx) }) } } func TestConfigConvertToTarget(t *testing.T) { tests := []struct { // ********* Setup ********* // Test description. description string // ********* Action ********* // The current directory where Soong is being executed. dir string // The current prefix string to be pre-appended to the target. prefix string // ********* Validation ********* // The expected target to be invoked in ninja. expectedTarget string }{{ description: "one level directory in source tree", dir: "test1", prefix: "MODULES-IN-", expectedTarget: "MODULES-IN-test1", }, { description: "multiple level directories in source tree", dir: "test1/test2/test3/test4", prefix: "GET-INSTALL-PATH-IN-", expectedTarget: "GET-INSTALL-PATH-IN-test1-test2-test3-test4", }} for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { target := convertToTarget(tt.dir, tt.prefix) if target != tt.expectedTarget { t.Errorf("expected %s, got %s for target", tt.expectedTarget, target) } }) } } func setTop(t *testing.T, dir string) func() { curDir, err := os.Getwd() if err != nil { t.Fatalf("failed to get current directory: %v", err) } if err := os.Chdir(dir); err != nil { t.Fatalf("failed to change directory to top dir %s: %v", dir, err) } return func() { os.Chdir(curDir) } } func createBuildFiles(t *testing.T, topDir string, buildFiles []string) { for _, buildFile := range buildFiles { buildFile = filepath.Join(topDir, buildFile) if err := ioutil.WriteFile(buildFile, []byte{}, 0644); err != nil { t.Errorf("failed to create file %s: %v", buildFile, err) } } } func createDirectories(t *testing.T, topDir string, dirs []string) { for _, dir := range dirs { dir = filepath.Join(topDir, dir) if err := os.MkdirAll(dir, 0755); err != nil { t.Errorf("failed to create %s directory: %v", dir, err) } } } func TestConfigGetTargets(t *testing.T) { ctx := testContext() tests := []struct { // ********* Setup ********* // Test description. description string // Directories that exist in the source tree. dirsInTrees []string // Build files that exists in the source tree. buildFiles []string // ********* Action ********* // Directories passed in to soong_ui. dirs []string // Current directory that the user executed the build action command. curDir string // ********* Validation ********* // Expected targets from the function. expectedTargets []string // Expecting error from running test case. errStr string }{{ description: "one target dir specified", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{"0/1/2/3/Android.bp"}, dirs: []string{"1/2/3"}, curDir: "0", expectedTargets: []string{"MODULES-IN-0-1-2-3"}, }, { description: "one target dir specified, build file does not exist", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{}, dirs: []string{"1/2/3"}, curDir: "0", errStr: "Build file not found for 0/1/2/3 directory", }, { description: "one target dir specified, invalid targets specified", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{}, dirs: []string{"1/2/3:t1:t2"}, curDir: "0", errStr: "1/2/3:t1:t2 not in proper directory:target1,target2,... format (\":\" was specified more than once)", }, { description: "one target dir specified, no targets specified but has colon", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{"0/1/2/3/Android.bp"}, dirs: []string{"1/2/3:"}, curDir: "0", expectedTargets: []string{"MODULES-IN-0-1-2-3"}, }, { description: "one target dir specified, two targets specified", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{"0/1/2/3/Android.bp"}, dirs: []string{"1/2/3:t1,t2"}, curDir: "0", expectedTargets: []string{"t1", "t2"}, }, { description: "one target dir specified, no targets and has a comma", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{"0/1/2/3/Android.bp"}, dirs: []string{"1/2/3:,"}, curDir: "0", errStr: "0/1/2/3 not in proper directory:target1,target2,... format", }, { description: "one target dir specified, improper targets defined", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{"0/1/2/3/Android.bp"}, dirs: []string{"1/2/3:,t1"}, curDir: "0", errStr: "0/1/2/3 not in proper directory:target1,target2,... format", }, { description: "one target dir specified, blank target", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{"0/1/2/3/Android.bp"}, dirs: []string{"1/2/3:t1,"}, curDir: "0", errStr: "0/1/2/3 not in proper directory:target1,target2,... format", }, { description: "one target dir specified, many targets specified", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{"0/1/2/3/Android.bp"}, dirs: []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"}, curDir: "0", expectedTargets: []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"}, }, { description: "one target dir specified, one target specified, build file does not exist", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{}, dirs: []string{"1/2/3:t1"}, curDir: "0", errStr: "Couldn't locate a build file from 0/1/2/3 directory", }, { description: "one target dir specified, one target specified, build file not in target dir", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{"0/1/2/Android.mk"}, dirs: []string{"1/2/3:t1"}, curDir: "0", errStr: "Couldn't locate a build file from 0/1/2/3 directory", }, { description: "one target dir specified, build file not in target dir", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{"0/1/2/Android.mk"}, dirs: []string{"1/2/3"}, curDir: "0", expectedTargets: []string{"MODULES-IN-0-1-2"}, }, { description: "multiple targets dir specified, targets specified", dirsInTrees: []string{"0/1/2/3", "0/3/4"}, buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"}, dirs: []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"}, curDir: "0", expectedTargets: []string{"t1", "t2", "t3", "t4", "t5"}, }, { description: "multiple targets dir specified, one directory has targets specified", dirsInTrees: []string{"0/1/2/3", "0/3/4"}, buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"}, dirs: []string{"1/2/3:t1,t2", "3/4"}, curDir: "0", expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"}, }, { description: "two dirs specified, only one dir exist", dirsInTrees: []string{"0/1/2/3"}, buildFiles: []string{"0/1/2/3/Android.mk"}, dirs: []string{"1/2/3:t1", "3/4"}, curDir: "0", errStr: "couldn't find directory 0/3/4", }, { description: "multiple targets dirs specified at root source tree", dirsInTrees: []string{"0/1/2/3", "0/3/4"}, buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"}, dirs: []string{"0/1/2/3:t1,t2", "0/3/4"}, curDir: ".", expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"}, }, { description: "no directories specified", dirsInTrees: []string{"0/1/2/3", "0/3/4"}, buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"}, dirs: []string{}, curDir: ".", }} for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { defer logger.Recover(func(err error) { if tt.errStr == "" { t.Fatalf("Got unexpected error: %v", err) } if tt.errStr != err.Error() { t.Errorf("expected %s, got %s", tt.errStr, err.Error()) } }) // Create the root source tree. topDir, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } defer os.RemoveAll(topDir) createDirectories(t, topDir, tt.dirsInTrees) createBuildFiles(t, topDir, tt.buildFiles) r := setTop(t, topDir) defer r() targets := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-") if !reflect.DeepEqual(targets, tt.expectedTargets) { t.Errorf("expected %v, got %v for targets", tt.expectedTargets, targets) } // If the execution reached here and there was an expected error code, the unit test case failed. if tt.errStr != "" { t.Errorf("expecting error %s", tt.errStr) } }) } } func TestConfigFindBuildFile(t *testing.T) { ctx := testContext() tests := []struct { // ********* Setup ********* // Test description. description string // Array of build files to create in dir. buildFiles []string // Directories that exist in the source tree. dirsInTrees []string // ********* Action ********* // The base directory is where findBuildFile is invoked. dir string // ********* Validation ********* // Expected build file path to find. expectedBuildFile string }{{ description: "build file exists at leaf directory", buildFiles: []string{"1/2/3/Android.bp"}, dirsInTrees: []string{"1/2/3"}, dir: "1/2/3", expectedBuildFile: "1/2/3/Android.mk", }, { description: "build file exists in all directory paths", buildFiles: []string{"1/Android.mk", "1/2/Android.mk", "1/2/3/Android.mk"}, dirsInTrees: []string{"1/2/3"}, dir: "1/2/3", expectedBuildFile: "1/2/3/Android.mk", }, { description: "build file does not exist in all directory paths", buildFiles: []string{}, dirsInTrees: []string{"1/2/3"}, dir: "1/2/3", expectedBuildFile: "", }, { description: "build file exists only at top directory", buildFiles: []string{"Android.bp"}, dirsInTrees: []string{"1/2/3"}, dir: "1/2/3", expectedBuildFile: "", }, { description: "build file exist in a subdirectory", buildFiles: []string{"1/2/Android.bp"}, dirsInTrees: []string{"1/2/3"}, dir: "1/2/3", expectedBuildFile: "1/2/Android.mk", }, { description: "build file exists in a subdirectory", buildFiles: []string{"1/Android.mk"}, dirsInTrees: []string{"1/2/3"}, dir: "1/2/3", expectedBuildFile: "1/Android.mk", }, { description: "top directory", buildFiles: []string{"Android.bp"}, dirsInTrees: []string{}, dir: ".", expectedBuildFile: "", }, { description: "build file exists in subdirectory", buildFiles: []string{"1/2/3/Android.bp", "1/2/4/Android.bp"}, dirsInTrees: []string{"1/2/3", "1/2/4"}, dir: "1/2", expectedBuildFile: "1/2/Android.mk", }, { description: "build file exists in parent subdirectory", buildFiles: []string{"1/5/Android.bp"}, dirsInTrees: []string{"1/2/3", "1/2/4", "1/5"}, dir: "1/2", expectedBuildFile: "1/Android.mk", }, { description: "build file exists in deep parent's subdirectory.", buildFiles: []string{"1/5/6/Android.bp"}, dirsInTrees: []string{"1/2/3", "1/2/4", "1/5/6", "1/5/7"}, dir: "1/2", expectedBuildFile: "1/Android.mk", }} for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { defer logger.Recover(func(err error) { t.Fatalf("Got unexpected error: %v", err) }) topDir, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } defer os.RemoveAll(topDir) createDirectories(t, topDir, tt.dirsInTrees) createBuildFiles(t, topDir, tt.buildFiles) curDir, err := os.Getwd() if err != nil { t.Fatalf("Could not get working directory: %v", err) } defer func() { os.Chdir(curDir) }() if err := os.Chdir(topDir); err != nil { t.Fatalf("Could not change top dir to %s: %v", topDir, err) } buildFile := findBuildFile(ctx, tt.dir) if buildFile != tt.expectedBuildFile { t.Errorf("expected %q, got %q for build file", tt.expectedBuildFile, buildFile) } }) } } func TestConfigSplitArgs(t *testing.T) { tests := []struct { // ********* Setup ********* // Test description. description string // ********* Action ********* // Arguments passed in to soong_ui. args []string // ********* Validation ********* // Expected newArgs list after extracting the directories. expectedNewArgs []string // Expected directories expectedDirs []string }{{ description: "flags but no directories specified", args: []string{"showcommands", "-j", "-k"}, expectedNewArgs: []string{"showcommands", "-j", "-k"}, expectedDirs: []string{}, }, { description: "flags and one directory specified", args: []string{"snod", "-j", "dir:target1,target2"}, expectedNewArgs: []string{"snod", "-j"}, expectedDirs: []string{"dir:target1,target2"}, }, { description: "flags and directories specified", args: []string{"dist", "-k", "dir1", "dir2:target1,target2"}, expectedNewArgs: []string{"dist", "-k"}, expectedDirs: []string{"dir1", "dir2:target1,target2"}, }, { description: "only directories specified", args: []string{"dir1", "dir2", "dir3:target1,target2"}, expectedNewArgs: []string{}, expectedDirs: []string{"dir1", "dir2", "dir3:target1,target2"}, }} for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { args, dirs := splitArgs(tt.args) if !reflect.DeepEqual(tt.expectedNewArgs, args) { t.Errorf("expected %v, got %v for arguments", tt.expectedNewArgs, args) } if !reflect.DeepEqual(tt.expectedDirs, dirs) { t.Errorf("expected %v, got %v for directories", tt.expectedDirs, dirs) } }) } } type envVar struct { name string value string } type buildActionTestCase struct { // ********* Setup ********* // Test description. description string // Directories that exist in the source tree. dirsInTrees []string // Build files that exists in the source tree. buildFiles []string // Create root symlink that points to topDir. rootSymlink bool // ********* Action ********* // Arguments passed in to soong_ui. args []string // Directory where the build action was invoked. curDir string // WITH_TIDY_ONLY environment variable specified. tidyOnly string // ********* Validation ********* // Expected arguments to be in Config instance. expectedArgs []string // Expecting error from running test case. expectedErrStr string } func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction) { ctx := testContext() defer logger.Recover(func(err error) { if tt.expectedErrStr == "" { t.Fatalf("Got unexpected error: %v", err) } if tt.expectedErrStr != err.Error() { t.Errorf("expected %s, got %s", tt.expectedErrStr, err.Error()) } }) // Environment variables to set it to blank on every test case run. resetEnvVars := []string{ "WITH_TIDY_ONLY", } for _, name := range resetEnvVars { if err := os.Unsetenv(name); err != nil { t.Fatalf("failed to unset environment variable %s: %v", name, err) } } if tt.tidyOnly != "" { if err := os.Setenv("WITH_TIDY_ONLY", tt.tidyOnly); err != nil { t.Errorf("failed to set WITH_TIDY_ONLY to %s: %v", tt.tidyOnly, err) } } // Create the root source tree. topDir, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } defer os.RemoveAll(topDir) createDirectories(t, topDir, tt.dirsInTrees) createBuildFiles(t, topDir, tt.buildFiles) if tt.rootSymlink { // Create a secondary root source tree which points to the true root source tree. symlinkTopDir, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("failed to create symlink temp dir: %v", err) } defer os.RemoveAll(symlinkTopDir) symlinkTopDir = filepath.Join(symlinkTopDir, "root") err = os.Symlink(topDir, symlinkTopDir) if err != nil { t.Fatalf("failed to create symlink: %v", err) } topDir = symlinkTopDir } r := setTop(t, topDir) defer r() // The next block is to create the root build file. rootBuildFileDir := filepath.Dir(srcDirFileCheck) if err := os.MkdirAll(rootBuildFileDir, 0755); err != nil { t.Fatalf("Failed to create %s directory: %v", rootBuildFileDir, err) } if err := ioutil.WriteFile(srcDirFileCheck, []byte{}, 0644); err != nil { t.Fatalf("failed to create %s file: %v", srcDirFileCheck, err) } args := getConfigArgs(action, tt.curDir, ctx, tt.args) if !reflect.DeepEqual(tt.expectedArgs, args) { t.Fatalf("expected %v, got %v for config arguments", tt.expectedArgs, args) } // If the execution reached here and there was an expected error code, the unit test case failed. if tt.expectedErrStr != "" { t.Errorf("expecting error %s", tt.expectedErrStr) } } func TestGetConfigArgsBuildModules(t *testing.T) { tests := []buildActionTestCase{{ description: "normal execution from the root source tree directory", dirsInTrees: []string{"0/1/2", "0/2", "0/3"}, buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "0/3/Android.mk"}, args: []string{"-j", "fake_module", "fake_module2"}, curDir: ".", tidyOnly: "", expectedArgs: []string{"-j", "fake_module", "fake_module2"}, }, { description: "normal execution in deep directory", dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"}, 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"}, args: []string{"-j", "fake_module", "fake_module2", "-k"}, curDir: "1/2/3/4/5/6/7/8/9", tidyOnly: "", expectedArgs: []string{"-j", "fake_module", "fake_module2", "-k"}, }, { description: "normal execution in deep directory, no targets", dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"}, 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"}, args: []string{"-j", "-k"}, curDir: "1/2/3/4/5/6/7/8/9", tidyOnly: "", expectedArgs: []string{"-j", "-k"}, }, { description: "normal execution in root source tree, no args", dirsInTrees: []string{"0/1/2", "0/2", "0/3"}, buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"}, args: []string{}, curDir: "0/2", tidyOnly: "", expectedArgs: []string{}, }, { description: "normal execution in symlink root source tree, no args", dirsInTrees: []string{"0/1/2", "0/2", "0/3"}, buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"}, rootSymlink: true, args: []string{}, curDir: "0/2", tidyOnly: "", expectedArgs: []string{}, }} for _, tt := range tests { t.Run("build action BUILD_MODULES with dependencies, "+tt.description, func(t *testing.T) { testGetConfigArgs(t, tt, BUILD_MODULES) }) } } func TestGetConfigArgsBuildModulesInDirectory(t *testing.T) { tests := []buildActionTestCase{{ description: "normal execution in a directory", dirsInTrees: []string{"0/1/2"}, buildFiles: []string{"0/1/2/Android.mk"}, args: []string{"fake-module"}, curDir: "0/1/2", tidyOnly: "", expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"}, }, { description: "build file in parent directory", dirsInTrees: []string{"0/1/2"}, buildFiles: []string{"0/1/Android.mk"}, args: []string{}, curDir: "0/1/2", tidyOnly: "", expectedArgs: []string{"MODULES-IN-0-1"}, }, { description: "build file in parent directory, multiple module names passed in", dirsInTrees: []string{"0/1/2"}, buildFiles: []string{"0/1/Android.mk"}, args: []string{"fake-module1", "fake-module2", "fake-module3"}, curDir: "0/1/2", tidyOnly: "", expectedArgs: []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"}, }, { description: "build file in 2nd level parent directory", dirsInTrees: []string{"0/1/2"}, buildFiles: []string{"0/Android.bp"}, args: []string{}, curDir: "0/1/2", tidyOnly: "", expectedArgs: []string{"MODULES-IN-0"}, }, { description: "build action executed at root directory", dirsInTrees: []string{}, buildFiles: []string{}, rootSymlink: false, args: []string{}, curDir: ".", tidyOnly: "", expectedArgs: []string{}, }, { description: "build action executed at root directory in symlink", dirsInTrees: []string{}, buildFiles: []string{}, rootSymlink: true, args: []string{}, curDir: ".", tidyOnly: "", expectedArgs: []string{}, }, { description: "build file not found", dirsInTrees: []string{"0/1/2"}, buildFiles: []string{}, args: []string{}, curDir: "0/1/2", tidyOnly: "", expectedArgs: []string{"MODULES-IN-0-1-2"}, expectedErrStr: "Build file not found for 0/1/2 directory", }, { description: "GET-INSTALL-PATH specified,", dirsInTrees: []string{"0/1/2"}, buildFiles: []string{"0/1/Android.mk"}, args: []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"}, curDir: "0/1/2", tidyOnly: "", expectedArgs: []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"}, }, { description: "tidy only environment variable specified,", dirsInTrees: []string{"0/1/2"}, buildFiles: []string{"0/1/Android.mk"}, args: []string{"GET-INSTALL-PATH"}, curDir: "0/1/2", tidyOnly: "true", expectedArgs: []string{"tidy_only"}, }, { description: "normal execution in root directory with args", dirsInTrees: []string{}, buildFiles: []string{}, args: []string{"-j", "-k", "fake_module"}, curDir: "", tidyOnly: "", expectedArgs: []string{"-j", "-k", "fake_module"}, }} for _, tt := range tests { t.Run("build action BUILD_MODULES_IN_DIR, "+tt.description, func(t *testing.T) { testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY) }) } } func TestGetConfigArgsBuildModulesInDirectories(t *testing.T) { tests := []buildActionTestCase{{ description: "normal execution in a directory", dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"}, buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"}, args: []string{"3.1/", "3.2/", "3.3/"}, curDir: "0/1/2", tidyOnly: "", expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-2-3.3"}, }, { description: "GET-INSTALL-PATH specified", dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3"}, buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/Android.bp"}, args: []string{"GET-INSTALL-PATH", "2/3.1/", "2/3.2", "3"}, curDir: "0/1", tidyOnly: "", 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"}, }, { description: "tidy only environment variable specified", dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"}, buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"}, args: []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3"}, curDir: "0/1/2", tidyOnly: "1", expectedArgs: []string{"tidy_only"}, }, { description: "normal execution from top dir directory", dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, 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"}, rootSymlink: false, args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, curDir: ".", tidyOnly: "", 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"}, }, { description: "normal execution from top dir directory in symlink", dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, 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"}, rootSymlink: true, args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, curDir: ".", tidyOnly: "", 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"}, }} for _, tt := range tests { t.Run("build action BUILD_MODULES_IN_DIRS, "+tt.description, func(t *testing.T) { testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES) }) } }