reporter.hpp 5.9 KB

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