-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathstatus_matchers.h
365 lines (313 loc) · 12.4 KB
/
status_matchers.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
// Copied and adapted from https://github.com/google/iree.
// Copyright 2019 Google LLC
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GUTILS_INTERNAL_STATUS_MATCHERS_H_
#define GUTILS_INTERNAL_STATUS_MATCHERS_H_
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <ostream>
#include <string>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#undef EXPECT_OK
#undef ASSERT_OK
#undef ASSERT_OK_AND_ASSIGN
namespace gutils {
namespace internal {
// Implements a gMock matcher that checks that an absl::StaturOr<T> has an OK
// status and that the contained T value matches another matcher.
template <typename T>
class IsOkAndHoldsMatcher
: public ::testing::MatcherInterface<const absl::StatusOr<T> &> {
public:
template <typename MatcherT>
IsOkAndHoldsMatcher(MatcherT &&value_matcher)
: value_matcher_(::testing::SafeMatcherCast<const T &>(value_matcher)) {}
// From testing::MatcherInterface.
void DescribeTo(std::ostream *os) const override {
*os << "is OK and contains a value that ";
value_matcher_.DescribeTo(os);
}
// From testing::MatcherInterface.
void DescribeNegationTo(std::ostream *os) const override {
*os << "is not OK or contains a value that ";
value_matcher_.DescribeNegationTo(os);
}
// From testing::MatcherInterface.
bool MatchAndExplain(
const absl::StatusOr<T> &status_or,
::testing::MatchResultListener *listener) const override {
if (!status_or.ok()) {
*listener << "which is not OK";
return false;
}
::testing::StringMatchResultListener value_listener;
bool is_a_match =
value_matcher_.MatchAndExplain(status_or.value(), &value_listener);
std::string value_explanation = value_listener.str();
if (!value_explanation.empty()) {
*listener << absl::StrCat("which contains a value ", value_explanation);
}
return is_a_match;
}
private:
const ::testing::Matcher<const T &> value_matcher_;
};
// A polymorphic IsOkAndHolds() matcher.
//
// IsOkAndHolds() returns a matcher that can be used to process an IsOkAndHolds
// expectation. However, the value type T is not provided when IsOkAndHolds() is
// invoked. The value type is only inferable when the gUnit framework invokes
// the matcher with a value. Consequently, the IsOkAndHolds() function must
// return an object that is implicitly convertible to a matcher for
// absl::StatusOr<T>. gUnit refers to such an object as a polymorphic matcher,
// since it can be used to match with more than one type of value.
template <typename ValueMatcherT>
class IsOkAndHoldsGenerator {
public:
explicit IsOkAndHoldsGenerator(ValueMatcherT value_matcher)
: value_matcher_(std::move(value_matcher)) {}
template <typename T>
operator ::testing::Matcher<const absl::StatusOr<T> &>() const {
return ::testing::MakeMatcher(new IsOkAndHoldsMatcher<T>(value_matcher_));
}
private:
const ValueMatcherT value_matcher_;
};
// Implements a gMock matcher for checking error-code expectations on
// absl::Status and absl::StatusOr objects.
template <typename Enum, typename Matchee>
class StatusMatcher : public ::testing::MatcherInterface<Matchee> {
public:
StatusMatcher(Enum code, absl::optional<absl::string_view> message)
: code_(code), message_(message) {}
// From testing::MatcherInterface.
//
// Describes the expected error code.
void DescribeTo(std::ostream *os) const override {
*os << "error code " << StatusCodeToString(code_);
if (message_.has_value()) {
*os << "::'" << message_.value() << "'";
}
}
// From testing::MatcherInterface.
//
// Tests whether |matchee| has an error code that meets this matcher's
// expectation. If an error message string is specified in this matcher, it
// also tests that |matchee| has an error message that matches that
// expectation.
bool MatchAndExplain(
Matchee &matchee,
::testing::MatchResultListener *listener) const override {
if (GetCode(matchee) != code_) {
*listener << "whose error code is "
<< StatusCodeToString(GetCode(matchee));
return false;
}
if (message_.has_value() && GetMessage(matchee) != message_.value()) {
*listener << "whose error message is '" << GetMessage(matchee) << "'";
return false;
}
return true;
}
private:
template <typename T>
absl::StatusCode GetCode(const T &matchee) const {
return GetCode(matchee.status());
}
absl::StatusCode GetCode(const absl::Status &status) const {
return status.code();
}
template <typename T>
absl::string_view GetMessage(const T &matchee) const {
return GetMessage(matchee.status());
}
absl::string_view GetMessage(const absl::Status &status) const {
return status.message();
}
// Expected error code.
const Enum code_;
// Expected error message (empty if none expected and verified).
const absl::optional<std::string> message_;
};
// StatusMatcherGenerator is an intermediate object returned by
// gutils::testing::status::StatusIs().
// It implements implicit type-cast operators to supported matcher types:
// Matcher<const absl::Status &> and Matcher<const absl::StatusOr<T> &>. These
// typecast operators create gMock matchers that test OK expectations on a
// status container.
template <typename Enum>
class StatusIsMatcherGenerator {
public:
StatusIsMatcherGenerator(Enum code, absl::optional<absl::string_view> message)
: code_(code), message_(message) {}
// Type-cast operator for Matcher<const absl::Status &>.
operator ::testing::Matcher<const absl::Status &>() const {
return ::testing::MakeMatcher(
new internal::StatusMatcher<Enum, const absl::Status &>(code_,
message_));
}
// Type-cast operator for Matcher<const absl::StatusOr<T> &>.
template <class T>
operator ::testing::Matcher<const absl::StatusOr<T> &>() const {
return ::testing::MakeMatcher(
new internal::StatusMatcher<Enum, const absl::StatusOr<T> &>(code_,
message_));
}
private:
// Expected error code.
const Enum code_;
// Expected error message (empty if none expected and verified).
const absl::optional<std::string> message_;
};
// Implements a gMock matcher that checks whether a status container (e.g.
// absl::Status or absl::StatusOr<T>) has an OK status.
template <class T>
class IsOkMatcherImpl : public ::testing::MatcherInterface<T> {
public:
IsOkMatcherImpl() = default;
// From testing::MatcherInterface.
//
// Describes the OK expectation.
void DescribeTo(std::ostream *os) const override { *os << "is OK"; }
// From testing::MatcherInterface.
//
// Describes the negative OK expectation.
void DescribeNegationTo(std::ostream *os) const override {
*os << "is not OK";
}
// From testing::MatcherInterface.
//
// Tests whether |status_container|'s OK value meets this matcher's
// expectation.
bool MatchAndExplain(
const T &status_container,
::testing::MatchResultListener *listener) const override {
if (!status_container.ok()) {
*listener << "which is not OK";
return false;
}
return true;
}
};
// IsOkMatcherGenerator is an intermediate object returned by gutils::IsOk().
// It implements implicit type-cast operators to supported matcher types:
// Matcher<const absl::Status &> and Matcher<const absl::StatusOr<T> &>. These
// typecast operators create gMock matchers that test OK expectations on a
// status container.
class IsOkMatcherGenerator {
public:
// Type-cast operator for Matcher<const absl::Status &>.
operator ::testing::Matcher<const absl::Status &>() const {
return ::testing::MakeMatcher(
new internal::IsOkMatcherImpl<const absl::Status &>());
}
// Type-cast operator for Matcher<const absl::StatusOr<T> &>.
template <class T>
operator ::testing::Matcher<const absl::StatusOr<T> &>() const {
return ::testing::MakeMatcher(
new internal::IsOkMatcherImpl<const absl::StatusOr<T> &>());
}
};
} // namespace internal
namespace testing {
namespace status {
namespace internal = ::gutils::internal;
// Returns a gMock matcher that expects an absl::StatusOr<T> object to have an
// OK status and for the contained T object to match |value_matcher|.
//
// Example:
//
// absl::StatusOr<string> raven_speech_result = raven.Speak();
// EXPECT_THAT(raven_speech_result, IsOkAndHolds(HasSubstr("nevermore")));
//
// If foo is an object of type T and foo_result is an object of type
// absl::StatusOr<T>, you can write:
//
// EXPECT_THAT(foo_result, IsOkAndHolds(foo));
//
// instead of:
//
// EXPECT_THAT(foo_result, IsOkAndHolds(Eq(foo)));
template <typename ValueMatcherT>
internal::IsOkAndHoldsGenerator<ValueMatcherT> IsOkAndHolds(
ValueMatcherT value_matcher) {
return internal::IsOkAndHoldsGenerator<ValueMatcherT>(value_matcher);
}
// Returns a gMock matcher that expects an absl::Status object to have the
// given |code|.
template <typename Enum>
internal::StatusIsMatcherGenerator<Enum> StatusIs(Enum code) {
return internal::StatusIsMatcherGenerator<Enum>(code, absl::nullopt);
}
// Returns a gMock matcher that expects an absl::Status object to have the
// given |code| and |message|.
template <typename Enum>
internal::StatusIsMatcherGenerator<Enum> StatusIs(Enum code,
absl::string_view message) {
return internal::StatusIsMatcherGenerator<Enum>(code, message);
}
// Returns an internal::IsOkMatcherGenerator, which may be typecast to a
// Matcher<absl::Status> or Matcher<absl::StatusOr<T>>. These gMock
// matchers test that a given status container has an OK status.
inline internal::IsOkMatcherGenerator IsOk() {
return internal::IsOkMatcherGenerator();
}
} // namespace status
} // namespace testing
// Macros for testing the results of functions that return absl::Status or
// absl::StatusOr<T> (for any type T).
#define EXPECT_OK(rexpr) EXPECT_THAT(rexpr, ::gutils::testing::status::IsOk())
#define ASSERT_OK(rexpr) ASSERT_THAT(rexpr, ::gutils::testing::status::IsOk())
// Executes an expression that returns an absl::StatusOr<T>, and assigns the
// contained variable to lhs if the error code is OK.
// If the absl::Status is non-OK, generates a test failure and returns from the
// current function, which must have a void return type.
//
// Example: Assigning to an existing value
// ASSERT_OK_AND_ASSIGN(ValueType value, MaybeGetValue(arg));
//
// The value assignment example might expand into:
// absl::StatusOr<ValueType> status_or_value = MaybeGetValue(arg);
// ASSERT_OK(status_or_value.status());
// ValueType value = status_or_value.value();
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr) \
IREE_ASSERT_OK_AND_ASSIGN_IMPL( \
IREE_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \
rexpr);
#define IREE_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \
auto statusor = (rexpr); \
ASSERT_OK(statusor.status()) << statusor.status(); \
lhs = std::move(statusor.value())
#define IREE_STATUS_MACROS_CONCAT_NAME(x, y) \
IREE_STATUS_MACROS_CONCAT_IMPL(x, y)
#define IREE_STATUS_MACROS_CONCAT_IMPL(x, y) x##y
// Implements the PrintTo() method for absl::StatusOr<T>. This method is
// used by gUnit to print absl::StatusOr<T> objects for debugging. The
// implementation relies on gUnit for printing values of T when a
// absl::StatusOr<T> object is OK and contains a value.
template <typename T>
void PrintTo(const absl::StatusOr<T> &statusor, std::ostream *os) {
if (!statusor.ok()) {
*os << statusor.status();
} else {
*os << absl::StrCat("OK: ", ::testing::PrintToString(statusor.value()));
}
}
} // namespace gutils
#endif // GUTILS_INTERNAL_STATUS_MATCHERS_H_