mimic++ v10.0.1
Loading...
Searching...
No Matches
Expectation.hpp
Go to the documentation of this file.
1// Copyright Dominic (DNKpp) Koepke 2024-2026.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at
4// https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef MIMICPP_EXPECTATION_EXPECTATION_HPP
7#define MIMICPP_EXPECTATION_EXPECTATION_HPP
8
9#pragma once
10
11#include "mimic++/Call.hpp"
12#include "mimic++/Fwd.hpp"
17
18#ifndef MIMICPP_DETAIL_IS_MODULE
19 #include <any>
20 #include <memory>
21 #include <optional>
22 #include <tuple>
23 #include <type_traits>
24 #include <utility>
25 #include <vector>
26#endif
27
29{
63
70 {
71 private:
72 class Concept
73 {
74 public:
75 virtual ~Concept() noexcept(false)
76 {
77 }
78
79 Concept(const Concept&) = delete;
80 Concept& operator=(const Concept&) = delete;
81 Concept(Concept&&) = delete;
82 Concept& operator=(Concept&&) = delete;
83
84 [[nodiscard]]
85 virtual util::SourceLocation const& from() const noexcept = 0;
86
87 [[nodiscard]]
88 virtual StringT const& mock_name() const noexcept = 0;
89
90 [[nodiscard]]
91 virtual reporting::ExpectationReport report() const = 0;
92
93 [[nodiscard]]
94 virtual bool is_satisfied() const noexcept = 0;
95
96 [[nodiscard]]
97 virtual bool is_applicable() const noexcept = 0;
98
99 [[nodiscard]]
100 virtual std::optional<MatchResults> matches(std::any&& opaqueCallInfo) const = 0;
101
102 [[nodiscard]]
103 virtual std::any consume(std::any&& opaqueCallInfo) = 0;
104
105 protected:
106 Concept() = default;
107 };
108
109 template <
110 typename Signature,
111 control_policy ControlPolicy,
112 finalize_policy_for<Signature> FinalizePolicy,
114 class Model final
115 : public Concept
116 {
117 public:
118 using CallInfoRef = std::reference_wrapper<call::info_for_signature_t<Signature> const>;
120 using PolicyList = std::tuple<Policies...>;
121
122 template <typename ControlPolicyArg, typename FinalizerArg, typename... PolicyArgs>
123 [[nodiscard]]
124 explicit constexpr Model(
127 ControlPolicyArg&& controlArg,
128 FinalizerArg&& finalizerArg,
129 PolicyArgs&&... args)
130 : m_From{std::move(from)},
131 m_Target{std::move(target)},
132 m_ControlPolicy{std::forward<ControlPolicyArg>(controlArg)},
133 m_Policies{std::forward<PolicyArgs>(args)...},
134 m_Finalizer{std::forward<FinalizerArg>(finalizerArg)}
135 {
136 }
137
138 [[nodiscard]]
139 util::SourceLocation const& from() const noexcept override
140 {
141 return m_From;
142 }
143
144 [[nodiscard]]
145 StringT const& mock_name() const noexcept override
146 {
147 return m_Target.name;
148 }
149
150 [[nodiscard]]
151 reporting::ExpectationReport report() const override
152 {
154 .from = m_From,
155 .target = m_Target,
156 .controlReport = m_ControlPolicy.state(),
157 .finalizerDescription = std::nullopt,
158 };
159 }
160
161 [[nodiscard]]
162 bool is_satisfied() const noexcept override
163 {
164 return m_ControlPolicy.is_satisfied()
165 && std::apply(
166 [](auto const&... policies) { return (... && policies.is_satisfied()); },
167 m_Policies);
168 }
169
170 [[nodiscard]]
171 bool is_applicable() const noexcept override
172 {
173 return std::holds_alternative<reporting::state_applicable>(m_ControlPolicy.state());
174 }
175
176 [[nodiscard]]
177 std::optional<MatchResults> matches(std::any&& opaqueCallInfo) const override
178 {
179 MIMICPP_ASSERT(opaqueCallInfo.has_value(), "Empty call is not allowed.");
180 if (auto const* const info = std::any_cast<CallInfoRef>(&opaqueCallInfo))
181 {
182 return {gather_requirement_outcomes(info->get())};
183 }
184
185 return std::nullopt;
186 }
187
188 [[nodiscard]]
189 std::any consume(std::any&& opaqueCallInfo) override
190 {
191 auto const* const info = std::any_cast<CallInfoRef>(&opaqueCallInfo);
192 MIMICPP_ASSERT(info, "Invalid call type.");
193
194 m_ControlPolicy.consume();
195 std::apply(
196 [&](auto&... policies) { (..., policies.consume(info->get())); },
197 m_Policies);
198
199 return finalize(info->get());
200 }
201
202 private:
205 ControlPolicy m_ControlPolicy;
206 PolicyList m_Policies;
207 [[no_unique_address]] FinalizePolicy m_Finalizer{};
208
209 [[nodiscard]]
210 MatchResults gather_requirement_outcomes(call::info_for_signature_t<Signature> const& call) const
211 {
212 return std::apply(
213 [&](auto const&... policies) { return MatchResults{policies.matches(call)...}; },
214 m_Policies);
215 }
216
217 [[nodiscard]]
218 std::any finalize(call::info_for_signature_t<Signature> const& call)
219 {
220 // Workaround: MSVC (x86, Release) emits C4702 "unreachable code" here when the finalizer always throws (e.g. finally::throws),
221 // because the optimizer inlines it.
222 // By constructing the `result` first empty and then re-assigning it, seems to please it.
223 std::any result{};
224
225 if constexpr (std::is_void_v<Return>)
226 {
227 m_Finalizer.finalize_call(call);
228 result = std::make_any<call::ResultStorage<void>>();
229 }
230 else
231 {
232 result = std::make_any<call::ResultStorage<Return>>(m_Finalizer.finalize_call(call));
233 }
234
235 return result;
236 }
237 };
238
239 public:
243 virtual ~Expectation() = default;
244
248 Expectation(Expectation const&) = default;
249
254
259
264
277 template <
278 typename Signature,
279 control_policy ControlPolicy,
280 finalize_policy_for<Signature> FinalizePolicy,
282 [[nodiscard]] //
283 explicit Expectation(
284 std::in_place_type_t<Signature> const /*signatureTag*/,
287 ControlPolicy controlPolicy,
288 FinalizePolicy finalizePolicy,
289 Policies... policies)
290 : m_Inner{
291 std::make_shared<Model<Signature, ControlPolicy, FinalizePolicy, Policies...>>(
292 std::move(from),
293 std::move(target),
294 std::move(controlPolicy),
295 std::move(finalizePolicy),
296 std::move(policies)...)}
297 {
298 }
299
304 [[nodiscard]]
305 util::SourceLocation const& from() const noexcept
306 {
307 return m_Inner->from();
308 }
309
314 [[nodiscard]]
315 StringT const& mock_name() const noexcept
316 {
317 return m_Inner->mock_name();
318 }
319
324 [[nodiscard]]
326 {
327 return m_Inner->report();
328 }
329
334 [[nodiscard]]
335 bool is_satisfied() const noexcept
336 {
337 return m_Inner->is_satisfied();
338 }
339
344 [[nodiscard]]
345 bool is_applicable() const noexcept
346 {
347 return m_Inner->is_applicable();
348 }
349
356 template <typename Return, typename... Args>
357 [[nodiscard]]
358 std::optional<MatchResults> matches(call::Info<Return, Args...> const& callInfo) const
359 {
360 return m_Inner->matches(std::any{std::ref(callInfo)});
361 }
362
368 template <typename Return, typename... Args>
369 [[nodiscard]]
370 Return consume(call::Info<Return, Args...> const& callInfo)
371 {
372 std::any opaqueResult = m_Inner->consume(std::any{std::ref(callInfo)});
373 auto* const resultStorage = std::any_cast<call::ResultStorage<Return>>(&opaqueResult);
374 MIMICPP_ASSERT(resultStorage, "Invalid result-type.");
375
376 return resultStorage->extract();
377 }
378
379 [[nodiscard]]
380 friend bool operator==(Expectation const&, Expectation const&) = default;
381
382 private:
383 std::shared_ptr<Concept> m_Inner;
384 };
385
389}
390
391#endif
#define MIMICPP_ASSERT(condition, msg)
Definition Config.hpp:54
Definition Call.hpp:27
bool is_satisfied() const noexcept
Queries all policies, whether they are satisfied.
Definition Expectation.hpp:335
bool is_applicable() const noexcept
Queries the control policy, whether it's in the applicable state.
Definition Expectation.hpp:345
Expectation & operator=(Expectation const &)=default
Defaulted copy-assignment-operator.
Expectation(std::in_place_type_t< Signature > const, util::SourceLocation from, reporting::TargetReport target, ControlPolicy controlPolicy, FinalizePolicy finalizePolicy, Policies... policies)
Constructs the expectation with the given arguments.
Definition Expectation.hpp:283
StringT const & mock_name() const noexcept
Returns the name of the related mock.
Definition Expectation.hpp:315
virtual ~Expectation()=default
Defaulted virtual destructor.
reporting::ExpectationReport report() const
Creates a report of the internal state.
Definition Expectation.hpp:325
util::SourceLocation const & from() const noexcept
Returns the source-location, where this expectation was created.
Definition Expectation.hpp:305
std::optional< MatchResults > matches(call::Info< Return, Args... > const &callInfo) const
Queries all policies, whether they accept the given call.
Definition Expectation.hpp:358
Expectation(Expectation &&)=default
Defaulted move-constructor.
friend bool operator==(Expectation const &, Expectation const &)=default
Return consume(call::Info< Return, Args... > const &callInfo)
Informs all policies that the given call was accepted.
Definition Expectation.hpp:370
Expectation & operator=(Expectation &&)=default
Defaulted move-assignment-operator.
Expectation(Expectation const &)=default
Defaulted copy-constructor.
Contains the extracted info from a typed expectation.
Definition ExpectationReport.hpp:85
Contains the extracted mock info.
Definition TargetReport.hpp:22
StringT name
Definition TargetReport.hpp:24
A thin wrapper around general source-location info.
Definition SourceLocation.hpp:33
Determines whether the given type satisfies the requirements of a control-policy.
Definition Common.hpp:75
Determines whether the given type satisfies the requirements of an expectation-policy for the given s...
Definition Common.hpp:49
Determines whether the given type satisfies the requirements of a finalize-policy for the given signa...
Definition Common.hpp:63
typename signature_return_type< Signature >::type signature_return_type_t
Convenience alias, exposing the type member alias of the actual type-trait.
Definition Fwd.hpp:230
Definition Call.hpp:24
typename info_for_signature< Signature >::type info_for_signature_t
Definition Call.hpp:47
Definition ArgumentList.hpp:24
Definition Builder.hpp:33
std::vector< MatchResult > MatchResults
Definition Common.hpp:42
std::basic_string< CharT, CharTraitsT > StringT
Definition Fwd.hpp:397