///
/// \file unittest.h
///
/// Header file to be included in all unit test files.
///
/// \section disclaimer Historical Permission Notice and Disclaimer
/// Copyright (c) 2003 Alan Green
/// 
/// Permission to use, copy, modify and distribute this software and its documentation
/// for any purpose and without fee is hereby granted, provided that the above
/// copyright notice appear in all copies and that both the copyright notice and
/// this permission notice appear in supporting documentation. Alan Green makes no
/// representations about the suitability of this software for any purpose. It is
/// provided "as is" without express or implied warranty.
/// 
/// ALAN GREEN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
/// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ALAN GREEN
/// BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
/// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
/// CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
/// WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifndef _UNITTEST_H
#define _UNITTEST_H


///
/// \mainpage A Simple UnitTest Framework
///
/// \section Why Why Another Unit Test framework?
///
/// The CppUnit framework requires a modern compiler, able to handle features
/// such as namespaces. This unit test framework is able to work in a less full 
/// featured environment.
///
/// \section Writing Writing Unit Tests
///
/// Unit tests are written in .cpp files. You must include the unittest.h
/// header file. Each unit test function is declared with the #DECLARE_UNIT_TEST 
/// macro.
///
/// Inside each unit test, the #CHECK macro can be called as many times as is 
/// required to check the state of the unittest.
///
/// \code
/// #include "unittest.h"
///
/// DECLARE_UNIT_TEST(testOne, "testing.cat") {
///    int a = 3 + 2;
///    CHECK(a == 4);
///}
/// \endcode
///
/// \section Running Running Unit Tests
///
/// Link the subsystem under test with the main function in TestRunner.cpp.
///
/// \section disclaimer Historical Permission Notice and Disclaimer
/// Copyright (c) 2003 Alan Green
/// 
/// Permission to use, copy, modify and distribute this software and its documentation
/// for any purpose and without fee is hereby granted, provided that the above
/// copyright notice appear in all copies and that both the copyright notice and
/// this permission notice appear in supporting documentation. Alan Green makes no
/// representations about the suitability of this software for any purpose. It is
/// provided "as is" without express or implied warranty.
/// 
/// ALAN GREEN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
/// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ALAN GREEN
/// BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
/// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
/// CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
/// WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

// Forward declarations
class UnitTestInformation;
class UnitTestListNode;
class UnitTestException;

/// \brief The type of a unit test function.
///
/// The type of a unit test function. You can declare your own unit test 
/// functions, but it is usually more convenient to use the DECLARE_UNIT_TEST 
/// macro, which also registers the function.
typedef void (*UnitTestFn)(void);

/// \brief The registry of unit tests.
/// 
/// Maintains a central registry of unit tests so that they can be found and
/// run as a group.
class UnitTestRegistry {
public:

    /// Run the unittests
    /// \return The number of failures (zero means all passed)
    int run();

    /// Get a reference to the singleton unit test registry.
    static UnitTestRegistry &get();

private:
    friend class UnitTestInformation;

    // Called by the DECLARE_UNIT_TEST macro
    // Not intended for public consumption
    void reg(UnitTestInformation *pInfo);

    /// Will be only a single instance
    UnitTestRegistry();

    void runOneTest(UnitTestInformation *test);

    UnitTestListNode *testList;

    int passed;
    int failed;
};

/// Information about a single unit test.
class UnitTestInformation {
public:
    /// Constructor. Constructing a unit test information object will also 
    /// register it.
    /// \param fn The actual function to run for this unit test
    /// \param category A category string that can be used to select groups
    ///        of unit tests.
    /// \param name The name of the unit test
    UnitTestInformation(UnitTestFn fn, char * category, char *name);

    /// Gets a pointer to the actual unit test function
    UnitTestFn getFunction() {return fn;};

    /// \brief Gets the category string for this unit test
    char * getCategory() {return category;};

    /// Gets the name of this particular unit test
    char * getName() {return name;};
private:
    UnitTestFn fn;
    char *category;
    char *name;
};



/// Declare a unit test function. Also builds the corresponding 
/// UnitTestInformation object.
/// \param testname The name of this particular test. Will be used as the 
///        name of the test function.
/// \param category Category used to select a subset of all tests for running
#define DECLARE_UNIT_TEST(testname, category) \
    void testname(void); \
    static UnitTestInformation testname##Info (testname, category, #testname); \
    void testname(void)


/// An exception recording a failure in a unit test
class UnitTestException {
public:
    /// You may call this directly, but better to let the CHECK macro call
    /// it for you.
    UnitTestException(char *file, int line, char *expr);

    /// Find the message that went with this exception
    char *getMessage();
private:
    char *file;
    int line;
    char *message;
};

/// Check to see that a particular expression is true
/// \param expr The expression to check.
#define CHECK(expr) \
    if (!(expr)) { \
        throw UnitTestException(__FILE__, __LINE__, "false: " #expr); \
    }

#endif // _UNITTEST_H

