// Copyright 2015 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 android import ( "errors" "fmt" "reflect" "strconv" "strings" "testing" "github.com/google/blueprint/proptools" ) type strsTestCase struct { in []string out string err []error } var commonValidatePathTestCases = []strsTestCase{ { in: []string{""}, out: "", }, { in: []string{"a/b"}, out: "a/b", }, { in: []string{"a/b", "c"}, out: "a/b/c", }, { in: []string{"a/.."}, out: ".", }, { in: []string{"."}, out: ".", }, { in: []string{".."}, out: "", err: []error{errors.New("Path is outside directory: ..")}, }, { in: []string{"../a"}, out: "", err: []error{errors.New("Path is outside directory: ../a")}, }, { in: []string{"b/../../a"}, out: "", err: []error{errors.New("Path is outside directory: ../a")}, }, { in: []string{"/a"}, out: "", err: []error{errors.New("Path is outside directory: /a")}, }, { in: []string{"a", "../b"}, out: "", err: []error{errors.New("Path is outside directory: ../b")}, }, { in: []string{"a", "b/../../c"}, out: "", err: []error{errors.New("Path is outside directory: ../c")}, }, { in: []string{"a", "./.."}, out: "", err: []error{errors.New("Path is outside directory: ..")}, }, } var validateSafePathTestCases = append(commonValidatePathTestCases, []strsTestCase{ { in: []string{"$host/../$a"}, out: "$a", }, }...) var validatePathTestCases = append(commonValidatePathTestCases, []strsTestCase{ { in: []string{"$host/../$a"}, out: "", err: []error{errors.New("Path contains invalid character($): $host/../$a")}, }, { in: []string{"$host/.."}, out: "", err: []error{errors.New("Path contains invalid character($): $host/..")}, }, }...) func TestValidateSafePath(t *testing.T) { for _, testCase := range validateSafePathTestCases { t.Run(strings.Join(testCase.in, ","), func(t *testing.T) { ctx := &configErrorWrapper{} out, err := validateSafePath(testCase.in...) if err != nil { reportPathError(ctx, err) } check(t, "validateSafePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err) }) } } func TestValidatePath(t *testing.T) { for _, testCase := range validatePathTestCases { t.Run(strings.Join(testCase.in, ","), func(t *testing.T) { ctx := &configErrorWrapper{} out, err := validatePath(testCase.in...) if err != nil { reportPathError(ctx, err) } check(t, "validatePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err) }) } } func TestOptionalPath(t *testing.T) { var path OptionalPath checkInvalidOptionalPath(t, path) path = OptionalPathForPath(nil) checkInvalidOptionalPath(t, path) } func checkInvalidOptionalPath(t *testing.T, path OptionalPath) { t.Helper() if path.Valid() { t.Errorf("Uninitialized OptionalPath should not be valid") } if path.String() != "" { t.Errorf("Uninitialized OptionalPath String() should return \"\", not %q", path.String()) } defer func() { if r := recover(); r == nil { t.Errorf("Expected a panic when calling Path() on an uninitialized OptionalPath") } }() path.Path() } func check(t *testing.T, testType, testString string, got interface{}, err []error, expected interface{}, expectedErr []error) { t.Helper() printedTestCase := false e := func(s string, expected, got interface{}) { t.Helper() if !printedTestCase { t.Errorf("test case %s: %s", testType, testString) printedTestCase = true } t.Errorf("incorrect %s", s) t.Errorf(" expected: %s", p(expected)) t.Errorf(" got: %s", p(got)) } if !reflect.DeepEqual(expectedErr, err) { e("errors:", expectedErr, err) } if !reflect.DeepEqual(expected, got) { e("output:", expected, got) } } func p(in interface{}) string { if v, ok := in.([]interface{}); ok { s := make([]string, len(v)) for i := range v { s[i] = fmt.Sprintf("%#v", v[i]) } return "[" + strings.Join(s, ", ") + "]" } else { return fmt.Sprintf("%#v", in) } } type moduleInstallPathContextImpl struct { baseModuleContext inData bool inTestcases bool inSanitizerDir bool inRamdisk bool inRecovery bool inRoot bool forceOS *OsType } func (m moduleInstallPathContextImpl) Config() Config { return m.baseModuleContext.config } func (moduleInstallPathContextImpl) AddNinjaFileDeps(deps ...string) {} func (m moduleInstallPathContextImpl) InstallInData() bool { return m.inData } func (m moduleInstallPathContextImpl) InstallInTestcases() bool { return m.inTestcases } func (m moduleInstallPathContextImpl) InstallInSanitizerDir() bool { return m.inSanitizerDir } func (m moduleInstallPathContextImpl) InstallInRamdisk() bool { return m.inRamdisk } func (m moduleInstallPathContextImpl) InstallInRecovery() bool { return m.inRecovery } func (m moduleInstallPathContextImpl) InstallInRoot() bool { return m.inRoot } func (m moduleInstallPathContextImpl) InstallBypassMake() bool { return false } func (m moduleInstallPathContextImpl) InstallForceOS() *OsType { return m.forceOS } func pathTestConfig(buildDir string) Config { return TestConfig(buildDir, nil, "", nil) } func TestPathForModuleInstall(t *testing.T) { testConfig := pathTestConfig("") hostTarget := Target{Os: Linux} deviceTarget := Target{Os: Android} testCases := []struct { name string ctx *moduleInstallPathContextImpl in []string out string }{ { name: "host binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: hostTarget.Os, target: hostTarget, }, }, in: []string{"bin", "my_test"}, out: "host/linux-x86/bin/my_test", }, { name: "system binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, }, }, in: []string{"bin", "my_test"}, out: "target/product/test_device/system/bin/my_test", }, { name: "vendor binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: socSpecificModule, }, }, }, in: []string{"bin", "my_test"}, out: "target/product/test_device/vendor/bin/my_test", }, { name: "odm binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: deviceSpecificModule, }, }, }, in: []string{"bin", "my_test"}, out: "target/product/test_device/odm/bin/my_test", }, { name: "product binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: productSpecificModule, }, }, }, in: []string{"bin", "my_test"}, out: "target/product/test_device/product/bin/my_test", }, { name: "system_ext binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: systemExtSpecificModule, }, }, }, in: []string{"bin", "my_test"}, out: "target/product/test_device/system_ext/bin/my_test", }, { name: "root binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, }, inRoot: true, }, in: []string{"my_test"}, out: "target/product/test_device/root/my_test", }, { name: "recovery binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, }, inRecovery: true, }, in: []string{"bin/my_test"}, out: "target/product/test_device/recovery/root/system/bin/my_test", }, { name: "recovery root binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, }, inRecovery: true, inRoot: true, }, in: []string{"my_test"}, out: "target/product/test_device/recovery/root/my_test", }, { name: "system native test binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, }, inData: true, }, in: []string{"nativetest", "my_test"}, out: "target/product/test_device/data/nativetest/my_test", }, { name: "vendor native test binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: socSpecificModule, }, }, inData: true, }, in: []string{"nativetest", "my_test"}, out: "target/product/test_device/data/nativetest/my_test", }, { name: "odm native test binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: deviceSpecificModule, }, }, inData: true, }, in: []string{"nativetest", "my_test"}, out: "target/product/test_device/data/nativetest/my_test", }, { name: "product native test binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: productSpecificModule, }, }, inData: true, }, in: []string{"nativetest", "my_test"}, out: "target/product/test_device/data/nativetest/my_test", }, { name: "system_ext native test binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: systemExtSpecificModule, }, }, inData: true, }, in: []string{"nativetest", "my_test"}, out: "target/product/test_device/data/nativetest/my_test", }, { name: "sanitized system binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, }, inSanitizerDir: true, }, in: []string{"bin", "my_test"}, out: "target/product/test_device/data/asan/system/bin/my_test", }, { name: "sanitized vendor binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: socSpecificModule, }, }, inSanitizerDir: true, }, in: []string{"bin", "my_test"}, out: "target/product/test_device/data/asan/vendor/bin/my_test", }, { name: "sanitized odm binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: deviceSpecificModule, }, }, inSanitizerDir: true, }, in: []string{"bin", "my_test"}, out: "target/product/test_device/data/asan/odm/bin/my_test", }, { name: "sanitized product binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: productSpecificModule, }, }, inSanitizerDir: true, }, in: []string{"bin", "my_test"}, out: "target/product/test_device/data/asan/product/bin/my_test", }, { name: "sanitized system_ext binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: systemExtSpecificModule, }, }, inSanitizerDir: true, }, in: []string{"bin", "my_test"}, out: "target/product/test_device/data/asan/system_ext/bin/my_test", }, { name: "sanitized system native test binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, }, inData: true, inSanitizerDir: true, }, in: []string{"nativetest", "my_test"}, out: "target/product/test_device/data/asan/data/nativetest/my_test", }, { name: "sanitized vendor native test binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: socSpecificModule, }, }, inData: true, inSanitizerDir: true, }, in: []string{"nativetest", "my_test"}, out: "target/product/test_device/data/asan/data/nativetest/my_test", }, { name: "sanitized odm native test binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: deviceSpecificModule, }, }, inData: true, inSanitizerDir: true, }, in: []string{"nativetest", "my_test"}, out: "target/product/test_device/data/asan/data/nativetest/my_test", }, { name: "sanitized product native test binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: productSpecificModule, }, }, inData: true, inSanitizerDir: true, }, in: []string{"nativetest", "my_test"}, out: "target/product/test_device/data/asan/data/nativetest/my_test", }, { name: "sanitized system_ext native test binary", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, earlyModuleContext: earlyModuleContext{ kind: systemExtSpecificModule, }, }, inData: true, inSanitizerDir: true, }, in: []string{"nativetest", "my_test"}, out: "target/product/test_device/data/asan/data/nativetest/my_test", }, { name: "device testcases", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, }, inTestcases: true, }, in: []string{"my_test", "my_test_bin"}, out: "target/product/test_device/testcases/my_test/my_test_bin", }, { name: "host testcases", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: hostTarget.Os, target: hostTarget, }, inTestcases: true, }, in: []string{"my_test", "my_test_bin"}, out: "host/linux-x86/testcases/my_test/my_test_bin", }, { name: "forced host testcases", ctx: &moduleInstallPathContextImpl{ baseModuleContext: baseModuleContext{ os: deviceTarget.Os, target: deviceTarget, }, inTestcases: true, forceOS: &Linux, }, in: []string{"my_test", "my_test_bin"}, out: "host/linux-x86/testcases/my_test/my_test_bin", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { tc.ctx.baseModuleContext.config = testConfig output := PathForModuleInstall(tc.ctx, tc.in...) if output.basePath.path != tc.out { t.Errorf("unexpected path:\n got: %q\nwant: %q\n", output.basePath.path, tc.out) } }) } } func TestDirectorySortedPaths(t *testing.T) { config := TestConfig("out", nil, "", map[string][]byte{ "Android.bp": nil, "a.txt": nil, "a/txt": nil, "a/b/c": nil, "a/b/d": nil, "b": nil, "b/b.txt": nil, "a/a.txt": nil, }) ctx := PathContextForTesting(config) makePaths := func() Paths { return Paths{ PathForSource(ctx, "a.txt"), PathForSource(ctx, "a/txt"), PathForSource(ctx, "a/b/c"), PathForSource(ctx, "a/b/d"), PathForSource(ctx, "b"), PathForSource(ctx, "b/b.txt"), PathForSource(ctx, "a/a.txt"), } } expected := []string{ "a.txt", "a/a.txt", "a/b/c", "a/b/d", "a/txt", "b", "b/b.txt", } paths := makePaths() reversePaths := ReversePaths(paths) sortedPaths := PathsToDirectorySortedPaths(paths) reverseSortedPaths := PathsToDirectorySortedPaths(reversePaths) if !reflect.DeepEqual(Paths(sortedPaths).Strings(), expected) { t.Fatalf("sorted paths:\n %#v\n != \n %#v", paths.Strings(), expected) } if !reflect.DeepEqual(Paths(reverseSortedPaths).Strings(), expected) { t.Fatalf("sorted reversed paths:\n %#v\n !=\n %#v", reversePaths.Strings(), expected) } expectedA := []string{ "a/a.txt", "a/b/c", "a/b/d", "a/txt", } inA := sortedPaths.PathsInDirectory("a") if !reflect.DeepEqual(inA.Strings(), expectedA) { t.Errorf("FilesInDirectory(a):\n %#v\n != \n %#v", inA.Strings(), expectedA) } expectedA_B := []string{ "a/b/c", "a/b/d", } inA_B := sortedPaths.PathsInDirectory("a/b") if !reflect.DeepEqual(inA_B.Strings(), expectedA_B) { t.Errorf("FilesInDirectory(a/b):\n %#v\n != \n %#v", inA_B.Strings(), expectedA_B) } expectedB := []string{ "b/b.txt", } inB := sortedPaths.PathsInDirectory("b") if !reflect.DeepEqual(inB.Strings(), expectedB) { t.Errorf("FilesInDirectory(b):\n %#v\n != \n %#v", inA.Strings(), expectedA) } } func TestMaybeRel(t *testing.T) { testCases := []struct { name string base string target string out string isRel bool }{ { name: "normal", base: "a/b/c", target: "a/b/c/d", out: "d", isRel: true, }, { name: "parent", base: "a/b/c/d", target: "a/b/c", isRel: false, }, { name: "not relative", base: "a/b", target: "c/d", isRel: false, }, { name: "abs1", base: "/a", target: "a", isRel: false, }, { name: "abs2", base: "a", target: "/a", isRel: false, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { ctx := &configErrorWrapper{} out, isRel := MaybeRel(ctx, testCase.base, testCase.target) if len(ctx.errors) > 0 { t.Errorf("MaybeRel(..., %s, %s) reported unexpected errors %v", testCase.base, testCase.target, ctx.errors) } if isRel != testCase.isRel || out != testCase.out { t.Errorf("MaybeRel(..., %s, %s) want %v, %v got %v, %v", testCase.base, testCase.target, testCase.out, testCase.isRel, out, isRel) } }) } } func TestPathForSource(t *testing.T) { testCases := []struct { name string buildDir string src string err string }{ { name: "normal", buildDir: "out", src: "a/b/c", }, { name: "abs", buildDir: "out", src: "/a/b/c", err: "is outside directory", }, { name: "in out dir", buildDir: "out", src: "out/a/b/c", err: "is in output", }, } funcs := []struct { name string f func(ctx PathContext, pathComponents ...string) (SourcePath, error) }{ {"pathForSource", pathForSource}, {"safePathForSource", safePathForSource}, } for _, f := range funcs { t.Run(f.name, func(t *testing.T) { for _, test := range testCases { t.Run(test.name, func(t *testing.T) { testConfig := pathTestConfig(test.buildDir) ctx := &configErrorWrapper{config: testConfig} _, err := f.f(ctx, test.src) if len(ctx.errors) > 0 { t.Fatalf("unexpected errors %v", ctx.errors) } if err != nil { if test.err == "" { t.Fatalf("unexpected error %q", err.Error()) } else if !strings.Contains(err.Error(), test.err) { t.Fatalf("incorrect error, want substring %q got %q", test.err, err.Error()) } } else { if test.err != "" { t.Fatalf("missing error %q", test.err) } } }) } }) } } type pathForModuleSrcTestModule struct { ModuleBase props struct { Srcs []string `android:"path"` Exclude_srcs []string `android:"path"` Src *string `android:"path"` Module_handles_missing_deps bool } src string rel string srcs []string rels []string missingDeps []string } func pathForModuleSrcTestModuleFactory() Module { module := &pathForModuleSrcTestModule{} module.AddProperties(&module.props) InitAndroidModule(module) return module } func (p *pathForModuleSrcTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { var srcs Paths if p.props.Module_handles_missing_deps { srcs, p.missingDeps = PathsAndMissingDepsForModuleSrcExcludes(ctx, p.props.Srcs, p.props.Exclude_srcs) } else { srcs = PathsForModuleSrcExcludes(ctx, p.props.Srcs, p.props.Exclude_srcs) } p.srcs = srcs.Strings() for _, src := range srcs { p.rels = append(p.rels, src.Rel()) } if p.props.Src != nil { src := PathForModuleSrc(ctx, *p.props.Src) if src != nil { p.src = src.String() p.rel = src.Rel() } } if !p.props.Module_handles_missing_deps { p.missingDeps = ctx.GetMissingDependencies() } ctx.Build(pctx, BuildParams{ Rule: Touch, Output: PathForModuleOut(ctx, "output"), }) } type pathForModuleSrcOutputFileProviderModule struct { ModuleBase props struct { Outs []string Tagged []string } outs Paths tagged Paths } func pathForModuleSrcOutputFileProviderModuleFactory() Module { module := &pathForModuleSrcOutputFileProviderModule{} module.AddProperties(&module.props) InitAndroidModule(module) return module } func (p *pathForModuleSrcOutputFileProviderModule) GenerateAndroidBuildActions(ctx ModuleContext) { for _, out := range p.props.Outs { p.outs = append(p.outs, PathForModuleOut(ctx, out)) } for _, tagged := range p.props.Tagged { p.tagged = append(p.tagged, PathForModuleOut(ctx, tagged)) } } func (p *pathForModuleSrcOutputFileProviderModule) OutputFiles(tag string) (Paths, error) { switch tag { case "": return p.outs, nil case ".tagged": return p.tagged, nil default: return nil, fmt.Errorf("unsupported tag %q", tag) } } type pathForModuleSrcTestCase struct { name string bp string srcs []string rels []string src string rel string } func testPathForModuleSrc(t *testing.T, buildDir string, tests []pathForModuleSrcTestCase) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctx := NewTestContext() ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory) ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory) ctx.RegisterModuleType("filegroup", FileGroupFactory) fgBp := ` filegroup { name: "a", srcs: ["src/a"], } ` ofpBp := ` output_file_provider { name: "b", outs: ["gen/b"], tagged: ["gen/c"], } ` mockFS := map[string][]byte{ "fg/Android.bp": []byte(fgBp), "foo/Android.bp": []byte(test.bp), "ofp/Android.bp": []byte(ofpBp), "fg/src/a": nil, "foo/src/b": nil, "foo/src/c": nil, "foo/src/d": nil, "foo/src/e/e": nil, "foo/src_special/$": nil, } config := TestConfig(buildDir, nil, "", mockFS) ctx.Register(config) _, errs := ctx.ParseFileList(".", []string{"fg/Android.bp", "foo/Android.bp", "ofp/Android.bp"}) FailIfErrored(t, errs) _, errs = ctx.PrepareBuildActions(config) FailIfErrored(t, errs) m := ctx.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule) if g, w := m.srcs, test.srcs; !reflect.DeepEqual(g, w) { t.Errorf("want srcs %q, got %q", w, g) } if g, w := m.rels, test.rels; !reflect.DeepEqual(g, w) { t.Errorf("want rels %q, got %q", w, g) } if g, w := m.src, test.src; g != w { t.Errorf("want src %q, got %q", w, g) } if g, w := m.rel, test.rel; g != w { t.Errorf("want rel %q, got %q", w, g) } }) } } func TestPathsForModuleSrc(t *testing.T) { tests := []pathForModuleSrcTestCase{ { name: "path", bp: ` test { name: "foo", srcs: ["src/b"], }`, srcs: []string{"foo/src/b"}, rels: []string{"src/b"}, }, { name: "glob", bp: ` test { name: "foo", srcs: [ "src/*", "src/e/*", ], }`, srcs: []string{"foo/src/b", "foo/src/c", "foo/src/d", "foo/src/e/e"}, rels: []string{"src/b", "src/c", "src/d", "src/e/e"}, }, { name: "recursive glob", bp: ` test { name: "foo", srcs: ["src/**/*"], }`, srcs: []string{"foo/src/b", "foo/src/c", "foo/src/d", "foo/src/e/e"}, rels: []string{"src/b", "src/c", "src/d", "src/e/e"}, }, { name: "filegroup", bp: ` test { name: "foo", srcs: [":a"], }`, srcs: []string{"fg/src/a"}, rels: []string{"src/a"}, }, { name: "output file provider", bp: ` test { name: "foo", srcs: [":b"], }`, srcs: []string{buildDir + "/.intermediates/ofp/b/gen/b"}, rels: []string{"gen/b"}, }, { name: "output file provider tagged", bp: ` test { name: "foo", srcs: [":b{.tagged}"], }`, srcs: []string{buildDir + "/.intermediates/ofp/b/gen/c"}, rels: []string{"gen/c"}, }, { name: "output file provider with exclude", bp: ` test { name: "foo", srcs: [":b", ":c"], exclude_srcs: [":c"] } output_file_provider { name: "c", outs: ["gen/c"], }`, srcs: []string{buildDir + "/.intermediates/ofp/b/gen/b"}, rels: []string{"gen/b"}, }, { name: "special characters glob", bp: ` test { name: "foo", srcs: ["src_special/*"], }`, srcs: []string{"foo/src_special/$"}, rels: []string{"src_special/$"}, }, } testPathForModuleSrc(t, buildDir, tests) } func TestPathForModuleSrc(t *testing.T) { tests := []pathForModuleSrcTestCase{ { name: "path", bp: ` test { name: "foo", src: "src/b", }`, src: "foo/src/b", rel: "src/b", }, { name: "glob", bp: ` test { name: "foo", src: "src/e/*", }`, src: "foo/src/e/e", rel: "src/e/e", }, { name: "filegroup", bp: ` test { name: "foo", src: ":a", }`, src: "fg/src/a", rel: "src/a", }, { name: "output file provider", bp: ` test { name: "foo", src: ":b", }`, src: buildDir + "/.intermediates/ofp/b/gen/b", rel: "gen/b", }, { name: "output file provider tagged", bp: ` test { name: "foo", src: ":b{.tagged}", }`, src: buildDir + "/.intermediates/ofp/b/gen/c", rel: "gen/c", }, { name: "special characters glob", bp: ` test { name: "foo", src: "src_special/*", }`, src: "foo/src_special/$", rel: "src_special/$", }, } testPathForModuleSrc(t, buildDir, tests) } func TestPathsForModuleSrc_AllowMissingDependencies(t *testing.T) { bp := ` test { name: "foo", srcs: [":a"], exclude_srcs: [":b"], src: ":c", } test { name: "bar", srcs: [":d"], exclude_srcs: [":e"], module_handles_missing_deps: true, } ` config := TestConfig(buildDir, nil, bp, nil) config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true) ctx := NewTestContext() ctx.SetAllowMissingDependencies(true) ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory) ctx.Register(config) _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) FailIfErrored(t, errs) _, errs = ctx.PrepareBuildActions(config) FailIfErrored(t, errs) foo := ctx.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule) if g, w := foo.missingDeps, []string{"a", "b", "c"}; !reflect.DeepEqual(g, w) { t.Errorf("want foo missing deps %q, got %q", w, g) } if g, w := foo.srcs, []string{}; !reflect.DeepEqual(g, w) { t.Errorf("want foo srcs %q, got %q", w, g) } if g, w := foo.src, ""; g != w { t.Errorf("want foo src %q, got %q", w, g) } bar := ctx.ModuleForTests("bar", "").Module().(*pathForModuleSrcTestModule) if g, w := bar.missingDeps, []string{"d", "e"}; !reflect.DeepEqual(g, w) { t.Errorf("want bar missing deps %q, got %q", w, g) } if g, w := bar.srcs, []string{}; !reflect.DeepEqual(g, w) { t.Errorf("want bar srcs %q, got %q", w, g) } } func ExampleOutputPath_ReplaceExtension() { ctx := &configErrorWrapper{ config: TestConfig("out", nil, "", nil), } p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art") p2 := p.ReplaceExtension(ctx, "oat") fmt.Println(p, p2) fmt.Println(p.Rel(), p2.Rel()) // Output: // out/system/framework/boot.art out/system/framework/boot.oat // boot.art boot.oat } func ExampleOutputPath_FileInSameDir() { ctx := &configErrorWrapper{ config: TestConfig("out", nil, "", nil), } p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art") p2 := p.InSameDir(ctx, "oat", "arm", "boot.vdex") fmt.Println(p, p2) fmt.Println(p.Rel(), p2.Rel()) // Output: // out/system/framework/boot.art out/system/framework/oat/arm/boot.vdex // boot.art oat/arm/boot.vdex } func BenchmarkFirstUniquePaths(b *testing.B) { implementations := []struct { name string f func(Paths) Paths }{ { name: "list", f: firstUniquePathsList, }, { name: "map", f: firstUniquePathsMap, }, } const maxSize = 1024 uniquePaths := make(Paths, maxSize) for i := range uniquePaths { uniquePaths[i] = PathForTesting(strconv.Itoa(i)) } samePath := make(Paths, maxSize) for i := range samePath { samePath[i] = uniquePaths[0] } f := func(b *testing.B, imp func(Paths) Paths, paths Paths) { for i := 0; i < b.N; i++ { b.ReportAllocs() paths = append(Paths(nil), paths...) imp(paths) } } for n := 1; n <= maxSize; n <<= 1 { b.Run(strconv.Itoa(n), func(b *testing.B) { for _, implementation := range implementations { b.Run(implementation.name, func(b *testing.B) { b.Run("same", func(b *testing.B) { f(b, implementation.f, samePath[:n]) }) b.Run("unique", func(b *testing.B) { f(b, implementation.f, uniquePaths[:n]) }) }) } }) } }