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 |