Cpputest

CppUTest unit testing and mocking framework for C/C++

Download Release 3.8 as .zip Download Release 3.8 as .tar.gz

CppUTest plugins can be installed in the main and ‘extend’ the unit test framework. A plugin is a place where you can put work that needs to be done in all unit tests.

Table of Content

SetPointerPlugin

Description

The SetPointerPlugin provides a Pointer restore mechanism - helpful when tests overwrite a pointer that must be restored to its original value after the test. This is especially helpful when a pointer to a function is modified for test purposes.

Example

int main(int ac, char** av)
{
    TestRegistry* r = TestRegistry::getCurrentRegistry();
    SetPointerPlugin ps("PointerStore");
    r->installPlugin(&ps);
    return CommandLineTestRunner::RunAllTests(ac, av);
}

TEST_GROUP(HelloWorld)
{
   static int output_method(const char* output, ...)
   {
      va_list arguments;
      va_start(arguments, output);
      cpputest_snprintf(buffer, BUFFER_SIZE, output, arguments);
      va_end(arguments);
      return 1;
   }
   void setup()
   {
      // overwrite the production function pointer witha an output method that captures
      // output in a buffer.
      UT_PTR_SET(helloWorldApiInstance.printHelloWorld_output, &output_method);
   }
   void teardown()
   {
   }
};

TEST(HelloWorld, PrintOk)
{
   printHelloWorld();
   STRCMP_EQUAL("Hello World!\n", buffer)
}

// Hello.h
#ifndef HELLO_H_
#define HELLO_H_

extern void printHelloWorld();

struct helloWorldApi {
   int (*printHelloWorld_output) (const char*, ...);
};

#endif

// Hello.c

#include <stdio.h>
#include "hello.h"

// in production, print with printf.
struct helloWorldApi helloWorldApiInstance = {
   &printf
};

void printHelloWorld()
{
   helloWorldApiInstance.printHelloWorld_output("Hello World!\n");
}

MockSupportPlugin

MockSupportPlugin makes the work with mocks easier. It does the following work for you automatically:

Installing the MockPlugin means you’ll have to add to main something like:

#include "CppUTest/TestRegistry.h"
#include "CppUTestExt/MockSupportPlugin.h"

MyDummyComparator dummyComparator;
MockSupportPlugin mockPlugin;

mockPlugin.installComparator("MyDummyType", dummyComparator);
TestRegistry::getCurrentRegistry()->installPlugin(&mockPlugin);

This code creates a comparator for MyDummy and installs it at the plugin. This means the comparator is available for all test cases. It creates the plugin and installs it at the current test registry. After installing the plugin, you don’t have to worry too much anymore about calling checkExpectations or cleaning your MockSupport.

IEEE754ExceptionsPlugin

Description

This plugin detects floating point error conditions and fails the test, if any were found. According to the IEEE754 Floating Point Standard, floating point errors do not by default cause abnormal program termination. Rather, flags are set to indicate a problem, and the operation returns a defined value such as Infinity or Not-a-Number (NaN).

This is a list of floating point error conditions, and how they are supported by the plugin:

FE_DIVBYZERO   /* supported (v3.8) */
FE_OVERFLOW    /* supported (v3.8) */
FE_UNDERFLOW   /* supported (v3.8) */
FE_INVALID     /* supported (v3.8) */
FE_INEXACT     /* supported; disabled by default (v3.8) */
FE_DENORMAL    /* NOT supported (v3.8) */

You can turn on FE_INEXACT checking manually, although this probably won’t be very useful most of the time, since almost every floating-point operation is likely to set this flag:

IEEE754ExceptionsPlugin::enableInexact();
IEEE754ExceptionsPlugin::disableInexact();

Example

#include "CppUTest/CommandLineTestRunner.h"
#include "CppUTest/TestRegistry.h"
#include "CppUTestExt/IEEE754ExceptionsPlugin.h"

int main(int ac, char** av)
{
    IEEE754ExceptionsPlugin ieee754Plugin;
    TestRegistry::getCurrentRegistry()->installPlugin(&ieee754Plugin);
    return CommandLineTestRunner::RunAllTests(ac, av);
}

