View Javadoc
1   /*
2    * Copyright (C) 2010, Red Hat Inc.
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.attributes;
44  
45  import static org.junit.Assert.assertEquals;
46  import static org.junit.Assert.assertFalse;
47  import static org.junit.Assert.assertNotNull;
48  import static org.junit.Assert.assertTrue;
49  
50  import org.junit.Test;
51  
52  /**
53   * Tests git attributes pattern matches
54   */
55  public class AttributesMatcherTest {
56  
57  	@Test
58  	public void testBasic() {
59  		String pattern = "/test.stp";
60  		assertMatched(pattern, "/test.stp");
61  
62  		pattern = "#/test.stp";
63  		assertNotMatched(pattern, "/test.stp");
64  	}
65  
66  	@Test
67  	public void testFileNameWildcards() {
68  		//Test basic * and ? for any pattern + any character
69  		String pattern = "*.st?";
70  		assertMatched(pattern, "/test.stp");
71  		assertMatched(pattern, "/anothertest.stg");
72  		assertMatched(pattern, "/anothertest.st0");
73  		assertNotMatched(pattern, "/anothertest.sta1");
74  		//Check that asterisk does not expand to "/"
75  		assertNotMatched(pattern, "/another/test.sta1");
76  
77  		//Same as above, with a leading slash to ensure that doesn't cause problems
78  		pattern = "/*.st?";
79  		assertMatched(pattern, "/test.stp");
80  		assertMatched(pattern, "/anothertest.stg");
81  		assertMatched(pattern, "/anothertest.st0");
82  		assertNotMatched(pattern, "/anothertest.sta1");
83  		//Check that asterisk does not expand to "/"
84  		assertNotMatched(pattern, "/another/test.sta1");
85  
86  		//Test for numbers
87  		pattern = "*.sta[0-5]";
88  		assertMatched(pattern,  "/test.sta5");
89  		assertMatched(pattern, "/test.sta4");
90  		assertMatched(pattern, "/test.sta3");
91  		assertMatched(pattern, "/test.sta2");
92  		assertMatched(pattern, "/test.sta1");
93  		assertMatched(pattern, "/test.sta0");
94  		assertMatched(pattern, "/anothertest.sta2");
95  		assertNotMatched(pattern, "test.stag");
96  		assertNotMatched(pattern, "test.sta6");
97  
98  		//Test for letters
99  		pattern = "/[tv]est.sta[a-d]";
100 		assertMatched(pattern,  "/test.staa");
101 		assertMatched(pattern, "/test.stab");
102 		assertMatched(pattern, "/test.stac");
103 		assertMatched(pattern, "/test.stad");
104 		assertMatched(pattern, "/vest.stac");
105 		assertNotMatched(pattern, "test.stae");
106 		assertNotMatched(pattern, "test.sta9");
107 
108 		//Test child directory/file is matched
109 		pattern = "/src/ne?";
110 		assertMatched(pattern, "/src/new/");
111 		assertMatched(pattern, "/src/new");
112 		assertNotMatched(pattern, "/src/new/a.c");
113 		assertNotMatched(pattern, "/src/new/a/a.c");
114 		assertNotMatched(pattern, "/src/new.c");
115 
116 		//Test name-only fnmatcher matches
117 		pattern = "ne?";
118 		assertMatched(pattern, "/src/new/");
119 		assertMatched(pattern, "/src/new");
120 		assertNotMatched(pattern, "/src/new/a.c");
121 		assertNotMatched(pattern, "/src/new/a/a.c");
122 		assertMatched(pattern, "/neb");
123 		assertNotMatched(pattern, "/src/new.c");
124 	}
125 
126 	@Test
127 	public void testTargetWithoutLeadingSlash() {
128 		//Test basic * and ? for any pattern + any character
129 		String pattern = "/*.st?";
130 		assertMatched(pattern, "test.stp");
131 		assertMatched(pattern, "anothertest.stg");
132 		assertMatched(pattern, "anothertest.st0");
133 		assertNotMatched(pattern, "anothertest.sta1");
134 		//Check that asterisk does not expand to ""
135 		assertNotMatched(pattern, "another/test.sta1");
136 
137 		//Same as above, with a leading slash to ensure that doesn't cause problems
138 		pattern = "/*.st?";
139 		assertMatched(pattern, "test.stp");
140 		assertMatched(pattern, "anothertest.stg");
141 		assertMatched(pattern, "anothertest.st0");
142 		assertNotMatched(pattern, "anothertest.sta1");
143 		//Check that asterisk does not expand to ""
144 		assertNotMatched(pattern, "another/test.sta1");
145 
146 		//Test for numbers
147 		pattern = "/*.sta[0-5]";
148 		assertMatched(pattern,  "test.sta5");
149 		assertMatched(pattern, "test.sta4");
150 		assertMatched(pattern, "test.sta3");
151 		assertMatched(pattern, "test.sta2");
152 		assertMatched(pattern, "test.sta1");
153 		assertMatched(pattern, "test.sta0");
154 		assertMatched(pattern, "anothertest.sta2");
155 		assertNotMatched(pattern, "test.stag");
156 		assertNotMatched(pattern, "test.sta6");
157 
158 		//Test for letters
159 		pattern = "/[tv]est.sta[a-d]";
160 		assertMatched(pattern,  "test.staa");
161 		assertMatched(pattern, "test.stab");
162 		assertMatched(pattern, "test.stac");
163 		assertMatched(pattern, "test.stad");
164 		assertMatched(pattern, "vest.stac");
165 		assertNotMatched(pattern, "test.stae");
166 		assertNotMatched(pattern, "test.sta9");
167 
168 		//Test child directory/file is matched
169 		pattern = "/src/ne?";
170 		assertMatched(pattern, "src/new/");
171 		assertMatched(pattern, "src/new");
172 		assertNotMatched(pattern, "src/new/a.c");
173 		assertNotMatched(pattern, "src/new/a/a.c");
174 		assertNotMatched(pattern, "src/new.c");
175 
176 		//Test name-only fnmatcher matches
177 		pattern = "ne?";
178 		assertMatched(pattern, "src/new/");
179 		assertMatched(pattern, "src/new");
180 		assertNotMatched(pattern, "src/new/a.c");
181 		assertNotMatched(pattern, "src/new/a/a.c");
182 		assertMatched(pattern, "neb");
183 		assertNotMatched(pattern, "src/new.c");
184 	}
185 
186 	@Test
187 	public void testParentDirectoryGitAttributes() {
188 		//Contains git attribute patterns such as might be seen in a parent directory
189 
190 		//Test for wildcards
191 		String pattern = "/*/*.c";
192 		assertMatched(pattern, "/file/a.c");
193 		assertMatched(pattern, "/src/a.c");
194 		assertNotMatched(pattern, "/src/new/a.c");
195 
196 		//Test child directory/file is matched
197 		pattern = "/src/new";
198 		assertMatched(pattern, "/src/new/");
199 		assertMatched(pattern, "/src/new");
200 		assertNotMatched(pattern, "/src/new/a.c");
201 		assertNotMatched(pattern, "/src/new/a/a.c");
202 		assertNotMatched(pattern, "/src/new.c");
203 
204 		//Test child directory is matched, slash after name
205 		pattern = "/src/new/";
206 		assertMatched(pattern, "/src/new/");
207 		assertNotMatched(pattern, "/src/new/a.c");
208 		assertNotMatched(pattern, "/src/new/a/a.c");
209 		assertNotMatched(pattern, "/src/new");
210 		assertNotMatched(pattern, "/src/new.c");
211 
212 		//Test directory is matched by name only
213 		pattern = "b1";
214 		assertNotMatched(pattern, "/src/new/a/b1/a.c");
215 		assertNotMatched(pattern, "/src/new/a/b2/file.c");
216 		assertNotMatched(pattern, "/src/new/a/bb1/file.c");
217 		assertNotMatched(pattern, "/src/new/a/file.c");
218 		assertNotMatched(pattern, "/src/new/a/bb1");
219 		assertMatched(pattern, "/src/new/a/b1");
220 	}
221 
222 	@Test
223 	public void testTrailingSlash() {
224 		String pattern = "/src/";
225 		assertMatched(pattern, "/src/");
226 		assertNotMatched(pattern, "/src/new");
227 		assertNotMatched(pattern, "/src/new/a.c");
228 		assertNotMatched(pattern, "/src/a.c");
229 		assertNotMatched(pattern, "/src");
230 		assertNotMatched(pattern, "/srcA/");
231 
232 		pattern = "src/";
233 		assertMatched(pattern, "src/");
234 		assertMatched(pattern, "/src/");
235 		assertNotMatched(pattern, "src");
236 		assertNotMatched(pattern, "/src/new");
237 		assertNotMatched(pattern, "/src/new/a.c");
238 		assertNotMatched(pattern, "/src/a.c");
239 		assertNotMatched(pattern, "foo/src/a.c");
240 		assertNotMatched(pattern, "foo/src/bar/a.c");
241 		assertNotMatched(pattern, "foo/src/bar/src");
242 		assertMatched(pattern, "foo/src/");
243 		assertMatched(pattern, "foo/src/bar/src/");
244 	}
245 
246 	@Test
247 	public void testNameOnlyMatches() {
248 		/*
249 		 * Name-only matches do not contain any path separators
250 		 */
251 		//Test matches for file extension
252 		String pattern = "*.stp";
253 		assertMatched(pattern, "/test.stp");
254 		assertMatched(pattern, "/src/test.stp");
255 		assertNotMatched(pattern, "/test.stp1");
256 		assertNotMatched(pattern, "/test.astp");
257 		assertNotMatched(pattern, "test.stp/foo.bar");
258 		assertMatched(pattern, "test.stp");
259 		assertMatched(pattern, "test.stp/");
260 		assertMatched(pattern, "test.stp/test.stp");
261 
262 		//Test matches for name-only, applies to file name or folder name
263 		pattern = "src";
264 		assertMatched(pattern, "/src");
265 		assertMatched(pattern, "/src/");
266 		assertNotMatched(pattern, "/src/a.c");
267 		assertNotMatched(pattern, "/src/new/a.c");
268 		assertNotMatched(pattern, "/new/src/a.c");
269 		assertMatched(pattern, "/file/src");
270 
271 		//Test matches for name-only, applies only to folder names
272 		pattern = "src/";
273 		assertNotMatched(pattern, "/src/a.c");
274 		assertNotMatched(pattern, "/src/new/a.c");
275 		assertNotMatched(pattern, "/new/src/a.c");
276 		assertNotMatched(pattern, "/src");
277 		assertNotMatched(pattern, "/file/src");
278 		assertMatched(pattern, "/file/src/");
279 
280 		//Test matches for name-only, applies to file name or folder name
281 		//With a small wildcard
282 		pattern = "?rc";
283 		assertNotMatched(pattern, "/src/a.c");
284 		assertNotMatched(pattern, "/src/new/a.c");
285 		assertNotMatched(pattern, "/new/src/a.c");
286 		assertMatched(pattern, "/new/src/");
287 		assertMatched(pattern, "/file/src");
288 		assertMatched(pattern, "/src/");
289 
290 		//Test matches for name-only, applies to file name or folder name
291 		//With a small wildcard
292 		pattern = "?r[a-c]";
293 		assertNotMatched(pattern, "/src/a.c");
294 		assertNotMatched(pattern, "/src/new/a.c");
295 		assertNotMatched(pattern, "/new/src/a.c");
296 		assertMatched(pattern, "/file/src");
297 		assertMatched(pattern, "/src/");
298 		assertNotMatched(pattern, "/srb/a.c");
299 		assertNotMatched(pattern, "/grb/new/a.c");
300 		assertNotMatched(pattern, "/new/crb/a.c");
301 		assertMatched(pattern, "/file/3rb");
302 		assertMatched(pattern, "/xrb/");
303 		assertNotMatched(pattern, "/3ra/a.c");
304 		assertNotMatched(pattern, "/5ra/new/a.c");
305 		assertNotMatched(pattern, "/new/1ra/a.c");
306 		assertNotMatched(pattern, "/new/1ra/a.c/");
307 		assertMatched(pattern, "/file/dra");
308 		assertMatched(pattern, "/file/dra/");
309 		assertMatched(pattern, "/era/");
310 		assertNotMatched(pattern, "/crg");
311 		assertNotMatched(pattern, "/cr3");
312 	}
313 
314 	@Test
315 	public void testGetters() {
316 		AttributesRule r = new AttributesRule("/pattern/", "");
317 		assertFalse(r.isNameOnly());
318 		assertTrue(r.isDirOnly());
319 		assertNotNull(r.getAttributes());
320 		assertTrue(r.getAttributes().isEmpty());
321 		assertEquals(r.getPattern(), "/pattern");
322 
323 		r = new AttributesRule("/patter?/", "");
324 		assertFalse(r.isNameOnly());
325 		assertTrue(r.isDirOnly());
326 		assertNotNull(r.getAttributes());
327 		assertTrue(r.getAttributes().isEmpty());
328 		assertEquals(r.getPattern(), "/patter?");
329 
330 		r = new AttributesRule("patt*", "");
331 		assertTrue(r.isNameOnly());
332 		assertFalse(r.isDirOnly());
333 		assertNotNull(r.getAttributes());
334 		assertTrue(r.getAttributes().isEmpty());
335 		assertEquals(r.getPattern(), "patt*");
336 
337 		r = new AttributesRule("pattern", "attribute1");
338 		assertTrue(r.isNameOnly());
339 		assertFalse(r.isDirOnly());
340 		assertNotNull(r.getAttributes());
341 		assertFalse(r.getAttributes().isEmpty());
342 		assertEquals(r.getAttributes().size(), 1);
343 		assertEquals(r.getPattern(), "pattern");
344 
345 		r = new AttributesRule("pattern", "attribute1 -attribute2");
346 		assertTrue(r.isNameOnly());
347 		assertFalse(r.isDirOnly());
348 		assertNotNull(r.getAttributes());
349 		assertEquals(r.getAttributes().size(), 2);
350 		assertEquals(r.getPattern(), "pattern");
351 
352 		r = new AttributesRule("pattern", "attribute1 \t-attribute2 \t");
353 		assertTrue(r.isNameOnly());
354 		assertFalse(r.isDirOnly());
355 		assertNotNull(r.getAttributes());
356 		assertEquals(r.getAttributes().size(), 2);
357 		assertEquals(r.getPattern(), "pattern");
358 
359 		r = new AttributesRule("pattern", "attribute1\t-attribute2\t");
360 		assertTrue(r.isNameOnly());
361 		assertFalse(r.isDirOnly());
362 		assertNotNull(r.getAttributes());
363 		assertEquals(r.getAttributes().size(), 2);
364 		assertEquals(r.getPattern(), "pattern");
365 
366 		r = new AttributesRule("pattern", "attribute1\t -attribute2\t ");
367 		assertTrue(r.isNameOnly());
368 		assertFalse(r.isDirOnly());
369 		assertNotNull(r.getAttributes());
370 		assertEquals(r.getAttributes().size(), 2);
371 		assertEquals(r.getPattern(), "pattern");
372 
373 		r = new AttributesRule("pattern",
374 				"attribute1 -attribute2  attribute3=value ");
375 		assertTrue(r.isNameOnly());
376 		assertFalse(r.isDirOnly());
377 		assertNotNull(r.getAttributes());
378 		assertEquals(r.getAttributes().size(), 3);
379 		assertEquals(r.getPattern(), "pattern");
380 		assertEquals(r.getAttributes().get(0).toString(), "attribute1");
381 		assertEquals(r.getAttributes().get(1).toString(), "-attribute2");
382 		assertEquals(r.getAttributes().get(2).toString(), "attribute3=value");
383 	}
384 
385 	@Test
386 	public void testBracketsInGroup() {
387 		//combinations of brackets in brackets, escaped and not
388 
389 		String[] patterns = new String[]{"[[\\]]", "[\\[\\]]"};
390 		for (String pattern : patterns) {
391 			assertNotMatched(pattern, "");
392 			assertNotMatched(pattern, "[]");
393 			assertNotMatched(pattern, "][");
394 			assertNotMatched(pattern, "[\\[]");
395 			assertNotMatched(pattern, "[[]");
396 			assertNotMatched(pattern, "[[]]");
397 			assertNotMatched(pattern, "[\\[\\]]");
398 
399 			assertMatched(pattern, "[");
400 			assertMatched(pattern, "]");
401 		}
402 
403 		patterns = new String[]{"[[]]", "[\\[]]"};
404 		for (String pattern : patterns) {
405 			assertNotMatched(pattern, "");
406 			assertMatched(pattern, "[]");
407 			assertNotMatched(pattern, "][");
408 			assertNotMatched(pattern, "[\\[]");
409 			assertNotMatched(pattern, "[[]");
410 			assertNotMatched(pattern, "[[]]");
411 			assertNotMatched(pattern, "[\\[\\]]");
412 
413 			assertNotMatched(pattern, "[");
414 			assertNotMatched(pattern, "]");
415 		}
416 	}
417 
418 	/**
419 	 * Check for a match. If target ends with "/", match will assume that the
420 	 * target is meant to be a directory.
421 	 *
422 	 * @param pattern
423 	 *            Pattern as it would appear in a .gitattributes file
424 	 * @param target
425 	 *            Target file path relative to repository's GIT_DIR
426 	 */
427 	private void assertMatched(String pattern, String target) {
428 		boolean value = match(pattern, target);
429 		assertTrue("Expected a match for: " + pattern + " with: " + target,
430 				value);
431 	}
432 
433 	/**
434 	 * Check for a match. If target ends with "/", match will assume that the
435 	 * target is meant to be a directory.
436 	 *
437 	 * @param pattern
438 	 *            Pattern as it would appear in a .gitattributes file
439 	 * @param target
440 	 *            Target file path relative to repository's GIT_DIR
441 	 */
442 	private void assertNotMatched(String pattern, String target) {
443 		boolean value = match(pattern, target);
444 		assertFalse("Expected no match for: " + pattern + " with: " + target,
445 				value);
446 	}
447 
448 	/**
449 	 * Check for a match. If target ends with "/", match will assume that the
450 	 * target is meant to be a directory.
451 	 *
452 	 * @param pattern
453 	 *            Pattern as it would appear in a .gitattributes file
454 	 * @param target
455 	 *            Target file path relative to repository's GIT_DIR
456 	 * @return Result of {@link AttributesRule#isMatch(String, boolean)}
457 	 */
458 	private static boolean match(String pattern, String target) {
459 		AttributesRule r = new AttributesRule(pattern, "");
460 		//If speed of this test is ever an issue, we can use a presetRule field
461 		//to avoid recompiling a pattern each time.
462 		return r.isMatch(target, target.endsWith("/"));
463 	}
464 }