| // 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 "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); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | bool operator==(const ByteVector& a, ConstBufferView b) { | 
 |   return a == ByteVector(b.begin(), b.end()); | 
 | } | 
 |  | 
 | TEST(PatchTest, ParseSerializeElementMatch) { | 
 |   ByteVector data = { | 
 |       0x01, 0, 0, 0,  // old_offset | 
 |       0x03, 0, 0, 0,  // new_offset | 
 |       0x02, 0, 0, 0,  // old_length | 
 |       0x04, 0, 0, 0,  // new_length | 
 |       7,    0, 0, 0,  // kExeTypeDex | 
 |   }; | 
 |   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, 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 = { | 
 |       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 = { | 
 |       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 = { | 
 |       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 = { | 
 |       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 = { | 
 |       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 = { | 
 |       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(ReferenceDeltaSourceSinkTest, Empty) { | 
 |   ByteVector data = { | 
 |       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 = { | 
 |       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 = { | 
 |       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 = { | 
 |       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 = { | 
 |       0x01, 0, 0, 0,  // old_offset | 
 |       0x03, 0, 0, 0,  // new_offset | 
 |       0x02, 0, 0, 0,  // old_length | 
 |       0x04, 0, 0, 0,  // new_length | 
 |       1,    0, 0, 0,  // EXE_TYPE_WIN32_X86 | 
 |  | 
 |       1,    0, 0, 0,  // src_skip size | 
 |       0x10,           // src_skip content | 
 |       1,    0, 0, 0,  // dst_skip size | 
 |       0x11,           // dst_skip content | 
 |       1,    0, 0, 0,  // copy_count size | 
 |       0x12,           // copy_count content | 
 |  | 
 |       1,    0, 0, 0,  // extra_data size | 
 |       0x13,           // extra_data content | 
 |  | 
 |       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 | 
 |  | 
 |       1,    0, 0, 0,  // reference_delta size | 
 |       0x16,           // reference_delta content | 
 |  | 
 |       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 | 
 |   }; | 
 |  | 
 |   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(0x2U, element_match.old_element.size); | 
 |   EXPECT_EQ(0x3U, element_match.new_element.offset); | 
 |   EXPECT_EQ(0x4U, element_match.new_element.size); | 
 |  | 
 |   EquivalenceSource equivalence_source = | 
 |       patch_element_reader.GetEquivalenceSource(); | 
 |   EXPECT_EQ(ByteVector({0x10}), equivalence_source.src_skip()); | 
 |   EXPECT_EQ(ByteVector({0x11}), 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}, {0x11}, {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(EnsemblePatchTest, RawPatch) { | 
 |   ByteVector data = { | 
 |       0x5A, 0x75, 0x63, 0x00,  // magic | 
 |       0x10, 0x32, 0x54, 0x76,  // old_size | 
 |       0x00, 0x11, 0x22, 0x33,  // old_crc | 
 |       0x98, 0xBA, 0xDC, 0xFE,  // new_size | 
 |       0x44, 0x55, 0x66, 0x77,  // new_crc | 
 |  | 
 |       1,    0,    0,    0,  // number of element | 
 |  | 
 |       0x01, 0,    0,    0,     // old_offset | 
 |       0x00, 0,    0,    0,     // new_offset | 
 |       0x02, 0,    0,    0,     // old_length | 
 |       0x98, 0xBA, 0xDC, 0xFE,  // new_length | 
 |       1,    0,    0,    0,     // EXE_TYPE_WIN32_X86 | 
 |       0,    0,    0,    0,     // src_skip size | 
 |       0,    0,    0,    0,     // dst_skip size | 
 |       0,    0,    0,    0,     // copy_count size | 
 |       0,    0,    0,    0,     // extra_data size | 
 |       0,    0,    0,    0,     // raw_delta_skip size | 
 |       0,    0,    0,    0,     // raw_delta_diff size | 
 |       0,    0,    0,    0,     // reference_delta size | 
 |       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(0x76543210U, header.old_size); | 
 |   EXPECT_EQ(0x33221100U, header.old_crc); | 
 |   EXPECT_EQ(0xFEDCBA98U, 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({}); | 
 |   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 = { | 
 |       0x5A, 0x75, 0x63, 0x00,  // magic | 
 |       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 | 
 |  | 
 |       0x01, 0,    0,    0,  // old_offset | 
 |       0x00, 0,    0,    0,  // new_offset | 
 |       0x02, 0,    0,    0,  // old_length | 
 |       0x03, 0,    0,    0,  // new_length | 
 |       1,    0,    0,    0,  // EXE_TYPE_WIN32_X86 | 
 |       0,    0,    0,    0,  // src_skip size | 
 |       0,    0,    0,    0,  // dst_skip size | 
 |       0,    0,    0,    0,  // copy_count size | 
 |       0,    0,    0,    0,  // extra_data size | 
 |       0,    0,    0,    0,  // raw_delta_skip size | 
 |       0,    0,    0,    0,  // raw_delta_diff size | 
 |       0,    0,    0,    0,  // reference_delta size | 
 |       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 = { | 
 |       0x42, 0x42, 0x42, 0x00,  // magic | 
 |       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 | 
 |  | 
 |       0x01, 0,    0,    0,  // old_offset | 
 |       0x00, 0,    0,    0,  // new_offset | 
 |       0x02, 0,    0,    0,  // old_length | 
 |       0x03, 0,    0,    0,  // new_length | 
 |       1,    0,    0,    0,  // EXE_TYPE_WIN32_X86 | 
 |       0,    0,    0,    0,  // src_skip size | 
 |       0,    0,    0,    0,  // dst_skip size | 
 |       0,    0,    0,    0,  // copy_count size | 
 |       0,    0,    0,    0,  // extra_data size | 
 |       0,    0,    0,    0,  // raw_delta_skip size | 
 |       0,    0,    0,    0,  // raw_delta_diff size | 
 |       0,    0,    0,    0,  // reference_delta size | 
 |       0,    0,    0,    0,  // pool count | 
 |   }; | 
 |  | 
 |   TestInvalidInitialize<EnsemblePatchReader>(&data); | 
 | } | 
 |  | 
 | }  // namespace zucchini |