C++Spec 1.0.0
BDD testing for C++
Loading...
Searching...
No Matches
description.hpp
Go to the documentation of this file.
1
5#pragma once
6
7#include <deque>
8#include <forward_list>
9#include <list>
10#include <memory>
11#include <source_location>
12#include <string>
13#include <utility>
14
15#include "it.hpp"
16
17namespace CppSpec {
18
19template <class T>
20class ClassDescription; // forward-declaration for ClassDescription
21
22class Description : public Runnable {
23 using VoidBlock = std::function<void()>;
24
25 public:
26 using Block = std::function<void(Description&)>;
27
28 std::forward_list<LetBase*> lets;
29 std::deque<VoidBlock> after_alls;
30 std::deque<VoidBlock> before_eaches;
31 std::deque<VoidBlock> after_eaches;
32
33 private:
34 Block block;
35 std::list<std::unique_ptr<LetBase>> owned_lets_;
36
37 protected:
38 std::string description;
39
40 void exec_before_eaches();
41 void exec_after_eaches();
42
43 public:
44 // Primary constructor. Entry of all specs.
45 Description(const char* description,
46 Block block,
47 std::source_location location = std::source_location::current()) noexcept
48 : Runnable(location), block(std::move(block)), description(description) {
49 this->set_location(location);
50 }
51
52 Description(std::source_location location, std::string&& description) noexcept
53 : Runnable(location), description(std::move(description)) {}
54
55 Description(std::source_location location, const char* description, Block block) noexcept
56 : Runnable(location), block(std::move(block)), description(description) {}
57
58 /********* Specify/It *********/
59
60 ItD& it(const char* name, ItD::Block body, std::source_location location = std::source_location::current());
61 ItD& it(ItD::Block body, std::source_location location = std::source_location::current());
62
63 /********* Context ***********/
64
65 template <class T = std::nullptr_t>
66 Description& context(const char* description,
67 Block body,
68 std::source_location location = std::source_location::current());
69
70 template <Util::not_c_string T, class B>
71 ClassDescription<T>& context(T& subject, B block, std::source_location location = std::source_location::current());
72
73 template <class T, class B>
74 ClassDescription<T>& context(const char* description,
75 T& subject,
76 B block,
77 std::source_location location = std::source_location::current());
78
79 template <Util::not_c_string T, class B>
80 ClassDescription<T>& context(T&& subject, B block, std::source_location location = std::source_location::current());
81
82 template <class T, class B>
83 ClassDescription<T>& context(const char* description,
84 T&& subject,
85 B block,
86 std::source_location location = std::source_location::current());
87
88 template <class T, typename U>
89 ClassDescription<T>& context(std::initializer_list<U> init_list,
90 std::function<void(ClassDescription<T>&)> block,
91 std::source_location location = std::source_location::current());
92
93 /********* Each/All *********/
94
95 void before_each(VoidBlock block);
96 void before_all(VoidBlock block);
97 void after_each(VoidBlock block);
98 void after_all(VoidBlock block);
99
100 /********* Let *********/
101
102 template <typename F>
103 auto& let(F factory);
104 void reset_lets() noexcept;
105
106 /********* Standard getters *********/
107
108 [[nodiscard]] virtual std::string get_description() const noexcept { return description; }
109 [[nodiscard]] virtual std::string get_subject_type() const noexcept { return ""; }
110
111 /********* Run *********/
112
113 void run() override;
114 // std::function<int(int, char **)>
115 template <typename Formatter>
116 inline auto as_main();
117};
118
119using Context = Description;
120
121/*>>>>>>>>>>>>>>>>>>>> Description <<<<<<<<<<<<<<<<<<<<<<<<<*/
122
123/*========= Description::it =========*/
124
125inline ItD& Description::it(const char* description, ItD::Block block, std::source_location location) {
126 exec_before_eaches();
127 auto* it = this->make_child<ItD>(location, description, block);
128 it->timed_run();
129 exec_after_eaches();
130 return *it;
131}
132
133inline ItD& Description::it(ItD::Block block, std::source_location location) {
134 exec_before_eaches();
135 auto* it = this->make_child<ItD>(location, block);
136 it->timed_run();
137 exec_after_eaches();
138 return *it;
139}
140
141/*========= Description::context =========*/
142
143template <class T>
144inline Context& Description::context(const char* description, Block body, std::source_location location) {
145 auto* context = this->make_child<Context>(location, description, body);
146 context->before_eaches = this->before_eaches;
147 context->after_eaches = this->after_eaches;
148 context->timed_run();
149 return *context;
150}
151
152/*========= Description:: each/alls =========*/
153
154inline void Description::before_each(VoidBlock b) {
155 before_eaches.push_back(std::move(b));
156}
157
158inline void Description::before_all(VoidBlock b) {
159 b();
160}
161
162inline void Description::after_each(VoidBlock b) {
163 after_eaches.push_back(b);
164}
165
166inline void Description::after_all(VoidBlock b) {
167 after_alls.push_back(b);
168}
169
170/*----------- private -------------*/
171
172inline void Description::exec_before_eaches() {
173 for (VoidBlock& b : before_eaches) {
174 b();
175 }
176}
177
178inline void Description::exec_after_eaches() {
179 for (VoidBlock& b : after_eaches) {
180 b();
181 }
182}
183
184/*========= Description::let =========*/
185
186template <typename F>
187auto& Description::let(F factory) {
188 using T = decltype(std::declval<F>()());
189 auto ptr = std::make_unique<Let<T>>(std::move(factory));
190 auto* raw = ptr.get();
191 owned_lets_.push_back(std::move(ptr));
192 lets.push_front(raw);
193 return *raw;
194}
195
196inline void Description::reset_lets() noexcept {
197 // For every let in our list, reset it.
198 for (auto& let : lets) {
199 let->reset();
200 }
201
202 // Recursively reset all the lets in the family tree
203 if (this->has_parent()) {
204 this->get_parent_as<Description>()->reset_lets();
205 }
206}
207
208/*========= Description::run =========*/
209
210inline void Description::run() {
211 block(*this); // Run the block
212 for (VoidBlock& a : after_alls) {
213 a(); // Run all our after_alls
214 }
215}
216
217/*>>>>>>>>>>>>>>>>>>>> ItD <<<<<<<<<<<<<<<<<<<<<<<<<*/
218
219/*========= ItD::run =========*/
220
221inline void ItD::run() {
222 block(*this);
223 this->get_parent_as<Description>()->reset_lets();
224}
225
226} // namespace CppSpec
A Description with a defined subject.
Definition class_description.hpp:22
Definition description.hpp:22
An it embedded in a Description.
Definition it.hpp:27
bool has_parent() noexcept
Check to see if the Runnable has a parent.
Definition runnable.hpp:56