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