views:

102

answers:

3

I am working on a quite large C library that doesn't have any tests now. As the API starts to be final, I'd like to start writing unit tests.

Nearly all my functions acts on the first parameter (a structure).

The naive approach to unit test is to have the pre function call structure in a known state, call the function, and then compare the pre call structure with the expected result.

Now this works with structure composed of scalar types, but as for allocated memory, I was wondering what kind of approach you were using.

For example, imagine an image structure, when you do:

CreateImage(&img, x, y);

you expect the img->x to be x, img->y to be y and img->pixels to be a pointer to something big enough to hold x * y * sizeof(pixel).

Checking for the first two is trivial, but what about the img->pixels? I don't mean to check if the malloc call was successful, as I can [overload] malloc, but I want to know if malloc was called properly.

This is especially important in case like that:

CreateImage(*img, x, y)
{
    img->x = x; img->y = y;
    /* do something, dhoo, that something is broken and modify x or y */
    img->pixels = malloc(x * y * sizeof(pixel)); /* wrong allocation size */
    if(!img->pixels) error("no memory");
}

I hope my question is clear.

Thanks.

+1  A: 

You can overload malloc so as to set a number of expectations on it. Since you know before you make the call how much memory it needs to allocate you could something of the sort.

SetMallocExpectation(x * y * sizeof(pixel));
CreateImage(&img, x, y);
if ( InvalidMalloc() ) {
    // The malloc call was with a different size 
}
ResetMalloc();

As for the implementation I figure it'd look something like this

size_t mallocExpectations[SOME_SIZE];
int mallocCur = 0;
int mallocError = 0;
int numExpectations = 0;

void* MyMalloc(size_t size) {

    if ( size != mallocExpectations[mallocCur++] ) {
        mallocError = 1;
    }

    return (*malloc)(size);
}

void SetMallocExpectation(size_t size) {
    mallocExpectations[numExpectations++] = size;
}

int InvalidMalloc() {
    return mallocError;
}

void ResetMalloc() {
    mallocCur = 0;
    mallocError = 0;
    curExpecations = 0;
}
Juan
That's a good idea. I wait for more though.
+2  A: 

In your unit test, have your overloaded malloc function log the value that it was called with (eg. alloc_size).

Then just test that all of these are true:

  • alloc_size % sizeof(pixel) == 0
  • alloc_size % x == 0
  • alloc_size % y == 0
  • ((alloc_size / sizeof(pixel)) / x ) / y == 1

Do not just replicate the code from the function you're testing - ie. the multiplication - because you may end up replicating a bug.

In this case you would potentially replicate a bug - if there's no bounds tests on x and y, then the multiplication in the malloc call is itself buggy (consider what would happen on a system with a 32 bit size_t, sizeof(pixel) == 4, x == 40000 and y == 40000).

caf
Good idea too, in the same field as Juan's answer.
+1 for mentioning overflow
philippe
+1  A: 

Another idea is to try and fill the buffer created by malloc to maximal size, and then run your test suite with Valgrind. This should surface allocation problems. Running it with Valgrind is helpful for other things as well, such as detecting memory leaks.

Eli Bendersky