github.com/ianlewis/go-gitignore@v0.1.1-0.20231110021210-4a0f15cbd56f/parser_test.go (about)

     1  // Copyright 2016 Denormal Limited
     2  // Copyright 2023 Google LLC
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //      http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package gitignore_test
    17  
    18  import (
    19  	"strings"
    20  	"testing"
    21  
    22  	"github.com/ianlewis/go-gitignore"
    23  )
    24  
    25  type parsetest struct {
    26  	good     int
    27  	bad      int
    28  	position []gitignore.Position
    29  	failures []gitignore.Error
    30  	errors   func(gitignore.Error) bool
    31  } // parsetest{}
    32  
    33  // TestParser tests the behaviour of gitignore.Parser
    34  func TestParser(t *testing.T) {
    35  	_test := &parsetest{good: _GITPATTERNS, bad: _GITBADPATTERNS}
    36  	_test.position = make([]gitignore.Position, 0)
    37  
    38  	// record the position of encountered errors
    39  	_test.errors = func(e gitignore.Error) bool {
    40  		_test.position = append(_test.position, e.Position())
    41  		return true
    42  	}
    43  
    44  	// run this parser test
    45  	parse(t, _test)
    46  } // TestParser()
    47  
    48  // TestParserError tests the behaviour of the gitignore.Parser with an error
    49  // handler that returns false on receiving an error
    50  func TestParserError(t *testing.T) {
    51  	_test := &parsetest{good: _GITPATTERNSFALSE, bad: _GITBADPATTERNSFALSE}
    52  	_test.position = make([]gitignore.Position, 0)
    53  
    54  	// record the position of encountered errors
    55  	//		- return false to stop parsing
    56  	_test.errors = func(e gitignore.Error) bool {
    57  		_test.position = append(_test.position, e.Position())
    58  		return false
    59  	}
    60  
    61  	// run this parser test
    62  	parse(t, _test)
    63  } // TestParserError()
    64  
    65  func TestParserInvalid(t *testing.T) {
    66  	_test := &parsetest{good: _GITINVALIDPATTERNS, bad: _GITINVALIDERRORS}
    67  	_test.position = make([]gitignore.Position, 0)
    68  
    69  	// record the position of encountered errors
    70  	_test.errors = func(e gitignore.Error) bool {
    71  		_test.position = append(_test.position, e.Position())
    72  		_test.failures = append(_test.failures, e)
    73  		return true
    74  	}
    75  
    76  	// run this parser test
    77  	invalidparse(t, _test)
    78  } // TestParserInvalid()
    79  
    80  func TestParserInvalidFalse(t *testing.T) {
    81  	_test := &parsetest{
    82  		good: _GITINVALIDPATTERNSFALSE,
    83  		bad:  _GITINVALIDERRORSFALSE,
    84  	}
    85  	_test.position = make([]gitignore.Position, 0)
    86  
    87  	// record the position of encountered errors
    88  	_test.errors = func(e gitignore.Error) bool {
    89  		_test.position = append(_test.position, e.Position())
    90  		_test.failures = append(_test.failures, e)
    91  		return false
    92  	}
    93  
    94  	// run this parser test
    95  	invalidparse(t, _test)
    96  } // TestParserInvalidFalse()
    97  
    98  func parse(t *testing.T, test *parsetest) {
    99  	// create a temporary .gitignore
   100  	_buffer, _err := buffer(_GITIGNORE)
   101  	if _err != nil {
   102  		t.Fatalf("unable to create temporary .gitignore: %s", _err.Error())
   103  	}
   104  
   105  	// ensure we have a non-nil Parser instance
   106  	_parser := gitignore.NewParser(_buffer, test.errors)
   107  	if _parser == nil {
   108  		t.Error("expected non-nil Parser instance; nil found")
   109  	}
   110  
   111  	// before we parse, what position do we have?
   112  	_position := _parser.Position()
   113  	if !coincident(_position, _BEGINNING) {
   114  		t.Errorf(
   115  			"beginning position mismatch; expected %s, got %s",
   116  			pos(_BEGINNING), pos(_position),
   117  		)
   118  	}
   119  
   120  	// attempt to parse the .gitignore
   121  	_patterns := _parser.Parse()
   122  
   123  	// ensure we encountered the expected bad patterns
   124  	if len(test.position) != test.bad {
   125  		t.Errorf(
   126  			"parse error mismatch; expected %d errors, got %d",
   127  			test.bad, len(test.position),
   128  		)
   129  	} else {
   130  		// ensure the bad pattern positions are correct
   131  		for _i := 0; _i < test.bad; _i++ {
   132  			_got := test.position[_i]
   133  			_expected := _GITBADPOSITION[_i]
   134  
   135  			if !coincident(_got, _expected) {
   136  				t.Errorf(
   137  					"bad pattern position mismatch; expected %q, got %q",
   138  					pos(_expected), pos(_got),
   139  				)
   140  			}
   141  		}
   142  	}
   143  
   144  	// ensure we encountered the right number of good patterns
   145  	if len(_patterns) != test.good {
   146  		t.Errorf(
   147  			"parse pattern mismatch; expected %d patterns, got %d",
   148  			test.good, len(_patterns),
   149  		)
   150  		return
   151  	}
   152  
   153  	// ensure the good pattern positions are correct
   154  	for _i := 0; _i < len(_patterns); _i++ {
   155  		_got := _patterns[_i].Position()
   156  		_expected := _GITPOSITION[_i]
   157  
   158  		if !coincident(_got, _expected) {
   159  			t.Errorf(
   160  				"pattern position mismatch; expected %q, got %q",
   161  				pos(_expected), pos(_got),
   162  			)
   163  		}
   164  	}
   165  
   166  	// ensure the retrieved patterns are correct
   167  	//		- we check the string form of the pattern against the respective
   168  	//	      lines from the .gitignore
   169  	//		- we must special-case patterns that end in whitespace that
   170  	//		  can be ignored (i.e. it's not escaped)
   171  	_lines := strings.Split(_GITIGNORE, "\n")
   172  	for _i := 0; _i < len(_patterns); _i++ {
   173  		_pattern := _patterns[_i]
   174  		_got := _pattern.String()
   175  		_line := _pattern.Position().Line
   176  		_expected := _lines[_line-1]
   177  
   178  		if _got != _expected {
   179  			// if the two strings aren't the same, then check to see if
   180  			// the difference is trailing whitespace
   181  			//		- the expected string may have whitespace, while the
   182  			//		  pattern string does not
   183  			//		- patterns have their trailing whitespace removed, so
   184  			//	    - we perform this check here, since it's possible for
   185  			//		  a pattern to end in a whitespace character (e.g. '\ ')
   186  			//		  and we don't want to be too heavy handed with our
   187  			//		  removal of whitespace
   188  			//		- only do this check for non-comments
   189  			if !strings.HasPrefix(_expected, "#") {
   190  				_new := strings.TrimRight(_expected, " \t")
   191  				if _new == _got {
   192  					continue
   193  				}
   194  			}
   195  			t.Errorf(
   196  				"pattern mismatch; expected %q, got %q at %s",
   197  				_expected, _got, pos(_pattern.Position()),
   198  			)
   199  		}
   200  	}
   201  } // parse()
   202  
   203  func invalidparse(t *testing.T, test *parsetest) {
   204  	_buffer, _err := buffer(_GITINVALID)
   205  	if _err != nil {
   206  		t.Fatalf("unable to create temporary .gitignore: %s", _err.Error())
   207  	}
   208  
   209  	// create the parser instance
   210  	_parser := gitignore.NewParser(_buffer, test.errors)
   211  	if _parser == nil {
   212  		t.Error("expected non-nil Parser instance; nil found")
   213  	}
   214  
   215  	// attempt to parse the .gitignore
   216  	_patterns := _parser.Parse()
   217  
   218  	// ensure we have the correct number of errors encountered
   219  	if len(test.failures) != test.bad {
   220  		t.Fatalf(
   221  			"unexpected invalid parse errors; expected %d, got %d",
   222  			test.bad, len(test.failures),
   223  		)
   224  	} else {
   225  		for _i := 0; _i < test.bad; _i++ {
   226  			_expected := _GITINVALIDERROR[_i]
   227  			_got := test.failures[_i]
   228  
   229  			// is this error the same as expected?
   230  			//			if !_got.Is(_expected) {
   231  			if _got.Underlying() != _expected {
   232  				t.Fatalf(
   233  					"unexpected invalid parse error; expected %q, got %q",
   234  					_expected.Error(), _got.Error(),
   235  				)
   236  			}
   237  		}
   238  	}
   239  
   240  	// ensure we have the correct number of patterns
   241  	if len(_patterns) != test.good {
   242  		t.Fatalf(
   243  			"unexpected invalid parse patterns; expected %d, got %d",
   244  			test.good, len(_patterns),
   245  		)
   246  	} else {
   247  		for _i := 0; _i < test.good; _i++ {
   248  			_expected := _GITINVALIDPATTERN[_i]
   249  			_got := _patterns[_i]
   250  
   251  			// is this pattern the same as expected?
   252  			if _got.String() != _expected {
   253  				t.Fatalf(
   254  					"unexpected invalid parse pattern; expected %q, got %q",
   255  					_expected, _got,
   256  				)
   257  			}
   258  		}
   259  	}
   260  } // invalidparse()