1*e7b1675dSTing-Kang Chang // Copyright 2018 Google Inc.
2*e7b1675dSTing-Kang Chang //
3*e7b1675dSTing-Kang Chang // Licensed under the Apache License, Version 2.0 (the "License");
4*e7b1675dSTing-Kang Chang // you may not use this file except in compliance with the License.
5*e7b1675dSTing-Kang Chang // You may obtain a copy of the License at
6*e7b1675dSTing-Kang Chang //
7*e7b1675dSTing-Kang Chang // http://www.apache.org/licenses/LICENSE-2.0
8*e7b1675dSTing-Kang Chang //
9*e7b1675dSTing-Kang Chang // Unless required by applicable law or agreed to in writing, software
10*e7b1675dSTing-Kang Chang // distributed under the License is distributed on an "AS IS" BASIS,
11*e7b1675dSTing-Kang Chang // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e7b1675dSTing-Kang Chang // See the License for the specific language governing permissions and
13*e7b1675dSTing-Kang Chang // limitations under the License.
14*e7b1675dSTing-Kang Chang //
15*e7b1675dSTing-Kang Chang ////////////////////////////////////////////////////////////////////////////////
16*e7b1675dSTing-Kang Chang
17*e7b1675dSTing-Kang Chang #ifndef TINK_UTIL_TEST_MATCHERS_H_
18*e7b1675dSTing-Kang Chang #define TINK_UTIL_TEST_MATCHERS_H_
19*e7b1675dSTing-Kang Chang
20*e7b1675dSTing-Kang Chang #include <ostream>
21*e7b1675dSTing-Kang Chang #include <string>
22*e7b1675dSTing-Kang Chang
23*e7b1675dSTing-Kang Chang #include "gmock/gmock.h"
24*e7b1675dSTing-Kang Chang #include "gtest/gtest.h"
25*e7b1675dSTing-Kang Chang #include "absl/status/status.h"
26*e7b1675dSTing-Kang Chang #include "tink/util/status.h"
27*e7b1675dSTing-Kang Chang #include "tink/util/statusor.h"
28*e7b1675dSTing-Kang Chang
29*e7b1675dSTing-Kang Chang namespace crypto {
30*e7b1675dSTing-Kang Chang namespace tink {
31*e7b1675dSTing-Kang Chang namespace test {
32*e7b1675dSTing-Kang Chang namespace internal {
33*e7b1675dSTing-Kang Chang
34*e7b1675dSTing-Kang Chang ////////////////////////////////////////////////////////////
35*e7b1675dSTing-Kang Chang // Implementation of IsOkAndHolds().
36*e7b1675dSTing-Kang Chang
37*e7b1675dSTing-Kang Chang // Monomorphic implementation of matcher IsOkAndHolds(m). StatusOrType is a
38*e7b1675dSTing-Kang Chang // reference to StatusOr<T>.
39*e7b1675dSTing-Kang Chang template <typename StatusOrType>
40*e7b1675dSTing-Kang Chang class IsOkAndHoldsMatcherImpl
41*e7b1675dSTing-Kang Chang : public ::testing::MatcherInterface<StatusOrType> {
42*e7b1675dSTing-Kang Chang public:
43*e7b1675dSTing-Kang Chang using value_type =
44*e7b1675dSTing-Kang Chang typename std::remove_reference<StatusOrType>::type::value_type;
45*e7b1675dSTing-Kang Chang
46*e7b1675dSTing-Kang Chang template <typename InnerMatcher>
IsOkAndHoldsMatcherImpl(InnerMatcher && inner_matcher)47*e7b1675dSTing-Kang Chang explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher)
48*e7b1675dSTing-Kang Chang : inner_matcher_(::testing::SafeMatcherCast<const value_type&>(
49*e7b1675dSTing-Kang Chang std::forward<InnerMatcher>(inner_matcher))) {}
50*e7b1675dSTing-Kang Chang
DescribeTo(std::ostream * os)51*e7b1675dSTing-Kang Chang void DescribeTo(std::ostream* os) const override {
52*e7b1675dSTing-Kang Chang *os << "is OK and has a value that ";
53*e7b1675dSTing-Kang Chang inner_matcher_.DescribeTo(os);
54*e7b1675dSTing-Kang Chang }
55*e7b1675dSTing-Kang Chang
DescribeNegationTo(std::ostream * os)56*e7b1675dSTing-Kang Chang void DescribeNegationTo(std::ostream* os) const override {
57*e7b1675dSTing-Kang Chang *os << "isn't OK or has a value that ";
58*e7b1675dSTing-Kang Chang inner_matcher_.DescribeNegationTo(os);
59*e7b1675dSTing-Kang Chang }
60*e7b1675dSTing-Kang Chang
MatchAndExplain(StatusOrType actual_value,::testing::MatchResultListener * result_listener)61*e7b1675dSTing-Kang Chang bool MatchAndExplain(
62*e7b1675dSTing-Kang Chang StatusOrType actual_value,
63*e7b1675dSTing-Kang Chang ::testing::MatchResultListener* result_listener) const override {
64*e7b1675dSTing-Kang Chang if (!actual_value.ok()) {
65*e7b1675dSTing-Kang Chang *result_listener << "which has status " << actual_value.status();
66*e7b1675dSTing-Kang Chang return false;
67*e7b1675dSTing-Kang Chang }
68*e7b1675dSTing-Kang Chang
69*e7b1675dSTing-Kang Chang ::testing::StringMatchResultListener inner_listener;
70*e7b1675dSTing-Kang Chang const bool matches =
71*e7b1675dSTing-Kang Chang inner_matcher_.MatchAndExplain(*actual_value, &inner_listener);
72*e7b1675dSTing-Kang Chang const std::string inner_explanation = inner_listener.str();
73*e7b1675dSTing-Kang Chang if (!inner_explanation.empty()) {
74*e7b1675dSTing-Kang Chang *result_listener << "which contains value "
75*e7b1675dSTing-Kang Chang << ::testing::PrintToString(*actual_value) << ", "
76*e7b1675dSTing-Kang Chang << inner_explanation;
77*e7b1675dSTing-Kang Chang }
78*e7b1675dSTing-Kang Chang return matches;
79*e7b1675dSTing-Kang Chang }
80*e7b1675dSTing-Kang Chang
81*e7b1675dSTing-Kang Chang private:
82*e7b1675dSTing-Kang Chang const ::testing::Matcher<const value_type&> inner_matcher_;
83*e7b1675dSTing-Kang Chang };
84*e7b1675dSTing-Kang Chang
85*e7b1675dSTing-Kang Chang // Implements IsOkAndHolds(m) as a polymorphic matcher.
86*e7b1675dSTing-Kang Chang template <typename InnerMatcher>
87*e7b1675dSTing-Kang Chang class IsOkAndHoldsMatcher {
88*e7b1675dSTing-Kang Chang public:
IsOkAndHoldsMatcher(InnerMatcher inner_matcher)89*e7b1675dSTing-Kang Chang explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher)
90*e7b1675dSTing-Kang Chang : inner_matcher_(std::move(inner_matcher)) {}
91*e7b1675dSTing-Kang Chang
92*e7b1675dSTing-Kang Chang // Converts this polymorphic matcher to a monomorphic matcher of the
93*e7b1675dSTing-Kang Chang // given type. StatusOrType can be either StatusOr<T> or a
94*e7b1675dSTing-Kang Chang // reference to StatusOr<T>.
95*e7b1675dSTing-Kang Chang template <typename StatusOrType>
96*e7b1675dSTing-Kang Chang operator ::testing::Matcher<StatusOrType>() const { // NOLINT
97*e7b1675dSTing-Kang Chang return ::testing::Matcher<StatusOrType>(
98*e7b1675dSTing-Kang Chang new IsOkAndHoldsMatcherImpl<const StatusOrType&>(inner_matcher_));
99*e7b1675dSTing-Kang Chang }
100*e7b1675dSTing-Kang Chang
101*e7b1675dSTing-Kang Chang private:
102*e7b1675dSTing-Kang Chang const InnerMatcher inner_matcher_;
103*e7b1675dSTing-Kang Chang };
104*e7b1675dSTing-Kang Chang } // namespace internal
105*e7b1675dSTing-Kang Chang
StatusToString(const util::Status & s)106*e7b1675dSTing-Kang Chang inline std::string StatusToString(const util::Status& s) {
107*e7b1675dSTing-Kang Chang return s.ToString();
108*e7b1675dSTing-Kang Chang }
109*e7b1675dSTing-Kang Chang
110*e7b1675dSTing-Kang Chang template <typename T>
StatusToString(const util::StatusOr<T> & s)111*e7b1675dSTing-Kang Chang std::string StatusToString(const util::StatusOr<T>& s) {
112*e7b1675dSTing-Kang Chang return s.status().ToString();
113*e7b1675dSTing-Kang Chang }
114*e7b1675dSTing-Kang Chang
115*e7b1675dSTing-Kang Chang // Matches a util::StatusOk() value.
116*e7b1675dSTing-Kang Chang // This is better than EXPECT_TRUE(status.ok())
117*e7b1675dSTing-Kang Chang // because the error message is a part of the failure messsage.
118*e7b1675dSTing-Kang Chang MATCHER(IsOk,
119*e7b1675dSTing-Kang Chang absl::StrCat(negation ? "isn't" : "is", " a Status with an OK value")) {
120*e7b1675dSTing-Kang Chang if (arg.ok()) {
121*e7b1675dSTing-Kang Chang return true;
122*e7b1675dSTing-Kang Chang }
123*e7b1675dSTing-Kang Chang *result_listener << StatusToString(arg);
124*e7b1675dSTing-Kang Chang return false;
125*e7b1675dSTing-Kang Chang }
126*e7b1675dSTing-Kang Chang
127*e7b1675dSTing-Kang Chang // Returns a gMock matcher that matches a StatusOr<> whose status is
128*e7b1675dSTing-Kang Chang // OK and whose value matches the inner matcher.
129*e7b1675dSTing-Kang Chang template <typename InnerMatcher>
130*e7b1675dSTing-Kang Chang internal::IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>
IsOkAndHolds(InnerMatcher && inner_matcher)131*e7b1675dSTing-Kang Chang IsOkAndHolds(InnerMatcher&& inner_matcher) {
132*e7b1675dSTing-Kang Chang return internal::IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>(
133*e7b1675dSTing-Kang Chang std::forward<InnerMatcher>(inner_matcher));
134*e7b1675dSTing-Kang Chang }
135*e7b1675dSTing-Kang Chang
136*e7b1675dSTing-Kang Chang // Matches a Status with the specified 'code' as code().
137*e7b1675dSTing-Kang Chang MATCHER_P(StatusIs, code,
138*e7b1675dSTing-Kang Chang "is a Status with a " + absl::StatusCodeToString(code) + " code") {
139*e7b1675dSTing-Kang Chang if (arg.code() == code) {
140*e7b1675dSTing-Kang Chang return true;
141*e7b1675dSTing-Kang Chang }
142*e7b1675dSTing-Kang Chang *result_listener << ::testing::PrintToString(arg);
143*e7b1675dSTing-Kang Chang return false;
144*e7b1675dSTing-Kang Chang }
145*e7b1675dSTing-Kang Chang
146*e7b1675dSTing-Kang Chang // Matches a Status whose code() equals 'code', and whose message() matches
147*e7b1675dSTing-Kang Chang // 'message_macher'.
148*e7b1675dSTing-Kang Chang MATCHER_P2(StatusIs, code, message_matcher, "") {
149*e7b1675dSTing-Kang Chang return (arg.code() == code) &&
150*e7b1675dSTing-Kang Chang testing::Matches(message_matcher)(std::string(arg.message()));
151*e7b1675dSTing-Kang Chang }
152*e7b1675dSTing-Kang Chang
153*e7b1675dSTing-Kang Chang // Matches a Keyset::Key with `key`.
154*e7b1675dSTing-Kang Chang MATCHER_P(EqualsKey, key, "is equals to the expected key") {
155*e7b1675dSTing-Kang Chang if (arg.key_id() == key.key_id() && arg.status() == key.status() &&
156*e7b1675dSTing-Kang Chang arg.output_prefix_type() == key.output_prefix_type() &&
157*e7b1675dSTing-Kang Chang arg.key_data().type_url() == key.key_data().type_url() &&
158*e7b1675dSTing-Kang Chang arg.key_data().key_material_type() ==
159*e7b1675dSTing-Kang Chang key.key_data().key_material_type() &&
160*e7b1675dSTing-Kang Chang arg.key_data().value() == key.key_data().value()) {
161*e7b1675dSTing-Kang Chang return true;
162*e7b1675dSTing-Kang Chang }
163*e7b1675dSTing-Kang Chang *result_listener << arg.DebugString();
164*e7b1675dSTing-Kang Chang return false;
165*e7b1675dSTing-Kang Chang }
166*e7b1675dSTing-Kang Chang
167*e7b1675dSTing-Kang Chang } // namespace test
168*e7b1675dSTing-Kang Chang } // namespace tink
169*e7b1675dSTing-Kang Chang } // namespace crypto
170*e7b1675dSTing-Kang Chang
171*e7b1675dSTing-Kang Chang #endif // TINK_UTIL_TEST_MATCHERS_H_
172