| // 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 versions_test |
| |
| import ( |
| "go/ast" |
| "go/parser" |
| "go/token" |
| "go/types" |
| "testing" |
| |
| "golang.org/x/tools/internal/versions" |
| ) |
| |
| func TestIsValid(t *testing.T) { |
| // valid versions |
| for _, x := range []string{ |
| "go1.21", |
| "go1.21.2", |
| "go1.21rc", |
| "go1.21rc2", |
| "go0.0", // ?? |
| "go1", |
| "go2", |
| "go1.20.0-bigcorp", |
| } { |
| if !versions.IsValid(x) { |
| t.Errorf("expected versions.IsValid(%q) to hold", x) |
| } |
| } |
| |
| // invalid versions |
| for _, x := range []string{ |
| "", |
| "bad", |
| "1.21", |
| "v1.21", |
| "go", |
| "goAA", |
| "go2_3", |
| "go1.BB", |
| "go1.21.", |
| "go1.21.2_2", |
| "go1.21rc_2", |
| "go1.21rc2_", |
| "go1.600+auto", |
| } { |
| if versions.IsValid(x) { |
| t.Errorf("expected versions.IsValid(%q) to not hold", x) |
| } |
| } |
| } |
| |
| func TestVersionComparisons(t *testing.T) { |
| for _, item := range []struct { |
| x, y string |
| want int |
| }{ |
| // All comparisons of go2, go1.21.2, go1.21rc2, go1.21rc2, go1, go0.0, "", bad |
| {"go2", "go2", 0}, |
| {"go2", "go1.21.2", +1}, |
| {"go2", "go1.21rc2", +1}, |
| {"go2", "go1.21rc", +1}, |
| {"go2", "go1.21", +1}, |
| {"go2", "go1", +1}, |
| {"go2", "go0.0", +1}, |
| {"go2", "", +1}, |
| {"go2", "bad", +1}, |
| {"go1.21.2", "go1.21.2", 0}, |
| {"go1.21.2", "go1.21rc2", +1}, |
| {"go1.21.2", "go1.21rc", +1}, |
| {"go1.21.2", "go1.21", +1}, |
| {"go1.21.2", "go1", +1}, |
| {"go1.21.2", "go0.0", +1}, |
| {"go1.21.2", "", +1}, |
| {"go1.21.2", "bad", +1}, |
| {"go1.21rc2", "go1.21rc2", 0}, |
| {"go1.21rc2", "go1.21rc", +1}, |
| {"go1.21rc2", "go1.21", +1}, |
| {"go1.21rc2", "go1", +1}, |
| {"go1.21rc2", "go0.0", +1}, |
| {"go1.21rc2", "", +1}, |
| {"go1.21rc2", "bad", +1}, |
| {"go1.21rc", "go1.21rc", 0}, |
| {"go1.21rc", "go1.21", +1}, |
| {"go1.21rc", "go1", +1}, |
| {"go1.21rc", "go0.0", +1}, |
| {"go1.21rc", "", +1}, |
| {"go1.21rc", "bad", +1}, |
| {"go1.21", "go1.21", 0}, |
| {"go1.21", "go1", +1}, |
| {"go1.21", "go0.0", +1}, |
| {"go1.21", "", +1}, |
| {"go1.21", "bad", +1}, |
| {"go1", "go1", 0}, |
| {"go1", "go0.0", +1}, |
| {"go1", "", +1}, |
| {"go1", "bad", +1}, |
| {"go0.0", "go0.0", 0}, |
| {"go0.0", "", +1}, |
| {"go0.0", "bad", +1}, |
| {"", "", 0}, |
| {"", "bad", 0}, |
| {"bad", "bad", 0}, |
| // Other tests. |
| {"go1.20", "go1.20.0-bigcorp", 0}, |
| {"go1.21", "go1.21.0-bigcorp", -1}, // Starting in Go 1.21, patch missing is different from explicit .0. |
| {"go1.21.0", "go1.21.0-bigcorp", 0}, // Starting in Go 1.21, patch missing is different from explicit .0. |
| {"go1.19rc1", "go1.19", -1}, |
| } { |
| got := versions.Compare(item.x, item.y) |
| if got != item.want { |
| t.Errorf("versions.Compare(%q, %q)=%d. expected %d", item.x, item.y, got, item.want) |
| } |
| reverse := versions.Compare(item.y, item.x) |
| if reverse != -got { |
| t.Errorf("versions.Compare(%q, %q)=%d. expected %d", item.y, item.x, reverse, -got) |
| } |
| } |
| } |
| |
| func TestLang(t *testing.T) { |
| for _, item := range []struct { |
| x string |
| want string |
| }{ |
| // valid |
| {"go1.21rc2", "go1.21"}, |
| {"go1.21.2", "go1.21"}, |
| {"go1.21", "go1.21"}, |
| {"go1", "go1"}, |
| // invalid |
| {"bad", ""}, |
| {"1.21", ""}, |
| } { |
| if got := versions.Lang(item.x); got != item.want { |
| t.Errorf("versions.Lang(%q)=%q. expected %q", item.x, got, item.want) |
| } |
| } |
| |
| } |
| |
| func TestKnown(t *testing.T) { |
| for _, v := range [...]string{ |
| versions.Go1_18, |
| versions.Go1_19, |
| versions.Go1_20, |
| versions.Go1_21, |
| versions.Go1_22, |
| } { |
| if !versions.IsValid(v) { |
| t.Errorf("Expected known version %q to be valid.", v) |
| } |
| if v != versions.Lang(v) { |
| t.Errorf("Expected known version %q == Lang(%q).", v, versions.Lang(v)) |
| } |
| } |
| } |
| |
| func TestAtLeast(t *testing.T) { |
| for _, item := range [...]struct { |
| v, release string |
| want bool |
| }{ |
| {versions.Future, versions.Go1_22, true}, |
| {versions.Go1_22, versions.Go1_22, true}, |
| {"go1.21", versions.Go1_22, false}, |
| {"invalid", versions.Go1_22, false}, |
| } { |
| if got := versions.AtLeast(item.v, item.release); got != item.want { |
| t.Errorf("AtLeast(%q, %q)=%v. wanted %v", item.v, item.release, got, item.want) |
| } |
| } |
| } |
| |
| func TestBefore(t *testing.T) { |
| for _, item := range [...]struct { |
| v, release string |
| want bool |
| }{ |
| {versions.Future, versions.Go1_22, false}, |
| {versions.Go1_22, versions.Go1_22, false}, |
| {"go1.21", versions.Go1_22, true}, |
| {"invalid", versions.Go1_22, true}, // invalid < Go1_22 |
| } { |
| if got := versions.Before(item.v, item.release); got != item.want { |
| t.Errorf("Before(%q, %q)=%v. wanted %v", item.v, item.release, got, item.want) |
| } |
| } |
| } |
| |
| func TestFileVersions(t *testing.T) { |
| const source = ` |
| package P |
| ` |
| fset := token.NewFileSet() |
| f, err := parser.ParseFile(fset, "hello.go", source, 0) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| for _, conf := range []types.Config{ |
| {GoVersion: versions.Go1_22}, |
| {}, // GoVersion is unset. |
| } { |
| info := &types.Info{ |
| FileVersions: make(map[*ast.File]string), |
| } |
| |
| _, err = conf.Check("P", fset, []*ast.File{f}, info) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| v := versions.FileVersion(info, f) |
| if !versions.AtLeast(v, versions.Go1_22) { |
| t.Errorf("versions.AtLeast(%q, %q) expected to hold", v, versions.Go1_22) |
| } |
| |
| if versions.Before(v, versions.Go1_22) { |
| t.Errorf("versions.AtLeast(%q, %q) expected to be false", v, versions.Go1_22) |
| } |
| |
| if conf.GoVersion == "" && v != versions.Future { |
| t.Error("Expected the FileVersion to be the Future when conf.GoVersion is unset") |
| } |
| } |
| } |