]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | // | |
7 | ||
8 | #ifndef BEAST_UNIT_TEST_REPORTER_HPP | |
9 | #define BEAST_UNIT_TEST_REPORTER_HPP | |
10 | ||
11 | #include <beast/unit_test/amount.hpp> | |
12 | #include <beast/unit_test/recorder.hpp> | |
13 | #include <boost/lexical_cast.hpp> | |
14 | #include <boost/optional.hpp> | |
15 | #include <algorithm> | |
16 | #include <chrono> | |
17 | #include <functional> | |
18 | #include <iomanip> | |
19 | #include <iostream> | |
20 | #include <sstream> | |
21 | #include <string> | |
22 | #include <utility> | |
23 | ||
24 | namespace beast { | |
25 | namespace unit_test { | |
26 | ||
27 | namespace detail { | |
28 | ||
29 | /** A simple test runner that writes everything to a stream in real time. | |
30 | The totals are output when the object is destroyed. | |
31 | */ | |
32 | template<class = void> | |
33 | class reporter : public runner | |
34 | { | |
35 | private: | |
36 | using clock_type = std::chrono::steady_clock; | |
37 | ||
38 | struct case_results | |
39 | { | |
40 | std::string name; | |
41 | std::size_t total = 0; | |
42 | std::size_t failed = 0; | |
43 | ||
44 | explicit | |
45 | case_results(std::string name_ = "") | |
46 | : name(std::move(name_)) | |
47 | { | |
48 | } | |
49 | }; | |
50 | ||
51 | struct suite_results | |
52 | { | |
53 | std::string name; | |
54 | std::size_t cases = 0; | |
55 | std::size_t total = 0; | |
56 | std::size_t failed = 0; | |
57 | typename clock_type::time_point start = clock_type::now(); | |
58 | ||
59 | explicit | |
60 | suite_results(std::string name_ = "") | |
61 | : name(std::move(name_)) | |
62 | { | |
63 | } | |
64 | ||
65 | void | |
66 | add(case_results const& r); | |
67 | }; | |
68 | ||
69 | struct results | |
70 | { | |
71 | using run_time = std::pair<std::string, | |
72 | typename clock_type::duration>; | |
73 | ||
74 | enum | |
75 | { | |
76 | max_top = 10 | |
77 | }; | |
78 | ||
79 | std::size_t suites = 0; | |
80 | std::size_t cases = 0; | |
81 | std::size_t total = 0; | |
82 | std::size_t failed = 0; | |
83 | std::vector<run_time> top; | |
84 | typename clock_type::time_point start = clock_type::now(); | |
85 | ||
86 | void | |
87 | add(suite_results const& r); | |
88 | }; | |
89 | ||
90 | std::ostream& os_; | |
91 | results results_; | |
92 | suite_results suite_results_; | |
93 | case_results case_results_; | |
94 | ||
95 | public: | |
96 | reporter(reporter const&) = delete; | |
97 | reporter& operator=(reporter const&) = delete; | |
98 | ||
99 | ~reporter(); | |
100 | ||
101 | explicit | |
102 | reporter(std::ostream& os = std::cout); | |
103 | ||
104 | private: | |
105 | static | |
106 | std::string | |
107 | fmtdur(typename clock_type::duration const& d); | |
108 | ||
109 | virtual | |
110 | void | |
111 | on_suite_begin(suite_info const& info) override; | |
112 | ||
113 | virtual | |
114 | void | |
115 | on_suite_end() override; | |
116 | ||
117 | virtual | |
118 | void | |
119 | on_case_begin(std::string const& name) override; | |
120 | ||
121 | virtual | |
122 | void | |
123 | on_case_end() override; | |
124 | ||
125 | virtual | |
126 | void | |
127 | on_pass() override; | |
128 | ||
129 | virtual | |
130 | void | |
131 | on_fail(std::string const& reason) override; | |
132 | ||
133 | virtual | |
134 | void | |
135 | on_log(std::string const& s) override; | |
136 | }; | |
137 | ||
138 | //------------------------------------------------------------------------------ | |
139 | ||
140 | template<class _> | |
141 | void | |
142 | reporter<_>:: | |
143 | suite_results::add(case_results const& r) | |
144 | { | |
145 | ++cases; | |
146 | total += r.total; | |
147 | failed += r.failed; | |
148 | } | |
149 | ||
150 | template<class _> | |
151 | void | |
152 | reporter<_>:: | |
153 | results::add(suite_results const& r) | |
154 | { | |
155 | ++suites; | |
156 | total += r.total; | |
157 | cases += r.cases; | |
158 | failed += r.failed; | |
159 | auto const elapsed = clock_type::now() - r.start; | |
160 | if(elapsed >= std::chrono::seconds{1}) | |
161 | { | |
162 | auto const iter = std::lower_bound(top.begin(), | |
163 | top.end(), elapsed, | |
164 | [](run_time const& t1, | |
165 | typename clock_type::duration const& t2) | |
166 | { | |
167 | return t1.second > t2; | |
168 | }); | |
169 | if(iter != top.end()) | |
170 | { | |
171 | if(top.size() == max_top) | |
172 | top.resize(top.size() - 1); | |
173 | top.emplace(iter, r.name, elapsed); | |
174 | } | |
175 | else if(top.size() < max_top) | |
176 | { | |
177 | top.emplace_back(r.name, elapsed); | |
178 | } | |
179 | } | |
180 | } | |
181 | ||
182 | //------------------------------------------------------------------------------ | |
183 | ||
184 | template<class _> | |
185 | reporter<_>:: | |
186 | reporter(std::ostream& os) | |
187 | : os_(os) | |
188 | { | |
189 | } | |
190 | ||
191 | template<class _> | |
192 | reporter<_>::~reporter() | |
193 | { | |
194 | if(results_.top.size() > 0) | |
195 | { | |
196 | os_ << "Longest suite times:\n"; | |
197 | for(auto const& i : results_.top) | |
198 | os_ << std::setw(8) << | |
199 | fmtdur(i.second) << " " << i.first << '\n'; | |
200 | } | |
201 | auto const elapsed = clock_type::now() - results_.start; | |
202 | os_ << | |
203 | fmtdur(elapsed) << ", " << | |
204 | amount{results_.suites, "suite"} << ", " << | |
205 | amount{results_.cases, "case"} << ", " << | |
206 | amount{results_.total, "test"} << " total, " << | |
207 | amount{results_.failed, "failure"} << | |
208 | std::endl; | |
209 | } | |
210 | ||
211 | template<class _> | |
212 | std::string | |
213 | reporter<_>::fmtdur(typename clock_type::duration const& d) | |
214 | { | |
215 | using namespace std::chrono; | |
216 | auto const ms = duration_cast<milliseconds>(d); | |
217 | if(ms < seconds{1}) | |
218 | return boost::lexical_cast<std::string>( | |
219 | ms.count()) + "ms"; | |
220 | std::stringstream ss; | |
221 | ss << std::fixed << std::setprecision(1) << | |
222 | (ms.count()/1000.) << "s"; | |
223 | return ss.str(); | |
224 | } | |
225 | ||
226 | template<class _> | |
227 | void | |
228 | reporter<_>:: | |
229 | on_suite_begin(suite_info const& info) | |
230 | { | |
231 | suite_results_ = suite_results{info.full_name()}; | |
232 | } | |
233 | ||
234 | template<class _> | |
235 | void | |
236 | reporter<_>::on_suite_end() | |
237 | { | |
238 | results_.add(suite_results_); | |
239 | } | |
240 | ||
241 | template<class _> | |
242 | void | |
243 | reporter<_>:: | |
244 | on_case_begin(std::string const& name) | |
245 | { | |
246 | case_results_ = case_results(name); | |
247 | os_ << suite_results_.name << | |
248 | (case_results_.name.empty() ? "" : | |
249 | (" " + case_results_.name)) << std::endl; | |
250 | } | |
251 | ||
252 | template<class _> | |
253 | void | |
254 | reporter<_>:: | |
255 | on_case_end() | |
256 | { | |
257 | suite_results_.add(case_results_); | |
258 | } | |
259 | ||
260 | template<class _> | |
261 | void | |
262 | reporter<_>:: | |
263 | on_pass() | |
264 | { | |
265 | ++case_results_.total; | |
266 | } | |
267 | ||
268 | template<class _> | |
269 | void | |
270 | reporter<_>:: | |
271 | on_fail(std::string const& reason) | |
272 | { | |
273 | ++case_results_.failed; | |
274 | ++case_results_.total; | |
275 | os_ << | |
276 | "#" << case_results_.total << " failed" << | |
277 | (reason.empty() ? "" : ": ") << reason << std::endl; | |
278 | } | |
279 | ||
280 | template<class _> | |
281 | void | |
282 | reporter<_>:: | |
283 | on_log(std::string const& s) | |
284 | { | |
285 | os_ << s; | |
286 | } | |
287 | ||
288 | } // detail | |
289 | ||
290 | using reporter = detail::reporter<>; | |
291 | ||
292 | } // unit_test | |
293 | } // beast | |
294 | ||
295 | #endif |