diff options
Diffstat (limited to 'org.eclipse.jgit.test/tst/org/eclipse/jgit')
113 files changed, 23717 insertions, 0 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterReflowTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterReflowTest.java new file mode 100644 index 0000000000..d9e50d20af --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterReflowTest.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2009, Johannes E. Schindelin <johannes.schindelin@gmx.de> + * 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.diff; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import junit.framework.TestCase; + +import org.eclipse.jgit.patch.FileHeader; +import org.eclipse.jgit.patch.Patch; +import org.eclipse.jgit.util.RawParseUtils; + +public class DiffFormatterReflowTest extends TestCase { + private RawText a; + + private RawText b; + + private FileHeader file; + + private ByteArrayOutputStream out; + + private DiffFormatter fmt; + + protected void setUp() throws Exception { + super.setUp(); + out = new ByteArrayOutputStream(); + fmt = new DiffFormatter(); + } + + public void testNegativeContextFails() throws IOException { + init("X"); + try { + fmt.setContext(-1); + fail("accepted negative context"); + } catch (IllegalArgumentException e) { + // pass + } + } + + public void testContext0() throws IOException { + init("X"); + fmt.setContext(0); + assertFormatted(); + } + + public void testContext1() throws IOException { + init("X"); + fmt.setContext(1); + assertFormatted(); + } + + public void testContext3() throws IOException { + init("X"); + fmt.setContext(3); + assertFormatted(); + } + + public void testContext5() throws IOException { + init("X"); + fmt.setContext(5); + assertFormatted(); + } + + public void testContext10() throws IOException { + init("X"); + fmt.setContext(10); + assertFormatted(); + } + + public void testContext100() throws IOException { + init("X"); + fmt.setContext(100); + assertFormatted(); + } + + public void testEmpty1() throws IOException { + init("E"); + assertFormatted("E.patch"); + } + + public void testNoNewLine1() throws IOException { + init("Y"); + assertFormatted("Y.patch"); + } + + public void testNoNewLine2() throws IOException { + init("Z"); + assertFormatted("Z.patch"); + } + + private void init(final String name) throws IOException { + a = new RawText(readFile(name + "_PreImage")); + b = new RawText(readFile(name + "_PostImage")); + file = parseTestPatchFile(name + ".patch").getFiles().get(0); + } + + private void assertFormatted() throws IOException { + assertFormatted(getName() + ".out"); + } + + private void assertFormatted(final String name) throws IOException { + fmt.format(out, file, a, b); + final String exp = RawParseUtils.decode(readFile(name)); + assertEquals(exp, RawParseUtils.decode(out.toByteArray())); + } + + private byte[] readFile(final String patchFile) throws IOException { + final InputStream in = getClass().getResourceAsStream(patchFile); + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } + try { + final byte[] buf = new byte[1024]; + final ByteArrayOutputStream temp = new ByteArrayOutputStream(); + int n; + while ((n = in.read(buf)) > 0) + temp.write(buf, 0, n); + return temp.toByteArray(); + } finally { + in.close(); + } + } + + private Patch parseTestPatchFile(final String patchFile) throws IOException { + final InputStream in = getClass().getResourceAsStream(patchFile); + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } + try { + final Patch p = new Patch(); + p.parse(in); + return p; + } finally { + in.close(); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/EditListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/EditListTest.java new file mode 100644 index 0000000000..5c2c3d0a22 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/EditListTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 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.diff; + +import java.util.Iterator; + +import junit.framework.TestCase; + +public class EditListTest extends TestCase { + public void testEmpty() { + final EditList l = new EditList(); + assertEquals(0, l.size()); + assertTrue(l.isEmpty()); + assertEquals("EditList[]", l.toString()); + + assertTrue(l.equals(l)); + assertTrue(l.equals(new EditList())); + assertFalse(l.equals("")); + assertEquals(l.hashCode(), new EditList().hashCode()); + } + + public void testAddOne() { + final Edit e = new Edit(1, 2, 1, 1); + final EditList l = new EditList(); + l.add(e); + assertEquals(1, l.size()); + assertFalse(l.isEmpty()); + assertSame(e, l.get(0)); + assertSame(e, l.iterator().next()); + + assertTrue(l.equals(l)); + assertFalse(l.equals(new EditList())); + + final EditList l2 = new EditList(); + l2.add(e); + assertTrue(l.equals(l2)); + assertTrue(l2.equals(l)); + assertEquals(l.hashCode(), l2.hashCode()); + } + + public void testAddTwo() { + final Edit e1 = new Edit(1, 2, 1, 1); + final Edit e2 = new Edit(8, 8, 8, 12); + final EditList l = new EditList(); + l.add(e1); + l.add(e2); + assertEquals(2, l.size()); + assertSame(e1, l.get(0)); + assertSame(e2, l.get(1)); + + final Iterator<Edit> i = l.iterator(); + assertSame(e1, i.next()); + assertSame(e2, i.next()); + + assertTrue(l.equals(l)); + assertFalse(l.equals(new EditList())); + + final EditList l2 = new EditList(); + l2.add(e1); + l2.add(e2); + assertTrue(l.equals(l2)); + assertTrue(l2.equals(l)); + assertEquals(l.hashCode(), l2.hashCode()); + } + + public void testSet() { + final Edit e1 = new Edit(1, 2, 1, 1); + final Edit e2 = new Edit(3, 4, 3, 3); + final EditList l = new EditList(); + l.add(e1); + assertSame(e1, l.get(0)); + assertSame(e1, l.set(0, e2)); + assertSame(e2, l.get(0)); + } + + public void testRemove() { + final Edit e1 = new Edit(1, 2, 1, 1); + final Edit e2 = new Edit(8, 8, 8, 12); + final EditList l = new EditList(); + l.add(e1); + l.add(e2); + l.remove(e1); + assertEquals(1, l.size()); + assertSame(e2, l.get(0)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/EditTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/EditTest.java new file mode 100644 index 0000000000..6f3d21e558 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/EditTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2009, Johannes E. Schindelin <johannes.schindelin@gmx.de> + * 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.diff; + +import junit.framework.TestCase; + +public class EditTest extends TestCase { + public void testCreate() { + final Edit e = new Edit(1, 2, 3, 4); + assertEquals(1, e.getBeginA()); + assertEquals(2, e.getEndA()); + assertEquals(3, e.getBeginB()); + assertEquals(4, e.getEndB()); + } + + public void testCreateEmpty() { + final Edit e = new Edit(1, 3); + assertEquals(1, e.getBeginA()); + assertEquals(1, e.getEndA()); + assertEquals(3, e.getBeginB()); + assertEquals(3, e.getEndB()); + } + + public void testSwap() { + final Edit e = new Edit(1, 2, 3, 4); + e.swap(); + assertEquals(3, e.getBeginA()); + assertEquals(4, e.getEndA()); + assertEquals(1, e.getBeginB()); + assertEquals(2, e.getEndB()); + } + + public void testType_Insert() { + final Edit e = new Edit(1, 1, 1, 2); + assertSame(Edit.Type.INSERT, e.getType()); + } + + public void testType_Delete() { + final Edit e = new Edit(1, 2, 1, 1); + assertSame(Edit.Type.DELETE, e.getType()); + } + + public void testType_Replace() { + final Edit e = new Edit(1, 2, 1, 4); + assertSame(Edit.Type.REPLACE, e.getType()); + } + + public void testType_Empty() { + assertSame(Edit.Type.EMPTY, new Edit(1, 1, 2, 2).getType()); + assertSame(Edit.Type.EMPTY, new Edit(1, 2).getType()); + } + + public void testToString() { + final Edit e = new Edit(1, 2, 1, 4); + assertEquals("REPLACE(1-2,1-4)", e.toString()); + } + + public void testEquals1() { + final Edit e1 = new Edit(1, 2, 3, 4); + final Edit e2 = new Edit(1, 2, 3, 4); + + assertTrue(e1.equals(e1)); + assertTrue(e1.equals(e2)); + assertTrue(e2.equals(e1)); + assertEquals(e1.hashCode(), e2.hashCode()); + assertFalse(e1.equals("")); + } + + public void testNotEquals1() { + assertFalse(new Edit(1, 2, 3, 4).equals(new Edit(0, 2, 3, 4))); + } + + public void testNotEquals2() { + assertFalse(new Edit(1, 2, 3, 4).equals(new Edit(1, 0, 3, 4))); + } + + public void testNotEquals3() { + assertFalse(new Edit(1, 2, 3, 4).equals(new Edit(1, 2, 0, 4))); + } + + public void testNotEquals4() { + assertFalse(new Edit(1, 2, 3, 4).equals(new Edit(1, 2, 3, 0))); + } + + public void testExtendA() { + final Edit e = new Edit(1, 2, 1, 1); + + e.extendA(); + assertEquals(new Edit(1, 3, 1, 1), e); + + e.extendA(); + assertEquals(new Edit(1, 4, 1, 1), e); + } + + public void testExtendB() { + final Edit e = new Edit(1, 2, 1, 1); + + e.extendB(); + assertEquals(new Edit(1, 2, 1, 2), e); + + e.extendB(); + assertEquals(new Edit(1, 2, 1, 3), e); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RawTextTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RawTextTest.java new file mode 100644 index 0000000000..5cb8bc3945 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RawTextTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2009, Johannes E. Schindelin <johannes.schindelin@gmx.de> + * 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.diff; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.util.RawParseUtils; + +public class RawTextTest extends TestCase { + public void testEmpty() { + final RawText r = new RawText(new byte[0]); + assertEquals(0, r.size()); + } + + public void testEquals() { + final RawText a = new RawText(Constants.encodeASCII("foo-a\nfoo-b\n")); + final RawText b = new RawText(Constants.encodeASCII("foo-b\nfoo-c\n")); + + assertEquals(2, a.size()); + assertEquals(2, b.size()); + + // foo-a != foo-b + assertFalse(a.equals(0, b, 0)); + assertFalse(b.equals(0, a, 0)); + + // foo-b == foo-b + assertTrue(a.equals(1, b, 0)); + assertTrue(b.equals(0, a, 1)); + } + + public void testWriteLine1() throws IOException { + final RawText a = new RawText(Constants.encodeASCII("foo-a\nfoo-b\n")); + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + a.writeLine(o, 0); + final byte[] r = o.toByteArray(); + assertEquals("foo-a", RawParseUtils.decode(r)); + } + + public void testWriteLine2() throws IOException { + final RawText a = new RawText(Constants.encodeASCII("foo-a\nfoo-b")); + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + a.writeLine(o, 1); + final byte[] r = o.toByteArray(); + assertEquals("foo-b", RawParseUtils.decode(r)); + } + + public void testWriteLine3() throws IOException { + final RawText a = new RawText(Constants.encodeASCII("a\n\nb\n")); + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + a.writeLine(o, 1); + final byte[] r = o.toByteArray(); + assertEquals("", RawParseUtils.decode(r)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java new file mode 100644 index 0000000000..cc37c2bf8b --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java @@ -0,0 +1,197 @@ +/* + * 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.dircache; + +import java.io.File; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.RepositoryTestCase; + +public class DirCacheBasicTest extends RepositoryTestCase { + public void testReadMissing_RealIndex() throws Exception { + final File idx = new File(db.getDirectory(), "index"); + assertFalse(idx.exists()); + + final DirCache dc = DirCache.read(db); + assertNotNull(dc); + assertEquals(0, dc.getEntryCount()); + } + + public void testReadMissing_TempIndex() throws Exception { + final File idx = new File(db.getDirectory(), "tmp_index"); + assertFalse(idx.exists()); + + final DirCache dc = DirCache.read(idx); + assertNotNull(dc); + assertEquals(0, dc.getEntryCount()); + } + + public void testLockMissing_RealIndex() throws Exception { + final File idx = new File(db.getDirectory(), "index"); + final File lck = new File(db.getDirectory(), "index.lock"); + assertFalse(idx.exists()); + assertFalse(lck.exists()); + + final DirCache dc = DirCache.lock(db); + assertNotNull(dc); + assertFalse(idx.exists()); + assertTrue(lck.exists()); + assertEquals(0, dc.getEntryCount()); + + dc.unlock(); + assertFalse(idx.exists()); + assertFalse(lck.exists()); + } + + public void testLockMissing_TempIndex() throws Exception { + final File idx = new File(db.getDirectory(), "tmp_index"); + final File lck = new File(db.getDirectory(), "tmp_index.lock"); + assertFalse(idx.exists()); + assertFalse(lck.exists()); + + final DirCache dc = DirCache.lock(idx); + assertNotNull(dc); + assertFalse(idx.exists()); + assertTrue(lck.exists()); + assertEquals(0, dc.getEntryCount()); + + dc.unlock(); + assertFalse(idx.exists()); + assertFalse(lck.exists()); + } + + public void testWriteEmptyUnlock_RealIndex() throws Exception { + final File idx = new File(db.getDirectory(), "index"); + final File lck = new File(db.getDirectory(), "index.lock"); + assertFalse(idx.exists()); + assertFalse(lck.exists()); + + final DirCache dc = DirCache.lock(db); + assertEquals(0, lck.length()); + dc.write(); + assertEquals(12 + 20, lck.length()); + + dc.unlock(); + assertFalse(idx.exists()); + assertFalse(lck.exists()); + } + + public void testWriteEmptyCommit_RealIndex() throws Exception { + final File idx = new File(db.getDirectory(), "index"); + final File lck = new File(db.getDirectory(), "index.lock"); + assertFalse(idx.exists()); + assertFalse(lck.exists()); + + final DirCache dc = DirCache.lock(db); + assertEquals(0, lck.length()); + dc.write(); + assertEquals(12 + 20, lck.length()); + + assertTrue(dc.commit()); + assertTrue(idx.exists()); + assertFalse(lck.exists()); + assertEquals(12 + 20, idx.length()); + } + + public void testWriteEmptyReadEmpty_RealIndex() throws Exception { + final File idx = new File(db.getDirectory(), "index"); + final File lck = new File(db.getDirectory(), "index.lock"); + assertFalse(idx.exists()); + assertFalse(lck.exists()); + { + final DirCache dc = DirCache.lock(db); + dc.write(); + assertTrue(dc.commit()); + assertTrue(idx.exists()); + } + { + final DirCache dc = DirCache.read(db); + assertEquals(0, dc.getEntryCount()); + } + } + + public void testWriteEmptyLockEmpty_RealIndex() throws Exception { + final File idx = new File(db.getDirectory(), "index"); + final File lck = new File(db.getDirectory(), "index.lock"); + assertFalse(idx.exists()); + assertFalse(lck.exists()); + { + final DirCache dc = DirCache.lock(db); + dc.write(); + assertTrue(dc.commit()); + assertTrue(idx.exists()); + } + { + final DirCache dc = DirCache.lock(db); + assertEquals(0, dc.getEntryCount()); + assertTrue(idx.exists()); + assertTrue(lck.exists()); + dc.unlock(); + } + } + + public void testBuildThenClear() throws Exception { + final DirCache dc = DirCache.read(db); + + final String[] paths = { "a.", "a.b", "a/b", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) + ents[i] = new DirCacheEntry(paths[i]); + + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + + assertEquals(paths.length, dc.getEntryCount()); + dc.clear(); + assertEquals(0, dc.getEntryCount()); + } + + public void testFindOnEmpty() throws Exception { + final DirCache dc = DirCache.newInCore(); + final byte[] path = Constants.encode("a"); + assertEquals(-1, dc.findEntry(path, path.length)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java new file mode 100644 index 0000000000..03bb7f5e83 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2008, 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.dircache; + +import java.util.Collections; + +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; + +public class DirCacheBuilderIteratorTest extends RepositoryTestCase { + public void testPathFilterGroup_DoesNotSkipTail() throws Exception { + final DirCache dc = DirCache.read(db); + + final FileMode mode = FileMode.REGULAR_FILE; + final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) { + ents[i] = new DirCacheEntry(paths[i]); + ents[i].setFileMode(mode); + } + { + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + } + + final int expIdx = 2; + final DirCacheBuilder b = dc.builder(); + final TreeWalk tw = new TreeWalk(db); + tw.reset(); + tw.addTree(new DirCacheBuildIterator(b)); + tw.setRecursive(true); + tw.setFilter(PathFilterGroup.createFromStrings(Collections + .singleton(paths[expIdx]))); + + assertTrue("found " + paths[expIdx], tw.next()); + final DirCacheIterator c = tw.getTree(0, DirCacheIterator.class); + assertNotNull(c); + assertEquals(expIdx, c.ptr); + assertSame(ents[expIdx], c.getDirCacheEntry()); + assertEquals(paths[expIdx], tw.getPathString()); + assertEquals(mode.getBits(), tw.getRawMode(0)); + assertSame(mode, tw.getFileMode(0)); + b.add(c.getDirCacheEntry()); + + assertFalse("no more entries", tw.next()); + + b.finish(); + assertEquals(ents.length, dc.getEntryCount()); + for (int i = 0; i < ents.length; i++) + assertSame(ents[i], dc.getEntry(i)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java new file mode 100644 index 0000000000..fe02a1b23e --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2008, 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.dircache; + +import java.io.File; + +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.RepositoryTestCase; + +public class DirCacheBuilderTest extends RepositoryTestCase { + public void testBuildEmpty() throws Exception { + { + final DirCache dc = DirCache.lock(db); + final DirCacheBuilder b = dc.builder(); + assertNotNull(b); + b.finish(); + dc.write(); + assertTrue(dc.commit()); + } + { + final DirCache dc = DirCache.read(db); + assertEquals(0, dc.getEntryCount()); + } + } + + public void testBuildOneFile_FinishWriteCommit() throws Exception { + final String path = "a-file-path"; + final FileMode mode = FileMode.REGULAR_FILE; + final long lastModified = 1218123387057L; + final int length = 1342; + final DirCacheEntry entOrig; + { + final DirCache dc = DirCache.lock(db); + final DirCacheBuilder b = dc.builder(); + assertNotNull(b); + + entOrig = new DirCacheEntry(path); + entOrig.setFileMode(mode); + entOrig.setLastModified(lastModified); + entOrig.setLength(length); + + assertNotSame(path, entOrig.getPathString()); + assertEquals(path, entOrig.getPathString()); + assertEquals(ObjectId.zeroId(), entOrig.getObjectId()); + assertEquals(mode.getBits(), entOrig.getRawMode()); + assertEquals(0, entOrig.getStage()); + assertEquals(lastModified, entOrig.getLastModified()); + assertEquals(length, entOrig.getLength()); + assertFalse(entOrig.isAssumeValid()); + b.add(entOrig); + + b.finish(); + assertEquals(1, dc.getEntryCount()); + assertSame(entOrig, dc.getEntry(0)); + + dc.write(); + assertTrue(dc.commit()); + } + { + final DirCache dc = DirCache.read(db); + assertEquals(1, dc.getEntryCount()); + + final DirCacheEntry entRead = dc.getEntry(0); + assertNotSame(entOrig, entRead); + assertEquals(path, entRead.getPathString()); + assertEquals(ObjectId.zeroId(), entOrig.getObjectId()); + assertEquals(mode.getBits(), entOrig.getRawMode()); + assertEquals(0, entOrig.getStage()); + assertEquals(lastModified, entOrig.getLastModified()); + assertEquals(length, entOrig.getLength()); + assertFalse(entOrig.isAssumeValid()); + } + } + + public void testBuildOneFile_Commit() throws Exception { + final String path = "a-file-path"; + final FileMode mode = FileMode.REGULAR_FILE; + final long lastModified = 1218123387057L; + final int length = 1342; + final DirCacheEntry entOrig; + { + final DirCache dc = DirCache.lock(db); + final DirCacheBuilder b = dc.builder(); + assertNotNull(b); + + entOrig = new DirCacheEntry(path); + entOrig.setFileMode(mode); + entOrig.setLastModified(lastModified); + entOrig.setLength(length); + + assertNotSame(path, entOrig.getPathString()); + assertEquals(path, entOrig.getPathString()); + assertEquals(ObjectId.zeroId(), entOrig.getObjectId()); + assertEquals(mode.getBits(), entOrig.getRawMode()); + assertEquals(0, entOrig.getStage()); + assertEquals(lastModified, entOrig.getLastModified()); + assertEquals(length, entOrig.getLength()); + assertFalse(entOrig.isAssumeValid()); + b.add(entOrig); + + assertTrue(b.commit()); + assertEquals(1, dc.getEntryCount()); + assertSame(entOrig, dc.getEntry(0)); + assertFalse(new File(db.getDirectory(), "index.lock").exists()); + } + { + final DirCache dc = DirCache.read(db); + assertEquals(1, dc.getEntryCount()); + + final DirCacheEntry entRead = dc.getEntry(0); + assertNotSame(entOrig, entRead); + assertEquals(path, entRead.getPathString()); + assertEquals(ObjectId.zeroId(), entOrig.getObjectId()); + assertEquals(mode.getBits(), entOrig.getRawMode()); + assertEquals(0, entOrig.getStage()); + assertEquals(lastModified, entOrig.getLastModified()); + assertEquals(length, entOrig.getLength()); + assertFalse(entOrig.isAssumeValid()); + } + } + + public void testFindSingleFile() throws Exception { + final String path = "a-file-path"; + final DirCache dc = DirCache.read(db); + final DirCacheBuilder b = dc.builder(); + assertNotNull(b); + + final DirCacheEntry entOrig = new DirCacheEntry(path); + assertNotSame(path, entOrig.getPathString()); + assertEquals(path, entOrig.getPathString()); + b.add(entOrig); + b.finish(); + + assertEquals(1, dc.getEntryCount()); + assertSame(entOrig, dc.getEntry(0)); + assertEquals(0, dc.findEntry(path)); + + assertEquals(-1, dc.findEntry("@@-before")); + assertEquals(0, real(dc.findEntry("@@-before"))); + + assertEquals(-2, dc.findEntry("a-zoo")); + assertEquals(1, real(dc.findEntry("a-zoo"))); + + assertSame(entOrig, dc.getEntry(path)); + } + + public void testAdd_InGitSortOrder() throws Exception { + final DirCache dc = DirCache.read(db); + + final String[] paths = { "a.", "a.b", "a/b", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) + ents[i] = new DirCacheEntry(paths[i]); + + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + + assertEquals(paths.length, dc.getEntryCount()); + for (int i = 0; i < paths.length; i++) { + assertSame(ents[i], dc.getEntry(i)); + assertEquals(paths[i], dc.getEntry(i).getPathString()); + assertEquals(i, dc.findEntry(paths[i])); + assertSame(ents[i], dc.getEntry(paths[i])); + } + } + + public void testAdd_ReverseGitSortOrder() throws Exception { + final DirCache dc = DirCache.read(db); + + final String[] paths = { "a.", "a.b", "a/b", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) + ents[i] = new DirCacheEntry(paths[i]); + + final DirCacheBuilder b = dc.builder(); + for (int i = ents.length - 1; i >= 0; i--) + b.add(ents[i]); + b.finish(); + + assertEquals(paths.length, dc.getEntryCount()); + for (int i = 0; i < paths.length; i++) { + assertSame(ents[i], dc.getEntry(i)); + assertEquals(paths[i], dc.getEntry(i).getPathString()); + assertEquals(i, dc.findEntry(paths[i])); + assertSame(ents[i], dc.getEntry(paths[i])); + } + } + + public void testBuilderClear() throws Exception { + final DirCache dc = DirCache.read(db); + + final String[] paths = { "a.", "a.b", "a/b", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) + ents[i] = new DirCacheEntry(paths[i]); + { + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + } + assertEquals(paths.length, dc.getEntryCount()); + { + final DirCacheBuilder b = dc.builder(); + b.finish(); + } + assertEquals(0, dc.getEntryCount()); + } + + private static int real(int eIdx) { + if (eIdx < 0) + eIdx = -(eIdx + 1); + return eIdx; + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java new file mode 100644 index 0000000000..ab0c77110d --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2008, 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.dircache; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.util.JGitTestUtil; + +public class DirCacheCGitCompatabilityTest extends RepositoryTestCase { + private final File index = pathOf("gitgit.index"); + + public void testReadIndex_LsFiles() throws Exception { + final Map<String, CGitIndexRecord> ls = readLsFiles(); + final DirCache dc = new DirCache(index); + assertEquals(0, dc.getEntryCount()); + dc.read(); + assertEquals(ls.size(), dc.getEntryCount()); + { + final Iterator<CGitIndexRecord> rItr = ls.values().iterator(); + for (int i = 0; rItr.hasNext(); i++) + assertEqual(rItr.next(), dc.getEntry(i)); + } + } + + public void testTreeWalk_LsFiles() throws Exception { + final Map<String, CGitIndexRecord> ls = readLsFiles(); + final DirCache dc = new DirCache(index); + assertEquals(0, dc.getEntryCount()); + dc.read(); + assertEquals(ls.size(), dc.getEntryCount()); + { + final Iterator<CGitIndexRecord> rItr = ls.values().iterator(); + final TreeWalk tw = new TreeWalk(db); + tw.reset(); + tw.setRecursive(true); + tw.addTree(new DirCacheIterator(dc)); + while (rItr.hasNext()) { + final DirCacheIterator dcItr; + + assertTrue(tw.next()); + dcItr = tw.getTree(0, DirCacheIterator.class); + assertNotNull(dcItr); + + assertEqual(rItr.next(), dcItr.getDirCacheEntry()); + } + } + } + + private static void assertEqual(final CGitIndexRecord c, + final DirCacheEntry j) { + assertNotNull(c); + assertNotNull(j); + + assertEquals(c.path, j.getPathString()); + assertEquals(c.id, j.getObjectId()); + assertEquals(c.mode, j.getRawMode()); + assertEquals(c.stage, j.getStage()); + } + + public void testReadIndex_DirCacheTree() throws Exception { + final Map<String, CGitIndexRecord> cList = readLsFiles(); + final Map<String, CGitLsTreeRecord> cTree = readLsTree(); + final DirCache dc = new DirCache(index); + assertEquals(0, dc.getEntryCount()); + dc.read(); + assertEquals(cList.size(), dc.getEntryCount()); + + final DirCacheTree jTree = dc.getCacheTree(false); + assertNotNull(jTree); + assertEquals("", jTree.getNameString()); + assertEquals("", jTree.getPathString()); + assertTrue(jTree.isValid()); + assertEquals(ObjectId + .fromString("698dd0b8d0c299f080559a1cffc7fe029479a408"), jTree + .getObjectId()); + assertEquals(cList.size(), jTree.getEntrySpan()); + + final ArrayList<CGitLsTreeRecord> subtrees = new ArrayList<CGitLsTreeRecord>(); + for (final CGitLsTreeRecord r : cTree.values()) { + if (FileMode.TREE.equals(r.mode)) + subtrees.add(r); + } + assertEquals(subtrees.size(), jTree.getChildCount()); + + for (int i = 0; i < jTree.getChildCount(); i++) { + final DirCacheTree sj = jTree.getChild(i); + final CGitLsTreeRecord sc = subtrees.get(i); + assertEquals(sc.path, sj.getNameString()); + assertEquals(sc.path + "/", sj.getPathString()); + assertTrue(sj.isValid()); + assertEquals(sc.id, sj.getObjectId()); + } + } + + private File pathOf(final String name) { + return JGitTestUtil.getTestResourceFile(name); + } + + private Map<String, CGitIndexRecord> readLsFiles() throws Exception { + final LinkedHashMap<String, CGitIndexRecord> r = new LinkedHashMap<String, CGitIndexRecord>(); + final BufferedReader br = new BufferedReader(new InputStreamReader( + new FileInputStream(pathOf("gitgit.lsfiles")), "UTF-8")); + try { + String line; + while ((line = br.readLine()) != null) { + final CGitIndexRecord cr = new CGitIndexRecord(line); + r.put(cr.path, cr); + } + } finally { + br.close(); + } + return r; + } + + private Map<String, CGitLsTreeRecord> readLsTree() throws Exception { + final LinkedHashMap<String, CGitLsTreeRecord> r = new LinkedHashMap<String, CGitLsTreeRecord>(); + final BufferedReader br = new BufferedReader(new InputStreamReader( + new FileInputStream(pathOf("gitgit.lstree")), "UTF-8")); + try { + String line; + while ((line = br.readLine()) != null) { + final CGitLsTreeRecord cr = new CGitLsTreeRecord(line); + r.put(cr.path, cr); + } + } finally { + br.close(); + } + return r; + } + + private static class CGitIndexRecord { + final int mode; + + final ObjectId id; + + final int stage; + + final String path; + + CGitIndexRecord(final String line) { + final int tab = line.indexOf('\t'); + final int sp1 = line.indexOf(' '); + final int sp2 = line.indexOf(' ', sp1 + 1); + mode = Integer.parseInt(line.substring(0, sp1), 8); + id = ObjectId.fromString(line.substring(sp1 + 1, sp2)); + stage = Integer.parseInt(line.substring(sp2 + 1, tab)); + path = line.substring(tab + 1); + } + } + + private static class CGitLsTreeRecord { + final int mode; + + final ObjectId id; + + final String path; + + CGitLsTreeRecord(final String line) { + final int tab = line.indexOf('\t'); + final int sp1 = line.indexOf(' '); + final int sp2 = line.indexOf(' ', sp1 + 1); + mode = Integer.parseInt(line.substring(0, sp1), 8); + id = ObjectId.fromString(line.substring(sp2 + 1, tab)); + path = line.substring(tab + 1); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java new file mode 100644 index 0000000000..cdc659a21c --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2008, 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.dircache; + +import org.eclipse.jgit.lib.RepositoryTestCase; + +public class DirCacheFindTest extends RepositoryTestCase { + public void testEntriesWithin() throws Exception { + final DirCache dc = DirCache.read(db); + + final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) + ents[i] = new DirCacheEntry(paths[i]); + final int aFirst = 1; + final int aLast = 3; + + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + + assertEquals(paths.length, dc.getEntryCount()); + for (int i = 0; i < ents.length; i++) + assertSame(ents[i], dc.getEntry(i)); + + { + final DirCacheEntry[] aContents = dc.getEntriesWithin("a"); + assertNotNull(aContents); + assertEquals(aLast - aFirst + 1, aContents.length); + for (int i = aFirst, j = 0; i <= aLast; i++, j++) + assertSame(ents[i], aContents[j]); + } + { + final DirCacheEntry[] aContents = dc.getEntriesWithin("a/"); + assertNotNull(aContents); + assertEquals(aLast - aFirst + 1, aContents.length); + for (int i = aFirst, j = 0; i <= aLast; i++, j++) + assertSame(ents[i], aContents[j]); + } + + assertNotNull(dc.getEntriesWithin("a.")); + assertEquals(0, dc.getEntriesWithin("a.").length); + + assertNotNull(dc.getEntriesWithin("a0b")); + assertEquals(0, dc.getEntriesWithin("a0b.").length); + + assertNotNull(dc.getEntriesWithin("zoo")); + assertEquals(0, dc.getEntriesWithin("zoo.").length); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java new file mode 100644 index 0000000000..db9f684fed --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2008, 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.dircache; + +import java.util.Collections; + +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; + +public class DirCacheIteratorTest extends RepositoryTestCase { + public void testEmptyTree_NoTreeWalk() throws Exception { + final DirCache dc = DirCache.read(db); + assertEquals(0, dc.getEntryCount()); + + final DirCacheIterator i = new DirCacheIterator(dc); + assertTrue(i.eof()); + } + + public void testEmptyTree_WithTreeWalk() throws Exception { + final DirCache dc = DirCache.read(db); + assertEquals(0, dc.getEntryCount()); + + final TreeWalk tw = new TreeWalk(db); + tw.reset(); + tw.addTree(new DirCacheIterator(dc)); + assertFalse(tw.next()); + } + + public void testNoSubtree_NoTreeWalk() throws Exception { + final DirCache dc = DirCache.read(db); + + final String[] paths = { "a.", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) + ents[i] = new DirCacheEntry(paths[i]); + + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + + final DirCacheIterator i = new DirCacheIterator(dc); + int pathIdx = 0; + for (; !i.eof(); i.next(1)) { + assertEquals(pathIdx, i.ptr); + assertSame(ents[pathIdx], i.getDirCacheEntry()); + pathIdx++; + } + assertEquals(paths.length, pathIdx); + } + + public void testNoSubtree_WithTreeWalk() throws Exception { + final DirCache dc = DirCache.read(db); + + final String[] paths = { "a.", "a0b" }; + final FileMode[] modes = { FileMode.EXECUTABLE_FILE, FileMode.GITLINK }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) { + ents[i] = new DirCacheEntry(paths[i]); + ents[i].setFileMode(modes[i]); + } + + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + + final DirCacheIterator i = new DirCacheIterator(dc); + final TreeWalk tw = new TreeWalk(db); + tw.reset(); + tw.addTree(i); + int pathIdx = 0; + while (tw.next()) { + assertSame(i, tw.getTree(0, DirCacheIterator.class)); + assertEquals(pathIdx, i.ptr); + assertSame(ents[pathIdx], i.getDirCacheEntry()); + assertEquals(paths[pathIdx], tw.getPathString()); + assertEquals(modes[pathIdx].getBits(), tw.getRawMode(0)); + assertSame(modes[pathIdx], tw.getFileMode(0)); + pathIdx++; + } + assertEquals(paths.length, pathIdx); + } + + public void testSingleSubtree_NoRecursion() throws Exception { + final DirCache dc = DirCache.read(db); + + final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) { + ents[i] = new DirCacheEntry(paths[i]); + ents[i].setFileMode(FileMode.REGULAR_FILE); + } + + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + + final String[] expPaths = { "a.", "a", "a0b" }; + final FileMode[] expModes = { FileMode.REGULAR_FILE, FileMode.TREE, + FileMode.REGULAR_FILE }; + final int expPos[] = { 0, -1, 4 }; + + final DirCacheIterator i = new DirCacheIterator(dc); + final TreeWalk tw = new TreeWalk(db); + tw.reset(); + tw.addTree(i); + tw.setRecursive(false); + int pathIdx = 0; + while (tw.next()) { + assertSame(i, tw.getTree(0, DirCacheIterator.class)); + assertEquals(expModes[pathIdx].getBits(), tw.getRawMode(0)); + assertSame(expModes[pathIdx], tw.getFileMode(0)); + assertEquals(expPaths[pathIdx], tw.getPathString()); + + if (expPos[pathIdx] >= 0) { + assertEquals(expPos[pathIdx], i.ptr); + assertSame(ents[expPos[pathIdx]], i.getDirCacheEntry()); + } else { + assertSame(FileMode.TREE, tw.getFileMode(0)); + } + + pathIdx++; + } + assertEquals(expPaths.length, pathIdx); + } + + public void testSingleSubtree_Recursive() throws Exception { + final DirCache dc = DirCache.read(db); + + final FileMode mode = FileMode.REGULAR_FILE; + final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) { + ents[i] = new DirCacheEntry(paths[i]); + ents[i].setFileMode(mode); + } + + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + + final DirCacheIterator i = new DirCacheIterator(dc); + final TreeWalk tw = new TreeWalk(db); + tw.reset(); + tw.addTree(i); + tw.setRecursive(true); + int pathIdx = 0; + while (tw.next()) { + final DirCacheIterator c = tw.getTree(0, DirCacheIterator.class); + assertNotNull(c); + assertEquals(pathIdx, c.ptr); + assertSame(ents[pathIdx], c.getDirCacheEntry()); + assertEquals(paths[pathIdx], tw.getPathString()); + assertEquals(mode.getBits(), tw.getRawMode(0)); + assertSame(mode, tw.getFileMode(0)); + pathIdx++; + } + assertEquals(paths.length, pathIdx); + } + + public void testTwoLevelSubtree_Recursive() throws Exception { + final DirCache dc = DirCache.read(db); + + final FileMode mode = FileMode.REGULAR_FILE; + final String[] paths = { "a.", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) { + ents[i] = new DirCacheEntry(paths[i]); + ents[i].setFileMode(mode); + } + + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + + final TreeWalk tw = new TreeWalk(db); + tw.reset(); + tw.addTree(new DirCacheIterator(dc)); + tw.setRecursive(true); + int pathIdx = 0; + while (tw.next()) { + final DirCacheIterator c = tw.getTree(0, DirCacheIterator.class); + assertNotNull(c); + assertEquals(pathIdx, c.ptr); + assertSame(ents[pathIdx], c.getDirCacheEntry()); + assertEquals(paths[pathIdx], tw.getPathString()); + assertEquals(mode.getBits(), tw.getRawMode(0)); + assertSame(mode, tw.getFileMode(0)); + pathIdx++; + } + assertEquals(paths.length, pathIdx); + } + + public void testTwoLevelSubtree_FilterPath() throws Exception { + final DirCache dc = DirCache.read(db); + + final FileMode mode = FileMode.REGULAR_FILE; + final String[] paths = { "a.", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) { + ents[i] = new DirCacheEntry(paths[i]); + ents[i].setFileMode(mode); + } + + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + + final TreeWalk tw = new TreeWalk(db); + for (int victimIdx = 0; victimIdx < paths.length; victimIdx++) { + tw.reset(); + tw.addTree(new DirCacheIterator(dc)); + tw.setFilter(PathFilterGroup.createFromStrings(Collections + .singleton(paths[victimIdx]))); + tw.setRecursive(tw.getFilter().shouldBeRecursive()); + assertTrue(tw.next()); + final DirCacheIterator c = tw.getTree(0, DirCacheIterator.class); + assertNotNull(c); + assertEquals(victimIdx, c.ptr); + assertSame(ents[victimIdx], c.getDirCacheEntry()); + assertEquals(paths[victimIdx], tw.getPathString()); + assertEquals(mode.getBits(), tw.getRawMode(0)); + assertSame(mode, tw.getFileMode(0)); + assertFalse(tw.next()); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheLargePathTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheLargePathTest.java new file mode 100644 index 0000000000..ceaadf97b6 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheLargePathTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2008, 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.dircache; + +import java.io.IOException; + +import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.lib.RepositoryTestCase; + +public class DirCacheLargePathTest extends RepositoryTestCase { + public void testPath_4090() throws Exception { + testLongPath(4090); + } + + public void testPath_4094() throws Exception { + testLongPath(4094); + } + + public void testPath_4095() throws Exception { + testLongPath(4095); + } + + public void testPath_4096() throws Exception { + testLongPath(4096); + } + + public void testPath_16384() throws Exception { + testLongPath(16384); + } + + private void testLongPath(final int len) throws CorruptObjectException, + IOException { + final String longPath = makeLongPath(len); + final String shortPath = "~~~ shorter-path"; + + final DirCacheEntry longEnt = new DirCacheEntry(longPath); + final DirCacheEntry shortEnt = new DirCacheEntry(shortPath); + assertEquals(longPath, longEnt.getPathString()); + assertEquals(shortPath, shortEnt.getPathString()); + + { + final DirCache dc1 = DirCache.lock(db); + { + final DirCacheBuilder b = dc1.builder(); + b.add(longEnt); + b.add(shortEnt); + assertTrue(b.commit()); + } + assertEquals(2, dc1.getEntryCount()); + assertSame(longEnt, dc1.getEntry(0)); + assertSame(shortEnt, dc1.getEntry(1)); + } + { + final DirCache dc2 = DirCache.read(db); + assertEquals(2, dc2.getEntryCount()); + + assertNotSame(longEnt, dc2.getEntry(0)); + assertEquals(longPath, dc2.getEntry(0).getPathString()); + + assertNotSame(shortEnt, dc2.getEntry(1)); + assertEquals(shortPath, dc2.getEntry(1).getPathString()); + } + } + + private static String makeLongPath(final int len) { + final StringBuilder r = new StringBuilder(len); + for (int i = 0; i < len; i++) + r.append('a' + (i % 26)); + return r.toString(); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java new file mode 100644 index 0000000000..b84b240a74 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2008, 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.dircache; + +import java.io.IOException; + +import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.lib.RepositoryTestCase; + +public class DirCacheTreeTest extends RepositoryTestCase { + public void testEmptyCache_NoCacheTree() throws Exception { + final DirCache dc = DirCache.read(db); + assertNull(dc.getCacheTree(false)); + } + + public void testEmptyCache_CreateEmptyCacheTree() throws Exception { + final DirCache dc = DirCache.read(db); + final DirCacheTree tree = dc.getCacheTree(true); + assertNotNull(tree); + assertSame(tree, dc.getCacheTree(false)); + assertSame(tree, dc.getCacheTree(true)); + assertEquals("", tree.getNameString()); + assertEquals("", tree.getPathString()); + assertEquals(0, tree.getChildCount()); + assertEquals(0, tree.getEntrySpan()); + assertFalse(tree.isValid()); + } + + public void testEmptyCache_Clear_NoCacheTree() throws Exception { + final DirCache dc = DirCache.read(db); + final DirCacheTree tree = dc.getCacheTree(true); + assertNotNull(tree); + dc.clear(); + assertNull(dc.getCacheTree(false)); + assertNotSame(tree, dc.getCacheTree(true)); + } + + public void testSingleSubtree() throws Exception { + final DirCache dc = DirCache.read(db); + + final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) + ents[i] = new DirCacheEntry(paths[i]); + final int aFirst = 1; + final int aLast = 3; + + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + + assertNull(dc.getCacheTree(false)); + final DirCacheTree root = dc.getCacheTree(true); + assertNotNull(root); + assertSame(root, dc.getCacheTree(true)); + assertEquals("", root.getNameString()); + assertEquals("", root.getPathString()); + assertEquals(1, root.getChildCount()); + assertEquals(dc.getEntryCount(), root.getEntrySpan()); + assertFalse(root.isValid()); + + final DirCacheTree aTree = root.getChild(0); + assertNotNull(aTree); + assertSame(aTree, root.getChild(0)); + assertEquals("a", aTree.getNameString()); + assertEquals("a/", aTree.getPathString()); + assertEquals(0, aTree.getChildCount()); + assertEquals(aLast - aFirst + 1, aTree.getEntrySpan()); + assertFalse(aTree.isValid()); + } + + public void testTwoLevelSubtree() throws Exception { + final DirCache dc = DirCache.read(db); + + final String[] paths = { "a.", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) + ents[i] = new DirCacheEntry(paths[i]); + final int aFirst = 1; + final int aLast = 4; + final int acFirst = 2; + final int acLast = 3; + + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + b.finish(); + + assertNull(dc.getCacheTree(false)); + final DirCacheTree root = dc.getCacheTree(true); + assertNotNull(root); + assertSame(root, dc.getCacheTree(true)); + assertEquals("", root.getNameString()); + assertEquals("", root.getPathString()); + assertEquals(1, root.getChildCount()); + assertEquals(dc.getEntryCount(), root.getEntrySpan()); + assertFalse(root.isValid()); + + final DirCacheTree aTree = root.getChild(0); + assertNotNull(aTree); + assertSame(aTree, root.getChild(0)); + assertEquals("a", aTree.getNameString()); + assertEquals("a/", aTree.getPathString()); + assertEquals(1, aTree.getChildCount()); + assertEquals(aLast - aFirst + 1, aTree.getEntrySpan()); + assertFalse(aTree.isValid()); + + final DirCacheTree acTree = aTree.getChild(0); + assertNotNull(acTree); + assertSame(acTree, aTree.getChild(0)); + assertEquals("c", acTree.getNameString()); + assertEquals("a/c/", acTree.getPathString()); + assertEquals(0, acTree.getChildCount()); + assertEquals(acLast - acFirst + 1, acTree.getEntrySpan()); + assertFalse(acTree.isValid()); + } + + /** + * We had bugs related to buffer size in the DirCache. This test creates an + * index larger than the default BufferedInputStream buffer size. This made + * the DirCache unable to read the extensions when index size exceeded the + * buffer size (in some cases at least). + * + * @throws CorruptObjectException + * @throws IOException + */ + public void testWriteReadTree() throws CorruptObjectException, IOException { + final DirCache dc = DirCache.lock(db); + + final String A = String.format("a%2000s", "a"); + final String B = String.format("b%2000s", "b"); + final String[] paths = { A + ".", A + "." + B, A + "/" + B, A + "0" + B }; + final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; + for (int i = 0; i < paths.length; i++) + ents[i] = new DirCacheEntry(paths[i]); + + final DirCacheBuilder b = dc.builder(); + for (int i = 0; i < ents.length; i++) + b.add(ents[i]); + + b.commit(); + DirCache read = DirCache.read(db); + + assertEquals(paths.length, read.getEntryCount()); + assertEquals(1, read.getCacheTree(true).getChildCount()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/fnmatch/FileNameMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/fnmatch/FileNameMatcherTest.java new file mode 100644 index 0000000000..752a1e2f68 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/fnmatch/FileNameMatcherTest.java @@ -0,0 +1,764 @@ +/* + * Copyright (C) 2008, Florian Koeberle <florianskarten@web.de> + * Copyright (C) 2008, Florian Köberle <florianskarten@web.de> + * 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.fnmatch; + +import org.eclipse.jgit.errors.InvalidPatternException; +import org.eclipse.jgit.fnmatch.FileNameMatcher; + +import junit.framework.TestCase; + +public class FileNameMatcherTest extends TestCase { + + private void assertMatch(final String pattern, final String input, + final boolean matchExpected, final boolean appendCanMatchExpected) + throws InvalidPatternException { + final FileNameMatcher matcher = new FileNameMatcher(pattern, null); + matcher.append(input); + assertEquals(matchExpected, matcher.isMatch()); + assertEquals(appendCanMatchExpected, matcher.canAppendMatch()); + } + + private void assertFileNameMatch(final String pattern, final String input, + final char excludedCharacter, final boolean matchExpected, + final boolean appendCanMatchExpected) + throws InvalidPatternException { + final FileNameMatcher matcher = new FileNameMatcher(pattern, + new Character(excludedCharacter)); + matcher.append(input); + assertEquals(matchExpected, matcher.isMatch()); + assertEquals(appendCanMatchExpected, matcher.canAppendMatch()); + } + + public void testVerySimplePatternCase0() throws Exception { + assertMatch("", "", true, false); + } + + public void testVerySimplePatternCase1() throws Exception { + assertMatch("ab", "a", false, true); + } + + public void testVerySimplePatternCase2() throws Exception { + assertMatch("ab", "ab", true, false); + } + + public void testVerySimplePatternCase3() throws Exception { + assertMatch("ab", "ac", false, false); + } + + public void testVerySimplePatternCase4() throws Exception { + assertMatch("ab", "abc", false, false); + } + + public void testVerySimpleWirdcardCase0() throws Exception { + assertMatch("?", "a", true, false); + } + + public void testVerySimpleWildCardCase1() throws Exception { + assertMatch("??", "a", false, true); + } + + public void testVerySimpleWildCardCase2() throws Exception { + assertMatch("??", "ab", true, false); + } + + public void testVerySimpleWildCardCase3() throws Exception { + assertMatch("??", "abc", false, false); + } + + public void testVerySimpleStarCase0() throws Exception { + assertMatch("*", "", true, true); + } + + public void testVerySimpleStarCase1() throws Exception { + assertMatch("*", "a", true, true); + } + + public void testVerySimpleStarCase2() throws Exception { + assertMatch("*", "ab", true, true); + } + + public void testSimpleStarCase0() throws Exception { + assertMatch("a*b", "a", false, true); + } + + public void testSimpleStarCase1() throws Exception { + assertMatch("a*c", "ac", true, true); + } + + public void testSimpleStarCase2() throws Exception { + assertMatch("a*c", "ab", false, true); + } + + public void testSimpleStarCase3() throws Exception { + assertMatch("a*c", "abc", true, true); + } + + public void testManySolutionsCase0() throws Exception { + assertMatch("a*a*a", "aaa", true, true); + } + + public void testManySolutionsCase1() throws Exception { + assertMatch("a*a*a", "aaaa", true, true); + } + + public void testManySolutionsCase2() throws Exception { + assertMatch("a*a*a", "ababa", true, true); + } + + public void testManySolutionsCase3() throws Exception { + assertMatch("a*a*a", "aaaaaaaa", true, true); + } + + public void testManySolutionsCase4() throws Exception { + assertMatch("a*a*a", "aaaaaaab", false, true); + } + + public void testVerySimpleGroupCase0() throws Exception { + assertMatch("[ab]", "a", true, false); + } + + public void testVerySimpleGroupCase1() throws Exception { + assertMatch("[ab]", "b", true, false); + } + + public void testVerySimpleGroupCase2() throws Exception { + assertMatch("[ab]", "ab", false, false); + } + + public void testVerySimpleGroupRangeCase0() throws Exception { + assertMatch("[b-d]", "a", false, false); + } + + public void testVerySimpleGroupRangeCase1() throws Exception { + assertMatch("[b-d]", "b", true, false); + } + + public void testVerySimpleGroupRangeCase2() throws Exception { + assertMatch("[b-d]", "c", true, false); + } + + public void testVerySimpleGroupRangeCase3() throws Exception { + assertMatch("[b-d]", "d", true, false); + } + + public void testVerySimpleGroupRangeCase4() throws Exception { + assertMatch("[b-d]", "e", false, false); + } + + public void testVerySimpleGroupRangeCase5() throws Exception { + assertMatch("[b-d]", "-", false, false); + } + + public void testTwoGroupsCase0() throws Exception { + assertMatch("[b-d][ab]", "bb", true, false); + } + + public void testTwoGroupsCase1() throws Exception { + assertMatch("[b-d][ab]", "ca", true, false); + } + + public void testTwoGroupsCase2() throws Exception { + assertMatch("[b-d][ab]", "fa", false, false); + } + + public void testTwoGroupsCase3() throws Exception { + assertMatch("[b-d][ab]", "bc", false, false); + } + + public void testTwoRangesInOneGroupCase0() throws Exception { + assertMatch("[b-ce-e]", "a", false, false); + } + + public void testTwoRangesInOneGroupCase1() throws Exception { + assertMatch("[b-ce-e]", "b", true, false); + } + + public void testTwoRangesInOneGroupCase2() throws Exception { + assertMatch("[b-ce-e]", "c", true, false); + } + + public void testTwoRangesInOneGroupCase3() throws Exception { + assertMatch("[b-ce-e]", "d", false, false); + } + + public void testTwoRangesInOneGroupCase4() throws Exception { + assertMatch("[b-ce-e]", "e", true, false); + } + + public void testTwoRangesInOneGroupCase5() throws Exception { + assertMatch("[b-ce-e]", "f", false, false); + } + + public void testIncompleteRangesInOneGroupCase0() throws Exception { + assertMatch("a[b-]", "ab", true, false); + } + + public void testIncompleteRangesInOneGroupCase1() throws Exception { + assertMatch("a[b-]", "ac", false, false); + } + + public void testIncompleteRangesInOneGroupCase2() throws Exception { + assertMatch("a[b-]", "a-", true, false); + } + + public void testCombinedRangesInOneGroupCase0() throws Exception { + assertMatch("[a-c-e]", "b", true, false); + } + + /** + * The c belongs to the range a-c. "-e" is no valid range so d should not + * match. + * + * @throws Exception + * for some reasons + */ + public void testCombinedRangesInOneGroupCase1() throws Exception { + assertMatch("[a-c-e]", "d", false, false); + } + + public void testCombinedRangesInOneGroupCase2() throws Exception { + assertMatch("[a-c-e]", "e", true, false); + } + + public void testInversedGroupCase0() throws Exception { + assertMatch("[!b-c]", "a", true, false); + } + + public void testInversedGroupCase1() throws Exception { + assertMatch("[!b-c]", "b", false, false); + } + + public void testInversedGroupCase2() throws Exception { + assertMatch("[!b-c]", "c", false, false); + } + + public void testInversedGroupCase3() throws Exception { + assertMatch("[!b-c]", "d", true, false); + } + + public void testAlphaGroupCase0() throws Exception { + assertMatch("[[:alpha:]]", "d", true, false); + } + + public void testAlphaGroupCase1() throws Exception { + assertMatch("[[:alpha:]]", ":", false, false); + } + + public void testAlphaGroupCase2() throws Exception { + // \u00f6 = 'o' with dots on it + assertMatch("[[:alpha:]]", "\u00f6", true, false); + } + + public void test2AlphaGroupsCase0() throws Exception { + // \u00f6 = 'o' with dots on it + assertMatch("[[:alpha:]][[:alpha:]]", "a\u00f6", true, false); + assertMatch("[[:alpha:]][[:alpha:]]", "a1", false, false); + } + + public void testAlnumGroupCase0() throws Exception { + assertMatch("[[:alnum:]]", "a", true, false); + } + + public void testAlnumGroupCase1() throws Exception { + assertMatch("[[:alnum:]]", "1", true, false); + } + + public void testAlnumGroupCase2() throws Exception { + assertMatch("[[:alnum:]]", ":", false, false); + } + + public void testBlankGroupCase0() throws Exception { + assertMatch("[[:blank:]]", " ", true, false); + } + + public void testBlankGroupCase1() throws Exception { + assertMatch("[[:blank:]]", "\t", true, false); + } + + public void testBlankGroupCase2() throws Exception { + assertMatch("[[:blank:]]", "\r", false, false); + } + + public void testBlankGroupCase3() throws Exception { + assertMatch("[[:blank:]]", "\n", false, false); + } + + public void testBlankGroupCase4() throws Exception { + assertMatch("[[:blank:]]", "a", false, false); + } + + public void testCntrlGroupCase0() throws Exception { + assertMatch("[[:cntrl:]]", "a", false, false); + } + + public void testCntrlGroupCase1() throws Exception { + assertMatch("[[:cntrl:]]", String.valueOf((char) 7), true, false); + } + + public void testDigitGroupCase0() throws Exception { + assertMatch("[[:digit:]]", "0", true, false); + } + + public void testDigitGroupCase1() throws Exception { + assertMatch("[[:digit:]]", "5", true, false); + } + + public void testDigitGroupCase2() throws Exception { + assertMatch("[[:digit:]]", "9", true, false); + } + + public void testDigitGroupCase3() throws Exception { + // \u06f9 = EXTENDED ARABIC-INDIC DIGIT NINE + assertMatch("[[:digit:]]", "\u06f9", true, false); + } + + public void testDigitGroupCase4() throws Exception { + assertMatch("[[:digit:]]", "a", false, false); + } + + public void testDigitGroupCase5() throws Exception { + assertMatch("[[:digit:]]", "]", false, false); + } + + public void testGraphGroupCase0() throws Exception { + assertMatch("[[:graph:]]", "]", true, false); + } + + public void testGraphGroupCase1() throws Exception { + assertMatch("[[:graph:]]", "a", true, false); + } + + public void testGraphGroupCase2() throws Exception { + assertMatch("[[:graph:]]", ".", true, false); + } + + public void testGraphGroupCase3() throws Exception { + assertMatch("[[:graph:]]", "0", true, false); + } + + public void testGraphGroupCase4() throws Exception { + assertMatch("[[:graph:]]", " ", false, false); + } + + public void testGraphGroupCase5() throws Exception { + // \u00f6 = 'o' with dots on it + assertMatch("[[:graph:]]", "\u00f6", true, false); + } + + public void testLowerGroupCase0() throws Exception { + assertMatch("[[:lower:]]", "a", true, false); + } + + public void testLowerGroupCase1() throws Exception { + assertMatch("[[:lower:]]", "h", true, false); + } + + public void testLowerGroupCase2() throws Exception { + assertMatch("[[:lower:]]", "A", false, false); + } + + public void testLowerGroupCase3() throws Exception { + assertMatch("[[:lower:]]", "H", false, false); + } + + public void testLowerGroupCase4() throws Exception { + // \u00e4 = small 'a' with dots on it + assertMatch("[[:lower:]]", "\u00e4", true, false); + } + + public void testLowerGroupCase5() throws Exception { + assertMatch("[[:lower:]]", ".", false, false); + } + + public void testPrintGroupCase0() throws Exception { + assertMatch("[[:print:]]", "]", true, false); + } + + public void testPrintGroupCase1() throws Exception { + assertMatch("[[:print:]]", "a", true, false); + } + + public void testPrintGroupCase2() throws Exception { + assertMatch("[[:print:]]", ".", true, false); + } + + public void testPrintGroupCase3() throws Exception { + assertMatch("[[:print:]]", "0", true, false); + } + + public void testPrintGroupCase4() throws Exception { + assertMatch("[[:print:]]", " ", true, false); + } + + public void testPrintGroupCase5() throws Exception { + // \u00f6 = 'o' with dots on it + assertMatch("[[:print:]]", "\u00f6", true, false); + } + + public void testPunctGroupCase0() throws Exception { + assertMatch("[[:punct:]]", ".", true, false); + } + + public void testPunctGroupCase1() throws Exception { + assertMatch("[[:punct:]]", "@", true, false); + } + + public void testPunctGroupCase2() throws Exception { + assertMatch("[[:punct:]]", " ", false, false); + } + + public void testPunctGroupCase3() throws Exception { + assertMatch("[[:punct:]]", "a", false, false); + } + + public void testSpaceGroupCase0() throws Exception { + assertMatch("[[:space:]]", " ", true, false); + } + + public void testSpaceGroupCase1() throws Exception { + assertMatch("[[:space:]]", "\t", true, false); + } + + public void testSpaceGroupCase2() throws Exception { + assertMatch("[[:space:]]", "\r", true, false); + } + + public void testSpaceGroupCase3() throws Exception { + assertMatch("[[:space:]]", "\n", true, false); + } + + public void testSpaceGroupCase4() throws Exception { + assertMatch("[[:space:]]", "a", false, false); + } + + public void testUpperGroupCase0() throws Exception { + assertMatch("[[:upper:]]", "a", false, false); + } + + public void testUpperGroupCase1() throws Exception { + assertMatch("[[:upper:]]", "h", false, false); + } + + public void testUpperGroupCase2() throws Exception { + assertMatch("[[:upper:]]", "A", true, false); + } + + public void testUpperGroupCase3() throws Exception { + assertMatch("[[:upper:]]", "H", true, false); + } + + public void testUpperGroupCase4() throws Exception { + // \u00c4 = 'A' with dots on it + assertMatch("[[:upper:]]", "\u00c4", true, false); + } + + public void testUpperGroupCase5() throws Exception { + assertMatch("[[:upper:]]", ".", false, false); + } + + public void testXDigitGroupCase0() throws Exception { + assertMatch("[[:xdigit:]]", "a", true, false); + } + + public void testXDigitGroupCase1() throws Exception { + assertMatch("[[:xdigit:]]", "d", true, false); + } + + public void testXDigitGroupCase2() throws Exception { + assertMatch("[[:xdigit:]]", "f", true, false); + } + + public void testXDigitGroupCase3() throws Exception { + assertMatch("[[:xdigit:]]", "0", true, false); + } + + public void testXDigitGroupCase4() throws Exception { + assertMatch("[[:xdigit:]]", "5", true, false); + } + + public void testXDigitGroupCase5() throws Exception { + assertMatch("[[:xdigit:]]", "9", true, false); + } + + public void testXDigitGroupCase6() throws Exception { + assertMatch("[[:xdigit:]]", "۹", false, false); + } + + public void testXDigitGroupCase7() throws Exception { + assertMatch("[[:xdigit:]]", ".", false, false); + } + + public void testWordroupCase0() throws Exception { + assertMatch("[[:word:]]", "g", true, false); + } + + public void testWordroupCase1() throws Exception { + // \u00f6 = 'o' with dots on it + assertMatch("[[:word:]]", "\u00f6", true, false); + } + + public void testWordroupCase2() throws Exception { + assertMatch("[[:word:]]", "5", true, false); + } + + public void testWordroupCase3() throws Exception { + assertMatch("[[:word:]]", "_", true, false); + } + + public void testWordroupCase4() throws Exception { + assertMatch("[[:word:]]", " ", false, false); + } + + public void testWordroupCase5() throws Exception { + assertMatch("[[:word:]]", ".", false, false); + } + + public void testMixedGroupCase0() throws Exception { + assertMatch("[A[:lower:]C3-5]", "A", true, false); + } + + public void testMixedGroupCase1() throws Exception { + assertMatch("[A[:lower:]C3-5]", "C", true, false); + } + + public void testMixedGroupCase2() throws Exception { + assertMatch("[A[:lower:]C3-5]", "e", true, false); + } + + public void testMixedGroupCase3() throws Exception { + assertMatch("[A[:lower:]C3-5]", "3", true, false); + } + + public void testMixedGroupCase4() throws Exception { + assertMatch("[A[:lower:]C3-5]", "4", true, false); + } + + public void testMixedGroupCase5() throws Exception { + assertMatch("[A[:lower:]C3-5]", "5", true, false); + } + + public void testMixedGroupCase6() throws Exception { + assertMatch("[A[:lower:]C3-5]", "B", false, false); + } + + public void testMixedGroupCase7() throws Exception { + assertMatch("[A[:lower:]C3-5]", "2", false, false); + } + + public void testMixedGroupCase8() throws Exception { + assertMatch("[A[:lower:]C3-5]", "6", false, false); + } + + public void testMixedGroupCase9() throws Exception { + assertMatch("[A[:lower:]C3-5]", ".", false, false); + } + + public void testSpecialGroupCase0() throws Exception { + assertMatch("[[]", "[", true, false); + } + + public void testSpecialGroupCase1() throws Exception { + assertMatch("[]]", "]", true, false); + } + + public void testSpecialGroupCase2() throws Exception { + assertMatch("[]a]", "]", true, false); + } + + public void testSpecialGroupCase3() throws Exception { + assertMatch("[a[]", "[", true, false); + } + + public void testSpecialGroupCase4() throws Exception { + assertMatch("[a[]", "a", true, false); + } + + public void testSpecialGroupCase5() throws Exception { + assertMatch("[!]]", "]", false, false); + } + + public void testSpecialGroupCase6() throws Exception { + assertMatch("[!]]", "x", true, false); + } + + public void testSpecialGroupCase7() throws Exception { + assertMatch("[:]]", ":]", true, false); + } + + public void testSpecialGroupCase8() throws Exception { + assertMatch("[:]]", ":", false, true); + } + + public void testSpecialGroupCase9() throws Exception { + try { + assertMatch("[[:]", ":", true, true); + fail("InvalidPatternException expected"); + } catch (InvalidPatternException e) { + // expected + } + } + + public void testUnsupportedGroupCase0() throws Exception { + try { + assertMatch("[[=a=]]", "b", false, false); + fail("InvalidPatternException expected"); + } catch (InvalidPatternException e) { + assertTrue(e.getMessage().contains("[=a=]")); + } + } + + public void testUnsupportedGroupCase1() throws Exception { + try { + assertMatch("[[.a.]]", "b", false, false); + fail("InvalidPatternException expected"); + } catch (InvalidPatternException e) { + assertTrue(e.getMessage().contains("[.a.]")); + } + } + + public void testFilePathSimpleCase() throws Exception { + assertFileNameMatch("a/b", "a/b", '/', true, false); + } + + public void testFilePathCase0() throws Exception { + assertFileNameMatch("a*b", "a/b", '/', false, false); + } + + public void testFilePathCase1() throws Exception { + assertFileNameMatch("a?b", "a/b", '/', false, false); + } + + public void testFilePathCase2() throws Exception { + assertFileNameMatch("a*b", "a\\b", '\\', false, false); + } + + public void testFilePathCase3() throws Exception { + assertFileNameMatch("a?b", "a\\b", '\\', false, false); + } + + public void testReset() throws Exception { + final String pattern = "helloworld"; + final FileNameMatcher matcher = new FileNameMatcher(pattern, null); + matcher.append("helloworld"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + matcher.reset(); + matcher.append("hello"); + assertEquals(false, matcher.isMatch()); + assertEquals(true, matcher.canAppendMatch()); + matcher.append("world"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + matcher.append("to much"); + assertEquals(false, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + matcher.reset(); + matcher.append("helloworld"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + } + + public void testCreateMatcherForSuffix() throws Exception { + final String pattern = "helloworld"; + final FileNameMatcher matcher = new FileNameMatcher(pattern, null); + matcher.append("hello"); + final FileNameMatcher childMatcher = matcher.createMatcherForSuffix(); + assertEquals(false, matcher.isMatch()); + assertEquals(true, matcher.canAppendMatch()); + assertEquals(false, childMatcher.isMatch()); + assertEquals(true, childMatcher.canAppendMatch()); + matcher.append("world"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + assertEquals(false, childMatcher.isMatch()); + assertEquals(true, childMatcher.canAppendMatch()); + childMatcher.append("world"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + assertEquals(true, childMatcher.isMatch()); + assertEquals(false, childMatcher.canAppendMatch()); + childMatcher.reset(); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + assertEquals(false, childMatcher.isMatch()); + assertEquals(true, childMatcher.canAppendMatch()); + childMatcher.append("world"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + assertEquals(true, childMatcher.isMatch()); + assertEquals(false, childMatcher.canAppendMatch()); + } + + public void testCopyConstructor() throws Exception { + final String pattern = "helloworld"; + final FileNameMatcher matcher = new FileNameMatcher(pattern, null); + matcher.append("hello"); + final FileNameMatcher copy = new FileNameMatcher(matcher); + assertEquals(false, matcher.isMatch()); + assertEquals(true, matcher.canAppendMatch()); + assertEquals(false, copy.isMatch()); + assertEquals(true, copy.canAppendMatch()); + matcher.append("world"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + assertEquals(false, copy.isMatch()); + assertEquals(true, copy.canAppendMatch()); + copy.append("world"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + assertEquals(true, copy.isMatch()); + assertEquals(false, copy.canAppendMatch()); + copy.reset(); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + assertEquals(false, copy.isMatch()); + assertEquals(true, copy.canAppendMatch()); + copy.append("helloworld"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + assertEquals(true, copy.isMatch()); + assertEquals(false, copy.canAppendMatch()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/AbbreviatedObjectIdTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/AbbreviatedObjectIdTest.java new file mode 100644 index 0000000000..45f8907da7 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/AbbreviatedObjectIdTest.java @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2008, 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.lib; + +import junit.framework.TestCase; + +public class AbbreviatedObjectIdTest extends TestCase { + public void testEmpty_FromByteArray() { + final AbbreviatedObjectId i; + i = AbbreviatedObjectId.fromString(new byte[] {}, 0, 0); + assertNotNull(i); + assertEquals(0, i.length()); + assertFalse(i.isComplete()); + assertEquals("", i.name()); + } + + public void testEmpty_FromString() { + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(""); + assertNotNull(i); + assertEquals(0, i.length()); + assertFalse(i.isComplete()); + assertEquals("", i.name()); + } + + public void testFull_FromByteArray() { + final String s = "7b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final byte[] b = Constants.encodeASCII(s); + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(b, 0, + b.length); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertTrue(i.isComplete()); + assertEquals(s, i.name()); + + final ObjectId f = i.toObjectId(); + assertNotNull(f); + assertEquals(ObjectId.fromString(s), f); + assertEquals(f.hashCode(), i.hashCode()); + } + + public void testFull_FromString() { + final String s = "7b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertTrue(i.isComplete()); + assertEquals(s, i.name()); + + final ObjectId f = i.toObjectId(); + assertNotNull(f); + assertEquals(ObjectId.fromString(s), f); + assertEquals(f.hashCode(), i.hashCode()); + } + + public void test1_FromString() { + final String s = "7"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test2_FromString() { + final String s = "7b"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test3_FromString() { + final String s = "7b6"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test4_FromString() { + final String s = "7b6e"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test5_FromString() { + final String s = "7b6e8"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test6_FromString() { + final String s = "7b6e80"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test7_FromString() { + final String s = "7b6e806"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test8_FromString() { + final String s = "7b6e8067"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test9_FromString() { + final String s = "7b6e8067e"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void test17_FromString() { + final String s = "7b6e8067ec96acef9"; + final AbbreviatedObjectId i = AbbreviatedObjectId.fromString(s); + assertNotNull(i); + assertEquals(s.length(), i.length()); + assertFalse(i.isComplete()); + assertEquals(s, i.name()); + assertNull(i.toObjectId()); + } + + public void testEquals_Short() { + final String s = "7b6e8067"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(s); + final AbbreviatedObjectId b = AbbreviatedObjectId.fromString(s); + assertNotSame(a, b); + assertTrue(a.hashCode() == b.hashCode()); + assertTrue(a.equals(b)); + assertTrue(b.equals(a)); + } + + public void testEquals_Full() { + final String s = "7b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(s); + final AbbreviatedObjectId b = AbbreviatedObjectId.fromString(s); + assertNotSame(a, b); + assertTrue(a.hashCode() == b.hashCode()); + assertTrue(a.equals(b)); + assertTrue(b.equals(a)); + } + + public void testNotEquals_SameLength() { + final String sa = "7b6e8067"; + final String sb = "7b6e806e"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(sa); + final AbbreviatedObjectId b = AbbreviatedObjectId.fromString(sb); + assertFalse(a.equals(b)); + assertFalse(b.equals(a)); + } + + public void testNotEquals_DiffLength() { + final String sa = "7b6e8067abcd"; + final String sb = "7b6e8067"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(sa); + final AbbreviatedObjectId b = AbbreviatedObjectId.fromString(sb); + assertFalse(a.equals(b)); + assertFalse(b.equals(a)); + } + + public void testPrefixCompare_Full() { + final String s1 = "7b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(s1); + final ObjectId i1 = ObjectId.fromString(s1); + assertEquals(0, a.prefixCompare(i1)); + assertTrue(i1.startsWith(a)); + + final String s2 = "7b6e8067ec96acef9a4184b43210d583b6d2f99b"; + final ObjectId i2 = ObjectId.fromString(s2); + assertTrue(a.prefixCompare(i2) < 0); + assertFalse(i2.startsWith(a)); + + final String s3 = "7b6e8067ec96acef9a4184b43210d583b6d2f999"; + final ObjectId i3 = ObjectId.fromString(s3); + assertTrue(a.prefixCompare(i3) > 0); + assertFalse(i3.startsWith(a)); + } + + public void testPrefixCompare_1() { + final String sa = "7"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(sa); + + final String s1 = "7b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final ObjectId i1 = ObjectId.fromString(s1); + assertEquals(0, a.prefixCompare(i1)); + assertTrue(i1.startsWith(a)); + + final String s2 = "8b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final ObjectId i2 = ObjectId.fromString(s2); + assertTrue(a.prefixCompare(i2) < 0); + assertFalse(i2.startsWith(a)); + + final String s3 = "6b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final ObjectId i3 = ObjectId.fromString(s3); + assertTrue(a.prefixCompare(i3) > 0); + assertFalse(i3.startsWith(a)); + } + + public void testPrefixCompare_7() { + final String sa = "7b6e806"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(sa); + + final String s1 = "7b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final ObjectId i1 = ObjectId.fromString(s1); + assertEquals(0, a.prefixCompare(i1)); + assertTrue(i1.startsWith(a)); + + final String s2 = "7b6e8167ec86acef9a4184b43210d583b6d2f99a"; + final ObjectId i2 = ObjectId.fromString(s2); + assertTrue(a.prefixCompare(i2) < 0); + assertFalse(i2.startsWith(a)); + + final String s3 = "7b6e8057eca6acef9a4184b43210d583b6d2f99a"; + final ObjectId i3 = ObjectId.fromString(s3); + assertTrue(a.prefixCompare(i3) > 0); + assertFalse(i3.startsWith(a)); + } + + public void testPrefixCompare_8() { + final String sa = "7b6e8067"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(sa); + + final String s1 = "7b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final ObjectId i1 = ObjectId.fromString(s1); + assertEquals(0, a.prefixCompare(i1)); + assertTrue(i1.startsWith(a)); + + final String s2 = "7b6e8167ec86acef9a4184b43210d583b6d2f99a"; + final ObjectId i2 = ObjectId.fromString(s2); + assertTrue(a.prefixCompare(i2) < 0); + assertFalse(i2.startsWith(a)); + + final String s3 = "7b6e8057eca6acef9a4184b43210d583b6d2f99a"; + final ObjectId i3 = ObjectId.fromString(s3); + assertTrue(a.prefixCompare(i3) > 0); + assertFalse(i3.startsWith(a)); + } + + public void testPrefixCompare_9() { + final String sa = "7b6e8067e"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(sa); + + final String s1 = "7b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final ObjectId i1 = ObjectId.fromString(s1); + assertEquals(0, a.prefixCompare(i1)); + assertTrue(i1.startsWith(a)); + + final String s2 = "7b6e8167ec86acef9a4184b43210d583b6d2f99a"; + final ObjectId i2 = ObjectId.fromString(s2); + assertTrue(a.prefixCompare(i2) < 0); + assertFalse(i2.startsWith(a)); + + final String s3 = "7b6e8057eca6acef9a4184b43210d583b6d2f99a"; + final ObjectId i3 = ObjectId.fromString(s3); + assertTrue(a.prefixCompare(i3) > 0); + assertFalse(i3.startsWith(a)); + } + + public void testPrefixCompare_17() { + final String sa = "7b6e8067ec96acef9"; + final AbbreviatedObjectId a = AbbreviatedObjectId.fromString(sa); + + final String s1 = "7b6e8067ec96acef9a4184b43210d583b6d2f99a"; + final ObjectId i1 = ObjectId.fromString(s1); + assertEquals(0, a.prefixCompare(i1)); + assertTrue(i1.startsWith(a)); + + final String s2 = "7b6e8067eca6acef9a4184b43210d583b6d2f99a"; + final ObjectId i2 = ObjectId.fromString(s2); + assertTrue(a.prefixCompare(i2) < 0); + assertFalse(i2.startsWith(a)); + + final String s3 = "7b6e8067ec86acef9a4184b43210d583b6d2f99a"; + final ObjectId i3 = ObjectId.fromString(s3); + assertTrue(a.prefixCompare(i3) > 0); + assertFalse(i3.startsWith(a)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java new file mode 100644 index 0000000000..644c7b366c --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConcurrentRepackTest.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com> + * 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.lib; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; + +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.revwalk.RevWalk; + +public class ConcurrentRepackTest extends RepositoryTestCase { + public void setUp() throws Exception { + WindowCacheConfig windowCacheConfig = new WindowCacheConfig(); + windowCacheConfig.setPackedGitOpenFiles(1); + WindowCache.reconfigure(windowCacheConfig); + super.setUp(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + WindowCacheConfig windowCacheConfig = new WindowCacheConfig(); + WindowCache.reconfigure(windowCacheConfig); + } + + public void testObjectInNewPack() throws IncorrectObjectTypeException, + IOException { + // Create a new object in a new pack, and test that it is present. + // + final Repository eden = createNewEmptyRepo(); + final RevObject o1 = writeBlob(eden, "o1"); + pack(eden, o1); + assertEquals(o1.name(), parse(o1).name()); + } + + public void testObjectMovedToNewPack1() + throws IncorrectObjectTypeException, IOException { + // Create an object and pack it. Then remove that pack and put the + // object into a different pack file, with some other object. We + // still should be able to access the objects. + // + final Repository eden = createNewEmptyRepo(); + final RevObject o1 = writeBlob(eden, "o1"); + final File[] out1 = pack(eden, o1); + assertEquals(o1.name(), parse(o1).name()); + + final RevObject o2 = writeBlob(eden, "o2"); + pack(eden, o2, o1); + + // Force close, and then delete, the old pack. + // + whackCache(); + delete(out1); + + // Now here is the interesting thing. Will git figure the new + // object exists in the new pack, and not the old one. + // + assertEquals(o2.name(), parse(o2).name()); + assertEquals(o1.name(), parse(o1).name()); + } + + public void testObjectMovedWithinPack() + throws IncorrectObjectTypeException, IOException { + // Create an object and pack it. + // + final Repository eden = createNewEmptyRepo(); + final RevObject o1 = writeBlob(eden, "o1"); + final File[] out1 = pack(eden, o1); + assertEquals(o1.name(), parse(o1).name()); + + // Force close the old pack. + // + whackCache(); + + // Now overwrite the old pack in place. This method of creating a + // different pack under the same file name is partially broken. We + // should also have a different file name because the list of objects + // within the pack has been modified. + // + final RevObject o2 = writeBlob(eden, "o2"); + final PackWriter pw = new PackWriter(eden, NullProgressMonitor.INSTANCE); + pw.addObject(o2); + pw.addObject(o1); + write(out1, pw); + + // Try the old name, then the new name. The old name should cause the + // pack to reload when it opens and the index and pack mismatch. + // + assertEquals(o1.name(), parse(o1).name()); + assertEquals(o2.name(), parse(o2).name()); + } + + public void testObjectMovedToNewPack2() + throws IncorrectObjectTypeException, IOException { + // Create an object and pack it. Then remove that pack and put the + // object into a different pack file, with some other object. We + // still should be able to access the objects. + // + final Repository eden = createNewEmptyRepo(); + final RevObject o1 = writeBlob(eden, "o1"); + final File[] out1 = pack(eden, o1); + assertEquals(o1.name(), parse(o1).name()); + + final ObjectLoader load1 = db.openBlob(o1); + assertNotNull(load1); + + final RevObject o2 = writeBlob(eden, "o2"); + pack(eden, o2, o1); + + // Force close, and then delete, the old pack. + // + whackCache(); + delete(out1); + + // Now here is the interesting thing... can the loader we made + // earlier still resolve the object, even though its underlying + // pack is gone, but the object still exists. + // + final ObjectLoader load2 = db.openBlob(o1); + assertNotNull(load2); + assertNotSame(load1, load2); + + final byte[] data2 = load2.getCachedBytes(); + final byte[] data1 = load1.getCachedBytes(); + assertNotNull(data2); + assertNotNull(data1); + assertNotSame(data1, data2); // cache should be per-pack, not per object + assertTrue(Arrays.equals(data1, data2)); + assertEquals(load2.getType(), load1.getType()); + } + + private static void whackCache() { + final WindowCacheConfig config = new WindowCacheConfig(); + config.setPackedGitOpenFiles(1); + WindowCache.reconfigure(config); + } + + private RevObject parse(final AnyObjectId id) + throws MissingObjectException, IOException { + return new RevWalk(db).parseAny(id); + } + + private File[] pack(final Repository src, final RevObject... list) + throws IOException { + final PackWriter pw = new PackWriter(src, NullProgressMonitor.INSTANCE); + for (final RevObject o : list) { + pw.addObject(o); + } + + final ObjectId name = pw.computeName(); + final File packFile = fullPackFileName(name, ".pack"); + final File idxFile = fullPackFileName(name, ".idx"); + final File[] files = new File[] { packFile, idxFile }; + write(files, pw); + return files; + } + + private static void write(final File[] files, final PackWriter pw) + throws IOException { + final long begin = files[0].getParentFile().lastModified(); + FileOutputStream out; + + out = new FileOutputStream(files[0]); + try { + pw.writePack(out); + } finally { + out.close(); + } + + out = new FileOutputStream(files[1]); + try { + pw.writeIndex(out); + } finally { + out.close(); + } + + touch(begin, files[0].getParentFile()); + } + + private static void delete(final File[] list) { + final long begin = list[0].getParentFile().lastModified(); + for (final File f : list) { + f.delete(); + assertFalse(f + " was removed", f.exists()); + } + touch(begin, list[0].getParentFile()); + } + + private static void touch(final long begin, final File dir) { + while (begin >= dir.lastModified()) { + try { + Thread.sleep(25); + } catch (InterruptedException ie) { + // + } + dir.setLastModified(System.currentTimeMillis()); + } + } + + private File fullPackFileName(final ObjectId name, final String suffix) { + final File packdir = new File(db.getObjectsDirectory(), "pack"); + return new File(packdir, "pack-" + name.name() + suffix); + } + + private RevObject writeBlob(final Repository repo, final String data) + throws IOException { + final RevWalk revWalk = new RevWalk(repo); + final byte[] bytes = Constants.encode(data); + final ObjectWriter ow = new ObjectWriter(repo); + final ObjectId id = ow.writeBlob(bytes); + try { + parse(id); + fail("Object " + id.name() + " should not exist in test repository"); + } catch (MissingObjectException e) { + // Ok + } + return revWalk.lookupBlob(id); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConstantsEncodingTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConstantsEncodingTest.java new file mode 100644 index 0000000000..96568ff232 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConstantsEncodingTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2008, 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.lib; + +import java.io.UnsupportedEncodingException; +import java.util.Arrays; + +import junit.framework.TestCase; + +public class ConstantsEncodingTest extends TestCase { + public void testEncodeASCII_SimpleASCII() + throws UnsupportedEncodingException { + final String src = "abc"; + final byte[] exp = { 'a', 'b', 'c' }; + final byte[] res = Constants.encodeASCII(src); + assertTrue(Arrays.equals(exp, res)); + assertEquals(src, new String(res, 0, res.length, "UTF-8")); + } + + public void testEncodeASCII_FailOnNonASCII() { + final String src = "Ūnĭcōde̽"; + try { + Constants.encodeASCII(src); + fail("Incorrectly accepted a Unicode character"); + } catch (IllegalArgumentException err) { + assertEquals("Not ASCII string: " + src, err.getMessage()); + } + } + + public void testEncodeASCII_Number13() { + final long src = 13; + final byte[] exp = { '1', '3' }; + final byte[] res = Constants.encodeASCII(src); + assertTrue(Arrays.equals(exp, res)); + } + + public void testEncode_SimpleASCII() throws UnsupportedEncodingException { + final String src = "abc"; + final byte[] exp = { 'a', 'b', 'c' }; + final byte[] res = Constants.encode(src); + assertTrue(Arrays.equals(exp, res)); + assertEquals(src, new String(res, 0, res.length, "UTF-8")); + } + + public void testEncode_Unicode() throws UnsupportedEncodingException { + final String src = "Ūnĭcōde̽"; + final byte[] exp = { (byte) 0xC5, (byte) 0xAA, 0x6E, (byte) 0xC4, + (byte) 0xAD, 0x63, (byte) 0xC5, (byte) 0x8D, 0x64, 0x65, + (byte) 0xCC, (byte) 0xBD }; + final byte[] res = Constants.encode(src); + assertTrue(Arrays.equals(exp, res)); + assertEquals(src, new String(res, 0, res.length, "UTF-8")); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java new file mode 100644 index 0000000000..f871cc0524 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com> + * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.File; +import java.io.IOException; + +public class IndexDiffTest extends RepositoryTestCase { + public void testAdded() throws IOException { + GitIndex index = new GitIndex(db); + writeTrashFile("file1", "file1"); + writeTrashFile("dir/subfile", "dir/subfile"); + Tree tree = new Tree(db); + + index.add(trash, new File(trash, "file1")); + index.add(trash, new File(trash, "dir/subfile")); + IndexDiff diff = new IndexDiff(tree, index); + diff.diff(); + assertEquals(2, diff.getAdded().size()); + assertTrue(diff.getAdded().contains("file1")); + assertTrue(diff.getAdded().contains("dir/subfile")); + assertEquals(0, diff.getChanged().size()); + assertEquals(0, diff.getModified().size()); + assertEquals(0, diff.getRemoved().size()); + } + + public void testRemoved() throws IOException { + GitIndex index = new GitIndex(db); + writeTrashFile("file2", "file2"); + writeTrashFile("dir/file3", "dir/file3"); + + Tree tree = new Tree(db); + tree.addFile("file2"); + tree.addFile("dir/file3"); + assertEquals(2, tree.memberCount()); + tree.findBlobMember("file2").setId(ObjectId.fromString("30d67d4672d5c05833b7192cc77a79eaafb5c7ad")); + Tree tree2 = (Tree) tree.findTreeMember("dir"); + tree2.findBlobMember("file3").setId(ObjectId.fromString("873fb8d667d05436d728c52b1d7a09528e6eb59b")); + tree2.setId(new ObjectWriter(db).writeTree(tree2)); + tree.setId(new ObjectWriter(db).writeTree(tree)); + + IndexDiff diff = new IndexDiff(tree, index); + diff.diff(); + assertEquals(2, diff.getRemoved().size()); + assertTrue(diff.getRemoved().contains("file2")); + assertTrue(diff.getRemoved().contains("dir/file3")); + assertEquals(0, diff.getChanged().size()); + assertEquals(0, diff.getModified().size()); + assertEquals(0, diff.getAdded().size()); + } + + public void testModified() throws IOException { + GitIndex index = new GitIndex(db); + + + index.add(trash, writeTrashFile("file2", "file2")); + index.add(trash, writeTrashFile("dir/file3", "dir/file3")); + + writeTrashFile("dir/file3", "changed"); + + Tree tree = new Tree(db); + tree.addFile("file2").setId(ObjectId.fromString("0123456789012345678901234567890123456789")); + tree.addFile("dir/file3").setId(ObjectId.fromString("0123456789012345678901234567890123456789")); + assertEquals(2, tree.memberCount()); + + Tree tree2 = (Tree) tree.findTreeMember("dir"); + tree2.setId(new ObjectWriter(db).writeTree(tree2)); + tree.setId(new ObjectWriter(db).writeTree(tree)); + IndexDiff diff = new IndexDiff(tree, index); + diff.diff(); + assertEquals(2, diff.getChanged().size()); + assertTrue(diff.getChanged().contains("file2")); + assertTrue(diff.getChanged().contains("dir/file3")); + assertEquals(1, diff.getModified().size()); + assertTrue(diff.getModified().contains("dir/file3")); + assertEquals(0, diff.getAdded().size()); + assertEquals(0, diff.getRemoved().size()); + assertEquals(0, diff.getMissing().size()); + } + + public void testUnchangedSimple() throws IOException { + GitIndex index = new GitIndex(db); + + index.add(trash, writeTrashFile("a.b", "a.b")); + index.add(trash, writeTrashFile("a.c", "a.c")); + index.add(trash, writeTrashFile("a=c", "a=c")); + index.add(trash, writeTrashFile("a=d", "a=d")); + + Tree tree = new Tree(db); + // got the hash id'd from the data using echo -n a.b|git hash-object -t blob --stdin + tree.addFile("a.b").setId(ObjectId.fromString("f6f28df96c2b40c951164286e08be7c38ec74851")); + tree.addFile("a.c").setId(ObjectId.fromString("6bc0e647512d2a0bef4f26111e484dc87df7f5ca")); + tree.addFile("a=c").setId(ObjectId.fromString("06022365ddbd7fb126761319633bf73517770714")); + tree.addFile("a=d").setId(ObjectId.fromString("fa6414df3da87840700e9eeb7fc261dd77ccd5c2")); + + tree.setId(new ObjectWriter(db).writeTree(tree)); + + IndexDiff diff = new IndexDiff(tree, index); + diff.diff(); + assertEquals(0, diff.getChanged().size()); + assertEquals(0, diff.getAdded().size()); + assertEquals(0, diff.getRemoved().size()); + assertEquals(0, diff.getMissing().size()); + assertEquals(0, diff.getModified().size()); + } + + /** + * This test has both files and directories that involve + * the tricky ordering used by Git. + * + * @throws IOException + */ + public void testUnchangedComplex() throws IOException { + GitIndex index = new GitIndex(db); + + index.add(trash, writeTrashFile("a.b", "a.b")); + index.add(trash, writeTrashFile("a.c", "a.c")); + index.add(trash, writeTrashFile("a/b.b/b", "a/b.b/b")); + index.add(trash, writeTrashFile("a/b", "a/b")); + index.add(trash, writeTrashFile("a/c", "a/c")); + index.add(trash, writeTrashFile("a=c", "a=c")); + index.add(trash, writeTrashFile("a=d", "a=d")); + + Tree tree = new Tree(db); + // got the hash id'd from the data using echo -n a.b|git hash-object -t blob --stdin + tree.addFile("a.b").setId(ObjectId.fromString("f6f28df96c2b40c951164286e08be7c38ec74851")); + tree.addFile("a.c").setId(ObjectId.fromString("6bc0e647512d2a0bef4f26111e484dc87df7f5ca")); + tree.addFile("a/b.b/b").setId(ObjectId.fromString("8d840bd4e2f3a48ff417c8e927d94996849933fd")); + tree.addFile("a/b").setId(ObjectId.fromString("db89c972fc57862eae378f45b74aca228037d415")); + tree.addFile("a/c").setId(ObjectId.fromString("52ad142a008aeb39694bafff8e8f1be75ed7f007")); + tree.addFile("a=c").setId(ObjectId.fromString("06022365ddbd7fb126761319633bf73517770714")); + tree.addFile("a=d").setId(ObjectId.fromString("fa6414df3da87840700e9eeb7fc261dd77ccd5c2")); + + Tree tree3 = (Tree) tree.findTreeMember("a/b.b"); + tree3.setId(new ObjectWriter(db).writeTree(tree3)); + Tree tree2 = (Tree) tree.findTreeMember("a"); + tree2.setId(new ObjectWriter(db).writeTree(tree2)); + tree.setId(new ObjectWriter(db).writeTree(tree)); + + IndexDiff diff = new IndexDiff(tree, index); + diff.diff(); + assertEquals(0, diff.getChanged().size()); + assertEquals(0, diff.getAdded().size()); + assertEquals(0, diff.getRemoved().size()); + assertEquals(0, diff.getMissing().size()); + assertEquals(0, diff.getModified().size()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexTreeWalkerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexTreeWalkerTest.java new file mode 100644 index 0000000000..eb7b622d6c --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexTreeWalkerTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com> + * Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +import org.eclipse.jgit.lib.GitIndex.Entry; + +public class IndexTreeWalkerTest extends RepositoryTestCase { + private ArrayList<String> treeOnlyEntriesVisited = new ArrayList<String>(); + private ArrayList<String> bothVisited = new ArrayList<String>(); + private ArrayList<String> indexOnlyEntriesVisited = new ArrayList<String>(); + + private class TestIndexTreeVisitor extends AbstractIndexTreeVisitor { + public void visitEntry(TreeEntry treeEntry, Entry indexEntry, File file) { + if (treeEntry == null) + indexOnlyEntriesVisited.add(indexEntry.getName()); + else if (indexEntry == null) + treeOnlyEntriesVisited.add(treeEntry.getFullName()); + else bothVisited.add(indexEntry.getName()); + } + } + + /* + * Need to think about what I really need to be able to do.... + * + * 1) Visit all entries in index and tree + * 2) Get all directories that exist in the index, but not in the tree + * -- I'm pretty sure that I don't need to do the other way around + * because I already + */ + + public void testTreeOnlyOneLevel() throws IOException { + GitIndex index = new GitIndex(db); + Tree tree = new Tree(db); + tree.addFile("foo"); + tree.addFile("bar"); + + new IndexTreeWalker(index, tree, trash, new TestIndexTreeVisitor()).walk(); + + assertTrue(treeOnlyEntriesVisited.get(0).equals("bar")); + assertTrue(treeOnlyEntriesVisited.get(1).equals("foo")); + } + + public void testIndexOnlyOneLevel() throws IOException { + GitIndex index = new GitIndex(db); + Tree tree = new Tree(db); + + index.add(trash, writeTrashFile("foo", "foo")); + index.add(trash, writeTrashFile("bar", "bar")); + new IndexTreeWalker(index, tree, trash, new TestIndexTreeVisitor()).walk(); + + assertTrue(indexOnlyEntriesVisited.get(0).equals("bar")); + assertTrue(indexOnlyEntriesVisited.get(1).equals("foo")); + } + + public void testBoth() throws IOException { + GitIndex index = new GitIndex(db); + Tree tree = new Tree(db); + + index.add(trash, writeTrashFile("a", "a")); + tree.addFile("b/b"); + index.add(trash, writeTrashFile("c", "c")); + tree.addFile("c"); + + new IndexTreeWalker(index, tree, trash, new TestIndexTreeVisitor()).walk(); + assertTrue(indexOnlyEntriesVisited.contains("a")); + assertTrue(treeOnlyEntriesVisited.contains("b/b")); + assertTrue(bothVisited.contains("c")); + + } + + public void testIndexOnlySubDirs() throws IOException { + GitIndex index = new GitIndex(db); + Tree tree = new Tree(db); + + index.add(trash, writeTrashFile("foo/bar/baz", "foobar")); + index.add(trash, writeTrashFile("asdf", "asdf")); + new IndexTreeWalker(index, tree, trash, new TestIndexTreeVisitor()).walk(); + + assertEquals("asdf", indexOnlyEntriesVisited.get(0)); + assertEquals("foo/bar/baz", indexOnlyEntriesVisited.get(1)); + } + + public void testLeavingTree() throws IOException { + GitIndex index = new GitIndex(db); + index.add(trash, writeTrashFile("foo/bar", "foo/bar")); + index.add(trash, writeTrashFile("foobar", "foobar")); + + new IndexTreeWalker(index, db.mapTree(index.writeTree()), trash, new AbstractIndexTreeVisitor() { + @Override + public void visitEntry(TreeEntry entry, Entry indexEntry, File f) { + if (entry == null || indexEntry == null) + fail(); + } + + @Override + public void finishVisitTree(Tree tree, int i, String curDir) + throws IOException { + if (tree.memberCount() == 0) + fail(); + if (i == 0) + fail(); + } + + }).walk(); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MockSystemReader.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MockSystemReader.java new file mode 100644 index 0000000000..2e94632cee --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/MockSystemReader.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2009, Yann Simon <yann.simon.fr@gmail.com> + * 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.lib; + +import java.util.HashMap; +import java.util.Map; +import java.util.TimeZone; + +import org.eclipse.jgit.util.SystemReader; + +class MockSystemReader extends SystemReader { + final Map<String, String> values = new HashMap<String, String>(); + + FileBasedConfig userGitConfig; + + MockSystemReader() { + init(Constants.OS_USER_NAME_KEY); + init(Constants.GIT_AUTHOR_NAME_KEY); + init(Constants.GIT_AUTHOR_EMAIL_KEY); + init(Constants.GIT_COMMITTER_NAME_KEY); + init(Constants.GIT_COMMITTER_EMAIL_KEY); + userGitConfig = new FileBasedConfig(null); + } + + private void init(final String n) { + values.put(n, n); + } + + public String getenv(String variable) { + return values.get(variable); + } + + public String getProperty(String key) { + return values.get(key); + } + + public FileBasedConfig openUserConfig() { + return userGitConfig; + } + + public String getHostname() { + return "fake.host.example.com"; + } + + @Override + public long getCurrentTime() { + return 1250379778668L; // Sat Aug 15 20:12:58 GMT-03:30 2009 + } + + @Override + public int getTimezone(long when) { + return TimeZone.getTimeZone("GMT-3:30").getOffset(when); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java new file mode 100644 index 0000000000..7c2676bc7d --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java @@ -0,0 +1,1331 @@ +/* + * Copyright (C) 2008, Google Inc. + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import junit.framework.TestCase; + +import org.eclipse.jgit.errors.CorruptObjectException; + +public class ObjectCheckerTest extends TestCase { + private ObjectChecker checker; + + protected void setUp() throws Exception { + super.setUp(); + checker = new ObjectChecker(); + } + + public void testInvalidType() { + try { + checker.check(Constants.OBJ_BAD, new byte[0]); + fail("Did not throw CorruptObjectException"); + } catch (CorruptObjectException e) { + final String m = e.getMessage(); + assertEquals("Invalid object type: " + Constants.OBJ_BAD, m); + } + } + + public void testCheckBlob() throws CorruptObjectException { + // Any blob should pass... + checker.checkBlob(new byte[0]); + checker.checkBlob(new byte[1]); + + checker.check(Constants.OBJ_BLOB, new byte[0]); + checker.check(Constants.OBJ_BLOB, new byte[1]); + } + + public void testValidCommitNoParent() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author A. U. Thor <author@localhost> 1 +0000\n"); + b.append("committer A. U. Thor <author@localhost> 1 +0000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkCommit(data); + checker.check(Constants.OBJ_COMMIT, data); + } + + public void testValidCommitBlankAuthor() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author <> 0 +0000\n"); + b.append("committer <> 0 +0000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkCommit(data); + checker.check(Constants.OBJ_COMMIT, data); + } + + public void testValidCommit1Parent() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("parent "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author A. U. Thor <author@localhost> 1 +0000\n"); + b.append("committer A. U. Thor <author@localhost> 1 +0000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkCommit(data); + checker.check(Constants.OBJ_COMMIT, data); + } + + public void testValidCommit2Parent() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("parent "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("parent "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author A. U. Thor <author@localhost> 1 +0000\n"); + b.append("committer A. U. Thor <author@localhost> 1 +0000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkCommit(data); + checker.check(Constants.OBJ_COMMIT, data); + } + + public void testValidCommit128Parent() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + for (int i = 0; i < 128; i++) { + b.append("parent "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + } + + b.append("author A. U. Thor <author@localhost> 1 +0000\n"); + b.append("committer A. U. Thor <author@localhost> 1 +0000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkCommit(data); + checker.check(Constants.OBJ_COMMIT, data); + } + + public void testValidCommitNormalTime() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + final String when = "1222757360 -0730"; + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author A. U. Thor <author@localhost> " + when + "\n"); + b.append("committer A. U. Thor <author@localhost> " + when + "\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkCommit(data); + checker.check(Constants.OBJ_COMMIT, data); + } + + public void testInvalidCommitNoTree1() { + final StringBuilder b = new StringBuilder(); + + b.append("parent "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + assertEquals("no tree header", e.getMessage()); + } + } + + public void testInvalidCommitNoTree2() { + final StringBuilder b = new StringBuilder(); + + b.append("trie "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + assertEquals("no tree header", e.getMessage()); + } + } + + public void testInvalidCommitNoTree3() { + final StringBuilder b = new StringBuilder(); + + b.append("tree"); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + assertEquals("no tree header", e.getMessage()); + } + } + + public void testInvalidCommitNoTree4() { + final StringBuilder b = new StringBuilder(); + + b.append("tree\t"); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + assertEquals("no tree header", e.getMessage()); + } + } + + public void testInvalidCommitInvalidTree1() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("zzzzfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + assertEquals("invalid tree", e.getMessage()); + } + } + + public void testInvalidCommitInvalidTree2() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append("z\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + assertEquals("invalid tree", e.getMessage()); + } + } + + public void testInvalidCommitInvalidTree3() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9b"); + b.append("\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + assertEquals("invalid tree", e.getMessage()); + } + } + + public void testInvalidCommitInvalidTree4() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + assertEquals("invalid tree", e.getMessage()); + } + } + + public void testInvalidCommitInvalidParent1() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("parent "); + b.append("\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + assertEquals("invalid parent", e.getMessage()); + } + } + + public void testInvalidCommitInvalidParent2() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("parent "); + b.append("zzzzfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append("\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + assertEquals("invalid parent", e.getMessage()); + } + } + + public void testInvalidCommitInvalidParent3() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("parent "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append("\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + assertEquals("invalid parent", e.getMessage()); + } + } + + public void testInvalidCommitInvalidParent4() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("parent "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append("z\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + assertEquals("invalid parent", e.getMessage()); + } + } + + public void testInvalidCommitInvalidParent5() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("parent\t"); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append("\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + // Yes, really, we complain about author not being + // found as the invalid parent line wasn't consumed. + assertEquals("no author", e.getMessage()); + } + } + + public void testInvalidCommitNoAuthor() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("committer A. U. Thor <author@localhost> 1 +0000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + // Yes, really, we complain about author not being + // found as the invalid parent line wasn't consumed. + assertEquals("no author", e.getMessage()); + } + } + + public void testInvalidCommitNoCommitter1() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author A. U. Thor <author@localhost> 1 +0000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + // Yes, really, we complain about author not being + // found as the invalid parent line wasn't consumed. + assertEquals("no committer", e.getMessage()); + } + } + + public void testInvalidCommitNoCommitter2() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author A. U. Thor <author@localhost> 1 +0000\n"); + b.append("\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + // Yes, really, we complain about author not being + // found as the invalid parent line wasn't consumed. + assertEquals("no committer", e.getMessage()); + } + } + + public void testInvalidCommitInvalidAuthor1() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author A. U. Thor <foo 1 +0000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + // Yes, really, we complain about author not being + // found as the invalid parent line wasn't consumed. + assertEquals("invalid author", e.getMessage()); + } + } + + public void testInvalidCommitInvalidAuthor2() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author A. U. Thor foo> 1 +0000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + // Yes, really, we complain about author not being + // found as the invalid parent line wasn't consumed. + assertEquals("invalid author", e.getMessage()); + } + } + + public void testInvalidCommitInvalidAuthor3() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author 1 +0000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + // Yes, really, we complain about author not being + // found as the invalid parent line wasn't consumed. + assertEquals("invalid author", e.getMessage()); + } + } + + public void testInvalidCommitInvalidAuthor4() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author a <b> +0000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + // Yes, really, we complain about author not being + // found as the invalid parent line wasn't consumed. + assertEquals("invalid author", e.getMessage()); + } + } + + public void testInvalidCommitInvalidAuthor5() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author a <b>\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + // Yes, really, we complain about author not being + // found as the invalid parent line wasn't consumed. + assertEquals("invalid author", e.getMessage()); + } + } + + public void testInvalidCommitInvalidAuthor6() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author a <b> z"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + // Yes, really, we complain about author not being + // found as the invalid parent line wasn't consumed. + assertEquals("invalid author", e.getMessage()); + } + } + + public void testInvalidCommitInvalidAuthor7() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author a <b> 1 z"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + // Yes, really, we complain about author not being + // found as the invalid parent line wasn't consumed. + assertEquals("invalid author", e.getMessage()); + } + } + + public void testInvalidCommitInvalidCommitter() { + final StringBuilder b = new StringBuilder(); + + b.append("tree "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("author a <b> 1 +0000\n"); + b.append("committer a <"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkCommit(data); + fail("Did not catch corrupt object"); + } catch (CorruptObjectException e) { + // Yes, really, we complain about author not being + // found as the invalid parent line wasn't consumed. + assertEquals("invalid committer", e.getMessage()); + } + } + + public void testValidTag() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("type commit\n"); + b.append("tag test-tag\n"); + b.append("tagger A. U. Thor <author@localhost> 1 +0000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTag(data); + checker.check(Constants.OBJ_TAG, data); + } + + public void testInvalidTagNoObject1() { + final StringBuilder b = new StringBuilder(); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("no object header", e.getMessage()); + } + } + + public void testInvalidTagNoObject2() { + final StringBuilder b = new StringBuilder(); + + b.append("object\t"); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("no object header", e.getMessage()); + } + } + + public void testInvalidTagNoObject3() { + final StringBuilder b = new StringBuilder(); + + b.append("obejct "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("no object header", e.getMessage()); + } + } + + public void testInvalidTagNoObject4() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("zz9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("invalid object", e.getMessage()); + } + } + + public void testInvalidTagNoObject5() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append(" \n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("invalid object", e.getMessage()); + } + } + + public void testInvalidTagNoObject6() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("invalid object", e.getMessage()); + } + } + + public void testInvalidTagNoType1() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("no type header", e.getMessage()); + } + } + + public void testInvalidTagNoType2() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("type\tcommit\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("no type header", e.getMessage()); + } + } + + public void testInvalidTagNoType3() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("tpye commit\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("no type header", e.getMessage()); + } + } + + public void testInvalidTagNoType4() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("type commit"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("no tag header", e.getMessage()); + } + } + + public void testInvalidTagNoTagHeader1() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("type commit\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("no tag header", e.getMessage()); + } + } + + public void testInvalidTagNoTagHeader2() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("type commit\n"); + b.append("tag\tfoo\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("no tag header", e.getMessage()); + } + } + + public void testInvalidTagNoTagHeader3() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("type commit\n"); + b.append("tga foo\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("no tag header", e.getMessage()); + } + } + + public void testInvalidTagNoTagHeader4() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("type commit\n"); + b.append("tag foo"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("no tagger header", e.getMessage()); + } + } + + public void testInvalidTagNoTaggerHeader1() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("type commit\n"); + b.append("tag foo\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("no tagger header", e.getMessage()); + } + } + + public void testInvalidTagInvalidTaggerHeader1() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("type commit\n"); + b.append("tag foo\n"); + b.append("tagger \n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("invalid tagger", e.getMessage()); + } + } + + public void testInvalidTagInvalidTaggerHeader3() { + final StringBuilder b = new StringBuilder(); + + b.append("object "); + b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189"); + b.append('\n'); + + b.append("type commit\n"); + b.append("tag foo\n"); + b.append("tagger a < 1 +000\n"); + + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTag(data); + fail("incorrectly accepted invalid tag"); + } catch (CorruptObjectException e) { + assertEquals("invalid tagger", e.getMessage()); + } + } + + public void testValidEmptyTree() throws CorruptObjectException { + checker.checkTree(new byte[0]); + checker.check(Constants.OBJ_TREE, new byte[0]); + } + + public void testValidTree1() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 regular-file"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTree2() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "100755 executable"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTree3() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "40000 tree"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTree4() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "120000 symlink"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTree5() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "160000 git link"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTree6() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 .a"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTreeSorting1() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 fooaaa"); + entry(b, "100755 foobar"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTreeSorting2() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "100755 fooaaa"); + entry(b, "100644 foobar"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTreeSorting3() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "40000 a"); + entry(b, "100644 b"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTreeSorting4() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 a"); + entry(b, "40000 b"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTreeSorting5() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 a.c"); + entry(b, "40000 a"); + entry(b, "100644 a0c"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTreeSorting6() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "40000 a"); + entry(b, "100644 apple"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTreeSorting7() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "40000 an orang"); + entry(b, "40000 an orange"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testValidTreeSorting8() throws CorruptObjectException { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 a"); + entry(b, "100644 a0c"); + entry(b, "100644 b"); + final byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + public void testInvalidTreeModeStartsWithZero1() { + final StringBuilder b = new StringBuilder(); + entry(b, "0 a"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("mode starts with '0'", e.getMessage()); + } + } + + public void testInvalidTreeModeStartsWithZero2() { + final StringBuilder b = new StringBuilder(); + entry(b, "0100644 a"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("mode starts with '0'", e.getMessage()); + } + } + + public void testInvalidTreeModeStartsWithZero3() { + final StringBuilder b = new StringBuilder(); + entry(b, "040000 a"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("mode starts with '0'", e.getMessage()); + } + } + + public void testInvalidTreeModeNotOctal1() { + final StringBuilder b = new StringBuilder(); + entry(b, "8 a"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid mode character", e.getMessage()); + } + } + + public void testInvalidTreeModeNotOctal2() { + final StringBuilder b = new StringBuilder(); + entry(b, "Z a"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid mode character", e.getMessage()); + } + } + + public void testInvalidTreeModeNotSupportedMode1() { + final StringBuilder b = new StringBuilder(); + entry(b, "1 a"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid mode 1", e.getMessage()); + } + } + + public void testInvalidTreeModeNotSupportedMode2() { + final StringBuilder b = new StringBuilder(); + entry(b, "170000 a"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid mode " + 0170000, e.getMessage()); + } + } + + public void testInvalidTreeModeMissingName() { + final StringBuilder b = new StringBuilder(); + b.append("100644"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("truncated in mode", e.getMessage()); + } + } + + public void testInvalidTreeNameContainsSlash() { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 a/b"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("name contains '/'", e.getMessage()); + } + } + + public void testInvalidTreeNameIsEmpty() { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 "); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("zero length name", e.getMessage()); + } + } + + public void testInvalidTreeNameIsDot() { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 ."); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid name '.'", e.getMessage()); + } + } + + public void testInvalidTreeNameIsDotDot() { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 .."); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid name '..'", e.getMessage()); + } + } + + public void testInvalidTreeTruncatedInName() { + final StringBuilder b = new StringBuilder(); + b.append("100644 b"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("truncated in name", e.getMessage()); + } + } + + public void testInvalidTreeTruncatedInObjectId() { + final StringBuilder b = new StringBuilder(); + b.append("100644 b\0\1\2"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("truncated in object id", e.getMessage()); + } + } + + public void testInvalidTreeBadSorting1() { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 foobar"); + entry(b, "100644 fooaaa"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("incorrectly sorted", e.getMessage()); + } + } + + public void testInvalidTreeBadSorting2() { + final StringBuilder b = new StringBuilder(); + entry(b, "40000 a"); + entry(b, "100644 a.c"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("incorrectly sorted", e.getMessage()); + } + } + + public void testInvalidTreeBadSorting3() { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 a0c"); + entry(b, "40000 a"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("incorrectly sorted", e.getMessage()); + } + } + + public void testInvalidTreeDuplicateNames1() { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 a"); + entry(b, "100644 a"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("duplicate entry names", e.getMessage()); + } + } + + public void testInvalidTreeDuplicateNames2() { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 a"); + entry(b, "100755 a"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("duplicate entry names", e.getMessage()); + } + } + + public void testInvalidTreeDuplicateNames3() { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 a"); + entry(b, "40000 a"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("duplicate entry names", e.getMessage()); + } + } + + public void testInvalidTreeDuplicateNames4() { + final StringBuilder b = new StringBuilder(); + entry(b, "100644 a"); + entry(b, "100644 a.c"); + entry(b, "100644 a.d"); + entry(b, "100644 a.e"); + entry(b, "40000 a"); + entry(b, "100644 zoo"); + final byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("duplicate entry names", e.getMessage()); + } + } + + private static void entry(final StringBuilder b, final String modeName) { + b.append(modeName); + b.append('\0'); + for (int i = 0; i < Constants.OBJECT_ID_LENGTH; i++) + b.append((char) i); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java new file mode 100644 index 0000000000..31de3d98ad --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexTestCase.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> + * 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.lib; + +import java.io.File; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.PackIndex.MutableEntry; + +public abstract class PackIndexTestCase extends RepositoryTestCase { + + PackIndex smallIdx; + + PackIndex denseIdx; + + public void setUp() throws Exception { + super.setUp(); + smallIdx = PackIndex.open(getFileForPack34be9032()); + denseIdx = PackIndex.open(getFileForPackdf2982f28()); + } + + /** + * Return file with appropriate index version for prepared pack. + * + * @return file with index + */ + public abstract File getFileForPack34be9032(); + + /** + * Return file with appropriate index version for prepared pack. + * + * @return file with index + */ + public abstract File getFileForPackdf2982f28(); + + /** + * Verify CRC32 support. + * + * @throws MissingObjectException + * @throws UnsupportedOperationException + */ + public abstract void testCRC32() throws MissingObjectException, + UnsupportedOperationException; + + /** + * Test contracts of Iterator methods and this implementation remove() + * limitations. + */ + public void testIteratorMethodsContract() { + Iterator<PackIndex.MutableEntry> iter = smallIdx.iterator(); + while (iter.hasNext()) { + iter.next(); + } + + try { + iter.next(); + fail("next() unexpectedly returned element"); + } catch (NoSuchElementException x) { + // expected + } + + try { + iter.remove(); + fail("remove() shouldn't be implemented"); + } catch (UnsupportedOperationException x) { + // expected + } + } + + /** + * Test results of iterator comparing to content of well-known (prepared) + * small index. + */ + public void testIteratorReturnedValues1() { + Iterator<PackIndex.MutableEntry> iter = smallIdx.iterator(); + assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", iter.next() + .name()); + assertEquals("540a36d136cf413e4b064c2b0e0a4db60f77feab", iter.next() + .name()); + assertEquals("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259", iter.next() + .name()); + assertEquals("6ff87c4664981e4397625791c8ea3bbb5f2279a3", iter.next() + .name()); + assertEquals("82c6b885ff600be425b4ea96dee75dca255b69e7", iter.next() + .name()); + assertEquals("902d5476fa249b7abc9d84c611577a81381f0327", iter.next() + .name()); + assertEquals("aabf2ffaec9b497f0950352b3e582d73035c2035", iter.next() + .name()); + assertEquals("c59759f143fb1fe21c197981df75a7ee00290799", iter.next() + .name()); + assertFalse(iter.hasNext()); + } + + /** + * Compare offset from iterator entries with output of findOffset() method. + */ + public void testCompareEntriesOffsetsWithFindOffsets() { + for (MutableEntry me : smallIdx) { + assertEquals(smallIdx.findOffset(me.toObjectId()), me.getOffset()); + } + for (MutableEntry me : denseIdx) { + assertEquals(denseIdx.findOffset(me.toObjectId()), me.getOffset()); + } + } + + /** + * Test partial results of iterator comparing to content of well-known + * (prepared) dense index, that may need multi-level indexing. + */ + public void testIteratorReturnedValues2() { + Iterator<PackIndex.MutableEntry> iter = denseIdx.iterator(); + while (!iter.next().name().equals( + "0a3d7772488b6b106fb62813c4d6d627918d9181")) { + // just iterating + } + assertEquals("1004d0d7ac26fbf63050a234c9b88a46075719d3", iter.next() + .name()); // same level-1 + assertEquals("10da5895682013006950e7da534b705252b03be6", iter.next() + .name()); // same level-1 + assertEquals("1203b03dc816ccbb67773f28b3c19318654b0bc8", iter.next() + .name()); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java new file mode 100644 index 0000000000..f3082fb294 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV1Test.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com> + * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> + * Copyright (C) 2009, Matthias Sohn <matthias.sohn@sap.com> + * 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.lib; + +import java.io.File; + +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.util.JGitTestUtil; + +public class PackIndexV1Test extends PackIndexTestCase { + @Override + public File getFileForPack34be9032() { + return JGitTestUtil.getTestResourceFile( + "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idx"); + } + + @Override + public File getFileForPackdf2982f28() { + return JGitTestUtil.getTestResourceFile( + "pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.idx"); + } + + /** + * Verify CRC32 - V1 should not index anything. + * + * @throws MissingObjectException + */ + @Override + public void testCRC32() throws MissingObjectException { + assertFalse(smallIdx.hasCRC32Support()); + try { + smallIdx.findCRC32(ObjectId + .fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904")); + fail("index V1 shouldn't support CRC"); + } catch (UnsupportedOperationException x) { + // expected + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java new file mode 100644 index 0000000000..c5669f9d24 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackIndexV2Test.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com> + * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> + * Copyright (C) 2009, Matthias Sohn <matthias.sohn@sap.com> + * 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.lib; + +import java.io.File; + +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.util.JGitTestUtil; + +public class PackIndexV2Test extends PackIndexTestCase { + @Override + public File getFileForPack34be9032() { + return JGitTestUtil.getTestResourceFile( + "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idxV2"); + } + + @Override + public File getFileForPackdf2982f28() { + return JGitTestUtil.getTestResourceFile( + "pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.idxV2"); + } + + /** + * Verify CRC32 indexing. + * + * @throws UnsupportedOperationException + * @throws MissingObjectException + */ + @Override + public void testCRC32() throws MissingObjectException, + UnsupportedOperationException { + assertTrue(smallIdx.hasCRC32Support()); + assertEquals(0x00000000C2B64258l, smallIdx.findCRC32(ObjectId + .fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))); + assertEquals(0x0000000072AD57C2l, smallIdx.findCRC32(ObjectId + .fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"))); + assertEquals(0x00000000FF10A479l, smallIdx.findCRC32(ObjectId + .fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"))); + assertEquals(0x0000000034B27DDCl, smallIdx.findCRC32(ObjectId + .fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3"))); + assertEquals(0x000000004743F1E4l, smallIdx.findCRC32(ObjectId + .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"))); + assertEquals(0x00000000640B358Bl, smallIdx.findCRC32(ObjectId + .fromString("902d5476fa249b7abc9d84c611577a81381f0327"))); + assertEquals(0x000000002A17CB5El, smallIdx.findCRC32(ObjectId + .fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"))); + assertEquals(0x000000000B3B5BA6l, smallIdx.findCRC32(ObjectId + .fromString("c59759f143fb1fe21c197981df75a7ee00290799"))); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java new file mode 100644 index 0000000000..19b705813f --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackReverseIndexTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com> + * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> + * 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.lib; + +import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.lib.PackIndex.MutableEntry; +import org.eclipse.jgit.util.JGitTestUtil; + +public class PackReverseIndexTest extends RepositoryTestCase { + + private PackIndex idx; + + private PackReverseIndex reverseIdx; + + /** + * Set up tested class instance, test constructor by the way. + */ + public void setUp() throws Exception { + super.setUp(); + // index with both small (< 2^31) and big offsets + idx = PackIndex.open(JGitTestUtil.getTestResourceFile( + "pack-huge.idx")); + reverseIdx = new PackReverseIndex(idx); + } + + /** + * Test findObject() for all index entries. + */ + public void testFindObject() { + for (MutableEntry me : idx) + assertEquals(me.toObjectId(), reverseIdx.findObject(me.getOffset())); + } + + /** + * Test findObject() with illegal argument. + */ + public void testFindObjectWrongOffset() { + assertNull(reverseIdx.findObject(0)); + } + + /** + * Test findNextOffset() for all index entries. + * + * @throws CorruptObjectException + */ + public void testFindNextOffset() throws CorruptObjectException { + long offset = findFirstOffset(); + assertTrue(offset > 0); + for (int i = 0; i < idx.getObjectCount(); i++) { + long newOffset = reverseIdx.findNextOffset(offset, Long.MAX_VALUE); + assertTrue(newOffset > offset); + if (i == idx.getObjectCount() - 1) + assertEquals(newOffset, Long.MAX_VALUE); + else + assertEquals(newOffset, idx.findOffset(reverseIdx + .findObject(newOffset))); + offset = newOffset; + } + } + + /** + * Test findNextOffset() with wrong illegal argument as offset. + */ + public void testFindNextOffsetWrongOffset() { + try { + reverseIdx.findNextOffset(0, Long.MAX_VALUE); + fail("findNextOffset() should throw exception"); + } catch (CorruptObjectException x) { + // expected + } + } + + private long findFirstOffset() { + long min = Long.MAX_VALUE; + for (MutableEntry me : idx) + min = Math.min(min, me.getOffset()); + return min; + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java new file mode 100644 index 0000000000..7032907496 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PackWriterTest.java @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> + * 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.lib; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.PackIndex.MutableEntry; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.IndexPack; +import org.eclipse.jgit.util.JGitTestUtil; + +public class PackWriterTest extends RepositoryTestCase { + + private static final List<ObjectId> EMPTY_LIST_OBJECT = Collections + .<ObjectId> emptyList(); + + private static final List<RevObject> EMPTY_LIST_REVS = Collections + .<RevObject> emptyList(); + + private PackWriter writer; + + private ByteArrayOutputStream os; + + private PackOutputStream cos; + + private File packBase; + + private File packFile; + + private File indexFile; + + private PackFile pack; + + public void setUp() throws Exception { + super.setUp(); + os = new ByteArrayOutputStream(); + cos = new PackOutputStream(os); + packBase = new File(trash, "tmp_pack"); + packFile = new File(trash, "tmp_pack.pack"); + indexFile = new File(trash, "tmp_pack.idx"); + writer = new PackWriter(db, new TextProgressMonitor()); + } + + /** + * Test constructor for exceptions, default settings, initialization. + */ + public void testContructor() { + assertEquals(false, writer.isDeltaBaseAsOffset()); + assertEquals(true, writer.isReuseDeltas()); + assertEquals(true, writer.isReuseObjects()); + assertEquals(0, writer.getObjectsNumber()); + } + + /** + * Change default settings and verify them. + */ + public void testModifySettings() { + writer.setDeltaBaseAsOffset(true); + writer.setReuseDeltas(false); + writer.setReuseObjects(false); + + assertEquals(true, writer.isDeltaBaseAsOffset()); + assertEquals(false, writer.isReuseDeltas()); + assertEquals(false, writer.isReuseObjects()); + } + + /** + * Write empty pack by providing empty sets of interesting/uninteresting + * objects and check for correct format. + * + * @throws IOException + */ + public void testWriteEmptyPack1() throws IOException { + createVerifyOpenPack(EMPTY_LIST_OBJECT, EMPTY_LIST_OBJECT, false, false); + + assertEquals(0, writer.getObjectsNumber()); + assertEquals(0, pack.getObjectCount()); + assertEquals("da39a3ee5e6b4b0d3255bfef95601890afd80709", writer + .computeName().name()); + } + + /** + * Write empty pack by providing empty iterator of objects to write and + * check for correct format. + * + * @throws IOException + */ + public void testWriteEmptyPack2() throws IOException { + createVerifyOpenPack(EMPTY_LIST_REVS.iterator()); + + assertEquals(0, writer.getObjectsNumber()); + assertEquals(0, pack.getObjectCount()); + } + + /** + * Try to pass non-existing object as uninteresting, with non-ignoring + * setting. + * + * @throws IOException + */ + public void testNotIgnoreNonExistingObjects() throws IOException { + final ObjectId nonExisting = ObjectId + .fromString("0000000000000000000000000000000000000001"); + try { + createVerifyOpenPack(EMPTY_LIST_OBJECT, Collections.nCopies(1, + nonExisting), false, false); + fail("Should have thrown MissingObjectException"); + } catch (MissingObjectException x) { + // expected + } + } + + /** + * Try to pass non-existing object as uninteresting, with ignoring setting. + * + * @throws IOException + */ + public void testIgnoreNonExistingObjects() throws IOException { + final ObjectId nonExisting = ObjectId + .fromString("0000000000000000000000000000000000000001"); + createVerifyOpenPack(EMPTY_LIST_OBJECT, Collections.nCopies(1, + nonExisting), false, true); + // shouldn't throw anything + } + + /** + * Create pack basing on only interesting objects, then precisely verify + * content. No delta reuse here. + * + * @throws IOException + */ + public void testWritePack1() throws IOException { + writer.setReuseDeltas(false); + writeVerifyPack1(); + } + + /** + * Test writing pack without object reuse. Pack content/preparation as in + * {@link #testWritePack1()}. + * + * @throws IOException + */ + public void testWritePack1NoObjectReuse() throws IOException { + writer.setReuseDeltas(false); + writer.setReuseObjects(false); + writeVerifyPack1(); + } + + /** + * Create pack basing on both interesting and uninteresting objects, then + * precisely verify content. No delta reuse here. + * + * @throws IOException + */ + public void testWritePack2() throws IOException { + writeVerifyPack2(false); + } + + /** + * Test pack writing with deltas reuse, delta-base first rule. Pack + * content/preparation as in {@link #testWritePack2()}. + * + * @throws IOException + */ + public void testWritePack2DeltasReuseRefs() throws IOException { + writeVerifyPack2(true); + } + + /** + * Test pack writing with delta reuse. Delta bases referred as offsets. Pack + * configuration as in {@link #testWritePack2DeltasReuseRefs()}. + * + * @throws IOException + */ + public void testWritePack2DeltasReuseOffsets() throws IOException { + writer.setDeltaBaseAsOffset(true); + writeVerifyPack2(true); + } + + /** + * Test pack writing with delta reuse. Raw-data copy (reuse) is made on a + * pack with CRC32 index. Pack configuration as in + * {@link #testWritePack2DeltasReuseRefs()}. + * + * @throws IOException + */ + public void testWritePack2DeltasCRC32Copy() throws IOException { + final File packDir = new File(db.getObjectsDirectory(), "pack"); + final File crc32Pack = new File(packDir, + "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack"); + final File crc32Idx = new File(packDir, + "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idx"); + copyFile(JGitTestUtil.getTestResourceFile( + "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idxV2"), + crc32Idx); + db.openPack(crc32Pack, crc32Idx); + + writeVerifyPack2(true); + } + + /** + * Create pack basing on fixed objects list, then precisely verify content. + * No delta reuse here. + * + * @throws IOException + * @throws MissingObjectException + * + */ + public void testWritePack3() throws MissingObjectException, IOException { + writer.setReuseDeltas(false); + final ObjectId forcedOrder[] = new ObjectId[] { + ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), + ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"), + ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), + ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"), + ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"), + ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") }; + final RevWalk parser = new RevWalk(db); + final RevObject forcedOrderRevs[] = new RevObject[forcedOrder.length]; + for (int i = 0; i < forcedOrder.length; i++) + forcedOrderRevs[i] = parser.parseAny(forcedOrder[i]); + + createVerifyOpenPack(Arrays.asList(forcedOrderRevs).iterator()); + + assertEquals(forcedOrder.length, writer.getObjectsNumber()); + verifyObjectsOrder(forcedOrder); + assertEquals("ed3f96b8327c7c66b0f8f70056129f0769323d86", writer + .computeName().name()); + } + + /** + * Another pack creation: basing on both interesting and uninteresting + * objects. No delta reuse possible here, as this is a specific case when we + * write only 1 commit, associated with 1 tree, 1 blob. + * + * @throws IOException + */ + public void testWritePack4() throws IOException { + writeVerifyPack4(false); + } + + /** + * Test thin pack writing: 1 blob delta base is on objects edge. Pack + * configuration as in {@link #testWritePack4()}. + * + * @throws IOException + */ + public void testWritePack4ThinPack() throws IOException { + writeVerifyPack4(true); + } + + /** + * Compare sizes of packs created using {@link #testWritePack2()} and + * {@link #testWritePack2DeltasReuseRefs()}. The pack using deltas should + * be smaller. + * + * @throws Exception + */ + public void testWritePack2SizeDeltasVsNoDeltas() throws Exception { + testWritePack2(); + final long sizePack2NoDeltas = cos.length(); + tearDown(); + setUp(); + testWritePack2DeltasReuseRefs(); + final long sizePack2DeltasRefs = cos.length(); + + assertTrue(sizePack2NoDeltas > sizePack2DeltasRefs); + } + + /** + * Compare sizes of packs created using + * {@link #testWritePack2DeltasReuseRefs()} and + * {@link #testWritePack2DeltasReuseOffsets()}. The pack with delta bases + * written as offsets should be smaller. + * + * @throws Exception + */ + public void testWritePack2SizeOffsetsVsRefs() throws Exception { + testWritePack2DeltasReuseRefs(); + final long sizePack2DeltasRefs = cos.length(); + tearDown(); + setUp(); + testWritePack2DeltasReuseOffsets(); + final long sizePack2DeltasOffsets = cos.length(); + + assertTrue(sizePack2DeltasRefs > sizePack2DeltasOffsets); + } + + /** + * Compare sizes of packs created using {@link #testWritePack4()} and + * {@link #testWritePack4ThinPack()}. Obviously, the thin pack should be + * smaller. + * + * @throws Exception + */ + public void testWritePack4SizeThinVsNoThin() throws Exception { + testWritePack4(); + final long sizePack4 = cos.length(); + tearDown(); + setUp(); + testWritePack4ThinPack(); + final long sizePack4Thin = cos.length(); + + assertTrue(sizePack4 > sizePack4Thin); + } + + public void testWriteIndex() throws Exception { + writer.setIndexVersion(2); + writeVerifyPack4(false); + + // Validate that IndexPack came up with the right CRC32 value. + final PackIndex idx1 = PackIndex.open(indexFile); + assertTrue(idx1 instanceof PackIndexV2); + assertEquals(0x4743F1E4L, idx1.findCRC32(ObjectId + .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"))); + + // Validate that an index written by PackWriter is the same. + final File idx2File = new File(indexFile.getAbsolutePath() + ".2"); + final FileOutputStream is = new FileOutputStream(idx2File); + try { + writer.writeIndex(is); + } finally { + is.close(); + } + final PackIndex idx2 = PackIndex.open(idx2File); + assertTrue(idx2 instanceof PackIndexV2); + assertEquals(idx1.getObjectCount(), idx2.getObjectCount()); + assertEquals(idx1.getOffset64Count(), idx2.getOffset64Count()); + + for (int i = 0; i < idx1.getObjectCount(); i++) { + final ObjectId id = idx1.getObjectId(i); + assertEquals(id, idx2.getObjectId(i)); + assertEquals(idx1.findOffset(id), idx2.findOffset(id)); + assertEquals(idx1.findCRC32(id), idx2.findCRC32(id)); + } + } + + // TODO: testWritePackDeltasCycle() + // TODO: testWritePackDeltasDepth() + + private void writeVerifyPack1() throws IOException { + final LinkedList<ObjectId> interestings = new LinkedList<ObjectId>(); + interestings.add(ObjectId + .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")); + createVerifyOpenPack(interestings, EMPTY_LIST_OBJECT, false, false); + + final ObjectId expectedOrder[] = new ObjectId[] { + ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), + ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"), + ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"), + ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), + ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"), + ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"), + ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"), + ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") }; + + assertEquals(expectedOrder.length, writer.getObjectsNumber()); + verifyObjectsOrder(expectedOrder); + assertEquals("34be9032ac282b11fa9babdc2b2a93ca996c9c2f", writer + .computeName().name()); + } + + private void writeVerifyPack2(boolean deltaReuse) throws IOException { + writer.setReuseDeltas(deltaReuse); + final LinkedList<ObjectId> interestings = new LinkedList<ObjectId>(); + interestings.add(ObjectId + .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")); + final LinkedList<ObjectId> uninterestings = new LinkedList<ObjectId>(); + uninterestings.add(ObjectId + .fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab")); + createVerifyOpenPack(interestings, uninterestings, false, false); + + final ObjectId expectedOrder[] = new ObjectId[] { + ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), + ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"), + ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), + ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"), + ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"), + ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") }; + if (deltaReuse) { + // objects order influenced (swapped) by delta-base first rule + ObjectId temp = expectedOrder[4]; + expectedOrder[4] = expectedOrder[5]; + expectedOrder[5] = temp; + } + assertEquals(expectedOrder.length, writer.getObjectsNumber()); + verifyObjectsOrder(expectedOrder); + assertEquals("ed3f96b8327c7c66b0f8f70056129f0769323d86", writer + .computeName().name()); + } + + private void writeVerifyPack4(final boolean thin) throws IOException { + final LinkedList<ObjectId> interestings = new LinkedList<ObjectId>(); + interestings.add(ObjectId + .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")); + final LinkedList<ObjectId> uninterestings = new LinkedList<ObjectId>(); + uninterestings.add(ObjectId + .fromString("c59759f143fb1fe21c197981df75a7ee00290799")); + createVerifyOpenPack(interestings, uninterestings, thin, false); + + final ObjectId writtenObjects[] = new ObjectId[] { + ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), + ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), + ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") }; + assertEquals(writtenObjects.length, writer.getObjectsNumber()); + ObjectId expectedObjects[]; + if (thin) { + expectedObjects = new ObjectId[4]; + System.arraycopy(writtenObjects, 0, expectedObjects, 0, + writtenObjects.length); + expectedObjects[3] = ObjectId + .fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3"); + + } else { + expectedObjects = writtenObjects; + } + verifyObjectsOrder(expectedObjects); + assertEquals("cded4b74176b4456afa456768b2b5aafb41c44fc", writer + .computeName().name()); + } + + private void createVerifyOpenPack(final Collection<ObjectId> interestings, + final Collection<ObjectId> uninterestings, final boolean thin, + final boolean ignoreMissingUninteresting) + throws MissingObjectException, IOException { + writer.setThin(thin); + writer.setIgnoreMissingUninteresting(ignoreMissingUninteresting); + writer.preparePack(interestings, uninterestings); + writer.writePack(cos); + verifyOpenPack(thin); + } + + private void createVerifyOpenPack(final Iterator<RevObject> objectSource) + throws MissingObjectException, IOException { + writer.preparePack(objectSource); + writer.writePack(cos); + verifyOpenPack(false); + } + + private void verifyOpenPack(final boolean thin) throws IOException { + if (thin) { + final InputStream is = new ByteArrayInputStream(os.toByteArray()); + final IndexPack indexer = new IndexPack(db, is, packBase); + try { + indexer.index(new TextProgressMonitor()); + fail("indexer should grumble about missing object"); + } catch (IOException x) { + // expected + } + } + final InputStream is = new ByteArrayInputStream(os.toByteArray()); + final IndexPack indexer = new IndexPack(db, is, packBase); + indexer.setKeepEmpty(true); + indexer.setFixThin(thin); + indexer.setIndexVersion(2); + indexer.index(new TextProgressMonitor()); + pack = new PackFile(indexFile, packFile); + } + + private void verifyObjectsOrder(final ObjectId objectsOrder[]) { + final List<PackIndex.MutableEntry> entries = new ArrayList<PackIndex.MutableEntry>(); + + for (MutableEntry me : pack) { + entries.add(me.cloneEntry()); + } + Collections.sort(entries, new Comparator<PackIndex.MutableEntry>() { + public int compare(MutableEntry o1, MutableEntry o2) { + return Long.signum(o1.getOffset() - o2.getOffset()); + } + }); + + int i = 0; + for (MutableEntry me : entries) { + assertEquals(objectsOrder[i++].toObjectId(), me.toObjectId()); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java new file mode 100644 index 0000000000..5ac75d8043 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReadTreeTest.java @@ -0,0 +1,581 @@ +/* + * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com> + * Copyright (C) 2008-2009, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; + +import org.eclipse.jgit.errors.CheckoutConflictException; + +public class ReadTreeTest extends RepositoryTestCase { + + private Tree theHead; + private Tree theMerge; + private GitIndex theIndex; + private WorkDirCheckout theReadTree; + // Each of these rules are from the read-tree manpage + // go there to see what they mean. + // Rule 0 is left out for obvious reasons :) + public void testRules1thru3_NoIndexEntry() throws IOException { + GitIndex index = new GitIndex(db); + + Tree head = new Tree(db); + FileTreeEntry headFile = head.addFile("foo"); + ObjectId objectId = ObjectId.fromString("ba78e065e2c261d4f7b8f42107588051e87e18e9"); + headFile.setId(objectId); + Tree merge = new Tree(db); + + WorkDirCheckout readTree = new WorkDirCheckout(db, trash, head, index, merge); + readTree.prescanTwoTrees(); + + assertTrue(readTree.removed.contains("foo")); + + readTree = new WorkDirCheckout(db, trash, merge, index, head); + readTree.prescanTwoTrees(); + + assertEquals(objectId, readTree.updated.get("foo")); + + ObjectId anotherId = ObjectId.fromString("ba78e065e2c261d4f7b8f42107588051e87e18ee"); + merge.addFile("foo").setId(anotherId); + + readTree = new WorkDirCheckout(db, trash, head, index, merge); + readTree.prescanTwoTrees(); + + assertEquals(anotherId, readTree.updated.get("foo")); + } + + void setupCase(HashMap<String, String> headEntries, + HashMap<String, String> mergeEntries, + HashMap<String, String> indexEntries) throws IOException { + theHead = buildTree(headEntries); + theMerge = buildTree(mergeEntries); + theIndex = buildIndex(indexEntries); + } + + private GitIndex buildIndex(HashMap<String, String> indexEntries) throws IOException { + GitIndex index = new GitIndex(db); + + if (indexEntries == null) + return index; + for (java.util.Map.Entry<String,String> e : indexEntries.entrySet()) { + index.add(trash, writeTrashFile(e.getKey(), e.getValue())).forceRecheck(); + } + + return index; + } + + private Tree buildTree(HashMap<String, String> headEntries) throws IOException { + Tree tree = new Tree(db); + + if (headEntries == null) + return tree; + for (java.util.Map.Entry<String,String> e : headEntries.entrySet()) { + tree.addFile(e.getKey()).setId(genSha1(e.getValue())); + } + + return tree; + } + + ObjectId genSha1(String data) { + InputStream is = new ByteArrayInputStream(data.getBytes()); + ObjectWriter objectWriter = new ObjectWriter(db); + try { + return objectWriter.writeObject(Constants.OBJ_BLOB, data + .getBytes().length, is, true); + } catch (IOException e) { + fail(e.toString()); + } + return null; + } + + private WorkDirCheckout go() throws IOException { + theReadTree = new WorkDirCheckout(db, trash, theHead, theIndex, theMerge); + theReadTree.prescanTwoTrees(); + return theReadTree; + } + + // for these rules, they all have clean yes/no options + // but it doesn't matter if the entry is clean or not + // so we can just ignore the state in the filesystem entirely + public void testRules4thru13_IndexEntryNotInHead() throws IOException { + // rules 4 and 5 + HashMap<String, String> idxMap; + + idxMap = new HashMap<String, String>(); + idxMap.put("foo", "foo"); + setupCase(null, null, idxMap); + theReadTree = go(); + + assertTrue(theReadTree.updated.isEmpty()); + assertTrue(theReadTree.removed.isEmpty()); + assertTrue(theReadTree.conflicts.isEmpty()); + + // rules 6 and 7 + idxMap = new HashMap<String, String>(); + idxMap.put("foo", "foo"); + setupCase(null, idxMap, idxMap); + theReadTree = go(); + + assertAllEmpty(); + + // rules 8 and 9 + HashMap<String, String> mergeMap; + mergeMap = new HashMap<String, String>(); + + mergeMap.put("foo", "merge"); + setupCase(null, mergeMap, idxMap); + go(); + + assertTrue(theReadTree.updated.isEmpty()); + assertTrue(theReadTree.removed.isEmpty()); + assertTrue(theReadTree.conflicts.contains("foo")); + + // rule 10 + + HashMap<String, String> headMap = new HashMap<String, String>(); + headMap.put("foo", "foo"); + setupCase(headMap, null, idxMap); + go(); + + assertTrue(theReadTree.removed.contains("foo")); + assertTrue(theReadTree.updated.isEmpty()); + assertTrue(theReadTree.conflicts.isEmpty()); + + // rule 11 + setupCase(headMap, null, idxMap); + new File(trash, "foo").delete(); + writeTrashFile("foo", "bar"); + theIndex.getMembers()[0].forceRecheck(); + go(); + + assertTrue(theReadTree.removed.isEmpty()); + assertTrue(theReadTree.updated.isEmpty()); + assertTrue(theReadTree.conflicts.contains("foo")); + + // rule 12 & 13 + headMap.put("foo", "head"); + setupCase(headMap, null, idxMap); + go(); + + assertTrue(theReadTree.removed.isEmpty()); + assertTrue(theReadTree.updated.isEmpty()); + assertTrue(theReadTree.conflicts.contains("foo")); + + // rules 14 & 15 + setupCase(headMap, headMap, idxMap); + go(); + + assertAllEmpty(); + + // rules 16 & 17 + setupCase(headMap, mergeMap, idxMap); go(); + assertTrue(theReadTree.conflicts.contains("foo")); + + // rules 18 & 19 + setupCase(headMap, idxMap, idxMap); go(); + assertAllEmpty(); + + // rule 20 + setupCase(idxMap, mergeMap, idxMap); go(); + assertTrue(theReadTree.updated.containsKey("foo")); + + // rules 21 + setupCase(idxMap, mergeMap, idxMap); + new File(trash, "foo").delete(); + writeTrashFile("foo", "bar"); + theIndex.getMembers()[0].forceRecheck(); + go(); + assertTrue(theReadTree.conflicts.contains("foo")); + } + + private void assertAllEmpty() { + assertTrue(theReadTree.removed.isEmpty()); + assertTrue(theReadTree.updated.isEmpty()); + assertTrue(theReadTree.conflicts.isEmpty()); + } + + public void testDirectoryFileSimple() throws IOException { + theIndex = new GitIndex(db); + theIndex.add(trash, writeTrashFile("DF", "DF")); + Tree treeDF = db.mapTree(theIndex.writeTree()); + + recursiveDelete(new File(trash, "DF")); + theIndex = new GitIndex(db); + theIndex.add(trash, writeTrashFile("DF/DF", "DF/DF")); + Tree treeDFDF = db.mapTree(theIndex.writeTree()); + + theIndex = new GitIndex(db); + recursiveDelete(new File(trash, "DF")); + + theIndex.add(trash, writeTrashFile("DF", "DF")); + theReadTree = new WorkDirCheckout(db, trash, treeDF, theIndex, treeDFDF); + theReadTree.prescanTwoTrees(); + + assertTrue(theReadTree.removed.contains("DF")); + assertTrue(theReadTree.updated.containsKey("DF/DF")); + + recursiveDelete(new File(trash, "DF")); + theIndex = new GitIndex(db); + theIndex.add(trash, writeTrashFile("DF/DF", "DF/DF")); + + theReadTree = new WorkDirCheckout(db, trash, treeDFDF, theIndex, treeDF); + theReadTree.prescanTwoTrees(); + assertTrue(theReadTree.removed.contains("DF/DF")); + assertTrue(theReadTree.updated.containsKey("DF")); + } + + /* + * Directory/File Conflict cases: + * It's entirely possible that in practice a number of these may be equivalent + * to the cases described in git-read-tree.txt. As long as it does the right thing, + * that's all I care about. These are basically reverse-engineered from + * what git currently does. If there are tests for these in git, it's kind of + * hard to track them all down... + * + * H I M Clean H==M H==I I==M Result + * ------------------------------------------------------------------ + *1 D D F Y N Y N Update + *2 D D F N N Y N Conflict + *3 D F D Y N N Update + *4 D F D N N N Update + *5 D F F Y N N Y Keep + *6 D F F N N N Y Keep + *7 F D F Y Y N N Update + *8 F D F N Y N N Conflict + *9 F D F Y N N N Update + *10 F D D N N Y Keep + *11 F D D N N N Conflict + *12 F F D Y N Y N Update + *13 F F D N N Y N Conflict + *14 F F D N N N Conflict + *15 0 F D N N N Conflict + *16 0 D F Y N N N Update + *17 0 D F N N N Conflict + *18 F 0 D Update + *19 D 0 F Update + */ + + public void testDirectoryFileConflicts_1() throws Exception { + // 1 + doit(mk("DF/DF"), mk("DF"), mk("DF/DF")); + assertNoConflicts(); + assertUpdated("DF"); + assertRemoved("DF/DF"); + } + + public void testDirectoryFileConflicts_2() throws Exception { + // 2 + setupCase(mk("DF/DF"), mk("DF"), mk("DF/DF")); + writeTrashFile("DF/DF", "different"); + go(); + assertConflict("DF/DF"); + + } + + public void testDirectoryFileConflicts_3() throws Exception { + // 3 - the first to break! + doit(mk("DF/DF"), mk("DF/DF"), mk("DF")); + assertUpdated("DF/DF"); + assertRemoved("DF"); + } + + public void testDirectoryFileConflicts_4() throws Exception { + // 4 (basically same as 3, just with H and M different) + doit(mk("DF/DF"), mkmap("DF/DF", "foo"), mk("DF")); + assertUpdated("DF/DF"); + assertRemoved("DF"); + + } + + public void testDirectoryFileConflicts_5() throws Exception { + // 5 + doit(mk("DF/DF"), mk("DF"), mk("DF")); + assertRemoved("DF/DF"); + + } + + public void testDirectoryFileConflicts_6() throws Exception { + // 6 + setupCase(mk("DF/DF"), mk("DF"), mk("DF")); + writeTrashFile("DF", "different"); + go(); + assertRemoved("DF/DF"); + } + + public void testDirectoryFileConflicts_7() throws Exception { + // 7 + doit(mk("DF"), mk("DF"), mk("DF/DF")); + assertUpdated("DF"); + assertRemoved("DF/DF"); + + cleanUpDF(); + setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF")); + go(); + assertRemoved("DF/DF/DF/DF/DF"); + assertUpdated("DF/DF"); + + cleanUpDF(); + setupCase(mk("DF/DF"), mk("DF/DF"), mk("DF/DF/DF/DF/DF")); + writeTrashFile("DF/DF/DF/DF/DF", "diff"); + go(); + assertConflict("DF/DF/DF/DF/DF"); + assertUpdated("DF/DF"); + + } + + // 8 ? + + public void testDirectoryFileConflicts_9() throws Exception { + // 9 + doit(mk("DF"), mkmap("DF", "QP"), mk("DF/DF")); + assertRemoved("DF/DF"); + assertUpdated("DF"); + } + + public void testDirectoryFileConflicts_10() throws Exception { + // 10 + cleanUpDF(); + doit(mk("DF"), mk("DF/DF"), mk("DF/DF")); + assertNoConflicts(); + + } + + public void testDirectoryFileConflicts_11() throws Exception { + // 11 + doit(mk("DF"), mk("DF/DF"), mkmap("DF/DF", "asdf")); + assertConflict("DF/DF"); + } + + public void testDirectoryFileConflicts_12() throws Exception { + // 12 + cleanUpDF(); + doit(mk("DF"), mk("DF/DF"), mk("DF")); + assertRemoved("DF"); + assertUpdated("DF/DF"); + } + + public void testDirectoryFileConflicts_13() throws Exception { + // 13 + cleanUpDF(); + setupCase(mk("DF"), mk("DF/DF"), mk("DF")); + writeTrashFile("DF", "asdfsdf"); + go(); + assertConflict("DF"); + assertUpdated("DF/DF"); + } + + public void testDirectoryFileConflicts_14() throws Exception { + // 14 + cleanUpDF(); + doit(mk("DF"), mk("DF/DF"), mkmap("DF", "Foo")); + assertConflict("DF"); + assertUpdated("DF/DF"); + } + + public void testDirectoryFileConflicts_15() throws Exception { + // 15 + doit(mkmap(), mk("DF/DF"), mk("DF")); + assertRemoved("DF"); + assertUpdated("DF/DF"); + } + + public void testDirectoryFileConflicts_15b() throws Exception { + // 15, take 2, just to check multi-leveled + doit(mkmap(), mk("DF/DF/DF/DF"), mk("DF")); + assertRemoved("DF"); + assertUpdated("DF/DF/DF/DF"); + } + + public void testDirectoryFileConflicts_16() throws Exception { + // 16 + cleanUpDF(); + doit(mkmap(), mk("DF"), mk("DF/DF/DF")); + assertRemoved("DF/DF/DF"); + assertUpdated("DF"); + } + + public void testDirectoryFileConflicts_17() throws Exception { + // 17 + cleanUpDF(); + setupCase(mkmap(), mk("DF"), mk("DF/DF/DF")); + writeTrashFile("DF/DF/DF", "asdf"); + go(); + assertConflict("DF/DF/DF"); + assertUpdated("DF"); + } + + public void testDirectoryFileConflicts_18() throws Exception { + // 18 + cleanUpDF(); + doit(mk("DF/DF"), mk("DF/DF/DF/DF"), null); + assertRemoved("DF/DF"); + assertUpdated("DF/DF/DF/DF"); + } + + public void testDirectoryFileConflicts_19() throws Exception { + // 19 + cleanUpDF(); + doit(mk("DF/DF/DF/DF"), mk("DF/DF/DF"), null); + assertRemoved("DF/DF/DF/DF"); + assertUpdated("DF/DF/DF"); + } + + private void cleanUpDF() throws Exception { + tearDown(); + setUp(); + recursiveDelete(new File(trash, "DF")); + } + + private void assertConflict(String s) { + assertTrue(theReadTree.conflicts.contains(s)); + } + + private void assertUpdated(String s) { + assertTrue(theReadTree.updated.containsKey(s)); + } + + private void assertRemoved(String s) { + assertTrue(theReadTree.removed.contains(s)); + } + + private void assertNoConflicts() { + assertTrue(theReadTree.conflicts.isEmpty()); + } + + private void doit(HashMap<String, String> h, HashMap<String, String>m, + HashMap<String, String> i) throws IOException { + setupCase(h, m, i); + go(); + } + + private static HashMap<String, String> mk(String a) { + return mkmap(a, a); + } + + private static HashMap<String, String> mkmap(String... args) { + if ((args.length % 2) > 0) + throw new IllegalArgumentException("needs to be pairs"); + + HashMap<String, String> map = new HashMap<String, String>(); + for (int i = 0; i < args.length; i += 2) { + map.put(args[i], args[i+1]); + } + + return map; + } + + public void testUntrackedConflicts() throws IOException { + setupCase(null, mk("foo"), null); + writeTrashFile("foo", "foo"); + go(); + + assertConflict("foo"); + + recursiveDelete(new File(trash, "foo")); + setupCase(null, mk("foo"), null); + writeTrashFile("foo/bar/baz", ""); + writeTrashFile("foo/blahblah", ""); + go(); + + assertConflict("foo/bar/baz"); + assertConflict("foo/blahblah"); + + recursiveDelete(new File(trash, "foo")); + + setupCase(mkmap("foo/bar", "", "foo/baz", ""), + mk("foo"), mkmap("foo/bar", "", "foo/baz", "")); + assertTrue(new File(trash, "foo/bar").exists()); + go(); + + assertNoConflicts(); + } + + public void testCloseNameConflictsX0() throws IOException { + setupCase(mkmap("a/a", "a/a-c"), mkmap("a/a","a/a", "b.b/b.b","b.b/b.bs"), mkmap("a/a", "a/a-c") ); + checkout(); + go(); + assertNoConflicts(); + } + + public void testCloseNameConflicts1() throws IOException { + setupCase(mkmap("a/a", "a/a-c"), mkmap("a/a","a/a", "a.a/a.a","a.a/a.a"), mkmap("a/a", "a/a-c") ); + checkout(); + go(); + assertNoConflicts(); + } + + private void checkout() throws IOException { + theReadTree = new WorkDirCheckout(db, trash, theHead, theIndex, theMerge); + theReadTree.checkout(); + } + + public void testCheckoutOutChanges() throws IOException { + setupCase(mk("foo"), mk("foo/bar"), mk("foo")); + checkout(); + + assertFalse(new File(trash, "foo").isFile()); + assertTrue(new File(trash, "foo/bar").isFile()); + recursiveDelete(new File(trash, "foo")); + + setupCase(mk("foo/bar"), mk("foo"), mk("foo/bar")); + checkout(); + + assertFalse(new File(trash, "foo/bar").isFile()); + assertTrue(new File(trash, "foo").isFile()); + + setupCase(mk("foo"), mkmap("foo", "qux"), mkmap("foo", "bar")); + + try { + checkout(); + fail("did not throw exception"); + } catch (CheckoutConflictException e) { + // should have thrown + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java new file mode 100644 index 0000000000..d7b6193ce1 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2009, Robin Rosenberg + * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com> + * 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.lib; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Map; + +import org.eclipse.jgit.lib.Ref.Storage; +import org.eclipse.jgit.lib.RefUpdate.Result; + +/** + * Misc tests for refs. A lot of things are tested elsewhere so not having a + * test for a ref related method, does not mean it is untested. + */ +public class RefTest extends RepositoryTestCase { + + public void testReadAllIncludingSymrefs() throws Exception { + ObjectId masterId = db.resolve("refs/heads/master"); + RefUpdate updateRef = db.updateRef("refs/remotes/origin/master"); + updateRef.setNewObjectId(masterId); + updateRef.setForceUpdate(true); + updateRef.update(); + db + .writeSymref("refs/remotes/origin/HEAD", + "refs/remotes/origin/master"); + + ObjectId r = db.resolve("refs/remotes/origin/HEAD"); + assertEquals(masterId, r); + + Map<String, Ref> allRefs = db.getAllRefs(); + Ref refHEAD = allRefs.get("refs/remotes/origin/HEAD"); + assertNotNull(refHEAD); + assertEquals(masterId, refHEAD.getObjectId()); + assertTrue(refHEAD.isPeeled()); + assertNull(refHEAD.getPeeledObjectId()); + + Ref refmaster = allRefs.get("refs/remotes/origin/master"); + assertEquals(masterId, refmaster.getObjectId()); + assertFalse(refmaster.isPeeled()); + assertNull(refmaster.getPeeledObjectId()); + } + + public void testReadSymRefToPacked() throws IOException { + db.writeSymref("HEAD", "refs/heads/b"); + Ref ref = db.getRef("HEAD"); + assertEquals(Ref.Storage.LOOSE_PACKED, ref.getStorage()); + } + + public void testReadSymRefToLoosePacked() throws IOException { + ObjectId pid = db.resolve("refs/heads/master^"); + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(pid); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); // internal + + db.writeSymref("HEAD", "refs/heads/master"); + Ref ref = db.getRef("HEAD"); + assertEquals(Ref.Storage.LOOSE_PACKED, ref.getStorage()); + } + + public void testReadLooseRef() throws IOException { + RefUpdate updateRef = db.updateRef("ref/heads/new"); + updateRef.setNewObjectId(db.resolve("refs/heads/master")); + Result update = updateRef.update(); + assertEquals(Result.NEW, update); + Ref ref = db.getRef("ref/heads/new"); + assertEquals(Storage.LOOSE, ref.getStorage()); + } + + /** + * Let an "outsider" create a loose ref with the same name as a packed one + * + * @throws IOException + * @throws InterruptedException + */ + public void testReadLoosePackedRef() throws IOException, + InterruptedException { + Ref ref = db.getRef("refs/heads/master"); + assertEquals(Storage.PACKED, ref.getStorage()); + FileOutputStream os = new FileOutputStream(new File(db.getDirectory(), + "refs/heads/master")); + os.write(ref.getObjectId().name().getBytes()); + os.write('\n'); + os.close(); + + ref = db.getRef("refs/heads/master"); + assertEquals(Storage.LOOSE_PACKED, ref.getStorage()); + } + + /** + * Modify a packed ref using the API. This creates a loose ref too, ie. + * LOOSE_PACKED + * + * @throws IOException + */ + public void testReadSimplePackedRefSameRepo() throws IOException { + Ref ref = db.getRef("refs/heads/master"); + ObjectId pid = db.resolve("refs/heads/master^"); + assertEquals(Storage.PACKED, ref.getStorage()); + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(pid); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); + + ref = db.getRef("refs/heads/master"); + assertEquals(Storage.LOOSE_PACKED, ref.getStorage()); + } + + public void testOrigResolvedNamesBranch() throws IOException { + Ref ref = db.getRef("a"); + assertEquals("refs/heads/a", ref.getName()); + assertEquals("refs/heads/a", ref.getOrigName()); + } + + public void testOrigResolvedNamesSymRef() throws IOException { + Ref ref = db.getRef("HEAD"); + assertEquals("refs/heads/master", ref.getName()); + assertEquals("HEAD", ref.getOrigName()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java new file mode 100644 index 0000000000..3704c56173 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefUpdateTest.java @@ -0,0 +1,768 @@ +/* + * Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org> + * Copyright (C) 2008-2009, Robin Rosenberg <robin.rosenberg@dewire.com> + * 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.lib; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.jgit.lib.RefUpdate.Result; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; + +public class RefUpdateTest extends RepositoryTestCase { + + private RefUpdate updateRef(final String name) throws IOException { + final RefUpdate ref = db.updateRef(name); + ref.setNewObjectId(db.resolve(Constants.HEAD)); + return ref; + } + + private void delete(final RefUpdate ref, final Result expected) + throws IOException { + delete(ref, expected, true, true); + } + + private void delete(final RefUpdate ref, final Result expected, + final boolean exists, final boolean removed) throws IOException { + assertEquals(exists, db.getAllRefs().containsKey(ref.getName())); + assertEquals(expected, ref.delete()); + assertEquals(!removed, db.getAllRefs().containsKey(ref.getName())); + } + + public void testNoCacheObjectIdSubclass() throws IOException { + final String newRef = "refs/heads/abc"; + final RefUpdate ru = updateRef(newRef); + final RevCommit newid = new RevCommit(ru.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid); + Result update = ru.update(); + assertEquals(Result.NEW, update); + final Ref r = db.getAllRefs().get(newRef); + assertNotNull(r); + assertEquals(newRef, r.getName()); + assertNotNull(r.getObjectId()); + assertNotSame(newid, r.getObjectId()); + assertSame(ObjectId.class, r.getObjectId().getClass()); + assertEquals(newid.copy(), r.getObjectId()); + List<org.eclipse.jgit.lib.ReflogReader.Entry> reverseEntries1 = db.getReflogReader("refs/heads/abc").getReverseEntries(); + org.eclipse.jgit.lib.ReflogReader.Entry entry1 = reverseEntries1.get(0); + assertEquals(1, reverseEntries1.size()); + assertEquals(ObjectId.zeroId(), entry1.getOldId()); + assertEquals(r.getObjectId(), entry1.getNewId()); + assertEquals(new PersonIdent(db).toString(), entry1.getWho().toString()); + assertEquals("", entry1.getComment()); + List<org.eclipse.jgit.lib.ReflogReader.Entry> reverseEntries2 = db.getReflogReader("HEAD").getReverseEntries(); + assertEquals(0, reverseEntries2.size()); + } + + public void testNewNamespaceConflictWithLoosePrefixNameExists() + throws IOException { + final String newRef = "refs/heads/z"; + final RefUpdate ru = updateRef(newRef); + final RevCommit newid = new RevCommit(ru.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid); + Result update = ru.update(); + assertEquals(Result.NEW, update); + // end setup + final String newRef2 = "refs/heads/z/a"; + final RefUpdate ru2 = updateRef(newRef2); + final RevCommit newid2 = new RevCommit(ru2.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid2); + Result update2 = ru2.update(); + assertEquals(Result.LOCK_FAILURE, update2); + assertEquals(1, db.getReflogReader("refs/heads/z").getReverseEntries().size()); + assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); + } + + public void testNewNamespaceConflictWithPackedPrefixNameExists() + throws IOException { + final String newRef = "refs/heads/master/x"; + final RefUpdate ru = updateRef(newRef); + final RevCommit newid = new RevCommit(ru.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid); + Result update = ru.update(); + assertEquals(Result.LOCK_FAILURE, update); + assertNull(db.getReflogReader("refs/heads/master/x")); + assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); + } + + public void testNewNamespaceConflictWithLoosePrefixOfExisting() + throws IOException { + final String newRef = "refs/heads/z/a"; + final RefUpdate ru = updateRef(newRef); + final RevCommit newid = new RevCommit(ru.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid); + Result update = ru.update(); + assertEquals(Result.NEW, update); + // end setup + final String newRef2 = "refs/heads/z"; + final RefUpdate ru2 = updateRef(newRef2); + final RevCommit newid2 = new RevCommit(ru2.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid2); + Result update2 = ru2.update(); + assertEquals(Result.LOCK_FAILURE, update2); + assertEquals(1, db.getReflogReader("refs/heads/z/a").getReverseEntries().size()); + assertNull(db.getReflogReader("refs/heads/z")); + assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); + } + + public void testNewNamespaceConflictWithPackedPrefixOfExisting() + throws IOException { + final String newRef = "refs/heads/prefix"; + final RefUpdate ru = updateRef(newRef); + final RevCommit newid = new RevCommit(ru.getNewObjectId()) { + // empty + }; + ru.setNewObjectId(newid); + Result update = ru.update(); + assertEquals(Result.LOCK_FAILURE, update); + assertNull(db.getReflogReader("refs/heads/prefix")); + assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); + } + + /** + * Delete a ref that is pointed to by HEAD + * + * @throws IOException + */ + public void testDeleteHEADreferencedRef() throws IOException { + ObjectId pid = db.resolve("refs/heads/master^"); + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(pid); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); // internal + + RefUpdate updateRef2 = db.updateRef("refs/heads/master"); + Result delete = updateRef2.delete(); + assertEquals(Result.REJECTED_CURRENT_BRANCH, delete); + assertEquals(pid, db.resolve("refs/heads/master")); + assertEquals(1,db.getReflogReader("refs/heads/master").getReverseEntries().size()); + assertEquals(0,db.getReflogReader("HEAD").getReverseEntries().size()); + } + + public void testLooseDelete() throws IOException { + final String newRef = "refs/heads/abc"; + RefUpdate ref = updateRef(newRef); + ref.update(); // create loose ref + ref = updateRef(newRef); // refresh + delete(ref, Result.NO_CHANGE); + assertNull(db.getReflogReader("refs/heads/abc")); + } + + public void testDeleteHead() throws IOException { + final RefUpdate ref = updateRef(Constants.HEAD); + delete(ref, Result.REJECTED_CURRENT_BRANCH, true, false); + assertEquals(0, db.getReflogReader("refs/heads/master").getReverseEntries().size()); + assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); + } + + /** + * Delete a loose ref and make sure the directory in refs is deleted too, + * and the reflog dir too + * + * @throws IOException + */ + public void testDeleteLooseAndItsDirectory() throws IOException { + ObjectId pid = db.resolve("refs/heads/c^"); + RefUpdate updateRef = db.updateRef("refs/heads/z/c"); + updateRef.setNewObjectId(pid); + updateRef.setForceUpdate(true); + updateRef.setRefLogMessage("new test ref", false); + Result update = updateRef.update(); + assertEquals(Result.NEW, update); // internal + assertTrue(new File(db.getDirectory(), Constants.R_HEADS + "z") + .exists()); + assertTrue(new File(db.getDirectory(), "logs/refs/heads/z").exists()); + + // The real test here + RefUpdate updateRef2 = db.updateRef("refs/heads/z/c"); + updateRef2.setForceUpdate(true); + Result delete = updateRef2.delete(); + assertEquals(Result.FORCED, delete); + assertNull(db.resolve("refs/heads/z/c")); + assertFalse(new File(db.getDirectory(), Constants.R_HEADS + "z") + .exists()); + assertFalse(new File(db.getDirectory(), "logs/refs/heads/z").exists()); + } + + public void testDeleteNotFound() throws IOException { + final RefUpdate ref = updateRef("refs/heads/xyz"); + delete(ref, Result.NEW, false, true); + } + + public void testDeleteFastForward() throws IOException { + final RefUpdate ref = updateRef("refs/heads/a"); + delete(ref, Result.FAST_FORWARD); + } + + public void testDeleteForce() throws IOException { + final RefUpdate ref = db.updateRef("refs/heads/b"); + ref.setNewObjectId(db.resolve("refs/heads/a")); + delete(ref, Result.REJECTED, true, false); + ref.setForceUpdate(true); + delete(ref, Result.FORCED); + } + + public void testRefKeySameAsOrigName() { + Map<String, Ref> allRefs = db.getAllRefs(); + for (Entry<String, Ref> e : allRefs.entrySet()) { + assertEquals(e.getKey(), e.getValue().getOrigName()); + + } + } + + /** + * Try modify a ref forward, fast forward + * + * @throws IOException + */ + public void testUpdateRefForward() throws IOException { + ObjectId ppid = db.resolve("refs/heads/master^"); + ObjectId pid = db.resolve("refs/heads/master"); + + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(ppid); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); + assertEquals(ppid, db.resolve("refs/heads/master")); + + // real test + RefUpdate updateRef2 = db.updateRef("refs/heads/master"); + updateRef2.setNewObjectId(pid); + Result update2 = updateRef2.update(); + assertEquals(Result.FAST_FORWARD, update2); + assertEquals(pid, db.resolve("refs/heads/master")); + } + + /** + * Delete a ref that exists both as packed and loose. Make sure the ref + * cannot be resolved after delete. + * + * @throws IOException + */ + public void testDeleteLoosePacked() throws IOException { + ObjectId pid = db.resolve("refs/heads/c^"); + RefUpdate updateRef = db.updateRef("refs/heads/c"); + updateRef.setNewObjectId(pid); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); // internal + + // The real test here + RefUpdate updateRef2 = db.updateRef("refs/heads/c"); + updateRef2.setForceUpdate(true); + Result delete = updateRef2.delete(); + assertEquals(Result.FORCED, delete); + assertNull(db.resolve("refs/heads/c")); + } + + /** + * Try modify a ref to same + * + * @throws IOException + */ + public void testUpdateRefNoChange() throws IOException { + ObjectId pid = db.resolve("refs/heads/master"); + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(pid); + Result update = updateRef.update(); + assertEquals(Result.NO_CHANGE, update); + assertEquals(pid, db.resolve("refs/heads/master")); + } + + /** + * Test case originating from + * <a href="http://bugs.eclipse.org/285991">bug 285991</a> + * + * Make sure the in memory cache is updated properly after + * update of symref. This one did not fail because the + * ref was packed due to implementation issues. + * + * @throws Exception + */ + public void testRefsCacheAfterUpdate() throws Exception { + // Do not use the defalt repo for this case. + Map<String, Ref> allRefs = db.getAllRefs(); + ObjectId oldValue = db.resolve("HEAD"); + ObjectId newValue = db.resolve("HEAD^"); + // first make HEAD refer to loose ref + RefUpdate updateRef = db.updateRef(Constants.HEAD); + updateRef.setForceUpdate(true); + updateRef.setNewObjectId(newValue); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); + + // now update that ref + updateRef = db.updateRef(Constants.HEAD); + updateRef.setForceUpdate(true); + updateRef.setNewObjectId(oldValue); + update = updateRef.update(); + assertEquals(Result.FAST_FORWARD, update); + allRefs = db.getAllRefs(); + assertEquals("refs/heads/master", allRefs.get("refs/heads/master").getName()); + assertEquals("refs/heads/master", allRefs.get("refs/heads/master").getOrigName()); + assertEquals("refs/heads/master", allRefs.get("HEAD").getName()); + assertEquals("HEAD", allRefs.get("HEAD").getOrigName()); + } + + /** + * Test case originating from + * <a href="http://bugs.eclipse.org/285991">bug 285991</a> + * + * Make sure the in memory cache is updated properly after + * update of symref. + * + * @throws Exception + */ + public void testRefsCacheAfterUpdateLoosOnly() throws Exception { + // Do not use the defalt repo for this case. + Map<String, Ref> allRefs = db.getAllRefs(); + ObjectId oldValue = db.resolve("HEAD"); + db.writeSymref(Constants.HEAD, "refs/heads/newref"); + RefUpdate updateRef = db.updateRef(Constants.HEAD); + updateRef.setForceUpdate(true); + updateRef.setNewObjectId(oldValue); + Result update = updateRef.update(); + assertEquals(Result.NEW, update); + allRefs = db.getAllRefs(); + assertEquals("refs/heads/newref", allRefs.get("HEAD").getName()); + assertEquals("HEAD", allRefs.get("HEAD").getOrigName()); + assertEquals("refs/heads/newref", allRefs.get("refs/heads/newref").getName()); + assertEquals("refs/heads/newref", allRefs.get("refs/heads/newref").getOrigName()); + } + + /** + * Try modify a ref, but get wrong expected old value + * + * @throws IOException + */ + public void testUpdateRefLockFailureWrongOldValue() throws IOException { + ObjectId pid = db.resolve("refs/heads/master"); + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(pid); + updateRef.setExpectedOldObjectId(db.resolve("refs/heads/master^")); + Result update = updateRef.update(); + assertEquals(Result.LOCK_FAILURE, update); + assertEquals(pid, db.resolve("refs/heads/master")); + } + + /** + * Try modify a ref forward, fast forward, checking old value first + * + * @throws IOException + */ + public void testUpdateRefForwardWithCheck1() throws IOException { + ObjectId ppid = db.resolve("refs/heads/master^"); + ObjectId pid = db.resolve("refs/heads/master"); + + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(ppid); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); + assertEquals(ppid, db.resolve("refs/heads/master")); + + // real test + RefUpdate updateRef2 = db.updateRef("refs/heads/master"); + updateRef2.setExpectedOldObjectId(ppid); + updateRef2.setNewObjectId(pid); + Result update2 = updateRef2.update(); + assertEquals(Result.FAST_FORWARD, update2); + assertEquals(pid, db.resolve("refs/heads/master")); + } + + /** + * Try modify a ref forward, fast forward, checking old commit first + * + * @throws IOException + */ + public void testUpdateRefForwardWithCheck2() throws IOException { + ObjectId ppid = db.resolve("refs/heads/master^"); + ObjectId pid = db.resolve("refs/heads/master"); + + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(ppid); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals(Result.FORCED, update); + assertEquals(ppid, db.resolve("refs/heads/master")); + + // real test + RevCommit old = new RevWalk(db).parseCommit(ppid); + RefUpdate updateRef2 = db.updateRef("refs/heads/master"); + updateRef2.setExpectedOldObjectId(old); + updateRef2.setNewObjectId(pid); + Result update2 = updateRef2.update(); + assertEquals(Result.FAST_FORWARD, update2); + assertEquals(pid, db.resolve("refs/heads/master")); + } + + /** + * Try modify a ref that is locked + * + * @throws IOException + */ + public void testUpdateRefLockFailureLocked() throws IOException { + ObjectId opid = db.resolve("refs/heads/master"); + ObjectId pid = db.resolve("refs/heads/master^"); + RefUpdate updateRef = db.updateRef("refs/heads/master"); + updateRef.setNewObjectId(pid); + LockFile lockFile1 = new LockFile(new File(db.getDirectory(),"refs/heads/master")); + try { + assertTrue(lockFile1.lock()); // precondition to test + Result update = updateRef.update(); + assertEquals(Result.LOCK_FAILURE, update); + assertEquals(opid, db.resolve("refs/heads/master")); + LockFile lockFile2 = new LockFile(new File(db.getDirectory(),"refs/heads/master")); + assertFalse(lockFile2.lock()); // was locked, still is + } finally { + lockFile1.unlock(); + } + } + + /** + * Try to delete a ref. Delete requires force. + * + * @throws IOException + */ + public void testDeleteLoosePackedRejected() throws IOException { + ObjectId pid = db.resolve("refs/heads/c^"); + ObjectId oldpid = db.resolve("refs/heads/c"); + RefUpdate updateRef = db.updateRef("refs/heads/c"); + updateRef.setNewObjectId(pid); + Result update = updateRef.update(); + assertEquals(Result.REJECTED, update); + assertEquals(oldpid, db.resolve("refs/heads/c")); + } + + public void testRenameBranchNoPreviousLog() throws IOException { + assertFalse("precondition, no log on old branchg", new File(db + .getDirectory(), "logs/refs/heads/b").exists()); + ObjectId rb = db.resolve("refs/heads/b"); + ObjectId oldHead = db.resolve(Constants.HEAD); + assertFalse(rb.equals(oldHead)); // assumption for this test + RefRename renameRef = db.renameRef("refs/heads/b", + "refs/heads/new/name"); + Result result = renameRef.rename(); + assertEquals(Result.RENAMED, result); + assertEquals(rb, db.resolve("refs/heads/new/name")); + assertNull(db.resolve("refs/heads/b")); + assertEquals(1, db.getReflogReader("new/name").getReverseEntries().size()); + assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name") + .getLastEntry().getComment()); + assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists()); + assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged + } + + public void testRenameBranchHasPreviousLog() throws IOException { + ObjectId rb = db.resolve("refs/heads/b"); + ObjectId oldHead = db.resolve(Constants.HEAD); + assertFalse("precondition for this test, branch b != HEAD", rb + .equals(oldHead)); + RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b"); + assertTrue("no log on old branch", new File(db.getDirectory(), + "logs/refs/heads/b").exists()); + RefRename renameRef = db.renameRef("refs/heads/b", + "refs/heads/new/name"); + Result result = renameRef.rename(); + assertEquals(Result.RENAMED, result); + assertEquals(rb, db.resolve("refs/heads/new/name")); + assertNull(db.resolve("refs/heads/b")); + assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size()); + assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name") + .getLastEntry().getComment()); + assertEquals("Just a message", db.getReflogReader("new/name") + .getReverseEntries().get(1).getComment()); + assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists()); + assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged + } + + public void testRenameCurrentBranch() throws IOException { + ObjectId rb = db.resolve("refs/heads/b"); + db.writeSymref(Constants.HEAD, "refs/heads/b"); + ObjectId oldHead = db.resolve(Constants.HEAD); + assertTrue("internal test condition, b == HEAD", rb.equals(oldHead)); + RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b"); + assertTrue("no log on old branch", new File(db.getDirectory(), + "logs/refs/heads/b").exists()); + RefRename renameRef = db.renameRef("refs/heads/b", + "refs/heads/new/name"); + Result result = renameRef.rename(); + assertEquals(Result.RENAMED, result); + assertEquals(rb, db.resolve("refs/heads/new/name")); + assertNull(db.resolve("refs/heads/b")); + assertEquals("Branch: renamed b to new/name", db.getReflogReader( + "new/name").getLastEntry().getComment()); + assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists()); + assertEquals(rb, db.resolve(Constants.HEAD)); + assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size()); + assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name").getReverseEntries().get(0).getComment()); + assertEquals("Just a message", db.getReflogReader("new/name").getReverseEntries().get(1).getComment()); + } + + public void testRenameBranchAlsoInPack() throws IOException { + ObjectId rb = db.resolve("refs/heads/b"); + ObjectId rb2 = db.resolve("refs/heads/b~1"); + assertEquals(Ref.Storage.PACKED, db.getRef("refs/heads/b").getStorage()); + RefUpdate updateRef = db.updateRef("refs/heads/b"); + updateRef.setNewObjectId(rb2); + updateRef.setForceUpdate(true); + Result update = updateRef.update(); + assertEquals("internal check new ref is loose", Result.FORCED, update); + assertEquals(Ref.Storage.LOOSE_PACKED, db.getRef("refs/heads/b") + .getStorage()); + RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/b"); + assertTrue("no log on old branch", new File(db.getDirectory(), + "logs/refs/heads/b").exists()); + RefRename renameRef = db.renameRef("refs/heads/b", + "refs/heads/new/name"); + Result result = renameRef.rename(); + assertEquals(Result.RENAMED, result); + assertEquals(rb2, db.resolve("refs/heads/new/name")); + assertNull(db.resolve("refs/heads/b")); + assertEquals("Branch: renamed b to new/name", db.getReflogReader( + "new/name").getLastEntry().getComment()); + assertEquals(3, db.getReflogReader("refs/heads/new/name").getReverseEntries().size()); + assertEquals("Branch: renamed b to new/name", db.getReflogReader("refs/heads/new/name").getReverseEntries().get(0).getComment()); + assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size()); + // make sure b's log file is gone too. + assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists()); + + // Create new Repository instance, to reread caches and make sure our + // assumptions are persistent. + Repository ndb = new Repository(db.getDirectory()); + assertEquals(rb2, ndb.resolve("refs/heads/new/name")); + assertNull(ndb.resolve("refs/heads/b")); + } + + public void tryRenameWhenLocked(String toLock, String fromName, + String toName, String headPointsTo) throws IOException { + // setup + db.writeSymref(Constants.HEAD, headPointsTo); + ObjectId oldfromId = db.resolve(fromName); + ObjectId oldHeadId = db.resolve(Constants.HEAD); + RefLogWriter.writeReflog(db, oldfromId, oldfromId, "Just a message", + fromName); + List<org.eclipse.jgit.lib.ReflogReader.Entry> oldFromLog = db + .getReflogReader(fromName).getReverseEntries(); + List<org.eclipse.jgit.lib.ReflogReader.Entry> oldHeadLog = oldHeadId != null ? db + .getReflogReader(Constants.HEAD).getReverseEntries() : null; + + assertTrue("internal check, we have a log", new File(db.getDirectory(), + "logs/" + fromName).exists()); + + // "someone" has branch X locked + LockFile lockFile = new LockFile(new File(db.getDirectory(), toLock)); + try { + assertTrue(lockFile.lock()); + + // Now this is our test + RefRename renameRef = db.renameRef(fromName, toName); + Result result = renameRef.rename(); + assertEquals(Result.LOCK_FAILURE, result); + + // Check that the involved refs are the same despite the failure + assertExists(false, toName); + if (!toLock.equals(toName)) + assertExists(false, toName + ".lock"); + assertExists(true, toLock + ".lock"); + if (!toLock.equals(fromName)) + assertExists(false, "logs/" + fromName + ".lock"); + assertExists(false, "logs/" + toName + ".lock"); + assertEquals(oldHeadId, db.resolve(Constants.HEAD)); + assertEquals(oldfromId, db.resolve(fromName)); + assertNull(db.resolve(toName)); + assertEquals(oldFromLog.toString(), db.getReflogReader(fromName) + .getReverseEntries().toString()); + if (oldHeadId != null) + assertEquals(oldHeadLog, db.getReflogReader(Constants.HEAD) + .getReverseEntries()); + } finally { + lockFile.unlock(); + } + } + + private void assertExists(boolean positive, String toName) { + assertEquals(toName + (positive ? " " : " does not ") + "exist", + positive, new File(db.getDirectory(), toName).exists()); + } + + public void testRenameBranchCannotLockAFileHEADisFromLockHEAD() + throws IOException { + tryRenameWhenLocked("HEAD", "refs/heads/b", "refs/heads/new/name", + "refs/heads/b"); + } + + public void testRenameBranchCannotLockAFileHEADisFromLockFrom() + throws IOException { + tryRenameWhenLocked("refs/heads/b", "refs/heads/b", + "refs/heads/new/name", "refs/heads/b"); + } + + public void testRenameBranchCannotLockAFileHEADisFromLockTo() + throws IOException { + tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b", + "refs/heads/new/name", "refs/heads/b"); + } + + public void testRenameBranchCannotLockAFileHEADisToLockFrom() + throws IOException { + tryRenameWhenLocked("refs/heads/b", "refs/heads/b", + "refs/heads/new/name", "refs/heads/new/name"); + } + + public void testRenameBranchCannotLockAFileHEADisToLockTo() + throws IOException { + tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b", + "refs/heads/new/name", "refs/heads/new/name"); + } + + public void testRenameBranchCannotLockAFileHEADisToLockTmp() + throws IOException { + tryRenameWhenLocked("RENAMED-REF.." + Thread.currentThread().getId(), + "refs/heads/b", "refs/heads/new/name", "refs/heads/new/name"); + } + + public void testRenameBranchCannotLockAFileHEADisOtherLockFrom() + throws IOException { + tryRenameWhenLocked("refs/heads/b", "refs/heads/b", + "refs/heads/new/name", "refs/heads/a"); + } + + public void testRenameBranchCannotLockAFileHEADisOtherLockTo() + throws IOException { + tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b", + "refs/heads/new/name", "refs/heads/a"); + } + + public void testRenameBranchCannotLockAFileHEADisOtherLockTmp() + throws IOException { + tryRenameWhenLocked("RENAMED-REF.." + Thread.currentThread().getId(), + "refs/heads/b", "refs/heads/new/name", "refs/heads/a"); + } + + public void testRenameRefNameColission1avoided() throws IOException { + // setup + ObjectId rb = db.resolve("refs/heads/b"); + db.writeSymref(Constants.HEAD, "refs/heads/a"); + RefUpdate updateRef = db.updateRef("refs/heads/a"); + updateRef.setNewObjectId(rb); + updateRef.setRefLogMessage("Setup", false); + assertEquals(Result.FAST_FORWARD, updateRef.update()); + ObjectId oldHead = db.resolve(Constants.HEAD); + assertTrue(rb.equals(oldHead)); // assumption for this test + RefLogWriter.writeReflog(db, rb, rb, "Just a message", "refs/heads/a"); + assertTrue("internal check, we have a log", new File(db.getDirectory(), + "logs/refs/heads/a").exists()); + + // Now this is our test + RefRename renameRef = db.renameRef("refs/heads/a", "refs/heads/a/b"); + Result result = renameRef.rename(); + assertEquals(Result.RENAMED, result); + assertNull(db.resolve("refs/heads/a")); + assertEquals(rb, db.resolve("refs/heads/a/b")); + assertEquals(3, db.getReflogReader("a/b").getReverseEntries().size()); + assertEquals("Branch: renamed a to a/b", db.getReflogReader("a/b") + .getReverseEntries().get(0).getComment()); + assertEquals("Just a message", db.getReflogReader("a/b") + .getReverseEntries().get(1).getComment()); + assertEquals("Setup", db.getReflogReader("a/b").getReverseEntries() + .get(2).getComment()); + // same thing was logged to HEAD + assertEquals("Branch: renamed a to a/b", db.getReflogReader("HEAD") + .getReverseEntries().get(0).getComment()); + } + + public void testRenameRefNameColission2avoided() throws IOException { + // setup + ObjectId rb = db.resolve("refs/heads/b"); + db.writeSymref(Constants.HEAD, "refs/heads/prefix/a"); + RefUpdate updateRef = db.updateRef("refs/heads/prefix/a"); + updateRef.setNewObjectId(rb); + updateRef.setRefLogMessage("Setup", false); + updateRef.setForceUpdate(true); + assertEquals(Result.FORCED, updateRef.update()); + ObjectId oldHead = db.resolve(Constants.HEAD); + assertTrue(rb.equals(oldHead)); // assumption for this test + RefLogWriter.writeReflog(db, rb, rb, "Just a message", + "refs/heads/prefix/a"); + assertTrue("internal check, we have a log", new File(db.getDirectory(), + "logs/refs/heads/prefix/a").exists()); + + // Now this is our test + RefRename renameRef = db.renameRef("refs/heads/prefix/a", + "refs/heads/prefix"); + Result result = renameRef.rename(); + assertEquals(Result.RENAMED, result); + + assertNull(db.resolve("refs/heads/prefix/a")); + assertEquals(rb, db.resolve("refs/heads/prefix")); + assertEquals(3, db.getReflogReader("prefix").getReverseEntries().size()); + assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader( + "prefix").getReverseEntries().get(0).getComment()); + assertEquals("Just a message", db.getReflogReader("prefix") + .getReverseEntries().get(1).getComment()); + assertEquals("Setup", db.getReflogReader("prefix").getReverseEntries() + .get(2).getComment()); + assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader( + "HEAD").getReverseEntries().get(0).getComment()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java new file mode 100644 index 0000000000..dae7cb8955 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogReaderTest.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2009, Robin Rosenberg + * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com> + * 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.lib; + +import java.text.SimpleDateFormat; +import java.util.List; + +import org.eclipse.jgit.lib.ReflogReader.Entry; + +public class ReflogReaderTest extends RepositoryTestCase { + + static byte[] oneLine = "da85355dfc525c9f6f3927b876f379f46ccf826e 3e7549db262d1e836d9bf0af7e22355468f1717c A O Thor Too <authortoo@wri.tr> 1243028200 +0200\tcommit: Add a toString for debugging to RemoteRefUpdate\n" + .getBytes(); + + static byte[] twoLine = ("0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\tbranch: Created from rr/renamebranchv4\n" + + "c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\n") + .getBytes(); + + static byte[] twoLineWithAppendInProgress = ("0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\tbranch: Created from rr/renamebranchv4\n" + + "c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\n" + + "54794942a18a237c57a80719afed44bb78172b10 ") + .getBytes(); + + static byte[] aLine = "1111111111111111111111111111111111111111 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\tbranch: change to a\n" + .getBytes(); + + static byte[] masterLine = "2222222222222222222222222222222222222222 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\tbranch: change to master\n" + .getBytes(); + + static byte[] headLine = "3333333333333333333333333333333333333333 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\tbranch: change to HEAD\n" + .getBytes(); + + public void testReadOneLine() throws Exception { + setupReflog("logs/refs/heads/master", oneLine); + + ReflogReader reader = new ReflogReader(db, "refs/heads/master"); + Entry e = reader.getLastEntry(); + assertEquals(ObjectId + .fromString("da85355dfc525c9f6f3927b876f379f46ccf826e"), e + .getOldId()); + assertEquals(ObjectId + .fromString("3e7549db262d1e836d9bf0af7e22355468f1717c"), e + .getNewId()); + assertEquals("A O Thor Too", e.getWho().getName()); + assertEquals("authortoo@wri.tr", e.getWho().getEmailAddress()); + assertEquals(120, e.getWho().getTimeZoneOffset()); + assertEquals("2009-05-22T23:36:40", iso(e.getWho())); + assertEquals("commit: Add a toString for debugging to RemoteRefUpdate", + e.getComment()); + } + + private String iso(PersonIdent id) { + final SimpleDateFormat fmt; + fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + fmt.setTimeZone(id.getTimeZone()); + return fmt.format(id.getWhen()); + } + + public void testReadTwoLine() throws Exception { + setupReflog("logs/refs/heads/master", twoLine); + + ReflogReader reader = new ReflogReader(db, "refs/heads/master"); + List<Entry> reverseEntries = reader.getReverseEntries(); + assertEquals(2, reverseEntries.size()); + Entry e = reverseEntries.get(0); + assertEquals(ObjectId + .fromString("c6734895958052a9dbc396cff4459dc1a25029ab"), e + .getOldId()); + assertEquals(ObjectId + .fromString("54794942a18a237c57a80719afed44bb78172b10"), e + .getNewId()); + assertEquals("Same A U Thor", e.getWho().getName()); + assertEquals("same.author@example.com", e.getWho().getEmailAddress()); + assertEquals(60, e.getWho().getTimeZoneOffset()); + assertEquals("2009-05-22T22:36:42", iso(e.getWho())); + assertEquals( + "rebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d", + e.getComment()); + + e = reverseEntries.get(1); + assertEquals(ObjectId + .fromString("0000000000000000000000000000000000000000"), e + .getOldId()); + assertEquals(ObjectId + .fromString("c6734895958052a9dbc396cff4459dc1a25029ab"), e + .getNewId()); + assertEquals("A U Thor", e.getWho().getName()); + assertEquals("thor@committer.au", e.getWho().getEmailAddress()); + assertEquals(-60, e.getWho().getTimeZoneOffset()); + assertEquals("2009-05-22T20:36:41", iso(e.getWho())); + assertEquals("branch: Created from rr/renamebranchv4", e.getComment()); + } + + public void testReadWhileAppendIsInProgress() throws Exception { + setupReflog("logs/refs/heads/master", twoLineWithAppendInProgress); + ReflogReader reader = new ReflogReader(db, "refs/heads/master"); + List<Entry> reverseEntries = reader.getReverseEntries(); + assertEquals(2, reverseEntries.size()); + Entry e = reverseEntries.get(0); + assertEquals(ObjectId + .fromString("c6734895958052a9dbc396cff4459dc1a25029ab"), e + .getOldId()); + assertEquals(ObjectId + .fromString("54794942a18a237c57a80719afed44bb78172b10"), e + .getNewId()); + assertEquals("Same A U Thor", e.getWho().getName()); + assertEquals("same.author@example.com", e.getWho().getEmailAddress()); + assertEquals(60, e.getWho().getTimeZoneOffset()); + assertEquals("2009-05-22T22:36:42", iso(e.getWho())); + assertEquals( + "rebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d", + e.getComment()); + // while similar to testReadTwoLine, we can assume that if we get the last entry + // right, everything else is too + } + + + public void testReadRightLog() throws Exception { + setupReflog("logs/refs/heads/a", aLine); + setupReflog("logs/refs/heads/master", masterLine); + setupReflog("logs/HEAD", headLine); + assertEquals("branch: change to master", db.getReflogReader("master") + .getLastEntry().getComment()); + assertEquals("branch: change to a", db.getReflogReader("a") + .getLastEntry().getComment()); + assertEquals("branch: change to HEAD", db.getReflogReader("HEAD") + .getLastEntry().getComment()); + } + + public void testNoLog() throws Exception { + assertEquals(0, db.getReflogReader("master").getReverseEntries().size()); + assertNull(db.getReflogReader("master").getLastEntry()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java new file mode 100644 index 0000000000..c0591755f4 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 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.lib; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.RepositoryCache.FileKey; + +public class RepositoryCacheTest extends RepositoryTestCase { + public void testNonBareFileKey() { + File gitdir = db.getDirectory(); + File parent = gitdir.getParentFile(); + File other = new File(parent, "notagit"); + assertEquals(gitdir, FileKey.exact(gitdir).getFile()); + assertEquals(parent, FileKey.exact(parent).getFile()); + assertEquals(other, FileKey.exact(other).getFile()); + + assertEquals(gitdir, FileKey.lenient(gitdir).getFile()); + assertEquals(gitdir, FileKey.lenient(parent).getFile()); + assertEquals(other, FileKey.lenient(other).getFile()); + } + + public void testBareFileKey() throws IOException { + Repository bare = createNewEmptyRepo(true); + File gitdir = bare.getDirectory(); + File parent = gitdir.getParentFile(); + String name = gitdir.getName(); + assertTrue(name.endsWith(".git")); + name = name.substring(0, name.length() - 4); + + assertEquals(gitdir, FileKey.exact(gitdir).getFile()); + + assertEquals(gitdir, FileKey.lenient(gitdir).getFile()); + assertEquals(gitdir, FileKey.lenient(new File(parent, name)).getFile()); + } + + public void testFileKeyOpenExisting() throws IOException { + Repository r; + + r = new FileKey(db.getDirectory()).open(true); + assertNotNull(r); + assertEquals(db.getDirectory(), r.getDirectory()); + r.close(); + + r = new FileKey(db.getDirectory()).open(false); + assertNotNull(r); + assertEquals(db.getDirectory(), r.getDirectory()); + r.close(); + } + + public void testFileKeyOpenNew() throws IOException { + final Repository n = createNewEmptyRepo(true); + final File gitdir = n.getDirectory(); + n.close(); + recursiveDelete(gitdir); + assertFalse(gitdir.exists()); + + try { + new FileKey(gitdir).open(true); + fail("incorrectly opened a non existant repository"); + } catch (RepositoryNotFoundException e) { + assertEquals("repository not found: " + gitdir, e.getMessage()); + } + + final Repository o = new FileKey(gitdir).open(false); + assertNotNull(o); + assertEquals(gitdir, o.getDirectory()); + assertFalse(gitdir.exists()); + } + + public void testCacheRegisterOpen() throws Exception { + final File dir = db.getDirectory(); + RepositoryCache.register(db); + assertSame(db, RepositoryCache.open(FileKey.exact(dir))); + + assertEquals(".git", dir.getName()); + final File parent = dir.getParentFile(); + assertSame(db, RepositoryCache.open(FileKey.lenient(parent))); + } + + public void testCacheOpen() throws Exception { + final FileKey loc = FileKey.exact(db.getDirectory()); + final Repository d2 = RepositoryCache.open(loc); + assertNotSame(db, d2); + assertSame(d2, RepositoryCache.open(FileKey.exact(loc.getFile()))); + d2.close(); + d2.close(); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryConfigTest.java new file mode 100644 index 0000000000..08e701ab50 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryConfigTest.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com> + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> + * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.util.Arrays; +import java.util.LinkedList; + +import junit.framework.TestCase; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.util.SystemReader; + +/** + * Test reading of git config + */ +public class RepositoryConfigTest extends TestCase { + public void test001_ReadBareKey() throws ConfigInvalidException { + final Config c = parse("[foo]\nbar\n"); + assertEquals(true, c.getBoolean("foo", null, "bar", false)); + assertEquals("", c.getString("foo", null, "bar")); + } + + public void test002_ReadWithSubsection() throws ConfigInvalidException { + final Config c = parse("[foo \"zip\"]\nbar\n[foo \"zap\"]\nbar=false\nn=3\n"); + assertEquals(true, c.getBoolean("foo", "zip", "bar", false)); + assertEquals("", c.getString("foo","zip", "bar")); + assertEquals(false, c.getBoolean("foo", "zap", "bar", true)); + assertEquals("false", c.getString("foo", "zap", "bar")); + assertEquals(3, c.getInt("foo", "zap", "n", 4)); + assertEquals(4, c.getInt("foo", "zap","m", 4)); + } + + public void test003_PutRemote() { + final Config c = new Config(); + c.setString("sec", "ext", "name", "value"); + c.setString("sec", "ext", "name2", "value2"); + final String expText = "[sec \"ext\"]\n\tname = value\n\tname2 = value2\n"; + assertEquals(expText, c.toText()); + } + + public void test004_PutGetSimple() { + Config c = new Config(); + c.setString("my", null, "somename", "false"); + assertEquals("false", c.getString("my", null, "somename")); + assertEquals("[my]\n\tsomename = false\n", c.toText()); + } + + public void test005_PutGetStringList() { + Config c = new Config(); + final LinkedList<String> values = new LinkedList<String>(); + values.add("value1"); + values.add("value2"); + c.setStringList("my", null, "somename", values); + + final Object[] expArr = values.toArray(); + final String[] actArr = c.getStringList("my", null, "somename"); + assertTrue(Arrays.equals(expArr, actArr)); + + final String expText = "[my]\n\tsomename = value1\n\tsomename = value2\n"; + assertEquals(expText, c.toText()); + } + + public void test006_readCaseInsensitive() throws ConfigInvalidException { + final Config c = parse("[Foo]\nBar\n"); + assertEquals(true, c.getBoolean("foo", null, "bar", false)); + assertEquals("", c.getString("foo", null, "bar")); + } + + public void test007_readUserConfig() { + final MockSystemReader mockSystemReader = new MockSystemReader(); + SystemReader.setInstance(mockSystemReader); + final String hostname = mockSystemReader.getHostname(); + final Config userGitConfig = mockSystemReader.userGitConfig; + final Config localConfig = new Config(userGitConfig); + mockSystemReader.values.clear(); + + String authorName; + String authorEmail; + + // no values defined nowhere + authorName = localConfig.get(UserConfig.KEY).getAuthorName(); + authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail(); + assertEquals(Constants.UNKNOWN_USER_DEFAULT, authorName); + assertEquals(Constants.UNKNOWN_USER_DEFAULT + "@" + hostname, authorEmail); + + // the system user name is defined + mockSystemReader.values.put(Constants.OS_USER_NAME_KEY, "os user name"); + localConfig.uncache(UserConfig.KEY); + authorName = localConfig.get(UserConfig.KEY).getAuthorName(); + assertEquals("os user name", authorName); + + if (hostname != null && hostname.length() != 0) { + authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail(); + assertEquals("os user name@" + hostname, authorEmail); + } + + // the git environment variables are defined + mockSystemReader.values.put(Constants.GIT_AUTHOR_NAME_KEY, "git author name"); + mockSystemReader.values.put(Constants.GIT_AUTHOR_EMAIL_KEY, "author@email"); + localConfig.uncache(UserConfig.KEY); + authorName = localConfig.get(UserConfig.KEY).getAuthorName(); + authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail(); + assertEquals("git author name", authorName); + assertEquals("author@email", authorEmail); + + // the values are defined in the global configuration + userGitConfig.setString("user", null, "name", "global username"); + userGitConfig.setString("user", null, "email", "author@globalemail"); + authorName = localConfig.get(UserConfig.KEY).getAuthorName(); + authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail(); + assertEquals("global username", authorName); + assertEquals("author@globalemail", authorEmail); + + // the values are defined in the local configuration + localConfig.setString("user", null, "name", "local username"); + localConfig.setString("user", null, "email", "author@localemail"); + authorName = localConfig.get(UserConfig.KEY).getAuthorName(); + authorEmail = localConfig.get(UserConfig.KEY).getAuthorEmail(); + assertEquals("local username", authorName); + assertEquals("author@localemail", authorEmail); + + authorName = localConfig.get(UserConfig.KEY).getCommitterName(); + authorEmail = localConfig.get(UserConfig.KEY).getCommitterEmail(); + assertEquals("local username", authorName); + assertEquals("author@localemail", authorEmail); + } + + public void testReadBoolean_TrueFalse1() throws ConfigInvalidException { + final Config c = parse("[s]\na = true\nb = false\n"); + assertEquals("true", c.getString("s", null, "a")); + assertEquals("false", c.getString("s", null, "b")); + + assertTrue(c.getBoolean("s", "a", false)); + assertFalse(c.getBoolean("s", "b", true)); + } + + public void testReadBoolean_TrueFalse2() throws ConfigInvalidException { + final Config c = parse("[s]\na = TrUe\nb = fAlSe\n"); + assertEquals("TrUe", c.getString("s", null, "a")); + assertEquals("fAlSe", c.getString("s", null, "b")); + + assertTrue(c.getBoolean("s", "a", false)); + assertFalse(c.getBoolean("s", "b", true)); + } + + public void testReadBoolean_YesNo1() throws ConfigInvalidException { + final Config c = parse("[s]\na = yes\nb = no\n"); + assertEquals("yes", c.getString("s", null, "a")); + assertEquals("no", c.getString("s", null, "b")); + + assertTrue(c.getBoolean("s", "a", false)); + assertFalse(c.getBoolean("s", "b", true)); + } + + public void testReadBoolean_YesNo2() throws ConfigInvalidException { + final Config c = parse("[s]\na = yEs\nb = NO\n"); + assertEquals("yEs", c.getString("s", null, "a")); + assertEquals("NO", c.getString("s", null, "b")); + + assertTrue(c.getBoolean("s", "a", false)); + assertFalse(c.getBoolean("s", "b", true)); + } + + public void testReadBoolean_OnOff1() throws ConfigInvalidException { + final Config c = parse("[s]\na = on\nb = off\n"); + assertEquals("on", c.getString("s", null, "a")); + assertEquals("off", c.getString("s", null, "b")); + + assertTrue(c.getBoolean("s", "a", false)); + assertFalse(c.getBoolean("s", "b", true)); + } + + public void testReadBoolean_OnOff2() throws ConfigInvalidException { + final Config c = parse("[s]\na = ON\nb = OFF\n"); + assertEquals("ON", c.getString("s", null, "a")); + assertEquals("OFF", c.getString("s", null, "b")); + + assertTrue(c.getBoolean("s", "a", false)); + assertFalse(c.getBoolean("s", "b", true)); + } + + public void testReadLong() throws ConfigInvalidException { + assertReadLong(1L); + assertReadLong(-1L); + assertReadLong(Long.MIN_VALUE); + assertReadLong(Long.MAX_VALUE); + assertReadLong(4L * 1024 * 1024 * 1024, "4g"); + assertReadLong(3L * 1024 * 1024, "3 m"); + assertReadLong(8L * 1024, "8 k"); + + try { + assertReadLong(-1, "1.5g"); + fail("incorrectly accepted 1.5g"); + } catch (IllegalArgumentException e) { + assertEquals("Invalid integer value: s.a=1.5g", e.getMessage()); + } + } + + private void assertReadLong(long exp) throws ConfigInvalidException { + assertReadLong(exp, String.valueOf(exp)); + } + + private void assertReadLong(long exp, String act) + throws ConfigInvalidException { + final Config c = parse("[s]\na = " + act + "\n"); + assertEquals(exp, c.getLong("s", null, "a", 0L)); + } + + private Config parse(final String content) throws ConfigInvalidException { + final Config c = new Config(null); + c.fromText(content); + return c; + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java new file mode 100644 index 0000000000..2870e4126a --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2007-2009, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.eclipse.jgit.util.JGitTestUtil; +import org.eclipse.jgit.util.SystemReader; + +/** + * Base class for most JGit unit tests. + * + * Sets up a predefined test repository and has support for creating additional + * repositories and destroying them when the tests are finished. + * + * A system property <em>jgit.junit.usemmap</em> defines whether memory mapping + * is used. Memory mapping has an effect on the file system, in that memory + * mapped files in java cannot be deleted as long as they mapped arrays have not + * been reclaimed by the garbage collector. The programmer cannot control this + * with precision, though hinting using <em>{@link java.lang.System#gc}</em> + * often helps. + */ +public abstract class RepositoryTestCase extends TestCase { + + protected final File trashParent = new File("trash"); + + protected File trash; + + protected File trash_git; + + protected static final PersonIdent jauthor; + + protected static final PersonIdent jcommitter; + + static { + jauthor = new PersonIdent("J. Author", "jauthor@example.com"); + jcommitter = new PersonIdent("J. Committer", "jcommitter@example.com"); + } + + protected boolean packedGitMMAP; + + /** + * Configure JGit before setting up test repositories. + */ + protected void configure() { + final WindowCacheConfig c = new WindowCacheConfig(); + c.setPackedGitLimit(128 * WindowCacheConfig.KB); + c.setPackedGitWindowSize(8 * WindowCacheConfig.KB); + c.setPackedGitMMAP("true".equals(System.getProperty("jgit.junit.usemmap"))); + c.setDeltaBaseCacheLimit(8 * WindowCacheConfig.KB); + WindowCache.reconfigure(c); + } + + /** + * Utility method to delete a directory recursively. It is + * also used internally. If a file or directory cannot be removed + * it throws an AssertionFailure. + * + * @param dir + */ + protected void recursiveDelete(final File dir) { + recursiveDelete(dir, false, getClass().getName() + "." + getName(), true); + } + + protected static boolean recursiveDelete(final File dir, boolean silent, + final String name, boolean failOnError) { + assert !(silent && failOnError); + if (!dir.exists()) + return silent; + final File[] ls = dir.listFiles(); + if (ls != null) { + for (int k = 0; k < ls.length; k++) { + final File e = ls[k]; + if (e.isDirectory()) { + silent = recursiveDelete(e, silent, name, failOnError); + } else { + if (!e.delete()) { + if (!silent) { + reportDeleteFailure(name, failOnError, e); + } + silent = !failOnError; + } + } + } + } + if (!dir.delete()) { + if (!silent) { + reportDeleteFailure(name, failOnError, dir); + } + silent = !failOnError; + } + return silent; + } + + private static void reportDeleteFailure(final String name, + boolean failOnError, final File e) { + String severity; + if (failOnError) + severity = "Error"; + else + severity = "Warning"; + String msg = severity + ": Failed to delete " + e; + if (name != null) + msg += " in " + name; + if (failOnError) + fail(msg); + else + System.out.println(msg); + } + + protected static void copyFile(final File src, final File dst) + throws IOException { + final FileInputStream fis = new FileInputStream(src); + try { + final FileOutputStream fos = new FileOutputStream(dst); + try { + final byte[] buf = new byte[4096]; + int r; + while ((r = fis.read(buf)) > 0) { + fos.write(buf, 0, r); + } + } finally { + fos.close(); + } + } finally { + fis.close(); + } + } + + protected File writeTrashFile(final String name, final String data) + throws IOException { + File tf = new File(trash, name); + File tfp = tf.getParentFile(); + if (!tfp.exists() && !tf.getParentFile().mkdirs()) + throw new Error("Could not create directory " + tf.getParentFile()); + final OutputStreamWriter fw = new OutputStreamWriter( + new FileOutputStream(tf), "UTF-8"); + try { + fw.write(data); + } finally { + fw.close(); + } + return tf; + } + + protected static void checkFile(File f, final String checkData) + throws IOException { + Reader r = new InputStreamReader(new FileInputStream(f), "ISO-8859-1"); + try { + char[] data = new char[(int) f.length()]; + if (f.length() != r.read(data)) + throw new IOException("Internal error reading file data from "+f); + assertEquals(checkData, new String(data)); + } finally { + r.close(); + } + } + + protected Repository db; + + private static Thread shutdownhook; + private static List<Runnable> shutDownCleanups = new ArrayList<Runnable>(); + private static int testcount; + + private ArrayList<Repository> repositoriesToClose = new ArrayList<Repository>(); + + public void setUp() throws Exception { + super.setUp(); + configure(); + final String name = getClass().getName() + "." + getName(); + recursiveDelete(trashParent, true, name, false); // Cleanup old failed stuff + trash = new File(trashParent,"trash"+System.currentTimeMillis()+"."+(testcount++)); + trash_git = new File(trash, ".git").getCanonicalFile(); + if (shutdownhook == null) { + shutdownhook = new Thread() { + @Override + public void run() { + // This may look superfluous, but is an extra attempt + // to clean up. First GC to release as many resources + // as possible and then try to clean up one test repo + // at a time (to record problems) and finally to drop + // the directory containing all test repositories. + System.gc(); + for (Runnable r : shutDownCleanups) + r.run(); + recursiveDelete(trashParent, false, null, false); + } + }; + Runtime.getRuntime().addShutdownHook(shutdownhook); + } + + final MockSystemReader mockSystemReader = new MockSystemReader(); + mockSystemReader.userGitConfig = new FileBasedConfig(new File( + trash_git, "usergitconfig")); + SystemReader.setInstance(mockSystemReader); + + db = new Repository(trash_git); + db.create(); + + final String[] packs = { + "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f", + "pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371", + "pack-9fb5b411fe6dfa89cc2e6b89d2bd8e5de02b5745", + "pack-546ff360fe3488adb20860ce3436a2d6373d2796", + "pack-cbdeda40019ae0e6e789088ea0f51f164f489d14", + "pack-e6d07037cbcf13376308a0a995d1fa48f8f76aaa", + "pack-3280af9c07ee18a87705ef50b0cc4cd20266cf12" + }; + final File packDir = new File(db.getObjectsDirectory(), "pack"); + for (int k = 0; k < packs.length; k++) { + copyFile(JGitTestUtil.getTestResourceFile(packs[k] + ".pack"), new File(packDir, + packs[k] + ".pack")); + copyFile(JGitTestUtil.getTestResourceFile(packs[k] + ".idx"), new File(packDir, + packs[k] + ".idx")); + } + + copyFile(JGitTestUtil.getTestResourceFile("packed-refs"), new File(trash_git,"packed-refs")); + } + + protected void tearDown() throws Exception { + RepositoryCache.clear(); + db.close(); + for (Repository r : repositoriesToClose) + r.close(); + + // Since memory mapping is controlled by the GC we need to + // tell it this is a good time to clean up and unlock + // memory mapped files. + if (packedGitMMAP) + System.gc(); + + final String name = getClass().getName() + "." + getName(); + recursiveDelete(trash, false, name, true); + for (Repository r : repositoriesToClose) + recursiveDelete(r.getWorkDir(), false, name, true); + repositoriesToClose.clear(); + + super.tearDown(); + } + + /** + * Helper for creating extra empty repos + * + * @return a new empty git repository for testing purposes + * + * @throws IOException + */ + protected Repository createNewEmptyRepo() throws IOException { + return createNewEmptyRepo(false); + } + + /** + * Helper for creating extra empty repos + * + * @param bare if true, create a bare repository. + * @return a new empty git repository for testing purposes + * + * @throws IOException + */ + protected Repository createNewEmptyRepo(boolean bare) throws IOException { + final File newTestRepo = new File(trashParent, "new" + + System.currentTimeMillis() + "." + (testcount++) + + (bare ? "" : "/") + ".git").getCanonicalFile(); + assertFalse(newTestRepo.exists()); + final Repository newRepo = new Repository(newTestRepo); + newRepo.create(); + final String name = getClass().getName() + "." + getName(); + shutDownCleanups.add(new Runnable() { + public void run() { + recursiveDelete(newTestRepo, false, name, false); + } + }); + repositoriesToClose.add(newRepo); + return newRepo; + } + + protected void setupReflog(String logName, byte[] data) + throws FileNotFoundException, IOException { + File logfile = new File(db.getDirectory(), logName); + if (!logfile.getParentFile().mkdirs() + && !logfile.getParentFile().isDirectory()) { + throw new IOException( + "oops, cannot create the directory for the test reflog file" + + logfile); + } + FileOutputStream fileOutputStream = new FileOutputStream(logfile); + try { + fileOutputStream.write(data); + } finally { + fileOutputStream.close(); + } + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_ObjectId.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_ObjectId.java new file mode 100644 index 0000000000..03176cb8fd --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_ObjectId.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2008, Jonas Fonseca <fonseca@diku.dk> + * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import junit.framework.TestCase; + +public class T0001_ObjectId extends TestCase { + public void test001_toString() { + final String x = "def4c620bc3713bb1bb26b808ec9312548e73946"; + final ObjectId oid = ObjectId.fromString(x); + assertEquals(x, oid.name()); + } + + public void test002_toString() { + final String x = "ff00eedd003713bb1bb26b808ec9312548e73946"; + final ObjectId oid = ObjectId.fromString(x); + assertEquals(x, oid.name()); + } + + public void test003_equals() { + final String x = "def4c620bc3713bb1bb26b808ec9312548e73946"; + final ObjectId a = ObjectId.fromString(x); + final ObjectId b = ObjectId.fromString(x); + assertEquals(a.hashCode(), b.hashCode()); + assertTrue("a and b are same", a.equals(b)); + } + + public void test004_isId() { + assertTrue("valid id", ObjectId + .isId("def4c620bc3713bb1bb26b808ec9312548e73946")); + } + + public void test005_notIsId() { + assertFalse("bob is not an id", ObjectId.isId("bob")); + } + + public void test006_notIsId() { + assertFalse("39 digits is not an id", ObjectId + .isId("def4c620bc3713bb1bb26b808ec9312548e7394")); + } + + public void test007_isId() { + assertTrue("uppercase is accepted", ObjectId + .isId("Def4c620bc3713bb1bb26b808ec9312548e73946")); + } + + public void test008_notIsId() { + assertFalse("g is not a valid hex digit", ObjectId + .isId("gef4c620bc3713bb1bb26b808ec9312548e73946")); + } + + public void test009_toString() { + final String x = "ff00eedd003713bb1bb26b808ec9312548e73946"; + final ObjectId oid = ObjectId.fromString(x); + assertEquals(x, ObjectId.toString(oid)); + } + + public void test010_toString() { + final String x = "0000000000000000000000000000000000000000"; + assertEquals(x, ObjectId.toString(null)); + } + + public void test011_toString() { + final String x = "0123456789ABCDEFabcdef1234567890abcdefAB"; + final ObjectId oid = ObjectId.fromString(x); + assertEquals(x.toLowerCase(), oid.name()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_PersonIdent.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_PersonIdent.java new file mode 100644 index 0000000000..aaa88c0281 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_PersonIdent.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2006-2007, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.util.Date; +import java.util.TimeZone; + +import junit.framework.TestCase; + +public class T0001_PersonIdent extends TestCase { + public void test001_NewIdent() { + final PersonIdent p = new PersonIdent("A U Thor", "author@example.com", + new Date(1142878501000L), TimeZone.getTimeZone("EST")); + assertEquals("A U Thor", p.getName()); + assertEquals("author@example.com", p.getEmailAddress()); + assertEquals(1142878501000L, p.getWhen().getTime()); + assertEquals("A U Thor <author@example.com> 1142878501 -0500", p + .toExternalString()); + } + + public void test002_ParseIdent() { + final String i = "A U Thor <author@example.com> 1142878501 -0500"; + final PersonIdent p = new PersonIdent(i); + assertEquals(i, p.toExternalString()); + assertEquals("A U Thor", p.getName()); + assertEquals("author@example.com", p.getEmailAddress()); + assertEquals(1142878501000L, p.getWhen().getTime()); + } + + public void test003_ParseIdent() { + final String i = "A U Thor <author@example.com> 1142878501 +0230"; + final PersonIdent p = new PersonIdent(i); + assertEquals(i, p.toExternalString()); + assertEquals("A U Thor", p.getName()); + assertEquals("author@example.com", p.getEmailAddress()); + assertEquals(1142878501000L, p.getWhen().getTime()); + } + + public void test004_ParseIdent() { + final String i = "A U Thor<author@example.com> 1142878501 +0230"; + final PersonIdent p = new PersonIdent(i); + assertEquals("A U Thor", p.getName()); + assertEquals("author@example.com", p.getEmailAddress()); + assertEquals(1142878501000L, p.getWhen().getTime()); + } + + public void test005_ParseIdent() { + final String i = "A U Thor<author@example.com>1142878501 +0230"; + final PersonIdent p = new PersonIdent(i); + assertEquals("A U Thor", p.getName()); + assertEquals("author@example.com", p.getEmailAddress()); + assertEquals(1142878501000L, p.getWhen().getTime()); + } + + public void test006_ParseIdent() { + final String i = "A U Thor <author@example.com>1142878501 +0230"; + final PersonIdent p = new PersonIdent(i); + assertEquals("A U Thor", p.getName()); + assertEquals("author@example.com", p.getEmailAddress()); + assertEquals(1142878501000L, p.getWhen().getTime()); + } + + public void test007_ParseIdent() { + final String i = "A U Thor<author@example.com>1142878501 +0230 "; + final PersonIdent p = new PersonIdent(i); + assertEquals("A U Thor", p.getName()); + assertEquals("author@example.com", p.getEmailAddress()); + assertEquals(1142878501000L, p.getWhen().getTime()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0002_Tree.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0002_Tree.java new file mode 100644 index 0000000000..66c3c75f18 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0002_Tree.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +public class T0002_Tree extends RepositoryTestCase { + private static final ObjectId SOME_FAKE_ID = ObjectId.fromString( + "0123456789abcdef0123456789abcdef01234567"); + + private int compareNamesUsingSpecialCompare(String a,String b) throws UnsupportedEncodingException { + char lasta = '\0'; + byte[] abytes; + if (a.length() > 0 && a.charAt(a.length()-1) == '/') { + lasta = '/'; + a = a.substring(0, a.length() - 1); + } + abytes = a.getBytes("ISO-8859-1"); + char lastb = '\0'; + byte[] bbytes; + if (b.length() > 0 && b.charAt(b.length()-1) == '/') { + lastb = '/'; + b = b.substring(0, b.length() - 1); + } + bbytes = b.getBytes("ISO-8859-1"); + return Tree.compareNames(abytes, bbytes, lasta, lastb); + } + + public void test000_sort_01() throws UnsupportedEncodingException { + assertEquals(0, compareNamesUsingSpecialCompare("a","a")); + } + public void test000_sort_02() throws UnsupportedEncodingException { + assertEquals(-1, compareNamesUsingSpecialCompare("a","b")); + assertEquals(1, compareNamesUsingSpecialCompare("b","a")); + } + public void test000_sort_03() throws UnsupportedEncodingException { + assertEquals(1, compareNamesUsingSpecialCompare("a:","a")); + assertEquals(1, compareNamesUsingSpecialCompare("a/","a")); + assertEquals(-1, compareNamesUsingSpecialCompare("a","a/")); + assertEquals(-1, compareNamesUsingSpecialCompare("a","a:")); + assertEquals(1, compareNamesUsingSpecialCompare("a:","a/")); + assertEquals(-1, compareNamesUsingSpecialCompare("a/","a:")); + } + public void test000_sort_04() throws UnsupportedEncodingException { + assertEquals(-1, compareNamesUsingSpecialCompare("a.a","a/a")); + assertEquals(1, compareNamesUsingSpecialCompare("a/a","a.a")); + } + public void test000_sort_05() throws UnsupportedEncodingException { + assertEquals(-1, compareNamesUsingSpecialCompare("a.","a/")); + assertEquals(1, compareNamesUsingSpecialCompare("a/","a.")); + + } + + public void test001_createEmpty() throws IOException { + final Tree t = new Tree(db); + assertTrue("isLoaded", t.isLoaded()); + assertTrue("isModified", t.isModified()); + assertTrue("no parent", t.getParent() == null); + assertTrue("isRoot", t.isRoot()); + assertTrue("no name", t.getName() == null); + assertTrue("no nameUTF8", t.getNameUTF8() == null); + assertTrue("has entries array", t.members() != null); + assertTrue("entries is empty", t.members().length == 0); + assertEquals("full name is empty", "", t.getFullName()); + assertTrue("no id", t.getId() == null); + assertTrue("tree is self", t.getTree() == t); + assertTrue("database is r", t.getRepository() == db); + assertTrue("no foo child", t.findTreeMember("foo") == null); + assertTrue("no foo child", t.findBlobMember("foo") == null); + } + + public void test002_addFile() throws IOException { + final Tree t = new Tree(db); + t.setId(SOME_FAKE_ID); + assertTrue("has id", t.getId() != null); + assertFalse("not modified", t.isModified()); + + final String n = "bob"; + final FileTreeEntry f = t.addFile(n); + assertNotNull("have file", f); + assertEquals("name matches", n, f.getName()); + assertEquals("name matches", f.getName(), new String(f.getNameUTF8(), + "UTF-8")); + assertEquals("full name matches", n, f.getFullName()); + assertTrue("no id", f.getId() == null); + assertTrue("is modified", t.isModified()); + assertTrue("has no id", t.getId() == null); + assertTrue("found bob", t.findBlobMember(f.getName()) == f); + + final TreeEntry[] i = t.members(); + assertNotNull("members array not null", i); + assertTrue("iterator is not empty", i != null && i.length > 0); + assertTrue("iterator returns file", i != null && i[0] == f); + assertTrue("iterator is empty", i != null && i.length == 1); + } + + public void test004_addTree() throws IOException { + final Tree t = new Tree(db); + t.setId(SOME_FAKE_ID); + assertTrue("has id", t.getId() != null); + assertFalse("not modified", t.isModified()); + + final String n = "bob"; + final Tree f = t.addTree(n); + assertNotNull("have tree", f); + assertEquals("name matches", n, f.getName()); + assertEquals("name matches", f.getName(), new String(f.getNameUTF8(), + "UTF-8")); + assertEquals("full name matches", n, f.getFullName()); + assertTrue("no id", f.getId() == null); + assertTrue("parent matches", f.getParent() == t); + assertTrue("repository matches", f.getRepository() == db); + assertTrue("isLoaded", f.isLoaded()); + assertFalse("has items", f.members().length > 0); + assertFalse("is root", f.isRoot()); + assertTrue("tree is self", f.getTree() == f); + assertTrue("parent is modified", t.isModified()); + assertTrue("parent has no id", t.getId() == null); + assertTrue("found bob child", t.findTreeMember(f.getName()) == f); + + final TreeEntry[] i = t.members(); + assertTrue("iterator is not empty", i.length > 0); + assertTrue("iterator returns file", i[0] == f); + assertTrue("iterator is empty", i.length == 1); + } + + public void test005_addRecursiveFile() throws IOException { + final Tree t = new Tree(db); + final FileTreeEntry f = t.addFile("a/b/c"); + assertNotNull("created f", f); + assertEquals("c", f.getName()); + assertEquals("b", f.getParent().getName()); + assertEquals("a", f.getParent().getParent().getName()); + assertTrue("t is great-grandparent", t == f.getParent().getParent() + .getParent()); + } + + public void test005_addRecursiveTree() throws IOException { + final Tree t = new Tree(db); + final Tree f = t.addTree("a/b/c"); + assertNotNull("created f", f); + assertEquals("c", f.getName()); + assertEquals("b", f.getParent().getName()); + assertEquals("a", f.getParent().getParent().getName()); + assertTrue("t is great-grandparent", t == f.getParent().getParent() + .getParent()); + } + + public void test006_addDeepTree() throws IOException { + final Tree t = new Tree(db); + + final Tree e = t.addTree("e"); + assertNotNull("have e", e); + assertTrue("e.parent == t", e.getParent() == t); + final Tree f = t.addTree("f"); + assertNotNull("have f", f); + assertTrue("f.parent == t", f.getParent() == t); + final Tree g = f.addTree("g"); + assertNotNull("have g", g); + assertTrue("g.parent == f", g.getParent() == f); + final Tree h = g.addTree("h"); + assertNotNull("have h", h); + assertTrue("h.parent = g", h.getParent() == g); + + h.setId(SOME_FAKE_ID); + assertTrue("h not modified", !h.isModified()); + g.setId(SOME_FAKE_ID); + assertTrue("g not modified", !g.isModified()); + f.setId(SOME_FAKE_ID); + assertTrue("f not modified", !f.isModified()); + e.setId(SOME_FAKE_ID); + assertTrue("e not modified", !e.isModified()); + t.setId(SOME_FAKE_ID); + assertTrue("t not modified.", !t.isModified()); + + assertEquals("full path of h ok", "f/g/h", h.getFullName()); + assertTrue("Can find h", t.findTreeMember(h.getFullName()) == h); + assertTrue("Can't find f/z", t.findBlobMember("f/z") == null); + assertTrue("Can't find y/z", t.findBlobMember("y/z") == null); + + final FileTreeEntry i = h.addFile("i"); + assertNotNull(i); + assertEquals("full path of i ok", "f/g/h/i", i.getFullName()); + assertTrue("Can find i", t.findBlobMember(i.getFullName()) == i); + assertTrue("h modified", h.isModified()); + assertTrue("g modified", g.isModified()); + assertTrue("f modified", f.isModified()); + assertTrue("e not modified", !e.isModified()); + assertTrue("t modified", t.isModified()); + + assertTrue("h no id", h.getId() == null); + assertTrue("g no id", g.getId() == null); + assertTrue("f no id", f.getId() == null); + assertTrue("e has id", e.getId() != null); + assertTrue("t no id", t.getId() == null); + } + + public void test007_manyFileLookup() throws IOException { + final Tree t = new Tree(db); + final List<FileTreeEntry> files = new ArrayList<FileTreeEntry>(26 * 26); + for (char level1 = 'a'; level1 <= 'z'; level1++) { + for (char level2 = 'a'; level2 <= 'z'; level2++) { + final String n = "." + level1 + level2 + "9"; + final FileTreeEntry f = t.addFile(n); + assertNotNull("File " + n + " added.", f); + assertEquals(n, f.getName()); + files.add(f); + } + } + assertEquals(files.size(), t.memberCount()); + final TreeEntry[] ents = t.members(); + assertNotNull(ents); + assertEquals(files.size(), ents.length); + for (int k = 0; k < ents.length; k++) { + assertTrue("File " + files.get(k).getName() + + " is at " + k + ".", files.get(k) == ents[k]); + } + } + + public void test008_SubtreeInternalSorting() throws IOException { + final Tree t = new Tree(db); + final FileTreeEntry e0 = t.addFile("a-b"); + final FileTreeEntry e1 = t.addFile("a-"); + final FileTreeEntry e2 = t.addFile("a=b"); + final Tree e3 = t.addTree("a"); + final FileTreeEntry e4 = t.addFile("a="); + + final TreeEntry[] ents = t.members(); + assertSame(e1, ents[0]); + assertSame(e0, ents[1]); + assertSame(e3, ents[2]); + assertSame(e4, ents[3]); + assertSame(e2, ents[4]); + } + + public void test009_SymlinkAndGitlink() throws IOException { + final Tree symlinkTree = db.mapTree("symlink"); + assertTrue("Symlink entry exists", symlinkTree.existsBlob("symlink.txt")); + final Tree gitlinkTree = db.mapTree("gitlink"); + assertTrue("Gitlink entry exists", gitlinkTree.existsBlob("submodule")); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java new file mode 100644 index 0000000000..d17cea6aec --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java @@ -0,0 +1,581 @@ +/* + * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com> + * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +import org.eclipse.jgit.errors.ConfigInvalidException; + +public class T0003_Basic extends RepositoryTestCase { + public void test001_Initalize() { + final File gitdir = new File(trash, ".git"); + final File objects = new File(gitdir, "objects"); + final File objects_pack = new File(objects, "pack"); + final File objects_info = new File(objects, "info"); + final File refs = new File(gitdir, "refs"); + final File refs_heads = new File(refs, "heads"); + final File refs_tags = new File(refs, "tags"); + final File HEAD = new File(gitdir, "HEAD"); + + assertTrue("Exists " + trash, trash.isDirectory()); + assertTrue("Exists " + objects, objects.isDirectory()); + assertTrue("Exists " + objects_pack, objects_pack.isDirectory()); + assertTrue("Exists " + objects_info, objects_info.isDirectory()); + assertEquals(2, objects.listFiles().length); + assertTrue("Exists " + refs, refs.isDirectory()); + assertTrue("Exists " + refs_heads, refs_heads.isDirectory()); + assertTrue("Exists " + refs_tags, refs_tags.isDirectory()); + assertTrue("Exists " + HEAD, HEAD.isFile()); + assertEquals(23, HEAD.length()); + } + + public void test002_WriteEmptyTree() throws IOException { + // One of our test packs contains the empty tree object. If the pack is + // open when we create it we won't write the object file out as a loose + // object (as it already exists in the pack). + // + final Repository newdb = createNewEmptyRepo(); + final Tree t = new Tree(newdb); + t.accept(new WriteTree(trash, newdb), TreeEntry.MODIFIED_ONLY); + assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", t.getId() + .name()); + final File o = new File(new File(new File(newdb.getDirectory(), + "objects"), "4b"), "825dc642cb6eb9a060e54bf8d69288fbee4904"); + assertTrue("Exists " + o, o.isFile()); + assertTrue("Read-only " + o, !o.canWrite()); + } + + public void test002_WriteEmptyTree2() throws IOException { + // File shouldn't exist as it is in a test pack. + // + final Tree t = new Tree(db); + t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); + assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", t.getId() + .name()); + final File o = new File(new File(new File(trash_git, "objects"), "4b"), + "825dc642cb6eb9a060e54bf8d69288fbee4904"); + assertFalse("Exists " + o, o.isFile()); + } + + public void test003_WriteShouldBeEmptyTree() throws IOException { + final Tree t = new Tree(db); + final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); + t.addFile("should-be-empty").setId(emptyId); + t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); + assertEquals("7bb943559a305bdd6bdee2cef6e5df2413c3d30a", t.getId() + .name()); + + File o; + o = new File(new File(new File(trash_git, "objects"), "7b"), + "b943559a305bdd6bdee2cef6e5df2413c3d30a"); + assertTrue("Exists " + o, o.isFile()); + assertTrue("Read-only " + o, !o.canWrite()); + + o = new File(new File(new File(trash_git, "objects"), "e6"), + "9de29bb2d1d6434b8b29ae775ad8c2e48c5391"); + assertTrue("Exists " + o, o.isFile()); + assertTrue("Read-only " + o, !o.canWrite()); + } + + public void test004_CheckNewConfig() { + final RepositoryConfig c = db.getConfig(); + assertNotNull(c); + assertEquals("0", c.getString("core", null, "repositoryformatversion")); + assertEquals("0", c.getString("CoRe", null, "REPOSITORYFoRmAtVeRsIoN")); + assertEquals("true", c.getString("core", null, "filemode")); + assertEquals("true", c.getString("cOrE", null, "fIlEModE")); + assertNull(c.getString("notavalue", null, "reallyNotAValue")); + } + + public void test005_ReadSimpleConfig() { + final RepositoryConfig c = db.getConfig(); + assertNotNull(c); + assertEquals("0", c.getString("core", null, "repositoryformatversion")); + assertEquals("0", c.getString("CoRe", null, "REPOSITORYFoRmAtVeRsIoN")); + assertEquals("true", c.getString("core", null, "filemode")); + assertEquals("true", c.getString("cOrE", null, "fIlEModE")); + assertNull(c.getString("notavalue", null, "reallyNotAValue")); + } + + public void test006_ReadUglyConfig() throws IOException, + ConfigInvalidException { + final RepositoryConfig c = db.getConfig(); + final File cfg = new File(db.getDirectory(), "config"); + final FileWriter pw = new FileWriter(cfg); + final String configStr = " [core];comment\n\tfilemode = yes\n" + + "[user]\n" + + " email = A U Thor <thor@example.com> # Just an example...\n" + + " name = \"A Thor \\\\ \\\"\\t \"\n" + + " defaultCheckInComment = a many line\\n\\\ncomment\\n\\\n" + + " to test\n"; + pw.write(configStr); + pw.close(); + c.load(); + assertEquals("yes", c.getString("core", null, "filemode")); + assertEquals("A U Thor <thor@example.com>", c + .getString("user", null, "email")); + assertEquals("A Thor \\ \"\t ", c.getString("user", null, "name")); + assertEquals("a many line\ncomment\n to test", c.getString("user", + null, "defaultCheckInComment")); + c.save(); + final FileReader fr = new FileReader(cfg); + final char[] cbuf = new char[configStr.length()]; + fr.read(cbuf); + fr.close(); + assertEquals(configStr, new String(cbuf)); + } + + public void test007_Open() throws IOException { + final Repository db2 = new Repository(db.getDirectory()); + assertEquals(db.getDirectory(), db2.getDirectory()); + assertEquals(db.getObjectsDirectory(), db2.getObjectsDirectory()); + assertNotSame(db.getConfig(), db2.getConfig()); + } + + public void test008_FailOnWrongVersion() throws IOException { + final File cfg = new File(db.getDirectory(), "config"); + final FileWriter pw = new FileWriter(cfg); + final String badvers = "ihopethisisneveraversion"; + final String configStr = "[core]\n" + "\trepositoryFormatVersion=" + + badvers + "\n"; + pw.write(configStr); + pw.close(); + + try { + new Repository(db.getDirectory()); + fail("incorrectly opened a bad repository"); + } catch (IOException ioe) { + assertTrue(ioe.getMessage().indexOf("format") > 0); + assertTrue(ioe.getMessage().indexOf(badvers) > 0); + } + } + + public void test009_CreateCommitOldFormat() throws IOException, + ConfigInvalidException { + writeTrashFile(".git/config", "[core]\n" + "legacyHeaders=1\n"); + db.getConfig().load(); + + final Tree t = new Tree(db); + final FileTreeEntry f = t.addFile("i-am-a-file"); + writeTrashFile(f.getName(), "and this is the data in me\n"); + t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); + assertEquals(ObjectId.fromString("00b1f73724f493096d1ffa0b0f1f1482dbb8c936"), + t.getTreeId()); + + final Commit c = new Commit(db); + c.setAuthor(new PersonIdent(jauthor, 1154236443000L, -4 * 60)); + c.setCommitter(new PersonIdent(jcommitter, 1154236443000L, -4 * 60)); + c.setMessage("A Commit\n"); + c.setTree(t); + assertEquals(t.getTreeId(), c.getTreeId()); + c.commit(); + final ObjectId cmtid = ObjectId.fromString( + "803aec4aba175e8ab1d666873c984c0308179099"); + assertEquals(cmtid, c.getCommitId()); + + // Verify the commit we just wrote is in the correct format. + final XInputStream xis = new XInputStream(new FileInputStream(db + .toFile(cmtid))); + try { + assertEquals(0x78, xis.readUInt8()); + assertEquals(0x9c, xis.readUInt8()); + assertTrue(0x789c % 31 == 0); + } finally { + xis.close(); + } + + // Verify we can read it. + final Commit c2 = db.mapCommit(cmtid); + assertNotNull(c2); + assertEquals(c.getMessage(), c2.getMessage()); + assertEquals(c.getTreeId(), c2.getTreeId()); + assertEquals(c.getAuthor(), c2.getAuthor()); + assertEquals(c.getCommitter(), c2.getCommitter()); + } + + public void test012_SubtreeExternalSorting() throws IOException { + final ObjectId emptyBlob = new ObjectWriter(db).writeBlob(new byte[0]); + final Tree t = new Tree(db); + final FileTreeEntry e0 = t.addFile("a-"); + final FileTreeEntry e1 = t.addFile("a-b"); + final FileTreeEntry e2 = t.addFile("a/b"); + final FileTreeEntry e3 = t.addFile("a="); + final FileTreeEntry e4 = t.addFile("a=b"); + + e0.setId(emptyBlob); + e1.setId(emptyBlob); + e2.setId(emptyBlob); + e3.setId(emptyBlob); + e4.setId(emptyBlob); + + t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); + assertEquals(ObjectId.fromString("b47a8f0a4190f7572e11212769090523e23eb1ea"), + t.getId()); + } + + public void test020_createBlobTag() throws IOException { + final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); + final Tag t = new Tag(db); + t.setObjId(emptyId); + t.setType("blob"); + t.setTag("test020"); + t.setAuthor(new PersonIdent(jauthor, 1154236443000L, -4 * 60)); + t.setMessage("test020 tagged\n"); + t.tag(); + assertEquals("6759556b09fbb4fd8ae5e315134481cc25d46954", t.getTagId().name()); + + Tag mapTag = db.mapTag("test020"); + assertEquals("blob", mapTag.getType()); + assertEquals("test020 tagged\n", mapTag.getMessage()); + assertEquals(new PersonIdent(jauthor, 1154236443000L, -4 * 60), mapTag.getAuthor()); + assertEquals("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", mapTag.getObjId().name()); + } + + public void test020b_createBlobPlainTag() throws IOException { + test020_createBlobTag(); + Tag t = new Tag(db); + t.setTag("test020b"); + t.setObjId(ObjectId.fromString("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")); + t.tag(); + + Tag mapTag = db.mapTag("test020b"); + assertEquals("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", mapTag.getObjId().name()); + + // We do not repeat the plain tag test for other object types + } + + public void test021_createTreeTag() throws IOException { + final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); + final Tree almostEmptyTree = new Tree(db); + almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId, "empty".getBytes(), false)); + final ObjectId almostEmptyTreeId = new ObjectWriter(db).writeTree(almostEmptyTree); + final Tag t = new Tag(db); + t.setObjId(almostEmptyTreeId); + t.setType("tree"); + t.setTag("test021"); + t.setAuthor(new PersonIdent(jauthor, 1154236443000L, -4 * 60)); + t.setMessage("test021 tagged\n"); + t.tag(); + assertEquals("b0517bc8dbe2096b419d42424cd7030733f4abe5", t.getTagId().name()); + + Tag mapTag = db.mapTag("test021"); + assertEquals("tree", mapTag.getType()); + assertEquals("test021 tagged\n", mapTag.getMessage()); + assertEquals(new PersonIdent(jauthor, 1154236443000L, -4 * 60), mapTag.getAuthor()); + assertEquals("417c01c8795a35b8e835113a85a5c0c1c77f67fb", mapTag.getObjId().name()); + } + + public void test022_createCommitTag() throws IOException { + final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); + final Tree almostEmptyTree = new Tree(db); + almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId, "empty".getBytes(), false)); + final ObjectId almostEmptyTreeId = new ObjectWriter(db).writeTree(almostEmptyTree); + final Commit almostEmptyCommit = new Commit(db); + almostEmptyCommit.setAuthor(new PersonIdent(jauthor, 1154236443000L, -2 * 60)); // not exactly the same + almostEmptyCommit.setCommitter(new PersonIdent(jauthor, 1154236443000L, -2 * 60)); + almostEmptyCommit.setMessage("test022\n"); + almostEmptyCommit.setTreeId(almostEmptyTreeId); + ObjectId almostEmptyCommitId = new ObjectWriter(db).writeCommit(almostEmptyCommit); + final Tag t = new Tag(db); + t.setObjId(almostEmptyCommitId); + t.setType("commit"); + t.setTag("test022"); + t.setAuthor(new PersonIdent(jauthor, 1154236443000L, -4 * 60)); + t.setMessage("test022 tagged\n"); + t.tag(); + assertEquals("0ce2ebdb36076ef0b38adbe077a07d43b43e3807", t.getTagId().name()); + + Tag mapTag = db.mapTag("test022"); + assertEquals("commit", mapTag.getType()); + assertEquals("test022 tagged\n", mapTag.getMessage()); + assertEquals(new PersonIdent(jauthor, 1154236443000L, -4 * 60), mapTag.getAuthor()); + assertEquals("b5d3b45a96b340441f5abb9080411705c51cc86c", mapTag.getObjId().name()); + } + + public void test023_createCommitNonAnullii() throws IOException { + final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); + final Tree almostEmptyTree = new Tree(db); + almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId, "empty".getBytes(), false)); + final ObjectId almostEmptyTreeId = new ObjectWriter(db).writeTree(almostEmptyTree); + Commit commit = new Commit(db); + commit.setTreeId(almostEmptyTreeId); + commit.setAuthor(new PersonIdent("Joe H\u00e4cker","joe@example.com",4294967295000L,60)); + commit.setCommitter(new PersonIdent("Joe Hacker","joe2@example.com",4294967295000L,60)); + commit.setEncoding("UTF-8"); + commit.setMessage("\u00dcbergeeks"); + ObjectId cid = new ObjectWriter(db).writeCommit(commit); + assertEquals("4680908112778718f37e686cbebcc912730b3154", cid.name()); + } + + public void test024_createCommitNonAscii() throws IOException { + final ObjectId emptyId = new ObjectWriter(db).writeBlob(new byte[0]); + final Tree almostEmptyTree = new Tree(db); + almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId, "empty".getBytes(), false)); + final ObjectId almostEmptyTreeId = new ObjectWriter(db).writeTree(almostEmptyTree); + Commit commit = new Commit(db); + commit.setTreeId(almostEmptyTreeId); + commit.setAuthor(new PersonIdent("Joe H\u00e4cker","joe@example.com",4294967295000L,60)); + commit.setCommitter(new PersonIdent("Joe Hacker","joe2@example.com",4294967295000L,60)); + commit.setEncoding("ISO-8859-1"); + commit.setMessage("\u00dcbergeeks"); + ObjectId cid = new ObjectWriter(db).writeCommit(commit); + assertEquals("2979b39d385014b33287054b87f77bcb3ecb5ebf", cid.name()); + } + + public void test025_packedRefs() throws IOException { + test020_createBlobTag(); + test021_createTreeTag(); + test022_createCommitTag(); + + if (!new File(db.getDirectory(),"refs/tags/test020").delete()) throw new Error("Cannot delete unpacked tag"); + if (!new File(db.getDirectory(),"refs/tags/test021").delete()) throw new Error("Cannot delete unpacked tag"); + if (!new File(db.getDirectory(),"refs/tags/test022").delete()) throw new Error("Cannot delete unpacked tag"); + + // We cannot resolve it now, since we have no ref + Tag mapTag20missing = db.mapTag("test020"); + assertNull(mapTag20missing); + + // Construct packed refs file + PrintWriter w = new PrintWriter(new FileWriter(new File(db.getDirectory(), "packed-refs"))); + w.println("# packed-refs with: peeled"); + w.println("6759556b09fbb4fd8ae5e315134481cc25d46954 refs/tags/test020"); + w.println("^e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); + w.println("b0517bc8dbe2096b419d42424cd7030733f4abe5 refs/tags/test021"); + w.println("^417c01c8795a35b8e835113a85a5c0c1c77f67fb"); + w.println("0ce2ebdb36076ef0b38adbe077a07d43b43e3807 refs/tags/test022"); + w.println("^b5d3b45a96b340441f5abb9080411705c51cc86c"); + w.close(); + + Tag mapTag20 = db.mapTag("test020"); + assertNotNull("have tag test020", mapTag20); + assertEquals("blob", mapTag20.getType()); + assertEquals("test020 tagged\n", mapTag20.getMessage()); + assertEquals(new PersonIdent(jauthor, 1154236443000L, -4 * 60), mapTag20.getAuthor()); + assertEquals("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", mapTag20.getObjId().name()); + + Tag mapTag21 = db.mapTag("test021"); + assertEquals("tree", mapTag21.getType()); + assertEquals("test021 tagged\n", mapTag21.getMessage()); + assertEquals(new PersonIdent(jauthor, 1154236443000L, -4 * 60), mapTag21.getAuthor()); + assertEquals("417c01c8795a35b8e835113a85a5c0c1c77f67fb", mapTag21.getObjId().name()); + + Tag mapTag22 = db.mapTag("test022"); + assertEquals("commit", mapTag22.getType()); + assertEquals("test022 tagged\n", mapTag22.getMessage()); + assertEquals(new PersonIdent(jauthor, 1154236443000L, -4 * 60), mapTag22.getAuthor()); + assertEquals("b5d3b45a96b340441f5abb9080411705c51cc86c", mapTag22.getObjId().name()); + } + + public void test025_computeSha1NoStore() throws IOException { + byte[] data = "test025 some data, more than 16 bytes to get good coverage" + .getBytes("ISO-8859-1"); + // TODO: but we do not test legacy header writing + final ObjectId id = new ObjectWriter(db).computeBlobSha1(data.length, + new ByteArrayInputStream(data)); + assertEquals("4f561df5ecf0dfbd53a0dc0f37262fef075d9dde", id.name()); + } + + public void test026_CreateCommitMultipleparents() throws IOException { + final Tree t = new Tree(db); + final FileTreeEntry f = t.addFile("i-am-a-file"); + writeTrashFile(f.getName(), "and this is the data in me\n"); + t.accept(new WriteTree(trash, db), TreeEntry.MODIFIED_ONLY); + assertEquals(ObjectId.fromString("00b1f73724f493096d1ffa0b0f1f1482dbb8c936"), + t.getTreeId()); + + final Commit c1 = new Commit(db); + c1.setAuthor(new PersonIdent(jauthor, 1154236443000L, -4 * 60)); + c1.setCommitter(new PersonIdent(jcommitter, 1154236443000L, -4 * 60)); + c1.setMessage("A Commit\n"); + c1.setTree(t); + assertEquals(t.getTreeId(), c1.getTreeId()); + c1.commit(); + final ObjectId cmtid1 = ObjectId.fromString( + "803aec4aba175e8ab1d666873c984c0308179099"); + assertEquals(cmtid1, c1.getCommitId()); + + final Commit c2 = new Commit(db); + c2.setAuthor(new PersonIdent(jauthor, 1154236443000L, -4 * 60)); + c2.setCommitter(new PersonIdent(jcommitter, 1154236443000L, -4 * 60)); + c2.setMessage("A Commit 2\n"); + c2.setTree(t); + assertEquals(t.getTreeId(), c2.getTreeId()); + c2.setParentIds(new ObjectId[] { c1.getCommitId() } ); + c2.commit(); + final ObjectId cmtid2 = ObjectId.fromString( + "95d068687c91c5c044fb8c77c5154d5247901553"); + assertEquals(cmtid2, c2.getCommitId()); + + Commit rm2 = db.mapCommit(cmtid2); + assertNotSame(c2, rm2); // assert the parsed objects is not from the cache + assertEquals(c2.getAuthor(), rm2.getAuthor()); + assertEquals(c2.getCommitId(), rm2.getCommitId()); + assertEquals(c2.getMessage(), rm2.getMessage()); + assertEquals(c2.getTree().getTreeId(), rm2.getTree().getTreeId()); + assertEquals(1, rm2.getParentIds().length); + assertEquals(c1.getCommitId(), rm2.getParentIds()[0]); + + final Commit c3 = new Commit(db); + c3.setAuthor(new PersonIdent(jauthor, 1154236443000L, -4 * 60)); + c3.setCommitter(new PersonIdent(jcommitter, 1154236443000L, -4 * 60)); + c3.setMessage("A Commit 3\n"); + c3.setTree(t); + assertEquals(t.getTreeId(), c3.getTreeId()); + c3.setParentIds(new ObjectId[] { c1.getCommitId(), c2.getCommitId() }); + c3.commit(); + final ObjectId cmtid3 = ObjectId.fromString( + "ce6e1ce48fbeeb15a83f628dc8dc2debefa066f4"); + assertEquals(cmtid3, c3.getCommitId()); + + Commit rm3 = db.mapCommit(cmtid3); + assertNotSame(c3, rm3); // assert the parsed objects is not from the cache + assertEquals(c3.getAuthor(), rm3.getAuthor()); + assertEquals(c3.getCommitId(), rm3.getCommitId()); + assertEquals(c3.getMessage(), rm3.getMessage()); + assertEquals(c3.getTree().getTreeId(), rm3.getTree().getTreeId()); + assertEquals(2, rm3.getParentIds().length); + assertEquals(c1.getCommitId(), rm3.getParentIds()[0]); + assertEquals(c2.getCommitId(), rm3.getParentIds()[1]); + + final Commit c4 = new Commit(db); + c4.setAuthor(new PersonIdent(jauthor, 1154236443000L, -4 * 60)); + c4.setCommitter(new PersonIdent(jcommitter, 1154236443000L, -4 * 60)); + c4.setMessage("A Commit 4\n"); + c4.setTree(t); + assertEquals(t.getTreeId(), c3.getTreeId()); + c4.setParentIds(new ObjectId[] { c1.getCommitId(), c2.getCommitId(), c3.getCommitId() }); + c4.commit(); + final ObjectId cmtid4 = ObjectId.fromString( + "d1fca9fe3fef54e5212eb67902c8ed3e79736e27"); + assertEquals(cmtid4, c4.getCommitId()); + + Commit rm4 = db.mapCommit(cmtid4); + assertNotSame(c4, rm3); // assert the parsed objects is not from the cache + assertEquals(c4.getAuthor(), rm4.getAuthor()); + assertEquals(c4.getCommitId(), rm4.getCommitId()); + assertEquals(c4.getMessage(), rm4.getMessage()); + assertEquals(c4.getTree().getTreeId(), rm4.getTree().getTreeId()); + assertEquals(3, rm4.getParentIds().length); + assertEquals(c1.getCommitId(), rm4.getParentIds()[0]); + assertEquals(c2.getCommitId(), rm4.getParentIds()[1]); + assertEquals(c3.getCommitId(), rm4.getParentIds()[2]); + } + + public void test027_UnpackedRefHigherPriorityThanPacked() throws IOException { + PrintWriter writer = new PrintWriter(new FileWriter(new File(db.getDirectory(), "refs/heads/a"))); + String unpackedId = "7f822839a2fe9760f386cbbbcb3f92c5fe81def7"; + writer.print(unpackedId); + writer.print('\n'); + writer.close(); + + ObjectId resolved = db.resolve("refs/heads/a"); + assertEquals(unpackedId, resolved.name()); + } + + public void test028_LockPackedRef() throws IOException { + writeTrashFile(".git/packed-refs", "7f822839a2fe9760f386cbbbcb3f92c5fe81def7 refs/heads/foobar"); + writeTrashFile(".git/HEAD", "ref: refs/heads/foobar\n"); + + ObjectId resolve = db.resolve("HEAD"); + assertEquals("7f822839a2fe9760f386cbbbcb3f92c5fe81def7", resolve.name()); + + RefUpdate lockRef = db.updateRef("HEAD"); + ObjectId newId = ObjectId.fromString("07f822839a2fe9760f386cbbbcb3f92c5fe81def"); + lockRef.setNewObjectId(newId); + assertEquals(RefUpdate.Result.FORCED, lockRef.forceUpdate()); + + assertTrue(new File(db.getDirectory(), "refs/heads/foobar").exists()); + assertEquals(newId, db.resolve("refs/heads/foobar")); + + // Again. The ref already exists + RefUpdate lockRef2 = db.updateRef("HEAD"); + ObjectId newId2 = ObjectId.fromString("7f822839a2fe9760f386cbbbcb3f92c5fe81def7"); + lockRef2.setNewObjectId(newId2); + assertEquals(RefUpdate.Result.FORCED, lockRef2.forceUpdate()); + + assertTrue(new File(db.getDirectory(), "refs/heads/foobar").exists()); + assertEquals(newId2, db.resolve("refs/heads/foobar")); + } + + public void test029_mapObject() throws IOException { + assertEquals(new byte[0].getClass(), db.mapObject(ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"), null).getClass()); + assertEquals(Commit.class, db.mapObject(ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"), null).getClass()); + assertEquals(Tree.class, db.mapObject(ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"), null).getClass()); + assertEquals(Tag.class, db.mapObject(ObjectId.fromString("17768080a2318cd89bba4c8b87834401e2095703"), null).getClass()); + } + + public void test30_stripWorkDir() { + File relCwd = new File("."); + File absCwd = relCwd.getAbsoluteFile(); + File absBase = new File(new File(absCwd, "repo"), "workdir"); + File relBase = new File(new File(relCwd, "repo"), "workdir"); + assertEquals(absBase.getAbsolutePath(), relBase.getAbsolutePath()); + + File relBaseFile = new File(new File(relBase, "other"), "module.c"); + File absBaseFile = new File(new File(absBase, "other"), "module.c"); + assertEquals("other/module.c", Repository.stripWorkDir(relBase, relBaseFile)); + assertEquals("other/module.c", Repository.stripWorkDir(relBase, absBaseFile)); + assertEquals("other/module.c", Repository.stripWorkDir(absBase, relBaseFile)); + assertEquals("other/module.c", Repository.stripWorkDir(absBase, absBaseFile)); + + File relNonFile = new File(new File(relCwd, "not-repo"), ".gitignore"); + File absNonFile = new File(new File(absCwd, "not-repo"), ".gitignore"); + assertEquals("", Repository.stripWorkDir(relBase, relNonFile)); + assertEquals("", Repository.stripWorkDir(absBase, absNonFile)); + + assertEquals("", Repository.stripWorkDir(db.getWorkDir(), db.getWorkDir())); + + File file = new File(new File(db.getWorkDir(), "subdir"), "File.java"); + assertEquals("subdir/File.java", Repository.stripWorkDir(db.getWorkDir(), file)); + + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java new file mode 100644 index 0000000000..adddbfe099 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0004_PackReader.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com> + * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.jgit.util.JGitTestUtil; + +public class T0004_PackReader extends RepositoryTestCase { + private static final String PACK_NAME = "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f"; + private static final File TEST_PACK = JGitTestUtil.getTestResourceFile(PACK_NAME + ".pack"); + private static final File TEST_IDX = JGitTestUtil.getTestResourceFile(PACK_NAME + ".idx"); + + public void test003_lookupCompressedObject() throws IOException { + final PackFile pr; + final ObjectId id; + final PackedObjectLoader or; + + id = ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"); + pr = new PackFile(TEST_IDX, TEST_PACK); + or = pr.get(new WindowCursor(), id); + assertNotNull(or); + assertEquals(Constants.OBJ_TREE, or.getType()); + assertEquals(35, or.getSize()); + assertEquals(7738, or.getDataOffset()); + pr.close(); + } + + public void test004_lookupDeltifiedObject() throws IOException { + final ObjectId id; + final ObjectLoader or; + + id = ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"); + or = db.openObject(id); + assertNotNull(or); + assertTrue(or instanceof PackedObjectLoader); + assertEquals(Constants.OBJ_BLOB, or.getType()); + assertEquals(18009, or.getSize()); + assertEquals(537, ((PackedObjectLoader) or).getDataOffset()); + } + + public void test005_todopack() throws IOException { + final File todopack = JGitTestUtil.getTestResourceFile("todopack"); + if (!todopack.isDirectory()) { + System.err.println("Skipping " + getName() + ": no " + todopack); + return; + } + + final File packDir = new File(db.getObjectsDirectory(), "pack"); + final String packname = "pack-2e71952edc41f3ce7921c5e5dd1b64f48204cf35"; + copyFile(new File(todopack, packname + ".pack"), new File(packDir, + packname + ".pack")); + copyFile(new File(todopack, packname + ".idx"), new File(packDir, + packname + ".idx")); + Tree t; + + t = db + .mapTree(ObjectId.fromString( + "aac9df07f653dd18b935298deb813e02c32d2e6f")); + assertNotNull(t); + t.memberCount(); + + t = db + .mapTree(ObjectId.fromString( + "6b9ffbebe7b83ac6a61c9477ab941d999f5d0c96")); + assertNotNull(t); + t.memberCount(); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0007_Index.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0007_Index.java new file mode 100644 index 0000000000..74e4bfbdea --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0007_Index.java @@ -0,0 +1,455 @@ +/* + * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.eclipse.jgit.lib.GitIndex.Entry; +import org.eclipse.jgit.util.FS; + +public class T0007_Index extends RepositoryTestCase { + + static boolean canrungitstatus; + static { + try { + canrungitstatus = system(new File("."),"git --version") == 0; + } catch (IOException e) { + System.out.println("Warning: cannot invoke native git to validate index"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private static int system(File dir, String cmd) throws IOException, + InterruptedException { + final Process process = Runtime.getRuntime().exec(cmd, null, dir); + new Thread() { + public void run() { + try { + InputStream s = process.getErrorStream(); + for (int c = s.read(); c != -1; c = s.read()) { + System.err.print((char) c); + } + s.close(); + } catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } + }.start(); + final Thread t2 = new Thread() { + public void run() { + synchronized (this) { + try { + InputStream e = process.getInputStream(); + for (int c = e.read(); c != -1; c = e.read()) { + System.out.print((char) c); + } + e.close(); + } catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } + } + }; + t2.start(); + process.getOutputStream().close(); + int ret = process.waitFor(); + synchronized (t2) { + return ret; + } + } + + public void testCreateEmptyIndex() throws Exception { + GitIndex index = new GitIndex(db); + index.write(); +// native git doesn't like an empty index +// assertEquals(0,system(trash,"git status")); + + GitIndex indexr = new GitIndex(db); + indexr.read(); + assertEquals(0, indexr.getMembers().length); + } + + public void testReadWithNoIndex() throws Exception { + GitIndex index = new GitIndex(db); + index.read(); + assertEquals(0, index.getMembers().length); + } + + public void testCreateSimpleSortTestIndex() throws Exception { + GitIndex index = new GitIndex(db); + writeTrashFile("a/b", "data:a/b"); + writeTrashFile("a:b", "data:a:b"); + writeTrashFile("a.b", "data:a.b"); + index.add(trash, new File(trash, "a/b")); + index.add(trash, new File(trash, "a:b")); + index.add(trash, new File(trash, "a.b")); + index.write(); + + assertEquals("a/b", index.getEntry("a/b").getName()); + assertEquals("a:b", index.getEntry("a:b").getName()); + assertEquals("a.b", index.getEntry("a.b").getName()); + assertNull(index.getEntry("a*b")); + + // Repeat test for re-read index + GitIndex indexr = new GitIndex(db); + indexr.read(); + assertEquals("a/b", indexr.getEntry("a/b").getName()); + assertEquals("a:b", indexr.getEntry("a:b").getName()); + assertEquals("a.b", indexr.getEntry("a.b").getName()); + assertNull(indexr.getEntry("a*b")); + + if (canrungitstatus) + assertEquals(0, system(trash, "git status")); + } + + public void testUpdateSimpleSortTestIndex() throws Exception { + GitIndex index = new GitIndex(db); + writeTrashFile("a/b", "data:a/b"); + writeTrashFile("a:b", "data:a:b"); + writeTrashFile("a.b", "data:a.b"); + index.add(trash, new File(trash, "a/b")); + index.add(trash, new File(trash, "a:b")); + index.add(trash, new File(trash, "a.b")); + writeTrashFile("a/b", "data:a/b modified"); + index.add(trash, new File(trash, "a/b")); + index.write(); + if (canrungitstatus) + assertEquals(0, system(trash, "git status")); + } + + public void testWriteTree() throws Exception { + GitIndex index = new GitIndex(db); + writeTrashFile("a/b", "data:a/b"); + writeTrashFile("a:b", "data:a:b"); + writeTrashFile("a.b", "data:a.b"); + index.add(trash, new File(trash, "a/b")); + index.add(trash, new File(trash, "a:b")); + index.add(trash, new File(trash, "a.b")); + index.write(); + + ObjectId id = index.writeTree(); + assertEquals("c696abc3ab8e091c665f49d00eb8919690b3aec3", id.name()); + + writeTrashFile("a/b", "data:a/b"); + index.add(trash, new File(trash, "a/b")); + + if (canrungitstatus) + assertEquals(0, system(trash, "git status")); + } + + public void testReadTree() throws Exception { + // Prepare tree + GitIndex index = new GitIndex(db); + writeTrashFile("a/b", "data:a/b"); + writeTrashFile("a:b", "data:a:b"); + writeTrashFile("a.b", "data:a.b"); + index.add(trash, new File(trash, "a/b")); + index.add(trash, new File(trash, "a:b")); + index.add(trash, new File(trash, "a.b")); + index.write(); + + ObjectId id = index.writeTree(); + System.out.println("wrote id " + id); + assertEquals("c696abc3ab8e091c665f49d00eb8919690b3aec3", id.name()); + GitIndex index2 = new GitIndex(db); + + index2.readTree(db.mapTree(ObjectId.fromString( + "c696abc3ab8e091c665f49d00eb8919690b3aec3"))); + Entry[] members = index2.getMembers(); + assertEquals(3, members.length); + assertEquals("a.b", members[0].getName()); + assertEquals("a/b", members[1].getName()); + assertEquals("a:b", members[2].getName()); + assertEquals(3, members.length); + + GitIndex indexr = new GitIndex(db); + indexr.read(); + Entry[] membersr = indexr.getMembers(); + assertEquals(3, membersr.length); + assertEquals("a.b", membersr[0].getName()); + assertEquals("a/b", membersr[1].getName()); + assertEquals("a:b", membersr[2].getName()); + assertEquals(3, membersr.length); + + if (canrungitstatus) + assertEquals(0, system(trash, "git status")); + } + + public void testReadTree2() throws Exception { + // Prepare a larger tree to test some odd cases in tree writing + GitIndex index = new GitIndex(db); + File f1 = writeTrashFile("a/a/a/a", "data:a/a/a/a"); + File f2 = writeTrashFile("a/c/c", "data:a/c/c"); + File f3 = writeTrashFile("a/b", "data:a/b"); + File f4 = writeTrashFile("a:b", "data:a:b"); + File f5 = writeTrashFile("a/d", "data:a/d"); + File f6 = writeTrashFile("a.b", "data:a.b"); + index.add(trash, f1); + index.add(trash, f2); + index.add(trash, f3); + index.add(trash, f4); + index.add(trash, f5); + index.add(trash, f6); + index.write(); + ObjectId id = index.writeTree(); + System.out.println("wrote id " + id); + assertEquals("ba78e065e2c261d4f7b8f42107588051e87e18e9", id.name()); + GitIndex index2 = new GitIndex(db); + + index2.readTree(db.mapTree(ObjectId.fromString( + "ba78e065e2c261d4f7b8f42107588051e87e18e9"))); + Entry[] members = index2.getMembers(); + assertEquals(6, members.length); + assertEquals("a.b", members[0].getName()); + assertEquals("a/a/a/a", members[1].getName()); + assertEquals("a/b", members[2].getName()); + assertEquals("a/c/c", members[3].getName()); + assertEquals("a/d", members[4].getName()); + assertEquals("a:b", members[5].getName()); + + // reread and test + GitIndex indexr = new GitIndex(db); + indexr.read(); + Entry[] membersr = indexr.getMembers(); + assertEquals(6, membersr.length); + assertEquals("a.b", membersr[0].getName()); + assertEquals("a/a/a/a", membersr[1].getName()); + assertEquals("a/b", membersr[2].getName()); + assertEquals("a/c/c", membersr[3].getName()); + assertEquals("a/d", membersr[4].getName()); + assertEquals("a:b", membersr[5].getName()); + } + + public void testDelete() throws Exception { + GitIndex index = new GitIndex(db); + writeTrashFile("a/b", "data:a/b"); + writeTrashFile("a:b", "data:a:b"); + writeTrashFile("a.b", "data:a.b"); + index.add(trash, new File(trash, "a/b")); + index.add(trash, new File(trash, "a:b")); + index.add(trash, new File(trash, "a.b")); + index.write(); + index.writeTree(); + index.remove(trash, new File(trash, "a:b")); + index.write(); + assertEquals("a.b", index.getMembers()[0].getName()); + assertEquals("a/b", index.getMembers()[1].getName()); + + GitIndex indexr = new GitIndex(db); + indexr.read(); + assertEquals("a.b", indexr.getMembers()[0].getName()); + assertEquals("a/b", indexr.getMembers()[1].getName()); + + if (canrungitstatus) + assertEquals(0, system(trash, "git status")); + } + + public void testCheckout() throws Exception { + // Prepare tree, remote it and checkout + GitIndex index = new GitIndex(db); + File aslashb = writeTrashFile("a/b", "data:a/b"); + File acolonb = writeTrashFile("a:b", "data:a:b"); + File adotb = writeTrashFile("a.b", "data:a.b"); + index.add(trash, aslashb); + index.add(trash, acolonb); + index.add(trash, adotb); + index.write(); + index.writeTree(); + delete(aslashb); + delete(acolonb); + delete(adotb); + delete(aslashb.getParentFile()); + + GitIndex index2 = new GitIndex(db); + assertEquals(0, index2.getMembers().length); + + index2.readTree(db.mapTree(ObjectId.fromString( + "c696abc3ab8e091c665f49d00eb8919690b3aec3"))); + + index2.checkout(trash); + assertEquals("data:a/b", content(aslashb)); + assertEquals("data:a:b", content(acolonb)); + assertEquals("data:a.b", content(adotb)); + + if (canrungitstatus) + assertEquals(0, system(trash, "git status")); + } + + public void test030_executeBit_coreModeTrue() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, Error, Exception { + if (!FS.INSTANCE.supportsExecute()) { + System.err.println("Test ignored since platform FS does not support the execute permission"); + return; + } + try { + // coremode true is the default, typically set to false + // by git init (but not jgit!) + Method canExecute = File.class.getMethod("canExecute", (Class[])null); + Method setExecute = File.class.getMethod("setExecutable", new Class[] { Boolean.TYPE }); + File execFile = writeTrashFile("exec","exec"); + if (!((Boolean)setExecute.invoke(execFile, new Object[] { Boolean.TRUE })).booleanValue()) + throw new Error("could not set execute bit on "+execFile.getAbsolutePath()+"for test"); + File nonexecFile = writeTrashFile("nonexec","nonexec"); + if (!((Boolean)setExecute.invoke(nonexecFile, new Object[] { Boolean.FALSE })).booleanValue()) + throw new Error("could not clear execute bit on "+nonexecFile.getAbsolutePath()+"for test"); + + GitIndex index = new GitIndex(db); + index.filemode = Boolean.TRUE; // TODO: we need a way to set this using config + index.add(trash, execFile); + index.add(trash, nonexecFile); + Tree tree = db.mapTree(index.writeTree()); + assertEquals(FileMode.EXECUTABLE_FILE, tree.findBlobMember(execFile.getName()).getMode()); + assertEquals(FileMode.REGULAR_FILE, tree.findBlobMember(nonexecFile.getName()).getMode()); + + index.write(); + + if (!execFile.delete()) + throw new Error("Problem in test, cannot delete test file "+execFile.getAbsolutePath()); + if (!nonexecFile.delete()) + throw new Error("Problem in test, cannot delete test file "+nonexecFile.getAbsolutePath()); + GitIndex index2 = new GitIndex(db); + index2.filemode = Boolean.TRUE; // TODO: we need a way to set this using config + index2.read(); + index2.checkout(trash); + assertTrue(((Boolean)canExecute.invoke(execFile,(Object[])null)).booleanValue()); + assertFalse(((Boolean)canExecute.invoke(nonexecFile,(Object[])null)).booleanValue()); + + assertFalse(index2.getEntry(execFile.getName()).isModified(trash)); + assertFalse(index2.getEntry(nonexecFile.getName()).isModified(trash)); + + if (!((Boolean)setExecute.invoke(execFile, new Object[] { Boolean.FALSE })).booleanValue()) + throw new Error("could not clear set execute bit on "+execFile.getAbsolutePath()+"for test"); + if (!((Boolean)setExecute.invoke(nonexecFile, new Object[] { Boolean.TRUE })).booleanValue()) + throw new Error("could set execute bit on "+nonexecFile.getAbsolutePath()+"for test"); + + assertTrue(index2.getEntry(execFile.getName()).isModified(trash)); + assertTrue(index2.getEntry(nonexecFile.getName()).isModified(trash)); + + } catch (NoSuchMethodException e) { + System.err.println("Test ignored when running under JDK < 1.6"); + return; + } + } + + public void test031_executeBit_coreModeFalse() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, Error, Exception { + if (!FS.INSTANCE.supportsExecute()) { + System.err.println("Test ignored since platform FS does not support the execute permission"); + return; + } + try { + // coremode true is the default, typically set to false + // by git init (but not jgit!) + Method canExecute = File.class.getMethod("canExecute", (Class[])null); + Method setExecute = File.class.getMethod("setExecutable", new Class[] { Boolean.TYPE }); + File execFile = writeTrashFile("exec","exec"); + if (!((Boolean)setExecute.invoke(execFile, new Object[] { Boolean.TRUE })).booleanValue()) + throw new Error("could not set execute bit on "+execFile.getAbsolutePath()+"for test"); + File nonexecFile = writeTrashFile("nonexec","nonexec"); + if (!((Boolean)setExecute.invoke(nonexecFile, new Object[] { Boolean.FALSE })).booleanValue()) + throw new Error("could not clear execute bit on "+nonexecFile.getAbsolutePath()+"for test"); + + GitIndex index = new GitIndex(db); + index.filemode = Boolean.FALSE; // TODO: we need a way to set this using config + index.add(trash, execFile); + index.add(trash, nonexecFile); + Tree tree = db.mapTree(index.writeTree()); + assertEquals(FileMode.REGULAR_FILE, tree.findBlobMember(execFile.getName()).getMode()); + assertEquals(FileMode.REGULAR_FILE, tree.findBlobMember(nonexecFile.getName()).getMode()); + + index.write(); + + if (!execFile.delete()) + throw new Error("Problem in test, cannot delete test file "+execFile.getAbsolutePath()); + if (!nonexecFile.delete()) + throw new Error("Problem in test, cannot delete test file "+nonexecFile.getAbsolutePath()); + GitIndex index2 = new GitIndex(db); + index2.filemode = Boolean.FALSE; // TODO: we need a way to set this using config + index2.read(); + index2.checkout(trash); + assertFalse(((Boolean)canExecute.invoke(execFile,(Object[])null)).booleanValue()); + assertFalse(((Boolean)canExecute.invoke(nonexecFile,(Object[])null)).booleanValue()); + + assertFalse(index2.getEntry(execFile.getName()).isModified(trash)); + assertFalse(index2.getEntry(nonexecFile.getName()).isModified(trash)); + + if (!((Boolean)setExecute.invoke(execFile, new Object[] { Boolean.FALSE })).booleanValue()) + throw new Error("could not clear set execute bit on "+execFile.getAbsolutePath()+"for test"); + if (!((Boolean)setExecute.invoke(nonexecFile, new Object[] { Boolean.TRUE })).booleanValue()) + throw new Error("could set execute bit on "+nonexecFile.getAbsolutePath()+"for test"); + + // no change since we ignore the execute bit + assertFalse(index2.getEntry(execFile.getName()).isModified(trash)); + assertFalse(index2.getEntry(nonexecFile.getName()).isModified(trash)); + + } catch (NoSuchMethodException e) { + System.err.println("Test ignored when running under JDK < 1.6"); + return; + } + } + + private String content(File f) throws IOException { + byte[] buf = new byte[(int) f.length()]; + FileInputStream is = new FileInputStream(f); + try { + int read = is.read(buf); + assertEquals(f.length(), read); + return new String(buf, 0); + } finally { + is.close(); + } + } + + private void delete(File f) throws IOException { + if (!f.delete()) + throw new IOException("Failed to delete f"); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0008_testparserev.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0008_testparserev.java new file mode 100644 index 0000000000..ac730ff6a2 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0008_testparserev.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2008, Jonas Fonseca <fonseca@diku.dk> + * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.IOException; + +public class T0008_testparserev extends RepositoryTestCase { + + public void testObjectId_existing() throws IOException { + assertEquals("49322bb17d3acc9146f98c97d078513228bbf3c0",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0").name()); + } + + public void testObjectId_nonexisting() throws IOException { + assertEquals("49322bb17d3acc9146f98c97d078513228bbf3c1",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c1").name()); + } + + public void testObjectId_objectid_implicit_firstparent() throws IOException { + assertEquals("6e1475206e57110fcef4b92320436c1e9872a322",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^").name()); + assertEquals("1203b03dc816ccbb67773f28b3c19318654b0bc8",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^^").name()); + assertEquals("bab66b48f836ed950c99134ef666436fb07a09a0",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^^^").name()); + } + + public void testObjectId_objectid_self() throws IOException { + assertEquals("49322bb17d3acc9146f98c97d078513228bbf3c0",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^0").name()); + assertEquals("49322bb17d3acc9146f98c97d078513228bbf3c0",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^0^0").name()); + assertEquals("49322bb17d3acc9146f98c97d078513228bbf3c0",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^0^0^0").name()); + } + + public void testObjectId_objectid_explicit_firstparent() throws IOException { + assertEquals("6e1475206e57110fcef4b92320436c1e9872a322",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^1").name()); + assertEquals("1203b03dc816ccbb67773f28b3c19318654b0bc8",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^1^1").name()); + assertEquals("bab66b48f836ed950c99134ef666436fb07a09a0",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^1^1^1").name()); + } + + public void testObjectId_objectid_explicit_otherparents() throws IOException { + assertEquals("6e1475206e57110fcef4b92320436c1e9872a322",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^1").name()); + assertEquals("f73b95671f326616d66b2afb3bdfcdbbce110b44",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^2").name()); + assertEquals("d0114ab8ac326bab30e3a657a0397578c5a1af88",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^3").name()); + assertEquals("d0114ab8ac326bab30e3a657a0397578c5a1af88",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^03").name()); + } + + public void testRef_refname() throws IOException { + assertEquals("49322bb17d3acc9146f98c97d078513228bbf3c0",db.resolve("master^0").name()); + assertEquals("6e1475206e57110fcef4b92320436c1e9872a322",db.resolve("master^").name()); + assertEquals("6e1475206e57110fcef4b92320436c1e9872a322",db.resolve("refs/heads/master^1").name()); + } + + public void testDistance() throws IOException { + assertEquals("49322bb17d3acc9146f98c97d078513228bbf3c0",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0~0").name()); + assertEquals("6e1475206e57110fcef4b92320436c1e9872a322",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0~1").name()); + assertEquals("1203b03dc816ccbb67773f28b3c19318654b0bc8",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0~2").name()); + assertEquals("bab66b48f836ed950c99134ef666436fb07a09a0",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0~3").name()); + assertEquals("bab66b48f836ed950c99134ef666436fb07a09a0",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0~03").name()); + } + + public void testTree() throws IOException { + assertEquals("6020a3b8d5d636e549ccbd0c53e2764684bb3125",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^{tree}").name()); + assertEquals("02ba32d3649e510002c21651936b7077aa75ffa9",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^^{tree}").name()); + } + + public void testHEAD() throws IOException { + assertEquals("6020a3b8d5d636e549ccbd0c53e2764684bb3125",db.resolve("HEAD^{tree}").name()); + } + + public void testDerefCommit() throws IOException { + assertEquals("49322bb17d3acc9146f98c97d078513228bbf3c0",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^{}").name()); + assertEquals("49322bb17d3acc9146f98c97d078513228bbf3c0",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^{commit}").name()); + // double deref + assertEquals("6020a3b8d5d636e549ccbd0c53e2764684bb3125",db.resolve("49322bb17d3acc9146f98c97d078513228bbf3c0^{commit}^{tree}").name()); + } + + public void testDerefTag() throws IOException { + assertEquals("17768080a2318cd89bba4c8b87834401e2095703",db.resolve("refs/tags/B").name()); + assertEquals("d86a2aada2f5e7ccf6f11880bfb9ab404e8a8864",db.resolve("refs/tags/B^{commit}").name()); + assertEquals("032c063ce34486359e3ee3d4f9e5c225b9e1a4c2",db.resolve("refs/tags/B10th").name()); + assertEquals("d86a2aada2f5e7ccf6f11880bfb9ab404e8a8864",db.resolve("refs/tags/B10th^{commit}").name()); + assertEquals("d86a2aada2f5e7ccf6f11880bfb9ab404e8a8864",db.resolve("refs/tags/B10th^{}").name()); + assertEquals("d86a2aada2f5e7ccf6f11880bfb9ab404e8a8864",db.resolve("refs/tags/B10th^0").name()); + assertEquals("d86a2aada2f5e7ccf6f11880bfb9ab404e8a8864",db.resolve("refs/tags/B10th~0").name()); + assertEquals("0966a434eb1a025db6b71485ab63a3bfbea520b6",db.resolve("refs/tags/B10th^").name()); + assertEquals("0966a434eb1a025db6b71485ab63a3bfbea520b6",db.resolve("refs/tags/B10th^1").name()); + assertEquals("0966a434eb1a025db6b71485ab63a3bfbea520b6",db.resolve("refs/tags/B10th~1").name()); + assertEquals("2c349335b7f797072cf729c4f3bb0914ecb6dec9",db.resolve("refs/tags/B10th~2").name()); + } + + public void testDerefBlob() throws IOException { + assertEquals("fd608fbe625a2b456d9f15c2b1dc41f252057dd7",db.resolve("spearce-gpg-pub^{}").name()); + assertEquals("fd608fbe625a2b456d9f15c2b1dc41f252057dd7",db.resolve("spearce-gpg-pub^{blob}").name()); + assertEquals("fd608fbe625a2b456d9f15c2b1dc41f252057dd7",db.resolve("fd608fbe625a2b456d9f15c2b1dc41f252057dd7^{}").name()); + assertEquals("fd608fbe625a2b456d9f15c2b1dc41f252057dd7",db.resolve("fd608fbe625a2b456d9f15c2b1dc41f252057dd7^{blob}").name()); + } + + public void testDerefTree() throws IOException { + assertEquals("032c063ce34486359e3ee3d4f9e5c225b9e1a4c2",db.resolve("refs/tags/B10th").name()); + assertEquals("856ec208ae6cadac25a6d74f19b12bb27a24fe24",db.resolve("032c063ce34486359e3ee3d4f9e5c225b9e1a4c2^{tree}").name()); + assertEquals("856ec208ae6cadac25a6d74f19b12bb27a24fe24",db.resolve("refs/tags/B10th^{tree}").name()); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TreeIteratorLeafOnlyTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TreeIteratorLeafOnlyTest.java new file mode 100644 index 0000000000..6b19cc1e89 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TreeIteratorLeafOnlyTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.IOException; + +public class TreeIteratorLeafOnlyTest extends RepositoryTestCase { + + /** Empty tree */ + public void testEmpty() { + Tree tree = new Tree(db); + TreeIterator i = makeIterator(tree); + assertFalse(i.hasNext()); + } + + /** + * one file + * + * @throws IOException + */ + public void testSimpleF1() throws IOException { + Tree tree = new Tree(db); + tree.addFile("x"); + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("x", i.next().getName()); + } + + /** + * two files + * + * @throws IOException + */ + public void testSimpleF2() throws IOException { + Tree tree = new Tree(db); + tree.addFile("a"); + tree.addFile("x"); + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("a", i.next().getName()); + assertEquals("x", i.next().getName()); + } + + /** + * Empty tree + * + * @throws IOException + */ + public void testSimpleT() throws IOException { + Tree tree = new Tree(db); + tree.addTree("a"); + TreeIterator i = makeIterator(tree); + assertFalse(i.hasNext()); + } + + public void testTricky() throws IOException { + Tree tree = new Tree(db); + tree.addFile("a.b"); + tree.addFile("a.c"); + tree.addFile("a/b.b/b"); + tree.addFile("a/b"); + tree.addFile("a/c"); + tree.addFile("a=c"); + tree.addFile("a=d"); + + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("a.b", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a.c", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a/b", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a/b.b/b", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a/c", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a=c", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a=d", i.next().getFullName()); + assertFalse(i.hasNext()); + } + + private TreeIterator makeIterator(Tree tree) { + return new TreeIterator(tree); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TreeIteratorPostOrderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TreeIteratorPostOrderTest.java new file mode 100644 index 0000000000..caf8bff2a7 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TreeIteratorPostOrderTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.IOException; + +public class TreeIteratorPostOrderTest extends RepositoryTestCase { + + /** Empty tree */ + public void testEmpty() { + Tree tree = new Tree(db); + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("", i.next().getFullName()); + assertFalse(i.hasNext()); + } + + /** + * one file + * + * @throws IOException + */ + public void testSimpleF1() throws IOException { + Tree tree = new Tree(db); + tree.addFile("x"); + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("x", i.next().getName()); + assertTrue(i.hasNext()); + assertEquals("", i.next().getFullName()); + assertFalse(i.hasNext()); + } + + /** + * two files + * + * @throws IOException + */ + public void testSimpleF2() throws IOException { + Tree tree = new Tree(db); + tree.addFile("a"); + tree.addFile("x"); + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("a", i.next().getName()); + assertEquals("x", i.next().getName()); + assertTrue(i.hasNext()); + assertEquals("", i.next().getFullName()); + assertFalse(i.hasNext()); + } + + /** + * Empty tree + * + * @throws IOException + */ + public void testSimpleT() throws IOException { + Tree tree = new Tree(db); + tree.addTree("a"); + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("a", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("", i.next().getFullName()); + assertFalse(i.hasNext()); + } + + public void testTricky() throws IOException { + Tree tree = new Tree(db); + tree.addFile("a.b"); + tree.addFile("a.c"); + tree.addFile("a/b.b/b"); + tree.addFile("a/b"); + tree.addFile("a/c"); + tree.addFile("a=c"); + tree.addFile("a=d"); + + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("a.b", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a.c", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a/b", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a/b.b/b", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a/b.b", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a/c", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a=c", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a=d", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("", i.next().getFullName()); + assertFalse(i.hasNext()); + } + + private TreeIterator makeIterator(Tree tree) { + return new TreeIterator(tree, TreeIterator.Order.POSTORDER); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TreeIteratorPreOrderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TreeIteratorPreOrderTest.java new file mode 100644 index 0000000000..c6f41446d2 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TreeIteratorPreOrderTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.IOException; + +public class TreeIteratorPreOrderTest extends RepositoryTestCase { + + /** Empty tree */ + public void testEmpty() { + Tree tree = new Tree(db); + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("", i.next().getFullName()); + assertFalse(i.hasNext()); + } + + /** + * one file + * + * @throws IOException + */ + public void testSimpleF1() throws IOException { + Tree tree = new Tree(db); + tree.addFile("x"); + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("x", i.next().getName()); + assertFalse(i.hasNext()); + } + + /** + * two files + * + * @throws IOException + */ + public void testSimpleF2() throws IOException { + Tree tree = new Tree(db); + tree.addFile("a"); + tree.addFile("x"); + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a", i.next().getName()); + assertEquals("x", i.next().getName()); + assertFalse(i.hasNext()); + } + + /** + * Empty tree + * + * @throws IOException + */ + public void testSimpleT() throws IOException { + Tree tree = new Tree(db); + tree.addTree("a"); + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a", i.next().getFullName()); + assertFalse(i.hasNext()); + } + + public void testTricky() throws IOException { + Tree tree = new Tree(db); + tree.addFile("a.b"); + tree.addFile("a.c"); + tree.addFile("a/b.b/b"); + tree.addFile("a/b"); + tree.addFile("a/c"); + tree.addFile("a=c"); + tree.addFile("a=d"); + + TreeIterator i = makeIterator(tree); + assertTrue(i.hasNext()); + assertEquals("", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a.b", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a.c", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a/b", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a/b.b", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a/b.b/b", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a/c", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a=c", i.next().getFullName()); + assertTrue(i.hasNext()); + assertEquals("a=d", i.next().getFullName()); + assertFalse(i.hasNext()); + } + + private TreeIterator makeIterator(Tree tree) { + return new TreeIterator(tree, TreeIterator.Order.PREORDER); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java new file mode 100644 index 0000000000..79e2eba701 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 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.lib; + +import junit.framework.TestCase; + +public class ValidRefNameTest extends TestCase { + private static void assertValid(final boolean exp, final String name) { + assertEquals("\"" + name + "\"", exp, Repository.isValidRefName(name)); + } + + public void testEmptyString() { + assertValid(false, ""); + assertValid(false, "/"); + } + + public void testMustHaveTwoComponents() { + assertValid(false, "master"); + assertValid(true, "heads/master"); + } + + public void testValidHead() { + assertValid(true, "refs/heads/master"); + assertValid(true, "refs/heads/pu"); + assertValid(true, "refs/heads/z"); + assertValid(true, "refs/heads/FoO"); + } + + public void testValidTag() { + assertValid(true, "refs/tags/v1.0"); + } + + public void testNoLockSuffix() { + assertValid(false, "refs/heads/master.lock"); + } + + public void testNoDirectorySuffix() { + assertValid(false, "refs/heads/master/"); + } + + public void testNoSpace() { + assertValid(false, "refs/heads/i haz space"); + } + + public void testNoAsciiControlCharacters() { + for (char c = '\0'; c < ' '; c++) + assertValid(false, "refs/heads/mast" + c + "er"); + } + + public void testNoBareDot() { + assertValid(false, "refs/heads/."); + assertValid(false, "refs/heads/.."); + assertValid(false, "refs/heads/./master"); + assertValid(false, "refs/heads/../master"); + } + + public void testNoLeadingOrTrailingDot() { + assertValid(false, "."); + assertValid(false, "refs/heads/.bar"); + assertValid(false, "refs/heads/..bar"); + assertValid(false, "refs/heads/bar."); + } + + public void testContainsDot() { + assertValid(true, "refs/heads/m.a.s.t.e.r"); + assertValid(false, "refs/heads/master..pu"); + } + + public void testNoMagicRefCharacters() { + assertValid(false, "refs/heads/master^"); + assertValid(false, "refs/heads/^master"); + assertValid(false, "^refs/heads/master"); + + assertValid(false, "refs/heads/master~"); + assertValid(false, "refs/heads/~master"); + assertValid(false, "~refs/heads/master"); + + assertValid(false, "refs/heads/master:"); + assertValid(false, "refs/heads/:master"); + assertValid(false, ":refs/heads/master"); + } + + public void testShellGlob() { + assertValid(false, "refs/heads/master?"); + assertValid(false, "refs/heads/?master"); + assertValid(false, "?refs/heads/master"); + + assertValid(false, "refs/heads/master["); + assertValid(false, "refs/heads/[master"); + assertValid(false, "[refs/heads/master"); + + assertValid(false, "refs/heads/master*"); + assertValid(false, "refs/heads/*master"); + assertValid(false, "*refs/heads/master"); + } + + public void testValidSpecialCharacters() { + assertValid(true, "refs/heads/!"); + assertValid(true, "refs/heads/\""); + assertValid(true, "refs/heads/#"); + assertValid(true, "refs/heads/$"); + assertValid(true, "refs/heads/%"); + assertValid(true, "refs/heads/&"); + assertValid(true, "refs/heads/'"); + assertValid(true, "refs/heads/("); + assertValid(true, "refs/heads/)"); + assertValid(true, "refs/heads/+"); + assertValid(true, "refs/heads/,"); + assertValid(true, "refs/heads/-"); + assertValid(true, "refs/heads/;"); + assertValid(true, "refs/heads/<"); + assertValid(true, "refs/heads/="); + assertValid(true, "refs/heads/>"); + assertValid(true, "refs/heads/@"); + assertValid(true, "refs/heads/]"); + assertValid(true, "refs/heads/_"); + assertValid(true, "refs/heads/`"); + assertValid(true, "refs/heads/{"); + assertValid(true, "refs/heads/|"); + assertValid(true, "refs/heads/}"); + + // This is valid on UNIX, but not on Windows + // hence we make in invalid due to non-portability + // + assertValid(false, "refs/heads/\\"); + } + + public void testUnicodeNames() { + assertValid(true, "refs/heads/\u00e5ngstr\u00f6m"); + } + + public void testRefLogQueryIsValidRef() { + assertValid(false, "refs/heads/master@{1}"); + assertValid(false, "refs/heads/master@{1.hour.ago}"); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheGetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheGetTest.java new file mode 100644 index 0000000000..55b568d8fd --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheGetTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 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.lib; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.util.JGitTestUtil; +import org.eclipse.jgit.util.MutableInteger; + +public class WindowCacheGetTest extends RepositoryTestCase { + private List<TestObject> toLoad; + + @Override + public void setUp() throws Exception { + super.setUp(); + + toLoad = new ArrayList<TestObject>(); + final BufferedReader br = new BufferedReader(new InputStreamReader( + new FileInputStream(JGitTestUtil + .getTestResourceFile("all_packed_objects.txt")), + Constants.CHARSET)); + try { + String line; + while ((line = br.readLine()) != null) { + final String[] parts = line.split(" {1,}"); + final TestObject o = new TestObject(); + o.id = ObjectId.fromString(parts[0]); + o.setType(parts[1]); + o.rawSize = Integer.parseInt(parts[2]); + // parts[3] is the size-in-pack + o.offset = Long.parseLong(parts[4]); + toLoad.add(o); + } + } finally { + br.close(); + } + assertEquals(96, toLoad.size()); + } + + public void testCache_Defaults() throws IOException { + final WindowCacheConfig cfg = new WindowCacheConfig(); + WindowCache.reconfigure(cfg); + doCacheTests(); + checkLimits(cfg); + + final WindowCache cache = WindowCache.getInstance(); + assertEquals(6, cache.getOpenFiles()); + assertEquals(17346, cache.getOpenBytes()); + } + + public void testCache_TooFewFiles() throws IOException { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitOpenFiles(2); + WindowCache.reconfigure(cfg); + doCacheTests(); + checkLimits(cfg); + } + + public void testCache_TooSmallLimit() throws IOException { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitWindowSize(4096); + cfg.setPackedGitLimit(4096); + WindowCache.reconfigure(cfg); + doCacheTests(); + checkLimits(cfg); + } + + private void checkLimits(final WindowCacheConfig cfg) { + final WindowCache cache = WindowCache.getInstance(); + assertTrue(cache.getOpenFiles() <= cfg.getPackedGitOpenFiles()); + assertTrue(cache.getOpenBytes() <= cfg.getPackedGitLimit()); + assertTrue(0 < cache.getOpenFiles()); + assertTrue(0 < cache.getOpenBytes()); + } + + private void doCacheTests() throws IOException { + for (final TestObject o : toLoad) { + final ObjectLoader or = db.openObject(o.id); + assertNotNull(or); + assertTrue(or instanceof PackedObjectLoader); + assertEquals(o.type, or.getType()); + assertEquals(o.rawSize, or.getRawSize()); + assertEquals(o.offset, ((PackedObjectLoader) or).getObjectOffset()); + } + } + + private class TestObject { + ObjectId id; + + int type; + + int rawSize; + + long offset; + + void setType(final String typeStr) throws CorruptObjectException { + final byte[] typeRaw = Constants.encode(typeStr + " "); + final MutableInteger ptr = new MutableInteger(); + type = Constants.decodeTypeString(id, typeRaw, (byte) ' ', ptr); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java new file mode 100644 index 0000000000..9e093c85bd --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WindowCacheReconfigureTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 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.lib; + +public class WindowCacheReconfigureTest extends RepositoryTestCase { + public void testConfigureCache_PackedGitLimit_0() { + try { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitLimit(0); + WindowCache.reconfigure(cfg); + fail("incorrectly permitted PackedGitLimit = 0"); + } catch (IllegalArgumentException e) { + // + } + } + + public void testConfigureCache_PackedGitWindowSize_0() { + try { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitWindowSize(0); + WindowCache.reconfigure(cfg); + fail("incorrectly permitted PackedGitWindowSize = 0"); + } catch (IllegalArgumentException e) { + assertEquals("Invalid window size", e.getMessage()); + } + } + + public void testConfigureCache_PackedGitWindowSize_512() { + try { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitWindowSize(512); + WindowCache.reconfigure(cfg); + fail("incorrectly permitted PackedGitWindowSize = 512"); + } catch (IllegalArgumentException e) { + assertEquals("Invalid window size", e.getMessage()); + } + } + + public void testConfigureCache_PackedGitWindowSize_4097() { + try { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitWindowSize(4097); + WindowCache.reconfigure(cfg); + fail("incorrectly permitted PackedGitWindowSize = 4097"); + } catch (IllegalArgumentException e) { + assertEquals("Window size must be power of 2", e.getMessage()); + } + } + + public void testConfigureCache_PackedGitOpenFiles_0() { + try { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitOpenFiles(0); + WindowCache.reconfigure(cfg); + fail("incorrectly permitted PackedGitOpenFiles = 0"); + } catch (IllegalArgumentException e) { + assertEquals("Open files must be >= 1", e.getMessage()); + } + } + + public void testConfigureCache_PackedGitWindowSizeAbovePackedGitLimit() { + try { + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitLimit(1024); + cfg.setPackedGitWindowSize(8192); + WindowCache.reconfigure(cfg); + fail("incorrectly permitted PackedGitWindowSize > PackedGitLimit"); + } catch (IllegalArgumentException e) { + assertEquals("Window size must be < limit", e.getMessage()); + } + } + + public void testConfigureCache_Limits1() { + // This test is just to force coverage over some lower bounds for + // the table. We don't want the table to wind up with too small + // of a size. This is highly dependent upon the table allocation + // algorithm actually implemented in WindowCache. + // + final WindowCacheConfig cfg = new WindowCacheConfig(); + cfg.setPackedGitLimit(6 * 4096 / 5); + cfg.setPackedGitWindowSize(4096); + WindowCache.reconfigure(cfg); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WorkDirCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WorkDirCheckoutTest.java new file mode 100644 index 0000000000..2599468faa --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/WorkDirCheckoutTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com> + * Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +import org.eclipse.jgit.errors.CheckoutConflictException; + +public class WorkDirCheckoutTest extends RepositoryTestCase { + public void testFindingConflicts() throws IOException { + GitIndex index = new GitIndex(db); + index.add(trash, writeTrashFile("bar", "bar")); + index.add(trash, writeTrashFile("foo/bar/baz/qux", "foo/bar")); + recursiveDelete(new File(trash, "bar")); + recursiveDelete(new File(trash, "foo")); + writeTrashFile("bar/baz/qux/foo", "another nasty one"); + writeTrashFile("foo", "troublesome little bugger"); + + WorkDirCheckout workDirCheckout = new WorkDirCheckout(db, trash, index, + index); + workDirCheckout.prescanOneTree(); + ArrayList<String> conflictingEntries = workDirCheckout + .getConflicts(); + ArrayList<String> removedEntries = workDirCheckout.getRemoved(); + assertEquals("bar/baz/qux/foo", conflictingEntries.get(0)); + assertEquals("foo", conflictingEntries.get(1)); + + GitIndex index2 = new GitIndex(db); + recursiveDelete(new File(trash, "bar")); + recursiveDelete(new File(trash, "foo")); + + index2.add(trash, writeTrashFile("bar/baz/qux/foo", "bar")); + index2.add(trash, writeTrashFile("foo", "lalala")); + + workDirCheckout = new WorkDirCheckout(db, trash, index2, index); + workDirCheckout.prescanOneTree(); + + conflictingEntries = workDirCheckout.getConflicts(); + removedEntries = workDirCheckout.getRemoved(); + assertTrue(conflictingEntries.isEmpty()); + assertTrue(removedEntries.contains("bar/baz/qux/foo")); + assertTrue(removedEntries.contains("foo")); + } + + public void testCheckingOutWithConflicts() throws IOException { + GitIndex index = new GitIndex(db); + index.add(trash, writeTrashFile("bar", "bar")); + index.add(trash, writeTrashFile("foo/bar/baz/qux", "foo/bar")); + recursiveDelete(new File(trash, "bar")); + recursiveDelete(new File(trash, "foo")); + writeTrashFile("bar/baz/qux/foo", "another nasty one"); + writeTrashFile("foo", "troublesome little bugger"); + + try { + WorkDirCheckout workDirCheckout = new WorkDirCheckout(db, trash, + index, index); + workDirCheckout.checkout(); + fail("Should have thrown exception"); + } catch (CheckoutConflictException e) { + // all is well + } + + WorkDirCheckout workDirCheckout = new WorkDirCheckout(db, trash, index, + index); + workDirCheckout.setFailOnConflict(false); + workDirCheckout.checkout(); + + assertTrue(new File(trash, "bar").isFile()); + assertTrue(new File(trash, "foo/bar/baz/qux").isFile()); + + GitIndex index2 = new GitIndex(db); + recursiveDelete(new File(trash, "bar")); + recursiveDelete(new File(trash, "foo")); + index2.add(trash, writeTrashFile("bar/baz/qux/foo", "bar")); + writeTrashFile("bar/baz/qux/bar", "evil? I thought it said WEEVIL!"); + index2.add(trash, writeTrashFile("foo", "lalala")); + + workDirCheckout = new WorkDirCheckout(db, trash, index2, index); + workDirCheckout.setFailOnConflict(false); + workDirCheckout.checkout(); + + assertTrue(new File(trash, "bar").isFile()); + assertTrue(new File(trash, "foo/bar/baz/qux").isFile()); + assertNotNull(index2.getEntry("bar")); + assertNotNull(index2.getEntry("foo/bar/baz/qux")); + assertNull(index2.getEntry("bar/baz/qux/foo")); + assertNull(index2.getEntry("foo")); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java new file mode 100644 index 0000000000..eef32b9276 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/XInputStream.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> + * 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.lib; + +import java.io.BufferedInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.jgit.util.NB; + +class XInputStream extends BufferedInputStream { + private final byte[] intbuf = new byte[8]; + + XInputStream(final InputStream s) { + super(s); + } + + synchronized byte[] readFully(final int len) throws IOException { + final byte[] b = new byte[len]; + readFully(b, 0, len); + return b; + } + + synchronized void readFully(final byte[] b, int o, int len) + throws IOException { + int r; + while (len > 0 && (r = read(b, o, len)) > 0) { + o += r; + len -= r; + } + if (len > 0) + throw new EOFException(); + } + + int readUInt8() throws IOException { + final int r = read(); + if (r < 0) + throw new EOFException(); + return r; + } + + long readUInt32() throws IOException { + readFully(intbuf, 0, 4); + return NB.decodeUInt32(intbuf, 0); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/empty.gitindex.dat b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/empty.gitindex.dat Binary files differnew file mode 100644 index 0000000000..3330d716f1 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/empty.gitindex.dat diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/sorttest.gitindex.dat b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/sorttest.gitindex.dat Binary files differnew file mode 100644 index 0000000000..217f2e3811 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/sorttest.gitindex.dat diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java new file mode 100644 index 0000000000..42e653be37 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2008, Robin Rosenberg + * 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.merge; + +import java.io.ByteArrayInputStream; + +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheBuilder; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.lib.Commit; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectWriter; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.treewalk.TreeWalk; + +public class CherryPickTest extends RepositoryTestCase { + public void testPick() throws Exception { + // B---O + // \----P---T + // + // Cherry-pick "T" onto "O". This shouldn't introduce "p-fail", which + // was created by "P", nor should it modify "a", which was done by "P". + // + final DirCache treeB = DirCache.read(db); + final DirCache treeO = DirCache.read(db); + final DirCache treeP = DirCache.read(db); + final DirCache treeT = DirCache.read(db); + { + final DirCacheBuilder b = treeB.builder(); + final DirCacheBuilder o = treeO.builder(); + final DirCacheBuilder p = treeP.builder(); + final DirCacheBuilder t = treeT.builder(); + + b.add(makeEntry("a", FileMode.REGULAR_FILE)); + + o.add(makeEntry("a", FileMode.REGULAR_FILE)); + o.add(makeEntry("o", FileMode.REGULAR_FILE)); + + p.add(makeEntry("a", FileMode.REGULAR_FILE, "q")); + p.add(makeEntry("p-fail", FileMode.REGULAR_FILE)); + + t.add(makeEntry("a", FileMode.REGULAR_FILE)); + t.add(makeEntry("t", FileMode.REGULAR_FILE)); + + b.finish(); + o.finish(); + p.finish(); + t.finish(); + } + + final ObjectWriter ow = new ObjectWriter(db); + final ObjectId B = commit(ow, treeB, new ObjectId[] {}); + final ObjectId O = commit(ow, treeO, new ObjectId[] { B }); + final ObjectId P = commit(ow, treeP, new ObjectId[] { B }); + final ObjectId T = commit(ow, treeT, new ObjectId[] { P }); + + ThreeWayMerger twm = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); + twm.setBase(P); + boolean merge = twm.merge(new ObjectId[] { O, T }); + assertTrue(merge); + + final TreeWalk tw = new TreeWalk(db); + tw.setRecursive(true); + tw.reset(twm.getResultTreeId()); + + assertTrue(tw.next()); + assertEquals("a", tw.getPathString()); + assertCorrectId(treeO, tw); + + assertTrue(tw.next()); + assertEquals("o", tw.getPathString()); + assertCorrectId(treeO, tw); + + assertTrue(tw.next()); + assertEquals("t", tw.getPathString()); + assertCorrectId(treeT, tw); + + assertFalse(tw.next()); + } + + private void assertCorrectId(final DirCache treeT, final TreeWalk tw) { + assertEquals(treeT.getEntry(tw.getPathString()).getObjectId(), tw + .getObjectId(0)); + } + + private ObjectId commit(final ObjectWriter ow, final DirCache treeB, + final ObjectId[] parentIds) throws Exception { + final Commit c = new Commit(db); + c.setTreeId(treeB.writeTree(ow)); + c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0)); + c.setCommitter(c.getAuthor()); + c.setParentIds(parentIds); + c.setMessage("Tree " + c.getTreeId().name()); + return ow.writeCommit(c); + } + + private DirCacheEntry makeEntry(final String path, final FileMode mode) + throws Exception { + return makeEntry(path, mode, path); + } + + private DirCacheEntry makeEntry(final String path, final FileMode mode, + final String content) throws Exception { + final DirCacheEntry ent = new DirCacheEntry(path); + ent.setFileMode(mode); + final byte[] contentBytes = Constants.encode(content); + ent.setObjectId(new ObjectWriter(db).computeBlobSha1( + contentBytes.length, new ByteArrayInputStream(contentBytes))); + return ent; + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java new file mode 100644 index 0000000000..0e50220067 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2008, Robin Rosenberg + * 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.merge; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheBuilder; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.lib.Commit; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectWriter; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.treewalk.TreeWalk; + +public class SimpleMergeTest extends RepositoryTestCase { + + public void testOurs() throws IOException { + Merger ourMerger = MergeStrategy.OURS.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") }); + assertTrue(merge); + assertEquals(db.mapTree("a").getId(), ourMerger.getResultTreeId()); + } + + public void testTheirs() throws IOException { + Merger ourMerger = MergeStrategy.THEIRS.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") }); + assertTrue(merge); + assertEquals(db.mapTree("c").getId(), ourMerger.getResultTreeId()); + } + + public void testTrivialTwoWay() throws IOException { + Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") }); + assertTrue(merge); + assertEquals("02ba32d3649e510002c21651936b7077aa75ffa9",ourMerger.getResultTreeId().name()); + } + + public void testTrivialTwoWay_disjointhistories() throws IOException { + Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c~4") }); + assertTrue(merge); + assertEquals("86265c33b19b2be71bdd7b8cb95823f2743d03a8",ourMerger.getResultTreeId().name()); + } + + public void testTrivialTwoWay_ok() throws IOException { + Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a^0^0^0"), db.resolve("a^0^0^1") }); + assertTrue(merge); + assertEquals(db.mapTree("a^0^0").getId(), ourMerger.getResultTreeId()); + } + + public void testTrivialTwoWay_conflict() throws IOException { + Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("f"), db.resolve("g") }); + assertFalse(merge); + } + + public void testTrivialTwoWay_validSubtreeSort() throws Exception { + final DirCache treeB = DirCache.read(db); + final DirCache treeO = DirCache.read(db); + final DirCache treeT = DirCache.read(db); + { + final DirCacheBuilder b = treeB.builder(); + final DirCacheBuilder o = treeO.builder(); + final DirCacheBuilder t = treeT.builder(); + + b.add(makeEntry("libelf-po/a", FileMode.REGULAR_FILE)); + b.add(makeEntry("libelf/c", FileMode.REGULAR_FILE)); + + o.add(makeEntry("Makefile", FileMode.REGULAR_FILE)); + o.add(makeEntry("libelf-po/a", FileMode.REGULAR_FILE)); + o.add(makeEntry("libelf/c", FileMode.REGULAR_FILE)); + + t.add(makeEntry("libelf-po/a", FileMode.REGULAR_FILE)); + t.add(makeEntry("libelf/c", FileMode.REGULAR_FILE, "blah")); + + b.finish(); + o.finish(); + t.finish(); + } + + final ObjectWriter ow = new ObjectWriter(db); + final ObjectId b = commit(ow, treeB, new ObjectId[] {}); + final ObjectId o = commit(ow, treeO, new ObjectId[] { b }); + final ObjectId t = commit(ow, treeT, new ObjectId[] { b }); + + Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { o, t }); + assertTrue(merge); + + final TreeWalk tw = new TreeWalk(db); + tw.setRecursive(true); + tw.reset(ourMerger.getResultTreeId()); + + assertTrue(tw.next()); + assertEquals("Makefile", tw.getPathString()); + assertCorrectId(treeO, tw); + + assertTrue(tw.next()); + assertEquals("libelf-po/a", tw.getPathString()); + assertCorrectId(treeO, tw); + + assertTrue(tw.next()); + assertEquals("libelf/c", tw.getPathString()); + assertCorrectId(treeT, tw); + + assertFalse(tw.next()); + } + + public void testTrivialTwoWay_concurrentSubtreeChange() throws Exception { + final DirCache treeB = DirCache.read(db); + final DirCache treeO = DirCache.read(db); + final DirCache treeT = DirCache.read(db); + { + final DirCacheBuilder b = treeB.builder(); + final DirCacheBuilder o = treeO.builder(); + final DirCacheBuilder t = treeT.builder(); + + b.add(makeEntry("d/o", FileMode.REGULAR_FILE)); + b.add(makeEntry("d/t", FileMode.REGULAR_FILE)); + + o.add(makeEntry("d/o", FileMode.REGULAR_FILE, "o !")); + o.add(makeEntry("d/t", FileMode.REGULAR_FILE)); + + t.add(makeEntry("d/o", FileMode.REGULAR_FILE)); + t.add(makeEntry("d/t", FileMode.REGULAR_FILE, "t !")); + + b.finish(); + o.finish(); + t.finish(); + } + + final ObjectWriter ow = new ObjectWriter(db); + final ObjectId b = commit(ow, treeB, new ObjectId[] {}); + final ObjectId o = commit(ow, treeO, new ObjectId[] { b }); + final ObjectId t = commit(ow, treeT, new ObjectId[] { b }); + + Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { o, t }); + assertTrue(merge); + + final TreeWalk tw = new TreeWalk(db); + tw.setRecursive(true); + tw.reset(ourMerger.getResultTreeId()); + + assertTrue(tw.next()); + assertEquals("d/o", tw.getPathString()); + assertCorrectId(treeO, tw); + + assertTrue(tw.next()); + assertEquals("d/t", tw.getPathString()); + assertCorrectId(treeT, tw); + + assertFalse(tw.next()); + } + + public void testTrivialTwoWay_conflictSubtreeChange() throws Exception { + final DirCache treeB = DirCache.read(db); + final DirCache treeO = DirCache.read(db); + final DirCache treeT = DirCache.read(db); + { + final DirCacheBuilder b = treeB.builder(); + final DirCacheBuilder o = treeO.builder(); + final DirCacheBuilder t = treeT.builder(); + + b.add(makeEntry("d/o", FileMode.REGULAR_FILE)); + b.add(makeEntry("d/t", FileMode.REGULAR_FILE)); + + o.add(makeEntry("d/o", FileMode.REGULAR_FILE)); + o.add(makeEntry("d/t", FileMode.REGULAR_FILE, "o !")); + + t.add(makeEntry("d/o", FileMode.REGULAR_FILE, "t !")); + t.add(makeEntry("d/t", FileMode.REGULAR_FILE, "t !")); + + b.finish(); + o.finish(); + t.finish(); + } + + final ObjectWriter ow = new ObjectWriter(db); + final ObjectId b = commit(ow, treeB, new ObjectId[] {}); + final ObjectId o = commit(ow, treeO, new ObjectId[] { b }); + final ObjectId t = commit(ow, treeT, new ObjectId[] { b }); + + Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { o, t }); + assertFalse(merge); + } + + public void testTrivialTwoWay_leftDFconflict1() throws Exception { + final DirCache treeB = DirCache.read(db); + final DirCache treeO = DirCache.read(db); + final DirCache treeT = DirCache.read(db); + { + final DirCacheBuilder b = treeB.builder(); + final DirCacheBuilder o = treeO.builder(); + final DirCacheBuilder t = treeT.builder(); + + b.add(makeEntry("d/o", FileMode.REGULAR_FILE)); + b.add(makeEntry("d/t", FileMode.REGULAR_FILE)); + + o.add(makeEntry("d", FileMode.REGULAR_FILE)); + + t.add(makeEntry("d/o", FileMode.REGULAR_FILE)); + t.add(makeEntry("d/t", FileMode.REGULAR_FILE, "t !")); + + b.finish(); + o.finish(); + t.finish(); + } + + final ObjectWriter ow = new ObjectWriter(db); + final ObjectId b = commit(ow, treeB, new ObjectId[] {}); + final ObjectId o = commit(ow, treeO, new ObjectId[] { b }); + final ObjectId t = commit(ow, treeT, new ObjectId[] { b }); + + Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { o, t }); + assertFalse(merge); + } + + public void testTrivialTwoWay_rightDFconflict1() throws Exception { + final DirCache treeB = DirCache.read(db); + final DirCache treeO = DirCache.read(db); + final DirCache treeT = DirCache.read(db); + { + final DirCacheBuilder b = treeB.builder(); + final DirCacheBuilder o = treeO.builder(); + final DirCacheBuilder t = treeT.builder(); + + b.add(makeEntry("d/o", FileMode.REGULAR_FILE)); + b.add(makeEntry("d/t", FileMode.REGULAR_FILE)); + + o.add(makeEntry("d/o", FileMode.REGULAR_FILE)); + o.add(makeEntry("d/t", FileMode.REGULAR_FILE, "o !")); + + t.add(makeEntry("d", FileMode.REGULAR_FILE)); + + b.finish(); + o.finish(); + t.finish(); + } + + final ObjectWriter ow = new ObjectWriter(db); + final ObjectId b = commit(ow, treeB, new ObjectId[] {}); + final ObjectId o = commit(ow, treeO, new ObjectId[] { b }); + final ObjectId t = commit(ow, treeT, new ObjectId[] { b }); + + Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { o, t }); + assertFalse(merge); + } + + public void testTrivialTwoWay_leftDFconflict2() throws Exception { + final DirCache treeB = DirCache.read(db); + final DirCache treeO = DirCache.read(db); + final DirCache treeT = DirCache.read(db); + { + final DirCacheBuilder b = treeB.builder(); + final DirCacheBuilder o = treeO.builder(); + final DirCacheBuilder t = treeT.builder(); + + b.add(makeEntry("d", FileMode.REGULAR_FILE)); + + o.add(makeEntry("d", FileMode.REGULAR_FILE, "o !")); + + t.add(makeEntry("d/o", FileMode.REGULAR_FILE)); + + b.finish(); + o.finish(); + t.finish(); + } + + final ObjectWriter ow = new ObjectWriter(db); + final ObjectId b = commit(ow, treeB, new ObjectId[] {}); + final ObjectId o = commit(ow, treeO, new ObjectId[] { b }); + final ObjectId t = commit(ow, treeT, new ObjectId[] { b }); + + Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { o, t }); + assertFalse(merge); + } + + public void testTrivialTwoWay_rightDFconflict2() throws Exception { + final DirCache treeB = DirCache.read(db); + final DirCache treeO = DirCache.read(db); + final DirCache treeT = DirCache.read(db); + { + final DirCacheBuilder b = treeB.builder(); + final DirCacheBuilder o = treeO.builder(); + final DirCacheBuilder t = treeT.builder(); + + b.add(makeEntry("d", FileMode.REGULAR_FILE)); + + o.add(makeEntry("d/o", FileMode.REGULAR_FILE)); + + t.add(makeEntry("d", FileMode.REGULAR_FILE, "t !")); + + b.finish(); + o.finish(); + t.finish(); + } + + final ObjectWriter ow = new ObjectWriter(db); + final ObjectId b = commit(ow, treeB, new ObjectId[] {}); + final ObjectId o = commit(ow, treeO, new ObjectId[] { b }); + final ObjectId t = commit(ow, treeT, new ObjectId[] { b }); + + Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db); + boolean merge = ourMerger.merge(new ObjectId[] { o, t }); + assertFalse(merge); + } + + private void assertCorrectId(final DirCache treeT, final TreeWalk tw) { + assertEquals(treeT.getEntry(tw.getPathString()).getObjectId(), tw + .getObjectId(0)); + } + + private ObjectId commit(final ObjectWriter ow, final DirCache treeB, + final ObjectId[] parentIds) throws Exception { + final Commit c = new Commit(db); + c.setTreeId(treeB.writeTree(ow)); + c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0)); + c.setCommitter(c.getAuthor()); + c.setParentIds(parentIds); + c.setMessage("Tree " + c.getTreeId().name()); + return ow.writeCommit(c); + } + + private DirCacheEntry makeEntry(final String path, final FileMode mode) + throws Exception { + return makeEntry(path, mode, path); + } + + private DirCacheEntry makeEntry(final String path, final FileMode mode, + final String content) throws Exception { + final DirCacheEntry ent = new DirCacheEntry(path); + ent.setFileMode(mode); + final byte[] contentBytes = Constants.encode(content); + ent.setObjectId(new ObjectWriter(db).computeBlobSha1( + contentBytes.length, new ByteArrayInputStream(contentBytes))); + return ent; + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/EditListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/EditListTest.java new file mode 100644 index 0000000000..c265bc097b --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/EditListTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 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.patch; + +import java.io.IOException; +import java.io.InputStream; + +import junit.framework.TestCase; + +import org.eclipse.jgit.diff.Edit; +import org.eclipse.jgit.diff.EditList; + +public class EditListTest extends TestCase { + public void testHunkHeader() throws IOException { + final Patch p = parseTestPatchFile("testGetText_BothISO88591.patch"); + final FileHeader fh = p.getFiles().get(0); + + final EditList list0 = fh.getHunks().get(0).toEditList(); + assertEquals(1, list0.size()); + assertEquals(new Edit(4 - 1, 5 - 1, 4 - 1, 5 - 1), list0.get(0)); + + final EditList list1 = fh.getHunks().get(1).toEditList(); + assertEquals(1, list1.size()); + assertEquals(new Edit(16 - 1, 17 - 1, 16 - 1, 17 - 1), list1.get(0)); + } + + public void testFileHeader() throws IOException { + final Patch p = parseTestPatchFile("testGetText_BothISO88591.patch"); + final FileHeader fh = p.getFiles().get(0); + final EditList e = fh.toEditList(); + assertEquals(2, e.size()); + assertEquals(new Edit(4 - 1, 5 - 1, 4 - 1, 5 - 1), e.get(0)); + assertEquals(new Edit(16 - 1, 17 - 1, 16 - 1, 17 - 1), e.get(1)); + } + + public void testTypes() throws IOException { + final Patch p = parseTestPatchFile("testEditList_Types.patch"); + final FileHeader fh = p.getFiles().get(0); + final EditList e = fh.toEditList(); + assertEquals(3, e.size()); + assertEquals(new Edit(3 - 1, 3 - 1, 3 - 1, 4 - 1), e.get(0)); + assertEquals(new Edit(17 - 1, 19 - 1, 18 - 1, 18 - 1), e.get(1)); + assertEquals(new Edit(23 - 1, 25 - 1, 22 - 1, 28 - 1), e.get(2)); + } + + private Patch parseTestPatchFile(final String patchFile) throws IOException { + final InputStream in = getClass().getResourceAsStream(patchFile); + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } + try { + final Patch p = new Patch(); + p.parse(in); + return p; + } finally { + in.close(); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/FileHeaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/FileHeaderTest.java new file mode 100644 index 0000000000..8d9e302a82 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/FileHeaderTest.java @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2008, 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.patch; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; + +public class FileHeaderTest extends TestCase { + public void testParseGitFileName_Empty() { + final FileHeader fh = data(""); + assertEquals(-1, fh.parseGitFileName(0, fh.buf.length)); + assertNotNull(fh.getHunks()); + assertTrue(fh.getHunks().isEmpty()); + assertFalse(fh.hasMetaDataChanges()); + } + + public void testParseGitFileName_NoLF() { + final FileHeader fh = data("a/ b/"); + assertEquals(-1, fh.parseGitFileName(0, fh.buf.length)); + } + + public void testParseGitFileName_NoSecondLine() { + final FileHeader fh = data("\n"); + assertEquals(-1, fh.parseGitFileName(0, fh.buf.length)); + } + + public void testParseGitFileName_EmptyHeader() { + final FileHeader fh = data("\n\n"); + assertEquals(1, fh.parseGitFileName(0, fh.buf.length)); + } + + public void testParseGitFileName_Foo() { + final String name = "foo"; + final FileHeader fh = header(name); + assertEquals(gitLine(name).length(), fh.parseGitFileName(0, + fh.buf.length)); + assertEquals(name, fh.getOldName()); + assertSame(fh.getOldName(), fh.getNewName()); + assertFalse(fh.hasMetaDataChanges()); + } + + public void testParseGitFileName_FailFooBar() { + final FileHeader fh = data("a/foo b/bar\n-"); + assertTrue(fh.parseGitFileName(0, fh.buf.length) > 0); + assertNull(fh.getOldName()); + assertNull(fh.getNewName()); + assertFalse(fh.hasMetaDataChanges()); + } + + public void testParseGitFileName_FooSpBar() { + final String name = "foo bar"; + final FileHeader fh = header(name); + assertEquals(gitLine(name).length(), fh.parseGitFileName(0, + fh.buf.length)); + assertEquals(name, fh.getOldName()); + assertSame(fh.getOldName(), fh.getNewName()); + assertFalse(fh.hasMetaDataChanges()); + } + + public void testParseGitFileName_DqFooTabBar() { + final String name = "foo\tbar"; + final String dqName = "foo\\tbar"; + final FileHeader fh = dqHeader(dqName); + assertEquals(dqGitLine(dqName).length(), fh.parseGitFileName(0, + fh.buf.length)); + assertEquals(name, fh.getOldName()); + assertSame(fh.getOldName(), fh.getNewName()); + assertFalse(fh.hasMetaDataChanges()); + } + + public void testParseGitFileName_DqFooSpLfNulBar() { + final String name = "foo \n\0bar"; + final String dqName = "foo \\n\\0bar"; + final FileHeader fh = dqHeader(dqName); + assertEquals(dqGitLine(dqName).length(), fh.parseGitFileName(0, + fh.buf.length)); + assertEquals(name, fh.getOldName()); + assertSame(fh.getOldName(), fh.getNewName()); + assertFalse(fh.hasMetaDataChanges()); + } + + public void testParseGitFileName_SrcFooC() { + final String name = "src/foo/bar/argh/code.c"; + final FileHeader fh = header(name); + assertEquals(gitLine(name).length(), fh.parseGitFileName(0, + fh.buf.length)); + assertEquals(name, fh.getOldName()); + assertSame(fh.getOldName(), fh.getNewName()); + assertFalse(fh.hasMetaDataChanges()); + } + + public void testParseGitFileName_SrcFooCNonStandardPrefix() { + final String name = "src/foo/bar/argh/code.c"; + final String header = "project-v-1.0/" + name + " mydev/" + name + "\n"; + final FileHeader fh = data(header + "-"); + assertEquals(header.length(), fh.parseGitFileName(0, fh.buf.length)); + assertEquals(name, fh.getOldName()); + assertSame(fh.getOldName(), fh.getNewName()); + assertFalse(fh.hasMetaDataChanges()); + } + + public void testParseUnicodeName_NewFile() { + final FileHeader fh = data("diff --git \"a/\\303\\205ngstr\\303\\266m\" \"b/\\303\\205ngstr\\303\\266m\"\n" + + "new file mode 100644\n" + + "index 0000000..7898192\n" + + "--- /dev/null\n" + + "+++ \"b/\\303\\205ngstr\\303\\266m\"\n" + + "@@ -0,0 +1 @@\n" + "+a\n"); + assertParse(fh); + + assertEquals("/dev/null", fh.getOldName()); + assertSame(FileHeader.DEV_NULL, fh.getOldName()); + assertEquals("\u00c5ngstr\u00f6m", fh.getNewName()); + + assertSame(FileHeader.ChangeType.ADD, fh.getChangeType()); + assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType()); + assertTrue(fh.hasMetaDataChanges()); + + assertSame(FileMode.MISSING, fh.getOldMode()); + assertSame(FileMode.REGULAR_FILE, fh.getNewMode()); + + assertEquals("0000000", fh.getOldId().name()); + assertEquals("7898192", fh.getNewId().name()); + assertEquals(0, fh.getScore()); + } + + public void testParseUnicodeName_DeleteFile() { + final FileHeader fh = data("diff --git \"a/\\303\\205ngstr\\303\\266m\" \"b/\\303\\205ngstr\\303\\266m\"\n" + + "deleted file mode 100644\n" + + "index 7898192..0000000\n" + + "--- \"a/\\303\\205ngstr\\303\\266m\"\n" + + "+++ /dev/null\n" + + "@@ -1 +0,0 @@\n" + "-a\n"); + assertParse(fh); + + assertEquals("\u00c5ngstr\u00f6m", fh.getOldName()); + assertEquals("/dev/null", fh.getNewName()); + assertSame(FileHeader.DEV_NULL, fh.getNewName()); + + assertSame(FileHeader.ChangeType.DELETE, fh.getChangeType()); + assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType()); + assertTrue(fh.hasMetaDataChanges()); + + assertSame(FileMode.REGULAR_FILE, fh.getOldMode()); + assertSame(FileMode.MISSING, fh.getNewMode()); + + assertEquals("7898192", fh.getOldId().name()); + assertEquals("0000000", fh.getNewId().name()); + assertEquals(0, fh.getScore()); + } + + public void testParseModeChange() { + final FileHeader fh = data("diff --git a/a b b/a b\n" + + "old mode 100644\n" + "new mode 100755\n"); + assertParse(fh); + assertEquals("a b", fh.getOldName()); + assertEquals("a b", fh.getNewName()); + + assertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType()); + assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType()); + assertTrue(fh.hasMetaDataChanges()); + + assertNull(fh.getOldId()); + assertNull(fh.getNewId()); + + assertSame(FileMode.REGULAR_FILE, fh.getOldMode()); + assertSame(FileMode.EXECUTABLE_FILE, fh.getNewMode()); + assertEquals(0, fh.getScore()); + } + + public void testParseRename100_NewStyle() { + final FileHeader fh = data("diff --git a/a b/ c/\\303\\205ngstr\\303\\266m\n" + + "similarity index 100%\n" + + "rename from a\n" + + "rename to \" c/\\303\\205ngstr\\303\\266m\"\n"); + int ptr = fh.parseGitFileName(0, fh.buf.length); + assertTrue(ptr > 0); + assertNull(fh.getOldName()); // can't parse names on a rename + assertNull(fh.getNewName()); + + ptr = fh.parseGitHeaders(ptr, fh.buf.length); + assertTrue(ptr > 0); + + assertEquals("a", fh.getOldName()); + assertEquals(" c/\u00c5ngstr\u00f6m", fh.getNewName()); + + assertSame(FileHeader.ChangeType.RENAME, fh.getChangeType()); + assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType()); + assertTrue(fh.hasMetaDataChanges()); + + assertNull(fh.getOldId()); + assertNull(fh.getNewId()); + + assertNull(fh.getOldMode()); + assertNull(fh.getNewMode()); + + assertEquals(100, fh.getScore()); + } + + public void testParseRename100_OldStyle() { + final FileHeader fh = data("diff --git a/a b/ c/\\303\\205ngstr\\303\\266m\n" + + "similarity index 100%\n" + + "rename old a\n" + + "rename new \" c/\\303\\205ngstr\\303\\266m\"\n"); + int ptr = fh.parseGitFileName(0, fh.buf.length); + assertTrue(ptr > 0); + assertNull(fh.getOldName()); // can't parse names on a rename + assertNull(fh.getNewName()); + + ptr = fh.parseGitHeaders(ptr, fh.buf.length); + assertTrue(ptr > 0); + + assertEquals("a", fh.getOldName()); + assertEquals(" c/\u00c5ngstr\u00f6m", fh.getNewName()); + + assertSame(FileHeader.ChangeType.RENAME, fh.getChangeType()); + assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType()); + assertTrue(fh.hasMetaDataChanges()); + + assertNull(fh.getOldId()); + assertNull(fh.getNewId()); + + assertNull(fh.getOldMode()); + assertNull(fh.getNewMode()); + + assertEquals(100, fh.getScore()); + } + + public void testParseCopy100() { + final FileHeader fh = data("diff --git a/a b/ c/\\303\\205ngstr\\303\\266m\n" + + "similarity index 100%\n" + + "copy from a\n" + + "copy to \" c/\\303\\205ngstr\\303\\266m\"\n"); + int ptr = fh.parseGitFileName(0, fh.buf.length); + assertTrue(ptr > 0); + assertNull(fh.getOldName()); // can't parse names on a copy + assertNull(fh.getNewName()); + + ptr = fh.parseGitHeaders(ptr, fh.buf.length); + assertTrue(ptr > 0); + + assertEquals("a", fh.getOldName()); + assertEquals(" c/\u00c5ngstr\u00f6m", fh.getNewName()); + + assertSame(FileHeader.ChangeType.COPY, fh.getChangeType()); + assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType()); + assertTrue(fh.hasMetaDataChanges()); + + assertNull(fh.getOldId()); + assertNull(fh.getNewId()); + + assertNull(fh.getOldMode()); + assertNull(fh.getNewMode()); + + assertEquals(100, fh.getScore()); + } + + public void testParseFullIndexLine_WithMode() { + final String oid = "78981922613b2afb6025042ff6bd878ac1994e85"; + final String nid = "61780798228d17af2d34fce4cfbdf35556832472"; + final FileHeader fh = data("diff --git a/a b/a\n" + "index " + oid + + ".." + nid + " 100644\n" + "--- a/a\n" + "+++ b/a\n"); + assertParse(fh); + + assertEquals("a", fh.getOldName()); + assertEquals("a", fh.getNewName()); + + assertSame(FileMode.REGULAR_FILE, fh.getOldMode()); + assertSame(FileMode.REGULAR_FILE, fh.getNewMode()); + assertFalse(fh.hasMetaDataChanges()); + + assertNotNull(fh.getOldId()); + assertNotNull(fh.getNewId()); + + assertTrue(fh.getOldId().isComplete()); + assertTrue(fh.getNewId().isComplete()); + + assertEquals(ObjectId.fromString(oid), fh.getOldId().toObjectId()); + assertEquals(ObjectId.fromString(nid), fh.getNewId().toObjectId()); + } + + public void testParseFullIndexLine_NoMode() { + final String oid = "78981922613b2afb6025042ff6bd878ac1994e85"; + final String nid = "61780798228d17af2d34fce4cfbdf35556832472"; + final FileHeader fh = data("diff --git a/a b/a\n" + "index " + oid + + ".." + nid + "\n" + "--- a/a\n" + "+++ b/a\n"); + assertParse(fh); + + assertEquals("a", fh.getOldName()); + assertEquals("a", fh.getNewName()); + assertFalse(fh.hasMetaDataChanges()); + + assertNull(fh.getOldMode()); + assertNull(fh.getNewMode()); + + assertNotNull(fh.getOldId()); + assertNotNull(fh.getNewId()); + + assertTrue(fh.getOldId().isComplete()); + assertTrue(fh.getNewId().isComplete()); + + assertEquals(ObjectId.fromString(oid), fh.getOldId().toObjectId()); + assertEquals(ObjectId.fromString(nid), fh.getNewId().toObjectId()); + } + + public void testParseAbbrIndexLine_WithMode() { + final int a = 7; + final String oid = "78981922613b2afb6025042ff6bd878ac1994e85"; + final String nid = "61780798228d17af2d34fce4cfbdf35556832472"; + final FileHeader fh = data("diff --git a/a b/a\n" + "index " + + oid.substring(0, a - 1) + ".." + nid.substring(0, a - 1) + + " 100644\n" + "--- a/a\n" + "+++ b/a\n"); + assertParse(fh); + + assertEquals("a", fh.getOldName()); + assertEquals("a", fh.getNewName()); + + assertSame(FileMode.REGULAR_FILE, fh.getOldMode()); + assertSame(FileMode.REGULAR_FILE, fh.getNewMode()); + assertFalse(fh.hasMetaDataChanges()); + + assertNotNull(fh.getOldId()); + assertNotNull(fh.getNewId()); + + assertFalse(fh.getOldId().isComplete()); + assertFalse(fh.getNewId().isComplete()); + + assertEquals(oid.substring(0, a - 1), fh.getOldId().name()); + assertEquals(nid.substring(0, a - 1), fh.getNewId().name()); + + assertTrue(ObjectId.fromString(oid).startsWith(fh.getOldId())); + assertTrue(ObjectId.fromString(nid).startsWith(fh.getNewId())); + } + + public void testParseAbbrIndexLine_NoMode() { + final int a = 7; + final String oid = "78981922613b2afb6025042ff6bd878ac1994e85"; + final String nid = "61780798228d17af2d34fce4cfbdf35556832472"; + final FileHeader fh = data("diff --git a/a b/a\n" + "index " + + oid.substring(0, a - 1) + ".." + nid.substring(0, a - 1) + + "\n" + "--- a/a\n" + "+++ b/a\n"); + assertParse(fh); + + assertEquals("a", fh.getOldName()); + assertEquals("a", fh.getNewName()); + + assertNull(fh.getOldMode()); + assertNull(fh.getNewMode()); + assertFalse(fh.hasMetaDataChanges()); + + assertNotNull(fh.getOldId()); + assertNotNull(fh.getNewId()); + + assertFalse(fh.getOldId().isComplete()); + assertFalse(fh.getNewId().isComplete()); + + assertEquals(oid.substring(0, a - 1), fh.getOldId().name()); + assertEquals(nid.substring(0, a - 1), fh.getNewId().name()); + + assertTrue(ObjectId.fromString(oid).startsWith(fh.getOldId())); + assertTrue(ObjectId.fromString(nid).startsWith(fh.getNewId())); + } + + private static void assertParse(final FileHeader fh) { + int ptr = fh.parseGitFileName(0, fh.buf.length); + assertTrue(ptr > 0); + ptr = fh.parseGitHeaders(ptr, fh.buf.length); + assertTrue(ptr > 0); + } + + private static FileHeader data(final String in) { + return new FileHeader(Constants.encodeASCII(in), 0); + } + + private static FileHeader header(final String path) { + return data(gitLine(path) + "--- " + path + "\n"); + } + + private static String gitLine(final String path) { + return "a/" + path + " b/" + path + "\n"; + } + + private static FileHeader dqHeader(final String path) { + return data(dqGitLine(path) + "--- " + path + "\n"); + } + + private static String dqGitLine(final String path) { + return "\"a/" + path + "\" \"b/" + path + "\"\n"; + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/GetTextTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/GetTextTest.java new file mode 100644 index 0000000000..8d06987fde --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/GetTextTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2008, 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.patch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; + +import junit.framework.TestCase; + +public class GetTextTest extends TestCase { + public void testGetText_BothISO88591() throws IOException { + final Charset cs = Charset.forName("ISO-8859-1"); + final Patch p = parseTestPatchFile(); + assertTrue(p.getErrors().isEmpty()); + assertEquals(1, p.getFiles().size()); + final FileHeader fh = p.getFiles().get(0); + assertEquals(2, fh.getHunks().size()); + assertEquals(readTestPatchFile(cs), fh.getScriptText(cs, cs)); + } + + public void testGetText_NoBinary() throws IOException { + final Charset cs = Charset.forName("ISO-8859-1"); + final Patch p = parseTestPatchFile(); + assertTrue(p.getErrors().isEmpty()); + assertEquals(1, p.getFiles().size()); + final FileHeader fh = p.getFiles().get(0); + assertEquals(0, fh.getHunks().size()); + assertEquals(readTestPatchFile(cs), fh.getScriptText(cs, cs)); + } + + public void testGetText_Convert() throws IOException { + final Charset csOld = Charset.forName("ISO-8859-1"); + final Charset csNew = Charset.forName("UTF-8"); + final Patch p = parseTestPatchFile(); + assertTrue(p.getErrors().isEmpty()); + assertEquals(1, p.getFiles().size()); + final FileHeader fh = p.getFiles().get(0); + assertEquals(2, fh.getHunks().size()); + + // Read the original file as ISO-8859-1 and fix up the one place + // where we changed the character encoding. That makes the exp + // string match what we really expect to get back. + // + String exp = readTestPatchFile(csOld); + exp = exp.replace("\303\205ngstr\303\266m", "\u00c5ngstr\u00f6m"); + + assertEquals(exp, fh.getScriptText(csOld, csNew)); + } + + public void testGetText_DiffCc() throws IOException { + final Charset csOld = Charset.forName("ISO-8859-1"); + final Charset csNew = Charset.forName("UTF-8"); + final Patch p = parseTestPatchFile(); + assertTrue(p.getErrors().isEmpty()); + assertEquals(1, p.getFiles().size()); + final CombinedFileHeader fh = (CombinedFileHeader) p.getFiles().get(0); + assertEquals(1, fh.getHunks().size()); + + // Read the original file as ISO-8859-1 and fix up the one place + // where we changed the character encoding. That makes the exp + // string match what we really expect to get back. + // + String exp = readTestPatchFile(csOld); + exp = exp.replace("\303\205ngstr\303\266m", "\u00c5ngstr\u00f6m"); + + assertEquals(exp, fh + .getScriptText(new Charset[] { csNew, csOld, csNew })); + } + + private Patch parseTestPatchFile() throws IOException { + final String patchFile = getName() + ".patch"; + final InputStream in = getClass().getResourceAsStream(patchFile); + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } + try { + final Patch p = new Patch(); + p.parse(in); + return p; + } finally { + in.close(); + } + } + + private String readTestPatchFile(final Charset cs) throws IOException { + final String patchFile = getName() + ".patch"; + final InputStream in = getClass().getResourceAsStream(patchFile); + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } + try { + final InputStreamReader r = new InputStreamReader(in, cs); + char[] tmp = new char[2048]; + final StringBuilder s = new StringBuilder(); + int n; + while ((n = r.read(tmp)) > 0) + s.append(tmp, 0, n); + return s.toString(); + } finally { + in.close(); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchCcErrorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchCcErrorTest.java new file mode 100644 index 0000000000..f2bae6eb19 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchCcErrorTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2008, 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.patch; + +import java.io.IOException; +import java.io.InputStream; + +import junit.framework.TestCase; + +public class PatchCcErrorTest extends TestCase { + public void testError_CcTruncatedOld() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(1, p.getFiles().size()); + assertEquals(3, p.getErrors().size()); + { + final FormatError e = p.getErrors().get(0); + assertSame(FormatError.Severity.ERROR, e.getSeverity()); + assertEquals( + "Truncated hunk, at least 1 lines is missing for ancestor 1", + e.getMessage()); + assertEquals(346, e.getOffset()); + assertTrue(e.getLineText().startsWith( + "@@@ -55,12 -163,13 +163,15 @@@ public ")); + } + { + final FormatError e = p.getErrors().get(1); + assertSame(FormatError.Severity.ERROR, e.getSeverity()); + assertEquals( + "Truncated hunk, at least 2 lines is missing for ancestor 2", + e.getMessage()); + assertEquals(346, e.getOffset()); + assertTrue(e.getLineText().startsWith( + "@@@ -55,12 -163,13 +163,15 @@@ public ")); + } + { + final FormatError e = p.getErrors().get(2); + assertSame(FormatError.Severity.ERROR, e.getSeverity()); + assertEquals("Truncated hunk, at least 3 new lines is missing", e + .getMessage()); + assertEquals(346, e.getOffset()); + assertTrue(e.getLineText().startsWith( + "@@@ -55,12 -163,13 +163,15 @@@ public ")); + } + } + + private Patch parseTestPatchFile() throws IOException { + final String patchFile = getName() + ".patch"; + final InputStream in = getClass().getResourceAsStream(patchFile); + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } + try { + final Patch p = new Patch(); + p.parse(in); + return p; + } finally { + in.close(); + } + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchCcTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchCcTest.java new file mode 100644 index 0000000000..e97d373d9a --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchCcTest.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2008, 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.patch; + +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.jgit.lib.FileMode; + +import junit.framework.TestCase; + +public class PatchCcTest extends TestCase { + public void testParse_OneFileCc() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(1, p.getFiles().size()); + assertTrue(p.getErrors().isEmpty()); + + final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0); + + assertEquals("org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java", + cfh.getNewName()); + assertEquals(cfh.getNewName(), cfh.getOldName()); + + assertEquals(98, cfh.startOffset); + + assertEquals(2, cfh.getParentCount()); + assertSame(cfh.getOldId(0), cfh.getOldId()); + assertEquals("169356b", cfh.getOldId(0).name()); + assertEquals("dd8c317", cfh.getOldId(1).name()); + assertEquals("fd85931", cfh.getNewId().name()); + + assertSame(cfh.getOldMode(0), cfh.getOldMode()); + assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(0)); + assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(1)); + assertSame(FileMode.EXECUTABLE_FILE, cfh.getNewMode()); + assertSame(FileHeader.ChangeType.MODIFY, cfh.getChangeType()); + assertSame(FileHeader.PatchType.UNIFIED, cfh.getPatchType()); + + assertEquals(1, cfh.getHunks().size()); + { + final CombinedHunkHeader h = cfh.getHunks().get(0); + + assertSame(cfh, h.getFileHeader()); + assertEquals(346, h.startOffset); + assertEquals(764, h.endOffset); + + assertSame(h.getOldImage(0), h.getOldImage()); + assertSame(cfh.getOldId(0), h.getOldImage(0).getId()); + assertSame(cfh.getOldId(1), h.getOldImage(1).getId()); + + assertEquals(55, h.getOldImage(0).getStartLine()); + assertEquals(12, h.getOldImage(0).getLineCount()); + assertEquals(3, h.getOldImage(0).getLinesAdded()); + assertEquals(0, h.getOldImage(0).getLinesDeleted()); + + assertEquals(163, h.getOldImage(1).getStartLine()); + assertEquals(13, h.getOldImage(1).getLineCount()); + assertEquals(2, h.getOldImage(1).getLinesAdded()); + assertEquals(0, h.getOldImage(1).getLinesDeleted()); + + assertEquals(163, h.getNewStartLine()); + assertEquals(15, h.getNewLineCount()); + + assertEquals(10, h.getLinesContext()); + } + } + + public void testParse_CcNewFile() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(1, p.getFiles().size()); + assertTrue(p.getErrors().isEmpty()); + + final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0); + + assertSame(FileHeader.DEV_NULL, cfh.getOldName()); + assertEquals("d", cfh.getNewName()); + + assertEquals(187, cfh.startOffset); + + assertEquals(2, cfh.getParentCount()); + assertSame(cfh.getOldId(0), cfh.getOldId()); + assertEquals("0000000", cfh.getOldId(0).name()); + assertEquals("0000000", cfh.getOldId(1).name()); + assertEquals("4bcfe98", cfh.getNewId().name()); + + assertSame(cfh.getOldMode(0), cfh.getOldMode()); + assertSame(FileMode.MISSING, cfh.getOldMode(0)); + assertSame(FileMode.MISSING, cfh.getOldMode(1)); + assertSame(FileMode.REGULAR_FILE, cfh.getNewMode()); + assertSame(FileHeader.ChangeType.ADD, cfh.getChangeType()); + assertSame(FileHeader.PatchType.UNIFIED, cfh.getPatchType()); + + assertEquals(1, cfh.getHunks().size()); + { + final CombinedHunkHeader h = cfh.getHunks().get(0); + + assertSame(cfh, h.getFileHeader()); + assertEquals(273, h.startOffset); + assertEquals(300, h.endOffset); + + assertSame(h.getOldImage(0), h.getOldImage()); + assertSame(cfh.getOldId(0), h.getOldImage(0).getId()); + assertSame(cfh.getOldId(1), h.getOldImage(1).getId()); + + assertEquals(1, h.getOldImage(0).getStartLine()); + assertEquals(0, h.getOldImage(0).getLineCount()); + assertEquals(1, h.getOldImage(0).getLinesAdded()); + assertEquals(0, h.getOldImage(0).getLinesDeleted()); + + assertEquals(1, h.getOldImage(1).getStartLine()); + assertEquals(0, h.getOldImage(1).getLineCount()); + assertEquals(1, h.getOldImage(1).getLinesAdded()); + assertEquals(0, h.getOldImage(1).getLinesDeleted()); + + assertEquals(1, h.getNewStartLine()); + assertEquals(1, h.getNewLineCount()); + + assertEquals(0, h.getLinesContext()); + } + } + + public void testParse_CcDeleteFile() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(1, p.getFiles().size()); + assertTrue(p.getErrors().isEmpty()); + + final CombinedFileHeader cfh = (CombinedFileHeader) p.getFiles().get(0); + + assertEquals("a", cfh.getOldName()); + assertSame(FileHeader.DEV_NULL, cfh.getNewName()); + + assertEquals(187, cfh.startOffset); + + assertEquals(2, cfh.getParentCount()); + assertSame(cfh.getOldId(0), cfh.getOldId()); + assertEquals("7898192", cfh.getOldId(0).name()); + assertEquals("2e65efe", cfh.getOldId(1).name()); + assertEquals("0000000", cfh.getNewId().name()); + + assertSame(cfh.getOldMode(0), cfh.getOldMode()); + assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(0)); + assertSame(FileMode.REGULAR_FILE, cfh.getOldMode(1)); + assertSame(FileMode.MISSING, cfh.getNewMode()); + assertSame(FileHeader.ChangeType.DELETE, cfh.getChangeType()); + assertSame(FileHeader.PatchType.UNIFIED, cfh.getPatchType()); + + assertTrue(cfh.getHunks().isEmpty()); + } + + private Patch parseTestPatchFile() throws IOException { + final String patchFile = getName() + ".patch"; + final InputStream in = getClass().getResourceAsStream(patchFile); + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } + try { + final Patch p = new Patch(); + p.parse(in); + return p; + } finally { + in.close(); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchErrorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchErrorTest.java new file mode 100644 index 0000000000..62a107130e --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchErrorTest.java @@ -0,0 +1,180 @@ +/* + * 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.patch; + +import java.io.IOException; +import java.io.InputStream; + +import junit.framework.TestCase; + +public class PatchErrorTest extends TestCase { + public void testError_DisconnectedHunk() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(1, p.getFiles().size()); + { + final FileHeader fh = p.getFiles().get(0); + assertEquals( + "org.eclipse.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java", + fh.getNewName()); + assertEquals(1, fh.getHunks().size()); + } + + assertEquals(1, p.getErrors().size()); + final FormatError e = p.getErrors().get(0); + assertSame(FormatError.Severity.ERROR, e.getSeverity()); + assertEquals("Hunk disconnected from file", e.getMessage()); + assertEquals(18, e.getOffset()); + assertTrue(e.getLineText().startsWith("@@ -109,4 +109,11 @@ assert")); + } + + public void testError_TruncatedOld() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(1, p.getFiles().size()); + assertEquals(1, p.getErrors().size()); + + final FormatError e = p.getErrors().get(0); + assertSame(FormatError.Severity.ERROR, e.getSeverity()); + assertEquals("Truncated hunk, at least 1 old lines is missing", e + .getMessage()); + assertEquals(313, e.getOffset()); + assertTrue(e.getLineText().startsWith("@@ -236,9 +236,9 @@ protected ")); + } + + public void testError_TruncatedNew() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(1, p.getFiles().size()); + assertEquals(1, p.getErrors().size()); + + final FormatError e = p.getErrors().get(0); + assertSame(FormatError.Severity.ERROR, e.getSeverity()); + assertEquals("Truncated hunk, at least 1 new lines is missing", e + .getMessage()); + assertEquals(313, e.getOffset()); + assertTrue(e.getLineText().startsWith("@@ -236,9 +236,9 @@ protected ")); + } + + public void testError_BodyTooLong() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(1, p.getFiles().size()); + assertEquals(1, p.getErrors().size()); + + final FormatError e = p.getErrors().get(0); + assertSame(FormatError.Severity.WARNING, e.getSeverity()); + assertEquals("Hunk header 4:11 does not match body line count of 4:12", + e.getMessage()); + assertEquals(349, e.getOffset()); + assertTrue(e.getLineText().startsWith("@@ -109,4 +109,11 @@ assert")); + } + + public void testError_GarbageBetweenFiles() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(2, p.getFiles().size()); + { + final FileHeader fh = p.getFiles().get(0); + assertEquals( + "org.eclipse.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java", + fh.getNewName()); + assertEquals(1, fh.getHunks().size()); + } + { + final FileHeader fh = p.getFiles().get(1); + assertEquals( + "org.eclipse.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java", + fh.getNewName()); + assertEquals(1, fh.getHunks().size()); + } + + assertEquals(1, p.getErrors().size()); + final FormatError e = p.getErrors().get(0); + assertSame(FormatError.Severity.WARNING, e.getSeverity()); + assertEquals("Unexpected hunk trailer", e.getMessage()); + assertEquals(926, e.getOffset()); + assertEquals("I AM NOT HERE\n", e.getLineText()); + } + + public void testError_GitBinaryNoForwardHunk() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(2, p.getFiles().size()); + { + final FileHeader fh = p.getFiles().get(0); + assertEquals("org.spearce.egit.ui/icons/toolbar/fetchd.png", fh + .getNewName()); + assertSame(FileHeader.PatchType.GIT_BINARY, fh.getPatchType()); + assertTrue(fh.getHunks().isEmpty()); + assertNull(fh.getForwardBinaryHunk()); + } + { + final FileHeader fh = p.getFiles().get(1); + assertEquals("org.spearce.egit.ui/icons/toolbar/fetche.png", fh + .getNewName()); + assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType()); + assertTrue(fh.getHunks().isEmpty()); + assertNull(fh.getForwardBinaryHunk()); + } + + assertEquals(1, p.getErrors().size()); + final FormatError e = p.getErrors().get(0); + assertSame(FormatError.Severity.ERROR, e.getSeverity()); + assertEquals("Missing forward-image in GIT binary patch", e + .getMessage()); + assertEquals(297, e.getOffset()); + assertEquals("\n", e.getLineText()); + } + + private Patch parseTestPatchFile() throws IOException { + final String patchFile = getName() + ".patch"; + final InputStream in = getClass().getResourceAsStream(patchFile); + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } + try { + final Patch p = new Patch(); + p.parse(in); + return p; + } finally { + in.close(); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchTest.java new file mode 100644 index 0000000000..52d6e27ca8 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchTest.java @@ -0,0 +1,358 @@ +/* + * 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.patch; + +import java.io.IOException; +import java.io.InputStream; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; + +public class PatchTest extends TestCase { + public void testEmpty() { + final Patch p = new Patch(); + assertTrue(p.getFiles().isEmpty()); + assertTrue(p.getErrors().isEmpty()); + } + + public void testParse_ConfigCaseInsensitive() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(2, p.getFiles().size()); + assertTrue(p.getErrors().isEmpty()); + + final FileHeader fRepositoryConfigTest = p.getFiles().get(0); + final FileHeader fRepositoryConfig = p.getFiles().get(1); + + assertEquals( + "org.eclipse.jgit.test/tst/org/spearce/jgit/lib/RepositoryConfigTest.java", + fRepositoryConfigTest.getNewName()); + + assertEquals( + "org.eclipse.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java", + fRepositoryConfig.getNewName()); + + assertEquals(572, fRepositoryConfigTest.startOffset); + assertEquals(1490, fRepositoryConfig.startOffset); + + assertEquals("da7e704", fRepositoryConfigTest.getOldId().name()); + assertEquals("34ce04a", fRepositoryConfigTest.getNewId().name()); + assertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfigTest + .getPatchType()); + assertSame(FileMode.REGULAR_FILE, fRepositoryConfigTest.getOldMode()); + assertSame(FileMode.REGULAR_FILE, fRepositoryConfigTest.getNewMode()); + assertEquals(1, fRepositoryConfigTest.getHunks().size()); + { + final HunkHeader h = fRepositoryConfigTest.getHunks().get(0); + assertSame(fRepositoryConfigTest, h.getFileHeader()); + assertEquals(921, h.startOffset); + assertEquals(109, h.getOldImage().getStartLine()); + assertEquals(4, h.getOldImage().getLineCount()); + assertEquals(109, h.getNewStartLine()); + assertEquals(11, h.getNewLineCount()); + + assertEquals(4, h.getLinesContext()); + assertEquals(7, h.getOldImage().getLinesAdded()); + assertEquals(0, h.getOldImage().getLinesDeleted()); + assertSame(fRepositoryConfigTest.getOldId(), h.getOldImage() + .getId()); + + assertEquals(1490, h.endOffset); + } + + assertEquals("45c2f8a", fRepositoryConfig.getOldId().name()); + assertEquals("3291bba", fRepositoryConfig.getNewId().name()); + assertSame(FileHeader.PatchType.UNIFIED, fRepositoryConfig + .getPatchType()); + assertSame(FileMode.REGULAR_FILE, fRepositoryConfig.getOldMode()); + assertSame(FileMode.REGULAR_FILE, fRepositoryConfig.getNewMode()); + assertEquals(3, fRepositoryConfig.getHunks().size()); + { + final HunkHeader h = fRepositoryConfig.getHunks().get(0); + assertSame(fRepositoryConfig, h.getFileHeader()); + assertEquals(1803, h.startOffset); + assertEquals(236, h.getOldImage().getStartLine()); + assertEquals(9, h.getOldImage().getLineCount()); + assertEquals(236, h.getNewStartLine()); + assertEquals(9, h.getNewLineCount()); + + assertEquals(7, h.getLinesContext()); + assertEquals(2, h.getOldImage().getLinesAdded()); + assertEquals(2, h.getOldImage().getLinesDeleted()); + assertSame(fRepositoryConfig.getOldId(), h.getOldImage().getId()); + + assertEquals(2434, h.endOffset); + } + { + final HunkHeader h = fRepositoryConfig.getHunks().get(1); + assertEquals(2434, h.startOffset); + assertEquals(300, h.getOldImage().getStartLine()); + assertEquals(7, h.getOldImage().getLineCount()); + assertEquals(300, h.getNewStartLine()); + assertEquals(7, h.getNewLineCount()); + + assertEquals(6, h.getLinesContext()); + assertEquals(1, h.getOldImage().getLinesAdded()); + assertEquals(1, h.getOldImage().getLinesDeleted()); + + assertEquals(2816, h.endOffset); + } + { + final HunkHeader h = fRepositoryConfig.getHunks().get(2); + assertEquals(2816, h.startOffset); + assertEquals(954, h.getOldImage().getStartLine()); + assertEquals(7, h.getOldImage().getLineCount()); + assertEquals(954, h.getNewStartLine()); + assertEquals(7, h.getNewLineCount()); + + assertEquals(6, h.getLinesContext()); + assertEquals(1, h.getOldImage().getLinesAdded()); + assertEquals(1, h.getOldImage().getLinesDeleted()); + + assertEquals(3035, h.endOffset); + } + } + + public void testParse_NoBinary() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(5, p.getFiles().size()); + assertTrue(p.getErrors().isEmpty()); + + for (int i = 0; i < 4; i++) { + final FileHeader fh = p.getFiles().get(i); + assertSame(FileHeader.ChangeType.ADD, fh.getChangeType()); + assertNotNull(fh.getOldId()); + assertNotNull(fh.getNewId()); + assertEquals("0000000", fh.getOldId().name()); + assertSame(FileMode.MISSING, fh.getOldMode()); + assertSame(FileMode.REGULAR_FILE, fh.getNewMode()); + assertTrue(fh.getNewName().startsWith( + "org.spearce.egit.ui/icons/toolbar/")); + assertSame(FileHeader.PatchType.BINARY, fh.getPatchType()); + assertTrue(fh.getHunks().isEmpty()); + assertTrue(fh.hasMetaDataChanges()); + + assertNull(fh.getForwardBinaryHunk()); + assertNull(fh.getReverseBinaryHunk()); + } + + final FileHeader fh = p.getFiles().get(4); + assertEquals("org.spearce.egit.ui/plugin.xml", fh.getNewName()); + assertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType()); + assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType()); + assertFalse(fh.hasMetaDataChanges()); + assertEquals("ee8a5a0", fh.getNewId().name()); + assertNull(fh.getForwardBinaryHunk()); + assertNull(fh.getReverseBinaryHunk()); + assertEquals(1, fh.getHunks().size()); + assertEquals(272, fh.getHunks().get(0).getOldImage().getStartLine()); + } + + public void testParse_GitBinaryLiteral() throws IOException { + final Patch p = parseTestPatchFile(); + final int[] binsizes = { 359, 393, 372, 404 }; + assertEquals(5, p.getFiles().size()); + assertTrue(p.getErrors().isEmpty()); + + for (int i = 0; i < 4; i++) { + final FileHeader fh = p.getFiles().get(i); + assertSame(FileHeader.ChangeType.ADD, fh.getChangeType()); + assertNotNull(fh.getOldId()); + assertNotNull(fh.getNewId()); + assertEquals(ObjectId.zeroId().name(), fh.getOldId().name()); + assertSame(FileMode.REGULAR_FILE, fh.getNewMode()); + assertTrue(fh.getNewName().startsWith( + "org.spearce.egit.ui/icons/toolbar/")); + assertSame(FileHeader.PatchType.GIT_BINARY, fh.getPatchType()); + assertTrue(fh.getHunks().isEmpty()); + assertTrue(fh.hasMetaDataChanges()); + + final BinaryHunk fwd = fh.getForwardBinaryHunk(); + final BinaryHunk rev = fh.getReverseBinaryHunk(); + assertNotNull(fwd); + assertNotNull(rev); + assertEquals(binsizes[i], fwd.getSize()); + assertEquals(0, rev.getSize()); + + assertSame(fh, fwd.getFileHeader()); + assertSame(fh, rev.getFileHeader()); + + assertSame(BinaryHunk.Type.LITERAL_DEFLATED, fwd.getType()); + assertSame(BinaryHunk.Type.LITERAL_DEFLATED, rev.getType()); + } + + final FileHeader fh = p.getFiles().get(4); + assertEquals("org.spearce.egit.ui/plugin.xml", fh.getNewName()); + assertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType()); + assertSame(FileHeader.PatchType.UNIFIED, fh.getPatchType()); + assertFalse(fh.hasMetaDataChanges()); + assertEquals("ee8a5a0", fh.getNewId().name()); + assertNull(fh.getForwardBinaryHunk()); + assertNull(fh.getReverseBinaryHunk()); + assertEquals(1, fh.getHunks().size()); + assertEquals(272, fh.getHunks().get(0).getOldImage().getStartLine()); + } + + public void testParse_GitBinaryDelta() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(1, p.getFiles().size()); + assertTrue(p.getErrors().isEmpty()); + + final FileHeader fh = p.getFiles().get(0); + assertTrue(fh.getNewName().startsWith("zero.bin")); + assertSame(FileHeader.ChangeType.MODIFY, fh.getChangeType()); + assertSame(FileHeader.PatchType.GIT_BINARY, fh.getPatchType()); + assertSame(FileMode.REGULAR_FILE, fh.getNewMode()); + + assertNotNull(fh.getOldId()); + assertNotNull(fh.getNewId()); + assertEquals("08e7df176454f3ee5eeda13efa0adaa54828dfd8", fh.getOldId() + .name()); + assertEquals("d70d8710b6d32ff844af0ee7c247e4b4b051867f", fh.getNewId() + .name()); + + assertTrue(fh.getHunks().isEmpty()); + assertFalse(fh.hasMetaDataChanges()); + + final BinaryHunk fwd = fh.getForwardBinaryHunk(); + final BinaryHunk rev = fh.getReverseBinaryHunk(); + assertNotNull(fwd); + assertNotNull(rev); + assertEquals(12, fwd.getSize()); + assertEquals(11, rev.getSize()); + + assertSame(fh, fwd.getFileHeader()); + assertSame(fh, rev.getFileHeader()); + + assertSame(BinaryHunk.Type.DELTA_DEFLATED, fwd.getType()); + assertSame(BinaryHunk.Type.DELTA_DEFLATED, rev.getType()); + + assertEquals(496, fh.endOffset); + } + + public void testParse_FixNoNewline() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(1, p.getFiles().size()); + assertTrue(p.getErrors().isEmpty()); + + final FileHeader f = p.getFiles().get(0); + + assertEquals("a", f.getNewName()); + assertEquals(252, f.startOffset); + + assertEquals("2e65efe", f.getOldId().name()); + assertEquals("f2ad6c7", f.getNewId().name()); + assertSame(FileHeader.PatchType.UNIFIED, f.getPatchType()); + assertSame(FileMode.REGULAR_FILE, f.getOldMode()); + assertSame(FileMode.REGULAR_FILE, f.getNewMode()); + assertEquals(1, f.getHunks().size()); + { + final HunkHeader h = f.getHunks().get(0); + assertSame(f, h.getFileHeader()); + assertEquals(317, h.startOffset); + assertEquals(1, h.getOldImage().getStartLine()); + assertEquals(1, h.getOldImage().getLineCount()); + assertEquals(1, h.getNewStartLine()); + assertEquals(1, h.getNewLineCount()); + + assertEquals(0, h.getLinesContext()); + assertEquals(1, h.getOldImage().getLinesAdded()); + assertEquals(1, h.getOldImage().getLinesDeleted()); + assertSame(f.getOldId(), h.getOldImage().getId()); + + assertEquals(363, h.endOffset); + } + } + + public void testParse_AddNoNewline() throws IOException { + final Patch p = parseTestPatchFile(); + assertEquals(1, p.getFiles().size()); + assertTrue(p.getErrors().isEmpty()); + + final FileHeader f = p.getFiles().get(0); + + assertEquals("a", f.getNewName()); + assertEquals(256, f.startOffset); + + assertEquals("f2ad6c7", f.getOldId().name()); + assertEquals("c59d9b6", f.getNewId().name()); + assertSame(FileHeader.PatchType.UNIFIED, f.getPatchType()); + assertSame(FileMode.REGULAR_FILE, f.getOldMode()); + assertSame(FileMode.REGULAR_FILE, f.getNewMode()); + assertEquals(1, f.getHunks().size()); + { + final HunkHeader h = f.getHunks().get(0); + assertSame(f, h.getFileHeader()); + assertEquals(321, h.startOffset); + assertEquals(1, h.getOldImage().getStartLine()); + assertEquals(1, h.getOldImage().getLineCount()); + assertEquals(1, h.getNewStartLine()); + assertEquals(1, h.getNewLineCount()); + + assertEquals(0, h.getLinesContext()); + assertEquals(1, h.getOldImage().getLinesAdded()); + assertEquals(1, h.getOldImage().getLinesDeleted()); + assertSame(f.getOldId(), h.getOldImage().getId()); + + assertEquals(367, h.endOffset); + } + } + + private Patch parseTestPatchFile() throws IOException { + final String patchFile = getName() + ".patch"; + final InputStream in = getClass().getResourceAsStream(patchFile); + if (in == null) { + fail("No " + patchFile + " test vector"); + return null; // Never happens + } + try { + final Patch p = new Patch(); + p.parse(in); + return p; + } finally { + in.close(); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/AlwaysEmptyRevQueueTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/AlwaysEmptyRevQueueTest.java new file mode 100644 index 0000000000..d752501c12 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/AlwaysEmptyRevQueueTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 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.revwalk; + +public class AlwaysEmptyRevQueueTest extends RevWalkTestCase { + private final AbstractRevQueue q = AbstractRevQueue.EMPTY_QUEUE; + + public void testEmpty() throws Exception { + assertNull(q.next()); + assertTrue(q.everbodyHasFlag(RevWalk.UNINTERESTING)); + assertFalse(q.anybodyHasFlag(RevWalk.UNINTERESTING)); + assertEquals(0, q.outputType()); + } + + public void testClear() throws Exception { + q.clear(); + testEmpty(); + } + + public void testAddFails() throws Exception { + try { + q.add(commit()); + fail("Did not throw UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + // expected result + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java new file mode 100644 index 0000000000..b3a92951b6 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 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.revwalk; + +public class DateRevQueueTest extends RevQueueTestCase<DateRevQueue> { + protected DateRevQueue create() { + return new DateRevQueue(); + } + + public void testEmpty() throws Exception { + super.testEmpty(); + assertNull(q.peek()); + assertEquals(Generator.SORT_COMMIT_TIME_DESC, q.outputType()); + } + + public void testCloneEmpty() throws Exception { + q = new DateRevQueue(AbstractRevQueue.EMPTY_QUEUE); + assertNull(q.next()); + } + + public void testInsertOutOfOrder() throws Exception { + final RevCommit a = parse(commit()); + final RevCommit b = parse(commit(10, a)); + final RevCommit c1 = parse(commit(5, b)); + final RevCommit c2 = parse(commit(-50, b)); + + q.add(c2); + q.add(a); + q.add(b); + q.add(c1); + + assertCommit(c1, q.next()); + assertCommit(b, q.next()); + assertCommit(a, q.next()); + assertCommit(c2, q.next()); + assertNull(q.next()); + } + + public void testInsertTie() throws Exception { + final RevCommit a = parse(commit()); + final RevCommit b = parse(commit(0, a)); + { + q = create(); + q.add(a); + q.add(b); + + assertCommit(a, q.next()); + assertCommit(b, q.next()); + assertNull(q.next()); + } + { + q = create(); + q.add(b); + q.add(a); + + assertCommit(b, q.next()); + assertCommit(a, q.next()); + assertNull(q.next()); + } + } + + public void testCloneFIFO() throws Exception { + final RevCommit a = parse(commit()); + final RevCommit b = parse(commit(200, a)); + final RevCommit c = parse(commit(200, b)); + + final FIFORevQueue src = new FIFORevQueue(); + src.add(a); + src.add(b); + src.add(c); + + q = new DateRevQueue(src); + assertFalse(q.everbodyHasFlag(RevWalk.UNINTERESTING)); + assertFalse(q.anybodyHasFlag(RevWalk.UNINTERESTING)); + assertCommit(c, q.peek()); + assertCommit(c, q.peek()); + + assertCommit(c, q.next()); + assertCommit(b, q.next()); + assertCommit(a, q.next()); + assertNull(q.next()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FIFORevQueueTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FIFORevQueueTest.java new file mode 100644 index 0000000000..3f4daab9f6 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FIFORevQueueTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 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.revwalk; + +import java.util.ArrayList; + +public class FIFORevQueueTest extends RevQueueTestCase<FIFORevQueue> { + protected FIFORevQueue create() { + return new FIFORevQueue(); + } + + public void testEmpty() throws Exception { + super.testEmpty(); + assertEquals(0, q.outputType()); + } + + public void testCloneEmpty() throws Exception { + q = new FIFORevQueue(AbstractRevQueue.EMPTY_QUEUE); + assertNull(q.next()); + } + + public void testAddLargeBlocks() throws Exception { + final ArrayList<RevCommit> lst = new ArrayList<RevCommit>(); + for (int i = 0; i < 3 * BlockRevQueue.Block.BLOCK_SIZE; i++) { + final RevCommit c = commit(); + lst.add(c); + q.add(c); + } + for (int i = 0; i < lst.size(); i++) + assertSame(lst.get(i), q.next()); + } + + public void testUnpopAtFront() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(); + final RevCommit c = commit(); + + q.add(a); + q.unpop(b); + q.unpop(c); + + assertSame(c, q.next()); + assertSame(b, q.next()); + assertSame(a, q.next()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java new file mode 100644 index 0000000000..d199f04ccb --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java @@ -0,0 +1,323 @@ +/* + * Copyright (C) 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.revwalk; + +import java.util.List; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.RepositoryTestCase; + +public class FooterLineTest extends RepositoryTestCase { + public void testNoFooters_EmptyBody() { + final RevCommit commit = parse(""); + final List<FooterLine> footers = commit.getFooterLines(); + assertNotNull(footers); + assertEquals(0, footers.size()); + } + + public void testNoFooters_NewlineOnlyBody1() { + final RevCommit commit = parse("\n"); + final List<FooterLine> footers = commit.getFooterLines(); + assertNotNull(footers); + assertEquals(0, footers.size()); + } + + public void testNoFooters_NewlineOnlyBody5() { + final RevCommit commit = parse("\n\n\n\n\n"); + final List<FooterLine> footers = commit.getFooterLines(); + assertNotNull(footers); + assertEquals(0, footers.size()); + } + + public void testNoFooters_OneLineBodyNoLF() { + final RevCommit commit = parse("this is a commit"); + final List<FooterLine> footers = commit.getFooterLines(); + assertNotNull(footers); + assertEquals(0, footers.size()); + } + + public void testNoFooters_OneLineBodyWithLF() { + final RevCommit commit = parse("this is a commit\n"); + final List<FooterLine> footers = commit.getFooterLines(); + assertNotNull(footers); + assertEquals(0, footers.size()); + } + + public void testNoFooters_ShortBodyNoLF() { + final RevCommit commit = parse("subject\n\nbody of commit"); + final List<FooterLine> footers = commit.getFooterLines(); + assertNotNull(footers); + assertEquals(0, footers.size()); + } + + public void testNoFooters_ShortBodyWithLF() { + final RevCommit commit = parse("subject\n\nbody of commit\n"); + final List<FooterLine> footers = commit.getFooterLines(); + assertNotNull(footers); + assertEquals(0, footers.size()); + } + + public void testSignedOffBy_OneUserNoLF() { + final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + + "Signed-off-by: A. U. Thor <a@example.com>"); + final List<FooterLine> footers = commit.getFooterLines(); + FooterLine f; + + assertNotNull(footers); + assertEquals(1, footers.size()); + + f = footers.get(0); + assertEquals("Signed-off-by", f.getKey()); + assertEquals("A. U. Thor <a@example.com>", f.getValue()); + assertEquals("a@example.com", f.getEmailAddress()); + } + + public void testSignedOffBy_OneUserWithLF() { + final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + + "Signed-off-by: A. U. Thor <a@example.com>\n"); + final List<FooterLine> footers = commit.getFooterLines(); + FooterLine f; + + assertNotNull(footers); + assertEquals(1, footers.size()); + + f = footers.get(0); + assertEquals("Signed-off-by", f.getKey()); + assertEquals("A. U. Thor <a@example.com>", f.getValue()); + assertEquals("a@example.com", f.getEmailAddress()); + } + + public void testSignedOffBy_IgnoreWhitespace() { + // We only ignore leading whitespace on the value, trailing + // is assumed part of the value. + // + final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + + "Signed-off-by: A. U. Thor <a@example.com> \n"); + final List<FooterLine> footers = commit.getFooterLines(); + FooterLine f; + + assertNotNull(footers); + assertEquals(1, footers.size()); + + f = footers.get(0); + assertEquals("Signed-off-by", f.getKey()); + assertEquals("A. U. Thor <a@example.com> ", f.getValue()); + assertEquals("a@example.com", f.getEmailAddress()); + } + + public void testEmptyValueNoLF() { + final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + + "Signed-off-by:"); + final List<FooterLine> footers = commit.getFooterLines(); + FooterLine f; + + assertNotNull(footers); + assertEquals(1, footers.size()); + + f = footers.get(0); + assertEquals("Signed-off-by", f.getKey()); + assertEquals("", f.getValue()); + assertNull(f.getEmailAddress()); + } + + public void testEmptyValueWithLF() { + final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + + "Signed-off-by:\n"); + final List<FooterLine> footers = commit.getFooterLines(); + FooterLine f; + + assertNotNull(footers); + assertEquals(1, footers.size()); + + f = footers.get(0); + assertEquals("Signed-off-by", f.getKey()); + assertEquals("", f.getValue()); + assertNull(f.getEmailAddress()); + } + + public void testShortKey() { + final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + + "K:V\n"); + final List<FooterLine> footers = commit.getFooterLines(); + FooterLine f; + + assertNotNull(footers); + assertEquals(1, footers.size()); + + f = footers.get(0); + assertEquals("K", f.getKey()); + assertEquals("V", f.getValue()); + assertNull(f.getEmailAddress()); + } + + public void testNonDelimtedEmail() { + final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + + "Acked-by: re@example.com\n"); + final List<FooterLine> footers = commit.getFooterLines(); + FooterLine f; + + assertNotNull(footers); + assertEquals(1, footers.size()); + + f = footers.get(0); + assertEquals("Acked-by", f.getKey()); + assertEquals("re@example.com", f.getValue()); + assertEquals("re@example.com", f.getEmailAddress()); + } + + public void testNotEmail() { + final RevCommit commit = parse("subject\n\nbody of commit\n" + "\n" + + "Acked-by: Main Tain Er\n"); + final List<FooterLine> footers = commit.getFooterLines(); + FooterLine f; + + assertNotNull(footers); + assertEquals(1, footers.size()); + + f = footers.get(0); + assertEquals("Acked-by", f.getKey()); + assertEquals("Main Tain Er", f.getValue()); + assertNull(f.getEmailAddress()); + } + + public void testSignedOffBy_ManyUsers() { + final RevCommit commit = parse("subject\n\nbody of commit\n" + + "Not-A-Footer-Line: this line must not be read as a footer\n" + + "\n" // paragraph break, now footers appear in final block + + "Signed-off-by: A. U. Thor <a@example.com>\n" + + "CC: <some.mailing.list@example.com>\n" + + "Acked-by: Some Reviewer <sr@example.com>\n" + + "Signed-off-by: Main Tain Er <mte@example.com>\n"); + final List<FooterLine> footers = commit.getFooterLines(); + FooterLine f; + + assertNotNull(footers); + assertEquals(4, footers.size()); + + f = footers.get(0); + assertEquals("Signed-off-by", f.getKey()); + assertEquals("A. U. Thor <a@example.com>", f.getValue()); + assertEquals("a@example.com", f.getEmailAddress()); + + f = footers.get(1); + assertEquals("CC", f.getKey()); + assertEquals("<some.mailing.list@example.com>", f.getValue()); + assertEquals("some.mailing.list@example.com", f.getEmailAddress()); + + f = footers.get(2); + assertEquals("Acked-by", f.getKey()); + assertEquals("Some Reviewer <sr@example.com>", f.getValue()); + assertEquals("sr@example.com", f.getEmailAddress()); + + f = footers.get(3); + assertEquals("Signed-off-by", f.getKey()); + assertEquals("Main Tain Er <mte@example.com>", f.getValue()); + assertEquals("mte@example.com", f.getEmailAddress()); + } + + public void testSignedOffBy_SkipNonFooter() { + final RevCommit commit = parse("subject\n\nbody of commit\n" + + "Not-A-Footer-Line: this line must not be read as a footer\n" + + "\n" // paragraph break, now footers appear in final block + + "Signed-off-by: A. U. Thor <a@example.com>\n" + + "CC: <some.mailing.list@example.com>\n" + + "not really a footer line but we'll skip it anyway\n" + + "Acked-by: Some Reviewer <sr@example.com>\n" + + "Signed-off-by: Main Tain Er <mte@example.com>\n"); + final List<FooterLine> footers = commit.getFooterLines(); + FooterLine f; + + assertNotNull(footers); + assertEquals(4, footers.size()); + + f = footers.get(0); + assertEquals("Signed-off-by", f.getKey()); + assertEquals("A. U. Thor <a@example.com>", f.getValue()); + + f = footers.get(1); + assertEquals("CC", f.getKey()); + assertEquals("<some.mailing.list@example.com>", f.getValue()); + + f = footers.get(2); + assertEquals("Acked-by", f.getKey()); + assertEquals("Some Reviewer <sr@example.com>", f.getValue()); + + f = footers.get(3); + assertEquals("Signed-off-by", f.getKey()); + assertEquals("Main Tain Er <mte@example.com>", f.getValue()); + } + + public void testFilterFootersIgnoreCase() { + final RevCommit commit = parse("subject\n\nbody of commit\n" + + "Not-A-Footer-Line: this line must not be read as a footer\n" + + "\n" // paragraph break, now footers appear in final block + + "Signed-Off-By: A. U. Thor <a@example.com>\n" + + "CC: <some.mailing.list@example.com>\n" + + "Acked-by: Some Reviewer <sr@example.com>\n" + + "signed-off-by: Main Tain Er <mte@example.com>\n"); + final List<String> footers = commit.getFooterLines("signed-off-by"); + + assertNotNull(footers); + assertEquals(2, footers.size()); + + assertEquals("A. U. Thor <a@example.com>", footers.get(0)); + assertEquals("Main Tain Er <mte@example.com>", footers.get(1)); + } + + private RevCommit parse(final String msg) { + final StringBuilder buf = new StringBuilder(); + buf.append("tree " + ObjectId.zeroId().name() + "\n"); + buf.append("author A. U. Thor <a@example.com> 1 +0000\n"); + buf.append("committer A. U. Thor <a@example.com> 1 +0000\n"); + buf.append("\n"); + buf.append(msg); + + final RevWalk walk = new RevWalk(db); + walk.setRetainBody(true); + final RevCommit c = new RevCommit(ObjectId.zeroId()); + c.parseCanonical(walk, Constants.encode(buf.toString())); + return c; + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/LIFORevQueueTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/LIFORevQueueTest.java new file mode 100644 index 0000000000..7676a71503 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/LIFORevQueueTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 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.revwalk; + +import java.util.ArrayList; +import java.util.Collections; + +public class LIFORevQueueTest extends RevQueueTestCase<LIFORevQueue> { + protected LIFORevQueue create() { + return new LIFORevQueue(); + } + + public void testEmpty() throws Exception { + super.testEmpty(); + assertEquals(0, q.outputType()); + } + + public void testCloneEmpty() throws Exception { + q = new LIFORevQueue(AbstractRevQueue.EMPTY_QUEUE); + assertNull(q.next()); + } + + public void testAddLargeBlocks() throws Exception { + final ArrayList<RevCommit> lst = new ArrayList<RevCommit>(); + for (int i = 0; i < 3 * BlockRevQueue.Block.BLOCK_SIZE; i++) { + final RevCommit c = commit(); + lst.add(c); + q.add(c); + } + Collections.reverse(lst); + for (int i = 0; i < lst.size(); i++) + assertSame(lst.get(i), q.next()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectWalkTest.java new file mode 100644 index 0000000000..7dddeee205 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectWalkTest.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 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.revwalk; + +public class ObjectWalkTest extends RevWalkTestCase { + protected ObjectWalk objw; + + protected RevWalk createRevWalk() { + return objw = new ObjectWalk(db); + } + + public void testNoCommits() throws Exception { + assertNull(objw.next()); + assertNull(objw.nextObject()); + } + + public void testTwoCommitsEmptyTree() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + markStart(b); + + assertCommit(b, objw.next()); + assertCommit(a, objw.next()); + assertNull(objw.next()); + + assertSame(emptyTree, objw.nextObject()); + assertNull(objw.nextObject()); + } + + public void testOneCommitOneTreeTwoBlob() throws Exception { + final RevBlob f0 = blob("0"); + final RevBlob f1 = blob("1"); + final RevTree t = tree(file("0", f0), file("1", f1), file("2", f1)); + final RevCommit a = commit(t); + markStart(a); + + assertCommit(a, objw.next()); + assertNull(objw.next()); + + assertSame(t, objw.nextObject()); + assertSame(f0, objw.nextObject()); + assertSame(f1, objw.nextObject()); + assertNull(objw.nextObject()); + } + + public void testTwoCommitTwoTreeTwoBlob() throws Exception { + final RevBlob f0 = blob("0"); + final RevBlob f1 = blob("1"); + final RevBlob f2 = blob("0v2"); + final RevTree ta = tree(file("0", f0), file("1", f1), file("2", f1)); + final RevTree tb = tree(file("0", f2), file("1", f1), file("2", f1)); + final RevCommit a = commit(ta); + final RevCommit b = commit(tb, a); + markStart(b); + + assertCommit(b, objw.next()); + assertCommit(a, objw.next()); + assertNull(objw.next()); + + assertSame(tb, objw.nextObject()); + assertSame(f2, objw.nextObject()); + assertSame(f1, objw.nextObject()); + + assertSame(ta, objw.nextObject()); + assertSame(f0, objw.nextObject()); + + assertNull(objw.nextObject()); + } + + public void testTwoCommitDeepTree1() throws Exception { + final RevBlob f0 = blob("0"); + final RevBlob f1 = blob("0v2"); + final RevTree ta = tree(file("a/b/0", f0)); + final RevTree tb = tree(file("a/b/1", f1)); + final RevCommit a = commit(ta); + final RevCommit b = commit(tb, a); + markStart(b); + + assertCommit(b, objw.next()); + assertCommit(a, objw.next()); + assertNull(objw.next()); + + assertSame(tb, objw.nextObject()); + assertSame(get(tb, "a"), objw.nextObject()); + assertSame(get(tb, "a/b"), objw.nextObject()); + assertSame(f1, objw.nextObject()); + + assertSame(ta, objw.nextObject()); + assertSame(get(ta, "a"), objw.nextObject()); + assertSame(get(ta, "a/b"), objw.nextObject()); + assertSame(f0, objw.nextObject()); + + assertNull(objw.nextObject()); + } + + public void testTwoCommitDeepTree2() throws Exception { + final RevBlob f1 = blob("1"); + final RevTree ta = tree(file("a/b/0", f1), file("a/c/q", f1)); + final RevTree tb = tree(file("a/b/1", f1), file("a/c/q", f1)); + final RevCommit a = commit(ta); + final RevCommit b = commit(tb, a); + markStart(b); + + assertCommit(b, objw.next()); + assertCommit(a, objw.next()); + assertNull(objw.next()); + + assertSame(tb, objw.nextObject()); + assertSame(get(tb, "a"), objw.nextObject()); + assertSame(get(tb, "a/b"), objw.nextObject()); + assertSame(f1, objw.nextObject()); + assertSame(get(tb, "a/c"), objw.nextObject()); + + assertSame(ta, objw.nextObject()); + assertSame(get(ta, "a"), objw.nextObject()); + assertSame(get(ta, "a/b"), objw.nextObject()); + + assertNull(objw.nextObject()); + } + + public void testCull() throws Exception { + final RevBlob f1 = blob("1"); + final RevBlob f2 = blob("2"); + final RevBlob f3 = blob("3"); + final RevBlob f4 = blob("4"); + + final RevTree ta = tree(file("a/1", f1), file("c/3", f3)); + final RevCommit a = commit(ta); + + final RevTree tb = tree(file("a/1", f2), file("c/3", f3)); + final RevCommit b1 = commit(tb, a); + final RevCommit b2 = commit(tb, b1); + + final RevTree tc = tree(file("a/1", f4)); + final RevCommit c1 = commit(tc, a); + final RevCommit c2 = commit(tc, c1); + + markStart(b2); + markUninteresting(c2); + + assertCommit(b2, objw.next()); + assertCommit(b1, objw.next()); + assertNull(objw.next()); + + assertTrue(a.has(RevFlag.UNINTERESTING)); + assertTrue(ta.has(RevFlag.UNINTERESTING)); + assertTrue(f1.has(RevFlag.UNINTERESTING)); + assertTrue(f3.has(RevFlag.UNINTERESTING)); + + assertSame(tb, objw.nextObject()); + assertSame(get(tb, "a"), objw.nextObject()); + assertSame(f2, objw.nextObject()); + assertNull(objw.nextObject()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java new file mode 100644 index 0000000000..b7e84419c9 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java @@ -0,0 +1,321 @@ +/* + * 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.revwalk; + +import java.io.ByteArrayOutputStream; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.RepositoryTestCase; + +public class RevCommitParseTest extends RepositoryTestCase { + public void testParse_NoParents() throws Exception { + final ObjectId treeId = id("9788669ad918b6fcce64af8882fc9a81cb6aba67"); + final String authorName = "A U. Thor"; + final String authorEmail = "a_u_thor@example.com"; + final int authorTime = 1218123387; + + final String committerName = "C O. Miter"; + final String committerEmail = "comiter@example.com"; + final int committerTime = 1218123390; + final StringBuilder body = new StringBuilder(); + + body.append("tree "); + body.append(treeId.name()); + body.append("\n"); + + body.append("author "); + body.append(authorName); + body.append(" <"); + body.append(authorEmail); + body.append("> "); + body.append(authorTime); + body.append(" +0700\n"); + + body.append("committer "); + body.append(committerName); + body.append(" <"); + body.append(committerEmail); + body.append("> "); + body.append(committerTime); + body.append(" -0500\n"); + + body.append("\n"); + + final RevWalk rw = new RevWalk(db); + final RevCommit c; + + c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); + assertNull(c.getTree()); + assertNull(c.parents); + + c.parseCanonical(rw, body.toString().getBytes("UTF-8")); + assertNotNull(c.getTree()); + assertEquals(treeId, c.getTree().getId()); + assertSame(rw.lookupTree(treeId), c.getTree()); + + assertNotNull(c.parents); + assertEquals(0, c.parents.length); + assertEquals("", c.getFullMessage()); + + final PersonIdent cAuthor = c.getAuthorIdent(); + assertNotNull(cAuthor); + assertEquals(authorName, cAuthor.getName()); + assertEquals(authorEmail, cAuthor.getEmailAddress()); + + final PersonIdent cCommitter = c.getCommitterIdent(); + assertNotNull(cCommitter); + assertEquals(committerName, cCommitter.getName()); + assertEquals(committerEmail, cCommitter.getEmailAddress()); + } + + private RevCommit create(final String msg) throws Exception { + final StringBuilder b = new StringBuilder(); + b.append("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"); + b.append("author A U. Thor <a_u_thor@example.com> 1218123387 +0700\n"); + b.append("committer C O. Miter <c@example.com> 1218123390 -0500\n"); + b.append("\n"); + b.append(msg); + + final RevCommit c; + c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); + c.parseCanonical(new RevWalk(db), b.toString().getBytes("UTF-8")); + return c; + } + + public void testParse_WeirdHeaderOnlyCommit() throws Exception { + final StringBuilder b = new StringBuilder(); + b.append("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"); + b.append("author A U. Thor <a_u_thor@example.com> 1218123387 +0700\n"); + b.append("committer C O. Miter <c@example.com> 1218123390 -0500\n"); + + final RevCommit c; + c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); + c.parseCanonical(new RevWalk(db), b.toString().getBytes("UTF-8")); + + assertEquals("", c.getFullMessage()); + assertEquals("", c.getShortMessage()); + } + + public void testParse_implicit_UTF8_encoded() throws Exception { + final ByteArrayOutputStream b = new ByteArrayOutputStream(); + b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes("UTF-8")); + b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n".getBytes("UTF-8")); + b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("Sm\u00f6rg\u00e5sbord\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("\u304d\u308c\u3044\n".getBytes("UTF-8")); + final RevCommit c; + c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); // bogus id + c.parseCanonical(new RevWalk(db), b.toByteArray()); + + assertSame(Constants.CHARSET, c.getEncoding()); + assertEquals("F\u00f6r fattare", c.getAuthorIdent().getName()); + assertEquals("Sm\u00f6rg\u00e5sbord", c.getShortMessage()); + assertEquals("Sm\u00f6rg\u00e5sbord\n\n\u304d\u308c\u3044\n", c.getFullMessage()); + } + + public void testParse_implicit_mixed_encoded() throws Exception { + final ByteArrayOutputStream b = new ByteArrayOutputStream(); + b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes("UTF-8")); + b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n".getBytes("ISO-8859-1")); + b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("Sm\u00f6rg\u00e5sbord\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("\u304d\u308c\u3044\n".getBytes("UTF-8")); + final RevCommit c; + c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); // bogus id + c.parseCanonical(new RevWalk(db), b.toByteArray()); + + assertSame(Constants.CHARSET, c.getEncoding()); + assertEquals("F\u00f6r fattare", c.getAuthorIdent().getName()); + assertEquals("Sm\u00f6rg\u00e5sbord", c.getShortMessage()); + assertEquals("Sm\u00f6rg\u00e5sbord\n\n\u304d\u308c\u3044\n", c.getFullMessage()); + } + + /** + * Test parsing of a commit whose encoding is given and works. + * + * @throws Exception + */ + public void testParse_explicit_encoded() throws Exception { + final ByteArrayOutputStream b = new ByteArrayOutputStream(); + b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes("EUC-JP")); + b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n".getBytes("EUC-JP")); + b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n".getBytes("EUC-JP")); + b.write("encoding euc_JP\n".getBytes("EUC-JP")); + b.write("\n".getBytes("EUC-JP")); + b.write("\u304d\u308c\u3044\n".getBytes("EUC-JP")); + b.write("\n".getBytes("EUC-JP")); + b.write("Hi\n".getBytes("EUC-JP")); + final RevCommit c; + c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); // bogus id + c.parseCanonical(new RevWalk(db), b.toByteArray()); + + assertEquals("EUC-JP", c.getEncoding().name()); + assertEquals("F\u00f6r fattare", c.getAuthorIdent().getName()); + assertEquals("\u304d\u308c\u3044", c.getShortMessage()); + assertEquals("\u304d\u308c\u3044\n\nHi\n", c.getFullMessage()); + } + + /** + * This is a twisted case, but show what we expect here. We can revise the + * expectations provided this case is updated. + * + * What happens here is that an encoding us given, but data is not encoded + * that way (and we can detect it), so we try other encodings. + * + * @throws Exception + */ + public void testParse_explicit_bad_encoded() throws Exception { + final ByteArrayOutputStream b = new ByteArrayOutputStream(); + b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes("UTF-8")); + b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n".getBytes("ISO-8859-1")); + b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n".getBytes("UTF-8")); + b.write("encoding EUC-JP\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("\u304d\u308c\u3044\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("Hi\n".getBytes("UTF-8")); + final RevCommit c; + c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); // bogus id + c.parseCanonical(new RevWalk(db), b.toByteArray()); + + assertEquals("EUC-JP", c.getEncoding().name()); + assertEquals("F\u00f6r fattare", c.getAuthorIdent().getName()); + assertEquals("\u304d\u308c\u3044", c.getShortMessage()); + assertEquals("\u304d\u308c\u3044\n\nHi\n", c.getFullMessage()); + } + + /** + * This is a twisted case too, but show what we expect here. We can revise the + * expectations provided this case is updated. + * + * What happens here is that an encoding us given, but data is not encoded + * that way (and we can detect it), so we try other encodings. Here data could + * actually be decoded in the stated encoding, but we override using UTF-8. + * + * @throws Exception + */ + public void testParse_explicit_bad_encoded2() throws Exception { + final ByteArrayOutputStream b = new ByteArrayOutputStream(); + b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes("UTF-8")); + b.write("author F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n".getBytes("UTF-8")); + b.write("committer C O. Miter <c@example.com> 1218123390 -0500\n".getBytes("UTF-8")); + b.write("encoding ISO-8859-1\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("\u304d\u308c\u3044\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("Hi\n".getBytes("UTF-8")); + final RevCommit c; + c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); // bogus id + c.parseCanonical(new RevWalk(db), b.toByteArray()); + + assertEquals("ISO-8859-1", c.getEncoding().name()); + assertEquals("F\u00f6r fattare", c.getAuthorIdent().getName()); + assertEquals("\u304d\u308c\u3044", c.getShortMessage()); + assertEquals("\u304d\u308c\u3044\n\nHi\n", c.getFullMessage()); + } + + public void testParse_NoMessage() throws Exception { + final String msg = ""; + final RevCommit c = create(msg); + assertEquals(msg, c.getFullMessage()); + assertEquals(msg, c.getShortMessage()); + } + + public void testParse_OnlyLFMessage() throws Exception { + final RevCommit c = create("\n"); + assertEquals("\n", c.getFullMessage()); + assertEquals("", c.getShortMessage()); + } + + public void testParse_ShortLineOnlyNoLF() throws Exception { + final String shortMsg = "This is a short message."; + final RevCommit c = create(shortMsg); + assertEquals(shortMsg, c.getFullMessage()); + assertEquals(shortMsg, c.getShortMessage()); + } + + public void testParse_ShortLineOnlyEndLF() throws Exception { + final String shortMsg = "This is a short message."; + final String fullMsg = shortMsg + "\n"; + final RevCommit c = create(fullMsg); + assertEquals(fullMsg, c.getFullMessage()); + assertEquals(shortMsg, c.getShortMessage()); + } + + public void testParse_ShortLineOnlyEmbeddedLF() throws Exception { + final String fullMsg = "This is a\nshort message."; + final String shortMsg = fullMsg.replace('\n', ' '); + final RevCommit c = create(fullMsg); + assertEquals(fullMsg, c.getFullMessage()); + assertEquals(shortMsg, c.getShortMessage()); + } + + public void testParse_ShortLineOnlyEmbeddedAndEndingLF() throws Exception { + final String fullMsg = "This is a\nshort message.\n"; + final String shortMsg = "This is a short message."; + final RevCommit c = create(fullMsg); + assertEquals(fullMsg, c.getFullMessage()); + assertEquals(shortMsg, c.getShortMessage()); + } + + public void testParse_GitStyleMessage() throws Exception { + final String shortMsg = "This fixes a bug."; + final String body = "We do it with magic and pixie dust and stuff.\n" + + "\n" + "Signed-off-by: A U. Thor <author@example.com>\n"; + final String fullMsg = shortMsg + "\n" + "\n" + body; + final RevCommit c = create(fullMsg); + assertEquals(fullMsg, c.getFullMessage()); + assertEquals(shortMsg, c.getShortMessage()); + } + + private static ObjectId id(final String str) { + return ObjectId.fromString(str); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevFlagSetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevFlagSetTest.java new file mode 100644 index 0000000000..13f1cfc4cc --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevFlagSetTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 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.revwalk; + +import java.util.Arrays; +import java.util.Iterator; + +public class RevFlagSetTest extends RevWalkTestCase { + public void testEmpty() { + final RevFlagSet set = new RevFlagSet(); + assertEquals(0, set.mask); + assertEquals(0, set.size()); + assertNotNull(set.iterator()); + assertFalse(set.iterator().hasNext()); + } + + public void testAddOne() { + final String flagName = "flag"; + final RevFlag flag = rw.newFlag(flagName); + assertTrue(0 != flag.mask); + assertSame(flagName, flag.name); + + final RevFlagSet set = new RevFlagSet(); + assertTrue(set.add(flag)); + assertFalse(set.add(flag)); + assertEquals(flag.mask, set.mask); + assertEquals(1, set.size()); + final Iterator<RevFlag> i = set.iterator(); + assertTrue(i.hasNext()); + assertSame(flag, i.next()); + assertFalse(i.hasNext()); + } + + public void testAddTwo() { + final RevFlag flag1 = rw.newFlag("flag_1"); + final RevFlag flag2 = rw.newFlag("flag_2"); + assertTrue((flag1.mask & flag2.mask) == 0); + + final RevFlagSet set = new RevFlagSet(); + assertTrue(set.add(flag1)); + assertTrue(set.add(flag2)); + assertEquals(flag1.mask | flag2.mask, set.mask); + assertEquals(2, set.size()); + } + + public void testContainsAll() { + final RevFlag flag1 = rw.newFlag("flag_1"); + final RevFlag flag2 = rw.newFlag("flag_2"); + final RevFlagSet set1 = new RevFlagSet(); + assertTrue(set1.add(flag1)); + assertTrue(set1.add(flag2)); + + assertTrue(set1.containsAll(set1)); + assertTrue(set1.containsAll(Arrays + .asList(new RevFlag[] { flag1, flag2 }))); + + final RevFlagSet set2 = new RevFlagSet(); + set2.add(rw.newFlag("flag_3")); + assertFalse(set1.containsAll(set2)); + } + + public void testEquals() { + final RevFlag flag1 = rw.newFlag("flag_1"); + final RevFlag flag2 = rw.newFlag("flag_2"); + final RevFlagSet set = new RevFlagSet(); + assertTrue(set.add(flag1)); + assertTrue(set.add(flag2)); + + assertTrue(new RevFlagSet(set).equals(set)); + assertTrue(new RevFlagSet(Arrays.asList(new RevFlag[] { flag1, flag2 })) + .equals(set)); + } + + public void testRemove() { + final RevFlag flag1 = rw.newFlag("flag_1"); + final RevFlag flag2 = rw.newFlag("flag_2"); + final RevFlagSet set = new RevFlagSet(); + assertTrue(set.add(flag1)); + assertTrue(set.add(flag2)); + + assertTrue(set.remove(flag1)); + assertFalse(set.remove(flag1)); + assertEquals(flag2.mask, set.mask); + assertFalse(set.contains(flag1)); + } + + public void testContains() { + final RevFlag flag1 = rw.newFlag("flag_1"); + final RevFlag flag2 = rw.newFlag("flag_2"); + final RevFlagSet set = new RevFlagSet(); + set.add(flag1); + assertTrue(set.contains(flag1)); + assertFalse(set.contains(flag2)); + assertFalse(set.contains("bob")); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevObjectTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevObjectTest.java new file mode 100644 index 0000000000..87ecaa8c53 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevObjectTest.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 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.revwalk; + +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.Constants; + +public class RevObjectTest extends RevWalkTestCase { + public void testId() throws Exception { + final RevCommit a = commit(); + assertSame(a, a.getId()); + } + + public void testEqualsIsIdentity() throws Exception { + final RevCommit a1 = commit(); + final RevCommit b1 = commit(); + + assertTrue(a1.equals(a1)); + assertTrue(a1.equals((Object) a1)); + assertFalse(a1.equals(b1)); + + assertFalse(a1.equals(a1.copy())); + assertFalse(a1.equals((Object) a1.copy())); + assertFalse(a1.equals("")); + + final RevWalk rw2 = new RevWalk(db); + final RevCommit a2 = rw2.parseCommit(a1); + final RevCommit b2 = rw2.parseCommit(b1); + assertNotSame(a1, a2); + assertNotSame(b1, b2); + + assertFalse(a1.equals(a2)); + assertFalse(b1.equals(b2)); + + assertEquals(a1.hashCode(), a2.hashCode()); + assertEquals(b1.hashCode(), b2.hashCode()); + + assertTrue(AnyObjectId.equals(a1, a2)); + assertTrue(AnyObjectId.equals(b1, b2)); + } + + public void testRevObjectTypes() throws Exception { + assertEquals(Constants.OBJ_TREE, emptyTree.getType()); + assertEquals(Constants.OBJ_COMMIT, commit().getType()); + assertEquals(Constants.OBJ_BLOB, blob("").getType()); + assertEquals(Constants.OBJ_TAG, tag("emptyTree", emptyTree).getType()); + } + + public void testHasRevFlag() throws Exception { + final RevCommit a = commit(); + assertFalse(a.has(RevFlag.UNINTERESTING)); + a.flags |= RevWalk.UNINTERESTING; + assertTrue(a.has(RevFlag.UNINTERESTING)); + } + + public void testHasAnyFlag() throws Exception { + final RevCommit a = commit(); + final RevFlag flag1 = rw.newFlag("flag1"); + final RevFlag flag2 = rw.newFlag("flag2"); + final RevFlagSet s = new RevFlagSet(); + s.add(flag1); + s.add(flag2); + + assertFalse(a.hasAny(s)); + a.flags |= flag1.mask; + assertTrue(a.hasAny(s)); + } + + public void testHasAllFlag() throws Exception { + final RevCommit a = commit(); + final RevFlag flag1 = rw.newFlag("flag1"); + final RevFlag flag2 = rw.newFlag("flag2"); + final RevFlagSet s = new RevFlagSet(); + s.add(flag1); + s.add(flag2); + + assertFalse(a.hasAll(s)); + a.flags |= flag1.mask; + assertFalse(a.hasAll(s)); + a.flags |= flag2.mask; + assertTrue(a.hasAll(s)); + } + + public void testAddRevFlag() throws Exception { + final RevCommit a = commit(); + final RevFlag flag1 = rw.newFlag("flag1"); + final RevFlag flag2 = rw.newFlag("flag2"); + assertEquals(0, a.flags); + + a.add(flag1); + assertEquals(flag1.mask, a.flags); + + a.add(flag2); + assertEquals(flag1.mask | flag2.mask, a.flags); + } + + public void testAddRevFlagSet() throws Exception { + final RevCommit a = commit(); + final RevFlag flag1 = rw.newFlag("flag1"); + final RevFlag flag2 = rw.newFlag("flag2"); + final RevFlagSet s = new RevFlagSet(); + s.add(flag1); + s.add(flag2); + + assertEquals(0, a.flags); + + a.add(s); + assertEquals(flag1.mask | flag2.mask, a.flags); + } + + public void testRemoveRevFlag() throws Exception { + final RevCommit a = commit(); + final RevFlag flag1 = rw.newFlag("flag1"); + final RevFlag flag2 = rw.newFlag("flag2"); + a.add(flag1); + a.add(flag2); + assertEquals(flag1.mask | flag2.mask, a.flags); + a.remove(flag2); + assertEquals(flag1.mask, a.flags); + } + + public void testRemoveRevFlagSet() throws Exception { + final RevCommit a = commit(); + final RevFlag flag1 = rw.newFlag("flag1"); + final RevFlag flag2 = rw.newFlag("flag2"); + final RevFlag flag3 = rw.newFlag("flag3"); + final RevFlagSet s = new RevFlagSet(); + s.add(flag1); + s.add(flag2); + a.add(flag3); + a.add(s); + assertEquals(flag1.mask | flag2.mask | flag3.mask, a.flags); + a.remove(s); + assertEquals(flag3.mask, a.flags); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevQueueTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevQueueTestCase.java new file mode 100644 index 0000000000..24e84b041d --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevQueueTestCase.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 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.revwalk; + +public abstract class RevQueueTestCase<T extends AbstractRevQueue> extends + RevWalkTestCase { + protected T q; + + public void setUp() throws Exception { + super.setUp(); + q = create(); + } + + protected abstract T create(); + + public void testEmpty() throws Exception { + assertNull(q.next()); + assertTrue(q.everbodyHasFlag(RevWalk.UNINTERESTING)); + assertFalse(q.anybodyHasFlag(RevWalk.UNINTERESTING)); + } + + public void testClear() throws Exception { + final RevCommit a = parse(commit()); + final RevCommit b = parse(commit(a)); + + q.add(a); + q.add(b); + q.clear(); + assertNull(q.next()); + } + + public void testHasFlags() throws Exception { + final RevCommit a = parse(commit()); + final RevCommit b = parse(commit(a)); + + q.add(a); + q.add(b); + + assertFalse(q.everbodyHasFlag(RevWalk.UNINTERESTING)); + assertFalse(q.anybodyHasFlag(RevWalk.UNINTERESTING)); + + a.flags |= RevWalk.UNINTERESTING; + assertFalse(q.everbodyHasFlag(RevWalk.UNINTERESTING)); + assertTrue(q.anybodyHasFlag(RevWalk.UNINTERESTING)); + + b.flags |= RevWalk.UNINTERESTING; + assertTrue(q.everbodyHasFlag(RevWalk.UNINTERESTING)); + assertTrue(q.anybodyHasFlag(RevWalk.UNINTERESTING)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java new file mode 100644 index 0000000000..8800536d27 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java @@ -0,0 +1,356 @@ +/* + * 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.revwalk; + +import java.io.ByteArrayOutputStream; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.RepositoryTestCase; + +public class RevTagParseTest extends RepositoryTestCase { + public void testTagBlob() throws Exception { + testOneType(Constants.OBJ_BLOB); + } + + public void testTagTree() throws Exception { + testOneType(Constants.OBJ_TREE); + } + + public void testTagCommit() throws Exception { + testOneType(Constants.OBJ_COMMIT); + } + + public void testTagTag() throws Exception { + testOneType(Constants.OBJ_TAG); + } + + private void testOneType(final int typeCode) throws Exception { + final ObjectId id = id("9788669ad918b6fcce64af8882fc9a81cb6aba67"); + final StringBuilder b = new StringBuilder(); + b.append("object " + id.name() + "\n"); + b.append("type " + Constants.typeString(typeCode) + "\n"); + b.append("tag v1.2.3.4.5\n"); + b.append("tagger A U. Thor <a_u_thor@example.com> 1218123387 +0700\n"); + b.append("\n"); + + final RevWalk rw = new RevWalk(db); + final RevTag c; + + c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); + assertNull(c.getObject()); + assertNull(c.getTagName()); + + c.parseCanonical(rw, b.toString().getBytes("UTF-8")); + assertNotNull(c.getObject()); + assertEquals(id, c.getObject().getId()); + assertSame(rw.lookupAny(id, typeCode), c.getObject()); + } + + public void testParseAllFields() throws Exception { + final ObjectId treeId = id("9788669ad918b6fcce64af8882fc9a81cb6aba67"); + final String name = "v1.2.3.4.5"; + final String taggerName = "A U. Thor"; + final String taggerEmail = "a_u_thor@example.com"; + final int taggerTime = 1218123387; + + final StringBuilder body = new StringBuilder(); + + body.append("object "); + body.append(treeId.name()); + body.append("\n"); + + body.append("type tree\n"); + + body.append("tag "); + body.append(name); + body.append("\n"); + + body.append("tagger "); + body.append(taggerName); + body.append(" <"); + body.append(taggerEmail); + body.append("> "); + body.append(taggerTime); + body.append(" +0700\n"); + + body.append("\n"); + + final RevWalk rw = new RevWalk(db); + final RevTag c; + + c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); + assertNull(c.getObject()); + assertNull(c.getTagName()); + + c.parseCanonical(rw, body.toString().getBytes("UTF-8")); + assertNotNull(c.getObject()); + assertEquals(treeId, c.getObject().getId()); + assertSame(rw.lookupTree(treeId), c.getObject()); + + assertNotNull(c.getTagName()); + assertEquals(name, c.getTagName()); + assertEquals("", c.getFullMessage()); + + final PersonIdent cTagger = c.getTaggerIdent(); + assertNotNull(cTagger); + assertEquals(taggerName, cTagger.getName()); + assertEquals(taggerEmail, cTagger.getEmailAddress()); + } + + private RevTag create(final String msg) throws Exception { + final StringBuilder b = new StringBuilder(); + b.append("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"); + b.append("type tree\n"); + b.append("tag v1.2.3.4.5\n"); + b.append("tagger A U. Thor <a_u_thor@example.com> 1218123387 +0700\n"); + b.append("\n"); + b.append(msg); + + final RevTag c; + c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); + c.parseCanonical(new RevWalk(db), b.toString().getBytes("UTF-8")); + return c; + } + + public void testParse_implicit_UTF8_encoded() throws Exception { + final ByteArrayOutputStream b = new ByteArrayOutputStream(); + b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n" + .getBytes("UTF-8")); + b.write("type tree\n".getBytes("UTF-8")); + b.write("tag v1.2.3.4.5\n".getBytes("UTF-8")); + + b + .write("tagger F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n" + .getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("Sm\u00f6rg\u00e5sbord\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("\u304d\u308c\u3044\n".getBytes("UTF-8")); + final RevTag c; + c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); + c.parseCanonical(new RevWalk(db), b.toByteArray()); + + assertEquals("F\u00f6r fattare", c.getTaggerIdent().getName()); + assertEquals("Sm\u00f6rg\u00e5sbord", c.getShortMessage()); + assertEquals("Sm\u00f6rg\u00e5sbord\n\n\u304d\u308c\u3044\n", c + .getFullMessage()); + } + + public void testParse_implicit_mixed_encoded() throws Exception { + final ByteArrayOutputStream b = new ByteArrayOutputStream(); + b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n" + .getBytes("UTF-8")); + b.write("type tree\n".getBytes("UTF-8")); + b.write("tag v1.2.3.4.5\n".getBytes("UTF-8")); + b + .write("tagger F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n" + .getBytes("ISO-8859-1")); + b.write("\n".getBytes("UTF-8")); + b.write("Sm\u00f6rg\u00e5sbord\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("\u304d\u308c\u3044\n".getBytes("UTF-8")); + final RevTag c; + c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); + c.parseCanonical(new RevWalk(db), b.toByteArray()); + + assertEquals("F\u00f6r fattare", c.getTaggerIdent().getName()); + assertEquals("Sm\u00f6rg\u00e5sbord", c.getShortMessage()); + assertEquals("Sm\u00f6rg\u00e5sbord\n\n\u304d\u308c\u3044\n", c + .getFullMessage()); + } + + /** + * Test parsing of a commit whose encoding is given and works. + * + * @throws Exception + */ + public void testParse_explicit_encoded() throws Exception { + final ByteArrayOutputStream b = new ByteArrayOutputStream(); + b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n" + .getBytes("EUC-JP")); + b.write("type tree\n".getBytes("EUC-JP")); + b.write("tag v1.2.3.4.5\n".getBytes("EUC-JP")); + b + .write("tagger F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n" + .getBytes("EUC-JP")); + b.write("encoding euc_JP\n".getBytes("EUC-JP")); + b.write("\n".getBytes("EUC-JP")); + b.write("\u304d\u308c\u3044\n".getBytes("EUC-JP")); + b.write("\n".getBytes("EUC-JP")); + b.write("Hi\n".getBytes("EUC-JP")); + final RevTag c; + c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); + c.parseCanonical(new RevWalk(db), b.toByteArray()); + + assertEquals("F\u00f6r fattare", c.getTaggerIdent().getName()); + assertEquals("\u304d\u308c\u3044", c.getShortMessage()); + assertEquals("\u304d\u308c\u3044\n\nHi\n", c.getFullMessage()); + } + + /** + * This is a twisted case, but show what we expect here. We can revise the + * expectations provided this case is updated. + * + * What happens here is that an encoding us given, but data is not encoded + * that way (and we can detect it), so we try other encodings. + * + * @throws Exception + */ + public void testParse_explicit_bad_encoded() throws Exception { + final ByteArrayOutputStream b = new ByteArrayOutputStream(); + b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n" + .getBytes("UTF-8")); + b.write("type tree\n".getBytes("UTF-8")); + b.write("tag v1.2.3.4.5\n".getBytes("UTF-8")); + b + .write("tagger F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n" + .getBytes("ISO-8859-1")); + b.write("encoding EUC-JP\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("\u304d\u308c\u3044\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("Hi\n".getBytes("UTF-8")); + final RevTag c; + c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); + c.parseCanonical(new RevWalk(db), b.toByteArray()); + + assertEquals("F\u00f6r fattare", c.getTaggerIdent().getName()); + assertEquals("\u304d\u308c\u3044", c.getShortMessage()); + assertEquals("\u304d\u308c\u3044\n\nHi\n", c.getFullMessage()); + } + + /** + * This is a twisted case too, but show what we expect here. We can revise + * the expectations provided this case is updated. + * + * What happens here is that an encoding us given, but data is not encoded + * that way (and we can detect it), so we try other encodings. Here data + * could actually be decoded in the stated encoding, but we override using + * UTF-8. + * + * @throws Exception + */ + public void testParse_explicit_bad_encoded2() throws Exception { + final ByteArrayOutputStream b = new ByteArrayOutputStream(); + b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n" + .getBytes("UTF-8")); + b.write("type tree\n".getBytes("UTF-8")); + b.write("tag v1.2.3.4.5\n".getBytes("UTF-8")); + b + .write("tagger F\u00f6r fattare <a_u_thor@example.com> 1218123387 +0700\n" + .getBytes("UTF-8")); + b.write("encoding ISO-8859-1\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("\u304d\u308c\u3044\n".getBytes("UTF-8")); + b.write("\n".getBytes("UTF-8")); + b.write("Hi\n".getBytes("UTF-8")); + final RevTag c; + c = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); + c.parseCanonical(new RevWalk(db), b.toByteArray()); + + assertEquals("F\u00f6r fattare", c.getTaggerIdent().getName()); + assertEquals("\u304d\u308c\u3044", c.getShortMessage()); + assertEquals("\u304d\u308c\u3044\n\nHi\n", c.getFullMessage()); + } + + public void testParse_NoMessage() throws Exception { + final String msg = ""; + final RevTag c = create(msg); + assertEquals(msg, c.getFullMessage()); + assertEquals(msg, c.getShortMessage()); + } + + public void testParse_OnlyLFMessage() throws Exception { + final RevTag c = create("\n"); + assertEquals("\n", c.getFullMessage()); + assertEquals("", c.getShortMessage()); + } + + public void testParse_ShortLineOnlyNoLF() throws Exception { + final String shortMsg = "This is a short message."; + final RevTag c = create(shortMsg); + assertEquals(shortMsg, c.getFullMessage()); + assertEquals(shortMsg, c.getShortMessage()); + } + + public void testParse_ShortLineOnlyEndLF() throws Exception { + final String shortMsg = "This is a short message."; + final String fullMsg = shortMsg + "\n"; + final RevTag c = create(fullMsg); + assertEquals(fullMsg, c.getFullMessage()); + assertEquals(shortMsg, c.getShortMessage()); + } + + public void testParse_ShortLineOnlyEmbeddedLF() throws Exception { + final String fullMsg = "This is a\nshort message."; + final String shortMsg = fullMsg.replace('\n', ' '); + final RevTag c = create(fullMsg); + assertEquals(fullMsg, c.getFullMessage()); + assertEquals(shortMsg, c.getShortMessage()); + } + + public void testParse_ShortLineOnlyEmbeddedAndEndingLF() throws Exception { + final String fullMsg = "This is a\nshort message.\n"; + final String shortMsg = "This is a short message."; + final RevTag c = create(fullMsg); + assertEquals(fullMsg, c.getFullMessage()); + assertEquals(shortMsg, c.getShortMessage()); + } + + public void testParse_GitStyleMessage() throws Exception { + final String shortMsg = "This fixes a bug."; + final String body = "We do it with magic and pixie dust and stuff.\n" + + "\n" + "Signed-off-by: A U. Thor <author@example.com>\n"; + final String fullMsg = shortMsg + "\n" + "\n" + body; + final RevTag c = create(fullMsg); + assertEquals(fullMsg, c.getFullMessage()); + assertEquals(shortMsg, c.getShortMessage()); + } + + private static ObjectId id(final String str) { + return ObjectId.fromString(str); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCullTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCullTest.java new file mode 100644 index 0000000000..9e879c5f0d --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkCullTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 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.revwalk; + +public class RevWalkCullTest extends RevWalkTestCase { + public void testProperlyCullAllAncestors1() throws Exception { + // Credit goes to Junio C Hamano <gitster@pobox.com> for this + // test case in git-core (t/t6009-rev-list-parent.sh) + // + // We induce a clock skew so two is dated before one. + // + final RevCommit a = commit(); + final RevCommit b = commit(-2400, a); + final RevCommit c = commit(b); + final RevCommit d = commit(c); + + markStart(a); + markUninteresting(d); + assertNull(rw.next()); + } + + public void testProperlyCullAllAncestors2() throws Exception { + // Despite clock skew on c1 being very old it should not + // produce, neither should a or b, or any part of that chain. + // + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c1 = commit(-5, b); + final RevCommit c2 = commit(10, b); + final RevCommit d = commit(c1, c2); + + markStart(d); + markUninteresting(c1); + assertCommit(d, rw.next()); + assertCommit(c2, rw.next()); + assertNull(rw.next()); + } + + public void testProperlyCullAllAncestors_LongHistory() throws Exception { + final RevCommit a = commit(); + RevCommit b = commit(a); + for (int i = 0; i < 24; i++) { + b = commit(b); + if ((i & 2) == 0) + markUninteresting(b); + } + final RevCommit c = commit(b); + + markStart(c); + markUninteresting(b); + assertCommit(c, rw.next()); + assertNull(rw.next()); + + // We should have aborted before we got back so far that "a" + // would be parsed. Thus, its parents shouldn't be allocated. + // + assertNull(a.parents); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java new file mode 100644 index 0000000000..db4c38e72b --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java @@ -0,0 +1,297 @@ +/* + * Copyright (C) 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.revwalk; + +import java.io.IOException; +import java.util.Date; + +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.errors.StopWalkException; +import org.eclipse.jgit.revwalk.filter.AndRevFilter; +import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter; +import org.eclipse.jgit.revwalk.filter.NotRevFilter; +import org.eclipse.jgit.revwalk.filter.OrRevFilter; +import org.eclipse.jgit.revwalk.filter.RevFilter; + +public class RevWalkFilterTest extends RevWalkTestCase { + private static final MyAll MY_ALL = new MyAll(); + + public void testFilter_ALL() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(RevFilter.ALL); + markStart(c); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testFilter_Negate_ALL() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(RevFilter.ALL.negate()); + markStart(c); + assertNull(rw.next()); + } + + public void testFilter_NOT_ALL() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(NotRevFilter.create(RevFilter.ALL)); + markStart(c); + assertNull(rw.next()); + } + + public void testFilter_NONE() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(RevFilter.NONE); + markStart(c); + assertNull(rw.next()); + } + + public void testFilter_NOT_NONE() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(NotRevFilter.create(RevFilter.NONE)); + markStart(c); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testFilter_ALL_And_NONE() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(AndRevFilter.create(RevFilter.ALL, RevFilter.NONE)); + markStart(c); + assertNull(rw.next()); + } + + public void testFilter_NONE_And_ALL() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(AndRevFilter.create(RevFilter.NONE, RevFilter.ALL)); + markStart(c); + assertNull(rw.next()); + } + + public void testFilter_ALL_Or_NONE() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(OrRevFilter.create(RevFilter.ALL, RevFilter.NONE)); + markStart(c); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testFilter_NONE_Or_ALL() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(OrRevFilter.create(RevFilter.NONE, RevFilter.ALL)); + markStart(c); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testFilter_MY_ALL_And_NONE() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(AndRevFilter.create(MY_ALL, RevFilter.NONE)); + markStart(c); + assertNull(rw.next()); + } + + public void testFilter_NONE_And_MY_ALL() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(AndRevFilter.create(RevFilter.NONE, MY_ALL)); + markStart(c); + assertNull(rw.next()); + } + + public void testFilter_MY_ALL_Or_NONE() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(OrRevFilter.create(MY_ALL, RevFilter.NONE)); + markStart(c); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testFilter_NONE_Or_MY_ALL() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + + rw.setRevFilter(OrRevFilter.create(RevFilter.NONE, MY_ALL)); + markStart(c); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testFilter_NO_MERGES() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c1 = commit(b); + final RevCommit c2 = commit(b); + final RevCommit d = commit(c1, c2); + final RevCommit e = commit(d); + + rw.setRevFilter(RevFilter.NO_MERGES); + markStart(e); + assertCommit(e, rw.next()); + assertCommit(c2, rw.next()); + assertCommit(c1, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testCommitTimeRevFilter() throws Exception { + final RevCommit a = commit(); + tick(100); + + final RevCommit b = commit(a); + tick(100); + + Date since = new Date(nowTick); + final RevCommit c1 = commit(b); + tick(100); + + final RevCommit c2 = commit(b); + tick(100); + + Date until = new Date(nowTick); + final RevCommit d = commit(c1, c2); + tick(100); + + final RevCommit e = commit(d); + + { + RevFilter after = CommitTimeRevFilter.after(since); + assertNotNull(after); + rw.setRevFilter(after); + markStart(e); + assertCommit(e, rw.next()); + assertCommit(d, rw.next()); + assertCommit(c2, rw.next()); + assertCommit(c1, rw.next()); + assertNull(rw.next()); + } + + { + RevFilter before = CommitTimeRevFilter.before(until); + assertNotNull(before); + rw.reset(); + rw.setRevFilter(before); + markStart(e); + assertCommit(c2, rw.next()); + assertCommit(c1, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + { + RevFilter between = CommitTimeRevFilter.between(since, until); + assertNotNull(between); + rw.reset(); + rw.setRevFilter(between); + markStart(e); + assertCommit(c2, rw.next()); + assertCommit(c1, rw.next()); + assertNull(rw.next()); + } + } + + private static class MyAll extends RevFilter { + @Override + public RevFilter clone() { + return this; + } + + @Override + public boolean include(RevWalk walker, RevCommit cmit) + throws StopWalkException, MissingObjectException, + IncorrectObjectTypeException, IOException { + return true; + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkMergeBaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkMergeBaseTest.java new file mode 100644 index 0000000000..10c9f9b12e --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkMergeBaseTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 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.revwalk; + +import org.eclipse.jgit.revwalk.filter.RevFilter; +import org.eclipse.jgit.treewalk.filter.TreeFilter; + +public class RevWalkMergeBaseTest extends RevWalkTestCase { + public void testNone() throws Exception { + final RevCommit c1 = commit(commit(commit())); + final RevCommit c2 = commit(commit(commit())); + + rw.setRevFilter(RevFilter.MERGE_BASE); + markStart(c1); + markStart(c2); + assertNull(rw.next()); + } + + public void testDisallowTreeFilter() throws Exception { + final RevCommit c1 = commit(); + final RevCommit c2 = commit(); + + rw.setRevFilter(RevFilter.MERGE_BASE); + rw.setTreeFilter(TreeFilter.ANY_DIFF); + markStart(c1); + markStart(c2); + try { + assertNull(rw.next()); + fail("did not throw IllegalStateException"); + } catch (IllegalStateException ise) { + // expected result + } + } + + public void testSimple() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c1 = commit(commit(commit(commit(commit(b))))); + final RevCommit c2 = commit(commit(commit(commit(commit(b))))); + + rw.setRevFilter(RevFilter.MERGE_BASE); + markStart(c1); + markStart(c2); + assertCommit(b, rw.next()); + assertNull(rw.next()); + } + + public void testMultipleHeads_SameBase1() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c1 = commit(commit(commit(commit(commit(b))))); + final RevCommit c2 = commit(commit(commit(commit(commit(b))))); + final RevCommit c3 = commit(commit(commit(b))); + + rw.setRevFilter(RevFilter.MERGE_BASE); + markStart(c1); + markStart(c2); + markStart(c3); + assertCommit(b, rw.next()); + assertNull(rw.next()); + } + + public void testMultipleHeads_SameBase2() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + final RevCommit d1 = commit(commit(commit(commit(commit(b))))); + final RevCommit d2 = commit(commit(commit(commit(commit(c))))); + final RevCommit d3 = commit(commit(commit(c))); + + rw.setRevFilter(RevFilter.MERGE_BASE); + markStart(d1); + markStart(d2); + markStart(d3); + assertCommit(b, rw.next()); + assertNull(rw.next()); + } + + public void testCrissCross() throws Exception { + // See http://marc.info/?l=git&m=111463358500362&w=2 for a nice + // description of what this test is creating. We don't have a + // clean merge base for d,e as they each merged the parents b,c + // in different orders. + // + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(a); + final RevCommit d = commit(b, c); + final RevCommit e = commit(c, b); + + rw.setRevFilter(RevFilter.MERGE_BASE); + markStart(d); + markStart(e); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertNull(rw.next()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkPathFilter1Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkPathFilter1Test.java new file mode 100644 index 0000000000..986a886566 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkPathFilter1Test.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 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.revwalk; + +import java.util.Collections; + +import org.eclipse.jgit.treewalk.filter.AndTreeFilter; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; +import org.eclipse.jgit.treewalk.filter.TreeFilter; + +public class RevWalkPathFilter1Test extends RevWalkTestCase { + protected void filter(final String path) { + rw.setTreeFilter(AndTreeFilter.create(PathFilterGroup + .createFromStrings(Collections.singleton(path)), + TreeFilter.ANY_DIFF)); + } + + public void testEmpty_EmptyTree() throws Exception { + final RevCommit a = commit(); + filter("a"); + markStart(a); + assertNull(rw.next()); + } + + public void testEmpty_NoMatch() throws Exception { + final RevCommit a = commit(tree(file("0", blob("0")))); + filter("a"); + markStart(a); + assertNull(rw.next()); + } + + public void testSimple1() throws Exception { + final RevCommit a = commit(tree(file("0", blob("0")))); + filter("0"); + markStart(a); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testEdits_MatchNone() throws Exception { + final RevCommit a = commit(tree(file("0", blob("a")))); + final RevCommit b = commit(tree(file("0", blob("b"))), a); + final RevCommit c = commit(tree(file("0", blob("c"))), b); + final RevCommit d = commit(tree(file("0", blob("d"))), c); + filter("a"); + markStart(d); + assertNull(rw.next()); + } + + public void testEdits_MatchAll() throws Exception { + final RevCommit a = commit(tree(file("0", blob("a")))); + final RevCommit b = commit(tree(file("0", blob("b"))), a); + final RevCommit c = commit(tree(file("0", blob("c"))), b); + final RevCommit d = commit(tree(file("0", blob("d"))), c); + filter("0"); + markStart(d); + assertCommit(d, rw.next()); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testStringOfPearls_FilePath1() throws Exception { + final RevCommit a = commit(tree(file("d/f", blob("a")))); + final RevCommit b = commit(tree(file("d/f", blob("a"))), a); + final RevCommit c = commit(tree(file("d/f", blob("b"))), b); + filter("d/f"); + markStart(c); + + assertCommit(c, rw.next()); + assertEquals(1, c.getParentCount()); + assertCommit(a, c.getParent(0)); // b was skipped + + assertCommit(a, rw.next()); + assertEquals(0, a.getParentCount()); + assertNull(rw.next()); + } + + public void testStringOfPearls_FilePath2() throws Exception { + final RevCommit a = commit(tree(file("d/f", blob("a")))); + final RevCommit b = commit(tree(file("d/f", blob("a"))), a); + final RevCommit c = commit(tree(file("d/f", blob("b"))), b); + final RevCommit d = commit(tree(file("d/f", blob("b"))), c); + filter("d/f"); + markStart(d); + + // d was skipped + assertCommit(c, rw.next()); + assertEquals(1, c.getParentCount()); + assertCommit(a, c.getParent(0)); // b was skipped + + assertCommit(a, rw.next()); + assertEquals(0, a.getParentCount()); + assertNull(rw.next()); + } + + public void testStringOfPearls_DirPath2() throws Exception { + final RevCommit a = commit(tree(file("d/f", blob("a")))); + final RevCommit b = commit(tree(file("d/f", blob("a"))), a); + final RevCommit c = commit(tree(file("d/f", blob("b"))), b); + final RevCommit d = commit(tree(file("d/f", blob("b"))), c); + filter("d"); + markStart(d); + + // d was skipped + assertCommit(c, rw.next()); + assertEquals(1, c.getParentCount()); + assertCommit(a, c.getParent(0)); // b was skipped + + assertCommit(a, rw.next()); + assertEquals(0, a.getParentCount()); + assertNull(rw.next()); + } + + public void testStringOfPearls_FilePath3() throws Exception { + final RevCommit a = commit(tree(file("d/f", blob("a")))); + final RevCommit b = commit(tree(file("d/f", blob("a"))), a); + final RevCommit c = commit(tree(file("d/f", blob("b"))), b); + final RevCommit d = commit(tree(file("d/f", blob("b"))), c); + final RevCommit e = commit(tree(file("d/f", blob("b"))), d); + final RevCommit f = commit(tree(file("d/f", blob("b"))), e); + final RevCommit g = commit(tree(file("d/f", blob("b"))), f); + final RevCommit h = commit(tree(file("d/f", blob("b"))), g); + final RevCommit i = commit(tree(file("d/f", blob("c"))), h); + filter("d/f"); + markStart(i); + + assertCommit(i, rw.next()); + assertEquals(1, i.getParentCount()); + assertCommit(c, i.getParent(0)); // h..d was skipped + + assertCommit(c, rw.next()); + assertEquals(1, c.getParentCount()); + assertCommit(a, c.getParent(0)); // b was skipped + + assertCommit(a, rw.next()); + assertEquals(0, a.getParentCount()); + assertNull(rw.next()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkPathFilter6012Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkPathFilter6012Test.java new file mode 100644 index 0000000000..73d41eae64 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkPathFilter6012Test.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 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.revwalk; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.HashMap; + +import org.eclipse.jgit.treewalk.filter.AndTreeFilter; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; +import org.eclipse.jgit.treewalk.filter.TreeFilter; + +// Note: Much of this test case is broken as it depends upon +// the graph applying topological sorting *before* doing merge +// simplification. It also depends upon a difference between +// full history and non-full history for a path, something we +// don't quite yet have a distiction for in JGit. +// +public class RevWalkPathFilter6012Test extends RevWalkTestCase { + private static final String pA = "pA", pF = "pF", pE = "pE"; + + private RevCommit a, b, c, d, e, f, g, h, i; + + private HashMap<RevCommit, String> byName; + + public void setUp() throws Exception { + super.setUp(); + + // Test graph was stolen from git-core t6012-rev-list-simplify + // (by Junio C Hamano in 65347030590bcc251a9ff2ed96487a0f1b9e9fa8) + // + final RevBlob zF = blob("zF"); + final RevBlob zH = blob("zH"); + final RevBlob zI = blob("zI"); + final RevBlob zS = blob("zS"); + final RevBlob zY = blob("zY"); + + a = commit(tree(file(pF, zH))); + b = commit(tree(file(pF, zI)), a); + c = commit(tree(file(pF, zI)), a); + d = commit(tree(file(pA, zS), file(pF, zI)), c); + parse(d); + e = commit(d.getTree(), d, b); + f = commit(tree(file(pA, zS), file(pE, zY), file(pF, zI)), e); + parse(f); + g = commit(tree(file(pE, zY), file(pF, zI)), b); + h = commit(f.getTree(), g, f); + i = commit(tree(file(pA, zS), file(pE, zY), file(pF, zF)), h); + + byName = new HashMap<RevCommit, String>(); + for (Field z : RevWalkPathFilter6012Test.class.getDeclaredFields()) { + if (z.getType() == RevCommit.class) + byName.put((RevCommit) z.get(this), z.getName()); + } + } + + protected void check(final RevCommit... order) throws Exception { + markStart(i); + final StringBuilder act = new StringBuilder(); + for (final RevCommit z : rw) { + final String name = byName.get(z); + assertNotNull(name); + act.append(name); + act.append(' '); + } + final StringBuilder exp = new StringBuilder(); + for (final RevCommit z : order) { + final String name = byName.get(z); + assertNotNull(name); + exp.append(name); + exp.append(' '); + } + assertEquals(exp.toString(), act.toString()); + } + + protected void filter(final String path) { + rw.setTreeFilter(AndTreeFilter.create(PathFilterGroup + .createFromStrings(Collections.singleton(path)), + TreeFilter.ANY_DIFF)); + } + + public void test1() throws Exception { + // TODO --full-history + check(i, h, g, f, e, d, c, b, a); + } + + public void test2() throws Exception { + // TODO --full-history + filter(pF); + // TODO fix broken test + // check(i, h, e, c, b, a); + } + + public void test3() throws Exception { + // TODO --full-history + rw.sort(RevSort.TOPO); + filter(pF); + // TODO fix broken test + // check(i, h, e, c, b, a); + } + + public void test4() throws Exception { + // TODO --full-history + rw.sort(RevSort.COMMIT_TIME_DESC); + filter(pF); + // TODO fix broken test + // check(i, h, e, c, b, a); + } + + public void test5() throws Exception { + // TODO --simplify-merges + filter(pF); + // TODO fix broken test + // check(i, e, c, b, a); + } + + public void test6() throws Exception { + filter(pF); + check(i, b, a); + } + + public void test7() throws Exception { + rw.sort(RevSort.TOPO); + filter(pF); + check(i, b, a); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkSortTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkSortTest.java new file mode 100644 index 0000000000..0d3e0cf5aa --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkSortTest.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 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.revwalk; + +public class RevWalkSortTest extends RevWalkTestCase { + public void testSort_Default() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(1, a); + final RevCommit c = commit(1, b); + final RevCommit d = commit(1, c); + + markStart(d); + assertCommit(d, rw.next()); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testSort_COMMIT_TIME_DESC() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + final RevCommit d = commit(c); + + rw.sort(RevSort.COMMIT_TIME_DESC); + markStart(d); + assertCommit(d, rw.next()); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testSort_REVERSE() throws Exception { + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(b); + final RevCommit d = commit(c); + + rw.sort(RevSort.REVERSE); + markStart(d); + assertCommit(a, rw.next()); + assertCommit(b, rw.next()); + assertCommit(c, rw.next()); + assertCommit(d, rw.next()); + assertNull(rw.next()); + } + + public void testSort_COMMIT_TIME_DESC_OutOfOrder1() throws Exception { + // Despite being out of order time-wise, a strand-of-pearls must + // still maintain topological order. + // + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c = commit(-5, b); + final RevCommit d = commit(10, c); + assertTrue(parse(a).getCommitTime() < parse(d).getCommitTime()); + assertTrue(parse(c).getCommitTime() < parse(b).getCommitTime()); + + rw.sort(RevSort.COMMIT_TIME_DESC); + markStart(d); + assertCommit(d, rw.next()); + assertCommit(c, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testSort_COMMIT_TIME_DESC_OutOfOrder2() throws Exception { + // c1 is back dated before its parent. + // + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c1 = commit(-5, b); + final RevCommit c2 = commit(10, b); + final RevCommit d = commit(c1, c2); + + rw.sort(RevSort.COMMIT_TIME_DESC); + markStart(d); + assertCommit(d, rw.next()); + assertCommit(c2, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertCommit(c1, rw.next()); + assertNull(rw.next()); + } + + public void testSort_TOPO() throws Exception { + // c1 is back dated before its parent. + // + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c1 = commit(-5, b); + final RevCommit c2 = commit(10, b); + final RevCommit d = commit(c1, c2); + + rw.sort(RevSort.TOPO); + markStart(d); + assertCommit(d, rw.next()); + assertCommit(c2, rw.next()); + assertCommit(c1, rw.next()); + assertCommit(b, rw.next()); + assertCommit(a, rw.next()); + assertNull(rw.next()); + } + + public void testSort_TOPO_REVERSE() throws Exception { + // c1 is back dated before its parent. + // + final RevCommit a = commit(); + final RevCommit b = commit(a); + final RevCommit c1 = commit(-5, b); + final RevCommit c2 = commit(10, b); + final RevCommit d = commit(c1, c2); + + rw.sort(RevSort.TOPO); + rw.sort(RevSort.REVERSE, true); + markStart(d); + assertCommit(a, rw.next()); + assertCommit(b, rw.next()); + assertCommit(c1, rw.next()); + assertCommit(c2, rw.next()); + assertCommit(d, rw.next()); + assertNull(rw.next()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java new file mode 100644 index 0000000000..50fbce41aa --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 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.revwalk; + +import java.util.Collections; +import java.util.Date; + +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheBuilder; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.lib.Commit; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectWriter; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.lib.Tag; +import org.eclipse.jgit.lib.Tree; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; + +/** Support for tests of the {@link RevWalk} class. */ +public abstract class RevWalkTestCase extends RepositoryTestCase { + protected ObjectWriter ow; + + protected RevTree emptyTree; + + protected long nowTick; + + protected RevWalk rw; + + public void setUp() throws Exception { + super.setUp(); + ow = new ObjectWriter(db); + rw = createRevWalk(); + emptyTree = rw.parseTree(ow.writeTree(new Tree(db))); + nowTick = 1236977987000L; + } + + protected RevWalk createRevWalk() { + return new RevWalk(db); + } + + protected void tick(final int secDelta) { + nowTick += secDelta * 1000L; + } + + protected RevBlob blob(final String content) throws Exception { + return rw.lookupBlob(ow.writeBlob(Constants.encode(content))); + } + + protected DirCacheEntry file(final String path, final RevBlob blob) + throws Exception { + final DirCacheEntry e = new DirCacheEntry(path); + e.setFileMode(FileMode.REGULAR_FILE); + e.setObjectId(blob); + return e; + } + + protected RevTree tree(final DirCacheEntry... entries) throws Exception { + final DirCache dc = DirCache.newInCore(); + final DirCacheBuilder b = dc.builder(); + for (final DirCacheEntry e : entries) + b.add(e); + b.finish(); + return rw.lookupTree(dc.writeTree(ow)); + } + + protected RevObject get(final RevTree tree, final String path) + throws Exception { + final TreeWalk tw = new TreeWalk(db); + tw.setFilter(PathFilterGroup.createFromStrings(Collections + .singleton(path))); + tw.reset(tree); + while (tw.next()) { + if (tw.isSubtree() && !path.equals(tw.getPathString())) { + tw.enterSubtree(); + continue; + } + final ObjectId entid = tw.getObjectId(0); + final FileMode entmode = tw.getFileMode(0); + return rw.lookupAny(entid, entmode.getObjectType()); + } + fail("Can't find " + path + " in tree " + tree.name()); + return null; // never reached. + } + + protected RevCommit commit(final RevCommit... parents) throws Exception { + return commit(1, emptyTree, parents); + } + + protected RevCommit commit(final RevTree tree, final RevCommit... parents) + throws Exception { + return commit(1, tree, parents); + } + + protected RevCommit commit(final int secDelta, final RevCommit... parents) + throws Exception { + return commit(secDelta, emptyTree, parents); + } + + protected RevCommit commit(final int secDelta, final RevTree tree, + final RevCommit... parents) throws Exception { + tick(secDelta); + final Commit c = new Commit(db); + c.setTreeId(tree); + c.setParentIds(parents); + c.setAuthor(new PersonIdent(jauthor, new Date(nowTick))); + c.setCommitter(new PersonIdent(jcommitter, new Date(nowTick))); + c.setMessage(""); + return rw.lookupCommit(ow.writeCommit(c)); + } + + protected RevTag tag(final String name, final RevObject dst) + throws Exception { + final Tag t = new Tag(db); + t.setType(Constants.typeString(dst.getType())); + t.setObjId(dst.toObjectId()); + t.setTag(name); + t.setTagger(new PersonIdent(jcommitter, new Date(nowTick))); + t.setMessage(""); + return (RevTag) rw.lookupAny(ow.writeTag(t), Constants.OBJ_TAG); + } + + protected <T extends RevObject> T parse(final T t) throws Exception { + rw.parseBody(t); + return t; + } + + protected void markStart(final RevCommit commit) throws Exception { + rw.markStart(commit); + } + + protected void markUninteresting(final RevCommit commit) throws Exception { + rw.markUninteresting(commit); + } + + protected void assertCommit(final RevCommit exp, final RevCommit act) { + assertSame(exp, act); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java new file mode 100644 index 0000000000..fb9b358b2a --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2008, Google Inc. + * Copyright (C) 2008, Mike Ralphson <mike@abacus.co.uk> + * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * 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.transport; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.Set; + +import org.eclipse.jgit.errors.MissingBundlePrerequisiteException; +import org.eclipse.jgit.errors.NotSupportedException; +import org.eclipse.jgit.errors.TransportException; +import org.eclipse.jgit.lib.NullProgressMonitor; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; + +public class BundleWriterTest extends RepositoryTestCase { + + public void testWrite0() throws Exception { + // Create a tiny bundle, (well one of) the first commits only + final byte[] bundle = makeBundle("refs/heads/firstcommit", + "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", null); + + // Then we clone a new repo from that bundle and do a simple test. This + // makes sure + // we could read the bundle we created. + Repository newRepo = createNewEmptyRepo(); + FetchResult fetchResult = fetchFromBundle(newRepo, bundle); + Ref advertisedRef = fetchResult + .getAdvertisedRef("refs/heads/firstcommit"); + + // We expect firstcommit to appear by id + assertEquals("42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", advertisedRef + .getObjectId().name()); + // ..and by name as the bundle created a new ref + assertEquals("42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", newRepo + .resolve("refs/heads/firstcommit").name()); + } + + /** + * Incremental bundle test + * + * @throws Exception + */ + public void testWrite1() throws Exception { + byte[] bundle; + + // Create a small bundle, an early commit + bundle = makeBundle("refs/heads/aa", db.resolve("a").name(), null); + + // Then we clone a new repo from that bundle and do a simple test. This + // makes sure + // we could read the bundle we created. + Repository newRepo = createNewEmptyRepo(); + FetchResult fetchResult = fetchFromBundle(newRepo, bundle); + Ref advertisedRef = fetchResult.getAdvertisedRef("refs/heads/aa"); + + assertEquals(db.resolve("a").name(), advertisedRef.getObjectId().name()); + assertEquals(db.resolve("a").name(), newRepo.resolve("refs/heads/aa") + .name()); + assertNull(newRepo.resolve("refs/heads/a")); + + // Next an incremental bundle + bundle = makeBundle("refs/heads/cc", db.resolve("c").name(), + new RevWalk(db).parseCommit(db.resolve("a").toObjectId())); + fetchResult = fetchFromBundle(newRepo, bundle); + advertisedRef = fetchResult.getAdvertisedRef("refs/heads/cc"); + assertEquals(db.resolve("c").name(), advertisedRef.getObjectId().name()); + assertEquals(db.resolve("c").name(), newRepo.resolve("refs/heads/cc") + .name()); + assertNull(newRepo.resolve("refs/heads/c")); + assertNull(newRepo.resolve("refs/heads/a")); // still unknown + + try { + // Check that we actually needed the first bundle + Repository newRepo2 = createNewEmptyRepo(); + fetchResult = fetchFromBundle(newRepo2, bundle); + fail("We should not be able to fetch from bundle with prerequisites that are not fulfilled"); + } catch (MissingBundlePrerequisiteException e) { + assertTrue(e.getMessage() + .indexOf(db.resolve("refs/heads/a").name()) >= 0); + } + } + + private FetchResult fetchFromBundle(final Repository newRepo, + final byte[] bundle) throws URISyntaxException, + NotSupportedException, TransportException { + final URIish uri = new URIish("in-memory://"); + final ByteArrayInputStream in = new ByteArrayInputStream(bundle); + final RefSpec rs = new RefSpec("refs/heads/*:refs/heads/*"); + final Set<RefSpec> refs = Collections.singleton(rs); + return new TransportBundleStream(newRepo, uri, in).fetch( + NullProgressMonitor.INSTANCE, refs); + } + + private byte[] makeBundle(final String name, + final String anObjectToInclude, final RevCommit assume) + throws FileNotFoundException, IOException { + final BundleWriter bw; + + bw = new BundleWriter(db, NullProgressMonitor.INSTANCE); + bw.include(name, ObjectId.fromString(anObjectToInclude)); + if (assume != null) + bw.assume(assume); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + bw.writeBundle(out); + return out.toByteArray(); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/IndexPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/IndexPackTest.java new file mode 100644 index 0000000000..78f4393c3a --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/IndexPackTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com> + * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * 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.transport; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.PackFile; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.lib.TextProgressMonitor; +import org.eclipse.jgit.util.JGitTestUtil; + +/** + * Test indexing of git packs. A pack is read from a stream, copied + * to a new pack and an index is created. Then the packs are tested + * to make sure they contain the expected objects (well we don't test + * for all of them unless the packs are very small). + */ +public class IndexPackTest extends RepositoryTestCase { + + /** + * Test indexing one of the test packs in the egit repo. It has deltas. + * + * @throws IOException + */ + public void test1() throws IOException { + File packFile = JGitTestUtil.getTestResourceFile("pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack"); + final InputStream is = new FileInputStream(packFile); + try { + IndexPack pack = new IndexPack(db, is, new File(trash, "tmp_pack1")); + pack.index(new TextProgressMonitor()); + PackFile file = new PackFile(new File(trash, "tmp_pack1.idx"), new File(trash, "tmp_pack1.pack")); + assertTrue(file.hasObject(ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))); + assertTrue(file.hasObject(ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"))); + assertTrue(file.hasObject(ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"))); + assertTrue(file.hasObject(ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3"))); + assertTrue(file.hasObject(ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"))); + assertTrue(file.hasObject(ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"))); + assertTrue(file.hasObject(ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"))); + assertTrue(file.hasObject(ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"))); + } finally { + is.close(); + } + } + + /** + * This is just another pack. It so happens that we have two convenient pack to + * test with in the repository. + * + * @throws IOException + */ + public void test2() throws IOException { + File packFile = JGitTestUtil.getTestResourceFile("pack-df2982f284bbabb6bdb59ee3fcc6eb0983e20371.pack"); + final InputStream is = new FileInputStream(packFile); + try { + IndexPack pack = new IndexPack(db, is, new File(trash, "tmp_pack2")); + pack.index(new TextProgressMonitor()); + PackFile file = new PackFile(new File(trash, "tmp_pack2.idx"), new File(trash, "tmp_pack2.pack")); + assertTrue(file.hasObject(ObjectId.fromString("02ba32d3649e510002c21651936b7077aa75ffa9"))); + assertTrue(file.hasObject(ObjectId.fromString("0966a434eb1a025db6b71485ab63a3bfbea520b6"))); + assertTrue(file.hasObject(ObjectId.fromString("09efc7e59a839528ac7bda9fa020dc9101278680"))); + assertTrue(file.hasObject(ObjectId.fromString("0a3d7772488b6b106fb62813c4d6d627918d9181"))); + assertTrue(file.hasObject(ObjectId.fromString("1004d0d7ac26fbf63050a234c9b88a46075719d3"))); + assertTrue(file.hasObject(ObjectId.fromString("10da5895682013006950e7da534b705252b03be6"))); + assertTrue(file.hasObject(ObjectId.fromString("1203b03dc816ccbb67773f28b3c19318654b0bc8"))); + assertTrue(file.hasObject(ObjectId.fromString("15fae9e651043de0fd1deef588aa3fbf5a7a41c6"))); + assertTrue(file.hasObject(ObjectId.fromString("16f9ec009e5568c435f473ba3a1df732d49ce8c3"))); + assertTrue(file.hasObject(ObjectId.fromString("1fd7d579fb6ae3fe942dc09c2c783443d04cf21e"))); + assertTrue(file.hasObject(ObjectId.fromString("20a8ade77639491ea0bd667bf95de8abf3a434c8"))); + assertTrue(file.hasObject(ObjectId.fromString("2675188fd86978d5bc4d7211698b2118ae3bf658"))); + // and lots more... + } finally { + is.close(); + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/LongMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/LongMapTest.java new file mode 100644 index 0000000000..abb6fe0db0 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/LongMapTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 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.transport; + +import junit.framework.TestCase; + +public class LongMapTest extends TestCase { + private LongMap<Long> map; + + protected void setUp() throws Exception { + super.setUp(); + map = new LongMap<Long>(); + } + + public void testEmptyMap() { + assertFalse(map.containsKey(0)); + assertFalse(map.containsKey(1)); + + assertNull(map.get(0)); + assertNull(map.get(1)); + + assertNull(map.remove(0)); + assertNull(map.remove(1)); + } + + public void testInsertMinValue() { + final Long min = Long.valueOf(Long.MIN_VALUE); + assertNull(map.put(Long.MIN_VALUE, min)); + assertTrue(map.containsKey(Long.MIN_VALUE)); + assertSame(min, map.get(Long.MIN_VALUE)); + assertFalse(map.containsKey(Integer.MIN_VALUE)); + } + + public void testReplaceMaxValue() { + final Long min = Long.valueOf(Long.MAX_VALUE); + final Long one = Long.valueOf(1); + assertNull(map.put(Long.MAX_VALUE, min)); + assertSame(min, map.get(Long.MAX_VALUE)); + assertSame(min, map.put(Long.MAX_VALUE, one)); + assertSame(one, map.get(Long.MAX_VALUE)); + } + + public void testRemoveOne() { + final long start = 1; + assertNull(map.put(start, Long.valueOf(start))); + assertEquals(Long.valueOf(start), map.remove(start)); + assertFalse(map.containsKey(start)); + } + + public void testRemoveCollision1() { + // This test relies upon the fact that we always >>> 1 the value + // to derive an unsigned hash code. Thus, 0 and 1 fall into the + // same hash bucket. Further it relies on the fact that we add + // the 2nd put at the top of the chain, so removing the 1st will + // cause a different code path. + // + assertNull(map.put(0, Long.valueOf(0))); + assertNull(map.put(1, Long.valueOf(1))); + assertEquals(Long.valueOf(0), map.remove(0)); + + assertFalse(map.containsKey(0)); + assertTrue(map.containsKey(1)); + } + + public void testRemoveCollision2() { + // This test relies upon the fact that we always >>> 1 the value + // to derive an unsigned hash code. Thus, 0 and 1 fall into the + // same hash bucket. Further it relies on the fact that we add + // the 2nd put at the top of the chain, so removing the 2nd will + // cause a different code path. + // + assertNull(map.put(0, Long.valueOf(0))); + assertNull(map.put(1, Long.valueOf(1))); + assertEquals(Long.valueOf(1), map.remove(1)); + + assertTrue(map.containsKey(0)); + assertFalse(map.containsKey(1)); + } + + public void testSmallMap() { + final long start = 12; + final long n = 8; + for (long i = start; i < start + n; i++) + assertNull(map.put(i, Long.valueOf(i))); + for (long i = start; i < start + n; i++) + assertEquals(Long.valueOf(i), map.get(i)); + } + + public void testLargeMap() { + final long start = Integer.MAX_VALUE; + final long n = 100000; + for (long i = start; i < start + n; i++) + assertNull(map.put(i, Long.valueOf(i))); + for (long i = start; i < start + n; i++) + assertEquals(Long.valueOf(i), map.get(i)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java new file mode 100644 index 0000000000..f66e2fd33e --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2008, 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.transport; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; + +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.transport.OpenSshConfig.Host; + +public class OpenSshConfigTest extends RepositoryTestCase { + private File home; + + private File configFile; + + private OpenSshConfig osc; + + public void setUp() throws Exception { + super.setUp(); + + home = new File(trash, "home"); + home.mkdir(); + + configFile = new File(new File(home, ".ssh"), "config"); + configFile.getParentFile().mkdir(); + + System.setProperty("user.name", "jex_junit"); + osc = new OpenSshConfig(home, configFile); + } + + private void config(final String data) throws IOException { + final OutputStreamWriter fw = new OutputStreamWriter( + new FileOutputStream(configFile), "UTF-8"); + fw.write(data); + fw.close(); + } + + public void testNoConfig() { + final Host h = osc.lookup("repo.or.cz"); + assertNotNull(h); + assertEquals("repo.or.cz", h.getHostName()); + assertEquals("jex_junit", h.getUser()); + assertEquals(22, h.getPort()); + assertNull(h.getIdentityFile()); + } + + public void testSeparatorParsing() throws Exception { + config("Host\tfirst\n" + + "\tHostName\tfirst.tld\n" + + "\n" + + "Host second\n" + + " HostName\tsecond.tld\n" + + "Host=third\n" + + "HostName=third.tld\n\n\n" + + "\t Host = fourth\n\n\n" + + " \t HostName\t=fourth.tld\n" + + "Host\t = last\n" + + "HostName \t last.tld"); + assertNotNull(osc.lookup("first")); + assertEquals("first.tld", osc.lookup("first").getHostName()); + assertNotNull(osc.lookup("second")); + assertEquals("second.tld", osc.lookup("second").getHostName()); + assertNotNull(osc.lookup("third")); + assertEquals("third.tld", osc.lookup("third").getHostName()); + assertNotNull(osc.lookup("fourth")); + assertEquals("fourth.tld", osc.lookup("fourth").getHostName()); + assertNotNull(osc.lookup("last")); + assertEquals("last.tld", osc.lookup("last").getHostName()); + } + + public void testQuoteParsing() throws Exception { + config("Host \"good\"\n" + + " HostName=\"good.tld\"\n" + + " Port=\"6007\"\n" + + " User=\"gooduser\"\n" + + "Host multiple unquoted and \"quoted\" \"hosts\"\n" + + " Port=\"2222\"\n" + + "Host \"spaced\"\n" + + "# Bad host name, but testing preservation of spaces\n" + + " HostName=\" spaced\ttld \"\n" + + "# Misbalanced quotes\n" + + "Host \"bad\"\n" + + "# OpenSSH doesn't allow this but ...\n" + + " HostName=bad.tld\"\n"); + assertEquals("good.tld", osc.lookup("good").getHostName()); + assertEquals("gooduser", osc.lookup("good").getUser()); + assertEquals(6007, osc.lookup("good").getPort()); + assertEquals(2222, osc.lookup("multiple").getPort()); + assertEquals(2222, osc.lookup("quoted").getPort()); + assertEquals(2222, osc.lookup("and").getPort()); + assertEquals(2222, osc.lookup("unquoted").getPort()); + assertEquals(2222, osc.lookup("hosts").getPort()); + assertEquals(" spaced\ttld ", osc.lookup("spaced").getHostName()); + assertEquals("bad.tld\"", osc.lookup("bad").getHostName()); + } + + public void testAlias_DoesNotMatch() throws Exception { + config("Host orcz\n" + "\tHostName repo.or.cz\n"); + final Host h = osc.lookup("repo.or.cz"); + assertNotNull(h); + assertEquals("repo.or.cz", h.getHostName()); + assertEquals("jex_junit", h.getUser()); + assertEquals(22, h.getPort()); + assertNull(h.getIdentityFile()); + } + + public void testAlias_OptionsSet() throws Exception { + config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\tPort 2222\n" + + "\tUser jex\n" + "\tIdentityFile .ssh/id_jex\n" + + "\tForwardX11 no\n"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals("repo.or.cz", h.getHostName()); + assertEquals("jex", h.getUser()); + assertEquals(2222, h.getPort()); + assertEquals(new File(home, ".ssh/id_jex"), h.getIdentityFile()); + } + + public void testAlias_OptionsKeywordCaseInsensitive() throws Exception { + config("hOsT orcz\n" + "\thOsTnAmE repo.or.cz\n" + "\tPORT 2222\n" + + "\tuser jex\n" + "\tidentityfile .ssh/id_jex\n" + + "\tForwardX11 no\n"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals("repo.or.cz", h.getHostName()); + assertEquals("jex", h.getUser()); + assertEquals(2222, h.getPort()); + assertEquals(new File(home, ".ssh/id_jex"), h.getIdentityFile()); + } + + public void testAlias_OptionsInherit() throws Exception { + config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n" + + "\tHostName not.a.host.example.com\n" + "\tPort 2222\n" + + "\tUser jex\n" + "\tIdentityFile .ssh/id_jex\n" + + "\tForwardX11 no\n"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals("repo.or.cz", h.getHostName()); + assertEquals("jex", h.getUser()); + assertEquals(2222, h.getPort()); + assertEquals(new File(home, ".ssh/id_jex"), h.getIdentityFile()); + } + + public void testAlias_PreferredAuthenticationsDefault() throws Exception { + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertNull(h.getPreferredAuthentications()); + } + + public void testAlias_PreferredAuthentications() throws Exception { + config("Host orcz\n" + "\tPreferredAuthentications publickey\n"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals("publickey", h.getPreferredAuthentications()); + } + + public void testAlias_InheritPreferredAuthentications() throws Exception { + config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n" + + "\tPreferredAuthentications publickey, hostbased\n"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals("publickey,hostbased", h.getPreferredAuthentications()); + } + + public void testAlias_BatchModeDefault() throws Exception { + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals(false, h.isBatchMode()); + } + + public void testAlias_BatchModeYes() throws Exception { + config("Host orcz\n" + "\tBatchMode yes\n"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals(true, h.isBatchMode()); + } + + public void testAlias_InheritBatchMode() throws Exception { + config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n" + + "\tBatchMode yes\n"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals(true, h.isBatchMode()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java new file mode 100644 index 0000000000..1bcac9e37b --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineInTest.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 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.transport; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.MutableObjectId; +import org.eclipse.jgit.lib.ObjectId; + +// Note, test vectors created with: +// +// perl -e 'printf "%4.4x%s\n", 4+length($ARGV[0]),$ARGV[0]' + +public class PacketLineInTest extends TestCase { + private ByteArrayInputStream rawIn; + + private PacketLineIn in; + + // readString + + public void testReadString1() throws IOException { + init("0006a\n0007bc\n"); + assertEquals("a", in.readString()); + assertEquals("bc", in.readString()); + assertEOF(); + } + + public void testReadString2() throws IOException { + init("0032want fcfcfb1fd94829c1a1704f894fc111d14770d34e\n"); + final String act = in.readString(); + assertEquals("want fcfcfb1fd94829c1a1704f894fc111d14770d34e", act); + assertEOF(); + } + + public void testReadString4() throws IOException { + init("0005a0006bc"); + assertEquals("a", in.readString()); + assertEquals("bc", in.readString()); + assertEOF(); + } + + public void testReadString5() throws IOException { + // accept both upper and lower case + init("000Fhi i am a s"); + assertEquals("hi i am a s", in.readString()); + assertEOF(); + + init("000fhi i am a s"); + assertEquals("hi i am a s", in.readString()); + assertEOF(); + } + + public void testReadString_LenHELO() { + init("HELO"); + try { + in.readString(); + fail("incorrectly accepted invalid packet header"); + } catch (IOException e) { + assertEquals("Invalid packet line header: HELO", e.getMessage()); + } + } + + public void testReadString_Len0001() { + init("0001"); + try { + in.readString(); + fail("incorrectly accepted invalid packet header"); + } catch (IOException e) { + assertEquals("Invalid packet line header: 0001", e.getMessage()); + } + } + + public void testReadString_Len0002() { + init("0002"); + try { + in.readString(); + fail("incorrectly accepted invalid packet header"); + } catch (IOException e) { + assertEquals("Invalid packet line header: 0002", e.getMessage()); + } + } + + public void testReadString_Len0003() { + init("0003"); + try { + in.readString(); + fail("incorrectly accepted invalid packet header"); + } catch (IOException e) { + assertEquals("Invalid packet line header: 0003", e.getMessage()); + } + } + + public void testReadString_Len0004() throws IOException { + init("0004"); + final String act = in.readString(); + assertEquals("", act); + assertNotSame(PacketLineIn.END, act); + assertEOF(); + } + + public void testReadString_End() throws IOException { + init("0000"); + assertSame(PacketLineIn.END, in.readString()); + assertEOF(); + } + + // readStringNoLF + + public void testReadStringRaw1() throws IOException { + init("0005a0006bc"); + assertEquals("a", in.readStringRaw()); + assertEquals("bc", in.readStringRaw()); + assertEOF(); + } + + public void testReadStringRaw2() throws IOException { + init("0031want fcfcfb1fd94829c1a1704f894fc111d14770d34e"); + final String act = in.readStringRaw(); + assertEquals("want fcfcfb1fd94829c1a1704f894fc111d14770d34e", act); + assertEOF(); + } + + public void testReadStringRaw3() throws IOException { + init("0004"); + final String act = in.readStringRaw(); + assertEquals("", act); + assertNotSame(PacketLineIn.END, act); + assertEOF(); + } + + public void testReadStringRaw_End() throws IOException { + init("0000"); + assertSame(PacketLineIn.END, in.readStringRaw()); + assertEOF(); + } + + public void testReadStringRaw4() { + init("HELO"); + try { + in.readStringRaw(); + fail("incorrectly accepted invalid packet header"); + } catch (IOException e) { + assertEquals("Invalid packet line header: HELO", e.getMessage()); + } + } + + // readACK + + public void testReadACK_NAK() throws IOException { + final ObjectId expid = ObjectId + .fromString("fcfcfb1fd94829c1a1704f894fc111d14770d34e"); + final MutableObjectId actid = new MutableObjectId(); + actid.fromString(expid.name()); + + init("0008NAK\n"); + assertSame(PacketLineIn.AckNackResult.NAK, in.readACK(actid)); + assertTrue(actid.equals(expid)); + assertEOF(); + } + + public void testReadACK_ACK1() throws IOException { + final ObjectId expid = ObjectId + .fromString("fcfcfb1fd94829c1a1704f894fc111d14770d34e"); + final MutableObjectId actid = new MutableObjectId(); + + init("0031ACK fcfcfb1fd94829c1a1704f894fc111d14770d34e\n"); + assertSame(PacketLineIn.AckNackResult.ACK, in.readACK(actid)); + assertTrue(actid.equals(expid)); + assertEOF(); + } + + public void testReadACK_ACKcontinue1() throws IOException { + final ObjectId expid = ObjectId + .fromString("fcfcfb1fd94829c1a1704f894fc111d14770d34e"); + final MutableObjectId actid = new MutableObjectId(); + + init("003aACK fcfcfb1fd94829c1a1704f894fc111d14770d34e continue\n"); + assertSame(PacketLineIn.AckNackResult.ACK_CONTINUE, in.readACK(actid)); + assertTrue(actid.equals(expid)); + assertEOF(); + } + + public void testReadACK_Invalid1() { + init("HELO"); + try { + in.readACK(new MutableObjectId()); + fail("incorrectly accepted invalid packet header"); + } catch (IOException e) { + assertEquals("Invalid packet line header: HELO", e.getMessage()); + } + } + + public void testReadACK_Invalid2() { + init("0009HELO\n"); + try { + in.readACK(new MutableObjectId()); + fail("incorrectly accepted invalid ACK/NAK"); + } catch (IOException e) { + assertEquals("Expected ACK/NAK, got: HELO", e.getMessage()); + } + } + + public void testReadACK_Invalid3() { + init("0000"); + try { + in.readACK(new MutableObjectId()); + fail("incorrectly accepted no ACK/NAK"); + } catch (IOException e) { + assertEquals("Expected ACK/NAK, found EOF", e.getMessage()); + } + } + + // test support + + private void init(final String msg) { + rawIn = new ByteArrayInputStream(Constants.encodeASCII(msg)); + in = new PacketLineIn(rawIn); + } + + private void assertEOF() { + assertEquals(-1, rawIn.read()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineOutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineOutTest.java new file mode 100644 index 0000000000..6eb98ac126 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PacketLineOutTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 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.transport; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Constants; + +// Note, test vectors created with: +// +// perl -e 'printf "%4.4x%s\n", 4+length($ARGV[0]),$ARGV[0]' + +public class PacketLineOutTest extends TestCase { + private ByteArrayOutputStream rawOut; + + private PacketLineOut out; + + protected void setUp() throws Exception { + super.setUp(); + rawOut = new ByteArrayOutputStream(); + out = new PacketLineOut(rawOut); + } + + // writeString + + public void testWriteString1() throws IOException { + out.writeString("a"); + out.writeString("bc"); + assertBuffer("0005a0006bc"); + } + + public void testWriteString2() throws IOException { + out.writeString("a\n"); + out.writeString("bc\n"); + assertBuffer("0006a\n0007bc\n"); + } + + public void testWriteString3() throws IOException { + out.writeString(""); + assertBuffer("0004"); + } + + // end + + public void testWriteEnd() throws IOException { + final int[] flushCnt = new int[1]; + final OutputStream mockout = new OutputStream() { + @Override + public void write(int arg0) throws IOException { + rawOut.write(arg0); + } + + @Override + public void flush() throws IOException { + flushCnt[0]++; + } + }; + + new PacketLineOut(mockout).end(); + assertBuffer("0000"); + assertEquals(1, flushCnt[0]); + } + + // writePacket + + public void testWritePacket1() throws IOException { + out.writePacket(new byte[] { 'a' }); + assertBuffer("0005a"); + } + + public void testWritePacket2() throws IOException { + out.writePacket(new byte[] { 'a', 'b', 'c', 'd' }); + assertBuffer("0008abcd"); + } + + public void testWritePacket3() throws IOException { + final int buflen = SideBandOutputStream.MAX_BUF + - SideBandOutputStream.HDR_SIZE; + final byte[] buf = new byte[buflen]; + for (int i = 0; i < buf.length; i++) { + buf[i] = (byte) i; + } + out.writePacket(buf); + out.flush(); + + final byte[] act = rawOut.toByteArray(); + final String explen = Integer.toString(buf.length + 4, 16); + assertEquals(4 + buf.length, act.length); + assertEquals(new String(act, 0, 4, "UTF-8"), explen); + for (int i = 0, j = 4; i < buf.length; i++, j++) { + assertEquals(buf[i], act[j]); + } + } + + // writeChannelPacket + + public void testWriteChannelPacket1() throws IOException { + out.writeChannelPacket(1, new byte[] { 'a' }, 0, 1); + assertBuffer("0006\001a"); + } + + public void testWriteChannelPacket2() throws IOException { + out.writeChannelPacket(2, new byte[] { 'b' }, 0, 1); + assertBuffer("0006\002b"); + } + + public void testWriteChannelPacket3() throws IOException { + out.writeChannelPacket(3, new byte[] { 'c' }, 0, 1); + assertBuffer("0006\003c"); + } + + // flush + + public void testFlush() throws IOException { + final int[] flushCnt = new int[1]; + final OutputStream mockout = new OutputStream() { + @Override + public void write(int arg0) throws IOException { + fail("should not write"); + } + + @Override + public void flush() throws IOException { + flushCnt[0]++; + } + }; + + new PacketLineOut(mockout).flush(); + assertEquals(1, flushCnt[0]); + } + + private void assertBuffer(final String exp) throws IOException { + assertEquals(exp, new String(rawOut.toByteArray(), + Constants.CHARACTER_ENCODING)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java new file mode 100644 index 0000000000..4a47456f69 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> + * 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.transport; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import org.eclipse.jgit.errors.NotSupportedException; +import org.eclipse.jgit.errors.TransportException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ProgressMonitor; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.lib.TextProgressMonitor; +import org.eclipse.jgit.lib.RefUpdate.Result; +import org.eclipse.jgit.transport.RemoteRefUpdate.Status; + +public class PushProcessTest extends RepositoryTestCase { + private PushProcess process; + + private MockTransport transport; + + private HashSet<RemoteRefUpdate> refUpdates; + + private HashSet<Ref> advertisedRefs; + + private Status connectionUpdateStatus; + + @Override + public void setUp() throws Exception { + super.setUp(); + transport = new MockTransport(db, new URIish()); + refUpdates = new HashSet<RemoteRefUpdate>(); + advertisedRefs = new HashSet<Ref>(); + connectionUpdateStatus = Status.OK; + } + + /** + * Test for fast-forward remote update. + * + * @throws IOException + */ + public void testUpdateFastForward() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "2c349335b7f797072cf729c4f3bb0914ecb6dec9", + "refs/heads/master", false, null, null); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef")); + testOneUpdateStatus(rru, ref, Status.OK, true); + } + + /** + * Test for non fast-forward remote update, when remote object is not known + * to local repository. + * + * @throws IOException + */ + public void testUpdateNonFastForwardUnknownObject() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "2c349335b7f797072cf729c4f3bb0914ecb6dec9", + "refs/heads/master", false, null, null); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("0000000000000000000000000000000000000001")); + testOneUpdateStatus(rru, ref, Status.REJECTED_NONFASTFORWARD, null); + } + + /** + * Test for non fast-forward remote update, when remote object is known to + * local repository, but it is not an ancestor of new object. + * + * @throws IOException + */ + public void testUpdateNonFastForward() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "ac7e7e44c1885efb472ad54a78327d66bfc4ecef", + "refs/heads/master", false, null, null); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9")); + testOneUpdateStatus(rru, ref, Status.REJECTED_NONFASTFORWARD, null); + } + + /** + * Test for non fast-forward remote update, when force update flag is set. + * + * @throws IOException + */ + public void testUpdateNonFastForwardForced() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "ac7e7e44c1885efb472ad54a78327d66bfc4ecef", + "refs/heads/master", true, null, null); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9")); + testOneUpdateStatus(rru, ref, Status.OK, false); + } + + /** + * Test for remote ref creation. + * + * @throws IOException + */ + public void testUpdateCreateRef() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "ac7e7e44c1885efb472ad54a78327d66bfc4ecef", + "refs/heads/master", false, null, null); + testOneUpdateStatus(rru, null, Status.OK, true); + } + + /** + * Test for remote ref deletion. + * + * @throws IOException + */ + public void testUpdateDelete() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, null, + "refs/heads/master", false, null, null); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9")); + testOneUpdateStatus(rru, ref, Status.OK, true); + } + + /** + * Test for remote ref deletion (try), when that ref doesn't exist on remote + * repo. + * + * @throws IOException + */ + public void testUpdateDeleteNonExisting() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, null, + "refs/heads/master", false, null, null); + testOneUpdateStatus(rru, null, Status.NON_EXISTING, null); + } + + /** + * Test for remote ref update, when it is already up to date. + * + * @throws IOException + */ + public void testUpdateUpToDate() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "2c349335b7f797072cf729c4f3bb0914ecb6dec9", + "refs/heads/master", false, null, null); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9")); + testOneUpdateStatus(rru, ref, Status.UP_TO_DATE, null); + } + + /** + * Test for remote ref update with expected remote object. + * + * @throws IOException + */ + public void testUpdateExpectedRemote() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "2c349335b7f797072cf729c4f3bb0914ecb6dec9", + "refs/heads/master", false, null, ObjectId + .fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef")); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef")); + testOneUpdateStatus(rru, ref, Status.OK, true); + } + + /** + * Test for remote ref update with expected old object set, when old object + * is not that expected one. + * + * @throws IOException + */ + public void testUpdateUnexpectedRemote() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "2c349335b7f797072cf729c4f3bb0914ecb6dec9", + "refs/heads/master", false, null, ObjectId + .fromString("0000000000000000000000000000000000000001")); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef")); + testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null); + } + + /** + * Test for remote ref update with expected old object set, when old object + * is not that expected one and force update flag is set (which should have + * lower priority) - shouldn't change behavior. + * + * @throws IOException + */ + public void testUpdateUnexpectedRemoteVsForce() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "2c349335b7f797072cf729c4f3bb0914ecb6dec9", + "refs/heads/master", true, null, ObjectId + .fromString("0000000000000000000000000000000000000001")); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef")); + testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null); + } + + /** + * Test for remote ref update, when connection rejects update. + * + * @throws IOException + */ + public void testUpdateRejectedByConnection() throws IOException { + connectionUpdateStatus = Status.REJECTED_OTHER_REASON; + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "2c349335b7f797072cf729c4f3bb0914ecb6dec9", + "refs/heads/master", false, null, null); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef")); + testOneUpdateStatus(rru, ref, Status.REJECTED_OTHER_REASON, null); + } + + /** + * Test for remote refs updates with mixed cases that shouldn't depend on + * each other. + * + * @throws IOException + */ + public void testUpdateMixedCases() throws IOException { + final RemoteRefUpdate rruOk = new RemoteRefUpdate(db, null, + "refs/heads/master", false, null, null); + final Ref refToChange = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9")); + final RemoteRefUpdate rruReject = new RemoteRefUpdate(db, null, + "refs/heads/nonexisting", false, null, null); + refUpdates.add(rruOk); + refUpdates.add(rruReject); + advertisedRefs.add(refToChange); + executePush(); + assertEquals(Status.OK, rruOk.getStatus()); + assertEquals(true, rruOk.isFastForward()); + assertEquals(Status.NON_EXISTING, rruReject.getStatus()); + } + + /** + * Test for local tracking ref update. + * + * @throws IOException + */ + public void testTrackingRefUpdateEnabled() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "2c349335b7f797072cf729c4f3bb0914ecb6dec9", + "refs/heads/master", false, "refs/remotes/test/master", null); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef")); + refUpdates.add(rru); + advertisedRefs.add(ref); + final PushResult result = executePush(); + final TrackingRefUpdate tru = result + .getTrackingRefUpdate("refs/remotes/test/master"); + assertNotNull(tru); + assertEquals("refs/remotes/test/master", tru.getLocalName()); + assertEquals(Result.NEW, tru.getResult()); + } + + /** + * Test for local tracking ref update disabled. + * + * @throws IOException + */ + public void testTrackingRefUpdateDisabled() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "2c349335b7f797072cf729c4f3bb0914ecb6dec9", + "refs/heads/master", false, null, null); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef")); + refUpdates.add(rru); + advertisedRefs.add(ref); + final PushResult result = executePush(); + assertTrue(result.getTrackingRefUpdates().isEmpty()); + } + + /** + * Test for local tracking ref update when remote update has failed. + * + * @throws IOException + */ + public void testTrackingRefUpdateOnReject() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "ac7e7e44c1885efb472ad54a78327d66bfc4ecef", + "refs/heads/master", false, null, null); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("2c349335b7f797072cf729c4f3bb0914ecb6dec9")); + final PushResult result = testOneUpdateStatus(rru, ref, + Status.REJECTED_NONFASTFORWARD, null); + assertTrue(result.getTrackingRefUpdates().isEmpty()); + } + + /** + * Test for push operation result - that contains expected elements. + * + * @throws IOException + */ + public void testPushResult() throws IOException { + final RemoteRefUpdate rru = new RemoteRefUpdate(db, + "2c349335b7f797072cf729c4f3bb0914ecb6dec9", + "refs/heads/master", false, "refs/remotes/test/master", null); + final Ref ref = new Ref(Ref.Storage.LOOSE, "refs/heads/master", + ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef")); + refUpdates.add(rru); + advertisedRefs.add(ref); + final PushResult result = executePush(); + assertEquals(1, result.getTrackingRefUpdates().size()); + assertEquals(1, result.getAdvertisedRefs().size()); + assertEquals(1, result.getRemoteUpdates().size()); + assertNotNull(result.getTrackingRefUpdate("refs/remotes/test/master")); + assertNotNull(result.getAdvertisedRef("refs/heads/master")); + assertNotNull(result.getRemoteUpdate("refs/heads/master")); + } + + private PushResult testOneUpdateStatus(final RemoteRefUpdate rru, + final Ref advertisedRef, final Status expectedStatus, + Boolean fastForward) throws NotSupportedException, + TransportException { + refUpdates.add(rru); + if (advertisedRef != null) + advertisedRefs.add(advertisedRef); + final PushResult result = executePush(); + assertEquals(expectedStatus, rru.getStatus()); + if (fastForward != null) + assertEquals(fastForward.booleanValue(), rru.isFastForward()); + return result; + } + + private PushResult executePush() throws NotSupportedException, + TransportException { + process = new PushProcess(transport, refUpdates); + return process.execute(new TextProgressMonitor()); + } + + private class MockTransport extends Transport { + MockTransport(Repository local, URIish uri) { + super(local, uri); + } + + @Override + public FetchConnection openFetch() throws NotSupportedException, + TransportException { + throw new NotSupportedException("mock"); + } + + @Override + public PushConnection openPush() throws NotSupportedException, + TransportException { + return new MockPushConnection(); + } + + @Override + public void close() { + // nothing here + } + } + + private class MockPushConnection extends BaseConnection implements + PushConnection { + MockPushConnection() { + final Map<String, Ref> refsMap = new HashMap<String, Ref>(); + for (final Ref r : advertisedRefs) + refsMap.put(r.getName(), r); + available(refsMap); + } + + @Override + public void close() { + // nothing here + } + + public void push(ProgressMonitor monitor, + Map<String, RemoteRefUpdate> refsToUpdate) + throws TransportException { + for (final RemoteRefUpdate rru : refsToUpdate.values()) { + assertEquals(Status.NOT_ATTEMPTED, rru.getStatus()); + rru.setStatus(connectionUpdateStatus); + } + } + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java new file mode 100644 index 0000000000..38dbe0962e --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * 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.transport; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Ref; + +public class RefSpecTest extends TestCase { + public void testMasterMaster() { + final String sn = "refs/heads/master"; + final RefSpec rs = new RefSpec(sn + ":" + sn); + assertFalse(rs.isForceUpdate()); + assertFalse(rs.isWildcard()); + assertEquals(sn, rs.getSource()); + assertEquals(sn, rs.getDestination()); + assertEquals(sn + ":" + sn, rs.toString()); + assertEquals(rs, new RefSpec(rs.toString())); + + Ref r = new Ref(Ref.Storage.LOOSE, sn, null); + assertTrue(rs.matchSource(r)); + assertTrue(rs.matchDestination(r)); + assertSame(rs, rs.expandFromSource(r)); + + r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null); + assertFalse(rs.matchSource(r)); + assertFalse(rs.matchDestination(r)); + } + + public void testSplitLastColon() { + final String lhs = ":m:a:i:n:t"; + final String rhs = "refs/heads/maint"; + final RefSpec rs = new RefSpec(lhs + ":" + rhs); + assertFalse(rs.isForceUpdate()); + assertFalse(rs.isWildcard()); + assertEquals(lhs, rs.getSource()); + assertEquals(rhs, rs.getDestination()); + assertEquals(lhs + ":" + rhs, rs.toString()); + assertEquals(rs, new RefSpec(rs.toString())); + } + + public void testForceMasterMaster() { + final String sn = "refs/heads/master"; + final RefSpec rs = new RefSpec("+" + sn + ":" + sn); + assertTrue(rs.isForceUpdate()); + assertFalse(rs.isWildcard()); + assertEquals(sn, rs.getSource()); + assertEquals(sn, rs.getDestination()); + assertEquals("+" + sn + ":" + sn, rs.toString()); + assertEquals(rs, new RefSpec(rs.toString())); + + Ref r = new Ref(Ref.Storage.LOOSE, sn, null); + assertTrue(rs.matchSource(r)); + assertTrue(rs.matchDestination(r)); + assertSame(rs, rs.expandFromSource(r)); + + r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null); + assertFalse(rs.matchSource(r)); + assertFalse(rs.matchDestination(r)); + } + + public void testMaster() { + final String sn = "refs/heads/master"; + final RefSpec rs = new RefSpec(sn); + assertFalse(rs.isForceUpdate()); + assertFalse(rs.isWildcard()); + assertEquals(sn, rs.getSource()); + assertNull(rs.getDestination()); + assertEquals(sn, rs.toString()); + assertEquals(rs, new RefSpec(rs.toString())); + + Ref r = new Ref(Ref.Storage.LOOSE, sn, null); + assertTrue(rs.matchSource(r)); + assertFalse(rs.matchDestination(r)); + assertSame(rs, rs.expandFromSource(r)); + + r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null); + assertFalse(rs.matchSource(r)); + assertFalse(rs.matchDestination(r)); + } + + public void testForceMaster() { + final String sn = "refs/heads/master"; + final RefSpec rs = new RefSpec("+" + sn); + assertTrue(rs.isForceUpdate()); + assertFalse(rs.isWildcard()); + assertEquals(sn, rs.getSource()); + assertNull(rs.getDestination()); + assertEquals("+" + sn, rs.toString()); + assertEquals(rs, new RefSpec(rs.toString())); + + Ref r = new Ref(Ref.Storage.LOOSE, sn, null); + assertTrue(rs.matchSource(r)); + assertFalse(rs.matchDestination(r)); + assertSame(rs, rs.expandFromSource(r)); + + r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null); + assertFalse(rs.matchSource(r)); + assertFalse(rs.matchDestination(r)); + } + + public void testDeleteMaster() { + final String sn = "refs/heads/master"; + final RefSpec rs = new RefSpec(":" + sn); + assertFalse(rs.isForceUpdate()); + assertFalse(rs.isWildcard()); + assertNull(rs.getSource()); + assertEquals(sn, rs.getDestination()); + assertEquals(":" + sn, rs.toString()); + assertEquals(rs, new RefSpec(rs.toString())); + + Ref r = new Ref(Ref.Storage.LOOSE, sn, null); + assertFalse(rs.matchSource(r)); + assertTrue(rs.matchDestination(r)); + assertSame(rs, rs.expandFromSource(r)); + + r = new Ref(Ref.Storage.LOOSE, sn + "-and-more", null); + assertFalse(rs.matchSource(r)); + assertFalse(rs.matchDestination(r)); + } + + public void testForceRemotesOrigin() { + final String srcn = "refs/heads/*"; + final String dstn = "refs/remotes/origin/*"; + final RefSpec rs = new RefSpec("+" + srcn + ":" + dstn); + assertTrue(rs.isForceUpdate()); + assertTrue(rs.isWildcard()); + assertEquals(srcn, rs.getSource()); + assertEquals(dstn, rs.getDestination()); + assertEquals("+" + srcn + ":" + dstn, rs.toString()); + assertEquals(rs, new RefSpec(rs.toString())); + + Ref r; + RefSpec expanded; + + r = new Ref(Ref.Storage.LOOSE, "refs/heads/master", null); + assertTrue(rs.matchSource(r)); + assertFalse(rs.matchDestination(r)); + expanded = rs.expandFromSource(r); + assertNotSame(rs, expanded); + assertTrue(expanded.isForceUpdate()); + assertFalse(expanded.isWildcard()); + assertEquals(r.getName(), expanded.getSource()); + assertEquals("refs/remotes/origin/master", expanded.getDestination()); + + r = new Ref(Ref.Storage.LOOSE, "refs/remotes/origin/next", null); + assertFalse(rs.matchSource(r)); + assertTrue(rs.matchDestination(r)); + + r = new Ref(Ref.Storage.LOOSE, "refs/tags/v1.0", null); + assertFalse(rs.matchSource(r)); + assertFalse(rs.matchDestination(r)); + } + + public void testCreateEmpty() { + final RefSpec rs = new RefSpec(); + assertFalse(rs.isForceUpdate()); + assertFalse(rs.isWildcard()); + assertEquals("HEAD", rs.getSource()); + assertNull(rs.getDestination()); + assertEquals("HEAD", rs.toString()); + } + + public void testSetForceUpdate() { + final String s = "refs/heads/*:refs/remotes/origin/*"; + final RefSpec a = new RefSpec(s); + assertFalse(a.isForceUpdate()); + RefSpec b = a.setForceUpdate(true); + assertNotSame(a, b); + assertFalse(a.isForceUpdate()); + assertTrue(b.isForceUpdate()); + assertEquals(s, a.toString()); + assertEquals("+" + s, b.toString()); + } + + public void testSetSource() { + final RefSpec a = new RefSpec(); + final RefSpec b = a.setSource("refs/heads/master"); + assertNotSame(a, b); + assertEquals("HEAD", a.toString()); + assertEquals("refs/heads/master", b.toString()); + } + + public void testSetDestination() { + final RefSpec a = new RefSpec(); + final RefSpec b = a.setDestination("refs/heads/master"); + assertNotSame(a, b); + assertEquals("HEAD", a.toString()); + assertEquals("HEAD:refs/heads/master", b.toString()); + } + + public void testSetDestination_SourceNull() { + final RefSpec a = new RefSpec(); + RefSpec b; + + b = a.setDestination("refs/heads/master"); + b = b.setSource(null); + assertNotSame(a, b); + assertEquals("HEAD", a.toString()); + assertEquals(":refs/heads/master", b.toString()); + } + + public void testSetSourceDestination() { + final RefSpec a = new RefSpec(); + final RefSpec b; + b = a.setSourceDestination("refs/heads/*", "refs/remotes/origin/*"); + assertNotSame(a, b); + assertEquals("HEAD", a.toString()); + assertEquals("refs/heads/*:refs/remotes/origin/*", b.toString()); + } + + public void testExpandFromDestination_NonWildcard() { + final String src = "refs/heads/master"; + final String dst = "refs/remotes/origin/master"; + final RefSpec a = new RefSpec(src + ":" + dst); + final RefSpec r = a.expandFromDestination(dst); + assertSame(a, r); + assertFalse(r.isWildcard()); + assertEquals(src, r.getSource()); + assertEquals(dst, r.getDestination()); + } + + public void testExpandFromDestination_Wildcard() { + final String src = "refs/heads/master"; + final String dst = "refs/remotes/origin/master"; + final RefSpec a = new RefSpec("refs/heads/*:refs/remotes/origin/*"); + final RefSpec r = a.expandFromDestination(dst); + assertNotSame(a, r); + assertFalse(r.isWildcard()); + assertEquals(src, r.getSource()); + assertEquals(dst, r.getDestination()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RemoteConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RemoteConfigTest.java new file mode 100644 index 0000000000..3d9b51f9b6 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RemoteConfigTest.java @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * 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.transport; + +import java.util.List; + +import junit.framework.TestCase; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.Config; + +public class RemoteConfigTest extends TestCase { + private Config config; + + protected void setUp() throws Exception { + super.setUp(); + config = new Config(); + } + + private void readConfig(final String dat) throws ConfigInvalidException { + config = new Config(); + config.fromText(dat); + } + + private void checkConfig(final String exp) { + assertEquals(exp, config.toText()); + } + + private static void assertEquals(final String exp, final URIish act) { + assertEquals(exp, act != null ? act.toString() : null); + } + + public void testSimple() throws Exception { + readConfig("[remote \"spearce\"]\n" + + "url = http://www.spearce.org/egit.git\n" + + "fetch = +refs/heads/*:refs/remotes/spearce/*\n"); + + final RemoteConfig rc = new RemoteConfig(config, "spearce"); + final List<URIish> allURIs = rc.getURIs(); + RefSpec spec; + + assertEquals("spearce", rc.getName()); + assertNotNull(allURIs); + assertNotNull(rc.getFetchRefSpecs()); + assertNotNull(rc.getPushRefSpecs()); + assertNotNull(rc.getTagOpt()); + assertEquals(0, rc.getTimeout()); + assertSame(TagOpt.AUTO_FOLLOW, rc.getTagOpt()); + + assertEquals(1, allURIs.size()); + assertEquals("http://www.spearce.org/egit.git", allURIs.get(0)); + + assertEquals(1, rc.getFetchRefSpecs().size()); + spec = rc.getFetchRefSpecs().get(0); + assertTrue(spec.isForceUpdate()); + assertTrue(spec.isWildcard()); + assertEquals("refs/heads/*", spec.getSource()); + assertEquals("refs/remotes/spearce/*", spec.getDestination()); + + assertEquals(0, rc.getPushRefSpecs().size()); + } + + public void testSimpleNoTags() throws Exception { + readConfig("[remote \"spearce\"]\n" + + "url = http://www.spearce.org/egit.git\n" + + "fetch = +refs/heads/*:refs/remotes/spearce/*\n" + + "tagopt = --no-tags\n"); + final RemoteConfig rc = new RemoteConfig(config, "spearce"); + assertSame(TagOpt.NO_TAGS, rc.getTagOpt()); + } + + public void testSimpleAlwaysTags() throws Exception { + readConfig("[remote \"spearce\"]\n" + + "url = http://www.spearce.org/egit.git\n" + + "fetch = +refs/heads/*:refs/remotes/spearce/*\n" + + "tagopt = --tags\n"); + final RemoteConfig rc = new RemoteConfig(config, "spearce"); + assertSame(TagOpt.FETCH_TAGS, rc.getTagOpt()); + } + + public void testMirror() throws Exception { + readConfig("[remote \"spearce\"]\n" + + "url = http://www.spearce.org/egit.git\n" + + "fetch = +refs/heads/*:refs/heads/*\n" + + "fetch = refs/tags/*:refs/tags/*\n"); + + final RemoteConfig rc = new RemoteConfig(config, "spearce"); + final List<URIish> allURIs = rc.getURIs(); + RefSpec spec; + + assertEquals("spearce", rc.getName()); + assertNotNull(allURIs); + assertNotNull(rc.getFetchRefSpecs()); + assertNotNull(rc.getPushRefSpecs()); + + assertEquals(1, allURIs.size()); + assertEquals("http://www.spearce.org/egit.git", allURIs.get(0)); + + assertEquals(2, rc.getFetchRefSpecs().size()); + + spec = rc.getFetchRefSpecs().get(0); + assertTrue(spec.isForceUpdate()); + assertTrue(spec.isWildcard()); + assertEquals("refs/heads/*", spec.getSource()); + assertEquals("refs/heads/*", spec.getDestination()); + + spec = rc.getFetchRefSpecs().get(1); + assertFalse(spec.isForceUpdate()); + assertTrue(spec.isWildcard()); + assertEquals("refs/tags/*", spec.getSource()); + assertEquals("refs/tags/*", spec.getDestination()); + + assertEquals(0, rc.getPushRefSpecs().size()); + } + + public void testBackup() throws Exception { + readConfig("[remote \"backup\"]\n" + + "url = http://www.spearce.org/egit.git\n" + + "url = user@repo.or.cz:/srv/git/egit.git\n" + + "push = +refs/heads/*:refs/heads/*\n" + + "push = refs/tags/*:refs/tags/*\n"); + + final RemoteConfig rc = new RemoteConfig(config, "backup"); + final List<URIish> allURIs = rc.getURIs(); + RefSpec spec; + + assertEquals("backup", rc.getName()); + assertNotNull(allURIs); + assertNotNull(rc.getFetchRefSpecs()); + assertNotNull(rc.getPushRefSpecs()); + + assertEquals(2, allURIs.size()); + assertEquals("http://www.spearce.org/egit.git", allURIs.get(0)); + assertEquals("user@repo.or.cz:/srv/git/egit.git", allURIs.get(1)); + + assertEquals(0, rc.getFetchRefSpecs().size()); + + assertEquals(2, rc.getPushRefSpecs().size()); + spec = rc.getPushRefSpecs().get(0); + assertTrue(spec.isForceUpdate()); + assertTrue(spec.isWildcard()); + assertEquals("refs/heads/*", spec.getSource()); + assertEquals("refs/heads/*", spec.getDestination()); + + spec = rc.getPushRefSpecs().get(1); + assertFalse(spec.isForceUpdate()); + assertTrue(spec.isWildcard()); + assertEquals("refs/tags/*", spec.getSource()); + assertEquals("refs/tags/*", spec.getDestination()); + } + + public void testUploadPack() throws Exception { + readConfig("[remote \"example\"]\n" + + "url = user@example.com:egit.git\n" + + "fetch = +refs/heads/*:refs/remotes/example/*\n" + + "uploadpack = /path/to/git/git-upload-pack\n" + + "receivepack = /path/to/git/git-receive-pack\n"); + + final RemoteConfig rc = new RemoteConfig(config, "example"); + final List<URIish> allURIs = rc.getURIs(); + RefSpec spec; + + assertEquals("example", rc.getName()); + assertNotNull(allURIs); + assertNotNull(rc.getFetchRefSpecs()); + assertNotNull(rc.getPushRefSpecs()); + + assertEquals(1, allURIs.size()); + assertEquals("user@example.com:egit.git", allURIs.get(0)); + + assertEquals(1, rc.getFetchRefSpecs().size()); + spec = rc.getFetchRefSpecs().get(0); + assertTrue(spec.isForceUpdate()); + assertTrue(spec.isWildcard()); + assertEquals("refs/heads/*", spec.getSource()); + assertEquals("refs/remotes/example/*", spec.getDestination()); + + assertEquals(0, rc.getPushRefSpecs().size()); + + assertEquals("/path/to/git/git-upload-pack", rc.getUploadPack()); + assertEquals("/path/to/git/git-receive-pack", rc.getReceivePack()); + } + + public void testUnknown() throws Exception { + readConfig(""); + + final RemoteConfig rc = new RemoteConfig(config, "backup"); + assertEquals(0, rc.getURIs().size()); + assertEquals(0, rc.getFetchRefSpecs().size()); + assertEquals(0, rc.getPushRefSpecs().size()); + assertEquals("git-upload-pack", rc.getUploadPack()); + assertEquals("git-receive-pack", rc.getReceivePack()); + } + + public void testAddURI() throws Exception { + readConfig(""); + + final URIish uri = new URIish("/some/dir"); + final RemoteConfig rc = new RemoteConfig(config, "backup"); + assertEquals(0, rc.getURIs().size()); + + assertTrue(rc.addURI(uri)); + assertEquals(1, rc.getURIs().size()); + assertSame(uri, rc.getURIs().get(0)); + + assertFalse(rc.addURI(new URIish(uri.toString()))); + assertEquals(1, rc.getURIs().size()); + } + + public void testRemoveFirstURI() throws Exception { + readConfig(""); + + final URIish a = new URIish("/some/dir"); + final URIish b = new URIish("/another/dir"); + final URIish c = new URIish("/more/dirs"); + final RemoteConfig rc = new RemoteConfig(config, "backup"); + assertTrue(rc.addURI(a)); + assertTrue(rc.addURI(b)); + assertTrue(rc.addURI(c)); + + assertEquals(3, rc.getURIs().size()); + assertSame(a, rc.getURIs().get(0)); + assertSame(b, rc.getURIs().get(1)); + assertSame(c, rc.getURIs().get(2)); + + assertTrue(rc.removeURI(a)); + assertEquals(2, rc.getURIs().size()); + assertSame(b, rc.getURIs().get(0)); + assertSame(c, rc.getURIs().get(1)); + } + + public void testRemoveMiddleURI() throws Exception { + readConfig(""); + + final URIish a = new URIish("/some/dir"); + final URIish b = new URIish("/another/dir"); + final URIish c = new URIish("/more/dirs"); + final RemoteConfig rc = new RemoteConfig(config, "backup"); + assertTrue(rc.addURI(a)); + assertTrue(rc.addURI(b)); + assertTrue(rc.addURI(c)); + + assertEquals(3, rc.getURIs().size()); + assertSame(a, rc.getURIs().get(0)); + assertSame(b, rc.getURIs().get(1)); + assertSame(c, rc.getURIs().get(2)); + + assertTrue(rc.removeURI(b)); + assertEquals(2, rc.getURIs().size()); + assertSame(a, rc.getURIs().get(0)); + assertSame(c, rc.getURIs().get(1)); + } + + public void testRemoveLastURI() throws Exception { + readConfig(""); + + final URIish a = new URIish("/some/dir"); + final URIish b = new URIish("/another/dir"); + final URIish c = new URIish("/more/dirs"); + final RemoteConfig rc = new RemoteConfig(config, "backup"); + assertTrue(rc.addURI(a)); + assertTrue(rc.addURI(b)); + assertTrue(rc.addURI(c)); + + assertEquals(3, rc.getURIs().size()); + assertSame(a, rc.getURIs().get(0)); + assertSame(b, rc.getURIs().get(1)); + assertSame(c, rc.getURIs().get(2)); + + assertTrue(rc.removeURI(c)); + assertEquals(2, rc.getURIs().size()); + assertSame(a, rc.getURIs().get(0)); + assertSame(b, rc.getURIs().get(1)); + } + + public void testRemoveOnlyURI() throws Exception { + readConfig(""); + + final URIish a = new URIish("/some/dir"); + final RemoteConfig rc = new RemoteConfig(config, "backup"); + assertTrue(rc.addURI(a)); + + assertEquals(1, rc.getURIs().size()); + assertSame(a, rc.getURIs().get(0)); + + assertTrue(rc.removeURI(a)); + assertEquals(0, rc.getURIs().size()); + } + + public void testCreateOrigin() throws Exception { + final RemoteConfig rc = new RemoteConfig(config, "origin"); + rc.addURI(new URIish("/some/dir")); + rc.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/" + + rc.getName() + "/*")); + rc.update(config); + checkConfig("[remote \"origin\"]\n" + "\turl = /some/dir\n" + + "\tfetch = +refs/heads/*:refs/remotes/origin/*\n"); + } + + public void testSaveAddURI() throws Exception { + readConfig("[remote \"spearce\"]\n" + + "url = http://www.spearce.org/egit.git\n" + + "fetch = +refs/heads/*:refs/remotes/spearce/*\n"); + + final RemoteConfig rc = new RemoteConfig(config, "spearce"); + rc.addURI(new URIish("/some/dir")); + assertEquals(2, rc.getURIs().size()); + rc.update(config); + checkConfig("[remote \"spearce\"]\n" + + "\turl = http://www.spearce.org/egit.git\n" + + "\turl = /some/dir\n" + + "\tfetch = +refs/heads/*:refs/remotes/spearce/*\n"); + } + + public void testSaveRemoveLastURI() throws Exception { + readConfig("[remote \"spearce\"]\n" + + "url = http://www.spearce.org/egit.git\n" + + "url = /some/dir\n" + + "fetch = +refs/heads/*:refs/remotes/spearce/*\n"); + + final RemoteConfig rc = new RemoteConfig(config, "spearce"); + assertEquals(2, rc.getURIs().size()); + rc.removeURI(new URIish("/some/dir")); + assertEquals(1, rc.getURIs().size()); + rc.update(config); + checkConfig("[remote \"spearce\"]\n" + + "\turl = http://www.spearce.org/egit.git\n" + + "\tfetch = +refs/heads/*:refs/remotes/spearce/*\n"); + } + + public void testSaveRemoveFirstURI() throws Exception { + readConfig("[remote \"spearce\"]\n" + + "url = http://www.spearce.org/egit.git\n" + + "url = /some/dir\n" + + "fetch = +refs/heads/*:refs/remotes/spearce/*\n"); + + final RemoteConfig rc = new RemoteConfig(config, "spearce"); + assertEquals(2, rc.getURIs().size()); + rc.removeURI(new URIish("http://www.spearce.org/egit.git")); + assertEquals(1, rc.getURIs().size()); + rc.update(config); + checkConfig("[remote \"spearce\"]\n" + "\turl = /some/dir\n" + + "\tfetch = +refs/heads/*:refs/remotes/spearce/*\n"); + } + + public void testSaveNoTags() throws Exception { + final RemoteConfig rc = new RemoteConfig(config, "origin"); + rc.addURI(new URIish("/some/dir")); + rc.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/" + + rc.getName() + "/*")); + rc.setTagOpt(TagOpt.NO_TAGS); + rc.update(config); + checkConfig("[remote \"origin\"]\n" + "\turl = /some/dir\n" + + "\tfetch = +refs/heads/*:refs/remotes/origin/*\n" + + "\ttagopt = --no-tags\n"); + } + + public void testSaveAllTags() throws Exception { + final RemoteConfig rc = new RemoteConfig(config, "origin"); + rc.addURI(new URIish("/some/dir")); + rc.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/" + + rc.getName() + "/*")); + rc.setTagOpt(TagOpt.FETCH_TAGS); + rc.update(config); + checkConfig("[remote \"origin\"]\n" + "\turl = /some/dir\n" + + "\tfetch = +refs/heads/*:refs/remotes/origin/*\n" + + "\ttagopt = --tags\n"); + } + + public void testSimpleTimeout() throws Exception { + readConfig("[remote \"spearce\"]\n" + + "url = http://www.spearce.org/egit.git\n" + + "fetch = +refs/heads/*:refs/remotes/spearce/*\n" + + "timeout = 12\n"); + final RemoteConfig rc = new RemoteConfig(config, "spearce"); + assertEquals(12, rc.getTimeout()); + } + + public void testSaveTimeout() throws Exception { + final RemoteConfig rc = new RemoteConfig(config, "origin"); + rc.addURI(new URIish("/some/dir")); + rc.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/" + + rc.getName() + "/*")); + rc.setTimeout(60); + rc.update(config); + checkConfig("[remote \"origin\"]\n" + "\turl = /some/dir\n" + + "\tfetch = +refs/heads/*:refs/remotes/origin/*\n" + + "\ttimeout = 60\n"); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java new file mode 100644 index 0000000000..3c79f138c8 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 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.transport; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Constants; + +// Note, test vectors created with: +// +// perl -e 'printf "%4.4x%s\n", 4+length($ARGV[0]),$ARGV[0]' + +public class SideBandOutputStreamTest extends TestCase { + private ByteArrayOutputStream rawOut; + + private PacketLineOut pckOut; + + protected void setUp() throws Exception { + super.setUp(); + rawOut = new ByteArrayOutputStream(); + pckOut = new PacketLineOut(rawOut); + } + + public void testWrite_CH_DATA() throws IOException { + final SideBandOutputStream out; + out = new SideBandOutputStream(SideBandOutputStream.CH_DATA, pckOut); + out.write(new byte[] { 'a', 'b', 'c' }); + assertBuffer("0008\001abc"); + } + + public void testWrite_CH_PROGRESS() throws IOException { + final SideBandOutputStream out; + out = new SideBandOutputStream(SideBandOutputStream.CH_PROGRESS, pckOut); + out.write(new byte[] { 'a', 'b', 'c' }); + assertBuffer("0008\002abc"); + } + + public void testWrite_CH_ERROR() throws IOException { + final SideBandOutputStream out; + out = new SideBandOutputStream(SideBandOutputStream.CH_ERROR, pckOut); + out.write(new byte[] { 'a', 'b', 'c' }); + assertBuffer("0008\003abc"); + } + + public void testWrite_Small() throws IOException { + final SideBandOutputStream out; + out = new SideBandOutputStream(SideBandOutputStream.CH_DATA, pckOut); + out.write('a'); + out.write('b'); + out.write('c'); + assertBuffer("0006\001a0006\001b0006\001c"); + } + + public void testWrite_Large() throws IOException { + final int buflen = SideBandOutputStream.MAX_BUF + - SideBandOutputStream.HDR_SIZE; + final byte[] buf = new byte[buflen]; + for (int i = 0; i < buf.length; i++) { + buf[i] = (byte) i; + } + + final SideBandOutputStream out; + out = new SideBandOutputStream(SideBandOutputStream.CH_DATA, pckOut); + out.write(buf); + + final byte[] act = rawOut.toByteArray(); + final String explen = Integer.toString(buf.length + 5, 16); + assertEquals(5 + buf.length, act.length); + assertEquals(new String(act, 0, 4, "UTF-8"), explen); + assertEquals(1, act[4]); + for (int i = 0, j = 5; i < buf.length; i++, j++) { + assertEquals(buf[i], act[j]); + } + } + + public void testFlush() throws IOException { + final int[] flushCnt = new int[1]; + final OutputStream mockout = new OutputStream() { + @Override + public void write(int arg0) throws IOException { + fail("should not write"); + } + + @Override + public void flush() throws IOException { + flushCnt[0]++; + } + }; + + new SideBandOutputStream(SideBandOutputStream.CH_DATA, + new PacketLineOut(mockout)).flush(); + assertEquals(0, flushCnt[0]); + + new SideBandOutputStream(SideBandOutputStream.CH_ERROR, + new PacketLineOut(mockout)).flush(); + assertEquals(1, flushCnt[0]); + + new SideBandOutputStream(SideBandOutputStream.CH_PROGRESS, + new PacketLineOut(mockout)).flush(); + assertEquals(2, flushCnt[0]); + } + + private void assertBuffer(final String exp) throws IOException { + assertEquals(exp, new String(rawOut.toByteArray(), + Constants.CHARACTER_ENCODING)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java new file mode 100644 index 0000000000..75e661bcf9 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> + * 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.transport; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import org.eclipse.jgit.lib.RepositoryConfig; +import org.eclipse.jgit.lib.RepositoryTestCase; + +public class TransportTest extends RepositoryTestCase { + private Transport transport; + + private RemoteConfig remoteConfig; + + @Override + public void setUp() throws Exception { + super.setUp(); + final RepositoryConfig config = db.getConfig(); + remoteConfig = new RemoteConfig(config, "test"); + remoteConfig.addURI(new URIish("http://everyones.loves.git/u/2")); + transport = null; + } + + @Override + protected void tearDown() throws Exception { + if (transport != null) { + transport.close(); + transport = null; + } + super.tearDown(); + } + + /** + * Test RefSpec to RemoteRefUpdate conversion with simple RefSpec - no + * wildcard, no tracking ref in repo configuration. + * + * @throws IOException + */ + public void testFindRemoteRefUpdatesNoWildcardNoTracking() + throws IOException { + transport = Transport.open(db, remoteConfig); + final Collection<RemoteRefUpdate> result = transport + .findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec( + "refs/heads/master:refs/heads/x"))); + + assertEquals(1, result.size()); + final RemoteRefUpdate rru = result.iterator().next(); + assertNull(rru.getExpectedOldObjectId()); + assertFalse(rru.isForceUpdate()); + assertEquals("refs/heads/master", rru.getSrcRef()); + assertEquals(db.resolve("refs/heads/master"), rru.getNewObjectId()); + assertEquals("refs/heads/x", rru.getRemoteName()); + } + + /** + * Test RefSpec to RemoteRefUpdate conversion with no-destination RefSpec + * (destination should be set up for the same name as source). + * + * @throws IOException + */ + public void testFindRemoteRefUpdatesNoWildcardNoDestination() + throws IOException { + transport = Transport.open(db, remoteConfig); + final Collection<RemoteRefUpdate> result = transport + .findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec( + "+refs/heads/master"))); + + assertEquals(1, result.size()); + final RemoteRefUpdate rru = result.iterator().next(); + assertNull(rru.getExpectedOldObjectId()); + assertTrue(rru.isForceUpdate()); + assertEquals("refs/heads/master", rru.getSrcRef()); + assertEquals(db.resolve("refs/heads/master"), rru.getNewObjectId()); + assertEquals("refs/heads/master", rru.getRemoteName()); + } + + /** + * Test RefSpec to RemoteRefUpdate conversion with wildcard RefSpec. + * + * @throws IOException + */ + public void testFindRemoteRefUpdatesWildcardNoTracking() throws IOException { + transport = Transport.open(db, remoteConfig); + final Collection<RemoteRefUpdate> result = transport + .findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec( + "+refs/heads/*:refs/heads/test/*"))); + + assertEquals(12, result.size()); + boolean foundA = false; + boolean foundB = false; + for (final RemoteRefUpdate rru : result) { + if ("refs/heads/a".equals(rru.getSrcRef()) + && "refs/heads/test/a".equals(rru.getRemoteName())) + foundA = true; + if ("refs/heads/b".equals(rru.getSrcRef()) + && "refs/heads/test/b".equals(rru.getRemoteName())) + foundB = true; + } + assertTrue(foundA); + assertTrue(foundB); + } + + /** + * Test RefSpec to RemoteRefUpdate conversion for more than one RefSpecs + * handling. + * + * @throws IOException + */ + public void testFindRemoteRefUpdatesTwoRefSpecs() throws IOException { + transport = Transport.open(db, remoteConfig); + final RefSpec specA = new RefSpec("+refs/heads/a:refs/heads/b"); + final RefSpec specC = new RefSpec("+refs/heads/c:refs/heads/d"); + final Collection<RefSpec> specs = Arrays.asList(specA, specC); + final Collection<RemoteRefUpdate> result = transport + .findRemoteRefUpdatesFor(specs); + + assertEquals(2, result.size()); + boolean foundA = false; + boolean foundC = false; + for (final RemoteRefUpdate rru : result) { + if ("refs/heads/a".equals(rru.getSrcRef()) + && "refs/heads/b".equals(rru.getRemoteName())) + foundA = true; + if ("refs/heads/c".equals(rru.getSrcRef()) + && "refs/heads/d".equals(rru.getRemoteName())) + foundC = true; + } + assertTrue(foundA); + assertTrue(foundC); + } + + /** + * Test RefSpec to RemoteRefUpdate conversion for tracking ref search. + * + * @throws IOException + */ + public void testFindRemoteRefUpdatesTrackingRef() throws IOException { + remoteConfig.addFetchRefSpec(new RefSpec( + "refs/heads/*:refs/remotes/test/*")); + transport = Transport.open(db, remoteConfig); + final Collection<RemoteRefUpdate> result = transport + .findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec( + "+refs/heads/a:refs/heads/a"))); + + assertEquals(1, result.size()); + final TrackingRefUpdate tru = result.iterator().next() + .getTrackingRefUpdate(); + assertEquals("refs/remotes/test/a", tru.getLocalName()); + assertEquals("refs/heads/a", tru.getRemoteName()); + assertEquals(db.resolve("refs/heads/a"), tru.getNewObjectId()); + assertNull(tru.getOldObjectId()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java new file mode 100644 index 0000000000..2598fdc1f5 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com> + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * 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.transport; + +import junit.framework.TestCase; + +public class URIishTest extends TestCase { + + public void testUnixFile() throws Exception { + final String str = "/home/m y"; + URIish u = new URIish(str); + assertNull(u.getScheme()); + assertFalse(u.isRemote()); + assertEquals(str, u.getPath()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testWindowsFile() throws Exception { + final String str = "D:/m y"; + URIish u = new URIish(str); + assertNull(u.getScheme()); + assertFalse(u.isRemote()); + assertEquals(str, u.getPath()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testWindowsFile2() throws Exception { + final String str = "D:\\m y"; + URIish u = new URIish(str); + assertNull(u.getScheme()); + assertFalse(u.isRemote()); + assertEquals("D:/m y", u.getPath()); + assertEquals("D:/m y", u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testUNC() throws Exception { + final String str = "\\\\some\\place"; + URIish u = new URIish(str); + assertNull(u.getScheme()); + assertFalse(u.isRemote()); + assertEquals("//some/place", u.getPath()); + assertEquals("//some/place", u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testFileProtoUnix() throws Exception { + final String str = "file:///home/m y"; + URIish u = new URIish(str); + assertEquals("file", u.getScheme()); + assertFalse(u.isRemote()); + assertEquals("/home/m y", u.getPath()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testFileProtoWindows() throws Exception { + final String str = "file:///D:/m y"; + URIish u = new URIish(str); + assertEquals("file", u.getScheme()); + assertFalse(u.isRemote()); + assertEquals("D:/m y", u.getPath()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testGitProtoUnix() throws Exception { + final String str = "git://example.com/home/m y"; + URIish u = new URIish(str); + assertEquals("git", u.getScheme()); + assertTrue(u.isRemote()); + assertEquals("example.com", u.getHost()); + assertEquals("/home/m y", u.getPath()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testGitProtoUnixPort() throws Exception { + final String str = "git://example.com:333/home/m y"; + URIish u = new URIish(str); + assertEquals("git", u.getScheme()); + assertTrue(u.isRemote()); + assertEquals("example.com", u.getHost()); + assertEquals("/home/m y", u.getPath()); + assertEquals(333, u.getPort()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testGitProtoWindowsPort() throws Exception { + final String str = "git://example.com:338/D:/m y"; + URIish u = new URIish(str); + assertEquals("git", u.getScheme()); + assertTrue(u.isRemote()); + assertEquals("D:/m y", u.getPath()); + assertEquals(338, u.getPort()); + assertEquals("example.com", u.getHost()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testGitProtoWindows() throws Exception { + final String str = "git://example.com/D:/m y"; + URIish u = new URIish(str); + assertEquals("git", u.getScheme()); + assertTrue(u.isRemote()); + assertEquals("D:/m y", u.getPath()); + assertEquals("example.com", u.getHost()); + assertEquals(-1, u.getPort()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testScpStyleWithoutUser() throws Exception { + final String str = "example.com:some/p ath"; + URIish u = new URIish(str); + assertNull(u.getScheme()); + assertTrue(u.isRemote()); + assertEquals("some/p ath", u.getPath()); + assertEquals("example.com", u.getHost()); + assertEquals(-1, u.getPort()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testScpStyleWithUser() throws Exception { + final String str = "user@example.com:some/p ath"; + URIish u = new URIish(str); + assertNull(u.getScheme()); + assertTrue(u.isRemote()); + assertEquals("some/p ath", u.getPath()); + assertEquals("user", u.getUser()); + assertEquals("example.com", u.getHost()); + assertEquals(-1, u.getPort()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testGitSshProto() throws Exception { + final String str = "git+ssh://example.com/some/p ath"; + URIish u = new URIish(str); + assertEquals("git+ssh", u.getScheme()); + assertTrue(u.isRemote()); + assertEquals("/some/p ath", u.getPath()); + assertEquals("example.com", u.getHost()); + assertEquals(-1, u.getPort()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testSshGitProto() throws Exception { + final String str = "ssh+git://example.com/some/p ath"; + URIish u = new URIish(str); + assertEquals("ssh+git", u.getScheme()); + assertTrue(u.isRemote()); + assertEquals("/some/p ath", u.getPath()); + assertEquals("example.com", u.getHost()); + assertEquals(-1, u.getPort()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testSshProto() throws Exception { + final String str = "ssh://example.com/some/p ath"; + URIish u = new URIish(str); + assertEquals("ssh", u.getScheme()); + assertTrue(u.isRemote()); + assertEquals("/some/p ath", u.getPath()); + assertEquals("example.com", u.getHost()); + assertEquals(-1, u.getPort()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testSshProtoWithUserAndPort() throws Exception { + final String str = "ssh://user@example.com:33/some/p ath"; + URIish u = new URIish(str); + assertEquals("ssh", u.getScheme()); + assertTrue(u.isRemote()); + assertEquals("/some/p ath", u.getPath()); + assertEquals("example.com", u.getHost()); + assertEquals("user", u.getUser()); + assertNull(u.getPass()); + assertEquals(33, u.getPort()); + assertEquals(str, u.toString()); + assertEquals(u, new URIish(str)); + } + + public void testSshProtoWithUserPassAndPort() throws Exception { + final String str = "ssh://user:pass@example.com:33/some/p ath"; + URIish u = new URIish(str); + assertEquals("ssh", u.getScheme()); + assertTrue(u.isRemote()); + assertEquals("/some/p ath", u.getPath()); + assertEquals("example.com", u.getHost()); + assertEquals("user", u.getUser()); + assertEquals("pass", u.getPass()); + assertEquals(33, u.getPort()); + assertEquals(str, u.toPrivateString()); + assertEquals(u.setPass(null).toPrivateString(), u.toString()); + assertEquals(u, new URIish(str)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java new file mode 100644 index 0000000000..e96445a30a --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2009, Tor Arne Vestbø <torarnv@gmail.com> + * 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 java.io.IOException; + +import junit.framework.TestCase; + +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.Repository; + + +public class AbstractTreeIteratorTest extends TestCase { + private static String prefix(String path) { + final int s = path.lastIndexOf('/'); + return s > 0 ? path.substring(0, s) : ""; + } + + public class FakeTreeIterator extends WorkingTreeIterator { + public FakeTreeIterator(String pathName, FileMode fileMode) { + super(prefix(pathName)); + mode = fileMode.getBits(); + + final int s = pathName.lastIndexOf('/'); + final byte[] name = Constants.encode(pathName.substring(s + 1)); + ensurePathCapacity(pathOffset + name.length, pathOffset); + System.arraycopy(name, 0, path, pathOffset, name.length); + pathLen = pathOffset + name.length; + } + + @Override + public AbstractTreeIterator createSubtreeIterator(Repository repo) + throws IncorrectObjectTypeException, IOException { + return null; + } + } + + public void testPathCompare() throws Exception { + assertTrue(new FakeTreeIterator("a", FileMode.REGULAR_FILE).pathCompare( + new FakeTreeIterator("a", FileMode.TREE)) < 0); + + assertTrue(new FakeTreeIterator("a", FileMode.TREE).pathCompare( + new FakeTreeIterator("a", FileMode.REGULAR_FILE)) > 0); + + assertTrue(new FakeTreeIterator("a", FileMode.REGULAR_FILE).pathCompare( + new FakeTreeIterator("a", FileMode.REGULAR_FILE)) == 0); + + assertTrue(new FakeTreeIterator("a", FileMode.TREE).pathCompare( + new FakeTreeIterator("a", FileMode.TREE)) == 0); + } + + public void testGrowPath() throws Exception { + final FakeTreeIterator i = new FakeTreeIterator("ab", FileMode.TREE); + final byte[] origpath = i.path; + assertEquals(i.path[0], 'a'); + assertEquals(i.path[1], 'b'); + + i.growPath(2); + + assertNotSame(origpath, i.path); + assertEquals(origpath.length * 2, i.path.length); + assertEquals(i.path[0], 'a'); + assertEquals(i.path[1], 'b'); + } + + public void testEnsurePathCapacityFastCase() throws Exception { + final FakeTreeIterator i = new FakeTreeIterator("ab", FileMode.TREE); + final int want = 50; + final byte[] origpath = i.path; + assertEquals(i.path[0], 'a'); + assertEquals(i.path[1], 'b'); + assertTrue(want < i.path.length); + + i.ensurePathCapacity(want, 2); + + assertSame(origpath, i.path); + assertEquals(i.path[0], 'a'); + assertEquals(i.path[1], 'b'); + } + + public void testEnsurePathCapacityGrows() throws Exception { + final FakeTreeIterator i = new FakeTreeIterator("ab", FileMode.TREE); + final int want = 384; + final byte[] origpath = i.path; + assertEquals(i.path[0], 'a'); + assertEquals(i.path[1], 'b'); + assertTrue(i.path.length < want); + + i.ensurePathCapacity(want, 2); + + assertNotSame(origpath, i.path); + assertEquals(512, i.path.length); + assertEquals(i.path[0], 'a'); + assertEquals(i.path[1], 'b'); + } + + public void testEntryFileMode() { + for (FileMode m : new FileMode[] { FileMode.TREE, + FileMode.REGULAR_FILE, FileMode.EXECUTABLE_FILE, + FileMode.GITLINK, FileMode.SYMLINK }) { + final FakeTreeIterator i = new FakeTreeIterator("a", m); + assertEquals(m.getBits(), i.getEntryRawMode()); + assertSame(m, i.getEntryFileMode()); + } + } + + public void testEntryPath() { + FakeTreeIterator i = new FakeTreeIterator("a/b/cd", FileMode.TREE); + assertEquals("a/b/cd", i.getEntryPathString()); + assertEquals(2, i.getNameLength()); + byte[] b = new byte[3]; + b[0] = 0x0a; + i.getName(b, 1); + assertEquals(0x0a, b[0]); + assertEquals('c', b[1]); + assertEquals('d', b[2]); + } + + public void testCreateEmptyTreeIterator() { + FakeTreeIterator i = new FakeTreeIterator("a/b/cd", FileMode.TREE); + EmptyTreeIterator e = i.createEmptyTreeIterator(); + assertNotNull(e); + assertEquals(i.getEntryPathString() + "/", e.getEntryPathString()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java new file mode 100644 index 0000000000..da25f8d632 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java @@ -0,0 +1,353 @@ +/* + * 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 java.io.ByteArrayOutputStream; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.util.RawParseUtils; + +public class CanonicalTreeParserTest extends TestCase { + 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; + + public void setUp() throws Exception { + super.setUp(); + + 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(final byte[]... data) throws Exception { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (final 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(Constants.CHARSET, ctp.path, + ctp.pathOffset, ctp.pathLen); + } + + public void testEmptyTree_AtEOF() throws Exception { + ctp.reset(new byte[0]); + assertTrue(ctp.eof()); + } + + 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()); + } + + 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()); + } + + public void testOneEntry_Seek1IsEOF() throws Exception { + ctp.reset(tree1); + ctp.next(1); + assertTrue(ctp.eof()); + } + + public void testTwoEntries_Seek2IsEOF() throws Exception { + ctp.reset(tree2); + ctp.next(2); + assertTrue(ctp.eof()); + } + + public void testThreeEntries_Seek3IsEOF() throws Exception { + ctp.reset(tree3); + ctp.next(3); + assertTrue(ctp.eof()); + } + + 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()); + } + + 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()); + } + + 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()); + } + + 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()); + } + + 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()); + } + + 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()); + } + + 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()); + } + + 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()); + } + + 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(Constants.CHARSET, ctp.path, + ctp.pathOffset, ctp.pathLen)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/EmptyTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/EmptyTreeIteratorTest.java new file mode 100644 index 0000000000..111264b1c9 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/EmptyTreeIteratorTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2008, 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 org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.RepositoryTestCase; + +public class EmptyTreeIteratorTest extends RepositoryTestCase { + public void testAtEOF() throws Exception { + final EmptyTreeIterator etp = new EmptyTreeIterator(); + assertTrue(etp.first()); + assertTrue(etp.eof()); + } + + public void testCreateSubtreeIterator() throws Exception { + final EmptyTreeIterator etp = new EmptyTreeIterator(); + final AbstractTreeIterator sub = etp.createSubtreeIterator(db); + assertNotNull(sub); + assertTrue(sub.first()); + assertTrue(sub.eof()); + assertTrue(sub instanceof EmptyTreeIterator); + } + + public void testEntryObjectId() throws Exception { + final EmptyTreeIterator etp = new EmptyTreeIterator(); + assertSame(ObjectId.zeroId(), etp.getEntryObjectId()); + assertNotNull(etp.idBuffer()); + assertEquals(0, etp.idOffset()); + assertEquals(ObjectId.zeroId(), ObjectId.fromRaw(etp.idBuffer())); + } + + public void testNextDoesNothing() throws Exception { + final EmptyTreeIterator etp = new EmptyTreeIterator(); + etp.next(1); + assertTrue(etp.first()); + assertTrue(etp.eof()); + assertEquals(ObjectId.zeroId(), ObjectId.fromRaw(etp.idBuffer())); + + etp.next(1); + assertTrue(etp.first()); + assertTrue(etp.eof()); + assertEquals(ObjectId.zeroId(), ObjectId.fromRaw(etp.idBuffer())); + } + + public void testBackDoesNothing() throws Exception { + final EmptyTreeIterator etp = new EmptyTreeIterator(); + etp.back(1); + assertTrue(etp.first()); + assertTrue(etp.eof()); + assertEquals(ObjectId.zeroId(), ObjectId.fromRaw(etp.idBuffer())); + + etp.back(1); + assertTrue(etp.first()); + assertTrue(etp.eof()); + assertEquals(ObjectId.zeroId(), ObjectId.fromRaw(etp.idBuffer())); + } + + public void testStopWalkCallsParent() throws Exception { + final boolean called[] = new boolean[1]; + assertFalse(called[0]); + + final EmptyTreeIterator parent = new EmptyTreeIterator() { + @Override + public void stopWalk() { + called[0] = true; + } + }; + parent.createSubtreeIterator(db).stopWalk(); + assertTrue(called[0]); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java new file mode 100644 index 0000000000..081290310a --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2008, 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 java.io.File; +import java.security.MessageDigest; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.util.RawParseUtils; + +public class FileTreeIteratorTest extends RepositoryTestCase { + private final String[] paths = { "a,", "a,b", "a/b", "a0b" }; + + private long[] mtime; + + public void setUp() throws Exception { + super.setUp(); + + // We build the entries backwards so that on POSIX systems we + // are likely to get the entries in the trash directory in the + // opposite order of what they should be in for the iteration. + // This should stress the sorting code better than doing it in + // the correct order. + // + mtime = new long[paths.length]; + for (int i = paths.length - 1; i >= 0; i--) { + final String s = paths[i]; + writeTrashFile(s, s); + mtime[i] = new File(trash, s).lastModified(); + } + } + + public void testEmptyIfRootIsFile() throws Exception { + final File r = new File(trash, paths[0]); + assertTrue(r.isFile()); + final FileTreeIterator fti = new FileTreeIterator(r); + assertTrue(fti.first()); + assertTrue(fti.eof()); + } + + public void testEmptyIfRootDoesNotExist() throws Exception { + final File r = new File(trash, "not-existing-file"); + assertFalse(r.exists()); + final FileTreeIterator fti = new FileTreeIterator(r); + assertTrue(fti.first()); + assertTrue(fti.eof()); + } + + public void testEmptyIfRootIsEmpty() throws Exception { + final File r = new File(trash, "not-existing-file"); + assertFalse(r.exists()); + r.mkdir(); + assertTrue(r.isDirectory()); + + final FileTreeIterator fti = new FileTreeIterator(r); + assertTrue(fti.first()); + assertTrue(fti.eof()); + } + + public void testSimpleIterate() throws Exception { + final FileTreeIterator top = new FileTreeIterator(trash); + + assertTrue(top.first()); + assertFalse(top.eof()); + assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode); + assertEquals(paths[0], nameOf(top)); + assertEquals(paths[0].length(), top.getEntryLength()); + assertEquals(mtime[0], top.getEntryLastModified()); + + top.next(1); + assertFalse(top.first()); + assertFalse(top.eof()); + assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode); + assertEquals(paths[1], nameOf(top)); + assertEquals(paths[1].length(), top.getEntryLength()); + assertEquals(mtime[1], top.getEntryLastModified()); + + top.next(1); + assertFalse(top.first()); + assertFalse(top.eof()); + assertEquals(FileMode.TREE.getBits(), top.mode); + + final AbstractTreeIterator sub = top.createSubtreeIterator(db); + assertTrue(sub instanceof FileTreeIterator); + final FileTreeIterator subfti = (FileTreeIterator) sub; + assertTrue(sub.first()); + assertFalse(sub.eof()); + assertEquals(paths[2], nameOf(sub)); + assertEquals(paths[2].length(), subfti.getEntryLength()); + assertEquals(mtime[2], subfti.getEntryLastModified()); + + sub.next(1); + assertTrue(sub.eof()); + + top.next(1); + assertFalse(top.first()); + assertFalse(top.eof()); + assertEquals(FileMode.REGULAR_FILE.getBits(), top.mode); + assertEquals(paths[3], nameOf(top)); + assertEquals(paths[3].length(), top.getEntryLength()); + assertEquals(mtime[3], top.getEntryLastModified()); + + top.next(1); + assertTrue(top.eof()); + } + + public void testComputeFileObjectId() throws Exception { + final FileTreeIterator top = new FileTreeIterator(trash); + + final MessageDigest md = Constants.newMessageDigest(); + md.update(Constants.encodeASCII(Constants.TYPE_BLOB)); + md.update((byte) ' '); + md.update(Constants.encodeASCII(paths[0].length())); + md.update((byte) 0); + md.update(Constants.encode(paths[0])); + final ObjectId expect = ObjectId.fromRaw(md.digest()); + + assertEquals(expect, top.getEntryObjectId()); + + // Verify it was cached by removing the file and getting it again. + // + new File(trash, paths[0]).delete(); + assertEquals(expect, top.getEntryObjectId()); + } + + private static String nameOf(final AbstractTreeIterator i) { + return RawParseUtils.decode(Constants.CHARSET, i.path, 0, i.pathLen); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java new file mode 100644 index 0000000000..35298b803f --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/NameConflictTreeWalkTest.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2008, 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 java.io.ByteArrayInputStream; + +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheBuilder; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.dircache.DirCacheIterator; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectWriter; +import org.eclipse.jgit.lib.RepositoryTestCase; + +public class NameConflictTreeWalkTest extends RepositoryTestCase { + private static final FileMode TREE = FileMode.TREE; + + private static final FileMode SYMLINK = FileMode.SYMLINK; + + private static final FileMode MISSING = FileMode.MISSING; + + private static final FileMode REGULAR_FILE = FileMode.REGULAR_FILE; + + private static final FileMode EXECUTABLE_FILE = FileMode.EXECUTABLE_FILE; + + public void testNoDF_NoGap() throws Exception { + final DirCache tree0 = DirCache.read(db); + final DirCache tree1 = DirCache.read(db); + { + final DirCacheBuilder b0 = tree0.builder(); + final DirCacheBuilder b1 = tree1.builder(); + + b0.add(makeEntry("a", REGULAR_FILE)); + b0.add(makeEntry("a.b", EXECUTABLE_FILE)); + b1.add(makeEntry("a/b", REGULAR_FILE)); + b0.add(makeEntry("a0b", SYMLINK)); + + b0.finish(); + b1.finish(); + assertEquals(3, tree0.getEntryCount()); + assertEquals(1, tree1.getEntryCount()); + } + + final TreeWalk tw = new TreeWalk(db); + tw.reset(); + tw.addTree(new DirCacheIterator(tree0)); + tw.addTree(new DirCacheIterator(tree1)); + + assertModes("a", REGULAR_FILE, MISSING, tw); + assertModes("a.b", EXECUTABLE_FILE, MISSING, tw); + assertModes("a", MISSING, TREE, tw); + tw.enterSubtree(); + assertModes("a/b", MISSING, REGULAR_FILE, tw); + assertModes("a0b", SYMLINK, MISSING, tw); + } + + public void testDF_NoGap() throws Exception { + final DirCache tree0 = DirCache.read(db); + final DirCache tree1 = DirCache.read(db); + { + final DirCacheBuilder b0 = tree0.builder(); + final DirCacheBuilder b1 = tree1.builder(); + + b0.add(makeEntry("a", REGULAR_FILE)); + b0.add(makeEntry("a.b", EXECUTABLE_FILE)); + b1.add(makeEntry("a/b", REGULAR_FILE)); + b0.add(makeEntry("a0b", SYMLINK)); + + b0.finish(); + b1.finish(); + assertEquals(3, tree0.getEntryCount()); + assertEquals(1, tree1.getEntryCount()); + } + + final NameConflictTreeWalk tw = new NameConflictTreeWalk(db); + tw.reset(); + tw.addTree(new DirCacheIterator(tree0)); + tw.addTree(new DirCacheIterator(tree1)); + + assertModes("a", REGULAR_FILE, TREE, tw); + assertTrue(tw.isSubtree()); + tw.enterSubtree(); + assertModes("a/b", MISSING, REGULAR_FILE, tw); + assertModes("a.b", EXECUTABLE_FILE, MISSING, tw); + assertModes("a0b", SYMLINK, MISSING, tw); + } + + public void testDF_GapByOne() throws Exception { + final DirCache tree0 = DirCache.read(db); + final DirCache tree1 = DirCache.read(db); + { + final DirCacheBuilder b0 = tree0.builder(); + final DirCacheBuilder b1 = tree1.builder(); + + b0.add(makeEntry("a", REGULAR_FILE)); + b0.add(makeEntry("a.b", EXECUTABLE_FILE)); + b1.add(makeEntry("a.b", EXECUTABLE_FILE)); + b1.add(makeEntry("a/b", REGULAR_FILE)); + b0.add(makeEntry("a0b", SYMLINK)); + + b0.finish(); + b1.finish(); + assertEquals(3, tree0.getEntryCount()); + assertEquals(2, tree1.getEntryCount()); + } + + final NameConflictTreeWalk tw = new NameConflictTreeWalk(db); + tw.reset(); + tw.addTree(new DirCacheIterator(tree0)); + tw.addTree(new DirCacheIterator(tree1)); + + assertModes("a", REGULAR_FILE, TREE, tw); + assertTrue(tw.isSubtree()); + tw.enterSubtree(); + assertModes("a/b", MISSING, REGULAR_FILE, tw); + assertModes("a.b", EXECUTABLE_FILE, EXECUTABLE_FILE, tw); + assertModes("a0b", SYMLINK, MISSING, tw); + } + + public void testDF_SkipsSeenSubtree() throws Exception { + final DirCache tree0 = DirCache.read(db); + final DirCache tree1 = DirCache.read(db); + { + final DirCacheBuilder b0 = tree0.builder(); + final DirCacheBuilder b1 = tree1.builder(); + + b0.add(makeEntry("a", REGULAR_FILE)); + b1.add(makeEntry("a.b", EXECUTABLE_FILE)); + b1.add(makeEntry("a/b", REGULAR_FILE)); + b0.add(makeEntry("a0b", SYMLINK)); + b1.add(makeEntry("a0b", SYMLINK)); + + b0.finish(); + b1.finish(); + assertEquals(2, tree0.getEntryCount()); + assertEquals(3, tree1.getEntryCount()); + } + + final NameConflictTreeWalk tw = new NameConflictTreeWalk(db); + tw.reset(); + tw.addTree(new DirCacheIterator(tree0)); + tw.addTree(new DirCacheIterator(tree1)); + + assertModes("a", REGULAR_FILE, TREE, tw); + assertTrue(tw.isSubtree()); + tw.enterSubtree(); + assertModes("a/b", MISSING, REGULAR_FILE, tw); + assertModes("a.b", MISSING, EXECUTABLE_FILE, tw); + assertModes("a0b", SYMLINK, SYMLINK, tw); + } + + private DirCacheEntry makeEntry(final String path, final FileMode mode) + throws Exception { + final byte[] pathBytes = Constants.encode(path); + final DirCacheEntry ent = new DirCacheEntry(path); + ent.setFileMode(mode); + ent.setObjectId(new ObjectWriter(db).computeBlobSha1(pathBytes.length, + new ByteArrayInputStream(pathBytes))); + return ent; + } + + private static void assertModes(final String path, final FileMode mode0, + final FileMode mode1, final TreeWalk tw) throws Exception { + assertTrue("has " + path, tw.next()); + assertEquals(path, tw.getPathString()); + assertEquals(mode0, tw.getFileMode(0)); + assertEquals(mode1, tw.getFileMode(1)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/PostOrderTreeWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/PostOrderTreeWalkTest.java new file mode 100644 index 0000000000..d136b8f297 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/PostOrderTreeWalkTest.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2008, 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 java.io.ByteArrayInputStream; + +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheBuilder; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.dircache.DirCacheIterator; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectWriter; +import org.eclipse.jgit.lib.RepositoryTestCase; + +import static org.eclipse.jgit.lib.FileMode.REGULAR_FILE; +import static org.eclipse.jgit.lib.FileMode.TREE; + +public class PostOrderTreeWalkTest extends RepositoryTestCase { + public void testInitialize_NoPostOrder() throws Exception { + final TreeWalk tw = new TreeWalk(db); + assertFalse(tw.isPostOrderTraversal()); + } + + public void testInitialize_TogglePostOrder() throws Exception { + final TreeWalk tw = new TreeWalk(db); + assertFalse(tw.isPostOrderTraversal()); + tw.setPostOrderTraversal(true); + assertTrue(tw.isPostOrderTraversal()); + tw.setPostOrderTraversal(false); + assertFalse(tw.isPostOrderTraversal()); + } + + public void testResetDoesNotAffectPostOrder() throws Exception { + final TreeWalk tw = new TreeWalk(db); + tw.setPostOrderTraversal(true); + assertTrue(tw.isPostOrderTraversal()); + tw.reset(); + assertTrue(tw.isPostOrderTraversal()); + + tw.setPostOrderTraversal(false); + assertFalse(tw.isPostOrderTraversal()); + tw.reset(); + assertFalse(tw.isPostOrderTraversal()); + } + + public void testNoPostOrder() throws Exception { + final DirCache tree = DirCache.read(db); + { + final DirCacheBuilder b = tree.builder(); + + b.add(makeFile("a")); + b.add(makeFile("b/c")); + b.add(makeFile("b/d")); + b.add(makeFile("q")); + + b.finish(); + assertEquals(4, tree.getEntryCount()); + } + + final TreeWalk tw = new TreeWalk(db); + tw.reset(); + tw.setPostOrderTraversal(false); + tw.addTree(new DirCacheIterator(tree)); + + assertModes("a", REGULAR_FILE, tw); + assertModes("b", TREE, tw); + assertTrue(tw.isSubtree()); + assertFalse(tw.isPostChildren()); + tw.enterSubtree(); + assertModes("b/c", REGULAR_FILE, tw); + assertModes("b/d", REGULAR_FILE, tw); + assertModes("q", REGULAR_FILE, tw); + } + + public void testWithPostOrder_EnterSubtree() throws Exception { + final DirCache tree = DirCache.read(db); + { + final DirCacheBuilder b = tree.builder(); + + b.add(makeFile("a")); + b.add(makeFile("b/c")); + b.add(makeFile("b/d")); + b.add(makeFile("q")); + + b.finish(); + assertEquals(4, tree.getEntryCount()); + } + + final TreeWalk tw = new TreeWalk(db); + tw.reset(); + tw.setPostOrderTraversal(true); + tw.addTree(new DirCacheIterator(tree)); + + assertModes("a", REGULAR_FILE, tw); + + assertModes("b", TREE, tw); + assertTrue(tw.isSubtree()); + assertFalse(tw.isPostChildren()); + tw.enterSubtree(); + assertModes("b/c", REGULAR_FILE, tw); + assertModes("b/d", REGULAR_FILE, tw); + + assertModes("b", TREE, tw); + assertTrue(tw.isSubtree()); + assertTrue(tw.isPostChildren()); + + assertModes("q", REGULAR_FILE, tw); + } + + public void testWithPostOrder_NoEnterSubtree() throws Exception { + final DirCache tree = DirCache.read(db); + { + final DirCacheBuilder b = tree.builder(); + + b.add(makeFile("a")); + b.add(makeFile("b/c")); + b.add(makeFile("b/d")); + b.add(makeFile("q")); + + b.finish(); + assertEquals(4, tree.getEntryCount()); + } + + final TreeWalk tw = new TreeWalk(db); + tw.reset(); + tw.setPostOrderTraversal(true); + tw.addTree(new DirCacheIterator(tree)); + + assertModes("a", REGULAR_FILE, tw); + + assertModes("b", TREE, tw); + assertTrue(tw.isSubtree()); + assertFalse(tw.isPostChildren()); + + assertModes("q", REGULAR_FILE, tw); + } + + private DirCacheEntry makeFile(final String path) throws Exception { + final byte[] pathBytes = Constants.encode(path); + final DirCacheEntry ent = new DirCacheEntry(path); + ent.setFileMode(REGULAR_FILE); + ent.setObjectId(new ObjectWriter(db).computeBlobSha1(pathBytes.length, + new ByteArrayInputStream(pathBytes))); + return ent; + } + + private static void assertModes(final String path, final FileMode mode0, + final TreeWalk tw) throws Exception { + assertTrue("has " + path, tw.next()); + assertEquals(path, tw.getPathString()); + assertEquals(mode0, tw.getFileMode(0)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkBasicDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkBasicDiffTest.java new file mode 100644 index 0000000000..581683e34f --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkBasicDiffTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2008, 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 org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectWriter; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.lib.Tree; +import org.eclipse.jgit.treewalk.filter.TreeFilter; + +public class TreeWalkBasicDiffTest extends RepositoryTestCase { + public void testMissingSubtree_DetectFileAdded_FileModified() + throws Exception { + final ObjectWriter ow = new ObjectWriter(db); + final ObjectId aFileId = ow.writeBlob("a".getBytes()); + final ObjectId bFileId = ow.writeBlob("b".getBytes()); + final ObjectId cFileId1 = ow.writeBlob("c-1".getBytes()); + final ObjectId cFileId2 = ow.writeBlob("c-2".getBytes()); + + // Create sub-a/empty, sub-c/empty = hello. + final ObjectId oldTree; + { + final Tree root = new Tree(db); + { + final Tree subA = root.addTree("sub-a"); + subA.addFile("empty").setId(aFileId); + subA.setId(ow.writeTree(subA)); + } + { + final Tree subC = root.addTree("sub-c"); + subC.addFile("empty").setId(cFileId1); + subC.setId(ow.writeTree(subC)); + } + oldTree = ow.writeTree(root); + } + + // Create sub-a/empty, sub-b/empty, sub-c/empty. + final ObjectId newTree; + { + final Tree root = new Tree(db); + { + final Tree subA = root.addTree("sub-a"); + subA.addFile("empty").setId(aFileId); + subA.setId(ow.writeTree(subA)); + } + { + final Tree subB = root.addTree("sub-b"); + subB.addFile("empty").setId(bFileId); + subB.setId(ow.writeTree(subB)); + } + { + final Tree subC = root.addTree("sub-c"); + subC.addFile("empty").setId(cFileId2); + subC.setId(ow.writeTree(subC)); + } + newTree = ow.writeTree(root); + } + + final TreeWalk tw = new TreeWalk(db); + tw.reset(new ObjectId[] { oldTree, newTree }); + tw.setRecursive(true); + tw.setFilter(TreeFilter.ANY_DIFF); + + assertTrue(tw.next()); + assertEquals("sub-b/empty", tw.getPathString()); + assertEquals(FileMode.MISSING, tw.getFileMode(0)); + assertEquals(FileMode.REGULAR_FILE, tw.getFileMode(1)); + assertEquals(ObjectId.zeroId(), tw.getObjectId(0)); + assertEquals(bFileId, tw.getObjectId(1)); + + assertTrue(tw.next()); + assertEquals("sub-c/empty", tw.getPathString()); + assertEquals(FileMode.REGULAR_FILE, tw.getFileMode(0)); + assertEquals(FileMode.REGULAR_FILE, tw.getFileMode(1)); + assertEquals(cFileId1, tw.getObjectId(0)); + assertEquals(cFileId2, tw.getObjectId(1)); + + assertFalse(tw.next()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/AlwaysCloneTreeFilter.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/AlwaysCloneTreeFilter.java new file mode 100644 index 0000000000..8e2cca44ae --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/AlwaysCloneTreeFilter.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008, 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.filter; + +import org.eclipse.jgit.treewalk.TreeWalk; + +class AlwaysCloneTreeFilter extends TreeFilter { + @Override + public TreeFilter clone() { + return new AlwaysCloneTreeFilter(); + } + + @Override + public boolean include(final TreeWalk walker) { + return false; + } + + @Override + public boolean shouldBeRecursive() { + return false; + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/NotTreeFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/NotTreeFilterTest.java new file mode 100644 index 0000000000..7c7ab3e6ae --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/NotTreeFilterTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2008, 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.filter; + +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.treewalk.TreeWalk; + +public class NotTreeFilterTest extends RepositoryTestCase { + public void testWrap() throws Exception { + final TreeWalk tw = new TreeWalk(db); + final TreeFilter a = TreeFilter.ALL; + final TreeFilter n = NotTreeFilter.create(a); + assertNotNull(n); + assertTrue(a.include(tw)); + assertFalse(n.include(tw)); + } + + public void testNegateIsUnwrap() throws Exception { + final TreeFilter a = PathFilter.create("a/b"); + final TreeFilter n = NotTreeFilter.create(a); + assertSame(a, n.negate()); + } + + public void testShouldBeRecursive_ALL() throws Exception { + final TreeFilter a = TreeFilter.ALL; + final TreeFilter n = NotTreeFilter.create(a); + assertEquals(a.shouldBeRecursive(), n.shouldBeRecursive()); + } + + public void testShouldBeRecursive_PathFilter() throws Exception { + final TreeFilter a = PathFilter.create("a/b"); + assertTrue(a.shouldBeRecursive()); + final TreeFilter n = NotTreeFilter.create(a); + assertTrue(n.shouldBeRecursive()); + } + + public void testCloneIsDeepClone() throws Exception { + final TreeFilter a = new AlwaysCloneTreeFilter(); + assertNotSame(a, a.clone()); + final TreeFilter n = NotTreeFilter.create(a); + assertNotSame(n, n.clone()); + } + + public void testCloneIsSparseWhenPossible() throws Exception { + final TreeFilter a = TreeFilter.ALL; + assertSame(a, a.clone()); + final TreeFilter n = NotTreeFilter.create(a); + assertSame(n, n.clone()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTestCase.java new file mode 100644 index 0000000000..1aaefc415f --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTestCase.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 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.filter; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheBuilder; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectWriter; +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.treewalk.TreeWalk; + +public class PathSuffixFilterTestCase extends RepositoryTestCase { + + public void testNonRecursiveFiltering() throws IOException { + final ObjectWriter ow = new ObjectWriter(db); + final ObjectId aSth = ow.writeBlob("a.sth".getBytes()); + final ObjectId aTxt = ow.writeBlob("a.txt".getBytes()); + final DirCache dc = DirCache.read(db); + final DirCacheBuilder builder = dc.builder(); + final DirCacheEntry aSthEntry = new DirCacheEntry("a.sth"); + aSthEntry.setFileMode(FileMode.REGULAR_FILE); + aSthEntry.setObjectId(aSth); + final DirCacheEntry aTxtEntry = new DirCacheEntry("a.txt"); + aTxtEntry.setFileMode(FileMode.REGULAR_FILE); + aTxtEntry.setObjectId(aTxt); + builder.add(aSthEntry); + builder.add(aTxtEntry); + builder.finish(); + final ObjectId treeId = dc.writeTree(ow); + + + final TreeWalk tw = new TreeWalk(db); + tw.setFilter(PathSuffixFilter.create(".txt")); + tw.addTree(treeId); + + List<String> paths = new LinkedList<String>(); + while (tw.next()) { + paths.add(tw.getPathString()); + } + + List<String> expected = new LinkedList<String>(); + expected.add("a.txt"); + + assertEquals(expected, paths); + } + + public void testRecursiveFiltering() throws IOException { + final ObjectWriter ow = new ObjectWriter(db); + final ObjectId aSth = ow.writeBlob("a.sth".getBytes()); + final ObjectId aTxt = ow.writeBlob("a.txt".getBytes()); + final ObjectId bSth = ow.writeBlob("b.sth".getBytes()); + final ObjectId bTxt = ow.writeBlob("b.txt".getBytes()); + final DirCache dc = DirCache.read(db); + final DirCacheBuilder builder = dc.builder(); + final DirCacheEntry aSthEntry = new DirCacheEntry("a.sth"); + aSthEntry.setFileMode(FileMode.REGULAR_FILE); + aSthEntry.setObjectId(aSth); + final DirCacheEntry aTxtEntry = new DirCacheEntry("a.txt"); + aTxtEntry.setFileMode(FileMode.REGULAR_FILE); + aTxtEntry.setObjectId(aTxt); + builder.add(aSthEntry); + builder.add(aTxtEntry); + final DirCacheEntry bSthEntry = new DirCacheEntry("sub/b.sth"); + bSthEntry.setFileMode(FileMode.REGULAR_FILE); + bSthEntry.setObjectId(bSth); + final DirCacheEntry bTxtEntry = new DirCacheEntry("sub/b.txt"); + bTxtEntry.setFileMode(FileMode.REGULAR_FILE); + bTxtEntry.setObjectId(bTxt); + builder.add(bSthEntry); + builder.add(bTxtEntry); + builder.finish(); + final ObjectId treeId = dc.writeTree(ow); + + + final TreeWalk tw = new TreeWalk(db); + tw.setRecursive(true); + tw.setFilter(PathSuffixFilter.create(".txt")); + tw.addTree(treeId); + + List<String> paths = new LinkedList<String>(); + while (tw.next()) { + paths.add(tw.getPathString()); + } + + List<String> expected = new LinkedList<String>(); + expected.add("a.txt"); + expected.add("sub/b.txt"); + + assertEquals(expected, paths); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/TreeFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/TreeFilterTest.java new file mode 100644 index 0000000000..12326eade2 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/TreeFilterTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008, 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.filter; + +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.treewalk.TreeWalk; + +public class TreeFilterTest extends RepositoryTestCase { + public void testALL_IncludesAnything() throws Exception { + final TreeWalk tw = new TreeWalk(db); + assertTrue(TreeFilter.ALL.include(tw)); + } + + public void testALL_ShouldNotBeRecursive() throws Exception { + assertFalse(TreeFilter.ALL.shouldBeRecursive()); + } + + public void testALL_IdentityClone() throws Exception { + assertSame(TreeFilter.ALL, TreeFilter.ALL.clone()); + } + + public void testNotALL_IncludesNothing() throws Exception { + final TreeWalk tw = new TreeWalk(db); + assertFalse(TreeFilter.ALL.negate().include(tw)); + } + + public void testANY_DIFF_IncludesSingleTreeCase() throws Exception { + final TreeWalk tw = new TreeWalk(db); + assertTrue(TreeFilter.ANY_DIFF.include(tw)); + } + + public void testANY_DIFF_ShouldNotBeRecursive() throws Exception { + assertFalse(TreeFilter.ANY_DIFF.shouldBeRecursive()); + } + + public void testANY_DIFF_IdentityClone() throws Exception { + assertSame(TreeFilter.ANY_DIFF, TreeFilter.ANY_DIFF.clone()); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IntListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IntListTest.java new file mode 100644 index 0000000000..ecabeeea5b --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IntListTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2008, 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.util; + +import junit.framework.TestCase; + +public class IntListTest extends TestCase { + public void testEmpty_DefaultCapacity() { + final IntList i = new IntList(); + assertEquals(0, i.size()); + try { + i.get(0); + fail("Accepted 0 index on empty list"); + } catch (ArrayIndexOutOfBoundsException e) { + assertTrue(true); + } + } + + public void testEmpty_SpecificCapacity() { + final IntList i = new IntList(5); + assertEquals(0, i.size()); + try { + i.get(0); + fail("Accepted 0 index on empty list"); + } catch (ArrayIndexOutOfBoundsException e) { + assertTrue(true); + } + } + + public void testAdd_SmallGroup() { + final IntList i = new IntList(); + final int n = 5; + for (int v = 0; v < n; v++) + i.add(10 + v); + assertEquals(n, i.size()); + + for (int v = 0; v < n; v++) + assertEquals(10 + v, i.get(v)); + try { + i.get(n); + fail("Accepted out of bound index on list"); + } catch (ArrayIndexOutOfBoundsException e) { + assertTrue(true); + } + } + + public void testAdd_ZeroCapacity() { + final IntList i = new IntList(0); + assertEquals(0, i.size()); + i.add(1); + assertEquals(1, i.get(0)); + } + + public void testAdd_LargeGroup() { + final IntList i = new IntList(); + final int n = 500; + for (int v = 0; v < n; v++) + i.add(10 + v); + assertEquals(n, i.size()); + + for (int v = 0; v < n; v++) + assertEquals(10 + v, i.get(v)); + try { + i.get(n); + fail("Accepted out of bound index on list"); + } catch (ArrayIndexOutOfBoundsException e) { + assertTrue(true); + } + } + + public void testFillTo0() { + final IntList i = new IntList(); + i.fillTo(0, Integer.MIN_VALUE); + assertEquals(0, i.size()); + } + + public void testFillTo1() { + final IntList i = new IntList(); + i.fillTo(1, Integer.MIN_VALUE); + assertEquals(1, i.size()); + i.add(0); + assertEquals(Integer.MIN_VALUE, i.get(0)); + assertEquals(0, i.get(1)); + } + + public void testFillTo100() { + final IntList i = new IntList(); + i.fillTo(100, Integer.MIN_VALUE); + assertEquals(100, i.size()); + i.add(3); + assertEquals(Integer.MIN_VALUE, i.get(99)); + assertEquals(3, i.get(100)); + } + + public void testClear() { + final IntList i = new IntList(); + final int n = 5; + for (int v = 0; v < n; v++) + i.add(10 + v); + assertEquals(n, i.size()); + + i.clear(); + assertEquals(0, i.size()); + try { + i.get(0); + fail("Accepted 0 index on empty list"); + } catch (ArrayIndexOutOfBoundsException e) { + assertTrue(true); + } + } + + public void testToString() { + final IntList i = new IntList(); + i.add(1); + assertEquals("[1]", i.toString()); + i.add(13); + i.add(5); + assertEquals("[1, 13, 5]", i.toString()); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/JGitTestUtil.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/JGitTestUtil.java new file mode 100644 index 0000000000..37418417d3 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/JGitTestUtil.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008-2009, Google Inc. + * Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com> + * Copyright (C) 2008, Jonas Fonseca <fonseca@diku.dk> + * 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.util; + +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; + +public abstract class JGitTestUtil { + public static final String CLASSPATH_TO_RESOURCES = "org/eclipse/jgit/test/resources/"; + + private JGitTestUtil() { + throw new UnsupportedOperationException(); + } + + public static File getTestResourceFile(final String fileName) { + if (fileName == null || fileName.length() <= 0) { + return null; + } + final URL url = cl().getResource(CLASSPATH_TO_RESOURCES + fileName); + if (url == null) { + // If URL is null then try to load it as it was being + // loaded previously + return new File("tst", fileName); + } + try { + return new File(url.toURI()); + } catch(URISyntaxException e) { + return new File(url.getPath()); + } + } + + private static ClassLoader cl() { + return JGitTestUtil.class.getClassLoader(); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java new file mode 100644 index 0000000000..21fea9e9d5 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/NBTest.java @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2008, 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.util; + +import junit.framework.TestCase; + +public class NBTest extends TestCase { + public void testCompareUInt32() { + assertTrue(NB.compareUInt32(0, 0) == 0); + assertTrue(NB.compareUInt32(1, 0) > 0); + assertTrue(NB.compareUInt32(0, 1) < 0); + assertTrue(NB.compareUInt32(-1, 0) > 0); + assertTrue(NB.compareUInt32(0, -1) < 0); + assertTrue(NB.compareUInt32(-1, 1) > 0); + assertTrue(NB.compareUInt32(1, -1) < 0); + } + + public void testDecodeUInt16() { + assertEquals(0, NB.decodeUInt16(b(0, 0), 0)); + assertEquals(0, NB.decodeUInt16(padb(3, 0, 0), 3)); + + assertEquals(3, NB.decodeUInt16(b(0, 3), 0)); + assertEquals(3, NB.decodeUInt16(padb(3, 0, 3), 3)); + + assertEquals(0xde03, NB.decodeUInt16(b(0xde, 3), 0)); + assertEquals(0xde03, NB.decodeUInt16(padb(3, 0xde, 3), 3)); + + assertEquals(0x03de, NB.decodeUInt16(b(3, 0xde), 0)); + assertEquals(0x03de, NB.decodeUInt16(padb(3, 3, 0xde), 3)); + + assertEquals(0xffff, NB.decodeUInt16(b(0xff, 0xff), 0)); + assertEquals(0xffff, NB.decodeUInt16(padb(3, 0xff, 0xff), 3)); + } + + public void testDecodeInt32() { + assertEquals(0, NB.decodeInt32(b(0, 0, 0, 0), 0)); + assertEquals(0, NB.decodeInt32(padb(3, 0, 0, 0, 0), 3)); + + assertEquals(3, NB.decodeInt32(b(0, 0, 0, 3), 0)); + assertEquals(3, NB.decodeInt32(padb(3, 0, 0, 0, 3), 3)); + + assertEquals(0xdeadbeef, NB.decodeInt32(b(0xde, 0xad, 0xbe, 0xef), 0)); + assertEquals(0xdeadbeef, NB.decodeInt32( + padb(3, 0xde, 0xad, 0xbe, 0xef), 3)); + + assertEquals(0x0310adef, NB.decodeInt32(b(0x03, 0x10, 0xad, 0xef), 0)); + assertEquals(0x0310adef, NB.decodeInt32( + padb(3, 0x03, 0x10, 0xad, 0xef), 3)); + + assertEquals(0xffffffff, NB.decodeInt32(b(0xff, 0xff, 0xff, 0xff), 0)); + assertEquals(0xffffffff, NB.decodeInt32( + padb(3, 0xff, 0xff, 0xff, 0xff), 3)); + } + + public void testDecodeUInt32() { + assertEquals(0L, NB.decodeUInt32(b(0, 0, 0, 0), 0)); + assertEquals(0L, NB.decodeUInt32(padb(3, 0, 0, 0, 0), 3)); + + assertEquals(3L, NB.decodeUInt32(b(0, 0, 0, 3), 0)); + assertEquals(3L, NB.decodeUInt32(padb(3, 0, 0, 0, 3), 3)); + + assertEquals(0xdeadbeefL, NB.decodeUInt32(b(0xde, 0xad, 0xbe, 0xef), 0)); + assertEquals(0xdeadbeefL, NB.decodeUInt32(padb(3, 0xde, 0xad, 0xbe, + 0xef), 3)); + + assertEquals(0x0310adefL, NB.decodeUInt32(b(0x03, 0x10, 0xad, 0xef), 0)); + assertEquals(0x0310adefL, NB.decodeUInt32(padb(3, 0x03, 0x10, 0xad, + 0xef), 3)); + + assertEquals(0xffffffffL, NB.decodeUInt32(b(0xff, 0xff, 0xff, 0xff), 0)); + assertEquals(0xffffffffL, NB.decodeUInt32(padb(3, 0xff, 0xff, 0xff, + 0xff), 3)); + } + + public void testDecodeUInt64() { + assertEquals(0L, NB.decodeUInt64(b(0, 0, 0, 0, 0, 0, 0, 0), 0)); + assertEquals(0L, NB.decodeUInt64(padb(3, 0, 0, 0, 0, 0, 0, 0, 0), 3)); + + assertEquals(3L, NB.decodeUInt64(b(0, 0, 0, 0, 0, 0, 0, 3), 0)); + assertEquals(3L, NB.decodeUInt64(padb(3, 0, 0, 0, 0, 0, 0, 0, 3), 3)); + + assertEquals(0xdeadbeefL, NB.decodeUInt64(b(0, 0, 0, 0, 0xde, 0xad, + 0xbe, 0xef), 0)); + assertEquals(0xdeadbeefL, NB.decodeUInt64(padb(3, 0, 0, 0, 0, 0xde, + 0xad, 0xbe, 0xef), 3)); + + assertEquals(0x0310adefL, NB.decodeUInt64(b(0, 0, 0, 0, 0x03, 0x10, + 0xad, 0xef), 0)); + assertEquals(0x0310adefL, NB.decodeUInt64(padb(3, 0, 0, 0, 0, 0x03, + 0x10, 0xad, 0xef), 3)); + + assertEquals(0xc0ffee78deadbeefL, NB.decodeUInt64(b(0xc0, 0xff, 0xee, + 0x78, 0xde, 0xad, 0xbe, 0xef), 0)); + assertEquals(0xc0ffee78deadbeefL, NB.decodeUInt64(padb(3, 0xc0, 0xff, + 0xee, 0x78, 0xde, 0xad, 0xbe, 0xef), 3)); + + assertEquals(0x00000000ffffffffL, NB.decodeUInt64(b(0, 0, 0, 0, 0xff, + 0xff, 0xff, 0xff), 0)); + assertEquals(0x00000000ffffffffL, NB.decodeUInt64(padb(3, 0, 0, 0, 0, + 0xff, 0xff, 0xff, 0xff), 3)); + assertEquals(0xffffffffffffffffL, NB.decodeUInt64(b(0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff), 0)); + assertEquals(0xffffffffffffffffL, NB.decodeUInt64(padb(3, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff), 3)); + } + + public void testEncodeInt16() { + final byte[] out = new byte[16]; + + prepareOutput(out); + NB.encodeInt16(out, 0, 0); + assertOutput(b(0, 0), out, 0); + + prepareOutput(out); + NB.encodeInt16(out, 3, 0); + assertOutput(b(0, 0), out, 3); + + prepareOutput(out); + NB.encodeInt16(out, 0, 3); + assertOutput(b(0, 3), out, 0); + + prepareOutput(out); + NB.encodeInt16(out, 3, 3); + assertOutput(b(0, 3), out, 3); + + prepareOutput(out); + NB.encodeInt16(out, 0, 0xdeac); + assertOutput(b(0xde, 0xac), out, 0); + + prepareOutput(out); + NB.encodeInt16(out, 3, 0xdeac); + assertOutput(b(0xde, 0xac), out, 3); + + prepareOutput(out); + NB.encodeInt16(out, 3, -1); + assertOutput(b(0xff, 0xff), out, 3); + } + + public void testEncodeInt32() { + final byte[] out = new byte[16]; + + prepareOutput(out); + NB.encodeInt32(out, 0, 0); + assertOutput(b(0, 0, 0, 0), out, 0); + + prepareOutput(out); + NB.encodeInt32(out, 3, 0); + assertOutput(b(0, 0, 0, 0), out, 3); + + prepareOutput(out); + NB.encodeInt32(out, 0, 3); + assertOutput(b(0, 0, 0, 3), out, 0); + + prepareOutput(out); + NB.encodeInt32(out, 3, 3); + assertOutput(b(0, 0, 0, 3), out, 3); + + prepareOutput(out); + NB.encodeInt32(out, 0, 0xdeac); + assertOutput(b(0, 0, 0xde, 0xac), out, 0); + + prepareOutput(out); + NB.encodeInt32(out, 3, 0xdeac); + assertOutput(b(0, 0, 0xde, 0xac), out, 3); + + prepareOutput(out); + NB.encodeInt32(out, 0, 0xdeac9853); + assertOutput(b(0xde, 0xac, 0x98, 0x53), out, 0); + + prepareOutput(out); + NB.encodeInt32(out, 3, 0xdeac9853); + assertOutput(b(0xde, 0xac, 0x98, 0x53), out, 3); + + prepareOutput(out); + NB.encodeInt32(out, 3, -1); + assertOutput(b(0xff, 0xff, 0xff, 0xff), out, 3); + } + + public void testEncodeInt64() { + final byte[] out = new byte[16]; + + prepareOutput(out); + NB.encodeInt64(out, 0, 0L); + assertOutput(b(0, 0, 0, 0, 0, 0, 0, 0), out, 0); + + prepareOutput(out); + NB.encodeInt64(out, 3, 0L); + assertOutput(b(0, 0, 0, 0, 0, 0, 0, 0), out, 3); + + prepareOutput(out); + NB.encodeInt64(out, 0, 3L); + assertOutput(b(0, 0, 0, 0, 0, 0, 0, 3), out, 0); + + prepareOutput(out); + NB.encodeInt64(out, 3, 3L); + assertOutput(b(0, 0, 0, 0, 0, 0, 0, 3), out, 3); + + prepareOutput(out); + NB.encodeInt64(out, 0, 0xdeacL); + assertOutput(b(0, 0, 0, 0, 0, 0, 0xde, 0xac), out, 0); + + prepareOutput(out); + NB.encodeInt64(out, 3, 0xdeacL); + assertOutput(b(0, 0, 0, 0, 0, 0, 0xde, 0xac), out, 3); + + prepareOutput(out); + NB.encodeInt64(out, 0, 0xdeac9853L); + assertOutput(b(0, 0, 0, 0, 0xde, 0xac, 0x98, 0x53), out, 0); + + prepareOutput(out); + NB.encodeInt64(out, 3, 0xdeac9853L); + assertOutput(b(0, 0, 0, 0, 0xde, 0xac, 0x98, 0x53), out, 3); + + prepareOutput(out); + NB.encodeInt64(out, 0, 0xac431242deac9853L); + assertOutput(b(0xac, 0x43, 0x12, 0x42, 0xde, 0xac, 0x98, 0x53), out, 0); + + prepareOutput(out); + NB.encodeInt64(out, 3, 0xac431242deac9853L); + assertOutput(b(0xac, 0x43, 0x12, 0x42, 0xde, 0xac, 0x98, 0x53), out, 3); + + prepareOutput(out); + NB.encodeInt64(out, 3, -1L); + assertOutput(b(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff), out, 3); + } + + private static void prepareOutput(final byte[] buf) { + for (int i = 0; i < buf.length; i++) + buf[i] = (byte) (0x77 + i); + } + + private static void assertOutput(final byte[] expect, final byte[] buf, + final int offset) { + for (int i = 0; i < offset; i++) + assertEquals((byte) (0x77 + i), buf[i]); + for (int i = 0; i < expect.length; i++) + assertEquals(expect[i], buf[offset + i]); + for (int i = offset + expect.length; i < buf.length; i++) + assertEquals((byte) (0x77 + i), buf[i]); + } + + private static byte[] b(final int a, final int b) { + return new byte[] { (byte) a, (byte) b }; + } + + private static byte[] padb(final int len, final int a, final int b) { + final byte[] r = new byte[len + 2]; + for (int i = 0; i < len; i++) + r[i] = (byte) 0xaf; + r[len] = (byte) a; + r[len + 1] = (byte) b; + return r; + } + + private static byte[] b(final int a, final int b, final int c, final int d) { + return new byte[] { (byte) a, (byte) b, (byte) c, (byte) d }; + } + + private static byte[] padb(final int len, final int a, final int b, + final int c, final int d) { + final byte[] r = new byte[len + 4]; + for (int i = 0; i < len; i++) + r[i] = (byte) 0xaf; + r[len] = (byte) a; + r[len + 1] = (byte) b; + r[len + 2] = (byte) c; + r[len + 3] = (byte) d; + return r; + } + + private static byte[] b(final int a, final int b, final int c, final int d, + final int e, final int f, final int g, final int h) { + return new byte[] { (byte) a, (byte) b, (byte) c, (byte) d, (byte) e, + (byte) f, (byte) g, (byte) h }; + } + + private static byte[] padb(final int len, final int a, final int b, + final int c, final int d, final int e, final int f, final int g, + final int h) { + final byte[] r = new byte[len + 8]; + for (int i = 0; i < len; i++) + r[i] = (byte) 0xaf; + r[len] = (byte) a; + r[len + 1] = (byte) b; + r[len + 2] = (byte) c; + r[len + 3] = (byte) d; + r[len + 4] = (byte) e; + r[len + 5] = (byte) f; + r[len + 6] = (byte) g; + r[len + 7] = (byte) h; + return r; + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringBourneStyleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringBourneStyleTest.java new file mode 100644 index 0000000000..b9a721a464 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringBourneStyleTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2008, 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.util; + +import static org.eclipse.jgit.util.QuotedString.BOURNE; +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Constants; + +public class QuotedStringBourneStyleTest extends TestCase { + private static void assertQuote(final String in, final String exp) { + final String r = BOURNE.quote(in); + assertNotSame(in, r); + assertFalse(in.equals(r)); + assertEquals('\'' + exp + '\'', r); + } + + private static void assertDequote(final String exp, final String in) { + final byte[] b = Constants.encode('\'' + in + '\''); + final String r = BOURNE.dequote(b, 0, b.length); + assertEquals(exp, r); + } + + public void testQuote_Empty() { + assertEquals("''", BOURNE.quote("")); + } + + public void testDequote_Empty1() { + assertEquals("", BOURNE.dequote(new byte[0], 0, 0)); + } + + public void testDequote_Empty2() { + assertEquals("", BOURNE.dequote(new byte[] { '\'', '\'' }, 0, 2)); + } + + public void testDequote_SoleSq() { + assertEquals("", BOURNE.dequote(new byte[] { '\'' }, 0, 1)); + } + + public void testQuote_BareA() { + assertQuote("a", "a"); + } + + public void testDequote_BareA() { + final String in = "a"; + final byte[] b = Constants.encode(in); + assertEquals(in, BOURNE.dequote(b, 0, b.length)); + } + + public void testDequote_BareABCZ_OnlyBC() { + final String in = "abcz"; + final byte[] b = Constants.encode(in); + final int p = in.indexOf('b'); + assertEquals("bc", BOURNE.dequote(b, p, p + 2)); + } + + public void testDequote_LoneBackslash() { + assertDequote("\\", "\\"); + } + + public void testQuote_NamedEscapes() { + assertQuote("'", "'\\''"); + assertQuote("!", "'\\!'"); + + assertQuote("a'b", "a'\\''b"); + assertQuote("a!b", "a'\\!'b"); + } + + public void testDequote_NamedEscapes() { + assertDequote("'", "'\\''"); + assertDequote("!", "'\\!'"); + + assertDequote("a'b", "a'\\''b"); + assertDequote("a!b", "a'\\!'b"); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringBourneUserPathStyleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringBourneUserPathStyleTest.java new file mode 100644 index 0000000000..69201249c0 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringBourneUserPathStyleTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2008, 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.util; + +import static org.eclipse.jgit.util.QuotedString.BOURNE_USER_PATH; +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Constants; + +public class QuotedStringBourneUserPathStyleTest extends TestCase { + private static void assertQuote(final String in, final String exp) { + final String r = BOURNE_USER_PATH.quote(in); + assertNotSame(in, r); + assertFalse(in.equals(r)); + assertEquals('\'' + exp + '\'', r); + } + + private static void assertDequote(final String exp, final String in) { + final byte[] b = Constants.encode('\'' + in + '\''); + final String r = BOURNE_USER_PATH.dequote(b, 0, b.length); + assertEquals(exp, r); + } + + public void testQuote_Empty() { + assertEquals("''", BOURNE_USER_PATH.quote("")); + } + + public void testDequote_Empty1() { + assertEquals("", BOURNE_USER_PATH.dequote(new byte[0], 0, 0)); + } + + public void testDequote_Empty2() { + assertEquals("", BOURNE_USER_PATH.dequote(new byte[] { '\'', '\'' }, 0, + 2)); + } + + public void testDequote_SoleSq() { + assertEquals("", BOURNE_USER_PATH.dequote(new byte[] { '\'' }, 0, 1)); + } + + public void testQuote_BareA() { + assertQuote("a", "a"); + } + + public void testDequote_BareA() { + final String in = "a"; + final byte[] b = Constants.encode(in); + assertEquals(in, BOURNE_USER_PATH.dequote(b, 0, b.length)); + } + + public void testDequote_BareABCZ_OnlyBC() { + final String in = "abcz"; + final byte[] b = Constants.encode(in); + final int p = in.indexOf('b'); + assertEquals("bc", BOURNE_USER_PATH.dequote(b, p, p + 2)); + } + + public void testDequote_LoneBackslash() { + assertDequote("\\", "\\"); + } + + public void testQuote_NamedEscapes() { + assertQuote("'", "'\\''"); + assertQuote("!", "'\\!'"); + + assertQuote("a'b", "a'\\''b"); + assertQuote("a!b", "a'\\!'b"); + } + + public void testDequote_NamedEscapes() { + assertDequote("'", "'\\''"); + assertDequote("!", "'\\!'"); + + assertDequote("a'b", "a'\\''b"); + assertDequote("a!b", "a'\\!'b"); + } + + public void testQuote_User() { + assertEquals("~foo/", BOURNE_USER_PATH.quote("~foo")); + assertEquals("~foo/", BOURNE_USER_PATH.quote("~foo/")); + assertEquals("~/", BOURNE_USER_PATH.quote("~/")); + + assertEquals("~foo/'a'", BOURNE_USER_PATH.quote("~foo/a")); + assertEquals("~/'a'", BOURNE_USER_PATH.quote("~/a")); + } + + public void testDequote_User() { + assertEquals("~foo", BOURNE_USER_PATH.dequote("~foo")); + assertEquals("~foo/", BOURNE_USER_PATH.dequote("~foo/")); + assertEquals("~/", BOURNE_USER_PATH.dequote("~/")); + + assertEquals("~foo/a", BOURNE_USER_PATH.dequote("~foo/'a'")); + assertEquals("~/a", BOURNE_USER_PATH.dequote("~/'a'")); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringGitPathStyleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringGitPathStyleTest.java new file mode 100644 index 0000000000..4a161fa01c --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/QuotedStringGitPathStyleTest.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2008, 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.util; + +import static org.eclipse.jgit.util.QuotedString.GIT_PATH; + +import java.io.UnsupportedEncodingException; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Constants; + +public class QuotedStringGitPathStyleTest extends TestCase { + private static void assertQuote(final String exp, final String in) { + final String r = GIT_PATH.quote(in); + assertNotSame(in, r); + assertFalse(in.equals(r)); + assertEquals('"' + exp + '"', r); + } + + private static void assertDequote(final String exp, final String in) { + final byte[] b; + try { + b = ('"' + in + '"').getBytes("ISO-8859-1"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + final String r = GIT_PATH.dequote(b, 0, b.length); + assertEquals(exp, r); + } + + public void testQuote_Empty() { + assertEquals("\"\"", GIT_PATH.quote("")); + } + + public void testDequote_Empty1() { + assertEquals("", GIT_PATH.dequote(new byte[0], 0, 0)); + } + + public void testDequote_Empty2() { + assertEquals("", GIT_PATH.dequote(new byte[] { '"', '"' }, 0, 2)); + } + + public void testDequote_SoleDq() { + assertEquals("\"", GIT_PATH.dequote(new byte[] { '"' }, 0, 1)); + } + + public void testQuote_BareA() { + final String in = "a"; + assertSame(in, GIT_PATH.quote(in)); + } + + public void testDequote_BareA() { + final String in = "a"; + final byte[] b = Constants.encode(in); + assertEquals(in, GIT_PATH.dequote(b, 0, b.length)); + } + + public void testDequote_BareABCZ_OnlyBC() { + final String in = "abcz"; + final byte[] b = Constants.encode(in); + final int p = in.indexOf('b'); + assertEquals("bc", GIT_PATH.dequote(b, p, p + 2)); + } + + public void testDequote_LoneBackslash() { + assertDequote("\\", "\\"); + } + + public void testQuote_NamedEscapes() { + assertQuote("\\a", "\u0007"); + assertQuote("\\b", "\b"); + assertQuote("\\f", "\f"); + assertQuote("\\n", "\n"); + assertQuote("\\r", "\r"); + assertQuote("\\t", "\t"); + assertQuote("\\v", "\u000B"); + assertQuote("\\\\", "\\"); + assertQuote("\\\"", "\""); + } + + public void testDequote_NamedEscapes() { + assertDequote("\u0007", "\\a"); + assertDequote("\b", "\\b"); + assertDequote("\f", "\\f"); + assertDequote("\n", "\\n"); + assertDequote("\r", "\\r"); + assertDequote("\t", "\\t"); + assertDequote("\u000B", "\\v"); + assertDequote("\\", "\\\\"); + assertDequote("\"", "\\\""); + } + + public void testDequote_OctalAll() { + for (int i = 0; i < 127; i++) { + assertDequote("" + (char) i, octalEscape(i)); + } + for (int i = 128; i < 256; i++) { + int f = 0xC0 | (i >> 6); + int s = 0x80 | (i & 0x3f); + assertDequote("" + (char) i, octalEscape(f)+octalEscape(s)); + } + } + + private String octalEscape(int i) { + String s = Integer.toOctalString(i); + while (s.length() < 3) { + s = "0" + s; + } + return "\\"+s; + } + + public void testQuote_OctalAll() { + assertQuote("\\001", "\1"); + assertQuote("\\176", "~"); + assertQuote("\\303\\277", "\u00ff"); // \u00ff in UTF-8 + } + + public void testDequote_UnknownEscapeQ() { + assertDequote("\\q", "\\q"); + } + + public void testDequote_FooTabBar() { + assertDequote("foo\tbar", "foo\\tbar"); + } + + public void testDequote_Latin1() { + assertDequote("\u00c5ngstr\u00f6m", "\\305ngstr\\366m"); // Latin1 + } + + public void testDequote_UTF8() { + assertDequote("\u00c5ngstr\u00f6m", "\\303\\205ngstr\\303\\266m"); + } + + public void testDequote_RawUTF8() { + assertDequote("\u00c5ngstr\u00f6m", "\303\205ngstr\303\266m"); + } + + public void testDequote_RawLatin1() { + assertDequote("\u00c5ngstr\u00f6m", "\305ngstr\366m"); + } + + public void testQuote_Ang() { + assertQuote("\\303\\205ngstr\\303\\266m", "\u00c5ngstr\u00f6m"); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_HexParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_HexParseTest.java new file mode 100644 index 0000000000..a2c9e9dbd1 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_HexParseTest.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 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.util; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Constants; + +public class RawParseUtils_HexParseTest extends TestCase { + public void testInt4_1() { + assertEquals(0, RawParseUtils.parseHexInt4((byte) '0')); + assertEquals(1, RawParseUtils.parseHexInt4((byte) '1')); + assertEquals(2, RawParseUtils.parseHexInt4((byte) '2')); + assertEquals(3, RawParseUtils.parseHexInt4((byte) '3')); + assertEquals(4, RawParseUtils.parseHexInt4((byte) '4')); + assertEquals(5, RawParseUtils.parseHexInt4((byte) '5')); + assertEquals(6, RawParseUtils.parseHexInt4((byte) '6')); + assertEquals(7, RawParseUtils.parseHexInt4((byte) '7')); + assertEquals(8, RawParseUtils.parseHexInt4((byte) '8')); + assertEquals(9, RawParseUtils.parseHexInt4((byte) '9')); + assertEquals(10, RawParseUtils.parseHexInt4((byte) 'a')); + assertEquals(11, RawParseUtils.parseHexInt4((byte) 'b')); + assertEquals(12, RawParseUtils.parseHexInt4((byte) 'c')); + assertEquals(13, RawParseUtils.parseHexInt4((byte) 'd')); + assertEquals(14, RawParseUtils.parseHexInt4((byte) 'e')); + assertEquals(15, RawParseUtils.parseHexInt4((byte) 'f')); + + assertEquals(10, RawParseUtils.parseHexInt4((byte) 'A')); + assertEquals(11, RawParseUtils.parseHexInt4((byte) 'B')); + assertEquals(12, RawParseUtils.parseHexInt4((byte) 'C')); + assertEquals(13, RawParseUtils.parseHexInt4((byte) 'D')); + assertEquals(14, RawParseUtils.parseHexInt4((byte) 'E')); + assertEquals(15, RawParseUtils.parseHexInt4((byte) 'F')); + + assertNotHex('q'); + assertNotHex(' '); + assertNotHex('.'); + } + + private static void assertNotHex(final char c) { + try { + RawParseUtils.parseHexInt4((byte) c); + fail("Incorrectly acccepted " + c); + } catch (ArrayIndexOutOfBoundsException e) { + // pass + } + } + + public void testInt16() { + assertEquals(0x0000, parse16("0000")); + assertEquals(0x0001, parse16("0001")); + assertEquals(0x1234, parse16("1234")); + assertEquals(0xdead, parse16("dead")); + assertEquals(0xBEEF, parse16("BEEF")); + assertEquals(0x4321, parse16("4321")); + assertEquals(0xffff, parse16("ffff")); + + try { + parse16("noth"); + fail("Incorrectly acccepted \"noth\""); + } catch (ArrayIndexOutOfBoundsException e) { + // pass + } + + try { + parse16("01"); + fail("Incorrectly acccepted \"01\""); + } catch (ArrayIndexOutOfBoundsException e) { + // pass + } + + try { + parse16("000."); + fail("Incorrectly acccepted \"000.\""); + } catch (ArrayIndexOutOfBoundsException e) { + // pass + } + } + + private static int parse16(final String str) { + return RawParseUtils.parseHexInt16(Constants.encodeASCII(str), 0); + } + + public void testInt32() { + assertEquals(0x00000000, parse32("00000000")); + assertEquals(0x00000001, parse32("00000001")); + assertEquals(0xc0ffEE42, parse32("c0ffEE42")); + assertEquals(0xffffffff, parse32("ffffffff")); + assertEquals(-1, parse32("ffffffff")); + + try { + parse32("noth"); + fail("Incorrectly acccepted \"noth\""); + } catch (ArrayIndexOutOfBoundsException e) { + // pass + } + + try { + parse32("notahexs"); + fail("Incorrectly acccepted \"notahexs\""); + } catch (ArrayIndexOutOfBoundsException e) { + // pass + } + + try { + parse32("01"); + fail("Incorrectly acccepted \"01\""); + } catch (ArrayIndexOutOfBoundsException e) { + // pass + } + + try { + parse32("0000000."); + fail("Incorrectly acccepted \"0000000.\""); + } catch (ArrayIndexOutOfBoundsException e) { + // pass + } + } + + private static int parse32(final String str) { + return RawParseUtils.parseHexInt32(Constants.encodeASCII(str), 0); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_LineMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_LineMapTest.java new file mode 100644 index 0000000000..6dcd56ee0d --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_LineMapTest.java @@ -0,0 +1,98 @@ +/* + * 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.util; + +import java.io.UnsupportedEncodingException; + +import junit.framework.TestCase; + +public class RawParseUtils_LineMapTest extends TestCase { + public void testEmpty() { + final IntList map = RawParseUtils.lineMap(new byte[] {}, 0, 0); + assertNotNull(map); + assertEquals(2, map.size()); + assertEquals(Integer.MIN_VALUE, map.get(0)); + assertEquals(0, map.get(1)); + } + + public void testOneBlankLine() { + final IntList map = RawParseUtils.lineMap(new byte[] { '\n' }, 0, 1); + assertEquals(3, map.size()); + assertEquals(Integer.MIN_VALUE, map.get(0)); + assertEquals(0, map.get(1)); + assertEquals(1, map.get(2)); + } + + public void testTwoLineFooBar() throws UnsupportedEncodingException { + final byte[] buf = "foo\nbar\n".getBytes("ISO-8859-1"); + final IntList map = RawParseUtils.lineMap(buf, 0, buf.length); + assertEquals(4, map.size()); + assertEquals(Integer.MIN_VALUE, map.get(0)); + assertEquals(0, map.get(1)); + assertEquals(4, map.get(2)); + assertEquals(buf.length, map.get(3)); + } + + public void testTwoLineNoLF() throws UnsupportedEncodingException { + final byte[] buf = "foo\nbar".getBytes("ISO-8859-1"); + final IntList map = RawParseUtils.lineMap(buf, 0, buf.length); + assertEquals(4, map.size()); + assertEquals(Integer.MIN_VALUE, map.get(0)); + assertEquals(0, map.get(1)); + assertEquals(4, map.get(2)); + assertEquals(buf.length, map.get(3)); + } + + public void testFourLineBlanks() throws UnsupportedEncodingException { + final byte[] buf = "foo\n\n\nbar\n".getBytes("ISO-8859-1"); + final IntList map = RawParseUtils.lineMap(buf, 0, buf.length); + assertEquals(6, map.size()); + assertEquals(Integer.MIN_VALUE, map.get(0)); + assertEquals(0, map.get(1)); + assertEquals(4, map.get(2)); + assertEquals(5, map.get(3)); + assertEquals(6, map.get(4)); + assertEquals(buf.length, map.get(5)); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_MatchTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_MatchTest.java new file mode 100644 index 0000000000..29459326da --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_MatchTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 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.util; + +import junit.framework.TestCase; + +import org.eclipse.jgit.lib.Constants; + +public class RawParseUtils_MatchTest extends TestCase { + public void testMatch_Equal() { + final byte[] src = Constants.encodeASCII(" differ\n"); + final byte[] dst = Constants.encodeASCII("foo differ\n"); + assertTrue(RawParseUtils.match(dst, 3, src) == 3 + src.length); + } + + public void testMatch_NotEqual() { + final byte[] src = Constants.encodeASCII(" differ\n"); + final byte[] dst = Constants.encodeASCII("a differ\n"); + assertTrue(RawParseUtils.match(dst, 2, src) < 0); + } + + public void testMatch_Prefix() { + final byte[] src = Constants.encodeASCII("author "); + final byte[] dst = Constants.encodeASCII("author A. U. Thor"); + assertTrue(RawParseUtils.match(dst, 0, src) == src.length); + assertTrue(RawParseUtils.match(dst, 1, src) < 0); + } + + public void testMatch_TooSmall() { + final byte[] src = Constants.encodeASCII("author "); + final byte[] dst = Constants.encodeASCII("author autho"); + assertTrue(RawParseUtils.match(dst, src.length + 1, src) < 0); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java new file mode 100644 index 0000000000..912380dcd1 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 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.util; + +import junit.framework.TestCase; + +public class StringUtilsTest extends TestCase { + public void testToLowerCaseChar() { + assertEquals('a', StringUtils.toLowerCase('A')); + assertEquals('z', StringUtils.toLowerCase('Z')); + + assertEquals('a', StringUtils.toLowerCase('a')); + assertEquals('z', StringUtils.toLowerCase('z')); + + assertEquals((char) 0, StringUtils.toLowerCase((char) 0)); + assertEquals((char) 0xffff, StringUtils.toLowerCase((char) 0xffff)); + } + + public void testToLowerCaseString() { + assertEquals("\n abcdefghijklmnopqrstuvwxyz\n", StringUtils + .toLowerCase("\n ABCDEFGHIJKLMNOPQRSTUVWXYZ\n")); + } + + public void testEqualsIgnoreCase1() { + final String a = "FOO"; + assertTrue(StringUtils.equalsIgnoreCase(a, a)); + } + + public void testEqualsIgnoreCase2() { + assertFalse(StringUtils.equalsIgnoreCase("a", "")); + } + + public void testEqualsIgnoreCase3() { + assertFalse(StringUtils.equalsIgnoreCase("a", "b")); + assertFalse(StringUtils.equalsIgnoreCase("ac", "ab")); + } + + public void testEqualsIgnoreCase4() { + assertTrue(StringUtils.equalsIgnoreCase("a", "a")); + assertTrue(StringUtils.equalsIgnoreCase("A", "a")); + assertTrue(StringUtils.equalsIgnoreCase("a", "A")); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java new file mode 100644 index 0000000000..eb24172249 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2008, 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.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; + +import junit.framework.TestCase; + +public class TemporaryBufferTest extends TestCase { + public void testEmpty() throws IOException { + final TemporaryBuffer b = new TemporaryBuffer(); + try { + b.close(); + assertEquals(0, b.length()); + final byte[] r = b.toByteArray(); + assertNotNull(r); + assertEquals(0, r.length); + } finally { + b.destroy(); + } + } + + public void testOneByte() throws IOException { + final TemporaryBuffer b = new TemporaryBuffer(); + final byte test = (byte) new TestRng(getName()).nextInt(); + try { + b.write(test); + b.close(); + assertEquals(1, b.length()); + { + final byte[] r = b.toByteArray(); + assertNotNull(r); + assertEquals(1, r.length); + assertEquals(test, r[0]); + } + { + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + b.writeTo(o, null); + o.close(); + final byte[] r = o.toByteArray(); + assertEquals(1, r.length); + assertEquals(test, r[0]); + } + } finally { + b.destroy(); + } + } + + public void testOneBlock_BulkWrite() throws IOException { + final TemporaryBuffer b = new TemporaryBuffer(); + final byte[] test = new TestRng(getName()) + .nextBytes(TemporaryBuffer.Block.SZ); + try { + b.write(test, 0, 2); + b.write(test, 2, 4); + b.write(test, 6, test.length - 6 - 2); + b.write(test, test.length - 2, 2); + b.close(); + assertEquals(test.length, b.length()); + { + final byte[] r = b.toByteArray(); + assertNotNull(r); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + { + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + b.writeTo(o, null); + o.close(); + final byte[] r = o.toByteArray(); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + } finally { + b.destroy(); + } + } + + public void testOneBlockAndHalf_BulkWrite() throws IOException { + final TemporaryBuffer b = new TemporaryBuffer(); + final byte[] test = new TestRng(getName()) + .nextBytes(TemporaryBuffer.Block.SZ * 3 / 2); + try { + b.write(test, 0, 2); + b.write(test, 2, 4); + b.write(test, 6, test.length - 6 - 2); + b.write(test, test.length - 2, 2); + b.close(); + assertEquals(test.length, b.length()); + { + final byte[] r = b.toByteArray(); + assertNotNull(r); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + { + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + b.writeTo(o, null); + o.close(); + final byte[] r = o.toByteArray(); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + } finally { + b.destroy(); + } + } + + public void testOneBlockAndHalf_SingleWrite() throws IOException { + final TemporaryBuffer b = new TemporaryBuffer(); + final byte[] test = new TestRng(getName()) + .nextBytes(TemporaryBuffer.Block.SZ * 3 / 2); + try { + for (int i = 0; i < test.length; i++) + b.write(test[i]); + b.close(); + assertEquals(test.length, b.length()); + { + final byte[] r = b.toByteArray(); + assertNotNull(r); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + { + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + b.writeTo(o, null); + o.close(); + final byte[] r = o.toByteArray(); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + } finally { + b.destroy(); + } + } + + public void testOneBlockAndHalf_Copy() throws IOException { + final TemporaryBuffer b = new TemporaryBuffer(); + final byte[] test = new TestRng(getName()) + .nextBytes(TemporaryBuffer.Block.SZ * 3 / 2); + try { + final ByteArrayInputStream in = new ByteArrayInputStream(test); + b.write(in.read()); + b.copy(in); + b.close(); + assertEquals(test.length, b.length()); + { + final byte[] r = b.toByteArray(); + assertNotNull(r); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + { + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + b.writeTo(o, null); + o.close(); + final byte[] r = o.toByteArray(); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + } finally { + b.destroy(); + } + } + + public void testLarge_SingleWrite() throws IOException { + final TemporaryBuffer b = new TemporaryBuffer(); + final byte[] test = new TestRng(getName()) + .nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 3); + try { + b.write(test); + b.close(); + assertEquals(test.length, b.length()); + { + final byte[] r = b.toByteArray(); + assertNotNull(r); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + { + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + b.writeTo(o, null); + o.close(); + final byte[] r = o.toByteArray(); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + } finally { + b.destroy(); + } + } + + public void testInCoreLimit_SwitchOnAppendByte() throws IOException { + final TemporaryBuffer b = new TemporaryBuffer(); + final byte[] test = new TestRng(getName()) + .nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT + 1); + try { + b.write(test, 0, test.length - 1); + b.write(test[test.length - 1]); + b.close(); + assertEquals(test.length, b.length()); + { + final byte[] r = b.toByteArray(); + assertNotNull(r); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + { + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + b.writeTo(o, null); + o.close(); + final byte[] r = o.toByteArray(); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + } finally { + b.destroy(); + } + } + + public void testInCoreLimit_SwitchBeforeAppendByte() throws IOException { + final TemporaryBuffer b = new TemporaryBuffer(); + final byte[] test = new TestRng(getName()) + .nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 3); + try { + b.write(test, 0, test.length - 1); + b.write(test[test.length - 1]); + b.close(); + assertEquals(test.length, b.length()); + { + final byte[] r = b.toByteArray(); + assertNotNull(r); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + { + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + b.writeTo(o, null); + o.close(); + final byte[] r = o.toByteArray(); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + } finally { + b.destroy(); + } + } + + public void testInCoreLimit_SwitchOnCopy() throws IOException { + final TemporaryBuffer b = new TemporaryBuffer(); + final byte[] test = new TestRng(getName()) + .nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 2); + try { + final ByteArrayInputStream in = new ByteArrayInputStream(test, + TemporaryBuffer.DEFAULT_IN_CORE_LIMIT, test.length + - TemporaryBuffer.DEFAULT_IN_CORE_LIMIT); + b.write(test, 0, TemporaryBuffer.DEFAULT_IN_CORE_LIMIT); + b.copy(in); + b.close(); + assertEquals(test.length, b.length()); + { + final byte[] r = b.toByteArray(); + assertNotNull(r); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + { + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + b.writeTo(o, null); + o.close(); + final byte[] r = o.toByteArray(); + assertEquals(test.length, r.length); + assertTrue(Arrays.equals(test, r)); + } + } finally { + b.destroy(); + } + } + + public void testDestroyWhileOpen() throws IOException { + final TemporaryBuffer b = new TemporaryBuffer(); + try { + b.write(new TestRng(getName()) + .nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 2)); + } finally { + b.destroy(); + } + } + + public void testRandomWrites() throws IOException { + final TemporaryBuffer b = new TemporaryBuffer(); + final TestRng rng = new TestRng(getName()); + final int max = TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 2; + final byte[] expect = new byte[max]; + try { + int written = 0; + boolean onebyte = true; + while (written < max) { + if (onebyte) { + final byte v = (byte) rng.nextInt(); + b.write(v); + expect[written++] = v; + } else { + final int len = Math + .min(rng.nextInt() & 127, max - written); + final byte[] tmp = rng.nextBytes(len); + b.write(tmp, 0, len); + System.arraycopy(tmp, 0, expect, written, len); + written += len; + } + onebyte = !onebyte; + } + assertEquals(expect.length, written); + b.close(); + + assertEquals(expect.length, b.length()); + { + final byte[] r = b.toByteArray(); + assertNotNull(r); + assertEquals(expect.length, r.length); + assertTrue(Arrays.equals(expect, r)); + } + { + final ByteArrayOutputStream o = new ByteArrayOutputStream(); + b.writeTo(o, null); + o.close(); + final byte[] r = o.toByteArray(); + assertEquals(expect.length, r.length); + assertTrue(Arrays.equals(expect, r)); + } + } finally { + b.destroy(); + } + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TestRng.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TestRng.java new file mode 100644 index 0000000000..4110b55baa --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TestRng.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008, 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.util; + +/** Toy RNG to ensure we get predictable numbers during unit tests. */ +public class TestRng { + private int next; + + public TestRng(final String seed) { + next = 0; + for (int i = 0; i < seed.length(); i++) + next = next * 11 + seed.charAt(i); + } + + public byte[] nextBytes(final int cnt) { + final byte[] r = new byte[cnt]; + for (int i = 0; i < cnt; i++) + r[i] = (byte) nextInt(); + return r; + } + + public int nextInt() { + next = next * 1103515245 + 12345; + return next; + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutInputStreamTest.java new file mode 100644 index 0000000000..cd16288b64 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutInputStreamTest.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 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.util.io; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jgit.util.NB; +import org.eclipse.jgit.util.io.InterruptTimer; +import org.eclipse.jgit.util.io.TimeoutInputStream; + +import junit.framework.TestCase; + +public class TimeoutInputStreamTest extends TestCase { + private static final int timeout = 250; + + private PipedOutputStream out; + + private PipedInputStream in; + + private InterruptTimer timer; + + private TimeoutInputStream is; + + private long start; + + protected void setUp() throws Exception { + super.setUp(); + out = new PipedOutputStream(); + in = new PipedInputStream(out); + timer = new InterruptTimer(); + is = new TimeoutInputStream(in, timer); + is.setTimeout(timeout); + } + + protected void tearDown() throws Exception { + timer.terminate(); + for (Thread t : active()) + assertFalse(t instanceof InterruptTimer.AlarmThread); + super.tearDown(); + } + + public void testTimeout_readByte_Success1() throws IOException { + out.write('a'); + assertEquals('a', is.read()); + } + + public void testTimeout_readByte_Success2() throws IOException { + final byte[] exp = new byte[] { 'a', 'b', 'c' }; + out.write(exp); + assertEquals(exp[0], is.read()); + assertEquals(exp[1], is.read()); + assertEquals(exp[2], is.read()); + out.close(); + assertEquals(-1, is.read()); + } + + public void testTimeout_readByte_Timeout() throws IOException { + beginRead(); + try { + is.read(); + fail("incorrectly read a byte"); + } catch (InterruptedIOException e) { + // expected + } + assertTimeout(); + } + + public void testTimeout_readBuffer_Success1() throws IOException { + final byte[] exp = new byte[] { 'a', 'b', 'c' }; + final byte[] act = new byte[exp.length]; + out.write(exp); + NB.readFully(is, act, 0, act.length); + assertTrue(Arrays.equals(exp, act)); + } + + public void testTimeout_readBuffer_Success2() throws IOException { + final byte[] exp = new byte[] { 'a', 'b', 'c' }; + final byte[] act = new byte[exp.length]; + out.write(exp); + NB.readFully(is, act, 0, 1); + NB.readFully(is, act, 1, 1); + NB.readFully(is, act, 2, 1); + assertTrue(Arrays.equals(exp, act)); + } + + public void testTimeout_readBuffer_Timeout() throws IOException { + beginRead(); + try { + NB.readFully(is, new byte[512], 0, 512); + fail("incorrectly read bytes"); + } catch (InterruptedIOException e) { + // expected + } + assertTimeout(); + } + + public void testTimeout_skip_Success() throws IOException { + final byte[] exp = new byte[] { 'a', 'b', 'c' }; + out.write(exp); + assertEquals(2, is.skip(2)); + assertEquals('c', is.read()); + } + + public void testTimeout_skip_Timeout() throws IOException { + beginRead(); + try { + is.skip(1024); + fail("incorrectly skipped bytes"); + } catch (InterruptedIOException e) { + // expected + } + assertTimeout(); + } + + private void beginRead() { + start = now(); + } + + private void assertTimeout() { + // Our timeout was supposed to be ~250 ms. Since this is a timing + // test we can't assume we spent *exactly* the timeout period, as + // there may be other activity going on in the system. Instead we + // look for the delta between the start and end times to be within + // 50 ms of the expected timeout. + // + final long wait = now() - start; + assertTrue(Math.abs(wait - timeout) < 50); + } + + private static List<Thread> active() { + Thread[] all = new Thread[16]; + int n = Thread.currentThread().getThreadGroup().enumerate(all); + while (n == all.length) { + all = new Thread[all.length * 2]; + n = Thread.currentThread().getThreadGroup().enumerate(all); + } + return Arrays.asList(all).subList(0, n); + } + + private static long now() { + return System.currentTimeMillis(); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutOutputStreamTest.java new file mode 100644 index 0000000000..bba8640ff3 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/TimeoutOutputStreamTest.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 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.util.io; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jgit.util.NB; +import org.eclipse.jgit.util.io.InterruptTimer; +import org.eclipse.jgit.util.io.TimeoutOutputStream; + +import junit.framework.TestCase; + +public class TimeoutOutputStreamTest extends TestCase { + private static final int timeout = 250; + + private PipedOutputStream out; + + private FullPipeInputStream in; + + private InterruptTimer timer; + + private TimeoutOutputStream os; + + private long start; + + protected void setUp() throws Exception { + super.setUp(); + out = new PipedOutputStream(); + in = new FullPipeInputStream(out); + timer = new InterruptTimer(); + os = new TimeoutOutputStream(out, timer); + os.setTimeout(timeout); + } + + protected void tearDown() throws Exception { + timer.terminate(); + for (Thread t : active()) + assertFalse(t instanceof InterruptTimer.AlarmThread); + super.tearDown(); + } + + public void testTimeout_writeByte_Success1() throws IOException { + in.free(1); + os.write('a'); + in.want(1); + assertEquals('a', in.read()); + } + + public void testTimeout_writeByte_Success2() throws IOException { + final byte[] exp = new byte[] { 'a', 'b', 'c' }; + final byte[] act = new byte[exp.length]; + in.free(exp.length); + os.write(exp[0]); + os.write(exp[1]); + os.write(exp[2]); + in.want(exp.length); + in.read(act); + assertTrue(Arrays.equals(exp, act)); + } + + public void testTimeout_writeByte_Timeout() throws IOException { + beginWrite(); + try { + os.write('\n'); + fail("incorrectly write a byte"); + } catch (InterruptedIOException e) { + // expected + } + assertTimeout(); + } + + public void testTimeout_writeBuffer_Success1() throws IOException { + final byte[] exp = new byte[] { 'a', 'b', 'c' }; + final byte[] act = new byte[exp.length]; + in.free(exp.length); + os.write(exp); + in.want(exp.length); + in.read(act); + assertTrue(Arrays.equals(exp, act)); + } + + public void testTimeout_writeBuffer_Timeout() throws IOException { + beginWrite(); + try { + os.write(new byte[512]); + fail("incorrectly wrote bytes"); + } catch (InterruptedIOException e) { + // expected + } + assertTimeout(); + } + + public void testTimeout_flush_Success() throws IOException { + final boolean[] called = new boolean[1]; + os = new TimeoutOutputStream(new OutputStream() { + @Override + public void write(int b) throws IOException { + fail("should not have written"); + } + + @Override + public void flush() throws IOException { + called[0] = true; + } + }, timer); + os.setTimeout(timeout); + os.flush(); + assertTrue(called[0]); + } + + public void testTimeout_flush_Timeout() throws IOException { + final boolean[] called = new boolean[1]; + os = new TimeoutOutputStream(new OutputStream() { + @Override + public void write(int b) throws IOException { + fail("should not have written"); + } + + @Override + public void flush() throws IOException { + called[0] = true; + for (;;) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + } + }, timer); + os.setTimeout(timeout); + + beginWrite(); + try { + os.flush(); + fail("incorrectly flushed"); + } catch (InterruptedIOException e) { + // expected + } + assertTimeout(); + assertTrue(called[0]); + } + + public void testTimeout_close_Success() throws IOException { + final boolean[] called = new boolean[1]; + os = new TimeoutOutputStream(new OutputStream() { + @Override + public void write(int b) throws IOException { + fail("should not have written"); + } + + @Override + public void close() throws IOException { + called[0] = true; + } + }, timer); + os.setTimeout(timeout); + os.close(); + assertTrue(called[0]); + } + + public void testTimeout_close_Timeout() throws IOException { + final boolean[] called = new boolean[1]; + os = new TimeoutOutputStream(new OutputStream() { + @Override + public void write(int b) throws IOException { + fail("should not have written"); + } + + @Override + public void close() throws IOException { + called[0] = true; + for (;;) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + } + }, timer); + os.setTimeout(timeout); + + beginWrite(); + try { + os.close(); + fail("incorrectly closed"); + } catch (InterruptedIOException e) { + // expected + } + assertTimeout(); + assertTrue(called[0]); + } + + private void beginWrite() { + start = now(); + } + + private void assertTimeout() { + // Our timeout was supposed to be ~250 ms. Since this is a timing + // test we can't assume we spent *exactly* the timeout period, as + // there may be other activity going on in the system. Instead we + // look for the delta between the start and end times to be within + // 50 ms of the expected timeout. + // + final long wait = now() - start; + assertTrue(Math.abs(wait - timeout) < 50); + } + + private static List<Thread> active() { + Thread[] all = new Thread[16]; + int n = Thread.currentThread().getThreadGroup().enumerate(all); + while (n == all.length) { + all = new Thread[all.length * 2]; + n = Thread.currentThread().getThreadGroup().enumerate(all); + } + return Arrays.asList(all).subList(0, n); + } + + private static long now() { + return System.currentTimeMillis(); + } + + private final class FullPipeInputStream extends PipedInputStream { + FullPipeInputStream(PipedOutputStream src) throws IOException { + super(src); + src.write(new byte[PIPE_SIZE]); + } + + void want(int cnt) throws IOException { + NB.skipFully(this, PIPE_SIZE - cnt); + } + + void free(int cnt) throws IOException { + NB.skipFully(this, cnt); + } + } +} |