Guides – need help? here you find your answers

CUTE Framework Guide

How Things Work

I tried to create a simple, orthogonal and thus easy to extend and adapt testing framework that stays easy to use. I avoided some complexity for users of CUTE by exploiting modern C++ library features in the Boost library that is part of the std::tr1 standard.

Note that all classes presented below are in namespace cute, which is omitted for the sake of brevity.

CUTE Test

The core class stores test functions using std::function. With std::function, any parameterless function or functor can be a test. In addition, each cute::test has a name for easier identification. That name is given either during construction or derived from a functor’s typeid. The GNU g++ compiler requires that you demangle the name given by the type_info object, while VC++ provides a human readable type_info::name() result directly.

struct test{
    void operator()()const{ theTest(); }
    std::string name()const{ return name_;}

    template <typename VoidFunctor>
    test(VoidFunctor const &t, std::string sname = demangle(typeid(VoidFunctor).name()))
        :name_(sname),theTest(t){}

    template <typename VoidFunctor>
    test(std::string sname,VoidFunctor const &t)
        :name_(sname),theTest(t){}

    private:
        std::string name_;
        std::function<void()> theTest;
};

As you can see, there is no need to inherit from class test.

For simple functions, or when you want to name your tests differently from the functor’s type, you can use the CUTE() macro:

#define CUTE(name) cute::test((&name),(#name))

CUTE is a function-like macro that takes the name of a test function and instantiates the test class with the address of that test function and its name.

Using a template constructor allows you to use any kind of functor that can be stored in a std::function<void()>, but this means that the functor can take no parameters. To construct with functions, functors or member functions with parameters, use std::bind() as shown below.

Sweet Suites

Running a single test with cute::runner is not very interesting. You might as well just call that function directly and check the results. The power of unit testing is realized when you have a larger collection of test cases that run after every compile and on a build server after every check-in. Thus there is a need for running many tests at once.

In contrast to other unit testing frameworks (including JUnit) I refrained from applying the Composite design pattern [GoF] for implementing the test case container. I love Composite and it is handy in many situations for tree structures, but it comes at the price of strong coupling by inheritance and lower cohesion in the base class, because of the need to support the composite class’ interface. The simplest solution I came up with is to simply represent the test suite as a std::vector<cute::test>. Instead of a hierarchy of suites, you just run a sequence of tests. When the tests run, the hierarchy plays no role. You still can arrange your many tests into separate suites, but before you run them, you either concatenate the vectors or you run the suites individually in your main() function using the runner.

Tests can be added to the suite using vector::push_back(), but to make it really easy to fill your suite with tests, CUTE also provides an overloaded operator+= that will append a test object to a suite:

typedef std::vector<test> suite;
suite &operator+=(suite &left, suite const &right);
suite &operator+=(suite &left, test const &right);

This idea is blatantly stolen from boost::assign.

So this is all it takes to build a test suite:

suite s;
s += TestFunctorA{};
s += CUTE(testFunctionB);
// and so on    ...

If you really want to organize your test as a sequence of test suites, CUTE provides a suite_test functor that will take a test suite and run it through its call operator. However, if any test in a suite_test fails, the remaining tests will not be run.

CUTE’s Eclipse plug-in eases the construction of test suites by providing automatic code generation and adjustment for registering test functions in suites. You can have standalone CUTE executables for a single suite, or test multiple suites, each in a separate library project.

Assertions and Failures

A unit testing framework would not be complete without a way to actually check something in a convenient way. One principle of testing is to fail fast, so any failed test assertion will abort the current test and signal the failure to the top-level runner. You might have already guessed that this is done by throwing an exception. Later on, we will want to know where that test failed, so I introduced an exception class test_failure that takes the source file name and line number in the source file. Java does this automatically for exceptions, but as C++ programmers we must obtain and store this information ourselves. We rely on the preprocessor to actually know where we are in the code. Another std::string allows sending additional information from the test programmer to the debugger of a failing test.

This is how cute_base.h looks without the necessary #include guards and #include <string>:

struct test_failure {
        std::string reason;
        std::string filename;
        int lineno;
        test_failure(std::string const &r,char const *f, int line)
        :reason(r),filename(f),lineno(line)
        { 	}
        char const * what() const { return reason.c_str(); }
};

For actually writing test assertions, I provided macros that will throw if a test fails:

#define ASSERTM(msg,cond) do { if (!(cond)) \
    throw cute::test_failure( \
        CUTE_FUNCNAME_PREFIX+cute::cute_to_string::backslashQuoteTabNewline(msg), \
        __FILE__,__LINE__); \
    } while(false)
#define ASSERT(cond) ASSERTM(#cond,cond)
#define FAIL() ASSERTM("FAIL()",false)
#define FAILM(msg) ASSERTM(msg,false)

This is all you need to get started. However, some convenience is popular in testing frameworks. Unfortunately, convenience often tends to be over-engineered and I am not yet sure if the convenience functionality I provided is yet simple enough. Therefore I ask for your feedback on how to make things simpler or confirmation that it is already simple enough.

Testing for Equality

Testing two values for equality is probably the most popular test. Therefore, all testing frameworks provide a means to test for equality. JUnit, for example, provides a complete set of overloaded equality tests. C++ templates can do that as well with less code. For more complex data types, such as strings, it can be difficult to see the difference between two values, when they are simply printed in the error message.

void anotherTest(){
    ASSERT_EQUAL(42,lifeTheUniverseAndEverything);
}

One means to implement ASSERT_EQUAL would be to just #define it to map to ASSERT((expected)==(actual)). However, from my personal experience of C++ unit testing since 1998, this gives too little information when the comparison fails. This is especially true for strings or domain objects, where seeing the two unequal values is often essential for correcting the programming mistake. In my former life, we had custom error messages for a failed string comparison that allowed us to spot the difference easily. Therefore, CUTE provides a template implementation of ASSERT_EQUAL. This is of course called by a macro to enable file position reporting.

I speculated (perhaps wrongly) that it would be useful to specify your own mechanism to create the message if two values differ, which is implemented as a to-be-overloaded interface in the namespace cute::cute_to_string:

namespace cute_to_string {
    template <typename T>
    std::string to_string(T const &t) {
        std::ostringstream os;
	to_stream(os,t);
	return os.str();
    }
    // common overloads of interface that work without an ostream
    static inline std::string to_string(char const *const &s){
        return s;
    }
    static inline std::string to_string(std::string const &s){
	return s;
    }
}

Your overloaded to_string function is then called in diff_values which composes the standard message for your failed test case…

template <typename ExpectedValue, typename ActualValue>
std::string diff_values(ExpectedValue const &expected
					, ActualValue const & actual
					, char const *left="expected"
					, char const *right="but was"){
    // construct a simple message...to be parsed by IDE support
    std::string res;
    res += ' ';
    res += left;
    res += ":\t" + cute_to_string::backslashQuoteTabNewline(cute_to_string::to_string(expected))+'\t';
    res += right;
    res +=":\t"+cute_to_string::backslashQuoteTabNewline(cute_to_string::to_string(actual))+'\t';
    return res;
}

…and which is called in case your ASSERT throws a test_failure.

