Add test fixture support

Adds the test fixture support and converts a few tests to exercise the
code and show how it works.

Bug: 181070625
Test: m nothing
Change-Id: I0a2b40fff93b6041f9aa8c4ef0aba91da1bc8bf3
This commit is contained in:
Paul Duffin 2021-02-24 01:49:52 +00:00
parent 30e3e9d21d
commit 358161232c
8 changed files with 730 additions and 24 deletions

View File

@ -34,6 +34,7 @@ bootstrap_go_package {
"deptag.go", "deptag.go",
"expand.go", "expand.go",
"filegroup.go", "filegroup.go",
"fixture.go",
"hooks.go", "hooks.go",
"image.go", "image.go",
"license.go", "license.go",
@ -87,6 +88,7 @@ bootstrap_go_package {
"depset_test.go", "depset_test.go",
"deptag_test.go", "deptag_test.go",
"expand_test.go", "expand_test.go",
"fixture_test.go",
"license_kind_test.go", "license_kind_test.go",
"license_test.go", "license_test.go",
"licenses_test.go", "licenses_test.go",

View File

@ -44,3 +44,5 @@ func TestMain(m *testing.M) {
os.Exit(run()) os.Exit(run())
} }
var emptyTestFixtureFactory = NewFixtureFactory(&buildDir)

View File

@ -305,10 +305,7 @@ func TestArchConfigFuchsia(buildDir string, env map[string]string, bp string, fs
return testConfig return testConfig
} }
// TestArchConfig returns a Config object suitable for using for tests that func modifyTestConfigToSupportArchMutator(testConfig Config) {
// need to run the arch mutator.
func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
testConfig := TestConfig(buildDir, env, bp, fs)
config := testConfig.config config := testConfig.config
config.Targets = map[OsType][]Target{ config.Targets = map[OsType][]Target{
@ -334,7 +331,13 @@ func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[st
config.TestProductVariables.DeviceArchVariant = proptools.StringPtr("armv8-a") config.TestProductVariables.DeviceArchVariant = proptools.StringPtr("armv8-a")
config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm") config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm")
config.TestProductVariables.DeviceSecondaryArchVariant = proptools.StringPtr("armv7-a-neon") config.TestProductVariables.DeviceSecondaryArchVariant = proptools.StringPtr("armv7-a-neon")
}
// TestArchConfig returns a Config object suitable for using for tests that
// need to run the arch mutator.
func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
testConfig := TestConfig(buildDir, env, bp, fs)
modifyTestConfigToSupportArchMutator(testConfig)
return testConfig return testConfig
} }

View File

