// 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 "headerparser.h"
#include <stdlib.h>  // rand, srand
#include <string>
#include <vector>
#include "testing.h"
#include "varint_bigendian.h"

namespace open_vcdiff {
namespace {  // anonymous

using std::vector;

class VCDiffHeaderParserTest : public testing::Test {
 protected:
  typedef std::string string;

  static const int kTestSize = 1024;

  VCDiffHeaderParserTest() : parser(NULL) { }

  virtual ~VCDiffHeaderParserTest() {
    delete parser;
  }

  virtual void SetUp() {
    srand(1);  // make sure each test uses the same data set
  }

  void StartParsing() {
    parser = new VCDiffHeaderParser(
        encoded_buffer_.data(),
        encoded_buffer_.data() + encoded_buffer_.size());
    EXPECT_EQ(encoded_buffer_.data(), parser->UnparsedData());
  }

  void VerifyByte(unsigned char expected_value) {
    unsigned char decoded_byte = 0;
    const char* prior_position = parser->UnparsedData();
    EXPECT_TRUE(parser->ParseByte(&decoded_byte));
    EXPECT_EQ(expected_value, decoded_byte);
    EXPECT_EQ(RESULT_SUCCESS, parser->GetResult());
    EXPECT_EQ(prior_position + sizeof(unsigned char),
              parser->UnparsedData());
  }

  void VerifyInt32(int32_t expected_value) {
    int32_t decoded_integer = 0;
    const char* prior_position = parser->UnparsedData();
    EXPECT_TRUE(parser->ParseInt32("decoded int32", &decoded_integer));
    EXPECT_EQ(expected_value, decoded_integer);
    EXPECT_EQ(RESULT_SUCCESS, parser->GetResult());
    EXPECT_EQ(prior_position + VarintBE<int32_t>::Length(decoded_integer),
              parser->UnparsedData());
  }

  void VerifyUInt32(uint32_t expected_value) {
    uint32_t decoded_integer = 0;
    const char* prior_position = parser->UnparsedData();
    EXPECT_TRUE(parser->ParseUInt32("decoded uint32", &decoded_integer));
    EXPECT_EQ(expected_value, decoded_integer);
    EXPECT_EQ(RESULT_SUCCESS, parser->GetResult());
    EXPECT_EQ(prior_position + VarintBE<int64_t>::Length(decoded_integer),
              parser->UnparsedData());
  }

  void VerifyChecksum(VCDChecksum expected_value) {
    VCDChecksum decoded_checksum = 0;
    const char* prior_position = parser->UnparsedData();
    EXPECT_TRUE(parser->ParseChecksum("decoded checksum", &decoded_checksum));
    EXPECT_EQ(expected_value, decoded_checksum);
    EXPECT_EQ(RESULT_SUCCESS, parser->GetResult());
    EXPECT_EQ(prior_position + VarintBE<int64_t>::Length(decoded_checksum),
              parser->UnparsedData());
  }

