blob: eb9e03eb8d377c57039624d364e18b99290a5524 [file] [log] [blame]
// Copyright 2019 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 version
import (
"testing"
"golang.org/x/mod/semver"
)
func TestForSorting(t *testing.T) {
for _, test := range []struct {
in, want string
}{
{"v1.2.3", "1,2,3~"},
{"v12.48.301", "a12,a48,b301~"},
{"v0.9.3-alpha.1", "0,9,3,~alpha,1"},
{"v1.2.3-rc.20150901.-", "1,2,3,~rc,g20150901,~-"},
{"v1.2.3-alpha.789+build", "1,2,3,~alpha,b789"},
} {
got := ForSorting(test.in)
if got != test.want {
t.Errorf("ForSorting(%s) = %s, want %s", test.in, got, test.want)
}
}
}
func TestForSortingOrder(t *testing.T) {
// A list of valid semantic versions, in order.
semvers := []string{
"v0.0.0-20180713131340-b395d2d6f5ee",
"v0.0.0-20190124233150-8f7fa2680c82",
"v0.0.0",
"v0.0.1",
"v0.1.0",
"v1.0.0-alpha",
"v1.0.0-alpha.1",
"v1.0.0-alpha.beta",
"v1.0.0-beta",
"v1.0.0-beta.2",
"v1.0.0-beta.11",
"v1.0.0-rc.1",
"v1.0.0",
"v1.2.0",
"v1.11.0",
"v1.12.0-alph.1",
"v1.12.0-alpha",
// These next two would order incorrectly if we used '.' as a separator, because
// '.' comes after '-'.
"v2.0.0-z.a",
"v2.0.0-z-",
// These next two would sort incorrectly if we did not prepend non-numeric components
// with a '~'.
"v2.1.0-a.1",
"v2.1.0-a.-",
}
// Check that the test has the ordering right according to the semver package.
for i := range semvers {
if !semver.IsValid(semvers[i]) {
t.Fatalf("test is broken: bad semver: %s", semvers[i])
}
if i > 0 {
if semver.Compare(semvers[i-1], semvers[i]) >= 0 {
t.Fatalf("test is broken: %s is not less than %s", semvers[i-1], semvers[i])
}
}
}
// Check that ForSorting produces the correct ordering.
var prev string
for _, v := range semvers {
got := ForSorting(v)
if prev != "" && prev >= got {
t.Errorf("%s: %s >= %s, want less than", v, prev, got)
}
prev = got
}
}
func TestAppendNumericPrefix(t *testing.T) {
for _, test := range []struct {
n int
want string
}{
{1, ""},
{2, "a"},
{3, "b"},
{26, "y"},
{53, "zz"}, // 53-1 = 26*2
{100, "zzzu"}, // 100-1 = 26*3 + 21
} {
got := string(appendNumericPrefix(nil, test.n))
if got != test.want {
t.Errorf("%d: got %s, want %s", test.n, got, test.want)
}
}
}
func TestParseVersionType(t *testing.T) {
testCases := []struct {
name, version string
wantVersionType Type
wantErr bool
}{
{
name: "pseudo major version",
version: "v1.0.0-20190311183353-d8887717615a",
wantVersionType: TypePseudo,
},
{
name: "pseudo prerelease version",
version: "v1.2.3-pre.0.20190311183353-d8887717615a",
wantVersionType: TypePseudo,
},
{
name: "pseudo minor version",
version: "v1.2.4-0.20190311183353-d8887717615a",
wantVersionType: TypePseudo,
},
{
name: "pseudo version invalid",
version: "v1.2.3-20190311183353-d8887717615a",
wantVersionType: TypePrerelease,
},
{
name: "valid release",
version: "v1.0.0",
wantVersionType: TypeRelease,
},
{
name: "valid prerelease",
version: "v1.0.0-alpha.1",
wantVersionType: TypePrerelease,
},
{
name: "invalid version",
version: "not_a_version",
wantVersionType: "",
wantErr: true,
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
if gotVt, err := ParseType(test.version); (test.wantErr == (err != nil)) && test.wantVersionType != gotVt {
t.Errorf("parseVersionType(%v) = %v, want %v", test.version, gotVt, test.wantVersionType)
}
})
}
}
func TestLatestOf(t *testing.T) {
for _, test := range []struct {
name string
versions []string
want string
}{
{
name: "highest release",
versions: []string{"v1.2.3", "v1.0.0", "v1.9.0-pre"},
want: "v1.2.3",
},
{
name: "highest pre-release if no release",
versions: []string{"v1.2.3-alpha", "v1.0.0-beta", "v1.9.0-pre"},
want: "v1.9.0-pre",
},
{
name: "prefer pre-release to pseudo",
versions: []string{"v1.0.0-20180713131340-b395d2d6f5ee", "v0.0.0-alpha"},
want: "v0.0.0-alpha",
},
{
name: "highest pseudo if no pre-release or release",
versions: []string{"v0.0.0-20180713131340-b395d2d6f5ee", "v0.0.0-20190124233150-8f7fa2680c82"},
want: "v0.0.0-20190124233150-8f7fa2680c82",
},
{
name: "use incompatible",
versions: []string{"v1.2.3", "v1.0.0", "v2.0.0+incompatible"},
want: "v2.0.0+incompatible",
},
} {
t.Run(test.name, func(t *testing.T) {
got := LatestOf(test.versions)
if got != test.want {
t.Errorf("got %s, want %s", got, test.want)
}
})
}
}
func TestLatest(t *testing.T) {
pseudo := "v0.0.0-20190124233150-8f7fa2680c82"
for _, test := range []struct {
name string
versions []string
hasGoMod func(string) (bool, error)
want string
}{
{
name: "empty",
versions: nil,
want: "",
},
{
name: "tagged release",
versions: []string{pseudo, "v0.1.0", "v1.2.3-pre"},
want: "v0.1.0",
},
{
name: "tagged pre-release",
versions: []string{pseudo, "v1.2.3-pre"},
want: "v1.2.3-pre",
},
{
name: "pseudo",
versions: []string{pseudo},
want: pseudo,
},
{
name: "incompatible with go.mod",
versions: []string{"v2.0.0+incompatible", "v1.2.3"},
want: "v1.2.3",
},
{
name: "incompatible without go.mod",
versions: []string{"v2.0.0+incompatible", "v1.2.3"},
hasGoMod: func(v string) (bool, error) { return false, nil },
want: "v2.0.0+incompatible",
},
{
name: "incompatible without tagged go.mod",
// Although the latest compatible version has a go.mod file,
// it is not a tagged version.
versions: []string{pseudo, "v2.0.0+incompatible"},
want: "v2.0.0+incompatible",
},
} {
t.Run(test.name, func(t *testing.T) {
if test.hasGoMod == nil {
test.hasGoMod = func(v string) (bool, error) { return true, nil }
}
got, err := LatestVersion(test.versions, test.hasGoMod)
if err != nil {
t.Fatal(err)
}
if got != test.want {
t.Errorf("got %q, want %q", got, test.want)
}
})
}
}