Unit testing in C offers its own special challenges. Unit testing an application heavily dependent upon threads, written in C, offers even more.
Brian inherited an application where the main loop of the actual application looked something like:
int main(int argc, char **argv)
{
msg_t *msg;
if (!init()) exit(EXIT_FAILURE);
for ( ; ; )
{
msg = get_msg();
if (msg == NULL) continue;
process_msg(msg);
}
}
Read an input message, handle the message, read the next message, in an endless loop. Now, that's a perfectly reasonable main
method for a message-oriented service, but certainly you wouldn't want to unit test that way.
Brian writes:
As it turns out, this particular test application is just a clone of application which it is supposed to test, but it's linked with a "brillant" test framework rather than the normal application framework.
The test application would read test messages from an input file, which allowed it to simulate receiving messages. It would then pass those messages off to the code being tested. It's the way it decided testing was over where the problems showed up:
msg_t *get_msg()
{
struct stat fileInfo;
char rec[1024];
/* touch stoptesting to shutdown this application */
if (stat("stoptesting", &fileInfo) != -1)
{
printf("Done testing...");
system("rm stoptesting");
if (shutdownFn) shutdownFn();
exit(EXIT_SUCCESS);
}
if (fgets(rec, sizeof(rec), inFile) == NULL ||
feof(inFile))
{
return NULL;
}
return fileRecordToMsg(rec);
}
The test application stat
s a file called stoptesting
. If the file exists, the program exits. It uses system("rm stoptesting")
to call out to the shell to remove the file (ignoring the perfectly good unlink
syscall). The absolute only indication that this is how you stop the application is that comment in the middle of the method.
If, like Brian, you didn't read through every single line of code before running the test application, you'd find yourself staring at a program that refuses to exit, but also isn't using any meaningful quantities of CPU, which makes you assume something is wrong with your test program.
Which yes, there is something wrong with the test program. More than one thing, actually. Sure, the "touch a file to exit" is wrong, but as Brian points out, so is the name of the file:
The real WTF? They should have named the file "ofdeath" so at least shutting it down would be fun:$ touch ofdeath