mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-01-18 04:58:51 +02:00
Add C error handler.
Basic try ... catch ... finally pattern to simplify error handling in C.
This commit is contained in:
parent
10dfbd90b5
commit
f1e739b4aa
@ -28,6 +28,10 @@
|
||||
<release-item>
|
||||
<p>Add <file>LibC.template.pm</file> to simplify LibC module generation.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>Add C error handler.</p>
|
||||
</release-item>
|
||||
</release-refactor-list>
|
||||
</release-core-list>
|
||||
|
||||
|
@ -156,6 +156,8 @@ use constant ERROR_PATH_EXISTS => ERROR_MIN
|
||||
push @EXPORT, qw(ERROR_PATH_EXISTS);
|
||||
use constant ERROR_FILE_EXISTS => ERROR_MINIMUM + 68;
|
||||
push @EXPORT, qw(ERROR_FILE_EXISTS);
|
||||
use constant ERROR_MEMORY => ERROR_MINIMUM + 69; # Thrown by C library
|
||||
push @EXPORT, qw(ERROR_CRYPT);
|
||||
|
||||
use constant ERROR_INVALID_VALUE => ERROR_MAXIMUM - 2;
|
||||
push @EXPORT, qw(ERROR_INVALID_VALUE);
|
||||
|
@ -25,6 +25,7 @@ C includes
|
||||
|
||||
These includes are from the src directory. There is no Perl-specific code in them.
|
||||
***********************************************************************************************************************************/
|
||||
#include "common/error.h"
|
||||
#include "common/type.h"
|
||||
#include "config/config.h"
|
||||
#include "config/configRule.h"
|
||||
|
269
src/common/error.c
Normal file
269
src/common/error.c
Normal file
@ -0,0 +1,269 @@
|
||||
/***********************************************************************************************************************************
|
||||
Error Handler
|
||||
***********************************************************************************************************************************/
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Maximum allowed number of nested try blocks
|
||||
***********************************************************************************************************************************/
|
||||
#define ERROR_TRY_MAX 32
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
States for each try
|
||||
***********************************************************************************************************************************/
|
||||
typedef enum {errorStateBegin, errorStateTry, errorStateCatch, errorStateFinal, errorStateEnd} ErrorState;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Track error handling
|
||||
***********************************************************************************************************************************/
|
||||
struct
|
||||
{
|
||||
// Array of jump buffers
|
||||
jmp_buf jumpList[ERROR_TRY_MAX];
|
||||
|
||||
// State of each try
|
||||
int tryTotal;
|
||||
|
||||
struct
|
||||
{
|
||||
ErrorState state;
|
||||
bool uncaught;
|
||||
} tryList[ERROR_TRY_MAX + 1];
|
||||
|
||||
// Last error
|
||||
struct
|
||||
{
|
||||
const ErrorType *errorType; // Error type
|
||||
const char *fileName; // Source file where the error occurred
|
||||
int fileLine; // Source file line where the error occurred
|
||||
const char *message; // Description of the error
|
||||
} error;
|
||||
} errorContext;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Message buffer and buffer size
|
||||
|
||||
The message buffer is statically allocated so there is some space to store error messages. Not being able to allocate such a small
|
||||
amount of memory seems pretty unlikely so just keep the code simple and let the loader deal with massively constrained memory
|
||||
situations.
|
||||
|
||||
The temp buffer is required because the error message being passed might be the error already stored in the message buffer.
|
||||
***********************************************************************************************************************************/
|
||||
#define ERROR_MESSAGE_BUFFER_SIZE 8192
|
||||
|
||||
static char messageBuffer[ERROR_MESSAGE_BUFFER_SIZE];
|
||||
static char messageBufferTemp[ERROR_MESSAGE_BUFFER_SIZE];
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Error type
|
||||
***********************************************************************************************************************************/
|
||||
const ErrorType *
|
||||
errorType()
|
||||
{
|
||||
return errorContext.error.errorType;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Error code (pulled from error type)
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
errorCode()
|
||||
{
|
||||
return errorTypeCode(errorType());
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Error filename
|
||||
***********************************************************************************************************************************/
|
||||
const char *
|
||||
errorFileName()
|
||||
{
|
||||
return errorContext.error.fileName;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Error file line number
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
errorFileLine()
|
||||
{
|
||||
return errorContext.error.fileLine;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Error message
|
||||
***********************************************************************************************************************************/
|
||||
const char *
|
||||
errorMessage()
|
||||
{
|
||||
return errorContext.error.message;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Error name (pulled from error type)
|
||||
***********************************************************************************************************************************/
|
||||
const char *
|
||||
errorName()
|
||||
{
|
||||
return errorTypeName(errorType());
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Is this error an instance of the error type?
|
||||
***********************************************************************************************************************************/
|
||||
bool
|
||||
errorInstanceOf(const ErrorType *errorTypeTest)
|
||||
{
|
||||
return errorType() == errorTypeTest || errorTypeExtends(errorType(), errorTypeTest);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Return current error context state
|
||||
***********************************************************************************************************************************/
|
||||
static ErrorState
|
||||
errorInternalState()
|
||||
{
|
||||
return errorContext.tryList[errorContext.tryTotal].state;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
True when in try state
|
||||
***********************************************************************************************************************************/
|
||||
bool
|
||||
errorInternalStateTry()
|
||||
{
|
||||
return errorInternalState() == errorStateTry;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
True when in catch state and the expected error matches
|
||||
***********************************************************************************************************************************/
|
||||
bool
|
||||
errorInternalStateCatch(const ErrorType *errorTypeCatch)
|
||||
{
|
||||
return errorInternalState() == errorStateCatch && errorInstanceOf(errorTypeCatch) && errorInternalProcess(true);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
True when in final state
|
||||
***********************************************************************************************************************************/
|
||||
bool
|
||||
errorInternalStateFinal()
|
||||
{
|
||||
return errorInternalState() == errorStateFinal;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Return jump buffer for current try
|
||||
***********************************************************************************************************************************/
|
||||
jmp_buf *
|
||||
errorInternalJump()
|
||||
{
|
||||
return &errorContext.jumpList[errorContext.tryTotal - 1];
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Begin the try block
|
||||
***********************************************************************************************************************************/
|
||||
bool errorInternalTry(const char *fileName, int fileLine)
|
||||
{
|
||||
// If try total has been exceeded then throw an error
|
||||
if (errorContext.tryTotal >= ERROR_TRY_MAX)
|
||||
errorInternalThrow(&AssertError, fileName, fileLine, "too many nested try blocks");
|
||||
|
||||
// Increment try total
|
||||
errorContext.tryTotal++;
|
||||
|
||||
// Setup try
|
||||
errorContext.tryList[errorContext.tryTotal].state = errorStateBegin;
|
||||
errorContext.tryList[errorContext.tryTotal].uncaught = false;
|
||||
|
||||
// Try setup was successful
|
||||
return true;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Propogate the error up so it can be caught
|
||||
***********************************************************************************************************************************/
|
||||
void errorInternalPropagate()
|
||||
{
|
||||
// Mark the error as uncaught
|
||||
errorContext.tryList[errorContext.tryTotal].uncaught = true;
|
||||
|
||||
// If there is a parent try then jump to it
|
||||
if (errorContext.tryTotal > 0)
|
||||
longjmp(errorContext.jumpList[errorContext.tryTotal - 1], 1);
|
||||
|
||||
// If there was no try to catch this error then output to stderr
|
||||
if (fprintf( // {uncovered - output to stderr is a problem for test harness}
|
||||
stderr, "\nUncaught %s: %s\n thrown at %s:%d\n\n",
|
||||
errorName(), errorMessage(), errorFileName(), errorFileLine()) > 0)
|
||||
fflush(stderr); // {+uncovered}
|
||||
|
||||
// Exit with failure
|
||||
exit(EXIT_FAILURE); // {uncovered - exit failure is a problem for test harness}
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Process the error through each try and state
|
||||
***********************************************************************************************************************************/
|
||||
bool errorInternalProcess(bool catch)
|
||||
{
|
||||
// If a catch statement then return
|
||||
if (catch)
|
||||
{
|
||||
errorContext.tryList[errorContext.tryTotal].uncaught = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Increment the state
|
||||
errorContext.tryList[errorContext.tryTotal].state++;
|
||||
|
||||
// If the error has been caught then increment the state
|
||||
if (errorContext.tryList[errorContext.tryTotal].state == errorStateCatch &&
|
||||
!errorContext.tryList[errorContext.tryTotal].uncaught)
|
||||
{
|
||||
errorContext.tryList[errorContext.tryTotal].state++;
|
||||
}
|
||||
|
||||
// Return if not done
|
||||
if (errorContext.tryList[errorContext.tryTotal].state < errorStateEnd)
|
||||
return true;
|
||||
|
||||
// Remove the try
|
||||
errorContext.tryTotal--;
|
||||
|
||||
// If not caught in the last try then propogate
|
||||
if (errorContext.tryList[errorContext.tryTotal + 1].uncaught)
|
||||
errorInternalPropagate();
|
||||
|
||||
// Nothing left to process
|
||||
return false;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Throw an error
|
||||
***********************************************************************************************************************************/
|
||||
void errorInternalThrow(const ErrorType *errorType, const char *fileName, int fileLine, const char *format, ...)
|
||||
{
|
||||
// Setup error data
|
||||
errorContext.error.errorType = errorType;
|
||||
errorContext.error.fileName = fileName;
|
||||
errorContext.error.fileLine = fileLine;
|
||||
|
||||
// Create message
|
||||
va_list argument;
|
||||
va_start(argument, format);
|
||||
vsnprintf(messageBufferTemp, ERROR_MESSAGE_BUFFER_SIZE - 1, format, argument);
|
||||
|
||||
// Assign message to the error
|
||||
strcpy(messageBuffer, messageBufferTemp);
|
||||
errorContext.error.message = (const char *)messageBuffer;
|
||||
|
||||
// Propogate the error
|
||||
errorInternalPropagate();
|
||||
} // {uncoverable - errorPropagate() does not return}
|
106
src/common/error.h
Normal file
106
src/common/error.h
Normal file
@ -0,0 +1,106 @@
|
||||
/***********************************************************************************************************************************
|
||||
Error Handler
|
||||
|
||||
Implement a try ... catch ... finally error handler.
|
||||
|
||||
ERROR_TRY
|
||||
{
|
||||
<Do something that might throw an error>
|
||||
}
|
||||
ERROR_CATCH(Error1)
|
||||
{
|
||||
<Handle Error1>
|
||||
}
|
||||
ERROR_CATCH(Error2)
|
||||
{
|
||||
<Handle Error2>
|
||||
}
|
||||
ERROR_CATCH_ANY()
|
||||
{
|
||||
<Handle errors that are not Error1 or Error2>
|
||||
}
|
||||
FINALLY
|
||||
{
|
||||
<Cleanup code that runs in all cases>
|
||||
}
|
||||
|
||||
The ERROR_CATCH() and ERROR_FINALLY() blocks are optional but at least one must be specified. There is no need for an ERROR_TRY
|
||||
block by itself because errors will automatically be propagated to the nearest try block in the call stack.
|
||||
|
||||
Never call return from within any of the ERROR*() blocks.
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef ERROR_H
|
||||
#define ERROR_H
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
#include "common/errorType.h"
|
||||
#include "common/type.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Error accessor functions
|
||||
***********************************************************************************************************************************/
|
||||
const ErrorType *errorType();
|
||||
int errorCode();
|
||||
const char *errorFileName();
|
||||
int errorFileLine();
|
||||
const char *errorMessage();
|
||||
const char *errorName();
|
||||
bool errorInstanceOf(const ErrorType *errorTypeTest);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Begin a block where errors can be thrown
|
||||
***********************************************************************************************************************************/
|
||||
#define ERROR_TRY() \
|
||||
if (errorInternalTry(__FILE__, __LINE__) && setjmp(*errorInternalJump()) >= 0) \
|
||||
while (errorInternalProcess(false)) \
|
||||
if (errorInternalStateTry())
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Catch a specific error thrown in the try block
|
||||
***********************************************************************************************************************************/
|
||||
#define ERROR_CATCH(errorTypeCatch) \
|
||||
else if (errorInternalStateCatch(&errorTypeCatch))
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Catch any error thrown in the try block
|
||||
***********************************************************************************************************************************/
|
||||
#define ERROR_CATCH_ANY() \
|
||||
else if (errorInternalStateCatch(&RuntimeError))
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Code to run whether the try block was successful or not
|
||||
***********************************************************************************************************************************/
|
||||
#define ERROR_FINALLY() \
|
||||
else if (errorInternalStateFinal())
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Throw an error
|
||||
|
||||
Errors can be thrown any time, but if there is no ERROR_TRY block somewhere in the call stack then the program will exit and print
|
||||
the error information to stderr.
|
||||
***********************************************************************************************************************************/
|
||||
#define ERROR_THROW(errorType, ...) \
|
||||
errorInternalThrow(&errorType, __FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Rethrow the current error
|
||||
***********************************************************************************************************************************/
|
||||
#define ERROR_RETHROW() \
|
||||
errorInternalPropagate()
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Internal functions
|
||||
|
||||
These functions are used by the macros to implement the error handler and should never be called independently.
|
||||
***********************************************************************************************************************************/
|
||||
bool errorInternalTry(const char *fileName, int fileLine);
|
||||
jmp_buf *errorInternalJump();
|
||||
bool errorInternalStateTry();
|
||||
bool errorInternalStateCatch(const ErrorType *errorTypeCatch);
|
||||
bool errorInternalStateFinal();
|
||||
bool errorInternalProcess(bool catch);
|
||||
void errorInternalPropagate();
|
||||
void errorInternalThrow(const ErrorType *errorType, const char *fileName, int fileLine, const char *format, ...);
|
||||
|
||||
#endif
|
78
src/common/errorType.c
Normal file
78
src/common/errorType.c
Normal file
@ -0,0 +1,78 @@
|
||||
/***********************************************************************************************************************************
|
||||
Application-Defined Errors
|
||||
***********************************************************************************************************************************/
|
||||
#include "errorType.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Error code range -- chosen to not overlap with defined return values
|
||||
***********************************************************************************************************************************/
|
||||
#define ERROR_CODE_MIN 25
|
||||
#define ERROR_CODE_MAX 125
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Represents an error type
|
||||
***********************************************************************************************************************************/
|
||||
struct ErrorType
|
||||
{
|
||||
const int code;
|
||||
const char *name;
|
||||
const struct ErrorType *parentType;
|
||||
};
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macro to create error structs
|
||||
***********************************************************************************************************************************/
|
||||
#define ERROR_DEFINE(code, name, parentType) \
|
||||
const ErrorType name = {code, #name, &parentType}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Define errors
|
||||
***********************************************************************************************************************************/
|
||||
ERROR_DEFINE(ERROR_CODE_MIN, AssertError, RuntimeError);
|
||||
|
||||
ERROR_DEFINE(ERROR_CODE_MIN + 69, MemoryError, RuntimeError);
|
||||
|
||||
ERROR_DEFINE(ERROR_CODE_MAX, RuntimeError, RuntimeError);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Error type code
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
errorTypeCode(const ErrorType *errorType)
|
||||
{
|
||||
return errorType->code;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Error type name
|
||||
***********************************************************************************************************************************/
|
||||
const char *
|
||||
errorTypeName(const ErrorType *errorType)
|
||||
{
|
||||
return errorType->name;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Error type parent
|
||||
***********************************************************************************************************************************/
|
||||
const ErrorType *
|
||||
errorTypeParent(const ErrorType *errorType)
|
||||
{
|
||||
return errorType->parentType;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Does the child error type extend the parent error type?
|
||||
***********************************************************************************************************************************/
|
||||
bool errorTypeExtends(const ErrorType *child, const ErrorType *parent)
|
||||
{
|
||||
// Search for the parent
|
||||
for (; child && errorTypeParent(child) != child; child = (ErrorType *)errorTypeParent(child))
|
||||
{
|
||||
if (errorTypeParent(child) == parent)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parent was not found
|
||||
return false;
|
||||
}
|
29
src/common/errorType.h
Normal file
29
src/common/errorType.h
Normal file
@ -0,0 +1,29 @@
|
||||
/***********************************************************************************************************************************
|
||||
Application-Defined Errors
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef ERROR_TYPE_H
|
||||
#define ERROR_TYPE_H
|
||||
|
||||
#include "common/type.h"
|
||||
|
||||
// Represents an error type
|
||||
typedef struct ErrorType ErrorType;
|
||||
|
||||
// Macros for declaring and defining new error types
|
||||
#define ERROR_DECLARE(name) \
|
||||
extern const ErrorType name
|
||||
|
||||
// Error types
|
||||
ERROR_DECLARE(AssertError);
|
||||
|
||||
ERROR_DECLARE(MemoryError);
|
||||
|
||||
ERROR_DECLARE(RuntimeError);
|
||||
|
||||
// Functions
|
||||
int errorTypeCode(const ErrorType *errorType);
|
||||
const char *errorTypeName(const ErrorType *errorType);
|
||||
const ErrorType * errorTypeParent(const ErrorType *errorType);
|
||||
bool errorTypeExtends(const ErrorType *child, const ErrorType *parent);
|
||||
|
||||
#endif
|
@ -107,6 +107,17 @@ my $oTestDef =
|
||||
'common/type' => TESTDEF_COVERAGE_NOCODE,
|
||||
},
|
||||
},
|
||||
{
|
||||
&TESTDEF_NAME => 'error',
|
||||
&TESTDEF_TOTAL => 4,
|
||||
&TESTDEF_C => true,
|
||||
|
||||
&TESTDEF_COVERAGE =>
|
||||
{
|
||||
'common/error' => TESTDEF_COVERAGE_FULL,
|
||||
'common/errorType' => TESTDEF_COVERAGE_FULL,
|
||||
},
|
||||
},
|
||||
{
|
||||
&TESTDEF_NAME => 'http-client',
|
||||
&TESTDEF_TOTAL => 2,
|
||||
|
@ -3,6 +3,9 @@ C Test Harness
|
||||
***********************************************************************************************************************************/
|
||||
#include "common/type.h"
|
||||
|
||||
// Bogus values
|
||||
#define BOGUS_STR "BOGUS"
|
||||
|
||||
// Functions
|
||||
void testAdd(int run, bool selected);
|
||||
bool testBegin(const char *name);
|
||||
|
169
test/src/module/common/errorTest.c
Normal file
169
test/src/module/common/errorTest.c
Normal file
@ -0,0 +1,169 @@
|
||||
/***********************************************************************************************************************************
|
||||
Test Error Handling
|
||||
***********************************************************************************************************************************/
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
testTryRecurse - test to blow up try stack
|
||||
***********************************************************************************************************************************/
|
||||
int testTryRecurseTotal = 0;
|
||||
bool testTryRecurseCatch = false;
|
||||
bool testTryRecurseFinally = false;
|
||||
|
||||
void testTryRecurse()
|
||||
{
|
||||
ERROR_TRY()
|
||||
{
|
||||
testTryRecurseTotal++;
|
||||
assert(errorContext.tryTotal == testTryRecurseTotal + 1);
|
||||
|
||||
testTryRecurse();
|
||||
}
|
||||
ERROR_CATCH(MemoryError)
|
||||
{
|
||||
testTryRecurseCatch = true; // {uncoverable - catch should never be executed}
|
||||
}
|
||||
ERROR_FINALLY()
|
||||
{
|
||||
testTryRecurseFinally = true;
|
||||
}
|
||||
} // {uncoverable - function throws error, never returns}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test Run
|
||||
***********************************************************************************************************************************/
|
||||
void testRun()
|
||||
{
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
if (testBegin("check that try stack is initialized correctly"))
|
||||
{
|
||||
assert(errorContext.tryTotal == 0);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
if (testBegin("ERROR_TRY() with no errors"))
|
||||
{
|
||||
bool tryDone = false;
|
||||
bool catchDone = false;
|
||||
bool finallyDone = false;
|
||||
|
||||
ERROR_TRY()
|
||||
{
|
||||
assert(errorContext.tryTotal == 1);
|
||||
tryDone = true;
|
||||
}
|
||||
ERROR_CATCH_ANY()
|
||||
{
|
||||
catchDone = true; // {uncoverable - catch should never be executed}
|
||||
}
|
||||
ERROR_FINALLY()
|
||||
{
|
||||
assert(errorContext.tryList[1].state == errorStateFinal);
|
||||
finallyDone = true;
|
||||
}
|
||||
|
||||
assert(tryDone);
|
||||
assert(!catchDone);
|
||||
assert(finallyDone);
|
||||
assert(errorContext.tryTotal == 0);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
if (testBegin("ERROR_TRY() with multiple catches"))
|
||||
{
|
||||
bool tryDone = false;
|
||||
bool catchDone = false;
|
||||
bool finallyDone = false;
|
||||
|
||||
ERROR_TRY()
|
||||
{
|
||||
assert(errorContext.tryTotal == 1);
|
||||
|
||||
ERROR_TRY()
|
||||
{
|
||||
assert(errorContext.tryTotal == 2);
|
||||
|
||||
ERROR_TRY()
|
||||
{
|
||||
assert(errorContext.tryTotal == 3);
|
||||
|
||||
ERROR_TRY()
|
||||
{
|
||||
assert(errorContext.tryTotal == 4);
|
||||
tryDone = true;
|
||||
|
||||
ERROR_THROW(AssertError, BOGUS_STR);
|
||||
}
|
||||
}
|
||||
ERROR_CATCH(AssertError)
|
||||
{
|
||||
// Finally below should run even though this error has been rethrown
|
||||
ERROR_RETHROW();
|
||||
}
|
||||
ERROR_FINALLY()
|
||||
{
|
||||
finallyDone = true;
|
||||
}
|
||||
}
|
||||
ERROR_CATCH_ANY()
|
||||
{
|
||||
ERROR_RETHROW();
|
||||
}
|
||||
}
|
||||
ERROR_CATCH(MemoryError)
|
||||
{
|
||||
assert(false); // {uncoverable - catch should never be executed}
|
||||
}
|
||||
ERROR_CATCH(RuntimeError)
|
||||
{
|
||||
assert(errorContext.tryTotal == 1);
|
||||
assert(errorContext.tryList[1].state == errorStateCatch);
|
||||
|
||||
catchDone = true;
|
||||
}
|
||||
|
||||
assert(tryDone);
|
||||
assert(catchDone);
|
||||
assert(finallyDone);
|
||||
assert(errorContext.tryTotal == 0);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
if (testBegin("too deep recursive TRY_ERROR()"))
|
||||
{
|
||||
bool tryDone = false;
|
||||
bool catchDone = false;
|
||||
bool finallyDone = false;
|
||||
|
||||
ERROR_TRY()
|
||||
{
|
||||
tryDone = true;
|
||||
testTryRecurse();
|
||||
}
|
||||
ERROR_CATCH(AssertError)
|
||||
{
|
||||
assert(errorCode() == AssertError.code);
|
||||
assert(errorFileName() != NULL);
|
||||
assert(errorFileLine() >= 1);
|
||||
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;
|
||||
}
|
||||
ERROR_FINALLY()
|
||||
{
|
||||
finallyDone = true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user