--- /dev/null
+/*
+ * Copyright (C) 2023, Google Inc.
+ *
+ * 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.revwalk;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.List;
+
+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.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BitmapIndex;
+import org.eclipse.jgit.lib.BitmapIndex.Bitmap;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapLookupListener;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.junit.Before;
+import org.junit.Test;
+
+public class BitmapWalkerTest 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());
+ }
+
+ private static class BitmapWalkCounter implements BitmapLookupListener {
+ int withBitmap;
+
+ int withoutBitmap;
+
+ @Override
+ public void onBitmapFound(AnyObjectId oid) {
+ withBitmap += 1;
+ }
+
+ @Override
+ public void onBitmapNotFound(AnyObjectId oid) {
+ withoutBitmap += 1;
+ }
+ }
+
+ @Test
+ public void counters_bitmapAtTip() throws Exception {
+ try (RevWalk rw = repo.getRevWalk();
+ ObjectReader or = rw.getObjectReader()) {
+ BitmapWalkCounter counter = new BitmapWalkCounter();
+ BitmapIndex bitmapIndex = or.getBitmapIndex();
+ bitmapIndex.addBitmapLookupListener(counter);
+ BitmapWalker bw = new BitmapWalker(rw.toObjectWalkWithSameObjects(),
+ bitmapIndex, NullProgressMonitor.INSTANCE);
+ BitmapBuilder bitmap = bw.findObjects(List.of(tipWithBitmap), null,
+ true);
+ // First commit has a tree, so in total 4 objects
+ assertEquals(4, bitmap.cardinality());
+ assertEquals(1, counter.withBitmap);
+ assertEquals(0, counter.withoutBitmap);
+ assertEquals(0, bw.getCountOfBitmapIndexMisses());
+ }
+ }
+
+ @Test
+ public void counters_bitmapAfterAStep() throws Exception {
+ System.out.println("Old tip: " + tipWithBitmap);
+ RevCommit newTip = repo.commit().parent(tipWithBitmap).create();
+ System.out.println("New tip: " + newTip);
+ try (RevWalk rw = repo.getRevWalk();
+ ObjectReader or = rw.getObjectReader()) {
+ BitmapWalkCounter counter = new BitmapWalkCounter();
+ BitmapIndex bitmapIndex = or.getBitmapIndex();
+ bitmapIndex.addBitmapLookupListener(counter);
+ BitmapWalker bw = new BitmapWalker(rw.toObjectWalkWithSameObjects(),
+ bitmapIndex, NullProgressMonitor.INSTANCE);
+
+ bw.findObjects(List.of(newTip), null, true);
+
+ assertEquals(1, counter.withBitmap);
+ // It checks bitmap before marking as interesting, and again in the
+ // walk
+ assertEquals(2, counter.withoutBitmap);
+ assertEquals(1, bw.getCountOfBitmapIndexMisses());
+ }
+ }
+
+ @Test
+ public void counters_bitmapAfterThreeSteps() throws Exception {
+ RevCommit newOne = repo.commit().parent(tipWithBitmap).create();
+ RevCommit newTwo = repo.commit().parent(newOne).create();
+ RevCommit newTip = repo.commit().parent(newTwo).create();
+
+ try (RevWalk rw = repo.getRevWalk();
+ ObjectReader or = rw.getObjectReader()) {
+ BitmapWalkCounter counter = new BitmapWalkCounter();
+ BitmapIndex bitmapIndex = or.getBitmapIndex();
+ bitmapIndex.addBitmapLookupListener(counter);
+ BitmapWalker bw = new BitmapWalker(rw.toObjectWalkWithSameObjects(),
+ bitmapIndex, NullProgressMonitor.INSTANCE);
+
+ bw.findObjects(List.of(newTip), null, true);
+
+ assertEquals(1, counter.withBitmap);
+ assertEquals(4, counter.withoutBitmap);
+ assertEquals(3, bw.getCountOfBitmapIndexMisses());
+ }
+ }
+
+ @Test
+ public void counters_bitmapAfterThreeStepsWithSeen() throws Exception {
+ RevCommit newOne = repo.commit().parent(tipWithBitmap).create();
+ RevCommit newTwo = repo.commit().parent(newOne).create();
+ RevCommit newTip = repo.commit().parent(newTwo).create();
+
+ try (RevWalk rw = repo.getRevWalk();
+ ObjectReader or = rw.getObjectReader()) {
+ BitmapIndex bitmapIndex = or.getBitmapIndex();
+ Bitmap seen = bitmapIndex.getBitmap(tipWithBitmap);
+ BitmapBuilder seenBB = bitmapIndex.newBitmapBuilder().or(seen);
+ BitmapWalkCounter counter = new BitmapWalkCounter();
+ bitmapIndex.addBitmapLookupListener(counter);
+ BitmapWalker bw = new BitmapWalker(rw.toObjectWalkWithSameObjects(),
+ bitmapIndex, NullProgressMonitor.INSTANCE);
+
+ bw.findObjects(List.of(newTip), seenBB, true);
+
+ assertEquals(0, counter.withBitmap);
+ assertEquals(4, counter.withoutBitmap);
+ assertEquals(3, bw.getCountOfBitmapIndexMisses());
+ }
+ }
+}