template <typename ExpectedValue, typename ActualValue>
void assert_equal(ExpectedValue const &expected
			,ActualValue const &actual
			,std::string const &msg
			,char const *file
			,int line) {
    typedef typename impl_place_for_traits::is_integral<ExpectedValue> exp_integral;
    typedef typename impl_place_for_traits::is_integral<ActualValue> act_integral;
    if (cute_do_equals::do_equals(expected,actual,exp_integral(),act_integral()))
        return;
    throw test_failure(msg + diff_values(expected,actual),file,line);
}
#define ASSERT_EQUALM(msg,expected,actual) cute::assert_equal((expected),(actual), \
    CUTE_FUNCNAME_PREFIX+cute::cute_to_string::backslashQuoteTabNewline(msg),__FILE__,__LINE__)
#define ASSERT_EQUAL(expected,actual) ASSERT_EQUALM(#expected " == " #actual, (expected),(actual))

As of version 1.5, CUTE allows all kinds of types to be compared by ASSERT_EQUAL. While earlier versions allowed only types where operator<<(ostream &,TYPE) was defined, some template meta-programming tricks now allow also other types, as long as operator==(expected,actual) is defined and delivers a bool compatible result. For integer types, meta-programming ensures that no signed-unsigned comparison warning is issued anymore. Comparing two floating point values without specifying a delta, automatically selects a delta that masks the least significant decimal digit, based on the size of expected. Floating point comparison subtracts actual and expected and sees if the absolute value of the difference is less than delta, by using std::abs().

Exception Testing

Another good unit testing practice is to verify that things go wrong as intended.

To embed a piece of code (an expression, or anyhting that can be passed as a macro parameter) that should throw a specific exception type, you can use the macro…

ASSERT_THROWS(code,exception_type);

…within your test function. For example:

void test_that_something_throws() {
    ASSERT_THROWS(should_throw_std_exception(),std::exception);
}

This test will fail if should_throw_std_exception() does not throw an exception of type std::exception. Any other exception will lead to an error, in contrast to failure.

There is no need to implement the try-catch again by hand to test error conditions. What is missing is the ability to expect a runtime error recognized by the operating system such as an invalid memory access. Those are usually signaled instead of thrown as a nice C++ exception.

You might need parenthesis around the code in the macro parameter to disambiguate commas, particularly commas in a parameter list.

Listening Customization

You have already seen that the runner class template can be specialized by providing a listener. The runner class is an inverted application of the Template Method design pattern [GoF]. Instead of implementing the methods called dynamically in a subclass, you provide a template parameter that acts as a base class to the class @runner@, which holds the template methods runit() and operator().

template <typename Listener=null_listener>
struct runner{
    Listener &listener;
    std::vector<std::string> args;
    runner(Listener &l, int argc = 0, const char *const *argv = 0):listener(l){
        if(needsFiltering(argc,argv)){
            args.reserve(argc-1);
            std::remove_copy_if(argv + 1, argv + argc,back_inserter(args),std::logical_not<char const *>());
        }
    }
    bool operator()(const test & t) const
    {
        return runit(t);
    }

    bool operator ()(suite const &s, const char *info = "") const
    {
        runner_aux::ArgvTestFilter filter(info,args);
        bool result = true;
        if(filter.shouldrunsuite){
            listener.begin(s, info,
                count_if(s.begin(),s.end(),boost_or_tr1::bind(&runner_aux::ArgvTestFilter::shouldRun,
                    filter,boost_or_tr1::bind(&test::name,_1))));
            for(suite::const_iterator it = s.begin();it != s.end();++it){
                if (filter.shouldRun(it->name())) result = this->runit(*it) && result;
            }
            listener.end(s, info);
        }
        return result;
    }
private:
    bool needsFiltering(int argc, const char *const *argv) const
    {
        return argc > 1 && argv ;
    }

    bool runit(const test & t) const
    {
        try {
            listener.start(t);
            t();
            listener.success(t, "OK");
            return true;
        } catch(const cute::test_failure & e){
            listener.failure(t, e);
        } catch(...) {
            listener.error(t,"unknown exception thrown");
        }
    return false;
    }
};

If you look back to runner::runit(), you will recognize that if any reasonable exception is thrown, it would be hard to diagnose the reason for the error. Therefore, I included catch clauses for std::exception, string and char pointers to get information required for diagnosis. The demangling is required for GNU g++ to get a human-readable information from the exception’s class name.

} catch(const std::exception & exc){
    listener.error(t, demangle(exc.what()).c_str());
} catch(std::string & s){
    listener.error(t, s.c_str());
} catch(const char *&cs) {
    listener.error(t,cs);
}

Again I ask you for feedback if doing this seems over-engineered. Are you throwing strings as error indicators?

As you can see, there are a bunch of methods delegated to the base class given as runner’s template parameter (begin, end, start, success, failure, error). The default template parameter null_listener applies the Null Object design pattern and provides the concept all fitting Listener base classes.

struct null_listener{ // defines Contract of runner parameter
    void begin(suite const &, char const * /*info*/, size_t /*n_of_tests*/){}
    void end(suite const &, char const * /*info*/){}
    void start(test const &){}
    void success(test const &,char const * /*msg*/){}
    void failure(test const &,test_failure const &){}
    void error(test const &,char const * /*what*/){}
};

Whenever you need to collect the test results or you want to have a nice GUI showing progress with the tests, you can create your own custom listener.

Again you can stack listeners using an inverted version of a Decorator design pattern [GoF]. Here is an example of an inverted Decorator using C++ templates that counts the number of tests by category:

template <typename Listener=null_listener>
struct counting_listener:Listener{
    counting_listener()
    :Listener()
    ,numberOfTests(0),successfulTests(0)
    ,failedTests(0),errors(0),numberOfSuites(0){}

    counting_listener(Listener const &s)
    :Listener(s)
    ,numberOfTests(0),successfulTests(0)
    ,failedTests(0),errors(0),numberOfSuites(0){}

    void begin(suite const &s, char const *info, size_t size){
        ++numberOfSuites;
        Listener::begin(s,info, size);
    }
    void start(test const &t){
        ++numberOfTests;
        Listener::start(t);
    }
    void success(test const &t,char const *msg){
        ++successfulTests;
        Listener::success(t,msg);
    }
    void failure(test const &t,test_failure const &e){
        ++failedTests;
        Listener::failure(t,e);
    }
    void error(test const &t,char const *what){
        ++errors;
        Listener::error(t,what);
    }
    int numberOfTests;
    int successfulTests;
    int failedTests;
    int errors;
    int numberOfSuites;
};

From the above schema, you can derive your own stackable listener classes, such as a listener that displays in a GUI the progress and results of tests as they run. If you do so, please share your solution.

Member Functions as Tests

With std::bind() at your disposal, it is easy to construct a functor object from a class and its member function. Again this is canned in a macro that can be used like this:

CUTE_MEMFUN(testobject,TestClass,test1);
CUTE_SMEMFUN(TestClass,test2);
CUTE_CONTEXT_MEMFUN(contextObject,TestClass,test3);

The first version uses object testobject, an instance of TestClass, as the target for the member function test1. The second version creates a new instance of TestClass to then call its member function test2 when the test is executed. The last macro provides a means to pass an additional object to TestClass’ constructor when it is incarnated. The idea of incarnating the test object and thus have its constructor and destructor run as part of the test comes from Kevlin Henney and is implemented in Paul Grenyer’s testing framework Aeryn.

The macro CUTE_MEMFUN delegates its work to a template function as follows:

