This commit addresses two issues:

(1) unreachable code in for loop shenanigans is eliminated.
(2) code after a failing REQUIRE check no longer executes. Used a
    decorating TestReporter to achive this.
This commit is contained in:
Austin Gilbert 2014-12-06 22:59:28 -06:00 • committed by Patrick Johnmeyer
parent 06308ee802
commit a9161c1ba6
8 changed files with 201 additions and 10 deletions

View file

@ -41,6 +41,10 @@
if (!UnitTest::Check(value)) \
UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), #value); \
}) \
UT_CATCH (UnitTest::AssertException, e, \
{ \
UT_THROW(); \
}) \
UT_CATCH (std::exception, e, \
{ \
UnitTest::MemoryOutStream message; \
@ -60,7 +64,11 @@
UT_TRY \
({ \
UnitTest::CheckEqual(*UnitTest::CurrentTest::Results(), expected, actual, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \
}) \
}) \
UT_CATCH (UnitTest::AssertException, e, \
{ \
UT_THROW(); \
}) \
UT_CATCH (std::exception, e, \
{ \
UnitTest::MemoryOutStream message; \
@ -81,6 +89,10 @@
({ \
UnitTest::CheckClose(*UnitTest::CurrentTest::Results(), expected, actual, tolerance, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \
}) \
UT_CATCH (UnitTest::AssertException, e, \
{ \
UT_THROW(); \
}) \
UT_CATCH (std::exception, e, \
{ \
UnitTest::MemoryOutStream message; \
@ -100,7 +112,11 @@
UT_TRY \
({ \
UnitTest::CheckArrayEqual(*UnitTest::CurrentTest::Results(), expected, actual, count, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \
}) \
}) \
UT_CATCH (UnitTest::AssertException, e, \
{ \
UT_THROW(); \
}) \
UT_CATCH (std::exception, e, \
{ \
UnitTest::MemoryOutStream message; \
@ -121,6 +137,10 @@
({ \
UnitTest::CheckArrayClose(*UnitTest::CurrentTest::Results(), expected, actual, count, tolerance, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \
}) \
UT_CATCH (UnitTest::AssertException, e, \
{ \
UT_THROW(); \
}) \
UT_CATCH (std::exception, e, \
{ \
UnitTest::MemoryOutStream message; \
@ -140,7 +160,11 @@
UT_TRY \
({ \
UnitTest::CheckArray2DClose(*UnitTest::CurrentTest::Results(), expected, actual, rows, columns, tolerance, UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__)); \
}) \
}) \
UT_CATCH (UnitTest::AssertException, e, \
{ \
UT_THROW(); \
}) \
UT_CATCH (std::exception, e, \
{ \
UnitTest::MemoryOutStream message; \

View file

@ -1,19 +1,14 @@
#ifndef UNITTEST_REQUIREMACROS_H
#define UNITTEST_REQUIREMACROS_H
#include "HelperMacros.h"
#include "ExceptionMacros.h"
#include "CurrentTest.h"
#include "RequiredCheckTestReporter.h"
#ifdef REQUIRE
#error UnitTest++ redefines REQUIRE
#endif
#ifndef UNITTEST_NO_EXCEPTIONS
#define REQUIRE \
for (int failuresBeforeTest = UnitTest::CurrentTest::Results()->GetFailureCount(), newFailures = 0, run = 0; \
(run == 0) || ((newFailures != 0) && (throw UnitTest::AssertException(), true)); \
newFailures = UnitTest::CurrentTest::Results()->GetFailureCount() - failuresBeforeTest, run = 1)
#define REQUIRE for(UnitTest::RequiredCheckTestReporter decoratedReporter(UnitTest::CurrentTest::Results()); decoratedReporter.next(); )
#endif
#ifdef UNITTEST_NO_EXCEPTIONS

View file

@ -0,0 +1,29 @@
#include "RequiredCheckTestReporter.h"
#include "CurrentTest.h"
#include "TestResults.h"
namespace UnitTest {
RequiredCheckTestReporter::RequiredCheckTestReporter(TestResults* results)
: m_results(results)
, m_throwingReporter(0)
, m_continue(0)
{
if(m_results)
{
m_throwingReporter.setDecorated(m_results->m_testReporter);
m_results->m_testReporter = &m_throwingReporter;
}
}
RequiredCheckTestReporter::~RequiredCheckTestReporter()
{
if(m_results) m_results->m_testReporter = m_throwingReporter.getDecorated();
}
bool RequiredCheckTestReporter::next()
{
return m_continue++ == 0;
}
}

View file

@ -0,0 +1,29 @@
#ifndef UNITTEST_REQUIRED_CHECK_TEST_REPORTER_H
#define UNITTEST_REQUIRED_CHECK_TEST_REPORTER_H
#include "HelperMacros.h"
#include "ThrowingTestReporter.h"
namespace UnitTest {
class TestResults;
// This RAII class decorates the current TestReporter with
// a version that throws after reporting a failure.
class UNITTEST_LINKAGE RequiredCheckTestReporter
{
public:
explicit RequiredCheckTestReporter(TestResults* results);
~RequiredCheckTestReporter();
bool next();
private:
TestResults* m_results;
ThrowingTestReporter m_throwingReporter;
int m_continue;
};
}
#endif

View file

@ -5,6 +5,7 @@
namespace UnitTest {
class RequiredCheckTestReporter;
class TestReporter;
class TestDetails;
@ -22,6 +23,9 @@ namespace UnitTest {
int GetFailureCount() const;
private:
friend class RequiredCheckTestReporter;
private:
TestReporter* m_testReporter;
int m_totalTestCount;
int m_failedTestCount;

View file

@ -0,0 +1,51 @@
#include "ThrowingTestReporter.h"
#include "AssertException.h"
namespace UnitTest {
ThrowingTestReporter::ThrowingTestReporter(TestReporter* decoratedReporter)
: m_decoratedReporter(decoratedReporter)
{
}
//virtual
ThrowingTestReporter::~ThrowingTestReporter()
{
}
//virtual
void ThrowingTestReporter::ReportTestStart(TestDetails const& test)
{
if(m_decoratedReporter) m_decoratedReporter->ReportTestStart(test);
}
//virtual
void ThrowingTestReporter::ReportFailure(TestDetails const& test, char const* failure)
{
if(m_decoratedReporter) m_decoratedReporter->ReportFailure(test, failure);
throw AssertException();
}
//virtual
void ThrowingTestReporter::ReportTestFinish(TestDetails const& test, float secondsElapsed)
{
if(m_decoratedReporter) m_decoratedReporter->ReportTestFinish(test, secondsElapsed);
}
//virtual
void ThrowingTestReporter::ReportSummary(int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed)
{
if(m_decoratedReporter) m_decoratedReporter->ReportSummary(totalTestCount, failedTestCount, failureCount, secondsElapsed);
}
TestReporter* ThrowingTestReporter::getDecorated() const
{
return m_decoratedReporter;
}
void ThrowingTestReporter::setDecorated(TestReporter* reporter)
{
m_decoratedReporter = reporter;
}
}

View file

@ -0,0 +1,29 @@
#ifndef UNITTEST_THROWINGTESTREPORTER_H
#define UNITTEST_THROWINGTESTREPORTER_H
#include "TestReporter.h"
namespace UnitTest {
// A TestReporter that throws when ReportFailure is called. Otherwise it
// forwards the calls to a decorated TestReporter
class ThrowingTestReporter : public TestReporter
{
public:
explicit ThrowingTestReporter(TestReporter* reporter);
virtual ~ThrowingTestReporter();
virtual void ReportTestStart(TestDetails const& test);
virtual void ReportFailure(TestDetails const& test, char const* failure);
virtual void ReportTestFinish(TestDetails const& test, float secondsElapsed);
virtual void ReportSummary(int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed);
TestReporter* getDecorated() const;
void setDecorated(TestReporter* reporter);
private:
TestReporter* m_decoratedReporter;
};
}
#endif

View file

@ -117,7 +117,37 @@ TEST(RequireMacroSupportsMultipleChecksWithFailingChecks)
CHECK(failure);
CHECK(exception);
}
TEST(RequireMacroDoesntExecuteCodeAfterAFailingCheck)
{
bool failure = false;
bool exception = false;
bool run = false;
{
RecordingReporter reporter;
UnitTest::TestResults testResults(&reporter);
ScopedCurrentTest scopedResults(testResults);
try{
REQUIRE
{
CHECK(false);
run = true; // this shouldn't get executed.
}
}
catch (const UnitTest::AssertException&)
{
exception = true;
}
failure = (testResults.GetFailureCount() > 0);
}
CHECK(failure);
CHECK(exception);
CHECK(!run);
}
TEST(FailureReportsCorrectTestName)
{
RecordingReporter reporter;