blob: 41bd355a2f23f985f60313826f92c56251eba214 [file] [log] [blame]
/*
* Copyright (C) 2008-2009, Google 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.treewalk;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.FileMode.REGULAR_FILE;
import static org.eclipse.jgit.lib.FileMode.SYMLINK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.TreeFormatter;
import org.eclipse.jgit.util.RawParseUtils;
import org.junit.Before;
import org.junit.Test;
public class CanonicalTreeParserTest {
private final CanonicalTreeParser ctp = new CanonicalTreeParser();
private final FileMode m644 = FileMode.REGULAR_FILE;
private final FileMode mt = FileMode.TREE;
private final ObjectId hash_a = ObjectId
.fromString("6b9c715d21d5486e59083fb6071566aa6ecd4d42");
private final ObjectId hash_foo = ObjectId
.fromString("a213e8e25bb2442326e86cbfb9ef56319f482869");
private final ObjectId hash_sometree = ObjectId
.fromString("daf4bdb0d7bb24319810fe0e73aa317663448c93");
private byte[] tree1;
private byte[] tree2;
private byte[] tree3;
@Before
public void setUp() throws Exception {
tree1 = mktree(entry(m644, "a", hash_a));
tree2 = mktree(entry(m644, "a", hash_a), entry(m644, "foo", hash_foo));
tree3 = mktree(entry(m644, "a", hash_a), entry(mt, "b_sometree",
hash_sometree), entry(m644, "foo", hash_foo));
}
private static byte[] mktree(byte[]... data) throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
for (byte[] e : data)
out.write(e);
return out.toByteArray();
}
private static byte[] entry(final FileMode mode, final String name,
final ObjectId id) throws Exception {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
mode.copyTo(out);
out.write(' ');
out.write(Constants.encode(name));
out.write(0);
id.copyRawTo(out);
return out.toByteArray();
}
private String path() {
return RawParseUtils.decode(UTF_8, ctp.path,
ctp.pathOffset, ctp.pathLen);
}
@Test
public void testEmptyTree_AtEOF() throws Exception {
ctp.reset(new byte[0]);
assertTrue(ctp.eof());
}
@Test
public void testOneEntry_Forward() throws Exception {
ctp.reset(tree1);
assertTrue(ctp.first());
assertFalse(ctp.eof());
assertEquals(m644.getBits(), ctp.mode);
assertEquals("a", path());
assertEquals(hash_a, ctp.getEntryObjectId());
ctp.next(1);
assertFalse(ctp.first());
assertTrue(ctp.eof());
}
@Test
public void testTwoEntries_ForwardOneAtATime() throws Exception {
ctp.reset(tree2);
assertTrue(ctp.first());
assertFalse(ctp.eof());
assertEquals(m644.getBits(), ctp.mode);
assertEquals("a", path());
assertEquals(hash_a, ctp.getEntryObjectId());
ctp.next(1);
assertFalse(ctp.eof());
assertEquals(m644.getBits(), ctp.mode);
assertEquals("foo", path());
assertEquals(hash_foo, ctp.getEntryObjectId());
ctp.next(1);
assertFalse(ctp.first());
assertTrue(ctp.eof());
}
@Test
public void testOneEntry_Seek1IsEOF() throws Exception {
ctp.reset(tree1);
ctp.next(1);
assertTrue(ctp.eof());
}
@Test
public void testTwoEntries_Seek2IsEOF() throws Exception {
ctp.reset(tree2);
ctp.next(2);
assertTrue(ctp.eof());
}
@Test
public void testThreeEntries_Seek3IsEOF() throws Exception {
ctp.reset(tree3);
ctp.next(3);
assertTrue(ctp.eof());
}
@Test
public void testThreeEntries_Seek2() throws Exception {
ctp.reset(tree3);
ctp.next(2);
assertFalse(ctp.eof());
assertFalse(ctp.eof());
assertEquals(m644.getBits(), ctp.mode);
assertEquals("foo", path());
assertEquals(hash_foo, ctp.getEntryObjectId());
ctp.next(1);
assertTrue(ctp.eof());
}
@Test
public void testOneEntry_Backwards() throws Exception {
ctp.reset(tree1);
ctp.next(1);
assertFalse(ctp.first());
assertTrue(ctp.eof());
ctp.back(1);
assertTrue(ctp.first());
assertFalse(ctp.eof());
assertEquals(m644.getBits(), ctp.mode);
assertEquals("a", path());
assertEquals(hash_a, ctp.getEntryObjectId());
}
@Test
public void testTwoEntries_BackwardsOneAtATime() throws Exception {
ctp.reset(tree2);
ctp.next(2);
assertTrue(ctp.eof());
ctp.back(1);
assertFalse(ctp.eof());
assertEquals(m644.getBits(), ctp.mode);
assertEquals("foo", path());
assertEquals(hash_foo, ctp.getEntryObjectId());
ctp.back(1);
assertFalse(ctp.eof());
assertEquals(m644.getBits(), ctp.mode);
assertEquals("a", path());
assertEquals(hash_a, ctp.getEntryObjectId());
}
@Test
public void testTwoEntries_BackwardsTwo() throws Exception {
ctp.reset(tree2);
ctp.next(2);
assertTrue(ctp.eof());
ctp.back(2);
assertFalse(ctp.eof());
assertEquals(m644.getBits(), ctp.mode);
assertEquals("a", path());
assertEquals(hash_a, ctp.getEntryObjectId());
ctp.next(1);
assertFalse(ctp.eof());
assertEquals(m644.getBits(), ctp.mode);
assertEquals("foo", path());
assertEquals(hash_foo, ctp.getEntryObjectId());
ctp.next(1);
assertTrue(ctp.eof());
}
@Test
public void testThreeEntries_BackwardsTwo() throws Exception {
ctp.reset(tree3);
ctp.next(3);
assertTrue(ctp.eof());
ctp.back(2);
assertFalse(ctp.eof());
assertEquals(mt.getBits(), ctp.mode);
assertEquals("b_sometree", path());
assertEquals(hash_sometree, ctp.getEntryObjectId());
ctp.next(1);
assertFalse(ctp.eof());
assertEquals(m644.getBits(), ctp.mode);
assertEquals("foo", path());
assertEquals(hash_foo, ctp.getEntryObjectId());
ctp.next(1);
assertTrue(ctp.eof());
}
@Test
public void testBackwards_ConfusingPathName() throws Exception {
final String aVeryConfusingName = "confusing 644 entry 755 and others";
ctp.reset(mktree(entry(m644, "a", hash_a), entry(mt, aVeryConfusingName,
hash_sometree), entry(m644, "foo", hash_foo)));
ctp.next(3);
assertTrue(ctp.eof());
ctp.back(2);
assertFalse(ctp.eof());
assertEquals(mt.getBits(), ctp.mode);
assertEquals(aVeryConfusingName, path());
assertEquals(hash_sometree, ctp.getEntryObjectId());
ctp.back(1);
assertFalse(ctp.eof());
assertEquals(m644.getBits(), ctp.mode);
assertEquals("a", path());
assertEquals(hash_a, ctp.getEntryObjectId());
}
@Test
public void testBackwords_Prebuilts1() throws Exception {
// What is interesting about this test is the ObjectId for the
// "darwin-x86" path entry ends in an octal digit (37 == '7').
// Thus when scanning backwards we could over scan and consume
// part of the SHA-1, and miss the path terminator.
//
final ObjectId common = ObjectId
.fromString("af7bf97cb9bce3f60f1d651a0ef862e9447dd8bc");
final ObjectId darwinx86 = ObjectId
.fromString("e927f7398240f78face99e1a738dac54ef738e37");
final ObjectId linuxx86 = ObjectId
.fromString("ac08dd97120c7cb7d06e98cd5b152011183baf21");
final ObjectId windows = ObjectId
.fromString("6c4c64c221a022bb973165192cca4812033479df");
ctp.reset(mktree(entry(mt, "common", common), entry(mt, "darwin-x86",
darwinx86), entry(mt, "linux-x86", linuxx86), entry(mt,
"windows", windows)));
ctp.next(3);
assertEquals("windows", ctp.getEntryPathString());
assertSame(mt, ctp.getEntryFileMode());
assertEquals(windows, ctp.getEntryObjectId());
ctp.back(1);
assertEquals("linux-x86", ctp.getEntryPathString());
assertSame(mt, ctp.getEntryFileMode());
assertEquals(linuxx86, ctp.getEntryObjectId());
ctp.next(1);
assertEquals("windows", ctp.getEntryPathString());
assertSame(mt, ctp.getEntryFileMode());
assertEquals(windows, ctp.getEntryObjectId());
}
@Test
public void testBackwords_Prebuilts2() throws Exception {
// What is interesting about this test is the ObjectId for the
// "darwin-x86" path entry ends in an octal digit (37 == '7').
// Thus when scanning backwards we could over scan and consume
// part of the SHA-1, and miss the path terminator.
//
final ObjectId common = ObjectId
.fromString("af7bf97cb9bce3f60f1d651a0ef862e9447dd8bc");
final ObjectId darwinx86 = ObjectId
.fromString("0000000000000000000000000000000000000037");
final ObjectId linuxx86 = ObjectId
.fromString("ac08dd97120c7cb7d06e98cd5b152011183baf21");
final ObjectId windows = ObjectId
.fromString("6c4c64c221a022bb973165192cca4812033479df");
ctp.reset(mktree(entry(mt, "common", common), entry(mt, "darwin-x86",
darwinx86), entry(mt, "linux-x86", linuxx86), entry(mt,
"windows", windows)));
ctp.next(3);
assertEquals("windows", ctp.getEntryPathString());
assertSame(mt, ctp.getEntryFileMode());
assertEquals(windows, ctp.getEntryObjectId());
ctp.back(1);
assertEquals("linux-x86", ctp.getEntryPathString());
assertSame(mt, ctp.getEntryFileMode());
assertEquals(linuxx86, ctp.getEntryObjectId());
ctp.next(1);
assertEquals("windows", ctp.getEntryPathString());
assertSame(mt, ctp.getEntryFileMode());
assertEquals(windows, ctp.getEntryObjectId());
}
@Test
public void testFreakingHugePathName() throws Exception {
final int n = AbstractTreeIterator.DEFAULT_PATH_SIZE * 4;
final StringBuilder b = new StringBuilder(n);
for (int i = 0; i < n; i++)
b.append('q');
final String name = b.toString();
ctp.reset(entry(m644, name, hash_a));
assertFalse(ctp.eof());
assertEquals(name, RawParseUtils.decode(UTF_8, ctp.path,
ctp.pathOffset, ctp.pathLen));
}
@Test
public void testFindAttributesWhenFirst() throws CorruptObjectException {
TreeFormatter tree = new TreeFormatter();
tree.append(".gitattributes", REGULAR_FILE, hash_a);
ctp.reset(tree.toByteArray());
assertTrue(ctp.findFile(".gitattributes"));
assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode());
assertEquals(".gitattributes", ctp.getEntryPathString());
assertEquals(hash_a, ctp.getEntryObjectId());
}
@Test
public void testFindAttributesWhenSecond() throws CorruptObjectException {
TreeFormatter tree = new TreeFormatter();
tree.append(".config", SYMLINK, hash_a);
tree.append(".gitattributes", REGULAR_FILE, hash_foo);
ctp.reset(tree.toByteArray());
assertTrue(ctp.findFile(".gitattributes"));
assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode());
assertEquals(".gitattributes", ctp.getEntryPathString());
assertEquals(hash_foo, ctp.getEntryObjectId());
}
@Test
public void testFindAttributesWhenMissing() throws CorruptObjectException {
TreeFormatter tree = new TreeFormatter();
tree.append("src", REGULAR_FILE, hash_a);
tree.append("zoo", REGULAR_FILE, hash_foo);
ctp.reset(tree.toByteArray());
assertFalse(ctp.findFile(".gitattributes"));
assertEquals(11, ctp.idOffset()); // Did not walk the entire tree.
assertEquals("src", ctp.getEntryPathString());
}
}