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) 24 25// Contains tests for java.CreateClasspathElements logic from java/classpath_element.go that 26// requires apexes. 27 28// testClasspathElementContext is a ClasspathElementContext suitable for use in tests. 29type testClasspathElementContext struct { 30 android.OtherModuleProviderContext 31 testContext *android.TestContext 32 module android.Module 33 errs []error 34} 35 36func (t *testClasspathElementContext) ModuleErrorf(fmt string, args ...interface{}) { 37 t.errs = append(t.errs, t.testContext.ModuleErrorf(t.module, fmt, args...)) 38} 39 40var _ java.ClasspathElementContext = (*testClasspathElementContext)(nil) 41 42func TestCreateClasspathElements(t *testing.T) { 43 preparer := android.GroupFixturePreparers( 44 prepareForTestWithPlatformBootclasspath, 45 prepareForTestWithArtApex, 46 prepareForTestWithMyapex, 47 // For otherapex. 48 android.FixtureMergeMockFs(android.MockFS{ 49 "system/sepolicy/apex/otherapex-file_contexts": nil, 50 }), 51 java.PrepareForTestWithJavaSdkLibraryFiles, 52 java.FixtureWithLastReleaseApis("foo", "othersdklibrary"), 53 java.FixtureConfigureApexBootJars("myapex:bar"), 54 android.FixtureWithRootAndroidBp(` 55 apex { 56 name: "com.android.art", 57 key: "com.android.art.key", 58 bootclasspath_fragments: [ 59 "art-bootclasspath-fragment", 60 ], 61 java_libs: [ 62 "othersdklibrary", 63 ], 64 updatable: false, 65 } 66 67 apex_key { 68 name: "com.android.art.key", 69 public_key: "com.android.art.avbpubkey", 70 private_key: "com.android.art.pem", 71 } 72 73 bootclasspath_fragment { 74 name: "art-bootclasspath-fragment", 75 image_name: "art", 76 apex_available: [ 77 "com.android.art", 78 ], 79 contents: [ 80 "baz", 81 "quuz", 82 ], 83 hidden_api: { 84 split_packages: ["*"], 85 }, 86 } 87 88 java_library { 89 name: "baz", 90 apex_available: [ 91 "com.android.art", 92 ], 93 srcs: ["b.java"], 94 installable: true, 95 } 96 97 java_library { 98 name: "quuz", 99 apex_available: [ 100 "com.android.art", 101 ], 102 srcs: ["b.java"], 103 installable: true, 104 } 105 106 apex { 107 name: "myapex", 108 key: "myapex.key", 109 bootclasspath_fragments: [ 110 "mybootclasspath-fragment", 111 ], 112 java_libs: [ 113 "othersdklibrary", 114 ], 115 updatable: false, 116 } 117 118 apex_key { 119 name: "myapex.key", 120 public_key: "testkey.avbpubkey", 121 private_key: "testkey.pem", 122 } 123 124 bootclasspath_fragment { 125 name: "mybootclasspath-fragment", 126 apex_available: [ 127 "myapex", 128 ], 129 contents: [ 130 "bar", 131 ], 132 hidden_api: { 133 split_packages: ["*"], 134 }, 135 } 136 137 java_library { 138 name: "bar", 139 srcs: ["b.java"], 140 installable: true, 141 apex_available: ["myapex"], 142 permitted_packages: ["bar"], 143 } 144 145 java_sdk_library { 146 name: "foo", 147 srcs: ["b.java"], 148 } 149 150 java_sdk_library { 151 name: "othersdklibrary", 152 srcs: ["b.java"], 153 shared_library: false, 154 apex_available: [ 155 "com.android.art", 156 "myapex", 157 ], 158 } 159 160 apex { 161 name: "otherapex", 162 key: "otherapex.key", 163 java_libs: [ 164 "otherapexlibrary", 165 ], 166 updatable: false, 167 } 168 169 apex_key { 170 name: "otherapex.key", 171 public_key: "testkey.avbpubkey", 172 private_key: "testkey.pem", 173 } 174 175 java_library { 176 name: "otherapexlibrary", 177 srcs: ["b.java"], 178 installable: true, 179 apex_available: ["otherapex"], 180 permitted_packages: ["otherapexlibrary"], 181 } 182 183 platform_bootclasspath { 184 name: "myplatform-bootclasspath", 185 186 fragments: [ 187 { 188 apex: "com.android.art", 189 module: "art-bootclasspath-fragment", 190 }, 191 { 192 apex: "myapex", 193 module: "mybootclasspath-fragment", 194 }, 195 ], 196 } 197 `), 198 ) 199 200 result := preparer.RunTest(t) 201 202 artFragment := result.Module("art-bootclasspath-fragment", "android_common_apex10000") 203 artBaz := result.Module("baz", "android_common_apex10000") 204 artQuuz := result.Module("quuz", "android_common_apex10000") 205 206 myFragment := result.Module("mybootclasspath-fragment", "android_common_apex10000") 207 myBar := result.Module("bar", "android_common_apex10000") 208 209 other := result.Module("othersdklibrary", "android_common_apex10000") 210 211 otherApexLibrary := result.Module("otherapexlibrary", "android_common_apex10000") 212 213 platformFoo := result.Module("quuz", "android_common") 214 215 bootclasspath := result.Module("myplatform-bootclasspath", "android_common") 216 217 // Use a custom assertion method instead of AssertDeepEquals as the latter formats the output 218 // using %#v which results in meaningless output as ClasspathElements are pointers. 219 assertElementsEquals := func(t *testing.T, message string, expected, actual java.ClasspathElements) { 220 if !reflect.DeepEqual(expected, actual) { 221 t.Errorf("%s: expected:\n %s\n got:\n %s", message, expected, actual) 222 } 223 } 224 225 expectFragmentElement := func(module android.Module, contents ...android.Module) java.ClasspathElement { 226 return &java.ClasspathFragmentElement{module, contents} 227 } 228 expectLibraryElement := func(module android.Module) java.ClasspathElement { 229 return &java.ClasspathLibraryElement{module} 230 } 231 232 newCtx := func() *testClasspathElementContext { 233 return &testClasspathElementContext{ 234 OtherModuleProviderContext: result.TestContext.OtherModuleProviderAdaptor(), 235 testContext: result.TestContext, 236 module: bootclasspath, 237 } 238 } 239 240 // Verify that CreateClasspathElements works when given valid input. 241 t.Run("art:baz, art:quuz, my:bar, foo", func(t *testing.T) { 242 ctx := newCtx() 243 elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, artQuuz, myBar, platformFoo}, []android.Module{artFragment, myFragment}) 244 expectedElements := java.ClasspathElements{ 245 expectFragmentElement(artFragment, artBaz, artQuuz), 246 expectFragmentElement(myFragment, myBar), 247 expectLibraryElement(platformFoo), 248 } 249 assertElementsEquals(t, "elements", expectedElements, elements) 250 }) 251 252 // Verify that CreateClasspathElements detects when an apex has multiple fragments. 253 t.Run("multiple fragments for same apex", func(t *testing.T) { 254 ctx := newCtx() 255 elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{artFragment, artFragment}) 256 android.FailIfNoMatchingErrors(t, "apex com.android.art has multiple fragments, art-bootclasspath-fragment{.*} and art-bootclasspath-fragment{.*}", ctx.errs) 257 expectedElements := java.ClasspathElements{} 258 assertElementsEquals(t, "elements", expectedElements, elements) 259 }) 260 261 // Verify that CreateClasspathElements detects when a library is in multiple fragments. 262 t.Run("library from multiple fragments", func(t *testing.T) { 263 ctx := newCtx() 264 elements := java.CreateClasspathElements(ctx, []android.Module{other}, []android.Module{artFragment, myFragment}) 265 android.FailIfNoMatchingErrors(t, "library othersdklibrary{.*} is in two separate fragments, art-bootclasspath-fragment{.*} and mybootclasspath-fragment{.*}", ctx.errs) 266 expectedElements := java.ClasspathElements{} 267 assertElementsEquals(t, "elements", expectedElements, elements) 268 }) 269 270 // Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and 271 // are separated by a library from another fragment. 272 t.Run("discontiguous separated by fragment", func(t *testing.T) { 273 ctx := newCtx() 274 elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, myBar, artQuuz, platformFoo}, []android.Module{artFragment, myFragment}) 275 expectedElements := java.ClasspathElements{ 276 expectFragmentElement(artFragment, artBaz, artQuuz), 277 expectFragmentElement(myFragment, myBar), 278 expectLibraryElement(platformFoo), 279 } 280 assertElementsEquals(t, "elements", expectedElements, elements) 281 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) 282 }) 283 284 // Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and 285 // are separated by a standalone library. 286 t.Run("discontiguous separated by library", func(t *testing.T) { 287 ctx := newCtx() 288 elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, platformFoo, artQuuz, myBar}, []android.Module{artFragment, myFragment}) 289 expectedElements := java.ClasspathElements{ 290 expectFragmentElement(artFragment, artBaz, artQuuz), 291 expectLibraryElement(platformFoo), 292 expectFragmentElement(myFragment, myBar), 293 } 294 assertElementsEquals(t, "elements", expectedElements, elements) 295 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) 296 }) 297 298 // Verify that CreateClasspathElements detects when there a library on the classpath that 299 // indicates it is from an apex the supplied fragments list does not contain a fragment for that 300 // apex. 301 t.Run("no fragment for apex", func(t *testing.T) { 302 ctx := newCtx() 303 elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, otherApexLibrary}, []android.Module{artFragment}) 304 expectedElements := java.ClasspathElements{ 305 expectFragmentElement(artFragment, artBaz), 306 } 307 assertElementsEquals(t, "elements", expectedElements, elements) 308 android.FailIfNoMatchingErrors(t, `library otherapexlibrary{.*} is from apexes \[otherapex\] which have no corresponding fragment in \[art-bootclasspath-fragment{.*}\]`, ctx.errs) 309 }) 310} 311