This commit is contained in:
Gabriel Schlozer 2017-01-20 01:02:43 +00:00 • committed by GitHub
commit 05d62be59a
25 changed files with 1498 additions and 41 deletions

View file

@ -9,6 +9,7 @@
#include "AssertException.h"
#include "RequiredCheckException.h"
#include "CurrentTest.h"
#include "ParameterizedManager.h"
#ifdef UNITTEST_NO_EXCEPTIONS
#include "ReportAssertImpl.h"
@ -20,12 +21,16 @@
namespace UnitTest {
//TODO remove template and parameter "templateIsTest", replace with an interface
template< typename T >
void ExecuteTest(T& testObject, TestDetails const& details, bool isMockTest)
void ExecuteTest(T& testObject, TestDetails const& details, bool isMockTest, bool templateIsTest)
{
if (isMockTest == false)
CurrentTest::Details() = &details;
if (templateIsTest)
ParameterizedManager::getInstance().beginExecute(&details);
#ifdef UNITTEST_NO_EXCEPTIONS
if (UNITTEST_SET_ASSERT_JUMP_TARGET() == 0)
{
@ -54,6 +59,8 @@ namespace UnitTest {
#ifdef UNITTEST_NO_EXCEPTIONS
}
#endif
if (templateIsTest)
ParameterizedManager::getInstance().endExecute(&details);
}
}

View file

@ -0,0 +1,43 @@
#ifndef UNITTEST_PARAMETERIZEDMACROS_H
#define UNITTEST_PARAMETERIZEDMACROS_H
#include "TestParameter.h"
#define UNITTEST_SET_TEST_PARAMETER_LISTENER(Type, Name, ListenerPtr, SetUpBody) \
class ParameterizedCreator##Name \
{ \
public: \
ParameterizedCreator##Name() : globalInstance(getGlobalInstance()) {} \
UnitTest::TestParameter<Type> & globalInstance; \
private: \
static UnitTest::TestParameter<Type> & getGlobalInstance() \
{ \
static UnitTest::TestParameter<Type> instance(#Name, create(), ListenerPtr); \
return instance; \
} \
static vector<Type> create() { vector<Type> values; SetUpBody return values; } \
} static parameterizedCreator##Name##Instance; \
\
static UnitTest::TestParameter<Type> & Name(parameterizedCreator##Name##Instance.globalInstance)
#define UNITTEST_SET_TEST_PARAMETER(Type, Name, SetUpBody) \
UNITTEST_SET_TEST_PARAMETER_LISTENER(Type, Name, nullptr, SetUpBody)
#ifndef UNITTEST_DISABLE_SHORT_MACROS
#ifdef SET_TEST_PARAMETER_LISTENER
#error SET_TEST_PARAMETER_LISTENER already defined, re-configure with UNITTEST_ENABLE_SHORT_MACROS set to 0 and use UNITTEST_SET_TEST_PARAMETER_LISTENER instead
#else
#define SET_TEST_PARAMETER_LISTENER UNITTEST_SET_TEST_PARAMETER_LISTENER
#endif
#ifdef SET_TEST_PARAMETER
#error SET_TEST_PARAMETER already defined, re-configure with UNITTEST_ENABLE_SHORT_MACROS set to 0 and use UNITTEST_SET_TEST_PARAMETER instead
#else
#define SET_TEST_PARAMETER UNITTEST_SET_TEST_PARAMETER
#endif
#endif
#endif

View file

@ -0,0 +1,371 @@
#include "ParameterizedManager.h"
#include "Test.h"
#include <sstream>
using namespace std;
using namespace UnitTest;
ParameterizedManager::ParameterizedManager()
: _currentTest(nullptr),
_nextTestBackup(nullptr),
_iterationDone(false),
_executing(false)
{
}
ParameterizedManager::~ParameterizedManager()
{
}
ParameterizedManager & ParameterizedManager::getInstance()
{
static ParameterizedManager stored;
return stored;
}
TestListNode* const ParameterizedManager::retrieveTest(TestDetails const * const details)
{
for (TestListNode* iNode = Test::GetTestList().GetHead(); iNode != nullptr; iNode = iNode->m_next)
{
// Warning: do not use TestDetails::sameTest here for optimisation reason
if (&iNode->m_test->m_details == details)
{
return iNode;
}
}
string errorMessage = "Impossible to retrieve test ";
errorMessage += details->testName;
throw runtime_error(errorMessage);
}
bool ParameterizedManager::isCurrentTest(TestDetails const * const details) const
{
if (_currentTest == nullptr)
{
return false;
}
return _currentTest->m_test->m_details.sameTest(*details);
}
bool ParameterizedManager::hasMoreIndexes(TestParameterAbstract* const parameterized)
{
if (!parameterized->hasMoreValues(1))
{
return false;
}
vector<IgnoredIndex> & ignoredIndexes = _ignoredIndexes[parameterized];
if (!ignoredIndexes.empty())
{
bool allRemainingsAreIgnored = true;
for (size_t iIndex = parameterized->_index + 1; iIndex < parameterized->valuesCount(); iIndex++)
{
if (findIgnored(ignoredIndexes, iIndex) == ignoredIndexes.end())
{
allRemainingsAreIgnored = false;
break;
}
}
if (allRemainingsAreIgnored)
{
return false;
}
}
return true;
}
void ParameterizedManager::beginExecute(TestDetails const * const details)
{
if (_currentTest != nullptr)
{
if (isCurrentTest(details))
{
_currentTest->m_next = _nextTestBackup;
_executing = true;
}
return;
}
_currentTest = retrieveTest(details);
_nextTestBackup = _currentTest->m_next;
_iterationDone = false;
_executing = true;
}
void ParameterizedManager::endExecute(TestDetails const * const details)
{
if (!isCurrentTest(details))
{
return;
}
while (!_stack.empty())
{
TestParameterAbstract* iParameterized = _stack.back();
if (hasMoreIndexes(iParameterized))
{
break;
}
else
{
_stack.pop_back();
}
}
if (_stack.empty())
{
_currentTest = nullptr;
_nextTestBackup = nullptr;
clearNonGlobalIgnoredIndexes();
}
else
{
_currentTest->m_next = _currentTest; // Loop itself
}
_iterationDone = false;
_executing = false;
}
void ParameterizedManager::clearNonGlobalIgnoredIndexes()
{
for (IgnoredIndexesMap::iterator it1 = _ignoredIndexes.begin(); it1 != _ignoredIndexes.end();)
{
vector<IgnoredIndex> & ignoredIndexes = it1->second;
for (vector<IgnoredIndex>::iterator it2 = ignoredIndexes.begin(); it2 != ignoredIndexes.end();)
{
if (!it2->global)
{
it2 = ignoredIndexes.erase(it2);
}
else
{
it2++;
}
}
if (ignoredIndexes.empty())
{
_ignoredIndexes.erase(it1++); // Note: map erase is a bit special
}
else
{
it1++;
}
}
}
void ParameterizedManager::updateParameter(TestParameterAbstract* const parameterized)
{
importIgnoredParameter(parameterized);
vector<IgnoredIndex> & ignoredIndexes = _ignoredIndexes[parameterized];
bool repeat = true;
while (repeat)
{
iterate(parameterized);
vector<IgnoredIndex>::iterator ignoredIt = findIgnored(ignoredIndexes, parameterized->_index);
repeat = (ignoredIt != ignoredIndexes.end());
if (repeat)
{
_iterationDone = false;
}
}
}
void ParameterizedManager::iterate(TestParameterAbstract* const parameterized)
{
bool firstIndex = false;
if (stackParameter(parameterized, firstIndex))
{
parameterized->nextIndex(firstIndex);
}
}
bool ParameterizedManager::stackParameter(TestParameterAbstract* const parameterized, bool & outFirstIndex)
{
if (find(_stack.begin(), _stack.end(), parameterized) == _stack.end())
{
_iterationDone = true;
outFirstIndex = true;
_stack.push_back(parameterized);
return true;
}
if (!_iterationDone)
{
if (!_stack.empty() && _stack.back() == parameterized)
{
_iterationDone = true;
outFirstIndex = false;
return true;
}
}
outFirstIndex = false;
return false;
}
vector<ParameterizedManager::IgnoredIndex>::iterator ParameterizedManager::findIgnored(vector<IgnoredIndex> & ignoredIndexes, size_t index)
{
for (vector<IgnoredIndex>::iterator it = ignoredIndexes.begin(); it != ignoredIndexes.end(); it++)
{
if (index == it->index)
{
return it;
}
}
return ignoredIndexes.end();
}
void ParameterizedManager::importIgnoredParameter(TestParameterAbstract* const parameterized)
{
IgnoredIndexesByNameMap::iterator it = _pendingIgnoredIndexes.find(parameterized->getName());
if (it == _pendingIgnoredIndexes.end())
{
return;
}
vector<IgnoredIndex> & pendingIgnoredIndexes = it->second;
while (!pendingIgnoredIndexes.empty())
{
const IgnoredIndex & iIgnored = pendingIgnoredIndexes.back();
ignoreIndex(parameterized, iIgnored.index, iIgnored.global ? GLOBAL : LOCAL);
pendingIgnoredIndexes.pop_back();
}
}
bool ParameterizedManager::isGlobal(IgnoreScope scope)
{
switch (scope)
{
case AUTO: return !_executing;
case LOCAL: return false;
case GLOBAL:return true;
}
throw runtime_error("Invalid enum value for ignore scope");
}
ParameterizedManager & ParameterizedManager::ignoreIndex(TestParameterAbstract* const parameterized, size_t index, IgnoreScope scope)
{
bool global = isGlobal(scope);
vector<IgnoredIndex> & ignoredIndexes = _ignoredIndexes[parameterized];
if (index >= parameterized->valuesCount())
{
return *this;
}
vector<IgnoredIndex>::iterator ignoredIt = findIgnored(ignoredIndexes, index);
if (ignoredIt != ignoredIndexes.end())
{
// Upgrade to global if required
if (global && !ignoredIt->global)
{
ignoredIt->global = true;
}
}
else
{
if (ignoredIndexes.size() + 1 == parameterized->valuesCount())
{
throw runtime_error("all parameters have been ignored, can not proceed");
}
ignoredIndexes.push_back(IgnoredIndex(index, global));
}
return *this;
}
ParameterizedManager & ParameterizedManager::ignoreIndex(const string & parameterName, size_t index, IgnoreScope scope)
{
if (parameterName.empty())
{
throw invalid_argument("Empty parameter name given in ignore index");
}
// Note: do not care about duplicates indexes for now
bool global = isGlobal(scope);
_pendingIgnoredIndexes[parameterName].push_back(IgnoredIndex(index, global));
return *this;
}
ParameterizedManager & ParameterizedManager::ignoreIndexes(const string & parametersArrayRange, IgnoreScope scope)
{
string parameterName;
vector<int> indexes = parseParameter(parametersArrayRange, parameterName);
for (size_t i = 0; i < indexes.size(); i++)
{
ignoreIndex(parameterName, indexes[i], scope);
}
return *this;
}
vector<int> ParameterizedManager::parseParameter(const string & parametersArrayRange, string & outParameterName)
{
if (parametersArrayRange.empty())
{
throw invalid_argument("Empty parameter array range given");
}
size_t openPos = parametersArrayRange.find('[');
size_t closePos = parametersArrayRange.find(']');
if (openPos == 0 || openPos == string::npos || closePos == string::npos || closePos < openPos)
{
throw invalid_argument("Misformatted parameter array range index, should be pzMyParam[0,1,2]");
}
outParameterName = parametersArrayRange.substr(0, openPos);
string indexes = parametersArrayRange.substr(openPos + 1, closePos - openPos - 1);
stringstream stream(indexes);
string item;
vector<int> indexesList;
while (getline(stream, item, ','))
{
int index = stoi(item);
indexesList.push_back(index);
}
return indexesList;
}
bool ParameterizedManager::isIndexIgnored(TestParameterAbstract* const parameterized, size_t index)
{
vector<IgnoredIndex> & ignoredIndexes = _ignoredIndexes[parameterized];
vector<IgnoredIndex>::iterator ignoredIt = findIgnored(ignoredIndexes, index);
return (ignoredIt != ignoredIndexes.end());
}
const vector<TestParameterAbstract*> & ParameterizedManager::getStack(TestDetails const * const details) const
{
if (!isCurrentTest(details))
{
static vector<TestParameterAbstract*> emptyStored;
return emptyStored;
}
return _stack;
}

View file

@ -0,0 +1,63 @@
#ifndef UNITTEST_PARAMETERIZEDMANAGER_H
#define UNITTEST_PARAMETERIZEDMANAGER_H
#include <vector>
#include <unordered_map>
#include "TestDetails.h"
#include "TestList.h"
#include "TestParameter.h"
namespace UnitTest
{
using namespace std;
class ParameterizedManager
{
public:
static ParameterizedManager & getInstance();
TestListNode* const getCurrentTest() const { return _currentTest; }
bool isCurrentTest(TestDetails const * const details) const;
void beginExecute(TestDetails const * const details);
void endExecute(TestDetails const * const details);
void updateParameter(TestParameterAbstract* const parameterized);
ParameterizedManager & ignoreIndex(TestParameterAbstract* const parameterized, size_t index, IgnoreScope scope = AUTO);
ParameterizedManager & ignoreIndex(const string & parameterName, size_t index, IgnoreScope scope = AUTO);
ParameterizedManager & ignoreIndexes(const string & parametersArrayRange, IgnoreScope scope = AUTO);
bool isIndexIgnored(TestParameterAbstract* const parameterized, size_t index);
const vector<TestParameterAbstract*> & getStack(TestDetails const * const details) const;
private:
struct IgnoredIndex
{
IgnoredIndex(size_t index, bool global) : index(index), global(global) {}
size_t index;
bool global;
};
typedef unordered_map<TestParameterAbstract*, vector<IgnoredIndex>> IgnoredIndexesMap;
typedef unordered_map<string, vector<IgnoredIndex>> IgnoredIndexesByNameMap;
ParameterizedManager();
virtual ~ParameterizedManager();
TestListNode* const retrieveTest(TestDetails const * const details);
void iterate(TestParameterAbstract* const parameterized);
bool stackParameter(TestParameterAbstract* const parameterized, bool & outFirstIndex);
bool hasMoreIndexes(TestParameterAbstract* const parameterized);
bool isGlobal(IgnoreScope scope);
vector<IgnoredIndex>::iterator findIgnored(vector<IgnoredIndex> & ignoredIndexes, size_t index);
void importIgnoredParameter(TestParameterAbstract* const parameterized);
void clearNonGlobalIgnoredIndexes();
static vector<int> parseParameter(const string & parametersArrayRange, string & outParameterName);
TestListNode* _currentTest;
TestListNode* _nextTestBackup;
vector<TestParameterAbstract*> _stack;
IgnoredIndexesMap _ignoredIndexes;
IgnoredIndexesByNameMap _pendingIgnoredIndexes;
volatile bool _iterationDone;
volatile bool _executing;
};
}
#endif

View file

@ -0,0 +1,69 @@
#include "SuitePredicate.h"
using namespace std;
using namespace UnitTest;
SuitePredicate::SuitePredicate()
: _all(false)
{
}
SuitePredicate::~SuitePredicate()
{
}
void SuitePredicate::addSuite(char const* suiteName)
{
_suiteNames.push_back(suiteName);
}
void SuitePredicate::addTest(char const* testName)
{
_testNames.push_back(testName);
}
void SuitePredicate::addAll()
{
_all = true;
}
bool SuitePredicate::operator()(Test const * const test) const
{
if (_all)
{
return true;
}
return (mustExecuteSuite(test) || mustExecuteTest(test));
}
bool SuitePredicate::mustExecuteSuite(Test const * const test) const
{
for (size_t i = 0; i < _suiteNames.size(); i++)
{
if (_suiteNames[i] == test->m_details.suiteName)
{
return true;
}
}
return false;
}
bool SuitePredicate::mustExecuteTest(Test const * const test) const
{
for (size_t i = 0; i < _testNames.size(); i++)
{
if (_testNames[i] == test->m_details.testName)
{
return true;
}
}
return false;
}

View file

@ -0,0 +1,34 @@
#ifndef UNITTEST_SUITEPREDICATE_H
#define UNITTEST_SUITEPREDICATE_H
#include <string>
#include <vector>
#include "Test.h"
namespace UnitTest
{
using namespace std;
class UNITTEST_LINKAGE SuitePredicate
{
public:
SuitePredicate();
virtual ~SuitePredicate();
void addSuite(char const* suiteName);
void addTest(char const* testName);
void addAll();
bool operator()(Test const * const test) const;
private:
bool mustExecuteSuite(Test const * const test) const;
bool mustExecuteTest(Test const * const test) const;
vector<string> _suiteNames;
vector<string> _testNames;
bool _all;
};
}
#endif

View file

@ -20,7 +20,6 @@ namespace UnitTest {
Test::Test(char const* testName, char const* suiteName, char const* filename, int lineNumber)
: m_details(testName, suiteName, filename, lineNumber)
, m_nextTest(0)
, m_isMockTest(false)
{}
@ -29,7 +28,7 @@ namespace UnitTest {
void Test::Run()
{
ExecuteTest(*this, m_details, m_isMockTest);
ExecuteTest(*this, m_details, m_isMockTest, true);
}
void Test::RunImpl() const

View file

@ -16,7 +16,6 @@ namespace UnitTest {
void Run();
TestDetails const m_details;
Test* m_nextTest;
mutable bool m_isMockTest;

View file

@ -1,4 +1,5 @@
#include "TestDetails.h"
#include <cstring>
namespace UnitTest {
@ -18,5 +19,29 @@ namespace UnitTest {
, timeConstraintExempt(details.timeConstraintExempt)
{}
bool TestDetails::sameTest(const TestDetails & details) const
{
if (&details == this)
{
return true;
}
// Fast pointer comparison
if (details.suiteName == suiteName &&
details.testName == testName &&
details.filename == filename)
{
return true;
}
// Long string comparison
if (!strcmp(details.suiteName, suiteName) &&
!strcmp(details.testName, testName) &&
!strcmp(details.filename, filename))
{
return true;
}
return false;
}
}

View file

@ -17,6 +17,8 @@ namespace UnitTest {
int const lineNumber;
mutable bool timeConstraintExempt;
bool sameTest(const TestDetails & details) const;
TestDetails(TestDetails const&); // Why is it public? --> http://gcc.gnu.org/bugs.html#cxx_rvalbind
private:
TestDetails& operator=(TestDetails const&);

View file

@ -5,27 +5,37 @@
namespace UnitTest {
TestListNode::TestListNode(Test* test)
: m_test(test),
m_next(0)
{
}
TestList::TestList()
: m_head(0)
, m_tail(0)
{}
void TestList::Add(Test* test)
TestListNode* TestList::Add(Test* test)
{
TestListNode* node = 0;
if (m_tail == 0)
{
assert(m_head == 0);
m_head = test;
m_tail = test;
node = new TestListNode(test);
m_head = node;
m_tail = node;
}
else
{
m_tail->m_nextTest = test;
m_tail = test;
node = new TestListNode(test);
m_tail->m_next = node;
m_tail = node;
}
return node;
}
Test* TestList::GetHead() const
TestListNode* TestList::GetHead() const
{
return m_head;
}

View file

@ -7,17 +7,25 @@ namespace UnitTest {
class Test;
class UNITTEST_LINKAGE TestListNode
{
public:
TestListNode(Test* test);
Test* m_test;
TestListNode* m_next;
};
class UNITTEST_LINKAGE TestList
{
public:
TestList();
void Add (Test* test);
TestListNode* Add(Test* test);
Test* GetHead() const;
TestListNode* GetHead() const;
private:
Test* m_head;
Test* m_tail;
TestListNode* m_head;
TestListNode* m_tail;
};

View file

@ -71,7 +71,7 @@
({ \
Fixture ## Name ## Helper fixtureHelper(m_details); \
ctorOk = true; \
UnitTest::ExecuteTest(fixtureHelper, m_details, false); \
UnitTest::ExecuteTest(fixtureHelper, m_details, false, false); \
}) \
UNITTEST_IMPL_CATCH (UnitTest::AssertException, e, \
{ \

View file

@ -0,0 +1,78 @@
#include "TestParameter.h"
#include <sstream>
#include "ParameterizedManager.h"
using namespace std;
using namespace UnitTest;
TestParameterAbstract::TestParameterAbstract(const string & name)
: _name(name),
_index(0)
{
}
TestParameterAbstract::~TestParameterAbstract()
{
}
size_t TestParameterAbstract::getCurrentIndex()
{
updateCurrentIndex();
return _index;
}
string TestParameterAbstract::getNameCurrent() const
{
stringstream output;
output << (_name.empty() ? "unknown" : _name) << "[" << _index << "]";
return output.str();
}
void TestParameterAbstract::updateCurrentIndex()
{
ParameterizedManager::getInstance().updateParameter(this);
}
bool TestParameterAbstract::hasMoreValues(int advance) const
{
return (_index + advance < (int)valuesCount());
}
void TestParameterAbstract::nextIndex(bool first)
{
if (first)
{
if (valuesCount() == 0)
{
throw runtime_error("No values for parameterized test");
}
_index = 0;
}
else
{
_index++;
}
peekCurrent(&ParameterizedManager::getInstance().getCurrentTest()->m_test->m_details, _index);
}
TestParameterAbstract & TestParameterAbstract::ignoreIndex(size_t index, IgnoreScope scope)
{
ParameterizedManager::getInstance().ignoreIndex(this, index, scope);
return *this;
}
bool TestParameterAbstract::isIndexIgnored(size_t index)
{
return ParameterizedManager::getInstance().isIndexIgnored(this, index);
}

121
UnitTest++/TestParameter.h Normal file
View file

@ -0,0 +1,121 @@
#ifndef UNITTEST_TESTPARAMETER_H
#define UNITTEST_TESTPARAMETER_H
#include <string>
#include <vector>
#include <algorithm>
#include "Test.h"
#include "TestList.h"
namespace UnitTest
{
using namespace std;
enum IgnoreScope
{
AUTO,
LOCAL,
GLOBAL
};
class TestParameterAbstract
{
friend class ParameterizedManager;
public:
TestParameterAbstract(const string & name);
virtual ~TestParameterAbstract();
size_t getCurrentIndex();
const string & getName() const { return _name; }
string getNameCurrent() const;
TestParameterAbstract & ignoreIndex(size_t index, IgnoreScope scope = AUTO);
bool isIndexIgnored(size_t index);
protected:
void updateCurrentIndex();
virtual void peekCurrent(TestDetails const * const details, size_t index) = 0;
virtual size_t valuesCount() const = 0;
private:
bool hasMoreValues(int advance = 0) const;
void nextIndex(bool first);
string _name;
size_t _index;
};
template<class T>
class TestParameter : public TestParameterAbstract
{
public:
struct IParameterListener
{
virtual void onNext(TestDetails const * const details, T currentValue, size_t currentIndex) = 0;
};
TestParameter(const string & name, vector<T> values, IParameterListener* const listener = nullptr)
: TestParameterAbstract(name),
_values(values),
_listener(listener)
{
}
T operator()()
{
size_t index = getCurrentIndex();
return _values[index];
}
const vector<T> & values() const
{
return _values;
}
TestParameter<T> & ignoreIndex(size_t index)
{
TestParameterAbstract::ignoreIndex(index);
return *this;
}
TestParameter<T> & ignore(T p)
{
vector<T>::iterator it = find(_values.begin(), _values.end(), p);
if (it == _values.end())
{
return *this;
}
return ignoreIndex(it - _values.begin());
}
bool isIgnored(T p)
{
vector<T>::iterator it = find(_values.begin(), _values.end(), p);
if (it == _values.end())
{
return true;
}
return isIndexIgnored(it - _values.begin());
}
protected:
virtual void peekCurrent(TestDetails const * const details, size_t index) override
{
if (_listener != nullptr)
{
_listener->onNext(details, _values[index], index);
}
}
virtual size_t valuesCount() const override
{
return _values.size();
}
private:
vector<T> _values;
IParameterListener* const _listener;
};
}
#endif

View file

@ -2,6 +2,8 @@
#include "TestReporter.h"
#include "TestDetails.h"
#include "MemoryOutStream.h"
#include "ParameterizedManager.h"
namespace UnitTest {
@ -31,7 +33,24 @@ namespace UnitTest {
}
if (m_testReporter)
m_testReporter->ReportFailure(test, failure);
{
MemoryOutStream stream;
stream << failure;
const vector<TestParameterAbstract*> & parameters = ParameterizedManager::getInstance().getStack(&test);
if (!parameters.empty())
{
stream << " Parameters: ";
for (size_t i = 0; i < parameters.size(); i++)
{
if (i != 0)
{
stream << ", ";
}
stream << parameters[i]->getNameCurrent();
}
}
m_testReporter->ReportFailure(test, stream.GetText());
}
}
void TestResults::OnTestFinish(TestDetails const& test, float secondsElapsed)

View file

@ -4,6 +4,8 @@
#include "TestReporterStdout.h"
#include "TimeHelpers.h"
#include "MemoryOutStream.h"
#include "SuitePredicate.h"
#include "ParameterizedManager.h"
#include <cstring>
@ -17,6 +19,112 @@ namespace UnitTest {
return runner.RunTestsIf(Test::GetTestList(), NULL, True(), 0);
}
bool findArgumentListIndex(int argc, char**argv, char const* argument, int & outFrom, int & outCount)
{
if (argc <= 1)
{
return false;
}
outCount = 0;
if (strlen(argument) > 0)
{
outFrom = 0;
for (int i = 1; i < argc; i++)
{
if (strcmp(argument, argv[i]) == 0)
{
outFrom = i + 1;
break;
}
}
if (outFrom == 0)
{
return false;
}
}
else
{
outFrom = 1;
}
for (int i = outFrom; i < argc; i++)
{
char* value = argv[i];
if (strlen(value) >= 2 && value[0] == '-'&& value[1] == '-')
{
break;
}
outCount++;
}
return true;
}
bool readSuiteArgument(SuitePredicate & predicate, int argc, char**argv, char const* argument)
{
int from, count;
if (!findArgumentListIndex(argc, argv, argument, from, count))
{
return false;
}
for (int i = from; i < from + count; i++)
{
predicate.addSuite(argv[i]);
}
return true;
}
bool readTestArgument(SuitePredicate & predicate, int argc, char**argv, char const* argument)
{
int from, count;
if (!findArgumentListIndex(argc, argv, argument, from, count))
{
return false;
}
for (int i = from; i < from + count; i++)
{
predicate.addTest(argv[i]);
}
return true;
}
bool readIgnoreParamArgument(int argc, char**argv, char const* argument)
{
int from, count;
if (!findArgumentListIndex(argc, argv, argument, from, count))
{
return false;
}
for (int i = from; i < from + count; i++)
{
ParameterizedManager::getInstance().ignoreIndexes(argv[i]);
}
return true;
}
int RunTestsCmd(int argc, char**argv, char const* suiteArgument, char const* testArgument, char const* ignoreParamArgument)
{
SuitePredicate predicate;
bool specific = false;
specific |= readSuiteArgument(predicate, argc, argv, suiteArgument);
specific |= readTestArgument(predicate, argc, argv, testArgument);
specific |= readTestArgument(predicate, argc, argv, "");
readIgnoreParamArgument(argc, argv, ignoreParamArgument);
if (!specific)
{
predicate.addAll();
}
//run selected test(s) only
TestReporterStdout reporter;
TestRunner runner(reporter);
return runner.RunTestsIf(Test::GetTestList(), 0, predicate, 0);
}
TestRunner::TestRunner(TestReporter& reporter)
: m_reporter(&reporter)

View file

@ -13,6 +13,18 @@ namespace UnitTest {
UNITTEST_LINKAGE int RunAllTests();
/**
* Commands:
* --test One or multiple test names to execute (specify "--test" is optional if it is the first argument), can be combined with --suite
* --suite One or multiple suite names to execute, can be combined with --test
* --ignoreparam One or multiple test parameter name, with index(es) specified, using this syntax: pzMyParam[4,8,12,7]
*
* Usage examples:
* myTests.exe --suite MySuite1 MyOtherSuite --test MySpecialTest MyOtherTest --ignoreparam pzMyPlatforms[0,2,3] pzMyParam[4,8]
* myTests.exe MySpecialTest MyOtherTest --suite MySuite1
*/
UNITTEST_LINKAGE int RunTestsCmd(int argc, char**argv, char const* suiteArgument = "--suite", char const* testArgument = "--test", char const* ignoreParamArgument = "--ignoreparam");
struct True
{
bool operator()(const Test* const) const
@ -31,14 +43,14 @@ namespace UnitTest {
int RunTestsIf(TestList const& list, char const* suiteName,
const Predicate& predicate, int maxTestTimeInMs) const
{
Test* curTest = list.GetHead();
TestListNode* curTest = list.GetHead();
while (curTest != 0)
{
if (IsTestInSuite(curTest, suiteName) && predicate(curTest))
RunTest(m_result, curTest, maxTestTimeInMs);
if (IsTestInSuite(curTest->m_test, suiteName) && predicate(curTest->m_test))
RunTest(m_result, curTest->m_test, maxTestTimeInMs);
curTest = curTest->m_nextTest;
curTest = curTest->m_next;
}
return Finish();

View file

@ -8,5 +8,6 @@
#include "TestRunner.h"
#include "TimeConstraint.h"
#include "ReportAssert.h"
#include "ParameterizedMacros.h"
#endif

View file

@ -1,6 +1,6 @@
#include "UnitTest++/UnitTestPP.h"
int main(int, char const *[])
int main(int argc, char**argv)
{
return UnitTest::RunAllTests();
return UnitTest::RunTestsCmd(argc, argv);
}

View file

@ -37,7 +37,9 @@ UNITTEST_SUITE(LongMacros)
defined(SUITE) || \
defined(TEST) || \
defined(TEST_FIXTURE) || \
defined(REQUIRE)
defined(REQUIRE) || \
defined(SET_TEST_PARAMETER_LISTENER) || \
defined(SET_TEST_PARAMETER)
UNITTEST_CHECK(false);
#endif

View file

@ -0,0 +1,451 @@
#include "UnitTest++/UnitTest++.h"
#include <string>
#include "UnitTest++/TestParameter.h"
#include "UnitTest++/ParameterizedManager.h"
#include "ScopedCurrentTest.h"
#include "RecordingReporter.h"
using namespace std;
using namespace UnitTest;
SUITE(ParameterizedTest)
{
string simpleVowels;
int simpleVowelsHitCount = 0;
SET_TEST_PARAMETER(string, pzVowel, {
values.push_back("A");
values.push_back("E");
values.push_back("I");
values.push_back("O");
values.push_back("U");
values.push_back("Y");
});
TEST(SimpleVowelConcat)
{
simpleVowels += pzVowel();
simpleVowelsHitCount++;
}
TEST(SimpleVowelConcat_Verify)
{
CHECK_EQUAL(6, simpleVowelsHitCount);
CHECK_EQUAL("AEIOUY", simpleVowels);
}
//////////
int hitCountNonParameterized = 0;
TEST(NonParameterizedExecutedOnce)
{
hitCountNonParameterized++;
CHECK_EQUAL(1, hitCountNonParameterized);
}
//////////
bool enteredEmpty = false;
int hitCountEmpty = 0;
SET_TEST_PARAMETER(int, pzEmpty, {});
TEST(WhenNoParameters_throwsException)
{
enteredEmpty = true;
try
{
pzEmpty();
}
catch (runtime_error e) // Expected case
{
CHECK_EQUAL("No values for parameterized test", e.what());
return;
}
CHECK(false);
hitCountEmpty++;
}
TEST(WhenNoParameters_throwsException_Verify)
{
CHECK_EQUAL(0, hitCountEmpty);
CHECK(enteredEmpty);
}
//////////
int hitCountSingle = 0;
static int singleValueSuiteSum = 0;
SET_TEST_PARAMETER(int, pzSingle, {
values.push_back(2);
});
TEST(WhenSingleValue_singleExecution)
{
singleValueSuiteSum += pzSingle();
CHECK_EQUAL(2, singleValueSuiteSum);
hitCountSingle++;
}
TEST(WhenSingleValue_singleExecution_Verification)
{
CHECK_EQUAL(2, singleValueSuiteSum);
CHECK_EQUAL(1, hitCountSingle);
}
//////////
string voyelReuse;
TEST(ReusePreviousParameterized)
{
voyelReuse += pzVowel() + "-"; // Add a separator for
}
TEST(ReusePreviousParameterized_Verify)
{
CHECK_EQUAL("A-E-I-O-U-Y-", voyelReuse);
}
//////////
SET_TEST_PARAMETER(string, pzOneTwo, {
values.push_back("1");
values.push_back("2");
});
string nestedParameters;
TEST(NestedParameters)
{
nestedParameters += pzVowel();
nestedParameters += pzOneTwo();
}
TEST(NestedParameters_Verify)
{
CHECK_EQUAL("A1A2E1E2I1I2O1O2U1U2Y1Y2", nestedParameters);
}
//////////
string useSeveralTimes;
TEST(UseSeveralTimes_DoesNotIncrement)
{
useSeveralTimes += pzOneTwo();
useSeveralTimes += pzOneTwo();
}
TEST(UseSeveralTimes_DoesNotIncrement_Verify)
{
CHECK_EQUAL("1122", useSeveralTimes);
}
//////////
struct Fixture {};
string withFixture;
TEST_FIXTURE(Fixture, WorksWithFixture)
{
withFixture += pzOneTwo();
}
TEST(WorksWithFixture_Verify)
{
CHECK_EQUAL("12", withFixture);
}
//////////
SET_TEST_PARAMETER(int, pzSingleBis, {
values.push_back(3);
});
TEST(FailedMessage_ContainsIndexes)
{
class FailingParameterizedTest : public Test
{
public:
FailingParameterizedTest()
: Test(CurrentTest::Details()->testName, CurrentTest::Details()->suiteName, CurrentTest::Details()->filename) {}
virtual void RunImpl() const
{
pzSingle();
pzSingleBis();
REQUIRE CHECK(false);
}
};
RecordingReporter reporter;
TestResults results(&reporter);
{
ScopedCurrentTest scopedResults(results);
FailingParameterizedTest().Run();
}
string expectedFailMessage = "false Parameters: "
+ pzSingle.getName() + "[0], "
+ pzSingleBis.getName() + "[0]";
CHECK_EQUAL(expectedFailMessage, string(reporter.lastFailedMessage));
}
//////////
TEST(IgnoreIndex_OutOfRange_DoesNotThrowException)
{
size_t out = pzVowel.values().size();
pzVowel.ignoreIndex(out);
}
TEST(IgnoreIndex_IgnoreAll_ThrowsException)
{
size_t last = pzVowel.values().size() - 1;
for (size_t i = 0; i < last; i++)
{
pzVowel.ignoreIndex(i);
}
CHECK_THROW(pzVowel.ignoreIndex(last), runtime_error);
}
//////////
size_t ignoreIndexLast_count = 0;
string ignoreIndexLast;
TEST(IgnoreIndex_IgnoreLast)
{
size_t lastIndex = pzOneTwo.values().size() - 1;
pzOneTwo.ignoreIndex(lastIndex);
ignoreIndexLast += pzOneTwo();
// WARNING: this is not the test itself, it is only for reveal a "dead loop"
REQUIRE CHECK(ignoreIndexLast_count < lastIndex);
ignoreIndexLast_count++;
}
TEST(IgnoreIndex_IgnoreLast_Verify)
{
CHECK_EQUAL("1", ignoreIndexLast);
}
//////////
string ignoreSomeVowels;
TEST(IgnoreIndex_IgnoreSome)
{
pzVowel
.ignoreIndex(1) // "E"
.ignoreIndex(4); // "U"
ignoreSomeVowels += pzVowel();
}
TEST(IgnoreIndex_IgnoreSome_Verify)
{
CHECK_EQUAL("AIOY", ignoreSomeVowels);
}
//////////
string ignoreWithNested;
TEST(IgnoreIndex_IgnoreWithNested)
{
pzVowel
.ignoreIndex(0) // "A"
.ignoreIndex(2) // "I"
.ignoreIndex(3) // "O"
.ignoreIndex(4); // "U"
ignoreWithNested += pzVowel();
ignoreWithNested += pzOneTwo();
}
TEST(IgnoreIndex_IgnoreWithNested_Verify)
{
CHECK_EQUAL("E1E2Y1Y2", ignoreWithNested);
}
//////////
string ignoreSomeVowelsByValue;
TEST(Ignore_IgnoreSome)
{
pzVowel
.ignore("E")
.ignore("U");
ignoreSomeVowelsByValue += pzVowel();
}
TEST(Ignore_IgnoreSome_Verify)
{
CHECK_EQUAL("AIOY", ignoreSomeVowelsByValue);
}
//////////
SET_TEST_PARAMETER(string, pzVowelPartial, {
values = pzVowel.values();
});
struct Initializer
{
Initializer()
{
ParameterizedManager::getInstance()
.ignoreIndex(&pzVowelPartial, 1) // "E"
.ignoreIndex(&pzVowelPartial, 4); // "U"
}
} initializerInstance;
string globalIgnoreSomeVowels;
// Note: the test is executed twice (A/B)
TEST(GlobalIgnore_IgnoreSome_A)
{
globalIgnoreSomeVowels += pzVowelPartial();
}
TEST(GlobalIgnore_IgnoreSome_B)
{
globalIgnoreSomeVowels += pzVowelPartial();
}
TEST(GlobalIgnore_IgnoreSome_Verify)
{
CHECK_EQUAL("AIOYAIOY", globalIgnoreSomeVowels);
}
//////////
TEST(Ignore_Getter)
{
pzVowel.ignore("E");
CHECK(pzVowel.isIgnored("E"));
CHECK(!pzVowel.isIgnored("Y"));
}
TEST(IgnoreGlobal_Getter)
{
CHECK(pzVowelPartial.isIgnored("E"));
CHECK(!pzVowelPartial.isIgnored("Y"));
}
//////////
vector<TestListNode*> currentTestsNodes;
TEST(NoDeadLoopInTestList)
{
pzOneTwo(); // Important for perform looping
for (TestListNode* iNode = Test::GetTestList().GetHead(); iNode != nullptr; iNode = iNode->m_next)
{
REQUIRE CHECK(iNode != iNode->m_next);
currentTestsNodes.push_back(iNode);
}
}
TEST(NoDeadLoopInTestList_Verify)
{
vector<TestListNode*> expectedNodes;
for (size_t i = 0; i < pzOneTwo.values().size(); i++) // tested vector contains repeated values because it is filled by parameterized test
{
for (TestListNode* iNode = Test::GetTestList().GetHead(); iNode != nullptr; iNode = iNode->m_next)
{
expectedNodes.push_back(iNode);
}
}
REQUIRE CHECK_EQUAL(expectedNodes.size(), currentTestsNodes.size());
CHECK_ARRAY_EQUAL(expectedNodes, currentTestsNodes, expectedNodes.size());
}
//////////
struct Initializer4pzIgnoredByNameVowel
{
Initializer4pzIgnoredByNameVowel()
{
ParameterizedManager::getInstance()
.ignoreIndex("pzIgnoredByNameVowel", 2) // "I"
.ignoreIndex("pzIgnoredByNameVowel", 5); // "Y"
}
} initializer4pzIgnoredByNameVowelInstance;
SET_TEST_PARAMETER(string, pzIgnoredByNameVowel, {
values = pzVowel.values();
});
string ignoreSomeVowelsByName_Global;
TEST(Ignore_IgnoreByName_Global)
{
ignoreSomeVowelsByName_Global += pzIgnoredByNameVowel();
}
TEST(Ignore_IgnoreByName_Global_Verify)
{
CHECK_EQUAL("AEOU", ignoreSomeVowelsByName_Global);
}
string ignoreSomeVowelsByName_Local;
TEST(Ignore_IgnoreByName_Local)
{
ParameterizedManager::getInstance()
.ignoreIndex("pzIgnoredByNameVowel", 3); // "O"
ignoreSomeVowelsByName_Local += pzIgnoredByNameVowel();
}
TEST(Ignore_IgnoreByName_Local_Verify)
{
CHECK_EQUAL("AEU", ignoreSomeVowelsByName_Local);
}
//////////
SET_TEST_PARAMETER(string, pzIgnoredByNameVowelSome, {
values = pzVowel.values();
});
string ignoreByNameRangeEmptyIsAllowed;
TEST(Ignore_IgnoreByNameRange_EmptyIsAllowed)
{
ParameterizedManager::getInstance().ignoreIndexes("pzIgnoredByNameVowelSome[]");
ignoreByNameRangeEmptyIsAllowed += pzIgnoredByNameVowelSome();
}
TEST(Ignore_IgnoreByNameRange_EmptyIsAllowed_Verify)
{
CHECK_EQUAL("AEIOUY", ignoreByNameRangeEmptyIsAllowed);
}
string ignoreByNameRangeSingleValue;
TEST(Ignore_IgnoreByNameRange_SingleValue)
{
ParameterizedManager::getInstance().ignoreIndexes("pzIgnoredByNameVowelSome[2]");
ignoreByNameRangeSingleValue += pzIgnoredByNameVowelSome();
}
TEST(Ignore_IgnoreByNameRange_SingleValue_Verify)
{
CHECK_EQUAL("AEOUY", ignoreByNameRangeSingleValue);
}
string ignoreByNameRangeSeveralUnorderedValues;
TEST(Ignore_IgnoreByNameRange_SeveralUnorderedValues)
{
ParameterizedManager::getInstance().ignoreIndexes("pzIgnoredByNameVowelSome[3,4,0]");
ignoreByNameRangeSeveralUnorderedValues += pzIgnoredByNameVowelSome();
}
TEST(Ignore_IgnoreByNameRange_SeveralUnorderedValues_Verify)
{
CHECK_EQUAL("EIY", ignoreByNameRangeSeveralUnorderedValues);
}
}

35
tests/TestTestDetails.cpp Normal file
View file

@ -0,0 +1,35 @@
#include "UnitTest++/UnitTestPP.h"
#include "UnitTest++/TestDetails.h"
using namespace UnitTest;
namespace {
TEST(SameTest_SameValues_AreEqual)
{
TestDetails details1("test1", "suite1", "file1", 1234);
TestDetails details2("test1", "suite1", "file1", 1234);
CHECK(details1.sameTest(details2));
}
TEST(SameTest_DifferentValues_AreNotEqual)
{
TestDetails details1("test1", "suite1", "file1", 1234);
TestDetails details2("test2", "suite2", "file2", 4321);
CHECK(!details1.sameTest(details2));
}
TEST(SameTest_LineNumber_AreIgnored)
{
TestDetails details1("test1", "suite1", "file1", 1234);
TestDetails details2("test1", "suite1", "file1", 0);
CHECK(details1.sameTest(details2));
}
TEST(SameTest_CopyConstructor_AreEqual)
{
TestDetails details1("test1", "suite1", "file1", 1234);
TestDetails details2(details1, 4321);
CHECK(details1.sameTest(details2));
}
}

View file

@ -18,8 +18,8 @@ namespace {
TestList list;
list.Add(&test);
CHECK(list.GetHead() == &test);
CHECK(test.m_nextTest == 0);
CHECK(list.GetHead()->m_test == &test);
CHECK(list.GetHead()->m_next == 0);
}
TEST(AddingSecondTestAddsItToEndOfList)
@ -28,12 +28,12 @@ namespace {
Test test2("test2");
TestList list;
list.Add(&test1);
list.Add(&test2);
TestListNode* test1Node = list.Add(&test1);
TestListNode* test2Node = list.Add(&test2);
CHECK(list.GetHead() == &test1);
CHECK(test1.m_nextTest == &test2);
CHECK(test2.m_nextTest == 0);
CHECK(list.GetHead()->m_test == &test1);
CHECK(test1Node->m_next->m_test == &test2);
CHECK(test2Node->m_next == 0);
}
TEST(ListAdderAddsTestToList)
@ -43,8 +43,8 @@ namespace {
Test test("");
ListAdder adder(list, &test);
CHECK(list.GetHead() == &test);
CHECK(test.m_nextTest == 0);
CHECK(list.GetHead()->m_test == &test);
CHECK(list.GetHead()->m_next == 0);
}
}

View file

@ -52,7 +52,7 @@ namespace {
TEST (TestsAreAddedToTheListThroughMacro)
{
CHECK(list1.GetHead() != 0);
CHECK(list1.GetHead()->m_nextTest == 0);
CHECK(list1.GetHead()->m_next == 0);
}
#ifndef UNITTEST_NO_EXCEPTIONS
@ -78,7 +78,7 @@ namespace {
TestResults result(&reporter);
{
ScopedCurrentTest scopedResults(result);
list2.GetHead()->Run();
list2.GetHead()->m_test->Run();
}
CHECK(strstr(reporter.lastFailedMessage, "xception"));
@ -119,8 +119,8 @@ namespace {
TEST(TestAddedWithTEST_EXMacroGetsDefaultSuite)
{
CHECK(macroTestList1.GetHead() != NULL);
CHECK_EQUAL ("MacroTestHelper1", macroTestList1.GetHead()->m_details.testName);
CHECK_EQUAL ("DefaultSuite", macroTestList1.GetHead()->m_details.suiteName);
CHECK_EQUAL ("MacroTestHelper1", macroTestList1.GetHead()->m_test->m_details.testName);
CHECK_EQUAL ("DefaultSuite", macroTestList1.GetHead()->m_test->m_details.suiteName);
}
TestList macroTestList2;
@ -130,8 +130,8 @@ namespace {
TEST(TestAddedWithTEST_FIXTURE_EXMacroGetsDefaultSuite)
{
CHECK(macroTestList2.GetHead() != NULL);
CHECK_EQUAL ("MacroTestHelper2", macroTestList2.GetHead()->m_details.testName);
CHECK_EQUAL ("DefaultSuite", macroTestList2.GetHead()->m_details.suiteName);
CHECK_EQUAL ("MacroTestHelper2", macroTestList2.GetHead()->m_test->m_details.testName);
CHECK_EQUAL ("DefaultSuite", macroTestList2.GetHead()->m_test->m_details.suiteName);
}
#ifndef UNITTEST_NO_EXCEPTIONS
@ -154,7 +154,7 @@ namespace {
TestResults result(&reporter);
{
ScopedCurrentTest scopedResult(result);
throwingFixtureTestList1.GetHead()->Run();
throwingFixtureTestList1.GetHead()->m_test->Run();
}
int const failureCount = result.GetFailedTestCount();
@ -181,7 +181,7 @@ namespace {
TestResults result(&reporter);
{
ScopedCurrentTest scopedResult(result);
throwingFixtureTestList2.GetHead()->Run();
throwingFixtureTestList2.GetHead()->m_test->Run();
}
int const failureCount = result.GetFailedTestCount();
@ -209,7 +209,7 @@ namespace {
TestResults result(&reporter);
{
ScopedCurrentTest scopedResults(result);
ctorAssertFixtureTestList.GetHead()->Run();
ctorAssertFixtureTestList.GetHead()->m_test->Run();
}
const int failureCount = result.GetFailedTestCount();