ed49204e85
Using generics for the providers API allows a type to be associated with a ProviderKey, resulting in a type-safe API without that doesn't require runtime type assertions by every caller. Unfortunately, Go does not allow generic types in methods, only in functions [1]. This prevents a type-safe API on ModuleContext, and requires moving the API to be functions that take a ModuleContext as a parameter. [1] https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#no-parameterized-methods) Bug: 316410648 Test: provider_test.go Change-Id: Ide91de9f2a2a7d075b05e287c7cc86b395db0edb
425 lines
14 KiB
Go
425 lines
14 KiB
Go
// Copyright 2020 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 blueprint
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
type providerTestModule struct {
|
|
SimpleName
|
|
properties struct {
|
|
Deps []string
|
|
}
|
|
|
|
mutatorProviderValues []string
|
|
generateBuildActionsProviderValues []string
|
|
}
|
|
|
|
func newProviderTestModule() (Module, []interface{}) {
|
|
m := &providerTestModule{}
|
|
return m, []interface{}{&m.properties, &m.SimpleName.Properties}
|
|
}
|
|
|
|
type providerTestMutatorInfo struct {
|
|
Values []string
|
|
}
|
|
|
|
type providerTestGenerateBuildActionsInfo struct {
|
|
Value string
|
|
}
|
|
|
|
type providerTestUnsetInfo string
|
|
|
|
var providerTestMutatorInfoProvider = NewMutatorProvider[*providerTestMutatorInfo]("provider_mutator")
|
|
var providerTestGenerateBuildActionsInfoProvider = NewProvider[*providerTestGenerateBuildActionsInfo]()
|
|
var providerTestUnsetInfoProvider = NewMutatorProvider[providerTestUnsetInfo]("provider_mutator")
|
|
var providerTestUnusedMutatorProvider = NewMutatorProvider[*struct{ unused string }]("nonexistent_mutator")
|
|
|
|
func (p *providerTestModule) GenerateBuildActions(ctx ModuleContext) {
|
|
unset, ok := ModuleProvider(ctx, providerTestUnsetInfoProvider)
|
|
if ok {
|
|
panic(fmt.Errorf("expected false return value for providerTestGenerateBuildActionsInfoProvider before it was set"))
|
|
}
|
|
if unset != "" {
|
|
panic(fmt.Errorf("expected zero value for providerTestGenerateBuildActionsInfoProvider before it was set, got %q",
|
|
unset))
|
|
}
|
|
|
|
// Verify reading providerTestUnusedMutatorProvider doesn't panic
|
|
_, _ = ModuleProvider(ctx, providerTestUnusedMutatorProvider)
|
|
|
|
SetProvider(ctx, providerTestGenerateBuildActionsInfoProvider, &providerTestGenerateBuildActionsInfo{
|
|
Value: ctx.ModuleName(),
|
|
})
|
|
|
|
mp, ok := ModuleProvider(ctx, providerTestMutatorInfoProvider)
|
|
if ok {
|
|
p.mutatorProviderValues = mp.Values
|
|
}
|
|
|
|
ctx.VisitDirectDeps(func(module Module) {
|
|
gbap, _ := OtherModuleProvider(ctx, module, providerTestGenerateBuildActionsInfoProvider)
|
|
if gbap != nil {
|
|
p.generateBuildActionsProviderValues = append(p.generateBuildActionsProviderValues, gbap.Value)
|
|
}
|
|
})
|
|
}
|
|
|
|
func providerTestDepsMutator(ctx BottomUpMutatorContext) {
|
|
if p, ok := ctx.Module().(*providerTestModule); ok {
|
|
ctx.AddDependency(ctx.Module(), nil, p.properties.Deps...)
|
|
}
|
|
}
|
|
|
|
func providerTestMutator(ctx BottomUpMutatorContext) {
|
|
values := []string{strings.ToLower(ctx.ModuleName())}
|
|
|
|
ctx.VisitDirectDeps(func(module Module) {
|
|
mp, _ := OtherModuleProvider(ctx, module, providerTestMutatorInfoProvider)
|
|
if mp != nil {
|
|
values = append(values, mp.Values...)
|
|
}
|
|
})
|
|
|
|
SetProvider(ctx, providerTestMutatorInfoProvider, &providerTestMutatorInfo{
|
|
Values: values,
|
|
})
|
|
}
|
|
|
|
func providerTestAfterMutator(ctx BottomUpMutatorContext) {
|
|
// Verify reading providerTestUnusedMutatorProvider doesn't panic
|
|
_, _ = ModuleProvider(ctx, providerTestMutatorInfoProvider)
|
|
}
|
|
|
|
func TestProviders(t *testing.T) {
|
|
ctx := NewContext()
|
|
ctx.RegisterModuleType("provider_module", newProviderTestModule)
|
|
ctx.RegisterBottomUpMutator("provider_deps_mutator", providerTestDepsMutator)
|
|
ctx.RegisterBottomUpMutator("provider_mutator", providerTestMutator)
|
|
ctx.RegisterBottomUpMutator("provider_after_mutator", providerTestAfterMutator)
|
|
|
|
ctx.MockFileSystem(map[string][]byte{
|
|
"Android.bp": []byte(`
|
|
provider_module {
|
|
name: "A",
|
|
deps: ["B"],
|
|
}
|
|
|
|
provider_module {
|
|
name: "B",
|
|
deps: ["C", "D"],
|
|
}
|
|
|
|
provider_module {
|
|
name: "C",
|
|
deps: ["D"],
|
|
}
|
|
|
|
provider_module {
|
|
name: "D",
|
|
}
|
|
`),
|
|
})
|
|
|
|
_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
|
|
if len(errs) == 0 {
|
|
_, errs = ctx.ResolveDependencies(nil)
|
|
}
|
|
if len(errs) == 0 {
|
|
_, errs = ctx.PrepareBuildActions(nil)
|
|
}
|
|
if len(errs) > 0 {
|
|
t.Errorf("unexpected errors:")
|
|
for _, err := range errs {
|
|
t.Errorf(" %s", err)
|
|
}
|
|
t.FailNow()
|
|
}
|
|
|
|
aModule := ctx.moduleGroupFromName("A", nil).moduleByVariantName("").logicModule.(*providerTestModule)
|
|
if g, w := aModule.generateBuildActionsProviderValues, []string{"B"}; !reflect.DeepEqual(g, w) {
|
|
t.Errorf("expected A.generateBuildActionsProviderValues %q, got %q", w, g)
|
|
}
|
|
if g, w := aModule.mutatorProviderValues, []string{"a", "b", "c", "d", "d"}; !reflect.DeepEqual(g, w) {
|
|
t.Errorf("expected A.mutatorProviderValues %q, got %q", w, g)
|
|
}
|
|
|
|
bModule := ctx.moduleGroupFromName("B", nil).moduleByVariantName("").logicModule.(*providerTestModule)
|
|
if g, w := bModule.generateBuildActionsProviderValues, []string{"C", "D"}; !reflect.DeepEqual(g, w) {
|
|
t.Errorf("expected B.generateBuildActionsProviderValues %q, got %q", w, g)
|
|
}
|
|
if g, w := bModule.mutatorProviderValues, []string{"b", "c", "d", "d"}; !reflect.DeepEqual(g, w) {
|
|
t.Errorf("expected B.mutatorProviderValues %q, got %q", w, g)
|
|
}
|
|
}
|
|
|
|
type invalidProviderUsageMutatorInfo string
|
|
type invalidProviderUsageGenerateBuildActionsInfo string
|
|
|
|
var invalidProviderUsageMutatorInfoProvider = NewMutatorProvider[invalidProviderUsageMutatorInfo]("mutator_under_test")
|
|
var invalidProviderUsageGenerateBuildActionsInfoProvider = NewProvider[invalidProviderUsageGenerateBuildActionsInfo]()
|
|
|
|
type invalidProviderUsageTestModule struct {
|
|
parent *invalidProviderUsageTestModule
|
|
|
|
SimpleName
|
|
properties struct {
|
|
Deps []string
|
|
|
|
Early_mutator_set_of_mutator_provider bool
|
|
Late_mutator_set_of_mutator_provider bool
|
|
Late_build_actions_set_of_mutator_provider bool
|
|
Early_mutator_set_of_build_actions_provider bool
|
|
|
|
Early_mutator_get_of_mutator_provider bool
|
|
Early_module_get_of_mutator_provider bool
|
|
Early_mutator_get_of_build_actions_provider bool
|
|
Early_module_get_of_build_actions_provider bool
|
|
|
|
Duplicate_set bool
|
|
}
|
|
}
|
|
|
|
func invalidProviderUsageDepsMutator(ctx BottomUpMutatorContext) {
|
|
if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok {
|
|
ctx.AddDependency(ctx.Module(), nil, i.properties.Deps...)
|
|
}
|
|
}
|
|
|
|
func invalidProviderUsageParentMutator(ctx TopDownMutatorContext) {
|
|
if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok {
|
|
ctx.VisitDirectDeps(func(module Module) {
|
|
module.(*invalidProviderUsageTestModule).parent = i
|
|
})
|
|
}
|
|
}
|
|
|
|
func invalidProviderUsageBeforeMutator(ctx BottomUpMutatorContext) {
|
|
if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok {
|
|
if i.properties.Early_mutator_set_of_mutator_provider {
|
|
// A mutator attempting to set the value of a provider associated with a later mutator.
|
|
SetProvider(ctx, invalidProviderUsageMutatorInfoProvider, invalidProviderUsageMutatorInfo(""))
|
|
}
|
|
if i.properties.Early_mutator_get_of_mutator_provider {
|
|
// A mutator attempting to get the value of a provider associated with a later mutator.
|
|
_, _ = ModuleProvider(ctx, invalidProviderUsageMutatorInfoProvider)
|
|
}
|
|
}
|
|
}
|
|
|
|
func invalidProviderUsageMutatorUnderTest(ctx TopDownMutatorContext) {
|
|
if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok {
|
|
if i.properties.Early_mutator_set_of_build_actions_provider {
|
|
// A mutator attempting to set the value of a non-mutator provider.
|
|
SetProvider(ctx, invalidProviderUsageGenerateBuildActionsInfoProvider, invalidProviderUsageGenerateBuildActionsInfo(""))
|
|
}
|
|
if i.properties.Early_mutator_get_of_build_actions_provider {
|
|
// A mutator attempting to get the value of a non-mutator provider.
|
|
_, _ = ModuleProvider(ctx, invalidProviderUsageGenerateBuildActionsInfoProvider)
|
|
}
|
|
if i.properties.Early_module_get_of_mutator_provider {
|
|
// A mutator attempting to get the value of a provider associated with this mutator on
|
|
// a module for which this mutator hasn't run. This is a top down mutator so
|
|
// dependencies haven't run yet.
|
|
ctx.VisitDirectDeps(func(module Module) {
|
|
_, _ = OtherModuleProvider(ctx, module, invalidProviderUsageMutatorInfoProvider)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func invalidProviderUsageAfterMutator(ctx BottomUpMutatorContext) {
|
|
if i, ok := ctx.Module().(*invalidProviderUsageTestModule); ok {
|
|
if i.properties.Late_mutator_set_of_mutator_provider {
|
|
// A mutator trying to set the value of a provider associated with an earlier mutator.
|
|
SetProvider(ctx, invalidProviderUsageMutatorInfoProvider, invalidProviderUsageMutatorInfo(""))
|
|
}
|
|
if i.properties.Late_mutator_set_of_mutator_provider {
|
|
// A mutator trying to set the value of a provider associated with an earlier mutator.
|
|
SetProvider(ctx, invalidProviderUsageMutatorInfoProvider, invalidProviderUsageMutatorInfo(""))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (i *invalidProviderUsageTestModule) GenerateBuildActions(ctx ModuleContext) {
|
|
if i.properties.Late_build_actions_set_of_mutator_provider {
|
|
// A GenerateBuildActions trying to set the value of a provider associated with a mutator.
|
|
SetProvider(ctx, invalidProviderUsageMutatorInfoProvider, invalidProviderUsageMutatorInfo(""))
|
|
}
|
|
if i.properties.Early_module_get_of_build_actions_provider {
|
|
// A GenerateBuildActions trying to get the value of a provider on a module for which
|
|
// GenerateBuildActions hasn't run.
|
|
_, _ = OtherModuleProvider(ctx, i.parent, invalidProviderUsageGenerateBuildActionsInfoProvider)
|
|
}
|
|
if i.properties.Duplicate_set {
|
|
SetProvider(ctx, invalidProviderUsageGenerateBuildActionsInfoProvider, invalidProviderUsageGenerateBuildActionsInfo(""))
|
|
SetProvider(ctx, invalidProviderUsageGenerateBuildActionsInfoProvider, invalidProviderUsageGenerateBuildActionsInfo(""))
|
|
}
|
|
}
|
|
|
|
func TestInvalidProvidersUsage(t *testing.T) {
|
|
run := func(t *testing.T, module string, prop string, panicMsg string) {
|
|
t.Helper()
|
|
ctx := NewContext()
|
|
ctx.RegisterModuleType("invalid_provider_usage_test_module", func() (Module, []interface{}) {
|
|
m := &invalidProviderUsageTestModule{}
|
|
return m, []interface{}{&m.properties, &m.SimpleName.Properties}
|
|
})
|
|
ctx.RegisterBottomUpMutator("deps", invalidProviderUsageDepsMutator)
|
|
ctx.RegisterBottomUpMutator("before", invalidProviderUsageBeforeMutator)
|
|
ctx.RegisterTopDownMutator("mutator_under_test", invalidProviderUsageMutatorUnderTest)
|
|
ctx.RegisterBottomUpMutator("after", invalidProviderUsageAfterMutator)
|
|
ctx.RegisterTopDownMutator("parent", invalidProviderUsageParentMutator)
|
|
|
|
// Don't invalidate the parent pointer and before GenerateBuildActions.
|
|
ctx.SkipCloneModulesAfterMutators = true
|
|
|
|
var parentBP, moduleUnderTestBP, childBP string
|
|
|
|
prop += ": true,"
|
|
|
|
switch module {
|
|
case "parent":
|
|
parentBP = prop
|
|
case "module_under_test":
|
|
moduleUnderTestBP = prop
|
|
case "child":
|
|
childBP = prop
|
|
}
|
|
|
|
bp := fmt.Sprintf(`
|
|
invalid_provider_usage_test_module {
|
|
name: "parent",
|
|
deps: ["module_under_test"],
|
|
%s
|
|
}
|
|
|
|
invalid_provider_usage_test_module {
|
|
name: "module_under_test",
|
|
deps: ["child"],
|
|
%s
|
|
}
|
|
|
|
invalid_provider_usage_test_module {
|
|
name: "child",
|
|
%s
|
|
}
|
|
|
|
`,
|
|
parentBP,
|
|
moduleUnderTestBP,
|
|
childBP)
|
|
|
|
ctx.MockFileSystem(map[string][]byte{
|
|
"Android.bp": []byte(bp),
|
|
})
|
|
|
|
_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
|
|
|
|
if len(errs) == 0 {
|
|
_, errs = ctx.ResolveDependencies(nil)
|
|
}
|
|
|
|
if len(errs) == 0 {
|
|
_, errs = ctx.PrepareBuildActions(nil)
|
|
}
|
|
|
|
if len(errs) == 0 {
|
|
t.Fatal("expected an error")
|
|
}
|
|
|
|
if len(errs) > 1 {
|
|
t.Errorf("expected a single error, got %d:", len(errs))
|
|
for i, err := range errs {
|
|
t.Errorf("%d: %s", i, err)
|
|
}
|
|
t.FailNow()
|
|
}
|
|
|
|
if panicErr, ok := errs[0].(panicError); ok {
|
|
if panicErr.panic != panicMsg {
|
|
t.Fatalf("expected panic %q, got %q", panicMsg, panicErr.panic)
|
|
}
|
|
} else {
|
|
t.Fatalf("expected a panicError, got %T: %s", errs[0], errs[0].Error())
|
|
}
|
|
|
|
}
|
|
|
|
tests := []struct {
|
|
prop string
|
|
module string
|
|
|
|
panicMsg string
|
|
skip string
|
|
}{
|
|
{
|
|
prop: "early_mutator_set_of_mutator_provider",
|
|
module: "module_under_test",
|
|
panicMsg: "Can't set value of provider blueprint.invalidProviderUsageMutatorInfo before mutator mutator_under_test started",
|
|
},
|
|
{
|
|
prop: "late_mutator_set_of_mutator_provider",
|
|
module: "module_under_test",
|
|
panicMsg: "Can't set value of provider blueprint.invalidProviderUsageMutatorInfo after mutator mutator_under_test finished",
|
|
},
|
|
{
|
|
prop: "late_build_actions_set_of_mutator_provider",
|
|
module: "module_under_test",
|
|
panicMsg: "Can't set value of provider blueprint.invalidProviderUsageMutatorInfo after mutator mutator_under_test finished",
|
|
},
|
|
{
|
|
prop: "early_mutator_set_of_build_actions_provider",
|
|
module: "module_under_test",
|
|
panicMsg: "Can't set value of provider blueprint.invalidProviderUsageGenerateBuildActionsInfo before GenerateBuildActions started",
|
|
},
|
|
|
|
{
|
|
prop: "early_mutator_get_of_mutator_provider",
|
|
module: "module_under_test",
|
|
panicMsg: "Can't get value of provider blueprint.invalidProviderUsageMutatorInfo before mutator mutator_under_test finished",
|
|
},
|
|
{
|
|
prop: "early_module_get_of_mutator_provider",
|
|
module: "module_under_test",
|
|
panicMsg: "Can't get value of provider blueprint.invalidProviderUsageMutatorInfo before mutator mutator_under_test finished",
|
|
},
|
|
{
|
|
prop: "early_mutator_get_of_build_actions_provider",
|
|
module: "module_under_test",
|
|
panicMsg: "Can't get value of provider blueprint.invalidProviderUsageGenerateBuildActionsInfo before GenerateBuildActions finished",
|
|
},
|
|
{
|
|
prop: "early_module_get_of_build_actions_provider",
|
|
module: "module_under_test",
|
|
panicMsg: "Can't get value of provider blueprint.invalidProviderUsageGenerateBuildActionsInfo before GenerateBuildActions finished",
|
|
},
|
|
{
|
|
prop: "duplicate_set",
|
|
module: "module_under_test",
|
|
panicMsg: "Value of provider blueprint.invalidProviderUsageGenerateBuildActionsInfo is already set",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.prop, func(t *testing.T) {
|
|
run(t, tt.module, tt.prop, tt.panicMsg)
|
|
})
|
|
}
|
|
}
|