1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <gmock/gmock.h>
20 #include <gtest/gtest.h>
21 
22 #include <ostream>
23 #include <type_traits>
24 
25 namespace android::error {
26 
27 /**
28  * Example Usage:
29  * Given a function with signature
30  *       Result<T, U> foo()
31  * Matchers can be used as follows:
32  *       EXPECT_THAT(foo(), IsOkAnd(Eq(T{})));
33  *       EXPECT_THAT(foo(), IsErrorAnd(Eq(U{})));
34  */
35 template <typename ExpectedT>
36 class IsOkAndImpl : public ::testing::MatcherInterface<ExpectedT> {
37   public:
38     using ValueT = std::remove_reference_t<ExpectedT>::value_type;
39 
40     template <typename InnerMatcher>
IsOkAndImpl(InnerMatcher innerMatcher)41     explicit IsOkAndImpl(InnerMatcher innerMatcher)
42         : inner_matcher_(::testing::SafeMatcherCast<const ValueT&>(
43                   std::forward<InnerMatcher>(innerMatcher))) {}
44 
MatchAndExplain(ExpectedT val,::testing::MatchResultListener * listener)45     bool MatchAndExplain(ExpectedT val, ::testing::MatchResultListener* listener) const {
46         if (!val.has_value()) {
47             *listener << "which has error " << ::testing::PrintToString(val.error());
48             return false;
49         }
50         const auto res = inner_matcher_.MatchAndExplain(val.value(), listener);
51         if (!res) {
52             *listener << "which has value " << ::testing::PrintToString(val.value());
53         }
54         return res;
55     }
56 
DescribeTo(std::ostream * os)57     void DescribeTo(std::ostream* os) const {
58         *os << "contains expected value which ";
59         inner_matcher_.DescribeTo(os);
60     }
61 
DescribeNegationTo(std::ostream * os)62     void DescribeNegationTo(std::ostream* os) const {
63         *os << "does not contain expected, or contains expected value which ";
64         inner_matcher_.DescribeNegationTo(os);
65     }
66 
67   private:
68     ::testing::Matcher<const ValueT&> inner_matcher_;
69 };
70 
71 template <typename InnerMatcher>
72 class IsOkAnd {
73   public:
IsOkAnd(InnerMatcher innerMatcher)74     explicit IsOkAnd(InnerMatcher innerMatcher) : inner_matcher_(std::move(innerMatcher)) {}
75 
76     template <typename T>
77     operator ::testing::Matcher<T>() const {
78         return ::testing::Matcher<T>{new IsOkAndImpl<const T&>(inner_matcher_)};
79     }
80 
81   private:
82     InnerMatcher inner_matcher_;
83 };
84 
85 template <typename ExpectedT>
86 class IsErrorAndImpl : public ::testing::MatcherInterface<ExpectedT> {
87   public:
88     using ErrorT = typename std::remove_reference_t<ExpectedT>::error_type;
89 
90     template <typename InnerMatcher>
IsErrorAndImpl(InnerMatcher innerMatcher)91     explicit IsErrorAndImpl(InnerMatcher innerMatcher)
92         : inner_matcher_(::testing::SafeMatcherCast<const ErrorT&>(
93                   std::forward<InnerMatcher>(innerMatcher))) {}
94 
MatchAndExplain(ExpectedT val,::testing::MatchResultListener * listener)95     bool MatchAndExplain(ExpectedT val, ::testing::MatchResultListener* listener) const {
96         if (val.has_value()) {
97             *listener << "which has value " << ::testing::PrintToString(val.value());
98             return false;
99         }
100 
101         const auto res = inner_matcher_.MatchAndExplain(val.error(), listener);
102         if (!res) {
103             *listener << "which has error " << ::testing::PrintToString(val.error());
104         }
105         return res;
106     }
107 
DescribeTo(std::ostream * os)108     void DescribeTo(std::ostream* os) const {
109         *os << "contains error value which ";
110         inner_matcher_.DescribeTo(os);
111     }
112 
DescribeNegationTo(std::ostream * os)113     void DescribeNegationTo(std::ostream* os) const {
114         *os << "does not contain error value, or contains error value which ";
115         inner_matcher_.DescribeNegationTo(os);
116     }
117 
118   private:
119     ::testing::Matcher<const ErrorT&> inner_matcher_;
120 };
121 
122 template <typename InnerMatcher>
123 class IsErrorAnd {
124   public:
IsErrorAnd(InnerMatcher innerMatcher)125     explicit IsErrorAnd(InnerMatcher innerMatcher) : inner_matcher_(std::move(innerMatcher)) {}
126 
127     template <typename T>
128     operator ::testing::Matcher<T>() const {
129         return ::testing::Matcher<T>{new IsErrorAndImpl<const T&>(inner_matcher_)};
130     }
131 
132   private:
133     InnerMatcher inner_matcher_;
134 };
135 
136 }  // namespace android::error
137