Browse Source

Break up GCTest to run in parallel

Separate some sections of the GC tests into different test classes.
Individual classes permits running in parallel under Buck, reducing
test latency if there are sufficient CPUs available.

Change-Id: I5eb177f78efd4aa8ac857d0d8b1a1fd81ca07790
tags/v3.2.0.201312181205-r
Shawn Pearce 10 years ago
parent
commit
53616a85ef

+ 0
- 777
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GCTest.java View File

@@ -1,777 +0,0 @@
/*
* Copyright (C) 2012, Christian Halstrick <christian.halstrick@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.internal.storage.file;

import static java.lang.Integer.valueOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.GC.RepoStatistics;
import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.EmptyProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.Merger;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.util.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class GCTest extends LocalDiskRepositoryTestCase {
private TestRepository<FileRepository> tr;

private FileRepository repo;

private GC gc;

private RepoStatistics stats;

@Before
public void setUp() throws Exception {
super.setUp();
repo = createWorkRepository();
tr = new TestRepository<FileRepository>((repo));
gc = new GC(repo);
}

@After
public void tearDown() throws Exception {
super.tearDown();
}

// GC.packRefs tests

@Test
public void packRefs_looseRefPacked() throws Exception {
RevBlob a = tr.blob("a");
tr.lightweightTag("t", a);

gc.packRefs();
assertSame(repo.getRef("t").getStorage(), Storage.PACKED);
}

@Test
public void concurrentPackRefs_onlyOneWritesPackedRefs() throws Exception {
RevBlob a = tr.blob("a");
tr.lightweightTag("t", a);

final CyclicBarrier syncPoint = new CyclicBarrier(2);

Callable<Integer> packRefs = new Callable<Integer>() {

/** @return 0 for success, 1 in case of error when writing pack */
public Integer call() throws Exception {
syncPoint.await();
try {
gc.packRefs();
return valueOf(0);
} catch (IOException e) {
return valueOf(1);
}
}
};
ExecutorService pool = Executors.newFixedThreadPool(2);
try {
Future<Integer> p1 = pool.submit(packRefs);
Future<Integer> p2 = pool.submit(packRefs);
assertEquals(1, p1.get().intValue() + p2.get().intValue());
} finally {
pool.shutdown();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}
}

@Test
public void packRefsWhileRefLocked_refNotPackedNoError()
throws Exception {
RevBlob a = tr.blob("a");
tr.lightweightTag("t1", a);
tr.lightweightTag("t2", a);
LockFile refLock = new LockFile(new File(repo.getDirectory(),
"refs/tags/t1"), repo.getFS());
try {
refLock.lock();
gc.packRefs();
} finally {
refLock.unlock();
}

assertSame(repo.getRef("refs/tags/t1").getStorage(), Storage.LOOSE);
assertSame(repo.getRef("refs/tags/t2").getStorage(), Storage.PACKED);
}

