12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241 |
- // 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
- useBazel bool
- bazelProdMode bool
- bazelStagingMode bool
- expectedBuildConfig *smpb.BuildConfig
- }{
- {
- name: "none set",
- environ: Environment{},
- expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(false),
- UseGoma: proto.Bool(false),
- UseRbe: proto.Bool(false),
- BazelMixedBuild: proto.Bool(false),
- ForceDisableBazelMixedBuild: 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),
- BazelMixedBuild: proto.Bool(false),
- ForceDisableBazelMixedBuild: 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),
- BazelMixedBuild: proto.Bool(false),
- ForceDisableBazelMixedBuild: 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),
- BazelMixedBuild: proto.Bool(false),
- ForceDisableBazelMixedBuild: proto.Bool(false),
- NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
- },
- },
- {
- name: "disable mixed builds",
- environ: Environment{"BUILD_BROKEN_DISABLE_BAZEL=1"},
- expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(false),
- UseGoma: proto.Bool(false),
- UseRbe: proto.Bool(false),
- BazelMixedBuild: proto.Bool(false),
- ForceDisableBazelMixedBuild: proto.Bool(true),
- NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
- },
- },
- {
- name: "use bazel as ninja",
- environ: Environment{},
- useBazel: true,
- expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(false),
- UseGoma: proto.Bool(false),
- UseRbe: proto.Bool(false),
- BazelMixedBuild: proto.Bool(false),
- ForceDisableBazelMixedBuild: proto.Bool(false),
- NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
- },
- },
- {
- name: "bazel mixed build from prod mode",
- environ: Environment{},
- bazelProdMode: true,
- expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(false),
- UseGoma: proto.Bool(false),
- UseRbe: proto.Bool(false),
- BazelMixedBuild: proto.Bool(true),
- ForceDisableBazelMixedBuild: proto.Bool(false),
- NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
- },
- },
- {
- name: "bazel mixed build from staging mode",
- environ: Environment{},
- bazelStagingMode: true,
- expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(false),
- UseGoma: proto.Bool(false),
- UseRbe: proto.Bool(false),
- BazelMixedBuild: proto.Bool(true),
- ForceDisableBazelMixedBuild: proto.Bool(false),
- NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
- },
- },
- {
- name: "specified targets",
- environ: Environment{},
- useBazel: true,
- arguments: []string{"droid", "dist"},
- expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(false),
- UseGoma: proto.Bool(false),
- UseRbe: proto.Bool(false),
- BazelMixedBuild: proto.Bool(false),
- Targets: []string{"droid", "dist"},
- ForceDisableBazelMixedBuild: proto.Bool(false),
- NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
- },
- },
- {
- name: "all set",
- environ: Environment{
- "FORCE_USE_GOMA=1",
- "USE_GOMA=1",
- "USE_RBE=1",
- "BUILD_BROKEN_DISABLE_BAZEL=1",
- },
- useBazel: true,
- bazelProdMode: true,
- expectedBuildConfig: &smpb.BuildConfig{
- ForceUseGoma: proto.Bool(true),
- UseGoma: proto.Bool(true),
- UseRbe: proto.Bool(true),
- BazelMixedBuild: proto.Bool(true),
- ForceDisableBazelMixedBuild: proto.Bool(true),
- NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(),
- },
- },
- }
- ctx := testContext()
- for _, tc := range tests {
- t.Run(tc.name, func(t *testing.T) {
- c := &configImpl{
- environ: &tc.environ,
- bazelProdMode: tc.bazelProdMode,
- bazelStagingMode: tc.bazelStagingMode,
- arguments: tc.arguments,
- }
- config := Config{c}
- checkBazelMode(ctx, config)
- 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)
- }
- })
- }
- }
|