testing: Started using the MiniCppUnit framework
This commit is contained in:
parent
1c5ed9b1c0
commit
61242571dd
4
Makefile
4
Makefile
|
@ -7,7 +7,7 @@ OBJS=$(SRCS:%.cpp=%.o)
|
||||||
|
|
||||||
all: ${OBJS} main.o
|
all: ${OBJS} main.o
|
||||||
g++ -Wall -g -o cppcheck $^
|
g++ -Wall -g -o cppcheck $^
|
||||||
test: ${OBJS} tests.o
|
test: ${OBJS} TestsRunner.o MiniCppUnit.o testmemleak.o
|
||||||
g++ -Wall -g -o cppcheck_test $^
|
g++ -Wall -g -o testsrunner $^
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o cppcheck_test cppcheck
|
rm -f *.o cppcheck_test cppcheck
|
||||||
|
|
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003-2004 Pau Arum<EFBFBD> & David Garc<EFBFBD>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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MiniCppUnit.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <float.h>
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
inline bool isnan(T x) {
|
||||||
|
return _isnan(x) != 0;
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
inline bool isinf(T x) {
|
||||||
|
return _finite(x) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TestsListener& TestsListener::theInstance()
|
||||||
|
{
|
||||||
|
static TestsListener instancia;
|
||||||
|
return instancia;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream& TestsListener::errorsLog()
|
||||||
|
{
|
||||||
|
if (_currentTestName)
|
||||||
|
_log << "\n" << errmsgTag_nameOfTest() << (*_currentTestName) << "\n";
|
||||||
|
return _log;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TestsListener::logString()
|
||||||
|
{
|
||||||
|
std::string aRetornar = _log.str();
|
||||||
|
_log.str("");
|
||||||
|
return aRetornar;
|
||||||
|
}
|
||||||
|
void TestsListener::currentTestName( std::string& name)
|
||||||
|
{
|
||||||
|
_currentTestName = &name;
|
||||||
|
}
|
||||||
|
void TestsListener::testHasRun()
|
||||||
|
{
|
||||||
|
std::cout << ".";
|
||||||
|
theInstance()._executed++;
|
||||||
|
}
|
||||||
|
void TestsListener::testHasFailed()
|
||||||
|
{
|
||||||
|
std::cout << "F";
|
||||||
|
theInstance()._failed++;
|
||||||
|
throw TestFailedException();
|
||||||
|
}
|
||||||
|
void TestsListener::testHasThrown()
|
||||||
|
{
|
||||||
|
std::cout << "E";
|
||||||
|
theInstance()._exceptions++;
|
||||||
|
}
|
||||||
|
std::string TestsListener::summary()
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
os << "\nSummary:\n"
|
||||||
|
<< Assert::bold() << "\tExecuted Tests: "
|
||||||
|
<< _executed << Assert::normal() << std::endl
|
||||||
|
<< Assert::green() << "\tPassed Tests: "
|
||||||
|
<< (_executed-_failed-_exceptions)
|
||||||
|
<< Assert::normal() << std::endl;
|
||||||
|
if (_failed > 0)
|
||||||
|
{
|
||||||
|
os << Assert::red() << "\tFailed Tests: "
|
||||||
|
<< _failed << Assert::normal() << std::endl;
|
||||||
|
}
|
||||||
|
if (_exceptions > 0)
|
||||||
|
{
|
||||||
|
os << Assert::yellow() << "\tUnexpected exceptions: "
|
||||||
|
<< _exceptions << Assert::normal() << std::endl;
|
||||||
|
}
|
||||||
|
os << std::endl;
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
bool TestsListener::allTestsPassed()
|
||||||
|
{
|
||||||
|
return !theInstance()._exceptions && !theInstance()._failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Assert::assertTrue(char* strExpression, bool expression,
|
||||||
|
const char* file, int linia)
|
||||||
|
{
|
||||||
|
if (!expression)
|
||||||
|
{
|
||||||
|
TestsListener::theInstance().errorsLog() << "\n"
|
||||||
|
<< errmsgTag_testFailedIn() << file
|
||||||
|
<< errmsgTag_inLine() << linia << "\n"
|
||||||
|
<< errmsgTag_failedExpression()
|
||||||
|
<< bold() << strExpression << normal() << "\n";
|
||||||
|
TestsListener::theInstance().testHasFailed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Assert::assertTrueMissatge(char* strExpression, bool expression,
|
||||||
|
const char* missatge, const char* file, int linia)
|
||||||
|
{
|
||||||
|
if (!expression)
|
||||||
|
{
|
||||||
|
TestsListener::theInstance().errorsLog() << "\n"
|
||||||
|
<< errmsgTag_testFailedIn() << file
|
||||||
|
<< errmsgTag_inLine() << linia << "\n"
|
||||||
|
<< errmsgTag_failedExpression()
|
||||||
|
<< bold() << strExpression << "\n"
|
||||||
|
<< missatge<< normal() << "\n";
|
||||||
|
TestsListener::theInstance().testHasFailed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Assert::assertEquals( const char * expected, const char * result,
|
||||||
|
const char* file, int linia )
|
||||||
|
{
|
||||||
|
assertEquals(std::string(expected), std::string(result),
|
||||||
|
file, linia);
|
||||||
|
|
||||||
|
}
|
||||||
|
void Assert::assertEquals( const bool& expected, const bool& result,
|
||||||
|
const char* file, int linia )
|
||||||
|
{
|
||||||
|
assertEquals(
|
||||||
|
(expected?"true":"false"),
|
||||||
|
(result?"true":"false"),
|
||||||
|
file, linia);
|
||||||
|
}
|
||||||
|
|
||||||
|
// floating point numbers comparisons taken
|
||||||
|
// from c/c++ users journal. dec 04 pag 10
|
||||||
|
bool isNaN(double x)
|
||||||
|
{
|
||||||
|
bool b1 = (x < 0.0);
|
||||||
|
bool b2 = (x >= 0.0);
|
||||||
|
return !(b1 || b2);
|
||||||
|
}
|
||||||
|
|
||||||
|
double scaledEpsilon(const double& expected, const double& fuzzyEpsilon )
|
||||||
|
{
|
||||||
|
const double aa = fabs(expected)+1;
|
||||||
|
return (std::isinf(aa))? fuzzyEpsilon: fuzzyEpsilon * aa;
|
||||||
|
}
|
||||||
|
bool fuzzyEquals(double expected, double result, double fuzzyEpsilon)
|
||||||
|
{
|
||||||
|
return (expected==result) || ( fabs(expected-result) <= scaledEpsilon(expected, fuzzyEpsilon) );
|
||||||
|
}
|
||||||
|
void Assert::assertEquals( const double& expected, const double& result,
|
||||||
|
const char* file, int linia )
|
||||||
|
{
|
||||||
|
const double fuzzyEpsilon = 0.000001;
|
||||||
|
assertEqualsEpsilon( expected, result, fuzzyEpsilon, file, linia );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Assert::assertEquals( const float& expected, const float& result,
|
||||||
|
const char* file, int linia )
|
||||||
|
{
|
||||||
|
assertEquals((double)expected, (double)result, file, linia);
|
||||||
|
}
|
||||||
|
void Assert::assertEquals( const long double& expected, const long double& result,
|
||||||
|
const char* file, int linia )
|
||||||
|
{
|
||||||
|
assertEquals((double)expected, (double)result, file, linia);
|
||||||
|
}
|
||||||
|
void Assert::assertEqualsEpsilon( const double& expected, const double& result, const double& epsilon,
|
||||||
|
const char* file, int linia )
|
||||||
|
{
|
||||||
|
if (isNaN(expected) && isNaN(result) ) return;
|
||||||
|
if (!isNaN(expected) && !isNaN(result) && fuzzyEquals(expected, result, epsilon) ) return;
|
||||||
|
|
||||||
|
TestsListener::theInstance().errorsLog()
|
||||||
|
<< errmsgTag_testFailedIn() << file
|
||||||
|
<< errmsgTag_inLine() << linia << "\n"
|
||||||
|
<< errmsgTag_expected()
|
||||||
|
<< bold() << expected << normal() << " "
|
||||||
|
<< errmsgTag_butWas()
|
||||||
|
<< bold() << result << normal() << "\n";
|
||||||
|
TestsListener::theInstance().testHasFailed();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Assert::notEqualIndex( const std::string & one, const std::string & other )
|
||||||
|
{
|
||||||
|
int end = std::min(one.length(), other.length());
|
||||||
|
for ( int index = 0; index < end; index++ )
|
||||||
|
if (one[index] != other[index] )
|
||||||
|
return index;
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* we overload the assert with string doing colored diffs
|
||||||
|
*
|
||||||
|
* MS Visual6 doesn't allow string by reference :-(
|
||||||
|
*/
|
||||||
|
void Assert::assertEquals( const std::string expected, const std::string result,
|
||||||
|
const char* file, int linia )
|
||||||
|
{
|
||||||
|
if(expected == result)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int indexDiferent = notEqualIndex(expected, result);
|
||||||
|
TestsListener::theInstance().errorsLog()
|
||||||
|
<< file << ", linia: " << linia << "\n"
|
||||||
|
<< errmsgTag_expected() << "\n" << blue()
|
||||||
|
<< expected.substr(0,indexDiferent)
|
||||||
|
<< green() << expected.substr(indexDiferent)
|
||||||
|
<< normal() << "\n"
|
||||||
|
<< errmsgTag_butWas() << blue() << "\n"
|
||||||
|
<< result.substr(0,indexDiferent)
|
||||||
|
<< red() << result.substr(indexDiferent)
|
||||||
|
<< normal() << std::endl;
|
||||||
|
|
||||||
|
TestsListener::theInstance().testHasFailed();
|
||||||
|
}
|
||||||
|
void Assert::fail(const char* motiu, const char* file, int linia)
|
||||||
|
{
|
||||||
|
TestsListener::theInstance().errorsLog() <<
|
||||||
|
file << errmsgTag_inLine() << linia << "\n" <<
|
||||||
|
"Reason: " << motiu << "\n";
|
||||||
|
|
||||||
|
TestsListener::theInstance().testHasFailed();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,487 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2003-2004 Pau Arum<EFBFBD> & David Garc<EFBFBD>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<MyTests>
|
||||||
|
* {
|
||||||
|
* 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 <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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:
|
||||||
|
#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<typename AType>
|
||||||
|
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 <typename ConcreteTestFixture>
|
||||||
|
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<Test*> 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<FixtureCreator> _creators;
|
||||||
|
public:
|
||||||
|
/** Accessor to the (static) singleton instance */
|
||||||
|
static TestFixtureFactory& theInstance()
|
||||||
|
{
|
||||||
|
static TestFixtureFactory theFactory;
|
||||||
|
return theFactory;
|
||||||
|
}
|
||||||
|
bool runTests()
|
||||||
|
{
|
||||||
|
std::list<FixtureCreator>::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<EFBFBD>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<MyTests>
|
||||||
|
* {
|
||||||
|
* public:
|
||||||
|
* TEST_FIXTURE( MyTests )
|
||||||
|
* {
|
||||||
|
* TEST_CASE( test );
|
||||||
|
* // etc
|
||||||
|
* }
|
||||||
|
* void test()
|
||||||
|
* {
|
||||||
|
* ASSERT_EQUALS( 4, 1+1+2 );
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define TEST_FIXTURE( ConcreteFixture ) \
|
||||||
|
ConcreteFixture() : TestFixture<ConcreteFixture>( #ConcreteFixture )
|
||||||
|
|
||||||
|
#define TEST_CASE( methodName ) \
|
||||||
|
afegeixCasDeTest( this, &ConcreteFixture::methodName, #methodName );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // MiniCppUnit_hxx
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "MiniCppUnit.h"
|
||||||
|
#include "tokenize.h"
|
||||||
|
|
||||||
|
bool ShowAll = true;
|
||||||
|
bool CheckCodingStyle = true;
|
||||||
|
bool Debug = false;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Provide a dummy filename for the error messages
|
||||||
|
Files.push_back( std::string("test.cpp") );
|
||||||
|
|
||||||
|
return TestFixtureFactory::theInstance().runTests() ? 0 : -1;
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
|
||||||
|
#include "tokenize.h"
|
||||||
|
#include "CheckMemoryLeak.h"
|
||||||
|
#include "MiniCppUnit.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
extern std::ostringstream errout;
|
||||||
|
|
||||||
|
class TestMemleak : public TestFixture<TestMemleak>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
void check( const char code[] )
|
||||||
|
{
|
||||||
|
// Tokenize..
|
||||||
|
tokens = tokens_back = NULL;
|
||||||
|
std::istringstream istr(code);
|
||||||
|
TokenizeCode( istr );
|
||||||
|
SimplifyTokenList();
|
||||||
|
|
||||||
|
// Clear the error buffer..
|
||||||
|
errout.str("");
|
||||||
|
|
||||||
|
// Check for memory leaks..
|
||||||
|
CheckMemoryLeak();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
TEST_FIXTURE( TestMemleak )
|
||||||
|
{
|
||||||
|
TEST_CASE( simple1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void simple1()
|
||||||
|
{
|
||||||
|
check( "void f()\n"
|
||||||
|
"{\n"
|
||||||
|
" int *a = new int[10];\n"
|
||||||
|
"}\n" );
|
||||||
|
ASSERT_EQUALS( std::string("[test.cpp:4]: Memory leak: a\n"), errout.str() );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_FIXTURE( TestMemleak )
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue