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