github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/model/project_matrix_test.go (about) 1 package model 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/evergreen-ci/evergreen/command" 8 . "github.com/smartystreets/goconvey/convey" 9 ) 10 11 func TestMatrixIntermediateParsing(t *testing.T) { 12 Convey("Testing different project files with matrix definitions", t, func() { 13 Convey("a set of axes should parse", func() { 14 axes := ` 15 axes: 16 - id: os 17 display_name: Operating System 18 values: 19 - id: ubuntu 20 display_name: Ubuntu 21 tags: "linux" 22 variables: 23 user: root 24 run_on: ubuntu_small 25 - id: rhel 26 display_name: Red Hat 27 tags: ["linux", "enterprise"] 28 run_on: 29 - rhel55 30 - rhel62 31 ` 32 p, errs := createIntermediateProject([]byte(axes)) 33 So(errs, ShouldBeNil) 34 axis := p.Axes[0] 35 So(axis.Id, ShouldEqual, "os") 36 So(axis.DisplayName, ShouldEqual, "Operating System") 37 So(len(axis.Values), ShouldEqual, 2) 38 So(axis.Values[0], ShouldResemble, axisValue{ 39 Id: "ubuntu", 40 DisplayName: "Ubuntu", 41 Tags: []string{"linux"}, 42 Variables: map[string]string{"user": "root"}, 43 RunOn: []string{"ubuntu_small"}, 44 }) 45 So(axis.Values[1], ShouldResemble, axisValue{ 46 Id: "rhel", 47 DisplayName: "Red Hat", 48 Tags: []string{"linux", "enterprise"}, 49 RunOn: []string{"rhel55", "rhel62"}, 50 }) 51 }) 52 Convey("a barebones matrix definition should parse", func() { 53 simple := ` 54 buildvariants: 55 - matrix_name: "test" 56 matrix_spec: {"os": ".linux", "bits":["32", "64"]} 57 exclude_spec: [{"os":"ubuntu", "bits":"32"}] 58 - matrix_name: "test2" 59 matrix_spec: 60 os: "windows95" 61 color: 62 - red 63 - blue 64 - green 65 ` 66 p, errs := createIntermediateProject([]byte(simple)) 67 So(errs, ShouldBeNil) 68 So(len(p.BuildVariants), ShouldEqual, 2) 69 m1 := *p.BuildVariants[0].matrix 70 So(m1, ShouldResemble, matrix{ 71 Id: "test", 72 Spec: matrixDefinition{ 73 "os": []string{".linux"}, 74 "bits": []string{"32", "64"}, 75 }, 76 Exclude: []matrixDefinition{ 77 {"os": []string{"ubuntu"}, "bits": []string{"32"}}, 78 }, 79 }) 80 m2 := *p.BuildVariants[1].matrix 81 So(m2, ShouldResemble, matrix{ 82 Id: "test2", 83 Spec: matrixDefinition{ 84 "os": []string{"windows95"}, 85 "color": []string{"red", "blue", "green"}, 86 }, 87 }) 88 }) 89 Convey("a mixed definition should parse", func() { 90 simple := ` 91 buildvariants: 92 - matrix_name: "test" 93 matrix_spec: {"os": "*", "bits": "*"} 94 - name: "single_variant" 95 tasks: "*" 96 ` 97 p, errs := createIntermediateProject([]byte(simple)) 98 So(errs, ShouldBeNil) 99 So(len(p.BuildVariants), ShouldEqual, 2) 100 m1 := *p.BuildVariants[0].matrix 101 So(m1.Id, ShouldEqual, "test") 102 So(p.BuildVariants[1].Name, ShouldEqual, "single_variant") 103 So(p.BuildVariants[1].Tasks, ShouldResemble, parserBVTasks{parserBVTask{Name: "*"}}) 104 }) 105 }) 106 } 107 108 func TestMatrixDefinitionAllCells(t *testing.T) { 109 Convey("With a set of test definitions", t, func() { 110 Convey("an empty definition should return an empty list", func() { 111 a := matrixDefinition{} 112 cells := a.allCells() 113 So(len(cells), ShouldEqual, 0) 114 }) 115 Convey("an empty axis should cause a panic", func() { 116 a := matrixDefinition{ 117 "a": []string{}, 118 "b": []string{"1"}, 119 } 120 So(func() { a.allCells() }, ShouldPanic) 121 }) 122 Convey("a one-cell matrix should return a one-item list", func() { 123 a := matrixDefinition{ 124 "a": []string{"0"}, 125 } 126 cells := a.allCells() 127 So(len(cells), ShouldEqual, 1) 128 So(cells, ShouldContainResembling, matrixValue{"a": "0"}) 129 b := matrixDefinition{ 130 "a": []string{"0"}, 131 "b": []string{"1"}, 132 "c": []string{"2"}, 133 } 134 cells = b.allCells() 135 So(len(cells), ShouldEqual, 1) 136 So(cells, ShouldContainResembling, matrixValue{"a": "0", "b": "1", "c": "2"}) 137 }) 138 Convey("a one-axis matrix should return an equivalent list", func() { 139 a := matrixDefinition{ 140 "a": []string{"0", "1", "2"}, 141 } 142 cells := a.allCells() 143 So(len(cells), ShouldEqual, 3) 144 So(cells, ShouldContainResembling, matrixValue{"a": "0"}) 145 So(cells, ShouldContainResembling, matrixValue{"a": "1"}) 146 So(cells, ShouldContainResembling, matrixValue{"a": "2"}) 147 b := matrixDefinition{ 148 "a": []string{"0"}, 149 "b": []string{"0", "1", "2"}, 150 } 151 cells = b.allCells() 152 So(len(cells), ShouldEqual, 3) 153 So(cells, ShouldContainResembling, matrixValue{"b": "0", "a": "0"}) 154 So(cells, ShouldContainResembling, matrixValue{"b": "1", "a": "0"}) 155 So(cells, ShouldContainResembling, matrixValue{"b": "2", "a": "0"}) 156 c := matrixDefinition{ 157 "c": []string{"0", "1", "2"}, 158 "d": []string{"0"}, 159 } 160 cells = c.allCells() 161 So(len(cells), ShouldEqual, 3) 162 So(cells, ShouldContainResembling, matrixValue{"c": "0", "d": "0"}) 163 So(cells, ShouldContainResembling, matrixValue{"c": "1", "d": "0"}) 164 So(cells, ShouldContainResembling, matrixValue{"c": "2", "d": "0"}) 165 }) 166 Convey("a 2x2 matrix should expand properly", func() { 167 a := matrixDefinition{ 168 "a": []string{"0", "1"}, 169 "b": []string{"0", "1"}, 170 } 171 cells := a.allCells() 172 So(len(cells), ShouldEqual, 4) 173 So(cells, ShouldContainResembling, matrixValue{"a": "0", "b": "0"}) 174 So(cells, ShouldContainResembling, matrixValue{"a": "1", "b": "0"}) 175 So(cells, ShouldContainResembling, matrixValue{"a": "0", "b": "1"}) 176 So(cells, ShouldContainResembling, matrixValue{"a": "1", "b": "1"}) 177 }) 178 Convey("a disgustingly large matrix should expand properly", func() { 179 bigList := func(max int) []string { 180 out := []string{} 181 for i := 0; i < max; i++ { 182 out = append(out, fmt.Sprint(i)) 183 } 184 return out 185 } 186 187 huge := matrixDefinition{ 188 "a": bigList(15), 189 "b": bigList(290), 190 "c": bigList(20), 191 } 192 cells := huge.allCells() 193 So(len(cells), ShouldEqual, 15*290*20) 194 So(cells, ShouldContainResembling, matrixValue{"a": "0", "b": "0", "c": "0"}) 195 So(cells, ShouldContainResembling, matrixValue{"a": "14", "b": "289", "c": "19"}) 196 // some random guesses just for fun 197 So(cells, ShouldContainResembling, matrixValue{"a": "10", "b": "29", "c": "1"}) 198 So(cells, ShouldContainResembling, matrixValue{"a": "1", "b": "2", "c": "17"}) 199 So(cells, ShouldContainResembling, matrixValue{"a": "8", "b": "100", "c": "5"}) 200 }) 201 }) 202 } 203 204 func TestMatrixDefinitionContains(t *testing.T) { 205 Convey("With a set of test definitions", t, func() { 206 Convey("an empty definition should match nothing", func() { 207 a := matrixDefinition{} 208 So(a.contains(matrixValue{"a": "0"}), ShouldBeFalse) 209 }) 210 Convey("all definitions contain the empty value", func() { 211 a := matrixDefinition{} 212 So(a.contains(matrixValue{}), ShouldBeTrue) 213 b := matrixDefinition{ 214 "a": []string{"0", "1"}, 215 "b": []string{"0", "1"}, 216 } 217 So(b.contains(matrixValue{}), ShouldBeTrue) 218 }) 219 Convey("a one-axis matrix should match all of its elements", func() { 220 a := matrixDefinition{ 221 "a": []string{"0", "1", "2"}, 222 } 223 So(a.contains(matrixValue{"a": "0"}), ShouldBeTrue) 224 So(a.contains(matrixValue{"a": "1"}), ShouldBeTrue) 225 So(a.contains(matrixValue{"a": "2"}), ShouldBeTrue) 226 So(a.contains(matrixValue{"a": "3"}), ShouldBeFalse) 227 }) 228 Convey("a 2x2 matrix should match all of its elements", func() { 229 a := matrixDefinition{ 230 "a": []string{"0", "1"}, 231 "b": []string{"0", "1"}, 232 } 233 cells := a.allCells() 234 So(len(cells), ShouldEqual, 4) 235 So(a.contains(matrixValue{"a": "0", "b": "0"}), ShouldBeTrue) 236 So(a.contains(matrixValue{"a": "1", "b": "0"}), ShouldBeTrue) 237 So(a.contains(matrixValue{"a": "0", "b": "1"}), ShouldBeTrue) 238 So(a.contains(matrixValue{"a": "1", "b": "1"}), ShouldBeTrue) 239 So(a.contains(matrixValue{"a": "1", "b": "2"}), ShouldBeFalse) 240 Convey("and sub-match all of its individual axis values", func() { 241 So(a.contains(matrixValue{"a": "0"}), ShouldBeTrue) 242 So(a.contains(matrixValue{"a": "1"}), ShouldBeTrue) 243 So(a.contains(matrixValue{"b": "0"}), ShouldBeTrue) 244 So(a.contains(matrixValue{"b": "1"}), ShouldBeTrue) 245 So(a.contains(matrixValue{"b": "7"}), ShouldBeFalse) 246 So(a.contains(matrixValue{"c": "1"}), ShouldBeFalse) 247 So(a.contains(matrixValue{"a": "1", "b": "1", "c": "1"}), ShouldBeFalse) 248 }) 249 }) 250 }) 251 } 252 253 func TestBuildMatrixVariantSimple(t *testing.T) { 254 testMatrix := &matrix{Id: "test"} 255 Convey("With a set of test axes", t, func() { 256 axes := []matrixAxis{ 257 { 258 Id: "a", 259 Values: []axisValue{ 260 {Id: "0", Tags: []string{"zero"}}, 261 {Id: "1", Tags: []string{"odd"}}, 262 {Id: "2", Tags: []string{"even", "prime"}}, 263 {Id: "3", Tags: []string{"odd", "prime"}}, 264 }, 265 }, 266 { 267 Id: "b", 268 Values: []axisValue{ 269 {Id: "0", Tags: []string{"zero"}}, 270 {Id: "1", Tags: []string{"odd"}}, 271 {Id: "2", Tags: []string{"even", "prime"}}, 272 {Id: "3", Tags: []string{"odd", "prime"}}, 273 }, 274 }, 275 } 276 Convey("and matrix value test:{a:0, b:0}", func() { 277 mv := matrixValue{"a": "0", "b": "0"} 278 Convey("the variant should build without error", func() { 279 v, err := buildMatrixVariant(axes, mv, testMatrix, nil) 280 So(err, ShouldBeNil) 281 Convey("with id='test__a~0_b~0', tags=[zero]", func() { 282 So(v.Name, ShouldEqual, "test__a~0_b~0") 283 So(v.matrixVal, ShouldResemble, mv) 284 So(v.Tags, ShouldContain, "zero") 285 So(v.matrixId, ShouldEqual, "test") 286 }) 287 }) 288 }) 289 Convey("and matrix value test:{a:1, b:3}", func() { 290 mv := matrixValue{"b": "3", "a": "1"} 291 Convey("the variant should build without error", func() { 292 v, err := buildMatrixVariant(axes, mv, testMatrix, nil) 293 So(err, ShouldBeNil) 294 Convey("with id='test__a~1_b~3', tags=[odd, prime]", func() { 295 So(v.Name, ShouldEqual, "test__a~1_b~3") 296 So(v.Tags, ShouldContain, "odd") 297 So(v.Tags, ShouldContain, "prime") 298 }) 299 }) 300 }) 301 Convey("and a matrix value that references non-existent axis values", func() { 302 mv := matrixValue{"b": "2", "a": "4"} 303 Convey("should return an error", func() { 304 _, err := buildMatrixVariant(axes, mv, testMatrix, nil) 305 So(err, ShouldNotBeNil) 306 }) 307 }) 308 Convey("and a matrix value that references non-existent axis names", func() { 309 mv := matrixValue{"b": "2", "coolfun": "4"} 310 Convey("should return an error", func() { 311 _, err := buildMatrixVariant(axes, mv, testMatrix, nil) 312 So(err, ShouldNotBeNil) 313 }) 314 }) 315 }) 316 } 317 318 // helper for pulling variants out of a list 319 func findVariant(vs []parserBV, id string) parserBV { 320 for _, v := range vs { 321 if v.Name == id { 322 return v 323 } 324 } 325 panic("not found") 326 } 327 328 func TestMatrixVariantsSimple(t *testing.T) { 329 Convey("With a delicious set of test axes", t, func() { 330 // These tests are structured around a magical project that tests 331 // colorful candies. We will be testing M&Ms, Skittles, and Necco Wafers 332 // (all candies copyright their respective holders). We need to test 333 // each color of each candy individually, so we've decided to simplify 334 // our variant definitions with a matrix! The colors are as follows: 335 // M&Ms: red, orange, yellow, green, blue, brown (6) 336 // Skittles: red, orange, yellow, green, purple (5) 337 // Necco: orange, yellow, green, purple, pink, brown, black, white (8) 338 // TODO: maybe move this up top for multiple tests 339 axes := []matrixAxis{ 340 { 341 Id: "color", 342 Values: []axisValue{ 343 {Id: "red", Tags: []string{"hot_color"}}, 344 {Id: "pink", Tags: []string{"hot_color"}}, 345 {Id: "orange", Tags: []string{"hot_color"}}, 346 {Id: "yellow", Tags: []string{"hot_color"}}, 347 {Id: "brown", Tags: []string{"hot_color"}}, 348 {Id: "green", Tags: []string{"cool_color"}}, 349 {Id: "blue", Tags: []string{"cool_color"}}, 350 {Id: "purple", Tags: []string{"cool_color"}}, 351 {Id: "black"}, 352 {Id: "white"}, 353 }, 354 }, 355 { 356 Id: "brand", 357 Values: []axisValue{ 358 {Id: "m&ms", Tags: []string{"chocolate"}}, 359 {Id: "skittles", Tags: []string{"chewy"}}, 360 {Id: "necco", Tags: []string{"chalk"}}, 361 }, 362 }, 363 } 364 ase := NewAxisSelectorEvaluator(axes) 365 So(ase, ShouldNotBeNil) 366 Convey("and a valid matrix", func() { 367 m := matrix{ 368 Id: "candy", 369 Spec: matrixDefinition{ 370 "color": []string{ 371 "red", "orange", "yellow", "brown", "green", 372 "blue", "purple", "black", "white", "pink", 373 }, 374 "brand": []string{"m&ms", "skittles", "necco"}, 375 }, 376 Exclude: []matrixDefinition{ 377 {"brand": []string{"skittles"}, "color": []string{"brown", "blue"}}, 378 {"brand": []string{"m&ms"}, "color": []string{"purple"}}, 379 {"brand": []string{"m&ms", "skittles"}, 380 "color": []string{"pink", "black", "white"}}, 381 {"brand": []string{"necco"}, "color": []string{"red", "blue"}}, 382 }, 383 } 384 Convey("building a list of variants should succeed", func() { 385 vs, errs := buildMatrixVariants(axes, ase, []matrix{m}) 386 So(errs, ShouldBeNil) 387 Convey("and return the correct list of combinations", func() { 388 So(len(vs), ShouldEqual, 19) 389 // check a couple random samples 390 d1 := findVariant(vs, "candy__color~yellow_brand~skittles") 391 So(d1.Tags, ShouldContain, "hot_color") 392 So(d1.Tags, ShouldContain, "chewy") 393 d2 := findVariant(vs, "candy__color~black_brand~necco") 394 So(len(d2.Tags), ShouldEqual, 1) 395 So(d2.Tags, ShouldContain, "chalk") 396 // ensure all values are in there... 397 vals := []matrixValue{} 398 for _, v := range vs { 399 vals = append(vals, v.matrixVal) 400 } 401 So(vals, ShouldContainResembling, matrixValue{"brand": "m&ms", "color": "red"}) 402 So(vals, ShouldContainResembling, matrixValue{"brand": "m&ms", "color": "orange"}) 403 So(vals, ShouldContainResembling, matrixValue{"brand": "m&ms", "color": "yellow"}) 404 So(vals, ShouldContainResembling, matrixValue{"brand": "m&ms", "color": "green"}) 405 So(vals, ShouldContainResembling, matrixValue{"brand": "m&ms", "color": "blue"}) 406 So(vals, ShouldContainResembling, matrixValue{"brand": "m&ms", "color": "brown"}) 407 So(vals, ShouldContainResembling, matrixValue{"brand": "skittles", "color": "red"}) 408 So(vals, ShouldContainResembling, matrixValue{"brand": "skittles", "color": "orange"}) 409 So(vals, ShouldContainResembling, matrixValue{"brand": "skittles", "color": "yellow"}) 410 So(vals, ShouldContainResembling, matrixValue{"brand": "skittles", "color": "green"}) 411 So(vals, ShouldContainResembling, matrixValue{"brand": "skittles", "color": "purple"}) 412 So(vals, ShouldContainResembling, matrixValue{"brand": "necco", "color": "orange"}) 413 So(vals, ShouldContainResembling, matrixValue{"brand": "necco", "color": "yellow"}) 414 So(vals, ShouldContainResembling, matrixValue{"brand": "necco", "color": "green"}) 415 So(vals, ShouldContainResembling, matrixValue{"brand": "necco", "color": "purple"}) 416 So(vals, ShouldContainResembling, matrixValue{"brand": "necco", "color": "pink"}) 417 So(vals, ShouldContainResembling, matrixValue{"brand": "necco", "color": "white"}) 418 So(vals, ShouldContainResembling, matrixValue{"brand": "necco", "color": "black"}) 419 }) 420 }) 421 }) 422 Convey("and a valid matrix using tag selectors", func() { 423 m := matrix{ 424 Id: "candy", 425 Spec: matrixDefinition{ 426 "color": []string{".hot_color", ".cool_color"}, // all but white and black 427 "brand": []string{"*"}, 428 }, 429 Exclude: []matrixDefinition{ 430 {"brand": []string{".chewy"}, "color": []string{"brown", "blue"}}, 431 {"brand": []string{".chocolate"}, "color": []string{"purple"}}, 432 {"brand": []string{"!.chewy", "skittles"}, "color": []string{"pink"}}, 433 {"brand": []string{"!skittles !m&ms"}, "color": []string{"red", "blue"}}, 434 }, 435 } 436 Convey("building a list of variations should succeed", func() { 437 vs, errs := buildMatrixVariants(axes, ase, []matrix{m}) 438 So(errs, ShouldBeNil) 439 Convey("and return the correct list of combinations", func() { 440 // ensure all values are in there... 441 So(len(vs), ShouldEqual, 16) 442 vals := []matrixValue{} 443 for _, d := range vs { 444 vals = append(vals, d.matrixVal) 445 } 446 So(vals, ShouldContainResembling, matrixValue{"brand": "m&ms", "color": "red"}) 447 So(vals, ShouldContainResembling, matrixValue{"brand": "m&ms", "color": "orange"}) 448 So(vals, ShouldContainResembling, matrixValue{"brand": "m&ms", "color": "yellow"}) 449 So(vals, ShouldContainResembling, matrixValue{"brand": "m&ms", "color": "green"}) 450 So(vals, ShouldContainResembling, matrixValue{"brand": "m&ms", "color": "blue"}) 451 So(vals, ShouldContainResembling, matrixValue{"brand": "m&ms", "color": "brown"}) 452 So(vals, ShouldContainResembling, matrixValue{"brand": "skittles", "color": "red"}) 453 So(vals, ShouldContainResembling, matrixValue{"brand": "skittles", "color": "orange"}) 454 So(vals, ShouldContainResembling, matrixValue{"brand": "skittles", "color": "yellow"}) 455 So(vals, ShouldContainResembling, matrixValue{"brand": "skittles", "color": "green"}) 456 So(vals, ShouldContainResembling, matrixValue{"brand": "skittles", "color": "purple"}) 457 So(vals, ShouldContainResembling, matrixValue{"brand": "necco", "color": "orange"}) 458 So(vals, ShouldContainResembling, matrixValue{"brand": "necco", "color": "yellow"}) 459 So(vals, ShouldContainResembling, matrixValue{"brand": "necco", "color": "green"}) 460 So(vals, ShouldContainResembling, matrixValue{"brand": "necco", "color": "purple"}) 461 }) 462 }) 463 }) 464 Convey("and a matrix that uses wrong axes", func() { 465 m := matrix{ 466 Id: "candy", 467 Spec: matrixDefinition{ 468 "strength": []string{"weak", "middle", "big-n-tough"}, 469 }, 470 } 471 Convey("should fail to build", func() { 472 vs, errs := buildMatrixVariants(axes, ase, []matrix{m}) 473 So(len(vs), ShouldEqual, 0) 474 So(len(errs), ShouldEqual, 3) 475 }) 476 }) 477 Convey("and a matrix that uses wrong axis values", func() { 478 m := matrix{ 479 Id: "candy", 480 Spec: matrixDefinition{ 481 "color": []string{"salmon", "infrared"}, 482 }, 483 } 484 Convey("should fail to build", func() { 485 vs, errs := buildMatrixVariants(axes, ase, []matrix{m}) 486 So(len(vs), ShouldEqual, 0) 487 So(len(errs), ShouldEqual, 2) 488 }) 489 }) 490 }) 491 } 492 493 func TestMergeAxisValue(t *testing.T) { 494 Convey("With a parserBV", t, func() { 495 pbv := parserBV{ 496 RunOn: []string{"basic_distro"}, 497 Modules: []string{"basic_module"}, 498 Tags: []string{"basic"}, 499 BatchTime: nil, 500 Stepback: nil, 501 Expansions: map[string]string{ 502 "v1": "test", 503 }, 504 } 505 Convey("a valid axis value should merge successfully", func() { 506 av := axisValue{ 507 RunOn: []string{"special_distro"}, 508 Modules: []string{"module++"}, 509 Tags: []string{"enterprise"}, 510 BatchTime: new(int), 511 Stepback: new(bool), 512 Variables: map[string]string{ 513 "v2": "new", 514 }, 515 } 516 So(pbv.mergeAxisValue(av), ShouldBeNil) 517 So(pbv.RunOn, ShouldResemble, av.RunOn) 518 So(pbv.Modules, ShouldResemble, av.Modules) 519 So(pbv.Tags, ShouldContain, "basic") 520 So(pbv.Tags, ShouldContain, "enterprise") 521 So(pbv.Stepback, ShouldNotBeNil) 522 So(pbv.BatchTime, ShouldNotBeNil) 523 So(pbv.Expansions, ShouldResemble, command.Expansions{ 524 "v1": "test", 525 "v2": "new", 526 }) 527 }) 528 Convey("a valid axis value full of expansions should merge successfully", func() { 529 av := axisValue{ 530 RunOn: []string{"${v1}", "${v2}"}, 531 Modules: []string{"${v1}__"}, 532 Tags: []string{"fat${v2}"}, 533 Variables: map[string]string{ 534 "v2": "${v1}!", 535 }, 536 } 537 So(pbv.mergeAxisValue(av), ShouldBeNil) 538 So(pbv.RunOn, ShouldResemble, parserStringSlice{"test", "test!"}) 539 So(pbv.Modules, ShouldResemble, parserStringSlice{"test__"}) 540 So(pbv.Tags, ShouldContain, "basic") 541 So(pbv.Tags, ShouldContain, "fattest!") 542 So(pbv.Expansions, ShouldResemble, command.Expansions{ 543 "v1": "test", 544 "v2": "test!", 545 }) 546 }) 547 Convey("an axis value with a bad tag expansion should fail", func() { 548 av := axisValue{ 549 Tags: []string{"fat${"}, 550 } 551 So(pbv.mergeAxisValue(av), ShouldNotBeNil) 552 }) 553 Convey("an axis value with a bad variables expansion should fail", func() { 554 av := axisValue{ 555 Variables: map[string]string{ 556 "v2": "${sdsad", 557 }, 558 } 559 So(pbv.mergeAxisValue(av), ShouldNotBeNil) 560 }) 561 }) 562 } 563 564 func TestRulesEvaluation(t *testing.T) { 565 Convey("With a series of test parserBVs and tasks", t, func() { 566 taskDefs := []parserTask{ 567 {Name: "red", Tags: []string{"primary", "warm"}}, 568 {Name: "orange", Tags: []string{"secondary", "warm"}}, 569 {Name: "yellow", Tags: []string{"primary", "warm"}}, 570 {Name: "green", Tags: []string{"secondary", "cool"}}, 571 {Name: "blue", Tags: []string{"primary", "cool"}}, 572 {Name: "purple", Tags: []string{"secondary", "cool"}}, 573 {Name: "brown", Tags: []string{"tertiary"}}, 574 {Name: "black", Tags: []string{"special"}}, 575 {Name: "white", Tags: []string{"special"}}, 576 } 577 tse := NewParserTaskSelectorEvaluator(taskDefs) 578 Convey("a variant with a 'remove' rule should remove the given tasks", func() { 579 bvs := []parserBV{{ 580 Name: "test", 581 Tasks: parserBVTasks{ 582 {Name: "blue"}, 583 {Name: ".special"}, 584 {Name: ".tertiary"}, 585 }, 586 matrixRules: []ruleAction{ 587 {RemoveTasks: []string{".primary !.warm"}}, //remove blue 588 {RemoveTasks: []string{"brown"}}, 589 }, 590 }} 591 evaluated, errs := evaluateBuildVariants(tse, nil, bvs) 592 So(errs, ShouldBeNil) 593 v1 := evaluated[0] 594 So(v1.Name, ShouldEqual, "test") 595 So(len(v1.Tasks), ShouldEqual, 2) 596 }) 597 Convey("a variant with an 'add' rule should add the given tasks", func() { 598 bvs := []parserBV{{ 599 Name: "test", 600 Tasks: parserBVTasks{ 601 {Name: ".special"}, 602 }, 603 matrixRules: []ruleAction{ 604 {AddTasks: []parserBVTask{{Name: ".primary"}}}, 605 {AddTasks: []parserBVTask{{Name: ".warm"}}}, 606 {AddTasks: []parserBVTask{{Name: "green", DependsOn: []parserDependency{{ 607 taskSelector: taskSelector{Name: ".warm"}, 608 }}}}}, 609 }, 610 }} 611 evaluated, errs := evaluateBuildVariants(tse, nil, bvs) 612 So(errs, ShouldBeNil) 613 v1 := evaluated[0] 614 So(v1.Name, ShouldEqual, "test") 615 So(len(v1.Tasks), ShouldEqual, 7) 616 }) 617 Convey("a series of add and remove rules should execute in order", func() { 618 bvs := []parserBV{{ 619 Name: "test", 620 Tasks: parserBVTasks{ 621 {Name: ".secondary"}, 622 }, 623 matrixRules: []ruleAction{ 624 {AddTasks: []parserBVTask{{Name: ".primary"}}}, 625 {RemoveTasks: []string{".secondary"}}, 626 {AddTasks: []parserBVTask{{Name: ".warm"}}}, 627 {RemoveTasks: []string{"orange"}}, 628 {AddTasks: []parserBVTask{{Name: "orange", DependsOn: []parserDependency{{ 629 taskSelector: taskSelector{Name: ".warm"}, 630 }}}}}, 631 }, 632 }} 633 evaluated, errs := evaluateBuildVariants(tse, nil, bvs) 634 So(errs, ShouldBeNil) 635 v1 := evaluated[0] 636 So(v1.Name, ShouldEqual, "test") 637 So(len(v1.Tasks), ShouldEqual, 4) 638 }) 639 Convey("conflicting added tasks should fail", func() { 640 bvs := []parserBV{{ 641 // case where conflicts take place against existing tasks 642 Name: "test1", 643 Tasks: parserBVTasks{ 644 {Name: ".warm"}, 645 }, 646 matrixRules: []ruleAction{ 647 {AddTasks: []parserBVTask{{Name: "orange", DependsOn: []parserDependency{{ 648 taskSelector: taskSelector{Name: ".warm"}, 649 }}}}}, 650 }, 651 }, { 652 // case where conflicts are within the same rule 653 Name: "test2", 654 Tasks: parserBVTasks{}, 655 matrixRules: []ruleAction{ 656 {AddTasks: []parserBVTask{{Name: ".warm"}, {Name: "orange", DependsOn: []parserDependency{{ 657 taskSelector: taskSelector{Name: ".warm"}, 658 }}}}}, 659 }, 660 }} 661 _, errs := evaluateBuildVariants(tse, nil, bvs) 662 So(errs, ShouldNotBeNil) 663 So(len(errs), ShouldEqual, 3) 664 }) 665 Convey("a 'remove' rule for an unknown task should fail", func() { 666 bvs := []parserBV{{ 667 Name: "test", 668 Tasks: parserBVTasks{ 669 {Name: "blue"}, 670 {Name: ".special"}, 671 {Name: ".tertiary"}, 672 }, 673 matrixRules: []ruleAction{ 674 {RemoveTasks: []string{".amazing"}}, //remove blue 675 {RemoveTasks: []string{"rainbow"}}, 676 }, 677 }} 678 _, errs := evaluateBuildVariants(tse, nil, bvs) 679 So(errs, ShouldNotBeNil) 680 So(len(errs), ShouldEqual, 2) 681 }) 682 }) 683 }