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 android.FixtureWithRootAndroidBp(` 62 apex { 63 name: "com.android.art", 64 key: "com.android.art.key", 65 bootclasspath_fragments: [ 66 "art-bootclasspath-fragment", 67 ], 68 java_libs: [ 69 "othersdklibrary", 70 ], 71 updatable: false, 72 } 73 74 apex_key { 75 name: "com.android.art.key", 76 public_key: "com.android.art.avbpubkey", 77 private_key: "com.android.art.pem", 78 } 79 80 bootclasspath_fragment { 81 name: "art-bootclasspath-fragment", 82 apex_available: [ 83 "com.android.art", 84 ], 85 contents: [ 86 "baz", 87 "quuz", 88 ], 89 } 90 91 java_library { 92 name: "baz", 93 apex_available: [ 94 "com.android.art", 95 ], 96 srcs: ["b.java"], 97 installable: true, 98 } 99 100 java_library { 101 name: "quuz", 102 apex_available: [ 103 "com.android.art", 104 ], 105 srcs: ["b.java"], 106 installable: true, 107 } 108 109 apex { 110 name: "myapex", 111 key: "myapex.key", 112 bootclasspath_fragments: [ 113 "mybootclasspath-fragment", 114 ], 115 java_libs: [ 116 "othersdklibrary", 117 ], 118 updatable: false, 119 } 120 121 apex_key { 122 name: "myapex.key", 123 public_key: "testkey.avbpubkey", 124 private_key: "testkey.pem", 125 } 126 127 bootclasspath_fragment { 128 name: "mybootclasspath-fragment", 129 apex_available: [ 130 "myapex", 131 ], 132 contents: [ 133 "bar", 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 bootclasspath_fragment { 161 name: "non-apex-fragment", 162 contents: ["othersdklibrary"], 163 } 164 165 apex { 166 name: "otherapex", 167 key: "otherapex.key", 168 java_libs: [ 169 "otherapexlibrary", 170 ], 171 updatable: false, 172 } 173 174 apex_key { 175 name: "otherapex.key", 176 public_key: "testkey.avbpubkey", 177 private_key: "testkey.pem", 178 } 179 180 java_library { 181 name: "otherapexlibrary", 182 srcs: ["b.java"], 183 installable: true, 184 apex_available: ["otherapex"], 185 permitted_packages: ["otherapexlibrary"], 186 } 187 188 platform_bootclasspath { 189 name: "myplatform-bootclasspath", 190 191 fragments: [ 192 { 193 apex: "com.android.art", 194 module: "art-bootclasspath-fragment", 195 }, 196 ], 197 } 198 `), 199 ) 200 201 result := preparer.RunTest(t) 202 203 artFragment := result.Module("art-bootclasspath-fragment", "android_common_apex10000") 204 artBaz := result.Module("baz", "android_common_apex10000") 205 artQuuz := result.Module("quuz", "android_common_apex10000") 206 207 myFragment := result.Module("mybootclasspath-fragment", "android_common_apex10000") 208 myBar := result.Module("bar", "android_common_apex10000") 209 210 nonApexFragment := result.Module("non-apex-fragment", "android_common") 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 a fragment does not have an associated apex. 251 t.Run("non apex fragment", func(t *testing.T) { 252 ctx := newCtx() 253 elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{nonApexFragment}) 254 android.FailIfNoMatchingErrors(t, "fragment non-apex-fragment{.*} is not part of an apex", ctx.errs) 255 expectedElements := java.ClasspathElements{} 256 assertElementsEquals(t, "elements", expectedElements, elements) 257 }) 258 259 // Verify that CreateClasspathElements detects when an apex has multiple fragments. 260 t.Run("multiple fragments for same apex", func(t *testing.T) { 261 ctx := newCtx() 262 elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{artFragment, artFragment}) 263 android.FailIfNoMatchingErrors(t, "apex com.android.art has multiple fragments, art-bootclasspath-fragment{.*} and art-bootclasspath-fragment{.*}", ctx.errs) 264 expectedElements := java.ClasspathElements{} 265 assertElementsEquals(t, "elements", expectedElements, elements) 266 }) 267 268 // Verify that CreateClasspathElements detects when a library is in multiple fragments. 269 t.Run("library from multiple fragments", func(t *testing.T) { 270 ctx := newCtx() 271 elements := java.CreateClasspathElements(ctx, []android.Module{other}, []android.Module{artFragment, myFragment}) 272 android.FailIfNoMatchingErrors(t, "library othersdklibrary{.*} is in two separate fragments, art-bootclasspath-fragment{.*} and mybootclasspath-fragment{.*}", ctx.errs) 273 expectedElements := java.ClasspathElements{} 274 assertElementsEquals(t, "elements", expectedElements, elements) 275 }) 276 277 // Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and 278 // are separated by a library from another fragment. 279 t.Run("discontiguous separated by fragment", func(t *testing.T) { 280 ctx := newCtx() 281 elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, myBar, artQuuz, platformFoo}, []android.Module{artFragment, myFragment}) 282 expectedElements := java.ClasspathElements{ 283 expectFragmentElement(artFragment, artBaz, artQuuz), 284 expectFragmentElement(myFragment, myBar), 285 expectLibraryElement(platformFoo), 286 } 287 assertElementsEquals(t, "elements", expectedElements, elements) 288 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) 289 }) 290 291 // Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and 292 // are separated by a standalone library. 293 t.Run("discontiguous separated by library", func(t *testing.T) { 294 ctx := newCtx() 295 elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, platformFoo, artQuuz, myBar}, []android.Module{artFragment, myFragment}) 296 expectedElements := java.ClasspathElements{ 297 expectFragmentElement(artFragment, artBaz, artQuuz), 298 expectLibraryElement(platformFoo), 299 expectFragmentElement(myFragment, myBar), 300 } 301 assertElementsEquals(t, "elements", expectedElements, elements) 302 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) 303 }) 304 305 // Verify that CreateClasspathElements detects when there a library on the classpath that 306 // indicates it is from an apex the supplied fragments list does not contain a fragment for that 307 // apex. 308 t.Run("no fragment for apex", func(t *testing.T) { 309 ctx := newCtx() 310 elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, otherApexLibrary}, []android.Module{artFragment}) 311 expectedElements := java.ClasspathElements{ 312 expectFragmentElement(artFragment, artBaz), 313 } 314 assertElementsEquals(t, "elements", expectedElements, elements) 315 android.FailIfNoMatchingErrors(t, `library otherapexlibrary{.*} is from apexes \[otherapex\] which have no corresponding fragment in \[art-bootclasspath-fragment{.*}\]`, ctx.errs) 316 }) 317} 318