In a previous post I went through the steps to run the Check tutorial, had a bit of a whinge about how difficult is had all been, and mumbled a bit about coming back when I’d figured out how to make everything work on a real project. In fact, that was much simpler than I expected.

At the end of this, I’ll have a simple C program with a corresponding build that uses the Check testing framework.

Install Check

One of the nice things about Check is that you can find it in a lot of repositories. Ubuntu, CentOS and MacPorts all have a check package, but in the worst case you may have to compile it for your platform.

I’m starting out with a fresh Ubuntu install, so I could install Check with apt-get, but for the sake of it, I’m going to compile it instead. First I needed to get a bit of a tool chain set up:

apt-get install git build-essential curl

Then, I downloaded Check from sourceforge.net:

curl -L "http://downloads.sourceforge.net/project/check/check/0.9.8/check-0.9.8.tar.gz?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fcheck%2Ffiles%2F&ts=1346064997&use_mirror=switch" > check-0.9.8.tar.gz

I unzipped check-0.9.8.tar.gz and built it. The Check build uses autotools, which is a build technology for people who want to collaborate, but with people they don’t like very much. Check’s other dependencies are listed in the readme.

apt-get install automake autoconf libtool pkg-config texinfo

Then I built Check.

autoreconf --install
./configure
make
make install

I’m mainly interested in the headers and libraries, which the installation puts in /usr/local/include and /usr/local/lib.

During the build, I need to make sure that /usr/local/include is on the include path, and /usr/local/lib on the library path. If I was building manually with make I would use make -L /usr/local/lib -I /usr/local/include. Instead I’ll add this to the makefile.

###Create a Project

I created a project folder containing src, test, include and lib directories with:

mkdir -p project/{src,test,include,lib}

and created the following root makefile in the project directory:

{% highlight make %} all: +$(MAKE) -C src +$(MAKE) -C test

check: +$(MAKE) -C test check

clean: +$(MAKE) -C src clean +$(MAKE) -C test clean {% endhighlight %}

I wrote a simple C function to test some implementation:

{% highlight c %} const char* greet(const char *name) { return “”; } {% endhighlight %}

and a corresponding header:

{% highlight c %} const char* greet(const char *); {% endhighlight %}

plus a main method.

{% highlight c %} #include <stdio.h> #include “greet.h”

int main(void) { puts(greet(“Tom”)); return 0; } {% endhighlight %}

The following Makefile lives in src, and is called by the root makefile:

{% highlight make %} program_NAME := main program_SRCS := $(wildcard *.c) program_OBJS := ${program_SRCS:.c=.o}

CFLAGS += -I ../include

.PHONY: all clean

all: $(program_NAME)

$(program_NAME): $(program_OBJS) $(CC) $(CFLAGS) -o $(program_NAME) $(program_OBJS) cp greet.o ../lib

clean: $(RM) $(program_NAME) $(program_OBJS) {% endhighlight %}

I wrote a simple failing test with no implementation:

{% highlight c %} #include <check.h> #include <greet.h>

START_TEST (check_correct_greeting_is_returned) { fail(“test not implemented”); } END_TEST {% endhighlight %}

and then, a more complicated test runner. There’s no requirement to separate the test runner out from the test suites, but I prefer it.

{% highlight c %} #include <check.h> #include “check_main.c”

Suite * request_suite(void) { Suite *s = suite_create(“request”);

TCase *tc_core = tcase_create("Core");
tcase_add_test(tc_core, check_correct_greeting_is_returned);
suite_add_tcase(s, tc_core);

return s;

}

int main(void) { int number_failed; Suite *s = request_suite(); SRunner *sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr);

return (number_failed == 0) ? 0 : 1;

} {% endhighlight %}

I based test_runner.c on some example code I found in the Check documentation which you can see here.

The test Makefile looks like this:

{% highlight make %} check_NAME := test_runner check_SRCS := $(wildcard *.c) check_OBJS := ${check_SRCS:.c=.o} check_LIBS := -lcheck

program_OBJS := $(wildcard ../lib/*.o)

CFLAGS += -L/usr/local/lib -I/usr/local/include -I../include

.PHONY: all clean check

all: $(check_NAME)

$(check_NAME): $(check_OBJS) $(CC) $(CFLAGS) -o $(check_NAME) $(check_OBJS) $(program_OBJS) $(check_LIBS)

check: all ./test_runner

clean: $(RM) $(check_NAME) $(check_OBJS) {% endhighlight %}

Now I can run make check to see the unit test working, i.e. failing.

vagrant@precise64:~/Development/project$ make check
make -C test check
make[1]: Entering directory `/home/vagrant/Development/project/test'
cc -L/usr/local/lib -I/usr/local/include -I../include   -c -o check_main.o check_main.c
cc -L/usr/local/lib -I/usr/local/include -I../include   -c -o test_runner.o test_runner.c
cc -L/usr/local/lib -I/usr/local/include -I../include -o test_runner check_main.o test_runner.o ../lib/greet.o -lcheck
./test_runner
Running suite(s): request
0%: Checks: 1, Failures: 1, Errors: 0
check_main.c:7:F:Core:check_correct_greeting_is_returned:0: test not implemented
make[1]: *** [check] Error 1
make[1]: Leaving directory `/home/vagrant/Development/project/test'
make: *** [check] Error 2

###Code some code

Finally, I did some of that programming stuff to make the program do what I expect, which is to greet me with “ohai Tom! xxx”.

{% highlight diff %} #include <check.h> +#include <string.h> #include <greet.h>

START_TEST (check_correct_greeting_is_returned) {

  •   fail("test not implemented");
    
  •   fail_unless(0 == strncmp("ohai Tom! xxx", greet("Tom"), (size_t) 255));
    

} END_TEST {% endhighlight %}

Now my test fails with an assertion error:

vagrant@precise64:~/Development/project$ make check
make -C test check
make[1]: Entering directory `/home/vagrant/Development/project/test'
./test_runner
Running suite(s): request
0%: Checks: 1, Failures: 1, Errors: 0
check_main.c:8:F:Core:check_correct_greeting_is_returned:0: Assertion '0 == strncmp("ohai Tom! xxx", greet("Tom"), (size_t) 255)' failed
make[1]: *** [check] Error 1
make[1]: Leaving directory `/home/vagrant/Development/project/test'
make: *** [check] Error 2

Because TDD is all about writing the simplest code to pass the test, my implementation is as follows:

{% highlight diff %} — a/src/greet.c +++ b/src/greet.c @@ -1,5 +1,5 @@ const char* greet(const char *name) {

  •   return "";
    
  •   return "ohai Tom! xxx";
    

} {% endhighlight %}

Merci beaucoup, et bonne nuit.

You can download the simple project here, or check out my pet project, pushy, on github for a more realistic example.