Unit Testing C with cmocka

λ September 28, 2019
Tags: c, testing

I’ve recently abandoned my attempts at using mercury for writing a game as I had already spent ages on the FFI to SDL, which, despite being functional and good, I realised that I wanted to write a game more than I wanted to learn mercury!

Having spent a decade or more in the functional programming mindset, I decided that I had had enough of all of this high falutin’ programming language malarky to last a lifetime. Having recently looked at C++ and walked away I decided that, to hell with it, it’s time to go back 34 years to where I started, with “C”.

Doing it the right way. TDD.

Never having experienced TDD with C, I looked around for a decent framework that would help me quell my biggest fear about using “C” for a non-trivial game project: segfaults and dangling pointers.

I settled on “cmocka” as it looked like it had a good pedigree (it was ‘cmockery’ in a previous lifetime) and so far it hasn’t let me down. I have only so far used the basic assertion testing which has gotten me a long way.

cmocka

I use make for my project as it now has several files in it, I normally keep everything in one file until the time comes when I am satisfied that the code being developed is ready to be split away into its own little house.

Writing a test

One of the things I’ve wired into the game is the Penner easing functions as I wanted to experiment with using them to move stuff around, not just physical movement but tweening any arbitrary scalar value that might control alpha blending, colours, volumes, whatever I want.

Here’s one of the tests for my tween class that provides some timer functionality to allow a tween to happen over a set number of milliseconds:

static void tween_init_float_as_expected(void **state) {
  TWEEN t = {0};
  assert_float_equal
    ( 100.11
      , tween_init(&t, 100.11, 200.22, 1000, NULL)
      , 0.001);
  assert_int_equal(TwIdle, t.state);
  assert_float_equal(100.11, t.start, 0.001);
  assert_float_equal(200.22, t.end, 0.001);
  assert_float_equal(1000, t.duration, 0.001);
  assert_float_equal(100.11, t.value, 0.001);
  assert_null(t.fn);
}

I use “gnu” style as it’s the default in emacs, I like it, and why waste time on it? clang-format can deal with it later should I so choose.

Line 2 sets my tween instance to zeroes, then I initialise the tween to go from 100.11 to 200.22 over one second, 1000 ms. The NULL is the tween easing function. This test is only ensuring the initial state of the tween so I don’t need an easing function.

Line 3-6 calls the initialiser function, and then asserts that the return value is in fact the starting value for the tween, all tween functions return the last known current value.

As you can see, the usual assertions are available and so far I have managed just fine with those. There is much more depth to cmocka though, it of course allows mocks, can test for memory leaks etc etc and as and when the time comes I will pursue these options.

Running The Tests

My make file switches between test and game modes, and when it runs, it shows the default STDOUT mode, but XML and TAP output formats are also supported if that’s what you need.

Here’s my test main function and you can see that it’s very very easy to put together “groups” of tests, currently I have just one group. Having multiple groups means you can choose to run just that group later should your number of tests become very very large. For me, for now, simple is all I need and cmocka is proving very nice indeed…

int game_test(int argc, char* argv[])
{
  const struct CMUnitTest tests[] = {
    // placement grid
    cmocka_unit_test(midpoint_test),
    cmocka_unit_test(grid_placement_test_on_negative),
    cmocka_unit_test(grid_placement_test_on_exceeded_limits),
    cmocka_unit_test(grid_placement_test_on_zero),
    cmocka_unit_test(grid_placement_test_on_middle),
    // tweens
    cmocka_unit_test(tween_init_float_as_expected),
    cmocka_unit_test(tween_idle_remains_idle_as_float),
    cmocka_unit_test(tween_reset_correct_initial_values_as_float),
    cmocka_unit_test(tween_completes_with_endvalue_as_float_positive_step),
  };

  return cmocka_run_group_tests(tests, NULL, NULL);
}

My “C” Technique

I use assert() -everywhere- that I expect to see a pointer have a non-null value. Later I could replace the assert macro to not terminate abruptly but TBH, if the assertion fails something pretty bad has gone wrong and the game won’t have a snowballs chance in hell of not seg-faulting anyway!

I also have developed the habit of making any output variables the last output variables to a function, like Prolog / Mercury would have you do.

Actually, I think that I have come up with some nice tactics for myself to alleviate my memory management fears which maybe interesting enough for another post sometime.

Valgrind

Not good. No leaks in my code that I can see, cmocka has some test_malloc() and test_calloc() functions that I will investigate further in due course but I am preparing to dig into something called suppression’s that let you mask out the crap caused by the libraries that seem to leak for England, libX11 for example. It’s really hard to see that your own app is clean with so much other action going down!

Comments