// 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_.c_str(), 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_.c_str(), 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_.c_str(), 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_.c_str(), 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_.c_str(), 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_.c_str(),
            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).c_str(), 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_.c_str(), 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_.c_str(),
                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_.c_str(), 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_.c_str(), 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_.c_str(), 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_.c_str(), 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).c_str(), 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_.c_str(), 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_.c_str(), 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_.c_str(),
                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_.c_str(), 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).c_str(), 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_.c_str(), 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_).c_str(), 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_.c_str(), 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_.c_str(), 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_.c_str(), 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_.c_str(), 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_.c_str(), 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_.c_str(), 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_.c_str(), 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_.c_str(), 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_.c_str(), 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