View Javadoc
1   /*
2    * Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.ignore;
11  
12  import static org.eclipse.jgit.ignore.internal.Strings.split;
13  import static org.junit.Assert.assertArrayEquals;
14  import static org.junit.Assert.assertFalse;
15  import static org.junit.Assert.assertTrue;
16  import static org.junit.Assert.fail;
17  
18  import org.junit.Before;
19  import org.junit.Test;
20  
21  public class FastIgnoreRuleTest {
22  
23  	private boolean pathMatch;
24  
25  	@Before
26  	public void setup() {
27  		pathMatch = false;
28  	}
29  
30  	@Test
31  	public void testSimpleCharClass() {
32  		assertMatched("][a]", "]a");
33  		assertMatched("[a]", "a");
34  		assertMatched("][a]", "]a");
35  		assertMatched("[a]", "a/");
36  		assertMatched("[a]", "a/b");
37  
38  		assertMatched("[a]", "b/a");
39  		assertMatched("[a]", "b/a/");
40  		assertMatched("[a]", "b/a/b");
41  
42  		assertMatched("[a]", "/a/");
43  		assertMatched("[a]", "/a/b");
44  
45  		assertMatched("[a]", "c/a/b");
46  		assertMatched("[a]", "c/b/a");
47  
48  		assertMatched("/[a]", "a");
49  		assertMatched("/[a]", "a/");
50  		assertMatched("/[a]", "a/b");
51  		assertMatched("/[a]", "/a");
52  		assertMatched("/[a]", "/a/");
53  		assertMatched("/[a]", "/a/b");
54  
55  		assertMatched("[a]/", "a/");
56  		assertMatched("[a]/", "a/b");
57  		assertMatched("[a]/", "/a/");
58  		assertMatched("[a]/", "/a/b");
59  
60  		assertMatched("/[a]/", "a/");
61  		assertMatched("/[a]/", "a/b");
62  		assertMatched("/[a]/", "/a/");
63  		assertMatched("/[a]/", "/a/b");
64  	}
65  
66  	@Test
67  	public void testCharClass() {
68  		assertMatched("[v-z]", "x");
69  		assertMatched("[v-z]", "x/");
70  		assertMatched("[v-z]", "x/b");
71  
72  		assertMatched("[v-z]", "b/x");
73  		assertMatched("[v-z]", "b/x/");
74  		assertMatched("[v-z]", "b/x/b");
75  
76  		assertMatched("[v-z]", "/x/");
77  		assertMatched("[v-z]", "/x/b");
78  
79  		assertMatched("[v-z]", "c/x/b");
80  		assertMatched("[v-z]", "c/b/x");
81  
82  		assertMatched("/[v-z]", "x");
83  		assertMatched("/[v-z]", "x/");
84  		assertMatched("/[v-z]", "x/b");
85  		assertMatched("/[v-z]", "/x");
86  		assertMatched("/[v-z]", "/x/");
87  		assertMatched("/[v-z]", "/x/b");
88  
89  		assertMatched("[v-z]/", "x/");
90  		assertMatched("[v-z]/", "x/b");
91  		assertMatched("[v-z]/", "/x/");
92  		assertMatched("[v-z]/", "/x/b");
93  
94  		assertMatched("/[v-z]/", "x/");
95  		assertMatched("/[v-z]/", "x/b");
96  		assertMatched("/[v-z]/", "/x/");
97  		assertMatched("/[v-z]/", "/x/b");
98  	}
99  
100 	@Test
101 	public void testTrailingSpaces() {
102 		assertMatched("a ", "a");
103 		assertMatched("a/ ", "a/");
104 		assertMatched("a/ ", "a/b");
105 		assertMatched("a/\\ ", "a/ ");
106 		assertNotMatched("a/\\ ", "a/");
107 		assertNotMatched("a/\\ ", "a/b");
108 		assertNotMatched("/ ", "a");
109 	}
110 
111 	@Test
112 	public void testAsteriskDot() {
113 		assertMatched("*.a", ".a");
114 		assertMatched("*.a", "/.a");
115 		assertMatched("*.a", "a.a");
116 		assertMatched("*.a", "/b.a");
117 		assertMatched("*.a", "b.a");
118 		assertMatched("*.a", "/a/b.a");
119 		assertMatched("*.a", "/b/.a");
120 	}
121 
122 	@Test
123 	public void testAsteriskDotDoNotMatch() {
124 		assertNotMatched("*.a", ".ab");
125 		assertNotMatched("*.a", "/.ab");
126 		assertNotMatched("*.a", "/b.ba");
127 		assertNotMatched("*.a", "a.ab");
128 		assertNotMatched("*.a", "/b.ab");
129 		assertNotMatched("*.a", "b.ab");
130 		assertNotMatched("*.a", "/a/b.ab");
131 		assertNotMatched("*.a", "/b/.ab");
132 	}
133 
134 	@Test
135 	public void testDotAsteriskMatch() {
136 		assertMatched("a.*", "a.");
137 		assertMatched("a.*", "a./");
138 		assertMatched("a.*", "a.b");
139 
140 		assertMatched("a.*", "b/a.b");
141 		assertMatched("a.*", "b/a.b/");
142 		assertMatched("a.*", "b/a.b/b");
143 
144 		assertMatched("a.*", "/a.b/");
145 		assertMatched("a.*", "/a.b/b");
146 
147 		assertMatched("a.*", "c/a.b/b");
148 		assertMatched("a.*", "c/b/a.b");
149 
150 		assertMatched("/a.*", "a.b");
151 		assertMatched("/a.*", "a.b/");
152 		assertMatched("/a.*", "a.b/b");
153 		assertMatched("/a.*", "/a.b");
154 		assertMatched("/a.*", "/a.b/");
155 		assertMatched("/a.*", "/a.b/b");
156 
157 		assertMatched("/a.*/b", "a.b/b");
158 		assertMatched("/a.*/b", "/a.b/b");
159 		assertMatched("/a.*/b", "/a.bc/b");
160 		assertMatched("/a.*/b", "/a./b");
161 	}
162 
163 	@Test
164 	public void testAsterisk() {
165 		assertMatched("a*", "a");
166 		assertMatched("a*", "a/");
167 		assertMatched("a*", "ab");
168 
169 		assertMatched("a*", "b/ab");
170 		assertMatched("a*", "b/ab/");
171 		assertMatched("a*", "b/ab/b");
172 
173 		assertMatched("a*", "b/abc");
174 		assertMatched("a*", "b/abc/");
175 		assertMatched("a*", "b/abc/b");
176 
177 		assertMatched("a*", "/abc/");
178 		assertMatched("a*", "/abc/b");
179 
180 		assertMatched("a*", "c/abc/b");
181 		assertMatched("a*", "c/b/abc");
182 
183 		assertMatched("/a*", "abc");
184 		assertMatched("/a*", "abc/");
185 		assertMatched("/a*", "abc/b");
186 		assertMatched("/a*", "/abc");
187 		assertMatched("/a*", "/abc/");
188 		assertMatched("/a*", "/abc/b");
189 
190 		assertMatched("/a*/b", "abc/b");
191 		assertMatched("/a*/b", "/abc/b");
192 		assertMatched("/a*/b", "/abcd/b");
193 		assertMatched("/a*/b", "/a/b");
194 	}
195 
196 	@Test
197 	public void testQuestionmark() {
198 		assertMatched("a?", "ab");
199 		assertMatched("a?", "ab/");
200 
201 		assertMatched("a?", "b/ab");
202 		assertMatched("a?", "b/ab/");
203 		assertMatched("a?", "b/ab/b");
204 
205 		assertMatched("a?", "/ab/");
206 		assertMatched("a?", "/ab/b");
207 
208 		assertMatched("a?", "c/ab/b");
209 		assertMatched("a?", "c/b/ab");
210 
211 		assertMatched("/a?", "ab");
212 		assertMatched("/a?", "ab/");
213 		assertMatched("/a?", "ab/b");
214 		assertMatched("/a?", "/ab");
215 		assertMatched("/a?", "/ab/");
216 		assertMatched("/a?", "/ab/b");
217 
218 		assertMatched("/a?/b", "ab/b");
219 		assertMatched("/a?/b", "/ab/b");
220 	}
221 
222 	@Test
223 	public void testQuestionmarkDoNotMatch() {
224 		assertNotMatched("a?", "a/");
225 		assertNotMatched("a?", "abc");
226 		assertNotMatched("a?", "abc/");
227 
228 		assertNotMatched("a?", "b/abc");
229 		assertNotMatched("a?", "b/abc/");
230 
231 		assertNotMatched("a?", "/abc/");
232 		assertNotMatched("a?", "/abc/b");
233 
234 		assertNotMatched("a?", "c/abc/b");
235 		assertNotMatched("a?", "c/b/abc");
236 
237 		assertNotMatched("/a?", "abc");
238 		assertNotMatched("/a?", "abc/");
239 		assertNotMatched("/a?", "abc/b");
240 		assertNotMatched("/a?", "/abc");
241 		assertNotMatched("/a?", "/abc/");
242 		assertNotMatched("/a?", "/abc/b");
243 
244 		assertNotMatched("/a?/b", "abc/b");
245 		assertNotMatched("/a?/b", "/abc/b");
246 		assertNotMatched("/a?/b", "/a/b");
247 	}
248 
249 	@Test
250 	public void testSimplePatterns() {
251 		assertMatched("a", "a");
252 		assertMatched("a", "a/");
253 		assertMatched("a", "a/b");
254 
255 		assertMatched("a", "b/a");
256 		assertMatched("a", "b/a/");
257 		assertMatched("a", "b/a/b");
258 
259 		assertMatched("a", "/a/");
260 		assertMatched("a", "/a/b");
261 
262 		assertMatched("a", "c/a/b");
263 		assertMatched("a", "c/b/a");
264 
265 		assertMatched("/a", "a");
266 		assertMatched("/a", "a/");
267 		assertMatched("/a", "a/b");
268 		assertMatched("/a", "/a");
269 		assertMatched("/a", "/a/");
270 		assertMatched("/a", "/a/b");
271 
272 		assertMatched("a/", "a/");
273 		assertMatched("a/", "a/b");
274 		assertMatched("a/", "/a/");
275 		assertMatched("a/", "/a/b");
276 
277 		assertMatched("/a/", "a/");
278 		assertMatched("/a/", "a/b");
279 		assertMatched("/a/", "/a/");
280 		assertMatched("/a/", "/a/b");
281 
282 	}
283 
284 	@Test
285 	public void testSimplePatternsDoNotMatch() {
286 		assertNotMatched("ab", "a");
287 		assertNotMatched("abc", "a/");
288 		assertNotMatched("abc", "a/b");
289 
290 		assertNotMatched("a", "ab");
291 		assertNotMatched("a", "ba");
292 		assertNotMatched("a", "aa");
293 
294 		assertNotMatched("a", "b/ab");
295 		assertNotMatched("a", "b/ba");
296 
297 		assertNotMatched("a", "b/ba");
298 		assertNotMatched("a", "b/ab");
299 
300 		assertNotMatched("a", "b/ba/");
301 		assertNotMatched("a", "b/ba/b");
302 
303 		assertNotMatched("a", "/aa");
304 		assertNotMatched("a", "aa/");
305 		assertNotMatched("a", "/aa/");
306 
307 		assertNotMatched("/a", "b/a");
308 		assertNotMatched("/a", "/b/a/");
309 
310 		assertNotMatched("a/", "a");
311 		assertNotMatched("a/", "b/a");
312 
313 		assertNotMatched("/a/", "a");
314 		assertNotMatched("/a/", "/a");
315 		assertNotMatched("/a/", "b/a");
316 	}
317 
318 	@Test
319 	public void testSegments() {
320 		assertMatched("/a/b", "a/b");
321 		assertMatched("/a/b", "/a/b");
322 		assertMatched("/a/b", "/a/b/");
323 		assertMatched("/a/b", "/a/b/c");
324 
325 		assertMatched("a/b", "a/b");
326 		assertMatched("a/b", "/a/b");
327 		assertMatched("a/b", "/a/b/");
328 		assertMatched("a/b", "/a/b/c");
329 
330 		assertMatched("a/b/", "a/b/");
331 		assertMatched("a/b/", "/a/b/");
332 		assertMatched("a/b/", "/a/b/c");
333 	}
334 
335 	@Test
336 	public void testSegmentsDoNotMatch() {
337 		assertNotMatched("a/b", "/a/bb");
338 		assertNotMatched("a/b", "/aa/b");
339 		assertNotMatched("a/b", "a/bb");
340 		assertNotMatched("a/b", "aa/b");
341 		assertNotMatched("a/b", "c/aa/b");
342 		assertNotMatched("a/b", "c/a/bb");
343 		assertNotMatched("a/b/", "/a/b");
344 		assertNotMatched("/a/b/", "/a/b");
345 		assertNotMatched("/a/b", "c/a/b");
346 		assertNotMatched("/a/b/", "c/a/b");
347 		assertNotMatched("/a/b/", "c/a/b/");
348 
349 		// XXX why is it like this????
350 		assertNotMatched("a/b", "c/a/b");
351 		assertNotMatched("a/b", "c/a/b/");
352 		assertNotMatched("a/b", "c/a/b/c");
353 		assertNotMatched("a/b/", "c/a/b/");
354 		assertNotMatched("a/b/", "c/a/b/c");
355 	}
356 
357 	@Test
358 	public void testWildmatch() {
359 		assertMatched("**/a/b", "a/b");
360 		assertMatched("**/a/b", "c/a/b");
361 		assertMatched("**/a/b", "c/d/a/b");
362 		assertMatched("**/**/a/b", "c/d/a/b");
363 
364 		assertMatched("/**/a/b", "a/b");
365 		assertMatched("/**/a/b", "c/a/b");
366 		assertMatched("/**/a/b", "c/d/a/b");
367 		assertMatched("/**/**/a/b", "c/d/a/b");
368 
369 		assertMatched("a/b/**", "a/b/c");
370 		assertMatched("a/b/**", "a/b/c/d/");
371 		assertMatched("a/b/**/**", "a/b/c/d");
372 
373 		assertMatched("**/a/**/b", "c/d/a/b");
374 		assertMatched("**/a/**/b", "c/d/a/e/b");
375 		assertMatched("**/**/a/**/**/b", "c/d/a/e/b");
376 
377 		assertMatched("/**/a/**/b", "c/d/a/b");
378 		assertMatched("/**/a/**/b", "c/d/a/e/b");
379 		assertMatched("/**/**/a/**/**/b", "c/d/a/e/b");
380 
381 		assertMatched("a/**/b", "a/b");
382 		assertMatched("a/**/b", "a/c/b");
383 		assertMatched("a/**/b", "a/c/d/b");
384 		assertMatched("a/**/**/b", "a/c/d/b");
385 
386 		assertMatched("a/**/b/**/c", "a/c/b/d/c");
387 		assertMatched("a/**/**/b/**/**/c", "a/c/b/d/c");
388 
389 		assertMatched("**/", "a/");
390 		assertMatched("**/", "a/b");
391 		assertMatched("**/", "a/b/c");
392 		assertMatched("**/**/", "a/");
393 		assertMatched("**/**/", "a/b");
394 		assertMatched("**/**/", "a/b/");
395 		assertMatched("**/**/", "a/b/c");
396 		assertMatched("x/**/", "x/a/");
397 		assertMatched("x/**/", "x/a/b");
398 		assertMatched("x/**/", "x/a/b/");
399 		assertMatched("**/x/", "a/x/");
400 		assertMatched("**/x/", "a/b/x/");
401 	}
402 
403 	@Test
404 	public void testWildmatchDoNotMatch() {
405 		assertNotMatched("a/**", "a/");
406 		assertNotMatched("a/b/**", "a/b/");
407 		assertNotMatched("a/**", "a");
408 		assertNotMatched("a/b/**", "a/b");
409 		assertNotMatched("a/b/**/", "a/b");
410 		assertNotMatched("a/b/**/**", "a/b");
411 		assertNotMatched("**/a/b", "a/c/b");
412 		assertNotMatched("!/**/*.zip", "c/a/b.zip");
413 		assertNotMatched("!**/*.zip", "c/a/b.zip");
414 		assertNotMatched("a/**/b", "a/c/bb");
415 
416 		assertNotMatched("**/", "a");
417 		assertNotMatched("**/**/", "a");
418 		assertNotMatched("**/x/", "a/b/x");
419 	}
420 
421 	@SuppressWarnings("unused")
422 	@Test
423 	public void testSimpleRules() {
424 		try {
425 			new FastIgnoreRule(null);
426 			fail("Illegal input allowed!");
427 		} catch (IllegalArgumentException e) {
428 			// expected
429 		}
430 		assertFalse(new FastIgnoreRule("/").isMatch("/", false));
431 		assertFalse(new FastIgnoreRule("//").isMatch("//", false));
432 		assertFalse(new FastIgnoreRule("#").isMatch("#", false));
433 		assertFalse(new FastIgnoreRule("").isMatch("", false));
434 		assertFalse(new FastIgnoreRule(" ").isMatch(" ", false));
435 	}
436 
437 	@Test
438 	public void testSplit() {
439 		try {
440 			split("/", '/').toArray();
441 			fail("should not allow single slash");
442 		} catch (IllegalStateException e) {
443 			// expected
444 		}
445 
446 		assertArrayEquals(new String[] { "a", "b" }, split("a/b", '/')
447 				.toArray());
448 		assertArrayEquals(new String[] { "a", "b/" }, split("a/b/", '/')
449 				.toArray());
450 		assertArrayEquals(new String[] { "/a", "b" }, split("/a/b", '/')
451 				.toArray());
452 		assertArrayEquals(new String[] { "/a", "b/" }, split("/a/b/", '/')
453 				.toArray());
454 		assertArrayEquals(new String[] { "/a", "b", "c" }, split("/a/b/c", '/')
455 				.toArray());
456 		assertArrayEquals(new String[] { "/a", "b", "c/" },
457 				split("/a/b/c/", '/').toArray());
458 	}
459 
460 	@Test
461 	public void testPathMatch() {
462 		pathMatch = true;
463 		assertMatched("a", "a");
464 		assertMatched("a/", "a/");
465 		assertNotMatched("a/", "a/b");
466 
467 		assertMatched("**", "a");
468 		assertMatched("**", "a/");
469 		assertMatched("**", "a/b");
470 
471 		assertNotMatched("**/", "a");
472 		assertNotMatched("**/", "a/b");
473 		assertMatched("**/", "a/");
474 		assertMatched("**/", "a/b/");
475 
476 		assertNotMatched("x/**/", "x/a");
477 		assertNotMatched("x/**/", "x/a/b");
478 		assertMatched("x/**/", "x/a/");
479 		assertMatched("x/**/", "x/y/a/");
480 	}
481 
482 	@Test
483 	public void testFileNameWithLineTerminator() {
484 		assertMatched("a?", "a\r");
485 		assertMatched("a?", "dir/a\r");
486 		assertMatched("a?", "a\r/file");
487 		assertMatched("*a", "\ra");
488 		assertMatched("dir/*a*", "dir/\ra\r");
489 	}
490 
491 	private void assertMatched(String pattern, String path) {
492 		boolean match = match(pattern, path);
493 		String result = path + " is " + (match ? "ignored" : "not ignored")
494 				+ " via '" + pattern + "' rule";
495 		if (!match) {
496 			System.err.println(result);
497 		}
498 		assertTrue("Expected a match for: " + pattern + " with: " + path,
499 					match);
500 
501 		if (pattern.startsWith("!")) {
502 			pattern = pattern.substring(1);
503 		} else {
504 			pattern = "!" + pattern;
505 		}
506 		match = match(pattern, path);
507 		assertFalse("Expected no match for: " + pattern + " with: " + path,
508 				match);
509 	}
510 
511 	private void assertNotMatched(String pattern, String path) {
512 		boolean match = match(pattern, path);
513 		String result = path + " is " + (match ? "ignored" : "not ignored")
514 				+ " via '" + pattern + "' rule";
515 		if (match) {
516 			System.err.println(result);
517 		}
518 		assertFalse("Expected no match for: " + pattern + " with: " + path,
519 					match);
520 
521 		if (pattern.startsWith("!")) {
522 			pattern = pattern.substring(1);
523 		} else {
524 			pattern = "!" + pattern;
525 		}
526 		match = match(pattern, path);
527 		assertTrue("Expected a match for: " + pattern + " with: " + path,
528 					match);
529 	}
530 
531 	/**
532 	 * Check for a match. If target ends with "/", match will assume that the
533 	 * target is meant to be a directory.
534 	 *
535 	 * @param pattern
536 	 *            Pattern as it would appear in a .gitignore file
537 	 * @param target
538 	 *            Target file path relative to repository's GIT_DIR
539 	 * @return Result of {@link FastIgnoreRule#isMatch(String, boolean)}
540 	 */
541 	private boolean match(String pattern, String target) {
542 		boolean isDirectory = target.endsWith("/");
543 		FastIgnoreRule r = new FastIgnoreRule(pattern);
544 		// If speed of this test is ever an issue, we can use a presetRule field
545 		// to avoid recompiling a pattern each time.
546 		boolean match = r.isMatch(target, isDirectory, pathMatch);
547 		if (r.getNegation())
548 			match = !match;
549 		return match;
550 	}
551 }