// 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 godoc import ( "bytes" "go/build" "io/ioutil" "os" "path/filepath" "reflect" "regexp" "runtime" "testing" "text/template" "golang.org/x/tools/godoc/vfs" "golang.org/x/tools/godoc/vfs/mapfs" ) // setupGoroot creates temporary directory to act as GOROOT when running tests // that depend upon the build package. It updates build.Default to point to the // new GOROOT. // It returns a function that can be called to reset build.Default and remove // the temporary directory. func setupGoroot(t *testing.T) (cleanup func()) { var stdLib = map[string]string{ "src/fmt/fmt.go": `// Package fmt implements formatted I/O. package fmt type Stringer interface { String() string } `, } goroot, err := ioutil.TempDir("", "cmdline_test") if err != nil { t.Fatal(err) } origContext := build.Default build.Default = build.Context{ GOROOT: goroot, Compiler: "gc", } for relname, contents := range stdLib { name := filepath.Join(goroot, relname) if err := os.MkdirAll(filepath.Dir(name), 0770); err != nil { t.Fatal(err) } if err := ioutil.WriteFile(name, []byte(contents), 0770); err != nil { t.Fatal(err) } } return func() { if err := os.RemoveAll(goroot); err != nil { t.Log(err) } build.Default = origContext } } func TestPaths(t *testing.T) { cleanup := setupGoroot(t) defer cleanup() pres := &Presentation{ pkgHandler: handlerServer{ fsRoot: "/fsroot", }, } fs := make(vfs.NameSpace) absPath := "/foo/fmt" if runtime.GOOS == "windows" { absPath = `c:\foo\fmt` } for _, tc := range []struct { desc string path string expAbs string expRel string }{ { "Absolute path", absPath, "/target", "/target", }, { "Local import", "../foo/fmt", "/target", "/target", }, { "Import", "fmt", "/target", "fmt", }, { "Default", "unknownpkg", "/fsroot/unknownpkg", "unknownpkg", }, } { abs, rel := paths(fs, pres, tc.path) if abs != tc.expAbs || rel != tc.expRel { t.Errorf("%s: paths(%q) = %s,%s; want %s,%s", tc.desc, tc.path, abs, rel, tc.expAbs, tc.expRel) } } } func TestMakeRx(t *testing.T) { for _, tc := range []struct { desc string names []string exp string }{ { desc: "empty string", names: []string{""}, exp: `^$`, }, { desc: "simple text", names: []string{"a"}, exp: `^a$`, }, { desc: "two words", names: []string{"foo", "bar"}, exp: `^foo$|^bar$`, }, { desc: "word & non-trivial", names: []string{"foo", `ab?c`}, exp: `^foo$|ab?c`, }, { desc: "bad regexp", names: []string{`(."`}, exp: `(."`, }, } { expRE, expErr := regexp.Compile(tc.exp) if re, err := makeRx(tc.names); !reflect.DeepEqual(err, expErr) && !reflect.DeepEqual(re, expRE) { t.Errorf("%s: makeRx(%v) = %q,%q; want %q,%q", tc.desc, tc.names, re, err, expRE, expErr) } } } func TestCommandLine(t *testing.T) { cleanup := setupGoroot(t) defer cleanup() mfs := mapfs.New(map[string]string{ "src/bar/bar.go": `// Package bar is an example. package bar `, "src/foo/foo.go": `// Package foo. package foo // First function is first. func First() { } // Second function is second. func Second() { } `, "src/gen/gen.go": `// Package gen package gen //line notgen.go:3 // F doc //line 1 should appear // line 2 should appear func F() //line foo.go:100`, // no newline on end to check corner cases! "src/vet/vet.go": `// Package vet package vet `, "src/cmd/go/doc.go": `// The go command package main `, "src/cmd/gofmt/doc.go": `// The gofmt command package main `, "src/cmd/vet/vet.go": `// The vet command package main `, }) fs := make(vfs.NameSpace) fs.Bind("/", mfs, "/", vfs.BindReplace) c := NewCorpus(fs) p := &Presentation{Corpus: c} p.cmdHandler = handlerServer{ p: p, c: c, pattern: "/cmd/", fsRoot: "/src/cmd", } p.pkgHandler = handlerServer{ p: p, c: c, pattern: "/pkg/", fsRoot: "/src", exclude: []string{"/src/cmd"}, } p.initFuncMap() p.PackageText = template.Must(template.New("PackageText").Funcs(p.FuncMap()).Parse(`{{$info := .}}{{$filtered := .IsFiltered}}{{if $filtered}}{{range .PAst}}{{range .Decls}}{{node $info .}}{{end}}{{end}}{{else}}{{with .PAst}}{{range $filename, $ast := .}}{{$filename}}: {{node $ $ast}}{{end}}{{end}}{{end}}{{with .PDoc}}{{if $.IsMain}}COMMAND {{.Doc}}{{else}}PACKAGE {{.Doc}}{{end}}{{with .Funcs}} {{range .}}{{node $ .Decl}} {{comment_text .Doc " " "\t"}}{{end}}{{end}}{{end}}`)) for _, tc := range []struct { desc string args []string exp string err bool }{ { desc: "standard package", args: []string{"fmt"}, exp: "PACKAGE Package fmt implements formatted I/O.\n", }, { desc: "package", args: []string{"bar"}, exp: "PACKAGE Package bar is an example.\n", }, { desc: "package w. filter", args: []string{"foo", "First"}, exp: "PACKAGE \nfunc First()\n First function is first.\n", }, { desc: "package w. bad filter", args: []string{"foo", "DNE"}, exp: "PACKAGE ", }, { desc: "source mode", args: []string{"src/bar"}, exp: "bar/bar.go:\n// Package bar is an example.\npackage bar\n", }, { desc: "source mode w. filter", args: []string{"src/foo", "Second"}, exp: "// Second function is second.\nfunc Second() {\n}", }, { desc: "package w. //line comments", args: []string{"gen", "F"}, exp: "PACKAGE \nfunc F()\n F doc //line 1 should appear line 2 should appear\n", }, { desc: "command", args: []string{"go"}, exp: "COMMAND The go command\n", }, { desc: "forced command", args: []string{"cmd/gofmt"}, exp: "COMMAND The gofmt command\n", }, { desc: "bad arg", args: []string{"doesnotexist"}, err: true, }, { desc: "both command and package", args: []string{"vet"}, exp: "use 'godoc cmd/vet' for documentation on the vet command \n\nPACKAGE Package vet\n", }, { desc: "root directory", args: []string{"/"}, exp: "", }, } { w := new(bytes.Buffer) err := CommandLine(w, fs, p, tc.args) if got, want := w.String(), tc.exp; got != want || tc.err == (err == nil) { t.Errorf("%s: CommandLine(%v) = %q (%v); want %q (%v)", tc.desc, tc.args, got, err, want, tc.err) } } }