xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/http2/test_tools/random_decoder_test_base.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 <type_traits>
17 #include <utility>
18 
19 #include "absl/strings/string_view.h"
20 #include "quiche/http2/decoder/decode_buffer.h"
21 #include "quiche/http2/decoder/decode_status.h"
22 #include "quiche/http2/test_tools/http2_random.h"
23 #include "quiche/http2/test_tools/verify_macros.h"
24 #include "quiche/common/platform/api/quiche_export.h"
25 #include "quiche/common/platform/api/quiche_test.h"
26 #include "quiche/common/quiche_callbacks.h"
27 
28 namespace http2 {
29 namespace test {
30 
31 // Some helpers.
32 
33 template <typename T, size_t N>
ToStringPiece(T (& data)[N])34 absl::string_view ToStringPiece(T (&data)[N]) {
35   return absl::string_view(reinterpret_cast<const char*>(data), N * sizeof(T));
36 }
37 
38 // Overwrite the enum with some random value, probably not a valid value for
39 // the enum type, but which fits into its storage.
40 template <typename T,
41           typename E = typename std::enable_if<std::is_enum<T>::value>::type>
CorruptEnum(T * out,Http2Random * rng)42 void CorruptEnum(T* out, Http2Random* rng) {
43   // Per cppreference.com, if the destination type of a static_cast is
44   // smaller than the source type (i.e. type of r and uint32 below), the
45   // resulting value is the smallest unsigned value equal to the source value
46   // modulo 2^n, where n is the number of bits used to represent the
47   // destination type unsigned U.
48   using underlying_type_T = typename std::underlying_type<T>::type;
49   using unsigned_underlying_type_T =
50       typename std::make_unsigned<underlying_type_T>::type;
51   auto r = static_cast<unsigned_underlying_type_T>(rng->Rand32());
52   *out = static_cast<T>(r);
53 }
54 
55 // Base class for tests of the ability to decode a sequence of bytes with
56 // various boundaries between the DecodeBuffers provided to the decoder.
57 class QUICHE_NO_EXPORT RandomDecoderTest : public quiche::test::QuicheTest {
58  public:
59   // SelectSize returns the size of the next DecodeBuffer to be passed to the
60   // decoder. Note that RandomDecoderTest allows that size to be zero, though
61   // some decoders can't deal with that on the first byte, hence the |first|
62   // parameter.
63   using SelectSize = quiche::MultiUseCallback<size_t(bool first, size_t offset,
64                                                      size_t remaining)>;
65 
66   // Validator returns an AssertionResult so test can do:
67   // EXPECT_THAT(DecodeAndValidate(..., validator));
68   using AssertionResult = ::testing::AssertionResult;
69   using Validator = quiche::MultiUseCallback<AssertionResult(
70       const DecodeBuffer& input, DecodeStatus status)>;
71   using NoArgValidator = quiche::MultiUseCallback<AssertionResult()>;
72 
73   RandomDecoderTest();
74 
75  protected:
76   // Start decoding; call allows sub-class to Reset the decoder, or deal with
77   // the first byte if that is done in a unique fashion.  Might be called with
78   // a zero byte buffer.
79   virtual DecodeStatus StartDecoding(DecodeBuffer* db) = 0;
80 
81   // Resume decoding of the input after a prior call to StartDecoding, and
82   // possibly many calls to ResumeDecoding.
83   virtual DecodeStatus ResumeDecoding(DecodeBuffer* db) = 0;
84 
85   // Return true if a decode status of kDecodeDone indicates that
86   // decoding should stop.
87   virtual bool StopDecodeOnDone();
88 
89   // Decode buffer |original| until we run out of input, or kDecodeDone is
90   // returned by the decoder AND StopDecodeOnDone() returns true. Segments
91   // (i.e. cuts up) the original DecodeBuffer into (potentially) smaller buffers
92   // by calling |select_size| to decide how large each buffer should be.
93   // We do this to test the ability to deal with arbitrary boundaries, as might
94   // happen in transport.
95   // Returns the final DecodeStatus.
96   DecodeStatus DecodeSegments(DecodeBuffer* original,
97                               const SelectSize& select_size);
98 
99   // Decode buffer |original| until we run out of input, or kDecodeDone is
100   // returned by the decoder AND StopDecodeOnDone() returns true. Segments
101   // (i.e. cuts up) the original DecodeBuffer into (potentially) smaller buffers
102   // by calling |select_size| to decide how large each buffer should be.
103   // We do this to test the ability to deal with arbitrary boundaries, as might
104   // happen in transport.
105   // Invokes |validator| with the final decode status and the original decode
106   // buffer, with the cursor advanced as far as has been consumed by the decoder
107   // and returns validator's result.
DecodeSegmentsAndValidate(DecodeBuffer * original,const SelectSize & select_size,const Validator & validator)108   ::testing::AssertionResult DecodeSegmentsAndValidate(
109       DecodeBuffer* original, const SelectSize& select_size,
110       const Validator& validator) {
111     DecodeStatus status = DecodeSegments(original, select_size);
112     return validator(*original, status);
113   }
114 
115   // Returns a SelectSize function for fast decoding, i.e. passing all that
116   // is available to the decoder.
SelectRemaining()117   static SelectSize SelectRemaining() {
118     return [](bool /*first*/, size_t /*offset*/, size_t remaining) -> size_t {
119       return remaining;
120     };
121   }
122 
123   // Returns a SelectSize function for decoding a single byte at a time.
SelectOne()124   static SelectSize SelectOne() {
125     return [](bool /*first*/, size_t /*offset*/,
126               size_t /*remaining*/) -> size_t { return 1; };
127   }
128 
129   // Returns a SelectSize function for decoding a single byte at a time, where
130   // zero byte buffers are also allowed. Alternates between zero and one.
131   static SelectSize SelectZeroAndOne(bool return_non_zero_on_first);
132 
133   // Returns a SelectSize function for decoding random sized segments.
134   SelectSize SelectRandom(bool return_non_zero_on_first);
135 
136   // Decode |original| multiple times, with different segmentations of the
137   // decode buffer, validating after each decode, and confirming that they
138   // each decode the same amount. Returns on the first failure, else returns
139   // success.
140   AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* original,
141                                                bool return_non_zero_on_first,
142                                                const Validator& validator);
143 
ToValidator(std::nullptr_t)144   static Validator ToValidator(std::nullptr_t) {
145     return [](const DecodeBuffer& /*input*/, DecodeStatus /*status*/) {
146       return ::testing::AssertionSuccess();
147     };
148   }
149 
ToValidator(Validator validator)150   static Validator ToValidator(Validator validator) {
151     if (validator == nullptr) {
152       return ToValidator(nullptr);
153     }
154     return validator;
155   }
156 
ToValidator(NoArgValidator validator)157   static Validator ToValidator(NoArgValidator validator) {
158     if (validator == nullptr) {
159       return ToValidator(nullptr);
160     }
161     return [validator = std::move(validator)](const DecodeBuffer& /*input*/,
162                                               DecodeStatus /*status*/) {
163       return validator();
164     };
165   }
166 
167   // Wraps a validator with another validator
168   // that first checks that the DecodeStatus is kDecodeDone and
169   // that the DecodeBuffer is empty.
170   // TODO(jamessynge): Replace this overload with the next, as using this method
171   // usually means that the wrapped function doesn't need to be passed the
172   // DecodeBuffer nor the DecodeStatus.
ValidateDoneAndEmpty(Validator wrapped)173   static Validator ValidateDoneAndEmpty(Validator wrapped) {
174     return [wrapped = std::move(wrapped)](
175                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 = std::move(wrapped)](
187                const DecodeBuffer& input,
188                DecodeStatus status) -> AssertionResult {
189       HTTP2_VERIFY_EQ(status, DecodeStatus::kDecodeDone);
190       HTTP2_VERIFY_EQ(0u, input.Remaining()) << "\nOffset=" << input.Offset();
191       if (wrapped) {
192         return wrapped();
193       }
194       return ::testing::AssertionSuccess();
195     };
196   }
ValidateDoneAndEmpty()197   static Validator ValidateDoneAndEmpty() {
198     return ValidateDoneAndEmpty(NoArgValidator());
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,Validator wrapped)207   static Validator ValidateDoneAndOffset(uint32_t offset, Validator wrapped) {
208     return [wrapped = std::move(wrapped), offset](
209                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 = std::move(wrapped), offset](
223                const DecodeBuffer& input,
224                DecodeStatus status) -> AssertionResult {
225       HTTP2_VERIFY_EQ(status, DecodeStatus::kDecodeDone);
226       HTTP2_VERIFY_EQ(offset, input.Offset())
227           << "\nRemaining=" << input.Remaining();
228       if (wrapped) {
229         return wrapped();
230       }
231       return ::testing::AssertionSuccess();
232     };
233   }
ValidateDoneAndOffset(uint32_t offset)234   static Validator ValidateDoneAndOffset(uint32_t offset) {
235     return ValidateDoneAndOffset(offset, NoArgValidator());
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