Extends the ContainsFrames matcher to work with absl::string_view. Also requires that all input data be matched by a frame expectation.

PiperOrigin-RevId: 369494888
Change-Id: I0117497858eb0aaac4a41d2849501b0bbe8105b2
diff --git a/http2/adapter/oghttp2_adapter_test.cc b/http2/adapter/oghttp2_adapter_test.cc
index 799c9bf..d5e7b1d 100644
--- a/http2/adapter/oghttp2_adapter_test.cc
+++ b/http2/adapter/oghttp2_adapter_test.cc
@@ -61,7 +61,7 @@
 
   EXPECT_THAT(
       adapter_->GetBytesToWrite(absl::nullopt),
-      ContainsFrames(
+      EqualsFrames(
           {spdy::SpdyFrameType::SETTINGS, spdy::SpdyFrameType::PRIORITY,
            spdy::SpdyFrameType::RST_STREAM, spdy::SpdyFrameType::PING,
            spdy::SpdyFrameType::GOAWAY, spdy::SpdyFrameType::WINDOW_UPDATE}));
@@ -83,8 +83,8 @@
   EXPECT_FALSE(adapter_->session().want_write());
   EXPECT_THAT(
       absl::StrCat(first_part, second_part),
-      ContainsFrames({spdy::SpdyFrameType::SETTINGS,
-                      spdy::SpdyFrameType::GOAWAY, spdy::SpdyFrameType::PING}));
+      EqualsFrames({spdy::SpdyFrameType::SETTINGS, spdy::SpdyFrameType::GOAWAY,
+                    spdy::SpdyFrameType::PING}));
 }
 
 }  // namespace
diff --git a/http2/adapter/test_utils.cc b/http2/adapter/test_utils.cc
index 9e1db99..fafdb9f 100644
--- a/http2/adapter/test_utils.cc
+++ b/http2/adapter/test_utils.cc
@@ -23,17 +23,15 @@
   return out;
 }
 
-// Custom gMock matcher, used to determine if a particular type of frame
-// is in a string. This is useful in tests where we want to show that a
-// particular control frame type is serialized for sending to the peer.
+// Custom gMock matcher, used to implement EqualsFrames().
 class SpdyControlFrameMatcher
-    : public testing::MatcherInterface<const std::string> {
+    : 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(const std::string s,
+  bool MatchAndExplain(absl::string_view s,
                        testing::MatchResultListener* listener) const override {
     spdy::SpdyFrameReader reader(s.data(), s.size());
 
@@ -43,6 +41,11 @@
         return false;
       }
     }
+    if (!reader.IsDoneReading()) {
+      size_t bytes_remaining = s.size() - reader.GetBytesConsumed();
+      *listener << "; " << bytes_remaining << " bytes left to read!";
+      return false;
+    }
     return true;
   }
 
@@ -105,13 +108,13 @@
 
 }  // namespace
 
