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
VectorFromString(const std::string & s)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|.
DataSubvector(size_t start,size_t 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.
TEST_F(EndsleyPatchWriterTest,CreateEmptyPatchTest)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
TEST_F(EndsleyPatchWriterTest,CreateCompressedPatchTest)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
TEST_F(EndsleyPatchWriterTest,CreateEmptyBrotliPatchTest)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.
TEST_F(EndsleyPatchWriterTest,DataInNiceOrderTest)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.
TEST_F(EndsleyPatchWriterTest,DataInBadOrderTest)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
TEST_F(EndsleyPatchWriterTest,FlushOnlyWhenWorthItTest)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