diff --git a/CMakeLists.txt b/CMakeLists.txt
index 04f62a9..ce2e566 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,7 @@ cmake_minimum_required (VERSION 3.16)
set (PROJECT_NAME "TestCPP")
set (PROJECT_GROUP_NAME "cpptesting")
-project (${PROJECT_NAME} VERSION 0.2.1 LANGUAGES CXX)
+project (${PROJECT_NAME} VERSION 1.0.0 LANGUAGES CXX)
set (CMAKE_CXX_STANDARD 11)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
diff --git a/TestFramework.project b/TestFramework.project
index d6badc7..bab697a 100644
--- a/TestFramework.project
+++ b/TestFramework.project
@@ -3,12 +3,22 @@
-
+
+
+
+
-
+
+
+
+
+
+
+
+
@@ -48,6 +58,10 @@
+
+
+
+
@@ -59,6 +73,10 @@
+
+
+
+
diff --git a/cmake/BuildTypeHandling.cmake b/cmake/BuildTypeHandling.cmake
index eba34cd..67543b7 100644
--- a/cmake/BuildTypeHandling.cmake
+++ b/cmake/BuildTypeHandling.cmake
@@ -80,13 +80,23 @@ endif ()
list (
APPEND
GCC_CLANG_RELEASE_BUILD_OPTS
- -O3 # Optimize the Release build
- -Wall # Enable most warnings
- -Wextra # Enable even more warnings
- -Wpedantic # Enable most of the rest of the warnings
- -Werror # Treat all warnings as errors
- -Wno-unused-parameter # Unused parameters occur in the Release
- # build in debugLog
+ -O3 # Optimize the Release build
+ -Wall # Enable most warnings
+ -Wextra # Enable even more warnings
+ -Wpedantic # Enable most of the rest of the warnings
+ -Werror # Treat all warnings as errors
+ -Wno-unused-parameter # Unused parameters occur in the Release
+ # build in debugLog
+ -Wno-unused-lambda-capture # Avoid MSVC error C3493 - There is
+ # implementation divergence here and
+ # since we're not using >=C++14 there
+ # is no workaround other than to ignore
+ # this warning (the MSVC issue is an
+ # error). A workaround for >=C++14 is to
+ # use an explicit capture - if ever I
+ # change the library to use >=C++14 I
+ # can remove this and use an explicit
+ # capture.
)
list (
APPEND
@@ -94,7 +104,7 @@ list (
-g # Enable all debugging information
-Og # Ensure the compiler doesn't use optimizations that would harm
# debuggability of the resulting code
- -Wall -Wextra -Wpedantic -Werror
+ -Wall -Wextra -Wpedantic -Werror -Wno-unused-lambda-capture
)
list (
APPEND
@@ -135,6 +145,12 @@ if (${CMAKE_BUILD_TYPE} STREQUAL "Release")
PUBLIC
${MSVC_RELEASE_BUILD_OPTS}
)
+
+ target_compile_options (
+ ${PROJECT_NAME}_Assertions_test
+ PUBLIC
+ ${MSVC_RELEASE_BUILD_OPTS}
+ )
endif ()
else ()
@@ -164,6 +180,12 @@ if (${CMAKE_BUILD_TYPE} STREQUAL "Release")
PUBLIC
${GCC_CLANG_RELEASE_BUILD_OPTS}
)
+
+ target_compile_options (
+ ${PROJECT_NAME}_Assertions_test
+ PUBLIC
+ ${GCC_CLANG_RELEASE_BUILD_OPTS}
+ )
endif ()
endif ()
@@ -195,6 +217,11 @@ else ()
PUBLIC
DEBUG_LOG
)
+ target_compile_definitions (
+ ${PROJECT_NAME}_Assertions_test
+ PUBLIC
+ DEBUG_LOG
+ )
endif ()
if (MSVC)
@@ -224,6 +251,12 @@ else ()
PUBLIC
${MSVC_DEBUG_BUILD_OPTS}
)
+
+ target_compile_options (
+ ${PROJECT_NAME}_Assertions_test
+ PUBLIC
+ ${MSVC_DEBUG_BUILD_OPTS}
+ )
endif ()
else ()
@@ -256,6 +289,12 @@ else ()
${COVERAGE_BUILD_OPTS}
)
+ target_compile_options (
+ ${PROJECT_NAME}_Assertions_test
+ PUBLIC
+ ${COVERAGE_BUILD_OPTS}
+ )
+
else ()
target_compile_options (
${PROJECT_NAME}
diff --git a/cmake/Includes.cmake b/cmake/Includes.cmake
index 200a7ae..a34ba82 100644
--- a/cmake/Includes.cmake
+++ b/cmake/Includes.cmake
@@ -32,4 +32,10 @@ if (BUILD_TESTING)
test/include
include
)
+
+ target_include_directories (
+ ${PROJECT_NAME}_Assertions_test PRIVATE
+ test/include
+ include
+ )
endif ()
diff --git a/cmake/Installing.cmake b/cmake/Installing.cmake
index 39ac2b2..7606865 100644
--- a/cmake/Installing.cmake
+++ b/cmake/Installing.cmake
@@ -1,8 +1,31 @@
+list (
+ APPEND
+ TESTCPP_PUBLIC_HEADERS
+ include/TestCPP.h
+)
+
+list (
+ APPEND
+ TESTCPP_PRIVATE_HEADERS
+ include/internal/TestCPPAssertions.h
+ include/internal/TestCPPCommon.h
+ include/internal/TestCPPExceptions.h
+ include/internal/TestCPPTestCase.h
+ include/internal/TestCPPTestSuite.h
+ include/internal/TestCPPUtil.h
+)
+
set_target_properties (
${PROJECT_NAME}
PROPERTIES
PUBLIC_HEADER
- include/TestCPP.h
+ "${TESTCPP_PUBLIC_HEADERS}"
+)
+set_target_properties (
+ ${PROJECT_NAME}
+ PROPERTIES
+ PRIVATE_HEADER
+ "${TESTCPP_PRIVATE_HEADERS}"
)
include (GNUInstallDirs)
install (
@@ -10,6 +33,8 @@ install (
EXPORT ${PROJECT_NAME}Targets
PUBLIC_HEADER
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
+ PRIVATE_HEADER
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/internal
)
export (
EXPORT ${PROJECT_NAME}Targets
diff --git a/cmake/Linking.cmake b/cmake/Linking.cmake
index f6c6cb0..f585edf 100644
--- a/cmake/Linking.cmake
+++ b/cmake/Linking.cmake
@@ -46,6 +46,13 @@ if (BUILD_TESTING)
ole32
dbgeng
)
+ target_link_libraries (
+ ${PROJECT_NAME}_Assertions_test
+ ${PROJECT_NAME}
+ gcov
+ ole32
+ dbgeng
+ )
elseif (${TESTCPP_STACKTRACE_ENABLED})
target_link_libraries (
@@ -60,6 +67,12 @@ if (BUILD_TESTING)
gcov
dl
)
+ target_link_libraries (
+ ${PROJECT_NAME}_Assertions_test
+ ${PROJECT_NAME}
+ gcov
+ dl
+ )
else ()
target_link_libraries (
@@ -72,6 +85,11 @@ if (BUILD_TESTING)
${PROJECT_NAME}
gcov
)
+ target_link_libraries (
+ ${PROJECT_NAME}_Assertions_test
+ ${PROJECT_NAME}
+ gcov
+ )
endif ()
else ()
@@ -88,6 +106,12 @@ if (BUILD_TESTING)
ole32
dbgeng
)
+ target_link_libraries (
+ ${PROJECT_NAME}_Assertions_test
+ ${PROJECT_NAME}
+ ole32
+ dbgeng
+ )
elseif (${TESTCPP_STACKTRACE_ENABLED})
target_link_libraries (
@@ -100,6 +124,11 @@ if (BUILD_TESTING)
${PROJECT_NAME}
dl
)
+ target_link_libraries (
+ ${PROJECT_NAME}_Assertions_test
+ ${PROJECT_NAME}
+ dl
+ )
else ()
target_link_libraries (
@@ -110,6 +139,10 @@ if (BUILD_TESTING)
${PROJECT_NAME}_TestSuite_test
${PROJECT_NAME}
)
+ target_link_libraries (
+ ${PROJECT_NAME}_Assertions_test
+ ${PROJECT_NAME}
+ )
endif ()
endif ()
endif ()
diff --git a/cmake/Targets.cmake b/cmake/Targets.cmake
index 49fa350..66b198e 100644
--- a/cmake/Targets.cmake
+++ b/cmake/Targets.cmake
@@ -1,7 +1,10 @@
add_library (
${PROJECT_NAME}
+ src/TestCPPAssertions.cpp
+ src/TestCPPExceptions.cpp
+ src/TestCPPTestCase.cpp
+ src/TestCPPTestSuite.cpp
src/TestCPPUtil.cpp
- src/TestCPP.cpp
)
add_library (
${PROJECT_GROUP_NAME}::${PROJECT_NAME}
@@ -30,4 +33,10 @@ if (BUILD_TESTING)
test/src/TestSuite/TestSuiteTests.cpp
test/src/TestCPPTestSuiteMain.cpp
)
+
+ add_executable (
+ ${PROJECT_NAME}_Assertions_test
+ test/src/Assertions/AssertionsTests.cpp
+ test/src/TestCPPAssertionsMain.cpp
+ )
endif ()
diff --git a/cmake/Testing.cmake b/cmake/Testing.cmake
index e454ef8..b7c4c1b 100644
--- a/cmake/Testing.cmake
+++ b/cmake/Testing.cmake
@@ -8,4 +8,9 @@ if (BUILD_TESTING)
NAME ${PROJECT_NAME}TestSuiteTests
COMMAND ${PROJECT_NAME}_TestSuite_test
)
+
+ add_test (
+ NAME ${PROJECT_NAME}AssertionsTests
+ COMMAND ${PROJECT_NAME}_Assertions_test
+ )
endif ()
diff --git a/demo/src/main.cpp b/demo/src/main.cpp
index eb65b50..dd0cd66 100644
--- a/demo/src/main.cpp
+++ b/demo/src/main.cpp
@@ -1,5 +1,4 @@
#include "TestCPP.h"
-#include "TestCPPUtil.h"
using TestCPP::TestSuite;
using std::string;
@@ -12,7 +11,7 @@ int main(void)
{
try {
TestSuite suite(
- string("Demo Test Suite"),
+ "Demo Test Suite",
make_tuple(
"simpleTest",
diff --git a/demo/src/tests.cpp b/demo/src/tests.cpp
index 7660d8a..77f6042 100644
--- a/demo/src/tests.cpp
+++ b/demo/src/tests.cpp
@@ -6,13 +6,16 @@ namespace TestCPP {
void simpleTest () {
int lower = 5;
int higher = 9;
- TestSuite::assertTrue(higher > lower, "Something is seriously wrong.");
+ Assertions::assertTrue(
+ higher > lower,
+ "Something is seriously wrong."
+ );
}
void otherSimpleTest () {
string s1 = string("A string");
string s2 = string("another string");
- TestSuite::assertNotEquals(s1, s2);
+ Assertions::assertNotEquals(s1, s2);
}
}
}
diff --git a/include/TestCPP.h b/include/TestCPP.h
index 0567063..74f6ad2 100644
--- a/include/TestCPP.h
+++ b/include/TestCPP.h
@@ -25,313 +25,14 @@ OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to
*/
-//Original author: Jonathan Hyry CSU-Fullerton SECS 6896-02 Fall 2014/15
-
-#ifndef TESTCPP_CLASSES_
-#define TESTCPP_CLASSES_
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-using std::atomic_int;
-using std::chrono::nanoseconds;
-using std::chrono::system_clock;
-using std::chrono::duration_cast;
-using std::enable_if;
-using std::endl;
-using std::forward;
-using std::function;
-using std::move;
-using std::runtime_error;
-using std::string;
-using std::streambuf;
-using std::stringstream;
-using std::unique_ptr;
-using std::vector;
-
-namespace TestCPP {
- class TestCPPException : public runtime_error {
- public:
- TestCPPException (const char * msg);
- TestCPPException (string&& msg);
- };
-
- class TestFailedException : public TestCPPException {
- public:
- TestFailedException (const char * msg);
- TestFailedException (string&& msg);
- };
-
- class TestCaseName {
- public:
- TestCaseName () = default;
- TestCaseName (const char* name);
-
- const string& getTestName ();
-
- friend std::ostream& operator<< (
- std::ostream& s,
- TestCaseName& tcName
- )
- {
- s << tcName.getTestName();
- return s;
- }
-
- private:
- string testCaseName;
-
- };
-
- class TestCase {
-
- public:
- enum TestCaseOutCompareOptions {
- CONTAINS,
- EXACT
- };
-
- TestCase (
- TestCaseName&& testName,
- function test,
- bool testPassedMessage = true,
- bool captureOut = false,
- bool captureLog = false,
- bool captureErr = false,
- TestCaseOutCompareOptions opt = CONTAINS
- );
-
- TestCase (TestCase& o);
- TestCase (TestCase&& o);
-
- TestCase& operator= (TestCase& rhs);
- TestCase& operator= (TestCase&& rhs);
-
- ~TestCase ();
-
- void setNotifyPassed (bool);
- void outCompareOption (TestCaseOutCompareOptions opt);
- void clearStdoutCapture ();
- void clearLogCapture ();
- void clearStderrCapture ();
- bool checkStdout (string against);
- bool checkLog (string against);
- bool checkStderr (string against);
- bool go ();
- long long getLastRuntime ();
-
- private:
- bool notifyTestPassed;
- bool pass;
- long long lastRunTime;
-
- TestCaseName testName;
- function test;
-
- TestCaseOutCompareOptions option;
-
- void captureStdout ();
- void captureClog ();
- void captureStdErr ();
- void logTestFailure (string);
- void runTest ();
- bool checkOutput (TestCaseOutCompareOptions opt, string source,
- string against);
-
- static atomic_int stdoutCaptureCasesConstructed;
- static atomic_int logCaptureCasesConstructed;
- static atomic_int stderrCaptureCasesConstructed;
- static atomic_int stdoutCaptureCasesDestroyed;
- static atomic_int logCaptureCasesDestroyed;
- static atomic_int stderrCaptureCasesDestroyed;
-
- static unique_ptr stdoutBuffer;
- static unique_ptr clogBuffer;
- static unique_ptr stderrBuffer;
- static unique_ptr stdoutOriginal;
- static unique_ptr clogOriginal;
- static unique_ptr stderrOriginal;
-
- template
- static nanoseconds duration (F func, Args&&... args)
- {
- auto start = system_clock::now();
- func(forward(args)...);
- return duration_cast(
- system_clock::now() - start
- );
- }
- };
-
- class TestSuite {
-
- public:
- template
- TestSuite (string suiteName,
- typename enable_if::type)
- {
- this->testPassedMessage = true;
- this->setSuiteName(suiteName);
- this->tests = vector();
- }
-
- template
- TestSuite (string suiteName, TestType ...tests) {
- this->testPassedMessage = true;
- this->setSuiteName(suiteName);
- this->tests = vector();
-
- this->addTests(tests...);
- }
-
- template
- void addTest (T&& test) {
- this->tests.emplace_back(
- std::get<0>(test),
- std::get<1>(test),
- this->testPassedMessage
- );
- }
-
- template
- typename enable_if::type
- inline addTests () { }
-
- template
- void addTests (Test test, OtherTests ...tests) {
- addTest(move(test));
- addTests(tests...);
- }
-
- template
- static T getTestObject (ConstructionArgs ...args) {
- return T(args...);
- }
-
- void setSuiteName (string testSuiteName);
-
- template
- static void assertEquals (
- T1 expected, T2 actual,
- string failureMessage = "Arguments are not equivalent!"
- )
- {
- if (expected != actual) {
- stringstream err;
-
- err << "Equivalence assertion failed!" << endl;
- err << failureMessage << endl;
- err << "Expected: <" << expected << ">" << endl;
- err << "Actual: <" << actual << ">" << endl;
-
- throw TestFailedException(err.str());
- }
- }
-
- template
- static void assertNotEquals (
- T1 expected, T2 actual,
- string failureMessage = "Arguments are equivalent!"
- )
- {
- if (expected == actual) {
- stringstream err;
-
- err << "Non-Equivalence assertion failed!" << endl;
- err << failureMessage << endl;
- err << "Expected: <" << expected << ">" << endl;
- err << "Actual: <" << actual << ">" << endl;
-
- throw TestFailedException(err.str());
- }
- }
-
- template
- static void assertNull (
- T ptr,
- string failureMessage = "Object is not null!"
- )
- {
- bool null = ptr == nullptr;
-
- if (!null) {
- stringstream err;
-
- err << "Null assertion failed!" << endl;
- err << failureMessage << endl;
-
- throw TestFailedException(err.str());
- }
- }
-
- template
- static void assertNotNull (
- T ptr,
- string failureMessage = "Object is null!"
- )
- {
- bool notNull = ptr != nullptr;
-
- if (!notNull) {
- stringstream err;
-
- err << "Not Null assertion failed!" << endl;
- err << failureMessage << endl;
-
- throw TestFailedException(err.str());
- }
- }
-
- static void assertThrows (
- function shouldThrow,
- string failureMessage =
- "Should have thrown something!"
- );
-
- static void assertNoThrows (
- function shouldNotThrow,
- string failureMessage =
- "Should not have thrown anything!"
- );
-
- static void assertTrue (
- bool condition,
- string failureMessage = "Condition is false!"
- );
-
- static void assertFalse (
- bool condition,
- string failureMessage = "Condition is true!"
- );
-
- static void fail (
- string failureMessage = "Forced test failure!"
- );
-
- void enableTestPassedMessage ();
- void disableTestPassedMessage ();
-
- unsigned getLastRunFailCount ();
-
- void run ();
-
- private:
- bool testPassedMessage;
- bool lastRunSucceeded;
- unsigned lastRunSuccessCount;
- unsigned lastRunFailCount;
- unsigned long long totalRuntime;
-
- string suiteName;
- vector tests;
- };
-}
+#ifndef TESTCPP_AGGREGATE_
+#define TESTCPP_AGGREGATE_
+
+#include "internal/TestCPPAssertions.h"
+#include "internal/TestCPPCommon.h"
+#include "internal/TestCPPExceptions.h"
+#include "internal/TestCPPTestCase.h"
+#include "internal/TestCPPTestSuite.h"
+#include "internal/TestCPPUtil.h"
#endif
diff --git a/include/internal/TestCPPAssertions.h b/include/internal/TestCPPAssertions.h
new file mode 100644
index 0000000..8032643
--- /dev/null
+++ b/include/internal/TestCPPAssertions.h
@@ -0,0 +1,260 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+ */
+
+#ifndef TESTCPP_ASSERTIONS_
+#define TESTCPP_ASSERTIONS_
+
+#include
+#include
+#include
+#include
+
+#include "TestCPPExceptions.h"
+
+using std::endl;
+using std::function;
+using std::string;
+using std::stringstream;
+
+/**
+ * The base namespace for all TestCPP library code.
+ */
+namespace TestCPP {
+
+ /**
+ * @class Assertions
+ * @author Jonathan Hyry
+ * @date 11/05/24
+ * @file TestCPPAssertions.h
+ * @brief Contains the TestCPP library's assertions.
+ *
+ * Defines some assertions here, where they are templated.
+ * Declares the rest.
+ *
+ * This is the place where the TestCPP Assertions API is defined.
+ */
+ class Assertions {
+ public:
+ /**
+ * @brief Check that something equals something else using the
+ * built-in operator== for each type.
+ * @param expected The value that the actual value should be
+ * equivalent to.
+ * @param actual The actual value that will be checked against
+ * the expected value.
+ * @param failureMessage Failure message that should be logged
+ * if the assertion fails. This
+ * defaults to a generic failure
+ * message related to the assertion
+ * type.
+ */
+ template
+ static void assertEquals (
+ T1 expected, T2 actual,
+ string failureMessage = "Arguments are not equivalent!"
+ )
+ {
+ if (expected != actual) {
+ stringstream err;
+
+ err << "Equivalence assertion failed!" << endl;
+ err << failureMessage << endl;
+ err << "Expected: <" << expected << ">" << endl;
+ err << "Actual: <" << actual << ">" << endl;
+
+ throw TestFailedException(err.str());
+ }
+ }
+
+ /**
+ * @brief Check that something is not equivalent to something
+ * else using the built-in operator== for each type.
+ * @param expected The value that the actual value should not be
+ * equivalent to.
+ * @param actual The actual value that will be checked against
+ * the expected value.
+ * @param failureMessage Failure message that should be logged
+ * if the assertion fails. This
+ * defaults to a generic failure
+ * message related to the assertion
+ * type.
+ */
+ template
+ static void assertNotEquals (
+ T1 expected, T2 actual,
+ string failureMessage = "Arguments are equivalent!"
+ )
+ {
+ if (expected == actual) {
+ stringstream err;
+
+ err << "Non-Equivalence assertion failed!" << endl;
+ err << failureMessage << endl;
+ err << "Expected: <" << expected << ">" << endl;
+ err << "Actual: <" << actual << ">" << endl;
+
+ throw TestFailedException(err.str());
+ }
+ }
+
+ /**
+ * @brief Check that a pointer is null.
+ * @param ptr The pointer to check.
+ * @param failureMessage Failure message that should be logged
+ * if the assertion fails. This
+ * defaults to a generic failure
+ * message related to the assertion
+ * type.
+ */
+ template
+ static void assertNull (
+ T ptr,
+ string failureMessage = "Object is not null!"
+ )
+ {
+ bool null = ptr == nullptr;
+
+ if (!null) {
+ stringstream err;
+
+ err << "Null assertion failed!" << endl;
+ err << failureMessage << endl;
+
+ throw TestFailedException(err.str());
+ }
+ }
+
+ /**
+ * @brief Check that a pointer is non-null.
+ * @param ptr The pointer to check.
+ * @param failureMessage Failure message that should be logged
+ * if the assertion fails. This
+ * defaults to a generic failure
+ * message related to the assertion
+ * type.
+ */
+ template
+ static void assertNotNull (
+ T ptr,
+ string failureMessage = "Object is null!"
+ )
+ {
+ bool notNull = ptr != nullptr;
+
+ if (!notNull) {
+ stringstream err;
+
+ err << "Not Null assertion failed!" << endl;
+ err << failureMessage << endl;
+
+ throw TestFailedException(err.str());
+ }
+ }
+
+ /**
+ * @brief Verify that a function throws something.
+ * @param shouldThrow The function to check, to ensure that it
+ * throws something.
+ * @param failureMessage Failure message that should be logged
+ * if the assertion fails. This
+ * defaults to a generic failure
+ * message related to the assertion
+ * type.
+ */
+ static void assertThrows (
+ function shouldThrow,
+ string failureMessage =
+ "Should have thrown something!"
+ );
+
+ /**
+ * @brief Verify that a function does not throw something.
+ * @param shouldThrow The function to check, to ensure that it
+ * does not throw something.
+ * @param failureMessage Failure message that should be logged
+ * if the assertion fails. This
+ * defaults to a generic failure
+ * message related to the assertion
+ * type.
+ */
+ static void assertNoThrows (
+ function shouldNotThrow,
+ string failureMessage =
+ "Should not have thrown anything!"
+ );
+
+ /**
+ * @brief Verify that a logical condition is true.
+ * @param condition The result of the logical condition to
+ * check.
+ * @param failureMessage Failure message that should be logged
+ * if the assertion fails. This
+ * defaults to a generic failure
+ * message related to the assertion
+ * type.
+ */
+ static void assertTrue (
+ bool condition,
+ string failureMessage = "Condition is false!"
+ );
+
+ /**
+ * @brief Verify that a logical condition is not true.
+ * @param condition The result of the logical condition to
+ * check.
+ * @param failureMessage Failure message that should be logged
+ * if the assertion fails. This
+ * defaults to a generic failure
+ * message related to the assertion
+ * type.
+ */
+ static void assertFalse (
+ bool condition,
+ string failureMessage = "Condition is true!"
+ );
+
+ /**
+ * @brief Force a test to fail.
+ * @param failureMessage Failure message that should be logged
+ * if the assertion fails. This
+ * defaults to a generic failure
+ * message related to the assertion
+ * type.
+ *
+ * Useful in certain situations when:
+ * - Generating code for test cases, to ensure that skeletons
+ * fail by default.
+ * - Forcing test failure in certain circumstances where there
+ * is nothing to assert but a certain code path is taken.
+ */
+ static void fail (
+ string failureMessage = "Forced test failure!"
+ );
+ };
+}
+
+#endif
diff --git a/include/internal/TestCPPCommon.h b/include/internal/TestCPPCommon.h
new file mode 100644
index 0000000..5747719
--- /dev/null
+++ b/include/internal/TestCPPCommon.h
@@ -0,0 +1,106 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+ */
+
+#ifndef TESTCPP_COMMON_
+#define TESTCPP_COMMON_
+
+/**
+ * The base namespace for all TestCPP library code.
+ */
+namespace TestCPP {
+
+ /**
+ * @class TestCPPCommon
+ * @author Jonathan Hyry
+ * @date 03/05/24
+ * @file TestCPP.h
+ * @brief Categories of common objects that are used.
+ */
+ class TestCPPCommon {
+ public:
+ /**
+ * @class Nums
+ * @author Jonathan Hyry
+ * @date 03/05/24
+ * @file TestCPP.h
+ * @brief Common magic numbers used by the library.
+ */
+ struct Nums {
+ static constexpr const int TIME_PRECISION = 4;
+ static constexpr const double NANOS_IN_SEC = 1000000000.0;
+ };
+
+ /**
+ * @class Strings
+ * @author Jonathan Hyry
+ * @date 03/05/24
+ * @file TestCPP.h
+ * @brief Common literal strings used by the library.
+ *
+ * As struct member name prefixes and postfixes, underscores
+ * denote leading and trailing spaces, respectively.
+ */
+ struct Strings {
+ static constexpr const char * ALL_ = "All ";
+ static constexpr const char * APOS = "'";
+ static constexpr const char * _FAIL_ = " failed! ";
+ static constexpr const char * FINISHED_SUITE_ =
+ "Finished running test suite ";
+ static constexpr const char * FWSL = "/";
+ static constexpr const char * _IN_ABOUT_ = " in about ";
+ static constexpr const char * _NCONTAIN_ =
+ " does not contain ";
+ static constexpr const char * _NEQUIV_ =
+ " is not equivalent to ";
+ static constexpr const char * NTR = "No tests to run!";
+ static constexpr const char * NVTN =
+ "Not a valid test name!";
+ static constexpr const char * PARENL = "(";
+ static constexpr const char * PARENR = ")";
+ static constexpr const char * _PASS_ = " passed! ";
+ static constexpr const char * _PASSED = " passed";
+ static constexpr const char * REASON_ = "Reason: ";
+ static constexpr const char * SEC = "s";
+ static constexpr const char * SP = " ";
+ static constexpr const char * START_RUN =
+ "Starting run of test ";
+ static constexpr const char * SUITE_ = "Suite ";
+ static constexpr const char * _SUITE_TESTS_PASSED =
+ " suite tests passed!";
+ static constexpr const char * TEST_ = "Test ";
+ static constexpr const char * TEST_EXC_ =
+ "Exception occurred during test run: ";
+ static constexpr const char * UNK_CMP_OPT_ =
+ "Unknown comparison option! ";
+ static constexpr const char * UNK_EXC =
+ "Unknown error occurred in test!";
+ static constexpr const char * UNK_OPT_ = "Unknown option ";
+ };
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/include/internal/TestCPPExceptions.h b/include/internal/TestCPPExceptions.h
new file mode 100644
index 0000000..ec4bb64
--- /dev/null
+++ b/include/internal/TestCPPExceptions.h
@@ -0,0 +1,102 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+ */
+
+#ifndef TESTCPP_EXCEPTIONS_
+#define TESTCPP_EXCEPTIONS_
+
+#include
+#include
+
+using std::runtime_error;
+using std::string;
+
+/**
+ * The base namespace for all TestCPP library code.
+ */
+namespace TestCPP {
+
+ /**
+ * @class TestCPPException
+ * @author Jonathan Hyry
+ * @date 03/05/24
+ * @file TestCPP.h
+ * @brief Provides a custom base exception for failure conditions.
+ *
+ * There are two types of failures in the library:
+ * - Errors caused by bugs, system problems, etc.
+ * - Test failures
+ *
+ * This type, when used directly, is for representing the first type
+ * of failure: errors in the library caused by bugs or other
+ * problems.
+ */
+ class TestCPPException : public runtime_error {
+ public:
+ /**
+ * Construct an exception of this type with a string literal for
+ * its failure message.
+ */
+ TestCPPException (const char * msg);
+
+ /**
+ * Construct an exception of this type with a string object for
+ * its failure message.
+ */
+ TestCPPException (string&& msg);
+ };
+
+ /**
+ * @class TestFailedException
+ * @author Jonathan Hyry
+ * @date 03/05/24
+ * @file TestCPP.h
+ * @brief Provides an exception type specifically for test failures.
+ *
+ * There are two types of failures in the library:
+ * - Errors caused by bugs, system problems, etc.
+ * - Test failures
+ *
+ * This type is for representing the second type of failure: test
+ * failures.
+ */
+ class TestFailedException : public TestCPPException {
+ public:
+ /**
+ * Construct an exception of this type with a string literal for
+ * its failure message.
+ */
+ TestFailedException (const char * msg);
+
+ /**
+ * Construct an exception of this type with a string literal for
+ * its failure message.
+ */
+ TestFailedException (string&& msg);
+ };
+}
+
+#endif
diff --git a/include/internal/TestCPPTestCase.h b/include/internal/TestCPPTestCase.h
new file mode 100644
index 0000000..0d39113
--- /dev/null
+++ b/include/internal/TestCPPTestCase.h
@@ -0,0 +1,356 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+ */
+
+//Original author: Jonathan Hyry CSU-Fullerton SECS 6896-02 Fall 2014/15
+
+#ifndef TESTCPP_TESTCASE_TYPE_
+#define TESTCPP_TESTCASE_TYPE_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "TestCPPUtil.h"
+
+using std::atomic_int;
+using std::chrono::nanoseconds;
+using std::chrono::system_clock;
+using std::chrono::duration_cast;
+using std::enable_if;
+using std::endl;
+using std::forward;
+using std::function;
+using std::move;
+using std::ostream;
+using std::runtime_error;
+using std::string;
+using std::streambuf;
+using std::stringstream;
+using std::unique_ptr;
+using std::vector;
+
+/**
+ * The base namespace for all TestCPP library code.
+ */
+namespace TestCPP {
+
+ /**
+ * @class TestCase
+ * @author Jonathan Hyry
+ * @date 03/05/24
+ * @file TestCPP.h
+ * @brief Provides an interface for implementing tests.
+ *
+ * TestCase is one of three core types that define the TestCPP API.
+ * TestCase provides faculties for defining and controlling tests
+ * and the stdout/stderr/clog environment.
+ * TestCase objects are defined by the following elements:
+ * - their name
+ * - the code that will be run that defines what the test does
+ * and how it does it
+ * - whether to log test passage
+ * - output capturing flags
+ * - how captured output is analyzed (exact or contains)
+ */
+ class TestCase {
+
+ public:
+ /**
+ * Provides a specification for how to analyze captured stdout,
+ * stderr, and clog data.
+ */
+ enum TestCaseOutCompareOptions {
+ CONTAINS,
+ EXACT
+ };
+
+ /**
+ * @brief Construct a test case with possibility to use all
+ * options.
+ *
+ * @param testName The name of the test as a TestObjName.
+ * @param test The implementation or pointer to the test.
+ * @param testPassedMessage Whether to omit a message indicating
+ * test passage.
+ * @param captureOut Whether to capture stdout output for
+ * analysis after the test run.
+ * @param captureLog Whether to capture clog output for
+ * analysis after the test run.
+ * @param captureErr Whether to capture stderr output for
+ * analysis after the test run.
+ * @param opt Technique for comparing actual output with
+ * expected output after the test run.
+ *
+ * Instantiate and define a test case.
+ * All parameters are optional other than the test name and the
+ * test function.
+ * Optional parameters have the following default values:
+ * - testPassedMessage defaults to true, so by default test
+ * cases will emit a message when the pass.
+ * - captureOut defaults to false, the test case by default
+ * does not capture stdout data.
+ * - captureLog defaults to false, the test case by default
+ * does not capture data streamed to clog.
+ * - captureErr defaults to false, the test case by default
+ * does not capture stderr data.
+ * - opt defaults to CONTAINS, so when output is captured and
+ * checked for something, the default technique is to check
+ * that the output of a given stream contains a given
+ * string.
+ */
+ TestCase (
+ TestObjName&& testName,
+ function test,
+ bool testPassedMessage = true,
+ bool captureOut = false,
+ bool captureLog = false,
+ bool captureErr = false,
+ TestCaseOutCompareOptions opt = CONTAINS
+ );
+
+ /**
+ * @brief Construct a TestCase by copying it from another
+ * TestCase.
+ * @param o The test case from which to make a copy.
+ */
+ TestCase (TestCase& o);
+
+ /**
+ * @brief Construct a TestCase by moving all data from another
+ * TestCase.
+ * @param o Move everything from this TestCase into the new one.
+ */
+ TestCase (TestCase&& o);
+
+ /**
+ * @brief Copy a TestCase into another TestCase.
+ * @param rhs The test case to copy from.
+ * @return A reference to the new TestCase copy.
+ */
+ TestCase& operator= (TestCase& rhs);
+
+ /**
+ * @brief Move a TestCase into another TestCase.
+ * @param rhs Move everything from this TestCase into the new
+ * one.
+ * @return A reference to the TestCase that everything from the
+ * old TestCase was moved into.
+ */
+ TestCase& operator= (TestCase&& rhs);
+
+ /**
+ * @brief Destroy a TestCase object.
+ */
+ ~TestCase ();
+
+ /**
+ * @brief Set whether to notify in std::clog when a test passes.
+ * @param shouldNotify Will notify on test passed if true.
+ */
+ void setNotifyPassed (bool shouldNotify);
+
+ /**
+ * @brief Set the output comparison mode.
+ * @param opt Accepts specified mode from the referenced enum.
+ *
+ * If this is called with an option specified that is different
+ * from what is set for this test case already, subsequent
+ * calls to any of the output check functions will use the
+ * updated mode that is specified in the call to this function.
+ */
+ void outCompareOption (TestCaseOutCompareOptions opt);
+
+ /**
+ * @brief Clears the captured output from stdout.
+ *
+ * This can be used for checking sections of output based on
+ * test configuration.
+ */
+ void clearStdoutCapture ();
+
+ /**
+ * @brief Clears the captured output from std::clog.
+ *
+ * This can be used for checking sections of output based on
+ * test configuration.
+ */
+ void clearLogCapture ();
+
+ /**
+ * @brief Clears the captured output from stderr.
+ *
+ * This can be used for checking sections of output based on
+ * test configuration.
+ */
+ void clearStderrCapture ();
+
+ /**
+ * @brief Check the argument against what is captured from
+ * stdout using the configured comparison mode.
+ * @param against The value to check the captured output against
+ * @return True if the argument results in a successful check
+ * using the configured comparison mode.
+ */
+ bool checkStdout (string against);
+
+ /**
+ * @brief Check the argument against what is captured from
+ * std::clog using the configured comparison mode.
+ * @param against The value to check the captured output against
+ * @return True if the argument results in a successful check
+ * using the configured comparison mode.
+ */
+ bool checkLog (string against);
+
+ /**
+ * @brief Check the argument against what is captured from
+ * stderr using the configured comparison mode.
+ * @param against The value to check the captured output against
+ * @return True if the argument results in a successful check
+ * using the configured comparison mode, false
+ * otherwise.
+ */
+ bool checkStderr (string against);
+
+ /**
+ * @brief Run the test case.
+ * @return True if the test ran successfully, false otherwise.
+ */
+ bool go ();
+
+ /**
+ * @brief Returns the duration of the last run in nanoseconds.
+ * @return The duration of the last run of this TestCase in
+ * nanoseconds.
+ */
+ long long getLastRuntime ();
+
+ private:
+ bool notifyTestPassed = false;
+ bool pass = false;
+ bool stdoutCaptured = false;
+ bool clogCaptured = false;
+ bool stderrCaptured = false;
+ long long lastRunTime = -1;
+
+ TestObjName testName;
+ function test;
+
+ TestCaseOutCompareOptions option =
+ TestCaseOutCompareOptions::CONTAINS;
+
+ /**
+ * @brief If instructed, capture stdout while this test is
+ * active.
+ */
+ void captureStdout ();
+ /**
+ * @brief If instructed, capture clog while this test is
+ * active.
+ */
+ void captureClog ();
+ /**
+ * @brief If instructed, capture stderr while this test is
+ * active.
+ */
+ void captureStdErr ();
+ /**
+ * @brief Write a test failure reason to the specified stream.
+ * @param out The stream to write the test failure reason to.
+ * @param reason The test failure reason to write.
+ */
+ void logFailure (ostream& out, string& reason);
+ /**
+ * @brief If a test encounters an error while running, this
+ * function will be called to log the test error.
+ * @param failureMessage The error message from the test that
+ * should be logged.
+ */
+ void logTestFailure (string failureMessage);
+ /**
+ * @brief Internal test run controller.
+ */
+ void runTest ();
+ /**
+ * @brief Handles the internal logic for calls to checkStdout,
+ * checkLog, and checkStderr based on the selected
+ * comparison technique for this test.
+ * @param source The actual output
+ * @param against The expected output, or a portion of the
+ * expected output.
+ * @return True if the argument results in a successful check
+ * using the configured comparison mode, false
+ * otherwise.
+ */
+ bool checkOutput (string source, string against);
+
+ static atomic_int stdoutCaptureCasesConstructed;
+ static atomic_int logCaptureCasesConstructed;
+ static atomic_int stderrCaptureCasesConstructed;
+ static atomic_int stdoutCaptureCasesDestroyed;
+ static atomic_int logCaptureCasesDestroyed;
+ static atomic_int stderrCaptureCasesDestroyed;
+
+ static unique_ptr
+ stdoutBuffer;
+ static unique_ptr
+ clogBuffer;
+ static unique_ptr
+ stderrBuffer;
+ static unique_ptr
+ stdoutOriginal;
+ static unique_ptr
+ clogOriginal;
+ static unique_ptr
+ stderrOriginal;
+
+ /**
+ * @brief Measure the duration of a function when run.
+ * @param func Measure the duration of this function when run.
+ * @param args A template pack of arguments to apply to the
+ * given function of which to measure the duration.
+ * @return The duration of the function run, in nanoseconds.
+ */
+ template
+ static nanoseconds duration (F func, Args&&... args)
+ {
+ auto start = system_clock::now();
+ func(forward(args)...);
+ return duration_cast(
+ system_clock::now() - start
+ );
+ }
+ };
+}
+
+#endif
diff --git a/include/internal/TestCPPTestSuite.h b/include/internal/TestCPPTestSuite.h
new file mode 100644
index 0000000..e47717b
--- /dev/null
+++ b/include/internal/TestCPPTestSuite.h
@@ -0,0 +1,173 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+ */
+
+//Original author: Jonathan Hyry CSU-Fullerton SECS 6896-02 Fall 2014/15
+
+#ifndef TESTCPP_TESTSUITE_TYPE_
+#define TESTCPP_TESTSUITE_TYPE_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using std::atomic_int;
+using std::chrono::nanoseconds;
+using std::chrono::system_clock;
+using std::chrono::duration_cast;
+using std::enable_if;
+using std::endl;
+using std::forward;
+using std::function;
+using std::move;
+using std::runtime_error;
+using std::string;
+using std::streambuf;
+using std::stringstream;
+using std::unique_ptr;
+using std::vector;
+
+/**
+ * The base namespace for all TestCPP library code.
+ */
+namespace TestCPP {
+
+ /**
+ * @class TestSuite
+ * @author Jonathan Hyry
+ * @date 05/05/24
+ * @file TestCPP.h
+ * @brief Defines a container for a collection of TestCase objects.
+ *
+ *
+ */
+ class TestSuite {
+
+ public:
+ /**
+ * @brief Specialization for constructing a test suite with no
+ * tests.
+ */
+ template
+ TestSuite (TestObjName&& suiteName,
+ typename enable_if::type)
+ {
+ this->testSuitePassedMessage = true;
+ this->setSuiteName(move(suiteName));
+ this->tests = vector();
+ }
+
+ /**
+ * @brief The general case for constructing a test suite with
+ * tests.
+ */
+ template
+ TestSuite (TestObjName&& suiteName, TestType ...tests) {
+ this->testSuitePassedMessage = true;
+ this->setSuiteName(move(suiteName));
+ this->tests = vector();
+
+ this->addTests(tests...);
+ }
+
+ /**
+ * @brief Add a test to this test suite.
+ *
+ * Appropriate specializations defined in the source file.
+ */
+ template
+ void addTest (T&& test);
+
+ /**
+ * @brief Specialization to handle when someone tries to call
+ * the template function with no tests.
+ *
+ * This is a noop.
+ */
+ template
+ typename enable_if::type
+ inline addTests () { }
+
+ /**
+ * @brief Add one or more tests at once to the test suite.
+ * @param test The first test to add.
+ * @param tests The rest of the tests to add.
+ */
+ template
+ void addTests (Test test, OtherTests ...tests) {
+ addTest(move(test));
+ addTests(tests...);
+ }
+
+ /**
+ * @brief Sets the name of this test suite.
+ */
+ void setSuiteName (TestObjName&& testSuiteName);
+
+ /**
+ * @brief After called, all tests in the suite will emit a
+ * message if they pass.
+ */
+ void enableTestPassedMessage ();
+
+ /**
+ * @brief After called, all tests in the suite will not emit a
+ * message if they pass.
+ */
+ void disableTestPassedMessage ();
+
+ /**
+ * @brief Calculate the total number of tests in the suite that
+ * failed after the last suite run.
+ * @return The total number of tests that failed in the last
+ * suite run.
+ */
+ unsigned getLastRunFailCount ();
+
+ /**
+ * @brief Run all tests in the test suite.
+ */
+ void run ();
+
+ private:
+ bool testSuitePassedMessage;
+ bool lastRunSucceeded;
+ unsigned lastRunSuccessCount;
+ unsigned lastRunFailCount;
+ unsigned long long totalRuntime;
+
+ TestObjName suiteName;
+ vector tests;
+ };
+}
+
+#endif
diff --git a/include/internal/TestCPPUtil.h b/include/internal/TestCPPUtil.h
new file mode 100644
index 0000000..045def4
--- /dev/null
+++ b/include/internal/TestCPPUtil.h
@@ -0,0 +1,133 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+ */
+
+#ifndef TESTCPP_UTIL_
+#define TESTCPP_UTIL_
+
+#include
+
+using std::string;
+
+/**
+ * The base namespace for all TestCPP library code.
+ */
+namespace TestCPP {
+
+ /**
+ * @class TestObjName
+ * @author Jonathan Hyry
+ * @date 03/05/24
+ * @file TestCPP.h
+ * @brief Provides a null-safe name for test objects.
+ *
+ * Both TestCase and TestSuite objects use this for their name to
+ * protect against nullptr/NULL being used to create a std::string
+ * and name the test with that non-null-char-*-based std::string.
+ */
+ class TestObjName {
+ public:
+ /**
+ * @brief Construct an empty Test Object Name object.
+ * @return The empty TestObjName.
+ *
+ * This should never be called by user code but is required for
+ * the code to compile.
+ */
+ TestObjName () = default;
+
+ /**
+ * @brief Construct a Test Object Name object with a string
+ * literal or existing const char *.
+ * @return The TestObjName, where it is verified that the name
+ * used to construct it was not null.
+ */
+ TestObjName (const char* name);
+
+ /**
+ * @brief Get the encapsulated name for the TestCPP object that
+ * holds this object.
+ * @return The name of the TestCPP object that this object
+ * names.
+ */
+ const string& getName ();
+
+ /**
+ * @brief Output the test object name to the specified stream.
+ * @param s The stream to output to.
+ * @param tcName The test object name object.
+ * @return The stream for chaining.
+ */
+ friend std::ostream& operator<< (
+ std::ostream& s,
+ TestObjName& tcName
+ );
+
+ private:
+ string testCaseName;
+ };
+
+ /**
+ * The namespace for general TestCPP utility code.
+ */
+ namespace Util {
+
+ /**
+ * @brief Log a message that will only be output when debug
+ * logging is enabled.
+ *
+ * @param message The debug message to log.
+ * @param omitNewline Whether or not to end the log with a
+ * newline character.
+ * Defaults to false.
+ */
+ void debugLog (const string& message, bool omitNewline = false);
+
+ /**
+ * @brief Check if a std::string is contained within another
+ * std::string; this exists since we're not using C++26
+ * here.
+ *
+ * @param source The base std::string to check within.
+ * @param contains Checks the base std::string to see if it
+ * contains this std::string.
+ *
+ * @return True if source includes contains in whole, false
+ * otherwise.
+ */
+ bool stringContains (const string& source,
+ const string& contains);
+
+ /**
+ * @brief Safely converts unsigned integer values to signed.
+ * @param toCast The unsigned value to convert.
+ * @return The signed value equivalent to the unsigned value.
+ */
+ int unsignedToSigned(unsigned toCast);
+ }
+}
+
+#endif
diff --git a/src/TestCPPAssertions.cpp b/src/TestCPPAssertions.cpp
new file mode 100644
index 0000000..f689234
--- /dev/null
+++ b/src/TestCPPAssertions.cpp
@@ -0,0 +1,124 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+ */
+
+#include "internal/TestCPPAssertions.h"
+
+using std::clog;
+using std::current_exception;
+using std::endl;
+using std::exception;
+using std::exception_ptr;
+using std::function;
+using std::move;
+using std::rethrow_exception;
+using std::string;
+using std::stringstream;
+
+namespace TestCPP {
+
+ void Assertions::assertThrows (
+ function shouldThrow,
+ string failureMessage
+ )
+ {
+ try {
+ shouldThrow();
+ }
+ catch (...) {
+ exception_ptr eptr = current_exception();
+
+ if (eptr) {
+ try {
+ rethrow_exception(eptr);
+ }
+ catch (const exception& e) {
+ clog << "assertThrows caught exception: "
+ << TestFailedException(e.what()).what()
+ << endl;
+ }
+ }
+ else {
+ clog << "Something was thrown, not sure what." << endl
+ << "This satisfies the assertion, so no failure is"
+ << " present. "
+ << TestFailedException("Unknown thrown object").
+ what();
+ }
+
+ return;
+ }
+
+ throw TestFailedException(move(failureMessage));
+ }
+
+ void Assertions::assertNoThrows (
+ function shouldNotThrow,
+ string failureMessage
+ )
+ {
+ try {
+ shouldNotThrow();
+ }
+ catch (...) {
+ throw TestFailedException(move(failureMessage));
+ }
+ }
+
+ void Assertions::assertTrue (
+ bool condition,
+ string failureMessage
+ )
+ {
+ if (!condition) {
+ stringstream err;
+
+ err << "Boolean Truth assertion failed!" << endl;
+ err << failureMessage << endl;
+
+ throw TestFailedException(err.str());
+ }
+ }
+
+ void Assertions::assertFalse (
+ bool condition,
+ string failureMessage
+ )
+ {
+ if (condition) {
+ stringstream err;
+
+ err << "Boolean False assertion failed!" << endl;
+ err << failureMessage << endl;
+
+ throw TestFailedException(err.str());
+ }
+ }
+
+ void Assertions::fail(string failureMessage) {
+ throw TestFailedException(move(failureMessage));
+ }
+}
\ No newline at end of file
diff --git a/include/TestCPPUtil.h b/src/TestCPPExceptions.cpp
similarity index 56%
rename from include/TestCPPUtil.h
rename to src/TestCPPExceptions.cpp
index cae25cf..78d6ae0 100644
--- a/include/TestCPPUtil.h
+++ b/src/TestCPPExceptions.cpp
@@ -25,20 +25,49 @@ OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to
*/
-#ifndef TESTCPP_UTIL_
-#define TESTCPP_UTIL_
+#include
-#include
+#include "internal/TestCPPExceptions.h"
+#ifdef TESTCPP_STACKTRACE_ENABLED
+#include
+#endif
+
+using std::clog;
+using std::move;
using std::string;
+using std::runtime_error;
namespace TestCPP {
- namespace Util {
- void debugLog (const string& message, bool omitNewline = false);
- bool stringContains (const string& source,
- const string& contains);
- int unsignedToSigned(unsigned toCast);
+
+ TestCPPException::TestCPPException (const char * msg) :
+ runtime_error(msg)
+ {
+#ifdef TESTCPP_STACKTRACE_ENABLED
+ clog << boost::stacktrace::stacktrace();
+#endif
+ }
+ TestCPPException::TestCPPException (string&& msg) :
+ runtime_error(move(msg))
+ {
+#ifdef TESTCPP_STACKTRACE_ENABLED
+ clog << boost::stacktrace::stacktrace();
+#endif
}
-}
+ TestFailedException::TestFailedException (const char * msg) :
+ TestCPPException(msg)
+ {
+#ifdef TESTCPP_STACKTRACE_ENABLED
+ clog << boost::stacktrace::stacktrace();
#endif
+ }
+
+ TestFailedException::TestFailedException (string&& msg) :
+ TestCPPException(move(msg))
+ {
+#ifdef TESTCPP_STACKTRACE_ENABLED
+ clog << boost::stacktrace::stacktrace();
+#endif
+ }
+}
diff --git a/src/TestCPP.cpp b/src/TestCPPTestCase.cpp
similarity index 53%
rename from src/TestCPP.cpp
rename to src/TestCPPTestCase.cpp
index aa287e5..475aae4 100644
--- a/src/TestCPP.cpp
+++ b/src/TestCPPTestCase.cpp
@@ -27,7 +27,9 @@ For more information, please refer to
//Original author: Jonathan Hyry CSU-Fullerton SECS 6896-02 Fall 2014/15
-#include "TestCPP.h"
+#include "internal/TestCPPCommon.h"
+#include "internal/TestCPPExceptions.h"
+#include "internal/TestCPPTestCase.h"
#ifdef TESTCPP_STACKTRACE_ENABLED
#include
@@ -35,71 +37,28 @@ For more information, please refer to
#include
-#include "TestCPPUtil.h"
+#include "internal/TestCPPUtil.h"
using TestCPP::Util::debugLog;
using std::cerr;
using std::clog;
using std::cout;
-using std::current_exception;
using std::endl;
using std::exception;
-using std::exception_ptr;
using std::fixed;
using std::function;
using std::invalid_argument;
using std::move;
-using std::ostream;
using std::rethrow_exception;
using std::runtime_error;
using std::setprecision;
using std::string;
using std::tuple;
-namespace TestCPP {
- TestCPPException::TestCPPException (const char * msg) :
- runtime_error(msg)
- {
-#ifdef TESTCPP_STACKTRACE_ENABLED
- clog << boost::stacktrace::stacktrace();
-#endif
- }
- TestCPPException::TestCPPException (string&& msg) :
- runtime_error(move(msg))
- {
-#ifdef TESTCPP_STACKTRACE_ENABLED
- clog << boost::stacktrace::stacktrace();
-#endif
- }
-
- TestFailedException::TestFailedException (const char * msg) :
- TestCPPException(msg)
- {
-#ifdef TESTCPP_STACKTRACE_ENABLED
- clog << boost::stacktrace::stacktrace();
-#endif
- }
-
- TestFailedException::TestFailedException (string&& msg) :
- TestCPPException(move(msg))
- {
-#ifdef TESTCPP_STACKTRACE_ENABLED
- clog << boost::stacktrace::stacktrace();
-#endif
- }
+using TCPPNum = TestCPP::TestCPPCommon::Nums;
+using TCPPStr = TestCPP::TestCPPCommon::Strings;
- TestCaseName::TestCaseName (const char* name) {
- if (name) {
- this->testCaseName = name;
- }
- else {
- throw TestCPPException("Not a valid test name!");
- }
- }
-
- const string& TestCaseName::getTestName () {
- return this->testCaseName;
- }
+namespace TestCPP {
atomic_int TestCase::stdoutCaptureCasesConstructed;
atomic_int TestCase::logCaptureCasesConstructed;
@@ -108,14 +67,38 @@ namespace TestCPP {
atomic_int TestCase::logCaptureCasesDestroyed;
atomic_int TestCase::stderrCaptureCasesDestroyed;
- unique_ptr TestCase::stdoutBuffer = nullptr;
- unique_ptr TestCase::clogBuffer = nullptr;
- unique_ptr TestCase::stderrBuffer = nullptr;
- unique_ptr TestCase::stdoutOriginal = nullptr;
- unique_ptr TestCase::clogOriginal = nullptr;
- unique_ptr TestCase::stderrOriginal = nullptr;
-
- TestCase::TestCase (TestCaseName&& name,
+ unique_ptr
+ TestCase::stdoutBuffer =
+ unique_ptr(
+ nullptr, [](stringstream*){}
+ );
+ unique_ptr
+ TestCase::clogBuffer =
+ unique_ptr(
+ nullptr, [](stringstream*){}
+ );
+ unique_ptr
+ TestCase::stderrBuffer =
+ unique_ptr(
+ nullptr, [](stringstream*){}
+ );
+ unique_ptr
+ TestCase::stdoutOriginal =
+ unique_ptr(
+ nullptr, [](streambuf*){}
+ );
+ unique_ptr
+ TestCase::clogOriginal =
+ unique_ptr(
+ nullptr, [](streambuf*){}
+ );
+ unique_ptr
+ TestCase::stderrOriginal =
+ unique_ptr(
+ nullptr, [](streambuf*){}
+ );
+
+ TestCase::TestCase (TestObjName&& name,
function test,
bool msg,
bool captureOut, bool captureLog,
@@ -131,12 +114,18 @@ namespace TestCPP {
captureStdout();
}
if (captureLog) {
+ debugLog("CaptureLog windows segfault check - clog cap");
captureClog();
+ debugLog("CaptureLog windows segfault check - clog end");
}
if (captureErr) {
captureStdErr();
}
+ this->stdoutCaptured = captureOut;
+ this->clogCaptured = captureLog;
+ this->stderrCaptured = captureErr;
+
this->option = opt;
}
@@ -147,6 +136,20 @@ namespace TestCPP {
this->pass = o.pass;
this->lastRunTime = o.lastRunTime;
+ this->stdoutCaptured = o.stdoutCaptured;
+ this->clogCaptured = o.clogCaptured;
+ this->stderrCaptured = o.stderrCaptured;
+
+ if (this->stdoutCaptured) {
+ captureStdout();
+ }
+ if (this->clogCaptured) {
+ captureClog();
+ }
+ if (this->stderrCaptured) {
+ captureStdErr();
+ }
+
this->testName = o.testName;
this->test = o.test;
}
@@ -158,6 +161,20 @@ namespace TestCPP {
this->pass = move(o.pass);
this->lastRunTime = move(o.lastRunTime);
+ this->stdoutCaptured = move(o.stdoutCaptured);
+ this->clogCaptured = move(o.clogCaptured);
+ this->stderrCaptured = move(o.stderrCaptured);
+
+ if (this->stdoutCaptured) {
+ captureStdout();
+ }
+ if (this->clogCaptured) {
+ captureClog();
+ }
+ if (this->stderrCaptured) {
+ captureStdErr();
+ }
+
this->testName = move(o.testName);
this->test = move(o.test);
}
@@ -169,6 +186,7 @@ namespace TestCPP {
TestCase::stdoutCaptureCasesConstructed - 1)
{
cout.rdbuf(TestCase::stdoutOriginal.release());
+ delete TestCase::stdoutBuffer.get();
TestCase::stdoutBuffer = nullptr;
}
@@ -180,6 +198,7 @@ namespace TestCPP {
TestCase::logCaptureCasesConstructed - 1)
{
clog.rdbuf(TestCase::clogOriginal.release());
+ delete TestCase::clogBuffer.get();
TestCase::clogBuffer = nullptr;
}
@@ -191,6 +210,7 @@ namespace TestCPP {
TestCase::stderrCaptureCasesConstructed - 1)
{
cerr.rdbuf(TestCase::stderrOriginal.release());
+ delete TestCase::stderrBuffer.get();
TestCase::stderrBuffer = nullptr;
}
@@ -205,6 +225,20 @@ namespace TestCPP {
this->pass = rhs.pass;
this->lastRunTime = rhs.lastRunTime;
+ this->stdoutCaptured = rhs.stdoutCaptured;
+ this->clogCaptured = rhs.clogCaptured;
+ this->stderrCaptured = rhs.stderrCaptured;
+
+ if (this->stdoutCaptured) {
+ captureStdout();
+ }
+ if (this->clogCaptured) {
+ captureClog();
+ }
+ if (this->stderrCaptured) {
+ captureStdErr();
+ }
+
this->testName = rhs.testName;
this->test = rhs.test;
@@ -218,6 +252,20 @@ namespace TestCPP {
this->pass = move(rhs.pass);
this->lastRunTime = move(rhs.lastRunTime);
+ this->stdoutCaptured = move(rhs.stdoutCaptured);
+ this->clogCaptured = move(rhs.clogCaptured);
+ this->stderrCaptured = move(rhs.stderrCaptured);
+
+ if (this->stdoutCaptured) {
+ captureStdout();
+ }
+ if (this->clogCaptured) {
+ captureClog();
+ }
+ if (this->stderrCaptured) {
+ captureStdErr();
+ }
+
this->testName = move(rhs.testName);
this->test = move(rhs.test);
@@ -228,24 +276,56 @@ namespace TestCPP {
return this->lastRunTime;
}
+ void TestCase::logFailure(ostream& out, string& reason) {
+ out << fixed;
+ out << setprecision(TCPPNum::TIME_PRECISION);
+ out << TCPPStr::TEST_ << this->testName << TCPPStr::_FAIL_
+ << TCPPStr::PARENL
+ << static_cast(this->lastRunTime)/
+ TCPPNum::NANOS_IN_SEC
+ << TCPPStr::SEC << TCPPStr::PARENR
+ << endl;
+ out << TCPPStr::REASON_ << reason << endl;
+ }
+
void TestCase::logTestFailure (string reason) {
- clog << fixed;
- clog << setprecision(4);
- clog << "Test " << this->testName << " failed! ("
- << static_cast(this->lastRunTime)
- /1000000000.0 << "s)" << endl;
- clog << "Reason: " << reason << endl;
+ unique_ptr logStream = nullptr;
+
+ if (this->clogOriginal != nullptr) {
+ logStream = unique_ptr(
+ new ostream(this->clogOriginal.get())
+ );
+ }
+ else {
+ logStream = unique_ptr(&clog);
+ }
+
+ logFailure(*logStream, reason);
+
+ if (this->clogOriginal != nullptr) {
+ logStream->flush();
+
+ // If someone is looking for something in the message,
+ // and it's captured, make sure it's there.
+ logFailure(clog, reason);
+ }
+
+ logStream.release();
+ logStream.reset();
}
void TestCase::runTest () {
- clog << "Starting run of test " << this->testName << endl;
+ clog << TCPPStr::START_RUN << this->testName << endl;
this->lastRunTime = duration(this->test).count();
if (this->notifyTestPassed) {
clog << fixed;
- clog << setprecision(4);
- clog << "Test " << this->testName << " passed! ("
- << static_cast(this->lastRunTime)
- /1000000000.0 << "s)" << endl;
+ clog << setprecision(TCPPNum::TIME_PRECISION);
+ clog << TCPPStr::TEST_ << this->testName << TCPPStr::_PASS_
+ << TCPPStr::PARENL
+ << static_cast(this->lastRunTime)/
+ TCPPNum::NANOS_IN_SEC
+ << TCPPStr::SEC << TCPPStr::PARENR
+ << endl;
}
this->pass = true;
}
@@ -259,7 +339,7 @@ namespace TestCPP {
this->pass = false;
logTestFailure(errorMessage);
}
- catch(string errorMessage) {
+ catch(string& errorMessage) {
this->pass = false;
logTestFailure(errorMessage);
}
@@ -269,7 +349,7 @@ namespace TestCPP {
}
catch (...) {
this->pass = false;
- logTestFailure("Unknown error occurred in test!");
+ logTestFailure(TCPPStr::UNK_EXC);
}
return false;
@@ -284,12 +364,14 @@ namespace TestCPP {
TestCase::stdoutCaptureCasesDestroyed)
{
TestCase::stdoutCaptureCasesConstructed += 1;
- TestCase::stdoutBuffer = unique_ptr(
- new stringstream()
- );
- TestCase::stdoutOriginal = unique_ptr(
- cout.rdbuf()
- );
+ TestCase::stdoutBuffer =
+ unique_ptr(
+ new stringstream(), [](stringstream *) {}
+ );
+ TestCase::stdoutOriginal =
+ unique_ptr(
+ cout.rdbuf(), [](streambuf *) {}
+ );
cout.rdbuf(TestCase::stdoutBuffer->rdbuf());
}
else {
@@ -302,12 +384,14 @@ namespace TestCPP {
TestCase::logCaptureCasesDestroyed)
{
TestCase::logCaptureCasesConstructed += 1;
- TestCase::clogBuffer = unique_ptr(
- new stringstream()
- );
- TestCase::clogOriginal = unique_ptr(
- clog.rdbuf()
- );
+ TestCase::clogBuffer =
+ unique_ptr(
+ new stringstream(), [](stringstream *) {}
+ );
+ TestCase::clogOriginal =
+ unique_ptr(
+ clog.rdbuf(), [](streambuf *) {}
+ );
clog.rdbuf(TestCase::clogBuffer->rdbuf());
}
else {
@@ -320,12 +404,14 @@ namespace TestCPP {
TestCase::stderrCaptureCasesDestroyed)
{
TestCase::stderrCaptureCasesConstructed += 1;
- TestCase::stderrBuffer = unique_ptr(
- new stringstream()
- );
- TestCase::stderrOriginal = unique_ptr(
- cerr.rdbuf()
- );
+ TestCase::stderrBuffer =
+ unique_ptr(
+ new stringstream(), [](stringstream *) {}
+ );
+ TestCase::stderrOriginal =
+ unique_ptr(
+ cerr.rdbuf(), [](streambuf *) {}
+ );
cerr.rdbuf(TestCase::stderrBuffer->rdbuf());
}
else {
@@ -342,7 +428,7 @@ namespace TestCPP {
default:
stringstream error;
- error << "Unknown option " << opt;
+ error << TCPPStr::UNK_OPT_ << opt;
throw TestCPPException(error.str());
}
}
@@ -366,32 +452,32 @@ namespace TestCPP {
}
bool TestCase::checkStdout (string against) {
- return checkOutput(this->option, TestCase::stdoutBuffer->str(),
+ return checkOutput(TestCase::stdoutBuffer->str(),
against);
}
bool TestCase::checkLog (string against) {
- return checkOutput(this->option, TestCase::clogBuffer->str(),
+ return checkOutput(TestCase::clogBuffer->str(),
against);
}
bool TestCase::checkStderr (string against) {
- return checkOutput(this->option, TestCase::stderrBuffer->str(),
+ return checkOutput(TestCase::stderrBuffer->str(),
against);
}
- bool TestCase::checkOutput (TestCase::TestCaseOutCompareOptions opt,
- string source, string against)
+ bool TestCase::checkOutput (string source, string against)
{
- switch (opt) {
+ switch (this->option) {
case EXACT:
if (source == against) {
return true;
}
else {
stringstream nomatch;
- nomatch << "'" << source << "' is not equivalent to '";
- nomatch << against << "'";
+ nomatch << TCPPStr::APOS << source << TCPPStr::APOS;
+ nomatch << TCPPStr::_NEQUIV_ << TCPPStr::APOS;
+ nomatch << against << TCPPStr::APOS;
if (this->clogOriginal != nullptr) {
ostream tmp(this->clogOriginal.get());
@@ -411,8 +497,9 @@ namespace TestCPP {
}
else {
stringstream nomatch;
- nomatch << "'" << source << "' does not contain '";
- nomatch << against << "'";
+ nomatch << TCPPStr::APOS << source << TCPPStr::APOS;
+ nomatch << TCPPStr::_NCONTAIN_ << TCPPStr::APOS;
+ nomatch << against << TCPPStr::APOS;
if (this->clogOriginal != nullptr) {
ostream tmp(this->clogOriginal.get());
@@ -428,185 +515,8 @@ namespace TestCPP {
default:
stringstream re;
- re << "Unknown comparison option! " << opt;
+ re << TCPPStr::UNK_CMP_OPT_ << this->option;
throw TestCPPException(re.str());
}
}
-
- void TestSuite::enableTestPassedMessage () {
- this->testPassedMessage = true;
- for (TestCase test : this->tests) {
- test.setNotifyPassed(true);
- }
- }
- void TestSuite::disableTestPassedMessage () {
- this->testPassedMessage = false;
- for (TestCase test : this->tests) {
- test.setNotifyPassed(false);
- }
- }
-
- void TestSuite::setSuiteName (string testSuiteName) {
- if (testSuiteName.data()) {
- this->suiteName = testSuiteName;
- }
- else {
- stringstream e;
- e << "An invalid string was passed as the Test Suite Name!";
- throw TestCPPException(e.str());
- }
- }
-
- unsigned TestSuite::getLastRunFailCount () {
- return this->lastRunFailCount;
- }
-
- void TestSuite::run () {
- if (this->tests.size() == 0) {
- clog << "No tests to run!" << endl;
- return;
- }
-
- this->lastRunSucceeded = true;
- this->lastRunFailCount = 0;
- this->lastRunSuccessCount = 0;
- this->totalRuntime = 0;
-
- clog << endl
- << "Starting to run test suite '" << this->suiteName << "'"
- << endl << endl;
-
- for (TestCase test : this->tests) {
- bool testPassed = false;
- try {
- testPassed = test.go();
- }
- catch (exception& e) {
- clog << "Exception occurred during test run: "
- << e.what() << endl;
- }
- catch (...) {
- cerr << "An unknown error occurred during test run."
- << endl;
- }
-
- if (!testPassed && this->lastRunSucceeded) {
- this->lastRunFailCount++;
- this->lastRunSucceeded = false;
- }
- else if (!testPassed) {
- this->lastRunFailCount++;
- }
- else {
- this->lastRunSuccessCount++;
- }
-
- this->totalRuntime += test.getLastRuntime();
- }
-
- clog << endl;
-
- if (this->testPassedMessage &&
- this->lastRunFailCount == 0) {
- clog << "All '" << this->suiteName
- << "' suite tests passed!" << endl;
- }
-
- double suiteRuntimeElapsed = static_cast(
- this->totalRuntime)/1000000000.0;
-
- clog << fixed;
- clog << setprecision(0);
- clog << "Finished running suite '" << this->suiteName << "' in "
- << suiteRuntimeElapsed << "s ("<< this->lastRunSuccessCount
- << "/" << this->tests.size() << " passed)" << endl;
- clog << endl;
- }
-
- template<>
- void TestSuite::addTest (TestCase&& test) {
- this->tests.emplace_back(test);
- }
-
- void TestSuite::assertThrows (
- function shouldThrow,
- string failureMessage
- )
- {
- try {
- shouldThrow();
- }
- catch (...) {
- exception_ptr eptr = current_exception();
-
- if (eptr) {
- try {
- rethrow_exception(eptr);
- }
- catch (const exception& e) {
- clog << "assertThrows caught exception: "
- << TestFailedException(e.what()).what()
- << endl;
- }
- }
- else {
- clog << "Something was thrown, not sure what." << endl
- << "This satisfies the assertion, so no failure is"
- << " present. "
- << TestFailedException("Unknown thrown object").
- what();
- }
-
- return;
- }
-
- throw TestFailedException(move(failureMessage));
- }
-
- void TestSuite::assertNoThrows (
- function shouldNotThrow,
- string failureMessage
- )
- {
- try {
- shouldNotThrow();
- }
- catch (...) {
- throw TestFailedException(move(failureMessage));
- }
- }
-
- void TestSuite::assertTrue (
- bool condition,
- string failureMessage
- )
- {
- if (!condition) {
- stringstream err;
-
- err << "Boolean Truth assertion failed!" << std::endl;
- err << failureMessage << std::endl;
-
- throw TestFailedException(err.str());
- }
- }
-
- void TestSuite::assertFalse (
- bool condition,
- string failureMessage
- )
- {
- if (condition) {
- stringstream err;
-
- err << "Boolean False assertion failed!" << std::endl;
- err << failureMessage << std::endl;
-
- throw TestFailedException(err.str());
- }
- }
-
- void TestSuite::fail(string failureMessage) {
- throw TestFailedException(move(failureMessage));
- }
}
diff --git a/src/TestCPPTestSuite.cpp b/src/TestCPPTestSuite.cpp
new file mode 100644
index 0000000..c5b04fa
--- /dev/null
+++ b/src/TestCPPTestSuite.cpp
@@ -0,0 +1,172 @@
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+ */
+
+//Original author: Jonathan Hyry CSU-Fullerton SECS 6896-02 Fall 2014/15
+
+#include
+
+#include "internal/TestCPPCommon.h"
+#include "internal/TestCPPTestCase.h"
+#include "internal/TestCPPTestSuite.h"
+
+using std::cerr;
+using std::clog;
+using std::cout;
+using std::endl;
+using std::exception;
+using std::fixed;
+using std::function;
+using std::invalid_argument;
+using std::move;
+using std::ostream;
+using std::rethrow_exception;
+using std::runtime_error;
+using std::setprecision;
+using std::string;
+using std::tuple;
+
+using TCPPNum = TestCPP::TestCPPCommon::Nums;
+using TCPPStr = TestCPP::TestCPPCommon::Strings;
+
+namespace TestCPP {
+
+ void TestSuite::enableTestPassedMessage () {
+ this->testSuitePassedMessage = true;
+ for (unsigned i = 0; i < this->tests.size(); i += 1) {
+ this->tests[i].setNotifyPassed(true);
+ }
+ }
+ void TestSuite::disableTestPassedMessage () {
+ this->testSuitePassedMessage = false;
+ for (unsigned i = 0; i < this->tests.size(); i += 1) {
+ this->tests[i].setNotifyPassed(false);
+ }
+ }
+
+ void TestSuite::setSuiteName (TestObjName&& testSuiteName) {
+ this->suiteName = move(testSuiteName);
+ }
+
+ unsigned TestSuite::getLastRunFailCount () {
+ return this->lastRunFailCount;
+ }
+
+ void TestSuite::run () {
+ if (this->tests.size() == 0) {
+ clog << TCPPStr::NTR << endl;
+ return;
+ }
+
+ this->lastRunSucceeded = true;
+ this->lastRunFailCount = 0;
+ this->lastRunSuccessCount = 0;
+ this->totalRuntime = 0;
+
+ clog << endl
+ << TCPPStr::START_RUN << TCPPStr::SUITE_
+ << TCPPStr::APOS << this->suiteName << TCPPStr::APOS
+ << endl
+ << endl;
+
+ for (TestCase test : this->tests) {
+ bool testPassed = false;
+ try {
+ testPassed = test.go();
+ }
+ catch (exception& e) {
+ clog << TCPPStr::TEST_EXC_ << e.what()
+ << endl;
+ }
+ catch (...) {
+ cerr << TCPPStr::UNK_EXC
+ << endl;
+ }
+
+ if (!testPassed) {
+ this->lastRunFailCount++;
+
+ if (this->lastRunSucceeded) {
+ this->lastRunSucceeded = false;
+ }
+ }
+ else {
+ this->lastRunSuccessCount++;
+ }
+
+ this->totalRuntime += test.getLastRuntime();
+ }
+
+ clog << endl;
+
+ if (this->testSuitePassedMessage &&
+ this->lastRunFailCount == 0) {
+ clog << TCPPStr::ALL_ << TCPPStr::APOS << this->suiteName
+ << TCPPStr::APOS << TCPPStr::_SUITE_TESTS_PASSED
+ << endl;
+ }
+
+ double suiteRuntimeElapsed = static_cast(
+ this->totalRuntime)/TCPPNum::NANOS_IN_SEC;
+
+ clog << fixed;
+ clog << setprecision(0);
+ clog << TCPPStr::FINISHED_SUITE_ << TCPPStr::APOS
+ << this->suiteName << TCPPStr::APOS << TCPPStr::_IN_ABOUT_
+ << suiteRuntimeElapsed << TCPPStr::SEC << TCPPStr::SP
+ << TCPPStr::PARENL << this->lastRunSuccessCount
+ << TCPPStr::FWSL << this->tests.size() << TCPPStr::_PASSED
+ << TCPPStr::PARENR
+ << endl;
+ }
+
+ /**
+ * @brief Add a test to this test suite.
+ *
+ * The test should be a TestCase object.
+ */
+ template<>
+ void TestSuite::addTest (TestCase&& test) {
+ this->tests.emplace_back(test);
+ }
+
+ /**
+ * @brief Add a test to this test suite.
+ *
+ * The test should be defined as a tuple with 2 elements to use
+ * this. The first element is the test name, and the second
+ * element is the test function that defines the test.
+ */
+ template<>
+ void TestSuite::addTest (tuple>&& test) {
+ this->tests.emplace_back(
+ std::get<0>(test),
+ std::get<1>(test),
+ this->testSuitePassedMessage
+ );
+ }
+}
diff --git a/src/TestCPPUtil.cpp b/src/TestCPPUtil.cpp
index b48693e..671d8ea 100644
--- a/src/TestCPPUtil.cpp
+++ b/src/TestCPPUtil.cpp
@@ -28,10 +28,14 @@ For more information, please refer to
#include
#include
-#include "TestCPPUtil.h"
+#include "internal/TestCPPCommon.h"
+#include "internal/TestCPPExceptions.h"
+#include "internal/TestCPPUtil.h"
#ifdef DEBUG_LOG
#include
+#else
+#include
#endif
#ifdef DEBUG_LOG
@@ -39,8 +43,34 @@ using std::clog;
using std::endl;
#endif
+using TCPPStr = TestCPP::TestCPPCommon::Strings;
+
namespace TestCPP {
+
+ TestObjName::TestObjName (const char* name) {
+ if (name) {
+ this->testCaseName = name;
+ }
+ else {
+ throw TestCPPException(TCPPStr::NVTN);
+ }
+ }
+
+ const string& TestObjName::getName () {
+ return this->testCaseName;
+ }
+
+ std::ostream& operator<< (
+ std::ostream& s,
+ TestObjName& tcName
+ )
+ {
+ s << tcName.getName();
+ return s;
+ }
+
namespace Util {
+
void debugLog(const string& message, bool omitNewline) {
#ifdef DEBUG_LOG
clog << message;
diff --git a/test/include/Assertions/AssertionsSuite.h b/test/include/Assertions/AssertionsSuite.h
new file mode 100644
index 0000000..68596b1
--- /dev/null
+++ b/test/include/Assertions/AssertionsSuite.h
@@ -0,0 +1,33 @@
+#ifndef TESTCPP_ASSERTIONS_SUITE_
+#define TESTCPP_ASSERTIONS_SUITE_
+
+#include "AssertionsTests.h"
+
+namespace TestCPP {
+ namespace Testing {
+ namespace AssertionsSuite {
+ TestSuite suite(
+ "TestCPP Assertions Tests",
+
+ make_tuple(
+ "assertNull Test",
+ function(AssertionsTests::TestAssertNull)
+ ),
+ make_tuple(
+ "assertNotNull Test",
+ function(AssertionsTests::TestAssertNotNull)
+ ),
+ make_tuple(
+ "assertTrue Test",
+ function(AssertionsTests::TestAssertTrue)
+ ),
+ make_tuple(
+ "assertFalse Test",
+ function(AssertionsTests::TestAssertFalse)
+ )
+ );
+ }
+ }
+}
+
+#endif
diff --git a/test/include/Assertions/AssertionsTests.h b/test/include/Assertions/AssertionsTests.h
new file mode 100644
index 0000000..9a69fb7
--- /dev/null
+++ b/test/include/Assertions/AssertionsTests.h
@@ -0,0 +1,15 @@
+#ifndef TESTCPP_ASSERTIONS_TESTS_
+#define TESTCPP_ASSERTIONS_TESTS_
+
+namespace TestCPP {
+ namespace Testing {
+ namespace AssertionsTests {
+ void TestAssertTrue ();
+ void TestAssertFalse ();
+ void TestAssertNull ();
+ void TestAssertNotNull ();
+ }
+ }
+}
+
+#endif
diff --git a/test/include/TestCase/TestCaseSuite.h b/test/include/TestCase/TestCaseSuite.h
index ba3115b..68b7bc2 100644
--- a/test/include/TestCase/TestCaseSuite.h
+++ b/test/include/TestCase/TestCaseSuite.h
@@ -17,6 +17,18 @@ namespace TestCPP {
"Case runner Test",
function(TestCaseTests::TestTestCaseGo)
),
+ make_tuple(
+ "Case runner Test - string thrown",
+ function(TestCaseTests::TestTestCaseGoThrowStr)
+ ),
+ make_tuple(
+ "Case runner Test - char thrown",
+ function(TestCaseTests::TestTestCaseGoThrowChr)
+ ),
+ make_tuple(
+ "Case runner Test - test catchall",
+ function(TestCaseTests::TestTestCaseGoThrowInt)
+ ),
make_tuple(
"Case setNotifyPassed Test",
function(
diff --git a/test/include/TestCase/TestCaseTests.h b/test/include/TestCase/TestCaseTests.h
index d1569f8..90b526c 100644
--- a/test/include/TestCase/TestCaseTests.h
+++ b/test/include/TestCase/TestCaseTests.h
@@ -6,6 +6,9 @@ namespace TestCPP {
namespace TestCaseTests {
void TestConstructCase ();
void TestTestCaseGo ();
+ void TestTestCaseGoThrowStr ();
+ void TestTestCaseGoThrowChr ();
+ void TestTestCaseGoThrowInt ();
void TestTestCaseSetNotifyPassed ();
}
}
diff --git a/test/include/TestSuite/TestSuiteSuite.h b/test/include/TestSuite/TestSuiteSuite.h
index ca195ec..68cef2c 100644
--- a/test/include/TestSuite/TestSuiteSuite.h
+++ b/test/include/TestSuite/TestSuiteSuite.h
@@ -10,24 +10,64 @@ namespace TestCPP {
"TestCPP TestSuite Tests",
make_tuple(
- "Suite construction Test",
- function(TestSuiteTests::TestConstructSuite)
+ "Suite construction Test - no tests",
+ function(
+ TestSuiteTests::TestConstructSuiteBare
+ )
),
make_tuple(
- "assertNull Test",
- function(TestSuiteTests::TestAssertNull)
+ "Suite construction Test - TestCases",
+ function(
+ TestSuiteTests::TestConstructSuiteTestCases
+ )
),
make_tuple(
- "assertNotNull Test",
- function(TestSuiteTests::TestAssertNotNull)
+ "Suite construction Test - tuples",
+ function(
+ TestSuiteTests::TestConstructSuiteTuples
+ )
),
make_tuple(
- "assertTrue Test",
- function(TestSuiteTests::TestAssertTrue)
+ "Suite construction Test - mixed",
+ function(
+ TestSuiteTests::TestConstructSuiteMixed
+ )
),
make_tuple(
- "assertFalse Test",
- function(TestSuiteTests::TestAssertFalse)
+ "Suite enable test passed message - no tests",
+ function(
+ TestSuiteTests::TestEnableTestPassedMessageNoTests
+ )
+ ),
+ make_tuple(
+ "Suite enable test passed message - one test",
+ function(
+ TestSuiteTests::TestEnableTestPassedMessageOneTest
+ )
+ ),
+ make_tuple(
+ "Suite enable test passed message - many tests",
+ function(
+ TestSuiteTests::TestEnableTestPassedMessageManyTests
+ )
+ ),
+ make_tuple(
+ "Suite disable test passed message - no tests",
+ function(
+ TestSuiteTests::TestDisableTestPassedMessageNoTests
+ )
+ ),
+ make_tuple(
+ "Suite disable test passed message - one test",
+ function(
+ TestSuiteTests::TestDisableTestPassedMessageOneTest
+ )
+ ),
+ make_tuple(
+ "Suite disable test passed message - many tests",
+ function(
+ TestSuiteTests::TestDisableTestPassedMessageManyTests
+ )
)
);
}
diff --git a/test/include/TestSuite/TestSuiteTests.h b/test/include/TestSuite/TestSuiteTests.h
index 22aeb86..878c6a6 100644
--- a/test/include/TestSuite/TestSuiteTests.h
+++ b/test/include/TestSuite/TestSuiteTests.h
@@ -4,11 +4,16 @@
namespace TestCPP {
namespace Testing {
namespace TestSuiteTests {
- void TestConstructSuite ();
- void TestAssertTrue ();
- void TestAssertFalse ();
- void TestAssertNull ();
- void TestAssertNotNull ();
+ void TestConstructSuiteBare ();
+ void TestConstructSuiteTestCases ();
+ void TestConstructSuiteTuples ();
+ void TestConstructSuiteMixed ();
+ void TestEnableTestPassedMessageNoTests ();
+ void TestEnableTestPassedMessageOneTest ();
+ void TestEnableTestPassedMessageManyTests ();
+ void TestDisableTestPassedMessageNoTests ();
+ void TestDisableTestPassedMessageOneTest ();
+ void TestDisableTestPassedMessageManyTests ();
}
}
}
diff --git a/test/src/Assertions/AssertionsTests.cpp b/test/src/Assertions/AssertionsTests.cpp
new file mode 100644
index 0000000..10f4634
--- /dev/null
+++ b/test/src/Assertions/AssertionsTests.cpp
@@ -0,0 +1,48 @@
+#include "TestCPP.h"
+#include "Assertions/AssertionsTests.h"
+
+namespace TestCPP {
+ namespace Testing {
+ namespace AssertionsTests {
+ void TestAssertTrue () {
+ int lower = 5;
+ int higher = 9;
+
+ Assertions::assertTrue(higher > lower,
+ "Negated condtion!");
+ }
+
+ void TestAssertFalse () {
+ int lower = 5;
+ int higher = 9;
+
+ Assertions::assertFalse(higher < lower,
+ "Negated condtion!");
+ }
+
+ void TestAssertNull () {
+ string * nullString = nullptr;
+
+ Assertions::assertNull(nullString, "nullptr is null!");
+ }
+
+ void TestAssertNotNull () {
+ string notNull("non-null");
+
+ Assertions::assertNotNull(
+ ¬Null,
+ "A constructed std::string is not null!"
+ );
+ Assertions::assertNotNull(
+ "non-null",
+ "A const char * is not null!"
+ );
+
+ int testInt = 5;
+
+ Assertions::assertNotNull(&testInt,
+ "An int pointer is not null!");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/src/TestCPPAssertionsMain.cpp b/test/src/TestCPPAssertionsMain.cpp
new file mode 100644
index 0000000..b0e5374
--- /dev/null
+++ b/test/src/TestCPPAssertionsMain.cpp
@@ -0,0 +1,25 @@
+#include "TestCPP.h"
+
+using TestCPP::TestCase;
+using TestCPP::TestSuite;
+using std::string;
+using std::make_tuple;
+using std::function;
+
+#include "Assertions/AssertionsSuite.h"
+
+int main(void)
+{
+ try {
+ TestCPP::Testing::AssertionsSuite::suite.run();
+ return TestCPP::Util::unsignedToSigned(
+ TestCPP::Testing::AssertionsSuite::suite.
+ getLastRunFailCount()
+ );
+ }
+ catch (std::exception& e) {
+ std::cerr << "Test suite run failed with an exception: "
+ << e.what() << std::endl;
+ return -1;
+ }
+}
diff --git a/test/src/TestCPPTestCaseMain.cpp b/test/src/TestCPPTestCaseMain.cpp
index 89287f2..3db3ee6 100644
--- a/test/src/TestCPPTestCaseMain.cpp
+++ b/test/src/TestCPPTestCaseMain.cpp
@@ -1,5 +1,4 @@
#include "TestCPP.h"
-#include "TestCPPUtil.h"
using TestCPP::TestCase;
using TestCPP::TestSuite;
diff --git a/test/src/TestCPPTestSuiteMain.cpp b/test/src/TestCPPTestSuiteMain.cpp
index 61f8968..8cb0f8a 100644
--- a/test/src/TestCPPTestSuiteMain.cpp
+++ b/test/src/TestCPPTestSuiteMain.cpp
@@ -1,5 +1,4 @@
#include "TestCPP.h"
-#include "TestCPPUtil.h"
using TestCPP::TestCase;
using TestCPP::TestSuite;
diff --git a/test/src/TestCase/TestCaseTestChunks.cpp b/test/src/TestCase/TestCaseTestChunks.cpp
index 7d6d4fe..a022227 100644
--- a/test/src/TestCase/TestCaseTestChunks.cpp
+++ b/test/src/TestCase/TestCaseTestChunks.cpp
@@ -1,7 +1,6 @@
#include
#include "TestCPP.h"
-#include "TestCPPUtil.h"
#include "TestCase/TestCaseTestChunks.h"
using TestCPP::TestCase;
@@ -83,7 +82,6 @@ namespace TestCPP {
function([](){}),
false, false, true
));
-
test = unique_ptr(new TestCase(
"ConstructCase Test - w/NF,COT,CLF",
function([](){}),
diff --git a/test/src/TestCase/TestCaseTests.cpp b/test/src/TestCase/TestCaseTests.cpp
index 530a68d..8fef9ec 100644
--- a/test/src/TestCase/TestCaseTests.cpp
+++ b/test/src/TestCase/TestCaseTests.cpp
@@ -1,9 +1,10 @@
#include "TestCPP.h"
-#include "TestCPPUtil.h"
#include "TestCase/TestCaseTestChunks.h"
using TestCPP::Util::debugLog;
+using TCPPStr = TestCPP::TestCPPCommon::Strings;
+
namespace TestCPP {
namespace Testing {
namespace TestCaseTests {
@@ -15,7 +16,7 @@ namespace TestCPP {
));
debugLog("Construct with nullptr string");
- TestSuite::assertThrows(
+ Assertions::assertThrows(
[]() {
debugLog("Construct with nullptr string", true);
debugLog(" - assertThrows lambda");
@@ -41,12 +42,82 @@ namespace TestCPP {
true
));
- TestSuite::assertTrue(
+ Assertions::assertTrue(
test->go(),
"Should have succeeded basic no-op test!"
);
}
+ void TestTestCaseGoThrowStr () {
+ const string throwStr = "Test throw string!";
+
+ auto test = unique_ptr(new TestCase(
+ "SUB-TEST TestCaseGo case Test - throws str",
+ function([&throwStr](){
+ throw throwStr;
+ }),
+ true, false, true, false,
+ TestCase::TestCaseOutCompareOptions::CONTAINS
+ ));
+
+ Assertions::assertFalse(
+ test->go(),
+ "Should have succeeded str throws test!"
+ );
+
+ Assertions::assertTrue(
+ test->checkLog(throwStr),
+ "Something is off, expected output does not exist!"
+ );
+ }
+
+ void TestTestCaseGoThrowChr () {
+ constexpr const char * throwChr =
+ "Test throw const char *!";
+
+ auto test = unique_ptr(new TestCase(
+ "SUB-TEST TestCaseGo case Test - throws chr",
+ function([&throwChr](){
+ throw throwChr;
+ }),
+ true, false, true, false,
+ TestCase::TestCaseOutCompareOptions::CONTAINS
+ ));
+
+ Assertions::assertFalse(
+ test->go(),
+ "Should have succeeded chr throws test!"
+ );
+
+ string tcLog(throwChr);
+
+ Assertions::assertTrue(
+ test->checkLog(tcLog),
+ "Something is off, expected output does not exist!"
+ );
+ }
+
+ void TestTestCaseGoThrowInt () {
+ auto test = unique_ptr(new TestCase(
+ "SUB-TEST TestCaseGo case Test - throws int",
+ function([](){
+ throw -1;
+ }),
+ true, false, true, false,
+ TestCase::TestCaseOutCompareOptions::CONTAINS
+ ));
+
+ Assertions::assertFalse(
+ test->go(),
+ "Should have succeeded catchall throws test!"
+ );
+
+ Assertions::assertTrue(
+ test->checkLog(TCPPStr::UNK_EXC),
+ "Something is off, expected output does not exist!"
+ );
+ }
+
void TestTestCaseSetNotifyPassed () {
auto test = unique_ptr(new TestCase(
"TestCaseSetNotifyPassed case Test",
@@ -55,7 +126,7 @@ namespace TestCPP {
TestCase::TestCaseOutCompareOptions::CONTAINS
));
- TestSuite::assertTrue(
+ Assertions::assertTrue(
test->go(),
"TestSetNotifyPassed go() 1"
);
@@ -64,26 +135,26 @@ namespace TestCPP {
tcLog << "Test ";
tcLog << "TestCaseSetNotifyPassed case Test passed! (";
- TestSuite::assertTrue(
+ Assertions::assertTrue(
!test->checkLog(tcLog.str()),
"TestSetNotifyPassed checkLog() 1"
);
- TestSuite::assertTrue(
+ Assertions::assertTrue(
!test->checkLog(string("s)")),
"TestSetNotifyPassed checkLog() 2"
);
test->setNotifyPassed(true);
- TestSuite::assertTrue(
+ Assertions::assertTrue(
test->go(),
"TestSetNotifyPassed go() 2"
);
- TestSuite::assertTrue(
+ Assertions::assertTrue(
test->checkLog(tcLog.str()),
"TestSetNotifyPassed checkLog() 3"
);
- TestSuite::assertTrue(
+ Assertions::assertTrue(
test->checkLog(string("s)")),
"TestSetNotifyPassed checkLog() 4"
);
@@ -91,16 +162,16 @@ namespace TestCPP {
test->clearLogCapture();
test->setNotifyPassed(false);
- TestSuite::assertTrue(
+ Assertions::assertTrue(
test->go(),
"TestSetNotifyPassed go() 3"
);
- TestSuite::assertTrue(
+ Assertions::assertTrue(
!test->checkLog(tcLog.str()),
"TestSetNotifyPassed checkLog() 5"
);
- TestSuite::assertTrue(
+ Assertions::assertTrue(
!test->checkLog(string("s)")),
"TestSetNotifyPassed checkLog() 6"
);
diff --git a/test/src/TestSuite/TestSuiteTests.cpp b/test/src/TestSuite/TestSuiteTests.cpp
index 6ee58d7..52189a1 100644
--- a/test/src/TestSuite/TestSuiteTests.cpp
+++ b/test/src/TestSuite/TestSuiteTests.cpp
@@ -4,50 +4,538 @@
namespace TestCPP {
namespace Testing {
namespace TestSuiteTests {
- void TestConstructSuite () {
- auto test = unique_ptr(new TestSuite(
- "Suite construction"
+ void TestConstructSuiteBare () {
+ auto testSuite = unique_ptr(new TestSuite(
+ "Suite construction - bare"
));
}
+ void TestConstructSuiteTestCases () {
+ TestCase test1("dummy 1", [](){}),
+ test2("dummy 2", [](){});
- void TestAssertTrue () {
- int lower = 5;
- int higher = 9;
+ auto testSuite = unique_ptr(new TestSuite(
+ "Suite construction - TestCases",
+ test1
+ ));
+ testSuite = unique_ptr(new TestSuite(
+ "Suite construction - TestCases",
+ test1, test2
+ ));
+ }
+ void TestConstructSuiteTuples () {
+ auto testSuite = unique_ptr(new TestSuite(
+ "Suite construction - tuples",
+ make_tuple("dummy 1", function([](){}))
+ ));
+ testSuite = unique_ptr(new TestSuite(
+ "Suite construction - tuples",
+ make_tuple("dummy 1", function([](){})),
+ make_tuple("dummy 2", function([](){}))
+ ));
+ }
+ void TestConstructSuiteMixed () {
+ TestCase test1("dummy 1", [](){}),
+ test2("dummy 2", [](){});
+ auto testSuite = unique_ptr(new TestSuite(
+ "Suite construction - mixed",
+ test1,
+ make_tuple("dummy 1", function([](){}))
+ ));
+ testSuite = unique_ptr(new TestSuite(
+ "Suite construction - mixed",
+ test1, test2,
+ make_tuple("dummy 1", function([](){}))
+ ));
+ testSuite = unique_ptr(new TestSuite(
+ "Suite construction - mixed",
+ test1,
+ make_tuple("dummy 1", function([](){})),
+ make_tuple("dummy 2", function([](){}))
+ ));
+ testSuite = unique_ptr(new TestSuite(
+ "Suite construction - mixed",
+ test1, test2,
+ make_tuple("dummy 1", function([](){})),
+ make_tuple("dummy 2", function([](){}))
+ ));
+ testSuite = unique_ptr(new TestSuite(
+ "Suite construction - mixed",
+ test1,
+ make_tuple("dummy 1", function([](){})),
+ make_tuple("dummy 2", function([](){})),
+ test2
+ ));
+ testSuite = unique_ptr(new TestSuite(
+ "Suite construction - mixed",
+ make_tuple("dummy 1", function([](){})),
+ test1, test2,
+ make_tuple("dummy 2", function([](){}))
+ ));
+ testSuite = unique_ptr(new TestSuite(
+ "Suite construction - mixed",
+ test1,
+ make_tuple("dummy 1", function([](){})),
+ test2,
+ make_tuple("dummy 2", function([](){}))
+ ));
+ testSuite = unique_ptr(new TestSuite(
+ "Suite construction - mixed",
+ make_tuple("dummy 1", function([](){})),
+ test2,
+ make_tuple("dummy 2", function([](){})),
+ test1
+ ));
+ }
- TestSuite::assertTrue(higher > lower,
- "Negated condtion!");
+ void TestEnableTestPassedMessageNoTests() {
+ auto testSuite = unique_ptr(new TestSuite(
+ "SUBSUITE - Test Passed Message Enabled - no tests"
+ ));
+ testSuite->enableTestPassedMessage();
+ testSuite->run();
}
+ void TestEnableTestPassedMessageOneTest() {
+ const char * suiteName =
+ "SUBSUITE - Test Passed Message Enabled - one test";
+ const char * testName =
+ "SUBTEST - enable test passed message dummy 1";
+
+ TestCase test(
+ testName,
+ [](){},
+ false,
+ false,
+ true,
+ false
+ );
+
+ auto testSuite = unique_ptr(new TestSuite(
+ suiteName, test
+ ));
+
+ testSuite->enableTestPassedMessage();
+ testSuite->run();
+
+ stringstream tsLog;
+ tsLog << "All '";
+ tsLog << suiteName;
+ tsLog << "' suite tests passed!";
+
+ stringstream tcLog;
+ tcLog << "Test ";
+ tcLog << testName;
+ tcLog << " passed! (";
+
+ string secondsParen = "s)";
+
+ Assertions::assertTrue(
+ test.checkLog(tcLog.str()),
+ "Should have notified on test passage! 1"
+ );
+ Assertions::assertTrue(
+ test.checkLog(secondsParen),
+ "Should have notified on test passage! 2"
+ );
+ Assertions::assertTrue(
+ test.checkLog(tsLog.str()),
+ "Should have notified on suite passage! 1"
+ );
+
+ test.clearLogCapture();
- void TestAssertFalse () {
- int lower = 5;
- int higher = 9;
+ test = TestCase(
+ testName,
+ [](){},
+ true,
+ false,
+ true,
+ false
+ );
+
+ testSuite = unique_ptr(new TestSuite(
+ suiteName, test
+ ));
+
+ testSuite->enableTestPassedMessage();
+ testSuite->run();
- TestSuite::assertFalse(higher < lower,
- "Negated condtion!");
+ Assertions::assertTrue(
+ test.checkLog(tcLog.str()),
+ "Should have notified on test passage! 3"
+ );
+ Assertions::assertTrue(
+ test.checkLog(secondsParen),
+ "Should have notified on test passage! 4"
+ );
+ Assertions::assertTrue(
+ test.checkLog(tsLog.str()),
+ "Should have notified on suite passage! 2"
+ );
}
+ void TestEnableTestPassedMessageManyTests() {
+ const char * suiteName =
+ "SUBSUITE - Test Passed Message Enabled - many tests";
+ const char * test1Name =
+ "SUBTEST - enable test passed message dummy 1";
+ const char * test2Name =
+ "SUBTEST - enable test passed message dummy 2";
+ const char * test3Name =
+ "SUBTEST - enable test passed message dummy 3";
+
+ TestCase test1(
+ test1Name,
+ [](){},
+ false,
+ false,
+ true,
+ false
+ );
+ TestCase test2(
+ test2Name,
+ [](){},
+ false,
+ false,
+ true,
+ false
+ );
+ TestCase test3(
+ test3Name,
+ [](){},
+ false,
+ false,
+ true,
+ false
+ );
+
+ auto testSuite = unique_ptr(new TestSuite(
+ suiteName, test1, test2, test3
+ ));
+ testSuite->enableTestPassedMessage();
+ testSuite->run();
+
+ stringstream tsLog;
+ tsLog << "All '";
+ tsLog << suiteName;
+ tsLog << "' suite tests passed!";
+
+ stringstream tc1Log;
+ tc1Log << "Test ";
+ tc1Log << test1Name;
+ tc1Log << " passed! (";
+ stringstream tc2Log;
+ tc2Log << "Test ";
+ tc2Log << test2Name;
+ tc2Log << " passed! (";
+ stringstream tc3Log;
+ tc3Log << "Test ";
+ tc3Log << test3Name;
+ tc3Log << " passed! (";
+
+ string secondsParen = "s)";
+
+ Assertions::assertTrue(
+ test1.checkLog(tc1Log.str()),
+ "Should have notified on test passage! 1"
+ );
+ Assertions::assertTrue(
+ test1.checkLog(tc2Log.str()),
+ "Should have notified on test passage! 2"
+ );
+ Assertions::assertTrue(
+ test1.checkLog(tc3Log.str()),
+ "Should have notified on test passage! 3"
+ );
+ Assertions::assertTrue(
+ test1.checkLog(secondsParen),
+ "Should have notified on test passage! 4"
+ );
+ Assertions::assertTrue(
+ test1.checkLog(tsLog.str()),
+ "Should have notified on suite passage! 1"
+ );
+
+ test1.clearLogCapture();
+
+ test1 = TestCase(
+ test1Name,
+ [](){},
+ true,
+ false,
+ true,
+ false
+ );
+ test2 = TestCase(
+ test2Name,
+ [](){},
+ true,
+ false,
+ true,
+ false
+ );
+ test3 = TestCase(
+ test3Name,
+ [](){},
+ true,
+ false,
+ true,
+ false
+ );
+
+ testSuite = unique_ptr(new TestSuite(
+ suiteName, test1, test2, test3
+ ));
- void TestAssertNull () {
- string * nullString = nullptr;
+ testSuite->enableTestPassedMessage();
+ testSuite->run();
- TestSuite::assertNull(nullString, "nullptr is null!");
+ Assertions::assertTrue(
+ test1.checkLog(tc1Log.str()),
+ "Should have notified on test passage! 5"
+ );
+ Assertions::assertTrue(
+ test1.checkLog(tc2Log.str()),
+ "Should have notified on test passage! 6"
+ );
+ Assertions::assertTrue(
+ test1.checkLog(tc3Log.str()),
+ "Should have notified on test passage! 7"
+ );
+ Assertions::assertTrue(
+ test1.checkLog(secondsParen),
+ "Should have notified on test passage! 8"
+ );
+ Assertions::assertTrue(
+ test1.checkLog(tsLog.str()),
+ "Should have notified on suite passage! 2"
+ );
}
- void TestAssertNotNull () {
- string notNull("non-null");
+ void TestDisableTestPassedMessageNoTests() {
+ auto testSuite = unique_ptr(new TestSuite(
+ "Test Passed Message Disabled - no tests"
+ ));
+ testSuite->disableTestPassedMessage();
+ testSuite->run();
+ }
+ void TestDisableTestPassedMessageOneTest() {
+ const char * suiteName =
+ "SUBSUITE - Test Passed Message Disabled - one test";
+ const char * testName =
+ "SUBTEST - disable test passed message dummy 1";
- TestSuite::assertNotNull(
- ¬Null,
- "A constructed std::string is not null!"
+ TestCase test(
+ testName,
+ [](){},
+ false,
+ false,
+ true,
+ false
);
- TestSuite::assertNotNull(
- "non-null",
- "A const char * is not null!"
+
+ auto testSuite = unique_ptr(new TestSuite(
+ suiteName, test
+ ));
+
+ test.clearLogCapture();
+
+ testSuite->disableTestPassedMessage();
+ testSuite->run();
+
+ stringstream tsLog;
+ tsLog << "All '";
+ tsLog << suiteName;
+ tsLog << "' suite tests passed!";
+
+ stringstream tcLog;
+ tcLog << "Test ";
+ tcLog << testName;
+ tcLog << " passed! (";
+
+ string secondsParen = "s)";
+
+ Assertions::assertFalse(
+ test.checkLog(tcLog.str()),
+ "Should not have notified on test passage! 1"
);
+ Assertions::assertFalse(
+ test.checkLog(secondsParen),
+ "Should not have notified on test passage! 2"
+ );
+ Assertions::assertFalse(
+ test.checkLog(tsLog.str()),
+ "Should not have notified on suite passage! 1"
+ );
+
+ test = TestCase(
+ testName,
+ [](){},
+ true,
+ false,
+ true,
+ false
+ );
+
+ testSuite = unique_ptr(new TestSuite(
+ suiteName, test
+ ));
+
+ test.clearLogCapture();
- int testInt = 5;
+ testSuite->disableTestPassedMessage();
+ testSuite->run();
- TestSuite::assertNotNull(&testInt,
- "An int pointer is not null!");
+ Assertions::assertFalse(
+ test.checkLog(tcLog.str()),
+ "Should not have notified on test passage! 3"
+ );
+ Assertions::assertFalse(
+ test.checkLog(secondsParen),
+ "Should not have notified on test passage! 4"
+ );
+ Assertions::assertFalse(
+ test.checkLog(tsLog.str()),
+ "Should not have notified on suite passage! 2"
+ );
+ }
+ void TestDisableTestPassedMessageManyTests() {
+ const char * suiteName =
+ "SUBSUITE - Test Passed Message Disabled - many tests";
+ const char * test1Name =
+ "SUBTEST - disable test passed message dummy 1";
+ const char * test2Name =
+ "SUBTEST - disable test passed message dummy 2";
+ const char * test3Name =
+ "SUBTEST - disable test passed message dummy 3";
+
+ TestCase test1(
+ test1Name,
+ [](){},
+ false,
+ false,
+ true,
+ false
+ );
+ TestCase test2(
+ test2Name,
+ [](){},
+ false,
+ false,
+ true,
+ false
+ );
+ TestCase test3(
+ test3Name,
+ [](){},
+ false,
+ false,
+ true,
+ false
+ );
+
+ auto testSuite = unique_ptr(new TestSuite(
+ suiteName, test1, test2, test3
+ ));
+
+ test1.clearLogCapture();
+
+ testSuite->disableTestPassedMessage();
+ testSuite->run();
+
+ stringstream tsLog;
+ tsLog << "All '";
+ tsLog << suiteName;
+ tsLog << "' suite tests passed!";
+
+ stringstream tc1Log;
+ tc1Log << "Test ";
+ tc1Log << test1Name;
+ tc1Log << " passed! (";
+ stringstream tc2Log;
+ tc2Log << "Test ";
+ tc2Log << test2Name;
+ tc2Log << " passed! (";
+ stringstream tc3Log;
+ tc3Log << "Test ";
+ tc3Log << test3Name;
+ tc3Log << " passed! (";
+
+ string secondsParen = "s)";
+
+ Assertions::assertFalse(
+ test1.checkLog(tc1Log.str()),
+ "Should not have notified on test passage! 1"
+ );
+ Assertions::assertFalse(
+ test1.checkLog(tc2Log.str()),
+ "Should not have notified on test passage! 2"
+ );
+ Assertions::assertFalse(
+ test1.checkLog(tc3Log.str()),
+ "Should not have notified on test passage! 3"
+ );
+ Assertions::assertFalse(
+ test1.checkLog(secondsParen),
+ "Should not have notified on test passage! 4"
+ );
+ Assertions::assertFalse(
+ test1.checkLog(tsLog.str()),
+ "Should not have notified on suite passage! 1"
+ );
+
+ test1 = TestCase(
+ test1Name,
+ [](){},
+ true,
+ false,
+ true,
+ false
+ );
+ test2 = TestCase(
+ test2Name,
+ [](){},
+ true,
+ false,
+ true,
+ false
+ );
+ test3 = TestCase(
+ test3Name,
+ [](){},
+ true,
+ false,
+ true,
+ false
+ );
+
+ testSuite = unique_ptr(new TestSuite(
+ suiteName, test1, test2, test3
+ ));
+
+ test1.clearLogCapture();
+
+ testSuite->disableTestPassedMessage();
+ testSuite->run();
+
+ Assertions::assertFalse(
+ test1.checkLog(tc1Log.str()),
+ "Should not have notified on test passage! 5"
+ );
+ Assertions::assertFalse(
+ test1.checkLog(tc2Log.str()),
+ "Should not have notified on test passage! 6"
+ );
+ Assertions::assertFalse(
+ test1.checkLog(tc3Log.str()),
+ "Should not have notified on test passage! 7"
+ );
+ Assertions::assertFalse(
+ test1.checkLog(secondsParen),
+ "Should not have notified on test passage! 8"
+ );
+ Assertions::assertFalse(
+ test1.checkLog(tsLog.str()),
+ "Should not have notified on suite passage! 2"
+ );
}
}
}