SimpleTest.h 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #pragma once
  2. #include <cstdio>
  3. #include <map>
  4. #include <string_view>
  5. namespace SimpleTest {
  6. using TestFunc = void (*)();
  7. using Registry = std::map<std::string_view, TestFunc, std::less<>>;
  8. inline Registry g_registry;
  9. inline Registry& registry()
  10. {
  11. return g_registry;
  12. }
  13. struct failure
  14. {
  15. char const* file;
  16. int line;
  17. char const* expr;
  18. };
  19. struct Registrar
  20. {
  21. template <std::size_t N>
  22. Registrar(char const (&name)[N], TestFunc f)
  23. {
  24. auto [it, inserted] =
  25. registry().emplace(std::string_view{ name, N ? (N - 1) : 0 }, f);
  26. if (!inserted) {
  27. std::printf("[ WARN ] duplicate test name: %.*s\n",
  28. int(it->first.size()), it->first.data());
  29. }
  30. }
  31. };
  32. inline Registry const& all()
  33. {
  34. return registry();
  35. }
  36. inline TestFunc find(std::string_view name)
  37. {
  38. auto it = registry().find(name);
  39. return it == registry().end() ? nullptr : it->second;
  40. }
  41. }
  42. #define SIMPLETEST_STRINGIFY(a) #a
  43. #define SIMPLETEST_XSTRINGIFY(a) SIMPLETEST_STRINGIFY(a)
  44. #define SIMPLETEST_CONCAT_(a, b) a##b
  45. #define SIMPLETEST_CONCAT(a, b) SIMPLETEST_CONCAT_(a, b)
  46. #define TEST(name_literal) \
  47. static void SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)(); \
  48. static ::SimpleTest::Registrar SIMPLETEST_CONCAT(_simpletest_reg_, \
  49. __LINE__)( \
  50. name_literal, &SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)); \
  51. static void SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)()
  52. // Minimal assertion
  53. #define REQUIRE(expr) \
  54. do { \
  55. if (!(expr)) \
  56. throw ::SimpleTest::failure{ __FILE__, __LINE__, #expr }; \
  57. } while (0)
  58. int main(int argc, char** argv)
  59. {
  60. using namespace ::SimpleTest;
  61. std::string_view arg1 =
  62. (argc >= 2) ? std::string_view{ argv[1] } : std::string_view{};
  63. if (arg1 == "--list") {
  64. bool first = true;
  65. for (auto const& [name, _] : registry()) {
  66. if (!first)
  67. std::printf(",");
  68. std::printf("%.*s", int(name.size()), name.data());
  69. first = false;
  70. }
  71. std::printf("\n");
  72. return 0;
  73. }
  74. if (arg1 == "--test") {
  75. if (argc < 3) {
  76. std::printf("usage: %s [--list] [--test <name>]\n", argv[0]);
  77. return 2;
  78. }
  79. #ifdef SIMPLETEST_CONFIG
  80. std::printf("SimpleTest built with config: " SIMPLETEST_XSTRINGIFY(
  81. SIMPLETEST_CONFIG) "\n");
  82. #endif
  83. std::string_view name{ argv[2] };
  84. auto it = registry().find(name);
  85. if (it == registry().end()) {
  86. std::printf("[ NOTFOUND ] %s\n", argv[2]);
  87. return 2;
  88. }
  89. int failed = 0;
  90. std::printf("[ RUN ] %.*s\n", int(it->first.size()),
  91. it->first.data());
  92. try {
  93. it->second();
  94. std::printf("[ OK] %.*s\n", int(it->first.size()),
  95. it->first.data());
  96. } catch (failure const& f) {
  97. std::printf("[ FAILED ] %.*s at %s:%d : %s\n", int(it->first.size()),
  98. it->first.data(), f.file, f.line, f.expr);
  99. failed = 1;
  100. } catch (...) {
  101. std::printf("[ FAILED ] %.*s : unknown exception\n",
  102. int(it->first.size()), it->first.data());
  103. failed = 1;
  104. }
  105. return failed;
  106. }
  107. if (argc > 1) {
  108. std::printf("usage: %s [--list] [--test <name>]\n", argv[0]);
  109. return 2;
  110. }
  111. #ifdef SIMPLETEST_CONFIG
  112. std::printf("SimpleTest built with config: " SIMPLETEST_XSTRINGIFY(
  113. SIMPLETEST_CONFIG) "\n");
  114. #endif
  115. // Default: run all tests.
  116. int failed = 0;
  117. for (auto const& [name, func] : all()) {
  118. std::printf("[ RUN ] %.*s\n", int(name.size()), name.data());
  119. try {
  120. func();
  121. std::printf("[ OK ] %.*s\n", int(name.size()), name.data());
  122. } catch (failure const& f) {
  123. std::printf("[ FAILED ] %.*s at %s:%d : %s\n", int(name.size()),
  124. name.data(), f.file, f.line, f.expr);
  125. failed = 1;
  126. } catch (...) {
  127. std::printf("[ FAILED ] %.*s : unknown exception\n", int(name.size()),
  128. name.data());
  129. failed = 1;
  130. }
  131. }
  132. return failed;
  133. }