123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443 |
- package parser_test
- import (
- "bytes"
- "path"
- "path/filepath"
- "reflect"
- "testing"
- "text/template"
- "go-common/app/tool/gengo/args"
- "go-common/app/tool/gengo/namer"
- "go-common/app/tool/gengo/parser"
- "go-common/app/tool/gengo/types"
- )
- func TestRecursive(t *testing.T) {
- d := args.Default()
- d.InputDirs = []string{"go-common/app/tool/gengo/testdata/a/..."}
- b, err := d.NewBuilder()
- if err != nil {
- t.Fatalf("Fail making builder: %v", err)
- }
- _, err = b.FindTypes()
- if err != nil {
- t.Fatalf("Fail finding types: %v", err)
- }
- foundB := false
- for _, p := range b.FindPackages() {
- t.Logf("Package: %v", p)
- if p == "go-common/app/tool/gengo/testdata/a/b" {
- foundB = true
- }
- }
- if !foundB {
- t.Errorf("Expected to find packages a and b")
- }
- }
- type file struct {
- path string
- contents string
- }
- // Pass files in topological order - deps first!
- func construct(t *testing.T, files []file, testNamer namer.Namer) (*parser.Builder, types.Universe, []*types.Type) {
- b := parser.New()
- for _, f := range files {
- if err := b.AddFileForTest(path.Dir(f.path), filepath.FromSlash(f.path), []byte(f.contents)); err != nil {
- t.Fatal(err)
- }
- }
- u, err := b.FindTypes()
- if err != nil {
- t.Fatal(err)
- }
- orderer := namer.Orderer{Namer: testNamer}
- o := orderer.OrderUniverse(u)
- return b, u, o
- }
- func TestBuilder(t *testing.T) {
- var testFiles = []file{
- {
- path: "base/common/proto/common.go", contents: `
- package common
- type Object struct {
- ID int64
- }
- `,
- }, {
- path: "base/foo/proto/foo.go", contents: `
- package foo
- import (
- "base/common/proto"
- )
- type Blah struct {
- common.Object
- Count int64
- Frobbers map[string]*Frobber
- Baz []Object
- Nickname *string
- NumberIsAFavorite map[int]bool
- }
- type Frobber struct {
- Name string
- Amount int64
- }
- type Object struct {
- common.Object
- }
- func AFunc(obj1 common.Object, obj2 Object) Frobber {
- }
- var AVar Frobber
-
- var (
- AnotherVar = Frobber{}
- )
- `,
- },
- }
- var tmplText = `
- package o
- {{define "Struct"}}type {{Name .}} interface { {{range $m := .Members}}{{$n := Name $m.Type}}
- {{if $m.Embedded}}{{$n}}{{else}}{{$m.Name}}() {{$n}}{{if $m.Type.Elem}}{{else}}
- Set{{$m.Name}}({{$n}}){{end}}{{end}}{{end}}
- }
- {{end}}
- {{define "Func"}}{{$s := .Underlying.Signature}}var {{Name .}} func({{range $index,$elem := $s.Parameters}}{{if $index}}, {{end}}{{Raw $elem}}{{end}}) {{if $s.Results|len |gt 1}}({{end}}{{range $index,$elem := $s.Results}}{{if $index}}, {{end}}{{Raw .}}{{end}}{{if $s.Results|len |gt 1}}){{end}} = {{Raw .}}
- {{end}}
- {{define "Var"}}{{$t := .Underlying}}var {{Name .}} {{Raw $t}} = {{Raw .}}
- {{end}}
- {{range $t := .}}{{if eq $t.Kind "Struct"}}{{template "Struct" $t}}{{end}}{{end}}
- {{range $t := .}}{{if eq $t.Kind "DeclarationOf"}}{{if eq $t.Underlying.Kind "Func"}}{{template "Func" $t}}{{end}}{{end}}{{end}}
- {{range $t := .}}{{if eq $t.Kind "DeclarationOf"}}{{if ne $t.Underlying.Kind "Func"}}{{template "Var" $t}}{{end}}{{end}}{{end}}`
- var expect = `
- package o
- type CommonObject interface {
- ID() Int64
- SetID(Int64)
- }
- type FooBlah interface {
- CommonObject
- Count() Int64
- SetCount(Int64)
- Frobbers() MapStringToPointerFooFrobber
- Baz() SliceFooObject
- Nickname() PointerString
- NumberIsAFavorite() MapIntToBool
- }
- type FooFrobber interface {
- Name() String
- SetName(String)
- Amount() Int64
- SetAmount(Int64)
- }
- type FooObject interface {
- CommonObject
- }
- var FooAFunc func(proto.Object, proto.Object) proto.Frobber = proto.AFunc
- var FooAVar proto.Frobber = proto.AVar
- var FooAnotherVar proto.Frobber = proto.AnotherVar
- `
- testNamer := namer.NewPublicNamer(1, "proto")
- rawNamer := namer.NewRawNamer("o", nil)
- _, u, o := construct(t, testFiles, testNamer)
- t.Logf("\n%v\n\n", o)
- args := map[string]interface{}{
- "Name": testNamer.Name,
- "Raw": rawNamer.Name,
- }
- tmpl := template.Must(
- template.New("").
- Funcs(args).
- Parse(tmplText),
- )
- buf := &bytes.Buffer{}
- tmpl.Execute(buf, o)
- if e, a := expect, buf.String(); e != a {
- t.Errorf("Wanted, got:\n%v\n-----\n%v\n", e, a)
- }
- if p := u.Package("base/foo/proto"); !p.HasImport("base/common/proto") {
- t.Errorf("Unexpected lack of import line: %s", p.Imports)
- }
- }
- func TestStructParse(t *testing.T) {
- var structTest = file{
- path: "base/foo/proto/foo.go",
- contents: `
- package foo
- // Blah is a test.
- // A test, I tell you.
- type Blah struct {
- // A is the first field.
- A int64 ` + "`" + `json:"a"` + "`" + `
-
- // B is the second field.
- // Multiline comments work.
- B string ` + "`" + `json:"b"` + "`" + `
- }
- `,
- }
- _, u, o := construct(t, []file{structTest}, namer.NewPublicNamer(0))
- t.Logf("%#v", o)
- blahT := u.Type(types.Name{Package: "base/foo/proto", Name: "Blah"})
- if blahT == nil {
- t.Fatal("type not found")
- }
- if e, a := types.Struct, blahT.Kind; e != a {
- t.Errorf("struct kind wrong, wanted %v, got %v", e, a)
- }
- if e, a := []string{"Blah is a test.", "A test, I tell you."}, blahT.CommentLines; !reflect.DeepEqual(e, a) {
- t.Errorf("struct comment wrong, wanted %q, got %q", e, a)
- }
- m := types.Member{
- Name: "B",
- Embedded: false,
- CommentLines: []string{"B is the second field.", "Multiline comments work."},
- Tags: `json:"b"`,
- Type: types.String,
- }
- if e, a := m, blahT.Members[1]; !reflect.DeepEqual(e, a) {
- t.Errorf("wanted, got:\n%#v\n%#v", e, a)
- }
- }
- func TestParseSecondClosestCommentLines(t *testing.T) {
- const fileName = "base/foo/proto/foo.go"
- testCases := []struct {
- testFile file
- expected []string
- }{
- {
- testFile: file{
- path: fileName, contents: `
- package foo
- // Blah's SecondClosestCommentLines.
- // Another line.
- // Blah is a test.
- // A test, I tell you.
- type Blah struct {
- a int
- }
- `},
- expected: []string{"Blah's SecondClosestCommentLines.", "Another line."},
- },
- {
- testFile: file{
- path: fileName, contents: `
- package foo
- // Blah's SecondClosestCommentLines.
- // Another line.
-
- type Blah struct {
- a int
- }
- `},
- expected: []string{"Blah's SecondClosestCommentLines.", "Another line."},
- },
- }
- for _, test := range testCases {
- _, u, o := construct(t, []file{test.testFile}, namer.NewPublicNamer(0))
- t.Logf("%#v", o)
- blahT := u.Type(types.Name{Package: "base/foo/proto", Name: "Blah"})
- if e, a := test.expected, blahT.SecondClosestCommentLines; !reflect.DeepEqual(e, a) {
- t.Errorf("struct second closest comment wrong, wanted %q, got %q", e, a)
- }
- }
- }
- func TestTypeKindParse(t *testing.T) {
- var testFiles = []file{
- {path: "a/foo.go", contents: "package a\ntype Test string\n"},
- {path: "b/foo.go", contents: "package b\ntype Test map[int]string\n"},
- {path: "c/foo.go", contents: "package c\ntype Test []string\n"},
- {path: "d/foo.go", contents: "package d\ntype Test struct{a int; b struct{a int}; c map[int]string; d *string}\n"},
- {path: "e/foo.go", contents: "package e\ntype Test *string\n"},
- {path: "f/foo.go", contents: `
- package f
- import (
- "a"
- "b"
- )
- type Test []a.Test
- type Test2 *a.Test
- type Test3 map[a.Test]b.Test
- type Test4 struct {
- a struct {a a.Test; b b.Test}
- b map[a.Test]b.Test
- c *a.Test
- d []a.Test
- e []string
- }
- `},
- {path: "g/foo.go", contents: `
- package g
- type Test func(a, b string) (c, d string)
- func (t Test) Method(a, b string) (c, d string) { return t(a, b) }
- type Interface interface{Method(a, b string) (c, d string)}
- `},
- }
- // Check that the right types are found, and the namers give the expected names.
- assertions := []struct {
- Package, Name string
- k types.Kind
- names []string
- }{
- {
- Package: "a", Name: "Test", k: types.Alias,
- names: []string{"Test", "ATest", "test", "aTest", "a.Test"},
- },
- {
- Package: "b", Name: "Test", k: types.Map,
- names: []string{"Test", "BTest", "test", "bTest", "b.Test"},
- },
- {
- Package: "c", Name: "Test", k: types.Slice,
- names: []string{"Test", "CTest", "test", "cTest", "c.Test"},
- },
- {
- Package: "d", Name: "Test", k: types.Struct,
- names: []string{"Test", "DTest", "test", "dTest", "d.Test"},
- },
- {
- Package: "e", Name: "Test", k: types.Pointer,
- names: []string{"Test", "ETest", "test", "eTest", "e.Test"},
- },
- {
- Package: "f", Name: "Test", k: types.Slice,
- names: []string{"Test", "FTest", "test", "fTest", "f.Test"},
- },
- {
- Package: "g", Name: "Test", k: types.Func,
- names: []string{"Test", "GTest", "test", "gTest", "g.Test"},
- },
- {
- Package: "g", Name: "Interface", k: types.Interface,
- names: []string{"Interface", "GInterface", "interface", "gInterface", "g.Interface"},
- },
- {
- Package: "", Name: "string", k: types.Builtin,
- names: []string{"String", "String", "string", "string", "string"},
- },
- {
- Package: "", Name: "int", k: types.Builtin,
- names: []string{"Int", "Int", "int", "int", "int"},
- },
- {
- Package: "", Name: "struct{a int}", k: types.Struct,
- names: []string{"StructInt", "StructInt", "structInt", "structInt", "struct{a int}"},
- },
- {
- Package: "", Name: "struct{a a.Test; b b.Test}", k: types.Struct,
- names: []string{"StructTestTest", "StructATestBTest", "structTestTest", "structATestBTest", "struct{a a.Test; b b.Test}"},
- },
- {
- Package: "", Name: "map[int]string", k: types.Map,
- names: []string{"MapIntToString", "MapIntToString", "mapIntToString", "mapIntToString", "map[int]string"},
- },
- {
- Package: "", Name: "map[a.Test]b.Test", k: types.Map,
- names: []string{"MapTestToTest", "MapATestToBTest", "mapTestToTest", "mapATestToBTest", "map[a.Test]b.Test"},
- },
- {
- Package: "", Name: "[]string", k: types.Slice,
- names: []string{"SliceString", "SliceString", "sliceString", "sliceString", "[]string"},
- },
- {
- Package: "", Name: "[]a.Test", k: types.Slice,
- names: []string{"SliceTest", "SliceATest", "sliceTest", "sliceATest", "[]a.Test"},
- },
- {
- Package: "", Name: "*string", k: types.Pointer,
- names: []string{"PointerString", "PointerString", "pointerString", "pointerString", "*string"},
- },
- {
- Package: "", Name: "*a.Test", k: types.Pointer,
- names: []string{"PointerTest", "PointerATest", "pointerTest", "pointerATest", "*a.Test"},
- },
- }
- namers := []namer.Namer{
- namer.NewPublicNamer(0),
- namer.NewPublicNamer(1),
- namer.NewPrivateNamer(0),
- namer.NewPrivateNamer(1),
- namer.NewRawNamer("", nil),
- }
- for nameIndex, namer := range namers {
- _, u, _ := construct(t, testFiles, namer)
- t.Logf("Found types:\n")
- for pkgName, pkg := range u {
- for typeName, cur := range pkg.Types {
- t.Logf("%q-%q: %s %s", pkgName, typeName, cur.Name, cur.Kind)
- }
- }
- t.Logf("\n\n")
- for _, item := range assertions {
- n := types.Name{Package: item.Package, Name: item.Name}
- thisType := u.Type(n)
- if thisType == nil {
- t.Errorf("type %s not found", n)
- continue
- }
- underlyingType := thisType
- if item.k != types.Alias && thisType.Kind == types.Alias {
- underlyingType = thisType.Underlying
- if underlyingType == nil {
- t.Errorf("underlying type %s not found", n)
- continue
- }
- }
- if e, a := item.k, underlyingType.Kind; e != a {
- t.Errorf("%v-%s: type kind wrong, wanted %v, got %v (%#v)", nameIndex, n, e, a, underlyingType)
- }
- if e, a := item.names[nameIndex], namer.Name(thisType); e != a {
- t.Errorf("%v-%s: Expected %q, got %q", nameIndex, n, e, a)
- }
- }
- // Also do some one-off checks
- gtest := u.Type(types.Name{Package: "g", Name: "Test"})
- if e, a := 1, len(gtest.Methods); e != a {
- t.Errorf("expected %v but found %v methods: %#v", e, a, gtest)
- }
- iface := u.Type(types.Name{Package: "g", Name: "Interface"})
- if e, a := 1, len(iface.Methods); e != a {
- t.Errorf("expected %v but found %v methods: %#v", e, a, iface)
- }
- }
- }
|