blob: 26879fb3b65c526c0ed9c412e3a7d4c909a6f6b8 [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"
"fmt"
"strings"
"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
`,
},
{
`existing2`,
`
module m
require (
x.y/z v1.2.3 // first
x.z/a v0.1.0 // first-a
)
require x.y/z v1.4.5 // second
require (
x.y/z v1.6.7 // third
x.z/a v0.2.0 // third-a
)
`,
"x.y/z", "v1.8.9",
`
module m
require (
x.y/z v1.8.9 // first
x.z/a v0.1.0 // first-a
)
require x.z/a v0.2.0 // third-a
`,
},
{
`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
)
`,
},
}
type require struct {
path, vers string
indirect bool
}
var setRequireTests = []struct {
desc string
in string
mods []require
out string
}{
{
`https://golang.org/issue/45932`,
`module m
require (
x.y/a v1.2.3 //indirect
x.y/b v1.2.3
x.y/c v1.2.3
)
`,
[]require{
{"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`,
`module m
require (
x.y/b v1.2.3
x.y/a v1.2.3
x.y/d v1.2.3
)
`,
[]require{
{"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
)
`,
[]require{
{"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
)
`,
},
{
`existing_multi`,
`module m
require x.y/a v1.2.3
require x.y/b v1.2.3
require x.y/c v1.0.0 // not v1.2.3!
require x.y/d v1.2.3 // comment kept
require x.y/e v1.2.3 // comment kept
require x.y/f v1.2.3 // indirect
require x.y/g v1.2.3 // indirect
`,
[]require{
{"x.y/h", "v1.2.3", false},
{"x.y/a", "v1.2.3", false},
{"x.y/b", "v1.2.3", false},
{"x.y/c", "v1.2.3", false},
{"x.y/d", "v1.2.3", false},
{"x.y/e", "v1.2.3", true},
{"x.y/f", "v1.2.3", false},
{"x.y/g", "v1.2.3", false},
},
`module m
require x.y/a v1.2.3
require x.y/b v1.2.3
require x.y/c v1.2.3 // not v1.2.3!
require x.y/d v1.2.3 // comment kept
require x.y/e v1.2.3 // indirect; comment kept
require x.y/f v1.2.3
require (
x.y/g v1.2.3
x.y/h v1.2.3
)
`,
},
{
`existing_duplicate`,
`module m
require (
x.y/a v1.0.0 // zero
x.y/a v1.1.0 // one
x.y/a v1.2.3 // two
)
`,
[]require{
{"x.y/a", "v1.2.3", true},
},
`module m
require x.y/a v1.2.3 // indirect; zero
`,
},
{
`existing_duplicate_multi`,
`module m
require x.y/a v1.0.0 // zero
require x.y/a v1.1.0 // one
require x.y/a v1.2.3 // two
`,
[]require{
{"x.y/a", "v1.2.3", true},
},
`module m
require x.y/a v1.2.3 // indirect; zero
`,
},
}
var setRequireSeparateIndirectTests = []struct {
desc string
in string
mods []require
out string
}{
{
`https://golang.org/issue/45932`,
`module m
require (
x.y/a v1.2.3 //indirect
x.y/b v1.2.3
x.y/c v1.2.3
)
`,
[]require{
{"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`,
`module m
require (
x.y/b v1.2.3
x.y/a v1.2.3
x.y/d v1.2.3
)
`,
[]require{
{"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
)
`,
[]require{
{"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
)
`,
},
{
`existing_line`,
`module m
require x.y/a v1.0.0
require x.y/c v1.0.0 // indirect
`,
[]require{
{"x.y/a", "v1.2.3", false},
{"x.y/b", "v1.2.3", false},
{"x.y/c", "v1.2.3", true},
{"x.y/d", "v1.2.3", true},
},
`module m
require (
x.y/a v1.2.3
x.y/b v1.2.3
)
require (
x.y/c v1.2.3 // indirect
x.y/d v1.2.3 // indirect
)`,
},
{
`existing_multi`,
`module m
require x.y/a v1.2.3
require x.y/b v1.2.3 // demoted to indirect
require x.y/c v1.0.0 // not v1.2.3!
require x.y/d v1.2.3 // comment kept
require x.y/e v1.2.3 // comment kept
require x.y/f v1.2.3 // indirect; promoted to direct
// promoted to direct
require x.y/g v1.2.3 // indirect
require x.y/i v1.2.3 // indirect
require x.y/j v1.2.3 // indirect
`,
[]require{
{"x.y/h", "v1.2.3", false}, // out of alphabetical order
{"x.y/i", "v1.2.3", true},
{"x.y/j", "v1.2.3", true},
{"x.y/a", "v1.2.3", false},
{"x.y/b", "v1.2.3", true},
{"x.y/c", "v1.2.3", false},
{"x.y/d", "v1.2.3", false},
{"x.y/e", "v1.2.3", true},
{"x.y/f", "v1.2.3", false},
{"x.y/g", "v1.2.3", false},
},
`module m
require (
x.y/a v1.2.3
x.y/h v1.2.3
)
require x.y/b v1.2.3 // indirect; demoted to indirect
require x.y/c v1.2.3 // not v1.2.3!
require x.y/d v1.2.3 // comment kept
require x.y/e v1.2.3 // indirect; comment kept
require x.y/f v1.2.3 // promoted to direct
// promoted to direct
require x.y/g v1.2.3
require x.y/i v1.2.3 // indirect
require x.y/j v1.2.3 // indirect
`,
},
{
`existing_duplicate`,
`module m
require (
x.y/a v1.0.0 // zero
x.y/a v1.1.0 // one
x.y/a v1.2.3 // two
)
`,
[]require{
{"x.y/a", "v1.2.3", true},
},
`module m
require x.y/a v1.2.3 // indirect; zero
`,
},
{
`existing_duplicate_multi`,
`module m
require x.y/a v1.0.0 // zero
require x.y/a v1.1.0 // one
require x.y/a v1.2.3 // two
`,
[]require{
{"x.y/a", "v1.2.3", true},
},
`module m
require x.y/a v1.2.3 // indirect; zero
`,
},
{
`existing_duplicate_mix_indirect`,
`module m
require (
x.y/a v1.0.0 // zero
x.y/a v1.1.0 // indirect; one
x.y/a v1.2.3 // indirect; two
)
`,
[]require{
{"x.y/a", "v1.2.3", true},
},
`module m
require x.y/a v1.2.3 // indirect; zero
`,
},
{
`existing_duplicate_mix_direct`,
`module m
require (
x.y/a v1.0.0 // indirect; zero
x.y/a v1.1.0 // one
x.y/a v1.2.3 // two
)
`,
[]require{
{"x.y/a", "v1.2.3", false},
},
`module m
require x.y/a v1.2.3 // zero
`,
},
{
`add_indirect_after_last_direct`,
`module m
require (
x.y/a v1.0.0 // comment a preserved
x.y/d v1.0.0 // comment d preserved
)
require (
x.y/b v1.0.0 // comment b preserved
x.y/e v1.0.0 // comment e preserved
)
go 1.17
`,
[]require{
{"x.y/a", "v1.2.3", false},
{"x.y/b", "v1.2.3", false},
{"x.y/c", "v1.2.3", true},
{"x.y/d", "v1.2.3", false},
{"x.y/e", "v1.2.3", false},
{"x.y/f", "v1.2.3", true},
},
`module m
require (
x.y/a v1.2.3 // comment a preserved
x.y/d v1.2.3 // comment d preserved
)
require (
x.y/b v1.2.3 // comment b preserved
x.y/e v1.2.3 // comment e preserved
)
require (
x.y/c v1.2.3 // indirect
x.y/f v1.2.3 // indirect
)
go 1.17
`,
},
{
`add_direct_before_first_indirect`,
`module m
require (
x.y/b v1.0.0 // indirect; comment b preserved
x.y/e v1.0.0 // indirect; comment d preserved
)
require (
x.y/c v1.0.0 // indirect; comment c preserved
x.y/f v1.0.0 // indirect; comment e preserved
)
`,
[]require{
{"x.y/a", "v1.2.3", false},
{"x.y/b", "v1.2.3", true},
{"x.y/c", "v1.2.3", true},
{"x.y/d", "v1.2.3", false},
{"x.y/e", "v1.2.3", true},
{"x.y/f", "v1.2.3", true},
},
`module m
require (
x.y/b v1.2.3 // indirect; comment b preserved
x.y/e v1.2.3 // indirect; comment d preserved
)
require (
x.y/c v1.2.3 // indirect; comment c preserved
x.y/f v1.2.3 // indirect; comment e preserved
)
require (
x.y/a v1.2.3
x.y/d v1.2.3
)
`,
},
{
`add_indirect_after_mixed`,
`module m
require (
x.y/a v1.0.0
x.y/b v1.0.0 // indirect
)
`,
[]require{
{"x.y/a", "v1.2.3", false},
{"x.y/b", "v1.2.3", true},
{"x.y/c", "v1.2.3", true},
{"x.y/d", "v1.2.3", false},
{"x.y/e", "v1.2.3", true},
},
`module m
require (
x.y/a v1.2.3
x.y/d v1.2.3
)
require (
x.y/b v1.2.3 // indirect
x.y/c v1.2.3 // indirect
x.y/e v1.2.3 // indirect
)
`,
},
{
`preserve_block_comment_indirect_to_direct`,
`module m
// save
require (
x.y/a v1.2.3 // indirect
)
`,
[]require{
{"x.y/a", "v1.2.3", false},
},
`module m
// save
require x.y/a v1.2.3
`,
},
{
`preserve_block_comment_direct_to_indirect`,
`module m
// save
require (
x.y/a v1.2.3
)
`,
[]require{
{"x.y/a", "v1.2.3", true},
},
`module m
// save
require x.y/a v1.2.3 // indirect
`,
},
{
`regroup_flat_uncommented_block`,
`module m
require (
x.y/a v1.0.0 // a
x.y/b v1.0.0 // indirect; b
x.y/c v1.0.0 // indirect
)`,
[]require{
{"x.y/a", "v1.2.3", false},
{"x.y/b", "v1.2.3", true},
{"x.y/c", "v1.2.3", true},
{"x.y/d", "v1.2.3", false},
},
`module m
require (
x.y/a v1.2.3 // a
x.y/d v1.2.3
)
require (
x.y/b v1.2.3 // indirect; b
x.y/c v1.2.3 // indirect
)`,
},
{
`dont_regroup_flat_commented_block`,
`module m
// dont regroup
require (
x.y/a v1.0.0
x.y/b v1.0.0 // indirect
x.y/c v1.0.0 // indirect
)`,
[]require{
{"x.y/a", "v1.2.3", false},
{"x.y/b", "v1.2.3", true},
{"x.y/c", "v1.2.3", true},
{"x.y/d", "v1.2.3", false},
},
`module m
// dont regroup
require (
x.y/a v1.2.3
x.y/b v1.2.3 // indirect
x.y/c v1.2.3 // indirect
)
require x.y/d v1.2.3`,
},
}
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 addExcludeTests = []struct {
desc string
in string
path string
version string
out string
}{
{
`compatible`,
`module m
`,
`example.com`,
`v1.2.3`,
`module m
exclude example.com v1.2.3
`,
},
{
`gopkg.in v0`,
`module m
`,
`gopkg.in/foo.v0`,
`v0.2.3`,
`module m
exclude gopkg.in/foo.v0 v0.2.3
`,
},
{
`gopkg.in v1`,
`module m
`,
`gopkg.in/foo.v1`,
`v1.2.3`,
`module m
exclude gopkg.in/foo.v1 v1.2.3
`,
},
}
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 moduleDeprecatedTests = []struct {
desc, in, want string
}{
// retractRationaleTests exercises some of the same code, so these tests
// don't exhaustively cover comment extraction.
{
`no_comment`,
`module m`,
``,
},
{
`other_comment`,
`// yo
module m`,
``,
},
{
`deprecated_no_colon`,
`//Deprecated
module m`,
``,
},
{
`deprecated_no_space`,
`//Deprecated:blah
module m`,
`blah`,
},
{
`deprecated_simple`,
`// Deprecated: blah
module m`,
`blah`,
},
{
`deprecated_lowercase`,
`// deprecated: blah
module m`,
``,
},
{
`deprecated_multiline`,
`// Deprecated: one
// two
module m`,
"one\ntwo",
},
{
`deprecated_mixed`,
`// some other comment
// Deprecated: blah
module m`,
``,
},
{
`deprecated_middle`,
`// module m is Deprecated: blah
module m`,
``,
},
{
`deprecated_multiple`,
`// Deprecated: a
// Deprecated: b
module m`,
"a\nDeprecated: b",
},
{
`deprecated_paragraph`,
`// Deprecated: a
// b
//
// c
module m`,
"a\nb",
},
{
`deprecated_paragraph_space`,
`// Deprecated: the next line has a space
//
// c
module m`,
"the next line has a space",
},
{
`deprecated_suffix`,
`module m // Deprecated: blah`,
`blah`,
},
{
`deprecated_mixed_suffix`,
`// some other comment
module m // Deprecated: blah`,
``,
},
{
`deprecated_mixed_suffix_paragraph`,
`// some other comment
//
module m // Deprecated: blah`,
`blah`,
},
{
`deprecated_block`,
`// Deprecated: blah
module (
m
)`,
`blah`,
},
}
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 {
desc string
path string
low, high string
wantErr string
}{
{
`blank_version`,
`example.com/m`,
``,
``,
`version "" invalid: must be of the form v1.2.3`,
},
{
`missing prefix`,
`example.com/m`,
`1.0.0`,
`1.0.0`,
`version "1.0.0" invalid: must be of the form v1.2.3`,
},
{
`non-canonical`,
`example.com/m`,
`v1.2`,
`v1.2`,
`version "v1.2" invalid: must be of the form v1.2.3`,
},
{
`invalid range`,
`example.com/m`,
`v1.2.3`,
`v1.3`,
`version "v1.3" invalid: must be of the form v1.2.3`,
},
{
`mismatched major`,
`example.com/m/v2`,
`v1.0.0`,
`v1.0.0`,
`version "v1.0.0" invalid: should be v2, not v1`,
},
{
`missing +incompatible`,
`example.com/m`,
`v2.0.0`,
`v2.0.0`,
`version "v2.0.0" invalid: should be v2.0.0+incompatible (or module example.com/m/v2)`,
},
}
var addExcludeValidateVersionTests = []struct {
desc string
path string
version string
wantErr string
}{
{
`blank version`,
`example.com/m`,
``,
`version "" invalid: must be of the form v1.2.3`,
},
{
`missing prefix`,
`example.com/m`,
`1.0.0`,
`version "1.0.0" invalid: must be of the form v1.2.3`,
},
{
`non-canonical`,
`example.com/m`,
`v1.2`,
`version "v1.2" invalid: must be of the form v1.2.3`,
},
{
`mismatched major`,
`example.com/m/v2`,
`v1.2.3`,
`version "v1.2.3" invalid: should be v2, not v1`,
},
{
`missing +incompatible`,
`example.com/m`,
`v2.3.4`,
`version "v2.3.4" invalid: should be v2.3.4+incompatible (or module example.com/m/v2)`,
},
}
var fixVersionTests = []struct {
desc, in, want, wantErr string
fix VersionFixer
}{
{
desc: `require`,
in: `require example.com/m 1.0.0`,
want: `require example.com/m v1.0.0`,
fix: fixV,
},
{
desc: `replace`,
in: `replace example.com/m 1.0.0 => example.com/m 1.1.0`,
want: `replace example.com/m v1.0.0 => example.com/m v1.1.0`,
fix: fixV,
},
{
desc: `replace_version_in_path`,
in: `replace example.com/m@v1.0.0 => example.com/m@v1.1.0`,
wantErr: `replacement module must match format 'path version', not 'path@version'`,
fix: fixV,
},
{
desc: `replace_version_in_later_path`,
in: `replace example.com/m => example.com/m@v1.1.0`,
wantErr: `replacement module must match format 'path version', not 'path@version'`,
fix: fixV,
},
{
desc: `exclude`,
in: `exclude example.com/m 1.0.0`,
want: `exclude example.com/m v1.0.0`,
fix: fixV,
},
{
desc: `retract_single`,
in: `module example.com/m
retract 1.0.0`,
want: `module example.com/m
retract v1.0.0`,
fix: fixV,
},
{
desc: `retract_interval`,
in: `module example.com/m
retract [1.0.0, 1.1.0]`,
want: `module example.com/m
retract [v1.0.0, v1.1.0]`,
fix: fixV,
},
{
desc: `retract_nomod`,
in: `retract 1.0.0`,
wantErr: `in:1: no module directive found, so retract cannot be used`,
fix: fixV,
},
}
func fixV(path, version string) (string, error) {
if path != "example.com/m" {
return "", fmt.Errorf("module path must be example.com/m")
}
return "v" + version, nil
}
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 {
err := f.AddRequire(tt.path, tt.vers)
f.Cleanup()
return err
})
})
}
}
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)
f.Cleanup()
return nil
})
if len(f.Require) != len(mods) {
t.Errorf("after Cleanup, len(Require) = %v; want %v", len(f.Require), len(mods))
}
})
}
}
func TestSetRequireSeparateIndirect(t *testing.T) {
for _, tt := range setRequireSeparateIndirectTests {
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.SetRequireSeparateIndirect(mods)
f.Cleanup()
return nil
})
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 TestAddExclude(t *testing.T) {
for _, tt := range addExcludeTests {
t.Run(tt.desc, func(t *testing.T) {
testEdit(t, tt.in, tt.out, true, func(f *File) error {
return f.AddExclude(tt.path, 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 TestModuleDeprecated(t *testing.T) {
for _, tt := range moduleDeprecatedTests {
t.Run(tt.desc, func(t *testing.T) {
f, err := Parse("in", []byte(tt.in), nil)
if err != nil {
t.Fatal(err)
}
if f.Module.Deprecated != tt.want {
t.Errorf("got %q; want %q", f.Module.Deprecated, 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.desc, func(t *testing.T) {
f := new(File)
if tt.path != "" {
if err := f.AddModuleStmt(tt.path); err != nil {
t.Fatal(err)
}
t.Logf("module %s", AutoQuote(tt.path))
}
interval := VersionInterval{Low: tt.low, High: tt.high}
if err := f.AddRetract(interval, ``); err == nil || err.Error() != tt.wantErr {
errStr := "<nil>"
if err != nil {
errStr = fmt.Sprintf("%#q", err)
}
t.Fatalf("f.AddRetract(%+v, ``) = %s\nwant %#q", interval, errStr, tt.wantErr)
}
})
}
}
func TestAddExcludeValidateVersion(t *testing.T) {
for _, tt := range addExcludeValidateVersionTests {
t.Run(tt.desc, func(t *testing.T) {
f, err := Parse("in", []byte("module m"), nil)
if err != nil {
t.Fatal(err)
}
if err = f.AddExclude(tt.path, tt.version); err == nil || err.Error() != tt.wantErr {
errStr := "<nil>"
if err != nil {
errStr = fmt.Sprintf("%#q", err)
}
t.Fatalf("f.AddExclude(%q, %q) = %s\nwant %#q", tt.path, tt.version, errStr, tt.wantErr)
}
})
}
}
func TestFixVersion(t *testing.T) {
for _, tt := range fixVersionTests {
t.Run(tt.desc, func(t *testing.T) {
inFile, err := Parse("in", []byte(tt.in), tt.fix)
if err != nil {
if tt.wantErr == "" {
t.Fatalf("unexpected error: %v", err)
}
if errMsg := err.Error(); !strings.Contains(errMsg, tt.wantErr) {
t.Fatalf("got error %q; want error containing %q", errMsg, tt.wantErr)
}
return
}
got, err := inFile.Format()
if err != nil {
t.Fatal(err)
}
outFile, err := Parse("out", []byte(tt.want), nil)
if err != nil {
t.Fatal(err)
}
want, err := outFile.Format()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(got, want) {
t.Fatalf("got:\n%s\nwant:\n%s", got, want)
}
})
}
}