/// 
/// \file unittest.cpp
///
/// Implementation for unittest library.
///
/// \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.
///

#include <stdlib.h>
#include "unittest.h"
#include <iostream>
using namespace ::std;

/// \brief A node in a list of unit tests.
/// Used by the UnitTestRegistry to remember UnitTestInformation objects.
class UnitTestListNode {
    friend class UnitTestRegistry;

    UnitTestListNode(UnitTestInformation *pInfo)
        : pNext(NULL), pInfo(pInfo) 
    {
    };
    UnitTestListNode *pNext;
    UnitTestInformation *pInfo;
};

static UnitTestRegistry *registry_instance = NULL;

UnitTestRegistry::UnitTestRegistry() : testList(NULL), passed(0), failed(0) {
}


void UnitTestRegistry::reg(UnitTestInformation *pInfo) {
    // Find pointer to last node (will be NULL)
    UnitTestListNode **ppLastNode = &testList;
    while (*ppLastNode) {
        ppLastNode = &((*ppLastNode)->pNext);
    }
    // Set it to a new node
    *ppLastNode = new UnitTestListNode(pInfo);
}


int UnitTestRegistry::run() {
    UnitTestListNode *test = testList;

    passed = 0;
    failed = 0;

    cout << "Running tests: ";
    while (test) {
        runOneTest(test->pInfo);
        test = test->pNext;
    }
    cout << endl 
        << "Test Summary\n"
        << "============\n";
    cout << endl << "Total tests: " << (passed + failed) << endl;
    cout << "Passed:      " << passed << endl;
    cout << "Failed:      " << failed << endl;
    return failed;
}


void UnitTestRegistry::runOneTest(UnitTestInformation *pInfo) {
    try {
        (pInfo->getFunction())();
        passed += 1;
        cout << ".";
    }
    catch (UnitTestException &e) {
        failed += 1;
        cout << endl << e.getMessage() << endl;
    }
    catch (...) {
        // Should be able to distinguish between failures
        // and errors
        failed += 1;
        cout << endl << "Unknown exception caught." << endl;
    }
}


UnitTestRegistry &UnitTestRegistry::get() {
    if (!registry_instance) {
        registry_instance = new UnitTestRegistry();
    }
    return *registry_instance;
}

UnitTestException::UnitTestException(char *f, int l, char *expr) 
    : file(f), line(l), message(expr) {
}

char *UnitTestException::getMessage() {
    return message;
}

UnitTestInformation::UnitTestInformation(
    UnitTestFn fn, 
    char * category, 
    char *name
) : fn(fn), category(category), name(name)
{
    UnitTestRegistry::get().reg(this);
}

