golang.org/x/tools@v0.21.0/godoc/server_test.go (about) 1 // Copyright 2018 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 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 // TestIgnoredGoFiles tests the scenario where a folder has no .go or .c files, 21 // but has an ignored go file. 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`})) // No newline at end to check corner cases. 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 // Test that redirect is sent back correctly. 105 // Used to panic. See golang.org/issue/40665. 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 }