diff options
Diffstat (limited to 'src/pkg/go/printer/printer_test.go')
-rw-r--r-- | src/pkg/go/printer/printer_test.go | 242 |
1 files changed, 230 insertions, 12 deletions
diff --git a/src/pkg/go/printer/printer_test.go b/src/pkg/go/printer/printer_test.go index ff2d906b5..497d671f2 100644 --- a/src/pkg/go/printer/printer_test.go +++ b/src/pkg/go/printer/printer_test.go @@ -7,10 +7,10 @@ package printer import ( "bytes" "flag" - "io/ioutil" "go/ast" "go/parser" "go/token" + "io/ioutil" "path/filepath" "testing" "time" @@ -62,11 +62,18 @@ func runcheck(t *testing.T, source, golden string, mode checkMode) { // format source var buf bytes.Buffer - if _, err := cfg.Fprint(&buf, fset, prog); err != nil { + if err := cfg.Fprint(&buf, fset, prog); err != nil { t.Error(err) } res := buf.Bytes() + // formatted source must be valid + if _, err := parser.ParseFile(fset, "", res, 0); err != nil { + t.Error(err) + t.Logf("\n%s", res) + return + } + // update golden files if necessary if *update { if err := ioutil.WriteFile(golden, res, 0644); err != nil { @@ -107,7 +114,7 @@ func check(t *testing.T, source, golden string, mode checkMode) { // start a timer to produce a time-out signal tc := make(chan int) go func() { - time.Sleep(10e9) // plenty of a safety margin, even for very slow machines + time.Sleep(10 * time.Second) // plenty of a safety margin, even for very slow machines tc <- 0 }() @@ -133,7 +140,7 @@ type entry struct { mode checkMode } -// Use gotest -update to create/update the respective golden files. +// Use go test -update to create/update the respective golden files. var data = []entry{ {"empty.input", "empty.golden", 0}, {"comments.input", "comments.golden", 0}, @@ -147,15 +154,12 @@ var data = []entry{ } func TestFiles(t *testing.T) { - for i, e := range data { + for _, e := range data { source := filepath.Join(dataDir, e.source) golden := filepath.Join(dataDir, e.golden) check(t, source, golden, e.mode) // TODO(gri) check that golden is idempotent //check(t, golden, golden, e.mode) - if testing.Short() && i >= 3 { - break - } } } @@ -171,14 +175,14 @@ func TestLineComments(t *testing.T) { ` fset := token.NewFileSet() - ast1, err1 := parser.ParseFile(fset, "", src, parser.ParseComments) - if err1 != nil { - panic(err1) + f, err := parser.ParseFile(fset, "", src, parser.ParseComments) + if err != nil { + panic(err) // error in test } var buf bytes.Buffer fset = token.NewFileSet() // use the wrong file set - Fprint(&buf, fset, ast1) + Fprint(&buf, fset, f) nlines := 0 for _, ch := range buf.Bytes() { @@ -190,5 +194,219 @@ func TestLineComments(t *testing.T) { const expected = 3 if nlines < expected { t.Errorf("got %d, expected %d\n", nlines, expected) + t.Errorf("result:\n%s", buf.Bytes()) + } +} + +// Verify that the printer can be invoked during initialization. +func init() { + const name = "foobar" + var buf bytes.Buffer + if err := Fprint(&buf, fset, &ast.Ident{Name: name}); err != nil { + panic(err) // error in test + } + // in debug mode, the result contains additional information; + // ignore it + if s := buf.String(); !debug && s != name { + panic("got " + s + ", want " + name) + } +} + +// Verify that the printer doesn't crash if the AST contains BadXXX nodes. +func TestBadNodes(t *testing.T) { + const src = "package p\n(" + const res = "package p\nBadDecl\n" + f, err := parser.ParseFile(fset, "", src, parser.ParseComments) + if err == nil { + t.Error("expected illegal program") // error in test + } + var buf bytes.Buffer + Fprint(&buf, fset, f) + if buf.String() != res { + t.Errorf("got %q, expected %q", buf.String(), res) + } +} + +// testComment verifies that f can be parsed again after printing it +// with its first comment set to comment at any possible source offset. +func testComment(t *testing.T, f *ast.File, srclen int, comment *ast.Comment) { + f.Comments[0].List[0] = comment + var buf bytes.Buffer + for offs := 0; offs <= srclen; offs++ { + buf.Reset() + // Printing f should result in a correct program no + // matter what the (incorrect) comment position is. + if err := Fprint(&buf, fset, f); err != nil { + t.Error(err) + } + if _, err := parser.ParseFile(fset, "", buf.Bytes(), 0); err != nil { + t.Fatalf("incorrect program for pos = %d:\n%s", comment.Slash, buf.String()) + } + // Position information is just an offset. + // Move comment one byte down in the source. + comment.Slash++ + } +} + +// Verify that the printer produces always produces a correct program +// even if the position information of comments introducing newlines +// is incorrect. +func TestBadComments(t *testing.T) { + const src = ` +// first comment - text and position changed by test +package p +import "fmt" +const pi = 3.14 // rough circle +var ( + x, y, z int = 1, 2, 3 + u, v float64 +) +func fibo(n int) { + if n < 2 { + return n /* seed values */ + } + return fibo(n-1) + fibo(n-2) +} +` + + f, err := parser.ParseFile(fset, "", src, parser.ParseComments) + if err != nil { + t.Error(err) // error in test + } + + comment := f.Comments[0].List[0] + pos := comment.Pos() + if fset.Position(pos).Offset != 1 { + t.Error("expected offset 1") // error in test + } + + testComment(t, f, len(src), &ast.Comment{Slash: pos, Text: "//-style comment"}) + testComment(t, f, len(src), &ast.Comment{Slash: pos, Text: "/*-style comment */"}) + testComment(t, f, len(src), &ast.Comment{Slash: pos, Text: "/*-style \n comment */"}) + testComment(t, f, len(src), &ast.Comment{Slash: pos, Text: "/*-style comment \n\n\n */"}) +} + +type visitor chan *ast.Ident + +func (v visitor) Visit(n ast.Node) (w ast.Visitor) { + if ident, ok := n.(*ast.Ident); ok { + v <- ident + } + return v +} + +// idents is an iterator that returns all idents in f via the result channel. +func idents(f *ast.File) <-chan *ast.Ident { + v := make(visitor) + go func() { + ast.Walk(v, f) + close(v) + }() + return v +} + +// identCount returns the number of identifiers found in f. +func identCount(f *ast.File) int { + n := 0 + for _ = range idents(f) { + n++ } + return n +} + +// Verify that the SourcePos mode emits correct //line comments +// by testing that position information for matching identifiers +// is maintained. +func TestSourcePos(t *testing.T) { + const src = ` +package p +import ( "go/printer"; "math" ) +const pi = 3.14; var x = 0 +type t struct{ x, y, z int; u, v, w float32 } +func (t *t) foo(a, b, c int) int { + return a*t.x + b*t.y + + // two extra lines here + // ... + c*t.z +} +` + + // parse original + f1, err := parser.ParseFile(fset, "src", src, parser.ParseComments) + if err != nil { + t.Fatal(err) + } + + // pretty-print original + var buf bytes.Buffer + err = (&Config{Mode: UseSpaces | SourcePos, Tabwidth: 8}).Fprint(&buf, fset, f1) + if err != nil { + t.Fatal(err) + } + + // parse pretty printed original + // (//line comments must be interpreted even w/o parser.ParseComments set) + f2, err := parser.ParseFile(fset, "", buf.Bytes(), 0) + if err != nil { + t.Fatalf("%s\n%s", err, buf.Bytes()) + } + + // At this point the position information of identifiers in f2 should + // match the position information of corresponding identifiers in f1. + + // number of identifiers must be > 0 (test should run) and must match + n1 := identCount(f1) + n2 := identCount(f2) + if n1 == 0 { + t.Fatal("got no idents") + } + if n2 != n1 { + t.Errorf("got %d idents; want %d", n2, n1) + } + + // verify that all identifiers have correct line information + i2range := idents(f2) + for i1 := range idents(f1) { + i2 := <-i2range + + if i2.Name != i1.Name { + t.Errorf("got ident %s; want %s", i2.Name, i1.Name) + } + + l1 := fset.Position(i1.Pos()).Line + l2 := fset.Position(i2.Pos()).Line + if l2 != l1 { + t.Errorf("got line %d; want %d for %s", l2, l1, i1.Name) + } + } + + if t.Failed() { + t.Logf("\n%s", buf.Bytes()) + } +} + +// TextX is a skeleton test that can be filled in for debugging one-off cases. +// Do not remove. +func TestX(t *testing.T) { + const src = ` +package p +func _() {} +` + // parse original + f, err := parser.ParseFile(fset, "src", src, parser.ParseComments) + if err != nil { + t.Fatal(err) + } + + // pretty-print original + var buf bytes.Buffer + if err = (&Config{Mode: UseSpaces, Tabwidth: 8}).Fprint(&buf, fset, f); err != nil { + t.Fatal(err) + } + + // parse pretty printed original + if _, err := parser.ParseFile(fset, "", buf.Bytes(), 0); err != nil { + t.Fatalf("%s\n%s", err, buf.Bytes()) + } + } |