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