mimic++ v9.2.1
Loading...
Searching...
No Matches
ControlPolicies.hpp
Go to the documentation of this file.
1// Copyright Dominic (DNKpp) Koepke 2024 - 2025.
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_POLICIES_CONTROL_POLICY_HPP
7#define MIMICPP_POLICIES_CONTROL_POLICY_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 <limits>
19 #include <memory>
20 #include <optional>
21 #include <stdexcept>
22 #include <tuple>
23 #include <utility>
24 #include <vector>
25#endif
26
27namespace mimicpp::detail
28{
29 template <typename... Sequences>
30 [[nodiscard]]
31 constexpr std::tuple<std::tuple<std::shared_ptr<Sequences>, sequence::Id>...> make_sequence_entries(
32 util::SourceLocation loc,
33 std::tuple<std::shared_ptr<Sequences>...> const& sequences) noexcept
34 {
35 // This is a workaround due to some issues with clang-17 with c++23 and libstdc++
36 // That configuration prevents the direct initialization, thus we have to default construct first and
37 // setup afterwards. Compilers will probably detect that and optimize accordingly.
38 std::tuple<std::tuple<std::shared_ptr<Sequences>, sequence::Id>...> result{};
39 std::invoke(
40 [&]<std::size_t... indices>([[maybe_unused]] std::index_sequence<indices...> const) noexcept {
41 (..., (std::get<indices>(result) = std::tuple{
42 std::get<indices>(sequences),
43 std::get<indices>(sequences)->add(loc),
44 }));
45 },
46 std::index_sequence_for<Sequences...>{});
47 return result;
48 }
49
50 class TimesConfig
51 {
52 public:
53 [[nodiscard]]
54 TimesConfig() = default;
55
56 [[nodiscard]]
57 constexpr TimesConfig(int const min, int const max)
58 {
59 if (min < 0
60 || max < 0
61 || max < min)
62 {
63 throw std::invalid_argument{
64 "min must be less or equal to max and both must not be less than zero."};
65 }
66
67 m_Min = min;
68 m_Max = max;
69 }
70
71 [[nodiscard]]
72 constexpr int min() const noexcept
73 {
74 return m_Min;
75 }
76
77 [[nodiscard]]
78 constexpr int max() const noexcept
79 {
80 return m_Max;
81 }
82
83 private:
84 int m_Min{1};
85 int m_Max{1};
86 };
87}
88
89namespace mimicpp
90{
91 namespace detail
92 {
93 [[nodiscard]]
94 constexpr std::tuple<std::vector<sequence::rating>, std::vector<reporting::SequenceReport>> gather_sequence_reports(auto const& sequenceEntries)
95 {
96 std::vector<reporting::SequenceReport> inapplicable{};
97 std::vector<sequence::rating> ratings{};
98
99 auto const handleSequence = [&](auto& seq, sequence::Id const id) {
100 if (std::optional const priority = seq->priority_of(id))
101 {
102 ratings.emplace_back(*priority, seq->tag());
103 }
104 else
105 {
106 inapplicable.emplace_back(reporting::make_sequence_report(*seq));
107 }
108 };
109
110 std::apply(
111 [&](auto const&... entries) {
112 (..., handleSequence(std::get<0>(entries), std::get<1>(entries)));
113 },
114 sequenceEntries);
115
116 return {std::move(ratings), std::move(inapplicable)};
117 }
118
119 [[nodiscard]]
120 reporting::control_state_t make_control_state(
121 int const min,
122 int const max,
123 int const count,
124 auto const& sequenceEntries)
125 {
126 if (count == max)
127 {
128 return reporting::state_saturated{
129 .min = min,
130 .max = max,
131 .count = count,
132 .sequences = std::apply(
133 [](auto const&... entries) {
134 return std::vector<reporting::SequenceReport>{reporting::make_sequence_report(*std::get<0>(entries))...};
135 },
136 sequenceEntries)};
137 }
138
139 auto&& [ratings, inapplicable] = gather_sequence_reports(sequenceEntries);
140 if (!std::ranges::empty(inapplicable))
141 {
142 return reporting::state_inapplicable{
143 .min = min,
144 .max = max,
145 .count = count,
146 .sequences = std::move(ratings),
147 .inapplicableSequences = std::move(inapplicable)};
148 }
149
150 return reporting::state_applicable{
151 .min = min,
152 .max = max,
153 .count = count,
154 .sequenceRatings = std::move(ratings),
155 };
156 }
157 }
158
159 template <typename... Sequences>
161 {
162 public:
163 static constexpr std::size_t sequenceCount{sizeof...(Sequences)};
164
165 [[nodiscard]]
166 explicit constexpr ControlPolicy(
168 detail::TimesConfig const& timesConfig,
169 sequence::detail::Config<Sequences...> const& sequenceConfig) noexcept
170 : m_Min{timesConfig.min()},
171 m_Max{timesConfig.max()},
172 m_Sequences{detail::make_sequence_entries(std::move(loc), sequenceConfig.sequences())}
173 {
174 update_sequence_states();
175 }
176
177 [[nodiscard]]
178 constexpr bool is_satisfied() const noexcept
179 {
180 return m_Min <= m_Count
181 && m_Count <= m_Max;
182 }
183
184 [[nodiscard]]
185 constexpr bool is_saturated() const noexcept
186 {
187 return m_Count == m_Max;
188 }
189
190 [[nodiscard]]
191 constexpr bool is_applicable() const noexcept
192 {
193 return m_Count < m_Max
194 && std::apply(
195 [](auto const&... entries) noexcept {
196 return (... && std::get<0>(entries)->is_consumable(std::get<1>(entries)));
197 },
198 m_Sequences);
199 }
200
201 constexpr void consume() noexcept
202 {
203 MIMICPP_ASSERT(is_applicable(), "Policy is inapplicable.");
204
205 std::apply(
206 [](auto&... entries) noexcept {
207 (..., std::get<0>(entries)->consume(std::get<1>(entries)));
208 },
209 m_Sequences);
210
211 ++m_Count;
212 update_sequence_states();
213 }
214
215 [[nodiscard]]
217 {
218 return detail::make_control_state(
219 m_Min,
220 m_Max,
221 m_Count,
222 m_Sequences);
223 }
224
225 private:
226 int m_Min;
227 int m_Max;
228 int m_Count{};
229 std::tuple<
230 std::tuple<std::shared_ptr<Sequences>, sequence::Id>...>
231 m_Sequences{};
232
233 constexpr void update_sequence_states() noexcept
234 {
235 if (m_Count == m_Max)
236 {
237 constexpr auto set_saturated = [](auto& sequence, auto const& id) { sequence->set_saturated(id); };
238 std::apply(
239 [&](auto&... entries) noexcept { (..., std::apply(set_saturated, entries)); },
240 m_Sequences);
241 }
242 else if (m_Count == m_Min)
243 {
244 constexpr auto set_satisfied = [](auto& sequence, auto const& id) { sequence->set_satisfied(id); };
245 std::apply(
246 [&](auto&... entries) noexcept { (..., std::apply(set_satisfied, entries)); },
247 m_Sequences);
248 }
249 }
250 };
251}
252
254{
269
282 [[nodiscard]]
283 constexpr auto times(int const min, int const max)
284 {
285 return mimicpp::detail::TimesConfig{min, max};
286 }
287
297 [[nodiscard]]
298 constexpr auto times(int const exactly)
299 {
300 return mimicpp::detail::TimesConfig(exactly, exactly);
301 }
302
312 [[nodiscard]]
313 constexpr auto at_least(int const min)
314 {
315 return mimicpp::detail::TimesConfig{min, std::numeric_limits<int>::max()};
316 }
317
327 [[nodiscard]]
328 constexpr auto at_most(int const max)
329 {
330 return mimicpp::detail::TimesConfig{0, max};
331 }
332
339 [[nodiscard]]
340 consteval auto never() noexcept
341 {
342 constexpr mimicpp::detail::TimesConfig config{0, 0};
343
344 return config;
345 }
346
354 [[nodiscard]]
355 consteval auto once() noexcept
356 {
357 constexpr mimicpp::detail::TimesConfig config{1, 1};
358
359 return config;
360 }
361
369 [[nodiscard]]
370 consteval auto twice() noexcept
371 {
372 constexpr mimicpp::detail::TimesConfig config{2, 2};
373
374 return config;
375 }
376
381 [[nodiscard]]
382 consteval auto any_times() noexcept
383 {
384 constexpr mimicpp::detail::TimesConfig config{0, std::numeric_limits<int>::max()};
385
386 return config;
387 }
388
392}
393
394#endif
#define MIMICPP_ASSERT(condition, msg)
Definition Config.hpp:51
#define MIMICPP_DETAIL_MODULE_EXPORT
Definition Config.hpp:19
reporting::control_state_t state() const
Definition ControlPolicies.hpp:216
constexpr void consume() noexcept
Definition ControlPolicies.hpp:201
constexpr ControlPolicy(util::SourceLocation loc, detail::TimesConfig const &timesConfig, sequence::detail::Config< Sequences... > const &sequenceConfig) noexcept
Definition ControlPolicies.hpp:166
constexpr bool is_satisfied() const noexcept
Definition ControlPolicies.hpp:178
static constexpr std::size_t sequenceCount
Definition ControlPolicies.hpp:163
constexpr bool is_saturated() const noexcept
Definition ControlPolicies.hpp:185
constexpr bool is_applicable() const noexcept
Definition ControlPolicies.hpp:191
A thin wrapper around general source-location info.
Definition SourceLocation.hpp:38
consteval auto never() noexcept
Specifies a times policy with both limits set to 0.
Definition ControlPolicies.hpp:340
constexpr auto times(int const min, int const max)
Specifies a times policy with a limit range.
Definition ControlPolicies.hpp:283
consteval auto any_times() noexcept
Specifies a times-policy with no constraints on how many times an expectation may match.
Definition ControlPolicies.hpp:382
constexpr auto at_least(int const min)
Specifies a times policy with just a lower limit.
Definition ControlPolicies.hpp:313
consteval auto twice() noexcept
Specifies a times policy with both limits set to 2.
Definition ControlPolicies.hpp:370
consteval auto once() noexcept
Specifies a times policy with both limits set to 1.
Definition ControlPolicies.hpp:355
constexpr auto at_most(int const max)
Specifies a times policy with just an upper limit.
Definition ControlPolicies.hpp:328
constexpr SequenceReport make_sequence_report(sequence::detail::BasicSequence< Id, priorityStrategy > const &seq)
Definition SequenceReport.hpp:36
std::variant< state_inapplicable, state_applicable, state_saturated > control_state_t
Denotes an expectation state.
Definition ExpectationReport.hpp:75
Definition ArgRequirementPolicies.hpp:125
Id
Definition Fwd.hpp:406
Definition Call.hpp:24