Linker Substitution in C – Limitations and Workarounds

Let’s say you have code that in some test cases you want the real code and other test cases you want a test double. In C, using link-time binding, you only get to have a single function with a specific name.

I’d suggest that most the time this comes up you want to directly test some module (or function) and you want a substitute when testing code that depends on the module’s interface.

What options do you have?

  • Create multiple test runners, where one test runner would use the real function and the other test runner would use the test double.
  • Function pointer substitution.
  • #include the .c file in a namespace in the test file.
  • Linker wrapping.
  • Weak linking.

Multiple test runners

This might be a decent solution. Though it can lead to needing many test runners.

Function pointer substitution

Changing a direct function call to an indirect function call is a small change, and the compiler and linker will make sure you get it right. There are a couple examples in my book, in chapter nine. Here’s one of them.

// RandomMinute.h -- original
int RandomMinute_Get(void);
// RandomMinute.c -- original

int RandomMinute_Get(void)
{
    return bound - rand() % (bound * 2 + 1);
}
// RandomMinute.h - revised
extern int (*RandomMinute_Get)(void);
// RandomMinute.c - revised

static int __randomMinute_Get(void)
{
    return bound - rand() % (bound * 2 + 1);
}

int (*RandomMinute_Get)(void) = __randomMinute_Get;

That refactor is pretty easy, but you must make sure all users of RandomMinute_Get() include the header file. If you happen to be lazy about calling functions without having them advertised in a header file, the caller will think this is a direct function call. Bad things will happen.

In the test group file, write FakeRandomMinute_Get().

//Test file for SomethingThatUses_RandomMinute_Get

extern "C"
{
    static randomMinuteValue;
    static randomMinuteIncrement;
    static void FakeRandomMinute_Init(int first, int increment)
    {
        randomMinuteValue = first;
        randomMinuteIncrement = increment;
    }

    static int FakeRandomMinute_Get(void)
    {
        int value = randomMinuteValue;
        randomMinuteValue += randomMinuteIncrement;
        return value
    }
}

in setup() override RandomMinute_Get() with FakeRandomMinute_Get().

//Test file for SomethingThatUses_RandomMinute_Get - Continued

TEST_GROUP(SomethingThatUses_RandomMinute_Get)
{
    void setup()
    {
        //other setup
        FakeRandomMinute_Init(-30, 3);
        UT_PTR_SET(RandomMinute_Get, FakeRandomMinute_Get);
    }
}
    void teardown()
    {
    }
}

UT_PTR_SET overrides the function pointer and then restores its original value after teardown(). Your code under test now has predcitable random minutes.

In another test group, the production RamdomMinute_Get() function can be tested without overriding the function pointer.

// RandomMinuteTest.cpp

TEST_GROUP(RandomMinute) {}

TEST(RandomMinute, GetIsInRange)
{
    for (int i = 0; i < 100; i++)
    {
        minute = RandomMinute_Get();
        AssertMinuteIsInRange();
    }
}

But maybe you don’t want to use a function pointer. You might prefer this next solution.

#include the .c file in a C++ namespace

In this solution, the test-double’s function name in FakeRandomMinute.c is the same as the function being faked, business as usual for link-time substitution.

// FakeRandomMinute.h
#include "RandomMinute.h"
void FakeRandomMinute_Init(int first, int increment);
// FakeRandomMinute.c
#include "FakeRandomMinute.h"

void FakeRandomMinute_Init(int first, int increment)
{
    randomMinuteValue = first;
    randomMinuteIncrement = increment;
}

int RandomMinute_Get(void)
{
    int value = randomMinuteValue;
    randomMinuteValue += randomMinuteIncrement;
    return value
}

To test the production implementation, the the RandomMinute.c file can be #included in the test file. In addition, the #include needs to be wrapped in a C++ namespace to keep the name out of the global namespace.

// in your test file

namespace Test
{
    #include "RandomMinute.c"
}

In the test cases, make calls to Test::RandomMinute_Get().

TEST(RandomMinute, GetIsInRange)
{
    for (int i = 0; i < 100; i++)
    {
        minute = Test::RandomMinute_Get();
        AssertMinuteIsInRange();
    }
}

Linker Wrapping

The linker tricks are not generally available except from gcc. I’ve experimented with wrapping in gcc., but could not find it in visual studio or IAR (at the time I looked).

In gcc, wrap RamdomMinute_Get() with these command line options:

