diff options
Diffstat (limited to 'org.eclipse.jgit.test/tst/org/eclipse/jgit')
17 files changed, 699 insertions, 7 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java index 87be813c85..7c1cbc37d6 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java @@ -12,6 +12,7 @@ package org.eclipse.jgit.api; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import java.util.List; @@ -160,6 +161,20 @@ public class BranchCommandTest extends RepositoryTestCase { - allBefore); } + @Test + public void testExistingNameInBothBranchesAndTags() throws Exception { + git.branchCreate().setName("test").call(); + git.tag().setName("test").call(); + + // existing name not allowed w/o force + assertThrows("Create branch with existing ref name should fail", + RefAlreadyExistsException.class, + () -> git.branchCreate().setName("test").call()); + + // existing name allowed with force option + git.branchCreate().setName("test").setForce(true).call(); + } + @Test(expected = InvalidRefNameException.class) public void testInvalidBranchHEAD() throws Exception { git.branchCreate().setName("HEAD").call(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java index 7f820b0433..16f7cd1eb0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java @@ -2221,7 +2221,7 @@ public class RebaseCommandTest extends RepositoryTestCase { checkoutBranch("refs/heads/master"); writeTrashFile(FILE1, "modified file1"); git.add().addFilepattern(FILE1).call(); - git.commit().setMessage("commit3").call(); + git.commit().setMessage("commit2").call(); // checkout topic branch / modify file0 checkoutBranch("refs/heads/topic"); @@ -2240,6 +2240,57 @@ public class RebaseCommandTest extends RepositoryTestCase { assertEquals(RepositoryState.SAFE, db.getRepositoryState()); } + @Test + public void testFastForwardRebaseWithAutoStashConflict() throws Exception { + // create file0, add and commit + db.getConfig().setBoolean(ConfigConstants.CONFIG_REBASE_SECTION, null, + ConfigConstants.CONFIG_KEY_AUTOSTASH, true); + writeTrashFile("file0", "file0"); + git.add().addFilepattern("file0").call(); + git.commit().setMessage("commit0").call(); + // create file1, add and commit + writeTrashFile(FILE1, "file1"); + git.add().addFilepattern(FILE1).call(); + RevCommit commit = git.commit().setMessage("commit1").call(); + + // create topic branch + createBranch(commit, "refs/heads/topic"); + + // checkout master branch / modify file1, add and commit + checkoutBranch("refs/heads/master"); + writeTrashFile(FILE1, "modified file1"); + git.add().addFilepattern(FILE1).call(); + RevCommit master = git.commit().setMessage("commit2").call(); + + // checkout topic branch / modify file0 and file1 + checkoutBranch("refs/heads/topic"); + writeTrashFile("file0", "unstaged modified file0"); + writeTrashFile(FILE1, "unstaged modified file1"); + + // rebase + assertEquals(Status.STASH_APPLY_CONFLICTS, + git.rebase().setUpstream("refs/heads/master").call() + .getStatus()); + checkFile(new File(db.getWorkTree(), "file0"), + "unstaged modified file0"); + checkFile(new File(db.getWorkTree(), FILE1), + "<<<<<<< HEAD\n" + + "modified file1\n" + + "=======\n" + + "unstaged modified file1\n" + + ">>>>>>> stash\n"); + // If there is a merge conflict, the index is not reset, and thus file0 + // is staged here. This is the same behavior as in C git. + String expected = "[file0, mode:100644, content:unstaged modified file0]" + + "[file1, mode:100644, stage:1, content:file1]" + + "[file1, mode:100644, stage:2, content:modified file1]" + + "[file1, mode:100644, stage:3, content:unstaged modified file1]"; + assertEquals(expected, indexState(CONTENT)); + assertEquals(RepositoryState.SAFE, db.getRepositoryState()); + assertEquals(master, db.resolve(Constants.HEAD)); + assertEquals(master, db.resolve("refs/heads/topic")); + } + private List<DiffEntry> getStashedDiff() throws AmbiguousObjectException, IncorrectObjectTypeException, IOException, MissingObjectException { ObjectId stashId = db.resolve("stash@{0}"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java index 1c7b8d13a8..4ebe994ef7 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java @@ -39,6 +39,7 @@ import org.junit.Test; * Test revert command */ public class RevertCommandTest extends RepositoryTestCase { + @Test public void testRevert() throws IOException, JGitInternalException, GitAPIException { @@ -90,7 +91,43 @@ public class RevertCommandTest extends RepositoryTestCase { assertTrue(reader.getLastEntry().getComment() .startsWith("revert: Revert \"")); } + } + + @Test + public void testRevertWithChangeId() + throws IOException, JGitInternalException, GitAPIException { + try (Git git = new Git(db)) { + writeTrashFile("a", "first line\nthird line\n"); + git.add().addFilepattern("a").call(); + git.commit().setMessage("create a").call(); + + writeTrashFile("a", "first line\nsecond line\nthird line\n"); + git.add().addFilepattern("a").call(); + RevCommit second = git.commit().setMessage("changed a").call(); + + writeTrashFile("a", + "first line\nsecond line\nthird line\nfourth line\n"); + git.add().addFilepattern("a").call(); + git.commit().setMessage("changed a again").call(); + + git.revert().include(second).setInsertChangeId(true).call(); + assertEquals(RepositoryState.SAFE, db.getRepositoryState()); + + checkFile(new File(db.getWorkTree(), "a"), + "first line\nthird line\nfourth line\n"); + Iterator<RevCommit> history = git.log().call().iterator(); + RevCommit revertCommit = history.next(); + String expectedMessage = "Revert \"changed a\"\n\n" + + "This reverts commit " + second.getId().getName() + ".\n"; + String commitMessage = revertCommit.getFullMessage(); + assertTrue(commitMessage.matches("^\\Q" + expectedMessage + + "\\E\nChange-Id: I[a-fA-F0-9]{40}\n$")); + assertEquals("changed a again", history.next().getFullMessage()); + assertEquals("changed a", history.next().getFullMessage()); + assertEquals("create a", history.next().getFullMessage()); + assertFalse(history.hasNext()); + } } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java index 5040a3b6ad..9f65ee2074 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java @@ -14,6 +14,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; @@ -31,6 +32,7 @@ import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.revwalk.RevBlob; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; @@ -135,6 +137,59 @@ public class CommitGraphWriterTest extends RepositoryTestCase { NB.decodeInt32(data, 56)); } + @Test + public void testProgressMonitor() throws Exception { + RevCommit root = commit(); + RevCommit a = commit(root); + RevCommit b = commit(root); + RevCommit tip = commit(a, b); + Set<ObjectId> wants = Collections.singleton(tip); + + NonNestedTasksProgressMonitor nonNested = new NonNestedTasksProgressMonitor(); + GraphCommits graphCommits = GraphCommits.fromWalk(nonNested, wants, + walk); + writer = new CommitGraphWriter(graphCommits, true); + writer.write(nonNested, os); + } + + private static class NonNestedTasksProgressMonitor + implements ProgressMonitor { + + boolean inTask; + + @Override + public void start(int totalTasks) { + // empty + } + + @Override + public void beginTask(String title, int totalWork) { + assertFalse("Previous monitoring task is not closed", inTask); + inTask = true; + } + + @Override + public void update(int completed) { + // empty + } + + @Override + public void endTask() { + assertTrue("Closing task that wasn't started", inTask); + inTask = false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public void showDuration(boolean enabled) { + // empty + } + } + static HashSet<String> changedPathStrings(byte[] data) { int oidf_offset = -1; int bidx_offset = -1; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java index 48f6e06385..8b27b829b2 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java @@ -61,6 +61,7 @@ public abstract class GcTestCase extends LocalDiskRepositoryTestCase { * the depth of the commit chain. * @return the commit that is the tip of the commit chain * @throws Exception + * if an error occurred */ protected RevCommit commitChain(int depth) throws Exception { if (depth <= 0) @@ -93,6 +94,7 @@ public abstract class GcTestCase extends LocalDiskRepositoryTestCase { * number of files added per commit * @return the commit that is the tip of the commit chain * @throws Exception + * if an error occurred */ protected RevCommit commitChain(int depth, int width) throws Exception { if (depth <= 0) { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java index 67bba18e2b..24bdc4a97a 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java @@ -66,7 +66,9 @@ public abstract class PackIndexTestCase extends RepositoryTestCase { * Verify CRC32 support. * * @throws MissingObjectException + * object is missing in the underlying index * @throws UnsupportedOperationException + * the index doesn't have CRC */ public abstract void testCRC32() throws MissingObjectException, UnsupportedOperationException; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java index 619e585a90..2bafde65d3 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java @@ -51,6 +51,7 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.StringUtils; import org.junit.Before; import org.junit.Test; @@ -1349,6 +1350,18 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase { assertEquals(Storage.LOOSE, ref.getStorage()); } + @Test + public void testCommonRefPrefix() { + assertEquals("", StringUtils.commonPrefix()); + assertEquals("HEAD", StringUtils.commonPrefix("HEAD")); + assertEquals("", StringUtils.commonPrefix("HEAD", "")); + assertEquals("", StringUtils.commonPrefix("HEAD", "refs/heads/")); + assertEquals("refs/heads/", + StringUtils.commonPrefix("refs/heads/master", "refs/heads/")); + assertEquals("refs/heads/", + StringUtils.commonPrefix("refs/heads/", "refs/heads/main")); + } + void writePackedRef(String name, AnyObjectId id) throws IOException { writePackedRefs(id.name() + " " + name + "\n"); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BitmapIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BitmapIndexTest.java new file mode 100644 index 0000000000..ee4fa8bcc7 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BitmapIndexTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024, Google Inc. and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.lib; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.eclipse.jgit.internal.storage.file.FileRepository; +import org.eclipse.jgit.internal.storage.file.GC; +import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.junit.Before; +import org.junit.Test; + +public class BitmapIndexTest extends LocalDiskRepositoryTestCase { + + private static final String MAIN = "refs/heads/main"; + + TestRepository<FileRepository> repo; + + RevCommit tipWithBitmap; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + FileRepository db = createWorkRepository(); + repo = new TestRepository<>(db); + + RevCommit base = repo.commit().create(); + RevCommit one = repo.commit().parent(base).create(); + tipWithBitmap = repo.commit().parent(one).create(); + repo.update(MAIN, tipWithBitmap); + + GC gc = new GC(repo.getRepository()); + gc.setAuto(false); + gc.gc().get(); + + assertNotNull(repo.getRevWalk().getObjectReader().getBitmapIndex()); + } + + + @Test + public void listener_getBitmap_counted() throws Exception { + try (RevWalk rw = repo.getRevWalk(); + ObjectReader or = rw.getObjectReader()) { + BitmapLookupCounter counter = new BitmapLookupCounter(); + BitmapIndex bitmapIndex = or.getBitmapIndex(); + bitmapIndex.addBitmapLookupListener(counter); + + bitmapIndex.getBitmap(tipWithBitmap); + bitmapIndex.getBitmap(tipWithBitmap); + bitmapIndex.getBitmap(ObjectId.zeroId()); + + assertEquals(2, counter.bitmapFound); + assertEquals(1, counter.bitmapNotFound); + } + } + + private static class BitmapLookupCounter + implements BitmapIndex.BitmapLookupListener { + int bitmapFound = 0; + + int bitmapNotFound = 0; + + @Override + public void onBitmapFound(AnyObjectId oid) { + bitmapFound += 1; + } + + @Override + public void onBitmapNotFound(AnyObjectId oid) { + bitmapNotFound += 1; + } + } +}
\ No newline at end of file diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java index 36cf77bb01..0c0257df90 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java @@ -507,6 +507,35 @@ public class ConfigTest { } @Test + public void testRemoveBranchSection() throws ConfigInvalidException { + Config c = parse("" // + + "[branch \"keep\"]\n" + + " merge = master.branch.to.keep.in.the.file\n" + + "\n" + + "[branch \"remove\"]\n" + + " merge = this.will.get.deleted\n" + + " remote = origin-for-some-long-gone-place\n" + + "\n" + + "\n" + + "[core-section-not-to-remove-in-test]\n" + + " packedGitLimit = 14\n" + + "\n" + + "[other]\n" + + " foo = bar\n"); + assertFalse(c.removeSection("branch", "does.not.exist")); + assertTrue(c.removeSection("branch", "remove")); + assertEquals("" // + + "[branch \"keep\"]\n" + + " merge = master.branch.to.keep.in.the.file\n" + + "\n" + + "[core-section-not-to-remove-in-test]\n" + + " packedGitLimit = 14\n" + + "\n" + + "[other]\n" + + " foo = bar\n", c.toText()); + } + + @Test public void testUnsetBranchSection() throws ConfigInvalidException { Config c = parse("" // + "[branch \"keep\"]\n" @@ -516,8 +545,12 @@ public class ConfigTest { + " merge = this.will.get.deleted\n" + " remote = origin-for-some-long-gone-place\n" + "\n" + + "\n" + "[core-section-not-to-remove-in-test]\n" - + " packedGitLimit = 14\n"); + + " packedGitLimit = 14\n" + + "\n" + + "[other]\n" + + " foo = bar\n"); c.unsetSection("branch", "does.not.exist"); c.unsetSection("branch", "remove"); assertEquals("" // @@ -525,7 +558,10 @@ public class ConfigTest { + " merge = master.branch.to.keep.in.the.file\n" + "\n" + "[core-section-not-to-remove-in-test]\n" - + " packedGitLimit = 14\n", c.toText()); + + " packedGitLimit = 14\n" + + "\n" + + "[other]\n" + + " foo = bar\n", c.toText()); } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java index 92d47c2966..2aac15bbb6 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java @@ -496,6 +496,14 @@ public class PatchApplierTest { Result result = applyPatch(); verifyChange(result, "x_last_rm_nl"); } + + @Test + public void testVeryLongFile() throws Exception { + init("very_long_file"); + + Result result = applyPatch(); + verifyChange(result, "very_long_file"); + } } public static class WithWorktree extends Base { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java index 01f6a3a0a0..303aedcd00 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java @@ -318,6 +318,57 @@ public class FooterLineTest extends RepositoryTestCase { assertFalse("not CC", line.matches(FooterKey.CC)); } + @Test + public void testMultilineFooters() { + String msg = buildMessage("subject\n\nbody of commit\n" + + "Not-A-Footer-Line: this line must not be read as a footer\n" + + "\n" // paragraph break, now footers appear in final block + + "Notes: The change must not be merged until dependency ABC is\n" + + " updated.\n" + + "CC: <some.mailing.list@example.com>\n" + + "not really a footer line but we'll skip it anyway\n" + + "Acked-by: Some Reviewer <sr@example.com>\n"); + List<FooterLine> footers = FooterLine.fromMessage(msg); + FooterLine f; + + assertNotNull(footers); + assertEquals(3, footers.size()); + + f = footers.get(0); + assertEquals("Notes", f.getKey()); + assertEquals( + "The change must not be merged until dependency ABC is updated.", + f.getValue()); + + f = footers.get(1); + assertEquals("CC", f.getKey()); + assertEquals("<some.mailing.list@example.com>", f.getValue()); + + f = footers.get(2); + assertEquals("Acked-by", f.getKey()); + assertEquals("Some Reviewer <sr@example.com>", f.getValue()); + } + + @Test + public void testMultilineFooters_multipleWhitespaceAreAllowed() { + String msg = buildMessage("subject\n\nbody of commit\n" + + "Not-A-Footer-Line: this line must not be read as a footer\n" + + "\n" // paragraph break, now footers appear in final block + + "Notes: The change must not be merged until dependency ABC is\n" + + " updated.\n"); + List<FooterLine> footers = FooterLine.fromMessage(msg); + FooterLine f; + + assertNotNull(footers); + assertEquals(1, footers.size()); + + f = footers.get(0); + assertEquals("Notes", f.getKey()); + assertEquals( + "The change must not be merged until dependency ABC is updated.", + f.getValue()); + } + private String buildMessage(String msg) { StringBuilder buf = new StringBuilder(); buf.append("tree " + ObjectId.zeroId().name() + "\n"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/test/resources/SampleDataRepositoryTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/test/resources/SampleDataRepositoryTestCase.java index 578128326f..4f5e35f129 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/test/resources/SampleDataRepositoryTestCase.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/test/resources/SampleDataRepositoryTestCase.java @@ -35,6 +35,7 @@ public abstract class SampleDataRepositoryTestCase extends RepositoryTestCase { * @param repo * test repository to receive packfile copies * @throws IOException + * an error occurred */ public static void copyCGitTestPacks(FileRepository repo) throws IOException { final String[] packs = { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RequestValidatorTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RequestValidatorTestCase.java index cc910b3b9f..f589a399a4 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RequestValidatorTestCase.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RequestValidatorTestCase.java @@ -70,20 +70,30 @@ public abstract class RequestValidatorTestCase { } /** + * Check if the validator accepts a reachable commit + * * @return true if a commit reachable from a visible tip (but not directly * the tip) is valid */ protected abstract boolean isReachableCommitValid(); - /** @return true if a commit not reachable from any tip is valid */ + /** + * Check if the validator accepts an unreachable commit + * + * @return true if a commit not reachable from any tip is valid + **/ protected abstract boolean isUnreachableCommitValid(); /** + * Check if the validator accepts a previously advertised tip + * * @return true if the commit directly pointed by an advertised ref is valid */ protected abstract boolean isAdvertisedTipValid(); /** + * Check if the validator accepts a previous unadvertised tip + * * @return true if the object directly pointed by a non-advertised ref is * valid */ @@ -92,17 +102,23 @@ public abstract class RequestValidatorTestCase { // UploadPack doesn't allow to ask for blobs when there is no // bitmap. Test both cases separately. /** + * Check if the validator accepts a reachable blob (repo with bitmaps) + * * @return true if a reachable blob is valid (and the repo has bitmaps) */ protected abstract boolean isReachableBlobValid_withBitmaps(); /** + * Check if the validator accepts a reachable blob (repo without bitmaps) + * * @return true if a reachable blob is valid (and the repo does NOT have * bitmaps) */ protected abstract boolean isReachableBlobValid_withoutBitmaps(); /** + * Check if the validator accepts an unreachable blob + * * @return true if a blob unreachable from any tip is valid */ protected abstract boolean isUnreachableBlobValid(); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java index f9687f9f82..026492f7b6 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java @@ -2215,7 +2215,8 @@ public class UploadPackTest { uri = testProtocol.register(ctx, server); - TestProtocol.setFetchConfig(new FetchConfig(true, MAX_HAVES, true)); + TestProtocol.setFetchConfig(new FetchConfig(true, MAX_HAVES, + /* useNegotiationTip= */true)); try (Transport tn = testProtocol.open(uri, clientRepo.getRepository(), "server")) { @@ -2335,7 +2336,8 @@ public class UploadPackTest { }, null); uri = testProtocol.register(ctx, server); - TestProtocol.setFetchConfig(new FetchConfig(true, MAX_HAVES, true)); + TestProtocol.setFetchConfig(new FetchConfig(true, MAX_HAVES, + /* useNegotiationTip= */true)); try (Transport tn = testProtocol.open(uri, clientRepo.getRepository(), "server")) { @@ -2362,6 +2364,67 @@ public class UploadPackTest { } } + /** + * <pre> + * remote: + * foo <- foofoo <-- branchFoo + * bar <- barbar <-- branchBar + * + * client: + * none + * + * fetch(branchFoo) should not send have and should get only branchFoo back + * </pre> + */ + @Test + public void testNegotiationTipDoesNotDoFullClone() throws Exception { + RevCommit fooParent = remote.commit().message("foo").create(); + RevCommit fooChild = remote.commit().message("foofoo").parent(fooParent) + .create(); + RevCommit barParent = remote.commit().message("bar").create(); + RevCommit barChild = remote.commit().message("barbar").parent(barParent) + .create(); + + // Remote has branchFoo at fooChild and branchBar at barChild + remote.update("branchFoo", fooChild); + remote.update("branchBar", barChild); + + AtomicReference<UploadPack> uploadPack = new AtomicReference<>(); + CountHavesPreUploadHook countHavesHook = new CountHavesPreUploadHook(); + + // Client does not have branchFoo & branchBar + try (TestRepository<InMemoryRepository> clientRepo = new TestRepository<>( + client)) { + testProtocol = new TestProtocol<>((Object req, Repository db) -> { + UploadPack up = new UploadPack(db); + up.setPreUploadHook(countHavesHook); + uploadPack.set(up); + return up; + }, null); + + uri = testProtocol.register(ctx, server); + + TestProtocol.setFetchConfig(new FetchConfig(true, MAX_HAVES, + /* useNegotiationTip= */true)); + try (Transport tn = testProtocol.open(uri, + clientRepo.getRepository(), "server")) { + + tn.fetch(NullProgressMonitor.INSTANCE, + Collections.singletonList( + new RefSpec("refs/heads/branchFoo")), + "branchFoo"); + } + } + + assertTrue(client.getObjectDatabase().has(fooParent.toObjectId())); + assertTrue(client.getObjectDatabase().has(fooChild.toObjectId())); + assertFalse(client.getObjectDatabase().has(barParent.toObjectId())); + assertFalse(client.getObjectDatabase().has(barChild.toObjectId())); + + assertEquals(0, uploadPack.get().getStatistics().getHaves()); + assertTrue(countHavesHook.havesSentDuringNegotiation.isEmpty()); + } + private static class CountHavesPreUploadHook implements PreUploadHook { Set<ObjectId> havesSentDuringNegotiation = new HashSet<>(); @@ -2937,7 +3000,70 @@ public class UploadPackTest { assertThat(pckIn.readString(), is("packfile")); parsePack(recvStream); assertTrue(client.getObjectDatabase().has(one.toObjectId())); - assertEquals(1, ((RefCallsCountingRepository)server).numRefCalls()); + assertEquals(0, ((RefCallsCountingRepository)server).numRefCalls()); + } + + /* + * Invokes UploadPack with specified protocol version and sends it the given + * lines, and returns UploadPack statistics (use uploadPackSetup to get the + * output stream) + */ + private PackStatistics uploadPackV2SetupStats(String... inputLines) + throws Exception { + + ByteArrayInputStream send = linesAsInputStream(inputLines); + String version = TransferConfig.ProtocolVersion.V2.version(); + server.getConfig().setString(ConfigConstants.CONFIG_PROTOCOL_SECTION, + null, ConfigConstants.CONFIG_KEY_VERSION, version); + try (UploadPack up = new UploadPack(server)) { + up.setExtraParameters(Sets.of("version=".concat(version))); + + ByteArrayOutputStream recv = new ByteArrayOutputStream(); + up.upload(send, recv, null); + return up.getStatistics(); + } + } + + @Test + public void testUseWantedRefsAsAdvertisedSetV2_onlyWantedRefs() + throws Exception { + server = new RefCallsCountingRepository( + new DfsRepositoryDescription("server")); + remote = new TestRepository<>(server); + RevCommit one = remote.commit().message("1").create(); + RevCommit two = remote.commit().message("2").create(); + RevCommit three = remote.commit().message("3").create(); + remote.update("one", one); + remote.update("two", two); + remote.update("three", three); + server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", + true); + PackStatistics packStats = uploadPackV2SetupStats("command=fetch\n", + PacketLineIn.delimiter(), "want-ref refs/heads/one\n", + "want-ref refs/heads/two\n", "done\n", PacketLineIn.end()); + assertEquals("only wanted-refs", 2, packStats.getAdvertised()); + assertEquals(0, ((RefCallsCountingRepository) server).numRefCalls()); + } + + @Test + public void testUseWantedRefsAsAdvertisedSetV2_withWantId() + throws Exception { + server = new RefCallsCountingRepository( + new DfsRepositoryDescription("server")); + remote = new TestRepository<>(server); + RevCommit one = remote.commit().message("1").create(); + RevCommit two = remote.commit().message("2").create(); + RevCommit three = remote.commit().message("3").create(); + remote.update("one", one); + remote.update("two", two); + remote.update("three", three); + server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", + true); + PackStatistics packStats = uploadPackV2SetupStats("command=fetch\n", + PacketLineIn.delimiter(), "want-ref refs/heads/one\n", + "want " + one.getName() + "\n", "done\n", PacketLineIn.end()); + assertEquals("all refs", 3, packStats.getAdvertised()); + assertEquals(1, ((RefCallsCountingRepository) server).numRefCalls()); } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java index 171d80c3da..51236e1cc0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java @@ -13,6 +13,7 @@ package org.eclipse.jgit.util; import static java.time.Instant.EPOCH; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeNoException; import static org.junit.Assume.assumeTrue; @@ -233,4 +234,26 @@ public class FSTest { assertFalse(RepositoryCache.FileKey .isGitRepository(new File("repo.git"), FS.DETECTED)); } + + @Test + public void testSearchPath() throws IOException { + File f1 = new File(trash, "file1"); + FileUtils.createNewFile(f1); + f1.setExecutable(true); + File f2 = new File(trash, "file2"); + FileUtils.createNewFile(f2); + assertEquals(f1, FS.searchPath(trash.getAbsolutePath(), "file1")); + assertNull(FS.searchPath(trash.getAbsolutePath(), "file2")); + } + + @Test + public void testSearchPathEmptyPath() { + assertNull(FS.searchPath("", "file1")); + assertNull(FS.searchPath(File.pathSeparator, "file1")); + assertNull(FS.searchPath(File.pathSeparator + File.pathSeparator, + "file1")); + assertNull(FS.searchPath( + " " + File.pathSeparator + " " + File.pathSeparator + " \t", + "file1")); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java index aa7247e105..015da164c3 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java @@ -153,4 +153,23 @@ public class StringUtilsTest { () -> StringUtils.parseLongWithSuffix("8000000000000000000G", false)); } + + @Test + public void testCommonPrefix() { + assertEquals("", StringUtils.commonPrefix((String[]) null)); + assertEquals("", StringUtils.commonPrefix(new String[] {})); + assertEquals("", StringUtils.commonPrefix(new String[] { null })); + assertEquals("", StringUtils.commonPrefix(null, null)); + assertEquals("", StringUtils.commonPrefix("", "")); + assertEquals("", StringUtils.commonPrefix(null, "")); + assertEquals("", StringUtils.commonPrefix("abcd", null, null)); + assertEquals("", StringUtils.commonPrefix(null, null, "abcd")); + assertEquals("", StringUtils.commonPrefix("", "abcd")); + assertEquals("", StringUtils.commonPrefix("abcd", "efgh")); + assertEquals("abcd", StringUtils.commonPrefix("abcd")); + assertEquals("ab", StringUtils.commonPrefix("abcd", "ab")); + assertEquals("abcd", StringUtils.commonPrefix("abcd", "abcdefgh")); + assertEquals("foo bar ", + StringUtils.commonPrefix("foo bar 42", "foo bar 24")); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/ByteBufferInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/ByteBufferInputStreamTest.java new file mode 100644 index 0000000000..ec9f96ed96 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/ByteBufferInputStreamTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2023, SAP SE and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.util.io; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Arrays; + +import org.eclipse.jgit.internal.JGitText; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ByteBufferInputStreamTest { + + private static final byte data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }; + + private ByteBuffer buf; + + private ByteBufferInputStream is; + + @Before + public void setup() { + buf = ByteBuffer.wrap(data); + is = new ByteBufferInputStream(buf); + } + + @After + public void tearDown() { + is.close(); + } + + @Test + public void testRead() throws IOException { + assertEquals(0x00, is.read()); + assertEquals(0x01, is.read()); + assertEquals(0x02, is.read()); + assertEquals(0x03, is.read()); + assertEquals(0x04, is.read()); + assertEquals(0x05, is.read()); + assertEquals(0x06, is.read()); + assertEquals(0x07, is.read()); + assertEquals(0x08, is.read()); + assertEquals(0x09, is.read()); + assertEquals(0x0A, is.read()); + assertEquals(0x0B, is.read()); + assertEquals(0x0C, is.read()); + assertEquals(0x0D, is.read()); + assertEquals(0x0E, is.read()); + assertEquals(0x0F, is.read()); + assertEquals(-1, is.read()); + } + + @Test + public void testReadMultiple() throws IOException { + byte[] x = new byte[5]; + int n = is.read(x); + assertEquals(5, n); + assertArrayEquals(new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }, x); + } + + @Test + public void testReadMultipleOffset() throws IOException { + byte[] x = new byte[7]; + int n = is.read(x, 4, 3); + assertEquals(3, n); + assertArrayEquals( + new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02 }, + x); + } + + @Test + public void testReadAll() throws IOException { + byte[] x = is.readAllBytes(); + assertEquals(16, x.length); + assertArrayEquals(data, x); + } + + @Test + public void testMarkReset() throws IOException { + byte[] x = new byte[5]; + int n = is.read(x); + assertEquals(11, is.available()); + assertTrue(is.markSupported()); + is.mark(is.available()); + is.reset(); + byte[] y = new byte[5]; + int m = is.read(y); + assertEquals(n, m); + assertArrayEquals(new byte[] { 0x05, 0x06, 0x07, 0x08, 0x09 }, y); + } + + @Test + public void testClosed() { + is.close(); + Exception e = assertThrows(IOException.class, () -> is.read()); + assertEquals(JGitText.get().inputStreamClosed, e.getMessage()); + } + + @Test + public void testReadNBytes() throws IOException { + byte[] x = is.readNBytes(4); + assertArrayEquals(new byte[] { 0x00, 0x01, 0x02, 0x03 }, x); + } + + @Test + public void testReadNBytesOffset() throws IOException { + byte[] x = new byte[10]; + Arrays.fill(x, (byte) 0x0F); + is.readNBytes(x, 3, 4); + assertArrayEquals(new byte[] { 0x0F, 0x0F, 0x0F, 0x00, 0x01, 0x02, 0x03, + 0x0F, 0x0F, 0x0F }, x); + } + + @Test + public void testRead0() throws IOException { + byte[] x = new byte[7]; + int n = is.read(x, 4, 0); + assertEquals(0, n); + + is.readAllBytes(); + n = is.read(x, 4, 3); + assertEquals(-1, n); + } + + @Test + public void testSkip() throws IOException { + assertEquals(15, is.skip(15)); + assertEquals(0x0F, is.read()); + assertEquals(-1, is.read()); + } + + @Test + public void testSkip0() throws IOException { + assertEquals(0, is.skip(0)); + assertEquals(0x00, is.read()); + } +} |