@Test
public void packRefsWhileRefUpdated_refUpdateSucceeds()
throws Exception {
RevBlob a = tr.blob("a");
tr.lightweightTag("t", a);
final RevBlob b = tr.blob("b");

final CyclicBarrier refUpdateLockedRef = new CyclicBarrier(2);
final CyclicBarrier packRefsDone = new CyclicBarrier(2);
ExecutorService pool = Executors.newFixedThreadPool(2);
try {
Future<Result> result = pool.submit(new Callable<Result>() {

public Result call() throws Exception {
RefUpdate update = new RefDirectoryUpdate(
(RefDirectory) repo.getRefDatabase(),
repo.getRef("refs/tags/t")) {
@Override
public boolean isForceUpdate() {
try {
refUpdateLockedRef.await();
packRefsDone.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (BrokenBarrierException e) {
Thread.currentThread().interrupt();
}
return super.isForceUpdate();
}
};
update.setForceUpdate(true);
update.setNewObjectId(b);
return update.update();
}
});

pool.submit(new Callable<Void>() {
public Void call() throws Exception {
refUpdateLockedRef.await();
gc.packRefs();
packRefsDone.await();
return null;
}
});

assertSame(result.get(), Result.FORCED);

} finally {
pool.shutdownNow();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}

assertEquals(repo.getRef("refs/tags/t").getObjectId(), b);
}

// GC.repack tests

@Test
public void repackEmptyRepo_noPackCreated() throws IOException {
gc.repack();
assertEquals(0, repo.getObjectDatabase().getPacks().size());
}

@Test
public void concurrentRepack() throws Exception {
final CyclicBarrier syncPoint = new CyclicBarrier(2);

class DoRepack extends EmptyProgressMonitor implements
Callable<Integer> {

public void beginTask(String title, int totalWork) {
if (title.equals(JGitText.get().writingObjects)) {
try {
syncPoint.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (BrokenBarrierException ignored) {
//
}
}
}

/** @return 0 for success, 1 in case of error when writing pack */
public Integer call() throws Exception {
try {
gc.setProgressMonitor(this);
gc.repack();
return valueOf(0);
} catch (IOException e) {
// leave the syncPoint in broken state so any awaiting
// threads and any threads that call await in the future get
// the BrokenBarrierException
Thread.currentThread().interrupt();
try {
syncPoint.await();
} catch (InterruptedException ignored) {
//
}
return valueOf(1);
}
}
}

RevBlob a = tr.blob("a");
tr.lightweightTag("t", a);

ExecutorService pool = Executors.newFixedThreadPool(2);
try {
DoRepack repack1 = new DoRepack();
DoRepack repack2 = new DoRepack();
Future<Integer> result1 = pool.submit(repack1);
Future<Integer> result2 = pool.submit(repack2);
assertEquals(0, result1.get().intValue() + result2.get().intValue());
} finally {
pool.shutdown();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}
}

// GC.prune tests

@Test
public void nonReferencedNonExpiredObject_notPruned() throws Exception {
RevBlob a = tr.blob("a");
gc.setExpire(new Date(lastModified(a)));
gc.prune(Collections.<ObjectId> emptySet());
assertTrue(repo.hasObject(a));
}

@Test
public void nonReferencedExpiredObject_pruned() throws Exception {
RevBlob a = tr.blob("a");
gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
assertFalse(repo.hasObject(a));
}

@Test
public void nonReferencedExpiredObjectTree_pruned() throws Exception {
RevBlob a = tr.blob("a");
RevTree t = tr.tree(tr.file("a", a));
gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
assertFalse(repo.hasObject(t));
assertFalse(repo.hasObject(a));
}

@Test
public void nonReferencedObjects_onlyExpiredPruned() throws Exception {
RevBlob a = tr.blob("a");
gc.setExpire(new Date(lastModified(a) + 1));

fsTick();
RevBlob b = tr.blob("b");

gc.prune(Collections.<ObjectId> emptySet());
assertFalse(repo.hasObject(a));
assertTrue(repo.hasObject(b));
}

@Test
public void lightweightTag_objectNotPruned() throws Exception {
RevBlob a = tr.blob("a");
tr.lightweightTag("t", a);
gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
assertTrue(repo.hasObject(a));
}

@Test
public void annotatedTag_objectNotPruned() throws Exception {
RevBlob a = tr.blob("a");
RevTag t = tr.tag("t", a); // this doesn't create the refs/tags/t ref
tr.lightweightTag("t", t);

gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
assertTrue(repo.hasObject(t));
assertTrue(repo.hasObject(a));
}

@Test
public void branch_historyNotPruned() throws Exception {
RevCommit tip = commitChain(10);
tr.branch("b").update(tip);
gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
do {
assertTrue(repo.hasObject(tip));
tr.parseBody(tip);
RevTree t = tip.getTree();
assertTrue(repo.hasObject(t));
assertTrue(repo.hasObject(tr.get(t, "a")));
tip = tip.getParentCount() > 0 ? tip.getParent(0) : null;
} while (tip != null);
}

@Test
public void deleteBranch_historyPruned() throws Exception {
RevCommit tip = commitChain(10);
tr.branch("b").update(tip);
RefUpdate update = repo.updateRef("refs/heads/b");
update.setForceUpdate(true);
update.delete();
gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
assertTrue(gc.getStatistics().numberOfLooseObjects == 0);
}

@Test
public void deleteMergedBranch_historyNotPruned() throws Exception {
RevCommit parent = tr.commit().create();
RevCommit b1Tip = tr.branch("b1").commit().parent(parent).add("x", "x")
.create();
RevCommit b2Tip = tr.branch("b2").commit().parent(parent).add("y", "y")
.create();

// merge b1Tip and b2Tip and update refs/heads/b1 to the merge commit
Merger merger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(repo);
merger.merge(b1Tip, b2Tip);
CommitBuilder cb = tr.commit();
cb.parent(b1Tip).parent(b2Tip);
cb.setTopLevelTree(merger.getResultTreeId());
RevCommit mergeCommit = cb.create();
RefUpdate u = repo.updateRef("refs/heads/b1");
u.setNewObjectId(mergeCommit);
u.update();

RefUpdate update = repo.updateRef("refs/heads/b2");
update.setForceUpdate(true);
update.delete();

gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
assertTrue(repo.hasObject(b2Tip));
}

@Test
public void testPackAllObjectsInOnePack() throws Exception {
tr.branch("refs/heads/master").commit().add("A", "A").add("B", "B")
.create();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);

// Do the gc again and check that it hasn't changed anything
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}

@Test
public void testPackRepoWithCorruptReflog() throws Exception {
// create a reflog entry "0000... 0000... foobar" by doing an initial
// refupdate for HEAD which points to a non-existing ref. The
// All-Projects repo of gerrit instances had such entries
RefUpdate ru = repo.updateRef(Constants.HEAD);
ru.link("refs/to/garbage");
tr.branch("refs/heads/master").commit().add("A", "A").add("B", "B")
.create();
// make sure HEAD exists
Git.wrap(repo).checkout().setName("refs/heads/master").call();
gc.gc();
}

@Test
public void testKeepFiles() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
assertEquals(0, stats.numberOfPackFiles);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);

