Support testing Rules in Modules and Rules and Builds in Singletons

am: 4c83e5ccd4

Change-Id: If0fc3ebf39c1556fa447367f90650325d43c43ec
This commit is contained in:
Colin Cross 2019-03-04 19:27:21 -08:00 committed by android-build-merger
commit 6a64436220
5 changed files with 263 additions and 93 deletions

View file

@ -193,6 +193,7 @@ type Module interface {
GetProperties() []interface{}
BuildParamsForTests() []BuildParams
RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
VariablesForTests() map[string]string
}
@ -477,6 +478,7 @@ type ModuleBase struct {
// For tests
buildParams []BuildParams
ruleParams map[blueprint.Rule]blueprint.RuleParams
variables map[string]string
prefer32 func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool
@ -496,6 +498,10 @@ func (a *ModuleBase) BuildParamsForTests() []BuildParams {
return a.buildParams
}
func (a *ModuleBase) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams {
return a.ruleParams
}
func (a *ModuleBase) VariablesForTests() map[string]string {
return a.variables
}
@ -795,6 +801,10 @@ func (a *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext)
variables: make(map[string]string),
}
if ctx.config.captureBuild {
ctx.ruleParams = make(map[blueprint.Rule]blueprint.RuleParams)
}
desc := "//" + ctx.ModuleDir() + ":" + ctx.ModuleName() + " "
var suffix []string
if ctx.Os().Class != Device && ctx.Os().Class != Generic {
@ -854,6 +864,7 @@ func (a *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext)
}
a.buildParams = ctx.buildParams
a.ruleParams = ctx.ruleParams
a.variables = ctx.variables
}
@ -877,6 +888,7 @@ type androidModuleContext struct {
// For tests
buildParams []BuildParams
ruleParams map[blueprint.Rule]blueprint.RuleParams
variables map[string]string
}
@ -952,7 +964,13 @@ func (a *androidModuleContext) Variable(pctx PackageContext, name, value string)
func (a *androidModuleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams,
argNames ...string) blueprint.Rule {
return a.ModuleContext.Rule(pctx.PackageContext, name, params, argNames...)
rule := a.ModuleContext.Rule(pctx.PackageContext, name, params, argNames...)
if a.config.captureBuild {
a.ruleParams[rule] = params
}
return rule
}
func (a *androidModuleContext) Build(pctx PackageContext, params BuildParams) {

View file

@ -61,7 +61,7 @@ func SingletonFactoryAdaptor(factory SingletonFactory) blueprint.SingletonFactor
if makevars, ok := singleton.(SingletonMakeVarsProvider); ok {
registerSingletonMakeVarsProvider(makevars)
}
return singletonAdaptor{singleton}
return &singletonAdaptor{Singleton: singleton}
}
}

View file

@ -364,17 +364,26 @@ func TestRuleBuilder_Build(t *testing.T) {
_, errs = ctx.PrepareBuildActions(config)
FailIfErrored(t, errs)
foo := ctx.ModuleForTests("foo", "").Rule("rule")
check := func(t *testing.T, params TestingBuildParams, wantOutput string) {
if len(params.RuleParams.CommandDeps) != 1 || params.RuleParams.CommandDeps[0] != "cp" {
t.Errorf("want RuleParams.CommandDeps = [%q], got %q", "cp", params.RuleParams.CommandDeps)
}
// TODO: make RuleParams accessible to tests and verify rule.Command().Tools() ends up in CommandDeps
if len(params.Implicits) != 1 || params.Implicits[0].String() != "bar" {
t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings())
}
if len(foo.Implicits) != 1 || foo.Implicits[0].String() != "bar" {
t.Errorf("want foo.Implicits = [%q], got %q", "bar", foo.Implicits.Strings())
}
wantOutput := filepath.Join(buildDir, ".intermediates", "foo", "foo")
if len(foo.Outputs) != 1 || foo.Outputs[0].String() != wantOutput {
t.Errorf("want foo.Outputs = [%q], got %q", wantOutput, foo.Outputs.Strings())
if len(params.Outputs) != 1 || params.Outputs[0].String() != wantOutput {
t.Errorf("want Outputs = [%q], got %q", wantOutput, params.Outputs.Strings())
}
}
t.Run("module", func(t *testing.T) {
check(t, ctx.ModuleForTests("foo", "").Rule("rule"),
filepath.Join(buildDir, ".intermediates", "foo", "foo"))
})
t.Run("singleton", func(t *testing.T) {
check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"),
filepath.Join(buildDir, "baz"))
})
}

View file

