lightweight_test.hpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. #ifndef BHO_CORE_LIGHTWEIGHT_TEST_HPP
  2. #define BHO_CORE_LIGHTWEIGHT_TEST_HPP
  3. // MS compatible compilers support #pragma once
  4. #if defined(_MSC_VER)
  5. # pragma once
  6. #endif
  7. //
  8. // bho/core/lightweight_test.hpp - lightweight test library
  9. //
  10. // Copyright (c) 2002, 2009, 2014 Peter Dimov
  11. // Copyright (2) Beman Dawes 2010, 2011
  12. // Copyright (3) Ion Gaztanaga 2013
  13. //
  14. // Copyright 2018 Glen Joseph Fernandes
  15. // (glenjofe@gmail.com)
  16. //
  17. // Distributed under the Boost Software License, Version 1.0.
  18. // See accompanying file LICENSE_1_0.txt or copy at
  19. // http://www.boost.org/LICENSE_1_0.txt
  20. //
  21. #include <asio2/bho/core/detail/lwt_unattended.hpp>
  22. #include <asio2/bho/current_function.hpp>
  23. #include <asio2/bho/config.hpp>
  24. #include <exception>
  25. #include <iostream>
  26. #include <iterator>
  27. #include <string>
  28. #include <cstdlib>
  29. #include <cstring>
  30. #include <cstddef>
  31. #include <cctype>
  32. // IDE's like Visual Studio perform better if output goes to std::cout or
  33. // some other stream, so allow user to configure output stream:
  34. #ifndef BHO_LIGHTWEIGHT_TEST_OSTREAM
  35. # define BHO_LIGHTWEIGHT_TEST_OSTREAM std::cerr
  36. #endif
  37. namespace bho
  38. {
  39. namespace detail
  40. {
  41. class test_result
  42. {
  43. public:
  44. test_result(): report_( false ), errors_( 0 )
  45. {
  46. core::detail::lwt_unattended();
  47. }
  48. ~test_result()
  49. {
  50. if( !report_ )
  51. {
  52. BHO_LIGHTWEIGHT_TEST_OSTREAM << "main() should return report_errors()" << std::endl;
  53. std::abort();
  54. }
  55. }
  56. int& errors()
  57. {
  58. return errors_;
  59. }
  60. void done()
  61. {
  62. report_ = true;
  63. }
  64. private:
  65. bool report_;
  66. int errors_;
  67. };
  68. inline test_result& test_results()
  69. {
  70. static test_result instance;
  71. return instance;
  72. }
  73. inline int& test_errors()
  74. {
  75. return test_results().errors();
  76. }
  77. inline bool test_impl(char const * expr, char const * file, int line, char const * function, bool v)
  78. {
  79. if( v )
  80. {
  81. test_results();
  82. return true;
  83. }
  84. else
  85. {
  86. BHO_LIGHTWEIGHT_TEST_OSTREAM
  87. << file << "(" << line << "): test '" << expr << "' failed in function '"
  88. << function << "'" << std::endl;
  89. ++test_results().errors();
  90. return false;
  91. }
  92. }
  93. inline void error_impl(char const * msg, char const * file, int line, char const * function)
  94. {
  95. BHO_LIGHTWEIGHT_TEST_OSTREAM
  96. << file << "(" << line << "): " << msg << " in function '"
  97. << function << "'" << std::endl;
  98. ++test_results().errors();
  99. }
  100. inline void throw_failed_impl(const char* expr, char const * excep, char const * file, int line, char const * function)
  101. {
  102. BHO_LIGHTWEIGHT_TEST_OSTREAM
  103. << file << "(" << line << "): expression '" << expr << "' did not throw exception '" << excep << "' in function '"
  104. << function << "'" << std::endl;
  105. ++test_results().errors();
  106. }
  107. inline void no_throw_failed_impl(const char* expr, const char* file, int line, const char* function)
  108. {
  109. BHO_LIGHTWEIGHT_TEST_OSTREAM
  110. << file << "(" << line << "): expression '" << expr << "' threw an exception in function '"
  111. << function << "'" << std::endl;
  112. ++test_results().errors();
  113. }
  114. inline void no_throw_failed_impl(const char* expr, const char* what, const char* file, int line, const char* function)
  115. {
  116. BHO_LIGHTWEIGHT_TEST_OSTREAM
  117. << file << "(" << line << "): expression '" << expr << "' threw an exception in function '"
  118. << function << "': " << what << std::endl;
  119. ++test_results().errors();
  120. }
  121. // In the comparisons below, it is possible that T and U are signed and unsigned integer types, which generates warnings in some compilers.
  122. // A cleaner fix would require common_type trait or some meta-programming, which would introduce a dependency on BHO.TypeTraits. To avoid
  123. // the dependency we just disable the warnings.
  124. #if defined(__clang__) && defined(__has_warning)
  125. # if __has_warning("-Wsign-compare")
  126. # pragma clang diagnostic push
  127. # pragma clang diagnostic ignored "-Wsign-compare"
  128. # endif
  129. #elif defined(_MSC_VER)
  130. # pragma warning(push)
  131. # pragma warning(disable: 4389)
  132. #elif defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406
  133. # pragma GCC diagnostic push
  134. # pragma GCC diagnostic ignored "-Wsign-compare"
  135. #endif
  136. // specialize test output for char pointers to avoid printing as cstring
  137. template <class T> inline const T& test_output_impl(const T& v) { return v; }
  138. inline const void* test_output_impl(const char* v) { return v; }
  139. inline const void* test_output_impl(const unsigned char* v) { return v; }
  140. inline const void* test_output_impl(const signed char* v) { return v; }
  141. inline const void* test_output_impl(char* v) { return v; }
  142. inline const void* test_output_impl(unsigned char* v) { return v; }
  143. inline const void* test_output_impl(signed char* v) { return v; }
  144. template<class T> inline const void* test_output_impl(T volatile* v) { return const_cast<T*>(v); }
  145. #if !defined( BHO_NO_CXX11_NULLPTR )
  146. inline const void* test_output_impl(std::nullptr_t) { return nullptr; }
  147. #endif
  148. // print chars as numeric
  149. inline int test_output_impl( signed char const& v ) { return v; }
  150. inline unsigned test_output_impl( unsigned char const& v ) { return v; }
  151. // Whether wchar_t is signed is implementation-defined
  152. template<bool Signed> struct lwt_long_type {};
  153. template<> struct lwt_long_type<true> { typedef long type; };
  154. template<> struct lwt_long_type<false> { typedef unsigned long type; };
  155. inline lwt_long_type<(static_cast<wchar_t>(-1) < static_cast<wchar_t>(0))>::type test_output_impl( wchar_t const& v ) { return v; }
  156. #if !defined( BHO_NO_CXX11_CHAR16_T )
  157. inline unsigned long test_output_impl( char16_t const& v ) { return v; }
  158. #endif
  159. #if !defined( BHO_NO_CXX11_CHAR32_T )
  160. inline unsigned long test_output_impl( char32_t const& v ) { return v; }
  161. #endif
  162. inline std::string test_output_impl( char const& v )
  163. {
  164. if( std::isprint( static_cast<unsigned char>( v ) ) )
  165. {
  166. return std::string( 1, v );
  167. }
  168. else
  169. {
  170. static const char char_table[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
  171. char buffer[ 4 ];
  172. buffer[ 0 ] = '\\';
  173. buffer[ 1 ] = 'x';
  174. buffer[ 2 ] = char_table[ (static_cast<unsigned char>( v ) >> 4u) & 0x0f ];
  175. buffer[ 3 ] = char_table[ static_cast<unsigned char>( v ) & 0x0f ];
  176. return std::string( buffer, 4u );
  177. }
  178. }
  179. // predicates
  180. struct lw_test_eq
  181. {
  182. template <typename T, typename U>
  183. bool operator()(const T& t, const U& u) const { return t == u; }
  184. };
  185. struct lw_test_ne
  186. {
  187. template <typename T, typename U>
  188. bool operator()(const T& t, const U& u) const { return t != u; }
  189. };
  190. struct lw_test_lt
  191. {
  192. template <typename T, typename U>
  193. bool operator()(const T& t, const U& u) const { return t < u; }
  194. };
  195. struct lw_test_le
  196. {
  197. template <typename T, typename U>
  198. bool operator()(const T& t, const U& u) const { return t <= u; }
  199. };
  200. struct lw_test_gt
  201. {
  202. template <typename T, typename U>
  203. bool operator()(const T& t, const U& u) const { return t > u; }
  204. };
  205. struct lw_test_ge
  206. {
  207. template <typename T, typename U>
  208. bool operator()(const T& t, const U& u) const { return t >= u; }
  209. };
  210. // lwt_predicate_name
  211. template<class T> char const * lwt_predicate_name( T const& )
  212. {
  213. return "~=";
  214. }
  215. inline char const * lwt_predicate_name( lw_test_eq const& )
  216. {
  217. return "==";
  218. }
  219. inline char const * lwt_predicate_name( lw_test_ne const& )
  220. {
  221. return "!=";
  222. }
  223. inline char const * lwt_predicate_name( lw_test_lt const& )
  224. {
  225. return "<";
  226. }
  227. inline char const * lwt_predicate_name( lw_test_le const& )
  228. {
  229. return "<=";
  230. }
  231. inline char const * lwt_predicate_name( lw_test_gt const& )
  232. {
  233. return ">";
  234. }
  235. inline char const * lwt_predicate_name( lw_test_ge const& )
  236. {
  237. return ">=";
  238. }
  239. //
  240. template<class BinaryPredicate, class T, class U>
  241. inline bool test_with_impl(BinaryPredicate pred, char const * expr1, char const * expr2,
  242. char const * file, int line, char const * function,
  243. T const & t, U const & u)
  244. {
  245. if( pred(t, u) )
  246. {
  247. test_results();
  248. return true;
  249. }
  250. else
  251. {
  252. BHO_LIGHTWEIGHT_TEST_OSTREAM
  253. << file << "(" << line << "): test '" << expr1 << " " << lwt_predicate_name(pred) << " " << expr2
  254. << "' ('" << test_output_impl(t) << "' " << lwt_predicate_name(pred) << " '" << test_output_impl(u)
  255. << "') failed in function '" << function << "'" << std::endl;
  256. ++test_results().errors();
  257. return false;
  258. }
  259. }
  260. inline bool test_cstr_eq_impl( char const * expr1, char const * expr2,
  261. char const * file, int line, char const * function, char const * const t, char const * const u )
  262. {
  263. if( std::strcmp(t, u) == 0 )
  264. {
  265. test_results();
  266. return true;
  267. }
  268. else
  269. {
  270. BHO_LIGHTWEIGHT_TEST_OSTREAM
  271. << file << "(" << line << "): test '" << expr1 << " == " << expr2 << "' ('" << t
  272. << "' == '" << u << "') failed in function '" << function << "'" << std::endl;
  273. ++test_results().errors();
  274. return false;
  275. }
  276. }
  277. inline bool test_cstr_ne_impl( char const * expr1, char const * expr2,
  278. char const * file, int line, char const * function, char const * const t, char const * const u )
  279. {
  280. if( std::strcmp(t, u) != 0 )
  281. {
  282. test_results();
  283. return true;
  284. }
  285. else
  286. {
  287. BHO_LIGHTWEIGHT_TEST_OSTREAM
  288. << file << "(" << line << "): test '" << expr1 << " != " << expr2 << "' ('" << t
  289. << "' != '" << u << "') failed in function '" << function << "'" << std::endl;
  290. ++test_results().errors();
  291. return false;
  292. }
  293. }
  294. template<class FormattedOutputFunction, class InputIterator1, class InputIterator2>
  295. bool test_all_eq_impl(FormattedOutputFunction& output,
  296. char const * file, int line, char const * function,
  297. InputIterator1 first_begin, InputIterator1 first_end,
  298. InputIterator2 second_begin, InputIterator2 second_end)
  299. {
  300. InputIterator1 first_it = first_begin;
  301. InputIterator2 second_it = second_begin;
  302. typename std::iterator_traits<InputIterator1>::difference_type first_index = 0;
  303. typename std::iterator_traits<InputIterator2>::difference_type second_index = 0;
  304. std::size_t error_count = 0;
  305. const std::size_t max_count = 8;
  306. do
  307. {
  308. while ((first_it != first_end) && (second_it != second_end) && (*first_it == *second_it))
  309. {
  310. ++first_it;
  311. ++second_it;
  312. ++first_index;
  313. ++second_index;
  314. }
  315. if ((first_it == first_end) || (second_it == second_end))
  316. {
  317. break; // do-while
  318. }
  319. if (error_count == 0)
  320. {
  321. output << file << "(" << line << "): Container contents differ in function '" << function << "':";
  322. }
  323. else if (error_count >= max_count)
  324. {
  325. output << " ...";
  326. break;
  327. }
  328. output << " [" << first_index << "] '" << test_output_impl(*first_it) << "' != '" << test_output_impl(*second_it) << "'";
  329. ++first_it;
  330. ++second_it;
  331. ++first_index;
  332. ++second_index;
  333. ++error_count;
  334. } while (first_it != first_end);
  335. first_index += std::distance(first_it, first_end);
  336. second_index += std::distance(second_it, second_end);
  337. if (first_index != second_index)
  338. {
  339. if (error_count == 0)
  340. {
  341. output << file << "(" << line << "): Container sizes differ in function '" << function << "': size(" << first_index << ") != size(" << second_index << ")";
  342. }
  343. else
  344. {
  345. output << " [*] size(" << first_index << ") != size(" << second_index << ")";
  346. }
  347. ++error_count;
  348. }
  349. if (error_count == 0)
  350. {
  351. test_results();
  352. return true;
  353. }
  354. else
  355. {
  356. output << std::endl;
  357. ++test_results().errors();
  358. return false;
  359. }
  360. }
  361. template<class FormattedOutputFunction, class InputIterator1, class InputIterator2, typename BinaryPredicate>
  362. bool test_all_with_impl(FormattedOutputFunction& output,
  363. char const * file, int line, char const * function,
  364. InputIterator1 first_begin, InputIterator1 first_end,
  365. InputIterator2 second_begin, InputIterator2 second_end,
  366. BinaryPredicate predicate)
  367. {
  368. InputIterator1 first_it = first_begin;
  369. InputIterator2 second_it = second_begin;
  370. typename std::iterator_traits<InputIterator1>::difference_type first_index = 0;
  371. typename std::iterator_traits<InputIterator2>::difference_type second_index = 0;
  372. std::size_t error_count = 0;
  373. const std::size_t max_count = 8;
  374. do
  375. {
  376. while ((first_it != first_end) && (second_it != second_end) && predicate(*first_it, *second_it))
  377. {
  378. ++first_it;
  379. ++second_it;
  380. ++first_index;
  381. ++second_index;
  382. }
  383. if ((first_it == first_end) || (second_it == second_end))
  384. {
  385. break; // do-while
  386. }
  387. if (error_count == 0)
  388. {
  389. output << file << "(" << line << "): Container contents differ in function '" << function << "':";
  390. }
  391. else if (error_count >= max_count)
  392. {
  393. output << " ...";
  394. break;
  395. }
  396. output << " [" << first_index << "]";
  397. ++first_it;
  398. ++second_it;
  399. ++first_index;
  400. ++second_index;
  401. ++error_count;
  402. } while (first_it != first_end);
  403. first_index += std::distance(first_it, first_end);
  404. second_index += std::distance(second_it, second_end);
  405. if (first_index != second_index)
  406. {
  407. if (error_count == 0)
  408. {
  409. output << file << "(" << line << "): Container sizes differ in function '" << function << "': size(" << first_index << ") != size(" << second_index << ")";
  410. }
  411. else
  412. {
  413. output << " [*] size(" << first_index << ") != size(" << second_index << ")";
  414. }
  415. ++error_count;
  416. }
  417. if (error_count == 0)
  418. {
  419. test_results();
  420. return true;
  421. }
  422. else
  423. {
  424. output << std::endl;
  425. ++test_results().errors();
  426. return false;
  427. }
  428. }
  429. #if defined(__clang__) && defined(__has_warning)
  430. # if __has_warning("-Wsign-compare")
  431. # pragma clang diagnostic pop
  432. # endif
  433. #elif defined(_MSC_VER)
  434. # pragma warning(pop)
  435. #elif defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406
  436. # pragma GCC diagnostic pop
  437. #endif
  438. } // namespace detail
  439. inline int report_errors()
  440. {
  441. bho::detail::test_result& result = bho::detail::test_results();
  442. result.done();
  443. int errors = result.errors();
  444. if( errors == 0 )
  445. {
  446. BHO_LIGHTWEIGHT_TEST_OSTREAM
  447. << "No errors detected." << std::endl;
  448. }
  449. else
  450. {
  451. BHO_LIGHTWEIGHT_TEST_OSTREAM
  452. << errors << " error" << (errors == 1? "": "s") << " detected." << std::endl;
  453. }
  454. // `return report_errors();` from main only supports 8 bit exit codes
  455. return errors < 256? errors: 255;
  456. }
  457. namespace core
  458. {
  459. inline void lwt_init()
  460. {
  461. bho::detail::test_results();
  462. }
  463. } // namespace core
  464. } // namespace bho
  465. #define BHO_TEST(expr) ( ::bho::detail::test_impl(#expr, __FILE__, __LINE__, BHO_CURRENT_FUNCTION, (expr)? true: false) )
  466. #define BHO_TEST_NOT(expr) BHO_TEST(!(expr))
  467. #define BHO_ERROR(msg) ( ::bho::detail::error_impl(msg, __FILE__, __LINE__, BHO_CURRENT_FUNCTION) )
  468. #define BHO_TEST_WITH(expr1,expr2,predicate) ( ::bho::detail::test_with_impl(predicate, #expr1, #expr2, __FILE__, __LINE__, BHO_CURRENT_FUNCTION, expr1, expr2) )
  469. #define BHO_TEST_EQ(expr1,expr2) ( ::bho::detail::test_with_impl(::bho::detail::lw_test_eq(), #expr1, #expr2, __FILE__, __LINE__, BHO_CURRENT_FUNCTION, expr1, expr2) )
  470. #define BHO_TEST_NE(expr1,expr2) ( ::bho::detail::test_with_impl(::bho::detail::lw_test_ne(), #expr1, #expr2, __FILE__, __LINE__, BHO_CURRENT_FUNCTION, expr1, expr2) )
  471. #define BHO_TEST_LT(expr1,expr2) ( ::bho::detail::test_with_impl(::bho::detail::lw_test_lt(), #expr1, #expr2, __FILE__, __LINE__, BHO_CURRENT_FUNCTION, expr1, expr2) )
  472. #define BHO_TEST_LE(expr1,expr2) ( ::bho::detail::test_with_impl(::bho::detail::lw_test_le(), #expr1, #expr2, __FILE__, __LINE__, BHO_CURRENT_FUNCTION, expr1, expr2) )
  473. #define BHO_TEST_GT(expr1,expr2) ( ::bho::detail::test_with_impl(::bho::detail::lw_test_gt(), #expr1, #expr2, __FILE__, __LINE__, BHO_CURRENT_FUNCTION, expr1, expr2) )
  474. #define BHO_TEST_GE(expr1,expr2) ( ::bho::detail::test_with_impl(::bho::detail::lw_test_ge(), #expr1, #expr2, __FILE__, __LINE__, BHO_CURRENT_FUNCTION, expr1, expr2) )
  475. #define BHO_TEST_CSTR_EQ(expr1,expr2) ( ::bho::detail::test_cstr_eq_impl(#expr1, #expr2, __FILE__, __LINE__, BHO_CURRENT_FUNCTION, expr1, expr2) )
  476. #define BHO_TEST_CSTR_NE(expr1,expr2) ( ::bho::detail::test_cstr_ne_impl(#expr1, #expr2, __FILE__, __LINE__, BHO_CURRENT_FUNCTION, expr1, expr2) )
  477. #define BHO_TEST_ALL_EQ(begin1, end1, begin2, end2) ( ::bho::detail::test_all_eq_impl(BHO_LIGHTWEIGHT_TEST_OSTREAM, __FILE__, __LINE__, BHO_CURRENT_FUNCTION, begin1, end1, begin2, end2) )
  478. #define BHO_TEST_ALL_WITH(begin1, end1, begin2, end2, predicate) ( ::bho::detail::test_all_with_impl(BHO_LIGHTWEIGHT_TEST_OSTREAM, __FILE__, __LINE__, BHO_CURRENT_FUNCTION, begin1, end1, begin2, end2, predicate) )
  479. #ifndef BHO_NO_EXCEPTIONS
  480. #define BHO_TEST_THROWS( EXPR, EXCEP ) \
  481. try { \
  482. EXPR; \
  483. ::bho::detail::throw_failed_impl \
  484. (#EXPR, #EXCEP, __FILE__, __LINE__, BHO_CURRENT_FUNCTION); \
  485. } \
  486. catch(EXCEP const&) { \
  487. ::bho::detail::test_results(); \
  488. } \
  489. catch(...) { \
  490. ::bho::detail::throw_failed_impl \
  491. (#EXPR, #EXCEP, __FILE__, __LINE__, BHO_CURRENT_FUNCTION); \
  492. } \
  493. //
  494. #else
  495. #define BHO_TEST_THROWS( EXPR, EXCEP )
  496. #endif
  497. #ifndef BHO_NO_EXCEPTIONS
  498. # define BHO_TEST_NO_THROW(EXPR) \
  499. try { \
  500. EXPR; \
  501. } catch (const std::exception& e) { \
  502. ::bho::detail::no_throw_failed_impl \
  503. (#EXPR, e.what(), __FILE__, __LINE__, BHO_CURRENT_FUNCTION); \
  504. } catch (...) { \
  505. ::bho::detail::no_throw_failed_impl \
  506. (#EXPR, __FILE__, __LINE__, BHO_CURRENT_FUNCTION); \
  507. }
  508. //
  509. #else
  510. # define BHO_TEST_NO_THROW(EXPR) { EXPR; }
  511. #endif
  512. #endif // #ifndef BHO_CORE_LIGHTWEIGHT_TEST_HPP