Add RecordingHttp2Visitor for testing.

This CL introduces a new Http2VisitorInterface implementation that records the
sequence of events it receives. That sequence of events can then be compared
between adapter implementations.

PiperOrigin-RevId: 374210574
diff --git a/http2/adapter/adapter_impl_comparison_test.cc b/http2/adapter/adapter_impl_comparison_test.cc
new file mode 100644
index 0000000..11f03bb
--- /dev/null
+++ b/http2/adapter/adapter_impl_comparison_test.cc
@@ -0,0 +1,83 @@
+#include "http2/adapter/recording_http2_visitor.h"
+
+#include "http2/adapter/http2_protocol.h"
+#include "http2/adapter/nghttp2_adapter.h"
+#include "http2/adapter/oghttp2_adapter.h"
+#include "http2/adapter/test_frame_sequence.h"
+#include "common/platform/api/quiche_test.h"
+#include "spdy/core/spdy_protocol.h"
+
+namespace http2 {
+namespace adapter {
+namespace test {
+namespace {
+
+TEST(AdapterImplComparisonTest, ClientHandlesFrames) {
+  RecordingHttp2Visitor nghttp2_visitor;
+  std::unique_ptr<NgHttp2Adapter> nghttp2_adapter =
+      NgHttp2Adapter::CreateClientAdapter(nghttp2_visitor);
+
+  RecordingHttp2Visitor oghttp2_visitor;
+  OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
+  std::unique_ptr<OgHttp2Adapter> oghttp2_adapter =
+      OgHttp2Adapter::Create(oghttp2_visitor, options);
+
+  const std::string initial_frames = TestFrameSequence()
+                                         .ServerPreface()
+                                         .Ping(42)
+                                         .WindowUpdate(0, 1000)
+                                         .Serialize();
+
+  nghttp2_adapter->ProcessBytes(initial_frames);
+  oghttp2_adapter->ProcessBytes(initial_frames);
+
+  EXPECT_EQ(nghttp2_visitor.GetEventSequence(),
+            oghttp2_visitor.GetEventSequence());
+
+  // TODO(b/181586191): Consider consistent behavior for delivering events on
+  // non-existent streams between nghttp2_adapter and oghttp2_adapter.
+}
+
+TEST(AdapterImplComparisonTest, ServerHandlesFrames) {
+  RecordingHttp2Visitor nghttp2_visitor;
+  std::unique_ptr<NgHttp2Adapter> nghttp2_adapter =
+      NgHttp2Adapter::CreateServerAdapter(nghttp2_visitor);
+
+  RecordingHttp2Visitor oghttp2_visitor;
+  OgHttp2Adapter::Options options{.perspective = Perspective::kServer};
+  std::unique_ptr<OgHttp2Adapter> oghttp2_adapter =
+      OgHttp2Adapter::Create(oghttp2_visitor, options);
+
+  const std::string frames = TestFrameSequence()
+                                 .ClientPreface()
+                                 .Ping(42)
+                                 .WindowUpdate(0, 1000)
+                                 .Headers(1,
+                                          {{":method", "POST"},
+                                           {":scheme", "https"},
+                                           {":authority", "example.com"},
+                                           {":path", "/this/is/request/one"}},
+                                          /*fin=*/false)
+                                 .WindowUpdate(1, 2000)
+                                 .Data(1, "This is the request body.")
+                                 .Headers(3,
+                                          {{":method", "GET"},
+                                           {":scheme", "http"},
+                                           {":authority", "example.com"},
+                                           {":path", "/this/is/request/two"}},
+                                          /*fin=*/true)
+                                 .RstStream(3, Http2ErrorCode::CANCEL)
+                                 .Ping(47)
+                                 .Serialize();
+
+  nghttp2_adapter->ProcessBytes(frames);
+  oghttp2_adapter->ProcessBytes(frames);
+
+  EXPECT_EQ(nghttp2_visitor.GetEventSequence(),
+            oghttp2_visitor.GetEventSequence());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace adapter
+}  // namespace http2
diff --git a/http2/adapter/recording_http2_visitor.cc b/http2/adapter/recording_http2_visitor.cc
new file mode 100644
index 0000000..78ec423
--- /dev/null
+++ b/http2/adapter/recording_http2_visitor.cc
@@ -0,0 +1,155 @@
+#include "http2/adapter/recording_http2_visitor.h"
+
+#include "absl/strings/str_format.h"
+#include "http2/adapter/http2_protocol.h"
+
+namespace http2 {
+namespace adapter {
+namespace test {
+
+void RecordingHttp2Visitor::OnConnectionError() {
+  events_.push_back("OnConnectionError");
+}
+
+void RecordingHttp2Visitor::OnFrameHeader(Http2StreamId stream_id,
+                                          size_t length,
+                                          uint8_t type,
+                                          uint8_t flags) {
+  events_.push_back(absl::StrFormat("OnFrameHeader %d %d %d %d", stream_id,
+                                    length, type, flags));
+}
+
+void RecordingHttp2Visitor::OnSettingsStart() {
+  events_.push_back("OnSettingsStart");
+}
+
+void RecordingHttp2Visitor::OnSetting(Http2Setting setting) {
+  events_.push_back(absl::StrFormat(
+      "OnSetting %s %d", Http2SettingsIdToString(setting.id), setting.value));
+}
+
+void RecordingHttp2Visitor::OnSettingsEnd() {
+  events_.push_back("OnSettingsEnd");
+}
+
+void RecordingHttp2Visitor::OnSettingsAck() {
+  events_.push_back("OnSettingsAck");
+}
+
+void RecordingHttp2Visitor::OnBeginHeadersForStream(Http2StreamId stream_id) {
+  events_.push_back(absl::StrFormat("OnBeginHeadersForStream %d", stream_id));
+}
+
+void RecordingHttp2Visitor::OnHeaderForStream(Http2StreamId stream_id,
+                                              absl::string_view name,
+                                              absl::string_view value) {
+  events_.push_back(
+      absl::StrFormat("OnHeaderForStream %d %s %s", stream_id, name, value));
+}
+
+void RecordingHttp2Visitor::OnEndHeadersForStream(Http2StreamId stream_id) {
+  events_.push_back(absl::StrFormat("OnEndHeadersForStream %d", stream_id));
+}
+
+void RecordingHttp2Visitor::OnBeginDataForStream(Http2StreamId stream_id,
+                                                 size_t payload_length) {
+  events_.push_back(
+      absl::StrFormat("OnBeginDataForStream %d %d", stream_id, payload_length));
+}
+
+void RecordingHttp2Visitor::OnDataForStream(Http2StreamId stream_id,
+                                            absl::string_view data) {
+  events_.push_back(absl::StrFormat("OnDataForStream %d %s", stream_id, data));
+}
+
+void RecordingHttp2Visitor::OnEndStream(Http2StreamId stream_id) {
+  events_.push_back(absl::StrFormat("OnEndStream %d", stream_id));
+}
+
+void RecordingHttp2Visitor::OnRstStream(Http2StreamId stream_id,
+                                        Http2ErrorCode error_code) {
+  events_.push_back(absl::StrFormat("OnRstStream %d %s", stream_id,
+                                    Http2ErrorCodeToString(error_code)));
+}
+
+void RecordingHttp2Visitor::OnCloseStream(Http2StreamId stream_id,
+                                          Http2ErrorCode error_code) {
+  events_.push_back(absl::StrFormat("OnCloseStream %d %s", stream_id,
+                                    Http2ErrorCodeToString(error_code)));
+}
+
+void RecordingHttp2Visitor::OnPriorityForStream(Http2StreamId stream_id,
+                                                Http2StreamId parent_stream_id,
+                                                int weight,
+                                                bool exclusive) {
+  events_.push_back(absl::StrFormat("OnPriorityForStream %d %d %d %d",
+                                    stream_id, parent_stream_id, weight,
+                                    exclusive));
+}
+
+void RecordingHttp2Visitor::OnPing(Http2PingId ping_id, bool is_ack) {
+  events_.push_back(absl::StrFormat("OnPing %d %d", ping_id, is_ack));
+}
+
+void RecordingHttp2Visitor::OnPushPromiseForStream(
+    Http2StreamId stream_id,
+    Http2StreamId promised_stream_id) {
+  events_.push_back(absl::StrFormat("OnPushPromiseForStream %d %d", stream_id,
+                                    promised_stream_id));
+}
+
+void RecordingHttp2Visitor::OnGoAway(Http2StreamId last_accepted_stream_id,
+                                     Http2ErrorCode error_code,
+                                     absl::string_view opaque_data) {
+  events_.push_back(
+      absl::StrFormat("OnGoAway %d %s %s", last_accepted_stream_id,
+                      Http2ErrorCodeToString(error_code), opaque_data));
+}
+
+void RecordingHttp2Visitor::OnWindowUpdate(Http2StreamId stream_id,
+                                           int window_increment) {
+  events_.push_back(
+      absl::StrFormat("OnWindowUpdate %d %d", stream_id, window_increment));
+}
+
+void RecordingHttp2Visitor::OnReadyToSendDataForStream(Http2StreamId stream_id,
+                                                       char* destination_buffer,
+                                                       size_t length,
+                                                       ssize_t* written,
+                                                       bool* end_stream) {
+  // TODO(b/181586191): Revisit this. The visitor is expected to write to the
+  // |destination_buffer| and set the other pointer values appropriately.
+  events_.push_back(
+      absl::StrFormat("OnReadyToSendDataForStream %d %d", stream_id, length));
+}
+
+void RecordingHttp2Visitor::OnReadyToSendMetadataForStream(
+    Http2StreamId stream_id,
+    char* buffer,
+    size_t length,
+    ssize_t* written) {
+  // TODO(b/181586191): Revisit this. The visitor is expected to write to the
+  // |buffer| and set *written appropriately.
+  events_.push_back(absl::StrFormat("OnReadyToSendMetadataForStream %d %d",
+                                    stream_id, length));
+}
+
+void RecordingHttp2Visitor::OnBeginMetadataForStream(Http2StreamId stream_id,
+                                                     size_t payload_length) {
+  events_.push_back(absl::StrFormat("OnBeginMetadataForStream %d %d", stream_id,
+                                    payload_length));
+}
+
+void RecordingHttp2Visitor::OnMetadataForStream(Http2StreamId stream_id,
+                                                absl::string_view metadata) {
+  events_.push_back(
+      absl::StrFormat("OnMetadataForStream %d %s", stream_id, metadata));
+}
+
+void RecordingHttp2Visitor::OnMetadataEndForStream(Http2StreamId stream_id) {
+  events_.push_back(absl::StrFormat("OnMetadataEndForStream %d", stream_id));
+}
+
+}  // namespace test
+}  // namespace adapter
+}  // namespace http2
diff --git a/http2/adapter/recording_http2_visitor.h b/http2/adapter/recording_http2_visitor.h
new file mode 100644
index 0000000..452b451
--- /dev/null
+++ b/http2/adapter/recording_http2_visitor.h
@@ -0,0 +1,80 @@
+#ifndef QUICHE_HTTP2_ADAPTER_RECORDING_HTTP2_VISITOR_H_
+#define QUICHE_HTTP2_ADAPTER_RECORDING_HTTP2_VISITOR_H_
+
+#include <list>
+#include <string>
+
+#include "http2/adapter/http2_visitor_interface.h"
+#include "common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace adapter {
+namespace test {
+
+// A visitor implementation that records the sequence of callbacks it receives.
+class RecordingHttp2Visitor : public Http2VisitorInterface {
+ public:
+  using Event = std::string;
+  using EventSequence = std::list<Event>;
+
+  // From Http2VisitorInterface
+  void OnConnectionError() override;
+  void OnFrameHeader(Http2StreamId stream_id,
+                     size_t length,
+                     uint8_t type,
+                     uint8_t flags) override;
+  void OnSettingsStart() override;
+  void OnSetting(Http2Setting setting) override;
+  void OnSettingsEnd() override;
+  void OnSettingsAck() override;
+  void OnBeginHeadersForStream(Http2StreamId stream_id) override;
+  void OnHeaderForStream(Http2StreamId stream_id,
+                         absl::string_view name,
+                         absl::string_view value) override;
+  void OnEndHeadersForStream(Http2StreamId stream_id) override;
+  void OnBeginDataForStream(Http2StreamId stream_id,
+                            size_t payload_length) override;
+  void OnDataForStream(Http2StreamId stream_id,
+                       absl::string_view data) override;
+  void OnEndStream(Http2StreamId stream_id) override;
+  void OnRstStream(Http2StreamId stream_id, Http2ErrorCode error_code) override;
+  void OnCloseStream(Http2StreamId stream_id,
+                     Http2ErrorCode error_code) override;
+  void OnPriorityForStream(Http2StreamId stream_id,
+                           Http2StreamId parent_stream_id,
+                           int weight,
+                           bool exclusive) override;
+  void OnPing(Http2PingId ping_id, bool is_ack) override;
+  void OnPushPromiseForStream(Http2StreamId stream_id,
+                              Http2StreamId promised_stream_id) override;
+  void OnGoAway(Http2StreamId last_accepted_stream_id,
+                Http2ErrorCode error_code,
+                absl::string_view opaque_data) override;
+  void OnWindowUpdate(Http2StreamId stream_id, int window_increment) override;
+  void OnReadyToSendDataForStream(Http2StreamId stream_id,
+                                  char* destination_buffer,
+                                  size_t length,
+                                  ssize_t* written,
+                                  bool* end_stream) override;
+  void OnReadyToSendMetadataForStream(Http2StreamId stream_id,
+                                      char* buffer,
+                                      size_t length,
+                                      ssize_t* written) override;
+  void OnBeginMetadataForStream(Http2StreamId stream_id,
+                                size_t payload_length) override;
+  void OnMetadataForStream(Http2StreamId stream_id,
+                           absl::string_view metadata) override;
+  void OnMetadataEndForStream(Http2StreamId stream_id) override;
+
+  const EventSequence& GetEventSequence() const { return events_; }
+  void Clear() { events_.clear(); }
+
+ private:
+  EventSequence events_;
+};
+
+}  // namespace test
+}  // namespace adapter
+}  // namespace http2
+
+#endif  // QUICHE_HTTP2_ADAPTER_RECORDING_HTTP2_VISITOR_H_
diff --git a/http2/adapter/recording_http2_visitor_test.cc b/http2/adapter/recording_http2_visitor_test.cc
new file mode 100644
index 0000000..8f278d7
--- /dev/null
+++ b/http2/adapter/recording_http2_visitor_test.cc
@@ -0,0 +1,135 @@
+#include "http2/adapter/recording_http2_visitor.h"
+
+#include <list>
+
+#include "http2/adapter/http2_protocol.h"
+#include "http2/test_tools/http2_random.h"
+#include "common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace adapter {
+namespace test {
+namespace {
+
+using ::testing::IsEmpty;
+
+TEST(RecordingHttp2VisitorTest, EmptySequence) {
+  RecordingHttp2Visitor chocolate_visitor;
+  RecordingHttp2Visitor vanilla_visitor;
+
+  EXPECT_THAT(chocolate_visitor.GetEventSequence(), IsEmpty());
+  EXPECT_THAT(vanilla_visitor.GetEventSequence(), IsEmpty());
+  EXPECT_EQ(chocolate_visitor.GetEventSequence(),
+            vanilla_visitor.GetEventSequence());
+
+  chocolate_visitor.OnSettingsStart();
+
+  EXPECT_THAT(chocolate_visitor.GetEventSequence(), testing::Not(IsEmpty()));
+  EXPECT_THAT(vanilla_visitor.GetEventSequence(), IsEmpty());
+  EXPECT_NE(chocolate_visitor.GetEventSequence(),
+            vanilla_visitor.GetEventSequence());
+
+  chocolate_visitor.Clear();
+
+  EXPECT_THAT(chocolate_visitor.GetEventSequence(), IsEmpty());
+  EXPECT_THAT(vanilla_visitor.GetEventSequence(), IsEmpty());
+  EXPECT_EQ(chocolate_visitor.GetEventSequence(),
+            vanilla_visitor.GetEventSequence());
+}
+
+TEST(RecordingHttp2VisitorTest, SameEventsProduceSameSequence) {
+  RecordingHttp2Visitor chocolate_visitor;
+  RecordingHttp2Visitor vanilla_visitor;
+
+  // Prepare some random values to deliver with the events.
+  http2::test::Http2Random random;
+  const Http2StreamId stream_id = random.Uniform(kMaxStreamId);
+  const Http2StreamId another_stream_id = random.Uniform(kMaxStreamId);
+  const size_t length = random.Rand16();
+  const uint8_t type = random.Rand8();
+  const uint8_t flags = random.Rand8();
+  const Http2ErrorCode error_code = static_cast<Http2ErrorCode>(
+      random.Uniform(static_cast<int>(Http2ErrorCode::MAX_ERROR_CODE)));
+  const Http2Setting setting = {.id = random.Rand16(),
+                                .value = random.Rand32()};
+  const absl::string_view alphabet = "abcdefghijklmnopqrstuvwxyz0123456789-";
+  const std::string some_string =
+      random.RandStringWithAlphabet(random.Rand8(), alphabet);
+  const std::string another_string =
+      random.RandStringWithAlphabet(random.Rand8(), alphabet);
+  const uint16_t some_int = random.Rand16();
+  const bool some_bool = random.OneIn(2);
+
+  // Send the same arbitrary sequence of events to both visitors.
+  std::list<RecordingHttp2Visitor*> visitors = {&chocolate_visitor,
+                                                &vanilla_visitor};
+  for (RecordingHttp2Visitor* visitor : visitors) {
+    visitor->OnConnectionError();
+    visitor->OnFrameHeader(stream_id, length, type, flags);
+    visitor->OnSettingsStart();
+    visitor->OnSetting(setting);
+    visitor->OnSettingsEnd();
+    visitor->OnSettingsAck();
+    visitor->OnBeginHeadersForStream(stream_id);
+    visitor->OnHeaderForStream(stream_id, some_string, another_string);
+    visitor->OnEndHeadersForStream(stream_id);
+    visitor->OnBeginDataForStream(stream_id, length);
+    visitor->OnDataForStream(stream_id, some_string);
+    visitor->OnDataForStream(stream_id, another_string);
+    visitor->OnEndStream(stream_id);
+    visitor->OnRstStream(stream_id, error_code);
+    visitor->OnCloseStream(stream_id, error_code);
+    visitor->OnPriorityForStream(stream_id, another_stream_id, some_int,
+                                 some_bool);
+    visitor->OnPing(some_int, some_bool);
+    visitor->OnPushPromiseForStream(stream_id, another_stream_id);
+    visitor->OnGoAway(stream_id, error_code, some_string);
+    visitor->OnWindowUpdate(stream_id, some_int);
+    visitor->OnReadyToSendDataForStream(
+        stream_id, /*destination_buffer=*/nullptr, length, /*written=*/nullptr,
+        /*end_stream=*/nullptr);
+    visitor->OnReadyToSendMetadataForStream(stream_id, /*buffer=*/nullptr,
+                                            length, /*written=*/nullptr);
+    visitor->OnBeginMetadataForStream(stream_id, length);
+    visitor->OnMetadataForStream(stream_id, some_string);
+    visitor->OnMetadataForStream(stream_id, another_string);
+    visitor->OnMetadataEndForStream(stream_id);
+  }
+
+  EXPECT_EQ(chocolate_visitor.GetEventSequence(),
+            vanilla_visitor.GetEventSequence());
+}
+
+TEST(RecordingHttp2VisitorTest, DifferentEventsProduceDifferentSequence) {
+  RecordingHttp2Visitor chocolate_visitor;
+  RecordingHttp2Visitor vanilla_visitor;
+  EXPECT_EQ(chocolate_visitor.GetEventSequence(),
+            vanilla_visitor.GetEventSequence());
+
+  const Http2StreamId stream_id = 1;
+  const size_t length = 42;
+
+  // Different events with the same method arguments should produce different
+  // event sequences.
+  chocolate_visitor.OnBeginDataForStream(stream_id, length);
+  vanilla_visitor.OnBeginMetadataForStream(stream_id, length);
+  EXPECT_NE(chocolate_visitor.GetEventSequence(),
+            vanilla_visitor.GetEventSequence());
+
+  chocolate_visitor.Clear();
+  vanilla_visitor.Clear();
+  EXPECT_EQ(chocolate_visitor.GetEventSequence(),
+            vanilla_visitor.GetEventSequence());
+
+  // The same events with different method arguments should produce different
+  // event sequences.
+  chocolate_visitor.OnBeginHeadersForStream(stream_id);
+  vanilla_visitor.OnBeginHeadersForStream(stream_id + 2);
+  EXPECT_NE(chocolate_visitor.GetEventSequence(),
+            vanilla_visitor.GetEventSequence());
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace adapter
+}  // namespace http2