| // Copyright 2011 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package main | |
| import ( | |
| "bytes" | |
| "flag" | |
| "os" | |
| "os/exec" | |
| "path/filepath" | |
| "runtime" | |
| "strings" | |
| "testing" | |
| "text/scanner" | |
| ) | |
| var update = flag.Bool("update", false, "update .golden files") | |
| // gofmtFlags looks for a comment of the form | |
| // | |
| // //gofmt flags | |
| // | |
| // within the first maxLines lines of the given file, | |
| // and returns the flags string, if any. Otherwise it | |
| // returns the empty string. | |
| func gofmtFlags(filename string, maxLines int) string { | |
| f, err := os.Open(filename) | |
| if err != nil { | |
| return "" // ignore errors - they will be found later | |
| } | |
| defer f.Close() | |
| // initialize scanner | |
| var s scanner.Scanner | |
| s.Init(f) | |
| s.Error = func(*scanner.Scanner, string) {} // ignore errors | |
| s.Mode = scanner.GoTokens &^ scanner.SkipComments // want comments | |
| // look for //gofmt comment | |
| for s.Line <= maxLines { | |
| switch s.Scan() { | |
| case scanner.Comment: | |
| const prefix = "//gofmt " | |
| if t := s.TokenText(); strings.HasPrefix(t, prefix) { | |
| return strings.TrimSpace(t[len(prefix):]) | |
| } | |
| case scanner.EOF: | |
| return "" | |
| } | |
| } | |
| return "" | |
| } | |
| func runTest(t *testing.T, in, out string) { | |
| // process flags | |
| *simplifyAST = false | |
| *rewriteRule = "" | |
| stdin := false | |
| for _, flag := range strings.Split(gofmtFlags(in, 20), " ") { | |
| elts := strings.SplitN(flag, "=", 2) | |
| name := elts[0] | |
| value := "" | |
| if len(elts) == 2 { | |
| value = elts[1] | |
| } | |
| switch name { | |
| case "": | |
| // no flags | |
| case "-r": | |
| *rewriteRule = value | |
| case "-s": | |
| *simplifyAST = true | |
| case "-stdin": | |
| // fake flag - pretend input is from stdin | |
| stdin = true | |
| case "-G": | |
| // fake flag - allow parsing type parameters | |
| allowTypeParams = true | |
| default: | |
| t.Errorf("unrecognized flag name: %s", name) | |
| } | |
| } | |
| initParserMode() | |
| initRewrite() | |
| var buf bytes.Buffer | |
| err := processFile(in, nil, &buf, stdin) | |
| if err != nil { | |
| t.Error(err) | |
| return | |
| } | |
| expected, err := os.ReadFile(out) | |
| if err != nil { | |
| t.Error(err) | |
| return | |
| } | |
| if got := buf.Bytes(); !bytes.Equal(got, expected) { | |
| if *update { | |
| if in != out { | |
| if err := os.WriteFile(out, got, 0666); err != nil { | |
| t.Error(err) | |
| } | |
| return | |
| } | |
| // in == out: don't accidentally destroy input | |
| t.Errorf("WARNING: -update did not rewrite input file %s", in) | |
| } | |
| t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in) | |
| d, err := diffWithReplaceTempFile(expected, got, in) | |
| if err == nil { | |
| t.Errorf("%s", d) | |
| } | |
| if err := os.WriteFile(in+".gofmt", got, 0666); err != nil { | |
| t.Error(err) | |
| } | |
| } | |
| } | |
| // TestRewrite processes testdata/*.input files and compares them to the | |
| // corresponding testdata/*.golden files. The gofmt flags used to process | |
| // a file must be provided via a comment of the form | |
| // | |
| // //gofmt flags | |
| // | |
| // in the processed file within the first 20 lines, if any. | |
| func TestRewrite(t *testing.T) { | |
| // determine input files | |
| match, err := filepath.Glob("testdata/*.input") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| // add larger examples | |
| match = append(match, "gofmt.go", "gofmt_test.go") | |
| for _, in := range match { | |
| out := in // for files where input and output are identical | |
| if strings.HasSuffix(in, ".input") { | |
| out = in[:len(in)-len(".input")] + ".golden" | |
| } | |
| runTest(t, in, out) | |
| if in != out { | |
| // Check idempotence. | |
| runTest(t, out, out) | |
| } | |
| } | |
| } | |
| // Test case for issue 3961. | |
| func TestCRLF(t *testing.T) { | |
| const input = "testdata/crlf.input" // must contain CR/LF's | |
| const golden = "testdata/crlf.golden" // must not contain any CR's | |
| data, err := os.ReadFile(input) | |
| if err != nil { | |
| t.Error(err) | |
| } | |
| if !bytes.Contains(data, []byte("\r\n")) { | |
| t.Errorf("%s contains no CR/LF's", input) | |
| } | |
| data, err = os.ReadFile(golden) | |
| if err != nil { | |
| t.Error(err) | |
| } | |
| if bytes.Contains(data, []byte("\r")) { | |
| t.Errorf("%s contains CR's", golden) | |
| } | |
| } | |
| func TestBackupFile(t *testing.T) { | |
| dir, err := os.MkdirTemp("", "gofmt_test") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer os.RemoveAll(dir) | |
| name, err := backupFile(filepath.Join(dir, "foo.go"), []byte(" package main"), 0644) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| t.Logf("Created: %s", name) | |
| } | |
| func TestDiff(t *testing.T) { | |
| if _, err := exec.LookPath("diff"); err != nil { | |
| t.Skipf("skip test on %s: diff command is required", runtime.GOOS) | |
| } | |
| in := []byte("first\nsecond\n") | |
| out := []byte("first\nthird\n") | |
| filename := "difftest.txt" | |
| b, err := diffWithReplaceTempFile(in, out, filename) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if runtime.GOOS == "windows" { | |
| b = bytes.ReplaceAll(b, []byte{'\r', '\n'}, []byte{'\n'}) | |
| } | |
| bs := bytes.SplitN(b, []byte{'\n'}, 3) | |
| line0, line1 := bs[0], bs[1] | |
| if prefix := "--- difftest.txt.orig"; !bytes.HasPrefix(line0, []byte(prefix)) { | |
| t.Errorf("diff: first line should start with `%s`\ngot: %s", prefix, line0) | |
| } | |
| if prefix := "+++ difftest.txt"; !bytes.HasPrefix(line1, []byte(prefix)) { | |
| t.Errorf("diff: second line should start with `%s`\ngot: %s", prefix, line1) | |
| } | |
| want := `@@ -1,2 +1,2 @@ | |
| first | |
| -second | |
| +third | |
| ` | |
| if got := string(bs[2]); got != want { | |
| t.Errorf("diff: got:\n%s\nwant:\n%s", got, want) | |
| } | |
| } | |
| func TestReplaceTempFilename(t *testing.T) { | |
| diff := []byte(`--- /tmp/tmpfile1 2017-02-08 00:53:26.175105619 +0900 | |
| +++ /tmp/tmpfile2 2017-02-08 00:53:38.415151275 +0900 | |
| @@ -1,2 +1,2 @@ | |
| first | |
| -second | |
| +third | |
| `) | |
| want := []byte(`--- path/to/file.go.orig 2017-02-08 00:53:26.175105619 +0900 | |
| +++ path/to/file.go 2017-02-08 00:53:38.415151275 +0900 | |
| @@ -1,2 +1,2 @@ | |
| first | |
| -second | |
| +third | |
| `) | |
| // Check path in diff output is always slash regardless of the | |
| // os.PathSeparator (`/` or `\`). | |
| sep := string(os.PathSeparator) | |
| filename := strings.Join([]string{"path", "to", "file.go"}, sep) | |
| got, err := replaceTempFilename(diff, filename) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if !bytes.Equal(got, want) { | |
| t.Errorf("os.PathSeparator='%s': replacedDiff:\ngot:\n%s\nwant:\n%s", sep, got, want) | |
| } | |
| } |