1// Copyright 2020 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package java
16
17import (
18	"fmt"
19	"path/filepath"
20	"testing"
21
22	"android/soong/android"
23
24	"github.com/google/blueprint/proptools"
25)
26
27// TODO(b/177892522): Move these tests into a more appropriate place.
28
29func fixtureSetPrebuiltHiddenApiDirProductVariable(prebuiltHiddenApiDir *string) android.FixturePreparer {
30	return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
31		variables.PrebuiltHiddenApiDir = prebuiltHiddenApiDir
32	})
33}
34
35var prepareForTestWithDefaultPlatformBootclasspath = android.FixtureAddTextFile("frameworks/base/boot/Android.bp", `
36	platform_bootclasspath {
37		name: "platform-bootclasspath",
38	}
39`)
40
41var hiddenApiFixtureFactory = android.GroupFixturePreparers(
42	PrepareForTestWithJavaDefaultModules,
43	PrepareForTestWithHiddenApiBuildComponents,
44)
45
46func TestHiddenAPISingleton(t *testing.T) {
47	result := android.GroupFixturePreparers(
48		hiddenApiFixtureFactory,
49		FixtureConfigureBootJars("platform:foo"),
50		prepareForTestWithDefaultPlatformBootclasspath,
51	).RunTestWithBp(t, `
52		java_library {
53			name: "foo",
54			srcs: ["a.java"],
55			compile_dex: true,
56		}
57	`)
58
59	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
60	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
61	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
62	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
63}
64
65func TestHiddenAPISingletonWithSourceAndPrebuiltPreferredButNoDex(t *testing.T) {
66	expectedErrorMessage := "module prebuilt_foo{os:android,arch:common} does not provide a dex jar"
67
68	android.GroupFixturePreparers(
69		hiddenApiFixtureFactory,
70		FixtureConfigureBootJars("platform:foo"),
71		prepareForTestWithDefaultPlatformBootclasspath,
72	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorMessage)).
73		RunTestWithBp(t, `
74		java_library {
75			name: "foo",
76			srcs: ["a.java"],
77			compile_dex: true,
78		}
79
80		java_import {
81			name: "foo",
82			jars: ["a.jar"],
83			prefer: true,
84		}
85	`)
86}
87
88func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
89	result := android.GroupFixturePreparers(
90		hiddenApiFixtureFactory,
91		FixtureConfigureBootJars("platform:foo"),
92		prepareForTestWithDefaultPlatformBootclasspath,
93	).RunTestWithBp(t, `
94		java_import {
95			name: "foo",
96			jars: ["a.jar"],
97			compile_dex: true,
98	}
99	`)
100
101	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
102	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
103	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
104	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
105}
106
107func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
108	result := android.GroupFixturePreparers(
109		hiddenApiFixtureFactory,
110		FixtureConfigureBootJars("platform:foo"),
111		prepareForTestWithDefaultPlatformBootclasspath,
112	).RunTestWithBp(t, `
113		java_library {
114			name: "foo",
115			srcs: ["a.java"],
116			compile_dex: true,
117		}
118
119		java_import {
120			name: "foo",
121			jars: ["a.jar"],
122			compile_dex: true,
123			prefer: false,
124		}
125	`)
126
127	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
128	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
129	fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
130	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
131
132	prebuiltJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/dex/foo.jar"
133	android.AssertStringDoesNotContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
134}
135
136func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
137	result := android.GroupFixturePreparers(
138		hiddenApiFixtureFactory,
139		FixtureConfigureBootJars("platform:foo"),
140		prepareForTestWithDefaultPlatformBootclasspath,
141	).RunTestWithBp(t, `
142		java_library {
143			name: "foo",
144			srcs: ["a.java"],
145			compile_dex: true,
146		}
147
148		java_import {
149			name: "foo",
150			jars: ["a.jar"],
151			compile_dex: true,
152			prefer: true,
153		}
154	`)
155
156	hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
157	hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
158	prebuiltJarArg := "--boot-dex=out/soong/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
159	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
160
161	fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
162	android.AssertStringDoesNotContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
163}
164
165func TestHiddenAPISingletonSdks(t *testing.T) {
166	testCases := []struct {
167		name             string
168		unbundledBuild   bool
169		publicStub       string
170		systemStub       string
171		testStub         string
172		corePlatformStub string
173
174		// Additional test preparer
175		preparer android.FixturePreparer
176	}{
177		{
178			name:             "testBundled",
179			unbundledBuild:   false,
180			publicStub:       "android_stubs_current_exportable",
181			systemStub:       "android_system_stubs_current_exportable",
182			testStub:         "android_test_stubs_current_exportable",
183			corePlatformStub: "legacy.core.platform.api.stubs.exportable",
184			preparer:         android.GroupFixturePreparers(),
185		}, {
186			name:             "testUnbundled",
187			unbundledBuild:   true,
188			publicStub:       "sdk_public_current_android",
189			systemStub:       "sdk_system_current_android",
190			testStub:         "sdk_test_current_android",
191			corePlatformStub: "legacy.core.platform.api.stubs.exportable",
192			preparer:         PrepareForTestWithPrebuiltsOfCurrentApi,
193		},
194	}
195	for _, tc := range testCases {
196		t.Run(tc.name, func(t *testing.T) {
197			result := android.GroupFixturePreparers(
198				hiddenApiFixtureFactory,
199				tc.preparer,
200				prepareForTestWithDefaultPlatformBootclasspath,
201				// Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi
202				// file creation.
203				FixtureConfigureBootJars("platform:foo"),
204				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
205					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild)
206					variables.BuildFlags = map[string]string{
207						"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
208					}
209				}),
210			).RunTestWithBp(t, `
211		java_library {
212			name: "foo",
213			srcs: ["a.java"],
214			compile_dex: true,
215		}
216		`)
217
218			hiddenAPI := result.ModuleForTests("platform-bootclasspath", "android_common")
219			hiddenapiRule := hiddenAPI.Rule("platform-bootclasspath-monolithic-hiddenapi-stub-flags")
220			wantPublicStubs := "--public-stub-classpath=" + generateSdkDexPath(tc.publicStub, tc.unbundledBuild)
221			android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantPublicStubs)
222
223			wantSystemStubs := "--system-stub-classpath=" + generateSdkDexPath(tc.systemStub, tc.unbundledBuild)
224			android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantSystemStubs)
225
226			wantTestStubs := "--test-stub-classpath=" + generateSdkDexPath(tc.testStub, tc.unbundledBuild)
227			android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantTestStubs)
228
229			wantCorePlatformStubs := "--core-platform-stub-classpath=" + generateDexPath(defaultJavaDir, tc.corePlatformStub)
230			android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantCorePlatformStubs)
231		})
232	}
233}
234
235func generateDexedPath(subDir, dex, module string) string {
236	return fmt.Sprintf("out/soong/.intermediates/%s/android_common/%s/%s.jar", subDir, dex, module)
237}
238
239func generateDexPath(moduleDir string, module string) string {
240	return generateDexedPath(filepath.Join(moduleDir, module), "dex", module)
241}
242
243func generateSdkDexPath(module string, unbundled bool) string {
244	if unbundled {
245		return generateDexedPath("prebuilts/sdk/"+module, "dex", module)
246	}
247	return generateDexPath(defaultJavaDir, module)
248}
249
250func TestHiddenAPISingletonWithPrebuiltCsvFile(t *testing.T) {
251
252	// The idea behind this test is to ensure that when the build is
253	// confugured with a PrebuiltHiddenApiDir that the rules for the
254	// hiddenapi singleton copy the prebuilts to the typical output
255	// location, and then use that output location for the hiddenapi encode
256	// dex step.
257
258	// Where to find the prebuilt hiddenapi files:
259	prebuiltHiddenApiDir := "path/to/prebuilt/hiddenapi"
260
261	result := android.GroupFixturePreparers(
262		hiddenApiFixtureFactory,
263		FixtureConfigureBootJars("platform:foo"),
264		fixtureSetPrebuiltHiddenApiDirProductVariable(&prebuiltHiddenApiDir),
265	).RunTestWithBp(t, `
266		java_import {
267			name: "foo",
268			jars: ["a.jar"],
269			compile_dex: true,
270	}
271	`)
272
273	expectedCpInput := prebuiltHiddenApiDir + "/hiddenapi-flags.csv"
274	expectedCpOutput := "out/soong/hiddenapi/hiddenapi-flags.csv"
275	expectedFlagsCsv := "out/soong/hiddenapi/hiddenapi-flags.csv"
276
277	foo := result.ModuleForTests("foo", "android_common")
278
279	hiddenAPI := result.SingletonForTests("hiddenapi")
280	cpRule := hiddenAPI.Rule("Cp")
281	actualCpInput := cpRule.BuildParams.Input
282	actualCpOutput := cpRule.BuildParams.Output
283	encodeDexRule := foo.Rule("hiddenAPIEncodeDex")
284	actualFlagsCsv := encodeDexRule.BuildParams.Args["flagsCsv"]
285
286	android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule input", expectedCpInput, actualCpInput)
287
288	android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule output", expectedCpOutput, actualCpOutput)
289
290	android.AssertStringEquals(t, "hiddenapi encode dex rule flags csv", expectedFlagsCsv, actualFlagsCsv)
291}
292
293func TestHiddenAPIEncoding_JavaSdkLibrary(t *testing.T) {
294
295	result := android.GroupFixturePreparers(
296		hiddenApiFixtureFactory,
297		FixtureConfigureBootJars("platform:foo"),
298		PrepareForTestWithJavaSdkLibraryFiles,
299		FixtureWithLastReleaseApis("foo"),
300
301		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
302		// is disabled.
303		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
304	).RunTestWithBp(t, `
305		java_sdk_library {
306			name: "foo",
307			srcs: ["a.java"],
308			shared_library: false,
309			compile_dex: true,
310			public: {enabled: true},
311		}
312	`)
313
314	checkDexEncoded := func(t *testing.T, name, unencodedDexJar, encodedDexJar string) {
315		moduleForTests := result.ModuleForTests(name+".impl", "android_common")
316
317		encodeDexRule := moduleForTests.Rule("hiddenAPIEncodeDex")
318		actualUnencodedDexJar := encodeDexRule.Input
319
320		// Make sure that the module has its dex jar encoded.
321		android.AssertStringEquals(t, "encode embedded java_library", unencodedDexJar, actualUnencodedDexJar.String())
322
323		// Make sure that the encoded dex jar is the exported one.
324		errCtx := moduleErrorfTestCtx{}
325		exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath(errCtx).Path()
326		android.AssertPathRelativeToTopEquals(t, "encode embedded java_library", encodedDexJar, exportedDexJar)
327	}
328
329	// The java_library embedded with the java_sdk_library must be dex encoded.
330	t.Run("foo", func(t *testing.T) {
331		expectedUnencodedDexJar := "out/soong/.intermediates/foo.impl/android_common/aligned/foo.jar"
332		expectedEncodedDexJar := "out/soong/.intermediates/foo.impl/android_common/hiddenapi/foo.jar"
333		checkDexEncoded(t, "foo", expectedUnencodedDexJar, expectedEncodedDexJar)
334	})
335}
336