26
28
var fset = token.NewFileSet()
28
func lineString(text []byte, i int) string {
30
for i < len(text) && text[i] != '\n' {
33
return string(text[i0:i])
36
30
type checkMode uint
39
33
export checkMode = 1 << iota
43
func runcheck(t *testing.T, source, golden string, mode checkMode) {
45
prog, err := parser.ParseFile(fset, source, nil, parser.ParseComments)
38
// format parses src, prints the corresponding AST, verifies the resulting
39
// src is syntactically correct, and returns the resulting src or an error
41
func format(src []byte, mode checkMode) ([]byte, error) {
43
f, err := parser.ParseFile(fset, "", src, parser.ParseComments)
45
return nil, fmt.Errorf("parse: %s\n%s", err, src)
51
48
// filter exports if necessary
52
49
if mode&export != 0 {
53
ast.FileExports(prog) // ignore result
54
prog.Comments = nil // don't print comments that are not in AST
50
ast.FileExports(f) // ignore result
51
f.Comments = nil // don't print comments that are not in AST
57
54
// determine printer configuration
60
57
cfg.Mode |= RawFormat
64
61
var buf bytes.Buffer
65
if err := cfg.Fprint(&buf, fset, prog); err != nil {
62
if err := cfg.Fprint(&buf, fset, f); err != nil {
63
return nil, fmt.Errorf("print: %s", err)
66
// make sure formated output is syntactically correct
70
// formatted source must be valid
71
68
if _, err := parser.ParseFile(fset, "", res, 0); err != nil {
69
return nil, fmt.Errorf("re-parse: %s\n%s", err, buf.Bytes())
75
// lineAt returns the line in text starting at offset offs.
76
func lineAt(text []byte, offs int) []byte {
78
for i < len(text) && text[i] != '\n' {
84
// diff compares a and b.
85
func diff(aname, bname string, a, b []byte) error {
86
var buf bytes.Buffer // holding long error message
90
fmt.Fprintf(&buf, "\nlength changed: len(%s) = %d, len(%s) = %d", aname, len(a), bname, len(b))
96
for i := 0; i < len(a) && i < len(b); i++ {
99
fmt.Fprintf(&buf, "\n%s:%d:%d: %s", aname, line, i-offs+1, lineAt(a, offs))
100
fmt.Fprintf(&buf, "\n%s:%d:%d: %s", bname, line, i-offs+1, lineAt(b, offs))
101
fmt.Fprintf(&buf, "\n\n")
111
return errors.New(buf.String())
116
func runcheck(t *testing.T, source, golden string, mode checkMode) {
117
src, err := ioutil.ReadFile(source)
123
res, err := format(src, mode)
93
if len(res) != len(gld) {
94
t.Errorf("len = %d, expected %d (= len(%s))", len(res), len(gld), golden)
144
// formatted source and golden must be the same
145
if err := diff(source, golden, res, gld); err != nil {
98
for i, line, offs := 0, 1, 0; i < len(res) && i < len(gld); i++ {
101
t.Errorf("%s:%d:%d: %s", source, line, i-offs+1, lineString(res, offs))
102
t.Errorf("%s:%d:%d: %s", golden, line, i-offs+1, lineString(gld, offs))
150
if mode&idempotent != 0 {
151
// formatting golden must be idempotent
152
// (This is very difficult to achieve in general and for now
153
// it is only checked for files explicitly marked as such.)
154
res, err = format(gld, mode)
155
if err := diff(golden, fmt.Sprintf("format(%s)", golden), gld, res); err != nil {
156
t.Errorf("golden is not idempotent: %s", err)
143
191
// Use go test -update to create/update the respective golden files.
144
192
var data = []entry{
145
{"empty.input", "empty.golden", 0},
193
{"empty.input", "empty.golden", idempotent},
146
194
{"comments.input", "comments.golden", 0},
147
195
{"comments.input", "comments.x", export},
148
{"linebreaks.input", "linebreaks.golden", 0},
149
{"expressions.input", "expressions.golden", 0},
150
{"expressions.input", "expressions.raw", rawFormat},
196
{"comments2.input", "comments2.golden", idempotent},
197
{"linebreaks.input", "linebreaks.golden", idempotent},
198
{"expressions.input", "expressions.golden", idempotent},
199
{"expressions.input", "expressions.raw", rawFormat | idempotent},
151
200
{"declarations.input", "declarations.golden", 0},
152
201
{"statements.input", "statements.golden", 0},
153
{"slow.input", "slow.golden", 0},
202
{"slow.input", "slow.golden", idempotent},
156
205
func TestFiles(t *testing.T) {
437
var decls = []string{
439
"const pi = 3.1415\nconst e = 2.71828\n\nvar x = pi",
440
"func sum(x, y int) int\t{ return x + y }",
443
func TestDeclLists(t *testing.T) {
444
for _, src := range decls {
445
file, err := parser.ParseFile(fset, "", "package p;"+src, parser.ParseComments)
447
panic(err) // error in test
451
err = Fprint(&buf, fset, file.Decls) // only print declarations
453
panic(err) // error in test
458
t.Errorf("\ngot : %q\nwant: %q\n", out, src)
463
var stmts = []string{
465
"select {}\nvar a, b = 1, 2\nreturn a + b",
466
"go f()\ndefer func() {}()",
469
func TestStmtLists(t *testing.T) {
470
for _, src := range stmts {
471
file, err := parser.ParseFile(fset, "", "package p; func _() {"+src+"}", parser.ParseComments)
473
panic(err) // error in test
477
err = Fprint(&buf, fset, file.Decls[0].(*ast.FuncDecl).Body.List) // only print statements
479
panic(err) // error in test
484
t.Errorf("\ngot : %q\nwant: %q\n", out, src)
489
func TestBaseIndent(t *testing.T) {
490
// The testfile must not contain multi-line raw strings since those
491
// are not indented (because their values must not change) and make
493
const filename = "printer.go"
494
src, err := ioutil.ReadFile(filename)
496
panic(err) // error in test
499
file, err := parser.ParseFile(fset, filename, src, 0)
501
panic(err) // error in test
505
for indent := 0; indent < 4; indent++ {
507
(&Config{Tabwidth: tabwidth, Indent: indent}).Fprint(&buf, fset, file)
508
// all code must be indented by at least 'indent' tabs
509
lines := bytes.Split(buf.Bytes(), []byte{'\n'})
510
for i, line := range lines {
512
continue // empty lines don't have indentation
515
for j, b := range line {
517
// end of indentation
523
t.Errorf("line %d: got only %d tabs; want at least %d: %q", i, n, indent, line)
529
// TestFuncType tests that an ast.FuncType with a nil Params field
530
// can be printed (per go/ast specification). Test case for issue 3870.
531
func TestFuncType(t *testing.T) {
533
Name: &ast.Ident{Name: "p"},
536
Name: &ast.Ident{Name: "f"},
537
Type: &ast.FuncType{},
543
if err := Fprint(&buf, fset, src); err != nil {
548
const want = `package p
554
t.Fatalf("got:\n%s\nwant:\n%s\n", got, want)
388
558
// TextX is a skeleton test that can be filled in for debugging one-off cases.
389
559
// Do not remove.
390
560
func TestX(t *testing.T) {
396
f, err := parser.ParseFile(fset, "src", src, parser.ParseComments)
565
_, err := format([]byte(src), 0)
401
// pretty-print original
403
if err = (&Config{Mode: UseSpaces, Tabwidth: 8}).Fprint(&buf, fset, f); err != nil {
407
// parse pretty printed original
408
if _, err := parser.ParseFile(fset, "", buf.Bytes(), 0); err != nil {
409
t.Fatalf("%s\n%s", err, buf.Bytes())