1
2
3
4
5 package godoc
6
7 import (
8 "go/doc"
9 "net/http"
10 "net/http/httptest"
11 "net/url"
12 "sort"
13 "strings"
14 "testing"
15 "text/template"
16
17 "golang.org/x/tools/godoc/vfs/mapfs"
18 )
19
20
21
22 func TestIgnoredGoFiles(t *testing.T) {
23 packagePath := "github.com/package"
24 packageComment := "main is documented in an ignored .go file"
25
26 c := NewCorpus(mapfs.New(map[string]string{
27 "src/" + packagePath + "/ignored.go": `// +build ignore
28
29 // ` + packageComment + `
30 package main`}))
31 srv := &handlerServer{
32 p: &Presentation{
33 Corpus: c,
34 },
35 c: c,
36 }
37 pInfo := srv.GetPageInfo("/src/"+packagePath, packagePath, NoFiltering, "linux", "amd64")
38
39 if pInfo.PDoc == nil {
40 t.Error("pInfo.PDoc = nil; want non-nil.")
41 } else {
42 if got, want := pInfo.PDoc.Doc, packageComment+"\n"; got != want {
43 t.Errorf("pInfo.PDoc.Doc = %q; want %q.", got, want)
44 }
45 if got, want := pInfo.PDoc.Name, "main"; got != want {
46 t.Errorf("pInfo.PDoc.Name = %q; want %q.", got, want)
47 }
48 if got, want := pInfo.PDoc.ImportPath, packagePath; got != want {
49 t.Errorf("pInfo.PDoc.ImportPath = %q; want %q.", got, want)
50 }
51 }
52 if pInfo.FSet == nil {
53 t.Error("pInfo.FSet = nil; want non-nil.")
54 }
55 }
56
57 func TestIssue5247(t *testing.T) {
58 const packagePath = "example.com/p"
59 c := NewCorpus(mapfs.New(map[string]string{
60 "src/" + packagePath + "/p.go": `package p
61
62 //line notgen.go:3
63 // F doc //line 1 should appear
64 // line 2 should appear
65 func F()
66 //line foo.go:100`}))
67
68 srv := &handlerServer{
69 p: &Presentation{Corpus: c},
70 c: c,
71 }
72 pInfo := srv.GetPageInfo("/src/"+packagePath, packagePath, 0, "linux", "amd64")
73 if got, want := pInfo.PDoc.Funcs[0].Doc, "F doc //line 1 should appear\nline 2 should appear\n"; got != want {
74 t.Errorf("pInfo.PDoc.Funcs[0].Doc = %q; want %q", got, want)
75 }
76 }
77
78 func testServeBody(t *testing.T, p *Presentation, path, body string) {
79 t.Helper()
80 r := &http.Request{URL: &url.URL{Path: path}}
81 rw := httptest.NewRecorder()
82 p.ServeFile(rw, r)
83 if rw.Code != 200 || !strings.Contains(rw.Body.String(), body) {
84 t.Fatalf("GET %s: expected 200 w/ %q: got %d w/ body:\n%s",
85 path, body, rw.Code, rw.Body)
86 }
87 }
88
89 func TestRedirectAndMetadata(t *testing.T) {
90 c := NewCorpus(mapfs.New(map[string]string{
91 "doc/y/index.html": "Hello, y.",
92 "doc/x/index.html": `<!--{
93 "Path": "/doc/x/"
94 }-->
95
96 Hello, x.
97 `}))
98 c.updateMetadata()
99 p := &Presentation{
100 Corpus: c,
101 GodocHTML: template.Must(template.New("").Parse(`{{printf "%s" .Body}}`)),
102 }
103
104
105
106 for _, elem := range []string{"x", "y"} {
107 dir := "/doc/" + elem + "/"
108
109 r := &http.Request{URL: &url.URL{Path: dir + "index.html"}}
110 rw := httptest.NewRecorder()
111 p.ServeFile(rw, r)
112 loc := rw.Result().Header.Get("Location")
113 if rw.Code != 301 || loc != dir {
114 t.Errorf("GET %s: expected 301 -> %q, got %d -> %q", r.URL.Path, dir, rw.Code, loc)
115 }
116
117 testServeBody(t, p, dir, "Hello, "+elem)
118 }
119 }
120
121 func TestMarkdown(t *testing.T) {
122 p := &Presentation{
123 Corpus: NewCorpus(mapfs.New(map[string]string{
124 "doc/test.md": "**bold**",
125 "doc/test2.md": `{{"*template*"}}`,
126 })),
127 GodocHTML: template.Must(template.New("").Parse(`{{printf "%s" .Body}}`)),
128 }
129
130 testServeBody(t, p, "/doc/test.html", "<strong>bold</strong>")
131 testServeBody(t, p, "/doc/test2.html", "<em>template</em>")
132 }
133
134 func TestGenerics(t *testing.T) {
135 c := NewCorpus(mapfs.New(map[string]string{
136 "blah/blah.go": `package blah
137
138 var A AStruct[int]
139
140 type AStruct[T any] struct {
141 A string
142 X T
143 }
144
145 func (a *AStruct[T]) Method() T {
146 return a.X
147 }
148
149 func (a AStruct[T]) NonPointerMethod() T {
150 return a.X
151 }
152
153 func NewAStruct[T any](arg T) *AStruct[T] {
154 return &AStruct[T]{ X: arg }
155 }
156
157 type NonGenericStruct struct {
158 B int
159 }
160
161 func (b *NonGenericStruct) NonGenericMethod() int {
162 return b.B
163 }
164
165 func NewNonGenericStruct(arg int) *NonGenericStruct {
166 return &NonGenericStruct{arg}
167 }
168
169 type Pair[K, V any] struct {
170 K K
171 V V
172 }
173
174 func (p Pair[K, V]) Apply(kf func(K) K, vf func(V) V) Pair[K, V] {
175 return &Pair{ K: kf(p.K), V: vf(p.V) }
176 }
177
178 func (p *Pair[K, V]) Set(k K, v V) {
179 p.K = k
180 p.V = v
181 }
182
183 func NewPair[K, V any](k K, v V) Pair[K, V] {
184 return Pair[K, V]{ k, v }
185 }
186 `}))
187
188 srv := &handlerServer{
189 p: &Presentation{
190 Corpus: c,
191 },
192 c: c,
193 }
194 pInfo := srv.GetPageInfo("/blah/", "", NoFiltering, "linux", "amd64")
195 t.Logf("%v\n", pInfo)
196
197 findType := func(name string) *doc.Type {
198 for _, typ := range pInfo.PDoc.Types {
199 if typ.Name == name {
200 return typ
201 }
202 }
203 return nil
204 }
205
206 assertFuncs := func(typ *doc.Type, typFuncs []*doc.Func, funcs ...string) {
207 typfuncs := make([]string, len(typFuncs))
208 for i := range typFuncs {
209 typfuncs[i] = typFuncs[i].Name
210 }
211 sort.Strings(typfuncs)
212 sort.Strings(funcs)
213 if len(typfuncs) != len(funcs) {
214 t.Errorf("function mismatch for type %q, got: %q, want: %q", typ.Name, typfuncs, funcs)
215 return
216 }
217 for i := range funcs {
218 if funcs[i] != typfuncs[i] {
219 t.Errorf("function mismatch for type %q: got: %q, want: %q", typ.Name, typfuncs, funcs)
220 return
221 }
222 }
223 }
224
225 aStructType := findType("AStruct")
226 assertFuncs(aStructType, aStructType.Funcs, "NewAStruct")
227 assertFuncs(aStructType, aStructType.Methods, "Method", "NonPointerMethod")
228
229 nonGenericStructType := findType("NonGenericStruct")
230 assertFuncs(nonGenericStructType, nonGenericStructType.Funcs, "NewNonGenericStruct")
231 assertFuncs(nonGenericStructType, nonGenericStructType.Methods, "NonGenericMethod")
232
233 pairType := findType("Pair")
234 assertFuncs(pairType, pairType.Funcs, "NewPair")
235 assertFuncs(pairType, pairType.Methods, "Apply", "Set")
236
237 if len(pInfo.PDoc.Funcs) > 0 {
238 t.Errorf("unexpected functions in package documentation")
239 }
240 }
241
View as plain text