You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

294 lines
6.3 KiB

  1. // Copyright 2013 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package godoc
  5. import (
  6. "bytes"
  7. "go/build"
  8. "io/ioutil"
  9. "os"
  10. "path/filepath"
  11. "reflect"
  12. "regexp"
  13. "runtime"
  14. "testing"
  15. "text/template"
  16. "golang.org/x/tools/godoc/vfs"
  17. "golang.org/x/tools/godoc/vfs/mapfs"
  18. )
  19. // setupGoroot creates temporary directory to act as GOROOT when running tests
  20. // that depend upon the build package. It updates build.Default to point to the
  21. // new GOROOT.
  22. // It returns a function that can be called to reset build.Default and remove
  23. // the temporary directory.
  24. func setupGoroot(t *testing.T) (cleanup func()) {
  25. var stdLib = map[string]string{
  26. "src/fmt/fmt.go": `// Package fmt implements formatted I/O.
  27. package fmt
  28. type Stringer interface {
  29. String() string
  30. }
  31. `,
  32. }
  33. goroot, err := ioutil.TempDir("", "cmdline_test")
  34. if err != nil {
  35. t.Fatal(err)
  36. }
  37. origContext := build.Default
  38. build.Default = build.Context{
  39. GOROOT: goroot,
  40. Compiler: "gc",
  41. }
  42. for relname, contents := range stdLib {
  43. name := filepath.Join(goroot, relname)
  44. if err := os.MkdirAll(filepath.Dir(name), 0770); err != nil {
  45. t.Fatal(err)
  46. }
  47. if err := ioutil.WriteFile(name, []byte(contents), 0770); err != nil {
  48. t.Fatal(err)
  49. }
  50. }
  51. return func() {
  52. if err := os.RemoveAll(goroot); err != nil {
  53. t.Log(err)
  54. }
  55. build.Default = origContext
  56. }
  57. }
  58. func TestPaths(t *testing.T) {
  59. cleanup := setupGoroot(t)
  60. defer cleanup()
  61. pres := &Presentation{
  62. pkgHandler: handlerServer{
  63. fsRoot: "/fsroot",
  64. },
  65. }
  66. fs := make(vfs.NameSpace)
  67. absPath := "/foo/fmt"
  68. if runtime.GOOS == "windows" {
  69. absPath = `c:\foo\fmt`
  70. }
  71. for _, tc := range []struct {
  72. desc string
  73. path string
  74. expAbs string
  75. expRel string
  76. }{
  77. {
  78. "Absolute path",
  79. absPath,
  80. "/target",
  81. "/target",
  82. },
  83. {
  84. "Local import",
  85. "../foo/fmt",
  86. "/target",
  87. "/target",
  88. },
  89. {
  90. "Import",
  91. "fmt",
  92. "/target",
  93. "fmt",
  94. },
  95. {
  96. "Default",
  97. "unknownpkg",
  98. "/fsroot/unknownpkg",
  99. "unknownpkg",
  100. },
  101. } {
  102. abs, rel := paths(fs, pres, tc.path)
  103. if abs != tc.expAbs || rel != tc.expRel {
  104. t.Errorf("%s: paths(%q) = %s,%s; want %s,%s", tc.desc, tc.path, abs, rel, tc.expAbs, tc.expRel)
  105. }
  106. }
  107. }
  108. func TestMakeRx(t *testing.T) {
  109. for _, tc := range []struct {
  110. desc string
  111. names []string
  112. exp string
  113. }{
  114. {
  115. desc: "empty string",
  116. names: []string{""},
  117. exp: `^$`,
  118. },
  119. {
  120. desc: "simple text",
  121. names: []string{"a"},
  122. exp: `^a$`,
  123. },
  124. {
  125. desc: "two words",
  126. names: []string{"foo", "bar"},
  127. exp: `^foo$|^bar$`,
  128. },
  129. {
  130. desc: "word & non-trivial",
  131. names: []string{"foo", `ab?c`},
  132. exp: `^foo$|ab?c`,
  133. },
  134. {
  135. desc: "bad regexp",
  136. names: []string{`(."`},
  137. exp: `(."`,
  138. },
  139. } {
  140. expRE, expErr := regexp.Compile(tc.exp)
  141. if re, err := makeRx(tc.names); !reflect.DeepEqual(err, expErr) && !reflect.DeepEqual(re, expRE) {
  142. t.Errorf("%s: makeRx(%v) = %q,%q; want %q,%q", tc.desc, tc.names, re, err, expRE, expErr)
  143. }
  144. }
  145. }
  146. func TestCommandLine(t *testing.T) {
  147. cleanup := setupGoroot(t)
  148. defer cleanup()
  149. mfs := mapfs.New(map[string]string{
  150. "src/bar/bar.go": `// Package bar is an example.
  151. package bar
  152. `,
  153. "src/foo/foo.go": `// Package foo.
  154. package foo
  155. // First function is first.
  156. func First() {
  157. }
  158. // Second function is second.
  159. func Second() {
  160. }
  161. `,
  162. "src/gen/gen.go": `// Package gen
  163. package gen
  164. //line notgen.go:3
  165. // F doc //line 1 should appear
  166. // line 2 should appear
  167. func F()
  168. //line foo.go:100`, // no newline on end to check corner cases!
  169. "src/vet/vet.go": `// Package vet
  170. package vet
  171. `,
  172. "src/cmd/go/doc.go": `// The go command
  173. package main
  174. `,
  175. "src/cmd/gofmt/doc.go": `// The gofmt command
  176. package main
  177. `,
  178. "src/cmd/vet/vet.go": `// The vet command
  179. package main
  180. `,
  181. })
  182. fs := make(vfs.NameSpace)
  183. fs.Bind("/", mfs, "/", vfs.BindReplace)
  184. c := NewCorpus(fs)
  185. p := &Presentation{Corpus: c}
  186. p.cmdHandler = handlerServer{
  187. p: p,
  188. c: c,
  189. pattern: "/cmd/",
  190. fsRoot: "/src/cmd",
  191. }
  192. p.pkgHandler = handlerServer{
  193. p: p,
  194. c: c,
  195. pattern: "/pkg/",
  196. fsRoot: "/src",
  197. exclude: []string{"/src/cmd"},
  198. }
  199. p.initFuncMap()
  200. 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}}:
  201. {{node $ $ast}}{{end}}{{end}}{{end}}{{with .PDoc}}{{if $.IsMain}}COMMAND {{.Doc}}{{else}}PACKAGE {{.Doc}}{{end}}{{with .Funcs}}
  202. {{range .}}{{node $ .Decl}}
  203. {{comment_text .Doc " " "\t"}}{{end}}{{end}}{{end}}`))
  204. for _, tc := range []struct {
  205. desc string
  206. args []string
  207. exp string
  208. err bool
  209. }{
  210. {
  211. desc: "standard package",
  212. args: []string{"fmt"},
  213. exp: "PACKAGE Package fmt implements formatted I/O.\n",
  214. },
  215. {
  216. desc: "package",
  217. args: []string{"bar"},
  218. exp: "PACKAGE Package bar is an example.\n",
  219. },
  220. {
  221. desc: "package w. filter",
  222. args: []string{"foo", "First"},
  223. exp: "PACKAGE \nfunc First()\n First function is first.\n",
  224. },
  225. {
  226. desc: "package w. bad filter",
  227. args: []string{"foo", "DNE"},
  228. exp: "PACKAGE ",
  229. },
  230. {
  231. desc: "source mode",
  232. args: []string{"src/bar"},
  233. exp: "bar/bar.go:\n// Package bar is an example.\npackage bar\n",
  234. },
  235. {
  236. desc: "source mode w. filter",
  237. args: []string{"src/foo", "Second"},
  238. exp: "// Second function is second.\nfunc Second() {\n}",
  239. },
  240. {
  241. desc: "package w. //line comments",
  242. args: []string{"gen", "F"},
  243. exp: "PACKAGE \nfunc F()\n F doc //line 1 should appear line 2 should appear\n",
  244. },
  245. {
  246. desc: "command",
  247. args: []string{"go"},
  248. exp: "COMMAND The go command\n",
  249. },
  250. {
  251. desc: "forced command",
  252. args: []string{"cmd/gofmt"},
  253. exp: "COMMAND The gofmt command\n",
  254. },
  255. {
  256. desc: "bad arg",
  257. args: []string{"doesnotexist"},
  258. err: true,
  259. },
  260. {
  261. desc: "both command and package",
  262. args: []string{"vet"},
  263. exp: "use 'godoc cmd/vet' for documentation on the vet command \n\nPACKAGE Package vet\n",
  264. },
  265. {
  266. desc: "root directory",
  267. args: []string{"/"},
  268. exp: "",
  269. },
  270. } {
  271. w := new(bytes.Buffer)
  272. err := CommandLine(w, fs, p, tc.args)
  273. if got, want := w.String(), tc.exp; got != want || tc.err == (err == nil) {
  274. t.Errorf("%s: CommandLine(%v) = %q (%v); want %q (%v)",
  275. tc.desc, tc.args, got, err, want, tc.err)
  276. }
  277. }
  278. }