Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/src/Progmasoft/Catch3/Strategy/Context.hpp
blob: 2ba832672cbd8c428c6708cf23c507f4af9ae6a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright (c) 2026 Progmasoft.
// SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-2.0
#pragma once

#include <Progmasoft/Catch3/ToString.hpp>
#include <cstddef>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

namespace Progmasoft::Catch3
{

    /// What one run of a property body recorded besides its verdict.
    ///
    /// The engine installs a fresh record before each run of the body, on
    /// the running thread, and reads it afterwards. The functions below
    /// write into the record of the current thread; called outside a
    /// property they do nothing.
    struct TrialRecord final
    {
        struct Coverage final
        {
            std::string Label;
            double Required = 0.0;
            bool Held = false;
        };

        struct Score final
        {
            std::string Label;
            double Value = 0.0;
        };

        std::vector<std::string> Notes;
        std::vector<std::string> Labels;
        std::vector<Coverage> Covers;
        std::vector<Score> Targets;
        std::optional<std::string> Failure;
        bool Discarded = false;
    };

    namespace Detail
    {

        /// The record of the property body running on this thread, or null.
        [[nodiscard]] TrialRecord *
        CurrentTrialRecord() noexcept;

        /// Installs a record for the lifetime of the scope.
        class TrialRecordScope final
        {
        public:
            explicit TrialRecordScope(TrialRecord &record) noexcept;
            ~TrialRecordScope();
            TrialRecordScope(const TrialRecordScope &) = delete;
            TrialRecordScope &
            operator=(const TrialRecordScope &) = delete;

        private:
            TrialRecord *previous_;
        };

        void
        RecordPropertyFailure(std::string_view expression,
                              std::string_view file,
                              std::size_t line);

        /// Fails the current trial with a message, as the relation checks
        /// do when they compare two results.
        void
        RecordPropertyFailureMessage(std::string message);

        /// The name a property gets when PropertyOptions::Name is empty;
        /// the CATCH3_ macros set it to the test case name and the line.
        [[nodiscard]] std::string
        DefaultPropertyName();

        class PropertyNameScope final
        {
        public:
            explicit PropertyNameScope(std::string name);
            ~PropertyNameScope();
            PropertyNameScope(const PropertyNameScope &) = delete;
            PropertyNameScope &
            operator=(const PropertyNameScope &) = delete;

        private:
            std::string name_;
            const std::string *previous_;
        };

    } // namespace Detail

    /// Adds a line to the failure report of the current trial.
    void
    Note(std::string text);

    /// Adds a value to the failure report, rendered as Catch2 renders it.
    template<typename ValueType>
    void
    Note(std::string_view name, const ValueType &value)
    {
        Note(std::string(name) + " = "
             + Catch::StringMaker<ValueType>::convert(value));
    }

    /// Counts the current trial under label; the counts are reported with
    /// PropertyOptions::ShowStatistics and with a Cover failure.
    void
    Classify(bool condition, std::string label);

    /// Requires that condition holds in at least requiredPercentage of the
    /// trials; otherwise the property fails with kInsufficientCoverage.
    void
    Cover(double requiredPercentage, bool condition, std::string label);

    /// Reports a score that the engine tries to maximize after the regular
    /// trials: it mutates the highest-scoring input to search for a higher
    /// score, which finds inputs that random sampling rarely reaches.
    void
    Target(double score, std::string label = {});

} // namespace Progmasoft::Catch3

/// Fails the current property trial, without unwinding, when the condition
/// is false. Use it in a property body that returns void.
#define CATCH3_PROPERTY_ASSERT(...)                                            \
    do                                                                         \
    {                                                                          \
        if (!static_cast<bool>(__VA_ARGS__))                                   \
        {                                                                      \
            ::Progmasoft::Catch3::Detail::RecordPropertyFailure(               \
                #__VA_ARGS__,                                                  \
                __FILE__,                                                      \
                static_cast<std::size_t>(__LINE__));                           \
            return;                                                            \
        }                                                                      \
    } while (false)

/// Discards the current trial when the condition is false. A discarded
/// trial is neither a pass nor a failure; see PropertyOptions::MaxDiscardRatio.
#define CATCH3_PROPERTY_ASSUME(...)                                            \
    do                                                                         \
    {                                                                          \
        if (!static_cast<bool>(__VA_ARGS__))                                   \
        {                                                                      \
            if (auto *catch3Record                                             \
                = ::Progmasoft::Catch3::Detail::CurrentTrialRecord())          \
                catch3Record->Discarded = true;                                \
            return;                                                            \
        }                                                                      \
    } while (false)