C++Spec 1.0.0
BDD testing for C++
Loading...
Searching...
No Matches
runnable.hpp
1#pragma once
2
3#include <chrono>
4#include <list>
5#include <source_location>
6#include <string>
7#include "result.hpp"
8
9namespace CppSpec {
10
11class Result;
12
13namespace Formatters {
14class BaseFormatter; // Forward declaration to allow reference
15}
16
25class Runnable {
26 // The parent of this child
27 // We use a raw pointer here instead of the safer std::shared_ptr
28 // due to the way that tests are inherently constructed
29 // (`describe some_spec("a test", $ { ... });`). In order to use
30 // a shared pointer, each object that is set as the parent must be
31 // contained in a shared pointer. As tests are created by `describe ...`,
32 // the root object is not wrapped by a shared pointer. Attempting to create
33 // this shared pointer at some later time doesn't work, as it results in
34 // trying to delete the current object `this` once the pointer goes out of
35 // scope. Since children are always destroyed before their parents, this
36 // isn't a problem anyways. In addition, any structures that are children
37 // are allocated on the stack for speed reasons.
38 Runnable* parent = nullptr;
39
40 // The source file location of the Runnable object
41 std::source_location location;
42
43 std::list<std::shared_ptr<Runnable>> children_; // List of children
44
45 std::chrono::time_point<std::chrono::system_clock> start_time_;
46 std::chrono::duration<double> runtime_{};
47
48 public:
49 Runnable(std::source_location location) : location(location) {}
50
51 virtual ~Runnable() = default;
52
53 /*--------- Parent helper functions -------------*/
54
56 bool has_parent() noexcept { return parent != nullptr; }
57 [[nodiscard]] bool has_parent() const noexcept { return parent != nullptr; }
58
59 // TODO: Look in to making these references instead of pointer returns
61 [[nodiscard]] Runnable* get_parent() noexcept { return parent; }
62 [[nodiscard]] const Runnable* get_parent() const noexcept { return parent; }
63
64 std::list<std::shared_ptr<Runnable>>& get_children() noexcept { return children_; }
65 [[nodiscard]] const std::list<std::shared_ptr<Runnable>>& get_children() const noexcept { return children_; }
66
67 template <class C>
68 C* get_parent_as() noexcept {
69 return static_cast<C*>(parent);
70 }
71
72 template <class C>
73 [[nodiscard]] const C* get_parent_as() const noexcept {
74 return static_cast<const C*>(parent);
75 }
76
77 template <typename T, typename... Args>
78 T* make_child(Args&&... args) {
79 auto child = std::make_shared<T>(std::forward<Args>(args)...);
80 auto* child_ptr = child.get();
81 child->parent = this;
82 children_.push_back(std::move(child));
83 return child_ptr;
84 }
85
86 /*--------- Primary member functions -------------*/
87
88 // Calculate the padding for printing this object
89 [[nodiscard]] std::string padding() const noexcept;
90
91 // Get the location of the object
92 [[nodiscard]] std::source_location get_location() const noexcept { return this->location; }
93
94 // Set the location of the object
95 void set_location(std::source_location location) noexcept { this->location = location; }
96
97 virtual void run() = 0;
98
99 virtual void timed_run() {
100 using namespace std::chrono;
101 start_time_ = system_clock::now();
102 time_point start_time = high_resolution_clock::now();
103 run();
104 time_point end = high_resolution_clock::now();
105 runtime_ = end - start_time;
106 }
107
108 [[nodiscard]] std::chrono::duration<double> get_runtime() const { return runtime_; }
109
110 [[nodiscard]] std::chrono::time_point<std::chrono::system_clock> get_start_time() const { return start_time_; }
111
112 [[nodiscard]] virtual Result get_result() const {
113 Result result = Result::success(location);
114 for (const auto& child : get_children()) {
115 result = Result::reduce(result, child->get_result());
116 }
117 return result;
118 }
119
120 [[nodiscard]] size_t num_tests() const noexcept {
121 if (get_children().empty()) {
122 return 1; // This is a leaf node
123 }
124
125 // This is not a leaf node, so we need to count the children
126 size_t count = 0;
127 for (const auto& child : get_children()) {
128 count += child->num_tests(); // +1 for the child itself
129 }
130 return count;
131 }
132
133 [[nodiscard]] size_t num_failures() const noexcept {
134 if (get_children().empty()) {
135 return this->get_result().is_failure() ? 1 : 0; // This is a leaf node
136 }
137
138 // This is not a leaf node, so we need to count the children
139 size_t count = 0;
140 for (const auto& child : get_children()) {
141 count += child->num_failures(); // +1 for the child itself
142 }
143 return count;
144 }
145};
146
147/*>>>>>>>>>>>>>>>>>>>> Runnable <<<<<<<<<<<<<<<<<<<<<<<<<*/
148
153// TODO: Refactor this into Runnable::depth
154inline std::string Runnable::padding() const noexcept {
155 return this->has_parent() ? this->get_parent()->padding() + " " : "";
156}
157
158} // namespace CppSpec
Definition formatters_base.hpp:33
Definition result.hpp:13
Base class for all objects in the execution tree.
Definition runnable.hpp:25
Runnable * get_parent() noexcept
Get the Runnable's parent.
Definition runnable.hpp:61
std::string padding() const noexcept
Generate padding (indentation) fore the current object.
Definition runnable.hpp:154
bool has_parent() noexcept
Check to see if the Runnable has a parent.
Definition runnable.hpp:56