blob: 03123ed409c4649fc4047f2687172fef5abc6861 [file] [log] [blame]
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package modfile
import (
"bytes"
"testing"
"golang.org/x/mod/module"
)
var addRequireTests = []struct {
desc string
in string
path string
vers string
out string
}{
{
`existing`,
`
module m
require x.y/z v1.2.3
`,
"x.y/z", "v1.5.6",
`
module m
require x.y/z v1.5.6
`,
},
{
`new`,
`
module m
require x.y/z v1.2.3
`,
"x.y/w", "v1.5.6",
`
module m
require (
x.y/z v1.2.3
x.y/w v1.5.6
)
`,
},
{
`new2`,
`
module m
require x.y/z v1.2.3
require x.y/q/v2 v2.3.4
`,
"x.y/w", "v1.5.6",
`
module m
require x.y/z v1.2.3
require (
x.y/q/v2 v2.3.4
x.y/w v1.5.6
)
`,
},
}
var setRequireTests = []struct {
desc string
in string
mods []struct {
path string
vers string
indirect bool
}
out string
}{
{
`existing`,
`module m
require (
x.y/b v1.2.3
x.y/a v1.2.3
x.y/d v1.2.3
)
`,
[]struct {
path string
vers string
indirect bool
}{
{"x.y/a", "v1.2.3", false},
{"x.y/b", "v1.2.3", false},
{"x.y/c", "v1.2.3", false},
},
`module m
require (
x.y/a v1.2.3
x.y/b v1.2.3
x.y/c v1.2.3
)
`,
},
{
`existing_indirect`,
`module m
require (
x.y/a v1.2.3
x.y/b v1.2.3 //
x.y/c v1.2.3 //c
x.y/d v1.2.3 // c
x.y/e v1.2.3 // indirect
x.y/f v1.2.3 //indirect
x.y/g v1.2.3 // indirect
)
`,
[]struct {
path string
vers string
indirect bool
}{
{"x.y/a", "v1.2.3", true},
{"x.y/b", "v1.2.3", true},
{"x.y/c", "v1.2.3", true},
{"x.y/d", "v1.2.3", true},
{"x.y/e", "v1.2.3", true},
{"x.y/f", "v1.2.3", true},
{"x.y/g", "v1.2.3", true},
},
`module m
require (
x.y/a v1.2.3 // indirect
x.y/b v1.2.3 // indirect
x.y/c v1.2.3 // indirect; c
x.y/d v1.2.3 // indirect; c
x.y/e v1.2.3 // indirect
x.y/f v1.2.3 //indirect
x.y/g v1.2.3 // indirect
)
`,
},
}
var addGoTests = []struct {
desc string
in string
version string
out string
}{
{
`module_only`,
`module m
`,
`1.14`,
`module m
go 1.14
`,
},
{
`module_before_require`,
`module m
require x.y/a v1.2.3
`,
`1.14`,
`module m
go 1.14
require x.y/a v1.2.3
`,
},
{
`require_before_module`,
`require x.y/a v1.2.3
module example.com/inverted
`,
`1.14`,
`require x.y/a v1.2.3
module example.com/inverted
go 1.14
`,
},
{
`require_only`,
`require x.y/a v1.2.3
`,
`1.14`,
`require x.y/a v1.2.3
go 1.14
`,
},
}
var addRetractTests = []struct {
desc string
in string
low string
high string
rationale string
out string
}{
{
`new_singleton`,
`module m
`,
`v1.2.3`,
`v1.2.3`,
``,
`module m
retract v1.2.3
`,
},
{
`new_interval`,
`module m
`,
`v1.0.0`,
`v1.1.0`,
``,
`module m
retract [v1.0.0, v1.1.0]`,
},
{
`duplicate_with_rationale`,
`module m
retract v1.2.3
`,
`v1.2.3`,
`v1.2.3`,
`bad`,
`module m
retract (
v1.2.3
// bad
v1.2.3
)
`,
},
{
`duplicate_multiline_rationale`,
`module m
retract [v1.2.3, v1.2.3]
`,
`v1.2.3`,
`v1.2.3`,
`multi
line`,
`module m
retract (
[v1.2.3, v1.2.3]
// multi
// line
v1.2.3
)
`,
},
{
`duplicate_interval`,
`module m
retract [v1.0.0, v1.1.0]
`,
`v1.0.0`,
`v1.1.0`,
``,
`module m
retract (
[v1.0.0, v1.1.0]
[v1.0.0, v1.1.0]
)
`,
},
{
`duplicate_singleton`,
`module m
retract v1.2.3
`,
`v1.2.3`,
`v1.2.3`,
``,
`module m
retract (
v1.2.3
v1.2.3
)
`,
},
}
var dropRetractTests = []struct {
desc string
in string
low string
high string
out string
}{
{
`singleton_no_match`,
`module m
retract v1.2.3
`,
`v1.0.0`,
`v1.0.0`,
`module m
retract v1.2.3
`,
},
{
`singleton_match_one`,
`module m
retract v1.2.2
retract v1.2.3
retract v1.2.4
`,
`v1.2.3`,
`v1.2.3`,
`module m
retract v1.2.2
retract v1.2.4
`,
},
{
`singleton_match_all`,
`module m
retract v1.2.3 // first
retract v1.2.3 // second
`,
`v1.2.3`,
`v1.2.3`,
`module m
`,
},
{
`interval_match`,
`module m
retract [v1.2.3, v1.2.3]
`,
`v1.2.3`,
`v1.2.3`,
`module m
`,
},
{
`interval_superset_no_match`,
`module m
retract [v1.0.0, v1.1.0]
`,
`v1.0.0`,
`v1.2.0`,
`module m
retract [v1.0.0, v1.1.0]
`,
},
{
`singleton_match_middle`,
`module m
retract v1.2.3
`,
`v1.2.3`,
`v1.2.3`,
`module m
`,
},
{
`interval_match_middle_block`,
`module m
retract (
v1.0.0
[v1.1.0, v1.2.0]
v1.3.0
)
`,
`v1.1.0`,
`v1.2.0`,
`module m
retract (
v1.0.0
v1.3.0
)
`,
},
{
`interval_match_all`,
`module m
retract [v1.0.0, v1.1.0]
retract [v1.0.0, v1.1.0]
`,
`v1.0.0`,
`v1.1.0`,
`module m
`,
},
}
var retractRationaleTests = []struct {
desc, in, want string
}{
{
`no_comment`,
`module m
retract v1.0.0`,
``,
},
{
`prefix_one`,
`module m
// prefix
retract v1.0.0
`,
`prefix`,
},
{
`prefix_multiline`,
`module m
// one
//
// two
//
// three
retract v1.0.0`,
`one
two
three`,
},
{
`suffix`,
`module m
retract v1.0.0 // suffix
`,
`suffix`,
},
{
`prefix_suffix_after`,
`module m
// prefix
retract v1.0.0 // suffix
`,
`prefix
suffix`,
},
{
`block_only`,
`// block
retract (
v1.0.0
)
`,
`block`,
},
{
`block_and_line`,
`// block
retract (
// line
v1.0.0
)
`,
`line`,
},
}
var sortBlocksTests = []struct {
desc, in, out string
strict bool
}{
{
`exclude_duplicates_removed`,
`module m
exclude x.y/z v1.0.0 // a
exclude x.y/z v1.0.0 // b
exclude (
x.y/w v1.1.0
x.y/z v1.0.0 // c
)
`,
`module m
exclude x.y/z v1.0.0 // a
exclude (
x.y/w v1.1.0
)`,
true,
},
{
`replace_duplicates_removed`,
`module m
replace x.y/z v1.0.0 => ./a
replace x.y/z v1.1.0 => ./b
replace (
x.y/z v1.0.0 => ./c
)
`,
`module m
replace x.y/z v1.1.0 => ./b
replace (
x.y/z v1.0.0 => ./c
)
`,
true,
},
{
`retract_duplicates_not_removed`,
`module m
// block
retract (
v1.0.0 // one
v1.0.0 // two
)`,
`module m
// block
retract (
v1.0.0 // one
v1.0.0 // two
)`,
true,
},
// Tests below this point just check sort order.
// Non-retract blocks are sorted lexicographically in ascending order.
// retract blocks are sorted using semver in descending order.
{
`sort_lexicographically`,
`module m
sort (
aa
cc
bb
zz
v1.2.0
v1.11.0
)`,
`module m
sort (
aa
bb
cc
v1.11.0
v1.2.0
zz
)
`,
false,
},
{
`sort_retract`,
`module m
retract (
[v1.2.0, v1.3.0]
[v1.1.0, v1.3.0]
[v1.1.0, v1.2.0]
v1.0.0
v1.1.0
v1.2.0
v1.3.0
v1.4.0
)
`,
`module m
retract (
v1.4.0
v1.3.0
[v1.2.0, v1.3.0]
v1.2.0
[v1.1.0, v1.3.0]
[v1.1.0, v1.2.0]
v1.1.0
v1.0.0
)
`,
false,
},
}
var addRetractValidateVersionTests = []struct {
dsc, low, high string
}{
{
"blank_version",
"",
"",
},
{
"missing_prefix",
"1.0.0",
"1.0.0",
},
{
"non_canonical",
"v1.2",
"v1.2",
},
{
"invalid_range",
"v1.2.3",
"v1.3",
},
}
var addExcludeValidateVersionTests = []struct {
dsc, ver string
}{
{
"blank_version",
"",
},
{
"missing_prefix",
"1.0.0",
},
{
"non_canonical",
"v1.2",
},
}
func TestAddRequire(t *testing.T) {
for _, tt := range addRequireTests {
t.Run(tt.desc, func(t *testing.T) {
testEdit(t, tt.in, tt.out, true, func(f *File) error {
return f.AddRequire(tt.path, tt.vers)
})
})
}
}
func TestSetRequire(t *testing.T) {
for _, tt := range setRequireTests {
t.Run(tt.desc, func(t *testing.T) {
var mods []*Require
for _, mod := range tt.mods {
mods = append(mods, &Require{
Mod: module.Version{
Path: mod.path,
Version: mod.vers,
},
Indirect: mod.indirect,
})
}
f := testEdit(t, tt.in, tt.out, true, func(f *File) error {
f.SetRequire(mods)
return nil
})
f.Cleanup()
if len(f.Require) != len(mods) {
t.Errorf("after Cleanup, len(Require) = %v; want %v", len(f.Require), len(mods))
}
})
}
}
func TestAddGo(t *testing.T) {
for _, tt := range addGoTests {
t.Run(tt.desc, func(t *testing.T) {
testEdit(t, tt.in, tt.out, true, func(f *File) error {
return f.AddGoStmt(tt.version)
})
})
}
}
func TestAddRetract(t *testing.T) {
for _, tt := range addRetractTests {
t.Run(tt.desc, func(t *testing.T) {
testEdit(t, tt.in, tt.out, true, func(f *File) error {
return f.AddRetract(VersionInterval{Low: tt.low, High: tt.high}, tt.rationale)
})
})
}
}
func TestDropRetract(t *testing.T) {
for _, tt := range dropRetractTests {
t.Run(tt.desc, func(t *testing.T) {
testEdit(t, tt.in, tt.out, true, func(f *File) error {
if err := f.DropRetract(VersionInterval{Low: tt.low, High: tt.high}); err != nil {
return err
}
f.Cleanup()
return nil
})
})
}
}
func TestRetractRationale(t *testing.T) {
for _, tt := range retractRationaleTests {
t.Run(tt.desc, func(t *testing.T) {
f, err := Parse("in", []byte(tt.in), nil)
if err != nil {
t.Fatal(err)
}
if len(f.Retract) != 1 {
t.Fatalf("got %d retract directives; want 1", len(f.Retract))
}
if got := f.Retract[0].Rationale; got != tt.want {
t.Errorf("got %q; want %q", got, tt.want)
}
})
}
}
func TestSortBlocks(t *testing.T) {
for _, tt := range sortBlocksTests {
t.Run(tt.desc, func(t *testing.T) {
testEdit(t, tt.in, tt.out, tt.strict, func(f *File) error {
f.SortBlocks()
return nil
})
})
}
}
func testEdit(t *testing.T, in, want string, strict bool, transform func(f *File) error) *File {
t.Helper()
parse := Parse
if !strict {
parse = ParseLax
}
f, err := parse("in", []byte(in), nil)
if err != nil {
t.Fatal(err)
}
g, err := parse("out", []byte(want), nil)
if err != nil {
t.Fatal(err)
}
golden, err := g.Format()
if err != nil {
t.Fatal(err)
}
if err := transform(f); err != nil {
t.Fatal(err)
}
out, err := f.Format()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(out, golden) {
t.Errorf("have:\n%s\nwant:\n%s", out, golden)
}
return f
}
func TestAddRetractValidateVersion(t *testing.T) {
for _, tt := range addRetractValidateVersionTests {
t.Run(tt.dsc, func(t *testing.T) {
f, err := Parse("in", []byte("module m"), nil)
if err != nil {
t.Fatal(err)
}
if err = f.AddRetract(VersionInterval{Low: tt.low, High: tt.high}, ""); err == nil {
t.Fatal("expected AddRetract to complain about version format")
}
})
}
}
func TestAddExcludeValidateVersion(t *testing.T) {
for _, tt := range addExcludeValidateVersionTests {
t.Run(tt.dsc, func(t *testing.T) {
f, err := Parse("in", []byte("module m"), nil)
if err != nil {
t.Fatal(err)
}
if err = f.AddExclude("aa", tt.ver); err == nil {
t.Fatal("expected AddExclude to complain about version format")
}
})
}
}