/* * Copyright (c) 2003-2004 Pau Arum. & David Garc.a * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef MiniCppUnit_hxx #define MiniCppUnit_hxx /** * @mainpage * miniCppUnit * (C) 2003-2006 Pau Arumi & David Garcia * * @version 2.5 2006-03-14 * - MS Visual compatibility: SConstruct ccflags, usage example, #ifdefs * @version 2.4 2006-03-14 * - exit test case after first failure * - double and float comparison with fuzzy equals (using scalable epsilon) * - have into account not a numbers * - new ASSERT_EQUALS_EPSILON macro * - more colors, and disabled when comiled in MS Visual * - removed catalan location. * - UsageExample.cxx now uses all macros and features * @version 2.3 2006-02-13 added usage example and SConstruct * @version 2.2 2004-11-28 code in english and tests suites * @version 2.1 2004-11-04 char* especialization * @version 2.0 2004-10-26 TestsFactory * @version 1.0 2003-10-28 initial * * Example of use: * * @code * #include "MiniCppUnit.hxx" * class MyTests : public TestFixture * { * public: * TEST_FIXTURE( MyTests ) * { * CAS_DE_TEST( testAddition ); * // etc * } * void testAddition() * { * ASSERT_EQUALS( 4, 1+1+2 ); * } * // etc * }; * * REGISTER_FIXTURE( MyTests ); * @endcode * @code * int main() * { * return TestFixtureFactory::theInstance().runTests() ? 0 : -1; * } * @endcode * Good things: * * - it's a tiny framework made up of two or three src files. * => no need to install as a library * - object oriented and makes use of several GoF patterns * - very simple usage. Just needs to learn very few C macros * - string asserts are simpler to use than cppunit * - string asserts are enhanced with coloured diffs * - concrete test classes are totally decoupled via static factory * => no src file have to include them all. * - it have test suite hierarchies * - compatible with non-standard compliant VisualC6 * (though not necessary good ;) */ #include #include #include #include /** * A singleton class. * Receives tests results and stores messages to the test log * for later listing. * It's a singleton for an easy global access from the 'Asserts' * methods but it is probably asking for a refactoring in order to limit * access only to TestFixtures */ class TestsListener { public: /** accessor to the global (static) singleton instance */ static TestsListener& theInstance(); std::stringstream& errorsLog(); std::string logString(); void currentTestName( std::string& name); static void testHasRun(); static void testHasFailed(); static void testHasThrown(); /** the human readable summary of run tests*/ std::string summary(); /** returns wheather all run tests have passed */ static bool allTestsPassed(); private: static const char* errmsgTag_nameOfTest() { return "Test failed: "; } /** constructor private: force the singleton to be wellbehaved ! */ TestsListener() : _currentTestName(0) { _executed=_failed=_exceptions=0; } std::string* _currentTestName; std::stringstream _log; unsigned _executed; unsigned _failed; unsigned _exceptions; }; class TestFailedException { }; /** * Abstract class with interface that allows run a test. That is runTest * and name. It is implemented by TestFixture and TestCase * * It does the 'Component' role in the 'Composite' patten **/ class Test { public: virtual ~Test(){} /** run the test: exercice the code and check results*/ virtual void runTest() = 0; /** the test human-readable name */ virtual std::string name() const = 0; }; /** * This class is just a placeholder for all assert functions --as static methods. * It is meant for being used just by the assert macros */ class Assert { static const char * errmsgTag_testFailedIn() { return "Test failed in "; } static const char * errmsgTag_inLine() { return ", line: "; }; static const char * errmsgTag_failedExpression() { return "Failed expression: "; } static const char * errmsgTag_expected() { return "Expected: "; } static const char * errmsgTag_butWas() { return "But was: "; } public: #if defined(_MSC_VER) || defined(__BORLANDC__) static const char * blue() { return ""; } static const char * green() { return ""; } static const char * red() { return ""; } static const char * normal() { return ""; } static const char * bold() { return ""; } static const char * yellow() { return ""; } #else static const char * blue() { return "\033[36;1m"; } static const char * green() { return "\033[32;1m"; } static const char * red() { return "\033[31;1m"; } static const char * normal() { return "\033[0m"; } static const char * bold() { return "\033[" "1m"; } static const char * yellow() { return "\033[93;1m"; } #endif template static void assertEquals( const AType& expected, const AType& result, const char* file="", int linia=0 ) { if(expected != result) { TestsListener::theInstance().errorsLog() << file << ", linia: " << linia << "\n" << errmsgTag_expected() << " " << expected << " " << errmsgTag_butWas() << " " << result << "\n"; TestsListener::theInstance().testHasFailed(); } } static void assertTrue(char* strExpression, bool expression, const char* file="", int linia=0); static void assertTrueMissatge(char* strExpression, bool expression, const char* missatge, const char* file="", int linia=0); static void assertEquals( const char * expected, const char * result, const char* file="", int linia=0 ); static void assertEquals( const bool& expected, const bool& result, const char* file="", int linia=0 ); static void assertEquals( const double& expected, const double& result, const char* file="", int linia=0 ); static void assertEquals( const float& expected, const float& result, const char* file="", int linia=0 ); static void assertEquals( const long double& expected, const long double& result, const char* file="", int linia=0 ); static void assertEqualsEpsilon( const double& expected, const double& result, const double& epsilon, const char* file="", int linia=0 ); static int notEqualIndex( const std::string & one, const std::string & other ); /** * we overload the assert with string doing colored diffs * * MS Visual6 doesn't allow string by reference :-( */ static void assertEquals( const std::string expected, const std::string result, const char* file="", int linia=0 ); static void fail(const char* motiu, const char* file="", int linia=0); }; /** * A TestFixture is a class that contain TestCases --which corresponds to * ConcreteTestFixture methods-- common objects uder tests, and setUp and * tearDown methods which are automatically executed before and after each * test case. * * Is the base class of ConcreteFixtures implemented by the framework user * * It does the 'Composite' role in the 'Composite' GoF pattern. * Its composite children are TestCases, which wrapps the test methods. * * It is a template class parametrized by ConcreteTestFixture so that it can * instantiate TestCase objects templatized with this same parameter: it needs the * concrete class type for calling its non-static methods. */ template class TestFixture : public Test { protected: typedef ConcreteTestFixture ConcreteFixture; typedef void(ConcreteTestFixture::*TestCaseMethod)(); /** * Wrapper for the test methods of concrete TestFixtures. * * Makes the 'Leave' role in the 'Composite' GoF pattern because can't be * be a composition of other tests. * * It's also a case of 'Command' pattern because it encapsules in an object * certain functionality whose execution depends on some deferred entity. */ class TestCase : public Test { public: TestCase(ConcreteFixture* parent, TestCaseMethod method, const std::string & name) : _parent(parent), _testCaseMethod(method), _name(name) { } /** calls TestFixture method. setUp and tearDown methods are called by * its parent TestFixture (in its runTest method). * it is robust to unexpected exceptions (throw) */ void runTest() { TestsListener::theInstance().testHasRun(); TestsListener::theInstance().currentTestName(_name); try { (_parent->*_testCaseMethod)(); } catch( std::exception& error ) { TestsListener::theInstance().testHasThrown(); TestsListener::theInstance().errorsLog() << "std::exception catched by MiniCppUnit: \n" << "what() : " << Assert::yellow() << error.what() << Assert::normal() << "\n"; } catch ( TestFailedException& failure) //just for skiping current test case { } catch(...) { TestsListener::theInstance().testHasThrown(); TestsListener::theInstance().errorsLog() << "non standard exception catched by MiniCppUnit.\n"; } } /** the TestFixture method hame */ std::string name() const { return _name; } private: ConcreteFixture* _parent; TestCaseMethod _testCaseMethod; std::string _name; }; //------------- end of class TestCase ---------------------------- private: typedef std::list TestCases; TestCases _testCases; std::string _name; void testsList() const { std::cout << "\n+ " << name() << "\n"; for( TestCases::const_iterator it=_testCases.begin(); it!=_testCases.end(); it++ ) std::cout << " - "<< (*it)->name() << "\n"; } public: virtual void setUp() {} virtual void tearDown() {} std::string name() const { return _name; }; TestFixture(const std::string& name="A text fixture") : _name(name) { } void afegeixCasDeTest(ConcreteFixture* parent, TestCaseMethod method, const char* name) { TestCase* casDeTest = new TestCase(parent, method, _name + "::" + name); _testCases.push_back( casDeTest ); } /** calls each test after setUp and tearDown TestFixture methods */ void runTest() { testsList(); TestCases::iterator it; for( it=_testCases.begin(); it!=_testCases.end(); it++) { setUp(); (*it)->runTest(); tearDown(); } } /** TestCase that wrapps TestFixture methods are dynamically created and owned by * the TestFixture. So here we clean it up*/ ~TestFixture() { TestCases::iterator it; for( it =_testCases.begin(); it!=_testCases.end(); it++) delete (*it); } }; /** * This class is aimed to hold a creator method for each concrete TestFixture */ class TestFixtureFactory { private: /** Well behaved singleton: * Don't allow instantiation apart from theInstance(), so private ctr.*/ TestFixtureFactory() { } typedef Test* (*FixtureCreator)(); std::list _creators; public: /** Accessor to the (static) singleton instance */ static TestFixtureFactory& theInstance() { static TestFixtureFactory theFactory; return theFactory; } bool runTests() { std::list::iterator it; for(it=_creators.begin(); it!=_creators.end(); it++) { FixtureCreator creator = *it; Test* test = creator(); test->runTest(); delete test; } std::string errors = TestsListener::theInstance().logString(); if (errors!="") std::cout << "\n\nError Details:\n" << errors; std::cout << TestsListener::theInstance().summary(); return TestsListener::theInstance().allTestsPassed(); } void addFixtureCreator(FixtureCreator creator) { _creators.push_back( creator ); } }; /** * Macro a usar despr�s de cada classe de test */ #define REGISTER_FIXTURE( ConcreteTestFixture ) \ \ Test* Creador##ConcreteTestFixture() { return new ConcreteTestFixture; } \ \ class Registrador##ConcreteTestFixture \ { \ public: \ Registrador##ConcreteTestFixture() \ { \ TestFixtureFactory::theInstance().addFixtureCreator( \ Creador##ConcreteTestFixture); \ } \ }; \ static Registrador##ConcreteTestFixture estatic##ConcreteTestFixture; /** * Assert macros to use in test methods. An assert is a test condition * we want to check. */ #define ASSERT_EQUALS( expected, result) \ Assert::assertEquals( expected, result, __FILE__, __LINE__ ); #define ASSERT_EQUALS_EPSILON( expected, result, epsilon) \ Assert::assertEqualsEpsilon( expected, result, epsilon, __FILE__, __LINE__ ); #define ASSERT( exp ) \ Assert::assertTrue(#exp, exp, __FILE__, __LINE__); #define ASSERT_MESSAGE( exp, message ) \ Assert::assertTrueMissatge(#exp, exp, message, __FILE__, __LINE__); #define FAIL( why ) \ Assert::fail(#why, __FILE__, __LINE__); /** * Macros that allows to write the constructor of the concrete TestFixture. * What the constructor does is agregate a wrapper for each test case (method) * As easy to write as this: * * @code * class MyTests : public TestFixture * { * public: * TEST_FIXTURE( MyTests ) * { * TEST_CASE( test ); * // etc * } * void test() * { * ASSERT_EQUALS( 4, 1+1+2 ); * } * @endcode */ #define TEST_FIXTURE( ConcreteFixture ) \ ConcreteFixture() : TestFixture( #ConcreteFixture ) #define TEST_CASE( methodName ) \ afegeixCasDeTest( this, &ConcreteFixture::methodName, #methodName ); #endif // MiniCppUnit_hxx