cmd/godoc: fix careless crash introduced by recent analysis CL

+ basic integration test

LGTM=bgarcia
R=bgarcia, bradfitz
CC=golang-codereviews
https://golang.org/cl/76410045
This commit is contained in:
Alan Donovan 2014-03-16 16:17:13 -04:00
Родитель 80c4f06c0f
Коммит 7877131709
2 изменённых файлов: 90 добавлений и 7 удалений

Просмотреть файл

@ -5,7 +5,11 @@
package main_test
import (
"bytes"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
@ -13,6 +17,7 @@ import (
"runtime"
"strings"
"testing"
"time"
)
var godocTests = []struct {
@ -57,15 +62,18 @@ var godocTests = []struct {
},
}
// Basic regression test for godoc command-line tool.
func TestGodoc(t *testing.T) {
// 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()) {
tmp, err := ioutil.TempDir("", "godoc-regtest-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmp)
bin := filepath.Join(tmp, "godoc")
bin = filepath.Join(tmp, "godoc")
if runtime.GOOS == "windows" {
bin += ".exe"
}
@ -74,6 +82,13 @@ func TestGodoc(t *testing.T) {
t.Fatalf("Building godoc: %v", err)
}
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()
for _, test := range godocTests {
cmd := exec.Command(bin, test.args...)
cmd.Args[0] = "godoc"
@ -96,3 +111,68 @@ func TestGodoc(t *testing.T) {
}
}
}
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()
}
func waitForServer(t *testing.T, address string) {
// Poll every 50ms for a total of 5s.
for i := 0; i < 100; i++ {
time.Sleep(50 * time.Millisecond)
conn, err := net.Dial("tcp", address)
if err != nil {
continue
}
conn.Close()
return
}
t.Fatalf("Server %q failed to respond in 5 seconds", address)
}
// Basic integration test for godoc HTTP interface.
func TestWeb(t *testing.T) {
bin, cleanup := buildGodoc(t)
defer cleanup()
addr := serverAddress(t)
cmd := exec.Command(bin, fmt.Sprintf("-http=%s", addr))
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr
cmd.Args[0] = "godoc"
if err := cmd.Start(); err != nil {
t.Fatalf("failed to start godoc: %s", err)
}
defer cmd.Process.Kill()
waitForServer(t, addr)
tests := []struct{ path, substr string }{
{"/", "Go is an open source programming language"},
{"/pkg/fmt/", "Package fmt implements formatted I/O"},
{"/src/pkg/fmt/", "scan_test.go"},
{"/src/pkg/fmt/print.go", "// Println formats using"},
}
for _, test := range tests {
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)
}
if bytes.Index(body, []byte(test.substr)) < 0 {
t.Errorf("GET %s: want substring %q in body, got:\n%s",
url, test.substr, string(body))
}
}
}

Просмотреть файл

@ -218,6 +218,9 @@ func (res *Result) fileInfo(url string) *fileInfo {
res.mu.Lock()
fi, ok := res.fileInfos[url]
if !ok {
if res.fileInfos == nil {
res.fileInfos = make(map[string]*fileInfo)
}
fi = new(fileInfo)
res.fileInfos[url] = fi
}
@ -240,6 +243,9 @@ func (res *Result) pkgInfo(importPath string) *pkgInfo {
res.mu.Lock()
pi, ok := res.pkgInfos[importPath]
if !ok {
if res.pkgInfos == nil {
res.pkgInfos = make(map[string]*pkgInfo)
}
pi = new(pkgInfo)
res.pkgInfos[importPath] = pi
}
@ -297,9 +303,6 @@ func (a *analysis) posURL(pos token.Pos, len int) string {
// enabled by the pta flag.
//
func Run(pta bool, result *Result) {
result.fileInfos = make(map[string]*fileInfo)
result.pkgInfos = make(map[string]*pkgInfo)
conf := loader.Config{
SourceImports: true,
AllowTypeErrors: true,