Unit testing value classes with templates

May 13th, 2008

Classes with value semantics are a very important, err, class of classes. Frequently used as the building blocks for other code, it is important that they are tested and behave as expected.

The following function templates are designed to make testing the basic properties of value classes as simple as possible. They are designed to be used with a simple C++ testing framework such as hshgtest.

The tests are designed to test classes that are default constructible and have a number of properties each of which affect the value of the class. Changing any of the properties away from its default value should change the value of the class. Copy construction and assignment should preserve the value of the class in the expected way and value classes may have a serialization mechanism which should preserve the value of the class through an intermediate ‘flat’ form, typically a byte sequence.

To test a particular class, it is required to specialize the following function template:

template< class T >
void FillTestVector( std::vector< T >& vec );

The implementation should populate vec with any number of class instances each of which must differ from a default constructed instance of the class. Typically one would create as many instances as the class has distinct properties and make each instance differ from a default constructed instance by perturbing just one property from its default value. This maximizes the tests’ coverage.

The “Not Equal” Test

The first set of tests is a basic sanity check of operators == and != and of the set of values chosen for the FillTestVector specialization. Instances should compare equal to themselves and non-default instances should compare unequal to default constructed instances.

template< class T >
void NeqTest()
{
	typedef typename std::vector< T >::iterator iterator;

	std::vector< T > vec;
	FillTestVector( vec );

	T def;
	HSHG_ASSERT( def == def );
	HSHG_ASSERT( !(def != def) );

	HSHG_ASSERT( !vec.empty() );

	for( iterator i = vec.begin(); i != vec.end(); ++i )
	{
		HSHG_ASSERT( def != *i );
		HSHG_ASSERT( !(def == *i) );
		HSHG_ASSERT( *i == *i );
		HSHG_ASSERT( !(*i != *i) );
	}
}

The “CopyConstruct” Test

As the name implies… tests the copy constructor.

template< class T >
void CopyConstructTest()
{
	typedef typename std::vector< T >::iterator iterator;

	std::vector< T > vec;
	FillTestVector( vec );

	HSHG_ASSERT( !vec.empty() );

	for( iterator i = vec.begin(); i != vec.end(); ++i )
	{
		T copy( *i );
		HSHG_ASSERT( copy == *i );
	}
}

The “Assignment” Test

As the name implies… tests the assignment operator.

template< class T >
void AssignmentTest()
{
	typedef typename std::vector< T >::iterator iterator;

	std::vector< T > vec;
	FillTestVector( vec );

	HSHG_ASSERT( !vec.empty() );

	for( iterator i = vec.begin(); i != vec.end(); ++i )
	{
		T def;
		def = *i;
		HSHG_ASSERT( def == *i );
	}
}

The Serialization Test

This test is a little more flexible. There are numerous ways of streaming class and numerous representations of a ‘flat’ serialized class. The serialization and deserialization functions are abstracted into two class templates, Freezer and Thawer.

template< class S, class T >
struct Freezer;

template< class S, class T >
struct Thawer;

template< class S, class T >
S Freeze( const T& t )
{
	return Freezer< S, T >::freeze( t );
}

template< class S, class T >
T Thaw( const S& s )
{
	return Thawer< S, T >::thaw( s );
}

template< class S, class T >
void StreamTest()
{
	typedef typename std::vector< T >::iterator iterator;

	std::vector< T > vec;
	FillTestVector( vec );

	HSHG_ASSERT( !vec.empty() );

	for( iterator i = vec.begin(); i != vec.end(); ++i )
	{
		T deiced( Thaw< S, T >( Freeze< S, T >( *i ) ) );

		HSHG_ASSERT( deiced == *i );
	}
}

The two function templates, Freeze and Thaw, serialize and deserialize the class under test (T) though an intermediate flat data class (S) using the Freezer and Thawer class templates. This test requires a specialization of Freezer and Thawer to specify how the serialization is performed.

Here is a partial specialization which uses standard insertion and extraction operators with std::stringstream classes to serialize the class under test to and from a std::string.

template< class T >
struct Freezer< std::string, T >
{
	static std::string freeze( const T& t)
	{
		std::ostringstream r;
		r << t;
		return r.str();
	}
};

template< class T >
struct Thawer< std::string, T >
{
	static T thaw( const std::string& s)
	{
		std::istringstream r( s );
		T t;
		r >> t;
		return t;
	}
};

