| // 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 ( |
| "bytes" |
| "fmt" |
| "reflect" |
| "strings" |
| "testing" |
| |
| "github.com/google/blueprint/gobtools" |
| ) |
| |
| type providerTestModule struct { |
| ModuleBase |
| SimpleName |
| properties struct { |
| Deps []string |
| } |
| |
| mutatorProviderValues []string |
| generateBuildActionsProviderValues []string |
| } |
| |
| func newProviderTestModule() (Module, []interface{}) { |
| m := &providerTestModule{} |
| return m, []interface{}{&m.properties, &m.SimpleName.Properties} |
| } |
| |
| var providerTestSingletonProvider = NewSingletonProvider[bool]() |
| |
| type providerTestParallelSingleton struct { |
| visitAllSingletons bool |
| } |
| |
| func (s *providerTestParallelSingleton) GenerateBuildActions(ctx SingletonContext) { |
| if s.visitAllSingletons { |
| ctx.VisitAllSingletons(func(s SingletonProxy) {}) |
| } |
| ctx.SetSingletonProvider(providerTestSingletonProvider, true) |
| } |
| |
| func (s *providerTestParallelSingleton) IncrementalSupported() bool { |
| return true |
| } |
| |
| type providerTestSequentialSingleton struct { |
| getModuleProvider bool |
| setModuleProvider bool |
| } |
| |
| func (s *providerTestSequentialSingleton) GenerateBuildActions(ctx SingletonContext) { |
| if s.getModuleProvider { |
| ctx.VisitAllSingletons(func(s SingletonProxy) { |
| ctx.OtherSingletonProvider(s, providerTestMutatorInfoProvider) |
| }) |
| } |
| if s.setModuleProvider { |
| ctx.SetSingletonProvider(providerTestMutatorInfoProvider, nil) |
| } |
| ctx.VisitAllSingletons(func(s SingletonProxy) { |
| ctx.OtherSingletonProvider(s, providerTestSingletonProvider) |
| }) |
| } |
| |
| func (s *providerTestSequentialSingleton) IncrementalSupported() bool { |
| return true |
| } |
| |
| type providerTestMutatorInfo struct { |
| Values []string |
| } |
| |
| type providerTestGenerateBuildActionsInfo struct { |
| Value string |
| } |
| |
| func (providerTestGenerateBuildActionsInfo) Encode(ctx gobtools.EncContext, buf *bytes.Buffer) error { |
| return nil |
| } |
| |
| func (providerTestGenerateBuildActionsInfo) GetTypeId() int16 { |
| return -1 |
| } |
| |
| 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 |
| |
| func (invalidProviderUsageGenerateBuildActionsInfo) Encode(ctx gobtools.EncContext, buf *bytes.Buffer) error { |
| return nil |
| } |
| |
| func (invalidProviderUsageGenerateBuildActionsInfo) GetTypeId() int16 { |
| return -1 |
| } |
| |
| var invalidProviderUsageMutatorInfoProvider = NewMutatorProvider[invalidProviderUsageMutatorInfo]("mutator_under_test") |
| var invalidProviderUsageGenerateBuildActionsInfoProvider = NewProvider[invalidProviderUsageGenerateBuildActionsInfo]() |
| |
| type invalidProviderUsageTestModule struct { |
| ModuleBase |
| 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_mutator_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 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 BottomUpMutatorContext) { |
| 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) |
| } |
| } |
| } |
| |
| 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.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.RegisterBottomUpMutator("mutator_under_test", invalidProviderUsageMutatorUnderTest) |
| ctx.RegisterBottomUpMutator("after", invalidProviderUsageAfterMutator) |
| |
| // 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_mutator_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) |
| }) |
| } |
| } |
| |
| func TestInvalidSingletonProvidersUsage(t *testing.T) { |
| type singletonType struct { |
| name string |
| factory SingletonFactory |
| parallel bool |
| } |
| |
| run := func(t *testing.T, singletons []singletonType, panicMsg string) { |
| t.Helper() |
| ctx := NewContext() |
| for _, s := range singletons { |
| ctx.RegisterSingletonType(s.name, s.factory, s.parallel) |
| } |
| |
| ctx.MockFileSystem(map[string][]byte{ |
| "Android.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(panicMsg) == 0 { |
| if len(errs) != 0 { |
| t.Fatal("got an error: ", errs) |
| } |
| return |
| } |
| |
| 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 { |
| testName string |
| singletons []singletonType |
| panicMsg string |
| }{ |
| { |
| testName: "get_non_singleton_provider", |
| singletons: []singletonType{ |
| { |
| name: "parallel_singleton", |
| factory: func() Singleton { |
| return &providerTestParallelSingleton{} |
| }, |
| parallel: true, |
| }, |
| { |
| name: "sequential_singleton", |
| factory: func() Singleton { |
| return &providerTestSequentialSingleton{ |
| getModuleProvider: true, |
| } |
| }, |
| parallel: false, |
| }, |
| }, |
| panicMsg: "Can't get value of non-singleton provider *blueprint.providerTestMutatorInfo usign singleton specific get method", |
| }, |
| { |
| testName: "set_non_singleton_provider", |
| singletons: []singletonType{ |
| { |
| name: "sequential_singleton", |
| factory: func() Singleton { |
| return &providerTestSequentialSingleton{ |
| setModuleProvider: true, |
| } |
| }, |
| parallel: false, |
| }, |
| }, |
| panicMsg: "Can't set value of non-singleton provider *blueprint.providerTestMutatorInfo inside singleton sequential_singleton", |
| }, |
| { |
| testName: "set_get_singleton_provider", |
| singletons: []singletonType{ |
| { |
| name: "parallel_singleton", |
| factory: func() Singleton { |
| return &providerTestParallelSingleton{} |
| }, |
| parallel: true, |
| }, |
| { |
| name: "sequential_singleton", |
| factory: func() Singleton { |
| return &providerTestSequentialSingleton{} |
| }, |
| parallel: false, |
| }, |
| }, |
| panicMsg: "", |
| }, |
| { |
| testName: "visit_all_singletons_in_parallel_singleton", |
| singletons: []singletonType{ |
| { |
| name: "parallel_singleton", |
| factory: func() Singleton { |
| return &providerTestParallelSingleton{visitAllSingletons: true} |
| }, |
| parallel: true, |
| }, |
| }, |
| panicMsg: "VisitAllSingletons not allowed in parallel singletons parallel_singleton", |
| }, |
| } |
| |
| for _, tt := range tests { |
| t.Run(tt.testName, func(t *testing.T) { |
| run(t, tt.singletons, tt.panicMsg) |
| }) |
| } |
| } |