tester: Update the testing library
This commit is contained in:
parent
f629c019e6
commit
2665e315e6
@ -15,7 +15,8 @@ pushd Build
|
|||||||
REM Z7 Combine multi-debug files to one debug file
|
REM Z7 Combine multi-debug files to one debug file
|
||||||
REM wd4201 Nonstandard extension used: nameless struct/union
|
REM wd4201 Nonstandard extension used: nameless struct/union
|
||||||
REM Tp Treat header file as CPP source file
|
REM Tp Treat header file as CPP source file
|
||||||
set flags=-MT -EHa -GR- -Od -Oi -Z7 -wd4201 -D DQN_TEST_WITH_MAIN %code_dir%Dqn_Tests.cpp -link -nologo
|
set compile_flags=-MT -EHa -GR- -Od -Oi -Z7 -wd4201 -D DQN_TEST_WITH_MAIN
|
||||||
|
set linker_flags=-link -nologo
|
||||||
set msvc_flags=-fsanitize=address -D STBSP__ASAN=__declspec(no_sanitize_address)
|
set msvc_flags=-fsanitize=address -D STBSP__ASAN=__declspec(no_sanitize_address)
|
||||||
set clang_flags=-fsanitize=address -fsanitize=undefined
|
set clang_flags=-fsanitize=address -fsanitize=undefined
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ pushd Build
|
|||||||
|
|
||||||
if not exist msvc mkdir msvc
|
if not exist msvc mkdir msvc
|
||||||
pushd msvc
|
pushd msvc
|
||||||
cl %msvc_flags% %flags%
|
cl %compile_flags% %msvc_flags% %code_dir%Dqn_Tests.cpp %link_flags%
|
||||||
popd
|
popd
|
||||||
|
|
||||||
REM ------------------------------------------------------------------------
|
REM ------------------------------------------------------------------------
|
||||||
@ -42,6 +43,6 @@ pushd Build
|
|||||||
|
|
||||||
if not exist clang mkdir clang
|
if not exist clang mkdir clang
|
||||||
pushd clang
|
pushd clang
|
||||||
clang-cl %clang_flags% %flags%
|
clang-cl %compile_flags% %clang_flags% %code_dir%Dqn_Tests.cpp %link_flags%
|
||||||
popd
|
popd
|
||||||
popd
|
popd
|
||||||
|
195
dqn_tester.h
195
dqn_tester.h
@ -1,12 +1,11 @@
|
|||||||
#if !defined(DQN_TESTER_H)
|
#if !defined(DQN_TESTER_H)
|
||||||
#define DQN_TESTER_H
|
#define DQN_TESTER_H
|
||||||
// -----------------------------------------------------------------------------
|
//
|
||||||
// NOTE: Overview
|
// NOTE: Overview
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// A super minimal testing framework, most of the logic here is the pretty
|
// A super minimal testing framework, most of the logic here is the pretty
|
||||||
// printing of test results.
|
// printing of test results.
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// NOTE: Configuration
|
// NOTE: Configuration
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// #define DQN_TESTER_IMPLEMENTATION
|
// #define DQN_TESTER_IMPLEMENTATION
|
||||||
@ -34,11 +33,11 @@
|
|||||||
// Define this to a terminal color code to specify what color sucess will be
|
// Define this to a terminal color code to specify what color sucess will be
|
||||||
// presented as.
|
// presented as.
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// NOTE: Macros
|
// NOTE: Macros
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#if !defined(DQN_TESTER_RESULT_LPAD)
|
#if !defined(DQN_TESTER_RESULT_LPAD)
|
||||||
#define DQN_TESTER_RESULT_LPAD 90
|
#define DQN_TESTER_RESULT_LPAD 90
|
||||||
@ -63,99 +62,115 @@
|
|||||||
#define DQN_TESTER_COLOR_RESET "\x1b[0m"
|
#define DQN_TESTER_COLOR_RESET "\x1b[0m"
|
||||||
|
|
||||||
#define DQN_TESTER_BEGIN_GROUP(fmt, ...) fprintf(stdout, fmt "\n", ##__VA_ARGS__)
|
#define DQN_TESTER_BEGIN_GROUP(fmt, ...) fprintf(stdout, fmt "\n", ##__VA_ARGS__)
|
||||||
#define DQN_TESTER_END_GROUP(test) \
|
#define DQN_TESTER_END_GROUP(test) \
|
||||||
do \
|
do { \
|
||||||
{ \
|
bool all_clear = (test)->num_tests_ok_in_group == (test)->num_tests_in_group; \
|
||||||
bool all_clear = (test)->num_tests_ok_in_group == (test)->num_tests_in_group; \
|
fprintf(stdout, \
|
||||||
fprintf(stdout, \
|
"%s\n %02d/%02d tests passed -- %s\n\n" DQN_TESTER_COLOR_RESET, \
|
||||||
"%s\n %02d/%02d tests passed -- %s\n\n" DQN_TESTER_COLOR_RESET, \
|
all_clear ? DQN_TESTER_GOOD_COLOR : DQN_TESTER_BAD_COLOR, \
|
||||||
all_clear ? DQN_TESTER_GOOD_COLOR : DQN_TESTER_BAD_COLOR, \
|
(test)->num_tests_ok_in_group, \
|
||||||
(test)->num_tests_ok_in_group, \
|
(test)->num_tests_in_group, \
|
||||||
(test)->num_tests_in_group, \
|
all_clear ? "OK" : "FAILED"); \
|
||||||
all_clear ? "OK" : "FAILED"); \
|
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define DQN_TESTER_ASSERTF(test, expr, fmt, ...) DQN_TESTER_ASSERTF_AT((test), __FILE__, __LINE__, (expr), fmt, ## __VA_ARGS__)
|
#define DQN_TESTER_ASSERTF(test, expr, fmt, ...) \
|
||||||
|
DQN_TESTER_ASSERTF_AT((test), __FILE__, __LINE__, (expr), fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
#define DQN_TESTER_ASSERT(test, expr) DQN_TESTER_ASSERT_AT((test), __FILE__, __LINE__, (expr))
|
#define DQN_TESTER_ASSERT(test, expr) DQN_TESTER_ASSERT_AT((test), __FILE__, __LINE__, (expr))
|
||||||
|
|
||||||
#define DQN_TESTER_LOG(test, fmt, ...) \
|
#define DQN_TESTER_LOG(test, fmt, ...) \
|
||||||
do \
|
do { \
|
||||||
{ \
|
if ((test)->log_count++ == 0) { \
|
||||||
if ((test)->log_count++ == 0) fprintf(stdout, "\n"); \
|
fprintf(stdout, "\n"); \
|
||||||
fprintf(stdout, "%*s" fmt "\n", DQN_TESTER_SPACING * 2, "", ##__VA_ARGS__); \
|
} \
|
||||||
|
fprintf(stdout, "%*sLog: " fmt "\n", DQN_TESTER_SPACING * 2, "", ##__VA_ARGS__); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define DQN_TESTER_ASSERTF_AT(test, file, line, expr, fmt, ...) \
|
#define DQN_TESTER_ASSERTF_AT(test, file, line, expr, fmt, ...) \
|
||||||
do \
|
do { \
|
||||||
{ \
|
if (!(expr)) { \
|
||||||
if (!(expr)) \
|
if ((test)->log_count++ == 0) { \
|
||||||
{ \
|
fprintf(stdout, "\n"); \
|
||||||
if ((test)->log_count++ == 0) fprintf(stdout, "\n"); \
|
} \
|
||||||
(test)->failed = true; \
|
(test)->state = Dqn_TesterState_TestFailed; \
|
||||||
fprintf(stderr, \
|
fprintf(stderr, \
|
||||||
"%*sFile: %s:%d\n" \
|
"%*sAssertion Triggered\n" \
|
||||||
"%*sExpression: [" #expr "]\n" \
|
"%*sFile: %s:%d\n" \
|
||||||
"%*sReason: " fmt "\n", \
|
"%*sExpression: [" #expr "]\n" \
|
||||||
DQN_TESTER_SPACING * 2, \
|
"%*sReason: " fmt "\n", \
|
||||||
"", \
|
DQN_TESTER_SPACING * 2, \
|
||||||
file, \
|
"", \
|
||||||
line, \
|
DQN_TESTER_SPACING * 3, \
|
||||||
DQN_TESTER_SPACING * 2, \
|
"", \
|
||||||
"", \
|
file, \
|
||||||
DQN_TESTER_SPACING * 2, \
|
line, \
|
||||||
"", \
|
DQN_TESTER_SPACING * 3, \
|
||||||
##__VA_ARGS__); \
|
"", \
|
||||||
} \
|
DQN_TESTER_SPACING * 3, \
|
||||||
|
"", \
|
||||||
|
##__VA_ARGS__); \
|
||||||
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define DQN_TESTER_ASSERT_AT(test, file, line, expr) \
|
#define DQN_TESTER_ASSERT_AT(test, file, line, expr) \
|
||||||
do \
|
do { \
|
||||||
{ \
|
if (!(expr)) { \
|
||||||
if (!(expr)) \
|
if ((test)->log_count++ == 0) { \
|
||||||
{ \
|
fprintf(stdout, "\n"); \
|
||||||
if ((test)->log_count++ == 0) fprintf(stdout, "\n"); \
|
} \
|
||||||
(test)->failed = true; \
|
(test)->state = Dqn_TesterState_TestFailed; \
|
||||||
fprintf(stderr, \
|
fprintf(stderr, \
|
||||||
"%*sFile: %s:%d\n" \
|
"%*sFile: %s:%d\n" \
|
||||||
"%*sExpression: [" #expr "]\n", \
|
"%*sExpression: [" #expr "]\n", \
|
||||||
DQN_TESTER_SPACING * 2, \
|
DQN_TESTER_SPACING * 2, \
|
||||||
"", \
|
"", \
|
||||||
file, \
|
file, \
|
||||||
line, \
|
line, \
|
||||||
DQN_TESTER_SPACING * 2, \
|
DQN_TESTER_SPACING * 2, \
|
||||||
""); \
|
""); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// NOTE: Header
|
// NOTE: Header
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
typedef struct Dqn_Tester
|
enum Dqn_TesterState {
|
||||||
{
|
Dqn_TesterState_Nil,
|
||||||
int num_tests_in_group;
|
Dqn_TesterState_TestBegun,
|
||||||
int num_tests_ok_in_group;
|
Dqn_TesterState_TestFailed,
|
||||||
int log_count;
|
};
|
||||||
bool failed;
|
|
||||||
|
typedef struct Dqn_Tester {
|
||||||
|
int num_tests_in_group;
|
||||||
|
int num_tests_ok_in_group;
|
||||||
|
int log_count;
|
||||||
|
Dqn_TesterState state;
|
||||||
} Dqn_Tester;
|
} Dqn_Tester;
|
||||||
|
|
||||||
|
void Dqn_TesterBeginV(Dqn_Tester *test, char const *fmt, va_list args);
|
||||||
void Dqn_TesterBegin(Dqn_Tester *test, char const *fmt, ...);
|
void Dqn_TesterBegin(Dqn_Tester *test, char const *fmt, ...);
|
||||||
void Dqn_TesterEnd(Dqn_Tester *test);
|
void Dqn_TesterEnd(Dqn_Tester *test);
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
struct Dqn_TesterBeginScopedTest {
|
||||||
|
Dqn_TesterBeginScopedTest(Dqn_Tester *test, char const *fmt, ...);
|
||||||
|
~Dqn_TesterBeginScopedTest();
|
||||||
|
Dqn_Tester *test;
|
||||||
|
};
|
||||||
|
#endif // __cplusplus
|
||||||
#endif // DQN_TESTER_H
|
#endif // DQN_TESTER_H
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// NOTE: Implementation
|
// NOTE: Implementation
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
#if defined(DQN_TESTER_IMPLEMENTATION)
|
#if defined(DQN_TESTER_IMPLEMENTATION)
|
||||||
void Dqn_TesterBegin(Dqn_Tester *test, char const *fmt, ...)
|
void Dqn_TesterBeginV(Dqn_Tester *test, char const *fmt, va_list args)
|
||||||
{
|
{
|
||||||
test->num_tests_in_group++;
|
assert(test->state == Dqn_TesterState_Nil &&
|
||||||
test->failed = false;
|
"Nesting a unit test within another unit test is not allowed, ensure"
|
||||||
test->log_count = 0;
|
"the first test has finished by calling Dqn_TesterEnd");
|
||||||
|
|
||||||
va_list args;
|
test->num_tests_in_group++;
|
||||||
va_start(args, fmt);
|
test->state = Dqn_TesterState_TestBegun;
|
||||||
|
test->log_count = 0;
|
||||||
|
|
||||||
int size_required = 0;
|
int size_required = 0;
|
||||||
{
|
{
|
||||||
@ -169,33 +184,55 @@ void Dqn_TesterBegin(Dqn_Tester *test, char const *fmt, ...)
|
|||||||
vprintf(fmt, args);
|
vprintf(fmt, args);
|
||||||
for (int pad = DQN_TESTER_SPACING + size_required; pad < DQN_TESTER_RESULT_LPAD; pad++)
|
for (int pad = DQN_TESTER_SPACING + size_required; pad < DQN_TESTER_RESULT_LPAD; pad++)
|
||||||
putc(DQN_TESTER_RESULT_PAD_CHAR, stdout);
|
putc(DQN_TESTER_RESULT_PAD_CHAR, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dqn_TesterBegin(Dqn_Tester *test, char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_TesterBeginV(test, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dqn_TesterEnd(Dqn_Tester *test)
|
void Dqn_TesterEnd(Dqn_Tester *test)
|
||||||
{
|
{
|
||||||
if (test->log_count != 0)
|
assert(test->state != Dqn_TesterState_Nil && "Test was marked as ended but a test was never commenced using Dqn_TesterBegin");
|
||||||
{
|
if (test->log_count != 0) {
|
||||||
// NOTE: We try and print the result on the same line as the test name,
|
// NOTE: We try and print the result on the same line as the test name,
|
||||||
// but if there were logs printed throughout the test then we must print
|
// but if there were logs printed throughout the test then we must print
|
||||||
// the result on a new line.
|
// the result on a new line.
|
||||||
|
|
||||||
printf("%*s", DQN_TESTER_SPACING, "");
|
printf("%*s", DQN_TESTER_SPACING, "");
|
||||||
for (int pad = DQN_TESTER_SPACING; pad < DQN_TESTER_RESULT_LPAD; pad++)
|
for (int pad = DQN_TESTER_SPACING; pad < DQN_TESTER_RESULT_LPAD; pad++)
|
||||||
putc(DQN_TESTER_RESULT_PAD_CHAR, stdout);
|
putc(DQN_TESTER_RESULT_PAD_CHAR, stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test->failed)
|
if (test->state == Dqn_TesterState_TestFailed) {
|
||||||
fprintf(stdout, DQN_TESTER_BAD_COLOR " FAILED");
|
fprintf(stdout, DQN_TESTER_BAD_COLOR " FAILED");
|
||||||
else
|
} else {
|
||||||
{
|
|
||||||
fprintf(stdout, DQN_TESTER_GOOD_COLOR " OK");
|
fprintf(stdout, DQN_TESTER_GOOD_COLOR " OK");
|
||||||
test->num_tests_ok_in_group++;
|
test->num_tests_ok_in_group++;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stdout, DQN_TESTER_COLOR_RESET "\n");
|
fprintf(stdout, DQN_TESTER_COLOR_RESET "\n");
|
||||||
|
if (test->log_count != 0) {
|
||||||
if (test->log_count != 0)
|
|
||||||
putc('\n', stdout);
|
putc('\n', stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
test->state = Dqn_TesterState_Nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
Dqn_TesterBeginScopedTest::Dqn_TesterBeginScopedTest(Dqn_Tester *test, char const *fmt, ...)
|
||||||
|
: test(test)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_TesterBeginV(test, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dqn_TesterBeginScopedTest::~Dqn_TesterBeginScopedTest() {
|
||||||
|
Dqn_TesterEnd(test);
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
#endif // DQN_TESTER_IMPLEMENTATION
|
#endif // DQN_TESTER_IMPLEMENTATION
|
||||||
|
Loading…
Reference in New Issue
Block a user