github.com/galaxyobe/gen@v0.0.0-20220910125335-392fa8f0990f/third_party/gengo/parser/parse_test.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package parser_test
    18  
    19  import (
    20  	"bytes"
    21  	"path"
    22  	"path/filepath"
    23  	"reflect"
    24  	"testing"
    25  	"text/template"
    26  
    27  	"github.com/davecgh/go-spew/spew"
    28  	"github.com/google/go-cmp/cmp"
    29  	"github.com/google/go-cmp/cmp/cmpopts"
    30  
    31  	"k8s.io/gengo/args"
    32  	"k8s.io/gengo/namer"
    33  	"k8s.io/gengo/parser"
    34  	"k8s.io/gengo/types"
    35  )
    36  
    37  func TestRecursive(t *testing.T) {
    38  	dir := "k8s.io/gengo/testdata/a"
    39  	d := args.Default()
    40  	d.InputDirs = []string{dir + "/..."}
    41  	b, err := d.NewBuilder()
    42  	if err != nil {
    43  		t.Fatalf("Fail making builder: %v", err)
    44  	}
    45  	findTypes, err := b.FindTypes()
    46  	if err != nil {
    47  		t.Fatalf("Fail finding types: %v", err)
    48  	}
    49  	foundB := false
    50  	foundC := false
    51  	for _, p := range b.FindPackages() {
    52  		t.Logf("Package: %v", p)
    53  		if p == "k8s.io/gengo/testdata/a/b" {
    54  			foundB = true
    55  		}
    56  		if p == "k8s.io/gengo/testdata/a/c" {
    57  			foundC = true
    58  		}
    59  	}
    60  	if !foundB {
    61  		t.Errorf("Expected to find packages a and b")
    62  	}
    63  	if foundC {
    64  		t.Error("Did not expect to find package c")
    65  	}
    66  	if name := findTypes[dir].Types["AA"].Methods["AFunc"].Name.Name; name != "func (*k8s.io/gengo/testdata/a.AA).AFunc(i *int, j int) (*k8s.io/gengo/testdata/a.A, k8s.io/gengo/testdata/a/b.ITest, error)" {
    67  		t.Errorf("Parse method type error, got name: %s", name)
    68  	}
    69  	// only has three package: package "a", package "b", and package "" for all
    70  	if len(findTypes) != 3 {
    71  		t.Error("Parse type error, and take type path as package")
    72  	}
    73  }
    74  
    75  func TestRecursiveWithTestGoFiles(t *testing.T) {
    76  	d := args.Default()
    77  	d.IncludeTestFiles = true
    78  	d.InputDirs = []string{"k8s.io/gengo/testdata/a/..."}
    79  	b, err := d.NewBuilder()
    80  	if err != nil {
    81  		t.Fatalf("Fail making builder: %v", err)
    82  	}
    83  	_, err = b.FindTypes()
    84  	if err != nil {
    85  		t.Fatalf("Fail finding types: %v", err)
    86  	}
    87  	foundB := false
    88  	foundC := false
    89  	for _, p := range b.FindPackages() {
    90  		t.Logf("Package: %v", p)
    91  		if p == "k8s.io/gengo/testdata/a/b" {
    92  			foundB = true
    93  		}
    94  		if p == "k8s.io/gengo/testdata/a/c" {
    95  			foundC = true
    96  		}
    97  	}
    98  	if !foundC || !foundB {
    99  		t.Errorf("Expected to find packages a, b and c")
   100  	}
   101  }
   102  
   103  type file struct {
   104  	path     string
   105  	contents string
   106  }
   107  
   108  // Pass files in topological order - deps first!
   109  func construct(t *testing.T, files []file, testNamer namer.Namer) (*parser.Builder, types.Universe, []*types.Type) {
   110  	b := parser.New()
   111  	for _, f := range files {
   112  		if err := b.AddFileForTest(path.Dir(f.path), filepath.FromSlash(f.path), []byte(f.contents)); err != nil {
   113  			t.Fatal(err)
   114  		}
   115  	}
   116  	u, err := b.FindTypes()
   117  	if err != nil {
   118  		t.Fatal(err)
   119  	}
   120  	orderer := namer.Orderer{Namer: testNamer}
   121  	o := orderer.OrderUniverse(u)
   122  	return b, u, o
   123  }
   124  
   125  func TestBuilder(t *testing.T) {
   126  	var testFiles = []file{
   127  		{
   128  			path: "base/common/proto/common.go", contents: `
   129                  package common
   130  
   131                  type Object struct {
   132      	            ID int64
   133                  }
   134                  `,
   135  		}, {
   136  			path: "base/foo/proto/foo.go", contents: `
   137                  package foo
   138  
   139                  import (
   140  	                "base/common/proto"
   141                  )
   142  
   143                  type Blah struct {
   144      	            common.Object
   145      	            Count int64
   146      	            Frobbers map[string]*Frobber
   147      	            Baz []Object
   148      	            Nickname *string
   149      	            NumberIsAFavorite map[int]bool
   150                  }
   151  
   152                  type Frobber struct {
   153  	                Name string
   154  	                Amount int64
   155                  }
   156  
   157                  type Object struct {
   158  	                common.Object
   159                  }
   160  
   161                  func AFunc(obj1 common.Object, obj2 Object) Frobber {
   162                  }
   163  
   164                  var AVar Frobber
   165      
   166                  var (
   167  	                AnotherVar = Frobber{}
   168                  )
   169  
   170  		type Enumeration string
   171  		const (
   172  			EnumSymbol Enumeration = "enumSymbolValue"
   173  		)
   174  
   175  		type Degrees int
   176  		const (
   177  			FirstDegree Degrees = iota
   178  			SecondDegree
   179  			ThirdDegree
   180  		)
   181  
   182  		const ConstNineNine = 99
   183  		const ConstHundred = ConstNineNine + 1
   184  
   185  		const ConstExpr = 1 - 0.707i * 9.3
   186  		const ConstFloat = float64(7.8)
   187  		const ConstString = "constant string"
   188                  `,
   189  		},
   190  	}
   191  
   192  	var tmplText = `
   193  package o
   194  {{define "Struct"}}type {{Name .}} interface { {{range $m := .Members}}{{$n := Name $m.Type}}
   195  	{{if $m.Embedded}}{{$n}}{{else}}{{$m.Name}}() {{$n}}{{if $m.Type.Elem}}{{else}}
   196  	Set{{$m.Name}}({{$n}}){{end}}{{end}}{{end}}
   197  }
   198  
   199  {{end}}
   200  {{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 .}}
   201  
   202  {{end}}
   203  {{define "Var"}}{{$t := .Underlying}}var {{Name .}} {{Raw $t}} = {{Raw .}}
   204  
   205  {{end}}
   206  {{define "Const"}}{{$t := .Underlying}}const {{Name .}} {{Raw $t}} = {{Raw .}}({{ .ConstValue }})
   207  
   208  {{end}}
   209  {{range $t := .}}{{if eq $t.Kind "Struct"}}{{template "Struct" $t}}{{end}}{{end}}
   210  {{range $t := .}}{{if eq $t.Kind "DeclarationOf"}}{{if eq $t.Underlying.Kind "Func"}}{{template "Func" $t}}{{end}}{{end}}{{end}}
   211  {{range $t := .}}{{if eq $t.Kind "DeclarationOf"}}{{if eq $t.Underlying.Kind "Struct"}}{{template "Var" $t}}{{end}}{{end}}{{end}}
   212  {{range $t := .}}{{if eq $t.Kind "DeclarationOf"}}{{if eq $t.Underlying.Kind "Alias"}}{{template "Const" $t}}{{end}}{{end}}{{end}}`
   213  
   214  	var expect = `
   215  package o
   216  
   217  
   218  
   219  
   220  type CommonObject interface { 
   221  	ID() Int64
   222  	SetID(Int64)
   223  }
   224  
   225  type FooBlah interface { 
   226  	CommonObject
   227  	Count() Int64
   228  	SetCount(Int64)
   229  	Frobbers() MapStringToPointerFooFrobber
   230  	Baz() SliceFooObject
   231  	Nickname() PointerString
   232  	NumberIsAFavorite() MapIntToBool
   233  }
   234  
   235  type FooFrobber interface { 
   236  	Name() String
   237  	SetName(String)
   238  	Amount() Int64
   239  	SetAmount(Int64)
   240  }
   241  
   242  type FooObject interface { 
   243  	CommonObject
   244  }
   245  
   246  
   247  var FooAFunc func(proto.Object, proto.Object) proto.Frobber = proto.AFunc
   248  
   249  
   250  var FooAVar proto.Frobber = proto.AVar
   251  
   252  var FooAnotherVar proto.Frobber = proto.AnotherVar
   253  
   254  
   255  const FooEnumSymbol proto.Enumeration = proto.EnumSymbol(enumSymbolValue)
   256  
   257  const FooFirstDegree proto.Degrees = proto.FirstDegree(0)
   258  
   259  const FooSecondDegree proto.Degrees = proto.SecondDegree(1)
   260  
   261  const FooThirdDegree proto.Degrees = proto.ThirdDegree(2)
   262  
   263  `
   264  	testNamer := namer.NewPublicNamer(1, "proto")
   265  	rawNamer := namer.NewRawNamer("o", nil)
   266  	_, u, o := construct(t, testFiles, testNamer)
   267  	t.Logf("\n%v\n\n", o)
   268  	args := map[string]interface{}{
   269  		"Name": testNamer.Name,
   270  		"Raw":  rawNamer.Name,
   271  	}
   272  	tmpl := template.Must(
   273  		template.New("").
   274  			Funcs(args).
   275  			Parse(tmplText),
   276  	)
   277  	buf := &bytes.Buffer{}
   278  	tmpl.Execute(buf, o)
   279  	if e, a := expect, buf.String(); e != a {
   280  		cmp.Diff(e, a)
   281  		t.Errorf("Wanted, got:\n%v\n-----\n%v\nDiff:\n%s", e, a, cmp.Diff(e, a))
   282  	}
   283  	if p := u.Package("base/foo/proto"); !p.HasImport("base/common/proto") {
   284  		t.Errorf("Unexpected lack of import line: %#v", p.Imports)
   285  	}
   286  
   287  	strPtr := func(s string) *string { return &s }
   288  
   289  	expectedConst := map[string]*types.Type{
   290  		"EnumSymbol": &types.Type{
   291  			Name:       types.Name{Package: "base/foo/proto", Name: "EnumSymbol"},
   292  			Kind:       types.DeclarationOf,
   293  			ConstValue: strPtr("enumSymbolValue"),
   294  		},
   295  		"FirstDegree": &types.Type{
   296  			Name:       types.Name{Package: "base/foo/proto", Name: "FirstDegree"},
   297  			Kind:       types.DeclarationOf,
   298  			ConstValue: strPtr("0"),
   299  		},
   300  		"SecondDegree": &types.Type{
   301  			Name:       types.Name{Package: "base/foo/proto", Name: "SecondDegree"},
   302  			Kind:       types.DeclarationOf,
   303  			ConstValue: strPtr("1"),
   304  		},
   305  		"ThirdDegree": &types.Type{
   306  			Name:       types.Name{Package: "base/foo/proto", Name: "ThirdDegree"},
   307  			Kind:       types.DeclarationOf,
   308  			ConstValue: strPtr("2"),
   309  		},
   310  		"ConstNineNine": &types.Type{
   311  			Name:       types.Name{Package: "base/foo/proto", Name: "ConstNineNine"},
   312  			Kind:       types.DeclarationOf,
   313  			ConstValue: strPtr("99"),
   314  		},
   315  		"ConstHundred": &types.Type{
   316  			Name:       types.Name{Package: "base/foo/proto", Name: "ConstHundred"},
   317  			Kind:       types.DeclarationOf,
   318  			ConstValue: strPtr("100"),
   319  		},
   320  		"ConstFloat": &types.Type{
   321  			Name:       types.Name{Package: "base/foo/proto", Name: "ConstFloat"},
   322  			Kind:       types.DeclarationOf,
   323  			ConstValue: strPtr("7.8"),
   324  		},
   325  		"ConstExpr": &types.Type{
   326  			Name:       types.Name{Package: "base/foo/proto", Name: "ConstExpr"},
   327  			Kind:       types.DeclarationOf,
   328  			ConstValue: strPtr("(1 + -6.5751i)"),
   329  		},
   330  		"ConstString": &types.Type{
   331  			Name:       types.Name{Package: "base/foo/proto", Name: "ConstString"},
   332  			Kind:       types.DeclarationOf,
   333  			ConstValue: strPtr("constant string"),
   334  		},
   335  	}
   336  
   337  	if diff := cmp.Diff(
   338  		u.Package("base/foo/proto").Constants, expectedConst,
   339  		cmpopts.IgnoreFields(types.Type{}, "Underlying"),
   340  		cmpopts.IgnoreFields(types.Type{}, "CommentLines"),
   341  		cmpopts.IgnoreFields(types.Type{}, "SecondClosestCommentLines"),
   342  	); diff != "" {
   343  		t.Errorf("Constant mismatch: %s", diff)
   344  	}
   345  
   346  	if len(u.Package("base/foo/proto").Constants) != len(expectedConst) {
   347  		t.Errorf("Wanted %d constants, got: %s",
   348  			len(expectedConst), spew.Sdump(u.Package("base/foo/proto").Constants))
   349  	}
   350  }
   351  
   352  func TestStructParse(t *testing.T) {
   353  	var structTest = file{
   354  		path: "base/foo/proto/foo.go",
   355  		contents: `
   356              package foo
   357  
   358              // Blah is a test.
   359              // A test, I tell you.
   360              type Blah struct {
   361  	            // A is the first field.
   362  	            A int64 ` + "`" + `json:"a"` + "`" + `
   363              
   364  	            // B is the second field.
   365  	            // Multiline comments work.
   366  	            B string ` + "`" + `json:"b"` + "`" + `
   367              }
   368              `,
   369  	}
   370  
   371  	_, u, o := construct(t, []file{structTest}, namer.NewPublicNamer(0))
   372  	t.Logf("%#v", o)
   373  	blahT := u.Type(types.Name{Package: "base/foo/proto", Name: "Blah"})
   374  	if blahT == nil {
   375  		t.Fatal("type not found")
   376  	}
   377  	if e, a := types.Struct, blahT.Kind; e != a {
   378  		t.Errorf("struct kind wrong, wanted %v, got %v", e, a)
   379  	}
   380  	if e, a := []string{"Blah is a test.", "A test, I tell you."}, blahT.CommentLines; !reflect.DeepEqual(e, a) {
   381  		t.Errorf("struct comment wrong, wanted %q, got %q", e, a)
   382  	}
   383  	m := types.Member{
   384  		Name:         "B",
   385  		Embedded:     false,
   386  		CommentLines: []string{"B is the second field.", "Multiline comments work."},
   387  		Tags:         `json:"b"`,
   388  		Type:         types.String,
   389  	}
   390  	if e, a := m, blahT.Members[1]; !reflect.DeepEqual(e, a) {
   391  		t.Errorf("wanted, got:\n%#v\n%#v", e, a)
   392  	}
   393  }
   394  
   395  func TestParseSecondClosestCommentLines(t *testing.T) {
   396  	const fileName = "base/foo/proto/foo.go"
   397  	testCases := []struct {
   398  		testFile file
   399  		expected []string
   400  	}{
   401  		{
   402  			testFile: file{
   403  				path: fileName, contents: `
   404  				    package foo
   405                      // Blah's SecondClosestCommentLines.
   406                      // Another line.
   407  
   408                      // Blah is a test.
   409                      // A test, I tell you.
   410                      type Blah struct {
   411  	                    a int
   412                      }
   413                      `},
   414  			expected: []string{"Blah's SecondClosestCommentLines.", "Another line."},
   415  		},
   416  		{
   417  			testFile: file{
   418  				path: fileName, contents: `
   419  				    package foo
   420                      // Blah's SecondClosestCommentLines.
   421                      // Another line.
   422                      
   423                      type Blah struct {
   424  	                    a int
   425                      }
   426                      `},
   427  			expected: []string{"Blah's SecondClosestCommentLines.", "Another line."},
   428  		},
   429  	}
   430  	for _, test := range testCases {
   431  		_, u, o := construct(t, []file{test.testFile}, namer.NewPublicNamer(0))
   432  		t.Logf("%#v", o)
   433  		blahT := u.Type(types.Name{Package: "base/foo/proto", Name: "Blah"})
   434  		if e, a := test.expected, blahT.SecondClosestCommentLines; !reflect.DeepEqual(e, a) {
   435  			t.Errorf("struct second closest comment wrong, wanted %q, got %q", e, a)
   436  		}
   437  	}
   438  }
   439  
   440  func TestParseMethodParameterAndResultNames(t *testing.T) {
   441  	const fileName = "base/foo/proto/foo.go"
   442  	testCases := []struct {
   443  		testFile            file
   444  		expectedParamNames  map[string][]string
   445  		expectedResultNames map[string][]string
   446  	}{
   447  		{
   448  			testFile: file{
   449  				path: fileName, contents: `
   450  				    package foo
   451  
   452  					type bar struct{} 
   453  
   454                      func (b *bar) SingleParam(param1 int) {}
   455                      func (b *bar) MultipleParams(param1, param2 int) {}
   456                      func (b *bar) SingleParamSingleResult(param1 int) (out1 bool) {}
   457                      func (b *bar) MultipleParamsMultipleResults(param1 bool, param2 int) (out1 bool, out2 string) {}
   458  					func (b *bar) NoParamsMultipleResults() (out1 bool, out2 string) {}
   459  					func (b *bar) NoParamsSingleResults() (out1 bool) {}
   460  					func (b *bar) NoParamsNoResults() {}
   461  					func (b *bar) UnnamedSingleParamNoResults(int) {}
   462  					func (b *bar) UnnamedMultipleParamsNoResult(int, bool) {}
   463  					func (b *bar) NoParamsSingleUnnamedResults() int {}
   464  					func (b *bar) NoParamsMultipleUnnamedResults() (int, string) {}
   465                      `},
   466  			expectedParamNames: map[string][]string{
   467  				"SingleParam":                    {"param1"},
   468  				"MultipleParams":                 {"param1", "param2"},
   469  				"SingleParamSingleResult":        {"param1"},
   470  				"MultipleParamsMultipleResults":  {"param1", "param2"},
   471  				"NoParamsMultipleResults":        nil,
   472  				"NoParamsSingleResults":          nil,
   473  				"NoParamsNoResults":              nil,
   474  				"UnnamedSingleParamNoResults":    {""},
   475  				"UnnamedMultipleParamsNoResult":  {"", ""},
   476  				"NoParamsSingleUnnamedResults":   nil,
   477  				"NoParamsMultipleUnnamedResults": nil,
   478  			},
   479  			expectedResultNames: map[string][]string{
   480  				"SingleParam":                    nil,
   481  				"MultipleParams":                 nil,
   482  				"SingleParamSingleResult":        {"out1"},
   483  				"MultipleParamsMultipleResults":  {"out1", "out2"},
   484  				"NoParamsMultipleResults":        {"out1", "out2"},
   485  				"NoParamsSingleResults":          {"out1"},
   486  				"NoParamsNoResults":              nil,
   487  				"UnnamedSingleParamNoResults":    nil,
   488  				"UnnamedMultipleParamsNoResult":  nil,
   489  				"NoParamsSingleUnnamedResults":   {""},
   490  				"NoParamsMultipleUnnamedResults": {"", ""},
   491  			},
   492  		},
   493  	}
   494  	for _, test := range testCases {
   495  		_, u, o := construct(t, []file{test.testFile}, namer.NewPublicNamer(0))
   496  		t.Logf("%#v", o)
   497  		blahT := u.Type(types.Name{Package: "base/foo/proto", Name: "bar"})
   498  
   499  		for methodName, methodType := range blahT.Methods {
   500  			expectedParamNames := test.expectedParamNames[methodName]
   501  			actualParamNames := methodType.Signature.ParameterNames
   502  			if !reflect.DeepEqual(expectedParamNames, actualParamNames) {
   503  				t.Errorf("%s param names parsed incorrectly wrong, wanted %v, got %v", methodName, expectedParamNames,
   504  					actualParamNames)
   505  			}
   506  
   507  			expectedResultNames := test.expectedResultNames[methodName]
   508  			actualResultNames := methodType.Signature.ResultNames
   509  			if !reflect.DeepEqual(expectedResultNames, actualResultNames) {
   510  				t.Errorf("%s result names parsed incorrectly wrong, wanted %v, got %v", methodName, expectedResultNames,
   511  					actualResultNames)
   512  			}
   513  		}
   514  
   515  	}
   516  }
   517  
   518  func TestParseMethodCommentLines(t *testing.T) {
   519  	const fileName = "base/foo/proto/foo.go"
   520  	testCases := []struct {
   521  		testFile file
   522  		expected []string
   523  	}{
   524  		{
   525  			testFile: file{
   526  				path: fileName, contents: `
   527  				    package foo
   528  
   529                      type Blah struct {
   530  	                    a int
   531                      }
   532  
   533                      // BlahFunc's CommentLines.
   534                      // Another line.
   535                      func (b *Blah) BlahFunc() {}
   536                      `},
   537  			expected: []string{"BlahFunc's CommentLines.", "Another line."},
   538  		},
   539  		{
   540  			testFile: file{
   541  				path: fileName, contents: `
   542  				    package foo
   543  
   544                      type Blah interface {
   545  	                    // BlahFunc's CommentLines.
   546  	                    // Another line.
   547  	                    BlahFunc()
   548                      }
   549                      `},
   550  			expected: []string{"BlahFunc's CommentLines.", "Another line."},
   551  		},
   552  	}
   553  	for _, test := range testCases {
   554  		_, u, o := construct(t, []file{test.testFile}, namer.NewPublicNamer(0))
   555  		t.Logf("%#v", o)
   556  		blahT := u.Type(types.Name{Package: "base/foo/proto", Name: "Blah"})
   557  		blahM := blahT.Methods["BlahFunc"]
   558  		if e, a := test.expected, blahM.CommentLines; !reflect.DeepEqual(e, a) {
   559  			t.Errorf("method comment wrong, wanted %q, got %q", e, a)
   560  		}
   561  	}
   562  
   563  	signatureTestCases := []struct {
   564  		testFile file
   565  		expected []string
   566  	}{
   567  		{
   568  			testFile: file{
   569  				path: fileName, contents: `
   570  				    package foo
   571  
   572                      type Blah struct {
   573  	                    a int
   574                      }
   575  
   576                      // Method1 CommentLines.
   577                      func (b *Blah) Method1(sameArg int) {}
   578  
   579  					// Method2 CommentLines.
   580                      func (b *Blah) Method2(sameArg int) {}
   581                      `},
   582  		},
   583  		{
   584  			testFile: file{
   585  				path: fileName, contents: `
   586  				    package foo
   587  
   588                      type Blah interface {
   589  						// Method1 CommentLines.
   590  						Method1(sameArg int) error
   591  
   592  						// Method2 CommentLines.
   593  						Method2(sameArg int) error
   594                      }
   595                      `},
   596  		},
   597  	}
   598  	for _, test := range signatureTestCases {
   599  		_, u, o := construct(t, []file{test.testFile}, namer.NewPublicNamer(0))
   600  		t.Logf("%#v", o)
   601  		blahT := u.Type(types.Name{Package: "base/foo/proto", Name: "Blah"})
   602  		blahM1 := blahT.Methods["Method1"]
   603  		blahM2 := blahT.Methods["Method2"]
   604  		c1 := blahM1.CommentLines
   605  		c2 := blahM2.CommentLines
   606  		if reflect.DeepEqual(c1, c2) {
   607  			t.Errorf("same signature method comment got equal, %v == %v", c1, c2)
   608  		}
   609  	}
   610  }
   611  
   612  func TestParseConstantCommentLines(t *testing.T) {
   613  	testFile := file{
   614  		path: "base/foo/proto/foo.go",
   615  		contents: `
   616  package foo
   617  
   618  // FooString is a string of foo.
   619  type FooString string
   620  
   621  // FooStringOne is one foo.
   622  const FooStringOne FooString = "One"
   623  
   624  // An important integer.
   625  // This one is nine.
   626  const OtherInt = 9
   627  `,
   628  	}
   629  
   630  	expectComment := func(obj *types.Type, lines []string) {
   631  		t.Helper()
   632  		if !reflect.DeepEqual(obj.CommentLines, lines) {
   633  			t.Errorf("wrong const comment for %q: wanted %q, got %q",
   634  				obj.Name,
   635  				lines, obj.CommentLines,
   636  			)
   637  		}
   638  	}
   639  
   640  	_, u, _ := construct(t, []file{testFile}, namer.NewPublicNamer(0))
   641  
   642  	expectComment(
   643  		u.Constant(types.Name{Package: "base/foo/proto", Name: "FooStringOne"}),
   644  		[]string{"FooStringOne is one foo."},
   645  	)
   646  
   647  	expectComment(
   648  		u.Constant(types.Name{Package: "base/foo/proto", Name: "OtherInt"}),
   649  		[]string{"An important integer.", "This one is nine."},
   650  	)
   651  }
   652  
   653  func TestTypeKindParse(t *testing.T) {
   654  	var testFiles = []file{
   655  		{path: "a/foo.go", contents: "package a\ntype Test string\n"},
   656  		{path: "b/foo.go", contents: "package b\ntype Test map[int]string\n"},
   657  		{path: "c/foo.go", contents: "package c\ntype Test []string\n"},
   658  		{path: "d/foo.go", contents: "package d\ntype Test struct{a int; b struct{a int}; c map[int]string; d *string}\n"},
   659  		{path: "e/foo.go", contents: "package e\ntype Test *string\n"},
   660  		{path: "f/foo.go", contents: `
   661              package f
   662              import (
   663  	            "a"
   664  	            "b"
   665              )
   666              type Test []a.Test
   667              type Test2 *a.Test
   668              type Test3 map[a.Test]b.Test
   669              type Test4 struct {
   670  	            a struct {a a.Test; b b.Test}
   671  	            b map[a.Test]b.Test
   672  	            c *a.Test
   673  	            d []a.Test
   674  	            e []string
   675              }
   676              `},
   677  		{path: "g/foo.go", contents: `
   678              package g
   679              type Test func(a, b string) (c, d string)
   680              func (t Test) Method(a, b string) (c, d string) { return t(a, b) }
   681              type Interface interface{Method(a, b string) (c, d string)}
   682              `},
   683  		{path: "h/foo.go", contents: `
   684              package h
   685              import "a"
   686              type Test [1]a.Test
   687              `},
   688  	}
   689  
   690  	// Check that the right types are found, and the namers give the expected names.
   691  
   692  	assertions := []struct {
   693  		Package, Name string
   694  		k             types.Kind
   695  		names         []string
   696  	}{
   697  		{
   698  			Package: "a", Name: "Test", k: types.Alias,
   699  			names: []string{"Test", "ATest", "test", "aTest", "a.Test"},
   700  		},
   701  		{
   702  			Package: "b", Name: "Test", k: types.Map,
   703  			names: []string{"Test", "BTest", "test", "bTest", "b.Test"},
   704  		},
   705  		{
   706  			Package: "c", Name: "Test", k: types.Slice,
   707  			names: []string{"Test", "CTest", "test", "cTest", "c.Test"},
   708  		},
   709  		{
   710  			Package: "d", Name: "Test", k: types.Struct,
   711  			names: []string{"Test", "DTest", "test", "dTest", "d.Test"},
   712  		},
   713  		{
   714  			Package: "e", Name: "Test", k: types.Pointer,
   715  			names: []string{"Test", "ETest", "test", "eTest", "e.Test"},
   716  		},
   717  		{
   718  			Package: "f", Name: "Test", k: types.Slice,
   719  			names: []string{"Test", "FTest", "test", "fTest", "f.Test"},
   720  		},
   721  		{
   722  			Package: "g", Name: "Test", k: types.Func,
   723  			names: []string{"Test", "GTest", "test", "gTest", "g.Test"},
   724  		},
   725  		{
   726  			Package: "g", Name: "Interface", k: types.Interface,
   727  			names: []string{"Interface", "GInterface", "interface", "gInterface", "g.Interface"},
   728  		},
   729  		{
   730  			Package: "h", Name: "Test", k: types.Array,
   731  			names: []string{"Test", "HTest", "test", "hTest", "h.Test"},
   732  		},
   733  		{
   734  			Package: "", Name: "string", k: types.Builtin,
   735  			names: []string{"String", "String", "string", "string", "string"},
   736  		},
   737  		{
   738  			Package: "", Name: "int", k: types.Builtin,
   739  			names: []string{"Int", "Int", "int", "int", "int"},
   740  		},
   741  		{
   742  			Package: "", Name: "struct{a int}", k: types.Struct,
   743  			names: []string{"StructInt", "StructInt", "structInt", "structInt", "struct{a int}"},
   744  		},
   745  		{
   746  			Package: "", Name: "struct{a a.Test; b b.Test}", k: types.Struct,
   747  			names: []string{"StructTestTest", "StructATestBTest", "structTestTest", "structATestBTest", "struct{a a.Test; b b.Test}"},
   748  		},
   749  		{
   750  			Package: "", Name: "map[int]string", k: types.Map,
   751  			names: []string{"MapIntToString", "MapIntToString", "mapIntToString", "mapIntToString", "map[int]string"},
   752  		},
   753  		{
   754  			Package: "", Name: "map[a.Test]b.Test", k: types.Map,
   755  			names: []string{"MapTestToTest", "MapATestToBTest", "mapTestToTest", "mapATestToBTest", "map[a.Test]b.Test"},
   756  		},
   757  		{
   758  			Package: "", Name: "[]string", k: types.Slice,
   759  			names: []string{"SliceString", "SliceString", "sliceString", "sliceString", "[]string"},
   760  		},
   761  		{
   762  			Package: "", Name: "[]a.Test", k: types.Slice,
   763  			names: []string{"SliceTest", "SliceATest", "sliceTest", "sliceATest", "[]a.Test"},
   764  		},
   765  		{
   766  			Package: "", Name: "*string", k: types.Pointer,
   767  			names: []string{"PointerString", "PointerString", "pointerString", "pointerString", "*string"},
   768  		},
   769  		{
   770  			Package: "", Name: "*a.Test", k: types.Pointer,
   771  			names: []string{"PointerTest", "PointerATest", "pointerTest", "pointerATest", "*a.Test"},
   772  		},
   773  	}
   774  
   775  	namers := []namer.Namer{
   776  		namer.NewPublicNamer(0),
   777  		namer.NewPublicNamer(1),
   778  		namer.NewPrivateNamer(0),
   779  		namer.NewPrivateNamer(1),
   780  		namer.NewRawNamer("", nil),
   781  	}
   782  
   783  	for nameIndex, namer := range namers {
   784  		_, u, _ := construct(t, testFiles, namer)
   785  		t.Logf("Found types:\n")
   786  		for pkgName, pkg := range u {
   787  			for typeName, cur := range pkg.Types {
   788  				t.Logf("%q-%q: %s %s", pkgName, typeName, cur.Name, cur.Kind)
   789  			}
   790  		}
   791  		t.Logf("\n\n")
   792  
   793  		for _, item := range assertions {
   794  			n := types.Name{Package: item.Package, Name: item.Name}
   795  			thisType := u.Type(n)
   796  			if thisType == nil {
   797  				t.Errorf("type %s not found", n)
   798  				continue
   799  			}
   800  			underlyingType := thisType
   801  			if item.k != types.Alias && thisType.Kind == types.Alias {
   802  				underlyingType = thisType.Underlying
   803  				if underlyingType == nil {
   804  					t.Errorf("underlying type %s not found", n)
   805  					continue
   806  				}
   807  			}
   808  			if e, a := item.k, underlyingType.Kind; e != a {
   809  				t.Errorf("%v-%s: type kind wrong, wanted %v, got %v (%#v)", nameIndex, n, e, a, underlyingType)
   810  			}
   811  			if e, a := item.names[nameIndex], namer.Name(thisType); e != a {
   812  				t.Errorf("%v-%s: Expected %q, got %q", nameIndex, n, e, a)
   813  			}
   814  		}
   815  
   816  		// Also do some one-off checks
   817  		gtest := u.Type(types.Name{Package: "g", Name: "Test"})
   818  		if e, a := 1, len(gtest.Methods); e != a {
   819  			t.Errorf("expected %v but found %v methods: %#v", e, a, gtest)
   820  		}
   821  		iface := u.Type(types.Name{Package: "g", Name: "Interface"})
   822  		if e, a := 1, len(iface.Methods); e != a {
   823  			t.Errorf("expected %v but found %v methods: %#v", e, a, iface)
   824  		}
   825  	}
   826  }