Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C Unit Testing - Returning from a stubbed graceful exit routine

Tags:

c

unit-testing

Here is the scenario I've got. The function I'm testing has an error condition that, if hit, calls a graceful exit function to free any global memory, close handles, and exit the program.

Obviously, I'll want to write a test that tickles this condition to make sure that it is handled correctly but I don't want the graceful exit routine to actually exit the program since that would stop any remaining tests. This means stubbing the graceful exit routine. The problem with stubbing and not calling exit is that the flow of control returns to the function under test (which is bad since the routine was supposed to exit).

Here is the actual question: How do we return control from the stubbed function to the test instead of to the function under test?

I can do a setjmp / longjmp, but since "gotos" are bad in general, I'd love any other suggestions. (Keep in mind that this is procedural C, not C++, so exceptions aren't going to work as far as I know)

EDIT As Soren and others have suggested below, making exit do nothing when testing is a great idea. There are several ways to do this whether it be through a #define statement or a stub for the exit() routine.

HOWEVER, doing so presents the problem that I'm really after a solution for (other than setjmp / longjmp). Take a look at this scenario:

void gracefulExit() {
    // Clean Up
    exit();
}

void routineUnderTest() {
    // Do some calcs

    if (someBadCondition == TRUE)
        gracefulExit()

    // Do some more calcs
}

If exit() does nothing in this scenario, gracefulExit() will return control back to the routine under test, which should not happen. Hence, I need a way to make exit() (or the stubbed version of gracefulExit()) return control to the test instead of the function under test.

setjmp / longjmp (aka goto) is a way to do this, although not really an elegant way. Any ideas on how to solve that?

EDIT #2

As fizzer mentioned, setjmp/longjmp is a valid way to handle this situation. It is the likely way that I will handle it permanently.

I have, however, received another possible solution from a co-worker. Instead of #defining the gracefulExit() routine to a stub routine, do the following:

#define gracefulExit return NULL

The particular function under test handles this just fine since NULL is a valid return value for it. I haven't tested this in every possible case yet (e.g. a function that has a void return value). As mentioned earlier, I will likely use the setjmp/longjmp way of solving this problem, but if this additional solution sparks an idea for somebody, great!

like image 894
Taylor Price Avatar asked Aug 01 '11 21:08

Taylor Price


1 Answers

I would suggest your clean-up method creates an unpublished interface to do the exit.

so

   void (*exitfunc((int) = exit;
   void myCleanUp() {
      .... do the cleanup
      (*exitfunc)(-1); // this will in normal operation call exit
   }

and in your unit test code, you "override" the exit function as

   void donothing(int exitcode) {}
   unittest(){
      extern void (*exitfunc((int);
      exitfunc = donothing; // or use a longjump if clean exit cannot be made without
      ... do the test.....

This would mean that your code compiles to the same whether in a unit test or somewhere else, and the difference in behaviour is only occuring when you execute the unit-test. The alternatives using conditional compiling for unit testing means that you will end up with .o files where you don't know if they were intended for test or production, and you will have bad stuff happening to your project.

like image 72
Soren Avatar answered Oct 22 '22 04:10

Soren