docreflect/generate/generate_test.go

360 lines
8.8 KiB
Go

package generate
import (
"bytes"
"code.squareroundforest.org/arpio/notation"
"fmt"
"os"
"path"
"runtime"
"strings"
"testing"
)
func check(c ...string) map[string]string {
m := make(map[string]string)
for i := 0; i < len(c); i += 2 {
name := c[i]
var value string
if i < len(c)-1 {
value = c[i+1]
}
m[name] = value
}
return m
}
func testGenerate(check map[string]string, errstr string, o options, gopath ...string) func(t *testing.T) {
return func(t *testing.T) {
d, err := generate(o, gopath...)
if errstr != "" {
if err == nil {
t.Fatal("failed to fail")
}
if !strings.Contains(err.Error(), errstr) {
t.Fatal("unexpected error", err)
}
return
}
if err != nil {
t.Fatal(err)
}
var failed bool
for name, expect := range check {
dn, ok := d[name]
if !ok {
failed = true
break
}
if !strings.Contains(dn, expect) {
failed = true
break
}
}
if failed {
t.Log("failed to get the right documentation")
t.Log("expected matches:")
t.Log(notation.Sprintw(check))
t.Log("documetation got:")
t.Log(notation.Sprintw(d))
t.Fatal()
}
}
}
func TestGenerate(t *testing.T) {
wd := path.Clean(path.Join(os.Getenv("PWD"), ".."))
h := os.Getenv("HOME")
o := options{
wd: wd,
goroot: runtime.GOROOT(),
gomod: "code.squareroundforest.org/arpio/docreflect",
modules: map[string]string{
"golang.org/x/mod@v0.27.0": path.Join(h, "go", "pkg/mod", "golang.org/x/mod@v0.27.0"),
"code.squareroundforest.org/arpio/notation@v0.0.0-20241225183158-af3bd591a174": path.Join(
h, "go", "pkg/mod", "code.squareroundforest.org/arpio/notation@v0.0.0-20241225183158-af3bd591a174",
),
"code.squareroundforest.org/arpio/docreflect": wd,
},
}
t.Run("with modules", func(t *testing.T) {
t.Run(
"stdlib",
testGenerate(check("strings.Join", "Join concatenates", "strings.Join", "func(elems, sep)"), "", o, "strings.Join"),
)
notationPrintln := "code.squareroundforest.org/arpio/notation.Println"
t.Run(
"imported module",
testGenerate(
check(
notationPrintln,
"Println prints",
notationPrintln,
"func(v)",
),
"",
o,
notationPrintln,
),
)
localDocs := "code.squareroundforest.org/arpio/docreflect.Docs"
t.Run(
"local",
testGenerate(check(localDocs, "Docs returns the documentation for"), "", o, localDocs),
)
packagePath := "code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage"
t.Run(
"package",
testGenerate(
check(
packagePath, "Package testpackage is a test package",
fmt.Sprintf("%s.ExportedType", packagePath), "ExportedType has docs",
fmt.Sprintf("%s.ExportedType.Bar.Baz", packagePath), "Baz is another field",
),
"",
o,
packagePath,
),
)
t.Run("symbol", func(t *testing.T) {
t.Run("type", testGenerate(
check(
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType",
"ExportedType has docs",
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType2",
"",
),
"",
o,
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType",
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType2",
))
t.Run("const", testGenerate(
check(
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.C1",
"C1 is a const",
),
"",
o,
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.C1",
))
t.Run("const grouped", testGenerate(
check(
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.C3",
"Cx is a const",
),
"",
o,
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.C3",
))
t.Run("var", testGenerate(
check(
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.V1",
"V1 is a var",
),
"",
o,
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.V1",
))
t.Run("var grouped", testGenerate(
check(
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.V3",
"Vx is a var",
),
"",
o,
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.V3",
))
t.Run("func", testGenerate(
check(
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedFunc",
"ExportedFunc has documentation",
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.New",
"New create a new instance of ExportedType",
),
"",
o,
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.New",
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedFunc",
))
})
t.Run("field", testGenerate(
check(
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType.Foo",
"Foo is a field",
),
"",
o,
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType.Foo",
))
t.Run("method", testGenerate(
check(
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType.Method",
"Method is a method of ExportedType",
),
"",
o,
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType.Method",
))
t.Run("inline struct type expression", testGenerate(
check(
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType.Bar",
"Bar is an inline struct type expression",
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType.Bar.Baz",
"Baz is another field",
),
"",
o,
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType.Bar",
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType.Bar.Baz",
))
t.Run("unexported symbol", testGenerate(
check(
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.unexportedFunc",
"unexportedFunc can have documentation",
),
"",
o,
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.unexportedFunc",
))
mainFunc := "code.squareroundforest.org/arpio/docreflect/internal/tests/src/command.main"
t.Run("main package", testGenerate(check(mainFunc, "main func"), "", o, mainFunc))
})
t.Run("errors", func(t *testing.T) {
t.Run("package not found", testGenerate(nil, "package", o, "foo.bar.baz/qux"))
t.Run(
"symbol not found",
testGenerate(nil, "symbol", o, "code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.Qux"),
)
t.Run(
"field not found",
testGenerate(
nil,
"symbol",
o,
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType.Qux",
),
)
t.Run(
"field not found, second level",
testGenerate(
nil,
"symbol",
o,
"code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType.Foo.Qux",
),
)
t.Run(
"method not found",
testGenerate(nil, "symbol", o, "code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.ExportedType2.Qux"),
)
t.Run(
"invalid path",
testGenerate(nil, "foo", o, "./foo/bar"),
)
t.Run(
"non-existent module path",
testGenerate(nil, "package", o, "code.squareroundforest.org/arpio/notation/foo.Foo"),
)
t.Run(
"parse failed, syntax error",
testGenerate(nil, "package", o, "code.squareroundforest.org/arpio/docreflect/internal/tests/src/syntaxerror"),
)
t.Run(
"no type spec",
testGenerate(nil, "symbol", o, "code.squareroundforest.org/arpio/docreflect/internal/tests/src/testpackage.Foo.Qux"),
)
})
t.Run("init options", func(t *testing.T) {
o := initOptions()
if o.wd != os.Getenv("PWD") {
t.Fatal("wd")
}
if o.goroot != runtime.GOROOT() {
t.Fatal("goroot")
}
if o.gomod != "code.squareroundforest.org/arpio/docreflect" {
t.Fatal("gomod")
}
for _, module := range []string{
"code.squareroundforest.org/arpio/notation",
"golang.org/x/mod",
} {
var found bool
for key := range o.modules {
if strings.Contains(key, module) {
found = true
}
}
if !found {
t.Fatal("gomod")
}
}
})
}
func TestFormat(t *testing.T) {
// header
// import
// few items
// escaping
b := bytes.NewBuffer(nil)
d := map[string]string{
"foo": "bar",
"baz": "qux",
}
if err := format(b, "testpackage", d); err != nil {
t.Fatal(err)
}
o := b.String()
if o != `package testpackage
import "code.squareroundforest.org/arpio/docreflect"
func init() {
docreflect.Register("baz", "qux")
docreflect.Register("foo", "bar")
}
` {
t.Fatal()
}
}