@ -24,6 +24,10 @@ func init() {
RegisterBp2BuildMutator("filegroup", FilegroupBp2Build) RegisterBp2BuildMutator("filegroup", FilegroupBp2Build)
} }
var PrepareForTestWithFilegroup = FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("filegroup", FileGroupFactory)
})
// https://docs.bazel.build/versions/master/be/general.html#filegroup // https://docs.bazel.build/versions/master/be/general.html#filegroup
type bazelFilegroupAttributes struct { type bazelFilegroupAttributes struct {
Srcs bazel.LabelList Srcs bazel.LabelList

604
android/fixture.go Normal file
View File

@ -0,0 +1,604 @@
// Copyright 2021 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package android
import (
"reflect"
"strings"
"testing"
)
// Provides support for creating test fixtures on which tests can be run. Reduces duplication
// of test setup by allow tests to easily reuse setup code.
//
// Fixture
// =======
// These determine the environment within which a test can be run. Fixtures are mutable and are
// created by FixtureFactory instances and mutated by FixturePreparer instances. They are created by
// first creating a base Fixture (which is essentially empty) and then applying FixturePreparer
// instances to it to modify the environment.
//
// FixtureFactory
// ==============
// These are responsible for creating fixtures. Factories are immutable and are intended to be
// initialized once and reused to create multiple fixtures. Each factory has a list of fixture
// preparers that prepare a fixture for running a test. Factories can also be used to create other
// factories by extending them with additional fixture preparers.
//
// FixturePreparer
// ===============
// These are responsible for modifying a Fixture in preparation for it to run a test. Preparers are
// intended to be immutable and able to prepare multiple Fixture objects simultaneously without
// them sharing any data.
//
// FixturePreparers are only ever invoked once per test fixture. Prior to invocation the list of
// FixturePreparers are flattened and deduped while preserving the order they first appear in the
// list. This makes it easy to reuse, group and combine FixturePreparers together.
//
// Each small self contained piece of test setup should be their own FixturePreparer. e.g.
// * A group of related modules.
// * A group of related mutators.
// * A combination of both.
// * Configuration.
//
// They should not overlap, e.g. the same module type should not be registered by different
// FixturePreparers as using them both would cause a build error. In that case the preparer should
// be split into separate parts and combined together using FixturePreparers(...).
//
// e.g. attempting to use AllPreparers in preparing a Fixture would break as it would attempt to
// register module bar twice:
// var Preparer1 = FixtureRegisterWithContext(RegisterModuleFooAndBar)
// var Preparer2 = FixtureRegisterWithContext(RegisterModuleBarAndBaz)
// var AllPreparers = FixturePreparers(Preparer1, Preparer2)
//
// However, when restructured like this it would work fine:
// var PreparerFoo = FixtureRegisterWithContext(RegisterModuleFoo)
// var PreparerBar = FixtureRegisterWithContext(RegisterModuleBar)
// var PreparerBaz = FixtureRegisterWithContext(RegisterModuleBaz)
// var Preparer1 = FixturePreparers(RegisterModuleFoo, RegisterModuleBar)
// var Preparer2 = FixturePreparers(RegisterModuleBar, RegisterModuleBaz)
// var AllPreparers = FixturePreparers(Preparer1, Preparer2)
//
// As after deduping and flattening AllPreparers would result in the following preparers being
// applied:
// 1. PreparerFoo
// 2. PreparerBar
// 3. PreparerBaz
//
// Preparers can be used for both integration and unit tests.
//
// Integration tests typically use all the module types, mutators and singletons that are available
// for that package to try and replicate the behavior of the runtime build as closely as possible.
// However, that realism comes at a cost of increased fragility (as they can be broken by changes in
// many different parts of the build) and also increased runtime, especially if they use lots of
// singletons and mutators.
//
// Unit tests on the other hand try and minimize the amount of code being tested which makes them
// less susceptible to changes elsewhere in the build and quick to run but at a cost of potentially
// not testing realistic scenarios.
//
// Supporting unit tests effectively require that preparers are available at the lowest granularity
// possible. Supporting integration tests effectively require that the preparers are organized into
// groups that provide all the functionality available.
//
// At least in terms of tests that check the behavior of build components via processing
// `Android.bp` there is no clear separation between a unit test and an integration test. Instead
// they vary from one end that tests a single module (e.g. filegroup) to the other end that tests a
// whole system of modules, mutators and singletons (e.g. apex + hiddenapi).
//
// TestResult
// ==========
// These are created by running tests in a Fixture and provide access to the Config and TestContext
// in which the tests were run.
//
// Example
// =======
//
// An exported preparer for use by other packages that need to use java modules.
//
// package java
// var PrepareForIntegrationTestWithJava = FixturePreparers(
// android.PrepareForIntegrationTestWithAndroid,
// FixtureRegisterWithContext(RegisterAGroupOfRelatedModulesMutatorsAndSingletons),
// FixtureRegisterWithContext(RegisterAnotherGroupOfRelatedModulesMutatorsAndSingletons),
// ...
// )
//
// Some files to use in tests in the java package.
//
// var javaMockFS = android.MockFS{
// "api/current.txt": nil,
// "api/removed.txt": nil,
// ...
// }
//
// A package private factory for use for testing java within the java package.
//
// var javaFixtureFactory = NewFixtureFactory(
// PrepareForIntegrationTestWithJava,
// FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
// ctx.RegisterModuleType("test_module", testModule)
// }),
// javaMockFS.AddToFixture(),
// ...
// }
//
// func TestJavaStuff(t *testing.T) {
// result := javaFixtureFactory.RunTest(t,
// android.FixtureWithRootAndroidBp(`java_library {....}`),
// android.MockFS{...}.AddToFixture(),
// )
// ... test result ...
// }
//
// package cc
// var PrepareForTestWithCC = FixturePreparers(
// android.PrepareForArchMutator,
// android.prepareForPrebuilts,
// FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
// ...
// )
//
// package apex
//
// var PrepareForApex = FixturePreparers(
// ...
// )
//
// Use modules and mutators from java, cc and apex. Any duplicate preparers (like
// android.PrepareForArchMutator) will be automatically deduped.
//
// var apexFixtureFactory = android.NewFixtureFactory(
// PrepareForJava,
// PrepareForCC,
// PrepareForApex,
// )
// Factory for Fixture objects.
//
// This is configured with a set of FixturePreparer objects that are used to
// initialize each Fixture instance this creates.
type FixtureFactory interface {
// Creates a copy of this instance and adds some additional preparers.
//
// Before the preparers are used they are combined with the preparers provided when the factory
// was created, any groups of preparers are flattened, and the list is deduped so that each
// preparer is only used once. See the file documentation in android/fixture.go for more details.
Extend(preparers ...FixturePreparer) FixtureFactory
// Create a Fixture.
Fixture(t *testing.T, preparers ...FixturePreparer) Fixture
// Run the test, expecting no errors, returning a TestResult instance.
//
// Shorthand for Fixture(t, preparers...).RunTest()
RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult
// Run the test with the supplied Android.bp file.
//
// Shorthand for RunTest(t, android.FixtureWithRootAndroidBp(bp))
RunTestWithBp(t *testing.T, bp string) *TestResult
}
// Create a new FixtureFactory that will apply the supplied preparers.
//
// The buildDirSupplier is a pointer to the package level buildDir variable that is initialized by
// the package level setUp method. It has to be a pointer to the variable as the variable will not
// have been initialized at the time the factory is created.
func NewFixtureFactory(buildDirSupplier *string, preparers ...FixturePreparer) FixtureFactory {
return &fixtureFactory{
buildDirSupplier: buildDirSupplier,
preparers: dedupAndFlattenPreparers(nil, preparers),
}
}
// A set of mock files to add to the mock file system.
type MockFS map[string][]byte
func (fs MockFS) Merge(extra map[string][]byte) {
for p, c := range extra {
fs[p] = c
}
}
func (fs MockFS) AddToFixture() FixturePreparer {
return FixtureMergeMockFs(fs)
}
// Modify the config
func FixtureModifyConfig(mutator func(config Config)) FixturePreparer {
return newSimpleFixturePreparer(func(f *fixture) {
mutator(f.config)
})
}
// Modify the config and context
func FixtureModifyConfigAndContext(mutator func(config Config, ctx *TestContext)) FixturePreparer {
return newSimpleFixturePreparer(func(f *fixture) {
mutator(f.config, f.ctx)
})
}
// Modify the context
func FixtureModifyContext(mutator func(ctx *TestContext)) FixturePreparer {
return newSimpleFixturePreparer(func(f *fixture) {
mutator(f.ctx)
})
}
func FixtureRegisterWithContext(registeringFunc func(ctx RegistrationContext)) FixturePreparer {
return FixtureModifyContext(func(ctx *TestContext) { registeringFunc(ctx) })
}
// Modify the mock filesystem
func FixtureModifyMockFS(mutator func(fs MockFS)) FixturePreparer {
return newSimpleFixturePreparer(func(f *fixture) {
mutator(f.mockFS)
})
}
// Merge the supplied file system into the mock filesystem.
//
// Paths that already exist in the mock file system are overridden.
func FixtureMergeMockFs(mockFS MockFS) FixturePreparer {
return FixtureModifyMockFS(func(fs MockFS) {
fs.Merge(mockFS)
})
}
// Add a file to the mock filesystem
func FixtureAddFile(path string, contents []byte) FixturePreparer {
return FixtureModifyMockFS(func(fs MockFS) {
fs[path] = contents
})
}
// Add a text file to the mock filesystem
func FixtureAddTextFile(path string, contents string) FixturePreparer {
return FixtureAddFile(path, []byte(contents))
}
// Add the root Android.bp file with the supplied contents.
func FixtureWithRootAndroidBp(contents string) FixturePreparer {
return FixtureAddTextFile("Android.bp", contents)
}
// Create a composite FixturePreparer that is equivalent to applying each of the supplied
// FixturePreparer instances in order.
func FixturePreparers(preparers ...FixturePreparer) FixturePreparer {
return &compositeFixturePreparer{dedupAndFlattenPreparers(nil, preparers)}
}
type simpleFixturePreparerVisitor func(preparer *simpleFixturePreparer)
// FixturePreparer is an opaque interface that can change a fixture.
type FixturePreparer interface {
// visit calls the supplied visitor with each *simpleFixturePreparer instances in this preparer,
visit(simpleFixturePreparerVisitor)
}
type fixturePreparers []FixturePreparer
func (f fixturePreparers) visit(visitor simpleFixturePreparerVisitor) {
for _, p := range f {
p.visit(visitor)
}
}
// dedupAndFlattenPreparers removes any duplicates and flattens any composite FixturePreparer
// instances.
//
// base - a list of already flattened and deduped preparers that will be applied first before
// the list of additional preparers. Any duplicates of these in the additional preparers
// will be ignored.
//
// preparers - a list of additional unflattened, undeduped preparers that will be applied after the
// base preparers.
//
// Returns a deduped and flattened list of the preparers minus any that exist in the base preparers.
func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers fixturePreparers) []*simpleFixturePreparer {
var list []*simpleFixturePreparer
visited := make(map[*simpleFixturePreparer]struct{})
// Mark the already flattened and deduped preparers, if any, as having been seen so that
// duplicates of these in the additional preparers will be discarded.
for _, s := range base {
visited[s] = struct{}{}
}
preparers.visit(func(preparer *simpleFixturePreparer) {
if _, seen := visited[preparer]; !seen {
visited[preparer] = struct{}{}
list = append(list, preparer)
}
})
return list
}
// compositeFixturePreparer is a FixturePreparer created from a list of fixture preparers.
type compositeFixturePreparer struct {
preparers []*simpleFixturePreparer
}
func (c *compositeFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
for _, p := range c.preparers {
p.visit(visitor)
}
}
// simpleFixturePreparer is a FixturePreparer that applies a function to a fixture.
type simpleFixturePreparer struct {
function func(fixture *fixture)
}
func (s *simpleFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
visitor(s)
}
func newSimpleFixturePreparer(preparer func(fixture *fixture)) FixturePreparer {
return &simpleFixturePreparer{function: preparer}
}
// Fixture defines the test environment.
type Fixture interface {
// Run the test, expecting no errors, returning a TestResult instance.
RunTest() *TestResult
}
// Provides general test support.
type TestHelper struct {
*testing.T
}
// AssertBoolEquals checks if the expected and actual values are equal and if they are not then it
// reports an error prefixed with the supplied message and including a reason for why it failed.
func (h *TestHelper) AssertBoolEquals(message string, expected bool, actual bool) {
h.Helper()
if actual != expected {
h.Errorf("%s: expected %t, actual %t", message, expected, actual)
}
}
// AssertStringEquals checks if the expected and actual values are equal and if they are not then
// it reports an error prefixed with the supplied message and including a reason for why it failed.
func (h *TestHelper) AssertStringEquals(message string, expected string, actual string) {
h.Helper()
if actual != expected {
h.Errorf("%s: expected %s, actual %s", message, expected, actual)
}
}
// AssertTrimmedStringEquals checks if the expected and actual values are the same after trimming
// leading and trailing spaces from them both. If they are not then it reports an error prefixed
// with the supplied message and including a reason for why it failed.
func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) {
h.Helper()
h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual))
}
// AssertStringDoesContain checks if the string contains the expected substring. If it does not
// then it reports an error prefixed with the supplied message and including a reason for why it
// failed.
func (h *TestHelper) AssertStringDoesContain(message string, s string, expectedSubstring string) {
h.Helper()
if !strings.Contains(s, expectedSubstring) {
h.Errorf("%s: could not find %q within %q", message, expectedSubstring, s)
}
}
// AssertStringDoesNotContain checks if the string contains the expected substring. If it does then
// it reports an error prefixed with the supplied message and including a reason for why it failed.
func (h *TestHelper) AssertStringDoesNotContain(message string, s string, unexpectedSubstring string) {
h.Helper()
if strings.Contains(s, unexpectedSubstring) {
h.Errorf("%s: unexpectedly found %q within %q", message, unexpectedSubstring, s)
}
}
// AssertArrayString checks if the expected and actual values are equal and if they are not then it
// reports an error prefixed with the supplied message and including a reason for why it failed.
func (h *TestHelper) AssertArrayString(message string, expected, actual []string) {
h.Helper()
if len(actual) != len(expected) {
h.Errorf("%s: expected %d (%q), actual (%d) %q", message, len(expected), expected, len(actual), actual)
return
}
for i := range actual {
if actual[i] != expected[i] {
h.Errorf("%s: expected %d-th, %q (%q), actual %q (%q)",
message, i, expected[i], expected, actual[i], actual)
return
}
}
}
// AssertArrayString checks if the expected and actual values are equal using reflect.DeepEqual and
// if they are not then it reports an error prefixed with the supplied message and including a
// reason for why it failed.
func (h *TestHelper) AssertDeepEquals(message string, expected interface{}, actual interface{}) {
h.Helper()
if !reflect.DeepEqual(actual, expected) {
h.Errorf("%s: expected:\n %#v\n got:\n %#v", message, expected, actual)
}
}
// Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods.
type testContext struct {
*TestContext
}
// The result of running a test.
type TestResult struct {
TestHelper
testContext
fixture *fixture
Config Config
}
var _ FixtureFactory = (*fixtureFactory)(nil)
type fixtureFactory struct {
buildDirSupplier *string
preparers []*simpleFixturePreparer
}
func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixtureFactory {
all := append(f.preparers, dedupAndFlattenPreparers(f.preparers, preparers)...)
return &fixtureFactory{
buildDirSupplier: f.buildDirSupplier,
preparers: all,
}
}
func (f *fixtureFactory) Fixture(t *testing.T, preparers ...FixturePreparer) Fixture {
config := TestConfig(*f.buildDirSupplier, nil, "", nil)
ctx := NewTestContext(config)
fixture := &fixture{
factory: f,
t: t,
config: config,
ctx: ctx,
mockFS: make(MockFS),
}
for _, preparer := range f.preparers {
preparer.function(fixture)
}
for _, preparer := range dedupAndFlattenPreparers(f.preparers, preparers) {
preparer.function(fixture)
}
return fixture
}
func (f *fixtureFactory) RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult {
t.Helper()
fixture := f.Fixture(t, preparers...)
return fixture.RunTest()
}
func (f *fixtureFactory) RunTestWithBp(t *testing.T, bp string) *TestResult {
t.Helper()
return f.RunTest(t, FixtureWithRootAndroidBp(bp))
}
type fixture struct {
factory *fixtureFactory
t *testing.T
config Config
ctx *TestContext
mockFS MockFS
}
func (f *fixture) RunTest() *TestResult {
f.t.Helper()
ctx := f.ctx
// The TestConfig() method assumes that the mock filesystem is available when creating so creates
// the mock file system immediately. Similarly, the NewTestContext(Config) method assumes that the
// supplied Config's FileSystem has been properly initialized before it is called and so it takes
// its own reference to the filesystem. However, fixtures create the Config and TestContext early
// so they can be modified by preparers at which time the mockFS has not been populated (because
// it too is modified by preparers). So, this reinitializes the Config and TestContext's
// FileSystem using the now populated mockFS.
f.config.mockFileSystem("", f.mockFS)
ctx.SetFs(ctx.config.fs)
if ctx.config.mockBpList != "" {
ctx.SetModuleListFile(ctx.config.mockBpList)
}
ctx.Register()
_, errs := ctx.ParseBlueprintsFiles("ignored")
FailIfErrored(f.t, errs)
_, errs = ctx.PrepareBuildActions(f.config)
FailIfErrored(f.t, errs)
result := &TestResult{
TestHelper: TestHelper{T: f.t},
testContext: testContext{ctx},
fixture: f,
Config: f.config,
}
return result
}
// NormalizePathForTesting removes the test invocation specific build directory from the supplied
// path.
//
// If the path is within the build directory (e.g. an OutputPath) then this returns the relative
// path to avoid tests having to deal with the dynamically generated build directory.
//
// Otherwise, this returns the supplied path as it is almost certainly a source path that is
// relative to the root of the source tree.
//
// Even though some information is removed from some paths and not others it should be possible to
// differentiate between them by the paths themselves, e.g. output paths will likely include
// ".intermediates" but source paths won't.
func (r *TestResult) NormalizePathForTesting(path Path) string {
pathContext := PathContextForTesting(r.Config)
pathAsString := path.String()
if rel, isRel := MaybeRel(pathContext, r.Config.BuildDir(), pathAsString); isRel {
return rel
}
return pathAsString
}
// NormalizePathsForTesting normalizes each path in the supplied list and returns their normalized
// forms.
func (r *TestResult) NormalizePathsForTesting(paths Paths) []string {
var result []string
for _, path := range paths {
result = append(result, r.NormalizePathForTesting(path))
}
return result
}
// NewFixture creates a new test fixture that is based on the one that created this result. It is
// intended to test the output of module types that generate content to be processed by the build,
// e.g. sdk snapshots.
func (r *TestResult) NewFixture(preparers ...FixturePreparer) Fixture {
return r.fixture.factory.Fixture(r.T, preparers...)
}
// RunTest is shorthand for NewFixture(preparers...).RunTest().
func (r *TestResult) RunTest(preparers ...FixturePreparer) *TestResult {
r.Helper()
return r.fixture.factory.Fixture(r.T, preparers...).RunTest()
}
// Module returns the module with the specific name and of the specified variant.
func (r *TestResult) Module(name string, variant string) Module {
return r.ModuleForTests(name, variant).Module()
}
// Create a *TestResult object suitable for use within a subtest.
//
// This ensures that any errors reported by the TestResult, e.g. from within one of its
// Assert... methods, will be associated with the sub test and not the main test.
//
// result := ....RunTest()
// t.Run("subtest", func(t *testing.T) {
// subResult := result.ResultForSubTest(t)
// subResult.AssertStringEquals("something", ....)
// })
func (r *TestResult) ResultForSubTest(t *testing.T) *TestResult {
subTestResult := *r
r.T = t
return &subTestResult
}

