You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

433 rivejä
13 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the Do What The Fuck You Want To
  7. // Public License, Version 2, as published by Sam Hocevar. See
  8. // http://www.wtfpl.net/ for more details.
  9. //
  10. //
  11. // The Unit test framework
  12. // -----------------------
  13. //
  14. #if !defined __LOL_UNIT_H__
  15. #define __LOL_UNIT_H__
  16. #include <iostream>
  17. #include <iomanip>
  18. #include <sstream>
  19. #include <cstdio>
  20. namespace lol
  21. {
  22. /*
  23. * This is the base class for all fixtures. It keeps track of all
  24. * fixtures registered through the lolunit_declare_fixture macro and puts them
  25. * in a linked list.
  26. */
  27. class FixtureBase
  28. {
  29. friend class TextTestRunner;
  30. public:
  31. virtual void SetUp(void) {};
  32. virtual void TearDown(void) {};
  33. protected:
  34. FixtureBase() : m_next(NULL), m_testcases(0), m_failcases(0) {}
  35. virtual ~FixtureBase() {}
  36. static void AddFixture(FixtureBase *fixture)
  37. {
  38. /* Protect against several instances of the same Fixture subclass */
  39. if (fixture->MarkFixture())
  40. return;
  41. FixtureListHelper(fixture);
  42. }
  43. static FixtureBase *FixtureList() { return FixtureListHelper(NULL); }
  44. virtual void RunFixture() = 0;
  45. virtual bool MarkFixture() = 0;
  46. /* Prevent compiler complaints about unreachable code */
  47. static inline bool True() { return true; }
  48. FixtureBase *m_next;
  49. int m_testcases, m_failcases;
  50. int m_asserts, m_failure;
  51. char const *m_fixturename, *m_currentname;
  52. std::stringstream m_errorlog, m_context;
  53. private:
  54. /* The FixtureBase class keeps track of all instanciated children
  55. * fixtures through this method. */
  56. static FixtureBase *FixtureListHelper(FixtureBase *set)
  57. {
  58. static FixtureBase *head = NULL, *tail = NULL;
  59. if (set)
  60. {
  61. if (!head) head = set;
  62. if (tail) tail->m_next = set;
  63. tail = set;
  64. }
  65. return head;
  66. }
  67. };
  68. /*
  69. * This template specialises FixtureBase and provides registration of
  70. * test cases in a linked list through the lolunit_declare_test macro.
  71. */
  72. template<class T> class Fixture : protected FixtureBase
  73. {
  74. public:
  75. typedef T FixtureClass;
  76. struct TestCase
  77. {
  78. void (FixtureClass::* m_fun)();
  79. char const *m_testname;
  80. TestCase *m_next;
  81. protected:
  82. static inline std::string make_msg(std::string const str)
  83. {
  84. return "- " + str + "\n";
  85. }
  86. };
  87. Fixture()
  88. {
  89. AddFixture(this);
  90. }
  91. /* Run all test cases in this fixture. */
  92. virtual void RunFixture()
  93. {
  94. m_errorlog.str("");
  95. m_testcases = 0;
  96. m_failcases = 0;
  97. for (TestCase *c = TestCaseList(); c; c = c->m_next)
  98. {
  99. m_testcases++;
  100. m_asserts = 0;
  101. m_failure = false;
  102. m_currentname = c->m_testname;
  103. m_context.str("");
  104. std::cout << '.';
  105. (static_cast<FixtureClass *>(this)->*c->m_fun)();
  106. if (m_failure) std::cout << 'F';
  107. }
  108. }
  109. /* Mark the current fixture type as already registered and return whether
  110. * it was seen before. */
  111. virtual bool MarkFixture()
  112. {
  113. static bool seen = false;
  114. if (seen)
  115. {
  116. SealFixture();
  117. return true;
  118. }
  119. seen = true;
  120. return false;
  121. }
  122. /* Manage Fixture sealing. Once SealFixture() has been called, we
  123. * will no longer accept TestCase registrations. */
  124. static void SealFixture() { SealFixtureHelper(true); }
  125. static bool IsFixtureSealed() { return SealFixtureHelper(false); }
  126. /* Each Fixture class specialisation keeps track of its instanciated
  127. * test cases. */
  128. static void AddTestCase(TestCase *that, char const *name,
  129. void (FixtureClass::*fun)())
  130. {
  131. if (IsFixtureSealed())
  132. return;
  133. that->m_fun = fun;
  134. that->m_testname = name;
  135. that->m_next = NULL;
  136. TestCaseListHelper(that);
  137. }
  138. static TestCase *TestCaseList() { return TestCaseListHelper(NULL); }
  139. private:
  140. static bool SealFixtureHelper(bool set)
  141. {
  142. static bool sealed = false;
  143. if (set) sealed = true;
  144. return sealed;
  145. }
  146. static TestCase *TestCaseListHelper(TestCase *set)
  147. {
  148. static TestCase *head = NULL, *tail = NULL;
  149. if (set)
  150. {
  151. if (!head) head = set;
  152. if (tail) tail->m_next = set;
  153. tail = set;
  154. }
  155. return head;
  156. }
  157. };
  158. /*
  159. * This simple class runs all automatically registered tests and reports
  160. * on error and success in the standard output.
  161. */
  162. class TextTestRunner
  163. {
  164. public:
  165. bool Run()
  166. {
  167. bool ret = true;
  168. std::stringstream errors("");
  169. int failcases = 0, testcases = 0;
  170. for (FixtureBase *f = FixtureBase::FixtureList(); f; f = f->m_next)
  171. {
  172. f->SetUp();
  173. f->RunFixture();
  174. f->TearDown();
  175. errors << f->m_errorlog.str();
  176. testcases += f->m_testcases;
  177. failcases += f->m_failcases;
  178. }
  179. std::cout << "\n";
  180. std::cout << "\n\n";
  181. if (failcases)
  182. {
  183. std::cout << "!!!FAILURES!!!\n";
  184. std::cout << "Test Results:\n";
  185. std::cout << "Run: " << testcases
  186. << " Failures: " << failcases
  187. << " Errors: 0\n"; /* TODO: handle errors */
  188. std::cout << errors.str();
  189. ret = false;
  190. }
  191. else
  192. {
  193. std::cout << "OK (" << testcases << " tests)\n";
  194. }
  195. std::cout << "\n\n";
  196. return ret;
  197. }
  198. };
  199. #define lolunit_assert_generic(msg, cond) \
  200. do { \
  201. m_asserts++; \
  202. if (True() && !(cond)) \
  203. { \
  204. m_errorlog << "\n\n"; \
  205. m_errorlog << ++m_failcases << ") test: " \
  206. << lol_unit_helper_name(this) << "::" << m_currentname \
  207. << " (F) line: " << __LINE__ << " " \
  208. << __FILE__ << "\n"; \
  209. m_errorlog << "assertion failed\n"; \
  210. m_errorlog << "- Expression: " << #cond << "\n"; \
  211. m_errorlog << msg; \
  212. m_failure = true; \
  213. return; \
  214. } \
  215. } while (!True())
  216. #define lolunit_assert_op(op, modifier, opdesc, msg, a, b) \
  217. do { \
  218. m_asserts++; \
  219. if (True() && !modifier((a) op (b))) \
  220. { \
  221. m_errorlog << "\n\n"; \
  222. m_errorlog << ++m_failcases << ") test: " \
  223. << lol_unit_helper_name(this) << "::" << m_currentname \
  224. << " (F) line: " << __LINE__ << " " \
  225. << __FILE__ << "\n"; \
  226. m_errorlog << opdesc << " assertion failed\n"; \
  227. m_errorlog << "- Expected: " << #a << " = " << (a) << "\n"; \
  228. m_errorlog << "- Actual : " << #b << " = " << (b) << "\n"; \
  229. m_errorlog << msg; \
  230. m_errorlog << m_context.str(); \
  231. m_failure = true; \
  232. return; \
  233. } \
  234. } while (!True())
  235. #define lolunit_assert_doubles_equal_generic(msg, a, b, t) \
  236. do { \
  237. m_asserts++; \
  238. using std::fabs; \
  239. if (True() && fabs((a) - (b)) > fabs((t))) \
  240. { \
  241. m_errorlog << "\n\n"; \
  242. m_errorlog << ++m_failcases << ") test: " \
  243. << lol_unit_helper_name(this) << "::" << m_currentname \
  244. << " (F) line: " << __LINE__ << " " \
  245. << __FILE__ << "\n"; \
  246. m_errorlog << "double equality assertion failed\n"; \
  247. std::streamsize old_prec = m_errorlog.precision(); \
  248. m_errorlog << std::setprecision(16); \
  249. m_errorlog << "- Expected: " << #a << " = " << (a) << "\n"; \
  250. m_errorlog << "- Actual : " << #b << " = " << (b) << "\n"; \
  251. m_errorlog << "- Delta : " << (t) << "\n"; \
  252. m_errorlog << std::setprecision(old_prec); \
  253. m_errorlog << msg; \
  254. m_errorlog << m_context.str(); \
  255. m_failure = true; \
  256. return; \
  257. } \
  258. } while (!True())
  259. /*
  260. * Public helper macros
  261. */
  262. #define lolunit_declare_fixture(N) \
  263. class N; \
  264. /* This pattern allows us to statically create a Fixture instance \
  265. * before its exact implementation was defined. */ \
  266. template<typename T> struct lol_unit_helper_fixture_##N \
  267. { \
  268. lol_unit_helper_fixture_##N() { p = new T(); } \
  269. ~lol_unit_helper_fixture_##N() { delete p; } \
  270. T *p; \
  271. }; \
  272. lol_unit_helper_fixture_##N<N> lol_unit_helper_fixture_##N##_instance; \
  273. /* Allow to retrieve the class name without using RTTI and without \
  274. * knowing the type of "this". */ \
  275. static inline char const *lol_unit_helper_name(N *p) \
  276. { \
  277. (void)p; \
  278. return #N; \
  279. } \
  280. /* Now the user can define the implementation */ \
  281. class N : public lol::Fixture<N>
  282. #define lolunit_declare_test(N) \
  283. /* For each test in the fixture, we create an object that will \
  284. * automatically register the test method in a list global to the \
  285. * specialised fixture. */ \
  286. class lol_unit_helper_test_##N : public TestCase \
  287. { \
  288. public: \
  289. lol_unit_helper_test_##N() \
  290. { \
  291. AddTestCase(this, #N, \
  292. (void (FixtureClass::*)()) &FixtureClass::N); \
  293. } \
  294. }; \
  295. lol_unit_helper_test_##N lol_unit_helper_test_instance_##N; \
  296. void N()
  297. /*
  298. * Provide context for error messages
  299. */
  300. #define lolunit_set_context(n) \
  301. do { \
  302. m_context.str(""); \
  303. m_context << "- Context : " << #n << " = " << (n) << "\n"; \
  304. } while (!True())
  305. #define lolunit_unset_context(n) \
  306. m_context.str("")
  307. /*
  308. * Public assert macros
  309. */
  310. #define lolunit_fail(msg) \
  311. do { \
  312. m_asserts++; \
  313. m_errorlog << "\n\n"; \
  314. m_errorlog << ++m_failcases << ") test: " \
  315. << lol_unit_helper_name(this) << "::" << m_currentname \
  316. << " (F) line: " << __LINE__ << " " \
  317. << __FILE__ << "\n"; \
  318. m_errorlog << "forced failure\n"; \
  319. m_errorlog << make_msg(msg); \
  320. m_errorlog << m_context.str(); \
  321. m_failure = true; \
  322. return; \
  323. } while (!True())
  324. #define lolunit_assert(cond) \
  325. lolunit_assert_generic("", cond)
  326. #define lolunit_assert_message(m, cond) \
  327. lolunit_assert_generic(make_msg(m), cond)
  328. #define lolunit_assert_equal(a, b) \
  329. lolunit_assert_op(==, (bool), "equality", "", a, b)
  330. #define lolunit_assert_equal_message(m, a, b) \
  331. lolunit_assert_op(==, (bool), "equality", make_msg(m), a, b)
  332. #define lolunit_assert_different(a, b) \
  333. lolunit_assert_op(!=, (bool), "inequality", "", a, b)
  334. #define lolunit_assert_different_message(m, a, b) \
  335. lolunit_assert_op(!=, (bool), "inequality", make_msg(m), a, b)
  336. #define lolunit_assert_less(a, b) \
  337. lolunit_assert_op(<, (bool), "less than", "", a, b)
  338. #define lolunit_assert_less_message(m, a, b) \
  339. lolunit_assert_op(<, (bool), "less than", make_msg(m), a, b)
  340. #define lolunit_assert_lequal(a, b) \
  341. lolunit_assert_op(<=, (bool), "less than or equal", "", a, b)
  342. #define lolunit_assert_lequal_message(m, a, b) \
  343. lolunit_assert_op(<=, (bool), "less than or equal", make_msg(m), a, b)
  344. #define lolunit_assert_greater(a, b) \
  345. lolunit_assert_op(>, (bool), "greater than", "", a, b)
  346. #define lolunit_assert_greater_message(m, a, b) \
  347. lolunit_assert_op(>, (bool), "greater than", make_msg(m), a, b)
  348. #define lolunit_assert_gequal(a, b) \
  349. lolunit_assert_op(>=, (bool), "greater than or equal", "", a, b)
  350. #define lolunit_assert_gequal_message(m, a, b) \
  351. lolunit_assert_op(>=, (bool), "greater than or equal", make_msg(m), a, b)
  352. #define lolunit_assert_not_equal(a, b) \
  353. lolunit_assert_op(==, !, "not equality", "", a, b)
  354. #define lolunit_assert_not_equal_message(m, a, b) \
  355. lolunit_assert_op(==, !, "not equality", make_msg(m), a, b)
  356. #define lolunit_assert_not_different(a, b) \
  357. lolunit_assert_op(!=, !, "not inequality", "", a, b)
  358. #define lolunit_assert_not_different_message(m, a, b) \
  359. lolunit_assert_op(!=, !, "not inequality", make_msg(m), a, b)
  360. #define lolunit_assert_not_less(a, b) \
  361. lolunit_assert_op(<, !, "not less than", "", a, b)
  362. #define lolunit_assert_not_less_message(m, a, b) \
  363. lolunit_assert_op(<, !, "not less than", make_msg(m), a, b)
  364. #define lolunit_assert_not_lequal(a, b) \
  365. lolunit_assert_op(<=, !, "not less than or equal", "", a, b)
  366. #define lolunit_assert_not_lequal_message(m, a, b) \
  367. lolunit_assert_op(<=, !, "not less than or equal", make_msg(m), a, b)
  368. #define lolunit_assert_not_greater(a, b) \
  369. lolunit_assert_op(>, !, "not greater than", "", a, b)
  370. #define lolunit_assert_not_greater_message(m, a, b) \
  371. lolunit_assert_op(>, !, "not greater than", make_msg(m), a, b)
  372. #define lolunit_assert_not_gequal(a, b) \
  373. lolunit_assert_op(>=, !, "not greater than or equal", "", a, b)
  374. #define lolunit_assert_not_gequal_message(m, a, b) \
  375. lolunit_assert_op(>=, !, "not greater than or equal", make_msg(m), a, b)
  376. #define lolunit_assert_doubles_equal(a, b, t) \
  377. lolunit_assert_doubles_equal_generic("", a, b, t)
  378. #define lolunit_assert_doubles_equal_message(msg, a, b, t) \
  379. lolunit_assert_doubles_equal_generic(make_msg(msg), a, b, t)
  380. } /* namespace lol */
  381. #endif // __LOL_UNIT_H__