22 Aug 2007 aleix   » (Journeyer)

Simple macros for Check

During my vacation I have had the opportunity to add unit testing to SCEW (Simple C Expat Wrapper). I looked at various C unit testing frameworks and I decided to use Check. Most of them follow the xUnit approach, but I chose Check because tests run in a separate address space other than the test runner.

I found that writing test cases was a bit hard using Check’s syntax, for example following the manual you can write this integer check:

fail_unless (money_amount (m) == 5,
             "Amount not set correctly on creation");

This is fine if you are reading the code, but if the check fails the output doesn’t show you what the actual or expected values are, so the manual suggests changing it for:

fail_unless(money_amount (m) == 5,
            "Amount was %d, instead of 5", money_amount (m));

which is quite better than the first one, but to painful if you have to write it for every check. So, why not write a helper macro that checks for integers, prints the actual and expected values and also shows you the check that is being done?

#define CHECK_INT(A, B, MSG, ...)                                       \
  do                                                                    \
    {                                                                   \
      enum { MAX_BUFFER = 250 };                                        \
      static char buffer[MAX_BUFFER];                                   \
      sprintf (buffer, MSG, ##__VA_ARGS__);                             \
      fail_unless ((A) == (B),                                          \
                   "(%s) == (%s) \n  Actual: %d \n  Expected: %d \n  %s", \
                   #A, #B, A, B, buffer);                               \
    }                                                                   \
  while (0);

With this macro you can now write code like this:

CHECK_INT (scew_list_size (list), N_ELEMENTS,
           "Number of children found searching by name");

which is really easy to read and in the test’s output you can see the actual and expected values, the performed test and the user message clarifying the intention of the check.

element.c:205:F:Core:test_search:0: (scew_list_size (list)) == (N_ELEMENTS)
  Actual: 1
  Expected: 12
  Number of children found searching by name

The same happens with strings, so instead of writing this:

fail_if (strcmp (money_currency (m), "USD") != 0,
         "Currency not set correctly on creation");

or this:

if (strcmp (money_currency (m), "USD") != 0)
  {
    fail ("Currency not set correctly on creation");
  }

we can write a string checking macro that shows the actual and expected strings and the check being done:

#define CHECK_STR(A, B, MSG, ...)                                       \
  do                                                                    \
    {                                                                   \
      if (strcmp ((A), (B)) != 0)                                       \
        {                                                               \
          enum { CHECK_MAX_BUFFER = 250 };                              \
          static char buffer[CHECK_MAX_BUFFER];                         \
          sprintf (buffer, MSG, ##__VA_ARGS__);                         \
          fail ("(%s) == (%s) \n  Actual: %s \n  Expected: %s \n  %s",  \
                #A, #B, A, B, buffer);                                  \
        }                                                               \
    }                                                                   \
  while (0);

As before, this would be the new output:

element.c:97:F:Core:test_accessors:0: (scew_element_name (element)) == (NAME)
  Actual: new_root
  Expected: root
  Element name do not match

Well, this is not a big deal, but I have found it quite useful. Below, is the list of macros I am using right now:

#define CHECK_INT(A, B, MSG, ...)                                       \
  do                                                                    \
    {                                                                   \
      enum { MAX_BUFFER = 250 };                                        \
      static char buffer[MAX_BUFFER];                                   \
      sprintf (buffer, MSG, ##__VA_ARGS__);                             \
      fail_unless ((A) == (B),                                          \
                   "(%s) == (%s) \n  Actual: %d \n  Expected: %d \n  %s", \
                   #A, #B, A, B, buffer);                               \
    }                                                                   \
  while (0);

#define CHECK_STR(A, B, MSG, ...)                                       \
  do                                                                    \
    {                                                                   \
      if (strcmp ((A), (B)) != 0)                                       \
        {                                                               \
          enum { CHECK_MAX_BUFFER = 250 };                              \
          static char buffer[CHECK_MAX_BUFFER];                         \
          sprintf (buffer, MSG, ##__VA_ARGS__);                         \
          fail ("(%s) == (%s) \n  Actual: %s \n  Expected: %s \n  %s",  \
                #A, #B, A, B, buffer);                                  \
        }                                                               \
    }                                                                   \
  while (0);

#define CHECK_PTR(A, MSG, ...)                                          \
  do                                                                    \
    {                                                                   \
      enum { MAX_BUFFER = 250 };                                        \
      static char buffer[MAX_BUFFER];                                   \
      sprintf (buffer, MSG, ##__VA_ARGS__);                             \
      fail_unless ((A) != NULL, "(%s) != NULL \n  %s", #A, buffer);     \
    }                                                                   \
  while (0);

#define CHECK_NULL_PTR(A, MSG, ...)                                     \
  do                                                                    \
    {                                                                   \
      enum { MAX_BUFFER = 250 };                                        \
      static char buffer[MAX_BUFFER];                                   \
      sprintf (buffer, MSG, ##__VA_ARGS__);                             \
      fail_unless ((A) == NULL, "(%s) == NULL \n  %s", #A, buffer);     \
    }                                                                   \
  while (0);

Update 2007/08/11: I have updated the macros so variable number of arguments are allowed (see variadic macros).

Syndicated 2007-08-06 16:07:23 from axelio

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!