1// Copyright 2019 Google Inc. All rights reserved. 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 sdk 16 17import ( 18 "fmt" 19 "path/filepath" 20 "strings" 21 "testing" 22 23 "android/soong/android" 24 "android/soong/apex" 25 "android/soong/cc" 26 "android/soong/genrule" 27 "android/soong/java" 28 29 "github.com/google/blueprint/proptools" 30) 31 32// Prepare for running an sdk test with an apex. 33var prepareForSdkTestWithApex = android.GroupFixturePreparers( 34 apex.PrepareForTestWithApexBuildComponents, 35 android.FixtureAddTextFile("sdk/tests/Android.bp", ` 36 apex_key { 37 name: "myapex.key", 38 public_key: "myapex.avbpubkey", 39 private_key: "myapex.pem", 40 } 41 42 android_app_certificate { 43 name: "myapex.cert", 44 certificate: "myapex", 45 } 46 `), 47 48 android.FixtureMergeMockFs(map[string][]byte{ 49 "apex_manifest.json": nil, 50 "system/sepolicy/apex/myapex-file_contexts": nil, 51 "system/sepolicy/apex/myapex2-file_contexts": nil, 52 "system/sepolicy/apex/mysdkapex-file_contexts": nil, 53 "sdk/tests/myapex.avbpubkey": nil, 54 "sdk/tests/myapex.pem": nil, 55 "sdk/tests/myapex.x509.pem": nil, 56 "sdk/tests/myapex.pk8": nil, 57 }), 58) 59 60// Legacy preparer used for running tests within the sdk package. 61// 62// This includes everything that was needed to run any test in the sdk package prior to the 63// introduction of the test fixtures. Tests that are being converted to use fixtures directly 64// rather than through the testSdkError() and testSdkWithFs() methods should avoid using this and 65// instead should use the various preparers directly using android.GroupFixturePreparers(...) to 66// group them when necessary. 67// 68// deprecated 69var prepareForSdkTest = android.GroupFixturePreparers( 70 cc.PrepareForTestWithCcDefaultModules, 71 genrule.PrepareForTestWithGenRuleBuildComponents, 72 java.PrepareForTestWithJavaBuildComponents, 73 PrepareForTestWithSdkBuildComponents, 74 75 prepareForSdkTestWithApex, 76 77 cc.PrepareForTestOnWindows, 78 android.FixtureModifyConfig(func(config android.Config) { 79 // Add windows as a default disable OS to test behavior when some OS variants 80 // are disabled. 81 config.Targets[android.Windows] = []android.Target{ 82 {android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", true}, 83 } 84 }), 85 86 // Add a build number file. 87 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { 88 variables.BuildNumberFile = proptools.StringPtr(BUILD_NUMBER_FILE) 89 }), 90 91 // Make sure that every test provides all the source files. 92 android.PrepareForTestDisallowNonExistentPaths, 93 android.MockFS{ 94 "Test.java": nil, 95 }.AddToFixture(), 96) 97 98var PrepareForTestWithSdkBuildComponents = android.GroupFixturePreparers( 99 android.FixtureRegisterWithContext(registerModuleExportsBuildComponents), 100 android.FixtureRegisterWithContext(registerSdkBuildComponents), 101) 102 103func testSdkWithFs(t *testing.T, bp string, fs android.MockFS) *android.TestResult { 104 t.Helper() 105 return android.GroupFixturePreparers( 106 prepareForSdkTest, 107 fs.AddToFixture(), 108 ).RunTestWithBp(t, bp) 109} 110 111func testSdkError(t *testing.T, pattern, bp string) { 112 t.Helper() 113 prepareForSdkTest. 114 ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)). 115 RunTestWithBp(t, bp) 116} 117 118func ensureListContains(t *testing.T, result []string, expected string) { 119 t.Helper() 120 if !android.InList(expected, result) { 121 t.Errorf("%q is not found in %v", expected, result) 122 } 123} 124 125// Analyse the sdk build rules to extract information about what it is doing. 126// 127// e.g. find the src/dest pairs from each cp command, the various zip files 128// generated, etc. 129func getSdkSnapshotBuildInfo(t *testing.T, result *android.TestResult, sdk *sdk) *snapshotBuildInfo { 130 info := &snapshotBuildInfo{ 131 t: t, 132 r: result, 133 androidBpContents: sdk.GetAndroidBpContentsForTests(), 134 infoContents: sdk.GetInfoContentsForTests(), 135 snapshotTestCustomizations: map[snapshotTest]*snapshotTestCustomization{}, 136 targetBuildRelease: sdk.builderForTests.targetBuildRelease, 137 } 138 139 buildParams := sdk.BuildParamsForTests() 140 copyRules := &strings.Builder{} 141 otherCopyRules := &strings.Builder{} 142 snapshotDirPrefix := sdk.builderForTests.snapshotDir.String() + "/" 143 144 seenBuildNumberFile := false 145 for _, bp := range buildParams { 146 switch bp.Rule.String() { 147 case android.Cp.String(): 148 output := bp.Output 149 // Get destination relative to the snapshot root 150 dest := output.Rel() 151 src := android.NormalizePathForTesting(bp.Input) 152 // We differentiate between copy rules for the snapshot, and copy rules for the install file. 153 if strings.HasPrefix(output.String(), snapshotDirPrefix) { 154 // Don't include the build-number.txt file in the copy rules as that would break lots of 155 // tests, just verify that it is copied here as it should appear in every snapshot. 156 if output.Base() == BUILD_NUMBER_FILE { 157 seenBuildNumberFile = true 158 } else { 159 // Get source relative to build directory. 160 _, _ = fmt.Fprintf(copyRules, "%s -> %s\n", src, dest) 161 } 162 info.snapshotContents = append(info.snapshotContents, dest) 163 } else { 164 _, _ = fmt.Fprintf(otherCopyRules, "%s -> %s\n", src, dest) 165 } 166 167 case repackageZip.String(): 168 // Add the destdir to the snapshot contents as that is effectively where 169 // the content of the repackaged zip is copied. 170 dest := bp.Args["destdir"] 171 info.snapshotContents = append(info.snapshotContents, dest) 172 173 case zipFiles.String(): 174 // This could be an intermediate zip file and not the actual output zip. 175 // In that case this will be overridden when the rule to merge the zips 176 // is processed. 177 info.outputZip = android.NormalizePathForTesting(bp.Output) 178 179 case mergeZips.String(): 180 // Copy the current outputZip to the intermediateZip. 181 info.intermediateZip = info.outputZip 182 mergeInput := android.NormalizePathForTesting(bp.Input) 183 if info.intermediateZip != mergeInput { 184 t.Errorf("Expected intermediate zip %s to be an input to merge zips but found %s instead", 185 info.intermediateZip, mergeInput) 186 } 187 188 // Override output zip (which was actually the intermediate zip file) with the actual 189 // output zip. 190 info.outputZip = android.NormalizePathForTesting(bp.Output) 191 192 // Save the zips to be merged into the intermediate zip. 193 info.mergeZips = android.NormalizePathsForTesting(bp.Inputs) 194 } 195 } 196 197 if !seenBuildNumberFile { 198 panic(fmt.Sprintf("Every snapshot must include the %s file", BUILD_NUMBER_FILE)) 199 } 200 201 info.copyRules = copyRules.String() 202 info.otherCopyRules = otherCopyRules.String() 203 204 return info 205} 206 207// The enum of different sdk snapshot tests performed by CheckSnapshot. 208type snapshotTest int 209 210const ( 211 // The enumeration of the different test configurations. 212 // A test with the snapshot/Android.bp file but without the original Android.bp file. 213 checkSnapshotWithoutSource snapshotTest = iota 214 215 // A test with both the original source and the snapshot, with the source preferred. 216 checkSnapshotWithSourcePreferred 217 218 // A test with both the original source and the snapshot, with the snapshot preferred. 219 checkSnapshotPreferredWithSource 220 221 // The directory into which the snapshot will be 'unpacked'. 222 snapshotSubDir = "snapshot" 223) 224 225// Check the snapshot build rules. 226// 227// Takes a list of functions which check different facets of the snapshot build rules. 228// Allows each test to customize what is checked without duplicating lots of code 229// or proliferating check methods of different flavors. 230func CheckSnapshot(t *testing.T, result *android.TestResult, name string, dir string, checkers ...snapshotBuildInfoChecker) { 231 t.Helper() 232 233 // The sdk CommonOS variant is always responsible for generating the snapshot. 234 variant := android.CommonOS.Name 235 236 sdk := result.Module(name, variant).(*sdk) 237 238 snapshotBuildInfo := getSdkSnapshotBuildInfo(t, result, sdk) 239 240 // Check state of the snapshot build. 241 for _, checker := range checkers { 242 checker(snapshotBuildInfo) 243 } 244 245 // Make sure that the generated zip file is in the correct place. 246 actual := snapshotBuildInfo.outputZip 247 if dir != "" { 248 dir = filepath.Clean(dir) + "/" 249 } 250 suffix := "-" + soongSdkSnapshotVersionCurrent 251 252 expectedZipPath := fmt.Sprintf(".intermediates/%s%s/%s/%s%s.zip", dir, name, variant, name, suffix) 253 android.AssertStringEquals(t, "Snapshot zip file in wrong place", expectedZipPath, actual) 254 255 // Populate a mock filesystem with the files that would have been copied by 256 // the rules. 257 fs := android.MockFS{} 258 for _, dest := range snapshotBuildInfo.snapshotContents { 259 fs[filepath.Join(snapshotSubDir, dest)] = nil 260 } 261 fs[filepath.Join(snapshotSubDir, "Android.bp")] = []byte(snapshotBuildInfo.androidBpContents) 262 263 // If the generated snapshot builders not for the current release then it cannot be loaded by 264 // the current release. 265 if snapshotBuildInfo.targetBuildRelease != buildReleaseCurrent { 266 return 267 } 268 269 // The preparers from the original source fixture. 270 sourcePreparers := result.Preparer() 271 272 // Preparer to combine the snapshot and the source. 273 snapshotPreparer := android.GroupFixturePreparers(sourcePreparers, fs.AddToFixture()) 274 275 var runSnapshotTestWithCheckers = func(t *testing.T, testConfig snapshotTest, extraPreparer android.FixturePreparer) { 276 t.Helper() 277 customization := snapshotBuildInfo.snapshotTestCustomization(testConfig) 278 customizedPreparers := android.GroupFixturePreparers(customization.preparers...) 279 280 // TODO(b/183184375): Set Config.TestAllowNonExistentPaths = false to verify that all the 281 // files the snapshot needs are actually copied into the snapshot. 282 283 // Run the snapshot with the snapshot preparer and the extra preparer, which must come after as 284 // it may need to modify parts of the MockFS populated by the snapshot preparer. 285 result := android.GroupFixturePreparers(snapshotPreparer, extraPreparer, customizedPreparers). 286 ExtendWithErrorHandler(customization.errorHandler). 287 RunTest(t) 288 289 // Perform any additional checks the test need on the result of processing the snapshot. 290 for _, checker := range customization.checkers { 291 checker(t, result) 292 } 293 } 294 295 t.Run("snapshot without source", func(t *testing.T) { 296 // Remove the source Android.bp file to make sure it works without. 297 removeSourceAndroidBp := android.FixtureModifyMockFS(func(fs android.MockFS) { 298 delete(fs, "Android.bp") 299 }) 300 301 runSnapshotTestWithCheckers(t, checkSnapshotWithoutSource, removeSourceAndroidBp) 302 }) 303 304 t.Run("snapshot with source preferred", func(t *testing.T) { 305 runSnapshotTestWithCheckers(t, checkSnapshotWithSourcePreferred, android.NullFixturePreparer) 306 }) 307 308 t.Run("snapshot preferred with source", func(t *testing.T) { 309 // Replace the snapshot/Android.bp file with one where "prefer: false," has been replaced with 310 // "prefer: true," 311 preferPrebuilts := android.FixtureModifyMockFS(func(fs android.MockFS) { 312 snapshotBpFile := filepath.Join(snapshotSubDir, "Android.bp") 313 unpreferred := string(fs[snapshotBpFile]) 314 fs[snapshotBpFile] = []byte(strings.ReplaceAll(unpreferred, "prefer: false,", "prefer: true,")) 315 }) 316 317 runSnapshotTestWithCheckers(t, checkSnapshotPreferredWithSource, preferPrebuilts) 318 }) 319} 320 321type snapshotBuildInfoChecker func(info *snapshotBuildInfo) 322 323// Check that the snapshot's generated Android.bp is correct. 324// 325// Both the expected and actual string are both trimmed before comparing. 326func checkAndroidBpContents(expected string) snapshotBuildInfoChecker { 327 return func(info *snapshotBuildInfo) { 328 info.t.Helper() 329 android.AssertTrimmedStringEquals(info.t, "Android.bp contents do not match", expected, info.androidBpContents) 330 } 331} 332 333// Check that the snapshot's copy rules are correct. 334// 335// The copy rules are formatted as <src> -> <dest>, one per line and then compared 336// to the supplied expected string. Both the expected and actual string are trimmed 337// before comparing. 338func checkAllCopyRules(expected string) snapshotBuildInfoChecker { 339 return func(info *snapshotBuildInfo) { 340 info.t.Helper() 341 android.AssertTrimmedStringEquals(info.t, "Incorrect copy rules", expected, info.copyRules) 342 } 343} 344 345func checkAllOtherCopyRules(expected string) snapshotBuildInfoChecker { 346 return func(info *snapshotBuildInfo) { 347 info.t.Helper() 348 android.AssertTrimmedStringEquals(info.t, "Incorrect copy rules", expected, info.otherCopyRules) 349 } 350} 351 352// Check that the specified paths match the list of zips to merge with the intermediate zip. 353func checkMergeZips(expected ...string) snapshotBuildInfoChecker { 354 return func(info *snapshotBuildInfo) { 355 info.t.Helper() 356 if info.intermediateZip == "" { 357 info.t.Errorf("No intermediate zip file was created") 358 } 359 360 android.AssertDeepEquals(info.t, "mismatching merge zip files", expected, info.mergeZips) 361 } 362} 363 364// Check that the snapshot's info contents are ciorrect. 365// 366// Both the expected and actual string are both trimmed before comparing. 367func checkInfoContents(config android.Config, expected string) snapshotBuildInfoChecker { 368 return func(info *snapshotBuildInfo) { 369 info.t.Helper() 370 android.AssertTrimmedStringEquals(info.t, "info contents do not match", 371 expected, android.StringRelativeToTop(config, info.infoContents)) 372 } 373} 374 375type resultChecker func(t *testing.T, result *android.TestResult) 376 377// snapshotTestPreparer registers a preparer that will be used to customize the specified 378// snapshotTest. 379func snapshotTestPreparer(snapshotTest snapshotTest, preparer android.FixturePreparer) snapshotBuildInfoChecker { 380 return func(info *snapshotBuildInfo) { 381 customization := info.snapshotTestCustomization(snapshotTest) 382 customization.preparers = append(customization.preparers, preparer) 383 } 384} 385 386// snapshotTestChecker registers a checker that will be run against the result of processing the 387// generated snapshot for the specified snapshotTest. 388func snapshotTestChecker(snapshotTest snapshotTest, checker resultChecker) snapshotBuildInfoChecker { 389 return func(info *snapshotBuildInfo) { 390 customization := info.snapshotTestCustomization(snapshotTest) 391 customization.checkers = append(customization.checkers, checker) 392 } 393} 394 395// snapshotTestErrorHandler registers an error handler to use when processing the snapshot 396// in the specific test case. 397// 398// Generally, the snapshot should work with all the test cases but some do not and just in case 399// there are a lot of issues to resolve, or it will take a lot of time this is a 400// get-out-of-jail-free card that allows progress to be made. 401// 402// deprecated: should only be used as a temporary workaround with an attached to do and bug. 403func snapshotTestErrorHandler(snapshotTest snapshotTest, handler android.FixtureErrorHandler) snapshotBuildInfoChecker { 404 return func(info *snapshotBuildInfo) { 405 customization := info.snapshotTestCustomization(snapshotTest) 406 customization.errorHandler = handler 407 } 408} 409 410// Encapsulates information provided by each test to customize a specific snapshotTest. 411type snapshotTestCustomization struct { 412 // Preparers that are used to customize the test fixture before running the test. 413 preparers []android.FixturePreparer 414 415 // Checkers that are run on the result of processing the preferred snapshot in a specific test 416 // case. 417 checkers []resultChecker 418 419 // Specify an error handler for when processing a specific test case. 420 // 421 // In some cases the generated snapshot cannot be used in a test configuration. Those cases are 422 // invariably bugs that need to be resolved but sometimes that can take a while. This provides a 423 // mechanism to temporarily ignore that error. 424 errorHandler android.FixtureErrorHandler 425} 426 427// Encapsulates information about the snapshot build structure in order to insulate tests from 428// knowing too much about internal structures. 429// 430// All source/input paths are relative either the build directory. All dest/output paths are 431// relative to the snapshot root directory. 432type snapshotBuildInfo struct { 433 t *testing.T 434 435 // The result from RunTest() 436 r *android.TestResult 437 438 // The contents of the generated Android.bp file 439 androidBpContents string 440 441 // The contents of the info file. 442 infoContents string 443 444 // The paths, relative to the snapshot root, of all files and directories copied into the 445 // snapshot. 446 snapshotContents []string 447 448 // A formatted representation of the src/dest pairs for a snapshot, one pair per line, 449 // of the format src -> dest 450 copyRules string 451 452 // A formatted representation of the src/dest pairs for files not in a snapshot, one pair 453 // per line, of the format src -> dest 454 otherCopyRules string 455 456 // The path to the intermediate zip, which is a zip created from the source files copied 457 // into the snapshot directory and which will be merged with other zips to form the final output. 458 // Is am empty string if there is no intermediate zip because there are no zips to merge in. 459 intermediateZip string 460 461 // The paths to the zips to merge into the output zip, does not include the intermediate 462 // zip. 463 mergeZips []string 464 465 // The final output zip. 466 outputZip string 467 468 // The target build release. 469 targetBuildRelease *buildRelease 470 471 // The test specific customizations for each snapshot test. 472 snapshotTestCustomizations map[snapshotTest]*snapshotTestCustomization 473} 474 475// snapshotTestCustomization gets the test specific customization for the specified snapshotTest. 476// 477// If no customization was created previously then it creates a default customization. 478func (i *snapshotBuildInfo) snapshotTestCustomization(snapshotTest snapshotTest) *snapshotTestCustomization { 479 customization := i.snapshotTestCustomizations[snapshotTest] 480 if customization == nil { 481 customization = &snapshotTestCustomization{ 482 errorHandler: android.FixtureExpectsNoErrors, 483 } 484 i.snapshotTestCustomizations[snapshotTest] = customization 485 } 486 return customization 487} 488