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 noAliasMutator(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 aliasMutator(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 createAliasMutator(name string) func(ctx BottomUpMutatorContext) { 53 return func(ctx BottomUpMutatorContext) { 54 if ctx.ModuleName() == name { 55 ctx.CreateVariations("a", "b") 56 ctx.CreateAliasVariation("c", "a") 57 ctx.CreateAliasVariation("d", "b") 58 ctx.CreateAliasVariation("e", "a") 59 } 60 } 61} 62 63func addVariantDepsMutator(variants []Variation, tag DependencyTag, from, to string) func(ctx BottomUpMutatorContext) { 64 return func(ctx BottomUpMutatorContext) { 65 if ctx.ModuleName() == from { 66 ctx.AddVariationDependencies(variants, tag, to) 67 } 68 } 69} 70 71func addVariantDepsResultMutator(variants []Variation, tag DependencyTag, from, to string, results map[string][]Module) func(ctx BottomUpMutatorContext) { 72 return func(ctx BottomUpMutatorContext) { 73 if ctx.ModuleName() == from { 74 ret := ctx.AddVariationDependencies(variants, tag, to) 75 results[ctx.ModuleName()] = ret 76 } 77 } 78} 79 80func TestAliasVariation(t *testing.T) { 81 runWithFailures := func(ctx *Context, expectedErr string) { 82 t.Helper() 83 bp := ` 84 test { 85 name: "foo", 86 } 87 88 test { 89 name: "bar", 90 } 91 ` 92 93 mockFS := map[string][]byte{ 94 "Blueprints": []byte(bp), 95 } 96 97 ctx.MockFileSystem(mockFS) 98 99 _, errs := ctx.ParseFileList(".", []string{"Blueprints"}, nil) 100 if len(errs) > 0 { 101 t.Errorf("unexpected parse errors:") 102 for _, err := range errs { 103 t.Errorf(" %s", err) 104 } 105 } 106 107 _, errs = ctx.ResolveDependencies(nil) 108 if len(errs) > 0 { 109 if expectedErr == "" { 110 t.Errorf("unexpected dep errors:") 111 for _, err := range errs { 112 t.Errorf(" %s", err) 113 } 114 } else { 115 for _, err := range errs { 116 if strings.Contains(err.Error(), expectedErr) { 117 continue 118 } else { 119 t.Errorf("unexpected dep error: %s", err) 120 } 121 } 122 } 123 } else if expectedErr != "" { 124 t.Errorf("missing dep error: %s", expectedErr) 125 } 126 } 127 128 run := func(ctx *Context) { 129 t.Helper() 130 runWithFailures(ctx, "") 131 } 132 133 t.Run("simple", func(t *testing.T) { 134 // Creates a module "bar" with variants "a" and "b" and alias "" -> "b". 135 // Tests a dependency from "foo" to "bar" variant "b" through alias "". 136 ctx := NewContext() 137 ctx.RegisterModuleType("test", newModuleCtxTestModule) 138 ctx.RegisterBottomUpMutator("1", aliasMutator("bar")) 139 ctx.RegisterBottomUpMutator("2", addVariantDepsMutator(nil, nil, "foo", "bar")) 140 141 run(ctx) 142 143 foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") 144 barB := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("b") 145 146 if g, w := foo.forwardDeps, []*moduleInfo{barB}; !reflect.DeepEqual(g, w) { 147 t.Fatalf("expected foo deps to be %q, got %q", w, g) 148 } 149 }) 150 151 t.Run("chained", func(t *testing.T) { 152 // Creates a module "bar" with variants "a_a", "a_b", "b_a" and "b_b" and aliases "" -> "b_b", 153 // "a" -> "a_b", and "b" -> "b_b". 154 // Tests a dependency from "foo" to "bar" variant "b_b" through alias "". 155 ctx := NewContext() 156 ctx.RegisterModuleType("test", newModuleCtxTestModule) 157 ctx.RegisterBottomUpMutator("1", aliasMutator("bar")) 158 ctx.RegisterBottomUpMutator("2", aliasMutator("bar")) 159 ctx.RegisterBottomUpMutator("3", addVariantDepsMutator(nil, nil, "foo", "bar")) 160 161 run(ctx) 162 163 foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") 164 barBB := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("b_b") 165 166 if g, w := foo.forwardDeps, []*moduleInfo{barBB}; !reflect.DeepEqual(g, w) { 167 t.Fatalf("expected foo deps to be %q, got %q", w, g) 168 } 169 }) 170 171 t.Run("chained2", func(t *testing.T) { 172 // Creates a module "bar" with variants "a_a", "a_b", "b_a" and "b_b" and aliases "" -> "b_b", 173 // "a" -> "a_b", and "b" -> "b_b". 174 // Tests a dependency from "foo" to "bar" variant "a_b" through alias "a". 175 ctx := NewContext() 176 ctx.RegisterModuleType("test", newModuleCtxTestModule) 177 ctx.RegisterBottomUpMutator("1", aliasMutator("bar")) 178 ctx.RegisterBottomUpMutator("2", aliasMutator("bar")) 179 ctx.RegisterBottomUpMutator("3", addVariantDepsMutator([]Variation{{"1", "a"}}, nil, "foo", "bar")) 180 181 run(ctx) 182 183 foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") 184 barAB := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("a_b") 185 186 if g, w := foo.forwardDeps, []*moduleInfo{barAB}; !reflect.DeepEqual(g, w) { 187 t.Fatalf("expected foo deps to be %q, got %q", w, g) 188 } 189 }) 190 191 t.Run("removed dangling alias", func(t *testing.T) { 192 // Creates a module "bar" with variants "a" and "b" and aliases "" -> "b", then splits the variants into 193 // "a_a", "a_b", "b_a" and "b_b" without creating new aliases. 194 // Tests a dependency from "foo" to removed "bar" alias "" fails. 195 ctx := NewContext() 196 ctx.RegisterModuleType("test", newModuleCtxTestModule) 197 ctx.RegisterBottomUpMutator("1", aliasMutator("bar")) 198 ctx.RegisterBottomUpMutator("2", noAliasMutator("bar")) 199 ctx.RegisterBottomUpMutator("3", addVariantDepsMutator(nil, nil, "foo", "bar")) 200 201 runWithFailures(ctx, `dependency "bar" of "foo" missing variant:`+"\n \n"+ 202 "available variants:"+ 203 "\n 1:a,2:a\n 1:a,2:b\n 1:b,2:a\n 1:b,2:b") 204 }) 205} 206 207func TestCreateAliasVariations(t *testing.T) { 208 runWithFailures := func(ctx *Context, expectedErr string) { 209 t.Helper() 210 bp := ` 211 test { 212 name: "foo", 213 } 214 215 test { 216 name: "bar", 217 } 218 ` 219 220 mockFS := map[string][]byte{ 221 "Blueprints": []byte(bp), 222 } 223 224 ctx.MockFileSystem(mockFS) 225 226 _, errs := ctx.ParseFileList(".", []string{"Blueprints"}, nil) 227 if len(errs) > 0 { 228 t.Errorf("unexpected parse errors:") 229 for _, err := range errs { 230 t.Errorf(" %s", err) 231 } 232 } 233 234 _, errs = ctx.ResolveDependencies(nil) 235 if len(errs) > 0 { 236 if expectedErr == "" { 237 t.Errorf("unexpected dep errors:") 238 for _, err := range errs { 239 t.Errorf(" %s", err) 240 } 241 } else { 242 for _, err := range errs { 243 if strings.Contains(err.Error(), expectedErr) { 244 continue 245 } else { 246 t.Errorf("unexpected dep error: %s", err) 247 } 248 } 249 } 250 } else if expectedErr != "" { 251 t.Errorf("missing dep error: %s", expectedErr) 252 } 253 } 254 255 run := func(ctx *Context) { 256 t.Helper() 257 runWithFailures(ctx, "") 258 } 259 260 t.Run("simple", func(t *testing.T) { 261 // Creates a module "bar" with variants "a" and "b" and alias "c" -> "a", "d" -> "b", and "e" -> "a". 262 // Tests a dependency from "foo" to "bar" variant "b" through alias "d". 263 ctx := NewContext() 264 ctx.RegisterModuleType("test", newModuleCtxTestModule) 265 ctx.RegisterBottomUpMutator("1", createAliasMutator("bar")) 266 ctx.RegisterBottomUpMutator("2", addVariantDepsMutator([]Variation{{"1", "d"}}, nil, "foo", "bar")) 267 268 run(ctx) 269 270 foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") 271 barB := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("b") 272 273 if g, w := foo.forwardDeps, []*moduleInfo{barB}; !reflect.DeepEqual(g, w) { 274 t.Fatalf("expected foo deps to be %q, got %q", w, g) 275 } 276 }) 277 278 t.Run("chained", func(t *testing.T) { 279 // Creates a module "bar" with variants "a_a", "a_b", "b_a" and "b_b" and aliases "c" -> "a_b", 280 // "d" -> "b_b", and "d" -> "a_b". 281 // Tests a dependency from "foo" to "bar" variant "b_b" through alias "d". 282 ctx := NewContext() 283 ctx.RegisterModuleType("test", newModuleCtxTestModule) 284 ctx.RegisterBottomUpMutator("1", createAliasMutator("bar")) 285 ctx.RegisterBottomUpMutator("2", aliasMutator("bar")) 286 ctx.RegisterBottomUpMutator("3", addVariantDepsMutator([]Variation{{"1", "d"}}, nil, "foo", "bar")) 287 288 run(ctx) 289 290 foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") 291 barBB := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("b_b") 292 293 if g, w := foo.forwardDeps, []*moduleInfo{barBB}; !reflect.DeepEqual(g, w) { 294 t.Fatalf("expected foo deps to be %q, got %q", w, g) 295 } 296 }) 297 298 t.Run("removed dangling alias", func(t *testing.T) { 299 // Creates a module "bar" with variants "a" and "b" and alias "c" -> "a", "d" -> "b", and "e" -> "a", 300 // then splits the variants into "a_a", "a_b", "b_a" and "b_b" without creating new aliases. 301 // Tests a dependency from "foo" to removed "bar" alias "d" fails. 302 ctx := NewContext() 303 ctx.RegisterModuleType("test", newModuleCtxTestModule) 304 ctx.RegisterBottomUpMutator("1", createAliasMutator("bar")) 305 ctx.RegisterBottomUpMutator("2", noAliasMutator("bar")) 306 ctx.RegisterBottomUpMutator("3", addVariantDepsMutator([]Variation{{"1", "d"}}, nil, "foo", "bar")) 307 308 runWithFailures(ctx, `dependency "bar" of "foo" missing variant:`+"\n 1:d\n"+ 309 "available variants:"+ 310 "\n 1:a,2:a\n 1:a,2:b\n 1:b,2:a\n 1:b,2:b") 311 }) 312} 313 314func expectedErrors(t *testing.T, errs []error, expectedMessages ...string) { 315 t.Helper() 316 if len(errs) != len(expectedMessages) { 317 t.Errorf("expected %d error, found: %q", len(expectedMessages), errs) 318 } else { 319 for i, expected := range expectedMessages { 320 err := errs[i] 321 if err.Error() != expected { 322 t.Errorf("expected error %q found %q", expected, err) 323 } 324 } 325 } 326} 327 328func TestAddVariationDependencies(t *testing.T) { 329 runWithFailures := func(ctx *Context, expectedErr string) { 330 t.Helper() 331 bp := ` 332 test { 333 name: "foo", 334 } 335 336 test { 337 name: "bar", 338 } 339 ` 340 341 mockFS := map[string][]byte{ 342 "Blueprints": []byte(bp), 343 } 344 345 ctx.MockFileSystem(mockFS) 346 347 _, errs := ctx.ParseFileList(".", []string{"Blueprints"}, nil) 348 if len(errs) > 0 { 349 t.Errorf("unexpected parse errors:") 350 for _, err := range errs { 351 t.Errorf(" %s", err) 352 } 353 } 354 355 _, errs = ctx.ResolveDependencies(nil) 356 if len(errs) > 0 { 357 if expectedErr == "" { 358 t.Errorf("unexpected dep errors:") 359 for _, err := range errs { 360 t.Errorf(" %s", err) 361 } 362 } else { 363 for _, err := range errs { 364 if strings.Contains(err.Error(), expectedErr) { 365 continue 366 } else { 367 t.Errorf("unexpected dep error: %s", err) 368 } 369 } 370 } 371 } else if expectedErr != "" { 372 t.Errorf("missing dep error: %s", expectedErr) 373 } 374 } 375 376 run := func(ctx *Context) { 377 t.Helper() 378 runWithFailures(ctx, "") 379 } 380 381 t.Run("parallel", func(t *testing.T) { 382 ctx := NewContext() 383 ctx.RegisterModuleType("test", newModuleCtxTestModule) 384 results := make(map[string][]Module) 385 depsMutator := addVariantDepsResultMutator(nil, nil, "foo", "bar", results) 386 ctx.RegisterBottomUpMutator("deps", depsMutator).Parallel() 387 388 run(ctx) 389 390 foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") 391 bar := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("") 392 393 if g, w := foo.forwardDeps, []*moduleInfo{bar}; !reflect.DeepEqual(g, w) { 394 t.Fatalf("expected foo deps to be %q, got %q", w, g) 395 } 396 397 if g, w := results["foo"], []Module{bar.logicModule}; !reflect.DeepEqual(g, w) { 398 t.Fatalf("expected AddVariationDependencies return value to be %q, got %q", w, g) 399 } 400 }) 401 402 t.Run("non-parallel", func(t *testing.T) { 403 ctx := NewContext() 404 ctx.RegisterModuleType("test", newModuleCtxTestModule) 405 results := make(map[string][]Module) 406 depsMutator := addVariantDepsResultMutator(nil, nil, "foo", "bar", results) 407 ctx.RegisterBottomUpMutator("deps", depsMutator) 408 run(ctx) 409 410 foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") 411 bar := ctx.moduleGroupFromName("bar", nil).moduleByVariantName("") 412 413 if g, w := foo.forwardDeps, []*moduleInfo{bar}; !reflect.DeepEqual(g, w) { 414 t.Fatalf("expected foo deps to be %q, got %q", w, g) 415 } 416 417 if g, w := results["foo"], []Module{nil}; !reflect.DeepEqual(g, w) { 418 t.Fatalf("expected AddVariationDependencies return value to be %q, got %q", w, g) 419 } 420 }) 421 422 t.Run("missing", func(t *testing.T) { 423 ctx := NewContext() 424 ctx.RegisterModuleType("test", newModuleCtxTestModule) 425 results := make(map[string][]Module) 426 depsMutator := addVariantDepsResultMutator(nil, nil, "foo", "baz", results) 427 ctx.RegisterBottomUpMutator("deps", depsMutator).Parallel() 428 runWithFailures(ctx, `"foo" depends on undefined module "baz"`) 429 430 foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") 431 432 if g, w := foo.forwardDeps, []*moduleInfo(nil); !reflect.DeepEqual(g, w) { 433 t.Fatalf("expected foo deps to be %q, got %q", w, g) 434 } 435 436 if g, w := results["foo"], []Module{nil}; !reflect.DeepEqual(g, w) { 437 t.Fatalf("expected AddVariationDependencies return value to be %q, got %q", w, g) 438 } 439 }) 440 441 t.Run("allow missing", func(t *testing.T) { 442 ctx := NewContext() 443 ctx.SetAllowMissingDependencies(true) 444 ctx.RegisterModuleType("test", newModuleCtxTestModule) 445 results := make(map[string][]Module) 446 depsMutator := addVariantDepsResultMutator(nil, nil, "foo", "baz", results) 447 ctx.RegisterBottomUpMutator("deps", depsMutator).Parallel() 448 run(ctx) 449 450 foo := ctx.moduleGroupFromName("foo", nil).moduleByVariantName("") 451 452 if g, w := foo.forwardDeps, []*moduleInfo(nil); !reflect.DeepEqual(g, w) { 453 t.Fatalf("expected foo deps to be %q, got %q", w, g) 454 } 455 456 if g, w := results["foo"], []Module{nil}; !reflect.DeepEqual(g, w) { 457 t.Fatalf("expected AddVariationDependencies return value to be %q, got %q", w, g) 458 } 459 }) 460 461} 462 463func TestCheckBlueprintSyntax(t *testing.T) { 464 factories := map[string]ModuleFactory{ 465 "test": newModuleCtxTestModule, 466 } 467 468 t.Run("valid", func(t *testing.T) { 469 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 470test { 471 name: "test", 472} 473`) 474 expectedErrors(t, errs) 475 }) 476 477 t.Run("syntax error", func(t *testing.T) { 478 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 479test { 480 name: "test", 481 482`) 483 484 expectedErrors(t, errs, `path/Blueprint:5:1: expected "}", found EOF`) 485 }) 486 487 t.Run("unknown module type", func(t *testing.T) { 488 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 489test2 { 490 name: "test", 491} 492`) 493 494 expectedErrors(t, errs, `path/Blueprint:2:1: unrecognized module type "test2"`) 495 }) 496 497 t.Run("unknown property name", func(t *testing.T) { 498 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 499test { 500 nam: "test", 501} 502`) 503 504 expectedErrors(t, errs, `path/Blueprint:3:5: unrecognized property "nam"`) 505 }) 506 507 t.Run("invalid property type", func(t *testing.T) { 508 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 509test { 510 name: false, 511} 512`) 513 514 expectedErrors(t, errs, `path/Blueprint:3:8: can't assign bool value to string property "name"`) 515 }) 516 517 t.Run("multiple failures", func(t *testing.T) { 518 errs := CheckBlueprintSyntax(factories, "path/Blueprint", ` 519test { 520 name: false, 521} 522 523test2 { 524 name: false, 525} 526`) 527 528 expectedErrors(t, errs, 529 `path/Blueprint:3:8: can't assign bool value to string property "name"`, 530 `path/Blueprint:6:1: unrecognized module type "test2"`, 531 ) 532 }) 533} 534