Source file src/cmd/gofmt/long_test.go

     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  	"io/fs"
    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 := 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 = 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  		// A known instance of gofmt not being idempotent
    90  		// (see Issue #24472)
    91  		if strings.HasSuffix(filename, "issue22662.go") {
    92  			t.Log("known gofmt idempotency bug (Issue #24472)")
    93  			return
    94  		}
    95  		t.Errorf("gofmt %s not idempotent", filename)
    96  	}
    97  }
    98  
    99  func testFiles(t *testing.T, filenames <-chan string, done chan<- int) {
   100  	b1 := new(bytes.Buffer)
   101  	b2 := new(bytes.Buffer)
   102  	for filename := range filenames {
   103  		testFile(t, b1, b2, filename)
   104  	}
   105  	done <- 0
   106  }
   107  
   108  func genFilenames(t *testing.T, filenames chan<- string) {
   109  	defer close(filenames)
   110  
   111  	handleFile := func(filename string, d fs.DirEntry, err error) error {
   112  		if err != nil {
   113  			t.Error(err)
   114  			return nil
   115  		}
   116  		if isGoFile(d) {
   117  			filenames <- filename
   118  			nfiles++
   119  		}
   120  		return nil
   121  	}
   122  
   123  	// test Go files provided via -files, if any
   124  	if *files != "" {
   125  		for _, filename := range strings.Split(*files, ",") {
   126  			fi, err := os.Stat(filename)
   127  			handleFile(filename, &statDirEntry{fi}, err)
   128  		}
   129  		return // ignore files under -root
   130  	}
   131  
   132  	// otherwise, test all Go files under *root
   133  	filepath.WalkDir(*root, handleFile)
   134  }
   135  
   136  func TestAll(t *testing.T) {
   137  	if testing.Short() {
   138  		return
   139  	}
   140  
   141  	if *ngo < 1 {
   142  		*ngo = 1 // make sure test is run
   143  	}
   144  	if *verbose {
   145  		fmt.Printf("running test using %d goroutines\n", *ngo)
   146  	}
   147  
   148  	// generate filenames
   149  	filenames := make(chan string, 32)
   150  	go genFilenames(t, filenames)
   151  
   152  	// launch test goroutines
   153  	done := make(chan int)
   154  	for i := 0; i < *ngo; i++ {
   155  		go testFiles(t, filenames, done)
   156  	}
   157  
   158  	// wait for all test goroutines to complete
   159  	for i := 0; i < *ngo; i++ {
   160  		<-done
   161  	}
   162  
   163  	if *verbose {
   164  		fmt.Printf("processed %d files\n", nfiles)
   165  	}
   166  }
   167  
   168  type statDirEntry struct {
   169  	info fs.FileInfo
   170  }
   171  
   172  func (d *statDirEntry) Name() string               { return d.info.Name() }
   173  func (d *statDirEntry) IsDir() bool                { return d.info.IsDir() }
   174  func (d *statDirEntry) Type() fs.FileMode          { return d.info.Mode().Type() }
   175  func (d *statDirEntry) Info() (fs.FileInfo, error) { return d.info, nil }
   176  

View as plain text