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 java.util.Arrays.asList;
13  import static org.hamcrest.CoreMatchers.hasItem;
14  import static org.hamcrest.MatcherAssert.assertThat;
15  import static org.junit.Assert.assertEquals;
16  import static org.junit.Assert.assertFalse;
17  import static org.junit.Assert.assertNotNull;
18  import static org.junit.Assert.assertTrue;
19  
20  import java.io.IOException;
21  import java.util.Collections;
22  import java.util.List;
23  
24  import org.eclipse.jgit.api.Git;
25  import org.eclipse.jgit.attributes.Attribute.State;
26  import org.eclipse.jgit.dircache.DirCacheIterator;
27  import org.eclipse.jgit.junit.RepositoryTestCase;
28  import org.eclipse.jgit.lib.FileMode;
29  import org.eclipse.jgit.treewalk.TreeWalk;
30  import org.junit.Before;
31  import org.junit.Test;
32  
33  /**
34   * Tests attributes node behavior on the index.
35   */
36  public class AttributesNodeDirCacheIteratorTest extends RepositoryTestCase {
37  
38  	private static final FileMode D = FileMode.TREE;
39  
40  	private static final FileMode F = FileMode.REGULAR_FILE;
41  
42  	private static Attribute EOL_LF = new Attribute("eol", "lf");
43  
44  	private static Attribute DELTA_UNSET = new Attribute("delta", State.UNSET);
45  
46  	private Git git;
47  
48  	@Override
49  	@Before
50  	public void setUp() throws Exception {
51  		super.setUp();
52  		git = new Git(db);
53  
54  	}
55  
56  	@Test
57  	public void testRules() throws Exception {
58  		writeAttributesFile(".git/info/attributes", "windows* eol=crlf");
59  
60  		writeAttributesFile(".gitattributes", "*.txt eol=lf");
61  		writeTrashFile("windows.file", "");
62  		writeTrashFile("windows.txt", "");
63  		writeTrashFile("readme.txt", "");
64  
65  		writeAttributesFile("src/config/.gitattributes", "*.txt -delta");
66  		writeTrashFile("src/config/readme.txt", "");
67  		writeTrashFile("src/config/windows.file", "");
68  		writeTrashFile("src/config/windows.txt", "");
69  
70  		// Adds file to index
71  		git.add().addFilepattern(".").call();
72  
73  		try (TreeWalk walk = beginWalk()) {
74  			assertIteration(walk, F, ".gitattributes");
75  			assertIteration(walk, F, "readme.txt", asList(EOL_LF));
76  
77  			assertIteration(walk, D, "src");
78  
79  			assertIteration(walk, D, "src/config");
80  			assertIteration(walk, F, "src/config/.gitattributes");
81  			assertIteration(walk, F, "src/config/readme.txt",
82  					asList(DELTA_UNSET));
83  			assertIteration(walk, F, "src/config/windows.file", null);
84  			assertIteration(walk, F, "src/config/windows.txt",
85  					asList(DELTA_UNSET));
86  
87  			assertIteration(walk, F, "windows.file", null);
88  			assertIteration(walk, F, "windows.txt", asList(EOL_LF));
89  
90  			assertFalse("Not all files tested", walk.next());
91  		}
92  	}
93  
94  	/**
95  	 * Checks that if there is no .gitattributes file in the repository
96  	 * everything still work fine.
97  	 *
98  	 * @throws Exception
99  	 */
100 	@Test
101 	public void testNoAttributes() throws Exception {
102 		writeTrashFile("l0.txt", "");
103 		writeTrashFile("level1/l1.txt", "");
104 		writeTrashFile("level1/level2/l2.txt", "");
105 
106 		// Adds file to index
107 		git.add().addFilepattern(".").call();
108 
109 		try (TreeWalk walk = beginWalk()) {
110 			assertIteration(walk, F, "l0.txt");
111 
112 			assertIteration(walk, D, "level1");
113 			assertIteration(walk, F, "level1/l1.txt");
114 
115 			assertIteration(walk, D, "level1/level2");
116 			assertIteration(walk, F, "level1/level2/l2.txt");
117 
118 			assertFalse("Not all files tested", walk.next());
119 		}
120 	}
121 
122 	/**
123 	 * Checks that empty .gitattribute files do not return incorrect value.
124 	 *
125 	 * @throws Exception
126 	 */
127 	@Test
128 	public void testEmptyGitAttributeFile() throws Exception {
129 		writeAttributesFile(".git/info/attributes", "");
130 		writeTrashFile("l0.txt", "");
131 		writeAttributesFile(".gitattributes", "");
132 		writeTrashFile("level1/l1.txt", "");
133 		writeTrashFile("level1/level2/l2.txt", "");
134 
135 		// Adds file to index
136 		git.add().addFilepattern(".").call();
137 
138 		try (TreeWalk walk = beginWalk()) {
139 			assertIteration(walk, F, ".gitattributes");
140 			assertIteration(walk, F, "l0.txt");
141 
142 			assertIteration(walk, D, "level1");
143 			assertIteration(walk, F, "level1/l1.txt");
144 
145 			assertIteration(walk, D, "level1/level2");
146 			assertIteration(walk, F, "level1/level2/l2.txt");
147 
148 			assertFalse("Not all files tested", walk.next());
149 		}
150 	}
151 
152 	@Test
153 	public void testNoMatchingAttributes() throws Exception {
154 		writeAttributesFile(".git/info/attributes", "*.java delta");
155 		writeAttributesFile(".gitattributes", "*.java -delta");
156 		writeAttributesFile("levelA/.gitattributes", "*.java eol=lf");
157 		writeAttributesFile("levelB/.gitattributes", "*.txt eol=lf");
158 
159 		writeTrashFile("levelA/lA.txt", "");
160 
161 		// Adds file to index
162 		git.add().addFilepattern(".").call();
163 
164 		try (TreeWalk walk = beginWalk()) {
165 			assertIteration(walk, F, ".gitattributes");
166 
167 			assertIteration(walk, D, "levelA");
168 			assertIteration(walk, F, "levelA/.gitattributes");
169 			assertIteration(walk, F, "levelA/lA.txt");
170 
171 			assertIteration(walk, D, "levelB");
172 			assertIteration(walk, F, "levelB/.gitattributes");
173 
174 			assertFalse("Not all files tested", walk.next());
175 		}
176 	}
177 
178 	@Test
179 	public void testIncorrectAttributeFileName() throws Exception {
180 		writeAttributesFile("levelA/file.gitattributes", "*.txt -delta");
181 		writeAttributesFile("gitattributes", "*.txt eol=lf");
182 
183 		writeTrashFile("l0.txt", "");
184 		writeTrashFile("levelA/lA.txt", "");
185 
186 		// Adds file to index
187 		git.add().addFilepattern(".").call();
188 
189 		try (TreeWalk walk = beginWalk()) {
190 			assertIteration(walk, F, "gitattributes");
191 
192 			assertIteration(walk, F, "l0.txt");
193 
194 			assertIteration(walk, D, "levelA");
195 			assertIteration(walk, F, "levelA/file.gitattributes");
196 			assertIteration(walk, F, "levelA/lA.txt");
197 
198 			assertFalse("Not all files tested", walk.next());
199 		}
200 	}
201 
202 	private void assertIteration(TreeWalk walk, FileMode type, String pathName)
203 			throws IOException {
204 		assertIteration(walk, type, pathName,
205 				Collections.<Attribute> emptyList());
206 	}
207 
208 	private void assertIteration(TreeWalk walk, FileMode type, String pathName,
209 			List<Attribute> nodeAttrs) throws IOException {
210 		assertTrue("walk has entry", walk.next());
211 		assertEquals(pathName, walk.getPathString());
212 		assertEquals(type, walk.getFileMode(0));
213 		DirCacheIterator itr = walk.getTree(0, DirCacheIterator.class);
214 		assertNotNull("has tree", itr);
215 
216 		AttributesNode attributesNode = itr.getEntryAttributesNode(db
217 				.newObjectReader());
218 		assertAttributesNode(walk, pathName, attributesNode, nodeAttrs);
219 
220 		if (D.equals(type))
221 			walk.enterSubtree();
222 
223 	}
224 
225 	private void assertAttributesNode(TreeWalk walk, String pathName,
226 			AttributesNode attributesNode, List<Attribute> nodeAttrs)
227 					throws IOException {
228 		if (attributesNode == null)
229 			assertTrue(nodeAttrs == null || nodeAttrs.isEmpty());
230 		else {
231 
232 			Attributes entryAttributes = new Attributes();
233 			new AttributesHandler(walk).mergeAttributes(attributesNode,
234 					pathName,
235 					false,
236 					entryAttributes);
237 
238 			if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
239 				for (Attribute attribute : nodeAttrs) {
240 					assertThat(entryAttributes.getAll(),
241 							hasItem(attribute));
242 				}
243 			} else {
244 				assertTrue(
245 						"The entry "
246 								+ pathName
247 								+ " should not have any attributes. Instead, the following attributes are applied to this file "
248 								+ entryAttributes.toString(),
249 						entryAttributes.isEmpty());
250 			}
251 		}
252 	}
253 
254 	private void writeAttributesFile(String name, String... rules)
255 			throws IOException {
256 		StringBuilder data = new StringBuilder();
257 		for (String line : rules)
258 			data.append(line + "\n");
259 		writeTrashFile(name, data.toString());
260 	}
261 
262 	private TreeWalk beginWalk() throws Exception {
263 		TreeWalk newWalk = new TreeWalk(db);
264 		newWalk.addTree(new DirCacheIterator(db.readDirCache()));
265 		return newWalk;
266 	}
267 }