news

[Published in Open Source For You (OSFY) magazine, September 2013 edition.]

CUnit is a free and open source, unit testing, software framework written in C. It provides a very simple interface to write unit tests, and has a number of assertions for testing data and functions.

CUnit is created as a library that links with the user’s code under test. It is packaged and is available in most GNU/Linux distributions. It has been released under the LGPLv2+ license. You can install it on Fedora, for example, using the following command:

$ sudo yum install CUnit

The CUnit framework helps manage test cases, the test registry, and test suites. A systematic approach is to write unit tests for your code, initialize the CUnit test registry, add the test suites to the test registry, and then add the unit tests to the suites. Set-up and clean-up functions can be written for each test suite. All the test suites can be executed together or can be run selectively. The user interface method to run the tests needs to be decided before executing them. The test registry needs to be cleaned up before returning the error condition and exiting from the test run. Consider the following simple example:

#include <CUnit/CUnit.h>

int init_suite(void)  { return 0; }
int clean_suite(void) { return 0; }

int
is_even (int x)
{
  return (x % 2 == 0);
}

void
test_is_even (void)
{
  CU_ASSERT(is_even(1)  == 0);
  CU_ASSERT(is_even(2)  == 1);
  CU_ASSERT(is_even(3)  == 0);
}

int
main (void)
{
   CU_pSuite pSuite = NULL;

   /* Initialize CUnit test registry */
   if (CUE_SUCCESS != CU_initialize_registry())
      return CU_get_error();

   /* Add suite to registry */
   pSuite = CU_add_suite("Basic_Test_Suite", init_suite, clean_suite);
   if (NULL == pSuite) {
      CU_cleanup_registry();
      return CU_get_error();
      }

   /* add test to suite */
   if ((NULL == CU_add_test(pSuite, "test_is_even", test_is_even)))
   {
      CU_cleanup_registry();
      return CU_get_error();
   }

   /* Run tests using Basic interface */
   CU_basic_run_tests();

   /* Clean up registry and return */
   CU_cleanup_registry();
   return CU_get_error();
}

You can compile the above code using:

$ gcc basic.c -o basic -lcunit -lcurses

The above step can also be abstracted and automated in a Makefile for subsequent compilation and testing, as follows:

CC = gcc
CUNIT_LDFLAGS = -lcunit -lcurses

objects = basic

all:
	$(foreach file,$(objects), $(CC) $(file).c -o $(file) $(CUNIT_LDFLAGS) ;)

clean:
	rm -f *~ *.o $(objects)

You can type ‘make’ in the terminal to compile the code, as follows:

$ make
gcc basic.c -o basic -lcunit -lcurses ;

On execution, you get the following output:

$ ./basic 


     CUnit - A unit testing framework for C - Version 2.1-2
     http://cunit.sourceforge.net/



Run Summary:    Type  Total    Ran Passed Failed Inactive
              suites      1      1    n/a      0        0
               tests      1      1      1      0        0
             asserts      3      3      3      0      n/a

Elapsed time =    0.000 seconds

The above example uses the Basic interface output from CUnit. A number of assertions have been defined in CUnit/CUnit.h file. Some of them are:

CU_ASSERT(int expression) CU_ASSERT_FATAL(int expression) Check expression is non-zero
CU_ASSERT_TRUE(value) CU_ASSERT_TRUE_FATAL(value) Check value is non-zero
CU_ASSERT_FALSE(value) CU_ASSERT_FALSE_FATAL(value) Check value is zero
CU_ASSERT_EQUAL(actual, expected) CU_ASSERT_EQUAL_FATAL(actual, expected) Check actual == expected
CU_ASSERT_NOT_EQUAL(actual, expected) CU_ASSERT_NOT_EQUAL_FATAL(actual, expected) Check actual != expected
CU_ASSERT_PTR_NULL(value) CU_ASSERT_PTR_NULL_FATAL(value) Check pointer == NULL
CU_ASSERT_PTR_NOT_NULL(value) CU_ASSERT_PTR_NOT_NULL_FATAL(value) Check pointer != NULL
CU_ASSERT_STRING_EQUAL(actual, expected) CU_ASSERT_STRING_EQUAL_FATAL(actual, expected) Check actual == expected

The Automated interface dumps the output of the test results to an XML file. The following code snippet is used for the Automated interface:

   /* Run tests using the Automated interface */
   CU_automated_run_tests();

The compilation step is similar to the Basic example. The output is dumped to the CUnitAutomated-Results.xml file, which contains the following:

