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.
 
 
 

297 lines
12 KiB

  1. // Copyright 2014 The Go Authors. All rights reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file or at
  5. // https://developers.google.com/open-source/licenses/bsd.
  6. package gosrc
  7. import (
  8. "fmt"
  9. "io/ioutil"
  10. "net/http"
  11. "path"
  12. "reflect"
  13. "regexp"
  14. "strings"
  15. "testing"
  16. )
  17. var testWeb = map[string]string{
  18. // Package at root of a GitHub repo.
  19. "https://alice.org/pkg": `<head> <meta name="go-import" content="alice.org/pkg git https://github.com/alice/pkg"></head>`,
  20. // Package in sub-diretory.
  21. "https://alice.org/pkg/sub": `<head> <meta name="go-import" content="alice.org/pkg git https://github.com/alice/pkg"><body>`,
  22. // Fallback to http.
  23. "http://alice.org/pkg/http": `<head> <meta name="go-import" content="alice.org/pkg git https://github.com/alice/pkg">`,
  24. // Meta tag in sub-directory does not match meta tag at root.
  25. "https://alice.org/pkg/mismatch": `<head> <meta name="go-import" content="alice.org/pkg hg https://github.com/alice/pkg">`,
  26. // More than one matching meta tag.
  27. "http://alice.org/pkg/multiple": `<head> ` +
  28. `<meta name="go-import" content="alice.org/pkg git https://github.com/alice/pkg">` +
  29. `<meta name="go-import" content="alice.org/pkg git https://github.com/alice/pkg">`,
  30. // Package with go-source meta tag.
  31. "https://alice.org/pkg/source": `<head>` +
  32. `<meta name="go-import" content="alice.org/pkg git https://github.com/alice/pkg">` +
  33. `<meta name="go-source" content="alice.org/pkg http://alice.org/pkg http://alice.org/pkg{/dir} http://alice.org/pkg{/dir}?f={file}#Line{line}">`,
  34. "https://alice.org/pkg/ignore": `<head>` +
  35. `<title>Hello</title>` +
  36. // Unknown meta name
  37. `<meta name="go-junk" content="alice.org/pkg http://alice.org/pkg http://alice.org/pkg{/dir} http://alice.org/pkg{/dir}?f={file}#Line{line}">` +
  38. // go-source before go-meta
  39. `<meta name="go-source" content="alice.org/pkg http://alice.org/pkg http://alice.org/pkg{/dir} http://alice.org/pkg{/dir}?f={file}#Line{line}">` +
  40. // go-import tag for the package
  41. `<meta name="go-import" content="alice.org/pkg git https://github.com/alice/pkg">` +
  42. // go-import with wrong number of fields
  43. `<meta name="go-import" content="alice.org/pkg https://github.com/alice/pkg">` +
  44. // go-import with no fields
  45. `<meta name="go-import" content="">` +
  46. // go-source with wrong number of fields
  47. `<meta name="go-source" content="alice.org/pkg blah">` +
  48. // meta tag for a different package
  49. `<meta name="go-import" content="alice.org/other git https://github.com/alice/other">` +
  50. // meta tag for a different package
  51. `<meta name="go-import" content="alice.org/other git https://github.com/alice/other">` +
  52. `</head>` +
  53. // go-import outside of head
  54. `<meta name="go-import" content="alice.org/pkg git https://github.com/alice/pkg">`,
  55. // Package at root of a Git repo.
  56. "https://bob.com/pkg": `<head> <meta name="go-import" content="bob.com/pkg git https://vcs.net/bob/pkg.git">`,
  57. // Package at in sub-directory of a Git repo.
  58. "https://bob.com/pkg/sub": `<head> <meta name="go-import" content="bob.com/pkg git https://vcs.net/bob/pkg.git">`,
  59. // Package with go-source meta tag.
  60. "https://bob.com/pkg/source": `<head>` +
  61. `<meta name="go-import" content="bob.com/pkg git https://vcs.net/bob/pkg.git">` +
  62. `<meta name="go-source" content="bob.com/pkg http://bob.com/pkg http://bob.com/pkg{/dir}/ http://bob.com/pkg{/dir}/?f={file}#Line{line}">`,
  63. // Meta refresh to godoc.org
  64. "http://rsc.io/benchstat": `<!DOCTYPE html><html><head>` +
  65. `<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>` +
  66. `<meta name="go-import" content="rsc.io/benchstat git https://github.com/rsc/benchstat">` +
  67. `<meta http-equiv="refresh" content="0; url=https://godoc.org/rsc.io/benchstat">` +
  68. `</head>`,
  69. // Package with go-source meta tag, where {file} appears on the right of '#' in the file field URL template.
  70. "https://azul3d.org/examples": `<!DOCTYPE html><html><head>` +
  71. `<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>` +
  72. `<meta name="go-import" content="azul3d.org/examples git https://github.com/azul3d/examples">` +
  73. `<meta name="go-source" content="azul3d.org/examples https://github.com/azul3d/examples https://gotools.org/azul3d.org/examples{/dir} https://gotools.org/azul3d.org/examples{/dir}#{file}-L{line}">` +
  74. `<meta http-equiv="refresh" content="0; url=https://godoc.org/azul3d.org/examples">` +
  75. `</head>`,
  76. "https://azul3d.org/examples/abs": `<!DOCTYPE html><html><head>` +
  77. `<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>` +
  78. `<meta name="go-import" content="azul3d.org/examples git https://github.com/azul3d/examples">` +
  79. `<meta name="go-source" content="azul3d.org/examples https://github.com/azul3d/examples https://gotools.org/azul3d.org/examples{/dir} https://gotools.org/azul3d.org/examples{/dir}#{file}-L{line}">` +
  80. `<meta http-equiv="refresh" content="0; url=https://godoc.org/azul3d.org/examples/abs">` +
  81. `</head>`,
  82. }
  83. var getDynamicTests = []struct {
  84. importPath string
  85. dir *Directory
  86. }{
  87. {"alice.org/pkg", &Directory{
  88. BrowseURL: "https://github.com/alice/pkg",
  89. ImportPath: "alice.org/pkg",
  90. LineFmt: "%s#L%d",
  91. ProjectName: "pkg",
  92. ProjectRoot: "alice.org/pkg",
  93. ProjectURL: "https://alice.org/pkg",
  94. ResolvedPath: "github.com/alice/pkg",
  95. VCS: "git",
  96. Files: []*File{{Name: "main.go", BrowseURL: "https://github.com/alice/pkg/blob/master/main.go"}},
  97. }},
  98. {"alice.org/pkg/sub", &Directory{
  99. BrowseURL: "https://github.com/alice/pkg/tree/master/sub",
  100. ImportPath: "alice.org/pkg/sub",
  101. LineFmt: "%s#L%d",
  102. ProjectName: "pkg",
  103. ProjectRoot: "alice.org/pkg",
  104. ProjectURL: "https://alice.org/pkg",
  105. ResolvedPath: "github.com/alice/pkg/sub",
  106. VCS: "git",
  107. Files: []*File{{Name: "main.go", BrowseURL: "https://github.com/alice/pkg/blob/master/sub/main.go"}},
  108. }},
  109. {"alice.org/pkg/http", &Directory{
  110. BrowseURL: "https://github.com/alice/pkg/tree/master/http",
  111. ImportPath: "alice.org/pkg/http",
  112. LineFmt: "%s#L%d",
  113. ProjectName: "pkg",
  114. ProjectRoot: "alice.org/pkg",
  115. ProjectURL: "https://alice.org/pkg",
  116. ResolvedPath: "github.com/alice/pkg/http",
  117. VCS: "git",
  118. Files: []*File{{Name: "main.go", BrowseURL: "https://github.com/alice/pkg/blob/master/http/main.go"}},
  119. }},
  120. {"alice.org/pkg/source", &Directory{
  121. BrowseURL: "http://alice.org/pkg/source",
  122. ImportPath: "alice.org/pkg/source",
  123. LineFmt: "%s#Line%d",
  124. ProjectName: "pkg",
  125. ProjectRoot: "alice.org/pkg",
  126. ProjectURL: "http://alice.org/pkg",
  127. ResolvedPath: "github.com/alice/pkg/source",
  128. VCS: "git",
  129. Files: []*File{{Name: "main.go", BrowseURL: "http://alice.org/pkg/source?f=main.go"}},
  130. }},
  131. {"alice.org/pkg/ignore", &Directory{
  132. BrowseURL: "http://alice.org/pkg/ignore",
  133. ImportPath: "alice.org/pkg/ignore",
  134. LineFmt: "%s#Line%d",
  135. ProjectName: "pkg",
  136. ProjectRoot: "alice.org/pkg",
  137. ProjectURL: "http://alice.org/pkg",
  138. ResolvedPath: "github.com/alice/pkg/ignore",
  139. VCS: "git",
  140. Files: []*File{{Name: "main.go", BrowseURL: "http://alice.org/pkg/ignore?f=main.go"}},
  141. }},
  142. {"alice.org/pkg/mismatch", nil},
  143. {"alice.org/pkg/multiple", nil},
  144. {"alice.org/pkg/notfound", nil},
  145. {"bob.com/pkg", &Directory{
  146. ImportPath: "bob.com/pkg",
  147. ProjectName: "pkg",
  148. ProjectRoot: "bob.com/pkg",
  149. ProjectURL: "https://bob.com/pkg",
  150. ResolvedPath: "vcs.net/bob/pkg.git",
  151. VCS: "git",
  152. Files: []*File{{Name: "main.go"}},
  153. }},
  154. {"bob.com/pkg/sub", &Directory{
  155. ImportPath: "bob.com/pkg/sub",
  156. ProjectName: "pkg",
  157. ProjectRoot: "bob.com/pkg",
  158. ProjectURL: "https://bob.com/pkg",
  159. ResolvedPath: "vcs.net/bob/pkg.git/sub",
  160. VCS: "git",
  161. Files: []*File{{Name: "main.go"}},
  162. }},
  163. {"bob.com/pkg/source", &Directory{
  164. BrowseURL: "http://bob.com/pkg/source/",
  165. ImportPath: "bob.com/pkg/source",
  166. LineFmt: "%s#Line%d",
  167. ProjectName: "pkg",
  168. ProjectRoot: "bob.com/pkg",
  169. ProjectURL: "http://bob.com/pkg",
  170. ResolvedPath: "vcs.net/bob/pkg.git/source",
  171. VCS: "git",
  172. Files: []*File{{Name: "main.go", BrowseURL: "http://bob.com/pkg/source/?f=main.go"}},
  173. }},
  174. {"rsc.io/benchstat", &Directory{
  175. BrowseURL: "https://github.com/rsc/benchstat",
  176. ImportPath: "rsc.io/benchstat",
  177. LineFmt: "%s#L%d",
  178. ProjectName: "benchstat",
  179. ProjectRoot: "rsc.io/benchstat",
  180. ProjectURL: "https://github.com/rsc/benchstat",
  181. ResolvedPath: "github.com/rsc/benchstat",
  182. VCS: "git",
  183. Files: []*File{{Name: "main.go", BrowseURL: "https://github.com/rsc/benchstat/blob/master/main.go"}},
  184. }},
  185. {"azul3d.org/examples/abs", &Directory{
  186. BrowseURL: "https://gotools.org/azul3d.org/examples/abs",
  187. ImportPath: "azul3d.org/examples/abs",
  188. LineFmt: "%s-L%d",
  189. ProjectName: "examples",
  190. ProjectRoot: "azul3d.org/examples",
  191. ProjectURL: "https://github.com/azul3d/examples",
  192. ResolvedPath: "github.com/azul3d/examples/abs",
  193. VCS: "git",
  194. Files: []*File{{Name: "main.go", BrowseURL: "https://gotools.org/azul3d.org/examples/abs#main.go"}},
  195. }},
  196. }
  197. type testTransport map[string]string
  198. func (t testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
  199. statusCode := http.StatusOK
  200. req.URL.RawQuery = ""
  201. body, ok := t[req.URL.String()]
  202. if !ok {
  203. statusCode = http.StatusNotFound
  204. }
  205. resp := &http.Response{
  206. StatusCode: statusCode,
  207. Body: ioutil.NopCloser(strings.NewReader(body)),
  208. }
  209. return resp, nil
  210. }
  211. var githubPattern = regexp.MustCompile(`^github\.com/(?P<owner>[a-z0-9A-Z_.\-]+)/(?P<repo>[a-z0-9A-Z_.\-]+)(?P<dir>/[a-z0-9A-Z_.\-/]*)?$`)
  212. func testGet(client *http.Client, match map[string]string, etag string) (*Directory, error) {
  213. importPath := match["importPath"]
  214. if m := githubPattern.FindStringSubmatch(importPath); m != nil {
  215. browseURL := fmt.Sprintf("https://github.com/%s/%s", m[1], m[2])
  216. if m[3] != "" {
  217. browseURL = fmt.Sprintf("%s/tree/master%s", browseURL, m[3])
  218. }
  219. return &Directory{
  220. BrowseURL: browseURL,
  221. ImportPath: importPath,
  222. LineFmt: "%s#L%d",
  223. ProjectName: m[2],
  224. ProjectRoot: fmt.Sprintf("github.com/%s/%s", m[1], m[2]),
  225. ProjectURL: fmt.Sprintf("https://github.com/%s/%s", m[1], m[2]),
  226. VCS: "git",
  227. Files: []*File{{
  228. Name: "main.go",
  229. BrowseURL: fmt.Sprintf("https://github.com/%s/%s/blob/master%s/main.go", m[1], m[2], m[3]),
  230. }},
  231. }, nil
  232. }
  233. if strings.HasPrefix(match["repo"], "vcs.net") {
  234. return &Directory{
  235. ImportPath: importPath,
  236. ProjectName: path.Base(match["repo"]),
  237. ProjectRoot: fmt.Sprintf("%s.%s", match["repo"], match["vcs"]),
  238. VCS: match["vcs"],
  239. Files: []*File{{Name: "main.go"}},
  240. }, nil
  241. }
  242. return nil, errNoMatch
  243. }
  244. func TestGetDynamic(t *testing.T) {
  245. savedServices := services
  246. savedGetVCSDirFn := getVCSDirFn
  247. defer func() {
  248. services = savedServices
  249. getVCSDirFn = savedGetVCSDirFn
  250. }()
  251. services = []*service{{pattern: regexp.MustCompile(".*"), get: testGet}}
  252. getVCSDirFn = testGet
  253. client := &http.Client{Transport: testTransport(testWeb)}
  254. for _, tt := range getDynamicTests {
  255. dir, err := getDynamic(client, tt.importPath, "")
  256. if tt.dir == nil {
  257. if err == nil {
  258. t.Errorf("getDynamic(client, %q, etag) did not return expected error", tt.importPath)
  259. }
  260. continue
  261. }
  262. if err != nil {
  263. t.Errorf("getDynamic(client, %q, etag) return unexpected error: %v", tt.importPath, err)
  264. continue
  265. }
  266. if !reflect.DeepEqual(dir, tt.dir) {
  267. t.Errorf("getDynamic(client, %q, etag) =\n %+v,\nwant %+v", tt.importPath, dir, tt.dir)
  268. for i, f := range dir.Files {
  269. var want *File
  270. if i < len(tt.dir.Files) {
  271. want = tt.dir.Files[i]
  272. }
  273. t.Errorf("file %d = %+v, want %+v", i, f, want)
  274. }
  275. }
  276. }
  277. }