| // 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" |
| "internal/diff" |
| "os" |
| "path/filepath" |
| "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 = "" |
| info, err := os.Lstat(in) |
| if err != nil { |
| t.Error(err) |
| return |
| } |
| 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 |
| info = nil |
| default: |
| t.Errorf("unrecognized flag name: %s", name) |
| } |
| } |
| |
| initParserMode() |
| initRewrite() |
| |
| const maxWeight = 2 << 20 |
| var buf, errBuf bytes.Buffer |
| s := newSequencer(maxWeight, &buf, &errBuf) |
| s.Add(fileWeight(in, info), func(r *reporter) error { |
| return processFile(in, info, nil, r) |
| }) |
| if errBuf.Len() > 0 { |
| t.Logf("%q", errBuf.Bytes()) |
| } |
| if s.GetExitCode() != 0 { |
| t.Fail() |
| } |
| |
| 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)\n%s", in, out, in, |
| diff.Diff("expected", expected, "got", got)) |
| 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 { |
| name := filepath.Base(in) |
| t.Run(name, func(t *testing.T) { |
| 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 && !t.Failed() { |
| // 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) |
| } |