1// Copyright 2011 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// This test applies gofmt to all Go files under -root. 6// To test specific files provide a list of comma-separated 7// filenames via the -files flag: go test -files=gofmt.go . 8 9package main 10 11import ( 12 "bytes" 13 "flag" 14 "fmt" 15 "go/ast" 16 "go/printer" 17 "go/token" 18 "internal/testenv" 19 "io" 20 "io/fs" 21 "os" 22 "path/filepath" 23 "runtime" 24 "strings" 25 "testing" 26) 27 28var ( 29 root = flag.String("root", runtime.GOROOT(), "test root directory") 30 files = flag.String("files", "", "comma-separated list of files to test") 31 ngo = flag.Int("n", runtime.NumCPU(), "number of goroutines used") 32 verbose = flag.Bool("verbose", false, "verbose mode") 33 nfiles int // number of files processed 34) 35 36func gofmt(fset *token.FileSet, filename string, src *bytes.Buffer) error { 37 f, _, _, err := parse(fset, filename, src.Bytes(), false) 38 if err != nil { 39 return err 40 } 41 ast.SortImports(fset, f) 42 src.Reset() 43 return (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(src, fset, f) 44} 45 46func testFile(t *testing.T, b1, b2 *bytes.Buffer, filename string) { 47 // open file 48 f, err := os.Open(filename) 49 if err != nil { 50 t.Error(err) 51 return 52 } 53 54 // read file 55 b1.Reset() 56 _, err = io.Copy(b1, f) 57 f.Close() 58 if err != nil { 59 t.Error(err) 60 return 61 } 62 63 // exclude files w/ syntax errors (typically test cases) 64 fset := token.NewFileSet() 65 if _, _, _, err = parse(fset, filename, b1.Bytes(), false); err != nil { 66 if *verbose { 67 fmt.Fprintf(os.Stderr, "ignoring %s\n", err) 68 } 69 return 70 } 71 72 // gofmt file 73 if err = gofmt(fset, filename, b1); err != nil { 74 t.Errorf("1st gofmt failed: %v", err) 75 return 76 } 77 78 // make a copy of the result 79 b2.Reset() 80 b2.Write(b1.Bytes()) 81 82 // gofmt result again 83 if err = gofmt(fset, filename, b2); err != nil { 84 t.Errorf("2nd gofmt failed: %v", err) 85 return 86 } 87 88 // the first and 2nd result should be identical 89 if !bytes.Equal(b1.Bytes(), b2.Bytes()) { 90 // A known instance of gofmt not being idempotent 91 // (see Issue #24472) 92 if strings.HasSuffix(filename, "issue22662.go") { 93 t.Log("known gofmt idempotency bug (Issue #24472)") 94 return 95 } 96 t.Errorf("gofmt %s not idempotent", filename) 97 } 98} 99 100func testFiles(t *testing.T, filenames <-chan string, done chan<- int) { 101 b1 := new(bytes.Buffer) 102 b2 := new(bytes.Buffer) 103 for filename := range filenames { 104 testFile(t, b1, b2, filename) 105 } 106 done <- 0 107} 108 109func genFilenames(t *testing.T, filenames chan<- string) { 110 defer close(filenames) 111 112 handleFile := func(filename string, d fs.DirEntry, err error) error { 113 if err != nil { 114 t.Error(err) 115 return nil 116 } 117 // don't descend into testdata directories 118 if isGoFile(d) && !strings.Contains(filepath.ToSlash(filename), "/testdata/") { 119 filenames <- filename 120 nfiles++ 121 } 122 return nil 123 } 124 125 // test Go files provided via -files, if any 126 if *files != "" { 127 for _, filename := range strings.Split(*files, ",") { 128 fi, err := os.Stat(filename) 129 handleFile(filename, fs.FileInfoToDirEntry(fi), err) 130 } 131 return // ignore files under -root 132 } 133 134 // otherwise, test all Go files under *root 135 goroot := *root 136 if goroot == "" { 137 goroot = testenv.GOROOT(t) 138 } 139 filepath.WalkDir(goroot, handleFile) 140} 141 142func TestAll(t *testing.T) { 143 if testing.Short() { 144 return 145 } 146 147 if *ngo < 1 { 148 *ngo = 1 // make sure test is run 149 } 150 if *verbose { 151 fmt.Printf("running test using %d goroutines\n", *ngo) 152 } 153 154 // generate filenames 155 filenames := make(chan string, 32) 156 go genFilenames(t, filenames) 157 158 // launch test goroutines 159 done := make(chan int) 160 for i := 0; i < *ngo; i++ { 161 go testFiles(t, filenames, done) 162 } 163 164 // wait for all test goroutines to complete 165 for i := 0; i < *ngo; i++ { 166 <-done 167 } 168 169 if *verbose { 170 fmt.Printf("processed %d files\n", nfiles) 171 } 172} 173