|  | // 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 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`, | 
|  | }, | 
|  | }, | 
|  | // Test for the rule restricting use of is_auto_generated | 
|  | { | 
|  | name: `"is_auto_generated" outside allowed directory`, | 
|  | fs: map[string][]byte{ | 
|  | "a/b/Android.bp": []byte(` | 
|  | filesystem { | 
|  | name: "baaz", | 
|  | is_auto_generated: true, | 
|  | } | 
|  | `), | 
|  | }, | 
|  | expectedErrors: []string{ | 
|  | `is_auto_generated property is only allowed for filesystem modules in build/soong/fsgen directory`, | 
|  | }, | 
|  | }, | 
|  | // Test for the rule restricting use of prebuilt_* module | 
|  | { | 
|  | name: `"prebuilt_usr_srec" defined in Android.bp file`, | 
|  | fs: map[string][]byte{ | 
|  | "a/b/Android.bp": []byte(` | 
|  | prebuilt_usr_srec { | 
|  | name: "foo", | 
|  | } | 
|  | `), | 
|  | }, | 
|  | expectedErrors: []string{ | 
|  | `module type not allowed to be defined in bp file`, | 
|  | }, | 
|  | }, | 
|  | } | 
|  |  | 
|  | 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) | 
|  | ctx.RegisterModuleType("filesystem", newMockFilesystemModule) | 
|  | ctx.RegisterModuleType("prebuilt_usr_srec", newMockPrebuiltUsrSrecModule) | 
|  | }), | 
|  | ) | 
|  |  | 
|  | 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) { | 
|  | } | 
|  |  | 
|  | type mockPrebuiltUsrSrecModule struct { | 
|  | ModuleBase | 
|  | } | 
|  |  | 
|  | func (p *mockPrebuiltUsrSrecModule) GenerateAndroidBuildActions(ModuleContext) { | 
|  | } | 
|  |  | 
|  | func newMockPrebuiltUsrSrecModule() Module { | 
|  | m := &mockPrebuiltUsrSrecModule{} | 
|  | InitAndroidModule(m) | 
|  | return m | 
|  | } |