| // 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) { |
| } |