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