From f945c424d0cc7688cd160fd5ed9636cd2479e378 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Tue, 5 Jan 2010 11:44:52 -0800
Subject: [PATCH] Abstract out utility functions for creating test commits

These routines create a fairly clean DSL for writing out the
structure of a repository in a test case.  Abstract them into
a helper class that we can reuse in other test environments.

Change-Id: I55cce3d557e1a28afe2fdf37b3a5b67e2651c9f1
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../eclipse/jgit/junit/TestRepository.java    | 650 ++++++++++++++++++
 .../jgit/revwalk/DateRevQueueTest.java        |  20 +-
 .../eclipse/jgit/revwalk/ObjectWalkTest.java  |   5 +-
 .../eclipse/jgit/revwalk/RevObjectTest.java   |   6 +-
 .../jgit/revwalk/RevQueueTestCase.java        |  10 +-
 .../jgit/revwalk/RevWalkFilterTest.java       |   6 +-
 .../revwalk/RevWalkPathFilter6012Test.java    |   6 +-
 .../eclipse/jgit/revwalk/RevWalkSortTest.java |   6 +-
 .../eclipse/jgit/revwalk/RevWalkTestCase.java |  97 +--
 9 files changed, 704 insertions(+), 102 deletions(-)
 create mode 100644 org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java

diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
new file mode 100644
index 000000000..ce8b3e6ee
--- /dev/null
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2009-2010, 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.junit;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
+
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEditor;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath;
+import org.eclipse.jgit.dircache.DirCacheEditor.DeleteTree;
+import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
+import org.eclipse.jgit.errors.ObjectWritingException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Commit;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.LockFile;
+import org.eclipse.jgit.lib.ObjectDirectory;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefWriter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Tag;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+
+/** Wrapper to make creating test data easier. */
+public class TestRepository {
+	private static final PersonIdent author;
+
+	private static final PersonIdent committer;
+
+	static {
+		final MockSystemReader m = new MockSystemReader();
+		final long now = m.getCurrentTime();
+		final int tz = m.getTimezone(now);
+
+		final String an = "J. Author";
+		final String ae = "jauthor@example.com";
+		author = new PersonIdent(an, ae, now, tz);
+
+		final String cn = "J. Committer";
+		final String ce = "jcommitter@example.com";
+		committer = new PersonIdent(cn, ce, now, tz);
+	}
+
+	private final Repository db;
+
+	private final RevWalk pool;
+
+	private final ObjectWriter writer;
+
+	private long now;
+
+	/**
+	 * Wrap a repository with test building tools.
+	 *
+	 * @param db
+	 *            the test repository to write into.
+	 * @throws Exception
+	 */
+	public TestRepository(Repository db) throws Exception {
+		this(db, new RevWalk(db));
+	}
+
+	/**
+	 * Wrap a repository with test building tools.
+	 *
+	 * @param db
+	 *            the test repository to write into.
+	 * @param rw
+	 *            the RevObject pool to use for object lookup.
+	 * @throws Exception
+	 */
+	public TestRepository(Repository db, RevWalk rw) throws Exception {
+		this.db = db;
+		this.pool = rw;
+		this.writer = new ObjectWriter(db);
+		this.now = 1236977987000L;
+	}
+
+	/** @return the repository this helper class operates against. */
+	public Repository getRepository() {
+		return db;
+	}
+
+	/** @return get the RevWalk pool all objects are allocated through. */
+	public RevWalk getRevWalk() {
+		return pool;
+	}
+
+	/** @return current time adjusted by {@link #tick(int)}. */
+	public Date getClock() {
+		return new Date(now);
+	}
+
+	/**
+	 * Adjust the current time that will used by the next commit.
+	 *
+	 * @param secDelta
+	 *            number of seconds to add to the current time.
+	 */
+	public void tick(final int secDelta) {
+		now += secDelta * 1000L;
+	}
+
+	/**
+	 * Create a new blob object in the repository.
+	 *
+	 * @param content
+	 *            file content, will be UTF-8 encoded.
+	 * @return reference to the blob.
+	 * @throws Exception
+	 */
+	public RevBlob blob(final String content) throws Exception {
+		return blob(content.getBytes("UTF-8"));
+	}
+
+	/**
+	 * Create a new blob object in the repository.
+	 *
+	 * @param content
+	 *            binary file content.
+	 * @return reference to the blob.
+	 * @throws Exception
+	 */
+	public RevBlob blob(final byte[] content) throws Exception {
+		return pool.lookupBlob(writer.writeBlob(content));
+	}
+
+	/**
+	 * Construct a regular file mode tree entry.
+	 *
+	 * @param path
+	 *            path of the file.
+	 * @param blob
+	 *            a blob, previously constructed in the repository.
+	 * @return the entry.
+	 * @throws Exception
+	 */
+	public 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;
+	}
+
+	/**
+	 * Construct a tree from a specific listing of file entries.
+	 *
+	 * @param entries
+	 *            the files to include in the tree. The collection does not need
+	 *            to be sorted properly and may be empty.
+	 * @return reference to the tree specified by the entry list.
+	 * @throws Exception
+	 */
+	public 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 pool.lookupTree(dc.writeTree(writer));
+	}
+
+	/**
+	 * Lookup an entry stored in a tree, failing if not present.
+	 *
+	 * @param tree
+	 *            the tree to search.
+	 * @param path
+	 *            the path to find the entry of.
+	 * @return the parsed object entry at this path, never null.
+	 * @throws AssertionFailedError
+	 *             if the path does not exist in the given tree.
+	 * @throws Exception
+	 */
+	public RevObject get(final RevTree tree, final String path)
+			throws AssertionFailedError, 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 pool.lookupAny(entid, entmode.getObjectType());
+		}
+		Assert.fail("Can't find " + path + " in tree " + tree.name());
+		return null; // never reached.
+	}
+
+	/**
+	 * Create a new commit.
+	 * <p>
+	 * See {@link #commit(int, RevTree, RevCommit...)}. The tree is the empty
+	 * tree (no files or subdirectories).
+	 *
+	 * @param parents
+	 *            zero or more parents of the commit.
+	 * @return the new commit.
+	 * @throws Exception
+	 */
+	public RevCommit commit(final RevCommit... parents) throws Exception {
+		return commit(1, tree(), parents);
+	}
+
+	/**
+	 * Create a new commit.
+	 * <p>
+	 * See {@link #commit(int, RevTree, RevCommit...)}.
+	 *
+	 * @param tree
+	 *            the root tree for the commit.
+	 * @param parents
+	 *            zero or more parents of the commit.
+	 * @return the new commit.
+	 * @throws Exception
+	 */
+	public RevCommit commit(final RevTree tree, final RevCommit... parents)
+			throws Exception {
+		return commit(1, tree, parents);
+	}
+
+	/**
+	 * Create a new commit.
+	 * <p>
+	 * See {@link #commit(int, RevTree, RevCommit...)}. The tree is the empty
+	 * tree (no files or subdirectories).
+	 *
+	 * @param secDelta
+	 *            number of seconds to advance {@link #tick(int)} by.
+	 * @param parents
+	 *            zero or more parents of the commit.
+	 * @return the new commit.
+	 * @throws Exception
+	 */
+	public RevCommit commit(final int secDelta, final RevCommit... parents)
+			throws Exception {
+		return commit(secDelta, tree(), parents);
+	}
+
+	/**
+	 * Create a new commit.
+	 * <p>
+	 * The author and committer identities are stored using the current
+	 * timestamp, after being incremented by {@code secDelta}. The message body
+	 * is empty.
+	 *
+	 * @param secDelta
+	 *            number of seconds to advance {@link #tick(int)} by.
+	 * @param tree
+	 *            the root tree for the commit.
+	 * @param parents
+	 *            zero or more parents of the commit.
+	 * @return the new commit.
+	 * @throws Exception
+	 */
+	public 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(author, new Date(now)));
+		c.setCommitter(new PersonIdent(committer, new Date(now)));
+		c.setMessage("");
+		return pool.lookupCommit(writer.writeCommit(c));
+	}
+
+	/** @return a new commit builder. */
+	public CommitBuilder commit() {
+		return new CommitBuilder();
+	}
+
+	/**
+	 * Construct an annotated tag object pointing at another object.
+	 * <p>
+	 * The tagger is the committer identity, at the current time as specified by
+	 * {@link #tick(int)}. The time is not increased.
+	 * <p>
+	 * The tag message is empty.
+	 *
+	 * @param name
+	 *            name of the tag. Traditionally a tag name should not start
+	 *            with {@code refs/tags/}.
+	 * @param dst
+	 *            object the tag should be pointed at.
+	 * @return the annotated tag object.
+	 * @throws Exception
+	 */
+	public 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(committer, new Date(now)));
+		t.setMessage("");
+		return (RevTag) pool.lookupAny(writer.writeTag(t), Constants.OBJ_TAG);
+	}
+
+	/**
+	 * Update a reference to point to an object.
+	 *
+	 * @param ref
+	 *            the name of the reference to update to. If {@code ref} does
+	 *            not start with {@code refs/} and is not the magic names
+	 *            {@code HEAD} {@code FETCH_HEAD} or {@code MERGE_HEAD}, then
+	 *            {@code refs/heads/} will be prefixed in front of the given
+	 *            name, thereby assuming it is a branch.
+	 * @param to
+	 *            the target object.
+	 * @return the target object.
+	 * @throws Exception
+	 */
+	public RevCommit update(String ref, CommitBuilder to) throws Exception {
+		return update(ref, to.create());
+	}
+
+	/**
+	 * Update a reference to point to an object.
+	 *
+	 * @param <T>
+	 *            type of the target object.
+	 * @param ref
+	 *            the name of the reference to update to. If {@code ref} does
+	 *            not start with {@code refs/} and is not the magic names
+	 *            {@code HEAD} {@code FETCH_HEAD} or {@code MERGE_HEAD}, then
+	 *            {@code refs/heads/} will be prefixed in front of the given
+	 *            name, thereby assuming it is a branch.
+	 * @param obj
+	 *            the target object.
+	 * @return the target object.
+	 * @throws Exception
+	 */
+	public <T extends AnyObjectId> T update(String ref, T obj) throws Exception {
+		if (Constants.HEAD.equals(ref)) {
+		} else if ("FETCH_HEAD".equals(ref)) {
+		} else if ("MERGE_HEAD".equals(ref)) {
+		} else if (ref.startsWith(Constants.R_REFS)) {
+		} else
+			ref = Constants.R_HEADS + ref;
+
+		RefUpdate u = db.updateRef(ref);
+		u.setNewObjectId(obj);
+		switch (u.forceUpdate()) {
+		case FAST_FORWARD:
+		case FORCED:
+		case NEW:
+		case NO_CHANGE:
+			updateServerInfo();
+			return obj;
+
+		default:
+			throw new IOException("Cannot write " + ref + " " + u.getResult());
+		}
+	}
+
+	/**
+	 * Update the dumb client server info files.
+	 *
+	 * @throws Exception
+	 */
+	public void updateServerInfo() throws Exception {
+		if (db.getObjectDatabase() instanceof ObjectDirectory) {
+			RefWriter rw = new RefWriter(db.getAllRefs().values()) {
+				@Override
+				protected void writeFile(final String name, final byte[] bin)
+						throws IOException {
+					final File p = new File(db.getDirectory(), name);
+					final LockFile lck = new LockFile(p);
+					if (!lck.lock())
+						throw new ObjectWritingException("Can't write " + p);
+					try {
+						lck.write(bin);
+					} catch (IOException ioe) {
+						throw new ObjectWritingException("Can't write " + p);
+					}
+					if (!lck.commit())
+						throw new ObjectWritingException("Can't write " + p);
+				}
+			};
+			rw.writePackedRefs();
+			rw.writeInfoRefs();
+		}
+	}
+
+	/**
+	 * Ensure the body of the given object has been parsed.
+	 *
+	 * @param <T>
+	 *            type of object, e.g. {@link RevTag} or {@link RevCommit}.
+	 * @param object
+	 *            reference to the (possibly unparsed) object to force body
+	 *            parsing of.
+	 * @return {@code object}
+	 * @throws Exception
+	 */
+	public <T extends RevObject> T parseBody(final T object) throws Exception {
+		pool.parseBody(object);
+		return object;
+	}
+
+	/**
+	 * Create a new branch builder for this repository.
+	 *
+	 * @param ref
+	 *            name of the branch to be constructed. If {@code ref} does not
+	 *            start with {@code refs/} the prefix {@code refs/heads/} will
+	 *            be added.
+	 * @return builder for the named branch.
+	 */
+	public BranchBuilder branch(String ref) {
+		if (Constants.HEAD.equals(ref)) {
+		} else if (ref.startsWith(Constants.R_REFS)) {
+		} else
+			ref = Constants.R_HEADS + ref;
+		return new BranchBuilder(ref);
+	}
+
+	/** Helper to build a branch with one or more commits */
+	public class BranchBuilder {
+		private final String ref;
+
+		BranchBuilder(final String ref) {
+			this.ref = ref;
+		}
+
+		/**
+		 * @return construct a new commit builder that updates this branch. If
+		 *         the branch already exists, the commit builder will have its
+		 *         first parent as the current commit and its tree will be
+		 *         initialized to the current files.
+		 * @throws Exception
+		 *             the commit builder can't read the current branch state
+		 */
+		public CommitBuilder commit() throws Exception {
+			return new CommitBuilder(this);
+		}
+
+		/**
+		 * Forcefully update this branch to a particular commit.
+		 *
+		 * @param to
+		 *            the commit to update to.
+		 * @return {@code to}.
+		 * @throws Exception
+		 */
+		public RevCommit update(CommitBuilder to) throws Exception {
+			return update(to.create());
+		}
+
+		/**
+		 * Forcefully update this branch to a particular commit.
+		 *
+		 * @param to
+		 *            the commit to update to.
+		 * @return {@code to}.
+		 * @throws Exception
+		 */
+		public RevCommit update(RevCommit to) throws Exception {
+			return TestRepository.this.update(ref, to);
+		}
+	}
+
+	/** Helper to generate a commit. */
+	public class CommitBuilder {
+		private final BranchBuilder branch;
+
+		private final DirCache tree = DirCache.newInCore();
+
+		private final List<RevCommit> parents = new ArrayList<RevCommit>(2);
+
+		private int tick = 1;
+
+		private String message = "";
+
+		private RevCommit self;
+
+		CommitBuilder() {
+			branch = null;
+		}
+
+		CommitBuilder(BranchBuilder b) throws Exception {
+			branch = b;
+
+			Ref ref = db.getRef(branch.ref);
+			if (ref != null) {
+				parent(pool.parseCommit(ref.getObjectId()));
+			}
+		}
+
+		CommitBuilder(CommitBuilder prior) throws Exception {
+			branch = prior.branch;
+
+			DirCacheBuilder b = tree.builder();
+			for (int i = 0; i < prior.tree.getEntryCount(); i++)
+				b.add(prior.tree.getEntry(i));
+			b.finish();
+
+			parents.add(prior.create());
+		}
+
+		public CommitBuilder parent(RevCommit p) throws Exception {
+			if (parents.isEmpty()) {
+				DirCacheBuilder b = tree.builder();
+				parseBody(p);
+				b.addTree(new byte[0], DirCacheEntry.STAGE_0, db, p.getTree());
+				b.finish();
+			}
+			parents.add(p);
+			return this;
+		}
+
+		public CommitBuilder noParents() {
+			parents.clear();
+			return this;
+		}
+
+		public CommitBuilder noFiles() {
+			tree.clear();
+			return this;
+		}
+
+		public CommitBuilder add(String path, String content) throws Exception {
+			return add(path, blob(content));
+		}
+
+		public CommitBuilder add(String path, final RevBlob id)
+				throws Exception {
+			DirCacheEditor e = tree.editor();
+			e.add(new PathEdit(path) {
+				@Override
+				public void apply(DirCacheEntry ent) {
+					ent.setFileMode(FileMode.REGULAR_FILE);
+					ent.setObjectId(id);
+				}
+			});
+			e.finish();
+			return this;
+		}
+
+		public CommitBuilder rm(String path) {
+			DirCacheEditor e = tree.editor();
+			e.add(new DeletePath(path));
+			e.add(new DeleteTree(path));
+			e.finish();
+			return this;
+		}
+
+		public CommitBuilder message(String m) {
+			message = m;
+			return this;
+		}
+
+		public CommitBuilder tick(int secs) {
+			tick = secs;
+			return this;
+		}
+
+		public RevCommit create() throws Exception {
+			if (self == null) {
+				TestRepository.this.tick(tick);
+
+				final Commit c = new Commit(db);
+				c.setTreeId(pool.lookupTree(tree.writeTree(writer)));
+				c.setParentIds(parents.toArray(new RevCommit[parents.size()]));
+				c.setAuthor(new PersonIdent(author, new Date(now)));
+				c.setCommitter(new PersonIdent(committer, new Date(now)));
+				c.setMessage(message);
+
+				self = pool.lookupCommit(writer.writeCommit(c));
+
+				if (branch != null)
+					branch.update(self);
+			}
+			return self;
+		}
+
+		public CommitBuilder child() throws Exception {
+			return new CommitBuilder(this);
+		}
+	}
+}
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
index b3a92951b..ee9c81cbd 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009-2010, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -60,10 +60,10 @@ public void testCloneEmpty() throws Exception {
 	}
 
 	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));
