• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DNKpp / mimicpp / 18388726968

09 Oct 2025 08:46PM UTC coverage: 92.629% (-5.5%) from 98.112%
18388726968

Pull #138

github

web-flow
Merge b67d4c028 into 798c20ab0
Pull Request #138: CI: Use latest gcovr

1797 of 2079 branches covered (86.44%)

Branch coverage included in aggregate %.

2413 of 2466 relevant lines covered (97.85%)

7268.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.83
/include/mimic++/policies/ControlPolicies.hpp
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"
13
#include "mimic++/config/Config.hpp"
14
#include "mimic++/reporting/ExpectationReport.hpp"
15

16
#ifndef MIMICPP_DETAIL_IS_MODULE
17
    #include <limits>
18
    #include <memory>
19
    #include <optional>
20
    #include <stdexcept>
21
    #include <tuple>
22
    #include <utility>
23
    #include <vector>
24
#endif
25

26
namespace mimicpp::detail
27
{
28
    template <typename... Sequences>
29
    [[nodiscard]]
30
    constexpr std::tuple<std::tuple<std::shared_ptr<Sequences>, sequence::Id>...> make_sequence_entries(
31
        const std::tuple<std::shared_ptr<Sequences>...>& sequences) noexcept
32
    {
33
        // This is a workaround due to some issues with clang-17 with c++23 and libstdc++
34
        // That configuration prevents the direct initialization, thus we have to default construct first and
35
        // setup afterwards. Compilers will probably detect that and optimize accordingly.
36
        std::tuple<std::tuple<std::shared_ptr<Sequences>, sequence::Id>...> result{};
20,744✔
37
        std::invoke(
42,094✔
38
            [&]<std::size_t... indices>([[maybe_unused]] const std::index_sequence<indices...>) noexcept {
20,968✔
39
                ((std::get<indices>(result) =
20,752✔
40
                      std::tuple{
41
                          std::get<indices>(sequences),
20,752✔
42
                          std::get<indices>(sequences)->add(),
20,760✔
43
                      }),
44
                 ...);
45
            },
46
            std::index_sequence_for<Sequences...>{});
47
        return result;
21,047✔
48
    }
49

50
    class TimesConfig
51
    {
52
    public:
53
        [[nodiscard]]
54
        TimesConfig() = default;
55

56
        [[nodiscard]]
57
        constexpr TimesConfig(const int min, const int max)
58
        {
252✔
59
            if (min < 0
252✔
60
                || max < 0
229✔
61
                || max < min)
227✔
62
            {
63
                throw std::invalid_argument{
64
                    "min must be less or equal to max and both must not be less than zero."};
65✔
65
            }
66

67
            m_Min = min;
187✔
68
            m_Max = max;
187✔
69
        }
187✔
70

71
        [[nodiscard]]
72
        constexpr int min() const noexcept
73
        {
74
            return m_Min;
21,116✔
75
        }
76

77
        [[nodiscard]]
78
        constexpr int max() const noexcept
79
        {
80
            return m_Max;
21,116✔
81
        }
82

83
    private:
84
        int m_Min{1};
85
        int m_Max{1};
86
    };
87
}
88

