mirror of
https://github.com/throwtheswitch/cexception.git
synced 2026-06-05 21:24:36 +00:00
switched format of catches to pass in a variable to fill, more like C++.
git-svn-id: http://cexception.svn.sourceforge.net/svnroot/cexception/trunk@5 50f63946-2846-0410-8d77-f904c773002e
This commit is contained in:
+78
-84
@@ -12,6 +12,29 @@ 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
|
||||
|
||||
--------------------------------------------------------------------
|
||||
@@ -35,22 +58,19 @@ 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. In addition, an optional details field can be
|
||||
included, allowing a pointer or additional numerical information to be
|
||||
added.
|
||||
from the Catch block.
|
||||
|
||||
Throws can occur from within function calls or directly within the
|
||||
function itself.
|
||||
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 as
|
||||
provided by many other operating systems. Because of this, there are
|
||||
a number of limitations that should be observed in order to
|
||||
successfully utilize this library:
|
||||
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.
|
||||
@@ -62,11 +82,11 @@ successfully utilize this library:
|
||||
Gotos and returns would bypass some of these steps, resulting in
|
||||
memory leaks or unpredictable behavior.
|
||||
|
||||
2. If you change stack variables within your Try block (local variables
|
||||
for example), and 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.
|
||||
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?
|
||||
|
||||
@@ -74,17 +94,7 @@ successfully utilize this library:
|
||||
memory location was updated and not just a register unless the
|
||||
variable is marked volatile.
|
||||
|
||||
3. While EXCEPTION_ID and EXCEPTION_DETAIL are available outside a
|
||||
Catch, it is good practice to ONLY access them from within the
|
||||
Catch block.
|
||||
|
||||
Why?
|
||||
|
||||
EXCEPTION_ID is altered whenever a new Try block is encountered.
|
||||
Both are obviously altered on Throws. It's best to isolate calls
|
||||
to these to places where your state is well known.
|
||||
|
||||
4. Memory which is malloc'd or new'd is not automatically released
|
||||
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.
|
||||
@@ -95,74 +105,44 @@ successfully utilize this library:
|
||||
replacing or wrapping malloc calls or something like that. This
|
||||
is a light framework, so these options were not desirable.
|
||||
|
||||
5. If additional Try blocks are started from within a Catch (either
|
||||
in the immediate function or from within a function called from
|
||||
the Catch block), it will alter the EXCEPTION_ID. It is therefore
|
||||
good practice to NOT call such functions from a Catch block.
|
||||
|
||||
Why?
|
||||
|
||||
See #3 above.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
API
|
||||
--------------------------------------------------------------------
|
||||
|
||||
void Exception_Init(void);
|
||||
|
||||
Try
|
||||
---
|
||||
|
||||
Try is a macro which starts a protected block. It MUST be followed by
|
||||
a pair of braces, enclosing the data that is to be protected. It MUST
|
||||
be followed by a Catch block.
|
||||
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
|
||||
-----
|
||||
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 could have been thrown by a
|
||||
call to Throw or ThrowDetailed.
|
||||
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).
|
||||
|
||||
EXCEPTION_ID
|
||||
------------
|
||||
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;
|
||||
|
||||
EXCEPTION_ID is a macro which returns the ID of the exception which was
|
||||
passed to Throw or ThrowDetailed.
|
||||
|
||||
EXCEPTION_DETAILS
|
||||
----------------
|
||||
|
||||
EXCEPTION_DETAILS is a macro which returns the detail int of the exception.
|
||||
If Throw was called, it will contain the line number of the Throw. If
|
||||
ThrowDetailed was called, it will contain whatever detail information was
|
||||
provided.
|
||||
|
||||
Throw
|
||||
-----
|
||||
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. Try takes
|
||||
a single argument, which is an exception id to be used in Catch as the
|
||||
reason for the error.
|
||||
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.
|
||||
|
||||
ThrowDetailed
|
||||
-------------
|
||||
|
||||
Operates identically to Throw, with the added ability to send additional
|
||||
details. This detail is a single int, which can represent whatever the
|
||||
developer desires. This has been used as a pointer to an error message,
|
||||
pointer to data which must be freed, an instance id of a problem, etc.
|
||||
|
||||
Rethrow
|
||||
-------
|
||||
Rethrow can be used in a Catch block and allows the current error to be
|
||||
pushed back an additional level of protected blocks. Because protected
|
||||
blocks can be nested, it is sometimes useful to handle some errors in
|
||||
inner blocks while throwing the rest to outside blocks. This function
|
||||
does that, allowing the error to be passed on unchanged.
|
||||
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
|
||||
@@ -172,7 +152,8 @@ 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
|
||||
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
|
||||
@@ -180,22 +161,31 @@ multi-tasking environment.
|
||||
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. This is the only overhead in the system.
|
||||
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.
|
||||
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_NONE - set to a number which will never be an exception id in
|
||||
your system.
|
||||
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)
|
||||
tasks in the system). Defaults to 1 (for single
|
||||
tasking environments).
|
||||
|
||||
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
|
||||
@@ -205,9 +195,13 @@ 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 using MinGW's gcc.
|
||||
Modify the makefile to include the proper paths to tools, then run make
|
||||
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
|
||||
|
||||
+28
-19
@@ -3,36 +3,45 @@
|
||||
volatile EXCEPTION_FRAME_T ExceptionFrames[EXCEPTION_NUM_ID];
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// ThrowDetailed
|
||||
// Throw
|
||||
//------------------------------------------------------------------------------------------
|
||||
//
|
||||
// PARAMETERS: None
|
||||
//
|
||||
// DESCRIPTION: throws a software exception with details
|
||||
// DESCRIPTION: throws a software exception
|
||||
//
|
||||
// RETURNS: None
|
||||
//
|
||||
//------------------------------------------------------------------------------------------
|
||||
void ThrowDetailed(unsigned int ExceptionID, unsigned int Details)
|
||||
void Throw(EXCEPTION_T ExceptionID)
|
||||
{
|
||||
MY_FRAME.Exception = ExceptionID;
|
||||
MY_FRAME.Details = Details;
|
||||
longjmp(*MY_FRAME.pFrame, 1);
|
||||
unsigned int MY_ID = EXCEPTION_GET_ID;
|
||||
ExceptionFrames[MY_ID].Exception = ExceptionID;
|
||||
longjmp(*ExceptionFrames[MY_ID].pFrame, 1);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// Rethrow
|
||||
// Explaination of what it's all for:
|
||||
//------------------------------------------------------------------------------------------
|
||||
//
|
||||
// PARAMETERS: None
|
||||
//
|
||||
// DESCRIPTION: rethrows a software exception that has already been thrown
|
||||
//
|
||||
// RETURNS: None
|
||||
//
|
||||
//------------------------------------------------------------------------------------------
|
||||
void Rethrow()
|
||||
{
|
||||
longjmp(*MY_FRAME.pFrame, 1);
|
||||
}
|
||||
/*
|
||||
#define Try
|
||||
{ <- give us some local scope. most compilers are happy with this
|
||||
jmp_buf *PrevFrame, NewFrame; <- prev frame points to the last try block's frame. new frame gets created on stack for this Try block
|
||||
unsigned int MY_ID = EXCEPTION_GET_ID; <- look up this task's id for use in frame array. always 0 if single-tasking
|
||||
PrevFrame = ExceptionFrames[EXCEPTION_GET_ID].pFrame; <- set pointer to point at old frame (which array is currently pointing at)
|
||||
ExceptionFrames[MY_ID].pFrame = &NewFrame; <- set array to point at my new frame instead, now
|
||||
ExceptionFrames[MY_ID].Exception = EXCEPTION_NONE; <- initialize my exception id to be NONE
|
||||
if (setjmp(NewFrame) == 0) { <- do setjmp. it returns 1 if longjump called, otherwise 0
|
||||
if (&PrevFrame) <- this is here to force proper scoping. it requires braces or a single line to be but after Try, otherwise won't compile. This is always true at this point.
|
||||
|
||||
#define Catch(e)
|
||||
else { } <- this also forces proper scoping. Without this they could stick their own 'else' in and it would get ugly
|
||||
ExceptionFrames[MY_ID].Exception = EXCEPTION_NONE; <- no errors happened, so just set the exception id to NONE (in case it was corrupted)
|
||||
}
|
||||
else <- an exception occurred
|
||||
{ e = ExceptionFrames[MY_ID].Exception; e=e;} <- assign the caught exception id to the variable passed in.
|
||||
ExceptionFrames[MY_ID].pFrame = PrevFrame; <- make the pointer in the array point at the previous frame again, as if NewFrame never existed.
|
||||
} <- finish off that local scope we created to have our own variables
|
||||
if (ExceptionFrames[EXCEPTION_GET_ID].Exception != EXCEPTION_NONE) <- start the actual 'catch' processing if we have an exception id saved away
|
||||
*/
|
||||
|
||||
|
||||
+21
-19
@@ -15,40 +15,42 @@
|
||||
#define EXCEPTION_GET_ID (0) //use the first index always because there is only one anyway
|
||||
#endif
|
||||
|
||||
#ifndef EXCEPTION_T
|
||||
#define EXCEPTION_T unsigned int
|
||||
#endif
|
||||
|
||||
//exception frame structures
|
||||
typedef struct {
|
||||
jmp_buf* pFrame;
|
||||
volatile unsigned int Exception;
|
||||
volatile unsigned int Details;
|
||||
volatile EXCEPTION_T Exception;
|
||||
} EXCEPTION_FRAME_T;
|
||||
|
||||
//actual root frame storage (only one if single-tasking)
|
||||
extern volatile EXCEPTION_FRAME_T ExceptionFrames[];
|
||||
|
||||
#define MY_FRAME (ExceptionFrames[EXCEPTION_GET_ID])
|
||||
#define MY_FRAME_FAST (ExceptionFrames[MY_ID])
|
||||
#define EXCEPTION_ID (MY_FRAME.Exception)
|
||||
#define EXCEPTION_DETAILS (MY_FRAME.Details)
|
||||
|
||||
//Try (see C file for explanation)
|
||||
#define Try \
|
||||
{ \
|
||||
jmp_buf *PrevFrame, NewFrame; \
|
||||
unsigned int MY_ID = EXCEPTION_GET_ID; \
|
||||
PrevFrame = MY_FRAME.pFrame; \
|
||||
MY_FRAME_FAST.pFrame = &NewFrame; \
|
||||
MY_FRAME_FAST.Details = 0; \
|
||||
MY_FRAME_FAST.Exception = EXCEPTION_NONE; \
|
||||
PrevFrame = ExceptionFrames[EXCEPTION_GET_ID].pFrame; \
|
||||
ExceptionFrames[MY_ID].pFrame = &NewFrame; \
|
||||
ExceptionFrames[MY_ID].Exception = EXCEPTION_NONE; \
|
||||
if (setjmp(NewFrame) == 0) { \
|
||||
if (&PrevFrame)
|
||||
if (&PrevFrame)
|
||||
|
||||
#define Catch \
|
||||
//Catch (see C file for explanation)
|
||||
#define Catch(e) \
|
||||
else { } \
|
||||
MY_FRAME_FAST.Exception = EXCEPTION_NONE; \
|
||||
ExceptionFrames[MY_ID].Exception = EXCEPTION_NONE; \
|
||||
} \
|
||||
MY_FRAME_FAST.pFrame = PrevFrame; \
|
||||
else \
|
||||
{ e = ExceptionFrames[MY_ID].Exception; e=e; } \
|
||||
ExceptionFrames[MY_ID].pFrame = PrevFrame; \
|
||||
} \
|
||||
if (MY_FRAME.Exception != EXCEPTION_NONE)
|
||||
if (ExceptionFrames[EXCEPTION_GET_ID].Exception != EXCEPTION_NONE)
|
||||
|
||||
#define Throw(id) ThrowDetailed(id, __LINE__)
|
||||
void ThrowDetailed(unsigned int ExceptionID, unsigned int Details);
|
||||
void Rethrow(void);
|
||||
//Throw an Error
|
||||
void Throw(EXCEPTION_T ExceptionID);
|
||||
|
||||
#endif // _CEXCEPTION_H
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#ifndef _EXCEPTION_H
|
||||
#define _EXCEPTION_H
|
||||
|
||||
// Define the reserved value representing NO EXCEPTION
|
||||
#define EXCEPTION_NONE (0x5A5A5A5A)
|
||||
//Optionally define the exception type (something like an int which can be directly assigned)
|
||||
#define EXCEPTION_T int
|
||||
|
||||
// Optionally define the reserved value representing NO EXCEPTION
|
||||
#define EXCEPTION_NONE (1234)
|
||||
|
||||
// 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
|
||||
@@ -13,11 +16,15 @@
|
||||
// For example, Quadros might include the following implementation:
|
||||
#ifndef TEST
|
||||
#include "OSAPI.h"
|
||||
#define EXCEPTION_GET_ID() (KS_GetTaskID())
|
||||
#define EXCEPTION_GET_ID (KS_GetTaskID())
|
||||
#define EXCEPTION_NUM_ID (NTASKS + 1)
|
||||
#endif
|
||||
|
||||
// INCLUDE THE ACTUAL CEXCEPTION LIBRARY
|
||||
#include "CException.h"
|
||||
|
||||
//This could be a good place to define/include some error ID's:
|
||||
#define ERROR_ID_EVERYTHING_IS_BROKEN (0x88)
|
||||
#define ERROR_ID_ONLY_THIS_IS_BROKEN (0x77)
|
||||
|
||||
#endif // _EXCEPTION_H
|
||||
+123
-50
@@ -12,36 +12,64 @@ void tearDown(void)
|
||||
void test_BasicTryDoesNothingIfNoThrow(void)
|
||||
{
|
||||
int i;
|
||||
EXCEPTION_T e = 0x5a5a;
|
||||
|
||||
Try
|
||||
{
|
||||
i += 1;
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
TEST_FAIL("Should Not Enter Catch If Not Thrown")
|
||||
}
|
||||
|
||||
//verify that e was untouched
|
||||
TEST_ASSERT_EQUAL(0x5a5a, e);
|
||||
}
|
||||
|
||||
void test_BasicThrowAndCatch(void)
|
||||
{
|
||||
volatile unsigned int ID = 0;
|
||||
EXCEPTION_T e;
|
||||
|
||||
Try
|
||||
{
|
||||
Throw(0xBEEFBEEF);
|
||||
TEST_FAIL("Should Have Thrown An Error")
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
ID = EXCEPTION_ID;
|
||||
//verify that e has the right data
|
||||
TEST_ASSERT_EQUAL(0xBEEFBEEF, e)
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(0xBEEFBEEF, ID);
|
||||
//verify that e STILL has the right data
|
||||
TEST_ASSERT_EQUAL(0xBEEFBEEF, e);
|
||||
}
|
||||
|
||||
void test_BasicThrowAndCatch_WithMiniSyntax(void)
|
||||
{
|
||||
EXCEPTION_T e;
|
||||
|
||||
//Mini Throw and Catch
|
||||
Try
|
||||
Throw(0xBEEFBEEF);
|
||||
Catch(e)
|
||||
TEST_ASSERT_EQUAL(0xBEEFBEEF, e);
|
||||
TEST_ASSERT_EQUAL(0xBEEFBEEF, e);
|
||||
|
||||
//Mini Passthrough
|
||||
Try
|
||||
e = 0;
|
||||
Catch(e)
|
||||
TEST_FAIL("I shouldn't be caught because there was no throw");
|
||||
|
||||
TEST_ASSERT_EQUAL(0, e);
|
||||
}
|
||||
|
||||
void test_VerifyVolatilesSurviveThrowAndCatch(void)
|
||||
{
|
||||
volatile unsigned int VolVal = 0;
|
||||
EXCEPTION_T e;
|
||||
|
||||
Try
|
||||
{
|
||||
@@ -49,24 +77,28 @@ void test_VerifyVolatilesSurviveThrowAndCatch(void)
|
||||
Throw(0xBEEFBEEF);
|
||||
TEST_FAIL("Should Have Thrown An Error")
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
VolVal += 2;
|
||||
TEST_ASSERT_EQUAL(0xBEEFBEEF, EXCEPTION_ID);
|
||||
TEST_ASSERT_EQUAL(0xBEEFBEEF, e);
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(4, VolVal);
|
||||
TEST_ASSERT_EQUAL(0xBEEFBEEF, e);
|
||||
}
|
||||
|
||||
void HappyExceptionThrower(unsigned int ID)
|
||||
{
|
||||
if (ID != 0)
|
||||
Throw(ID);
|
||||
{
|
||||
Throw(ID);
|
||||
}
|
||||
}
|
||||
|
||||
void test_ThrowFromASubFunctionAndCatchInRootFunc(void)
|
||||
{
|
||||
volatile unsigned int ID = 0;
|
||||
EXCEPTION_T e;
|
||||
|
||||
Try
|
||||
{
|
||||
@@ -74,26 +106,29 @@ void test_ThrowFromASubFunctionAndCatchInRootFunc(void)
|
||||
HappyExceptionThrower(0xBADDF00D);
|
||||
TEST_FAIL("Should Have Thrown An Exception");
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
ID = EXCEPTION_ID;
|
||||
ID = e;
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(0xBADDF00D, ID);
|
||||
//verify that I can pass that value to something else
|
||||
TEST_ASSERT_EQUAL(0xBADDF00D, e);
|
||||
}
|
||||
|
||||
void HappyExceptionRethrower(unsigned int ID)
|
||||
{
|
||||
EXCEPTION_T e;
|
||||
|
||||
Try
|
||||
{
|
||||
Throw(ID);
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
switch (EXCEPTION_ID)
|
||||
switch (e)
|
||||
{
|
||||
case 0xBADDF00D:
|
||||
Rethrow();
|
||||
Throw(0xBADDBEEF);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -104,41 +139,96 @@ void HappyExceptionRethrower(unsigned int ID)
|
||||
void test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc(void)
|
||||
{
|
||||
volatile unsigned int ID = 0;
|
||||
EXCEPTION_T e;
|
||||
|
||||
Try
|
||||
{
|
||||
HappyExceptionRethrower(0xBADDF00D);
|
||||
TEST_FAIL("Should Have Rethrown Exception");
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
ID = EXCEPTION_ID;
|
||||
ID = 1;
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(0xBADDF00D, ID);
|
||||
TEST_ASSERT_EQUAL(0xBADDBEEF, e);
|
||||
TEST_ASSERT_EQUAL(1, ID);
|
||||
}
|
||||
|
||||
void test_ThrowAndCatchFromASubFunctionAndNoRethrowToCatchInRootFunc(void)
|
||||
{
|
||||
EXCEPTION_T e = 3;
|
||||
|
||||
Try
|
||||
{
|
||||
HappyExceptionRethrower(0xBADDBEEF);
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
TEST_FAIL("Should Not Have Thrown Error");
|
||||
TEST_FAIL("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)
|
||||
{
|
||||
EXCEPTION_T e;
|
||||
|
||||
Try
|
||||
{
|
||||
HappyExceptionThrower(0xBADDBEEF);
|
||||
TEST_FAIL("Should Have Thrown Exception");
|
||||
}
|
||||
Catch(e)
|
||||
{
|
||||
TEST_ASSERT_EQUAL(0xBADDBEEF, e);
|
||||
HappyExceptionRethrower(0x12345678);
|
||||
TEST_ASSERT_EQUAL(0xBADDBEEF, e);
|
||||
}
|
||||
TEST_ASSERT_EQUAL(0xBADDBEEF, e);
|
||||
}
|
||||
|
||||
void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThatEachExceptionIdIndependent(void)
|
||||
{
|
||||
EXCEPTION_T e1, e2;
|
||||
|
||||
Try
|
||||
{
|
||||
HappyExceptionThrower(0xBADDBEEF);
|
||||
TEST_FAIL("Should Have Thrown Exception");
|
||||
}
|
||||
Catch(e1)
|
||||
{
|
||||
TEST_ASSERT_EQUAL(0xBADDBEEF, e1);
|
||||
Try
|
||||
{
|
||||
HappyExceptionThrower(0x12345678);
|
||||
}
|
||||
Catch(e2)
|
||||
{
|
||||
TEST_ASSERT_EQUAL(0x12345678, e2);
|
||||
}
|
||||
TEST_ASSERT_EQUAL(0x12345678, e2);
|
||||
TEST_ASSERT_EQUAL(0xBADDBEEF, e1);
|
||||
}
|
||||
TEST_ASSERT_EQUAL(0x12345678, e2);
|
||||
TEST_ASSERT_EQUAL(0xBADDBEEF, e1);
|
||||
}
|
||||
|
||||
void test_CanHaveMultipleTryBlocksInASingleFunction(void)
|
||||
{
|
||||
EXCEPTION_T e;
|
||||
|
||||
Try
|
||||
{
|
||||
HappyExceptionThrower(0x01234567);
|
||||
TEST_FAIL("Should Have Thrown Exception");
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
TEST_ASSERT_EQUAL(0x01234567, EXCEPTION_ID);
|
||||
TEST_ASSERT_EQUAL(0x01234567, e);
|
||||
}
|
||||
|
||||
Try
|
||||
@@ -146,15 +236,17 @@ void test_CanHaveMultipleTryBlocksInASingleFunction(void)
|
||||
HappyExceptionThrower(0xF00D8888);
|
||||
TEST_FAIL("Should Have Thrown Exception");
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
TEST_ASSERT_EQUAL(0xF00D8888, EXCEPTION_ID);
|
||||
TEST_ASSERT_EQUAL(0xF00D8888, e);
|
||||
}
|
||||
}
|
||||
|
||||
void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void)
|
||||
{
|
||||
int i = 0;
|
||||
EXCEPTION_T e;
|
||||
|
||||
Try
|
||||
{
|
||||
Try
|
||||
@@ -163,12 +255,12 @@ void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void)
|
||||
i = 1;
|
||||
TEST_FAIL("Should Have Rethrown Exception");
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
TEST_ASSERT_EQUAL(0x01234567, EXCEPTION_ID);
|
||||
TEST_ASSERT_EQUAL(0x01234567, e);
|
||||
}
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
TEST_FAIL("Should Have Been Caught By Inside Catch");
|
||||
}
|
||||
@@ -177,42 +269,23 @@ void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void)
|
||||
void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void)
|
||||
{
|
||||
int i = 0;
|
||||
EXCEPTION_T e;
|
||||
|
||||
Try
|
||||
{
|
||||
Try
|
||||
{
|
||||
i = 2;
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
TEST_FAIL("Should NotBe Caught Here");
|
||||
}
|
||||
HappyExceptionThrower(0x01234567);
|
||||
TEST_FAIL("Should Have Rethrown Exception");
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
TEST_ASSERT_EQUAL(0x01234567, EXCEPTION_ID);
|
||||
TEST_ASSERT_EQUAL(0x01234567, e);
|
||||
}
|
||||
}
|
||||
|
||||
void HappyDetailedExceptionThrower(unsigned int ID, unsigned int Details)
|
||||
{
|
||||
if (ID != 0)
|
||||
ThrowDetailed(ID, Details);
|
||||
}
|
||||
|
||||
void test_CanThrowADetailedExceptionAndCheckOutTheResults(void)
|
||||
{
|
||||
Try
|
||||
{
|
||||
HappyDetailedExceptionThrower(0x12345678, 0x90ABCDEF);
|
||||
TEST_FAIL("Should Have Thrown An Exception");
|
||||
}
|
||||
Catch
|
||||
{
|
||||
TEST_ASSERT_EQUAL(0x12345678, EXCEPTION_ID);
|
||||
TEST_ASSERT_EQUAL(0x90ABCDEF, EXCEPTION_DETAILS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ extern void tearDown(void);
|
||||
|
||||
extern void test_BasicTryDoesNothingIfNoThrow(void);
|
||||
extern void test_BasicThrowAndCatch(void);
|
||||
extern void test_BasicThrowAndCatch_WithMiniSyntax(void);
|
||||
extern void test_VerifyVolatilesSurviveThrowAndCatch(void);
|
||||
extern void test_ThrowFromASubFunctionAndCatchInRootFunc(void);
|
||||
extern void test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc(void);
|
||||
@@ -14,10 +15,12 @@ extern void test_ThrowAndCatchFromASubFunctionAndNoRethrowToCatchInRootFunc(void
|
||||
extern void test_CanHaveMultipleTryBlocksInASingleFunction(void);
|
||||
extern void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void);
|
||||
extern void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void);
|
||||
extern void test_CanThrowADetailedExceptionAndCheckOutTheResults(void);
|
||||
extern void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThisDoesntCorruptExceptionId(void);
|
||||
extern void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThatEachExceptionIdIndependent(void);
|
||||
|
||||
static void runTest(UnityTestFunction test)
|
||||
{
|
||||
EXCEPTION_T e;
|
||||
if (TEST_PROTECT())
|
||||
{
|
||||
setUp();
|
||||
@@ -25,7 +28,7 @@ static void runTest(UnityTestFunction test)
|
||||
{
|
||||
test();
|
||||
}
|
||||
Catch
|
||||
Catch(e)
|
||||
{
|
||||
TEST_FAIL("Unexpected exception!")
|
||||
}
|
||||
@@ -42,6 +45,7 @@ int main(void)
|
||||
// RUN_TEST calls runTest
|
||||
RUN_TEST(test_BasicTryDoesNothingIfNoThrow);
|
||||
RUN_TEST(test_BasicThrowAndCatch);
|
||||
RUN_TEST(test_BasicThrowAndCatch_WithMiniSyntax);
|
||||
RUN_TEST(test_VerifyVolatilesSurviveThrowAndCatch);
|
||||
RUN_TEST(test_ThrowFromASubFunctionAndCatchInRootFunc);
|
||||
RUN_TEST(test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc);
|
||||
@@ -49,7 +53,8 @@ int main(void)
|
||||
RUN_TEST(test_CanHaveMultipleTryBlocksInASingleFunction);
|
||||
RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside);
|
||||
RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside);
|
||||
RUN_TEST(test_CanThrowADetailedExceptionAndCheckOutTheResults);
|
||||
RUN_TEST(test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThisDoesntCorruptExceptionId);
|
||||
RUN_TEST(test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThatEachExceptionIdIndependent);
|
||||
|
||||
UnityEnd();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user