Putting it all together

For convenience, we might want to put these all together so that testing a new class is as simple as possible.

template< class T >
void TestAll()
{
	NeqTest< T >();
	CopyConstructTest< T >();
	AssignmentTest< T >();
	StreamTest< std::string, T >();
}

Now adding a new class to be tested is as simple as added a specialization of FillTestVector and adding a TestAll() test call.


struct MyNewValue
{
    MyNewValue() : _ivy(-1) {}

    int _ivy;
    std::string _leaf;
};

template<>
void FillTestVector( std::vector< T >& vec )
{
    vec.resize(2);
    vec[0]._ivy = 42;
    vec[1]._leaf = “non-default string”;
}

// Add TestAll< MyNewValue >() to test calls.

Testing a new property is as simple as adding more test values to the test vector in the FillTestVector specialization. Testing that the property contributes to the equality operator, that the property is appropriately preserved in copy construction, assignment and serialization happens automatically.

Address Space Monitor 0.6 Release

April 17th, 2008

Address Space Monitor 0.6 has been released. In true ‘alpha’ quality style it has gained many features, while probably being no more stable than the previous release.

The major new feature is virtual address space recording which enables you to record the address space usage of a program over time and later analyse the recording and compare it with other runs.

Address Space Monitor now comes bundled with a number of command line utilities for manipulating recordings and extracting statistics from them, but for now these are completely undocumented so you should not rely on their interface (or indeed their existence) in future releases.

As always, the installer and source are both available here and as before the license is an MIT license variant.

Address Space Monitor

Please use responsibly.

The polymorphic equality problem

February 21st, 2008

For many situations the use of RTTI in C++ is unnecessary and it can often be an indicator of poor design. There are, however, some circumstances in which a dynamic_cast can make things cleaner and reduce dependencies.

For many classes an equality operator is desirable, its implementation often passes through to its members in a natural way, however some classes may hold pointers (or auto_ptr) to base types because they require a member to have polymorphic behaviour but that member should still have value semantics.

The problem then, is how to compare two objects through pointers to their common base class. The result of the comparison should be result of comparing the derived objects if the derived types are the same and false otherwise.

Here is my “dynamic_cast” solution.

class Base
{
public:
    virtual ~Base() {}

    virtual bool equal( const Base& b ) const = 0;
    bool operator==( const Base& b ) const { return equal( b ); }
};

class Derived1 : public Base
{
public:
    Derived1( int v ) : _val( v ) {}

    bool equal( const Base& b ) const
    {
        const Derived1* pb = dynamic_cast< const Derived1* >( &b );
        return pb != 0 && *this == *pb;
    }

    bool operator==( const Derived1& other ) const { return _val == other._val; }

private:
    int _val;
};

class Derived2: public Base
{
public:
    Derived2( int v ) : _val( v ) {}

    bool equal( const Base& b ) const
    {
        const Derived2* pb = dynamic_cast< const Derived2* >( &b );
        return pb != 0 && *this == *pb;
    }

    bool operator==( const Derived2& other ) const { return _val == other._val; }

private:
    int _val;
};

Of course this part is just repetitive boiler plate code so we should split it out into a function.

bool equal( const Base& b ) const
{
    const Derived1* pb = dynamic_cast< const Derived1* >( &b );
    return pb != 0 && *this == *pb;
}

Of course, it relies on a type for the correct function so it needs to be a function template.

template< class BaseT, class DerivedT >
bool IsEqual( const DerivedT* d, const BaseT& b )
{
    const DerivedT* pb = dynamic_cast< const DerivedT* >( &b );
    return pb != 0 && *this == *pb;
}

bool Derived1::equal( const Base& b ) const
{
    return IsEqual( this, b );
}

Note how we can use template argument deduction so that we don’t have to explicitly specify ‘Derived1′. If you fancy, you can use this to make a macro and just put the macro in each class declaration. This is one step beyond what I’d do, though.

#define DOES_BASE_POLY_EQ bool equal( const Base&b ) const { return IsEqual( this, b ); }

Of course, always using a == b might not be correct for all classes. This behaviour could be made parameterizable.

template< class T, class U, class E = std::equal_to< T > >
struct IsEqualHelper
{
    bool operator()( const T* t, const U& u ) const
    {
        const T* ut = dynamic_cast< const T* >( &u );
        return ut != 0 && E()( *t, *ut );
    }
};

