mimic++ v10.0.1
Loading...
Searching...
No Matches
Registry.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_REGISTRY_HPP
7#define MIMICPP_EXPECTATION_REGISTRY_HPP
8
9#pragma once
10
11#include "mimic++/Fwd.hpp"
12#include "mimic++/Sequence.hpp"
16
17#ifndef MIMICPP_DETAIL_IS_MODULE
18 #include <algorithm>
19 #include <mutex>
20 #include <optional>
21 #include <ranges>
22 #include <span>
23 #include <tuple>
24 #include <utility>
25 #include <vector>
26#endif
27
29{
36 {
37 public:
38 using Ptr = std::shared_ptr<Registry>;
39
43 Registry(Registry const&) = delete;
44
48 Registry& operator=(Registry const&) = delete;
49
53 [[nodiscard]]
54 Registry(Registry&&) = delete;
55
60
64 ~Registry() = default;
65
69 [[nodiscard]]
70 Registry() = default;
71
76 template <typename... Args>
77 requires std::constructible_from<Expectation, Args...>
78 [[nodiscard]]
79 Expectation create(Args&&... args)
80 {
81 std::scoped_lock const lock{m_ExpectationsMx};
82 return m_Expectations.emplace_back(std::forward<Args>(args)...);
83 }
84
93 {
94 std::scoped_lock const lock{m_ExpectationsMx};
95
96 auto const iter = std::ranges::find(m_Expectations, expectation);
97 MIMICPP_ASSERT(iter != m_Expectations.cend(), "Expectation does not belong to this registry.");
98 m_Expectations.erase(iter);
99
100 if (!expectation.is_satisfied())
101 {
102 reporting::detail::report_unfulfilled_expectation(expectation.report());
103 }
104 }
105
116 template <typename Signature>
117 [[nodiscard]]
119 {
120 std::scoped_lock const lock{m_ExpectationsMx};
121 auto&& [matches, inapplicableMatches, noMatches] = evaluate_expectations(target, call);
122
123 std::size_t const stacktraceSkip{1u + call.baseStacktraceSkip};
124 if (!std::ranges::empty(matches))
125 {
126 return handle_matched_call<Signature>(
127 std::move(target),
128 std::move(call),
129 std::move(matches),
130 stacktraceSkip + 1u);
131 }
132
133 if (!std::ranges::empty(inapplicableMatches))
134 {
135 reporting::detail::report_inapplicable_matches(
136 reporting::make_call_report(std::move(target), std::move(call), util::stacktrace::current(stacktraceSkip)),
137 make_match_reports(std::move(inapplicableMatches)));
138 }
139
140 reporting::detail::report_no_matches(
141 reporting::make_call_report(std::move(target), std::move(call), util::stacktrace::current(stacktraceSkip)),
142 make_match_reports(std::move(noMatches)));
143 }
144
145 private:
146 std::vector<Expectation> m_Expectations{};
147 std::mutex m_ExpectationsMx{};
148
149 [[nodiscard]]
150 static std::optional<MatchResults> try_match_call(
151 reporting::TargetReport const& target,
152 auto const& callInfo,
153 Expectation const& expectation) noexcept
154 {
155 try
156 {
157 return expectation.matches(callInfo);
158 }
159 catch (...)
160 {
161 reporting::detail::report_unhandled_exception(
163 target,
164 callInfo,
165 util::stacktrace::current(3u + callInfo.baseStacktraceSkip)),
166 expectation.report(),
167 std::current_exception());
168 }
169
170 return std::nullopt;
171 }
172
173 struct evaluation_results
174 {
175 struct entry
176 {
177 std::reference_wrapper<Expectation> expectation;
179
180 [[nodiscard]]
182 {
184 .expectationReport = expectation.get().report(),
185 .matchResults = std::move(results),
186 };
187 }
188 };
189
190 std::vector<entry> matches{};
191 std::vector<entry> inapplicableMatches{};
192 std::vector<entry> noMatches{};
193 };
194
195 [[nodiscard]]
196 auto evaluate_expectations(reporting::TargetReport const& target, auto const& callInfo)
197 {
198 evaluation_results results{};
199
200 for (auto& exp : std::views::reverse(m_Expectations))
201 {
202 if (std::optional result = try_match_call(target, callInfo, exp))
203 {
204 if (std::ranges::any_of(*result, [](auto const& el) { return std::holds_alternative<MatchFailure>(el); }))
205 {
206 results.noMatches.emplace_back(exp, *std::move(result));
207 }
208 else if (!exp.is_applicable())
209 {
210 results.inapplicableMatches.emplace_back(exp, *std::move(result));
211 }
212 else
213 {
214 results.matches.emplace_back(exp, *std::move(result));
215 }
216 }
217 }
218
219 return results;
220 }
221
222 template <typename Signature>
223 [[nodiscard]]
224 signature_return_type_t<Signature> handle_matched_call(
225 reporting::TargetReport&& target,
227 std::vector<evaluation_results::entry>&& matches,
228 std::size_t const stacktraceSkip)
229 {
230 std::vector reports = std::invoke([&] {
231 auto view = std::views::transform(
232 matches,
233 [](auto const& entry) { return entry.expectation.get().report(); });
234 return std::vector(view.begin(), view.end());
235 });
236 MIMICPP_ASSERT(matches.size() == reports.size(), "Size mismatch.");
237 auto const bestIter = std::ranges::max_element(
238 reports,
239 std::not_fn(&sequence::detail::has_better_rating),
240 [](reporting::ExpectationReport const& el) noexcept -> const auto& {
241 return std::get<reporting::state_applicable>(el.controlReport).sequenceRatings;
242 });
243 auto& bestMatch = matches[std::ranges::distance(reports.cbegin(), bestIter)];
244
246 {
247 // Todo: Avoid the call copy
248 // Maybe we can prevent the copy here, but we should keep the instruction order as-is,
249 // because in cases of a throwing finalizer, we might introduce bugs.
250 // At least there are some tests, which will fail if done incorrectly.
251 reporting::detail::report_full_match(
252 reporting::make_call_report(std::move(target), callInfo, util::stacktrace::current(stacktraceSkip)),
253 {.expectationReport = std::move(*bestIter), .matchResults = std::move(bestMatch.results)});
254 }
255
256 return bestMatch.expectation.get().consume(callInfo);
257 }
258
259 [[nodiscard]]
260 static std::vector<reporting::MatchReport> make_match_reports(std::vector<evaluation_results::entry>&& outcomes)
261 {
262 std::vector<reporting::MatchReport> reports{};
263 reports.reserve(outcomes.size());
264 std::ranges::transform(
265 outcomes,
266 std::back_inserter(reports),
267 [](evaluation_results::entry& el) { return std::move(el).to_match_report(); });
268
269 return reports;
270 }
271 };
272}
273
274#endif
#define MIMICPP_ASSERT(condition, msg)
Definition Config.hpp:54
#define MIMICPP_DETAIL_MODULE_EXPORT
Definition Config.hpp:19
The type-erased expectation type with shared ownership.
Definition Expectation.hpp:70
Registry()=default
Defaulted default constructor.
Expectation create(Args &&... args)
Creates a new expectation from the given arguments.
Definition Registry.hpp:79
Registry & operator=(Registry &&)=delete
Deleted move-assignment-operator.
void remove(Expectation const &expectation)
Removes the given expectation from the internal storage.
Definition Registry.hpp:92
Registry(Registry &&)=delete
Deleted move-constructor.
~Registry()=default
Defaulted destructor.
std::shared_ptr< Registry > Ptr
Definition Registry.hpp:38
Registry(Registry const &)=delete
Deleted copy-constructor.
Registry & operator=(Registry const &)=delete
Deleted copy-assignment-operator.
signature_return_type_t< Signature > handle_call(reporting::TargetReport target, call::info_for_signature_t< Signature > call)
Handles the incoming call.
Definition Registry.hpp:118
Contains the expectation-report and the match-results to a specific call.
Definition MatchReport.hpp:22
Contains the extracted mock info.
Definition TargetReport.hpp:22
CallReport make_call_report(TargetReport target, call::Info< Return, Params... > callInfo, util::Stacktrace stacktrace)
Generates the call report for a given call info.
Definition CallReport.hpp:79
std::atomic_bool & report_success() noexcept
Controls whether successful matches are reported.
Definition Settings.hpp:32
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
constexpr detail::current_fn current
Function object, which generates the current-stacktrace.
Definition Stacktrace.hpp:473
Definition Call.hpp:24
typename info_for_signature< Signature >::type info_for_signature_t
Definition Call.hpp:47
Definition Builder.hpp:33
std::vector< MatchResult > MatchResults
Definition Common.hpp:42
Definition FloatingPointMatchers.hpp:25
reporting::MatchReport to_match_report() &&
Definition Registry.hpp:181
MatchResults results
Definition Registry.hpp:178
std::reference_wrapper< Expectation > expectation
Definition Registry.hpp:177