#include "minitest.h" #include #include #include #include namespace { struct TestInfo { const char* test_name; const char* case_name; minitest::TestFunction* test_function; TestInfo* next; }; TestInfo* g_test_infos; TestInfo** g_test_infos_tail; } // namespace namespace minitest { namespace internal { String::String(const char* str, size_t len) { Resize(len); ::memcpy(str_, str, len); size_ = len; } String& String::operator+=(const String& other) { size_t old_size = size_; Resize(old_size + other.size_); ::memcpy(str_ + old_size, other.str_, other.size_); return *this; } String& String::operator+=(const char* str) { size_t len = ::strlen(str); size_t old_size = size_; Resize(old_size + len); ::memcpy(str_ + old_size, str, len); return *this; } String& String::operator+=(char ch) { Resize(size_ + 1); str_[size_ - 1] = ch; return *this; } String& String::operator<<(const String& other) { (*this) += other; return *this; } String& String::operator<<(const char* str) { (*this) += str; return *this; } String& String::operator<<(char ch) { (*this) += ch; return *this; } String& String::operator<<(bool v) { (*this) += (v ? "true" : "false"); return *this; } #define MINITEST_STRING_OPERATOR_LL_(ParamType, Format) \ String& String::operator<<(ParamType v) { \ char buf[20]; \ ::snprintf(buf, sizeof(buf), Format, v); \ (*this) += buf; \ return *this; \ } MINITEST_STRING_OPERATOR_LL_(signed char, "%hhd") MINITEST_STRING_OPERATOR_LL_(unsigned char, "%hhu") MINITEST_STRING_OPERATOR_LL_(short, "%hd") MINITEST_STRING_OPERATOR_LL_(unsigned short, "%hu"); MINITEST_STRING_OPERATOR_LL_(int, "%d") MINITEST_STRING_OPERATOR_LL_(unsigned, "%u") MINITEST_STRING_OPERATOR_LL_(long, "%ld") MINITEST_STRING_OPERATOR_LL_(unsigned long, "%lu") MINITEST_STRING_OPERATOR_LL_(long long, "%lld") MINITEST_STRING_OPERATOR_LL_(unsigned long long, "%llu") MINITEST_STRING_OPERATOR_LL_(float, "%f") MINITEST_STRING_OPERATOR_LL_(double, "%f") MINITEST_STRING_OPERATOR_LL_(long double, "%Lf") MINITEST_STRING_OPERATOR_LL_(const void*, "%p") #undef MINITEST_STRING_OPERATOR_LL_ void String::Clear() { ::free(str_); str_ = NULL; size_ = 0; capacity_ = 0; } void String::Resize(size_t new_size) { if (new_size > capacity_) { size_t new_capacity = capacity_; while (new_capacity < new_size) new_capacity += (new_capacity >> 1) + 8; Reserve(new_capacity); } str_[new_size] = '\0'; size_ = new_size; } void String::Reserve(size_t new_capacity) { str_ = reinterpret_cast(::realloc(str_, new_capacity + 1)); if (new_capacity > capacity_) ::memset(str_ + capacity_, '\0', new_capacity - capacity_); capacity_ = new_capacity; } } // namespace internal internal::String Format(const char* format, ...) { internal::String result; va_list args, args2; va_start(args, format); // Note: Resize(n) allocates at least n+1 bytes. result.Resize(100); int len; for (;;) { va_copy(args2, args); len = vsnprintf(&result[0], result.size(), format, args2); va_end(args2); // On Windows, snprintf() returns -1 on truncation. On other // platforms, it returns the size of the string, without truncation. if (len >= 0 && static_cast(len) <= result.size()) break; result.Resize(result.size() * 2); } va_end(args); return result; } void TestCase::Failure() { if (result_ == PASS) result_ = FAIL; if (!text_.empty()) printf("%s\n", text_.c_str()); } void TestCase::FatalFailure() { result_ = FATAL; if (!text_.empty()) printf("%s\n", text_.c_str()); } internal::String& TestCase::GetText() { text_.Clear(); return text_; } void RegisterTest(const char* test_name, const char* case_name, TestFunction* test_function) { if (g_test_infos_tail == NULL) g_test_infos_tail = &g_test_infos; TestInfo* info = reinterpret_cast(::malloc(sizeof(*info))); info->test_name = test_name; info->case_name = case_name; info->test_function = test_function; *g_test_infos_tail = info; g_test_infos_tail = &info->next; } } // namespace minitest int main(void) { printf("--- TESTS STARTING ---\n"); TestInfo* info = g_test_infos; unsigned num_failures = 0; unsigned num_tests = 0; for (; info != NULL; info = info->next) { minitest::TestCase testcase; printf("[ RUNNING ] %s.%s\n", info->test_name, info->case_name); num_tests += 1; info->test_function(&testcase); const char* status; switch (testcase.result()) { case minitest::TestCase::PASS: status = "OK"; break; case minitest::TestCase::FAIL: case minitest::TestCase::FATAL: status = "FAIL"; num_failures += 1; break; } printf("[ %9s ] %s.%s\n", status, info->test_name, info->case_name); } printf("--- TESTS COMPLETED ---\n"); printf("tests completed: %d\n", num_tests); printf("tests passed: %d\n", num_tests - num_failures); printf("tests failed: %d\n", num_failures); return (num_failures > 0); }