Testing.hpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. ///
  2. /// \file Testing.hpp
  3. ///
  4. /// Macros for creating self-test plugins.
  5. /// Self tests are installed into the registry and executed at runtime.
  6. /// The test macros are loosely based on the Boost unit test suite.
  7. ///
  8. /// \copyright
  9. /// Copyright (c) 2013-2017 Josh Blum
  10. /// 2020 Nicholas Corgan
  11. /// SPDX-License-Identifier: BSL-1.0
  12. ///
  13. #pragma once
  14. #include <Pothos/Config.hpp>
  15. #include <Pothos/Object.hpp>
  16. #include <Pothos/Plugin.hpp>
  17. #include <Pothos/Callable.hpp>
  18. #include <Pothos/Exception.hpp>
  19. #include <cmath>
  20. #include <memory>
  21. #include <string>
  22. /*!
  23. * Declare a unit test block of code inside a plugin.
  24. *
  25. * The unit test will be installed into /path/name in the PluginRegistry.
  26. * Once installed, this unit test can be checked with the test utility.
  27. *
  28. * \param path a valid PluginPath
  29. * \param name a valid function name
  30. *
  31. * Example usage:
  32. * \code
  33. * POTHOS_TEST_BLOCK("/sys/foo", test_bar)
  34. * {
  35. * POTHOS_TEST_EQUAL(etc...);
  36. * }
  37. * \endcode
  38. */
  39. #define POTHOS_TEST_BLOCK(path, name) \
  40. POTHOS_STATIC_FIXTURE_DECL void name ## Runner(void); \
  41. template <Pothos::Detail::InitFcn runner> \
  42. struct name : Pothos::TestingBase \
  43. { \
  44. void runTestsImpl(void) \
  45. { \
  46. POTHOS_TEST_CHECKPOINT(); \
  47. runner(); \
  48. } \
  49. }; \
  50. pothos_static_block(name) \
  51. { \
  52. std::shared_ptr<Pothos::TestingBase> testObj(new name<name ## Runner>()); \
  53. Pothos::PluginRegistry::add(Pothos::Plugin( \
  54. Pothos::PluginPath(path).join(#name), Pothos::Object(testObj))); \
  55. } \
  56. POTHOS_STATIC_FIXTURE_DECL void name ## Runner(void)
  57. //! Checkpoint macro to track last successful line executed
  58. #define POTHOS_TEST_CHECKPOINT() \
  59. Pothos::TestingBase::current().report("checkpoint", "", "", __LINE__, __FILE__)
  60. //! Private macro to test a statement for exceptions
  61. #define __POTHOS_TEST_STATEMENT(message, statement) \
  62. { \
  63. Pothos::TestingBase::current().report(message, #statement, "", __LINE__, __FILE__); \
  64. try{statement;} \
  65. catch(const std::string &ex){Pothos::TestingBase::current().report(message, #statement, ex, __LINE__, __FILE__);} \
  66. catch(const Pothos::Exception &ex){Pothos::TestingBase::current().report(message, #statement, ex.displayText(), __LINE__, __FILE__);} \
  67. catch(const std::exception &ex){Pothos::TestingBase::current().report(message, #statement, ex.what(), __LINE__, __FILE__);} \
  68. catch(...){Pothos::TestingBase::current().report(message, #statement, "unknown", __LINE__, __FILE__);} \
  69. }
  70. //! Private macro to assert on a statement's truth
  71. #define __POTHOS_TEST_ASSERT(message, statement) \
  72. { \
  73. __POTHOS_TEST_STATEMENT(message, if (statement) {} else throw std::string("statement \"" #statement "\" evaluated false");); \
  74. }
  75. //! Private macro to assert on a statement's falsehood
  76. #define __POTHOS_TEST_ASSERT_FALSE(message, statement) \
  77. { \
  78. __POTHOS_TEST_STATEMENT(message, if (!(statement)) {} else throw std::string("statement \"" #statement "\" evaluated true");); \
  79. }
  80. //
  81. // Basic assertions
  82. //
  83. //! Test if statement is true
  84. #define POTHOS_TEST_TRUE(statement) \
  85. { \
  86. __POTHOS_TEST_ASSERT("assert true " #statement, statement); \
  87. }
  88. //! Test if statement is false
  89. #define POTHOS_TEST_FALSE(statement) \
  90. { \
  91. __POTHOS_TEST_ASSERT_FALSE("assert false " #statement, statement); \
  92. }
  93. //
  94. // Scalar comparisons
  95. //
  96. //! Test if two values are equal
  97. #define POTHOS_TEST_EQUAL(lhs, rhs) \
  98. { \
  99. __POTHOS_TEST_ASSERT( \
  100. "assert equal " + Pothos::TestingBase::current().toString(lhs) + \
  101. " == " + Pothos::TestingBase::current().toString(rhs), (lhs) == (rhs)); \
  102. }
  103. //! Test if lhs > rhs
  104. #define POTHOS_TEST_GT(lhs, rhs) \
  105. { \
  106. __POTHOS_TEST_ASSERT( \
  107. "assert " + Pothos::TestingBase::current().toString(lhs) + \
  108. " > " + Pothos::TestingBase::current().toString(rhs), (lhs) > (rhs)); \
  109. }
  110. //! Test if lhs >= rhs
  111. #define POTHOS_TEST_GE(lhs, rhs) \
  112. { \
  113. __POTHOS_TEST_ASSERT( \
  114. "assert " + Pothos::TestingBase::current().toString(lhs) + \
  115. " >= " + Pothos::TestingBase::current().toString(rhs), (lhs) >= (rhs)); \
  116. }
  117. //! Test if lhs < rhs
  118. #define POTHOS_TEST_LT(lhs, rhs) \
  119. { \
  120. __POTHOS_TEST_ASSERT( \
  121. "assert " + Pothos::TestingBase::current().toString(lhs) + \
  122. " < " + Pothos::TestingBase::current().toString(rhs), (lhs) < (rhs)); \
  123. }
  124. //! Test if lhs <= rhs
  125. #define POTHOS_TEST_LE(lhs, rhs) \
  126. { \
  127. __POTHOS_TEST_ASSERT( \
  128. "assert " + Pothos::TestingBase::current().toString(lhs) + \
  129. " <= " + Pothos::TestingBase::current().toString(rhs), (lhs) <= (rhs)); \
  130. }
  131. //! Test if two values are equal within tolerance
  132. #define POTHOS_TEST_CLOSE(lhs, rhs, tol) \
  133. { \
  134. __POTHOS_TEST_ASSERT( \
  135. "assert close " + Pothos::TestingBase::current().toString(lhs) + \
  136. " ~= " + Pothos::TestingBase::current().toString(rhs), (std::abs((lhs) - (rhs)) <= (tol))); \
  137. }
  138. //! Test if two values are not equal
  139. #define POTHOS_TEST_NOT_EQUAL(lhs, rhs) \
  140. { \
  141. __POTHOS_TEST_ASSERT( \
  142. "assert not equal " + Pothos::TestingBase::current().toString(lhs) + \
  143. " != " + Pothos::TestingBase::current().toString(rhs), (lhs) != (rhs)); \
  144. }
  145. //! Test if two values are not equal within tolerance
  146. #define POTHOS_TEST_NOT_CLOSE(lhs, rhs, tol) \
  147. { \
  148. __POTHOS_TEST_ASSERT( \
  149. "assert close " + Pothos::TestingBase::current().toString(lhs) + \
  150. " !~= " + Pothos::TestingBase::current().toString(rhs), (std::abs((lhs) - (rhs)) > (tol))); \
  151. }
  152. //
  153. // Vector comparisons
  154. //
  155. //! Test two vectors for equality
  156. #define POTHOS_TEST_EQUALV(lhs, rhs) \
  157. { \
  158. POTHOS_TEST_EQUAL((lhs).size(), (rhs).size()); \
  159. for (size_t i = 0; i < (lhs).size(); i++) \
  160. { \
  161. __POTHOS_TEST_ASSERT( \
  162. "index " + Pothos::TestingBase::current().toString(i) + \
  163. " asserts " + Pothos::TestingBase::current().toString((lhs)[i]) + \
  164. " == " + Pothos::TestingBase::current().toString((rhs)[i]), (lhs)[i] == (rhs)[i]); \
  165. } \
  166. }
  167. //! Test two vectors for equality within tolerance
  168. #define POTHOS_TEST_CLOSEV(lhs, rhs, tol) \
  169. { \
  170. POTHOS_TEST_EQUAL((lhs).size(), (rhs).size()); \
  171. for (size_t i = 0; i < (lhs).size(); i++) \
  172. { \
  173. __POTHOS_TEST_ASSERT( \
  174. "index " + Pothos::TestingBase::current().toString(i) + \
  175. " asserts " + Pothos::TestingBase::current().toString((lhs)[i]) + \
  176. " ~= " + Pothos::TestingBase::current().toString((rhs)[i]), (std::abs((lhs)[i] - (rhs)[i]) <= (tol))); \
  177. } \
  178. }
  179. //! Test two vectors for nonequality
  180. #define POTHOS_TEST_NOT_EQUALV(lhs, rhs) \
  181. { \
  182. if((lhs).size() == (rhs.size())) \
  183. { \
  184. bool anyNotEqual = false; \
  185. for (size_t i = 0; (i < (lhs).size()) && !anyNotEqual; i++) \
  186. { \
  187. anyNotEqual = ((lhs)[i] != (rhs)[i]); \
  188. } \
  189. __POTHOS_TEST_ASSERT( \
  190. "assert not equal " + Pothos::TestingBase::current().toString(lhs) + \
  191. " != " + Pothos::TestingBase::current().toString(rhs), anyNotEqual); \
  192. } \
  193. }
  194. //! Test two vectors for nonequality
  195. #define POTHOS_TEST_NOT_CLOSEV(lhs, rhs, tol) \
  196. { \
  197. if((lhs).size() == (rhs.size())) \
  198. { \
  199. bool anyNotEqual = false; \
  200. for (size_t i = 0; (i < (lhs).size()) && !anyNotEqual; i++) \
  201. { \
  202. anyNotEqual = (std::abs((lhs)[i] - (rhs)[i]) <= (tol)); \
  203. } \
  204. __POTHOS_TEST_ASSERT( \
  205. "assert not close " + Pothos::TestingBase::current().toString(lhs) + \
  206. " !~= " + Pothos::TestingBase::current().toString(rhs), anyNotEqual); \
  207. } \
  208. }
  209. //
  210. // Array comparisons
  211. //
  212. //! Test two arrays for equality
  213. #define POTHOS_TEST_EQUALA(lhs, rhs, size) \
  214. { \
  215. for (size_t i = 0; i < (size); i++) \
  216. { \
  217. __POTHOS_TEST_ASSERT( \
  218. "index " + Pothos::TestingBase::current().toString(i) + \
  219. " asserts " + Pothos::TestingBase::current().toString((lhs)[i]) + \
  220. " == " + Pothos::TestingBase::current().toString((rhs)[i]), (lhs)[i] == (rhs)[i]); \
  221. } \
  222. }
  223. //! Test two arrays for equality within tolerance
  224. #define POTHOS_TEST_CLOSEA(lhs, rhs, tol, size) \
  225. { \
  226. for (size_t i = 0; i < (size); i++) \
  227. { \
  228. __POTHOS_TEST_ASSERT( \
  229. "index " + Pothos::TestingBase::current().toString(i) + \
  230. " asserts " + Pothos::TestingBase::current().toString((lhs)[i]) + \
  231. " ~= " + Pothos::TestingBase::current().toString((rhs)[i]), (std::abs((lhs)[i] - (rhs)[i]) <= (tol))); \
  232. } \
  233. }
  234. //! Test two arrays for nonequality
  235. #define POTHOS_TEST_NOT_EQUALA(lhs, rhs, size) \
  236. { \
  237. bool anyNotEqual = false; \
  238. for (size_t i = 0; (i < (size)) && !anyNotEqual; i++) \
  239. { \
  240. anyNotEqual = ((lhs)[i] != (rhs)[i]); \
  241. } \
  242. __POTHOS_TEST_ASSERT( \
  243. "assert arrays not equal ", anyNotEqual); \
  244. }
  245. //! Test two arrays for nonequality within tolerance
  246. #define POTHOS_TEST_NOT_CLOSEA(lhs, rhs, tol, size) \
  247. { \
  248. bool anyNotEqual = false; \
  249. for (size_t i = 0; (i < (size)) && !anyNotEqual; i++) \
  250. { \
  251. anyNotEqual = (std::abs((lhs)[i] - (rhs)[i]) <= (tol)); \
  252. } \
  253. __POTHOS_TEST_ASSERT( \
  254. "assert arrays not close ", anyNotEqual); \
  255. }
  256. //
  257. // Exception tests
  258. //
  259. //! Test that a statement throws a particular exception
  260. #define POTHOS_TEST_THROWS(statement, expectedException) \
  261. { \
  262. Pothos::TestingBase::current().report(#statement, #statement, "", __LINE__, __FILE__); \
  263. __POTHOS_TEST_STATEMENT(#statement " must throw " #expectedException, \
  264. try{statement;throw std::string("statement \"" #statement "\" did not throw");} \
  265. catch(const expectedException &){}); \
  266. }
  267. namespace Pothos {
  268. struct POTHOS_API TestingBase
  269. {
  270. TestingBase(void);
  271. virtual ~TestingBase(void);
  272. static TestingBase &current(void);
  273. void runTests();
  274. virtual void runTestsImpl() = 0;
  275. void report(
  276. const std::string &message,
  277. const std::string &statement,
  278. const std::string &error,
  279. const int line,
  280. const std::string &file);
  281. template <typename T>
  282. std::string toString(const T &v)
  283. {
  284. return Pothos::Object(v).toString();
  285. }
  286. };
  287. } //namespace Pothos