Executing unit tests for functions that contain endless loops poses a problem when checking assertions. This is because test cases require the tested function to exit in order to perform assertion validation. In some situations, however, users can implement test cases for a function containing an endless loop. This is accomplished by using special macros and functions that allow conditional breaks of endless loop code execution:
Name | Details |
---|---|
CPPTEST_REGISTER_JMP (expression) | Sets an internal jump buffer using setjmp or sigsetjmp and evaluates the passed expression. The buffer can be jumped to using the CPPTEST_JMP API call. This macro is typically used inside the test case where it wraps the call to the tested function (see example below). |
CPPTEST_JMP (value) | Executes a longjmp call (longjmp or siglongjmp is used), which restores the exectuion state to the most recent invocation of the This macro is typically used inside the stub body. It accepts an integer argument that will be returned inside the test case via the cpptestGetJmpReturn function. The integer argrument can be used to verify the correctness of the performed jump (see example below). |
int CDECL_CALL cpptestGetJmpReturn(); | Returns the return value of the most recent CPPTEST_JMP call, i.e. the argument of longjmp/siglongjmp, which is a return value from setmp/sigsetjmp. |
These macros are intended to be used in conjunction and are intended to provide users with the ability to break the execution of the tested code (e.g. inside the stub body) and return to the test case to perform assertion checks. They function similarly to the setjmp and longjmp calls declared in the standard setjmp.h header.
This feature is useful for testing typical events-processing embedded functions that contain endless loops. Breaking test code execution is accomplished by a call to CPPTEST_JMP. The recommended technique for injecting a call to CPPTEST_JMP is through the stub body.
The following example illustrates the most common use for these macros and functions:
/* Function to be tested */ void signalData(void) { while(1) { // Get the returning frequency from the frequency sensor. if (scanFrequencySensor (returningFrequency) == FAILURE) { // If sensor has an error, don't give semaphore, // report error, call recovery and retry log_error("The sensor has encountered an error, retrying...\n"); recover(); } else { semGive(dataSemId); taskDelay(DELAY_TICKS); } } } /* Test case */ /* CPPTEST_TEST_CASE_BEGIN test_signalData */ /* CPPTEST_TEST_CASE_CONTEXT void signalData(void) */ void TestSuite_main_c_883563c3_test_signalData() { /* Pre-condition initialization */ /* Initializing global variable returningFrequency */ returningFrequency = 0; /* Tested function call */ CPPTEST_REGISTER_JMP(signalData()) /* Post-condition check */ CPPTEST_POST_CONDITION_INTEGER("cpptestGetJmpReturn()=", ( cpptestGetJmpReturn() )); CPPTEST_ASSERT_INTEGER_EQUAL(0, ( returningFrequency )); } /* CPPTEST_TEST_CASE_END test_signalData */ /* Stub */ /** User stub definition for function: int scanFrequencySensor(int) */ EXTERN_C_LINKAGE int scanFrequencySensor (int returningValue) ; EXTERN_C_LINKAGE int CppTest_Stub_scanFrequencySensor (int returningValue) { static unsigned int counter = 0; unsigned int ret = 0; counter++; //Simulate failure every 5 scans of the sensor if (counter == 5) { CPPTEST_REPORT("Simulating sensor scan failure") ret = FAILURE; } else if (counter == 10) { CPPTEST_REPORT("Testing accomplished - going back to test case for assertions check") CPPTEST_JMP(counter) } else { ret = scanFrequencySensor(returningValue); } return ret; }
When C++test executes the TestSuite_main_c_883563c3_test_signalData test case, it saves the jump buffer prior to calling the tested function. When the tested code executes, it performs the call to CPPTEST_JMP from the CppTest_Stub_scanFrequencySensor stub body, which transfers code execution back to the test case to perform assertions checks.