Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/gofmt/gofmt_test.go

Documentation: cmd/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  package main
     6  
     7  import (
     8  	"bytes"
     9  	"flag"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"runtime"
    14  	"strings"
    15  	"testing"
    16  	"text/scanner"
    17  )
    18  
    19  var update = flag.Bool("update", false, "update .golden files")
    20  
    21  // gofmtFlags looks for a comment of the form
    22  //
    23  //	//gofmt flags
    24  //
    25  // within the first maxLines lines of the given file,
    26  // and returns the flags string, if any. Otherwise it
    27  // returns the empty string.
    28  func gofmtFlags(filename string, maxLines int) string {
    29  	f, err := os.Open(filename)
    30  	if err != nil {
    31  		return "" // ignore errors - they will be found later
    32  	}
    33  	defer f.Close()
    34  
    35  	// initialize scanner
    36  	var s scanner.Scanner
    37  	s.Init(f)
    38  	s.Error = func(*scanner.Scanner, string) {}       // ignore errors
    39  	s.Mode = scanner.GoTokens &^ scanner.SkipComments // want comments
    40  
    41  	// look for //gofmt comment
    42  	for s.Line <= maxLines {
    43  		switch s.Scan() {
    44  		case scanner.Comment:
    45  			const prefix = "//gofmt "
    46  			if t := s.TokenText(); strings.HasPrefix(t, prefix) {
    47  				return strings.TrimSpace(t[len(prefix):])
    48  			}
    49  		case scanner.EOF:
    50  			return ""
    51  		}
    52  	}
    53  
    54  	return ""
    55  }
    56  
    57  var typeParamsEnabled = false
    58  
    59  func runTest(t *testing.T, in, out string) {
    60  	// process flags
    61  	*simplifyAST = false
    62  	*rewriteRule = ""
    63  	stdin := false
    64  	for _, flag := range strings.Split(gofmtFlags(in, 20), " ") {
    65  		elts := strings.SplitN(flag, "=", 2)
    66  		name := elts[0]
    67  		value := ""
    68  		if len(elts) == 2 {
    69  			value = elts[1]
    70  		}
    71  		switch name {
    72  		case "":
    73  			// no flags
    74  		case "-r":
    75  			*rewriteRule = value
    76  		case "-s":
    77  			*simplifyAST = true
    78  		case "-stdin":
    79  			// fake flag - pretend input is from stdin
    80  			stdin = true
    81  		case "-G":
    82  			// fake flag - test is for generic code
    83  			if !typeParamsEnabled {
    84  				return
    85  			}
    86  		default:
    87  			t.Errorf("unrecognized flag name: %s", name)
    88  		}
    89  	}
    90  
    91  	initParserMode()
    92  	initRewrite()
    93  
    94  	var buf bytes.Buffer
    95  	err := processFile(in, nil, &buf, stdin)
    96  	if err != nil {
    97  		t.Error(err)
    98  		return
    99  	}
   100  
   101  	expected, err := os.ReadFile(out)
   102  	if err != nil {
   103  		t.Error(err)
   104  		return
   105  	}
   106  
   107  	if got := buf.Bytes(); !bytes.Equal(got, expected) {
   108  		if *update {
   109  			if in != out {
   110  				if err := os.WriteFile(out, got, 0666); err != nil {
   111  					t.Error(err)
   112  				}
   113  				return
   114  			}
   115  			// in == out: don't accidentally destroy input
   116  			t.Errorf("WARNING: -update did not rewrite input file %s", in)
   117  		}
   118  
   119  		t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in)
   120  		d, err := diffWithReplaceTempFile(expected, got, in)
   121  		if err == nil {
   122  			t.Errorf("%s", d)
   123  		}
   124  		if err := os.WriteFile(in+".gofmt", got, 0666); err != nil {
   125  			t.Error(err)
   126  		}
   127  	}
   128  }
   129  
   130  // TestRewrite processes testdata/*.input files and compares them to the
   131  // corresponding testdata/*.golden files. The gofmt flags used to process
   132  // a file must be provided via a comment of the form
   133  //
   134  //	//gofmt flags
   135  //
   136  // in the processed file within the first 20 lines, if any.
   137  func TestRewrite(t *testing.T) {
   138  	// determine input files
   139  	match, err := filepath.Glob("testdata/*.input")
   140  	if err != nil {
   141  		t.Fatal(err)
   142  	}
   143  
   144  	// add larger examples
   145  	match = append(match, "gofmt.go", "gofmt_test.go")
   146  
   147  	for _, in := range match {
   148  		out := in // for files where input and output are identical
   149  		if strings.HasSuffix(in, ".input") {
   150  			out = in[:len(in)-len(".input")] + ".golden"
   151  		}
   152  		runTest(t, in, out)
   153  		if in != out {
   154  			// Check idempotence.
   155  			runTest(t, out, out)
   156  		}
   157  	}
   158  }
   159  
   160  // Test case for issue 3961.
   161  func TestCRLF(t *testing.T) {
   162  	const input = "testdata/crlf.input"   // must contain CR/LF's
   163  	const golden = "testdata/crlf.golden" // must not contain any CR's
   164  
   165  	data, err := os.ReadFile(input)
   166  	if err != nil {
   167  		t.Error(err)
   168  	}
   169  	if !bytes.Contains(data, []byte("\r\n")) {
   170  		t.Errorf("%s contains no CR/LF's", input)
   171  	}
   172  
   173  	data, err = os.ReadFile(golden)
   174  	if err != nil {
   175  		t.Error(err)
   176  	}
   177  	if bytes.Contains(data, []byte("\r")) {
   178  		t.Errorf("%s contains CR's", golden)
   179  	}
   180  }
   181  
   182  func TestBackupFile(t *testing.T) {
   183  	dir, err := os.MkdirTemp("", "gofmt_test")
   184  	if err != nil {
   185  		t.Fatal(err)
   186  	}
   187  	defer os.RemoveAll(dir)
   188  	name, err := backupFile(filepath.Join(dir, "foo.go"), []byte("  package main"), 0644)
   189  	if err != nil {
   190  		t.Fatal(err)
   191  	}
   192  	t.Logf("Created: %s", name)
   193  }
   194  
   195  func TestDiff(t *testing.T) {
   196  	if _, err := exec.LookPath("diff"); err != nil {
   197  		t.Skipf("skip test on %s: diff command is required", runtime.GOOS)
   198  	}
   199  	in := []byte("first\nsecond\n")
   200  	out := []byte("first\nthird\n")
   201  	filename := "difftest.txt"
   202  	b, err := diffWithReplaceTempFile(in, out, filename)
   203  	if err != nil {
   204  		t.Fatal(err)
   205  	}
   206  
   207  	if runtime.GOOS == "windows" {
   208  		b = bytes.ReplaceAll(b, []byte{'\r', '\n'}, []byte{'\n'})
   209  	}
   210  
   211  	bs := bytes.SplitN(b, []byte{'\n'}, 3)
   212  	line0, line1 := bs[0], bs[1]
   213  
   214  	if prefix := "--- difftest.txt.orig"; !bytes.HasPrefix(line0, []byte(prefix)) {
   215  		t.Errorf("diff: first line should start with `%s`\ngot: %s", prefix, line0)
   216  	}
   217  
   218  	if prefix := "+++ difftest.txt"; !bytes.HasPrefix(line1, []byte(prefix)) {
   219  		t.Errorf("diff: second line should start with `%s`\ngot: %s", prefix, line1)
   220  	}
   221  
   222  	want := `@@ -1,2 +1,2 @@
   223   first
   224  -second
   225  +third
   226  `
   227  
   228  	if got := string(bs[2]); got != want {
   229  		t.Errorf("diff: got:\n%s\nwant:\n%s", got, want)
   230  	}
   231  }
   232  
   233  func TestReplaceTempFilename(t *testing.T) {
   234  	diff := []byte(`--- /tmp/tmpfile1	2017-02-08 00:53:26.175105619 +0900
   235  +++ /tmp/tmpfile2	2017-02-08 00:53:38.415151275 +0900
   236  @@ -1,2 +1,2 @@
   237   first
   238  -second
   239  +third
   240  `)
   241  	want := []byte(`--- path/to/file.go.orig	2017-02-08 00:53:26.175105619 +0900
   242  +++ path/to/file.go	2017-02-08 00:53:38.415151275 +0900
   243  @@ -1,2 +1,2 @@
   244   first
   245  -second
   246  +third
   247  `)
   248  	// Check path in diff output is always slash regardless of the
   249  	// os.PathSeparator (`/` or `\`).
   250  	sep := string(os.PathSeparator)
   251  	filename := strings.Join([]string{"path", "to", "file.go"}, sep)
   252  	got, err := replaceTempFilename(diff, filename)
   253  	if err != nil {
   254  		t.Fatal(err)
   255  	}
   256  	if !bytes.Equal(got, want) {
   257  		t.Errorf("os.PathSeparator='%s': replacedDiff:\ngot:\n%s\nwant:\n%s", sep, got, want)
   258  	}
   259  }
   260  

View as plain text