Merge changes I406c5bef,Ibde685d7,I1c09412d,I9bec563c,I34f06abd into pi-dev
* changes:
Allow VNDK extensions under vendor or device
Add unit tests for android/neverallow.go
Extract failIfErrored() to android/testing.go
RemoveFromList() should remove all matches
Add unit tests for android/util.go
diff --git a/Android.bp b/Android.bp
index 1d2c516..00db8f2d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -71,6 +71,7 @@
"android/config_test.go",
"android/expand_test.go",
"android/namespace_test.go",
+ "android/neverallow_test.go",
"android/paths_test.go",
"android/prebuilt_test.go",
"android/util_test.go",
diff --git a/android/namespace_test.go b/android/namespace_test.go
index a6fc9d5..8bec0ad 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -628,7 +628,7 @@
func setupTest(t *testing.T, bps map[string]string) (ctx *TestContext) {
ctx, errs := setupTestExpectErrs(bps)
- failIfErrored(t, errs)
+ FailIfErrored(t, errs)
return ctx
}
@@ -692,12 +692,3 @@
InitAndroidModule(m)
return m
}
-
-func failIfErrored(t *testing.T, errs []error) {
- if len(errs) > 0 {
- for _, err := range errs {
- t.Error(err)
- }
- t.FailNow()
- }
-}
diff --git a/android/neverallow.go b/android/neverallow.go
index 261f2ee..8fba4b9 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -46,9 +46,15 @@
}
var neverallows = []*rule{
- neverallow().in("vendor", "device").with("vndk.enabled", "true").
+ neverallow().
+ in("vendor", "device").
+ with("vndk.enabled", "true").
+ without("vendor", "true").
because("the VNDK can never contain a library that is device dependent."),
- neverallow().with("vndk.enabled", "true").without("owner", "").
+ neverallow().
+ with("vndk.enabled", "true").
+ without("vendor", "true").
+ without("owner", "").
because("a VNDK module can never have an owner."),
neverallow().notIn("libcore").with("no_standard_libs", "true"),
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
new file mode 100644
index 0000000..a278365
--- /dev/null
+++ b/android/neverallow_test.go
@@ -0,0 +1,217 @@
+// Copyright 2018 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 (
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+var neverallowTests = []struct {
+ name string
+ fs map[string][]byte
+ expectedError string
+}{
+ {
+ name: "no vndk.enabled under vendor directory",
+ fs: map[string][]byte{
+ "vendor/Blueprints": []byte(`
+ cc_library {
+ name: "libvndk",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ }`),
+ },
+ expectedError: "VNDK can never contain a library that is device dependent",
+ },
+ {
+ name: "no vndk.enabled under device directory",
+ fs: map[string][]byte{
+ "device/Blueprints": []byte(`
+ cc_library {
+ name: "libvndk",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ }`),
+ },
+ expectedError: "VNDK can never contain a library that is device dependent",
+ },
+ {
+ name: "vndk-ext under vendor or device directory",
+ fs: map[string][]byte{
+ "device/Blueprints": []byte(`
+ cc_library {
+ name: "libvndk1_ext",
+ vendor: true,
+ vndk: {
+ enabled: true,
+ },
+ }`),
+ "vendor/Blueprints": []byte(`
+ cc_library {
+ name: "libvndk2_ext",
+ vendor: true,
+ vndk: {
+ enabled: true,
+ },
+ }`),
+ },
+ expectedError: "",
+ },
+
+ {
+ name: "no enforce_vintf_manifest.cflags",
+ fs: map[string][]byte{
+ "Blueprints": []byte(`
+ cc_library {
+ name: "libexample",
+ product_variables: {
+ enforce_vintf_manifest: {
+ cflags: ["-DSHOULD_NOT_EXIST"],
+ },
+ },
+ }`),
+ },
+ expectedError: "manifest enforcement should be independent",
+ },
+ {
+ name: "libhidltransport enforce_vintf_manifest.cflags",
+ fs: map[string][]byte{
+ "Blueprints": []byte(`
+ cc_library {
+ name: "libhidltransport",
+ product_variables: {
+ enforce_vintf_manifest: {
+ cflags: ["-DSHOULD_NOT_EXIST"],
+ },
+ },
+ }`),
+ },
+ expectedError: "",
+ },
+
+ {
+ name: "no treble_linker_namespaces.cflags",
+ fs: map[string][]byte{
+ "Blueprints": []byte(`
+ cc_library {
+ name: "libexample",
+ product_variables: {
+ treble_linker_namespaces: {
+ cflags: ["-DSHOULD_NOT_EXIST"],
+ },
+ },
+ }`),
+ },
+ expectedError: "nothing should care if linker namespaces are enabled or not",
+ },
+ {
+ name: "libc_bionic_ndk treble_linker_namespaces.cflags",
+ fs: map[string][]byte{
+ "Blueprints": []byte(`
+ cc_library {
+ name: "libc_bionic_ndk",
+ product_variables: {
+ treble_linker_namespaces: {
+ cflags: ["-DSHOULD_NOT_EXIST"],
+ },
+ },
+ }`),
+ },
+ expectedError: "",
+ },
+}
+
+func TestNeverallow(t *testing.T) {
+ buildDir, err := ioutil.TempDir("", "soong_neverallow_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(buildDir)
+
+ config := TestConfig(buildDir, nil)
+
+ for _, test := range neverallowTests {
+ t.Run(test.name, func(t *testing.T) {
+ _, errs := testNeverallow(t, config, test.fs)
+
+ if test.expectedError == "" {
+ FailIfErrored(t, errs)
+ } else {
+ FailIfNoMatchingErrors(t, test.expectedError, errs)
+ }
+ })
+ }
+}
+
+func testNeverallow(t *testing.T, config Config, fs map[string][]byte) (*TestContext, []error) {
+ ctx := NewTestContext()
+ ctx.RegisterModuleType("cc_library", ModuleFactoryAdaptor(newMockCcLibraryModule))
+ ctx.PostDepsMutators(registerNeverallowMutator)
+ ctx.Register()
+
+ ctx.MockFileSystem(fs)
+
+ _, errs := ctx.ParseBlueprintsFiles("Blueprints")
+ if len(errs) > 0 {
+ return ctx, errs
+ }
+
+ _, errs = ctx.PrepareBuildActions(config)
+ return ctx, errs
+}
+
+type mockProperties struct {
+ Vendor_available *bool
+
+ Vndk struct {
+ Enabled *bool
+ Support_system_process *bool
+ Extends *string
+ }
+
+ Product_variables struct {
+ Enforce_vintf_manifest struct {
+ Cflags []string
+ }
+
+ Treble_linker_namespaces struct {
+ Cflags []string
+ }
+ }
+}
+
+type mockCcLibraryModule struct {
+ ModuleBase
+ properties mockProperties
+}
+
+func newMockCcLibraryModule() Module {
+ m := &mockCcLibraryModule{}
+ m.AddProperties(&m.properties)
+ InitAndroidModule(m)
+ return m
+}
+
+func (p *mockCcLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
+}
+
+func (p *mockCcLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
+}
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 93f5805..69ce16a 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -138,9 +138,9 @@
})
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
- fail(t, errs)
+ FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
- fail(t, errs)
+ FailIfErrored(t, errs)
foo := ctx.ModuleForTests("foo", "")
@@ -231,12 +231,3 @@
func (s *sourceModule) GenerateAndroidBuildActions(ctx ModuleContext) {
}
-
-func fail(t *testing.T, errs []error) {
- if len(errs) > 0 {
- for _, err := range errs {
- t.Error(err)
- }
- t.FailNow()
- }
-}
diff --git a/android/testing.go b/android/testing.go
index ae012b0..f5d33e1 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -17,7 +17,9 @@
import (
"fmt"
"path/filepath"
+ "regexp"
"strings"
+ "testing"
"github.com/google/blueprint"
)
@@ -152,3 +154,36 @@
panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v",
file, searchedOutputs))
}
+
+func FailIfErrored(t *testing.T, errs []error) {
+ t.Helper()
+ if len(errs) > 0 {
+ for _, err := range errs {
+ t.Error(err)
+ }
+ t.FailNow()
+ }
+}
+
+func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) {
+ t.Helper()
+
+ matcher, err := regexp.Compile(pattern)
+ if err != nil {
+ t.Errorf("failed to compile regular expression %q because %s", pattern, err)
+ }
+
+ found := false
+ for _, err := range errs {
+ if matcher.FindStringIndex(err.Error()) != nil {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("missing the expected error %q (checked %d error(s))", pattern, len(errs))
+ for i, err := range errs {
+ t.Errorf("errs[%d] = %s", i, err)
+ }
+ }
+}
diff --git a/android/util.go b/android/util.go
index 854d782..e9b9764 100644
--- a/android/util.go
+++ b/android/util.go
@@ -101,11 +101,18 @@
func RemoveFromList(s string, list []string) (bool, []string) {
i := IndexList(s, list)
- if i != -1 {
- return true, append(list[:i], list[i+1:]...)
- } else {
+ if i == -1 {
return false, list
}
+
+ result := make([]string, 0, len(list)-1)
+ result = append(result, list[:i]...)
+ for _, l := range list[i+1:] {
+ if l != s {
+ result = append(result, l)
+ }
+ }
+ return true, result
}
// FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
diff --git a/android/util_test.go b/android/util_test.go
index 32f92b4..1c791b2 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -118,3 +118,244 @@
}
}
}
+
+func TestJoinWithPrefix(t *testing.T) {
+ testcases := []struct {
+ name string
+ input []string
+ expected string
+ }{
+ {
+ name: "zero_inputs",
+ input: []string{},
+ expected: "",
+ },
+ {
+ name: "one_input",
+ input: []string{"a"},
+ expected: "prefix:a",
+ },
+ {
+ name: "two_inputs",
+ input: []string{"a", "b"},
+ expected: "prefix:a prefix:b",
+ },
+ }
+
+ prefix := "prefix:"
+
+ for _, testCase := range testcases {
+ t.Run(testCase.name, func(t *testing.T) {
+ out := JoinWithPrefix(testCase.input, prefix)
+ if out != testCase.expected {
+ t.Errorf("incorrect output:")
+ t.Errorf(" input: %#v", testCase.input)
+ t.Errorf(" prefix: %#v", prefix)
+ t.Errorf(" expected: %#v", testCase.expected)
+ t.Errorf(" got: %#v", out)
+ }
+ })
+ }
+}
+
+func TestIndexList(t *testing.T) {
+ input := []string{"a", "b", "c"}
+
+ testcases := []struct {
+ key string
+ expected int
+ }{
+ {
+ key: "a",
+ expected: 0,
+ },
+ {
+ key: "b",
+ expected: 1,
+ },
+ {
+ key: "c",
+ expected: 2,
+ },
+ {
+ key: "X",
+ expected: -1,
+ },
+ }
+
+ for _, testCase := range testcases {
+ t.Run(testCase.key, func(t *testing.T) {
+ out := IndexList(testCase.key, input)
+ if out != testCase.expected {
+ t.Errorf("incorrect output:")
+ t.Errorf(" key: %#v", testCase.key)
+ t.Errorf(" input: %#v", input)
+ t.Errorf(" expected: %#v", testCase.expected)
+ t.Errorf(" got: %#v", out)
+ }
+ })
+ }
+}
+
+func TestInList(t *testing.T) {
+ input := []string{"a"}
+
+ testcases := []struct {
+ key string
+ expected bool
+ }{
+ {
+ key: "a",
+ expected: true,
+ },
+ {
+ key: "X",
+ expected: false,
+ },
+ }
+
+ for _, testCase := range testcases {
+ t.Run(testCase.key, func(t *testing.T) {
+ out := InList(testCase.key, input)
+ if out != testCase.expected {
+ t.Errorf("incorrect output:")
+ t.Errorf(" key: %#v", testCase.key)
+ t.Errorf(" input: %#v", input)
+ t.Errorf(" expected: %#v", testCase.expected)
+ t.Errorf(" got: %#v", out)
+ }
+ })
+ }
+}
+
+func TestPrefixInList(t *testing.T) {
+ prefixes := []string{"a", "b"}
+
+ testcases := []struct {
+ str string
+ expected bool
+ }{
+ {
+ str: "a-example",
+ expected: true,
+ },
+ {
+ str: "b-example",
+ expected: true,
+ },
+ {
+ str: "X-example",
+ expected: false,
+ },
+ }
+
+ for _, testCase := range testcases {
+ t.Run(testCase.str, func(t *testing.T) {
+ out := PrefixInList(testCase.str, prefixes)
+ if out != testCase.expected {
+ t.Errorf("incorrect output:")
+ t.Errorf(" str: %#v", testCase.str)
+ t.Errorf(" prefixes: %#v", prefixes)
+ t.Errorf(" expected: %#v", testCase.expected)
+ t.Errorf(" got: %#v", out)
+ }
+ })
+ }
+}
+
+func TestFilterList(t *testing.T) {
+ input := []string{"a", "b", "c", "c", "b", "d", "a"}
+ filter := []string{"a", "c"}
+ remainder, filtered := FilterList(input, filter)
+
+ expected := []string{"b", "b", "d"}
+ if !reflect.DeepEqual(remainder, expected) {
+ t.Errorf("incorrect remainder output:")
+ t.Errorf(" input: %#v", input)
+ t.Errorf(" filter: %#v", filter)
+ t.Errorf(" expected: %#v", expected)
+ t.Errorf(" got: %#v", remainder)
+ }
+
+ expected = []string{"a", "c", "c", "a"}
+ if !reflect.DeepEqual(filtered, expected) {
+ t.Errorf("incorrect filtered output:")
+ t.Errorf(" input: %#v", input)
+ t.Errorf(" filter: %#v", filter)
+ t.Errorf(" expected: %#v", expected)
+ t.Errorf(" got: %#v", filtered)
+ }
+}
+
+func TestRemoveListFromList(t *testing.T) {
+ input := []string{"a", "b", "c", "d", "a", "c", "d"}
+ filter := []string{"a", "c"}
+ expected := []string{"b", "d", "d"}
+ out := RemoveListFromList(input, filter)
+ if !reflect.DeepEqual(out, expected) {
+ t.Errorf("incorrect output:")
+ t.Errorf(" input: %#v", input)
+ t.Errorf(" filter: %#v", filter)
+ t.Errorf(" expected: %#v", expected)
+ t.Errorf(" got: %#v", out)
+ }
+}
+
+func TestRemoveFromList(t *testing.T) {
+ testcases := []struct {
+ name string
+ key string
+ input []string
+ expectedFound bool
+ expectedOut []string
+ }{
+ {
+ name: "remove_one_match",
+ key: "a",
+ input: []string{"a", "b", "c"},
+ expectedFound: true,
+ expectedOut: []string{"b", "c"},
+ },
+ {
+ name: "remove_three_matches",
+ key: "a",
+ input: []string{"a", "b", "a", "c", "a"},
+ expectedFound: true,
+ expectedOut: []string{"b", "c"},
+ },
+ {
+ name: "remove_zero_matches",
+ key: "X",
+ input: []string{"a", "b", "a", "c", "a"},
+ expectedFound: false,
+ expectedOut: []string{"a", "b", "a", "c", "a"},
+ },
+ {
+ name: "remove_all_matches",
+ key: "a",
+ input: []string{"a", "a", "a", "a"},
+ expectedFound: true,
+ expectedOut: []string{},
+ },
+ }
+
+ for _, testCase := range testcases {
+ t.Run(testCase.name, func(t *testing.T) {
+ found, out := RemoveFromList(testCase.key, testCase.input)
+ if found != testCase.expectedFound {
+ t.Errorf("incorrect output:")
+ t.Errorf(" key: %#v", testCase.key)
+ t.Errorf(" input: %#v", testCase.input)
+ t.Errorf(" expected: %#v", testCase.expectedFound)
+ t.Errorf(" got: %#v", found)
+ }
+ if !reflect.DeepEqual(out, testCase.expectedOut) {
+ t.Errorf("incorrect output:")
+ t.Errorf(" key: %#v", testCase.key)
+ t.Errorf(" input: %#v", testCase.input)
+ t.Errorf(" expected: %#v", testCase.expectedOut)
+ t.Errorf(" got: %#v", out)
+ }
+ })
+ }
+}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 19e4703..2efee59 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -22,7 +22,6 @@
"io/ioutil"
"os"
"reflect"
- "regexp"
"sort"
"strings"
"testing"
@@ -147,9 +146,9 @@
ctx := createTestContext(t, config, bp)
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- failIfErrored(t, errs)
+ android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
- failIfErrored(t, errs)
+ android.FailIfErrored(t, errs)
return ctx
}
@@ -178,13 +177,13 @@
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
if len(errs) > 0 {
- failIfNoMatchingErrors(t, pattern, errs)
+ android.FailIfNoMatchingErrors(t, pattern, errs)
return
}
_, errs = ctx.PrepareBuildActions(config)
if len(errs) > 0 {
- failIfNoMatchingErrors(t, pattern, errs)
+ android.FailIfNoMatchingErrors(t, pattern, errs)
return
}
@@ -1064,38 +1063,6 @@
}
}
-func failIfErrored(t *testing.T, errs []error) {
- if len(errs) > 0 {
- for _, err := range errs {
- t.Error(err)
- }
- t.FailNow()
- }
-}
-
-func failIfNoMatchingErrors(t *testing.T, pattern string, errs []error) {
- matcher, err := regexp.Compile(pattern)
- if err != nil {
- t.Errorf("failed to compile regular expression %q because %s", pattern, err)
- }
-
- found := false
-
- for _, err := range errs {
- if matcher.FindStringIndex(err.Error()) != nil {
- found = true
- break
- }
- }
-
- if !found {
- t.Errorf("missing the expected error %q (checked %d error(s))", pattern, len(errs))
- for i, err := range errs {
- t.Errorf("errs[%d] = %s", i, err)
- }
- }
-}
-
func getOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
for _, moduleName := range moduleNames {
module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
diff --git a/cc/test_data_test.go b/cc/test_data_test.go
index 434edcd..4a7b0f7 100644
--- a/cc/test_data_test.go
+++ b/cc/test_data_test.go
@@ -135,9 +135,9 @@
ctx.Register()
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
- fail(t, errs)
+ android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
- fail(t, errs)
+ android.FailIfErrored(t, errs)
foo := ctx.ModuleForTests("foo", "")
@@ -186,12 +186,3 @@
func (test *testDataTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
test.data = ctx.ExpandSources(test.Properties.Data, nil)
}
-
-func fail(t *testing.T, errs []error) {
- if len(errs) > 0 {
- for _, err := range errs {
- t.Error(err)
- }
- t.FailNow()
- }
-}
diff --git a/java/java_test.go b/java/java_test.go
index 5d6a6e0..6ef406f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -190,9 +190,9 @@
func run(t *testing.T, ctx *android.TestContext, config android.Config) {
t.Helper()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- fail(t, errs)
+ android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
- fail(t, errs)
+ android.FailIfErrored(t, errs)
}
func testJava(t *testing.T, bp string) *android.TestContext {
@@ -977,13 +977,3 @@
t.Errorf(`foo inputs %v != ["java-fg/c.java"]`, javac.Inputs)
}
}
-
-func fail(t *testing.T, errs []error) {
- t.Helper()
- if len(errs) > 0 {
- for _, err := range errs {
- t.Error(err)
- }
- t.FailNow()
- }
-}
diff --git a/python/python_test.go b/python/python_test.go
index 176c6ec..ad80167 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -347,7 +347,7 @@
ctx.Register()
ctx.MockFileSystem(d.mockFiles)
_, testErrs := ctx.ParseBlueprintsFiles(bpFile)
- fail(t, testErrs)
+ android.FailIfErrored(t, testErrs)
_, actErrs := ctx.PrepareBuildActions(config)
if len(actErrs) > 0 {
testErrs = append(testErrs, expectErrors(t, actErrs, d.errors)...)
@@ -360,7 +360,7 @@
e.parSpec, e.depsParSpecs)...)
}
}
- fail(t, testErrs)
+ android.FailIfErrored(t, testErrs)
})
}
}
@@ -458,12 +458,3 @@
func tearDownBuildEnv(buildDir string) {
os.RemoveAll(buildDir)
}
-
-func fail(t *testing.T, errs []error) {
- if len(errs) > 0 {
- for _, err := range errs {
- t.Error(err)
- }
- t.FailNow()
- }
-}