blob: dfdec9e2f04f5c403574c031114aa9fffe94e70e [file] [log] [blame]
// Copyright 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/quic/core/qpack/qpack_instruction_encoder.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/quic/platform/api/quic_test.h"
namespace quic {
namespace test {
class QpackInstructionWithValuesPeer {
public:
static QpackInstructionWithValues CreateQpackInstructionWithValues(
const QpackInstruction* instruction) {
QpackInstructionWithValues instruction_with_values;
instruction_with_values.instruction_ = instruction;
return instruction_with_values;
}
static void set_s_bit(QpackInstructionWithValues* instruction_with_values,
bool s_bit) {
instruction_with_values->s_bit_ = s_bit;
}
static void set_varint(QpackInstructionWithValues* instruction_with_values,
uint64_t varint) {
instruction_with_values->varint_ = varint;
}
static void set_varint2(QpackInstructionWithValues* instruction_with_values,
uint64_t varint2) {
instruction_with_values->varint2_ = varint2;
}
static void set_name(QpackInstructionWithValues* instruction_with_values,
absl::string_view name) {
instruction_with_values->name_ = name;
}
static void set_value(QpackInstructionWithValues* instruction_with_values,
absl::string_view value) {
instruction_with_values->value_ = value;
}
};
namespace {
class QpackInstructionEncoderTest : public QuicTest {
protected:
QpackInstructionEncoderTest() : verified_position_(0) {}
~QpackInstructionEncoderTest() override = default;
// Append encoded |instruction| to |output_|.
void EncodeInstruction(
const QpackInstructionWithValues& instruction_with_values) {
encoder_.Encode(instruction_with_values, &output_);
}
// Compare substring appended to |output_| since last EncodedSegmentMatches()
// call against hex-encoded argument.
bool EncodedSegmentMatches(absl::string_view hex_encoded_expected_substring) {
auto recently_encoded =
absl::string_view(output_).substr(verified_position_);
auto expected = absl::HexStringToBytes(hex_encoded_expected_substring);
verified_position_ = output_.size();
return recently_encoded == expected;
}
private:
QpackInstructionEncoder encoder_;
std::string output_;
std::string::size_type verified_position_;
};
TEST_F(QpackInstructionEncoderTest, Varint) {
const QpackInstruction instruction{QpackInstructionOpcode{0x00, 0x80},
{{QpackInstructionFieldType::kVarint, 7}}};
auto instruction_with_values =
QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues(
&instruction);
QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 5);
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("05"));
QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 127);
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("7f00"));
}
TEST_F(QpackInstructionEncoderTest, SBitAndTwoVarint2) {
const QpackInstruction instruction{
QpackInstructionOpcode{0x80, 0xc0},
{{QpackInstructionFieldType::kSbit, 0x20},
{QpackInstructionFieldType::kVarint, 5},
{QpackInstructionFieldType::kVarint2, 8}}};
auto instruction_with_values =
QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues(
&instruction);
QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, true);
QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 5);
QpackInstructionWithValuesPeer::set_varint2(&instruction_with_values, 200);
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("a5c8"));
QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, false);
QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 31);
QpackInstructionWithValuesPeer::set_varint2(&instruction_with_values, 356);
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("9f00ff65"));
}
TEST_F(QpackInstructionEncoderTest, SBitAndVarintAndValue) {
const QpackInstruction instruction{QpackInstructionOpcode{0xc0, 0xc0},
{{QpackInstructionFieldType::kSbit, 0x20},
{QpackInstructionFieldType::kVarint, 5},
{QpackInstructionFieldType::kValue, 7}}};
auto instruction_with_values =
QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues(
&instruction);
QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, true);
QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 100);
QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "foo");
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("ff458294e7"));
QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, false);
QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 3);
QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "bar");
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("c303626172"));
}
TEST_F(QpackInstructionEncoderTest, Name) {
const QpackInstruction instruction{QpackInstructionOpcode{0xe0, 0xe0},
{{QpackInstructionFieldType::kName, 4}}};
auto instruction_with_values =
QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues(
&instruction);
QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "");
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("e0"));
QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "foo");
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("f294e7"));
QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "bar");
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("e3626172"));
}
TEST_F(QpackInstructionEncoderTest, Value) {
const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0},
{{QpackInstructionFieldType::kValue, 3}}};
auto instruction_with_values =
QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues(
&instruction);
QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "");
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("f0"));
QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "foo");
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("fa94e7"));
QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "bar");
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("f3626172"));
}
TEST_F(QpackInstructionEncoderTest, SBitAndNameAndValue) {
const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0},
{{QpackInstructionFieldType::kSbit, 0x08},
{QpackInstructionFieldType::kName, 2},
{QpackInstructionFieldType::kValue, 7}}};
auto instruction_with_values =
QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues(
&instruction);
QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, false);
QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "");
QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "");
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("f000"));
QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, true);
QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "foo");
QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "bar");
EncodeInstruction(instruction_with_values);
EXPECT_TRUE(EncodedSegmentMatches("fe94e703626172"));
}
} // namespace
} // namespace test
} // namespace quic