1
2
3
4
5 package modfile
6
7 import (
8 "bytes"
9 "fmt"
10 "strings"
11 "testing"
12
13 "golang.org/x/mod/module"
14 )
15
16 var addRequireTests = []struct {
17 desc string
18 in string
19 path string
20 vers string
21 out string
22 }{
23 {
24 `existing`,
25 `
26 module m
27 require x.y/z v1.2.3
28 `,
29 "x.y/z", "v1.5.6",
30 `
31 module m
32 require x.y/z v1.5.6
33 `,
34 },
35 {
36 `existing2`,
37 `
38 module m
39 require (
40 x.y/z v1.2.3 // first
41 x.z/a v0.1.0 // first-a
42 )
43 require x.y/z v1.4.5 // second
44 require (
45 x.y/z v1.6.7 // third
46 x.z/a v0.2.0 // third-a
47 )
48 `,
49 "x.y/z", "v1.8.9",
50 `
51 module m
52
53 require (
54 x.y/z v1.8.9 // first
55 x.z/a v0.1.0 // first-a
56 )
57
58 require x.z/a v0.2.0 // third-a
59 `,
60 },
61 {
62 `new`,
63 `
64 module m
65 require x.y/z v1.2.3
66 `,
67 "x.y/w", "v1.5.6",
68 `
69 module m
70 require (
71 x.y/z v1.2.3
72 x.y/w v1.5.6
73 )
74 `,
75 },
76 {
77 `new2`,
78 `
79 module m
80 require x.y/z v1.2.3
81 require x.y/q/v2 v2.3.4
82 `,
83 "x.y/w", "v1.5.6",
84 `
85 module m
86 require x.y/z v1.2.3
87 require (
88 x.y/q/v2 v2.3.4
89 x.y/w v1.5.6
90 )
91 `,
92 },
93 {
94 `unattached_comments`,
95 `
96 module m
97 require (
98 foo v0.0.0-00010101000000-000000000000
99 // bar v0.0.0-00010101000000-000000000000
100 )
101 `,
102 "foo", "v0.0.0-00010101000000-000000000000",
103 `
104 module m
105 require (
106 foo v0.0.0-00010101000000-000000000000
107 // bar v0.0.0-00010101000000-000000000000
108 )
109 `,
110 },
111 }
112
113 type require struct {
114 path, vers string
115 indirect bool
116 }
117
118 var setRequireTests = []struct {
119 desc string
120 in string
121 mods []require
122 out string
123 }{
124 {
125 `https://golang.org/issue/45932`,
126 `module m
127 require (
128 x.y/a v1.2.3 //indirect
129 x.y/b v1.2.3
130 x.y/c v1.2.3
131 )
132 `,
133 []require{
134 {"x.y/a", "v1.2.3", false},
135 {"x.y/b", "v1.2.3", false},
136 {"x.y/c", "v1.2.3", false},
137 },
138 `module m
139 require (
140 x.y/a v1.2.3
141 x.y/b v1.2.3
142 x.y/c v1.2.3
143 )
144 `,
145 },
146 {
147 `existing`,
148 `module m
149 require (
150 x.y/b v1.2.3
151
152 x.y/a v1.2.3
153 x.y/d v1.2.3
154 )
155 `,
156 []require{
157 {"x.y/a", "v1.2.3", false},
158 {"x.y/b", "v1.2.3", false},
159 {"x.y/c", "v1.2.3", false},
160 },
161 `module m
162 require (
163 x.y/a v1.2.3
164 x.y/b v1.2.3
165 x.y/c v1.2.3
166 )
167 `,
168 },
169 {
170 `existing_indirect`,
171 `module m
172 require (
173 x.y/a v1.2.3
174 x.y/b v1.2.3 //
175 x.y/c v1.2.3 //c
176 x.y/d v1.2.3 // c
177 x.y/e v1.2.3 // indirect
178 x.y/f v1.2.3 //indirect
179 x.y/g v1.2.3 // indirect
180 )
181 `,
182 []require{
183 {"x.y/a", "v1.2.3", true},
184 {"x.y/b", "v1.2.3", true},
185 {"x.y/c", "v1.2.3", true},
186 {"x.y/d", "v1.2.3", true},
187 {"x.y/e", "v1.2.3", true},
188 {"x.y/f", "v1.2.3", true},
189 {"x.y/g", "v1.2.3", true},
190 },
191 `module m
192 require (
193 x.y/a v1.2.3 // indirect
194 x.y/b v1.2.3 // indirect
195 x.y/c v1.2.3 // indirect; c
196 x.y/d v1.2.3 // indirect; c
197 x.y/e v1.2.3 // indirect
198 x.y/f v1.2.3 //indirect
199 x.y/g v1.2.3 // indirect
200 )
201 `,
202 },
203 {
204 `existing_multi`,
205 `module m
206 require x.y/a v1.2.3
207 require x.y/b v1.2.3
208 require x.y/c v1.0.0 // not v1.2.3!
209 require x.y/d v1.2.3 // comment kept
210 require x.y/e v1.2.3 // comment kept
211 require x.y/f v1.2.3 // indirect
212 require x.y/g v1.2.3 // indirect
213 `,
214 []require{
215 {"x.y/h", "v1.2.3", false},
216 {"x.y/a", "v1.2.3", false},
217 {"x.y/b", "v1.2.3", false},
218 {"x.y/c", "v1.2.3", false},
219 {"x.y/d", "v1.2.3", false},
220 {"x.y/e", "v1.2.3", true},
221 {"x.y/f", "v1.2.3", false},
222 {"x.y/g", "v1.2.3", false},
223 },
224 `module m
225 require x.y/a v1.2.3
226
227 require x.y/b v1.2.3
228
229 require x.y/c v1.2.3 // not v1.2.3!
230
231 require x.y/d v1.2.3 // comment kept
232
233 require x.y/e v1.2.3 // indirect; comment kept
234
235 require x.y/f v1.2.3
236
237 require (
238 x.y/g v1.2.3
239 x.y/h v1.2.3
240 )
241 `,
242 },
243 {
244 `existing_duplicate`,
245 `module m
246 require (
247 x.y/a v1.0.0 // zero
248 x.y/a v1.1.0 // one
249 x.y/a v1.2.3 // two
250 )
251 `,
252 []require{
253 {"x.y/a", "v1.2.3", true},
254 },
255 `module m
256 require x.y/a v1.2.3 // indirect; zero
257 `,
258 },
259 {
260 `existing_duplicate_multi`,
261 `module m
262 require x.y/a v1.0.0 // zero
263 require x.y/a v1.1.0 // one
264 require x.y/a v1.2.3 // two
265 `,
266 []require{
267 {"x.y/a", "v1.2.3", true},
268 },
269 `module m
270 require x.y/a v1.2.3 // indirect; zero
271 `,
272 },
273 }
274
275 var setRequireSeparateIndirectTests = []struct {
276 desc string
277 in string
278 mods []require
279 out string
280 }{
281 {
282 `https://golang.org/issue/45932`,
283 `module m
284 require (
285 x.y/a v1.2.3 //indirect
286 x.y/b v1.2.3
287 x.y/c v1.2.3
288 )
289 `,
290 []require{
291 {"x.y/a", "v1.2.3", false},
292 {"x.y/b", "v1.2.3", false},
293 {"x.y/c", "v1.2.3", false},
294 },
295 `module m
296 require (
297 x.y/a v1.2.3
298 x.y/b v1.2.3
299 x.y/c v1.2.3
300 )
301 `,
302 },
303 {
304 `existing`,
305 `module m
306 require (
307 x.y/b v1.2.3
308
309 x.y/a v1.2.3
310 x.y/d v1.2.3
311 )
312 `,
313 []require{
314 {"x.y/a", "v1.2.3", false},
315 {"x.y/b", "v1.2.3", false},
316 {"x.y/c", "v1.2.3", false},
317 },
318 `module m
319 require (
320 x.y/a v1.2.3
321 x.y/b v1.2.3
322 x.y/c v1.2.3
323 )
324 `,
325 },
326 {
327 `existing_indirect`,
328 `module m
329 require (
330 x.y/a v1.2.3
331 x.y/b v1.2.3 //
332 x.y/c v1.2.3 //c
333 x.y/d v1.2.3 // c
334 x.y/e v1.2.3 // indirect
335 x.y/f v1.2.3 //indirect
336 x.y/g v1.2.3 // indirect
337 )
338 `,
339 []require{
340 {"x.y/a", "v1.2.3", true},
341 {"x.y/b", "v1.2.3", true},
342 {"x.y/c", "v1.2.3", true},
343 {"x.y/d", "v1.2.3", true},
344 {"x.y/e", "v1.2.3", true},
345 {"x.y/f", "v1.2.3", true},
346 {"x.y/g", "v1.2.3", true},
347 },
348 `module m
349 require (
350 x.y/a v1.2.3 // indirect
351 x.y/b v1.2.3 // indirect
352 x.y/c v1.2.3 // indirect; c
353 x.y/d v1.2.3 // indirect; c
354 x.y/e v1.2.3 // indirect
355 x.y/f v1.2.3 //indirect
356 x.y/g v1.2.3 // indirect
357 )
358 `,
359 },
360 {
361 `existing_line`,
362 `module m
363 require x.y/a v1.0.0
364 require x.y/c v1.0.0 // indirect
365 `,
366 []require{
367 {"x.y/a", "v1.2.3", false},
368 {"x.y/b", "v1.2.3", false},
369 {"x.y/c", "v1.2.3", true},
370 {"x.y/d", "v1.2.3", true},
371 },
372 `module m
373 require (
374 x.y/a v1.2.3
375 x.y/b v1.2.3
376 )
377 require (
378 x.y/c v1.2.3 // indirect
379 x.y/d v1.2.3 // indirect
380 )`,
381 },
382 {
383 `existing_multi`,
384 `module m
385 require x.y/a v1.2.3
386 require x.y/b v1.2.3 // demoted to indirect
387 require x.y/c v1.0.0 // not v1.2.3!
388 require x.y/d v1.2.3 // comment kept
389 require x.y/e v1.2.3 // comment kept
390 require x.y/f v1.2.3 // indirect; promoted to direct
391 // promoted to direct
392 require x.y/g v1.2.3 // indirect
393 require x.y/i v1.2.3 // indirect
394 require x.y/j v1.2.3 // indirect
395 `,
396 []require{
397 {"x.y/h", "v1.2.3", false},
398 {"x.y/i", "v1.2.3", true},
399 {"x.y/j", "v1.2.3", true},
400 {"x.y/a", "v1.2.3", false},
401 {"x.y/b", "v1.2.3", true},
402 {"x.y/c", "v1.2.3", false},
403 {"x.y/d", "v1.2.3", false},
404 {"x.y/e", "v1.2.3", true},
405 {"x.y/f", "v1.2.3", false},
406 {"x.y/g", "v1.2.3", false},
407 },
408 `module m
409 require (
410 x.y/a v1.2.3
411 x.y/h v1.2.3
412 )
413 require x.y/b v1.2.3 // indirect; demoted to indirect
414 require x.y/c v1.2.3 // not v1.2.3!
415 require x.y/d v1.2.3 // comment kept
416 require x.y/e v1.2.3 // indirect; comment kept
417 require x.y/f v1.2.3 // promoted to direct
418 // promoted to direct
419 require x.y/g v1.2.3
420 require x.y/i v1.2.3 // indirect
421 require x.y/j v1.2.3 // indirect
422 `,
423 },
424 {
425 `existing_duplicate`,
426 `module m
427 require (
428 x.y/a v1.0.0 // zero
429 x.y/a v1.1.0 // one
430 x.y/a v1.2.3 // two
431 )
432 `,
433 []require{
434 {"x.y/a", "v1.2.3", true},
435 },
436 `module m
437 require x.y/a v1.2.3 // indirect; zero
438 `,
439 },
440 {
441 `existing_duplicate_multi`,
442 `module m
443 require x.y/a v1.0.0 // zero
444 require x.y/a v1.1.0 // one
445 require x.y/a v1.2.3 // two
446 `,
447 []require{
448 {"x.y/a", "v1.2.3", true},
449 },
450 `module m
451 require x.y/a v1.2.3 // indirect; zero
452 `,
453 },
454 {
455 `existing_duplicate_mix_indirect`,
456 `module m
457 require (
458 x.y/a v1.0.0 // zero
459 x.y/a v1.1.0 // indirect; one
460 x.y/a v1.2.3 // indirect; two
461 )
462 `,
463 []require{
464 {"x.y/a", "v1.2.3", true},
465 },
466 `module m
467 require x.y/a v1.2.3 // indirect; zero
468 `,
469 },
470 {
471 `existing_duplicate_mix_direct`,
472 `module m
473 require (
474 x.y/a v1.0.0 // indirect; zero
475 x.y/a v1.1.0 // one
476 x.y/a v1.2.3 // two
477 )
478 `,
479 []require{
480 {"x.y/a", "v1.2.3", false},
481 },
482 `module m
483 require x.y/a v1.2.3 // zero
484 `,
485 },
486 {
487 `add_indirect_after_last_direct`,
488 `module m
489 require (
490 x.y/a v1.0.0 // comment a preserved
491 x.y/d v1.0.0 // comment d preserved
492 )
493 require (
494 x.y/b v1.0.0 // comment b preserved
495 x.y/e v1.0.0 // comment e preserved
496 )
497 go 1.17
498 `,
499 []require{
500 {"x.y/a", "v1.2.3", false},
501 {"x.y/b", "v1.2.3", false},
502 {"x.y/c", "v1.2.3", true},
503 {"x.y/d", "v1.2.3", false},
504 {"x.y/e", "v1.2.3", false},
505 {"x.y/f", "v1.2.3", true},
506 },
507 `module m
508 require (
509 x.y/a v1.2.3 // comment a preserved
510 x.y/d v1.2.3 // comment d preserved
511 )
512 require (
513 x.y/b v1.2.3 // comment b preserved
514 x.y/e v1.2.3 // comment e preserved
515 )
516 require (
517 x.y/c v1.2.3 // indirect
518 x.y/f v1.2.3 // indirect
519 )
520 go 1.17
521 `,
522 },
523 {
524 `add_direct_before_first_indirect`,
525 `module m
526 require (
527 x.y/b v1.0.0 // indirect; comment b preserved
528 x.y/e v1.0.0 // indirect; comment d preserved
529 )
530 require (
531 x.y/c v1.0.0 // indirect; comment c preserved
532 x.y/f v1.0.0 // indirect; comment e preserved
533 )
534 `,
535 []require{
536 {"x.y/a", "v1.2.3", false},
537 {"x.y/b", "v1.2.3", true},
538 {"x.y/c", "v1.2.3", true},
539 {"x.y/d", "v1.2.3", false},
540 {"x.y/e", "v1.2.3", true},
541 {"x.y/f", "v1.2.3", true},
542 },
543 `module m
544 require (
545 x.y/b v1.2.3 // indirect; comment b preserved
546 x.y/e v1.2.3 // indirect; comment d preserved
547 )
548 require (
549 x.y/c v1.2.3 // indirect; comment c preserved
550 x.y/f v1.2.3 // indirect; comment e preserved
551 )
552 require (
553 x.y/a v1.2.3
554 x.y/d v1.2.3
555 )
556 `,
557 },
558 {
559 `add_indirect_after_mixed`,
560 `module m
561 require (
562 x.y/a v1.0.0
563 x.y/b v1.0.0 // indirect
564 )
565 `,
566 []require{
567 {"x.y/a", "v1.2.3", false},
568 {"x.y/b", "v1.2.3", true},
569 {"x.y/c", "v1.2.3", true},
570 {"x.y/d", "v1.2.3", false},
571 {"x.y/e", "v1.2.3", true},
572 },
573 `module m
574 require (
575 x.y/a v1.2.3
576 x.y/d v1.2.3
577 )
578 require (
579 x.y/b v1.2.3 // indirect
580 x.y/c v1.2.3 // indirect
581 x.y/e v1.2.3 // indirect
582 )
583 `,
584 },
585 {
586 `preserve_block_comment_indirect_to_direct`,
587 `module m
588 // save
589 require (
590 x.y/a v1.2.3 // indirect
591 )
592 `,
593 []require{
594 {"x.y/a", "v1.2.3", false},
595 },
596 `module m
597
598 // save
599 require x.y/a v1.2.3
600 `,
601 },
602 {
603 `preserve_block_comment_direct_to_indirect`,
604 `module m
605 // save
606 require (
607 x.y/a v1.2.3
608 )
609 `,
610 []require{
611 {"x.y/a", "v1.2.3", true},
612 },
613 `module m
614
615 // save
616 require x.y/a v1.2.3 // indirect
617 `,
618 },
619 {
620 `regroup_flat_uncommented_block`,
621 `module m
622 require (
623 x.y/a v1.0.0 // a
624 x.y/b v1.0.0 // indirect; b
625 x.y/c v1.0.0 // indirect
626 )`,
627 []require{
628 {"x.y/a", "v1.2.3", false},
629 {"x.y/b", "v1.2.3", true},
630 {"x.y/c", "v1.2.3", true},
631 {"x.y/d", "v1.2.3", false},
632 },
633 `module m
634 require (
635 x.y/a v1.2.3 // a
636 x.y/d v1.2.3
637 )
638 require (
639 x.y/b v1.2.3 // indirect; b
640 x.y/c v1.2.3 // indirect
641 )`,
642 },
643 {
644 `dont_regroup_flat_commented_block`,
645 `module m
646 // dont regroup
647 require (
648 x.y/a v1.0.0
649 x.y/b v1.0.0 // indirect
650 x.y/c v1.0.0 // indirect
651 )`,
652 []require{
653 {"x.y/a", "v1.2.3", false},
654 {"x.y/b", "v1.2.3", true},
655 {"x.y/c", "v1.2.3", true},
656 {"x.y/d", "v1.2.3", false},
657 },
658 `module m
659 // dont regroup
660 require (
661 x.y/a v1.2.3
662 x.y/b v1.2.3 // indirect
663 x.y/c v1.2.3 // indirect
664 )
665 require x.y/d v1.2.3`,
666 },
667 }
668
669 var addGoTests = []struct {
670 desc string
671 in string
672 version string
673 out string
674 }{
675 {
676 `module_only`,
677 `module m
678 `,
679 `1.14`,
680 `module m
681 go 1.14
682 `,
683 },
684 {
685 `module_before_require`,
686 `module m
687 require x.y/a v1.2.3
688 `,
689 `1.14`,
690 `module m
691 go 1.14
692 require x.y/a v1.2.3
693 `,
694 },
695 {
696 `require_before_module`,
697 `require x.y/a v1.2.3
698 module example.com/inverted
699 `,
700 `1.14`,
701 `require x.y/a v1.2.3
702 module example.com/inverted
703 go 1.14
704 `,
705 },
706 {
707 `require_only`,
708 `require x.y/a v1.2.3
709 `,
710 `1.14`,
711 `require x.y/a v1.2.3
712 go 1.14
713 `,
714 },
715 }
716
717 var dropGoTests = []struct {
718 desc string
719 in string
720 out string
721 }{
722 {
723 `module_only`,
724 `module m
725 go 1.14
726 `,
727 `module m
728 `,
729 },
730 {
731 `module_before_require`,
732 `module m
733 go 1.14
734 require x.y/a v1.2.3
735 `,
736 `module m
737 require x.y/a v1.2.3
738 `,
739 },
740 {
741 `require_before_module`,
742 `require x.y/a v1.2.3
743 module example.com/inverted
744 go 1.14
745 `,
746 `require x.y/a v1.2.3
747 module example.com/inverted
748 `,
749 },
750 {
751 `require_only`,
752 `require x.y/a v1.2.3
753 go 1.14
754 `,
755 `require x.y/a v1.2.3
756 `,
757 },
758 }
759
760 var addToolchainTests = []struct {
761 desc string
762 in string
763 version string
764 out string
765 }{
766 {
767 `empty`,
768 ``,
769 `go1.17`,
770 `toolchain go1.17
771 `,
772 },
773 {
774 `aftergo`,
775 `// this is a comment
776 require x v1.0.0
777
778 go 1.17
779
780 require y v1.0.0
781 `,
782 `go1.17`,
783 `// this is a comment
784 require x v1.0.0
785
786 go 1.17
787
788 toolchain go1.17
789
790 require y v1.0.0
791 `,
792 },
793 {
794 `already_have_toolchain`,
795 `go 1.17
796
797 toolchain go1.18
798 `,
799 `go1.19`,
800 `go 1.17
801
802 toolchain go1.19
803 `,
804 },
805 }
806
807 var dropToolchainTests = []struct {
808 desc string
809 in string
810 out string
811 }{
812 {
813 `empty`,
814 `toolchain go1.17
815 `,
816 ``,
817 },
818 {
819 `aftergo`,
820 `// this is a comment
821 require x v1.0.0
822
823 go 1.17
824
825 toolchain go1.17
826
827 require y v1.0.0
828 `,
829 `// this is a comment
830 require x v1.0.0
831
832 go 1.17
833
834 require y v1.0.0
835 `,
836 },
837 {
838 `already_have_toolchain`,
839 `go 1.17
840
841 toolchain go1.18
842 `,
843 `go 1.17
844 `,
845 },
846 }
847
848 var addExcludeTests = []struct {
849 desc string
850 in string
851 path string
852 version string
853 out string
854 }{
855 {
856 `compatible`,
857 `module m
858 `,
859 `example.com`,
860 `v1.2.3`,
861 `module m
862 exclude example.com v1.2.3
863 `,
864 },
865 {
866 `gopkg.in v0`,
867 `module m
868 `,
869 `gopkg.in/foo.v0`,
870 `v0.2.3`,
871 `module m
872 exclude gopkg.in/foo.v0 v0.2.3
873 `,
874 },
875 {
876 `gopkg.in v1`,
877 `module m
878 `,
879 `gopkg.in/foo.v1`,
880 `v1.2.3`,
881 `module m
882 exclude gopkg.in/foo.v1 v1.2.3
883 `,
884 },
885 }
886
887 var addRetractTests = []struct {
888 desc string
889 in string
890 low string
891 high string
892 rationale string
893 out string
894 }{
895 {
896 `new_singleton`,
897 `module m
898 `,
899 `v1.2.3`,
900 `v1.2.3`,
901 ``,
902 `module m
903 retract v1.2.3
904 `,
905 },
906 {
907 `new_interval`,
908 `module m
909 `,
910 `v1.0.0`,
911 `v1.1.0`,
912 ``,
913 `module m
914 retract [v1.0.0, v1.1.0]`,
915 },
916 {
917 `duplicate_with_rationale`,
918 `module m
919 retract v1.2.3
920 `,
921 `v1.2.3`,
922 `v1.2.3`,
923 `bad`,
924 `module m
925 retract (
926 v1.2.3
927 // bad
928 v1.2.3
929 )
930 `,
931 },
932 {
933 `duplicate_multiline_rationale`,
934 `module m
935 retract [v1.2.3, v1.2.3]
936 `,
937 `v1.2.3`,
938 `v1.2.3`,
939 `multi
940 line`,
941 `module m
942 retract (
943 [v1.2.3, v1.2.3]
944 // multi
945 // line
946 v1.2.3
947 )
948 `,
949 },
950 {
951 `duplicate_interval`,
952 `module m
953 retract [v1.0.0, v1.1.0]
954 `,
955 `v1.0.0`,
956 `v1.1.0`,
957 ``,
958 `module m
959 retract (
960 [v1.0.0, v1.1.0]
961 [v1.0.0, v1.1.0]
962 )
963 `,
964 },
965 {
966 `duplicate_singleton`,
967 `module m
968 retract v1.2.3
969 `,
970 `v1.2.3`,
971 `v1.2.3`,
972 ``,
973 `module m
974 retract (
975 v1.2.3
976 v1.2.3
977 )
978 `,
979 },
980 }
981
982 var dropRetractTests = []struct {
983 desc string
984 in string
985 low string
986 high string
987 out string
988 }{
989 {
990 `singleton_no_match`,
991 `module m
992 retract v1.2.3
993 `,
994 `v1.0.0`,
995 `v1.0.0`,
996 `module m
997 retract v1.2.3
998 `,
999 },
1000 {
1001 `singleton_match_one`,
1002 `module m
1003 retract v1.2.2
1004 retract v1.2.3
1005 retract v1.2.4
1006 `,
1007 `v1.2.3`,
1008 `v1.2.3`,
1009 `module m
1010 retract v1.2.2
1011 retract v1.2.4
1012 `,
1013 },
1014 {
1015 `singleton_match_all`,
1016 `module m
1017 retract v1.2.3 // first
1018 retract v1.2.3 // second
1019 `,
1020 `v1.2.3`,
1021 `v1.2.3`,
1022 `module m
1023 `,
1024 },
1025 {
1026 `interval_match`,
1027 `module m
1028 retract [v1.2.3, v1.2.3]
1029 `,
1030 `v1.2.3`,
1031 `v1.2.3`,
1032 `module m
1033 `,
1034 },
1035 {
1036 `interval_superset_no_match`,
1037 `module m
1038 retract [v1.0.0, v1.1.0]
1039 `,
1040 `v1.0.0`,
1041 `v1.2.0`,
1042 `module m
1043 retract [v1.0.0, v1.1.0]
1044 `,
1045 },
1046 {
1047 `singleton_match_middle`,
1048 `module m
1049 retract v1.2.3
1050 `,
1051 `v1.2.3`,
1052 `v1.2.3`,
1053 `module m
1054 `,
1055 },
1056 {
1057 `interval_match_middle_block`,
1058 `module m
1059 retract (
1060 v1.0.0
1061 [v1.1.0, v1.2.0]
1062 v1.3.0
1063 )
1064 `,
1065 `v1.1.0`,
1066 `v1.2.0`,
1067 `module m
1068 retract (
1069 v1.0.0
1070 v1.3.0
1071 )
1072 `,
1073 },
1074 {
1075 `interval_match_all`,
1076 `module m
1077 retract [v1.0.0, v1.1.0]
1078 retract [v1.0.0, v1.1.0]
1079 `,
1080 `v1.0.0`,
1081 `v1.1.0`,
1082 `module m
1083 `,
1084 },
1085 }
1086
1087 var retractRationaleTests = []struct {
1088 desc, in, want string
1089 }{
1090 {
1091 `no_comment`,
1092 `module m
1093 retract v1.0.0`,
1094 ``,
1095 },
1096 {
1097 `prefix_one`,
1098 `module m
1099 // prefix
1100 retract v1.0.0
1101 `,
1102 `prefix`,
1103 },
1104 {
1105 `prefix_multiline`,
1106 `module m
1107 // one
1108 //
1109 // two
1110 //
1111 // three
1112 retract v1.0.0`,
1113 `one
1114
1115 two
1116
1117 three`,
1118 },
1119 {
1120 `suffix`,
1121 `module m
1122 retract v1.0.0 // suffix
1123 `,
1124 `suffix`,
1125 },
1126 {
1127 `prefix_suffix_after`,
1128 `module m
1129 // prefix
1130 retract v1.0.0 // suffix
1131 `,
1132 `prefix
1133 suffix`,
1134 },
1135 {
1136 `block_only`,
1137 `// block
1138 retract (
1139 v1.0.0
1140 )
1141 `,
1142 `block`,
1143 },
1144 {
1145 `block_and_line`,
1146 `// block
1147 retract (
1148 // line
1149 v1.0.0
1150 )
1151 `,
1152 `line`,
1153 },
1154 }
1155
1156 var moduleDeprecatedTests = []struct {
1157 desc, in, want string
1158 }{
1159
1160
1161 {
1162 `no_comment`,
1163 `module m`,
1164 ``,
1165 },
1166 {
1167 `other_comment`,
1168 `// yo
1169 module m`,
1170 ``,
1171 },
1172 {
1173 `deprecated_no_colon`,
1174 `//Deprecated
1175 module m`,
1176 ``,
1177 },
1178 {
1179 `deprecated_no_space`,
1180 `//Deprecated:blah
1181 module m`,
1182 `blah`,
1183 },
1184 {
1185 `deprecated_simple`,
1186 `// Deprecated: blah
1187 module m`,
1188 `blah`,
1189 },
1190 {
1191 `deprecated_lowercase`,
1192 `// deprecated: blah
1193 module m`,
1194 ``,
1195 },
1196 {
1197 `deprecated_multiline`,
1198 `// Deprecated: one
1199 // two
1200 module m`,
1201 "one\ntwo",
1202 },
1203 {
1204 `deprecated_mixed`,
1205 `// some other comment
1206 // Deprecated: blah
1207 module m`,
1208 ``,
1209 },
1210 {
1211 `deprecated_middle`,
1212 `// module m is Deprecated: blah
1213 module m`,
1214 ``,
1215 },
1216 {
1217 `deprecated_multiple`,
1218 `// Deprecated: a
1219 // Deprecated: b
1220 module m`,
1221 "a\nDeprecated: b",
1222 },
1223 {
1224 `deprecated_paragraph`,
1225 `// Deprecated: a
1226 // b
1227 //
1228 // c
1229 module m`,
1230 "a\nb",
1231 },
1232 {
1233 `deprecated_paragraph_space`,
1234 `// Deprecated: the next line has a space
1235 //
1236 // c
1237 module m`,
1238 "the next line has a space",
1239 },
1240 {
1241 `deprecated_suffix`,
1242 `module m // Deprecated: blah`,
1243 `blah`,
1244 },
1245 {
1246 `deprecated_mixed_suffix`,
1247 `// some other comment
1248 module m // Deprecated: blah`,
1249 ``,
1250 },
1251 {
1252 `deprecated_mixed_suffix_paragraph`,
1253 `// some other comment
1254 //
1255 module m // Deprecated: blah`,
1256 `blah`,
1257 },
1258 {
1259 `deprecated_block`,
1260 `// Deprecated: blah
1261 module (
1262 m
1263 )`,
1264 `blah`,
1265 },
1266 }
1267
1268 var sortBlocksTests = []struct {
1269 desc, in, out string
1270 strict bool
1271 }{
1272 {
1273 `exclude_duplicates_removed`,
1274 `module m
1275 exclude x.y/z v1.0.0 // a
1276 exclude x.y/z v1.0.0 // b
1277 exclude (
1278 x.y/w v1.1.0
1279 x.y/z v1.0.0 // c
1280 )
1281 `,
1282 `module m
1283 exclude x.y/z v1.0.0 // a
1284 exclude (
1285 x.y/w v1.1.0
1286 )`,
1287 true,
1288 },
1289 {
1290 `replace_duplicates_removed`,
1291 `module m
1292 replace x.y/z v1.0.0 => ./a
1293 replace x.y/z v1.1.0 => ./b
1294 replace (
1295 x.y/z v1.0.0 => ./c
1296 )
1297 `,
1298 `module m
1299 replace x.y/z v1.1.0 => ./b
1300 replace (
1301 x.y/z v1.0.0 => ./c
1302 )
1303 `,
1304 true,
1305 },
1306 {
1307 `retract_duplicates_not_removed`,
1308 `module m
1309 // block
1310 retract (
1311 v1.0.0 // one
1312 v1.0.0 // two
1313 )`,
1314 `module m
1315 // block
1316 retract (
1317 v1.0.0 // one
1318 v1.0.0 // two
1319 )`,
1320 true,
1321 },
1322
1323
1324
1325 {
1326 `sort_lexicographically`,
1327 `module m
1328 sort (
1329 aa
1330 cc
1331 bb
1332 zz
1333 v1.2.0
1334 v1.11.0
1335 )`,
1336 `module m
1337 sort (
1338 aa
1339 bb
1340 cc
1341 v1.11.0
1342 v1.2.0
1343 zz
1344 )
1345 `,
1346 false,
1347 },
1348 {
1349 `sort_retract`,
1350 `module m
1351 retract (
1352 [v1.2.0, v1.3.0]
1353 [v1.1.0, v1.3.0]
1354 [v1.1.0, v1.2.0]
1355 v1.0.0
1356 v1.1.0
1357 v1.2.0
1358 v1.3.0
1359 v1.4.0
1360 )
1361 `,
1362 `module m
1363 retract (
1364 v1.4.0
1365 v1.3.0
1366 [v1.2.0, v1.3.0]
1367 v1.2.0
1368 [v1.1.0, v1.3.0]
1369 [v1.1.0, v1.2.0]
1370 v1.1.0
1371 v1.0.0
1372 )
1373 `,
1374 false,
1375 },
1376
1377
1378 {
1379 `sort_exclude_go121_semver`,
1380 `module m
1381 go 1.21
1382 exclude (
1383 b.example/m v0.9.0
1384 a.example/m v1.0.0
1385 b.example/m v0.10.0
1386 c.example/m v1.1.0
1387 b.example/m v0.11.0
1388 )`,
1389 `module m
1390 go 1.21
1391 exclude (
1392 a.example/m v1.0.0
1393 b.example/m v0.9.0
1394 b.example/m v0.10.0
1395 b.example/m v0.11.0
1396 c.example/m v1.1.0
1397 )
1398 `,
1399 true,
1400 },
1401 {
1402 `sort_exclude_!go121_lexicographically`,
1403 `module m
1404 exclude (
1405 b.example/m v0.9.0
1406 a.example/m v1.0.0
1407 b.example/m v0.10.0
1408 c.example/m v1.1.0
1409 b.example/m v0.11.0
1410 )`,
1411 `module m
1412 exclude (
1413 a.example/m v1.0.0
1414 b.example/m v0.10.0
1415 b.example/m v0.11.0
1416 b.example/m v0.9.0
1417 c.example/m v1.1.0
1418 )
1419 `,
1420 true,
1421 },
1422 }
1423
1424 var addRetractValidateVersionTests = []struct {
1425 desc string
1426 path string
1427 low, high string
1428 wantErr string
1429 }{
1430 {
1431 `blank_version`,
1432 `example.com/m`,
1433 ``,
1434 ``,
1435 `version "" invalid: must be of the form v1.2.3`,
1436 },
1437 {
1438 `missing prefix`,
1439 `example.com/m`,
1440 `1.0.0`,
1441 `1.0.0`,
1442 `version "1.0.0" invalid: must be of the form v1.2.3`,
1443 },
1444 {
1445 `non-canonical`,
1446 `example.com/m`,
1447 `v1.2`,
1448 `v1.2`,
1449 `version "v1.2" invalid: must be of the form v1.2.3`,
1450 },
1451 {
1452 `invalid range`,
1453 `example.com/m`,
1454 `v1.2.3`,
1455 `v1.3`,
1456 `version "v1.3" invalid: must be of the form v1.2.3`,
1457 },
1458 {
1459 `mismatched major`,
1460 `example.com/m/v2`,
1461 `v1.0.0`,
1462 `v1.0.0`,
1463 `version "v1.0.0" invalid: should be v2, not v1`,
1464 },
1465 {
1466 `missing +incompatible`,
1467 `example.com/m`,
1468 `v2.0.0`,
1469 `v2.0.0`,
1470 `version "v2.0.0" invalid: should be v2.0.0+incompatible (or module example.com/m/v2)`,
1471 },
1472 }
1473
1474 var addExcludeValidateVersionTests = []struct {
1475 desc string
1476 path string
1477 version string
1478 wantErr string
1479 }{
1480 {
1481 `blank version`,
1482 `example.com/m`,
1483 ``,
1484 `version "" invalid: must be of the form v1.2.3`,
1485 },
1486 {
1487 `missing prefix`,
1488 `example.com/m`,
1489 `1.0.0`,
1490 `version "1.0.0" invalid: must be of the form v1.2.3`,
1491 },
1492 {
1493 `non-canonical`,
1494 `example.com/m`,
1495 `v1.2`,
1496 `version "v1.2" invalid: must be of the form v1.2.3`,
1497 },
1498 {
1499 `mismatched major`,
1500 `example.com/m/v2`,
1501 `v1.2.3`,
1502 `version "v1.2.3" invalid: should be v2, not v1`,
1503 },
1504 {
1505 `missing +incompatible`,
1506 `example.com/m`,
1507 `v2.3.4`,
1508 `version "v2.3.4" invalid: should be v2.3.4+incompatible (or module example.com/m/v2)`,
1509 },
1510 }
1511
1512 var fixVersionTests = []struct {
1513 desc, in, want, wantErr string
1514 fix VersionFixer
1515 }{
1516 {
1517 desc: `require`,
1518 in: `require example.com/m 1.0.0`,
1519 want: `require example.com/m v1.0.0`,
1520 fix: fixV,
1521 },
1522 {
1523 desc: `replace`,
1524 in: `replace example.com/m 1.0.0 => example.com/m 1.1.0`,
1525 want: `replace example.com/m v1.0.0 => example.com/m v1.1.0`,
1526 fix: fixV,
1527 },
1528 {
1529 desc: `replace_version_in_path`,
1530 in: `replace example.com/m@v1.0.0 => example.com/m@v1.1.0`,
1531 wantErr: `replacement module must match format 'path version', not 'path@version'`,
1532 fix: fixV,
1533 },
1534 {
1535 desc: `replace_version_in_later_path`,
1536 in: `replace example.com/m => example.com/m@v1.1.0`,
1537 wantErr: `replacement module must match format 'path version', not 'path@version'`,
1538 fix: fixV,
1539 },
1540 {
1541 desc: `exclude`,
1542 in: `exclude example.com/m 1.0.0`,
1543 want: `exclude example.com/m v1.0.0`,
1544 fix: fixV,
1545 },
1546 {
1547 desc: `retract_single`,
1548 in: `module example.com/m
1549 retract 1.0.0`,
1550 want: `module example.com/m
1551 retract v1.0.0`,
1552 fix: fixV,
1553 },
1554 {
1555 desc: `retract_interval`,
1556 in: `module example.com/m
1557 retract [1.0.0, 1.1.0]`,
1558 want: `module example.com/m
1559 retract [v1.0.0, v1.1.0]`,
1560 fix: fixV,
1561 },
1562 {
1563 desc: `retract_nomod`,
1564 in: `retract 1.0.0`,
1565 wantErr: `in:1: no module directive found, so retract cannot be used`,
1566 fix: fixV,
1567 },
1568 }
1569
1570 var modifyEmptyFilesTests = []struct {
1571 desc string
1572 operations func(f *File)
1573 want string
1574 }{
1575 {
1576 desc: `addGoStmt`,
1577 operations: func(f *File) {
1578 f.AddGoStmt("1.20")
1579 },
1580 want: `go 1.20`,
1581 },
1582 }
1583
1584 func fixV(path, version string) (string, error) {
1585 if path != "example.com/m" {
1586 return "", fmt.Errorf("module path must be example.com/m")
1587 }
1588 return "v" + version, nil
1589 }
1590
1591 func TestAddRequire(t *testing.T) {
1592 for _, tt := range addRequireTests {
1593 t.Run(tt.desc, func(t *testing.T) {
1594 testEdit(t, tt.in, tt.out, true, func(f *File) error {
1595 err := f.AddRequire(tt.path, tt.vers)
1596 f.Cleanup()
1597 return err
1598 })
1599 })
1600 }
1601 }
1602
1603 func TestSetRequire(t *testing.T) {
1604 for _, tt := range setRequireTests {
1605 t.Run(tt.desc, func(t *testing.T) {
1606 var mods []*Require
1607 for _, mod := range tt.mods {
1608 mods = append(mods, &Require{
1609 Mod: module.Version{
1610 Path: mod.path,
1611 Version: mod.vers,
1612 },
1613 Indirect: mod.indirect,
1614 })
1615 }
1616
1617 f := testEdit(t, tt.in, tt.out, true, func(f *File) error {
1618 f.SetRequire(mods)
1619 f.Cleanup()
1620 return nil
1621 })
1622
1623 if len(f.Require) != len(mods) {
1624 t.Errorf("after Cleanup, len(Require) = %v; want %v", len(f.Require), len(mods))
1625 }
1626 })
1627 }
1628 }
1629
1630 func TestSetRequireSeparateIndirect(t *testing.T) {
1631 for _, tt := range setRequireSeparateIndirectTests {
1632 t.Run(tt.desc, func(t *testing.T) {
1633 var mods []*Require
1634 for _, mod := range tt.mods {
1635 mods = append(mods, &Require{
1636 Mod: module.Version{
1637 Path: mod.path,
1638 Version: mod.vers,
1639 },
1640 Indirect: mod.indirect,
1641 })
1642 }
1643
1644 f := testEdit(t, tt.in, tt.out, true, func(f *File) error {
1645 f.SetRequireSeparateIndirect(mods)
1646 f.Cleanup()
1647 return nil
1648 })
1649
1650 if len(f.Require) != len(mods) {
1651 t.Errorf("after Cleanup, len(Require) = %v; want %v", len(f.Require), len(mods))
1652 }
1653 })
1654 }
1655 }
1656
1657 func TestAddGo(t *testing.T) {
1658 for _, tt := range addGoTests {
1659 t.Run(tt.desc, func(t *testing.T) {
1660 testEdit(t, tt.in, tt.out, true, func(f *File) error {
1661 return f.AddGoStmt(tt.version)
1662 })
1663 })
1664 }
1665 }
1666
1667 func TestDropGo(t *testing.T) {
1668 for _, tt := range dropGoTests {
1669 t.Run(tt.desc, func(t *testing.T) {
1670 testEdit(t, tt.in, tt.out, true, func(f *File) error {
1671 f.DropGoStmt()
1672 return nil
1673 })
1674 })
1675 }
1676 }
1677
1678 func TestAddToolchain(t *testing.T) {
1679 for _, tt := range addToolchainTests {
1680 t.Run(tt.desc, func(t *testing.T) {
1681 testEdit(t, tt.in, tt.out, true, func(f *File) error {
1682 return f.AddToolchainStmt(tt.version)
1683 })
1684 })
1685 }
1686 }
1687
1688 func TestDropToolchain(t *testing.T) {
1689 for _, tt := range dropToolchainTests {
1690 t.Run(tt.desc, func(t *testing.T) {
1691 testEdit(t, tt.in, tt.out, true, func(f *File) error {
1692 f.DropToolchainStmt()
1693 return nil
1694 })
1695 })
1696 }
1697 }
1698
1699 func TestAddExclude(t *testing.T) {
1700 for _, tt := range addExcludeTests {
1701 t.Run(tt.desc, func(t *testing.T) {
1702 testEdit(t, tt.in, tt.out, true, func(f *File) error {
1703 return f.AddExclude(tt.path, tt.version)
1704 })
1705 })
1706 }
1707 }
1708
1709 func TestAddRetract(t *testing.T) {
1710 for _, tt := range addRetractTests {
1711 t.Run(tt.desc, func(t *testing.T) {
1712 testEdit(t, tt.in, tt.out, true, func(f *File) error {
1713 return f.AddRetract(VersionInterval{Low: tt.low, High: tt.high}, tt.rationale)
1714 })
1715 })
1716 }
1717 }
1718
1719 func TestDropRetract(t *testing.T) {
1720 for _, tt := range dropRetractTests {
1721 t.Run(tt.desc, func(t *testing.T) {
1722 testEdit(t, tt.in, tt.out, true, func(f *File) error {
1723 if err := f.DropRetract(VersionInterval{Low: tt.low, High: tt.high}); err != nil {
1724 return err
1725 }
1726 f.Cleanup()
1727 return nil
1728 })
1729 })
1730 }
1731 }
1732
1733 func TestRetractRationale(t *testing.T) {
1734 for _, tt := range retractRationaleTests {
1735 t.Run(tt.desc, func(t *testing.T) {
1736 f, err := Parse("in", []byte(tt.in), nil)
1737 if err != nil {
1738 t.Fatal(err)
1739 }
1740 if len(f.Retract) != 1 {
1741 t.Fatalf("got %d retract directives; want 1", len(f.Retract))
1742 }
1743 if got := f.Retract[0].Rationale; got != tt.want {
1744 t.Errorf("got %q; want %q", got, tt.want)
1745 }
1746 })
1747 }
1748 }
1749
1750 func TestModuleDeprecated(t *testing.T) {
1751 for _, tt := range moduleDeprecatedTests {
1752 t.Run(tt.desc, func(t *testing.T) {
1753 f, err := Parse("in", []byte(tt.in), nil)
1754 if err != nil {
1755 t.Fatal(err)
1756 }
1757 if f.Module.Deprecated != tt.want {
1758 t.Errorf("got %q; want %q", f.Module.Deprecated, tt.want)
1759 }
1760 })
1761 }
1762 }
1763
1764 func TestSortBlocks(t *testing.T) {
1765 for _, tt := range sortBlocksTests {
1766 t.Run(tt.desc, func(t *testing.T) {
1767 testEdit(t, tt.in, tt.out, tt.strict, func(f *File) error {
1768 f.SortBlocks()
1769 return nil
1770 })
1771 })
1772 }
1773 }
1774
1775 func testEdit(t *testing.T, in, want string, strict bool, transform func(f *File) error) *File {
1776 t.Helper()
1777 parse := Parse
1778 if !strict {
1779 parse = ParseLax
1780 }
1781 f, err := parse("in", []byte(in), nil)
1782 if err != nil {
1783 t.Fatal(err)
1784 }
1785 g, err := parse("out", []byte(want), nil)
1786 if err != nil {
1787 t.Fatal(err)
1788 }
1789 golden, err := g.Format()
1790 if err != nil {
1791 t.Fatal(err)
1792 }
1793
1794 if err := transform(f); err != nil {
1795 t.Fatal(err)
1796 }
1797 out, err := f.Format()
1798 if err != nil {
1799 t.Fatal(err)
1800 }
1801 if !bytes.Equal(out, golden) {
1802 t.Errorf("have:\n%s\nwant:\n%s", out, golden)
1803 }
1804
1805 return f
1806 }
1807
1808 func TestAddRetractValidateVersion(t *testing.T) {
1809 for _, tt := range addRetractValidateVersionTests {
1810 t.Run(tt.desc, func(t *testing.T) {
1811 f := new(File)
1812 if tt.path != "" {
1813 if err := f.AddModuleStmt(tt.path); err != nil {
1814 t.Fatal(err)
1815 }
1816 t.Logf("module %s", AutoQuote(tt.path))
1817 }
1818 interval := VersionInterval{Low: tt.low, High: tt.high}
1819 if err := f.AddRetract(interval, ``); err == nil || err.Error() != tt.wantErr {
1820 errStr := "<nil>"
1821 if err != nil {
1822 errStr = fmt.Sprintf("%#q", err)
1823 }
1824 t.Fatalf("f.AddRetract(%+v, ``) = %s\nwant %#q", interval, errStr, tt.wantErr)
1825 }
1826 })
1827 }
1828 }
1829
1830 func TestAddExcludeValidateVersion(t *testing.T) {
1831 for _, tt := range addExcludeValidateVersionTests {
1832 t.Run(tt.desc, func(t *testing.T) {
1833 f, err := Parse("in", []byte("module m"), nil)
1834 if err != nil {
1835 t.Fatal(err)
1836 }
1837 if err = f.AddExclude(tt.path, tt.version); err == nil || err.Error() != tt.wantErr {
1838 errStr := "<nil>"
1839 if err != nil {
1840 errStr = fmt.Sprintf("%#q", err)
1841 }
1842 t.Fatalf("f.AddExclude(%q, %q) = %s\nwant %#q", tt.path, tt.version, errStr, tt.wantErr)
1843 }
1844 })
1845 }
1846 }
1847
1848 func TestFixVersion(t *testing.T) {
1849 for _, tt := range fixVersionTests {
1850 t.Run(tt.desc, func(t *testing.T) {
1851 inFile, err := Parse("in", []byte(tt.in), tt.fix)
1852 if err != nil {
1853 if tt.wantErr == "" {
1854 t.Fatalf("unexpected error: %v", err)
1855 }
1856 if errMsg := err.Error(); !strings.Contains(errMsg, tt.wantErr) {
1857 t.Fatalf("got error %q; want error containing %q", errMsg, tt.wantErr)
1858 }
1859 return
1860 }
1861 got, err := inFile.Format()
1862 if err != nil {
1863 t.Fatal(err)
1864 }
1865
1866 outFile, err := Parse("out", []byte(tt.want), nil)
1867 if err != nil {
1868 t.Fatal(err)
1869 }
1870 want, err := outFile.Format()
1871 if err != nil {
1872 t.Fatal(err)
1873 }
1874
1875 if !bytes.Equal(got, want) {
1876 t.Fatalf("got:\n%s\nwant:\n%s", got, want)
1877 }
1878 })
1879 }
1880 }
1881
1882 func TestAddOnEmptyFile(t *testing.T) {
1883 for _, tt := range modifyEmptyFilesTests {
1884 t.Run(tt.desc, func(t *testing.T) {
1885 f := &File{}
1886 tt.operations(f)
1887
1888 expect, err := Parse("out", []byte(tt.want), nil)
1889 if err != nil {
1890 t.Fatal(err)
1891 }
1892 golden, err := expect.Format()
1893 if err != nil {
1894 t.Fatal(err)
1895 }
1896 got, err := f.Format()
1897 if err != nil {
1898 t.Fatal(err)
1899 }
1900
1901 if !bytes.Equal(got, golden) {
1902 t.Fatalf("got:\n%s\nwant:\n%s", got, golden)
1903 }
1904 })
1905 }
1906 }
1907
View as plain text