Add some of the basic status matchers for QUICHE.

Status matchers are commonly used inside google3, but the open-source version of Abseil/gtest does not ship those.  This CL contains the most basic ones (IsOk, IsOkAndHolds and two-argument version of StatusIs).

Since we are using Status/StatusOr more widely these days, this will be helpful for writing unit tests and porting the existing ones.

PiperOrigin-RevId: 502628290
diff --git a/build/source_list.bzl b/build/source_list.bzl
index 0db8800..91be1a0 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -1048,6 +1048,7 @@
     "common/simple_buffer_allocator_test.cc",
     "common/structured_headers_generated_test.cc",
     "common/structured_headers_test.cc",
+    "common/test_tools/quiche_test_utils_test.cc",
     "http2/adapter/event_forwarder_test.cc",
     "http2/adapter/header_validator_test.cc",
     "http2/adapter/noop_header_validator_test.cc",
diff --git a/build/source_list.gni b/build/source_list.gni
index 62962e7..965802a 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -1048,6 +1048,7 @@
     "src/quiche/common/simple_buffer_allocator_test.cc",
     "src/quiche/common/structured_headers_generated_test.cc",
     "src/quiche/common/structured_headers_test.cc",
+    "src/quiche/common/test_tools/quiche_test_utils_test.cc",
     "src/quiche/http2/adapter/event_forwarder_test.cc",
     "src/quiche/http2/adapter/header_validator_test.cc",
     "src/quiche/http2/adapter/noop_header_validator_test.cc",
diff --git a/build/source_list.json b/build/source_list.json
index e7cde56..a79092f 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -1047,6 +1047,7 @@
     "quiche/common/simple_buffer_allocator_test.cc",
     "quiche/common/structured_headers_generated_test.cc",
     "quiche/common/structured_headers_test.cc",
+    "quiche/common/test_tools/quiche_test_utils_test.cc",
     "quiche/http2/adapter/event_forwarder_test.cc",
     "quiche/http2/adapter/header_validator_test.cc",
     "quiche/http2/adapter/noop_header_validator_test.cc",
diff --git a/quiche/common/test_tools/quiche_test_utils.h b/quiche/common/test_tools/quiche_test_utils.h
index c233051..d35ed7f 100644
--- a/quiche/common/test_tools/quiche_test_utils.h
+++ b/quiche/common/test_tools/quiche_test_utils.h
@@ -7,6 +7,8 @@
 
 #include <string>
 
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
 #include "absl/strings/string_view.h"
 #include "quiche/common/platform/api/quiche_iovec.h"
 #include "quiche/common/platform/api/quiche_test.h"
@@ -27,16 +29,44 @@
 // This function checks if IDNAs are supported.
 bool GoogleUrlSupportsIdnaForTest();
 
+// Takes either a Status or StatusOr<T>, and returns just the Status.
+inline const absl::Status& ExtractStatus(const absl::Status& status) {
+  return status;
+}
+template <typename T>
+const absl::Status& ExtractStatus(const absl::StatusOr<T>& status_or) {
+  return status_or.status();
+}
+
 // Abseil does not provide absl::Status-related macros, so we have to provide
 // those instead.
 MATCHER(IsOk, "Checks if an instance of absl::Status is ok.") {
   if (arg.ok()) {
     return true;
   }
-  *result_listener << "Expected status OK, got " << arg;
+  *result_listener << "Expected status OK, got " << ExtractStatus(arg);
   return false;
 }
 
+MATCHER_P(IsOkAndHolds, matcher,
+          "Matcher against the inner value of absl::StatusOr") {
+  if (!arg.ok()) {
+    *result_listener << "Expected status OK, got " << arg.status();
+    return false;
+  }
+  return ::testing::ExplainMatchResult(matcher, arg.value(), result_listener);
+}
+
+MATCHER_P2(StatusIs, code, matcher, "Matcher against a specific status code") {
+  if (ExtractStatus(arg).code() != code) {
+    *result_listener << "Expected status " << absl::StatusCodeToString(code)
+                     << ", got " << ExtractStatus(arg);
+    return false;
+  }
+  return ::testing::ExplainMatchResult(matcher, ExtractStatus(arg).message(),
+                                       result_listener);
+}
+
 #define QUICHE_EXPECT_OK(arg) EXPECT_THAT((arg), ::quiche::test::IsOk())
 #define QUICHE_ASSERT_OK(arg) ASSERT_THAT((arg), ::quiche::test::IsOk())
 
diff --git a/quiche/common/test_tools/quiche_test_utils_test.cc b/quiche/common/test_tools/quiche_test_utils_test.cc
new file mode 100644
index 0000000..17427a5
--- /dev/null
+++ b/quiche/common/test_tools/quiche_test_utils_test.cc
@@ -0,0 +1,37 @@
+#include "quiche/common/test_tools/quiche_test_utils.h"
+
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "quiche/common/platform/api/quiche_test.h"
+
+namespace quiche::test {
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Not;
+
+TEST(QuicheTestUtilsTest, StatusMatchers) {
+  const absl::Status ok = absl::OkStatus();
+  QUICHE_EXPECT_OK(ok);
+  QUICHE_ASSERT_OK(ok);
+  EXPECT_THAT(ok, IsOk());
+
+  const absl::StatusOr<int> ok_with_value = 2023;
+  QUICHE_EXPECT_OK(ok_with_value);
+  QUICHE_ASSERT_OK(ok_with_value);
+  EXPECT_THAT(ok_with_value, IsOk());
+  EXPECT_THAT(ok_with_value, IsOkAndHolds(2023));
+
+  const absl::Status err = absl::InternalError("test error");
+  EXPECT_THAT(err, Not(IsOk()));
+  EXPECT_THAT(err, StatusIs(absl::StatusCode::kInternal, HasSubstr("test")));
+
+  const absl::StatusOr<int> err_with_value = absl::InternalError("test error");
+  EXPECT_THAT(err_with_value, Not(IsOk()));
+  EXPECT_THAT(err_with_value, Not(IsOkAndHolds(2023)));
+  EXPECT_THAT(err_with_value,
+              StatusIs(absl::StatusCode::kInternal, HasSubstr("test")));
+}
+
+}  // namespace
+}  // namespace quiche::test