Alex Deymo | 710b3da | 2017-10-26 13:13:28 +0200 | [diff] [blame] | 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_; |
Alex Deymo | 19fc575 | 2018-02-15 16:56:39 +0100 | [diff] [blame] | 34 | EndsleyPatchWriter patch_writer_{&data_, CompressorType::kNoCompression, 0}; |
Alex Deymo | 710b3da | 2017-10-26 13:13:28 +0200 | [diff] [blame] | 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 | |
Alex Deymo | 19fc575 | 2018-02-15 16:56:39 +0100 | [diff] [blame] | 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 | |
Alex Deymo | 710b3da | 2017-10-26 13:13:28 +0200 | [diff] [blame] | 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 |