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()