aboutsummaryrefslogtreecommitdiffstats
path: root/tests/lib/unit++/unit++.h
diff options
context:
space:
mode:
Diffstat (limited to 'tests/lib/unit++/unit++.h')
-rw-r--r--tests/lib/unit++/unit++.h344
1 files changed, 344 insertions, 0 deletions
diff --git a/tests/lib/unit++/unit++.h b/tests/lib/unit++/unit++.h
new file mode 100644
index 00000000..68483e78
--- /dev/null
+++ b/tests/lib/unit++/unit++.h
@@ -0,0 +1,344 @@
+// Copyright (C) 2001 Claus Dr�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