xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/http2/test_tools/random_decoder_test_base.cc (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 #include "quiche/http2/test_tools/random_decoder_test_base.h"
6 
7 #include <stddef.h>
8 
9 #include <algorithm>
10 #include <memory>
11 
12 #include "quiche/http2/decoder/decode_buffer.h"
13 #include "quiche/http2/decoder/decode_status.h"
14 #include "quiche/http2/http2_constants.h"
15 #include "quiche/http2/test_tools/verify_macros.h"
16 #include "quiche/common/platform/api/quiche_logging.h"
17 #include "quiche/common/platform/api/quiche_test.h"
18 
19 using ::testing::AssertionResult;
20 
21 namespace http2 {
22 namespace test {
23 
24 RandomDecoderTest::RandomDecoderTest() = default;
25 
StopDecodeOnDone()26 bool RandomDecoderTest::StopDecodeOnDone() { return stop_decode_on_done_; }
27 
DecodeSegments(DecodeBuffer * original,const SelectSize & select_size)28 DecodeStatus RandomDecoderTest::DecodeSegments(DecodeBuffer* original,
29                                                const SelectSize& select_size) {
30   DecodeStatus status = DecodeStatus::kDecodeInProgress;
31   bool first = true;
32   QUICHE_VLOG(2) << "DecodeSegments: input size=" << original->Remaining();
33   while (first || original->HasData()) {
34     size_t remaining = original->Remaining();
35     size_t size =
36         std::min(remaining, select_size(first, original->Offset(), remaining));
37     DecodeBuffer db(original->cursor(), size);
38     QUICHE_VLOG(2) << "Decoding " << size << " bytes of " << remaining
39                    << " remaining";
40     if (first) {
41       first = false;
42       status = StartDecoding(&db);
43     } else {
44       status = ResumeDecoding(&db);
45     }
46     // A decoder MUST consume some input (if any is available), else we could
47     // get stuck in infinite loops.
48     if (db.Offset() == 0 && db.HasData() &&
49         status != DecodeStatus::kDecodeError) {
50       ADD_FAILURE() << "Decoder didn't make any progress; db.FullSize="
51                     << db.FullSize()
52                     << "   original.Offset=" << original->Offset();
53       return DecodeStatus::kDecodeError;
54     }
55     original->AdvanceCursor(db.Offset());
56     switch (status) {
57       case DecodeStatus::kDecodeDone:
58         if (original->Empty() || StopDecodeOnDone()) {
59           return DecodeStatus::kDecodeDone;
60         }
61         continue;
62       case DecodeStatus::kDecodeInProgress:
63         continue;
64       case DecodeStatus::kDecodeError:
65         return DecodeStatus::kDecodeError;
66     }
67   }
68   return status;
69 }
70 
71 // Decode |original| multiple times, with different segmentations, validating
72 // after each decode, returning on the first failure.
DecodeAndValidateSeveralWays(DecodeBuffer * original,bool return_non_zero_on_first,const Validator & validator)73 AssertionResult RandomDecoderTest::DecodeAndValidateSeveralWays(
74     DecodeBuffer* original, bool return_non_zero_on_first,
75     const Validator& validator) {
76   const uint32_t original_remaining = original->Remaining();
77   QUICHE_VLOG(1) << "DecodeAndValidateSeveralWays - Start, remaining = "
78                  << original_remaining;
79   uint32_t first_consumed;
80   {
81     // Fast decode (no stopping unless decoder does so).
82     DecodeBuffer input(original->cursor(), original_remaining);
83     QUICHE_VLOG(2) << "DecodeSegmentsAndValidate with SelectRemaining";
84     HTTP2_VERIFY_SUCCESS(
85         DecodeSegmentsAndValidate(&input, SelectRemaining(), validator))
86         << "\nFailed with SelectRemaining; input.Offset=" << input.Offset()
87         << "; input.Remaining=" << input.Remaining();
88     first_consumed = input.Offset();
89   }
90   if (original_remaining <= 30) {
91     // Decode again, one byte at a time.
92     DecodeBuffer input(original->cursor(), original_remaining);
93     QUICHE_VLOG(2) << "DecodeSegmentsAndValidate with SelectOne";
94     HTTP2_VERIFY_SUCCESS(
95         DecodeSegmentsAndValidate(&input, SelectOne(), validator))
96         << "\nFailed with SelectOne; input.Offset=" << input.Offset()
97         << "; input.Remaining=" << input.Remaining();
98     HTTP2_VERIFY_EQ(first_consumed, input.Offset())
99         << "\nFailed with SelectOne";
100   }
101   if (original_remaining <= 20) {
102     // Decode again, one or zero bytes at a time.
103     DecodeBuffer input(original->cursor(), original_remaining);
104     QUICHE_VLOG(2) << "DecodeSegmentsAndValidate with SelectZeroAndOne";
105     HTTP2_VERIFY_SUCCESS(DecodeSegmentsAndValidate(
106         &input, SelectZeroAndOne(return_non_zero_on_first), validator))
107         << "\nFailed with SelectZeroAndOne";
108     HTTP2_VERIFY_EQ(first_consumed, input.Offset())
109         << "\nFailed with SelectZeroAndOne; input.Offset=" << input.Offset()
110         << "; input.Remaining=" << input.Remaining();
111   }
112   {
113     // Decode again, with randomly selected segment sizes.
114     DecodeBuffer input(original->cursor(), original_remaining);
115     QUICHE_VLOG(2) << "DecodeSegmentsAndValidate with SelectRandom";
116     HTTP2_VERIFY_SUCCESS(DecodeSegmentsAndValidate(
117         &input, SelectRandom(return_non_zero_on_first), validator))
118         << "\nFailed with SelectRandom; input.Offset=" << input.Offset()
119         << "; input.Remaining=" << input.Remaining();
120     HTTP2_VERIFY_EQ(first_consumed, input.Offset())
121         << "\nFailed with SelectRandom";
122   }
123   HTTP2_VERIFY_EQ(original_remaining, original->Remaining());
124   original->AdvanceCursor(first_consumed);
125   QUICHE_VLOG(1) << "DecodeAndValidateSeveralWays - SUCCESS";
126   return ::testing::AssertionSuccess();
127 }
128 
129 // static
SelectZeroAndOne(bool return_non_zero_on_first)130 RandomDecoderTest::SelectSize RandomDecoderTest::SelectZeroAndOne(
131     bool return_non_zero_on_first) {
132   std::shared_ptr<bool> zero_next(new bool);
133   *zero_next = !return_non_zero_on_first;
134   return [zero_next](bool /*first*/, size_t /*offset*/,
135                      size_t /*remaining*/) -> size_t {
136     if (*zero_next) {
137       *zero_next = false;
138       return 0;
139     } else {
140       *zero_next = true;
141       return 1;
142     }
143   };
144 }
145 
SelectRandom(bool return_non_zero_on_first)146 RandomDecoderTest::SelectSize RandomDecoderTest::SelectRandom(
147     bool return_non_zero_on_first) {
148   return [this, return_non_zero_on_first](bool first, size_t /*offset*/,
149                                           size_t remaining) -> size_t {
150     uint32_t r = random_.Rand32();
151     if (first && return_non_zero_on_first) {
152       QUICHE_CHECK_LT(0u, remaining);
153       if (remaining == 1) {
154         return 1;
155       }
156       return 1 + (r % remaining);  // size in range [1, remaining).
157     }
158     return r % (remaining + 1);  // size in range [0, remaining].
159   };
160 }
161 
RandStreamId()162 uint32_t RandomDecoderTest::RandStreamId() {
163   return random_.Rand32() & StreamIdMask();
164 }
165 
166 }  // namespace test
167 }  // namespace http2
168