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