diff --git a/android/module.go b/android/module.go index 516fa7878..d1a779d5b 100644 --- a/android/module.go +++ b/android/module.go @@ -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) { diff --git a/android/register.go b/android/register.go index 93c287032..d79982a31 100644 --- a/android/register.go +++ b/android/register.go @@ -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} } } diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go index f738fafcf..f157ab7d9 100644 --- a/android/rule_builder_test.go +++ b/android/rule_builder_test.go @@ -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")) + }) } diff --git a/android/singleton.go b/android/singleton.go index 05ec6b542..a59d54aa2 100644 --- a/android/singleton.go +++ b/android/singleton.go @@ -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) } diff --git a/android/testing.go b/android/testing.go index b7a043e58..b4008af37 100644 --- a/android/testing.go +++ b/android/testing.go @@ -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) {