github.com/bazelbuild/rules_webtesting@v0.2.0/go/metadata/capabilities/capabilities_test.go (about) 1 // Copyright 2016 Google Inc. 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 15 package capabilities 16 17 import ( 18 "fmt" 19 "reflect" 20 "testing" 21 ) 22 23 func TestMerge(t *testing.T) { 24 testCases := []struct { 25 name string 26 input1 map[string]interface{} 27 input2 map[string]interface{} 28 result map[string]interface{} 29 }{ 30 { 31 name: "int,int", 32 input1: map[string]interface{}{"v": 1}, 33 input2: map[string]interface{}{"v": 2}, 34 result: map[string]interface{}{"v": 2}, 35 }, 36 { 37 name: "string,bool", 38 input1: map[string]interface{}{"v": "a string"}, 39 input2: map[string]interface{}{"v": true}, 40 result: map[string]interface{}{"v": true}, 41 }, 42 { 43 name: "int,string", 44 input1: map[string]interface{}{"v": 1}, 45 input2: map[string]interface{}{"v": "a string"}, 46 result: map[string]interface{}{"v": "a string"}, 47 }, 48 { 49 name: "int,slice", 50 input1: map[string]interface{}{"v": 1}, 51 input2: map[string]interface{}{"v": []interface{}{"string 1", 2, true, nil}}, 52 result: map[string]interface{}{"v": []interface{}{"string 1", 2, true, nil}}, 53 }, 54 { 55 name: "slice,slice", 56 input1: map[string]interface{}{"v": []interface{}{"string 1", "string 2"}}, 57 input2: map[string]interface{}{"v": []interface{}{1, 2}}, 58 result: map[string]interface{}{"v": []interface{}{"string 1", "string 2", 1, 2}}, 59 }, 60 { 61 name: "int,map", 62 input1: map[string]interface{}{"v": 1}, 63 input2: map[string]interface{}{"v": map[string]interface{}{ 64 "1": 1, 65 "2": 2, 66 }}, 67 result: map[string]interface{}{"v": map[string]interface{}{ 68 "1": 1, 69 "2": 2, 70 }}, 71 }, 72 { 73 name: "map,map", 74 input1: map[string]interface{}{"v": map[string]interface{}{ 75 "1": 1, 76 "2": 2, 77 }}, 78 input2: map[string]interface{}{"v": map[string]interface{}{ 79 "2": 3, 80 "3": 4, 81 }}, 82 result: map[string]interface{}{"v": map[string]interface{}{"1": 1, "2": 3, "3": 4}}, 83 }, 84 { 85 name: "mixed", 86 input1: map[string]interface{}{ 87 "1": []interface{}{1, 2}, 88 "2": map[string]interface{}{"a": "an a", "b": "a b", "c": "a c"}, 89 "3": 3, 90 }, 91 input2: map[string]interface{}{ 92 "1": []interface{}{"a"}, 93 "2": map[string]interface{}{"a": "a c", "b": "a d"}, 94 }, 95 result: map[string]interface{}{ 96 "1": []interface{}{1, 2, "a"}, 97 "2": map[string]interface{}{"a": "a c", "b": "a d", "c": "a c"}, 98 "3": 3, 99 }, 100 }, 101 { 102 name: "args -- no redefines", 103 input1: map[string]interface{}{ 104 "args": []interface{}{ 105 "an option", 106 "--anOption", 107 "--anOption=true", 108 "-anotherOption", 109 map[string]interface{}{ 110 "some": "map", 111 }, 112 }, 113 }, 114 input2: map[string]interface{}{ 115 "args": []interface{}{ 116 "an option", 117 "anOption", 118 "-anOption=true", 119 "-anotherOption", 120 map[string]interface{}{ 121 "some": "map", 122 }, 123 }, 124 }, 125 result: map[string]interface{}{ 126 "args": []interface{}{ 127 "an option", 128 "--anOption", 129 "--anOption=true", 130 "-anotherOption", 131 map[string]interface{}{ 132 "some": "map", 133 }, 134 "an option", 135 "anOption", 136 "-anOption=true", 137 "-anotherOption", 138 map[string]interface{}{ 139 "some": "map", 140 }, 141 }, 142 }, 143 }, 144 { 145 name: "args -- redefines", 146 input1: map[string]interface{}{ 147 "args": []interface{}{ 148 "an option", 149 "--anOption", 150 "--anOption=true", 151 "--optionToLeave=this", 152 map[string]interface{}{ 153 "some": "map", 154 }, 155 }, 156 }, 157 input2: map[string]interface{}{ 158 "args": []interface{}{ 159 "an option", 160 "--anOption=false", 161 "--anotherOption", 162 "-optionToLeave=that", 163 map[string]interface{}{ 164 "some": "map", 165 }, 166 }, 167 }, 168 result: map[string]interface{}{ 169 "args": []interface{}{ 170 "an option", 171 "--optionToLeave=this", 172 map[string]interface{}{ 173 "some": "map", 174 }, 175 "an option", 176 "--anOption=false", 177 "--anotherOption", 178 "-optionToLeave=that", 179 map[string]interface{}{ 180 "some": "map", 181 }, 182 }, 183 }, 184 }, 185 } 186 187 for _, tc := range testCases { 188 t.Run(tc.name, func(t *testing.T) { 189 if result := Merge(tc.input1, tc.input2); !reflect.DeepEqual(tc.result, result) { 190 t.Errorf("Got Merge(%+v, %+v) == %+v, expected %+v", tc.input1, tc.input2, result, tc.result) 191 } 192 }) 193 } 194 } 195 196 func TestFromNewSessionArgs(t *testing.T) { 197 testCases := []struct { 198 name string 199 args map[string]interface{} 200 want *Capabilities 201 wantErr bool 202 }{ 203 { 204 name: "empty args", 205 args: map[string]interface{}{}, 206 want: &Capabilities{ 207 AlwaysMatch: map[string]interface{}{}, 208 W3CSupported: false, 209 }, 210 wantErr: false, 211 }, 212 { 213 name: "alwaysMatch", 214 args: map[string]interface{}{ 215 "capabilities": map[string]interface{}{ 216 "alwaysMatch": map[string]interface{}{ 217 "key1": "value1", 218 }, 219 }, 220 }, 221 want: &Capabilities{ 222 AlwaysMatch: map[string]interface{}{ 223 "key1": "value1", 224 }, 225 W3CSupported: true, 226 }, 227 wantErr: false, 228 }, 229 { 230 name: "requiredCapabilities", 231 args: map[string]interface{}{ 232 "requiredCapabilities": map[string]interface{}{ 233 "key1": "value1", 234 }, 235 }, 236 want: &Capabilities{ 237 AlwaysMatch: map[string]interface{}{ 238 "key1": "value1", 239 }, 240 W3CSupported: false, 241 }, 242 wantErr: false, 243 }, 244 { 245 name: "desiredCapabilities", 246 args: map[string]interface{}{ 247 "desiredCapabilities": map[string]interface{}{ 248 "key1": "value1", 249 }, 250 }, 251 want: &Capabilities{ 252 AlwaysMatch: map[string]interface{}{ 253 "key1": "value1", 254 }, 255 W3CSupported: false, 256 }, 257 wantErr: false, 258 }, 259 { 260 name: "all three", 261 args: map[string]interface{}{ 262 "capabilities": map[string]interface{}{ 263 "alwaysMatch": map[string]interface{}{ 264 "key1": "value1", 265 }, 266 }, 267 "desiredCapabilities": map[string]interface{}{ 268 "key2": "value2", 269 }, 270 "requiredCapabilities": map[string]interface{}{ 271 "key3": "value3", 272 }, 273 }, 274 want: &Capabilities{ 275 AlwaysMatch: map[string]interface{}{ 276 "key1": "value1", 277 "key2": "value2", 278 "key3": "value3", 279 }, 280 W3CSupported: true, 281 }, 282 wantErr: false, 283 }, 284 { 285 name: "all three, same value ok", 286 args: map[string]interface{}{ 287 "capabilities": map[string]interface{}{ 288 "alwaysMatch": map[string]interface{}{ 289 "key1": "value1", 290 }, 291 }, 292 "desiredCapabilities": map[string]interface{}{ 293 "key1": "value1", 294 }, 295 "requiredCapabilities": map[string]interface{}{ 296 "key1": "value1", 297 }, 298 }, 299 want: &Capabilities{ 300 AlwaysMatch: map[string]interface{}{ 301 "key1": "value1", 302 }, 303 W3CSupported: true, 304 }, 305 wantErr: false, 306 }, 307 { 308 name: "always, required != desired", 309 args: map[string]interface{}{ 310 "capabilities": map[string]interface{}{ 311 "alwaysMatch": map[string]interface{}{ 312 "key1": "value1", 313 }, 314 }, 315 "desiredCapabilities": map[string]interface{}{ 316 "key1": "value12", 317 }, 318 "requiredCapabilities": map[string]interface{}{ 319 "key1": "value1", 320 }, 321 }, 322 want: nil, 323 wantErr: true, 324 }, 325 { 326 name: "always, desired != required", 327 args: map[string]interface{}{ 328 "capabilities": map[string]interface{}{ 329 "alwaysMatch": map[string]interface{}{ 330 "key1": "value1", 331 }, 332 }, 333 "desiredCapabilities": map[string]interface{}{ 334 "key1": "value1", 335 }, 336 "requiredCapabilities": map[string]interface{}{ 337 "key1": "value12", 338 }, 339 }, 340 want: nil, 341 wantErr: true, 342 }, 343 { 344 name: "always != desired. required", 345 args: map[string]interface{}{ 346 "capabilities": map[string]interface{}{ 347 "alwaysMatch": map[string]interface{}{ 348 "key1": "value12", 349 }, 350 }, 351 "desiredCapabilities": map[string]interface{}{ 352 "key1": "value1", 353 }, 354 "requiredCapabilities": map[string]interface{}{ 355 "key1": "value1", 356 }, 357 }, 358 want: nil, 359 wantErr: true, 360 }, 361 { 362 name: "firstMatch, no conflicts", 363 args: map[string]interface{}{ 364 "capabilities": map[string]interface{}{ 365 "alwaysMatch": map[string]interface{}{ 366 "key1": "value1", 367 }, 368 "firstMatch": []interface{}{ 369 map[string]interface{}{ 370 "key2": "value2", 371 }, 372 map[string]interface{}{ 373 "key2": "value3", 374 }, 375 }, 376 }, 377 }, 378 want: &Capabilities{ 379 AlwaysMatch: map[string]interface{}{ 380 "key1": "value1", 381 }, 382 FirstMatch: []map[string]interface{}{ 383 { 384 "key2": "value2", 385 }, 386 { 387 "key2": "value3", 388 }, 389 }, 390 W3CSupported: true, 391 }, 392 wantErr: false, 393 }, 394 { 395 name: "firstMatch, same value as alwaysMatch", 396 args: map[string]interface{}{ 397 "capabilities": map[string]interface{}{ 398 "alwaysMatch": map[string]interface{}{ 399 "key1": "value1", 400 }, 401 "firstMatch": []interface{}{ 402 map[string]interface{}{ 403 "key1": "value1", 404 "key2": "value2", 405 }, 406 map[string]interface{}{ 407 "key2": "value3", 408 }, 409 }, 410 }, 411 }, 412 want: &Capabilities{ 413 AlwaysMatch: map[string]interface{}{ 414 "key1": "value1", 415 }, 416 FirstMatch: []map[string]interface{}{ 417 { 418 "key2": "value2", 419 }, 420 { 421 "key2": "value3", 422 }, 423 }, 424 W3CSupported: true, 425 }, 426 wantErr: false, 427 }, 428 { 429 name: "firstMatch, different value than alwaysMatch", 430 args: map[string]interface{}{ 431 "capabilities": map[string]interface{}{ 432 "alwaysMatch": map[string]interface{}{ 433 "key1": "value1", 434 }, 435 "firstMatch": []interface{}{ 436 map[string]interface{}{ 437 "key1": "value12", 438 "key2": "value2", 439 }, 440 map[string]interface{}{ 441 "key2": "value3", 442 }, 443 }, 444 }, 445 }, 446 want: nil, 447 wantErr: true, 448 }, 449 } 450 451 for _, tc := range testCases { 452 t.Run(tc.name, func(t *testing.T) { 453 got, err := FromNewSessionArgs(tc.args) 454 455 if err != nil || tc.wantErr { 456 if (err != nil) != tc.wantErr { 457 t.Fatalf("got err %v, wantErr==%t", err, tc.wantErr) 458 } 459 return 460 } 461 462 if !reflect.DeepEqual(got, tc.want) { 463 t.Fatalf("got %#v, want %#v", got, tc.want) 464 } 465 }) 466 } 467 } 468 469 func TestMergeOver(t *testing.T) { 470 testCases := []struct { 471 name string 472 this *Capabilities 473 other map[string]interface{} 474 want *Capabilities 475 }{ 476 { 477 name: "empty", 478 this: &Capabilities{ 479 AlwaysMatch: map[string]interface{}{}, 480 }, 481 other: map[string]interface{}{}, 482 want: &Capabilities{ 483 AlwaysMatch: map[string]interface{}{}, 484 }, 485 }, 486 { 487 name: "no overlap", 488 this: &Capabilities{ 489 AlwaysMatch: map[string]interface{}{ 490 "key1": "value1", 491 }, 492 FirstMatch: []map[string]interface{}{ 493 { 494 "key2": "value2", 495 }, 496 { 497 "key3": "value3", 498 }, 499 }, 500 }, 501 other: map[string]interface{}{ 502 "key4": "value4", 503 }, 504 want: &Capabilities{ 505 AlwaysMatch: map[string]interface{}{ 506 "key1": "value1", 507 "key4": "value4", 508 }, 509 FirstMatch: []map[string]interface{}{ 510 { 511 "key2": "value2", 512 }, 513 { 514 "key3": "value3", 515 }, 516 }, 517 }, 518 }, 519 { 520 name: "overlaps always", 521 this: &Capabilities{ 522 AlwaysMatch: map[string]interface{}{ 523 "key1": "value1", 524 }, 525 FirstMatch: []map[string]interface{}{ 526 { 527 "key2": "value2", 528 }, 529 { 530 "key3": "value3", 531 }, 532 }, 533 }, 534 other: map[string]interface{}{ 535 "key1": "value4", 536 }, 537 want: &Capabilities{ 538 AlwaysMatch: map[string]interface{}{ 539 "key1": "value1", 540 }, 541 FirstMatch: []map[string]interface{}{ 542 { 543 "key2": "value2", 544 }, 545 { 546 "key3": "value3", 547 }, 548 }, 549 }, 550 }, 551 { 552 name: "overlaps first[0]", 553 this: &Capabilities{ 554 AlwaysMatch: map[string]interface{}{ 555 "key1": "value1", 556 }, 557 FirstMatch: []map[string]interface{}{ 558 { 559 "key2": "value2", 560 }, 561 { 562 "key3": "value3", 563 }, 564 }, 565 }, 566 other: map[string]interface{}{ 567 "key2": "value4", 568 }, 569 want: &Capabilities{ 570 AlwaysMatch: map[string]interface{}{ 571 "key1": "value1", 572 }, 573 FirstMatch: []map[string]interface{}{ 574 { 575 "key2": "value2", 576 }, 577 { 578 "key2": "value4", 579 "key3": "value3", 580 }, 581 }, 582 }, 583 }, 584 { 585 name: "overlaps first[1]", 586 this: &Capabilities{ 587 AlwaysMatch: map[string]interface{}{ 588 "key1": "value1", 589 }, 590 FirstMatch: []map[string]interface{}{ 591 { 592 "key2": "value2", 593 }, 594 { 595 "key3": "value3", 596 }, 597 }, 598 }, 599 other: map[string]interface{}{ 600 "key3": "value4", 601 }, 602 want: &Capabilities{ 603 AlwaysMatch: map[string]interface{}{ 604 "key1": "value1", 605 }, 606 FirstMatch: []map[string]interface{}{ 607 { 608 "key2": "value2", 609 "key3": "value4", 610 }, 611 { 612 "key3": "value3", 613 }, 614 }, 615 }, 616 }, 617 { 618 name: "overlap and non-overlap", 619 this: &Capabilities{ 620 AlwaysMatch: map[string]interface{}{ 621 "key1": "value1", 622 }, 623 FirstMatch: []map[string]interface{}{ 624 { 625 "key2": "value2", 626 }, 627 { 628 "key3": "value3", 629 }, 630 }, 631 }, 632 other: map[string]interface{}{ 633 "key1": "value11", 634 "key2": "value22", 635 "key3": "value33", 636 "key4": "value4", 637 }, 638 want: &Capabilities{ 639 AlwaysMatch: map[string]interface{}{ 640 "key1": "value1", 641 "key4": "value4", 642 }, 643 FirstMatch: []map[string]interface{}{ 644 { 645 "key2": "value2", 646 "key3": "value33", 647 }, 648 { 649 "key2": "value22", 650 "key3": "value3", 651 }, 652 }, 653 }, 654 }, 655 } 656 657 for _, tc := range testCases { 658 t.Run(tc.name, func(t *testing.T) { 659 got := tc.this.MergeOver(tc.other) 660 if !reflect.DeepEqual(got, tc.want) { 661 t.Fatalf("got %#v, want %#v", got, tc.want) 662 } 663 }) 664 } 665 } 666 667 func TestResolve(t *testing.T) { 668 testCases := []struct { 669 name string 670 in *Capabilities 671 resolver Resolver 672 out *Capabilities 673 err bool 674 }{ 675 { 676 name: "empty capabilities", 677 in: &Capabilities{ 678 AlwaysMatch: map[string]interface{}{}, 679 }, 680 resolver: func(prefix, name string) (string, error) { 681 return "", fmt.Errorf("resolver called with %s:%s", prefix, name) 682 }, 683 out: &Capabilities{ 684 AlwaysMatch: map[string]interface{}{}, 685 }, 686 err: false, 687 }, 688 { 689 name: "NoOP resolver", 690 in: &Capabilities{ 691 AlwaysMatch: map[string]interface{}{ 692 "abc": "%p:n%", 693 }, 694 FirstMatch: []map[string]interface{}{ 695 { 696 "xyz": "%n:p%", 697 }, 698 }, 699 }, 700 resolver: NoOPResolver, 701 out: &Capabilities{ 702 AlwaysMatch: map[string]interface{}{ 703 "abc": "%p:n%", 704 }, 705 FirstMatch: []map[string]interface{}{ 706 { 707 "xyz": "%n:p%", 708 }, 709 }, 710 }, 711 err: false, 712 }, 713 { 714 name: "MapResolver", 715 in: &Capabilities{ 716 AlwaysMatch: map[string]interface{}{ 717 "abc": "%p:n%", 718 }, 719 FirstMatch: []map[string]interface{}{ 720 { 721 "xyz": "%n:p%", 722 }, 723 }, 724 }, 725 resolver: MapResolver("p", map[string]string{"n": "some value"}), 726 out: &Capabilities{ 727 AlwaysMatch: map[string]interface{}{ 728 "abc": "some value", 729 }, 730 FirstMatch: []map[string]interface{}{ 731 { 732 "xyz": "%n:p%", 733 }, 734 }, 735 }, 736 err: false, 737 }, 738 { 739 name: "complex input", 740 in: &Capabilities{ 741 AlwaysMatch: map[string]interface{}{ 742 "abc": []interface{}{"%p:n%"}, 743 }, 744 FirstMatch: []map[string]interface{}{ 745 { 746 "xyz": map[string]interface{}{"zyx": "%n:p%=%p:n%"}, 747 }, 748 }, 749 }, 750 resolver: func(prefix, name string) (string, error) { 751 if prefix == "p" && name == "n" { 752 return "some-value", nil 753 } 754 if prefix == "n" && name == "p" { 755 return "value-some", nil 756 } 757 return "", fmt.Errorf("unknown %s:%s", prefix, name) 758 }, 759 out: &Capabilities{ 760 AlwaysMatch: map[string]interface{}{ 761 "abc": []interface{}{"some-value"}, 762 }, 763 FirstMatch: []map[string]interface{}{ 764 { 765 "xyz": map[string]interface{}{"zyx": "value-some=some-value"}, 766 }, 767 }, 768 }, 769 err: false, 770 }, 771 { 772 name: "resolver returns error", 773 in: &Capabilities{ 774 AlwaysMatch: map[string]interface{}{ 775 "abc": []interface{}{"%p:n%"}, 776 }, 777 FirstMatch: []map[string]interface{}{ 778 { 779 "xyz": map[string]interface{}{"zyx": "%n:p%=%p:n%"}, 780 }, 781 { 782 "bad": "%x:y%", 783 }, 784 }, 785 }, 786 resolver: func(prefix, name string) (string, error) { 787 if prefix == "p" && name == "n" { 788 return "some-value", nil 789 } 790 if prefix == "n" && name == "p" { 791 return "value-some", nil 792 } 793 return "", fmt.Errorf("unknown %s:%s", prefix, name) 794 }, 795 out: nil, 796 err: true, 797 }, 798 } 799 800 for _, tc := range testCases { 801 t.Run(tc.name, func(t *testing.T) { 802 out, err := tc.in.Resolve(tc.resolver) 803 804 if err != nil { 805 if !tc.err { 806 t.Fatal(err) 807 } 808 return 809 } 810 if tc.err { 811 t.Fatalf("got nil err, want err") 812 } 813 814 if !reflect.DeepEqual(out, tc.out) { 815 t.Fatalf("got %#v, want %#v", out, tc.out) 816 } 817 }) 818 } 819 }