1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.internal.storage.file;
12
13 import static org.eclipse.jgit.internal.storage.pack.PackWriter.NONE;
14 import static org.eclipse.jgit.lib.Constants.INFO_ALTERNATES;
15 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
16 import static org.junit.Assert.assertEquals;
17 import static org.junit.Assert.assertFalse;
18 import static org.junit.Assert.assertNotNull;
19 import static org.junit.Assert.assertTrue;
20 import static org.junit.Assert.fail;
21
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.File;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.text.ParseException;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Set;
34
35 import org.eclipse.jgit.errors.MissingObjectException;
36 import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
37 import org.eclipse.jgit.internal.storage.pack.PackWriter;
38 import org.eclipse.jgit.junit.JGitTestUtil;
39 import org.eclipse.jgit.junit.TestRepository;
40 import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
41 import org.eclipse.jgit.lib.NullProgressMonitor;
42 import org.eclipse.jgit.lib.ObjectId;
43 import org.eclipse.jgit.lib.ObjectIdSet;
44 import org.eclipse.jgit.lib.ObjectInserter;
45 import org.eclipse.jgit.lib.Repository;
46 import org.eclipse.jgit.lib.Sets;
47 import org.eclipse.jgit.revwalk.DepthWalk;
48 import org.eclipse.jgit.revwalk.ObjectWalk;
49 import org.eclipse.jgit.revwalk.RevBlob;
50 import org.eclipse.jgit.revwalk.RevCommit;
51 import org.eclipse.jgit.revwalk.RevObject;
52 import org.eclipse.jgit.revwalk.RevWalk;
53 import org.eclipse.jgit.storage.pack.PackConfig;
54 import org.eclipse.jgit.storage.pack.PackStatistics;
55 import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
56 import org.eclipse.jgit.transport.PackParser;
57 import org.junit.After;
58 import org.junit.Before;
59 import org.junit.Test;
60
61 public class PackWriterTest extends SampleDataRepositoryTestCase {
62
63 private static final List<RevObject> EMPTY_LIST_REVS = Collections
64 .<RevObject> emptyList();
65
66 private static final Set<ObjectIdSet> EMPTY_ID_SET = Collections
67 .<ObjectIdSet> emptySet();
68
69 private PackConfig config;
70
71 private PackWriter writer;
72
73 private ByteArrayOutputStream os;
74
75 private PackFile pack;
76
77 private ObjectInserter inserter;
78
79 private FileRepository dst;
80
81 private RevBlob contentA;
82
83 private RevBlob contentB;
84
85 private RevBlob contentC;
86
87 private RevBlob contentD;
88
89 private RevBlob contentE;
90
91 private RevCommit c1;
92
93 private RevCommit c2;
94
95 private RevCommit c3;
96
97 private RevCommit c4;
98
99 private RevCommit c5;
100
101 @Override
102 @Before
103 public void setUp() throws Exception {
104 super.setUp();
105 os = new ByteArrayOutputStream();
106 config = new PackConfig(db);
107
108 dst = createBareRepository();
109 File alt = new File(dst.getObjectDatabase().getDirectory(), INFO_ALTERNATES);
110 alt.getParentFile().mkdirs();
111 write(alt, db.getObjectDatabase().getDirectory().getAbsolutePath() + "\n");
112 }
113
114 @Override
115 @After
116 public void tearDown() throws Exception {
117 if (writer != null) {
118 writer.close();
119 writer = null;
120 }
121 if (inserter != null) {
122 inserter.close();
123 inserter = null;
124 }
125 super.tearDown();
126 }
127
128
129
130
131
132
133 @Test
134 public void testContructor() throws IOException {
135 writer = new PackWriter(config, db.newObjectReader());
136 assertFalse(writer.isDeltaBaseAsOffset());
137 assertTrue(config.isReuseDeltas());
138 assertTrue(config.isReuseObjects());
139 assertEquals(0, writer.getObjectCount());
140 }
141
142
143
144
145 @Test
146 public void testModifySettings() {
147 config.setReuseDeltas(false);
148 config.setReuseObjects(false);
149 config.setDeltaBaseAsOffset(false);
150 assertFalse(config.isReuseDeltas());
151 assertFalse(config.isReuseObjects());
152 assertFalse(config.isDeltaBaseAsOffset());
153
154 writer = new PackWriter(config, db.newObjectReader());
155 writer.setDeltaBaseAsOffset(true);
156 assertTrue(writer.isDeltaBaseAsOffset());
157 assertFalse(config.isDeltaBaseAsOffset());
158 }
159
160
161
162
163
164
165
166 @Test
167 public void testWriteEmptyPack1() throws IOException {
168 createVerifyOpenPack(NONE, NONE, false, false);
169
170 assertEquals(0, writer.getObjectCount());
171 assertEquals(0, pack.getObjectCount());
172 assertEquals("da39a3ee5e6b4b0d3255bfef95601890afd80709", writer
173 .computeName().name());
174 }
175
176
177
178
179
180
181
182 @Test
183 public void testWriteEmptyPack2() throws IOException {
184 createVerifyOpenPack(EMPTY_LIST_REVS);
185
186 assertEquals(0, writer.getObjectCount());
187 assertEquals(0, pack.getObjectCount());
188 }
189
190
191
192
193
194
195
196 @Test
197 public void testNotIgnoreNonExistingObjects() throws IOException {
198 final ObjectId nonExisting = ObjectId
199 .fromString("0000000000000000000000000000000000000001");
200 try {
201 createVerifyOpenPack(NONE, haves(nonExisting), false, false);
202 fail("Should have thrown MissingObjectException");
203 } catch (MissingObjectException x) {
204
205 }
206 }
207
208
209
210
211
212
213 @Test
214 public void testIgnoreNonExistingObjects() throws IOException {
215 final ObjectId nonExisting = ObjectId
216 .fromString("0000000000000000000000000000000000000001");
217 createVerifyOpenPack(NONE, haves(nonExisting), false, true);
218
219 }
220
221
222
223
224
225
226
227
228
229 @Test
230 public void testIgnoreNonExistingObjectsWithBitmaps() throws IOException,
231 ParseException {
232 final ObjectId nonExisting = ObjectId
233 .fromString("0000000000000000000000000000000000000001");
234 new GC(db).gc();
235 createVerifyOpenPack(NONE, haves(nonExisting), false, true, true);
236
237 }
238
239
240
241
242
243
244
245 @Test
246 public void testWritePack1() throws IOException {
247 config.setReuseDeltas(false);
248 writeVerifyPack1();
249 }
250
251
252
253
254
255
256
257 @Test
258 public void testWritePack1NoObjectReuse() throws IOException {
259 config.setReuseDeltas(false);
260 config.setReuseObjects(false);
261 writeVerifyPack1();
262 }
263
264
265
266
267
268
269
270 @Test
271 public void testWritePack2() throws IOException {
272 writeVerifyPack2(false);
273 }
274
275
276
277
278
279
280
281 @Test
282 public void testWritePack2DeltasReuseRefs() throws IOException {
283 writeVerifyPack2(true);
284 }
285
286
287
288
289
290
291
292 @Test
293 public void testWritePack2DeltasReuseOffsets() throws IOException {
294 config.setDeltaBaseAsOffset(true);
295 writeVerifyPack2(true);
296 }
297
298
299
300
301
302
303
304
305 @Test
306 public void testWritePack2DeltasCRC32Copy() throws IOException {
307 final File packDir = db.getObjectDatabase().getPackDirectory();
308 final File crc32Pack = new File(packDir,
309 "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack");
310 final File crc32Idx = new File(packDir,
311 "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idx");
312 copyFile(JGitTestUtil.getTestResourceFile(
313 "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idxV2"),
314 crc32Idx);
315 db.openPack(crc32Pack);
316
317 writeVerifyPack2(true);
318 }
319
320
321
322
323
324
325
326
327
328 @Test
329 public void testWritePack3() throws MissingObjectException, IOException {
330 config.setReuseDeltas(false);
331 final ObjectId forcedOrder[] = new ObjectId[] {
332 ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
333 ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"),
334 ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
335 ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"),
336 ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") ,
337 ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") };
338 try (RevWalk parser = new RevWalk(db)) {
339 final RevObject forcedOrderRevs[] = new RevObject[forcedOrder.length];
340 for (int i = 0; i < forcedOrder.length; i++)
341 forcedOrderRevs[i] = parser.parseAny(forcedOrder[i]);
342
343 createVerifyOpenPack(Arrays.asList(forcedOrderRevs));
344 }
345
346 assertEquals(forcedOrder.length, writer.getObjectCount());
347 verifyObjectsOrder(forcedOrder);
348 assertEquals("ed3f96b8327c7c66b0f8f70056129f0769323d86", writer
349 .computeName().name());
350 }
351
352
353
354
355
356
357
358
359 @Test
360 public void testWritePack4() throws IOException {
361 writeVerifyPack4(false);
362 }
363
364
365
366
367
368
369
370 @Test
371 public void testWritePack4ThinPack() throws IOException {
372 writeVerifyPack4(true);
373 }
374
375
376
377
378
379
380
381
382 @Test
383 public void testWritePack2SizeDeltasVsNoDeltas() throws Exception {
384 config.setReuseDeltas(false);
385 config.setDeltaCompress(false);
386 testWritePack2();
387 final long sizePack2NoDeltas = os.size();
388 tearDown();
389 setUp();
390 testWritePack2DeltasReuseRefs();
391 final long sizePack2DeltasRefs = os.size();
392
393 assertTrue(sizePack2NoDeltas > sizePack2DeltasRefs);
394 }
395
396
397
398
399
400
401
402
403
404 @Test
405 public void testWritePack2SizeOffsetsVsRefs() throws Exception {
406 testWritePack2DeltasReuseRefs();
407 final long sizePack2DeltasRefs = os.size();
408 tearDown();
409 setUp();
410 testWritePack2DeltasReuseOffsets();
411 final long sizePack2DeltasOffsets = os.size();
412
413 assertTrue(sizePack2DeltasRefs > sizePack2DeltasOffsets);
414 }
415
416
417
418
419
420
421
422
423 @Test
424 public void testWritePack4SizeThinVsNoThin() throws Exception {
425 testWritePack4();
426 final long sizePack4 = os.size();
427 tearDown();
428 setUp();
429 testWritePack4ThinPack();
430 final long sizePack4Thin = os.size();
431
432 assertTrue(sizePack4 > sizePack4Thin);
433 }
434
435 @Test
436 public void testDeltaStatistics() throws Exception {
437 config.setDeltaCompress(true);
438
439 FileRepository repo = createBareRepository();
440 ArrayList<RevObject> blobs = new ArrayList<>();
441 try (TestRepository<FileRepository> testRepo = new TestRepository<>(
442 repo)) {
443 blobs.add(testRepo.blob(genDeltableData(1000)));
444 blobs.add(testRepo.blob(genDeltableData(1005)));
445 try (PackWriter pw = new PackWriter(repo)) {
446 NullProgressMonitor m = NullProgressMonitor.INSTANCE;
447 pw.preparePack(blobs.iterator());
448 pw.writePack(m, m, os);
449 PackStatistics stats = pw.getStatistics();
450 assertEquals(1, stats.getTotalDeltas());
451 assertTrue("Delta bytes not set.",
452 stats.byObjectType(OBJ_BLOB).getDeltaBytes() > 0);
453 }
454 }
455 }
456
457
458 private String genDeltableData(int length) {
459 assertTrue("Generated data must have a length > 0", length > 0);
460 char[] data = {'a', 'b', 'c', '\n'};
461 StringBuilder builder = new StringBuilder(length);
462 for (int i = 0; i < length; i++) {
463 builder.append(data[i % 4]);
464 }
465 return builder.toString();
466 }
467
468
469 @Test
470 public void testWriteIndex() throws Exception {
471 config.setIndexVersion(2);
472 writeVerifyPack4(false);
473
474 File packFile = pack.getPackFile();
475 String name = packFile.getName();
476 String base = name.substring(0, name.lastIndexOf('.'));
477 File indexFile = new File(packFile.getParentFile(), base + ".idx");
478
479
480 final PackIndex idx1 = PackIndex.open(indexFile);
481 assertTrue(idx1 instanceof PackIndexV2);
482 assertEquals(0x4743F1E4L, idx1.findCRC32(ObjectId
483 .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")));
484
485
486 final File idx2File = new File(indexFile.getAbsolutePath() + ".2");
487 try (FileOutputStream is = new FileOutputStream(idx2File)) {
488 writer.writeIndex(is);
489 }
490 final PackIndex idx2 = PackIndex.open(idx2File);
491 assertTrue(idx2 instanceof PackIndexV2);
492 assertEquals(idx1.getObjectCount(), idx2.getObjectCount());
493 assertEquals(idx1.getOffset64Count(), idx2.getOffset64Count());
494
495 for (int i = 0; i < idx1.getObjectCount(); i++) {
496 final ObjectId id = idx1.getObjectId(i);
497 assertEquals(id, idx2.getObjectId(i));
498 assertEquals(idx1.findOffset(id), idx2.findOffset(id));
499 assertEquals(idx1.findCRC32(id), idx2.findCRC32(id));
500 }
501 }
502
503 @Test
504 public void testExclude() throws Exception {
505
506 FileRepository repo = createBareRepository();
507
508 try (TestRepository<FileRepository> testRepo = new TestRepository<>(
509 repo)) {
510 BranchBuilder bb = testRepo.branch("refs/heads/master");
511 contentA = testRepo.blob("A");
512 c1 = bb.commit().add("f", contentA).create();
513 testRepo.getRevWalk().parseHeaders(c1);
514 PackIndex pf1 = writePack(repo, wants(c1), EMPTY_ID_SET);
515 assertContent(pf1, Arrays.asList(c1.getId(), c1.getTree().getId(),
516 contentA.getId()));
517 contentB = testRepo.blob("B");
518 c2 = bb.commit().add("f", contentB).create();
519 testRepo.getRevWalk().parseHeaders(c2);
520 PackIndex pf2 = writePack(repo, wants(c2),
521 Sets.of((ObjectIdSet) pf1));
522 assertContent(pf2, Arrays.asList(c2.getId(), c2.getTree().getId(),
523 contentB.getId()));
524 }
525 }
526
527 private static void assertContent(PackIndex pi, List<ObjectId> expected) {
528 assertEquals("Pack index has wrong size.", expected.size(),
529 pi.getObjectCount());
530 for (int i = 0; i < pi.getObjectCount(); i++)
531 assertTrue(
532 "Pack index didn't contain the expected id "
533 + pi.getObjectId(i),
534 expected.contains(pi.getObjectId(i)));
535 }
536
537 @Test
538 public void testShallowIsMinimalDepth1() throws Exception {
539 try (FileRepository repo = setupRepoForShallowFetch()) {
540 PackIndex idx = writeShallowPack(repo, 1, wants(c2), NONE, NONE);
541 assertContent(idx, Arrays.asList(c2.getId(), c2.getTree().getId(),
542 contentA.getId(), contentB.getId()));
543
544
545 idx = writeShallowPack(repo, 1, wants(c5), haves(c2), shallows(c2));
546 assertContent(idx, Arrays.asList(c5.getId(), c5.getTree().getId(),
547 contentC.getId(), contentD.getId(), contentE.getId()));
548 }
549 }
550
551 @Test
552 public void testShallowIsMinimalDepth2() throws Exception {
553 try (FileRepository repo = setupRepoForShallowFetch()) {
554 PackIndex idx = writeShallowPack(repo, 2, wants(c2), NONE, NONE);
555 assertContent(idx,
556 Arrays.asList(c1.getId(), c2.getId(), c1.getTree().getId(),
557 c2.getTree().getId(), contentA.getId(),
558 contentB.getId()));
559
560
561 idx = writeShallowPack(repo, 2, wants(c5), haves(c1, c2),
562 shallows(c1));
563 assertContent(idx,
564 Arrays.asList(c4.getId(), c5.getId(), c4.getTree().getId(),
565 c5.getTree().getId(), contentC.getId(),
566 contentD.getId(), contentE.getId()));
567 }
568 }
569
570 @Test
571 public void testShallowFetchShallowParentDepth1() throws Exception {
572 try (FileRepository repo = setupRepoForShallowFetch()) {
573 PackIndex idx = writeShallowPack(repo, 1, wants(c5), NONE, NONE);
574 assertContent(idx, Arrays.asList(c5.getId(), c5.getTree().getId(),
575 contentA.getId(), contentB.getId(), contentC.getId(),
576 contentD.getId(), contentE.getId()));
577
578 idx = writeShallowPack(repo, 1, wants(c4), haves(c5), shallows(c5));
579 assertContent(idx, Arrays.asList(c4.getId(), c4.getTree().getId()));
580 }
581 }
582
583 @Test
584 public void testShallowFetchShallowParentDepth2() throws Exception {
585 try (FileRepository repo = setupRepoForShallowFetch()) {
586 PackIndex idx = writeShallowPack(repo, 2, wants(c5), NONE, NONE);
587 assertContent(idx,
588 Arrays.asList(c4.getId(), c5.getId(), c4.getTree().getId(),
589 c5.getTree().getId(), contentA.getId(),
590 contentB.getId(), contentC.getId(),
591 contentD.getId(), contentE.getId()));
592
593 idx = writeShallowPack(repo, 2, wants(c3), haves(c4, c5),
594 shallows(c4));
595 assertContent(idx, Arrays.asList(c2.getId(), c3.getId(),
596 c2.getTree().getId(), c3.getTree().getId()));
597 }
598 }
599
600 @Test
601 public void testShallowFetchShallowAncestorDepth1() throws Exception {
602 try (FileRepository repo = setupRepoForShallowFetch()) {
603 PackIndex idx = writeShallowPack(repo, 1, wants(c5), NONE, NONE);
604 assertContent(idx, Arrays.asList(c5.getId(), c5.getTree().getId(),
605 contentA.getId(), contentB.getId(), contentC.getId(),
606 contentD.getId(), contentE.getId()));
607
608 idx = writeShallowPack(repo, 1, wants(c3), haves(c5), shallows(c5));
609 assertContent(idx, Arrays.asList(c3.getId(), c3.getTree().getId()));
610 }
611 }
612
613 @Test
614 public void testShallowFetchShallowAncestorDepth2() throws Exception {
615 try (FileRepository repo = setupRepoForShallowFetch()) {
616 PackIndex idx = writeShallowPack(repo, 2, wants(c5), NONE, NONE);
617 assertContent(idx,
618 Arrays.asList(c4.getId(), c5.getId(), c4.getTree().getId(),
619 c5.getTree().getId(), contentA.getId(),
620 contentB.getId(), contentC.getId(),
621 contentD.getId(), contentE.getId()));
622
623 idx = writeShallowPack(repo, 2, wants(c2), haves(c4, c5),
624 shallows(c4));
625 assertContent(idx, Arrays.asList(c1.getId(), c2.getId(),
626 c1.getTree().getId(), c2.getTree().getId()));
627 }
628 }
629
630 private FileRepository setupRepoForShallowFetch() throws Exception {
631 FileRepository repo = createBareRepository();
632
633
634 repo.incrementOpen();
635 try (TestRepository<Repository> r = new TestRepository<>(repo)) {
636 BranchBuilder bb = r.branch("refs/heads/master");
637 contentA = r.blob("A");
638 contentB = r.blob("B");
639 contentC = r.blob("C");
640 contentD = r.blob("D");
641 contentE = r.blob("E");
642 c1 = bb.commit().add("a", contentA).create();
643 c2 = bb.commit().add("b", contentB).create();
644 c3 = bb.commit().add("c", contentC).create();
645 c4 = bb.commit().add("d", contentD).create();
646 c5 = bb.commit().add("e", contentE).create();
647 r.getRevWalk().parseHeaders(c5);
648 return repo;
649 }
650 }
651
652 private static PackIndex writePack(FileRepository repo,
653 Set<? extends ObjectId> want, Set<ObjectIdSet> excludeObjects)
654 throws IOException {
655 try (RevWalk walk = new RevWalk(repo)) {
656 return writePack(repo, walk, 0, want, NONE, excludeObjects);
657 }
658 }
659
660 private static PackIndex writeShallowPack(FileRepository repo, int depth,
661 Set<? extends ObjectId> want, Set<? extends ObjectId> have,
662 Set<? extends ObjectId> shallow) throws IOException {
663
664
665 try (DepthWalk.RevWalk walk = new DepthWalk.RevWalk(repo, depth - 1)) {
666 walk.assumeShallow(shallow);
667 return writePack(repo, walk, depth, want, have, EMPTY_ID_SET);
668 }
669 }
670
671 private static PackIndex writePack(FileRepository repo, RevWalk walk,
672 int depth, Set<? extends ObjectId> want,
673 Set<? extends ObjectId> have, Set<ObjectIdSet> excludeObjects)
674 throws IOException {
675 try (PackWriter pw = new PackWriter(repo)) {
676 pw.setDeltaBaseAsOffset(true);
677 pw.setReuseDeltaCommits(false);
678 for (ObjectIdSet idx : excludeObjects) {
679 pw.excludeObjects(idx);
680 }
681 if (depth > 0) {
682 pw.setShallowPack(depth, null);
683 }
684
685 ObjectWalk ow = walk.toObjectWalkWithSameObjects();
686
687 pw.preparePack(NullProgressMonitor.INSTANCE, ow, want, have, NONE);
688 String id = pw.computeName().getName();
689 File packdir = repo.getObjectDatabase().getPackDirectory();
690 File packFile = new File(packdir, "pack-" + id + ".pack");
691 try (FileOutputStream packOS = new FileOutputStream(packFile)) {
692 pw.writePack(NullProgressMonitor.INSTANCE,
693 NullProgressMonitor.INSTANCE, packOS);
694 }
695 File idxFile = new File(packdir, "pack-" + id + ".idx");
696 try (FileOutputStream idxOS = new FileOutputStream(idxFile)) {
697 pw.writeIndex(idxOS);
698 }
699 return PackIndex.open(idxFile);
700 }
701 }
702
703
704
705
706 private void writeVerifyPack1() throws IOException {
707 final HashSet<ObjectId> interestings = new HashSet<>();
708 interestings.add(ObjectId
709 .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
710 createVerifyOpenPack(interestings, NONE, false, false);
711
712 final ObjectId expectedOrder[] = new ObjectId[] {
713 ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
714 ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"),
715 ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"),
716 ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
717 ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"),
718 ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"),
719 ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3"),
720 ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") };
721
722 assertEquals(expectedOrder.length, writer.getObjectCount());
723 verifyObjectsOrder(expectedOrder);
724 assertEquals("34be9032ac282b11fa9babdc2b2a93ca996c9c2f", writer
725 .computeName().name());
726 }
727
728 private void writeVerifyPack2(boolean deltaReuse) throws IOException {
729 config.setReuseDeltas(deltaReuse);
730 final HashSet<ObjectId> interestings = new HashSet<>();
731 interestings.add(ObjectId
732 .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
733 final HashSet<ObjectId> uninterestings = new HashSet<>();
734 uninterestings.add(ObjectId
735 .fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"));
736 createVerifyOpenPack(interestings, uninterestings, false, false);
737
738 final ObjectId expectedOrder[] = new ObjectId[] {
739 ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
740 ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"),
741 ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
742 ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"),
743 ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") ,
744 ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") };
745 if (!config.isReuseDeltas() && !config.isDeltaCompress()) {
746
747 swap(expectedOrder, 4, 5);
748 }
749 assertEquals(expectedOrder.length, writer.getObjectCount());
750 verifyObjectsOrder(expectedOrder);
751 assertEquals("ed3f96b8327c7c66b0f8f70056129f0769323d86", writer
752 .computeName().name());
753 }
754
755 private static void swap(ObjectId[] arr, int a, int b) {
756 ObjectId tmp = arr[a];
757 arr[a] = arr[b];
758 arr[b] = tmp;
759 }
760
761 private void writeVerifyPack4(final boolean thin) throws IOException {
762 final HashSet<ObjectId> interestings = new HashSet<>();
763 interestings.add(ObjectId
764 .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
765 final HashSet<ObjectId> uninterestings = new HashSet<>();
766 uninterestings.add(ObjectId
767 .fromString("c59759f143fb1fe21c197981df75a7ee00290799"));
768 createVerifyOpenPack(interestings, uninterestings, thin, false);
769
770 final ObjectId writtenObjects[] = new ObjectId[] {
771 ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
772 ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
773 ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") };
774 assertEquals(writtenObjects.length, writer.getObjectCount());
775 ObjectId expectedObjects[];
776 if (thin) {
777 expectedObjects = new ObjectId[4];
778 System.arraycopy(writtenObjects, 0, expectedObjects, 0,
779 writtenObjects.length);
780 expectedObjects[3] = ObjectId
781 .fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3");
782
783 } else {
784 expectedObjects = writtenObjects;
785 }
786 verifyObjectsOrder(expectedObjects);
787 assertEquals("cded4b74176b4456afa456768b2b5aafb41c44fc", writer
788 .computeName().name());
789 }
790
791 private void createVerifyOpenPack(final Set<ObjectId> interestings,
792 final Set<ObjectId> uninterestings, final boolean thin,
793 final boolean ignoreMissingUninteresting)
794 throws MissingObjectException, IOException {
795 createVerifyOpenPack(interestings, uninterestings, thin,
796 ignoreMissingUninteresting, false);
797 }
798
799 private void createVerifyOpenPack(final Set<ObjectId> interestings,
800 final Set<ObjectId> uninterestings, final boolean thin,
801 final boolean ignoreMissingUninteresting, boolean useBitmaps)
802 throws MissingObjectException, IOException {
803 NullProgressMonitor m = NullProgressMonitor.INSTANCE;
804 writer = new PackWriter(config, db.newObjectReader());
805 writer.setUseBitmaps(useBitmaps);
806 writer.setThin(thin);
807 writer.setIgnoreMissingUninteresting(ignoreMissingUninteresting);
808 writer.preparePack(m, interestings, uninterestings);
809 writer.writePack(m, m, os);
810 writer.close();
811 verifyOpenPack(thin);
812 }
813
814 private void createVerifyOpenPack(List<RevObject> objectSource)
815 throws MissingObjectException, IOException {
816 NullProgressMonitor m = NullProgressMonitor.INSTANCE;
817 writer = new PackWriter(config, db.newObjectReader());
818 writer.preparePack(objectSource.iterator());
819 assertEquals(objectSource.size(), writer.getObjectCount());
820 writer.writePack(m, m, os);
821 writer.close();
822 verifyOpenPack(false);
823 }
824
825 private void verifyOpenPack(boolean thin) throws IOException {
826 final byte[] packData = os.toByteArray();
827
828 if (thin) {
829 PackParser p = index(packData);
830 try {
831 p.parse(NullProgressMonitor.INSTANCE);
832 fail("indexer should grumble about missing object");
833 } catch (IOException x) {
834
835 }
836 }
837
838 ObjectDirectoryPackParser p = (ObjectDirectoryPackParser) index(packData);
839 p.setKeepEmpty(true);
840 p.setAllowThin(thin);
841 p.setIndexVersion(2);
842 p.parse(NullProgressMonitor.INSTANCE);
843 pack = p.getPackFile();
844 assertNotNull("have PackFile after parsing", pack);
845 }
846
847 private PackParser index(byte[] packData) throws IOException {
848 if (inserter == null)
849 inserter = dst.newObjectInserter();
850 return inserter.newPackParser(new ByteArrayInputStream(packData));
851 }
852
853 private void verifyObjectsOrder(ObjectId objectsOrder[]) {
854 final List<PackIndex.MutableEntry> entries = new ArrayList<>();
855
856 for (MutableEntry me : pack) {
857 entries.add(me.cloneEntry());
858 }
859 Collections.sort(entries, (MutableEntry o1, MutableEntry o2) -> Long
860 .signum(o1.getOffset() - o2.getOffset()));
861
862 int i = 0;
863 for (MutableEntry me : entries) {
864 assertEquals(objectsOrder[i++].toObjectId(), me.toObjectId());
865 }
866 }
867
868 private static Set<ObjectId> haves(ObjectId... objects) {
869 return Sets.of(objects);
870 }
871
872 private static Set<ObjectId> wants(ObjectId... objects) {
873 return Sets.of(objects);
874 }
875
876 private static Set<ObjectId> shallows(ObjectId... objects) {
877 return Sets.of(objects);
878 }
879 }