blob: 759c7d27f04850fb2298352379ae2508fca99035 [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 stdlib
import (
"context"
"errors"
"flag"
"io/fs"
"reflect"
"testing"
"golang.org/x/mod/semver"
"golang.org/x/pkgsite/internal/testenv"
"golang.org/x/pkgsite/internal/version"
)
var (
clone = flag.Bool("clone", false, "test actual clones of the Go repo")
repoPath = flag.String("path", "", "path to Go repo to test")
)
func TestTagForVersion(t *testing.T) {
for _, test := range []struct {
name string
version string
want string
wantErr bool
}{
{
name: "std version v1.0.0",
version: "v1.0.0",
want: "go1",
},
{
name: "std version v1.12.5",
version: "v1.12.5",
want: "go1.12.5",
},
{
name: "std version v1.13, incomplete canonical version",
version: "v1.13",
want: "go1.13",
},
{
name: "std version v1.13.0-beta.1",
version: "v1.13.0-beta.1",
want: "go1.13beta1",
},
{
name: "std version v1.9.0-rc.2",
version: "v1.9.0-rc.2",
want: "go1.9rc2",
},
{
name: "std with digitless prerelease",
version: "v1.13.0-prerelease",
want: "go1.13prerelease",
},
{
name: "version v1.13.0",
version: "v1.13.0",
want: "go1.13",
},
{
name: "version v1.20.0-rc.2",
version: "v1.20.0-rc.2",
want: "go1.20rc2",
},
{
name: "version v1.20.0",
version: "v1.20.0",
want: "go1.20",
},
{
name: "version v1.21.0-rc.2",
version: "v1.21.0-rc.2",
want: "go1.21rc2",
},
{
name: "version v1.21.0",
version: "v1.21.0",
want: "go1.21.0",
},
{
name: "master branch",
version: "master",
want: "master",
},
{
name: "bad std semver",
version: "v1.x",
wantErr: true,
},
{
name: "more bad std semver",
version: "v1.0-",
wantErr: true,
},
{
name: "bad prerelease",
version: "v1.13.0-beta1",
wantErr: true,
},
{
name: "another bad prerelease",
version: "v1.13.0-whatevs99",
wantErr: true,
},
} {
t.Run(test.name, func(t *testing.T) {
got, err := TagForVersion(test.version)
if (err != nil) != test.wantErr {
t.Errorf("TagForVersion(%q) = %q, %v, wantErr %v", test.version, got, err, test.wantErr)
return
}
if got != test.want {
t.Fatalf("TagForVersion(%q) = %q, %v, wanted %q, %v", test.version, got, err, test.want, nil)
}
})
}
}
func TestMajorVersionForVersion(t *testing.T) {
for _, test := range []struct {
in string
want string // empty => error
}{
{"", ""},
{"garbage", ""},
{"v1.0.0", "go1"},
{"v1.13.3", "go1"},
{"v1.9.0-rc.2", "go1"},
{"v2.1.3", "go2"},
{"v2.1.3", "go2"},
{"v0.0.0-20230307225218-457fd1d52d17", "go1"},
} {
got, err := MajorVersionForVersion(test.in)
if (err != nil) != (test.want == "") {
t.Errorf("%q: err: got %v, wanted error: %t", test.in, err, test.want == "")
}
if err == nil && got != test.want {
t.Errorf("%q: got %q, want %q", test.in, got, test.want)
}
}
}
func TestContentDir(t *testing.T) {
ctx := context.Background()
testenv.MustHaveExecPath(t, "git")
defer WithTestData()()
for _, resolvedVersion := range []string{
"v1.3.2",
"v1.12.5",
"v1.14.6",
version.Master,
} {
t.Run(resolvedVersion, func(t *testing.T) {
cdir, gotResolvedVersion, gotTime, err := ContentDir(ctx, resolvedVersion)
if err != nil {
t.Fatal(err)
}
if SupportedBranches[resolvedVersion] {
if !version.IsPseudo(gotResolvedVersion) {
t.Errorf("resolved version: %s is not a pseudo-version", gotResolvedVersion)
}
} else if gotResolvedVersion != resolvedVersion {
t.Errorf("resolved version: got %s, want %s", gotResolvedVersion, resolvedVersion)
}
if !gotTime.Equal(TestCommitTime) {
t.Errorf("commit time: got %s, want %s", gotTime, TestCommitTime)
}
checkContentDirFiles(t, cdir, resolvedVersion)
})
}
}
func TestContentDirCloneAndOpen(t *testing.T) {
ctx := context.Background()
run := func(t *testing.T) {
for _, resolvedVersion := range []string{
"v1.3.2",
"v1.14.6",
version.Master,
version.Latest,
} {
t.Run(resolvedVersion, func(t *testing.T) {
cdir, _, _, err := ContentDir(ctx, resolvedVersion)
if err != nil {
t.Fatal(err)
}
checkContentDirFiles(t, cdir, resolvedVersion)
})
}
}
t.Run("clone", func(t *testing.T) {
if !*clone {
t.Skip("-clone not supplied")
}
defer withGoRepo(&remoteGoRepo{})()
run(t)
})
t.Run("local", func(t *testing.T) {
if *repoPath == "" {
t.Skip("-path not supplied")
}
lgr := newLocalGoRepo(*repoPath)
defer withGoRepo(lgr)()
run(t)
})
}
func checkContentDirFiles(t *testing.T, cdir fs.FS, resolvedVersion string) {
wantFiles := map[string]bool{
"LICENSE": true,
"errors/errors.go": true,
"errors/errors_test.go": true,
}
if semver.Compare(resolvedVersion, "v1.13.0") > 0 || resolvedVersion == TestMasterVersion {
wantFiles["cmd/README.vendor"] = true
}
if semver.Compare(resolvedVersion, "v1.14.0") > 0 {
wantFiles["context/context.go"] = true
}
const readmeVendorFile = "README.vendor"
if _, err := fs.Stat(cdir, readmeVendorFile); !errors.Is(err, fs.ErrNotExist) {
t.Fatalf("fs.Stat returned %v; want %q to be removed", err, readmeVendorFile)
}
err := fs.WalkDir(cdir, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
delete(wantFiles, path)
return nil
})
if err != nil {
t.Fatal(err)
}
if len(wantFiles) > 0 {
t.Errorf("zip missing files: %v", reflect.ValueOf(wantFiles).MapKeys())
}
}
func TestZipInfo(t *testing.T) {
defer WithTestData()()
for _, tc := range []struct {
requestedVersion string
want string
}{
{
requestedVersion: "latest",
want: "v1.21.0",
},
{
requestedVersion: "master",
want: "master",
},
} {
gotVersion, err := ZipInfo(tc.requestedVersion)
if err != nil {
t.Fatal(err)
}
if want := tc.want; gotVersion != want {
t.Errorf("version: got %q, want %q", gotVersion, want)
}
}
}
func TestVersions(t *testing.T) {
testVersions := func(wants []string) {
got, err := Versions()
if err != nil {
t.Fatal(err)
}
gotmap := map[string]bool{}
for _, g := range got {
gotmap[g] = true
}
for _, w := range wants {
if !gotmap[w] {
t.Errorf("missing %s", w)
}
}
}
commonWants := []string{
"v1.4.2",
"v1.9.0-rc.1",
"v1.11.0",
"v1.13.0-beta.1",
}
otherWants := append([]string{"v1.17.6"}, commonWants...)
t.Run("test", func(t *testing.T) {
defer WithTestData()()
testWants := append([]string{"v1.21.0"}, commonWants...)
testVersions(testWants)
})
t.Run("local", func(t *testing.T) {
if *repoPath == "" {
t.Skip("-path not supplied")
}
lgr := newLocalGoRepo(*repoPath)
defer withGoRepo(lgr)()
testVersions(otherWants)
})
t.Run("remote", func(t *testing.T) {
if !*clone {
t.Skip("-clone not supplied")
}
defer withGoRepo(&remoteGoRepo{})()
testVersions(otherWants)
})
}
func TestVersionForTag(t *testing.T) {
for _, test := range []struct {
in, want string
}{
{"", ""},
{"go1", "v1.0.0"},
{"go1.9beta2", "v1.9.0-beta.2"},
{"go1.12", "v1.12.0"},
{"go1.9.7", "v1.9.7"},
{"go2.0", "v2.0.0"},
{"go1.9rc2", "v1.9.0-rc.2"},
{"go1.1beta", ""},
{"go1.0", ""},
{"weekly.2012-02-14", ""},
{"latest", "latest"},
{"go1.21.0", "v1.21.0"},
{"go1.21", "v1.21.0"},
} {
got := VersionForTag(test.in)
if got != test.want {
t.Errorf("VersionForTag(%q) = %q, want %q", test.in, got, test.want)
}
}
}
func TestContains(t *testing.T) {
for _, test := range []struct {
in string
want bool
}{
{"fmt", true},
{"encoding/json", true},
{"something/with.dots", true},
{"example.com", false},
{"example.com/fmt", false},
} {
got := Contains(test.in)
if got != test.want {
t.Errorf("Contains(%q) = %t, want %t", test.in, got, test.want)
}
}
}
func TestDirectory(t *testing.T) {
for _, tc := range []struct {
version string
want string
}{
{
version: "v1.3.0-beta2",
want: "src/pkg",
},
{
version: "v1.16.0-beta1",
want: "src",
},
{
version: "master",
want: "src",
},
} {
got := Directory(tc.version)
if got != tc.want {
t.Errorf("Directory(%s) = %s, want %s", tc.version, got, tc.want)
}
}
}
func TestVersionMatchesHash(t *testing.T) {
v := "v0.0.0-20210910212848-c8dfa306babb"
h := "c8dfa306babb91e88f8ba25329b3ef8aa11944e1"
if !VersionMatchesHash(v, h) {
t.Error("got false, want true")
}
h = "c8dfa306babXb91e88f8ba25329b3ef8aa11944e1"
if VersionMatchesHash(v, h) {
t.Error("got true, want false")
}
}
func TestResolveSupportedBranches(t *testing.T) {
testenv.MustHaveExternalNetwork(t) // ResolveSupportedBranches accesses the go repo at go.googlesource.com
testenv.MustHaveExecPath(t, "git") // ResolveSupportedBranches uses the git command to do so.
got, err := ResolveSupportedBranches()
if err != nil {
t.Fatal(err)
}
// We can't check the hashes because they change, but we can check the keys.
for key := range got {
if !SupportedBranches[key] {
t.Errorf("got key %q not in SupportedBranches", key)
}
}
if g, w := len(got), len(SupportedBranches); g != w {
t.Errorf("got %d hashes, want %d", g, w)
}
}