/* * 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 #if _MSC_VER < 1300 /** necesary for Visual 6 which don't define std::min */ namespace std { template T min(const T &a, const T &b) { return a < b ? a : b; } } #endif #include "../teamcity/teamcity_cppunit.h" extern JetBrains::TeamcityProgressListener gTeamCityListener; /** * 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 testHasPassed(); static void testHasFailed(const char *reason, const char *file, int line); 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(); 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: #ifdef _MSC_VER 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) { std::stringstream anError; anError << file << ", linia: " << linia << "\n" << errmsgTag_expected() << " " << expected << " " << errmsgTag_butWas() << " " << result << "\n"; // TestsListener::theInstance().errorsLog() << anError; TestsListener::theInstance().testHasFailed(anError.str().c_str(), file, linia); } } 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)(); TestsListener::theInstance().testHasPassed(); } 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 &) //just for skiping current test case { // the assert() calls testHasFailed() } 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*/ virtual ~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