• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 apex
16
17import (
18	"reflect"
19	"testing"
20
21	"android/soong/android"
22	"android/soong/java"
23	"github.com/google/blueprint"
24)
25
26// Contains tests for java.CreateClasspathElements logic from java/classpath_element.go that
27// requires apexes.
28
29// testClasspathElementContext is a ClasspathElementContext suitable for use in tests.
30type testClasspathElementContext struct {
31	testContext *android.TestContext
32	module      android.Module
33	errs        []error
34}
35
36func (t *testClasspathElementContext) OtherModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool {
37	return t.testContext.ModuleHasProvider(module, provider)
38}
39
40func (t *testClasspathElementContext) OtherModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{} {
41	return t.testContext.ModuleProvider(module, provider)
42}
43
44func (t *testClasspathElementContext) ModuleErrorf(fmt string, args ...interface{}) {
45	t.errs = append(t.errs, t.testContext.ModuleErrorf(t.module, fmt, args...))
46}
47
48var _ java.ClasspathElementContext = (*testClasspathElementContext)(nil)
49
50func TestCreateClasspathElements(t *testing.T) {
51	preparer := android.GroupFixturePreparers(
52		prepareForTestWithPlatformBootclasspath,
53		prepareForTestWithArtApex,
54		prepareForTestWithMyapex,
55		// For otherapex.
56		android.FixtureMergeMockFs(android.MockFS{
57			"system/sepolicy/apex/otherapex-file_contexts": nil,
58		}),
59		java.PrepareForTestWithJavaSdkLibraryFiles,
60		java.FixtureWithLastReleaseApis("foo", "othersdklibrary"),
61		java.FixtureConfigureApexBootJars("myapex:bar"),
62		android.FixtureWithRootAndroidBp(`
63		apex {
64			name: "com.android.art",
65			key: "com.android.art.key",
66 			bootclasspath_fragments: [
67				"art-bootclasspath-fragment",
68			],
69			java_libs: [
70				"othersdklibrary",
71			],
72			updatable: false,
73		}
74
75		apex_key {
76			name: "com.android.art.key",
77			public_key: "com.android.art.avbpubkey",
78			private_key: "com.android.art.pem",
79		}
80
81		bootclasspath_fragment {
82			name: "art-bootclasspath-fragment",
83			image_name: "art",
84			apex_available: [
85				"com.android.art",
86			],
87			contents: [
88				"baz",
89				"quuz",
90			],
91			hidden_api: {
92				split_packages: ["*"],
93			},
94		}
95
96		java_library {
97			name: "baz",
98			apex_available: [
99				"com.android.art",
100			],
101			srcs: ["b.java"],
102			installable: true,
103		}
104
105		java_library {
106			name: "quuz",
107			apex_available: [
108				"com.android.art",
109			],
110			srcs: ["b.java"],
111			installable: true,
112		}
113
114		apex {
115			name: "myapex",
116			key: "myapex.key",
117 			bootclasspath_fragments: [
118				"mybootclasspath-fragment",
119			],
120			java_libs: [
121				"othersdklibrary",
122			],
123			updatable: false,
124		}
125
126		apex_key {
127			name: "myapex.key",
128			public_key: "testkey.avbpubkey",
129			private_key: "testkey.pem",
130		}
131
132		bootclasspath_fragment {
133			name: "mybootclasspath-fragment",
134			apex_available: [
135				"myapex",
136			],
137			contents: [
138				"bar",
139			],
140			hidden_api: {
141				split_packages: ["*"],
142			},
143		}
144
145		java_library {
146			name: "bar",
147			srcs: ["b.java"],
148			installable: true,
149			apex_available: ["myapex"],
150			permitted_packages: ["bar"],
151		}
152
153		java_sdk_library {
154			name: "foo",
155			srcs: ["b.java"],
156		}
157
158		java_sdk_library {
159			name: "othersdklibrary",
160			srcs: ["b.java"],
161			shared_library: false,
162			apex_available: [
163				"com.android.art",
164				"myapex",
165			],
166		}
167
168		apex {
169			name: "otherapex",
170			key: "otherapex.key",
171			java_libs: [
172				"otherapexlibrary",
173			],
174			updatable: false,
175		}
176
177		apex_key {
178			name: "otherapex.key",
179			public_key: "testkey.avbpubkey",
180			private_key: "testkey.pem",
181		}
182
183		java_library {
184			name: "otherapexlibrary",
185			srcs: ["b.java"],
186			installable: true,
187			apex_available: ["otherapex"],
188			permitted_packages: ["otherapexlibrary"],
189		}
190
191		platform_bootclasspath {
192			name: "myplatform-bootclasspath",
193
194			fragments: [
195				{
196					apex: "com.android.art",
197					module: "art-bootclasspath-fragment",
198				},
199				{
200					apex: "myapex",
201					module: "mybootclasspath-fragment",
202				},
203			],
204		}
205	`),
206	)
207
208	result := preparer.RunTest(t)
209
210	artFragment := result.Module("art-bootclasspath-fragment", "android_common_apex10000")
211	artBaz := result.Module("baz", "android_common_apex10000")
212	artQuuz := result.Module("quuz", "android_common_apex10000")
213
214	myFragment := result.Module("mybootclasspath-fragment", "android_common_apex10000")
215	myBar := result.Module("bar", "android_common_apex10000")
216
217	other := result.Module("othersdklibrary", "android_common_apex10000")
218
219	otherApexLibrary := result.Module("otherapexlibrary", "android_common_apex10000")
220
221	platformFoo := result.Module("quuz", "android_common")
222
223	bootclasspath := result.Module("myplatform-bootclasspath", "android_common")
224
225	// Use a custom assertion method instead of AssertDeepEquals as the latter formats the output
226	// using %#v which results in meaningless output as ClasspathElements are pointers.
227	assertElementsEquals := func(t *testing.T, message string, expected, actual java.ClasspathElements) {
228		if !reflect.DeepEqual(expected, actual) {
229			t.Errorf("%s: expected:\n  %s\n got:\n  %s", message, expected, actual)
230		}
231	}
232
233	expectFragmentElement := func(module android.Module, contents ...android.Module) java.ClasspathElement {
234		return &java.ClasspathFragmentElement{module, contents}
235	}
236	expectLibraryElement := func(module android.Module) java.ClasspathElement {
237		return &java.ClasspathLibraryElement{module}
238	}
239
240	newCtx := func() *testClasspathElementContext {
241		return &testClasspathElementContext{testContext: result.TestContext, module: bootclasspath}
242	}
243
244	// Verify that CreateClasspathElements works when given valid input.
245	t.Run("art:baz, art:quuz, my:bar, foo", func(t *testing.T) {
246		ctx := newCtx()
247		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, artQuuz, myBar, platformFoo}, []android.Module{artFragment, myFragment})
248		expectedElements := java.ClasspathElements{
249			expectFragmentElement(artFragment, artBaz, artQuuz),
250			expectFragmentElement(myFragment, myBar),
251			expectLibraryElement(platformFoo),
252		}
253		assertElementsEquals(t, "elements", expectedElements, elements)
254	})
255
256	// Verify that CreateClasspathElements detects when an apex has multiple fragments.
257	t.Run("multiple fragments for same apex", func(t *testing.T) {
258		ctx := newCtx()
259		elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{artFragment, artFragment})
260		android.FailIfNoMatchingErrors(t, "apex com.android.art has multiple fragments, art-bootclasspath-fragment{.*} and art-bootclasspath-fragment{.*}", ctx.errs)
261		expectedElements := java.ClasspathElements{}
262		assertElementsEquals(t, "elements", expectedElements, elements)
263	})
264
265	// Verify that CreateClasspathElements detects when a library is in multiple fragments.
266	t.Run("library from multiple fragments", func(t *testing.T) {
267		ctx := newCtx()
268		elements := java.CreateClasspathElements(ctx, []android.Module{other}, []android.Module{artFragment, myFragment})
269		android.FailIfNoMatchingErrors(t, "library othersdklibrary{.*} is in two separate fragments, art-bootclasspath-fragment{.*} and mybootclasspath-fragment{.*}", ctx.errs)
270		expectedElements := java.ClasspathElements{}
271		assertElementsEquals(t, "elements", expectedElements, elements)
272	})
273
274	// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
275	// are separated by a library from another fragment.
276	t.Run("discontiguous separated by fragment", func(t *testing.T) {
277		ctx := newCtx()
278		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, myBar, artQuuz, platformFoo}, []android.Module{artFragment, myFragment})
279		expectedElements := java.ClasspathElements{
280			expectFragmentElement(artFragment, artBaz, artQuuz),
281			expectFragmentElement(myFragment, myBar),
282			expectLibraryElement(platformFoo),
283		}
284		assertElementsEquals(t, "elements", expectedElements, elements)
285		android.FailIfNoMatchingErrors(t, "libraries from the same fragment must be contiguous, however baz{.*} and quuz{os:android,arch:common,apex:apex10000} from fragment art-bootclasspath-fragment{.*} are separated by libraries from fragment mybootclasspath-fragment{.*} like bar{.*}", ctx.errs)
286	})
287
288	// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
289	// are separated by a standalone library.
290	t.Run("discontiguous separated by library", func(t *testing.T) {
291		ctx := newCtx()
292		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, platformFoo, artQuuz, myBar}, []android.Module{artFragment, myFragment})
293		expectedElements := java.ClasspathElements{
294			expectFragmentElement(artFragment, artBaz, artQuuz),
295			expectLibraryElement(platformFoo),
296			expectFragmentElement(myFragment, myBar),
297		}
298		assertElementsEquals(t, "elements", expectedElements, elements)
299		android.FailIfNoMatchingErrors(t, "libraries from the same fragment must be contiguous, however baz{.*} and quuz{os:android,arch:common,apex:apex10000} from fragment art-bootclasspath-fragment{.*} are separated by library quuz{.*}", ctx.errs)
300	})
301
302	// Verify that CreateClasspathElements detects when there a library on the classpath that
303	// indicates it is from an apex the supplied fragments list does not contain a fragment for that
304	// apex.
305	t.Run("no fragment for apex", func(t *testing.T) {
306		ctx := newCtx()
307		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, otherApexLibrary}, []android.Module{artFragment})
308		expectedElements := java.ClasspathElements{
309			expectFragmentElement(artFragment, artBaz),
310		}
311		assertElementsEquals(t, "elements", expectedElements, elements)
312		android.FailIfNoMatchingErrors(t, `library otherapexlibrary{.*} is from apexes \[otherapex\] which have no corresponding fragment in \[art-bootclasspath-fragment{.*}\]`, ctx.errs)
313	})
314}
315