Iterator<PackFile> packIt = repo.getObjectDatabase().getPacks()
.iterator();
PackFile singlePack = packIt.next();
assertFalse(packIt.hasNext());
File keepFile = new File(singlePack.getPackFile().getPath() + ".keep");
assertFalse(keepFile.exists());
assertTrue(keepFile.createNewFile());
bb.commit().add("A", "A2").add("B", "B2").create();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(2, stats.numberOfPackFiles);

// check that no object is packed twice
Iterator<PackFile> packs = repo.getObjectDatabase().getPacks()
.iterator();
PackIndex ind1 = packs.next().getIndex();
assertEquals(4, ind1.getObjectCount());
PackIndex ind2 = packs.next().getIndex();
assertEquals(4, ind2.getObjectCount());
for (MutableEntry e: ind1)
if (ind2.hasObject(e.toObjectId()))
assertFalse(
"the following object is in both packfiles: "
+ e.toObjectId(),
ind2.hasObject(e.toObjectId()));
}

@Test
public void testPackRepoWithNoRefs() throws Exception {
tr.commit().add("A", "A").add("B", "B").create();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.gc();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
assertEquals(0, stats.numberOfPackFiles);
}

@Test
public void testPack2Commits() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();

stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}

@Test
public void testPackCommitsAndLooseOne() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
tr.update("refs/heads/master", first);

stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(2, stats.numberOfPackFiles);
}

@Test
public void testNotPackTwice() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().message("M").add("M", "M").create();
bb.commit().message("B").add("B", "Q").create();
bb.commit().message("A").add("A", "A").create();
RevCommit second = tr.commit().parent(first).message("R").add("R", "Q")
.create();
tr.update("refs/tags/t1", second);

Collection<PackFile> oldPacks = tr.getRepository().getObjectDatabase()
.getPacks();
assertEquals(0, oldPacks.size());
stats = gc.getStatistics();
assertEquals(11, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);

gc.setExpireAgeMillis(0);
fsTick();
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);

Iterator<PackFile> pIt = repo.getObjectDatabase().getPacks().iterator();
long c = pIt.next().getObjectCount();
if (c == 9)
assertEquals(2, pIt.next().getObjectCount());
else {
assertEquals(2, c);
assertEquals(9, pIt.next().getObjectCount());
}
}

@Test
public void testPackCommitsAndLooseOneNoReflog() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
tr.update("refs/heads/master", first);

stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);

FileUtils.delete(new File(repo.getDirectory(), "logs/HEAD"),
FileUtils.RETRY | FileUtils.SKIP_MISSING);
FileUtils.delete(
new File(repo.getDirectory(), "logs/refs/heads/master"),
FileUtils.RETRY | FileUtils.SKIP_MISSING);
gc.gc();

stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}

@Test
public void testPackCommitsAndLooseOneWithPruneNow() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
tr.update("refs/heads/master", first);

stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.setExpireAgeMillis(0);
fsTick();
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(2, stats.numberOfPackFiles);
}

@Test
public void testPruneOldPacksWithOpenHandleOnPack() throws Exception {
gc.setExpireAgeMillis(0);

BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
fsTick();
gc.gc();

Collection<PackFile> packs = repo.getObjectDatabase().getPacks();
assertEquals(1, packs.size());
PackFile pack = packs.iterator().next();
File packFile = pack.getPackFile();
File indexFile = new File(packFile.getParentFile(), "pack-"
+ pack.getPackName()
+ "."
+ PackExt.INDEX.getExtension());
FileInputStream fis = new FileInputStream(packFile);
try {
bb.commit().add("A", "A2").add("B", "B2").create();
fsTick();
gc.gc();
if (packFile.exists()) {
assertTrue(
"The pack was present but the index file was missing.",
indexFile.exists());
}

} finally {
fis.close();
}

}

@Test
public void testPackCommitsAndLooseOneWithPruneNowNoReflog()
throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
tr.update("refs/heads/master", first);

stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);

FileUtils.delete(new File(repo.getDirectory(), "logs/HEAD"),
FileUtils.RETRY | FileUtils.SKIP_MISSING);
FileUtils.delete(
new File(repo.getDirectory(), "logs/refs/heads/master"),
FileUtils.RETRY | FileUtils.SKIP_MISSING);
gc.setExpireAgeMillis(0);
gc.gc();

stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}

@Test
public void testIndexSavesObjects() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
bb.commit().add("A", "A3"); // this new content in index should survive
stats = gc.getStatistics();
assertEquals(9, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.gc();
stats = gc.getStatistics();
assertEquals(1, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}

@Test
public void testIndexSavesObjectsWithPruneNow() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
bb.commit().add("A", "A3"); // this new content in index should survive
stats = gc.getStatistics();
assertEquals(9, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.setExpireAgeMillis(0);
fsTick();
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}

@Test
public void testPruneNone() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
new File(repo.getDirectory(), Constants.LOGS + "/refs/heads/master")
.delete();
stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
tr.blob("x");
stats = gc.getStatistics();
assertEquals(9, stats.numberOfLooseObjects);
gc.prune(Collections.<ObjectId> emptySet());
stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
}

/**
* Create a chain of commits of given depth.
* <p>
* Each commit contains one file named "a" containing the index of the
* commit in the chain as its content. The created commit chain is
* referenced from any ref.
* <p>
* A chain of depth = N will create 3*N objects in Gits object database. For
* each depth level three objects are created: the commit object, the
* top-level tree object and a blob for the content of the file "a".
*
* @param depth
* the depth of the commit chain.
* @return the commit that is the tip of the commit chain
* @throws Exception
*/
private RevCommit commitChain(int depth) throws Exception {
if (depth <= 0)
throw new IllegalArgumentException("Chain depth must be > 0");
CommitBuilder cb = tr.commit();
RevCommit tip;
do {
--depth;
tip = cb.add("a", "" + depth).message("" + depth).create();
cb = cb.child();
} while (depth > 0);
return tip;
}

private long lastModified(AnyObjectId objectId) {
return repo.getObjectDatabase().fileFor(objectId).lastModified();
}

private static void fsTick() throws InterruptedException, IOException {
RepositoryTestCase.fsTick(null);
}
}

+ 162
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2012, Christian Halstrick <christian.halstrick@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.internal.storage.file;

import static org.junit.Assert.assertEquals;

import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;

import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;

public class GcBasicPackingTest extends GcTestCase {
@Test
public void repackEmptyRepo_noPackCreated() throws IOException {
gc.repack();
assertEquals(0, repo.getObjectDatabase().getPacks().size());
}

@Test
public void testPackRepoWithNoRefs() throws Exception {
tr.commit().add("A", "A").add("B", "B").create();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.gc();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
assertEquals(0, stats.numberOfPackFiles);
}

@Test
public void testPack2Commits() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();

stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}

