C++Spec 1.0.0
BDD testing for C++
Loading...
Searching...
No Matches
expectation.hpp
1#pragma once
2
3#include <exception>
4#include <regex>
5#include <source_location>
6#include <string>
7#include <vector>
8
10#include "matchers/contain.hpp"
11#include "matchers/equal.hpp"
12#include "matchers/errors/fail.hpp"
13#include "matchers/errors/have_value.hpp"
14#include "matchers/errors/throw.hpp"
18#include "matchers/numeric/be_within.hpp"
19#include "matchers/satisfy.hpp"
23
24#ifdef __cpp_lib_expected
25#include "matchers/errors/have_error.hpp"
26#endif
27
28namespace CppSpec {
29
46template <class A>
47class Expectation {
48 ItBase* it = nullptr;
49 std::source_location location;
50
51 protected:
52 bool is_positive_ = true;
53 // Have we been negated?
54 bool ignore_ = false;
55
56 public:
57 Expectation() = default;
58 explicit Expectation(std::source_location location) : location(location) {}
59
67 explicit Expectation(ItBase& it, std::source_location location) : it(&it), location(location) {}
68
70 // virtual const A &get_target() const & { return target; }
71 virtual A& get_target() & = 0;
72
73 [[nodiscard]] ItBase* get_it() const { return it; }
74 [[nodiscard]] std::source_location get_location() const { return location; }
75
77 [[nodiscard]] constexpr bool positive() const { return is_positive_; }
78 [[nodiscard]] constexpr bool ignored() const { return ignore_; }
79
80 /********* Modifiers *********/
81
82 virtual Expectation& not_() = 0;
83 virtual Expectation& ignore() = 0;
84
85 /********* Matchers *********/
86
87 template <class M>
88 void to(M matcher, std::string msg = "");
89
90 /*-------- to be... ----------*/
91
92 void to_be_false(std::string msg = "");
93 void to_be_falsy(std::string msg = "");
94 void to_be_null(std::string msg = "");
95 void to_be_true(std::string msg = "");
96 void to_be_truthy(std::string msg = "");
97
98 template <typename E>
99 void to_be_between(E min, E max, Matchers::RangeMode mode = Matchers::RangeMode::inclusive, std::string msg = "");
100
101 template <typename E>
102 void to_be_greater_than(E rhs, std::string msg = "");
103
104 template <typename E>
105 void to_be_less_than(E rhs, std::string msg = "");
106
107 template <typename E>
108 Matchers::BeWithinHelper<A, E> to_be_within(E expected, std::string msg = "");
109
110 /*-------- to... ----------*/
111
112 void to_end_with(std::string ending, std::string msg = "");
113 void to_fail(std::string msg = "");
114 void to_fail_with(std::string failure_message, std::string msg = "");
115 void to_match(std::regex regex, std::string msg = "");
116 void to_match(std::string str, std::string msg = "");
117 void to_partially_match(std::regex regex, std::string msg = "");
118 void to_partially_match(std::string str, std::string msg = "");
119 void to_satisfy(std::function<bool(A)> /*test*/, std::string msg = "");
120 void to_start_with(std::string start, std::string msg = "");
121
122 template <typename U>
123 void to_contain(std::initializer_list<U> expected, std::string msg = "");
124
125 template <typename E>
126 void to_contain(E expected, std::string msg = "");
127
128 template <typename U>
129 void to_end_with(std::initializer_list<U> start, std::string msg = "");
130
131 template <typename E>
132 void to_equal(E expected, std::string msg = "");
133
134 template <typename U>
135 void to_start_with(std::initializer_list<U> start, std::string msg = "");
136
137 void to_have_value(std::string msg = "");
138#if __cpp_lib_expected
139 void to_have_error(std::string msg = "");
140#endif
141};
142
153template <typename A>
154template <class M>
155void Expectation<A>::to(M matcher, std::string msg) {
156 static_assert(std::is_base_of_v<Matchers::MatcherBase<A, typename M::expected_t>, M>,
157 "Matcher is not a subclass of BaseMatcher.");
158 // auto base_matcher = static_cast<Matchers::BaseMatcher<A,typename
159 // M::expected_t>>(matcher);
160 matcher.set_message(std::move(msg)).run();
161}
162
171template <typename A>
172void Expectation<A>::to_be_false(std::string msg) {
173 static_assert(std::is_same_v<A, bool>,
174 ".to_be_false() can only be used on booleans or functions that return booleans");
175 to_equal(false, msg);
176}
177
185template <typename A>
186void Expectation<A>::to_be_falsy(std::string msg) {
187 to_satisfy([](const A& t) { return !static_cast<bool>(t); }, msg);
188}
189
197template <typename A>
198void Expectation<A>::to_be_null(std::string msg) {
199 Matchers::BeNullptr<A>(*this).set_message(std::move(msg)).run();
200}
201
209template <typename A>
210void Expectation<A>::to_be_true(std::string msg) {
211 static_assert(std::is_same_v<A, bool>,
212 ".to_be_true() can only be used on booleans or functions that return booleans");
213 // return to_be([](A t) { return static_cast<bool>(t); }, msg);
214 to_equal(true, msg);
215}
216
225template <typename A>
226void Expectation<A>::to_be_truthy(std::string msg) {
227 to_satisfy([](const A& t) { return static_cast<bool>(t); }, msg);
228}
229
240template <typename A>
241template <typename E>
242void Expectation<A>::to_be_between(E min, E max, Matchers::RangeMode mode, std::string msg) {
243 Matchers::BeBetween<A, E>(*this, min, max, mode).set_message(std::move(msg)).run();
244}
245
246template <typename A>
247template <typename E>
248void Expectation<A>::to_be_less_than(E rhs, std::string msg) {
249 Matchers::BeLessThan<A, E>(*this, rhs).set_message(std::move(msg)).run();
250}
251
252template <typename A>
253template <typename E>
254void Expectation<A>::to_be_greater_than(E rhs, std::string msg) {
255 Matchers::BeGreaterThan<A, E>(*this, rhs).set_message(std::move(msg)).run();
256}
257
266template <typename A>
267template <typename U>
268void Expectation<A>::to_contain(std::initializer_list<U> expected, std::string msg) {
269 Matchers::Contain<A, std::vector<U>, U>(*this, expected).set_message(std::move(msg)).run();
270}
271
280template <typename A>
281template <typename E>
282void Expectation<A>::to_contain(E expected, std::string msg) {
283 Matchers::Contain<A, E, E>(*this, expected).set_message(std::move(msg)).run();
284}
285
294template <typename A>
295template <typename E>
296void Expectation<A>::to_equal(E expected, std::string msg) {
297 Matchers::Equal<A, E>(*this, expected).set_message(std::move(msg)).run();
298}
299
308template <typename A>
309template <typename E>
311 Matchers::BeWithinHelper<A, E> matcher(*this, expected);
312 matcher.set_message(std::move(msg));
313 return matcher;
314}
315
316template <typename A>
317void Expectation<A>::to_fail(std::string msg) {
318 static_assert(is_result_v<A>, ".to_fail() must be used on an expression that returns a Result.");
319 Matchers::Fail<Result>(*this).set_message(std::move(msg)).run();
320}
321
322template <typename A>
323void Expectation<A>::to_fail_with(std::string failure_message, std::string msg) {
324 static_assert(is_result_v<A>, ".to_fail_with() must be used on an expression that returns a Result.");
325 Matchers::FailWith<A>(*this, failure_message).set_message(std::move(msg)).run();
326}
327
328template <typename A>
329void Expectation<A>::to_match(std::string str, std::string msg) {
330 Matchers::Match<A>(*this, str).set_message(std::move(msg)).run();
331}
332
333template <typename A>
334void Expectation<A>::to_match(std::regex regex, std::string msg) {
335 Matchers::Match<A>(*this, regex).set_message(std::move(msg)).run();
336}
337
338template <typename A>
339void Expectation<A>::to_partially_match(std::string str, std::string msg) {
340 Matchers::MatchPartial<A>(*this, str).set_message(std::move(msg)).run();
341}
342
343template <typename A>
344void Expectation<A>::to_partially_match(std::regex regex, std::string msg) {
345 Matchers::MatchPartial<A>(*this, regex).set_message(std::move(msg)).run();
346}
347
357template <typename A>
358void Expectation<A>::to_satisfy(std::function<bool(A)> test, std::string msg) {
359 Matchers::Satisfy<A>(*this, test).set_message(std::move(msg)).run();
360}
361
362template <typename A>
363void Expectation<A>::to_start_with(std::string start, std::string msg) {
364 Matchers::StartWith<std::string, std::string>(*this, start).set_message(std::move(msg)).run();
365}
366
367template <typename A>
368template <typename U>
369void Expectation<A>::to_start_with(std::initializer_list<U> start_sequence, std::string msg) {
370 Matchers::StartWith<A, std::initializer_list<U>>(*this, start_sequence).set_message(std::move(msg)).run();
371}
372
373template <typename A>
374void Expectation<A>::to_end_with(std::string ending, std::string msg) {
375 Matchers::EndWith<std::string, std::string>(*this, ending).set_message(std::move(msg)).run();
376}
377
378template <typename A>
379template <typename U>
380void Expectation<A>::to_end_with(std::initializer_list<U> start_sequence, std::string msg) {
381 Matchers::StartWith<A, std::initializer_list<U>>(*this, start_sequence).set_message(std::move(msg)).run();
382}
383
384template <typename A>
385void Expectation<A>::to_have_value(std::string msg) {
386 Matchers::HaveValue<A>(*this).set_message(std::move(msg)).run();
387}
388
389#if __cpp_lib_expected
390template <typename A>
391void Expectation<A>::to_have_error(std::string msg) {
392 Matchers::HaveError<A>(*this).set_message(std::move(msg)).run();
393}
394#endif
395
396template <typename A>
397class ExpectationValue : public Expectation<A> {
398 A value;
399
400 public:
408 ExpectationValue(ItBase& it, A value, std::source_location location) : Expectation<A>(it, location), value(value) {}
409 explicit ExpectationValue(A value, std::source_location location = std::source_location::current())
410 : Expectation<A>(location), value(value) {}
411
419 template <typename U>
420 ExpectationValue(ItBase& it, std::initializer_list<U> init_list, std::source_location location)
421 : Expectation<A>(it, location), value(std::vector<U>(init_list)) {}
422
424 A& get_target() & override { return value; }
425
426 ExpectationValue& not_() override {
427 this->is_positive_ = not this->is_positive_;
428 return *this;
429 }
430
431 ExpectationValue& ignore() override {
432 this->ignore_ = true;
433 return *this;
434 }
435};
436
437template <Util::is_functional F>
438class ExpectationFunc : public Expectation<decltype(std::declval<F>()())> {
439 using block_ret_t = decltype(std::declval<F>()());
440 F block;
441 std::shared_ptr<block_ret_t> computed = nullptr;
442
443 public:
444 ExpectationFunc(ExpectationFunc<F> const& copy, std::source_location location)
445 : Expectation<block_ret_t>(copy, location), block(copy.block) {}
446
454 ExpectationFunc(ItBase& it, F block, std::source_location location)
455 : Expectation<block_ret_t>(it, location), block(block) {}
456
468 // TODO: create a "lazy" parameter for differentiating between delayed and
469 // immediate execution
470 // ExpectationFunc(BaseIt &it, std::function<A()> block)
471 // : Expectation(it), block(block()){}; // block(block), has_block(true)
472 // {}
473
475 block_ret_t& get_target() & override {
476 if (computed == nullptr) {
477 computed = std::make_shared<block_ret_t>(block());
478 }
479 return *computed;
480 }
481
482 // auto get_target() & override -> decltype(std::declval<A>()()) & { return
483 // Expectation<A>::get_target()(); }
484
485 ExpectationFunc& not_() override {
486 this->is_positive_ = !this->is_positive_;
487 return *this;
488 }
489
490 ExpectationFunc& ignore() override {
491 this->ignore_ = true;
492 return *this;
493 }
494
495 Expectation<decltype(block())>& casted() { return static_cast<decltype(block())>(*this); }
496
497 template <typename Ex = std::exception>
498 void to_throw(std::string msg = "");
499};
500
501template <Util::is_functional F>
502template <typename Ex>
503void ExpectationFunc<F>::to_throw(std::string msg) {
504 Matchers::Throw<decltype(this->block.operator()()), Ex>(*this).set_message(std::move(msg)).run();
505}
506
507} // namespace CppSpec
Definition expectation.hpp:438
block_ret_t & get_target() &override
Create an Expectation using a function.
Definition expectation.hpp:475
ExpectationFunc(ItBase &it, F block, std::source_location location)
Create an ExpectationValue using a value.
Definition expectation.hpp:454
Definition expectation.hpp:397
A & get_target() &override
Get the target of the expectation.
Definition expectation.hpp:424
ExpectationValue(ItBase &it, std::initializer_list< U > init_list, std::source_location location)
Create an Expectation using an initializer list.
Definition expectation.hpp:420
ExpectationValue(ItBase &it, A value, std::source_location location)
Create an ExpectationValue using a value.
Definition expectation.hpp:408
Wraps the target of an expectation.
Definition expectation.hpp:47
Expectation(ItBase &it, std::source_location location)
Create an Expectation using a value.
Definition expectation.hpp:67
constexpr bool positive() const
Get whether the expectation is normal or negated.
Definition expectation.hpp:77
void to(M matcher, std::string msg="")
Definition expectation.hpp:155
void to_be_false(std::string msg="")
Match using the Matchers::Be matcher, testing for falsy-ness.
Definition expectation.hpp:172
void to_contain(std::initializer_list< U > expected, std::string msg="")
Match using the Matchers::Include matcher, given an initializer list.
Definition expectation.hpp:268
void to_be_falsy(std::string msg="")
Definition expectation.hpp:186
void to_be_true(std::string msg="")
Match using the Matchers::Be matcher, testing for truthy-ness.
Definition expectation.hpp:210
virtual A & get_target() &=0
Get the target of the expectation.
void to_satisfy(std::function< bool(A)>, std::string msg="")
Match using the Matchers::Satisfy matcher.
Definition expectation.hpp:358
void to_be_truthy(std::string msg="")
Definition expectation.hpp:226
void to_equal(E expected, std::string msg="")
Match using the Matchers::Equal matcher.
Definition expectation.hpp:296
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:242
void to_contain(E expected, std::string msg="")
Match using the Matchers::Include matcher.
Definition expectation.hpp:282
Matchers::BeWithinHelper< A, E > to_be_within(E expected, std::string msg="")
Match using the Matchers::BeWithin matcher.
Definition expectation.hpp:310
void to_be_null(std::string msg="")
Match using the Matchers::BeNullptr matcher.
Definition expectation.hpp:198
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