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 hidden_api: { 92 split_packages: ["*"], 93 }, 94 } 95 96 java_library { 97 name: "baz", 98 apex_available: [ 99 "com.android.art", 100 ], 101 srcs: ["b.java"], 102 installable: true, 103 } 104 105 java_library { 106 name: "quuz", 107 apex_available: [ 108 "com.android.art", 109 ], 110 srcs: ["b.java"], 111 installable: true, 112 } 113 114 apex { 115 name: "myapex", 116 key: "myapex.key", 117 bootclasspath_fragments: [ 118 "mybootclasspath-fragment", 119 ], 120 java_libs: [ 121 "othersdklibrary", 122 ], 123 updatable: false, 124 } 125 126 apex_key { 127 name: "myapex.key", 128 public_key: "testkey.avbpubkey", 129 private_key: "testkey.pem", 130 } 131 132 bootclasspath_fragment { 133 name: "mybootclasspath-fragment", 134 apex_available: [ 135 "myapex", 136 ], 137 contents: [ 138 "bar", 139 ], 140 hidden_api: { 141 split_packages: ["*"], 142 }, 143 } 144 145 java_library { 146 name: "bar", 147 srcs: ["b.java"], 148 installable: true, 149 apex_available: ["myapex"], 150 permitted_packages: ["bar"], 151 } 152 153 java_sdk_library { 154 name: "foo", 155 srcs: ["b.java"], 156 } 157 158 java_sdk_library { 159 name: "othersdklibrary", 160 srcs: ["b.java"], 161 shared_library: false, 162 apex_available: [ 163 "com.android.art", 164 "myapex", 165 ], 166 } 167 168 apex { 169 name: "otherapex", 170 key: "otherapex.key", 171 java_libs: [ 172 "otherapexlibrary", 173 ], 174 updatable: false, 175 } 176 177 apex_key { 178 name: "otherapex.key", 179 public_key: "testkey.avbpubkey", 180 private_key: "testkey.pem", 181 } 182 183 java_library { 184 name: "otherapexlibrary", 185 srcs: ["b.java"], 186 installable: true, 187 apex_available: ["otherapex"], 188 permitted_packages: ["otherapexlibrary"], 189 } 190 191 platform_bootclasspath { 192 name: "myplatform-bootclasspath", 193 194 fragments: [ 195 { 196 apex: "com.android.art", 197 module: "art-bootclasspath-fragment", 198 }, 199 { 200 apex: "myapex", 201 module: "mybootclasspath-fragment", 202 }, 203 ], 204 } 205 `), 206 ) 207 208 result := preparer.RunTest(t) 209 210 artFragment := result.Module("art-bootclasspath-fragment", "android_common_apex10000") 211 artBaz := result.Module("baz", "android_common_apex10000") 212 artQuuz := result.Module("quuz", "android_common_apex10000") 213 214 myFragment := result.Module("mybootclasspath-fragment", "android_common_apex10000") 215 myBar := result.Module("bar", "android_common_apex10000") 216 217 other := result.Module("othersdklibrary", "android_common_apex10000") 218 219 otherApexLibrary := result.Module("otherapexlibrary", "android_common_apex10000") 220 221 platformFoo := result.Module("quuz", "android_common") 222 223 bootclasspath := result.Module("myplatform-bootclasspath", "android_common") 224 225 // Use a custom assertion method instead of AssertDeepEquals as the latter formats the output 226 // using %#v which results in meaningless output as ClasspathElements are pointers. 227 assertElementsEquals := func(t *testing.T, message string, expected, actual java.ClasspathElements) { 228 if !reflect.DeepEqual(expected, actual) { 229 t.Errorf("%s: expected:\n %s\n got:\n %s", message, expected, actual) 230 } 231 } 232 233 expectFragmentElement := func(module android.Module, contents ...android.Module) java.ClasspathElement { 234 return &java.ClasspathFragmentElement{module, contents} 235 } 236 expectLibraryElement := func(module android.Module) java.ClasspathElement { 237 return &java.ClasspathLibraryElement{module} 238 } 239 240 newCtx := func() *testClasspathElementContext { 241 return &testClasspathElementContext{testContext: result.TestContext, module: bootclasspath} 242 } 243 244 // Verify that CreateClasspathElements works when given valid input. 245 t.Run("art:baz, art:quuz, my:bar, foo", func(t *testing.T) { 246 ctx := newCtx() 247 elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, artQuuz, myBar, platformFoo}, []android.Module{artFragment, myFragment}) 248 expectedElements := java.ClasspathElements{ 249 expectFragmentElement(artFragment, artBaz, artQuuz), 250 expectFragmentElement(myFragment, myBar), 251 expectLibraryElement(platformFoo), 252 } 253 assertElementsEquals(t, "elements", expectedElements, elements) 254 }) 255 256 // Verify that CreateClasspathElements detects when an apex has multiple fragments. 257 t.Run("multiple fragments for same apex", func(t *testing.T) { 258 ctx := newCtx() 259 elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{artFragment, artFragment}) 260 android.FailIfNoMatchingErrors(t, "apex com.android.art has multiple fragments, art-bootclasspath-fragment{.*} and art-bootclasspath-fragment{.*}", ctx.errs) 261 expectedElements := java.ClasspathElements{} 262 assertElementsEquals(t, "elements", expectedElements, elements) 263 }) 264 265 // Verify that CreateClasspathElements detects when a library is in multiple fragments. 266 t.Run("library from multiple fragments", func(t *testing.T) { 267 ctx := newCtx() 268 elements := java.CreateClasspathElements(ctx, []android.Module{other}, []android.Module{artFragment, myFragment}) 269 android.FailIfNoMatchingErrors(t, "library othersdklibrary{.*} is in two separate fragments, art-bootclasspath-fragment{.*} and mybootclasspath-fragment{.*}", ctx.errs) 270 expectedElements := java.ClasspathElements{} 271 assertElementsEquals(t, "elements", expectedElements, elements) 272 }) 273 274 // Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and 275 // are separated by a library from another fragment. 276 t.Run("discontiguous separated by fragment", func(t *testing.T) { 277 ctx := newCtx() 278 elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, myBar, artQuuz, platformFoo}, []android.Module{artFragment, myFragment}) 279 expectedElements := java.ClasspathElements{ 280 expectFragmentElement(artFragment, artBaz, artQuuz), 281 expectFragmentElement(myFragment, myBar), 282 expectLibraryElement(platformFoo), 283 } 284 assertElementsEquals(t, "elements", expectedElements, elements) 285 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) 286 }) 287 288 // Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and 289 // are separated by a standalone library. 290 t.Run("discontiguous separated by library", func(t *testing.T) { 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 ctx := newCtx() 307 elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, otherApexLibrary}, []android.Module{artFragment}) 308 expectedElements := java.ClasspathElements{ 309 expectFragmentElement(artFragment, artBaz), 310 } 311 assertElementsEquals(t, "elements", expectedElements, elements) 312 android.FailIfNoMatchingErrors(t, `library otherapexlibrary{.*} is from apexes \[otherapex\] which have no corresponding fragment in \[art-bootclasspath-fragment{.*}\]`, ctx.errs) 313 }) 314} 315