"Fossies" - the Fresh Open Source Software Archive 
Member "xdelta3-3.0.11/testing/regtest.cc" (19 Nov 2015, 35870 Bytes) of package /linux/misc/xdelta3-3.0.11.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /* -*- Mode: C++ -*- */
2 #include "test.h"
3 #include "random.h"
4 #include "sizes.h"
5
6 template <typename Constants>
7 class Regtest {
8 public:
9 typedef typename Constants::Sizes Sizes;
10
11 struct Options {
12 Options()
13 : encode_srcwin_maxsz(1<<20),
14 block_size(Constants::BLOCK_SIZE),
15 window_size(Constants::WINDOW_SIZE),
16 size_known(false),
17 iopt_size(XD3_DEFAULT_IOPT_SIZE),
18 smatch_cfg(XD3_SMATCH_DEFAULT) { }
19
20 xoff_t encode_srcwin_maxsz;
21 size_t block_size;
22 xoff_t window_size;
23 bool size_known;
24 usize_t iopt_size;
25 xd3_smatch_cfg smatch_cfg;
26 };
27
28 #include "segment.h"
29 #include "modify.h"
30 #include "file.h"
31 #include "cmp.h"
32 #include "delta.h"
33
34 void InMemoryEncodeDecode(const FileSpec &source_file,
35 const FileSpec &target_file,
36 Block *coded_data,
37 const Options &options) {
38 xd3_stream encode_stream;
39 xd3_config encode_config;
40 xd3_source encode_source;
41
42 xd3_stream decode_stream;
43 xd3_config decode_config;
44 xd3_source decode_source;
45 xoff_t verified_bytes = 0;
46 xoff_t encoded_bytes = 0;
47
48 if (coded_data) {
49 coded_data->Reset();
50 }
51
52 memset(&encode_stream, 0, sizeof (encode_stream));
53 memset(&encode_source, 0, sizeof (encode_source));
54
55 memset(&decode_stream, 0, sizeof (decode_stream));
56 memset(&decode_source, 0, sizeof (decode_source));
57
58 xd3_init_config(&encode_config, XD3_ADLER32);
59 xd3_init_config(&decode_config, XD3_ADLER32);
60
61 encode_config.winsize = options.window_size;
62 encode_config.iopt_size = options.iopt_size;
63 encode_config.smatch_cfg = options.smatch_cfg;
64
65 CHECK_EQ(0, xd3_config_stream (&encode_stream, &encode_config));
66 CHECK_EQ(0, xd3_config_stream (&decode_stream, &decode_config));
67
68 encode_source.blksize = options.block_size;
69 decode_source.blksize = options.block_size;
70
71 encode_source.max_winsize = options.encode_srcwin_maxsz;
72 decode_source.max_winsize = options.encode_srcwin_maxsz;
73
74 if (!options.size_known)
75 {
76 xd3_set_source (&encode_stream, &encode_source);
77 xd3_set_source (&decode_stream, &decode_source);
78 }
79 else
80 {
81 xd3_set_source_and_size (&encode_stream, &encode_source,
82 source_file.Size());
83 xd3_set_source_and_size (&decode_stream, &decode_source,
84 source_file.Size());
85 }
86
87 BlockIterator source_iterator(source_file, options.block_size);
88 BlockIterator target_iterator(target_file, Constants::WINDOW_SIZE);
89 Block encode_source_block, decode_source_block;
90 Block decoded_block, target_block;
91 bool encoding = true;
92 bool done = false;
93 bool done_after_input = false;
94
95 IF_DEBUG1 (XPR(NTR "source %"Q"u[%"Q"u] target %"Q"u winsize %"Z"u\n",
96 source_file.Size(), options.block_size,
97 target_file.Size(),
98 Constants::WINDOW_SIZE));
99
100 while (!done) {
101 target_iterator.Get(&target_block);
102
103 xoff_t blks = target_iterator.Blocks();
104
105 IF_DEBUG2(XPR(NTR "target in %s: %"Q"u..%"Q"u %"Q"u(%"Q"u) "
106 "verified %"Q"u\n",
107 encoding ? "encoding" : "decoding",
108 target_iterator.Offset(),
109 target_iterator.Offset() + target_block.Size(),
110 target_iterator.Blkno(), blks, verified_bytes));
111
112 if (blks == 0 || target_iterator.Blkno() == (blks - 1)) {
113 xd3_set_flags(&encode_stream, XD3_FLUSH | encode_stream.flags);
114 }
115
116 xd3_avail_input(&encode_stream, target_block.Data(), target_block.Size());
117 encoded_bytes += target_block.Size();
118
119 process:
120 int ret;
121 const char *msg;
122 if (encoding) {
123 ret = xd3_encode_input(&encode_stream);
124 msg = encode_stream.msg;
125 } else {
126 ret = xd3_decode_input(&decode_stream);
127 msg = decode_stream.msg;
128 }
129 (void) msg;
130
131 switch (ret) {
132 case XD3_OUTPUT:
133 if (encoding) {
134 if (coded_data != NULL) {
135 // Optional encoded-output to the caller
136 coded_data->Append(encode_stream.next_out,
137 encode_stream.avail_out);
138 }
139 // Feed this data to the decoder.
140 xd3_avail_input(&decode_stream,
141 encode_stream.next_out,
142 encode_stream.avail_out);
143 xd3_consume_output(&encode_stream);
144 encoding = false;
145 } else {
146 decoded_block.Append(decode_stream.next_out,
147 decode_stream.avail_out);
148 xd3_consume_output(&decode_stream);
149 }
150 goto process;
151
152 case XD3_GETSRCBLK: {
153 xd3_source *src = (encoding ? &encode_source : &decode_source);
154 Block *block = (encoding ? &encode_source_block : &decode_source_block);
155 if (encoding) {
156 IF_DEBUG2(XPR(NTR "[srcblock] %"Q"u last srcpos %"Q"u "
157 "encodepos %"Q"u\n",
158 encode_source.getblkno,
159 encode_stream.match_last_srcpos,
160 encode_stream.input_position + encode_stream.total_in));
161 }
162
163 source_iterator.SetBlock(src->getblkno);
164 source_iterator.Get(block);
165 src->curblkno = src->getblkno;
166 src->onblk = block->Size();
167 src->curblk = block->Data();
168
169 goto process;
170 }
171
172 case XD3_INPUT:
173 if (!encoding) {
174 encoding = true;
175 goto process;
176 } else {
177 if (done_after_input) {
178 done = true;
179 continue;
180 }
181
182 if (target_block.Size() < target_iterator.BlockSize()) {
183 encoding = false;
184 } else {
185 target_iterator.Next();
186 }
187 continue;
188 }
189
190 case XD3_WINFINISH:
191 if (encoding) {
192 if (encode_stream.flags & XD3_FLUSH) {
193 done_after_input = true;
194 }
195 encoding = false;
196 } else {
197 CHECK_EQ(0, CmpDifferentBlockBytesAtOffset(decoded_block,
198 target_file,
199 verified_bytes));
200 verified_bytes += decoded_block.Size();
201 decoded_block.Reset();
202 encoding = true;
203 }
204 goto process;
205
206 case XD3_WINSTART:
207 case XD3_GOTHEADER:
208 goto process;
209
210 default:
211 XPR(NTR "%s = %s %s\n", encoding ? "E " : " D",
212 xd3_strerror(ret),
213 msg == NULL ? "" : msg);
214
215 CHECK_EQ(0, ret);
216 CHECK_EQ(-1, ret);
217 }
218 }
219
220 CHECK_EQ(target_file.Size(), encoded_bytes);
221 CHECK_EQ(target_file.Size(), verified_bytes);
222 CHECK_EQ(0, xd3_close_stream(&decode_stream));
223 CHECK_EQ(0, xd3_close_stream(&encode_stream));
224 xd3_free_stream(&encode_stream);
225 xd3_free_stream(&decode_stream);
226 }
227
228 void MainEncodeDecode(const TmpFile &source_file,
229 const TmpFile &target_file,
230 ExtFile *coded_data,
231 const Options &options) {
232 vector<const char*> ecmd;
233 char bbuf[16];
234 snprintf(bbuf, sizeof(bbuf), "-B%"Q"u", options.encode_srcwin_maxsz);
235 ecmd.push_back("xdelta3");
236 ecmd.push_back(bbuf);
237 ecmd.push_back("-s");
238 ecmd.push_back(source_file.Name());
239 ecmd.push_back(target_file.Name());
240 ecmd.push_back(coded_data->Name());
241 ecmd.push_back(NULL);
242
243 CHECK_EQ(0, xd3_main_cmdline(ecmd.size() - 1,
244 const_cast<char**>(&ecmd[0])));
245
246 vector<const char*> dcmd;
247 ExtFile recon_file;
248 dcmd.push_back("xdelta3");
249 ecmd.push_back(bbuf);
250 dcmd.push_back("-d");
251 dcmd.push_back("-s");
252 dcmd.push_back(source_file.Name());
253 dcmd.push_back(coded_data->Name());
254 dcmd.push_back(recon_file.Name());
255 dcmd.push_back(NULL);
256
257 CHECK_EQ(0, xd3_main_cmdline(dcmd.size() - 1,
258 const_cast<char**>(&dcmd[0])));
259
260 CHECK_EQ(0, test_compare_files(recon_file.Name(),
261 target_file.Name()));
262 }
263
264 // Similar to xd3_process_memory, with support for test Options.
265 // Exercises xd3_process_stream.
266 int TestProcessMemory (int is_encode,
267 int (*func) (xd3_stream *),
268 const uint8_t *input,
269 usize_t input_size,
270 const uint8_t *source,
271 usize_t source_size,
272 uint8_t *output,
273 usize_t *output_size,
274 usize_t output_size_max,
275 const Options &options) {
276 xd3_stream stream;
277 xd3_config config;
278 xd3_source src;
279 int ret;
280
281 memset (& stream, 0, sizeof (stream));
282 memset (& config, 0, sizeof (config));
283
284 if (is_encode)
285 {
286 config.winsize = input_size;
287 config.iopt_size = options.iopt_size;
288 config.sprevsz = xd3_pow2_roundup (config.winsize);
289 }
290
291 if ((ret = xd3_config_stream (&stream, &config)) != 0)
292 {
293 goto exit;
294 }
295
296 if (source != NULL)
297 {
298 memset (& src, 0, sizeof (src));
299
300 src.blksize = source_size;
301 src.onblk = source_size;
302 src.curblk = source;
303 src.curblkno = 0;
304 src.max_winsize = source_size;
305
306 if ((ret = xd3_set_source_and_size (&stream, &src, source_size)) != 0)
307 {
308 goto exit;
309 }
310 }
311
312 if ((ret = xd3_process_stream (is_encode,
313 & stream,
314 func, 1,
315 input, input_size,
316 output,
317 output_size,
318 output_size_max)) != 0)
319 {
320 goto exit;
321 }
322
323 exit:
324 if (ret != 0)
325 {
326 IF_DEBUG2 (DP(RINT "test_process_memory: %d: %s\n", ret, stream.msg));
327 }
328 xd3_free_stream(&stream);
329 return ret;
330 }
331
332 void EncodeDecodeAPI(const FileSpec &spec0, const FileSpec &spec1,
333 Block *delta, const Options &options) {
334 Block from;
335 Block to;
336 spec0.Get(&from, 0, spec0.Size());
337 spec1.Get(&to, 0, spec1.Size());
338
339 delta->SetSize(to.Size() * 1.5);
340 usize_t out_size;
341 int enc_ret = TestProcessMemory(true,
342 &xd3_encode_input,
343 to.Data(),
344 to.Size(),
345 from.Data(),
346 from.Size(),
347 delta->Data(),
348 &out_size,
349 delta->Size(),
350 options);
351 CHECK_EQ(0, enc_ret);
352 delta->SetSize(out_size);
353
354 Block recon;
355 recon.SetSize(to.Size());
356 usize_t recon_size;
357 int dec_ret = xd3_decode_memory(delta->Data(),
358 delta->Size(),
359 from.Data(),
360 from.Size(),
361 recon.Data(),
362 &recon_size,
363 recon.Size(),
364 0);
365 CHECK_EQ(0, dec_ret);
366 CHECK_EQ(0, CmpDifferentBlockBytes(to, recon));
367 }
368
369 //////////////////////////////////////////////////////////////////////
370
371 void TestPrintf() {
372 char buf[64];
373 xoff_t x = XOFF_T_MAX;
374 snprintf_func (buf, sizeof(buf), "%"Q"u", x);
375 const char *expect = XD3_USE_LARGEFILE64 ?
376 "18446744073709551615" : "4294967295";
377 XD3_ASSERT(strcmp (buf, expect) == 0);
378 }
379
380 void TestRandomNumbers() {
381 MTRandom rand;
382 int rounds = 1<<20;
383 uint64_t usum = 0;
384 uint64_t esum = 0;
385
386 for (int i = 0; i < rounds; i++) {
387 usum += rand.Rand32();
388 esum += rand.ExpRand32(1024);
389 }
390
391 double allowed_error = 0.01;
392
393 uint32_t umean = usum / rounds;
394 uint32_t emean = esum / rounds;
395
396 uint32_t uexpect = UINT32_MAX / 2;
397 uint32_t eexpect = 1024;
398
399 if (umean < uexpect * (1.0 - allowed_error) ||
400 umean > uexpect * (1.0 + allowed_error)) {
401 XPR(NT "uniform mean error: %u != %u\n", umean, uexpect);
402 abort();
403 }
404
405 if (emean < eexpect * (1.0 - allowed_error) ||
406 emean > eexpect * (1.0 + allowed_error)) {
407 XPR(NT "exponential mean error: %u != %u\n", emean, eexpect);
408 abort();
409 }
410 }
411
412 void TestRandomFile() {
413 MTRandom rand1;
414 FileSpec spec1(&rand1);
415 BlockIterator bi(spec1);
416
417 spec1.GenerateFixedSize(0);
418 CHECK_EQ(0, spec1.Size());
419 CHECK_EQ(0, spec1.Segments());
420 CHECK_EQ(0, spec1.Blocks());
421 bi.SetBlock(0);
422 CHECK_EQ(0, bi.BytesOnBlock());
423
424 spec1.GenerateFixedSize(1);
425 CHECK_EQ(1, spec1.Size());
426 CHECK_EQ(1, spec1.Segments());
427 CHECK_EQ(1, spec1.Blocks());
428 bi.SetBlock(0);
429 CHECK_EQ(1, bi.BytesOnBlock());
430
431 spec1.GenerateFixedSize(Constants::BLOCK_SIZE);
432 CHECK_EQ(Constants::BLOCK_SIZE, spec1.Size());
433 CHECK_EQ(1, spec1.Segments());
434 CHECK_EQ(1, spec1.Blocks());
435 bi.SetBlock(0);
436 CHECK_EQ(Constants::BLOCK_SIZE, bi.BytesOnBlock());
437 bi.SetBlock(1);
438 CHECK_EQ(0, bi.BytesOnBlock());
439
440 spec1.GenerateFixedSize(Constants::BLOCK_SIZE + 1);
441 CHECK_EQ(Constants::BLOCK_SIZE + 1, spec1.Size());
442 CHECK_EQ(2, spec1.Segments());
443 CHECK_EQ(2, spec1.Blocks());
444 bi.SetBlock(0);
445 CHECK_EQ(Constants::BLOCK_SIZE, bi.BytesOnBlock());
446 bi.SetBlock(1);
447 CHECK_EQ(1, bi.BytesOnBlock());
448
449 spec1.GenerateFixedSize(Constants::BLOCK_SIZE * 2);
450 CHECK_EQ(Constants::BLOCK_SIZE * 2, spec1.Size());
451 CHECK_EQ(2, spec1.Segments());
452 CHECK_EQ(2, spec1.Blocks());
453 bi.SetBlock(0);
454 CHECK_EQ(Constants::BLOCK_SIZE, bi.BytesOnBlock());
455 bi.SetBlock(1);
456 CHECK_EQ(Constants::BLOCK_SIZE, bi.BytesOnBlock());
457 }
458
459 void TestFirstByte() {
460 MTRandom rand;
461 FileSpec spec0(&rand);
462 FileSpec spec1(&rand);
463
464 spec0.GenerateFixedSize(0);
465 spec1.GenerateFixedSize(1);
466 CHECK_EQ(0, CmpDifferentBytes(spec0, spec0));
467 CHECK_EQ(0, CmpDifferentBytes(spec1, spec1));
468 CHECK_EQ(1, CmpDifferentBytes(spec0, spec1));
469 CHECK_EQ(1, CmpDifferentBytes(spec1, spec0));
470
471 spec0.GenerateFixedSize(1);
472 spec0.ModifyTo(Modify1stByte(), &spec1);
473 CHECK_EQ(1, CmpDifferentBytes(spec0, spec1));
474
475 spec0.GenerateFixedSize(Constants::BLOCK_SIZE + 1);
476 spec0.ModifyTo(Modify1stByte(), &spec1);
477 CHECK_EQ(1, CmpDifferentBytes(spec0, spec1));
478
479 SizeIterator<size_t, Sizes> si(&rand, Constants::TEST_ROUNDS);
480
481 for (; !si.Done(); si.Next()) {
482 size_t size = si.Get();
483 if (size == 0) {
484 continue;
485 }
486 spec0.GenerateFixedSize(size);
487 spec0.ModifyTo(Modify1stByte(), &spec1);
488 InMemoryEncodeDecode(spec0, spec1, NULL, Options());
489 }
490 }
491
492 void TestModifyMutator() {
493 MTRandom rand;
494 FileSpec spec0(&rand);
495 FileSpec spec1(&rand);
496
497 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 3);
498
499 struct {
500 size_t size;
501 size_t addr;
502 } test_cases[] = {
503 { Constants::BLOCK_SIZE, 0 },
504 { Constants::BLOCK_SIZE / 2, 1 },
505 { Constants::BLOCK_SIZE, 1 },
506 { Constants::BLOCK_SIZE * 2, 1 },
507 };
508
509 for (size_t i = 0; i < SIZEOF_ARRAY(test_cases); i++) {
510 ChangeList cl1;
511 cl1.push_back(Change(Change::MODIFY, test_cases[i].size,
512 test_cases[i].addr));
513 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
514 CHECK_EQ(spec0.Size(), spec1.Size());
515
516 size_t diff = CmpDifferentBytes(spec0, spec1);
517 CHECK_LE(diff, test_cases[i].size);
518
519 // There is a 1/256 probability of the changed byte matching the
520 // original value. The following allows double the probability to
521 // pass.
522 CHECK_GE(diff, test_cases[i].size - (2 * test_cases[i].size / 256));
523
524 InMemoryEncodeDecode(spec0, spec1, NULL, Options());
525 }
526 }
527
528 void TestAddMutator() {
529 MTRandom rand;
530 FileSpec spec0(&rand);
531 FileSpec spec1(&rand);
532
533 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 2);
534 // TODO: fix this test (for all block sizes)! it's broken because
535 // the same byte could be added?
536
537 struct {
538 size_t size;
539 size_t addr;
540 size_t expected_adds;
541 } test_cases[] = {
542 { 1, 0, 2 /* 1st byte, last byte (short block) */ },
543 { 1, 1, 3 /* 1st 2 bytes, last byte */ },
544 { 1, Constants::BLOCK_SIZE - 1, 2 /* changed, last */ },
545 { 1, Constants::BLOCK_SIZE, 2 /* changed, last */ },
546 { 1, Constants::BLOCK_SIZE + 1, 3 /* changed + 1st of 2nd block, last */ },
547 { 1, 2 * Constants::BLOCK_SIZE, 1 /* last byte */ },
548 };
549
550 for (size_t i = 0; i < SIZEOF_ARRAY(test_cases); i++) {
551 ChangeList cl1;
552 cl1.push_back(Change(Change::ADD, test_cases[i].size, test_cases[i].addr));
553 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
554 CHECK_EQ(spec0.Size() + test_cases[i].size, spec1.Size());
555
556 Block coded;
557 InMemoryEncodeDecode(spec0, spec1, &coded, Options());
558
559 Delta delta(coded);
560 CHECK_EQ(test_cases[i].expected_adds,
561 delta.AddedBytes());
562 }
563 }
564
565 void TestDeleteMutator() {
566 MTRandom rand;
567 FileSpec spec0(&rand);
568 FileSpec spec1(&rand);
569
570 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 4);
571
572 struct {
573 size_t size;
574 size_t addr;
575 } test_cases[] = {
576 // Note: an entry { Constants::BLOCK_SIZE, 0 },
577 // does not work because the xd3_srcwin_move_point logic won't
578 // find a copy if it occurs >= double its size into the file.
579 { Constants::BLOCK_SIZE / 2, 0 },
580 { Constants::BLOCK_SIZE / 2, Constants::BLOCK_SIZE / 2 },
581 { Constants::BLOCK_SIZE, Constants::BLOCK_SIZE / 2 },
582 { Constants::BLOCK_SIZE * 2, Constants::BLOCK_SIZE * 3 / 2 },
583 { Constants::BLOCK_SIZE, Constants::BLOCK_SIZE * 2 },
584 };
585
586 for (size_t i = 0; i < SIZEOF_ARRAY(test_cases); i++) {
587 ChangeList cl1;
588 cl1.push_back(Change(Change::DELRANGE, test_cases[i].size,
589 test_cases[i].addr));
590 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
591 CHECK_EQ(spec0.Size() - test_cases[i].size, spec1.Size());
592
593 Block coded;
594 InMemoryEncodeDecode(spec0, spec1, &coded, Options());
595
596 Delta delta(coded);
597 CHECK_EQ(0, delta.AddedBytes());
598 }
599 }
600
601 void TestCopyMutator() {
602 MTRandom rand;
603 FileSpec spec0(&rand);
604 FileSpec spec1(&rand);
605
606 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 3);
607
608 struct {
609 size_t size;
610 size_t from;
611 size_t to;
612 } test_cases[] = {
613 // Copy is difficult to write tests for because where Xdelta finds
614 // copies, it does not enter checksums. So these tests copy data from
615 // later to earlier so that checksumming will start.
616 { Constants::BLOCK_SIZE / 2, Constants::BLOCK_SIZE / 2, 0 },
617 { Constants::BLOCK_SIZE, 2 * Constants::BLOCK_SIZE,
618 Constants::BLOCK_SIZE, },
619 };
620
621 for (size_t i = 0; i < SIZEOF_ARRAY(test_cases); i++) {
622 ChangeList cl1;
623 cl1.push_back(Change(Change::COPY, test_cases[i].size,
624 test_cases[i].from, test_cases[i].to));
625 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
626 CHECK_EQ(spec0.Size() + test_cases[i].size, spec1.Size());
627
628 Block coded;
629 InMemoryEncodeDecode(spec0, spec1, &coded, Options());
630
631 Delta delta(coded);
632 CHECK_EQ(0, delta.AddedBytes());
633 }
634 }
635
636 void TestMoveMutator() {
637 MTRandom rand;
638 FileSpec spec0(&rand);
639 FileSpec spec1(&rand);
640
641 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 3);
642
643 struct {
644 size_t size;
645 size_t from;
646 size_t to;
647 } test_cases[] = {
648 // This is easier to test than Copy but has the same trouble as Delete.
649 { Constants::BLOCK_SIZE / 2, Constants::BLOCK_SIZE / 2, 0 },
650 { Constants::BLOCK_SIZE / 2, 0, Constants::BLOCK_SIZE / 2 },
651 { Constants::BLOCK_SIZE, Constants::BLOCK_SIZE, 2 *
652 Constants::BLOCK_SIZE },
653 { Constants::BLOCK_SIZE, 2 * Constants::BLOCK_SIZE,
654 Constants::BLOCK_SIZE },
655 { Constants::BLOCK_SIZE * 3 / 2, Constants::BLOCK_SIZE,
656 Constants::BLOCK_SIZE * 3 / 2 },
657
658 // This is a no-op
659 { Constants::BLOCK_SIZE, Constants::BLOCK_SIZE * 2,
660 3 * Constants::BLOCK_SIZE },
661 };
662
663 for (size_t i = 0; i < SIZEOF_ARRAY(test_cases); i++) {
664 ChangeList cl1;
665 cl1.push_back(Change(Change::MOVE, test_cases[i].size,
666 test_cases[i].from, test_cases[i].to));
667 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
668 CHECK_EQ(spec0.Size(), spec1.Size());
669
670 Block coded;
671 InMemoryEncodeDecode(spec0, spec1, &coded, Options());
672
673 Delta delta(coded);
674 CHECK_EQ(0, delta.AddedBytes());
675 }
676 }
677
678 void TestOverwriteMutator() {
679 MTRandom rand;
680 FileSpec spec0(&rand);
681 FileSpec spec1(&rand);
682
683 spec0.GenerateFixedSize(Constants::BLOCK_SIZE);
684
685 ChangeList cl1;
686 cl1.push_back(Change(Change::COPYOVER, 10, 0, 20));
687 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
688 CHECK_EQ(spec0.Size(), spec1.Size());
689
690 Block b0, b1;
691 BlockIterator(spec0).Get(&b0);
692 BlockIterator(spec1).Get(&b1);
693
694 CHECK(memcmp(b0.Data(), b1.Data() + 20, 10) == 0);
695 CHECK(memcmp(b0.Data(), b1.Data(), 20) == 0);
696 CHECK(memcmp(b0.Data() + 30, b1.Data() + 30,
697 Constants::BLOCK_SIZE - 30) == 0);
698
699 cl1.clear();
700 cl1.push_back(Change(Change::COPYOVER, 10, 20, (xoff_t)0));
701 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
702 CHECK_EQ(spec0.Size(), spec1.Size());
703
704 BlockIterator(spec0).Get(&b0);
705 BlockIterator(spec1).Get(&b1);
706
707 CHECK(memcmp(b0.Data() + 20, b1.Data(), 10) == 0);
708 CHECK(memcmp(b0.Data() + 10, b1.Data() + 10,
709 Constants::BLOCK_SIZE - 10) == 0);
710 }
711
712 // Note: this test is written to expose a problem, but the problem was
713 // only exposed with BLOCK_SIZE = 128.
714 void TestNonBlocking() {
715 MTRandom rand;
716 FileSpec spec0(&rand);
717 FileSpec spec1(&rand);
718 FileSpec spec2(&rand);
719
720 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 3);
721
722 // This is a lazy target match
723 Change ct(Change::COPYOVER, 22,
724 Constants::BLOCK_SIZE + 50,
725 Constants::BLOCK_SIZE + 20);
726
727 // This is a source match just after the block boundary, shorter
728 // than the lazy target match.
729 Change cs1(Change::COPYOVER, 16,
730 Constants::BLOCK_SIZE + 51,
731 Constants::BLOCK_SIZE - 1);
732
733 // This overwrites the original source bytes.
734 Change cs2(Change::MODIFY, 108,
735 Constants::BLOCK_SIZE + 20);
736
737 // This changes the first blocks
738 Change c1st(Change::MODIFY, Constants::BLOCK_SIZE - 2, 0);
739
740 ChangeList csl;
741 csl.push_back(cs1);
742 csl.push_back(cs2);
743 csl.push_back(c1st);
744
745 spec0.ModifyTo(ChangeListMutator(csl), &spec1);
746
747 ChangeList ctl;
748 ctl.push_back(ct);
749 ctl.push_back(c1st);
750
751 spec0.ModifyTo(ChangeListMutator(ctl), &spec2);
752
753 InMemoryEncodeDecode(spec1, spec2, NULL, Options());
754 }
755
756 void TestEmptyInMemory() {
757 MTRandom rand;
758 FileSpec spec0(&rand);
759 FileSpec spec1(&rand);
760 Block block;
761
762 spec0.GenerateFixedSize(0);
763 spec1.GenerateFixedSize(0);
764
765 InMemoryEncodeDecode(spec0, spec1, &block, Options());
766
767 Delta delta(block);
768 CHECK_LT(0, block.Size());
769 CHECK_EQ(1, delta.Windows());
770 }
771
772 void TestBlockInMemory() {
773 MTRandom rand;
774 FileSpec spec0(&rand);
775 FileSpec spec1(&rand);
776 Block block;
777
778 spec0.GenerateFixedSize(Constants::BLOCK_SIZE);
779 spec1.GenerateFixedSize(Constants::BLOCK_SIZE);
780
781 InMemoryEncodeDecode(spec0, spec1, &block, Options());
782
783 Delta delta(block);
784 CHECK_EQ(spec1.Blocks(Constants::WINDOW_SIZE), delta.Windows());
785 }
786
787 void TestSmallStride() {
788 MTRandom rand;
789 FileSpec spec0(&rand);
790 usize_t size = Constants::BLOCK_SIZE * 4;
791 spec0.GenerateFixedSize(size);
792
793 // Note: Not very good performance due to hash collisions, note 3x
794 // multiplier below.
795 for (int s = 15; s < 101; s++) {
796 usize_t changes = 0;
797 ChangeList cl;
798 for (usize_t j = s; j < size; j += s, ++changes)
799 {
800 cl.push_back(Change(Change::MODIFY, 1, j));
801 }
802
803 FileSpec spec1(&rand);
804 spec0.ModifyTo(ChangeListMutator(cl), &spec1);
805
806 Options options;
807 options.encode_srcwin_maxsz = size;
808 options.iopt_size = 128;
809 options.smatch_cfg = XD3_SMATCH_SLOW;
810 options.size_known = false;
811
812 Block block;
813 InMemoryEncodeDecode(spec0, spec1, &block, options);
814 Delta delta(block);
815
816 IF_DEBUG1(DP(RINT "[stride=%d] changes=%u adds=%"Q"u\n",
817 s, changes, delta.AddedBytes()));
818 double allowance = Constants::BLOCK_SIZE < 8192 || s < 30 ? 3.0 : 1.1;
819 CHECK_GE(allowance * changes, (double)delta.AddedBytes());
820 }
821 }
822
823 void TestCopyWindow() {
824 // Construct an input that has many copies, to fill the IOPT buffer
825 // and force a source window decision. "srclen" may be set to a
826 // value that goes beyond the end-of-source.
827 const int clen = 16;
828 const int size = 4096;
829 const int nmov = size / clen;
830 const int iters = 16;
831 uint32_t added_01 = 0;
832 uint32_t added_10 = 0;
833 for (int i = 1; i <= iters; i++) {
834 MTRandom rand(MTRandom::TEST_SEED1 * i);
835 FileSpec spec0(&rand);
836 ChangeList cl;
837
838 spec0.GenerateFixedSize(size);
839
840 for (int j = 0; j < nmov; j += 2)
841 {
842 cl.push_back(Change(Change::MOVE,
843 clen, (j + 1) * clen, j * clen));
844 }
845
846 FileSpec spec1(&rand);
847 spec0.ModifyTo(ChangeListMutator(cl), &spec1);
848
849 Options options;
850 options.encode_srcwin_maxsz = size;
851 options.iopt_size = 128;
852 options.smatch_cfg = XD3_SMATCH_SLOW;
853
854 Block block1;
855 InMemoryEncodeDecode(spec0, spec1, &block1, options);
856 Delta delta1(block1);
857 // Allow one missed window (e.g., hash collisions)
858 added_01 += delta1.AddedBytes();
859
860 Block block2;
861 InMemoryEncodeDecode(spec1, spec0, &block2, options);
862 Delta delta2(block2);
863 // Allow one missed window (e.g., hash collisions)
864 added_10 += delta2.AddedBytes();
865
866 Block block3;
867 Block block4;
868 EncodeDecodeAPI(spec0, spec1, &block3, options);
869 EncodeDecodeAPI(spec1, spec0, &block4, options);
870 }
871 // Average less than 0.5 misses (of length clen) per iteration.
872 CHECK_GE(clen * iters / 2, added_01);
873 CHECK_GE(clen * iters / 2, added_10);
874 }
875
876 void TestCopyFromEnd() {
877 // Copies from the end of the source buffer, which reach a block
878 // boundary end-of-file.
879 const int size = 4096;
880 const int clen = 16;
881 const int nmov = (size / 2) / clen;
882 const int iters = 16;
883 uint32_t added_01 = 0;
884 uint32_t added_10 = 0;
885 for (int i = 1; i <= iters; i++) {
886 MTRandom rand(MTRandom::TEST_SEED1 * i);
887 FileSpec spec0(&rand);
888 ChangeList cl;
889
890 spec0.GenerateFixedSize(size);
891
892 cl.push_back(Change(Change::MODIFY, 2012, 2048));
893
894 for (int j = 0; j < nmov; j += 2)
895 {
896 cl.push_back(Change(Change::MOVE,
897 clen, (j + 1) * clen, j * clen));
898 }
899
900 cl.push_back(Change(Change::COPYOVER, 28, 4068, 3000));
901 cl.push_back(Change(Change::COPYOVER, 30, 4066, 3100));
902 cl.push_back(Change(Change::COPYOVER, 32, 4064, 3200));
903
904 FileSpec spec1(&rand);
905 spec0.ModifyTo(ChangeListMutator(cl), &spec1);
906
907 Options options;
908 options.encode_srcwin_maxsz = size;
909 options.iopt_size = 128;
910 options.smatch_cfg = XD3_SMATCH_SLOW;
911
912 Block block1;
913 InMemoryEncodeDecode(spec0, spec1, &block1, options);
914 Delta delta1(block1);
915 added_01 += delta1.AddedBytes();
916
917 Block block2;
918 InMemoryEncodeDecode(spec1, spec0, &block2, options);
919 Delta delta2(block2);
920 added_10 += delta2.AddedBytes();
921
922 Block block3;
923 Block block4;
924 EncodeDecodeAPI(spec0, spec1, &block3, options);
925 EncodeDecodeAPI(spec1, spec0, &block4, options);
926 }
927 CHECK_GE(2000 * iters, added_01);
928 CHECK_LE(2000 * iters, added_10);
929 }
930
931 void TestHalfBlockCopy() {
932 // Create a half-block copy, 7.5 blocks apart, in a pair of files:
933 // 0 1 ... 6 7
934 // spec0 [bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb][ccccc][bbbb_]
935 // spec1 [aaaaa][ccccc][aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_]
936 // where stage=
937 // 0: the final block is full
938 // a. (source)spec1->(target)spec0 copies block C: reads 8 source
939 // blocks during target block 0.
940 // b. (source)spec0->(target)spec1 does not copy block C b/c attempt
941 // to read past EOF empties block 0 from (virtual) block cache
942 // 1: the final block is less than full.
943 // a. (same) copies block C
944 // b. (same) copies block C, unlike 0a, no attempt to read past EOF
945 //
946 // "virtual" above refers to XD3_TOOFARBACK, since there is no caching
947 // in the API, there is simply a promise not to request blocks that are
948 // beyond source->max_winsize from the last known source file position.
949 for (int stage = 0; stage < 2; stage++)
950 {
951 IF_DEBUG1 (DP(RINT "half_block_copy stage %d\n", stage));
952
953 MTRandom rand;
954 FileSpec spec0(&rand);
955 FileSpec spec1(&rand);
956
957 spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 8 - stage);
958
959 ChangeList cl1;
960 cl1.push_back(Change(Change::MODIFY,
961 Constants::BLOCK_SIZE / 2, // size
962 0));
963 cl1.push_back(Change(Change::COPYOVER,
964 Constants::BLOCK_SIZE / 2, // size
965 Constants::BLOCK_SIZE * 7, // offset
966 Constants::BLOCK_SIZE / 2));
967 cl1.push_back(Change(Change::MODIFY,
968 Constants::BLOCK_SIZE * 7,
969 Constants::BLOCK_SIZE - stage));
970 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
971
972 Options options;
973 options.encode_srcwin_maxsz = Constants::BLOCK_SIZE * 8;
974
975 Block block0;
976 Block block1;
977 InMemoryEncodeDecode(spec0, spec1, &block0, options);
978 InMemoryEncodeDecode(spec1, spec0, &block1, options);
979
980 Delta delta0(block0);
981 Delta delta1(block1);
982
983 const int yes =
984 Constants::BLOCK_SIZE * 8 - Constants::BLOCK_SIZE / 2;
985 const int no =
986 Constants::BLOCK_SIZE * 8 - Constants::BLOCK_SIZE / 2;
987
988 if (stage == 0)
989 {
990 CHECK_EQ(yes, delta0.AddedBytes());
991 CHECK_EQ(no, delta1.AddedBytes());
992 }
993 else
994 {
995 CHECK_EQ(yes, delta0.AddedBytes());
996 CHECK_EQ(yes, delta1.AddedBytes());
997 }
998 }
999 }
1000
1001 void FourWayMergeTest(const FileSpec &spec0,
1002 const FileSpec &spec1,
1003 const FileSpec &spec2,
1004 const FileSpec &spec3) {
1005 TmpFile f0, f1, f2, f3;
1006 ExtFile d01, d12, d23;
1007 Options options;
1008 options.encode_srcwin_maxsz =
1009 std::max(spec0.Size(), options.encode_srcwin_maxsz);
1010
1011 spec0.WriteTmpFile(&f0);
1012 spec1.WriteTmpFile(&f1);
1013 spec2.WriteTmpFile(&f2);
1014 spec3.WriteTmpFile(&f3);
1015
1016 MainEncodeDecode(f0, f1, &d01, options);
1017 MainEncodeDecode(f1, f2, &d12, options);
1018 MainEncodeDecode(f2, f3, &d23, options);
1019
1020 // Merge 2
1021 ExtFile out;
1022 vector<const char*> mcmd;
1023 mcmd.push_back("xdelta3");
1024 mcmd.push_back("merge");
1025 mcmd.push_back("-m");
1026 mcmd.push_back(d01.Name());
1027 mcmd.push_back(d12.Name());
1028 mcmd.push_back(out.Name());
1029 mcmd.push_back(NULL);
1030
1031 // XPR(NTR "Running one merge: %s\n", CommandToString(mcmd).c_str());
1032 CHECK_EQ(0, xd3_main_cmdline(mcmd.size() - 1,
1033 const_cast<char**>(&mcmd[0])));
1034
1035 ExtFile recon;
1036 vector<const char*> tcmd;
1037 tcmd.push_back("xdelta3");
1038 tcmd.push_back("-d");
1039 tcmd.push_back("-s");
1040 tcmd.push_back(f0.Name());
1041 tcmd.push_back(out.Name());
1042 tcmd.push_back(recon.Name());
1043 tcmd.push_back(NULL);
1044
1045 // XPR(NTR "Running one recon! %s\n", CommandToString(tcmd).c_str());
1046 CHECK_EQ(0, xd3_main_cmdline(tcmd.size() - 1,
1047 const_cast<char**>(&tcmd[0])));
1048 // XPR(NTR "Should equal! %s\n", f2.Name());
1049
1050 CHECK(recon.EqualsSpec(spec2));
1051
1052 // Merge 3
1053 ExtFile out3;
1054 vector<const char*> mcmd3;
1055 mcmd3.push_back("xdelta3");
1056 mcmd3.push_back("merge");
1057 mcmd3.push_back("-m");
1058 mcmd3.push_back(d01.Name());
1059 mcmd3.push_back("-m");
1060 mcmd3.push_back(d12.Name());
1061 mcmd3.push_back(d23.Name());
1062 mcmd3.push_back(out3.Name());
1063 mcmd3.push_back(NULL);
1064
1065 // XPR(NTR "Running one 3-merge: %s\n", CommandToString(mcmd3).c_str());
1066 CHECK_EQ(0, xd3_main_cmdline(mcmd3.size() - 1,
1067 const_cast<char**>(&mcmd3[0])));
1068
1069 ExtFile recon3;
1070 vector<const char*> tcmd3;
1071 tcmd3.push_back("xdelta3");
1072 tcmd3.push_back("-d");
1073 tcmd3.push_back("-s");
1074 tcmd3.push_back(f0.Name());
1075 tcmd3.push_back(out3.Name());
1076 tcmd3.push_back(recon3.Name());
1077 tcmd3.push_back(NULL);
1078
1079 // XPR(NTR "Running one 3-recon %s\n", CommandToString(tcmd3).c_str());
1080 CHECK_EQ(0, xd3_main_cmdline(tcmd3.size() - 1,
1081 const_cast<char**>(&tcmd3[0])));
1082 // XPR(NTR "Should equal %s\n", f3.Name());
1083
1084 CHECK(recon3.EqualsSpec(spec3));
1085 }
1086
1087 void TestMergeCommand1() {
1088 /* Repeat random-input testing for a number of iterations.
1089 * Test 2, 3, and 4-file scenarios (i.e., 1, 2, and 3-delta merges). */
1090 MTRandom rand;
1091 FileSpec spec0(&rand);
1092 FileSpec spec1(&rand);
1093 FileSpec spec2(&rand);
1094 FileSpec spec3(&rand);
1095
1096 SizeIterator<size_t, Sizes> si0(&rand, 10);
1097
1098 for (; !si0.Done(); si0.Next()) {
1099 size_t size0 = si0.Get();
1100
1101 SizeIterator<size_t, Sizes> si1(&rand, 10);
1102 for (; !si1.Done(); si1.Next()) {
1103 size_t change1 = si1.Get();
1104
1105 if (change1 == 0) {
1106 continue;
1107 }
1108
1109 // XPR(NTR "S0 = %lu\n", size0);
1110 // XPR(NTR "C1 = %lu\n", change1);
1111 // XPR(NTR ".");
1112
1113 size_t add1_pos = size0 ? rand.Rand32() % size0 : 0;
1114 size_t del2_pos = size0 ? rand.Rand32() % size0 : 0;
1115
1116 spec0.GenerateFixedSize(size0);
1117
1118 ChangeList cl1, cl2, cl3;
1119
1120 size_t change3 = change1;
1121 size_t change3_pos;
1122
1123 if (change3 >= size0) {
1124 change3 = size0;
1125 change3_pos = 0;
1126 } else {
1127 change3_pos = rand.Rand32() % (size0 - change3);
1128 }
1129
1130 cl1.push_back(Change(Change::ADD, change1, add1_pos));
1131 cl2.push_back(Change(Change::DELRANGE, change1, del2_pos));
1132 cl3.push_back(Change(Change::MODIFY, change3, change3_pos));
1133
1134 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
1135 spec1.ModifyTo(ChangeListMutator(cl2), &spec2);
1136 spec2.ModifyTo(ChangeListMutator(cl3), &spec3);
1137
1138 FourWayMergeTest(spec0, spec1, spec2, spec3);
1139 FourWayMergeTest(spec3, spec2, spec1, spec0);
1140 }
1141 }
1142 }
1143
1144 void TestMergeCommand2() {
1145 /* Same as above, different mutation pattern. */
1146 /* TODO: run this with large sizes too */
1147 /* TODO: run this with small sizes too */
1148 MTRandom rand;
1149 FileSpec spec0(&rand);
1150 FileSpec spec1(&rand);
1151 FileSpec spec2(&rand);
1152 FileSpec spec3(&rand);
1153
1154 SizeIterator<size_t, Sizes> si0(&rand, 10);
1155 for (; !si0.Done(); si0.Next()) {
1156 size_t size0 = si0.Get();
1157
1158 SizeIterator<size_t, Sizes> si1(&rand, 10);
1159 for (; !si1.Done(); si1.Next()) {
1160 size_t size1 = si1.Get();
1161
1162 SizeIterator<size_t, Sizes> si2(&rand, 10);
1163 for (; !si2.Done(); si2.Next()) {
1164 size_t size2 = si2.Get();
1165
1166 SizeIterator<size_t, Sizes> si3(&rand, 10);
1167 for (; !si3.Done(); si3.Next()) {
1168 size_t size3 = si3.Get();
1169
1170 // We're only interested in three sizes, strictly decreasing. */
1171 if (size3 >= size2 || size2 >= size1 || size1 >= size0) {
1172 continue;
1173 }
1174
1175 // XPR(NTR "S0 = %lu\n", size0);
1176 // XPR(NTR "S1 = %lu\n", size1);
1177 // XPR(NTR "S2 = %lu\n", size2);
1178 // XPR(NTR "S3 = %lu\n", size3);
1179 // XPR(NTR ".");
1180
1181 spec0.GenerateFixedSize(size0);
1182
1183 ChangeList cl1, cl2, cl3;
1184
1185 cl1.push_back(Change(Change::DELRANGE, size0 - size1, 0));
1186 cl2.push_back(Change(Change::DELRANGE, size0 - size2, 0));
1187 cl3.push_back(Change(Change::DELRANGE, size0 - size3, 0));
1188
1189 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
1190 spec0.ModifyTo(ChangeListMutator(cl2), &spec2);
1191 spec0.ModifyTo(ChangeListMutator(cl3), &spec3);
1192
1193 FourWayMergeTest(spec0, spec1, spec2, spec3);
1194 FourWayMergeTest(spec3, spec2, spec1, spec0);
1195 }
1196 }
1197 }
1198 }
1199 }
1200
1201 void TestLastFrontierBlock() {
1202 // This test constructs an input that can expose
1203 // https://github.com/jmacd/xdelta/issues/188
1204 // when run through the command-line with source via a FIFO.
1205 // That is not tested here, but the test stays.
1206 if (Constants::WINDOW_SIZE < XD3_ALLOCSIZE)
1207 {
1208 return;
1209 }
1210
1211 MTRandom rand;
1212 FileSpec spec0(&rand);
1213 FileSpec spec1(&rand);
1214 const xoff_t size = XD3_ALLOCSIZE * 64; // == XD3_MINSRCWINSZ * 2
1215 const xoff_t edit = XD3_ALLOCSIZE;
1216
1217 Options options;
1218 options.encode_srcwin_maxsz = XD3_MINSRCWINSZ;
1219 options.block_size = XD3_ALLOCSIZE;
1220 options.window_size = XD3_MINSRCWINSZ;
1221 options.size_known = false;
1222
1223 spec0.GenerateFixedSize(size);
1224
1225 ChangeList cl;
1226
1227 // Modify the 0th byte in order to induce indexing of subsequent
1228 // bytes, but allow copying most of the file to keep the test fast.
1229 cl.push_back(Change(Change::MODIFY, 1, edit * 31));
1230 cl.push_back(Change(Change::COPYOVER, edit, edit * 31, edit * 63));
1231
1232 spec0.ModifyTo(ChangeListMutator(cl), &spec1);
1233
1234 Block noblock;
1235 InMemoryEncodeDecode(spec0, spec1, &noblock, options);
1236 InMemoryEncodeDecode(spec1, spec0, &noblock, options);
1237 }
1238
1239 }; // class Regtest<Constants>
1240
1241 #define TEST(x) XPR(NTR #x "...\n"); regtest.x()
1242
1243 // These tests are primarily tests of the testing framework itself.
1244 template <class T>
1245 void UnitTest() {
1246 Regtest<T> regtest;
1247 TEST(TestPrintf);
1248 TEST(TestRandomNumbers);
1249 TEST(TestRandomFile);
1250 TEST(TestFirstByte);
1251 TEST(TestModifyMutator);
1252 TEST(TestAddMutator);
1253 TEST(TestDeleteMutator);
1254 TEST(TestCopyMutator);
1255 TEST(TestMoveMutator);
1256 TEST(TestOverwriteMutator);
1257 }
1258
1259 // These are Xdelta tests.
1260 template <class T>
1261 void MainTest() {
1262 XPR(NT "Blocksize %"Q"u windowsize %"Z"u\n",
1263 T::BLOCK_SIZE, T::WINDOW_SIZE);
1264 Regtest<T> regtest;
1265 TEST(TestEmptyInMemory);
1266 TEST(TestBlockInMemory);
1267 TEST(TestSmallStride);
1268 TEST(TestCopyWindow);
1269 TEST(TestCopyFromEnd);
1270 TEST(TestNonBlocking);
1271 TEST(TestHalfBlockCopy);
1272 TEST(TestLastFrontierBlock);
1273 TEST(TestMergeCommand1);
1274 TEST(TestMergeCommand2);
1275 }
1276
1277 #undef TEST
1278
1279 int main(int argc, char **argv)
1280 {
1281 vector<const char*> mcmd;
1282 string pn;
1283 const char *sp = strrchr(argv[0], '/');
1284 if (sp != NULL) {
1285 pn.append(argv[0], sp - argv[0] + 1);
1286 }
1287 pn.append("xdelta3");
1288 mcmd.push_back(pn.c_str());
1289 mcmd.push_back("test");
1290 mcmd.push_back(NULL);
1291
1292 UnitTest<SmallBlock>();
1293 MainTest<SmallBlock>();
1294 MainTest<MixedBlock>();
1295 MainTest<OversizeBlock>();
1296 MainTest<LargeBlock>();
1297
1298 CHECK_EQ(0, xd3_main_cmdline(mcmd.size() - 1,
1299 const_cast<char**>(&mcmd[0])));
1300
1301 return 0;
1302 }
1303