blob: edbbf08edb57f8bf086299fa6d144fa0fd1e1a1c [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 "reflect"
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 by FixtureFactory instances and mutated by FixturePreparer instances. They are created by
31// first creating a base Fixture (which is essentially empty) and then applying FixturePreparer
32// instances to it to modify the environment.
33//
34// FixtureFactory
35// ==============
36// These are responsible for creating fixtures. Factories are immutable and are intended to be
37// initialized once and reused to create multiple fixtures. Each factory has a list of fixture
38// preparers that prepare a fixture for running a test. Factories can also be used to create other
39// factories by extending them with additional fixture preparers.
40//
41// FixturePreparer
42// ===============
43// These are responsible for modifying a Fixture in preparation for it to run a test. Preparers are
44// intended to be immutable and able to prepare multiple Fixture objects simultaneously without
45// them sharing any data.
46//
47// FixturePreparers are only ever invoked once per test fixture. Prior to invocation the list of
48// FixturePreparers are flattened and deduped while preserving the order they first appear in the
49// list. This makes it easy to reuse, group and combine FixturePreparers together.
50//
51// Each small self contained piece of test setup should be their own FixturePreparer. e.g.
52// * A group of related modules.
53// * A group of related mutators.
54// * A combination of both.
55// * Configuration.
56//
57// They should not overlap, e.g. the same module type should not be registered by different
58// FixturePreparers as using them both would cause a build error. In that case the preparer should
59// be split into separate parts and combined together using FixturePreparers(...).
60//
61// e.g. attempting to use AllPreparers in preparing a Fixture would break as it would attempt to
62// register module bar twice:
63// var Preparer1 = FixtureRegisterWithContext(RegisterModuleFooAndBar)
64// var Preparer2 = FixtureRegisterWithContext(RegisterModuleBarAndBaz)
Paul Duffina560d5a2021-02-28 01:38:51 +000065// var AllPreparers = GroupFixturePreparers(Preparer1, Preparer2)
Paul Duffin35816122021-02-24 01:49:52 +000066//
67// However, when restructured like this it would work fine:
68// var PreparerFoo = FixtureRegisterWithContext(RegisterModuleFoo)
69// var PreparerBar = FixtureRegisterWithContext(RegisterModuleBar)
70// var PreparerBaz = FixtureRegisterWithContext(RegisterModuleBaz)
Paul Duffina560d5a2021-02-28 01:38:51 +000071// var Preparer1 = GroupFixturePreparers(RegisterModuleFoo, RegisterModuleBar)
72// var Preparer2 = GroupFixturePreparers(RegisterModuleBar, RegisterModuleBaz)
73// var AllPreparers = GroupFixturePreparers(Preparer1, Preparer2)
Paul Duffin35816122021-02-24 01:49:52 +000074//
75// As after deduping and flattening AllPreparers would result in the following preparers being
76// applied:
77// 1. PreparerFoo
78// 2. PreparerBar
79// 3. PreparerBaz
80//
81// Preparers can be used for both integration and unit tests.
82//
83// Integration tests typically use all the module types, mutators and singletons that are available
84// for that package to try and replicate the behavior of the runtime build as closely as possible.
85// However, that realism comes at a cost of increased fragility (as they can be broken by changes in
86// many different parts of the build) and also increased runtime, especially if they use lots of
87// singletons and mutators.
88//
89// Unit tests on the other hand try and minimize the amount of code being tested which makes them
90// less susceptible to changes elsewhere in the build and quick to run but at a cost of potentially
91// not testing realistic scenarios.
92//
93// Supporting unit tests effectively require that preparers are available at the lowest granularity
94// possible. Supporting integration tests effectively require that the preparers are organized into
95// groups that provide all the functionality available.
96//
97// At least in terms of tests that check the behavior of build components via processing
98// `Android.bp` there is no clear separation between a unit test and an integration test. Instead
99// they vary from one end that tests a single module (e.g. filegroup) to the other end that tests a
100// whole system of modules, mutators and singletons (e.g. apex + hiddenapi).
101//
102// TestResult
103// ==========
104// These are created by running tests in a Fixture and provide access to the Config and TestContext
105// in which the tests were run.
106//
107// Example
108// =======
109//
110// An exported preparer for use by other packages that need to use java modules.
111//
112// package java
Paul Duffina560d5a2021-02-28 01:38:51 +0000113// var PrepareForIntegrationTestWithJava = GroupFixturePreparers(
Paul Duffin35816122021-02-24 01:49:52 +0000114// android.PrepareForIntegrationTestWithAndroid,
115// FixtureRegisterWithContext(RegisterAGroupOfRelatedModulesMutatorsAndSingletons),
116// FixtureRegisterWithContext(RegisterAnotherGroupOfRelatedModulesMutatorsAndSingletons),
117// ...
118// )
119//
120// Some files to use in tests in the java package.
121//
122// var javaMockFS = android.MockFS{
123// "api/current.txt": nil,
124// "api/removed.txt": nil,
125// ...
126// }
127//
128// A package private factory for use for testing java within the java package.
129//
130// var javaFixtureFactory = NewFixtureFactory(
131// PrepareForIntegrationTestWithJava,
132// FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
133// ctx.RegisterModuleType("test_module", testModule)
134// }),
135// javaMockFS.AddToFixture(),
136// ...
137// }
138//
139// func TestJavaStuff(t *testing.T) {
140// result := javaFixtureFactory.RunTest(t,
141// android.FixtureWithRootAndroidBp(`java_library {....}`),
142// android.MockFS{...}.AddToFixture(),
143// )
144// ... test result ...
145// }
146//
147// package cc
Paul Duffina560d5a2021-02-28 01:38:51 +0000148// var PrepareForTestWithCC = GroupFixturePreparers(
Paul Duffin35816122021-02-24 01:49:52 +0000149// android.PrepareForArchMutator,
150// android.prepareForPrebuilts,
151// FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
152// ...
153// )
154//
155// package apex
156//
Paul Duffina560d5a2021-02-28 01:38:51 +0000157// var PrepareForApex = GroupFixturePreparers(
Paul Duffin35816122021-02-24 01:49:52 +0000158// ...
159// )
160//
161// Use modules and mutators from java, cc and apex. Any duplicate preparers (like
162// android.PrepareForArchMutator) will be automatically deduped.
163//
164// var apexFixtureFactory = android.NewFixtureFactory(
165// PrepareForJava,
166// PrepareForCC,
167// PrepareForApex,
168// )
169
170// Factory for Fixture objects.
171//
172// This is configured with a set of FixturePreparer objects that are used to
173// initialize each Fixture instance this creates.
174type FixtureFactory interface {
175
176 // Creates a copy of this instance and adds some additional preparers.
177 //
178 // Before the preparers are used they are combined with the preparers provided when the factory
179 // was created, any groups of preparers are flattened, and the list is deduped so that each
180 // preparer is only used once. See the file documentation in android/fixture.go for more details.
181 Extend(preparers ...FixturePreparer) FixtureFactory
182
183 // Create a Fixture.
184 Fixture(t *testing.T, preparers ...FixturePreparer) Fixture
185
Paul Duffin46e37742021-03-09 11:55:20 +0000186 // ExtendWithErrorHandler creates a new FixtureFactory that will use the supplied error handler
187 // to check the errors (may be 0) reported by the test.
Paul Duffincfd33742021-02-27 11:59:02 +0000188 //
189 // The default handlers is FixtureExpectsNoErrors which will fail the go test immediately if any
190 // errors are reported.
Paul Duffin46e37742021-03-09 11:55:20 +0000191 ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory
Paul Duffincfd33742021-02-27 11:59:02 +0000192
193 // Run the test, checking any errors reported and returning a TestResult instance.
Paul Duffin35816122021-02-24 01:49:52 +0000194 //
195 // Shorthand for Fixture(t, preparers...).RunTest()
196 RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult
197
198 // Run the test with the supplied Android.bp file.
199 //
200 // Shorthand for RunTest(t, android.FixtureWithRootAndroidBp(bp))
201 RunTestWithBp(t *testing.T, bp string) *TestResult
Paul Duffin72018ad2021-03-04 19:36:49 +0000202
203 // RunTestWithConfig is a temporary method added to help ease the migration of existing tests to
204 // the test fixture.
205 //
206 // In order to allow the Config object to be customized separately to the TestContext a lot of
207 // existing test code has `test...WithConfig` funcs that allow the Config object to be supplied
208 // from the test and then have the TestContext created and configured automatically. e.g.
209 // testCcWithConfig, testCcErrorWithConfig, testJavaWithConfig, etc.
210 //
211 // This method allows those methods to be migrated to use the test fixture pattern without
212 // requiring that every test that uses those methods be migrated at the same time. That allows
213 // those tests to benefit from correctness in the order of registration quickly.
214 //
215 // This method discards the config (along with its mock file system, product variables,
216 // environment, etc.) that may have been set up by FixturePreparers.
217 //
218 // deprecated
219 RunTestWithConfig(t *testing.T, config Config) *TestResult
Paul Duffin35816122021-02-24 01:49:52 +0000220}
221
222// Create a new FixtureFactory that will apply the supplied preparers.
223//
224// The buildDirSupplier is a pointer to the package level buildDir variable that is initialized by
225// the package level setUp method. It has to be a pointer to the variable as the variable will not
226// have been initialized at the time the factory is created.
227func NewFixtureFactory(buildDirSupplier *string, preparers ...FixturePreparer) FixtureFactory {
228 return &fixtureFactory{
229 buildDirSupplier: buildDirSupplier,
230 preparers: dedupAndFlattenPreparers(nil, preparers),
Paul Duffincfd33742021-02-27 11:59:02 +0000231
232 // Set the default error handler.
233 errorHandler: FixtureExpectsNoErrors,
Paul Duffin35816122021-02-24 01:49:52 +0000234 }
235}
236
237// A set of mock files to add to the mock file system.
238type MockFS map[string][]byte
239
Paul Duffin6e9a4002021-03-11 19:01:26 +0000240// Merge adds the extra entries from the supplied map to this one.
241//
242// Fails if the supplied map files with the same paths are present in both of them.
Paul Duffin35816122021-02-24 01:49:52 +0000243func (fs MockFS) Merge(extra map[string][]byte) {
244 for p, c := range extra {
Paul Duffin6e9a4002021-03-11 19:01:26 +0000245 if _, ok := fs[p]; ok {
246 panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists", p))
247 }
Paul Duffin35816122021-02-24 01:49:52 +0000248 fs[p] = c
249 }
250}
251
252func (fs MockFS) AddToFixture() FixturePreparer {
253 return FixtureMergeMockFs(fs)
254}
255
256// Modify the config
257func FixtureModifyConfig(mutator func(config Config)) FixturePreparer {
258 return newSimpleFixturePreparer(func(f *fixture) {
259 mutator(f.config)
260 })
261}
262
263// Modify the config and context
264func FixtureModifyConfigAndContext(mutator func(config Config, ctx *TestContext)) FixturePreparer {
265 return newSimpleFixturePreparer(func(f *fixture) {
266 mutator(f.config, f.ctx)
267 })
268}
269
270// Modify the context
271func FixtureModifyContext(mutator func(ctx *TestContext)) FixturePreparer {
272 return newSimpleFixturePreparer(func(f *fixture) {
273 mutator(f.ctx)
274 })
275}
276
277func FixtureRegisterWithContext(registeringFunc func(ctx RegistrationContext)) FixturePreparer {
278 return FixtureModifyContext(func(ctx *TestContext) { registeringFunc(ctx) })
279}
280
281// Modify the mock filesystem
282func FixtureModifyMockFS(mutator func(fs MockFS)) FixturePreparer {
283 return newSimpleFixturePreparer(func(f *fixture) {
284 mutator(f.mockFS)
285 })
286}
287
288// Merge the supplied file system into the mock filesystem.
289//
290// Paths that already exist in the mock file system are overridden.
291func FixtureMergeMockFs(mockFS MockFS) FixturePreparer {
292 return FixtureModifyMockFS(func(fs MockFS) {
293 fs.Merge(mockFS)
294 })
295}
296
297// Add a file to the mock filesystem
Paul Duffin6e9a4002021-03-11 19:01:26 +0000298//
299// Fail if the filesystem already contains a file with that path, use FixtureOverrideFile instead.
Paul Duffin35816122021-02-24 01:49:52 +0000300func FixtureAddFile(path string, contents []byte) FixturePreparer {
301 return FixtureModifyMockFS(func(fs MockFS) {
Paul Duffin6e9a4002021-03-11 19:01:26 +0000302 if _, ok := fs[path]; ok {
303 panic(fmt.Errorf("attempted to add file %s to the mock filesystem but it already exists, use FixtureOverride*File instead", path))
304 }
Paul Duffin35816122021-02-24 01:49:52 +0000305 fs[path] = contents
306 })
307}
308
309// Add a text file to the mock filesystem
Paul Duffin6e9a4002021-03-11 19:01:26 +0000310//
311// Fail if the filesystem already contains a file with that path.
Paul Duffin35816122021-02-24 01:49:52 +0000312func FixtureAddTextFile(path string, contents string) FixturePreparer {
313 return FixtureAddFile(path, []byte(contents))
314}
315
Paul Duffin6e9a4002021-03-11 19:01:26 +0000316// Override a file in the mock filesystem
317//
318// If the file does not exist this behaves as FixtureAddFile.
319func FixtureOverrideFile(path string, contents []byte) FixturePreparer {
320 return FixtureModifyMockFS(func(fs MockFS) {
321 fs[path] = contents
322 })
323}
324
325// Override a text file in the mock filesystem
326//
327// If the file does not exist this behaves as FixtureAddTextFile.
328func FixtureOverrideTextFile(path string, contents string) FixturePreparer {
329 return FixtureOverrideFile(path, []byte(contents))
330}
331
Paul Duffin35816122021-02-24 01:49:52 +0000332// Add the root Android.bp file with the supplied contents.
333func FixtureWithRootAndroidBp(contents string) FixturePreparer {
334 return FixtureAddTextFile("Android.bp", contents)
335}
336
Paul Duffinbbccfcf2021-03-03 00:44:00 +0000337// Merge some environment variables into the fixture.
338func FixtureMergeEnv(env map[string]string) FixturePreparer {
339 return FixtureModifyConfig(func(config Config) {
340 for k, v := range env {
341 if k == "PATH" {
342 panic("Cannot set PATH environment variable")
343 }
344 config.env[k] = v
345 }
346 })
347}
348
349// Modify the env.
350//
351// Will panic if the mutator changes the PATH environment variable.
352func FixtureModifyEnv(mutator func(env map[string]string)) FixturePreparer {
353 return FixtureModifyConfig(func(config Config) {
354 oldPath := config.env["PATH"]
355 mutator(config.env)
356 newPath := config.env["PATH"]
357 if newPath != oldPath {
358 panic(fmt.Errorf("Cannot change PATH environment variable from %q to %q", oldPath, newPath))
359 }
360 })
361}
362
Paul Duffin2e0323d2021-03-04 15:11:01 +0000363// Allow access to the product variables when preparing the fixture.
364type FixtureProductVariables struct {
365 *productVariables
366}
367
368// Modify product variables.
369func FixtureModifyProductVariables(mutator func(variables FixtureProductVariables)) FixturePreparer {
370 return FixtureModifyConfig(func(config Config) {
371 productVariables := FixtureProductVariables{&config.productVariables}
372 mutator(productVariables)
373 })
374}
375
Paul Duffina560d5a2021-02-28 01:38:51 +0000376// GroupFixturePreparers creates a composite FixturePreparer that is equivalent to applying each of
377// the supplied FixturePreparer instances in order.
378//
379// Before preparing the fixture the list of preparers is flattened by replacing each
380// instance of GroupFixturePreparers with its contents.
381func GroupFixturePreparers(preparers ...FixturePreparer) FixturePreparer {
Paul Duffin35816122021-02-24 01:49:52 +0000382 return &compositeFixturePreparer{dedupAndFlattenPreparers(nil, preparers)}
383}
384
385type simpleFixturePreparerVisitor func(preparer *simpleFixturePreparer)
386
387// FixturePreparer is an opaque interface that can change a fixture.
388type FixturePreparer interface {
389 // visit calls the supplied visitor with each *simpleFixturePreparer instances in this preparer,
390 visit(simpleFixturePreparerVisitor)
391}
392
393type fixturePreparers []FixturePreparer
394
395func (f fixturePreparers) visit(visitor simpleFixturePreparerVisitor) {
396 for _, p := range f {
397 p.visit(visitor)
398 }
399}
400
401// dedupAndFlattenPreparers removes any duplicates and flattens any composite FixturePreparer
402// instances.
403//
404// base - a list of already flattened and deduped preparers that will be applied first before
405// the list of additional preparers. Any duplicates of these in the additional preparers
406// will be ignored.
407//
408// preparers - a list of additional unflattened, undeduped preparers that will be applied after the
409// base preparers.
410//
411// Returns a deduped and flattened list of the preparers minus any that exist in the base preparers.
412func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers fixturePreparers) []*simpleFixturePreparer {
413 var list []*simpleFixturePreparer
414 visited := make(map[*simpleFixturePreparer]struct{})
415
416 // Mark the already flattened and deduped preparers, if any, as having been seen so that
417 // duplicates of these in the additional preparers will be discarded.
418 for _, s := range base {
419 visited[s] = struct{}{}
420 }
421
422 preparers.visit(func(preparer *simpleFixturePreparer) {
423 if _, seen := visited[preparer]; !seen {
424 visited[preparer] = struct{}{}
425 list = append(list, preparer)
426 }
427 })
428 return list
429}
430
431// compositeFixturePreparer is a FixturePreparer created from a list of fixture preparers.
432type compositeFixturePreparer struct {
433 preparers []*simpleFixturePreparer
434}
435
436func (c *compositeFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
437 for _, p := range c.preparers {
438 p.visit(visitor)
439 }
440}
441
442// simpleFixturePreparer is a FixturePreparer that applies a function to a fixture.
443type simpleFixturePreparer struct {
444 function func(fixture *fixture)
445}
446
447func (s *simpleFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
448 visitor(s)
449}
450
451func newSimpleFixturePreparer(preparer func(fixture *fixture)) FixturePreparer {
452 return &simpleFixturePreparer{function: preparer}
453}
454
Paul Duffincfd33742021-02-27 11:59:02 +0000455// FixtureErrorHandler determines how to respond to errors reported by the code under test.
456//
457// Some possible responses:
458// * Fail the test if any errors are reported, see FixtureExpectsNoErrors.
459// * Fail the test if at least one error that matches a pattern is not reported see
460// FixtureExpectsAtLeastOneErrorMatchingPattern
461// * Fail the test if any unexpected errors are reported.
462//
463// Although at the moment all the error handlers are implemented as simply a wrapper around a
464// function this is defined as an interface to allow future enhancements, e.g. provide different
465// ways other than patterns to match an error and to combine handlers together.
466type FixtureErrorHandler interface {
467 // CheckErrors checks the errors reported.
468 //
469 // The supplied result can be used to access the state of the code under test just as the main
470 // body of the test would but if any errors other than ones expected are reported the state may
471 // be indeterminate.
Paul Duffin942481b2021-03-04 18:58:11 +0000472 CheckErrors(result *TestResult)
Paul Duffincfd33742021-02-27 11:59:02 +0000473}
474
475type simpleErrorHandler struct {
Paul Duffin942481b2021-03-04 18:58:11 +0000476 function func(result *TestResult)
Paul Duffincfd33742021-02-27 11:59:02 +0000477}
478
Paul Duffin942481b2021-03-04 18:58:11 +0000479func (h simpleErrorHandler) CheckErrors(result *TestResult) {
480 result.Helper()
481 h.function(result)
Paul Duffincfd33742021-02-27 11:59:02 +0000482}
483
484// The default fixture error handler.
485//
486// Will fail the test immediately if any errors are reported.
Paul Duffinea8a3862021-03-04 17:58:33 +0000487//
488// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
489// which the test is being run which means that the RunTest() method will not return.
Paul Duffincfd33742021-02-27 11:59:02 +0000490var FixtureExpectsNoErrors = FixtureCustomErrorHandler(
Paul Duffin942481b2021-03-04 18:58:11 +0000491 func(result *TestResult) {
492 result.Helper()
493 FailIfErrored(result.T, result.Errs)
Paul Duffincfd33742021-02-27 11:59:02 +0000494 },
495)
496
497// FixtureExpectsAtLeastOneMatchingError returns an error handler that will cause the test to fail
498// if at least one error that matches the regular expression is not found.
499//
500// The test will be failed if:
501// * No errors are reported.
502// * One or more errors are reported but none match the pattern.
503//
504// The test will not fail if:
505// * Multiple errors are reported that do not match the pattern as long as one does match.
Paul Duffinea8a3862021-03-04 17:58:33 +0000506//
507// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
508// which the test is being run which means that the RunTest() method will not return.
Paul Duffincfd33742021-02-27 11:59:02 +0000509func FixtureExpectsAtLeastOneErrorMatchingPattern(pattern string) FixtureErrorHandler {
Paul Duffin942481b2021-03-04 18:58:11 +0000510 return FixtureCustomErrorHandler(func(result *TestResult) {
511 result.Helper()
512 if !FailIfNoMatchingErrors(result.T, pattern, result.Errs) {
Paul Duffinea8a3862021-03-04 17:58:33 +0000513 result.FailNow()
514 }
Paul Duffincfd33742021-02-27 11:59:02 +0000515 })
516}
517
518// FixtureExpectsOneErrorToMatchPerPattern returns an error handler that will cause the test to fail
519// if there are any unexpected errors.
520//
521// The test will be failed if:
522// * The number of errors reported does not exactly match the patterns.
523// * One or more of the reported errors do not match a pattern.
524// * No patterns are provided and one or more errors are reported.
525//
526// The test will not fail if:
527// * One or more of the patterns does not match an error.
Paul Duffinea8a3862021-03-04 17:58:33 +0000528//
529// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within
530// which the test is being run which means that the RunTest() method will not return.
Paul Duffincfd33742021-02-27 11:59:02 +0000531func FixtureExpectsAllErrorsToMatchAPattern(patterns []string) FixtureErrorHandler {
Paul Duffin942481b2021-03-04 18:58:11 +0000532 return FixtureCustomErrorHandler(func(result *TestResult) {
533 result.Helper()
534 CheckErrorsAgainstExpectations(result.T, result.Errs, patterns)
Paul Duffincfd33742021-02-27 11:59:02 +0000535 })
536}
537
538// FixtureCustomErrorHandler creates a custom error handler
Paul Duffin942481b2021-03-04 18:58:11 +0000539func FixtureCustomErrorHandler(function func(result *TestResult)) FixtureErrorHandler {
Paul Duffincfd33742021-02-27 11:59:02 +0000540 return simpleErrorHandler{
541 function: function,
542 }
543}
544
Paul Duffin35816122021-02-24 01:49:52 +0000545// Fixture defines the test environment.
546type Fixture interface {
Paul Duffincfd33742021-02-27 11:59:02 +0000547 // Run the test, checking any errors reported and returning a TestResult instance.
Paul Duffin35816122021-02-24 01:49:52 +0000548 RunTest() *TestResult
549}
550
551// Provides general test support.
552type TestHelper struct {
553 *testing.T
554}
555
556// AssertBoolEquals checks if the expected and actual values are equal and if they are not then it
557// reports an error prefixed with the supplied message and including a reason for why it failed.
558func (h *TestHelper) AssertBoolEquals(message string, expected bool, actual bool) {
559 h.Helper()
560 if actual != expected {
561 h.Errorf("%s: expected %t, actual %t", message, expected, actual)
562 }
563}
564
565// AssertStringEquals checks if the expected and actual values are equal and if they are not then
566// it reports an error prefixed with the supplied message and including a reason for why it failed.
567func (h *TestHelper) AssertStringEquals(message string, expected string, actual string) {
568 h.Helper()
569 if actual != expected {
570 h.Errorf("%s: expected %s, actual %s", message, expected, actual)
571 }
572}
573
Paul Duffina3cb2b32021-03-10 09:15:22 +0000574// AssertErrorMessageEquals checks if the error is not nil and has the expected message. If it does
575// not then this reports an error prefixed with the supplied message and including a reason for why
576// it failed.
577func (h *TestHelper) AssertErrorMessageEquals(message string, expected string, actual error) {
578 h.Helper()
579 if actual == nil {
580 h.Errorf("Expected error but was nil")
581 } else if actual.Error() != expected {
582 h.Errorf("%s: expected %s, actual %s", message, expected, actual.Error())
583 }
584}
585
Paul Duffin35816122021-02-24 01:49:52 +0000586// AssertTrimmedStringEquals checks if the expected and actual values are the same after trimming
587// leading and trailing spaces from them both. If they are not then it reports an error prefixed
588// with the supplied message and including a reason for why it failed.
589func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) {
590 h.Helper()
591 h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual))
592}
593
594// AssertStringDoesContain checks if the string contains the expected substring. If it does not
595// then it reports an error prefixed with the supplied message and including a reason for why it
596// failed.
597func (h *TestHelper) AssertStringDoesContain(message string, s string, expectedSubstring string) {
598 h.Helper()
599 if !strings.Contains(s, expectedSubstring) {
600 h.Errorf("%s: could not find %q within %q", message, expectedSubstring, s)
601 }
602}
603
604// AssertStringDoesNotContain checks if the string contains the expected substring. If it does then
605// it reports an error prefixed with the supplied message and including a reason for why it failed.
606func (h *TestHelper) AssertStringDoesNotContain(message string, s string, unexpectedSubstring string) {
607 h.Helper()
608 if strings.Contains(s, unexpectedSubstring) {
609 h.Errorf("%s: unexpectedly found %q within %q", message, unexpectedSubstring, s)
610 }
611}
612
Paul Duffin93706ae2021-03-07 15:48:27 +0000613// AssertStringListContains checks if the list of strings contains the expected string. If it does
614// not then it reports an error prefixed with the supplied message and including a reason for why it
615// failed.
616func (h *TestHelper) AssertStringListContains(message string, list []string, expected string) {
617 h.Helper()
618 if !InList(expected, list) {
619 h.Errorf("%s: could not find %q within %q", message, expected, list)
620 }
621}
622
Paul Duffin35816122021-02-24 01:49:52 +0000623// AssertArrayString checks if the expected and actual values are equal and if they are not then it
624// reports an error prefixed with the supplied message and including a reason for why it failed.
625func (h *TestHelper) AssertArrayString(message string, expected, actual []string) {
626 h.Helper()
627 if len(actual) != len(expected) {
628 h.Errorf("%s: expected %d (%q), actual (%d) %q", message, len(expected), expected, len(actual), actual)
629 return
630 }
631 for i := range actual {
632 if actual[i] != expected[i] {
633 h.Errorf("%s: expected %d-th, %q (%q), actual %q (%q)",
634 message, i, expected[i], expected, actual[i], actual)
635 return
636 }
637 }
638}
639
Paul Duffin1ef166e2021-03-11 14:08:57 +0000640// AssertDeepEquals checks if the expected and actual values are equal using reflect.DeepEqual and
Paul Duffin35816122021-02-24 01:49:52 +0000641// if they are not then it reports an error prefixed with the supplied message and including a
642// reason for why it failed.
643func (h *TestHelper) AssertDeepEquals(message string, expected interface{}, actual interface{}) {
644 h.Helper()
645 if !reflect.DeepEqual(actual, expected) {
646 h.Errorf("%s: expected:\n %#v\n got:\n %#v", message, expected, actual)
647 }
648}
649
Paul Duffina3cb2b32021-03-10 09:15:22 +0000650// AssertPanic checks that the supplied function panics as expected.
651func (h *TestHelper) AssertPanic(message string, funcThatShouldPanic func()) {
652 h.Helper()
653 panicked := false
654 func() {
655 defer func() {
656 if x := recover(); x != nil {
657 panicked = true
658 }
659 }()
660 funcThatShouldPanic()
661 }()
662 if !panicked {
663 h.Error(message)
664 }
665}
666
Paul Duffin35816122021-02-24 01:49:52 +0000667// Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods.
668type testContext struct {
669 *TestContext
670}
671
672// The result of running a test.
673type TestResult struct {
674 TestHelper
675 testContext
676
677 fixture *fixture
678 Config Config
Paul Duffin942481b2021-03-04 18:58:11 +0000679
680 // The errors that were reported during the test.
681 Errs []error
Paul Duffin35816122021-02-24 01:49:52 +0000682}
683
684var _ FixtureFactory = (*fixtureFactory)(nil)
685
686type fixtureFactory struct {
687 buildDirSupplier *string
688 preparers []*simpleFixturePreparer
Paul Duffincfd33742021-02-27 11:59:02 +0000689 errorHandler FixtureErrorHandler
Paul Duffin35816122021-02-24 01:49:52 +0000690}
691
692func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixtureFactory {
Paul Duffinfa298852021-03-08 15:05:24 +0000693 // Create a new slice to avoid accidentally sharing the preparers slice from this factory with
694 // the extending factories.
695 var all []*simpleFixturePreparer
696 all = append(all, f.preparers...)
697 all = append(all, dedupAndFlattenPreparers(f.preparers, preparers)...)
Paul Duffincfd33742021-02-27 11:59:02 +0000698 // Copy the existing factory.
699 extendedFactory := &fixtureFactory{}
700 *extendedFactory = *f
701 // Use the extended list of preparers.
702 extendedFactory.preparers = all
703 return extendedFactory
Paul Duffin35816122021-02-24 01:49:52 +0000704}
705
706func (f *fixtureFactory) Fixture(t *testing.T, preparers ...FixturePreparer) Fixture {
707 config := TestConfig(*f.buildDirSupplier, nil, "", nil)
708 ctx := NewTestContext(config)
709 fixture := &fixture{
Paul Duffincfd33742021-02-27 11:59:02 +0000710 factory: f,
711 t: t,
712 config: config,
713 ctx: ctx,
714 mockFS: make(MockFS),
715 errorHandler: f.errorHandler,
Paul Duffin35816122021-02-24 01:49:52 +0000716 }
717
718 for _, preparer := range f.preparers {
719 preparer.function(fixture)
720 }
721
722 for _, preparer := range dedupAndFlattenPreparers(f.preparers, preparers) {
723 preparer.function(fixture)
724 }
725
726 return fixture
727}
728
Paul Duffin46e37742021-03-09 11:55:20 +0000729func (f *fixtureFactory) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory {
Paul Duffin52323b52021-03-04 19:15:47 +0000730 newFactory := &fixtureFactory{}
731 *newFactory = *f
732 newFactory.errorHandler = errorHandler
733 return newFactory
Paul Duffincfd33742021-02-27 11:59:02 +0000734}
735
Paul Duffin35816122021-02-24 01:49:52 +0000736func (f *fixtureFactory) RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult {
737 t.Helper()
738 fixture := f.Fixture(t, preparers...)
739 return fixture.RunTest()
740}
741
742func (f *fixtureFactory) RunTestWithBp(t *testing.T, bp string) *TestResult {
743 t.Helper()
744 return f.RunTest(t, FixtureWithRootAndroidBp(bp))
745}
746
Paul Duffin72018ad2021-03-04 19:36:49 +0000747func (f *fixtureFactory) RunTestWithConfig(t *testing.T, config Config) *TestResult {
748 t.Helper()
749 // Create the fixture as normal.
750 fixture := f.Fixture(t).(*fixture)
751
752 // Discard the mock filesystem as otherwise that will override the one in the config.
753 fixture.mockFS = nil
754
755 // Replace the config with the supplied one in the fixture.
756 fixture.config = config
757
758 // Ditto with config derived information in the TestContext.
759 ctx := fixture.ctx
760 ctx.config = config
761 ctx.SetFs(ctx.config.fs)
762 if ctx.config.mockBpList != "" {
763 ctx.SetModuleListFile(ctx.config.mockBpList)
764 }
765
766 return fixture.RunTest()
767}
768
Paul Duffin35816122021-02-24 01:49:52 +0000769type fixture struct {
Paul Duffincfd33742021-02-27 11:59:02 +0000770 // The factory used to create this fixture.
Paul Duffin35816122021-02-24 01:49:52 +0000771 factory *fixtureFactory
Paul Duffincfd33742021-02-27 11:59:02 +0000772
773 // The gotest state of the go test within which this was created.
774 t *testing.T
775
776 // The configuration prepared for this fixture.
777 config Config
778
779 // The test context prepared for this fixture.
780 ctx *TestContext
781
782 // The mock filesystem prepared for this fixture.
783 mockFS MockFS
784
785 // The error handler used to check the errors, if any, that are reported.
786 errorHandler FixtureErrorHandler
Paul Duffin35816122021-02-24 01:49:52 +0000787}
788
789func (f *fixture) RunTest() *TestResult {
790 f.t.Helper()
791
792 ctx := f.ctx
793
Paul Duffin72018ad2021-03-04 19:36:49 +0000794 // Do not use the fixture's mockFS to initialize the config's mock file system if it has been
795 // cleared by RunTestWithConfig.
796 if f.mockFS != nil {
797 // The TestConfig() method assumes that the mock filesystem is available when creating so
798 // creates the mock file system immediately. Similarly, the NewTestContext(Config) method
799 // assumes that the supplied Config's FileSystem has been properly initialized before it is
800 // called and so it takes its own reference to the filesystem. However, fixtures create the
801 // Config and TestContext early so they can be modified by preparers at which time the mockFS
802 // has not been populated (because it too is modified by preparers). So, this reinitializes the
803 // Config and TestContext's FileSystem using the now populated mockFS.
804 f.config.mockFileSystem("", f.mockFS)
805
806 ctx.SetFs(ctx.config.fs)
807 if ctx.config.mockBpList != "" {
808 ctx.SetModuleListFile(ctx.config.mockBpList)
809 }
Paul Duffin35816122021-02-24 01:49:52 +0000810 }
811
812 ctx.Register()
813 _, errs := ctx.ParseBlueprintsFiles("ignored")
Paul Duffincfd33742021-02-27 11:59:02 +0000814 if len(errs) == 0 {
815 _, errs = ctx.PrepareBuildActions(f.config)
816 }
Paul Duffin35816122021-02-24 01:49:52 +0000817
818 result := &TestResult{
819 TestHelper: TestHelper{T: f.t},
820 testContext: testContext{ctx},
821 fixture: f,
822 Config: f.config,
Paul Duffin942481b2021-03-04 18:58:11 +0000823 Errs: errs,
Paul Duffin35816122021-02-24 01:49:52 +0000824 }
Paul Duffincfd33742021-02-27 11:59:02 +0000825
Paul Duffin942481b2021-03-04 18:58:11 +0000826 f.errorHandler.CheckErrors(result)
Paul Duffincfd33742021-02-27 11:59:02 +0000827
Paul Duffin35816122021-02-24 01:49:52 +0000828 return result
829}
830
831// NormalizePathForTesting removes the test invocation specific build directory from the supplied
832// path.
833//
834// If the path is within the build directory (e.g. an OutputPath) then this returns the relative
835// path to avoid tests having to deal with the dynamically generated build directory.
836//
837// Otherwise, this returns the supplied path as it is almost certainly a source path that is
838// relative to the root of the source tree.
839//
840// Even though some information is removed from some paths and not others it should be possible to
841// differentiate between them by the paths themselves, e.g. output paths will likely include
842// ".intermediates" but source paths won't.
843func (r *TestResult) NormalizePathForTesting(path Path) string {
844 pathContext := PathContextForTesting(r.Config)
845 pathAsString := path.String()
846 if rel, isRel := MaybeRel(pathContext, r.Config.BuildDir(), pathAsString); isRel {
847 return rel
848 }
849 return pathAsString
850}
851
852// NormalizePathsForTesting normalizes each path in the supplied list and returns their normalized
853// forms.
854func (r *TestResult) NormalizePathsForTesting(paths Paths) []string {
855 var result []string
856 for _, path := range paths {
857 result = append(result, r.NormalizePathForTesting(path))
858 }
859 return result
860}
861
862// NewFixture creates a new test fixture that is based on the one that created this result. It is
863// intended to test the output of module types that generate content to be processed by the build,
864// e.g. sdk snapshots.
865func (r *TestResult) NewFixture(preparers ...FixturePreparer) Fixture {
866 return r.fixture.factory.Fixture(r.T, preparers...)
867}
868
869// RunTest is shorthand for NewFixture(preparers...).RunTest().
870func (r *TestResult) RunTest(preparers ...FixturePreparer) *TestResult {
871 r.Helper()
872 return r.fixture.factory.Fixture(r.T, preparers...).RunTest()
873}
874
875// Module returns the module with the specific name and of the specified variant.
876func (r *TestResult) Module(name string, variant string) Module {
877 return r.ModuleForTests(name, variant).Module()
878}
879
880// Create a *TestResult object suitable for use within a subtest.
881//
882// This ensures that any errors reported by the TestResult, e.g. from within one of its
883// Assert... methods, will be associated with the sub test and not the main test.
884//
885// result := ....RunTest()
886// t.Run("subtest", func(t *testing.T) {
887// subResult := result.ResultForSubTest(t)
888// subResult.AssertStringEquals("something", ....)
889// })
890func (r *TestResult) ResultForSubTest(t *testing.T) *TestResult {
891 subTestResult := *r
892 r.T = t
893 return &subTestResult
894}