github.com/yoheimuta/protolint@v0.49.8-0.20240515023657-4ecaebb7575d/internal/addon/rules/indentRule_test.go (about) 1 package rules_test 2 3 import ( 4 "reflect" 5 "strings" 6 "testing" 7 8 "github.com/yoheimuta/protolint/internal/util_test" 9 10 "github.com/yoheimuta/go-protoparser/v4/parser/meta" 11 12 "github.com/yoheimuta/protolint/internal/linter/file" 13 14 "github.com/yoheimuta/protolint/internal/setting_test" 15 16 "github.com/yoheimuta/protolint/internal/addon/rules" 17 "github.com/yoheimuta/protolint/linter/report" 18 "github.com/yoheimuta/protolint/linter/rule" 19 ) 20 21 func TestIndentRule_Apply(t *testing.T) { 22 defaultSpace := strings.Repeat(" ", 2) 23 24 tests := []struct { 25 name string 26 inputStyle string 27 inputProtoPath string 28 inputInsertNewline bool 29 wantFailures []report.Failure 30 wantExistErr bool 31 }{ 32 { 33 name: "correct syntax", 34 inputProtoPath: setting_test.TestDataPath("rules", "indentrule", "syntax.proto"), 35 }, 36 { 37 name: "incorrect syntax", 38 inputStyle: defaultSpace, 39 inputProtoPath: setting_test.TestDataPath("rules", "indentrule", "incorrect_syntax.proto"), 40 wantFailures: []report.Failure{ 41 report.Failuref( 42 meta.Position{ 43 Filename: setting_test.TestDataPath("rules", "indentrule", "incorrect_syntax.proto"), 44 Offset: 14, 45 Line: 2, 46 Column: 5, 47 }, 48 "INDENT", 49 `Found an incorrect indentation style "%s". "%s" is correct.`, 50 " ", 51 "", 52 ), 53 }, 54 }, 55 { 56 name: "correct enum", 57 inputProtoPath: setting_test.TestDataPath("rules", "indentrule", "enum.proto"), 58 }, 59 { 60 name: "incorrect enum", 61 inputProtoPath: setting_test.TestDataPath("rules", "indentrule", "incorrect_enum.proto"), 62 wantFailures: []report.Failure{ 63 report.Failuref( 64 meta.Position{ 65 Filename: setting_test.TestDataPath("rules", "indentrule", "incorrect_enum.proto"), 66 Offset: 67, 67 Line: 4, 68 Column: 9, 69 }, 70 "INDENT", 71 `Found an incorrect indentation style "%s". "%s" is correct.`, 72 " ", 73 defaultSpace, 74 ), 75 report.Failuref( 76 meta.Position{ 77 Filename: setting_test.TestDataPath("rules", "indentrule", "incorrect_enum.proto"), 78 Offset: 114, 79 Line: 6, 80 Column: 6, 81 }, 82 "INDENT", 83 `Found an incorrect indentation style "%s". "%s" is correct.`, 84 " ", 85 defaultSpace, 86 ), 87 report.Failuref( 88 meta.Position{ 89 Filename: setting_test.TestDataPath("rules", "indentrule", "incorrect_enum.proto"), 90 Offset: 162, 91 Line: 7, 92 Column: 2, 93 }, 94 "INDENT", 95 `Found an incorrect indentation style "%s". "%s" is correct.`, 96 " ", 97 "", 98 ), 99 }, 100 }, 101 { 102 name: "correct message", 103 inputProtoPath: setting_test.TestDataPath("rules", "indentrule", "message.proto"), 104 }, 105 { 106 name: "incorrect message", 107 inputProtoPath: setting_test.TestDataPath("rules", "indentrule", "incorrect_message.proto"), 108 wantFailures: []report.Failure{ 109 report.Failuref( 110 meta.Position{ 111 Filename: setting_test.TestDataPath("rules", "indentrule", "incorrect_message.proto"), 112 Offset: 100, 113 Line: 6, 114 Column: 3, 115 }, 116 "INDENT", 117 `Found an incorrect indentation style "%s". "%s" is correct.`, 118 " ", 119 strings.Repeat(defaultSpace, 2), 120 ), 121 report.Failuref( 122 meta.Position{ 123 Filename: setting_test.TestDataPath("rules", "indentrule", "incorrect_message.proto"), 124 Offset: 156, 125 Line: 9, 126 Column: 1, 127 }, 128 "INDENT", 129 `Found an incorrect indentation style "%s". "%s" is correct.`, 130 "", 131 defaultSpace, 132 ), 133 report.Failuref( 134 meta.Position{ 135 Filename: setting_test.TestDataPath("rules", "indentrule", "incorrect_message.proto"), 136 Offset: 287, 137 Line: 14, 138 Column: 7, 139 }, 140 "INDENT", 141 `Found an incorrect indentation style "%s". "%s" is correct.`, 142 " ", 143 strings.Repeat(defaultSpace, 2), 144 ), 145 }, 146 }, 147 { 148 name: "handle the proto containing extend. Fix https://github.com/yoheimuta/protolint/issues/63", 149 inputProtoPath: setting_test.TestDataPath("rules", "indentrule", "issue_63.proto"), 150 }, 151 { 152 name: `handle the case that the last rpc method of a service is having a statement block. 153 Fix https://github.com/yoheimuta/protolint/issues/74`, 154 inputProtoPath: setting_test.TestDataPath("rules", "indentrule", "issue_74.proto"), 155 }, 156 { 157 name: `skip wrong indentations of inner elements on the same line. 158 Fix https://github.com/yoheimuta/protolint/issues/139`, 159 inputProtoPath: setting_test.TestDataPath("rules", "indentrule", "issue_139.proto"), 160 }, 161 { 162 name: `detect only a toplevel indentation mistake and skip other than that on the same line. 163 Fix https://github.com/yoheimuta/protolint/issues/139`, 164 inputProtoPath: setting_test.TestDataPath("rules", "indentrule", "incorrect_issue_139.proto"), 165 wantFailures: []report.Failure{ 166 report.Failuref( 167 meta.Position{ 168 Filename: setting_test.TestDataPath("rules", "indentrule", "incorrect_issue_139.proto"), 169 Offset: 222, 170 Line: 11, 171 Column: 3, 172 }, 173 "INDENT", 174 `Found an incorrect indentation style "%s". "%s" is correct.`, 175 " ", 176 "", 177 ), 178 }, 179 }, 180 { 181 name: `do not skip wrong indentations of inner elements on the same line. 182 Fix https://github.com/yoheimuta/protolint/issues/139`, 183 inputProtoPath: setting_test.TestDataPath("rules", "indentrule", "incorrect_issue_139_short.proto"), 184 inputInsertNewline: true, 185 wantFailures: []report.Failure{ 186 report.Failuref( 187 meta.Position{ 188 Filename: setting_test.TestDataPath("rules", "indentrule", "incorrect_issue_139_short.proto"), 189 Offset: 82, 190 Line: 7, 191 Column: 3, 192 }, 193 "INDENT", 194 `Found an incorrect indentation style "%s". "%s" is correct.`, 195 " ", 196 "", 197 ), 198 report.Failuref( 199 meta.Position{ 200 Filename: setting_test.TestDataPath("rules", "indentrule", "incorrect_issue_139_short.proto"), 201 Offset: 104, 202 Line: 7, 203 Column: 25, 204 }, 205 "INDENT", 206 `Found a possible incorrect indentation style. Inserting a new line is recommended.`, 207 ), 208 report.Failuref( 209 meta.Position{ 210 Filename: setting_test.TestDataPath("rules", "indentrule", "incorrect_issue_139_short.proto"), 211 Offset: 127, 212 Line: 7, 213 Column: 48, 214 }, 215 "INDENT", 216 `Found a possible incorrect indentation style. Inserting a new line is recommended.`, 217 ), 218 }, 219 }, 220 { 221 name: `handle the case that the proto has a mixture of line ending formats like LF and CRLF. 222 Fix https://github.com/yoheimuta/protolint/issues/280`, 223 inputProtoPath: setting_test.TestDataPath("rules", "indentrule", "issue_280_mix_lineending.proto"), 224 wantFailures: []report.Failure{ 225 report.Failuref( 226 meta.Position{ 227 Filename: setting_test.TestDataPath("rules", "indentrule", "issue_280_mix_lineending.proto"), 228 Offset: 580, 229 Line: 27, 230 Column: 5, 231 }, 232 "INDENT", 233 `Found an incorrect indentation style "%s". "%s" is correct.`, 234 " ", 235 " ", 236 ), 237 }, 238 }, 239 } 240 241 for _, test := range tests { 242 test := test 243 t.Run(test.name, func(t *testing.T) { 244 rule := rules.NewIndentRule( 245 rule.SeverityError, 246 test.inputStyle, 247 !test.inputInsertNewline, 248 false, 249 ) 250 251 proto, err := file.NewProtoFile(test.inputProtoPath, test.inputProtoPath).Parse(false) 252 if err != nil { 253 t.Errorf(err.Error()) 254 return 255 } 256 257 got, err := rule.Apply(proto) 258 if test.wantExistErr { 259 if err == nil { 260 t.Errorf("got err nil, but want err") 261 } 262 return 263 } 264 if err != nil { 265 t.Errorf("got err %v, but want nil", err) 266 return 267 } 268 269 if !reflect.DeepEqual(got, test.wantFailures) { 270 t.Errorf("got %v, but want %v", got, test.wantFailures) 271 if len(got) != len(test.wantFailures) { 272 t.Errorf("len(got) %v, but len(want) %v", len(got), len(test.wantFailures)) 273 return 274 } 275 for k, v := range got { 276 if !reflect.DeepEqual(v.Pos(), test.wantFailures[k].Pos()) { 277 t.Errorf("got[%v].Pos() %v(offset=%v), but want[%v].Pos() %v", k, v.Pos(), v.Pos().Offset, k, test.wantFailures[k].Pos()) 278 continue 279 } 280 if !reflect.DeepEqual(v.Message(), test.wantFailures[k].Message()) { 281 t.Errorf("got[%v].Message() %v, but want[%v].Message() %v", k, v.Message(), k, test.wantFailures[k].Message()) 282 continue 283 } 284 if !reflect.DeepEqual(v, test.wantFailures[k]) { 285 t.Errorf("got[%v] %v, but want[%v] %v", k, v, k, test.wantFailures[k]) 286 continue 287 } 288 } 289 } 290 }) 291 } 292 } 293 294 func newTestIndentData( 295 fileName string, 296 ) (util_test.TestData, error) { 297 return util_test.NewTestData(setting_test.TestDataPath("rules", "indentrule", fileName)) 298 } 299 300 func TestIndentRule_Apply_fix(t *testing.T) { 301 space2 := strings.Repeat(" ", 2) 302 303 correctSyntaxPath, err := newTestIndentData("syntax.proto") 304 if err != nil { 305 t.Errorf("got err %v", err) 306 return 307 } 308 309 incorrectSyntaxPath, err := newTestIndentData("incorrect_syntax.proto") 310 if err != nil { 311 t.Errorf("got err %v", err) 312 return 313 } 314 315 correctEnumPath, err := newTestIndentData("enum.proto") 316 if err != nil { 317 t.Errorf("got err %v", err) 318 return 319 } 320 321 incorrectEnumPath, err := newTestIndentData("incorrect_enum.proto") 322 if err != nil { 323 t.Errorf("got err %v", err) 324 return 325 } 326 327 correctMessagePath, err := newTestIndentData("message.proto") 328 if err != nil { 329 t.Errorf("got err %v", err) 330 return 331 } 332 333 incorrectMessagePath, err := newTestIndentData("incorrect_message.proto") 334 if err != nil { 335 t.Errorf("got err %v", err) 336 return 337 } 338 339 correctIssue99Path, err := newTestIndentData("issue_99.proto") 340 if err != nil { 341 t.Errorf("got err %v", err) 342 return 343 } 344 345 incorrectIssue99Path, err := newTestIndentData("incorrect_issue_99.proto") 346 if err != nil { 347 t.Errorf("got err %v", err) 348 return 349 } 350 351 incorrectIssue139Path, err := newTestIndentData("incorrect_issue_139.proto") 352 if err != nil { 353 t.Errorf("got err %v", err) 354 return 355 } 356 357 correctIssue139Path, err := newTestIndentData("issue_139.proto") 358 if err != nil { 359 t.Errorf("got err %v", err) 360 return 361 } 362 363 correctIssue139InsertPath, err := newTestIndentData("issue_139_insert_linebreaks.proto") 364 if err != nil { 365 t.Errorf("got err %v", err) 366 return 367 } 368 369 tests := []struct { 370 name string 371 inputTestData util_test.TestData 372 inputInsertNewline bool 373 wantCorrectData util_test.TestData 374 }{ 375 { 376 name: "correct syntax", 377 inputTestData: correctSyntaxPath, 378 wantCorrectData: correctSyntaxPath, 379 }, 380 { 381 name: "incorrect syntax", 382 inputTestData: incorrectSyntaxPath, 383 wantCorrectData: correctSyntaxPath, 384 }, 385 { 386 name: "correct enum", 387 inputTestData: correctEnumPath, 388 wantCorrectData: correctEnumPath, 389 }, 390 { 391 name: "incorrect enum", 392 inputTestData: incorrectEnumPath, 393 wantCorrectData: correctEnumPath, 394 }, 395 { 396 name: "correct message", 397 inputTestData: correctMessagePath, 398 wantCorrectData: correctMessagePath, 399 }, 400 { 401 name: "incorrect message", 402 inputTestData: incorrectMessagePath, 403 wantCorrectData: correctMessagePath, 404 }, 405 { 406 name: "correct issue_99", 407 inputTestData: correctIssue99Path, 408 wantCorrectData: correctIssue99Path, 409 }, 410 { 411 name: "incorrect issue_99", 412 inputTestData: incorrectIssue99Path, 413 wantCorrectData: correctIssue99Path, 414 }, 415 { 416 name: "do nothing against inner elements on the same line. Fix https://github.com/yoheimuta/protolint/issues/139", 417 inputTestData: incorrectIssue139Path, 418 wantCorrectData: correctIssue139Path, 419 }, 420 { 421 name: "insert linebreaks against inner elements on the same line. Fix https://github.com/yoheimuta/protolint/issues/139", 422 inputTestData: incorrectIssue139Path, 423 inputInsertNewline: true, 424 wantCorrectData: correctIssue139InsertPath, 425 }, 426 } 427 428 for _, test := range tests { 429 test := test 430 t.Run(test.name, func(t *testing.T) { 431 rule_to_test := rules.NewIndentRule( 432 rule.SeverityError, 433 space2, 434 !test.inputInsertNewline, 435 true, 436 ) 437 438 proto, err := file.NewProtoFile(test.inputTestData.FilePath, test.inputTestData.FilePath).Parse(false) 439 if err != nil { 440 t.Errorf(err.Error()) 441 return 442 } 443 444 _, err = rule_to_test.Apply(proto) 445 if err != nil { 446 t.Errorf("got err %v, but want nil", err) 447 return 448 } 449 450 got, err := test.inputTestData.Data() 451 if !reflect.DeepEqual(got, test.wantCorrectData.OriginData) { 452 t.Errorf( 453 "got %s(%v), but want %s(%v)", 454 string(got), got, 455 string(test.wantCorrectData.OriginData), test.wantCorrectData.OriginData, 456 ) 457 } 458 459 // restore the file 460 defer func() { 461 err = test.inputTestData.Restore() 462 if err != nil { 463 t.Errorf("got err %v", err) 464 } 465 }() 466 467 // check whether the modified content can pass the lint in the end. 468 ruleOnlyCheck := rules.NewIndentRule( 469 rule.SeverityError, 470 space2, 471 !test.inputInsertNewline, 472 false, 473 ) 474 proto, err = file.NewProtoFile(test.inputTestData.FilePath, test.inputTestData.FilePath).Parse(false) 475 if err != nil { 476 t.Errorf(err.Error()) 477 return 478 } 479 gotCheck, err := ruleOnlyCheck.Apply(proto) 480 if err != nil { 481 t.Errorf("got err %v, but want nil", err) 482 return 483 } 484 if 0 < len(gotCheck) { 485 t.Errorf("got failures %v, but want no failures", gotCheck) 486 return 487 } 488 }) 489 } 490 }