summaryrefslogtreecommitdiffstats
path: root/sdch/open-vcdiff/src/vcdecoder4_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sdch/open-vcdiff/src/vcdecoder4_test.cc')
-rw-r--r--sdch/open-vcdiff/src/vcdecoder4_test.cc1063
1 files changed, 1063 insertions, 0 deletions
diff --git a/sdch/open-vcdiff/src/vcdecoder4_test.cc b/sdch/open-vcdiff/src/vcdecoder4_test.cc
new file mode 100644
index 0000000..e1fc90f
--- /dev/null
+++ b/sdch/open-vcdiff/src/vcdecoder4_test.cc
@@ -0,0 +1,1063 @@
+// Copyright 2008 Google Inc.
+// Author: Lincoln Smith
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <config.h>
+#include "google/vcdecoder.h"
+#include <string>
+#include "codetable.h"
+#include "testing.h"
+#include "vcdecoder_test.h"
+#include "vcdiff_defs.h" // VCD_SOURCE
+
+namespace open_vcdiff {
+namespace {
+
+// Use the interleaved file header with the standard encoding. Should work.
+class VCDiffDecoderInterleavedAllowedButNotUsed
+ : public VCDiffStandardDecoderTest {
+ public:
+ VCDiffDecoderInterleavedAllowedButNotUsed() {
+ UseInterleavedFileHeader();
+ }
+ virtual ~VCDiffDecoderInterleavedAllowedButNotUsed() { }
+};
+
+TEST_F(VCDiffDecoderInterleavedAllowedButNotUsed, Decode) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+TEST_F(VCDiffDecoderInterleavedAllowedButNotUsed, DecodeWithChecksum) {
+ ComputeAndAddChecksum();
+ InitializeDeltaFile();
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+typedef VCDiffDecoderInterleavedAllowedButNotUsed
+ VCDiffDecoderInterleavedAllowedButNotUsedByteByByte;
+
+TEST_F(VCDiffDecoderInterleavedAllowedButNotUsedByteByByte, Decode) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &output_));
+ }
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+TEST_F(VCDiffDecoderInterleavedAllowedButNotUsedByteByByte,
+ DecodeWithChecksum) {
+ ComputeAndAddChecksum();
+ InitializeDeltaFile();
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &output_));
+ }
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+// Use the standard file header with the interleaved encoding. Should fail.
+class VCDiffDecoderInterleavedUsedButNotSupported
+ : public VCDiffInterleavedDecoderTest {
+ public:
+ VCDiffDecoderInterleavedUsedButNotSupported() {
+ UseStandardFileHeader();
+ }
+ virtual ~VCDiffDecoderInterleavedUsedButNotSupported() { }
+};
+
+TEST_F(VCDiffDecoderInterleavedUsedButNotSupported, DecodeShouldFail) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_FALSE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_EQ("", output_);
+}
+
+TEST_F(VCDiffDecoderInterleavedUsedButNotSupported,
+ DecodeByteByByteShouldFail) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ bool failed = false;
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ if (!decoder_.DecodeChunk(&delta_file_[i], 1, &output_)) {
+ failed = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(failed);
+ // The decoder should not create more target bytes than were expected.
+ EXPECT_GE(expected_target_.size(), output_.size());
+}
+
+// Divides up the standard encoding into eight separate delta file windows.
+// Each delta instruction appears in its own window.
+class VCDiffStandardWindowDecoderTest : public VCDiffDecoderTest {
+ protected:
+ static const size_t kWindow2Size = 61;
+
+ VCDiffStandardWindowDecoderTest();
+ virtual ~VCDiffStandardWindowDecoderTest() {}
+
+ private:
+ static const char kWindowBody[];
+};
+
+const size_t VCDiffStandardWindowDecoderTest::kWindow2Size;
+
+const char VCDiffStandardWindowDecoderTest::kWindowBody[] = {
+// Window 1:
+ VCD_SOURCE, // Win_Indicator: take source from dictionary
+ FirstByteOfStringLength(kDictionary), // Source segment size
+ SecondByteOfStringLength(kDictionary),
+ 0x00, // Source segment position: start of dictionary
+ 0x08, // Length of the delta encoding
+ 0x1C, // Size of the target window (28)
+ 0x00, // Delta_indicator (no compression)
+ 0x00, // length of data for ADDs and RUNs
+ 0x02, // length of instructions section
+ 0x01, // length of addresses for COPYs
+ // No data for ADDs and RUNs
+ // Instructions and sizes (length 2)
+ 0x13, // VCD_COPY mode VCD_SELF, size 0
+ 0x1C, // Size of COPY (28)
+ // Addresses for COPYs (length 1)
+ 0x00, // Start of dictionary
+// Window 2:
+ 0x00, // Win_Indicator: No source segment (ADD only)
+ 0x44, // Length of the delta encoding
+ static_cast<char>(kWindow2Size), // Size of the target window (61)
+ 0x00, // Delta_indicator (no compression)
+ 0x3D, // length of data for ADDs and RUNs
+ 0x02, // length of instructions section
+ 0x00, // length of addresses for COPYs
+ // Data for ADD (length 61)
+ ' ', 'I', ' ', 'h', 'a', 'v', 'e', ' ', 's', 'a', 'i', 'd', ' ',
+ 'i', 't', ' ', 't', 'w', 'i', 'c', 'e', ':', '\n',
+ 'T', 'h', 'a', 't', ' ',
+ 'a', 'l', 'o', 'n', 'e', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ',
+ 'e', 'n', 'c', 'o', 'u', 'r', 'a', 'g', 'e', ' ',
+ 't', 'h', 'e', ' ', 'c', 'r', 'e', 'w', '.', '\n',
+ // Instructions and sizes (length 2)
+ 0x01, // VCD_ADD size 0
+ 0x3D, // Size of ADD (61)
+ // No addresses for COPYs
+// Window 3:
+ VCD_TARGET, // Win_Indicator: take source from decoded data
+ 0x59, // Source segment size: length of data decoded so far
+ 0x00, // Source segment position: start of decoded data
+ 0x08, // Length of the delta encoding
+ 0x2C, // Size of the target window
+ 0x00, // Delta_indicator (no compression)
+ 0x00, // length of data for ADDs and RUNs
+ 0x02, // length of instructions section
+ 0x01, // length of addresses for COPYs
+ // No data for ADDs and RUNs
+ // Instructions and sizes (length 2)
+ 0x23, // VCD_COPY mode VCD_HERE, size 0
+ 0x2C, // Size of COPY (44)
+ // Addresses for COPYs (length 1)
+ 0x58, // HERE mode address (27+61 back from here_address)
+// Window 4:
+ VCD_TARGET, // Win_Indicator: take source from decoded data
+ 0x05, // Source segment size: only 5 bytes needed for this COPY
+ 0x2E, // Source segment position: offset for COPY
+ 0x09, // Length of the delta encoding
+ 0x07, // Size of the target window
+ 0x00, // Delta_indicator (no compression)
+ 0x02, // length of data for ADDs and RUNs
+ 0x01, // length of instructions section
+ 0x01, // length of addresses for COPYs
+ // Data for ADD (length 2)
+ 'h', 'r',
+ // Instructions and sizes (length 1)
+ 0xA7, // VCD_ADD size 2 + VCD_COPY mode SELF size 5
+ // Addresses for COPYs (length 1)
+ 0x00, // SELF mode address (start of source segment)
+// Window 5:
+ 0x00, // Win_Indicator: No source segment (ADD only)
+ 0x0F, // Length of the delta encoding
+ 0x09, // Size of the target window
+ 0x00, // Delta_indicator (no compression)
+ 0x09, // length of data for ADDs and RUNs
+ 0x01, // length of instructions section
+ 0x00, // length of addresses for COPYs
+ // Data for ADD (length 9)
+ 'W', 'h', 'a', 't', ' ', 'I', ' ', 't', 'e',
+ // Instructions and sizes (length 1)
+ 0x0A, // VCD_ADD size 9
+ // No addresses for COPYs
+// Window 6:
+ 0x00, // Win_Indicator: No source segment (RUN only)
+ 0x08, // Length of the delta encoding
+ 0x02, // Size of the target window
+ 0x00, // Delta_indicator (no compression)
+ 0x01, // length of data for ADDs and RUNs
+ 0x02, // length of instructions section
+ 0x00, // length of addresses for COPYs
+ // Data for RUN (length 1)
+ 'l',
+ // Instructions and sizes (length 2)
+ 0x00, // VCD_RUN size 0
+ 0x02, // Size of RUN (2)
+ // No addresses for COPYs
+// Window 7:
+ 0x00, // Win_Indicator: No source segment (ADD only)
+ 0x22, // Length of the delta encoding
+ 0x1B, // Size of the target window
+ 0x00, // Delta_indicator (no compression)
+ 0x1B, // length of data for ADDs and RUNs
+ 0x02, // length of instructions section
+ 0x00, // length of addresses for COPYs
+ // Data for ADD: 4th section (length 27)
+ ' ', 'y', 'o', 'u', ' ',
+ 't', 'h', 'r', 'e', 'e', ' ', 't', 'i', 'm', 'e', 's', ' ', 'i', 's', ' ',
+ 't', 'r', 'u', 'e', '.', '\"', '\n',
+ // Instructions and sizes (length 2)
+ 0x01, // VCD_ADD size 0
+ 0x1B, // Size of ADD (27)
+ // No addresses for COPYs
+ };
+
+VCDiffStandardWindowDecoderTest::VCDiffStandardWindowDecoderTest() {
+ UseStandardFileHeader();
+ delta_window_body_.assign(kWindowBody, sizeof(kWindowBody));
+}
+
+TEST_F(VCDiffStandardWindowDecoderTest, Decode) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+// Bug 1287926: If DecodeChunk() stops in the middle of the window header,
+// and the expected size of the current target window is smaller than the
+// cumulative target bytes decoded so far, an underflow occurs and the decoder
+// tries to allocate ~MAX_INT bytes.
+TEST_F(VCDiffStandardWindowDecoderTest, DecodeBreakInFourthWindowHeader) {
+ // Parse file header + first two windows.
+ const size_t chunk_1_size = delta_file_header_.size() + 83;
+ // Parse third window, plus everything up to "Size of the target window" field
+ // of fourth window, but do not parse complete header of fourth window.
+ const size_t chunk_2_size = 12 + 5;
+ CHECK_EQ(VCD_TARGET, static_cast<unsigned char>(delta_file_[chunk_1_size]));
+ CHECK_EQ(0x00, static_cast<int>(delta_file_[chunk_1_size + chunk_2_size]));
+ string output_chunk1, output_chunk2, output_chunk3;
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[0],
+ chunk_1_size,
+ &output_chunk1));
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[chunk_1_size],
+ chunk_2_size,
+ &output_chunk2));
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[chunk_1_size + chunk_2_size],
+ delta_file_.size()
+ - (chunk_1_size + chunk_2_size),
+ &output_chunk3));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_chunk1 + output_chunk2 + output_chunk3);
+}
+
+TEST_F(VCDiffStandardWindowDecoderTest, DecodeChunkNoVcdTargetAllowed) {
+ decoder_.SetAllowVcdTarget(false);
+ // Parse file header + first two windows.
+ const size_t chunk_1_size = delta_file_header_.size() + 83;
+ // The third window begins with Win_Indicator = VCD_TARGET which is not
+ // allowed.
+ CHECK_EQ(VCD_TARGET, static_cast<unsigned char>(delta_file_[chunk_1_size]));
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[0], chunk_1_size, &output_));
+ // Just parsing one more byte (the VCD_TARGET) should result in an error.
+ EXPECT_FALSE(decoder_.DecodeChunk(&delta_file_[chunk_1_size], 1, &output_));
+ // The target data for the first two windows should have been output.
+ EXPECT_EQ(expected_target_.substr(0, 89), output_);
+}
+
+TEST_F(VCDiffStandardWindowDecoderTest, DecodeInTwoParts) {
+ const size_t delta_file_size = delta_file_.size();
+ for (size_t i = 1; i < delta_file_size; i++) {
+ string output_chunk1, output_chunk2;
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[0],
+ i,
+ &output_chunk1));
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i],
+ delta_file_size - i,
+ &output_chunk2));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_chunk1 + output_chunk2);
+ }
+}
+
+TEST_F(VCDiffStandardWindowDecoderTest, DecodeInThreeParts) {
+ const size_t delta_file_size = delta_file_.size();
+ for (size_t i = 1; i < delta_file_size - 1; i++) {
+ for (size_t j = i + 1; j < delta_file_size; j++) {
+ string output_chunk1, output_chunk2, output_chunk3;
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[0],
+ i,
+ &output_chunk1));
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i],
+ j - i,
+ &output_chunk2));
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[j],
+ delta_file_size - j,
+ &output_chunk3));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_,
+ output_chunk1 + output_chunk2 + output_chunk3);
+ }
+ }
+}
+
+// For the window test, the maximum target window size is much smaller than the
+// target file size. (The largest window is Window 2, with 61 target bytes.)
+// Use the minimum values possible.
+TEST_F(VCDiffStandardWindowDecoderTest, TargetMatchesWindowSizeLimit) {
+ decoder_.SetMaximumTargetWindowSize(kWindow2Size);
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+TEST_F(VCDiffStandardWindowDecoderTest, TargetMatchesFileSizeLimit) {
+ decoder_.SetMaximumTargetFileSize(expected_target_.size());
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+TEST_F(VCDiffStandardWindowDecoderTest, TargetExceedsWindowSizeLimit) {
+ decoder_.SetMaximumTargetWindowSize(kWindow2Size - 1);
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_FALSE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_EQ("", output_);
+}
+
+TEST_F(VCDiffStandardWindowDecoderTest, TargetExceedsFileSizeLimit) {
+ decoder_.SetMaximumTargetFileSize(expected_target_.size() - 1);
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_FALSE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_EQ("", output_);
+}
+
+typedef VCDiffStandardWindowDecoderTest
+ VCDiffStandardWindowDecoderTestByteByByte;
+
+TEST_F(VCDiffStandardWindowDecoderTestByteByByte, Decode) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &output_));
+ }
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+TEST_F(VCDiffStandardWindowDecoderTestByteByByte, DecodeExplicitVcdTarget) {
+ decoder_.SetAllowVcdTarget(true);
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &output_));
+ }
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+// Windows 3 and 4 use the VCD_TARGET flag, so decoder should signal an error.
+TEST_F(VCDiffStandardWindowDecoderTestByteByByte, DecodeNoVcdTarget) {
+ decoder_.SetAllowVcdTarget(false);
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ size_t i = 0;
+ for (; i < delta_file_.size(); ++i) {
+ if (!decoder_.DecodeChunk(&delta_file_[i], 1, &output_)) {
+ break;
+ }
+ }
+ // The failure should occur just at the position of the first VCD_TARGET.
+ EXPECT_EQ(delta_file_header_.size() + 83, i);
+ // The target data for the first two windows should have been output.
+ EXPECT_EQ(expected_target_.substr(0, 89), output_);
+}
+
+// Divides up the interleaved encoding into eight separate delta file windows.
+class VCDiffInterleavedWindowDecoderTest
+ : public VCDiffStandardWindowDecoderTest {
+ protected:
+ VCDiffInterleavedWindowDecoderTest();
+ virtual ~VCDiffInterleavedWindowDecoderTest() {}
+ private:
+ static const char kWindowBody[];
+};
+
+const char VCDiffInterleavedWindowDecoderTest::kWindowBody[] = {
+// Window 1:
+ VCD_SOURCE, // Win_Indicator: take source from dictionary
+ FirstByteOfStringLength(kDictionary), // Source segment size
+ SecondByteOfStringLength(kDictionary),
+ 0x00, // Source segment position: start of dictionary
+ 0x08, // Length of the delta encoding
+ 0x1C, // Size of the target window (28)
+ 0x00, // Delta_indicator (no compression)
+ 0x00, // length of data for ADDs and RUNs
+ 0x03, // length of instructions section
+ 0x00, // length of addresses for COPYs
+ 0x13, // VCD_COPY mode VCD_SELF, size 0
+ 0x1C, // Size of COPY (28)
+ 0x00, // Start of dictionary
+// Window 2:
+ 0x00, // Win_Indicator: No source segment (ADD only)
+ 0x44, // Length of the delta encoding
+ 0x3D, // Size of the target window (61)
+ 0x00, // Delta_indicator (no compression)
+ 0x00, // length of data for ADDs and RUNs
+ 0x3F, // length of instructions section
+ 0x00, // length of addresses for COPYs
+ 0x01, // VCD_ADD size 0
+ 0x3D, // Size of ADD (61)
+ ' ', 'I', ' ', 'h', 'a', 'v', 'e', ' ', 's', 'a', 'i', 'd', ' ',
+ 'i', 't', ' ', 't', 'w', 'i', 'c', 'e', ':', '\n',
+ 'T', 'h', 'a', 't', ' ',
+ 'a', 'l', 'o', 'n', 'e', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ',
+ 'e', 'n', 'c', 'o', 'u', 'r', 'a', 'g', 'e', ' ',
+ 't', 'h', 'e', ' ', 'c', 'r', 'e', 'w', '.', '\n',
+// Window 3:
+ VCD_TARGET, // Win_Indicator: take source from decoded data
+ 0x59, // Source segment size: length of data decoded so far
+ 0x00, // Source segment position: start of decoded data
+ 0x08, // Length of the delta encoding
+ 0x2C, // Size of the target window
+ 0x00, // Delta_indicator (no compression)
+ 0x00, // length of data for ADDs and RUNs
+ 0x03, // length of instructions section
+ 0x00, // length of addresses for COPYs
+ 0x23, // VCD_COPY mode VCD_HERE, size 0
+ 0x2C, // Size of COPY (44)
+ 0x58, // HERE mode address (27+61 back from here_address)
+// Window 4:
+ VCD_TARGET, // Win_Indicator: take source from decoded data
+ 0x05, // Source segment size: only 5 bytes needed for this COPY
+ 0x2E, // Source segment position: offset for COPY
+ 0x09, // Length of the delta encoding
+ 0x07, // Size of the target window
+ 0x00, // Delta_indicator (no compression)
+ 0x00, // length of data for ADDs and RUNs
+ 0x04, // length of instructions section
+ 0x00, // length of addresses for COPYs
+ 0xA7, // VCD_ADD size 2 + VCD_COPY mode SELF, size 5
+ 'h', 'r',
+ 0x00, // SELF mode address (start of source segment)
+// Window 5:
+ 0x00, // Win_Indicator: No source segment (ADD only)
+ 0x0F, // Length of the delta encoding
+ 0x09, // Size of the target window
+ 0x00, // Delta_indicator (no compression)
+ 0x00, // length of data for ADDs and RUNs
+ 0x0A, // length of instructions section
+ 0x00, // length of addresses for COPYs
+ 0x0A, // VCD_ADD size 9
+ 'W', 'h', 'a', 't', ' ', 'I', ' ', 't', 'e',
+// Window 6:
+ 0x00, // Win_Indicator: No source segment (RUN only)
+ 0x08, // Length of the delta encoding
+ 0x02, // Size of the target window
+ 0x00, // Delta_indicator (no compression)
+ 0x00, // length of data for ADDs and RUNs
+ 0x03, // length of instructions section
+ 0x00, // length of addresses for COPYs
+ 0x00, // VCD_RUN size 0
+ 0x02, // Size of RUN (2)
+ 'l',
+// Window 7:
+ 0x00, // Win_Indicator: No source segment (ADD only)
+ 0x22, // Length of the delta encoding
+ 0x1B, // Size of the target window
+ 0x00, // Delta_indicator (no compression)
+ 0x00, // length of data for ADDs and RUNs
+ 0x1D, // length of instructions section
+ 0x00, // length of addresses for COPYs
+ 0x01, // VCD_ADD size 0
+ 0x1B, // Size of ADD (27)
+ ' ', 'y', 'o', 'u', ' ',
+ 't', 'h', 'r', 'e', 'e', ' ', 't', 'i', 'm', 'e', 's', ' ', 'i', 's', ' ',
+ 't', 'r', 'u', 'e', '.', '\"', '\n',
+ };
+
+VCDiffInterleavedWindowDecoderTest::VCDiffInterleavedWindowDecoderTest() {
+ UseInterleavedFileHeader();
+ // delta_window_header_ is left blank. All window headers and bodies are
+ // lumped together in delta_window_body_. This means that AddChecksum()
+ // cannot be used to test the checksum feature.
+ delta_window_body_.assign(kWindowBody, sizeof(kWindowBody));
+}
+
+TEST_F(VCDiffInterleavedWindowDecoderTest, Decode) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+TEST_F(VCDiffInterleavedWindowDecoderTest, DecodeInTwoParts) {
+ const size_t delta_file_size = delta_file_.size();
+ for (size_t i = 1; i < delta_file_size; i++) {
+ string output_chunk1, output_chunk2;
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[0],
+ i,
+ &output_chunk1));
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i],
+ delta_file_size - i,
+ &output_chunk2));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_chunk1 + output_chunk2);
+ }
+}
+
+TEST_F(VCDiffInterleavedWindowDecoderTest, DecodeInThreeParts) {
+ const size_t delta_file_size = delta_file_.size();
+ for (size_t i = 1; i < delta_file_size - 1; i++) {
+ for (size_t j = i + 1; j < delta_file_size; j++) {
+ string output_chunk1, output_chunk2, output_chunk3;
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[0],
+ i,
+ &output_chunk1));
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i],
+ j - i,
+ &output_chunk2));
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[j],
+ delta_file_size - j,
+ &output_chunk3));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_,
+ output_chunk1 + output_chunk2 + output_chunk3);
+ }
+ }
+}
+
+typedef VCDiffInterleavedWindowDecoderTest
+ VCDiffInterleavedWindowDecoderTestByteByByte;
+
+TEST_F(VCDiffInterleavedWindowDecoderTestByteByByte, Decode) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &output_));
+ }
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+// Windows 3 and 4 use the VCD_TARGET flag, so decoder should signal an error.
+TEST_F(VCDiffInterleavedWindowDecoderTestByteByByte, DecodeNoVcdTarget) {
+ decoder_.SetAllowVcdTarget(false);
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ size_t i = 0;
+ for (; i < delta_file_.size(); ++i) {
+ if (!decoder_.DecodeChunk(&delta_file_[i], 1, &output_)) {
+ break;
+ }
+ }
+ // The failure should occur just at the position of the first VCD_TARGET.
+ EXPECT_EQ(delta_file_header_.size() + 83, i);
+ // The target data for the first two windows should have been output.
+ EXPECT_EQ(expected_target_.substr(0, 89), output_);
+}
+
+// The original version of VCDiffDecoder did not allow the caller to modify the
+// contents of output_string between calls to DecodeChunk(). That restriction
+// has been removed. Verify that the same result is still produced if the
+// output string is cleared after each call to DecodeChunk(). Use the window
+// encoding because it refers back to the previously decoded target data, which
+// is the feature that would fail if the restriction still applied.
+//
+TEST_F(VCDiffInterleavedWindowDecoderTest, OutputStringCanBeModified) {
+ string temp_output;
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &temp_output));
+ output_.append(temp_output);
+ temp_output.clear();
+ }
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+TEST_F(VCDiffInterleavedWindowDecoderTest, OutputStringIsPreserved) {
+ const string previous_data("Previous data");
+ output_ = previous_data;
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &output_));
+ }
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(previous_data + expected_target_, output_);
+}
+
+// A decode job that tests the ability to COPY across the boundary between
+// source data and target data.
+class VCDiffStandardCrossDecoderTest : public VCDiffDecoderTest {
+ protected:
+ static const char kExpectedTarget[];
+ static const char kWindowHeader[];
+ static const char kWindowBody[];
+
+ VCDiffStandardCrossDecoderTest();
+ virtual ~VCDiffStandardCrossDecoderTest() {}
+};
+
+const char VCDiffStandardCrossDecoderTest::kWindowHeader[] = {
+ VCD_SOURCE, // Win_Indicator: take source from dictionary
+ FirstByteOfStringLength(kDictionary), // Source segment size
+ SecondByteOfStringLength(kDictionary),
+ 0x00, // Source segment position: start of dictionary
+ 0x15, // Length of the delta encoding
+ StringLengthAsByte(kExpectedTarget), // Size of the target window
+ 0x00, // Delta_indicator (no compression)
+ 0x07, // length of data for ADDs and RUNs
+ 0x06, // length of instructions section
+ 0x03 // length of addresses for COPYs
+ };
+
+const char VCDiffStandardCrossDecoderTest::kWindowBody[] = {
+ // Data for ADD (length 7)
+ 'S', 'p', 'i', 'd', 'e', 'r', 's',
+ // Instructions and sizes (length 6)
+ 0x01, // VCD_ADD size 0
+ 0x07, // Size of ADD (7)
+ 0x23, // VCD_COPY mode VCD_HERE, size 0
+ 0x19, // Size of COPY (25)
+ 0x14, // VCD_COPY mode VCD_SELF, size 4
+ 0x25, // VCD_COPY mode VCD_HERE, size 5
+ // Addresses for COPYs (length 3)
+ 0x15, // HERE mode address for 1st copy (21 back from here_address)
+ 0x06, // SELF mode address for 2nd copy
+ 0x14 // HERE mode address for 3rd copy
+ };
+
+const char VCDiffStandardCrossDecoderTest::kExpectedTarget[] =
+ "Spiders in his hair.\n"
+ "Spiders in the air.\n";
+
+VCDiffStandardCrossDecoderTest::VCDiffStandardCrossDecoderTest() {
+ UseStandardFileHeader();
+ delta_window_header_.assign(kWindowHeader, sizeof(kWindowHeader));
+ delta_window_body_.assign(kWindowBody, sizeof(kWindowBody));
+ expected_target_.assign(kExpectedTarget);
+}
+
+TEST_F(VCDiffStandardCrossDecoderTest, Decode) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+typedef VCDiffStandardCrossDecoderTest VCDiffStandardCrossDecoderTestByteByByte;
+
+TEST_F(VCDiffStandardCrossDecoderTestByteByByte, Decode) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &output_));
+ }
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+// The same decode job that tests the ability to COPY across the boundary
+// between source data and target data, but using the interleaved format rather
+// than the standard format.
+class VCDiffInterleavedCrossDecoderTest
+ : public VCDiffStandardCrossDecoderTest {
+ protected:
+ VCDiffInterleavedCrossDecoderTest();
+ virtual ~VCDiffInterleavedCrossDecoderTest() {}
+
+ private:
+ static const char kWindowHeader[];
+ static const char kWindowBody[];
+};
+
+const char VCDiffInterleavedCrossDecoderTest::kWindowHeader[] = {
+ VCD_SOURCE, // Win_Indicator: take source from dictionary
+ FirstByteOfStringLength(kDictionary), // Source segment size
+ SecondByteOfStringLength(kDictionary),
+ 0x00, // Source segment position: start of dictionary
+ 0x15, // Length of the delta encoding
+ StringLengthAsByte(kExpectedTarget), // Size of the target window
+ 0x00, // Delta_indicator (no compression)
+ 0x00, // length of data for ADDs and RUNs
+ 0x10, // length of instructions section
+ 0x00, // length of addresses for COPYs
+ };
+
+const char VCDiffInterleavedCrossDecoderTest::kWindowBody[] = {
+ 0x01, // VCD_ADD size 0
+ 0x07, // Size of ADD (7)
+ // Data for ADD (length 7)
+ 'S', 'p', 'i', 'd', 'e', 'r', 's',
+ 0x23, // VCD_COPY mode VCD_HERE, size 0
+ 0x19, // Size of COPY (25)
+ 0x15, // HERE mode address for 1st copy (21 back from here_address)
+ 0x14, // VCD_COPY mode VCD_SELF, size 4
+ 0x06, // SELF mode address for 2nd copy
+ 0x25, // VCD_COPY mode VCD_HERE, size 5
+ 0x14 // HERE mode address for 3rd copy
+ };
+
+VCDiffInterleavedCrossDecoderTest::VCDiffInterleavedCrossDecoderTest() {
+ UseInterleavedFileHeader();
+ delta_window_header_.assign(kWindowHeader, sizeof(kWindowHeader));
+ delta_window_body_.assign(kWindowBody, sizeof(kWindowBody));
+}
+
+TEST_F(VCDiffInterleavedCrossDecoderTest, Decode) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+TEST_F(VCDiffInterleavedCrossDecoderTest, DecodeWithChecksum) {
+ ComputeAndAddChecksum();
+ InitializeDeltaFile();
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+typedef VCDiffInterleavedCrossDecoderTest
+ VCDiffInterleavedCrossDecoderTestByteByByte;
+
+TEST_F(VCDiffInterleavedCrossDecoderTestByteByByte, Decode) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &output_));
+ }
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+TEST_F(VCDiffInterleavedCrossDecoderTestByteByByte, DecodeWithChecksum) {
+ ComputeAndAddChecksum();
+ InitializeDeltaFile();
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &output_));
+ }
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+// Test using a custom code table and custom cache sizes with interleaved
+// format.
+class VCDiffCustomCodeTableDecoderTest : public VCDiffInterleavedDecoderTest {
+ protected:
+ static const char kFileHeader[];
+ static const char kWindowHeader[];
+ static const char kWindowBody[];
+ static const char kEncodedCustomCodeTable[];
+
+ VCDiffCustomCodeTableDecoderTest();
+ virtual ~VCDiffCustomCodeTableDecoderTest() {}
+};
+
+const char VCDiffCustomCodeTableDecoderTest::kFileHeader[] = {
+ 0xD6, // 'V' | 0x80
+ 0xC3, // 'C' | 0x80
+ 0xC4, // 'D' | 0x80
+ 'S', // SDCH version code
+ 0x02 // Hdr_Indicator: Use custom code table
+ };
+
+// Make a custom code table that includes exactly the instructions we need
+// to encode the first test's data without using any explicit length values.
+// Be careful not to replace any existing opcodes that have size 0,
+// to ensure that the custom code table is valid (can express all possible
+// values of inst (also known as instruction type) and mode with size 0.)
+// This encoding uses interleaved format, which is easier to read.
+//
+// Here are the changes to the standard code table:
+// ADD size 2 (opcode 3) => RUN size 2 (inst1[3] = VCD_RUN)
+// ADD size 16 (opcode 17) => ADD size 27 (size1[17] = 27)
+// ADD size 17 (opcode 18) => ADD size 61 (size1[18] = 61)
+// COPY mode 0 size 18 (opcode 34) => COPY mode 0 size 28 (size1[34] = 28)
+// COPY mode 1 size 18 (opcode 50) => COPY mode 1 size 44 (size1[50] = 44)
+//
+const char VCDiffCustomCodeTableDecoderTest::kEncodedCustomCodeTable[] = {
+ 0xD6, // 'V' | 0x80
+ 0xC3, // 'C' | 0x80
+ 0xC4, // 'D' | 0x80
+ 'S', // SDCH version code
+ 0x00, // Hdr_Indicator: no custom code table, no compression
+ VCD_SOURCE, // Win_Indicator: take source from dictionary
+ (sizeof(VCDiffCodeTableData) >> 7) | 0x80, // First byte of table length
+ sizeof(VCDiffCodeTableData) & 0x7F, // Second byte of table length
+ 0x00, // Source segment position: start of default code table
+ 0x1F, // Length of the delta encoding
+ (sizeof(VCDiffCodeTableData) >> 7) | 0x80, // First byte of table length
+ sizeof(VCDiffCodeTableData) & 0x7F, // Second byte of table length
+ 0x00, // Delta_indicator (no compression)
+ 0x00, // length of data for ADDs and RUNs (unused)
+ 0x19, // length of interleaved section
+ 0x00, // length of addresses for COPYs (unused)
+ 0x05, // VCD_ADD size 4
+ // Data for ADD (length 4)
+ VCD_RUN, VCD_ADD, VCD_ADD, VCD_RUN,
+ 0x13, // VCD_COPY mode VCD_SELF size 0
+ 0x84, // Size of copy: upper bits (512 - 4 + 17 = 525)
+ 0x0D, // Size of copy: lower bits
+ 0x04, // Address of COPY
+ 0x03, // VCD_ADD size 2
+ // Data for ADD (length 2)
+ 0x1B, 0x3D,
+ 0x3F, // VCD_COPY mode VCD_NEAR(0) size 15
+ 0x84, // Address of copy: upper bits (525 + 2 = 527)
+ 0x0F, // Address of copy: lower bits
+ 0x02, // VCD_ADD size 1
+ // Data for ADD (length 1)
+ 0x1C,
+ 0x4F, // VCD_COPY mode VCD_NEAR(1) size 15
+ 0x10, // Address of copy
+ 0x02, // VCD_ADD size 1
+ // Data for ADD (length 1)
+ 0x2C,
+ 0x53, // VCD_COPY mode VCD_NEAR(2) size 0
+ 0x87, // Size of copy: upper bits (256 * 4 - 51 = 973)
+ 0x4D, // Size of copy: lower bits
+ 0x10 // Address of copy
+ };
+
+// This is similar to VCDiffInterleavedDecoderTest, but uses the custom code
+// table to eliminate the need to explicitly encode instruction sizes.
+// Notice that NEAR(0) mode is used here where NEAR(1) mode was used in
+// VCDiffInterleavedDecoderTest. This is because the custom code table
+// has the size of the NEAR cache set to 1; only the most recent
+// COPY instruction is available. This will also be a test of
+// custom cache sizes.
+const char VCDiffCustomCodeTableDecoderTest::kWindowHeader[] = {
+ VCD_SOURCE, // Win_Indicator: take source from dictionary
+ FirstByteOfStringLength(kDictionary), // Source segment size
+ SecondByteOfStringLength(kDictionary),
+ 0x00, // Source segment position: start of dictionary
+ 0x74, // Length of the delta encoding
+ FirstByteOfStringLength(kExpectedTarget), // Size of the target window
+ SecondByteOfStringLength(kExpectedTarget),
+ 0x00, // Delta_indicator (no compression)
+ 0x00, // length of data for ADDs and RUNs (unused)
+ 0x6E, // length of interleaved section
+ 0x00 // length of addresses for COPYs (unused)
+ };
+
+const char VCDiffCustomCodeTableDecoderTest::kWindowBody[] = {
+ 0x22, // VCD_COPY mode VCD_SELF, size 28
+ 0x00, // Address of COPY: Start of dictionary
+ 0x12, // VCD_ADD size 61
+ // Data for ADD (length 61)
+ ' ', 'I', ' ', 'h', 'a', 'v', 'e', ' ', 's', 'a', 'i', 'd', ' ',
+ 'i', 't', ' ', 't', 'w', 'i', 'c', 'e', ':', '\n',
+ 'T', 'h', 'a', 't', ' ',
+ 'a', 'l', 'o', 'n', 'e', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ',
+ 'e', 'n', 'c', 'o', 'u', 'r', 'a', 'g', 'e', ' ',
+ 't', 'h', 'e', ' ', 'c', 'r', 'e', 'w', '.', '\n',
+ 0x32, // VCD_COPY mode VCD_HERE, size 44
+ 0x58, // HERE mode address (27+61 back from here_address)
+ 0xBF, // VCD_ADD size 2 + VCD_COPY mode NEAR(0), size 5
+ // Data for ADDs: 2nd section (length 2)
+ 'h', 'r',
+ 0x2D, // NEAR(0) mode address (45 after prior address)
+ 0x0A, // VCD_ADD size 9
+ // Data for ADDs: 3rd section (length 9)
+ 'W', 'h', 'a', 't', ' ',
+ 'I', ' ', 't', 'e',
+ 0x03, // VCD_RUN size 2
+ // Data for RUN: 4th section (length 1)
+ 'l',
+ 0x11, // VCD_ADD size 27
+ // Data for ADD: 4th section (length 27)
+ ' ', 'y', 'o', 'u', ' ',
+ 't', 'h', 'r', 'e', 'e', ' ', 't', 'i', 'm', 'e', 's', ' ', 'i', 's', ' ',
+ 't', 'r', 'u', 'e', '.', '\"', '\n'
+ };
+
+VCDiffCustomCodeTableDecoderTest::VCDiffCustomCodeTableDecoderTest() {
+ delta_file_header_.assign(kFileHeader, sizeof(kFileHeader));
+ delta_file_header_.push_back(0x01); // NEAR cache size (custom)
+ delta_file_header_.push_back(0x06); // SAME cache size (custom)
+ delta_file_header_.append(kEncodedCustomCodeTable,
+ sizeof(kEncodedCustomCodeTable));
+ delta_window_header_.assign(kWindowHeader, sizeof(kWindowHeader));
+ delta_window_body_.assign(kWindowBody, sizeof(kWindowBody));
+}
+
+TEST_F(VCDiffCustomCodeTableDecoderTest, CustomCodeTableEncodingMatches) {
+ VCDiffCodeTableData custom_code_table(
+ VCDiffCodeTableData::kDefaultCodeTableData);
+ custom_code_table.inst1[3] = VCD_RUN;
+ custom_code_table.size1[17] = 27;
+ custom_code_table.size1[18] = 61;
+ custom_code_table.size1[34] = 28;
+ custom_code_table.size1[50] = 44;
+
+ decoder_.StartDecoding(
+ reinterpret_cast<const char*>(
+ &VCDiffCodeTableData::kDefaultCodeTableData),
+ sizeof(VCDiffCodeTableData::kDefaultCodeTableData));
+ EXPECT_TRUE(decoder_.DecodeChunk(kEncodedCustomCodeTable,
+ sizeof(kEncodedCustomCodeTable),
+ &output_));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(sizeof(custom_code_table), output_.size());
+ const VCDiffCodeTableData* decoded_table =
+ reinterpret_cast<const VCDiffCodeTableData*>(output_.data());
+ EXPECT_EQ(VCD_RUN, decoded_table->inst1[0]);
+ EXPECT_EQ(VCD_RUN, decoded_table->inst1[3]);
+ EXPECT_EQ(27, decoded_table->size1[17]);
+ EXPECT_EQ(61, decoded_table->size1[18]);
+ EXPECT_EQ(28, decoded_table->size1[34]);
+ EXPECT_EQ(44, decoded_table->size1[50]);
+ for (int i = 0; i < VCDiffCodeTableData::kCodeTableSize; ++i) {
+ EXPECT_EQ(custom_code_table.inst1[i], decoded_table->inst1[i]);
+ EXPECT_EQ(custom_code_table.inst2[i], decoded_table->inst2[i]);
+ EXPECT_EQ(custom_code_table.size1[i], decoded_table->size1[i]);
+ EXPECT_EQ(custom_code_table.size2[i], decoded_table->size2[i]);
+ EXPECT_EQ(custom_code_table.mode1[i], decoded_table->mode1[i]);
+ EXPECT_EQ(custom_code_table.mode2[i], decoded_table->mode2[i]);
+ }
+}
+
+TEST_F(VCDiffCustomCodeTableDecoderTest, DecodeUsingCustomCodeTable) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_));
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+TEST_F(VCDiffCustomCodeTableDecoderTest, IncompleteCustomCodeTable) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_TRUE(decoder_.DecodeChunk(delta_file_header_.data(),
+ delta_file_header_.size() - 1,
+ &output_));
+ EXPECT_FALSE(decoder_.FinishDecoding());
+ EXPECT_EQ("", output_);
+}
+
+typedef VCDiffCustomCodeTableDecoderTest
+ VCDiffCustomCodeTableDecoderTestByteByByte;
+
+TEST_F(VCDiffCustomCodeTableDecoderTestByteByByte, DecodeUsingCustomCodeTable) {
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &output_));
+ }
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+TEST_F(VCDiffCustomCodeTableDecoderTestByteByByte, IncompleteCustomCodeTable) {
+ delta_file_.resize(delta_file_header_.size() - 1);
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &output_));
+ }
+ EXPECT_FALSE(decoder_.FinishDecoding());
+ EXPECT_EQ("", output_);
+}
+
+TEST_F(VCDiffCustomCodeTableDecoderTestByteByByte, CustomTableNoVcdTarget) {
+ decoder_.SetAllowVcdTarget(false);
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ for (size_t i = 0; i < delta_file_.size(); ++i) {
+ EXPECT_TRUE(decoder_.DecodeChunk(&delta_file_[i], 1, &output_));
+ }
+ EXPECT_TRUE(decoder_.FinishDecoding());
+ EXPECT_EQ(expected_target_, output_);
+}
+
+#ifdef GTEST_HAS_DEATH_TEST
+typedef VCDiffCustomCodeTableDecoderTest VCDiffCustomCodeTableDecoderDeathTest;
+
+TEST_F(VCDiffCustomCodeTableDecoderDeathTest, BadCustomCacheSizes) {
+ delta_file_header_.assign(kFileHeader, sizeof(kFileHeader));
+ delta_file_header_.push_back(0x81); // NEAR cache size (top bit)
+ delta_file_header_.push_back(0x10); // NEAR cache size (custom value 0x90)
+ delta_file_header_.push_back(0x81); // SAME cache size (top bit)
+ delta_file_header_.push_back(0x10); // SAME cache size (custom value 0x90)
+ delta_file_header_.append(kEncodedCustomCodeTable,
+ sizeof(kEncodedCustomCodeTable));
+ InitializeDeltaFile();
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_DEBUG_DEATH(EXPECT_FALSE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_)),
+ "cache");
+ EXPECT_EQ("", output_);
+}
+
+TEST_F(VCDiffCustomCodeTableDecoderDeathTest, BadCustomCacheSizesNoVcdTarget) {
+ decoder_.SetAllowVcdTarget(false);
+ delta_file_header_.assign(kFileHeader, sizeof(kFileHeader));
+ delta_file_header_.push_back(0x81); // NEAR cache size (top bit)
+ delta_file_header_.push_back(0x10); // NEAR cache size (custom value 0x90)
+ delta_file_header_.push_back(0x81); // SAME cache size (top bit)
+ delta_file_header_.push_back(0x10); // SAME cache size (custom value 0x90)
+ delta_file_header_.append(kEncodedCustomCodeTable,
+ sizeof(kEncodedCustomCodeTable));
+ InitializeDeltaFile();
+ decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
+ EXPECT_DEBUG_DEATH(EXPECT_FALSE(decoder_.DecodeChunk(delta_file_.data(),
+ delta_file_.size(),
+ &output_)),
+ "cache");
+ EXPECT_EQ("", output_);
+}
+
+#endif // GTEST_HAS_DEATH_TEST
+
+} // namespace open_vcdiff
+} // unnamed namespace