View Javadoc
1   /*
2    * Copyright (C) 2015, 2017 Ivan Motsch <ivan.motsch@bsiag.com> 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.assertTrue;
15  
16  import java.io.File;
17  import java.io.IOException;
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.Collections;
21  
22  import org.eclipse.jgit.junit.RepositoryTestCase;
23  import org.eclipse.jgit.lib.ConfigConstants;
24  import org.eclipse.jgit.lib.Constants;
25  import org.eclipse.jgit.lib.FileMode;
26  import org.eclipse.jgit.storage.file.FileBasedConfig;
27  import org.eclipse.jgit.treewalk.FileTreeIterator;
28  import org.eclipse.jgit.treewalk.TreeWalk;
29  import org.junit.Test;
30  
31  /**
32   * Tests {@link AttributesHandler}
33   */
34  public class AttributesHandlerTest extends RepositoryTestCase {
35  	private static final FileMode D = FileMode.TREE;
36  
37  	private static final FileMode F = FileMode.REGULAR_FILE;
38  
39  	@Test
40  	public void testExpandNonMacro1() throws Exception {
41  		setupRepo(null, null, null, "*.txt text");
42  
43  		try (TreeWalk walk = beginWalk()) {
44  			assertIteration(walk, D, "sub");
45  			assertIteration(walk, F, "sub/.gitattributes");
46  			assertIteration(walk, F, "sub/a.txt", attrs("text"));
47  			assertFalse("Not all files tested", walk.next());
48  		}
49  	}
50  
51  	@Test
52  	public void testExpandNonMacro2() throws Exception {
53  		setupRepo(null, null, null, "*.txt -text");
54  
55  		try (TreeWalk walk = beginWalk()) {
56  			assertIteration(walk, D, "sub");
57  			assertIteration(walk, F, "sub/.gitattributes");
58  			assertIteration(walk, F, "sub/a.txt", attrs("-text"));
59  			assertFalse("Not all files tested", walk.next());
60  		}
61  	}
62  
63  	@Test
64  	public void testExpandNonMacro3() throws Exception {
65  		setupRepo(null, null, null, "*.txt !text");
66  
67  		try (TreeWalk walk = beginWalk()) {
68  			assertIteration(walk, D, "sub");
69  			assertIteration(walk, F, "sub/.gitattributes");
70  			assertIteration(walk, F, "sub/a.txt", attrs(""));
71  			assertFalse("Not all files tested", walk.next());
72  		}
73  	}
74  
75  	@Test
76  	public void testExpandNonMacro4() throws Exception {
77  		setupRepo(null, null, null, "*.txt text=auto");
78  
79  		try (TreeWalk walk = beginWalk()) {
80  			assertIteration(walk, D, "sub");
81  			assertIteration(walk, F, "sub/.gitattributes");
82  			assertIteration(walk, F, "sub/a.txt", attrs("text=auto"));
83  			assertFalse("Not all files tested", walk.next());
84  		}
85  	}
86  
87  	@Test
88  	public void testExpandBuiltInMacro1() throws Exception {
89  		setupRepo(null, null, null, "*.txt binary");
90  
91  		try (TreeWalk walk = beginWalk()) {
92  			assertIteration(walk, D, "sub");
93  			assertIteration(walk, F, "sub/.gitattributes");
94  			assertIteration(walk, F, "sub/a.txt",
95  					attrs("binary -diff -merge -text"));
96  			assertFalse("Not all files tested", walk.next());
97  		}
98  	}
99  
100 	@Test
101 	public void testExpandBuiltInMacro2() throws Exception {
102 		setupRepo(null, null, null, "*.txt -binary");
103 
104 		try (TreeWalk walk = beginWalk()) {
105 			assertIteration(walk, D, "sub");
106 			assertIteration(walk, F, "sub/.gitattributes");
107 			assertIteration(walk, F, "sub/a.txt",
108 					attrs("-binary diff merge text"));
109 			assertFalse("Not all files tested", walk.next());
110 		}
111 	}
112 
113 	@Test
114 	public void testExpandBuiltInMacro3() throws Exception {
115 		setupRepo(null, null, null, "*.txt !binary");
116 
117 		try (TreeWalk walk = beginWalk()) {
118 			assertIteration(walk, D, "sub");
119 			assertIteration(walk, F, "sub/.gitattributes");
120 			assertIteration(walk, F, "sub/a.txt", attrs(""));
121 			assertFalse("Not all files tested", walk.next());
122 		}
123 	}
124 
125 	@Test
126 	public void testCustomGlobalMacro1() throws Exception {
127 		setupRepo(
128 				"[attr]foo a -b !c d=e", null, null, "*.txt foo");
129 
130 		try (TreeWalk walk = beginWalk()) {
131 			assertIteration(walk, D, "sub");
132 			assertIteration(walk, F, "sub/.gitattributes");
133 			assertIteration(walk, F, "sub/a.txt", attrs("foo a -b d=e"));
134 			assertFalse("Not all files tested", walk.next());
135 		}
136 	}
137 
138 	@Test
139 	public void testCustomGlobalMacro2() throws Exception {
140 		setupRepo("[attr]foo a -b !c d=e", null, null, "*.txt -foo");
141 
142 		try (TreeWalk walk = beginWalk()) {
143 			assertIteration(walk, D, "sub");
144 			assertIteration(walk, F, "sub/.gitattributes");
145 			assertIteration(walk, F, "sub/a.txt", attrs("-foo -a b d=e"));
146 			assertFalse("Not all files tested", walk.next());
147 		}
148 	}
149 
150 	@Test
151 	public void testCustomGlobalMacro3() throws Exception {
152 		setupRepo("[attr]foo a -b !c d=e", null, null, "*.txt !foo");
153 
154 		try (TreeWalk walk = beginWalk()) {
155 			assertIteration(walk, D, "sub");
156 			assertIteration(walk, F, "sub/.gitattributes");
157 			assertIteration(walk, F, "sub/a.txt", attrs(""));
158 			assertFalse("Not all files tested", walk.next());
159 		}
160 	}
161 
162 	@Test
163 	public void testCustomGlobalMacro4() throws Exception {
164 		setupRepo("[attr]foo a -b !c d=e", null, null, "*.txt foo=bar");
165 
166 		try (TreeWalk walk = beginWalk()) {
167 			assertIteration(walk, D, "sub");
168 			assertIteration(walk, F, "sub/.gitattributes");
169 			assertIteration(walk, F, "sub/a.txt", attrs("foo=bar a -b d=bar"));
170 			assertFalse("Not all files tested", walk.next());
171 		}
172 	}
173 
174 	@Test
175 	public void testInfoOverridesGlobal() throws Exception {
176 		setupRepo("[attr]foo bar1",
177 				"[attr]foo bar2", null, "*.txt foo");
178 
179 		try (TreeWalk walk = beginWalk()) {
180 			assertIteration(walk, D, "sub");
181 			assertIteration(walk, F, "sub/.gitattributes");
182 			assertIteration(walk, F, "sub/a.txt", attrs("foo bar2"));
183 			assertFalse("Not all files tested", walk.next());
184 		}
185 	}
186 
187 	@Test
188 	public void testWorkDirRootOverridesGlobal() throws Exception {
189 		setupRepo("[attr]foo bar1",
190 				null,
191 				"[attr]foo bar3", "*.txt foo");
192 
193 		try (TreeWalk walk = beginWalk()) {
194 			assertIteration(walk, F, ".gitattributes");
195 			assertIteration(walk, D, "sub");
196 			assertIteration(walk, F, "sub/.gitattributes");
197 			assertIteration(walk, F, "sub/a.txt", attrs("foo bar3"));
198 			assertFalse("Not all files tested", walk.next());
199 		}
200 	}
201 
202 	@Test
203 	public void testInfoOverridesWorkDirRoot() throws Exception {
204 		setupRepo("[attr]foo bar1",
205 				"[attr]foo bar2", "[attr]foo bar3", "*.txt foo");
206 
207 		try (TreeWalk walk = beginWalk()) {
208 			assertIteration(walk, F, ".gitattributes");
209 			assertIteration(walk, D, "sub");
210 			assertIteration(walk, F, "sub/.gitattributes");
211 			assertIteration(walk, F, "sub/a.txt", attrs("foo bar2"));
212 			assertFalse("Not all files tested", walk.next());
213 		}
214 	}
215 
216 	@Test
217 	public void testRecursiveMacro() throws Exception {
218 		setupRepo(
219 				"[attr]foo x bar -foo",
220 				null, null, "*.txt foo");
221 
222 		try (TreeWalk walk = beginWalk()) {
223 			assertIteration(walk, D, "sub");
224 			assertIteration(walk, F, "sub/.gitattributes");
225 			assertIteration(walk, F, "sub/a.txt", attrs("foo x bar"));
226 			assertFalse("Not all files tested", walk.next());
227 		}
228 	}
229 
230 	@Test
231 	public void testCyclicMacros() throws Exception {
232 		setupRepo(
233 				"[attr]foo x -bar\n[attr]bar y -foo", null, null, "*.txt foo");
234 
235 		try (TreeWalk walk = beginWalk()) {
236 			assertIteration(walk, D, "sub");
237 			assertIteration(walk, F, "sub/.gitattributes");
238 			assertIteration(walk, F, "sub/a.txt", attrs("foo x -bar -y"));
239 			assertFalse("Not all files tested", walk.next());
240 		}
241 	}
242 
243 	@Test
244 	public void testRelativePaths() throws Exception {
245 		setupRepo("sub/ global", "sub/** init",
246 				"sub/** top_sub\n*.txt top",
247 				"sub/** subsub\nsub/ subsub2\n*.txt foo");
248 		// The last sub/** is in sub/.gitattributes. It must not
249 		// apply to any of the files here. It would match for a
250 		// further subdirectory sub/sub. The sub/ rules must match
251 		// only for directories.
252 		try (TreeWalk walk = beginWalk()) {
253 			assertIteration(walk, F, ".gitattributes");
254 			assertIteration(walk, D, "sub", attrs("global"));
255 			assertIteration(walk, F, "sub/.gitattributes",
256 					attrs("init top_sub"));
257 			assertIteration(walk, F, "sub/a.txt",
258 					attrs("init foo top top_sub"));
259 			assertFalse("Not all files tested", walk.next());
260 		}
261 		// All right, let's see that they *do* apply in sub/sub:
262 		writeTrashFile("sub/sub/b.txt", "b");
263 		try (TreeWalk walk = beginWalk()) {
264 			assertIteration(walk, F, ".gitattributes");
265 			assertIteration(walk, D, "sub", attrs("global"));
266 			assertIteration(walk, F, "sub/.gitattributes",
267 					attrs("init top_sub"));
268 			assertIteration(walk, F, "sub/a.txt",
269 					attrs("init foo top top_sub"));
270 			assertIteration(walk, D, "sub/sub",
271 					attrs("init subsub2 top_sub global"));
272 			assertIteration(walk, F, "sub/sub/b.txt",
273 					attrs("init foo subsub top top_sub"));
274 			assertFalse("Not all files tested", walk.next());
275 		}
276 	}
277 
278 	@Test
279 	public void testNestedMatchNot() throws Exception {
280 		setupRepo(null, null, "*.xml xml\n*.jar jar", null);
281 		writeTrashFile("foo.xml/bar.jar", "b");
282 		writeTrashFile("foo.xml/bar.xml", "bx");
283 		writeTrashFile("sub/b.jar", "bj");
284 		writeTrashFile("sub/b.xml", "bx");
285 		// On foo.xml/bar.jar we must not have 'xml'
286 		try (TreeWalk walk = beginWalk()) {
287 			assertIteration(walk, F, ".gitattributes");
288 			assertIteration(walk, D, "foo.xml", attrs("xml"));
289 			assertIteration(walk, F, "foo.xml/bar.jar", attrs("jar"));
290 			assertIteration(walk, F, "foo.xml/bar.xml", attrs("xml"));
291 			assertIteration(walk, D, "sub");
292 			assertIteration(walk, F, "sub/a.txt");
293 			assertIteration(walk, F, "sub/b.jar", attrs("jar"));
294 			assertIteration(walk, F, "sub/b.xml", attrs("xml"));
295 			assertFalse("Not all files tested", walk.next());
296 		}
297 	}
298 
299 	@Test
300 	public void testNestedMatch() throws Exception {
301 		// See also CGitAttributeTest.testNestedMatch()
302 		setupRepo(null, null, "foo/ xml\nsub/foo/ sub\n*.jar jar", null);
303 		writeTrashFile("foo/bar.jar", "b");
304 		writeTrashFile("foo/bar.xml", "bx");
305 		writeTrashFile("sub/b.jar", "bj");
306 		writeTrashFile("sub/b.xml", "bx");
307 		writeTrashFile("sub/foo/b.jar", "bf");
308 		try (TreeWalk walk = beginWalk()) {
309 			assertIteration(walk, F, ".gitattributes");
310 			assertIteration(walk, D, "foo", attrs("xml"));
311 			assertIteration(walk, F, "foo/bar.jar", attrs("jar"));
312 			assertIteration(walk, F, "foo/bar.xml");
313 			assertIteration(walk, D, "sub");
314 			assertIteration(walk, F, "sub/a.txt");
315 			assertIteration(walk, F, "sub/b.jar", attrs("jar"));
316 			assertIteration(walk, F, "sub/b.xml");
317 			assertIteration(walk, D, "sub/foo", attrs("sub xml"));
318 			assertIteration(walk, F, "sub/foo/b.jar", attrs("jar"));
319 			assertFalse("Not all files tested", walk.next());
320 		}
321 	}
322 
323 	@Test
324 	public void testNestedMatchRecursive() throws Exception {
325 		setupRepo(null, null, "foo/** xml\n*.jar jar", null);
326 		writeTrashFile("foo/bar.jar", "b");
327 		writeTrashFile("foo/bar.xml", "bx");
328 		writeTrashFile("sub/b.jar", "bj");
329 		writeTrashFile("sub/b.xml", "bx");
330 		writeTrashFile("sub/foo/b.jar", "bf");
331 		// On foo.xml/bar.jar we must not have 'xml'
332 		try (TreeWalk walk = beginWalk()) {
333 			assertIteration(walk, F, ".gitattributes");
334 			assertIteration(walk, D, "foo");
335 			assertIteration(walk, F, "foo/bar.jar", attrs("jar xml"));
336 			assertIteration(walk, F, "foo/bar.xml", attrs("xml"));
337 			assertIteration(walk, D, "sub");
338 			assertIteration(walk, F, "sub/a.txt");
339 			assertIteration(walk, F, "sub/b.jar", attrs("jar"));
340 			assertIteration(walk, F, "sub/b.xml");
341 			assertIteration(walk, D, "sub/foo");
342 			assertIteration(walk, F, "sub/foo/b.jar", attrs("jar"));
343 			assertFalse("Not all files tested", walk.next());
344 		}
345 	}
346 
347 	@Test
348 	public void testStarMatchOnSlashNot() throws Exception {
349 		setupRepo(null, null, "s*xt bar", null);
350 		writeTrashFile("sub/a.txt", "1");
351 		writeTrashFile("foo/sext", "2");
352 		writeTrashFile("foo/s.txt", "3");
353 		try (TreeWalk walk = beginWalk()) {
354 			assertIteration(walk, F, ".gitattributes");
355 			assertIteration(walk, D, "foo");
356 			assertIteration(walk, F, "foo/s.txt", attrs("bar"));
357 			assertIteration(walk, F, "foo/sext", attrs("bar"));
358 			assertIteration(walk, D, "sub");
359 			assertIteration(walk, F, "sub/a.txt");
360 			assertFalse("Not all files tested", walk.next());
361 		}
362 	}
363 
364 	@Test
365 	public void testPrefixMatchNot() throws Exception {
366 		setupRepo(null, null, "sub/new bar", null);
367 		writeTrashFile("sub/new/foo.txt", "1");
368 		try (TreeWalk walk = beginWalk()) {
369 			assertIteration(walk, F, ".gitattributes");
370 			assertIteration(walk, D, "sub");
371 			assertIteration(walk, F, "sub/a.txt");
372 			assertIteration(walk, D, "sub/new", attrs("bar"));
373 			assertIteration(walk, F, "sub/new/foo.txt");
374 			assertFalse("Not all files tested", walk.next());
375 		}
376 	}
377 
378 	@Test
379 	public void testComplexPathMatch() throws Exception {
380 		setupRepo(null, null, "s[t-v]b/n[de]w bar", null);
381 		writeTrashFile("sub/new/foo.txt", "1");
382 		writeTrashFile("sub/ndw", "2");
383 		try (TreeWalk walk = beginWalk()) {
384 			assertIteration(walk, F, ".gitattributes");
385 			assertIteration(walk, D, "sub");
386 			assertIteration(walk, F, "sub/a.txt");
387 			assertIteration(walk, F, "sub/ndw", attrs("bar"));
388 			assertIteration(walk, D, "sub/new", attrs("bar"));
389 			assertIteration(walk, F, "sub/new/foo.txt");
390 			assertFalse("Not all files tested", walk.next());
391 		}
392 	}
393 
394 	@Test
395 	public void testStarPathMatch() throws Exception {
396 		setupRepo(null, null, "sub/new/* bar", null);
397 		writeTrashFile("sub/new/foo.txt", "1");
398 		writeTrashFile("sub/new/lower/foo.txt", "2");
399 		try (TreeWalk walk = beginWalk()) {
400 			assertIteration(walk, F, ".gitattributes");
401 			assertIteration(walk, D, "sub");
402 			assertIteration(walk, F, "sub/a.txt");
403 			assertIteration(walk, D, "sub/new");
404 			assertIteration(walk, F, "sub/new/foo.txt", attrs("bar"));
405 			assertIteration(walk, D, "sub/new/lower", attrs("bar"));
406 			assertIteration(walk, F, "sub/new/lower/foo.txt");
407 			assertFalse("Not all files tested", walk.next());
408 		}
409 	}
410 
411 	@Test
412 	public void testDirectoryMatchSubSimple() throws Exception {
413 		setupRepo(null, null, "sub/new/ bar", null);
414 		writeTrashFile("sub/new/foo.txt", "1");
415 		writeTrashFile("foo/sub/new/foo.txt", "2");
416 		writeTrashFile("sub/sub/new/foo.txt", "3");
417 		try (TreeWalk walk = beginWalk()) {
418 			assertIteration(walk, F, ".gitattributes");
419 			assertIteration(walk, D, "foo");
420 			assertIteration(walk, D, "foo/sub");
421 			assertIteration(walk, D, "foo/sub/new");
422 			assertIteration(walk, F, "foo/sub/new/foo.txt");
423 			assertIteration(walk, D, "sub");
424 			assertIteration(walk, F, "sub/a.txt");
425 			assertIteration(walk, D, "sub/new", attrs("bar"));
426 			assertIteration(walk, F, "sub/new/foo.txt");
427 			assertIteration(walk, D, "sub/sub");
428 			assertIteration(walk, D, "sub/sub/new");
429 			assertIteration(walk, F, "sub/sub/new/foo.txt");
430 			assertFalse("Not all files tested", walk.next());
431 		}
432 	}
433 
434 	@Test
435 	public void testDirectoryMatchSubRecursive() throws Exception {
436 		setupRepo(null, null, "**/sub/new/ bar", null);
437 		writeTrashFile("sub/new/foo.txt", "1");
438 		writeTrashFile("foo/sub/new/foo.txt", "2");
439 		try (TreeWalk walk = beginWalk()) {
440 			assertIteration(walk, F, ".gitattributes");
441 			assertIteration(walk, D, "foo");
442 			assertIteration(walk, D, "foo/sub");
443 			assertIteration(walk, D, "foo/sub/new", attrs("bar"));
444 			assertIteration(walk, F, "foo/sub/new/foo.txt");
445 			assertIteration(walk, D, "sub");
446 			assertIteration(walk, F, "sub/a.txt");
447 			assertIteration(walk, D, "sub/new", attrs("bar"));
448 			assertIteration(walk, F, "sub/new/foo.txt");
449 			assertFalse("Not all files tested", walk.next());
450 		}
451 	}
452 
453 	@Test
454 	public void testDirectoryMatchSubRecursiveBacktrack() throws Exception {
455 		setupRepo(null, null, "**/sub/new/ bar", null);
456 		writeTrashFile("sub/new/foo.txt", "1");
457 		writeTrashFile("foo/sub/new/foo.txt", "2");
458 		writeTrashFile("sub/sub/new/foo.txt", "3");
459 		try (TreeWalk walk = beginWalk()) {
460 			assertIteration(walk, F, ".gitattributes");
461 			assertIteration(walk, D, "foo");
462 			assertIteration(walk, D, "foo/sub");
463 			assertIteration(walk, D, "foo/sub/new", attrs("bar"));
464 			assertIteration(walk, F, "foo/sub/new/foo.txt");
465 			assertIteration(walk, D, "sub");
466 			assertIteration(walk, F, "sub/a.txt");
467 			assertIteration(walk, D, "sub/new", attrs("bar"));
468 			assertIteration(walk, F, "sub/new/foo.txt");
469 			assertIteration(walk, D, "sub/sub");
470 			assertIteration(walk, D, "sub/sub/new", attrs("bar"));
471 			assertIteration(walk, F, "sub/sub/new/foo.txt");
472 			assertFalse("Not all files tested", walk.next());
473 		}
474 	}
475 
476 	@Test
477 	public void testDirectoryMatchSubRecursiveBacktrack2() throws Exception {
478 		setupRepo(null, null, "**/**/sub/new/ bar", null);
479 		writeTrashFile("sub/new/foo.txt", "1");
480 		writeTrashFile("foo/sub/new/foo.txt", "2");
481 		writeTrashFile("sub/sub/new/foo.txt", "3");
482 		try (TreeWalk walk = beginWalk()) {
483 			assertIteration(walk, F, ".gitattributes");
484 			assertIteration(walk, D, "foo");
485 			assertIteration(walk, D, "foo/sub");
486 			assertIteration(walk, D, "foo/sub/new", attrs("bar"));
487 			assertIteration(walk, F, "foo/sub/new/foo.txt");
488 			assertIteration(walk, D, "sub");
489 			assertIteration(walk, F, "sub/a.txt");
490 			assertIteration(walk, D, "sub/new", attrs("bar"));
491 			assertIteration(walk, F, "sub/new/foo.txt");
492 			assertIteration(walk, D, "sub/sub");
493 			assertIteration(walk, D, "sub/sub/new", attrs("bar"));
494 			assertIteration(walk, F, "sub/sub/new/foo.txt");
495 			assertFalse("Not all files tested", walk.next());
496 		}
497 	}
498 
499 	@Test
500 	public void testDirectoryMatchSubComplex() throws Exception {
501 		setupRepo(null, null, "s[uv]b/n*/ bar", null);
502 		writeTrashFile("sub/new/foo.txt", "1");
503 		writeTrashFile("foo/sub/new/foo.txt", "2");
504 		try (TreeWalk walk = beginWalk()) {
505 			assertIteration(walk, F, ".gitattributes");
506 			assertIteration(walk, D, "foo");
507 			assertIteration(walk, D, "foo/sub");
508 			assertIteration(walk, D, "foo/sub/new");
509 			assertIteration(walk, F, "foo/sub/new/foo.txt");
510 			assertIteration(walk, D, "sub");
511 			assertIteration(walk, F, "sub/a.txt");
512 			assertIteration(walk, D, "sub/new", attrs("bar"));
513 			assertIteration(walk, F, "sub/new/foo.txt");
514 			assertFalse("Not all files tested", walk.next());
515 		}
516 	}
517 
518 	@Test
519 	public void testDirectoryMatch() throws Exception {
520 		setupRepo(null, null, "new/ bar", null);
521 		writeTrashFile("sub/new/foo.txt", "1");
522 		writeTrashFile("foo/sub/new/foo.txt", "2");
523 		writeTrashFile("foo/new", "3");
524 		try (TreeWalk walk = beginWalk()) {
525 			assertIteration(walk, F, ".gitattributes");
526 			assertIteration(walk, D, "foo");
527 			assertIteration(walk, F, "foo/new");
528 			assertIteration(walk, D, "foo/sub");
529 			assertIteration(walk, D, "foo/sub/new", attrs("bar"));
530 			assertIteration(walk, F, "foo/sub/new/foo.txt");
531 			assertIteration(walk, D, "sub");
532 			assertIteration(walk, F, "sub/a.txt");
533 			assertIteration(walk, D, "sub/new", attrs("bar"));
534 			assertIteration(walk, F, "sub/new/foo.txt");
535 			assertFalse("Not all files tested", walk.next());
536 		}
537 	}
538 
539 	private static Collection<Attribute> attrs(String s) {
540 		return new AttributesRule("*", s).getAttributes();
541 	}
542 
543 	private void assertIteration(TreeWalk walk, FileMode type, String pathName)
544 			throws IOException {
545 		assertIteration(walk, type, pathName,
546 				Collections.<Attribute> emptyList());
547 	}
548 
549 	private void assertIteration(TreeWalk walk, FileMode type, String pathName,
550 			Collection<Attribute> expectedAttrs) throws IOException {
551 		assertTrue("walk has entry", walk.next());
552 		assertEquals(pathName, walk.getPathString());
553 		assertEquals(type, walk.getFileMode(0));
554 
555 		if (expectedAttrs != null) {
556 			assertEquals(new ArrayList<>(expectedAttrs),
557 					new ArrayList<>(walk.getAttributes().getAll()));
558 		}
559 
560 		if (D.equals(type))
561 			walk.enterSubtree();
562 	}
563 
564 	/**
565 	 * @param globalAttributesContent
566 	 * @param infoAttributesContent
567 	 * @param rootAttributesContent
568 	 * @param subDirAttributesContent
569 	 * @throws Exception
570 	 *             Setup a repo with .gitattributes files and a test file
571 	 *             sub/a.txt
572 	 */
573 	private void setupRepo(
574 			String globalAttributesContent,
575 			String infoAttributesContent, String rootAttributesContent, String subDirAttributesContent)
576 					throws Exception {
577 		FileBasedConfig config = db.getConfig();
578 		if (globalAttributesContent != null) {
579 			File f = new File(db.getDirectory(), "global/attributes");
580 			write(f, globalAttributesContent);
581 			config.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
582 					ConfigConstants.CONFIG_KEY_ATTRIBUTESFILE,
583 					f.getAbsolutePath());
584 
585 		}
586 		if (infoAttributesContent != null) {
587 			File f = new File(db.getDirectory(), Constants.INFO_ATTRIBUTES);
588 			write(f, infoAttributesContent);
589 		}
590 		config.save();
591 
592 		if (rootAttributesContent != null) {
593 			writeAttributesFile(Constants.DOT_GIT_ATTRIBUTES,
594 					rootAttributesContent);
595 		}
596 
597 		if (subDirAttributesContent != null) {
598 			writeAttributesFile("sub/" + Constants.DOT_GIT_ATTRIBUTES,
599 					subDirAttributesContent);
600 		}
601 
602 		writeTrashFile("sub/a.txt", "a");
603 	}
604 
605 	private void writeAttributesFile(String name, String... rules)
606 			throws IOException {
607 		StringBuilder data = new StringBuilder();
608 		for (String line : rules)
609 			data.append(line + "\n");
610 		writeTrashFile(name, data.toString());
611 	}
612 
613 	private TreeWalk beginWalk() {
614 		TreeWalk newWalk = new TreeWalk(db);
615 		newWalk.addTree(new FileTreeIterator(db));
616 		return newWalk;
617 	}
618 }