1// Copyright (C) 2021 The Android Open Source Project
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	"strings"
19	"testing"
20
21	"android/soong/android"
22	"android/soong/dexpreopt"
23)
24
25// Contains some simple tests for bootclasspath_fragment logic, additional tests can be found in
26// apex/bootclasspath_fragment_test.go as the ART boot image requires modules from the ART apex.
27
28var prepareForTestWithBootclasspathFragment = android.GroupFixturePreparers(
29	PrepareForTestWithJavaDefaultModules,
30	dexpreopt.PrepareForTestByEnablingDexpreopt,
31)
32
33func TestBootclasspathFragment_UnknownImageName(t *testing.T) {
34	prepareForTestWithBootclasspathFragment.
35		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
36			`\Qimage_name: unknown image name "unknown", expected "art"\E`)).
37		RunTestWithBp(t, `
38			bootclasspath_fragment {
39				name: "unknown-bootclasspath-fragment",
40				image_name: "unknown",
41				contents: ["foo"],
42			}
43
44			java_library {
45				name: "foo",
46				srcs: ["foo.java"],
47				installable: true,
48			}
49		`)
50}
51
52func TestPrebuiltBootclasspathFragment_UnknownImageName(t *testing.T) {
53	prepareForTestWithBootclasspathFragment.
54		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
55			`\Qimage_name: unknown image name "unknown", expected "art"\E`)).
56		RunTestWithBp(t, `
57			prebuilt_bootclasspath_fragment {
58				name: "unknown-bootclasspath-fragment",
59				image_name: "unknown",
60				contents: ["foo"],
61			}
62
63			java_import {
64				name: "foo",
65				jars: ["foo.jar"],
66			}
67		`)
68}
69
70func TestBootclasspathFragmentInconsistentArtConfiguration_Platform(t *testing.T) {
71	android.GroupFixturePreparers(
72		prepareForTestWithBootclasspathFragment,
73		dexpreopt.FixtureSetArtBootJars("platform:foo", "apex:bar"),
74	).
75		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
76			`\QArtApexJars is invalid as it requests a platform variant of "foo"\E`)).
77		RunTestWithBp(t, `
78			bootclasspath_fragment {
79				name: "bootclasspath-fragment",
80				image_name: "art",
81				contents: ["foo", "bar"],
82				apex_available: [
83					"apex",
84				],
85			}
86
87			java_library {
88				name: "foo",
89				srcs: ["foo.java"],
90				installable: true,
91			}
92
93			java_library {
94				name: "bar",
95				srcs: ["bar.java"],
96				installable: true,
97			}
98		`)
99}
100
101func TestBootclasspathFragmentInconsistentArtConfiguration_ApexMixture(t *testing.T) {
102	android.GroupFixturePreparers(
103		prepareForTestWithBootclasspathFragment,
104		dexpreopt.FixtureSetArtBootJars("apex1:foo", "apex2:bar"),
105	).
106		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
107			`\QArtApexJars configuration is inconsistent, expected all jars to be in the same apex but it specifies apex "apex1" and "apex2"\E`)).
108		RunTestWithBp(t, `
109			bootclasspath_fragment {
110				name: "bootclasspath-fragment",
111				image_name: "art",
112				contents: ["foo", "bar"],
113				apex_available: [
114					"apex1",
115					"apex2",
116				],
117			}
118
119			java_library {
120				name: "foo",
121				srcs: ["foo.java"],
122				installable: true,
123			}
124
125			java_library {
126				name: "bar",
127				srcs: ["bar.java"],
128				installable: true,
129			}
130		`)
131}
132
133func TestBootclasspathFragment_Coverage(t *testing.T) {
134	prepareWithBp := android.FixtureWithRootAndroidBp(`
135		bootclasspath_fragment {
136			name: "myfragment",
137			contents: [
138				"mybootlib",
139			],
140			api: {
141				stub_libs: [
142					"mysdklibrary",
143				],
144			},
145			coverage: {
146				contents: [
147					"coveragelib",
148				],
149				api: {
150					stub_libs: [
151						"mycoveragestubs",
152					],
153				},
154			},
155			hidden_api: {
156				split_packages: ["*"],
157			},
158		}
159
160		java_library {
161			name: "mybootlib",
162			srcs: ["Test.java"],
163			system_modules: "none",
164			sdk_version: "none",
165			compile_dex: true,
166		}
167
168		java_library {
169			name: "coveragelib",
170			srcs: ["Test.java"],
171			system_modules: "none",
172			sdk_version: "none",
173			compile_dex: true,
174		}
175
176		java_sdk_library {
177			name: "mysdklibrary",
178			srcs: ["Test.java"],
179			compile_dex: true,
180			public: {enabled: true},
181			system: {enabled: true},
182		}
183
184		java_sdk_library {
185			name: "mycoveragestubs",
186			srcs: ["Test.java"],
187			compile_dex: true,
188			public: {enabled: true},
189		}
190	`)
191
192	checkContents := func(t *testing.T, result *android.TestResult, expected ...string) {
193		module := result.Module("myfragment", "android_common").(*BootclasspathFragmentModule)
194		android.AssertArrayString(t, "contents property", expected, module.properties.Contents)
195	}
196
197	preparer := android.GroupFixturePreparers(
198		prepareForTestWithBootclasspathFragment,
199		PrepareForTestWithJavaSdkLibraryFiles,
200		FixtureWithLastReleaseApis("mysdklibrary", "mycoveragestubs"),
201		FixtureConfigureApexBootJars("someapex:mybootlib"),
202		prepareWithBp,
203	)
204
205	t.Run("without coverage", func(t *testing.T) {
206		result := preparer.RunTest(t)
207		checkContents(t, result, "mybootlib")
208	})
209
210	t.Run("with coverage", func(t *testing.T) {
211		result := android.GroupFixturePreparers(
212			prepareForTestWithFrameworkJacocoInstrumentation,
213			preparer,
214		).RunTest(t)
215		checkContents(t, result, "mybootlib", "coveragelib")
216	})
217}
218
219func TestBootclasspathFragment_StubLibs(t *testing.T) {
220	result := android.GroupFixturePreparers(
221		prepareForTestWithBootclasspathFragment,
222		PrepareForTestWithJavaSdkLibraryFiles,
223		FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"),
224		FixtureConfigureApexBootJars("someapex:mysdklibrary"),
225		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
226			variables.BuildFlags = map[string]string{
227				"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
228			}
229		}),
230	).RunTestWithBp(t, `
231		bootclasspath_fragment {
232			name: "myfragment",
233			contents: ["mysdklibrary"],
234			api: {
235				stub_libs: [
236					"mystublib",
237					"myothersdklibrary",
238				],
239			},
240			core_platform_api: {
241				stub_libs: ["mycoreplatform.stubs"],
242			},
243			hidden_api: {
244				split_packages: ["*"],
245			},
246		}
247
248		java_library {
249			name: "mystublib",
250			srcs: ["Test.java"],
251			system_modules: "none",
252			sdk_version: "none",
253			compile_dex: true,
254		}
255
256		java_sdk_library {
257			name: "mysdklibrary",
258			srcs: ["a.java"],
259			shared_library: false,
260			public: {enabled: true},
261			system: {enabled: true},
262		}
263
264		java_sdk_library {
265			name: "myothersdklibrary",
266			srcs: ["a.java"],
267			shared_library: false,
268			public: {enabled: true},
269		}
270
271		java_sdk_library {
272			name: "mycoreplatform",
273			srcs: ["a.java"],
274			shared_library: false,
275			public: {enabled: true},
276		}
277	`)
278
279	fragment := result.Module("myfragment", "android_common")
280	info, _ := android.SingletonModuleProvider(result, fragment, HiddenAPIInfoProvider)
281
282	stubsJar := "out/soong/.intermediates/mystublib/android_common/dex/mystublib.jar"
283
284	// Stubs jars for mysdklibrary
285	publicStubsJar := "out/soong/.intermediates/mysdklibrary.stubs.exportable/android_common/dex/mysdklibrary.stubs.exportable.jar"
286	systemStubsJar := "out/soong/.intermediates/mysdklibrary.stubs.exportable.system/android_common/dex/mysdklibrary.stubs.exportable.system.jar"
287
288	// Stubs jars for myothersdklibrary
289	otherPublicStubsJar := "out/soong/.intermediates/myothersdklibrary.stubs.exportable/android_common/dex/myothersdklibrary.stubs.exportable.jar"
290
291	// Check that SdkPublic uses public stubs for all sdk libraries.
292	android.AssertPathsRelativeToTopEquals(t, "public dex stubs jar", []string{otherPublicStubsJar, publicStubsJar, stubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(PublicHiddenAPIScope))
293
294	// Check that SdkSystem uses system stubs for mysdklibrary and public stubs for myothersdklibrary
295	// as it does not provide system stubs.
296	android.AssertPathsRelativeToTopEquals(t, "system dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(SystemHiddenAPIScope))
297
298	// Check that SdkTest also uses system stubs for mysdklibrary as it does not provide test stubs
299	// and public stubs for myothersdklibrary as it does not provide test stubs either.
300	android.AssertPathsRelativeToTopEquals(t, "test dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(TestHiddenAPIScope))
301
302	// Check that SdkCorePlatform uses public stubs from the mycoreplatform library.
303	corePlatformStubsJar := "out/soong/.intermediates/mycoreplatform.stubs/android_common/dex/mycoreplatform.stubs.jar"
304	android.AssertPathsRelativeToTopEquals(t, "core platform dex stubs jar", []string{corePlatformStubsJar}, info.TransitiveStubDexJarsByScope.StubDexJarsForScope(CorePlatformHiddenAPIScope))
305
306	// Check the widest stubs.. The list contains the widest stub dex jar provided by each module.
307	expectedWidestPaths := []string{
308		// mycoreplatform's widest API is core platform.
309		corePlatformStubsJar,
310
311		// myothersdklibrary's widest API is public.
312		otherPublicStubsJar,
313
314		// sdklibrary's widest API is system.
315		systemStubsJar,
316
317		// mystublib's only provides one API and so it must be the widest.
318		stubsJar,
319	}
320
321	android.AssertPathsRelativeToTopEquals(t, "widest dex stubs jar", expectedWidestPaths, info.TransitiveStubDexJarsByScope.StubDexJarsForWidestAPIScope())
322}
323
324func TestFromTextWidestApiScope(t *testing.T) {
325	result := android.GroupFixturePreparers(
326		prepareForTestWithBootclasspathFragment,
327		PrepareForTestWithJavaSdkLibraryFiles,
328		android.FixtureModifyConfig(func(config android.Config) {
329			config.SetBuildFromTextStub(true)
330		}),
331		FixtureWithLastReleaseApis("mysdklibrary", "android-non-updatable"),
332		FixtureConfigureApexBootJars("someapex:mysdklibrary"),
333	).RunTestWithBp(t, `
334		bootclasspath_fragment {
335			name: "myfragment",
336			contents: ["mysdklibrary"],
337			additional_stubs: [
338				"android-non-updatable",
339			],
340			hidden_api: {
341				split_packages: ["*"],
342			},
343		}
344		java_sdk_library {
345			name: "mysdklibrary",
346			srcs: ["a.java"],
347			shared_library: false,
348			public: {enabled: true},
349			system: {enabled: true},
350		}
351		java_sdk_library {
352			name: "android-non-updatable",
353			srcs: ["b.java"],
354			compile_dex: true,
355			public: {
356				enabled: true,
357			},
358			system: {
359				enabled: true,
360			},
361			test: {
362				enabled: true,
363			},
364			module_lib: {
365				enabled: true,
366			},
367		}
368	`)
369
370	fragment := result.ModuleForTests("myfragment", "android_common")
371	dependencyStubDexFlag := "--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.test_module_lib/android_common/dex/android-non-updatable.stubs.test_module_lib.jar"
372	stubFlagsCommand := fragment.Output("modular-hiddenapi/stub-flags.csv").RuleParams.Command
373	android.AssertStringDoesContain(t,
374		"Stub flags generating command does not include the expected dependency stub dex file",
375		stubFlagsCommand, dependencyStubDexFlag)
376}
377
378func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) {
379	result := android.GroupFixturePreparers(
380		prepareForTestWithBootclasspathFragment,
381		PrepareForTestWithJavaSdkLibraryFiles,
382		FixtureWithLastReleaseApis("mysdklibrary", "mynewlibrary"),
383		FixtureConfigureApexBootJars("myapex:mybootlib", "myapex:mynewlibrary"),
384		android.MockFS{
385			"my-blocked.txt":                   nil,
386			"my-max-target-o-low-priority.txt": nil,
387			"my-max-target-p.txt":              nil,
388			"my-max-target-q.txt":              nil,
389			"my-max-target-r-low-priority.txt": nil,
390			"my-removed.txt":                   nil,
391			"my-unsupported-packages.txt":      nil,
392			"my-unsupported.txt":               nil,
393			"my-new-max-target-q.txt":          nil,
394		}.AddToFixture(),
395		android.FixtureWithRootAndroidBp(`
396			bootclasspath_fragment {
397				name: "mybootclasspathfragment",
398				apex_available: ["myapex"],
399				contents: ["mybootlib", "mynewlibrary"],
400				hidden_api: {
401					unsupported: [
402							"my-unsupported.txt",
403					],
404					removed: [
405							"my-removed.txt",
406					],
407					max_target_r_low_priority: [
408							"my-max-target-r-low-priority.txt",
409					],
410					max_target_q: [
411							"my-max-target-q.txt",
412					],
413					max_target_p: [
414							"my-max-target-p.txt",
415					],
416					max_target_o_low_priority: [
417							"my-max-target-o-low-priority.txt",
418					],
419					blocked: [
420							"my-blocked.txt",
421					],
422					unsupported_packages: [
423							"my-unsupported-packages.txt",
424					],
425					split_packages: ["sdklibrary"],
426					package_prefixes: ["sdklibrary.all.mine"],
427					single_packages: ["sdklibrary.mine"],
428				},
429			}
430
431			java_library {
432				name: "mybootlib",
433				apex_available: ["myapex"],
434				srcs: ["Test.java"],
435				system_modules: "none",
436				sdk_version: "none",
437				min_sdk_version: "1",
438				compile_dex: true,
439				permitted_packages: ["mybootlib"],
440			}
441
442			java_sdk_library {
443				name: "mynewlibrary",
444				apex_available: ["myapex"],
445				srcs: ["Test.java"],
446				min_sdk_version: "10",
447				compile_dex: true,
448				public: {enabled: true},
449				permitted_packages: ["mysdklibrary"],
450				hidden_api: {
451					max_target_q: [
452							"my-new-max-target-q.txt",
453					],
454					split_packages: ["sdklibrary", "newlibrary"],
455					package_prefixes: ["newlibrary.all.mine"],
456					single_packages: ["newlibrary.mine"],
457				},
458			}
459		`),
460	).RunTest(t)
461
462	// Make sure that the library exports hidden API properties for use by the bootclasspath_fragment.
463	library := result.Module("mynewlibrary", "android_common")
464	info, _ := android.SingletonModuleProvider(result, library, hiddenAPIPropertyInfoProvider)
465	android.AssertArrayString(t, "split packages", []string{"sdklibrary", "newlibrary"}, info.SplitPackages)
466	android.AssertArrayString(t, "package prefixes", []string{"newlibrary.all.mine"}, info.PackagePrefixes)
467	android.AssertArrayString(t, "single packages", []string{"newlibrary.mine"}, info.SinglePackages)
468	for _, c := range HiddenAPIFlagFileCategories {
469		expectedMaxTargetQPaths := []string(nil)
470		if c.PropertyName() == "max_target_q" {
471			expectedMaxTargetQPaths = []string{"my-new-max-target-q.txt"}
472		}
473		android.AssertPathsRelativeToTopEquals(t, c.PropertyName(), expectedMaxTargetQPaths, info.FlagFilesByCategory[c])
474	}
475
476	// Make sure that the signature-patterns.csv is passed all the appropriate package properties
477	// from the bootclasspath_fragment and its contents.
478	fragment := result.ModuleForTests("mybootclasspathfragment", "android_common")
479	rule := fragment.Output("modular-hiddenapi/signature-patterns.csv")
480	expectedCommand := strings.Join([]string{
481		"--split-package newlibrary",
482		"--split-package sdklibrary",
483		"--package-prefix newlibrary.all.mine",
484		"--package-prefix sdklibrary.all.mine",
485		"--single-package newlibrary.mine",
486		"--single-package sdklibrary",
487	}, " ")
488	android.AssertStringDoesContain(t, "signature patterns command", rule.RuleParams.Command, expectedCommand)
489}
490
491func TestBootclasspathFragment_Test(t *testing.T) {
492	result := android.GroupFixturePreparers(
493		prepareForTestWithBootclasspathFragment,
494		PrepareForTestWithJavaSdkLibraryFiles,
495		FixtureWithLastReleaseApis("mysdklibrary"),
496	).RunTestWithBp(t, `
497		bootclasspath_fragment {
498			name: "myfragment",
499			contents: ["mysdklibrary"],
500			hidden_api: {
501				split_packages: [],
502			},
503		}
504
505		bootclasspath_fragment_test {
506			name: "a_test_fragment",
507			contents: ["mysdklibrary"],
508			hidden_api: {
509				split_packages: [],
510			},
511		}
512
513
514		java_sdk_library {
515			name: "mysdklibrary",
516			srcs: ["a.java"],
517			shared_library: false,
518			public: {enabled: true},
519			system: {enabled: true},
520		}
521	`)
522
523	fragment := result.Module("myfragment", "android_common").(*BootclasspathFragmentModule)
524	android.AssertBoolEquals(t, "not a test fragment", false, fragment.isTestFragment())
525
526	fragment = result.Module("a_test_fragment", "android_common").(*BootclasspathFragmentModule)
527	android.AssertBoolEquals(t, "is a test fragment by type", true, fragment.isTestFragment())
528}
529