1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-07-01 00:25:06 +02:00
Files
pgbackrest/test/src/module/common/errorTest.c
David Steele 4e8d469f4d Use configure to generate Makefile variables for unit tests.
The unit test Makefile generation was a hodge-podge of constants and rules based on distros/versions that easily got out of date and did not work on an unknown system. All of this dates from the mixed Perl/C unit test implementation.

Instead use configure to generate most of the important Makefile variables, which allows the unit tests to run on multiple platforms, e.g. MacOS and FreeBSD.

There is plenty of work to be done here and not all the unit tests work on MacOS and FreeBSD for various reasons.

As a POC update the MacOS and FreeBSD tests on Cirrus-CI to run a few command unit tests.
2021-01-24 16:24:14 -05:00

364 lines
12 KiB
C

/***********************************************************************************************************************************
Test Error Handling
***********************************************************************************************************************************/
#include <assert.h>
#include "common/harnessFork.h"
/***********************************************************************************************************************************
Declare some error locally because real errors won't work for some tests -- they could also break as errors change
***********************************************************************************************************************************/
ERROR_DECLARE(TestParent1Error);
ERROR_DECLARE(TestParent2Error);
ERROR_DECLARE(TestChildError);
ERROR_DEFINE(101, TestParent1Error, TestParent1Error);
ERROR_DEFINE(102, TestParent2Error, TestParent1Error);
ERROR_DEFINE(200, TestChildError, TestParent2Error);
/***********************************************************************************************************************************
testTryRecurse - test to blow up try stack
***********************************************************************************************************************************/
volatile int testTryRecurseTotal = 0;
bool testTryRecurseCatch = false;
bool testTryRecurseFinally = false;
void
testTryRecurse(void)
{
TRY_BEGIN()
{
testTryRecurseTotal++;
assert(errorContext.tryTotal == testTryRecurseTotal + 1);
testTryRecurse();
}
CATCH(MemoryError)
{
testTryRecurseCatch = true; // {uncoverable - catch should never be executed}
}
FINALLY()
{
testTryRecurseFinally = true;
}
TRY_END();
} // {uncoverable - function throws error, never returns}
/***********************************************************************************************************************************
Test Run
***********************************************************************************************************************************/
void
testRun(void)
{
FUNCTION_HARNESS_VOID();
// *****************************************************************************************************************************
if (testBegin("check that try stack is initialized correctly"))
{
assert(errorContext.tryTotal == 0);
}
// *****************************************************************************************************************************
if (testBegin("errorTypeExtends"))
{
assert(errorTypeExtends(&TestParent1Error, &TestParent1Error));
assert(errorTypeExtends(&TestChildError, &TestParent1Error));
assert(errorTypeExtends(&TestChildError, &TestParent2Error));
assert(!errorTypeExtends(&TestChildError, &TestChildError));
}
// *****************************************************************************************************************************
if (testBegin("TRY with no errors"))
{
volatile bool tryDone = false;
bool catchDone = false;
bool finallyDone = false;
TRY_BEGIN()
{
assert(errorContext.tryTotal == 1);
tryDone = true;
}
CATCH_ANY()
{
catchDone = true; // {uncoverable - catch should never be executed}
}
FINALLY()
{
assert(errorContext.tryList[1].state == errorStateFinal);
finallyDone = true;
}
TRY_END();
assert(tryDone);
assert(!catchDone);
assert(finallyDone);
assert(errorContext.tryTotal == 0);
}
// *****************************************************************************************************************************
if (testBegin("TRY with multiple catches"))
{
volatile bool tryDone = false;
volatile bool catchDone = false;
volatile bool finallyDone = false;
assert(errorTryDepth() == 0);
TRY_BEGIN()
{
assert(errorTryDepth() == 1);
TRY_BEGIN()
{
assert(errorTryDepth() == 2);
TRY_BEGIN()
{
assert(errorTryDepth() == 3);
TRY_BEGIN()
{
assert(errorTryDepth() == 4);
tryDone = true;
char bigMessage[sizeof(messageBuffer) * 32];
memset(bigMessage, 'A', sizeof(bigMessage));
THROW(AssertError, bigMessage);
}
TRY_END();
}
CATCH(AssertError)
{
// Finally below should run even though this error has been rethrown
RETHROW();
}
FINALLY()
{
finallyDone = true;
}
TRY_END();
}
CATCH_ANY()
{
RETHROW();
}
TRY_END();
}
CATCH(MemoryError)
{
assert(false); // {uncoverable - catch should never be executed}
}
CATCH(RuntimeError)
{
assert(errorTryDepth() == 1);
assert(errorContext.tryList[1].state == errorStateCatch);
assert(strlen(errorMessage()) == sizeof(messageBuffer) - 1);
catchDone = true;
}
TRY_END();
assert(errorTryDepth() == 0);
assert(tryDone);
assert(catchDone);
assert(finallyDone);
assert(errorContext.tryTotal == 0);
}
// *****************************************************************************************************************************
if (testBegin("too deep recursive TRY_ERROR()"))
{
volatile bool tryDone = false;
bool catchDone = false;
bool finallyDone = false;
TRY_BEGIN()
{
tryDone = true;
testTryRecurse();
}
CATCH(AssertError)
{
assert(errorCode() == AssertError.code);
assert(strcmp(errorFileName(), TEST_PGB_PATH "/test/src/module/common/errorTest.c") == 0);
assert(strcmp(errorFunctionName(), "testTryRecurse") == 0);
assert(errorFileLine() == 29);
assert(
strcmp(
errorStackTrace(),
TEST_PGB_PATH "/test/src/module/common/errorTest:testTryRecurse:29:(test build required for parameters)")
== 0);
assert(strcmp(errorMessage(), "too many nested try blocks") == 0);
assert(strcmp(errorName(), AssertError.name) == 0);
assert(errorType() == &AssertError);
assert(errorTypeCode(errorType()) == AssertError.code);
assert(strcmp(errorTypeName(errorType()), AssertError.name) == 0);
catchDone = true;
}
FINALLY()
{
finallyDone = true;
}
TRY_END();
assert(tryDone);
assert(catchDone);
assert(finallyDone);
assert(errorContext.tryTotal == 0);
// This is only ERROR_TRY_MAX - 1 because one try was used up by the wrapper above the recursive function
assert(testTryRecurseTotal == ERROR_TRY_MAX - 1);
assert(!testTryRecurseCatch);
assert(testTryRecurseFinally);
}
// *****************************************************************************************************************************
if (testBegin("THROW_CODE() and THROW_CODE_FMT()"))
{
TRY_BEGIN()
{
THROW_CODE(25, "message");
}
CATCH_ANY()
{
assert(errorCode() == 25);
assert(strcmp(errorMessage(), "message") == 0);
}
TRY_END();
// -------------------------------------------------------------------------------------------------------------------------
TRY_BEGIN()
{
THROW_CODE_FMT(122, "message %d", 1);
}
CATCH_ANY()
{
assert(errorCode() == 122);
assert(strcmp(errorMessage(), "message 1") == 0);
}
TRY_END();
// -------------------------------------------------------------------------------------------------------------------------
TRY_BEGIN()
{
THROW_CODE(777, "message");
}
CATCH_ANY()
{
assert(errorCode() == UnknownError.code);
assert(strcmp(errorMessage(), "message") == 0);
}
TRY_END();
}
// *****************************************************************************************************************************
if (testBegin("THROW_SYS_ERROR() and THROW_SYS_ERROR_FMT()"))
{
THROW_ON_SYS_ERROR_FMT(false, AssertError, "no error");
TRY_BEGIN()
{
errno = E2BIG;
THROW_ON_SYS_ERROR(true, AssertError, "message");
}
CATCH_ANY()
{
printf("%s\n", errorMessage());
assert(errorCode() == AssertError.code);
assert(strcmp(errorMessage(), "message: [7] Argument list too long") == 0);
}
TRY_END();
// -------------------------------------------------------------------------------------------------------------------------
TRY_BEGIN()
{
errno = 0;
THROW_ON_SYS_ERROR_FMT(true, AssertError, "message %d", 77);
}
CATCH_ANY()
{
printf("%s\n", errorMessage());
assert(errorCode() == AssertError.code);
assert(strcmp(errorMessage(), "message 77") == 0);
}
TRY_END();
// -------------------------------------------------------------------------------------------------------------------------
TRY_BEGIN()
{
errno = E2BIG;
THROW_ON_SYS_ERROR_FMT(true, AssertError, "message %d", 77);
}
CATCH_ANY()
{
printf("%s\n", errorMessage());
assert(errorCode() == AssertError.code);
assert(strcmp(errorMessage(), "message 77: [7] Argument list too long") == 0);
}
TRY_END();
// -------------------------------------------------------------------------------------------------------------------------
TRY_BEGIN()
{
errno = 0;
THROW_SYS_ERROR(AssertError, "message");
}
CATCH_ANY()
{
printf("%s\n", errorMessage());
assert(errorCode() == AssertError.code);
assert(strcmp(errorMessage(), "message") == 0);
}
TRY_END();
// -------------------------------------------------------------------------------------------------------------------------
TRY_BEGIN()
{
errno = EIO;
THROW_SYS_ERROR_FMT(AssertError, "message %d", 1);
}
CATCH_ANY()
{
printf("%s\n", errorMessage());
assert(errorCode() == AssertError.code);
assert(strcmp(errorMessage(), "message 1: [5] Input/output error") == 0);
}
TRY_END();
// -------------------------------------------------------------------------------------------------------------------------
TRY_BEGIN()
{
errno = 0;
THROW_SYS_ERROR_FMT(AssertError, "message %d", 1);
}
CATCH_ANY()
{
printf("%s\n", errorMessage());
assert(errorCode() == AssertError.code);
assert(strcmp(errorMessage(), "message 1") == 0);
}
TRY_END();
}
// *****************************************************************************************************************************
if (testBegin("Uncaught error"))
{
// Test in a fork so the process does not actually exit
HARNESS_FORK_BEGIN()
{
HARNESS_FORK_CHILD_BEGIN(UnhandledError.code, false)
{
THROW(TestChildError, "does not get caught!");
}
HARNESS_FORK_CHILD_END();
}
HARNESS_FORK_END();
}
FUNCTION_HARNESS_RESULT_VOID();
}