// 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"

namespace open_vcdiff {
namespace {


// Decode an encoding that uses a RUN instruction to allocate 64MB.
class VCDiffLargeTargetTest : public VCDiffDecoderTest {
 protected:
  VCDiffLargeTargetTest();
  virtual ~VCDiffLargeTargetTest() {}

  static const char kLargeRunWindow[];
};

const char VCDiffLargeTargetTest::kLargeRunWindow[] = {
    0x00,  // Win_Indicator: no source segment
    0x0E,  // Length of the delta encoding
    0xA0,  // Size of the target window (0x4000000)
    0x80,  // Size of the target window cont'd
    0x80,  // Size of the target window cont'd
    0x00,  // Size of the target window cont'd
    0x00,  // Delta_indicator (no compression)
    0x00,  // length of data for ADDs and RUNs
    0x06,  // length of instructions section
    0x00,  // length of addresses for COPYs
    // Interleaved segment
    0x00,  // VCD_RUN size 0
    0xA0,  // Size of RUN (0x4000000)
    0x80,  // Size of RUN cont'd
    0x80,  // Size of RUN cont'd
    0x00,  // Size of RUN cont'd
    0xBE,  // Data for RUN
};

VCDiffLargeTargetTest::VCDiffLargeTargetTest() {
  UseInterleavedFileHeader();
}

// Ensure that, with allow_vcd_target set to false, we can decode any number of
// 64MB windows without running out of memory.
TEST_F(VCDiffLargeTargetTest, Decode) {
  // 50 x 64MB = 3.2GB, which should be too large if memory usage accumulates
  // during each iteration.
  const int kIterations = 50;
  decoder_.SetAllowVcdTarget(false);
  decoder_.SetMaximumTargetFileSize(0x4000000UL * 50);
  decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
  EXPECT_TRUE(decoder_.DecodeChunk(delta_file_header_.data(),
                                   delta_file_header_.size(),
                                   &output_));
  EXPECT_EQ("", output_);
  for (int i = 0; i < kIterations; i++) {
    EXPECT_TRUE(decoder_.DecodeChunk(kLargeRunWindow, sizeof(kLargeRunWindow),
                                     &output_));
    EXPECT_EQ(0x4000000U, output_.size());
    EXPECT_EQ(static_cast<char>(0xBE), output_[0]);
    EXPECT_EQ(static_cast<char>(0xBE),
              output_[output_.size() / 2]);  // middle element
    EXPECT_EQ(static_cast<char>(0xBE),
              output_[output_.size() - 1]);  // last element
    output_.clear();
  }
  EXPECT_TRUE(decoder_.FinishDecoding());
}

// If we don't increase the maximum target file size first, the same test should
// produce an error.
TEST_F(VCDiffLargeTargetTest, DecodeReachesMaxFileSize) {
  decoder_.SetAllowVcdTarget(false);
  decoder_.StartDecoding(dictionary_.data(), dictionary_.size());
  EXPECT_TRUE(decoder_.DecodeChunk(delta_file_header_.data(),
                                   delta_file_header_.size(),
                                   &output_));
  EXPECT_EQ("", output_);
  // The default maximum target file size is 64MB, which just matches the target
  // data produced by a single iteration.
  EXPECT_TRUE(decoder_.DecodeChunk(kLargeRunWindow, sizeof(kLargeRunWindow),
                                   &output_));
  EXPECT_EQ(0x4000000U, output_.size());
  EXPECT_EQ(static_cast<char>(0xBE), output_[0]);
  EXPECT_EQ(static_cast<char>(0xBE),
            output_[output_.size() / 2]);  // middle element
  EXPECT_EQ(static_cast<char>(0xBE),
            output_[output_.size() - 1]);  // last element
  output_.clear();
  // Trying to decode a second window should exceed the target file size limit.
  EXPECT_FALSE(decoder_.DecodeChunk(kLargeRunWindow, sizeof(kLargeRunWindow),
                                    &output_));
}

}  // unnamed namespace
}  // namespace open_vcdiff
