You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
6.7 KiB
187 lines
6.7 KiB
// Copyright 2017 The Chromium OS Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "bsdiff/endsley_patch_writer.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
namespace {
|
|
|
|
std::vector<uint8_t> VectorFromString(const std::string& s) {
|
|
return std::vector<uint8_t>(s.data(), s.data() + s.size());
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace bsdiff {
|
|
|
|
class EndsleyPatchWriterTest : public testing::Test {
|
|
protected:
|
|
// Return a subvector from |data_| starting at |start| of size at most |size|.
|
|
std::vector<uint8_t> DataSubvector(size_t start, size_t size) {
|
|
if (start > data_.size())
|
|
return std::vector<uint8_t>();
|
|
|
|
size = std::min(size, data_.size() - start);
|
|
return std::vector<uint8_t>(data_.begin() + start,
|
|
data_.begin() + start + size);
|
|
}
|
|
|
|
std::vector<uint8_t> data_;
|
|
EndsleyPatchWriter patch_writer_{&data_, CompressorType::kNoCompression, 0};
|
|
};
|
|
|
|
// Smoke check that a patch includes the new_size and magic header.
|
|
TEST_F(EndsleyPatchWriterTest, CreateEmptyPatchTest) {
|
|
EXPECT_TRUE(patch_writer_.Init(0));
|
|
EXPECT_TRUE(patch_writer_.Close());
|
|
|
|
// The empty header is set to 24 bytes.
|
|
EXPECT_EQ(24U, data_.size());
|
|
|
|
std::vector<uint8_t> empty_patch = {
|
|
// Magic header.
|
|
'E', 'N', 'D', 'S', 'L', 'E', 'Y', '/', 'B', 'S', 'D', 'I', 'F', 'F', '4',
|
|
'3',
|
|
// 8 zeros for the |new_size| of zero bytes.
|
|
0, 0, 0, 0, 0, 0, 0, 0};
|
|
EXPECT_EQ(empty_patch, data_);
|
|
}
|
|
|
|
TEST_F(EndsleyPatchWriterTest, CreateCompressedPatchTest) {
|
|
EndsleyPatchWriter compressed_writer(&data_, CompressorType::kBZ2, 9);
|
|
|
|
auto text = VectorFromString("HelloWorld");
|
|
EXPECT_TRUE(compressed_writer.Init(text.size()));
|
|
|
|
EXPECT_TRUE(compressed_writer.AddControlEntry(ControlEntry(5, 5, -2)));
|
|
EXPECT_TRUE(compressed_writer.WriteDiffStream(text.data(), 5));
|
|
EXPECT_TRUE(compressed_writer.WriteExtraStream(text.data() + 5, 5));
|
|
|
|
// Check that the output patch had no data written to it before Close() is
|
|
// called, since we are still compressing it.
|
|
EXPECT_TRUE(data_.empty());
|
|
|
|
EXPECT_TRUE(compressed_writer.Close());
|
|
|
|
// Check that the whole file is compressed with BZ2 by looking at the header.
|
|
const auto bz2_header = VectorFromString("BZh9");
|
|
data_.resize(4);
|
|
EXPECT_EQ(bz2_header, data_);
|
|
}
|
|
|
|
TEST_F(EndsleyPatchWriterTest, CreateEmptyBrotliPatchTest) {
|
|
EndsleyPatchWriter compressed_writer(&data_, CompressorType::kBrotli, 9);
|
|
EXPECT_TRUE(compressed_writer.Init(0));
|
|
EXPECT_TRUE(compressed_writer.Close());
|
|
}
|
|
|
|
// Test we generate the right patch when the control, diff and extra stream come
|
|
// in the right order.
|
|
TEST_F(EndsleyPatchWriterTest, DataInNiceOrderTest) {
|
|
auto text = VectorFromString("abcdeFGHIJ");
|
|
EXPECT_TRUE(patch_writer_.Init(10));
|
|
|
|
EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(2, 3, -2)));
|
|
EXPECT_TRUE(patch_writer_.WriteDiffStream(text.data(), 2));
|
|
EXPECT_TRUE(patch_writer_.WriteExtraStream(text.data() + 2, 3));
|
|
|
|
// Check that we are actually writing to the output vector as soon as we can.
|
|
EXPECT_EQ(24U + 24U + 2U + 3U, data_.size());
|
|
|
|
EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(0, 5, 1024)));
|
|
EXPECT_TRUE(patch_writer_.WriteExtraStream(text.data() + 5, 5));
|
|
|
|
EXPECT_TRUE(patch_writer_.Close());
|
|
|
|
// We have a header, 2 control entries and a total of 10 bytes of data.
|
|
EXPECT_EQ(24U + 24U * 2 + 10U, data_.size());
|
|
|
|
// Verify that control entry values are encoded properly in little-endian.
|
|
EXPECT_EQ((std::vector<uint8_t>{10, 0, 0, 0, 0, 0, 0, 0}),
|
|
DataSubvector(16U, 8)); // new_size
|
|
|
|
// Negative numbers are encoded with the sign bit in the most significant bit
|
|
// of the 8-byte number.
|
|
EXPECT_EQ((std::vector<uint8_t>{2, 0, 0, 0, 0, 0, 0, 0x80}),
|
|
DataSubvector(24U + 16, 8));
|
|
|
|
// The second member on the last control entry (1024) encoded in
|
|
// little-endian.
|
|
EXPECT_EQ((std::vector<uint8_t>{0, 4, 0, 0, 0, 0, 0, 0}),
|
|
DataSubvector(24U + 24U + 5U + 16U, 8));
|
|
|
|
// Check that the diff and extra data are sent one after the other in the
|
|
// right order.
|
|
EXPECT_EQ(VectorFromString("abcde"), DataSubvector(24U + 24U, 5));
|
|
}
|
|
|
|
// When we send first the diff or extra data it shouldn't be possible to
|
|
// write it to the patch, but at the end of the patch we should be able to
|
|
// write it all.
|
|
TEST_F(EndsleyPatchWriterTest, DataInBadOrderTest) {
|
|
auto text = VectorFromString("abcdeFGHIJ");
|
|
EXPECT_TRUE(patch_writer_.Init(10));
|
|
EXPECT_TRUE(patch_writer_.WriteDiffStream(text.data(), 5));
|
|
EXPECT_TRUE(patch_writer_.WriteExtraStream(text.data() + 5, 5));
|
|
|
|
// Writ all the control entries at the end, only the header should have been
|
|
// sent so far.
|
|
EXPECT_EQ(24U, data_.size());
|
|
|
|
EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(2, 3, -2)));
|
|
EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(2, 1, 1024)));
|
|
EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(1, 1, 1024)));
|
|
|
|
EXPECT_TRUE(patch_writer_.Close());
|
|
|
|
// We have a header, 3 control entries and a total of 10 bytes of data.
|
|
EXPECT_EQ(24U + 24U * 3 + 10U, data_.size());
|
|
|
|
// The data from the first and second control entries:
|
|
EXPECT_EQ(VectorFromString("abFGH"), DataSubvector(24U + 24U, 5));
|
|
EXPECT_EQ(VectorFromString("cdI"), DataSubvector(24U + 24U * 2 + 5, 3));
|
|
EXPECT_EQ(VectorFromString("eJ"), DataSubvector(24U + 24U * 3 + 8, 2));
|
|
}
|
|
|
|
TEST_F(EndsleyPatchWriterTest, FlushOnlyWhenWorthItTest) {
|
|
size_t kEntrySize = 1000; // must be even for this test.
|
|
size_t kNumEntries = 3000;
|
|
size_t kNewSize = kEntrySize * kNumEntries; // 3 MB
|
|
|
|
EXPECT_TRUE(patch_writer_.Init(kNewSize));
|
|
// Write all the extra and diff data first.
|
|
std::vector<uint8_t> zeros(kNewSize / 2, 0);
|
|
EXPECT_TRUE(patch_writer_.WriteDiffStream(zeros.data(), zeros.size()));
|
|
EXPECT_TRUE(patch_writer_.WriteExtraStream(zeros.data(), zeros.size()));
|
|
|
|
// No patch data flushed so far, only the header.
|
|
EXPECT_EQ(24U, data_.size());
|
|
|
|
ControlEntry entry(kEntrySize / 2, kEntrySize / 2, -1);
|
|
for (size_t i = 0; i < 10; i++) {
|
|
EXPECT_TRUE(patch_writer_.AddControlEntry(entry));
|
|
}
|
|
|
|
// Even if all the diff and extra data is available and some control entries
|
|
// are also available no information should have been flushed yet because we
|
|
// don't want the overhead of updating the diff_data_ and extra_data_ vectors.
|
|
EXPECT_EQ(24U, data_.size());
|
|
|
|
// Write the remaining entries.
|
|
for (size_t i = 0; i < kNumEntries - 10; i++) {
|
|
EXPECT_TRUE(patch_writer_.AddControlEntry(entry));
|
|
}
|
|
|
|
// Even before Close() is called, we have enough control entries to make it
|
|
// worth it calling flush at some point.
|
|
EXPECT_LT(24U, data_.size());
|
|
|
|
EXPECT_TRUE(patch_writer_.Close());
|
|
}
|
|
|
|
} // namespace bsdiff
|