diff options
Diffstat (limited to 'apex/classpath_element_test.go')
-rw-r--r-- | apex/classpath_element_test.go | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go new file mode 100644 index 000000000..0193127b8 --- /dev/null +++ b/apex/classpath_element_test.go @@ -0,0 +1,317 @@ +// 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" + "github.com/google/blueprint" +) + +// 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 { + testContext *android.TestContext + module android.Module + errs []error +} + +func (t *testClasspathElementContext) OtherModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool { + return t.testContext.ModuleHasProvider(module, provider) +} + +func (t *testClasspathElementContext) OtherModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{} { + return t.testContext.ModuleProvider(module, provider) +} + +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"), + 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", + apex_available: [ + "com.android.art", + ], + contents: [ + "baz", + "quuz", + ], + } + + 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", + ], + } + + 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", + ], + } + + bootclasspath_fragment { + name: "non-apex-fragment", + contents: ["othersdklibrary"], + } + + 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", + }, + ], + } + `), + ) + + 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") + + nonApexFragment := result.Module("non-apex-fragment", "android_common") + 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{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 a fragment does not have an associated apex. + t.Run("non apex fragment", func(t *testing.T) { + ctx := newCtx() + elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{nonApexFragment}) + android.FailIfNoMatchingErrors(t, "fragment non-apex-fragment{.*} is not part of an apex", ctx.errs) + expectedElements := java.ClasspathElements{} + 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) + }) +} |