github.com/F4RD1N/gomobile@v1.0.1/bind/bind_test.go (about)

     1  package bind
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"go/ast"
     7  	"go/build"
     8  	"go/format"
     9  	"go/importer"
    10  	"go/parser"
    11  	"go/token"
    12  	"go/types"
    13  	"io"
    14  	"io/ioutil"
    15  	"log"
    16  	"os"
    17  	"os/exec"
    18  	"path"
    19  	"path/filepath"
    20  	"runtime"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/F4RD1N/gomobile/internal/importers"
    25  	"github.com/F4RD1N/gomobile/internal/importers/java"
    26  	"github.com/F4RD1N/gomobile/internal/importers/objc"
    27  )
    28  
    29  func init() {
    30  	log.SetFlags(log.Lshortfile)
    31  }
    32  
    33  var updateFlag = flag.Bool("update", false, "Update the golden files.")
    34  
    35  var tests = []string{
    36  	"", // The universe package with the error type.
    37  	"testdata/basictypes.go",
    38  	"testdata/structs.go",
    39  	"testdata/interfaces.go",
    40  	"testdata/issue10788.go",
    41  	"testdata/issue12328.go",
    42  	"testdata/issue12403.go",
    43  	"testdata/issue29559.go",
    44  	"testdata/keywords.go",
    45  	"testdata/try.go",
    46  	"testdata/vars.go",
    47  	"testdata/ignore.go",
    48  	"testdata/doc.go",
    49  	"testdata/underscores.go",
    50  }
    51  
    52  var javaTests = []string{
    53  	"testdata/java.go",
    54  	"testdata/classes.go",
    55  }
    56  
    57  var objcTests = []string{
    58  	"testdata/objc.go",
    59  	"testdata/objcw.go",
    60  }
    61  
    62  var fset = token.NewFileSet()
    63  
    64  func fileRefs(t *testing.T, filename string, pkgPrefix string) *importers.References {
    65  	f, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
    66  	if err != nil {
    67  		t.Fatalf("%s: %v", filename, err)
    68  	}
    69  	refs, err := importers.AnalyzeFile(f, pkgPrefix)
    70  	if err != nil {
    71  		t.Fatalf("%s: %v", filename, err)
    72  	}
    73  	fakePath := path.Dir(filename)
    74  	for i := range refs.Embedders {
    75  		refs.Embedders[i].PkgPath = fakePath
    76  	}
    77  	return refs
    78  }
    79  
    80  func typeCheck(t *testing.T, filename string, gopath string) (*types.Package, *ast.File) {
    81  	f, err := parser.ParseFile(fset, filename, nil, parser.AllErrors|parser.ParseComments)
    82  	if err != nil {
    83  		t.Fatalf("%s: %v", filename, err)
    84  	}
    85  
    86  	pkgName := filepath.Base(filename)
    87  	pkgName = strings.TrimSuffix(pkgName, ".go")
    88  
    89  	// typecheck and collect typechecker errors
    90  	var conf types.Config
    91  	conf.Error = func(err error) {
    92  		t.Error(err)
    93  	}
    94  	if gopath != "" {
    95  		conf.Importer = importer.Default()
    96  		oldDefault := build.Default
    97  		defer func() { build.Default = oldDefault }()
    98  		build.Default.GOPATH = gopath
    99  	}
   100  	pkg, err := conf.Check(pkgName, fset, []*ast.File{f}, nil)
   101  	if err != nil {
   102  		t.Fatal(err)
   103  	}
   104  	return pkg, f
   105  }
   106  
   107  // diff runs the command "diff a b" and returns its output
   108  func diff(a, b string) string {
   109  	var buf bytes.Buffer
   110  	var cmd *exec.Cmd
   111  	switch runtime.GOOS {
   112  	case "plan9":
   113  		cmd = exec.Command("/bin/diff", "-c", a, b)
   114  	default:
   115  		cmd = exec.Command("/usr/bin/diff", "-u", a, b)
   116  	}
   117  	cmd.Stdout = &buf
   118  	cmd.Stderr = &buf
   119  	cmd.Run()
   120  	return buf.String()
   121  }
   122  
   123  func writeTempFile(t *testing.T, name string, contents []byte) string {
   124  	f, err := ioutil.TempFile("", name)
   125  	if err != nil {
   126  		t.Fatal(err)
   127  	}
   128  	if _, err := f.Write(contents); err != nil {
   129  		t.Fatal(err)
   130  	}
   131  	if err := f.Close(); err != nil {
   132  		t.Fatal(err)
   133  	}
   134  	return f.Name()
   135  }
   136  
   137  func TestGenObjc(t *testing.T) {
   138  	for _, filename := range tests {
   139  		var pkg *types.Package
   140  		var file *ast.File
   141  		if filename != "" {
   142  			pkg, file = typeCheck(t, filename, "")
   143  		}
   144  
   145  		var buf bytes.Buffer
   146  		g := &ObjcGen{
   147  			Generator: &Generator{
   148  				Printer: &Printer{Buf: &buf, IndentEach: []byte("\t")},
   149  				Fset:    fset,
   150  				Files:   []*ast.File{file},
   151  				Pkg:     pkg,
   152  			},
   153  		}
   154  		if pkg != nil {
   155  			g.AllPkg = []*types.Package{pkg}
   156  		}
   157  		g.Init(nil)
   158  
   159  		testcases := []struct {
   160  			suffix string
   161  			gen    func() error
   162  		}{
   163  			{
   164  				".objc.h.golden",
   165  				g.GenH,
   166  			},
   167  			{
   168  				".objc.m.golden",
   169  				g.GenM,
   170  			},
   171  			{
   172  				".objc.go.h.golden",
   173  				g.GenGoH,
   174  			},
   175  		}
   176  		for _, tc := range testcases {
   177  			buf.Reset()
   178  			if err := tc.gen(); err != nil {
   179  				t.Errorf("%s: %v", filename, err)
   180  				continue
   181  			}
   182  			out := writeTempFile(t, "generated"+tc.suffix, buf.Bytes())
   183  			defer os.Remove(out)
   184  			var golden string
   185  			if filename != "" {
   186  				golden = filename[:len(filename)-len(".go")]
   187  			} else {
   188  				golden = "testdata/universe"
   189  			}
   190  			golden += tc.suffix
   191  			if diffstr := diff(golden, out); diffstr != "" {
   192  				t.Errorf("%s: does not match Objective-C golden:\n%s", filename, diffstr)
   193  				if *updateFlag {
   194  					t.Logf("Updating %s...", golden)
   195  					err := exec.Command("/bin/cp", out, golden).Run()
   196  					if err != nil {
   197  						t.Errorf("Update failed: %s", err)
   198  					}
   199  				}
   200  			}
   201  		}
   202  	}
   203  }
   204  
   205  func genObjcPackages(t *testing.T, dir string, cg *ObjcWrapper) {
   206  	pkgBase := filepath.Join(dir, "src", "ObjC")
   207  	if err := os.MkdirAll(pkgBase, 0700); err != nil {
   208  		t.Fatal(err)
   209  	}
   210  	for i, jpkg := range cg.Packages() {
   211  		pkgDir := filepath.Join(pkgBase, jpkg)
   212  		if err := os.MkdirAll(pkgDir, 0700); err != nil {
   213  			t.Fatal(err)
   214  		}
   215  		pkgFile := filepath.Join(pkgDir, "package.go")
   216  		cg.Buf.Reset()
   217  		cg.GenPackage(i)
   218  		if err := ioutil.WriteFile(pkgFile, cg.Buf.Bytes(), 0600); err != nil {
   219  			t.Fatal(err)
   220  		}
   221  	}
   222  	cg.Buf.Reset()
   223  	cg.GenInterfaces()
   224  	clsFile := filepath.Join(pkgBase, "interfaces.go")
   225  	if err := ioutil.WriteFile(clsFile, cg.Buf.Bytes(), 0600); err != nil {
   226  		t.Fatal(err)
   227  	}
   228  
   229  	gocmd := filepath.Join(runtime.GOROOT(), "bin", "go")
   230  	cmd := exec.Command(
   231  		gocmd,
   232  		"install",
   233  		"-pkgdir="+filepath.Join(dir, "pkg", build.Default.GOOS+"_"+build.Default.GOARCH),
   234  		"ObjC/...",
   235  	)
   236  	cmd.Env = append(os.Environ(), "GOPATH="+dir, "GO111MODULE=off")
   237  	if out, err := cmd.CombinedOutput(); err != nil {
   238  		t.Fatalf("failed to go install the generated ObjC wrappers: %v: %s", err, string(out))
   239  	}
   240  }
   241  
   242  func genJavaPackages(t *testing.T, dir string, cg *ClassGen) {
   243  	buf := cg.Buf
   244  	cg.Buf = new(bytes.Buffer)
   245  	pkgBase := filepath.Join(dir, "src", "Java")
   246  	if err := os.MkdirAll(pkgBase, 0700); err != nil {
   247  		t.Fatal(err)
   248  	}
   249  	for i, jpkg := range cg.Packages() {
   250  		pkgDir := filepath.Join(pkgBase, jpkg)
   251  		if err := os.MkdirAll(pkgDir, 0700); err != nil {
   252  			t.Fatal(err)
   253  		}
   254  		pkgFile := filepath.Join(pkgDir, "package.go")
   255  		cg.Buf.Reset()
   256  		cg.GenPackage(i)
   257  		if err := ioutil.WriteFile(pkgFile, cg.Buf.Bytes(), 0600); err != nil {
   258  			t.Fatal(err)
   259  		}
   260  		io.Copy(buf, cg.Buf)
   261  	}
   262  	cg.Buf.Reset()
   263  	cg.GenInterfaces()
   264  	clsFile := filepath.Join(pkgBase, "interfaces.go")
   265  	if err := ioutil.WriteFile(clsFile, cg.Buf.Bytes(), 0600); err != nil {
   266  		t.Fatal(err)
   267  	}
   268  	io.Copy(buf, cg.Buf)
   269  	cg.Buf = buf
   270  
   271  	gocmd := filepath.Join(runtime.GOROOT(), "bin", "go")
   272  	cmd := exec.Command(
   273  		gocmd,
   274  		"install",
   275  		"-pkgdir="+filepath.Join(dir, "pkg", build.Default.GOOS+"_"+build.Default.GOARCH),
   276  		"Java/...",
   277  	)
   278  	cmd.Env = append(os.Environ(), "GOPATH="+dir, "GO111MODULE=off")
   279  	if out, err := cmd.CombinedOutput(); err != nil {
   280  		t.Fatalf("failed to go install the generated Java wrappers: %v: %s", err, string(out))
   281  	}
   282  }
   283  
   284  func TestGenJava(t *testing.T) {
   285  	allTests := tests
   286  	if java.IsAvailable() {
   287  		allTests = append(append([]string{}, allTests...), javaTests...)
   288  	}
   289  	for _, filename := range allTests {
   290  		var pkg *types.Package
   291  		var file *ast.File
   292  		var buf bytes.Buffer
   293  		var cg *ClassGen
   294  		var classes []*java.Class
   295  		if filename != "" {
   296  			refs := fileRefs(t, filename, "Java/")
   297  			imp := &java.Importer{}
   298  			var err error
   299  			classes, err = imp.Import(refs)
   300  			if err != nil {
   301  				t.Fatal(err)
   302  			}
   303  			tmpGopath := ""
   304  			if len(classes) > 0 {
   305  				tmpGopath, err = ioutil.TempDir(os.TempDir(), "gomobile-bind-test-")
   306  				if err != nil {
   307  					t.Fatal(err)
   308  				}
   309  				defer os.RemoveAll(tmpGopath)
   310  				cg = &ClassGen{
   311  					Printer: &Printer{
   312  						IndentEach: []byte("\t"),
   313  						Buf:        new(bytes.Buffer),
   314  					},
   315  				}
   316  				cg.Init(classes, refs.Embedders)
   317  				genJavaPackages(t, tmpGopath, cg)
   318  				cg.Buf = &buf
   319  			}
   320  			pkg, file = typeCheck(t, filename, tmpGopath)
   321  		}
   322  		g := &JavaGen{
   323  			Generator: &Generator{
   324  				Printer: &Printer{Buf: &buf, IndentEach: []byte("    ")},
   325  				Fset:    fset,
   326  				Files:   []*ast.File{file},
   327  				Pkg:     pkg,
   328  			},
   329  		}
   330  		if pkg != nil {
   331  			g.AllPkg = []*types.Package{pkg}
   332  		}
   333  		g.Init(classes)
   334  		testCases := []struct {
   335  			suffix string
   336  			gen    func() error
   337  		}{
   338  			{
   339  				".java.golden",
   340  				func() error {
   341  					for i := range g.ClassNames() {
   342  						if err := g.GenClass(i); err != nil {
   343  							return err
   344  						}
   345  					}
   346  					return g.GenJava()
   347  				},
   348  			},
   349  			{
   350  				".java.c.golden",
   351  				func() error {
   352  					if cg != nil {
   353  						cg.GenC()
   354  					}
   355  					return g.GenC()
   356  				},
   357  			},
   358  			{
   359  				".java.h.golden",
   360  				func() error {
   361  					if cg != nil {
   362  						cg.GenH()
   363  					}
   364  					return g.GenH()
   365  				},
   366  			},
   367  		}
   368  
   369  		for _, tc := range testCases {
   370  			buf.Reset()
   371  			if err := tc.gen(); err != nil {
   372  				t.Errorf("%s: %v", filename, err)
   373  				continue
   374  			}
   375  			out := writeTempFile(t, "generated"+tc.suffix, buf.Bytes())
   376  			defer os.Remove(out)
   377  			var golden string
   378  			if filename != "" {
   379  				golden = filename[:len(filename)-len(".go")]
   380  			} else {
   381  				golden = "testdata/universe"
   382  			}
   383  			golden += tc.suffix
   384  			if diffstr := diff(golden, out); diffstr != "" {
   385  				t.Errorf("%s: does not match Java golden:\n%s", filename, diffstr)
   386  
   387  				if *updateFlag {
   388  					t.Logf("Updating %s...", golden)
   389  					if err := exec.Command("/bin/cp", out, golden).Run(); err != nil {
   390  						t.Errorf("Update failed: %s", err)
   391  					}
   392  				}
   393  
   394  			}
   395  		}
   396  	}
   397  }
   398  
   399  func TestGenGo(t *testing.T) {
   400  	for _, filename := range tests {
   401  		var buf bytes.Buffer
   402  		var pkg *types.Package
   403  		if filename != "" {
   404  			pkg, _ = typeCheck(t, filename, "")
   405  		}
   406  		testGenGo(t, filename, &buf, pkg)
   407  	}
   408  }
   409  
   410  func TestGenGoJavaWrappers(t *testing.T) {
   411  	if !java.IsAvailable() {
   412  		t.Skipf("java is not available")
   413  	}
   414  	for _, filename := range javaTests {
   415  		var buf bytes.Buffer
   416  		refs := fileRefs(t, filename, "Java/")
   417  		imp := &java.Importer{}
   418  		classes, err := imp.Import(refs)
   419  		if err != nil {
   420  			t.Fatal(err)
   421  		}
   422  		tmpGopath, err := ioutil.TempDir(os.TempDir(), "gomobile-bind-test-")
   423  		if err != nil {
   424  			t.Fatal(err)
   425  		}
   426  		defer os.RemoveAll(tmpGopath)
   427  		cg := &ClassGen{
   428  			Printer: &Printer{
   429  				IndentEach: []byte("\t"),
   430  				Buf:        &buf,
   431  			},
   432  		}
   433  		cg.Init(classes, refs.Embedders)
   434  		genJavaPackages(t, tmpGopath, cg)
   435  		pkg, _ := typeCheck(t, filename, tmpGopath)
   436  		cg.GenGo()
   437  		testGenGo(t, filename, &buf, pkg)
   438  	}
   439  }
   440  
   441  func TestGenGoObjcWrappers(t *testing.T) {
   442  	if runtime.GOOS != "darwin" {
   443  		t.Skipf("can only generate objc wrappers on darwin")
   444  	}
   445  	for _, filename := range objcTests {
   446  		var buf bytes.Buffer
   447  		refs := fileRefs(t, filename, "ObjC/")
   448  		types, err := objc.Import(refs)
   449  		if err != nil {
   450  			t.Fatal(err)
   451  		}
   452  		tmpGopath, err := ioutil.TempDir(os.TempDir(), "gomobile-bind-test-")
   453  		if err != nil {
   454  			t.Fatal(err)
   455  		}
   456  		defer os.RemoveAll(tmpGopath)
   457  		cg := &ObjcWrapper{
   458  			Printer: &Printer{
   459  				IndentEach: []byte("\t"),
   460  				Buf:        &buf,
   461  			},
   462  		}
   463  		var genNames []string
   464  		for _, emb := range refs.Embedders {
   465  			genNames = append(genNames, emb.Name)
   466  		}
   467  		cg.Init(types, genNames)
   468  		genObjcPackages(t, tmpGopath, cg)
   469  		pkg, _ := typeCheck(t, filename, tmpGopath)
   470  		cg.GenGo()
   471  		testGenGo(t, filename, &buf, pkg)
   472  	}
   473  }
   474  
   475  func testGenGo(t *testing.T, filename string, buf *bytes.Buffer, pkg *types.Package) {
   476  	conf := &GeneratorConfig{
   477  		Writer: buf,
   478  		Fset:   fset,
   479  		Pkg:    pkg,
   480  	}
   481  	if pkg != nil {
   482  		conf.AllPkg = []*types.Package{pkg}
   483  	}
   484  	if err := GenGo(conf); err != nil {
   485  		t.Errorf("%s: %v", filename, err)
   486  		return
   487  	}
   488  	// TODO(hyangah): let GenGo format the generated go files.
   489  	out := writeTempFile(t, "go", gofmt(t, buf.Bytes()))
   490  	defer os.Remove(out)
   491  
   492  	golden := filename
   493  	if golden == "" {
   494  		golden = "testdata/universe"
   495  	}
   496  	golden += ".golden"
   497  
   498  	goldenContents, err := ioutil.ReadFile(golden)
   499  	if err != nil {
   500  		t.Fatalf("failed to read golden file: %v", err)
   501  	}
   502  
   503  	// format golden file using the current go version's formatting rule.
   504  	formattedGolden := writeTempFile(t, "go", gofmt(t, goldenContents))
   505  	defer os.Remove(formattedGolden)
   506  
   507  	if diffstr := diff(formattedGolden, out); diffstr != "" {
   508  		t.Errorf("%s: does not match Go golden:\n%s", filename, diffstr)
   509  
   510  		if *updateFlag {
   511  			t.Logf("Updating %s...", golden)
   512  			if err := exec.Command("/bin/cp", out, golden).Run(); err != nil {
   513  				t.Errorf("Update failed: %s", err)
   514  			}
   515  		}
   516  	}
   517  }
   518  
   519  // gofmt formats the collection of Go source files auto-generated by gobind.
   520  func gofmt(t *testing.T, src []byte) []byte {
   521  	t.Helper()
   522  	buf := &bytes.Buffer{}
   523  	mark := []byte(gobindPreamble)
   524  	for i, c := range bytes.Split(src, mark) {
   525  		if i == 0 {
   526  			buf.Write(c)
   527  			continue
   528  		}
   529  		tmp := append(mark, c...)
   530  		out, err := format.Source(tmp)
   531  		if err != nil {
   532  			t.Fatalf("failed to format Go file: error=%v\n----\n%s\n----", err, tmp)
   533  		}
   534  		if _, err := buf.Write(out); err != nil {
   535  			t.Fatalf("failed to write formatted file to buffer: %v", err)
   536  		}
   537  	}
   538  	return buf.Bytes()
   539  }
   540  
   541  func TestCustomPrefix(t *testing.T) {
   542  	const datafile = "testdata/customprefix.go"
   543  	pkg, file := typeCheck(t, datafile, "")
   544  
   545  	type testCase struct {
   546  		golden string
   547  		gen    func(w io.Writer) error
   548  	}
   549  	var buf bytes.Buffer
   550  	jg := &JavaGen{
   551  		JavaPkg: "com.example",
   552  		Generator: &Generator{
   553  			Printer: &Printer{Buf: &buf, IndentEach: []byte("    ")},
   554  			Fset:    fset,
   555  			AllPkg:  []*types.Package{pkg},
   556  			Files:   []*ast.File{file},
   557  			Pkg:     pkg,
   558  		},
   559  	}
   560  	jg.Init(nil)
   561  	testCases := []testCase{
   562  		{
   563  			"testdata/customprefix.java.golden",
   564  			func(w io.Writer) error {
   565  				buf.Reset()
   566  				for i := range jg.ClassNames() {
   567  					if err := jg.GenClass(i); err != nil {
   568  						return err
   569  					}
   570  				}
   571  				if err := jg.GenJava(); err != nil {
   572  					return err
   573  				}
   574  				_, err := io.Copy(w, &buf)
   575  				return err
   576  			},
   577  		},
   578  		{
   579  			"testdata/customprefix.java.h.golden",
   580  			func(w io.Writer) error {
   581  				buf.Reset()
   582  				if err := jg.GenH(); err != nil {
   583  					return err
   584  				}
   585  				_, err := io.Copy(w, &buf)
   586  				return err
   587  			},
   588  		},
   589  		{
   590  			"testdata/customprefix.java.c.golden",
   591  			func(w io.Writer) error {
   592  				buf.Reset()
   593  				if err := jg.GenC(); err != nil {
   594  					return err
   595  				}
   596  				_, err := io.Copy(w, &buf)
   597  				return err
   598  			},
   599  		},
   600  	}
   601  	for _, pref := range []string{"EX", ""} {
   602  		og := &ObjcGen{
   603  			Prefix: pref,
   604  			Generator: &Generator{
   605  				Printer: &Printer{Buf: &buf, IndentEach: []byte("    ")},
   606  				Fset:    fset,
   607  				AllPkg:  []*types.Package{pkg},
   608  				Pkg:     pkg,
   609  			},
   610  		}
   611  		og.Init(nil)
   612  		testCases = append(testCases, []testCase{
   613  			{
   614  				"testdata/customprefix" + pref + ".objc.go.h.golden",
   615  				func(w io.Writer) error {
   616  					buf.Reset()
   617  					if err := og.GenGoH(); err != nil {
   618  						return err
   619  					}
   620  					_, err := io.Copy(w, &buf)
   621  					return err
   622  				},
   623  			},
   624  			{
   625  				"testdata/customprefix" + pref + ".objc.h.golden",
   626  				func(w io.Writer) error {
   627  					buf.Reset()
   628  					if err := og.GenH(); err != nil {
   629  						return err
   630  					}
   631  					_, err := io.Copy(w, &buf)
   632  					return err
   633  				},
   634  			},
   635  			{
   636  				"testdata/customprefix" + pref + ".objc.m.golden",
   637  				func(w io.Writer) error {
   638  					buf.Reset()
   639  					if err := og.GenM(); err != nil {
   640  						return err
   641  					}
   642  					_, err := io.Copy(w, &buf)
   643  					return err
   644  				},
   645  			},
   646  		}...)
   647  	}
   648  
   649  	for _, tc := range testCases {
   650  		var buf bytes.Buffer
   651  		if err := tc.gen(&buf); err != nil {
   652  			t.Errorf("generating %s: %v", tc.golden, err)
   653  			continue
   654  		}
   655  		out := writeTempFile(t, "generated", buf.Bytes())
   656  		defer os.Remove(out)
   657  		if diffstr := diff(tc.golden, out); diffstr != "" {
   658  			t.Errorf("%s: generated file does not match:\b%s", tc.golden, diffstr)
   659  			if *updateFlag {
   660  				t.Logf("Updating %s...", tc.golden)
   661  				err := exec.Command("/bin/cp", out, tc.golden).Run()
   662  				if err != nil {
   663  					t.Errorf("Update failed: %s", err)
   664  				}
   665  			}
   666  		}
   667  	}
   668  }
   669  
   670  func TestLowerFirst(t *testing.T) {
   671  	testCases := []struct {
   672  		in, want string
   673  	}{
   674  		{"", ""},
   675  		{"Hello", "hello"},
   676  		{"HelloGopher", "helloGopher"},
   677  		{"hello", "hello"},
   678  		{"ID", "id"},
   679  		{"IDOrName", "idOrName"},
   680  		{"ΓειαΣας", "γειαΣας"},
   681  	}
   682  
   683  	for _, tc := range testCases {
   684  		if got := lowerFirst(tc.in); got != tc.want {
   685  			t.Errorf("lowerFirst(%q) = %q; want %q", tc.in, got, tc.want)
   686  		}
   687  	}
   688  }
   689  
   690  // Test that typeName work for anonymous qualified fields.
   691  func TestSelectorExprTypeName(t *testing.T) {
   692  	e, err := parser.ParseExprFrom(fset, "", "struct { bytes.Buffer }", 0)
   693  	if err != nil {
   694  		t.Fatal(err)
   695  	}
   696  	ft := e.(*ast.StructType).Fields.List[0].Type
   697  	if got, want := typeName(ft), "Buffer"; got != want {
   698  		t.Errorf("got: %q; want %q", got, want)
   699  	}
   700  }