template< class T, class U >
bool IsEqual( const T* t, const U& u )
{
    return IsEqualHelper< T, U >()( t, u );
}

We retain the IsEqual template function for ease of use in the simple case, but provide a template struct with an operator() and a parameterizable equality functor for which there is an obvious default in the standard C++ library.

What’s the alternative without RTTI? Unfortunately it’s a classic double dispatch problem for which there are no universally neat solutions. Here’s what I came up with. It’s not nice.

The basic strategy is that the comparing to base classes calls the virtual equal function on one of them, with the other as parameter. The virtual function mechanism now ensures that we can use a this pointer with the correct derived type in the derived equal function, the other parameter still has a type of base reference, though. To promote this to derived type we call a virtual helper function (iseq) on the base reference which is overloaded for all the different derived types. This ensures that we can now promote the other parameter to a derived pointer through a virtual function without losing information about the type of the first reference. When the two derived types differ, the base implementation (return false) is the correct behaviour. Only the iseq overload for the matching derived class needs to be overridden in each derived class. It can then call the appropriate equality operator for two derived instances.

The big ugliness is the requirement for a virtual function in the base class for each derived type which is a nasty reversed coupling between base and derived classes.


// Forward declarations of every single derived class
class Derived1;
class Derived2;
// ...

class Base
{
public:
    virtual ~Base() {}

    virtual bool equal( const Base& b ) const = 0;
    bool operator==( const Base& b ) const { return equal( b ); }

    // Default iseq helper virtual function for every single derived class
    virtual bool iseq( const Derived1& ) const { return false; }
    virtual bool iseq( const Derived2& ) const { return false; }
    // ...
};

class Derived1 : public Base
{
public:
    Derived1( int v ) : _val( v ) {}

    bool equal( const Base& b ) const
    {
        return b.iseq( *this );
    }

    bool operator==( const Derived1& other ) const { return _val == other._val; }

    virtual bool iseq( const Derived1& other ) const { return *this == other; }

private:
    int _val;
};

class Derived2: public Base
{
public:
    Derived2( int v ) : _val( v ) {}

    bool equal( const Base& b ) const
    {
        return b.iseq( *this );
    }

    bool operator==( const Derived2& other ) const { return _val == other._val; }

    virtual bool iseq( const Derived2& other ) const { return *this == other; }

private:
    int _val;
};

Quick reference guide for template declarations

January 29th, 2008

Function templates by example

Your basic template declaration. (No angle brackets after the template name.)

template< class T, class U > T* just_template( U );

Declaration of explicit (full) specialization:

template<> char* just_template< char, int >( int );

Explicit instantiation. (No angle brackets after the template keyword.)

template const char* just_template< const char, double >( double );

Class templates by example

Basic declaration:

template< class T > class YetAnotherTemplate;

Declaration of partial specialization (e.g. for pointer types):

template< class T > class YetAnotherTemplate< T* >;

Declaration of full specialization (for long):

template<> class YetAnotherTemplate< long >;

Explicit instantiation:

template class YetAnotherTemplate< float >;

Notes
You can’t partially specialize function templates. This is a good thing(tm) in my opinion. For a better explanation than I can give, see Sutter’s Mill.

Partial specialization doesn’t necessarily mean fewer template parameters, it really does just mean more specialized.

For a function template specialization you can omit any trailing template arguments that can be deduced; if all template arguments can be deduced you can just use the unadorned template name in the declaration of the specialization.

Similarly, for explicit instantiations of function templates you can just use the template name where the arguments can be deduced.

e.g.

template< class T > void f( T );

template f( int ); // Explicit instantiation

template<> f( double ); // Declaration of explicit specialization

Caution required with integer conversions

January 2nd, 2008

This applies to both C and C++, although the details are a little different in the more obscure corners. Consider this C++ snippet which is designed to position a streambuf somewhere a bit before its end:

pos = sb->pubseekoff( -128 - thing.GetSize(), ios_base::end, ios_base::in );

How did this fail spectacularly?

OK, here are the types.

sb is a std::streambuf
thing.GetSize() returns an unsigned int
pubseekoff takes a streambuf::off_type which is some sort of signed type for representing stream offsets.

Here’s what happened on my implementation.

128 is an integer constant, and fits in an int, so it’s an int.
After integral promotions, it’s still an int (phew!).

