error_test_helpers.h 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef CARBON_COMMON_ERROR_TEST_HELPERS_H_
  5. #define CARBON_COMMON_ERROR_TEST_HELPERS_H_
  6. #include <gmock/gmock.h>
  7. #include <concepts>
  8. #include "common/error.h"
  9. #include "common/ostream.h"
  10. namespace Carbon::Testing {
  11. // Matches the message for an error state of `ErrorOr<T>`. For example:
  12. // EXPECT_THAT(my_result, IsError(StrEq("error message")));
  13. class IsError {
  14. public:
  15. // NOLINTNEXTLINE(readability-identifier-naming)
  16. using is_gtest_matcher = void;
  17. explicit IsError(::testing::Matcher<std::string> matcher)
  18. : matcher_(std::move(matcher)) {}
  19. template <typename T, typename ErrorT>
  20. auto MatchAndExplain(const ErrorOr<T, ErrorT>& result,
  21. ::testing::MatchResultListener* listener) const -> bool {
  22. if (result.ok()) {
  23. *listener << "is a success";
  24. return false;
  25. } else {
  26. RawStringOstream os;
  27. os << result.error();
  28. return matcher_.MatchAndExplain(os.TakeStr(), listener);
  29. }
  30. }
  31. auto DescribeTo(std::ostream* os) const -> void {
  32. *os << "is an error and matches ";
  33. matcher_.DescribeTo(os);
  34. }
  35. auto DescribeNegationTo(std::ostream* os) const -> void {
  36. *os << "is a success or does not match ";
  37. matcher_.DescribeTo(os);
  38. }
  39. private:
  40. ::testing::Matcher<std::string> matcher_;
  41. };
  42. // Implementation of a success matcher for a specific `T` and `ErrorT` in an
  43. // `ErrorOr`. Supports a nested matcher for the `T` value.
  44. template <typename T, typename ErrorT>
  45. class IsSuccessMatcherImpl
  46. : public ::testing::MatcherInterface<const ErrorOr<T, ErrorT>&> {
  47. public:
  48. explicit IsSuccessMatcherImpl(const ::testing::Matcher<T>& matcher)
  49. : matcher_(matcher) {}
  50. auto MatchAndExplain(const ErrorOr<T, ErrorT>& result,
  51. ::testing::MatchResultListener* listener) const
  52. -> bool override {
  53. if (result.ok()) {
  54. return matcher_.MatchAndExplain(*result, listener);
  55. } else {
  56. *listener << "is an error with `" << result.error() << "`";
  57. return false;
  58. }
  59. }
  60. auto DescribeTo(std::ostream* os) const -> void override {
  61. *os << "is a success and matches ";
  62. matcher_.DescribeTo(os);
  63. }
  64. auto DescribeNegationTo(std::ostream* os) const -> void override {
  65. *os << "is an error or does not match ";
  66. matcher_.DescribeNegationTo(os);
  67. }
  68. private:
  69. ::testing::Matcher<T> matcher_;
  70. };
  71. // Polymorphic match implementation for GoogleTest.
  72. //
  73. // To support matching arbitrary types that `InnerMatcher` can also match, this
  74. // itself must match arbitrary types. This is accomplished by not being a
  75. // matcher itself, but by being convertible into matchers for any particular
  76. // `ErrorOr`.
  77. template <typename InnerMatcher>
  78. class IsSuccessMatcher {
  79. public:
  80. explicit IsSuccessMatcher(InnerMatcher matcher)
  81. : matcher_(std::move(matcher)) {}
  82. template <typename T, typename ErrorT>
  83. explicit(false)
  84. // NOLINTNEXTLINE(google-explicit-constructor): Required for matcher APIs.
  85. operator ::testing::Matcher<const ErrorOr<T, ErrorT>&>() const {
  86. return ::testing::Matcher<const ErrorOr<T, ErrorT>&>(
  87. new IsSuccessMatcherImpl<T, ErrorT>(
  88. ::testing::SafeMatcherCast<T>(matcher_)));
  89. }
  90. private:
  91. InnerMatcher matcher_;
  92. };
  93. // Returns a matcher the value for a non-error state of `ErrorOr<T>`.
  94. //
  95. // For example:
  96. // EXPECT_THAT(my_result, IsSuccess(Eq(3)));
  97. template <typename InnerMatcher>
  98. auto IsSuccess(InnerMatcher matcher) -> IsSuccessMatcher<InnerMatcher> {
  99. return IsSuccessMatcher<InnerMatcher>(matcher);
  100. }
  101. } // namespace Carbon::Testing
  102. namespace Carbon {
  103. // Supports printing `ErrorOr<T>` to `std::ostream` in tests.
  104. template <typename T, typename ErrorT>
  105. requires(std::same_as<ErrorT, Error> ||
  106. std::derived_from<ErrorT, ErrorBase<ErrorT>>)
  107. auto operator<<(std::ostream& out, const ErrorOr<T, ErrorT>& error_or)
  108. -> std::ostream& {
  109. if (error_or.ok()) {
  110. // Try and print the value, but only if we can find a viable `<<` overload
  111. // for the value type. This should ensure that the `formatv` below can
  112. // compile cleanly, and avoid erroring when using matchers on `ErrorOr` with
  113. // unprintable value types.
  114. if constexpr (requires(const T& value) { out << value; }) {
  115. out << llvm::formatv("ErrorOr{{.value = `{0}`}}", *error_or);
  116. } else {
  117. out << "ErrorOr{{.value = `<unknown>`}}";
  118. }
  119. } else {
  120. out << llvm::formatv("ErrorOr{{.error = \"{0}\"}}", error_or.error());
  121. }
  122. return out;
  123. }
  124. } // namespace Carbon
  125. #endif // CARBON_COMMON_ERROR_TEST_HELPERS_H_