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 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  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.OBJECT_ID_LENGTH;
  46. import static org.eclipse.jgit.lib.Constants.R_HEADS;
  47. import static org.eclipse.jgit.lib.Ref.Storage.NEW;
  48. import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
  49. import static org.junit.Assert.assertEquals;
  50. import static org.junit.Assert.assertFalse;
  51. import static org.junit.Assert.assertNull;
  52. import static org.junit.Assert.assertTrue;
  53. import java.io.ByteArrayOutputStream;
  54. import java.io.IOException;
  55. import java.util.ArrayList;
  56. import java.util.Arrays;
  57. import java.util.Collection;
  58. import java.util.Collections;
  59. import java.util.HashMap;
  60. import java.util.List;
  61. import java.util.Map;
  62. import org.eclipse.jgit.internal.storage.io.BlockSource;
  63. import org.eclipse.jgit.lib.ObjectId;
  64. import org.eclipse.jgit.lib.ObjectIdRef;
  65. import org.eclipse.jgit.lib.Ref;
  66. import org.eclipse.jgit.lib.RefComparator;
  67. import org.junit.Test;
  68. public class MergedReftableTest {
  69. @Test
  70. public void noTables() throws IOException {
  71. MergedReftable mr = merge(new byte[0][]);
  72. try (RefCursor rc = mr.allRefs()) {
  73. assertFalse(rc.next());
  74. }
  75. try (RefCursor rc = mr.seekRef(HEAD)) {
  76. assertFalse(rc.next());
  77. }
  78. try (RefCursor rc = mr.seekRef(R_HEADS)) {
  79. assertFalse(rc.next());
  80. }
  81. }
  82. @Test
  83. public void oneEmptyTable() throws IOException {
  84. MergedReftable mr = merge(write());
  85. try (RefCursor rc = mr.allRefs()) {
  86. assertFalse(rc.next());
  87. }
  88. try (RefCursor rc = mr.seekRef(HEAD)) {
  89. assertFalse(rc.next());
  90. }
  91. try (RefCursor rc = mr.seekRef(R_HEADS)) {
  92. assertFalse(rc.next());
  93. }
  94. }
  95. @Test
  96. public void twoEmptyTables() throws IOException {
  97. MergedReftable mr = merge(write(), write());
  98. try (RefCursor rc = mr.allRefs()) {
  99. assertFalse(rc.next());
  100. }
  101. try (RefCursor rc = mr.seekRef(HEAD)) {
  102. assertFalse(rc.next());
  103. }
  104. try (RefCursor rc = mr.seekRef(R_HEADS)) {
  105. assertFalse(rc.next());
  106. }
  107. }
  108. @SuppressWarnings("boxing")
  109. @Test
  110. public void oneTableScan() throws IOException {
  111. List<Ref> refs = new ArrayList<>();
  112. for (int i = 1; i <= 567; i++) {
  113. refs.add(ref(String.format("refs/heads/%03d", i), i));
  114. }
  115. MergedReftable mr = merge(write(refs));
  116. try (RefCursor rc = mr.allRefs()) {
  117. for (Ref exp : refs) {
  118. assertTrue("has " + exp.getName(), rc.next());
  119. Ref act = rc.getRef();
  120. assertEquals(exp.getName(), act.getName());
  121. assertEquals(exp.getObjectId(), act.getObjectId());
  122. }
  123. assertFalse(rc.next());
  124. }
  125. }
  126. @Test
  127. public void deleteIsHidden() throws IOException {
  128. List<Ref> delta1 = Arrays.asList(
  129. ref("refs/heads/apple", 1),
  130. ref("refs/heads/master", 2));
  131. List<Ref> delta2 = Arrays.asList(delete("refs/heads/apple"));
  132. MergedReftable mr = merge(write(delta1), write(delta2));
  133. try (RefCursor rc = mr.allRefs()) {
  134. assertTrue(rc.next());
  135. assertEquals("refs/heads/master", rc.getRef().getName());
  136. assertEquals(id(2), rc.getRef().getObjectId());
  137. assertFalse(rc.next());
  138. }
  139. }
  140. @Test
  141. public void twoTableSeek() throws IOException {
  142. List<Ref> delta1 = Arrays.asList(
  143. ref("refs/heads/apple", 1),
  144. ref("refs/heads/master", 2));
  145. List<Ref> delta2 = Arrays.asList(ref("refs/heads/banana", 3));
  146. MergedReftable mr = merge(write(delta1), write(delta2));
  147. try (RefCursor rc = mr.seekRef("refs/heads/master")) {
  148. assertTrue(rc.next());
  149. assertEquals("refs/heads/master", rc.getRef().getName());
  150. assertEquals(id(2), rc.getRef().getObjectId());
  151. assertFalse(rc.next());
  152. }
  153. }
  154. @Test
  155. public void twoTableById() throws IOException {
  156. List<Ref> delta1 = Arrays.asList(
  157. ref("refs/heads/apple", 1),
  158. ref("refs/heads/master", 2));
  159. List<Ref> delta2 = Arrays.asList(ref("refs/heads/banana", 3));
  160. MergedReftable mr = merge(write(delta1), write(delta2));
  161. try (RefCursor rc = mr.byObjectId(id(2))) {
  162. assertTrue(rc.next());
  163. assertEquals("refs/heads/master", rc.getRef().getName());
  164. assertEquals(id(2), rc.getRef().getObjectId());
  165. assertFalse(rc.next());
  166. }
  167. }
  168. @SuppressWarnings("boxing")
  169. @Test
  170. public void fourTableScan() throws IOException {
  171. List<Ref> base = new ArrayList<>();
  172. for (int i = 1; i <= 567; i++) {
  173. base.add(ref(String.format("refs/heads/%03d", i), i));
  174. }
  175. List<Ref> delta1 = Arrays.asList(
  176. ref("refs/heads/next", 4),
  177. ref(String.format("refs/heads/%03d", 55), 4096));
  178. List<Ref> delta2 = Arrays.asList(
  179. delete("refs/heads/next"),
  180. ref(String.format("refs/heads/%03d", 55), 8192));
  181. List<Ref> delta3 = Arrays.asList(
  182. ref("refs/heads/master", 4242),
  183. ref(String.format("refs/heads/%03d", 42), 5120),
  184. ref(String.format("refs/heads/%03d", 98), 6120));
  185. List<Ref> expected = merge(base, delta1, delta2, delta3);
  186. MergedReftable mr = merge(
  187. write(base),
  188. write(delta1),
  189. write(delta2),
  190. write(delta3));
  191. try (RefCursor rc = mr.allRefs()) {
  192. for (Ref exp : expected) {
  193. assertTrue("has " + exp.getName(), rc.next());
  194. Ref act = rc.getRef();
  195. assertEquals(exp.getName(), act.getName());
  196. assertEquals(exp.getObjectId(), act.getObjectId());
  197. }
  198. assertFalse(rc.next());
  199. }
  200. }
  201. @Test
  202. public void scanIncludeDeletes() throws IOException {
  203. List<Ref> delta1 = Arrays.asList(ref("refs/heads/next", 4));
  204. List<Ref> delta2 = Arrays.asList(delete("refs/heads/next"));
  205. List<Ref> delta3 = Arrays.asList(ref("refs/heads/master", 8));
  206. MergedReftable mr = merge(write(delta1), write(delta2), write(delta3));
  207. mr.setIncludeDeletes(true);
  208. try (RefCursor rc = mr.allRefs()) {
  209. assertTrue(rc.next());
  210. Ref r = rc.getRef();
  211. assertEquals("refs/heads/master", r.getName());
  212. assertEquals(id(8), r.getObjectId());
  213. assertTrue(rc.next());
  214. r = rc.getRef();
  215. assertEquals("refs/heads/next", r.getName());
  216. assertEquals(NEW, r.getStorage());
  217. assertNull(r.getObjectId());
  218. assertFalse(rc.next());
  219. }
  220. }
  221. @SuppressWarnings("boxing")
  222. @Test
  223. public void oneTableSeek() throws IOException {
  224. List<Ref> refs = new ArrayList<>();
  225. for (int i = 1; i <= 567; i++) {
  226. refs.add(ref(String.format("refs/heads/%03d", i), i));
  227. }
  228. MergedReftable mr = merge(write(refs));
  229. for (Ref exp : refs) {
  230. try (RefCursor rc = mr.seekRef(exp.getName())) {
  231. assertTrue("has " + exp.getName(), rc.next());
  232. Ref act = rc.getRef();
  233. assertEquals(exp.getName(), act.getName());
  234. assertEquals(exp.getObjectId(), act.getObjectId());
  235. assertFalse(rc.next());
  236. }
  237. }
  238. }
  239. @Test
  240. public void missedUpdate() throws IOException {
  241. ByteArrayOutputStream buf = new ByteArrayOutputStream();
  242. ReftableWriter writer = new ReftableWriter()
  243. .setMinUpdateIndex(1)
  244. .setMaxUpdateIndex(3)
  245. .begin(buf);
  246. writer.writeRef(ref("refs/heads/a", 1), 1);
  247. writer.writeRef(ref("refs/heads/c", 3), 3);
  248. writer.finish();
  249. byte[] base = buf.toByteArray();
  250. byte[] delta = write(Arrays.asList(
  251. ref("refs/heads/b", 2),
  252. ref("refs/heads/c", 4)),
  253. 2);
  254. MergedReftable mr = merge(base, delta);
  255. try (RefCursor rc = mr.allRefs()) {
  256. assertTrue(rc.next());
  257. assertEquals("refs/heads/a", rc.getRef().getName());
  258. assertEquals(id(1), rc.getRef().getObjectId());
  259. assertEquals(1, rc.getUpdateIndex());
  260. assertTrue(rc.next());
  261. assertEquals("refs/heads/b", rc.getRef().getName());
  262. assertEquals(id(2), rc.getRef().getObjectId());
  263. assertEquals(2, rc.getUpdateIndex());
  264. assertTrue(rc.next());
  265. assertEquals("refs/heads/c", rc.getRef().getName());
  266. assertEquals(id(3), rc.getRef().getObjectId());
  267. assertEquals(3, rc.getUpdateIndex());
  268. }
  269. }
  270. @Test
  271. public void compaction() throws IOException {
  272. List<Ref> delta1 = Arrays.asList(
  273. ref("refs/heads/next", 4),
  274. ref("refs/heads/master", 1));
  275. List<Ref> delta2 = Arrays.asList(delete("refs/heads/next"));
  276. List<Ref> delta3 = Arrays.asList(ref("refs/heads/master", 8));
  277. ReftableCompactor compactor = new ReftableCompactor();
  278. compactor.addAll(Arrays.asList(
  279. read(write(delta1)),
  280. read(write(delta2)),
  281. read(write(delta3))));
  282. ByteArrayOutputStream out = new ByteArrayOutputStream();
  283. compactor.compact(out);
  284. byte[] table = out.toByteArray();
  285. ReftableReader reader = read(table);
  286. try (RefCursor rc = reader.allRefs()) {
  287. assertTrue(rc.next());
  288. Ref r = rc.getRef();
  289. assertEquals("refs/heads/master", r.getName());
  290. assertEquals(id(8), r.getObjectId());
  291. assertFalse(rc.next());
  292. }
  293. }
  294. private static MergedReftable merge(byte[]... table) {
  295. List<Reftable> stack = new ArrayList<>(table.length);
  296. for (byte[] b : table) {
  297. stack.add(read(b));
  298. }
  299. return new MergedReftable(stack);
  300. }
  301. private static ReftableReader read(byte[] table) {
  302. return new ReftableReader(BlockSource.from(table));
  303. }
  304. private static Ref ref(String name, int id) {
  305. return new ObjectIdRef.PeeledNonTag(PACKED, name, id(id));
  306. }
  307. private static Ref delete(String name) {
  308. return new ObjectIdRef.Unpeeled(NEW, name, null);
  309. }
  310. private static ObjectId id(int i) {
  311. byte[] buf = new byte[OBJECT_ID_LENGTH];
  312. buf[0] = (byte) (i & 0xff);
  313. buf[1] = (byte) ((i >>> 8) & 0xff);
  314. buf[2] = (byte) ((i >>> 16) & 0xff);
  315. buf[3] = (byte) (i >>> 24);
  316. return ObjectId.fromRaw(buf);
  317. }
  318. private byte[] write(Ref... refs) throws IOException {
  319. return write(Arrays.asList(refs));
  320. }
  321. private byte[] write(Collection<Ref> refs) throws IOException {
  322. return write(refs, 1);
  323. }
  324. private byte[] write(Collection<Ref> refs, long updateIndex)
  325. throws IOException {
  326. ByteArrayOutputStream buffer = new ByteArrayOutputStream();
  327. new ReftableWriter()
  328. .setMinUpdateIndex(updateIndex)
  329. .setMaxUpdateIndex(updateIndex)
  330. .begin(buffer)
  331. .sortAndWriteRefs(refs)
  332. .finish();
  333. return buffer.toByteArray();
  334. }
  335. @SafeVarargs
  336. private static List<Ref> merge(List<Ref>... tables) {
  337. Map<String, Ref> expect = new HashMap<>();
  338. for (List<Ref> t : tables) {
  339. for (Ref r : t) {
  340. if (r.getStorage() == NEW && r.getObjectId() == null) {
  341. expect.remove(r.getName());
  342. } else {
  343. expect.put(r.getName(), r);
  344. }
  345. }
  346. }
  347. List<Ref> expected = new ArrayList<>(expect.values());
  348. Collections.sort(expected, RefComparator.INSTANCE);
  349. return expected;
  350. }
  351. }