summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quiche/http2/adapter/test_utils.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/third_party/quiche/src/quiche/http2/adapter/test_utils.cc')
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/adapter/test_utils.cc224
1 files changed, 224 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/adapter/test_utils.cc b/chromium/net/third_party/quiche/src/quiche/http2/adapter/test_utils.cc
new file mode 100644
index 00000000000..5d6d427378c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/adapter/test_utils.cc
@@ -0,0 +1,224 @@
+#include "quiche/http2/adapter/test_utils.h"
+
+#include <ostream>
+
+#include "absl/strings/str_format.h"
+#include "quiche/http2/adapter/http2_visitor_interface.h"
+#include "quiche/common/quiche_data_reader.h"
+#include "quiche/spdy/core/hpack/hpack_encoder.h"
+
+namespace http2 {
+namespace adapter {
+namespace test {
+namespace {
+
+using ConnectionError = Http2VisitorInterface::ConnectionError;
+
+} // anonymous namespace
+
+TestDataFrameSource::TestDataFrameSource(Http2VisitorInterface& visitor,
+ bool has_fin)
+ : visitor_(visitor), has_fin_(has_fin) {}
+
+void TestDataFrameSource::AppendPayload(absl::string_view payload) {
+ QUICHE_CHECK(!end_data_);
+ if (!payload.empty()) {
+ payload_fragments_.push_back(std::string(payload));
+ current_fragment_ = payload_fragments_.front();
+ }
+}
+
+void TestDataFrameSource::EndData() { end_data_ = true; }
+
+std::pair<int64_t, bool> TestDataFrameSource::SelectPayloadLength(
+ size_t max_length) {
+ if (return_error_) {
+ return {DataFrameSource::kError, false};
+ }
+ // The stream is done if there's no more data, or if |max_length| is at least
+ // as large as the remaining data.
+ const bool end_data = end_data_ && (current_fragment_.empty() ||
+ (payload_fragments_.size() == 1 &&
+ max_length >= current_fragment_.size()));
+ const int64_t length = std::min(max_length, current_fragment_.size());
+ return {length, end_data};
+}
+
+bool TestDataFrameSource::Send(absl::string_view frame_header,
+ size_t payload_length) {
+ QUICHE_LOG_IF(DFATAL, payload_length > current_fragment_.size())
+ << "payload_length: " << payload_length
+ << " current_fragment_size: " << current_fragment_.size();
+ const std::string concatenated =
+ absl::StrCat(frame_header, current_fragment_.substr(0, payload_length));
+ const int64_t result = visitor_.OnReadyToSend(concatenated);
+ if (result < 0) {
+ // Write encountered error.
+ visitor_.OnConnectionError(ConnectionError::kSendError);
+ current_fragment_ = {};
+ payload_fragments_.clear();
+ return false;
+ } else if (result == 0) {
+ // Write blocked.
+ return false;
+ } else if (static_cast<const size_t>(result) < concatenated.size()) {
+ // Probably need to handle this better within this test class.
+ QUICHE_LOG(DFATAL)
+ << "DATA frame not fully flushed. Connection will be corrupt!";
+ visitor_.OnConnectionError(ConnectionError::kSendError);
+ current_fragment_ = {};
+ payload_fragments_.clear();
+ return false;
+ }
+ if (payload_length > 0) {
+ current_fragment_.remove_prefix(payload_length);
+ }
+ if (current_fragment_.empty() && !payload_fragments_.empty()) {
+ payload_fragments_.erase(payload_fragments_.begin());
+ if (!payload_fragments_.empty()) {
+ current_fragment_ = payload_fragments_.front();
+ }
+ }
+ return true;
+}
+
+std::string EncodeHeaders(const spdy::SpdyHeaderBlock& entries) {
+ spdy::HpackEncoder encoder;
+ encoder.DisableCompression();
+ return encoder.EncodeHeaderBlock(entries);
+}
+
+TestMetadataSource::TestMetadataSource(const spdy::SpdyHeaderBlock& entries)
+ : encoded_entries_(EncodeHeaders(entries)) {
+ remaining_ = encoded_entries_;
+}
+
+std::pair<int64_t, bool> TestMetadataSource::Pack(uint8_t* dest,
+ size_t dest_len) {
+ const size_t copied = std::min(dest_len, remaining_.size());
+ std::memcpy(dest, remaining_.data(), copied);
+ remaining_.remove_prefix(copied);
+ return std::make_pair(copied, remaining_.empty());
+}
+
+namespace {
+
+using TypeAndOptionalLength =
+ std::pair<spdy::SpdyFrameType, absl::optional<size_t>>;
+
+std::ostream& operator<<(
+ std::ostream& os,
+ const std::vector<TypeAndOptionalLength>& types_and_lengths) {
+ for (const auto& type_and_length : types_and_lengths) {
+ os << "(" << spdy::FrameTypeToString(type_and_length.first) << ", "
+ << (type_and_length.second ? absl::StrCat(type_and_length.second.value())
+ : "<unspecified>")
+ << ") ";
+ }
+ return os;
+}
+
+std::string FrameTypeToString(uint8_t frame_type) {
+ if (spdy::IsDefinedFrameType(frame_type)) {
+ return spdy::FrameTypeToString(spdy::ParseFrameType(frame_type));
+ } else {
+ return absl::StrFormat("0x%x", static_cast<int>(frame_type));
+ }
+}
+
+// Custom gMock matcher, used to implement EqualsFrames().
+class SpdyControlFrameMatcher
+ : public testing::MatcherInterface<absl::string_view> {
+ public:
+ explicit SpdyControlFrameMatcher(
+ std::vector<TypeAndOptionalLength> types_and_lengths)
+ : expected_types_and_lengths_(std::move(types_and_lengths)) {}
+
+ bool MatchAndExplain(absl::string_view s,
+ testing::MatchResultListener* listener) const override {
+ quiche::QuicheDataReader reader(s.data(), s.size());
+
+ for (TypeAndOptionalLength expected : expected_types_and_lengths_) {
+ if (!MatchAndExplainOneFrame(expected.first, expected.second, &reader,
+ listener)) {
+ return false;
+ }
+ }
+ if (!reader.IsDoneReading()) {
+ *listener << "; " << reader.BytesRemaining() << " bytes left to read!";
+ return false;
+ }
+ return true;
+ }
+
+ bool MatchAndExplainOneFrame(spdy::SpdyFrameType expected_type,
+ absl::optional<size_t> expected_length,
+ quiche::QuicheDataReader* reader,
+ testing::MatchResultListener* listener) const {
+ uint32_t payload_length;
+ if (!reader->ReadUInt24(&payload_length)) {
+ *listener << "; unable to read length field for expected_type "
+ << FrameTypeToString(expected_type) << ". data too short!";
+ return false;
+ }
+
+ if (expected_length && payload_length != expected_length.value()) {
+ *listener << "; actual length: " << payload_length
+ << " but expected length: " << expected_length.value();
+ return false;
+ }
+
+ uint8_t raw_type;
+ if (!reader->ReadUInt8(&raw_type)) {
+ *listener << "; unable to read type field for expected_type "
+ << FrameTypeToString(expected_type) << ". data too short!";
+ return false;
+ }
+
+ if (raw_type != static_cast<uint8_t>(expected_type)) {
+ *listener << "; actual type: " << FrameTypeToString(raw_type)
+ << " but expected type: " << FrameTypeToString(expected_type);
+ return false;
+ }
+
+ // Seek past flags (1B), stream ID (4B), and payload. Reach the next frame.
+ reader->Seek(5 + payload_length);
+ return true;
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ *os << "Data contains frames of types in sequence "
+ << expected_types_and_lengths_;
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ *os << "Data does not contain frames of types in sequence "
+ << expected_types_and_lengths_;
+ }
+
+ private:
+ const std::vector<TypeAndOptionalLength> expected_types_and_lengths_;
+};
+
+} // namespace
+
+testing::Matcher<absl::string_view> EqualsFrames(
+ std::vector<std::pair<spdy::SpdyFrameType, absl::optional<size_t>>>
+ types_and_lengths) {
+ return MakeMatcher(new SpdyControlFrameMatcher(std::move(types_and_lengths)));
+}
+
+testing::Matcher<absl::string_view> EqualsFrames(
+ std::vector<spdy::SpdyFrameType> types) {
+ std::vector<std::pair<spdy::SpdyFrameType, absl::optional<size_t>>>
+ types_and_lengths;
+ types_and_lengths.reserve(types.size());
+ for (spdy::SpdyFrameType type : types) {
+ types_and_lengths.push_back({type, absl::nullopt});
+ }
+ return MakeMatcher(new SpdyControlFrameMatcher(std::move(types_and_lengths)));
+}
+
+} // namespace test
+} // namespace adapter
+} // namespace http2