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