| Index: net/http2/tools/random_decoder_test.cc
 | 
| diff --git a/net/http2/tools/random_decoder_test.cc b/net/http2/tools/random_decoder_test.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..8fd36743dff0dcdf11fe8a8161e5626c0567a39c
 | 
| --- /dev/null
 | 
| +++ b/net/http2/tools/random_decoder_test.cc
 | 
| @@ -0,0 +1,179 @@
 | 
| +// 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/tools/random_decoder_test.h"
 | 
| +
 | 
| +#include <stddef.h>
 | 
| +
 | 
| +#include <algorithm>
 | 
| +#include <memory>
 | 
| +
 | 
| +#include "base/logging.h"
 | 
| +#include "base/strings/string_number_conversions.h"
 | 
| +#include "base/strings/string_piece.h"
 | 
| +#include "net/http2/decoder/decode_buffer.h"
 | 
| +#include "net/http2/decoder/decode_status.h"
 | 
| +#include "net/http2/http2_constants.h"
 | 
| +#include "net/http2/tools/failure.h"
 | 
| +#include "testing/gtest/include/gtest/gtest.h"
 | 
| +
 | 
| +// It's rather time consuming to decode large buffers one at a time,
 | 
| +// especially with the log level cranked up. So, by default we don't do
 | 
| +// that unless explicitly requested.
 | 
| +
 | 
| +using ::testing::AssertionFailure;
 | 
| +using ::testing::AssertionResult;
 | 
| +using ::testing::AssertionSuccess;
 | 
| +using base::StringPiece;
 | 
| +
 | 
