diff --git a/lib/CException.c b/lib/CException.c index d2965aa..e0009c8 100644 --- a/lib/CException.c +++ b/lib/CException.c @@ -1,6 +1,6 @@ #include "CException.h" -volatile CEXCEPTION_FRAME_T CExceptionFrames[CEXCEPTION_NUM_ID]; +volatile CEXCEPTION_FRAME_T CExceptionFrames[CEXCEPTION_NUM_ID] = { 0 }; //------------------------------------------------------------------------------------------ // Throw @@ -9,11 +9,15 @@ void Throw(CEXCEPTION_T ExceptionID) { unsigned int MY_ID = CEXCEPTION_GET_ID; CExceptionFrames[MY_ID].Exception = ExceptionID; - longjmp(*CExceptionFrames[MY_ID].pFrame, 1); + if (CExceptionFrames[MY_ID].pFrame) + { + longjmp(*CExceptionFrames[MY_ID].pFrame, 1); + } + CEXCEPTION_NO_CATCH_HANDLER(MY_ID); } //------------------------------------------------------------------------------------------ -// Explaination of what it's all for: +// Explanation of what it's all for: //------------------------------------------------------------------------------------------ /* #define Try diff --git a/lib/CException.h b/lib/CException.h index c226253..c27625f 100644 --- a/lib/CException.h +++ b/lib/CException.h @@ -3,6 +3,12 @@ #include +#ifdef __cplusplus +extern "C" +{ +#endif + + //To Use CException, you have a number of options: //1. Just include it and run with the defaults //2. Define any of the following symbols at the command line to override them @@ -33,6 +39,11 @@ #define CEXCEPTION_T unsigned int #endif +//This is an optional special handler for when there is no global Catch +#ifndef CEXCEPTION_NO_CATCH_HANDLER +#define CEXCEPTION_NO_CATCH_HANDLER(id) +#endif + //exception frame structures typedef struct { jmp_buf* pFrame; @@ -67,4 +78,9 @@ extern volatile CEXCEPTION_FRAME_T CExceptionFrames[]; //Throw an Error void Throw(CEXCEPTION_T ExceptionID); +#ifdef __cplusplus +} // extern "C" +#endif + + #endif // _CEXCEPTION_H diff --git a/rakefile.rb b/rakefile.rb index 2458c6f..d538a6d 100644 --- a/rakefile.rb +++ b/rakefile.rb @@ -18,7 +18,7 @@ OUT_EXTENSION = '.out' SRC_FILES = "lib/CException.c test/TestException.c test/TestException_Runner.c #{UNITY_DIR}/unity.c" INC_DIRS = "-Ilib -Itest -I#{UNITY_DIR}" LIB_DIRS = C_LIBS.empty? ? '' : "-L#{C_LIBS}" -SYMBOLS = '-DTEST -DEXCEPTION_USE_CONFIG_FILE' +SYMBOLS = '-DTEST -DCEXCEPTION_USE_CONFIG_FILE' CLEAN.include("#{HERE}*.out") diff --git a/test/CExceptionConfig.h b/test/CExceptionConfig.h index 931dbcb..28e49ae 100644 --- a/test/CExceptionConfig.h +++ b/test/CExceptionConfig.h @@ -1,13 +1,30 @@ #ifndef _EXCEPTION_H #define _EXCEPTION_H +#include "Unity.h" + +extern volatile int TestingTheFallback; + //Optionally define the exception type (something like an int which can be directly assigned) #define CEXCEPTION_T int // Optionally define the reserved value representing NO EXCEPTION #define CEXCEPTION_NONE (1234) -// Multi-Tasking environments will need a couple of macros defined to make this library +// Optionally define a special handler for unhandled exceptions +#define CEXCEPTION_NO_CATCH_HANDLER(id) \ +{ \ + if (!TestingTheFallback) \ + { \ + TEST_FAIL_MESSAGE("Unexpected Exception!"); \ + } \ + else \ + { \ + TestingTheFallback--; \ + } \ +} + +// Multi-Tasking environments will need a couple of macros defined to make this library // properly handle multiple exception stacks. You will need to include and required // definitions, then define the following macros: // EXCEPTION_GET_ID - returns the id of the current task indexed 0 to (numtasks - 1) diff --git a/test/TestException.c b/test/TestException.c index 7199ad7..d7c69ee 100644 --- a/test/TestException.c +++ b/test/TestException.c @@ -1,8 +1,12 @@ #include "unity.h" #include "CException.h" +volatile int TestingTheFallback; + void setUp(void) { + CExceptionFrames[0].pFrame = NULL; + TestingTheFallback = 0; } void tearDown(void) @@ -289,3 +293,36 @@ void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void) TEST_ASSERT_EQUAL(0x01, e); } } + +void test_AThrowWithoutATryCatchWillUseDefaultHandlerIfSpecified(void) +{ + //Let the fallback handler know we're expecting it to get called this time, so don't fail + TestingTheFallback = 1; + + Throw(0xBE); + + //We know the fallback was run because it decrements the counter above + TEST_ASSERT_FALSE(TestingTheFallback); +} + +void test_AThrowWithoutOutsideATryCatchWillUseDefaultHandlerEvenAfterTryCatch(void) +{ + CEXCEPTION_T e; + + Try + { + //It's not really important that we do anything here. + } + Catch(e) + { + //The entire purpose here is just to make sure things get set back to using the default handler when done + } + + //Let the fallback handler know we're expecting it to get called this time, so don't fail + TestingTheFallback = 1; + + Throw(0xBE); + + //We know the fallback was run because it decrements the counter above + TEST_ASSERT_FALSE(TestingTheFallback); +} \ No newline at end of file diff --git a/test/TestException_Runner.c b/test/TestException_Runner.c index d9d34de..97224cb 100644 --- a/test/TestException_Runner.c +++ b/test/TestException_Runner.c @@ -17,21 +17,14 @@ extern void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void); extern void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void); extern void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThisDoesntCorruptExceptionId(void); extern void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThatEachExceptionIdIndependent(void); +extern void test_AThrowWithoutATryCatchWillUseDefaultHandlerIfSpecified(void); +extern void test_AThrowWithoutOutsideATryCatchWillUseDefaultHandlerEvenAfterTryCatch(void); static void runTest(UnityTestFunction test) { - CEXCEPTION_T e; if (TEST_PROTECT()) { - setUp(); - Try - { test(); - } - Catch(e) - { - TEST_FAIL_MESSAGE("Unexpected exception!") - } } tearDown(); } @@ -43,18 +36,20 @@ int main(void) UnityBegin(); // RUN_TEST calls runTest - RUN_TEST(test_BasicTryDoesNothingIfNoThrow, 12); - RUN_TEST(test_BasicThrowAndCatch, 30); - RUN_TEST(test_BasicThrowAndCatch_WithMiniSyntax, 49); - RUN_TEST(test_VerifyVolatilesSurviveThrowAndCatch, 69); - RUN_TEST(test_ThrowFromASubFunctionAndCatchInRootFunc, 98); - RUN_TEST(test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc, 139); - RUN_TEST(test_ThrowAndCatchFromASubFunctionAndNoRethrowToCatchInRootFunc, 158); - RUN_TEST(test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThisDoesntCorruptExceptionId, 175); - RUN_TEST(test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThatEachExceptionIdIndependent, 193); - RUN_TEST(test_CanHaveMultipleTryBlocksInASingleFunction, 220); - RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside, 245); - RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside, 269); + RUN_TEST(test_BasicTryDoesNothingIfNoThrow, 15); + RUN_TEST(test_BasicThrowAndCatch, 33); + RUN_TEST(test_BasicThrowAndCatch_WithMiniSyntax, 52); + RUN_TEST(test_VerifyVolatilesSurviveThrowAndCatch, 72); + RUN_TEST(test_ThrowFromASubFunctionAndCatchInRootFunc, 101); + RUN_TEST(test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc, 142); + RUN_TEST(test_ThrowAndCatchFromASubFunctionAndNoRethrowToCatchInRootFunc, 161); + RUN_TEST(test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThisDoesntCorruptExceptionId, 178); + RUN_TEST(test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThatEachExceptionIdIndependent, 196); + RUN_TEST(test_CanHaveMultipleTryBlocksInASingleFunction, 223); + RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside, 248); + RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside, 272); + RUN_TEST(test_AThrowWithoutATryCatchWillUseDefaultHandlerIfSpecified, 296); + RUN_TEST(test_AThrowWithoutOutsideATryCatchWillUseDefaultHandlerEvenAfterTryCatch, 308); UnityEnd();