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 	"io"
     19 	"os"
     20 	"path/filepath"
     21 	"runtime"
     22 	"strings"
     23 	"testing"
     24 )
     25 
     26 var (
     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 
     34 func 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 
     44 func 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 		t.Errorf("gofmt %s not idempotent", filename)
     89 	}
     90 }
     91 
     92 func testFiles(t *testing.T, filenames <-chan string, done chan<- int) {
     93 	b1 := new(bytes.Buffer)
     94 	b2 := new(bytes.Buffer)
     95 	for filename := range filenames {
     96 		testFile(t, b1, b2, filename)
     97 	}
     98 	done <- 0
     99 }
    100 
    101 func genFilenames(t *testing.T, filenames chan<- string) {
    102 	defer close(filenames)
    103 
    104 	handleFile := func(filename string, fi os.FileInfo, err error) error {
    105 		if err != nil {
    106 			t.Error(err)
    107 			return nil
    108 		}
    109 		if isGoFile(fi) {
    110 			filenames <- filename
    111 			nfiles++
    112 		}
    113 		return nil
    114 	}
    115 
    116 	// test Go files provided via -files, if any
    117 	if *files != "" {
    118 		for _, filename := range strings.Split(*files, ",") {
    119 			fi, err := os.Stat(filename)
    120 			handleFile(filename, fi, err)
    121 		}
    122 		return // ignore files under -root
    123 	}
    124 
    125 	// otherwise, test all Go files under *root
    126 	filepath.Walk(*root, handleFile)
    127 }
    128 
    129 func TestAll(t *testing.T) {
    130 	if testing.Short() {
    131 		return
    132 	}
    133 
    134 	if *ngo < 1 {
    135 		*ngo = 1 // make sure test is run
    136 	}
    137 	if *verbose {
    138 		fmt.Printf("running test using %d goroutines\n", *ngo)
    139 	}
    140 
    141 	// generate filenames
    142 	filenames := make(chan string, 32)
    143 	go genFilenames(t, filenames)
    144 
    145 	// launch test goroutines
    146 	done := make(chan int)
    147 	for i := 0; i < *ngo; i++ {
    148 		go testFiles(t, filenames, done)
    149 	}
    150 
    151 	// wait for all test goroutines to complete
    152 	for i := 0; i < *ngo; i++ {
    153 		<-done
    154 	}
    155 
    156 	if *verbose {
    157 		fmt.Printf("processed %d files\n", nfiles)
    158 	}
    159 }
    160