49
android/fixture_test.go Normal file
View File

@ -0,0 +1,49 @@
// Copyright 2021 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package android
import "testing"
// Make sure that FixturePreparer instances are only called once per fixture and in the order in
// which they were added.
func TestFixtureDedup(t *testing.T) {
list := []string{}
appendToList := func(s string) FixturePreparer {
return FixtureModifyConfig(func(_ Config) {
list = append(list, s)
})
}
preparer1 := appendToList("preparer1")
preparer2 := appendToList("preparer2")
preparer3 := appendToList("preparer3")
preparer4 := appendToList("preparer4")
preparer1Then2 := FixturePreparers(preparer1, preparer2)
preparer2Then1 := FixturePreparers(preparer2, preparer1)
buildDir := "build"
factory := NewFixtureFactory(&buildDir, preparer1, preparer2, preparer1, preparer1Then2)
extension := factory.Extend(preparer4, preparer2)
extension.Fixture(t, preparer1, preparer2, preparer2Then1, preparer3)
h := TestHelper{t}
h.AssertDeepEquals("preparers called in wrong order",
[]string{"preparer1", "preparer2", "preparer4", "preparer3"}, list)
}

View File

@ -262,7 +262,7 @@ var prebuiltsTests = []struct {
} }
func TestPrebuilts(t *testing.T) { func TestPrebuilts(t *testing.T) {
fs := map[string][]byte{ fs := MockFS{
"prebuilt_file": nil, "prebuilt_file": nil,
"source_file": nil, "source_file": nil,
} }
@ -277,32 +277,33 @@ func TestPrebuilts(t *testing.T) {
deps: [":bar"], deps: [":bar"],
}` }`
} }
config := TestArchConfig(buildDir, nil, bp, fs)
// Add windows to the target list to test the logic when a variant is // Add windows to the target list to test the logic when a variant is
// disabled by default. // disabled by default.
if !Windows.DefaultDisabled { if !Windows.DefaultDisabled {
t.Errorf("windows is assumed to be disabled by default") t.Errorf("windows is assumed to be disabled by default")
} }
config.config.Targets[Windows] = []Target{
{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
}
ctx := NewTestArchContext(config) result := emptyTestFixtureFactory.Extend(
registerTestPrebuiltBuildComponents(ctx) PrepareForTestWithArchMutator,
ctx.RegisterModuleType("filegroup", FileGroupFactory) PrepareForTestWithPrebuilts,
ctx.Register() PrepareForTestWithOverrides,
PrepareForTestWithFilegroup,
// Add a Windows target to the configuration.
FixtureModifyConfig(func(config Config) {
config.Targets[Windows] = []Target{
{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
}
}),
fs.AddToFixture(),
FixtureRegisterWithContext(registerTestPrebuiltModules),
).RunTestWithBp(t, bp)
_, errs := ctx.ParseBlueprintsFiles("Android.bp") for _, variant := range result.ModuleVariantsForTests("foo") {
FailIfErrored(t, errs) foo := result.ModuleForTests("foo", variant)
_, errs = ctx.PrepareBuildActions(config)
FailIfErrored(t, errs)
for _, variant := range ctx.ModuleVariantsForTests("foo") {
foo := ctx.ModuleForTests("foo", variant)
t.Run(foo.Module().Target().Os.String(), func(t *testing.T) { t.Run(foo.Module().Target().Os.String(), func(t *testing.T) {
var dependsOnSourceModule, dependsOnPrebuiltModule bool var dependsOnSourceModule, dependsOnPrebuiltModule bool
ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) { result.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
if _, ok := m.(*sourceModule); ok { if _, ok := m.(*sourceModule); ok {
dependsOnSourceModule = true dependsOnSourceModule = true
} }
@ -381,14 +382,18 @@ func TestPrebuilts(t *testing.T) {
} }
func registerTestPrebuiltBuildComponents(ctx RegistrationContext) { func registerTestPrebuiltBuildComponents(ctx RegistrationContext) {
ctx.RegisterModuleType("prebuilt", newPrebuiltModule) registerTestPrebuiltModules(ctx)
ctx.RegisterModuleType("source", newSourceModule)
ctx.RegisterModuleType("override_source", newOverrideSourceModule)
RegisterPrebuiltMutators(ctx) RegisterPrebuiltMutators(ctx)
ctx.PostDepsMutators(RegisterOverridePostDepsMutators) ctx.PostDepsMutators(RegisterOverridePostDepsMutators)
} }
func registerTestPrebuiltModules(ctx RegistrationContext) {
ctx.RegisterModuleType("prebuilt", newPrebuiltModule)
ctx.RegisterModuleType("source", newSourceModule)
ctx.RegisterModuleType("override_source", newOverrideSourceModule)
}
type prebuiltModule struct { type prebuiltModule struct {
ModuleBase ModuleBase
prebuilt Prebuilt prebuilt Prebuilt

View File

@ -48,6 +48,43 @@ func NewTestContext(config Config) *TestContext {
return ctx return ctx
} }
var PrepareForTestWithArchMutator = FixturePreparers(
// Configure architecture targets in the fixture config.
FixtureModifyConfig(modifyTestConfigToSupportArchMutator),
// Add the arch mutator to the context.
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.PreDepsMutators(registerArchMutator)
}),
)
var PrepareForTestWithDefaults = FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
})
var PrepareForTestWithComponentsMutator = FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.PreArchMutators(RegisterComponentsMutator)
})
var PrepareForTestWithPrebuilts = FixtureRegisterWithContext(RegisterPrebuiltMutators)
var PrepareForTestWithOverrides = FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.PostDepsMutators(RegisterOverridePostDepsMutators)
})
// Prepares an integration test with build components from the android package.
var PrepareForIntegrationTestWithAndroid = FixturePreparers(
// Mutators. Must match order in mutator.go.
PrepareForTestWithArchMutator,
PrepareForTestWithDefaults,
PrepareForTestWithComponentsMutator,
PrepareForTestWithPrebuilts,
PrepareForTestWithOverrides,
// Modules
PrepareForTestWithFilegroup,
)
func NewTestArchContext(config Config) *TestContext { func NewTestArchContext(config Config) *TestContext {
ctx := NewTestContext(config) ctx := NewTestContext(config)
ctx.preDeps = append(ctx.preDeps, registerArchMutator) ctx.preDeps = append(ctx.preDeps, registerArchMutator)