|  | // 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" | 
|  | smpb "android/soong/ui/metrics/metrics_proto" | 
|  | "android/soong/ui/status" | 
|  |  | 
|  | "google.golang.org/protobuf/encoding/prototext" | 
|  |  | 
|  | "google.golang.org/protobuf/proto" | 
|  | ) | 
|  |  | 
|  | 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) | 
|  | }) | 
|  |  | 
|  | env := Environment([]string{}) | 
|  | c := &configImpl{ | 
|  | environ:   &env, | 
|  | 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:  "multitree build action executed at root directory", | 
|  | dirsInTrees:  []string{}, | 
|  | buildFiles:   []string{}, | 
|  | rootSymlink:  false, | 
|  | args:         []string{"--multitree-build"}, | 
|  | curDir:       ".", | 
|  | tidyOnly:     "", | 
|  | expectedArgs: []string{"--multitree-build"}, | 
|  | }, { | 
|  | 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) | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestBuildConfig(t *testing.T) { | 
|  | tests := []struct { | 
|  | name                string | 
|  | environ             Environment | 
|  | arguments           []string | 
|  | expectedBuildConfig *smpb.BuildConfig | 
|  | }{ | 
|  | { | 
|  | name:    "none set", | 
|  | environ: Environment{}, | 
|  | expectedBuildConfig: &smpb.BuildConfig{ | 
|  | ForceUseGoma:          proto.Bool(false), | 
|  | UseGoma:               proto.Bool(false), | 
|  | UseRbe:                proto.Bool(false), | 
|  | NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), | 
|  | }, | 
|  | }, | 
|  | { | 
|  | name:    "force use goma", | 
|  | environ: Environment{"FORCE_USE_GOMA=1"}, | 
|  | expectedBuildConfig: &smpb.BuildConfig{ | 
|  | ForceUseGoma:          proto.Bool(true), | 
|  | UseGoma:               proto.Bool(false), | 
|  | UseRbe:                proto.Bool(false), | 
|  | NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), | 
|  | }, | 
|  | }, | 
|  | { | 
|  | name:    "use goma", | 
|  | environ: Environment{"USE_GOMA=1"}, | 
|  | expectedBuildConfig: &smpb.BuildConfig{ | 
|  | ForceUseGoma:          proto.Bool(false), | 
|  | UseGoma:               proto.Bool(true), | 
|  | UseRbe:                proto.Bool(false), | 
|  | NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), | 
|  | }, | 
|  | }, | 
|  | { | 
|  | name:    "use rbe", | 
|  | environ: Environment{"USE_RBE=1"}, | 
|  | expectedBuildConfig: &smpb.BuildConfig{ | 
|  | ForceUseGoma:          proto.Bool(false), | 
|  | UseGoma:               proto.Bool(false), | 
|  | UseRbe:                proto.Bool(true), | 
|  | NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), | 
|  | }, | 
|  | }, | 
|  | } | 
|  |  | 
|  | for _, tc := range tests { | 
|  | t.Run(tc.name, func(t *testing.T) { | 
|  | c := &configImpl{ | 
|  | environ:   &tc.environ, | 
|  | arguments: tc.arguments, | 
|  | } | 
|  | config := Config{c} | 
|  | actualBuildConfig := buildConfig(config) | 
|  | if expected := tc.expectedBuildConfig; !proto.Equal(expected, actualBuildConfig) { | 
|  | t.Errorf("Build config mismatch.\n"+ | 
|  | "Expected build config: %#v\n"+ | 
|  | "Actual build config: %#v", prototext.Format(expected), prototext.Format(actualBuildConfig)) | 
|  | } | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestGetMetricsUploaderApp(t *testing.T) { | 
|  |  | 
|  | metricsUploaderDir := "metrics_uploader_dir" | 
|  | metricsUploaderBinary := "metrics_uploader_binary" | 
|  | metricsUploaderPath := filepath.Join(metricsUploaderDir, metricsUploaderBinary) | 
|  | tests := []struct { | 
|  | description string | 
|  | environ     Environment | 
|  | createFiles bool | 
|  | expected    string | 
|  | }{{ | 
|  | description: "Uploader binary exist", | 
|  | environ:     Environment{"METRICS_UPLOADER=" + metricsUploaderPath}, | 
|  | createFiles: true, | 
|  | expected:    metricsUploaderPath, | 
|  | }, { | 
|  | description: "Uploader binary not exist", | 
|  | environ:     Environment{"METRICS_UPLOADER=" + metricsUploaderPath}, | 
|  | createFiles: false, | 
|  | expected:    "", | 
|  | }, { | 
|  | description: "Uploader binary variable not set", | 
|  | createFiles: true, | 
|  | expected:    "", | 
|  | }} | 
|  |  | 
|  | 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) | 
|  | }) | 
|  |  | 
|  | // 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) | 
|  |  | 
|  | expected := tt.expected | 
|  | if len(expected) > 0 { | 
|  | expected = filepath.Join(topDir, expected) | 
|  | } | 
|  |  | 
|  | if tt.createFiles { | 
|  | if err := os.MkdirAll(filepath.Join(topDir, metricsUploaderDir), 0755); err != nil { | 
|  | t.Errorf("failed to create %s directory: %v", metricsUploaderDir, err) | 
|  | } | 
|  | if err := ioutil.WriteFile(filepath.Join(topDir, metricsUploaderPath), []byte{}, 0644); err != nil { | 
|  | t.Errorf("failed to create file %s: %v", expected, err) | 
|  | } | 
|  | } | 
|  |  | 
|  | actual := GetMetricsUploader(topDir, &tt.environ) | 
|  |  | 
|  | if actual != expected { | 
|  | t.Errorf("expecting: %s, actual: %s", expected, actual) | 
|  | } | 
|  | }) | 
|  | } | 
|  | } |