+		final RevCommit a = parseBody(commit());
+		final RevCommit b = parseBody(commit(10, a));
+		final RevCommit c1 = parseBody(commit(5, b));
+		final RevCommit c2 = parseBody(commit(-50, b));
 
 		q.add(c2);
 		q.add(a);
@@ -78,8 +78,8 @@ public void testInsertOutOfOrder() throws Exception {
 	}
 
 	public void testInsertTie() throws Exception {
-		final RevCommit a = parse(commit());
-		final RevCommit b = parse(commit(0, a));
+		final RevCommit a = parseBody(commit());
+		final RevCommit b = parseBody(commit(0, a));
 		{
 			q = create();
 			q.add(a);
@@ -101,9 +101,9 @@ public void testInsertTie() throws Exception {
 	}
 
 	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 RevCommit a = parseBody(commit());
+		final RevCommit b = parseBody(commit(200, a));
+		final RevCommit c = parseBody(commit(200, b));
 
 		final FIFORevQueue src = new FIFORevQueue();
 		src.add(a);
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
index 7dddeee20..582406c01 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectWalkTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009-2010, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -46,6 +46,7 @@
 public class ObjectWalkTest extends RevWalkTestCase {
 	protected ObjectWalk objw;
 
+	@Override
 	protected RevWalk createRevWalk() {
 		return objw = new ObjectWalk(db);
 	}
@@ -64,7 +65,7 @@ public void testTwoCommitsEmptyTree() throws Exception {
 		assertCommit(a, objw.next());
 		assertNull(objw.next());
 
-		assertSame(emptyTree, objw.nextObject());
+		assertSame(tree(), objw.nextObject());
 		assertNull(objw.nextObject());
 	}
 
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
index 87ecaa8c5..d4f289a16 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevObjectTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevObjectTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009-2010, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -81,10 +81,10 @@ public void testEqualsIsIdentity() throws Exception {
 	}
 
 	public void testRevObjectTypes() throws Exception {
-		assertEquals(Constants.OBJ_TREE, emptyTree.getType());
+		assertEquals(Constants.OBJ_TREE, tree().getType());
 		assertEquals(Constants.OBJ_COMMIT, commit().getType());
 		assertEquals(Constants.OBJ_BLOB, blob("").getType());
-		assertEquals(Constants.OBJ_TAG, tag("emptyTree", emptyTree).getType());
+		assertEquals(Constants.OBJ_TAG, tag("emptyTree", tree()).getType());
 	}
 
 	public void testHasRevFlag() throws Exception {
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
index 24e84b041..c549054cb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevQueueTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevQueueTestCase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009-2010, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -61,8 +61,8 @@ public void testEmpty() throws Exception {
 	}
 
 	public void testClear() throws Exception {
-		final RevCommit a = parse(commit());
-		final RevCommit b = parse(commit(a));
+		final RevCommit a = parseBody(commit());
+		final RevCommit b = parseBody(commit(a));
 
 		q.add(a);
 		q.add(b);
@@ -71,8 +71,8 @@ public void testClear() throws Exception {
 	}
 
 	public void testHasFlags() throws Exception {
-		final RevCommit a = parse(commit());
-		final RevCommit b = parse(commit(a));
+		final RevCommit a = parseBody(commit());
+		final RevCommit b = parseBody(commit(a));
 
 		q.add(a);
 		q.add(b);
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
index db4c38e72..a6421c41d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009-2010, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -231,14 +231,14 @@ public void testCommitTimeRevFilter() throws Exception {
 		final RevCommit b = commit(a);
 		tick(100);
 
-		Date since = new Date(nowTick);
+		Date since = getClock();
 		final RevCommit c1 = commit(b);
 		tick(100);
 
 		final RevCommit c2 = commit(b);
 		tick(100);
 
-		Date until =  new Date(nowTick);
+		Date until = getClock();
 		final RevCommit d = commit(c1, c2);
 		tick(100);
 
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
index 73d41eae6..c4bfbf881 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkPathFilter6012Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkPathFilter6012Test.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009-2010, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -80,10 +80,10 @@ public void setUp() throws Exception {
 		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);
+		parseBody(d);
 		e = commit(d.getTree(), d, b);
 		f = commit(tree(file(pA, zS), file(pE, zY), file(pF, zI)), e);
-		parse(f);
+		parseBody(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);
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
index 0d3e0cf5a..65ed873bf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkSortTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkSortTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009-2010, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -96,8 +96,8 @@ public void testSort_COMMIT_TIME_DESC_OutOfOrder1() throws Exception {
 		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());
+		assertTrue(parseBody(a).getCommitTime() < parseBody(d).getCommitTime());
+		assertTrue(parseBody(c).getCommitTime() < parseBody(b).getCommitTime());
 
 		rw.sort(RevSort.COMMIT_TIME_DESC);
 		markStart(d);
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
index 312fc00b1..64052323f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, Google Inc.
+ * Copyright (C) 2009-2010, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -43,130 +43,81 @@
 
 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.junit.TestRepository;
 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;
+	private TestRepository util;
 
 	protected RevWalk rw;
 
+	@Override
 	public void setUp() throws Exception {
 		super.setUp();
-		ow = new ObjectWriter(db);
-		rw = createRevWalk();
-		emptyTree = rw.parseTree(ow.writeTree(new Tree(db)));
-		nowTick = 1236977987000L;
+		util = new TestRepository(db, createRevWalk());
+		rw = util.getRevWalk();
 	}
 
 	protected RevWalk createRevWalk() {
 		return new RevWalk(db);
 	}
 
+	protected Date getClock() {
+		return util.getClock();
+	}
+
 	protected void tick(final int secDelta) {
-		nowTick += secDelta * 1000L;
+		util.tick(secDelta);
 	}
 
 	protected RevBlob blob(final String content) throws Exception {
-		return rw.lookupBlob(ow.writeBlob(Constants.encode(content)));
+		return util.blob(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;
+		return util.file(path, blob);
 	}
 
 	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));
+		return util.tree(entries);
 	}
 
 	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.
+		return util.get(tree, path);
 	}
 
 	protected RevCommit commit(final RevCommit... parents) throws Exception {
-		return commit(1, emptyTree, parents);
+		return util.commit(parents);
 	}
 
 	protected RevCommit commit(final RevTree tree, final RevCommit... parents)
 			throws Exception {
-		return commit(1, tree, parents);
+		return util.commit(tree, parents);
 	}
 
 	protected RevCommit commit(final int secDelta, final RevCommit... parents)
 			throws Exception {
-		return commit(secDelta, emptyTree, parents);
+		return util.commit(secDelta, 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(author, new Date(nowTick)));
-		c.setCommitter(new PersonIdent(committer, new Date(nowTick)));
-		c.setMessage("");
-		return rw.lookupCommit(ow.writeCommit(c));
+		return util.commit(secDelta, tree, parents);
 	}
 
 	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(committer, 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;
+		return util.tag(name, dst);
+	}
+
+	protected <T extends RevObject> T parseBody(final T t) throws Exception {
+		return util.parseBody(t);
 	}
 
 	protected void markStart(final RevCommit commit) throws Exception {
-- 
GitLab