static volatile float f;

TEST_GROUP(CatchFloatingPointErrors)
{
    void setup()
    {
        IEEE754ExceptionsPlugin::disableInexact();
    }
};

TEST(CatchFloatingPointErrors, underflow)
{
    f = 0.01f;
    while (f > 0.0f) f *= f;
    CHECK(f == 0.0f);
}

TEST(CatchFloatingPointErrors, inexact) {
    IEEE754ExceptionsPlugin::enableInexact();
    f = 10.0f;
    DOUBLES_EQUAL(f / 3.0f, 3.333f, 0.001f);
}

The output of these tests will be:

$ ./example.exe
example.cpp:29: error: Failure in TEST(CatchFloatingPointErrors, inexact)
src/CppUTestExt/IEEE754ExceptionsPlugin.cpp:164: error:
        IEEE754_CHECK_CLEAR(FE_INEXACT) failed
.
example.cpp:22: error: Failure in TEST(CatchFloatingPointErrors, underflow)
src/CppUTestExt/IEEE754ExceptionsPlugin.cpp:164: error:
        IEEE754_CHECK_CLEAR(FE_UNDERFLOW) failed
.
Errors (2 failures, 2 tests, 2 ran, 12 checks, 0 ignored, 0 filtered out, 39 ms)
$  

Debugging floating point failures

When a test fails due to a floating point error, it can be challenging to find the location of the offending operation, since the plugin has no knowledge of where the flag was originally set. To aid in debugging, there is are a number of static methods you can use to set up a watch:

IEEE754ExceptionsPlugin::checkIeee754OverflowExceptionFlag();
IEEE754ExceptionsPlugin::checkIeee754UnderflowExceptionFlag();
IEEE754ExceptionsPlugin::checkIeee754InexactExceptionFlag();
IEEE754ExceptionsPlugin::checkIeee754DivByZeroExceptionFlag();

Here is an minimal example using Gdb, example.cpp:

#include "CppUTest/TestHarness.h"
#include "CppUTestExt/IEEE754ExceptionsPlugin.h"

static volatile float f = 1.0;
static volatile IEEE754ExceptionsPlugin plugin;   // Make sure this is linked, so the debugger knows it

int main(int, char**) {
    f /= 0.0f;           // the offending statement
    return 0;
}

1) Compile the example. Your command line will look roughly like this:

$ g++ -Wextra -Wall -Werror -g3 -O0 -std=c++11 -Iinclude -Llib example.cpp -lCppUTest -lCppUTestExt -o example.exe

2) Start the example in Gdb:

$ gdb ./example.exe
GNU gdb (GDB) 7.6.50.20130728-cvs (cygwin-special)
#...more gdb output here...
Reading symbols from /cygdrive/c/data/00_Dev/06_Faking/MiscellanousTests/example.exe...done.
(gdb)

3) Set a breakpoint and run the example:

(gdb) break main
Breakpoint 1 at 0x4011ab: file example.cpp, line 8.
(gdb) run
Starting program: /cygdrive/c/data/00_Dev/06_Faking/MiscellanousTests/example.exe
[New Thread 6476.0x2038]
[New Thread 6476.0x239c]

Breakpoint 1, main () at example.cpp:8
8           f /= 0.0f;
(gdb)

4) Set up the watch you need:

(gdb) watch IEEE754ExceptionsPlugin::checkIeee754DivByZeroExceptionFlag()
Watchpoint 2: IEEE754ExceptionsPlugin::checkIeee754DivByZeroExceptionFlag()
(gdb)

5) Stepping over the offending statement will change the value of your watch:

(gdb) next
Watchpoint 2: IEEE754ExceptionsPlugin::checkIeee754DivByZeroExceptionFlag()

Old value = false
New value = true
0x004011b5 in main () at example.cpp:8
8           f /= 0.0f;
(gdb)

Of course you don’t have to use commandline Gdb to do this; you can debug your code from within your favorite IDE (Eclipse, Code::Blocks, …) following basically the same procedure.