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