searchengine/util/tsrc/itk/inc/public/itktesters.h
changeset 0 671dee74050a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/searchengine/util/tsrc/itk/inc/public/itktesters.h	Mon Apr 19 14:40:16 2010 +0300
@@ -0,0 +1,508 @@
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: 
+*
+*/
+#ifndef ITK_ITKTESTERS_H_
+#define ITK_ITKTESTERS_H_
+
+#include <exception>
+#include <iosfwd>
+#include <list>
+#include <string>
+
+namespace Itk
+{
+
+    class TestMgr;
+
+    
+    class IOCaptureExc : public std::exception
+    {
+    private:
+        //
+        // private members
+        //
+        const std::string what_;
+
+    public:
+        //
+        // public operators
+        //
+        virtual const char * what() const throw();
+
+        //
+        // lifetime mgmt
+        //
+        IOCaptureExc(const char * what);
+        IOCaptureExc(const IOCaptureExc & that);
+    };
+
+
+    /**
+     * Base class for all different flavours of test cases.
+     */
+    class TesterBase
+    {
+    private:
+        //
+        // private members
+        //
+        const std::string        name_;
+
+    public:
+        //
+        // public operations
+        //
+
+        /**
+         * Constructor
+         *
+         * @param name the name of this test case, must not be NULL.
+         */
+        TesterBase(const char * name);
+
+        /**
+         * @returns the human readable name of the test case
+         */
+        const std::string & name() const;
+
+
+        /**
+         * Prints this tests (and any sub-tests) in a nicely
+         * indented form to the given output stream.
+         *
+         * @param os the output stream to print to
+         * @param indent the starting indentation level, optional
+         */
+        virtual void printHierarchy(std::ostream & os,
+                                    int            indent = 0) const;
+        
+
+        /**
+         * @returns the number of actual (leaf) test cases under this
+         * tester.
+         */
+        virtual size_t count() const;
+        
+        /**
+         * Runs the test(s) embodied in this instance with
+         * all the administrative housekeeping done wrt testMgr.
+         */
+        void run(TestMgr * testMgr);
+
+        /**
+         * Destructor
+         */
+        virtual ~TesterBase() = 0;
+
+    protected:
+
+        /**
+         * Actually runs the test(s) embodied in this instance.
+         */
+        virtual void doRun(TestMgr * testMgr) = 0;
+
+
+        void indentSpaces(int            indent,
+                          std::ostream & os) const;
+    };
+
+
+
+    /**
+     * A test suite is a set of test (cases, suites or contexts).
+     */
+    class SuiteTester : public TesterBase
+    {
+    private:
+        //
+        // private members
+        //
+        std::list<TesterBase*>      testerBases_;
+
+
+    public:
+        //
+        // public operators
+        //
+
+        /**
+         * Constructor
+         *
+         * @param name the name of this test suite, must not be NULL.
+         */
+        SuiteTester(const char * name);
+
+
+        /**
+         * From TesterBase.
+         */
+        virtual void printHierarchy(std::ostream & os,
+                                    int            indent = 0) const;
+
+
+        /**
+         * @returns the number of actual (leaf) test cases under this
+         * tester.
+         */
+        virtual size_t count() const;
+
+
+        /**
+         * Adds a test case into this suite.
+         */
+        void add(TesterBase * testerBase);
+
+        /**
+         * Adds a test case, implemented by a simple free function
+         * (C-style) to this suite.
+         *
+         * @param name the name of the test case, must not be NULL.
+         *
+         * @param testFunc the testMgr function, may use ITK_EXPECT,
+         * ITK_ASSERT and ITK_MSG macros
+         */
+        void add(const char * name,
+                 void      (* testFunc)(TestMgr *));
+
+
+        /**
+         * Adds a test case, implemented by a non-static member
+         * function of a C++ class to this suite.
+         *
+         * @param name the name of the test case, must not be NULL.
+         *
+         * @param ptr the instance on which the memFunc will have to
+         * operate, being a non-static member function. Its ownership
+         * is NOT transferred by this call. Must not be NULL. For
+         * static member functions, you use the function above.
+         *
+         * @param memFunc the member function implementing the test,
+         * may use ITK_EXPECT, ITK_ASSERT and ITK_MSG macros
+         */
+        template<typename C>
+        void add(const char * name,
+                 C          * ptr,
+                 void  (C:: * memFunc)(TestMgr *));
+
+        
+
+        /**
+         * Given to overloaded versions of add() below, it tells them
+         * to generate IO only, not to compare / test for it.
+         */
+        static const char REDIRECT_ONLY[];
+
+
+        /**
+         * The reason for this homebred test framework is these next
+         * two functions (the rest is provided by countless other
+         * frameworks).
+         *
+         * In some cases, notably when integrating with existing tests
+         * for 3rd party applications, it does not help to be able to
+         * assert things, simply because 3rd party test applications
+         * will produce a bunch of messages on stdout / stderr, and
+         * that cannot be tested by assertions.
+         *
+         * This is also the case when one tests a functionality of a
+         * text processor component. While it may be possible to
+         * manually load text, perform the operation and then compare
+         * it with what one should get, it is a manual, repetitive and
+         * tedious labor - not suited for test developers.
+         *
+         * Hence the need for capturing IO for test purposes,
+         * redirecting it to/from pre-defined test case files.
+         *
+         *
+         * 1 Capturing Output
+         * ==================
+         *
+         * Let's assume that defFilesBaseName is 'my-tests-1' and the
+         * TestMgr instance was constructed with 'c:\Data\' as its
+         * ioTestCasesDir argument. In this case we consider the base
+         * filepath 'c:\Data\my-test-1'.
+         *
+         * The standard output is always redirected to file
+         * 'c:\Data\my-test-1_res_out.txt', meaning that this file is
+         * the RESult that has been sent to the standard OUTput.
+         *
+         * At the completion of the test func execution, file
+         * 'c:\Data\my-test-1_exp_out.txt' (exp for EXPected) is
+         * tested for. If it exists, then these two files are
+         * compared, and the results are reported accordingly (success
+         * on same content, failure on difference). If the pre-defined
+         * file does not exist, then 'res' is saved as 'exp', and what
+         * is reported that an IO test definition has been created
+         * (neither pass, nor failure, but define).
+         *
+         * Obviously, you should get a 'success' instead of a 'define'
+         * on a second testrun, since the pre-defined output is there
+         * now - unless you have a test operation that produces
+         * different output on different invocations in which case
+         * you are almost beyond help, but see parameter 'lenience'.
+         *
+         * Once you have defined a test case thus, you have to
+         * manually go through it to make sure that what you defined
+         * is indeed the correct behaviour. 'Exp' files should be
+         * versioned and deployed along with the test application.
+         *
+         *
+         * 2 Capturing Error
+         * =================
+         *
+         * The standard error is always redirected too, just like the
+         * standard error. Files under considerations are
+         * 'c:\Data\my-test-1_res_err.txt' and
+         * 'c:\Data\my-test-1_exp_err.txt' in our example. The
+         * semantics are almost exactly the same as with the standard
+         * output, except if the 'res' (RESult) file is empty, then no
+         * 'exp' file will be created either. This is because if a
+         * program produces anything on the standard error, it usually
+         * already marks error. If the 'exp' file is there, then 'res'
+         * must be identical, if the 'exp' file is not there, then
+         * 'res' must be empty too on a test run.
+         *
+         * If some output is expected on the standard error, then the
+         * 'exp' file for the standard error should be versioned and
+         * deployed along with the test applciation.
+         * 
+         *
+         * 3 Capturing Input
+         * ================
+         *
+         * If 'c:\Data\my-test-1_in.txt' exists, then the standard
+         * input is 'redirected' to be read from this file. Therefore
+         * if your test function invokes an operation that expects
+         * some input on the console, it will come from that text
+         * file. If there is no 'in' file yet input is expected from
+         * the console, the testMgr user must enter that input
+         * manually, during run, interactively.
+         *
+         * 'In' files should be versioned and deployed along with the
+         * test application. There is no support for saving
+         * interactively entered text into an 'in' file (definition of
+         * input), that is, 'in' files must be created manually by the
+         * test case developer.
+         *
+         *
+         * @param name name of the test case, must not be NULL.
+         *
+         * @param testFunc the free function that can perform some
+         * testing. This function may invoke operations that produce
+         * output (or even demand input), and the testing is actually
+         * done by capturing this IO and comparing with the
+         * pre-defined output. The test function, of course, may also
+         * use the ususual ITK_EXPECT, ITK_ASSERT and ITK_MSG macros
+         * too.
+         *
+         * @param defFilesBaseName the base file name for the
+         * pre-defined tests for this test case
+         *
+         * @param lenience if defined, will allow lines containing
+         * that string differ. This is useful if you have a test ouput
+         * that generates the date/time or elapsed time or such thing,
+         * which will differ from execution to execution but should be
+         * considered as a pass. (Unfortunately no regexp supported.)
+         * A special case of leniency is if SuiteTester::REDIRECT_ONLY
+         * is passed along - that means that the output/error streams
+         * are captured and redirected to files, but they are not
+         * tested for equivalence at all. Useful in cases where one
+         * wants to generate performance output or other highly
+         * volatile output that differs from run to run and the actual
+         * content is not that important.
+         *
+         */
+        void add(const char   * name,
+                 void        (* testFunc)(TestMgr *),
+                 const char   * defFilesBaseName,
+                 const char   * lenience = NULL);
+
+
+        /**
+         * Exactly the same semantics of IO capturing as the add
+         * overload above, but with member functions instead of free
+         * functions.
+         */
+        template<typename C>
+        void add(const char   * name,
+                 C            * ptr,
+                 void    (C:: * memFunc)(TestMgr *),
+                 const char   * defFileBaseName,
+                 const char   * lenience = NULL);
+
+
+
+        /**
+         * @param name the name of a child test
+         *
+         * @returns the pointer to the child test, if found, otherwise
+         * NULL.
+         */
+        TesterBase * getChild(const char * name) const;
+
+
+        /**
+         * Destructs this test suite.
+         */
+        virtual ~SuiteTester();
+
+
+    protected:
+        virtual void doRun(TestMgr * testMgr);
+    };
+
+
+    
+    /**
+     * An exception class to be used to signal panic.
+     */
+    class PanicExc : public std::exception
+    {
+    private:
+        //
+        // private members
+        //
+        std::string file_;
+        size_t      line_;
+        std::string msg_;
+
+
+    public:
+        //
+        // public operators
+        //
+        PanicExc(const char * file,
+                 size_t       line,
+                 const char * format,
+                 ...);
+            
+        const char * file() const;
+        size_t line() const;
+        const char * msg() const;
+
+        virtual const char * what() const throw();
+    };
+    
+
+
+    /**
+     * The concept of a common state carried on by a sequence of test
+     * operations is abstracted by this interface. See comments for
+     * ContextTester.
+     */
+    class ITestContext
+    {
+    public:
+        //
+        // public operators
+        //
+
+        /**
+         * Sets up this context. If the setup has failed, use the
+         * ITK_PANIC macro.
+         */
+        virtual void setup() throw (PanicExc) = 0;
+
+
+        /**
+         * Tears down this context.
+         */
+        virtual void tearDown() throw () = 0;
+
+
+        /**
+         * Destructs this context.
+         */
+        virtual ~ITestContext() = 0;
+    };
+
+
+    /**
+     * Creates a context for a sequence (or even hierarchy) of test
+     * cases.
+     *
+     * If the test cases as such that they carry on a common state,
+     * that is, subsequent test operations assume a certain state as a
+     * result of previous test operations, then you have to somehow
+     * manage that state.
+     *
+     * That state is embodies by an ITestContext implementation. A state
+     * can be an object in the process of a context, but it can be
+     * equally something that is embodied in a file (or index
+     * database) in the file system.
+     *
+     * When a test context is executed, its context (ITestContext) is
+     * first set up, and when it completes execution, it is torn
+     * down. The easiest way is to implement the test operations as
+     * member functions of an ITestContext implementation, and add those
+     * member functions to this test context for execution (see
+     * SuiteTester::add()).
+     *
+     * ITK_ASSERT-s, when fail, always interrupt the execution of test
+     * sequence within one test context. That is, a failed ITK_ASSERT
+     * will abort the current test context. ITK_ASSERT has the
+     * semantics of failing a test in such a way that the common state
+     * is so corrupted that there is no whatsoever point in continuing
+     * with further test operations within the same test
+     * context. (There is always a root context, automatically
+     * employed, when calling Itk::TestMgr::test(...)).
+     */
+    class ContextTester : public SuiteTester
+    {
+    private:
+        //
+        // private members
+        //
+        ITestContext    * context_;
+
+    public:
+        //
+        // public
+        //
+        
+        /**
+         * Constructs a ContextTester.
+         *
+         * @param name the name of this test (context)
+         *
+         * @param context the context to use. Ownership of any
+         * non-NULL context is transferred to this newly constructed
+         * object (it will be deleted when this is destructed). May be
+         * NULL, in which case a default context is used - useful in
+         * situations when one wants to have a test context to wich
+         * ITK_ASSERT and ITK_PANIC macros should fall back to.
+         */
+        ContextTester(const char      * name,
+                      ITestContext    * context);
+
+
+        /**
+         * Destructor.
+         */
+        virtual ~ContextTester();
+
+    protected:
+        /**
+         *
+         */
+        virtual void doRun(TestMgr * testMgr);
+    };
+
+
+
+}
+
+#endif // ITK_ITKTESTERS_H_