| Index: net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc
 | 
| diff --git a/net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc b/net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..3ad47351b54be3ee72a12e876ca323676f5096b0
 | 
| --- /dev/null
 | 
| +++ b/net/http2/hpack/huffman/http2_hpack_huffman_decoder_test.cc
 | 
| @@ -0,0 +1,292 @@
 | 
| +// Copyright 2016 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 "net/http2/hpack/huffman/http2_hpack_huffman_decoder.h"
 | 
| +
 | 
| +// Tests of HpackHuffmanDecoder and HuffmanBitBuffer.
 | 
| +
 | 
| +#include <iostream>
 | 
| +#include <string>
 | 
| +
 | 
| +#include "base/bind.h"
 | 
| +#include "base/bind_helpers.h"
 | 
| +#include "base/macros.h"
 | 
| +#include "base/strings/string_piece.h"
 | 
| +#include "net/http2/decoder/decode_buffer.h"
 | 
| +#include "net/http2/decoder/decode_status.h"
 | 
| +#include "net/http2/tools/failure.h"
 | 
| +#include "net/http2/tools/random_decoder_test.h"
 | 
| +#include "net/spdy/spdy_test_utils.h"
 | 
| +#include "testing/gtest/include/gtest/gtest.h"
 | 
| +
 | 
| +using ::testing::AssertionResult;
 | 
| +using ::testing::AssertionSuccess;
 | 
| +using base::StringPiece;
 | 
| +using std::string;
 | 
| +
 | 
| +namespace net {
 | 
| +namespace test {
 | 
| +namespace {
 | 
| +
 | 
| +TEST(HuffmanBitBufferTest, Reset) {
 | 
| +  HuffmanBitBuffer bb;
 | 
| +  EXPECT_TRUE(bb.IsEmpty());
 | 
| +  EXPECT_TRUE(bb.InputProperlyTerminated());
 | 
| +  EXPECT_EQ(bb.count(), 0u);
 | 
| +  EXPECT_EQ(bb.free_count(), 64u);
 | 
| +  EXPECT_EQ(bb.value(), 0u);
 | 
| +}
 | 
| +
 | 
| +TEST(HuffmanBitBufferTest, AppendBytesAligned) {
 | 
| +  string s;
 | 
| +  s.push_back('\x11');
 | 
| +  s.push_back('\x22');
 | 
| +  s.push_back('\x33');
 | 
| +  StringPiece sp(s);
 | 
| +
 | 
| +  HuffmanBitBuffer bb;
 | 
| +  sp.remove_prefix(bb.AppendBytes(sp));
 | 
| +  EXPECT_TRUE(sp.empty());
 | 
| +  EXPECT_FALSE(bb.IsEmpty()) << bb;
 | 
| +  EXPECT_FALSE(bb.InputProperlyTerminated());
 | 
| +  EXPECT_EQ(bb.count(), 24u) << bb;
 | 
| +  EXPECT_EQ(bb.free_count(), 40u) << bb;
 | 
| +  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x112233) << 40) << bb;
 | 
| +
 | 
| +  s.clear();
 | 
| +  s.push_back('\x44');
 | 
| +  sp = s;
 | 
| +
 | 
| +  sp.remove_prefix(bb.AppendBytes(sp));
 | 
| +  EXPECT_TRUE(sp.empty());
 | 
| +  EXPECT_EQ(bb.count(), 32u) << bb;
 | 
| +  EXPECT_EQ(bb.free_count(), 32u) << bb;
 | 
| +  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x11223344) << 32) << bb;
 | 
| +
 | 
| +  s.clear();
 | 
| +  s.push_back('\x55');
 | 
| +  s.push_back('\x66');
 | 
| +  s.push_back('\x77');
 | 
| +  s.push_back('\x88');
 | 
| +  s.push_back('\x99');
 | 
| +  sp = s;
 | 
| +
 | 
| +  sp.remove_prefix(bb.AppendBytes(sp));
 | 
| +  EXPECT_EQ(sp.size(), 1u);
 | 
| +  EXPECT_EQ('\x99', sp[0]);
 | 
| +  EXPECT_EQ(bb.count(), 64u) << bb;
 | 
| +  EXPECT_EQ(bb.free_count(), 0u) << bb;
 | 
| +  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x1122334455667788LL)) << bb;
 | 
| +
 | 
| +  sp.remove_prefix(bb.AppendBytes(sp));
 | 
| +  EXPECT_EQ(sp.size(), 1u);
 | 
| +  EXPECT_EQ('\x99', sp[0]);
 | 
| +  EXPECT_EQ(bb.count(), 64u) << bb;
 | 
| +  EXPECT_EQ(bb.free_count(), 0u) << bb;
 | 
| +  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x1122334455667788LL)) << bb;
 | 
| +}
 | 
| +
 | 