@Test
public void testPackAllObjectsInOnePack() throws Exception {
tr.branch("refs/heads/master").commit().add("A", "A").add("B", "B")
.create();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);

// Do the gc again and check that it hasn't changed anything
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}

@Test
public void testPackCommitsAndLooseOne() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
tr.update("refs/heads/master", first);

stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(2, stats.numberOfPackFiles);
}

@Test
public void testNotPackTwice() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().message("M").add("M", "M").create();
bb.commit().message("B").add("B", "Q").create();
bb.commit().message("A").add("A", "A").create();
RevCommit second = tr.commit().parent(first).message("R").add("R", "Q")
.create();
tr.update("refs/tags/t1", second);

Collection<PackFile> oldPacks = tr.getRepository().getObjectDatabase()
.getPacks();
assertEquals(0, oldPacks.size());
stats = gc.getStatistics();
assertEquals(11, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);

gc.setExpireAgeMillis(0);
fsTick();
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);

Iterator<PackFile> pIt = repo.getObjectDatabase().getPacks().iterator();
long c = pIt.next().getObjectCount();
if (c == 9)
assertEquals(2, pIt.next().getObjectCount());
else {
assertEquals(2, c);
assertEquals(9, pIt.next().getObjectCount());
}
}
}

+ 119
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBranchPrunedTest.java View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2012, Christian Halstrick <christian.halstrick@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.internal.storage.file;

import static org.junit.Assert.assertTrue;

import java.util.Collections;

import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.Merger;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.junit.Test;

public class GcBranchPrunedTest extends GcTestCase {

@Test
public void branch_historyNotPruned() throws Exception {
RevCommit tip = commitChain(10);
tr.branch("b").update(tip);
gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
do {
assertTrue(repo.hasObject(tip));
tr.parseBody(tip);
RevTree t = tip.getTree();
assertTrue(repo.hasObject(t));
assertTrue(repo.hasObject(tr.get(t, "a")));
tip = tip.getParentCount() > 0 ? tip.getParent(0) : null;
} while (tip != null);
}

@Test
public void deleteBranch_historyPruned() throws Exception {
RevCommit tip = commitChain(10);
tr.branch("b").update(tip);
RefUpdate update = repo.updateRef("refs/heads/b");
update.setForceUpdate(true);
update.delete();
gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
assertTrue(gc.getStatistics().numberOfLooseObjects == 0);
}

@Test
public void deleteMergedBranch_historyNotPruned() throws Exception {
RevCommit parent = tr.commit().create();
RevCommit b1Tip = tr.branch("b1").commit().parent(parent).add("x", "x")
.create();
RevCommit b2Tip = tr.branch("b2").commit().parent(parent).add("y", "y")
.create();

// merge b1Tip and b2Tip and update refs/heads/b1 to the merge commit
Merger merger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(repo);
merger.merge(b1Tip, b2Tip);
CommitBuilder cb = tr.commit();
cb.parent(b1Tip).parent(b2Tip);
cb.setTopLevelTree(merger.getResultTreeId());
RevCommit mergeCommit = cb.create();
RefUpdate u = repo.updateRef("refs/heads/b1");
u.setNewObjectId(mergeCommit);
u.update();

RefUpdate update = repo.updateRef("refs/heads/b2");
update.setForceUpdate(true);
update.delete();

gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
assertTrue(repo.hasObject(b2Tip));
}
}

+ 119
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2012, Christian Halstrick <christian.halstrick@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.internal.storage.file;

import static java.lang.Integer.valueOf;
import static org.junit.Assert.assertEquals;

import java.io.IOException;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.EmptyProgressMonitor;
import org.eclipse.jgit.revwalk.RevBlob;
import org.junit.Test;

