Helper 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_U_INT(A, B, MSG, ...)                                     \\
  do                                                                    \\
    {                                                                   \\
      enum { MAX_BUFFER = 250 };                                        \\
      static char buffer[MAX_BUFFER];                                   \\
      sprintf (buffer, MSG, ##__VA_ARGS__);                             \\
      unsigned int v_a = (A);                                           \\
      unsigned int v_b = (B);                                           \\
      fail_unless (v_a == v_b,                                          \\
                   "(%s) == (%s) \\n  Actual: %d \\n  Expected: %d \\n  %s", \\
                   #A, #B, v_a, v_b, buffer);                           \\
    }                                                                   \\
  while (0)

With this macro you can now write code like this:

CHECK_U_INT (money_amount (m), 5, "Money amount mismatch");

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.

check.c:97:F:Core:test_amount:0: (money_amount (m)) == (5) 
  Actual: 2 
  Expected: 5
  Money amount mismatch

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                                                                    \\
    {                                                                   \\
      char const *str_a = (A);                                          \\
      char const *str_b = (B);                                          \\
      if (strcmp (str_a, str_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, str_a, str_b, buffer);                          \\
        }                                                               \\
    }                                                                   \\
  while (0)

As before, this would be the new output:

check.c:205:F:Core:test_currency:0: (money_currency (m)) == (USD) 
  Actual: EUR
  Expected: USD
  Currency not set correctly on creation

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_U_INT(A, B, MSG, ...)                                     \\
  do                                                                    \\
    {                                                                   \\
      enum { MAX_BUFFER = 250 };                                        \\
      static char buffer[MAX_BUFFER];                                   \\
      sprintf (buffer, MSG, ##__VA_ARGS__);                             \\
      unsigned int v_a = (A);                                           \\
      unsigned int v_b = (B);                                           \\
      fail_unless (v_a == v_b,                                          \\
                   "(%s) == (%s) \\n  Actual: %d \\n  Expected: %d \\n  %s", \\
                   #A, #B, v_a, v_b, buffer);                           \\
    }                                                                   \\
  while (0)

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

#define CHECK_BOOL(A, B, MSG, ...) CHECK_U_INT (A, B, MSG, ##__VA_ARGS__)

#define CHECK_STR(A, B, MSG, ...)                                       \\
  do                                                                    \\
    {                                                                   \\
      char const *str_a = (A);                                          \\
      char const *str_b = (B);                                          \\
      if (strcmp (str_a, str_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, str_a, str_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/10/05: Fix: using macro parameters more than once might cause multiple unnecessary function calls.

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

About these ads

One Response to “Helper macros for Check”

  1. Olmo Maldonado Says:

    Excellent article. You’ve covered two topics I was interested in reading about: Unit Testing and variadic macros.
    :D

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: