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.

323 lines
8.4 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/parser"
  8. "go/token"
  9. "strings"
  10. "testing"
  11. )
  12. func TestPkgLinkFunc(t *testing.T) {
  13. for _, tc := range []struct {
  14. path string
  15. want string
  16. }{
  17. {"/src/fmt", "pkg/fmt"},
  18. {"src/fmt", "pkg/fmt"},
  19. {"/fmt", "pkg/fmt"},
  20. {"fmt", "pkg/fmt"},
  21. } {
  22. if got := pkgLinkFunc(tc.path); got != tc.want {
  23. t.Errorf("pkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want)
  24. }
  25. }
  26. }
  27. func TestSrcPosLinkFunc(t *testing.T) {
  28. for _, tc := range []struct {
  29. src string
  30. line int
  31. low int
  32. high int
  33. want string
  34. }{
  35. {"/src/fmt/print.go", 42, 30, 50, "/src/fmt/print.go?s=30:50#L32"},
  36. {"/src/fmt/print.go", 2, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
  37. {"/src/fmt/print.go", 2, 0, 0, "/src/fmt/print.go#L2"},
  38. {"/src/fmt/print.go", 0, 0, 0, "/src/fmt/print.go"},
  39. {"/src/fmt/print.go", 0, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
  40. {"fmt/print.go", 0, 0, 0, "/src/fmt/print.go"},
  41. {"fmt/print.go", 0, 1, 5, "/src/fmt/print.go?s=1:5#L1"},
  42. } {
  43. if got := srcPosLinkFunc(tc.src, tc.line, tc.low, tc.high); got != tc.want {
  44. t.Errorf("srcLinkFunc(%v, %v, %v, %v) = %v; want %v", tc.src, tc.line, tc.low, tc.high, got, tc.want)
  45. }
  46. }
  47. }
  48. func TestSrcLinkFunc(t *testing.T) {
  49. for _, tc := range []struct {
  50. src string
  51. want string
  52. }{
  53. {"/src/fmt/print.go", "/src/fmt/print.go"},
  54. {"src/fmt/print.go", "/src/fmt/print.go"},
  55. {"/fmt/print.go", "/src/fmt/print.go"},
  56. {"fmt/print.go", "/src/fmt/print.go"},
  57. } {
  58. if got := srcLinkFunc(tc.src); got != tc.want {
  59. t.Errorf("srcLinkFunc(%v) = %v; want %v", tc.src, got, tc.want)
  60. }
  61. }
  62. }
  63. func TestQueryLinkFunc(t *testing.T) {
  64. for _, tc := range []struct {
  65. src string
  66. query string
  67. line int
  68. want string
  69. }{
  70. {"/src/fmt/print.go", "Sprintf", 33, "/src/fmt/print.go?h=Sprintf#L33"},
  71. {"/src/fmt/print.go", "Sprintf", 0, "/src/fmt/print.go?h=Sprintf"},
  72. {"src/fmt/print.go", "EOF", 33, "/src/fmt/print.go?h=EOF#L33"},
  73. {"src/fmt/print.go", "a%3f+%26b", 1, "/src/fmt/print.go?h=a%3f+%26b#L1"},
  74. } {
  75. if got := queryLinkFunc(tc.src, tc.query, tc.line); got != tc.want {
  76. t.Errorf("queryLinkFunc(%v, %v, %v) = %v; want %v", tc.src, tc.query, tc.line, got, tc.want)
  77. }
  78. }
  79. }
  80. func TestDocLinkFunc(t *testing.T) {
  81. for _, tc := range []struct {
  82. src string
  83. ident string
  84. want string
  85. }{
  86. {"fmt", "Sprintf", "/pkg/fmt/#Sprintf"},
  87. {"fmt", "EOF", "/pkg/fmt/#EOF"},
  88. } {
  89. if got := docLinkFunc(tc.src, tc.ident); got != tc.want {
  90. t.Errorf("docLinkFunc(%v, %v) = %v; want %v", tc.src, tc.ident, got, tc.want)
  91. }
  92. }
  93. }
  94. func TestSanitizeFunc(t *testing.T) {
  95. for _, tc := range []struct {
  96. src string
  97. want string
  98. }{
  99. {},
  100. {"foo", "foo"},
  101. {"func f()", "func f()"},
  102. {"func f(a int,)", "func f(a int)"},
  103. {"func f(a int,\n)", "func f(a int)"},
  104. {"func f(\n\ta int,\n\tb int,\n\tc int,\n)", "func f(a int, b int, c int)"},
  105. {" ( a, b, c ) ", "(a, b, c)"},
  106. {"( a, b, c int, foo bar , )", "(a, b, c int, foo bar)"},
  107. {"{ a, b}", "{a, b}"},
  108. {"[ a, b]", "[a, b]"},
  109. } {
  110. if got := sanitizeFunc(tc.src); got != tc.want {
  111. t.Errorf("sanitizeFunc(%v) = %v; want %v", tc.src, got, tc.want)
  112. }
  113. }
  114. }
  115. // Test that we add <span id="StructName.FieldName"> elements
  116. // to the HTML of struct fields.
  117. func TestStructFieldsIDAttributes(t *testing.T) {
  118. got := linkifySource(t, []byte(`
  119. package foo
  120. type T struct {
  121. NoDoc string
  122. // Doc has a comment.
  123. Doc string
  124. // Opt, if non-nil, is an option.
  125. Opt *int
  126. // Опция - другое поле.
  127. Опция bool
  128. }
  129. `))
  130. want := `type T struct {
  131. <span id="T.NoDoc"></span>NoDoc <a href="/pkg/builtin/#string">string</a>
  132. <span id="T.Doc"></span><span class="comment">// Doc has a comment.</span>
  133. Doc <a href="/pkg/builtin/#string">string</a>
  134. <span id="T.Opt"></span><span class="comment">// Opt, if non-nil, is an option.</span>
  135. Opt *<a href="/pkg/builtin/#int">int</a>
  136. <span id="T.Опция"></span><span class="comment">// Опция - другое поле.</span>
  137. Опция <a href="/pkg/builtin/#bool">bool</a>
  138. }`
  139. if got != want {
  140. t.Errorf("got: %s\n\nwant: %s\n", got, want)
  141. }
  142. }
  143. // Test that we add <span id="ConstName"> elements to the HTML
  144. // of definitions in const and var specs.
  145. func TestValueSpecIDAttributes(t *testing.T) {
  146. got := linkifySource(t, []byte(`
  147. package foo
  148. const (
  149. NoDoc string = "NoDoc"
  150. // Doc has a comment
  151. Doc = "Doc"
  152. NoVal
  153. )`))
  154. want := `const (
  155. <span id="NoDoc">NoDoc</span> <a href="/pkg/builtin/#string">string</a> = &#34;NoDoc&#34;
  156. <span class="comment">// Doc has a comment</span>
  157. <span id="Doc">Doc</span> = &#34;Doc&#34;
  158. <span id="NoVal">NoVal</span>
  159. )`
  160. if got != want {
  161. t.Errorf("got: %s\n\nwant: %s\n", got, want)
  162. }
  163. }
  164. func TestCompositeLitLinkFields(t *testing.T) {
  165. got := linkifySource(t, []byte(`
  166. package foo
  167. type T struct {
  168. X int
  169. }
  170. var S T = T{X: 12}`))
  171. want := `type T struct {
  172. <span id="T.X"></span>X <a href="/pkg/builtin/#int">int</a>
  173. }
  174. var <span id="S">S</span> <a href="#T">T</a> = <a href="#T">T</a>{<a href="#T.X">X</a>: 12}`
  175. if got != want {
  176. t.Errorf("got: %s\n\nwant: %s\n", got, want)
  177. }
  178. }
  179. func TestFuncDeclNotLink(t *testing.T) {
  180. // Function.
  181. got := linkifySource(t, []byte(`
  182. package http
  183. func Get(url string) (resp *Response, err error)`))
  184. want := `func Get(url <a href="/pkg/builtin/#string">string</a>) (resp *<a href="#Response">Response</a>, err <a href="/pkg/builtin/#error">error</a>)`
  185. if got != want {
  186. t.Errorf("got: %s\n\nwant: %s\n", got, want)
  187. }
  188. // Method.
  189. got = linkifySource(t, []byte(`
  190. package http
  191. func (h Header) Get(key string) string`))
  192. want = `func (h <a href="#Header">Header</a>) Get(key <a href="/pkg/builtin/#string">string</a>) <a href="/pkg/builtin/#string">string</a>`
  193. if got != want {
  194. t.Errorf("got: %s\n\nwant: %s\n", got, want)
  195. }
  196. }
  197. func linkifySource(t *testing.T, src []byte) string {
  198. p := &Presentation{
  199. DeclLinks: true,
  200. }
  201. fset := token.NewFileSet()
  202. af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments)
  203. if err != nil {
  204. t.Fatal(err)
  205. }
  206. var buf bytes.Buffer
  207. pi := &PageInfo{
  208. FSet: fset,
  209. }
  210. sep := ""
  211. for _, decl := range af.Decls {
  212. buf.WriteString(sep)
  213. sep = "\n"
  214. buf.WriteString(p.node_htmlFunc(pi, decl, true))
  215. }
  216. return buf.String()
  217. }
  218. func TestScanIdentifier(t *testing.T) {
  219. tests := []struct {
  220. in, want string
  221. }{
  222. {"foo bar", "foo"},
  223. {"foo/bar", "foo"},
  224. {" foo", ""},
  225. {"фоо", "фоо"},
  226. {"f123", "f123"},
  227. {"123f", ""},
  228. }
  229. for _, tt := range tests {
  230. got := scanIdentifier([]byte(tt.in))
  231. if string(got) != tt.want {
  232. t.Errorf("scanIdentifier(%q) = %q; want %q", tt.in, got, tt.want)
  233. }
  234. }
  235. }
  236. func TestReplaceLeadingIndentation(t *testing.T) {
  237. oldIndent := strings.Repeat(" ", 2)
  238. newIndent := strings.Repeat(" ", 4)
  239. tests := []struct {
  240. src, want string
  241. }{
  242. {" foo\n bar\n baz", " foo\n bar\n baz"},
  243. {" '`'\n '`'\n", " '`'\n '`'\n"},
  244. {" '\\''\n '`'\n", " '\\''\n '`'\n"},
  245. {" \"`\"\n \"`\"\n", " \"`\"\n \"`\"\n"},
  246. {" `foo\n bar`", " `foo\n bar`"},
  247. {" `foo\\`\n bar", " `foo\\`\n bar"},
  248. {" '\\`'`foo\n bar", " '\\`'`foo\n bar"},
  249. {
  250. " if true {\n foo := `One\n \tTwo\nThree`\n }\n",
  251. " if true {\n foo := `One\n \tTwo\n Three`\n }\n",
  252. },
  253. }
  254. for _, tc := range tests {
  255. if got := replaceLeadingIndentation(tc.src, oldIndent, newIndent); got != tc.want {
  256. t.Errorf("replaceLeadingIndentation:\n%v\n---\nhave:\n%v\n---\nwant:\n%v\n",
  257. tc.src, got, tc.want)
  258. }
  259. }
  260. }
  261. func TestSrcBreadcrumbFunc(t *testing.T) {
  262. for _, tc := range []struct {
  263. path string
  264. want string
  265. }{
  266. {"src/", `<span class="text-muted">src/</span>`},
  267. {"src/fmt/", `<a href="/src">src</a>/<span class="text-muted">fmt/</span>`},
  268. {"src/fmt/print.go", `<a href="/src">src</a>/<a href="/src/fmt">fmt</a>/<span class="text-muted">print.go</span>`},
  269. } {
  270. if got := srcBreadcrumbFunc(tc.path); got != tc.want {
  271. t.Errorf("srcBreadcrumbFunc(%v) = %v; want %v", tc.path, got, tc.want)
  272. }
  273. }
  274. }
  275. func TestSrcToPkgLinkFunc(t *testing.T) {
  276. for _, tc := range []struct {
  277. path string
  278. want string
  279. }{
  280. {"src/", `<a href="/pkg">Index</a>`},
  281. {"src/fmt/", `<a href="/pkg/fmt">fmt</a>`},
  282. {"pkg/", `<a href="/pkg">Index</a>`},
  283. {"pkg/LICENSE", `<a href="/pkg">Index</a>`},
  284. } {
  285. if got := srcToPkgLinkFunc(tc.path); got != tc.want {
  286. t.Errorf("srcToPkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want)
  287. }
  288. }
  289. }