mirror of
https://github.com/throwtheswitch/cexception.git
synced 2026-08-03 01:37:51 +00:00
Added ExitTry option to abort a Try block without an error.
Added Hooks for injecting code into blocks if required for a target. Updated documentation to match new features. Fixed test bugs Pulled in latest Unity
This commit is contained in:
Binary file not shown.
Binary file not shown.
+261
-242
@@ -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.
|
||||
|
||||
+110
-86
@@ -1,86 +1,110 @@
|
||||
#ifndef _CEXCEPTION_H
|
||||
#define _CEXCEPTION_H
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
#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 <setjmp.h>
|
||||
|
||||
#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
|
||||
|
||||
+389
-342
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
Vendored
+1
-1
Submodule vendor/unity updated: 47a778d606...bcf6515329
Reference in New Issue
Block a user