From: Ian Lance Taylor Date: Tue, 9 Jun 2015 17:30:08 +0000 (-0700) Subject: cmd/go: rewrite testsuite from bash to Go X-Git-Tag: go1.5beta1~297 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=dc32b7f0fda20e6f3a4f893ca886681cb765739e;p=gostls13.git cmd/go: rewrite testsuite from bash to Go Change-Id: I8473e3f7653d5389d5fcd94862f0831049b8266e Reviewed-on: https://go-review.googlesource.com/10809 Reviewed-by: Brad Fitzpatrick --- diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index b733d3a432..346bd6a6c1 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -12,14 +12,26 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "runtime" + "strconv" "strings" "testing" + "time" ) // Whether we can run go or ./testgo. var canRun = true +// The suffix for executables, because Windows. +var exeSuffix string + +// Whether we can run the race detector. +var canRace bool + +// Whether we can use cgo. +var canCgo bool + func init() { switch runtime.GOOS { case "android", "nacl": @@ -30,6 +42,11 @@ func init() { canRun = false } } + + switch runtime.GOOS { + case "windows": + exeSuffix = ".exe" + } } // The TestMain function creates a go command for testing purposes and @@ -38,12 +55,26 @@ func TestMain(m *testing.M) { flag.Parse() if canRun { - // We give the executable a .exe extension because Windows. - out, err := exec.Command("go", "build", "-tags", "testgo", "-o", "testgo.exe").CombinedOutput() + out, err := exec.Command("go", "build", "-tags", "testgo", "-o", "testgo"+exeSuffix).CombinedOutput() if err != nil { fmt.Fprintf(os.Stderr, "building testgo failed: %v\n%s", err, out) os.Exit(2) } + + if out, err := exec.Command("./testgo"+exeSuffix, "env", "CGO_ENABLED").Output(); err != nil { + fmt.Fprintf(os.Stderr, "running testgo failed: %v\n", err) + canRun = false + } else { + canCgo, err = strconv.ParseBool(strings.TrimSpace(string(out))) + if err != nil { + fmt.Fprintf(os.Stderr, "can't parse go env CGO_ENABLED output: %v\n", strings.TrimSpace(string(out))) + } + } + + switch runtime.GOOS { + case "linux", "darwin", "freebsd", "windows": + canRace = canCgo && runtime.GOARCH == "amd64" + } } // Don't let these environment variables confuse the test. @@ -54,14 +85,25 @@ func TestMain(m *testing.M) { r := m.Run() if canRun { - os.Remove("testgo.exe") + os.Remove("testgo" + exeSuffix) } os.Exit(r) } -// Skip a test if we can't run ./testgo. -func checkTestGo(t *testing.T) { +// Manage a single run of the testgo binary. +type testgoData struct { + t *testing.T + temps []string + wd string + env []string + tempdir string + ran bool + stdout, stderr bytes.Buffer +} + +// testgo sets up for a test that runs testgo. +func testgo(t *testing.T) *testgoData { if !canRun { switch runtime.GOOS { case "android", "nacl": @@ -75,82 +117,1648 @@ func checkTestGo(t *testing.T) { t.Skip("skipping for unknown reason") } } + + return &testgoData{t: t} +} + +// must gives a fatal error if err is not nil. +func (tg *testgoData) must(err error) { + if err != nil { + tg.t.Fatal(err) + } +} + +// check gives a test non-fatal error if err is not nil. +func (tg *testgoData) check(err error) { + if err != nil { + tg.t.Error(err) + } +} + +// pwd returns the current directory. +func (tg *testgoData) pwd() string { + wd, err := os.Getwd() + if err != nil { + tg.t.Fatalf("could not get working directory: %v", err) + } + return wd } -// runTestGo runs the test go command, returning stdout, stderr, and -// status. The contents of addEnv are added to the environment. -func runTestGo(addEnv []string, args ...string) (stdout, stderr string, err error) { +// cd changes the current directory to the named directory. Note that +// using this means that the test must not be run in parallel with any +// other tests. +func (tg *testgoData) cd(dir string) { + if tg.wd == "" { + tg.wd = tg.pwd() + } + tg.must(os.Chdir(dir)) +} + +// setenv sets an environment variable to use when running the test go +// command. +func (tg *testgoData) setenv(name, val string) { + tg.unsetenv(name) + tg.env = append(tg.env, name+"="+val) +} + +// unsetenv removes an environment variable. +func (tg *testgoData) unsetenv(name string) { + if tg.env == nil { + tg.env = append([]string(nil), os.Environ()...) + } + for i, v := range tg.env { + if strings.HasPrefix(v, name+"=") { + tg.env = append(tg.env[:i], tg.env[i+1:]...) + break + } + } +} + +// doRun runs the test go command, recording stdout and stderr and +// returning exit status. +func (tg *testgoData) doRun(args []string) error { if !canRun { - panic("runTestGo called but canRun false") + panic("testgoData.doRun called but canRun false") + } + tg.t.Logf("running testgo %v", args) + var prog string + if tg.wd == "" { + prog = "./testgo" + exeSuffix + } else { + prog = filepath.Join(tg.wd, "testgo"+exeSuffix) } + cmd := exec.Command(prog, args...) + tg.stdout.Reset() + tg.stderr.Reset() + cmd.Stdout = &tg.stdout + cmd.Stderr = &tg.stderr + cmd.Env = tg.env + status := cmd.Run() + if tg.stdout.Len() > 0 { + tg.t.Log("standard output:") + tg.t.Log(tg.stdout.String()) + } + if tg.stderr.Len() > 0 { + tg.t.Log("standard error:") + tg.t.Log(tg.stderr.String()) + } + tg.ran = true + return status +} - cmd := exec.Command("./testgo.exe", args...) - var ob, eb bytes.Buffer - cmd.Stdout = &ob - cmd.Stderr = &eb - if len(addEnv) > 0 { - cmd.Env = append(addEnv, os.Environ()...) +// run runs the test go command, and expects it to succeed. +func (tg *testgoData) run(args ...string) { + if status := tg.doRun(args); status != nil { + tg.t.Logf("go %v failed unexpectedly: %v", args, status) + tg.t.FailNow() } - err = cmd.Run() - return ob.String(), eb.String(), err } -// tempFile describes a file to put into a temporary directory. -type tempFile struct { - path string - contents string +// runFail runs the test go command, and expects it to fail. +func (tg *testgoData) runFail(args ...string) { + if status := tg.doRun(args); status == nil { + tg.t.Fatal("testgo succeeded unexpectedly") + } else { + tg.t.Log("testgo failed as expected:", status) + } } -// tempDir describes a temporary directory created for a single test. -type tempDir struct { - name string +// getStdout returns standard output of the testgo run as a string. +func (tg *testgoData) getStdout() string { + if !tg.ran { + tg.t.Fatal("internal testsuite error: stdout called before run") + } + return tg.stdout.String() } -// makeTempDir creates a temporary directory for a single test. -func makeTempDir(t *testing.T, files []tempFile) *tempDir { - dir, err := ioutil.TempDir("", "gotest") - if err != nil { - t.Fatal(err) +// getStderr returns standard error of the testgo run as a string. +func (tg *testgoData) getStderr() string { + if !tg.ran { + tg.t.Fatal("internal testsuite error: stdout called before run") + } + return tg.stderr.String() +} + +// doGrepMatch looks for a regular expression in a buffer, and returns +// whether it is found. The regular expression is matched against +// each line separately, as with the grep command. +func (tg *testgoData) doGrepMatch(match string, b *bytes.Buffer) bool { + if !tg.ran { + tg.t.Fatal("internal testsuite error: grep called before run") } - for _, f := range files { - if err := ioutil.WriteFile(filepath.Join(dir, f.path), []byte(f.contents), 0666); err != nil { - t.Fatal(err) + re := regexp.MustCompile(match) + for _, ln := range bytes.Split(b.Bytes(), []byte{'\n'}) { + if re.Match(ln) { + return true } } - return &tempDir{dir} + return false } -// path returns the absolute pathname to file within tempDir. -func (td *tempDir) path(name string) string { - return filepath.Join(td.name, name) +// doGrep looks for a regular expression in a buffer and fails if it +// is not found. The name argument is the name of the output we are +// searching, "output" or "error". The msg argument is logged on +// failure. +func (tg *testgoData) doGrep(match string, b *bytes.Buffer, name, msg string) { + if !tg.doGrepMatch(match, b) { + tg.t.Log(msg) + tg.t.Logf("pattern %v not found in standard %s", match, name) + tg.t.FailNow() + } +} + +// grepStdout looks for a regular expression in the test run's +// standard output and fails, logging msg, if it is not found. +func (tg *testgoData) grepStdout(match, msg string) { + tg.doGrep(match, &tg.stdout, "output", msg) } -// Remove a temporary directory after a test completes. This is -// normally called via defer. -func (td *tempDir) remove(t *testing.T) { - if err := os.RemoveAll(td.name); err != nil { - fmt.Fprintln(os.Stderr, err) +// grepStderr looks for a regular expression in the test run's +// standard error and fails, logging msg, if it is not found. +func (tg *testgoData) grepStderr(match, msg string) { + tg.doGrep(match, &tg.stderr, "error", msg) +} + +// grepBoth looks for a regular expression in the test run's standard +// output or stand error and fails, logging msg, if it is not found. +func (tg *testgoData) grepBoth(match, msg string) { + if !tg.doGrepMatch(match, &tg.stdout) && !tg.doGrepMatch(match, &tg.stderr) { + tg.t.Log(msg) + tg.t.Logf("pattern %v not found in standard output or standard error", match) + tg.t.FailNow() + } +} + +// doGrepNot looks for a regular expression in a buffer and fails if +// it is found. The name and msg arguments are as for doGrep. +func (tg *testgoData) doGrepNot(match string, b *bytes.Buffer, name, msg string) { + if tg.doGrepMatch(match, b) { + tg.t.Log(msg) + tg.t.Logf("pattern %v found unexpectedly in standard %s", match, name) + tg.t.FailNow() + } +} + +// grepStdoutNot looks for a regular expression in the test run's +// standard output and fails, logging msg, if it is found. +func (tg *testgoData) grepStdoutNot(match, msg string) { + tg.doGrepNot(match, &tg.stdout, "output", msg) +} + +// grepStderrNot looks for a regular expression in the test run's +// standard error and fails, logging msg, if it is found. +func (tg *testgoData) grepStderrNot(match, msg string) { + tg.doGrepNot(match, &tg.stderr, "error", msg) +} + +// grepBothNot looks for a regular expression in the test run's +// standard output or stand error and fails, logging msg, if it is +// found. +func (tg *testgoData) grepBothNot(match, msg string) { + if tg.doGrepMatch(match, &tg.stdout) || tg.doGrepMatch(match, &tg.stderr) { + tg.t.Log(msg) + tg.t.Fatalf("pattern %v found unexpectedly in standard output or standard error", match) + } +} + +// doGrepCount counts the number of times a regexp is seen in a buffer. +func (tg *testgoData) doGrepCount(match string, b *bytes.Buffer) int { + if !tg.ran { + tg.t.Fatal("internal testsuite error: doGrepCount called before run") + } + re := regexp.MustCompile(match) + c := 0 + for _, ln := range bytes.Split(b.Bytes(), []byte{'\n'}) { + if re.Match(ln) { + c++ + } + } + return c +} + +// grepCountStdout returns the number of times a regexp is seen in +// standard output. +func (tg *testgoData) grepCountStdout(match string) int { + return tg.doGrepCount(match, &tg.stdout) +} + +// grepCountStderr returns the number of times a regexp is seen in +// standard error. +func (tg *testgoData) grepCountStderr(match string) int { + return tg.doGrepCount(match, &tg.stderr) +} + +// grepCountBoth returns the number of times a regexp is seen in both +// standard output and standard error. +func (tg *testgoData) grepCountBoth(match string) int { + return tg.doGrepCount(match, &tg.stdout) + tg.doGrepCount(match, &tg.stderr) +} + +// creatingTemp records that the test plans to create a temporary file +// or directory. If the file or directory exists already, it will be +// removed. When the test completes, the file or directory will be +// removed if it exists. +func (tg *testgoData) creatingTemp(path string) { + if filepath.IsAbs(path) && !strings.HasPrefix(path, tg.tempdir) { + tg.t.Fatal("internal testsuite error: creatingTemp(%q) with absolute path not in temporary directory") + } + // If we have changed the working directory, make sure we have + // an absolute path, because we are going to change directory + // back before we remove the temporary. + if tg.wd != "" && !filepath.IsAbs(path) { + path = filepath.Join(tg.pwd(), path) + } + tg.must(os.RemoveAll(path)) + tg.temps = append(tg.temps, path) +} + +// makeTempdir makes a temporary directory for a run of testgo. If +// the temporary directory was already created, this does nothing. +func (tg *testgoData) makeTempdir() { + if tg.tempdir == "" { + var err error + tg.tempdir, err = ioutil.TempDir("", "gotest") + tg.must(err) + } +} + +// tempFile adds a temporary file for a run of testgo. +func (tg *testgoData) tempFile(path, contents string) { + tg.makeTempdir() + tg.must(os.MkdirAll(filepath.Join(tg.tempdir, filepath.Dir(path)), 0755)) + tg.must(ioutil.WriteFile(filepath.Join(tg.tempdir, path), []byte(contents), 0644)) +} + +// tempDir adds a temporary directory for a run of testgo. +func (tg *testgoData) tempDir(path string) { + tg.makeTempdir() + if err := os.MkdirAll(filepath.Join(tg.tempdir, path), 0755); err != nil && !os.IsExist(err) { + tg.t.Fatal(err) + } +} + +// path returns the absolute pathname to file with the temporary +// directory. +func (tg *testgoData) path(name string) string { + if tg.tempdir == "" { + tg.t.Fatalf("internal testsuite error: path(%q) with no tempdir", name) + } + if name == "." { + return tg.tempdir + } + return filepath.Join(tg.tempdir, name) +} + +// wantExecutable fails with msg if path is not executable. +func (tg *testgoData) wantExecutable(path, msg string) { + if st, err := os.Stat(path); err != nil { + if !os.IsNotExist(err) { + tg.t.Log(err) + } + tg.t.Fatal(msg) + } else { + if runtime.GOOS != "windows" && st.Mode()&0111 == 0 { + tg.t.Fatalf("binary %s exists but is not executable", path) + } + } +} + +// isStale returns whether pkg is stale. +func (tg *testgoData) isStale(pkg string) bool { + tg.run("list", "-f", "{{.Stale}}", pkg) + switch v := strings.TrimSpace(tg.getStdout()); v { + case "true": + return true + case "false": + return false + default: + tg.t.Fatalf("unexpected output checking staleness of package %v: %v", pkg, v) + panic("unreachable") + } +} + +// wantStale fails with msg if pkg is not stale. +func (tg *testgoData) wantStale(pkg, msg string) { + if !tg.isStale(pkg) { + tg.t.Fatal(msg) + } +} + +// wantNotStale fails with msg if pkg is stale. +func (tg *testgoData) wantNotStale(pkg, msg string) { + if tg.isStale(pkg) { + tg.t.Fatal(msg) + } +} + +// cleanup cleans up a test that runs testgo. +func (tg *testgoData) cleanup() { + if tg.wd != "" { + if err := os.Chdir(tg.wd); err != nil { + // We are unlikely to be able to continue. + fmt.Fprintln(os.Stderr, "could not restore working directory, crashing:", err) + os.Exit(2) + } + } + for _, path := range tg.temps { + tg.check(os.RemoveAll(path)) + } + if tg.tempdir != "" { + tg.check(os.RemoveAll(tg.tempdir)) } } func TestFileLineInErrorMessages(t *testing.T) { - checkTestGo(t) - td := makeTempDir(t, []tempFile{{"err.go", `package main; import "bar"`}}) - defer td.remove(t) - path := td.path("err.go") - stdout, stderr, err := runTestGo(nil, "run", path) - if err == nil { - t.Fatal("go command did not fail") - } - lines := strings.Split(stderr, "\n") - for _, ln := range lines { - if strings.HasPrefix(ln, path+":") { - // Test has passed. - return - } - } - t.Log(err) - t.Log(stdout) - t.Log(stderr) - t.Error("missing file:line in error message") + tg := testgo(t) + defer tg.cleanup() + tg.tempFile("err.go", `package main; import "bar"`) + path := tg.path("err.go") + tg.runFail("run", path) + tg.grepStderr("^"+regexp.QuoteMeta(path)+":", "missing file:line in error message") +} + +func TestProgramNameInCrashMessages(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.tempFile("triv.go", `package main; func main() {}`) + tg.runFail("build", "-ldflags", "-crash_for_testing", tg.path("triv.go")) + tg.grepStderr(`[/\\]tool[/\\].*[/\\]link`, "missing linker name in error message") +} + +func TestBrokenTestsWithoutTestFunctionsAllFail(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.runFail("test", "./testdata/src/badtest/...") + tg.grepBothNot("^ok", "test passed unexpectedly") + tg.grepBoth("FAIL.*badtest/badexec", "test did not run everything") + tg.grepBoth("FAIL.*badtest/badsyntax", "test did not run everything") + tg.grepBoth("FAIL.*badtest/badvar", "test did not run everything") +} + +func TestGoBuildDashAInDevBranch(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.run("install", "math") // should be up to date already but just in case + tg.setenv("TESTGO_IS_GO_RELEASE", "0") + tg.run("build", "-v", "-a", "math") + tg.grepStderr("runtime", "testgo build -a math in dev branch DID NOT build runtime, but should have") +} + +func TestGoBuilDashAInReleaseBranch(t *testing.T) { + if testing.Short() { + t.Skip("don't rebuild the standard libary in short mode") + } + + tg := testgo(t) + defer tg.cleanup() + tg.run("install", "math") // should be up to date already but just in case + tg.setenv("TESTGO_IS_GO_RELEASE", "1") + tg.run("build", "-v", "-a", "math") + tg.grepStderrNot("runtime", "testgo build -a math in dev branch DID build runtime, but should NOT have") +} + +func TestGoInstallCleansUpAfterGoBuild(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("skipping on Windows because of http://golang.org/issue/9645") + } + + tg := testgo(t) + defer tg.cleanup() + tg.tempFile("src/mycmd/main.go", `package main; func main(){}`) + tg.setenv("GOPATH", tg.path(".")) + tg.cd(tg.path("src/mycmd")) + + doesNotExist := func(file, msg string) { + if _, err := os.Stat(file); err == nil { + t.Fatal(msg) + } else if !os.IsNotExist(err) { + // See http://golang.org/issue/11132. + if runtime.GOOS == "plan9" && strings.Contains(err.Error(), "stat buffer too short") { + return + } + t.Fatal(msg, "error:", err) + } + } + + tg.run("build") + tg.wantExecutable("mycmd"+exeSuffix, "testgo build did not write command binary") + tg.run("install") + doesNotExist("mycmd"+exeSuffix, "testgo install did not remove command binary") + tg.run("build") + tg.wantExecutable("mycmd"+exeSuffix, "testgo build did not write command binary (second time)") + // Running install with arguments does not remove the target, + // even in the same directory. + tg.run("install", "mycmd") + tg.wantExecutable("mycmd"+exeSuffix, "testgo install mycmd removed command binary when run in mycmd") + tg.run("build") + tg.wantExecutable("mycmd"+exeSuffix, "testgo build did not write command binary (third time)") + // And especially not outside the directory. + tg.cd(tg.path(".")) + if data, err := ioutil.ReadFile("src/mycmd/mycmd"); err != nil { + t.Fatal("could not read file:", err) + } else { + if err := ioutil.WriteFile("mycmd", data, 0555); err != nil { + t.Fatal("could not write file:", err) + } + } + tg.run("install", "mycmd") + tg.wantExecutable("src/mycmd/mycmd"+exeSuffix, "testgo install mycmd removed command binary from its source dir when run outside mycmd") + tg.wantExecutable("mycmd"+exeSuffix, "testgo install mycmd removed command binary from current dir when run outside mycmd") +} + +func TestGoInstallRebuildsStalePackagesInOtherGOPATH(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.tempFile("d1/src/p1/p1.go", `package p1 + +import "p2" + +func F() { p2.F() } +`) + tg.tempFile("d2/src/p2/p2.go", `package p2 + +func F() {} +`) + sep := string(filepath.ListSeparator) + tg.setenv("GOPATH", tg.path("d1")+sep+tg.path("d2")) + tg.run("install", "p1") + tg.wantNotStale("p1", "./testgo list mypkg claims p1 is stale, incorrectly") + tg.wantNotStale("p2", "./testgo list mypkg claims p2 is stale, incorrectly") + // TODO(iant): Sleep for one "tick", where a tick is the + // granularity of mtime on the file system. + time.Sleep(time.Second) + if f, err := os.OpenFile(tg.path("d2/src/p2/p2.go"), os.O_WRONLY|os.O_APPEND, 0); err != nil { + t.Fatal(err) + } else if _, err = f.WriteString(`func G() {}`); err != nil { + t.Fatal(err) + } else { + tg.must(f.Close()) + } + tg.wantStale("p2", "./testgo list mypkg claims p2 is NOT stale, incorrectly") + tg.wantStale("p1", "./testgo list mypkg claims p1 is NOT stale, incorrectly") + + tg.run("install", "p1") + tg.wantNotStale("p2", "./testgo list mypkg claims p2 is stale after reinstall, incorrectly") + tg.wantNotStale("p1", "./testgo list mypkg claims p1 is stale after reinstall, incorrectly") +} + +func TestGoInstallDetectsRemovedFiles(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.tempFile("src/mypkg/x.go", `package mypkg`) + tg.tempFile("src/mypkg/y.go", `package mypkg`) + tg.tempFile("src/mypkg/z.go", `// +build missingtag + +package mypkg`) + tg.setenv("GOPATH", tg.path(".")) + tg.run("install", "mypkg") + tg.wantNotStale("mypkg", "./testgo list mypkg claims mypkg is stale, incorrectly") + // z.go was not part of the build; removing it is okay. + tg.must(os.Remove(tg.path("src/mypkg/z.go"))) + tg.wantNotStale("mypkg", "./testgo list mypkg claims mypkg is stale after removing z.go; should not be stale") + // y.go was part of the package; removing it should be detected. + tg.must(os.Remove(tg.path("src/mypkg/y.go"))) + tg.wantStale("mypkg", "./testgo list mypkg claims mypkg is NOT stale after removing y.go; should be stale") +} + +func TestGoInstsallDetectsRemovedFilesInPackageMain(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.tempFile("src/mycmd/x.go", `package main + +func main() {} +`) + tg.tempFile("src/mycmd/y.go", `package main`) + tg.tempFile("src/mycmd/z.go", `// +build missingtag + +package main +`) + tg.setenv("GOPATH", tg.path(".")) + tg.run("install", "mycmd") + tg.wantNotStale("mycmd", "./testgo list mypkg claims mycmd is stale, incorrectly") + // z.go was not part of the build; removing it is okay. + tg.must(os.Remove(tg.path("src/mycmd/z.go"))) + tg.wantNotStale("mycmd", "./testgo list mycmd claims mycmd is stale after removing z.go; should not be stale") + // y.go was part of the package; removing it should be detected. + tg.must(os.Remove(tg.path("src/mycmd/y.go"))) + tg.wantStale("mycmd", "./testgo list mycmd claims mycmd is NOT stale after removing y.go; should be stale") +} + +func testLocalRun(tg *testgoData, local, match string) { + out, err := exec.Command("./hello" + exeSuffix).Output() + if err != nil { + tg.t.Fatal("error running hello:", err) + } + if !regexp.MustCompile(match).Match(out) { + tg.t.Log(string(out)) + tg.t.Errorf("testdata/%s/easy.go did not generate expected output", local) + } +} + +func testLocalEasy(tg *testgoData, local string) { + tg.creatingTemp("./hello" + exeSuffix) + tg.run("build", "-o", "hello"+exeSuffix, filepath.Join("testdata", local, "easy.go")) + testLocalRun(tg, local, `(?m)^easysub\.Hello`) +} + +func testLocalEasySub(tg *testgoData, local string) { + tg.creatingTemp("./hello" + exeSuffix) + tg.run("build", "-o", "hello"+exeSuffix, filepath.Join("testdata", local, "easysub", "main.go")) + testLocalRun(tg, local, `(?m)^easysub\.Hello`) +} + +func testLocalHard(tg *testgoData, local string) { + tg.creatingTemp("./hello" + exeSuffix) + tg.run("build", "-o", "hello"+exeSuffix, filepath.Join("testdata", local, "hard.go")) + testLocalRun(tg, local, `(?m)^sub\.Hello`) +} + +func testLocalInstall(tg *testgoData, local string) { + tg.runFail("install", filepath.Join("testdata", local, "easy.go")) +} + +func TestLocalImportsEasy(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + testLocalEasy(tg, "local") +} + +func TestLocalImportsEasySub(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + testLocalEasySub(tg, "local") +} + +func TestLocalImportsHard(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + testLocalHard(tg, "local") +} + +func TestLocalImportsGoInstallShouldFail(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + testLocalInstall(tg, "local") +} + +const badDirName = `#$%:, &()*;<=>?\^{}` + +func copyBad(tg *testgoData) { + if runtime.GOOS == "windows" { + tg.t.Skipf("skipping test because %q is an invalid directory name", badDirName) + } + + tg.must(filepath.Walk("testdata/local", + func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + var data []byte + data, err = ioutil.ReadFile(path) + if err != nil { + return err + } + newpath := strings.Replace(path, "local", badDirName, 1) + tg.tempFile(newpath, string(data)) + return nil + })) + tg.cd(tg.path(".")) +} + +func TestBadImportsEasy(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + copyBad(tg) + testLocalEasy(tg, badDirName) +} + +func TestBadImportsEasySub(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + copyBad(tg) + testLocalEasySub(tg, badDirName) +} + +func TestBadImportsHard(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + copyBad(tg) + testLocalHard(tg, badDirName) +} + +func TestBadImportsGoInstallShouldFail(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + copyBad(tg) + testLocalInstall(tg, badDirName) +} + +func TestInternalPackagesInGOROOTAreRespected(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.runFail("build", "-v", "./testdata/testinternal") + tg.grepBoth("use of internal package not allowed", "wrong error message for testdata/testinternal") +} + +func TestInternalPackagesOutsideGOROOTAreRespected(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.runFail("build", "-v", "./testdata/testinternal2") + tg.grepBoth("use of internal package not allowed", "wrote error message for testdata/testinternal2") +} + +func testMove(t *testing.T, vcs, url, base, config string) { + if testing.Short() { + t.Skip("skipping test that uses network in short mode") + } + tg := testgo(t) + defer tg.cleanup() + tg.tempDir("src") + tg.setenv("GOPATH", tg.path(".")) + tg.run("get", "-d", url) + tg.run("get", "-d", "-u", url) + switch vcs { + case "svn": + // SVN doesn't believe in text files so we can't just edit the config. + // Check out a different repo into the wrong place. + tg.must(os.RemoveAll(tg.path("src/code.google.com/p/rsc-svn"))) + tg.run("get", "-d", "-u", "code.google.com/p/rsc-svn2/trunk") + tg.must(os.Rename(tg.path("src/code.google.com/p/rsc-svn2"), tg.path("src/code.google.com/p/rsc-svn"))) + default: + path := tg.path(filepath.Join("src", config)) + data, err := ioutil.ReadFile(path) + tg.must(err) + data = bytes.Replace(data, []byte(base), []byte(base+"XXX"), -1) + tg.must(ioutil.WriteFile(path, data, 0644)) + } + if vcs == "git" { + // git will ask for a username and password when we + // run go get -d -f -u. An empty username and + // password will work. Prevent asking by setting + // GIT_ASKPASS. + tg.creatingTemp("sink" + exeSuffix) + tg.tempFile("src/sink/sink.go", `package main; func main() {}`) + tg.run("build", "-o", "sink"+exeSuffix, "sink") + tg.setenv("GIT_ASKPASS", filepath.Join(tg.pwd(), "sink"+exeSuffix)) + } + tg.runFail("get", "-d", "-u", url) + tg.grepStderr("is a custom import path for", "go get -d -u "+url+" failed for wrong reason") + tg.runFail("get", "-d", "-f", "-u", url) + tg.grepStderr("validating server certificate|not found", "go get -d -f -u "+url+" failed for wrong reason") +} + +func TestMoveGit(t *testing.T) { + testMove(t, "git", "rsc.io/pdf", "pdf", "rsc.io/pdf/.git/config") +} + +// TODO(rsc): Set up a test case on bitbucket for hg. +// func TestMoveHG(t *testing.T) { +// testMove(t, "hg", "rsc.io/x86/x86asm", "x86", "rsc.io/x86/.hg/hgrc") +// } + +// TODO(rsc): Set up a test case on SourceForge (?) for svn. +// func testMoveSVN(t *testing.T) { +// testMove(t, "svn", "code.google.com/p/rsc-svn/trunk", "-", "-") +// } + +func TestImportCommandMatch(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/importcom")) + tg.run("build", "./testdata/importcom/works.go") +} + +func TestImportCommentMismatch(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/importcom")) + tg.runFail("build", "./testdata/importcom/wrongplace.go") + tg.grepStderr(`wrongplace expects import "my/x"`, "go build did not mention incorrect import") +} + +func TestImportCommentSyntaxError(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/importcom")) + tg.runFail("build", "./testdata/importcom/bad.go") + tg.grepStderr("cannot parse import comment", "go build did not mention syntax error") +} + +func TestImportCommentConflict(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/importcom")) + tg.runFail("build", "./testdata/importcom/conflict.go") + tg.grepStderr("found import comments", "go build did not mention comment conflict") +} + +func TestDisallowedCSourceFiles(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.runFail("build", "badc") + tg.grepStderr("C source files not allowed", "go test did not say C source files not allowed") +} + +func TestErrorMessageForSyntaxErrorInTestGoFileSaysFAIL(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.runFail("test", "syntaxerror") + tg.grepStderr("FAIL", "go test did not say FAIL") +} + +func TestWildcardsDoNotLookInUselessDirectories(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.runFail("list", "...") + tg.grepBoth("badpkg", "go list ... failure does not mention badpkg") + tg.run("list", "m...") +} + +func TestRelativeImportsGoTest(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.run("test", "./testdata/testimport") +} + +func TestRelativeImportsGoTestDashI(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.run("test", "-i", "./testdata/testimport") +} + +func TestRelativeImportsInCommandLinePackage(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + files, err := filepath.Glob("./testdata/testimport/*.go") + tg.must(err) + tg.run(append([]string{"test"}, files...)...) +} + +func TestVersionControlErrorMessageIncludesCorrectDirectory(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata/shadow/root1")) + tg.runFail("get", "-u", "foo") + + // TODO(iant): We should not have to use strconv.Quote here. + // The code in vcs.go should be changed so that it is not required. + quoted := strconv.Quote(filepath.Join("testdata", "shadow", "root1", "src", "foo")) + quoted = quoted[1 : len(quoted)-1] + + tg.grepStderr(regexp.QuoteMeta(quoted), "go get -u error does not mention shadow/root1/src/foo") +} + +func TestInstallFailsWithNoBuildableFiles(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.setenv("CGO_ENABLED", "0") + tg.runFail("install", "cgotest") + tg.grepStderr("no buildable Go source files", "go install cgotest did not report 'no buildable Go Source files'") +} + +// Test that without $GOBIN set, binaries get installed +// into the GOPATH bin directory. +func TestInstallIntoGOPATH(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.creatingTemp("testdata/bin/go-cmd-test" + exeSuffix) + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.run("install", "go-cmd-test") + tg.wantExecutable("testdata/bin/go-cmd-test"+exeSuffix, "go install go-cmd-test did not write to testdata/bin/go-cmd-test") +} + +func TestPackageMainTestImportsArchiveNotBinary(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + gobin := filepath.Join(tg.pwd(), "testdata", "bin") + tg.creatingTemp(gobin) + tg.setenv("GOBIN", gobin) + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.must(os.Chtimes("./testdata/src/main_test/m.go", time.Now(), time.Now())) + tg.run("test", "main_test") + tg.run("install", "main_test") + tg.wantNotStale("main_test", "after go install, main listed as stale") + tg.run("test", "main_test") +} + +// With $GOBIN set, binaries get installed to $GOBIN. +func TestInstallIntoGOBIN(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + gobin := filepath.Join(tg.pwd(), "testdata", "bin1") + tg.creatingTemp(gobin) + tg.setenv("GOBIN", gobin) + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.run("install", "go-cmd-test") + tg.wantExecutable("testdata/bin1/go-cmd-test"+exeSuffix, "go install go-cmd-test did not write to testdata/bin1/go-cmd-test") +} + +// Without $GOBIN set, installing a program outside $GOPATH should fail +// (there is nowhere to install it). +func TestInstallWithoutDestinationFails(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.runFail("install", "testdata/src/go-cmd-test/helloworld.go") + tg.grepStderr("no install location for .go files listed on command line", "wrong error") +} + +// With $GOBIN set, should install there. +func TestInstallToGOBINCommandLinePackage(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + gobin := filepath.Join(tg.pwd(), "testdata", "bin1") + tg.creatingTemp(gobin) + tg.setenv("GOBIN", gobin) + tg.run("install", "testdata/src/go-cmd-test/helloworld.go") + tg.wantExecutable("testdata/bin1/helloworld"+exeSuffix, "go install testdata/src/go-cmd-test/helloworld.go did not write testdata/bin1/helloworld") +} + +func TestGodocInstalls(t *testing.T) { + if testing.Short() { + t.Skip("skipping test that uses network in short mode") + } + + // godoc installs into GOBIN + tg := testgo(t) + defer tg.cleanup() + tg.tempDir("gobin") + tg.setenv("GOPATH", tg.path(".")) + tg.setenv("GOBIN", tg.path("gobin")) + tg.run("get", "golang.org/x/tools/cmd/godoc") + tg.wantExecutable(tg.path("gobin/godoc"), "did not install godoc to $GOBIN") + tg.unsetenv("GOBIN") + + // godoc installs into GOROOT + goroot := runtime.GOROOT() + tg.setenv("GOROOT", goroot) + tg.check(os.RemoveAll(filepath.Join(goroot, "bin", "godoc"))) + tg.run("install", "golang.org/x/tools/cmd/godoc") + tg.wantExecutable(filepath.Join(goroot, "bin", "godoc"), "did not install godoc to $GOROOT/bin") +} + +func TestInstalls(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.tempDir("gobin") + tg.setenv("GOPATH", tg.path(".")) + goroot := runtime.GOROOT() + tg.setenv("GOROOT", goroot) + + // cmd/fix installs into tool + tg.run("env", "GOOS") + goos := strings.TrimSpace(tg.getStdout()) + tg.setenv("GOOS", goos) + tg.run("env", "GOARCH") + goarch := strings.TrimSpace(tg.getStdout()) + tg.setenv("GOARCH", goarch) + fixbin := filepath.Join(goroot, "pkg", "tool", goos+"_"+goarch, "fix") + exeSuffix + tg.must(os.RemoveAll(fixbin)) + tg.run("install", "cmd/fix") + tg.wantExecutable(fixbin, "did not install cmd/fix to $GOROOT/pkg/tool") + tg.must(os.Remove(fixbin)) + tg.setenv("GOBIN", tg.path("gobin")) + tg.run("install", "cmd/fix") + tg.wantExecutable(fixbin, "did not install cmd/fix to $GOROOT/pkg/tool with $GOBIN set") + tg.unsetenv("GOBIN") + + // gopath program installs into GOBIN + tg.tempFile("src/progname/p.go", `package main; func main() {}`) + tg.setenv("GOBIN", tg.path("gobin")) + tg.run("install", "progname") + tg.unsetenv("GOBIN") + tg.wantExecutable(tg.path("gobin/progname")+exeSuffix, "did not install progname to $GOBIN/progname") + + // gopath program installs into GOPATH/bin + tg.run("install", "progname") + tg.wantExecutable(tg.path("bin/progname")+exeSuffix, "did not install progname to $GOPATH/bin/progname") +} + +func TestRejectRelativePathsInGOPATHCommandLinePackage(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", ".") + tg.runFail("build", "testdata/src/go-cmd-test/helloworld.go") +} + +func TestRejectRelativePathsInGOPATH(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + sep := string(filepath.ListSeparator) + tg.setenv("GOPATH", sep+filepath.Join(tg.pwd(), "testdata")+sep+".") + tg.runFail("build", "go-cmd-test") +} + +// Issue 4104. +func TestGoTestWithPackageListedMultipleTimes(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.run("test", "fmt", "fmt", "fmt", "fmt", "fmt") + if strings.Index(strings.TrimSpace(tg.getStdout()), "\n") != -1 { + t.Error("go test fmt fmt fmt fmt fmt tested the same package multiple times") + } +} + +func TestGoListHasAConsistentOrder(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.run("list", "std") + first := tg.getStdout() + tg.run("list", "std") + if first != tg.getStdout() { + t.Error("go list std ordering is inconsistent") + } +} + +func TestGoListStdDoesNotIncludeCommands(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.run("list", "std") + tg.grepStdoutNot("cmd/", "go list std shows commands") +} + +func TestGoListCmdOnlyShowsCommands(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.run("list", "cmd") + out := strings.TrimSpace(tg.getStdout()) + for _, line := range strings.Split(out, "\n") { + if strings.Index(line, "cmd/") == -1 { + t.Error("go list cmd shows non-commands") + break + } + } +} + +// Issue 4096. Validate the output of unsuccessful go install foo/quxx. +func TestUnsuccessfulGoInstallShouldMentionMissingPackage(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.runFail("install", "foo/quxx") + if tg.grepCountBoth(`cannot find package "foo/quxx" in any of`) != 1 { + t.Error(`go install foo/quxx expected error: .*cannot find package "foo/quxx" in any of`) + } +} + +func TestGOROOTSearchFailureReporting(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.runFail("install", "foo/quxx") + if tg.grepCountBoth(regexp.QuoteMeta(filepath.Join("foo", "quxx"))+` \(from \$GOROOT\)$`) != 1 { + t.Error(`go install foo/quxx expected error: .*foo/quxx (from $GOROOT)`) + } +} + +func TestMultipleGOPATHEntriesReportedSeparately(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + sep := string(filepath.ListSeparator) + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata", "a")+sep+filepath.Join(tg.pwd(), "testdata", "b")) + tg.runFail("install", "foo/quxx") + if tg.grepCountBoth(`testdata[/\\].[/\\]src[/\\]foo[/\\]quxx`) != 2 { + t.Error(`go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)\n.*testdata/b/src/foo/quxx`) + } +} + +// Test (from $GOPATH) annotation is reported for the first GOPATH entry, +func TestMentionGOPATHInFirstGOPATHEntry(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + sep := string(filepath.ListSeparator) + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata", "a")+sep+filepath.Join(tg.pwd(), "testdata", "b")) + tg.runFail("install", "foo/quxx") + if tg.grepCountBoth(regexp.QuoteMeta(filepath.Join("testdata", "a", "src", "foo", "quxx"))+` \(from \$GOPATH\)$`) != 1 { + t.Error(`go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)`) + } +} + +// but not on the second. +func TestMentionGOPATHNotOnSecondEntry(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + sep := string(filepath.ListSeparator) + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata", "a")+sep+filepath.Join(tg.pwd(), "testdata", "b")) + tg.runFail("install", "foo/quxx") + if tg.grepCountBoth(regexp.QuoteMeta(filepath.Join("testdata", "b", "src", "foo", "quxx"))+`$`) != 1 { + t.Error(`go install foo/quxx expected error: .*testdata/b/src/foo/quxx`) + } +} + +// Test missing GOPATH is reported. +func TestMissingGOPATHIsReported(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", "") + tg.runFail("install", "foo/quxx") + if tg.grepCountBoth(`\(\$GOPATH not set\)$`) != 1 { + t.Error(`go install foo/quxx expected error: ($GOPATH not set)`) + } +} + +// Issue 4186. go get cannot be used to download packages to $GOROOT. +// Test that without GOPATH set, go get should fail. +func TestWithoutGOPATHGoGetFails(t *testing.T) { + if testing.Short() { + t.Skip("skipping test that uses network in short mode") + } + + tg := testgo(t) + defer tg.cleanup() + tg.tempDir("src") + tg.setenv("GOPATH", "") + tg.setenv("GOROOT", tg.path(".")) + tg.runFail("get", "-d", "golang.org/x/codereview/cmd/hgpatch") +} + +// Test that with GOPATH=$GOROOT, go get should fail. +func TestWithGOPATHEqualsGOROOTGoGetFails(t *testing.T) { + if testing.Short() { + t.Skip("skipping test that uses network in short mode") + } + + tg := testgo(t) + defer tg.cleanup() + tg.tempDir("src") + tg.setenv("GOPATH", tg.path(".")) + tg.setenv("GOROOT", tg.path(".")) + tg.runFail("get", "-d", "golang.org/x/codereview/cmd/hgpatch") +} + +func TestLdflagsArgumentsWithSpacesIssue3941(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.tempFile("main.go", `package main +var extern string +func main() { + println(extern) +}`) + tg.run("run", "-ldflags", `-X main.extern "hello world"`, tg.path("main.go")) + tg.grepStderr("^hello world", `ldflags -X main.extern 'hello world' failed`) +} + +func TestGoTestCpuprofileLeavesBinaryBehind(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.creatingTemp("strings.prof") + tg.creatingTemp("strings.test" + exeSuffix) + tg.run("test", "-cpuprofile", "strings.prof", "strings") + tg.wantExecutable("strings.test"+exeSuffix, "go test -cpuprofile did not create strings.test") +} + +func TestGoTestCpuProfileDashOControlsBinaryLocation(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.creatingTemp("strings.prof") + tg.creatingTemp("mystrings.test" + exeSuffix) + tg.run("test", "-cpuprofile", "strings.prof", "-o", "mystrings.test"+exeSuffix, "strings") + tg.wantExecutable("mystrings.test"+exeSuffix, "go test -cpuprofile -o mystrings.test did not create mystrings.test") +} + +func TestGoTestDashCDashOControlsBinaryLocation(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.creatingTemp("mystrings.test" + exeSuffix) + tg.run("test", "-c", "-o", "mystrings.test"+exeSuffix, "strings") + tg.wantExecutable("mystrings.test"+exeSuffix, "go test -c -o mystrings.test did not create mystrings.test") +} + +func TestGoTestDashOWritesBinary(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.creatingTemp("mystrings.test" + exeSuffix) + tg.run("test", "-o", "mystrings.test"+exeSuffix, "strings") + tg.wantExecutable("mystrings.test"+exeSuffix, "go test -o mystrings.test did not create mystrings.test") +} + +// Issue 4568. +func TestSymlinksDoNotConfuseGoList(t *testing.T) { + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("skipping symlink test on %s", runtime.GOOS) + } + + tg := testgo(t) + defer tg.cleanup() + tg.tempDir("src") + tg.must(os.Symlink(tg.path("."), tg.path("src/dir1"))) + tg.tempFile("src/dir1/p.go", "package p") + tg.setenv("GOPATH", tg.path(".")) + tg.cd(tg.path("src")) + tg.run("list", "-f", "{{.Root}}", "dir1") + if strings.TrimSpace(tg.getStdout()) != tg.path(".") { + t.Error("confused by symlinks") + } +} + +// Issue 4515. +func TestInstallWithTags(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.tempDir("bin") + tg.tempFile("src/example/a/main.go", `package main +func main() {}`) + tg.tempFile("src/example/b/main.go", `// +build mytag + +package main +func main() {}`) + tg.setenv("GOPATH", tg.path(".")) + tg.run("install", "-tags", "mytag", "example/a", "example/b") + tg.wantExecutable(tg.path("bin/a"+exeSuffix), "go install example/a example/b did not install binaries") + tg.wantExecutable(tg.path("bin/b"+exeSuffix), "go install example/a example/b did not install binaries") + tg.must(os.Remove(tg.path("bin/a" + exeSuffix))) + tg.must(os.Remove(tg.path("bin/b" + exeSuffix))) + tg.run("install", "-tags", "mytag", "example/...") + tg.wantExecutable(tg.path("bin/a"+exeSuffix), "go install example/... did not install binaries") + tg.wantExecutable(tg.path("bin/b"+exeSuffix), "go install example/... did not install binaries") + tg.run("list", "-tags", "mytag", "example/b...") + if strings.TrimSpace(tg.getStdout()) != "example/b" { + t.Error("go list example/b did not find example/b") + } +} + +// Issue 4773 +func TestCaseCollisions(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.tempDir("src/example/a/pkg") + tg.tempDir("src/example/a/Pkg") + tg.tempDir("src/example/b") + tg.setenv("GOPATH", tg.path(".")) + tg.tempFile("src/example/a/a.go", `package p +import ( + _ "example/a/pkg" + _ "example/a/Pkg" +)`) + tg.tempFile("src/example/a/pkg/pkg.go", `package pkg`) + tg.tempFile("src/example/a/Pkg/pkg.go", `package pkg`) + tg.runFail("list", "example/a") + tg.grepStderr("case-insensitive import collision", "go list example/a did not report import collision") + tg.tempFile("src/example/b/file.go", `package b`) + tg.tempFile("src/example/b/FILE.go", `package b`) + f, err := os.Open(tg.path("src/example/b")) + tg.must(err) + names, err := f.Readdirnames(0) + tg.must(err) + tg.check(f.Close()) + args := []string{"list"} + if len(names) == 2 { + // case-sensitive file system, let directory read find both files + args = append(args, "example/b") + } else { + // case-insensitive file system, list files explicitly on command line + args = append(args, tg.path("src/example/b/file.go"), tg.path("src/example/b/FILE.go")) + } + tg.runFail(args...) + tg.grepStderr("case-insensitive file name collision", "go list example/b did not report file name collision") +} + +// Issue 8181. +func TestGoGetDashTIssue8181(t *testing.T) { + if testing.Short() { + t.Skip("skipping test that uses network in short mode") + } + + tg := testgo(t) + defer tg.cleanup() + tg.makeTempdir() + tg.setenv("GOPATH", tg.path(".")) + tg.run("get", "-t", "code.google.com/p/go-get-issue-8181/a", "code.google.com/p/go-get-issue-8181/b") + tg.runFail("list", "...") + tg.grepStdout("go.tools/godoc", "missing expected go.tools/godoc") +} + +func TestShadowingLogic(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + pwd := tg.pwd() + sep := string(filepath.ListSeparator) + tg.setenv("GOPATH", filepath.Join(pwd, "testdata", "shadow", "root1")+sep+filepath.Join(pwd, "testdata", "shadow", "root2")) + + // The math in root1 is not "math" because the standard math is. + tg.run("list", "-f", "({{.ImportPath}}) ({{.ConflictDir}})", "./testdata/shadow/root1/src/math") + pwdForwardSlash := strings.Replace(pwd, string(os.PathSeparator), "/", -1) + if !strings.HasPrefix(pwdForwardSlash, "/") { + pwdForwardSlash = "/" + pwdForwardSlash + } + // The output will have makeImportValid applies, but we only + // bother to deal with characters we might reasonably see. + pwdForwardSlash = strings.Replace(pwdForwardSlash, ":", "_", -1) + want := "(_" + pwdForwardSlash + "/testdata/shadow/root1/src/math) (" + filepath.Join(runtime.GOROOT(), "src", "math") + ")" + if strings.TrimSpace(tg.getStdout()) != want { + t.Error("shadowed math is not shadowed; looking for", want) + } + + // The foo in root1 is "foo". + tg.run("list", "-f", "({{.ImportPath}}) ({{.ConflictDir}})", "./testdata/shadow/root1/src/foo") + if strings.TrimSpace(tg.getStdout()) != "(foo) ()" { + t.Error("unshadowed foo is shadowed") + } + + // The foo in root2 is not "foo" because the foo in root1 got there first. + tg.run("list", "-f", "({{.ImportPath}}) ({{.ConflictDir}})", "./testdata/shadow/root2/src/foo") + want = "(_" + pwdForwardSlash + "/testdata/shadow/root2/src/foo) (" + filepath.Join(pwd, "testdata", "shadow", "root1", "src", "foo") + ")" + if strings.TrimSpace(tg.getStdout()) != want { + t.Error("shadowed foo is not shadowed; looking for", want) + } + + // The error for go install should mention the conflicting directory. + tg.runFail("install", "./testdata/shadow/root2/src/foo") + want = "go install: no install location for " + filepath.Join(pwd, "testdata", "shadow", "root2", "src", "foo") + ": hidden by " + filepath.Join(pwd, "testdata", "shadow", "root1", "src", "foo") + if strings.TrimSpace(tg.getStderr()) != want { + t.Error("wrong shadowed install error; looking for", want) + } +} + +// Only succeeds if source order is preserved. +func TestSourceFileNameOrderPreserved(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.run("test", "testdata/example1_test.go", "testdata/example2_test.go") +} + +// Check that coverage analysis works at all. +// Don't worry about the exact numbers but require not 0.0%. +func checkCoverage(tg *testgoData, data string) { + if regexp.MustCompile(`[^0-9]0\.0%`).MatchString(data) { + tg.t.Error("some coverage results are 0.0%") + } + tg.t.Log(data) +} + +func TestCoverageRuns(t *testing.T) { + if testing.Short() { + t.Skip("don't build libraries for coverage in short mode") + } + tg := testgo(t) + defer tg.cleanup() + tg.run("test", "-short", "-coverpkg=strings", "strings", "regexp") + data := tg.getStdout() + tg.getStderr() + tg.run("test", "-short", "-cover", "strings", "math", "regexp") + data += tg.getStdout() + tg.getStderr() + checkCoverage(tg, data) +} + +// Check that coverage analysis uses set mode. +func TestCoverageUsesSetMode(t *testing.T) { + if testing.Short() { + t.Skip("don't build libraries for coverage in short mode") + } + tg := testgo(t) + defer tg.cleanup() + tg.creatingTemp("testdata/cover.out") + tg.run("test", "-short", "-cover", "encoding/binary", "-coverprofile=testdata/cover.out") + data := tg.getStdout() + tg.getStderr() + if out, err := ioutil.ReadFile("testdata/cover.out"); err != nil { + t.Error(err) + } else { + if !bytes.Contains(out, []byte("mode: set")) { + t.Error("missing mode: set") + } + } + checkCoverage(tg, data) +} + +func TestCoverageUsesAtomicModeForRace(t *testing.T) { + if testing.Short() { + t.Skip("don't build libraries for coverage in short mode") + } + if !canRace { + t.Skip("skipping because race detector not supported") + } + + tg := testgo(t) + defer tg.cleanup() + tg.creatingTemp("testdata/cover.out") + tg.run("test", "-short", "-race", "-cover", "encoding/binary", "-coverprofile=testdata/cover.out") + data := tg.getStdout() + tg.getStderr() + if out, err := ioutil.ReadFile("testdata/cover.out"); err != nil { + t.Error(err) + } else { + if !bytes.Contains(out, []byte("mode: atomic")) { + t.Error("missing mode: atomic") + } + } + checkCoverage(tg, data) +} + +func TestCoverageUsesActualSettingToOverrideEvenForRace(t *testing.T) { + if testing.Short() { + t.Skip("don't build libraries for coverage in short mode") + } + if !canRace { + t.Skip("skipping because race detector not supported") + } + + tg := testgo(t) + defer tg.cleanup() + tg.creatingTemp("testdata/cover.out") + tg.run("test", "-short", "-race", "-cover", "encoding/binary", "-covermode=count", "-coverprofile=testdata/cover.out") + data := tg.getStdout() + tg.getStderr() + if out, err := ioutil.ReadFile("testdata/cover.out"); err != nil { + t.Error(err) + } else { + if !bytes.Contains(out, []byte("mode: count")) { + t.Error("missing mode: count") + } + } + checkCoverage(tg, data) +} + +func TestCoverageWithCgo(t *testing.T) { + if !canCgo { + t.Skip("skipping because cgo not enabled") + } + + tg := testgo(t) + defer tg.cleanup() + tg.run("test", "-short", "-cover", "./testdata/cgocover") + data := tg.getStdout() + tg.getStderr() + checkCoverage(tg, data) +} + +func TestCgoDependsOnSyscall(t *testing.T) { + if testing.Short() { + t.Skip("skipping test that removes $GOROOT/pkg/*_race in short mode") + } + if !canCgo { + t.Skip("skipping because cgo not enabled") + } + if !canRace { + t.Skip("skipping because race detector not supported") + } + + tg := testgo(t) + defer tg.cleanup() + files, err := filepath.Glob(filepath.Join(runtime.GOROOT(), "pkg", "*_race")) + tg.must(err) + for _, file := range files { + tg.check(os.RemoveAll(file)) + } + tg.tempFile("src/foo/foo.go", ` +package foo +//#include +import "C"`) + tg.setenv("GOPATH", tg.path(".")) + tg.run("build", "-race", "foo") +} + +func TestCgoShowsFullPathNames(t *testing.T) { + if !canCgo { + t.Skip("skipping because cgo not enabled") + } + + tg := testgo(t) + defer tg.cleanup() + tg.tempFile("src/x/y/dirname/foo.go", ` +package foo +import "C" +func f() {`) + tg.setenv("GOPATH", tg.path(".")) + tg.runFail("build", "x/y/dirname") + tg.grepBoth("x/y/dirname", "error did not use full path") +} + +func TestCgoHandlesWlORIGIN(t *testing.T) { + if !canCgo { + t.Skip("skipping because cgo not enabled") + } + + tg := testgo(t) + defer tg.cleanup() + tg.tempFile("src/origin/origin.go", `package origin +// #cgo !darwin LDFLAGS: -Wl,-rpath -Wl,$ORIGIN +// void f(void) {} +import "C" + +func f() { C.f() }`) + tg.setenv("GOPATH", tg.path(".")) + tg.run("build", "origin") +} + +// "go test -c -test.bench=XXX fmt" should not hang' +func TestIssue6480(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.creatingTemp("fmt.test" + exeSuffix) + tg.run("test", "-c", "-test.bench=XXX", "fmt") +} + +// cmd/cgo: undefined reference when linking a C-library using gccgo +func TestIssue7573(t *testing.T) { + if _, err := exec.LookPath("gccgo"); err != nil { + t.Skip("skipping because no gccgo compiler found") + } + + tg := testgo(t) + defer tg.cleanup() + tg.tempFile("src/cgoref/cgoref.go", ` +package main +// #cgo LDFLAGS: -L alibpath -lalib +// void f(void) {} +import "C" + +func main() { C.f() }`) + tg.setenv("GOPATH", tg.path(".")) + tg.run("build", "-n", "-compiler", "gccgo", "cgoref") + tg.grepStderr(`gccgo.*\-L alibpath \-lalib`, `no Go-inline "#cgo LDFLAGS:" ("-L alibpath -lalib") passed to gccgo linking stage`) +} + +func TestListTemplateCanUseContextFunction(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.run("list", "-f", "GOARCH: {{context.GOARCH}}") +} + +// cmd/go: "go test" should fail if package does not build +func TestIssue7108(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.runFail("test", "notest") +} + +// cmd/go: go test -a foo does not rebuild regexp. +func TestIssue6844(t *testing.T) { + if testing.Short() { + t.Skip("don't rebuild the standard libary in short mode") + } + + tg := testgo(t) + defer tg.cleanup() + tg.creatingTemp("deps.test" + exeSuffix) + tg.run("test", "-x", "-a", "-c", "testdata/dep_test.go") + tg.grepStderr("regexp", "go test -x -a -c testdata/dep-test.go did not rebuild regexp") +} + +func TestBuildDashIInstallsDependencies(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.tempFile("src/x/y/foo/foo.go", `package foo +func F() {}`) + tg.tempFile("src/x/y/bar/bar.go", `package bar +import "x/y/foo" +func F() { foo.F() }`) + tg.setenv("GOPATH", tg.path(".")) + + checkbar := func(desc string) { + // TODO(iant): Sleep for one "tick", where a tick is + // the granularity of mtime on the file system. + time.Sleep(time.Second) + tg.must(os.Chtimes(tg.path("src/x/y/foo/foo.go"), time.Now(), time.Now())) + time.Sleep(time.Second) + tg.run("build", "-v", "-i", "x/y/bar") + tg.grepBoth("x/y/foo", "first build -i "+desc+" did not build x/y/foo") + tg.run("build", "-v", "-i", "x/y/bar") + tg.grepBothNot("x/y/foo", "second build -i "+desc+" built x/y/foo") + } + checkbar("pkg") + tg.creatingTemp("bar" + exeSuffix) + tg.tempFile("src/x/y/bar/bar.go", `package main +import "x/y/foo" +func main() { foo.F() }`) + checkbar("cmd") +} + +func TestGoBuildInTestOnlyDirectoryFailsWithAGoodError(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.runFail("build", "./testdata/testonly") + tg.grepStderr("no buildable Go", "go build ./testdata/testonly produced unexpected error") +} + +func TestGoTestDetectsTestOnlyImportCycles(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.runFail("test", "-c", "testcycle/p3") + tg.grepStderr("import cycle not allowed in test", "go test testcycle/p3 produced unexpected error") +} + +func TestGoTestFooTestWorks(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.run("test", "testdata/standalone_test.go") +} + +func TestGoTestXtestonlyWorks(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.run("clean", "-i", "xtestonly") + tg.run("test", "xtestonly") +} + +func TestGoTestBuildsAnXtestContainingOnlyNonRunnableExamples(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.run("test", "-v", "./testdata/norunexample") + tg.grepStdout("File with non-runnable example was built.", "file with non-runnable example was not built") +} + +func TestGoGenerateHandlesSimpleCommand(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("skipping because windows has no echo command") + } + + tg := testgo(t) + defer tg.cleanup() + tg.run("generate", "./testdata/generate/test1.go") + tg.grepStdout("Success", "go generate ./testdata/generate/test1.go generated wrong output") +} + +func TestGoGenerateHandlesCommandAlias(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("skipping because windows has no echo command") + } + + tg := testgo(t) + defer tg.cleanup() + tg.run("generate", "./testdata/generate/test2.go") + tg.grepStdout("Now is the time for all good men", "go generate ./testdata/generate/test2.go generated wrong output") +} + +func TestGoGenerateVariableSubstitution(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("skipping because windows has no echo command") + } + + tg := testgo(t) + defer tg.cleanup() + tg.run("generate", "./testdata/generate/test3.go") + tg.grepStdout(runtime.GOARCH+" test3.go:7 pabc xyzp/test3.go/123", "go generate ./testdata/generate/test3.go generated wrong output") +} + +func TestGoGenerateRunFlag(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("skipping because windows has no echo command") + } + + tg := testgo(t) + defer tg.cleanup() + tg.run("generate", "-run", "y.s", "./testdata/generate/test4.go") + tg.grepStdout("yes", "go generate -run yes ./testdata/generate/test4.go did not select yes") + tg.grepStdoutNot("no", "go generate -run yes ./testdata/generate/test4.go selected no") +} + +func TestGoGetWorksWithVanityWildcards(t *testing.T) { + if testing.Short() { + t.Skip("skipping test that uses network in short mode") + } + + tg := testgo(t) + defer tg.cleanup() + tg.makeTempdir() + tg.setenv("GOPATH", tg.path(".")) + tg.run("get", "-u", "rsc.io/pdf/...") + tg.wantExecutable(tg.path("bin/pdfpasswd"+exeSuffix), "did not build rsc/io/pdf/pdfpasswd") +} + +func TestGoVetWithExternalTests(t *testing.T) { + if testing.Short() { + t.Skip("skipping test that uses network in short mode") + } + + tg := testgo(t) + defer tg.cleanup() + tg.makeTempdir() + tg.setenv("GOPATH", tg.path(".")) + tg.run("get", "golang.org/x/tools/cmd/vet") + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.runFail("vet", "vetpkg") + tg.grepBoth("missing argument for Printf", "go vet vetpkg did not find missing argument for Printf") +} + +func TestGoVetWithTags(t *testing.T) { + if testing.Short() { + t.Skip("skipping test that uses network in short mode") + } + + tg := testgo(t) + defer tg.cleanup() + tg.makeTempdir() + tg.setenv("GOPATH", tg.path(".")) + tg.run("get", "golang.org/x/tools/cmd/vet") + tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) + tg.runFail("vet", "-tags", "tagtest", "vetpkg") + tg.grepBoth(`c\.go.*wrong number of args for format`, "go get vetpkg did not run scan tagged file") +} + +// Issue 9767. +func TestGoGetRscIoToolstash(t *testing.T) { + if testing.Short() { + t.Skip("skipping test that uses network in short mode") + } + + tg := testgo(t) + defer tg.cleanup() + tg.tempDir("src/rsc.io") + tg.setenv("GOPATH", tg.path(".")) + tg.cd(tg.path("src/rsc.io")) + tg.run("get", "./toolstash") } diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash deleted file mode 100755 index d9ac3f793e..0000000000 --- a/src/cmd/go/test.bash +++ /dev/null @@ -1,1330 +0,0 @@ -#!/bin/bash -# Copyright 2012 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. - -set -e -go build -tags testgo -o testgo -go() { - echo TEST ERROR: ran go, not testgo: go "$@" >&2 - exit 2 -} - -started=false -testdesc="" -nl=" -" -TEST() { - if $started; then - stop - fi - echo TEST: "$@" - testdesc="$@" - started=true - ok=true -} -stop() { - if ! $started; then - echo TEST ERROR: stop missing start >&2 - exit 2 - fi - started=false - if $ok; then - echo PASS - else - echo FAIL - testfail="$testfail $testdesc$nl" - allok=false - fi -} - -ok=true -allok=true - -unset GOBIN -unset GOPATH -unset GOROOT - -TEST 'program name in crash messages' -linker=$(./testgo env GOCHAR)l -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -./testgo build -ldflags -crash_for_testing $(./testgo env GOROOT)/test/helloworld.go 2>$d/err.out || true -if ! grep -q "/tool/.*/$linker" $d/err.out; then - echo "missing linker name in error message" - cat $d/err.out - ok=false -fi -rm -r $d - -TEST broken tests without Test functions all fail -d=$(mktemp -d -t testgoXXX) -./testgo test ./testdata/src/badtest/... >$d/err 2>&1 || true -if grep -q '^ok' $d/err; then - echo test passed unexpectedly: - grep '^ok' $d/err - ok=false -elif ! grep -q 'FAIL.*badtest/badexec' $d/err || ! grep -q 'FAIL.*badtest/badsyntax' $d/err || ! grep -q 'FAIL.*badtest/badvar' $d/err; then - echo test did not run everything - cat $d/err - ok=false -fi -rm -rf $d - -TEST 'go build -a in dev branch' -./testgo install math || ok=false # should be up to date already but just in case -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -if ! TESTGO_IS_GO_RELEASE=0 ./testgo build -v -a math 2>$d/err.out; then - cat $d/err.out - ok=false -elif ! grep -q runtime $d/err.out; then - echo "testgo build -a math in dev branch DID NOT build runtime, but should have" - cat $d/err.out - ok=false -fi -rm -r $d - -TEST 'go build -a in release branch' -./testgo install math || ok=false # should be up to date already but just in case -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -if ! TESTGO_IS_GO_RELEASE=1 ./testgo build -v -a math 2>$d/err.out; then - cat $d/err.out - ok=false -elif grep -q runtime $d/err.out; then - echo "testgo build -a math in dev branch DID build runtime, but should NOT have" - cat $d/err.out - ok=false -fi -rm -r $d - -TEST 'go install cleans up after go build' -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -export GOPATH=$d -mkdir -p $d/src/mycmd -echo 'package main; func main(){}' >$d/src/mycmd/main.go -old=$(pwd) -cd $d/src/mycmd -"$old/testgo" build -if [ ! -x mycmd ]; then - echo "testgo build did not write command binary" - ok=false -fi -"$old/testgo" install -if [ -e mycmd ]; then - echo "testgo install did not remove command binary" - ok=false -fi -"$old/testgo" build -if [ ! -x mycmd ]; then - echo "testgo build did not write command binary (second time)" - ok=false -fi -# install with arguments does not remove the target, -# even in the same directory -"$old/testgo" install mycmd -if [ ! -e mycmd ]; then - echo "testgo install mycmd removed command binary when run in mycmd" - ok=false -fi -"$old/testgo" build -if [ ! -x mycmd ]; then - echo "testgo build did not write command binary (third time)" - ok=false -fi -# and especially not outside the directory -cd $d -cp src/mycmd/mycmd . -"$old/testgo" install mycmd -if [ ! -e $d/src/mycmd/mycmd ]; then - echo "testgo install mycmd removed command binary from its source dir when run outside mycmd" - ok=false -fi -if [ ! -e $d/mycmd ]; then - echo "testgo install mycmd removed command binary from current dir when run outside mycmd" - ok=false -fi -cd "$old" -rm -r $d - -TEST 'go install rebuilds stale packages in other GOPATH' -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -export GOPATH=$d/d1:$d/d2 -mkdir -p $d/d1/src/p1 $d/d2/src/p2 -echo 'package p1 - -import "p2" - -func F() { p2.F() } -' > $d/d1/src/p1/p1.go -echo 'package p2 - -func F() {} -' > $d/d2/src/p2/p2.go -if ! ./testgo install p1; then - echo "./testgo install p1 failed" - ok=false -elif [ "$(./testgo list -f '{{.Stale}}' p1)" != false ]; then - echo "./testgo list mypkg claims p1 is stale, incorrectly" - ok=false -elif [ "$(./testgo list -f '{{.Stale}}' p2)" != false ]; then - echo "./testgo list mypkg claims p2 is stale, incorrectly" - ok=false -else - sleep 1 - echo 'func G() {}' >>$d/d2/src/p2/p2.go - if [ "$(./testgo list -f '{{.Stale}}' p2)" != true ]; then - echo "./testgo list mypkg claims p2 is NOT stale, incorrectly" - ok=false - elif [ "$(./testgo list -f '{{.Stale}}' p1)" != true ]; then - echo "./testgo list mypkg claims p1 is NOT stale, incorrectly" - ok=false - fi - - if ! ./testgo install p1; then - echo "./testgo install p1 failed second time" - ok=false - else - if [ "$(./testgo list -f '{{.Stale}}' p2)" != false ]; then - echo "./testgo list mypkg claims p2 is stale after reinstall, incorrectly" - ok=false - elif [ "$(./testgo list -f '{{.Stale}}' p1)" != false ]; then - echo "./testgo list mypkg claims p1 is stale after reinstall, incorrectly" - ok=false - fi - fi -fi -rm -r $d - -TEST 'go install detects removed files' -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -export GOPATH=$d -mkdir -p $d/src/mypkg -echo package mypkg >$d/src/mypkg/x.go -echo package mypkg >$d/src/mypkg/y.go -echo '// +build missingtag - -package mypkg' >$d/src/mypkg/z.go -if ! ./testgo install mypkg; then - echo "./testgo install mypkg failed" - ok=false -elif [ "$(./testgo list -f '{{.Stale}}' mypkg)" != false ]; then - echo "./testgo list mypkg claims mypkg is stale, incorrectly" - ok=false -else - # z.go was not part of the build; removing it is okay. - rm $d/src/mypkg/z.go - if [ "$(./testgo list -f '{{.Stale}}' mypkg)" != false ]; then - echo "./testgo list mypkg claims mypkg is stale after removing z.go; should not be stale" - ok=false - ./testgo install mypkg - fi - # y.go was part of the package; removing it should be detected. - rm $d/src/mypkg/y.go - if [ "$(./testgo list -f '{{.Stale}}' mypkg)" != true ]; then - echo "./testgo list mypkg claims mypkg is NOT stale after removing y.go; should be stale" - ok=false - fi -fi -rm -r $d - -TEST 'go install detects removed files in package main' -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -export GOPATH=$d -mkdir -p $d/src/mycmd -echo 'package main - -func main() {} -' >$d/src/mycmd/x.go -echo package main >$d/src/mycmd/y.go -echo '// +build missingtag - -package main' >$d/src/mycmd/z.go -if ! ./testgo install mycmd; then - echo "./testgo install mycmd failed" - ok=false -elif [ "$(./testgo list -f '{{.Stale}}' mycmd)" != false ]; then - echo "./testgo list mypkg claims mycmd is stale, incorrectly" - ok=false -else - # z.go was not part of the build; removing it is okay. - rm $d/src/mycmd/z.go - if [ "$(./testgo list -f '{{.Stale}}' mycmd)" != false ]; then - echo "./testgo list mycmd claims mycmd is stale after removing z.go; should not be stale" - ok=false - ./testgo install mycmd - fi - # y.go was part of the package; removing it should be detected. - rm $d/src/mycmd/y.go - if [ "$(./testgo list -f '{{.Stale}}' mycmd)" != true ]; then - echo "./testgo list mycmd claims mycmd is NOT stale after removing y.go; should be stale" - ok=false - fi -fi -rm -r $d - - -# Test local (./) imports. -testlocal() { - local="$1" - TEST local imports $2 '(easy)' - ./testgo build -o hello "testdata/$local/easy.go" || ok=false - ./hello >hello.out || ok=false - if ! grep -q '^easysub\.Hello' hello.out; then - echo "testdata/$local/easy.go did not generate expected output" - cat hello.out - ok=false - fi - - TEST local imports $2 '(easysub)' - ./testgo build -o hello "testdata/$local/easysub/main.go" || ok=false - ./hello >hello.out || ok=false - if ! grep -q '^easysub\.Hello' hello.out; then - echo "testdata/$local/easysub/main.go did not generate expected output" - cat hello.out - ok=false - fi - - TEST local imports $2 '(hard)' - ./testgo build -o hello "testdata/$local/hard.go" || ok=false - ./hello >hello.out || ok=false - if ! grep -q '^sub\.Hello' hello.out || ! grep -q '^subsub\.Hello' hello.out ; then - echo "testdata/$local/hard.go did not generate expected output" - cat hello.out - ok=false - fi - - rm -f hello.out hello - - # Test that go install x.go fails. - TEST local imports $2 '(go install should fail)' - if ./testgo install "testdata/$local/easy.go" >/dev/null 2>&1; then - echo "go install testdata/$local/easy.go succeeded" - ok=false - fi -} - -# Test local imports -testlocal local '' - -# Test local imports again, with bad characters in the directory name. -bad='#$%:, &()*;<=>?\^{}' -rm -rf "testdata/$bad" -cp -R testdata/local "testdata/$bad" -testlocal "$bad" 'with bad characters in path' -rm -rf "testdata/$bad" - -TEST 'internal packages in $GOROOT are respected' -if ./testgo build -v ./testdata/testinternal >testdata/std.out 2>&1; then - echo "go build ./testdata/testinternal succeeded incorrectly" - ok=false -elif ! grep 'use of internal package not allowed' testdata/std.out >/dev/null; then - echo "wrong error message for testdata/testinternal" - cat std.out - ok=false -fi - -TEST 'internal packages outside $GOROOT are respected (as of Go 1.5)' -if ./testgo build -v ./testdata/testinternal2 >testdata/std.out 2>&1; then - echo "go build ./testdata/testinternal2 succeeded incorrectly" - ok=false -elif ! grep 'use of internal package not allowed' testdata/std.out >/dev/null; then - echo "wrong error message for testdata/testinternal2" - cat std.out - ok=false -fi - -# Test that 'go get -u' reports moved packages. -testmove() { - vcs=$1 - url=$2 - base=$3 - config=$4 - - TEST go get -u notices $vcs package that moved - d=$(mktemp -d -t testgoXXX) - mkdir -p $d/src - if ! GOPATH=$d ./testgo get -d $url; then - echo 'go get -d $url failed' - ok=false - elif ! GOPATH=$d ./testgo get -d -u $url; then - echo 'go get -d -u $url failed' - ok=false - else - set +e - case "$vcs" in - svn) - # SVN doesn't believe in text files so we can't just edit the config. - # Check out a different repo into the wrong place. - rm -rf $d/src/code.google.com/p/rsc-svn - GOPATH=$d ./testgo get -d -u code.google.com/p/rsc-svn2/trunk - mv $d/src/code.google.com/p/rsc-svn2 $d/src/code.google.com/p/rsc-svn - ;; - *) - echo '1,$s;'"$base"';'"$base"'XXX; -w -q' | ed $d/src/$config >/dev/null 2>&1 - esac - set -e - - if GOPATH=$d ./testgo get -d -u $url 2>$d/err; then - echo "go get -d -u $url succeeded with wrong remote repo" - cat $d/err - ok=false - elif ! grep 'is a custom import path for' $d/err >/dev/null; then - echo "go get -d -u $url failed for wrong reason" - cat $d/err - ok=false - fi - - if GOPATH=$d ./testgo get -d -f -u $url 2>$d/err; then - echo "go get -d -u $url succeeded with wrong remote repo" - cat $d/err - ok=false - elif ! egrep -i 'validating server certificate|not found' $d/err >/dev/null; then - echo "go get -d -f -u $url failed for wrong reason" - cat $d/err - ok=false - fi - fi - rm -rf $d -} - -testmove git rsc.io/pdf pdf rsc.io/pdf/.git/config - -# TODO(rsc): Set up a test case on bitbucket for hg. -# testmove hg rsc.io/x86/x86asm x86 rsc.io/x86/.hg/hgrc - -# TODO(rsc): Set up a test case on SourceForge (?) for svn. -# testmove svn code.google.com/p/rsc-svn/trunk - - - -export GOPATH=$(pwd)/testdata/importcom -TEST 'import comment - match' -if ! ./testgo build ./testdata/importcom/works.go; then - echo 'go build ./testdata/importcom/works.go failed' - ok=false -fi -TEST 'import comment - mismatch' -if ./testgo build ./testdata/importcom/wrongplace.go 2>testdata/err; then - echo 'go build ./testdata/importcom/wrongplace.go suceeded' - ok=false -elif ! grep 'wrongplace expects import "my/x"' testdata/err >/dev/null; then - echo 'go build did not mention incorrect import:' - cat testdata/err - ok=false -fi -TEST 'import comment - syntax error' -if ./testgo build ./testdata/importcom/bad.go 2>testdata/err; then - echo 'go build ./testdata/importcom/bad.go suceeded' - ok=false -elif ! grep 'cannot parse import comment' testdata/err >/dev/null; then - echo 'go build did not mention syntax error:' - cat testdata/err - ok=false -fi -TEST 'import comment - conflict' -if ./testgo build ./testdata/importcom/conflict.go 2>testdata/err; then - echo 'go build ./testdata/importcom/conflict.go suceeded' - ok=false -elif ! grep 'found import comments' testdata/err >/dev/null; then - echo 'go build did not mention comment conflict:' - cat testdata/err - ok=false -fi -rm -f ./testdata/err -unset GOPATH - -export GOPATH=$(pwd)/testdata/src -TEST disallowed C source files -export GOPATH=$(pwd)/testdata -if ./testgo build badc 2>testdata/err; then - echo 'go build badc succeeded' - ok=false -elif ! grep 'C source files not allowed' testdata/err >/dev/null; then - echo 'go test did not say C source files not allowed:' - cat testdata/err - ok=false -fi -rm -f ./testdata/err -unset GOPATH - -TEST error message for syntax error in test go file says FAIL -export GOPATH=$(pwd)/testdata -if ./testgo test syntaxerror 2>testdata/err; then - echo 'go test syntaxerror succeeded' - ok=false -elif ! grep FAIL testdata/err >/dev/null; then - echo 'go test did not say FAIL:' - cat testdata/err - ok=false -fi -rm -f ./testdata/err -unset GOPATH - -TEST wildcards do not look in useless directories -export GOPATH=$(pwd)/testdata -if ./testgo list ... >testdata/err 2>&1; then - echo "go list ... succeeded" - ok=false -elif ! grep badpkg testdata/err >/dev/null; then - echo "go list ... failure does not mention badpkg" - cat testdata/err - ok=false -elif ! ./testgo list m... >testdata/err 2>&1; then - echo "go list m... failed" - ok=false -fi -rm -rf ./testdata/err -unset GOPATH - -# Test tests with relative imports. -TEST relative imports '(go test)' -if ! ./testgo test ./testdata/testimport; then - echo "go test ./testdata/testimport failed" - ok=false -fi - -# Test installation with relative imports. -TEST relative imports '(go test -i)' -if ! ./testgo test -i ./testdata/testimport; then - echo "go test -i ./testdata/testimport failed" - ok=false -fi - -# Test tests with relative imports in packages synthesized -# from Go files named on the command line. -TEST relative imports in command-line package -if ! ./testgo test ./testdata/testimport/*.go; then - echo "go test ./testdata/testimport/*.go failed" - ok=false -fi - -TEST version control error message includes correct directory -export GOPATH=$(pwd)/testdata/shadow/root1 -if ./testgo get -u foo 2>testdata/err; then - echo "go get -u foo succeeded unexpectedly" - ok=false -elif ! grep testdata/shadow/root1/src/foo testdata/err >/dev/null; then - echo "go get -u error does not mention shadow/root1/src/foo:" - cat testdata/err - ok=false -fi -unset GOPATH - -TEST go install fails with no buildable files -export GOPATH=$(pwd)/testdata -export CGO_ENABLED=0 -if ./testgo install cgotest 2>testdata/err; then - echo "go install cgotest succeeded unexpectedly" -elif ! grep 'no buildable Go source files' testdata/err >/dev/null; then - echo "go install cgotest did not report 'no buildable Go source files'" - cat testdata/err - ok=false -fi -unset CGO_ENABLED -unset GOPATH - -# Test that without $GOBIN set, binaries get installed -# into the GOPATH bin directory. -TEST install into GOPATH -rm -rf testdata/bin -if ! GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then - echo "go install go-cmd-test failed" - ok=false -elif ! test -x testdata/bin/go-cmd-test; then - echo "go install go-cmd-test did not write to testdata/bin/go-cmd-test" - ok=false -fi - -TEST package main_test imports archive not binary -export GOBIN=$(pwd)/testdata/bin -mkdir -p $GOBIN -export GOPATH=$(pwd)/testdata -touch ./testdata/src/main_test/m.go -if ! ./testgo test main_test; then - echo "go test main_test failed without install" - ok=false -elif ! ./testgo install main_test; then - echo "go test main_test failed" - ok=false -elif [ "$(./testgo list -f '{{.Stale}}' main_test)" != false ]; then - echo "after go install, main listed as stale" - ok=false -elif ! ./testgo test main_test; then - echo "go test main_test failed after install" - ok=false -fi -rm -rf $GOBIN -unset GOBIN - -# And with $GOBIN set, binaries get installed to $GOBIN. -TEST install into GOBIN -if ! GOBIN=$(pwd)/testdata/bin1 GOPATH=$(pwd)/testdata ./testgo install go-cmd-test; then - echo "go install go-cmd-test failed" - ok=false -elif ! test -x testdata/bin1/go-cmd-test; then - echo "go install go-cmd-test did not write to testdata/bin1/go-cmd-test" - ok=false -fi - -# Without $GOBIN set, installing a program outside $GOPATH should fail -# (there is nowhere to install it). -TEST install without destination fails -if ./testgo install testdata/src/go-cmd-test/helloworld.go 2>testdata/err; then - echo "go install testdata/src/go-cmd-test/helloworld.go should have failed, did not" - ok=false -elif ! grep 'no install location for .go files listed on command line' testdata/err; then - echo "wrong error:" - cat testdata/err - ok=false -fi -rm -f testdata/err - -# With $GOBIN set, should install there. -TEST install to GOBIN '(command-line package)' -if ! GOBIN=$(pwd)/testdata/bin1 ./testgo install testdata/src/go-cmd-test/helloworld.go; then - echo "go install testdata/src/go-cmd-test/helloworld.go failed" - ok=false -elif ! test -x testdata/bin1/helloworld; then - echo "go install testdata/src/go-cmd-test/helloworld.go did not write testdata/bin1/helloworld" - ok=false -fi - -TEST godoc installs into GOBIN -d=$(mktemp -d -t testgoXXX) -export GOPATH=$d -mkdir $d/gobin -GOBIN=$d/gobin ./testgo get golang.org/x/tools/cmd/godoc || ok=false -if [ ! -x $d/gobin/godoc ]; then - echo did not install godoc to '$GOBIN' - GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' golang.org/x/tools/cmd/godoc || true - ok=false -fi - -TEST godoc installs into GOROOT -GOROOT=$(./testgo env GOROOT) -rm -f $GOROOT/bin/godoc -./testgo install golang.org/x/tools/cmd/godoc || ok=false -if [ ! -x $GOROOT/bin/godoc ]; then - echo did not install godoc to '$GOROOT/bin' - ./testgo list -f 'Target: {{.Target}}' golang.org/x/tools/cmd/godoc || true - ok=false -fi - -TEST cmd/fix installs into tool -GOOS=$(./testgo env GOOS) -GOARCH=$(./testgo env GOARCH) -rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix -./testgo install cmd/fix || ok=false -if [ ! -x $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix ]; then - echo 'did not install cmd/fix to $GOROOT/pkg/tool' - GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix || true - ok=false -fi -rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix -GOBIN=$d/gobin ./testgo install cmd/fix || ok=false -if [ ! -x $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix ]; then - echo 'did not install cmd/fix to $GOROOT/pkg/tool with $GOBIN set' - GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix || true - ok=false -fi - -TEST gopath program installs into GOBIN -mkdir $d/src/progname -echo 'package main; func main() {}' >$d/src/progname/p.go -GOBIN=$d/gobin ./testgo install progname || ok=false -if [ ! -x $d/gobin/progname ]; then - echo 'did not install progname to $GOBIN/progname' - ./testgo list -f 'Target: {{.Target}}' cmd/api || true - ok=false -fi -rm -f $d/gobin/progname $d/bin/progname - -TEST gopath program installs into GOPATH/bin -./testgo install progname || ok=false -if [ ! -x $d/bin/progname ]; then - echo 'did not install progname to $GOPATH/bin/progname' - ./testgo list -f 'Target: {{.Target}}' progname || true - ok=false -fi - -unset GOPATH -rm -rf $d - -# Reject relative paths in GOPATH. -TEST reject relative paths in GOPATH '(command-line package)' -if GOPATH=. ./testgo build testdata/src/go-cmd-test/helloworld.go; then - echo 'GOPATH="." go build should have failed, did not' - ok=false -fi - -TEST reject relative paths in GOPATH -if GOPATH=:$(pwd)/testdata:. ./testgo build go-cmd-test; then - echo 'GOPATH=":$(pwd)/testdata:." go build should have failed, did not' - ok=false -fi - -# issue 4104 -TEST go test with package listed multiple times -if [ $(./testgo test fmt fmt fmt fmt fmt | wc -l) -ne 1 ] ; then - echo 'go test fmt fmt fmt fmt fmt tested the same package multiple times' - ok=false -fi - -TEST go list has a consistent order -./testgo list std > test_std.list || ok=false -if ! ./testgo list std | cmp -s test_std.list - ; then - echo "go list std ordering is inconsistent" - ok=false -fi -rm -f test_std.list - -TEST go list std does not include commands -if ./testgo list std | grep cmd/; then - echo "go list std shows commands" - ok=false -fi - -TEST go list cmd only shows commands -if ./testgo list cmd | grep -v 'cmd/'; then - echo "go list cmd shows non-commands" - ok=false -fi - -# issue 4096. Validate the output of unsuccessful go install foo/quxx -TEST unsuccessful go install should mention missing package -if [ $(./testgo install 'foo/quxx' 2>&1 | grep -c 'cannot find package "foo/quxx" in any of') -ne 1 ] ; then - echo 'go install foo/quxx expected error: .*cannot find package "foo/quxx" in any of' - ok=false -fi -# test GOROOT search failure is reported -TEST GOROOT search failure reporting -if [ $(./testgo install 'foo/quxx' 2>&1 | egrep -c 'foo/quxx \(from \$GOROOT\)$') -ne 1 ] ; then - echo 'go install foo/quxx expected error: .*foo/quxx (from $GOROOT)' - ok=false -fi -# test multiple GOPATH entries are reported separately -TEST multiple GOPATH entries reported separately -if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/./src/foo/quxx') -ne 2 ] ; then - echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)\n.*testdata/b/src/foo/quxx' - ok=false -fi -# test (from $GOPATH) annotation is reported for the first GOPATH entry -TEST mention GOPATH in first GOPATH entry -if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/a/src/foo/quxx \(from \$GOPATH\)$') -ne 1 ] ; then - echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)' - ok=false -fi -# but not on the second -TEST but not the second entry -if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/b/src/foo/quxx$') -ne 1 ] ; then - echo 'go install foo/quxx expected error: .*testdata/b/src/foo/quxx' - ok=false -fi -# test missing GOPATH is reported -TEST missing GOPATH is reported -if [ $(GOPATH= ./testgo install 'foo/quxx' 2>&1 | egrep -c '\(\$GOPATH not set\)$') -ne 1 ] ; then - echo 'go install foo/quxx expected error: ($GOPATH not set)' - ok=false -fi - -# issue 4186. go get cannot be used to download packages to $GOROOT -# Test that without GOPATH set, go get should fail -TEST without GOPATH, go get fails -d=$(mktemp -d -t testgoXXX) -mkdir -p $d/src -if GOPATH= GOROOT=$d ./testgo get -d golang.org/x/codereview/cmd/hgpatch ; then - echo 'go get golang.org/x/codereview/cmd/hgpatch should not succeed with $GOPATH unset' - ok=false -fi -rm -rf $d - -# Test that with GOPATH=$GOROOT, go get should fail -TEST with GOPATH=GOROOT, go get fails -d=$(mktemp -d -t testgoXXX) -mkdir -p $d/src -if GOPATH=$d GOROOT=$d ./testgo get -d golang.org/x/codereview/cmd/hgpatch ; then - echo 'go get golang.org/x/codereview/cmd/hgpatch should not succeed with GOPATH=$GOROOT' - ok=false -fi -rm -rf $d - -TEST ldflags arguments with spaces '(issue 3941)' -d=$(mktemp -d -t testgoXXX) -cat >$d/main.go<hello.out || ok=false -if ! grep -q '^hello world' hello.out; then - echo "ldflags -X main.extern 'hello world' failed. Output:" - cat hello.out - ok=false -fi -rm -rf $d hello.out - -TEST go test -cpuprofile leaves binary behind -./testgo test -cpuprofile strings.prof strings || ok=false -if [ ! -x strings.test ]; then - echo "go test -cpuprofile did not create strings.test" - ok=false -fi -rm -f strings.prof strings.test - -TEST go test -cpuprofile -o controls binary location -./testgo test -cpuprofile strings.prof -o mystrings.test strings || ok=false -if [ ! -x mystrings.test ]; then - echo "go test -cpuprofile -o mystrings.test did not create mystrings.test" - ok=false -fi -rm -f strings.prof mystrings.test - -TEST go test -c -o controls binary location -./testgo test -c -o mystrings.test strings || ok=false -if [ ! -x mystrings.test ]; then - echo "go test -c -o mystrings.test did not create mystrings.test" - ok=false -fi -rm -f mystrings.test - -TEST go test -o writes binary -./testgo test -o mystrings.test strings || ok=false -if [ ! -x mystrings.test ]; then - echo "go test -o mystrings.test did not create mystrings.test" - ok=false -fi -rm -f mystrings.test - -TEST symlinks do not confuse go list '(issue 4568)' -old=$(pwd) -tmp=$(cd /tmp && pwd -P) -d=$(TMPDIR=$tmp mktemp -d -t testgoXXX) -mkdir -p $d/src -( - ln -s $d $d/src/dir1 - cd $d/src - echo package p >dir1/p.go - export GOPATH=$d - if [ "$($old/testgo list -f '{{.Root}}' dir1)" != "$d" ]; then - echo Confused by symlinks. - echo "Package in current directory $(pwd) should have Root $d" - env|grep WD - $old/testgo list -json . dir1 - touch $d/failed - fi -) -if [ -f $d/failed ]; then - ok=false -fi -rm -rf $d - -TEST 'install with tags (issue 4515)' -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -mkdir -p $d/src/example/a $d/src/example/b $d/bin -cat >$d/src/example/a/main.go <$d/src/example/b/main.go <$d/src/example/a/a.go <$d/src/example/a/pkg/pkg.go <$d/src/example/a/Pkg/pkg.go <$d/out; then - echo go list example/a should have failed, did not. - ok=false -elif ! grep "case-insensitive import collision" $d/out >/dev/null; then - echo go list example/a did not report import collision. - ok=false -fi -cat >$d/src/example/b/file.go <$d/src/example/b/FILE.go <$d/out; then - echo go list example/b should have failed, did not. - ok=false -elif ! grep "case-insensitive file name collision" $d/out >/dev/null; then - echo go list example/b did not report file name collision. - ok=false -fi - -TEST go get -t "code.google.com/p/go-get-issue-8181/{a,b}" -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -export GOPATH=$d -if ./testgo get -t code.google.com/p/go-get-issue-8181/{a,b}; then - ./testgo list ... | grep go.tools/godoc > /dev/null || ok=false -else - ok=false -fi -unset GOPATH -rm -rf $d - -TEST shadowing logic -export GOPATH=$(pwd)/testdata/shadow/root1:$(pwd)/testdata/shadow/root2 - -# The math in root1 is not "math" because the standard math is. -set +e -cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/math) -set -e -if [ "$cdir" != "(_$(pwd)/testdata/shadow/root1/src/math) ($GOROOT/src/math)" ]; then - echo shadowed math is not shadowed: "$cdir" - ok=false -fi - -# The foo in root1 is "foo". -set +e -cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/foo) -set -e -if [ "$cdir" != "(foo) ()" ]; then - echo unshadowed foo is shadowed: "$cdir" - ok=false -fi - -# The foo in root2 is not "foo" because the foo in root1 got there first. -set +e -cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root2/src/foo) -set -e -if [ "$cdir" != "(_$(pwd)/testdata/shadow/root2/src/foo) ($(pwd)/testdata/shadow/root1/src/foo)" ]; then - echo shadowed foo is not shadowed: "$cdir" - ok=false -fi - -# The error for go install should mention the conflicting directory. -set +e -err=$(./testgo install ./testdata/shadow/root2/src/foo 2>&1) -set -e -if [ "$err" != "go install: no install location for $(pwd)/testdata/shadow/root2/src/foo: hidden by $(pwd)/testdata/shadow/root1/src/foo" ]; then - echo wrong shadowed install error: "$err" - ok=false -fi - -# Only succeeds if source order is preserved. -TEST source file name order preserved -./testgo test testdata/example[12]_test.go || ok=false - -# Check that coverage analysis works at all. -# Don't worry about the exact numbers but require not 0.0%. -checkcoverage() { - if grep '[^0-9]0\.0%' testdata/cover.txt >/dev/null; then - echo 'some coverage results are 0.0%' - ok=false - fi - cat testdata/cover.txt - rm -f testdata/cover.txt -} - -TEST coverage runs -./testgo test -short -coverpkg=strings strings regexp >testdata/cover.txt 2>&1 || ok=false -./testgo test -short -cover strings math regexp >>testdata/cover.txt 2>&1 || ok=false -checkcoverage - -# Check that coverage analysis uses set mode. -TEST coverage uses set mode -if ./testgo test -short -cover encoding/binary -coverprofile=testdata/cover.out >testdata/cover.txt 2>&1; then - if ! grep -q 'mode: set' testdata/cover.out; then - ok=false - fi - checkcoverage -else - ok=false -fi -rm -f testdata/cover.out testdata/cover.txt - -TEST coverage uses atomic mode for -race. -if ./testgo test -short -race -cover encoding/binary -coverprofile=testdata/cover.out >testdata/cover.txt 2>&1; then - if ! grep -q 'mode: atomic' testdata/cover.out; then - ok=false - fi - checkcoverage -else - ok=false -fi -rm -f testdata/cover.out - -TEST coverage uses actual setting to override even for -race. -if ./testgo test -short -race -cover encoding/binary -covermode=count -coverprofile=testdata/cover.out >testdata/cover.txt 2>&1; then - if ! grep -q 'mode: count' testdata/cover.out; then - ok=false - fi - checkcoverage -else - ok=false -fi -rm -f testdata/cover.out - -TEST coverage with cgo -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -./testgo test -short -cover ./testdata/cgocover >testdata/cover.txt 2>&1 || ok=false -checkcoverage - -TEST cgo depends on syscall -rm -rf $GOROOT/pkg/*_race -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -export GOPATH=$d -mkdir -p $d/src/foo -echo ' -package foo -//#include -import "C" -' >$d/src/foo/foo.go -./testgo build -race foo || ok=false -rm -rf $d -unset GOPATH - -TEST cgo shows full path names -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -export GOPATH=$d -mkdir -p $d/src/x/y/dirname -echo ' -package foo -import "C" -func f() { -' >$d/src/x/y/dirname/foo.go -if ./testgo build x/y/dirname >$d/err 2>&1; then - echo build succeeded unexpectedly. - ok=false -elif ! grep x/y/dirname $d/err >/dev/null; then - echo error did not use full path. - cat $d/err - ok=false -fi -rm -rf $d -unset GOPATH - -TEST 'cgo handles -Wl,$ORIGIN' -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -export GOPATH=$d -mkdir -p $d/src/origin -echo ' -package origin -// #cgo !darwin LDFLAGS: -Wl,-rpath -Wl,$ORIGIN -// void f(void) {} -import "C" - -func f() { C.f() } -' >$d/src/origin/origin.go -if ! ./testgo build origin; then - echo build failed - ok=false -fi -rm -rf $d -unset GOPATH - -TEST 'Issue 6480: "go test -c -test.bench=XXX fmt" should not hang' -if ! ./testgo test -c -test.bench=XXX fmt; then - echo build test failed - ok=false -fi -rm -f fmt.test - -if which gccgo >/dev/null; then - TEST 'Issue 7573: cmd/cgo: undefined reference when linking a C-library using gccgo' - d=$(mktemp -d -t testgoXXX) - export GOPATH=$d - mkdir -p $d/src/cgoref - ldflags="-L alibpath -lalib" - echo " - package main - // #cgo LDFLAGS: $ldflags - // void f(void) {} - import \"C\" - - func main() { C.f() } - " >$d/src/cgoref/cgoref.go - go_cmds="$(./testgo build -n -compiler gccgo cgoref 2>&1 1>/dev/null)" - ldflags_count="$(echo "$go_cmds" | egrep -c "^gccgo.*$(echo $ldflags | sed -e 's/-/\\-/g')" || true)" - if [ "$ldflags_count" -lt 1 ]; then - echo "No Go-inline "#cgo LDFLAGS:" (\"$ldflags\") passed to gccgo linking stage." - ok=false - fi - rm -rf $d - unset ldflags_count - unset go_cmds - unset ldflags - unset GOPATH -fi - -TEST list template can use context function -if ! ./testgo list -f "GOARCH: {{context.GOARCH}}"; then - echo unable to use context in list template - ok=false -fi - -TEST 'Issue 7108: cmd/go: "go test" should fail if package does not build' -export GOPATH=$(pwd)/testdata -if ./testgo test notest >/dev/null 2>&1; then - echo 'go test notest succeeded, but should fail' - ok=false -fi -unset GOPATH - -TEST 'Issue 6844: cmd/go: go test -a foo does not rebuild regexp' -if ! ./testgo test -x -a -c testdata/dep_test.go 2>deplist; then - echo "go test -x -a -c testdata/dep_test.go failed" - ok=false -elif ! grep -q regexp deplist; then - echo "go test -x -a -c testdata/dep_test.go did not rebuild regexp" - ok=false -fi -rm -f deplist -rm -f deps.test - -TEST list template can use context function -if ! ./testgo list -f "GOARCH: {{context.GOARCH}}"; then - echo unable to use context in list template - ok=false -fi - -TEST build -i installs dependencies -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -export GOPATH=$d -mkdir -p $d/src/x/y/foo $d/src/x/y/bar -echo ' -package foo -func F() {} -' >$d/src/x/y/foo/foo.go -checkbar() { - desc="$1" - sleep 1 - touch $d/src/x/y/foo/foo.go - if ! ./testgo build -v -i x/y/bar &> $d/err; then - echo build -i "$1" failed - cat $d/err - ok=false - elif ! grep x/y/foo $d/err >/dev/null; then - echo first build -i "$1" did not build x/y/foo - cat $d/err - ok=false - fi - if ! ./testgo build -v -i x/y/bar &> $d/err; then - echo second build -i "$1" failed - cat $d/err - ok=false - elif grep x/y/foo $d/err >/dev/null; then - echo second build -i "$1" built x/y/foo - cat $d/err - ok=false - fi -} - -echo ' -package bar -import "x/y/foo" -func F() { foo.F() } -' >$d/src/x/y/bar/bar.go -checkbar pkg - -TEST build -i installs dependencies for command -echo ' -package main -import "x/y/foo" -func main() { foo.F() } -' >$d/src/x/y/bar/bar.go -checkbar cmd - -rm -rf $d bar -unset GOPATH - -TEST 'go build in test-only directory fails with a good error' -if ./testgo build ./testdata/testonly 2>testdata/err.out; then - echo "go build ./testdata/testonly succeeded, should have failed" - ok=false -elif ! grep 'no buildable Go' testdata/err.out >/dev/null; then - echo "go build ./testdata/testonly produced unexpected error:" - cat testdata/err.out - ok=false -fi -rm -f testdata/err.out - -TEST 'go test detects test-only import cycles' -export GOPATH=$(pwd)/testdata -if ./testgo test -c testcycle/p3 2>testdata/err.out; then - echo "go test testcycle/p3 succeeded, should have failed" - ok=false -elif ! grep 'import cycle not allowed in test' testdata/err.out >/dev/null; then - echo "go test testcycle/p3 produced unexpected error:" - cat testdata/err.out - ok=false -fi -rm -f testdata/err.out -unset GOPATH - -TEST 'go test foo_test.go works' -if ! ./testgo test testdata/standalone_test.go; then - echo "go test testdata/standalone_test.go failed" - ok=false -fi - -TEST 'go test xtestonly works' -export GOPATH=$(pwd)/testdata -./testgo clean -i xtestonly || ok=false -if ! ./testgo test xtestonly >/dev/null; then - echo "go test xtestonly failed" - ok=false -fi -unset GOPATH - -TEST 'go test builds an xtest containing only non-runnable examples' -if ! ./testgo test -v ./testdata/norunexample > testdata/std.out; then - echo "go test ./testdata/norunexample failed" - ok=false -elif ! grep 'File with non-runnable example was built.' testdata/std.out > /dev/null; then - echo "file with non-runnable example was not built" - ok=false -fi -rm -f testdata/std.out - -TEST 'go generate handles simple command' -if ! ./testgo generate ./testdata/generate/test1.go > testdata/std.out; then - echo "go generate ./testdata/generate/test1.go failed to run" - ok=false -elif ! grep 'Success' testdata/std.out > /dev/null; then - echo "go generate ./testdata/generate/test1.go generated wrong output" - ok=false -fi - -TEST 'go generate handles command alias' -if ! ./testgo generate ./testdata/generate/test2.go > testdata/std.out; then - echo "go generate ./testdata/generate/test2.go failed to run" - ok=false -elif ! grep 'Now is the time for all good men' testdata/std.out > /dev/null; then - echo "go generate ./testdata/generate/test2.go generated wrong output" - ok=false -fi - -TEST 'go generate variable substitution' -if ! ./testgo generate ./testdata/generate/test3.go > testdata/std.out; then - echo "go generate ./testdata/generate/test3.go failed to run" - ok=false -elif ! grep "$GOARCH test3.go:7 pabc xyzp/test3.go/123" testdata/std.out > /dev/null; then - echo "go generate ./testdata/generate/test3.go generated wrong output" - ok=false -fi - -TEST 'go generate run flag' -if ! ./testgo generate -run y.s ./testdata/generate/test4.go > testdata/std.out; then - echo "go test -run yes ./testdata/generate/test4.go failed to run" - ok=false -elif ! grep "yes" testdata/std.out > /dev/null; then - echo "go generate -run yes ./testdata/generate/test4.go did not select yes" - ok=false -elif grep "no" testdata/std.out > /dev/null; then - echo "go generate -run yes ./testdata/generate/test4.go selected no" - ok=false -fi - -TEST go get works with vanity wildcards -d=$(mktemp -d -t testgoXXX) -export GOPATH=$d -if ! ./testgo get -u rsc.io/pdf/...; then - ok=false -elif [ ! -x $d/bin/pdfpasswd ]; then - echo did not build rsc.io/pdf/pdfpasswd - ok=false -fi -unset GOPATH -rm -rf $d - -TEST go vet with external tests -d=$(mktemp -d -t testgoXXX) -export GOPATH=$d -./testgo get golang.org/x/tools/cmd/vet -export GOPATH=$(pwd)/testdata -if ./testgo vet vetpkg >$d/err 2>&1; then - echo "go vet vetpkg passes incorrectly" - ok=false -elif ! grep -q 'missing argument for Printf' $d/err; then - echo "go vet vetpkg did not find missing argument for Printf" - cat $d/err - ok=false -fi -unset GOPATH -rm -rf $d - -TEST go vet with -tags -d=$(mktemp -d -t testgoXXX) -export GOPATH=$d -./testgo get golang.org/x/tools/cmd/vet -export GOPATH=$(pwd)/testdata -if ./testgo vet -tags tagtest vetpkg >$d/err 2>&1; then - echo "go vet vetpkg passes incorrectly" - ok=false -elif ! grep -q 'c\.go.*wrong number of args for format' $d/err; then - echo "go vet vetpkg did not scan tagged file" - cat $d/err - ok=false -fi -unset GOPATH -rm -rf $d - -TEST go get ./rsc.io/toolstash '(golang.org/issue/9767)' -d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) -export GOPATH=$d -export testgo=$(pwd)/testgo -mkdir -p $GOPATH/src/rsc.io -(cd $GOPATH/src/rsc.io && $testgo get ./toolstash) || ok=false -unset GOPATH -unset testgo -rm -rf $d - -# clean up -if $started; then stop; fi -rm -rf testdata/bin testdata/bin1 testdata/std.out -rm -f testgo - -if $allok; then - echo PASS -else - echo FAIL: - echo "$testfail" - exit 1 -fi