| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 | #pragma once#include <cstdio>#include <map>#include <string_view>namespace SimpleTest {using TestFunc = void (*)();using Registry = std::map<std::string_view, TestFunc, std::less<>>;inline Registry g_registry;inline Registry& registry(){  return g_registry;}struct failure{  char const* file;  int line;  char const* expr;};struct Registrar{  template <std::size_t N>  Registrar(char const (&name)[N], TestFunc f)  {    auto [it, inserted] =      registry().emplace(std::string_view{ name, N ? (N - 1) : 0 }, f);    if (!inserted) {      std::printf("[  WARN    ] duplicate test name: %.*s\n",                  int(it->first.size()), it->first.data());    }  }};inline Registry const& all(){  return registry();}inline TestFunc find(std::string_view name){  auto it = registry().find(name);  return it == registry().end() ? nullptr : it->second;}}#define SIMPLETEST_CONCAT_(a, b) a##b#define SIMPLETEST_CONCAT(a, b) SIMPLETEST_CONCAT_(a, b)#define TEST(name_literal)                                                    \  static void SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)();                 \  static ::SimpleTest::Registrar SIMPLETEST_CONCAT(_simpletest_reg_,          \                                                   __LINE__)(                 \    name_literal, &SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__));             \  static void SIMPLETEST_CONCAT(_simpletest_fn_, __LINE__)()// Minimal assertion#define REQUIRE(expr)                                                         \  do {                                                                        \    if (!(expr))                                                              \      throw ::SimpleTest::failure{ __FILE__, __LINE__, #expr };               \  } while (0)int main(int argc, char** argv){  using namespace ::SimpleTest;  std::string_view arg1 =    (argc >= 2) ? std::string_view{ argv[1] } : std::string_view{};  if (arg1 == "--list") {    bool first = true;    for (auto const& [name, _] : registry()) {      if (!first)        std::printf(",");      std::printf("%.*s", int(name.size()), name.data());      first = false;    }    std::printf("\n");    return 0;  }  if (arg1 == "--test") {    if (argc < 3) {      std::printf("usage: %s [--list] [--test <name>]\n", argv[0]);      return 2;    }#ifdef SIMPLETEST_CONFIG    std::printf("SimpleTest built with config: %s\n", SIMPLETEST_CONFIG);#endif    std::string_view name{ argv[2] };    auto it = registry().find(name);    if (it == registry().end()) {      std::printf("[ NOTFOUND ] %s\n", argv[2]);      return 2;    }    int failed = 0;    std::printf("[ RUN      ] %.*s\n", int(it->first.size()),                it->first.data());    try {      it->second();      std::printf("[       OK] %.*s\n", int(it->first.size()),                  it->first.data());    } catch (failure const& f) {      std::printf("[  FAILED  ] %.*s at %s:%d : %s\n", int(it->first.size()),                  it->first.data(), f.file, f.line, f.expr);      failed = 1;    } catch (...) {      std::printf("[  FAILED  ] %.*s : unknown exception\n",                  int(it->first.size()), it->first.data());      failed = 1;    }    return failed;  }  if (argc > 1) {    std::printf("usage: %s [--list] [--test <name>]\n", argv[0]);    return 2;  }#ifdef SIMPLETEST_CONFIG  std::printf("SimpleTest built with config: %s\n", SIMPLETEST_CONFIG);#endif  // Default: run all tests.  int failed = 0;  for (auto const& [name, func] : all()) {    std::printf("[ RUN      ] %.*s\n", int(name.size()), name.data());    try {      func();      std::printf("[       OK ] %.*s\n", int(name.size()), name.data());    } catch (failure const& f) {      std::printf("[  FAILED  ] %.*s at %s:%d : %s\n", int(name.size()),                  name.data(), f.file, f.line, f.expr);      failed = 1;    } catch (...) {      std::printf("[  FAILED  ] %.*s : unknown exception\n", int(name.size()),                  name.data());      failed = 1;    }  }  return failed;}
 |