/*
 * kunittest.h
 *
 * Copyright (C)  2004  Zack Rusin <zack@kde.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file runner.h
 * Defines a set of macros and classes for running unit tests
 */

#ifndef KUNITTEST_RUNNER_H
#define KUNITTEST_RUNNER_H

#include <iostream>
using namespace std;

#include <qobject.h>
#include <qasciidict.h>
#include <qstring.h>

#include <kdelibs_export.h>

#include "tester.h"

class QSocketNotifier;

namespace KUnitTest
{
    /*! @def KUNITTEST_SUITE(suite)
     * 
     * This macro must be used if you are not making a test-module. The macro
     * defines the name of the test suite.
     */
    #define KUNITTEST_SUITE(suite)\
    static const QString s_kunittest_suite  = suite;

    /*! @def KUNITTEST_REGISTER_TESTER( tester )
     * @brief Automatic registration of Tester classes.
     *
     * This macro can be used to register the Tester into the global registry. Use
     * this macro in the implementation file of your Tester class. If you keep the
     * Tester classes in a shared or convenience library then you should not use this
     * macro as this macro relies on the static initialization of a TesterAutoregister class.
     * You can always use the static Runner::registerTester(const char *name, Tester *test) method.
    */
    #define KUNITTEST_REGISTER_TESTER( tester )\
    static TesterAutoregister tester##Autoregister( QString(s_kunittest_suite + QString("::") + QString::fromLocal8Bit(#tester)).local8Bit() , new tester ())

    #define KUNITTEST_REGISTER_NAMEDTESTER( name, tester )\
    static TesterAutoregister tester##Autoregister( QString(s_kunittest_suite + QString("::") + QString::fromLocal8Bit(name)).local8Bit() , new tester ())

    /*! The type of the registry. */
    typedef QAsciiDict<Tester> RegistryType;

    /*! A type that can be used to iterate through the registry. */
    typedef QAsciiDictIterator<Tester> RegistryIteratorType;
    
    /*! The Runner class holds a list of registered Tester classes and is able
     * to run those test cases. The Runner class follows the singleton design
     * pattern, which means that you can only have one Runner instance. This
     * instance can be retrieved using the Runner::self() method.
     *
     * The registry is an object of type RegistryType, it is able to map the name
     * of a test to a pointer to a Tester object. The registry is also a singleton
     * and can be accessed via Runner::registry(). Since there is only one registry,
     * which can be accessed at all times, test cases can be added without having to
     * worry if a Runner instance is present or not. This allows for a design in which
     * the KUnitTest library can be kept separate from the test case sources. Test cases
     * (classes inheriting from Tester) can be added using the static 
     * registerTester(const char *name, Tester *test) method. Allthough most users
     * will want to use the KUNITTEST_REGISTER_TESTER macro.
     *
     * @see KUNITTEST_REGISTER_TESTER
     */
    class KUNITTEST_EXPORT Runner : public QObject
    {
        Q_OBJECT
    
    public:
        /*! Registers a test case. A registry will be automatically created if necessary.
         * @param name The name of the test case.
         * @param test A pointer to a Tester object.
         */
        static void registerTester(const char *name, Tester *test);

        /*! @returns The registry holding all the Tester objects.
         */
        RegistryType &registry();

        /*! @returns The global Runner instance. If necessary an instance will be created.
         */
        static Runner *self();

        /*! @returns The number of registered test cases.
         */
        int numberOfTestCases();

        /*! Load all modules found in the folder. 
         * @param folder The folder where to look for modules.
         * @param query A regular expression. Only modules which match the query will be run.
         */
        static void loadModules(const QString &folder, const QString &query);

        /*! The runner can spit out special debug messages needed by the Perl script: kunittest_debughelper.
         * This script can attach the debug output of each suite to the results in the KUnitTest GUI.
         * Not very useful for console minded developers, so this static method can be used to disable
         * those debug messages.
         * @param enabled If true the debug messages are enabled (default), otherwise they are disabled.
         */
        static void setDebugCapturingEnabled(bool enabled);
            
    private:
        RegistryType         m_registry;
        static Runner       *s_self;
        static bool          s_debugCapturingEnabled;
    
    protected:
        Runner();
    
    public:
        /*! @returns The number of finished tests. */
        int numberOfTests() const;

        /*! @returns The number of passed tests. */
        int numberOfPassedTests() const;

        /*! @returns The number of failed tests, this includes the number of expected failures. */
        int numberOfFailedTests() const;

        /*! @returns The number of failed tests which were expected. */
        int numberOfExpectedFailures() const;

        /*! @returns The number of skipped tests. */
        int numberOfSkippedTests() const;

    public slots:
        /*! Call this slot to run all the registered tests.
         * @returns The number of finished tests.
         */
        int runTests();

        /*! Call this slot to run a single test.
         * @param name The name of the test case. This name has to correspond to the name
         * that was used to register the test. If the KUNITTEST_REGISTER_TESTER macro was
         * used to register the test case then this name is the class name.
         */
        void runTest(const char *name);

        /*! Call this slot to run tests with names starting with prefix.
         * @param prefix Only run tests starting with the string prefix.
         */
        void runMatchingTests(const QString &prefix);

        /*! Reset the Runner in order to prepare it to run one or more tests again.
         */
        void reset();

    signals:
        /*! Emitted after a test is finished.
         * @param name The name of the test.
         * @param test A pointer to the Tester object.
         */
        void finished(const char *name, Tester *test);
        void invoke();
    
    private:
        void registerTests();
    
    private:
        int globalSteps;
        int globalTests;
        int globalPasses;
        int globalFails;
        int globalXFails;
        int globalXPasses;
        int globalSkipped;
    };
    
    /*! The TesterAutoregister is a helper class to allow the automatic registration
     * of Tester classes.
     */
    class TesterAutoregister
    {
    public:
        /*! @param name A unique name that identifies the Tester class.
         * @param test A pointer to a Tester object.
         */
        TesterAutoregister(const char *name, Tester *test) 
        { 
            if ( test->name() == 0L ) test->setName(name);
            Runner::registerTester(name, test); 
        }
    };

}

#endif
