• 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		}
92
93		java_library {
94			name: "baz",
95			apex_available: [
96				"com.android.art",
97			],
98			srcs: ["b.java"],
99			installable: true,
100		}
101
102		java_library {
103			name: "quuz",
104			apex_available: [
105				"com.android.art",
106			],
107			srcs: ["b.java"],
108			installable: true,
109		}
110
111		apex {
112			name: "myapex",
113			key: "myapex.key",
114 			bootclasspath_fragments: [
115				"mybootclasspath-fragment",
116			],
117			java_libs: [
118				"othersdklibrary",
119			],
120			updatable: false,
121		}
122
123		apex_key {
124			name: "myapex.key",
125			public_key: "testkey.avbpubkey",
126			private_key: "testkey.pem",
127		}
128
129		bootclasspath_fragment {
130			name: "mybootclasspath-fragment",
131			apex_available: [
132				"myapex",
133			],
134			contents: [
135				"bar",
136			],
137		}
138
139		java_library {
140			name: "bar",
141			srcs: ["b.java"],
142			installable: true,
143			apex_available: ["myapex"],
144			permitted_packages: ["bar"],
145		}
146
147		java_sdk_library {
148			name: "foo",
149			srcs: ["b.java"],
150		}
151
152		java_sdk_library {
153			name: "othersdklibrary",
154			srcs: ["b.java"],
155			shared_library: false,
156			apex_available: [
157				"com.android.art",
158				"myapex",
159			],
160		}
161
162		apex {
163			name: "otherapex",
164			key: "otherapex.key",
165			java_libs: [
166				"otherapexlibrary",
167			],
168			updatable: false,
169		}
170
171		apex_key {
172			name: "otherapex.key",
173			public_key: "testkey.avbpubkey",
174			private_key: "testkey.pem",
175		}
176
177		java_library {
178			name: "otherapexlibrary",
179			srcs: ["b.java"],
180			installable: true,
181			apex_available: ["otherapex"],
182			permitted_packages: ["otherapexlibrary"],
183		}
184
185		platform_bootclasspath {
186			name: "myplatform-bootclasspath",
187
188			fragments: [
189				{
190					apex: "com.android.art",
191					module: "art-bootclasspath-fragment",
192				},
193				{
194					apex: "myapex",
195					module: "mybootclasspath-fragment",
196				},
197			],
198		}
199	`),
200	)
201
202	result := preparer.RunTest(t)
203
204	artFragment := result.Module("art-bootclasspath-fragment", "android_common_apex10000")
205	artBaz := result.Module("baz", "android_common_apex10000")
206	artQuuz := result.Module("quuz", "android_common_apex10000")
207
208	myFragment := result.Module("mybootclasspath-fragment", "android_common_apex10000")
209	myBar := result.Module("bar", "android_common_apex10000")
210
211	other := result.Module("othersdklibrary", "android_common_apex10000")
212
213	otherApexLibrary := result.Module("otherapexlibrary", "android_common_apex10000")
214
215	platformFoo := result.Module("quuz", "android_common")
216
217	bootclasspath := result.Module("myplatform-bootclasspath", "android_common")
218
219	// Use a custom assertion method instead of AssertDeepEquals as the latter formats the output
220	// using %#v which results in meaningless output as ClasspathElements are pointers.
221	assertElementsEquals := func(t *testing.T, message string, expected, actual java.ClasspathElements) {
222		if !reflect.DeepEqual(expected, actual) {
223			t.Errorf("%s: expected:\n  %s\n got:\n  %s", message, expected, actual)
224		}
225	}
226
227	expectFragmentElement := func(module android.Module, contents ...android.Module) java.ClasspathElement {
228		return &java.ClasspathFragmentElement{module, contents}
229	}
230	expectLibraryElement := func(module android.Module) java.ClasspathElement {
231		return &java.ClasspathLibraryElement{module}
232	}
233
234	newCtx := func() *testClasspathElementContext {
235		return &testClasspathElementContext{testContext: result.TestContext, module: bootclasspath}
236	}
237
238	// Verify that CreateClasspathElements works when given valid input.
239	t.Run("art:baz, art:quuz, my:bar, foo", func(t *testing.T) {
240		ctx := newCtx()
241		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, artQuuz, myBar, platformFoo}, []android.Module{artFragment, myFragment})
242		expectedElements := java.ClasspathElements{
243			expectFragmentElement(artFragment, artBaz, artQuuz),
244			expectFragmentElement(myFragment, myBar),
245			expectLibraryElement(platformFoo),
246		}
247		assertElementsEquals(t, "elements", expectedElements, elements)
248	})
249
250	// Verify that CreateClasspathElements detects when an apex has multiple fragments.
251	t.Run("multiple fragments for same apex", func(t *testing.T) {
252		ctx := newCtx()
253		elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{artFragment, artFragment})
254		android.FailIfNoMatchingErrors(t, "apex com.android.art has multiple fragments, art-bootclasspath-fragment{.*} and art-bootclasspath-fragment{.*}", ctx.errs)
255		expectedElements := java.ClasspathElements{}
256		assertElementsEquals(t, "elements", expectedElements, elements)
257	})
258
259	// Verify that CreateClasspathElements detects when a library is in multiple fragments.
260	t.Run("library from multiple fragments", func(t *testing.T) {
261		ctx := newCtx()
262		elements := java.CreateClasspathElements(ctx, []android.Module{other}, []android.Module{artFragment, myFragment})
263		android.FailIfNoMatchingErrors(t, "library othersdklibrary{.*} is in two separate fragments, art-bootclasspath-fragment{.*} and mybootclasspath-fragment{.*}", ctx.errs)
264		expectedElements := java.ClasspathElements{}
265		assertElementsEquals(t, "elements", expectedElements, elements)
266	})
267
268	// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
269	// are separated by a library from another fragment.
270	t.Run("discontiguous separated by fragment", func(t *testing.T) {
271		ctx := newCtx()
272		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, myBar, artQuuz, platformFoo}, []android.Module{artFragment, myFragment})
273		expectedElements := java.ClasspathElements{
274			expectFragmentElement(artFragment, artBaz, artQuuz),
275			expectFragmentElement(myFragment, myBar),
276			expectLibraryElement(platformFoo),
277		}
278		assertElementsEquals(t, "elements", expectedElements, elements)
279		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)
280	})
281
282	// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
283	// are separated by a standalone library.
284	t.Run("discontiguous separated by library", func(t *testing.T) {
285		ctx := newCtx()
286		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, platformFoo, artQuuz, myBar}, []android.Module{artFragment, myFragment})
287		expectedElements := java.ClasspathElements{
288			expectFragmentElement(artFragment, artBaz, artQuuz),
289			expectLibraryElement(platformFoo),
290			expectFragmentElement(myFragment, myBar),
291		}
292		assertElementsEquals(t, "elements", expectedElements, elements)
293		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)
294	})
295
296	// Verify that CreateClasspathElements detects when there a library on the classpath that
297	// indicates it is from an apex the supplied fragments list does not contain a fragment for that
298	// apex.
299	t.Run("no fragment for apex", func(t *testing.T) {
300		ctx := newCtx()
301		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, otherApexLibrary}, []android.Module{artFragment})
302		expectedElements := java.ClasspathElements{
303			expectFragmentElement(artFragment, artBaz),
304		}
305		assertElementsEquals(t, "elements", expectedElements, elements)
306		android.FailIfNoMatchingErrors(t, `library otherapexlibrary{.*} is from apexes \[otherapex\] which have no corresponding fragment in \[art-bootclasspath-fragment{.*}\]`, ctx.errs)
307	})
308}
309