View Javadoc
1   /*
2    * Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  package org.eclipse.jgit.ignore;
44  
45  import static org.eclipse.jgit.ignore.internal.Strings.split;
46  import static org.junit.Assert.assertArrayEquals;
47  import static org.junit.Assert.assertFalse;
48  import static org.junit.Assert.assertTrue;
49  import static org.junit.Assert.fail;
50  
51  import org.junit.Test;
52  
53  public class FastIgnoreRuleTest {
54  
55  	@Test
56  	public void testSimpleCharClass() {
57  		assertMatched("[a]", "a");
58  		assertMatched("][a]", "]a");
59  		assertMatched("[a]", "a/");
60  		assertMatched("[a]", "a/b");
61  
62  		assertMatched("[a]", "b/a");
63  		assertMatched("[a]", "b/a/");
64  		assertMatched("[a]", "b/a/b");
65  
66  		assertMatched("[a]", "/a/");
67  		assertMatched("[a]", "/a/b");
68  
69  		assertMatched("[a]", "c/a/b");
70  		assertMatched("[a]", "c/b/a");
71  
72  		assertMatched("/[a]", "a");
73  		assertMatched("/[a]", "a/");
74  		assertMatched("/[a]", "a/b");
75  		assertMatched("/[a]", "/a");
76  		assertMatched("/[a]", "/a/");
77  		assertMatched("/[a]", "/a/b");
78  
79  		assertMatched("[a]/", "a/");
80  		assertMatched("[a]/", "a/b");
81  		assertMatched("[a]/", "/a/");
82  		assertMatched("[a]/", "/a/b");
83  
84  		assertMatched("/[a]/", "a/");
85  		assertMatched("/[a]/", "a/b");
86  		assertMatched("/[a]/", "/a/");
87  		assertMatched("/[a]/", "/a/b");
88  	}
89  
90  	@Test
91  	public void testCharClass() {
92  		assertMatched("[v-z]", "x");
93  		assertMatched("[v-z]", "x/");
94  		assertMatched("[v-z]", "x/b");
95  
96  		assertMatched("[v-z]", "b/x");
97  		assertMatched("[v-z]", "b/x/");
98  		assertMatched("[v-z]", "b/x/b");
99  
100 		assertMatched("[v-z]", "/x/");
101 		assertMatched("[v-z]", "/x/b");
102 
103 		assertMatched("[v-z]", "c/x/b");
104 		assertMatched("[v-z]", "c/b/x");
105 
106 		assertMatched("/[v-z]", "x");
107 		assertMatched("/[v-z]", "x/");
108 		assertMatched("/[v-z]", "x/b");
109 		assertMatched("/[v-z]", "/x");
110 		assertMatched("/[v-z]", "/x/");
111 		assertMatched("/[v-z]", "/x/b");
112 
113 		assertMatched("[v-z]/", "x/");
114 		assertMatched("[v-z]/", "x/b");
115 		assertMatched("[v-z]/", "/x/");
116 		assertMatched("[v-z]/", "/x/b");
117 
118 		assertMatched("/[v-z]/", "x/");
119 		assertMatched("/[v-z]/", "x/b");
120 		assertMatched("/[v-z]/", "/x/");
121 		assertMatched("/[v-z]/", "/x/b");
122 	}
123 
124 	@Test
125 	public void testAsteriskDot() {
126 		assertMatched("*.a", ".a");
127 		assertMatched("*.a", "/.a");
128 		assertMatched("*.a", "a.a");
129 		assertMatched("*.a", "/b.a");
130 		assertMatched("*.a", "b.a");
131 		assertMatched("*.a", "/a/b.a");
132 		assertMatched("*.a", "/b/.a");
133 	}
134 
135 	@Test
136 	public void testAsteriskDotDoNotMatch() {
137 		assertNotMatched("*.a", ".ab");
138 		assertNotMatched("*.a", "/.ab");
139 		assertNotMatched("*.a", "/b.ba");
140 		assertNotMatched("*.a", "a.ab");
141 		assertNotMatched("*.a", "/b.ab");
142 		assertNotMatched("*.a", "b.ab");
143 		assertNotMatched("*.a", "/a/b.ab");
144 		assertNotMatched("*.a", "/b/.ab");
145 	}
146 
147 	@Test
148 	public void testDotAsteriskMatch() {
149 		assertMatched("a.*", "a.");
150 		assertMatched("a.*", "a./");
151 		assertMatched("a.*", "a.b");
152 
153 		assertMatched("a.*", "b/a.b");
154 		assertMatched("a.*", "b/a.b/");
155 		assertMatched("a.*", "b/a.b/b");
156 
157 		assertMatched("a.*", "/a.b/");
158 		assertMatched("a.*", "/a.b/b");
159 
160 		assertMatched("a.*", "c/a.b/b");
161 		assertMatched("a.*", "c/b/a.b");
162 
163 		assertMatched("/a.*", "a.b");
164 		assertMatched("/a.*", "a.b/");
165 		assertMatched("/a.*", "a.b/b");
166 		assertMatched("/a.*", "/a.b");
167 		assertMatched("/a.*", "/a.b/");
168 		assertMatched("/a.*", "/a.b/b");
169 
170 		assertMatched("/a.*/b", "a.b/b");
171 		assertMatched("/a.*/b", "/a.b/b");
172 		assertMatched("/a.*/b", "/a.bc/b");
173 		assertMatched("/a.*/b", "/a./b");
174 	}
175 
176 	@Test
177 	public void testAsterisk() {
178 		assertMatched("a*", "a");
179 		assertMatched("a*", "a/");
180 		assertMatched("a*", "ab");
181 
182 		assertMatched("a*", "b/ab");
183 		assertMatched("a*", "b/ab/");
184 		assertMatched("a*", "b/ab/b");
185 
186 		assertMatched("a*", "b/abc");
187 		assertMatched("a*", "b/abc/");
188 		assertMatched("a*", "b/abc/b");
189 
190 		assertMatched("a*", "/abc/");
191 		assertMatched("a*", "/abc/b");
192 
193 		assertMatched("a*", "c/abc/b");
194 		assertMatched("a*", "c/b/abc");
195 
196 		assertMatched("/a*", "abc");
197 		assertMatched("/a*", "abc/");
198 		assertMatched("/a*", "abc/b");
199 		assertMatched("/a*", "/abc");
200 		assertMatched("/a*", "/abc/");
201 		assertMatched("/a*", "/abc/b");
202 
203 		assertMatched("/a*/b", "abc/b");
204 		assertMatched("/a*/b", "/abc/b");
205 		assertMatched("/a*/b", "/abcd/b");
206 		assertMatched("/a*/b", "/a/b");
207 	}
208 
209 	@Test
210 	public void testQuestionmark() {
211 		assertMatched("a?", "ab");
212 		assertMatched("a?", "ab/");
213 
214 		assertMatched("a?", "b/ab");
215 		assertMatched("a?", "b/ab/");
216 		assertMatched("a?", "b/ab/b");
217 
218 		assertMatched("a?", "/ab/");
219 		assertMatched("a?", "/ab/b");
220 
221 		assertMatched("a?", "c/ab/b");
222 		assertMatched("a?", "c/b/ab");
223 
224 		assertMatched("/a?", "ab");
225 		assertMatched("/a?", "ab/");
226 		assertMatched("/a?", "ab/b");
227 		assertMatched("/a?", "/ab");
228 		assertMatched("/a?", "/ab/");
229 		assertMatched("/a?", "/ab/b");
230 
231 		assertMatched("/a?/b", "ab/b");
232 		assertMatched("/a?/b", "/ab/b");
233 	}
234 
235 	@Test
236 	public void testQuestionmarkDoNotMatch() {
237 		assertNotMatched("a?", "a/");
238 		assertNotMatched("a?", "abc");
239 		assertNotMatched("a?", "abc/");
240 
241 		assertNotMatched("a?", "b/abc");
242 		assertNotMatched("a?", "b/abc/");
243 
244 		assertNotMatched("a?", "/abc/");
245 		assertNotMatched("a?", "/abc/b");
246 
247 		assertNotMatched("a?", "c/abc/b");
248 		assertNotMatched("a?", "c/b/abc");
249 
250 		assertNotMatched("/a?", "abc");
251 		assertNotMatched("/a?", "abc/");
252 		assertNotMatched("/a?", "abc/b");
253 		assertNotMatched("/a?", "/abc");
254 		assertNotMatched("/a?", "/abc/");
255 		assertNotMatched("/a?", "/abc/b");
256 
257 		assertNotMatched("/a?/b", "abc/b");
258 		assertNotMatched("/a?/b", "/abc/b");
259 		assertNotMatched("/a?/b", "/a/b");
260 	}
261 
262 	@Test
263 	public void testSimplePatterns() {
264 		assertMatched("a", "a");
265 		assertMatched("a", "a/");
266 		assertMatched("a", "a/b");
267 
268 		assertMatched("a", "b/a");
269 		assertMatched("a", "b/a/");
270 		assertMatched("a", "b/a/b");
271 
272 		assertMatched("a", "/a/");
273 		assertMatched("a", "/a/b");
274 
275 		assertMatched("a", "c/a/b");
276 		assertMatched("a", "c/b/a");
277 
278 		assertMatched("/a", "a");
279 		assertMatched("/a", "a/");
280 		assertMatched("/a", "a/b");
281 		assertMatched("/a", "/a");
282 		assertMatched("/a", "/a/");
283 		assertMatched("/a", "/a/b");
284 
285 		assertMatched("a/", "a/");
286 		assertMatched("a/", "a/b");
287 		assertMatched("a/", "/a/");
288 		assertMatched("a/", "/a/b");
289 
290 		assertMatched("/a/", "a/");
291 		assertMatched("/a/", "a/b");
292 		assertMatched("/a/", "/a/");
293 		assertMatched("/a/", "/a/b");
294 
295 	}
296 
297 	@Test
298 	public void testSimplePatternsDoNotMatch() {
299 		assertNotMatched("ab", "a");
300 		assertNotMatched("abc", "a/");
301 		assertNotMatched("abc", "a/b");
302 
303 		assertNotMatched("a", "ab");
304 		assertNotMatched("a", "ba");
305 		assertNotMatched("a", "aa");
306 
307 		assertNotMatched("a", "b/ab");
308 		assertNotMatched("a", "b/ba");
309 
310 		assertNotMatched("a", "b/ba");
311 		assertNotMatched("a", "b/ab");
312 
313 		assertNotMatched("a", "b/ba/");
314 		assertNotMatched("a", "b/ba/b");
315 
316 		assertNotMatched("a", "/aa");
317 		assertNotMatched("a", "aa/");
318 		assertNotMatched("a", "/aa/");
319 
320 		assertNotMatched("/a", "b/a");
321 		assertNotMatched("/a", "/b/a/");
322 
323 		assertNotMatched("a/", "a");
324 		assertNotMatched("a/", "b/a");
325 
326 		assertNotMatched("/a/", "a");
327 		assertNotMatched("/a/", "/a");
328 		assertNotMatched("/a/", "b/a");
329 	}
330 
331 	@Test
332 	public void testSegments() {
333 		assertMatched("/a/b", "a/b");
334 		assertMatched("/a/b", "/a/b");
335 		assertMatched("/a/b", "/a/b/");
336 		assertMatched("/a/b", "/a/b/c");
337 
338 		assertMatched("a/b", "a/b");
339 		assertMatched("a/b", "/a/b");
340 		assertMatched("a/b", "/a/b/");
341 		assertMatched("a/b", "/a/b/c");
342 
343 		assertMatched("a/b/", "a/b/");
344 		assertMatched("a/b/", "/a/b/");
345 		assertMatched("a/b/", "/a/b/c");
346 	}
347 
348 	@Test
349 	public void testSegmentsDoNotMatch() {
350 		assertNotMatched("a/b", "/a/bb");
351 		assertNotMatched("a/b", "/aa/b");
352 		assertNotMatched("a/b", "a/bb");
353 		assertNotMatched("a/b", "aa/b");
354 		assertNotMatched("a/b", "c/aa/b");
355 		assertNotMatched("a/b", "c/a/bb");
356 		assertNotMatched("a/b/", "/a/b");
357 		assertNotMatched("/a/b/", "/a/b");
358 		assertNotMatched("/a/b", "c/a/b");
359 		assertNotMatched("/a/b/", "c/a/b");
360 		assertNotMatched("/a/b/", "c/a/b/");
361 
362 		// XXX why is it like this????
363 		assertNotMatched("a/b", "c/a/b");
364 		assertNotMatched("a/b", "c/a/b/");
365 		assertNotMatched("a/b", "c/a/b/c");
366 		assertNotMatched("a/b/", "c/a/b/");
367 		assertNotMatched("a/b/", "c/a/b/c");
368 	}
369 
370 	@Test
371 	public void testWildmatch() {
372 		assertMatched("**/a/b", "a/b");
373 		assertMatched("**/a/b", "c/a/b");
374 		assertMatched("**/a/b", "c/d/a/b");
375 		assertMatched("**/**/a/b", "c/d/a/b");
376 
377 		assertMatched("/**/a/b", "a/b");
378 		assertMatched("/**/a/b", "c/a/b");
379 		assertMatched("/**/a/b", "c/d/a/b");
380 		assertMatched("/**/**/a/b", "c/d/a/b");
381 
382 		assertMatched("a/b/**", "a/b");
383 		assertMatched("a/b/**", "a/b/c");
384 		assertMatched("a/b/**", "a/b/c/d/");
385 		assertMatched("a/b/**/**", "a/b/c/d");
386 
387 		assertMatched("**/a/**/b", "c/d/a/b");
388 		assertMatched("**/a/**/b", "c/d/a/e/b");
389 		assertMatched("**/**/a/**/**/b", "c/d/a/e/b");
390 
391 		assertMatched("/**/a/**/b", "c/d/a/b");
392 		assertMatched("/**/a/**/b", "c/d/a/e/b");
393 		assertMatched("/**/**/a/**/**/b", "c/d/a/e/b");
394 
395 		assertMatched("a/**/b", "a/b");
396 		assertMatched("a/**/b", "a/c/b");
397 		assertMatched("a/**/b", "a/c/d/b");
398 		assertMatched("a/**/**/b", "a/c/d/b");
399 
400 		assertMatched("a/**/b/**/c", "a/c/b/d/c");
401 		assertMatched("a/**/**/b/**/**/c", "a/c/b/d/c");
402 	}
403 
404 	@Test
405 	public void testWildmatchDoNotMatch() {
406 		assertNotMatched("**/a/b", "a/c/b");
407 		assertNotMatched("!/**/*.zip", "c/a/b.zip");
408 		assertNotMatched("!**/*.zip", "c/a/b.zip");
409 		assertNotMatched("a/**/b", "a/c/bb");
410 	}
411 
412 	@SuppressWarnings("unused")
413 	@Test
414 	public void testSimpleRules() {
415 		try {
416 			new FastIgnoreRule(null);
417 			fail("Illegal input allowed!");
418 		} catch (IllegalArgumentException e) {
419 			// expected
420 		}
421 		assertFalse(new FastIgnoreRule("/").isMatch("/", false));
422 		assertFalse(new FastIgnoreRule("//").isMatch("//", false));
423 		assertFalse(new FastIgnoreRule("#").isMatch("#", false));
424 		assertFalse(new FastIgnoreRule("").isMatch("", false));
425 		assertFalse(new FastIgnoreRule(" ").isMatch(" ", false));
426 	}
427 
428 	@Test
429 	public void testSplit() {
430 		try {
431 			split("/", '/').toArray();
432 			fail("should not allow single slash");
433 		} catch (IllegalStateException e) {
434 			// expected
435 		}
436 
437 		assertArrayEquals(new String[] { "a", "b" }, split("a/b", '/')
438 				.toArray());
439 		assertArrayEquals(new String[] { "a", "b/" }, split("a/b/", '/')
440 				.toArray());
441 		assertArrayEquals(new String[] { "/a", "b" }, split("/a/b", '/')
442 				.toArray());
443 		assertArrayEquals(new String[] { "/a", "b/" }, split("/a/b/", '/')
444 				.toArray());
445 		assertArrayEquals(new String[] { "/a", "b", "c" }, split("/a/b/c", '/')
446 				.toArray());
447 		assertArrayEquals(new String[] { "/a", "b", "c/" },
448 				split("/a/b/c/", '/').toArray());
449 	}
450 
451 	public void assertMatched(String pattern, String path) {
452 		boolean match = match(pattern, path);
453 		String result = path + " is " + (match ? "ignored" : "not ignored")
454 				+ " via '" + pattern + "' rule";
455 		if (!match) {
456 			System.err.println(result);
457 		}
458 		assertTrue("Expected a match for: " + pattern + " with: " + path,
459 					match);
460 
461 		if (pattern.startsWith("!")) {
462 			pattern = pattern.substring(1);
463 		} else {
464 			pattern = "!" + pattern;
465 		}
466 		match = match(pattern, path);
467 		assertFalse("Expected no match for: " + pattern + " with: " + path,
468 				match);
469 	}
470 
471 	public void assertNotMatched(String pattern, String path) {
472 		boolean match = match(pattern, path);
473 		String result = path + " is " + (match ? "ignored" : "not ignored")
474 				+ " via '" + pattern + "' rule";
475 		if (match) {
476 			System.err.println(result);
477 		}
478 		assertFalse("Expected no match for: " + pattern + " with: " + path,
479 					match);
480 
481 		if (pattern.startsWith("!")) {
482 			pattern = pattern.substring(1);
483 		} else {
484 			pattern = "!" + pattern;
485 		}
486 		match = match(pattern, path);
487 		assertTrue("Expected a match for: " + pattern + " with: " + path,
488 					match);
489 	}
490 
491 	/**
492 	 * Check for a match. If target ends with "/", match will assume that the
493 	 * target is meant to be a directory.
494 	 *
495 	 * @param pattern
496 	 *            Pattern as it would appear in a .gitignore file
497 	 * @param target
498 	 *            Target file path relative to repository's GIT_DIR
499 	 * @return Result of {@link FastIgnoreRule#isMatch(String, boolean)}
500 	 */
501 	private boolean match(String pattern, String target) {
502 		boolean isDirectory = target.endsWith("/");
503 		FastIgnoreRule r = new FastIgnoreRule(pattern);
504 		// If speed of this test is ever an issue, we can use a presetRule field
505 		// to avoid recompiling a pattern each time.
506 		boolean match = r.isMatch(target, isDirectory);
507 		if (r.getNegation())
508 			match = !match;
509 		return match;
510 	}
511 }