/context-alloc

simple pluggable memory allocation for easy testing of malloc returning NULL

Primary LanguageCGNU Lesser General Public License v3.0LGPL-3.0

Context-aware pluggable malloc / free scaffolding

Description
-----
"context-alloc" is a C library intended to simplify writing unit tests
for out-of-memory conditions. By introducing indirection, it grants the
option to mock alloctation and deallocation in tests to ensure that code
behaves as expected in out-of-memory situations.

Usage
-----

Copy the src/context-alloc.h and src/context-alloc.c directly into your
project.  Rename them and adjust them to fit in with your project's
naming conventions.

The "demo" directory contains an example of how to use context-alloc,
but in short the idea is that by default the standard functions malloc
and free are used, but alternatives may be plugged in, including a
pointer to an object for context, thus allowing easier testing of memory
exhaustion.

In the code, rather than calling malloc and free directly, keep a
function pointer to the allocation and freeing functions. Of course you
can have these as global variables, or better by adding them to your
struct, perhaps a little like this:

	typedef struct foo {
		context_malloc_func alloc;
		context_free_func free;
		void *mem_context;
		...
	} foo_s;


	foo_s *foo_new_custom_allocator(context_malloc_func c_alloc,
	                                context_free_func c_free,
	                                void *mem_context)
	{
		if (!c_alloc || !c_free) {
			c_alloc = context_stdlib_malloc;
			c_free = context_stdlib_free;
			mem_context = NULL;
		}
		...
	}

	foo_s *foo_new(void)
	{
		return foo_new_custom_allocator(NULL, NULL, NULL);
	}

	/* return 0 on success or 1 on memory allocation failure */
	int foo_bar(foo_s *foo, int bar)
	{
		char *buf = foo->alloc(foo->mem_context, BUF_LEN);
		if (!buf) {
			return 1;
		}
		...
		return 0;
	}

The "util" directory in the project includes example allocators which
allow easier testing of Out Of Memory conditions. In your tests, you can
do something like:

	int test_foo_bar_oom(void)
	{
		foo_s *foo;
		context_malloc_func ctx_alloc;
		context_free_func ctx_free;
		oom_injecting_context_s mctx;
		int i, err;

		ctx_alloc = oom_injecting_malloc;
		ctx_free = oom_injecting_free;
		oom_injecting_context_init(&mctx);

		/* return NULL on the 4th call to malloc: */
		mctx.attempts_to_fail_bitmask = 0x01 << 3;

		foo = foo_new_custom_allocator(ctx_malloc, ctx_free, &mctx);

		for(i = 0; i < 10; ++i) {
			err = foo_bar(foo, i);
			if (err) {
				/* malloc inside foo_bar() returned NULL */
				/* maybe add more sanity checks here */
				...
			}
		}
		...
	}

In the "demo" directory you can see a more complete example as described
above.

To run the demo, simply type "make" in the root of the project, or to
see more verbose output, build with DEBUG=1 like:

	make clean
	make DEBUG=1 check

To get code coverage reporting, use:

	make clean
	make DEBUG=1 coverage


License
-------

LGPL-3.0-or-later

This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.