diff --git a/docs/CExceptionSummary.odt b/docs/CExceptionSummary.odt index 53edb07..f8ea5d7 100644 Binary files a/docs/CExceptionSummary.odt and b/docs/CExceptionSummary.odt differ diff --git a/docs/CExceptionSummary.pdf b/docs/CExceptionSummary.pdf index 70c28a6..2ce685a 100644 Binary files a/docs/CExceptionSummary.pdf and b/docs/CExceptionSummary.pdf differ diff --git a/docs/readme.txt b/docs/readme.txt index c88713a..f25ed30 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -1,242 +1,261 @@ -==================================================================== -CException -==================================================================== - -CException is a basic exception framework for C, suitable for use in -embedded applications. It provides an exception framework similar in -use to C++, but with much less overhead. - -CException uses C standard library functions setjmp and longjmp to -operate. As long as the target system has these two functions defined, -this library should be useable with very little configuration. It -even supports environments where multiple program flows are in use, -such as real-time operating systems. - -There are about a gabillion exception frameworks using a similar -setjmp/longjmp method out there... and there will probably be more -in the future. Unfortunately, when we started our last embedded -project, all those that existed either (a) did not support multiple -tasks (therefore multiple stacks) or (b) were way more complex than -we really wanted. CException was born. - -Why use CException? - -0. It's ANSI C, and it beats passing error codes around. - -1. You want something simple... CException throws a single id. You can - define those ID's to be whatever you like. You might even choose which - type that number is for your project. But that's as far as it goes. - We weren't interested in passing objects or structs or strings... - just simple error codes. - -2. Performance... CException can be configured for single tasking or - multitasking. In single tasking, there is very little overhead past - the setjmp/longjmp calls (which are already fast). In multitasking, - your only additional overhead is the time it takes you to determine - a unique task id 0 - num_tasks. - -For the latest version, go to http://cexception.sourceforge.net - --------------------------------------------------------------------- -CONTENTS OF THIS DOCUMENT --------------------------------------------------------------------- - -Usage -Limitations -API -Configuration -Testing -License - --------------------------------------------------------------------- -Usage --------------------------------------------------------------------- - -Code that is to be protected are wrapped in Try { } Catch { } blocks. -The code directly following the Try call is "protected", meaning that -if any Throws occur, program control is directly transferred to the -start of the Catch block. - -A numerical exception ID is included with Throw, and is made accessible -from the Catch block. - -Throws can occur from within function calls (nested as deeply as you -like) or directly from within the function itself. - --------------------------------------------------------------------- -Limitations --------------------------------------------------------------------- - -This library was made to be as fast as possible, and provide basic -exception handling. It is not a full-blown exception library. Because -of this, there are a few limitations that should be observed in order -to successfully utilize this library: - -1. Do not directly "return" from within a Try block, nor "goto" - into or out of a Try block. - - Why? - - The "Try" macro allocates some local memory and alters a global - pointer. These are cleaned up at the top of the "Catch" macro. - Gotos and returns would bypass some of these steps, resulting in - memory leaks or unpredictable behavior. - -2. If (a) you change local (stack) variables within your Try block, - AND (b) wish to make use of the updated values after an exception - is thrown, those variables should be made volatile. Note that this - is ONLY for locals and ONLY when you need access to them after a - throw. - - Why? - - Compilers optimize. There is no way to guarantee that the actual - memory location was updated and not just a register unless the - variable is marked volatile. - -3. Memory which is malloc'd or new'd is not automatically released - when an error is thrown. This will sometimes be desirable, and - othertimes may not. It will be the responsibility of the Catch - block to perform this kind of cleanup. - - Why? - - There's just no easy way to track malloc'd memory, etc., without - replacing or wrapping malloc calls or something like that. This - is a light framework, so these options were not desirable. - --------------------------------------------------------------------- -API --------------------------------------------------------------------- - -Try ---- - -Try is a macro which starts a protected block. It MUST be followed by -a pair of braces or a single protected line (similar to an 'if'), -enclosing the data that is to be protected. It MUST be followed by a -Catch block (don't worry, you'll get compiler errors to let you know if -you mess any of that up). - -Catch(e) --------- - -Catch is a macro which ends the Try block and starts the error handling -block. The catch block is called if and only if an exception was thrown -while within the Try block. This error was thrown by a Throw call -somewhere within Try (or within a function called within Try, or a function -called by a function called within Try, etc). - -The single parameter 'e' is filled with the error code which was thrown. -This can be used for reporting, conditional cleanup, etc. (or you can just -ignore it if you really want... people ignore return codes all the time, -right?). 'e' should be of type EXCEPTION_T; - -Throw(e) --------- - -The method of throwing an error. Throws should only occur from within a -protected (Try...Catch) block, though it may easily be nested many function -calls deep without an impact on performance or functionality. Throw takes -a single argument, which is an exception id which will be passed to Catch -as the reason for the error. - -If you wish to Rethrow an error, this can be done by calling Throw(e) with -the error code you just caught. It IS valid to throw from a catch block. - --------------------------------------------------------------------- -CONFIGURATION --------------------------------------------------------------------- - -CException is a mostly portable library. It has one universal -dependency, and some macros which are required if working in a -multi-tasking environment. - -1. The standard C library setjmp must be available. Since this is part - of the standard library, chances are good that you'll be fine. - -2. If working in a multitasking environment, methods for obtaining an - index into an array of frames and to get the overall number of - id's are required. If the OS supports a method to retrieve Task - ID's, and those Tasks are number 0, 1, 2... you are in an ideal - situation. Otherwise, a more creative mapping function may be - required. Note that this function is likely to be called twice - for each protected block and once during a throw. This is the - only overhead in the system. - -Exception.h ------------------ -By convention, most projects include Exception.h which defines any -further requirements, then calls CException.h to do the gruntwork. All -of these are optional. You could directly include CException.h if -you wanted and just use the defaults provided. - -EXCEPTION_T - Set this to the type you want your exception id's - to be. Defaults to 'unsigned int'. - -EXCEPTION_NONE - Set this to a number which will never be an - exception id in your system. Defaults to 0x5a5a5a5a. - -EXCEPTION_GET_ID - If in a multi-tasking environment, this should be - set to be a call to the function described in #2 above. - Defaults to just return 0 all the time (good for - single tasking environments) - -EXCEPTION_NUM_ID - If in a multi-tasking environment, this should be set - to the number of ID's required (usually the number of - tasks in the system). Defaults to 1 (for single - tasking environments). - -CEXCEPTION_NO_CATCH_HANDLER(id) - This macro can be optionally specified. - It allows you to specify code to be called when a Throw - is made outside of Try...Catch protection. Consider - this the emergency fallback plan for when something has - gone terribly wrong. - -You may also want to include any header files which will commonly be -needed by the rest of your application where it uses exception handling -here. For example, OS header files or exception codes would be useful. - --------------------------------------------------------------------- -TESTING --------------------------------------------------------------------- - -If you want to validate that CException works with your tools or that -it works with your custom configuration, you may want to run the test -suite. - -The test suite included makes use of the Unity Test Framework. It will -require a native C compiler. The example makefile uses MinGW's gcc. -Modify the makefile to include the proper paths to tools, then run 'make' -to compile and run the test application. - -C_COMPILER - The C compiler to use to perform the tests -C_LIBS - The path to the C libraries (including setjmp) -UNITY_DIR - The path to the Unity framework (required to run tests) - (get it at http://unity.sourceforge.net) - --------------------------------------------------------------------- -LICENSE --------------------------------------------------------------------- - -This software is licensed under the MIT License - -Copyright (c) 2007-2012 Mark VanderVoord - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +==================================================================== +CException +==================================================================== + +CException is a basic exception framework for C, suitable for use in +embedded applications. It provides an exception framework similar in +use to C++, but with much less overhead. + +CException uses C standard library functions setjmp and longjmp to +operate. As long as the target system has these two functions defined, +this library should be useable with very little configuration. It +even supports environments where multiple program flows are in use, +such as real-time operating systems. + +There are about a gabillion exception frameworks using a similar +setjmp/longjmp method out there... and there will probably be more +in the future. Unfortunately, when we started our last embedded +project, all those that existed either (a) did not support multiple +tasks (therefore multiple stacks) or (b) were way more complex than +we really wanted. CException was born. + +Why use CException? + +0. It's ANSI C, and it beats passing error codes around. + +1. You want something simple... CException throws a single id. You can + define those ID's to be whatever you like. You might even choose which + type that number is for your project. But that's as far as it goes. + We weren't interested in passing objects or structs or strings... + just simple error codes. + +2. Performance... CException can be configured for single tasking or + multitasking. In single tasking, there is very little overhead past + the setjmp/longjmp calls (which are already fast). In multitasking, + your only additional overhead is the time it takes you to determine + a unique task id 0 - num_tasks. + +For the latest version, go to http://cexception.sourceforge.net + +-------------------------------------------------------------------- +CONTENTS OF THIS DOCUMENT +-------------------------------------------------------------------- + +Usage +Limitations +API +Configuration +Testing +License + +-------------------------------------------------------------------- +Usage +-------------------------------------------------------------------- + +Code that is to be protected are wrapped in Try { } Catch { } blocks. +The code directly following the Try call is "protected", meaning that +if any Throws occur, program control is directly transferred to the +start of the Catch block. + +A numerical exception ID is included with Throw, and is made accessible +from the Catch block. + +Throws can occur from within function calls (nested as deeply as you +like) or directly from within the function itself. + +-------------------------------------------------------------------- +Limitations +-------------------------------------------------------------------- + +This library was made to be as fast as possible, and provide basic +exception handling. It is not a full-blown exception library. Because +of this, there are a few limitations that should be observed in order +to successfully utilize this library: + +1. Do not directly "return" from within a Try block, nor "goto" + into or out of a Try block. + + Why? + + The "Try" macro allocates some local memory and alters a global + pointer. These are cleaned up at the top of the "Catch" macro. + Gotos and returns would bypass some of these steps, resulting in + memory leaks or unpredictable behavior. + +2. If (a) you change local (stack) variables within your Try block, + AND (b) wish to make use of the updated values after an exception + is thrown, those variables should be made volatile. Note that this + is ONLY for locals and ONLY when you need access to them after a + throw. + + Why? + + Compilers optimize. There is no way to guarantee that the actual + memory location was updated and not just a register unless the + variable is marked volatile. + +3. Memory which is malloc'd or new'd is not automatically released + when an error is thrown. This will sometimes be desirable, and + othertimes may not. It will be the responsibility of the Catch + block to perform this kind of cleanup. + + Why? + + There's just no easy way to track malloc'd memory, etc., without + replacing or wrapping malloc calls or something like that. This + is a light framework, so these options were not desirable. + +-------------------------------------------------------------------- +API +-------------------------------------------------------------------- + +Try +--- + +Try is a macro which starts a protected block. It MUST be followed by +a pair of braces or a single protected line (similar to an 'if'), +enclosing the data that is to be protected. It MUST be followed by a +Catch block (don't worry, you'll get compiler errors to let you know if +you mess any of that up). + +Catch(e) +-------- + +Catch is a macro which ends the Try block and starts the error handling +block. The catch block is called if and only if an exception was thrown +while within the Try block. This error was thrown by a Throw call +somewhere within Try (or within a function called within Try, or a function +called by a function called within Try, etc). + +The single parameter 'e' is filled with the error code which was thrown. +This can be used for reporting, conditional cleanup, etc. (or you can just +ignore it if you really want... people ignore return codes all the time, +right?). 'e' should be of type EXCEPTION_T; + +Throw(e) +-------- + +The method of throwing an error. Throws should only occur from within a +protected (Try...Catch) block, though it may easily be nested many function +calls deep without an impact on performance or functionality. Throw takes +a single argument, which is an exception id which will be passed to Catch +as the reason for the error. + +If you wish to Rethrow an error, this can be done by calling Throw(e) with +the error code you just caught. It IS valid to throw from a catch block. + +ExitTry() +--------- + +On rare occasion, you might want to immediately exit your current Try block +but NOT treat this as an error. Don't run the Catch. Just start executing +from after the Catch as if nothing had happened... That's what ExitTry is +for. + +-------------------------------------------------------------------- +CONFIGURATION +-------------------------------------------------------------------- + +CException is a mostly portable library. It has one universal +dependency, and some macros which are required if working in a +multi-tasking environment. + +1. The standard C library setjmp must be available. Since this is part + of the standard library, chances are good that you'll be fine. + +2. If working in a multitasking environment, methods for obtaining an + index into an array of frames and to get the overall number of + id's are required. If the OS supports a method to retrieve Task + ID's, and those Tasks are number 0, 1, 2... you are in an ideal + situation. Otherwise, a more creative mapping function may be + required. Note that this function is likely to be called twice + for each protected block and once during a throw. This is the + only overhead in the system. + +Exception.h +----------------- +By convention, most projects include Exception.h which defines any +further requirements, then calls CException.h to do the gruntwork. All +of these are optional. You could directly include CException.h if +you wanted and just use the defaults provided. + +EXCEPTION_T - Set this to the type you want your exception id's + to be. Defaults to 'unsigned int'. + +EXCEPTION_NONE - Set this to a number which will never be an + exception id in your system. Defaults to 0x5a5a5a5a. + +EXCEPTION_GET_ID - If in a multi-tasking environment, this should be + set to be a call to the function described in #2 above. + Defaults to just return 0 all the time (good for + single tasking environments) + +EXCEPTION_NUM_ID - If in a multi-tasking environment, this should be set + to the number of ID's required (usually the number of + tasks in the system). Defaults to 1 (for single + tasking environments). + +CEXCEPTION_NO_CATCH_HANDLER(id) - This macro can be optionally specified. + It allows you to specify code to be called when a Throw + is made outside of Try...Catch protection. Consider + this the emergency fallback plan for when something has + gone terribly wrong. + +You may also want to include any header files which will commonly be +needed by the rest of your application where it uses exception handling +here. For example, OS header files or exception codes would be useful. + +Finally, there are some hook macros which you can implement to inject +your own target-specific code in particular places. It is a rare instance +where you will need these, but they are here if you need them: + +CEXCEPTION_HOOK_START_TRY - called immediately before the Try block +CEXCEPTION_HOOK_HAPPY_TRY - called immediately after the Try block + if no exception was thrown +CEXCEPTION_HOOK_AFTER_TRY - called immediately after the Try block + OR before an exception is caught +CEXCEPTION_HOOK_START_CATCH - called immediately before the catch + +-------------------------------------------------------------------- +TESTING +-------------------------------------------------------------------- + +If you want to validate that CException works with your tools or that +it works with your custom configuration, you may want to run the test +suite. + +The test suite included makes use of the Unity Test Framework. It will +require a native C compiler. The example makefile uses MinGW's gcc. +Modify the makefile to include the proper paths to tools, then run 'make' +to compile and run the test application. + +C_COMPILER - The C compiler to use to perform the tests +C_LIBS - The path to the C libraries (including setjmp) +UNITY_DIR - The path to the Unity framework (required to run tests) + (get it at http://unity.sourceforge.net) + +-------------------------------------------------------------------- +LICENSE +-------------------------------------------------------------------- + +This software is licensed under the MIT License + +Copyright (c) 2007-2012 Mark VanderVoord + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/CException.h b/lib/CException.h index ace4cd2..32cb617 100644 --- a/lib/CException.h +++ b/lib/CException.h @@ -1,86 +1,110 @@ -#ifndef _CEXCEPTION_H -#define _CEXCEPTION_H - -#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 -//3. Include a header file before CException.h everywhere which defines any of these -//4. Create an Exception.h in your path, and just define EXCEPTION_USE_CONFIG_FILE first - -#ifdef CEXCEPTION_USE_CONFIG_FILE -#include "CExceptionConfig.h" -#endif - -//This is the value to assign when there isn't an exception -#ifndef CEXCEPTION_NONE -#define CEXCEPTION_NONE (0x5A5A5A5A) -#endif - -//This is number of exception stacks to keep track of (one per task) -#ifndef CEXCEPTION_NUM_ID -#define CEXCEPTION_NUM_ID (1) //there is only the one stack by default -#endif - -//This is the method of getting the current exception stack index (0 if only one stack) -#ifndef CEXCEPTION_GET_ID -#define CEXCEPTION_GET_ID (0) //use the first index always because there is only one anyway -#endif - -//The type to use to store the exception values. -#ifndef CEXCEPTION_T -#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; - CEXCEPTION_T volatile Exception; -} CEXCEPTION_FRAME_T; - -//actual root frame storage (only one if single-tasking) -extern volatile CEXCEPTION_FRAME_T CExceptionFrames[]; - -//Try (see C file for explanation) -#define Try \ - { \ - jmp_buf *PrevFrame, NewFrame; \ - unsigned int MY_ID = CEXCEPTION_GET_ID; \ - PrevFrame = CExceptionFrames[CEXCEPTION_GET_ID].pFrame; \ - CExceptionFrames[MY_ID].pFrame = (jmp_buf*)(&NewFrame); \ - CExceptionFrames[MY_ID].Exception = CEXCEPTION_NONE; \ - if (setjmp(NewFrame) == 0) { \ - if (1) - -//Catch (see C file for explanation) -#define Catch(e) \ - else { } \ - CExceptionFrames[MY_ID].Exception = CEXCEPTION_NONE; \ - } \ - else \ - { e = CExceptionFrames[MY_ID].Exception; e=e; } \ - CExceptionFrames[MY_ID].pFrame = PrevFrame; \ - } \ - if (CExceptionFrames[CEXCEPTION_GET_ID].Exception != CEXCEPTION_NONE) - -//Throw an Error -void Throw(CEXCEPTION_T ExceptionID); - -#ifdef __cplusplus -} // extern "C" -#endif - - -#endif // _CEXCEPTION_H +#ifndef _CEXCEPTION_H +#define _CEXCEPTION_H + +#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 +//3. Include a header file before CException.h everywhere which defines any of these +//4. Create an Exception.h in your path, and just define EXCEPTION_USE_CONFIG_FILE first + +#ifdef CEXCEPTION_USE_CONFIG_FILE +#include "CExceptionConfig.h" +#endif + +//This is the value to assign when there isn't an exception +#ifndef CEXCEPTION_NONE +#define CEXCEPTION_NONE (0x5A5A5A5A) +#endif + +//This is number of exception stacks to keep track of (one per task) +#ifndef CEXCEPTION_NUM_ID +#define CEXCEPTION_NUM_ID (1) //there is only the one stack by default +#endif + +//This is the method of getting the current exception stack index (0 if only one stack) +#ifndef CEXCEPTION_GET_ID +#define CEXCEPTION_GET_ID (0) //use the first index always because there is only one anyway +#endif + +//The type to use to store the exception values. +#ifndef CEXCEPTION_T +#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 + +//These hooks allow you to inject custom code into places, particularly useful for saving and restoring additional state +#ifndef CEXCEPTION_HOOK_START_TRY +#define CEXCEPTION_HOOK_START_TRY +#endif +#ifndef CEXCEPTION_HOOK_HAPPY_TRY +#define CEXCEPTION_HOOK_HAPPY_TRY +#endif +#ifndef CEXCEPTION_HOOK_AFTER_TRY +#define CEXCEPTION_HOOK_AFTER_TRY +#endif +#ifndef CEXCEPTION_HOOK_START_CATCH +#define CEXCEPTION_HOOK_START_CATCH +#endif + +//exception frame structures +typedef struct { + jmp_buf* pFrame; + CEXCEPTION_T volatile Exception; +} CEXCEPTION_FRAME_T; + +//actual root frame storage (only one if single-tasking) +extern volatile CEXCEPTION_FRAME_T CExceptionFrames[]; + +//Try (see C file for explanation) +#define Try \ + { \ + jmp_buf *PrevFrame, NewFrame; \ + unsigned int MY_ID = CEXCEPTION_GET_ID; \ + PrevFrame = CExceptionFrames[CEXCEPTION_GET_ID].pFrame; \ + CExceptionFrames[MY_ID].pFrame = (jmp_buf*)(&NewFrame); \ + CExceptionFrames[MY_ID].Exception = CEXCEPTION_NONE; \ + CEXCEPTION_HOOK_START_TRY; \ + if (setjmp(NewFrame) == 0) { \ + if (1) + +//Catch (see C file for explanation) +#define Catch(e) \ + else { } \ + CExceptionFrames[MY_ID].Exception = CEXCEPTION_NONE; \ + CEXCEPTION_HOOK_HAPPY_TRY; \ + } \ + else \ + { \ + e = CExceptionFrames[MY_ID].Exception; \ + (void)e; \ + CEXCEPTION_HOOK_START_CATCH; \ + } \ + CExceptionFrames[MY_ID].pFrame = PrevFrame; \ + CEXCEPTION_HOOK_AFTER_TRY; \ + } \ + if (CExceptionFrames[CEXCEPTION_GET_ID].Exception != CEXCEPTION_NONE) + +//Throw an Error +void Throw(CEXCEPTION_T ExceptionID); + +//Just exit the Try block and skip the Catch. +#define ExitTry() Throw(CEXCEPTION_NONE) + +#ifdef __cplusplus +} // extern "C" +#endif + + +#endif // _CEXCEPTION_H diff --git a/test/TestException.c b/test/TestException.c index f7df408..eedec81 100644 --- a/test/TestException.c +++ b/test/TestException.c @@ -1,342 +1,389 @@ -#include "unity.h" -#include "CException.h" - -volatile int TestingTheFallback; -volatile int TestingTheFallbackId; - -void setUp(void) -{ - CExceptionFrames[0].pFrame = NULL; - TestingTheFallback = 0; -} - -void tearDown(void) -{ -} - -void test_BasicTryDoesNothingIfNoThrow(void) -{ - int i = 0; - CEXCEPTION_T e = 0x5a; - - Try - { - i += 1; - } - Catch(e) - { - TEST_FAIL_MESSAGE("Should Not Enter Catch If Not Thrown"); - } - - //verify that e was untouched - TEST_ASSERT_EQUAL(0x5a, e); - - // verify that i was incremented once - TEST_ASSERT_EQUAL(1, i); -} - -void test_BasicThrowAndCatch(void) -{ - CEXCEPTION_T e; - - Try - { - Throw(0xBE); - TEST_FAIL_MESSAGE("Should Have Thrown An Error") - } - Catch(e) - { - //verify that e has the right data - TEST_ASSERT_EQUAL(0xBE, e); - } - - //verify that e STILL has the right data - TEST_ASSERT_EQUAL(0xBE, e); -} - -void test_BasicThrowAndCatch_WithMiniSyntax(void) -{ - CEXCEPTION_T e; - - //Mini Throw and Catch - Try - Throw(0xEF); - Catch(e) - TEST_ASSERT_EQUAL(0xEF, e); - TEST_ASSERT_EQUAL(0xEF, e); - - //Mini Passthrough - Try - e = 0; - Catch(e) - TEST_FAIL_MESSAGE("I shouldn't be caught because there was no throw"); - - TEST_ASSERT_EQUAL(0, e); -} - -void test_VerifyVolatilesSurviveThrowAndCatch(void) -{ - volatile unsigned int VolVal = 0; - CEXCEPTION_T e; - - Try - { - VolVal = 2; - Throw(0xBF); - TEST_FAIL_MESSAGE("Should Have Thrown An Error") - } - Catch(e) - { - VolVal += 2; - TEST_ASSERT_EQUAL(0xBF, e); - } - - TEST_ASSERT_EQUAL(4, VolVal); - TEST_ASSERT_EQUAL(0xBF, e); -} - -void HappyExceptionThrower(unsigned int ID) -{ - if (ID != 0) - { - Throw(ID); - } -} - -void test_ThrowFromASubFunctionAndCatchInRootFunc(void) -{ - volatile unsigned int ID = 0; - CEXCEPTION_T e; - - Try - { - - HappyExceptionThrower(0xBA); - TEST_FAIL_MESSAGE("Should Have Thrown An Exception"); - } - Catch(e) - { - ID = e; - } - - //verify that I can pass that value to something else - TEST_ASSERT_EQUAL(0xBA, e); - //verify that ID and e have the same value - TEST_ASSERT_EQUAL(ID, e); -} - -void HappyExceptionRethrower(unsigned int ID) -{ - CEXCEPTION_T e; - - Try - { - Throw(ID); - } - Catch(e) - { - switch (e) - { - case 0xBD: - Throw(0xBF); - break; - default: - break; - } - } -} - -void test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc(void) -{ - volatile unsigned int ID = 0; - CEXCEPTION_T e; - - Try - { - HappyExceptionRethrower(0xBD); - TEST_FAIL_MESSAGE("Should Have Rethrown Exception"); - } - Catch(e) - { - ID = 1; - } - - TEST_ASSERT_EQUAL(0xBF, e); - TEST_ASSERT_EQUAL(1, ID); -} - -void test_ThrowAndCatchFromASubFunctionAndNoRethrowToCatchInRootFunc(void) -{ - CEXCEPTION_T e = 3; - - Try - { - HappyExceptionRethrower(0xBF); - } - Catch(e) - { - TEST_FAIL_MESSAGE("Should Not Have Re-thrown Error (it should have already been caught)"); - } - - //verify that THIS e is still untouched, even though subfunction was touched - TEST_ASSERT_EQUAL(3, e); -} - -void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThisDoesntCorruptExceptionId(void) -{ - CEXCEPTION_T e; - - Try - { - HappyExceptionThrower(0xBF); - TEST_FAIL_MESSAGE("Should Have Thrown Exception"); - } - Catch(e) - { - TEST_ASSERT_EQUAL(0xBF, e); - HappyExceptionRethrower(0x12); - TEST_ASSERT_EQUAL(0xBF, e); - } - TEST_ASSERT_EQUAL(0xBF, e); -} - -void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThatEachExceptionIdIndependent(void) -{ - CEXCEPTION_T e1, e2; - - Try - { - HappyExceptionThrower(0xBF); - TEST_FAIL_MESSAGE("Should Have Thrown Exception"); - } - Catch(e1) - { - TEST_ASSERT_EQUAL(0xBF, e1); - Try - { - HappyExceptionThrower(0x12); - } - Catch(e2) - { - TEST_ASSERT_EQUAL(0x12, e2); - } - TEST_ASSERT_EQUAL(0x12, e2); - TEST_ASSERT_EQUAL(0xBF, e1); - } - TEST_ASSERT_EQUAL(0x12, e2); - TEST_ASSERT_EQUAL(0xBF, e1); -} - -void test_CanHaveMultipleTryBlocksInASingleFunction(void) -{ - CEXCEPTION_T e; - - Try - { - HappyExceptionThrower(0x01); - TEST_FAIL_MESSAGE("Should Have Thrown Exception"); - } - Catch(e) - { - TEST_ASSERT_EQUAL(0x01, e); - } - - Try - { - HappyExceptionThrower(0xF0); - TEST_FAIL_MESSAGE("Should Have Thrown Exception"); - } - Catch(e) - { - TEST_ASSERT_EQUAL(0xF0, e); - } -} - -void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void) -{ - int i = 0; - CEXCEPTION_T e; - - Try - { - Try - { - HappyExceptionThrower(0x01); - i = 1; - TEST_FAIL_MESSAGE("Should Have Rethrown Exception"); - } - Catch(e) - { - TEST_ASSERT_EQUAL(0x01, e); - } - } - Catch(e) - { - TEST_FAIL_MESSAGE("Should Have Been Caught By Inside Catch"); - } - - // verify that i is still zero - TEST_ASSERT_EQUAL(0, i); -} - -void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void) -{ - int i = 0; - CEXCEPTION_T e; - - Try - { - Try - { - i = 2; - } - Catch(e) - { - TEST_FAIL_MESSAGE("Should NotBe Caught Here"); - } - HappyExceptionThrower(0x01); - TEST_FAIL_MESSAGE("Should Have Rethrown Exception"); - } - Catch(e) - { - TEST_ASSERT_EQUAL(0x01, e); - } - - // verify that i is 2 - TEST_ASSERT_EQUAL(2, i); -} - -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); - TEST_ASSERT_EQUAL(0xBE, TestingTheFallbackId); -} - -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); - TEST_ASSERT_EQUAL(0xBE, TestingTheFallbackId); -} +#include "unity.h" +#include "CException.h" + +volatile int TestingTheFallback; +volatile int TestingTheFallbackId; + +void setUp(void) +{ + CExceptionFrames[0].pFrame = NULL; + TestingTheFallback = 0; +} + +void tearDown(void) +{ +} + +void test_BasicTryDoesNothingIfNoThrow(void) +{ + int i = 0; + CEXCEPTION_T e = 0x5a; + + Try + { + i += 1; + } + Catch(e) + { + TEST_FAIL_MESSAGE("Should Not Enter Catch If Not Thrown"); + } + + //verify that e was untouched + TEST_ASSERT_EQUAL(0x5a, e); + + // verify that i was incremented once + TEST_ASSERT_EQUAL(1, i); +} + +void test_BasicThrowAndCatch(void) +{ + CEXCEPTION_T e; + + Try + { + Throw(0xBE); + TEST_FAIL_MESSAGE("Should Have Thrown An Error"); + } + Catch(e) + { + //verify that e has the right data + TEST_ASSERT_EQUAL(0xBE, e); + } + + //verify that e STILL has the right data + TEST_ASSERT_EQUAL(0xBE, e); +} + +void test_BasicThrowAndCatch_WithMiniSyntax(void) +{ + CEXCEPTION_T e; + + //Mini Throw and Catch + Try + Throw(0xEF); + Catch(e) + TEST_ASSERT_EQUAL(0xEF, e); + TEST_ASSERT_EQUAL(0xEF, e); + + //Mini Passthrough + Try + e = 0; + Catch(e) + TEST_FAIL_MESSAGE("I shouldn't be caught because there was no throw"); + + TEST_ASSERT_EQUAL(0, e); +} + +void test_VerifyVolatilesSurviveThrowAndCatch(void) +{ + volatile unsigned int VolVal = 0; + CEXCEPTION_T e; + + Try + { + VolVal = 2; + Throw(0xBF); + TEST_FAIL_MESSAGE("Should Have Thrown An Error"); + } + Catch(e) + { + VolVal += 2; + TEST_ASSERT_EQUAL(0xBF, e); + } + + TEST_ASSERT_EQUAL(4, VolVal); + TEST_ASSERT_EQUAL(0xBF, e); +} + +void HappyExceptionThrower(unsigned int ID) +{ + if (ID != 0) + { + Throw(ID); + } +} + +void test_ThrowFromASubFunctionAndCatchInRootFunc(void) +{ + volatile unsigned int ID = 0; + CEXCEPTION_T e; + + Try + { + + HappyExceptionThrower(0xBA); + TEST_FAIL_MESSAGE("Should Have Thrown An Exception"); + } + Catch(e) + { + ID = e; + } + + //verify that I can pass that value to something else + TEST_ASSERT_EQUAL(0xBA, e); + //verify that ID and e have the same value + TEST_ASSERT_EQUAL(ID, e); +} + +void HappyExceptionRethrower(unsigned int ID) +{ + CEXCEPTION_T e; + + Try + { + Throw(ID); + } + Catch(e) + { + switch (e) + { + case 0xBD: + Throw(0xBF); + break; + default: + break; + } + } +} + +void test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc(void) +{ + volatile unsigned int ID = 0; + CEXCEPTION_T e; + + Try + { + HappyExceptionRethrower(0xBD); + TEST_FAIL_MESSAGE("Should Have Rethrown Exception"); + } + Catch(e) + { + ID = 1; + } + + TEST_ASSERT_EQUAL(0xBF, e); + TEST_ASSERT_EQUAL(1, ID); +} + +void test_ThrowAndCatchFromASubFunctionAndNoRethrowToCatchInRootFunc(void) +{ + CEXCEPTION_T e = 3; + + Try + { + HappyExceptionRethrower(0xBF); + } + Catch(e) + { + TEST_FAIL_MESSAGE("Should Not Have Re-thrown Error (it should have already been caught)"); + } + + //verify that THIS e is still untouched, even though subfunction was touched + TEST_ASSERT_EQUAL(3, e); +} + +void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThisDoesntCorruptExceptionId(void) +{ + CEXCEPTION_T e; + + Try + { + HappyExceptionThrower(0xBF); + TEST_FAIL_MESSAGE("Should Have Thrown Exception"); + } + Catch(e) + { + TEST_ASSERT_EQUAL(0xBF, e); + HappyExceptionRethrower(0x12); + TEST_ASSERT_EQUAL(0xBF, e); + } + TEST_ASSERT_EQUAL(0xBF, e); +} + +void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThatEachExceptionIdIndependent(void) +{ + CEXCEPTION_T e1, e2; + + Try + { + HappyExceptionThrower(0xBF); + TEST_FAIL_MESSAGE("Should Have Thrown Exception"); + } + Catch(e1) + { + TEST_ASSERT_EQUAL(0xBF, e1); + Try + { + HappyExceptionThrower(0x12); + } + Catch(e2) + { + TEST_ASSERT_EQUAL(0x12, e2); + } + TEST_ASSERT_EQUAL(0x12, e2); + TEST_ASSERT_EQUAL(0xBF, e1); + } + TEST_ASSERT_EQUAL(0x12, e2); + TEST_ASSERT_EQUAL(0xBF, e1); +} + +void test_CanHaveMultipleTryBlocksInASingleFunction(void) +{ + CEXCEPTION_T e; + + Try + { + HappyExceptionThrower(0x01); + TEST_FAIL_MESSAGE("Should Have Thrown Exception"); + } + Catch(e) + { + TEST_ASSERT_EQUAL(0x01, e); + } + + Try + { + HappyExceptionThrower(0xF0); + TEST_FAIL_MESSAGE("Should Have Thrown Exception"); + } + Catch(e) + { + TEST_ASSERT_EQUAL(0xF0, e); + } +} + +void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void) +{ + int i = 0; + CEXCEPTION_T e; + + Try + { + Try + { + HappyExceptionThrower(0x01); + i = 1; + TEST_FAIL_MESSAGE("Should Have Rethrown Exception"); + } + Catch(e) + { + TEST_ASSERT_EQUAL(0x01, e); + } + } + Catch(e) + { + TEST_FAIL_MESSAGE("Should Have Been Caught By Inside Catch"); + } + + // verify that i is still zero + TEST_ASSERT_EQUAL(0, i); +} + +void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void) +{ + int i = 0; + CEXCEPTION_T e; + + Try + { + Try + { + i = 2; + } + Catch(e) + { + TEST_FAIL_MESSAGE("Should Not Be Caught Here"); + } + HappyExceptionThrower(0x01); + TEST_FAIL_MESSAGE("Should Have Rethrown Exception"); + } + Catch(e) + { + TEST_ASSERT_EQUAL(0x01, e); + } + + // verify that i is 2 + TEST_ASSERT_EQUAL(2, i); +} + +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); + TEST_ASSERT_EQUAL(0xBE, TestingTheFallbackId); +} + +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); + TEST_ASSERT_EQUAL(0xBE, TestingTheFallbackId); +} + +void test_AbilityToExitTryWithoutThrowingAnError(void) +{ + int i=0; + CEXCEPTION_T e; + + Try + { + ExitTry(); + TEST_FAIL_MESSAGE("Should Have Exited Try Before This"); + } + Catch(e) + { + TEST_FAIL_MESSAGE("Should Not Have Been Caught"); + } + + // verify that i is still zero + TEST_ASSERT_EQUAL(0, i); +} + +void test_AbilityToExitTryWillOnlyExitOneLevel(void) +{ + int i=0; + CEXCEPTION_T e; + CEXCEPTION_T e2; + + Try + { + Try + { + ExitTry(); + TEST_FAIL_MESSAGE("Should Have Exited Try Before This"); + } + Catch(e) + { + TEST_FAIL_MESSAGE("Should Not Have Been Caught By Inside"); + } + i = 1; + } + Catch(e2) + { + TEST_FAIL_MESSAGE("Should Not Have Been Caught By Outside"); + } + + // verify that we picked up and ran after first Try + TEST_ASSERT_EQUAL(1, i); +} diff --git a/test/TestException_Runner.c b/test/TestException_Runner.c index bdc23d8..f1eb483 100644 --- a/test/TestException_Runner.c +++ b/test/TestException_Runner.c @@ -40,21 +40,13 @@ extern void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void); extern void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void); extern void test_AThrowWithoutATryCatchWillUseDefaultHandlerIfSpecified(void); extern void test_AThrowWithoutOutsideATryCatchWillUseDefaultHandlerEvenAfterTryCatch(void); - - -//=======Test Reset Option===== -void resetTest() -{ - tearDown(); - setUp(); -} - +extern void test_AbilityToExitTryWithoutThrowingAnError(void); +extern void test_AbilityToExitTryWillOnlyExitOneLevel(void); //=======MAIN===== int main(void) { - Unity.TestFile = "test/TestException.c"; - UnityBegin(); + UnityBegin("TestException.c"); RUN_TEST(test_BasicTryDoesNothingIfNoThrow, 16); RUN_TEST(test_BasicThrowAndCatch, 37); RUN_TEST(test_BasicThrowAndCatch_WithMiniSyntax, 56); @@ -69,6 +61,7 @@ int main(void) RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside, 281); RUN_TEST(test_AThrowWithoutATryCatchWillUseDefaultHandlerIfSpecified, 308); RUN_TEST(test_AThrowWithoutOutsideATryCatchWillUseDefaultHandlerEvenAfterTryCatch, 319); - + RUN_TEST(test_AbilityToExitTryWithoutThrowingAnError, 344); + RUN_TEST(test_AbilityToExitTryWillOnlyExitOneLevel, 363); return (UnityEnd()); } diff --git a/vendor/unity b/vendor/unity index 47a778d..bcf6515 160000 --- a/vendor/unity +++ b/vendor/unity @@ -1 +1 @@ -Subproject commit 47a778d606fbfabde459b0f725a67b18c1bce6ee +Subproject commit bcf6515329f4ce1062356a684ed1b6a9f05f5a0d