@ -76,10 +76,31 @@ type SingletonContext interface {
type singletonAdaptor struct {
Singleton
buildParams []BuildParams
ruleParams map[blueprint.Rule]blueprint.RuleParams
}
func (s singletonAdaptor) GenerateBuildActions(ctx blueprint.SingletonContext) {
s.Singleton.GenerateBuildActions(singletonContextAdaptor{ctx})
var _ testBuildProvider = (*singletonAdaptor)(nil)
func (s *singletonAdaptor) GenerateBuildActions(ctx blueprint.SingletonContext) {
sctx := &singletonContextAdaptor{SingletonContext: ctx}
if sctx.Config().captureBuild {
sctx.ruleParams = make(map[blueprint.Rule]blueprint.RuleParams)
}
s.Singleton.GenerateBuildActions(sctx)
s.buildParams = sctx.buildParams
s.ruleParams = sctx.ruleParams
}
func (s *singletonAdaptor) BuildParamsForTests() []BuildParams {
return s.buildParams
}
func (s *singletonAdaptor) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams {
return s.ruleParams
}
type Singleton interface {
@ -88,35 +109,45 @@ type Singleton interface {
type singletonContextAdaptor struct {
blueprint.SingletonContext
buildParams []BuildParams
ruleParams map[blueprint.Rule]blueprint.RuleParams
}
func (s singletonContextAdaptor) Config() Config {
func (s *singletonContextAdaptor) Config() Config {
return s.SingletonContext.Config().(Config)
}
func (s singletonContextAdaptor) DeviceConfig() DeviceConfig {
func (s *singletonContextAdaptor) DeviceConfig() DeviceConfig {
return DeviceConfig{s.Config().deviceConfig}
}
func (s singletonContextAdaptor) Variable(pctx PackageContext, name, value string) {
func (s *singletonContextAdaptor) Variable(pctx PackageContext, name, value string) {
s.SingletonContext.Variable(pctx.PackageContext, name, value)
}
func (s singletonContextAdaptor) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule {
return s.SingletonContext.Rule(pctx.PackageContext, name, params, argNames...)
func (s *singletonContextAdaptor) Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule {
rule := s.SingletonContext.Rule(pctx.PackageContext, name, params, argNames...)
if s.Config().captureBuild {
s.ruleParams[rule] = params
}
return rule
}
func (s singletonContextAdaptor) Build(pctx PackageContext, params BuildParams) {
func (s *singletonContextAdaptor) Build(pctx PackageContext, params BuildParams) {
if s.Config().captureBuild {
s.buildParams = append(s.buildParams, params)
}
bparams := convertBuildParams(params)
s.SingletonContext.Build(pctx.PackageContext, bparams)
}
func (s singletonContextAdaptor) SetNinjaBuildDir(pctx PackageContext, value string) {
func (s *singletonContextAdaptor) SetNinjaBuildDir(pctx PackageContext, value string) {
s.SingletonContext.SetNinjaBuildDir(pctx.PackageContext, value)
}
func (s singletonContextAdaptor) Eval(pctx PackageContext, ninjaStr string) (string, error) {
func (s *singletonContextAdaptor) Eval(pctx PackageContext, ninjaStr string) (string, error) {
return s.SingletonContext.Eval(pctx.PackageContext, ninjaStr)
}
@ -144,34 +175,34 @@ func predAdaptor(pred func(Module) bool) func(blueprint.Module) bool {
}
}
func (s singletonContextAdaptor) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
func (s *singletonContextAdaptor) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
s.SingletonContext.VisitAllModules(visit)
}
func (s singletonContextAdaptor) VisitAllModules(visit func(Module)) {
func (s *singletonContextAdaptor) VisitAllModules(visit func(Module)) {
s.SingletonContext.VisitAllModules(visitAdaptor(visit))
}
func (s singletonContextAdaptor) VisitAllModulesIf(pred func(Module) bool, visit func(Module)) {
func (s *singletonContextAdaptor) VisitAllModulesIf(pred func(Module) bool, visit func(Module)) {
s.SingletonContext.VisitAllModulesIf(predAdaptor(pred), visitAdaptor(visit))
}
func (s singletonContextAdaptor) VisitDepsDepthFirst(module Module, visit func(Module)) {
func (s *singletonContextAdaptor) VisitDepsDepthFirst(module Module, visit func(Module)) {
s.SingletonContext.VisitDepsDepthFirst(module, visitAdaptor(visit))
}
func (s singletonContextAdaptor) VisitDepsDepthFirstIf(module Module, pred func(Module) bool, visit func(Module)) {
func (s *singletonContextAdaptor) VisitDepsDepthFirstIf(module Module, pred func(Module) bool, visit func(Module)) {
s.SingletonContext.VisitDepsDepthFirstIf(module, predAdaptor(pred), visitAdaptor(visit))
}
func (s singletonContextAdaptor) VisitAllModuleVariants(module Module, visit func(Module)) {
func (s *singletonContextAdaptor) VisitAllModuleVariants(module Module, visit func(Module)) {
s.SingletonContext.VisitAllModuleVariants(module, visitAdaptor(visit))
}
func (s singletonContextAdaptor) PrimaryModule(module Module) Module {
func (s *singletonContextAdaptor) PrimaryModule(module Module) Module {
return s.SingletonContext.PrimaryModule(module).(Module)
}
func (s singletonContextAdaptor) FinalModule(module Module) Module {
func (s *singletonContextAdaptor) FinalModule(module Module) Module {
return s.SingletonContext.FinalModule(module).(Module)
}

View file

@ -31,7 +31,7 @@ func NewTestContext() *TestContext {
nameResolver := NewNameResolver(namespaceExportFilter)
ctx := &TestContext{
Context: blueprint.NewContext(),
Context: &Context{blueprint.NewContext()},
NameResolver: nameResolver,
}
@ -47,7 +47,7 @@ func NewTestArchContext() *TestContext {
}
type TestContext struct {
*blueprint.Context
*Context
preArch, preDeps, postDeps []RegisterMutatorFunc
NameResolver *NameResolver
}
@ -65,7 +65,7 @@ func (ctx *TestContext) PostDepsMutators(f RegisterMutatorFunc) {
}
func (ctx *TestContext) Register() {
registerMutators(ctx.Context, ctx.preArch, ctx.preDeps, ctx.postDeps)
registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps)
ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(EnvSingleton))
}
@ -102,6 +102,24 @@ func (ctx *TestContext) ModuleVariantsForTests(name string) []string {
return variants
}
// SingletonForTests returns a TestingSingleton for the singleton registered with the given name.
func (ctx *TestContext) SingletonForTests(name string) TestingSingleton {
allSingletonNames := []string{}
for _, s := range ctx.Singletons() {
n := ctx.SingletonName(s)
if n == name {
return TestingSingleton{
singleton: s.(*singletonAdaptor).Singleton,
provider: s.(testBuildProvider),
}
}
allSingletonNames = append(allSingletonNames, n)
}
panic(fmt.Errorf("failed to find singleton %q."+
"\nall singletons: %v", name, allSingletonNames))
}
// MockFileSystem causes the Context to replace all reads with accesses to the provided map of
// filenames to contents stored as a byte slice.
func (ctx *TestContext) MockFileSystem(files map[string][]byte) {
@ -121,6 +139,95 @@ func (ctx *TestContext) MockFileSystem(files map[string][]byte) {
ctx.Context.MockFileSystem(files)
}
type testBuildProvider interface {
BuildParamsForTests() []BuildParams
RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
}
type TestingBuildParams struct {
BuildParams
RuleParams blueprint.RuleParams
}
func newTestingBuildParams(provider testBuildProvider, bparams BuildParams) TestingBuildParams {
return TestingBuildParams{
BuildParams: bparams,
RuleParams: provider.RuleParamsForTests()[bparams.Rule],
}
}
func maybeBuildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams {
for _, p := range provider.BuildParamsForTests() {
if strings.Contains(p.Rule.String(), rule) {
return newTestingBuildParams(provider, p)
}
}
return TestingBuildParams{}
}
func buildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams {
p := maybeBuildParamsFromRule(provider, rule)
if p.Rule == nil {
panic(fmt.Errorf("couldn't find rule %q", rule))
}
return p
}
func maybeBuildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
for _, p := range provider.BuildParamsForTests() {
if p.Description == desc {
return newTestingBuildParams(provider, p)
}
}
return TestingBuildParams{}
}
func buildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
p := maybeBuildParamsFromDescription(provider, desc)
if p.Rule == nil {
panic(fmt.Errorf("couldn't find description %q", desc))
}
return p
}
func maybeBuildParamsFromOutput(provider testBuildProvider, file string) (TestingBuildParams, []string) {
var searchedOutputs []string
for _, p := range provider.BuildParamsForTests() {
outputs := append(WritablePaths(nil), p.Outputs...)
if p.Output != nil {
outputs = append(outputs, p.Output)
}
for _, f := range outputs {
if f.String() == file || f.Rel() == file {
return newTestingBuildParams(provider, p), nil
}
searchedOutputs = append(searchedOutputs, f.Rel())
}
}
return TestingBuildParams{}, searchedOutputs
}
func buildParamsFromOutput(provider testBuildProvider, file string) TestingBuildParams {
p, searchedOutputs := maybeBuildParamsFromOutput(provider, file)
if p.Rule == nil {
panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v",
file, searchedOutputs))
}
return p
}
func allOutputs(provider testBuildProvider) []string {
var outputFullPaths []string
for _, p := range provider.BuildParamsForTests() {
outputs := append(WritablePaths(nil), p.Outputs...)
if p.Output != nil {
outputs = append(outputs, p.Output)
}
outputFullPaths = append(outputFullPaths, outputs.Strings()...)
}
return outputFullPaths
}
// TestingModule is wrapper around an android.Module that provides methods to find information about individual
// ctx.Build parameters for verification in tests.
type TestingModule struct {
@ -134,91 +241,96 @@ func (m TestingModule) Module() Module {
// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty
// BuildParams if no rule is found.
func (m TestingModule) MaybeRule(rule string) BuildParams {
for _, p := range m.module.BuildParamsForTests() {
if strings.Contains(p.Rule.String(), rule) {
return p
}
}
return BuildParams{}
func (m TestingModule) MaybeRule(rule string) TestingBuildParams {
return maybeBuildParamsFromRule(m.module, rule)
}
// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found.
func (m TestingModule) Rule(rule string) BuildParams {
p := m.MaybeRule(rule)
if p.Rule == nil {
panic(fmt.Errorf("couldn't find rule %q", rule))
}
return p
func (m TestingModule) Rule(rule string) TestingBuildParams {
return buildParamsFromRule(m.module, rule)
}
// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty
// BuildParams if no rule is found.
func (m TestingModule) MaybeDescription(desc string) BuildParams {
for _, p := range m.module.BuildParamsForTests() {
if p.Description == desc {
return p
}
}
return BuildParams{}
func (m TestingModule) MaybeDescription(desc string) TestingBuildParams {
return maybeBuildParamsFromDescription(m.module, desc)
}
// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is
// found.
func (m TestingModule) Description(desc string) BuildParams {
p := m.MaybeDescription(desc)
if p.Rule == nil {
panic(fmt.Errorf("couldn't find description %q", desc))
}
return p
}
func (m TestingModule) maybeOutput(file string) (BuildParams, []string) {
var searchedOutputs []string
for _, p := range m.module.BuildParamsForTests() {
outputs := append(WritablePaths(nil), p.Outputs...)
if p.Output != nil {
outputs = append(outputs, p.Output)
}
for _, f := range outputs {
if f.String() == file || f.Rel() == file {
return p, nil
}
searchedOutputs = append(searchedOutputs, f.Rel())
}
}
return BuildParams{}, searchedOutputs
func (m TestingModule) Description(desc string) TestingBuildParams {
return buildParamsFromDescription(m.module, desc)
}
// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
// value matches the provided string. Returns an empty BuildParams if no rule is found.
func (m TestingModule) MaybeOutput(file string) BuildParams {
p, _ := m.maybeOutput(file)
func (m TestingModule) MaybeOutput(file string) TestingBuildParams {
p, _ := maybeBuildParamsFromOutput(m.module, file)
return p
}
// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
// value matches the provided string. Panics if no rule is found.
func (m TestingModule) Output(file string) BuildParams {
p, searchedOutputs := m.maybeOutput(file)
if p.Rule == nil {
panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v",
file, searchedOutputs))
}
return p
func (m TestingModule) Output(file string) TestingBuildParams {
return buildParamsFromOutput(m.module, file)
}
// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
func (m TestingModule) AllOutputs() []string {
var outputFullPaths []string
for _, p := range m.module.BuildParamsForTests() {
outputs := append(WritablePaths(nil), p.Outputs...)
if p.Output != nil {
outputs = append(outputs, p.Output)
}
outputFullPaths = append(outputFullPaths, outputs.Strings()...)
}
return outputFullPaths
return allOutputs(m.module)
}
// TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual
// ctx.Build parameters for verification in tests.
type TestingSingleton struct {
singleton Singleton
provider testBuildProvider
}
// Singleton returns the Singleton wrapped by the TestingSingleton.
func (s TestingSingleton) Singleton() Singleton {
return s.singleton
}
// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty
// BuildParams if no rule is found.
func (s TestingSingleton) MaybeRule(rule string) TestingBuildParams {
return maybeBuildParamsFromRule(s.provider, rule)
}
// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found.
func (s TestingSingleton) Rule(rule string) TestingBuildParams {
return buildParamsFromRule(s.provider, rule)
}
// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty
// BuildParams if no rule is found.
func (s TestingSingleton) MaybeDescription(desc string) TestingBuildParams {
return maybeBuildParamsFromDescription(s.provider, desc)
}
// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is
// found.
func (s TestingSingleton) Description(desc string) TestingBuildParams {
return buildParamsFromDescription(s.provider, desc)
}
// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
// value matches the provided string. Returns an empty BuildParams if no rule is found.
func (s TestingSingleton) MaybeOutput(file string) TestingBuildParams {
p, _ := maybeBuildParamsFromOutput(s.provider, file)
return p
}
// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
// value matches the provided string. Panics if no rule is found.
func (s TestingSingleton) Output(file string) TestingBuildParams {
return buildParamsFromOutput(s.provider, file)
}
// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
func (s TestingSingleton) AllOutputs() []string {
return allOutputs(s.provider)
}
func FailIfErrored(t *testing.T, errs []error) {