1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef SANDBOXED_API_UTIL_STATUS_MATCHERS_H_
16 #define SANDBOXED_API_UTIL_STATUS_MATCHERS_H_
17
18 #include <ostream>
19 #include <string>
20 #include <type_traits>
21 #include <utility>
22
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "absl/status/status.h"
26 #include "absl/status/statusor.h"
27 #include "absl/strings/string_view.h"
28 #include "absl/types/optional.h"
29 #include "sandboxed_api/util/status_macros.h"
30
31 #define SAPI_ASSERT_OK(expr) ASSERT_THAT(expr, ::sapi::IsOk())
32
33 #define SAPI_ASSERT_OK_AND_ASSIGN(lhs, rexpr) \
34 SAPI_ASSERT_OK_AND_ASSIGN_IMPL( \
35 SAPI_MACROS_IMPL_CONCAT(_sapi_statusor, __LINE__), lhs, rexpr)
36
37 #define SAPI_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \
38 auto statusor = (rexpr); \
39 ASSERT_THAT(statusor.status(), ::sapi::IsOk()); \
40 lhs = std::move(statusor).value()
41
42 namespace sapi {
43 namespace internal {
44
45 class IsOkMatcher {
46 public:
47 template <typename StatusT>
MatchAndExplain(const StatusT & status_container,::testing::MatchResultListener * listener)48 bool MatchAndExplain(const StatusT& status_container,
49 ::testing::MatchResultListener* listener) const {
50 if (!status_container.ok()) {
51 *listener << "which is not OK";
52 return false;
53 }
54 return true;
55 }
56
DescribeTo(std::ostream * os)57 void DescribeTo(std::ostream* os) const { *os << "is OK"; }
58
DescribeNegationTo(std::ostream * os)59 void DescribeNegationTo(std::ostream* os) const { *os << "is not OK"; }
60 };
61
62 class StatusIsMatcher {
63 public:
64 StatusIsMatcher(const StatusIsMatcher&) = default;
65
StatusIsMatcher(absl::StatusCode code,absl::optional<absl::string_view> message)66 StatusIsMatcher(absl::StatusCode code,
67 absl::optional<absl::string_view> message)
68 : code_(code), message_(message) {}
69
70 template <typename T>
MatchAndExplain(const T & value,::testing::MatchResultListener * listener)71 bool MatchAndExplain(const T& value,
72 ::testing::MatchResultListener* listener) const {
73 auto status = GetStatus(value);
74 if (code_ != status.code()) {
75 *listener << "whose error code is "
76 << absl::StatusCodeToString(status.code());
77 return false;
78 }
79 if (message_.has_value() && status.message() != message_.value()) {
80 *listener << "whose error message is '" << message_.value() << "'";
81 return false;
82 }
83 return true;
84 }
85
DescribeTo(std::ostream * os)86 void DescribeTo(std::ostream* os) const {
87 *os << "has a status code that is " << absl::StatusCodeToString(code_);
88 if (message_.has_value()) {
89 *os << ", and has an error message that is '" << message_.value() << "'";
90 }
91 }
92
DescribeNegationTo(std::ostream * os)93 void DescribeNegationTo(std::ostream* os) const {
94 *os << "has a status code that is not " << absl::StatusCodeToString(code_);
95 if (message_.has_value()) {
96 *os << ", and has an error message that is not '" << message_.value()
97 << "'";
98 }
99 }
100
101 private:
102 template <typename StatusT,
103 typename std::enable_if<
104 !std::is_void<decltype(std::declval<StatusT>().code())>::value,
105 int>::type = 0>
GetStatus(const StatusT & status)106 static const StatusT& GetStatus(const StatusT& status) {
107 return status;
108 }
109
110 template <typename StatusOrT,
111 typename StatusT = decltype(std::declval<StatusOrT>().status())>
GetStatus(const StatusOrT & status_or)112 static StatusT GetStatus(const StatusOrT& status_or) {
113 return status_or.status();
114 }
115
116 const absl::StatusCode code_;
117 const absl::optional<std::string> message_;
118 };
119
120 } // namespace internal
121
IsOk()122 inline ::testing::PolymorphicMatcher<internal::IsOkMatcher> IsOk() {
123 return ::testing::MakePolymorphicMatcher(internal::IsOkMatcher{});
124 }
125
126 inline ::testing::PolymorphicMatcher<internal::StatusIsMatcher> StatusIs(
127 absl::StatusCode code,
128 absl::optional<absl::string_view> message = absl::nullopt) {
129 return ::testing::MakePolymorphicMatcher(
130 internal::StatusIsMatcher(code, message));
131 }
132
133 } // namespace sapi
134
135 #endif // SANDBOXED_API_UTIL_STATUS_MATCHERS_H_
136