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 org.eclipse.jgit.lib.FileMode.REGULAR_FILE;
47  import static org.eclipse.jgit.lib.FileMode.SYMLINK;
48  import static org.junit.Assert.assertEquals;
49  import static org.junit.Assert.assertFalse;
50  import static org.junit.Assert.assertSame;
51  import static org.junit.Assert.assertTrue;
52  
53  import java.io.ByteArrayOutputStream;
54  
55  import org.eclipse.jgit.errors.CorruptObjectException;
56  import org.eclipse.jgit.lib.Constants;
57  import org.eclipse.jgit.lib.FileMode;
58  import org.eclipse.jgit.lib.ObjectId;
59  import org.eclipse.jgit.lib.TreeFormatter;
60  import org.eclipse.jgit.util.RawParseUtils;
61  import org.junit.Before;
62  import org.junit.Test;
63  
64  public class CanonicalTreeParserTest {
65  	private final CanonicalTreeParser ctp = new CanonicalTreeParser();
66  
67  	private final FileMode m644 = FileMode.REGULAR_FILE;
68  
69  	private final FileMode mt = FileMode.TREE;
70  
71  	private final ObjectId hash_a = ObjectId
72  			.fromString("6b9c715d21d5486e59083fb6071566aa6ecd4d42");
73  
74  	private final ObjectId hash_foo = ObjectId
75  			.fromString("a213e8e25bb2442326e86cbfb9ef56319f482869");
76  
77  	private final ObjectId hash_sometree = ObjectId
78  			.fromString("daf4bdb0d7bb24319810fe0e73aa317663448c93");
79  
80  	private byte[] tree1;
81  
82  	private byte[] tree2;
83  
84  	private byte[] tree3;
85  
86  	@Before
87  	public void setUp() throws Exception {
88  		tree1 = mktree(entry(m644, "a", hash_a));
89  		tree2 = mktree(entry(m644, "a", hash_a), entry(m644, "foo", hash_foo));
90  		tree3 = mktree(entry(m644, "a", hash_a), entry(mt, "b_sometree",
91  				hash_sometree), entry(m644, "foo", hash_foo));
92  	}
93  
94  	private static byte[] mktree(final byte[]... data) throws Exception {
95  		final ByteArrayOutputStream out = new ByteArrayOutputStream();
96  		for (final byte[] e : data)
97  			out.write(e);
98  		return out.toByteArray();
99  	}
100 
101 	private static byte[] entry(final FileMode mode, final String name,
102 			final ObjectId id) throws Exception {
103 		final ByteArrayOutputStream out = new ByteArrayOutputStream();
104 		mode.copyTo(out);
105 		out.write(' ');
106 		out.write(Constants.encode(name));
107 		out.write(0);
108 		id.copyRawTo(out);
109 		return out.toByteArray();
110 	}
111 
112 	private String path() {
113 		return RawParseUtils.decode(Constants.CHARSET, ctp.path,
114 				ctp.pathOffset, ctp.pathLen);
115 	}
116 
117 	@Test
118 	public void testEmptyTree_AtEOF() throws Exception {
119 		ctp.reset(new byte[0]);
120 		assertTrue(ctp.eof());
121 	}
122 
123 	@Test
124 	public void testOneEntry_Forward() throws Exception {
125 		ctp.reset(tree1);
126 
127 		assertTrue(ctp.first());
128 		assertFalse(ctp.eof());
129 		assertEquals(m644.getBits(), ctp.mode);
130 		assertEquals("a", path());
131 		assertEquals(hash_a, ctp.getEntryObjectId());
132 
133 		ctp.next(1);
134 		assertFalse(ctp.first());
135 		assertTrue(ctp.eof());
136 	}
137 
138 	@Test
139 	public void testTwoEntries_ForwardOneAtATime() throws Exception {
140 		ctp.reset(tree2);
141 
142 		assertTrue(ctp.first());
143 		assertFalse(ctp.eof());
144 		assertEquals(m644.getBits(), ctp.mode);
145 		assertEquals("a", path());
146 		assertEquals(hash_a, ctp.getEntryObjectId());
147 
148 		ctp.next(1);
149 		assertFalse(ctp.eof());
150 		assertEquals(m644.getBits(), ctp.mode);
151 		assertEquals("foo", path());
152 		assertEquals(hash_foo, ctp.getEntryObjectId());
153 
154 		ctp.next(1);
155 		assertFalse(ctp.first());
156 		assertTrue(ctp.eof());
157 	}
158 
159 	@Test
160 	public void testOneEntry_Seek1IsEOF() throws Exception {
161 		ctp.reset(tree1);
162 		ctp.next(1);
163 		assertTrue(ctp.eof());
164 	}
165 
166 	@Test
167 	public void testTwoEntries_Seek2IsEOF() throws Exception {
168 		ctp.reset(tree2);
169 		ctp.next(2);
170 		assertTrue(ctp.eof());
171 	}
172 
173 	@Test
174 	public void testThreeEntries_Seek3IsEOF() throws Exception {
175 		ctp.reset(tree3);
176 		ctp.next(3);
177 		assertTrue(ctp.eof());
178 	}
179 
180 	@Test
181 	public void testThreeEntries_Seek2() throws Exception {
182 		ctp.reset(tree3);
183 
184 		ctp.next(2);
185 		assertFalse(ctp.eof());
186 		assertFalse(ctp.eof());
187 		assertEquals(m644.getBits(), ctp.mode);
188 		assertEquals("foo", path());
189 		assertEquals(hash_foo, ctp.getEntryObjectId());
190 
191 		ctp.next(1);
192 		assertTrue(ctp.eof());
193 	}
194 
195 	@Test
196 	public void testOneEntry_Backwards() throws Exception {
197 		ctp.reset(tree1);
198 		ctp.next(1);
199 		assertFalse(ctp.first());
200 		assertTrue(ctp.eof());
201 
202 		ctp.back(1);
203 		assertTrue(ctp.first());
204 		assertFalse(ctp.eof());
205 		assertEquals(m644.getBits(), ctp.mode);
206 		assertEquals("a", path());
207 		assertEquals(hash_a, ctp.getEntryObjectId());
208 	}
209 
210 	@Test
211 	public void testTwoEntries_BackwardsOneAtATime() throws Exception {
212 		ctp.reset(tree2);
213 		ctp.next(2);
214 		assertTrue(ctp.eof());
215 
216 		ctp.back(1);
217 		assertFalse(ctp.eof());
218 		assertEquals(m644.getBits(), ctp.mode);
219 		assertEquals("foo", path());
220 		assertEquals(hash_foo, ctp.getEntryObjectId());
221 
222 		ctp.back(1);
223 		assertFalse(ctp.eof());
224 		assertEquals(m644.getBits(), ctp.mode);
225 		assertEquals("a", path());
226 		assertEquals(hash_a, ctp.getEntryObjectId());
227 	}
228 
229 	@Test
230 	public void testTwoEntries_BackwardsTwo() throws Exception {
231 		ctp.reset(tree2);
232 		ctp.next(2);
233 		assertTrue(ctp.eof());
234 
235 		ctp.back(2);
236 		assertFalse(ctp.eof());
237 		assertEquals(m644.getBits(), ctp.mode);
238 		assertEquals("a", path());
239 		assertEquals(hash_a, ctp.getEntryObjectId());
240 
241 		ctp.next(1);
242 		assertFalse(ctp.eof());
243 		assertEquals(m644.getBits(), ctp.mode);
244 		assertEquals("foo", path());
245 		assertEquals(hash_foo, ctp.getEntryObjectId());
246 
247 		ctp.next(1);
248 		assertTrue(ctp.eof());
249 	}
250 
251 	@Test
252 	public void testThreeEntries_BackwardsTwo() throws Exception {
253 		ctp.reset(tree3);
254 		ctp.next(3);
255 		assertTrue(ctp.eof());
256 
257 		ctp.back(2);
258 		assertFalse(ctp.eof());
259 		assertEquals(mt.getBits(), ctp.mode);
260 		assertEquals("b_sometree", path());
261 		assertEquals(hash_sometree, ctp.getEntryObjectId());
262 
263 		ctp.next(1);
264 		assertFalse(ctp.eof());
265 		assertEquals(m644.getBits(), ctp.mode);
266 		assertEquals("foo", path());
267 		assertEquals(hash_foo, ctp.getEntryObjectId());
268 
269 		ctp.next(1);
270 		assertTrue(ctp.eof());
271 	}
272 
273 	@Test
274 	public void testBackwards_ConfusingPathName() throws Exception {
275 		final String aVeryConfusingName = "confusing 644 entry 755 and others";
276 		ctp.reset(mktree(entry(m644, "a", hash_a), entry(mt, aVeryConfusingName,
277 				hash_sometree), entry(m644, "foo", hash_foo)));
278 		ctp.next(3);
279 		assertTrue(ctp.eof());
280 
281 		ctp.back(2);
282 		assertFalse(ctp.eof());
283 		assertEquals(mt.getBits(), ctp.mode);
284 		assertEquals(aVeryConfusingName, path());
285 		assertEquals(hash_sometree, ctp.getEntryObjectId());
286 
287 		ctp.back(1);
288 		assertFalse(ctp.eof());
289 		assertEquals(m644.getBits(), ctp.mode);
290 		assertEquals("a", path());
291 		assertEquals(hash_a, ctp.getEntryObjectId());
292 	}
293 
294 	@Test
295 	public void testBackwords_Prebuilts1() throws Exception {
296 		// What is interesting about this test is the ObjectId for the
297 		// "darwin-x86" path entry ends in an octal digit (37 == '7').
298 		// Thus when scanning backwards we could over scan and consume
299 		// part of the SHA-1, and miss the path terminator.
300 		//
301 		final ObjectId common = ObjectId
302 				.fromString("af7bf97cb9bce3f60f1d651a0ef862e9447dd8bc");
303 		final ObjectId darwinx86 = ObjectId
304 				.fromString("e927f7398240f78face99e1a738dac54ef738e37");
305 		final ObjectId linuxx86 = ObjectId
306 				.fromString("ac08dd97120c7cb7d06e98cd5b152011183baf21");
307 		final ObjectId windows = ObjectId
308 				.fromString("6c4c64c221a022bb973165192cca4812033479df");
309 
310 		ctp.reset(mktree(entry(mt, "common", common), entry(mt, "darwin-x86",
311 				darwinx86), entry(mt, "linux-x86", linuxx86), entry(mt,
312 				"windows", windows)));
313 		ctp.next(3);
314 		assertEquals("windows", ctp.getEntryPathString());
315 		assertSame(mt, ctp.getEntryFileMode());
316 		assertEquals(windows, ctp.getEntryObjectId());
317 
318 		ctp.back(1);
319 		assertEquals("linux-x86", ctp.getEntryPathString());
320 		assertSame(mt, ctp.getEntryFileMode());
321 		assertEquals(linuxx86, ctp.getEntryObjectId());
322 
323 		ctp.next(1);
324 		assertEquals("windows", ctp.getEntryPathString());
325 		assertSame(mt, ctp.getEntryFileMode());
326 		assertEquals(windows, ctp.getEntryObjectId());
327 	}
328 
329 	@Test
330 	public void testBackwords_Prebuilts2() throws Exception {
331 		// What is interesting about this test is the ObjectId for the
332 		// "darwin-x86" path entry ends in an octal digit (37 == '7').
333 		// Thus when scanning backwards we could over scan and consume
334 		// part of the SHA-1, and miss the path terminator.
335 		//
336 		final ObjectId common = ObjectId
337 				.fromString("af7bf97cb9bce3f60f1d651a0ef862e9447dd8bc");
338 		final ObjectId darwinx86 = ObjectId
339 				.fromString("0000000000000000000000000000000000000037");
340 		final ObjectId linuxx86 = ObjectId
341 				.fromString("ac08dd97120c7cb7d06e98cd5b152011183baf21");
342 		final ObjectId windows = ObjectId
343 				.fromString("6c4c64c221a022bb973165192cca4812033479df");
344 
345 		ctp.reset(mktree(entry(mt, "common", common), entry(mt, "darwin-x86",
346 				darwinx86), entry(mt, "linux-x86", linuxx86), entry(mt,
347 				"windows", windows)));
348 		ctp.next(3);
349 		assertEquals("windows", ctp.getEntryPathString());
350 		assertSame(mt, ctp.getEntryFileMode());
351 		assertEquals(windows, ctp.getEntryObjectId());
352 
353 		ctp.back(1);
354 		assertEquals("linux-x86", ctp.getEntryPathString());
355 		assertSame(mt, ctp.getEntryFileMode());
356 		assertEquals(linuxx86, ctp.getEntryObjectId());
357 
358 		ctp.next(1);
359 		assertEquals("windows", ctp.getEntryPathString());
360 		assertSame(mt, ctp.getEntryFileMode());
361 		assertEquals(windows, ctp.getEntryObjectId());
362 	}
363 
364 	@Test
365 	public void testFreakingHugePathName() throws Exception {
366 		final int n = AbstractTreeIterator.DEFAULT_PATH_SIZE * 4;
367 		final StringBuilder b = new StringBuilder(n);
368 		for (int i = 0; i < n; i++)
369 			b.append('q');
370 		final String name = b.toString();
371 		ctp.reset(entry(m644, name, hash_a));
372 		assertFalse(ctp.eof());
373 		assertEquals(name, RawParseUtils.decode(Constants.CHARSET, ctp.path,
374 				ctp.pathOffset, ctp.pathLen));
375 	}
376 
377 	@Test
378 	public void testFindAttributesWhenFirst() throws CorruptObjectException {
379 		TreeFormatter tree = new TreeFormatter();
380 		tree.append(".gitattributes", REGULAR_FILE, hash_a);
381 		ctp.reset(tree.toByteArray());
382 
383 		assertTrue(ctp.findFile(".gitattributes"));
384 		assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode());
385 		assertEquals(".gitattributes", ctp.getEntryPathString());
386 		assertEquals(hash_a, ctp.getEntryObjectId());
387 	}
388 
389 	@Test
390 	public void testFindAttributesWhenSecond() throws CorruptObjectException {
391 		TreeFormatter tree = new TreeFormatter();
392 		tree.append(".config", SYMLINK, hash_a);
393 		tree.append(".gitattributes", REGULAR_FILE, hash_foo);
394 		ctp.reset(tree.toByteArray());
395 
396 		assertTrue(ctp.findFile(".gitattributes"));
397 		assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode());
398 		assertEquals(".gitattributes", ctp.getEntryPathString());
399 		assertEquals(hash_foo, ctp.getEntryObjectId());
400 	}
401 
402 	@Test
403 	public void testFindAttributesWhenMissing() throws CorruptObjectException {
404 		TreeFormatter tree = new TreeFormatter();
405 		tree.append("src", REGULAR_FILE, hash_a);
406 		tree.append("zoo", REGULAR_FILE, hash_foo);
407 		ctp.reset(tree.toByteArray());
408 
409 		assertFalse(ctp.findFile(".gitattributes"));
410 		assertEquals(11, ctp.idOffset()); // Did not walk the entire tree.
411 		assertEquals("src", ctp.getEntryPathString());
412 	}
413 }