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.

Damn, I used to think I was good at programming

I used to think I was good at programming. Since I started doing TDD in 1999, I’ve come to discover that I am not so good. I make a lot of little mistakes. Copy-paste is a specifically error-prone activity for me. When I copy and paste and N changes are needed, on a good day I can usually get N-1 on the first try.

When I’m almost done, I get a little careless. I’m not the only one! Take a look at this analysis of the Last Line Effect. He show lots of copy-paste bug examples from real code from products you know. In the article the author also mentions this may not be just programming.

mountain-climbers often fall off in the last few dozen meters of their ascent. Not because they are tired; they are simply too joyful about almost reaching the top – they anticipate the sweet taste of victory, get less attentive, and make some fatal mistake. I guess something similar happens to programmers.

I’ve watched a lot of programmers program in training context. They are not so good at programming either. Little mistakes are the norm, not the exception. But programmers generally don’t know how error prone their work is, because the feedback loop is too long pre-TDD. TDD puts the mistakes in your face so you can find them before they hatch into bugs.

With the mistakes in your face, you can certainly see them and fix them. You might, though awareness, start to do them less as you work on your weakness.

Cause and Effect – Foundation of TDD

Virtually every line of code is written in response to some failure. It often is a test failure, but might be a compilation failure, or a web page that does not exist.

Through my 16 years of practicing TDD, I have come to discover that establishing cause and effect is critical to getting every line of code right. I’ve done most my TDD in embedded C and C++.

Over the last couple years, with the help of a couple friends, I built my ruby on rails website. I did not know ruby (or rails obviously). I know it a little bit now. I used to follow the several step procedures to bring up a new feature on my website. I would do the 3–6 steps outlined, and my new page would not come up. I’d spend considerable time trying to find the mistake and eventually get the thing to work. It would have involved several changes. The next day I would likely run into another problem because one of the changes was not really needed and I broke something else. In hindsight, I made changes without establishing cause and effect. My new feature started working often due to offsetting defects.

I started treating each step in the procedure as a test. For example If I tried to bring up a web page, that did not exist, what error would I get? I would look for a way to make one of the changes in the several step procedure and get a different error message. Getting new errors was not, but was it the right error? I learned to get the right error that moved my code in the right direction. I establish cause and effect. Cause and effect?! I am programming as an engineer. What is the problem? Can I impact that problem positively, and move my code in the right direction. Sometimes this cause and effect involves TDD tests, other times it is wiring code that once I get it right, I leave it alone

I just found this definition of engineering “skillful or artful contrivance; maneuvering.” I’d say that is the ideal I strive for in building software and a business. Yes, TDD effects how I run my business. TDD is built on cause and effect. Engineering! I doubt I would appreciate the cause and effect approach to programming (and life) if it were not for TDD.

TDD Guided by ZOMBIES

Have you had a hard time figuring out where to start with Test-Driven Development. What if ZOMBIES could help you build code that does exactly what you think it is supposed to do? What if ZOMBIES at the same time help you to build a test harness that can help you keep your code clean and behaving properly for a long and useful life? What if ZOMBIES could help!

I’m not talking about those zombies! ZOMBIES is an acronym.
Continue reading

Preventing Brittle Tests (and Production Code)

Have you written unit tests only later to find they slow you down because your changes cause a lot of test breakage? You think, these tests are not living up to the promises I heard. So you toss the tests and go back to business as usual.

Hey, not so fast. Maybe its not test in general, but your tests or your code. The first project I used TDD on in 1999/2000 we ran into this problem. Some were ready to give up. But I wanted the tests to work and looked for what was wrong. In our excitement we kept copying, pasteing and tweaking the test cases. They were an ugly (in hindsite) mass of duplication. Small changes made for ripple effects through the tests. But I could see, it was our fault.

Someone on Quora asked me “How do you make unit tests less brittle”. Here is a more complete answer based on having written my own bad tests and seen many learners of TDD and unit testing go the wrong direction with their designs and tests.
Continue reading

Why do engineers dislike Agile?

Being one of the people that participated in the creation of the Agile Manifesto, I find myself very disappointed by the reaction of engineers to the question “Are you practicing Agile?” Their shoulders drop. They start to slowly shake their heads. They mumble; they grumble. They tell me agile is horrible. I ask why. Reasons I head most often are:
Continue reading

I Object to Excessive Debug Code

I saw this picture on top of an article about some new web technology. What strikes me is that most this code has to do with debugging. It looks to me the programmers maintaining this code have sprinkled debug statements through the code to see what the code is doing.

Its Friday night, do you know what your code is doing?
Continue reading

Faking it, FFF and Variadic Macro Madness

I spend the day updating the Fake Function Framework, Mike Long’s (@meekrosoft) creation. With my client last week, one of the engineers had some problems with Visual Studio (don’t we all) and the Fake Function Framework (a.k.a FFF). I promised to integrate the changes into the FFF. The FFF is created with a ruby script, but you don’t have to care about that unless you have functions to fake that have more than 10 parameters. But anyway, I spent the day updating and reviewing the ruby script that generates the FFF.

Continue reading

TDD’s Impact on Testers

As programmers adopt test-driven development, they are going to prevent many defects that would have escaped to testers. Programmers that write unit tests just after programming are going to find many of the defects that would have escaped. Keep in mind that the programmer tests only check what the programmer thinks the code is supposed to do. The tests do not assure the code meets the customers’ needs. But making sure the code is doing what the programmer thinks it is supposed to do is needed for high quality systems. They work on purpose, rather than by the accident of offsetting defects.

Testers, and the whole team, are responsible for making sure the product does what they think the customer needs. These are different tests, they cover more code per test as well as checking the interactions between modules. As testers adopt automated testing, they need different skills than the manual tester that is following a script or exploring. Some adopt Acceptance Test Driven Development. For testers in this environment, the world is changing. Changing for the better with the intellectual challenges of automation, keeping the cost of retest low, and reducing the repetitive, error prone and boring manual tests. Not all manual tests go away, but we work to automate what is repetitive. In addition ATDD moves testers upstream from reacting to the code written to specifying system behavior by example.

All that said, most of the world of software development still practices Debug Later Programming and manual test, the same practices I used in my first job as a programmer. Congratulations for using 1979 programming techniques! We’ve learned a lot since then.

For some adopting TDD and ATDD is changing their world. For most it is not. There is so much legacy code out there, as well as people and organizations that do not know how or why to automate their tests, that the status quo of testing will likely have a long life.

I’ve got integration and system tests, why do I need unit tests?

Kent Beck told me years ago, if the code does not need to work, then there is no need to test it. He continued and observed, why bother writing it if it does not need to work. Since hearing that and discovering how frequently I make coding mistakes, I want thorough tests.

Maybe you are asking yourself “I’ve got integration and system tests, why do I need unit tests?”. The answer is simple, simple math.
Continue reading