template <typename TestClass>
test makeMemberFunctionTest(TestClass &t,void (TestClass::*fun)(),char const *name){
    return test(boost_or_tr1::bind(fun,boost_or_tr1::ref(t)),demangle(typeid(TestClass).name())+"::"+name);
}
#define CUTE_MEMFUN(testobject,TestClass,MemberFunctionName) \
    cute::makeMemberFunctionTest(testobject,\
        &TestClass::MemberFunctionName,\
        #MemberFunctionName)

When the template function makeMemberFunctionTest is called, it employs std::bind to create a functor object that will call the member function fun on object t. Again we can employ C++ reflection using typeid to derive part of the test object’s name. We need to derive the member function name again using the preprocessor with a macro. In order to also allow const member functions, the template function comes in two overloads, one using a reference (as shown) and the other using a const reference for the testing object.

Test Object Incarnation

I will spare you the details, and just present the mechanism of object incarnation and then calling a member function for the case where you can supply a context object:

template <typename TestClass,typename MemFun, typename Context>
struct incarnate_for_member_function_with_context_object {
    MemFun memfun;
    Context context;
    incarnate_for_member_function_with_context_object(MemFun f,Context c)
    :memfun(f),context(c){}
    incarnate_for_member_function_with_context_object(incarnate_for_member_function_with_context_object const &other)
    :memfun(other.memfun),context(other.context){}

    void operator()(){
        TestClass t(context);
        (t.*memfun)();
    }
};
template <typename TestClass, typename MemFun, typename Context>
test makeMemberFunctionTestWithContext(Context c,MemFun fun,char const *name){
    return test(incarnate_for_member_function_with_context_object<TestClass,MemFun,Context>(fun,c),
        demangle(typeid(TestClass).name())+"::"+name);
}

This allows you to use test classes with a constructor to set up a test fixture and a destructor for cleaning up after the test. This eliminates need to for explicit setUp() and tearDown() methods, as in JUnit.

Example

struct TestClass{
    static int callcounter;
    int i;
    TestClass():i(1){} // for incarnation; setUp
    TestClass(int j):i(j){} // for incarnation; setUp
    ~TestClass(){...} // for destruction; tearDown
    void test1(){
        ++callcounter;
        ASSERT_EQUAL(1,i++);
    }
    void test2() const {
        ++callcounter;
        ASSERT(true);
    }
    void test3() {
        ++callcounter;
        ASSERT_EQUAL(2,i++);
        ++i;
    }
    void test_incarnate(){
        ++callcounter;
        ASSERT_EQUAL(42,i++);
    }
    void test_incarnate_const() const {
        ++callcounter;
        ASSERT_EQUAL(43,i);
    }
};

....

cute::suite s3;
s3 += CUTE_SMEMFUN(TestClass,test1);
s3 += CUTE_SMEMFUN(TestClass,test2);

TestClass context{2};
s += CUTE_CONTEXT_MEMFUN(context, TestClass, test3);

CUTE Eclipse Plug-in Guide

Using the CUTE Eclipse Plug-in

The CUTE Eclipse plug-in integrates the CUTE C++ unit testing framework into the Eclipse CDT C/C++ integrated development environment. This plug-in provides all the important features that Java developers know from the JUnit plug-in:

  • Wizards to initialize and set up new tests
  • Test navigator with green/red bar
  • Diff-viewer for failing tests
  • Rerun functionality for single test (e.g. a failed one)

This page shows how to use the CUTE Eclipse plug-in once it is installed.

Functionality

Create a Project

Select File > New > C++ Project. In the C++ Project dialog, the CUTE Eclipse plug-in provides two new C++ project wizards in addition to those that come with CDT by default:

New Project Wizard

Select the type of CUTE project you want:

  • CUTE Project creates a standalone test project.
  • CUTE Suite Project asks you for a name, and creates a test suite with that name.

Specify the Project name and click Next >. On the following wizard page, you can choose which CUTE headers to use (recommended are the newest ones) and if you want to use Gcov and/or CUTE’s boost-headers (if one of these optional CUTE features was installed). If you specify an existing Eclipse project you want to test, CUTE creates a unit test for that project. Upon clicking Finish, the wizard creates a project containing all the CUTE unit test framework’s source files.

If you did not install Boost in the standard location or use CUTE’s boost-headers, you will need to specify boost’s headers installation location.

All of the wizards create a trivial test in file src/Test.cpp that will get you started. Expand this Test.cpp to create your unit test.

New Project Editor

Test Navigator with Green/Red Bar

To build the project, select the menu Project > Build All. Then, right click on the HelloCute project and select Run As > CUTE Test.

Standard Fail

Modify Test.cpp as shown below to make your unit test succeed.

#include "cute.h"
#include "ide_listener.h"
#include "xml_listener.h"
#include "cute_runner.h"

void thisIsATest() {
	std::string first, second, expected;
	first = "Hello";
	second = "World";
	expected = "Hello World";
	ASSERT_EQUAL(expected, first + " " + second);
}

bool runAllTests(int argc, char const *argv[]) {
	cute::suite s { };
	//TODO add your test here
	s.push_back(CUTE(thisIsATest));
	cute::xml_file_opener xmlfile(argc, argv);
	cute::xml_listener<cute::ide_listener<>> lis(xmlfile.out);
	auto runner = cute::makeRunner(lis, argc, argv);
	bool success = runner(s, "AllTests");
	return success;
}

int main(int argc, char const *argv[]) {
    return runAllTests(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Diff-Viewer for Failing Tests

With Test.cpp modified as follows…

#include "cute.h"
#include "ide_listener.h"
#include "xml_listener.h"
#include "cute_runner.h"

void thisIsATest() {
	std::string first, second, expected;
	first = "Hello";
	second = "World";
	expected = "Hello World";
	ASSERT_EQUAL(expected, first + "    \t  \n" + second);
}

bool runAllTests(int argc, char const *argv[]) {
	cute::suite s { };
	//TODO add your test here
	s.push_back(CUTE(thisIsATest));
	cute::xml_file_opener xmlfile(argc, argv);
	cute::xml_listener<cute::ide_listener<>> lis(xmlfile.out);
	auto runner = cute::makeRunner(lis, argc, argv);
	bool success = runner(s, "AllTests");
	return success;
}

int main(int argc, char const *argv[]) {
    return runAllTests(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Custom Fail

…double clicking at the location of the blue arrow (as shown above) pops up the result comparison.

Diff View Regular

Spaces, tabs and newlines can be turned on.

Diff View Detailed

Assertion Functions

The following assertion macros are available in the CUTE testing framework.

ASSERTM(msg, cond)
ASSERT(cond)
ASSERT_EQUALM(msg, expected, actual)
ASSERT_EQUAL(expected, actual)
ASSERT_EQUAL_DELTAM(msg, expected, actual, delta)
ASSERT_EQUAL_DELTA(expected, actual, delta)
ASSERT_EQUAL_RANGES(expbeg, expend, actbeg, actend)
ASSERT_EQUAL_RANGESM(msg, expbeg, expend, actbeg, actend)
ASSERT_GREATERM(msg, left, right)
ASSERT_GREATER(left, right)
ASSERT_GREATER_EQUALM(msg, left, right)
ASSERT_GREATER_EQUAL(left, right)
ASSERT_LESSM(msg, left, right)
ASSERT_LESS(left, right)
ASSERT_LESS_EQUAL(left, right)
ASSERT_LESS_EQUALM(msg, left, right)
ASSERT_THROWS(code, exc)
ASSERT_THROWSM(msg, code, exc)
FAIL()
FAILM(msg)
ASSERT*_DDT(cond, failure)
ASSERT*_DDTM(msg, cond, failure)
ASSERT_NOT_EQUAL_TO(left, right)
ASSERT_NOT_EQUAL_TOM(msg, left, right)

See Writing and Running CUTE Unit Test Suites for details.

Rerun Individual Tests, Suites or Groups of Tests

From within the CUTE Tests view you can select tests or suites from the tree and let these run individually. If the view was populated from a “Debug as CUTE Test” the re-run will be within the debugger as well.

XML Output

The CUTE framework can generate XML output. While this doesn’t directly link with the CUTE framework, you can click on the generated XML file in the project’s root folder from within CDT and might get Eclipse’s JUnit View if you have installed JDT as well. The XML output might be interesting for you when using hudson or jenkins.

Specify Boost’s Headers Installation Location

Right click on the newly created CUTE project and select Properties. Under C/C++ General->Preprocessor Include Paths, Macros etc., choose CDT User Setting Entries. Click Add… and specify the installation location of the boost headers.

Include Boost

TDD Support

The CUTE plug-in supports the user in creating and running unit tests for C++. Additionally, it provides decent support for Test Driven Development. When following Test Driven Development, the unit tests are written before the implementation. While writing the test cases, much semantic and syntactic information about the tested entities is specified. The CUTE plug-in coding assist supports the developer by generating the stubs as a framework for implementing the functionality.

Features

  • Creating Class Types
  • Creating Constructors
  • Creating (Member) Variables
  • Creating (Member) Functions
  • Creating (Member) Operators
  • Visibility Manipulation
  • Adapting Parameter Lists
  • Creating Namespaces

TDD Tutorial

Let us have a look at the TDD feature. We will introduce its functionality with a step-by-step example. Our objective is to develop a simple calculator.

CUTE Project

Create a CUTE Project in CDT.

New Project Wizard

Note: After creating the project there might be several markers indicating problems in Test.cpp. They will vanish as soon as CDT has finished indexing the symbols of that file.

Generating a Type

First we want to create a Calculator class. We will stick with the mental model of a pocket calculator, always displaying the current value. A member function named value shall return it. The initial value in the calculator is 0. This composes our first unit test:

void testInitialValue() {
    Calculator calc {};
    ASSERT_EQUAL(0, calc.value());
}

As there is already an example test case after creating a new CUTE Project, we can recycle this test by renaming it (Alt+Shift+R when the caret is at the test function name). Then we replace the code in the body with our test code.

Calculator Type Missing

An error marker appears at the line containing Calculator. Hovering the mouse cursor over the marker on the left or over the identifier Calculator reveals the problem: Type ‘Calculator’ cannot be resolved, indicating that at the current position the type Calculator is not known.

Calculator Type Could Not Be Resolved

By clicking this marker or by pressing Ctrl+1, a so called resolution appears:

Create Type Calculator

Selecting this resolution creates an empty type definition for Calculator. The kind of type can directly be specified from a list containing struct (which is default), class and enum.

Struct Calculator

The following code is generated:

struct Calculator {
};

void testInitialValue() {
	Calculator calc {};
	ASSERT_EQUAL(0, calc.value());
}

Generating a Member Function

Generating this empty type stub removed the marker at Calculator. But another marker appeared at the statement calc.value() as the type Calculator does not contain a member function value.

Again, by clicking the marker and selecting the resolution Create member function value, a stub for the corresponding function is generated in the type Calculator.

struct Calculator {
	int value() const {
		return int();
	}
};

Compiling and running the test works now. It even yields a green bar.

Moving the Type

We do not want to have the tested code in the same source files as the test code. Thus we move our implementation of Calculator to its own file.

To achieve this, you have to select the type definition and invoke the Extract to new header file refactoring (Alt+Shift+P).

Extract New Header File

This extracts the type definition Calculator to its own header file.

Calculator Header

An include directive is added to Test.cpp to retain accessibility of Calculator in the test.

Toggling Function Definition

In the new header file we can toggle the definition of @value@ out of the type. Use Toggle Function Definition (Alt+Shift+T) to separate the definition from the declaration of the selected member function.

Toggle Function Definition

If desired, the Toggle Function refactoring can be invoked again, which moves the definition of value to the source file Calculator.cpp. If that file does not exist, it is created.

Calculator Source

To have a proper separation of test and implementation projects, you need to move the files specifying the Calculator type to their own project. Currently, this is not supported by a refactoring we know. Thus we will skip this and stick with one single project.

Generating a Constructor

Now we extend our @Calculator@ type to be constructible with a specific value. To do so we create another test case in @Test.cpp@:

void testSpecifiedStartValue(){
    int startValue { 23 };
    Calculator calc(startValue);
    ASSERT_EQUAL(startValue, calc.value());
}

After writing the code above we encounter a further error marker at the declaration of calc.

No Such Constructor For Type Calculator

Cleary this constructor is missing, as we have no constructor defined for Calculator. The resolution for the problem accomplishes this for us.

Create Constructor Calculator

If we open the Calculator.h file, we see a new constructor defined in Calculator.

struct Calculator {
    Calculator(int& startValue) {
    }

    int value() const;
};

Adding a Member Variable

The new constructor does not do much. We can add a member variable to the initializer list to store the starting value. Of course we do not need to declare it manually. We just add the initialization and receive another marker:

Member Variable Cannot Be Resolved

The following resolution creates the declaration of the member variable in the private section:

Create Member Variable

This is the result:

struct Calculator {
    Calculator(int& startValue) : val { startValue } {
    }

    int value() const;

private:
    int val;
};

If we now change value() to return the val member variable we almost have two green-bar unit tests.

Generating a Default Constructor

In Test.cpp we see another error marker in the first test function. Through the declaration of the new explicit constructor we have removed the implicit default constructor. Our plug-in recognizes that and suggests to create another constructor:

Create Default Constructor

With this resolution we can add a default constructor with one click. We just need to add the initialization of val by hand.

Adding a Test Case to the Suite

There is also a warning marker indicating that we have not yet added this new test case to our test suite in Test.cpp at testSpecifiedStartValue. The plug-in can handle this too:

Add Test To Suite

The resolution adds the test function testSpecifiedStartValue to our test suite s:

bool runAllTests(int argc, char const *argv[]) {
	cute::suite s { };
	s.push_back(CUTE(testInitialValue));
	s.push_back(CUTE(testSpecifiedStartValue));
	cute::xml_file_opener xmlfile(argc, argv);
	cute::xml_listener<cute::ide_listener<>> lis(xmlfile.out);
	auto runner = cute::makeRunner(lis, argc, argv);
	bool success = runner(s, "AllTests");
	return success;
}

Now compiling and running our unit tests results in a green bar for both tests.

Other Cases

The steps described are examples of the capabilities of our plug-in’s TDD features. It can also recognize for example missing operators, local variables and free functions.

Limitations

As it is very complex to provide sensible code stubs for C++ just from the context where an entity is used, it takes quite some effort to achieve flawless code generation. Therefore, feedback is greatly appreciated.

Information about symbols, which is required for reporting errors and providing resolutions, heavily depends on the CDT index to be built completely.

Writing and Running CUTE Unit Test Suites

Here you will learn how to create and run tests for your code using the CUTE C++ unit testing framework. We begin with the initial trivial test src/Test.cpp that is created by the Using the CUTE Eclipse Plug-in.

Source File Organization

Before you start writing tests, you need a plan for organizing your source files.

Single File

If your test is short enough to fit into one file, then you can simply add it to the trivial source file src/Test.cpp provided by CUTE:

#include "cute.h"
#include "ide_listener.h"
#include "xml_listener.h"
#include "cute_runner.h"

// TODO #include the headers for the code you want to test

// TODO Add your test functions

void thisIsATest() {
    ASSERTM("start writing tests", false);	
}

bool runAllTests(int argc, char const *argv[]) {
    cute::suite s { };

    //TODO add your test here

    s.push_back(CUTE(thisIsATest));
    cute::xml_file_opener xmlfile(argc, argv);
    cute::xml_listener<cute::ide_listener<>> lis(xmlfile.out);
    auto runner = cute::makeRunner(lis, argc, argv);
    bool success = runner(s, "AllTests");
    return success;
}

int main(int argc, char const *argv[]) {
    return runAllTests(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Edit this file:

  1. #include the header files for the classes you are testing.
  2. Replace function thisIsATest() with your test functions.
  3. Replace thisIsATest in s.push_back(CUTE(thisIsATest)) with your test functions.

Partitioning Into Multiple Files

Chances are, you will want to partition your tests into multiple files. Generally, it is best to have one test suite for each source file in the project that you are unit testing. The test suite consists of a header (.h) file and an implementation (.cpp) file. Name them consistently. For example, put class myclass in files myclass.cpp and myclass.h, and put the unit test for myclass in myclassTest.cpp and myclassTest.h.

Writing Tests

Code Your Tests Using CUTE Assertions

The test consists of a series of lines that set up some situation to be checked, followed by a CUTE assertion to perform the check.

In your test implementation file (myclassTest.cpp in the above example), include the file that defines the CUTE assertions:

#include "cute.h"

The header cute.h provides a variety of macros you can use to verify conditions. Most assertions have two versions: one version uses the source code of the test itself as the message, and the other allows you to specify your own message msg.

  • ASSERTM(msg, cond)
  • ASSERT(cond)

    If cond is false, the test fails.

  • FAILM(msg)
  • FAIL()

    Fail unconditionally. The message “@FAIL()@” is used if no message is specified.

  • ASSERT_EQUALM(msg, expected, actual)
  • ASSERT_EQUAL(expected, actual)

    If expected and actual are not equal, fail and print the values of expected and actual. Specify an unsigned constant when comparing to unsigned value. For example,

ASSERT_EQUAL(5u, vect.size());

Take care to specify the expected value followed by the actual value, as shown above. If you reverse them, they appear backwards in the failure message.

  • ASSERT_NOT_EQUAL_TOM(msg, left, right)
  • ASSERT_NOT_EQUAL_TO(left, right)

    Fail if left and right are equals.

  • ASSERT_EQUAL_DELTAM(msg, expected, actual, delta)
  • ASSERT_EQUAL_DELTA(expected, actual, delta)

    Fail if expected and actual are different by more than delta. Use this assertion for real numbers.

  • ASSERT_EQUAL_RANGESM(msg, expbeg, expend, actbeg, actend)
  • ASSERT_EQUAL_RANGES(expbeg, expend, actbeg, actend)

    Fail if the ranges defined by expbeg and expend, and actbeg and actend are different.

  • ASSERT_THROWSM(msg, code, exception)
  • ASSERT_THROWS(code, exception)

    Fail if code does not throw exception of type exception.

void test_that_something_throws() {
    ASSERT_THROWS(should_throw_std_exception(),std::exception);
}
  • ASSERT_GREATERM(msg, left, right)
  • ASSERT_GREATER(left, right);
  • ASSERT_GREATER_EQUALM(msg, left, right)
  • ASSERT_GREATER_EQUAL(left, right);
  • ASSERT_LESSM(msg, left, right)
  • ASSERT_LESS(left, right);
  • ASSERT_LESS_EQUALM(msg, left, right)
  • ASSERT_LESS_EQUAL(left, right);

    Fail if left is greater/greater equals/lesser/lesser equals than right.

  • ASSERT*_DDTM(msg, cond, failure)
  • ASSERT*_DDT(cond, failure)

    All the above macros are available with DDT in the macro name. Use these macros to do data driven testing.

Put these assertions in the test implementation file (myclassTest.cpp in the above example).

Collect the Tests In a Test Suite

A CUTE test suite is a vector of tests. The tests are executed in the order in which they were appended to the suite. If an assertion in some test fails, the failure is reported, and the rest of the test is skipped. Execution continues with the next test in the suite. This means that a suite of many short tests is better than a few long tests:

  • With shorter tests, less test code is skipped upon a failure.
  • Each test can fail at most once, so a suite with more tests will show more failures to help you pinpoint bugs.

In the trivial source file provided with CUTE src/Test.cpp, include the test header file for your test. For example,

#include "myclassTest.h"

When the Test Is a Simple Function

If you prefer to write your tests as simple functions, implement the test function, and push it on the test suite using the CUTE() macro:

s.push_back(CUTE(mytestfunction));

When the Test Is a Functor

If you prefer to implement your test as a class or struct, define a functor class in a header file, say myclassTest.h:

// File myclassTest.h

class myclassTest {
public:
    myclassTest();
    // Must define void operator() with no arguments.
    // In implementation: add calls to cute-assert functions and methods like someFunction1
    void operator()();

private:
    // Whatever methods you need
    void someFunction1();
    void someFunction2();

    // Whatever member variables you need
    int memberVar1;
    int memberVar2;
};

Put the implementation of @mytestClass@ in a separate file, like myclassTest.cpp.

Returning to the test suite code (src/Test.cpp), include the test class header file and add the test functor to the test suite:

#include "cute.h"
#include "ide_listener.h"
#include "xml_listener.h"
#include "cute_runner.h"

// TODO #include the headers for the code you want to test
#include "myclassTest.h"
#include "anotherclassTest.h"

bool runAllTests(int argc, char const *argv[]) {
    cute::suite s { };

    //TODO add your test here
    s.push_back(myclassTest{ });
    s.push_back(anotherclassTest{ });

    cute::xml_file_opener xmlfile(argc, argv);
    cute::xml_listener<cute::ide_listener<>> lis(xmlfile.out);
    auto runner = cute::makeRunner(lis, argc, argv);
    bool success = runner(s, "AllTests");
    return success;
}

int main(int argc, char const *argv[]) {
    return runAllTests(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Running the CUTE Test

Compile and execute the test. The tests will be executed in the order in which they were appended to the suite. If an assertion fails, it is reported through the listener, and the test containing the failed assertion is aborted. Execution continues with the next test in the suite.

Adding New Test Functions

Of course you can just add new test functions by hand and and also add them to the test suite that way. CUTE also offers code generators to make these tasks faster and easier.

To add a new test function, place your cursor at the location where the new function should be inserted:

New Test Function Cursor Position

Right-click and select Source > New Test Function.

New Test Function Source Menu

At this point, you can give the new test function a unique name. Note that the test function has automatically been registered in the test suite.

Inserted New Test Function

Adding Test Functions to a Suite

If you write a new test function by hand, you can automatically add it to the test suite as described in the following.

Your test function (a function is only considered a test function if it contains at least one ASSERT*-statement) will automatically be annotated by a marker and yellow underlined as shown in the following image. Click the marker (or press Ctrl+1 when the caret is on the given line) and choose “Add test to suite”.

Add Test To Suite

Using Structs and Classes as Tests

A functor here is defined as: any class or struct with a public operator() that takes zero arguments. Place your cursor anywhere along the desired function. Right click Source > Add Test > Add Test functor to Suite.

#include "cute.h"
#include "ide_listener.h"
#include "xml_listener.h"
#include "cute_runner.h"

struct StructTest {
    void operator() () {
        ASSERTM("Failing test", false);
    }
};

struct WithConstructor {
	WithConstructor(int x) : x{ x } {

    }
    int x;
    void operator() () {
        ASSERT_EQUALM("x should be 5", 5, x);
    }
};

bool runAllTests(int argc, char const *argv[]) {
	cute::suite s { };
	s.push_back(StructTest());
	s.push_back(WithConstructor(4));
	s.push_back(WithConstructor(5));
	cute::xml_file_opener xmlfile(argc, argv);
	cute::xml_listener<cute::ide_listener<>> lis(xmlfile.out);
	auto runner = cute::makeRunner(lis, argc, argv);
	bool success = runner(s, "AllTests");
	return success;
}

int main(int argc, char const *argv[]) {
    return runAllTests(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Adding Test Member to Suite

A test method in a class or struct can be added. See the code bellow as example.

A class or struct method needs to be public, non static, parameterless, non union. The class needs to be default constructible. An instance method needs to be public, non static, parameterless, non union and its return type needs to be void.

#include "cute.h"
#include "ide_listener.h"
#include "xml_listener.h"
#include "cute_runner.h"

struct MemberTest {
    bool aTest() {
        ASSERT(false);
    }
};

struct WithConstructor {
    WithConstructor(int x) : x{ x } { }
    int x;
    void operator() () {
        ASSERT_EQUALM("x should be 5", 5, x);
    }

    void test10() {
        ASSERT_EQUALM("x should be 10", 10, x);
    }
};

bool runAllTests(int argc, char const *argv[]) {
	cute::suite s { };
	s.push_back(CUTE_SMEMFUN(MemberTest, aTest));
	s.push_back(WithConstructor(5));

	WithConstructor instance { 5 };
	s.push_back(CUTE_MEMFUN(instance, WithConstructor, operator())); //same as above
	s.push_back(CUTE_MEMFUN(instance, WithConstructor, test10));

	cute::xml_file_opener xmlfile(argc, argv);
	cute::xml_listener<cute::ide_listener<>> lis(xmlfile.out);
	auto runner = cute::makeRunner(lis, argc, argv);
	bool success = runner(s, "AllTests");
	return success;
}

int main(int argc, char const *argv[]) {
    return runAllTests(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Creating a Library Test Project

First, create a shared or static library project. Ensure that the library project is opened, else it wouldn’t be shown in the following steps. Next create a CUTE Test Project. To do this select File->New->C++ Project, then expand CUTE and select CUTE Project. Give your project a name and press _Next >.

New Library Test Project

Check the checkbox Add Library Dependency. Then select the desired library project you would like to test.

Select Library To Test

Press Next > or Finish to complete the wizard.

Under Project > Properties > C/C++ Build > Settings, one of the following compiler -I and Linker -l -L settings will be set. Subsequent changes can be managed by the user.

Creating a Suite Project

A CUTE project with a custom test suite name can be created easily with the CUTE Suite Project wizard. To do this, select File > New > C++ Project. Then, expand CUTE, select CUTE Suite Project and give your project a name.

New Suite Project

Click Next > and specify a suite name.

Enter Suite Name

A project with the structure shown below will be created.

Suite Project Explorer View

Add tests that belong to the newly created suite in <your_suite_name>.cpp.

Adding New Suite Modules

Right-click on a project, folder or file (.cpp or .h) and choose New > CUTE Suite File.

New Suite File Menu

Enter the name of your new suite and click Finish.

New Suite File Wizard

Now you need to have your runner also integrate the cute::suite that is returned by the make_suite_<your_suite_name>() function in <your_suite_name>.h.

The initial Test.cpp (or the file that contains your cute::makeRunner(...) call) should look similar to this:

#include "cute.h"
#include "ide_listener.h"
#include "xml_listener.h"
#include "cute_runner.h"
#include "MyNewTestSuite.h"

bool runSuite(int argc, char const *argv[]) {
    cute::xml_file_opener xmlfile(argc, argv);
    cute::xml_listener<cute::ide_listener<>> lis(xmlfile.out);

    auto runner = cute::makeRunner(lis, argc, argv);
    cute::suite s { make_suite_MyNewTestSuite() };

    bool success = runner(s, "MyNewTestSuite");
    return success;
}

int main(int argc, char const *argv[]) {
    return runSuite(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
}

Add an include to <your_suite_name>.h and instantiate a new cute::suite using make_suite_<your_suite_name>() as argument. Then add a runner call.

#include "cute.h"
#include "ide_listener.h"
#include "xml_listener.h"
#include "cute_runner.h"
#include "MyNewTestSuite.h"
#include "MyNewerTestSuite.h" //new line

bool runSuite(int argc, char const *argv[]) {
	cute::xml_file_opener xmlfile(argc, argv);
	cute::xml_listener<cute::ide_listener<>> lis(xmlfile.out);

	auto runner = cute::makeRunner(lis, argc, argv);
	cute::suite s { make_suite_MyNewTestSuite() };
	cute::suite MyNewerTestSuite { make_suite_MyNewerTestSuite() }; //new line

	bool success = runner(s, "MyNewTestSuite");
	success = runner(MyNewerTestSuite, "MyNewerTestSuite") && success; //new line
	return success;
}

int main(int argc, char const *argv[]) {
    return runSuite(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
}

After this, the CUTE test view should look as shown below:

Multiple Suites Test View

Mocking with CUTE

Refactoring Towards Seams

Unwanted dependencies are a critical problem in software development. We often have to break existing dependencies before we can change some piece of code. Breaking existing dependencies is also an important preliminary to introduce unit tests for legacy code — according to Feathers definition code without unit tests.

Feathers’ seams help in reasoning about the opportunities that exist when we have to break dependencies. The goal is to have a place where we can alter the behaviour of a program without modifying it in that place. This is important because editing the source code is often not an option (e. g., when a function the code depends on is provided by a system library).

What is a Seam?

Feathers characterises a seam as a place in our code base where we can alter behaviour without being forced to edit it in that place. This has the advantage that we can inject the dependencies from outside, which leads to both an improved design and better testability. Every seam has one important property: an enabling point. This is the place where we can choose between one behaviour or another. There are different kinds of seam types.

What Kinds of Seams Does C++ Provide?

C++ offers a wide variety of language mechanisms to create seams. Beside the classic way of using subtype polymorphism which relies on inheritance, C++ also provides static polymorphism through template parameters. With the help of the preprocessor or the linker we have additional ways of creating seams.

Screencast Introduction to Seams

Object Seam

Object seams are probably the most common seam type. To start with an example, consider the following code where the class GameFourWins has a hard coded dependency to Die:

// Die.h
struct Die {
  int roll() const ;
};
// Die.cpp
int Die::roll() const {
  return rand() % 6 + 1;
}
// GameFourWins.h
struct GameFourWins {
  void play(std::ostream& os);
private:
  Die die;
};
// GameFourWins.cpp
void GameFourWins::play(std::ostream& os = std::cout) {
  if (die.roll() == 4) {
    os << "You won!" << std::endl;
  } else {
    os << "You lost!" << std::endl;
  }
}

According to Feathers definition, the call to play is not a seam because it is missing an enabling point. We cannot alter the behaviour of the member function play without changing its function body because the used member variable die is based on the concrete class Die. Furthermore, we cannot subclass GameFourWins and override play because play is monomorphic (not virtual).

This fixed dependency also makes GameFourWins hard to test in isolation because Die uses C’s standard library pseudo-random number generator function rand. Although rand is a deterministic function since calls to it will return the same sequence of numbers for any given seed, it is hard and cumbersome to setup a specific seed for our purposes. The classic way to alter the behaviour of GameFourWins is to inject the dependency from outside. The injected class inherits from a base class, thus enabling subtype polymorphism. To achieve that, Mockator provides a refactoring called Extract Interface and creates the following code:

struct IDie {
  virtual ~IDie() {}
  virtual int roll() const =0;
};
struct Die : IDie {
  int roll() const {
    return rand() % 6 + 1;
  }
};
struct GameFourWins {
  GameFourWins(IDie& die) : die(die) {}
  void play(std::ostream& os=std::cout) {
    // as before
  }
private:
  IDie& die;
};

This way we can now inject a different kind of Die depending on the context we need. This is a seam because we now have an enabling point: The instance of Die that is passed to the constructor of GameFourWins.

Object Seams Screencast

Compile Seam

Although object seams are the classic way of injecting dependencies, we think there is often a better solution to achieve the same goals. C++ has a tool for this job providing static polymorphism: template parameters. With template parameters, we can inject dependencies at compile-time. We therefore call this seam compile seam.

The essential step for this seam type is the application of a the refactoring extract template parameter. The result of this refactoring can be seen here:

template <typename Dice=Die>
struct GameFourWinsT {
  void play(std::ostream& os = std::cout) {
    if (die.roll() == 4) {
      os << "You won !" << std::endl;
    } else {
      os << "You lost !" << std::endl;
    }
  }
private:
  Dice die;
};
typedef GameFourWinsT<> GameFourWins;

The enabling point of this seam is the place where the template class GameFourWinsT is instantiated.

The use of static polymorphism with template parameters has several advantages over object seams with subtype polymorphism. It does not incur the run-time overhead of calling virtual member functions that can be unacceptable for certain systems. Probably the most important advantage of using templates is that a template argument only needs to define the members that are actually used by the instantiation of the template (providing compile-time duck typing). This can ease the burden of an otherwise wide interface that one might need to implement in case of an object seam.

Compile Seams Screencast

Preprocessor Seam

C and C++ offer another possibility to alter the behaviour of code without touching it in that place using the preprocessor. Although we are able to change the behaviour of existing code as shown with object and compile seams before, we think preprocessor seams are especially useful for debugging purposes like tracing function calls. An example of this is shown next where we trace calls to C’s malloc function with the help of Mockator:

// malloc.h
#ifndef MALLOC_H_
#define MALLOC_H_
void* my_malloc(size_t size, const char* fileName, int lineNumber);
#define malloc(size) my_malloc((size), __FILE__ , __LINE__)
#endif

// malloc.cpp
#include "malloc.h"
#undef malloc
void* my_malloc(size_t size, const char* fileName, int lineNumber) {
  // remember allocation in statistics
  return malloc(size);
}

The enabling point for this seam are the options of our compiler to choose between the real and our tracing implementation. We use the option -include of the GNU compiler here to include the header file malloc.h into every translation unit. With #undef we are still able to call the original implementation of malloc.

Preprocessor Seams Screencast

Beside the separate preprocessing step that occurs before compilation, we also have a post-compilation step called linking in C and C++ that is used to combine the results the compiler has emitted. The linker gives us another kind of seam called link seam. We show three kinds of link seams here:

  • Shadowing functions through linking order (override functions in libraries with new definitions in object files)
  • Wrapping functions with GNU’s linker option -wrap (GNU Linux only)
  • Run-time function interception with the preload functionality of the dynamic linker for shared libraries (GNU Linux and Mac OS X only)

Shadow Functions Through Linking Order

In this type of link seam we make use of the linking order. The linker incorporates any undefined symbols from libraries which have not been defined in the given object files. If we pass the object files first before the libraries with the functions we want to replace, the GNU linker prefers them over those provided by the libraries. Note that this would not work if we placed the library before the object files. In this case, the linker would take the symbol from the library and yield a duplicate definition error when considering the object file. Mockator helps in shadowing functions and generates code and the necessary CDT build options to support this kind of link seam:

// shadow_roll.cpp
#include "Die.h"
int Die::roll() const {
  return 4;
}
// test.cpp
void testGameFourWins () {
  // ...
}
$ ar -r libGame.a Die.o GameFourWins.o
$ g++ -Ldir/to/GameLib -o Test test.o shadow_roll.o -lGame

The order given to the linker is exactly as we need it to prefer the symbol in the object file since the library comes at the end of the list. This list is the enabling point of this kind of link seam. If we leave shadow_roll.o out, the original version of roll is called as defined in the static library libGame.a. This type of link seam has one big disadvantage: it is not possible to call the original function anymore. This would be valuable if we just want to wrap the call for logging or analysis purposes or do something additional with the result of the function call.

Wrapping Functions With GNU’s Linker

The GNU linker ld provides a lesser-known feature which helps us to call the original function. This feature is available as a command line option called wrap. The man page of ld describes its functionality as follows: “Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to __wrap_symbol. Any undefined reference to __real_symbol will be resolved to symbol.”

As an example, we compile GameFourWins.cpp. If we study the symbols of the object file, we see that the call to Die::roll — mangled as _ZNK3Die4rollEv according to Itanium’s Application Binary Interface (ABI) that is used by GCC v4.x — is undefined (nm yields U for undefined symbols).

$ gcc -c GameFourWins.cpp -o GameFourWins.o
$ nm GameFourWins.o | grep roll
U _ZNK3Die4rollEv

This satisfies the condition of an undefined reference to a symbol. Thus we can apply a wrapper function here. Note that this would not be true if the definition of the function Die::roll would be in the same translation unit as its calling origin. If we now define a function according to the specified naming schema __wrap_symbol and use the linker flag -wrap, our function gets called instead of the original one. Mockator helps in applying this seam type by creating the following code and the corresponding build options in Eclipse CDT:

extern "C" {
  extern int __real__ZNK3Die4rollEv();
  int __wrap__ZNK3Die4rollEv() {
    // your intercepting functionality here
    return __real__ZNK3Die4rollEv();
  }
}
$ g++ -Xlinker -wrap=_ZNK3Die4rollEv -o Test test.o GameFourWins.o Die.o

To prevent the compiler from mangling the mangled name again, we need to define it in a C code block. Note that we also have to declare the function __real_symbol which we delegate to in order to satisfy the compiler. The linker will resolve this symbol to the original implementation of Die::roll.

Alas, this feature is only available with the GNU tool chain on Linux. GCC for Mac OS X does not offer the linker flag -wrap. A further constraint is that it does not work with inline functions but this is the case with all link seams presented here. Additionally, when the function to be wrapped is part of a shared library, we cannot use this option.

Run-time Function Interception

If we have to intercept functions from shared libraries, we can use this kind of link seam. It is based on the fact that it is possible to alter the run-time linking behaviour of the loader ld.so in a way that it considers libraries that would otherwise not be loaded. This can be accomplished by the environment variable LD_PRELOAD that the loader ld.so interprets.

With this we can instruct the loader to prefer our function instead of the ones provided by libraries normally resolved through the environment variable LD_LIBRARY_PATH or the system library directories. As an example, consider the following code and the CDT build options which is generated by Mockator to intercept function calls to Die::roll:

#include <dlfcn.h>
int rand(void) {
  typedef int (*funPtr)(void);
  static funPtr origFun = 0;
  if (!origFun) {
    void* tmpPtr = dlsym(RTLD_NEXT, "rand");
    origFun = reinterpret_cast<funPtr>(tmpPtr);
  }
  int notNeededHere = origFun();
  return 3;
}
$ LD_PRELOAD=path/to/libRand.so executable

The advantage of this solution compared to the first two link seams is that it does not require re-linking. It is solely based on altering the behaviour of ld.so. A disadvantage is that this mechanism is unreliable with member functions, because the member function pointer is not expected to have the same size as a void pointer.

Using Test Doubles

Although there are already various existing mock object libraries for C++, we believe that creating mock objects is still too complicated and time-consuming for developers. Mockator provides a mock object library and an Eclipse plug-in to create mock objects in a simple yet powerful way. Mockator leverages the new language facilities C++11 offers while still being compatible with C++98/03.

Features include:

  • Mock classes and functions with sophisticated IDE support
  • Easy conversion from fake to mock objects that collect call traces
  • Convenient specification of expected calls with C++11 initializer lists or with Boost assign including Eclipse linked edit mode support
  • Support for regular expressions to match calls with expectations

Creating Mock Objects

Move Test Double to Namespace

Converting Fake to Mock Objects

Toggle Mock Support

Registration Consistency

Mock Functions

Using Regular Expressions For Expectations

SCons Build Support

What is SCons?

SCons is an open source software build tool which tries to fix the numerous weaknesses of make and its derivatives. For example, the missing automatic dependency extraction, make’s complex syntax to describe build properties and cross-platform issues when using shell commands and scripts. SCons is a self-contained tool which is independent of existing platform utilities. Because it is based on Python, a SCons user has the full power of a programming language to deal with all build related issues.

It was long past time for autotools to be replaced, and SCons has won the race to become my build system of choice. Unified builds and extensibility with Python — how can you beat that?

Eric S. Raymond, author of The Cathedral and the Bazaar

Use SConsolidator to build your projects in Eclipse

Maintaining a SCons-based C/C++ project with Eclipse CDT meant that all the intelligence SCons puts into your project dependencies had to be re-entered into Eclipse CDT’s project settings, so that its indexer and parser would be able to know your code’s compile settings and enable many of its features. In addition, SCons’ intelligence comes at the price of relatively long build start-up times - when it (re-) analyses the project dependencies - which can become annoying when you just fix a simple syntax error.

SConsolidator addresses these issues and provides tool integration for SCons in Eclipse for a convenient development experience.

Main Features

  • Conversion of existing C++ CDT managed build projects to SCons projects
  • Import of existing SCons projects into Eclipse with wizard support
  • Interactive mode to quickly build single C/C++ source files speeding up round-trip times
  • A special view for a convenient build target management of all workspace projects
  • Graph visualization of build dependencies that helps in debugging SCons build issues

Works out of the box

SConsolidator has been successfully used to import the following SCons-based projects into Eclipse:

  • MongoDB
  • Blender
  • FreeNOS
  • Doom 3
  • COAST

Contribute

SConsolidator is available at Github. Found a bug or have a feature request?

Report bugs and feature requests on Github.

Installation

To use SConsolidator, you first have to install SCons (version 2.0 is the minimum requirement). SConsolidator requires at least a Eclipse Indigo release of the CDT, otherwise you won’t be able to install SConsolidator. Execute the following steps to install it in Eclipse:

  1. Choose the menu Help, Install New Software
  2. Type the URL of the SConsolidator update site into the site field: http://www.sconsolidator.com/update
  3. Then, you can either only install SConsolidator’s base functionality by choosing the feature SConsolidator - Base or additionally SConsolidator - Dependency Visualization for visualizing build target dependencies (the latter needs GEF and Zest to be installed).
  4. After the installation, Eclipse will ask to restart itself. Please do this.

Getting Started

Add SCons support to an existing project

To add SCons support to an existing C/C++ project, just right click on the project(s) in the project explorer and choose one of the following two submenus of SCons:

  • Use self-provided SCons build (aka “existing mode”) for projects where you provide the SCons build files
  • Use generated SCons build (aka “managed mode”) for projects where SConsolidator should create and update the SCons build files

Notice that you should use managed for simple C/C++ projects only. It can be considered as a good starting point if you are not yet familiar with SCons. As soon as your projects grow in size and complexity, you want to use your own provided SCons build files.

For non-C/C++ projects, you only have one menu named Add SCons support.

Building projects with SCons

If you request a build, the output of a SCons build run is shown in a project specific console. Compile errors are shown in red and hyperlinks are provided to directly jump to the location in the editor where the error was found by the compiler. Error markers are created for the compiler errors as we are used from Eclipse.

Target View

SCons target view shows all the SCons projects in the current workspace and allows you to maintain the targets on a project level. To request a build for a specific target, you have to select it in the view and click the build button in the upper right corner. The same can be achieved for a build in the interactive console.

There is a dialog to create a new target for a SCons project. There you can define the name of the target and additional SCons options which will be used when SCons is invoked. Furthermore, you can also specify a target alias which is also shown in the target view.

Dependency View

The dependency view visualizes the dependencies of the selected target SCons has emitted. You can choose a target of a SCons project in the workspace with a click on the lookup symbol in the upper right corner. The dependencies are then shown in the graph area.

C/C++ specific support

Create new SCons based C/C++ Managed Project

To create a new SCons based project, you can use the New Project wizard of Eclipse and choose C/C++ project. There you can see three new project types for SCons managed projects: executable, shared library and static library project.

SConsolidator also provides a facility to convert existing managed projects to SCons projects with a context menu on the project level (SCons -> Add SCons support) and with a wizard as shown in this figure.

Import of Existing SCons based C/C++ Project

SConsolidator helps you to import existing SCons based C/C++ projects with a wizard. You can choose the location where the code of the existing project is located. If the location contains an Eclipse project, the name of the project is deduced and shown in the corresponding input field. In case you want to set further options for the initial SCons run, you have the possibility to put them in the field SCons options. SConsolidator will then try to gather all include paths and macros of your project necessary for the CDT indexer to properly work and store them in the project settings.

Refresh SConsolidator after altering the SCons build

SConsolidator helps you to keep the build settings of your SCons build consistent with your Eclipse CDT project by (one-way) synchronizing the used include paths and macros. Whenever you changed your SCons build in a way that affects the used include paths or macros, click on the icon with the Switzerland flag in the toolbar or use the project context menu Refresh C/C++ project from SCons of SConsolidator. Both actions run your SCons build and collect all the necessary information for Eclipse CDT to work as expected.

Interactive Console

To build single targets without the need to invoke full builds with every change, we provide an interactive console with SConsolidator where you have all the possibilities known from SCons’ interactive mode. We allow building and cleaning of the corresponding target(s) for the source file currently loaded in the active editor and a redo of the last action as can be seen from the buttons. Of course, you can also type SCons commands in the interactive console directly.

CUTE is part of Cevelop, your number one IDE for safe C++ development