blob: cbdca62aa1d113bc76b0ab5d57367dc4edc804a2 [file] [log] [blame]
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package doc
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"io/fs"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"text/template"
)
var update = flag.Bool("update", false, "update golden (.out) files")
var files = flag.String("files", "", "consider only Go test files matching this regular expression")
const dataDir = "testdata"
var templateTxt = readTemplate("template.txt")
func readTemplate(filename string) *template.Template {
t := template.New(filename)
t.Funcs(template.FuncMap{
"node": nodeFmt,
"synopsis": synopsisFmt,
"indent": indentFmt,
})
return template.Must(t.ParseFiles(filepath.Join(dataDir, filename)))
}
func nodeFmt(node interface{}, fset *token.FileSet) string {
var buf bytes.Buffer
printer.Fprint(&buf, fset, node)
return strings.ReplaceAll(strings.TrimSpace(buf.String()), "\n", "\n\t")
}
func synopsisFmt(s string) string {
const n = 64
if len(s) > n {
// cut off excess text and go back to a word boundary
s = s[0:n]
if i := strings.LastIndexAny(s, "\t\n "); i >= 0 {
s = s[0:i]
}
s = strings.TrimSpace(s) + " ..."
}
return "// " + strings.ReplaceAll(s, "\n", " ")
}
func indentFmt(indent, s string) string {
end := ""
if strings.HasSuffix(s, "\n") {
end = "\n"
s = s[:len(s)-1]
}
return indent + strings.ReplaceAll(s, "\n", "\n"+indent) + end
}
func isGoFile(fi fs.FileInfo) bool {
name := fi.Name()
return !fi.IsDir() &&
len(name) > 0 && name[0] != '.' && // ignore .files
filepath.Ext(name) == ".go"
}
type bundle struct {
*Package
FSet *token.FileSet
}
func test(t *testing.T, mode Mode) {
// determine file filter
filter := isGoFile
if *files != "" {
rx, err := regexp.Compile(*files)
if err != nil {
t.Fatal(err)
}
filter = func(fi fs.FileInfo) bool {
return isGoFile(fi) && rx.MatchString(fi.Name())
}
}
// get packages
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, dataDir, filter, parser.ParseComments)
if err != nil {
t.Fatal(err)
}
// test packages
for _, pkg := range pkgs {
importPath := dataDir + "/" + pkg.Name
var files []*ast.File
for _, f := range pkg.Files {
files = append(files, f)
}
doc, err := NewFromFiles(fset, files, importPath, mode)
if err != nil {
t.Error(err)
continue
}
// golden files always use / in filenames - canonicalize them
for i, filename := range doc.Filenames {
doc.Filenames[i] = filepath.ToSlash(filename)
}
// print documentation
var buf bytes.Buffer
if err := templateTxt.Execute(&buf, bundle{doc, fset}); err != nil {
t.Error(err)
continue
}
got := buf.Bytes()
// update golden file if necessary
golden := filepath.Join(dataDir, fmt.Sprintf("%s.%d.golden", pkg.Name, mode))
if *update {
err := os.WriteFile(golden, got, 0644)
if err != nil {
t.Error(err)
}
continue
}
// get golden file
want, err := os.ReadFile(golden)
if err != nil {
t.Error(err)
continue
}
// compare
if !bytes.Equal(got, want) {
t.Errorf("package %s\n\tgot:\n%s\n\twant:\n%s", pkg.Name, got, want)
}
}
}
func Test(t *testing.T) {
test(t, 0)
test(t, AllDecls)
test(t, AllMethods)
}
func TestAnchorID(t *testing.T) {
const in = "Important Things 2 Know & Stuff"
const want = "hdr-Important_Things_2_Know___Stuff"
got := anchorID(in)
if got != want {
t.Errorf("anchorID(%q) = %q; want %q", in, got, want)
}
}