-testing::Matcher<const std::string> ContainsFrames(
+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<const std::string> ContainsFrames(
+testing::Matcher<absl::string_view> EqualsFrames(
     std::vector<spdy::SpdyFrameType> types) {
   std::vector<std::pair<spdy::SpdyFrameType, absl::optional<size_t>>>
       types_and_lengths;
diff --git a/http2/adapter/test_utils.h b/http2/adapter/test_utils.h
index 6276bf1..41d3b6c 100644
--- a/http2/adapter/test_utils.h
+++ b/http2/adapter/test_utils.h
@@ -4,6 +4,7 @@
 #include <string>
 #include <vector>
 
+#include "absl/strings/string_view.h"
 #include "common/platform/api/quiche_test.h"
 #include "spdy/core/spdy_protocol.h"
 
@@ -11,15 +12,19 @@
 namespace adapter {
 namespace test {
 
-// Matcher that checks whether a string contains HTTP/2 frames of the specified
-// ordered sequence of types and lengths.
-testing::Matcher<const std::string> ContainsFrames(
+// These matchers check whether a string consists entirely of HTTP/2 frames of
+// the specified ordered sequence. This is useful in tests where we want to show
+// that one or more particular frame types are serialized for sending to the
+// peer. The match will fail if there are input bytes not consumed by the
+// matcher.
+
+// Requires that frames match both types and lengths.
+testing::Matcher<absl::string_view> EqualsFrames(
     std::vector<std::pair<spdy::SpdyFrameType, absl::optional<size_t>>>
         types_and_lengths);
 
-// Matcher that checks whether a string contains HTTP/2 frames of the specified
-// ordered sequence of types.
-testing::Matcher<const std::string> ContainsFrames(
+// Requires that frames match the specified types.
+testing::Matcher<absl::string_view> EqualsFrames(
     std::vector<spdy::SpdyFrameType> types);
 
 }  // namespace test
diff --git a/http2/adapter/test_utils_test.cc b/http2/adapter/test_utils_test.cc
index e8f4ebd..0de7b28 100644
--- a/http2/adapter/test_utils_test.cc
+++ b/http2/adapter/test_utils_test.cc
@@ -10,38 +10,37 @@
 
 using spdy::SpdyFramer;
 
-TEST(ContainsFrames, Empty) {
-  EXPECT_THAT("", ContainsFrames(std::vector<spdy::SpdyFrameType>{}));
+TEST(EqualsFrames, Empty) {
+  EXPECT_THAT("", EqualsFrames(std::vector<spdy::SpdyFrameType>{}));
 }
 
-TEST(ContainsFrames, SingleFrameWithLength) {
+TEST(EqualsFrames, SingleFrameWithLength) {
   SpdyFramer framer{SpdyFramer::ENABLE_COMPRESSION};
 
   spdy::SpdyPingIR ping{511};
   EXPECT_THAT(framer.SerializeFrame(ping),
-              ContainsFrames({{spdy::SpdyFrameType::PING, 8}}));
+              EqualsFrames({{spdy::SpdyFrameType::PING, 8}}));
 
   spdy::SpdyWindowUpdateIR window_update{1, 101};
   EXPECT_THAT(framer.SerializeFrame(window_update),
-              ContainsFrames({{spdy::SpdyFrameType::WINDOW_UPDATE, 4}}));
+              EqualsFrames({{spdy::SpdyFrameType::WINDOW_UPDATE, 4}}));
 
   spdy::SpdyDataIR data{3, "Some example data, ha ha!"};
   EXPECT_THAT(framer.SerializeFrame(data),
-              ContainsFrames({{spdy::SpdyFrameType::DATA, 25}}));
+              EqualsFrames({{spdy::SpdyFrameType::DATA, 25}}));
 }
 
-TEST(ContainsFrames, SingleFrameWithoutLength) {
+TEST(EqualsFrames, SingleFrameWithoutLength) {
   SpdyFramer framer{SpdyFramer::ENABLE_COMPRESSION};
 
   spdy::SpdyRstStreamIR rst_stream{7, spdy::ERROR_CODE_REFUSED_STREAM};
-  EXPECT_THAT(
-      framer.SerializeFrame(rst_stream),
-      ContainsFrames({{spdy::SpdyFrameType::RST_STREAM, absl::nullopt}}));
+  EXPECT_THAT(framer.SerializeFrame(rst_stream),
+              EqualsFrames({{spdy::SpdyFrameType::RST_STREAM, absl::nullopt}}));
 
   spdy::SpdyGoAwayIR goaway{13, spdy::ERROR_CODE_ENHANCE_YOUR_CALM,
                             "Consider taking some deep breaths."};
   EXPECT_THAT(framer.SerializeFrame(goaway),
-              ContainsFrames({{spdy::SpdyFrameType::GOAWAY, absl::nullopt}}));
+              EqualsFrames({{spdy::SpdyFrameType::GOAWAY, absl::nullopt}}));
 
   spdy::Http2HeaderBlock block;
   block[":method"] = "GET";
@@ -49,10 +48,10 @@
   block[":authority"] = "example.com";
   spdy::SpdyHeadersIR headers{17, std::move(block)};
   EXPECT_THAT(framer.SerializeFrame(headers),
-              ContainsFrames({{spdy::SpdyFrameType::HEADERS, absl::nullopt}}));
+              EqualsFrames({{spdy::SpdyFrameType::HEADERS, absl::nullopt}}));
 }
 
-TEST(ContainsFrames, MultipleFrames) {
+TEST(EqualsFrames, MultipleFrames) {
   SpdyFramer framer{SpdyFramer::ENABLE_COMPRESSION};
 
   spdy::SpdyPingIR ping{511};
@@ -74,20 +73,48 @@
                    absl::string_view(framer.SerializeFrame(rst_stream)),
                    absl::string_view(framer.SerializeFrame(goaway)),
                    absl::string_view(framer.SerializeFrame(headers)));
+  absl::string_view frame_sequence_view = frame_sequence;
+  EXPECT_THAT(frame_sequence,
+              EqualsFrames({{spdy::SpdyFrameType::PING, absl::nullopt},
+                            {spdy::SpdyFrameType::WINDOW_UPDATE, absl::nullopt},
+                            {spdy::SpdyFrameType::DATA, 25},
+                            {spdy::SpdyFrameType::RST_STREAM, absl::nullopt},
+                            {spdy::SpdyFrameType::GOAWAY, 42},
+                            {spdy::SpdyFrameType::HEADERS, 19}}));
+  EXPECT_THAT(frame_sequence_view,
+              EqualsFrames({{spdy::SpdyFrameType::PING, absl::nullopt},
+                            {spdy::SpdyFrameType::WINDOW_UPDATE, absl::nullopt},
+                            {spdy::SpdyFrameType::DATA, 25},
+                            {spdy::SpdyFrameType::RST_STREAM, absl::nullopt},
+                            {spdy::SpdyFrameType::GOAWAY, 42},
+                            {spdy::SpdyFrameType::HEADERS, 19}}));
   EXPECT_THAT(
       frame_sequence,
-      ContainsFrames({{spdy::SpdyFrameType::PING, absl::nullopt},
-                      {spdy::SpdyFrameType::WINDOW_UPDATE, absl::nullopt},
-                      {spdy::SpdyFrameType::DATA, 25},
-                      {spdy::SpdyFrameType::RST_STREAM, absl::nullopt},
-                      {spdy::SpdyFrameType::GOAWAY, 42},
-                      {spdy::SpdyFrameType::HEADERS, 19}}));
-  EXPECT_THAT(
-      frame_sequence,
-      ContainsFrames(
+      EqualsFrames(
           {spdy::SpdyFrameType::PING, spdy::SpdyFrameType::WINDOW_UPDATE,
            spdy::SpdyFrameType::DATA, spdy::SpdyFrameType::RST_STREAM,
            spdy::SpdyFrameType::GOAWAY, spdy::SpdyFrameType::HEADERS}));
+  EXPECT_THAT(
+      frame_sequence_view,
+      EqualsFrames(
+          {spdy::SpdyFrameType::PING, spdy::SpdyFrameType::WINDOW_UPDATE,
+           spdy::SpdyFrameType::DATA, spdy::SpdyFrameType::RST_STREAM,
+           spdy::SpdyFrameType::GOAWAY, spdy::SpdyFrameType::HEADERS}));
+
+  // If the final frame type is removed the expectation fails, as there are
+  // bytes left to read.
+  EXPECT_THAT(
+      frame_sequence,
+      testing::Not(EqualsFrames(
+          {spdy::SpdyFrameType::PING, spdy::SpdyFrameType::WINDOW_UPDATE,
+           spdy::SpdyFrameType::DATA, spdy::SpdyFrameType::RST_STREAM,
+           spdy::SpdyFrameType::GOAWAY})));
+  EXPECT_THAT(
+      frame_sequence_view,
+      testing::Not(EqualsFrames(
+          {spdy::SpdyFrameType::PING, spdy::SpdyFrameType::WINDOW_UPDATE,
+           spdy::SpdyFrameType::DATA, spdy::SpdyFrameType::RST_STREAM,
+           spdy::SpdyFrameType::GOAWAY})));
 }
 
 }  // namespace