| // 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" |
| "internal/testenv" |
| "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 |
| } |
| // don't descend into testdata directories |
| if isGoFile(d) && !strings.Contains(filepath.ToSlash(filename), "/testdata/") { |
| 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 |
| goroot := *root |
| if goroot == "" { |
| goroot = testenv.GOROOT(t) |
| } |
| filepath.WalkDir(goroot, 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 } |