// Copyright (C) 2019 The Android Open Source Project // // 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 aidl import ( "fmt" "os" "path/filepath" "strings" "testing" "github.com/google/blueprint" "github.com/google/blueprint/proptools" "android/soong/android" "android/soong/cc" "android/soong/genrule" "android/soong/java" "android/soong/rust" ) func TestMain(m *testing.M) { os.Exit(m.Run()) } func withFiles(files map[string][]byte) android.FixturePreparer { return android.FixtureMergeMockFs(files) } func intPtr(v int) *int { return &v } func setReleaseEnv() android.FixturePreparer { return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { // Q is finalized as 29. No codename that is actively being developed. variables.Platform_sdk_version = intPtr(29) variables.Platform_sdk_codename = proptools.StringPtr("REL") variables.Platform_sdk_final = proptools.BoolPtr(true) variables.Platform_version_active_codenames = []string{} }) } func setTestFreezeEnv() android.FixturePreparer { return android.FixtureMergeEnv(map[string]string{ "AIDL_FROZEN_REL": "true", "AIDL_FROZEN_OWNERS": "aosp test", }) } func setTransitiveFreezeEnv() android.FixturePreparer { return android.FixtureMergeEnv(map[string]string{ "AIDL_TRANSITIVE_FREEZE": "true", }) } func _testAidl(t *testing.T, bp string, customizers ...android.FixturePreparer) android.FixturePreparer { t.Helper() preparers := []android.FixturePreparer{} preparers = append(preparers, cc.PrepareForTestWithCcDefaultModules, java.PrepareForTestWithJavaDefaultModules, genrule.PrepareForTestWithGenRuleBuildComponents, android.PrepareForTestWithNamespace, ) bp = bp + ` package { default_visibility: ["//visibility:public"], } java_defaults { name: "aidl-java-module-defaults", } cc_defaults { name: "aidl-cpp-module-defaults", } rust_defaults { name: "aidl-rust-module-defaults", } cc_library { name: "libbinder", recovery_available: true, } cc_library_static { name: "aidl-analyzer-main", host_supported: true, vendor_available: true, recovery_available: true, } cc_library { name: "libutils", recovery_available: true, } cc_library { name: "libcutils", recovery_available: true, } cc_library { name: "libbinder_ndk", recovery_available: true, stubs: { versions: ["29"], } } ndk_library { name: "libbinder_ndk", symbol_file: "libbinder_ndk.map.txt", first_version: "29", } cc_library { name: "liblog", no_libcrt: true, nocrt: true, system_shared_libs: [], } rust_library { name: "libstd", crate_name: "std", srcs: [""], no_stdlibs: true, sysroot: true, } rust_library { name: "libtest", crate_name: "test", srcs: [""], no_stdlibs: true, sysroot: true, } rust_library { name: "liblazy_static", crate_name: "lazy_static", srcs: [""], } rust_library { name: "libbinder_rs", crate_name: "binder", srcs: [""], } rust_proc_macro { name: "libasync_trait", crate_name: "async_trait", srcs: [""], no_stdlibs: true, } ` preparers = append(preparers, android.FixtureWithRootAndroidBp(bp)) preparers = append(preparers, android.FixtureAddTextFile("system/tools/aidl/build/Android.bp", ` aidl_interfaces_metadata { name: "aidl_metadata_json", visibility: ["//system/tools/aidl:__subpackages__"], } `)) preparers = append(preparers, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { // To keep tests stable, fix Platform_sdk_codename and Platform_sdk_final // Use setReleaseEnv() to test release version variables.Platform_sdk_version = intPtr(28) variables.Platform_sdk_codename = proptools.StringPtr("Q") variables.Platform_version_active_codenames = []string{"Q"} variables.Platform_sdk_final = proptools.BoolPtr(false) })) preparers = append(preparers, customizers...) preparers = append(preparers, rust.PrepareForTestWithRustBuildComponents, android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { ctx.RegisterModuleType("aidl_interface", AidlInterfaceFactory) ctx.RegisterModuleType("aidl_interface_headers", AidlInterfaceHeadersFactory) ctx.RegisterModuleType("aidl_interface_defaults", AidlInterfaceDefaultsFactory) ctx.RegisterModuleType("aidl_interfaces_metadata", aidlInterfacesMetadataSingletonFactory) ctx.RegisterModuleType("rust_defaults", func() android.Module { return rust.DefaultsFactory() }) ctx.PreArchMutators(registerPreArchMutators) ctx.PostDepsMutators(registerPostDepsMutators) }), ) return android.GroupFixturePreparers(preparers...) } func testAidl(t *testing.T, bp string, customizers ...android.FixturePreparer) (*android.TestContext, android.Config) { t.Helper() preparer := _testAidl(t, bp, customizers...) result := preparer.RunTest(t) return result.TestContext, result.Config } func testAidlError(t *testing.T, pattern, bp string, customizers ...android.FixturePreparer) { t.Helper() preparer := _testAidl(t, bp, customizers...) preparer. ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)). RunTest(t) } // asserts that there are expected module regardless of variants func assertModulesExists(t *testing.T, ctx *android.TestContext, names ...string) { t.Helper() missing := []string{} for _, name := range names { variants := ctx.ModuleVariantsForTests(name) if len(variants) == 0 { missing = append(missing, name) } } if len(missing) > 0 { // find all the modules that do exist allModuleNames := make(map[string]bool) ctx.VisitAllModules(func(m blueprint.Module) { allModuleNames[ctx.ModuleName(m)] = true }) t.Errorf("expected modules(%v) not found. all modules: %v", missing, android.SortedKeys(allModuleNames)) } } func assertContains(t *testing.T, actual, expected string) { t.Helper() if !strings.Contains(actual, expected) { t.Errorf("%q is not found in %q.", expected, actual) } } func assertListContains(t *testing.T, actual []string, expected string) { t.Helper() for _, a := range actual { if strings.Contains(a, expected) { return } } t.Errorf("%q is not found in %v.", expected, actual) } // Vintf module must have versions in release version func TestVintfWithoutVersionInRelease(t *testing.T) { vintfWithoutVersionBp := ` aidl_interface { name: "foo", stability: "vintf", srcs: [ "IFoo.aidl", ], backend: { rust: { enabled: true, }, }, }` expectedError := `module "foo_interface": versions: must be set \(need to be frozen\) because` testAidlError(t, expectedError, vintfWithoutVersionBp, setReleaseEnv()) testAidlError(t, expectedError, vintfWithoutVersionBp, setTestFreezeEnv()) ctx, _ := testAidl(t, vintfWithoutVersionBp) assertModulesExists(t, ctx, "foo-V1-java", "foo-V1-rust", "foo-V1-cpp", "foo-V1-ndk") } // Check if using unstable version in release cause an error. func TestUnstableVersionUsageInRelease(t *testing.T) { unstableVersionUsageInJavaBp := ` aidl_interface { name: "foo", versions: [ "1", ], srcs: [ "IFoo.aidl", ], } java_library { name: "bar", libs: ["foo-V2-java"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, }) expectedError1 := `foo-V2-java is an unfrozen development version, and it can't be used because "this is a release branch - freeze it or set 'owners:'"` testAidlError(t, expectedError1, unstableVersionUsageInJavaBp, setReleaseEnv(), files) expectedError2 := `foo-V2-java is an unfrozen development version, and it can't be used because "this is a release branch \(simulated by setting AIDL_FROZEN_REL\) - freeze it or set 'owners:'"` testAidlError(t, expectedError2, unstableVersionUsageInJavaBp, setTestFreezeEnv(), files) testAidl(t, unstableVersionUsageInJavaBp, files) // A stable version can be used in release version stableVersionUsageInJavaBp := ` aidl_interface { name: "foo", versions: [ "1", ], srcs: [ "IFoo.aidl", ], } java_library { name: "bar", libs: ["foo-V1-java"], }` testAidl(t, stableVersionUsageInJavaBp, setReleaseEnv(), files) testAidl(t, stableVersionUsageInJavaBp, setTestFreezeEnv(), files) testAidl(t, stableVersionUsageInJavaBp, files) } func TestUsingUnstableVersionIndirectlyInRelease(t *testing.T) { unstableVersionUsageInJavaBp := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], versions: ["1"], } aidl_interface { name: "foo", imports: ["xxx-V2"], // not OK versions: ["1"], srcs: ["IFoo.aidl"], } java_library { name: "bar", libs: ["foo-V1-java"], // OK }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/xxx/1/foo.1.aidl": nil, "aidl_api/xxx/1/.hash": nil, }) expectedError := `xxx-V2-java is an unfrozen development version` testAidlError(t, expectedError, unstableVersionUsageInJavaBp, setReleaseEnv(), files) testAidlError(t, expectedError, unstableVersionUsageInJavaBp, setTestFreezeEnv(), files) testAidl(t, unstableVersionUsageInJavaBp, files) } func TestFrozenTrueSimple(t *testing.T) { frozenTest := ` aidl_interface { name: "foo", versions: ["1"], frozen: true, srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, }) testAidl(t, frozenTest, files, setReleaseEnv()) testAidl(t, frozenTest, files, setTestFreezeEnv()) testAidl(t, frozenTest, files) } func TestFrozenWithNoVersions(t *testing.T) { frozenTest := ` aidl_interface { name: "foo", frozen: true, srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, }) expectedError := `cannot be frozen without versions` testAidlError(t, expectedError, frozenTest, files, setReleaseEnv()) testAidlError(t, expectedError, frozenTest, files, setTestFreezeEnv()) testAidlError(t, expectedError, frozenTest, files) } func TestFrozenImportingFrozen(t *testing.T) { frozenTest := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], frozen: true, versions: ["1"], } aidl_interface { name: "foo", imports: ["xxx-V1"], versions: ["1"], frozen: true, srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/xxx/1/foo.1.aidl": nil, "aidl_api/xxx/1/.hash": nil, }) testAidl(t, frozenTest, files, setReleaseEnv()) testAidl(t, frozenTest, files, setTestFreezeEnv()) testAidl(t, frozenTest, files) } func TestFrozenImportingVersionUnfrozen(t *testing.T) { frozenTest := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], frozen: false, versions: ["1"], } aidl_interface { name: "foo", imports: ["xxx-V1"], versions: ["1"], frozen: true, srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/xxx/1/foo.1.aidl": nil, "aidl_api/xxx/1/.hash": nil, }) testAidl(t, frozenTest, files, setReleaseEnv()) testAidl(t, frozenTest, files, setTestFreezeEnv()) testAidl(t, frozenTest, files) } func TestFrozenImportingUnfrozenWithFrozen(t *testing.T) { frozenTest := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], frozen: false, versions: ["1"], } aidl_interface { name: "foo", imports: ["xxx"], versions: ["1"], frozen: true, srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/xxx/1/foo.1.aidl": nil, "aidl_api/xxx/1/.hash": nil, }) expectedError := `"foo" imports "xxx" which is not frozen. Either "foo" must` testAidlError(t, expectedError, frozenTest, files, setReleaseEnv()) testAidlError(t, expectedError, frozenTest, files, setTestFreezeEnv()) testAidlError(t, expectedError, frozenTest, files) } func TestFrozenImportingUnfrozen(t *testing.T) { frozenTest := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], frozen: false, } aidl_interface { name: "foo", imports: ["xxx"], versions: ["1"], frozen: true, srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/xxx/1/foo.1.aidl": nil, "aidl_api/xxx/1/.hash": nil, }) expectedError := `versions: must be set \(need to be frozen\) because` testAidlError(t, expectedError, frozenTest, files, setReleaseEnv()) testAidlError(t, expectedError, frozenTest, files, setTestFreezeEnv()) expectedError = `"foo" imports "xxx" which is not frozen. Either "foo" must` testAidlError(t, expectedError, frozenTest, files) } // This is allowed to keep legacy behavior. It could be prevented directly after API-freeze // if all frozen interfaces are explicitly marked `frozen: true,`. func TestFrozenImportingUnSpecified(t *testing.T) { frozenTrueSimple := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], versions: ["1"], } aidl_interface { name: "foo", imports: ["xxx-V1"], versions: ["1"], frozen: true, srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/xxx/1/foo.1.aidl": nil, "aidl_api/xxx/1/.hash": nil, }) testAidl(t, frozenTrueSimple, files, setReleaseEnv()) testAidl(t, frozenTrueSimple, files, setTestFreezeEnv()) testAidl(t, frozenTrueSimple, files) } // Keeping legacy behavior if "frozen" is not specified func TestImportingNewLegacy(t *testing.T) { frozenTest := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], versions: ["1"], } aidl_interface { name: "foo", imports: ["xxx-V2"], versions_with_info: [ {version: "1", imports: ["xxx-V1"]}, ], srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/xxx/1/foo.1.aidl": nil, "aidl_api/xxx/1/.hash": nil, }) testAidl(t, frozenTest, files, setReleaseEnv()) testAidl(t, frozenTest, files, setTestFreezeEnv()) testAidl(t, frozenTest, files) } // We don't have a way to know if if "xxx" has changes to it and will // need a new version without the "frozen" attribute. So we keep the // legacy behavior and assume "foo" is still importing the old version. func TestFrozenImportingNewLegacy(t *testing.T) { frozenTest := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], versions: ["1"], } aidl_interface { name: "foo", imports: ["xxx-V1"], frozen: true, versions_with_info: [ {version: "1", imports: ["xxx-V1"]}, ], srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/xxx/1/foo.1.aidl": nil, "aidl_api/xxx/1/.hash": nil, }) testAidl(t, frozenTest, files, setReleaseEnv()) testAidl(t, frozenTest, files, setTestFreezeEnv()) testAidl(t, frozenTest, files) } func TestFrozenImportingNewImplicit(t *testing.T) { frozenTest := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], frozen: false, versions: ["1"], } aidl_interface { name: "foo", imports: ["xxx"], frozen: true, versions_with_info: [ {version: "1", imports: ["xxx-V1"]}, ], srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/xxx/1/foo.1.aidl": nil, "aidl_api/xxx/1/.hash": nil, }) expectedError := `"foo" imports "xxx" which is not frozen. Either "foo" must` testAidlError(t, expectedError, frozenTest, files, setReleaseEnv()) testAidlError(t, expectedError, frozenTest, files, setTestFreezeEnv()) testAidlError(t, expectedError, frozenTest, files) } func TestImportingOwned(t *testing.T) { frozenTest := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], owner: "unknown-owner", frozen: false, } aidl_interface { name: "foo", imports: ["xxx-V1"], frozen: false, versions_with_info: [ {version: "1", imports: []}, ], srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, }) expectedError := "Android.bp:10:10: module \"foo_interface\": imports: \"foo\" imports \"xxx\" which is an interface owned by \"unknown-owner\". This is not allowed because the owned interface will not be frozen at the same time." testAidlError(t, expectedError, frozenTest, files, setReleaseEnv()) testAidlError(t, expectedError, frozenTest, files, setTestFreezeEnv()) testAidlError(t, expectedError, frozenTest, files) } func TestImportingOwnedBothOwned(t *testing.T) { frozenTest := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], owner: "unknown-owner", frozen: false, } aidl_interface { name: "foo", imports: ["xxx-V1"], frozen: false, versions_with_info: [ {version: "1", imports: []}, ], srcs: ["IFoo.aidl"], owner: "unknown-owner-any", }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, }) testAidl(t, frozenTest, files, setReleaseEnv()) testAidl(t, frozenTest, files, setTestFreezeEnv()) testAidl(t, frozenTest, files) } func TestFrozenImportingNewExplicit(t *testing.T) { frozenTest := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], frozen: false, versions: ["1"], } aidl_interface { name: "foo", imports: ["xxx-V2"], frozen: true, versions_with_info: [ {version: "1", imports: ["xxx-V1"]}, ], srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/xxx/1/foo.1.aidl": nil, "aidl_api/xxx/1/.hash": nil, }) expectedError := "This interface is 'frozen: true' but the imports have changed. Set 'frozen: false' to allow changes: \\n Version current imports: map\\[xxx:2\\]\\n Version 1 imports: map\\[xxx:1\\]\\n" testAidlError(t, expectedError, frozenTest, files, setReleaseEnv()) testAidlError(t, expectedError, frozenTest, files, setTestFreezeEnv()) testAidlError(t, expectedError, frozenTest, files) } func TestNonFrozenImportingNewImplicit(t *testing.T) { frozenTest := ` aidl_interface { name: "xxx", srcs: ["IFoo.aidl"], frozen: false, versions: ["1"], } aidl_interface { name: "foo", imports: ["xxx-V1"], frozen: false, versions_with_info: [ {version: "1", imports: ["xxx-V1"]}, ], srcs: ["IFoo.aidl"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/xxx/1/foo.1.aidl": nil, "aidl_api/xxx/1/.hash": nil, }) testAidl(t, frozenTest, files, setReleaseEnv()) testAidl(t, frozenTest, files, setTestFreezeEnv()) testAidl(t, frozenTest, files) } // The module which has never been frozen and is not "unstable" is not allowed in release version. func TestNonVersionedModuleUsageInRelease(t *testing.T) { nonVersionedModuleUsageInJavaBp := ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], } java_library { name: "bar", libs: ["foo-V1-java"], }` expectedError := `"foo_interface": versions: must be set \(need to be frozen\) because` testAidlError(t, expectedError, nonVersionedModuleUsageInJavaBp, setReleaseEnv()) testAidlError(t, expectedError, nonVersionedModuleUsageInJavaBp, setTestFreezeEnv()) testAidl(t, nonVersionedModuleUsageInJavaBp) nonVersionedUnstableModuleUsageInJavaBp := ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], unstable: true, } java_library { name: "bar", libs: ["foo-java"], }` testAidl(t, nonVersionedUnstableModuleUsageInJavaBp, setReleaseEnv()) testAidl(t, nonVersionedUnstableModuleUsageInJavaBp, setTestFreezeEnv()) testAidl(t, nonVersionedUnstableModuleUsageInJavaBp) } func TestNonVersionedModuleOwnedByTestUsageInRelease(t *testing.T) { nonVersionedModuleUsageInJavaBp := ` aidl_interface { name: "foo", owner: "test", srcs: [ "IFoo.aidl", ], } java_library { name: "bar", libs: ["foo-V1-java"], }` expectedError := `"foo_interface": versions: must be set \(need to be frozen\) because` testAidl(t, nonVersionedModuleUsageInJavaBp, setReleaseEnv()) testAidlError(t, expectedError, nonVersionedModuleUsageInJavaBp, setTestFreezeEnv()) testAidl(t, nonVersionedModuleUsageInJavaBp) } func TestNonVersionedModuleOwnedByOtherUsageInRelease(t *testing.T) { nonVersionedModuleUsageInJavaBp := ` aidl_interface { name: "foo", owner: "unknown-owner", srcs: [ "IFoo.aidl", ], } java_library { name: "bar", libs: ["foo-V1-java"], }` testAidl(t, nonVersionedModuleUsageInJavaBp, setReleaseEnv()) testAidl(t, nonVersionedModuleUsageInJavaBp, setTestFreezeEnv()) testAidl(t, nonVersionedModuleUsageInJavaBp) } func TestImportInRelease(t *testing.T) { importInRelease := ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], imports: ["bar-V1"], versions: ["1"], } aidl_interface { name: "bar", srcs: [ "IBar.aidl", ], versions: ["1"], } ` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/bar/1/bar.1.aidl": nil, "aidl_api/bar/1/.hash": nil, }) testAidl(t, importInRelease, setReleaseEnv(), files) testAidl(t, importInRelease, setTestFreezeEnv(), files) testAidl(t, importInRelease, files) } func TestUnstableVersionedModuleUsageInRelease(t *testing.T) { nonVersionedModuleUsageInJavaBp := ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], versions: ["1"], } java_library { name: "bar", libs: ["foo-V2-java"], }` expectedError := `Android.bp:10:2: module \"bar\" variant \"android_common\": foo-V2-java is an unfrozen development version` testAidlError(t, expectedError, nonVersionedModuleUsageInJavaBp, setReleaseEnv()) testAidlError(t, expectedError, nonVersionedModuleUsageInJavaBp, setTestFreezeEnv()) testAidl(t, nonVersionedModuleUsageInJavaBp, withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, })) } func TestUnstableVersionedModuleOwnedByTestUsageInRelease(t *testing.T) { nonVersionedModuleUsageInJavaBp := ` aidl_interface { name: "foo", owner: "test", srcs: [ "IFoo.aidl", ], versions: ["1"], } java_library { name: "bar", libs: ["foo-V2-java"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, }) expectedError := `Android.bp:11:2: module \"bar\" variant \"android_common\": foo-V2-java is an unfrozen development version` testAidl(t, nonVersionedModuleUsageInJavaBp, setReleaseEnv(), files) testAidlError(t, expectedError, nonVersionedModuleUsageInJavaBp, setTestFreezeEnv(), files) testAidl(t, nonVersionedModuleUsageInJavaBp, files) } func TestFrozenModuleUsageInAllEnvs(t *testing.T) { bp := ` aidl_interface { name: "foo", frozen: true, srcs: [ "IFoo.aidl", ], versions: ["1"], } java_library { name: "bar", libs: ["foo-V2-java"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, }) expectedError := `Android.bp:11:2: module \"bar\" variant \"android_common\": foo-V2-java is an unfrozen development version` testAidlError(t, expectedError, bp, setReleaseEnv(), files) testAidlError(t, expectedError, bp, setTestFreezeEnv(), files) testAidlError(t, expectedError, bp, files) } func TestUnstableVersionedModuleOwnedByOtherUsageInRelease(t *testing.T) { nonVersionedModuleUsageInJavaBp := ` aidl_interface { name: "foo", owner: "unknown-owner", srcs: [ "IFoo.aidl", ], versions: ["1"], } java_library { name: "bar", libs: ["foo-V2-java"], }` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, }) testAidl(t, nonVersionedModuleUsageInJavaBp, setReleaseEnv(), files) testAidl(t, nonVersionedModuleUsageInJavaBp, setTestFreezeEnv(), files) testAidl(t, nonVersionedModuleUsageInJavaBp, files) } func TestUnstableModules(t *testing.T) { testAidlError(t, `module "foo_interface": stability: must be empty when "unstable" is true`, ` aidl_interface { name: "foo", stability: "vintf", unstable: true, srcs: [ "IFoo.aidl", ], backend: { rust: { enabled: true, }, }, } `) testAidlError(t, `module "foo_interface": versions: cannot have versions for an unstable interface`, ` aidl_interface { name: "foo", versions: [ "1", ], unstable: true, srcs: [ "IFoo.aidl", ], backend: { rust: { enabled: true, }, }, } `) ctx, _ := testAidl(t, ` aidl_interface { name: "foo", unstable: true, srcs: [ "IFoo.aidl", ], backend: { rust: { enabled: true, }, }, } `) assertModulesExists(t, ctx, "foo-java", "foo-rust", "foo-cpp", "foo-ndk") } func TestCreatesModulesWithNoVersions(t *testing.T) { ctx, _ := testAidl(t, ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], backend: { rust: { enabled: true, }, }, } `) assertModulesExists(t, ctx, "foo-V1-java", "foo-V1-rust", "foo-V1-cpp", "foo-V1-ndk") } func TestCreatesModulesWithFrozenVersions(t *testing.T) { // Each version should be under aidl_api// testAidlError(t, `No sources for a previous version in aidl_api/foo/1. Was a version manually added to .bp file?`, ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], versions: [ "1", ], backend: { rust: { enabled: true, }, }, } `) ctx, _ := testAidl(t, ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], versions: [ "1", ], backend: { rust: { enabled: true, }, }, } `, withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, })) // For frozen version "1" assertModulesExists(t, ctx, "foo-V1-java", "foo-V1-rust", "foo-V1-cpp", "foo-V1-ndk") // For ToT (current) assertModulesExists(t, ctx, "foo-V2-java", "foo-V2-rust", "foo-V2-cpp", "foo-V2-ndk") } func TestErrorsWithUnsortedVersions(t *testing.T) { testAidlError(t, `versions: should be sorted`, ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], versions: [ "2", "1", ], backend: { rust: { enabled: true, }, }, } `) } func TestErrorsWithDuplicateVersions(t *testing.T) { testAidlError(t, `versions: duplicate`, ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], versions: [ "1", "1", ], } `) } func TestErrorsWithNonPositiveVersions(t *testing.T) { testAidlError(t, `versions: should be > 0`, ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], versions: [ "-1", "1", ], } `) } func TestErrorsWithNonIntegerVersions(t *testing.T) { testAidlError(t, `versions: "first" is not an integer`, ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], versions: [ "first", ], } `) } const ( androidVariant = "android_common" nativeVariant = "android_arm_armv7-a-neon_shared" nativeRustVariant = "android_arm_armv7-a-neon_dylib" ) func TestNativeOutputIsAlwaysVersioned(t *testing.T) { var ctx *android.TestContext assertOutput := func(moduleName, variant, outputFilename string) { t.Helper() producer, ok := ctx.ModuleForTests(moduleName, variant).Module().(android.OutputFileProducer) if !ok { t.Errorf("%s(%s): should be OutputFileProducer.", moduleName, variant) } paths, err := producer.OutputFiles("") if err != nil { t.Errorf("%s(%s): failed to get OutputFiles: %v", moduleName, variant, err) } if len(paths) != 1 || paths[0].Base() != outputFilename { t.Errorf("%s(%s): expected output %q, but got %v", moduleName, variant, outputFilename, paths) } } // No versions ctx, _ = testAidl(t, ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], backend: { rust: { enabled: true, }, }, } `) // Even though there is no version, generated modules have version(V1) unless it isn't an unstable interface. assertOutput("foo-V1-java", androidVariant, "foo-V1-java.jar") assertOutput("foo-V1-cpp", nativeVariant, "foo-V1-cpp.so") assertOutput("foo-V1-rust", nativeRustVariant, "libfoo_V1.dylib.so") // With versions: "1", "2" ctx, _ = testAidl(t, ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], versions: [ "1", "2", ], backend: { rust: { enabled: true, }, }, } `, withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/foo/2/foo.2.aidl": nil, "aidl_api/foo/2/.hash": nil, })) // alias for the latest frozen version (=2) assertOutput("foo-V2-java", androidVariant, "foo-V2-java.jar") assertOutput("foo-V2-cpp", nativeVariant, "foo-V2-cpp.so") assertOutput("foo-V2-rust", nativeRustVariant, "libfoo_V2.dylib.so") // frozen "1" assertOutput("foo-V1-java", androidVariant, "foo-V1-java.jar") assertOutput("foo-V1-cpp", nativeVariant, "foo-V1-cpp.so") assertOutput("foo-V1-rust", nativeRustVariant, "libfoo_V1.dylib.so") // tot assertOutput("foo-V3-java", androidVariant, "foo-V3-java.jar") assertOutput("foo-V3-cpp", nativeVariant, "foo-V3-cpp.so") assertOutput("foo-V3-rust", nativeRustVariant, "libfoo_V3.dylib.so") // skip ndk since they follow the same rule with cpp } func TestImports(t *testing.T) { testAidlError(t, `Import does not exist:`, ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], imports: [ "bar", ] } `) testAidlError(t, `backend.java.enabled: Java backend not enabled in the imported AIDL interface "bar"`, ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], imports: [ "bar-V1", ] } aidl_interface { name: "bar", srcs: [ "IBar.aidl", ], backend: { java: { enabled: false, }, }, } `) testAidlError(t, `backend.cpp.enabled: C\+\+ backend not enabled in the imported AIDL interface "bar"`, ` aidl_interface { name: "foo", srcs: [ "IFoo.aidl", ], imports: [ "bar-V1", ] } aidl_interface { name: "bar", srcs: [ "IBar.aidl", ], backend: { cpp: { enabled: false, }, }, } `) testAidlError(t, `imports: "foo" depends on "bar" but does not specify a version`, ` aidl_interface { name: "foo", unstable: true, srcs: [ "IFoo.aidl", ], imports: [ "bar", ] } aidl_interface { name: "bar", srcs: [ "IBar.aidl", ], } `) ctx, _ := testAidl(t, ` aidl_interface_defaults { name: "foo-defaults", srcs: [ "IFoo.aidl", ], backend: { rust: { enabled: true, }, }, imports: [ "bar.1-V1", ] } aidl_interface { name: "foo", defaults: ["foo-defaults"], } aidl_interface { name: "bar.1", srcs: [ "IBar.aidl", ], backend: { rust: { enabled: true, }, }, } `) ldRule := ctx.ModuleForTests("foo-V1-cpp", nativeVariant).Rule("ld") libFlags := ldRule.Args["libFlags"] libBar := filepath.Join("bar.1-V1-cpp", nativeVariant, "bar.1-V1-cpp.so") if !strings.Contains(libFlags, libBar) { t.Errorf("%q is not found in %q", libBar, libFlags) } rustcRule := ctx.ModuleForTests("foo-V1-rust", nativeRustVariant).Rule("rustc") libFlags = rustcRule.Args["libFlags"] libBar = filepath.Join("out", "soong", ".intermediates", "bar.1-V1-rust", nativeRustVariant, "unstripped", "libbar_1_V1.dylib.so") libBarFlag := "--extern bar_1=" + libBar if !strings.Contains(libFlags, libBarFlag) { t.Errorf("%q is not found in %q", libBarFlag, libFlags) } } func TestDuplicatedVersions(t *testing.T) { // foo depends on myiface-V2-ndk via direct dep and also on // myiface-V1-ndk via indirect dep. This should be prohibited. testAidlError(t, `depends on multiple versions of the same aidl_interface: myiface-V1-.*, myiface-V2-.*`, ` aidl_interface { name: "myiface", srcs: ["IFoo.aidl"], versions: ["1", "2"], } cc_library { name: "foo", shared_libs: ["myiface-V2-ndk", "bar"], } cc_library { name: "bar", shared_libs: ["myiface-V1-ndk"], } `, withFiles(map[string][]byte{ "aidl_api/myiface/1/myiface.1.aidl": nil, "aidl_api/myiface/1/.hash": nil, "aidl_api/myiface/2/myiface.2.aidl": nil, "aidl_api/myiface/2/.hash": nil, })) testAidlError(t, `depends on multiple versions of the same aidl_interface: myiface-V1-.*, myiface-V2-.*`, ` aidl_interface { name: "myiface", srcs: ["IFoo.aidl"], versions: ["1"], } aidl_interface { name: "myiface2", srcs: ["IBar.aidl"], imports: ["myiface-V2"] } cc_library { name: "foobar", shared_libs: ["myiface-V1-ndk", "myiface2-V1-ndk"], } `, withFiles(map[string][]byte{ "aidl_api/myiface/1/myiface.1.aidl": nil, "aidl_api/myiface/1/.hash": nil, })) testAidlError(t, `depends on multiple versions of the same aidl_interface: myiface-V1-.*, myiface-V2-.*`, ` aidl_interface { name: "myiface", srcs: ["IFoo.aidl"], versions: ["1"], } aidl_interface { name: "myiface2", srcs: ["IBar.aidl"], imports: ["myiface-V2"] } cc_library { name: "foobar", srcs: [":myiface-V1-ndk-source"], shared_libs: ["myiface2-V1-ndk"], } `, withFiles(map[string][]byte{ "aidl_api/myiface/1/myiface.1.aidl": nil, "aidl_api/myiface/1/.hash": nil, })) // Okay to reference two different testAidl(t, ` aidl_interface { name: "myiface", srcs: ["IFoo.aidl"], versions: ["1"], } cc_library { name: "foobar", shared_libs: ["myiface-V1-cpp", "myiface-V1-ndk"], } `, withFiles(map[string][]byte{ "aidl_api/myiface/1/myiface.1.aidl": nil, "aidl_api/myiface/1/.hash": nil, })) testAidl(t, ` aidl_interface { name: "myiface", srcs: ["IFoo.aidl"], versions: ["1"], } aidl_interface { name: "myiface2", srcs: ["IBar.aidl"], imports: ["myiface-V2"] } cc_library { name: "foobar", srcs: [":myiface-V2-ndk-source"], shared_libs: ["myiface2-V1-ndk"], } `, withFiles(map[string][]byte{ "aidl_api/myiface/1/myiface.1.aidl": nil, "aidl_api/myiface/1/.hash": nil, })) testAidl(t, ` aidl_interface { name: "myiface", srcs: ["IFoo.aidl"], versions: ["1"], } aidl_interface { name: "myiface2", srcs: ["IBar.aidl"], imports: ["myiface-V2"] } cc_library { name: "foobar", shared_libs: ["myiface-V2-ndk", "myiface2-V1-ndk"], } `, withFiles(map[string][]byte{ "aidl_api/myiface/1/myiface.1.aidl": nil, "aidl_api/myiface/1/.hash": nil, })) } func TestVndkRequiresFrozen(t *testing.T) { testAidlError(t, `frozen: true or false must be specified when the VNDK is enabled on a versioned interface`, ` aidl_interface { name: "myiface", vendor_available: true, product_available: true, srcs: ["IFoo.aidl"], vndk: { enabled: true, }, } `) testAidlError(t, `vndk_use_version: must be specified if interface is unfrozen`, ` aidl_interface { name: "myiface", vendor_available: true, product_available: true, srcs: ["IFoo.aidl"], stability: "vintf", frozen: false, vndk: { enabled: true, }, } `) testAidl(t, ` aidl_interface { name: "myiface", vendor_available: true, product_available: true, srcs: ["IFoo.aidl"], stability: "vintf", frozen: false, vndk_use_version: "1", vndk: { enabled: true, }, } `) } func TestRecoveryAvailable(t *testing.T) { ctx, _ := testAidl(t, ` aidl_interface { name: "myiface", recovery_available: true, srcs: ["IFoo.aidl"], } `) ctx.ModuleForTests("myiface-V1-ndk", "android_recovery_arm64_armv8-a_shared") ctx.ModuleForTests("myiface-V1-cpp", "android_recovery_arm64_armv8-a_shared") } func TestRustDuplicateNames(t *testing.T) { testAidl(t, ` aidl_interface { name: "myiface", srcs: ["dir/a/Foo.aidl", "dir/b/Foo.aidl"], backend: { rust: { enabled: true, }, }, } `) } func TestAidlImportFlagsForImportedModules(t *testing.T) { customizer := withFiles(map[string][]byte{ "foo/Android.bp": []byte(` aidl_interface { name: "foo-iface", srcs: ["a/Foo.aidl"], imports: ["bar-iface-V2"], versions: ["1"], headers: ["boq-iface-headers"], } `), "foo/a/Foo.aidl": nil, "foo/aidl_api/foo-iface/current/a/Foo.aidl": nil, "foo/aidl_api/foo-iface/1/a/Foo.aidl": nil, "foo/aidl_api/foo-iface/1/.hash": nil, "bar/Android.bp": []byte(` aidl_interface { name: "bar-iface", srcs: ["b/Bar.aidl"], imports: ["baz-iface-V1"], versions: ["1"], } `), "bar/b/Bar.aidl": nil, "bar/aidl_api/bar-iface/current/b/Bar.aidl": nil, "bar/aidl_api/bar-iface/1/b/Bar.aidl": nil, "bar/aidl_api/bar-iface/1/.hash": nil, "baz/Android.bp": []byte(` aidl_interface { name: "baz-iface", srcs: ["b/Baz.aidl"], include_dirs: ["baz-include"], versions: ["1"], } `), "baz/b/Baz.aidl": nil, "baz/aidl_api/baz-iface/current/b/Baz.aidl": nil, "baz/aidl_api/baz-iface/1/b/Baz.aidl": nil, "baz/aidl_api/baz-iface/1/.hash": nil, "boq/Android.bp": []byte(` aidl_interface_headers { name: "boq-iface-headers", srcs: ["b/Boq.aidl"], } `), "boq/b/Baz.aidl": nil, }) ctx, _ := testAidl(t, ``, customizer) // checkapidump rule is to compare "compatibility" between ToT(dump) and "current" { rule := ctx.ModuleForTests("foo-iface-api", "").Output("checkapi_dump.timestamp") android.AssertStringEquals(t, "checkapi(dump == current) imports", "-Iboq", rule.Args["imports"]) android.AssertStringDoesContain(t, "checkapi(dump == current) optionalFlags", rule.Args["optionalFlags"], "-pout/soong/.intermediates/bar/bar-iface_interface/2/preprocessed.aidl") } // has_development rule runs --checkapi for equality between latest("1") // and ToT { rule := ctx.ModuleForTests("foo-iface-api", "").Output("has_development") android.AssertStringDoesContain(t, "checkapi(dump == latest(1)) should import import's preprocessed", rule.RuleParams.Command, "-pout/soong/.intermediates/bar/bar-iface_interface/2/preprocessed.aidl") } // compile (v1) { rule := ctx.ModuleForTests("foo-iface-V1-cpp-source", "").Output("a/Foo.cpp") android.AssertStringEquals(t, "compile(old=1) should import aidl_api/1", "-Ifoo/aidl_api/foo-iface/1 -Iboq", rule.Args["imports"]) android.AssertStringDoesContain(t, "compile(old=1) should import bar.preprocessed", rule.Args["optionalFlags"], "-pout/soong/.intermediates/bar/bar-iface_interface/2/preprocessed.aidl") } // compile ToT(v2) { rule := ctx.ModuleForTests("foo-iface-V2-cpp-source", "").Output("a/Foo.cpp") android.AssertStringEquals(t, "compile(tot=2) should import base dirs of srcs", "-Ifoo -Iboq", rule.Args["imports"]) android.AssertStringDoesContain(t, "compile(tot=2) should import bar.preprocessed", rule.Args["optionalFlags"], "-pout/soong/.intermediates/bar/bar-iface_interface/2/preprocessed.aidl") } } func TestAidlPreprocess(t *testing.T) { customizer := withFiles(map[string][]byte{ "foo/Android.bp": []byte(` aidl_interface { name: "foo-iface", local_include_dir: "src", include_dirs: [ "path1", "path2/sub", ], srcs: [ "src/foo/Foo.aidl", ], imports: [ "bar-iface", ], unstable: true, } aidl_interface { name: "bar-iface", local_include_dir: "src", srcs: [ "src/bar/Bar.aidl", ], unstable: true, } `), "foo/src/foo/Foo.aidl": nil, "foo/src/bar/Bar.aidl": nil, }) ctx, _ := testAidl(t, ``, customizer) rule := ctx.ModuleForTests("foo-iface_interface", "").Output("preprocessed.aidl") android.AssertStringDoesContain(t, "preprocessing should import srcs and include_dirs", rule.RuleParams.Command, "-Ifoo/src -Ipath1 -Ipath2/sub") android.AssertStringDoesContain(t, "preprocessing should import import's preprocess", rule.RuleParams.Command, "-pout/soong/.intermediates/foo/bar-iface_interface/preprocessed.aidl") } func TestAidlImportFlagsForUnstable(t *testing.T) { customizer := withFiles(map[string][]byte{ "foo/Android.bp": []byte(` aidl_interface { name: "foo-iface", local_include_dir: "src", include_dirs: [ "path1", "path2/sub", ], srcs: [ "src/foo/Foo.aidl", ], imports: [ "bar-iface", ], unstable: true, } aidl_interface { name: "bar-iface", local_include_dir: "src", srcs: [ "src/bar/Bar.aidl", ], unstable: true, } `), "foo/src/foo/Foo.aidl": nil, "foo/src/bar/Bar.aidl": nil, }) ctx, _ := testAidl(t, ``, customizer) rule := ctx.ModuleForTests("foo-iface-cpp-source", "").Output("foo/Foo.cpp") android.AssertStringEquals(t, "compile(unstable) should import foo/base_dirs(target) and bar/base_dirs(imported)", "-Ifoo/src -Ipath1 -Ipath2/sub", rule.Args["imports"]) android.AssertStringDoesContain(t, "compile(unstable) should import bar.preprocessed", rule.Args["optionalFlags"], "-pout/soong/.intermediates/foo/bar-iface_interface/preprocessed.aidl") } func TestSupportsGenruleAndFilegroup(t *testing.T) { customizer := withFiles(map[string][]byte{ "foo/Android.bp": []byte(` aidl_interface { name: "foo-iface", local_include_dir: "src", include_dirs: [ "path1", "path2/sub", ], srcs: [ "src/foo/Foo.aidl", ":filegroup1", ":gen1", ], imports: [ "bar-iface-V1", ], versions: ["1"], } filegroup { name: "filegroup1", path: "filegroup/sub", srcs: [ "filegroup/sub/pkg/Bar.aidl", ], } genrule { name: "gen1", cmd: "generate baz/Baz.aidl", out: [ "baz/Baz.aidl", ] } aidl_interface { name: "bar-iface", local_include_dir: "src", srcs: [ "src/bar/Bar.aidl", ":gen-bar", ], } genrule { name: "gen-bar", cmd: "generate gen/GenBar.aidl", out: [ "gen/GenBar.aidl", ] } `), "foo/aidl_api/foo-iface/1/foo/Foo.aidl": nil, "foo/aidl_api/foo-iface/1/.hash": nil, "foo/filegroup/sub/pkg/Bar.aidl": nil, "foo/src/foo/Foo.aidl": nil, }) ctx, _ := testAidl(t, ``, customizer) // aidlCompile for snapshots (v1) { rule := ctx.ModuleForTests("foo-iface-V1-cpp-source", "").Output("foo/Foo.cpp") android.AssertStringEquals(t, "compile(1) should import foo/aidl_api/1", "-Ifoo/aidl_api/foo-iface/1 -Ipath1 -Ipath2/sub", rule.Args["imports"]) android.AssertStringDoesContain(t, "compile(1) should import bar.preprocessed", rule.Args["optionalFlags"], "-pout/soong/.intermediates/foo/bar-iface_interface/1/preprocessed.aidl") } // aidlCompile for ToT (v2) { rule := ctx.ModuleForTests("foo-iface-V2-cpp-source", "").Output("foo/Foo.cpp") android.AssertStringEquals(t, "compile(tot=2) should import foo.base_dirs", "-Ifoo/src -Ifoo/filegroup/sub -Iout/soong/.intermediates/foo/gen1/gen -Ipath1 -Ipath2/sub", rule.Args["imports"]) android.AssertStringDoesContain(t, "compile(tot=2) should import bar.preprocessed", rule.Args["optionalFlags"], "-pout/soong/.intermediates/foo/bar-iface_interface/1/preprocessed.aidl") } // dumpapi { rule := ctx.ModuleForTests("foo-iface-api", "").Rule("aidlDumpApiRule") android.AssertPathsRelativeToTopEquals(t, "dumpapi should dump srcs/filegroups/genrules", []string{ "foo/src/foo/Foo.aidl", "foo/filegroup/sub/pkg/Bar.aidl", "out/soong/.intermediates/foo/gen1/gen/baz/Baz.aidl", }, rule.Inputs) dumpDir := "out/soong/.intermediates/foo/foo-iface-api/dump" android.AssertPathsRelativeToTopEquals(t, "dumpapi should dump with rel paths", []string{ dumpDir + "/foo/Foo.aidl", dumpDir + "/pkg/Bar.aidl", dumpDir + "/baz/Baz.aidl", dumpDir + "/.hash", }, rule.Outputs.Paths()) android.AssertStringEquals(t, "dumpapi should import base_dirs and include_dirs", "-Ifoo/src -Ifoo/filegroup/sub -Iout/soong/.intermediates/foo/gen1/gen -Ipath1 -Ipath2/sub", rule.Args["imports"]) android.AssertStringDoesContain(t, "dumpapi should import bar.preprocessed", rule.Args["optionalFlags"], "-pout/soong/.intermediates/foo/bar-iface_interface/1/preprocessed.aidl") } } func TestAidlFlags(t *testing.T) { ctx, _ := testAidl(t, ` aidl_interface { name: "myiface", srcs: ["a/Foo.aidl", "b/Bar.aidl"], flags: ["-Weverything", "-Werror"], backend: { rust: { enabled: true }} } `) for module, outputs := range map[string][]string{ "myiface-V1-cpp-source": {"a/Foo.h", "b/Bar.h"}, "myiface-V1-java-source": {"a/Foo.java", "b/Bar.java"}, "myiface-V1-ndk-source": {"aidl/a/Foo.h", "aidl/b/Bar.h"}, "myiface-V1-rust-source": {"a/Foo.rs", "b/Bar.rs"}, } { for _, output := range outputs { t.Run(module+"/"+output, func(t *testing.T) { params := ctx.ModuleForTests(module, "").Output(output) assertContains(t, params.Args["optionalFlags"], "-Weverything") assertContains(t, params.Args["optionalFlags"], "-Werror") }) } } } func TestAidlModuleJavaSdkVersionDeterminesMinSdkVersion(t *testing.T) { ctx, _ := testAidl(t, ` aidl_interface { name: "myiface", srcs: ["a/Foo.aidl"], backend: { java: { sdk_version: "28", }, }, } `, java.FixtureWithPrebuiltApis(map[string][]string{"28": {"foo"}})) params := ctx.ModuleForTests("myiface-V1-java-source", "").Output("a/Foo.java") assertContains(t, params.Args["optionalFlags"], "--min_sdk_version 28") } func TestAidlModuleNameContainsVersion(t *testing.T) { testAidlError(t, "aidl_interface should not have '-V suffix", ` aidl_interface { name: "myiface-V2", srcs: ["a/Foo.aidl", "b/Bar.aidl"], } `) // Ugly, but okay testAidl(t, ` aidl_interface { name: "myiface-V2aa", srcs: ["a/Foo.aidl", "b/Bar.aidl"], } `) } func TestExplicitAidlModuleImport(t *testing.T) { for _, importVersion := range []string{"V1", "V2"} { ctx, _ := testAidl(t, ` aidl_interface { name: "foo", srcs: ["Foo.aidl"], versions: [ "1", ], imports: ["bar-`+importVersion+`"] } aidl_interface { name: "bar", srcs: ["Bar.aidl"], versions: [ "1", ], } `, withFiles(map[string][]byte{ "aidl_api/foo/1/Foo.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/bar/1/Bar.aidl": nil, "aidl_api/bar/1/.hash": nil, })) for _, foo := range []string{"foo-V1-cpp", "foo-V2-cpp"} { ldRule := ctx.ModuleForTests(foo, nativeVariant).Rule("ld") libFlags := ldRule.Args["libFlags"] libBar := filepath.Join("bar-"+importVersion+"-cpp", nativeVariant, "bar-"+importVersion+"-cpp.so") if !strings.Contains(libFlags, libBar) { t.Errorf("%q is not found in %q", libBar, libFlags) } } } testAidlError(t, "module \"foo_interface\": imports: \"foo\" depends on \"bar\" version \"3\"", ` aidl_interface { name: "foo", srcs: ["Foo.aidl"], versions: [ "1", ], imports: ["bar-V3"] } aidl_interface { name: "bar", srcs: ["Bar.aidl"], versions: [ "1", ], } `, withFiles(map[string][]byte{ "aidl_api/foo/1/Foo.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/bar/1/Bar.aidl": nil, "aidl_api/bar/1/.hash": nil, })) } func TestUseVersionedPreprocessedWhenImporotedWithVersions(t *testing.T) { ctx, _ := testAidl(t, ` aidl_interface { name: "unstable-foo", srcs: ["foo/Foo.aidl"], imports: [ "bar-V2", "baz-V1", "unstable-bar", ], unstable: true, } aidl_interface { name: "foo", srcs: ["foo/Foo.aidl"], imports: [ "bar-V1", "baz-V1", ], versions: ["1"], } aidl_interface { name: "foo-no-versions", srcs: ["foo/Foo.aidl"], imports: [ "bar-V2", ], } aidl_interface { name: "bar", srcs: ["bar/Bar.aidl"], versions: ["1"], } aidl_interface { name: "unstable-bar", srcs: ["bar/Bar.aidl"], unstable: true, } aidl_interface { name: "baz", srcs: ["baz/Baz.aidl"], versions: ["1"], } `, withFiles(map[string][]byte{ "foo/Foo.aidl": nil, "bar/Bar.aidl": nil, "baz/Baz.aidl": nil, "aidl_api/foo/1/foo/Foo.aidl": nil, "aidl_api/foo/1/.hash": nil, "aidl_api/bar/1/bar/Bar.aidl": nil, "aidl_api/bar/1/.hash": nil, "aidl_api/baz/1/baz/Baz.aidl": nil, "aidl_api/baz/1/.hash": nil, })) { rule := ctx.ModuleForTests("foo-V2-java-source", "").Output("foo/Foo.java") android.AssertStringDoesContain(t, "foo-V2(tot) imports bar-V1 for 'bar-V1'", rule.Args["optionalFlags"], "-pout/soong/.intermediates/bar_interface/1/preprocessed.aidl") android.AssertStringDoesContain(t, "foo-V2(tot) imports baz-V1 for 'baz-V1'", rule.Args["optionalFlags"], "-pout/soong/.intermediates/baz_interface/1/preprocessed.aidl") } { rule := ctx.ModuleForTests("foo-V1-java-source", "").Output("foo/Foo.java") android.AssertStringDoesContain(t, "foo-V1 imports bar-V1(latest) for 'bar'", rule.Args["optionalFlags"], "-pout/soong/.intermediates/bar_interface/1/preprocessed.aidl") android.AssertStringDoesContain(t, "foo-V1 imports baz-V1 for 'baz-V1'", rule.Args["optionalFlags"], "-pout/soong/.intermediates/baz_interface/1/preprocessed.aidl") } { rule := ctx.ModuleForTests("unstable-foo-java-source", "").Output("foo/Foo.java") android.AssertStringDoesContain(t, "unstable-foo imports bar-V2(latest) for 'bar'", rule.Args["optionalFlags"], "-pout/soong/.intermediates/bar_interface/2/preprocessed.aidl") android.AssertStringDoesContain(t, "unstable-foo imports baz-V1 for 'baz-V1'", rule.Args["optionalFlags"], "-pout/soong/.intermediates/baz_interface/1/preprocessed.aidl") android.AssertStringDoesContain(t, "unstable-foo imports unstable-bar(ToT) for 'unstable-bar'", rule.Args["optionalFlags"], "-pout/soong/.intermediates/unstable-bar_interface/preprocessed.aidl") } { rule := ctx.ModuleForTests("foo-no-versions-V1-java-source", "").Output("foo/Foo.java") android.AssertStringDoesContain(t, "foo-no-versions-V1(latest) imports bar-V2(latest) for 'bar'", rule.Args["optionalFlags"], "-pout/soong/.intermediates/bar_interface/2/preprocessed.aidl") } } func FindModule(ctx *android.TestContext, name, variant, dir string) android.Module { var module android.Module ctx.VisitAllModules(func(m blueprint.Module) { if ctx.ModuleName(m) == name && ctx.ModuleSubDir(m) == variant && ctx.ModuleDir(m) == dir { module = m.(android.Module) } }) if module == nil { m := ctx.ModuleForTests(name, variant).Module() panic(fmt.Errorf("failed to find module %q variant %q dir %q, but found one in %q", name, variant, dir, ctx.ModuleDir(m))) } return module } func TestDuplicateInterfacesWithTheSameNameInDifferentSoongNamespaces(t *testing.T) { ctx, _ := testAidl(t, ``, withFiles(map[string][]byte{ "common/Android.bp": []byte(` aidl_interface { name: "common", srcs: ["ICommon.aidl"], versions: ["1", "2"], } `), "common/aidl_api/common/1/ICommon.aidl": nil, "common/aidl_api/common/1/.hash": nil, "common/aidl_api/common/2/ICommon.aidl": nil, "common/aidl_api/common/2/.hash": nil, "vendor/a/Android.bp": []byte(` soong_namespace {} `), "vendor/a/foo/Android.bp": []byte(` aidl_interface { name: "foo", owner: "vendor", srcs: ["IFoo.aidl"], imports: ["common-V1"], } `), "vendor/b/Android.bp": []byte(` soong_namespace {} `), "vendor/b/foo/Android.bp": []byte(` aidl_interface { name: "foo", owner: "vendor", srcs: ["IFoo.aidl"], imports: ["common-V2"], } `), })) aFooV1Java := FindModule(ctx, "foo-V1-java", "android_common", "vendor/a/foo").(*java.Library) android.AssertStringListContains(t, "a/foo deps", aFooV1Java.CompilerDeps(), "common-V1-java") bFooV1Java := FindModule(ctx, "foo-V1-java", "android_common", "vendor/b/foo").(*java.Library) android.AssertStringListContains(t, "a/foo deps", bFooV1Java.CompilerDeps(), "common-V2-java") } func TestUnstableChecksForAidlInterfacesInDifferentNamespaces(t *testing.T) { files := withFiles(map[string][]byte{ "vendor/a/Android.bp": []byte(` soong_namespace {} `), "vendor/a/foo/Android.bp": []byte(` aidl_interface { name: "foo", owner: "vendor", srcs: ["IFoo.aidl"], versions: ["1", "2"], } java_library { name: "bar", libs: ["foo-V2-java"], // OK } `), "vendor/a/foo/aidl_api/foo/1/IFoo.aidl": nil, "vendor/a/foo/aidl_api/foo/1/.hash": nil, "vendor/a/foo/aidl_api/foo/2/IFoo.aidl": nil, "vendor/a/foo/aidl_api/foo/2/.hash": nil, "vendor/b/Android.bp": []byte(` soong_namespace {} `), "vendor/b/foo/Android.bp": []byte(` aidl_interface { name: "foo", owner: "vendor", srcs: ["IFoo.aidl"], versions: ["1"], } java_library { name: "bar", libs: ["foo-V1-java"], // OK } `), "vendor/b/foo/aidl_api/foo/1/IFoo.aidl": nil, "vendor/b/foo/aidl_api/foo/1/.hash": nil, }) testAidl(t, ``, files, setReleaseEnv()) testAidl(t, ``, files, setTestFreezeEnv()) testAidl(t, ``, files) } func TestVersionsWithInfoAndVersions(t *testing.T) { conflictingFields := ` aidl_interface { name: "foo", versions: [ "1", ], versions_with_info: [ { version: "1", } ], } ` files := withFiles(map[string][]byte{ "aidl_api/foo/1/foo.1.aidl": nil, "aidl_api/foo/1/.hash": nil, }) expectedError := `Use versions_with_info instead of versions.` testAidlError(t, expectedError, conflictingFields, files) } func TestVersionsWithInfo(t *testing.T) { ctx, _ := testAidl(t, ``, withFiles(map[string][]byte{ "common/Android.bp": []byte(` aidl_interface { name: "common", srcs: ["ICommon.aidl"], versions: ["1", "2"], } `), "common/aidl_api/common/1/ICommon.aidl": nil, "common/aidl_api/common/1/.hash": nil, "common/aidl_api/common/2/ICommon.aidl": nil, "common/aidl_api/common/2/.hash": nil, "foo/Android.bp": []byte(` aidl_interface { name: "foo", srcs: ["IFoo.aidl"], imports: ["common-V3"], versions_with_info: [ {version: "1", imports: ["common-V1"]}, {version: "2", imports: ["common-V2"]}, ] } `), "foo/aidl_api/foo/1/IFoo.aidl": nil, "foo/aidl_api/foo/1/.hash": nil, "foo/aidl_api/foo/2/IFoo.aidl": nil, "foo/aidl_api/foo/2/.hash": nil, })) fooV1Java := FindModule(ctx, "foo-V1-java", "android_common", "foo").(*java.Library) android.AssertStringListContains(t, "a/foo-v1 deps", fooV1Java.CompilerDeps(), "common-V1-java") fooV2Java := FindModule(ctx, "foo-V2-java", "android_common", "foo").(*java.Library) android.AssertStringListContains(t, "a/foo-v2 deps", fooV2Java.CompilerDeps(), "common-V2-java") fooV3Java := FindModule(ctx, "foo-V3-java", "android_common", "foo").(*java.Library) android.AssertStringListContains(t, "a/foo-v3 deps", fooV3Java.CompilerDeps(), "common-V3-java") } func TestVersionsWithInfoImport(t *testing.T) { testAidlError(t, "imports in versions_with_info must specify its version", ``, withFiles(map[string][]byte{ "common/Android.bp": []byte(` aidl_interface { name: "common", srcs: ["ICommon.aidl"], versions: ["1", "2"], } `), "common/aidl_api/common/1/ICommon.aidl": nil, "common/aidl_api/common/1/.hash": nil, "common/aidl_api/common/2/ICommon.aidl": nil, "common/aidl_api/common/2/.hash": nil, "foo/Android.bp": []byte(` aidl_interface { name: "foo", srcs: ["IFoo.aidl"], imports: ["common"], versions_with_info: [ {version: "1", imports: ["common"]}, {version: "2", imports: ["common-V2"]}, ] } `), "foo/aidl_api/foo/1/IFoo.aidl": nil, "foo/aidl_api/foo/1/.hash": nil, "foo/aidl_api/foo/2/IFoo.aidl": nil, "foo/aidl_api/foo/2/.hash": nil, })) } func TestFreezeApiDeps(t *testing.T) { for _, transitive := range []bool{true, false} { for _, testcase := range []struct { string bool }{{"common-V3", true}, {"common-V2", false}} { im := testcase.string customizers := []android.FixturePreparer{ withFiles(map[string][]byte{ "common/Android.bp": []byte(` aidl_interface { name: "common", frozen: false, srcs: ["ICommon.aidl"], versions: ["1", "2"], } `), "common/aidl_api/common/1/ICommon.aidl": nil, "common/aidl_api/common/1/.hash": nil, "common/aidl_api/common/2/ICommon.aidl": nil, "common/aidl_api/common/2/.hash": nil, "foo/Android.bp": []byte(fmt.Sprintf(` aidl_interface { name: "foo", srcs: ["IFoo.aidl"], imports: ["%s"], frozen: false, versions_with_info: [ {version: "1", imports: ["common-V1"]}, {version: "2", imports: ["common-V2"]}, ] } `, im)), "foo/aidl_api/foo/1/IFoo.aidl": nil, "foo/aidl_api/foo/1/.hash": nil, "foo/aidl_api/foo/2/IFoo.aidl": nil, "foo/aidl_api/foo/2/.hash": nil, }), } if transitive { customizers = append(customizers, setTransitiveFreezeEnv()) } ctx, _ := testAidl(t, ``, customizers...) shouldHaveDep := transitive && testcase.bool fooFreezeApiRule := ctx.ModuleForTests("foo-api", "").Output("update_or_freeze_api_3.timestamp") commonFreezeApiOutput := ctx.ModuleForTests("common-api", "").Output("update_or_freeze_api_3.timestamp").Output.String() testMethod := android.AssertStringListDoesNotContain if shouldHaveDep { testMethod = android.AssertStringListContains } testMethod(t, "Only if AIDL_TRANSITIVE_FREEZE is set and an aidl_interface depends on an another aidl_interface's ToT version, an imported aidl_interface should be frozen as well.", fooFreezeApiRule.Implicits.Strings(), commonFreezeApiOutput) } } }