bind_test.go 16 KB

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