|  | // 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" | 
|  | "strings" | 
|  | "testing" | 
|  |  | 
|  | "github.com/google/blueprint/pathtools" | 
|  | ) | 
|  |  | 
|  | 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 { | 
|  | ctx := &configErrorWrapper{} | 
|  | out := validateSafePath(ctx, testCase.in...) | 
|  | check(t, "validateSafePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestValidatePath(t *testing.T) { | 
|  | for _, testCase := range validatePathTestCases { | 
|  | ctx := &configErrorWrapper{} | 
|  | out := validatePath(ctx, testCase.in...) | 
|  | 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) { | 
|  | 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) { | 
|  |  | 
|  | printedTestCase := false | 
|  | e := func(s string, expected, got interface{}) { | 
|  | 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 { | 
|  | androidBaseContextImpl | 
|  |  | 
|  | inData         bool | 
|  | inSanitizerDir bool | 
|  | } | 
|  |  | 
|  | func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem { | 
|  | return pathtools.MockFs(nil) | 
|  | } | 
|  |  | 
|  | func (m moduleInstallPathContextImpl) Config() interface{} { | 
|  | return m.androidBaseContextImpl.config | 
|  | } | 
|  |  | 
|  | func (moduleInstallPathContextImpl) AddNinjaFileDeps(deps ...string) {} | 
|  |  | 
|  | func (m moduleInstallPathContextImpl) InstallInData() bool { | 
|  | return m.inData | 
|  | } | 
|  |  | 
|  | func (m moduleInstallPathContextImpl) InstallInSanitizerDir() bool { | 
|  | return m.inSanitizerDir | 
|  | } | 
|  |  | 
|  | func TestPathForModuleInstall(t *testing.T) { | 
|  | testConfig := TestConfig("") | 
|  |  | 
|  | hostTarget := Target{Os: Linux} | 
|  | deviceTarget := Target{Os: Android} | 
|  |  | 
|  | testCases := []struct { | 
|  | name string | 
|  | ctx  *moduleInstallPathContextImpl | 
|  | in   []string | 
|  | out  string | 
|  | }{ | 
|  | { | 
|  | name: "host binary", | 
|  | ctx: &moduleInstallPathContextImpl{ | 
|  | androidBaseContextImpl: androidBaseContextImpl{ | 
|  | target: hostTarget, | 
|  | }, | 
|  | }, | 
|  | in:  []string{"bin", "my_test"}, | 
|  | out: "host/linux-x86/bin/my_test", | 
|  | }, | 
|  |  | 
|  | { | 
|  | name: "system binary", | 
|  | ctx: &moduleInstallPathContextImpl{ | 
|  | androidBaseContextImpl: androidBaseContextImpl{ | 
|  | target: deviceTarget, | 
|  | }, | 
|  | }, | 
|  | in:  []string{"bin", "my_test"}, | 
|  | out: "target/product/test_device/system/bin/my_test", | 
|  | }, | 
|  | { | 
|  | name: "vendor binary", | 
|  | ctx: &moduleInstallPathContextImpl{ | 
|  | androidBaseContextImpl: androidBaseContextImpl{ | 
|  | target: deviceTarget, | 
|  | vendor: true, | 
|  | }, | 
|  | }, | 
|  | in:  []string{"bin", "my_test"}, | 
|  | out: "target/product/test_device/vendor/bin/my_test", | 
|  | }, | 
|  |  | 
|  | { | 
|  | name: "system native test binary", | 
|  | ctx: &moduleInstallPathContextImpl{ | 
|  | androidBaseContextImpl: androidBaseContextImpl{ | 
|  | 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{ | 
|  | androidBaseContextImpl: androidBaseContextImpl{ | 
|  | target: deviceTarget, | 
|  | vendor: true, | 
|  | }, | 
|  | inData: true, | 
|  | }, | 
|  | in:  []string{"nativetest", "my_test"}, | 
|  | out: "target/product/test_device/data/nativetest/my_test", | 
|  | }, | 
|  |  | 
|  | { | 
|  | name: "sanitized system binary", | 
|  | ctx: &moduleInstallPathContextImpl{ | 
|  | androidBaseContextImpl: androidBaseContextImpl{ | 
|  | 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{ | 
|  | androidBaseContextImpl: androidBaseContextImpl{ | 
|  | target: deviceTarget, | 
|  | vendor: true, | 
|  | }, | 
|  | inSanitizerDir: true, | 
|  | }, | 
|  | in:  []string{"bin", "my_test"}, | 
|  | out: "target/product/test_device/data/asan/vendor/bin/my_test", | 
|  | }, | 
|  |  | 
|  | { | 
|  | name: "sanitized system native test binary", | 
|  | ctx: &moduleInstallPathContextImpl{ | 
|  | androidBaseContextImpl: androidBaseContextImpl{ | 
|  | 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{ | 
|  | androidBaseContextImpl: androidBaseContextImpl{ | 
|  | target: deviceTarget, | 
|  | vendor: true, | 
|  | }, | 
|  | inData:         true, | 
|  | inSanitizerDir: true, | 
|  | }, | 
|  | in:  []string{"nativetest", "my_test"}, | 
|  | out: "target/product/test_device/data/asan/data/nativetest/my_test", | 
|  | }, | 
|  | } | 
|  |  | 
|  | for _, tc := range testCases { | 
|  | t.Run(tc.name, func(t *testing.T) { | 
|  | tc.ctx.androidBaseContextImpl.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) | 
|  | } | 
|  | }) | 
|  | } | 
|  | } |