package fejkdata import ( "go/ast" "go/build" "go/parser" "go/token" "os" "regexp" "slices" "strconv" "strings" "testing" ) // Every backtick in the Vocabulary is a symbol. var backticked = regexp.MustCompile("`([^`\n]+)`") // TestVocabularyNamesDeclaredSymbols holds a renamed unit to its Vocabulary entry. func TestVocabularyNamesDeclaredSymbols(t *testing.T) { _, files := sourceFiles(t) declared := declaredSymbols(files) if len(namesDeclared(t, "the Vocabulary", vocabulary(t, files), declared)) == 0 { t.Fatal("the Vocabulary names no symbol, so nothing holds a renamed unit to its entry") } } // namesDeclared is every symbol text backticks, failing for each one the package // declares nowhere. func namesDeclared(t *testing.T, where, text string, declared map[string]bool) []string { t.Helper() var named []string for _, m := range backticked.FindAllStringSubmatch(text, -1) { if !declared[m[1]] { t.Errorf("%s names %s, which the package declares nowhere", where, m[1]) } named = append(named, m[1]) } return named } // namesAPass fails unless one symbol text backticks is a function: a field names // what a pass reads or writes, never the pass. func namesAPass(t *testing.T, where, text string, declared, funcs map[string]bool) { t.Helper() named := namesDeclared(t, where, text, declared) if !slices.ContainsFunc(named, func(n string) bool { return funcs[n] }) { t.Errorf("%s names no function, so it names no pass", where) } } func packageDir(t *testing.T) *build.Package { t.Helper() pkg, err := build.ImportDir(".", 0) if err != nil { t.Fatalf("read the package directory: %v", err) } return pkg } func sourceFiles(t *testing.T) (*token.FileSet, []*ast.File) { t.Helper() return parseFiles(t, packageDir(t).GoFiles) } func parseFiles(t *testing.T, names []string) (*token.FileSet, []*ast.File) { t.Helper() fset := token.NewFileSet() var files []*ast.File for _, name := range names { f, err := parser.ParseFile(fset, name, nil, parser.ParseComments) if err != nil { t.Fatalf("parse %s: %v", name, err) } files = append(files, f) } return fset, files } func vocabulary(t *testing.T, files []*ast.File) string { t.Helper() var sections strings.Builder for _, f := range files { for _, c := range f.Comments { section, found := strings.CutPrefix(c.Text(), "Vocabulary\n\n") if found && c.Pos() > f.Package { sections.WriteString(section) } } } if sections.Len() == 0 { t.Fatal("no comment below a package clause opens with a Vocabulary heading, so the words the package is written in are defined nowhere a reader of the code meets them") } return sections.String() } // declaredSymbols is every name the package declares at the top level, a method and // a struct field keyed under its type: pinSet.pin, tableRead.landsWhole. func declaredSymbols(files []*ast.File) map[string]bool { names := declaredFuncs(files) for _, f := range files { for _, decl := range f.Decls { if d, isGen := decl.(*ast.GenDecl); isGen { addSpecs(names, d) } } } return names } // declaredFuncs is every function the package declares, a method keyed under its // type: template.compileFormat. func declaredFuncs(files []*ast.File) map[string]bool { names := map[string]bool{} for _, f := range files { for _, decl := range f.Decls { d, isFunc := decl.(*ast.FuncDecl) if !isFunc { continue } names[d.Name.Name] = true if recv := receiverType(d.Recv); recv != "" { names[recv+"."+d.Name.Name] = true } } } return names } func addSpecs(names map[string]bool, d *ast.GenDecl) { for _, spec := range d.Specs { switch s := spec.(type) { case *ast.TypeSpec: names[s.Name.Name] = true addFields(names, s) case *ast.ValueSpec: for _, id := range s.Names { names[id.Name] = true } } } } func addFields(names map[string]bool, s *ast.TypeSpec) { st, isStruct := s.Type.(*ast.StructType) if !isStruct { return } for _, field := range st.Fields.List { for _, id := range field.Names { names[s.Name.Name+"."+id.Name] = true } } } func receiverType(recv *ast.FieldList) string { if recv == nil || len(recv.List) == 0 { return "" } e := recv.List[0].Type if star, isPointer := e.(*ast.StarExpr); isPointer { e = star.X } id, isIdent := e.(*ast.Ident) if !isIdent { return "" } return id.Name } const fillHeader = "Filled by " // TestTemplateFieldGroupsNameADeclaredPass holds a renamed pass to the group it // fills, and to the template doc. func TestTemplateFieldGroupsNameADeclaredPass(t *testing.T) { fset, files := sourceFiles(t) declared, funcs := declaredSymbols(files), declaredFuncs(files) doc, st, headers := templateStruct(t, fset, files) namesAPass(t, "the template doc", doc, declared, funcs) for _, field := range st.Fields.List { if len(field.Names) == 0 { t.Error("an embedded field of template sits under no header, so nothing says which pass fills it") } for _, id := range field.Names { header := headerAbove(headers, id.Pos()) if header == "" { t.Errorf("template.%s sits under no %q header, so nothing says which pass fills it", id.Name, fillHeader) continue } namesAPass(t, "the header above template."+id.Name, header, declared, funcs) } } } // templateStruct is the template struct, its doc, and the fill headers standing // between its fields, in source order. func templateStruct(t *testing.T, fset *token.FileSet, files []*ast.File) (string, *ast.StructType, []*ast.CommentGroup) { t.Helper() for _, f := range files { for _, decl := range f.Decls { d, isGen := decl.(*ast.GenDecl) if !isGen { continue } for _, spec := range d.Specs { s, isType := spec.(*ast.TypeSpec) if !isType || s.Name.Name != "template" { continue } st, isStruct := s.Type.(*ast.StructType) if !isStruct { t.Fatal("template is not a struct") } return d.Doc.Text() + s.Doc.Text(), st, fillHeaders(fset, f, st) } } } t.Fatal("the package declares no template struct") return "", nil, nil } // fillHeaders skips a header trailing a field: it names that field, never the // fields below it. func fillHeaders(fset *token.FileSet, f *ast.File, st *ast.StructType) []*ast.CommentGroup { trailing := map[int]bool{} for _, field := range st.Fields.List { trailing[fset.Position(field.End()).Line] = true } var found []*ast.CommentGroup for _, c := range f.Comments { if c.Pos() < st.Pos() || c.End() > st.End() || trailing[fset.Position(c.Pos()).Line] { continue } if strings.HasPrefix(c.Text(), fillHeader) { found = append(found, c) } } return found } // headerAbove is the last fill header standing above pos. func headerAbove(headers []*ast.CommentGroup, pos token.Pos) string { text := "" for _, c := range headers { if c.Pos() < pos { text = c.Text() } } return text } var ( printedCall = regexp.MustCompile(`(?m)^.*Fake\w*\(.*//\s*".*$`) printedFake = regexp.MustCompile(`\.Fake\("([^"]+)"\)\s*//\s*("(?:[^"\\]|\\.)*")`) ) // TestGoExamplesPrintWhatTheyRender replays each Go example's printed Fake calls on seed 42. func TestGoExamplesPrintWhatTheyRender(t *testing.T) { for _, name := range []string{"doc.go", "README.md"} { src, err := os.ReadFile(name) if err != nil { t.Fatal(err) } lines := printedCall.FindAllString(string(src), -1) if lines == nil { t.Errorf("%s prints no seeded Fake, so nothing holds its example to a render", name) } f, err := New(WithSeed(42)) if err != nil { t.Fatal(err) } for _, line := range lines { c := printedFake.FindStringSubmatch(line) if c == nil { t.Fatalf("%s prints a value this test cannot replay; spell it f.Fake(\"path\") // \"value\": %s", name, line) } want, err := strconv.Unquote(c[2]) if err != nil { t.Fatalf("%s prints %s: %v", name, c[2], err) } if got := fake(t, f, c[1]); got != want { t.Errorf("%s prints Fake(%q) = %q for seed 42, it renders %q", name, c[1], want, got) } } } }