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