Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4c83e5cc authored by Colin Cross's avatar Colin Cross
Browse files

Support testing Rules in Modules and Rules and Builds in Singletons

Add support for TestingModule to return RuleParams for rules created
by the module.

Refactor TestingModule to use helpers, and use the helpers to
implement a similar TestingSingleton.

Use the new functionality to test RuleBuilder's module and singleton
rules.

Test: none
Change-Id: I8348c56ff5086d0c49401f5a00faf7c864e6b6f3
parent 87ecbfe1
Loading
Loading
Loading
Loading
+19 −1
Original line number Diff line number Diff line
@@ -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) {
+1 −1
Original line number Diff line number Diff line
@@ -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}
	}
}

+18 −9
Original line number Diff line number Diff line
@@ -364,17 +364,26 @@ func TestRuleBuilder_Build(t *testing.T) {
	_, errs = ctx.PrepareBuildActions(config)
	FailIfErrored(t, errs)

	foo := ctx.ModuleForTests("foo", "").Rule("rule")

	// TODO: make RuleParams accessible to tests and verify rule.Command().Tools() ends up in CommandDeps
	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)
		}

	if len(foo.Implicits) != 1 || foo.Implicits[0].String() != "bar" {
		t.Errorf("want foo.Implicits = [%q], got %q", "bar", foo.Implicits.Strings())
		if len(params.Implicits) != 1 || params.Implicits[0].String() != "bar" {
			t.Errorf("want Implicits = [%q], got %q", "bar", params.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"))
	})
}
+49 −18
Original line number Diff line number Diff line
@@ -76,10 +76,31 @@ type SingletonContext interface {

type singletonAdaptor struct {
	Singleton

	buildParams []BuildParams
	ruleParams  map[blueprint.Rule]blueprint.RuleParams
}

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) GenerateBuildActions(ctx blueprint.SingletonContext) {
	s.Singleton.GenerateBuildActions(singletonContextAdaptor{ctx})
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)
}
+159 −47
Original line number Diff line number Diff line
@@ -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,86 +139,76 @@ func (ctx *TestContext) MockFileSystem(files map[string][]byte) {
	ctx.Context.MockFileSystem(files)
}

// 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 {
	module Module
type testBuildProvider interface {
	BuildParamsForTests() []BuildParams
	RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
}

// Module returns the Module wrapped by the TestingModule.
func (m TestingModule) Module() Module {
	return m.module
type TestingBuildParams struct {
	BuildParams
	RuleParams blueprint.RuleParams
}

// 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() {
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 p
			return newTestingBuildParams(provider, p)
		}
	}
	return BuildParams{}
	return TestingBuildParams{}
}

// 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)
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
}

// 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() {
func maybeBuildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
	for _, p := range provider.BuildParamsForTests() {
		if p.Description == desc {
			return p
			return newTestingBuildParams(provider, p)
		}
	}
	return BuildParams{}
	return TestingBuildParams{}
}

// 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)
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 (m TestingModule) maybeOutput(file string) (BuildParams, []string) {
func maybeBuildParamsFromOutput(provider testBuildProvider, file string) (TestingBuildParams, []string) {
	var searchedOutputs []string
	for _, p := range m.module.BuildParamsForTests() {
	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 p, nil
				return newTestingBuildParams(provider, p), nil
			}
			searchedOutputs = append(searchedOutputs, f.Rel())
		}
	}
	return BuildParams{}, searchedOutputs
	return TestingBuildParams{}, searchedOutputs
}

// 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)
	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)
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))
@@ -208,10 +216,9 @@ func (m TestingModule) Output(file string) BuildParams {
	return p
}

// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
func (m TestingModule) AllOutputs() []string {
func allOutputs(provider testBuildProvider) []string {
	var outputFullPaths []string
	for _, p := range m.module.BuildParamsForTests() {
	for _, p := range provider.BuildParamsForTests() {
		outputs := append(WritablePaths(nil), p.Outputs...)
		if p.Output != nil {
			outputs = append(outputs, p.Output)
@@ -221,6 +228,111 @@ func (m TestingModule) AllOutputs() []string {
	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 {
	module Module
}

// Module returns the Module wrapped by the TestingModule.
func (m TestingModule) Module() Module {
	return m.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) 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) 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) 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) 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) 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) 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 {
	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) {
	t.Helper()
	if len(errs) > 0 {