2013-07-17 01:32:45 -06:00
|
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package main_test
|
|
|
|
|
|
|
|
import (
|
2014-10-27 19:25:46 -06:00
|
|
|
"bufio"
|
2014-03-16 14:17:13 -06:00
|
|
|
"bytes"
|
|
|
|
"fmt"
|
2014-10-27 19:25:46 -06:00
|
|
|
"io"
|
2013-07-17 01:32:45 -06:00
|
|
|
"io/ioutil"
|
2014-03-16 14:17:13 -06:00
|
|
|
"net"
|
|
|
|
"net/http"
|
2013-07-17 01:32:45 -06:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"regexp"
|
2013-07-18 18:31:12 -06:00
|
|
|
"runtime"
|
2014-02-26 11:21:44 -07:00
|
|
|
"strings"
|
2013-07-17 01:32:45 -06:00
|
|
|
"testing"
|
2014-03-16 14:17:13 -06:00
|
|
|
"time"
|
2013-07-17 01:32:45 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
var godocTests = []struct {
|
2014-03-14 08:00:10 -06:00
|
|
|
args []string
|
|
|
|
matches []string // regular expressions
|
|
|
|
dontmatch []string // regular expressions
|
2013-07-17 01:32:45 -06:00
|
|
|
}{
|
|
|
|
{
|
2014-03-14 08:00:10 -06:00
|
|
|
args: []string{"fmt"},
|
|
|
|
matches: []string{
|
2013-07-17 01:32:45 -06:00
|
|
|
`import "fmt"`,
|
|
|
|
`Package fmt implements formatted I/O`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2014-03-14 08:00:10 -06:00
|
|
|
args: []string{"io", "WriteString"},
|
|
|
|
matches: []string{
|
2013-07-17 01:32:45 -06:00
|
|
|
`func WriteString\(`,
|
2014-01-30 13:32:23 -07:00
|
|
|
`WriteString writes the contents of the string s to w`,
|
2013-07-17 01:32:45 -06:00
|
|
|
},
|
|
|
|
},
|
2014-02-26 11:21:44 -07:00
|
|
|
{
|
2014-03-14 08:00:10 -06:00
|
|
|
args: []string{"nonexistingpkg"},
|
|
|
|
matches: []string{
|
cmd/godoc, godoc: implement build.Context.IsDir, update expected error string
Prior to this change, handlerServer.GetPageInfo created a build.Context
starting with build.Default, and provided custom implementations for
its IsAbsPath, ReadDir, and OpenFile funcs. Those funcs would operate
on h.c.fs virtual filesystem.
https://godoc.org/go/build#Context.IsDir is documented as:
// IsDir reports whether the path names a directory.
// If IsDir is nil, Import calls os.Stat and uses the result's IsDir method.
IsDir func(path string) bool
IsDir was left as nil, and so the default implementation was used.
The default implementation uses os.Stat and isn't aware of the h.c.fs
virtual filesystem.
This appears to have been harmless in the past, but after the change to
go/build in https://golang.org/cl/33158, it started to interfere with
the operation of godoc. The command godoc began to fail to resolve
relative import path "." in directories that exist, because the
incorrect IsDir implementation was looking in real filesystem, rather
than the virtual one:
$ ./godoc fmt
2017/03/04 18:59:50 cannot find package "." in:
/target
$ ./godoc -http=localhost:6060
2017/03/04 19:00:07 cannot find package "." in:
/src/fmt
Providing a custom implementation of IsDir that is aware of the h.c.fs
virtual filesystem, and performs a stat operation on the correct path
there resolves that problem. It also fixes the failing tests.
Additionally, because the exact error string returned from
Context.Import has changed after https://golang.org/cl/33158,
and now contains the text "package not found" rather than the
underlying error text from filesystem caused by a failed ReadDir
operation, the expected error message from "./godoc nonexistingpkg"
in a test needed to be updated to "cannot find package". It's no longer
dependent on the operating system.
It might be desirable to provide more relevant detail in the error
message from cmd/godoc when a package is not found, but that should be
determined and done in a followup CL. The scope of this one is to fix
normal functionality.
This change follows update to go/build in https://golang.org/cl/33158.
Helps golang/go#19401.
Change-Id: I00e2f746ec4a2fe7e640218adce75f15bdf29aaf
Reviewed-on: https://go-review.googlesource.com/37768
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2017-03-04 17:06:07 -07:00
|
|
|
`cannot find package`,
|
2014-02-26 11:21:44 -07:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2014-03-14 08:00:10 -06:00
|
|
|
args: []string{"fmt", "NonexistentSymbol"},
|
|
|
|
matches: []string{
|
|
|
|
`No match found\.`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
args: []string{"-src", "syscall", "Open"},
|
|
|
|
matches: []string{
|
|
|
|
`func Open\(`,
|
|
|
|
},
|
|
|
|
dontmatch: []string{
|
2014-02-26 11:21:44 -07:00
|
|
|
`No match found\.`,
|
|
|
|
},
|
|
|
|
},
|
2013-07-17 01:32:45 -06:00
|
|
|
}
|
|
|
|
|
2014-03-16 14:17:13 -06:00
|
|
|
// buildGodoc builds the godoc executable.
|
|
|
|
// It returns its path, and a cleanup function.
|
|
|
|
//
|
|
|
|
// TODO(adonovan): opt: do this at most once, and do the cleanup
|
|
|
|
// exactly once. How though? There's no atexit.
|
|
|
|
func buildGodoc(t *testing.T) (bin string, cleanup func()) {
|
2015-03-12 23:21:50 -06:00
|
|
|
if runtime.GOARCH == "arm" {
|
|
|
|
t.Skip("skipping test on arm platforms; too slow")
|
|
|
|
}
|
2013-07-17 01:32:45 -06:00
|
|
|
tmp, err := ioutil.TempDir("", "godoc-regtest-")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-01-15 11:16:57 -07:00
|
|
|
defer func() {
|
|
|
|
if cleanup == nil { // probably, go build failed.
|
|
|
|
os.RemoveAll(tmp)
|
|
|
|
}
|
|
|
|
}()
|
2013-07-17 01:32:45 -06:00
|
|
|
|
2014-03-16 14:17:13 -06:00
|
|
|
bin = filepath.Join(tmp, "godoc")
|
2013-07-18 18:31:12 -06:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
bin += ".exe"
|
|
|
|
}
|
2013-07-17 01:32:45 -06:00
|
|
|
cmd := exec.Command("go", "build", "-o", bin)
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
|
|
t.Fatalf("Building godoc: %v", err)
|
|
|
|
}
|
|
|
|
|
2014-03-16 14:17:13 -06:00
|
|
|
return bin, func() { os.RemoveAll(tmp) }
|
|
|
|
}
|
|
|
|
|
|
|
|
// Basic regression test for godoc command-line tool.
|
|
|
|
func TestCLI(t *testing.T) {
|
|
|
|
bin, cleanup := buildGodoc(t)
|
|
|
|
defer cleanup()
|
2013-07-17 01:32:45 -06:00
|
|
|
for _, test := range godocTests {
|
|
|
|
cmd := exec.Command(bin, test.args...)
|
|
|
|
cmd.Args[0] = "godoc"
|
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Running with args %#v: %v", test.args, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, pat := range test.matches {
|
|
|
|
re := regexp.MustCompile(pat)
|
|
|
|
if !re.Match(out) {
|
2014-02-26 11:21:44 -07:00
|
|
|
t.Errorf("godoc %v =\n%s\nwanted /%v/", strings.Join(test.args, " "), out, pat)
|
2013-07-17 01:32:45 -06:00
|
|
|
}
|
|
|
|
}
|
2014-03-14 08:00:10 -06:00
|
|
|
for _, pat := range test.dontmatch {
|
|
|
|
re := regexp.MustCompile(pat)
|
|
|
|
if re.Match(out) {
|
|
|
|
t.Errorf("godoc %v =\n%s\ndid not want /%v/", strings.Join(test.args, " "), out, pat)
|
|
|
|
}
|
|
|
|
}
|
2013-07-17 01:32:45 -06:00
|
|
|
}
|
|
|
|
}
|
2014-03-16 14:17:13 -06:00
|
|
|
|
|
|
|
func serverAddress(t *testing.T) string {
|
|
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
|
|
if err != nil {
|
|
|
|
ln, err = net.Listen("tcp6", "[::1]:0")
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer ln.Close()
|
|
|
|
return ln.Addr().String()
|
|
|
|
}
|
|
|
|
|
2015-07-30 23:02:02 -06:00
|
|
|
func waitForServerReady(t *testing.T, addr string) {
|
|
|
|
waitForServer(t,
|
|
|
|
fmt.Sprintf("http://%v/", addr),
|
|
|
|
"The Go Programming Language",
|
2016-10-12 09:50:13 -06:00
|
|
|
15*time.Second)
|
2015-07-30 23:02:02 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func waitForSearchReady(t *testing.T, addr string) {
|
|
|
|
waitForServer(t,
|
|
|
|
fmt.Sprintf("http://%v/search?q=FALLTHROUGH", addr),
|
|
|
|
"The list of tokens.",
|
|
|
|
2*time.Minute)
|
|
|
|
}
|
2015-03-10 05:29:27 -06:00
|
|
|
|
2015-07-30 23:02:02 -06:00
|
|
|
const pollInterval = 200 * time.Millisecond
|
2015-03-10 05:29:27 -06:00
|
|
|
|
2015-07-30 23:02:02 -06:00
|
|
|
func waitForServer(t *testing.T, url, match string, timeout time.Duration) {
|
2015-03-10 05:29:27 -06:00
|
|
|
// "health check" duplicated from x/tools/cmd/tipgodoc/tip.go
|
2015-07-30 23:02:02 -06:00
|
|
|
deadline := time.Now().Add(timeout)
|
2015-03-10 05:29:27 -06:00
|
|
|
for time.Now().Before(deadline) {
|
|
|
|
time.Sleep(pollInterval)
|
2015-07-30 23:02:02 -06:00
|
|
|
res, err := http.Get(url)
|
2014-03-16 14:17:13 -06:00
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2015-03-10 05:29:27 -06:00
|
|
|
rbody, err := ioutil.ReadAll(res.Body)
|
|
|
|
res.Body.Close()
|
|
|
|
if err == nil && res.StatusCode == http.StatusOK &&
|
2015-07-30 23:02:02 -06:00
|
|
|
bytes.Contains(rbody, []byte(match)) {
|
2015-03-10 05:29:27 -06:00
|
|
|
return
|
|
|
|
}
|
2014-03-16 14:17:13 -06:00
|
|
|
}
|
2015-07-30 23:02:02 -06:00
|
|
|
t.Fatalf("Server failed to respond in %v", timeout)
|
2014-03-16 14:17:13 -06:00
|
|
|
}
|
|
|
|
|
2014-10-27 23:59:58 -06:00
|
|
|
func killAndWait(cmd *exec.Cmd) {
|
|
|
|
cmd.Process.Kill()
|
|
|
|
cmd.Wait()
|
|
|
|
}
|
|
|
|
|
2014-03-16 14:17:13 -06:00
|
|
|
// Basic integration test for godoc HTTP interface.
|
|
|
|
func TestWeb(t *testing.T) {
|
2015-07-30 23:02:02 -06:00
|
|
|
testWeb(t, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Basic integration test for godoc HTTP interface.
|
|
|
|
func TestWebIndex(t *testing.T) {
|
|
|
|
if testing.Short() {
|
|
|
|
t.Skip("skipping test in -short mode")
|
|
|
|
}
|
|
|
|
testWeb(t, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Basic integration test for godoc HTTP interface.
|
|
|
|
func testWeb(t *testing.T, withIndex bool) {
|
2014-03-16 14:17:13 -06:00
|
|
|
bin, cleanup := buildGodoc(t)
|
|
|
|
defer cleanup()
|
|
|
|
addr := serverAddress(t)
|
2015-07-30 23:02:02 -06:00
|
|
|
args := []string{fmt.Sprintf("-http=%s", addr)}
|
|
|
|
if withIndex {
|
|
|
|
args = append(args, "-index", "-index_interval=-1s")
|
|
|
|
}
|
|
|
|
cmd := exec.Command(bin, args...)
|
2014-03-16 14:17:13 -06:00
|
|
|
cmd.Stdout = os.Stderr
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
cmd.Args[0] = "godoc"
|
2015-07-30 23:02:02 -06:00
|
|
|
cmd.Env = godocEnv()
|
2014-03-16 14:17:13 -06:00
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
t.Fatalf("failed to start godoc: %s", err)
|
|
|
|
}
|
2014-10-27 23:59:58 -06:00
|
|
|
defer killAndWait(cmd)
|
2015-07-30 23:02:02 -06:00
|
|
|
|
|
|
|
if withIndex {
|
|
|
|
waitForSearchReady(t, addr)
|
|
|
|
} else {
|
|
|
|
waitForServerReady(t, addr)
|
|
|
|
}
|
|
|
|
|
2014-10-13 10:47:02 -06:00
|
|
|
tests := []struct {
|
|
|
|
path string
|
|
|
|
match []string
|
|
|
|
dontmatch []string
|
2015-07-30 23:02:02 -06:00
|
|
|
needIndex bool
|
2014-10-13 10:47:02 -06:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
path: "/",
|
|
|
|
match: []string{"Go is an open source programming language"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
path: "/pkg/fmt/",
|
|
|
|
match: []string{"Package fmt implements formatted I/O"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
path: "/src/fmt/",
|
|
|
|
match: []string{"scan_test.go"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
path: "/src/fmt/print.go",
|
|
|
|
match: []string{"// Println formats using"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
path: "/pkg",
|
|
|
|
match: []string{
|
|
|
|
"Standard library",
|
|
|
|
"Package fmt implements formatted I/O",
|
|
|
|
},
|
|
|
|
dontmatch: []string{
|
|
|
|
"internal/syscall",
|
|
|
|
"cmd/gc",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
path: "/pkg/?m=all",
|
|
|
|
match: []string{
|
|
|
|
"Standard library",
|
|
|
|
"Package fmt implements formatted I/O",
|
2017-01-18 11:52:36 -07:00
|
|
|
"internal/syscall/?m=all",
|
2014-10-13 10:47:02 -06:00
|
|
|
},
|
|
|
|
dontmatch: []string{
|
|
|
|
"cmd/gc",
|
|
|
|
},
|
|
|
|
},
|
2015-03-10 05:29:27 -06:00
|
|
|
{
|
2016-11-03 12:58:50 -06:00
|
|
|
path: "/search?q=ListenAndServe",
|
2015-03-10 05:29:27 -06:00
|
|
|
match: []string{
|
|
|
|
"/src",
|
|
|
|
},
|
|
|
|
dontmatch: []string{
|
|
|
|
"/pkg/bootstrap",
|
|
|
|
},
|
2015-07-30 23:02:02 -06:00
|
|
|
needIndex: true,
|
2015-03-10 05:29:27 -06:00
|
|
|
},
|
2015-08-27 19:00:04 -06:00
|
|
|
{
|
|
|
|
path: "/pkg/strings/",
|
|
|
|
match: []string{
|
|
|
|
`href="/src/strings/strings.go"`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
path: "/cmd/compile/internal/amd64/",
|
|
|
|
match: []string{
|
2016-10-12 08:52:42 -06:00
|
|
|
`href="/src/cmd/compile/internal/amd64/prog.go"`,
|
2015-08-27 19:00:04 -06:00
|
|
|
},
|
|
|
|
},
|
2014-03-16 14:17:13 -06:00
|
|
|
}
|
|
|
|
for _, test := range tests {
|
2015-07-30 23:02:02 -06:00
|
|
|
if test.needIndex && !withIndex {
|
|
|
|
continue
|
|
|
|
}
|
2014-03-16 14:17:13 -06:00
|
|
|
url := fmt.Sprintf("http://%s%s", addr, test.path)
|
|
|
|
resp, err := http.Get(url)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("GET %s failed: %s", url, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
|
|
resp.Body.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("GET %s: failed to read body: %s (response: %v)", url, err, resp)
|
|
|
|
}
|
2014-10-13 10:47:02 -06:00
|
|
|
isErr := false
|
|
|
|
for _, substr := range test.match {
|
|
|
|
if !bytes.Contains(body, []byte(substr)) {
|
|
|
|
t.Errorf("GET %s: wanted substring %q in body", url, substr)
|
|
|
|
isErr = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, substr := range test.dontmatch {
|
|
|
|
if bytes.Contains(body, []byte(substr)) {
|
|
|
|
t.Errorf("GET %s: didn't want substring %q in body", url, substr)
|
|
|
|
isErr = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if isErr {
|
|
|
|
t.Errorf("GET %s: got:\n%s", url, body)
|
2014-03-16 14:17:13 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-04-17 13:51:27 -06:00
|
|
|
|
|
|
|
// Basic integration test for godoc -analysis=type (via HTTP interface).
|
|
|
|
func TestTypeAnalysis(t *testing.T) {
|
2015-07-31 16:26:00 -06:00
|
|
|
if runtime.GOOS == "plan9" {
|
|
|
|
t.Skip("skipping test on plan9 (issue #11974)") // see comment re: Plan 9 below
|
|
|
|
}
|
|
|
|
|
2014-04-17 13:51:27 -06:00
|
|
|
// Write a fake GOROOT/GOPATH.
|
|
|
|
tmpdir, err := ioutil.TempDir("", "godoc-analysis")
|
|
|
|
if err != nil {
|
2014-05-19 09:47:28 -06:00
|
|
|
t.Fatalf("ioutil.TempDir failed: %s", err)
|
2014-04-17 13:51:27 -06:00
|
|
|
}
|
|
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
for _, f := range []struct{ file, content string }{
|
2014-09-10 07:02:54 -06:00
|
|
|
{"goroot/src/lib/lib.go", `
|
2014-04-17 13:51:27 -06:00
|
|
|
package lib
|
|
|
|
type T struct{}
|
|
|
|
const C = 3
|
|
|
|
var V T
|
|
|
|
func (T) F() int { return C }
|
|
|
|
`},
|
|
|
|
{"gopath/src/app/main.go", `
|
|
|
|
package main
|
|
|
|
import "lib"
|
|
|
|
func main() { print(lib.V) }
|
|
|
|
`},
|
|
|
|
} {
|
|
|
|
file := filepath.Join(tmpdir, f.file)
|
|
|
|
if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
|
|
|
|
t.Fatalf("MkdirAll(%s) failed: %s", filepath.Dir(file), err)
|
|
|
|
}
|
|
|
|
if err := ioutil.WriteFile(file, []byte(f.content), 0644); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start the server.
|
|
|
|
bin, cleanup := buildGodoc(t)
|
|
|
|
defer cleanup()
|
|
|
|
addr := serverAddress(t)
|
|
|
|
cmd := exec.Command(bin, fmt.Sprintf("-http=%s", addr), "-analysis=type")
|
2014-05-13 20:39:20 -06:00
|
|
|
cmd.Env = append(cmd.Env, fmt.Sprintf("GOROOT=%s", filepath.Join(tmpdir, "goroot")))
|
|
|
|
cmd.Env = append(cmd.Env, fmt.Sprintf("GOPATH=%s", filepath.Join(tmpdir, "gopath")))
|
|
|
|
for _, e := range os.Environ() {
|
|
|
|
if strings.HasPrefix(e, "GOROOT=") || strings.HasPrefix(e, "GOPATH=") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
cmd.Env = append(cmd.Env, e)
|
|
|
|
}
|
2014-04-17 13:51:27 -06:00
|
|
|
cmd.Stdout = os.Stderr
|
2014-10-27 19:25:46 -06:00
|
|
|
stderr, err := cmd.StderrPipe()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2014-04-17 13:51:27 -06:00
|
|
|
cmd.Args[0] = "godoc"
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
t.Fatalf("failed to start godoc: %s", err)
|
|
|
|
}
|
2014-10-27 23:59:58 -06:00
|
|
|
defer killAndWait(cmd)
|
2015-07-30 23:02:02 -06:00
|
|
|
waitForServerReady(t, addr)
|
2014-04-17 13:51:27 -06:00
|
|
|
|
2014-10-27 19:25:46 -06:00
|
|
|
// Wait for type analysis to complete.
|
|
|
|
reader := bufio.NewReader(stderr)
|
|
|
|
for {
|
2015-07-31 16:26:00 -06:00
|
|
|
s, err := reader.ReadString('\n') // on Plan 9 this fails
|
2014-10-27 19:25:46 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
fmt.Fprint(os.Stderr, s)
|
|
|
|
if strings.Contains(s, "Type analysis complete.") {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
go io.Copy(os.Stderr, reader)
|
|
|
|
|
2014-04-21 15:56:06 -06:00
|
|
|
t0 := time.Now()
|
|
|
|
|
2014-04-17 13:51:27 -06:00
|
|
|
// Make an HTTP request and check for a regular expression match.
|
|
|
|
// The patterns are very crude checks that basic type information
|
|
|
|
// has been annotated onto the source view.
|
2014-04-21 15:56:06 -06:00
|
|
|
tryagain:
|
2014-04-17 13:51:27 -06:00
|
|
|
for _, test := range []struct{ url, pattern string }{
|
2014-09-10 07:02:54 -06:00
|
|
|
{"/src/lib/lib.go", "L2.*package .*Package docs for lib.*/lib"},
|
|
|
|
{"/src/lib/lib.go", "L3.*type .*type info for T.*struct"},
|
|
|
|
{"/src/lib/lib.go", "L5.*var V .*type T struct"},
|
|
|
|
{"/src/lib/lib.go", "L6.*func .*type T struct.*T.*return .*const C untyped int.*C"},
|
2014-04-17 13:51:27 -06:00
|
|
|
|
2014-09-10 07:02:54 -06:00
|
|
|
{"/src/app/main.go", "L2.*package .*Package docs for app"},
|
|
|
|
{"/src/app/main.go", "L3.*import .*Package docs for lib.*lib"},
|
|
|
|
{"/src/app/main.go", "L4.*func main.*package lib.*lib.*var lib.V lib.T.*V"},
|
2014-04-17 13:51:27 -06:00
|
|
|
} {
|
|
|
|
url := fmt.Sprintf("http://%s%s", addr, test.url)
|
|
|
|
resp, err := http.Get(url)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("GET %s failed: %s", url, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
|
|
resp.Body.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("GET %s: failed to read body: %s (response: %v)", url, err, resp)
|
|
|
|
continue
|
|
|
|
}
|
2014-04-21 15:56:06 -06:00
|
|
|
|
|
|
|
if !bytes.Contains(body, []byte("Static analysis features")) {
|
|
|
|
// Type analysis results usually become available within
|
|
|
|
// ~4ms after godoc startup (for this input on my machine).
|
|
|
|
if elapsed := time.Since(t0); elapsed > 500*time.Millisecond {
|
|
|
|
t.Fatalf("type analysis results still unavailable after %s", elapsed)
|
|
|
|
}
|
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
goto tryagain
|
|
|
|
}
|
|
|
|
|
2014-04-17 13:51:27 -06:00
|
|
|
match, err := regexp.Match(test.pattern, body)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("regexp.Match(%q) failed: %s", test.pattern, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !match {
|
|
|
|
// This is a really ugly failure message.
|
|
|
|
t.Errorf("GET %s: body doesn't match %q, got:\n%s",
|
|
|
|
url, test.pattern, string(body))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-07-30 23:02:02 -06:00
|
|
|
|
|
|
|
// godocEnv returns the process environment without the GOPATH variable.
|
|
|
|
// (We don't want the indexer looking at the local workspace during tests.)
|
|
|
|
func godocEnv() (env []string) {
|
|
|
|
for _, v := range os.Environ() {
|
|
|
|
if strings.HasPrefix(v, "GOPATH=") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
env = append(env, v)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|