1#ifndef BANDIT_INFO_REPORTER_H
2#define BANDIT_INFO_REPORTER_H
3
4#include <iostream>
5#include <stack>
6#include <bandit/reporters/colored_reporter.h>
7
8namespace bandit {
9 namespace detail {
10 struct info_reporter : public colored_reporter {
11 struct context_info {
12 context_info(const std::string& d) : desc(d), total(0), skipped(0), failed(0) {}
13
14 void merge(const context_info& ci) {
15 total += ci.total;
16 skipped += ci.skipped;
17 failed += ci.failed;
18 }
19
20 const std::string desc; // copy
21 int total;
22 int skipped;
23 int failed;
24 };
25
26 info_reporter(std::ostream& stm, const failure_formatter& failure_formatter, const colorizer& colorizer)
27 : colored_reporter(stm, failure_formatter, colorizer),
28 indentation_(0), not_yet_shown_(0), context_stack_() {}
29
30 info_reporter(const failure_formatter& failure_formatter, const colorizer& colorizer)
31 : info_reporter(std::cout, failure_formatter, colorizer) {}
32
33 info_reporter& operator=(const info_reporter&) {
34 return *this;
35 }
36
37 void list_failures_and_errors() {
38 if (specs_failed_ > 0) {
39 stm_
40 << colorizer_.red()
41 << "List of failures:"
42 << std::endl;
43 for (const auto& failure : failures_) {
44 stm_
45 << colorizer_.white()
46 << " (*) "
47 << colorizer_.red()
48 << failure << std::endl;
49 }
50 }
51 if (test_run_errors_.size() > 0) {
52 stm_
53 << colorizer_.red()
54 << "List of run errors:"
55 << std::endl;
56 for (const auto& error : test_run_errors_) {
57 stm_
58 << colorizer_.white()
59 << " (*) "
60 << colorizer_.red()
61 << error << std::endl;
62 }
63 }
64 }
65
66 void summary() {
67 stm_
68 << colorizer_.white()
69 << "Tests run: " << specs_run_
70 << std::endl;
71 if (specs_skipped_ > 0) {
72 stm_
73 << colorizer_.yellow()
74 << "Skipped: " << specs_skipped_
75 << std::endl;
76 }
77 if (specs_succeeded_ > 0) {
78 stm_
79 << colorizer_.green()
80 << "Passed: " << specs_succeeded_
81 << std::endl;
82 }
83 if (specs_failed_ > 0) {
84 stm_
85 << colorizer_.red()
86 << "Failed: " << specs_failed_
87 << std::endl;
88 }
89 if (test_run_errors_.size() > 0) {
90 stm_
91 << colorizer_.red()
92 << "Errors: " << test_run_errors_.size()
93 << std::endl;
94 }
95 stm_
96 << colorizer_.reset()
97 << std::endl;
98 }
99
100 void test_run_complete() override {
101 progress_reporter::test_run_complete();
102 stm_ << std::endl;
103 list_failures_and_errors();
104 summary();
105 stm_.flush();
106 }
107
108 void test_run_error(const std::string& desc, const struct test_run_error& err) override {
109 progress_reporter::test_run_error(desc, err);
110
111 std::stringstream ss;
112 ss << "Failed to run \"" << current_context_name() << "\": error \"" << err.what() << "\"" << std::endl;
113 test_run_errors_.push_back(ss.str());
114 }
115
116 void context_starting(const std::string& desc) override {
117 progress_reporter::context_starting(desc);
118 context_stack_.emplace(desc);
119 if (context_stack_.size() == 1) {
120 output_context_start_message();
121 } else {
122 ++not_yet_shown_;
123 }
124 }
125
126 void output_context_start_message() {
127 stm_
128 << indent()
129 << colorizer_.blue()
130 << "begin "
131 << colorizer_.white()
132 << context_stack_.top().desc
133 << colorizer_.reset()
134 << std::endl;
135 ++indentation_;
136 stm_.flush();
137 }
138
139 void output_not_yet_shown_context_start_messages() {
140 std::stack<context_info> temp_stack;
141 for (int i = 0; i < not_yet_shown_; ++i) {
142 temp_stack.push(context_stack_.top());
143 context_stack_.pop();
144 }
145 for (int i = 0; i < not_yet_shown_; ++i) {
146 context_stack_.push(temp_stack.top());
147 output_context_start_message();
148 temp_stack.pop();
149 }
150 not_yet_shown_ = 0;
151 }
152
153 void context_ended(const std::string& desc) override {
154 progress_reporter::context_ended(desc);
155 if (context_stack_.size() == 1 || context_stack_.top().total > context_stack_.top().skipped) {
156 output_context_end_message();
157 }
158 const context_info context = context_stack_.top(); // copy
159 context_stack_.pop();
160 if (!context_stack_.empty()) {
161 context_stack_.top().merge(context);
162 }
163 if (not_yet_shown_ > 0) {
164 --not_yet_shown_;
165 }
166 }
167
168 void output_context_end_message() {
169 const context_info& context = context_stack_.top();
170 --indentation_;
171 stm_
172 << indent()
173 << colorizer_.blue()
174 << "end "
175 << colorizer_.reset()
176 << context.desc;
177 if (context.total > 0) {
178 stm_
179 << colorizer_.white()
180 << " " << context.total << " total";
181 }
182 if (context.skipped > 0) {
183 stm_
184 << colorizer_.yellow()
185 << " " << context.skipped << " skipped";
186 }
187 if (context.failed > 0) {
188 stm_
189 << colorizer_.red()
190 << " " << context.failed << " failed";
191 }
192 stm_ << colorizer_.reset() << std::endl;
193 }
194
195 void it_skip(const std::string& desc) override {
196 progress_reporter::it_skip(desc);
197 ++context_stack_.top().total;
198 ++context_stack_.top().skipped;
199 }
200
201 void it_starting(const std::string& desc) override {
202 if (context_stack_.size() > 1 && context_stack_.top().total == context_stack_.top().skipped) {
203 output_not_yet_shown_context_start_messages();
204 }
205
206 progress_reporter::it_starting(desc);
207 stm_
208 << indent()
209 << colorizer_.yellow()
210 << "[ TEST ]"
211 << colorizer_.reset()
212 << " it " << desc;
213 ++indentation_;
214 stm_.flush();
215 }
216
217 void it_succeeded(const std::string& desc) override {
218 progress_reporter::it_succeeded(desc);
219 ++context_stack_.top().total;
220 --indentation_;
221 stm_
222 << "\r" << indent()
223 << colorizer_.green()
224 << "[ PASS ]"
225 << colorizer_.reset()
226 << " it " << desc
227 << std::endl;
228 stm_.flush();
229 }
230
231 void it_failed(const std::string& desc, const assertion_exception& ex) override {
232 ++specs_failed_;
233
234 std::stringstream ss;
235 ss << current_context_name() << " " << desc << ":" << std::endl
236 << failure_formatter_.format(ex);
237 failures_.push_back(ss.str());
238
239 ++context_stack_.top().total;
240 ++context_stack_.top().failed;
241 --indentation_;
242 stm_
243 << "\r" << indent()
244 << colorizer_.red()
245 << "[ FAIL ]"
246 << colorizer_.reset()
247 << " it " << desc
248 << std::endl;
249 stm_.flush();
250 }
251
252 void it_unknown_error(const std::string& desc) override {
253 ++specs_failed_;
254
255 std::stringstream ss;
256 ss << current_context_name() << " " << desc << ": Unknown exception" << std::endl;
257 failures_.push_back(ss.str());
258
259 ++context_stack_.top().total;
260 ++context_stack_.top().failed;
261 --indentation_;
262 stm_
263 << "\r" << indent()
264 << colorizer_.red()
265 << "-ERROR->"
266 << colorizer_.reset()
267 << " it " << desc
268 << std::endl;
269 stm_.flush();
270 }
271
272 private:
273 std::string indent() {
274 return std::string(2 * indentation_, ' ');
275 }
276
277 int indentation_;
278 int not_yet_shown_; // number of elements in stack that are not yet shown
279 std::stack<context_info> context_stack_;
280 };
281 }
282}
283#endif
284