| +TEST(HuffmanBitBufferTest, ConsumeBits) {
 | 
| +  string s;
 | 
| +  s.push_back('\x11');
 | 
| +  s.push_back('\x22');
 | 
| +  s.push_back('\x33');
 | 
| +  StringPiece sp(s);
 | 
| +
 | 
| +  HuffmanBitBuffer bb;
 | 
| +  sp.remove_prefix(bb.AppendBytes(sp));
 | 
| +  EXPECT_TRUE(sp.empty());
 | 
| +
 | 
| +  bb.ConsumeBits(1);
 | 
| +  EXPECT_EQ(bb.count(), 23u) << bb;
 | 
| +  EXPECT_EQ(bb.free_count(), 41u) << bb;
 | 
| +  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x112233) << 41) << bb;
 | 
| +
 | 
| +  bb.ConsumeBits(20);
 | 
| +  EXPECT_EQ(bb.count(), 3u) << bb;
 | 
| +  EXPECT_EQ(bb.free_count(), 61u) << bb;
 | 
| +  EXPECT_EQ(bb.value(), HuffmanAccumulator(0x3) << 61) << bb;
 | 
| +}
 | 
| +
 | 
| +TEST(HuffmanBitBufferTest, AppendBytesUnaligned) {
 | 
| +  string s;
 | 
| +  s.push_back('\x11');
 | 
| +  s.push_back('\x22');
 | 
| +  s.push_back('\x33');
 | 
| +  s.push_back('\x44');
 | 
| +  s.push_back('\x55');
 | 
| +  s.push_back('\x66');
 | 
| +  s.push_back('\x77');
 | 
| +  s.push_back('\x88');
 | 
| +  s.push_back('\x99');
 | 
| +  s.push_back('\xaa');
 | 
| +  s.push_back('\xbb');
 | 
| +  s.push_back('\xcc');
 | 
| +  s.push_back('\xdd');
 | 
| +  StringPiece sp(s);
 | 
| +
 | 
| +  HuffmanBitBuffer bb;
 | 
| +  sp.remove_prefix(bb.AppendBytes(sp));
 | 
| +  EXPECT_EQ(sp.size(), 5u);
 | 
| +  EXPECT_FALSE(bb.InputProperlyTerminated());
 | 
| +
 | 
| +  bb.ConsumeBits(15);
 | 
| +  EXPECT_EQ(bb.count(), 49u) << bb;
 | 
| +  EXPECT_EQ(bb.free_count(), 15u) << bb;
 | 
| +
 | 
| +  HuffmanAccumulator expected(0x1122334455667788);
 | 
| +  expected <<= 15;
 | 
| +  EXPECT_EQ(bb.value(), expected);
 | 
| +
 | 
| +  sp.remove_prefix(bb.AppendBytes(sp));
 | 
| +  EXPECT_EQ(sp.size(), 4u);
 | 
| +  EXPECT_EQ(bb.count(), 57u) << bb;
 | 
| +  EXPECT_EQ(bb.free_count(), 7u) << bb;
 | 
| +
 | 
| +  expected |= (HuffmanAccumulator(0x99) << 7);
 | 
| +  EXPECT_EQ(bb.value(), expected) << bb << std::hex
 | 
| +                                  << "\n   actual: " << bb.value()
 | 
| +                                  << "\n expected: " << expected;
 | 
| +}
 | 
| +
 | 
| +enum class DecoderChoice { IF_TREE, SHORT_CODE };
 | 
| +
 | 
| +class HpackHuffmanDecoderTest
 | 
| +    : public RandomDecoderTest,
 | 
| +      public ::testing::WithParamInterface<DecoderChoice> {
 | 
| + protected:
 | 
| +  HpackHuffmanDecoderTest() {
 | 
| +    // The decoder may return true, and its accumulator may be empty, at
 | 
| +    // many boundaries while decoding, and yet the whole string hasn't
 | 
| +    // been decoded.
 | 
| +    stop_decode_on_done_ = false;
 | 
| +  }
 | 
| +
 | 
| +  DecodeStatus StartDecoding(DecodeBuffer* b) override {
 | 
| +    input_bytes_seen_ = 0;
 | 
| +    output_buffer_.clear();
 | 
| +    decoder_.Reset();
 | 
| +    return ResumeDecoding(b);
 | 
| +  }
 | 
| +
 | 
| +  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
 | 
| +    input_bytes_seen_ += b->Remaining();
 | 
| +    StringPiece sp(b->cursor(), b->Remaining());
 | 
| +    if (DecodeFragment(sp)) {
 | 
| +      b->AdvanceCursor(b->Remaining());
 | 
| +      // Successfully decoded (or buffered) the bytes in StringPiece.
 | 
| +      EXPECT_LE(input_bytes_seen_, input_bytes_expected_);
 | 
| +      // Have we reached the end of the encoded string?
 | 
| +      if (input_bytes_expected_ == input_bytes_seen_) {
 | 
| +        if (decoder_.InputProperlyTerminated()) {
 | 
| +          return DecodeStatus::kDecodeDone;
 | 
| +        } else {
 | 
| +          return DecodeStatus::kDecodeError;
 | 
| +        }
 | 
| +      }
 | 
| +      return DecodeStatus::kDecodeInProgress;
 | 
| +    }
 | 
| +    return DecodeStatus::kDecodeError;
 | 
| +  }
 | 
