| // Copyright 2012 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package format |
| |
| import ( |
| "bytes" |
| "go/ast" |
| "go/parser" |
| "go/token" |
| "os" |
| "strings" |
| "testing" |
| ) |
| |
| const testfile = "format_test.go" |
| |
| func diff(t *testing.T, dst, src []byte) { |
| line := 1 |
| offs := 0 // line offset |
| for i := 0; i < len(dst) && i < len(src); i++ { |
| d := dst[i] |
| s := src[i] |
| if d != s { |
| t.Errorf("dst:%d: %s\n", line, dst[offs:i+1]) |
| t.Errorf("src:%d: %s\n", line, src[offs:i+1]) |
| return |
| } |
| if s == '\n' { |
| line++ |
| offs = i + 1 |
| } |
| } |
| if len(dst) != len(src) { |
| t.Errorf("len(dst) = %d, len(src) = %d\nsrc = %q", len(dst), len(src), src) |
| } |
| } |
| |
| func TestNode(t *testing.T) { |
| src, err := os.ReadFile(testfile) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| fset := token.NewFileSet() |
| file, err := parser.ParseFile(fset, testfile, src, parser.ParseComments) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| var buf bytes.Buffer |
| |
| if err = Node(&buf, fset, file); err != nil { |
| t.Fatal("Node failed:", err) |
| } |
| |
| diff(t, buf.Bytes(), src) |
| } |
| |
| // Node is documented to not modify the AST. |
| // Test that it is so even when numbers are normalized. |
| func TestNodeNoModify(t *testing.T) { |
| const ( |
| src = "package p\n\nconst _ = 0000000123i\n" |
| golden = "package p\n\nconst _ = 123i\n" |
| ) |
| |
| fset := token.NewFileSet() |
| file, err := parser.ParseFile(fset, "", src, parser.ParseComments) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| // Capture original address and value of a BasicLit node |
| // which will undergo formatting changes during printing. |
| wantLit := file.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec).Values[0].(*ast.BasicLit) |
| wantVal := wantLit.Value |
| |
| var buf bytes.Buffer |
| if err = Node(&buf, fset, file); err != nil { |
| t.Fatal("Node failed:", err) |
| } |
| diff(t, buf.Bytes(), []byte(golden)) |
| |
| // Check if anything changed after Node returned. |
| gotLit := file.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec).Values[0].(*ast.BasicLit) |
| gotVal := gotLit.Value |
| |
| if gotLit != wantLit { |
| t.Errorf("got *ast.BasicLit address %p, want %p", gotLit, wantLit) |
| } |
| if gotVal != wantVal { |
| t.Errorf("got *ast.BasicLit value %q, want %q", gotVal, wantVal) |
| } |
| } |
| |
| func TestSource(t *testing.T) { |
| src, err := os.ReadFile(testfile) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| res, err := Source(src) |
| if err != nil { |
| t.Fatal("Source failed:", err) |
| } |
| |
| diff(t, res, src) |
| } |
| |
| // Test cases that are expected to fail are marked by the prefix "ERROR". |
| // The formatted result must look the same as the input for successful tests. |
| var tests = []string{ |
| // declaration lists |
| `import "go/format"`, |
| "var x int", |
| "var x int\n\ntype T struct{}", |
| |
| // statement lists |
| "x := 0", |
| "f(a, b, c)\nvar x int = f(1, 2, 3)", |
| |
| // indentation, leading and trailing space |
| "\tx := 0\n\tgo f()", |
| "\tx := 0\n\tgo f()\n\n\n", |
| "\n\t\t\n\n\tx := 0\n\tgo f()\n\n\n", |
| "\n\t\t\n\n\t\t\tx := 0\n\t\t\tgo f()\n\n\n", |
| "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation added inside raw strings |
| "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\n\t\tfoo\n`\n\n\n", // no indentation removed inside raw strings |
| |
| // comments |
| "/* Comment */", |
| "\t/* Comment */ ", |
| "\n/* Comment */ ", |
| "i := 5 /* Comment */", // issue #5551 |
| "\ta()\n//line :1", // issue #11276 |
| "\t//xxx\n\ta()\n//line :2", // issue #11276 |
| "\ta() //line :1\n\tb()\n", // issue #11276 |
| "x := 0\n//line :1\n//line :2", // issue #11276 |
| |
| // whitespace |
| "", // issue #11275 |
| " ", // issue #11275 |
| "\t", // issue #11275 |
| "\t\t", // issue #11275 |
| "\n", // issue #11275 |
| "\n\n", // issue #11275 |
| "\t\n", // issue #11275 |
| |
| // erroneous programs |
| "ERROR1 + 2 +", |
| "ERRORx := 0", |
| |
| // build comments |
| "// copyright\n\n//go:build x\n\npackage p\n", |
| "// copyright\n\n//go:build x\n// +build x\n\npackage p\n", |
| } |
| |
| func String(s string) (string, error) { |
| res, err := Source([]byte(s)) |
| if err != nil { |
| return "", err |
| } |
| return string(res), nil |
| } |
| |
| func TestPartial(t *testing.T) { |
| for _, src := range tests { |
| if strings.HasPrefix(src, "ERROR") { |
| // test expected to fail |
| src = src[5:] // remove ERROR prefix |
| res, err := String(src) |
| if err == nil && res == src { |
| t.Errorf("formatting succeeded but was expected to fail:\n%q", src) |
| } |
| } else { |
| // test expected to succeed |
| res, err := String(src) |
| if err != nil { |
| t.Errorf("formatting failed (%s):\n%q", err, src) |
| } else if res != src { |
| t.Errorf("formatting incorrect:\nsource: %q\nresult: %q", src, res) |
| } |
| } |
| } |
| } |