blob: f213d66d45b56cbe18f60e49df64261b6c67e032 [file] [log] [blame]
Paul Duffin35816122021-02-24 01:49:52 +00001// 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 (
Paul Duffinbbccfcf2021-03-03 00:44:00 +000018 "fmt"
Paul Duffin35816122021-02-24 01:49:52 +000019 "testing"
20)
21
22// Provides support for creating test fixtures on which tests can be run. Reduces duplication
23// of test setup by allow tests to easily reuse setup code.
24//
25// Fixture
26// =======
27// These determine the environment within which a test can be run. Fixtures are mutable and are
28// created by FixtureFactory instances and mutated by FixturePreparer instances. They are created by
29// first creating a base Fixture (which is essentially empty) and then applying FixturePreparer
30// instances to it to modify the environment.
31//
32// FixtureFactory
33// ==============
34// These are responsible for creating fixtures. Factories are immutable and are intended to be
35// initialized once and reused to create multiple fixtures. Each factory has a list of fixture
36// preparers that prepare a fixture for running a test. Factories can also be used to create other
37// factories by extending them with additional fixture preparers.
38//
39// FixturePreparer
40// ===============
41// These are responsible for modifying a Fixture in preparation for it to run a test. Preparers are
42// intended to be immutable and able to prepare multiple Fixture objects simultaneously without
43// them sharing any data.
44//
45// FixturePreparers are only ever invoked once per test fixture. Prior to invocation the list of
46// FixturePreparers are flattened and deduped while preserving the order they first appear in the
47// list. This makes it easy to reuse, group and combine FixturePreparers together.
48//
49// Each small self contained piece of test setup should be their own FixturePreparer. e.g.
50// * A group of related modules.
51// * A group of related mutators.
52// * A combination of both.
53// * Configuration.
54//
55// They should not overlap, e.g. the same module type should not be registered by different
56// FixturePreparers as using them both would cause a build error. In that case the preparer should
57// be split into separate parts and combined together using FixturePreparers(...).
58//
59// e.g. attempting to use AllPreparers in preparing a Fixture would break as it would attempt to
60// register module bar twice:
61// var Preparer1 = FixtureRegisterWithContext(RegisterModuleFooAndBar)
62// var Preparer2 = FixtureRegisterWithContext(RegisterModuleBarAndBaz)
Paul Duffina560d5a2021-02-28 01:38:51 +000063// var AllPreparers = GroupFixturePreparers(Preparer1, Preparer2)
Paul Duffin35816122021-02-24 01:49:52 +000064//
65// However, when restructured like this it would work fine:
66// var PreparerFoo = FixtureRegisterWithContext(RegisterModuleFoo)
67// var PreparerBar = FixtureRegisterWithContext(RegisterModuleBar)
68// var PreparerBaz = FixtureRegisterWithContext(RegisterModuleBaz)
Paul Duffina560d5a2021-02-28 01:38:51 +000069// var Preparer1 = GroupFixturePreparers(RegisterModuleFoo, RegisterModuleBar)
70// var Preparer2 = GroupFixturePreparers(RegisterModuleBar, RegisterModuleBaz)
71// var AllPreparers = GroupFixturePreparers(Preparer1, Preparer2)
Paul Duffin35816122021-02-24 01:49:52 +000072//
73// As after deduping and flattening AllPreparers would result in the following preparers being
74// applied:
75// 1. PreparerFoo
76// 2. PreparerBar
77// 3. PreparerBaz
78//
79// Preparers can be used for both integration and unit tests.
80//
81// Integration tests typically use all the module types, mutators and singletons that are available
82// for that package to try and replicate the behavior of the runtime build as closely as possible.
83// However, that realism comes at a cost of increased fragility (as they can be broken by changes in
84// many different parts of the build) and also increased runtime, especially if they use lots of
85// singletons and mutators.
86//
87// Unit tests on the other hand try and minimize the amount of code being tested which makes them
88// less susceptible to changes elsewhere in the build and quick to run but at a cost of potentially
89// not testing realistic scenarios.
90//
91// Supporting unit tests effectively require that preparers are available at the lowest granularity
92// possible. Supporting integration tests effectively require that the preparers are organized into
93// groups that provide all the functionality available.
94//
95// At least in terms of tests that check the behavior of build components via processing
96// `Android.bp` there is no clear separation between a unit test and an integration test. Instead
97// they vary from one end that tests a single module (e.g. filegroup) to the other end that tests a
98// whole system of modules, mutators and singletons (e.g. apex + hiddenapi).
99//
100// TestResult
101// ==========
102// These are created by running tests in a Fixture and provide access to the Config and TestContext
103// in which the tests were run.
104//
105// Example
106// =======
107//
108// An exported preparer for use by other packages that need to use java modules.
109//
110// package java
Paul Duffina560d5a2021-02-28 01:38:51 +0000111// var PrepareForIntegrationTestWithJava = GroupFixturePreparers(
Paul Duffin35816122021-02-24 01:49:52 +0000112// android.PrepareForIntegrationTestWithAndroid,
113// FixtureRegisterWithContext(RegisterAGroupOfRelatedModulesMutatorsAndSingletons),
114// FixtureRegisterWithContext(RegisterAnotherGroupOfRelatedModulesMutatorsAndSingletons),
115// ...
116// )
117//
118// Some files to use in tests in the java package.
119//
120// var javaMockFS = android.MockFS{
121// "api/current.txt": nil,
122// "api/removed.txt": nil,
123// ...
124// }
125//
126// A package private factory for use for testing java within the java package.
127//
128// var javaFixtureFactory = NewFixtureFactory(
129// PrepareForIntegrationTestWithJava,
130// FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
131// ctx.RegisterModuleType("test_module", testModule)
132// }),
133// javaMockFS.AddToFixture(),
134// ...
135// }
136//
137// func TestJavaStuff(t *testing.T) {
138// result := javaFixtureFactory.RunTest(t,
139// android.FixtureWithRootAndroidBp(`java_library {....}`),
140// android.MockFS{...}.AddToFixture(),
141// )
142// ... test result ...
143// }
144//
145// package cc
Paul Duffina560d5a2021-02-28 01:38:51 +0000146// var PrepareForTestWithCC = GroupFixturePreparers(
Paul Duffin35816122021-02-24 01:49:52 +0000147// android.PrepareForArchMutator,
148// android.prepareForPrebuilts,
149// FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
150// ...
151// )
152//
153// package apex
154//
Paul Duffina560d5a2021-02-28 01:38:51 +0000155// var PrepareForApex = GroupFixturePreparers(
Paul Duffin35816122021-02-24 01:49:52 +0000156// ...
157// )
158//
159// Use modules and mutators from java, cc and apex. Any duplicate preparers (like
160// android.PrepareForArchMutator) will be automatically deduped.
161//
162// var apexFixtureFactory = android.NewFixtureFactory(
163// PrepareForJava,
164// PrepareForCC,
165// PrepareForApex,
166// )
167
168// Factory for Fixture objects.
169//
170// This is configured with a set of FixturePreparer objects that are used to
171// initialize each Fixture instance this creates.
172type FixtureFactory interface {
173
174 // Creates a copy of this instance and adds some additional preparers.
175 //
176 // Before the preparers are used they are combined with the preparers provided when the factory
177 // was created, any groups of preparers are flattened, and the list is deduped so that each
178 // preparer is only used once. See the file documentation in android/fixture.go for more details.
179 Extend(preparers ...FixturePreparer) FixtureFactory
180
181 // Create a Fixture.
182 Fixture(t *testing.T, preparers ...FixturePreparer) Fixture
183
Paul Duffin46e37742021-03-09 11:55:20 +0000184 // ExtendWithErrorHandler creates a new FixtureFactory that will use the supplied error handler
185 // to check the errors (may be 0) reported by the test.
Paul Duffincfd33742021-02-27 11:59:02 +0000186 //
187 // The default handlers is FixtureExpectsNoErrors which will fail the go test immediately if any
188 // errors are reported.
Paul Duffin46e37742021-03-09 11:55:20 +0000189 ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory
Paul Duffincfd33742021-02-27 11:59:02 +0000190
191 // Run the test, checking any errors reported and returning a TestResult instance.
Paul Duffin35816122021-02-24 01:49:52 +0000192 //
193 // Shorthand for Fixture(t, preparers...).RunTest()
194 RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult
195
196 // Run the test with the supplied Android.bp file.
197 //
198 // Shorthand for RunTest(t, android.FixtureWithRootAndroidBp(bp))
199 RunTestWithBp(t *testing.T, bp string) *TestResult
Paul Duffin72018ad2021-03-04 19:36:49 +0000200
201 // RunTestWithConfig is a temporary method added to help ease the migration of existing tests to
202 // the test fixture.
203 //
204 // In order to allow the Config object to be customized separately to the TestContext a lot of
205 // existing test code has `test...WithConfig` funcs that allow the Config object to be supplied
206 // from the test and then have the TestContext created and configured automatically. e.g.
207 // testCcWithConfig, testCcErrorWithConfig, testJavaWithConfig, etc.
208 //
209 // This method allows those methods to be migrated to use the test fixture pattern without
210 // requiring that every test that uses those methods be migrated at the same time. That allows
211 // those tests to benefit from correctness in the order of registration quickly.
212 //
213 // This method discards the config (along with its mock file system, product variables,
214 // environment, etc.) that may have been set up by FixturePreparers.
215 //
216 // deprecated
217 RunTestWithConfig(t *testing.T, config Config) *TestResult
Paul Duffin35816122021-02-24 01:49:52 +0000218}
219
220// Create a new FixtureFactory that will apply the supplied preparers.
221//
222// The buildDirSupplier is a pointer to the package level buildDir variable that is initialized by
223// the package level setUp method. It has to be a pointer to the variable as the variable will not
Paul Duffindff5ff02021-03-15 15:42:40 +0000224// have been initialized at the time the factory is created. If it is nil then a test specific
225// temporary directory will be created instead.
Paul Duffin35816122021-02-24 01:49:52 +0000226func NewFixtureFactory(buildDirSupplier *string, preparers ...FixturePreparer) FixtureFactory {
227 return &fixtureFactory{
228 buildDirSupplier: buildDirSupplier,
229 preparers: dedupAndFlattenPreparers(nil, preparers),
Paul Duffincfd33742021-02-27 11:59:02 +0000230
231 // Set the default error handler.
232 errorHandler: FixtureExpectsNoErrors,
Paul Duffin35816122021-02-24 01:49:52 +0000233 }
234}
235
236// A set of mock files to add to the mock file system.
237type MockFS map[string][]byte
238
Paul Duffin6e9a4002021-03-11 19:01:26 +0000239// Merge adds the extra entries from the supplied map to this one.
240//
241// Fails if the supplied map files with the same paths are present in both of them.
Paul Duffin35816122021-02-24 01:49:52 +0000242func (fs MockFS) Merge(extra map[string][]byte) {
243 for p, c := range extra {
Paul Duffin6e9a4002021-03-11 19:01:26 +0000244 if _, ok := fs[p]; ok {
245 panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists", p))
246 }
Paul Duffin35816122021-02-24 01:49:52 +0000247 fs[p] = c
248 }
249}
250
251func (fs MockFS) AddToFixture() FixturePreparer {
252 return FixtureMergeMockFs(fs)
253}
254
Paul Duffinae542a52021-03-09 03:15:28 +0000255// FixtureCustomPreparer allows for the modification of any aspect of the fixture.
256//
257// This should only be used if one of the other more specific preparers are not suitable.
258func FixtureCustomPreparer(mutator func(fixture Fixture)) FixturePreparer {
259 return newSimpleFixturePreparer(func(f *fixture) {
260 mutator(f)
261 })
262}
263
Paul Duffin35816122021-02-24 01:49:52 +0000264// Modify the config
265func FixtureModifyConfig(mutator func(config Config)) FixturePreparer {
266 return newSimpleFixturePreparer(func(f *fixture) {
267 mutator(f.config)
268 })
269}
270
271// Modify the config and context
272func FixtureModifyConfigAndContext(mutator func(config Config, ctx *TestContext)) FixturePreparer {
273 return newSimpleFixturePreparer(func(f *fixture) {
274 mutator(f.config, f.ctx)
275 })
276}
277
278// Modify the context
279func FixtureModifyContext(mutator func(ctx *TestContext)) FixturePreparer {
280 return newSimpleFixturePreparer(func(f *fixture) {
281 mutator(f.ctx)
282 })
283}
284
285func FixtureRegisterWithContext(registeringFunc func(ctx RegistrationContext)) FixturePreparer {
286 return FixtureModifyContext(func(ctx *TestContext) { registeringFunc(ctx) })
287}
288
289// Modify the mock filesystem
290func FixtureModifyMockFS(mutator func(fs MockFS)) FixturePreparer {
291 return newSimpleFixturePreparer(func(f *fixture) {
292 mutator(f.mockFS)
293 })
294}
295
296// Merge the supplied file system into the mock filesystem.
297//
298// Paths that already exist in the mock file system are overridden.
299func FixtureMergeMockFs(mockFS MockFS) FixturePreparer {
300 return FixtureModifyMockFS(func(fs MockFS) {
301 fs.Merge(mockFS)
302 })
303}
304
305// Add a file to the mock filesystem
Paul Duffin6e9a4002021-03-11 19:01:26 +0000306//
307// Fail if the filesystem already contains a file with that path, use FixtureOverrideFile instead.
Paul Duffin35816122021-02-24 01:49:52 +0000308func FixtureAddFile(path string, contents []byte) FixturePreparer {
309 return FixtureModifyMockFS(func(fs MockFS) {
Paul Duffin6e9a4002021-03-11 19:01:26 +0000310 if _, ok := fs[path]; ok {
311 panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists, use FixtureOverride*File instead", path))
312 }
Paul Duffin35816122021-02-24 01:49:52 +0000313 fs[path] = contents
314 })
315}
316
317// Add a text file to the mock filesystem
Paul Duffin6e9a4002021-03-11 19:01:26 +0000318//
319// Fail if the filesystem already contains a file with that path.
Paul Duffin35816122021-02-24 01:49:52 +0000320func FixtureAddTextFile(path string, contents string) FixturePreparer {
321 return FixtureAddFile(path, []byte(contents))
322}
323
Paul Duffin6e9a4002021-03-11 19:01:26 +0000324// Override a file in the mock filesystem
325//
326// If the file does not exist this behaves as FixtureAddFile.
327func FixtureOverrideFile(path string, contents []byte) FixturePreparer {
328 return FixtureModifyMockFS(func(fs MockFS) {
329 fs[path] = contents
330 })
331}
332
333// Override a text file in the mock filesystem
334//
335// If the file does not exist this behaves as FixtureAddTextFile.
336func FixtureOverrideTextFile(path string, contents string) FixturePreparer {
337 return FixtureOverrideFile(path, []byte(contents))
338}
339
Paul Duffin35816122021-02-24 01:49:52 +0000340// Add the root Android.bp file with the supplied contents.
341func FixtureWithRootAndroidBp(contents string) FixturePreparer {
342 return FixtureAddTextFile("Android.bp", contents)
343}
344
Paul Duffinbbccfcf2021-03-03 00:44:00 +0000345// Merge some environment variables into the fixture.
346func FixtureMergeEnv(env map[string]string) FixturePreparer {
347 return FixtureModifyConfig(func(config Config) {
348 for k, v := range env {
349 if k == "PATH" {
350 panic("Cannot set PATH environment variable")
351 }
352 config.env[k] = v
353 }
354 })
355}
356
357// Modify the env.
358//
359// Will panic if the mutator changes the PATH environment variable.
360func FixtureModifyEnv(mutator func(env map[string]string)) FixturePreparer {
361 return FixtureModifyConfig(func(config Config) {
362 oldPath := config.env["PATH"]
363 mutator(config.env)
364 newPath := config.env["PATH"]
365 if newPath != oldPath {
366 panic(fmt.Errorf("Cannot change PATH environment variable from %q to %q", oldPath, newPath))
367 }
368 })
369}
370
Paul Duffin2e0323d2021-03-04 15:11:01 +0000371// Allow access to the product variables when preparing the fixture.
372type FixtureProductVariables struct {
373 *productVariables
374}
375
376// Modify product variables.
377func FixtureModifyProductVariables(mutator func(variables FixtureProductVariables)) FixturePreparer {
378 return FixtureModifyConfig(func(config Config) {
379 productVariables := FixtureProductVariables{&config.productVariables}
380 mutator(productVariables)
381 })
382}
383
Paul Duffina560d5a2021-02-28 01:38:51 +0000384// GroupFixturePreparers creates a composite FixturePreparer that is equivalent to applying each of
385// the supplied FixturePreparer instances in order.
386//
387// Before preparing the fixture the list of preparers is flattened by replacing each
388// instance of GroupFixturePreparers with its contents.
389func GroupFixturePreparers(preparers ...FixturePreparer) FixturePreparer {
Paul Duffin35816122021-02-24 01:49:52 +0000390 return &compositeFixturePreparer{dedupAndFlattenPreparers(nil, preparers)}
391}
392
Paul Duffin50deaae2021-03-16 17:46:12 +0000393// NullFixturePreparer is a preparer that does nothing.
394var NullFixturePreparer = GroupFixturePreparers()
395
396// OptionalFixturePreparer will return the supplied preparer if it is non-nil, otherwise it will
397// return the NullFixturePreparer
398func OptionalFixturePreparer(preparer FixturePreparer) FixturePreparer {
399 if preparer == nil {
400 return NullFixturePreparer
401 } else {
402 return preparer
403 }
404}
405
Paul Duffin35816122021-02-24 01:49:52 +0000406type simpleFixturePreparerVisitor func(preparer *simpleFixturePreparer)
407
408// FixturePreparer is an opaque interface that can change a fixture.
409type FixturePreparer interface {
410 // visit calls the supplied visitor with each *simpleFixturePreparer instances in this preparer,
411 visit(simpleFixturePreparerVisitor)
412}
413
414type fixturePreparers []FixturePreparer
415
416func (f fixturePreparers) visit(visitor simpleFixturePreparerVisitor) {
417 for _, p := range f {
418 p.visit(visitor)
419 }
420}
421
422// dedupAndFlattenPreparers removes any duplicates and flattens any composite FixturePreparer
423// instances.
424//
425// base - a list of already flattened and deduped preparers that will be applied first before
426// the list of additional preparers. Any duplicates of these in the additional preparers
427// will be ignored.
428//
429// preparers - a list of additional unflattened, undeduped preparers that will be applied after the
430// base preparers.
431//
432// Returns a deduped and flattened list of the preparers minus any that exist in the base preparers.
433func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers fixturePreparers) []*simpleFixturePreparer {
434 var list []*simpleFixturePreparer
435 visited := make(map[*simpleFixturePreparer]struct{})
436
437 // Mark the already flattened and deduped preparers, if any, as having been seen so that
438 // duplicates of these in the additional preparers will be discarded.
439 for _, s := range base {
440 visited[s] = struct{}{}
441 }
442
443 preparers.visit(func(preparer *simpleFixturePreparer) {
444 if _, seen := visited[preparer]; !seen {
445 visited[preparer] = struct{}{}
446 list = append(list, preparer)
447 }
448 })
449 return list
450}
451
452// compositeFixturePreparer is a FixturePreparer created from a list of fixture preparers.
453type compositeFixturePreparer struct {
454 preparers []*simpleFixturePreparer
455}
456
457func (c *compositeFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
458 for _, p := range c.preparers {
459 p.visit(visitor)
460 }
461}
462
463// simpleFixturePreparer is a FixturePreparer that applies a function to a fixture.
464type simpleFixturePreparer struct {
465 function func(fixture *fixture)
466}
467
468func (s *simpleFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
469 visitor(s)
470}
471
472func newSimpleFixturePreparer(preparer func(fixture *fixture)) FixturePreparer {
473 return &simpleFixturePreparer{function: preparer}
474}
475
Paul Duffincfd33742021-02-27 11:59:02 +0000476// FixtureErrorHandler determines how to respond to errors reported by the code under test.
477//
478// Some possible responses:
479// * Fail the test if any errors are reported, see FixtureExpectsNoErrors.
480// * Fail the test if at least one error that matches a pattern is not reported see
481// FixtureExpectsAtLeastOneErrorMatchingPattern
482// * Fail the test if any unexpected errors are reported.
483//
484// Although at the moment all the error handlers are implemented as simply a wrapper around a
485// function this is defined as an interface to allow future enhancements, e.g. provide different
486// ways other than patterns to match an error and to combine handlers together.
487type FixtureErrorHandler interface {
488 // CheckErrors checks the errors reported.
489 //
490 // The supplied result can be used to access the state of the code under test just as the main
491 // body of the test would but if any errors other than ones expected are reported the state may
492 // be indeterminate.
Paul Duffinc81854a2021-03-12 12:22:27 +0000493 CheckErrors(t *testing.T, result *TestResult)
Paul Duffincfd33742021-02-27 11:59:02 +0000494}
495
496type simpleErrorHandler struct {
Paul Duffinc81854a2021-03-12 12:22:27 +0000497 function func(t *testing.T, result *TestResult)
Paul Duffincfd33742021-02-27 11:59:02 +0000498}
499
Paul Duffinc81854a2021-03-12 12:22:27 +0000500func (h simpleErrorHandler) CheckErrors(t *testing.T, result *TestResult) {
501 t.Helper()
502 h.function(t, result)
Paul Duffincfd33742021-02-27 11:59:02 +0000503}
504
505// The default fixture error handler.
506//
507// Will fail the test immediately if any errors are reported.
Paul Duffinea8a3862021-03-04 17:58:33 +0000508//
509// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
510// which the test is being run which means that the RunTest() method will not return.
Paul Duffincfd33742021-02-27 11:59:02 +0000511var FixtureExpectsNoErrors = FixtureCustomErrorHandler(
Paul Duffinc81854a2021-03-12 12:22:27 +0000512 func(t *testing.T, result *TestResult) {
513 t.Helper()
514 FailIfErrored(t, result.Errs)
Paul Duffincfd33742021-02-27 11:59:02 +0000515 },
516)
517
Paul Duffin85034e92021-03-17 00:20:34 +0000518// FixtureIgnoreErrors ignores any errors.
519//
520// If this is used then it is the responsibility of the test to check the TestResult.Errs does not
521// contain any unexpected errors.
522var FixtureIgnoreErrors = FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) {
523 // Ignore the errors
524})
525
Paul Duffincfd33742021-02-27 11:59:02 +0000526// FixtureExpectsAtLeastOneMatchingError returns an error handler that will cause the test to fail
527// if at least one error that matches the regular expression is not found.
528//
529// The test will be failed if:
530// * No errors are reported.
531// * One or more errors are reported but none match the pattern.
532//
533// The test will not fail if:
534// * Multiple errors are reported that do not match the pattern as long as one does match.
Paul Duffinea8a3862021-03-04 17:58:33 +0000535//
536// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
537// which the test is being run which means that the RunTest() method will not return.
Paul Duffincfd33742021-02-27 11:59:02 +0000538func FixtureExpectsAtLeastOneErrorMatchingPattern(pattern string) FixtureErrorHandler {
Paul Duffinc81854a2021-03-12 12:22:27 +0000539 return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) {
540 t.Helper()
541 if !FailIfNoMatchingErrors(t, pattern, result.Errs) {
542 t.FailNow()
Paul Duffinea8a3862021-03-04 17:58:33 +0000543 }
Paul Duffincfd33742021-02-27 11:59:02 +0000544 })
545}
546
547// FixtureExpectsOneErrorToMatchPerPattern returns an error handler that will cause the test to fail
548// if there are any unexpected errors.
549//
550// The test will be failed if:
551// * The number of errors reported does not exactly match the patterns.
552// * One or more of the reported errors do not match a pattern.
553// * No patterns are provided and one or more errors are reported.
554//
555// The test will not fail if:
556// * One or more of the patterns does not match an error.
Paul Duffinea8a3862021-03-04 17:58:33 +0000557//
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.
Paul Duffincfd33742021-02-27 11:59:02 +0000560func FixtureExpectsAllErrorsToMatchAPattern(patterns []string) FixtureErrorHandler {
Paul Duffinc81854a2021-03-12 12:22:27 +0000561 return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) {
562 t.Helper()
563 CheckErrorsAgainstExpectations(t, result.Errs, patterns)
Paul Duffincfd33742021-02-27 11:59:02 +0000564 })
565}
566
567// FixtureCustomErrorHandler creates a custom error handler
Paul Duffinc81854a2021-03-12 12:22:27 +0000568func FixtureCustomErrorHandler(function func(t *testing.T, result *TestResult)) FixtureErrorHandler {
Paul Duffincfd33742021-02-27 11:59:02 +0000569 return simpleErrorHandler{
570 function: function,
571 }
572}
573
Paul Duffin35816122021-02-24 01:49:52 +0000574// Fixture defines the test environment.
575type Fixture interface {
Paul Duffinae542a52021-03-09 03:15:28 +0000576 // Config returns the fixture's configuration.
577 Config() Config
578
579 // Context returns the fixture's test context.
580 Context() *TestContext
581
582 // MockFS returns the fixture's mock filesystem.
583 MockFS() MockFS
584
Paul Duffincfd33742021-02-27 11:59:02 +0000585 // Run the test, checking any errors reported and returning a TestResult instance.
Paul Duffin35816122021-02-24 01:49:52 +0000586 RunTest() *TestResult
587}
588
Paul Duffin35816122021-02-24 01:49:52 +0000589// Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods.
590type testContext struct {
591 *TestContext
592}
593
594// The result of running a test.
595type TestResult struct {
Paul Duffin35816122021-02-24 01:49:52 +0000596 testContext
597
598 fixture *fixture
599 Config Config
Paul Duffin942481b2021-03-04 18:58:11 +0000600
601 // The errors that were reported during the test.
602 Errs []error
Paul Duffin78c36212021-03-16 23:57:12 +0000603
604 // The ninja deps is a list of the ninja files dependencies that were added by the modules and
605 // singletons via the *.AddNinjaFileDeps() methods.
606 NinjaDeps []string
Paul Duffin35816122021-02-24 01:49:52 +0000607}
608
609var _ FixtureFactory = (*fixtureFactory)(nil)
610
611type fixtureFactory struct {
612 buildDirSupplier *string
613 preparers []*simpleFixturePreparer
Paul Duffincfd33742021-02-27 11:59:02 +0000614 errorHandler FixtureErrorHandler
Paul Duffin35816122021-02-24 01:49:52 +0000615}
616
617func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixtureFactory {
Paul Duffinfa298852021-03-08 15:05:24 +0000618 // Create a new slice to avoid accidentally sharing the preparers slice from this factory with
619 // the extending factories.
620 var all []*simpleFixturePreparer
621 all = append(all, f.preparers...)
622 all = append(all, dedupAndFlattenPreparers(f.preparers, preparers)...)
Paul Duffincfd33742021-02-27 11:59:02 +0000623 // Copy the existing factory.
624 extendedFactory := &fixtureFactory{}
625 *extendedFactory = *f
626 // Use the extended list of preparers.
627 extendedFactory.preparers = all
628 return extendedFactory
Paul Duffin35816122021-02-24 01:49:52 +0000629}
630
631func (f *fixtureFactory) Fixture(t *testing.T, preparers ...FixturePreparer) Fixture {
Paul Duffindff5ff02021-03-15 15:42:40 +0000632 var buildDir string
633 if f.buildDirSupplier == nil {
634 // Create a new temporary directory for this run. It will be automatically cleaned up when the
635 // test finishes.
636 buildDir = t.TempDir()
637 } else {
638 // Retrieve the buildDir from the supplier.
639 buildDir = *f.buildDirSupplier
640 }
641 config := TestConfig(buildDir, nil, "", nil)
Paul Duffin35816122021-02-24 01:49:52 +0000642 ctx := NewTestContext(config)
643 fixture := &fixture{
Paul Duffincfd33742021-02-27 11:59:02 +0000644 factory: f,
645 t: t,
646 config: config,
647 ctx: ctx,
648 mockFS: make(MockFS),
649 errorHandler: f.errorHandler,
Paul Duffin35816122021-02-24 01:49:52 +0000650 }
651
652 for _, preparer := range f.preparers {
653 preparer.function(fixture)
654 }
655
656 for _, preparer := range dedupAndFlattenPreparers(f.preparers, preparers) {
657 preparer.function(fixture)
658 }
659
660 return fixture
661}
662
Paul Duffin46e37742021-03-09 11:55:20 +0000663func (f *fixtureFactory) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory {
Paul Duffin52323b52021-03-04 19:15:47 +0000664 newFactory := &fixtureFactory{}
665 *newFactory = *f
666 newFactory.errorHandler = errorHandler
667 return newFactory
Paul Duffincfd33742021-02-27 11:59:02 +0000668}
669
Paul Duffin35816122021-02-24 01:49:52 +0000670func (f *fixtureFactory) RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult {
671 t.Helper()
672 fixture := f.Fixture(t, preparers...)
673 return fixture.RunTest()
674}
675
676func (f *fixtureFactory) RunTestWithBp(t *testing.T, bp string) *TestResult {
677 t.Helper()
678 return f.RunTest(t, FixtureWithRootAndroidBp(bp))
679}
680
Paul Duffin72018ad2021-03-04 19:36:49 +0000681func (f *fixtureFactory) RunTestWithConfig(t *testing.T, config Config) *TestResult {
682 t.Helper()
683 // Create the fixture as normal.
684 fixture := f.Fixture(t).(*fixture)
685
686 // Discard the mock filesystem as otherwise that will override the one in the config.
687 fixture.mockFS = nil
688
689 // Replace the config with the supplied one in the fixture.
690 fixture.config = config
691
692 // Ditto with config derived information in the TestContext.
693 ctx := fixture.ctx
694 ctx.config = config
695 ctx.SetFs(ctx.config.fs)
696 if ctx.config.mockBpList != "" {
697 ctx.SetModuleListFile(ctx.config.mockBpList)
698 }
699
700 return fixture.RunTest()
701}
702
Paul Duffin35816122021-02-24 01:49:52 +0000703type fixture struct {
Paul Duffincfd33742021-02-27 11:59:02 +0000704 // The factory used to create this fixture.
Paul Duffin35816122021-02-24 01:49:52 +0000705 factory *fixtureFactory
Paul Duffincfd33742021-02-27 11:59:02 +0000706
707 // The gotest state of the go test within which this was created.
708 t *testing.T
709
710 // The configuration prepared for this fixture.
711 config Config
712
713 // The test context prepared for this fixture.
714 ctx *TestContext
715
716 // The mock filesystem prepared for this fixture.
717 mockFS MockFS
718
719 // The error handler used to check the errors, if any, that are reported.
720 errorHandler FixtureErrorHandler
Paul Duffin35816122021-02-24 01:49:52 +0000721}
722
Paul Duffinae542a52021-03-09 03:15:28 +0000723func (f *fixture) Config() Config {
724 return f.config
725}
726
727func (f *fixture) Context() *TestContext {
728 return f.ctx
729}
730
731func (f *fixture) MockFS() MockFS {
732 return f.mockFS
733}
734
Paul Duffin35816122021-02-24 01:49:52 +0000735func (f *fixture) RunTest() *TestResult {
736 f.t.Helper()
737
738 ctx := f.ctx
739
Paul Duffin72018ad2021-03-04 19:36:49 +0000740 // Do not use the fixture's mockFS to initialize the config's mock file system if it has been
741 // cleared by RunTestWithConfig.
742 if f.mockFS != nil {
743 // The TestConfig() method assumes that the mock filesystem is available when creating so
744 // creates the mock file system immediately. Similarly, the NewTestContext(Config) method
745 // assumes that the supplied Config's FileSystem has been properly initialized before it is
746 // called and so it takes its own reference to the filesystem. However, fixtures create the
747 // Config and TestContext early so they can be modified by preparers at which time the mockFS
748 // has not been populated (because it too is modified by preparers). So, this reinitializes the
749 // Config and TestContext's FileSystem using the now populated mockFS.
750 f.config.mockFileSystem("", f.mockFS)
751
752 ctx.SetFs(ctx.config.fs)
753 if ctx.config.mockBpList != "" {
754 ctx.SetModuleListFile(ctx.config.mockBpList)
755 }
Paul Duffin35816122021-02-24 01:49:52 +0000756 }
757
758 ctx.Register()
Paul Duffin78c36212021-03-16 23:57:12 +0000759 var ninjaDeps []string
760 extraNinjaDeps, errs := ctx.ParseBlueprintsFiles("ignored")
Paul Duffincfd33742021-02-27 11:59:02 +0000761 if len(errs) == 0 {
Paul Duffin78c36212021-03-16 23:57:12 +0000762 ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
763 extraNinjaDeps, errs = ctx.PrepareBuildActions(f.config)
764 if len(errs) == 0 {
765 ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
766 }
Paul Duffincfd33742021-02-27 11:59:02 +0000767 }
Paul Duffin35816122021-02-24 01:49:52 +0000768
769 result := &TestResult{
Paul Duffin35816122021-02-24 01:49:52 +0000770 testContext: testContext{ctx},
771 fixture: f,
772 Config: f.config,
Paul Duffin942481b2021-03-04 18:58:11 +0000773 Errs: errs,
Paul Duffin78c36212021-03-16 23:57:12 +0000774 NinjaDeps: ninjaDeps,
Paul Duffin35816122021-02-24 01:49:52 +0000775 }
Paul Duffincfd33742021-02-27 11:59:02 +0000776
Paul Duffinc81854a2021-03-12 12:22:27 +0000777 f.errorHandler.CheckErrors(f.t, result)
Paul Duffincfd33742021-02-27 11:59:02 +0000778
Paul Duffin35816122021-02-24 01:49:52 +0000779 return result
780}
781
782// NormalizePathForTesting removes the test invocation specific build directory from the supplied
783// path.
784//
785// If the path is within the build directory (e.g. an OutputPath) then this returns the relative
786// path to avoid tests having to deal with the dynamically generated build directory.
787//
788// Otherwise, this returns the supplied path as it is almost certainly a source path that is
789// relative to the root of the source tree.
790//
791// Even though some information is removed from some paths and not others it should be possible to
792// differentiate between them by the paths themselves, e.g. output paths will likely include
793// ".intermediates" but source paths won't.
794func (r *TestResult) NormalizePathForTesting(path Path) string {
795 pathContext := PathContextForTesting(r.Config)
796 pathAsString := path.String()
797 if rel, isRel := MaybeRel(pathContext, r.Config.BuildDir(), pathAsString); isRel {
798 return rel
799 }
800 return pathAsString
801}
802
803// NormalizePathsForTesting normalizes each path in the supplied list and returns their normalized
804// forms.
805func (r *TestResult) NormalizePathsForTesting(paths Paths) []string {
806 var result []string
807 for _, path := range paths {
808 result = append(result, r.NormalizePathForTesting(path))
809 }
810 return result
811}
812
Paul Duffin35816122021-02-24 01:49:52 +0000813// Module returns the module with the specific name and of the specified variant.
814func (r *TestResult) Module(name string, variant string) Module {
815 return r.ModuleForTests(name, variant).Module()
816}