Home | History | Annotate | Download | only in bsdiff
      1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "bsdiff/endsley_patch_writer.h"
      6 
      7 #include <algorithm>
      8 
      9 #include <gtest/gtest.h>
     10 
     11 namespace {
     12 
     13 std::vector<uint8_t> VectorFromString(const std::string& s) {
     14   return std::vector<uint8_t>(s.data(), s.data() + s.size());
     15 }
     16 
     17 }  // namespace
     18 
     19 namespace bsdiff {
     20 
     21 class EndsleyPatchWriterTest : public testing::Test {
     22  protected:
     23   // Return a subvector from |data_| starting at |start| of size at most |size|.
     24   std::vector<uint8_t> DataSubvector(size_t start, size_t size) {
     25     if (start > data_.size())
     26       return std::vector<uint8_t>();
     27 
     28     size = std::min(size, data_.size() - start);
     29     return std::vector<uint8_t>(data_.begin() + start,
     30                                 data_.begin() + start + size);
     31   }
     32 
     33   std::vector<uint8_t> data_;
     34   EndsleyPatchWriter patch_writer_{&data_, CompressorType::kNoCompression, 0};
     35 };
     36 
     37 // Smoke check that a patch includes the new_size and magic header.
     38 TEST_F(EndsleyPatchWriterTest, CreateEmptyPatchTest) {
     39   EXPECT_TRUE(patch_writer_.Init(0));
     40   EXPECT_TRUE(patch_writer_.Close());
     41 
     42   // The empty header is set to 24 bytes.
     43   EXPECT_EQ(24U, data_.size());
     44 
     45   std::vector<uint8_t> empty_patch = {
     46       // Magic header.
     47       'E', 'N', 'D', 'S', 'L', 'E', 'Y', '/', 'B', 'S', 'D', 'I', 'F', 'F', '4',
     48       '3',
     49       // 8 zeros for the |new_size| of zero bytes.
     50       0, 0, 0, 0, 0, 0, 0, 0};
     51   EXPECT_EQ(empty_patch, data_);
     52 }
     53 
     54 TEST_F(EndsleyPatchWriterTest, CreateCompressedPatchTest) {
     55   EndsleyPatchWriter compressed_writer(&data_, CompressorType::kBZ2, 9);
     56 
     57   auto text = VectorFromString("HelloWorld");
     58   EXPECT_TRUE(compressed_writer.Init(text.size()));
     59 
     60   EXPECT_TRUE(compressed_writer.AddControlEntry(ControlEntry(5, 5, -2)));
     61   EXPECT_TRUE(compressed_writer.WriteDiffStream(text.data(), 5));
     62   EXPECT_TRUE(compressed_writer.WriteExtraStream(text.data() + 5, 5));
     63 
     64   // Check that the output patch had no data written to it before Close() is
     65   // called, since we are still compressing it.
     66   EXPECT_TRUE(data_.empty());
     67 
     68   EXPECT_TRUE(compressed_writer.Close());
     69 
     70   // Check that the whole file is compressed with BZ2 by looking at the header.
     71   const auto bz2_header = VectorFromString("BZh9");
     72   data_.resize(4);
     73   EXPECT_EQ(bz2_header, data_);
     74 }
     75 
     76 TEST_F(EndsleyPatchWriterTest, CreateEmptyBrotliPatchTest) {
     77   EndsleyPatchWriter compressed_writer(&data_, CompressorType::kBrotli, 9);
     78   EXPECT_TRUE(compressed_writer.Init(0));
     79   EXPECT_TRUE(compressed_writer.Close());
     80 }
     81 
     82 // Test we generate the right patch when the control, diff and extra stream come
     83 // in the right order.
     84 TEST_F(EndsleyPatchWriterTest, DataInNiceOrderTest) {
     85   auto text = VectorFromString("abcdeFGHIJ");
     86   EXPECT_TRUE(patch_writer_.Init(10));
     87 
     88   EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(2, 3, -2)));
     89   EXPECT_TRUE(patch_writer_.WriteDiffStream(text.data(), 2));
     90   EXPECT_TRUE(patch_writer_.WriteExtraStream(text.data() + 2, 3));
     91 
     92   // Check that we are actually writing to the output vector as soon as we can.
     93   EXPECT_EQ(24U + 24U + 2U + 3U, data_.size());
     94 
     95   EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(0, 5, 1024)));
     96   EXPECT_TRUE(patch_writer_.WriteExtraStream(text.data() + 5, 5));
     97 
     98   EXPECT_TRUE(patch_writer_.Close());
     99 
    100   // We have a header, 2 control entries and a total of 10 bytes of data.
    101   EXPECT_EQ(24U + 24U * 2 + 10U, data_.size());
    102 
    103   // Verify that control entry values are encoded properly in little-endian.
    104   EXPECT_EQ((std::vector<uint8_t>{10, 0, 0, 0, 0, 0, 0, 0}),
    105             DataSubvector(16U, 8));  // new_size
    106 
    107   // Negative numbers are encoded with the sign bit in the most significant bit
    108   // of the 8-byte number.
    109   EXPECT_EQ((std::vector<uint8_t>{2, 0, 0, 0, 0, 0, 0, 0x80}),
    110             DataSubvector(24U + 16, 8));
    111 
    112   // The second member on the last control entry (1024) encoded in
    113   // little-endian.
    114   EXPECT_EQ((std::vector<uint8_t>{0, 4, 0, 0, 0, 0, 0, 0}),
    115             DataSubvector(24U + 24U + 5U + 16U, 8));
    116 
    117   // Check that the diff and extra data are sent one after the other in the
    118   // right order.
    119   EXPECT_EQ(VectorFromString("abcde"), DataSubvector(24U + 24U, 5));
    120 }
    121 
    122 // When we send first the diff or extra data it shouldn't be possible to
    123 // write it to the patch, but at the end of the patch we should be able to
    124 // write it all.
    125 TEST_F(EndsleyPatchWriterTest, DataInBadOrderTest) {
    126   auto text = VectorFromString("abcdeFGHIJ");
    127   EXPECT_TRUE(patch_writer_.Init(10));
    128   EXPECT_TRUE(patch_writer_.WriteDiffStream(text.data(), 5));
    129   EXPECT_TRUE(patch_writer_.WriteExtraStream(text.data() + 5, 5));
    130 
    131   // Writ all the control entries at the end, only the header should have been
    132   // sent so far.
    133   EXPECT_EQ(24U, data_.size());
    134 
    135   EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(2, 3, -2)));
    136   EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(2, 1, 1024)));
    137   EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(1, 1, 1024)));
    138 
    139   EXPECT_TRUE(patch_writer_.Close());
    140 
    141   // We have a header, 3 control entries and a total of 10 bytes of data.
    142   EXPECT_EQ(24U + 24U * 3 + 10U, data_.size());
    143 
    144   // The data from the first and second control entries:
    145   EXPECT_EQ(VectorFromString("abFGH"), DataSubvector(24U + 24U, 5));
    146   EXPECT_EQ(VectorFromString("cdI"), DataSubvector(24U + 24U * 2 + 5, 3));
    147   EXPECT_EQ(VectorFromString("eJ"), DataSubvector(24U + 24U * 3 + 8, 2));
    148 }
    149 
    150 TEST_F(EndsleyPatchWriterTest, FlushOnlyWhenWorthItTest) {
    151   size_t kEntrySize = 1000;  // must be even for this test.
    152   size_t kNumEntries = 3000;
    153   size_t kNewSize = kEntrySize * kNumEntries;  // 3 MB
    154 
    155   EXPECT_TRUE(patch_writer_.Init(kNewSize));
    156   // Write all the extra and diff data first.
    157   std::vector<uint8_t> zeros(kNewSize / 2, 0);
    158   EXPECT_TRUE(patch_writer_.WriteDiffStream(zeros.data(), zeros.size()));
    159   EXPECT_TRUE(patch_writer_.WriteExtraStream(zeros.data(), zeros.size()));
    160 
    161   // No patch data flushed so far, only the header.
    162   EXPECT_EQ(24U, data_.size());
    163 
    164   ControlEntry entry(kEntrySize / 2, kEntrySize / 2, -1);
    165   for (size_t i = 0; i < 10; i++) {
    166     EXPECT_TRUE(patch_writer_.AddControlEntry(entry));
    167   }
    168 
    169   // Even if all the diff and extra data is available and some control entries
    170   // are also available no information should have been flushed yet because we
    171   // don't want the overhead of updating the diff_data_ and extra_data_ vectors.
    172   EXPECT_EQ(24U, data_.size());
    173 
    174   // Write the remaining entries.
    175   for (size_t i = 0; i < kNumEntries - 10; i++) {
    176     EXPECT_TRUE(patch_writer_.AddControlEntry(entry));
    177   }
    178 
    179   // Even before Close() is called, we have enough control entries to make it
    180   // worth it calling flush at some point.
    181   EXPECT_LT(24U, data_.size());
    182 
    183   EXPECT_TRUE(patch_writer_.Close());
    184 }
    185 
    186 }  // namespace bsdiff
    187