public class GcConcurrentTest extends GcTestCase {
@Test
public void concurrentRepack() throws Exception {
final CyclicBarrier syncPoint = new CyclicBarrier(2);

class DoRepack extends EmptyProgressMonitor implements
Callable<Integer> {

public void beginTask(String title, int totalWork) {
if (title.equals(JGitText.get().writingObjects)) {
try {
syncPoint.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (BrokenBarrierException ignored) {
//
}
}
}

/** @return 0 for success, 1 in case of error when writing pack */
public Integer call() throws Exception {
try {
gc.setProgressMonitor(this);
gc.repack();
return valueOf(0);
} catch (IOException e) {
// leave the syncPoint in broken state so any awaiting
// threads and any threads that call await in the future get
// the BrokenBarrierException
Thread.currentThread().interrupt();
try {
syncPoint.await();
} catch (InterruptedException ignored) {
//
}
return valueOf(1);
}
}
}

RevBlob a = tr.blob("a");
tr.lightweightTag("t", a);

ExecutorService pool = Executors.newFixedThreadPool(2);
try {
DoRepack repack1 = new DoRepack();
DoRepack repack2 = new DoRepack();
Future<Integer> result1 = pool.submit(repack1);
Future<Integer> result2 = pool.submit(repack2);
assertEquals(0, result1.get().intValue() + result2.get().intValue());
} finally {
pool.shutdown();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}
}
}

+ 85
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcDirCacheSavesObjectsTest.java View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2012, Christian Halstrick <christian.halstrick@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.internal.storage.file;

import static org.junit.Assert.assertEquals;

import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.junit.Test;

public class GcDirCacheSavesObjectsTest extends GcTestCase {
@Test
public void testDirCacheSavesObjects() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
bb.commit().add("A", "A3"); // this new content in index should survive
stats = gc.getStatistics();
assertEquals(9, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.gc();
stats = gc.getStatistics();
assertEquals(1, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}

@Test
public void testDirCacheSavesObjectsWithPruneNow() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
bb.commit().add("A", "A3"); // this new content in index should survive
stats = gc.getStatistics();
assertEquals(9, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.setExpireAgeMillis(0);
fsTick();
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}
}

+ 104
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2012, Christian Halstrick <christian.halstrick@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.internal.storage.file;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.util.Iterator;

import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.junit.Test;

public class GcKeepFilesTest extends GcTestCase {
@Test
public void testKeepFiles() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
assertEquals(0, stats.numberOfPackFiles);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);

Iterator<PackFile> packIt = repo.getObjectDatabase().getPacks()
.iterator();
PackFile singlePack = packIt.next();
assertFalse(packIt.hasNext());
File keepFile = new File(singlePack.getPackFile().getPath() + ".keep");
assertFalse(keepFile.exists());
assertTrue(keepFile.createNewFile());
bb.commit().add("A", "A2").add("B", "B2").create();
stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(2, stats.numberOfPackFiles);

// check that no object is packed twice
Iterator<PackFile> packs = repo.getObjectDatabase().getPacks()
.iterator();
PackIndex ind1 = packs.next().getIndex();
assertEquals(4, ind1.getObjectCount());
PackIndex ind2 = packs.next().getIndex();
assertEquals(4, ind2.getObjectCount());
for (MutableEntry e: ind1)
if (ind2.hasObject(e.toObjectId()))
assertFalse(
"the following object is in both packfiles: "
+ e.toObjectId(),
ind2.hasObject(e.toObjectId()));
}
}

+ 180
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java View File

@@ -0,0 +1,180 @@
/*
* Copyright (C) 2012, Christian Halstrick <christian.halstrick@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.internal.storage.file;

import static java.lang.Integer.valueOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevBlob;
import org.junit.Test;

public class GcPackRefsTest extends GcTestCase {
@Test
public void looseRefPacked() throws Exception {
RevBlob a = tr.blob("a");
tr.lightweightTag("t", a);

gc.packRefs();
assertSame(repo.getRef("t").getStorage(), Storage.PACKED);
}

@Test
public void concurrentOnlyOneWritesPackedRefs() throws Exception {
RevBlob a = tr.blob("a");
tr.lightweightTag("t", a);

final CyclicBarrier syncPoint = new CyclicBarrier(2);

Callable<Integer> packRefs = new Callable<Integer>() {

/** @return 0 for success, 1 in case of error when writing pack */
public Integer call() throws Exception {
syncPoint.await();
try {
gc.packRefs();
return valueOf(0);
} catch (IOException e) {
return valueOf(1);
}
}
};
ExecutorService pool = Executors.newFixedThreadPool(2);
try {
Future<Integer> p1 = pool.submit(packRefs);
Future<Integer> p2 = pool.submit(packRefs);
assertEquals(1, p1.get().intValue() + p2.get().intValue());
} finally {
pool.shutdown();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}
}

