"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