  string encoded_buffer_;
  VCDiffHeaderParser* parser;
};

TEST_F(VCDiffHeaderParserTest, ParseRandomBytes) {
  vector<unsigned char> byte_values;
  for (int i = 0; i < kTestSize; ++i) {
    unsigned char random_byte = PortableRandomInRange<unsigned char>(0xFF);
    encoded_buffer_.push_back(random_byte);
    byte_values.push_back(random_byte);
  }
  StartParsing();
  for (int position = 0; position < kTestSize; ++position) {
    VerifyByte(byte_values[position]);
  }
  unsigned char decoded_byte = 0;
  EXPECT_FALSE(parser->ParseByte(&decoded_byte));
  EXPECT_EQ(RESULT_END_OF_DATA, parser->GetResult());
  EXPECT_EQ(encoded_buffer_.data() + encoded_buffer_.size(),
            parser->UnparsedData());
}

TEST_F(VCDiffHeaderParserTest, ParseRandomInt32) {
  vector<int32_t> integer_values;
  for (int i = 0; i < kTestSize; ++i) {
    int32_t random_integer = PortableRandomInRange<int32_t>(0x7FFFFFFF);
    VarintBE<int32_t>::AppendToString(random_integer, &encoded_buffer_);
    integer_values.push_back(random_integer);
  }
  StartParsing();
  for (int i = 0; i < kTestSize; ++i) {
    VerifyInt32(integer_values[i]);
  }
  int32_t decoded_integer = 0;
  EXPECT_FALSE(parser->ParseInt32("decoded integer", &decoded_integer));
  EXPECT_EQ(RESULT_END_OF_DATA, parser->GetResult());
  EXPECT_EQ(encoded_buffer_.data() + encoded_buffer_.size(),
            parser->UnparsedData());
}

TEST_F(VCDiffHeaderParserTest, ParseRandomUInt32) {
  vector<uint32_t> integer_values;
  for (int i = 0; i < kTestSize; ++i) {
    uint32_t random_integer = PortableRandomInRange<uint32_t>(0xFFFFFFFF);
    VarintBE<int64_t>::AppendToString(random_integer, &encoded_buffer_);
    integer_values.push_back(random_integer);
  }
  StartParsing();
  uint32_t decoded_integer = 0;
  for (int i = 0; i < kTestSize; ++i) {
    VerifyUInt32(integer_values[i]);
  }
  EXPECT_FALSE(parser->ParseUInt32("decoded integer", &decoded_integer));
  EXPECT_EQ(RESULT_END_OF_DATA, parser->GetResult());
  EXPECT_EQ(encoded_buffer_.data() + encoded_buffer_.size(),
            parser->UnparsedData());
}

TEST_F(VCDiffHeaderParserTest, ParseRandomChecksum) {
  vector<VCDChecksum> checksum_values;
  for (int i = 0; i < kTestSize; ++i) {
    VCDChecksum random_checksum =
        PortableRandomInRange<VCDChecksum>(0xFFFFFFFF);
    VarintBE<int64_t>::AppendToString(random_checksum, &encoded_buffer_);
    checksum_values.push_back(random_checksum);
  }
  StartParsing();
  for (int i = 0; i < kTestSize; ++i) {
    VerifyChecksum(checksum_values[i]);
  }
  VCDChecksum decoded_checksum = 0;
  EXPECT_FALSE(parser->ParseChecksum("decoded checksum", &decoded_checksum));
  EXPECT_EQ(RESULT_END_OF_DATA, parser->GetResult());
  EXPECT_EQ(encoded_buffer_.data() + encoded_buffer_.size(),
            parser->UnparsedData());
}

TEST_F(VCDiffHeaderParserTest, ParseMixed) {
  VarintBE<int64_t>::AppendToString(0xCAFECAFE, &encoded_buffer_);
  encoded_buffer_.push_back(0xFF);
  VarintBE<int32_t>::AppendToString(0x02020202, &encoded_buffer_);
  VarintBE<int64_t>::AppendToString(0xCAFECAFE, &encoded_buffer_);
  encoded_buffer_.push_back(0xFF);
  encoded_buffer_.push_back(0xFF);
  StartParsing();
  VerifyUInt32(0xCAFECAFE);
  VerifyByte(0xFF);
  VerifyInt32(0x02020202);
  VerifyChecksum(0xCAFECAFE);
  int32_t incomplete_int32 = 0;
  EXPECT_FALSE(parser->ParseInt32("incomplete Varint", &incomplete_int32));
  EXPECT_EQ(0, incomplete_int32);
  EXPECT_EQ(RESULT_END_OF_DATA, parser->GetResult());
  EXPECT_EQ(encoded_buffer_.data() + encoded_buffer_.size() - 2,
            parser->UnparsedData());
}

TEST_F(VCDiffHeaderParserTest, ParseInvalidVarint) {
  // Start with a byte that has the continuation bit plus a high-order bit set
  encoded_buffer_.append(1, static_cast<char>(0xC0));
  // Add too many bytes with continuation bits
  encoded_buffer_.append(6, static_cast<char>(0x80));
  StartParsing();
  int32_t invalid_int32 = 0;
  EXPECT_FALSE(parser->ParseInt32("invalid Varint", &invalid_int32));
  EXPECT_EQ(0, invalid_int32);
  EXPECT_EQ(RESULT_ERROR, parser->GetResult());
  EXPECT_EQ(encoded_buffer_.data(), parser->UnparsedData());
  // After the parse failure, any other call to Parse... should return an error,
  // even though there is still a byte that could be read as valid.
  unsigned char decoded_byte = 0;
  EXPECT_FALSE(parser->ParseByte(&decoded_byte));
  EXPECT_EQ(0, decoded_byte);
  EXPECT_EQ(RESULT_ERROR, parser->GetResult());
  EXPECT_EQ(encoded_buffer_.data(), parser->UnparsedData());
}

}  //  namespace open_vcdiff
}  //  anonymous namespace