// 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 ( "testing" "github.com/google/blueprint" ) var neverallowTests = []struct { // The name of the test. name string // Optional test specific rules. If specified then they are used instead of the default rules. rules []Rule // Additional contents to add to the virtual filesystem used by the tests. fs map[string][]byte // The expected error patterns. If empty then no errors are expected, otherwise each error // reported must be matched by at least one of these patterns. A pattern matches if the error // message contains the pattern. A pattern does not have to match the whole error message. expectedErrors []string }{ // Test General Functionality // in direct deps tests { name: "not_allowed_in_direct_deps", rules: []Rule{ NeverAllow().InDirectDeps("not_allowed_in_direct_deps"), }, fs: map[string][]byte{ "top/Android.bp": []byte(` cc_library { name: "not_allowed_in_direct_deps", }`), "other/Android.bp": []byte(` cc_library { name: "libother", static_libs: ["not_allowed_in_direct_deps"], }`), }, expectedErrors: []string{ `module "libother": violates neverallow deps:not_allowed_in_direct_deps`, }, }, // Test android specific rules // include_dir rule tests { name: "include_dir not allowed to reference art", fs: map[string][]byte{ "other/Android.bp": []byte(` cc_library { name: "libother", include_dirs: ["art/libdexfile/include"], }`), }, expectedErrors: []string{ "all usages of 'art' have been migrated", }, }, { name: "include_dir can reference another location", fs: map[string][]byte{ "other/Android.bp": []byte(` cc_library { name: "libother", include_dirs: ["another/include"], }`), }, }, // Treble rule tests { name: "no vndk.enabled under vendor directory", fs: map[string][]byte{ "vendor/Android.bp": []byte(` cc_library { name: "libvndk", vendor_available: true, vndk: { enabled: true, }, }`), }, expectedErrors: []string{ "VNDK can never contain a library that is device dependent", }, }, { name: "no vndk.enabled under device directory", fs: map[string][]byte{ "device/Android.bp": []byte(` cc_library { name: "libvndk", vendor_available: true, vndk: { enabled: true, }, }`), }, expectedErrors: []string{ "VNDK can never contain a library that is device dependent", }, }, { name: "vndk-ext under vendor or device directory", fs: map[string][]byte{ "device/Android.bp": []byte(` cc_library { name: "libvndk1_ext", vendor: true, vndk: { enabled: true, }, }`), "vendor/Android.bp": []byte(` cc_library { name: "libvndk2_ext", vendor: true, vndk: { enabled: true, }, }`), }, }, { name: "no enforce_vintf_manifest.cflags", fs: map[string][]byte{ "Android.bp": []byte(` cc_library { name: "libexample", product_variables: { enforce_vintf_manifest: { cflags: ["-DSHOULD_NOT_EXIST"], }, }, }`), }, expectedErrors: []string{ "manifest enforcement should be independent", }, }, { name: "no treble_linker_namespaces.cflags", fs: map[string][]byte{ "Android.bp": []byte(` cc_library { name: "libexample", product_variables: { treble_linker_namespaces: { cflags: ["-DSHOULD_NOT_EXIST"], }, }, }`), }, expectedErrors: []string{ "nothing should care if linker namespaces are enabled or not", }, }, { name: "libc_bionic_ndk treble_linker_namespaces.cflags", fs: map[string][]byte{ "Android.bp": []byte(` cc_library { name: "libc_bionic_ndk", product_variables: { treble_linker_namespaces: { cflags: ["-DSHOULD_NOT_EXIST"], }, }, }`), }, }, { name: "java_device_for_host", fs: map[string][]byte{ "Android.bp": []byte(` java_device_for_host { name: "device_for_host", libs: ["core-libart"], }`), }, expectedErrors: []string{ "java_device_for_host can only be used in allowed projects", }, }, // CC sdk rule tests { name: `"sdk_variant_only" outside allowed list`, fs: map[string][]byte{ "Android.bp": []byte(` cc_library { name: "outside_allowed_list", sdk_version: "current", sdk_variant_only: true, }`), }, expectedErrors: []string{ `module "outside_allowed_list": violates neverallow`, }, }, { name: `"sdk_variant_only: false" outside allowed list`, fs: map[string][]byte{ "Android.bp": []byte(` cc_library { name: "outside_allowed_list", sdk_version: "current", sdk_variant_only: false, }`), }, expectedErrors: []string{ `module "outside_allowed_list": violates neverallow`, }, }, { name: `"platform" outside allowed list`, fs: map[string][]byte{ "Android.bp": []byte(` cc_library { name: "outside_allowed_list", platform: { shared_libs: ["libfoo"], }, }`), }, expectedErrors: []string{ `module "outside_allowed_list": violates neverallow`, }, }, { name: "uncompress_dex inside art", fs: map[string][]byte{ "art/Android.bp": []byte(` java_library { name: "inside_art_libraries", uncompress_dex: true, }`), }, }, { name: "uncompress_dex outside art", fs: map[string][]byte{ "other/Android.bp": []byte(` java_library { name: "outside_art_libraries", uncompress_dex: true, }`), }, expectedErrors: []string{ "module \"outside_art_libraries\": violates neverallow", }, }, { name: "disallowed makefile_goal", fs: map[string][]byte{ "Android.bp": []byte(` makefile_goal { name: "foo", product_out_path: "boot/trap.img" } `), }, expectedErrors: []string{ "Only boot images may be imported as a makefile goal.", }, }, } func TestNeverallow(t *testing.T) { for _, test := range neverallowTests { // Create a test per config to allow for test specific config, e.g. test rules. config := TestConfig(buildDir, nil, "", test.fs) t.Run(test.name, func(t *testing.T) { // If the test has its own rules then use them instead of the default ones. if test.rules != nil { SetTestNeverallowRules(config, test.rules) } _, errs := testNeverallow(config) CheckErrorsAgainstExpectations(t, errs, test.expectedErrors) }) } } func testNeverallow(config Config) (*TestContext, []error) { ctx := NewTestContext(config) ctx.RegisterModuleType("cc_library", newMockCcLibraryModule) ctx.RegisterModuleType("java_library", newMockJavaLibraryModule) ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule) ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule) ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule) ctx.PostDepsMutators(RegisterNeverallowMutator) ctx.Register() _, errs := ctx.ParseBlueprintsFiles("Android.bp") if len(errs) > 0 { return ctx, errs } _, errs = ctx.PrepareBuildActions(config) return ctx, errs } type mockCcLibraryProperties struct { Include_dirs []string Vendor_available *bool Static_libs []string Sdk_version *string Sdk_variant_only *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 } } Platform struct { Shared_libs []string } } type mockCcLibraryModule struct { ModuleBase properties mockCcLibraryProperties } func newMockCcLibraryModule() Module { m := &mockCcLibraryModule{} m.AddProperties(&m.properties) InitAndroidModule(m) return m } type neverallowTestDependencyTag struct { blueprint.BaseDependencyTag name string } var staticDepTag = neverallowTestDependencyTag{name: "static"} func (c *mockCcLibraryModule) DepsMutator(ctx BottomUpMutatorContext) { for _, lib := range c.properties.Static_libs { ctx.AddDependency(ctx.Module(), staticDepTag, lib) } } func (p *mockCcLibraryModule) GenerateAndroidBuildActions(ModuleContext) { } type mockJavaLibraryProperties struct { Libs []string Sdk_version *string Uncompress_dex *bool } type mockJavaLibraryModule struct { ModuleBase properties mockJavaLibraryProperties } func newMockJavaLibraryModule() Module { m := &mockJavaLibraryModule{} m.AddProperties(&m.properties) InitAndroidModule(m) return m } func (p *mockJavaLibraryModule) GenerateAndroidBuildActions(ModuleContext) { } type mockMakefileGoalProperties struct { Product_out_path *string } type mockMakefileGoalModule struct { ModuleBase properties mockMakefileGoalProperties } func newMockMakefileGoalModule() Module { m := &mockMakefileGoalModule{} m.AddProperties(&m.properties) InitAndroidModule(m) return m } func (p *mockMakefileGoalModule) GenerateAndroidBuildActions(ModuleContext) { }