1 /* -*- Mode: C++ -*- */
6 template <typename Constants>
9 typedef typename Constants::Sizes Sizes;
12 Options() : encode_srcwin_maxsz(1<<20),
13 block_size(Constants::BLOCK_SIZE),
15 uint64_t encode_srcwin_maxsz;
26 void InMemoryEncodeDecode(const FileSpec &source_file,
27 const FileSpec &target_file,
29 const Options &options = Options()) {
30 xd3_stream encode_stream;
31 xd3_config encode_config;
32 xd3_source encode_source;
34 xd3_stream decode_stream;
35 xd3_config decode_config;
36 xd3_source decode_source;
37 xoff_t verified_bytes = 0;
38 xoff_t encoded_bytes = 0;
44 memset(&encode_stream, 0, sizeof (encode_stream));
45 memset(&encode_source, 0, sizeof (encode_source));
47 memset(&decode_stream, 0, sizeof (decode_stream));
48 memset(&decode_source, 0, sizeof (decode_source));
50 xd3_init_config(&encode_config, XD3_ADLER32);
51 xd3_init_config(&decode_config, XD3_ADLER32);
53 encode_config.winsize = Constants::WINDOW_SIZE;
55 // TODO! the smatcher setup isn't working,
56 // if (options.large_cksum_step) {
57 // encode_config.smatch_cfg = XD3_SMATCH_SOFT;
58 // encode_config.smatcher_soft.large_step = options.large_cksum_step;
60 // if (options.large_cksum_size) {
61 // encode_config.smatch_cfg = XD3_SMATCH_SOFT;
62 // encode_config.smatcher_soft.large_look = options.large_cksum_size;
65 CHECK_EQ(0, xd3_config_stream (&encode_stream, &encode_config));
66 CHECK_EQ(0, xd3_config_stream (&decode_stream, &decode_config));
68 encode_source.blksize = options.block_size;
69 decode_source.blksize = options.block_size;
71 encode_source.max_winsize = options.encode_srcwin_maxsz;
72 decode_source.max_winsize = options.encode_srcwin_maxsz;
74 if (!options.size_known)
76 xd3_set_source (&encode_stream, &encode_source);
77 xd3_set_source (&decode_stream, &decode_source);
81 xd3_set_source_and_size (&encode_stream, &encode_source,
83 xd3_set_source_and_size (&decode_stream, &decode_source,
87 BlockIterator source_iterator(source_file, options.block_size);
88 BlockIterator target_iterator(target_file, Constants::READ_SIZE);
89 Block encode_source_block, decode_source_block;
90 Block decoded_block, target_block;
93 bool done_after_input = false;
95 IF_DEBUG1 (XPR(NTR "source %"Q"u[%"Q"u] target %"Q"u[%lu] winsize %lu\n",
96 source_file.Size(), options.block_size,
97 target_file.Size(), Constants::READ_SIZE,
98 Constants::WINDOW_SIZE));
101 target_iterator.Get(&target_block);
103 xoff_t blks = target_iterator.Blocks();
105 IF_DEBUG2(XPR(NTR "target in %s: %llu..%llu %"Q"u(%"Q"u) verified %"Q"u\n",
106 encoding ? "encoding" : "decoding",
107 target_iterator.Offset(),
108 target_iterator.Offset() + target_block.Size(),
109 target_iterator.Blkno(), blks, verified_bytes));
111 if (blks == 0 || target_iterator.Blkno() == (blks - 1)) {
112 xd3_set_flags(&encode_stream, XD3_FLUSH | encode_stream.flags);
115 xd3_avail_input(&encode_stream, target_block.Data(), target_block.Size());
116 encoded_bytes += target_block.Size();
122 ret = xd3_encode_input(&encode_stream);
123 msg = encode_stream.msg;
125 ret = xd3_decode_input(&decode_stream);
126 msg = decode_stream.msg;
133 if (coded_data != NULL) {
134 // Optional encoded-output to the caller
135 coded_data->Append(encode_stream.next_out,
136 encode_stream.avail_out);
138 // Feed this data to the decoder.
139 xd3_avail_input(&decode_stream,
140 encode_stream.next_out,
141 encode_stream.avail_out);
142 xd3_consume_output(&encode_stream);
145 decoded_block.Append(decode_stream.next_out,
146 decode_stream.avail_out);
147 xd3_consume_output(&decode_stream);
151 case XD3_GETSRCBLK: {
152 xd3_source *src = (encoding ? &encode_source : &decode_source);
153 Block *block = (encoding ? &encode_source_block : &decode_source_block);
155 IF_DEBUG1(XPR(NTR "[srcblock] %"Q"u last srcpos %"Q"u "
157 encode_source.getblkno,
158 encode_stream.match_last_srcpos,
159 encode_stream.input_position + encode_stream.total_in));
162 source_iterator.SetBlock(src->getblkno);
163 source_iterator.Get(block);
164 src->curblkno = src->getblkno;
165 src->onblk = block->Size();
166 src->curblk = block->Data();
176 if (done_after_input) {
181 if (target_block.Size() < target_iterator.BlockSize()) {
184 target_iterator.Next();
191 if (encode_stream.flags & XD3_FLUSH) {
192 done_after_input = true;
196 CHECK_EQ(0, CmpDifferentBlockBytesAtOffset(decoded_block,
199 verified_bytes += decoded_block.Size();
200 decoded_block.Reset();
210 XPR(NTR "%s = %s %s\n", encoding ? "E " : " D",
212 msg == NULL ? "" : msg);
219 CHECK_EQ(target_file.Size(), encoded_bytes);
220 CHECK_EQ(target_file.Size(), verified_bytes);
221 CHECK_EQ(0, xd3_close_stream(&decode_stream));
222 CHECK_EQ(0, xd3_close_stream(&encode_stream));
223 xd3_free_stream(&encode_stream);
224 xd3_free_stream(&decode_stream);
227 void MainEncodeDecode(const TmpFile &source_file,
228 const TmpFile &target_file,
230 const Options &options) {
231 vector<const char*> ecmd;
233 snprintf(buf, sizeof(buf), "-B%"Q"u", options.encode_srcwin_maxsz);
234 ecmd.push_back("xdelta3");
236 ecmd.push_back("-s");
237 ecmd.push_back(source_file.Name());
238 ecmd.push_back(target_file.Name());
239 ecmd.push_back(coded_data->Name());
240 ecmd.push_back(NULL);
242 CHECK_EQ(0, xd3_main_cmdline(ecmd.size() - 1,
243 const_cast<char**>(&ecmd[0])));
245 vector<const char*> dcmd;
247 dcmd.push_back("xdelta3");
249 dcmd.push_back("-d");
250 dcmd.push_back("-s");
251 dcmd.push_back(source_file.Name());
252 dcmd.push_back(coded_data->Name());
253 dcmd.push_back(recon_file.Name());
254 dcmd.push_back(NULL);
256 CHECK_EQ(0, xd3_main_cmdline(dcmd.size() - 1,
257 const_cast<char**>(&dcmd[0])));
259 CHECK_EQ(0, test_compare_files(recon_file.Name(),
260 target_file.Name()));
263 //////////////////////////////////////////////////////////////////////
265 void TestRandomNumbers() {
271 for (int i = 0; i < rounds; i++) {
272 usum += rand.Rand32();
273 esum += rand.ExpRand32(1024);
276 double allowed_error = 0.01;
278 uint32_t umean = usum / rounds;
279 uint32_t emean = esum / rounds;
281 uint32_t uexpect = UINT32_MAX / 2;
282 uint32_t eexpect = 1024;
284 if (umean < uexpect * (1.0 - allowed_error) ||
285 umean > uexpect * (1.0 + allowed_error)) {
286 XPR(NT "uniform mean error: %u != %u\n", umean, uexpect);
290 if (emean < eexpect * (1.0 - allowed_error) ||
291 emean > eexpect * (1.0 + allowed_error)) {
292 XPR(NT "exponential mean error: %u != %u\n", emean, eexpect);
297 void TestRandomFile() {
299 FileSpec spec1(&rand1);
300 BlockIterator bi(spec1);
302 spec1.GenerateFixedSize(0);
303 CHECK_EQ(0, spec1.Size());
304 CHECK_EQ(0, spec1.Segments());
305 CHECK_EQ(0, spec1.Blocks());
307 CHECK_EQ(0, bi.BytesOnBlock());
309 spec1.GenerateFixedSize(1);
310 CHECK_EQ(1, spec1.Size());
311 CHECK_EQ(1, spec1.Segments());
312 CHECK_EQ(1, spec1.Blocks());
314 CHECK_EQ(1, bi.BytesOnBlock());
316 spec1.GenerateFixedSize(Constants::BLOCK_SIZE);
317 CHECK_EQ(Constants::BLOCK_SIZE, spec1.Size());
318 CHECK_EQ(1, spec1.Segments());
319 CHECK_EQ(1, spec1.Blocks());
321 CHECK_EQ(Constants::BLOCK_SIZE, bi.BytesOnBlock());
323 CHECK_EQ(0, bi.BytesOnBlock());
325 spec1.GenerateFixedSize(Constants::BLOCK_SIZE + 1);
326 CHECK_EQ(Constants::BLOCK_SIZE + 1, spec1.Size());
327 CHECK_EQ(2, spec1.Segments());
328 CHECK_EQ(2, spec1.Blocks());
330 CHECK_EQ(Constants::BLOCK_SIZE, bi.BytesOnBlock());
332 CHECK_EQ(1, bi.BytesOnBlock());
334 spec1.GenerateFixedSize(Constants::BLOCK_SIZE * 2);
335 CHECK_EQ(Constants::BLOCK_SIZE * 2, spec1.Size());
336 CHECK_EQ(2, spec1.Segments());
337 CHECK_EQ(2, spec1.Blocks());
339 CHECK_EQ(Constants::BLOCK_SIZE, bi.BytesOnBlock());
341 CHECK_EQ(Constants::BLOCK_SIZE, bi.BytesOnBlock());
344 void TestFirstByte() {
346 FileSpec spec0(&rand);
347 FileSpec spec1(&rand);
349 spec0.GenerateFixedSize(0);
350 spec1.GenerateFixedSize(1);
351 CHECK_EQ(0, CmpDifferentBytes(spec0, spec0));
352 CHECK_EQ(0, CmpDifferentBytes(spec1, spec1));
353 CHECK_EQ(1, CmpDifferentBytes(spec0, spec1));
354 CHECK_EQ(1, CmpDifferentBytes(spec1, spec0));
356 spec0.GenerateFixedSize(1);
357 spec0.ModifyTo(Modify1stByte(), &spec1);
358 CHECK_EQ(1, CmpDifferentBytes(spec0, spec1));
360 spec0.GenerateFixedSize(Constants::BLOCK_SIZE + 1);
361 spec0.ModifyTo(Modify1stByte(), &spec1);
362 CHECK_EQ(1, CmpDifferentBytes(spec0, spec1));
364 SizeIterator<size_t, Sizes> si(&rand, Constants::TEST_ROUNDS);
366 for (; !si.Done(); si.Next()) {
367 size_t size = si.Get();
371 spec0.GenerateFixedSize(size);
372 spec0.ModifyTo(Modify1stByte(), &spec1);
373 InMemoryEncodeDecode(spec0, spec1, NULL);
377 void TestModifyMutator() {
379 FileSpec spec0(&rand);
380 FileSpec spec1(&rand);
382 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 3);
388 { Constants::BLOCK_SIZE, 0 },
389 { Constants::BLOCK_SIZE / 2, 1 },
390 { Constants::BLOCK_SIZE, 1 },
391 { Constants::BLOCK_SIZE * 2, 1 },
394 for (size_t i = 0; i < SIZEOF_ARRAY(test_cases); i++) {
396 cl1.push_back(Change(Change::MODIFY, test_cases[i].size,
397 test_cases[i].addr));
398 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
399 CHECK_EQ(spec0.Size(), spec1.Size());
401 size_t diff = CmpDifferentBytes(spec0, spec1);
402 CHECK_LE(diff, test_cases[i].size);
404 // There is a 1/256 probability of the changed byte matching the
405 // original value. The following allows double the probability to
407 CHECK_GE(diff, test_cases[i].size - (2 * test_cases[i].size / 256));
409 InMemoryEncodeDecode(spec0, spec1, NULL);
413 void TestAddMutator() {
415 FileSpec spec0(&rand);
416 FileSpec spec1(&rand);
418 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 2);
419 // TODO: fix this test (for all block sizes)! it's broken because
420 // the same byte could be added?
425 size_t expected_adds;
427 { 1, 0, 2 /* 1st byte, last byte (short block) */ },
428 { 1, 1, 3 /* 1st 2 bytes, last byte */ },
429 { 1, Constants::BLOCK_SIZE - 1, 2 /* changed, last */ },
430 { 1, Constants::BLOCK_SIZE, 2 /* changed, last */ },
431 { 1, Constants::BLOCK_SIZE + 1, 3 /* changed + 1st of 2nd block, last */ },
432 { 1, 2 * Constants::BLOCK_SIZE, 1 /* last byte */ },
435 for (size_t i = 0; i < SIZEOF_ARRAY(test_cases); i++) {
437 cl1.push_back(Change(Change::ADD, test_cases[i].size, test_cases[i].addr));
438 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
439 CHECK_EQ(spec0.Size() + test_cases[i].size, spec1.Size());
442 InMemoryEncodeDecode(spec0, spec1, &coded);
445 CHECK_EQ(test_cases[i].expected_adds,
450 void TestDeleteMutator() {
452 FileSpec spec0(&rand);
453 FileSpec spec1(&rand);
455 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 4);
461 // Note: an entry { Constants::BLOCK_SIZE, 0 },
462 // does not work because the xd3_srcwin_move_point logic won't
463 // find a copy if it occurs >= double its size into the file.
464 { Constants::BLOCK_SIZE / 2, 0 },
465 { Constants::BLOCK_SIZE / 2, Constants::BLOCK_SIZE / 2 },
466 { Constants::BLOCK_SIZE, Constants::BLOCK_SIZE / 2 },
467 { Constants::BLOCK_SIZE * 2, Constants::BLOCK_SIZE * 3 / 2 },
468 { Constants::BLOCK_SIZE, Constants::BLOCK_SIZE * 2 },
471 for (size_t i = 0; i < SIZEOF_ARRAY(test_cases); i++) {
473 cl1.push_back(Change(Change::DELETE, test_cases[i].size,
474 test_cases[i].addr));
475 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
476 CHECK_EQ(spec0.Size() - test_cases[i].size, spec1.Size());
479 InMemoryEncodeDecode(spec0, spec1, &coded);
482 CHECK_EQ(0, delta.AddedBytes());
486 void TestCopyMutator() {
488 FileSpec spec0(&rand);
489 FileSpec spec1(&rand);
491 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 3);
498 // Copy is difficult to write tests for because where Xdelta finds
499 // copies, it does not enter checksums. So these tests copy data from
500 // later to earlier so that checksumming will start.
501 { Constants::BLOCK_SIZE / 2, Constants::BLOCK_SIZE / 2, 0 },
502 { Constants::BLOCK_SIZE, 2 * Constants::BLOCK_SIZE,
503 Constants::BLOCK_SIZE, },
506 for (size_t i = 0; i < SIZEOF_ARRAY(test_cases); i++) {
508 cl1.push_back(Change(Change::COPY, test_cases[i].size,
509 test_cases[i].from, test_cases[i].to));
510 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
511 CHECK_EQ(spec0.Size() + test_cases[i].size, spec1.Size());
514 InMemoryEncodeDecode(spec0, spec1, &coded);
517 CHECK_EQ(0, delta.AddedBytes());
521 void TestMoveMutator() {
523 FileSpec spec0(&rand);
524 FileSpec spec1(&rand);
526 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 3);
533 // This is easier to test than Copy but has the same trouble as Delete.
534 { Constants::BLOCK_SIZE / 2, Constants::BLOCK_SIZE / 2, 0 },
535 { Constants::BLOCK_SIZE / 2, 0, Constants::BLOCK_SIZE / 2 },
536 { Constants::BLOCK_SIZE, Constants::BLOCK_SIZE, 2 *
537 Constants::BLOCK_SIZE },
538 { Constants::BLOCK_SIZE, 2 * Constants::BLOCK_SIZE,
539 Constants::BLOCK_SIZE },
540 { Constants::BLOCK_SIZE * 3 / 2, Constants::BLOCK_SIZE,
541 Constants::BLOCK_SIZE * 3 / 2 },
544 { Constants::BLOCK_SIZE, Constants::BLOCK_SIZE * 2,
545 3 * Constants::BLOCK_SIZE },
548 for (size_t i = 0; i < SIZEOF_ARRAY(test_cases); i++) {
550 cl1.push_back(Change(Change::MOVE, test_cases[i].size,
551 test_cases[i].from, test_cases[i].to));
552 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
553 CHECK_EQ(spec0.Size(), spec1.Size());
556 InMemoryEncodeDecode(spec0, spec1, &coded);
559 CHECK_EQ(0, delta.AddedBytes());
563 void TestOverwriteMutator() {
565 FileSpec spec0(&rand);
566 FileSpec spec1(&rand);
568 spec0.GenerateFixedSize(Constants::BLOCK_SIZE);
571 cl1.push_back(Change(Change::COPYOVER, 10, 0, 20));
572 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
573 CHECK_EQ(spec0.Size(), spec1.Size());
576 BlockIterator(spec0).Get(&b0);
577 BlockIterator(spec1).Get(&b1);
579 CHECK(memcmp(b0.Data(), b1.Data() + 20, 10) == 0);
580 CHECK(memcmp(b0.Data(), b1.Data(), 20) == 0);
581 CHECK(memcmp(b0.Data() + 30, b1.Data() + 30,
582 Constants::BLOCK_SIZE - 30) == 0);
585 cl1.push_back(Change(Change::COPYOVER, 10, 20, (xoff_t)0));
586 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
587 CHECK_EQ(spec0.Size(), spec1.Size());
589 BlockIterator(spec0).Get(&b0);
590 BlockIterator(spec1).Get(&b1);
592 CHECK(memcmp(b0.Data() + 20, b1.Data(), 10) == 0);
593 CHECK(memcmp(b0.Data() + 10, b1.Data() + 10,
594 Constants::BLOCK_SIZE - 10) == 0);
597 // Note: this test is written to expose a problem, but the problem was
598 // only exposed with BLOCK_SIZE = 128.
599 void TestNonBlockingProgress() {
601 FileSpec spec0(&rand);
602 FileSpec spec1(&rand);
603 FileSpec spec2(&rand);
605 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 3);
607 // This is a lazy target match
608 Change ct(Change::COPYOVER, 22,
609 Constants::BLOCK_SIZE + 50,
610 Constants::BLOCK_SIZE + 20);
612 // This is a source match just after the block boundary, shorter
613 // than the lazy target match.
614 Change cs1(Change::COPYOVER, 16,
615 Constants::BLOCK_SIZE + 51,
616 Constants::BLOCK_SIZE - 1);
618 // This overwrites the original source bytes.
619 Change cs2(Change::MODIFY, 108,
620 Constants::BLOCK_SIZE + 20);
622 // This changes the first blocks
623 Change c1st(Change::MODIFY, Constants::BLOCK_SIZE - 2, 0);
630 spec0.ModifyTo(ChangeListMutator(csl), &spec1);
636 spec0.ModifyTo(ChangeListMutator(ctl), &spec2);
638 InMemoryEncodeDecode(spec1, spec2, NULL);
641 void TestEmptyInMemory() {
643 FileSpec spec0(&rand);
644 FileSpec spec1(&rand);
647 spec0.GenerateFixedSize(0);
648 spec1.GenerateFixedSize(0);
650 InMemoryEncodeDecode(spec0, spec1, &block);
653 CHECK_LT(0, block.Size());
654 CHECK_EQ(1, delta.Windows());
657 void TestBlockInMemory() {
659 FileSpec spec0(&rand);
660 FileSpec spec1(&rand);
663 spec0.GenerateFixedSize(Constants::BLOCK_SIZE);
664 spec1.GenerateFixedSize(Constants::BLOCK_SIZE);
666 InMemoryEncodeDecode(spec0, spec1, &block);
669 CHECK_EQ(spec1.Blocks(Constants::WINDOW_SIZE), delta.Windows());
672 void TestHalfBlockCopy() {
674 FileSpec spec0(&rand);
675 FileSpec spec1(&rand);
677 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 4);
679 // Create a half-block copy, 2.5 blocks apart, from the second half
680 // of the source version to the first half of the target version.
682 // spec0 [bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb][ccccc][bbbbb]
683 // spec1 [aaaaa][ccccc][aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]
685 cl1.push_back(Change(Change::MODIFY,
686 Constants::BLOCK_SIZE / 2, // size
688 cl1.push_back(Change(Change::COPYOVER,
689 Constants::BLOCK_SIZE / 2, // size
690 Constants::BLOCK_SIZE * 3, // offset
691 Constants::BLOCK_SIZE / 2));
692 cl1.push_back(Change(Change::MODIFY,
693 Constants::BLOCK_SIZE * 3,
694 Constants::BLOCK_SIZE));
695 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
697 const int onecopy_adds =
698 4 * Constants::BLOCK_SIZE - Constants::BLOCK_SIZE / 2;
699 const int nocopy_adds = 4 * Constants::BLOCK_SIZE;
701 // Note the case b=4 is contrived: the caller should use a single block
702 // containing the entire source, if possible.
703 for (int b = 1; b <= 4; b++)
706 options.encode_srcwin_maxsz = Constants::BLOCK_SIZE * b;
710 InMemoryEncodeDecode(spec0, spec1, &block0, options);
711 InMemoryEncodeDecode(spec1, spec0, &block1, options);
712 Delta delta0(block0);
713 Delta delta1(block1);
715 // The first block never copies from the last source block, by
716 // design, because if the last source block is available when
717 // the first target block is ready, the caller is expected to
718 // use a single block.
719 CHECK_EQ(nocopy_adds, delta0.AddedBytes());
720 if (Constants::BLOCK_SIZE < 8192 || b > 2)
722 // For small-block inputs, the entire file is read into one
723 // block (the min source window size is 16kB).
725 // For large blocks, at least 3 blocks of source window are
727 CHECK_EQ(onecopy_adds, delta1.AddedBytes());
731 // When there are fewer than 3 source blocks.
732 CHECK_EQ(nocopy_adds, delta1.AddedBytes());
734 // XPR(NT "0=%zu 1=%zu\n", delta0.AddedBytes(), delta1.AddedBytes());
738 options.encode_srcwin_maxsz = Constants::BLOCK_SIZE * 4;
739 options.block_size = Constants::BLOCK_SIZE * 4;
741 // Test the whole-buffer case.
744 InMemoryEncodeDecode(spec0, spec1, &block0, options);
745 InMemoryEncodeDecode(spec1, spec0, &block1, options);
746 Delta delta0(block0);
747 Delta delta1(block1);
748 // This <= >= are only for blocksize = 512, which has irregular readsize.
749 CHECK_LE(onecopy_adds, delta0.AddedBytes());
750 CHECK_GE(onecopy_adds + 1, delta0.AddedBytes());
752 CHECK_EQ(onecopy_adds, delta1.AddedBytes());
753 // XPR(NT "0=%zu 1=%zu\n", delta0.AddedBytes(), delta1.AddedBytes());
756 void FourWayMergeTest(const FileSpec &spec0,
757 const FileSpec &spec1,
758 const FileSpec &spec2,
759 const FileSpec &spec3) {
760 TmpFile f0, f1, f2, f3;
761 ExtFile d01, d12, d23;
763 options.encode_srcwin_maxsz =
764 std::max(spec0.Size(), options.encode_srcwin_maxsz);
766 spec0.WriteTmpFile(&f0);
767 spec1.WriteTmpFile(&f1);
768 spec2.WriteTmpFile(&f2);
769 spec3.WriteTmpFile(&f3);
771 MainEncodeDecode(f0, f1, &d01, options);
772 MainEncodeDecode(f1, f2, &d12, options);
773 MainEncodeDecode(f2, f3, &d23, options);
777 vector<const char*> mcmd;
778 mcmd.push_back("xdelta3");
779 mcmd.push_back("merge");
780 mcmd.push_back("-m");
781 mcmd.push_back(d01.Name());
782 mcmd.push_back(d12.Name());
783 mcmd.push_back(out.Name());
784 mcmd.push_back(NULL);
786 // XPR(NTR "Running one merge: %s\n", CommandToString(mcmd).c_str());
787 CHECK_EQ(0, xd3_main_cmdline(mcmd.size() - 1,
788 const_cast<char**>(&mcmd[0])));
791 vector<const char*> tcmd;
792 tcmd.push_back("xdelta3");
793 tcmd.push_back("-d");
794 tcmd.push_back("-s");
795 tcmd.push_back(f0.Name());
796 tcmd.push_back(out.Name());
797 tcmd.push_back(recon.Name());
798 tcmd.push_back(NULL);
800 // XPR(NTR "Running one recon! %s\n", CommandToString(tcmd).c_str());
801 CHECK_EQ(0, xd3_main_cmdline(tcmd.size() - 1,
802 const_cast<char**>(&tcmd[0])));
803 // XPR(NTR "Should equal! %s\n", f2.Name());
805 CHECK(recon.EqualsSpec(spec2));
809 vector<const char*> mcmd3;
810 mcmd3.push_back("xdelta3");
811 mcmd3.push_back("merge");
812 mcmd3.push_back("-m");
813 mcmd3.push_back(d01.Name());
814 mcmd3.push_back("-m");
815 mcmd3.push_back(d12.Name());
816 mcmd3.push_back(d23.Name());
817 mcmd3.push_back(out3.Name());
818 mcmd3.push_back(NULL);
820 // XPR(NTR "Running one 3-merge: %s\n", CommandToString(mcmd3).c_str());
821 CHECK_EQ(0, xd3_main_cmdline(mcmd3.size() - 1,
822 const_cast<char**>(&mcmd3[0])));
825 vector<const char*> tcmd3;
826 tcmd3.push_back("xdelta3");
827 tcmd3.push_back("-d");
828 tcmd3.push_back("-s");
829 tcmd3.push_back(f0.Name());
830 tcmd3.push_back(out3.Name());
831 tcmd3.push_back(recon3.Name());
832 tcmd3.push_back(NULL);
834 // XPR(NTR "Running one 3-recon %s\n", CommandToString(tcmd3).c_str());
835 CHECK_EQ(0, xd3_main_cmdline(tcmd3.size() - 1,
836 const_cast<char**>(&tcmd3[0])));
837 // XPR(NTR "Should equal %s\n", f3.Name());
839 CHECK(recon3.EqualsSpec(spec3));
842 void TestMergeCommand1() {
843 /* Repeat random-input testing for a number of iterations.
844 * Test 2, 3, and 4-file scenarios (i.e., 1, 2, and 3-delta merges). */
846 FileSpec spec0(&rand);
847 FileSpec spec1(&rand);
848 FileSpec spec2(&rand);
849 FileSpec spec3(&rand);
851 SizeIterator<size_t, Sizes> si0(&rand, 10);
853 for (; !si0.Done(); si0.Next()) {
854 size_t size0 = si0.Get();
856 SizeIterator<size_t, Sizes> si1(&rand, 10);
857 for (; !si1.Done(); si1.Next()) {
858 size_t change1 = si1.Get();
864 // XPR(NTR "S0 = %lu\n", size0);
865 // XPR(NTR "C1 = %lu\n", change1);
868 size_t add1_pos = size0 ? rand.Rand32() % size0 : 0;
869 size_t del2_pos = size0 ? rand.Rand32() % size0 : 0;
871 spec0.GenerateFixedSize(size0);
873 ChangeList cl1, cl2, cl3;
875 size_t change3 = change1;
878 if (change3 >= size0) {
882 change3_pos = rand.Rand32() % (size0 - change3);
885 cl1.push_back(Change(Change::ADD, change1, add1_pos));
886 cl2.push_back(Change(Change::DELETE, change1, del2_pos));
887 cl3.push_back(Change(Change::MODIFY, change3, change3_pos));
889 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
890 spec1.ModifyTo(ChangeListMutator(cl2), &spec2);
891 spec2.ModifyTo(ChangeListMutator(cl3), &spec3);
893 FourWayMergeTest(spec0, spec1, spec2, spec3);
894 FourWayMergeTest(spec3, spec2, spec1, spec0);
899 void TestMergeCommand2() {
900 /* Same as above, different mutation pattern. */
901 /* TODO: run this with large sizes too */
902 /* TODO: run this with small sizes too */
904 FileSpec spec0(&rand);
905 FileSpec spec1(&rand);
906 FileSpec spec2(&rand);
907 FileSpec spec3(&rand);
909 SizeIterator<size_t, Sizes> si0(&rand, 10);
910 for (; !si0.Done(); si0.Next()) {
911 size_t size0 = si0.Get();
913 SizeIterator<size_t, Sizes> si1(&rand, 10);
914 for (; !si1.Done(); si1.Next()) {
915 size_t size1 = si1.Get();
917 SizeIterator<size_t, Sizes> si2(&rand, 10);
918 for (; !si2.Done(); si2.Next()) {
919 size_t size2 = si2.Get();
921 SizeIterator<size_t, Sizes> si3(&rand, 10);
922 for (; !si3.Done(); si3.Next()) {
923 size_t size3 = si3.Get();
925 // We're only interested in three sizes, strictly decreasing. */
926 if (size3 >= size2 || size2 >= size1 || size1 >= size0) {
930 // XPR(NTR "S0 = %lu\n", size0);
931 // XPR(NTR "S1 = %lu\n", size1);
932 // XPR(NTR "S2 = %lu\n", size2);
933 // XPR(NTR "S3 = %lu\n", size3);
936 spec0.GenerateFixedSize(size0);
938 ChangeList cl1, cl2, cl3;
940 cl1.push_back(Change(Change::DELETE, size0 - size1, 0));
941 cl2.push_back(Change(Change::DELETE, size0 - size2, 0));
942 cl3.push_back(Change(Change::DELETE, size0 - size3, 0));
944 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
945 spec0.ModifyTo(ChangeListMutator(cl2), &spec2);
946 spec0.ModifyTo(ChangeListMutator(cl3), &spec3);
948 FourWayMergeTest(spec0, spec1, spec2, spec3);
949 FourWayMergeTest(spec3, spec2, spec1, spec0);
956 }; // class Regtest<Constants>
958 #define TEST(x) XPR(NTR #x "...\n"); regtest.x()
960 // These tests are primarily tests of the testing framework itself.
964 TEST(TestRandomNumbers);
965 TEST(TestRandomFile);
967 TEST(TestModifyMutator);
968 TEST(TestAddMutator);
969 TEST(TestDeleteMutator);
970 TEST(TestCopyMutator);
971 TEST(TestMoveMutator);
972 TEST(TestOverwriteMutator);
975 // These are Xdelta tests.
978 XPR(NT "Blocksize %"Q"u readsize %"Q"u windowsize %"Q"u\n",
979 T::BLOCK_SIZE, T::READ_SIZE, T::WINDOW_SIZE);
981 TEST(TestEmptyInMemory);
982 TEST(TestBlockInMemory);
983 TEST(TestNonBlockingProgress);
984 TEST(TestHalfBlockCopy);
985 TEST(TestMergeCommand1);
986 TEST(TestMergeCommand2);
991 int main(int argc, char **argv)
993 vector<const char*> mcmd;
995 const char *sp = strrchr(argv[0], '/');
997 pn.append(argv[0], sp - argv[0] + 1);
999 pn.append("xdelta3");
1000 mcmd.push_back(pn.c_str());
1001 mcmd.push_back("test");
1002 mcmd.push_back(NULL);
1004 UnitTest<SmallBlock>();
1005 MainTest<SmallBlock>();
1006 MainTest<MixedBlock>();
1007 MainTest<PrimeBlock>();
1008 MainTest<OversizeBlock>();
1009 MainTest<LargeBlock>();
1011 CHECK_EQ(0, xd3_main_cmdline(mcmd.size() - 1,
1012 const_cast<char**>(&mcmd[0])));