| Index: net/http2/tools/random_decoder_test.h
|
| diff --git a/net/http2/tools/random_decoder_test.h b/net/http2/tools/random_decoder_test.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f3ab829d8067c64dddd2fe5a507b9ea90b661b95
|
| --- /dev/null
|
| +++ b/net/http2/tools/random_decoder_test.h
|
| @@ -0,0 +1,264 @@
|
| +// 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.
|
| +
|
| +#ifndef NET_HTTP2_TOOLS_RANDOM_DECODER_TEST_H_
|
| +#define NET_HTTP2_TOOLS_RANDOM_DECODER_TEST_H_
|
| +
|
| +// RandomDecoderTest is a base class for tests of decoding various kinds
|
| +// of HTTP/2 and HPACK encodings.
|
| +
|
| +// TODO(jamessynge): Move more methods into .cc file.
|
| +
|
| +#include <stddef.h>
|
| +
|
| +#include <memory>
|
| +#include <string>
|
| +#include <type_traits>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| +#include "base/logging.h"
|
| +#include "base/strings/string_piece.h"
|
| +#include "base/template_util.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/http2_random.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace net {
|
| +namespace test {
|
| +
|
| +// Some helpers.
|
| +
|
| +template <typename T, size_t N>
|
| +base::StringPiece ToStringPiece(T (&data)[N]) {
|
| + return base::StringPiece(reinterpret_cast<const char*>(data), N * sizeof(T));
|
| +}
|
| +
|
| +// strings/hex_ascii_dump.h doesn't support StringPiece args for this case.
|
| +std::string HexEncode(base::StringPiece s);
|
| +
|
| +// Overwrite the enum with some random value, probably not a valid value for
|
| +// the enum type, but which fits into its storage.
|
| +template <typename T,
|
| + typename E = typename std::enable_if<std::is_enum<T>::value>::type>
|
| +void CorruptEnum(T* out, RandomBase* rng) {
|
| + // Per cppreference.com, if the destination type of a static_cast is
|
| + // smaller than the source type (i.e. type of r and uint32 below), the
|
| + // resulting value is the smallest unsigned value equal to the source value
|
| + // modulo 2^n, where n is the number of bits used to represent the
|
| + // destination type unsigned U.
|
| + typedef typename base::underlying_type<T>::type underlying_type_T;
|
| + typedef typename std::make_unsigned<underlying_type_T>::type
|
| + unsigned_underlying_type_T;
|
| + auto r = static_cast<unsigned_underlying_type_T>(rng->Rand32());
|
| + *out = static_cast<T>(r);
|
| +}
|
| +
|
| +// Base class for tests of the ability to decode a sequence of bytes with
|
| +// various boundaries between the DecodeBuffers provided to the decoder.
|
| +class RandomDecoderTest : public ::testing::Test {
|
| + public:
|
| + // SelectSize returns the size of the next DecodeBuffer to be passed to the
|
| + // decoder. Note that RandomDecoderTest allows that size to be zero, though
|
| + // some decoders can't deal with that on the first byte, hence the |first|
|
| + // parameter.
|
| + typedef base::Callback<size_t(bool first, size_t offset, size_t remaining)>
|
| + SelectSize;
|
| +
|
| + // Validator returns an AssertionResult so test can do:
|
| + // EXPECT_THAT(DecodeAndValidate(..., validator));
|
| + typedef ::testing::AssertionResult AssertionResult;
|
| + typedef base::Callback<AssertionResult(const DecodeBuffer& input,
|
| + DecodeStatus status)>
|
| + Validator;
|
| + typedef base::Callback<AssertionResult()> NoArgValidator;
|
| +
|
| + RandomDecoderTest();
|
| +
|
| + protected:
|
| + // TODO(jamessynge): Modify StartDecoding, etc. to (somehow) return
|
| + // AssertionResult so that the VERIFY_* methods exported from
|
| + // gunit_helpers.h can be widely used.
|
| +
|
| + // Start decoding; call allows sub-class to Reset the decoder, or deal with
|
| + // the first byte if that is done in a unique fashion. Might be called with
|
| + // a zero byte buffer.
|
| + virtual DecodeStatus StartDecoding(DecodeBuffer* db) = 0;
|
| +
|
| + // Resume decoding of the input after a prior call to StartDecoding, and
|
| + // possibly many calls to ResumeDecoding.
|
| + virtual DecodeStatus ResumeDecoding(DecodeBuffer* db) = 0;
|
| +
|
| + // Return true if a decode status of kDecodeDone indicates that
|
| + // decoding should stop.
|
| + virtual bool StopDecodeOnDone();
|
| +
|
| + // Decode buffer |original| until we run out of input, or kDecodeDone is
|
| + // returned by the decoder AND StopDecodeOnDone() returns true. Segments
|
| + // (i.e. cuts up) the original DecodeBuffer into (potentially) smaller buffers
|
| + // by calling |select_size| to decide how large each buffer should be.
|
| + // We do this to test the ability to deal with arbitrary boundaries, as might
|
| + // happen in transport.
|
| + // Returns the final DecodeStatus.
|
| + DecodeStatus DecodeSegments(DecodeBuffer* original,
|
| + const SelectSize& select_size);
|
| +
|
| + // Decode buffer |original| until we run out of input, or kDecodeDone is
|
| + // returned by the decoder AND StopDecodeOnDone() returns true. Segments
|
| + // (i.e. cuts up) the original DecodeBuffer into (potentially) smaller buffers
|
| + // by calling |select_size| to decide how large each buffer should be.
|
| + // We do this to test the ability to deal with arbitrary boundaries, as might
|
| + // happen in transport.
|
| + // Invokes |validator| with the final decode status and the original decode
|
| + // buffer, with the cursor advanced as far as has been consumed by the decoder
|
| + // and returns validator's result.
|
| + ::testing::AssertionResult DecodeSegmentsAndValidate(
|
| + DecodeBuffer* original,
|
| + const SelectSize& select_size,
|
| + const Validator& validator) {
|
| + DecodeStatus status = DecodeSegments(original, select_size);
|
| + VERIFY_AND_RETURN_SUCCESS(validator.Run(*original, status));
|
| + }
|
| +
|
| + // Returns a size for fast decoding, i.e. passing all that
|
| + // is available to the decoder.
|
| + static size_t SelectRemaining(bool first, size_t offset, size_t remaining) {
|
| + return remaining;
|
| + }
|
| +
|
| + // Returns a size for decoding a single byte at a time.
|
| + static size_t SelectOne(bool first, size_t offset, size_t remaining) {
|
| + return 1;
|
| + }
|
| +
|
| + // Returns a size for decoding a single byte at a time, where
|
| + // zero byte buffers are also allowed. Alternates between zero and one.
|
| + static size_t SelectZeroAndOne(bool* zero_next,
|
| + bool first,
|
| + size_t offset,
|
| + size_t remaining);
|
| +
|
| + // Returns a size for decoding random sized segments.
|
| + size_t SelectRandom(bool return_non_zero_on_first,
|
| + bool first,
|
| + size_t offset,
|
| + size_t remaining);
|
| +
|
| + // Decode |original| multiple times, with different segmentations of the
|
| + // decode buffer, validating after each decode, and confirming that they
|
| + // each decode the same amount. Returns on the first failure, else returns
|
| + // success.
|
| + AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* original,
|
| + bool return_non_zero_on_first,
|
| + const Validator& validator);
|
| +
|
| + static AssertionResult SucceedingValidator(const DecodeBuffer& input,
|
| + DecodeStatus status) {
|
| + return ::testing::AssertionSuccess();
|
| + }
|
| +
|
| + static Validator ToValidator(const Validator& validator) { return validator; }
|
| +
|
| + static AssertionResult RunNoArgValidator(const NoArgValidator& validator,
|
| + const DecodeBuffer& input,
|
| + DecodeStatus status) {
|
| + return validator.Run();
|
| + }
|
| +
|
| + static Validator ToValidator(const NoArgValidator& validator) {
|
| + return base::Bind(&RunNoArgValidator, validator);
|
| + }
|
| +
|
| + // Wraps a validator with another validator
|
| + // that first checks that the DecodeStatus is kDecodeDone and
|
| + // that the DecodeBuffer is empty.
|
| + // TODO(jamessynge): Replace this overload with the next, as using this method
|
| + // usually means that the wrapped function doesn't need to be passed the
|
| + // DecodeBuffer nor the DecodeStatus.
|
| + static AssertionResult ValidateDoneAndEmptyImpl(const Validator& wrapped,
|
| + const DecodeBuffer& input,
|
| + DecodeStatus status) {
|
| + VERIFY_EQ(status, DecodeStatus::kDecodeDone);
|
| + VERIFY_EQ(0u, input.Remaining()) << "\nOffset=" << input.Offset();
|
| + return wrapped.Run(input, status);
|
| + }
|
| + static Validator ValidateDoneAndEmpty(const Validator& wrapped) {
|
| + return base::Bind(&ValidateDoneAndEmptyImpl, wrapped);
|
| + }
|
| + static AssertionResult ValidateDoneAndEmptyNoArgImpl(
|
| + const NoArgValidator& wrapped,
|
| + const DecodeBuffer& input,
|
| + DecodeStatus status) {
|
| + VERIFY_EQ(status, DecodeStatus::kDecodeDone);
|
| + VERIFY_EQ(0u, input.Remaining()) << "\nOffset=" << input.Offset();
|
| + return wrapped.Run();
|
| + }
|
| + static Validator ValidateDoneAndEmpty(const NoArgValidator& wrapped) {
|
| + return base::Bind(&ValidateDoneAndEmptyNoArgImpl, wrapped);
|
| + }
|
| + static Validator ValidateDoneAndEmpty() {
|
| + return ValidateDoneAndEmpty(base::Bind(&SucceedingValidator));
|
| + }
|
| +
|
| + // Wraps a validator with another validator
|
| + // that first checks that the DecodeStatus is kDecodeDone and
|
| + // that the DecodeBuffer has the expected offset.
|
| + // TODO(jamessynge): Replace this overload with the next, as using this method
|
| + // usually means that the wrapped function doesn't need to be passed the
|
| + // DecodeBuffer nor the DecodeStatus.
|
| + static AssertionResult ValidateDoneAndOffsetImpl(uint32_t offset,
|
| + const Validator& wrapped,
|
| + const DecodeBuffer& input,
|
| + DecodeStatus status) {
|
| + VERIFY_EQ(status, DecodeStatus::kDecodeDone);
|
| + VERIFY_EQ(offset, input.Offset()) << "\nRemaining=" << input.Remaining();
|
| + return wrapped.Run(input, status);
|
| + }
|
| + static Validator ValidateDoneAndOffset(uint32_t offset,
|
| + const Validator& wrapped) {
|
| + // Make a copy of |wrapped| (by not using base::ConstRef) to avoid lifetime
|
| + // issues if this method is called with a temporary Validator.
|
| + return base::Bind(&ValidateDoneAndOffsetImpl, offset, wrapped);
|
| + }
|
| + static AssertionResult ValidateDoneAndOffsetNoArgImpl(
|
| + uint32_t offset,
|
| + const NoArgValidator& wrapped,
|
| + const DecodeBuffer& input,
|
| + DecodeStatus status) {
|
| + VERIFY_EQ(status, DecodeStatus::kDecodeDone);
|
| + VERIFY_EQ(offset, input.Offset()) << "\nRemaining=" << input.Remaining();
|
| + return wrapped.Run();
|
| + }
|
| + static Validator ValidateDoneAndOffset(uint32_t offset,
|
| + const NoArgValidator& wrapped) {
|
| + // Make a copy of |wrapped| (by not using base::ConstRef) to avoid lifetime
|
| + // issues if this method is called with a temporary Validator.
|
| + return base::Bind(&ValidateDoneAndOffsetNoArgImpl, offset, wrapped);
|
| + }
|
| + static Validator ValidateDoneAndOffset(uint32_t offset) {
|
| + // Make a copy of |wrapped| (by not using base::ConstRef) to avoid lifetime
|
| + // issues if this method is called with a temporary Validator.
|
| + return ValidateDoneAndOffset(offset, base::Bind(&SucceedingValidator));
|
| + }
|
| +
|
| + // Expose |random_| as RandomBase so callers do not have to care about which
|
| + // sub-class of RandomBase is used, nor can they rely on the specific
|
| + // sub-class that RandomDecoderTest uses.
|
| + RandomBase& Random() { return random_; }
|
| + RandomBase* RandomPtr() { return &random_; }
|
| +
|
| + uint32_t RandStreamId();
|
| +
|
| + bool stop_decode_on_done_ = true;
|
| +
|
| + private:
|
| + Http2Random random_;
|
| +};
|
| +
|
| +} // namespace test
|
| +} // namespace net
|
| +
|
| +#endif // NET_HTTP2_TOOLS_RANDOM_DECODER_TEST_H_
|
|
|