vmime/tests/lib/unit++/unit++.h

345 lines
9.5 KiB
C
Raw Normal View History

// Copyright (C) 2001 Claus Dr<44>by
// Terms of use are in the file COPYING
#ifndef __TEST_FW_H_
#define __TEST_FW_H_
#include <vector>
#include <string>
#include <map>
#include <iostream>
#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<unit++/unit++.h>
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<out_of_range>(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 <location of libunit++> -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<typename C>
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<typename C>
testcase(C* par, const std::string& name, typename test_mfun<C>::mfp fp)
: cnt(new size_t(1)), tst(new test_mfun<C>(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<typename E>
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<const test&>(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<out_of_range>(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<typename E>
testcase exception_case(const testcase& tc)
{
return testcase(new exception_test<E>(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<std::string> 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<std::string> ids;
std::vector<testcase> 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 T1, class T2>
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<typename E>
void exception_test<E>::operator()()
{
try {
(static_cast<test&>(tc))();
fail("unexpected lack of exception");
} catch (E& ) {
// fine!
}
}
/// Assert that the assertion is true, that is fail #if (!assertion) ...#
template<class A> 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<class T1, class T2>
inline void assert_eq(const std::string& msg, T1 exp, T2 got)
{
if (!(exp == got))
throw assert_value_error<T1,T2>(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