/*********************************************************************************************************************************** Test Error Handling ***********************************************************************************************************************************/ #include #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/module/common/errorTest.c") == 0); assert(strcmp(errorFunctionName(), "testTryRecurse") == 0); assert(errorFileLine() == 29); assert( strcmp(errorStackTrace(), "test/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(); }