• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
278func FixtureRegisterWithContext(registeringFunc func(ctx RegistrationContext)) FixturePreparer {
279	return FixtureModifyContext(func(ctx *TestContext) { registeringFunc(ctx) })
280}
281
282// Modify the mock filesystem
283func FixtureModifyMockFS(mutator func(fs MockFS)) FixturePreparer {
284	return newSimpleFixturePreparer(func(f *fixture) {
285		mutator(f.mockFS)
286
287		// Make sure that invalid paths were not added to the mock filesystem.
288		for p, _ := range f.mockFS {
289			validateFixtureMockFSPath(p)
290		}
291	})
292}
293
294// Merge the supplied file system into the mock filesystem.
295//
296// Paths that already exist in the mock file system are overridden.
297func FixtureMergeMockFs(mockFS MockFS) FixturePreparer {
298	return FixtureModifyMockFS(func(fs MockFS) {
299		fs.Merge(mockFS)
300	})
301}
302
303// Add a file to the mock filesystem
304//
305// Fail if the filesystem already contains a file with that path, use FixtureOverrideFile instead.
306func FixtureAddFile(path string, contents []byte) FixturePreparer {
307	return FixtureModifyMockFS(func(fs MockFS) {
308		validateFixtureMockFSPath(path)
309		if _, ok := fs[path]; ok {
310			panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists, use FixtureOverride*File instead", path))
311		}
312		fs[path] = contents
313	})
314}
315
316// Add a text file to the mock filesystem
317//
318// Fail if the filesystem already contains a file with that path.
319func FixtureAddTextFile(path string, contents string) FixturePreparer {
320	return FixtureAddFile(path, []byte(contents))
321}
322
323// Override a file in the mock filesystem
324//
325// If the file does not exist this behaves as FixtureAddFile.
326func FixtureOverrideFile(path string, contents []byte) FixturePreparer {
327	return FixtureModifyMockFS(func(fs MockFS) {
328		fs[path] = contents
329	})
330}
331
332// Override a text file in the mock filesystem
333//
334// If the file does not exist this behaves as FixtureAddTextFile.
335func FixtureOverrideTextFile(path string, contents string) FixturePreparer {
336	return FixtureOverrideFile(path, []byte(contents))
337}
338
339// Add the root Android.bp file with the supplied contents.
340func FixtureWithRootAndroidBp(contents string) FixturePreparer {
341	return FixtureAddTextFile("Android.bp", contents)
342}
343
344// Merge some environment variables into the fixture.
345func FixtureMergeEnv(env map[string]string) FixturePreparer {
346	return FixtureModifyConfig(func(config Config) {
347		for k, v := range env {
348			if k == "PATH" {
349				panic("Cannot set PATH environment variable")
350			}
351			config.env[k] = v
352		}
353	})
354}
355
356// Modify the env.
357//
358// Will panic if the mutator changes the PATH environment variable.
359func FixtureModifyEnv(mutator func(env map[string]string)) FixturePreparer {
360	return FixtureModifyConfig(func(config Config) {
361		oldPath := config.env["PATH"]
362		mutator(config.env)
363		newPath := config.env["PATH"]
364		if newPath != oldPath {
365			panic(fmt.Errorf("Cannot change PATH environment variable from %q to %q", oldPath, newPath))
366		}
367	})
368}
369
370// Allow access to the product variables when preparing the fixture.
371type FixtureProductVariables struct {
372	*productVariables
373}
374
375// Modify product variables.
376func FixtureModifyProductVariables(mutator func(variables FixtureProductVariables)) FixturePreparer {
377	return FixtureModifyConfig(func(config Config) {
378		productVariables := FixtureProductVariables{&config.productVariables}
379		mutator(productVariables)
380	})
381}
382
383var PrepareForSkipTestOnMac = newSimpleFixturePreparer(func(fixture *fixture) {
384	if runtime.GOOS != "linux" {
385		fixture.t.Skip("Test is only supported on linux.")
386	}
387})
388
389// PrepareForDebug_DO_NOT_SUBMIT puts the fixture into debug which will cause it to output its
390// state before running the test.
391//
392// This must only be added temporarily to a test for local debugging and must be removed from the
393// test before submitting.
394var PrepareForDebug_DO_NOT_SUBMIT = newSimpleFixturePreparer(func(fixture *fixture) {
395	fixture.debug = true
396})
397
398// GroupFixturePreparers creates a composite FixturePreparer that is equivalent to applying each of
399// the supplied FixturePreparer instances in order.
400//
401// Before preparing the fixture the list of preparers is flattened by replacing each
402// instance of GroupFixturePreparers with its contents.
403func GroupFixturePreparers(preparers ...FixturePreparer) FixturePreparer {
404	all := dedupAndFlattenPreparers(nil, preparers)
405	return newFixturePreparer(all)
406}
407
408// NullFixturePreparer is a preparer that does nothing.
409var NullFixturePreparer = GroupFixturePreparers()
410
411// OptionalFixturePreparer will return the supplied preparer if it is non-nil, otherwise it will
412// return the NullFixturePreparer
413func OptionalFixturePreparer(preparer FixturePreparer) FixturePreparer {
414	if preparer == nil {
415		return NullFixturePreparer
416	} else {
417		return preparer
418	}
419}
420
421// FixturePreparer provides the ability to create, modify and then run tests within a fixture.
422type FixturePreparer interface {
423	// Return the flattened and deduped list of simpleFixturePreparer pointers.
424	list() []*simpleFixturePreparer
425
426	// Create a Fixture.
427	Fixture(t *testing.T) Fixture
428
429	// ExtendWithErrorHandler creates a new FixturePreparer that will use the supplied error handler
430	// to check the errors (may be 0) reported by the test.
431	//
432	// The default handlers is FixtureExpectsNoErrors which will fail the go test immediately if any
433	// errors are reported.
434	ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixturePreparer
435
436	// Run the test, checking any errors reported and returning a TestResult instance.
437	//
438	// Shorthand for Fixture(t).RunTest()
439	RunTest(t *testing.T) *TestResult
440
441	// RunTestWithCustomResult runs the test just as RunTest(t) does but instead of returning a
442	// *TestResult it returns the CustomTestResult that was returned by the custom
443	// FixtureTestRunner.PostParseProcessor method that ran the test, or the *TestResult if that
444	// method returned nil.
445	//
446	// This method must be used when needing to access custom data collected by the
447	// FixtureTestRunner.PostParseProcessor method.
448	//
449	// e.g. something like this
450	//
451	//   preparers := ...FixtureSetTestRunner(&myTestRunner)...
452	//   customResult := preparers.RunTestWithCustomResult(t).(*myCustomTestResult)
453	//   doSomething(customResult.data)
454	RunTestWithCustomResult(t *testing.T) CustomTestResult
455
456	// Run the test with the supplied Android.bp file.
457	//
458	// preparer.RunTestWithBp(t, bp) is shorthand for
459	// android.GroupFixturePreparers(preparer, android.FixtureWithRootAndroidBp(bp)).RunTest(t)
460	RunTestWithBp(t *testing.T, bp string) *TestResult
461
462	// RunTestWithConfig is a temporary method added to help ease the migration of existing tests to
463	// the test fixture.
464	//
465	// In order to allow the Config object to be customized separately to the TestContext a lot of
466	// existing test code has `test...WithConfig` funcs that allow the Config object to be supplied
467	// from the test and then have the TestContext created and configured automatically. e.g.
468	// testCcWithConfig, testCcErrorWithConfig, testJavaWithConfig, etc.
469	//
470	// This method allows those methods to be migrated to use the test fixture pattern without
471	// requiring that every test that uses those methods be migrated at the same time. That allows
472	// those tests to benefit from correctness in the order of registration quickly.
473	//
474	// This method discards the config (along with its mock file system, product variables,
475	// environment, etc.) that may have been set up by FixturePreparers.
476	//
477	// deprecated
478	RunTestWithConfig(t *testing.T, config Config) *TestResult
479}
480
481// dedupAndFlattenPreparers removes any duplicates and flattens any composite FixturePreparer
482// instances.
483//
484// base      - a list of already flattened and deduped preparers that will be applied first before
485//
486//	the list of additional preparers. Any duplicates of these in the additional preparers
487//	will be ignored.
488//
489// preparers - a list of additional unflattened, undeduped preparers that will be applied after the
490//
491//	base preparers.
492//
493// Returns a deduped and flattened list of the preparers starting with the ones in base with any
494// additional ones from the preparers list added afterwards.
495func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers []FixturePreparer) []*simpleFixturePreparer {
496	if len(preparers) == 0 {
497		return base
498	}
499
500	list := make([]*simpleFixturePreparer, len(base))
501	visited := make(map[*simpleFixturePreparer]struct{})
502
503	// Mark the already flattened and deduped preparers, if any, as having been seen so that
504	// duplicates of these in the additional preparers will be discarded. Add them to the output
505	// list.
506	for i, s := range base {
507		visited[s] = struct{}{}
508		list[i] = s
509	}
510
511	for _, p := range preparers {
512		for _, s := range p.list() {
513			if _, seen := visited[s]; !seen {
514				visited[s] = struct{}{}
515				list = append(list, s)
516			}
517		}
518	}
519
520	return list
521}
522
523// compositeFixturePreparer is a FixturePreparer created from a list of fixture preparers.
524type compositeFixturePreparer struct {
525	baseFixturePreparer
526	// The flattened and deduped list of simpleFixturePreparer pointers encapsulated within this
527	// composite preparer.
528	preparers []*simpleFixturePreparer
529}
530
531func (c *compositeFixturePreparer) list() []*simpleFixturePreparer {
532	return c.preparers
533}
534
535func newFixturePreparer(preparers []*simpleFixturePreparer) FixturePreparer {
536	if len(preparers) == 1 {
537		return preparers[0]
538	}
539	p := &compositeFixturePreparer{
540		preparers: preparers,
541	}
542	p.initBaseFixturePreparer(p)
543	return p
544}
545
546// simpleFixturePreparer is a FixturePreparer that applies a function to a fixture.
547type simpleFixturePreparer struct {
548	baseFixturePreparer
549	function func(fixture *fixture)
550}
551
552func (s *simpleFixturePreparer) list() []*simpleFixturePreparer {
553	return []*simpleFixturePreparer{s}
554}
555
556func newSimpleFixturePreparer(preparer func(fixture *fixture)) FixturePreparer {
557	p := &simpleFixturePreparer{function: preparer}
558	p.initBaseFixturePreparer(p)
559	return p
560}
561
562// FixtureErrorHandler determines how to respond to errors reported by the code under test.
563//
564// Some possible responses:
565//   - Fail the test if any errors are reported, see FixtureExpectsNoErrors.
566//   - Fail the test if at least one error that matches a pattern is not reported see
567//     FixtureExpectsAtLeastOneErrorMatchingPattern
568//   - Fail the test if any unexpected errors are reported.
569//
570// Although at the moment all the error handlers are implemented as simply a wrapper around a
571// function this is defined as an interface to allow future enhancements, e.g. provide different
572// ways other than patterns to match an error and to combine handlers together.
573type FixtureErrorHandler interface {
574	// CheckErrors checks the errors reported.
575	//
576	// The supplied result can be used to access the state of the code under test just as the main
577	// body of the test would but if any errors other than ones expected are reported the state may
578	// be indeterminate.
579	CheckErrors(t *testing.T, result *TestResult)
580}
581
582type simpleErrorHandler struct {
583	function func(t *testing.T, result *TestResult)
584}
585
586func (h simpleErrorHandler) CheckErrors(t *testing.T, result *TestResult) {
587	t.Helper()
588	h.function(t, result)
589}
590
591// The default fixture error handler.
592//
593// Will fail the test immediately if any errors are reported.
594//
595// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
596// which the test is being run which means that the RunTest() method will not return.
597var FixtureExpectsNoErrors = FixtureCustomErrorHandler(
598	func(t *testing.T, result *TestResult) {
599		t.Helper()
600		FailIfErrored(t, result.Errs)
601	},
602)
603
604// FixtureIgnoreErrors ignores any errors.
605//
606// If this is used then it is the responsibility of the test to check the TestResult.Errs does not
607// contain any unexpected errors.
608var FixtureIgnoreErrors = FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) {
609	// Ignore the errors
610})
611
612// FixtureExpectsAtLeastOneMatchingError returns an error handler that will cause the test to fail
613// if at least one error that matches the regular expression is not found.
614//
615// The test will be failed if:
616// * No errors are reported.
617// * One or more errors are reported but none match the pattern.
618//
619// The test will not fail if:
620// * Multiple errors are reported that do not match the pattern as long as one does match.
621//
622// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
623// which the test is being run which means that the RunTest() method will not return.
624func FixtureExpectsAtLeastOneErrorMatchingPattern(pattern string) FixtureErrorHandler {
625	return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) {
626		t.Helper()
627		if !FailIfNoMatchingErrors(t, pattern, result.Errs) {
628			t.FailNow()
629		}
630	})
631}
632
633// FixtureExpectsOneErrorToMatchPerPattern returns an error handler that will cause the test to fail
634// if there are any unexpected errors.
635//
636// The test will be failed if:
637// * The number of errors reported does not exactly match the patterns.
638// * One or more of the reported errors do not match a pattern.
639// * No patterns are provided and one or more errors are reported.
640//
641// The test will not fail if:
642// * One or more of the patterns does not match an error.
643//
644// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
645// which the test is being run which means that the RunTest() method will not return.
646func FixtureExpectsAllErrorsToMatchAPattern(patterns []string) FixtureErrorHandler {
647	return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) {
648		t.Helper()
649		CheckErrorsAgainstExpectations(t, result.Errs, patterns)
650	})
651}
652
653// FixtureExpectsOneErrorPattern returns an error handler that will cause the test to fail
654// if there is more than one error or the error does not match the pattern.
655//
656// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
657// which the test is being run which means that the RunTest() method will not return.
658func FixtureExpectsOneErrorPattern(pattern string) FixtureErrorHandler {
659	return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) {
660		t.Helper()
661		CheckErrorsAgainstExpectations(t, result.Errs, []string{pattern})
662	})
663}
664
665// FixtureCustomErrorHandler creates a custom error handler
666func FixtureCustomErrorHandler(function func(t *testing.T, result *TestResult)) FixtureErrorHandler {
667	return simpleErrorHandler{
668		function: function,
669	}
670}
671
672// Fixture defines the test environment.
673type Fixture interface {
674	// Config returns the fixture's configuration.
675	Config() Config
676
677	// Context returns the fixture's test context.
678	Context() *TestContext
679
680	// MockFS returns the fixture's mock filesystem.
681	MockFS() MockFS
682
683	// Run the test, checking any errors reported and returning a TestResult instance.
684	RunTest() CustomTestResult
685}
686
687// Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods.
688type testContext struct {
689	*TestContext
690}
691
692// The result of running a test.
693type TestResult struct {
694	testContext
695
696	fixture *fixture
697	Config  Config
698
699	// The errors that were reported during the test.
700	Errs []error
701
702	// The ninja deps is a list of the ninja files dependencies that were added by the modules and
703	// singletons via the *.AddNinjaFileDeps() methods.
704	NinjaDeps []string
705}
706
707func (r *TestResult) testResult() *TestResult { return r }
708
709// CustomTestResult is the interface that FixtureTestRunner implementations who wish to return
710// custom data must implement. It must embed *TestResult and initialize that to the value passed
711// into the method. It is returned from the FixtureTestRunner.FinalPreparer, passed into the
712// FixtureTestRunner.PostParseProcessor and returned from FixturePreparer.RunTestWithCustomResult.
713//
714// e.g. something like this:
715//
716//		type myCustomTestResult struct {
717//		    *android.TestResult
718//		    data []string
719//		}
720//
721//		func (r *myTestRunner) FinalPreparer(result *TestResult) CustomTestResult {
722//	     ... do some final test preparation ...
723//	     return &myCustomTestResult{TestResult: result)
724//	 }
725//
726//		func (r *myTestRunner) PostParseProcessor(result CustomTestResult) {
727//		    ...
728//		    myData := []string {....}
729//		    ...
730//		    customResult := result.(*myCustomTestResult)
731//	     customResult.data = myData
732//		}
733type CustomTestResult interface {
734	// testResult returns the embedded *TestResult.
735	testResult() *TestResult
736}
737
738var _ CustomTestResult = (*TestResult)(nil)
739
740type TestPathContext struct {
741	*TestResult
742}
743
744var _ PathContext = &TestPathContext{}
745
746func (t *TestPathContext) Config() Config {
747	return t.TestResult.Config
748}
749
750func (t *TestPathContext) AddNinjaFileDeps(deps ...string) {
751	panic("unimplemented")
752}
753
754func createFixture(t *testing.T, buildDir string, preparers []*simpleFixturePreparer) Fixture {
755	config := TestConfig(buildDir, nil, "", nil)
756	ctx := newTestContextForFixture(config)
757	fixture := &fixture{
758		preparers: preparers,
759		t:         t,
760		config:    config,
761		ctx:       ctx,
762		mockFS:    make(MockFS),
763		// Set the default error handler.
764		errorHandler: FixtureExpectsNoErrors,
765	}
766
767	for _, preparer := range preparers {
768		preparer.function(fixture)
769	}
770
771	return fixture
772}
773
774type baseFixturePreparer struct {
775	self FixturePreparer
776}
777
778func (b *baseFixturePreparer) initBaseFixturePreparer(self FixturePreparer) {
779	b.self = self
780}
781
782func (b *baseFixturePreparer) Fixture(t *testing.T) Fixture {
783	return createFixture(t, t.TempDir(), b.self.list())
784}
785
786func (b *baseFixturePreparer) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixturePreparer {
787	return GroupFixturePreparers(b.self, newSimpleFixturePreparer(func(fixture *fixture) {
788		fixture.errorHandler = errorHandler
789	}))
790}
791
792func (b *baseFixturePreparer) RunTest(t *testing.T) *TestResult {
793	t.Helper()
794	return b.RunTestWithCustomResult(t).testResult()
795}
796
797func (b *baseFixturePreparer) RunTestWithCustomResult(t *testing.T) CustomTestResult {
798	t.Helper()
799	fixture := b.self.Fixture(t)
800	return fixture.RunTest()
801}
802
803func (b *baseFixturePreparer) RunTestWithBp(t *testing.T, bp string) *TestResult {
804	t.Helper()
805	return GroupFixturePreparers(b.self, FixtureWithRootAndroidBp(bp)).RunTest(t)
806}
807
808func (b *baseFixturePreparer) RunTestWithConfig(t *testing.T, config Config) *TestResult {
809	t.Helper()
810	// Create the fixture as normal.
811	fixture := b.self.Fixture(t).(*fixture)
812
813	// Discard the mock filesystem as otherwise that will override the one in the config.
814	fixture.mockFS = nil
815
816	// Replace the config with the supplied one in the fixture.
817	fixture.config = config
818
819	// Ditto with config derived information in the TestContext.
820	ctx := fixture.ctx
821	ctx.config = config
822	ctx.SetFs(ctx.config.fs)
823	if ctx.config.mockBpList != "" {
824		ctx.SetModuleListFile(ctx.config.mockBpList)
825	}
826
827	return fixture.RunTest().testResult()
828}
829
830type fixture struct {
831	// The preparers used to create this fixture.
832	preparers []*simpleFixturePreparer
833
834	// The test runner used in this fixture, defaults to defaultTestRunner if not set.
835	testRunner FixtureTestRunner
836
837	// The gotest state of the go test within which this was created.
838	t *testing.T
839
840	// The configuration prepared for this fixture.
841	config Config
842
843	// The test context prepared for this fixture.
844	ctx *TestContext
845
846	// The mock filesystem prepared for this fixture.
847	mockFS MockFS
848
849	// The error handler used to check the errors, if any, that are reported.
850	errorHandler FixtureErrorHandler
851
852	// Debug mode status
853	debug bool
854}
855
856func (f *fixture) Config() Config {
857	return f.config
858}
859
860func (f *fixture) Context() *TestContext {
861	return f.ctx
862}
863
864func (f *fixture) MockFS() MockFS {
865	return f.mockFS
866}
867
868func (f *fixture) RunTest() CustomTestResult {
869	f.t.Helper()
870
871	// If in debug mode output the state of the fixture before running the test.
872	if f.debug {
873		f.outputDebugState()
874	}
875
876	ctx := f.ctx
877
878	// Do not use the fixture's mockFS to initialize the config's mock file system if it has been
879	// cleared by RunTestWithConfig.
880	if f.mockFS != nil {
881		// The TestConfig() method assumes that the mock filesystem is available when creating so
882		// creates the mock file system immediately. Similarly, the NewTestContext(Config) method
883		// assumes that the supplied Config's FileSystem has been properly initialized before it is
884		// called and so it takes its own reference to the filesystem. However, fixtures create the
885		// Config and TestContext early so they can be modified by preparers at which time the mockFS
886		// has not been populated (because it too is modified by preparers). So, this reinitializes the
887		// Config and TestContext's FileSystem using the now populated mockFS.
888		f.config.mockFileSystem("", f.mockFS)
889
890		ctx.SetFs(ctx.config.fs)
891		if ctx.config.mockBpList != "" {
892			ctx.SetModuleListFile(ctx.config.mockBpList)
893		}
894	}
895
896	// Create and set the Context's NameInterface. It needs to be created here as it depends on the
897	// configuration that has been prepared for this fixture.
898	resolver := NewNameResolver(ctx.config)
899
900	// Set the NameInterface in the main Context.
901	ctx.SetNameInterface(resolver)
902
903	// Set the NameResolver in the TestContext.
904	ctx.NameResolver = resolver
905
906	// If test runner has not been set then use the default runner.
907	if f.testRunner == nil {
908		f.testRunner = defaultTestRunner
909	}
910
911	// Create the result to collate result information.
912	result := &TestResult{
913		testContext: testContext{ctx},
914		fixture:     f,
915		Config:      f.config,
916	}
917
918	// Do any last minute preparation before parsing the blueprint files.
919	customResult := f.testRunner.FinalPreparer(result)
920
921	// Parse the blueprint files adding the information to the result.
922	extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored")
923	result.NinjaDeps = append(result.NinjaDeps, extraNinjaDeps...)
924	result.Errs = append(result.Errs, errs...)
925
926	if len(result.Errs) == 0 {
927		// If parsing the blueprint files was successful then perform any additional processing.
928		f.testRunner.PostParseProcessor(customResult)
929	}
930
931	f.errorHandler.CheckErrors(f.t, result)
932
933	return customResult
934}
935
936// standardTestRunner is the implementation of the default test runner
937type standardTestRunner struct{}
938
939func (s *standardTestRunner) FinalPreparer(result *TestResult) CustomTestResult {
940	// Register the hard coded mutators and singletons used by the standard Soong build as well as
941	// any additional instances that have been registered with this fixture.
942	result.TestContext.Register()
943	return result
944}
945
946func (s *standardTestRunner) PostParseProcessor(customResult CustomTestResult) {
947	result := customResult.(*TestResult)
948	ctx := result.TestContext
949	cfg := result.Config
950	// Prepare the build actions, i.e. run all the mutators, singletons and then invoke the
951	// GenerateAndroidBuildActions methods on all the modules.
952	extraNinjaDeps, errs := ctx.PrepareBuildActions(cfg)
953	result.NinjaDeps = append(result.NinjaDeps, extraNinjaDeps...)
954	result.CollateErrs(errs)
955}
956
957var defaultTestRunner FixtureTestRunner = &standardTestRunner{}
958
959func (f *fixture) outputDebugState() {
960	fmt.Printf("Begin Fixture State for %s\n", f.t.Name())
961	if len(f.config.env) == 0 {
962		fmt.Printf("  Fixture Env is empty\n")
963	} else {
964		fmt.Printf("  Begin Env\n")
965		for k, v := range f.config.env {
966			fmt.Printf("  - %s=%s\n", k, v)
967		}
968		fmt.Printf("  End Env\n")
969	}
970	if len(f.mockFS) == 0 {
971		fmt.Printf("  Mock FS is empty\n")
972	} else {
973		fmt.Printf("  Begin Mock FS Contents\n")
974		for p, c := range f.mockFS {
975			if c == nil {
976				fmt.Printf("\n  - %s: nil\n", p)
977			} else {
978				contents := string(c)
979				separator := "    ========================================================================"
980				fmt.Printf("  - %s\n%s\n", p, separator)
981				for i, line := range strings.Split(contents, "\n") {
982					fmt.Printf("      %6d:    %s\n", i+1, line)
983				}
984				fmt.Printf("%s\n", separator)
985			}
986		}
987		fmt.Printf("  End Mock FS Contents\n")
988	}
989	fmt.Printf("End Fixture State for %s\n", f.t.Name())
990}
991
992// NormalizePathForTesting removes the test invocation specific build directory from the supplied
993// path.
994//
995// If the path is within the build directory (e.g. an OutputPath) then this returns the relative
996// path to avoid tests having to deal with the dynamically generated build directory.
997//
998// Otherwise, this returns the supplied path as it is almost certainly a source path that is
999// relative to the root of the source tree.
1000//
1001// Even though some information is removed from some paths and not others it should be possible to
1002// differentiate between them by the paths themselves, e.g. output paths will likely include
1003// ".intermediates" but source paths won't.
1004func (r *TestResult) NormalizePathForTesting(path Path) string {
1005	pathContext := PathContextForTesting(r.Config)
1006	pathAsString := path.String()
1007	if rel, isRel := MaybeRel(pathContext, r.Config.SoongOutDir(), pathAsString); isRel {
1008		return rel
1009	}
1010	return pathAsString
1011}
1012
1013// NormalizePathsForTesting normalizes each path in the supplied list and returns their normalized
1014// forms.
1015func (r *TestResult) NormalizePathsForTesting(paths Paths) []string {
1016	var result []string
1017	for _, path := range paths {
1018		result = append(result, r.NormalizePathForTesting(path))
1019	}
1020	return result
1021}
1022
1023// Preparer will return a FixturePreparer encapsulating all the preparers used to create the fixture
1024// that produced this result.
1025//
1026// e.g. assuming that this result was created by running:
1027//
1028//	GroupFixturePreparers(preparer1, preparer2, preparer3).RunTest(t)
1029//
1030// Then this method will be equivalent to running:
1031//
1032//	GroupFixturePreparers(preparer1, preparer2, preparer3)
1033//
1034// This is intended for use by tests whose output is Android.bp files to verify that those files
1035// are valid, e.g. tests of the snapshots produced by the sdk module type.
1036func (r *TestResult) Preparer() FixturePreparer {
1037	return newFixturePreparer(r.fixture.preparers)
1038}
1039
1040// Module returns the module with the specific name and of the specified variant.
1041func (r *TestResult) Module(name string, variant string) Module {
1042	return r.ModuleForTests(name, variant).Module()
1043}
1044
1045// CollateErrs adds additional errors to the result and returns true if there is more than one
1046// error in the result.
1047func (r *TestResult) CollateErrs(errs []error) bool {
1048	r.Errs = append(r.Errs, errs...)
1049	return len(r.Errs) > 0
1050}
1051