github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/cmd/tast-lint/internal/check/messages_test.go (about)

     1  // Copyright 2019 The ChromiumOS Authors
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  package check
     6  
     7  import (
     8  	"testing"
     9  )
    10  
    11  func TestMessages(t *testing.T) {
    12  	const code = `package pkg
    13  
    14  import (
    15  	"context"
    16  
    17  	"go.chromium.org/tast/core/errors"
    18  	"go.chromium.org/tast/core/testing"
    19  )
    20  
    21  func Test(ctx context.Context, s *testing.State) {
    22  	var err error
    23  
    24  	// Bad messages:
    25  	s.Logf("Should use Log for single string arg")
    26  	s.Logf("Should use Log for single trailing %v", err)
    27  	s.Log("Shouldn't use trailing period.")
    28  	s.Log("Shouldn't contain\na newline")
    29  	s.Log("Should end with colon and space ", err)
    30  	s.Log("Should end with colon and space:", err)
    31  	s.Log(err)
    32  
    33  	// Other functions should also be checked:
    34  	s.Error(err)
    35  	s.Fatal(err)
    36  	s.Errorf("Just a string")
    37  	s.Fatalf("Just a string")
    38  	testing.ContextLog(ctx, err)
    39  	testing.ContextLogf(ctx, "Should've used ContextLog: %v", err)
    40  
    41  	// Good messages:
    42  	s.Log("Good single-arg message")
    43  	s.Log("Good message with error: ", err)
    44  	s.Logf("Good message with custom-formatted value %d", 3)
    45  	s.Logf("Good message with %v default-formatted values: %v", 2, err)
    46  
    47  	// Bad errors:
    48  	errors.New("shouldn't use trailing period.")
    49  	errors.New("shouldn't contain\na newline")
    50  	errors.Errorf("should use New for single string arg")
    51  	errors.Errorf("should use Wrap: %v", err)
    52  	errors.Errorf("should use Wrapf %s: %v", "here", err)
    53  	errors.Wrap(err, "shouldn't use trailing colon:")
    54  	errors.Wrap(err, "shouldn't use trailing colon: ")
    55  	errors.Wrap(err, "shouldn't use trailing colon:  ")
    56  	errors.Wrapf(err, "shouldn't wrap err twice: %v", err)
    57  
    58  	// Good errors:
    59  	errors.New("normal message")
    60  	errors.Errorf("need Errorf for multiple values: %v", true)
    61  	errors.Errorf("also okay for custom formatting: %d", 1)
    62  	errors.Wrapf(err, "this is okay: %v", 3)
    63  	errors.Wrap(err, "this is okay")
    64  
    65  	// Bad quoting:
    66  	s.Logf("Read value '%s'", "blah")
    67  	s.Errorf("Read value \"%v\"", "blah")
    68  
    69  	// Good quoting:
    70  	s.Logf("Read value %q", "blah")
    71  	s.Errorf("Read value '%d'", 123)
    72  
    73  	// Bad formatting. (Using Errorf/Fatalf/Logf instead of
    74  	// Error/Fatal/Log mistakenly).
    75  	s.Errorf("Some error: ", err)
    76  	s.Fatalf("Some error: ", err)
    77  	s.Logf("Some log: ", text)
    78  
    79  	// Verb counts.
    80  	s.Errorf("Got %d; want %d", actual, expect)
    81  	s.Errorf("Got %d %%; want 100 %%", actual)
    82  	s.Errorf("%%%%%d", actual)  // Test for an edge case about %%.
    83  
    84  	// Bad error construction with empty string.
    85  	errors.New("")
    86  	errors.Wrap(err, "")
    87  	s.Error("")
    88  	s.Fatal("")
    89  
    90  	// Good messages. (Allowed for line breaking)
    91  	s.Log("")
    92  	testing.ContextLog(ctx, "")
    93  
    94  	// Messages should start with correct case.
    95  	s.Error("Failed to start ARC: ", err)
    96  	s.Error("failed to start ARC: ", err)
    97  	s.Fatalf("Unexpected string %q received", "blah")
    98  	s.Fatalf("unexpected string %q received", "blah")
    99  	s.Log("Got messages")
   100  	s.Log("got messages")
   101  	testing.ContextLogf(ctx, "Found a file %q", "blah")
   102  	testing.ContextLogf(ctx, "found a file %q", "blah")
   103  	errors.New("could not start ARC")
   104  	errors.New("Could not start ARC")
   105  	errors.Wrapf(err, "too many (%d) files open", 28)
   106  	errors.Wrapf(err, "Too many (%d) files open", 28)
   107  
   108  	// Proper nouns are not subject to case rules.
   109  	s.Error("testSomething failed: ", err)
   110  	errors.New("ARC failed to start")
   111  
   112  	// Bad combinations substitutable with *f family.
   113  	s.Error(fmt.Sprintf("Foo (%d)", 42))
   114  	errors.New(fmt.Sprintf("foo (%d)", 42))
   115  	errors.Wrap(err, fmt.Sprintf("foo (%d)", 42))
   116  
   117  	// Allowed combinations for Sprintf
   118  	s.Error(fmt.Sprintf("Foo (%d)", 42), "bar")
   119  
   120  	// Bad usage of verbs in non-format logging
   121  	s.Error("Foo (%d)", 42)
   122  	errors.Wrap(err, "foo (%1.2f)", 4.2)
   123  	testing.ContextLog(ctx, "Foo: %#v", foo)
   124  
   125  	// Good usage of % in non-format logging
   126  	testing.ContextLog(ctx, "Waiting for 100% progress")
   127  }`
   128  
   129  	f, fs := parse(code, "test.go")
   130  	issues := Messages(fs, f, false)
   131  	expects := []string{
   132  		`test.go:14:2: Use s.Log("<msg>") instead of s.Logf("<msg>")`,
   133  		`test.go:15:2: Use s.Log("<msg> ", val) instead of s.Logf("<msg> %v", val)`,
   134  		`test.go:16:2: s.Log string arg should not contain trailing punctuation`,
   135  		`test.go:17:2: s.Log string arg should not contain embedded newlines`,
   136  		`test.go:18:2: s.Log string arg should end with ": " when followed by error`,
   137  		`test.go:19:2: s.Log string arg should end with ": " when followed by error`,
   138  		`test.go:20:2: Use s.Log("Something failed: ", err) instead of s.Log(err)`,
   139  		`test.go:23:2: Use s.Error("Something failed: ", err) instead of s.Error(err)`,
   140  		`test.go:24:2: Use s.Fatal("Something failed: ", err) instead of s.Fatal(err)`,
   141  		`test.go:25:2: Use s.Error("<msg>") instead of s.Errorf("<msg>")`,
   142  		`test.go:26:2: Use s.Fatal("<msg>") instead of s.Fatalf("<msg>")`,
   143  		`test.go:27:2: Use testing.ContextLog(ctx, "Something failed: ", err) instead of testing.ContextLog(ctx, err)`,
   144  		`test.go:28:2: Use testing.ContextLog(ctx, "<msg> ", val) instead of testing.ContextLogf(ctx, "<msg> %v", val)`,
   145  		`test.go:37:2: errors.New string arg should not contain trailing punctuation`,
   146  		`test.go:38:2: errors.New string arg should not contain embedded newlines`,
   147  		`test.go:39:2: Use errors.New("<msg>") instead of errors.Errorf("<msg>")`,
   148  		`test.go:40:2: Use errors.Wrap(err, "<msg>") instead of errors.Errorf("<msg>: %v", err)`,
   149  		`test.go:41:2: Use errors.Wrapf(err, "<msg>", ...) instead of errors.Errorf("<msg>: %v", ..., err)`,
   150  		`test.go:42:2: Use errors.Wrap(err, "shouldn't use trailing colon") instead of errors.Wrap(err, "shouldn't use trailing colon:")`,
   151  		`test.go:43:2: Use errors.Wrap(err, "shouldn't use trailing colon") instead of errors.Wrap(err, "shouldn't use trailing colon: ")`,
   152  		`test.go:44:2: Use errors.Wrap(err, "shouldn't use trailing colon") instead of errors.Wrap(err, "shouldn't use trailing colon:  ")`,
   153  		`test.go:45:2: Use errors.Wrap(err, "<msg>") instead of errors.Wrapf(err, "<msg>: %v", err)`,
   154  		`test.go:55:2: Use %q to quote values instead of manually quoting them`,
   155  		`test.go:56:2: Use %q to quote values instead of manually quoting them`,
   156  		`test.go:64:2: The number of verbs in format literal mismatches with the number of arguments`,
   157  		`test.go:65:2: The number of verbs in format literal mismatches with the number of arguments`,
   158  		`test.go:66:2: The number of verbs in format literal mismatches with the number of arguments`,
   159  		`test.go:74:2: Error message should have some surrounding context, so must not empty`,
   160  		`test.go:75:2: Error message should have some surrounding context, so must not empty`,
   161  		`test.go:76:2: Error message should have some surrounding context, so must not empty`,
   162  		`test.go:77:2: Error message should have some surrounding context, so must not empty`,
   163  		`test.go:85:2: Test failure messages should be capitalized`,
   164  		`test.go:87:2: Test failure messages should be capitalized`,
   165  		`test.go:89:2: Log messages should be capitalized`,
   166  		`test.go:91:2: Log messages should be capitalized`,
   167  		`test.go:93:2: Messages of the error type should not be capitalized`,
   168  		`test.go:95:2: Messages of the error type should not be capitalized`,
   169  		`test.go:102:2: Use s.Errorf(...) instead of s.Error(fmt.Sprintf(...))`,
   170  		`test.go:103:2: Use errors.Errorf(...) instead of errors.New(fmt.Sprintf(...))`,
   171  		`test.go:104:2: Use errors.Wrapf(err, ...) instead of errors.Wrap(err, fmt.Sprintf(...))`,
   172  		`test.go:110:2: s.Error has verbs in the first string (do you mean s.Errorf?)`,
   173  		`test.go:111:2: errors.Wrap has verbs in the first string (do you mean errors.Wrapf?)`,
   174  		`test.go:112:2: testing.ContextLog has verbs in the first string (do you mean testing.ContextLogf?)`,
   175  	}
   176  	verifyIssues(t, issues, expects)
   177  }
   178  
   179  func TestAutoFixMessages(t *testing.T) {
   180  	files := make(map[string]string)
   181  	expects := make(map[string]string)
   182  	const filename1 = "foo.go"
   183  	files[filename1] = `package pkg
   184  
   185  import (
   186  	"context"
   187  
   188  	"go.chromium.org/tast/core/testing"
   189  	"go.chromium.org/tast/core/errors"
   190  )
   191  
   192  func Test(ctx context.Context, s *testing.State) {
   193  	var err error
   194  
   195  	s.Logf("Should use Log for single string arg")
   196  	s.Errorf("Just a string")
   197  	s.Fatalf("Just a string")
   198  	errors.Errorf("got just a string with punctuation!")
   199  	testing.ContextLogf(ctx, "got just a small string")
   200  	s.Errorf("found case 1 but ignore case 1 if there is format identifier '%s'.")
   201  	s.Fatalf("got just a small string with punctuation.")
   202  
   203  	s.Error(fmt.Sprintf("Foo (%d)", 42))
   204  	errors.New(fmt.Sprintf("foo (%d)", 42))
   205  	errors.Wrap(err, fmt.Sprintf("foo (%d)", 42))
   206  	s.Logf(fmt.Sprintf("something %s", foo))
   207  
   208  	s.Logf("Should use Log for single trailing %v", err) // CASE 3->4
   209  	testing.ContextLogf(ctx, "Should've used ContextLog: %v", err)
   210  	s.Errorf("unexpected usage Errorf for single trailing %v", err) // CASE 3+4->10
   211  
   212  	s.Log("Should end with colon and space ", err)
   213  	s.Error("Should end with colon and space:", err)
   214  	s.Fatal("Should end with colon and space.", err)
   215  	testing.ContextLog(ctx, "found lower letter and end with ! instead of colon!", err)
   216  
   217  	errors.Errorf("should use Wrap: %v", err)
   218  	errors.Errorf("should use Wrapf %s%s: %v", "h", "ere", err)
   219  	errors.Errorf("Found use Errorf and upper letter: %v", err)
   220  	errors.Errorf("Found use Errorf, \"%s\" letter and %s: %v", "upper", "invalid quate", err)
   221  
   222  	s.Log("Shouldn't use trailing period.")
   223  	errors.New("shouldn't use trailing period.")
   224  	testing.ContextLogf(ctx, "\"%s\" should be %q not the \"%s\".", "\"%%s\"", "%%q", "\"%%s\"")
   225  
   226  	s.Logf("Read value '%s' \"%s\"", "blah", "blah")
   227  	s.Errorf("can read value \"%v\" '%v'", "blah", "blah")
   228  
   229  	errors.New("Could not start ARC")
   230  	errors.Wrapf(err, "Too many (%d) files open", 28)
   231  
   232  	s.Log("got messages")
   233  	testing.ContextLogf(ctx, "found a file %q", "blah")
   234  
   235  	s.Error("failed to start ARC: ", err)
   236  	s.Fatalf("unexpected string %q received", "blah")
   237  
   238  	s.Errorf(fmt.Sprintf("bar"))
   239  	s.Error(fmt.Sprintf("bar"))
   240  
   241  	// Won't change below.
   242  	s.Log("Hello\x20world")
   243  
   244  }
   245  `
   246  	expects[filename1] = `package pkg
   247  
   248  import (
   249  	"context"
   250  
   251  	"go.chromium.org/tast/core/errors"
   252  	"go.chromium.org/tast/core/testing"
   253  )
   254  
   255  func Test(ctx context.Context, s *testing.State) {
   256  	var err error
   257  
   258  	s.Log("Should use Log for single string arg")
   259  	s.Error("Just a string")
   260  	s.Fatal("Just a string")
   261  	errors.New("got just a string with punctuation")
   262  	testing.ContextLog(ctx, "Got just a small string")
   263  	s.Errorf("Found case 1 but ignore case 1 if there is format identifier %q")
   264  	s.Fatal("Got just a small string with punctuation")
   265  
   266  	s.Errorf("Foo (%d)", 42)
   267  	errors.Errorf("foo (%d)", 42)
   268  	errors.Wrapf(err, "foo (%d)", 42)
   269  	s.Logf("something %s", foo)
   270  
   271  	s.Log("Should use Log for single trailing: ", err) // CASE 3->4
   272  	testing.ContextLog(ctx, "Should've used ContextLog: ", err)
   273  	s.Error("Unexpected usage Errorf for single trailing: ", err) // CASE 3+4->10
   274  
   275  	s.Log("Should end with colon and space: ", err)
   276  	s.Error("Should end with colon and space: ", err)
   277  	s.Fatal("Should end with colon and space: ", err)
   278  	testing.ContextLog(ctx, "Found lower letter and end with ! instead of colon: ", err)
   279  
   280  	errors.Wrap(err, "should use Wrap")
   281  	errors.Wrapf(err, "should use Wrapf %s%s", "h", "ere")
   282  	errors.Wrap(err, "found use Errorf and upper letter")
   283  	errors.Wrapf(err, "found use Errorf, %q letter and %s", "upper", "invalid quate")
   284  
   285  	s.Log("Shouldn't use trailing period")
   286  	errors.New("shouldn't use trailing period")
   287  	testing.ContextLogf(ctx, "%q should be %q not the %q", "\"%%s\"", "%%q", "\"%%s\"")
   288  
   289  	s.Logf("Read value %q %q", "blah", "blah")
   290  	s.Errorf("Can read value %q %q", "blah", "blah")
   291  
   292  	errors.New("could not start ARC")
   293  	errors.Wrapf(err, "too many (%d) files open", 28)
   294  
   295  	s.Log("Got messages")
   296  	testing.ContextLogf(ctx, "Found a file %q", "blah")
   297  
   298  	s.Error("Failed to start ARC: ", err)
   299  	s.Fatalf("Unexpected string %q received", "blah")
   300  
   301  	s.Error("bar")
   302  	s.Error("bar")
   303  
   304  	// Won't change below.
   305  	s.Log("Hello\x20world")
   306  
   307  }
   308  `
   309  	const filename2 = "bar.go"
   310  	files[filename2] = `package new
   311  
   312  import "go.chromium.org/tast/core/errors"
   313  
   314  func main() {
   315  	return errors.Errorf("should use Wrapf %s%s: %v", "h", "ere", err)
   316  }
   317  `
   318  	expects[filename2] = `package new
   319  
   320  import "go.chromium.org/tast/core/errors"
   321  
   322  func main() {
   323  	return errors.Wrapf(err, "should use Wrapf %s%s", "h", "ere")
   324  }
   325  `
   326  	verifyAutoFix(t, Messages, files, expects)
   327  }