794 lines
26 KiB
C++
794 lines
26 KiB
C++
// Copyright 2017 The Chromium 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 "components/zucchini/patch_reader.h"
|
|
#include "components/zucchini/patch_writer.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "components/zucchini/version_info.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace zucchini {
|
|
|
|
namespace {
|
|
|
|
// Used for initialization of raw test data.
|
|
using ByteVector = std::vector<uint8_t>;
|
|
|
|
// Helper function that creates an object of type |T| and intializes it from
|
|
// data in |buffer|. Ensures initialization is successful. |buffer| is passed as
|
|
// pointer to avoid passing a temporay, which can causes dangling references.
|
|
template <class T>
|
|
T TestInitialize(const ByteVector* buffer) {
|
|
T value;
|
|
BufferSource buffer_source(buffer->data(), buffer->size());
|
|
EXPECT_TRUE(value.Initialize(&buffer_source));
|
|
EXPECT_TRUE(buffer_source.empty()); // Make sure all data has been consumed
|
|
return value;
|
|
}
|
|
|
|
// Helper function that creates an object of type |T| and tries to intialize it
|
|
// from invalid data in |buffer|, expecting the operation to fail. |buffer| is
|
|
// passed as pointer to avoid passing a temporary, which can causes dangling
|
|
// references.
|
|
template <class T>
|
|
void TestInvalidInitialize(const ByteVector* buffer) {
|
|
T value;
|
|
BufferSource buffer_source(buffer->data(), buffer->size());
|
|
EXPECT_FALSE(value.Initialize(&buffer_source));
|
|
}
|
|
|
|
// Helper function that serializes |value| into a buffer. Ensures that
|
|
// serialization is successful and that the result matches |expected|.
|
|
template <class T>
|
|
void TestSerialize(const ByteVector& expected, const T& value) {
|
|
size_t size = value.SerializedSize();
|
|
EXPECT_EQ(expected.size(), size);
|
|
ByteVector buffer(size);
|
|
BufferSink buffer_sink(buffer.data(), buffer.size());
|
|
EXPECT_TRUE(value.SerializeInto(&buffer_sink));
|
|
EXPECT_EQ(expected, buffer);
|
|
}
|
|
|
|
ByteVector CreatePatchElement() {
|
|
return {
|
|
// PatchElementHeader
|
|
0x01, 0, 0, 0, // old_offset
|
|
0x51, 0, 0, 0, // old_length
|
|
0x03, 0, 0, 0, // new_offset
|
|
0x13, 0, 0, 0, // new_length
|
|
'P', 'x', '8', '6', // exe_type = EXE_TYPE_WIN32_X86
|
|
0x01, 0x00, // element version
|
|
// EquivalenceSource
|
|
1, 0, 0, 0, // src_skip size
|
|
0x10, // src_skip content
|
|
1, 0, 0, 0, // dst_skip size
|
|
0x00, // dst_skip content
|
|
1, 0, 0, 0, // copy_count size
|
|
0x12, // copy_count content
|
|
// ExtraDataSource
|
|
1, 0, 0, 0, // extra_data size
|
|
0x13, // extra_data content
|
|
// RawDeltaSource
|
|
1, 0, 0, 0, // raw_delta_skip size
|
|
0x14, // raw_delta_skip content
|
|
1, 0, 0, 0, // raw_delta_diff size
|
|
0x15, // raw_delta_diff content
|
|
// ReferenceDeltaSource
|
|
1, 0, 0, 0, // reference_delta size
|
|
0x16, // reference_delta content
|
|
// PatchElementReader
|
|
2, 0, 0, 0, // pool count
|
|
0, // pool_tag
|
|
1, 0, 0, 0, // extra_targets size
|
|
0x17, // extra_targets content
|
|
2, // pool_tag
|
|
1, 0, 0, 0, // extra_targets size
|
|
0x18, // extra_targets content
|
|
};
|
|
}
|
|
|
|
ByteVector CreateElementMatch() {
|
|
return {
|
|
// PatchElementHeader
|
|
0x01, 0, 0, 0, // old_offset
|
|
0x02, 0, 0, 0, // old_length
|
|
0x03, 0, 0, 0, // new_offset
|
|
0x04, 0, 0, 0, // new_length
|
|
'D', 'E', 'X', ' ', // exe_type = kExeTypeDex
|
|
0x01, 0x00, // element version
|
|
};
|
|
}
|
|
|
|
// Helper to mutate test |data| (e.g., from CreatePatchElement()) at |idx| from
|
|
// |from_val| (as sanity check) to |to_val|.
|
|
void ModifyByte(size_t idx,
|
|
uint8_t from_val,
|
|
uint8_t to_val,
|
|
std::vector<uint8_t>* data) {
|
|
ASSERT_EQ(from_val, (*data)[idx]);
|
|
(*data)[idx] = to_val;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
bool operator==(const ByteVector& a, ConstBufferView b) {
|
|
return a == ByteVector(b.begin(), b.end());
|
|
}
|
|
|
|
TEST(PatchTest, ParseSerializeElementMatch) {
|
|
ByteVector data = CreateElementMatch();
|
|
BufferSource buffer_source(data.data(), data.size());
|
|
ElementMatch element_match = {};
|
|
EXPECT_TRUE(patch::ParseElementMatch(&buffer_source, &element_match));
|
|
EXPECT_EQ(kExeTypeDex, element_match.exe_type());
|
|
EXPECT_EQ(kExeTypeDex, element_match.old_element.exe_type);
|
|
EXPECT_EQ(kExeTypeDex, element_match.new_element.exe_type);
|
|
EXPECT_EQ(0x1U, element_match.old_element.offset);
|
|
EXPECT_EQ(0x2U, element_match.old_element.size);
|
|
EXPECT_EQ(0x3U, element_match.new_element.offset);
|
|
EXPECT_EQ(0x4U, element_match.new_element.size);
|
|
|
|
size_t size = patch::SerializedElementMatchSize(element_match);
|
|
EXPECT_EQ(data.size(), size);
|
|
ByteVector buffer(size);
|
|
BufferSink buffer_sink(buffer.data(), buffer.size());
|
|
EXPECT_TRUE(patch::SerializeElementMatch(element_match, &buffer_sink));
|
|
EXPECT_EQ(data, buffer);
|
|
}
|
|
|
|
TEST(PatchTest, ParseElementMatchTooSmall) {
|
|
ByteVector data = {4};
|
|
BufferSource buffer_source(data.data(), data.size());
|
|
ElementMatch element_match = {};
|
|
EXPECT_FALSE(patch::ParseElementMatch(&buffer_source, &element_match));
|
|
}
|
|
|
|
TEST(PatchTest, ParseElementMatchNoLength) {
|
|
// Set old_length to 0 to trigger an error.
|
|
{
|
|
ByteVector data = CreateElementMatch();
|
|
// old_length := 0.
|
|
ModifyByte(offsetof(PatchElementHeader, old_length), 0x02, 0x00, &data);
|
|
BufferSource buffer_source(data.data(), data.size());
|
|
ElementMatch element_match = {};
|
|
EXPECT_FALSE(patch::ParseElementMatch(&buffer_source, &element_match));
|
|
}
|
|
// Set new_length to 0 to trigger an error.
|
|
{
|
|
ByteVector data = CreateElementMatch();
|
|
// new_length := 0.
|
|
ModifyByte(offsetof(PatchElementHeader, new_length), 0x04, 0x00, &data);
|
|
BufferSource buffer_source(data.data(), data.size());
|
|
ElementMatch element_match = {};
|
|
EXPECT_FALSE(patch::ParseElementMatch(&buffer_source, &element_match));
|
|
}
|
|
// Set both new_length and old_length to 0 to trigger an error.
|
|
{
|
|
ByteVector data = CreateElementMatch();
|
|
// old_length := 0.
|
|
ModifyByte(offsetof(PatchElementHeader, old_length), 0x02, 0x00, &data);
|
|
// new_length := 0.
|
|
ModifyByte(offsetof(PatchElementHeader, new_length), 0x04, 0x00, &data);
|
|
BufferSource buffer_source(data.data(), data.size());
|
|
ElementMatch element_match = {};
|
|
EXPECT_FALSE(patch::ParseElementMatch(&buffer_source, &element_match));
|
|
}
|
|
}
|
|
|
|
TEST(PatchTest, ParseSerializeElementMatchExeMismatch) {
|
|
ByteVector buffer(28);
|
|
BufferSink buffer_sink(buffer.data(), buffer.size());
|
|
EXPECT_FALSE(patch::SerializeElementMatch(
|
|
ElementMatch{{{1, 2}, kExeTypeNoOp}, {{3, 4}, kExeTypeWin32X86}},
|
|
&buffer_sink));
|
|
}
|
|
|
|
TEST(PatchTest, SerializeElementMatchTooSmall) {
|
|
ByteVector buffer(4);
|
|
BufferSink buffer_sink(buffer.data(), buffer.size());
|
|
EXPECT_FALSE(patch::SerializeElementMatch(
|
|
ElementMatch{{{1, 2}, kExeTypeDex}, {{3, 4}, kExeTypeDex}},
|
|
&buffer_sink));
|
|
}
|
|
|
|
TEST(PatchTest, ParseSerializeBuffer) {
|
|
auto TestSerialize = [](const ByteVector& expected, const ByteVector& value) {
|
|
size_t size = patch::SerializedBufferSize(value);
|
|
EXPECT_EQ(expected.size(), size);
|
|
ByteVector buffer(size);
|
|
BufferSink buffer_sink(buffer.data(), buffer.size());
|
|
EXPECT_TRUE(patch::SerializeBuffer(value, &buffer_sink));
|
|
EXPECT_EQ(expected, buffer);
|
|
};
|
|
|
|
// |data| is passed as pointer to avoid passing a temporay, which can causes
|
|
// dangling references.
|
|
auto TestParse = [](const ByteVector* data) {
|
|
BufferSource value;
|
|
BufferSource buffer_source(data->data(), data->size());
|
|
EXPECT_TRUE(patch::ParseBuffer(&buffer_source, &value));
|
|
// Make sure all data has been consumed.
|
|
EXPECT_TRUE(buffer_source.empty());
|
|
return value;
|
|
};
|
|
|
|
ByteVector data = {
|
|
0, 0, 0, 0, // size
|
|
};
|
|
BufferSource buffer = TestParse(&data);
|
|
EXPECT_TRUE(buffer.empty());
|
|
TestSerialize(data, ByteVector({}));
|
|
|
|
data = {
|
|
3, 0, 0, 0, // size
|
|
1, 2, 3 // content
|
|
};
|
|
buffer = TestParse(&data);
|
|
EXPECT_EQ(3U, buffer.size());
|
|
EXPECT_EQ(ByteVector({1, 2, 3}), ByteVector(buffer.begin(), buffer.end()));
|
|
TestSerialize(data, ByteVector({1, 2, 3}));
|
|
|
|
// Ill-formed input.
|
|
data = {
|
|
3, 0, 0, 0, // size
|
|
1, 2 // insufficient content
|
|
};
|
|
BufferSource value;
|
|
BufferSource buffer_source(data.data(), data.size());
|
|
EXPECT_FALSE(patch::ParseBuffer(&buffer_source, &value));
|
|
EXPECT_TRUE(value.empty());
|
|
}
|
|
|
|
TEST(PatchTest, SerializeBufferTooSmall) {
|
|
ByteVector buffer(3);
|
|
BufferSink buffer_sink(buffer.data(), buffer.size());
|
|
EXPECT_FALSE(patch::SerializeBuffer(ByteVector(), &buffer_sink));
|
|
}
|
|
|
|
TEST(EquivalenceSinkSourceTest, Empty) {
|
|
ByteVector data = {
|
|
// EquivalenceSource
|
|
0, 0, 0, 0, // src_skip size
|
|
0, 0, 0, 0, // dst_skip size
|
|
0, 0, 0, 0, // copy_count size
|
|
};
|
|
EquivalenceSource equivalence_source =
|
|
TestInitialize<EquivalenceSource>(&data);
|
|
|
|
EXPECT_FALSE(equivalence_source.GetNext());
|
|
EXPECT_TRUE(equivalence_source.Done());
|
|
|
|
TestSerialize(data, EquivalenceSink());
|
|
}
|
|
|
|
TEST(EquivalenceSourceSinkTest, Normal) {
|
|
ByteVector data = {
|
|
// EquivalenceSource
|
|
2, 0, 0, 0, // src_skip size
|
|
6, 7, // src_skip content
|
|
2, 0, 0, 0, // dst_skip size
|
|
7, 1, // dst_skip content
|
|
2, 0, 0, 0, // copy_count size
|
|
2, 1 // copy_count content
|
|
};
|
|
EquivalenceSource equivalence_source =
|
|
TestInitialize<EquivalenceSource>(&data);
|
|
auto equivalence = equivalence_source.GetNext();
|
|
EXPECT_FALSE(equivalence_source.Done());
|
|
EXPECT_TRUE(equivalence.has_value());
|
|
EXPECT_EQ(offset_t(3), equivalence->src_offset);
|
|
EXPECT_EQ(offset_t(7), equivalence->dst_offset);
|
|
EXPECT_EQ(offset_t(2), equivalence->length);
|
|
|
|
equivalence = equivalence_source.GetNext();
|
|
EXPECT_TRUE(equivalence_source.Done());
|
|
EXPECT_TRUE(equivalence.has_value());
|
|
EXPECT_EQ(offset_t(1), equivalence->src_offset);
|
|
EXPECT_EQ(offset_t(10), equivalence->dst_offset);
|
|
EXPECT_EQ(offset_t(1), equivalence->length);
|
|
|
|
equivalence = equivalence_source.GetNext();
|
|
EXPECT_FALSE(equivalence.has_value());
|
|
|
|
EquivalenceSink equivalence_sink;
|
|
equivalence_sink.PutNext(Equivalence{3, 7, 2});
|
|
equivalence_sink.PutNext(Equivalence{1, 10, 1});
|
|
TestSerialize(data, equivalence_sink);
|
|
}
|
|
|
|
TEST(ExtraDataSourceSinkTest, Empty) {
|
|
ByteVector data = {
|
|
// ExtraDataSource
|
|
0, 0, 0, 0, // extra_data size
|
|
};
|
|
ExtraDataSource extra_data_source = TestInitialize<ExtraDataSource>(&data);
|
|
|
|
EXPECT_FALSE(extra_data_source.GetNext(2));
|
|
EXPECT_TRUE(extra_data_source.Done());
|
|
|
|
TestSerialize(data, ExtraDataSink());
|
|
}
|
|
|
|
TEST(ExtraDataSourceSinkTest, Normal) {
|
|
ByteVector data = {
|
|
// ExtraDataSource
|
|
5, 0, 0, 0, // extra_data size
|
|
1, 2, 3, 4, 5, // extra_data content
|
|
};
|
|
ExtraDataSource extra_data_source = TestInitialize<ExtraDataSource>(&data);
|
|
EXPECT_FALSE(extra_data_source.Done());
|
|
|
|
auto extra_data = extra_data_source.GetNext(3);
|
|
EXPECT_FALSE(extra_data_source.Done());
|
|
EXPECT_TRUE(extra_data.has_value());
|
|
EXPECT_EQ(size_t(3), extra_data->size());
|
|
EXPECT_EQ(ByteVector({1, 2, 3}),
|
|
ByteVector(extra_data->begin(), extra_data->end()));
|
|
|
|
extra_data = extra_data_source.GetNext(2);
|
|
EXPECT_TRUE(extra_data_source.Done());
|
|
EXPECT_TRUE(extra_data.has_value());
|
|
EXPECT_EQ(ByteVector({4, 5}),
|
|
ByteVector(extra_data->begin(), extra_data->end()));
|
|
|
|
extra_data = extra_data_source.GetNext(2);
|
|
EXPECT_FALSE(extra_data.has_value());
|
|
|
|
ExtraDataSink extra_data_sink;
|
|
|
|
ByteVector content = {1, 2, 3};
|
|
extra_data_sink.PutNext({content.data(), content.size()});
|
|
content = {4, 5};
|
|
extra_data_sink.PutNext({content.data(), content.size()});
|
|
TestSerialize(data, extra_data_sink);
|
|
}
|
|
|
|
TEST(RawDeltaSourceSinkTest, Empty) {
|
|
ByteVector data = {
|
|
// RawDeltaSource
|
|
0, 0, 0, 0, // raw_delta_skip size
|
|
0, 0, 0, 0, // raw_delta_diff size
|
|
};
|
|
RawDeltaSource raw_delta_source = TestInitialize<RawDeltaSource>(&data);
|
|
|
|
EXPECT_FALSE(raw_delta_source.GetNext());
|
|
EXPECT_TRUE(raw_delta_source.Done());
|
|
|
|
TestSerialize(data, RawDeltaSink());
|
|
}
|
|
|
|
TEST(RawDeltaSinkSourceSinkTest, Normal) {
|
|
ByteVector data = {
|
|
// RawDeltaSource
|
|
3, 0, 0, 0, // raw_delta_skip size
|
|
1, 3, 0, // raw_delta_skip content
|
|
3, 0, 0, 0, // raw_delta_diff size
|
|
42, 24, 235, // raw_delta_diff content
|
|
};
|
|
RawDeltaSource raw_delta_source = TestInitialize<RawDeltaSource>(&data);
|
|
EXPECT_FALSE(raw_delta_source.Done());
|
|
|
|
auto raw_delta = raw_delta_source.GetNext();
|
|
EXPECT_FALSE(raw_delta_source.Done());
|
|
EXPECT_TRUE(raw_delta.has_value());
|
|
EXPECT_EQ(1U, raw_delta->copy_offset);
|
|
EXPECT_EQ(42, raw_delta->diff);
|
|
|
|
raw_delta = raw_delta_source.GetNext();
|
|
EXPECT_FALSE(raw_delta_source.Done());
|
|
EXPECT_TRUE(raw_delta.has_value());
|
|
EXPECT_EQ(5U, raw_delta->copy_offset);
|
|
EXPECT_EQ(24, raw_delta->diff);
|
|
|
|
raw_delta = raw_delta_source.GetNext();
|
|
EXPECT_TRUE(raw_delta_source.Done());
|
|
EXPECT_TRUE(raw_delta.has_value());
|
|
EXPECT_EQ(6U, raw_delta->copy_offset);
|
|
EXPECT_EQ(-21, raw_delta->diff);
|
|
|
|
EXPECT_FALSE(raw_delta_source.GetNext());
|
|
EXPECT_TRUE(raw_delta_source.Done());
|
|
|
|
RawDeltaSink raw_delta_sink;
|
|
raw_delta_sink.PutNext({1, 42});
|
|
raw_delta_sink.PutNext({5, 24});
|
|
raw_delta_sink.PutNext({6, -21});
|
|
TestSerialize(data, raw_delta_sink);
|
|
}
|
|
|
|
TEST(RawDeltaSourceSinkTest, InvalidContent) {
|
|
ByteVector data = {
|
|
// RawDeltaSource
|
|
2, 0, 0, 0, // raw_delta_skip size
|
|
1, 3, // raw_delta_skip content
|
|
2, 0, 0, 0, // raw_delta_diff size
|
|
0, 4, // raw_delta_diff content
|
|
};
|
|
RawDeltaSource raw_delta_source = TestInitialize<RawDeltaSource>(&data);
|
|
EXPECT_FALSE(raw_delta_source.GetNext());
|
|
EXPECT_FALSE(raw_delta_source.Done());
|
|
}
|
|
|
|
TEST(ReferenceDeltaSourceSinkTest, Empty) {
|
|
ByteVector data = {
|
|
// ReferenceDeltaSource
|
|
0, 0, 0, 0, // reference_delta size
|
|
};
|
|
ReferenceDeltaSource reference_delta_source =
|
|
TestInitialize<ReferenceDeltaSource>(&data);
|
|
|
|
EXPECT_FALSE(reference_delta_source.GetNext());
|
|
EXPECT_TRUE(reference_delta_source.Done());
|
|
|
|
TestSerialize(data, ReferenceDeltaSink());
|
|
}
|
|
|
|
TEST(ReferenceDeltaSourceSinkTest, Normal) {
|
|
ByteVector data = {
|
|
// ReferenceDeltaSource
|
|
2, 0, 0, 0, // reference_delta size
|
|
84, 47, // reference_delta content
|
|
};
|
|
ReferenceDeltaSource reference_delta_source =
|
|
TestInitialize<ReferenceDeltaSource>(&data);
|
|
EXPECT_FALSE(reference_delta_source.Done());
|
|
|
|
auto delta = reference_delta_source.GetNext();
|
|
EXPECT_FALSE(reference_delta_source.Done());
|
|
EXPECT_TRUE(delta.has_value());
|
|
EXPECT_EQ(42, *delta);
|
|
|
|
delta = reference_delta_source.GetNext();
|
|
EXPECT_TRUE(reference_delta_source.Done());
|
|
EXPECT_TRUE(delta.has_value());
|
|
EXPECT_EQ(-24, *delta);
|
|
|
|
EXPECT_FALSE(reference_delta_source.GetNext());
|
|
EXPECT_TRUE(reference_delta_source.Done());
|
|
|
|
ReferenceDeltaSink reference_delta;
|
|
reference_delta.PutNext(42);
|
|
reference_delta.PutNext(-24);
|
|
TestSerialize(data, reference_delta);
|
|
}
|
|
|
|
TEST(TargetSourceSinkTest, Empty) {
|
|
ByteVector data = {
|
|
// TargetSource
|
|
0, 0, 0, 0, // extra_targets size
|
|
};
|
|
TargetSource target_source = TestInitialize<TargetSource>(&data);
|
|
|
|
EXPECT_FALSE(target_source.GetNext());
|
|
EXPECT_TRUE(target_source.Done());
|
|
|
|
TestSerialize(data, TargetSink());
|
|
}
|
|
|
|
TEST(TargetSourceSinkTest, Normal) {
|
|
ByteVector data = {
|
|
// TargetSource
|
|
2, 0, 0, 0, // extra_targets size
|
|
3, 1, // extra_targets content
|
|
};
|
|
TargetSource target_source = TestInitialize<TargetSource>(&data);
|
|
EXPECT_FALSE(target_source.Done());
|
|
|
|
auto target = target_source.GetNext();
|
|
EXPECT_FALSE(target_source.Done());
|
|
EXPECT_TRUE(target.has_value());
|
|
EXPECT_EQ(3U, *target);
|
|
|
|
target = target_source.GetNext();
|
|
EXPECT_TRUE(target_source.Done());
|
|
EXPECT_TRUE(target.has_value());
|
|
EXPECT_EQ(5U, *target);
|
|
|
|
EXPECT_FALSE(target_source.GetNext());
|
|
EXPECT_TRUE(target_source.Done());
|
|
|
|
TargetSink target_sink;
|
|
target_sink.PutNext(3);
|
|
target_sink.PutNext(5);
|
|
TestSerialize(data, target_sink);
|
|
}
|
|
|
|
TEST(PatchElementTest, Normal) {
|
|
ByteVector data = CreatePatchElement();
|
|
|
|
PatchElementReader patch_element_reader =
|
|
TestInitialize<PatchElementReader>(&data);
|
|
|
|
ElementMatch element_match = patch_element_reader.element_match();
|
|
EXPECT_EQ(kExeTypeWin32X86, element_match.exe_type());
|
|
EXPECT_EQ(kExeTypeWin32X86, element_match.old_element.exe_type);
|
|
EXPECT_EQ(kExeTypeWin32X86, element_match.new_element.exe_type);
|
|
EXPECT_EQ(0x1U, element_match.old_element.offset);
|
|
EXPECT_EQ(0x51U, element_match.old_element.size);
|
|
EXPECT_EQ(0x3U, element_match.new_element.offset);
|
|
EXPECT_EQ(0x13U, element_match.new_element.size);
|
|
|
|
EquivalenceSource equivalence_source =
|
|
patch_element_reader.GetEquivalenceSource();
|
|
EXPECT_EQ(ByteVector({0x10}), equivalence_source.src_skip());
|
|
EXPECT_EQ(ByteVector({0x00}), equivalence_source.dst_skip());
|
|
EXPECT_EQ(ByteVector({0x12}), equivalence_source.copy_count());
|
|
|
|
ExtraDataSource extra_data_source = patch_element_reader.GetExtraDataSource();
|
|
EXPECT_EQ(ByteVector({0x13}), extra_data_source.extra_data());
|
|
|
|
RawDeltaSource raw_delta_source = patch_element_reader.GetRawDeltaSource();
|
|
EXPECT_EQ(ByteVector({0x14}), raw_delta_source.raw_delta_skip());
|
|
EXPECT_EQ(ByteVector({0x15}), raw_delta_source.raw_delta_diff());
|
|
|
|
ReferenceDeltaSource reference_delta_source =
|
|
patch_element_reader.GetReferenceDeltaSource();
|
|
EXPECT_EQ(ByteVector({0x16}), reference_delta_source.reference_delta());
|
|
|
|
TargetSource target_source1 =
|
|
patch_element_reader.GetExtraTargetSource(PoolTag(0));
|
|
EXPECT_EQ(ByteVector({0x17}), target_source1.extra_targets());
|
|
TargetSource target_source2 =
|
|
patch_element_reader.GetExtraTargetSource(PoolTag(1));
|
|
EXPECT_EQ(ByteVector({}), target_source2.extra_targets());
|
|
TargetSource target_source3 =
|
|
patch_element_reader.GetExtraTargetSource(PoolTag(2));
|
|
EXPECT_EQ(ByteVector({0x18}), target_source3.extra_targets());
|
|
|
|
PatchElementWriter patch_element_writer(element_match);
|
|
|
|
patch_element_writer.SetEquivalenceSink(
|
|
EquivalenceSink({0x10}, {0x00}, {0x12}));
|
|
patch_element_writer.SetExtraDataSink(ExtraDataSink({0x13}));
|
|
patch_element_writer.SetRawDeltaSink(RawDeltaSink({0x14}, {0x15}));
|
|
patch_element_writer.SetReferenceDeltaSink(ReferenceDeltaSink({0x16}));
|
|
patch_element_writer.SetTargetSink(PoolTag(0), TargetSink({0x17}));
|
|
patch_element_writer.SetTargetSink(PoolTag(2), TargetSink({0x18}));
|
|
TestSerialize(data, patch_element_writer);
|
|
}
|
|
|
|
TEST(PatchElementTest, BadEquivalence) {
|
|
// If the "old" element is too small then the test should fail.
|
|
{
|
|
ByteVector data = CreatePatchElement();
|
|
// old_length := 0x4 (too small).
|
|
ModifyByte(offsetof(PatchElementHeader, old_length), 0x51, 0x04, &data);
|
|
TestInvalidInitialize<PatchElementReader>(&data);
|
|
}
|
|
|
|
// If the "new" element is too small then the test should fail.
|
|
{
|
|
ByteVector data = CreatePatchElement();
|
|
// new_length := 0x5 (too small).
|
|
ModifyByte(offsetof(PatchElementHeader, new_length), 0x13, 0x05, &data);
|
|
TestInvalidInitialize<PatchElementReader>(&data);
|
|
}
|
|
}
|
|
|
|
TEST(PatchElementTest, WrongExtraData) {
|
|
// Make "new" too large so insufficient extra data exists to cover the image.
|
|
{
|
|
ByteVector data = CreatePatchElement();
|
|
// new_length := 0x14 (too large).
|
|
ModifyByte(offsetof(PatchElementHeader, new_length), 0x13, 0x14, &data);
|
|
TestInvalidInitialize<PatchElementReader>(&data);
|
|
}
|
|
// Make "new" too small so there is too much extra data.
|
|
{
|
|
ByteVector data = CreatePatchElement();
|
|
// new_length := 0x12 (too small).
|
|
ModifyByte(offsetof(PatchElementHeader, new_length), 0x13, 0x12, &data);
|
|
TestInvalidInitialize<PatchElementReader>(&data);
|
|
}
|
|
}
|
|
|
|
TEST(PatchElementTest, WrongVersion) {
|
|
// Bump element version to 2.
|
|
{
|
|
ByteVector data = CreatePatchElement();
|
|
ModifyByte(offsetof(PatchElementHeader, version), 0x01, 0x02, &data);
|
|
TestInvalidInitialize<PatchElementReader>(&data);
|
|
}
|
|
// Bump element version to 0.
|
|
{
|
|
ByteVector data = CreatePatchElement();
|
|
ModifyByte(offsetof(PatchElementHeader, version), 0x01, 0x00, &data);
|
|
TestInvalidInitialize<PatchElementReader>(&data);
|
|
}
|
|
}
|
|
|
|
TEST(EnsemblePatchTest, RawPatch) {
|
|
ByteVector data = {
|
|
// PatchHeader
|
|
0x5A, 0x75, 0x63, 0x63, // magic
|
|
0x01, 0x00, 0x00, 0x00, // major/minor version
|
|
0x10, 0x32, 0x54, 0x76, // old_size
|
|
0x00, 0x11, 0x22, 0x33, // old_crc
|
|
0x01, 0, 0, 0, // new_size
|
|
0x44, 0x55, 0x66, 0x77, // new_crc
|
|
|
|
1, 0, 0, 0, // number of element
|
|
|
|
// PatchElementHeader
|
|
0x01, 0, 0, 0, // old_offset
|
|
0x02, 0, 0, 0, // old_length
|
|
0x00, 0, 0, 0, // new_offset
|
|
0x01, 0, 0, 0, // new_length
|
|
'P', 'x', '8', '6', // exe_type = EXE_TYPE_WIN32_X8
|
|
0x01, 0x00, // element version
|
|
// EquivalenceSource
|
|
0, 0, 0, 0, // src_skip size
|
|
0, 0, 0, 0, // dst_skip size
|
|
0, 0, 0, 0, // copy_count size
|
|
// ExtraDataSource
|
|
0x01, 0, 0, 0, // extra_data size
|
|
0x04, // extra_data content
|
|
// RawDeltaSource
|
|
0, 0, 0, 0, // raw_delta_skip size
|
|
0, 0, 0, 0, // raw_delta_diff size
|
|
// ReferenceDeltaSource
|
|
0, 0, 0, 0, // reference_delta size
|
|
// PatchElementReader
|
|
0, 0, 0, 0, // pool count
|
|
};
|
|
|
|
EnsemblePatchReader ensemble_patch_reader =
|
|
TestInitialize<EnsemblePatchReader>(&data);
|
|
|
|
PatchHeader header = ensemble_patch_reader.header();
|
|
EXPECT_EQ(PatchHeader::kMagic, header.magic);
|
|
EXPECT_EQ(kMajorVersion, header.major_version);
|
|
EXPECT_EQ(kMinorVersion, header.minor_version);
|
|
EXPECT_EQ(0x76543210U, header.old_size);
|
|
EXPECT_EQ(0x33221100U, header.old_crc);
|
|
EXPECT_EQ(0x01U, header.new_size);
|
|
EXPECT_EQ(0x77665544U, header.new_crc);
|
|
|
|
const std::vector<PatchElementReader>& elements =
|
|
ensemble_patch_reader.elements();
|
|
EXPECT_EQ(size_t(1), elements.size());
|
|
|
|
EnsemblePatchWriter ensemble_patch_writer(header);
|
|
PatchElementWriter patch_element_writer(elements[0].element_match());
|
|
patch_element_writer.SetEquivalenceSink({});
|
|
patch_element_writer.SetExtraDataSink(ExtraDataSink({0x04}));
|
|
patch_element_writer.SetRawDeltaSink({});
|
|
patch_element_writer.SetReferenceDeltaSink({});
|
|
ensemble_patch_writer.AddElement(std::move(patch_element_writer));
|
|
|
|
TestSerialize(data, ensemble_patch_writer);
|
|
}
|
|
|
|
TEST(EnsemblePatchTest, CheckFile) {
|
|
ByteVector data = {
|
|
// PatchHeader
|
|
0x5A, 0x75, 0x63, 0x63, // magic
|
|
0x01, 0x00, 0x00, 0x00, // major/minor version
|
|
0x05, 0x00, 0x00, 0x00, // old_size
|
|
0xDF, 0x13, 0xE4, 0x10, // old_crc
|
|
0x03, 0x00, 0x00, 0x00, // new_size
|
|
0xDC, 0xF7, 0x00, 0x40, // new_crc
|
|
|
|
1, 0, 0, 0, // number of element
|
|
|
|
// PatchElementHeader
|
|
0x01, 0, 0, 0, // old_offset
|
|
0x02, 0, 0, 0, // old_length
|
|
0x00, 0, 0, 0, // new_offset
|
|
0x03, 0, 0, 0, // new_length
|
|
'P', 'x', '8', '6', // exe_type = EXE_TYPE_WIN32_X86
|
|
0x01, 0x00, // element version
|
|
// EquivalenceSource
|
|
0, 0, 0, 0, // src_skip size
|
|
0, 0, 0, 0, // dst_skip size
|
|
0, 0, 0, 0, // copy_count size
|
|
// ExtraDataSource
|
|
0x03, 0, 0, 0, // extra_data size
|
|
'A', 'B', 'C', // extra_data content
|
|
// RawDeltaSource
|
|
0, 0, 0, 0, // raw_delta_skip size
|
|
0, 0, 0, 0, // raw_delta_diff size
|
|
// ReferenceDeltaSource
|
|
0, 0, 0, 0, // reference_delta size
|
|
// PatchElementReader
|
|
0, 0, 0, 0, // pool count
|
|
};
|
|
|
|
EnsemblePatchReader ensemble_patch_reader =
|
|
TestInitialize<EnsemblePatchReader>(&data);
|
|
|
|
ByteVector old_file = {0x10, 0x32, 0x54, 0x76, 0x98};
|
|
ByteVector new_file = {0xBA, 0xDC, 0xFE};
|
|
|
|
ConstBufferView old_image(old_file.data(), old_file.size());
|
|
ConstBufferView new_image(new_file.data(), new_file.size());
|
|
|
|
EXPECT_TRUE(ensemble_patch_reader.CheckOldFile(old_image));
|
|
EXPECT_TRUE(ensemble_patch_reader.CheckNewFile(new_image));
|
|
EXPECT_FALSE(ensemble_patch_reader.CheckOldFile(new_image));
|
|
EXPECT_FALSE(ensemble_patch_reader.CheckNewFile(old_image));
|
|
}
|
|
|
|
TEST(EnsemblePatchTest, InvalidMagic) {
|
|
ByteVector data = {
|
|
// PatchHeader
|
|
0x42, 0x42, 0x42, 0x42, // magic
|
|
0x01, 0x00, 0x00, 0x00, // major/minor version
|
|
0x10, 0x32, 0x54, 0x76, // old_size
|
|
0x00, 0x11, 0x22, 0x33, // old_crc
|
|
0x03, 0x00, 0x00, 0x00, // new_size
|
|
0x44, 0x55, 0x66, 0x77, // new_crc
|
|
|
|
1, 0, 0, 0, // number of element
|
|
|
|
// PatchElementHeader
|
|
0x01, 0, 0, 0, // old_offset
|
|
0x02, 0, 0, 0, // old_length
|
|
0x00, 0, 0, 0, // new_offset
|
|
0x03, 0, 0, 0, // new_length
|
|
'P', 'x', '8', '6', // exe_type = EXE_TYPE_WIN32_X86
|
|
0x01, 0x00, // element version
|
|
// EquivalenceSource
|
|
0, 0, 0, 0, // src_skip size
|
|
0, 0, 0, 0, // dst_skip size
|
|
0, 0, 0, 0, // copy_count size
|
|
// ExtraDataSource
|
|
0, 0, 0, 0, // extra_data size
|
|
// RawDeltaSource
|
|
0, 0, 0, 0, // raw_delta_skip size
|
|
0, 0, 0, 0, // raw_delta_diff size
|
|
// ReferenceDeltaSource
|
|
0, 0, 0, 0, // reference_delta size
|
|
// PatchElementReader
|
|
0, 0, 0, 0, // pool count
|
|
};
|
|
|
|
TestInvalidInitialize<EnsemblePatchReader>(&data);
|
|
}
|
|
|
|
TEST(EnsemblePatchTest, InvalidVersion) {
|
|
ByteVector data = {
|
|
// PatchHeader
|
|
0x5A, 0x75, 0x63, 0x63, // magic
|
|
0x02, 0x01, 0x00, 0x00, // major/minor version
|
|
0x10, 0x32, 0x54, 0x76, // old_size
|
|
0x00, 0x11, 0x22, 0x33, // old_crc
|
|
0x03, 0x00, 0x00, 0x00, // new_size
|
|
0x44, 0x55, 0x66, 0x77, // new_crc
|
|
|
|
1, 0, 0, 0, // number of element
|
|
|
|
// PatchElementHeader
|
|
0x01, 0, 0, 0, // old_offset
|
|
0x02, 0, 0, 0, // old_length
|
|
0x00, 0, 0, 0, // new_offset
|
|
0x03, 0, 0, 0, // new_length
|
|
'P', 'x', '8', '6', // exe_type = EXE_TYPE_WIN32_X86
|
|
0x01, 0x00, // element version
|
|
// EquivalenceSource
|
|
0, 0, 0, 0, // src_skip size
|
|
0, 0, 0, 0, // dst_skip size
|
|
0, 0, 0, 0, // copy_count size
|
|
// ExtraDataSource
|
|
0, 0, 0, 0, // extra_data size
|
|
// RawDeltaSource
|
|
0, 0, 0, 0, // raw_delta_skip size
|
|
0, 0, 0, 0, // raw_delta_diff size
|
|
// ReferenceDeltaSource
|
|
0, 0, 0, 0, // reference_delta size
|
|
// PatchElementReader
|
|
0, 0, 0, 0, // pool count
|
|
};
|
|
|
|
TestInvalidInitialize<EnsemblePatchReader>(&data);
|
|
}
|
|
|
|
} // namespace zucchini
|