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 }