You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MergedReftableTest.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. /*
  2. * Copyright (C) 2017, Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.internal.storage.reftable;
  44. import static org.eclipse.jgit.lib.Constants.HEAD;
  45. import static org.eclipse.jgit.lib.Constants.MASTER;
  46. import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
  47. import static org.eclipse.jgit.lib.Constants.R_HEADS;
  48. import static org.eclipse.jgit.lib.Ref.Storage.NEW;
  49. import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
  50. import static org.junit.Assert.assertEquals;
  51. import static org.junit.Assert.assertFalse;
  52. import static org.junit.Assert.assertNull;
  53. import static org.junit.Assert.assertTrue;
  54. import java.io.ByteArrayOutputStream;
  55. import java.io.IOException;
  56. import java.util.ArrayList;
  57. import java.util.Arrays;
  58. import java.util.Collection;
  59. import java.util.Collections;
  60. import java.util.HashMap;
  61. import java.util.List;
  62. import java.util.Map;
  63. import org.eclipse.jgit.internal.storage.io.BlockSource;
  64. import org.eclipse.jgit.lib.ObjectId;
  65. import org.eclipse.jgit.lib.ObjectIdRef;
  66. import org.eclipse.jgit.lib.Ref;
  67. import org.eclipse.jgit.lib.RefComparator;
  68. import org.eclipse.jgit.lib.SymbolicRef;
  69. import org.junit.Test;
  70. public class MergedReftableTest {
  71. @Test
  72. public void noTables() throws IOException {
  73. MergedReftable mr = merge(new byte[0][]);
  74. try (RefCursor rc = mr.allRefs()) {
  75. assertFalse(rc.next());
  76. }
  77. try (RefCursor rc = mr.seekRef(HEAD)) {
  78. assertFalse(rc.next());
  79. }
  80. try (RefCursor rc = mr.seekRefsWithPrefix(R_HEADS)) {
  81. assertFalse(rc.next());
  82. }
  83. }
  84. @Test
  85. public void oneEmptyTable() throws IOException {
  86. MergedReftable mr = merge(write());
  87. try (RefCursor rc = mr.allRefs()) {
  88. assertFalse(rc.next());
  89. }
  90. try (RefCursor rc = mr.seekRef(HEAD)) {
  91. assertFalse(rc.next());
  92. }
  93. try (RefCursor rc = mr.seekRefsWithPrefix(R_HEADS)) {
  94. assertFalse(rc.next());
  95. }
  96. }
  97. @Test
  98. public void twoEmptyTables() throws IOException {
  99. MergedReftable mr = merge(write(), write());
  100. try (RefCursor rc = mr.allRefs()) {
  101. assertFalse(rc.next());
  102. }
  103. try (RefCursor rc = mr.seekRef(HEAD)) {
  104. assertFalse(rc.next());
  105. }
  106. try (RefCursor rc = mr.seekRefsWithPrefix(R_HEADS)) {
  107. assertFalse(rc.next());
  108. }
  109. }
  110. @SuppressWarnings("boxing")
  111. @Test
  112. public void oneTableScan() throws IOException {
  113. List<Ref> refs = new ArrayList<>();
  114. for (int i = 1; i <= 567; i++) {
  115. refs.add(ref(String.format("refs/heads/%03d", i), i));
  116. }
  117. MergedReftable mr = merge(write(refs));
  118. try (RefCursor rc = mr.allRefs()) {
  119. for (Ref exp : refs) {
  120. assertTrue("has " + exp.getName(), rc.next());
  121. Ref act = rc.getRef();
  122. assertEquals(exp.getName(), act.getName());
  123. assertEquals(exp.getObjectId(), act.getObjectId());
  124. assertEquals(1, act.getUpdateIndex());
  125. }
  126. assertFalse(rc.next());
  127. }
  128. }
  129. @Test
  130. public void deleteIsHidden() throws IOException {
  131. List<Ref> delta1 = Arrays.asList(
  132. ref("refs/heads/apple", 1),
  133. ref("refs/heads/master", 2));
  134. List<Ref> delta2 = Arrays.asList(delete("refs/heads/apple"));
  135. MergedReftable mr = merge(write(delta1), write(delta2));
  136. try (RefCursor rc = mr.allRefs()) {
  137. assertTrue(rc.next());
  138. assertEquals("refs/heads/master", rc.getRef().getName());
  139. assertEquals(id(2), rc.getRef().getObjectId());
  140. assertEquals(1, rc.getRef().getUpdateIndex());
  141. assertFalse(rc.next());
  142. }
  143. }
  144. @Test
  145. public void twoTableSeek() throws IOException {
  146. List<Ref> delta1 = Arrays.asList(
  147. ref("refs/heads/apple", 1),
  148. ref("refs/heads/master", 2));
  149. List<Ref> delta2 = Arrays.asList(ref("refs/heads/banana", 3));
  150. MergedReftable mr = merge(write(delta1), write(delta2));
  151. try (RefCursor rc = mr.seekRef("refs/heads/master")) {
  152. assertTrue(rc.next());
  153. assertEquals("refs/heads/master", rc.getRef().getName());
  154. assertEquals(id(2), rc.getRef().getObjectId());
  155. assertFalse(rc.next());
  156. assertEquals(1, rc.getRef().getUpdateIndex());
  157. }
  158. }
  159. @Test
  160. public void twoTableById() throws IOException {
  161. List<Ref> delta1 = Arrays.asList(
  162. ref("refs/heads/apple", 1),
  163. ref("refs/heads/master", 2));
  164. List<Ref> delta2 = Arrays.asList(ref("refs/heads/banana", 3));
  165. MergedReftable mr = merge(write(delta1), write(delta2));
  166. try (RefCursor rc = mr.byObjectId(id(2))) {
  167. assertTrue(rc.next());
  168. assertEquals("refs/heads/master", rc.getRef().getName());
  169. assertEquals(id(2), rc.getRef().getObjectId());
  170. assertEquals(1, rc.getRef().getUpdateIndex());
  171. assertFalse(rc.next());
  172. }
  173. }
  174. @Test
  175. public void tableByIDDeletion() throws IOException {
  176. List<Ref> delta1 = Arrays.asList(
  177. ref("refs/heads/apple", 1),
  178. ref("refs/heads/master", 2));
  179. List<Ref> delta2 = Arrays.asList(ref("refs/heads/master", 3));
  180. MergedReftable mr = merge(write(delta1), write(delta2));
  181. try (RefCursor rc = mr.byObjectId(id(2))) {
  182. assertFalse(rc.next());
  183. }
  184. }
  185. @SuppressWarnings("boxing")
  186. @Test
  187. public void fourTableScan() throws IOException {
  188. List<Ref> base = new ArrayList<>();
  189. for (int i = 1; i <= 567; i++) {
  190. base.add(ref(String.format("refs/heads/%03d", i), i));
  191. }
  192. List<Ref> delta1 = Arrays.asList(
  193. ref("refs/heads/next", 4),
  194. ref(String.format("refs/heads/%03d", 55), 4096));
  195. List<Ref> delta2 = Arrays.asList(
  196. delete("refs/heads/next"),
  197. ref(String.format("refs/heads/%03d", 55), 8192));
  198. List<Ref> delta3 = Arrays.asList(
  199. ref("refs/heads/master", 4242),
  200. ref(String.format("refs/heads/%03d", 42), 5120),
  201. ref(String.format("refs/heads/%03d", 98), 6120));
  202. List<Ref> expected = merge(base, delta1, delta2, delta3);
  203. MergedReftable mr = merge(
  204. write(base),
  205. write(delta1),
  206. write(delta2),
  207. write(delta3));
  208. try (RefCursor rc = mr.allRefs()) {
  209. for (Ref exp : expected) {
  210. assertTrue("has " + exp.getName(), rc.next());
  211. Ref act = rc.getRef();
  212. assertEquals(exp.getName(), act.getName());
  213. assertEquals(exp.getObjectId(), act.getObjectId());
  214. assertEquals(1, rc.getRef().getUpdateIndex());
  215. }
  216. assertFalse(rc.next());
  217. }
  218. }
  219. @Test
  220. public void scanIncludeDeletes() throws IOException {
  221. List<Ref> delta1 = Arrays.asList(ref("refs/heads/next", 4));
  222. List<Ref> delta2 = Arrays.asList(delete("refs/heads/next"));
  223. List<Ref> delta3 = Arrays.asList(ref("refs/heads/master", 8));
  224. MergedReftable mr = merge(write(delta1), write(delta2), write(delta3));
  225. mr.setIncludeDeletes(true);
  226. try (RefCursor rc = mr.allRefs()) {
  227. assertTrue(rc.next());
  228. Ref r = rc.getRef();
  229. assertEquals("refs/heads/master", r.getName());
  230. assertEquals(id(8), r.getObjectId());
  231. assertEquals(1, rc.getRef().getUpdateIndex());
  232. assertTrue(rc.next());
  233. r = rc.getRef();
  234. assertEquals("refs/heads/next", r.getName());
  235. assertEquals(NEW, r.getStorage());
  236. assertNull(r.getObjectId());
  237. assertEquals(1, rc.getRef().getUpdateIndex());
  238. assertFalse(rc.next());
  239. }
  240. }
  241. @SuppressWarnings("boxing")
  242. @Test
  243. public void oneTableSeek() throws IOException {
  244. List<Ref> refs = new ArrayList<>();
  245. for (int i = 1; i <= 567; i++) {
  246. refs.add(ref(String.format("refs/heads/%03d", i), i));
  247. }
  248. MergedReftable mr = merge(write(refs));
  249. for (Ref exp : refs) {
  250. try (RefCursor rc = mr.seekRef(exp.getName())) {
  251. assertTrue("has " + exp.getName(), rc.next());
  252. Ref act = rc.getRef();
  253. assertEquals(exp.getName(), act.getName());
  254. assertEquals(exp.getObjectId(), act.getObjectId());
  255. assertEquals(1, act.getUpdateIndex());
  256. assertFalse(rc.next());
  257. }
  258. }
  259. }
  260. @Test
  261. public void missedUpdate() throws IOException {
  262. ByteArrayOutputStream buf = new ByteArrayOutputStream();
  263. ReftableWriter writer = new ReftableWriter(buf)
  264. .setMinUpdateIndex(1)
  265. .setMaxUpdateIndex(3)
  266. .begin();
  267. writer.writeRef(ref("refs/heads/a", 1), 1);
  268. writer.writeRef(ref("refs/heads/c", 3), 3);
  269. writer.finish();
  270. byte[] base = buf.toByteArray();
  271. byte[] delta = write(Arrays.asList(
  272. ref("refs/heads/b", 2),
  273. ref("refs/heads/c", 4)),
  274. 2);
  275. MergedReftable mr = merge(base, delta);
  276. try (RefCursor rc = mr.allRefs()) {
  277. assertTrue(rc.next());
  278. assertEquals("refs/heads/a", rc.getRef().getName());
  279. assertEquals(id(1), rc.getRef().getObjectId());
  280. assertEquals(1, rc.getRef().getUpdateIndex());
  281. assertTrue(rc.next());
  282. assertEquals("refs/heads/b", rc.getRef().getName());
  283. assertEquals(id(2), rc.getRef().getObjectId());
  284. assertEquals(2, rc.getRef().getUpdateIndex());
  285. assertTrue(rc.next());
  286. assertEquals("refs/heads/c", rc.getRef().getName());
  287. assertEquals(id(3), rc.getRef().getObjectId());
  288. assertEquals(3, rc.getRef().getUpdateIndex());
  289. }
  290. }
  291. @Test
  292. public void compaction() throws IOException {
  293. List<Ref> delta1 = Arrays.asList(
  294. ref("refs/heads/next", 4),
  295. ref("refs/heads/master", 1));
  296. List<Ref> delta2 = Arrays.asList(delete("refs/heads/next"));
  297. List<Ref> delta3 = Arrays.asList(ref("refs/heads/master", 8));
  298. ByteArrayOutputStream out = new ByteArrayOutputStream();
  299. ReftableCompactor compactor = new ReftableCompactor(out);
  300. compactor.addAll(Arrays.asList(
  301. read(write(delta1)),
  302. read(write(delta2)),
  303. read(write(delta3))));
  304. compactor.compact();
  305. byte[] table = out.toByteArray();
  306. ReftableReader reader = read(table);
  307. try (RefCursor rc = reader.allRefs()) {
  308. assertTrue(rc.next());
  309. Ref r = rc.getRef();
  310. assertEquals("refs/heads/master", r.getName());
  311. assertEquals(id(8), r.getObjectId());
  312. assertFalse(rc.next());
  313. }
  314. }
  315. @Test
  316. public void versioningSymbolicReftargetMoves() throws IOException {
  317. Ref master = ref(MASTER, 100);
  318. List<Ref> delta1 = Arrays.asList(master, sym(HEAD, MASTER));
  319. List<Ref> delta2 = Arrays.asList(ref(MASTER, 200));
  320. MergedReftable mr = merge(write(delta1, 1), write(delta2, 2));
  321. Ref head = mr.exactRef(HEAD);
  322. assertEquals(head.getUpdateIndex(), 1);
  323. Ref masterRef = mr.exactRef(MASTER);
  324. assertEquals(masterRef.getUpdateIndex(), 2);
  325. }
  326. @Test
  327. public void versioningSymbolicRefMoves() throws IOException {
  328. Ref branchX = ref("refs/heads/branchX", 200);
  329. List<Ref> delta1 = Arrays.asList(ref(MASTER, 100), branchX,
  330. sym(HEAD, MASTER));
  331. List<Ref> delta2 = Arrays.asList(sym(HEAD, "refs/heads/branchX"));
  332. List<Ref> delta3 = Arrays.asList(sym(HEAD, MASTER));
  333. MergedReftable mr = merge(write(delta1, 1), write(delta2, 2),
  334. write(delta3, 3));
  335. Ref head = mr.exactRef(HEAD);
  336. assertEquals(head.getUpdateIndex(), 3);
  337. Ref masterRef = mr.exactRef(MASTER);
  338. assertEquals(masterRef.getUpdateIndex(), 1);
  339. Ref branchRef = mr.exactRef(MASTER);
  340. assertEquals(branchRef.getUpdateIndex(), 1);
  341. }
  342. @Test
  343. public void versioningResolveRef() throws IOException {
  344. List<Ref> delta1 = Arrays.asList(sym(HEAD, "refs/heads/tmp"),
  345. sym("refs/heads/tmp", MASTER), ref(MASTER, 100));
  346. List<Ref> delta2 = Arrays.asList(ref(MASTER, 200));
  347. List<Ref> delta3 = Arrays.asList(ref(MASTER, 300));
  348. MergedReftable mr = merge(write(delta1, 1), write(delta2, 2),
  349. write(delta3, 3));
  350. Ref head = mr.exactRef(HEAD);
  351. Ref resolvedHead = mr.resolve(head);
  352. assertEquals(resolvedHead.getObjectId(), id(300));
  353. assertEquals("HEAD has not moved", resolvedHead.getUpdateIndex(), 1);
  354. Ref master = mr.exactRef(MASTER);
  355. Ref resolvedMaster = mr.resolve(master);
  356. assertEquals(resolvedMaster.getObjectId(), id(300));
  357. assertEquals("master also has update index",
  358. resolvedMaster.getUpdateIndex(), 3);
  359. }
  360. private static MergedReftable merge(byte[]... table) {
  361. List<ReftableReader> stack = new ArrayList<>(table.length);
  362. for (byte[] b : table) {
  363. stack.add(read(b));
  364. }
  365. return new MergedReftable(stack);
  366. }
  367. private static ReftableReader read(byte[] table) {
  368. return new ReftableReader(BlockSource.from(table));
  369. }
  370. private static Ref ref(String name, int id) {
  371. return new ObjectIdRef.PeeledNonTag(PACKED, name, id(id));
  372. }
  373. private static Ref sym(String name, String target) {
  374. return new SymbolicRef(name, newRef(target));
  375. }
  376. private static Ref newRef(String name) {
  377. return new ObjectIdRef.Unpeeled(NEW, name, null);
  378. }
  379. private static Ref delete(String name) {
  380. return new ObjectIdRef.Unpeeled(NEW, name, null);
  381. }
  382. private static ObjectId id(int i) {
  383. byte[] buf = new byte[OBJECT_ID_LENGTH];
  384. buf[0] = (byte) (i & 0xff);
  385. buf[1] = (byte) ((i >>> 8) & 0xff);
  386. buf[2] = (byte) ((i >>> 16) & 0xff);
  387. buf[3] = (byte) (i >>> 24);
  388. return ObjectId.fromRaw(buf);
  389. }
  390. private byte[] write(Ref... refs) throws IOException {
  391. return write(Arrays.asList(refs));
  392. }
  393. private byte[] write(Collection<Ref> refs) throws IOException {
  394. return write(refs, 1);
  395. }
  396. private byte[] write(Collection<Ref> refs, long updateIndex)
  397. throws IOException {
  398. ByteArrayOutputStream buffer = new ByteArrayOutputStream();
  399. new ReftableWriter(buffer)
  400. .setMinUpdateIndex(updateIndex)
  401. .setMaxUpdateIndex(updateIndex)
  402. .begin()
  403. .sortAndWriteRefs(refs)
  404. .finish();
  405. return buffer.toByteArray();
  406. }
  407. @SafeVarargs
  408. private static List<Ref> merge(List<Ref>... tables) {
  409. Map<String, Ref> expect = new HashMap<>();
  410. for (List<Ref> t : tables) {
  411. for (Ref r : t) {
  412. if (r.getStorage() == NEW && r.getObjectId() == null) {
  413. expect.remove(r.getName());
  414. } else {
  415. expect.put(r.getName(), r);
  416. }
  417. }
  418. }
  419. List<Ref> expected = new ArrayList<>(expect.values());
  420. Collections.sort(expected, RefComparator.INSTANCE);
  421. return expected;
  422. }
  423. }