| // Copyright 2023 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) | |
| } | |
| }) | |
| } | |
| } |