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