| // 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 ( |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "strings" |
| "testing" |
| |
| "github.com/google/blueprint" |
| "github.com/google/blueprint/proptools" |
| |
| "android/soong/android" |
| "android/soong/cc" |
| "android/soong/java" |
| ) |
| |
| var buildDir string |
| |
| func setUp() { |
| var err error |
| buildDir, err = ioutil.TempDir("", "soong_aidl_test") |
| if err != nil { |
| panic(err) |
| } |
| } |
| |
| func tearDown() { |
| os.RemoveAll(buildDir) |
| } |
| |
| func TestMain(m *testing.M) { |
| run := func() int { |
| setUp() |
| defer tearDown() |
| |
| return m.Run() |
| } |
| |
| os.Exit(run()) |
| } |
| |
| type testCustomizer func(fs map[string][]byte, config android.Config) |
| |
| func withFiles(files map[string][]byte) testCustomizer { |
| return func(fs map[string][]byte, config android.Config) { |
| for k, v := range files { |
| fs[k] = v |
| } |
| } |
| } |
| |
| func setReleaseEnv() testCustomizer { |
| return func(_ map[string][]byte, config android.Config) { |
| config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("REL") |
| config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(true) |
| } |
| } |
| |
| func _testAidl(t *testing.T, bp string, customizers ...testCustomizer) (*android.TestContext, android.Config) { |
| t.Helper() |
| |
| bp = bp + java.GatherRequiredDepsForTest() |
| bp = bp + cc.GatherRequiredDepsForTest(android.Android) |
| bp = bp + ` |
| java_defaults { |
| name: "aidl-java-module-defaults", |
| } |
| cc_defaults { |
| name: "aidl-cpp-module-defaults", |
| } |
| cc_library { |
| name: "libbinder", |
| } |
| cc_library { |
| name: "libutils", |
| } |
| cc_library { |
| name: "libcutils", |
| } |
| cc_library { |
| name: "libbinder_ndk", |
| } |
| ndk_library { |
| name: "libbinder_ndk", |
| symbol_file: "libbinder_ndk.map.txt", |
| first_version: "29", |
| } |
| aidl_interfaces_metadata { |
| name: "aidl_metadata_json", |
| } |
| ` |
| fs := map[string][]byte{} |
| |
| cc.GatherRequiredFilesForTest(fs) |
| |
| for _, c := range customizers { |
| // The fs now needs to be populated before creating the config, call customizers twice |
| // for now, once to get any fs changes, and later after the config was created to |
| // set product variables or targets. |
| tempConfig := android.TestArchConfig(buildDir, nil, bp, fs) |
| c(fs, tempConfig) |
| } |
| |
| config := android.TestArchConfig(buildDir, nil, bp, fs) |
| |
| // To keep tests stable, fix Platform_sdk_codename and Platform_sdk_final |
| // Use setReleaseEnv() to test release version |
| config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("Q") |
| config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(false) |
| |
| for _, c := range customizers { |
| // The fs now needs to be populated before creating the config, call customizers twice |
| // for now, earlier to get any fs changes, and now after the config was created to |
| // set product variables or targets. |
| tempFS := map[string][]byte{} |
| c(tempFS, config) |
| } |
| |
| ctx := android.NewTestArchContext() |
| cc.RegisterRequiredBuildComponentsForTest(ctx) |
| ctx.RegisterModuleType("aidl_interface", aidlInterfaceFactory) |
| ctx.RegisterModuleType("aidl_interfaces_metadata", aidlInterfacesMetadataSingletonFactory) |
| ctx.RegisterModuleType("android_app", java.AndroidAppFactory) |
| ctx.RegisterModuleType("java_defaults", func() android.Module { |
| return java.DefaultsFactory() |
| }) |
| ctx.RegisterModuleType("java_library_static", java.LibraryStaticFactory) |
| ctx.RegisterModuleType("java_library", java.LibraryFactory) |
| ctx.RegisterModuleType("java_system_modules", java.SystemModulesFactory) |
| ctx.RegisterModuleType("ndk_library", cc.NdkLibraryFactory) |
| |
| ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) |
| ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators) |
| ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { |
| ctx.BottomUp("checkUnstableModule", checkUnstableModuleMutator).Parallel() |
| }) |
| ctx.Register(config) |
| |
| return ctx, config |
| } |
| |
| func testAidl(t *testing.T, bp string, customizers ...testCustomizer) (*android.TestContext, android.Config) { |
| t.Helper() |
| ctx, config := _testAidl(t, bp, customizers...) |
| _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) |
| android.FailIfErrored(t, errs) |
| _, errs = ctx.PrepareBuildActions(config) |
| android.FailIfErrored(t, errs) |
| return ctx, config |
| } |
| |
| func testAidlError(t *testing.T, pattern, bp string, customizers ...testCustomizer) { |
| t.Helper() |
| ctx, config := _testAidl(t, bp, customizers...) |
| _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) |
| if len(errs) > 0 { |
| android.FailIfNoMatchingErrors(t, pattern, errs) |
| return |
| } |
| _, errs = ctx.PrepareBuildActions(config) |
| if len(errs) > 0 { |
| android.FailIfNoMatchingErrors(t, pattern, errs) |
| return |
| } |
| t.Fatalf("missing expected error %q (0 errors are returned)", pattern) |
| } |
| |
| // asserts that there are expected module regardless of variants |
| func assertModulesExists(t *testing.T, ctx *android.TestContext, names ...string) { |
| 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.SortedStringKeys(allModuleNames)) |
| } |
| } |
| |
| // Vintf module must have versions in release version |
| func TestVintfWithoutVersionInRelease(t *testing.T) { |
| vintfWithoutVersionBp := ` |
| aidl_interface { |
| name: "foo", |
| stability: "vintf", |
| srcs: [ |
| "IFoo.aidl", |
| ], |
| }` |
| expectedError := `module "foo_interface": versions: must be set \(need to be frozen\) when "unstable" is false and PLATFORM_VERSION_CODENAME is REL` |
| testAidlError(t, expectedError, vintfWithoutVersionBp, setReleaseEnv()) |
| |
| ctx, _ := testAidl(t, vintfWithoutVersionBp) |
| assertModulesExists(t, ctx, "foo-java", "foo-cpp", "foo-ndk", "foo-ndk_platform") |
| } |
| |
| // 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-unstable-java"], |
| }` |
| |
| expectedError := `unstable-java is disallowed in release version because it is unstable.` |
| testAidlError(t, expectedError, unstableVersionUsageInJavaBp, setReleaseEnv(), withFiles(map[string][]byte{ |
| "aidl_api/foo/1/foo.1.aidl": nil, |
| })) |
| |
| testAidl(t, unstableVersionUsageInJavaBp, withFiles(map[string][]byte{ |
| "aidl_api/foo/1/foo.1.aidl": nil, |
| })) |
| |
| // 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-java"], |
| }` |
| |
| testAidl(t, stableVersionUsageInJavaBp, setReleaseEnv(), withFiles(map[string][]byte{ |
| "aidl_api/foo/1/foo.1.aidl": nil, |
| })) |
| |
| testAidl(t, stableVersionUsageInJavaBp, withFiles(map[string][]byte{ |
| "aidl_api/foo/1/foo.1.aidl": nil, |
| })) |
| } |
| |
| // 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-java"], |
| }` |
| |
| expectedError := `"foo_interface": versions: must be set \(need to be frozen\) when "unstable" is false and PLATFORM_VERSION_CODENAME is REL.` |
| testAidlError(t, expectedError, nonVersionedModuleUsageInJavaBp, setReleaseEnv()) |
| 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) |
| } |
| |
| 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", |
| ], |
| } |
| `) |
| |
| testAidlError(t, `module "foo_interface": versions: cannot have versions for an unstable interface`, ` |
| aidl_interface { |
| name: "foo", |
| versions: [ |
| "1", |
| ], |
| unstable: true, |
| srcs: [ |
| "IFoo.aidl", |
| ], |
| } |
| `) |
| |
| ctx, _ := testAidl(t, ` |
| aidl_interface { |
| name: "foo", |
| unstable: true, |
| srcs: [ |
| "IFoo.aidl", |
| ], |
| } |
| `) |
| |
| assertModulesExists(t, ctx, "foo-java", "foo-cpp", "foo-ndk", "foo-ndk_platform") |
| } |
| |
| func TestCreatesModulesWithNoVersions(t *testing.T) { |
| ctx, _ := testAidl(t, ` |
| aidl_interface { |
| name: "foo", |
| srcs: [ |
| "IFoo.aidl", |
| ], |
| } |
| `) |
| |
| assertModulesExists(t, ctx, "foo-java", "foo-cpp", "foo-ndk", "foo-ndk_platform") |
| } |
| |
| func TestCreatesModulesWithFrozenVersions(t *testing.T) { |
| // Each version should be under aidl_api/<name>/<ver> |
| testAidlError(t, `aidl_api/foo/1`, ` |
| aidl_interface { |
| name: "foo", |
| srcs: [ |
| "IFoo.aidl", |
| ], |
| versions: [ |
| "1", |
| ], |
| } |
| `) |
| |
| ctx, _ := testAidl(t, ` |
| aidl_interface { |
| name: "foo", |
| srcs: [ |
| "IFoo.aidl", |
| ], |
| versions: [ |
| "1", |
| ], |
| } |
| `, withFiles(map[string][]byte{ |
| "aidl_api/foo/1/foo.1.aidl": nil, |
| })) |
| |
| // For alias for the latest frozen version (=1) |
| assertModulesExists(t, ctx, "foo-java", "foo-cpp", "foo-ndk", "foo-ndk_platform") |
| |
| // For frozen version "1" |
| // Note that it is not yet implemented to generate native modules for latest frozen version |
| assertModulesExists(t, ctx, "foo-V1-java") |
| |
| // For ToT (current) |
| assertModulesExists(t, ctx, "foo-unstable-java", "foo-unstable-cpp", "foo-unstable-ndk", "foo-unstable-ndk_platform") |
| } |
| |
| const ( |
| androidVariant = "android_common" |
| nativeVariant = "android_arm_armv7-a-neon_shared" |
| ) |
| |
| 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", |
| ], |
| } |
| `) |
| |
| assertOutput("foo-java", androidVariant, "foo-java.jar") |
| assertOutput("foo-cpp", nativeVariant, "foo-V1-cpp.so") |
| |
| // With versions: "1", "2" |
| ctx, _ = testAidl(t, ` |
| aidl_interface { |
| name: "foo", |
| srcs: [ |
| "IFoo.aidl", |
| ], |
| versions: [ |
| "1", "2", |
| ], |
| } |
| `, withFiles(map[string][]byte{ |
| "aidl_api/foo/1/foo.1.aidl": nil, |
| "aidl_api/foo/2/foo.2.aidl": nil, |
| })) |
| |
| // alias for the latest frozen version (=2) |
| assertOutput("foo-java", androidVariant, "foo-java.jar") |
| assertOutput("foo-cpp", nativeVariant, "foo-V2-cpp.so") |
| |
| // frozen "1" |
| assertOutput("foo-V1-java", androidVariant, "foo-V1-java.jar") |
| assertOutput("foo-V1-cpp", nativeVariant, "foo-V1-cpp.so") |
| |
| // tot |
| assertOutput("foo-unstable-java", androidVariant, "foo-unstable-java.jar") |
| assertOutput("foo-unstable-cpp", nativeVariant, "foo-V3-cpp.so") |
| |
| // skip ndk/ndk_platform since they follow the same rule with cpp |
| } |
| |
| func TestGenLogForNativeBackendRequiresJson(t *testing.T) { |
| testAidlError(t, `"foo-cpp" depends on .*"libjsoncpp"`, ` |
| aidl_interface { |
| name: "foo", |
| srcs: [ |
| "IFoo.aidl", |
| ], |
| backend: { |
| cpp: { |
| gen_log: true, |
| }, |
| }, |
| } |
| `) |
| testAidl(t, ` |
| aidl_interface { |
| name: "foo", |
| srcs: [ |
| "IFoo.aidl", |
| ], |
| backend: { |
| cpp: { |
| gen_log: true, |
| }, |
| }, |
| } |
| cc_library { |
| name: "libjsoncpp", |
| } |
| `) |
| } |
| |
| 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", |
| ] |
| } |
| 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", |
| ] |
| } |
| aidl_interface { |
| name: "bar", |
| srcs: [ |
| "IBar.aidl", |
| ], |
| backend: { |
| cpp: { |
| enabled: false, |
| }, |
| }, |
| } |
| `) |
| |
| ctx, _ := testAidl(t, ` |
| aidl_interface { |
| name: "foo", |
| srcs: [ |
| "IFoo.aidl", |
| ], |
| imports: [ |
| "bar", |
| ] |
| } |
| aidl_interface { |
| name: "bar", |
| srcs: [ |
| "IBar.aidl", |
| ], |
| } |
| `) |
| |
| ldRule := ctx.ModuleForTests("foo-cpp", nativeVariant).Rule("ld") |
| libFlags := ldRule.Args["libFlags"] |
| libBar := filepath.Join("bar-cpp", nativeVariant, "bar-V1-cpp.so") |
| if !strings.Contains(libFlags, libBar) { |
| t.Errorf("%q is not found in %q", libBar, libFlags) |
| } |
| } |