Home | History | Annotate | Download | only in gofmt
      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