89
namespace mimicpp
90
{
91
    namespace detail
92
    {
93
        [[nodiscard]]
94
        reporting::control_state_t make_control_state(
95
            const int min,
96
            const int max,
97
            const int count,
98
            const auto& sequenceEntries)
99
        {
100
            if (count == max)
48,492✔
101
            {
102
                return reporting::state_saturated{
103
                    .min = min,
104
                    .max = max,
105
                    .count = count,
106
                    .sequences = std::apply(
107
                        [](const auto&... entries) {
×
108
                            return std::vector<sequence::Tag>{
109
                                std::get<0>(entries)->tag()...};
9,566✔
110
                        },
111
                        sequenceEntries)};
3,212✔
112
            }
113

114
            std::vector<sequence::Tag> inapplicable{};
45,280✔
115
            std::vector<sequence::rating> ratings{};
45,280✔
116
            std::apply(
45,280✔
117
                [&](const auto&... entries) {
45,096✔
118
                    (...,
119
                     std::invoke(
44,653✔
120
                         [&](auto& seq, const sequence::Id id) {
44,653✔
121
                             if (const std::optional priority = seq->priority_of(id))
44,653✔
122
                             {
123
                                 ratings.emplace_back(
41,526✔
124
                                     *priority,
125
                                     seq->tag());
83,052✔
126
                             }
127
                             else
128
                             {
129
                                 inapplicable.emplace_back(seq->tag());
3,127✔
130
                             }
131
                         },
132
                         std::get<0>(entries),
44,653✔
133
                         std::get<1>(entries)));
44,653✔
134
                },
135
                sequenceEntries);
136

137
            if (!std::ranges::empty(inapplicable))
45,280✔
138
            {
139
                return reporting::state_inapplicable{
140
                    .min = min,
141
                    .max = max,
142
                    .count = count,
143
                    .sequenceRatings = std::move(ratings),
3,126✔
144
                    .inapplicableSequences = std::move(inapplicable)};
6,252✔
145
            }
146

147
            return reporting::state_applicable{
148
                .min = min,
149
                .max = max,
150
                .count = count,
151
                .sequenceRatings = std::move(ratings),
42,154✔
152
            };
42,154✔
153
        }
145,304✔
154
    }
155

156
    template <typename... Sequences>
157
    class ControlPolicy
158
    {
159
    public:
160
        static constexpr std::size_t sequenceCount{sizeof...(Sequences)};
161

162
        [[nodiscard]]
163
        explicit constexpr ControlPolicy(
164
            const detail::TimesConfig& timesConfig,
165
            const sequence::detail::Config<Sequences...>& sequenceConfig) noexcept
166
            : m_Min{timesConfig.min()},
21,047✔
167
              m_Max{timesConfig.max()},
21,047✔
168
              m_Sequences{
20,744✔
169
                  detail::make_sequence_entries(sequenceConfig.sequences())}
21,047✔
170
        {
171
            update_sequence_states();
21,047✔
172
        }
21,047✔
173

174
        [[nodiscard]]
175
        constexpr bool is_satisfied() const noexcept
176
        {
177
            return m_Min <= m_Count
21,502✔
178
                && m_Count <= m_Max;
21,502!
179
        }
180

181
        [[nodiscard]]
182
        constexpr bool is_saturated() const noexcept
183
        {
184
            return m_Count == m_Max;
185
        }
186

187
        [[nodiscard]]
188
        constexpr bool is_applicable() const noexcept
189
        {
190
            return m_Count < m_Max
21,198✔
191
                && std::apply(
42,395!
192
                       [](const auto&... entries) noexcept {
21,108✔
193
                           return (... && std::get<0>(entries)->is_consumable(std::get<1>(entries)));
21,197!
194
                       },
195
                       m_Sequences);
42,395✔
196
        }
197

198
        constexpr void consume() noexcept
199
        {
200
            MIMICPP_ASSERT(is_applicable(), "Policy is inapplicable.");
201

202
            std::apply(
21,197✔
203
                [](auto&... entries) noexcept {
21,107✔
204
                    (..., std::get<0>(entries)->consume(std::get<1>(entries)));
20,835✔
205
                },
206
                m_Sequences);
21,197✔
207

208
            ++m_Count;
21,197✔
209

210
            update_sequence_states();
21,197✔
211
        }
21,197✔
212

213
        [[nodiscard]]
214
        reporting::control_state_t state() const
215
        {
216
            return detail::make_control_state(
217
                m_Min,
48,492✔
218
                m_Max,
48,492✔
219
                m_Count,
48,492✔
220
                m_Sequences);
48,492✔
221
        }
222

223
    private:
224
        int m_Min;
225
        int m_Max;
226
        int m_Count{};
227
        std::tuple<
228
            std::tuple<std::shared_ptr<Sequences>, sequence::Id>...>
229
            m_Sequences{};
230

231
        constexpr void update_sequence_states() noexcept
232
        {
233
            if (m_Count == m_Min)
42,244✔
234
            {
235
                std::apply(
21,027✔
236
                    [](auto&... entries) noexcept {
20,948✔
237
                        (..., std::get<0>(entries)->set_satisfied(std::get<1>(entries)));
20,733✔
238
                    },
239
                    m_Sequences);
21,027✔
240
            }
241
            else if (m_Count == m_Max)
21,217✔
242
            {
243
                std::apply(
54✔
244
                    [](auto&... entries) noexcept {
52✔
245
                        (..., std::get<0>(entries)->set_saturated(std::get<1>(entries)));
32✔
246
                    },
247
                    m_Sequences);
54✔
248
            }
249
        }
42,244✔
250
    };
251
}
252

