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