| // Copyright (C) 2021 The Android Open Source Project | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | package apex | 
 |  | 
 | import ( | 
 | 	"reflect" | 
 | 	"testing" | 
 |  | 
 | 	"android/soong/android" | 
 | 	"android/soong/java" | 
 | ) | 
 |  | 
 | // Contains tests for java.CreateClasspathElements logic from java/classpath_element.go that | 
 | // requires apexes. | 
 |  | 
 | // testClasspathElementContext is a ClasspathElementContext suitable for use in tests. | 
 | type testClasspathElementContext struct { | 
 | 	android.OtherModuleProviderContext | 
 | 	testContext *android.TestContext | 
 | 	module      android.Module | 
 | 	errs        []error | 
 | } | 
 |  | 
 | func (t *testClasspathElementContext) ModuleErrorf(fmt string, args ...interface{}) { | 
 | 	t.errs = append(t.errs, t.testContext.ModuleErrorf(t.module, fmt, args...)) | 
 | } | 
 |  | 
 | var _ java.ClasspathElementContext = (*testClasspathElementContext)(nil) | 
 |  | 
 | func TestCreateClasspathElements(t *testing.T) { | 
 | 	preparer := android.GroupFixturePreparers( | 
 | 		prepareForTestWithPlatformBootclasspath, | 
 | 		prepareForTestWithArtApex, | 
 | 		prepareForTestWithMyapex, | 
 | 		// For otherapex. | 
 | 		android.FixtureMergeMockFs(android.MockFS{ | 
 | 			"system/sepolicy/apex/otherapex-file_contexts": nil, | 
 | 		}), | 
 | 		java.PrepareForTestWithJavaSdkLibraryFiles, | 
 | 		java.FixtureWithLastReleaseApis("foo", "othersdklibrary"), | 
 | 		java.FixtureConfigureApexBootJars("myapex:bar"), | 
 | 		android.FixtureWithRootAndroidBp(` | 
 | 		apex { | 
 | 			name: "com.android.art", | 
 | 			key: "com.android.art.key", | 
 |  			bootclasspath_fragments: [ | 
 | 				"art-bootclasspath-fragment", | 
 | 			], | 
 | 			java_libs: [ | 
 | 				"othersdklibrary", | 
 | 			], | 
 | 			updatable: false, | 
 | 		} | 
 |  | 
 | 		apex_key { | 
 | 			name: "com.android.art.key", | 
 | 			public_key: "com.android.art.avbpubkey", | 
 | 			private_key: "com.android.art.pem", | 
 | 		} | 
 |  | 
 | 		bootclasspath_fragment { | 
 | 			name: "art-bootclasspath-fragment", | 
 | 			image_name: "art", | 
 | 			apex_available: [ | 
 | 				"com.android.art", | 
 | 			], | 
 | 			contents: [ | 
 | 				"baz", | 
 | 				"quuz", | 
 | 			], | 
 | 			hidden_api: { | 
 | 				split_packages: ["*"], | 
 | 			}, | 
 | 		} | 
 |  | 
 | 		java_library { | 
 | 			name: "baz", | 
 | 			apex_available: [ | 
 | 				"com.android.art", | 
 | 			], | 
 | 			srcs: ["b.java"], | 
 | 			installable: true, | 
 | 		} | 
 |  | 
 | 		java_library { | 
 | 			name: "quuz", | 
 | 			apex_available: [ | 
 | 				"com.android.art", | 
 | 			], | 
 | 			srcs: ["b.java"], | 
 | 			installable: true, | 
 | 		} | 
 |  | 
 | 		apex { | 
 | 			name: "myapex", | 
 | 			key: "myapex.key", | 
 |  			bootclasspath_fragments: [ | 
 | 				"mybootclasspath-fragment", | 
 | 			], | 
 | 			java_libs: [ | 
 | 				"othersdklibrary", | 
 | 			], | 
 | 			updatable: false, | 
 | 		} | 
 |  | 
 | 		apex_key { | 
 | 			name: "myapex.key", | 
 | 			public_key: "testkey.avbpubkey", | 
 | 			private_key: "testkey.pem", | 
 | 		} | 
 |  | 
 | 		bootclasspath_fragment { | 
 | 			name: "mybootclasspath-fragment", | 
 | 			apex_available: [ | 
 | 				"myapex", | 
 | 			], | 
 | 			contents: [ | 
 | 				"bar", | 
 | 			], | 
 | 			hidden_api: { | 
 | 				split_packages: ["*"], | 
 | 			}, | 
 | 		} | 
 |  | 
 | 		java_library { | 
 | 			name: "bar", | 
 | 			srcs: ["b.java"], | 
 | 			installable: true, | 
 | 			apex_available: ["myapex"], | 
 | 			permitted_packages: ["bar"], | 
 | 		} | 
 |  | 
 | 		java_sdk_library { | 
 | 			name: "foo", | 
 | 			srcs: ["b.java"], | 
 | 		} | 
 |  | 
 | 		java_sdk_library { | 
 | 			name: "othersdklibrary", | 
 | 			srcs: ["b.java"], | 
 | 			shared_library: false, | 
 | 			apex_available: [ | 
 | 				"com.android.art", | 
 | 				"myapex", | 
 | 			], | 
 | 		} | 
 |  | 
 | 		apex { | 
 | 			name: "otherapex", | 
 | 			key: "otherapex.key", | 
 | 			java_libs: [ | 
 | 				"otherapexlibrary", | 
 | 			], | 
 | 			updatable: false, | 
 | 		} | 
 |  | 
 | 		apex_key { | 
 | 			name: "otherapex.key", | 
 | 			public_key: "testkey.avbpubkey", | 
 | 			private_key: "testkey.pem", | 
 | 		} | 
 |  | 
 | 		java_library { | 
 | 			name: "otherapexlibrary", | 
 | 			srcs: ["b.java"], | 
 | 			installable: true, | 
 | 			apex_available: ["otherapex"], | 
 | 			permitted_packages: ["otherapexlibrary"], | 
 | 		} | 
 |  | 
 | 		platform_bootclasspath { | 
 | 			name: "myplatform-bootclasspath", | 
 |  | 
 | 			fragments: [ | 
 | 				{ | 
 | 					apex: "com.android.art", | 
 | 					module: "art-bootclasspath-fragment", | 
 | 				}, | 
 | 				{ | 
 | 					apex: "myapex", | 
 | 					module: "mybootclasspath-fragment", | 
 | 				}, | 
 | 			], | 
 | 		} | 
 | 	`), | 
 | 	) | 
 |  | 
 | 	result := preparer.RunTest(t) | 
 |  | 
 | 	artFragment := result.Module("art-bootclasspath-fragment", "android_common_apex10000") | 
 | 	artBaz := result.Module("baz", "android_common_apex10000") | 
 | 	artQuuz := result.Module("quuz", "android_common_apex10000") | 
 |  | 
 | 	myFragment := result.Module("mybootclasspath-fragment", "android_common_apex10000") | 
 | 	myBar := result.Module("bar", "android_common_apex10000") | 
 |  | 
 | 	other := result.Module("othersdklibrary", "android_common_apex10000") | 
 |  | 
 | 	otherApexLibrary := result.Module("otherapexlibrary", "android_common_apex10000") | 
 |  | 
 | 	platformFoo := result.Module("quuz", "android_common") | 
 |  | 
 | 	bootclasspath := result.Module("myplatform-bootclasspath", "android_common") | 
 |  | 
 | 	// Use a custom assertion method instead of AssertDeepEquals as the latter formats the output | 
 | 	// using %#v which results in meaningless output as ClasspathElements are pointers. | 
 | 	assertElementsEquals := func(t *testing.T, message string, expected, actual java.ClasspathElements) { | 
 | 		if !reflect.DeepEqual(expected, actual) { | 
 | 			t.Errorf("%s: expected:\n  %s\n got:\n  %s", message, expected, actual) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	expectFragmentElement := func(module android.Module, contents ...android.Module) java.ClasspathElement { | 
 | 		return &java.ClasspathFragmentElement{module, contents} | 
 | 	} | 
 | 	expectLibraryElement := func(module android.Module) java.ClasspathElement { | 
 | 		return &java.ClasspathLibraryElement{module} | 
 | 	} | 
 |  | 
 | 	newCtx := func() *testClasspathElementContext { | 
 | 		return &testClasspathElementContext{ | 
 | 			OtherModuleProviderContext: result.TestContext.OtherModuleProviderAdaptor(), | 
 | 			testContext:                result.TestContext, | 
 | 			module:                     bootclasspath, | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// Verify that CreateClasspathElements works when given valid input. | 
 | 	t.Run("art:baz, art:quuz, my:bar, foo", func(t *testing.T) { | 
 | 		ctx := newCtx() | 
 | 		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, artQuuz, myBar, platformFoo}, []android.Module{artFragment, myFragment}) | 
 | 		expectedElements := java.ClasspathElements{ | 
 | 			expectFragmentElement(artFragment, artBaz, artQuuz), | 
 | 			expectFragmentElement(myFragment, myBar), | 
 | 			expectLibraryElement(platformFoo), | 
 | 		} | 
 | 		assertElementsEquals(t, "elements", expectedElements, elements) | 
 | 	}) | 
 |  | 
 | 	// Verify that CreateClasspathElements detects when an apex has multiple fragments. | 
 | 	t.Run("multiple fragments for same apex", func(t *testing.T) { | 
 | 		ctx := newCtx() | 
 | 		elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{artFragment, artFragment}) | 
 | 		android.FailIfNoMatchingErrors(t, "apex com.android.art has multiple fragments, art-bootclasspath-fragment{.*} and art-bootclasspath-fragment{.*}", ctx.errs) | 
 | 		expectedElements := java.ClasspathElements{} | 
 | 		assertElementsEquals(t, "elements", expectedElements, elements) | 
 | 	}) | 
 |  | 
 | 	// Verify that CreateClasspathElements detects when a library is in multiple fragments. | 
 | 	t.Run("library from multiple fragments", func(t *testing.T) { | 
 | 		ctx := newCtx() | 
 | 		elements := java.CreateClasspathElements(ctx, []android.Module{other}, []android.Module{artFragment, myFragment}) | 
 | 		android.FailIfNoMatchingErrors(t, "library othersdklibrary{.*} is in two separate fragments, art-bootclasspath-fragment{.*} and mybootclasspath-fragment{.*}", ctx.errs) | 
 | 		expectedElements := java.ClasspathElements{} | 
 | 		assertElementsEquals(t, "elements", expectedElements, elements) | 
 | 	}) | 
 |  | 
 | 	// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and | 
 | 	// are separated by a library from another fragment. | 
 | 	t.Run("discontiguous separated by fragment", func(t *testing.T) { | 
 | 		ctx := newCtx() | 
 | 		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, myBar, artQuuz, platformFoo}, []android.Module{artFragment, myFragment}) | 
 | 		expectedElements := java.ClasspathElements{ | 
 | 			expectFragmentElement(artFragment, artBaz, artQuuz), | 
 | 			expectFragmentElement(myFragment, myBar), | 
 | 			expectLibraryElement(platformFoo), | 
 | 		} | 
 | 		assertElementsEquals(t, "elements", expectedElements, elements) | 
 | 		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) | 
 | 	}) | 
 |  | 
 | 	// Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and | 
 | 	// are separated by a standalone library. | 
 | 	t.Run("discontiguous separated by library", func(t *testing.T) { | 
 | 		ctx := newCtx() | 
 | 		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, platformFoo, artQuuz, myBar}, []android.Module{artFragment, myFragment}) | 
 | 		expectedElements := java.ClasspathElements{ | 
 | 			expectFragmentElement(artFragment, artBaz, artQuuz), | 
 | 			expectLibraryElement(platformFoo), | 
 | 			expectFragmentElement(myFragment, myBar), | 
 | 		} | 
 | 		assertElementsEquals(t, "elements", expectedElements, elements) | 
 | 		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) | 
 | 	}) | 
 |  | 
 | 	// Verify that CreateClasspathElements detects when there a library on the classpath that | 
 | 	// indicates it is from an apex the supplied fragments list does not contain a fragment for that | 
 | 	// apex. | 
 | 	t.Run("no fragment for apex", func(t *testing.T) { | 
 | 		ctx := newCtx() | 
 | 		elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, otherApexLibrary}, []android.Module{artFragment}) | 
 | 		expectedElements := java.ClasspathElements{ | 
 | 			expectFragmentElement(artFragment, artBaz), | 
 | 		} | 
 | 		assertElementsEquals(t, "elements", expectedElements, elements) | 
 | 		android.FailIfNoMatchingErrors(t, `library otherapexlibrary{.*} is from apexes \[otherapex\] which have no corresponding fragment in \[art-bootclasspath-fragment{.*}\]`, ctx.errs) | 
 | 	}) | 
 | } |