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

source code of boost/libs/beast/include/boost/beast/_experimental/unit_test/reporter.hpp