// Copyright (C) 2001 Claus Dręby // Terms of use are in the file COPYING #ifndef __TEST_FW_H_ #define __TEST_FW_H_ #include #include #include #include #include "optmap.h" /** * The unitpp name space holds all the stuff needed to use the unit++ testing * framework. * * The normal way to make a test is like this: * \begin{verbatim} #include using namespace unitpp; // use anonymous namespace so all test classes can be named Test namespace { class Test : public suite { void test1() { // do test stuff assert_true("message", exp1); // exp1 should be true assert_eq("another msg", 123456, exp2); // exp2 should be 123456 // ... } void test2() { // do something that provokes exception out_of_range } public: Test() : suite("appropriate name for test suite") { // any setup you need add("id1", testcase(this, "Test 1", &Test::test1)); // make a testcase from the method testcase tc(this, "Test 2", &Test::test2); // add a testcase that expects the exception add("id2", exception_case(tc)); // add the suite to the global test suite suite::main().add("id", this); } } * theTest = new Test(); // by new, since testcase claims ownership } \end{verbatim} * * In order to make an executable test, simply link the above code against * libunit++, something like * * #g++ -o test++ mytest.cc -L -lunit++# * * This will generate a test called #test++# and the standard behaviour for a * test. Note that most shells have #test# defined as a shell builtin which * makes it a moderately bad name for a program, since it is rather hard to * get executed, hence #test++#. * @see main */ namespace unitpp { class visitor; /** * The heart of a test system: A test. The test is meant as a base class for * the tests that a client want performed. This means that all tests are to * be pointers dynamically allocated. However, the test system takes * responsibilities for freeing them again. * * The function call overload mechanism is used for the executable part of * the test. */ class test { std::string nam; public: /// A test just needs a name test(const std::string& name) : nam(name) {} virtual ~test() {} /// The execution of the test virtual void operator()() = 0; virtual void visit(visitor*); virtual test* get_child(const std::string&) { return 0; } std::string name() const { return nam; } }; /** * A test that is implemented by a member function. */ template class test_mfun : public test { public: typedef void (C::*mfp)(); /// An object, a name, and a pointer to a member function. test_mfun(C* par, const std::string& name, mfp fp) : test(name), par(par), fp(fp) {} /// Executed by invoking the function in the object. virtual void operator()() { (par->*fp)(); } private: C* par; mfp fp; }; /** * A ref counted reference to a test. This is what test suites are composed * of, and what ensures destruction. */ class testcase { size_t* cnt; test* tst; void dec_cnt(); public: /// Simply wrap -- and own -- a test. testcase(test* t); /// Keep the ref count testcase(const testcase& tr); /** * Make a testcase from a class and a member function. * * The normal usage is inside some test suite class Test: * * #add("id", testcase(this, "Testing this and that", &Test::test))# * * to make a test that invokes the test method on the instance of the * suite class. * \Ref{test_mfun} */ template testcase(C* par, const std::string& name, typename test_mfun::mfp fp) : cnt(new size_t(1)), tst(new test_mfun(par, name, fp)) { } ~testcase(); /// Assignment that maintains reference count. testcase& operator=(const testcase&); void visit(visitor* vp) const { tst->visit(vp); } operator test& () { return *tst; } operator const test& () const { return *tst; } }; /** * A wrapper class for the testcase class that succedes if the correct * exception is generated. */ template class exception_test : public test { public: /** * The constructor needs a testcase to wrap. This exception_test will * fail unless the wrapped testcase generates the exception. * * The name of the exception_test is copied from the wrapped test. */ exception_test(const testcase& tc) : test(static_cast(tc).name()), tc(tc) {} ~exception_test() {} /// Runs the wrapped test, and fails unless the correct exception is thrown. virtual void operator()(); private: testcase tc; }; /** * Generate a testcase that expects a specific exception from the testcase it * wraps. It can be used something like * * #testcase tc(this, "Test name", &Test::test);# * * #add("ex", exception_case(tc));# * * The name of the exception_case is copied from the wrapped testcase, and * the exception_case will execute the tc test case and report a failure * unless the #out_of_range# exception is generated. */ template testcase exception_case(const testcase& tc) { return testcase(new exception_test(tc)); } /** * Splits the string by char c. Each c will generate a new element in the * vector, including leading and trailing c. */ extern std::vector vectorize(const std::string& str, char c); /** * A suite is a test that happens to be a collection of tests. This is an * implementation of the Composite pattern. */ class suite : public test { std::vector ids; std::vector tests; public: /// Make an empty test suite. suite(const std::string& name) : test(name) {} virtual ~suite() {}; /// Add a testcase to the suite. void add(const std::string& id, const testcase& t); /** * Get a child with the specified id. * @return 0 if not found. */ virtual test* get_child(const std::string& id); /// An empty implementation. virtual void operator()() {} /// Allow a visitor to visit a suite node of the test tree. void visit(visitor*); /// Get a reference to the main test suite that the main program will run. static suite& main(); // Splits the string by dots, and use each id to find a suite or test. test* find(const std::string& id); }; /** * The visitor class is a base class for classes that wants to participate in * the visitor pattern with the test hierarchi. * * This is a slightly extended visitor pattern implementation, intended for * collaboration with the Composite pattern. The aggregate node (here the * suite node) is visited twice, before and after the children are visited. * This allows different algorithms to be implemented. */ class visitor { public: virtual ~visitor() {} /// Visit a test case, that is not a suite. virtual void visit(test&) = 0; /// Visit a suite node before the children are visited. virtual void visit(suite&) {}; /** * Visit a suite after the children are visited */ virtual void visit(suite&, int dummy) = 0; // post childs }; /// The basic for all failed assert statements. class assertion_error : public std::exception { std::string msg; public: /// An assertion error with the given message. assertion_error(const std::string& msg) : msg(msg) {} /// std::string message() const { return msg; } virtual ~assertion_error() throw () {} /** * The virtual method used for operator<<. */ virtual void out(std::ostream& os) const; }; /** * This exception represents a failed comparison between two values of types * T1 and T2. Both the expected and the actually value are kept. */ template class assert_value_error : public assertion_error { T1 exp; T2 got; public: /// Construct by message, expected and gotten. assert_value_error(const std::string& msg, T1& exp, T2& got) : assertion_error(msg), exp(exp), got(got) { } virtual ~assert_value_error() throw () {} /** * Specialized version that requires both T1 and T2 to support * operator<<(ostream&, Tn). */ virtual void out(std::ostream& os) const { os << message() << " [expected: `" << exp << "' got: `" << got << "']"; } }; /// The test was not succesful. inline void fail(const std::string& msg) { throw assertion_error(msg); } template void exception_test::operator()() { try { (static_cast(tc))(); fail("unexpected lack of exception"); } catch (E& ) { // fine! } } /// Assert that the assertion is true, that is fail #if (!assertion) ...# template inline void assert_true(const std::string& msg, A assertion) { if (!assertion) throw assertion_error(msg); } /// Assert that the two arguments are equal in the #==# sense. template inline void assert_eq(const std::string& msg, T1 exp, T2 got) { if (!(exp == got)) throw assert_value_error(msg, exp, got); } /* * Put an assertion error to a stream, using the out method. The out method * is virtual. */ inline std::ostream& operator<<(std::ostream& os, const unitpp::assertion_error& a) { a.out(os); return os; } /** * The singleton instance of the option handler of main. * * This allows a test to add its own flags to the resulting test program, in * the following way. * * #bool x_flg = false;# * #unitpp::options().add("x", new options_utils::opt_flag(x_flg));# * * If a -x is now given to the resulting test it will set the #x_flg# * variable; */ options_utils::optmap& options(); /** * An instance of this class hooks the GUI code into the test executable. * Hence, make a global variable of class gui_hook to allow the -g option to * a test. * * If the library is compiled without GUI support, it is still legal to * create an instance of gui_hook, but it will not add the -g option. */ class gui_hook { public: gui_hook(); }; } #endif