#include Test Double

It’s day one of adding tests to your legacy C code. You get stopped dead when the compiler announces that the code you are coaxing into the test harness can’t be compiled on this machine. You are stuck on the Make it compile step of Crash to Pass.

Moving your embedded legacy C code (embedded C code without tests) into a test harness can be a challenge. The legacy C code is likely to be tightly bound to the target processor. This might not be a problem for production, but for off-target unit testing, it is a big problem.

For C we have a limited mechanisms for breaking dependencies. In my book, I describe at length link-time and function pointer substitutions, but only touch on preprocessor stubbing.

In this article we’ll look at #include Test-Double as a way to break dependencies on a problem #include file.

You have a header file, provided by your silicon vendor, that depends on the target. This is similar to one I ran into last week:

/*
 *  ---- acmetypes.h ----
 */
 
#ifndef _ACME_STD_TYPES
#define _ACME_STD_TYPES
 
#if defined(_ACME_X42)
    typedef unsigned int        Uint_32;
    typedef unsigned short      Uint_16;
    typedef unsigned char       Uint_8;
 
    typedef int                 Int_32;
    typedef short               Int_16;
    typedef char                Int_8;
 
#elif defined(_ACME_A12)
    typedef unsigned long       Uint_32;
    typedef unsigned int        Uint_16;
    typedef unsigned char       Uint_8;
 
    typedef long                Int_32;
    typedef int                 Int_16;
    typedef char                Int_8;
#else
    #error <acmetypes.h> is not supported for this environment
#endif
 
#endif  /* _ACME_STD_TYPES */

The C preprocessor stops when it gets to the #error. You might think you can just define _ACME_X42 or _ACME_A12, but you are testing off-target, and likely on a 64 bit machine (as I am at the time of this writing), and that can lead to problems where int size matters.

It is always best to include the production header file, but sometimes that won’t work, especially in legacy situations. We don’t have to give up though. We can employ a #include Test-Double.

In this case it will be pretty easy. We can define a new header file with the same name, and put it in the include path in front of the production code include. #include Test-Double looks like this:

/*
 * acmetypes.h - test double for off target testing
 */
 
#ifndef ACMETYPES_H_
#define ACMETYPES_H_
 
#include <stdint.h>
 
typedef uint32_t		Uint_32;
typedef uint16_t		Uint_16;
typedef uint8_t			Uint_8;
 
typedef int32_t			Int_32;
typedef int16_t			Int_16;
typedef int8_t			Int_8;
 
#endif /* ACMETYPES_H_ */

Off target you have your development system’s native implementation of stdint.h. It is the standard way to deal with specific sized ints when you must. Above we just redefined the Acme types in therms of the portable types.

If you are using CppUTest and its makefile system, you can direct the makefile to look at your test-double header files before the production code header files, like this:

INCLUDE_DIRS =\
  .\
  include \
  include/* \
  $(CPPUTEST_HOME)/include/ \
  mocks/includes \
  $(ACME_INCLUDES) \

Point $(ACME_INCLUDES) at the silicon vendor dependent include directory. Place the test-double header file in some directory like mocks/includes. As long as mocks/includes is before $(ACME_INCLUDES) in the INCLUDE_DIRS, the test-double will take the place of the production code header file. During the production build mocks/includes will not be part of the include path.

You don’t need to use CppUTest’s makefile system for this. All compilers I am aware of use the include path approach.

To make sure this all works as we expect, we can also add this test case.

extern "C"
{
#include "acmetypes.h"
}
 
#include "CppUTest/TestHarness.h"
 
TEST_GROUP(acmetypes) {};
 
TEST(acmetypes, checkIntSizes)
{
	LONGS_EQUAL(1, sizeof(Uint_8));
	LONGS_EQUAL(1, sizeof(Int_8));
	LONGS_EQUAL(2, sizeof(Uint_16));
	LONGS_EQUAL(2, sizeof(Int_16));
	LONGS_EQUAL(4, sizeof(Uint_32));
	LONGS_EQUAL(4, sizeof(Int_32));
}

There are other situations where a #include stub can be used. For example break a long chain of #includes where you just need a couple symbols. But keep in mind, it is still better to use the production code header when you can.

If this helped, tell me about it. If I’m missing something, tell me that too.

One thought on “#include Test Double

Leave a Reply

Your email address will not be published. Required fields are marked *

Be gone spammers * Time limit is exhausted. Please reload the CAPTCHA.