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