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_com.android.art") 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_myapex") 206 myBar := result.Module("bar", "android_common_apex10000") 207 208 otherApexLibrary := result.Module("otherapexlibrary", "android_common_apex10000") 209 210 platformFoo := result.Module("quuz", "android_common") 211 212 bootclasspath := result.Module("myplatform-bootclasspath", "android_common") 213 214 // Use a custom assertion method instead of AssertDeepEquals as the latter formats the output 215 // using %#v which results in meaningless output as ClasspathElements are pointers. 216 assertElementsEquals := func(t *testing.T, message string, expected, actual java.ClasspathElements) { 217 if !reflect.DeepEqual(expected, actual) { 218 t.Errorf("%s: expected:\n %s\n got:\n %s", message, expected, actual) 219 } 220 } 221 222 expectFragmentElement := func(module android.Module, contents ...android.Module) java.ClasspathElement { 223 return &java.ClasspathFragmentElement{module, contents} 224 } 225 expectLibraryElement := func(module android.Module) java.ClasspathElement { 226 return &java.ClasspathLibraryElement{module} 227 } 228 229 newCtx := func() *testClasspathElementContext { 230 return &testClasspathElementContext{ 231 OtherModuleProviderContext: result.TestContext.OtherModuleProviderAdaptor(), 232 testContext: result.TestContext, 233 module: bootclasspath, 234 } 235 } 236 237 // Verify that CreateClasspathElements works when given valid input. 238 t.Run("art:baz, art:quuz, my:bar, foo", func(t *testing.T) { 239 t.Parallel() 240 ctx := newCtx() 241 elements := java.CreateClasspathElements(ctx, 242 []android.Module{artBaz, artQuuz, myBar, platformFoo}, 243 []android.Module{artFragment, myFragment}, 244 map[android.Module]string{artBaz: "com.android.art", artQuuz: "com.android.art", myBar: "myapex"}, 245 map[string]android.Module{"com.android.art": artFragment, "myapex": myFragment}) 246 expectedElements := java.ClasspathElements{ 247 expectFragmentElement(artFragment, artBaz, artQuuz), 248 expectFragmentElement(myFragment, myBar), 249 expectLibraryElement(platformFoo), 250 } 251 assertElementsEquals(t, "elements", expectedElements, elements) 252 }) 253 254 // Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and 255 // are separated by a library from another fragment. 256 t.Run("discontiguous separated by fragment", func(t *testing.T) { 257 t.Parallel() 258 ctx := newCtx() 259 elements := java.CreateClasspathElements(ctx, 260 []android.Module{artBaz, myBar, artQuuz, platformFoo}, 261 []android.Module{artFragment, myFragment}, 262 map[android.Module]string{artBaz: "com.android.art", artQuuz: "com.android.art", myBar: "myapex"}, 263 map[string]android.Module{"com.android.art": artFragment, "myapex": myFragment}) 264 expectedElements := java.ClasspathElements{ 265 expectFragmentElement(artFragment, artBaz, artQuuz), 266 expectFragmentElement(myFragment, myBar), 267 expectLibraryElement(platformFoo), 268 } 269 assertElementsEquals(t, "elements", expectedElements, elements) 270 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) 271 }) 272 273 // Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and 274 // are separated by a standalone library. 275 t.Run("discontiguous separated by library", func(t *testing.T) { 276 t.Parallel() 277 ctx := newCtx() 278 elements := java.CreateClasspathElements(ctx, 279 []android.Module{artBaz, platformFoo, artQuuz, myBar}, 280 []android.Module{artFragment, myFragment}, 281 map[android.Module]string{artBaz: "com.android.art", artQuuz: "com.android.art", myBar: "myapex"}, 282 map[string]android.Module{"com.android.art": artFragment, "myapex": myFragment}) 283 expectedElements := java.ClasspathElements{ 284 expectFragmentElement(artFragment, artBaz, artQuuz), 285 expectLibraryElement(platformFoo), 286 expectFragmentElement(myFragment, myBar), 287 } 288 assertElementsEquals(t, "elements", expectedElements, elements) 289 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) 290 }) 291 292 // Verify that CreateClasspathElements detects when there a library on the classpath that 293 // indicates it is from an apex the supplied fragments list does not contain a fragment for that 294 // apex. 295 t.Run("no fragment for apex", func(t *testing.T) { 296 t.Parallel() 297 ctx := newCtx() 298 elements := java.CreateClasspathElements(ctx, 299 []android.Module{artBaz, otherApexLibrary}, 300 []android.Module{artFragment}, 301 map[android.Module]string{artBaz: "com.android.art", otherApexLibrary: "otherapex"}, 302 map[string]android.Module{"com.android.art": artFragment}) 303 expectedElements := java.ClasspathElements{ 304 expectFragmentElement(artFragment, artBaz), 305 } 306 assertElementsEquals(t, "elements", expectedElements, elements) 307 android.FailIfNoMatchingErrors(t, `library otherapexlibrary{.*} is from apexes \[otherapex\] which have no corresponding fragment in \[art-bootclasspath-fragment{.*}\]`, ctx.errs) 308 }) 309} 310