Unit-testing C code with Python & swig & nose

by syoyo

nosetest.png

I’m finding a way how to test code of lucille.

There are some testing methods

– Unit test
– Use case(functional test)
– Behaviour driven development
– Formal verification(good for testing algorithm itself)
– Model checking for monte carlo algorithm( e.g. prism )
– etc.

In this article, I’d like to talk to about how to do unit testing for C function.

There’s already good unit testing framework for C, CUnit.
But writing fixture(setup, teardown) code also in C is harder & redundant & time-consuming,
so I tried to seek a way to test C code from scripting language.

An answer I’ve convinced so far is to use Python to write fixture and swig to call C function from Python.
And more, nose enables writing python-side test code much more simpler.

Here’s my trial of C, swig, Python and nose combination for unit testing.

C code to be tested

First, let we assume unit-test functions is defined in list.h(code grabbed from lucille source).


/*
 * simple list routine.
 *
 * $Id: list.h,v 1.1.1.1 2004/01/06 13:57:09 syoyo Exp $
 */

#ifndef LIST_H
#define LIST_H

#ifdef __cplusplus
extern "C" {
#endif

typedef struct _ri_list_t
{
	void		  *data;
	struct _ri_list_t *next;
	struct _ri_list_t *prev;
} ri_list_t;

extern ri_list_t *ri_list_new();
/* add data to last */
extern void	  ri_list_append(ri_list_t *list, void *data);
...

 
 
Swig

Then, write swig interface file test.i so that C functions can be callable from Python.


%module base_list
%{
#include "memory.h"
#include "list.h"
%}

%include "../../../../src/base/memory.h"
%include "../../../../src/base/list.h"


 
 
Provide setup.py to ease compile/link C code to make a module.


# setup.py
import distutils
from distutils.core import setup, Extension

import os

srcPath = "../../../../src/base"

srcList = [ "list.i"
          , os.path.join(srcPath, "list.c")
          , os.path.join(srcPath, "memory.c")
          ]

setup(name = "base_list",
      version = "1.0",
      ext_modules = [Extension("_base_list", sources=srcList, include_dirs = [srcPath])])

 
 

With setup.py, compiling module can be done by just typing


$ python setup.py build_ext --inplace

 
 

Python test code

Finally, write unit test code in python(test.py)
Thanks to nose, classes whose name having “test” is considered as a test,
so we don’t need to write unit test related things explicitly.


from base_list import *

import os, sys

class TestListNewIsNotNull():

    def setup(self):
        pass

    def teardown(self):
        pass

    def testListNew(self):
        l = ri_list_new()

        assert l != None

class TestListNextAfterListNewIsNull():

    def setup(self):
        self.lst = ri_list_new()

    def teardown(self):
        ri_list_free(self.lst)

    def test(self):
        l = ri_list_next(self.lst)

        assert l == None


 
 

Run test with nose

Functions/Classes/Directories with name having “test” is automatically executed by nose.


 $ nosetests -v
test.TestListNewIsNotNull.testListNew ... ok
test.TestListNextAfterListNewIsNull.test ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.008s

OK


Sounds nice?

All codes explained above are located at,

https://lucille.svn.sourceforge.net/svnroot/lucille/trunk/tests/unit/testBase/testList

Advertisements