mimic++ v10.0.1
Loading...
Searching...
No Matches
Builder.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_BUILDER_HPP
7#define MIMICPP_EXPECTATION_BUILDER_HPP
8
9#pragma once
10
11#include "mimic++/Fwd.hpp"
23
24#ifndef MIMICPP_DETAIL_IS_MODULE
25 #include <concepts>
26 #include <functional>
27 #include <tuple>
28 #include <type_traits>
29 #include <utility>
30#endif
31
33{
34 namespace detail
35 {
36 struct BuilderState
37 {
38 bool timesConfigured = false;
39 bool finalizeConfigured = false;
40
41 [[nodiscard]]
42 consteval BuilderState with(bool(BuilderState::* option)) const noexcept
43 {
44 MIMICPP_ASSERT(option, "Option must be non-null");
45 BuilderState copy{*this};
46 std::invoke(option, copy) = true;
47
48 return copy;
49 }
50 };
51
52 template <typename SequenceConfig, typename FinalizePolicy, typename... Policies>
53 struct BuilderPolicies
54 {
55 using FinalizePolicyType = FinalizePolicy;
56
57 using TimesConfig = policies::detail::TimesConfig;
58 TimesConfig times{};
59 SequenceConfig sequences{};
60 FinalizePolicy finalize{};
61 std::tuple<Policies...> policies{};
62
63 constexpr auto with_times(TimesConfig newTimes) &&
64 {
65 return BuilderPolicies{
66 std::move(newTimes),
67 std::move(sequences),
68 std::move(finalize),
69 std::move(policies)};
70 }
71
72 template <typename Policy>
73 constexpr auto with_sequences(Policy&& policy) &&
74 {
75 auto newSequences = sequences.concat(std::forward<Policy>(policy));
76
77 return BuilderPolicies<decltype(newSequences), FinalizePolicy, Policies...>{
78 std::move(times),
79 std::move(newSequences),
80 std::move(finalize),
81 std::move(policies)};
82 }
83
84 template <typename Policy>
85 constexpr auto with_finalize(Policy&& policy) &&
86 {
87 return BuilderPolicies<SequenceConfig, std::remove_cvref_t<Policy>, Policies...>{
88 std::move(times),
89 std::move(sequences),
90 std::forward<Policy>(policy),
91 std::move(policies)};
92 }
93
94 template <typename Policy>
95 constexpr auto with_policy(Policy&& policy) &&
96 {
97 return BuilderPolicies<SequenceConfig, FinalizePolicy, Policies..., std::remove_cvref_t<Policy>>{
98 std::move(times),
99 std::move(sequences),
100 std::move(finalize),
101 std::tuple_cat(
102 std::move(policies),
103 std::forward_as_tuple(std::forward<Policy>(policy)))};
104 }
105 };
106 }
107
108 template <typename Signature, detail::BuilderState state, typename Policies>
110 {
111 public:
112 using TimesConfig = policies::detail::TimesConfig;
113
114 BasicBuilder(BasicBuilder const&) = delete;
116
117 ~BasicBuilder() = default;
118
119 [[nodiscard]]
120 explicit BasicBuilder(Registry::Ptr&& registry, reporting::TargetReport&& target, Policies&& policies)
121 : m_Registry{std::move(registry)},
122 m_TargetReport{std::move(target)},
123 m_Policies{std::move(policies)}
124 {
125 MIMICPP_ASSERT(m_Registry, "Null registry is not allowed.");
126 }
127
128 [[nodiscard]]
131
132 template <typename Policy>
134 [[nodiscard]]
135 friend constexpr auto operator&&(BasicBuilder&& builder, Policy&& policy)
136 {
137 static_assert(
138 !std::same_as<policies::InitFinalize, std::remove_cvref_t<Policy>>,
139 "Explicitly specifying the `policies::InitFinalize` is disallowed.");
140
141 static_assert(
142 !state.finalizeConfigured,
143 "Only one finalize-policy may be specified per expectation. "
144 "See: https://dnkpp.github.io/mimicpp/db/d7a/group___e_x_p_e_c_t_a_t_i_o_n___f_i_n_a_l_i_z_e_r.html#details");
145
146 auto newPolicies = std::move(builder.m_Policies).with_finalize(std::forward<Policy>(policy));
147 using Builder = BasicBuilder<
148 Signature,
149 state.with(&detail::BuilderState::finalizeConfigured),
150 decltype(newPolicies)>;
151
152 return Builder{
153 std::move(builder.m_Registry),
154 std::move(builder.m_TargetReport),
155 std::move(newPolicies)};
156 }
157
158 template <typename Policy>
160 [[nodiscard]]
161 friend constexpr auto operator&&(BasicBuilder&& builder, Policy&& policy)
162 {
163 auto newPolicies = std::move(builder.m_Policies).with_policy(std::forward<Policy>(policy));
164 using Builder = BasicBuilder<
165 Signature,
166 state,
167 decltype(newPolicies)>;
168
169 return Builder{
170 std::move(builder.m_Registry),
171 std::move(builder.m_TargetReport),
172 std::move(newPolicies)};
173 }
174
175 [[nodiscard]]
176 friend constexpr auto operator&&(BasicBuilder&& builder, TimesConfig&& config)
177 {
178 static_assert(
179 !state.timesConfigured,
180 "Only one times-policy may be specified per expectation. "
181 "See: https://dnkpp.github.io/mimicpp/d7/d32/group___e_x_p_e_c_t_a_t_i_o_n___t_i_m_e_s.html#details");
182
183 using Builder = BasicBuilder<
184 Signature,
185 state.with(&detail::BuilderState::timesConfigured),
186 Policies>;
187
188 return Builder{
189 std::move(builder.m_Registry),
190 std::move(builder.m_TargetReport),
191 std::move(builder.m_Policies).with_times(std::forward<TimesConfig>(config))};
192 }
193
194 template <typename... Sequences>
195 [[nodiscard]]
196 friend constexpr auto operator&&(BasicBuilder&& builder, sequence::detail::Config<Sequences...>&& config)
197 {
198 auto newPolicies = std::move(builder.m_Policies).with_sequences(std::move(config));
199 using Builder = BasicBuilder<
200 Signature,
201 state,
202 decltype(newPolicies)>;
203
204 return Builder{
205 std::move(builder.m_Registry),
206 std::move(builder.m_TargetReport),
207 std::move(newPolicies)};
208 }
209
210 [[nodiscard]]
212 {
213 static_assert(
215 "For non-void return types, a finalize-policy must be specified. "
216 "See: https://dnkpp.github.io/mimicpp/db/d7a/group___e_x_p_e_c_t_a_t_i_o_n___f_i_n_a_l_i_z_e_r.html#details");
217
218 auto expectation = std::apply(
219 [&](auto&... policies) {
220 return m_Registry->create(
221 std::in_place_type<Signature>,
222 std::move(sourceLocation),
223 std::move(m_TargetReport),
224 policies::ControlPolicy{sourceLocation, std::move(m_Policies.times), std::move(m_Policies.sequences)},
225 std::move(m_Policies.finalize),
226 std::move(policies)...);
227 },
228 m_Policies.policies);
229
230 return Owner{std::move(m_Registry), std::move(expectation)};
231 }
232
233 private:
234 Registry::Ptr m_Registry;
235 reporting::TargetReport m_TargetReport;
236 Policies m_Policies;
237 };
238
239 namespace detail
240 {
241 template <typename Param, typename... Canary, matcher_for<Param> Arg>
242 [[nodiscard]]
243 constexpr auto make_arg_matcher([[maybe_unused]] util::priority_tag<2> const, Arg arg)
244 {
245 return arg;
246 }
247
248 // if the param is a character-pointer, there is no evidence, whether it denotes a null-terminated string or just an
249 // actual pointer to a value.
250 // But, the Mock user shall know it, thus if `Arg` is not a character-pointer, we enable this matcher.
251 template <string Param, typename... Canary, string Arg>
252 requires(!std::is_pointer_v<std::remove_reference_t<Param>>)
253 || (!std::is_pointer_v<std::remove_reference_t<Arg>>)
254 [[nodiscard]] //
255 constexpr auto make_arg_matcher([[maybe_unused]] util::priority_tag<1> const, Arg&& arg)
256 {
257 return matches::str::eq(std::forward<Arg>(arg));
258 }
259
260 template <typename Param, typename... Canary, util::weakly_equality_comparable_with<Param> Arg>
261 [[nodiscard]]
262 constexpr auto make_arg_matcher([[maybe_unused]] util::priority_tag<0> const, Arg&& arg)
263 {
264 return matches::eq(std::forward<Arg>(arg));
265 }
266
267 inline constexpr util::priority_tag<2> maxMakeArgMatcherTag{};
268 }
269
280 template <typename Arg, typename Param>
281 concept requirement_for = requires {
282 {
283 detail::make_arg_matcher<Param>(detail::maxMakeArgMatcherTag, std::declval<Arg>())
285 };
286
287 namespace detail
288 {
289 template <
290 typename Signature,
291 std::size_t index,
292 typename Arg,
293 typename... Canary,
296 constexpr auto make_arg_policy(Arg&& arg)
297 {
298 return expect::arg<index>(
299 detail::make_arg_matcher<Param>(maxMakeArgMatcherTag, std::forward<Arg>(arg)));
300 }
301
302 template <typename Signature, typename Builder, std::size_t... indices, typename... Args>
303 [[nodiscard]]
304 constexpr auto extend_builder_with_arg_policies(
305 Builder&& builder,
306 std::index_sequence<indices...> const /*seq*/,
307 Args&&... args)
308 {
309 return (
310 std::forward<Builder>(builder)
311 && ...
312 && detail::make_arg_policy<Signature, indices>(std::forward<Args>(args)));
313 }
314
315 template <typename Signature, typename... Args>
316 auto make_builder(Registry::Ptr registry, reporting::TargetReport target, Args&&... args)
317 {
318 using Policies = BuilderPolicies<sequence::detail::Config<>, policies::InitFinalize>;
319 using Builder = BasicBuilder<Signature, BuilderState{}, Policies>;
320
321 return detail::extend_builder_with_arg_policies<Signature>(
322 Builder{std::move(registry), std::move(target), Policies{}},
323 std::index_sequence_for<Args...>{},
324 std::forward<Args>(args)...);
325 }
326 }
327}
328
329#endif
#define MIMICPP_ASSERT(condition, msg)
Definition Config.hpp:54
#define MIMICPP_DETAIL_MODULE_EXPORT
Definition Config.hpp:19
BasicBuilder(Registry::Ptr &&registry, reporting::TargetReport &&target, Policies &&policies)
Definition Builder.hpp:120
friend constexpr auto operator&&(BasicBuilder &&builder, TimesConfig &&config)
Definition Builder.hpp:176
BasicBuilder(BasicBuilder const &)=delete
Owner finalize(util::SourceLocation sourceLocation) &&
Definition Builder.hpp:211
BasicBuilder & operator=(BasicBuilder const &)=delete
friend constexpr auto operator&&(BasicBuilder &&builder, Policy &&policy)
Definition Builder.hpp:161
friend constexpr auto operator&&(BasicBuilder &&builder, Policy &&policy)
Definition Builder.hpp:135
BasicBuilder(BasicBuilder &&)=default
policies::detail::TimesConfig TimesConfig
Definition Builder.hpp:112
BasicBuilder & operator=(BasicBuilder &&)=default
friend constexpr auto operator&&(BasicBuilder &&builder, sequence::detail::Config< Sequences... > &&config)
Definition Builder.hpp:196
Takes the ownership of an expectation and checks whether it's satisfied during destruction.
Definition Owner.hpp:42
std::shared_ptr< Registry > Ptr
Definition Registry.hpp:38
Definition ControlPolicies.hpp:161
Contains the extracted mock info.
Definition TargetReport.hpp:22
A thin wrapper around general source-location info.
Definition SourceLocation.hpp:33
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
Determines whether Arg can be used as a requirement for Param.
Definition Builder.hpp:281
Definition Common.hpp:206
constexpr auto arg(Matcher &&matcher, Projection &&projection={}) noexcept(std::is_nothrow_constructible_v< std::remove_cvref_t< Matcher >, Matcher > &&std::is_nothrow_constructible_v< std::remove_cvref_t< Projection >, Projection >)
Checks whether the selected argument satisfies the given matcher.
Definition RequirementPolicies.hpp:233
typename signature_param_type< index, Signature >::type signature_param_type_t
Convenience alias, exposing the type member alias of the actual type-trait.
Definition Fwd.hpp:278
Definition ArgumentList.hpp:24
Definition Builder.hpp:33
Definition PriorityTag.hpp:24