-128 is, therefore an int.

thing.GetSize() is an unsigned int, so -128 gets converted to an unsigned int for the binary ‘-’ operation. (Uh-oh!)

Finally this unsigned int gets converted into whatever streambuf::off_type is. If off_type behaved like a signed int then (implementation defined!) you might get the conversion to wrap around back to the negative numbers to a value that you were expecting in the first place.

On the other hand, off_type might act like a 64-bit signed integer type, with unsigned int being a 32-bit unsigned type. In this case, the very large positive unsigned 32-bit number that you were hoping was actually a reasonably small negative number will quite happily stay large and positive in the conversion. The function call then ends up trying to seek 3.99 GBytes beyond the end of the stream. Whoops.

Don’t try for exception safety

December 20th, 2007

Achieve it!

“Do or do not, there is no try” - Yoda, on strong exception safety.

I’ve decided that I like exceptions a lot more now that I know how to use them to my advantage. Take the following piece of code:


#include <cstring>
using std::memcmp;

size_t DoConversion( char* dst, size_t dst_len, const char* src, size_t src_len );
size_t DoConversion2( char* dst, size_t dst_len, const char* src, size_t src_len );

void test_conv( const char* test_data, size_t test_len )
{
        size_t bufsize = DoConversion( NULL, 0, test_data, test_len );

        char* buffer = new char[ bufsize ];

        DoConversion( buffer, bufsize, test_data, test_len );

        size_t bufsize2 = DoConversion2( NULL, 0, buffer, bufsize );

        if( bufsize2 != bufsize )
                throw TestFailed();

        char* buffer2 = new char[ bufsize2 ];

        DoConversion2( buffer2, bufsize2, buffer, bufsize );

        if( memcmp( buffer, buffer2, bufsize ) != 0 )
                throw TestFailed();

        delete[] buffer2;

        delete[] buffer;
}

Assume that DoConversion and DoConversion2 are “traditional” character string conversion functions. They take a source and destination buffer which they don’t memory manage and convert one to the other. If you supply a null destination buffer then they will tell you how big the destination buffer would have to be to complete the conversion without actually performing the conversion. Assume that they are less traditional in that they may throw a BadThing exception if something doesn’t work.

The test_conv function is obviously not exception safe, and in multiple ways. Trying to make it exception safe in a naive way - by adding some try/catch pairs - is verbose and error prone. I came up with this, but I have low confidence in the result (and I actually know of one definite reason why it is not exception safe).

void test_conv( const char* test_data, size_t test_len )
{
        size_t bufsize = DoConversion( NULL, 0, test_data, test_len );

        char* buffer = new char[ bufsize ];

        size_t bufsize2;

        try
        {
                DoConversion( buffer, bufsize, test_data, test_len );

                bufsize2 = DoConversion2( NULL, 0, buffer, bufsize );
        }
        catch( … )
        {
                delete[] buffer;
                throw;
        }

        if( bufsize2 != bufsize )
        {
                delete[] buffer;
                throw TestFailed();
        }

        char* buffer2 = new char[ bufsize2 ];

        try
        {
                DoConversion2( buffer2, bufsize2, buffer, bufsize );
        }
        catch( … )
        {
                delete[] buffer2;
                delete[] buffer;
                throw;
        }

        int res = memcmp( buffer, buffer2, bufsize );

        delete[] buffer2;
        delete[] buffer;

        if( res != 0 )
                throw TestFailed();
}

This is ugly in so many ways. buffer is allocated in one place and deallocated in one of four places (or even not at all!), depending on the particular path followed; bufsize2 now has to be declared before we can sensibly initialize it; the result of memcmp is cached so that deallocation can take place before deciding whether to throw or not (this was mildy shorter that duplicating the two delete statements yet again).

So here’s the answer: write a new class.

class AutoCharArray
{
public:
        AutoCharArray( size_t s ) : _buffer( new char[s] ) {}

        ~AutoCharArray() { delete[] _buffer; }

        operator char*() const { return _buffer; }

private:
        // No copying
        AutoCharArray( const AutoCharArray& );
        AutoCharArray& operator=( const AutoCharArray& );

        char* _buffer;
};

