summaryrefslogtreecommitdiffstats
path: root/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/gorelease/gorelease_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/gorelease/gorelease_test.go')
-rw-r--r--dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/gorelease/gorelease_test.go504
1 files changed, 504 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/gorelease/gorelease_test.go b/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/gorelease/gorelease_test.go
new file mode 100644
index 0000000..216c747
--- /dev/null
+++ b/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/gorelease/gorelease_test.go
@@ -0,0 +1,504 @@
+// 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 main
+
+import (
+ "bytes"
+ "context"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "sync"
+ "testing"
+
+ "golang.org/x/mod/module"
+ "golang.org/x/tools/txtar"
+)
+
+var (
+ testwork = flag.Bool("testwork", false, "preserve work directory")
+ updateGolden = flag.Bool("u", false, "update expected text in test files instead of failing")
+)
+
+var hasGitCache struct {
+ once sync.Once
+ found bool
+}
+
+// hasGit reports whether the git executable exists on the PATH.
+func hasGit() bool {
+ hasGitCache.once.Do(func() {
+ if _, err := exec.LookPath("git"); err != nil {
+ return
+ }
+ hasGitCache.found = true
+ })
+ return hasGitCache.found
+}
+
+// prepareProxy creates a proxy dir and returns an associated ctx.
+//
+// proxyVersions must be a map of module version to true. If proxyVersions is
+// empty, all modules in mod/ will be included in the proxy list. If proxy
+// versions is non-empty, only those modules in mod/ that match an entry in
+// proxyVersions will be included.
+//
+// ctx must be used in runRelease.
+// cleanup must be called when the relevant tests are finished.
+func prepareProxy(proxyVersions map[module.Version]bool, tests []*test) (ctx context.Context, cleanup func(), _ error) {
+ env := append(os.Environ(), "GO111MODULE=on", "GOSUMDB=off")
+
+ proxyDir, proxyURL, err := buildProxyDir(proxyVersions, tests)
+ if err != nil {
+ return nil, nil, fmt.Errorf("error building proxy dir: %v", err)
+ }
+ env = append(env, fmt.Sprintf("GOPROXY=%s", proxyURL))
+
+ cacheDir, err := ioutil.TempDir("", "gorelease_test-gocache")
+ if err != nil {
+ return nil, nil, err
+ }
+ env = append(env, fmt.Sprintf("GOPATH=%s", cacheDir))
+
+ return context.WithValue(context.Background(), "env", env), func() {
+ if *testwork {
+ fmt.Fprintf(os.Stderr, "test cache dir: %s\n", cacheDir)
+ fmt.Fprintf(os.Stderr, "test proxy dir: %s\ntest proxy URL: %s\n", proxyDir, proxyURL)
+ } else {
+ cmd := exec.Command("go", "clean", "-modcache")
+ cmd.Env = env
+ if err := cmd.Run(); err != nil {
+ fmt.Fprintln(os.Stderr, fmt.Errorf("error running go clean: %v", err))
+ }
+
+ if err := os.RemoveAll(cacheDir); err != nil {
+ fmt.Fprintln(os.Stderr, fmt.Errorf("error removing cache dir %s: %v", cacheDir, err))
+ }
+ if err := os.RemoveAll(proxyDir); err != nil {
+ fmt.Fprintln(os.Stderr, fmt.Errorf("error removing proxy dir %s: %v", proxyDir, err))
+ }
+ }
+ }, nil
+}
+
+// test describes an individual test case, written as a .test file in the
+// testdata directory.
+//
+// Each test is a txtar archive (see golang.org/x/tools/txtar). The comment
+// section (before the first file) contains a sequence of key=value pairs
+// (one per line) that configure the test.
+//
+// Most tests include a file named "want". The output of gorelease is compared
+// against this file. If the -u flag is set, this file is replaced with the
+// actual output of gorelease, and the test is written back to disk. This is
+// useful for updating tests after cosmetic changes.
+type test struct {
+ txtar.Archive
+
+ // testPath is the name of the .test file describing the test.
+ testPath string
+
+ // modPath (set with mod=...) is the path of the module being tested. Used
+ // to retrieve files from the test proxy.
+ modPath string
+
+ // version (set with version=...) is the name of a version to check out
+ // from the test proxy into the working directory. Some tests use this
+ // instead of specifying files they need in the txtar archive.
+ version string
+
+ // baseVersion (set with base=...) is the value of the -base flag to pass
+ // to gorelease.
+ baseVersion string
+
+ // releaseVersion (set with release=...) is the value of the -version flag
+ // to pass to gorelease.
+ releaseVersion string
+
+ // dir (set with dir=...) is the directory where gorelease should be invoked.
+ // If unset, gorelease is invoked in the directory where the txtar archive
+ // is unpacked. This is useful for invoking gorelease in a subdirectory.
+ dir string
+
+ // wantError (set with error=...) is true if the test expects a hard error
+ // (returned by runRelease).
+ wantError bool
+
+ // wantSuccess (set with success=...) is true if the test expects a report
+ // to be returned without errors or diagnostics. True by default.
+ wantSuccess bool
+
+ // skip (set with skip=...) is non-empty if the test should be skipped.
+ skip string
+
+ // want is set to the contents of the file named "want" in the txtar archive.
+ want []byte
+
+ // proxyVersions is used to set the exact contents of the GOPROXY.
+ //
+ // If empty, all of testadata/mod/ will be included in the proxy.
+ // If it is not empty, each entry must be of the form <modpath>@v<version>
+ // and exist in testdata/mod/.
+ proxyVersions map[module.Version]bool
+
+ // vcs is used to set the VCS that the root of the test should
+ // emulate. Allowed values are git, and hg.
+ vcs string
+}
+
+// readTest reads and parses a .test file with the given name.
+func readTest(testPath string) (*test, error) {
+ arc, err := txtar.ParseFile(testPath)
+ if err != nil {
+ return nil, err
+ }
+ t := &test{
+ Archive: *arc,
+ testPath: testPath,
+ wantSuccess: true,
+ }
+
+ for n, line := range bytes.Split(t.Comment, []byte("\n")) {
+ lineNum := n + 1
+ if i := bytes.IndexByte(line, '#'); i >= 0 {
+ line = line[:i]
+ }
+ line = bytes.TrimSpace(line)
+ if len(line) == 0 {
+ continue
+ }
+
+ var key, value string
+ if i := bytes.IndexByte(line, '='); i < 0 {
+ return nil, fmt.Errorf("%s:%d: no '=' found", testPath, lineNum)
+ } else {
+ key = strings.TrimSpace(string(line[:i]))
+ value = strings.TrimSpace(string(line[i+1:]))
+ }
+ switch key {
+ case "mod":
+ t.modPath = value
+ case "version":
+ t.version = value
+ case "base":
+ t.baseVersion = value
+ case "release":
+ t.releaseVersion = value
+ case "dir":
+ t.dir = value
+ case "skip":
+ t.skip = value
+ case "success":
+ t.wantSuccess, err = strconv.ParseBool(value)
+ if err != nil {
+ return nil, fmt.Errorf("%s:%d: %v", testPath, lineNum, err)
+ }
+ case "error":
+ t.wantError, err = strconv.ParseBool(value)
+ if err != nil {
+ return nil, fmt.Errorf("%s:%d: %v", testPath, lineNum, err)
+ }
+ case "proxyVersions":
+ if len(value) == 0 {
+ break
+ }
+ proxyVersions := make(map[module.Version]bool)
+ parts := strings.Split(value, ",")
+ for _, modpathWithVersion := range parts {
+ vParts := strings.Split(modpathWithVersion, "@")
+ if len(vParts) != 2 {
+ return nil, fmt.Errorf("proxyVersions entry %s is invalid: it should be of the format <modpath>@v<semver> (ex: github.com/foo/bar@v1.2.3)", modpathWithVersion)
+ }
+ modPath, version := vParts[0], vParts[1]
+ mv := module.Version{
+ Path: modPath,
+ Version: version,
+ }
+ proxyVersions[mv] = true
+ }
+ t.proxyVersions = proxyVersions
+ case "vcs":
+ t.vcs = value
+ default:
+ return nil, fmt.Errorf("%s:%d: unknown key: %q", testPath, lineNum, key)
+ }
+ }
+ if t.modPath == "" && (t.version != "" || (t.baseVersion != "" && t.baseVersion != "none")) {
+ return nil, fmt.Errorf("%s: version or base was set but mod was not set", testPath)
+ }
+
+ haveFiles := false
+ for _, f := range t.Files {
+ if f.Name == "want" {
+ t.want = bytes.TrimSpace(f.Data)
+ continue
+ }
+ haveFiles = true
+ }
+
+ if haveFiles && t.version != "" {
+ return nil, fmt.Errorf("%s: version is set but files are present", testPath)
+ }
+
+ return t, nil
+}
+
+// updateTest replaces the contents of the file named "want" within a test's
+// txtar archive, then formats and writes the test file.
+func updateTest(t *test, want []byte) error {
+ var wantFile *txtar.File
+ for i := range t.Files {
+ if t.Files[i].Name == "want" {
+ wantFile = &t.Files[i]
+ break
+ }
+ }
+ if wantFile == nil {
+ t.Files = append(t.Files, txtar.File{Name: "want"})
+ wantFile = &t.Files[len(t.Files)-1]
+ }
+
+ wantFile.Data = want
+ data := txtar.Format(&t.Archive)
+ return ioutil.WriteFile(t.testPath, data, 0666)
+}
+
+func TestRelease(t *testing.T) {
+ testPaths, err := filepath.Glob(filepath.FromSlash("testdata/*/*.test"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(testPaths) == 0 {
+ t.Fatal("no .test files found in testdata directory")
+ }
+
+ var tests []*test
+ for _, testPath := range testPaths {
+ test, err := readTest(testPath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tests = append(tests, test)
+ }
+
+ defaultContext, cleanup, err := prepareProxy(nil, tests)
+ if err != nil {
+ t.Fatalf("preparing test proxy: %v", err)
+ }
+ t.Cleanup(cleanup)
+
+ for _, test := range tests {
+ testName := strings.TrimSuffix(strings.TrimPrefix(filepath.ToSlash(test.testPath), "testdata/"), ".test")
+ t.Run(testName, testRelease(defaultContext, tests, test))
+ }
+}
+
+func TestRelease_gitRepo_uncommittedChanges(t *testing.T) {
+ ctx := context.Background()
+ buf := &bytes.Buffer{}
+ releaseDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ goModInit(t, releaseDir)
+ gitInit(t, releaseDir)
+
+ // Create an uncommitted change.
+ bContents := `package b
+const B = "b"`
+ if err := ioutil.WriteFile(filepath.Join(releaseDir, "b.go"), []byte(bContents), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ success, err := runRelease(ctx, buf, releaseDir, nil)
+ if got, want := err.Error(), fmt.Sprintf("repo %s has uncommitted changes", releaseDir); got != want {
+ t.Errorf("runRelease:\ngot error:\n%q\nwant error\n%q", got, want)
+ }
+ if success {
+ t.Errorf("runRelease: expected failure, got success")
+ }
+}
+
+func testRelease(ctx context.Context, tests []*test, test *test) func(t *testing.T) {
+ return func(t *testing.T) {
+ if test.skip != "" {
+ t.Skip(test.skip)
+ }
+
+ t.Parallel()
+
+ if len(test.proxyVersions) > 0 {
+ var cleanup func()
+ var err error
+ ctx, cleanup, err = prepareProxy(test.proxyVersions, tests)
+ if err != nil {
+ t.Fatalf("preparing test proxy: %v", err)
+ }
+ t.Cleanup(cleanup)
+ }
+
+ // Extract the files in the release version. They may be part of the
+ // test archive or in testdata/mod.
+ testDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if *testwork {
+ fmt.Fprintf(os.Stderr, "test dir: %s\n", testDir)
+ } else {
+ t.Cleanup(func() {
+ os.RemoveAll(testDir)
+ })
+ }
+
+ var arc *txtar.Archive
+ if test.version != "" {
+ arcBase := fmt.Sprintf("%s_%s.txt", strings.ReplaceAll(test.modPath, "/", "_"), test.version)
+ arcPath := filepath.Join("testdata/mod", arcBase)
+ var err error
+ arc, err = txtar.ParseFile(arcPath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ } else {
+ arc = &test.Archive
+ }
+ if err := extractTxtar(testDir, arc); err != nil {
+ t.Fatal(err)
+ }
+
+ switch test.vcs {
+ case "git":
+ // Convert testDir to a git repository with a single commit, to
+ // simulate a real user's module-in-a-git-repo.
+ gitInit(t, testDir)
+ case "hg":
+ // Convert testDir to a mercurial repository to simulate a real
+ // user's module-in-a-hg-repo.
+ hgInit(t, testDir)
+ case "":
+ // No VCS.
+ default:
+ t.Fatalf("unknown vcs %q", test.vcs)
+ }
+
+ // Generate the report and compare it against the expected text.
+ var args []string
+ if test.baseVersion != "" {
+ args = append(args, "-base="+test.baseVersion)
+ }
+ if test.releaseVersion != "" {
+ args = append(args, "-version="+test.releaseVersion)
+ }
+ buf := &bytes.Buffer{}
+ releaseDir := filepath.Join(testDir, test.dir)
+ success, err := runRelease(ctx, buf, releaseDir, args)
+ if err != nil {
+ if !test.wantError {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if errMsg := []byte(err.Error()); !bytes.Equal(errMsg, bytes.TrimSpace(test.want)) {
+ if *updateGolden {
+ if err := updateTest(test, errMsg); err != nil {
+ t.Fatal(err)
+ }
+ } else {
+ t.Fatalf("got error: %s; want error: %s", errMsg, test.want)
+ }
+ }
+ return
+ }
+ if test.wantError {
+ t.Fatalf("got success; want error %s", test.want)
+ }
+
+ got := bytes.TrimSpace(buf.Bytes())
+ if filepath.Separator != '/' {
+ got = bytes.ReplaceAll(got, []byte{filepath.Separator}, []byte{'/'})
+ }
+ if !bytes.Equal(got, test.want) {
+ if *updateGolden {
+ if err := updateTest(test, got); err != nil {
+ t.Fatal(err)
+ }
+ } else {
+ t.Fatalf("got:\n%s\n\nwant:\n%s", got, test.want)
+ }
+ }
+ if success != test.wantSuccess {
+ t.Fatalf("got success: %v; want success %v", success, test.wantSuccess)
+ }
+ }
+}
+
+// hgInit initialises a directory as a mercurial repo.
+func hgInit(t *testing.T, dir string) {
+ t.Helper()
+
+ if err := os.Mkdir(filepath.Join(dir, ".hg"), 0777); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := ioutil.WriteFile(filepath.Join(dir, ".hg", "branch"), []byte("default"), 0777); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// gitInit initialises a directory as a git repo, and adds a simple commit.
+func gitInit(t *testing.T, dir string) {
+ t.Helper()
+
+ if !hasGit() {
+ t.Skip("PATH does not contain git")
+ }
+
+ stdout := &bytes.Buffer{}
+ stderr := &bytes.Buffer{}
+
+ for _, args := range [][]string{
+ {"git", "init"},
+ {"git", "config", "user.name", "Gopher"},
+ {"git", "config", "user.email", "gopher@golang.org"},
+ {"git", "checkout", "-b", "test"},
+ {"git", "add", "-A"},
+ {"git", "commit", "-m", "test"},
+ } {
+ cmd := exec.Command(args[0], args[1:]...)
+ cmd.Dir = dir
+ cmd.Stdout = stdout
+ cmd.Stderr = stderr
+ if err := cmd.Run(); err != nil {
+ cmdArgs := strings.Join(args, " ")
+ t.Fatalf("%s\n%s\nerror running %q on dir %s: %v", stdout.String(), stderr.String(), cmdArgs, dir, err)
+ }
+ }
+}
+
+// goModInit runs `go mod init` in the given directory.
+func goModInit(t *testing.T, dir string) {
+ t.Helper()
+
+ aContents := `package a
+const A = "a"`
+ if err := ioutil.WriteFile(filepath.Join(dir, "a.go"), []byte(aContents), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ stdout := &bytes.Buffer{}
+ stderr := &bytes.Buffer{}
+ cmd := exec.Command("go", "mod", "init", "example.com/uncommitted")
+ cmd.Stdout = stdout
+ cmd.Stderr = stderr
+ cmd.Dir = dir
+ if err := cmd.Run(); err != nil {
+ t.Fatalf("error running `go mod init`: %s, %v", stderr.String(), err)
+ }
+}