// 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 }