github.com/yoheimuta/protolint@v0.49.8-0.20240515023657-4ecaebb7575d/linter/visitor/hasExtendedVisitor_test.go (about) 1 package visitor_test 2 3 import ( 4 "reflect" 5 "testing" 6 7 "github.com/yoheimuta/go-protoparser/v4/parser/meta" 8 9 "github.com/yoheimuta/protolint/internal/linter/file" 10 "github.com/yoheimuta/protolint/internal/setting_test" 11 "github.com/yoheimuta/protolint/internal/util_test" 12 "github.com/yoheimuta/protolint/linter/autodisable" 13 "github.com/yoheimuta/protolint/linter/visitor" 14 15 "github.com/yoheimuta/protolint/linter/report" 16 17 "github.com/yoheimuta/go-protoparser/v4/parser" 18 ) 19 20 type testVisitor struct { 21 *visitor.BaseAddVisitor 22 next bool 23 } 24 25 func (v *testVisitor) VisitMessage(message *parser.Message) bool { 26 v.AddFailuref(message.Meta.Pos, "Test Message") 27 return v.next 28 } 29 30 type testVisitorInvalidEnumField struct { 31 *visitor.BaseAddVisitor 32 next bool 33 } 34 35 func (v *testVisitorInvalidEnumField) VisitEnumField(field *parser.EnumField) bool { 36 v.AddFailuref(field.Meta.Pos, "Failed field") 37 return v.next 38 } 39 40 func TestRunVisitor(t *testing.T) { 41 tests := []struct { 42 name string 43 inputVisitor *testVisitor 44 inputProto *parser.Proto 45 inputRuleID string 46 wantExistErr bool 47 wantFailures []report.Failure 48 }{ 49 { 50 name: "visit no messages", 51 inputVisitor: &testVisitor{ 52 BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), 53 }, 54 inputProto: &parser.Proto{ 55 Meta: &parser.ProtoMeta{Filename: ""}, 56 }, 57 }, 58 { 59 name: "visit a message", 60 inputVisitor: &testVisitor{ 61 BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), 62 }, 63 inputProto: &parser.Proto{ 64 Meta: &parser.ProtoMeta{Filename: ""}, 65 ProtoBody: []parser.Visitee{ 66 &parser.Message{ 67 Meta: meta.Meta{ 68 Pos: meta.Position{ 69 Filename: "example.proto", 70 Offset: 100, 71 Line: 10, 72 Column: 5, 73 }, 74 }, 75 }, 76 }, 77 }, 78 wantFailures: []report.Failure{ 79 report.Failuref( 80 meta.Position{ 81 Filename: "example.proto", 82 Offset: 100, 83 Line: 10, 84 Column: 5, 85 }, 86 "MESSAGE_NAMES_UPPER_CAMEL_CASE", 87 "Test Message", 88 ), 89 }, 90 }, 91 { 92 name: "visit messages", 93 inputVisitor: &testVisitor{ 94 BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), 95 }, 96 inputProto: &parser.Proto{ 97 Meta: &parser.ProtoMeta{Filename: ""}, 98 ProtoBody: []parser.Visitee{ 99 &parser.Message{ 100 Meta: meta.Meta{ 101 Pos: meta.Position{ 102 Filename: "example.proto", 103 Offset: 100, 104 Line: 10, 105 Column: 5, 106 }, 107 }, 108 }, 109 &parser.Message{ 110 Meta: meta.Meta{ 111 Pos: meta.Position{ 112 Filename: "example.proto", 113 Offset: 200, 114 Line: 20, 115 Column: 10, 116 }, 117 }, 118 }, 119 }, 120 }, 121 wantFailures: []report.Failure{ 122 report.Failuref( 123 meta.Position{ 124 Filename: "example.proto", 125 Offset: 100, 126 Line: 10, 127 Column: 5, 128 }, 129 "MESSAGE_NAMES_UPPER_CAMEL_CASE", 130 "Test Message", 131 ), 132 report.Failuref( 133 meta.Position{ 134 Filename: "example.proto", 135 Offset: 200, 136 Line: 20, 137 Column: 10, 138 }, 139 "MESSAGE_NAMES_UPPER_CAMEL_CASE", 140 "Test Message", 141 ), 142 }, 143 }, 144 { 145 name: "visit messages recursively", 146 inputVisitor: &testVisitor{ 147 BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), 148 next: true, 149 }, 150 inputProto: &parser.Proto{ 151 Meta: &parser.ProtoMeta{Filename: ""}, 152 ProtoBody: []parser.Visitee{ 153 &parser.Message{ 154 MessageBody: []parser.Visitee{ 155 &parser.Message{ 156 Meta: meta.Meta{ 157 Pos: meta.Position{ 158 Filename: "example.proto", 159 Offset: 200, 160 Line: 20, 161 Column: 10, 162 }, 163 }, 164 }, 165 }, 166 Meta: meta.Meta{ 167 Pos: meta.Position{ 168 Filename: "example.proto", 169 Offset: 100, 170 Line: 10, 171 Column: 5, 172 }, 173 }, 174 }, 175 }, 176 }, 177 wantFailures: []report.Failure{ 178 report.Failuref( 179 meta.Position{ 180 Filename: "example.proto", 181 Offset: 100, 182 Line: 10, 183 Column: 5, 184 }, 185 "MESSAGE_NAMES_UPPER_CAMEL_CASE", 186 "Test Message", 187 ), 188 report.Failuref( 189 meta.Position{ 190 Filename: "example.proto", 191 Offset: 200, 192 Line: 20, 193 Column: 10, 194 }, 195 "MESSAGE_NAMES_UPPER_CAMEL_CASE", 196 "Test Message", 197 ), 198 }, 199 }, 200 { 201 name: "visit a message. one is disabled.", 202 inputVisitor: &testVisitor{ 203 BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), 204 }, 205 inputProto: &parser.Proto{ 206 Meta: &parser.ProtoMeta{Filename: ""}, 207 ProtoBody: []parser.Visitee{ 208 &parser.Message{ 209 Meta: meta.Meta{ 210 Pos: meta.Position{ 211 Filename: "example.proto", 212 Offset: 100, 213 Line: 10, 214 Column: 5, 215 }, 216 }, 217 Comments: []*parser.Comment{ 218 { 219 Raw: `// protolint:disable:next MESSAGE_NAMES_UPPER_CAMEL_CASE`, 220 }, 221 }, 222 }, 223 &parser.Message{ 224 Meta: meta.Meta{ 225 Pos: meta.Position{ 226 Filename: "example.proto", 227 Offset: 200, 228 Line: 20, 229 Column: 10, 230 }, 231 }, 232 }, 233 }, 234 }, 235 inputRuleID: `MESSAGE_NAMES_UPPER_CAMEL_CASE`, 236 wantFailures: []report.Failure{ 237 report.Failuref( 238 meta.Position{ 239 Filename: "example.proto", 240 Offset: 200, 241 Line: 20, 242 Column: 10, 243 }, 244 "MESSAGE_NAMES_UPPER_CAMEL_CASE", 245 "Test Message", 246 ), 247 }, 248 }, 249 { 250 name: "visit a message. one is disabled by an inline comment.", 251 inputVisitor: &testVisitor{ 252 BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), 253 }, 254 inputProto: &parser.Proto{ 255 Meta: &parser.ProtoMeta{Filename: ""}, 256 ProtoBody: []parser.Visitee{ 257 &parser.Message{ 258 Meta: meta.Meta{ 259 Pos: meta.Position{ 260 Filename: "example.proto", 261 Offset: 100, 262 Line: 10, 263 Column: 5, 264 }, 265 }, 266 InlineComment: &parser.Comment{ 267 Raw: `// protolint:disable:this MESSAGE_NAMES_UPPER_CAMEL_CASE`, 268 }, 269 }, 270 &parser.Message{ 271 Meta: meta.Meta{ 272 Pos: meta.Position{ 273 Filename: "example.proto", 274 Offset: 200, 275 Line: 20, 276 Column: 10, 277 }, 278 }, 279 }, 280 }, 281 }, 282 inputRuleID: `MESSAGE_NAMES_UPPER_CAMEL_CASE`, 283 wantFailures: []report.Failure{ 284 report.Failuref( 285 meta.Position{ 286 Filename: "example.proto", 287 Offset: 200, 288 Line: 20, 289 Column: 10, 290 }, 291 "MESSAGE_NAMES_UPPER_CAMEL_CASE", 292 "Test Message", 293 ), 294 }, 295 }, 296 { 297 name: "visit messages. others are disabled.", 298 inputVisitor: &testVisitor{ 299 BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), 300 }, 301 inputProto: &parser.Proto{ 302 Meta: &parser.ProtoMeta{Filename: ""}, 303 ProtoBody: []parser.Visitee{ 304 &parser.Message{ 305 Meta: meta.Meta{ 306 Pos: meta.Position{ 307 Filename: "example.proto", 308 Offset: 100, 309 Line: 10, 310 Column: 5, 311 }, 312 }, 313 Comments: []*parser.Comment{ 314 { 315 Raw: `// protolint:disable MESSAGE_NAMES_UPPER_CAMEL_CASE`, 316 }, 317 }, 318 }, 319 &parser.Message{ 320 Meta: meta.Meta{ 321 Pos: meta.Position{ 322 Filename: "example.proto", 323 Offset: 200, 324 Line: 20, 325 Column: 10, 326 }, 327 }, 328 }, 329 &parser.Message{ 330 Meta: meta.Meta{ 331 Pos: meta.Position{ 332 Filename: "example.proto", 333 Offset: 300, 334 Line: 30, 335 Column: 15, 336 }, 337 }, 338 Comments: []*parser.Comment{ 339 { 340 Raw: `// protolint:enable MESSAGE_NAMES_UPPER_CAMEL_CASE`, 341 }, 342 }, 343 }, 344 }, 345 }, 346 inputRuleID: `MESSAGE_NAMES_UPPER_CAMEL_CASE`, 347 wantFailures: []report.Failure{ 348 report.Failuref( 349 meta.Position{ 350 Filename: "example.proto", 351 Offset: 300, 352 Line: 30, 353 Column: 15, 354 }, 355 "MESSAGE_NAMES_UPPER_CAMEL_CASE", 356 "Test Message", 357 ), 358 }, 359 }, 360 { 361 name: "visit messages. others are disabled by a last line comment.", 362 inputVisitor: &testVisitor{ 363 BaseAddVisitor: visitor.NewBaseAddVisitor("MESSAGE_NAMES_UPPER_CAMEL_CASE", "error"), 364 }, 365 inputProto: &parser.Proto{ 366 Meta: &parser.ProtoMeta{Filename: ""}, 367 ProtoBody: []parser.Visitee{ 368 &parser.Message{ 369 Meta: meta.Meta{ 370 Pos: meta.Position{ 371 Filename: "example.proto", 372 Offset: 100, 373 Line: 10, 374 Column: 5, 375 }, 376 }, 377 Comments: []*parser.Comment{ 378 { 379 Raw: `// protolint:disable MESSAGE_NAMES_UPPER_CAMEL_CASE`, 380 }, 381 }, 382 }, 383 &parser.Message{ 384 Meta: meta.Meta{ 385 Pos: meta.Position{ 386 Filename: "example.proto", 387 Offset: 200, 388 Line: 20, 389 Column: 10, 390 }, 391 }, 392 }, 393 &parser.Comment{ 394 Raw: `// protolint:enable MESSAGE_NAMES_UPPER_CAMEL_CASE`, 395 }, 396 &parser.Message{ 397 Meta: meta.Meta{ 398 Pos: meta.Position{ 399 Filename: "example.proto", 400 Offset: 300, 401 Line: 30, 402 Column: 15, 403 }, 404 }, 405 }, 406 }, 407 }, 408 inputRuleID: `MESSAGE_NAMES_UPPER_CAMEL_CASE`, 409 wantFailures: []report.Failure{ 410 report.Failuref( 411 meta.Position{ 412 Filename: "example.proto", 413 Offset: 300, 414 Line: 30, 415 Column: 15, 416 }, 417 "MESSAGE_NAMES_UPPER_CAMEL_CASE", 418 "Test Message", 419 ), 420 }, 421 }, 422 } 423 424 for _, test := range tests { 425 test := test 426 t.Run(test.name, func(t *testing.T) { 427 got, err := visitor.RunVisitor( 428 test.inputVisitor, 429 test.inputProto, 430 test.inputRuleID, 431 ) 432 433 if test.wantExistErr { 434 if err == nil { 435 t.Errorf("got err nil, but want err") 436 } 437 return 438 } 439 if err != nil { 440 t.Errorf("got err %v, but want nil", err) 441 return 442 } 443 444 if !reflect.DeepEqual(got, test.wantFailures) { 445 t.Errorf("got %v, but want %v", got, test.wantFailures) 446 } 447 }) 448 } 449 } 450 451 func TestRunVisitorAutoDisable(t *testing.T) { 452 tests := []struct { 453 name string 454 inputVisitor visitor.HasExtendedVisitor 455 inputFilename string 456 inputRuleID string 457 inputPlacementType autodisable.PlacementType 458 wantExistErr bool 459 wantFailureCount int 460 wantFilename string 461 }{ 462 { 463 name: "Do nothing in case of no failures", 464 inputVisitor: &testVisitor{ 465 BaseAddVisitor: visitor.NewBaseAddVisitor("ENUM_FIELD_NAMES_UPPER_SNAKE_CASE", "error"), 466 }, 467 inputFilename: "invalid.proto", 468 inputRuleID: "ENUM_FIELD_NAMES_UPPER_SNAKE_CASE", 469 inputPlacementType: autodisable.Next, 470 wantFilename: "invalid.proto", 471 }, 472 { 473 name: "Insert a disable:next comment", 474 inputVisitor: &testVisitorInvalidEnumField{ 475 BaseAddVisitor: visitor.NewBaseAddVisitor("ENUM_FIELD_NAMES_UPPER_SNAKE_CASE", "error"), 476 }, 477 inputFilename: "invalid.proto", 478 inputRuleID: "ENUM_FIELD_NAMES_UPPER_SNAKE_CASE", 479 inputPlacementType: autodisable.Next, 480 wantFailureCount: 1, 481 wantFilename: "disable_next.proto", 482 }, 483 { 484 name: "Insert a disable:this comment", 485 inputVisitor: &testVisitorInvalidEnumField{ 486 BaseAddVisitor: visitor.NewBaseAddVisitor("ENUM_FIELD_NAMES_UPPER_SNAKE_CASE", "error"), 487 }, 488 inputFilename: "invalid.proto", 489 inputRuleID: "ENUM_FIELD_NAMES_UPPER_SNAKE_CASE", 490 inputPlacementType: autodisable.ThisThenNext, 491 wantFailureCount: 1, 492 wantFilename: "disable_this.proto", 493 }, 494 } 495 496 for _, test := range tests { 497 test := test 498 t.Run(test.name, func(t *testing.T) { 499 input, err := util_test.NewTestData(setting_test.TestDataPath("visitor", test.inputFilename)) 500 if err != nil { 501 t.Errorf("got err %v", err) 502 return 503 } 504 505 want, err := util_test.NewTestData(setting_test.TestDataPath("visitor", test.wantFilename)) 506 if err != nil { 507 t.Errorf("got err %v", err) 508 return 509 } 510 511 proto, err := file.NewProtoFile(input.FilePath, input.FilePath).Parse(false) 512 if err != nil { 513 t.Errorf(err.Error()) 514 return 515 } 516 517 got, err := visitor.RunVisitorAutoDisable( 518 test.inputVisitor, 519 proto, 520 test.inputRuleID, 521 test.inputPlacementType, 522 ) 523 524 if test.wantExistErr { 525 if err == nil { 526 t.Errorf("got err nil, but want err") 527 } 528 return 529 } else if err != nil { 530 t.Errorf("got err %v, but want nil", err) 531 return 532 } 533 534 if len(got) != test.wantFailureCount { 535 t.Errorf("len(got) %v, but want %v", len(got), test.wantFailureCount) 536 } 537 538 got2, _ := input.Data() 539 if !reflect.DeepEqual(got2, want.OriginData) { 540 t.Errorf( 541 "got %s(%v), but want %s(%v)", 542 string(got2), got, 543 string(want.OriginData), want.OriginData, 544 ) 545 } 546 547 err = input.Restore() 548 if err != nil { 549 t.Errorf("got err %v", err) 550 } 551 552 }) 553 } 554 }