|  | // Copyright 2020 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 java | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "path/filepath" | 
|  | "testing" | 
|  |  | 
|  | "android/soong/android" | 
|  |  | 
|  | "github.com/google/blueprint/proptools" | 
|  | ) | 
|  |  | 
|  | // TODO(b/177892522): Move these tests into a more appropriate place. | 
|  |  | 
|  | func fixtureSetPrebuiltHiddenApiDirProductVariable(prebuiltHiddenApiDir *string) android.FixturePreparer { | 
|  | return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { | 
|  | variables.PrebuiltHiddenApiDir = prebuiltHiddenApiDir | 
|  | }) | 
|  | } | 
|  |  | 
|  | var prepareForTestWithDefaultPlatformBootclasspath = android.FixtureAddTextFile("frameworks/base/boot/Android.bp", ` | 
|  | platform_bootclasspath { | 
|  | name: "platform-bootclasspath", | 
|  | } | 
|  | `) | 
|  |  | 
|  | var hiddenApiFixtureFactory = android.GroupFixturePreparers( | 
|  | PrepareForTestWithJavaDefaultModules, | 
|  | PrepareForTestWithHiddenApiBuildComponents, | 
|  | ) | 
|  |  | 
|  | func TestHiddenAPISingleton(t *testing.T) { | 
|  | result := android.GroupFixturePreparers( | 
|  | hiddenApiFixtureFactory, | 
|  | FixtureConfigureBootJars("platform:foo"), | 
|  | prepareForTestWithDefaultPlatformBootclasspath, | 
|  | ).RunTestWithBp(t, ` | 
|  | java_library { | 
|  | name: "foo", | 
|  | srcs: ["a.java"], | 
|  | compile_dex: true, | 
|  | } | 
|  | `) | 
|  |  | 
|  | hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common") | 
|  | hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags") | 
|  | want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar" | 
|  | android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want) | 
|  | } | 
|  |  | 
|  | func TestHiddenAPISingletonWithSourceAndPrebuiltPreferredButNoDex(t *testing.T) { | 
|  | expectedErrorMessage := "module prebuilt_foo{os:android,arch:common} does not provide a dex jar" | 
|  |  | 
|  | android.GroupFixturePreparers( | 
|  | hiddenApiFixtureFactory, | 
|  | FixtureConfigureBootJars("platform:foo"), | 
|  | prepareForTestWithDefaultPlatformBootclasspath, | 
|  | ).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorMessage)). | 
|  | RunTestWithBp(t, ` | 
|  | java_library { | 
|  | name: "foo", | 
|  | srcs: ["a.java"], | 
|  | compile_dex: true, | 
|  | } | 
|  |  | 
|  | java_import { | 
|  | name: "foo", | 
|  | jars: ["a.jar"], | 
|  | prefer: true, | 
|  | } | 
|  | `) | 
|  | } | 
|  |  | 
|  | func TestHiddenAPISingletonWithPrebuilt(t *testing.T) { | 
|  | result := android.GroupFixturePreparers( | 
|  | hiddenApiFixtureFactory, | 
|  | FixtureConfigureBootJars("platform:foo"), | 
|  | prepareForTestWithDefaultPlatformBootclasspath, | 
|  | ).RunTestWithBp(t, ` | 
|  | java_import { | 
|  | name: "foo", | 
|  | jars: ["a.jar"], | 
|  | compile_dex: true, | 
|  | } | 
|  | `) | 
|  |  | 
|  | hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common") | 
|  | hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags") | 
|  | want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar" | 
|  | android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want) | 
|  | } | 
|  |  | 
|  | func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) { | 
|  | result := android.GroupFixturePreparers( | 
|  | hiddenApiFixtureFactory, | 
|  | FixtureConfigureBootJars("platform:foo"), | 
|  | prepareForTestWithDefaultPlatformBootclasspath, | 
|  | ).RunTestWithBp(t, ` | 
|  | java_library { | 
|  | name: "foo", | 
|  | srcs: ["a.java"], | 
|  | compile_dex: true, | 
|  | } | 
|  |  | 
|  | java_import { | 
|  | name: "foo", | 
|  | jars: ["a.jar"], | 
|  | compile_dex: true, | 
|  | prefer: false, | 
|  | } | 
|  | `) | 
|  |  | 
|  | hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common") | 
|  | hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags") | 
|  | fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar" | 
|  | android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg) | 
|  |  | 
|  | prebuiltJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/dex/foo.jar" | 
|  | android.AssertStringDoesNotContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg) | 
|  | } | 
|  |  | 
|  | func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) { | 
|  | result := android.GroupFixturePreparers( | 
|  | hiddenApiFixtureFactory, | 
|  | FixtureConfigureBootJars("platform:foo"), | 
|  | prepareForTestWithDefaultPlatformBootclasspath, | 
|  | ).RunTestWithBp(t, ` | 
|  | java_library { | 
|  | name: "foo", | 
|  | srcs: ["a.java"], | 
|  | compile_dex: true, | 
|  | } | 
|  |  | 
|  | java_import { | 
|  | name: "foo", | 
|  | jars: ["a.jar"], | 
|  | compile_dex: true, | 
|  | prefer: true, | 
|  | } | 
|  | `) | 
|  |  | 
|  | hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common") | 
|  | hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags") | 
|  | prebuiltJarArg := "--boot-dex=out/soong/.intermediates/prebuilt_foo/android_common/dex/foo.jar" | 
|  | android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg) | 
|  |  | 
|  | fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar" | 
|  | android.AssertStringDoesNotContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg) | 
|  | } | 
|  |  | 
|  | func TestHiddenAPISingletonSdks(t *testing.T) { | 
|  | testCases := []struct { | 
|  | name             string | 
|  | unbundledBuild   bool | 
|  | publicStub       string | 
|  | systemStub       string | 
|  | testStub         string | 
|  | corePlatformStub string | 
|  |  | 
|  | // Additional test preparer | 
|  | preparer android.FixturePreparer | 
|  | }{ | 
|  | { | 
|  | name:             "testBundled", | 
|  | unbundledBuild:   false, | 
|  | publicStub:       "android_stubs_current_exportable", | 
|  | systemStub:       "android_system_stubs_current_exportable", | 
|  | testStub:         "android_test_stubs_current_exportable", | 
|  | corePlatformStub: "legacy.core.platform.api.stubs.exportable", | 
|  | preparer:         android.GroupFixturePreparers(), | 
|  | }, { | 
|  | name:             "testUnbundled", | 
|  | unbundledBuild:   true, | 
|  | publicStub:       "sdk_public_current_android", | 
|  | systemStub:       "sdk_system_current_android", | 
|  | testStub:         "sdk_test_current_android", | 
|  | corePlatformStub: "legacy.core.platform.api.stubs.exportable", | 
|  | preparer:         PrepareForTestWithPrebuiltsOfCurrentApi, | 
|  | }, | 
|  | } | 
|  | for _, tc := range testCases { | 
|  | t.Run(tc.name, func(t *testing.T) { | 
|  | result := android.GroupFixturePreparers( | 
|  | hiddenApiFixtureFactory, | 
|  | tc.preparer, | 
|  | prepareForTestWithDefaultPlatformBootclasspath, | 
|  | android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { | 
|  | variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild) | 
|  | variables.BuildFlags = map[string]string{ | 
|  | "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", | 
|  | } | 
|  | }), | 
|  | ).RunTest(t) | 
|  |  | 
|  | hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common") | 
|  | hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags") | 
|  | wantPublicStubs := "--public-stub-classpath=" + generateSdkDexPath(tc.publicStub, tc.unbundledBuild) | 
|  | android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantPublicStubs) | 
|  |  | 
|  | wantSystemStubs := "--system-stub-classpath=" + generateSdkDexPath(tc.systemStub, tc.unbundledBuild) | 
|  | android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantSystemStubs) | 
|  |  | 
|  | wantTestStubs := "--test-stub-classpath=" + generateSdkDexPath(tc.testStub, tc.unbundledBuild) | 
|  | android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantTestStubs) | 
|  |  | 
|  | wantCorePlatformStubs := "--core-platform-stub-classpath=" + generateDexPath(defaultJavaDir, tc.corePlatformStub) | 
|  | android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantCorePlatformStubs) | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func generateDexedPath(subDir, dex, module string) string { | 
|  | return fmt.Sprintf("out/soong/.intermediates/%s/android_common/%s/%s.jar", subDir, dex, module) | 
|  | } | 
|  |  | 
|  | func generateDexPath(moduleDir string, module string) string { | 
|  | return generateDexedPath(filepath.Join(moduleDir, module), "dex", module) | 
|  | } | 
|  |  | 
|  | func generateSdkDexPath(module string, unbundled bool) string { | 
|  | if unbundled { | 
|  | return generateDexedPath("prebuilts/sdk/"+module, "dex", module) | 
|  | } | 
|  | return generateDexPath(defaultJavaDir, module) | 
|  | } | 
|  |  | 
|  | func TestHiddenAPISingletonWithPrebuiltCsvFile(t *testing.T) { | 
|  |  | 
|  | // The idea behind this test is to ensure that when the build is | 
|  | // confugured with a PrebuiltHiddenApiDir that the rules for the | 
|  | // hiddenapi singleton copy the prebuilts to the typical output | 
|  | // location, and then use that output location for the hiddenapi encode | 
|  | // dex step. | 
|  |  | 
|  | // Where to find the prebuilt hiddenapi files: | 
|  | prebuiltHiddenApiDir := "path/to/prebuilt/hiddenapi" | 
|  |  | 
|  | result := android.GroupFixturePreparers( | 
|  | hiddenApiFixtureFactory, | 
|  | FixtureConfigureBootJars("platform:foo"), | 
|  | fixtureSetPrebuiltHiddenApiDirProductVariable(&prebuiltHiddenApiDir), | 
|  | ).RunTestWithBp(t, ` | 
|  | java_import { | 
|  | name: "foo", | 
|  | jars: ["a.jar"], | 
|  | compile_dex: true, | 
|  | } | 
|  | `) | 
|  |  | 
|  | expectedCpInput := prebuiltHiddenApiDir + "/hiddenapi-flags.csv" | 
|  | expectedCpOutput := "out/soong/hiddenapi/hiddenapi-flags.csv" | 
|  | expectedFlagsCsv := "out/soong/hiddenapi/hiddenapi-flags.csv" | 
|  |  | 
|  | foo := result.ModuleForTests("foo", "android_common") | 
|  |  | 
|  | hiddenAPI := result.SingletonForTests("hiddenapi") | 
|  | cpRule := hiddenAPI.Rule("Cp") | 
|  | actualCpInput := cpRule.BuildParams.Input | 
|  | actualCpOutput := cpRule.BuildParams.Output | 
|  | encodeDexRule := foo.Rule("hiddenAPIEncodeDex") | 
|  | actualFlagsCsv := encodeDexRule.BuildParams.Args["flagsCsv"] | 
|  |  | 
|  | android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule input", expectedCpInput, actualCpInput) | 
|  |  | 
|  | android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule output", expectedCpOutput, actualCpOutput) | 
|  |  | 
|  | android.AssertStringEquals(t, "hiddenapi encode dex rule flags csv", expectedFlagsCsv, actualFlagsCsv) | 
|  | } | 
|  |  | 
|  | func TestHiddenAPIEncoding_JavaSdkLibrary(t *testing.T) { | 
|  |  | 
|  | result := android.GroupFixturePreparers( | 
|  | hiddenApiFixtureFactory, | 
|  | FixtureConfigureBootJars("platform:foo"), | 
|  | PrepareForTestWithJavaSdkLibraryFiles, | 
|  | FixtureWithLastReleaseApis("foo"), | 
|  |  | 
|  | // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding | 
|  | // is disabled. | 
|  | android.FixtureAddTextFile("frameworks/base/Android.bp", ""), | 
|  | ).RunTestWithBp(t, ` | 
|  | java_sdk_library { | 
|  | name: "foo", | 
|  | srcs: ["a.java"], | 
|  | shared_library: false, | 
|  | compile_dex: true, | 
|  | public: {enabled: true}, | 
|  | } | 
|  | `) | 
|  |  | 
|  | checkDexEncoded := func(t *testing.T, name, unencodedDexJar, encodedDexJar string) { | 
|  | moduleForTests := result.ModuleForTests(name, "android_common") | 
|  |  | 
|  | encodeDexRule := moduleForTests.Rule("hiddenAPIEncodeDex") | 
|  | actualUnencodedDexJar := encodeDexRule.Input | 
|  |  | 
|  | // Make sure that the module has its dex jar encoded. | 
|  | android.AssertStringEquals(t, "encode embedded java_library", unencodedDexJar, actualUnencodedDexJar.String()) | 
|  |  | 
|  | // Make sure that the encoded dex jar is the exported one. | 
|  | errCtx := moduleErrorfTestCtx{} | 
|  | exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath(errCtx).Path() | 
|  | android.AssertPathRelativeToTopEquals(t, "encode embedded java_library", encodedDexJar, exportedDexJar) | 
|  | } | 
|  |  | 
|  | // The java_library embedded with the java_sdk_library must be dex encoded. | 
|  | t.Run("foo", func(t *testing.T) { | 
|  | expectedUnencodedDexJar := "out/soong/.intermediates/foo/android_common/aligned/foo.jar" | 
|  | expectedEncodedDexJar := "out/soong/.intermediates/foo/android_common/hiddenapi/foo.jar" | 
|  | checkDexEncoded(t, "foo", expectedUnencodedDexJar, expectedEncodedDexJar) | 
|  | }) | 
|  |  | 
|  | // The dex jar of the child implementation java_library of the java_sdk_library is not currently | 
|  | // dex encoded. | 
|  | t.Run("foo.impl", func(t *testing.T) { | 
|  | fooImpl := result.ModuleForTests("foo.impl", "android_common") | 
|  | encodeDexRule := fooImpl.MaybeRule("hiddenAPIEncodeDex") | 
|  | if encodeDexRule.Rule != nil { | 
|  | t.Errorf("foo.impl is not expected to be encoded") | 
|  | } | 
|  | }) | 
|  | } |