View Javadoc
1   /*
2    * Copyright (C) 2008-2009, Google Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.treewalk;
45  
46  import static java.nio.charset.StandardCharsets.UTF_8;
47  import static org.eclipse.jgit.lib.FileMode.REGULAR_FILE;
48  import static org.eclipse.jgit.lib.FileMode.SYMLINK;
49  import static org.junit.Assert.assertEquals;
50  import static org.junit.Assert.assertFalse;
51  import static org.junit.Assert.assertSame;
52  import static org.junit.Assert.assertTrue;
53  
54  import java.io.ByteArrayOutputStream;
55  
56  import org.eclipse.jgit.errors.CorruptObjectException;
57  import org.eclipse.jgit.lib.Constants;
58  import org.eclipse.jgit.lib.FileMode;
59  import org.eclipse.jgit.lib.ObjectId;
60  import org.eclipse.jgit.lib.TreeFormatter;
61  import org.eclipse.jgit.util.RawParseUtils;
62  import org.junit.Before;
63  import org.junit.Test;
64  
65  public class CanonicalTreeParserTest {
66  	private final CanonicalTreeParser ctp = new CanonicalTreeParser();
67  
68  	private final FileMode m644 = FileMode.REGULAR_FILE;
69  
70  	private final FileMode mt = FileMode.TREE;
71  
72  	private final ObjectId hash_a = ObjectId
73  			.fromString("6b9c715d21d5486e59083fb6071566aa6ecd4d42");
74  
75  	private final ObjectId hash_foo = ObjectId
76  			.fromString("a213e8e25bb2442326e86cbfb9ef56319f482869");
77  
78  	private final ObjectId hash_sometree = ObjectId
79  			.fromString("daf4bdb0d7bb24319810fe0e73aa317663448c93");
80  
81  	private byte[] tree1;
82  
83  	private byte[] tree2;
84  
85  	private byte[] tree3;
86  
87  	@Before
88  	public void setUp() throws Exception {
89  		tree1 = mktree(entry(m644, "a", hash_a));
90  		tree2 = mktree(entry(m644, "a", hash_a), entry(m644, "foo", hash_foo));
91  		tree3 = mktree(entry(m644, "a", hash_a), entry(mt, "b_sometree",
92  				hash_sometree), entry(m644, "foo", hash_foo));
93  	}
94  
95  	private static byte[] mktree(byte[]... data) throws Exception {
96  		final ByteArrayOutputStream out = new ByteArrayOutputStream();
97  		for (byte[] e : data)
98  			out.write(e);
99  		return out.toByteArray();
100 	}
101 
102 	private static byte[] entry(final FileMode mode, final String name,
103 			final ObjectId id) throws Exception {
104 		final ByteArrayOutputStream out = new ByteArrayOutputStream();
105 		mode.copyTo(out);
106 		out.write(' ');
107 		out.write(Constants.encode(name));
108 		out.write(0);
109 		id.copyRawTo(out);
110 		return out.toByteArray();
111 	}
112 
113 	private String path() {
114 		return RawParseUtils.decode(UTF_8, ctp.path,
115 				ctp.pathOffset, ctp.pathLen);
116 	}
117 
118 	@Test
119 	public void testEmptyTree_AtEOF() throws Exception {
120 		ctp.reset(new byte[0]);
121 		assertTrue(ctp.eof());
122 	}
123 
124 	@Test
125 	public void testOneEntry_Forward() throws Exception {
126 		ctp.reset(tree1);
127 
128 		assertTrue(ctp.first());
129 		assertFalse(ctp.eof());
130 		assertEquals(m644.getBits(), ctp.mode);
131 		assertEquals("a", path());
132 		assertEquals(hash_a, ctp.getEntryObjectId());
133 
134 		ctp.next(1);
135 		assertFalse(ctp.first());
136 		assertTrue(ctp.eof());
137 	}
138 
139 	@Test
140 	public void testTwoEntries_ForwardOneAtATime() throws Exception {
141 		ctp.reset(tree2);
142 
143 		assertTrue(ctp.first());
144 		assertFalse(ctp.eof());
145 		assertEquals(m644.getBits(), ctp.mode);
146 		assertEquals("a", path());
147 		assertEquals(hash_a, ctp.getEntryObjectId());
148 
149 		ctp.next(1);
150 		assertFalse(ctp.eof());
151 		assertEquals(m644.getBits(), ctp.mode);
152 		assertEquals("foo", path());
153 		assertEquals(hash_foo, ctp.getEntryObjectId());
154 
155 		ctp.next(1);
156 		assertFalse(ctp.first());
157 		assertTrue(ctp.eof());
158 	}
159 
160 	@Test
161 	public void testOneEntry_Seek1IsEOF() throws Exception {
162 		ctp.reset(tree1);
163 		ctp.next(1);
164 		assertTrue(ctp.eof());
165 	}
166 
167 	@Test
168 	public void testTwoEntries_Seek2IsEOF() throws Exception {
169 		ctp.reset(tree2);
170 		ctp.next(2);
171 		assertTrue(ctp.eof());
172 	}
173 
174 	@Test
175 	public void testThreeEntries_Seek3IsEOF() throws Exception {
176 		ctp.reset(tree3);
177 		ctp.next(3);
178 		assertTrue(ctp.eof());
179 	}
180 
181 	@Test
182 	public void testThreeEntries_Seek2() throws Exception {
183 		ctp.reset(tree3);
184 
185 		ctp.next(2);
186 		assertFalse(ctp.eof());
187 		assertFalse(ctp.eof());
188 		assertEquals(m644.getBits(), ctp.mode);
189 		assertEquals("foo", path());
190 		assertEquals(hash_foo, ctp.getEntryObjectId());
191 
192 		ctp.next(1);
193 		assertTrue(ctp.eof());
194 	}
195 
196 	@Test
197 	public void testOneEntry_Backwards() throws Exception {
198 		ctp.reset(tree1);
199 		ctp.next(1);
200 		assertFalse(ctp.first());
201 		assertTrue(ctp.eof());
202 
203 		ctp.back(1);
204 		assertTrue(ctp.first());
205 		assertFalse(ctp.eof());
206 		assertEquals(m644.getBits(), ctp.mode);
207 		assertEquals("a", path());
208 		assertEquals(hash_a, ctp.getEntryObjectId());
209 	}
210 
211 	@Test
212 	public void testTwoEntries_BackwardsOneAtATime() throws Exception {
213 		ctp.reset(tree2);
214 		ctp.next(2);
215 		assertTrue(ctp.eof());
216 
217 		ctp.back(1);
218 		assertFalse(ctp.eof());
219 		assertEquals(m644.getBits(), ctp.mode);
220 		assertEquals("foo", path());
221 		assertEquals(hash_foo, ctp.getEntryObjectId());
222 
223 		ctp.back(1);
224 		assertFalse(ctp.eof());
225 		assertEquals(m644.getBits(), ctp.mode);
226 		assertEquals("a", path());
227 		assertEquals(hash_a, ctp.getEntryObjectId());
228 	}
229 
230 	@Test
231 	public void testTwoEntries_BackwardsTwo() throws Exception {
232 		ctp.reset(tree2);
233 		ctp.next(2);
234 		assertTrue(ctp.eof());
235 
236 		ctp.back(2);
237 		assertFalse(ctp.eof());
238 		assertEquals(m644.getBits(), ctp.mode);
239 		assertEquals("a", path());
240 		assertEquals(hash_a, ctp.getEntryObjectId());
241 
242 		ctp.next(1);
243 		assertFalse(ctp.eof());
244 		assertEquals(m644.getBits(), ctp.mode);
245 		assertEquals("foo", path());
246 		assertEquals(hash_foo, ctp.getEntryObjectId());
247 
248 		ctp.next(1);
249 		assertTrue(ctp.eof());
250 	}
251 
252 	@Test
253 	public void testThreeEntries_BackwardsTwo() throws Exception {
254 		ctp.reset(tree3);
255 		ctp.next(3);
256 		assertTrue(ctp.eof());
257 
258 		ctp.back(2);
259 		assertFalse(ctp.eof());
260 		assertEquals(mt.getBits(), ctp.mode);
261 		assertEquals("b_sometree", path());
262 		assertEquals(hash_sometree, ctp.getEntryObjectId());
263 
264 		ctp.next(1);
265 		assertFalse(ctp.eof());
266 		assertEquals(m644.getBits(), ctp.mode);
267 		assertEquals("foo", path());
268 		assertEquals(hash_foo, ctp.getEntryObjectId());
269 
270 		ctp.next(1);
271 		assertTrue(ctp.eof());
272 	}
273 
274 	@Test
275 	public void testBackwards_ConfusingPathName() throws Exception {
276 		final String aVeryConfusingName = "confusing 644 entry 755 and others";
277 		ctp.reset(mktree(entry(m644, "a", hash_a), entry(mt, aVeryConfusingName,
278 				hash_sometree), entry(m644, "foo", hash_foo)));
279 		ctp.next(3);
280 		assertTrue(ctp.eof());
281 
282 		ctp.back(2);
283 		assertFalse(ctp.eof());
284 		assertEquals(mt.getBits(), ctp.mode);
285 		assertEquals(aVeryConfusingName, path());
286 		assertEquals(hash_sometree, ctp.getEntryObjectId());
287 
288 		ctp.back(1);
289 		assertFalse(ctp.eof());
290 		assertEquals(m644.getBits(), ctp.mode);
291 		assertEquals("a", path());
292 		assertEquals(hash_a, ctp.getEntryObjectId());
293 	}
294 
295 	@Test
296 	public void testBackwords_Prebuilts1() throws Exception {
297 		// What is interesting about this test is the ObjectId for the
298 		// "darwin-x86" path entry ends in an octal digit (37 == '7').
299 		// Thus when scanning backwards we could over scan and consume
300 		// part of the SHA-1, and miss the path terminator.
301 		//
302 		final ObjectId common = ObjectId
303 				.fromString("af7bf97cb9bce3f60f1d651a0ef862e9447dd8bc");
304 		final ObjectId darwinx86 = ObjectId
305 				.fromString("e927f7398240f78face99e1a738dac54ef738e37");
306 		final ObjectId linuxx86 = ObjectId
307 				.fromString("ac08dd97120c7cb7d06e98cd5b152011183baf21");
308 		final ObjectId windows = ObjectId
309 				.fromString("6c4c64c221a022bb973165192cca4812033479df");
310 
311 		ctp.reset(mktree(entry(mt, "common", common), entry(mt, "darwin-x86",
312 				darwinx86), entry(mt, "linux-x86", linuxx86), entry(mt,
313 				"windows", windows)));
314 		ctp.next(3);
315 		assertEquals("windows", ctp.getEntryPathString());
316 		assertSame(mt, ctp.getEntryFileMode());
317 		assertEquals(windows, ctp.getEntryObjectId());
318 
319 		ctp.back(1);
320 		assertEquals("linux-x86", ctp.getEntryPathString());
321 		assertSame(mt, ctp.getEntryFileMode());
322 		assertEquals(linuxx86, ctp.getEntryObjectId());
323 
324 		ctp.next(1);
325 		assertEquals("windows", ctp.getEntryPathString());
326 		assertSame(mt, ctp.getEntryFileMode());
327 		assertEquals(windows, ctp.getEntryObjectId());
328 	}
329 
330 	@Test
331 	public void testBackwords_Prebuilts2() throws Exception {
332 		// What is interesting about this test is the ObjectId for the
333 		// "darwin-x86" path entry ends in an octal digit (37 == '7').
334 		// Thus when scanning backwards we could over scan and consume
335 		// part of the SHA-1, and miss the path terminator.
336 		//
337 		final ObjectId common = ObjectId
338 				.fromString("af7bf97cb9bce3f60f1d651a0ef862e9447dd8bc");
339 		final ObjectId darwinx86 = ObjectId
340 				.fromString("0000000000000000000000000000000000000037");
341 		final ObjectId linuxx86 = ObjectId
342 				.fromString("ac08dd97120c7cb7d06e98cd5b152011183baf21");
343 		final ObjectId windows = ObjectId
344 				.fromString("6c4c64c221a022bb973165192cca4812033479df");
345 
346 		ctp.reset(mktree(entry(mt, "common", common), entry(mt, "darwin-x86",
347 				darwinx86), entry(mt, "linux-x86", linuxx86), entry(mt,
348 				"windows", windows)));
349 		ctp.next(3);
350 		assertEquals("windows", ctp.getEntryPathString());
351 		assertSame(mt, ctp.getEntryFileMode());
352 		assertEquals(windows, ctp.getEntryObjectId());
353 
354 		ctp.back(1);
355 		assertEquals("linux-x86", ctp.getEntryPathString());
356 		assertSame(mt, ctp.getEntryFileMode());
357 		assertEquals(linuxx86, ctp.getEntryObjectId());
358 
359 		ctp.next(1);
360 		assertEquals("windows", ctp.getEntryPathString());
361 		assertSame(mt, ctp.getEntryFileMode());
362 		assertEquals(windows, ctp.getEntryObjectId());
363 	}
364 
365 	@Test
366 	public void testFreakingHugePathName() throws Exception {
367 		final int n = AbstractTreeIterator.DEFAULT_PATH_SIZE * 4;
368 		final StringBuilder b = new StringBuilder(n);
369 		for (int i = 0; i < n; i++)
370 			b.append('q');
371 		final String name = b.toString();
372 		ctp.reset(entry(m644, name, hash_a));
373 		assertFalse(ctp.eof());
374 		assertEquals(name, RawParseUtils.decode(UTF_8, ctp.path,
375 				ctp.pathOffset, ctp.pathLen));
376 	}
377 
378 	@Test
379 	public void testFindAttributesWhenFirst() throws CorruptObjectException {
380 		TreeFormatter tree = new TreeFormatter();
381 		tree.append(".gitattributes", REGULAR_FILE, hash_a);
382 		ctp.reset(tree.toByteArray());
383 
384 		assertTrue(ctp.findFile(".gitattributes"));
385 		assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode());
386 		assertEquals(".gitattributes", ctp.getEntryPathString());
387 		assertEquals(hash_a, ctp.getEntryObjectId());
388 	}
389 
390 	@Test
391 	public void testFindAttributesWhenSecond() throws CorruptObjectException {
392 		TreeFormatter tree = new TreeFormatter();
393 		tree.append(".config", SYMLINK, hash_a);
394 		tree.append(".gitattributes", REGULAR_FILE, hash_foo);
395 		ctp.reset(tree.toByteArray());
396 
397 		assertTrue(ctp.findFile(".gitattributes"));
398 		assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode());
399 		assertEquals(".gitattributes", ctp.getEntryPathString());
400 		assertEquals(hash_foo, ctp.getEntryObjectId());
401 	}
402 
403 	@Test
404 	public void testFindAttributesWhenMissing() throws CorruptObjectException {
405 		TreeFormatter tree = new TreeFormatter();
406 		tree.append("src", REGULAR_FILE, hash_a);
407 		tree.append("zoo", REGULAR_FILE, hash_foo);
408 		ctp.reset(tree.toByteArray());
409 
410 		assertFalse(ctp.findFile(".gitattributes"));
411 		assertEquals(11, ctp.idOffset()); // Did not walk the entire tree.
412 		assertEquals("src", ctp.getEntryPathString());
413 	}
414 }