blob: 0efe329cbc2a9e7f96b39420a04d4895ea661cb3 [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 (
18 "reflect"
19 "strings"
20 "testing"
21)
22
23// Provides support for creating test fixtures on which tests can be run. Reduces duplication
24// of test setup by allow tests to easily reuse setup code.
25//
26// Fixture
27// =======
28// These determine the environment within which a test can be run. Fixtures are mutable and are
29// created by FixtureFactory instances and mutated by FixturePreparer instances. They are created by
30// first creating a base Fixture (which is essentially empty) and then applying FixturePreparer
31// instances to it to modify the environment.
32//
33// FixtureFactory
34// ==============
35// These are responsible for creating fixtures. Factories are immutable and are intended to be
36// initialized once and reused to create multiple fixtures. Each factory has a list of fixture
37// preparers that prepare a fixture for running a test. Factories can also be used to create other
38// factories by extending them with additional fixture preparers.
39//
40// FixturePreparer
41// ===============
42// These are responsible for modifying a Fixture in preparation for it to run a test. Preparers are
43// intended to be immutable and able to prepare multiple Fixture objects simultaneously without
44// them sharing any data.
45//
46// FixturePreparers are only ever invoked once per test fixture. Prior to invocation the list of
47// FixturePreparers are flattened and deduped while preserving the order they first appear in the
48// list. This makes it easy to reuse, group and combine FixturePreparers together.
49//
50// Each small self contained piece of test setup should be their own FixturePreparer. e.g.
51// * A group of related modules.
52// * A group of related mutators.
53// * A combination of both.
54// * Configuration.
55//
56// They should not overlap, e.g. the same module type should not be registered by different
57// FixturePreparers as using them both would cause a build error. In that case the preparer should
58// be split into separate parts and combined together using FixturePreparers(...).
59//
60// e.g. attempting to use AllPreparers in preparing a Fixture would break as it would attempt to
61// register module bar twice:
62// var Preparer1 = FixtureRegisterWithContext(RegisterModuleFooAndBar)
63// var Preparer2 = FixtureRegisterWithContext(RegisterModuleBarAndBaz)
64// var AllPreparers = FixturePreparers(Preparer1, Preparer2)
65//
66// However, when restructured like this it would work fine:
67// var PreparerFoo = FixtureRegisterWithContext(RegisterModuleFoo)
68// var PreparerBar = FixtureRegisterWithContext(RegisterModuleBar)
69// var PreparerBaz = FixtureRegisterWithContext(RegisterModuleBaz)
70// var Preparer1 = FixturePreparers(RegisterModuleFoo, RegisterModuleBar)
71// var Preparer2 = FixturePreparers(RegisterModuleBar, RegisterModuleBaz)
72// var AllPreparers = FixturePreparers(Preparer1, Preparer2)
73//
74// As after deduping and flattening AllPreparers would result in the following preparers being
75// applied:
76// 1. PreparerFoo
77// 2. PreparerBar
78// 3. PreparerBaz
79//
80// Preparers can be used for both integration and unit tests.
81//
82// Integration tests typically use all the module types, mutators and singletons that are available
83// for that package to try and replicate the behavior of the runtime build as closely as possible.
84// However, that realism comes at a cost of increased fragility (as they can be broken by changes in
85// many different parts of the build) and also increased runtime, especially if they use lots of
86// singletons and mutators.
87//
88// Unit tests on the other hand try and minimize the amount of code being tested which makes them
89// less susceptible to changes elsewhere in the build and quick to run but at a cost of potentially
90// not testing realistic scenarios.
91//
92// Supporting unit tests effectively require that preparers are available at the lowest granularity
93// possible. Supporting integration tests effectively require that the preparers are organized into
94// groups that provide all the functionality available.
95//
96// At least in terms of tests that check the behavior of build components via processing
97// `Android.bp` there is no clear separation between a unit test and an integration test. Instead
98// they vary from one end that tests a single module (e.g. filegroup) to the other end that tests a
99// whole system of modules, mutators and singletons (e.g. apex + hiddenapi).
100//
101// TestResult
102// ==========
103// These are created by running tests in a Fixture and provide access to the Config and TestContext
104// in which the tests were run.
105//
106// Example
107// =======
108//
109// An exported preparer for use by other packages that need to use java modules.
110//
111// package java
112// var PrepareForIntegrationTestWithJava = FixturePreparers(
113// android.PrepareForIntegrationTestWithAndroid,
114// FixtureRegisterWithContext(RegisterAGroupOfRelatedModulesMutatorsAndSingletons),
115// FixtureRegisterWithContext(RegisterAnotherGroupOfRelatedModulesMutatorsAndSingletons),
116// ...
117// )
118//
119// Some files to use in tests in the java package.
120//
121// var javaMockFS = android.MockFS{
122// "api/current.txt": nil,
123// "api/removed.txt": nil,
124// ...
125// }
126//
127// A package private factory for use for testing java within the java package.
128//
129// var javaFixtureFactory = NewFixtureFactory(
130// PrepareForIntegrationTestWithJava,
131// FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
132// ctx.RegisterModuleType("test_module", testModule)
133// }),
134// javaMockFS.AddToFixture(),
135// ...
136// }
137//
138// func TestJavaStuff(t *testing.T) {
139// result := javaFixtureFactory.RunTest(t,
140// android.FixtureWithRootAndroidBp(`java_library {....}`),
141// android.MockFS{...}.AddToFixture(),
142// )
143// ... test result ...
144// }
145//
146// package cc
147// var PrepareForTestWithCC = FixturePreparers(
148// android.PrepareForArchMutator,
149// android.prepareForPrebuilts,
150// FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
151// ...
152// )
153//
154// package apex
155//
156// var PrepareForApex = FixturePreparers(
157// ...
158// )
159//
160// Use modules and mutators from java, cc and apex. Any duplicate preparers (like
161// android.PrepareForArchMutator) will be automatically deduped.
162//
163// var apexFixtureFactory = android.NewFixtureFactory(
164// PrepareForJava,
165// PrepareForCC,
166// PrepareForApex,
167// )
168
169// Factory for Fixture objects.
170//
171// This is configured with a set of FixturePreparer objects that are used to
172// initialize each Fixture instance this creates.
173type FixtureFactory interface {
174
175 // Creates a copy of this instance and adds some additional preparers.
176 //
177 // Before the preparers are used they are combined with the preparers provided when the factory
178 // was created, any groups of preparers are flattened, and the list is deduped so that each
179 // preparer is only used once. See the file documentation in android/fixture.go for more details.
180 Extend(preparers ...FixturePreparer) FixtureFactory
181
182 // Create a Fixture.
183 Fixture(t *testing.T, preparers ...FixturePreparer) Fixture
184
185 // Run the test, expecting no errors, returning a TestResult instance.
186 //
187 // Shorthand for Fixture(t, preparers...).RunTest()
188 RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult
189
190 // Run the test with the supplied Android.bp file.
191 //
192 // Shorthand for RunTest(t, android.FixtureWithRootAndroidBp(bp))
193 RunTestWithBp(t *testing.T, bp string) *TestResult
194}
195
196// Create a new FixtureFactory that will apply the supplied preparers.
197//
198// The buildDirSupplier is a pointer to the package level buildDir variable that is initialized by
199// the package level setUp method. It has to be a pointer to the variable as the variable will not
200// have been initialized at the time the factory is created.
201func NewFixtureFactory(buildDirSupplier *string, preparers ...FixturePreparer) FixtureFactory {
202 return &fixtureFactory{
203 buildDirSupplier: buildDirSupplier,
204 preparers: dedupAndFlattenPreparers(nil, preparers),
205 }
206}
207
208// A set of mock files to add to the mock file system.
209type MockFS map[string][]byte
210
211func (fs MockFS) Merge(extra map[string][]byte) {
212 for p, c := range extra {
213 fs[p] = c
214 }
215}
216
217func (fs MockFS) AddToFixture() FixturePreparer {
218 return FixtureMergeMockFs(fs)
219}
220
221// Modify the config
222func FixtureModifyConfig(mutator func(config Config)) FixturePreparer {
223 return newSimpleFixturePreparer(func(f *fixture) {
224 mutator(f.config)
225 })
226}
227
228// Modify the config and context
229func FixtureModifyConfigAndContext(mutator func(config Config, ctx *TestContext)) FixturePreparer {
230 return newSimpleFixturePreparer(func(f *fixture) {
231 mutator(f.config, f.ctx)
232 })
233}
234
235// Modify the context
236func FixtureModifyContext(mutator func(ctx *TestContext)) FixturePreparer {
237 return newSimpleFixturePreparer(func(f *fixture) {
238 mutator(f.ctx)
239 })
240}
241
242func FixtureRegisterWithContext(registeringFunc func(ctx RegistrationContext)) FixturePreparer {
243 return FixtureModifyContext(func(ctx *TestContext) { registeringFunc(ctx) })
244}
245
246// Modify the mock filesystem
247func FixtureModifyMockFS(mutator func(fs MockFS)) FixturePreparer {
248 return newSimpleFixturePreparer(func(f *fixture) {
249 mutator(f.mockFS)
250 })
251}
252
253// Merge the supplied file system into the mock filesystem.
254//
255// Paths that already exist in the mock file system are overridden.
256func FixtureMergeMockFs(mockFS MockFS) FixturePreparer {
257 return FixtureModifyMockFS(func(fs MockFS) {
258 fs.Merge(mockFS)
259 })
260}
261
262// Add a file to the mock filesystem
263func FixtureAddFile(path string, contents []byte) FixturePreparer {
264 return FixtureModifyMockFS(func(fs MockFS) {
265 fs[path] = contents
266 })
267}
268
269// Add a text file to the mock filesystem
270func FixtureAddTextFile(path string, contents string) FixturePreparer {
271 return FixtureAddFile(path, []byte(contents))
272}
273
274// Add the root Android.bp file with the supplied contents.
275func FixtureWithRootAndroidBp(contents string) FixturePreparer {
276 return FixtureAddTextFile("Android.bp", contents)
277}
278
279// Create a composite FixturePreparer that is equivalent to applying each of the supplied
280// FixturePreparer instances in order.
281func FixturePreparers(preparers ...FixturePreparer) FixturePreparer {
282 return &compositeFixturePreparer{dedupAndFlattenPreparers(nil, preparers)}
283}
284
285type simpleFixturePreparerVisitor func(preparer *simpleFixturePreparer)
286
287// FixturePreparer is an opaque interface that can change a fixture.
288type FixturePreparer interface {
289 // visit calls the supplied visitor with each *simpleFixturePreparer instances in this preparer,
290 visit(simpleFixturePreparerVisitor)
291}
292
293type fixturePreparers []FixturePreparer
294
295func (f fixturePreparers) visit(visitor simpleFixturePreparerVisitor) {
296 for _, p := range f {
297 p.visit(visitor)
298 }
299}
300
301// dedupAndFlattenPreparers removes any duplicates and flattens any composite FixturePreparer
302// instances.
303//
304// base - a list of already flattened and deduped preparers that will be applied first before
305// the list of additional preparers. Any duplicates of these in the additional preparers
306// will be ignored.
307//
308// preparers - a list of additional unflattened, undeduped preparers that will be applied after the
309// base preparers.
310//
311// Returns a deduped and flattened list of the preparers minus any that exist in the base preparers.
312func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers fixturePreparers) []*simpleFixturePreparer {
313 var list []*simpleFixturePreparer
314 visited := make(map[*simpleFixturePreparer]struct{})
315
316 // Mark the already flattened and deduped preparers, if any, as having been seen so that
317 // duplicates of these in the additional preparers will be discarded.
318 for _, s := range base {
319 visited[s] = struct{}{}
320 }
321
322 preparers.visit(func(preparer *simpleFixturePreparer) {
323 if _, seen := visited[preparer]; !seen {
324 visited[preparer] = struct{}{}
325 list = append(list, preparer)
326 }
327 })
328 return list
329}
330
331// compositeFixturePreparer is a FixturePreparer created from a list of fixture preparers.
332type compositeFixturePreparer struct {
333 preparers []*simpleFixturePreparer
334}
335
336func (c *compositeFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
337 for _, p := range c.preparers {
338 p.visit(visitor)
339 }
340}
341
342// simpleFixturePreparer is a FixturePreparer that applies a function to a fixture.
343type simpleFixturePreparer struct {
344 function func(fixture *fixture)
345}
346
347func (s *simpleFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
348 visitor(s)
349}
350
351func newSimpleFixturePreparer(preparer func(fixture *fixture)) FixturePreparer {
352 return &simpleFixturePreparer{function: preparer}
353}
354
355// Fixture defines the test environment.
356type Fixture interface {
357 // Run the test, expecting no errors, returning a TestResult instance.
358 RunTest() *TestResult
359}
360
361// Provides general test support.
362type TestHelper struct {
363 *testing.T
364}
365
366// AssertBoolEquals checks if the expected and actual values are equal and if they are not then it
367// reports an error prefixed with the supplied message and including a reason for why it failed.
368func (h *TestHelper) AssertBoolEquals(message string, expected bool, actual bool) {
369 h.Helper()
370 if actual != expected {
371 h.Errorf("%s: expected %t, actual %t", message, expected, actual)
372 }
373}
374
375// AssertStringEquals checks if the expected and actual values are equal and if they are not then
376// it reports an error prefixed with the supplied message and including a reason for why it failed.
377func (h *TestHelper) AssertStringEquals(message string, expected string, actual string) {
378 h.Helper()
379 if actual != expected {
380 h.Errorf("%s: expected %s, actual %s", message, expected, actual)
381 }
382}
383
384// AssertTrimmedStringEquals checks if the expected and actual values are the same after trimming
385// leading and trailing spaces from them both. If they are not then it reports an error prefixed
386// with the supplied message and including a reason for why it failed.
387func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) {
388 h.Helper()
389 h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual))
390}
391
392// AssertStringDoesContain checks if the string contains the expected substring. If it does not
393// then it reports an error prefixed with the supplied message and including a reason for why it
394// failed.
395func (h *TestHelper) AssertStringDoesContain(message string, s string, expectedSubstring string) {
396 h.Helper()
397 if !strings.Contains(s, expectedSubstring) {
398 h.Errorf("%s: could not find %q within %q", message, expectedSubstring, s)
399 }
400}
401
402// AssertStringDoesNotContain checks if the string contains the expected substring. If it does then
403// it reports an error prefixed with the supplied message and including a reason for why it failed.
404func (h *TestHelper) AssertStringDoesNotContain(message string, s string, unexpectedSubstring string) {
405 h.Helper()
406 if strings.Contains(s, unexpectedSubstring) {
407 h.Errorf("%s: unexpectedly found %q within %q", message, unexpectedSubstring, s)
408 }
409}
410
411// AssertArrayString checks if the expected and actual values are equal and if they are not then it
412// reports an error prefixed with the supplied message and including a reason for why it failed.
413func (h *TestHelper) AssertArrayString(message string, expected, actual []string) {
414 h.Helper()
415 if len(actual) != len(expected) {
416 h.Errorf("%s: expected %d (%q), actual (%d) %q", message, len(expected), expected, len(actual), actual)
417 return
418 }
419 for i := range actual {
420 if actual[i] != expected[i] {
421 h.Errorf("%s: expected %d-th, %q (%q), actual %q (%q)",
422 message, i, expected[i], expected, actual[i], actual)
423 return
424 }
425 }
426}
427
428// AssertArrayString checks if the expected and actual values are equal using reflect.DeepEqual and
429// if they are not then it reports an error prefixed with the supplied message and including a
430// reason for why it failed.
431func (h *TestHelper) AssertDeepEquals(message string, expected interface{}, actual interface{}) {
432 h.Helper()
433 if !reflect.DeepEqual(actual, expected) {
434 h.Errorf("%s: expected:\n %#v\n got:\n %#v", message, expected, actual)
435 }
436}
437
438// Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods.
439type testContext struct {
440 *TestContext
441}
442
443// The result of running a test.
444type TestResult struct {
445 TestHelper
446 testContext
447
448 fixture *fixture
449 Config Config
450}
451
452var _ FixtureFactory = (*fixtureFactory)(nil)
453
454type fixtureFactory struct {
455 buildDirSupplier *string
456 preparers []*simpleFixturePreparer
457}
458
459func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixtureFactory {
460 all := append(f.preparers, dedupAndFlattenPreparers(f.preparers, preparers)...)
461 return &fixtureFactory{
462 buildDirSupplier: f.buildDirSupplier,
463 preparers: all,
464 }
465}
466
467func (f *fixtureFactory) Fixture(t *testing.T, preparers ...FixturePreparer) Fixture {
468 config := TestConfig(*f.buildDirSupplier, nil, "", nil)
469 ctx := NewTestContext(config)
470 fixture := &fixture{
471 factory: f,
472 t: t,
473 config: config,
474 ctx: ctx,
475 mockFS: make(MockFS),
476 }
477
478 for _, preparer := range f.preparers {
479 preparer.function(fixture)
480 }
481
482 for _, preparer := range dedupAndFlattenPreparers(f.preparers, preparers) {
483 preparer.function(fixture)
484 }
485
486 return fixture
487}
488
489func (f *fixtureFactory) RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult {
490 t.Helper()
491 fixture := f.Fixture(t, preparers...)
492 return fixture.RunTest()
493}
494
495func (f *fixtureFactory) RunTestWithBp(t *testing.T, bp string) *TestResult {
496 t.Helper()
497 return f.RunTest(t, FixtureWithRootAndroidBp(bp))
498}
499
500type fixture struct {
501 factory *fixtureFactory
502 t *testing.T
503 config Config
504 ctx *TestContext
505 mockFS MockFS
506}
507
508func (f *fixture) RunTest() *TestResult {
509 f.t.Helper()
510
511 ctx := f.ctx
512
513 // The TestConfig() method assumes that the mock filesystem is available when creating so creates
514 // the mock file system immediately. Similarly, the NewTestContext(Config) method assumes that the
515 // supplied Config's FileSystem has been properly initialized before it is called and so it takes
516 // its own reference to the filesystem. However, fixtures create the Config and TestContext early
517 // so they can be modified by preparers at which time the mockFS has not been populated (because
518 // it too is modified by preparers). So, this reinitializes the Config and TestContext's
519 // FileSystem using the now populated mockFS.
520 f.config.mockFileSystem("", f.mockFS)
521 ctx.SetFs(ctx.config.fs)
522 if ctx.config.mockBpList != "" {
523 ctx.SetModuleListFile(ctx.config.mockBpList)
524 }
525
526 ctx.Register()
527 _, errs := ctx.ParseBlueprintsFiles("ignored")
528 FailIfErrored(f.t, errs)
529 _, errs = ctx.PrepareBuildActions(f.config)
530 FailIfErrored(f.t, errs)
531
532 result := &TestResult{
533 TestHelper: TestHelper{T: f.t},
534 testContext: testContext{ctx},
535 fixture: f,
536 Config: f.config,
537 }
538 return result
539}
540
541// NormalizePathForTesting removes the test invocation specific build directory from the supplied
542// path.
543//
544// If the path is within the build directory (e.g. an OutputPath) then this returns the relative
545// path to avoid tests having to deal with the dynamically generated build directory.
546//
547// Otherwise, this returns the supplied path as it is almost certainly a source path that is
548// relative to the root of the source tree.
549//
550// Even though some information is removed from some paths and not others it should be possible to
551// differentiate between them by the paths themselves, e.g. output paths will likely include
552// ".intermediates" but source paths won't.
553func (r *TestResult) NormalizePathForTesting(path Path) string {
554 pathContext := PathContextForTesting(r.Config)
555 pathAsString := path.String()
556 if rel, isRel := MaybeRel(pathContext, r.Config.BuildDir(), pathAsString); isRel {
557 return rel
558 }
559 return pathAsString
560}
561
562// NormalizePathsForTesting normalizes each path in the supplied list and returns their normalized
563// forms.
564func (r *TestResult) NormalizePathsForTesting(paths Paths) []string {
565 var result []string
566 for _, path := range paths {
567 result = append(result, r.NormalizePathForTesting(path))
568 }
569 return result
570}
571
572// NewFixture creates a new test fixture that is based on the one that created this result. It is
573// intended to test the output of module types that generate content to be processed by the build,
574// e.g. sdk snapshots.
575func (r *TestResult) NewFixture(preparers ...FixturePreparer) Fixture {
576 return r.fixture.factory.Fixture(r.T, preparers...)
577}
578
579// RunTest is shorthand for NewFixture(preparers...).RunTest().
580func (r *TestResult) RunTest(preparers ...FixturePreparer) *TestResult {
581 r.Helper()
582 return r.fixture.factory.Fixture(r.T, preparers...).RunTest()
583}
584
585// Module returns the module with the specific name and of the specified variant.
586func (r *TestResult) Module(name string, variant string) Module {
587 return r.ModuleForTests(name, variant).Module()
588}
589
590// Create a *TestResult object suitable for use within a subtest.
591//
592// This ensures that any errors reported by the TestResult, e.g. from within one of its
593// Assert... methods, will be associated with the sub test and not the main test.
594//
595// result := ....RunTest()
596// t.Run("subtest", func(t *testing.T) {
597// subResult := result.ResultForSubTest(t)
598// subResult.AssertStringEquals("something", ....)
599// })
600func (r *TestResult) ResultForSubTest(t *testing.T) *TestResult {
601 subTestResult := *r
602 r.T = t
603 return &subTestResult
604}