// 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 #include #include #include #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; // 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 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 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 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* 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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&data); } } TEST(PatchElementTest, WrongVersion) { // Bump element version to 2. { ByteVector data = CreatePatchElement(); ModifyByte(offsetof(PatchElementHeader, version), 0x01, 0x02, &data); TestInvalidInitialize(&data); } // Bump element version to 0. { ByteVector data = CreatePatchElement(); ModifyByte(offsetof(PatchElementHeader, version), 0x01, 0x00, &data); TestInvalidInitialize(&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(&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& 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(&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(&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(&data); } } // namespace zucchini