diff --git a/UnitTest++/Checks.h b/UnitTest++/Checks.h index 70bd51b..7c37691 100644 --- a/UnitTest++/Checks.h +++ b/UnitTest++/Checks.h @@ -57,10 +57,10 @@ namespace UnitTest { template< typename Expected, typename Actual > void CheckArrayEqual(TestResults& results, Expected const& expected, Actual const& actual, - int const count, TestDetails const& details) + size_t const count, TestDetails const& details) { bool equal = true; - for (int i = 0; i < count; ++i) + for (size_t i = 0; i < count; ++i) equal &= (expected[i] == actual[i]); if (!equal) @@ -69,12 +69,12 @@ namespace UnitTest { stream << "Expected [ "; - for (int expectedIndex = 0; expectedIndex < count; ++expectedIndex) + for (size_t expectedIndex = 0; expectedIndex < count; ++expectedIndex) stream << expected[expectedIndex] << " "; stream << "] but was [ "; - for (int actualIndex = 0; actualIndex < count; ++actualIndex) + for (size_t actualIndex = 0; actualIndex < count; ++actualIndex) stream << actual[actualIndex] << " "; stream << "]"; @@ -84,17 +84,17 @@ namespace UnitTest { } template< typename Expected, typename Actual, typename Tolerance > - bool ArrayAreClose(Expected const& expected, Actual const& actual, int const count, Tolerance const& tolerance) + bool ArrayAreClose(Expected const& expected, Actual const& actual, size_t const count, Tolerance const& tolerance) { bool equal = true; - for (int i = 0; i < count; ++i) + for (size_t i = 0; i < count; ++i) equal &= AreClose(expected[i], actual[i], tolerance); return equal; } template< typename Expected, typename Actual, typename Tolerance > void CheckArrayClose(TestResults& results, Expected const& expected, Actual const& actual, - int const count, Tolerance const& tolerance, TestDetails const& details) + size_t const count, Tolerance const& tolerance, TestDetails const& details) { bool equal = ArrayAreClose(expected, actual, count, tolerance); @@ -103,11 +103,11 @@ namespace UnitTest { UnitTest::MemoryOutStream stream; stream << "Expected [ "; - for (int expectedIndex = 0; expectedIndex < count; ++expectedIndex) + for (size_t expectedIndex = 0; expectedIndex < count; ++expectedIndex) stream << expected[expectedIndex] << " "; stream << "] +/- " << tolerance << " but was [ "; - for (int actualIndex = 0; actualIndex < count; ++actualIndex) + for (size_t actualIndex = 0; actualIndex < count; ++actualIndex) stream << actual[actualIndex] << " "; stream << "]"; @@ -117,10 +117,10 @@ namespace UnitTest { template< typename Expected, typename Actual, typename Tolerance > void CheckArray2DClose(TestResults& results, Expected const& expected, Actual const& actual, - int const rows, int const columns, Tolerance const& tolerance, TestDetails const& details) + size_t const rows, size_t const columns, Tolerance const& tolerance, TestDetails const& details) { bool equal = true; - for (int i = 0; i < rows; ++i) + for (size_t i = 0; i < rows; ++i) equal &= ArrayAreClose(expected[i], actual[i], columns, tolerance); if (!equal) @@ -129,20 +129,20 @@ namespace UnitTest { stream << "Expected [ "; - for (int expectedRow = 0; expectedRow < rows; ++expectedRow) + for (size_t expectedRow = 0; expectedRow < rows; ++expectedRow) { stream << "[ "; - for (int expectedColumn = 0; expectedColumn < columns; ++expectedColumn) + for (size_t expectedColumn = 0; expectedColumn < columns; ++expectedColumn) stream << expected[expectedRow][expectedColumn] << " "; stream << "] "; } stream << "] +/- " << tolerance << " but was [ "; - for (int actualRow = 0; actualRow < rows; ++actualRow) + for (size_t actualRow = 0; actualRow < rows; ++actualRow) { stream << "[ "; - for (int actualColumn = 0; actualColumn < columns; ++actualColumn) + for (size_t actualColumn = 0; actualColumn < columns; ++actualColumn) stream << actual[actualRow][actualColumn] << " "; stream << "] "; } diff --git a/UnitTest++/ExecuteTest.h b/UnitTest++/ExecuteTest.h index 2759aad..46db10e 100644 --- a/UnitTest++/ExecuteTest.h +++ b/UnitTest++/ExecuteTest.h @@ -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); } } diff --git a/UnitTest++/ParameterizedMacros.h b/UnitTest++/ParameterizedMacros.h new file mode 100644 index 0000000..37a71bc --- /dev/null +++ b/UnitTest++/ParameterizedMacros.h @@ -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 & globalInstance; \ + private: \ + static UnitTest::TestParameter & getGlobalInstance() \ + { \ + static UnitTest::TestParameter instance(#Name, create(), ListenerPtr); \ + return instance; \ + } \ + static vector create() { vector values; SetUpBody return values; } \ + } static parameterizedCreator##Name##Instance; \ + \ + static UnitTest::TestParameter & 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 \ No newline at end of file diff --git a/UnitTest++/ParameterizedManager.cpp b/UnitTest++/ParameterizedManager.cpp new file mode 100644 index 0000000..25de010 --- /dev/null +++ b/UnitTest++/ParameterizedManager.cpp @@ -0,0 +1,371 @@ +#include "ParameterizedManager.h" + +#include "Test.h" +#include + +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 & 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 & ignoredIndexes = it1->second; + + for (vector::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 & ignoredIndexes = _ignoredIndexes[parameterized]; + bool repeat = true; + while (repeat) + { + iterate(parameterized); + + vector::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::iterator ParameterizedManager::findIgnored(vector & ignoredIndexes, size_t index) +{ + for (vector::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 & 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 & ignoredIndexes = _ignoredIndexes[parameterized]; + + if (index >= parameterized->valuesCount()) + { + return *this; + } + + vector::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 indexes = parseParameter(parametersArrayRange, parameterName); + for (size_t i = 0; i < indexes.size(); i++) + { + ignoreIndex(parameterName, indexes[i], scope); + } + return *this; +} + + +vector 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 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 & ignoredIndexes = _ignoredIndexes[parameterized]; + vector::iterator ignoredIt = findIgnored(ignoredIndexes, index); + return (ignoredIt != ignoredIndexes.end()); +} + + +const vector & ParameterizedManager::getStack(TestDetails const * const details) const +{ + if (!isCurrentTest(details)) + { + static vector emptyStored; + return emptyStored; + } + return _stack; +} diff --git a/UnitTest++/ParameterizedManager.h b/UnitTest++/ParameterizedManager.h new file mode 100644 index 0000000..3645265 --- /dev/null +++ b/UnitTest++/ParameterizedManager.h @@ -0,0 +1,63 @@ +#ifndef UNITTEST_PARAMETERIZEDMANAGER_H +#define UNITTEST_PARAMETERIZEDMANAGER_H + +#include +#include +#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 & 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> IgnoredIndexesMap; + typedef unordered_map> 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::iterator findIgnored(vector & ignoredIndexes, size_t index); + void importIgnoredParameter(TestParameterAbstract* const parameterized); + void clearNonGlobalIgnoredIndexes(); + + static vector parseParameter(const string & parametersArrayRange, string & outParameterName); + + TestListNode* _currentTest; + TestListNode* _nextTestBackup; + vector _stack; + IgnoredIndexesMap _ignoredIndexes; + IgnoredIndexesByNameMap _pendingIgnoredIndexes; + volatile bool _iterationDone; + volatile bool _executing; + }; +} + +#endif \ No newline at end of file diff --git a/UnitTest++/SuitePredicate.cpp b/UnitTest++/SuitePredicate.cpp new file mode 100644 index 0000000..8a5748d --- /dev/null +++ b/UnitTest++/SuitePredicate.cpp @@ -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; +} diff --git a/UnitTest++/SuitePredicate.h b/UnitTest++/SuitePredicate.h new file mode 100644 index 0000000..f9fb31c --- /dev/null +++ b/UnitTest++/SuitePredicate.h @@ -0,0 +1,34 @@ +#ifndef UNITTEST_SUITEPREDICATE_H +#define UNITTEST_SUITEPREDICATE_H + +#include +#include +#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 _suiteNames; + vector _testNames; + bool _all; + }; +} + +#endif \ No newline at end of file diff --git a/UnitTest++/Test.cpp b/UnitTest++/Test.cpp index 82ed710..889960e 100644 --- a/UnitTest++/Test.cpp +++ b/UnitTest++/Test.cpp @@ -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 diff --git a/UnitTest++/Test.h b/UnitTest++/Test.h index beb0769..5250452 100644 --- a/UnitTest++/Test.h +++ b/UnitTest++/Test.h @@ -16,7 +16,6 @@ namespace UnitTest { void Run(); TestDetails const m_details; - Test* m_nextTest; mutable bool m_isMockTest; diff --git a/UnitTest++/TestDetails.cpp b/UnitTest++/TestDetails.cpp index deb6639..61c6a65 100644 --- a/UnitTest++/TestDetails.cpp +++ b/UnitTest++/TestDetails.cpp @@ -1,4 +1,5 @@ #include "TestDetails.h" +#include 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; + } } diff --git a/UnitTest++/TestDetails.h b/UnitTest++/TestDetails.h index cf6f1d3..1dac653 100644 --- a/UnitTest++/TestDetails.h +++ b/UnitTest++/TestDetails.h @@ -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&); diff --git a/UnitTest++/TestList.cpp b/UnitTest++/TestList.cpp index a2bc4b7..75946f9 100644 --- a/UnitTest++/TestList.cpp +++ b/UnitTest++/TestList.cpp @@ -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; } diff --git a/UnitTest++/TestList.h b/UnitTest++/TestList.h index d52ab51..21c0dab 100644 --- a/UnitTest++/TestList.h +++ b/UnitTest++/TestList.h @@ -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; }; diff --git a/UnitTest++/TestMacros.h b/UnitTest++/TestMacros.h index d6bc204..f703ae4 100644 --- a/UnitTest++/TestMacros.h +++ b/UnitTest++/TestMacros.h @@ -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, \ { \ diff --git a/UnitTest++/TestParameter.cpp b/UnitTest++/TestParameter.cpp new file mode 100644 index 0000000..6de4578 --- /dev/null +++ b/UnitTest++/TestParameter.cpp @@ -0,0 +1,78 @@ +#include "TestParameter.h" + +#include +#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); +} diff --git a/UnitTest++/TestParameter.h b/UnitTest++/TestParameter.h new file mode 100644 index 0000000..83eb297 --- /dev/null +++ b/UnitTest++/TestParameter.h @@ -0,0 +1,121 @@ +#ifndef UNITTEST_TESTPARAMETER_H +#define UNITTEST_TESTPARAMETER_H + +#include +#include +#include + +#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 TestParameter : public TestParameterAbstract + { + public: + struct IParameterListener + { + virtual void onNext(TestDetails const * const details, T currentValue, size_t currentIndex) = 0; + }; + + TestParameter(const string & name, vector values, IParameterListener* const listener = nullptr) + : TestParameterAbstract(name), + _values(values), + _listener(listener) + { + } + + T operator()() + { + size_t index = getCurrentIndex(); + return _values[index]; + } + + const vector & values() const + { + return _values; + } + + TestParameter & ignoreIndex(size_t index) + { + TestParameterAbstract::ignoreIndex(index); + return *this; + } + + TestParameter & ignore(T p) + { + vector::iterator it = find(_values.begin(), _values.end(), p); + if (it == _values.end()) + { + return *this; + } + return ignoreIndex(it - _values.begin()); + } + + bool isIgnored(T p) + { + vector::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 _values; + IParameterListener* const _listener; + }; +} + +#endif \ No newline at end of file diff --git a/UnitTest++/TestResults.cpp b/UnitTest++/TestResults.cpp index e40ea70..818900f 100644 --- a/UnitTest++/TestResults.cpp +++ b/UnitTest++/TestResults.cpp @@ -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 & 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) diff --git a/UnitTest++/TestRunner.cpp b/UnitTest++/TestRunner.cpp index 8e0581d..a71de3e 100644 --- a/UnitTest++/TestRunner.cpp +++ b/UnitTest++/TestRunner.cpp @@ -4,6 +4,8 @@ #include "TestReporterStdout.h" #include "TimeHelpers.h" #include "MemoryOutStream.h" +#include "SuitePredicate.h" +#include "ParameterizedManager.h" #include @@ -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) diff --git a/UnitTest++/TestRunner.h b/UnitTest++/TestRunner.h index 914249a..78b8962 100644 --- a/UnitTest++/TestRunner.h +++ b/UnitTest++/TestRunner.h @@ -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(); diff --git a/UnitTest++/UnitTestPP.h b/UnitTest++/UnitTestPP.h index 3e02d31..08f15bc 100644 --- a/UnitTest++/UnitTestPP.h +++ b/UnitTest++/UnitTestPP.h @@ -8,5 +8,6 @@ #include "TestRunner.h" #include "TimeConstraint.h" #include "ReportAssert.h" +#include "ParameterizedMacros.h" #endif diff --git a/tests/Main.cpp b/tests/Main.cpp index 4a0f402..3a629a4 100644 --- a/tests/Main.cpp +++ b/tests/Main.cpp @@ -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); } diff --git a/tests/TestExceptions.cpp b/tests/TestExceptions.cpp index 972e64d..08f6547 100644 --- a/tests/TestExceptions.cpp +++ b/tests/TestExceptions.cpp @@ -253,7 +253,7 @@ namespace { class ThrowingObject { public: - float operator[](int) const + float operator[](size_t) const { throw "Test throw"; } @@ -262,7 +262,7 @@ namespace { class StdThrowingObject { public: - float operator[](int) const + float operator[](size_t) const { throw std::runtime_error("Test throw"); } @@ -451,7 +451,7 @@ namespace { class ThrowingObject2D { public: - float* operator[](int) const + float* operator[](size_t) const { throw "Test throw"; } @@ -460,7 +460,7 @@ namespace { class StdThrowingObject2D { public: - float* operator[](int) const + float* operator[](size_t) const { throw std::runtime_error("Test throw"); } diff --git a/tests/TestLongMacros.cpp b/tests/TestLongMacros.cpp index 6720a97..6c58830 100644 --- a/tests/TestLongMacros.cpp +++ b/tests/TestLongMacros.cpp @@ -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 diff --git a/tests/TestParameterizedTest.cpp b/tests/TestParameterizedTest.cpp new file mode 100644 index 0000000..6589131 --- /dev/null +++ b/tests/TestParameterizedTest.cpp @@ -0,0 +1,451 @@ +#include "UnitTest++/UnitTest++.h" + +#include +#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 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 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); + } +} diff --git a/tests/TestTestDetails.cpp b/tests/TestTestDetails.cpp new file mode 100644 index 0000000..4a3c8c4 --- /dev/null +++ b/tests/TestTestDetails.cpp @@ -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)); + } +} diff --git a/tests/TestTestList.cpp b/tests/TestTestList.cpp index 4a0bdcb..7b4faad 100644 --- a/tests/TestTestList.cpp +++ b/tests/TestTestList.cpp @@ -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); } } diff --git a/tests/TestTestMacros.cpp b/tests/TestTestMacros.cpp index e66bfb6..5f31149 100644 --- a/tests/TestTestMacros.cpp +++ b/tests/TestTestMacros.cpp @@ -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();