about summary refs log tree commit diff
path: root/third_party
diff options
context:
space:
mode:
authorKane York <kanepyork@gmail.com>2020-08-10T00·39-0700
committerkanepyork <rikingcoding@gmail.com>2020-08-15T00·52+0000
commite458e5255ad9aff8b4831a288c4f694f329fc7f3 (patch)
tree16ded8d35211571306b4e4c370e20adc5656dcb6 /third_party
parentdae33202fca3b8df6315ab06ae60127a3ec7ef00 (diff)
feat(tvix/tests): add gtest matchers for absl::Status r/1654
This allows you to write EXPECT_OK(statusor), as well as
EXPECT_THAT(status, IsStatusCode(StatusCode::kInvalidArgument).

Change-Id: I53bed694d812c501eb305ed4ddb358e1f9a68277
Reviewed-on: https://cl.tvl.fyi/c/depot/+/1704
Tested-by: BuildkiteCI
Reviewed-by: glittershark <grfn@gws.fyi>
Diffstat (limited to 'third_party')
-rw-r--r--third_party/nix/src/tests/status_helpers.h83
1 files changed, 83 insertions, 0 deletions
diff --git a/third_party/nix/src/tests/status_helpers.h b/third_party/nix/src/tests/status_helpers.h
new file mode 100644
index 0000000000..ca596dbb52
--- /dev/null
+++ b/third_party/nix/src/tests/status_helpers.h
@@ -0,0 +1,83 @@
+#pragma once
+
+#include <absl/status/status.h>
+#include <absl/status/statusor.h>
+#include <absl/strings/str_cat.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace testing {
+
+/*
+ * This file contains gtest matchers for absl::Status.
+ *
+ * Example usage:
+ *
+ *   EXPECT_OK(status); -- fails the test if 'status' is an error
+ *   ASSERT_OK(status); -- instantly fails the test if error
+ *
+ *   using ::testing::IsStatusCode;
+ *   EXPECT_THAT(status, IsStatusCode(absl::StatusCode::kInternal));
+ */
+
+namespace nix_internal {
+
+using ::testing::MakeMatcher;
+using ::testing::Matcher;
+using ::testing::MatcherInterface;
+using ::testing::MatchResultListener;
+
+MATCHER_P(IsStatusCode, code, "") { return arg.code() == code; }
+
+class StatusCodeMatcher {
+ public:
+  StatusCodeMatcher(absl::StatusCode code) : code_(code) {}
+
+  // Match on absl::Status.
+  template <class T,
+            typename std::enable_if<std::is_same<T, absl::Status>::value,
+                                    int>::type int_ = 0>
+  bool MatchAndExplain(const T& status,
+                       MatchResultListener* /* listener */) const {
+    return status.code() == code_;
+  }
+
+  // Match on absl::StatusOr.
+  //
+  // note: I check for the return value of ConsumeValueOrDie because it's the
+  // only non-overloaded member I could figure out how to select. Checking for
+  // the presence of .status() didn't work because it's overloaded, so
+  // std::invoke_result can't pick which overload to use.
+  template <class T,
+            typename std::enable_if<
+                std::is_same<typename std::invoke_result<
+                                 decltype(&T::ConsumeValueOrDie), T>::type,
+                             typename T::value_type>::value,
+                int>::type int_ = 0>
+  bool MatchAndExplain(const T& statusor,
+                       MatchResultListener* /* listener */) const {
+    return statusor.status().code() == code_;
+  }
+
+  void DescribeTo(std::ostream* os) const { *os << "is " << code_; }
+
+  void DescribeNegationTo(std::ostream* os) const { *os << "isn't " << code_; }
+
+ private:
+  absl::StatusCode code_;
+};
+
+}  // namespace nix_internal
+
+PolymorphicMatcher<nix_internal::StatusCodeMatcher> IsStatusCode(
+    absl::StatusCode code) {
+  return MakePolymorphicMatcher(nix_internal::StatusCodeMatcher(code));
+}
+
+#define EXPECT_OK(status) \
+  EXPECT_THAT((status), testing::IsStatusCode(absl::StatusCode::kOk))
+
+#define ASSERT_OK(status) \
+  ASSERT_THAT((status), testing::IsStatusCode(absl::StatusCode::kOk))
+
+}  // namespace testing