TestToolsLib.h 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. /*
  2. * TestToolsLib.h
  3. * --------------
  4. * Purpose: Unit test framework for libopenmpt.
  5. * Notes : This is more complex than the OpenMPT version because we cannot
  6. * rely on a debugger and have to deal with exceptions ourselves.
  7. * Authors: OpenMPT Devs
  8. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  9. */
  10. #pragma once
  11. #include "openmpt/all/BuildSettings.hpp"
  12. #ifdef ENABLE_TESTS
  13. #ifndef MODPLUG_TRACKER
  14. //#define MPT_TEST_CXX11
  15. #include "mpt/test/test.hpp"
  16. #include <type_traits>
  17. #include "mpt/base/bit.hpp"
  18. #include "openmpt/base/FlagSet.hpp"
  19. #include "../soundlib/Snd_defs.h"
  20. OPENMPT_NAMESPACE_BEGIN
  21. namespace Test {
  22. class mpt_test_reporter
  23. : public mpt::test::silent_reporter
  24. {
  25. public:
  26. mpt_test_reporter() = default;
  27. ~mpt_test_reporter() override = default;
  28. public:
  29. void case_run(const mpt::source_location & loc) override;
  30. void case_run(const mpt::source_location & loc, const char * text_e) override;
  31. void case_run(const mpt::source_location & loc, const char * text_ex, const char * text_e) override;
  32. void case_run(const mpt::source_location & loc, const char * text_a, const char * text_cmp, const char * text_b) override;
  33. void case_result(const mpt::source_location & loc, const mpt::test::result & result) override;
  34. };
  35. extern int fail_count;
  36. enum Verbosity
  37. {
  38. VerbosityQuiet,
  39. VerbosityNormal,
  40. VerbosityVerbose,
  41. };
  42. enum Fatality
  43. {
  44. FatalityContinue,
  45. FatalityStop
  46. };
  47. struct TestFailed
  48. {
  49. std::string values;
  50. TestFailed(const std::string &values) : values(values) { }
  51. TestFailed() { }
  52. };
  53. } // namespace Test
  54. template<typename T>
  55. struct ToStringHelper
  56. {
  57. std::string operator () (const T &x)
  58. {
  59. return mpt::afmt::val(x);
  60. }
  61. };
  62. #ifdef MPT_TEST_CXX11
  63. template<>
  64. struct ToStringHelper<mpt::endian>
  65. {
  66. std::string operator () (const mpt::endian &x)
  67. {
  68. if(x == mpt::endian::big) return "big";
  69. if(x == mpt::endian::little) return "little";
  70. return "unknown";
  71. }
  72. };
  73. template<typename enum_t, typename store_t>
  74. struct ToStringHelper<FlagSet<enum_t, store_t> >
  75. {
  76. std::string operator () (const FlagSet<enum_t, store_t> &x)
  77. {
  78. return mpt::afmt::val(x.GetRaw());
  79. }
  80. };
  81. template<typename enum_t>
  82. struct ToStringHelper<enum_value_type<enum_t> >
  83. {
  84. std::string operator () (const enum_value_type<enum_t> &x)
  85. {
  86. return mpt::afmt::val(x.as_bits());
  87. }
  88. };
  89. template<typename Ta, typename Tb>
  90. struct ToStringHelper<std::pair<Ta, Tb> >
  91. {
  92. std::string operator () (const std::pair<Ta, Tb> &x)
  93. {
  94. return std::string("{") + mpt::afmt::val(x.first) + std::string(",") + mpt::afmt::val(x.second) + std::string("}");
  95. }
  96. };
  97. template<std::size_t FRACT, typename T>
  98. struct ToStringHelper<FPInt<FRACT, T> >
  99. {
  100. std::string operator () (const FPInt<FRACT, T> &x)
  101. {
  102. return std::string("FPInt<") + mpt::afmt::val(FRACT) + std::string(",") + mpt::afmt::val(typeid(T).name()) + std::string(">{") + mpt::afmt::val(x.GetInt()) + std::string(".") + mpt::afmt::val(x.GetFract()) + std::string("}");
  103. }
  104. };
  105. template<>
  106. struct ToStringHelper<SamplePosition>
  107. {
  108. std::string operator () (const SamplePosition &x)
  109. {
  110. return mpt::afmt::val(x.GetInt()) + std::string(".") + std::string("0x") + mpt::afmt::hex0<8>(x.GetFract());
  111. }
  112. };
  113. #endif // MPT_TEST_CXX11
  114. namespace Test {
  115. class Testcase
  116. {
  117. private:
  118. Fatality const fatality;
  119. Verbosity const verbosity;
  120. const char * const desc;
  121. mpt::source_location const loc;
  122. public:
  123. Testcase(Fatality fatality, Verbosity verbosity, const char * const desc, const mpt::source_location &loc);
  124. public:
  125. std::string AsString() const;
  126. void ShowStart() const;
  127. void ShowProgress(const char * text) const;
  128. void ShowPass() const;
  129. void ShowFail(bool exception = false, const char * const text = nullptr) const;
  130. void ReportPassed();
  131. void ReportFailed();
  132. void ReportException();
  133. private:
  134. template <typename Tx, typename Ty>
  135. inline bool IsEqual(const Tx &x, const Ty &y, std::false_type, std::false_type)
  136. {
  137. return (x == y);
  138. }
  139. template <typename Tx, typename Ty>
  140. inline bool IsEqual(const Tx &x, const Ty &y, std::false_type, std::true_type)
  141. {
  142. return (x == y);
  143. }
  144. template <typename Tx, typename Ty>
  145. inline bool IsEqual(const Tx &x, const Ty &y, std::true_type, std::false_type)
  146. {
  147. return (x == y);
  148. }
  149. template <typename Tx, typename Ty>
  150. inline bool IsEqual(const Tx &x, const Ty &y, std::true_type /* is_integer */, std::true_type /* is_integer */ )
  151. {
  152. // Avoid signed-unsigned-comparison warnings and test equivalence in case of either type conversion direction.
  153. return ((x == static_cast<Tx>(y)) && (static_cast<Ty>(x) == y));
  154. }
  155. template <typename Tx, typename Ty, typename Teps>
  156. inline bool IsEqualEpsilon(const Tx &x, const Ty &y, const Teps &eps)
  157. {
  158. return std::abs(x - y) <= eps;
  159. }
  160. public:
  161. #ifdef MPT_TEST_CXX11
  162. private:
  163. template <typename Tx, typename Ty>
  164. MPT_NOINLINE void TypeCompareHelper(const Tx &x, const Ty &y)
  165. {
  166. if(!IsEqual(x, y, std::is_integral<Tx>(), std::is_integral<Ty>()))
  167. {
  168. throw TestFailed(MPT_AFORMAT("{} != {}")(ToStringHelper<Tx>()(x), ToStringHelper<Ty>()(y)));
  169. //throw TestFailed();
  170. }
  171. }
  172. template <typename Tx, typename Ty, typename Teps>
  173. MPT_NOINLINE void TypeCompareHelper(const Tx &x, const Ty &y, const Teps &eps)
  174. {
  175. if(!IsEqualEpsilon(x, y, eps))
  176. {
  177. throw TestFailed(MPT_AFORMAT("{} != {}")(ToStringHelper<Tx>()(x), ToStringHelper<Ty>()(y)));
  178. //throw TestFailed();
  179. }
  180. }
  181. public:
  182. template <typename Tfx, typename Tfy>
  183. MPT_NOINLINE void operator () (const Tfx &fx, const Tfy &fy)
  184. {
  185. ShowStart();
  186. try
  187. {
  188. ShowProgress("Calculate x ...");
  189. const auto x = fx();
  190. ShowProgress("Calculate y ...");
  191. const auto y = fy();
  192. ShowProgress("Compare ...");
  193. TypeCompareHelper(x, y);
  194. ReportPassed();
  195. } catch(...)
  196. {
  197. ReportFailed();
  198. }
  199. }
  200. template <typename Tfx, typename Tfy, typename Teps>
  201. MPT_NOINLINE void operator () (const Tfx &fx, const Tfy &fy, const Teps &eps)
  202. {
  203. ShowStart();
  204. try
  205. {
  206. ShowProgress("Calculate x ...");
  207. const auto x = fx();
  208. ShowProgress("Calculate y ...");
  209. const auto y = fy();
  210. ShowProgress("Compare ...");
  211. TypeCompareHelper(x, y, eps);
  212. ReportPassed();
  213. } catch(...)
  214. {
  215. ReportFailed();
  216. }
  217. }
  218. #define VERIFY_EQUAL(x,y) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} )
  219. #define VERIFY_EQUAL_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} )
  220. #define VERIFY_EQUAL_QUIET_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityQuiet , #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;} )
  221. #define VERIFY_EQUAL_EPS(x,y,eps) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( [&](){return (x) ;}, [&](){return (y) ;}, (eps) )
  222. #else
  223. public:
  224. template <typename Tx, typename Ty>
  225. MPT_NOINLINE void operator () (const Tx &x, const Ty &y)
  226. {
  227. ShowStart();
  228. try
  229. {
  230. if(!IsEqual(x, y, std::is_integral<Tx>(), std::is_integral<Ty>()))
  231. {
  232. //throw TestFailed(MPT_AFORMAT("{} != {}")(x, y));
  233. throw TestFailed();
  234. }
  235. ReportPassed();
  236. } catch(...)
  237. {
  238. ReportFailed();
  239. }
  240. }
  241. template <typename Tx, typename Ty, typename Teps>
  242. MPT_NOINLINE void operator () (const Tx &x, const Ty &y, const Teps &eps)
  243. {
  244. ShowStart();
  245. try
  246. {
  247. if(!IsEqualEpsilon(x, y, eps))
  248. {
  249. //throw TestFailed(MPT_AFORMAT("{} != {}")(x, y));
  250. throw TestFailed();
  251. }
  252. ReportPassed();
  253. } catch(...)
  254. {
  255. ReportFailed();
  256. }
  257. }
  258. #define VERIFY_EQUAL(x,y) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y) )
  259. #define VERIFY_EQUAL_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y) )
  260. #define VERIFY_EQUAL_QUIET_NONCONT(x,y) Test::Testcase(Test::FatalityStop , Test::VerbosityQuiet , #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y) )
  261. #define VERIFY_EQUAL_EPS(x,y,eps) Test::Testcase(Test::FatalityContinue, Test::VerbosityNormal, #x " == " #y , MPT_SOURCE_LOCATION_CURRENT() )( (x) , (y), (eps) )
  262. #endif
  263. };
  264. #define DO_TEST(func) \
  265. do { \
  266. Test::Testcase test(Test::FatalityStop, Test::VerbosityVerbose, #func , MPT_SOURCE_LOCATION_CURRENT() ); \
  267. try { \
  268. test.ShowStart(); \
  269. fail_count = 0; \
  270. func(); \
  271. if(fail_count > 0) { \
  272. throw Test::TestFailed(); \
  273. } \
  274. test.ReportPassed(); \
  275. } catch(...) { \
  276. test.ReportException(); \
  277. } \
  278. } while(0)
  279. } // namespace Test
  280. OPENMPT_NAMESPACE_END
  281. #endif // !MODPLUG_TRACKER
  282. #endif // ENABLE_TESTS