void test_conv( const char* test_data, size_t test_len )
{
        size_t bufsize = DoConversion( NULL, 0, test_data, test_len );

        AutoCharArray buffer( bufsize );

        DoConversion( buffer, bufsize, test_data, test_len );

        size_t bufsize2 = DoConversion2( NULL, 0, buffer, bufsize );

        if( bufsize2 != bufsize )
                throw TestFailed();

        AutoCharArray buffer2( bufsize2 );

        DoConversion2( buffer2, bufsize2, buffer, bufsize );

        if( memcmp( buffer, buffer2, bufsize ) != 0 )
                throw TestFailed();

}

AutoCharArray just manages the life time of the dynamically allocated char array as a C++ object. Because of this, we never have to worry about catching an rethrowing foreign exceptions. Because it is a C++ object, if it has been successfully constructed as a local object then it will be destroyed when the function exits, whether conventionally or via an exception. We don’t even have to worry about “new” failing. If new throws, the constructor will not have completed so the destructor will not be called on a bad pointer.

As well of all these advantages, the control flow for the optimistic ’success’ use case is obvious and easy to follow. It is not cluttered with a ton of “but in case that didn’t work” catch blocks. Overall, including the class definition, the entire code is no longer than the long winded “try/catch” quagmire of the first attempt.

A simple “AutoArray” class is very useful for this type of application, although I tend to prefer it as a template:

template< class T >
class AutoArray
{
public:
        AutoArray( size_t s ) : _buffer( new T[s] ) {}

        ~AutoArray() { delete[] _buffer; }

        operator T*() const { return _buffer; }

private:
        // No copying
        AutoArray( const AutoArray& );
        AutoArray& operator=( const AutoArray& );

        T* _buffer;
};

git backups interacting with git

December 5th, 2007

This is really important!

git as a generalized backup utility interacts with and git repositories that it finds in an ‘interesting’ way.

It treats them as a submodule, so instead of backing up the git repository, it just records a reference to the current HEAD of the submodule.

I believe that this is “by design”, but if you don’t set up the submodule configuration your backup repository won’t know where to find the correct repository with the recorded commit.

It also means that you need to be git pushing your precious repository data somewhere safe in any case.

git backup is also about three times slower than my tar based incremental backup, although incrementally saving the backups to a remote machine is quicker and backup browsing and recovery is a little easier.

git as a general purpose backup utility

December 3rd, 2007

When it was first suggested to me that you could just use git for backup I was not convinced. You would have these massive .git directories in high level places on your filesystem for one.

Now that I’ve had some time to reflect on the possibility I think that perhaps it isn’t such a crazy idea. It’s not actually true that you have to have a .git directory in the place that you want to back up. In fact, I am even trialling git alongside by regular “tar” based backup.

Here’s what I do. Suppose, for the sake of example, that I’m going to backup /home onto a separate backup partition called /backup.

Step 1 - Create a git repository for the backup

mkdir /backup/home.git
git --git-dir=/backup/home.git --work-tree=/home init

[
I used to do this as follows before I discovered about the --work-tree option to git. It has the same effect.

git --bare init
git config core.bare false
git config core.worktree /home

]

Step 2 - Initial backup

cd /home
git --git-dir=/backup/home.git add .
git --git-dir=/backup/home.git commit -m "Initial /home backup"

Step 3 - Copy backups to a safe remote machine
Assuming that you have a second machine where you want to store your backups to which you have ssh access (and has git installed), you can initialize a new empty git repository for this purpose.
Suppose that this machine is called other-machine and the repository is located at /backup/first-machine/home.git.

The initial remote backup is performed thus.

cd /backup/home.git
git remote add other-machine ssh://other-machine/backup/first-machine/home.git
git gc
git push other-machine master

The git gc seems fairly important. At this stage you have a massive git repository that hasn’t yet been packed. When you attempt to push it, git will want to perform a big “Deltifying” step to create a pack on the remote side. If you perform the git gc on the local side first it will perform the big “Deltifying” step and effectively store the results as a pack on the local side. The git push can use this and, having done the gc, subsequent local operations can also take advantage of the local pack whereas just letting the push do the pack would lose the work done from the local side.

Step 4 - Incremental backup

cd /home
git --git-dir=/backup/home.git add .
git --git-dir=/backup/home.git commit -a -m "Initial /home backup"

Performing both an “add” and a “commit -a” looks repetitive but is required as “commit -a” does not add new untracked files and “add” doesn’t ‘add’ file deletions to the index.

