1
0
pl-libs/pl_test.h
Jonathan Hoffstadt 5d464bc922
Some checks failed
Tests / Ubuntu (push) Failing after 6s
feat: pl_memory.h 1.0
2024-09-27 19:37:14 -05:00

541 lines
17 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
pl_test.h
Do this:
#define PL_TEST_IMPLEMENTATION
before you include this file in *one* C or C++ file to create the implementation.
// i.e. it should look like this:
#include ...
#include ...
#include ...
#define PL_TEST_IMPLEMENTATION
#include "pl_test.h"
Notes:
* for console color output on windows, define "PL_TEST_WIN32_COLOR" before
including the implementation
*/
// library version (format XYYZZ)
#define PL_TEST_VERSION "1.0.0"
#define PL_TEST_VERSION_NUM 10000
/*
Index of this file:
// [SECTION] header mess
// [SECTION] includes
// [SECTION] public api
// [SECTION] structs
// [SECTION] private
// [SECTION] c file
*/
//-----------------------------------------------------------------------------
// [SECTION] header mess
//-----------------------------------------------------------------------------
#ifndef PL_TEST_H
#define PL_TEST_H
//-----------------------------------------------------------------------------
// [SECTION] includes
//-----------------------------------------------------------------------------
#include <stdbool.h> // bool
#include <stdint.h> // uint32_t
//-----------------------------------------------------------------------------
// [SECTION] forward declarations & basic types
//-----------------------------------------------------------------------------
// forward declarations
typedef struct _plTestContext plTestContext;
typedef struct _plTestOptions plTestOptions;
typedef void (*PL_TEST_FUNCTION)(void*);
//-----------------------------------------------------------------------------
// [SECTION] macros
//-----------------------------------------------------------------------------
#define pl_test_register_test(TEST, DATA) pl__test_register_test((TEST), (DATA), #TEST)
//-----------------------------------------------------------------------------
// [SECTION] public api
//-----------------------------------------------------------------------------
plTestContext* pl_create_test_context(plTestOptions);
// tests
void pl_test_run_suite(const char* pcSuiteName);
bool pl_test_finish(void);
// booleans
bool pl_test_expect_true (bool bValue, const char* pcMsg);
bool pl_test_expect_false(bool bValue, const char* pcMsg);
// integers
bool pl_test_expect_int_equal (int iValue0, int iValue1, const char* pcMsg);
bool pl_test_expect_int_not_equal (int iValue0, int iValue1, const char* pcMsg);
bool pl_test_expect_uint32_equal (uint32_t uValue0, uint32_t uValue1, const char* pcMsg);
bool pl_test_expect_uint32_not_equal(uint32_t uValue0, uint32_t uValue1, const char* pcMsg);
bool pl_test_expect_uint64_equal (uint64_t uValue0, uint64_t uValue1, const char* pcMsg);
bool pl_test_expect_uint64_not_equal(uint64_t uValue0, uint64_t uValue1, const char* pcMsg);
// floating point
bool pl_test_expect_float_near_equal (float fValue0, float fValue1, float fError, const char* pcMsg);
bool pl_test_expect_float_near_not_equal (float fValue0, float fValue1, float fError, const char* pcMsg);
bool pl_test_expect_double_near_equal (double dValue0, double dValue1, double dError, const char* pcMsg);
bool pl_test_expect_double_near_not_equal(double dValue0, double dValue1, double dError, const char* pcMsg);
// strings
bool pl_test_expect_string_equal (const char* pcValue0, const char* pcValue1, const char* pcMsg);
bool pl_test_expect_string_not_equal(const char* pcValue0, const char* pcValue1, const char* pcMsg);
//-----------------------------------------------------------------------------
// [SECTION] structs
//-----------------------------------------------------------------------------
typedef struct _plTestOptions
{
bool bPrintSuiteResults;
bool bPrintAllPassedChecks;
bool bPrintColor;
} plTestOptions;
//-----------------------------------------------------------------------------
// [SECTION] private
//-----------------------------------------------------------------------------
void pl__test_register_test(PL_TEST_FUNCTION tTest, void* pData, const char* pcName);
#endif // PL_TEST_H
//-----------------------------------------------------------------------------
// [SECTION] c file
//-----------------------------------------------------------------------------
/*
Index of this file:
// [SECTION] header mess
// [SECTION] includes
// [SECTION] public api implementation
*/
//-----------------------------------------------------------------------------
// [SECTION] header mess
//-----------------------------------------------------------------------------
#ifdef PL_TEST_IMPLEMENTATION
#if defined(PL_TEST_WIN32_COLOR) && defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
static DWORD gtOriginalMode = 0;
static HANDLE gtStdOutHandle = 0;
static bool gbActiveColor = 0;
#endif
//-----------------------------------------------------------------------------
// [SECTION] includes
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
//-----------------------------------------------------------------------------
// [SECTION] internal api
//-----------------------------------------------------------------------------
static void pl__test_print_va (const char* cPFormat, va_list args);
void static pl__test_print_red (const char* cPFormat, const char* pcMsg, ...);
void static pl__test_print_green(const char* cPFormat, const char* pcMsg, ...);
//-----------------------------------------------------------------------------
// [SECTION] internal structs
//-----------------------------------------------------------------------------
typedef struct _plTest
{
bool bFailureOccured;
const char* pcName;
PL_TEST_FUNCTION tTest;
void* pData;
} plTest;
typedef struct _plTestContext
{
plTest* atTests;
plTest* ptCurrentTest;
uint32_t uTestSize;
uint32_t uTestCapacity;
uint32_t uFailedTest;
plTestOptions tOptions;
uint32_t uTotalPassedTests;
uint32_t uTotalFailedTests;
} plTestContext;
//-----------------------------------------------------------------------------
// [SECTION] global context
//-----------------------------------------------------------------------------
plTestContext* gptTestContext = NULL;
//-----------------------------------------------------------------------------
// [SECTION] public api implementation
//-----------------------------------------------------------------------------
plTestContext*
pl_create_test_context(plTestOptions tOptions)
{
#if defined(PL_TEST_WIN32_COLOR) && defined(_WIN32)
DWORD tCurrentMode = 0;
gbActiveColor = true;
gtStdOutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
if(gtStdOutHandle == INVALID_HANDLE_VALUE)
gbActiveColor = false;
else if(!GetConsoleMode(gtStdOutHandle, &tCurrentMode))
gbActiveColor = false;
gtOriginalMode = tCurrentMode;
tCurrentMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; // enable ANSI escape codes
if(!SetConsoleMode(gtStdOutHandle, tCurrentMode))
gbActiveColor = false;
if(!gbActiveColor)
tOptions.bPrintColor = false;
#endif
gptTestContext = (plTestContext*)malloc(sizeof(plTestContext));
memset(gptTestContext, 0, sizeof(plTestContext));
gptTestContext->uTestCapacity = 64;
gptTestContext->tOptions = tOptions;
gptTestContext->atTests = (plTest*)malloc(gptTestContext->uTestCapacity * sizeof(plTest));
memset(gptTestContext->atTests, 0, sizeof(gptTestContext->uTestCapacity * sizeof(plTest)));
return gptTestContext;
}
void
pl__test_register_test(PL_TEST_FUNCTION tTest, void* pData, const char* pcName)
{
gptTestContext->uTestSize++;
if(gptTestContext->uTestSize == gptTestContext->uTestCapacity)
{
plTest* atOldTests = gptTestContext->atTests;
gptTestContext->atTests = (plTest*)malloc(gptTestContext->uTestCapacity * sizeof(plTest) * 2);
memset(gptTestContext->atTests, 0, sizeof(gptTestContext->uTestCapacity * sizeof(plTest) * 2));
memcpy(gptTestContext->atTests, atOldTests, gptTestContext->uTestCapacity * sizeof(plTest));
free(atOldTests);
gptTestContext->uTestCapacity *= 2;
}
gptTestContext->atTests[gptTestContext->uTestSize - 1].pcName = pcName;
gptTestContext->atTests[gptTestContext->uTestSize - 1].tTest = tTest;
gptTestContext->atTests[gptTestContext->uTestSize - 1].bFailureOccured = false;
gptTestContext->atTests[gptTestContext->uTestSize - 1].pData = pData;
}
void
pl_test_run_suite(const char* pcSuiteName)
{
printf("\n------%s suite------\n\n", pcSuiteName);
for(uint32_t i = 0; i < gptTestContext->uTestSize; i++)
{
gptTestContext->ptCurrentTest = &gptTestContext->atTests[i];
gptTestContext->ptCurrentTest->bFailureOccured = false;
printf("running -> \"%s\"\n", gptTestContext->ptCurrentTest->pcName);
gptTestContext->ptCurrentTest->tTest(gptTestContext->ptCurrentTest->pData);
if(gptTestContext->ptCurrentTest->bFailureOccured)
{
pl__test_print_red("%s", NULL, " -> failed");
gptTestContext->uFailedTest++;
}
else
pl__test_print_green("%s", NULL, " passed");
}
if(gptTestContext->tOptions.bPrintSuiteResults)
{
printf("\nPassed: ");
pl__test_print_green("%u", NULL, gptTestContext->uTestSize - gptTestContext->uFailedTest);
printf("Failed: ");
pl__test_print_red("%u", NULL, gptTestContext->uFailedTest);
}
// printf("\n------End tests------\n\n");
// reset context
gptTestContext->uTotalPassedTests += gptTestContext->uTestSize - gptTestContext->uFailedTest;
gptTestContext->uTotalFailedTests += gptTestContext->uFailedTest;
gptTestContext->uTestSize = 0;
gptTestContext->uFailedTest = 0;
gptTestContext->ptCurrentTest = NULL;
memset(gptTestContext->atTests, 0, sizeof(plTest) * gptTestContext->uTestCapacity);
}
bool
pl_test_finish(void)
{
printf("\n------Results------\n");
printf("\nTests passed: ");
pl__test_print_green("%u", NULL, gptTestContext->uTotalPassedTests);
printf("Tests failed: ");
pl__test_print_red("%u", NULL, gptTestContext->uTotalFailedTests);
#if defined(PL_TEST_WIN32_COLOR) && defined(_WIN32)
if(gbActiveColor)
{
if(!SetConsoleMode(gtStdOutHandle, gtOriginalMode))
exit(GetLastError());
}
#endif
return gptTestContext->uTotalFailedTests == 0;
}
bool
pl_test_expect_true(bool bValue, const char* pcMsg)
{
if(bValue)
{
if(gptTestContext->tOptions.bPrintAllPassedChecks)
pl__test_print_green("Value: true | Expected Value: true", pcMsg);
return true;
}
pl__test_print_red("Value: false | Expected Value: true", pcMsg);
gptTestContext->ptCurrentTest->bFailureOccured = true;
return false;
}
bool
pl_test_expect_false(bool bValue, const char* pcMsg)
{
if(bValue)
{
pl__test_print_red("Value: true | Expected Value: false", pcMsg);
gptTestContext->ptCurrentTest->bFailureOccured = true;
return false;
}
if(gptTestContext->tOptions.bPrintAllPassedChecks)
pl__test_print_green("Value: false | Expected Value: false", pcMsg);
return true;
}
#define pl__test_expect_equal(value0, value1, pcMsg, format) \
if((value0) == (value1)) \
{ \
if(gptTestContext->tOptions.bPrintAllPassedChecks) \
pl__test_print_green(format " equals " format " | Equality Expected", (pcMsg), (value0), (value1)); \
return true; \
} \
pl__test_print_red(format " does not equal " format " | Equality Expected", (pcMsg), (value0), (value1)); \
gptTestContext->ptCurrentTest->bFailureOccured = true; \
return false;
#define pl__test_expect_not_equal(value0, value1, pcMsg, format) \
if((value0) == (value1)) \
{ \
pl__test_print_red(format " equals " format " | Equality Not Expected", (pcMsg), (value0), (value1)); \
gptTestContext->ptCurrentTest->bFailureOccured = true; \
return false; \
} \
if(gptTestContext->tOptions.bPrintAllPassedChecks) \
pl__test_print_green(format " does not equal " format " | Equality Expected", (pcMsg), (value0), (value1)); \
return true;
bool
pl_test_expect_int_equal(int iValue0, int iValue1, const char* pcMsg)
{
pl__test_expect_equal(iValue0, iValue1, pcMsg, "%i");
}
bool
pl_test_expect_int_not_equal(int iValue0, int iValue1, const char* pcMsg)
{
pl__test_expect_not_equal(iValue0, iValue1, pcMsg, "%i");
}
bool
pl_test_expect_uint64_equal(uint64_t uValue0, uint64_t uValue1, const char* pcMsg)
{
pl__test_expect_equal(uValue0, uValue1, pcMsg, "%llu");
}
bool
pl_test_expect_uint64_not_equal(uint64_t uValue0, uint64_t uValue1, const char* pcMsg)
{
pl__test_expect_not_equal(uValue0, uValue1, pcMsg, "%llu");
}
bool
pl_test_expect_uint32_equal(uint32_t uValue0, uint32_t uValue1, const char* pcMsg)
{
pl__test_expect_equal(uValue0, uValue1, pcMsg, "%u");
}
bool
pl_test_expect_uint32_not_equal(uint32_t uValue0, uint32_t uValue1, const char* pcMsg)
{
pl__test_expect_not_equal(uValue0, uValue1, pcMsg, "%u");
}
bool
pl_test_expect_float_near_equal(float fValue0, float fValue1, float fError, const char* pcMsg)
{
return pl_test_expect_double_near_equal((double)fValue0, (double)fValue1, (double)fError, pcMsg);
}
bool
pl_test_expect_float_near_not_equal(float fValue0, float fValue1, float fError, const char* pcMsg)
{
return pl_test_expect_double_near_not_equal((double)fValue0, (double)fValue1, (double)fError, pcMsg);
}
bool
pl_test_expect_double_near_equal(double dValue0, double dValue1, double dError, const char* pcMsg)
{
if(dValue0 >= dValue1 - dError && dValue0 <= dValue1 + dError)
{
if(gptTestContext->tOptions.bPrintAllPassedChecks)
pl__test_print_green("%0.6f equals %0.6f | Equality Expected within %0.6f", pcMsg, dValue0, dValue1, dError);
return true;
}
pl__test_print_red("%0.6f does not equal %0.6f | Equality Expected within %0.6f", pcMsg, dValue0, dValue1, dError);
gptTestContext->ptCurrentTest->bFailureOccured = true;
return false;
}
bool
pl_test_expect_double_near_not_equal(double dValue0, double dValue1, double dError, const char* pcMsg)
{
if(dValue0 >= dValue1 - dError && dValue0 <= dValue1 + dError)
{
pl__test_print_red("%0.f equals %0.6f | Equality Not Expected within %0.6f", pcMsg, dValue0, dValue1, dError);
gptTestContext->ptCurrentTest->bFailureOccured = true;
return false;
}
if(gptTestContext->tOptions.bPrintAllPassedChecks)
pl__test_print_green("%0.6f does not equal %0.6f | Equality Not Expected within %0.6f", pcMsg, dValue0, dValue1, dError);
return true;
}
bool
pl_test_expect_string_equal(const char* pcValue0, const char* pcValue1, const char* pcMsg)
{
if(strcmp(pcValue0, pcValue1) == 0)
{
if(gptTestContext->tOptions.bPrintAllPassedChecks)
pl__test_print_green("\"%s\" equals \"%s\" | Equality Expected", pcMsg, pcValue0, pcValue1);
return true;
}
pl__test_print_red("\"%s\" does not equal \"%s\" | Equality Expected", pcMsg, pcValue0, pcValue1);
gptTestContext->ptCurrentTest->bFailureOccured = true;
return false;
}
bool
pl_test_expect_string_not_equal(const char* pcValue0, const char* pcValue1, const char* pcMsg)
{
if(strcmp(pcValue0, pcValue1) == 0)
{
if(gptTestContext->tOptions.bPrintAllPassedChecks)
pl__test_print_green("\"%s\" equals \"%s\" | Equality Not Expected", pcMsg, pcValue0, pcValue1);
gptTestContext->ptCurrentTest->bFailureOccured = true;
return false;
}
pl__test_print_red("\"%s\" does not equal \"%s\" | Equality Not Expected", pcMsg, pcValue0, pcValue1);
return true;
}
//-----------------------------------------------------------------------------
// [SECTION] internal api implementation
//-----------------------------------------------------------------------------
static void
pl__test_print_va(const char* cPFormat, va_list args)
{
char dest[1024];
vsnprintf(dest, 1024, cPFormat, args);
printf("%s", dest);
}
void static
pl__test_print_red(const char* cPFormat, const char* pcMsg, ...)
{
if(gptTestContext->tOptions.bPrintColor)
{
#ifdef _WIN32
printf("");
#else
printf("\033[91m");
#endif
}
va_list argptr;
va_start(argptr, pcMsg);
pl__test_print_va(cPFormat, argptr);
va_end(argptr);
if(pcMsg)
printf(" %s\n", pcMsg);
else
printf("\n");
if(gptTestContext->tOptions.bPrintColor)
{
#ifdef _WIN32
printf("");
#else
printf("\033[0m");
#endif
}
}
static void
pl__test_print_green(const char* cPFormat, const char* pcMsg, ...)
{
if(gptTestContext->tOptions.bPrintColor)
{
#ifdef _WIN32
printf("");
#else
printf("\033[92m");
#endif
}
va_list argptr;
va_start(argptr, pcMsg);
pl__test_print_va(cPFormat, argptr);
va_end(argptr);
if(pcMsg)
printf(" %s\n", pcMsg);
else
printf("\n");
if(gptTestContext->tOptions.bPrintColor)
{
#ifdef _WIN32
printf("");
#else
printf("\033[0m");
#endif
}
}
#endif // PL_TEST_IMPLEMENTATION