Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does one unit test handling of the error conditions for Python/C APIs like PyType_Ready and PyObject_New?

It's fairly straightforward (if tedious) to unit test Python extension modules written in C, including the error cases for many of the Python/C APIs such as PyArg_ParseTuple. For example, the idiomatic way to start a C function which implements a Python function or method looks like:

    if (!PyArg_ParseTuple(args, "someformat:function_name")) {
        return NULL;
    }

The success case of this can be unit tested by calling the function with the correct number and type of arguments. The failure cases can also be tested by calling the function with first the wrong number of arguments and then the right number of arguments but passing values of the wrong type. This results in full branch test coverage of the C code.

However, it's not clear how to exercise the negative paths for other Python/C APIs. An idiomatic way to begin module initialization in a C extension looks like:

    if (PyType_Ready(&Some_Extension_Structure) < 0) {
        return 0;
    }

How can PyType_Ready be made to fail? Similarly, the C function for allocating a new instance of an extension type frequently uses an API like PyObject_New:

    self = PyObject_New(Some_Structure, &Some_Extension_Structure);
    if (self == NULL) {
        return NULL;
    }

How can one unit test this negative case (particularly considering PyObject_New is likely used many, many times over the course the execution of a single unit test method)?

It seems possible to build a general solution, relying on dynamic linker tricks such as LD_PRELOAD to provide fakes of these C APIs which can be directed to fail in the right ways at the right times. The cost of building a system like that seems a bit out of reach, though. Has someone else done it already and make the result available?

Are there Python/C-specific tricks that could make this testing easier?

Should I be thinking along some other lines entirely?

like image 296
Jean-Paul Calderone Avatar asked Feb 13 '12 16:02

Jean-Paul Calderone


1 Answers

This is a clear case for test doubles (for example, mocking). Since the Python C API doesn't offer any facilities for faking an out of memory condition, you'd have to do it yourself.

Create your own layer that provides PyType_Ready and PyObject_New. Have them pass through to the C API functions, unless some control, probably an environment variable, instructs them not to. They can cause any mayhem you desire, and test your code's reaction.

like image 194
Ned Batchelder Avatar answered Oct 06 '22 00:10

Ned Batchelder