// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "quiche/http2/hpack/huffman/hpack_huffman_encoder.h"

#include "absl/base/macros.h"
#include "absl/strings/escaping.h"
#include "quiche/common/platform/api/quiche_test.h"

namespace http2 {
namespace {

class HuffmanEncoderTest : public quiche::test::QuicheTestWithParam<bool> {
 protected:
  HuffmanEncoderTest() : use_fast_encoder_(GetParam()) {}
  virtual ~HuffmanEncoderTest() = default;

  void Encode(absl::string_view input, size_t encoded_size,
              std::string* output) {
    use_fast_encoder_ ? HuffmanEncodeFast(input, encoded_size, output)
                      : HuffmanEncode(input, encoded_size, output);
  }

  const bool use_fast_encoder_;
};

INSTANTIATE_TEST_SUITE_P(TwoEncoders, HuffmanEncoderTest, ::testing::Bool());

TEST_P(HuffmanEncoderTest, Empty) {
  std::string empty("");
  size_t encoded_size = HuffmanSize(empty);
  EXPECT_EQ(0u, encoded_size);

  std::string buffer;
  Encode(empty, encoded_size, &buffer);
  EXPECT_EQ("", buffer);
}

TEST_P(HuffmanEncoderTest, SpecRequestExamples) {
  std::string test_table[] = {
      absl::HexStringToBytes("f1e3c2e5f23a6ba0ab90f4ff"),
      "www.example.com",
      absl::HexStringToBytes("a8eb10649cbf"),
      "no-cache",
      absl::HexStringToBytes("25a849e95ba97d7f"),
      "custom-key",
      absl::HexStringToBytes("25a849e95bb8e8b4bf"),
      "custom-value",
  };
  for (size_t i = 0; i != ABSL_ARRAYSIZE(test_table); i += 2) {
    const std::string& huffman_encoded(test_table[i]);
    const std::string& plain_string(test_table[i + 1]);
    size_t encoded_size = HuffmanSize(plain_string);
    EXPECT_EQ(huffman_encoded.size(), encoded_size);
    std::string buffer;
    buffer.reserve(huffman_encoded.size());
    Encode(plain_string, encoded_size, &buffer);
    EXPECT_EQ(buffer, huffman_encoded) << "Error encoding " << plain_string;
  }
}

TEST_P(HuffmanEncoderTest, SpecResponseExamples) {
  // clang-format off
  std::string test_table[] = {
    absl::HexStringToBytes("6402"),
    "302",
    absl::HexStringToBytes("aec3771a4b"),
    "private",
    absl::HexStringToBytes("d07abe941054d444a8200595040b8166"
            "e082a62d1bff"),
    "Mon, 21 Oct 2013 20:13:21 GMT",
    absl::HexStringToBytes("9d29ad171863c78f0b97c8e9ae82ae43"
            "d3"),
    "https://www.example.com",
    absl::HexStringToBytes("94e7821dd7f2e6c7b335dfdfcd5b3960"
            "d5af27087f3672c1ab270fb5291f9587"
            "316065c003ed4ee5b1063d5007"),
    "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
  };
  // clang-format on
  for (size_t i = 0; i != ABSL_ARRAYSIZE(test_table); i += 2) {
    const std::string& huffman_encoded(test_table[i]);
    const std::string& plain_string(test_table[i + 1]);
    size_t encoded_size = HuffmanSize(plain_string);
    EXPECT_EQ(huffman_encoded.size(), encoded_size);
    std::string buffer;
    Encode(plain_string, encoded_size, &buffer);
    EXPECT_EQ(buffer, huffman_encoded) << "Error encoding " << plain_string;
  }
}

TEST_P(HuffmanEncoderTest, EncodedSizeAgreesWithEncodeString) {
  std::string test_table[] = {
      "",
      "Mon, 21 Oct 2013 20:13:21 GMT",
      "https://www.example.com",
      "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
      std::string(1, '\0'),
      std::string("foo\0bar", 7),
      std::string(256, '\0'),
  };
  // Modify last |test_table| entry to cover all codes.
  for (size_t i = 0; i != 256; ++i) {
    test_table[ABSL_ARRAYSIZE(test_table) - 1][i] = static_cast<char>(i);
  }

  for (size_t i = 0; i != ABSL_ARRAYSIZE(test_table); ++i) {
    const std::string& plain_string = test_table[i];
    size_t encoded_size = HuffmanSize(plain_string);
    std::string huffman_encoded;
    Encode(plain_string, encoded_size, &huffman_encoded);
    EXPECT_EQ(encoded_size, huffman_encoded.size());
  }
}

// Test that encoding appends to output without overwriting it.
TEST_P(HuffmanEncoderTest, AppendToOutput) {
  size_t encoded_size = HuffmanSize("foo");
  std::string buffer;
  Encode("foo", encoded_size, &buffer);
  EXPECT_EQ(absl::HexStringToBytes("94e7"), buffer);

  encoded_size = HuffmanSize("bar");
  Encode("bar", encoded_size, &buffer);
  EXPECT_EQ(absl::HexStringToBytes("94e78c767f"), buffer);
}

}  // namespace
}  // namespace http2
