Listing 5: Test harness for Stack.h

#include "Counter.h"
#include "TestClass.h"

#if defined( STACK_REED )
#include "Stack_Reed.h"
#elif defined( STACK_REED_FIXED )
#include "Stack_Reed_Fixed.h"
#elif defined( STACK_SUTTER_1 )
#include "Stack_Sutter_1.h"
#elif defined( STACK_SUTTER_3 )
#include "Stack_Sutter_3.h"
#else
    #error "Don't know which stack to include."
    // #include "Stack_Reed.h"
#endif

typedef TestClass element_type;
typedef Stack<element_type> TestType;

class TestConstructDestruct
{
public:
    static void DoTest() {
        TestType a;
        Counter::Test( 0 == a.Size(), "Default constructor" );
        Counter::Test( a.Consistent(), "a internal state" );
    }
};

class TestCopyConstruct1
{
public:
    static void DoTest() {
        TestType a;
        {
            TestType b( a );
            Counter::Test( 0 == b.Size(), "Copy empty" );
            Counter::Test( b.Consistent(), "b internal state" );
        }
        Counter::Test( a.Consistent(), "a internal state" );
    }
};

class TestCopyConstruct2
{
public:
    static void DoTest() {
        TestType a;
        a.Push( element_type() );
        a.Push( element_type() );
        {
            TestType b( a );
            Counter::Test( 2 == b.Size(), "copy with 2 elements" );
            Counter::Test( b.Consistent(), "b internal state" );
        }
        Counter::Test( a.Consistent(), "a internal state" );
    }
};

class TestAssign1
{
public:
    static void DoTest() {
        TestType a;
        TestType b;
        b = a;
        Counter::Test( 0 == b.Size(), "Assign empty" );
        Counter::Test( a.Consistent(), "a internal state" );
        Counter::Test( b.Consistent(), "b internal state" );
    }
};

class TestAssign2
{
public:
    static void DoTest() {
        TestType a;
        TestType b;
        a.Push( element_type() );
        a.Push( element_type() );
        b.Push( element_type() );
        b = a;
        Counter::Test( 2 == b.Size(), "Assign 2 elements" );
        Counter::Test( a.Consistent(), "a internal state" );
        Counter::Test( b.Consistent(), "b internal state" );

        a = a;
        Counter::Test( 2 == a.Size(), "Assign a to self" );
        Counter::Test( a.Consistent(), "a internal state" );
    }
};

class TestPush
{
public:
    static void DoTest() {
        // This tests that the class obeys commit or rollback 
        // semantics.
        TestType a;
        for( int i = 0; i < 12; ++i ) {
            try {
                a.Push( element_type() );
                Counter::Test( i+1 == a.Size(), "push 1 element" );
            }
            catch( ... ) {
                Counter::Test( i == a.Size(), 
                    "push 1 element recover from exception" );
                Counter::Test( a.Consistent(), 
                    "a internal state after exception" );
                throw;
            }
        }
        Counter::Test( a.Consistent(), "a internal state" );
    }
};

#ifdef STACK_HAS_TOP
class TestTop
{
public:
    static void DoTest() {
        TestType a;
        try {
            element_type b = a.Top(); // Should throw.
            Counter::Fail( "Top of empty" );
        } 
        catch( const char* ) {
            Counter::Pass( "Top of empty" );
        }
        a.Push( element_type() );
        try {
            element_type b = a.Top(); // Should not throw.
            Counter::Pass( "Top of non-empty" );
        }
        catch( const char* ) {
            Counter::Fail( "Top of non-empty" );
        }
        Counter::Test( a.Consistent(), "a internal state" );
    }
};

class TestPop
{
public:
    static void DoTest() {
        TestType a;
        a.Push( element_type(1) );
        a.Push( element_type(2) );
        a.Pop();
        Counter::Test( 1 == a.Size(), "Pop reduces size" );
        Counter::Test( element_type(1) == a.Top(), 
            "Correct top element" );
        a.Pop();
        Counter::Test( 0 == a.Size(), "Pop reduces size" );
        try {
            a.Pop();
            Counter::Fail( "Pop empty" );
        }
        catch( const char* ) {
            Counter::Pass( "Pop empty" );
        }
        Counter::Test( a.Consistent(), "a internal state" );
        Counter::Test( 0 == a.Size(), "a correct size" );
    }
};

#else // STACK_HAS_TOP

// Test old style pop which returns the popped element.
// Note that it's impossible to pass these tests, which is why
// the newer version changes the pop method to return nothing.

class TestPop
{
public:
    static void DoTest() {
        TestType a;
        element_type b(0);
        a.Push( element_type(1) );
        a.Push( element_type(2) );
        try {
            b = a.Pop();
            Counter::Test( 1 == a.Size(), "Pop reduces count" );
            Counter::Test( element_type(2) == b, 
                "Correct pop value" );
        } catch(...) {
            // This test will get confused with the 
            // assignment failing.
            Counter::Test( 2 == a.Size(), 
                "Pop rollback on exception 1" );
            // Can't test value of stack top.
            throw;
        }
        try {
            a.Pop();
            Counter::Test( 0 == a.Size(), "Pop reduces count" );
        } catch( ... ) {
            Counter::Test( 1 == a.Size(), 
                "Pop rollback on exception 2" );
            throw;
        }
        try {
            a.Pop();
            Counter::Fail( "Pop empty throws exception" );
        }
        catch( const char* ) {
            Counter::Pass( "Pop empty throws exception" );
            Counter::Test( 0 == a.Size(), "Pop empty stays empty" );
        }
        Counter::Test( a.Consistent(), "a internal state" );
    }
};

#endif // STACK_HAS_TOP

int main( int argc, char* argv[] )
{
    TestDriver<TestConstructDestruct>();
    TestDriver<TestCopyConstruct1>();
    TestDriver<TestPush>();
    TestDriver<TestCopyConstruct2>();
    TestDriver<TestAssign1>();
    TestDriver<TestAssign2>();
#ifdef STACK_HAS_TOP
    TestDriver<TestTop>();
#endif
    TestDriver<TestPop>();
    TestDriver<TestAssign2>();

    Counter::PrintTestSummary();
    
    return 0;
}
— End of Listing —