C++Spec 1.0.0
BDD testing for C++
Loading...
Searching...
No Matches
expectation.hpp
1#pragma once
2
3#include <exception>
4#include <optional>
5#include <regex>
6#include <source_location>
7#include <string>
8#include <vector>
9
11#include "matchers/contain.hpp"
12#include "matchers/equal.hpp"
13#include "matchers/errors/fail.hpp"
14#include "matchers/errors/have_value.hpp"
15#include "matchers/errors/throw.hpp"
19#include "matchers/numeric/be_within.hpp"
20#include "matchers/satisfy.hpp"
24
25#ifdef __cpp_lib_expected
26#include "matchers/errors/have_error.hpp"
27#endif
28
29namespace CppSpec {
30
47template <class A>
48class Expectation {
49 ItBase* it = nullptr;
50 std::source_location location;
51
52 protected:
53 bool is_positive_ = true;
54 // Have we been negated?
55 bool ignore_ = false;
56
57 public:
58 Expectation() = default;
59 explicit Expectation(std::source_location location) : location(location) {}
60
68 explicit Expectation(ItBase& it, std::source_location location) : it(&it), location(location) {}
69
71 // virtual const A &get_target() const & { return target; }
72 virtual A& get_target() & = 0;
73
74 [[nodiscard]] ItBase* get_it() const { return it; }
75 [[nodiscard]] std::source_location get_location() const { return location; }
76
78 [[nodiscard]] constexpr bool positive() const { return is_positive_; }
79 [[nodiscard]] constexpr bool ignored() const { return ignore_; }
80
81 /********* Modifiers *********/
82
83 virtual Expectation& not_() = 0;
84 virtual Expectation& ignore() = 0;
85
86 /********* Matchers *********/
87
88 template <class M>
89 void to(M matcher, std::string msg = "");
90
91 /*-------- to be... ----------*/
92
93 void to_be_false(std::string msg = "");
94 void to_be_falsy(std::string msg = "");
95 void to_be_null(std::string msg = "");
96 void to_be_true(std::string msg = "");
97 void to_be_truthy(std::string msg = "");
98
99 template <typename E>
100 void to_be_between(E min, E max, Matchers::RangeMode mode = Matchers::RangeMode::inclusive, std::string msg = "");
101
102 template <typename E>
103 void to_be_greater_than(E rhs, std::string msg = "");
104
105 template <typename E>
106 void to_be_less_than(E rhs, std::string msg = "");
107
108 template <typename E>
109 Matchers::BeWithinHelper<A, E> to_be_within(E expected, std::string msg = "");
110
111 /*-------- to... ----------*/
112
113 void to_end_with(std::string ending, std::string msg = "");
114 void to_fail(std::string msg = "");
115 void to_fail_with(std::string failure_message, std::string msg = "");
116 void to_match(std::regex regex, std::string msg = "");
117 void to_match(std::string str, std::string msg = "");
118 void to_partially_match(std::regex regex, std::string msg = "");
119 void to_partially_match(std::string str, std::string msg = "");
120 template <typename F>
121 requires std::invocable<F, A> && std::convertible_to<std::invoke_result_t<F, A>, bool>
122 void to_satisfy(F test, std::string msg = "");
123 void to_start_with(std::string start, std::string msg = "");
124
125 template <typename U>
126 void to_contain(std::initializer_list<U> expected, std::string msg = "");
127
128 template <typename E>
129 void to_contain(E expected, std::string msg = "");
130
131 template <typename U>
132 void to_end_with(std::initializer_list<U> start, std::string msg = "");
133
134 template <typename E>
135 void to_equal(E expected, std::string msg = "");
136
137 template <typename U>
138 void to_start_with(std::initializer_list<U> start, std::string msg = "");
139
140 void to_have_value(std::string msg = "");
141#if __cpp_lib_expected
142 void to_have_error(std::string msg = "");
143#endif
144};
145
156template <typename A>
157template <class M>
158void Expectation<A>::to(M matcher, std::string msg) {
159 static_assert(std::is_base_of_v<Matchers::MatcherBase<A, typename M::expected_t>, M>,
160 "Matcher is not a subclass of BaseMatcher.");
161 // auto base_matcher = static_cast<Matchers::BaseMatcher<A,typename
162 // M::expected_t>>(matcher);
163 matcher.set_message(std::move(msg)).run();
164}
165
174template <typename A>
175void Expectation<A>::to_be_false(std::string msg) {
176 static_assert(std::is_same_v<A, bool>,
177 ".to_be_false() can only be used on booleans or functions that return booleans");
178 to_equal(false, msg);
179}
180
188template <typename A>
189void Expectation<A>::to_be_falsy(std::string msg) {
190 to_satisfy([](const A& t) { return !static_cast<bool>(t); }, msg);
191}
192
200template <typename A>
201void Expectation<A>::to_be_null(std::string msg) {
202 Matchers::BeNullptr<A>(*this).set_message(std::move(msg)).run();
203}
204
212template <typename A>
213void Expectation<A>::to_be_true(std::string msg) {
214 static_assert(std::is_same_v<A, bool>,
215 ".to_be_true() can only be used on booleans or functions that return booleans");
216 // return to_be([](A t) { return static_cast<bool>(t); }, msg);
217 to_equal(true, msg);
218}
219
228template <typename A>
229void Expectation<A>::to_be_truthy(std::string msg) {
230 to_satisfy([](const A& t) { return static_cast<bool>(t); }, msg);
231}
232
243template <typename A>
244template <typename E>
245void Expectation<A>::to_be_between(E min, E max, Matchers::RangeMode mode, std::string msg) {
246 Matchers::BeBetween<A, E>(*this, min, max, mode).set_message(std::move(msg)).run();
247}
248
249template <typename A>
250template <typename E>
251void Expectation<A>::to_be_less_than(E rhs, std::string msg) {
252 Matchers::BeLessThan<A, E>(*this, rhs).set_message(std::move(msg)).run();
253}
254
255template <typename A>
256template <typename E>
257void Expectation<A>::to_be_greater_than(E rhs, std::string msg) {
258 Matchers::BeGreaterThan<A, E>(*this, rhs).set_message(std::move(msg)).run();
259}
260
269template <typename A>
270template <typename U>
271void Expectation<A>::to_contain(std::initializer_list<U> expected, std::string msg) {
272 Matchers::Contain<A, std::vector<U>, U>(*this, expected).set_message(std::move(msg)).run();
273}
274
283template <typename A>
284template <typename E>
285void Expectation<A>::to_contain(E expected, std::string msg) {
286 Matchers::Contain<A, E, E>(*this, expected).set_message(std::move(msg)).run();
287}
288
297template <typename A>
298template <typename E>
299void Expectation<A>::to_equal(E expected, std::string msg) {
300 Matchers::Equal<A, E>(*this, expected).set_message(std::move(msg)).run();
301}
302
311template <typename A>
312template <typename E>
314 Matchers::BeWithinHelper<A, E> matcher(*this, expected);
315 matcher.set_message(std::move(msg));
316 return matcher;
317}
318
319template <typename A>
320void Expectation<A>::to_fail(std::string msg) {
321 static_assert(is_result_v<A>, ".to_fail() must be used on an expression that returns a Result.");
322 Matchers::Fail<Result>(*this).set_message(std::move(msg)).run();
323}
324
325template <typename A>
326void Expectation<A>::to_fail_with(std::string failure_message, std::string msg) {
327 static_assert(is_result_v<A>, ".to_fail_with() must be used on an expression that returns a Result.");
328 Matchers::FailWith<A>(*this, failure_message).set_message(std::move(msg)).run();
329}
330
331template <typename A>
332void Expectation<A>::to_match(std::string str, std::string msg) {
333 Matchers::Match<A>(*this, str).set_message(std::move(msg)).run();
334}
335
336template <typename A>
337void Expectation<A>::to_match(std::regex regex, std::string msg) {
338 Matchers::Match<A>(*this, regex).set_message(std::move(msg)).run();
339}
340
341template <typename A>
342void Expectation<A>::to_partially_match(std::string str, std::string msg) {
343 Matchers::MatchPartial<A>(*this, str).set_message(std::move(msg)).run();
344}
345
346template <typename A>
347void Expectation<A>::to_partially_match(std::regex regex, std::string msg) {
348 Matchers::MatchPartial<A>(*this, regex).set_message(std::move(msg)).run();
349}
350
360template <typename A>
361template <typename F>
362 requires std::invocable<F, A> && std::convertible_to<std::invoke_result_t<F, A>, bool>
363void Expectation<A>::to_satisfy(F test, std::string msg) {
364 Matchers::Satisfy<A>(*this, std::function<bool(A)>(std::move(test))).set_message(std::move(msg)).run();
365}
366
367template <typename A>
368void Expectation<A>::to_start_with(std::string start, std::string msg) {
369 Matchers::StartWith<std::string, std::string>(*this, start).set_message(std::move(msg)).run();
370}
371
372template <typename A>
373template <typename U>
374void Expectation<A>::to_start_with(std::initializer_list<U> start_sequence, std::string msg) {
375 Matchers::StartWith<A, std::initializer_list<U>>(*this, start_sequence).set_message(std::move(msg)).run();
376}
377
378template <typename A>
379void Expectation<A>::to_end_with(std::string ending, std::string msg) {
380 Matchers::EndWith<std::string, std::string>(*this, ending).set_message(std::move(msg)).run();
381}
382
383template <typename A>
384template <typename U>
385void Expectation<A>::to_end_with(std::initializer_list<U> end_sequence, std::string msg) {
386 Matchers::EndWith<A, std::initializer_list<U>>(*this, end_sequence).set_message(std::move(msg)).run();
387}
388
389template <typename A>
390void Expectation<A>::to_have_value(std::string msg) {
391 Matchers::HaveValue<A>(*this).set_message(std::move(msg)).run();
392}
393
394#if __cpp_lib_expected
395template <typename A>
396void Expectation<A>::to_have_error(std::string msg) {
397 Matchers::HaveError<A>(*this).set_message(std::move(msg)).run();
398}
399#endif
400
401template <typename A>
402class ExpectationValue : public Expectation<A> {
403 A value;
404
405 public:
413 ExpectationValue(ItBase& it, A value, std::source_location location) : Expectation<A>(it, location), value(value) {}
414 explicit ExpectationValue(A value, std::source_location location = std::source_location::current())
415 : Expectation<A>(location), value(value) {}
416
424 template <typename U>
425 ExpectationValue(ItBase& it, std::initializer_list<U> init_list, std::source_location location)
426 : Expectation<A>(it, location), value(std::vector<U>(init_list)) {}
427
429 A& get_target() & override { return value; }
430
431 ExpectationValue& not_() override {
432 this->is_positive_ = not this->is_positive_;
433 return *this;
434 }
435
436 ExpectationValue& ignore() override {
437 this->ignore_ = true;
438 return *this;
439 }
440};
441
442template <Util::is_functional F>
443class ExpectationFunc : public Expectation<decltype(std::declval<F>()())> {
444 using block_ret_t = decltype(std::declval<F>()());
445 F block;
446 std::optional<block_ret_t> computed = std::nullopt;
447
448 public:
449 ExpectationFunc(ExpectationFunc<F> const& copy, std::source_location location)
450 : Expectation<block_ret_t>(copy, location), block(copy.block) {}
451
459 ExpectationFunc(ItBase& it, F block, std::source_location location)
460 : Expectation<block_ret_t>(it, location), block(block) {}
461
473 // TODO: create a "lazy" parameter for differentiating between delayed and
474 // immediate execution
475 // ExpectationFunc(BaseIt &it, std::function<A()> block)
476 // : Expectation(it), block(block()){}; // block(block), has_block(true)
477 // {}
478
480 block_ret_t& get_target() & override {
481 if (!computed.has_value()) {
482 computed.emplace(block());
483 }
484 return *computed;
485 }
486
487 // auto get_target() & override -> decltype(std::declval<A>()()) & { return
488 // Expectation<A>::get_target()(); }
489
490 ExpectationFunc& not_() override {
491 this->is_positive_ = !this->is_positive_;
492 return *this;
493 }
494
495 ExpectationFunc& ignore() override {
496 this->ignore_ = true;
497 return *this;
498 }
499
500 Expectation<decltype(block())>& casted() { return static_cast<decltype(block())>(*this); }
501
502 template <typename Ex = std::exception>
503 void to_throw(std::string msg = "");
504};
505
506template <Util::is_functional F>
507template <typename Ex>
508void ExpectationFunc<F>::to_throw(std::string msg) {
509 Matchers::Throw<decltype(this->block.operator()()), Ex>(*this).set_message(std::move(msg)).run();
510}
511
512} // namespace CppSpec
Definition expectation.hpp:443
block_ret_t & get_target() &override
Create an Expectation using a function.
Definition expectation.hpp:480
ExpectationFunc(ItBase &it, F block, std::source_location location)
Create an ExpectationValue using a value.
Definition expectation.hpp:459
Definition expectation.hpp:402
A & get_target() &override
Get the target of the expectation.
Definition expectation.hpp:429
ExpectationValue(ItBase &it, std::initializer_list< U > init_list, std::source_location location)
Create an Expectation using an initializer list.
Definition expectation.hpp:425
ExpectationValue(ItBase &it, A value, std::source_location location)
Create an ExpectationValue using a value.
Definition expectation.hpp:413
Wraps the target of an expectation.
Definition expectation.hpp:48
Expectation(ItBase &it, std::source_location location)
Create an Expectation using a value.
Definition expectation.hpp:68
constexpr bool positive() const
Get whether the expectation is normal or negated.
Definition expectation.hpp:78
void to(M matcher, std::string msg="")
Definition expectation.hpp:158
void to_be_false(std::string msg="")
Match using the Matchers::Be matcher, testing for falsy-ness.
Definition expectation.hpp:175
void to_contain(std::initializer_list< U > expected, std::string msg="")
Match using the Matchers::Include matcher, given an initializer list.
Definition expectation.hpp:271
void to_be_falsy(std::string msg="")
Definition expectation.hpp:189
void to_satisfy(F test, std::string msg="")
Match using the Matchers::Satisfy matcher.
Definition expectation.hpp:363
void to_be_true(std::string msg="")
Match using the Matchers::Be matcher, testing for truthy-ness.
Definition expectation.hpp:213
virtual A & get_target() &=0
Get the target of the expectation.
void to_be_truthy(std::string msg="")
Definition expectation.hpp:229
void to_equal(E expected, std::string msg="")
Match using the Matchers::Equal matcher.
Definition expectation.hpp:299
void to_be_between(E min, E max, Matchers::RangeMode mode=Matchers::RangeMode::inclusive, std::string msg="")
Match using the Matchers::BeBetween matcher, with an explicit range mode.
Definition expectation.hpp:245
Matchers::BeWithinHelper< A, E > to_be_within(E expected, std::string msg="")
Match using the Matchers::BeWithin matcher.
Definition expectation.hpp:313
void to_be_null(std::string msg="")
Match using the Matchers::BeNullptr matcher.
Definition expectation.hpp:201
Base class for it expressions.
Definition it_base.hpp:32
Definition be_between.hpp:11
Definition be_less_than.hpp:10
Definition be_nullptr.hpp:10
Definition be_within.hpp:10
Definition contain.hpp:61
Definition end_with.hpp:12
The equal matcher.
Definition equal.hpp:17
Definition fail.hpp:8
Definition have_error.hpp:14
Definition have_value.hpp:14
Definition match.hpp:28
Definition match.hpp:11
Result run()
Run the Matcher object.
Definition matcher_base.hpp:154
virtual MatcherBase & set_message(std::string message)
Set a custom failure message.
Definition matcher_base.hpp:101
Definition satisfy.hpp:21
Definition start_with.hpp:12
Definition throw.hpp:9