gcc -Wl,--wrap=RamdomMinute_Get ...

The linker will redirect calls to RamdomMinute_Get() to __wrap_RamdomMinute_Get(). You can get at there production implementation by calling __real_RamdomMinute_Get(). You need to provide __wrap_RamdomMinute_Get() to hold your fake implimentation and to keep the linker happy.

int __wrap_RamdomMinute_Get()
{
    // some fake implementation
}

Maybe some test cases want the real RamdomMinute_Get(), you could make the wrapped function default to the real with function.

static int (*customRamdomMinute_Get)() = 0;
int __wrap_RamdomMinute_Get()
{
    if (customRamdomMinute_Get)
            return customRamdomMinute_Get();
    return __real_RamdomMinute_Get();
}

I chose to use a function pointer for test specific behavior. This lets you have a generic fake implemention that is customized close to your test case.

There are some limitations so you may want to look at this Stack Overflow article for linker wrapping

Weak Linking

I’d say stay away from weak linking as it means adding non-standard stuff to your producion C code.

Finally

You can mix and match these techniques. Let me know how this works for you or if you know some other solutions to this problem.

Accessing static Data and Functions in Legacy C — Part 2

Maybe you read Part 1 of this article. If you did you’ll know it concerns adding tests to legacy code (legacy code is code without tests). You will also know that the code has file scope functions and data that we want to test directly.

My opinion on accessing private parts of well designed code, is that you do not need to. You can test well design code through its public interface. Take it as a sign that the design is deteriorating when you cannot find a way to fully test a module through its public interface.

Part 1 showed how to #include the code under test in the test file to gain access to the private parts, a pragmatic thing to do when wrestling untested code into a test harness. This article shows another technique that may have an advantage for you over the technique shown in Part 1. Including the code under test in a test case can only be done once in a test build. What if you need access to the hidden parts in two test cases? You can’t. That causes multiple definition errors at link time.

This article shows how to create a test access adapter to overcome that problem.
Continue reading

Accessing static Data and Functions in Legacy C — Part 1

And a Happy Leap Year Bug

It’s a new year; last year was a leap year; so the quadrennial reports of leap year bugs are coming in. Apologies are in the press from Apple, TomTom, and Microsoft. Trains we stopped from running in China. Somehow calling them glitches seems to make it someone else’s fault, something out of their control. How long have leap years been around? Julius Caesar introduced Leap Years in the Roman empire over 2000 years ago. The Gregorian calendar has been around since 1682. This is not a new idea, or a new bug.

I’m going to try to take one excuse away from the programmers that create these bugs by answering a question that comes up all the time, “How do I test static functions in my C code?”
Continue reading

Unit testing RTOS dependent code – RTOS Test-Double – Part 3

I’m going to bend the example a little, and look at how to write tests that interact with an interrupt service routine and again with a semaphore. I’ll also get some help with the tedious parts of building fakes by using Mike Long’s Fake Function Framework. (BTW: I am using a beta version, that has changes the released version does not yet have.)

This figure shows the concurrent runtime relationship between the MessageProcessor and its SerialDriver.


Continue reading

Unit testing RTOS dependent code – RTOS Test-Double – Part 2

In the last article, the OSSemPend() test-double was coded to handle a specific OSSemPend() application and test need. The semaphore was being used to signal when there is a message to process. It was the first need for a OSSemPend() test double and was quickly developed. As more RTOS dependent code is brought under test, a more general solution will be needed.

In this article, we’ll look at a test double that can be customized for each application.
Continue reading

Unit testing RTOS dependent code – RTOS Test-Double

When you’ve got legacy code that depends on the Real-time Operating System, you have a challenge to get your code off the target for unit testing. If you want to test with the concurrency provided by the RTOS, these are not really unit tests, and you won’t be able to write thorough tests, and you need unit tests to be through.

You’re going to need to make a test-double so you can get your code off the RTOS and into your development system for unit testing. In this article we’ll go through the steps to get started.

Continue reading

Hiding Non-standard C Keywords for Off-Target Testing

Some silicon vendors extend the C language so the programmers can easily interact with the silicon. Using these extensions tie production code to the silicon vendors compiler and consequently the code can only run on the target system. This is not a problem during production, but is a problem for off-target unit testing.

The good news is that we may be able to get around this problem without having to change production code, one of our goals when adding tests to legacy code.
Continue reading

#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.

Continue reading