<?xml version="1.0" ?> 
<?xml-stylesheet type="text/xsl" href="CUnit-Run.xsl" ?> 
<!DOCTYPE CUNIT_TEST_RUN_REPORT SYSTEM "CUnit-Run.dtd"> 
<CUNIT_TEST_RUN_REPORT> 
  <CUNIT_HEADER/> 
  <CUNIT_RESULT_LISTING> 
    <CUNIT_RUN_SUITE> 
      <CUNIT_RUN_SUITE_SUCCESS> 
        <SUITE_NAME> Basic_Test_Suite </SUITE_NAME> 
        <CUNIT_RUN_TEST_RECORD> 
          <CUNIT_RUN_TEST_SUCCESS> 
            <TEST_NAME> test_is_even </TEST_NAME> 
          </CUNIT_RUN_TEST_SUCCESS> 
        </CUNIT_RUN_TEST_RECORD> 
      </CUNIT_RUN_SUITE_SUCCESS> 
    </CUNIT_RUN_SUITE> 
  </CUNIT_RESULT_LISTING>
  <CUNIT_RUN_SUMMARY> 
    <CUNIT_RUN_SUMMARY_RECORD> 
      <TYPE> Suites </TYPE> 
      <TOTAL> 1 </TOTAL> 
      <RUN> 1 </RUN> 
      <SUCCEEDED> - NA - </SUCCEEDED> 
      <FAILED> 0 </FAILED> 
      <INACTIVE> 0 </INACTIVE> 
    </CUNIT_RUN_SUMMARY_RECORD> 
    <CUNIT_RUN_SUMMARY_RECORD> 
      <TYPE> Test Cases </TYPE> 
      <TOTAL> 1 </TOTAL> 
      <RUN> 1 </RUN> 
      <SUCCEEDED> 1 </SUCCEEDED> 
      <FAILED> 0 </FAILED> 
      <INACTIVE> 0 </INACTIVE> 
    </CUNIT_RUN_SUMMARY_RECORD> 
    <CUNIT_RUN_SUMMARY_RECORD> 
      <TYPE> Assertions </TYPE> 
      <TOTAL> 3 </TOTAL> 
      <RUN> 3 </RUN> 
      <SUCCEEDED> 3 </SUCCEEDED> 
      <FAILED> 0 </FAILED> 
      <INACTIVE> n/a </INACTIVE> 
    </CUNIT_RUN_SUMMARY_RECORD> 
  </CUNIT_RUN_SUMMARY> 
  <CUNIT_FOOTER> File Generated By CUnit v2.1-2 - Tue Jun 25 16:01:49 2013
 </CUNIT_FOOTER> 
</CUNIT_TEST_RUN_REPORT>

If you wish to specify the output XML filename, it can be set using the following command:

   CU_set_output_filename("Even")
   CU_automated_run_tests();

The above XML output is dumped to a ‘Even-Results.xml’ file. The Basic and Automated interfaces are non-interactive modes to run the tests. The interactive Console mode of running tests can be initiated by using the following command:

   /* Run tests in interactive Console mode */
   CU_console_run_tests();

On compilation and execution, the following menu is shown in the terminal:

 $  ./console 


      CUnit - A Unit testing framework for C - Version 2.1-2
	      http://cunit.sourceforge.net/


 ***************** CUNIT CONSOLE - MAIN MENU ******************************
 (R)un  (S)elect  (L)ist  (A)ctivate  (F)ailures  (O)ptions  (H)elp  (Q)uit
 Enter command: 

This allows you to run all the test suites, or select a suite to be executed. You can list all the registered suites, and also select one to modify it. You can activate or deactivate a suite, and can view any failures from the previous test run. The other interactive ‘Curses’ mode can be invoked using:

    /* Run tests in interactive Curses mode */
    CU_curses_run_tests();

A screenshot of the Curses interface is shown below:

CUnit curses screenshot

Let’s suppose you have the following code snippet where the third assert fails:

void
test_is_even (void)
{
  CU_ASSERT(is_even(1)  == 0);
  CU_ASSERT(is_even(2)  == 1);
  CU_ASSERT(is_even(4)  == 0);
  CU_ASSERT(is_even(3)  == 0);
}

On executing the above, you will get:

$ ./error 


     CUnit - A unit testing framework for C - Version 2.1-2
     http://cunit.sourceforge.net/


Suite Basic_Test_Suite, Test test_is_even had failures:
    1. error.c:17  - is_even(4) == 0

Run Summary:    Type  Total    Ran Passed Failed Inactive
              suites      1      1    n/a      0        0
               tests      1      1      0      1        0
             asserts      4      4      3      1      n/a

Elapsed time =    0.000 seconds

If you wish to terminate the execution of the test when an error occurs, you can use the code shown below:

...
void
test_is_even (void)
{
  CU_ASSERT(is_even(1)  == 0);
  CU_ASSERT(is_even(2)  == 1);
  CU_ASSERT_TRUE_FATAL(is_even(4)  == 0);
  CU_ASSERT(is_even(3)  == 0);
}

int
main (void)
{
  ...
  CU_set_error_action(CUEA_FAIL);
  ...
}

The corresponding output is shown below:

 ./error-fail 


     CUnit - A unit testing framework for C - Version 2.1-2
     http://cunit.sourceforge.net/


Suite Basic_Test_Suite, Test test_is_even had failures:
    1. error-fail.c:17  - CU_ASSERT_TRUE_FATAL(is_even(4) == 0)

Run Summary:    Type  Total    Ran Passed Failed Inactive
              suites      1      1    n/a      0        0
               tests      1      1      0      1        0
             asserts      3      3      2      1      n/a

Elapsed time =    0.000 seconds

The test execution stopped after the third assert failed. The different options for error actions are:

Error value Meaning
CUEA_IGNORE Continue on error
CUEA_FAIL Stop on error
CUEA_ABORT Call exit() on error

You can refer the CUnit Programmers Guide and API documentation to know more about CUnit.