253
MIMICPP_DETAIL_MODULE_EXPORT namespace mimicpp::expect
254
{
255
    /**
256
     * \defgroup EXPECTATION_TIMES times
257
     * \ingroup EXPECTATION
258
     * \brief Times indicates, how often an expectation must be matched.
259
     * \details During each expectation building users can specify a times policy once. If not specified, that policy defaults to ``once``.
260
     * If users attempt to specify a times policy more than once for a single expectation, a compile-error will occur.
261
     *
262
     * Times in general have both, a lower and an upper limit. Both limits are treated as inclusive.
263
     *
264
     *\{
265
     */
266

267
    /**
268
     * \brief Specifies a times policy with a limit range.
269
     * \param min The lower limit.
270
     * \param max The upper limit.
271
     * \return The newly created policy.
272
     * \throws std::invalid_argument if
273
     * - ``min < 0``,
274
     * - ``max < 0`` or
275
     * - ``max < min``.
276
     *
277
     * \snippet Times.cpp times
278
     */
279
    [[nodiscard]]
280
    constexpr auto times(const int min, const int max)
281
    {
282
        return mimicpp::detail::TimesConfig{min, max};
104✔
283
    }
284

285
    /**
286
     * \brief Specifies a times policy with an exact limit.
287
     * \param exactly The limit.
288
     * \return The newly created policy.
289
     * \details This requires the expectation to be matched exactly the specified times.
290
     * \throws std::invalid_argument if ``exactly < 0``.
291
     *
292
     * \snippet Times.cpp times single
293
     */
294
    [[nodiscard]]
295
    constexpr auto times(const int exactly)
296
    {
297
        return mimicpp::detail::TimesConfig(exactly, exactly);
21✔
298
    }
299

300
    /**
301
     * \brief Specifies a times policy with just a lower limit.
302
     * \param min The lower limit.
303
     * \return The newly created policy.
304
     * \details This requires the expectation to be matched at least ``min`` times or more.
305
     * \throws std::invalid_argument if ``min < 0``.
306
     *
307
     * \snippet Times.cpp at_least
308
     */
309
    [[nodiscard]]
310
    constexpr auto at_least(const int min)
311
    {
312
        return mimicpp::detail::TimesConfig{
8✔
313
            min,
314
            std::numeric_limits<int>::max()};
16✔
315
    }
316

317
    /**
318
     * \brief Specifies a times policy with just an upper limit.
319
     * \param max The upper limit.
320
     * \return The newly created policy.
321
     * \details This requires the expectation to be matched up to ``max`` times.
322
     * \throws std::invalid_argument if ``max < 0``.
323
     *
324
     * \snippet Times.cpp at_most
325
     */
326
    [[nodiscard]]
327
    constexpr auto at_most(const int max)
328
    {
329
        return mimicpp::detail::TimesConfig{
28✔
330
            0,
331
            max};
56✔
332
    }
333

334
    /**
335
     * \brief Specifies a times policy with both limits set to 0.
336
     * \return The newly created policy.
337
     * \details This requires the expectation to be never matched.
338
     * Useful for explicitly forbidding certain calls.
339
     */
340
    [[nodiscard]]
341
    consteval auto never() noexcept
342
    {
343
        constexpr mimicpp::detail::TimesConfig config{
344
            0,
345
            0};
346

347
        return config;
348
    }
349

350
    /**
351
     * \brief Specifies a times policy with both limits set to 1.
352
     * \return The newly created policy.
353
     * \details This requires the expectation to be matched exactly once.
354
     *
355
     * \snippet Times.cpp once
356
     */
357
    [[nodiscard]]
358
    consteval auto once() noexcept
359
    {
360
        constexpr mimicpp::detail::TimesConfig config{
361
            1,
362
            1};
363

364
        return config;
365
    }
366

367
    /**
368
     * \brief Specifies a times policy with both limits set to 2.
369
     * \return The newly created policy.
370
     * \details This requires the expectation to be matched exactly twice.
371
     *
372
     * \snippet Times.cpp twice
373
     */
374
    [[nodiscard]]
375
    consteval auto twice() noexcept
376
    {
377
        constexpr mimicpp::detail::TimesConfig config{
378
            2,
379
            2};
380

381
        return config;
382
    }
383

384
    /**
385
     * \}
386
     */
387

388
}
389

390
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc