SDL_test: allow disabling colorized output

This commit is contained in:
Anonymous Maarten
2025-12-03 03:24:01 +01:00
committed by Anonymous Maarten
parent 765a2e9118
commit 6665ebaa2e
9 changed files with 178 additions and 92 deletions

View File

@@ -4036,7 +4036,7 @@ sdl_compile_definitions(
##### Tests #####
if(SDL_TEST_LIBRARY)
file(GLOB TEST_SOURCES "${SDL3_SOURCE_DIR}/src/test/*.c")
file(GLOB TEST_SOURCES "${SDL3_SOURCE_DIR}/src/test/*.c" "${SDL3_SOURCE_DIR}/src/test/*.h")
target_sources(SDL3_test PRIVATE ${TEST_SOURCES})
if(APPLE)
set_target_properties(SDL3_test PROPERTIES

View File

@@ -195,6 +195,9 @@
<TreatWarningAsError>$(TreatWarningsAsError)</TreatWarningAsError>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\src\test\SDL_test_internal.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\test\SDL_test_assert.c" />
<ClCompile Include="..\..\src\test\SDL_test_common.c" />

View File

@@ -161,6 +161,9 @@
<TreatWarningAsError>$(TreatWarningsAsError)</TreatWarningAsError>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\src\test\SDL_test_internal.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\test\SDL_test_assert.c" />
<ClCompile Include="..\..\src\test\SDL_test_common.c" />

View File

@@ -42,6 +42,14 @@
extern "C" {
#endif
/**
* Prints given message with a timestamp in the TEST category and given priority.
*
* \param priority Priority of the message
* \param fmt Message to be logged
*/
void SDLCALL SDLTest_LogMessage(SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...);
/**
* Prints given message with a timestamp in the TEST category and INFO priority.
*

View File

@@ -25,28 +25,7 @@
*/
#include <SDL3/SDL_test.h>
/* Enable to have color in logs */
#if 1
#define COLOR_RED "\033[0;31m"
#define COLOR_GREEN "\033[0;32m"
#define COLOR_YELLOW "\033[0;93m"
#define COLOR_BLUE "\033[0;94m"
#define COLOR_END "\033[0m"
#else
#define COLOR_RED ""
#define COLOR_GREEN ""
#define COLOR_BLUE ""
#define COLOR_YELLOW ""
#define COLOR_END ""
#endif
/* Assert check message format */
#define SDLTEST_ASSERT_CHECK_FORMAT "Assert '%s': %s"
/* Assert summary message format */
#define SDLTEST_ASSERT_SUMMARY_FORMAT "Assert Summary: Total=%d " COLOR_GREEN "Passed=%d" COLOR_END " " COLOR_RED "Failed=%d" COLOR_END
#define SDLTEST_ASSERT_SUMMARY_FORMAT_OK "Assert Summary: Total=%d " COLOR_GREEN "Passed=%d" COLOR_END " " COLOR_GREEN "Failed=%d" COLOR_END
#include "SDL_test_internal.h"
/* ! counts the failed asserts */
static int SDLTest_AssertsFailed = 0;
@@ -54,6 +33,24 @@ static int SDLTest_AssertsFailed = 0;
/* ! counts the passed asserts */
static int SDLTest_AssertsPassed = 0;
static void SDLTest_LogAssertMessage(bool success, const char *assertion)
{
SDL_LogPriority priority;
const char *color;
const char *message;
if (success) {
priority = SDL_LOG_PRIORITY_INFO;
color = COLOR_GREEN;
message = "Passed";
} else {
priority = SDL_LOG_PRIORITY_ERROR;
color = COLOR_RED;
message = "Failed";
}
SDLTest_LogMessage(priority, "Assert '%s': %s%s%s", assertion, color, message, COLOR_END);
}
/*
* Assert that logs and break execution flow on failures (i.e. for harness errors).
*/
@@ -89,10 +86,10 @@ int SDLTest_AssertCheck(int assertCondition, SDL_PRINTF_FORMAT_STRING const char
/* Log pass or fail message */
if (assertCondition == ASSERT_FAIL) {
SDLTest_AssertsFailed++;
SDLTest_LogError(SDLTEST_ASSERT_CHECK_FORMAT, logMessage, COLOR_RED "Failed" COLOR_END);
SDLTest_LogAssertMessage(false, logMessage);
} else {
SDLTest_AssertsPassed++;
SDLTest_Log(SDLTEST_ASSERT_CHECK_FORMAT, logMessage, COLOR_GREEN "Passed" COLOR_END);
SDLTest_LogAssertMessage(true, logMessage);
}
return assertCondition;
@@ -114,7 +111,7 @@ void SDLTest_AssertPass(SDL_PRINTF_FORMAT_STRING const char *assertDescription,
/* Log pass message */
SDLTest_AssertsPassed++;
SDLTest_Log(SDLTEST_ASSERT_CHECK_FORMAT, logMessage, COLOR_GREEN "Passed" COLOR_END);
SDLTest_LogAssertMessage(true, logMessage);
}
/*
@@ -133,11 +130,12 @@ void SDLTest_ResetAssertSummary(void)
void SDLTest_LogAssertSummary(void)
{
int totalAsserts = SDLTest_AssertsPassed + SDLTest_AssertsFailed;
if (SDLTest_AssertsFailed == 0) {
SDLTest_Log(SDLTEST_ASSERT_SUMMARY_FORMAT_OK, totalAsserts, SDLTest_AssertsPassed, SDLTest_AssertsFailed);
} else {
SDLTest_LogError(SDLTEST_ASSERT_SUMMARY_FORMAT, totalAsserts, SDLTest_AssertsPassed, SDLTest_AssertsFailed);
}
bool success = SDLTest_AssertsFailed == 0;
SDLTest_LogMessage(success ? SDL_LOG_PRIORITY_INFO : SDL_LOG_PRIORITY_ERROR,
"Assert Summary: Total=%d " "%s" "Passed=%d" "%s" " " "%s" "Failed=%d" "%s",
totalAsserts, COLOR_GREEN, SDLTest_AssertsPassed, COLOR_END,
success ? COLOR_GREEN : COLOR_RED, SDLTest_AssertsFailed, COLOR_END);
}
/*

View File

@@ -21,15 +21,28 @@
/* Ported from original test/common.c file. */
#include <SDL3/SDL_test.h>
#include "SDL_test_internal.h"
#define SDL_MAIN_NOIMPL
#define SDL_MAIN_USE_CALLBACKS
#include <SDL3/SDL_main.h>
bool SDLTest_Color = true;
static bool get_environment_bool_variable(const char *name)
{
const char *var_string = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), name);
if (!var_string || var_string[0] == '\0') {
return false;
}
return true;
}
static const char *common_usage[] = {
"[-h | --help]",
"[--trackmem]",
"[--randmem]",
"[--no-color]",
"[--info all|video|modes|render|event|event_motion]",
"[--log all|error|system|audio|video|render|input]",
NULL
@@ -142,6 +155,10 @@ static int SDLCALL SDLTest_CommonStateParseCommonArguments(void *data, char **ar
/* Already handled in SDLTest_CommonCreateState() */
return 1;
}
if (SDL_strcasecmp(argv[index], "--no-color") == 0) {
SDLTest_Color = false;
return 1;
}
if (SDL_strcasecmp(argv[index], "--randmem") == 0) {
/* Already handled in SDLTest_CommonCreateState() */
return 1;
@@ -686,6 +703,8 @@ SDLTest_CommonState *SDLTest_CommonCreateState(char **argv, SDL_InitFlags flags)
int i;
SDLTest_CommonState *state;
SDLTest_Color = !get_environment_bool_variable("NO_COLOR");
/* Do this first so we catch all allocations */
for (i = 1; argv[i]; ++i) {
if (SDL_strcasecmp(argv[i], "--trackmem") == 0) {

View File

@@ -19,33 +19,25 @@
3. This notice may not be removed or altered from any source distribution.
*/
#include <SDL3/SDL_test.h>
#include "SDL_test_internal.h"
#include <stdlib.h> /* Needed for exit() */
/* Enable to have color in logs */
#if 1
#define COLOR_RED "\033[0;31m"
#define COLOR_GREEN "\033[0;32m"
#define COLOR_YELLOW "\033[0;93m"
#define COLOR_BLUE "\033[0;94m"
#define COLOR_END "\033[0m"
#else
#define COLOR_RED ""
#define COLOR_GREEN ""
#define COLOR_BLUE ""
#define COLOR_YELLOW ""
#define COLOR_END ""
#endif
/* Invalid test name/description message format */
#define SDLTEST_INVALID_NAME_FORMAT "(Invalid)"
/* Log summary message format */
#define SDLTEST_LOG_SUMMARY_FORMAT "%s Summary: Total=%d " COLOR_GREEN "Passed=%d" COLOR_END " " COLOR_RED "Failed=%d" COLOR_END " " COLOR_BLUE "Skipped=%d" COLOR_END
#define SDLTEST_LOG_SUMMARY_FORMAT_OK "%s Summary: Total=%d " COLOR_GREEN "Passed=%d" COLOR_END " " COLOR_GREEN "Failed=%d" COLOR_END " " COLOR_BLUE "Skipped=%d" COLOR_END
static void SDLTest_LogSummary(bool success, const char *name, int total, int passed, int failed, int skipped)
{
SDLTest_LogMessage(success ? SDL_LOG_PRIORITY_INFO : SDL_LOG_PRIORITY_ERROR,
"%s Summary: Total=%d " "%s" "Passed=%d" "%s" " " "%s" "Failed=%d" "%s" " " "%s" "Skipped=%d" "%s",
name, total, COLOR_GREEN, passed, COLOR_END, success ? COLOR_GREEN : COLOR_RED, failed, COLOR_END, COLOR_BLUE, skipped, COLOR_END);
}
/* Final result message format */
#define SDLTEST_FINAL_RESULT_FORMAT COLOR_YELLOW ">>> %s '%s':" COLOR_END " %s\n"
static void SDLTest_LogFinalResult(bool success, const char *stage, const char *name, const char *color_message, const char *message)
{
SDL_LogPriority priority = success ? SDL_LOG_PRIORITY_INFO : SDL_LOG_PRIORITY_ERROR;
SDLTest_LogMessage(priority, "%s>>> %s '%s':" "%s" " " "%s" "%s" "%s", COLOR_YELLOW, stage, name, COLOR_END, color_message ? color_message : "", message, color_message ? COLOR_END : "");
}
struct SDLTest_TestSuiteRunner {
struct
@@ -242,7 +234,7 @@ static int SDLTest_RunTest(SDLTest_TestSuiteReference *testSuite, const SDLTest_
}
if (!testCase->enabled && forceTestRun == false) {
SDLTest_Log(SDLTEST_FINAL_RESULT_FORMAT, "Test", testCase->name, "Skipped (Disabled)");
SDLTest_LogFinalResult(true, "Test", testCase->name, NULL, "Skipped (Disabled)");
return TEST_RESULT_SKIPPED;
}
@@ -259,7 +251,7 @@ static int SDLTest_RunTest(SDLTest_TestSuiteReference *testSuite, const SDLTest_
if (testSuite->testSetUp) {
testSuite->testSetUp(&data);
if (SDLTest_AssertSummaryToTestResult() == TEST_RESULT_FAILED) {
SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Suite Setup", testSuite->name, COLOR_RED "Failed" COLOR_END);
SDLTest_LogFinalResult(false, "Suite Setup", testSuite->name, COLOR_RED, "Failed");
return TEST_RESULT_SETUP_FAILURE;
}
}
@@ -301,13 +293,13 @@ static int SDLTest_RunTest(SDLTest_TestSuiteReference *testSuite, const SDLTest_
/* Final log based on test execution result */
if (testCaseResult == TEST_SKIPPED) {
/* Test was programmatically skipped */
SDLTest_Log(SDLTEST_FINAL_RESULT_FORMAT, "Test", testCase->name, COLOR_BLUE "Skipped (Programmatically)" COLOR_END);
SDLTest_LogFinalResult(true, "Test", testCase->name, COLOR_BLUE, "Skipped (Programmatically)");
} else if (testCaseResult == TEST_STARTED) {
/* Test did not return a TEST_COMPLETED value; assume it failed */
SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Test", testCase->name, COLOR_RED "Failed (test started, but did not return TEST_COMPLETED)" COLOR_END);
SDLTest_LogFinalResult(false, "Test", testCase->name, COLOR_RED, "Skipped (test started, but did not return TEST_COMPLETED)");
} else if (testCaseResult == TEST_ABORTED) {
/* Test was aborted early; assume it failed */
SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Test", testCase->name, COLOR_RED "Failed (Aborted)" COLOR_END);
SDLTest_LogFinalResult(false, "Test", testCase->name, COLOR_RED, "Failed (Aborted)");
} else {
SDLTest_LogAssertSummary();
}
@@ -572,9 +564,11 @@ int SDLTest_ExecuteTestSuiteRunner(SDLTest_TestSuiteRunner *runner)
if (suiteFilter == 1 && suiteFilterName && testSuite->name &&
SDL_strcasecmp(suiteFilterName, testSuite->name) != 0) {
/* Skip suite */
SDLTest_Log("===== Test Suite %i: '%s' " COLOR_BLUE "skipped" COLOR_END "\n",
SDLTest_Log("===== Test Suite %i: '%s' " "%s" "skipped" "%s" "\n",
suiteCounter,
currentSuiteName);
currentSuiteName,
COLOR_BLUE,
COLOR_END);
} else {
int nbTestCases = 0;
@@ -634,10 +628,12 @@ int SDLTest_ExecuteTestSuiteRunner(SDLTest_TestSuiteRunner *runner)
if (testFilter == 1 && testFilterName && testCase->name &&
SDL_strcasecmp(testFilterName, testCase->name) != 0) {
/* Skip test */
SDLTest_Log("===== Test Case %i.%i: '%s' " COLOR_BLUE "skipped" COLOR_END "\n",
SDLTest_Log("===== Test Case %i.%i: '%s' " "%s" "skipped" "%s" "\n",
suiteCounter,
testCounter,
currentTestName);
currentTestName,
COLOR_BLUE,
COLOR_END);
} else {
/* Override 'disabled' flag if we specified a test filter (i.e. force run for debugging) */
if (testFilter == 1 && !testCase->enabled) {
@@ -649,10 +645,12 @@ int SDLTest_ExecuteTestSuiteRunner(SDLTest_TestSuiteRunner *runner)
testStartSeconds = GetClock();
/* Log test started */
SDLTest_Log(COLOR_YELLOW "----- Test Case %i.%i: '%s' started" COLOR_END,
SDLTest_Log("%s" "----- Test Case %i.%i: '%s' started" "%s",
COLOR_YELLOW,
suiteCounter,
testCounter,
currentTestName);
currentTestName,
COLOR_END);
if (testCase->description && testCase->description[0] != '\0') {
SDLTest_Log("Test Description: '%s'",
(testCase->description) ? testCase->description : SDLTEST_INVALID_NAME_FORMAT);
@@ -703,13 +701,13 @@ int SDLTest_ExecuteTestSuiteRunner(SDLTest_TestSuiteRunner *runner)
/* Log final test result */
switch (testResult) {
case TEST_RESULT_PASSED:
SDLTest_Log(SDLTEST_FINAL_RESULT_FORMAT, "Test", currentTestName, COLOR_GREEN "Passed" COLOR_END);
SDLTest_LogFinalResult(true, "Test", currentTestName, COLOR_GREEN, "Passed");
break;
case TEST_RESULT_FAILED:
SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Test", currentTestName, COLOR_RED "Failed" COLOR_END);
SDLTest_LogFinalResult(false, "Test", currentTestName, COLOR_RED, "Failed");
break;
case TEST_RESULT_NO_ASSERT:
SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Test", currentTestName, COLOR_BLUE "No Asserts" COLOR_END);
SDLTest_LogFinalResult(false, "Test", currentTestName, COLOR_BLUE, "No Asserts");
break;
}
@@ -734,11 +732,11 @@ int SDLTest_ExecuteTestSuiteRunner(SDLTest_TestSuiteRunner *runner)
/* Log summary and final Suite result */
countSum = testPassedCount + testFailedCount + testSkippedCount;
if (testFailedCount == 0) {
SDLTest_Log(SDLTEST_LOG_SUMMARY_FORMAT_OK, "Suite", countSum, testPassedCount, testFailedCount, testSkippedCount);
SDLTest_Log(SDLTEST_FINAL_RESULT_FORMAT, "Suite", currentSuiteName, COLOR_GREEN "Passed" COLOR_END);
SDLTest_LogSummary(true, "Suite", countSum, testPassedCount, testFailedCount, testSkippedCount);
SDLTest_LogFinalResult(true, "Suite", currentSuiteName, COLOR_GREEN, "Passed");
} else {
SDLTest_LogError(SDLTEST_LOG_SUMMARY_FORMAT, "Suite", countSum, testPassedCount, testFailedCount, testSkippedCount);
SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Suite", currentSuiteName, COLOR_RED "Failed" COLOR_END);
SDLTest_LogSummary(false, "Suite", countSum, testPassedCount, testFailedCount, testSkippedCount);
SDLTest_LogFinalResult(false, "Suite", currentSuiteName, COLOR_RED, "Failed");
}
SDL_free(arrayTestCases);
@@ -761,19 +759,19 @@ int SDLTest_ExecuteTestSuiteRunner(SDLTest_TestSuiteRunner *runner)
countSum = totalTestPassedCount + totalTestFailedCount + totalTestSkippedCount;
if (totalTestFailedCount == 0) {
runResult = 0;
SDLTest_Log(SDLTEST_LOG_SUMMARY_FORMAT_OK, "Run", countSum, totalTestPassedCount, totalTestFailedCount, totalTestSkippedCount);
SDLTest_Log(SDLTEST_FINAL_RESULT_FORMAT, "Run /w seed", runSeed, COLOR_GREEN "Passed" COLOR_END);
SDLTest_LogSummary(true, "Run", countSum, totalTestPassedCount, totalTestFailedCount, totalTestSkippedCount);
SDLTest_LogFinalResult(true, "Run /w seed", runSeed, COLOR_GREEN, "Passed");
} else {
runResult = 1;
SDLTest_LogError(SDLTEST_LOG_SUMMARY_FORMAT, "Run", countSum, totalTestPassedCount, totalTestFailedCount, totalTestSkippedCount);
SDLTest_LogError(SDLTEST_FINAL_RESULT_FORMAT, "Run /w seed", runSeed, COLOR_RED "Failed" COLOR_END);
SDLTest_LogSummary(false, "Run", countSum, totalTestPassedCount, totalTestFailedCount, totalTestSkippedCount);
SDLTest_LogFinalResult(false, "Run /w seed", runSeed, COLOR_RED, "Failed");
}
/* Print repro steps for failed tests */
if (failedNumberOfTests > 0) {
SDLTest_Log("Harness input to repro failures:");
for (testCounter = 0; testCounter < failedNumberOfTests; testCounter++) {
SDLTest_Log(COLOR_RED " --seed %s --filter %s" COLOR_END, runSeed, failedTests[testCounter]->name);
SDLTest_Log("%s" " --seed %s --filter %s" "%s", COLOR_RED, runSeed, failedTests[testCounter]->name, COLOR_END);
}
}
SDL_free((void *)failedTests);

View File

@@ -0,0 +1,38 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_test_internal_h
#define SDL_test_internal_h
extern bool SDLTest_Color;
#define COLOR_RAW_RED "\033[0;31m"
#define COLOR_RAW_GREEN "\033[0;32m"
#define COLOR_RAW_YELLOW "\033[0;93m"
#define COLOR_RAW_BLUE "\033[0;94m"
#define COLOR_RAW_END "\033[0m"
#define COLOR_RED (SDLTest_Color ? COLOR_RAW_RED : "")
#define COLOR_GREEN (SDLTest_Color ? COLOR_RAW_GREEN : "")
#define COLOR_YELLOW (SDLTest_Color ? COLOR_RAW_YELLOW : "")
#define COLOR_BLUE (SDLTest_Color ? COLOR_RAW_BLUE : "")
#define COLOR_END (SDLTest_Color ? COLOR_RAW_END : "")
#endif // SDL_test_internal_h

View File

@@ -73,21 +73,46 @@ static const char *SDLTest_TimestampToString(const time_t timestamp)
}
/*
* Prints given message with a timestamp in the TEST category and INFO priority.
* Prints given message with a timestamp in the TEST category and given priority.
*/
void SDLTest_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
static void SDLTest_LogMessageV(SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap)
{
va_list list;
char logMessage[SDLTEST_MAX_LOGMESSAGE_LENGTH];
/* Print log message into a buffer */
SDL_memset(logMessage, 0, SDLTEST_MAX_LOGMESSAGE_LENGTH);
va_start(list, fmt);
(void)SDL_vsnprintf(logMessage, SDLTEST_MAX_LOGMESSAGE_LENGTH - 1, fmt, list);
va_end(list);
(void)SDL_vsnprintf(logMessage, SDLTEST_MAX_LOGMESSAGE_LENGTH - 1, fmt, ap);
/* Log with timestamp and newline */
SDL_LogMessage(SDL_LOG_CATEGORY_TEST, SDL_LOG_PRIORITY_INFO, " %s: %s", SDLTest_TimestampToString(time(NULL)), logMessage);
/* Log with timestamp and newline. Messages with lower priority are slightly indented. */
if (priority > SDL_LOG_PRIORITY_INFO) {
SDL_LogMessage(SDL_LOG_CATEGORY_TEST, priority, "%s: %s", SDLTest_TimestampToString(time(NULL)), logMessage);
} else {
SDL_LogMessage(SDL_LOG_CATEGORY_TEST, priority, " %s: %s", SDLTest_TimestampToString(time(NULL)), logMessage);
}
}
/*
* Prints given message with a timestamp in the TEST category and given priority.
*/
void SDLTest_LogMessage(SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
SDLTest_LogMessageV(priority, fmt, ap);
va_end(ap);
}
/*
* Prints given message with a timestamp in the TEST category and INFO priority.
*/
void SDLTest_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
SDLTest_LogMessageV(SDL_LOG_PRIORITY_INFO, fmt, ap);
va_end(ap);
}
/*
@@ -95,17 +120,11 @@ void SDLTest_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
*/
void SDLTest_LogError(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
{
va_list list;
char logMessage[SDLTEST_MAX_LOGMESSAGE_LENGTH];
va_list ap;
/* Print log message into a buffer */
SDL_memset(logMessage, 0, SDLTEST_MAX_LOGMESSAGE_LENGTH);
va_start(list, fmt);
(void)SDL_vsnprintf(logMessage, SDLTEST_MAX_LOGMESSAGE_LENGTH - 1, fmt, list);
va_end(list);
/* Log with timestamp and newline */
SDL_LogMessage(SDL_LOG_CATEGORY_TEST, SDL_LOG_PRIORITY_ERROR, "%s: %s", SDLTest_TimestampToString(time(NULL)), logMessage);
va_start(ap, fmt);
SDLTest_LogMessageV(SDL_LOG_PRIORITY_ERROR, fmt, ap);
va_end(ap);
}
static char nibble_to_char(Uint8 nibble)