| +namespace net {
 | 
| +namespace test {
 | 
| +
 | 
| +std::string HexEncode(StringPiece s) {
 | 
| +  return base::HexEncode(s.data(), s.size());
 | 
| +}
 | 
| +
 | 
| +RandomDecoderTest::RandomDecoderTest() {}
 | 
| +
 | 
| +bool RandomDecoderTest::StopDecodeOnDone() {
 | 
| +  return stop_decode_on_done_;
 | 
| +}
 | 
| +
 | 
| +DecodeStatus RandomDecoderTest::DecodeSegments(DecodeBuffer* original,
 | 
| +                                               const SelectSize& select_size) {
 | 
| +  DecodeStatus status = DecodeStatus::kDecodeInProgress;
 | 
| +  bool first = true;
 | 
| +  VLOG(2) << "DecodeSegments: input size=" << original->Remaining();
 | 
| +  while (first || original->HasData()) {
 | 
| +    size_t remaining = original->Remaining();
 | 
| +    size_t size = std::min(
 | 
| +        remaining, select_size.Run(first, original->Offset(), remaining));
 | 
| +    DecodeBuffer db(original->cursor(), size);
 | 
| +    VLOG(2) << "Decoding " << size << " bytes of " << remaining << " remaining";
 | 
| +    if (first) {
 | 
| +      first = false;
 | 
| +      status = StartDecoding(&db);
 | 
| +    } else {
 | 
| +      status = ResumeDecoding(&db);
 | 
| +    }
 | 
| +    // A decoder MUST consume some input (if any is available), else we could
 | 
| +    // get stuck in infinite loops.
 | 
| +    if (db.Offset() == 0 && db.HasData() &&
 | 
| +        status != DecodeStatus::kDecodeError) {
 | 
| +      ADD_FAILURE() << "Decoder didn't make any progress; db.FullSize="
 | 
| +                    << db.FullSize()
 | 
| +                    << "   original.Offset=" << original->Offset();
 | 
| +      return DecodeStatus::kDecodeError;
 | 
| +    }
 | 
| +    original->AdvanceCursor(db.Offset());
 | 
| +    switch (status) {
 | 
| +      case DecodeStatus::kDecodeDone:
 | 
| +        if (original->Empty() || StopDecodeOnDone()) {
 | 
| +          return DecodeStatus::kDecodeDone;
 | 
| +        }
 | 
| +        continue;
 | 
| +      case DecodeStatus::kDecodeInProgress:
 | 
| +        continue;
 | 
| +      case DecodeStatus::kDecodeError:
 | 
| +        return DecodeStatus::kDecodeError;
 | 
| +    }
 | 
| +  }
 | 
| +  return status;
 | 
| +}
 | 
| +
 | 
| +// Decode |original| multiple times, with different segmentations, validating
 | 
| +// after each decode, returning on the first failure.
 | 
| +AssertionResult RandomDecoderTest::DecodeAndValidateSeveralWays(
 | 
| +    DecodeBuffer* original,
 | 
| +    bool return_non_zero_on_first,
 | 
| +    const Validator& validator) {
 | 
| +  const uint32_t original_remaining = original->Remaining();
 | 
| +  VLOG(1) << "DecodeAndValidateSeveralWays - Start, remaining = "
 | 
| +          << original_remaining;
 | 
| +  uint32_t first_consumed;
 | 
| +  {
 | 
| +    // Fast decode (no stopping unless decoder does so).
 | 
| +    DecodeBuffer input(original->cursor(), original_remaining);
 | 
| +    VLOG(2) << "DecodeSegmentsAndValidate with SelectRemaining";
 | 
| +    VERIFY_SUCCESS(DecodeSegmentsAndValidate(
 | 
| +        &input, base::Bind(&SelectRemaining), validator))
 | 
| +        << "\nFailed with SelectRemaining; input.Offset=" << input.Offset()
 | 
| +        << "; input.Remaining=" << input.Remaining();
 | 
| +    first_consumed = input.Offset();
 | 
| +  }
 | 
| +  if (original_remaining <= 30) {
 | 
| +    // Decode again, one byte at a time.
 | 
| +    DecodeBuffer input(original->cursor(), original_remaining);
 | 
| +    VLOG(2) << "DecodeSegmentsAndValidate with SelectOne";
 | 
| +    VERIFY_SUCCESS(
 | 
| +        DecodeSegmentsAndValidate(&input, base::Bind(&SelectOne), validator))
 | 
| +        << "\nFailed with SelectOne; input.Offset=" << input.Offset()
 | 
| +        << "; input.Remaining=" << input.Remaining();
 | 
| +    VERIFY_EQ(first_consumed, input.Offset()) << "\nFailed with SelectOne";
 | 
| +  }
 | 
| +  if (original_remaining <= 20) {
 | 
| +    // Decode again, one or zero bytes at a time.
 | 
| +    DecodeBuffer input(original->cursor(), original_remaining);
 | 
| +    VLOG(2) << "DecodeSegmentsAndValidate with SelectZeroAndOne";
 | 
| +    bool zero_next = !return_non_zero_on_first;
 | 
| +    VERIFY_SUCCESS(DecodeSegmentsAndValidate(
 | 
| +        &input, base::Bind(&SelectZeroAndOne, &zero_next), validator))
 | 
| +        << "\nFailed with SelectZeroAndOne";
 | 
| +    VERIFY_EQ(first_consumed, input.Offset())
 | 
| +        << "\nFailed with SelectZeroAndOne; input.Offset=" << input.Offset()
 | 
| +        << "; input.Remaining=" << input.Remaining();
 | 
| +  }
 | 
| +  {
 | 
| +    // Decode again, with randomly selected segment sizes.
 | 
| +    DecodeBuffer input(original->cursor(), original_remaining);
 | 
| +    VLOG(2) << "DecodeSegmentsAndValidate with SelectRandom";
 | 
| +    VERIFY_SUCCESS(DecodeSegmentsAndValidate(
 | 
| +        &input, base::Bind(&RandomDecoderTest::SelectRandom,
 | 
| +                           base::Unretained(this), return_non_zero_on_first),
 | 
| +        validator))
 | 
| +        << "\nFailed with SelectRandom; input.Offset=" << input.Offset()
 | 
| +        << "; input.Remaining=" << input.Remaining();
 | 
| +    VERIFY_EQ(first_consumed, input.Offset()) << "\nFailed with SelectRandom";
 | 
| +  }
 | 
| +  VERIFY_EQ(original_remaining, original->Remaining());
 | 
| +  original->AdvanceCursor(first_consumed);
 | 
| +  VLOG(1) << "DecodeAndValidateSeveralWays - SUCCESS";
 | 
| +  return ::testing::AssertionSuccess();
 | 
| +}
 | 
| +
 | 
| +// static
 | 
| +size_t RandomDecoderTest::SelectZeroAndOne(bool* zero_next,
 | 
| +                                           bool first,
 | 
| +                                           size_t offset,
 | 
| +                                           size_t remaining) {
 | 
| +  if (*zero_next) {
 | 
| +    *zero_next = false;
 | 
| +    return 0;
 | 
| +  } else {
 | 
| +    *zero_next = true;
 | 
| +    return 1;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +size_t RandomDecoderTest::SelectRandom(bool return_non_zero_on_first,
 | 
| +                                       bool first,
 | 
| +                                       size_t offset,
 | 
| +                                       size_t remaining) {
 | 
| +  uint32_t r = random_.Rand32();
 | 
| +  if (first && return_non_zero_on_first) {
 | 
| +    CHECK_LT(0u, remaining);
 | 
| +    if (remaining == 1) {
 | 
| +      return 1;
 | 
| +    }
 | 
| +    return 1 + (r % remaining);  // size in range [1, remaining).
 | 
| +  }
 | 
| +  return r % (remaining + 1);  // size in range [0, remaining].
 | 
| +}
 | 
| +
 | 
| +uint32_t RandomDecoderTest::RandStreamId() {
 | 
| +  return random_.Rand32() & StreamIdMask();
 | 
| +}
 | 
| +
 | 
| +}  // namespace test
 | 
| +}  // namespace net
 | 
| 
 |