aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.test
diff options
context:
space:
mode:
authorArthur Daussy <arthur.daussy@obeo.fr>2013-01-31 20:27:10 +0100
committerChris Aniszczyk <caniszczyk@gmail.com>2015-01-07 10:52:06 -0800
commitc185484dcfb52aaae818bc111824f1a31ec0f806 (patch)
treea4688bfa9f6731109852c700fec422519a1d8e8d /org.eclipse.jgit.test
parent428d31efd305b08e83dbd9fdd895d18dc232c295 (diff)
downloadjgit-c185484dcfb52aaae818bc111824f1a31ec0f806.tar.gz
jgit-c185484dcfb52aaae818bc111824f1a31ec0f806.zip
Add basic support for .gitattributes
Core classes to parse and process .gitattributes files including support for reading attributes in WorkingTreeIterator and the dirCacheIterator. The implementation follows the git ignore implementation. It supports lazy reading attributes while walking the working tree. Bug: 342372 CQ: 9078 Change-Id: I05f3ce1861fbf9896b1bcb7816ba78af35f3ad3d Also-by: Marc Strapetz <marc.strapetz@syntevo.com> Also-by: Gunnar Wagenknecht <gunnar@wagenknecht.org> Also-by: Arthur Daussy <arthur.daussy@obeo.fr> Signed-off-by: Gunnar Wagenknecht <gunnar@wagenknecht.org> Signed-off-by: Marc Strapetz <marc.strapetz@syntevo.com> Signed-off-by: Arthur Daussy <arthur.daussy@obeo.fr> Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com> Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
Diffstat (limited to 'org.eclipse.jgit.test')
-rw-r--r--org.eclipse.jgit.test/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java182
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeTest.java77
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java412
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java296
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java282
6 files changed, 1250 insertions, 0 deletions
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 6ca3df17a3..af40e883cd 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -10,6 +10,7 @@ Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)",
org.eclipse.jgit.api;version="[3.7.0,3.8.0)",
org.eclipse.jgit.api.errors;version="[3.7.0,3.8.0)",
+ org.eclipse.jgit.attributes;version="[3.7.0,3.8.0)",
org.eclipse.jgit.awtui;version="[3.7.0,3.8.0)",
org.eclipse.jgit.blame;version="[3.7.0,3.8.0)",
org.eclipse.jgit.console;version="[3.7.0,3.8.0)",
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java
new file mode 100644
index 0000000000..ea250369a0
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2014, Obeo.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+import static org.eclipse.jgit.attributes.Attribute.State.SET;
+import static org.eclipse.jgit.attributes.Attribute.State.UNSET;
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Test {@link AttributesNode}
+ */
+public class AttributeNodeTest {
+
+ private static final Attribute A_SET_ATTR = new Attribute("A", SET);
+
+ private static final Attribute A_UNSET_ATTR = new Attribute("A", UNSET);
+
+ private static final Attribute B_SET_ATTR = new Attribute("B", SET);
+
+ private static final Attribute B_UNSET_ATTR = new Attribute("B", UNSET);
+
+ private static final Attribute C_VALUE_ATTR = new Attribute("C", "value");
+
+ private static final Attribute C_VALUE2_ATTR = new Attribute("C", "value2");
+
+ private InputStream is;
+
+ @After
+ public void after() throws IOException {
+ if (is != null)
+ is.close();
+ }
+
+ @Test
+ public void testBasic() throws IOException {
+ String attributeFileContent = "*.type1 A -B C=value\n"
+ + "*.type2 -A B C=value2";
+
+ is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ AttributesNode node = new AttributesNode();
+ node.parse(is);
+ assertAttribute("file.type1", node,
+ asSet(A_SET_ATTR, B_UNSET_ATTR, C_VALUE_ATTR));
+ assertAttribute("file.type2", node,
+ asSet(A_UNSET_ATTR, B_SET_ATTR, C_VALUE2_ATTR));
+ }
+
+ @Test
+ public void testNegativePattern() throws IOException {
+ String attributeFileContent = "!*.type1 A -B C=value\n"
+ + "!*.type2 -A B C=value2";
+
+ is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ AttributesNode node = new AttributesNode();
+ node.parse(is);
+ assertAttribute("file.type1", node, Collections.<Attribute> emptySet());
+ assertAttribute("file.type2", node, Collections.<Attribute> emptySet());
+ }
+
+ @Test
+ public void testEmptyNegativeAttributeKey() throws IOException {
+ String attributeFileContent = "*.type1 - \n" //
+ + "*.type2 - -A";
+ is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ AttributesNode node = new AttributesNode();
+ node.parse(is);
+ assertAttribute("file.type1", node, Collections.<Attribute> emptySet());
+ assertAttribute("file.type2", node, asSet(A_UNSET_ATTR));
+ }
+
+ @Test
+ public void testEmptyValueKey() throws IOException {
+ String attributeFileContent = "*.type1 = \n" //
+ + "*.type2 =value\n"//
+ + "*.type3 attr=\n";
+ is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ AttributesNode node = new AttributesNode();
+ node.parse(is);
+ assertAttribute("file.type1", node, Collections.<Attribute> emptySet());
+ assertAttribute("file.type2", node, Collections.<Attribute> emptySet());
+ assertAttribute("file.type3", node, asSet(new Attribute("attr", "")));
+ }
+
+ @Test
+ public void testEmptyLine() throws IOException {
+ String attributeFileContent = "*.type1 A -B C=value\n" //
+ + "\n" //
+ + " \n" //
+ + "*.type2 -A B C=value2";
+
+ is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ AttributesNode node = new AttributesNode();
+ node.parse(is);
+ assertAttribute("file.type1", node,
+ asSet(A_SET_ATTR, B_UNSET_ATTR, C_VALUE_ATTR));
+ assertAttribute("file.type2", node,
+ asSet(A_UNSET_ATTR, B_SET_ATTR, C_VALUE2_ATTR));
+ }
+
+ @Test
+ public void testTabSeparator() throws IOException {
+ String attributeFileContent = "*.type1 \tA -B\tC=value\n"
+ + "*.type2\t -A\tB C=value2\n" //
+ + "*.type3 \t\t B\n" //
+ + "*.type3\t-A";//
+
+ is = new ByteArrayInputStream(attributeFileContent.getBytes());
+ AttributesNode node = new AttributesNode();
+ node.parse(is);
+ assertAttribute("file.type1", node,
+ asSet(A_SET_ATTR, B_UNSET_ATTR, C_VALUE_ATTR));
+ assertAttribute("file.type2", node,
+ asSet(A_UNSET_ATTR, B_SET_ATTR, C_VALUE2_ATTR));
+ assertAttribute("file.type3", node, asSet(A_UNSET_ATTR, B_SET_ATTR));
+ }
+
+ private void assertAttribute(String path, AttributesNode node,
+ Set<Attribute> attrs) {
+ HashMap<String, Attribute> attributes = new HashMap<String, Attribute>();
+ node.getAttributes(path, false, attributes);
+ assertEquals(attrs, new HashSet<Attribute>(attributes.values()));
+ }
+
+ static Set<Attribute> asSet(Attribute... attrs) {
+ Set<Attribute> result = new HashSet<Attribute>();
+ for (Attribute attr : attrs)
+ result.add(attr);
+ return result;
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeTest.java
new file mode 100644
index 0000000000..93b954fa9a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010, Marc Strapetz <marc.strapetz@syntevo.com>
+ * Copyright (C) 2013, Gunnar Wagenknecht
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.eclipse.jgit.attributes.Attribute.State;
+import org.junit.Test;
+
+/**
+ * Tests {@link Attribute}
+ */
+public class AttributeTest {
+
+ @Test
+ public void testBasic() {
+ Attribute a = new Attribute("delta", State.SET);
+ assertEquals(a.getKey(), "delta");
+ assertEquals(a.getState(), State.SET);
+ assertNull(a.getValue());
+ assertEquals(a.toString(), "delta");
+
+ a = new Attribute("delta", State.UNSET);
+ assertEquals(a.getKey(), "delta");
+ assertEquals(a.getState(), State.UNSET);
+ assertNull(a.getValue());
+ assertEquals(a.toString(), "-delta");
+
+ a = new Attribute("delta", "value");
+ assertEquals(a.getKey(), "delta");
+ assertEquals(a.getState(), State.CUSTOM);
+ assertEquals(a.getValue(), "value");
+ assertEquals(a.toString(), "delta=value");
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java
new file mode 100644
index 0000000000..6865406927
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2010, Red Hat Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * Tests git attributes pattern matches
+ * <p>
+ * Inspired by {@link org.eclipse.jgit.ignore.IgnoreMatcherTest}
+ * </p>
+ */
+public class AttributesMatcherTest {
+
+ @Test
+ public void testBasic() {
+ String pattern = "/test.stp";
+ assertMatched(pattern, "/test.stp");
+
+ pattern = "#/test.stp";
+ assertNotMatched(pattern, "/test.stp");
+ }
+
+ @Test
+ public void testFileNameWildcards() {
+ //Test basic * and ? for any pattern + any character
+ String pattern = "*.st?";
+ assertMatched(pattern, "/test.stp");
+ assertMatched(pattern, "/anothertest.stg");
+ assertMatched(pattern, "/anothertest.st0");
+ assertNotMatched(pattern, "/anothertest.sta1");
+ //Check that asterisk does not expand to "/"
+ assertNotMatched(pattern, "/another/test.sta1");
+
+ //Same as above, with a leading slash to ensure that doesn't cause problems
+ pattern = "/*.st?";
+ assertMatched(pattern, "/test.stp");
+ assertMatched(pattern, "/anothertest.stg");
+ assertMatched(pattern, "/anothertest.st0");
+ assertNotMatched(pattern, "/anothertest.sta1");
+ //Check that asterisk does not expand to "/"
+ assertNotMatched(pattern, "/another/test.sta1");
+
+ //Test for numbers
+ pattern = "*.sta[0-5]";
+ assertMatched(pattern, "/test.sta5");
+ assertMatched(pattern, "/test.sta4");
+ assertMatched(pattern, "/test.sta3");
+ assertMatched(pattern, "/test.sta2");
+ assertMatched(pattern, "/test.sta1");
+ assertMatched(pattern, "/test.sta0");
+ assertMatched(pattern, "/anothertest.sta2");
+ assertNotMatched(pattern, "test.stag");
+ assertNotMatched(pattern, "test.sta6");
+
+ //Test for letters
+ pattern = "/[tv]est.sta[a-d]";
+ assertMatched(pattern, "/test.staa");
+ assertMatched(pattern, "/test.stab");
+ assertMatched(pattern, "/test.stac");
+ assertMatched(pattern, "/test.stad");
+ assertMatched(pattern, "/vest.stac");
+ assertNotMatched(pattern, "test.stae");
+ assertNotMatched(pattern, "test.sta9");
+
+ //Test child directory/file is matched
+ pattern = "/src/ne?";
+ assertMatched(pattern, "/src/new/");
+ assertMatched(pattern, "/src/new");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/src/new/a/a.c");
+ assertNotMatched(pattern, "/src/new.c");
+
+ //Test name-only fnmatcher matches
+ pattern = "ne?";
+ assertMatched(pattern, "/src/new/");
+ assertMatched(pattern, "/src/new");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/src/new/a/a.c");
+ assertMatched(pattern, "/neb");
+ assertNotMatched(pattern, "/src/new.c");
+ }
+
+ @Test
+ public void testTargetWithoutLeadingSlash() {
+ //Test basic * and ? for any pattern + any character
+ String pattern = "/*.st?";
+ assertMatched(pattern, "test.stp");
+ assertMatched(pattern, "anothertest.stg");
+ assertMatched(pattern, "anothertest.st0");
+ assertNotMatched(pattern, "anothertest.sta1");
+ //Check that asterisk does not expand to ""
+ assertNotMatched(pattern, "another/test.sta1");
+
+ //Same as above, with a leading slash to ensure that doesn't cause problems
+ pattern = "/*.st?";
+ assertMatched(pattern, "test.stp");
+ assertMatched(pattern, "anothertest.stg");
+ assertMatched(pattern, "anothertest.st0");
+ assertNotMatched(pattern, "anothertest.sta1");
+ //Check that asterisk does not expand to ""
+ assertNotMatched(pattern, "another/test.sta1");
+
+ //Test for numbers
+ pattern = "/*.sta[0-5]";
+ assertMatched(pattern, "test.sta5");
+ assertMatched(pattern, "test.sta4");
+ assertMatched(pattern, "test.sta3");
+ assertMatched(pattern, "test.sta2");
+ assertMatched(pattern, "test.sta1");
+ assertMatched(pattern, "test.sta0");
+ assertMatched(pattern, "anothertest.sta2");
+ assertNotMatched(pattern, "test.stag");
+ assertNotMatched(pattern, "test.sta6");
+
+ //Test for letters
+ pattern = "/[tv]est.sta[a-d]";
+ assertMatched(pattern, "test.staa");
+ assertMatched(pattern, "test.stab");
+ assertMatched(pattern, "test.stac");
+ assertMatched(pattern, "test.stad");
+ assertMatched(pattern, "vest.stac");
+ assertNotMatched(pattern, "test.stae");
+ assertNotMatched(pattern, "test.sta9");
+
+ //Test child directory/file is matched
+ pattern = "/src/ne?";
+ assertMatched(pattern, "src/new/");
+ assertMatched(pattern, "src/new");
+ assertMatched(pattern, "src/new/a.c");
+ assertMatched(pattern, "src/new/a/a.c");
+ assertNotMatched(pattern, "src/new.c");
+
+ //Test name-only fnmatcher matches
+ pattern = "ne?";
+ assertMatched(pattern, "src/new/");
+ assertMatched(pattern, "src/new");
+ assertMatched(pattern, "src/new/a.c");
+ assertMatched(pattern, "src/new/a/a.c");
+ assertMatched(pattern, "neb");
+ assertNotMatched(pattern, "src/new.c");
+ }
+
+ @Test
+ public void testParentDirectoryGitAttributes() {
+ //Contains git attribute patterns such as might be seen in a parent directory
+
+ //Test for wildcards
+ String pattern = "/*/*.c";
+ assertMatched(pattern, "/file/a.c");
+ assertMatched(pattern, "/src/a.c");
+ assertNotMatched(pattern, "/src/new/a.c");
+
+ //Test child directory/file is matched
+ pattern = "/src/new";
+ assertMatched(pattern, "/src/new/");
+ assertMatched(pattern, "/src/new");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/src/new/a/a.c");
+ assertNotMatched(pattern, "/src/new.c");
+
+ //Test child directory is matched, slash after name
+ pattern = "/src/new/";
+ assertMatched(pattern, "/src/new/");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/src/new/a/a.c");
+ assertNotMatched(pattern, "/src/new");
+ assertNotMatched(pattern, "/src/new.c");
+
+ //Test directory is matched by name only
+ pattern = "b1";
+ assertMatched(pattern, "/src/new/a/b1/a.c");
+ assertNotMatched(pattern, "/src/new/a/b2/file.c");
+ assertNotMatched(pattern, "/src/new/a/bb1/file.c");
+ assertNotMatched(pattern, "/src/new/a/file.c");
+ }
+
+ @Test
+ public void testTrailingSlash() {
+ String pattern = "/src/";
+ assertMatched(pattern, "/src/");
+ assertMatched(pattern, "/src/new");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/src/a.c");
+ assertNotMatched(pattern, "/src");
+ assertNotMatched(pattern, "/srcA/");
+ }
+
+ @Test
+ public void testNameOnlyMatches() {
+ /*
+ * Name-only matches do not contain any path separators
+ */
+ //Test matches for file extension
+ String pattern = "*.stp";
+ assertMatched(pattern, "/test.stp");
+ assertMatched(pattern, "/src/test.stp");
+ assertNotMatched(pattern, "/test.stp1");
+ assertNotMatched(pattern, "/test.astp");
+
+ //Test matches for name-only, applies to file name or folder name
+ pattern = "src";
+ assertMatched(pattern, "/src");
+ assertMatched(pattern, "/src/");
+ assertMatched(pattern, "/src/a.c");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/new/src/a.c");
+ assertMatched(pattern, "/file/src");
+
+ //Test matches for name-only, applies only to folder names
+ pattern = "src/";
+ assertMatched(pattern, "/src/");
+ assertMatched(pattern, "/src/a.c");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/new/src/a.c");
+ assertNotMatched(pattern, "/src");
+ assertNotMatched(pattern, "/file/src");
+
+ //Test matches for name-only, applies to file name or folder name
+ //With a small wildcard
+ pattern = "?rc";
+ assertMatched(pattern, "/src/a.c");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/new/src/a.c");
+ assertMatched(pattern, "/file/src");
+ assertMatched(pattern, "/src/");
+
+ //Test matches for name-only, applies to file name or folder name
+ //With a small wildcard
+ pattern = "?r[a-c]";
+ assertMatched(pattern, "/src/a.c");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/new/src/a.c");
+ assertMatched(pattern, "/file/src");
+ assertMatched(pattern, "/src/");
+ assertMatched(pattern, "/srb/a.c");
+ assertMatched(pattern, "/grb/new/a.c");
+ assertMatched(pattern, "/new/crb/a.c");
+ assertMatched(pattern, "/file/3rb");
+ assertMatched(pattern, "/xrb/");
+ assertMatched(pattern, "/3ra/a.c");
+ assertMatched(pattern, "/5ra/new/a.c");
+ assertMatched(pattern, "/new/1ra/a.c");
+ assertMatched(pattern, "/file/dra");
+ assertMatched(pattern, "/era/");
+ assertNotMatched(pattern, "/crg");
+ assertNotMatched(pattern, "/cr3");
+ }
+
+ @Test
+ public void testGetters() {
+ AttributesRule r = new AttributesRule("/pattern/", "");
+ assertFalse(r.isNameOnly());
+ assertTrue(r.dirOnly());
+ assertNotNull(r.getAttributes());
+ assertTrue(r.getAttributes().isEmpty());
+ assertEquals(r.getPattern(), "/pattern");
+
+ r = new AttributesRule("/patter?/", "");
+ assertFalse(r.isNameOnly());
+ assertTrue(r.dirOnly());
+ assertNotNull(r.getAttributes());
+ assertTrue(r.getAttributes().isEmpty());
+ assertEquals(r.getPattern(), "/patter?");
+
+ r = new AttributesRule("patt*", "");
+ assertTrue(r.isNameOnly());
+ assertFalse(r.dirOnly());
+ assertNotNull(r.getAttributes());
+ assertTrue(r.getAttributes().isEmpty());
+ assertEquals(r.getPattern(), "patt*");
+
+ r = new AttributesRule("pattern", "attribute1");
+ assertTrue(r.isNameOnly());
+ assertFalse(r.dirOnly());
+ assertNotNull(r.getAttributes());
+ assertFalse(r.getAttributes().isEmpty());
+ assertEquals(r.getAttributes().size(), 1);
+ assertEquals(r.getPattern(), "pattern");
+
+ r = new AttributesRule("pattern", "attribute1 -attribute2");
+ assertTrue(r.isNameOnly());
+ assertFalse(r.dirOnly());
+ assertNotNull(r.getAttributes());
+ assertEquals(r.getAttributes().size(), 2);
+ assertEquals(r.getPattern(), "pattern");
+
+ r = new AttributesRule("pattern", "attribute1 \t-attribute2 \t");
+ assertTrue(r.isNameOnly());
+ assertFalse(r.dirOnly());
+ assertNotNull(r.getAttributes());
+ assertEquals(r.getAttributes().size(), 2);
+ assertEquals(r.getPattern(), "pattern");
+
+ r = new AttributesRule("pattern", "attribute1\t-attribute2\t");
+ assertTrue(r.isNameOnly());
+ assertFalse(r.dirOnly());
+ assertNotNull(r.getAttributes());
+ assertEquals(r.getAttributes().size(), 2);
+ assertEquals(r.getPattern(), "pattern");
+
+ r = new AttributesRule("pattern", "attribute1\t -attribute2\t ");
+ assertTrue(r.isNameOnly());
+ assertFalse(r.dirOnly());
+ assertNotNull(r.getAttributes());
+ assertEquals(r.getAttributes().size(), 2);
+ assertEquals(r.getPattern(), "pattern");
+
+ r = new AttributesRule("pattern",
+ "attribute1 -attribute2 attribute3=value ");
+ assertTrue(r.isNameOnly());
+ assertFalse(r.dirOnly());
+ assertNotNull(r.getAttributes());
+ assertEquals(r.getAttributes().size(), 3);
+ assertEquals(r.getPattern(), "pattern");
+ assertEquals(r.getAttributes().get(0).toString(), "attribute1");
+ assertEquals(r.getAttributes().get(1).toString(), "-attribute2");
+ assertEquals(r.getAttributes().get(2).toString(), "attribute3=value");
+ }
+
+ /**
+ * Check for a match. If target ends with "/", match will assume that the
+ * target is meant to be a directory.
+ *
+ * @param pattern
+ * Pattern as it would appear in a .gitattributes file
+ * @param target
+ * Target file path relative to repository's GIT_DIR
+ */
+ public void assertMatched(String pattern, String target) {
+ boolean value = match(pattern, target);
+ assertTrue("Expected a match for: " + pattern + " with: " + target,
+ value);
+ }
+
+ /**
+ * Check for a match. If target ends with "/", match will assume that the
+ * target is meant to be a directory.
+ *
+ * @param pattern
+ * Pattern as it would appear in a .gitattributes file
+ * @param target
+ * Target file path relative to repository's GIT_DIR
+ */
+ public void assertNotMatched(String pattern, String target) {
+ boolean value = match(pattern, target);
+ assertFalse("Expected no match for: " + pattern + " with: " + target,
+ value);
+ }
+
+ /**
+ * Check for a match. If target ends with "/", match will assume that the
+ * target is meant to be a directory.
+ *
+ * @param pattern
+ * Pattern as it would appear in a .gitattributes file
+ * @param target
+ * Target file path relative to repository's GIT_DIR
+ * @return Result of {@link AttributesRule#isMatch(String, boolean)}
+ */
+ private static boolean match(String pattern, String target) {
+ AttributesRule r = new AttributesRule(pattern, "");
+ //If speed of this test is ever an issue, we can use a presetRule field
+ //to avoid recompiling a pattern each time.
+ return r.isMatch(target, target.endsWith("/"));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
new file mode 100644
index 0000000000..49279e6e5a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2010, Red Hat Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+import static java.util.Arrays.asList;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.attributes.Attribute.State;
+import org.eclipse.jgit.dircache.DirCacheIterator;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests attributes node behavior on the the index.
+ */
+public class AttributesNodeDirCacheIteratorTest extends RepositoryTestCase {
+
+ private static final FileMode D = FileMode.TREE;
+
+ private static final FileMode F = FileMode.REGULAR_FILE;
+
+ private static Attribute EOL_LF = new Attribute("eol", "lf");
+
+ private static Attribute DELTA_UNSET = new Attribute("delta", State.UNSET);
+
+ private Git git;
+
+ private TreeWalk walk;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ git = new Git(db);
+
+ }
+
+ @Test
+ public void testRules() throws Exception {
+ writeAttributesFile(".git/info/attributes", "windows* eol=crlf");
+
+ writeAttributesFile(".gitattributes", "*.txt eol=lf");
+ writeTrashFile("windows.file", "");
+ writeTrashFile("windows.txt", "");
+ writeTrashFile("readme.txt", "");
+
+ writeAttributesFile("src/config/.gitattributes", "*.txt -delta");
+ writeTrashFile("src/config/readme.txt", "");
+ writeTrashFile("src/config/windows.file", "");
+ writeTrashFile("src/config/windows.txt", "");
+
+ // Adds file to index
+ git.add().addFilepattern(".").call();
+
+ walk = beginWalk();
+
+ assertIteration(F, ".gitattributes");
+ assertIteration(F, "readme.txt", asList(EOL_LF));
+
+ assertIteration(D, "src");
+
+ assertIteration(D, "src/config");
+ assertIteration(F, "src/config/.gitattributes");
+ assertIteration(F, "src/config/readme.txt", asList(DELTA_UNSET));
+ assertIteration(F, "src/config/windows.file", null);
+ assertIteration(F, "src/config/windows.txt", asList(DELTA_UNSET));
+
+ assertIteration(F, "windows.file", null);
+ assertIteration(F, "windows.txt", asList(EOL_LF));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that if there is no .gitattributes file in the repository
+ * everything still work fine.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testNoAttributes() throws Exception {
+ writeTrashFile("l0.txt", "");
+ writeTrashFile("level1/l1.txt", "");
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ // Adds file to index
+ git.add().addFilepattern(".").call();
+ walk = beginWalk();
+
+ assertIteration(F, "l0.txt");
+
+ assertIteration(D, "level1");
+ assertIteration(F, "level1/l1.txt");
+
+ assertIteration(D, "level1/level2");
+ assertIteration(F, "level1/level2/l2.txt");
+
+ endWalk();
+ }
+
+ /**
+ * Checks that empty .gitattribute files do not return incorrect value.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testEmptyGitAttributeFile() throws Exception {
+ writeAttributesFile(".git/info/attributes", "");
+ writeTrashFile("l0.txt", "");
+ writeAttributesFile(".gitattributes", "");
+ writeTrashFile("level1/l1.txt", "");
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ // Adds file to index
+ git.add().addFilepattern(".").call();
+ walk = beginWalk();
+
+ assertIteration(F, ".gitattributes");
+ assertIteration(F, "l0.txt");
+
+ assertIteration(D, "level1");
+ assertIteration(F, "level1/l1.txt");
+
+ assertIteration(D, "level1/level2");
+ assertIteration(F, "level1/level2/l2.txt");
+
+ endWalk();
+ }
+
+ @Test
+ public void testNoMatchingAttributes() throws Exception {
+ writeAttributesFile(".git/info/attributes", "*.java delta");
+ writeAttributesFile(".gitattributes", "*.java -delta");
+ writeAttributesFile("levelA/.gitattributes", "*.java eol=lf");
+ writeAttributesFile("levelB/.gitattributes", "*.txt eol=lf");
+
+ writeTrashFile("levelA/lA.txt", "");
+
+ // Adds file to index
+ git.add().addFilepattern(".").call();
+ walk = beginWalk();
+
+ assertIteration(F, ".gitattributes");
+
+ assertIteration(D, "levelA");
+ assertIteration(F, "levelA/.gitattributes");
+ assertIteration(F, "levelA/lA.txt");
+
+ assertIteration(D, "levelB");
+ assertIteration(F, "levelB/.gitattributes");
+
+ endWalk();
+ }
+
+ @Test
+ public void testIncorrectAttributeFileName() throws Exception {
+ writeAttributesFile("levelA/file.gitattributes", "*.txt -delta");
+ writeAttributesFile("gitattributes", "*.txt eol=lf");
+
+ writeTrashFile("l0.txt", "");
+ writeTrashFile("levelA/lA.txt", "");
+
+ // Adds file to index
+ git.add().addFilepattern(".").call();
+ walk = beginWalk();
+
+ assertIteration(F, "gitattributes");
+
+ assertIteration(F, "l0.txt");
+
+ assertIteration(D, "levelA");
+ assertIteration(F, "levelA/file.gitattributes");
+ assertIteration(F, "levelA/lA.txt");
+
+ endWalk();
+ }
+
+ private void assertIteration(FileMode type, String pathName)
+ throws IOException {
+ assertIteration(type, pathName, Collections.<Attribute> emptyList());
+ }
+
+ private void assertIteration(FileMode type, String pathName,
+ List<Attribute> nodeAttrs) throws IOException {
+ assertTrue("walk has entry", walk.next());
+ assertEquals(pathName, walk.getPathString());
+ assertEquals(type, walk.getFileMode(0));
+ DirCacheIterator itr = walk.getTree(0, DirCacheIterator.class);
+ assertNotNull("has tree", itr);
+
+ AttributesNode attributeNode = itr.getEntryAttributesNode(db
+ .newObjectReader());
+ assertAttributeNode(pathName, attributeNode, nodeAttrs);
+
+ if (D.equals(type))
+ walk.enterSubtree();
+
+ }
+
+ private void assertAttributeNode(String pathName,
+ AttributesNode attributeNode, List<Attribute> nodeAttrs) {
+ if (attributeNode == null)
+ assertTrue(nodeAttrs == null || nodeAttrs.isEmpty());
+ else {
+
+ Map<String, Attribute> entryAttributes = new LinkedHashMap<String, Attribute>();
+ attributeNode.getAttributes(pathName, false, entryAttributes);
+
+ if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
+ for (Attribute attribute : nodeAttrs) {
+ assertThat(entryAttributes.values(), hasItem(attribute));
+ }
+ } else {
+ assertTrue(
+ "The entry "
+ + pathName
+ + " should not have any attributes. Instead, the following attributes are applied to this file "
+ + entryAttributes.toString(),
+ entryAttributes.isEmpty());
+ }
+ }
+ }
+
+ private void writeAttributesFile(String name, String... rules)
+ throws IOException {
+ StringBuilder data = new StringBuilder();
+ for (String line : rules)
+ data.append(line + "\n");
+ writeTrashFile(name, data.toString());
+ }
+
+ private TreeWalk beginWalk() throws Exception {
+ TreeWalk newWalk = new TreeWalk(db);
+ newWalk.addTree(new DirCacheIterator(db.readDirCache()));
+ return newWalk;
+ }
+
+ private void endWalk() throws IOException {
+ assertFalse("Not all files tested", walk.next());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
new file mode 100644
index 0000000000..64b0535d6a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2014, Obeo.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+import static java.util.Arrays.asList;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.attributes.Attribute.State;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+import org.junit.Test;
+
+/**
+ * Tests attributes node behavior on the local filesystem.
+ */
+public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase {
+
+ private static final FileMode D = FileMode.TREE;
+
+ private static final FileMode F = FileMode.REGULAR_FILE;
+
+ private static Attribute EOL_CRLF = new Attribute("eol", "crlf");
+
+ private static Attribute EOL_LF = new Attribute("eol", "lf");
+
+ private static Attribute DELTA_UNSET = new Attribute("delta", State.UNSET);
+
+ private static Attribute CUSTOM_VALUE = new Attribute("custom", "value");
+
+ private TreeWalk walk;
+
+ @Test
+ public void testRules() throws Exception {
+
+ File customAttributeFile = File.createTempFile("tmp_",
+ "customAttributeFile", null);
+ customAttributeFile.deleteOnExit();
+
+ JGitTestUtil.write(customAttributeFile, "*.txt custom=value");
+ db.getConfig().setString("core", null, "attributesfile",
+ customAttributeFile.getAbsolutePath());
+ writeAttributesFile(".git/info/attributes", "windows* eol=crlf");
+
+ writeAttributesFile(".gitattributes", "*.txt eol=lf");
+ writeTrashFile("windows.file", "");
+ writeTrashFile("windows.txt", "");
+ writeTrashFile("global.txt", "");
+ writeTrashFile("readme.txt", "");
+
+ writeAttributesFile("src/config/.gitattributes", "*.txt -delta");
+ writeTrashFile("src/config/readme.txt", "");
+ writeTrashFile("src/config/windows.file", "");
+ writeTrashFile("src/config/windows.txt", "");
+
+ walk = beginWalk();
+
+ assertIteration(F, ".gitattributes");
+ assertIteration(F, "global.txt", asList(EOL_LF), null,
+ asList(CUSTOM_VALUE));
+ assertIteration(F, "readme.txt", asList(EOL_LF), null,
+ asList(CUSTOM_VALUE));
+
+ assertIteration(D, "src");
+
+ assertIteration(D, "src/config");
+ assertIteration(F, "src/config/.gitattributes");
+ assertIteration(F, "src/config/readme.txt", asList(DELTA_UNSET), null,
+ asList(CUSTOM_VALUE));
+ assertIteration(F, "src/config/windows.file", null, asList(EOL_CRLF),
+ null);
+ assertIteration(F, "src/config/windows.txt", asList(DELTA_UNSET),
+ asList(EOL_CRLF), asList(CUSTOM_VALUE));
+
+ assertIteration(F, "windows.file", null, asList(EOL_CRLF), null);
+ assertIteration(F, "windows.txt", asList(EOL_LF), asList(EOL_CRLF),
+ asList(CUSTOM_VALUE));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that if there is no .gitattributes file in the repository
+ * everything still work fine.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testNoAttributes() throws Exception {
+ writeTrashFile("l0.txt", "");
+ writeTrashFile("level1/l1.txt", "");
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ walk = beginWalk();
+
+ assertIteration(F, "l0.txt");
+
+ assertIteration(D, "level1");
+ assertIteration(F, "level1/l1.txt");
+
+ assertIteration(D, "level1/level2");
+ assertIteration(F, "level1/level2/l2.txt");
+
+ endWalk();
+ }
+
+ /**
+ * Checks that empty .gitattribute files do not return incorrect value.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testEmptyGitAttributeFile() throws Exception {
+ writeAttributesFile(".git/info/attributes", "");
+ writeTrashFile("l0.txt", "");
+ writeAttributesFile(".gitattributes", "");
+ writeTrashFile("level1/l1.txt", "");
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ walk = beginWalk();
+
+ assertIteration(F, ".gitattributes");
+ assertIteration(F, "l0.txt");
+
+ assertIteration(D, "level1");
+ assertIteration(F, "level1/l1.txt");
+
+ assertIteration(D, "level1/level2");
+ assertIteration(F, "level1/level2/l2.txt");
+
+ endWalk();
+ }
+
+ @Test
+ public void testNoMatchingAttributes() throws Exception {
+ writeAttributesFile(".git/info/attributes", "*.java delta");
+ writeAttributesFile(".gitattributes", "*.java -delta");
+ writeAttributesFile("levelA/.gitattributes", "*.java eol=lf");
+ writeAttributesFile("levelB/.gitattributes", "*.txt eol=lf");
+
+ writeTrashFile("levelA/lA.txt", "");
+
+ walk = beginWalk();
+
+ assertIteration(F, ".gitattributes");
+
+ assertIteration(D, "levelA");
+ assertIteration(F, "levelA/.gitattributes");
+ assertIteration(F, "levelA/lA.txt");
+
+ assertIteration(D, "levelB");
+ assertIteration(F, "levelB/.gitattributes");
+
+ endWalk();
+ }
+
+ private void assertIteration(FileMode type, String pathName)
+ throws IOException {
+ assertIteration(type, pathName, Collections.<Attribute> emptyList(),
+ Collections.<Attribute> emptyList(),
+ Collections.<Attribute> emptyList());
+ }
+
+ private void assertIteration(FileMode type, String pathName,
+ List<Attribute> nodeAttrs, List<Attribute> infoAttrs,
+ List<Attribute> globalAttrs)
+ throws IOException {
+ assertTrue("walk has entry", walk.next());
+ assertEquals(pathName, walk.getPathString());
+ assertEquals(type, walk.getFileMode(0));
+ WorkingTreeIterator itr = walk.getTree(0, WorkingTreeIterator.class);
+ assertNotNull("has tree", itr);
+
+ AttributesNode attributeNode = itr.getEntryAttributesNode();
+ assertAttributeNode(pathName, attributeNode, nodeAttrs);
+ AttributesNode infoAttributeNode = itr.getInfoAttributesNode();
+ assertAttributeNode(pathName, infoAttributeNode, infoAttrs);
+ AttributesNode globalAttributeNode = itr.getGlobalAttributesNode();
+ assertAttributeNode(pathName, globalAttributeNode, globalAttrs);
+ if (D.equals(type))
+ walk.enterSubtree();
+
+ }
+
+ private void assertAttributeNode(String pathName,
+ AttributesNode attributeNode, List<Attribute> nodeAttrs) {
+ if (attributeNode == null)
+ assertTrue(nodeAttrs == null || nodeAttrs.isEmpty());
+ else {
+
+ Map<String, Attribute> entryAttributes = new LinkedHashMap<String, Attribute>();
+ attributeNode.getAttributes(pathName, false, entryAttributes);
+
+ if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
+ for (Attribute attribute : nodeAttrs) {
+ assertThat(entryAttributes.values(), hasItem(attribute));
+ }
+ } else {
+ assertTrue(
+ "The entry "
+ + pathName
+ + " should not have any attributes. Instead, the following attributes are applied to this file "
+ + entryAttributes.toString(),
+ entryAttributes.isEmpty());
+ }
+ }
+ }
+
+ private void writeAttributesFile(String name, String... rules)
+ throws IOException {
+ StringBuilder data = new StringBuilder();
+ for (String line : rules)
+ data.append(line + "\n");
+ writeTrashFile(name, data.toString());
+ }
+
+ private TreeWalk beginWalk() throws CorruptObjectException {
+ TreeWalk newWalk = new TreeWalk(db);
+ newWalk.addTree(new FileTreeIterator(db));
+ return newWalk;
+ }
+
+ private void endWalk() throws IOException {
+ assertFalse("Not all files tested", walk.next());
+ }
+}