// Copyright 2024 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 apex import ( "testing" "android/soong/aconfig/codegen" "android/soong/android" "android/soong/cc" "android/soong/genrule" "android/soong/java" "android/soong/rust" "github.com/google/blueprint/proptools" ) var withAconfigValidationError = android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.AconfigContainerValidation = "error" variables.BuildId = proptools.StringPtr("TEST.BUILD_ID") }) func TestValidationAcrossContainersExportedPass(t *testing.T) { testCases := []struct { name string bp string }{ { name: "Java lib passes for exported containers cross", bp: apex_default_bp + ` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", java_libs: [ "my_java_library_foo", ], updatable: false, } java_library { name: "my_java_library_foo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", static_libs: ["my_java_aconfig_library_foo"], apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "otherapex", srcs: ["foo.aconfig"], exportable: true, } java_aconfig_library { name: "my_java_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", mode: "exported", apex_available: [ "myapex", ], }`, }, { name: "Android app passes for exported containers cross", bp: apex_default_bp + ` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", apps: [ "my_android_app_foo", ], updatable: false, } android_app { name: "my_android_app_foo", srcs: ["foo/MyClass.java"], sdk_version: "none", system_modules: "none", stl: "none", static_libs: ["my_java_library_bar"], apex_available: [ "myapex" ], } java_library { name: "my_java_library_bar", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", static_libs: ["my_java_aconfig_library_bar"], apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_bar", package: "com.example.package", container: "otherapex", srcs: ["bar.aconfig"], exportable: true, } java_aconfig_library { name: "my_java_aconfig_library_bar", aconfig_declarations: "my_aconfig_declarations_bar", mode: "exported", apex_available: [ "myapex", ], }`, }, { name: "Cc lib passes for exported containers cross", bp: apex_default_bp + ` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", native_shared_libs: [ "my_cc_library_bar", ], binaries: [ "my_cc_binary_baz", ], updatable: false, } cc_library { name: "my_cc_library_bar", srcs: ["foo/bar/MyClass.cc"], static_libs: [ "my_cc_aconfig_library_bar", "my_cc_aconfig_library_baz", ], apex_available: [ "myapex", ], } cc_binary { name: "my_cc_binary_baz", srcs: ["foo/bar/MyClass.cc"], static_libs: ["my_cc_aconfig_library_baz"], apex_available: [ "myapex", ], } cc_library { name: "server_configurable_flags", srcs: ["server_configurable_flags.cc"], } cc_library { name: "libbase", srcs: ["libbase.cc"], } cc_library { name: "libaconfig_storage_read_api_cc", srcs: ["libaconfig_storage_read_api_cc.cc"], } cc_library { name: "libaconfig_storage_protos_cc", srcs: ["libaconfig_storage_protos_cc.cc"], } aconfig_declarations { name: "my_aconfig_declarations_bar", package: "com.example.package", container: "otherapex", srcs: ["bar.aconfig"], exportable: true, } cc_aconfig_library { name: "my_cc_aconfig_library_bar", aconfig_declarations: "my_aconfig_declarations_bar", apex_available: [ "myapex", ], mode: "exported", } aconfig_declarations { name: "my_aconfig_declarations_baz", package: "com.example.package", container: "otherapex", srcs: ["baz.aconfig"], exportable: true, } cc_aconfig_library { name: "my_cc_aconfig_library_baz", aconfig_declarations: "my_aconfig_declarations_baz", apex_available: [ "myapex", ], mode: "exported", }`, }, { name: "Rust lib passes for exported containers cross", bp: apex_default_bp + ` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", native_shared_libs: ["libmy_rust_library"], binaries: ["my_rust_binary"], updatable: false, } rust_library { name: "libflags_rust", // test mock crate_name: "flags_rust", srcs: ["lib.rs"], apex_available: ["myapex"], } rust_library { name: "liblazy_static", // test mock crate_name: "lazy_static", srcs: ["src/lib.rs"], apex_available: ["myapex"], } rust_library { name: "libaconfig_storage_read_api", // test mock crate_name: "aconfig_storage_read_api", srcs: ["src/lib.rs"], apex_available: ["myapex"], } rust_library { name: "liblogger", // test mock crate_name: "logger", srcs: ["src/lib.rs"], apex_available: ["myapex"], } rust_library { name: "liblog_rust", // test mock crate_name: "log_rust", srcs: ["src/lib.rs"], apex_available: ["myapex"], } rust_ffi_shared { name: "libmy_rust_library", srcs: ["src/lib.rs"], rustlibs: ["libmy_rust_aconfig_library_foo"], crate_name: "my_rust_library", apex_available: ["myapex"], } rust_binary { name: "my_rust_binary", srcs: ["foo/bar/MyClass.rs"], rustlibs: ["libmy_rust_aconfig_library_bar"], apex_available: ["myapex"], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "otherapex", srcs: ["foo.aconfig"], } aconfig_declarations { name: "my_aconfig_declarations_bar", package: "com.example.package", container: "otherapex", srcs: ["bar.aconfig"], } rust_aconfig_library { name: "libmy_rust_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", crate_name: "my_rust_aconfig_library_foo", apex_available: ["myapex"], mode: "exported", } rust_aconfig_library { name: "libmy_rust_aconfig_library_bar", aconfig_declarations: "my_aconfig_declarations_bar", crate_name: "my_rust_aconfig_library_bar", apex_available: ["myapex"], mode: "exported", }`, }, } for _, test := range testCases { t.Run(test.name, func(t *testing.T) { android.GroupFixturePreparers( java.PrepareForTestWithJavaDefaultModules, cc.PrepareForTestWithCcBuildComponents, rust.PrepareForTestWithRustDefaultModules, codegen.PrepareForTestWithAconfigBuildComponents, PrepareForTestWithApexBuildComponents, prepareForTestWithMyapex, withAconfigValidationError, ). RunTestWithBp(t, test.bp) }) } } func TestValidationAcrossContainersNotExportedFail(t *testing.T) { testCases := []struct { name string expectedError string bp string }{ { name: "Java lib fails for non-exported containers cross", bp: apex_default_bp + ` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", java_libs: [ "my_java_library_foo", ], updatable: false, } java_library { name: "my_java_library_foo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", static_libs: ["my_java_aconfig_library_foo"], apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "otherapex", srcs: ["foo.aconfig"], } java_aconfig_library { name: "my_java_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", apex_available: [ "myapex", ], }`, expectedError: `.*my_java_library_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`, }, { name: "Android app fails for non-exported containers cross", bp: apex_default_bp + ` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", apps: [ "my_android_app_foo", ], updatable: false, } android_app { name: "my_android_app_foo", srcs: ["foo/MyClass.java"], sdk_version: "none", system_modules: "none", stl: "none", static_libs: ["my_java_library_foo"], apex_available: [ "myapex" ], } java_library { name: "my_java_library_foo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", static_libs: ["my_java_aconfig_library_foo"], apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "otherapex", srcs: ["bar.aconfig"], } java_aconfig_library { name: "my_java_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", apex_available: [ "myapex", ], }`, expectedError: `.*my_android_app_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`, }, { name: "Cc lib fails for non-exported containers cross", bp: apex_default_bp + ` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", native_shared_libs: [ "my_cc_library_foo", ], updatable: false, } cc_library { name: "my_cc_library_foo", srcs: ["foo/bar/MyClass.cc"], shared_libs: [ "my_cc_aconfig_library_foo", ], apex_available: [ "myapex", ], } cc_library { name: "server_configurable_flags", srcs: ["server_configurable_flags.cc"], } cc_library { name: "libbase", srcs: ["libbase.cc"], } cc_library { name: "libaconfig_storage_read_api_cc", srcs: ["libaconfig_storage_read_api_cc.cc"], } cc_library { name: "libaconfig_storage_protos_cc", srcs: ["libaconfig_storage_protos_cc.cc"], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "otherapex", srcs: ["foo.aconfig"], } cc_aconfig_library { name: "my_cc_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", apex_available: [ "myapex", ], }`, expectedError: `.*my_cc_library_foo/myapex depends on my_cc_aconfig_library_foo/otherapex/production across containers`, }, { name: "Cc binary fails for non-exported containers cross", bp: apex_default_bp + ` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", binaries: [ "my_cc_binary_foo", ], updatable: false, } cc_library { name: "my_cc_library_foo", srcs: ["foo/bar/MyClass.cc"], static_libs: [ "my_cc_aconfig_library_foo", ], apex_available: [ "myapex", ], } cc_binary { name: "my_cc_binary_foo", srcs: ["foo/bar/MyClass.cc"], static_libs: ["my_cc_library_foo"], apex_available: [ "myapex", ], } cc_library { name: "server_configurable_flags", srcs: ["server_configurable_flags.cc"], } cc_library { name: "libbase", srcs: ["libbase.cc"], } cc_library { name: "libaconfig_storage_read_api_cc", srcs: ["libaconfig_storage_read_api_cc.cc"], } cc_library { name: "libaconfig_storage_protos_cc", srcs: ["libaconfig_storage_protos_cc.cc"], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "otherapex", srcs: ["foo.aconfig"], } cc_aconfig_library { name: "my_cc_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", apex_available: [ "myapex", ], }`, expectedError: `.*my_cc_binary_foo/myapex depends on my_cc_aconfig_library_foo/otherapex/production across containers`, }, { name: "Rust lib fails for non-exported containers cross", bp: apex_default_bp + ` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", native_shared_libs: ["libmy_rust_library"], updatable: false, } rust_library { name: "libflags_rust", // test mock crate_name: "flags_rust", srcs: ["lib.rs"], apex_available: ["myapex"], } rust_library { name: "liblazy_static", // test mock crate_name: "lazy_static", srcs: ["src/lib.rs"], apex_available: ["myapex"], } rust_library { name: "libaconfig_storage_read_api", // test mock crate_name: "aconfig_storage_read_api", srcs: ["src/lib.rs"], apex_available: ["myapex"], } rust_library { name: "liblogger", // test mock crate_name: "logger", srcs: ["src/lib.rs"], apex_available: ["myapex"], } rust_library { name: "liblog_rust", // test mock crate_name: "log_rust", srcs: ["src/lib.rs"], apex_available: ["myapex"], } rust_ffi_shared { name: "libmy_rust_library", srcs: ["src/lib.rs"], rustlibs: ["libmy_rust_aconfig_library_foo"], crate_name: "my_rust_library", apex_available: ["myapex"], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "otherapex", srcs: ["foo.aconfig"], } rust_aconfig_library { name: "libmy_rust_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", crate_name: "my_rust_aconfig_library_foo", apex_available: ["myapex"], }`, expectedError: `.*libmy_rust_aconfig_library_foo/myapex depends on libmy_rust_aconfig_library_foo/otherapex/production across containers`, }, { name: "Rust binary fails for non-exported containers cross", bp: apex_default_bp + ` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", binaries: ["my_rust_binary"], updatable: false, } rust_library { name: "libflags_rust", // test mock crate_name: "flags_rust", srcs: ["lib.rs"], apex_available: ["myapex"], } rust_library { name: "liblazy_static", // test mock crate_name: "lazy_static", srcs: ["src/lib.rs"], apex_available: ["myapex"], } rust_library { name: "libaconfig_storage_read_api", // test mock crate_name: "aconfig_storage_read_api", srcs: ["src/lib.rs"], apex_available: ["myapex"], } rust_library { name: "liblogger", // test mock crate_name: "logger", srcs: ["src/lib.rs"], apex_available: ["myapex"], } rust_library { name: "liblog_rust", // test mock crate_name: "log_rust", srcs: ["src/lib.rs"], apex_available: ["myapex"], } rust_binary { name: "my_rust_binary", srcs: ["foo/bar/MyClass.rs"], rustlibs: ["libmy_rust_aconfig_library_bar"], apex_available: ["myapex"], } aconfig_declarations { name: "my_aconfig_declarations_bar", package: "com.example.package", container: "otherapex", srcs: ["bar.aconfig"], } rust_aconfig_library { name: "libmy_rust_aconfig_library_bar", aconfig_declarations: "my_aconfig_declarations_bar", crate_name: "my_rust_aconfig_library_bar", apex_available: ["myapex"], }`, expectedError: `.*libmy_rust_aconfig_library_bar/myapex depends on libmy_rust_aconfig_library_bar/otherapex/production across containers`, }, { name: "Aconfig validation propagate along sourceOrOutputDependencyTag", bp: apex_default_bp + ` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", apps: [ "my_android_app_foo", ], updatable: false, } android_app { name: "my_android_app_foo", srcs: ["foo/MyClass.java"], sdk_version: "none", system_modules: "none", stl: "none", static_libs: ["my_java_library_foo"], apex_available: [ "myapex" ], } java_library { name: "my_java_library_foo", srcs: [":my_genrule_foo"], sdk_version: "none", system_modules: "none", apex_available: [ "myapex", ], } aconfig_declarations_group { name: "my_aconfig_declarations_group_foo", java_aconfig_libraries: [ "my_java_aconfig_library_foo", ], } filegroup { name: "my_filegroup_foo_srcjars", srcs: [ ":my_aconfig_declarations_group_foo{.srcjars}", ], } genrule { name: "my_genrule_foo", srcs: [":my_filegroup_foo_srcjars"], cmd: "cp $(in) $(out)", out: ["my_genrule_foo.srcjar"], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "otherapex", srcs: ["bar.aconfig"], } java_aconfig_library { name: "my_java_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", apex_available: [ "myapex", ], }`, expectedError: `.*my_android_app_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`, }, } for _, test := range testCases { t.Run(test.name, func(t *testing.T) { errorHandler := android.FixtureExpectsNoErrors if test.expectedError != "" { errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError) } android.GroupFixturePreparers( java.PrepareForTestWithJavaDefaultModules, cc.PrepareForTestWithCcBuildComponents, rust.PrepareForTestWithRustDefaultModules, codegen.PrepareForTestWithAconfigBuildComponents, genrule.PrepareForIntegrationTestWithGenrule, PrepareForTestWithApexBuildComponents, prepareForTestWithMyapex, withAconfigValidationError, ). ExtendWithErrorHandler(errorHandler). RunTestWithBp(t, test.bp) }) } } func TestValidationNotPropagateAcrossShared(t *testing.T) { testCases := []struct { name string bp string }{ { name: "Java shared lib not propagate aconfig validation", bp: apex_default_bp + ` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", java_libs: [ "my_java_library_bar", ], updatable: false, } java_library { name: "my_java_library_bar", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", libs: ["my_java_library_foo"], apex_available: [ "myapex", ], } java_library { name: "my_java_library_foo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", static_libs: ["my_java_aconfig_library_foo"], apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "otherapex", srcs: ["foo.aconfig"], } java_aconfig_library { name: "my_java_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", apex_available: [ "myapex", ], }`, }, } for _, test := range testCases { t.Run(test.name, func(t *testing.T) { android.GroupFixturePreparers( java.PrepareForTestWithJavaDefaultModules, cc.PrepareForTestWithCcBuildComponents, rust.PrepareForTestWithRustDefaultModules, codegen.PrepareForTestWithAconfigBuildComponents, PrepareForTestWithApexBuildComponents, prepareForTestWithMyapex, withAconfigValidationError, ). RunTestWithBp(t, test.bp) }) } }