12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148 |
- /*
- * Copyright (C) 2017, 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.internal.storage.reftable;
-
- import static java.nio.charset.StandardCharsets.UTF_8;
- import static org.eclipse.jgit.lib.Constants.HEAD;
- import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
- import static org.eclipse.jgit.lib.Constants.R_HEADS;
- import static org.eclipse.jgit.lib.Ref.Storage.NEW;
- import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
- import static org.hamcrest.CoreMatchers.containsString;
- import static org.hamcrest.MatcherAssert.assertThat;
- import static org.junit.Assert.assertEquals;
- import static org.junit.Assert.assertFalse;
- import static org.junit.Assert.assertNotNull;
- import static org.junit.Assert.assertNull;
- import static org.junit.Assert.assertSame;
- import static org.junit.Assert.assertThrows;
- import static org.junit.Assert.assertTrue;
- import static org.junit.Assert.fail;
-
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.List;
- import java.util.concurrent.locks.ReentrantLock;
- import java.util.stream.Collectors;
-
- import org.eclipse.jgit.internal.JGitText;
- import org.eclipse.jgit.internal.storage.io.BlockSource;
- import org.eclipse.jgit.internal.storage.reftable.ReftableWriter.Stats;
- import org.eclipse.jgit.lib.ObjectId;
- import org.eclipse.jgit.lib.ObjectIdRef;
- import org.eclipse.jgit.lib.PersonIdent;
- import org.eclipse.jgit.lib.Ref;
- import org.eclipse.jgit.lib.ReflogEntry;
- import org.eclipse.jgit.lib.SymbolicRef;
- import org.hamcrest.Matchers;
- import org.junit.Test;
-
- public class ReftableTest {
- private static final byte[] LAST_UTF8_CHAR = new byte[] {
- (byte)0x10,
- (byte)0xFF,
- (byte)0xFF};
-
- private static final String MASTER = "refs/heads/master";
- private static final String NEXT = "refs/heads/next";
- private static final String AFTER_NEXT = "refs/heads/nextnext";
- private static final String LAST = "refs/heads/nextnextnext";
- private static final String NOT_REF_HEADS = "refs/zzz/zzz";
- private static final String V1_0 = "refs/tags/v1.0";
-
- private Stats stats;
-
- @Test
- public void emptyTable() throws IOException {
- byte[] table = write();
- assertEquals(92 /* header, footer */, table.length);
- assertEquals('R', table[0]);
- assertEquals('E', table[1]);
- assertEquals('F', table[2]);
- assertEquals('T', table[3]);
- assertEquals(0x01, table[4]);
- assertTrue(ReftableConstants.isFileHeaderMagic(table, 0, 8));
- assertTrue(ReftableConstants.isFileHeaderMagic(table, 24, 92));
-
- Reftable t = read(table);
- try (RefCursor rc = t.allRefs()) {
- assertFalse(rc.next());
- }
- try (RefCursor rc = t.seekRef(HEAD)) {
- assertFalse(rc.next());
- }
- try (RefCursor rc = t.seekRefsWithPrefix(R_HEADS)) {
- assertFalse(rc.next());
- }
- try (LogCursor rc = t.allLogs()) {
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void emptyVirtualTableFromRefs() throws IOException {
- Reftable t = Reftable.from(Collections.emptyList());
- try (RefCursor rc = t.allRefs()) {
- assertFalse(rc.next());
- }
- try (RefCursor rc = t.seekRef(HEAD)) {
- assertFalse(rc.next());
- }
- try (LogCursor rc = t.allLogs()) {
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void estimateCurrentBytesOneRef() throws IOException {
- Ref exp = ref(MASTER, 1);
- int expBytes = 24 + 4 + 5 + 4 + MASTER.length() + 20 + 68;
-
- byte[] table;
- ReftableConfig cfg = new ReftableConfig();
- cfg.setIndexObjects(false);
- try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) {
- ReftableWriter writer = new ReftableWriter(buf).setConfig(cfg);
- writer.begin();
- assertEquals(92, writer.estimateTotalBytes());
- writer.writeRef(exp);
- assertEquals(expBytes, writer.estimateTotalBytes());
- writer.finish();
- table = buf.toByteArray();
- }
- assertEquals(expBytes, table.length);
- }
-
- @Test
- public void estimateCurrentBytesWithIndex() throws IOException {
- List<Ref> refs = new ArrayList<>();
- for (int i = 1; i <= 5670; i++) {
- @SuppressWarnings("boxing")
- Ref ref = ref(String.format("refs/heads/%04d", i), i);
- refs.add(ref);
- }
-
- ReftableConfig cfg = new ReftableConfig();
- cfg.setIndexObjects(false);
- cfg.setMaxIndexLevels(1);
-
- int expBytes = 147860;
- byte[] table;
- try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) {
- ReftableWriter writer = new ReftableWriter(buf).setConfig(cfg);
- writer.begin();
- writer.sortAndWriteRefs(refs);
- assertEquals(expBytes, writer.estimateTotalBytes());
- writer.finish();
- stats = writer.getStats();
- table = buf.toByteArray();
- }
- assertEquals(1, stats.refIndexLevels());
- assertEquals(expBytes, table.length);
- }
-
- @Test
- public void hasObjMapRefs() throws IOException {
- ArrayList<Ref> refs = new ArrayList<>();
- refs.add(ref(MASTER, 1));
- byte[] table = write(refs);
- ReftableReader t = read(table);
- assertTrue(t.hasObjectMap());
- }
-
- @Test
- public void hasObjMapRefsSmallTable() throws IOException {
- ArrayList<Ref> refs = new ArrayList<>();
- ReftableConfig cfg = new ReftableConfig();
- cfg.setIndexObjects(false);
- refs.add(ref(MASTER, 1));
- byte[] table = write(refs);
- ReftableReader t = read(table);
- assertTrue(t.hasObjectMap());
- }
-
- @Test
- public void hasObjLogs() throws IOException {
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
- String msg = "test";
- ReftableConfig cfg = new ReftableConfig();
- cfg.setIndexObjects(false);
-
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- ReftableWriter writer = new ReftableWriter(buffer)
- .setMinUpdateIndex(1)
- .setConfig(cfg)
- .setMaxUpdateIndex(1)
- .begin();
-
- writer.writeLog("master", 1, who, ObjectId.zeroId(), id(1), msg);
- writer.finish();
- byte[] table = buffer.toByteArray();
-
- ReftableReader t = read(table);
- assertTrue(t.hasObjectMap());
- }
-
- @Test
- public void hasObjMapRefsNoIndexObjects() throws IOException {
- ArrayList<Ref> refs = new ArrayList<>();
- ReftableConfig cfg = new ReftableConfig();
- cfg.setIndexObjects(false);
- cfg.setRefBlockSize(256);
- cfg.setAlignBlocks(true);
-
- // Fill up 5 blocks.
- int N = 256 * 5 / 25;
- for (int i= 0; i < N; i++) {
- @SuppressWarnings("boxing")
- Ref ref = ref(String.format("%02d/xxxxxxxxxx", i), i);
- refs.add(ref);
- }
- byte[] table = write(refs, cfg);
-
- ReftableReader t = read(table);
- assertFalse(t.hasObjectMap());
- }
-
- @Test
- public void oneIdRef() throws IOException {
- Ref exp = ref(MASTER, 1);
- byte[] table = write(exp);
- assertEquals(24 + 4 + 5 + 4 + MASTER.length() + 20 + 68, table.length);
-
- ReftableReader t = read(table);
- try (RefCursor rc = t.allRefs()) {
- assertTrue(rc.next());
- Ref act = rc.getRef();
- assertNotNull(act);
- assertEquals(PACKED, act.getStorage());
- assertTrue(act.isPeeled());
- assertFalse(act.isSymbolic());
- assertEquals(exp.getName(), act.getName());
- assertEquals(exp.getObjectId(), act.getObjectId());
- assertEquals(0, act.getUpdateIndex());
- assertNull(act.getPeeledObjectId());
- assertFalse(rc.wasDeleted());
- assertFalse(rc.next());
- }
- try (RefCursor rc = t.seekRef(MASTER)) {
- assertTrue(rc.next());
- Ref act = rc.getRef();
- assertNotNull(act);
- assertEquals(exp.getName(), act.getName());
- assertEquals(0, act.getUpdateIndex());
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void oneTagRef() throws IOException {
- Ref exp = tag(V1_0, 1, 2);
- byte[] table = write(exp);
- assertEquals(24 + 4 + 5 + 3 + V1_0.length() + 40 + 68, table.length);
-
- ReftableReader t = read(table);
- try (RefCursor rc = t.allRefs()) {
- assertTrue(rc.next());
- Ref act = rc.getRef();
- assertNotNull(act);
- assertEquals(PACKED, act.getStorage());
- assertTrue(act.isPeeled());
- assertFalse(act.isSymbolic());
- assertEquals(exp.getName(), act.getName());
- assertEquals(exp.getObjectId(), act.getObjectId());
- assertEquals(exp.getPeeledObjectId(), act.getPeeledObjectId());
- assertEquals(0, act.getUpdateIndex());
- }
- }
-
- @Test
- public void oneSymbolicRef() throws IOException {
- Ref exp = sym(HEAD, MASTER);
- byte[] table = write(exp);
- assertEquals(
- 24 + 4 + 5 + 2 + HEAD.length() + 2 + MASTER.length() + 68,
- table.length);
-
- ReftableReader t = read(table);
- try (RefCursor rc = t.allRefs()) {
- assertTrue(rc.next());
- Ref act = rc.getRef();
- assertNotNull(act);
- assertTrue(act.isSymbolic());
- assertEquals(exp.getName(), act.getName());
- assertNotNull(act.getLeaf());
- assertEquals(MASTER, act.getTarget().getName());
- assertNull(act.getObjectId());
- assertEquals(0, act.getUpdateIndex());
- }
- }
-
- @Test
- public void resolveSymbolicRef() throws IOException {
- Reftable t = read(write(
- sym(HEAD, "refs/heads/tmp"),
- sym("refs/heads/tmp", MASTER),
- ref(MASTER, 1)));
-
- Ref head = t.exactRef(HEAD);
- assertNull(head.getObjectId());
- assertEquals("refs/heads/tmp", head.getTarget().getName());
- assertEquals(0, head.getUpdateIndex());
-
- head = t.resolve(head);
- assertNotNull(head);
- assertEquals(id(1), head.getObjectId());
- assertEquals(0, head.getUpdateIndex());
-
- Ref master = t.exactRef(MASTER);
- assertNotNull(master);
- assertSame(master, t.resolve(master));
- assertEquals(0, master.getUpdateIndex());
- }
-
- @Test
- public void failDeepChainOfSymbolicRef() throws IOException {
- Reftable t = read(write(
- sym(HEAD, "refs/heads/1"),
- sym("refs/heads/1", "refs/heads/2"),
- sym("refs/heads/2", "refs/heads/3"),
- sym("refs/heads/3", "refs/heads/4"),
- sym("refs/heads/4", "refs/heads/5"),
- sym("refs/heads/5", MASTER),
- ref(MASTER, 1)));
-
- Ref head = t.exactRef(HEAD);
- assertNull(head.getObjectId());
- assertNull(t.resolve(head));
- }
-
- @Test
- public void oneDeletedRef() throws IOException {
- String name = "refs/heads/gone";
- Ref exp = newRef(name);
- byte[] table = write(exp);
- assertEquals(24 + 4 + 5 + 3 + name.length() + 68, table.length);
-
- ReftableReader t = read(table);
- try (RefCursor rc = t.allRefs()) {
- assertFalse(rc.next());
- }
-
- t.setIncludeDeletes(true);
- try (RefCursor rc = t.allRefs()) {
- assertTrue(rc.next());
- Ref act = rc.getRef();
- assertNotNull(act);
- assertFalse(act.isSymbolic());
- assertEquals(name, act.getName());
- assertEquals(NEW, act.getStorage());
- assertNull(act.getObjectId());
- assertTrue(rc.wasDeleted());
- }
- }
-
- @Test
- public void seekNotFound() throws IOException {
- Ref exp = ref(MASTER, 1);
- ReftableReader t = read(write(exp));
- try (RefCursor rc = t.seekRef("refs/heads/a")) {
- assertFalse(rc.next());
- }
- try (RefCursor rc = t.seekRef("refs/heads/n")) {
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void namespaceNotFound() throws IOException {
- Ref exp = ref(MASTER, 1);
- ReftableReader t = read(write(exp));
- try (RefCursor rc = t.seekRefsWithPrefix("refs/changes/")) {
- assertFalse(rc.next());
- }
- try (RefCursor rc = t.seekRefsWithPrefix("refs/tags/")) {
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void namespaceHeads() throws IOException {
- Ref master = ref(MASTER, 1);
- Ref next = ref(NEXT, 2);
- Ref v1 = tag(V1_0, 3, 4);
-
- ReftableReader t = read(write(master, next, v1));
- try (RefCursor rc = t.seekRefsWithPrefix("refs/tags/")) {
- assertTrue(rc.next());
- assertEquals(V1_0, rc.getRef().getName());
- assertEquals(0, rc.getRef().getUpdateIndex());
- assertFalse(rc.next());
- }
- try (RefCursor rc = t.seekRefsWithPrefix("refs/heads/")) {
- assertTrue(rc.next());
- assertEquals(MASTER, rc.getRef().getName());
- assertEquals(0, rc.getRef().getUpdateIndex());
-
- assertTrue(rc.next());
- assertEquals(NEXT, rc.getRef().getName());
- assertEquals(0, rc.getRef().getUpdateIndex());
-
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void seekPastRefWithRefCursor() throws IOException {
- Ref exp = ref(MASTER, 1);
- Ref next = ref(NEXT, 2);
- Ref afterNext = ref(AFTER_NEXT, 3);
- Ref afterNextNext = ref(LAST, 4);
- ReftableReader t = read(write(exp, next, afterNext, afterNextNext));
- try (RefCursor rc = t.seekRefsWithPrefix("")) {
- assertTrue(rc.next());
- assertEquals(MASTER, rc.getRef().getName());
-
- rc.seekPastPrefix("refs/heads/next/");
-
- assertTrue(rc.next());
- assertEquals(AFTER_NEXT, rc.getRef().getName());
- assertTrue(rc.next());
- assertEquals(LAST, rc.getRef().getName());
-
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void seekPastToNonExistentPrefixToTheMiddle() throws IOException {
- Ref exp = ref(MASTER, 1);
- Ref next = ref(NEXT, 2);
- Ref afterNext = ref(AFTER_NEXT, 3);
- Ref afterNextNext = ref(LAST, 4);
- ReftableReader t = read(write(exp, next, afterNext, afterNextNext));
- try (RefCursor rc = t.seekRefsWithPrefix("")) {
- rc.seekPastPrefix("refs/heads/master_non_existent");
-
- assertTrue(rc.next());
- assertEquals(NEXT, rc.getRef().getName());
-
- assertTrue(rc.next());
- assertEquals(AFTER_NEXT, rc.getRef().getName());
-
- assertTrue(rc.next());
- assertEquals(LAST, rc.getRef().getName());
-
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void seekPastToNonExistentPrefixToTheEnd() throws IOException {
- Ref exp = ref(MASTER, 1);
- Ref next = ref(NEXT, 2);
- Ref afterNext = ref(AFTER_NEXT, 3);
- Ref afterNextNext = ref(LAST, 4);
- ReftableReader t = read(write(exp, next, afterNext, afterNextNext));
- try (RefCursor rc = t.seekRefsWithPrefix("")) {
- rc.seekPastPrefix("refs/heads/nextnon_existent_end");
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void seekPastWithSeekRefsWithPrefix() throws IOException {
- Ref exp = ref(MASTER, 1);
- Ref next = ref(NEXT, 2);
- Ref afterNext = ref(AFTER_NEXT, 3);
- Ref afterNextNext = ref(LAST, 4);
- Ref notRefsHeads = ref(NOT_REF_HEADS, 5);
- ReftableReader t = read(write(exp, next, afterNext, afterNextNext, notRefsHeads));
- try (RefCursor rc = t.seekRefsWithPrefix("refs/heads/")) {
- rc.seekPastPrefix("refs/heads/next/");
- assertTrue(rc.next());
- assertEquals(AFTER_NEXT, rc.getRef().getName());
- assertTrue(rc.next());
- assertEquals(LAST, rc.getRef().getName());
-
- // NOT_REF_HEADS is next, but it's omitted because of
- // seekRefsWithPrefix("refs/heads/").
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void seekPastWithLotsOfRefs() throws IOException {
- Ref[] refs = new Ref[500];
- for (int i = 1; i <= 500; i++) {
- refs[i - 1] = ref(String.format("refs/%d", i), i);
- }
- ReftableReader t = read(write(refs));
- try (RefCursor rc = t.allRefs()) {
- rc.seekPastPrefix("refs/3");
- assertTrue(rc.next());
- assertEquals("refs/4", rc.getRef().getName());
- assertTrue(rc.next());
- assertEquals("refs/40", rc.getRef().getName());
-
- rc.seekPastPrefix("refs/8");
- assertTrue(rc.next());
- assertEquals("refs/9", rc.getRef().getName());
- assertTrue(rc.next());
- assertEquals("refs/90", rc.getRef().getName());
- assertTrue(rc.next());
- assertEquals("refs/91", rc.getRef().getName());
- }
- }
-
- @Test
- public void seekPastManyTimes() throws IOException {
- Ref exp = ref(MASTER, 1);
- Ref next = ref(NEXT, 2);
- Ref afterNext = ref(AFTER_NEXT, 3);
- Ref afterNextNext = ref(LAST, 4);
- ReftableReader t = read(write(exp, next, afterNext, afterNextNext));
-
- try (RefCursor rc = t.seekRefsWithPrefix("")) {
- rc.seekPastPrefix("refs/heads/master");
- rc.seekPastPrefix("refs/heads/next");
- rc.seekPastPrefix("refs/heads/nextnext");
- rc.seekPastPrefix("refs/heads/nextnextnext");
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void seekPastOnEmptyTable() throws IOException {
- ReftableReader t = read(write());
- try (RefCursor rc = t.seekRefsWithPrefix("")) {
- rc.seekPastPrefix("refs/");
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void indexScan() throws IOException {
- List<Ref> refs = new ArrayList<>();
- for (int i = 1; i <= 5670; i++) {
- @SuppressWarnings("boxing")
- Ref ref = ref(String.format("refs/heads/%04d", i), i);
- refs.add(ref);
- }
-
- byte[] table = write(refs);
- assertTrue(stats.refIndexLevels() > 0);
- assertTrue(stats.refIndexSize() > 0);
- assertScan(refs, read(table));
- }
-
- @Test
- public void indexSeek() throws IOException {
- List<Ref> refs = new ArrayList<>();
- for (int i = 1; i <= 5670; i++) {
- @SuppressWarnings("boxing")
- Ref ref = ref(String.format("refs/heads/%04d", i), i);
- refs.add(ref);
- }
-
- byte[] table = write(refs);
- assertTrue(stats.refIndexLevels() > 0);
- assertTrue(stats.refIndexSize() > 0);
- assertSeek(refs, read(table));
- }
-
- @Test
- public void noIndexScan() throws IOException {
- List<Ref> refs = new ArrayList<>();
- for (int i = 1; i <= 567; i++) {
- @SuppressWarnings("boxing")
- Ref ref = ref(String.format("refs/heads/%03d", i), i);
- refs.add(ref);
- }
-
- byte[] table = write(refs);
- assertEquals(0, stats.refIndexLevels());
- assertEquals(0, stats.refIndexSize());
- assertEquals(table.length, stats.totalBytes());
- assertScan(refs, read(table));
- }
-
- @Test
- public void noIndexSeek() throws IOException {
- List<Ref> refs = new ArrayList<>();
- for (int i = 1; i <= 567; i++) {
- @SuppressWarnings("boxing")
- Ref ref = ref(String.format("refs/heads/%03d", i), i);
- refs.add(ref);
- }
-
- byte[] table = write(refs);
- assertEquals(0, stats.refIndexLevels());
- assertSeek(refs, read(table));
- }
-
- @Test
- public void invalidRefWriteOrderSortAndWrite() {
- Ref master = ref(MASTER, 1);
- ReftableWriter writer = new ReftableWriter(new ByteArrayOutputStream())
- .setMinUpdateIndex(1)
- .setMaxUpdateIndex(1)
- .begin();
-
- List<Ref> refs = new ArrayList<>();
- refs.add(master);
- refs.add(master);
-
- IllegalArgumentException e = assertThrows(
- IllegalArgumentException.class,
- () -> writer.sortAndWriteRefs(refs));
- assertThat(e.getMessage(), containsString("records must be increasing"));
- }
-
- @Test
- public void invalidReflogWriteOrderUpdateIndex() throws IOException {
- ReftableWriter writer = new ReftableWriter(new ByteArrayOutputStream())
- .setMinUpdateIndex(1)
- .setMaxUpdateIndex(2)
- .begin();
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
- String msg = "test";
-
- writer.writeLog(MASTER, 1, who, ObjectId.zeroId(), id(1), msg);
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> writer.writeLog(
- MASTER, 2, who, ObjectId.zeroId(), id(2), msg));
- assertThat(e.getMessage(), containsString("records must be increasing"));
- }
-
- @Test
- public void invalidReflogWriteOrderName() throws IOException {
- ReftableWriter writer = new ReftableWriter(new ByteArrayOutputStream())
- .setMinUpdateIndex(1)
- .setMaxUpdateIndex(1)
- .begin();
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
- String msg = "test";
-
- writer.writeLog(NEXT, 1, who, ObjectId.zeroId(), id(1), msg);
- IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> writer.writeLog(
- MASTER, 1, who, ObjectId.zeroId(), id(2), msg));
- assertThat(e.getMessage(), containsString("records must be increasing"));
- }
-
- @Test
- public void withReflog() throws IOException {
- Ref master = ref(MASTER, 1);
- Ref next = ref(NEXT, 2);
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
- String msg = "test";
-
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- ReftableWriter writer = new ReftableWriter(buffer)
- .setMinUpdateIndex(1)
- .setMaxUpdateIndex(1)
- .begin();
-
- writer.writeRef(master);
- writer.writeRef(next);
-
- writer.writeLog(MASTER, 1, who, ObjectId.zeroId(), id(1), msg);
- writer.writeLog(NEXT, 1, who, ObjectId.zeroId(), id(2), msg);
-
- writer.finish();
- byte[] table = buffer.toByteArray();
- assertEquals(247, table.length);
-
- ReftableReader t = read(table);
- try (RefCursor rc = t.allRefs()) {
- assertTrue(rc.next());
- assertEquals(MASTER, rc.getRef().getName());
- assertEquals(id(1), rc.getRef().getObjectId());
- assertEquals(1, rc.getRef().getUpdateIndex());
-
- assertTrue(rc.next());
- assertEquals(NEXT, rc.getRef().getName());
- assertEquals(id(2), rc.getRef().getObjectId());
- assertEquals(1, rc.getRef().getUpdateIndex());
- assertFalse(rc.next());
- }
- try (LogCursor lc = t.allLogs()) {
- assertTrue(lc.next());
- assertEquals(MASTER, lc.getRefName());
- assertEquals(1, lc.getUpdateIndex());
- assertEquals(ObjectId.zeroId(), lc.getReflogEntry().getOldId());
- assertEquals(id(1), lc.getReflogEntry().getNewId());
- assertEquals(who, lc.getReflogEntry().getWho());
- assertEquals(msg, lc.getReflogEntry().getComment());
-
- assertTrue(lc.next());
- assertEquals(NEXT, lc.getRefName());
- assertEquals(1, lc.getUpdateIndex());
- assertEquals(ObjectId.zeroId(), lc.getReflogEntry().getOldId());
- assertEquals(id(2), lc.getReflogEntry().getNewId());
- assertEquals(who, lc.getReflogEntry().getWho());
- assertEquals(msg, lc.getReflogEntry().getComment());
-
- assertFalse(lc.next());
- }
- }
-
- @Test
- public void reflogReader() throws IOException {
- Ref master = ref(MASTER, 1);
- Ref next = ref(NEXT, 2);
-
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- ReftableWriter writer = new ReftableWriter(buffer).setMinUpdateIndex(1)
- .setMaxUpdateIndex(1).begin();
-
- writer.writeRef(master);
- writer.writeRef(next);
-
- PersonIdent who1 = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
- writer.writeLog(MASTER, 3, who1, ObjectId.zeroId(), id(1), "1");
- PersonIdent who2 = new PersonIdent("Log", "Ger", 1500079710, -8 * 60);
- writer.writeLog(MASTER, 2, who2, id(1), id(2), "2");
- PersonIdent who3 = new PersonIdent("Log", "Ger", 1500079711, -8 * 60);
- writer.writeLog(MASTER, 1, who3, id(2), id(3), "3");
-
- writer.finish();
- byte[] table = buffer.toByteArray();
-
- ReentrantLock lock = new ReentrantLock();
- ReftableReader t = read(table);
- ReftableReflogReader rlr = new ReftableReflogReader(lock, t, MASTER);
-
- assertEquals(rlr.getLastEntry().getWho(), who1);
- List<PersonIdent> all = rlr.getReverseEntries().stream()
- .map(x -> x.getWho()).collect(Collectors.toList());
- Matchers.contains(all, who3, who2, who1);
-
- assertEquals(rlr.getReverseEntry(1).getWho(), who2);
-
- List<ReflogEntry> reverse2 = rlr.getReverseEntries(2);
- Matchers.contains(reverse2, who3, who2);
-
- List<PersonIdent> more = rlr.getReverseEntries(4).stream()
- .map(x -> x.getWho()).collect(Collectors.toList());
- assertEquals(all, more);
- }
-
- @Test
- public void allRefs() throws IOException {
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- ReftableConfig cfg = new ReftableConfig();
- cfg.setRefBlockSize(1024);
- cfg.setLogBlockSize(1024);
- cfg.setAlignBlocks(true);
- ReftableWriter writer = new ReftableWriter(buffer)
- .setMinUpdateIndex(1)
- .setMaxUpdateIndex(1)
- .setConfig(cfg)
- .begin();
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
-
- // Fill out the 1st ref block.
- List<String> names = new ArrayList<>();
- for (int i = 0; i < 4; i++) {
- @SuppressWarnings("boxing")
- String name = new String(new char[220]).replace("\0", String.format("%c", i + 'a'));
- names.add(name);
- writer.writeRef(ref(name, i));
- }
-
- // Add some log data.
- writer.writeLog(MASTER, 1, who, ObjectId.zeroId(), id(1), "msg");
- writer.finish();
- byte[] table = buffer.toByteArray();
-
- ReftableReader t = read(table);
- RefCursor c = t.allRefs();
-
- int j = 0;
- while (c.next()) {
- assertEquals(names.get(j), c.getRef().getName());
- j++;
- }
- }
-
-
- @Test
- public void reflogSeek() throws IOException {
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
- String msg = "test";
- String msgNext = "test next";
-
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- ReftableWriter writer = new ReftableWriter(buffer)
- .setMinUpdateIndex(1)
- .setMaxUpdateIndex(1)
- .begin();
-
- writer.writeLog(MASTER, 1, who, ObjectId.zeroId(), id(1), msg);
- writer.writeLog(NEXT, 1, who, ObjectId.zeroId(), id(2), msgNext);
-
- writer.finish();
- byte[] table = buffer.toByteArray();
-
- ReftableReader t = read(table);
- try (LogCursor c = t.seekLog(MASTER, Long.MAX_VALUE)) {
- assertTrue(c.next());
- assertEquals(c.getReflogEntry().getComment(), msg);
- }
- try (LogCursor c = t.seekLog(MASTER, 0)) {
- assertFalse(c.next());
- }
- try (LogCursor c = t.seekLog(MASTER, 1)) {
- assertTrue(c.next());
- assertEquals(c.getUpdateIndex(), 1);
- assertEquals(c.getReflogEntry().getComment(), msg);
- }
- try (LogCursor c = t.seekLog(NEXT, Long.MAX_VALUE)) {
- assertTrue(c.next());
- assertEquals(c.getReflogEntry().getComment(), msgNext);
- }
- try (LogCursor c = t.seekLog(NEXT, 0)) {
- assertFalse(c.next());
- }
- try (LogCursor c = t.seekLog(NEXT, 1)) {
- assertTrue(c.next());
- assertEquals(c.getUpdateIndex(), 1);
- assertEquals(c.getReflogEntry().getComment(), msgNext);
- }
- }
-
- @Test
- public void reflogSeekPrefix() throws IOException {
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
-
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- ReftableWriter writer = new ReftableWriter(buffer)
- .setMinUpdateIndex(1)
- .setMaxUpdateIndex(1)
- .begin();
-
- writer.writeLog("branchname", 1, who, ObjectId.zeroId(), id(1), "branchname");
-
- writer.finish();
- byte[] table = buffer.toByteArray();
-
- ReftableReader t = read(table);
- try (LogCursor c = t.seekLog("branch", Long.MAX_VALUE)) {
- // We find a reflog block, but the iteration won't confuse branchname
- // and branch.
- assertFalse(c.next());
- }
- }
-
- @Test
- public void onlyReflog() throws IOException {
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
- String msg = "test";
-
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- ReftableWriter writer = new ReftableWriter(buffer)
- .setMinUpdateIndex(1)
- .setMaxUpdateIndex(1)
- .begin();
- writer.writeLog(MASTER, 1, who, ObjectId.zeroId(), id(1), msg);
- writer.writeLog(NEXT, 1, who, ObjectId.zeroId(), id(2), msg);
- writer.finish();
- byte[] table = buffer.toByteArray();
- stats = writer.getStats();
- assertEquals(170, table.length);
- assertEquals(0, stats.refCount());
- assertEquals(0, stats.refBytes());
- assertEquals(0, stats.refIndexLevels());
-
- ReftableReader t = read(table);
- try (RefCursor rc = t.allRefs()) {
- assertFalse(rc.next());
- }
- try (RefCursor rc = t.seekRefsWithPrefix("refs/heads/")) {
- assertFalse(rc.next());
- }
- try (LogCursor lc = t.allLogs()) {
- assertTrue(lc.next());
- assertEquals(MASTER, lc.getRefName());
- assertEquals(1, lc.getUpdateIndex());
- assertEquals(ObjectId.zeroId(), lc.getReflogEntry().getOldId());
- assertEquals(id(1), lc.getReflogEntry().getNewId());
- assertEquals(who, lc.getReflogEntry().getWho());
- // compare string too, to catch tz differences.
- assertEquals(who.toExternalString(), lc.getReflogEntry().getWho().toExternalString());
- assertEquals(msg, lc.getReflogEntry().getComment());
-
- assertTrue(lc.next());
- assertEquals(NEXT, lc.getRefName());
- assertEquals(1, lc.getUpdateIndex());
- assertEquals(ObjectId.zeroId(), lc.getReflogEntry().getOldId());
- assertEquals(id(2), lc.getReflogEntry().getNewId());
- assertEquals(who, lc.getReflogEntry().getWho());
- assertEquals(msg, lc.getReflogEntry().getComment());
-
- assertFalse(lc.next());
- }
- }
-
- @Test
- public void logScan() throws IOException {
- ReftableConfig cfg = new ReftableConfig();
- cfg.setRefBlockSize(256);
- cfg.setLogBlockSize(2048);
-
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- ReftableWriter writer = new ReftableWriter(cfg, buffer);
- writer.setMinUpdateIndex(1).setMaxUpdateIndex(1).begin();
-
- List<Ref> refs = new ArrayList<>();
- for (int i = 1; i <= 5670; i++) {
- @SuppressWarnings("boxing")
- Ref ref = ref(String.format("refs/heads/%04d", i), i);
- refs.add(ref);
- writer.writeRef(ref);
- }
-
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
- for (Ref ref : refs) {
- writer.writeLog(ref.getName(), 1, who,
- ObjectId.zeroId(), ref.getObjectId(),
- "create " + ref.getName());
- }
- writer.finish();
- stats = writer.getStats();
- assertTrue(stats.logBytes() > 4096);
- byte[] table = buffer.toByteArray();
-
- ReftableReader t = read(table);
- try (LogCursor lc = t.allLogs()) {
- for (Ref exp : refs) {
- assertTrue("has " + exp.getName(), lc.next());
- assertEquals(exp.getName(), lc.getRefName());
- ReflogEntry entry = lc.getReflogEntry();
- assertNotNull(entry);
- assertEquals(who, entry.getWho());
- assertEquals(ObjectId.zeroId(), entry.getOldId());
- assertEquals(exp.getObjectId(), entry.getNewId());
- assertEquals("create " + exp.getName(), entry.getComment());
- }
- assertFalse(lc.next());
- }
- }
-
- @Test
- public void byObjectIdOneRefNoIndex() throws IOException {
- List<Ref> refs = new ArrayList<>();
- for (int i = 1; i <= 200; i++) {
- @SuppressWarnings("boxing")
- Ref ref = ref(String.format("refs/heads/%02d", i), i);
- refs.add(ref);
- }
- refs.add(ref("refs/heads/master", 100));
-
- ReftableReader t = read(write(refs));
- assertEquals(0, stats.objIndexSize());
-
- try (RefCursor rc = t.byObjectId(id(42))) {
- assertTrue("has 42", rc.next());
- assertEquals("refs/heads/42", rc.getRef().getName());
- assertEquals(id(42), rc.getRef().getObjectId());
- assertEquals(0, rc.getRef().getUpdateIndex());
- assertFalse(rc.next());
- }
- try (RefCursor rc = t.byObjectId(id(100))) {
- assertTrue("has 100", rc.next());
- assertEquals("refs/heads/100", rc.getRef().getName());
- assertEquals(id(100), rc.getRef().getObjectId());
-
- assertTrue("has master", rc.next());
- assertEquals("refs/heads/master", rc.getRef().getName());
- assertEquals(id(100), rc.getRef().getObjectId());
- assertEquals(0, rc.getRef().getUpdateIndex());
-
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void byObjectIdOneRefWithIndex() throws IOException {
- List<Ref> refs = new ArrayList<>();
- for (int i = 1; i <= 5200; i++) {
- @SuppressWarnings("boxing")
- Ref ref = ref(String.format("refs/heads/%02d", i), i);
- refs.add(ref);
- }
- refs.add(ref("refs/heads/master", 100));
-
- ReftableReader t = read(write(refs));
- assertTrue(stats.objIndexSize() > 0);
-
- try (RefCursor rc = t.byObjectId(id(42))) {
- assertTrue("has 42", rc.next());
- assertEquals("refs/heads/42", rc.getRef().getName());
- assertEquals(id(42), rc.getRef().getObjectId());
- assertEquals(0, rc.getRef().getUpdateIndex());
- assertFalse(rc.next());
- }
- try (RefCursor rc = t.byObjectId(id(100))) {
- assertTrue("has 100", rc.next());
- assertEquals("refs/heads/100", rc.getRef().getName());
- assertEquals(id(100), rc.getRef().getObjectId());
-
- assertTrue("has master", rc.next());
- assertEquals("refs/heads/master", rc.getRef().getName());
- assertEquals(id(100), rc.getRef().getObjectId());
- assertEquals(0, rc.getRef().getUpdateIndex());
-
- assertFalse(rc.next());
- }
- }
-
- @Test
- public void byObjectIdSkipPastPrefix() throws IOException {
- ReftableReader t = read(write());
- try (RefCursor rc = t.byObjectId(id(2))) {
- assertThrows(UnsupportedOperationException.class, () -> rc.seekPastPrefix("refs/heads/"));
- }
- }
-
- @Test
- public void unpeeledDoesNotWrite() {
- try {
- write(new ObjectIdRef.Unpeeled(PACKED, MASTER, id(1)));
- fail("expected IOException");
- } catch (IOException e) {
- assertEquals(JGitText.get().peeledRefIsRequired, e.getMessage());
- }
- }
-
- @Test
- public void skipPastRefWithLastUTF8() throws IOException {
- ReftableReader t = read(write(ref(String.format("refs/heads/%sbla", new String(LAST_UTF8_CHAR
- , UTF_8)), 1)));
-
- try (RefCursor rc = t.allRefs()) {
- rc.seekPastPrefix("refs/heads/");
- assertFalse(rc.next());
- }
- }
-
-
- @Test
- public void nameTooLongDoesNotWrite() throws IOException {
- try {
- ReftableConfig cfg = new ReftableConfig();
- cfg.setRefBlockSize(64);
-
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- ReftableWriter writer = new ReftableWriter(cfg, buffer).begin();
- writer.writeRef(ref("refs/heads/i-am-not-a-teapot", 1));
- writer.finish();
- fail("expected BlockSizeTooSmallException");
- } catch (BlockSizeTooSmallException e) {
- assertEquals(85, e.getMinimumBlockSize());
- }
- }
-
- @Test
- public void badCrc32() throws IOException {
- byte[] table = write();
- table[table.length - 1] = 0x42;
-
- try {
- read(table).seekRef(HEAD);
- fail("expected IOException");
- } catch (IOException e) {
- assertEquals(JGitText.get().invalidReftableCRC, e.getMessage());
- }
- }
-
- private static void assertScan(List<Ref> refs, Reftable t)
- throws IOException {
- try (RefCursor rc = t.allRefs()) {
- for (Ref exp : refs) {
- assertTrue("has " + exp.getName(), rc.next());
- Ref act = rc.getRef();
- assertEquals(exp.getName(), act.getName());
- assertEquals(exp.getObjectId(), act.getObjectId());
- assertEquals(0, rc.getRef().getUpdateIndex());
- }
- assertFalse(rc.next());
- }
- }
-
- private static void assertSeek(List<Ref> refs, Reftable t)
- throws IOException {
- for (Ref exp : refs) {
- try (RefCursor rc = t.seekRef(exp.getName())) {
- assertTrue("has " + exp.getName(), rc.next());
- Ref act = rc.getRef();
- assertEquals(exp.getName(), act.getName());
- assertEquals(exp.getObjectId(), act.getObjectId());
- assertEquals(0, rc.getRef().getUpdateIndex());
- assertFalse(rc.next());
- }
- }
- }
-
- private static Ref ref(String name, int id) {
- return new ObjectIdRef.PeeledNonTag(PACKED, name, id(id));
- }
-
- private static Ref tag(String name, int id1, int id2) {
- return new ObjectIdRef.PeeledTag(PACKED, name, id(id1), id(id2));
- }
-
- private static Ref sym(String name, String target) {
- return new SymbolicRef(name, newRef(target));
- }
-
- private static Ref newRef(String name) {
- return new ObjectIdRef.Unpeeled(NEW, name, null);
- }
-
- private static ObjectId id(int i) {
- byte[] buf = new byte[OBJECT_ID_LENGTH];
- buf[0] = (byte) (i & 0xff);
- buf[1] = (byte) ((i >>> 8) & 0xff);
- buf[2] = (byte) ((i >>> 16) & 0xff);
- buf[3] = (byte) (i >>> 24);
- return ObjectId.fromRaw(buf);
- }
-
- private static ReftableReader read(byte[] table) {
- return new ReftableReader(BlockSource.from(table));
- }
-
- private byte[] write(Ref... refs) throws IOException {
- return write(Arrays.asList(refs));
- }
-
- private byte[] write(Collection<Ref> refs) throws IOException {
- return write(refs, new ReftableConfig());
- }
-
- private byte[] write(Collection<Ref> refs, ReftableConfig cfg) throws IOException {
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- stats = new ReftableWriter(buffer)
- .setConfig(cfg)
- .begin()
- .sortAndWriteRefs(refs)
- .finish()
- .getStats();
- return buffer.toByteArray();
- }
- }
|