Step 5 - Push incremental backup to remote machine

cd /backup/home.git
git push other-machine master

Well, that was easy.

Disadvantages
The initial “git gc” step can be very slow.
git does not store owner/group information or atime and utime information. The backup is content only.
“git add .” is not robust against files that disappear while git’s looking at them (e.g. lock files). It tends to fail with a “cannot stat” message when you really want it to not bother with that file and carry on.

extern arrays and pointers

November 17th, 2007

Quick, what’s the difference!

extern char data[];
extern char *data;

Well the first one’s and array and the second one is a pointer. You can treat them in the same way, but they are completely different things.

Here’s a function which looks the same with both declarations of data:

char f(int a)
{
    return data[a];
}

But if data is an array it compiles to the following. (gcc -O3 -fomit-frame-pointer)

f:
    # move the function parameter from the stack into $eax
    movl    4(%esp), %eax

    # access the byte at data + $eax and store into $eax
    movsbl  data(%eax),%eax

    ret

And if it’s a pointer it compiles to this.

f:
    # move the pointer value at the memory location data into $eax
    movl    data, %eax

    # move the function parameter from the stack into $edx
    movl    4(%esp), %edx  

    # access the byte at $edx + $eax and store into $eax
    movsbl  (%eax,%edx),%eax

    ret

It’s an easy enough mistake to make but I find it unintuitive the way that the change in external declaration silently changes the effect of the function without any change to the function definition itself.

It’s important to get external declarations consistent and this can be a problem if the array data lives in a non-C (or non-C++) file, e.g. test data included via an assembly file.

A mini test framework in a single header file

October 31st, 2007

After trying it on a number of projects, I’m now very enthusiastic about test driven development. At home I’ve rather missed the minimal support code that I had at my old job, so I’ve rewritten a miniature test framework in a single header file.

Only this time, it’s better. Framework implies something rather big. This isn’t. The design goal was to make something really simple and lightweight that just makes the process of writing tests as simple as possible with as few overheads as possible.

In this framework a test function is just a void function returning void. If the function doesn’t throw an exception when it is called then it has passed.

Here are some example tests, which show the three different type of assert macros provided. (Yes all the tests are broken!)

void strlen_test()
{
    HSHG_ASSERT( strlen( "1245" ) == 5 );
}

void sum_test()
{
    int total = 0;

    for( int i = 1; i <= 15; ++i )
    {
        total += 1;
        total += 7;
    }

    for( int i = 2; i <= 7; ++i )
    {
        total += i;
    }

    HSHG_ASSERT_DESC( total == 148, "Maximum break is 148" );
}

class MyThrowable
{
public:
    static void ThrowMe()
    {
        throw MyThrowable();
    }
};

void MyThrowable_test()
{
    HSHG_ASSERT_THROWS( true ? 0 : (MyThrowable::ThrowMe(), 0), MyThrowable );
}

The test namespace is HSHGTest and there is a struct TestFn for a test function which contains a char* for the function name and a pointer to the function itself.

There is then an inline function called RunTests that takes an array of these structs (terminated by one with a null function pointer); it runs each test in the array and reports to a given std::ostream; it then returns EXIT_SUCCESS if it ran some tests and they all passed, and EXIT_FAILURE otherwise. This makes it suitable for returning the result directly from a main function. Here is an example report.

testtest.cc:6: Test strlen_test failed. ( strlen( "1245" ) == 5 )
testtest.cc:24: Test sum_test failed. ( Maximum break is 148 )
testtest.cc:38: Test MyThrowable_test failed. ( Exception MyThrowable expected. )

If this sounds a bit laborious then there are some helper macros to set a suitable array up, and even a macro for a default main function:

HSHG_BEGIN_TESTS
HSHG_TEST_ENTRY( strlen_test )
HSHG_TEST_ENTRY( sum_test )
HSHG_TEST_ENTRY( MyThrowable_test )
HSHG_END_TESTS

HSHG_TEST_MAIN

These macros translate (roughly) into:

namespace
{

HSHGTest::TestFn tests[] =
{
    { “strlen_test”, strlen_test },
    { “sum_test”, sum_test },
    { “MyThrowable_test”, MyThrowable_test },
    { NULL, NULL }
};

}

int main()
{
    return HSHGTest::RunTests( tests, std::cout );
}

The “framework” is available for download here. HSHGTest frawework