1// Copyright 2018 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package modfile 6 7import ( 8 "bytes" 9 "testing" 10 11 "golang.org/x/mod/module" 12) 13 14var addRequireTests = []struct { 15 desc string 16 in string 17 path string 18 vers string 19 out string 20}{ 21 { 22 `existing`, 23 ` 24 module m 25 require x.y/z v1.2.3 26 `, 27 "x.y/z", "v1.5.6", 28 ` 29 module m 30 require x.y/z v1.5.6 31 `, 32 }, 33 { 34 `new`, 35 ` 36 module m 37 require x.y/z v1.2.3 38 `, 39 "x.y/w", "v1.5.6", 40 ` 41 module m 42 require ( 43 x.y/z v1.2.3 44 x.y/w v1.5.6 45 ) 46 `, 47 }, 48 { 49 `new2`, 50 ` 51 module m 52 require x.y/z v1.2.3 53 require x.y/q/v2 v2.3.4 54 `, 55 "x.y/w", "v1.5.6", 56 ` 57 module m 58 require x.y/z v1.2.3 59 require ( 60 x.y/q/v2 v2.3.4 61 x.y/w v1.5.6 62 ) 63 `, 64 }, 65} 66 67var setRequireTests = []struct { 68 desc string 69 in string 70 mods []struct { 71 path string 72 vers string 73 indirect bool 74 } 75 out string 76}{ 77 { 78 `existing`, 79 `module m 80 require ( 81 x.y/b v1.2.3 82 83 x.y/a v1.2.3 84 x.y/d v1.2.3 85 ) 86 `, 87 []struct { 88 path string 89 vers string 90 indirect bool 91 }{ 92 {"x.y/a", "v1.2.3", false}, 93 {"x.y/b", "v1.2.3", false}, 94 {"x.y/c", "v1.2.3", false}, 95 }, 96 `module m 97 require ( 98 x.y/a v1.2.3 99 x.y/b v1.2.3 100 x.y/c v1.2.3 101 ) 102 `, 103 }, 104 { 105 `existing_indirect`, 106 `module m 107 require ( 108 x.y/a v1.2.3 109 x.y/b v1.2.3 // 110 x.y/c v1.2.3 //c 111 x.y/d v1.2.3 // c 112 x.y/e v1.2.3 // indirect 113 x.y/f v1.2.3 //indirect 114 x.y/g v1.2.3 // indirect 115 ) 116 `, 117 []struct { 118 path string 119 vers string 120 indirect bool 121 }{ 122 {"x.y/a", "v1.2.3", true}, 123 {"x.y/b", "v1.2.3", true}, 124 {"x.y/c", "v1.2.3", true}, 125 {"x.y/d", "v1.2.3", true}, 126 {"x.y/e", "v1.2.3", true}, 127 {"x.y/f", "v1.2.3", true}, 128 {"x.y/g", "v1.2.3", true}, 129 }, 130 `module m 131 require ( 132 x.y/a v1.2.3 // indirect 133 x.y/b v1.2.3 // indirect 134 x.y/c v1.2.3 // indirect; c 135 x.y/d v1.2.3 // indirect; c 136 x.y/e v1.2.3 // indirect 137 x.y/f v1.2.3 //indirect 138 x.y/g v1.2.3 // indirect 139 ) 140 `, 141 }, 142} 143 144var addGoTests = []struct { 145 desc string 146 in string 147 version string 148 out string 149}{ 150 { 151 `module_only`, 152 `module m 153 `, 154 `1.14`, 155 `module m 156 go 1.14 157 `, 158 }, 159 { 160 `module_before_require`, 161 `module m 162 require x.y/a v1.2.3 163 `, 164 `1.14`, 165 `module m 166 go 1.14 167 require x.y/a v1.2.3 168 `, 169 }, 170 { 171 `require_before_module`, 172 `require x.y/a v1.2.3 173 module example.com/inverted 174 `, 175 `1.14`, 176 `require x.y/a v1.2.3 177 module example.com/inverted 178 go 1.14 179 `, 180 }, 181 { 182 `require_only`, 183 `require x.y/a v1.2.3 184 `, 185 `1.14`, 186 `require x.y/a v1.2.3 187 go 1.14 188 `, 189 }, 190} 191 192var addRetractTests = []struct { 193 desc string 194 in string 195 low string 196 high string 197 rationale string 198 out string 199}{ 200 { 201 `new_singleton`, 202 `module m 203 `, 204 `v1.2.3`, 205 `v1.2.3`, 206 ``, 207 `module m 208 retract v1.2.3 209 `, 210 }, 211 { 212 `new_interval`, 213 `module m 214 `, 215 `v1.0.0`, 216 `v1.1.0`, 217 ``, 218 `module m 219 retract [v1.0.0, v1.1.0]`, 220 }, 221 { 222 `duplicate_with_rationale`, 223 `module m 224 retract v1.2.3 225 `, 226 `v1.2.3`, 227 `v1.2.3`, 228 `bad`, 229 `module m 230 retract ( 231 v1.2.3 232 // bad 233 v1.2.3 234 ) 235 `, 236 }, 237 { 238 `duplicate_multiline_rationale`, 239 `module m 240 retract [v1.2.3, v1.2.3] 241 `, 242 `v1.2.3`, 243 `v1.2.3`, 244 `multi 245line`, 246 `module m 247 retract ( 248 [v1.2.3, v1.2.3] 249 // multi 250 // line 251 v1.2.3 252 ) 253 `, 254 }, 255 { 256 `duplicate_interval`, 257 `module m 258 retract [v1.0.0, v1.1.0] 259 `, 260 `v1.0.0`, 261 `v1.1.0`, 262 ``, 263 `module m 264 retract ( 265 [v1.0.0, v1.1.0] 266 [v1.0.0, v1.1.0] 267 ) 268 `, 269 }, 270 { 271 `duplicate_singleton`, 272 `module m 273 retract v1.2.3 274 `, 275 `v1.2.3`, 276 `v1.2.3`, 277 ``, 278 `module m 279 retract ( 280 v1.2.3 281 v1.2.3 282 ) 283 `, 284 }, 285} 286 287var dropRetractTests = []struct { 288 desc string 289 in string 290 low string 291 high string 292 out string 293}{ 294 { 295 `singleton_no_match`, 296 `module m 297 retract v1.2.3 298 `, 299 `v1.0.0`, 300 `v1.0.0`, 301 `module m 302 retract v1.2.3 303 `, 304 }, 305 { 306 `singleton_match_one`, 307 `module m 308 retract v1.2.2 309 retract v1.2.3 310 retract v1.2.4 311 `, 312 `v1.2.3`, 313 `v1.2.3`, 314 `module m 315 retract v1.2.2 316 retract v1.2.4 317 `, 318 }, 319 { 320 `singleton_match_all`, 321 `module m 322 retract v1.2.3 // first 323 retract v1.2.3 // second 324 `, 325 `v1.2.3`, 326 `v1.2.3`, 327 `module m 328 `, 329 }, 330 { 331 `interval_match`, 332 `module m 333 retract [v1.2.3, v1.2.3] 334 `, 335 `v1.2.3`, 336 `v1.2.3`, 337 `module m 338 `, 339 }, 340 { 341 `interval_superset_no_match`, 342 `module m 343 retract [v1.0.0, v1.1.0] 344 `, 345 `v1.0.0`, 346 `v1.2.0`, 347 `module m 348 retract [v1.0.0, v1.1.0] 349 `, 350 }, 351 { 352 `singleton_match_middle`, 353 `module m 354 retract v1.2.3 355 `, 356 `v1.2.3`, 357 `v1.2.3`, 358 `module m 359 `, 360 }, 361 { 362 `interval_match_middle_block`, 363 `module m 364 retract ( 365 v1.0.0 366 [v1.1.0, v1.2.0] 367 v1.3.0 368 ) 369 `, 370 `v1.1.0`, 371 `v1.2.0`, 372 `module m 373 retract ( 374 v1.0.0 375 v1.3.0 376 ) 377 `, 378 }, 379 { 380 `interval_match_all`, 381 `module m 382 retract [v1.0.0, v1.1.0] 383 retract [v1.0.0, v1.1.0] 384 `, 385 `v1.0.0`, 386 `v1.1.0`, 387 `module m 388 `, 389 }, 390} 391 392var retractRationaleTests = []struct { 393 desc, in, want string 394}{ 395 { 396 `no_comment`, 397 `module m 398 retract v1.0.0`, 399 ``, 400 }, 401 { 402 `prefix_one`, 403 `module m 404 // prefix 405 retract v1.0.0 406 `, 407 `prefix`, 408 }, 409 { 410 `prefix_multiline`, 411 `module m 412 // one 413 // 414 // two 415 // 416 // three 417 retract v1.0.0`, 418 `one 419 420two 421 422three`, 423 }, 424 { 425 `suffix`, 426 `module m 427 retract v1.0.0 // suffix 428 `, 429 `suffix`, 430 }, 431 { 432 `prefix_suffix_after`, 433 `module m 434 // prefix 435 retract v1.0.0 // suffix 436 `, 437 `prefix 438suffix`, 439 }, 440 { 441 `block_only`, 442 `// block 443 retract ( 444 v1.0.0 445 ) 446 `, 447 `block`, 448 }, 449 { 450 `block_and_line`, 451 `// block 452 retract ( 453 // line 454 v1.0.0 455 ) 456 `, 457 `line`, 458 }, 459} 460 461var sortBlocksTests = []struct { 462 desc, in, out string 463 strict bool 464}{ 465 { 466 `exclude_duplicates_removed`, 467 `module m 468 exclude x.y/z v1.0.0 // a 469 exclude x.y/z v1.0.0 // b 470 exclude ( 471 x.y/w v1.1.0 472 x.y/z v1.0.0 // c 473 ) 474 `, 475 `module m 476 exclude x.y/z v1.0.0 // a 477 exclude ( 478 x.y/w v1.1.0 479 )`, 480 true, 481 }, 482 { 483 `replace_duplicates_removed`, 484 `module m 485 replace x.y/z v1.0.0 => ./a 486 replace x.y/z v1.1.0 => ./b 487 replace ( 488 x.y/z v1.0.0 => ./c 489 ) 490 `, 491 `module m 492 replace x.y/z v1.1.0 => ./b 493 replace ( 494 x.y/z v1.0.0 => ./c 495 ) 496 `, 497 true, 498 }, 499 { 500 `retract_duplicates_not_removed`, 501 `module m 502 // block 503 retract ( 504 v1.0.0 // one 505 v1.0.0 // two 506 )`, 507 `module m 508 // block 509 retract ( 510 v1.0.0 // one 511 v1.0.0 // two 512 )`, 513 true, 514 }, 515 // Tests below this point just check sort order. 516 // Non-retract blocks are sorted lexicographically in ascending order. 517 // retract blocks are sorted using semver in descending order. 518 { 519 `sort_lexicographically`, 520 `module m 521 sort ( 522 aa 523 cc 524 bb 525 zz 526 v1.2.0 527 v1.11.0 528 )`, 529 `module m 530 sort ( 531 aa 532 bb 533 cc 534 v1.11.0 535 v1.2.0 536 zz 537 ) 538 `, 539 false, 540 }, 541 { 542 `sort_retract`, 543 `module m 544 retract ( 545 [v1.2.0, v1.3.0] 546 [v1.1.0, v1.3.0] 547 [v1.1.0, v1.2.0] 548 v1.0.0 549 v1.1.0 550 v1.2.0 551 v1.3.0 552 v1.4.0 553 ) 554 `, 555 `module m 556 retract ( 557 v1.4.0 558 v1.3.0 559 [v1.2.0, v1.3.0] 560 v1.2.0 561 [v1.1.0, v1.3.0] 562 [v1.1.0, v1.2.0] 563 v1.1.0 564 v1.0.0 565 ) 566 `, 567 false, 568 }, 569} 570 571var addRetractValidateVersionTests = []struct { 572 dsc, low, high string 573}{ 574 { 575 "blank_version", 576 "", 577 "", 578 }, 579 { 580 "missing_prefix", 581 "1.0.0", 582 "1.0.0", 583 }, 584 { 585 "non_canonical", 586 "v1.2", 587 "v1.2", 588 }, 589 { 590 "invalid_range", 591 "v1.2.3", 592 "v1.3", 593 }, 594} 595 596var addExcludeValidateVersionTests = []struct { 597 dsc, ver string 598}{ 599 { 600 "blank_version", 601 "", 602 }, 603 { 604 "missing_prefix", 605 "1.0.0", 606 }, 607 { 608 "non_canonical", 609 "v1.2", 610 }, 611} 612 613func TestAddRequire(t *testing.T) { 614 for _, tt := range addRequireTests { 615 t.Run(tt.desc, func(t *testing.T) { 616 testEdit(t, tt.in, tt.out, true, func(f *File) error { 617 return f.AddRequire(tt.path, tt.vers) 618 }) 619 }) 620 } 621} 622 623func TestSetRequire(t *testing.T) { 624 for _, tt := range setRequireTests { 625 t.Run(tt.desc, func(t *testing.T) { 626 var mods []*Require 627 for _, mod := range tt.mods { 628 mods = append(mods, &Require{ 629 Mod: module.Version{ 630 Path: mod.path, 631 Version: mod.vers, 632 }, 633 Indirect: mod.indirect, 634 }) 635 } 636 637 f := testEdit(t, tt.in, tt.out, true, func(f *File) error { 638 f.SetRequire(mods) 639 return nil 640 }) 641 642 f.Cleanup() 643 if len(f.Require) != len(mods) { 644 t.Errorf("after Cleanup, len(Require) = %v; want %v", len(f.Require), len(mods)) 645 } 646 }) 647 } 648} 649 650func TestAddGo(t *testing.T) { 651 for _, tt := range addGoTests { 652 t.Run(tt.desc, func(t *testing.T) { 653 testEdit(t, tt.in, tt.out, true, func(f *File) error { 654 return f.AddGoStmt(tt.version) 655 }) 656 }) 657 } 658} 659 660func TestAddRetract(t *testing.T) { 661 for _, tt := range addRetractTests { 662 t.Run(tt.desc, func(t *testing.T) { 663 testEdit(t, tt.in, tt.out, true, func(f *File) error { 664 return f.AddRetract(VersionInterval{Low: tt.low, High: tt.high}, tt.rationale) 665 }) 666 }) 667 } 668} 669 670func TestDropRetract(t *testing.T) { 671 for _, tt := range dropRetractTests { 672 t.Run(tt.desc, func(t *testing.T) { 673 testEdit(t, tt.in, tt.out, true, func(f *File) error { 674 if err := f.DropRetract(VersionInterval{Low: tt.low, High: tt.high}); err != nil { 675 return err 676 } 677 f.Cleanup() 678 return nil 679 }) 680 }) 681 } 682} 683 684func TestRetractRationale(t *testing.T) { 685 for _, tt := range retractRationaleTests { 686 t.Run(tt.desc, func(t *testing.T) { 687 f, err := Parse("in", []byte(tt.in), nil) 688 if err != nil { 689 t.Fatal(err) 690 } 691 if len(f.Retract) != 1 { 692 t.Fatalf("got %d retract directives; want 1", len(f.Retract)) 693 } 694 if got := f.Retract[0].Rationale; got != tt.want { 695 t.Errorf("got %q; want %q", got, tt.want) 696 } 697 }) 698 } 699} 700 701func TestSortBlocks(t *testing.T) { 702 for _, tt := range sortBlocksTests { 703 t.Run(tt.desc, func(t *testing.T) { 704 testEdit(t, tt.in, tt.out, tt.strict, func(f *File) error { 705 f.SortBlocks() 706 return nil 707 }) 708 }) 709 } 710} 711 712func testEdit(t *testing.T, in, want string, strict bool, transform func(f *File) error) *File { 713 t.Helper() 714 parse := Parse 715 if !strict { 716 parse = ParseLax 717 } 718 f, err := parse("in", []byte(in), nil) 719 if err != nil { 720 t.Fatal(err) 721 } 722 g, err := parse("out", []byte(want), nil) 723 if err != nil { 724 t.Fatal(err) 725 } 726 golden, err := g.Format() 727 if err != nil { 728 t.Fatal(err) 729 } 730 731 if err := transform(f); err != nil { 732 t.Fatal(err) 733 } 734 out, err := f.Format() 735 if err != nil { 736 t.Fatal(err) 737 } 738 if !bytes.Equal(out, golden) { 739 t.Errorf("have:\n%s\nwant:\n%s", out, golden) 740 } 741 742 return f 743} 744 745func TestAddRetractValidateVersion(t *testing.T) { 746 for _, tt := range addRetractValidateVersionTests { 747 t.Run(tt.dsc, func(t *testing.T) { 748 f, err := Parse("in", []byte("module m"), nil) 749 if err != nil { 750 t.Fatal(err) 751 } 752 if err = f.AddRetract(VersionInterval{Low: tt.low, High: tt.high}, ""); err == nil { 753 t.Fatal("expected AddRetract to complain about version format") 754 } 755 }) 756 } 757} 758 759func TestAddExcludeValidateVersion(t *testing.T) { 760 for _, tt := range addExcludeValidateVersionTests { 761 t.Run(tt.dsc, func(t *testing.T) { 762 f, err := Parse("in", []byte("module m"), nil) 763 if err != nil { 764 t.Fatal(err) 765 } 766 if err = f.AddExclude("aa", tt.ver); err == nil { 767 t.Fatal("expected AddExclude to complain about version format") 768 } 769 }) 770 } 771} 772