318 lines
10 KiB
Go
318 lines
10 KiB
Go
// 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)
|
|
})
|
|
}
|