1// Copyright 2021 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 android 16 17import ( 18 "fmt" 19 "runtime" 20 "strings" 21 "testing" 22) 23 24// Provides support for creating test fixtures on which tests can be run. Reduces duplication 25// of test setup by allow tests to easily reuse setup code. 26// 27// Fixture 28// ======= 29// These determine the environment within which a test can be run. Fixtures are mutable and are 30// created and mutated by FixturePreparer instances. They are created by first creating a base 31// Fixture (which is essentially empty) and then applying FixturePreparer instances to it to modify 32// the environment. 33// 34// FixturePreparer 35// =============== 36// These are responsible for modifying a Fixture in preparation for it to run a test. Preparers are 37// intended to be immutable and able to prepare multiple Fixture objects simultaneously without 38// them sharing any data. 39// 40// They provide the basic capabilities for running tests too. 41// 42// FixturePreparers are only ever applied once per test fixture. Prior to application the list of 43// FixturePreparers are flattened and deduped while preserving the order they first appear in the 44// list. This makes it easy to reuse, group and combine FixturePreparers together. 45// 46// Each small self contained piece of test setup should be their own FixturePreparer. e.g. 47// * A group of related modules. 48// * A group of related mutators. 49// * A combination of both. 50// * Configuration. 51// 52// They should not overlap, e.g. the same module type should not be registered by different 53// FixturePreparers as using them both would cause a build error. In that case the preparer should 54// be split into separate parts and combined together using FixturePreparers(...). 55// 56// e.g. attempting to use AllPreparers in preparing a Fixture would break as it would attempt to 57// register module bar twice: 58// var Preparer1 = FixtureRegisterWithContext(RegisterModuleFooAndBar) 59// var Preparer2 = FixtureRegisterWithContext(RegisterModuleBarAndBaz) 60// var AllPreparers = GroupFixturePreparers(Preparer1, Preparer2) 61// 62// However, when restructured like this it would work fine: 63// var PreparerFoo = FixtureRegisterWithContext(RegisterModuleFoo) 64// var PreparerBar = FixtureRegisterWithContext(RegisterModuleBar) 65// var PreparerBaz = FixtureRegisterWithContext(RegisterModuleBaz) 66// var Preparer1 = GroupFixturePreparers(RegisterModuleFoo, RegisterModuleBar) 67// var Preparer2 = GroupFixturePreparers(RegisterModuleBar, RegisterModuleBaz) 68// var AllPreparers = GroupFixturePreparers(Preparer1, Preparer2) 69// 70// As after deduping and flattening AllPreparers would result in the following preparers being 71// applied: 72// 1. PreparerFoo 73// 2. PreparerBar 74// 3. PreparerBaz 75// 76// Preparers can be used for both integration and unit tests. 77// 78// Integration tests typically use all the module types, mutators and singletons that are available 79// for that package to try and replicate the behavior of the runtime build as closely as possible. 80// However, that realism comes at a cost of increased fragility (as they can be broken by changes in 81// many different parts of the build) and also increased runtime, especially if they use lots of 82// singletons and mutators. 83// 84// Unit tests on the other hand try and minimize the amount of code being tested which makes them 85// less susceptible to changes elsewhere in the build and quick to run but at a cost of potentially 86// not testing realistic scenarios. 87// 88// Supporting unit tests effectively require that preparers are available at the lowest granularity 89// possible. Supporting integration tests effectively require that the preparers are organized into 90// groups that provide all the functionality available. 91// 92// At least in terms of tests that check the behavior of build components via processing 93// `Android.bp` there is no clear separation between a unit test and an integration test. Instead 94// they vary from one end that tests a single module (e.g. filegroup) to the other end that tests a 95// whole system of modules, mutators and singletons (e.g. apex + hiddenapi). 96// 97// TestResult 98// ========== 99// These are created by running tests in a Fixture and provide access to the Config and TestContext 100// in which the tests were run. 101// 102// Example 103// ======= 104// 105// An exported preparer for use by other packages that need to use java modules. 106// 107// package java 108// var PrepareForIntegrationTestWithJava = GroupFixturePreparers( 109// android.PrepareForIntegrationTestWithAndroid, 110// FixtureRegisterWithContext(RegisterAGroupOfRelatedModulesMutatorsAndSingletons), 111// FixtureRegisterWithContext(RegisterAnotherGroupOfRelatedModulesMutatorsAndSingletons), 112// ... 113// ) 114// 115// Some files to use in tests in the java package. 116// 117// var javaMockFS = android.MockFS{ 118// "api/current.txt": nil, 119// "api/removed.txt": nil, 120// ... 121// } 122// 123// A package private preparer for use for testing java within the java package. 124// 125// var prepareForJavaTest = android.GroupFixturePreparers( 126// PrepareForIntegrationTestWithJava, 127// FixtureRegisterWithContext(func(ctx android.RegistrationContext) { 128// ctx.RegisterModuleType("test_module", testModule) 129// }), 130// javaMockFS.AddToFixture(), 131// ... 132// } 133// 134// func TestJavaStuff(t *testing.T) { 135// result := android.GroupFixturePreparers( 136// prepareForJavaTest, 137// android.FixtureWithRootAndroidBp(`java_library {....}`), 138// android.MockFS{...}.AddToFixture(), 139// ).RunTest(t) 140// ... test result ... 141// } 142// 143// package cc 144// var PrepareForTestWithCC = android.GroupFixturePreparers( 145// android.PrepareForArchMutator, 146// android.prepareForPrebuilts, 147// FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest), 148// ... 149// ) 150// 151// package apex 152// 153// var PrepareForApex = GroupFixturePreparers( 154// ... 155// ) 156// 157// Use modules and mutators from java, cc and apex. Any duplicate preparers (like 158// android.PrepareForArchMutator) will be automatically deduped. 159// 160// var prepareForApexTest = android.GroupFixturePreparers( 161// PrepareForJava, 162// PrepareForCC, 163// PrepareForApex, 164// ) 165// 166 167// A set of mock files to add to the mock file system. 168type MockFS map[string][]byte 169 170// Merge adds the extra entries from the supplied map to this one. 171// 172// Fails if the supplied map files with the same paths are present in both of them. 173func (fs MockFS) Merge(extra map[string][]byte) { 174 for p, c := range extra { 175 validateFixtureMockFSPath(p) 176 if _, ok := fs[p]; ok { 177 panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists", p)) 178 } 179 fs[p] = c 180 } 181} 182 183// Ensure that tests cannot add paths into the mock file system which would not be allowed in the 184// runtime, e.g. absolute paths, paths relative to the 'out/' directory. 185func validateFixtureMockFSPath(path string) { 186 // This uses validateSafePath rather than validatePath because the latter prevents adding files 187 // that include a $ but there are tests that allow files with a $ to be used, albeit only by 188 // globbing. 189 validatedPath, err := validateSafePath(path) 190 if err != nil { 191 panic(err) 192 } 193 194 // Make sure that the path is canonical. 195 if validatedPath != path { 196 panic(fmt.Errorf("path %q is not a canonical path, use %q instead", path, validatedPath)) 197 } 198 199 if path == "out" || strings.HasPrefix(path, "out/") { 200 panic(fmt.Errorf("cannot add output path %q to the mock file system", path)) 201 } 202} 203 204func (fs MockFS) AddToFixture() FixturePreparer { 205 return FixtureMergeMockFs(fs) 206} 207 208// FixtureCustomPreparer allows for the modification of any aspect of the fixture. 209// 210// This should only be used if one of the other more specific preparers are not suitable. 211func FixtureCustomPreparer(mutator func(fixture Fixture)) FixturePreparer { 212 return newSimpleFixturePreparer(func(f *fixture) { 213 mutator(f) 214 }) 215} 216 217// FixtureTestRunner determines the type of test to run. 218// 219// If no custom FixtureTestRunner is provided (using the FixtureSetTestRunner) then the default test 220// runner will run a standard Soong test that corresponds to what happens when Soong is run on the 221// command line. 222type FixtureTestRunner interface { 223 // FinalPreparer is a function that is run immediately before parsing the blueprint files. It is 224 // intended to perform the initialization needed by PostParseProcessor. 225 // 226 // It returns a CustomTestResult that is passed into PostParseProcessor and returned from 227 // FixturePreparer.RunTestWithCustomResult. If it needs to return some custom data then it must 228 // provide its own implementation of CustomTestResult and return an instance of that. Otherwise, 229 // it can just return the supplied *TestResult. 230 FinalPreparer(result *TestResult) CustomTestResult 231 232 // PostParseProcessor is called after successfully parsing the blueprint files and can do further 233 // work on the result of parsing the files. 234 // 235 // Successfully parsing simply means that no errors were encountered when parsing the blueprint 236 // files. 237 // 238 // This must collate any information useful for testing, e.g. errs, ninja deps and custom data in 239 // the supplied result. 240 PostParseProcessor(result CustomTestResult) 241} 242 243// FixtureSetTestRunner sets the FixtureTestRunner in the fixture. 244// 245// It is an error if more than one of these is applied to a single fixture. If none of these are 246// applied then the fixture will use the defaultTestRunner which will run the test as if it was 247// being run in `m <target>`. 248func FixtureSetTestRunner(testRunner FixtureTestRunner) FixturePreparer { 249 return newSimpleFixturePreparer(func(fixture *fixture) { 250 if fixture.testRunner != nil { 251 panic("fixture test runner has already been set") 252 } 253 fixture.testRunner = testRunner 254 }) 255} 256 257// Modify the config 258func FixtureModifyConfig(mutator func(config Config)) FixturePreparer { 259 return newSimpleFixturePreparer(func(f *fixture) { 260 mutator(f.config) 261 }) 262} 263 264// Modify the config and context 265func FixtureModifyConfigAndContext(mutator func(config Config, ctx *TestContext)) FixturePreparer { 266 return newSimpleFixturePreparer(func(f *fixture) { 267 mutator(f.config, f.ctx) 268 }) 269} 270 271// Modify the context 272func FixtureModifyContext(mutator func(ctx *TestContext)) FixturePreparer { 273 return newSimpleFixturePreparer(func(f *fixture) { 274 mutator(f.ctx) 275 }) 276} 277 278// Sync the mock filesystem with the current config, then modify the context, 279// This allows context modification that requires filesystem access. 280func FixtureModifyContextWithMockFs(mutator func(ctx *TestContext)) FixturePreparer { 281 return newSimpleFixturePreparer(func(f *fixture) { 282 f.config.mockFileSystem("", f.mockFS) 283 mutator(f.ctx) 284 }) 285} 286 287func FixtureRegisterWithContext(registeringFunc func(ctx RegistrationContext)) FixturePreparer { 288 return FixtureModifyContext(func(ctx *TestContext) { registeringFunc(ctx) }) 289} 290 291// Modify the mock filesystem 292func FixtureModifyMockFS(mutator func(fs MockFS)) FixturePreparer { 293 return newSimpleFixturePreparer(func(f *fixture) { 294 mutator(f.mockFS) 295 296 // Make sure that invalid paths were not added to the mock filesystem. 297 for p, _ := range f.mockFS { 298 validateFixtureMockFSPath(p) 299 } 300 }) 301} 302 303// Merge the supplied file system into the mock filesystem. 304// 305// Paths that already exist in the mock file system are overridden. 306func FixtureMergeMockFs(mockFS MockFS) FixturePreparer { 307 return FixtureModifyMockFS(func(fs MockFS) { 308 fs.Merge(mockFS) 309 }) 310} 311 312// Add a file to the mock filesystem 313// 314// Fail if the filesystem already contains a file with that path, use FixtureOverrideFile instead. 315func FixtureAddFile(path string, contents []byte) FixturePreparer { 316 return FixtureModifyMockFS(func(fs MockFS) { 317 validateFixtureMockFSPath(path) 318 if _, ok := fs[path]; ok { 319 panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists, use FixtureOverride*File instead", path)) 320 } 321 fs[path] = contents 322 }) 323} 324 325// Add a text file to the mock filesystem 326// 327// Fail if the filesystem already contains a file with that path. 328func FixtureAddTextFile(path string, contents string) FixturePreparer { 329 return FixtureAddFile(path, []byte(contents)) 330} 331 332// Override a file in the mock filesystem 333// 334// If the file does not exist this behaves as FixtureAddFile. 335func FixtureOverrideFile(path string, contents []byte) FixturePreparer { 336 return FixtureModifyMockFS(func(fs MockFS) { 337 fs[path] = contents 338 }) 339} 340 341// Override a text file in the mock filesystem 342// 343// If the file does not exist this behaves as FixtureAddTextFile. 344func FixtureOverrideTextFile(path string, contents string) FixturePreparer { 345 return FixtureOverrideFile(path, []byte(contents)) 346} 347 348// Add the root Android.bp file with the supplied contents. 349func FixtureWithRootAndroidBp(contents string) FixturePreparer { 350 return FixtureAddTextFile("Android.bp", contents) 351} 352 353// Merge some environment variables into the fixture. 354func FixtureMergeEnv(env map[string]string) FixturePreparer { 355 return FixtureModifyConfig(func(config Config) { 356 for k, v := range env { 357 if k == "PATH" { 358 panic("Cannot set PATH environment variable") 359 } 360 config.env[k] = v 361 } 362 }) 363} 364 365// Modify the env. 366// 367// Will panic if the mutator changes the PATH environment variable. 368func FixtureModifyEnv(mutator func(env map[string]string)) FixturePreparer { 369 return FixtureModifyConfig(func(config Config) { 370 oldPath := config.env["PATH"] 371 mutator(config.env) 372 newPath := config.env["PATH"] 373 if newPath != oldPath { 374 panic(fmt.Errorf("Cannot change PATH environment variable from %q to %q", oldPath, newPath)) 375 } 376 }) 377} 378 379// Allow access to the product variables when preparing the fixture. 380type FixtureProductVariables struct { 381 *ProductVariables 382} 383 384// Modify product variables. 385func FixtureModifyProductVariables(mutator func(variables FixtureProductVariables)) FixturePreparer { 386 return FixtureModifyConfig(func(config Config) { 387 productVariables := FixtureProductVariables{&config.productVariables} 388 mutator(productVariables) 389 }) 390} 391 392var PrepareForSkipTestOnMac = newSimpleFixturePreparer(func(fixture *fixture) { 393 if runtime.GOOS != "linux" { 394 fixture.t.Skip("Test is only supported on linux.") 395 } 396}) 397 398// PrepareForDebug_DO_NOT_SUBMIT puts the fixture into debug which will cause it to output its 399// state before running the test. 400// 401// This must only be added temporarily to a test for local debugging and must be removed from the 402// test before submitting. 403var PrepareForDebug_DO_NOT_SUBMIT = newSimpleFixturePreparer(func(fixture *fixture) { 404 fixture.debug = true 405}) 406 407// GroupFixturePreparers creates a composite FixturePreparer that is equivalent to applying each of 408// the supplied FixturePreparer instances in order. 409// 410// Before preparing the fixture the list of preparers is flattened by replacing each 411// instance of GroupFixturePreparers with its contents. 412func GroupFixturePreparers(preparers ...FixturePreparer) FixturePreparer { 413 all := dedupAndFlattenPreparers(nil, preparers) 414 return newFixturePreparer(all) 415} 416 417// NullFixturePreparer is a preparer that does nothing. 418var NullFixturePreparer = GroupFixturePreparers() 419 420// OptionalFixturePreparer will return the supplied preparer if it is non-nil, otherwise it will 421// return the NullFixturePreparer 422func OptionalFixturePreparer(preparer FixturePreparer) FixturePreparer { 423 if preparer == nil { 424 return NullFixturePreparer 425 } else { 426 return preparer 427 } 428} 429 430// FixturePreparer provides the ability to create, modify and then run tests within a fixture. 431type FixturePreparer interface { 432 // Return the flattened and deduped list of simpleFixturePreparer pointers. 433 list() []*simpleFixturePreparer 434 435 // Create a Fixture. 436 Fixture(t *testing.T) Fixture 437 438 // ExtendWithErrorHandler creates a new FixturePreparer that will use the supplied error handler 439 // to check the errors (may be 0) reported by the test. 440 // 441 // The default handlers is FixtureExpectsNoErrors which will fail the go test immediately if any 442 // errors are reported. 443 ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixturePreparer 444 445 // Run the test, checking any errors reported and returning a TestResult instance. 446 // 447 // Shorthand for Fixture(t).RunTest() 448 RunTest(t *testing.T) *TestResult 449 450 // RunTestWithCustomResult runs the test just as RunTest(t) does but instead of returning a 451 // *TestResult it returns the CustomTestResult that was returned by the custom 452 // FixtureTestRunner.PostParseProcessor method that ran the test, or the *TestResult if that 453 // method returned nil. 454 // 455 // This method must be used when needing to access custom data collected by the 456 // FixtureTestRunner.PostParseProcessor method. 457 // 458 // e.g. something like this 459 // 460 // preparers := ...FixtureSetTestRunner(&myTestRunner)... 461 // customResult := preparers.RunTestWithCustomResult(t).(*myCustomTestResult) 462 // doSomething(customResult.data) 463 RunTestWithCustomResult(t *testing.T) CustomTestResult 464 465 // Run the test with the supplied Android.bp file. 466 // 467 // preparer.RunTestWithBp(t, bp) is shorthand for 468 // android.GroupFixturePreparers(preparer, android.FixtureWithRootAndroidBp(bp)).RunTest(t) 469 RunTestWithBp(t *testing.T, bp string) *TestResult 470 471 // RunTestWithConfig is a temporary method added to help ease the migration of existing tests to 472 // the test fixture. 473 // 474 // In order to allow the Config object to be customized separately to the TestContext a lot of 475 // existing test code has `test...WithConfig` funcs that allow the Config object to be supplied 476 // from the test and then have the TestContext created and configured automatically. e.g. 477 // testCcWithConfig, testCcErrorWithConfig, testJavaWithConfig, etc. 478 // 479 // This method allows those methods to be migrated to use the test fixture pattern without 480 // requiring that every test that uses those methods be migrated at the same time. That allows 481 // those tests to benefit from correctness in the order of registration quickly. 482 // 483 // This method discards the config (along with its mock file system, product variables, 484 // environment, etc.) that may have been set up by FixturePreparers. 485 // 486 // deprecated 487 RunTestWithConfig(t *testing.T, config Config) *TestResult 488} 489 490// dedupAndFlattenPreparers removes any duplicates and flattens any composite FixturePreparer 491// instances. 492// 493// base - a list of already flattened and deduped preparers that will be applied first before 494// 495// the list of additional preparers. Any duplicates of these in the additional preparers 496// will be ignored. 497// 498// preparers - a list of additional unflattened, undeduped preparers that will be applied after the 499// 500// base preparers. 501// 502// Returns a deduped and flattened list of the preparers starting with the ones in base with any 503// additional ones from the preparers list added afterwards. 504func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers []FixturePreparer) []*simpleFixturePreparer { 505 if len(preparers) == 0 { 506 return base 507 } 508 509 list := make([]*simpleFixturePreparer, len(base)) 510 visited := make(map[*simpleFixturePreparer]struct{}) 511 512 // Mark the already flattened and deduped preparers, if any, as having been seen so that 513 // duplicates of these in the additional preparers will be discarded. Add them to the output 514 // list. 515 for i, s := range base { 516 visited[s] = struct{}{} 517 list[i] = s 518 } 519 520 for _, p := range preparers { 521 for _, s := range p.list() { 522 if _, seen := visited[s]; !seen { 523 visited[s] = struct{}{} 524 list = append(list, s) 525 } 526 } 527 } 528 529 return list 530} 531 532// compositeFixturePreparer is a FixturePreparer created from a list of fixture preparers. 533type compositeFixturePreparer struct { 534 baseFixturePreparer 535 // The flattened and deduped list of simpleFixturePreparer pointers encapsulated within this 536 // composite preparer. 537 preparers []*simpleFixturePreparer 538} 539 540func (c *compositeFixturePreparer) list() []*simpleFixturePreparer { 541 return c.preparers 542} 543 544func newFixturePreparer(preparers []*simpleFixturePreparer) FixturePreparer { 545 if len(preparers) == 1 { 546 return preparers[0] 547 } 548 p := &compositeFixturePreparer{ 549 preparers: preparers, 550 } 551 p.initBaseFixturePreparer(p) 552 return p 553} 554 555// simpleFixturePreparer is a FixturePreparer that applies a function to a fixture. 556type simpleFixturePreparer struct { 557 baseFixturePreparer 558 function func(fixture *fixture) 559} 560 561func (s *simpleFixturePreparer) list() []*simpleFixturePreparer { 562 return []*simpleFixturePreparer{s} 563} 564 565func newSimpleFixturePreparer(preparer func(fixture *fixture)) FixturePreparer { 566 p := &simpleFixturePreparer{function: preparer} 567 p.initBaseFixturePreparer(p) 568 return p 569} 570 571// FixtureErrorHandler determines how to respond to errors reported by the code under test. 572// 573// Some possible responses: 574// - Fail the test if any errors are reported, see FixtureExpectsNoErrors. 575// - Fail the test if at least one error that matches a pattern is not reported see 576// FixtureExpectsAtLeastOneErrorMatchingPattern 577// - Fail the test if any unexpected errors are reported. 578// 579// Although at the moment all the error handlers are implemented as simply a wrapper around a 580// function this is defined as an interface to allow future enhancements, e.g. provide different 581// ways other than patterns to match an error and to combine handlers together. 582type FixtureErrorHandler interface { 583 // CheckErrors checks the errors reported. 584 // 585 // The supplied result can be used to access the state of the code under test just as the main 586 // body of the test would but if any errors other than ones expected are reported the state may 587 // be indeterminate. 588 CheckErrors(t *testing.T, result *TestResult) 589} 590 591type simpleErrorHandler struct { 592 function func(t *testing.T, result *TestResult) 593} 594 595func (h simpleErrorHandler) CheckErrors(t *testing.T, result *TestResult) { 596 t.Helper() 597 h.function(t, result) 598} 599 600// The default fixture error handler. 601// 602// Will fail the test immediately if any errors are reported. 603// 604// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within 605// which the test is being run which means that the RunTest() method will not return. 606var FixtureExpectsNoErrors = FixtureCustomErrorHandler( 607 func(t *testing.T, result *TestResult) { 608 t.Helper() 609 FailIfErrored(t, result.Errs) 610 }, 611) 612 613// FixtureIgnoreErrors ignores any errors. 614// 615// If this is used then it is the responsibility of the test to check the TestResult.Errs does not 616// contain any unexpected errors. 617var FixtureIgnoreErrors = FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) { 618 // Ignore the errors 619}) 620 621// FixtureExpectsAtLeastOneMatchingError returns an error handler that will cause the test to fail 622// if at least one error that matches the regular expression is not found. 623// 624// The test will be failed if: 625// * No errors are reported. 626// * One or more errors are reported but none match the pattern. 627// 628// The test will not fail if: 629// * Multiple errors are reported that do not match the pattern as long as one does match. 630// 631// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within 632// which the test is being run which means that the RunTest() method will not return. 633func FixtureExpectsAtLeastOneErrorMatchingPattern(pattern string) FixtureErrorHandler { 634 return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) { 635 t.Helper() 636 if !FailIfNoMatchingErrors(t, pattern, result.Errs) { 637 t.FailNow() 638 } 639 }) 640} 641 642// FixtureExpectsOneErrorToMatchPerPattern returns an error handler that will cause the test to fail 643// if there are any unexpected errors. 644// 645// The test will be failed if: 646// * The number of errors reported does not exactly match the patterns. 647// * One or more of the reported errors do not match a pattern. 648// * No patterns are provided and one or more errors are reported. 649// 650// The test will not fail if: 651// * One or more of the patterns does not match an error. 652// 653// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within 654// which the test is being run which means that the RunTest() method will not return. 655func FixtureExpectsAllErrorsToMatchAPattern(patterns []string) FixtureErrorHandler { 656 return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) { 657 t.Helper() 658 CheckErrorsAgainstExpectations(t, result.Errs, patterns) 659 }) 660} 661 662// FixtureExpectsOneErrorPattern returns an error handler that will cause the test to fail 663// if there is more than one error or the error does not match the pattern. 664// 665// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within 666// which the test is being run which means that the RunTest() method will not return. 667func FixtureExpectsOneErrorPattern(pattern string) FixtureErrorHandler { 668 return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) { 669 t.Helper() 670 CheckErrorsAgainstExpectations(t, result.Errs, []string{pattern}) 671 }) 672} 673 674// FixtureCustomErrorHandler creates a custom error handler 675func FixtureCustomErrorHandler(function func(t *testing.T, result *TestResult)) FixtureErrorHandler { 676 return simpleErrorHandler{ 677 function: function, 678 } 679} 680 681// Fixture defines the test environment. 682type Fixture interface { 683 // Config returns the fixture's configuration. 684 Config() Config 685 686 // Context returns the fixture's test context. 687 Context() *TestContext 688 689 // MockFS returns the fixture's mock filesystem. 690 MockFS() MockFS 691 692 // Run the test, checking any errors reported and returning a TestResult instance. 693 RunTest() CustomTestResult 694} 695 696// Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods. 697type testContext struct { 698 *TestContext 699} 700 701// The result of running a test. 702type TestResult struct { 703 testContext 704 705 fixture *fixture 706 Config Config 707 708 // The errors that were reported during the test. 709 Errs []error 710 711 // The ninja deps is a list of the ninja files dependencies that were added by the modules and 712 // singletons via the *.AddNinjaFileDeps() methods. 713 NinjaDeps []string 714} 715 716func (r *TestResult) testResult() *TestResult { return r } 717 718// CustomTestResult is the interface that FixtureTestRunner implementations who wish to return 719// custom data must implement. It must embed *TestResult and initialize that to the value passed 720// into the method. It is returned from the FixtureTestRunner.FinalPreparer, passed into the 721// FixtureTestRunner.PostParseProcessor and returned from FixturePreparer.RunTestWithCustomResult. 722// 723// e.g. something like this: 724// 725// type myCustomTestResult struct { 726// *android.TestResult 727// data []string 728// } 729// 730// func (r *myTestRunner) FinalPreparer(result *TestResult) CustomTestResult { 731// ... do some final test preparation ... 732// return &myCustomTestResult{TestResult: result) 733// } 734// 735// func (r *myTestRunner) PostParseProcessor(result CustomTestResult) { 736// ... 737// myData := []string {....} 738// ... 739// customResult := result.(*myCustomTestResult) 740// customResult.data = myData 741// } 742type CustomTestResult interface { 743 // testResult returns the embedded *TestResult. 744 testResult() *TestResult 745} 746 747var _ CustomTestResult = (*TestResult)(nil) 748 749type TestPathContext struct { 750 *TestResult 751} 752 753var _ PathContext = &TestPathContext{} 754 755func (t *TestPathContext) Config() Config { 756 return t.TestResult.Config 757} 758 759func (t *TestPathContext) AddNinjaFileDeps(deps ...string) { 760 panic("unimplemented") 761} 762 763func createFixture(t *testing.T, buildDir string, preparers []*simpleFixturePreparer) Fixture { 764 config := TestConfig(buildDir, nil, "", nil) 765 ctx := newTestContextForFixture(config) 766 fixture := &fixture{ 767 preparers: preparers, 768 t: t, 769 config: config, 770 ctx: ctx, 771 mockFS: make(MockFS), 772 // Set the default error handler. 773 errorHandler: FixtureExpectsNoErrors, 774 } 775 776 for _, preparer := range preparers { 777 preparer.function(fixture) 778 } 779 780 return fixture 781} 782 783type baseFixturePreparer struct { 784 self FixturePreparer 785} 786 787func (b *baseFixturePreparer) initBaseFixturePreparer(self FixturePreparer) { 788 b.self = self 789} 790 791func (b *baseFixturePreparer) Fixture(t *testing.T) Fixture { 792 return createFixture(t, t.TempDir(), b.self.list()) 793} 794 795func (b *baseFixturePreparer) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixturePreparer { 796 return GroupFixturePreparers(b.self, newSimpleFixturePreparer(func(fixture *fixture) { 797 fixture.errorHandler = errorHandler 798 })) 799} 800 801func (b *baseFixturePreparer) RunTest(t *testing.T) *TestResult { 802 t.Helper() 803 return b.RunTestWithCustomResult(t).testResult() 804} 805 806func (b *baseFixturePreparer) RunTestWithCustomResult(t *testing.T) CustomTestResult { 807 t.Helper() 808 fixture := b.self.Fixture(t) 809 return fixture.RunTest() 810} 811 812func (b *baseFixturePreparer) RunTestWithBp(t *testing.T, bp string) *TestResult { 813 t.Helper() 814 return GroupFixturePreparers(b.self, FixtureWithRootAndroidBp(bp)).RunTest(t) 815} 816 817func (b *baseFixturePreparer) RunTestWithConfig(t *testing.T, config Config) *TestResult { 818 t.Helper() 819 // Create the fixture as normal. 820 fixture := b.self.Fixture(t).(*fixture) 821 822 // Discard the mock filesystem as otherwise that will override the one in the config. 823 fixture.mockFS = nil 824 825 // Replace the config with the supplied one in the fixture. 826 fixture.config = config 827 828 // Ditto with config derived information in the TestContext. 829 ctx := fixture.ctx 830 ctx.config = config 831 ctx.SetFs(ctx.config.fs) 832 if ctx.config.mockBpList != "" { 833 ctx.SetModuleListFile(ctx.config.mockBpList) 834 } 835 836 return fixture.RunTest().testResult() 837} 838 839type fixture struct { 840 // The preparers used to create this fixture. 841 preparers []*simpleFixturePreparer 842 843 // The test runner used in this fixture, defaults to defaultTestRunner if not set. 844 testRunner FixtureTestRunner 845 846 // The gotest state of the go test within which this was created. 847 t *testing.T 848 849 // The configuration prepared for this fixture. 850 config Config 851 852 // The test context prepared for this fixture. 853 ctx *TestContext 854 855 // The mock filesystem prepared for this fixture. 856 mockFS MockFS 857 858 // The error handler used to check the errors, if any, that are reported. 859 errorHandler FixtureErrorHandler 860 861 // Debug mode status 862 debug bool 863} 864 865func (f *fixture) Config() Config { 866 return f.config 867} 868 869func (f *fixture) Context() *TestContext { 870 return f.ctx 871} 872 873func (f *fixture) MockFS() MockFS { 874 return f.mockFS 875} 876 877func (f *fixture) RunTest() CustomTestResult { 878 f.t.Helper() 879 880 // If in debug mode output the state of the fixture before running the test. 881 if f.debug { 882 f.outputDebugState() 883 } 884 885 ctx := f.ctx 886 887 // Do not use the fixture's mockFS to initialize the config's mock file system if it has been 888 // cleared by RunTestWithConfig. 889 if f.mockFS != nil { 890 // The TestConfig() method assumes that the mock filesystem is available when creating so 891 // creates the mock file system immediately. Similarly, the NewTestContext(Config) method 892 // assumes that the supplied Config's FileSystem has been properly initialized before it is 893 // called and so it takes its own reference to the filesystem. However, fixtures create the 894 // Config and TestContext early so they can be modified by preparers at which time the mockFS 895 // has not been populated (because it too is modified by preparers). So, this reinitializes the 896 // Config and TestContext's FileSystem using the now populated mockFS. 897 f.config.mockFileSystem("", f.mockFS) 898 899 ctx.SetFs(ctx.config.fs) 900 if ctx.config.mockBpList != "" { 901 ctx.SetModuleListFile(ctx.config.mockBpList) 902 } 903 } 904 905 // Create and set the Context's NameInterface. It needs to be created here as it depends on the 906 // configuration that has been prepared for this fixture. 907 resolver := NewNameResolver(ctx.config) 908 909 // Set the NameInterface in the main Context. 910 ctx.SetNameInterface(resolver) 911 912 // Set the NameResolver in the TestContext. 913 ctx.NameResolver = resolver 914 915 // If test runner has not been set then use the default runner. 916 if f.testRunner == nil { 917 f.testRunner = defaultTestRunner 918 } 919 920 // Create the result to collate result information. 921 result := &TestResult{ 922 testContext: testContext{ctx}, 923 fixture: f, 924 Config: f.config, 925 } 926 927 // Do any last minute preparation before parsing the blueprint files. 928 customResult := f.testRunner.FinalPreparer(result) 929 930 // Parse the blueprint files adding the information to the result. 931 extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored") 932 result.NinjaDeps = append(result.NinjaDeps, extraNinjaDeps...) 933 result.Errs = append(result.Errs, errs...) 934 935 if len(result.Errs) == 0 { 936 // If parsing the blueprint files was successful then perform any additional processing. 937 f.testRunner.PostParseProcessor(customResult) 938 } 939 940 f.errorHandler.CheckErrors(f.t, result) 941 942 return customResult 943} 944 945// standardTestRunner is the implementation of the default test runner 946type standardTestRunner struct{} 947 948func (s *standardTestRunner) FinalPreparer(result *TestResult) CustomTestResult { 949 // Register the hard coded mutators and singletons used by the standard Soong build as well as 950 // any additional instances that have been registered with this fixture. 951 result.TestContext.Register() 952 return result 953} 954 955func (s *standardTestRunner) PostParseProcessor(customResult CustomTestResult) { 956 result := customResult.(*TestResult) 957 ctx := result.TestContext 958 cfg := result.Config 959 // Prepare the build actions, i.e. run all the mutators, singletons and then invoke the 960 // GenerateAndroidBuildActions methods on all the modules. 961 extraNinjaDeps, errs := ctx.PrepareBuildActions(cfg) 962 result.NinjaDeps = append(result.NinjaDeps, extraNinjaDeps...) 963 result.CollateErrs(errs) 964} 965 966var defaultTestRunner FixtureTestRunner = &standardTestRunner{} 967 968func (f *fixture) outputDebugState() { 969 fmt.Printf("Begin Fixture State for %s\n", f.t.Name()) 970 if len(f.config.env) == 0 { 971 fmt.Printf(" Fixture Env is empty\n") 972 } else { 973 fmt.Printf(" Begin Env\n") 974 for k, v := range f.config.env { 975 fmt.Printf(" - %s=%s\n", k, v) 976 } 977 fmt.Printf(" End Env\n") 978 } 979 if len(f.mockFS) == 0 { 980 fmt.Printf(" Mock FS is empty\n") 981 } else { 982 fmt.Printf(" Begin Mock FS Contents\n") 983 for p, c := range f.mockFS { 984 if c == nil { 985 fmt.Printf("\n - %s: nil\n", p) 986 } else { 987 contents := string(c) 988 separator := " ========================================================================" 989 fmt.Printf(" - %s\n%s\n", p, separator) 990 for i, line := range strings.Split(contents, "\n") { 991 fmt.Printf(" %6d: %s\n", i+1, line) 992 } 993 fmt.Printf("%s\n", separator) 994 } 995 } 996 fmt.Printf(" End Mock FS Contents\n") 997 } 998 fmt.Printf("End Fixture State for %s\n", f.t.Name()) 999} 1000 1001// NormalizePathForTesting removes the test invocation specific build directory from the supplied 1002// path. 1003// 1004// If the path is within the build directory (e.g. an OutputPath) then this returns the relative 1005// path to avoid tests having to deal with the dynamically generated build directory. 1006// 1007// Otherwise, this returns the supplied path as it is almost certainly a source path that is 1008// relative to the root of the source tree. 1009// 1010// Even though some information is removed from some paths and not others it should be possible to 1011// differentiate between them by the paths themselves, e.g. output paths will likely include 1012// ".intermediates" but source paths won't. 1013func (r *TestResult) NormalizePathForTesting(path Path) string { 1014 pathContext := PathContextForTesting(r.Config) 1015 pathAsString := path.String() 1016 if rel, isRel := MaybeRel(pathContext, r.Config.SoongOutDir(), pathAsString); isRel { 1017 return rel 1018 } 1019 return pathAsString 1020} 1021 1022// NormalizePathsForTesting normalizes each path in the supplied list and returns their normalized 1023// forms. 1024func (r *TestResult) NormalizePathsForTesting(paths Paths) []string { 1025 var result []string 1026 for _, path := range paths { 1027 result = append(result, r.NormalizePathForTesting(path)) 1028 } 1029 return result 1030} 1031 1032// Preparer will return a FixturePreparer encapsulating all the preparers used to create the fixture 1033// that produced this result. 1034// 1035// e.g. assuming that this result was created by running: 1036// 1037// GroupFixturePreparers(preparer1, preparer2, preparer3).RunTest(t) 1038// 1039// Then this method will be equivalent to running: 1040// 1041// GroupFixturePreparers(preparer1, preparer2, preparer3) 1042// 1043// This is intended for use by tests whose output is Android.bp files to verify that those files 1044// are valid, e.g. tests of the snapshots produced by the sdk module type. 1045func (r *TestResult) Preparer() FixturePreparer { 1046 return newFixturePreparer(r.fixture.preparers) 1047} 1048 1049// Module returns the module with the specific name and of the specified variant. 1050func (r *TestResult) Module(name string, variant string) Module { 1051 return r.ModuleForTests(name, variant).Module() 1052} 1053 1054// CollateErrs adds additional errors to the result and returns true if there is more than one 1055// error in the result. 1056func (r *TestResult) CollateErrs(errs []error) bool { 1057 r.Errs = append(r.Errs, errs...) 1058 return len(r.Errs) > 0 1059} 1060