xref: /aosp_15_r20/external/tensorflow/tensorflow/core/platform/status_matchers.h (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
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     http://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 TENSORFLOW_CORE_PLATFORM_STATUS_MATCHERS_H_
16 #define TENSORFLOW_CORE_PLATFORM_STATUS_MATCHERS_H_
17 
18 #include <ostream>
19 #include <string>
20 #include <utility>
21 
22 #include "tensorflow/core/platform/status.h"
23 #include "tensorflow/core/platform/statusor.h"
24 #include "tensorflow/core/platform/test.h"
25 #include "tensorflow/core/protobuf/error_codes.pb.h"
26 
27 // Defines the following utilities:
28 //
29 // ===============
30 // IsOkAndHolds(m)
31 // ===============
32 //
33 // This matcher matches a StatusOr<T> value whose status is OK and whose inner
34 // value matches matcher m. Example:
35 //
36 //   using ::tensorflow::testing::IsOkAndHolds;
37 //   using ::testing::HasSubstr;
38 //   ...
39 //   StatusOr<std::string> status_or_message("Hello, world");
40 //   EXPECT_THAT(status_or_message, IsOkAndHolds("Hello, world")));
41 //   EXPECT_THAT(status_or_message, IsOkAndHolds(HasSubstr("Hello,")));
42 //
43 // ===============================
44 // StatusIs(status_code_matcher,
45 //          error_message_matcher)
46 // ===============================
47 //
48 // This matcher matches a Status or StatusOr<T> if the following are true:
49 //
50 //   - the status's code() matches status_code_matcher, and
51 //   - the status's error_message() matches error_message_matcher.
52 //
53 // Example:
54 //
55 //   using ::tensorflow::testing::StatusIs;
56 //   using ::testing::HasSubstr;
57 //   using ::testing::MatchesRegex;
58 //   using ::testing::Ne;
59 //   using ::testing::_;
60 //   StatusOr<std::string> GetMessage(int id);
61 //   ...
62 //
63 //   // The status code must be CANCELLED; the error message can be anything.
64 //   EXPECT_THAT(GetName(42),
65 //               StatusIs(tensorflow::error::CANCELLED, _));
66 //
67 //   // The status code can be anything; the error message must match the regex.
68 //   EXPECT_THAT(GetName(43),
69 //               StatusIs(_, MatchesRegex("server.*time-out")));
70 //
71 //   // The status code should not be CANCELLED; the error message can be
72 //   // anything with "Cancelled" in it.
73 //   EXPECT_THAT(GetName(44),
74 //               StatusIs(Ne(tensorflow::error::CANCELLED),
75 //                        HasSubstr("Cancelled"))));
76 //
77 // =============================
78 // StatusIs(status_code_matcher)
79 // =============================
80 //
81 // This is a shorthand for
82 //   StatusIs(status_code_matcher, ::testing::_)
83 //
84 // In other words, it's like the two-argument StatusIs(), except that it ignores
85 // error messages.
86 //
87 // ======
88 // IsOk()
89 // ======
90 //
91 // Matches a Status or StatusOr<T> whose status value is OK.
92 // Equivalent to 'StatusIs(error::OK)'.
93 //
94 // Example:
95 //   ...
96 //   StatusOr<std::string> message("Hello, world");
97 //   EXPECT_THAT(message, IsOk());
98 //   Status status = Status::OK();
99 //   EXPECT_THAT(status, IsOk());
100 
101 namespace tensorflow {
102 
103 template <typename T>
PrintTo(const StatusOr<T> & status_or,std::ostream * os)104 void PrintTo(const StatusOr<T>& status_or, std::ostream* os) {
105   *os << ::testing::PrintToString(status_or.status());
106   if (status_or.ok()) {
107     *os << ": " << ::testing::PrintToString(status_or.ValueOrDie());
108   }
109 }
110 
111 namespace error {
PrintTo(const tensorflow::error::Code code,std::ostream * os)112 inline void PrintTo(const tensorflow::error::Code code, std::ostream* os) {
113   *os << Code_Name(code);
114 }
115 }  // namespace error
116 
117 namespace testing {
118 namespace internal_status {
119 
GetStatus(const Status & status)120 inline const Status& GetStatus(const Status& status) { return status; }
121 
122 template <typename T>
GetStatus(const StatusOr<T> & status)123 inline const Status& GetStatus(const StatusOr<T>& status) {
124   return status.status();
125 }
126 
127 ////////////////////////////////////////////////////////////
128 // Implementation of IsOkAndHolds().
129 //
130 // Monomorphic implementation of matcher IsOkAndHolds(m). StatusOrType is a
131 // reference to StatusOr<T>.
132 template <typename StatusOrType>
133 class IsOkAndHoldsMatcherImpl
134     : public ::testing::MatcherInterface<StatusOrType> {
135  public:
136   typedef
137       typename std::remove_reference<StatusOrType>::type::value_type value_type;
138 
139   template <typename InnerMatcher>
IsOkAndHoldsMatcherImpl(InnerMatcher && inner_matcher)140   explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher)
141       : inner_matcher_(::testing::SafeMatcherCast<const value_type&>(
142             std::forward<InnerMatcher>(inner_matcher))) {}
143 
DescribeTo(std::ostream * os)144   void DescribeTo(std::ostream* os) const override {
145     *os << "is OK and has a value that ";
146     inner_matcher_.DescribeTo(os);
147   }
148 
DescribeNegationTo(std::ostream * os)149   void DescribeNegationTo(std::ostream* os) const override {
150     *os << "isn't OK or has a value that ";
151     inner_matcher_.DescribeNegationTo(os);
152   }
153 
MatchAndExplain(StatusOrType actual_value,::testing::MatchResultListener * result_listener)154   bool MatchAndExplain(
155       StatusOrType actual_value,
156       ::testing::MatchResultListener* result_listener) const override {
157     if (!actual_value.ok()) {
158       *result_listener << "which has status " << actual_value.status();
159       return false;
160     }
161 
162     ::testing::StringMatchResultListener inner_listener;
163     const bool matches =
164         inner_matcher_.MatchAndExplain(*actual_value, &inner_listener);
165     const std::string inner_explanation = inner_listener.str();
166     if (!inner_explanation.empty()) {
167       *result_listener << "which contains value "
168                        << ::testing::PrintToString(*actual_value) << ", "
169                        << inner_explanation;
170     }
171     return matches;
172   }
173 
174  private:
175   const ::testing::Matcher<const value_type&> inner_matcher_;
176 };
177 
178 // Implements IsOkAndHolds(m) as a polymorphic matcher.
179 template <typename InnerMatcher>
180 class IsOkAndHoldsMatcher {
181  public:
IsOkAndHoldsMatcher(InnerMatcher inner_matcher)182   explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher)
183       : inner_matcher_(std::move(inner_matcher)) {}
184 
185   // Converts this polymorphic matcher to a monomorphic matcher of the given
186   // type. StatusOrType can be either StatusOr<T> or a reference to StatusOr<T>.
187   template <typename StatusOrType>
188   operator ::testing::Matcher<StatusOrType>() const {  // NOLINT
189     return ::testing::Matcher<StatusOrType>(
190         new IsOkAndHoldsMatcherImpl<const StatusOrType&>(inner_matcher_));
191   }
192 
193  private:
194   const InnerMatcher inner_matcher_;
195 };
196 
197 ////////////////////////////////////////////////////////////
198 // Implementation of StatusIs().
199 //
200 // StatusIs() is a polymorphic matcher. This class is the common
201 // implementation of it shared by all types T where StatusIs() can be used as
202 // a Matcher<T>.
203 
204 class StatusIsMatcherCommonImpl {
205  public:
StatusIsMatcherCommonImpl(::testing::Matcher<const tensorflow::error::Code> code_matcher,::testing::Matcher<const std::string &> message_matcher)206   StatusIsMatcherCommonImpl(
207       ::testing::Matcher<const tensorflow::error::Code> code_matcher,
208       ::testing::Matcher<const std::string&> message_matcher)
209       : code_matcher_(std::move(code_matcher)),
210         message_matcher_(std::move(message_matcher)) {}
211 
212   void DescribeTo(std::ostream* os) const;
213 
214   void DescribeNegationTo(std::ostream* os) const;
215 
216   bool MatchAndExplain(const Status& status,
217                        ::testing::MatchResultListener* result_listener) const;
218 
219  private:
220   const ::testing::Matcher<const tensorflow::error::Code> code_matcher_;
221   const ::testing::Matcher<const std::string&> message_matcher_;
222 };
223 
224 // Monomorphic implementation of matcher StatusIs() for a given type T. T can
225 // be Status, StatusOr<>, or a reference to either of them.
226 template <typename T>
227 class MonoStatusIsMatcherImpl : public ::testing::MatcherInterface<T> {
228  public:
MonoStatusIsMatcherImpl(StatusIsMatcherCommonImpl common_impl)229   explicit MonoStatusIsMatcherImpl(StatusIsMatcherCommonImpl common_impl)
230       : common_impl_(std::move(common_impl)) {}
231 
DescribeTo(std::ostream * os)232   void DescribeTo(std::ostream* os) const override {
233     common_impl_.DescribeTo(os);
234   }
235 
DescribeNegationTo(std::ostream * os)236   void DescribeNegationTo(std::ostream* os) const override {
237     common_impl_.DescribeNegationTo(os);
238   }
239 
MatchAndExplain(T actual_value,::testing::MatchResultListener * result_listener)240   bool MatchAndExplain(
241       T actual_value,
242       ::testing::MatchResultListener* result_listener) const override {
243     return common_impl_.MatchAndExplain(GetStatus(actual_value),
244                                         result_listener);
245   }
246 
247  private:
248   StatusIsMatcherCommonImpl common_impl_;
249 };
250 
251 // Implements StatusIs() as a polymorphic matcher.
252 class StatusIsMatcher {
253  public:
StatusIsMatcher(::testing::Matcher<const tensorflow::error::Code> code_matcher,::testing::Matcher<const std::string &> message_matcher)254   StatusIsMatcher(
255       ::testing::Matcher<const tensorflow::error::Code> code_matcher,
256       ::testing::Matcher<const std::string&> message_matcher)
257       : common_impl_(
258             ::testing::MatcherCast<const tensorflow::error::Code>(code_matcher),
259             ::testing::MatcherCast<const std::string&>(message_matcher)) {}
260 
261   // Converts this polymorphic matcher to a monomorphic matcher of the given
262   // type. T can be StatusOr<>, Status, or a reference to either of them.
263   template <typename T>
264   operator ::testing::Matcher<T>() const {  // NOLINT
265     return ::testing::MakeMatcher(new MonoStatusIsMatcherImpl<T>(common_impl_));
266   }
267 
268  private:
269   const StatusIsMatcherCommonImpl common_impl_;
270 };
271 
272 // Monomorphic implementation of matcher IsOk() for a given type T.
273 // T can be Status, StatusOr<>, or a reference to either of them.
274 template <typename T>
275 class MonoIsOkMatcherImpl : public ::testing::MatcherInterface<T> {
276  public:
DescribeTo(std::ostream * os)277   void DescribeTo(std::ostream* os) const override { *os << "is OK"; }
DescribeNegationTo(std::ostream * os)278   void DescribeNegationTo(std::ostream* os) const override {
279     *os << "is not OK";
280   }
MatchAndExplain(T actual_value,::testing::MatchResultListener *)281   bool MatchAndExplain(T actual_value,
282                        ::testing::MatchResultListener*) const override {
283     return GetStatus(actual_value).ok();
284   }
285 };
286 
287 // Implements IsOk() as a polymorphic matcher.
288 class IsOkMatcher {
289  public:
290   template <typename T>
291   operator ::testing::Matcher<T>() const {  // NOLINT
292     return ::testing::Matcher<T>(new MonoIsOkMatcherImpl<const T&>());
293   }
294 };
295 }  // namespace internal_status
296 
297 // Returns a matcher that matches a StatusOr<> whose status is OK and whose
298 // value matches the inner matcher.
299 template <typename InnerMatcher>
300 internal_status::IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>
IsOkAndHolds(InnerMatcher && inner_matcher)301 IsOkAndHolds(InnerMatcher&& inner_matcher) {
302   return internal_status::IsOkAndHoldsMatcher<
303       typename std::decay<InnerMatcher>::type>(
304       std::forward<InnerMatcher>(inner_matcher));
305 }
306 
307 // Returns a matcher that matches a Status or StatusOr<> whose status code
308 // matches code_matcher, and whose error message matches message_matcher.
309 template <typename CodeMatcher, typename MessageMatcher>
StatusIs(CodeMatcher code_matcher,MessageMatcher message_matcher)310 internal_status::StatusIsMatcher StatusIs(CodeMatcher code_matcher,
311                                           MessageMatcher message_matcher) {
312   return internal_status::StatusIsMatcher(std::move(code_matcher),
313                                           std::move(message_matcher));
314 }
315 
316 // Returns a matcher that matches a Status or StatusOr<> whose status code
317 // matches code_matcher.
318 template <typename CodeMatcher>
StatusIs(CodeMatcher code_matcher)319 internal_status::StatusIsMatcher StatusIs(CodeMatcher code_matcher) {
320   return StatusIs(std::move(code_matcher), ::testing::_);
321 }
322 
323 // Returns a matcher that matches a Status or StatusOr<> which is OK.
IsOk()324 inline internal_status::IsOkMatcher IsOk() {
325   return internal_status::IsOkMatcher();
326 }
327 
328 }  // namespace testing
329 }  // namespace tensorflow
330 
331 #endif  // TENSORFLOW_CORE_PLATFORM_STATUS_MATCHERS_H_
332