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 | |
8 | namespace 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 | |