@Test
public void whileRefLockedRefNotPackedNoError()
throws Exception {
RevBlob a = tr.blob("a");
tr.lightweightTag("t1", a);
tr.lightweightTag("t2", a);
LockFile refLock = new LockFile(new File(repo.getDirectory(),
"refs/tags/t1"), repo.getFS());
try {
refLock.lock();
gc.packRefs();
} finally {
refLock.unlock();
}

assertSame(repo.getRef("refs/tags/t1").getStorage(), Storage.LOOSE);
assertSame(repo.getRef("refs/tags/t2").getStorage(), Storage.PACKED);
}

@Test
public void whileRefUpdatedRefUpdateSucceeds()
throws Exception {
RevBlob a = tr.blob("a");
tr.lightweightTag("t", a);
final RevBlob b = tr.blob("b");

final CyclicBarrier refUpdateLockedRef = new CyclicBarrier(2);
final CyclicBarrier packRefsDone = new CyclicBarrier(2);
ExecutorService pool = Executors.newFixedThreadPool(2);
try {
Future<Result> result = pool.submit(new Callable<Result>() {

public Result call() throws Exception {
RefUpdate update = new RefDirectoryUpdate(
(RefDirectory) repo.getRefDatabase(),
repo.getRef("refs/tags/t")) {
@Override
public boolean isForceUpdate() {
try {
refUpdateLockedRef.await();
packRefsDone.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (BrokenBarrierException e) {
Thread.currentThread().interrupt();
}
return super.isForceUpdate();
}
};
update.setForceUpdate(true);
update.setNewObjectId(b);
return update.update();
}
});

pool.submit(new Callable<Void>() {
public Void call() throws Exception {
refUpdateLockedRef.await();
gc.packRefs();
packRefsDone.await();
return null;
}
});

assertSame(result.get(), Result.FORCED);

} finally {
pool.shutdownNow();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}

assertEquals(repo.getRef("refs/tags/t").getObjectId(), b);
}
}

+ 120
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2012, Christian Halstrick <christian.halstrick@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.internal.storage.file;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.Collections;
import java.util.Date;

import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.junit.Test;

public class GcPruneNonReferencedTest extends GcTestCase {
@Test
public void nonReferencedNonExpiredObject_notPruned() throws Exception {
RevBlob a = tr.blob("a");
gc.setExpire(new Date(lastModified(a)));
gc.prune(Collections.<ObjectId> emptySet());
assertTrue(repo.hasObject(a));
}

@Test
public void nonReferencedExpiredObject_pruned() throws Exception {
RevBlob a = tr.blob("a");
gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
assertFalse(repo.hasObject(a));
}

@Test
public void nonReferencedExpiredObjectTree_pruned() throws Exception {
RevBlob a = tr.blob("a");
RevTree t = tr.tree(tr.file("a", a));
gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
assertFalse(repo.hasObject(t));
assertFalse(repo.hasObject(a));
}

@Test
public void nonReferencedObjects_onlyExpiredPruned() throws Exception {
RevBlob a = tr.blob("a");
gc.setExpire(new Date(lastModified(a) + 1));

fsTick();
RevBlob b = tr.blob("b");

gc.prune(Collections.<ObjectId> emptySet());
assertFalse(repo.hasObject(a));
assertTrue(repo.hasObject(b));
}

@Test
public void testPackCommitsAndLooseOneWithPruneNow() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
tr.update("refs/heads/master", first);

stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
gc.setExpireAgeMillis(0);
fsTick();
gc.gc();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(2, stats.numberOfPackFiles);
}
}

+ 146
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2012, Christian Halstrick <christian.halstrick@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.internal.storage.file;

import static org.junit.Assert.assertEquals;

import java.io.File;
import java.util.Collections;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.util.FileUtils;
import org.junit.Test;

public class GcReflogTest extends GcTestCase {
@Test
public void testPruneNone() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
new File(repo.getDirectory(), Constants.LOGS + "/refs/heads/master")
.delete();
stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
tr.blob("x");
stats = gc.getStatistics();
assertEquals(9, stats.numberOfLooseObjects);
gc.prune(Collections.<ObjectId> emptySet());
stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
}

