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