1710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo// Copyright 2017 The Chromium OS Authors. All rights reserved.
2710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo// Use of this source code is governed by a BSD-style license that can be
3710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo// found in the LICENSE file.
4710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
5710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo#include "bsdiff/endsley_patch_writer.h"
6710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
7710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo#include <algorithm>
8710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
9710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo#include <gtest/gtest.h>
10710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
11710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymonamespace {
12710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
13710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymostd::vector<uint8_t> VectorFromString(const std::string& s) {
14710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  return std::vector<uint8_t>(s.data(), s.data() + s.size());
15710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo}
16710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
17710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo}  // namespace
18710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
19710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymonamespace bsdiff {
20710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
21710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymoclass EndsleyPatchWriterTest : public testing::Test {
22710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo protected:
23710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // Return a subvector from |data_| starting at |start| of size at most |size|.
24710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  std::vector<uint8_t> DataSubvector(size_t start, size_t size) {
25710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo    if (start > data_.size())
26710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo      return std::vector<uint8_t>();
27710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
28710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo    size = std::min(size, data_.size() - start);
29710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo    return std::vector<uint8_t>(data_.begin() + start,
30710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo                                data_.begin() + start + size);
31710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  }
32710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
33710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  std::vector<uint8_t> data_;
3419fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  EndsleyPatchWriter patch_writer_{&data_, CompressorType::kNoCompression, 0};
35710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo};
36710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
37710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo// Smoke check that a patch includes the new_size and magic header.
38710b3daa482eb100bdaae7ffb46724273bd404d5Alex DeymoTEST_F(EndsleyPatchWriterTest, CreateEmptyPatchTest) {
39710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.Init(0));
40710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.Close());
41710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
42710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // The empty header is set to 24 bytes.
43710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ(24U, data_.size());
44710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
45710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  std::vector<uint8_t> empty_patch = {
46710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo      // Magic header.
47710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo      'E', 'N', 'D', 'S', 'L', 'E', 'Y', '/', 'B', 'S', 'D', 'I', 'F', 'F', '4',
48710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo      '3',
49710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo      // 8 zeros for the |new_size| of zero bytes.
50710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo      0, 0, 0, 0, 0, 0, 0, 0};
51710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ(empty_patch, data_);
52710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo}
53710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
5419fc575d869148d6b33407b115f40bee9b22e244Alex DeymoTEST_F(EndsleyPatchWriterTest, CreateCompressedPatchTest) {
5519fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  EndsleyPatchWriter compressed_writer(&data_, CompressorType::kBZ2, 9);
5619fc575d869148d6b33407b115f40bee9b22e244Alex Deymo
5719fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  auto text = VectorFromString("HelloWorld");
5819fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  EXPECT_TRUE(compressed_writer.Init(text.size()));
5919fc575d869148d6b33407b115f40bee9b22e244Alex Deymo
6019fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  EXPECT_TRUE(compressed_writer.AddControlEntry(ControlEntry(5, 5, -2)));
6119fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  EXPECT_TRUE(compressed_writer.WriteDiffStream(text.data(), 5));
6219fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  EXPECT_TRUE(compressed_writer.WriteExtraStream(text.data() + 5, 5));
6319fc575d869148d6b33407b115f40bee9b22e244Alex Deymo
6419fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  // Check that the output patch had no data written to it before Close() is
6519fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  // called, since we are still compressing it.
6619fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  EXPECT_TRUE(data_.empty());
6719fc575d869148d6b33407b115f40bee9b22e244Alex Deymo
6819fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  EXPECT_TRUE(compressed_writer.Close());
6919fc575d869148d6b33407b115f40bee9b22e244Alex Deymo
7019fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  // Check that the whole file is compressed with BZ2 by looking at the header.
7119fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  const auto bz2_header = VectorFromString("BZh9");
7219fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  data_.resize(4);
7319fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  EXPECT_EQ(bz2_header, data_);
7419fc575d869148d6b33407b115f40bee9b22e244Alex Deymo}
7519fc575d869148d6b33407b115f40bee9b22e244Alex Deymo
7619fc575d869148d6b33407b115f40bee9b22e244Alex DeymoTEST_F(EndsleyPatchWriterTest, CreateEmptyBrotliPatchTest) {
7719fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  EndsleyPatchWriter compressed_writer(&data_, CompressorType::kBrotli, 9);
7819fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  EXPECT_TRUE(compressed_writer.Init(0));
7919fc575d869148d6b33407b115f40bee9b22e244Alex Deymo  EXPECT_TRUE(compressed_writer.Close());
8019fc575d869148d6b33407b115f40bee9b22e244Alex Deymo}
8119fc575d869148d6b33407b115f40bee9b22e244Alex Deymo
82710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo// Test we generate the right patch when the control, diff and extra stream come
83710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo// in the right order.
84710b3daa482eb100bdaae7ffb46724273bd404d5Alex DeymoTEST_F(EndsleyPatchWriterTest, DataInNiceOrderTest) {
85710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  auto text = VectorFromString("abcdeFGHIJ");
86710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.Init(10));
87710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
88710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(2, 3, -2)));
89710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.WriteDiffStream(text.data(), 2));
90710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.WriteExtraStream(text.data() + 2, 3));
91710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
92710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // Check that we are actually writing to the output vector as soon as we can.
93710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ(24U + 24U + 2U + 3U, data_.size());
94710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
95710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(0, 5, 1024)));
96710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.WriteExtraStream(text.data() + 5, 5));
97710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
98710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.Close());
99710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
100710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // We have a header, 2 control entries and a total of 10 bytes of data.
101710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ(24U + 24U * 2 + 10U, data_.size());
102710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
103710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // Verify that control entry values are encoded properly in little-endian.
104710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ((std::vector<uint8_t>{10, 0, 0, 0, 0, 0, 0, 0}),
105710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo            DataSubvector(16U, 8));  // new_size
106710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
107710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // Negative numbers are encoded with the sign bit in the most significant bit
108710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // of the 8-byte number.
109710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ((std::vector<uint8_t>{2, 0, 0, 0, 0, 0, 0, 0x80}),
110710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo            DataSubvector(24U + 16, 8));
111710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
112710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // The second member on the last control entry (1024) encoded in
113710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // little-endian.
114710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ((std::vector<uint8_t>{0, 4, 0, 0, 0, 0, 0, 0}),
115710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo            DataSubvector(24U + 24U + 5U + 16U, 8));
116710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
117710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // Check that the diff and extra data are sent one after the other in the
118710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // right order.
119710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ(VectorFromString("abcde"), DataSubvector(24U + 24U, 5));
120710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo}
121710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
122710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo// When we send first the diff or extra data it shouldn't be possible to
123710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo// write it to the patch, but at the end of the patch we should be able to
124710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo// write it all.
125710b3daa482eb100bdaae7ffb46724273bd404d5Alex DeymoTEST_F(EndsleyPatchWriterTest, DataInBadOrderTest) {
126710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  auto text = VectorFromString("abcdeFGHIJ");
127710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.Init(10));
128710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.WriteDiffStream(text.data(), 5));
129710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.WriteExtraStream(text.data() + 5, 5));
130710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
131710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // Writ all the control entries at the end, only the header should have been
132710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // sent so far.
133710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ(24U, data_.size());
134710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
135710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(2, 3, -2)));
136710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(2, 1, 1024)));
137710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(1, 1, 1024)));
138710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
139710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.Close());
140710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
141710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // We have a header, 3 control entries and a total of 10 bytes of data.
142710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ(24U + 24U * 3 + 10U, data_.size());
143710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
144710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // The data from the first and second control entries:
145710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ(VectorFromString("abFGH"), DataSubvector(24U + 24U, 5));
146710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ(VectorFromString("cdI"), DataSubvector(24U + 24U * 2 + 5, 3));
147710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ(VectorFromString("eJ"), DataSubvector(24U + 24U * 3 + 8, 2));
148710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo}
149710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
150710b3daa482eb100bdaae7ffb46724273bd404d5Alex DeymoTEST_F(EndsleyPatchWriterTest, FlushOnlyWhenWorthItTest) {
151710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  size_t kEntrySize = 1000;  // must be even for this test.
152710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  size_t kNumEntries = 3000;
153710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  size_t kNewSize = kEntrySize * kNumEntries;  // 3 MB
154710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
155710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.Init(kNewSize));
156710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // Write all the extra and diff data first.
157710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  std::vector<uint8_t> zeros(kNewSize / 2, 0);
158710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.WriteDiffStream(zeros.data(), zeros.size()));
159710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.WriteExtraStream(zeros.data(), zeros.size()));
160710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
161710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // No patch data flushed so far, only the header.
162710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ(24U, data_.size());
163710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
164710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  ControlEntry entry(kEntrySize / 2, kEntrySize / 2, -1);
165710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  for (size_t i = 0; i < 10; i++) {
166710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo    EXPECT_TRUE(patch_writer_.AddControlEntry(entry));
167710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  }
168710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
169710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // Even if all the diff and extra data is available and some control entries
170710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // are also available no information should have been flushed yet because we
171710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // don't want the overhead of updating the diff_data_ and extra_data_ vectors.
172710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_EQ(24U, data_.size());
173710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
174710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // Write the remaining entries.
175710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  for (size_t i = 0; i < kNumEntries - 10; i++) {
176710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo    EXPECT_TRUE(patch_writer_.AddControlEntry(entry));
177710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  }
178710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
179710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // Even before Close() is called, we have enough control entries to make it
180710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  // worth it calling flush at some point.
181710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_LT(24U, data_.size());
182710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
183710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo  EXPECT_TRUE(patch_writer_.Close());
184710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo}
185710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo
186710b3daa482eb100bdaae7ffb46724273bd404d5Alex Deymo}  // namespace bsdiff
187