xref: /aosp_15_r20/external/federated-compute/fcp/testing/result_matchers.h (revision 14675a029014e728ec732f129a32e299b2da0601)
1 /*
2  * Copyright 2019 Google LLC
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 #ifndef FCP_TESTING_RESULT_MATCHERS_H_
18 #define FCP_TESTING_RESULT_MATCHERS_H_
19 
20 #include <string>
21 
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
24 #include "fcp/base/error.h"
25 #include "fcp/base/result.h"
26 
27 namespace fcp {
28 
29 // Allows to formulate test expectation on a result containing error as:
30 // EXPECT_THAT(result, IsError());
31 MATCHER(IsError, "") { return arg.is_error(); }
32 
33 // Allows to formulate test expectation on a non-error result with existing
34 // gtest matchers (such as Eq) as:
35 // EXPECT_THAT(result, HasValue(Eq(value)));
36 template <typename MatcherType>
37 class HasValueMatcher {
38  public:
HasValueMatcher(MatcherType matcher)39   explicit HasValueMatcher(MatcherType matcher)
40       : matcher_(std::move(matcher)) {}
41 
42   template <typename TargetType>
43   operator testing::Matcher<TargetType>() const {  // NOLINT
44     using D = std::remove_cv_t<std::remove_reference_t<TargetType>>;
45     static_assert(result_internal::ResultTraits<D>::is_result());
46     using V = typename result_internal::ResultTraits<D>::ValueType;
47     return testing::Matcher<TargetType>(
48         new Impl<V>(testing::SafeMatcherCast<V const&>(matcher_)));
49   }
50 
51  private:
52   template <typename ValueType>
53   class Impl : public testing::MatcherInterface<Result<ValueType> const&> {
54    public:
Impl(testing::Matcher<ValueType const &> matcher)55     explicit Impl(testing::Matcher<ValueType const&> matcher)
56         : concrete_matcher_(std::move(matcher)) {}
57 
58     bool MatchAndExplain(
59         Result<ValueType> const& arg,
60         testing::MatchResultListener* result_listener) const override;
61 
DescribeTo(std::ostream * os)62     void DescribeTo(std::ostream* os) const override {
63       *os << FormatDescription(false);
64     }
65 
DescribeNegationTo(std::ostream * os)66     void DescribeNegationTo(std::ostream* os) const override {
67       *os << FormatDescription(true);
68     }
69 
70    private:
71     std::string FormatDescription(bool negation) const;
72     testing::Matcher<ValueType const&> concrete_matcher_;
73   };
74 
75   MatcherType matcher_;
76 };
77 
78 template <typename MatcherType>
HasValue(MatcherType matcher)79 HasValueMatcher<MatcherType> HasValue(MatcherType matcher) {
80   return HasValueMatcher<MatcherType>(std::move(matcher));
81 }
82 
83 template <typename MatcherType>
84 template <typename ValueType>
MatchAndExplain(Result<ValueType> const & arg,testing::MatchResultListener * result_listener)85 bool HasValueMatcher<MatcherType>::Impl<ValueType>::MatchAndExplain(
86     Result<ValueType> const& arg,
87     testing::MatchResultListener* result_listener) const {
88   if (arg.is_error()) {
89     *result_listener << "is error";
90     return false;
91   } else {
92     ValueType const& value = arg.GetValueOrDie();
93     *result_listener << "value = " << testing::PrintToString(value);
94     return testing::ExplainMatchResult(concrete_matcher_, value,
95                                        result_listener);
96   }
97 }
98 
99 template <typename MatcherType>
100 template <typename ValueType>
FormatDescription(bool negation)101 std::string HasValueMatcher<MatcherType>::Impl<ValueType>::FormatDescription(
102     bool negation) const {
103   std::stringstream desc;
104   if (negation) {
105     concrete_matcher_.DescribeNegationTo(&desc);
106   } else {
107     concrete_matcher_.DescribeTo(&desc);
108   }
109   return desc.str();
110 }
111 
112 // Expect a particular status for testing failure modes of protocols.
113 // Prefer ExpectOk (defined in result.h) for OK status.
114 template <fcp::StatusCode Code>
115 struct ExpectStatus : public ExpectBase {
116   using ExpectBase::ExpectBase;
117   constexpr explicit ExpectStatus(
118       SourceLocation loc = SourceLocation::current())
ExpectBaseExpectStatus119       : ExpectBase(loc) {}
120 
operatorExpectStatus121   Result<Unit> operator()(const Status& s) const {
122     if (s.code() == Code) {
123       return Unit{};
124     } else {
125       return TraceUnexpectedStatus(Code, s);
126     }
127   }
128 };
129 
130 }  // namespace fcp
131 
132 #endif  // FCP_TESTING_RESULT_MATCHERS_H_
133