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