From e7b56d4e3cff2154df3c613e7dfb3ad709a050ec Mon Sep 17 00:00:00 2001 From: Austin Gilbert Date: Fri, 17 Oct 2014 17:12:55 -0500 Subject: [PATCH] Introducing additional functionality to allow client code to stop a unit test when an assert fails. The following macro has been added: REQUIRE An example of when these type of checks are useful: std::vector v = foo(); REQUIRE(CHECK_EQUAL(3, v.size())); // test stops here on a fail // so we don't segfault looking at // v[0] below. CHECK_EQUAL(1, v[0]); CHECK_EQUAL(2, v[1]); CHECK_EQUAL(3, v[2]); Multiple checks are supported as follows: REQUIRE({ CHECK_EQUAL(1, 2); CHECK(true); }; In the multiple check scenario, all the checks in the REQUIRE block will be run. After which, if any failures were reported, the TEST case will be stopped. When UNITTEST_NO_EXCEPTIONS is defined, REQUIRE is a noop. --- UnitTest++/RequireMacros.h | 28 ++ UnitTest++/UnitTestPP.h | 1 + tests/TestRequireMacros.cpp | 842 ++++++++++++++++++++++++++++++++++++ 3 files changed, 871 insertions(+) create mode 100644 UnitTest++/RequireMacros.h create mode 100644 tests/TestRequireMacros.cpp diff --git a/UnitTest++/RequireMacros.h b/UnitTest++/RequireMacros.h new file mode 100644 index 0000000..65817d9 --- /dev/null +++ b/UnitTest++/RequireMacros.h @@ -0,0 +1,28 @@ +#ifndef UNITTEST_REQUIREMACROS_H +#define UNITTEST_REQUIREMACROS_H + +#include "HelperMacros.h" +#include "ExceptionMacros.h" +#include "CurrentTest.h" + +#ifdef REQUIRE + #error UnitTest++ redefines REQUIRE +#endif + +#ifndef UNITTEST_NO_EXCEPTIONS + #define REQUIRE(test) \ + UNITTEST_MULTILINE_MACRO_BEGIN \ + int const failuresBeforeTest = UnitTest::CurrentTest::Results()->GetFailureCount(); \ + test; \ + int const failuresAfterTest = UnitTest::CurrentTest::Results()->GetFailureCount(); \ + if(failuresAfterTest > failuresBeforeTest) \ + { \ + UT_THROW(UnitTest::AssertException()); \ + } \ + UNITTEST_MULTILINE_MACRO_END + #endif +#endif + +#ifdef UNITTEST_NO_EXCEPTIONS + #define REQUIRE(test) test; +#endif diff --git a/UnitTest++/UnitTestPP.h b/UnitTest++/UnitTestPP.h index c9bbc0c..3e02d31 100644 --- a/UnitTest++/UnitTestPP.h +++ b/UnitTest++/UnitTestPP.h @@ -4,6 +4,7 @@ #include "Config.h" #include "TestMacros.h" #include "CheckMacros.h" +#include "RequireMacros.h" #include "TestRunner.h" #include "TimeConstraint.h" #include "ReportAssert.h" diff --git a/tests/TestRequireMacros.cpp b/tests/TestRequireMacros.cpp new file mode 100644 index 0000000..3025a34 --- /dev/null +++ b/tests/TestRequireMacros.cpp @@ -0,0 +1,842 @@ +#include "UnitTest++/UnitTestPP.h" +#include "UnitTest++/CurrentTest.h" +#include "RecordingReporter.h" +#include "ScopedCurrentTest.h" + +using namespace std; + +#ifndef UNITTEST_NO_EXCEPTIONS + +namespace { + +TEST(RequireCheckSucceedsOnTrue) +{ + bool failure = true; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + + ScopedCurrentTest scopedResults(testResults); + + try + { + REQUIRE(CHECK(true)); + } + catch(const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(!failure); + CHECK(!exception); +} + +TEST(RequiredCheckFailsOnFalse) +{ + bool failure = false; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + try + { + REQUIRE(CHECK(false)); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(failure); + CHECK(exception); +} + + +TEST(RequireMacroSupportsMultipleChecks) +{ + bool failure = false; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + try{ + REQUIRE({ + CHECK(true); + CHECK_EQUAL(1,1); + }); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(!failure); + CHECK(!exception); +} + + +TEST(RequireMacroSupportsMultipleChecksWithFailingChecks) +{ + bool failure = false; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + try{ + REQUIRE({ + CHECK(true); + CHECK_EQUAL(1,2); + }); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(failure); + CHECK(exception); +} + +TEST(FailureReportsCorrectTestName) +{ + RecordingReporter reporter; + { + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + try + { + REQUIRE(CHECK(false)); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK_EQUAL(m_details.testName, reporter.lastFailedTest); +} + +TEST(RequiredCheckFailureIncludesCheckContents) +{ + RecordingReporter reporter; + { + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + const bool yaddayadda = false; + + try + { + REQUIRE(CHECK(yaddayadda)); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK(strstr(reporter.lastFailedMessage, "yaddayadda")); +} + +TEST(RequiredCheckEqualSucceedsOnEqual) +{ + bool failure = true; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + try + { + REQUIRE(CHECK_EQUAL(1,1)); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(!failure); + CHECK(!exception); +} + +TEST(RequiredCheckEqualFailsOnNotEqual) +{ + bool failure = false; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + try + { + REQUIRE(CHECK_EQUAL(1, 2)); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(failure); + CHECK(exception); +} + +TEST(RequiredCheckEqualFailureContainsCorrectDetails) +{ + int line = 0; + RecordingReporter reporter; + { + UnitTest::TestResults testResults(&reporter); + UnitTest::TestDetails const testDetails("testName", "suiteName", "filename", -1); + ScopedCurrentTest scopedResults(testResults, &testDetails); + + try + { + line = __LINE__; REQUIRE(CHECK_EQUAL(1, 123)); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK_EQUAL("testName", reporter.lastFailedTest); + CHECK_EQUAL("suiteName", reporter.lastFailedSuite); + CHECK_EQUAL("filename", reporter.lastFailedFile); + CHECK_EQUAL(line, reporter.lastFailedLine); +} + +int g_sideEffect = 0; +int FunctionWithSideEffects() +{ + ++g_sideEffect; + return 1; +} + +TEST(RequiredCheckEqualDoesNotHaveSideEffectsWhenPassing) +{ + g_sideEffect = 0; + { + UnitTest::TestResults testResults; + ScopedCurrentTest scopedResults(testResults); + + try + { + REQUIRE(CHECK_EQUAL(1, FunctionWithSideEffects())); + } + catch (const UnitTest::AssertException&) + { + } + } + CHECK_EQUAL(1, g_sideEffect); +} + +TEST(RequiredCheckEqualDoesNotHaveSideEffectsWhenFailing) +{ + g_sideEffect = 0; + { + UnitTest::TestResults testResults; + ScopedCurrentTest scopedResults(testResults); + + try + { + REQUIRE(CHECK_EQUAL(2, FunctionWithSideEffects())); + } + catch (const UnitTest::AssertException&) + { + } + } + CHECK_EQUAL(1, g_sideEffect); +} + + +TEST(RequiredCheckCloseSucceedsOnEqual) +{ + bool failure = true; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + try + { + REQUIRE(CHECK_CLOSE(1.0f, 1.001f, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(!failure); + CHECK(!exception); +} + +TEST(RequiredCheckCloseFailsOnNotEqual) +{ + bool failure = false; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + try + { + REQUIRE(CHECK_CLOSE (1.0f, 1.1f, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(failure); + CHECK(exception); +} + +TEST(RequiredCheckCloseFailureContainsCorrectDetails) +{ + int line = 0; + RecordingReporter reporter; + { + UnitTest::TestResults testResults(&reporter); + UnitTest::TestDetails testDetails("test", "suite", "filename", -1); + ScopedCurrentTest scopedResults(testResults, &testDetails); + + try + { + line = __LINE__; REQUIRE(CHECK_CLOSE(1.0f, 1.1f, 0.01f)); + CHECK(false); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK_EQUAL("test", reporter.lastFailedTest); + CHECK_EQUAL("suite", reporter.lastFailedSuite); + CHECK_EQUAL("filename", reporter.lastFailedFile); + CHECK_EQUAL(line, reporter.lastFailedLine); +} + +TEST(RequiredCheckCloseDoesNotHaveSideEffectsWhenPassing) +{ + g_sideEffect = 0; + { + UnitTest::TestResults testResults; + ScopedCurrentTest scopedResults(testResults); + + try + { + REQUIRE(CHECK_CLOSE (1, FunctionWithSideEffects(), 0.1f)); + } + catch (const UnitTest::AssertException&) + { + } + } + CHECK_EQUAL(1, g_sideEffect); +} + +TEST(RequiredCheckCloseDoesNotHaveSideEffectsWhenFailing) +{ + g_sideEffect = 0; + { + UnitTest::TestResults testResults; + ScopedCurrentTest scopedResults(testResults); + + try + { + REQUIRE(CHECK_CLOSE(2, FunctionWithSideEffects(), 0.1f)); + } + catch (const UnitTest::AssertException&) + { + } + } + CHECK_EQUAL(1, g_sideEffect); +} + +TEST(RequiredCheckArrayCloseSucceedsOnEqual) +{ + bool failure = true; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + const float data[4] = { 0, 1, 2, 3 }; + + try + { + REQUIRE(CHECK_ARRAY_CLOSE (data, data, 4, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(!failure); + CHECK(!exception); +} + +TEST(RequiredCheckArrayCloseFailsOnNotEqual) +{ + bool failure = false; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + int const data1[4] = { 0, 1, 2, 3 }; + int const data2[4] = { 0, 1, 3, 3 }; + + try + { + REQUIRE(CHECK_ARRAY_CLOSE (data1, data2, 4, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(failure); + CHECK(exception); +} + +TEST(RequiredCheckArrayCloseFailureIncludesCheckExpectedAndActual) +{ + RecordingReporter reporter; + { + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + int const data1[4] = { 0, 1, 2, 3 }; + int const data2[4] = { 0, 1, 3, 3 }; + + try + { + REQUIRE(CHECK_ARRAY_CLOSE(data1, data2, 4, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK(strstr(reporter.lastFailedMessage, "xpected [ 0 1 2 3 ]")); + CHECK(strstr(reporter.lastFailedMessage, "was [ 0 1 3 3 ]")); +} + +TEST(RequiredCheckArrayCloseFailureContainsCorrectDetails) +{ + int line = 0; + RecordingReporter reporter; + { + UnitTest::TestResults testResults(&reporter); + UnitTest::TestDetails testDetails("arrayCloseTest", "arrayCloseSuite", "filename", -1); + ScopedCurrentTest scopedResults(testResults, &testDetails); + + int const data1[4] = { 0, 1, 2, 3 }; + int const data2[4] = { 0, 1, 3, 3 }; + + try + { + line = __LINE__; REQUIRE(CHECK_ARRAY_CLOSE (data1, data2, 4, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK_EQUAL("arrayCloseTest", reporter.lastFailedTest); + CHECK_EQUAL("arrayCloseSuite", reporter.lastFailedSuite); + CHECK_EQUAL("filename", reporter.lastFailedFile); + CHECK_EQUAL(line, reporter.lastFailedLine); +} + +TEST(RequiredCheckArrayCloseFailureIncludesTolerance) +{ + RecordingReporter reporter; + { + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + float const data1[4] = { 0, 1, 2, 3 }; + float const data2[4] = { 0, 1, 3, 3 }; + + try + { + REQUIRE(CHECK_ARRAY_CLOSE (data1, data2, 4, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK(strstr(reporter.lastFailedMessage, "0.01")); +} + +TEST(RequiredCheckArrayEqualSuceedsOnEqual) +{ + bool failure = true; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + const float data[4] = { 0, 1, 2, 3 }; + + try + { + REQUIRE(CHECK_ARRAY_EQUAL (data, data, 4)); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(!failure); + CHECK(!exception); +} + +TEST(RequiredCheckArrayEqualFailsOnNotEqual) +{ + bool failure = false; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + int const data1[4] = { 0, 1, 2, 3 }; + int const data2[4] = { 0, 1, 3, 3 }; + + try + { + REQUIRE(CHECK_ARRAY_EQUAL (data1, data2, 4)); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(failure); + CHECK(exception); +} + +TEST(RequiredCheckArrayEqualFailureIncludesCheckExpectedAndActual) +{ + RecordingReporter reporter; + { + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + int const data1[4] = { 0, 1, 2, 3 }; + int const data2[4] = { 0, 1, 3, 3 }; + + try + { + REQUIRE(CHECK_ARRAY_EQUAL (data1, data2, 4)); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK(strstr(reporter.lastFailedMessage, "xpected [ 0 1 2 3 ]")); + CHECK(strstr(reporter.lastFailedMessage, "was [ 0 1 3 3 ]")); +} + +TEST(RequiredCheckArrayEqualFailureContainsCorrectInfo) +{ + int line = 0; + RecordingReporter reporter; + { + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + int const data1[4] = { 0, 1, 2, 3 }; + int const data2[4] = { 0, 1, 3, 3 }; + + try + { + line = __LINE__; REQUIRE(CHECK_ARRAY_EQUAL (data1, data2, 4)); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK_EQUAL("RequiredCheckArrayEqualFailureContainsCorrectInfo", reporter.lastFailedTest); + CHECK_EQUAL(__FILE__, reporter.lastFailedFile); + CHECK_EQUAL(line, reporter.lastFailedLine); +} + +float const* FunctionWithSideEffects2() +{ + ++g_sideEffect; + static float const data[] = {1,2,3,4}; + return data; +} + +TEST(RequiredCheckArrayCloseDoesNotHaveSideEffectsWhenPassing) +{ + g_sideEffect = 0; + { + UnitTest::TestResults testResults; + ScopedCurrentTest scopedResults(testResults); + + const float data[] = { 0, 1, 2, 3 }; + + try + { + REQUIRE(CHECK_ARRAY_CLOSE (data, FunctionWithSideEffects2(), 4, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + } + } + CHECK_EQUAL(1, g_sideEffect); +} + +TEST(RequiredCheckArrayCloseDoesNotHaveSideEffectsWhenFailing) +{ + g_sideEffect = 0; + { + UnitTest::TestResults testResults; + ScopedCurrentTest scopedResults(testResults); + + const float data[] = { 0, 1, 3, 3 }; + + try + { + REQUIRE(CHECK_ARRAY_CLOSE (data, FunctionWithSideEffects2(), 4, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK_EQUAL(1, g_sideEffect); +} + +TEST(RequiredCheckArray2DCloseSucceedsOnEqual) +{ + bool failure = true; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + const float data[2][2] = { {0, 1}, {2, 3} }; + + try + { + REQUIRE(CHECK_ARRAY2D_CLOSE(data, data, 2, 2, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(!failure); + CHECK(!exception); +} + +TEST(RequiredCheckArray2DCloseFailsOnNotEqual) +{ + bool failure = false; + bool exception = false; + { + RecordingReporter reporter; + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + int const data1[2][2] = { {0, 1}, {2, 3} }; + int const data2[2][2] = { {0, 1}, {3, 3} }; + + try + { + REQUIRE(CHECK_ARRAY2D_CLOSE (data1, data2, 2, 2, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + exception = true; + } + + failure = (testResults.GetFailureCount() > 0); + } + + CHECK(failure); + CHECK(exception); +} + +TEST(RequiredCheckArray2DCloseFailureIncludesCheckExpectedAndActual) +{ + RecordingReporter reporter; + { + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + int const data1[2][2] = { {0, 1}, {2, 3} }; + int const data2[2][2] = { {0, 1}, {3, 3} }; + + try + { + REQUIRE(CHECK_ARRAY2D_CLOSE (data1, data2, 2, 2, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK(strstr(reporter.lastFailedMessage, "xpected [ [ 0 1 ] [ 2 3 ] ]")); + CHECK(strstr(reporter.lastFailedMessage, "was [ [ 0 1 ] [ 3 3 ] ]")); +} + +TEST(RequiredCheckArray2DCloseFailureContainsCorrectDetails) +{ + int line = 0; + RecordingReporter reporter; + { + UnitTest::TestResults testResults(&reporter); + UnitTest::TestDetails testDetails("array2DCloseTest", "array2DCloseSuite", "filename", -1); + ScopedCurrentTest scopedResults(testResults, &testDetails); + + int const data1[2][2] = { {0, 1}, {2, 3} }; + int const data2[2][2] = { {0, 1}, {3, 3} }; + + try + { + line = __LINE__; REQUIRE(CHECK_ARRAY2D_CLOSE (data1, data2, 2, 2, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK_EQUAL("array2DCloseTest", reporter.lastFailedTest); + CHECK_EQUAL("array2DCloseSuite", reporter.lastFailedSuite); + CHECK_EQUAL("filename", reporter.lastFailedFile); + CHECK_EQUAL(line, reporter.lastFailedLine); +} + +TEST(RequiredCheckArray2DCloseFailureIncludesTolerance) +{ + RecordingReporter reporter; + { + UnitTest::TestResults testResults(&reporter); + ScopedCurrentTest scopedResults(testResults); + + float const data1[2][2] = { {0, 1}, {2, 3} }; + float const data2[2][2] = { {0, 1}, {3, 3} }; + + try + { + REQUIRE(CHECK_ARRAY2D_CLOSE (data1, data2, 2, 2, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + } + } + + CHECK(strstr(reporter.lastFailedMessage, "0.01")); +} + +float const* const* FunctionWithSideEffects3() +{ + ++g_sideEffect; + static float const data1[] = {0,1}; + static float const data2[] = {2,3}; + static const float* const data[] = {data1, data2}; + return data; +} + +TEST(RequiredCheckArray2DCloseDoesNotHaveSideEffectsWhenPassing) +{ + g_sideEffect = 0; + { + UnitTest::TestResults testResults; + ScopedCurrentTest scopedResults(testResults); + + const float data[2][2] = { {0, 1}, {2, 3} }; + + try + { + REQUIRE(CHECK_ARRAY2D_CLOSE (data, FunctionWithSideEffects3(), 2, 2, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + } + } + CHECK_EQUAL(1, g_sideEffect); +} + +TEST(RequiredCheckArray2DCloseDoesNotHaveSideEffectsWhenFailing) +{ + g_sideEffect = 0; + { + UnitTest::TestResults testResults; + ScopedCurrentTest scopedResults(testResults); + + const float data[2][2] = { {0, 1}, {3, 3} }; + + try + { + REQUIRE(CHECK_ARRAY2D_CLOSE (data, FunctionWithSideEffects3(), 2, 2, 0.01f)); + } + catch (const UnitTest::AssertException&) + { + } + } + CHECK_EQUAL(1, g_sideEffect); +} + +} + +#endif