#ifndef CPPUNIT_TESTRESULT_H
#define CPPUNIT_TESTRESULT_H

#include <vector>

#ifndef CPPUNIT_GUARDS_H
#include "Guards.h"
#endif

#ifndef CPPUNIT_TESTFAILURE_H
#include "TestFailure.h"
#endif


class CppUnitException;
class Test;


/*
 * A TestResult collects the results of executing a test case. It is an 
 * instance of the Collecting Parameter pattern.
 *
 * The test framework distinguishes between failures and errors.
 * A failure is anticipated and checked for with assertions. Errors are
 * unanticipated problems signified by exceptions that are not generated
 * by the framework.
 *
 * TestResult supplies a template method 'setSynchronizationObject ()'
 * so that subclasses can provide mutual exclusion in the face of multiple
 * threads.  This can be useful when tests execute in one thread and
 * they fill a subclass of TestResult which effects change in another 
 * thread.  To have mutual exclusion, override setSynchronizationObject ()
 * and make sure that you create an instance of ExclusiveZone at the 
 * beginning of each method.
 *
 * see Test
 */

class TestResult
{
    REFERENCEOBJECT (TestResult)

public:
                                        TestResult  ();
    virtual                             ~TestResult ();

    virtual void                        addError       (Test *test, CppUnitException *e);
    virtual void                        addFailure     (Test *test, CppUnitException *e);
    virtual void                        addSkip        (Test *test, CppUnitException *e);
    virtual void                        startTest      (Test *test);
    virtual void                        endTest        (Test *test);
    virtual int                         runTests       ();
    virtual int                         testErrors     ();
    virtual int                         testFailures   ();
    virtual int                         testSkips      ();
    virtual bool                        wasSuccessful  ();
    virtual bool                        shouldStop     ();
    virtual void                        stop           ();

    virtual std::vector<TestFailure *>& errors         ();
    virtual std::vector<TestFailure *>& failures       ();
    virtual std::vector<TestFailure *>& skips          ();


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

        virtual void            lock                   () {}
        virtual void            unlock                 () {}
    };

    class ExclusiveZone
    {
        SynchronizationObject   *m_syncObject;

    public:
                                ExclusiveZone (SynchronizationObject *syncObject) 
                                : m_syncObject (syncObject) 
                                { m_syncObject->lock (); }

                                ~ExclusiveZone () 
                                { m_syncObject->unlock (); }
    };

protected:
    virtual void                setSynchronizationObject (SynchronizationObject *syncObject);

    std::vector<TestFailure *>  m_errors;
    std::vector<TestFailure *>  m_failures;
    std::vector<TestFailure *>  m_skips;
    int                         m_runTests;
    bool                        m_stop;
    SynchronizationObject       *m_syncObject;

};



// Construct a TestResult
inline TestResult::TestResult ()
: m_syncObject (new SynchronizationObject ())
{ m_runTests = 0; m_stop = false; }


// Adds an error to the list of errors. The passed in exception
// caused the error
inline void TestResult::addError (Test *test, CppUnitException *e)
{ ExclusiveZone zone (m_syncObject); m_errors.push_back (new TestFailure (test, e)); }


// Adds a failure to the list of failures. The passed in exception
// caused the failure.
inline void TestResult::addFailure (Test *test, CppUnitException *e)
{ ExclusiveZone zone (m_syncObject); m_failures.push_back (new TestFailure (test, e)); }

inline void TestResult::addSkip (Test *test, CppUnitException *e)
{ ExclusiveZone zone (m_syncObject); m_skips.push_back (new TestFailure (test, e)); }

// Informs the result that a test will be started.
inline void TestResult::startTest (Test *test)
{ ExclusiveZone zone (m_syncObject); m_runTests++; }

  
// Informs the result that a test was completed.
inline void TestResult::endTest (Test *test)
{ ExclusiveZone zone (m_syncObject); }


// Gets the number of run tests.
inline int TestResult::runTests ()
{ ExclusiveZone zone (m_syncObject); return m_runTests; }


// Gets the number of detected errors.
inline int TestResult::testErrors ()
{ ExclusiveZone zone (m_syncObject); return static_cast<int>(m_errors.size()); }


// Gets the number of detected failures.
inline int TestResult::testFailures ()
{ ExclusiveZone zone (m_syncObject); return static_cast<int>(m_failures.size()); }

inline int TestResult::testSkips ()
{ ExclusiveZone zone (m_syncObject); return static_cast<int>(m_skips.size()); }

// Returns whether the entire test was successful or not.
inline bool TestResult::wasSuccessful ()
{ ExclusiveZone zone (m_syncObject); return m_failures.size () == 0 && m_errors.size () == 0; }

// Returns a vector of the errors.
inline std::vector<TestFailure *>& TestResult::errors ()
{ ExclusiveZone zone (m_syncObject); return m_errors; }


// Returns a vector of the failures.
inline std::vector<TestFailure *>& TestResult::failures ()
{ ExclusiveZone zone (m_syncObject); return m_failures; }

inline std::vector<TestFailure *>& TestResult::skips ()
{ ExclusiveZone zone (m_syncObject); return m_skips; }

// Returns whether testing should be stopped
inline bool TestResult::shouldStop ()
{ ExclusiveZone zone (m_syncObject); return m_stop; }


// Stop testing
inline void TestResult::stop ()
{ ExclusiveZone zone (m_syncObject); m_stop = true; }


// Accept a new synchronization object for protection of this instance
// TestResult assumes ownership of the object
inline void TestResult::setSynchronizationObject (SynchronizationObject *syncObject)
{ delete m_syncObject; m_syncObject = syncObject; }


#endif