1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef QUICHE_HTTP2_TEST_TOOLS_RANDOM_DECODER_TEST_BASE_H_
6 #define QUICHE_HTTP2_TEST_TOOLS_RANDOM_DECODER_TEST_BASE_H_
7
8 // RandomDecoderTest is a base class for tests of decoding various kinds
9 // of HTTP/2 and HPACK encodings.
10
11 // TODO(jamessynge): Move more methods into .cc file.
12
13 #include <stddef.h>
14
15 #include <cstdint>
16 #include <functional>
17 #include <memory>
18 #include <type_traits>
19
20 #include "absl/strings/string_view.h"
21 #include "quiche/http2/decoder/decode_buffer.h"
22 #include "quiche/http2/decoder/decode_status.h"
23 #include "quiche/http2/test_tools/http2_random.h"
24 #include "quiche/http2/test_tools/verify_macros.h"
25 #include "quiche/common/platform/api/quiche_export.h"
26 #include "quiche/common/platform/api/quiche_logging.h"
27 #include "quiche/common/platform/api/quiche_test.h"
28
29 namespace http2 {
30 namespace test {
31
32 // Some helpers.
33
34 template <typename T, size_t N>
ToStringPiece(T (& data)[N])35 absl::string_view ToStringPiece(T (&data)[N]) {
36 return absl::string_view(reinterpret_cast<const char*>(data), N * sizeof(T));
37 }
38
39 // Overwrite the enum with some random value, probably not a valid value for
40 // the enum type, but which fits into its storage.
41 template <typename T,
42 typename E = typename std::enable_if<std::is_enum<T>::value>::type>
CorruptEnum(T * out,Http2Random * rng)43 void CorruptEnum(T* out, Http2Random* rng) {
44 // Per cppreference.com, if the destination type of a static_cast is
45 // smaller than the source type (i.e. type of r and uint32 below), the
46 // resulting value is the smallest unsigned value equal to the source value
47 // modulo 2^n, where n is the number of bits used to represent the
48 // destination type unsigned U.
49 using underlying_type_T = typename std::underlying_type<T>::type;
50 using unsigned_underlying_type_T =
51 typename std::make_unsigned<underlying_type_T>::type;
52 auto r = static_cast<unsigned_underlying_type_T>(rng->Rand32());
53 *out = static_cast<T>(r);
54 }
55
56 // Base class for tests of the ability to decode a sequence of bytes with
57 // various boundaries between the DecodeBuffers provided to the decoder.
58 class QUICHE_NO_EXPORT RandomDecoderTest : public quiche::test::QuicheTest {
59 public:
60 // SelectSize returns the size of the next DecodeBuffer to be passed to the
61 // decoder. Note that RandomDecoderTest allows that size to be zero, though
62 // some decoders can't deal with that on the first byte, hence the |first|
63 // parameter.
64 typedef std::function<size_t(bool first, size_t offset, size_t remaining)>
65 SelectSize;
66
67 // Validator returns an AssertionResult so test can do:
68 // EXPECT_THAT(DecodeAndValidate(..., validator));
69 typedef ::testing::AssertionResult AssertionResult;
70 typedef std::function<AssertionResult(const DecodeBuffer& input,
71 DecodeStatus status)>
72 Validator;
73 typedef std::function<AssertionResult()> NoArgValidator;
74
75 RandomDecoderTest();
76
77 protected:
78 // Start decoding; call allows sub-class to Reset the decoder, or deal with
79 // the first byte if that is done in a unique fashion. Might be called with
80 // a zero byte buffer.
81 virtual DecodeStatus StartDecoding(DecodeBuffer* db) = 0;
82
83 // Resume decoding of the input after a prior call to StartDecoding, and
84 // possibly many calls to ResumeDecoding.
85 virtual DecodeStatus ResumeDecoding(DecodeBuffer* db) = 0;
86
87 // Return true if a decode status of kDecodeDone indicates that
88 // decoding should stop.
89 virtual bool StopDecodeOnDone();
90
91 // Decode buffer |original| until we run out of input, or kDecodeDone is
92 // returned by the decoder AND StopDecodeOnDone() returns true. Segments
93 // (i.e. cuts up) the original DecodeBuffer into (potentially) smaller buffers
94 // by calling |select_size| to decide how large each buffer should be.
95 // We do this to test the ability to deal with arbitrary boundaries, as might
96 // happen in transport.
97 // Returns the final DecodeStatus.
98 DecodeStatus DecodeSegments(DecodeBuffer* original,
99 const SelectSize& select_size);
100
101 // Decode buffer |original| until we run out of input, or kDecodeDone is
102 // returned by the decoder AND StopDecodeOnDone() returns true. Segments
103 // (i.e. cuts up) the original DecodeBuffer into (potentially) smaller buffers
104 // by calling |select_size| to decide how large each buffer should be.
105 // We do this to test the ability to deal with arbitrary boundaries, as might
106 // happen in transport.
107 // Invokes |validator| with the final decode status and the original decode
108 // buffer, with the cursor advanced as far as has been consumed by the decoder
109 // and returns validator's result.
DecodeSegmentsAndValidate(DecodeBuffer * original,const SelectSize & select_size,const Validator & validator)110 ::testing::AssertionResult DecodeSegmentsAndValidate(
111 DecodeBuffer* original, const SelectSize& select_size,
112 const Validator& validator) {
113 DecodeStatus status = DecodeSegments(original, select_size);
114 return validator(*original, status);
115 }
116
117 // Returns a SelectSize function for fast decoding, i.e. passing all that
118 // is available to the decoder.
SelectRemaining()119 static SelectSize SelectRemaining() {
120 return [](bool /*first*/, size_t /*offset*/, size_t remaining) -> size_t {
121 return remaining;
122 };
123 }
124
125 // Returns a SelectSize function for decoding a single byte at a time.
SelectOne()126 static SelectSize SelectOne() {
127 return [](bool /*first*/, size_t /*offset*/,
128 size_t /*remaining*/) -> size_t { return 1; };
129 }
130
131 // Returns a SelectSize function for decoding a single byte at a time, where
132 // zero byte buffers are also allowed. Alternates between zero and one.
133 static SelectSize SelectZeroAndOne(bool return_non_zero_on_first);
134
135 // Returns a SelectSize function for decoding random sized segments.
136 SelectSize SelectRandom(bool return_non_zero_on_first);
137
138 // Decode |original| multiple times, with different segmentations of the
139 // decode buffer, validating after each decode, and confirming that they
140 // each decode the same amount. Returns on the first failure, else returns
141 // success.
142 AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* original,
143 bool return_non_zero_on_first,
144 const Validator& validator);
145
ToValidator(std::nullptr_t)146 static Validator ToValidator(std::nullptr_t) {
147 return [](const DecodeBuffer& /*input*/, DecodeStatus /*status*/) {
148 return ::testing::AssertionSuccess();
149 };
150 }
151
ToValidator(const Validator & validator)152 static Validator ToValidator(const Validator& validator) {
153 if (validator == nullptr) {
154 return ToValidator(nullptr);
155 }
156 return validator;
157 }
158
ToValidator(const NoArgValidator & validator)159 static Validator ToValidator(const NoArgValidator& validator) {
160 if (validator == nullptr) {
161 return ToValidator(nullptr);
162 }
163 return [validator](const DecodeBuffer& /*input*/, DecodeStatus /*status*/) {
164 return validator();
165 };
166 }
167
168 // Wraps a validator with another validator
169 // that first checks that the DecodeStatus is kDecodeDone and
170 // that the DecodeBuffer is empty.
171 // TODO(jamessynge): Replace this overload with the next, as using this method
172 // usually means that the wrapped function doesn't need to be passed the
173 // DecodeBuffer nor the DecodeStatus.
ValidateDoneAndEmpty(const Validator & wrapped)174 static Validator ValidateDoneAndEmpty(const Validator& wrapped) {
175 return [wrapped](const DecodeBuffer& input,
176 DecodeStatus status) -> AssertionResult {
177 HTTP2_VERIFY_EQ(status, DecodeStatus::kDecodeDone);
178 HTTP2_VERIFY_EQ(0u, input.Remaining()) << "\nOffset=" << input.Offset();
179 if (wrapped) {
180 return wrapped(input, status);
181 }
182 return ::testing::AssertionSuccess();
183 };
184 }
ValidateDoneAndEmpty(NoArgValidator wrapped)185 static Validator ValidateDoneAndEmpty(NoArgValidator wrapped) {
186 return [wrapped](const DecodeBuffer& input,
187 DecodeStatus status) -> AssertionResult {
188 HTTP2_VERIFY_EQ(status, DecodeStatus::kDecodeDone);
189 HTTP2_VERIFY_EQ(0u, input.Remaining()) << "\nOffset=" << input.Offset();
190 if (wrapped) {
191 return wrapped();
192 }
193 return ::testing::AssertionSuccess();
194 };
195 }
ValidateDoneAndEmpty()196 static Validator ValidateDoneAndEmpty() {
197 NoArgValidator validator;
198 return ValidateDoneAndEmpty(validator);
199 }
200
201 // Wraps a validator with another validator
202 // that first checks that the DecodeStatus is kDecodeDone and
203 // that the DecodeBuffer has the expected offset.
204 // TODO(jamessynge): Replace this overload with the next, as using this method
205 // usually means that the wrapped function doesn't need to be passed the
206 // DecodeBuffer nor the DecodeStatus.
ValidateDoneAndOffset(uint32_t offset,const Validator & wrapped)207 static Validator ValidateDoneAndOffset(uint32_t offset,
208 const Validator& wrapped) {
209 return [wrapped, offset](const DecodeBuffer& input,
210 DecodeStatus status) -> AssertionResult {
211 HTTP2_VERIFY_EQ(status, DecodeStatus::kDecodeDone);
212 HTTP2_VERIFY_EQ(offset, input.Offset())
213 << "\nRemaining=" << input.Remaining();
214 if (wrapped) {
215 return wrapped(input, status);
216 }
217 return ::testing::AssertionSuccess();
218 };
219 }
ValidateDoneAndOffset(uint32_t offset,NoArgValidator wrapped)220 static Validator ValidateDoneAndOffset(uint32_t offset,
221 NoArgValidator wrapped) {
222 return [wrapped, offset](const DecodeBuffer& input,
223 DecodeStatus status) -> AssertionResult {
224 HTTP2_VERIFY_EQ(status, DecodeStatus::kDecodeDone);
225 HTTP2_VERIFY_EQ(offset, input.Offset())
226 << "\nRemaining=" << input.Remaining();
227 if (wrapped) {
228 return wrapped();
229 }
230 return ::testing::AssertionSuccess();
231 };
232 }
ValidateDoneAndOffset(uint32_t offset)233 static Validator ValidateDoneAndOffset(uint32_t offset) {
234 NoArgValidator validator;
235 return ValidateDoneAndOffset(offset, validator);
236 }
237
238 // Expose |random_| as Http2Random so callers don't have to care about which
239 // sub-class of Http2Random is used, nor can they rely on the specific
240 // sub-class that RandomDecoderTest uses.
Random()241 Http2Random& Random() { return random_; }
RandomPtr()242 Http2Random* RandomPtr() { return &random_; }
243
244 uint32_t RandStreamId();
245
246 bool stop_decode_on_done_ = true;
247
248 private:
249 Http2Random random_;
250 };
251
252 } // namespace test
253 } // namespace http2
254
255 #endif // QUICHE_HTTP2_TEST_TOOLS_RANDOM_DECODER_TEST_BASE_H_
256