github.com/yoheimuta/protolint@v0.49.8-0.20240515023657-4ecaebb7575d/linter/disablerule/interpreter_test.go (about)

     1  package disablerule_test
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/yoheimuta/protolint/linter/disablerule"
     8  
     9  	"github.com/yoheimuta/go-protoparser/v4/parser"
    10  )
    11  
    12  func TestInterpreter_Interpret(t *testing.T) {
    13  	type inOut struct {
    14  		name                string
    15  		inputComments       []*parser.Comment
    16  		inputInlineComments []*parser.Comment
    17  		wantIsDisabled      bool
    18  	}
    19  	tests := []struct {
    20  		name        string
    21  		inputRuleID string
    22  		inOuts      []inOut
    23  	}{
    24  		{
    25  			name:        "disable:next comments skip ENUM_FIELD_NAMES_UPPER_SNAKE_CASE",
    26  			inputRuleID: "ENUM_FIELD_NAMES_UPPER_SNAKE_CASE",
    27  			inOuts: []inOut{
    28  				{
    29  					name: "rule is enabled when there are no comments",
    30  				},
    31  				{
    32  					name: "rule is enabled when there are no correct disable:next comments",
    33  					inputComments: []*parser.Comment{
    34  						{
    35  							Raw: `// disable:next ENUM_FIELD_NAMES_UPPER_SNAKE_CASE`,
    36  						},
    37  					},
    38  				},
    39  				{
    40  					name: "rule is enabled when the ruleID does not match it",
    41  					inputComments: []*parser.Comment{
    42  						{
    43  							Raw: `// protolint:disable:next ENUM_NAMES_UPPER_CAMEL_CASE`,
    44  						},
    45  					},
    46  				},
    47  				{
    48  					name: "rule is disabled when there is a disable:next comment with a ruleID",
    49  					inputComments: []*parser.Comment{
    50  						{
    51  							Raw: `// protolint:disable:next ENUM_FIELD_NAMES_UPPER_SNAKE_CASE`,
    52  						},
    53  					},
    54  					wantIsDisabled: true,
    55  				},
    56  				{
    57  					name: "rule is disabled when there is a disable:next c-style comment with a ruleID",
    58  					inputComments: []*parser.Comment{
    59  						{
    60  							Raw: `/*
    61  protolint:disable:next ENUM_FIELD_NAMES_UPPER_SNAKE_CASE
    62  */`,
    63  						},
    64  					},
    65  					wantIsDisabled: true,
    66  				},
    67  				{
    68  					name: "rule is disabled when there are disable:next comments with ruleIDs",
    69  					inputComments: []*parser.Comment{
    70  						{
    71  							Raw: `// protolint:disable:next ENUM_FIELD_NAMES_UPPER_SNAKE_CASE`,
    72  						},
    73  						{
    74  							Raw: `// protolint:disable:next ENUM_NAMES_UPPER_CAMEL_CASE`,
    75  						},
    76  					},
    77  					wantIsDisabled: true,
    78  				},
    79  			},
    80  		},
    81  		{
    82  			name:        "disable:next comments skip SERVICE_NAMES_UPPER_CAMEL_CASE",
    83  			inputRuleID: "SERVICE_NAMES_UPPER_CAMEL_CASE",
    84  			inOuts: []inOut{
    85  				{
    86  					name: "rule is enabled when the ruleID does not match it",
    87  					inputComments: []*parser.Comment{
    88  						{
    89  							Raw: `// protolint:disable:next ENUM_NAMES_UPPER_CAMEL_CASE`,
    90  						},
    91  					},
    92  				},
    93  				{
    94  					name: "rule is disabled when there is a disable:next comment with a ruleID",
    95  					inputComments: []*parser.Comment{
    96  						{
    97  							Raw: `// protolint:disable:next SERVICE_NAMES_UPPER_CAMEL_CASE`,
    98  						},
    99  					},
   100  					wantIsDisabled: true,
   101  				},
   102  			},
   103  		},
   104  		{
   105  			name:        "disable:this comment skips SERVICE_NAMES_UPPER_CAMEL_CASE",
   106  			inputRuleID: "SERVICE_NAMES_UPPER_CAMEL_CASE",
   107  			inOuts: []inOut{
   108  				{
   109  					name: "rule is enabled when the ruleID does not match it",
   110  					inputInlineComments: []*parser.Comment{
   111  						{
   112  							Raw: `// protolint:disable:this ENUM_NAMES_UPPER_CAMEL_CASE`,
   113  						},
   114  					},
   115  				},
   116  				{
   117  					name: "rule is disabled when there is a disable:this comment with a ruleID",
   118  					inputInlineComments: []*parser.Comment{
   119  						{
   120  							Raw: `// protolint:disable:this SERVICE_NAMES_UPPER_CAMEL_CASE`,
   121  						},
   122  					},
   123  					wantIsDisabled: true,
   124  				},
   125  			},
   126  		},
   127  		{
   128  			name:        "disable SERVICE_NAMES_UPPER_CAMEL_CASE",
   129  			inputRuleID: "SERVICE_NAMES_UPPER_CAMEL_CASE",
   130  			inOuts: []inOut{
   131  				{
   132  					name: "rule is not disabled when there is a disable comment with another ruleID",
   133  					inputComments: []*parser.Comment{
   134  						{
   135  							Raw: `// protolint:disable ENUM_FIELD_NAMES_UPPER_SNAKE_CASE`,
   136  						},
   137  					},
   138  				},
   139  				{
   140  					name: "rule is disabled when there is a disable comment with a ruleID",
   141  					inputComments: []*parser.Comment{
   142  						{
   143  							Raw: `// protolint:disable SERVICE_NAMES_UPPER_CAMEL_CASE`,
   144  						},
   145  					},
   146  					wantIsDisabled: true,
   147  				},
   148  				{
   149  					name:           "rule is always disabled after a disable comment",
   150  					wantIsDisabled: true,
   151  				},
   152  				{
   153  					name: "rule is disabled when there is a disable:next comment with a ruleID",
   154  					inputComments: []*parser.Comment{
   155  						{
   156  							Raw: `// protolint:disable:next SERVICE_NAMES_UPPER_CAMEL_CASE`,
   157  						},
   158  					},
   159  					wantIsDisabled: true,
   160  				},
   161  				{
   162  					name:           "rule is always disabled after a disable comment",
   163  					wantIsDisabled: true,
   164  				},
   165  			},
   166  		},
   167  		{
   168  			name:        "enable SERVICE_NAMES_UPPER_CAMEL_CASE",
   169  			inputRuleID: "SERVICE_NAMES_UPPER_CAMEL_CASE",
   170  			inOuts: []inOut{
   171  				{
   172  					name: "rule is disabled when there is a disable comment with a ruleID",
   173  					inputComments: []*parser.Comment{
   174  						{
   175  							Raw: `// protolint:disable SERVICE_NAMES_UPPER_CAMEL_CASE`,
   176  						},
   177  					},
   178  					wantIsDisabled: true,
   179  				},
   180  				{
   181  					name: "rule is not enabled when there is an enable comment with another ruleID",
   182  					inputComments: []*parser.Comment{
   183  						{
   184  							Raw: `// protolint:enable ENUM_FIELD_NAMES_UPPER_SNAKE_CASE`,
   185  						},
   186  					},
   187  					wantIsDisabled: true,
   188  				},
   189  				{
   190  					name: "rule is enabled when there is an enable comment with a same ruleID",
   191  					inputComments: []*parser.Comment{
   192  						{
   193  							Raw: `// protolint:enable SERVICE_NAMES_UPPER_CAMEL_CASE`,
   194  						},
   195  					},
   196  				},
   197  				{
   198  					name: "rule is always enabled after an enable comment",
   199  				},
   200  				{
   201  					name: "rule is disabled when there is a disable:next comment with a ruleID",
   202  					inputComments: []*parser.Comment{
   203  						{
   204  							Raw: `// protolint:disable:next SERVICE_NAMES_UPPER_CAMEL_CASE`,
   205  						},
   206  					},
   207  					wantIsDisabled: true,
   208  				},
   209  				{
   210  					name: "rule is always enabled after an enable comment and a disable:next comment",
   211  				},
   212  			},
   213  		},
   214  	}
   215  
   216  	for _, test := range tests {
   217  		test := test
   218  		t.Run(test.name, func(t *testing.T) {
   219  			interpreter := disablerule.NewInterpreter(test.inputRuleID)
   220  
   221  			for _, expect := range test.inOuts {
   222  				got := interpreter.Interpret(expect.inputComments, expect.inputInlineComments...)
   223  				if got != expect.wantIsDisabled {
   224  					t.Errorf("[%s] got %v, but want %v", expect.name, got, expect.wantIsDisabled)
   225  				}
   226  			}
   227  		})
   228  	}
   229  }
   230  
   231  func TestInterpreter_CallEachIfValid(t *testing.T) {
   232  	type outputType struct {
   233  		index int
   234  		line  string
   235  	}
   236  	tests := []struct {
   237  		name            string
   238  		inputRuleID     string
   239  		inputLines      []string
   240  		wantOutputLines []outputType
   241  	}{
   242  		{
   243  			name:        "All lines are valid, so the function is called for each line.",
   244  			inputRuleID: "MAX_LINE_LENGTH",
   245  			inputLines: []string{
   246  				`enum enumAllowingAlias {`,
   247  				`// disable:next MAX_LINE_LENGTH`,
   248  				`option allow_alias = true;`,
   249  				`}`,
   250  			},
   251  			wantOutputLines: []outputType{
   252  				{
   253  					index: 0,
   254  					line:  `enum enumAllowingAlias {`,
   255  				},
   256  				{
   257  					index: 1,
   258  					line:  `// disable:next MAX_LINE_LENGTH`,
   259  				},
   260  				{
   261  					index: 2,
   262  					line:  `option allow_alias = true;`,
   263  				},
   264  				{
   265  					index: 3,
   266  					line:  `}`,
   267  				},
   268  			},
   269  		},
   270  		{
   271  			name:        "protolint:disable:next works.",
   272  			inputRuleID: "MAX_LINE_LENGTH",
   273  			inputLines: []string{
   274  				`enum enumAllowingAlias {`,
   275  				`// protolint:disable:next MAX_LINE_LENGTH`,
   276  				`option allow_alias = true;`,
   277  				`}`,
   278  			},
   279  			wantOutputLines: []outputType{
   280  				{
   281  					index: 0,
   282  					line:  `enum enumAllowingAlias {`,
   283  				},
   284  				{
   285  					index: 1,
   286  					line:  `// protolint:disable:next MAX_LINE_LENGTH`,
   287  				},
   288  				{
   289  					index: 3,
   290  					line:  `}`,
   291  				},
   292  			},
   293  		},
   294  		{
   295  			name:        "protolint:disable:this works.",
   296  			inputRuleID: "MAX_LINE_LENGTH",
   297  			inputLines: []string{
   298  				`enum enumAllowingAlias { // protolint:disable:this MAX_LINE_LENGTH`,
   299  				`option allow_alias = true;`,
   300  				`}`,
   301  			},
   302  			wantOutputLines: []outputType{
   303  				{
   304  					index: 1,
   305  					line:  `option allow_alias = true;`,
   306  				},
   307  				{
   308  					index: 2,
   309  					line:  `}`,
   310  				},
   311  			},
   312  		},
   313  		{
   314  			name:        "protolint:disable and protolint:enable works",
   315  			inputRuleID: "MAX_LINE_LENGTH",
   316  			inputLines: []string{
   317  				`enum enumAllowingAlias {`,
   318  				`// protolint:disable MAX_LINE_LENGTH`,
   319  				`option allow_alias = true;`,
   320  				`UNKNOWN = 0;`,
   321  				`// protolint:enable MAX_LINE_LENGTH`,
   322  				`STARTED = 1;`,
   323  				`}`,
   324  			},
   325  			wantOutputLines: []outputType{
   326  				{
   327  					index: 0,
   328  					line:  `enum enumAllowingAlias {`,
   329  				},
   330  				{
   331  					index: 4,
   332  					line:  `// protolint:enable MAX_LINE_LENGTH`,
   333  				},
   334  				{
   335  					index: 5,
   336  					line:  `STARTED = 1;`,
   337  				},
   338  				{
   339  					index: 6,
   340  					line:  `}`,
   341  				},
   342  			},
   343  		},
   344  		{
   345  			name:        "the mix of protolint:disable commands works",
   346  			inputRuleID: "MAX_LINE_LENGTH",
   347  			inputLines: []string{
   348  				`// protolint:disable:next MAX_LINE_LENGTH`,
   349  				`enum enumAllowingAlias {`,
   350  				`// protolint:disable MAX_LINE_LENGTH`,
   351  				`option allow_alias = true; // protolint:disable:this MAX_LINE_LENGTH`,
   352  				`UNKNOWN = 0;`,
   353  				`// protolint:enable MAX_LINE_LENGTH`,
   354  				`STARTED = 1;`,
   355  				`RUNNING = 2; // protolint:disable:this MAX_LINE_LENGTH`,
   356  				`// protolint:disable:next MAX_LINE_LENGTH`,
   357  				`STOPPED = 3;`,
   358  				`}`,
   359  			},
   360  			wantOutputLines: []outputType{
   361  				{
   362  					index: 0,
   363  					line:  `// protolint:disable:next MAX_LINE_LENGTH`,
   364  				},
   365  				{
   366  					index: 5,
   367  					line:  `// protolint:enable MAX_LINE_LENGTH`,
   368  				},
   369  				{
   370  					index: 6,
   371  					line:  `STARTED = 1;`,
   372  				},
   373  				{
   374  					index: 8,
   375  					line:  `// protolint:disable:next MAX_LINE_LENGTH`,
   376  				},
   377  				{
   378  					index: 10,
   379  					line:  `}`,
   380  				},
   381  			},
   382  		},
   383  		{
   384  			name:        "protolint:disable doesn't collide with protolint:disable that has a different ruleID",
   385  			inputRuleID: "MAX_LINE_LENGTH",
   386  			inputLines: []string{
   387  				`// protolint:disable MAX_LINE_LENGTH`,
   388  				`syntax = "proto3";`,
   389  				`/** Schnufte test enumeration. */`,
   390  				`enum TestEnum {`,
   391  				`  TEST_ENUM_UNSPECIFIED = 0; // Unspecified.`,
   392  				`  FOO = 1;                               // protolint:disable:this ENUM_FIELD_NAMES_PREFIX`,
   393  				`}`,
   394  			},
   395  		},
   396  	}
   397  
   398  	for _, test := range tests {
   399  		test := test
   400  		t.Run(test.name, func(t *testing.T) {
   401  			interpreter := disablerule.NewInterpreter(test.inputRuleID)
   402  
   403  			var got []outputType
   404  			interpreter.CallEachIfValid(test.inputLines, func(index int, line string) {
   405  				got = append(got, outputType{
   406  					index: index,
   407  					line:  line,
   408  				})
   409  			})
   410  			if !reflect.DeepEqual(got, test.wantOutputLines) {
   411  				t.Errorf("got %v, but want %v", got, test.wantOutputLines)
   412  			}
   413  		})
   414  	}
   415  }