| +
 | 
| +  bool DecodeFragment(StringPiece sp) {
 | 
| +    switch (GetParam()) {
 | 
| +      case DecoderChoice::IF_TREE:
 | 
| +        return decoder_.DecodeWithIfTreeAndStruct(sp, &output_buffer_);
 | 
| +      case DecoderChoice::SHORT_CODE:
 | 
| +        return decoder_.DecodeShortCodesFirst(sp, &output_buffer_);
 | 
| +    }
 | 
| +
 | 
| +    NOTREACHED();
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  AssertionResult ValidatorForHuffmanDecodeAndValidateSeveralWays(
 | 
| +      StringPiece expected_plain) {
 | 
| +    VERIFY_EQ(output_buffer_.size(), expected_plain.size());
 | 
| +    VERIFY_EQ(output_buffer_, expected_plain);
 | 
| +    return AssertionSuccess();
 | 
| +  }
 | 
| +
 | 
| +  AssertionResult HuffmanDecodeAndValidateSeveralWays(
 | 
| +      StringPiece encoded,
 | 
| +      StringPiece expected_plain) {
 | 
| +    input_bytes_expected_ = encoded.size();
 | 
| +    DecodeBuffer db(encoded);
 | 
| +    bool return_non_zero_on_first = false;
 | 
| +    return DecodeAndValidateSeveralWays(
 | 
| +        &db, return_non_zero_on_first,
 | 
| +        ValidateDoneAndEmpty(
 | 
| +            base::Bind(&HpackHuffmanDecoderTest::
 | 
| +                           ValidatorForHuffmanDecodeAndValidateSeveralWays,
 | 
| +                       base::Unretained(this), expected_plain)));
 | 
| +  }
 | 
| +
 | 
| +  HpackHuffmanDecoder decoder_;
 | 
| +  string output_buffer_;
 | 
| +  size_t input_bytes_seen_;
 | 
| +  size_t input_bytes_expected_;
 | 
| +};
 | 
| +INSTANTIATE_TEST_CASE_P(AllDecoders,
 | 
| +                        HpackHuffmanDecoderTest,
 | 
| +                        ::testing::Values(DecoderChoice::IF_TREE,
 | 
| +                                          DecoderChoice::SHORT_CODE));
 | 
| +
 | 
| +TEST_P(HpackHuffmanDecoderTest, SpecRequestExamples) {
 | 
| +  HpackHuffmanDecoder decoder;
 | 
| +  string test_table[] = {
 | 
| +      a2b_hex("f1e3c2e5f23a6ba0ab90f4ff"),
 | 
| +      "www.example.com",
 | 
| +      a2b_hex("a8eb10649cbf"),
 | 
| +      "no-cache",
 | 
| +      a2b_hex("25a849e95ba97d7f"),
 | 
| +      "custom-key",
 | 
| +      a2b_hex("25a849e95bb8e8b4bf"),
 | 
| +      "custom-value",
 | 
| +  };
 | 
| +  for (size_t i = 0; i != arraysize(test_table); i += 2) {
 | 
| +    const string& huffman_encoded(test_table[i]);
 | 
| +    const string& plain_string(test_table[i + 1]);
 | 
| +    string buffer;
 | 
| +    decoder.Reset();
 | 
| +    EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder;
 | 
| +    EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder;
 | 
| +    EXPECT_EQ(buffer, plain_string);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +TEST_P(HpackHuffmanDecoderTest, SpecResponseExamples) {
 | 
| +  HpackHuffmanDecoder decoder;
 | 
| +  // clang-format off
 | 
| +  string test_table[] = {
 | 
| +    a2b_hex("6402"),
 | 
| +    "302",
 | 
| +    a2b_hex("aec3771a4b"),
 | 
| +    "private",
 | 
| +    a2b_hex("d07abe941054d444a8200595040b8166"
 | 
| +            "e082a62d1bff"),
 | 
| +    "Mon, 21 Oct 2013 20:13:21 GMT",
 | 
| +    a2b_hex("9d29ad171863c78f0b97c8e9ae82ae43"
 | 
| +            "d3"),
 | 
| +    "https://www.example.com",
 | 
| +    a2b_hex("94e7821dd7f2e6c7b335dfdfcd5b3960"
 | 
| +            "d5af27087f3672c1ab270fb5291f9587"
 | 
| +            "316065c003ed4ee5b1063d5007"),
 | 
| +    "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
 | 
| +  };
 | 
| +  // clang-format on
 | 
| +  for (size_t i = 0; i != arraysize(test_table); i += 2) {
 | 
| +    const string& huffman_encoded(test_table[i]);
 | 
| +    const string& plain_string(test_table[i + 1]);
 | 
| +    string buffer;
 | 
| +    decoder.Reset();
 | 
| +    EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder;
 | 
| +    EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder;
 | 
| +    EXPECT_EQ(buffer, plain_string);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +}  // namespace test
 | 
| +}  // namespace net
 | 
| 
 |