313 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			313 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
| // 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() {
 | |
| }
 | |
| 
 | |
| // unexported function is third.
 | |
| func unexported() {
 | |
| }
 | |
| `,
 | |
| 		"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",
 | |
| 	}
 | |
| 	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
 | |
| 		all  bool
 | |
| 		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. unexported filter",
 | |
| 			args: []string{"foo", "unexported"},
 | |
| 			all:  true,
 | |
| 			exp:  "PACKAGE \nfunc unexported()\n    unexported function is third.\n",
 | |
| 		},
 | |
| 		{
 | |
| 			desc: "package w. unexported filter",
 | |
| 			args: []string{"foo", "unexported"},
 | |
| 			all:  false,
 | |
| 			exp:  "PACKAGE ",
 | |
| 		},
 | |
| 		{
 | |
| 			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:  "",
 | |
| 		},
 | |
| 	} {
 | |
| 		p.AllMode = tc.all
 | |
| 		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), All(%v) = %q (%v); want %q (%v)",
 | |
| 				tc.desc, tc.args, tc.all, got, err, want, tc.err)
 | |
| 		}
 | |
| 	}
 | |
| }
 |