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

DNKpp / mimicpp / 19786733300

29 Nov 2025 04:59PM UTC coverage: 90.47% (-7.8%) from 98.254%
19786733300

Pull #138

github

web-flow
Merge 104959d5a into 4e0d69781
Pull Request #138: CI: Use latest gcovr

1822 of 2223 branches covered (81.96%)

Branch coverage included in aggregate %.

2450 of 2499 relevant lines covered (98.04%)

7444.51 hits per line

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

95.0
/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
#include "mimic++/reporting/SequenceReport.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

27
namespace 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{};
20,808✔
39
        std::invoke(
42,338✔
40
            [&]<std::size_t... indices>([[maybe_unused]] std::index_sequence<indices...> const) noexcept {
21,084✔
41
                (..., (std::get<indices>(result) = std::tuple{
20,816✔
42
                           std::get<indices>(sequences),
20,816✔
43
                           std::get<indices>(sequences)->add(loc),
20,824✔
44
                       }));
45
            },
46
            std::index_sequence_for<Sequences...>{});
47
        return result;
21,169✔
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
        {
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,239✔
75
        }
76

77
        [[nodiscard]]
78
        constexpr int max() const noexcept
79
        {
80
            return m_Max;
21,239✔
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
        constexpr std::tuple<std::vector<sequence::rating>, std::vector<reporting::SequenceReport>> gather_sequence_reports(auto const& sequenceEntries)
95
        {
96
            std::vector<reporting::SequenceReport> inapplicable{};
44,768✔
97
            std::vector<sequence::rating> ratings{};
44,768✔
98

99
            auto const handleSequence = [&](auto& seq, sequence::Id const id) {
44,768✔
100
                if (std::optional const priority = seq->priority_of(id))
44,781✔
101
                {
102
                    ratings.emplace_back(*priority, seq->tag());
41,654✔
103
                }
104
                else
105
                {
106
                    inapplicable.emplace_back(reporting::make_sequence_report(*seq));
3,127✔
107
                }
108
            };
109

110
            std::apply(
44,768✔
111
                [&](auto const&... entries) {
44,768✔
112
                    (..., handleSequence(std::get<0>(entries), std::get<1>(entries)));
44,768✔
113
                },
114
                sequenceEntries);
115

116
            return {std::move(ratings), std::move(inapplicable)};
134,304✔
117
        }
44,768✔
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)
48,734✔
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))...};
9,566✔
135
                        },
136
                        sequenceEntries)};
3,212✔
137
            }
138

139
            auto&& [ratings, inapplicable] = gather_sequence_reports(sequenceEntries);
45,522✔
140
            if (!std::ranges::empty(inapplicable))
45,522!
141
            {
142
                return reporting::state_inapplicable{
143
                    .min = min,
144
                    .max = max,
145
                    .count = count,
146
                    .sequences = std::move(ratings),
3,126✔
147
                    .inapplicableSequences = std::move(inapplicable)};
6,252✔
148
            }
149

150
            return reporting::state_applicable{
151
                .min = min,
152
                .max = max,
153
                .count = count,
154
                .sequenceRatings = std::move(ratings),
42,396✔
155
            };
42,396✔
156
        }
191,552✔
157
    }
158

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

165
        [[nodiscard]]
166
        explicit constexpr ControlPolicy(
167
            util::SourceLocation loc,
168
            detail::TimesConfig const& timesConfig,
169
            sequence::detail::Config<Sequences...> const& sequenceConfig) noexcept
170
            : m_Min{timesConfig.min()},
21,169✔
171
              m_Max{timesConfig.max()},
21,169✔
172
              m_Sequences{detail::make_sequence_entries(std::move(loc), sequenceConfig.sequences())}
42,338✔
173
        {
174
            update_sequence_states();
21,169✔
175
        }
21,169✔
176

177
        [[nodiscard]]
178
        constexpr bool is_satisfied() const noexcept
179
        {
180
            return m_Min <= m_Count
21,624✔
181
                && m_Count <= m_Max;
21,624!
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
21,319✔
194
                && std::apply(
42,637!
195
                       [](auto const&... entries) noexcept {
21,222✔
196
                           return (... && std::get<0>(entries)->is_consumable(std::get<1>(entries)));
21,318!
197
                       },
198
                       m_Sequences);
42,637✔
199
        }
200

201
        constexpr void consume() noexcept
202
        {
203
            MIMICPP_ASSERT(is_applicable(), "Policy is inapplicable.");
204

205
            std::apply(
21,318✔
206
                [](auto&... entries) noexcept {
21,221✔
207
                    (..., std::get<0>(entries)->consume(std::get<1>(entries)));
20,899✔
208
                },
209
                m_Sequences);
21,318✔
210

211
            ++m_Count;
21,318✔
212
            update_sequence_states();
21,318✔
213
        }
21,318✔
214

215
        [[nodiscard]]
216
        reporting::control_state_t state() const
217
        {
218
            return detail::make_control_state(
219
                m_Min,
48,734✔
220
                m_Max,
48,734✔
221
                m_Count,
48,734✔
222
                m_Sequences);
48,734✔
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_Min)
42,487✔
236
            {
237
                std::apply(
21,149✔
238
                    [](auto&... entries) noexcept {
21,064✔
239
                        (..., std::get<0>(entries)->set_satisfied(std::get<1>(entries)));
20,797✔
240
                    },
241
                    m_Sequences);
21,149✔
242
            }
243
            else if (m_Count == m_Max)
21,338✔
244
            {
245
                std::apply(
54✔
246
                    [](auto&... entries) noexcept {
52✔
247
                        (..., std::get<0>(entries)->set_saturated(std::get<1>(entries)));
32✔
248
                    },
249
                    m_Sequences);
54✔
250
            }
251
        }
42,487✔
252
    };
253
}
254

255
MIMICPP_DETAIL_MODULE_EXPORT namespace mimicpp::expect
256
{
257
    /**
258
     * \defgroup EXPECTATION_TIMES times
259
     * \ingroup EXPECTATION
260
     * \brief Specifies how many times an expectation must be matched.
261
     * \details
262
     * When defining an expectation, users may specify a times-policy exactly once.
263
     * If no policy is provided, it defaults to `expect::once()`.
264
     * Attempting to set more than one times-policy for the same expectation results in a compile-time error.
265
     *
266
     * A times-policy always defines both a lower and an upper bound on the number of allowed matches.
267
     * Both bounds are inclusive.
268
     *
269
     *\{
270
     */
271

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

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

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

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

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

346
        return config;
347
    }
348

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

361
        return config;
362
    }
363

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

376
        return config;
377
    }
378

379
    /**
380
     * \brief Specifies a times-policy with no constraints on how many times an expectation may match.
381
     * \return The newly created policy.
382
     */
383
    [[nodiscard]]
384
    consteval auto any_times() noexcept
385
    {
386
        constexpr mimicpp::detail::TimesConfig config{0, std::numeric_limits<int>::max()};
387

388
        return config;
389
    }
390

391
    /**
392
     * \}
393
     */
394
}
395

396
#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