// 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 ( "regexp" "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 MockFS // 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{ regexp.QuoteMeta("module \"libother\": violates neverallow requirements. Not allowed:\n\tdep(s): [\"not_allowed_in_direct_deps\"]"), }, }, { name: "multiple constraints", rules: []Rule{ NeverAllow(). InDirectDeps("not_allowed_in_direct_deps"). In("other"). ModuleType("cc_library"). NotIn("top"). NotModuleType("cc_binary"), }, 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{ regexp.QuoteMeta(`module "libother": violates neverallow requirements. Not allowed: in dirs: ["other/"] module types: ["cc_library"] dep(s): ["not_allowed_in_direct_deps"] EXCEPT in dirs: ["top/"] EXCEPT module types: ["cc_binary"]`), }, }, // 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 not allowed to reference art", fs: map[string][]byte{ "system/libfmq/Android.bp": []byte(` cc_library { name: "libother", include_dirs: ["any/random/file"], }`), }, expectedErrors: []string{ "all usages of them in 'system/libfmq' have been migrated", }, }, { name: "include_dir can work", 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", }, }, // Tests for the rule prohibiting the use of framework { name: "prohibit framework", fs: map[string][]byte{ "Android.bp": []byte(` java_library { name: "foo", libs: ["framework"], sdk_version: "current", }`), }, expectedErrors: []string{ "framework can't be used when building against SDK", }, }, // Test for the rule restricting use of implementation_installable { name: `"implementation_installable" outside allowed list`, fs: map[string][]byte{ "Android.bp": []byte(` cc_library { name: "outside_allowed_list", stubs: { implementation_installable: true, }, }`), }, expectedErrors: []string{ `module "outside_allowed_list": violates neverallow`, }, }, // Test for the rule restricting use of exclude_static_libs { name: `"exclude_static_libs" outside allowed directory`, fs: map[string][]byte{ "a/b/Android.bp": []byte(` java_library { name: "baz", exclude_static_libs: [ "bar", ], } `), }, expectedErrors: []string{ `exclude_static_libs property is only allowed for java modules defined in build/soong, libcore, and frameworks/base/api`, }, }, // Test for only allowing headers_only for framework-minus-apex-headers { name: `"headers_only" outside framework-minus-apex-headers modules`, fs: map[string][]byte{ "a/b/Android.bp": []byte(` java_library { name: "baz", headers_only: true, } `), }, expectedErrors: []string{ `headers_only can only be used for generating framework-minus-apex headers for non-updatable modules`, }, }, } var prepareForNeverAllowTest = GroupFixturePreparers( FixtureRegisterWithContext(func(ctx RegistrationContext) { ctx.RegisterModuleType("cc_library", newMockCcLibraryModule) ctx.RegisterModuleType("java_library", newMockJavaLibraryModule) ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule) ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule) }), ) func TestNeverallow(t *testing.T) { for _, test := range neverallowTests { t.Run(test.name, func(t *testing.T) { GroupFixturePreparers( prepareForNeverAllowTest, PrepareForTestWithNeverallowRules(test.rules), test.fs.AddToFixture(), ). ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)). RunTest(t) }) } } 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 } Stubs struct { Implementation_installable *bool } } 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 Exclude_static_libs []string Headers_only *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) { }