// 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 := ctx.Provider(providerTestUnsetInfoProvider).(providerTestUnsetInfo) if unset != "" { panic(fmt.Sprintf("expected zero value for providerTestGenerateBuildActionsInfoProvider before it was set, got %q", unset)) } _ = ctx.Provider(providerTestUnusedMutatorProvider) ctx.SetProvider(providerTestGenerateBuildActionsInfoProvider, &providerTestGenerateBuildActionsInfo{ Value: ctx.ModuleName(), }) mp := ctx.Provider(providerTestMutatorInfoProvider).(*providerTestMutatorInfo) if mp != nil { p.mutatorProviderValues = mp.Values } ctx.VisitDirectDeps(func(module Module) { gbap := ctx.OtherModuleProvider(module, providerTestGenerateBuildActionsInfoProvider).(*providerTestGenerateBuildActionsInfo) 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 := ctx.OtherModuleProvider(module, providerTestMutatorInfoProvider).(*providerTestMutatorInfo) if mp != nil { values = append(values, mp.Values...) } }) ctx.SetProvider(providerTestMutatorInfoProvider, &providerTestMutatorInfo{ Values: values, }) } func providerTestAfterMutator(ctx BottomUpMutatorContext) { _ = ctx.Provider(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{ "Blueprints": []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("Blueprints", 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. ctx.SetProvider(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. _ = ctx.Provider(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. ctx.SetProvider(invalidProviderUsageGenerateBuildActionsInfoProvider, invalidProviderUsageGenerateBuildActionsInfo("")) } if i.properties.Early_mutator_get_of_build_actions_provider { // A mutator attempting to get the value of a non-mutator provider. _ = ctx.Provider(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) { _ = ctx.OtherModuleProvider(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. ctx.SetProvider(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. ctx.SetProvider(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. ctx.SetProvider(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. _ = ctx.OtherModuleProvider(i.parent, invalidProviderUsageGenerateBuildActionsInfoProvider) } if i.properties.Duplicate_set { ctx.SetProvider(invalidProviderUsageGenerateBuildActionsInfoProvider, invalidProviderUsageGenerateBuildActionsInfo("")) ctx.SetProvider(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{ "Blueprints": []byte(bp), }) _, errs := ctx.ParseBlueprintsFiles("Blueprints", 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) }) } }