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.

PackWriterTest.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. /*
  2. * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  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.file;
  44. import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
  45. import static org.junit.Assert.assertEquals;
  46. import static org.junit.Assert.assertFalse;
  47. import static org.junit.Assert.assertNotNull;
  48. import static org.junit.Assert.assertTrue;
  49. import static org.junit.Assert.fail;
  50. import java.io.ByteArrayInputStream;
  51. import java.io.ByteArrayOutputStream;
  52. import java.io.File;
  53. import java.io.FileOutputStream;
  54. import java.io.IOException;
  55. import java.text.ParseException;
  56. import java.util.ArrayList;
  57. import java.util.Arrays;
  58. import java.util.Collections;
  59. import java.util.Comparator;
  60. import java.util.HashSet;
  61. import java.util.List;
  62. import java.util.Set;
  63. import org.eclipse.jgit.errors.MissingObjectException;
  64. import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
  65. import org.eclipse.jgit.internal.storage.pack.PackWriter;
  66. import org.eclipse.jgit.junit.JGitTestUtil;
  67. import org.eclipse.jgit.junit.TestRepository;
  68. import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
  69. import org.eclipse.jgit.lib.NullProgressMonitor;
  70. import org.eclipse.jgit.lib.ObjectId;
  71. import org.eclipse.jgit.lib.ObjectIdSet;
  72. import org.eclipse.jgit.lib.ObjectInserter;
  73. import org.eclipse.jgit.revwalk.RevBlob;
  74. import org.eclipse.jgit.revwalk.RevCommit;
  75. import org.eclipse.jgit.revwalk.RevObject;
  76. import org.eclipse.jgit.revwalk.RevWalk;
  77. import org.eclipse.jgit.storage.pack.PackConfig;
  78. import org.eclipse.jgit.storage.pack.PackStatistics;
  79. import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
  80. import org.eclipse.jgit.transport.PackParser;
  81. import org.junit.After;
  82. import org.junit.Before;
  83. import org.junit.Test;
  84. public class PackWriterTest extends SampleDataRepositoryTestCase {
  85. private static final Set<ObjectId> EMPTY_SET_OBJECT = Collections
  86. .<ObjectId> emptySet();
  87. private static final List<RevObject> EMPTY_LIST_REVS = Collections
  88. .<RevObject> emptyList();
  89. private PackConfig config;
  90. private PackWriter writer;
  91. private ByteArrayOutputStream os;
  92. private PackFile pack;
  93. private ObjectInserter inserter;
  94. private FileRepository dst;
  95. @Before
  96. public void setUp() throws Exception {
  97. super.setUp();
  98. os = new ByteArrayOutputStream();
  99. config = new PackConfig(db);
  100. dst = createBareRepository();
  101. File alt = new File(dst.getObjectDatabase().getDirectory(), "info/alternates");
  102. alt.getParentFile().mkdirs();
  103. write(alt, db.getObjectDatabase().getDirectory().getAbsolutePath() + "\n");
  104. }
  105. @After
  106. public void tearDown() throws Exception {
  107. if (writer != null) {
  108. writer.close();
  109. writer = null;
  110. }
  111. if (inserter != null) {
  112. inserter.close();
  113. inserter = null;
  114. }
  115. super.tearDown();
  116. }
  117. /**
  118. * Test constructor for exceptions, default settings, initialization.
  119. *
  120. * @throws IOException
  121. */
  122. @Test
  123. public void testContructor() throws IOException {
  124. writer = new PackWriter(config, db.newObjectReader());
  125. assertFalse(writer.isDeltaBaseAsOffset());
  126. assertTrue(config.isReuseDeltas());
  127. assertTrue(config.isReuseObjects());
  128. assertEquals(0, writer.getObjectCount());
  129. }
  130. /**
  131. * Change default settings and verify them.
  132. */
  133. @Test
  134. public void testModifySettings() {
  135. config.setReuseDeltas(false);
  136. config.setReuseObjects(false);
  137. config.setDeltaBaseAsOffset(false);
  138. assertFalse(config.isReuseDeltas());
  139. assertFalse(config.isReuseObjects());
  140. assertFalse(config.isDeltaBaseAsOffset());
  141. writer = new PackWriter(config, db.newObjectReader());
  142. writer.setDeltaBaseAsOffset(true);
  143. assertTrue(writer.isDeltaBaseAsOffset());
  144. assertFalse(config.isDeltaBaseAsOffset());
  145. }
  146. /**
  147. * Write empty pack by providing empty sets of interesting/uninteresting
  148. * objects and check for correct format.
  149. *
  150. * @throws IOException
  151. */
  152. @Test
  153. public void testWriteEmptyPack1() throws IOException {
  154. createVerifyOpenPack(EMPTY_SET_OBJECT, EMPTY_SET_OBJECT, false, false);
  155. assertEquals(0, writer.getObjectCount());
  156. assertEquals(0, pack.getObjectCount());
  157. assertEquals("da39a3ee5e6b4b0d3255bfef95601890afd80709", writer
  158. .computeName().name());
  159. }
  160. /**
  161. * Write empty pack by providing empty iterator of objects to write and
  162. * check for correct format.
  163. *
  164. * @throws IOException
  165. */
  166. @Test
  167. public void testWriteEmptyPack2() throws IOException {
  168. createVerifyOpenPack(EMPTY_LIST_REVS);
  169. assertEquals(0, writer.getObjectCount());
  170. assertEquals(0, pack.getObjectCount());
  171. }
  172. /**
  173. * Try to pass non-existing object as uninteresting, with non-ignoring
  174. * setting.
  175. *
  176. * @throws IOException
  177. */
  178. @Test
  179. public void testNotIgnoreNonExistingObjects() throws IOException {
  180. final ObjectId nonExisting = ObjectId
  181. .fromString("0000000000000000000000000000000000000001");
  182. try {
  183. createVerifyOpenPack(EMPTY_SET_OBJECT, Collections.singleton(
  184. nonExisting), false, false);
  185. fail("Should have thrown MissingObjectException");
  186. } catch (MissingObjectException x) {
  187. // expected
  188. }
  189. }
  190. /**
  191. * Try to pass non-existing object as uninteresting, with ignoring setting.
  192. *
  193. * @throws IOException
  194. */
  195. @Test
  196. public void testIgnoreNonExistingObjects() throws IOException {
  197. final ObjectId nonExisting = ObjectId
  198. .fromString("0000000000000000000000000000000000000001");
  199. createVerifyOpenPack(EMPTY_SET_OBJECT, Collections.singleton(
  200. nonExisting), false, true);
  201. // shouldn't throw anything
  202. }
  203. /**
  204. * Try to pass non-existing object as uninteresting, with ignoring setting.
  205. * Use a repo with bitmap indexes because then PackWriter will use
  206. * PackWriterBitmapWalker which had problems with this situation.
  207. *
  208. * @throws IOException
  209. * @throws ParseException
  210. */
  211. @Test
  212. public void testIgnoreNonExistingObjectsWithBitmaps() throws IOException,
  213. ParseException {
  214. final ObjectId nonExisting = ObjectId
  215. .fromString("0000000000000000000000000000000000000001");
  216. new GC(db).gc();
  217. createVerifyOpenPack(EMPTY_SET_OBJECT,
  218. Collections.singleton(nonExisting), false, true, true);
  219. // shouldn't throw anything
  220. }
  221. /**
  222. * Create pack basing on only interesting objects, then precisely verify
  223. * content. No delta reuse here.
  224. *
  225. * @throws IOException
  226. */
  227. @Test
  228. public void testWritePack1() throws IOException {
  229. config.setReuseDeltas(false);
  230. writeVerifyPack1();
  231. }
  232. /**
  233. * Test writing pack without object reuse. Pack content/preparation as in
  234. * {@link #testWritePack1()}.
  235. *
  236. * @throws IOException
  237. */
  238. @Test
  239. public void testWritePack1NoObjectReuse() throws IOException {
  240. config.setReuseDeltas(false);
  241. config.setReuseObjects(false);
  242. writeVerifyPack1();
  243. }
  244. /**
  245. * Create pack basing on both interesting and uninteresting objects, then
  246. * precisely verify content. No delta reuse here.
  247. *
  248. * @throws IOException
  249. */
  250. @Test
  251. public void testWritePack2() throws IOException {
  252. writeVerifyPack2(false);
  253. }
  254. /**
  255. * Test pack writing with deltas reuse, delta-base first rule. Pack
  256. * content/preparation as in {@link #testWritePack2()}.
  257. *
  258. * @throws IOException
  259. */
  260. @Test
  261. public void testWritePack2DeltasReuseRefs() throws IOException {
  262. writeVerifyPack2(true);
  263. }
  264. /**
  265. * Test pack writing with delta reuse. Delta bases referred as offsets. Pack
  266. * configuration as in {@link #testWritePack2DeltasReuseRefs()}.
  267. *
  268. * @throws IOException
  269. */
  270. @Test
  271. public void testWritePack2DeltasReuseOffsets() throws IOException {
  272. config.setDeltaBaseAsOffset(true);
  273. writeVerifyPack2(true);
  274. }
  275. /**
  276. * Test pack writing with delta reuse. Raw-data copy (reuse) is made on a
  277. * pack with CRC32 index. Pack configuration as in
  278. * {@link #testWritePack2DeltasReuseRefs()}.
  279. *
  280. * @throws IOException
  281. */
  282. @Test
  283. public void testWritePack2DeltasCRC32Copy() throws IOException {
  284. final File packDir = new File(db.getObjectDatabase().getDirectory(), "pack");
  285. final File crc32Pack = new File(packDir,
  286. "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack");
  287. final File crc32Idx = new File(packDir,
  288. "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idx");
  289. copyFile(JGitTestUtil.getTestResourceFile(
  290. "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idxV2"),
  291. crc32Idx);
  292. db.openPack(crc32Pack);
  293. writeVerifyPack2(true);
  294. }
  295. /**
  296. * Create pack basing on fixed objects list, then precisely verify content.
  297. * No delta reuse here.
  298. *
  299. * @throws IOException
  300. * @throws MissingObjectException
  301. *
  302. */
  303. @Test
  304. public void testWritePack3() throws MissingObjectException, IOException {
  305. config.setReuseDeltas(false);
  306. final ObjectId forcedOrder[] = new ObjectId[] {
  307. ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
  308. ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"),
  309. ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
  310. ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"),
  311. ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"),
  312. ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") };
  313. final RevWalk parser = new RevWalk(db);
  314. final RevObject forcedOrderRevs[] = new RevObject[forcedOrder.length];
  315. for (int i = 0; i < forcedOrder.length; i++)
  316. forcedOrderRevs[i] = parser.parseAny(forcedOrder[i]);
  317. createVerifyOpenPack(Arrays.asList(forcedOrderRevs));
  318. assertEquals(forcedOrder.length, writer.getObjectCount());
  319. verifyObjectsOrder(forcedOrder);
  320. assertEquals("ed3f96b8327c7c66b0f8f70056129f0769323d86", writer
  321. .computeName().name());
  322. }
  323. /**
  324. * Another pack creation: basing on both interesting and uninteresting
  325. * objects. No delta reuse possible here, as this is a specific case when we
  326. * write only 1 commit, associated with 1 tree, 1 blob.
  327. *
  328. * @throws IOException
  329. */
  330. @Test
  331. public void testWritePack4() throws IOException {
  332. writeVerifyPack4(false);
  333. }
  334. /**
  335. * Test thin pack writing: 1 blob delta base is on objects edge. Pack
  336. * configuration as in {@link #testWritePack4()}.
  337. *
  338. * @throws IOException
  339. */
  340. @Test
  341. public void testWritePack4ThinPack() throws IOException {
  342. writeVerifyPack4(true);
  343. }
  344. /**
  345. * Compare sizes of packs created using {@link #testWritePack2()} and
  346. * {@link #testWritePack2DeltasReuseRefs()}. The pack using deltas should
  347. * be smaller.
  348. *
  349. * @throws Exception
  350. */
  351. @Test
  352. public void testWritePack2SizeDeltasVsNoDeltas() throws Exception {
  353. testWritePack2();
  354. final long sizePack2NoDeltas = os.size();
  355. tearDown();
  356. setUp();
  357. testWritePack2DeltasReuseRefs();
  358. final long sizePack2DeltasRefs = os.size();
  359. assertTrue(sizePack2NoDeltas > sizePack2DeltasRefs);
  360. }
  361. /**
  362. * Compare sizes of packs created using
  363. * {@link #testWritePack2DeltasReuseRefs()} and
  364. * {@link #testWritePack2DeltasReuseOffsets()}. The pack with delta bases
  365. * written as offsets should be smaller.
  366. *
  367. * @throws Exception
  368. */
  369. @Test
  370. public void testWritePack2SizeOffsetsVsRefs() throws Exception {
  371. testWritePack2DeltasReuseRefs();
  372. final long sizePack2DeltasRefs = os.size();
  373. tearDown();
  374. setUp();
  375. testWritePack2DeltasReuseOffsets();
  376. final long sizePack2DeltasOffsets = os.size();
  377. assertTrue(sizePack2DeltasRefs > sizePack2DeltasOffsets);
  378. }
  379. /**
  380. * Compare sizes of packs created using {@link #testWritePack4()} and
  381. * {@link #testWritePack4ThinPack()}. Obviously, the thin pack should be
  382. * smaller.
  383. *
  384. * @throws Exception
  385. */
  386. @Test
  387. public void testWritePack4SizeThinVsNoThin() throws Exception {
  388. testWritePack4();
  389. final long sizePack4 = os.size();
  390. tearDown();
  391. setUp();
  392. testWritePack4ThinPack();
  393. final long sizePack4Thin = os.size();
  394. assertTrue(sizePack4 > sizePack4Thin);
  395. }
  396. @Test
  397. public void testDeltaStatistics() throws Exception {
  398. config.setDeltaCompress(true);
  399. FileRepository repo = createBareRepository();
  400. TestRepository<FileRepository> testRepo = new TestRepository<FileRepository>(repo);
  401. ArrayList<RevObject> blobs = new ArrayList<>();
  402. blobs.add(testRepo.blob(genDeltableData(1000)));
  403. blobs.add(testRepo.blob(genDeltableData(1005)));
  404. try (PackWriter pw = new PackWriter(repo)) {
  405. NullProgressMonitor m = NullProgressMonitor.INSTANCE;
  406. pw.preparePack(blobs.iterator());
  407. pw.writePack(m, m, os);
  408. PackStatistics stats = pw.getStatistics();
  409. assertEquals(1, stats.getTotalDeltas());
  410. assertTrue("Delta bytes not set.",
  411. stats.byObjectType(OBJ_BLOB).getDeltaBytes() > 0);
  412. }
  413. }
  414. // Generate consistent junk data for building files that delta well
  415. private String genDeltableData(int length) {
  416. assertTrue("Generated data must have a length > 0", length > 0);
  417. char[] data = {'a', 'b', 'c', '\n'};
  418. StringBuilder builder = new StringBuilder(length);
  419. for (int i = 0; i < length; i++) {
  420. builder.append(data[i % 4]);
  421. }
  422. return builder.toString();
  423. }
  424. @Test
  425. public void testWriteIndex() throws Exception {
  426. config.setIndexVersion(2);
  427. writeVerifyPack4(false);
  428. File packFile = pack.getPackFile();
  429. String name = packFile.getName();
  430. String base = name.substring(0, name.lastIndexOf('.'));
  431. File indexFile = new File(packFile.getParentFile(), base + ".idx");
  432. // Validate that IndexPack came up with the right CRC32 value.
  433. final PackIndex idx1 = PackIndex.open(indexFile);
  434. assertTrue(idx1 instanceof PackIndexV2);
  435. assertEquals(0x4743F1E4L, idx1.findCRC32(ObjectId
  436. .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")));
  437. // Validate that an index written by PackWriter is the same.
  438. final File idx2File = new File(indexFile.getAbsolutePath() + ".2");
  439. final FileOutputStream is = new FileOutputStream(idx2File);
  440. try {
  441. writer.writeIndex(is);
  442. } finally {
  443. is.close();
  444. }
  445. final PackIndex idx2 = PackIndex.open(idx2File);
  446. assertTrue(idx2 instanceof PackIndexV2);
  447. assertEquals(idx1.getObjectCount(), idx2.getObjectCount());
  448. assertEquals(idx1.getOffset64Count(), idx2.getOffset64Count());
  449. for (int i = 0; i < idx1.getObjectCount(); i++) {
  450. final ObjectId id = idx1.getObjectId(i);
  451. assertEquals(id, idx2.getObjectId(i));
  452. assertEquals(idx1.findOffset(id), idx2.findOffset(id));
  453. assertEquals(idx1.findCRC32(id), idx2.findCRC32(id));
  454. }
  455. }
  456. @Test
  457. public void testExclude() throws Exception {
  458. FileRepository repo = createBareRepository();
  459. TestRepository<FileRepository> testRepo = new TestRepository<FileRepository>(
  460. repo);
  461. BranchBuilder bb = testRepo.branch("refs/heads/master");
  462. RevBlob contentA = testRepo.blob("A");
  463. RevCommit c1 = bb.commit().add("f", contentA).create();
  464. testRepo.getRevWalk().parseHeaders(c1);
  465. PackIndex pf1 = writePack(repo, Collections.singleton(c1),
  466. Collections.<ObjectIdSet> emptySet());
  467. assertContent(
  468. pf1,
  469. Arrays.asList(c1.getId(), c1.getTree().getId(),
  470. contentA.getId()));
  471. RevBlob contentB = testRepo.blob("B");
  472. RevCommit c2 = bb.commit().add("f", contentB).create();
  473. testRepo.getRevWalk().parseHeaders(c2);
  474. PackIndex pf2 = writePack(repo, Collections.singleton(c2),
  475. Collections.<ObjectIdSet> singleton(pf1));
  476. assertContent(
  477. pf2,
  478. Arrays.asList(c2.getId(), c2.getTree().getId(),
  479. contentB.getId()));
  480. }
  481. private static void assertContent(PackIndex pi, List<ObjectId> expected) {
  482. assertEquals("Pack index has wrong size.", expected.size(),
  483. pi.getObjectCount());
  484. for (int i = 0; i < pi.getObjectCount(); i++)
  485. assertTrue(
  486. "Pack index didn't contain the expected id "
  487. + pi.getObjectId(i),
  488. expected.contains(pi.getObjectId(i)));
  489. }
  490. private static PackIndex writePack(FileRepository repo,
  491. Set<? extends ObjectId> want, Set<ObjectIdSet> excludeObjects)
  492. throws IOException {
  493. try (PackWriter pw = new PackWriter(repo)) {
  494. pw.setDeltaBaseAsOffset(true);
  495. pw.setReuseDeltaCommits(false);
  496. for (ObjectIdSet idx : excludeObjects)
  497. pw.excludeObjects(idx);
  498. pw.preparePack(NullProgressMonitor.INSTANCE, want,
  499. Collections.<ObjectId> emptySet());
  500. String id = pw.computeName().getName();
  501. File packdir = new File(repo.getObjectsDirectory(), "pack");
  502. File packFile = new File(packdir, "pack-" + id + ".pack");
  503. FileOutputStream packOS = new FileOutputStream(packFile);
  504. pw.writePack(NullProgressMonitor.INSTANCE,
  505. NullProgressMonitor.INSTANCE, packOS);
  506. packOS.close();
  507. File idxFile = new File(packdir, "pack-" + id + ".idx");
  508. FileOutputStream idxOS = new FileOutputStream(idxFile);
  509. pw.writeIndex(idxOS);
  510. idxOS.close();
  511. return PackIndex.open(idxFile);
  512. }
  513. }
  514. // TODO: testWritePackDeltasCycle()
  515. // TODO: testWritePackDeltasDepth()
  516. private void writeVerifyPack1() throws IOException {
  517. final HashSet<ObjectId> interestings = new HashSet<ObjectId>();
  518. interestings.add(ObjectId
  519. .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
  520. createVerifyOpenPack(interestings, EMPTY_SET_OBJECT, false, false);
  521. final ObjectId expectedOrder[] = new ObjectId[] {
  522. ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
  523. ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"),
  524. ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"),
  525. ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
  526. ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"),
  527. ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"),
  528. ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"),
  529. ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") };
  530. assertEquals(expectedOrder.length, writer.getObjectCount());
  531. verifyObjectsOrder(expectedOrder);
  532. assertEquals("34be9032ac282b11fa9babdc2b2a93ca996c9c2f", writer
  533. .computeName().name());
  534. }
  535. private void writeVerifyPack2(boolean deltaReuse) throws IOException {
  536. config.setReuseDeltas(deltaReuse);
  537. final HashSet<ObjectId> interestings = new HashSet<ObjectId>();
  538. interestings.add(ObjectId
  539. .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
  540. final HashSet<ObjectId> uninterestings = new HashSet<ObjectId>();
  541. uninterestings.add(ObjectId
  542. .fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"));
  543. createVerifyOpenPack(interestings, uninterestings, false, false);
  544. final ObjectId expectedOrder[] = new ObjectId[] {
  545. ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
  546. ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"),
  547. ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
  548. ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"),
  549. ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"),
  550. ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") };
  551. if (deltaReuse) {
  552. // objects order influenced (swapped) by delta-base first rule
  553. ObjectId temp = expectedOrder[4];
  554. expectedOrder[4] = expectedOrder[5];
  555. expectedOrder[5] = temp;
  556. }
  557. assertEquals(expectedOrder.length, writer.getObjectCount());
  558. verifyObjectsOrder(expectedOrder);
  559. assertEquals("ed3f96b8327c7c66b0f8f70056129f0769323d86", writer
  560. .computeName().name());
  561. }
  562. private void writeVerifyPack4(final boolean thin) throws IOException {
  563. final HashSet<ObjectId> interestings = new HashSet<ObjectId>();
  564. interestings.add(ObjectId
  565. .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
  566. final HashSet<ObjectId> uninterestings = new HashSet<ObjectId>();
  567. uninterestings.add(ObjectId
  568. .fromString("c59759f143fb1fe21c197981df75a7ee00290799"));
  569. createVerifyOpenPack(interestings, uninterestings, thin, false);
  570. final ObjectId writtenObjects[] = new ObjectId[] {
  571. ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
  572. ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
  573. ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") };
  574. assertEquals(writtenObjects.length, writer.getObjectCount());
  575. ObjectId expectedObjects[];
  576. if (thin) {
  577. expectedObjects = new ObjectId[4];
  578. System.arraycopy(writtenObjects, 0, expectedObjects, 0,
  579. writtenObjects.length);
  580. expectedObjects[3] = ObjectId
  581. .fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3");
  582. } else {
  583. expectedObjects = writtenObjects;
  584. }
  585. verifyObjectsOrder(expectedObjects);
  586. assertEquals("cded4b74176b4456afa456768b2b5aafb41c44fc", writer
  587. .computeName().name());
  588. }
  589. private void createVerifyOpenPack(final Set<ObjectId> interestings,
  590. final Set<ObjectId> uninterestings, final boolean thin,
  591. final boolean ignoreMissingUninteresting)
  592. throws MissingObjectException, IOException {
  593. createVerifyOpenPack(interestings, uninterestings, thin,
  594. ignoreMissingUninteresting, false);
  595. }
  596. private void createVerifyOpenPack(final Set<ObjectId> interestings,
  597. final Set<ObjectId> uninterestings, final boolean thin,
  598. final boolean ignoreMissingUninteresting, boolean useBitmaps)
  599. throws MissingObjectException, IOException {
  600. NullProgressMonitor m = NullProgressMonitor.INSTANCE;
  601. writer = new PackWriter(config, db.newObjectReader());
  602. writer.setUseBitmaps(useBitmaps);
  603. writer.setThin(thin);
  604. writer.setIgnoreMissingUninteresting(ignoreMissingUninteresting);
  605. writer.preparePack(m, interestings, uninterestings);
  606. writer.writePack(m, m, os);
  607. writer.close();
  608. verifyOpenPack(thin);
  609. }
  610. private void createVerifyOpenPack(final List<RevObject> objectSource)
  611. throws MissingObjectException, IOException {
  612. NullProgressMonitor m = NullProgressMonitor.INSTANCE;
  613. writer = new PackWriter(config, db.newObjectReader());
  614. writer.preparePack(objectSource.iterator());
  615. assertEquals(objectSource.size(), writer.getObjectCount());
  616. writer.writePack(m, m, os);
  617. writer.close();
  618. verifyOpenPack(false);
  619. }
  620. private void verifyOpenPack(final boolean thin) throws IOException {
  621. final byte[] packData = os.toByteArray();
  622. if (thin) {
  623. PackParser p = index(packData);
  624. try {
  625. p.parse(NullProgressMonitor.INSTANCE);
  626. fail("indexer should grumble about missing object");
  627. } catch (IOException x) {
  628. // expected
  629. }
  630. }
  631. ObjectDirectoryPackParser p = (ObjectDirectoryPackParser) index(packData);
  632. p.setKeepEmpty(true);
  633. p.setAllowThin(thin);
  634. p.setIndexVersion(2);
  635. p.parse(NullProgressMonitor.INSTANCE);
  636. pack = p.getPackFile();
  637. assertNotNull("have PackFile after parsing", pack);
  638. }
  639. private PackParser index(final byte[] packData) throws IOException {
  640. if (inserter == null)
  641. inserter = dst.newObjectInserter();
  642. return inserter.newPackParser(new ByteArrayInputStream(packData));
  643. }
  644. private void verifyObjectsOrder(final ObjectId objectsOrder[]) {
  645. final List<PackIndex.MutableEntry> entries = new ArrayList<PackIndex.MutableEntry>();
  646. for (MutableEntry me : pack) {
  647. entries.add(me.cloneEntry());
  648. }
  649. Collections.sort(entries, new Comparator<PackIndex.MutableEntry>() {
  650. public int compare(MutableEntry o1, MutableEntry o2) {
  651. return Long.signum(o1.getOffset() - o2.getOffset());
  652. }
  653. });
  654. int i = 0;
  655. for (MutableEntry me : entries) {
  656. assertEquals(objectsOrder[i++].toObjectId(), me.toObjectId());
  657. }
  658. }
  659. }