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