diff options
Diffstat (limited to 'src/cmd/gofmt/long_test.go')
-rw-r--r-- | src/cmd/gofmt/long_test.go | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/src/cmd/gofmt/long_test.go b/src/cmd/gofmt/long_test.go new file mode 100644 index 0000000..4a82170 --- /dev/null +++ b/src/cmd/gofmt/long_test.go @@ -0,0 +1,175 @@ +// Copyright 2011 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. + +// This test applies gofmt to all Go files under -root. +// To test specific files provide a list of comma-separated +// filenames via the -files flag: go test -files=gofmt.go . + +package main + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/printer" + "go/token" + "io" + "io/fs" + "os" + "path/filepath" + "runtime" + "strings" + "testing" +) + +var ( + root = flag.String("root", runtime.GOROOT(), "test root directory") + files = flag.String("files", "", "comma-separated list of files to test") + ngo = flag.Int("n", runtime.NumCPU(), "number of goroutines used") + verbose = flag.Bool("verbose", false, "verbose mode") + nfiles int // number of files processed +) + +func gofmt(fset *token.FileSet, filename string, src *bytes.Buffer) error { + f, _, _, err := parse(fset, filename, src.Bytes(), false) + if err != nil { + return err + } + ast.SortImports(fset, f) + src.Reset() + return (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(src, fset, f) +} + +func testFile(t *testing.T, b1, b2 *bytes.Buffer, filename string) { + // open file + f, err := os.Open(filename) + if err != nil { + t.Error(err) + return + } + + // read file + b1.Reset() + _, err = io.Copy(b1, f) + f.Close() + if err != nil { + t.Error(err) + return + } + + // exclude files w/ syntax errors (typically test cases) + fset := token.NewFileSet() + if _, _, _, err = parse(fset, filename, b1.Bytes(), false); err != nil { + if *verbose { + fmt.Fprintf(os.Stderr, "ignoring %s\n", err) + } + return + } + + // gofmt file + if err = gofmt(fset, filename, b1); err != nil { + t.Errorf("1st gofmt failed: %v", err) + return + } + + // make a copy of the result + b2.Reset() + b2.Write(b1.Bytes()) + + // gofmt result again + if err = gofmt(fset, filename, b2); err != nil { + t.Errorf("2nd gofmt failed: %v", err) + return + } + + // the first and 2nd result should be identical + if !bytes.Equal(b1.Bytes(), b2.Bytes()) { + // A known instance of gofmt not being idempotent + // (see Issue #24472) + if strings.HasSuffix(filename, "issue22662.go") { + t.Log("known gofmt idempotency bug (Issue #24472)") + return + } + t.Errorf("gofmt %s not idempotent", filename) + } +} + +func testFiles(t *testing.T, filenames <-chan string, done chan<- int) { + b1 := new(bytes.Buffer) + b2 := new(bytes.Buffer) + for filename := range filenames { + testFile(t, b1, b2, filename) + } + done <- 0 +} + +func genFilenames(t *testing.T, filenames chan<- string) { + defer close(filenames) + + handleFile := func(filename string, d fs.DirEntry, err error) error { + if err != nil { + t.Error(err) + return nil + } + if isGoFile(d) { + filenames <- filename + nfiles++ + } + return nil + } + + // test Go files provided via -files, if any + if *files != "" { + for _, filename := range strings.Split(*files, ",") { + fi, err := os.Stat(filename) + handleFile(filename, &statDirEntry{fi}, err) + } + return // ignore files under -root + } + + // otherwise, test all Go files under *root + filepath.WalkDir(*root, handleFile) +} + +func TestAll(t *testing.T) { + if testing.Short() { + return + } + + if *ngo < 1 { + *ngo = 1 // make sure test is run + } + if *verbose { + fmt.Printf("running test using %d goroutines\n", *ngo) + } + + // generate filenames + filenames := make(chan string, 32) + go genFilenames(t, filenames) + + // launch test goroutines + done := make(chan int) + for i := 0; i < *ngo; i++ { + go testFiles(t, filenames, done) + } + + // wait for all test goroutines to complete + for i := 0; i < *ngo; i++ { + <-done + } + + if *verbose { + fmt.Printf("processed %d files\n", nfiles) + } +} + +type statDirEntry struct { + info fs.FileInfo +} + +func (d *statDirEntry) Name() string { return d.info.Name() } +func (d *statDirEntry) IsDir() bool { return d.info.IsDir() } +func (d *statDirEntry) Type() fs.FileMode { return d.info.Mode().Type() } +func (d *statDirEntry) Info() (fs.FileInfo, error) { return d.info, nil } |