1// Copyright 2019 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package blueprint 16 17import ( 18 "reflect" 19 "strings" 20 "testing" 21) 22 23type moduleCtxTestModule struct { 24 SimpleName 25} 26 27func newModuleCtxTestModule() (Module, []interface{}) { 28 m := &moduleCtxTestModule{} 29 return m, []interface{}{&m.SimpleName.Properties} 30} 31 32func (f *moduleCtxTestModule) GenerateBuildActions(ModuleContext) { 33} 34 35func noCreateAliasMutator(name string) func(ctx BottomUpMutatorContext) { 36 return func(ctx BottomUpMutatorContext) { 37 if ctx.ModuleName() == name { 38 ctx.CreateVariations("a", "b") 39 } 40 } 41} 42 43func createAliasMutator(name string) func(ctx BottomUpMutatorContext) { 44 return func(ctx BottomUpMutatorContext) { 45 if ctx.ModuleName() == name { 46 ctx.CreateVariations("a", "b") 47 ctx.AliasVariation("b") 48 } 49 } 50} 51 52func addVariantDepsMutator(variants []Variation, tag DependencyTag, from, to string) func(ctx BottomUpMutatorContext) { 53 return func(ctx BottomUpMutatorContext) { 54 if ctx.ModuleName() == from { 55 ctx.AddVariationDependencies(variants, tag, to) 56 } 57 } 58} 59 60func TestAliases(t *testing.T) { 61 runWithFailures := func(ctx *Context, expectedErr string) { 62 t.Helper() 63 bp := ` 64 test { 65 name: "foo", 66 } 67 68 test { 69 name: "bar", 70 } 71 ` 72 73 mockFS := map[string][]byte{ 74 "Blueprints": []byte(bp), 75 } 76 77 ctx.MockFileSystem(mockFS) 78 79 _, errs := ctx.ParseFileList(".", []string{"Blueprints"}, nil) 80 if len(errs) > 0 { 81 t.Errorf("unexpected parse errors:") 82 for _, err := range errs { 83 t.Errorf(" %s", err) 84 } 85 } 86 87 _, errs = ctx.ResolveDependencies(nil) 88 if len(errs) > 0 { 89 if expectedErr == "" { 90 t.Errorf("unexpected dep errors:") 91 for _, err := range errs { 92 t.Errorf(" %s", err) 93 } 94 } else { 95 for _, err := range errs { 96 if strings.Contains(err.Error(), expectedErr) { 97 continue 98 } else { 99 t.Errorf("unexpected dep error: %s", err) 100 } 101 } 102 } 103 } else if expectedErr != "" { 104 t.Errorf("missing dep error: %s", expectedErr) 105 } 106 } 107 108 run := func(ctx *Context) { 109 t.Helper() 110 runWithFailures(ctx, "") 111 } 112 113 t.Run("simple", func(t *testing.T) { 114 // Creates a module "bar" with variants "a" and "b" and alias "" -> "b". 115 // Tests a dependency from "foo" to "bar" variant "b" through alias "". 116 ctx := NewContext() 117 ctx.RegisterModuleType("test", newModuleCtxTestModule) 118 ctx.RegisterBottomUpMutator("1", createAliasMutator("bar")) 119 ctx.RegisterBottomUpMutator("2", addVariantDepsMutator(nil, nil, "foo", "bar")) 120 121 run(ctx) 122 123 foo := ctx.moduleGroupFromName("foo", nil).modules[0] 124 barB := ctx.moduleGroupFromName("bar", nil).modules[1] 125 126 if g, w := barB.variantName, "b"; g != w { 127 t.Fatalf("expected bar.modules[1] variant to be %q, got %q", w, g) 128 } 129 130 if g, w := foo.forwardDeps, []*moduleInfo{barB}; !reflect.DeepEqual(g, w) { 131 t.Fatalf("expected foo deps to be %q, got %q", w, g) 132 } 133 }) 134 135 t.Run("chained", func(t *testing.T) { 136 // Creates a module "bar" with variants "a_a", "a_b", "b_a" and "b_b" and aliases "" -> "b_b", 137 // "a" -> "a_b", and "b" -> "b_b". 138 // Tests a dependency from "foo" to "bar" variant "b_b" through alias "". 139 ctx := NewContext() 140 ctx.RegisterModuleType("test", newModuleCtxTestModule) 141 ctx.RegisterBottomUpMutator("1", createAliasMutator("bar")) 142 ctx.RegisterBottomUpMutator("2", createAliasMutator("bar")) 143 ctx.RegisterBottomUpMutator("3", addVariantDepsMutator(nil, nil, "foo", "bar")) 144 145 run(ctx) 146 147 foo := ctx.moduleGroupFromName("foo", nil).modules[0] 148 barBB := ctx.moduleGroupFromName("bar", nil).modules[3] 149 150 if g, w := barBB.variantName, "b_b"; g != w { 151 t.Fatalf("expected bar.modules[3] variant to be %q, got %q", w, g) 152 } 153 154 if g, w := foo.forwardDeps, []*moduleInfo{barBB}; !reflect.DeepEqual(g, w) { 155 t.Fatalf("expected foo deps to be %q, got %q", w, g) 156 } 157 }) 158 159 t.Run("chained2", func(t *testing.T) { 160 // Creates a module "bar" with variants "a_a", "a_b", "b_a" and "b_b" and aliases "" -> "b_b", 161 // "a" -> "a_b", and "b" -> "b_b". 162 // Tests a dependency from "foo" to "bar" variant "a_b" through alias "a". 163 ctx := NewContext() 164 ctx.RegisterModuleType("test", newModuleCtxTestModule) 165 ctx.RegisterBottomUpMutator("1", createAliasMutator("bar")) 166 ctx.RegisterBottomUpMutator("2", createAliasMutator("bar")) 167 ctx.RegisterBottomUpMutator("3", addVariantDepsMutator([]Variation{{"1", "a"}}, nil, "foo", "bar")) 168 169 run(ctx) 170 171 foo := ctx.moduleGroupFromName("foo", nil).modules[0] 172 barAB := ctx.moduleGroupFromName("bar", nil).modules[1] 173 174 if g, w := barAB.variantName, "a_b"; g != w { 175 t.Fatalf("expected bar.modules[1] variant to be %q, got %q", w, g) 176 } 177 178 if g, w := foo.forwardDeps, []*moduleInfo{barAB}; !reflect.DeepEqual(g, w) { 179 t.Fatalf("expected foo deps to be %q, got %q", w, g) 180 } 181 }) 182 183 t.Run("removed dangling alias", func(t *testing.T) { 184 // Creates a module "bar" with variants "a" and "b" and aliases "" -> "b", then splits the variants into 185 // "a_a", "a_b", "b_a" and "b_b" without creating new aliases. 186 // Tests a dependency from "foo" to removed "bar" alias "" fails. 187 ctx := NewContext() 188 ctx.RegisterModuleType("test", newModuleCtxTestModule) 189 ctx.RegisterBottomUpMutator("1", createAliasMutator("bar")) 190 ctx.RegisterBottomUpMutator("2", noCreateAliasMutator("bar")) 191 ctx.RegisterBottomUpMutator("3", addVariantDepsMutator(nil, nil, "foo", "bar")) 192 193 runWithFailures(ctx, `dependency "bar" of "foo" missing variant:`+"\n \n"+ 194 "available variants:"+ 195 "\n 1:a, 2:a\n 1:a, 2:b\n 1:b, 2:a\n 1:b, 2:b") 196 }) 197} 198 199func expectedErrors(t *testing.T, errs []error, expectedMessages ...string) { 200 t.Helper() 201 if len(errs) != len(expectedMessages) { 202 t.Errorf("expected %d error, found: %q", len(expectedMessages), errs) 203 } else { 204 for i, expected := range expectedMessages { 205 err := errs[i] 206 if err.Error() != expected { 207 t.Errorf("expected error %q found %q", expected, err) 208 } 209 } 210 } 211} 212 213func TestCheckBlueprintSyntax(t *testing.T) { 214 factories := map[string]ModuleFactory{ 215 "test": newModuleCtxTestModule, 216 } 217 218 t.Run("valid", func(t *testing.T) { 219 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 220test { 221 name: "test", 222} 223`) 224 expectedErrors(t, errs) 225 }) 226 227 t.Run("syntax error", func(t *testing.T) { 228 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 229test { 230 name: "test", 231 232`) 233 234 expectedErrors(t, errs, `path/Blueprint:5:1: expected "}", found EOF`) 235 }) 236 237 t.Run("unknown module type", func(t *testing.T) { 238 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 239test2 { 240 name: "test", 241} 242`) 243 244 expectedErrors(t, errs, `path/Blueprint:2:1: unrecognized module type "test2"`) 245 }) 246 247 t.Run("unknown property name", func(t *testing.T) { 248 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 249test { 250 nam: "test", 251} 252`) 253 254 expectedErrors(t, errs, `path/Blueprint:3:5: unrecognized property "nam"`) 255 }) 256 257 t.Run("invalid property type", func(t *testing.T) { 258 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 259test { 260 name: false, 261} 262`) 263 264 expectedErrors(t, errs, `path/Blueprint:3:8: can't assign bool value to string property "name"`) 265 }) 266 267 t.Run("multiple failures", func(t *testing.T) { 268 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 269test { 270 name: false, 271} 272 273test2 { 274 name: false, 275} 276`) 277 278 expectedErrors(t, errs, 279 `path/Blueprint:3:8: can't assign bool value to string property "name"`, 280 `path/Blueprint:6:1: unrecognized module type "test2"`, 281 ) 282 }) 283} 284