@Test
public void testPackRepoWithCorruptReflog() throws Exception {
// create a reflog entry "0000... 0000... foobar" by doing an initial
// refupdate for HEAD which points to a non-existing ref. The
// All-Projects repo of gerrit instances had such entries
RefUpdate ru = repo.updateRef(Constants.HEAD);
ru.link("refs/to/garbage");
tr.branch("refs/heads/master").commit().add("A", "A").add("B", "B")
.create();
// make sure HEAD exists
Git.wrap(repo).checkout().setName("refs/heads/master").call();
gc.gc();
}

@Test
public void testPackCommitsAndLooseOneNoReflog() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
tr.update("refs/heads/master", first);

stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);

FileUtils.delete(new File(repo.getDirectory(), "logs/HEAD"),
FileUtils.RETRY | FileUtils.SKIP_MISSING);
FileUtils.delete(
new File(repo.getDirectory(), "logs/refs/heads/master"),
FileUtils.RETRY | FileUtils.SKIP_MISSING);
gc.gc();

stats = gc.getStatistics();
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}

@Test
public void testPackCommitsAndLooseOneWithPruneNowNoReflog()
throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
tr.update("refs/heads/master", first);

stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);

FileUtils.delete(new File(repo.getDirectory(), "logs/HEAD"),
FileUtils.RETRY | FileUtils.SKIP_MISSING);
FileUtils.delete(
new File(repo.getDirectory(), "logs/refs/heads/master"),
FileUtils.RETRY | FileUtils.SKIP_MISSING);
gc.setExpireAgeMillis(0);
gc.gc();

stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
}
}

+ 78
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTagTest.java View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2012, Christian Halstrick <christian.halstrick@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.internal.storage.file;

import static org.junit.Assert.assertTrue;

import java.util.Collections;

import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevTag;
import org.junit.Test;

public class GcTagTest extends GcTestCase {
@Test
public void lightweightTag_objectNotPruned() throws Exception {
RevBlob a = tr.blob("a");
tr.lightweightTag("t", a);
gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
assertTrue(repo.hasObject(a));
}

@Test
public void annotatedTag_objectNotPruned() throws Exception {
RevBlob a = tr.blob("a");
RevTag t = tr.tag("t", a); // this doesn't create the refs/tags/t ref
tr.lightweightTag("t", t);

gc.setExpireAgeMillis(0);
fsTick();
gc.prune(Collections.<ObjectId> emptySet());
assertTrue(repo.hasObject(t));
assertTrue(repo.hasObject(a));
}
}

+ 113
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2012, Christian Halstrick <christian.halstrick@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.internal.storage.file;

import java.io.IOException;

import org.eclipse.jgit.internal.storage.file.GC.RepoStatistics;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.After;
import org.junit.Before;

public abstract class GcTestCase extends LocalDiskRepositoryTestCase {
protected TestRepository<FileRepository> tr;
protected FileRepository repo;
protected GC gc;
protected RepoStatistics stats;

@Before
public void setUp() throws Exception {
super.setUp();
repo = createWorkRepository();
tr = new TestRepository<FileRepository>((repo));
gc = new GC(repo);
}

@After
public void tearDown() throws Exception {
super.tearDown();
}

/**
* Create a chain of commits of given depth.
* <p>
* Each commit contains one file named "a" containing the index of the
* commit in the chain as its content. The created commit chain is
* referenced from any ref.
* <p>
* A chain of depth = N will create 3*N objects in Gits object database. For
* each depth level three objects are created: the commit object, the
* top-level tree object and a blob for the content of the file "a".
*
* @param depth
* the depth of the commit chain.
* @return the commit that is the tip of the commit chain
* @throws Exception
*/
protected RevCommit commitChain(int depth) throws Exception {
if (depth <= 0)
throw new IllegalArgumentException("Chain depth must be > 0");
CommitBuilder cb = tr.commit();
RevCommit tip;
do {
--depth;
tip = cb.add("a", "" + depth).message("" + depth).create();
cb = cb.child();
} while (depth > 0);
return tip;
}

protected long lastModified(AnyObjectId objectId) {
return repo.getObjectDatabase().fileFor(objectId).lastModified();
}

protected static void fsTick() throws InterruptedException, IOException {
RepositoryTestCase.fsTick(null);
}
}

Loading…
Cancel
Save