diff options
author | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
commit | 15740faf9fe9fe4be08965098bbf2947e096aeeb (patch) | |
tree | a730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Testing/ConsoleTestReporter.cpp |
Diffstat (limited to 'Runtime/Testing/ConsoleTestReporter.cpp')
-rw-r--r-- | Runtime/Testing/ConsoleTestReporter.cpp | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/Runtime/Testing/ConsoleTestReporter.cpp b/Runtime/Testing/ConsoleTestReporter.cpp new file mode 100644 index 0000000..0039448 --- /dev/null +++ b/Runtime/Testing/ConsoleTestReporter.cpp @@ -0,0 +1,216 @@ +#include "UnityPrefix.h" + +#if ENABLE_UNIT_TESTS + +#include "ConsoleTestReporter.h" +#include <algorithm> +#include <stdio.h> + +#ifdef WIN32 +#define snprintf _snprintf +#endif + +using namespace UnitTest; +using namespace std; + +static ConsoleTestReporter* g_Instance; + +static bool UnitTestLogEntryHandler (LogType type, const char* log, va_list args) +{ + Assert (g_Instance != NULL); + + // Expand message string. + char buffer[4096]; + vsnprintf (buffer, sizeof (buffer), log, args); + va_end (args); + + // If we're not currently running a test, just dump the message straight to + // stdout. This path is used for printing the test start/finish/fail messages. + if (!g_Instance->IsCurrentlyRunningTest ()) + { + fputs (buffer, stdout); + fflush (stdout); + + #if UNITY_WIN + OutputDebugString (buffer); + #endif + } + else + { + // Otherwise, feed the message into the reporter. + // Ignore debug log messages. + if (type != LogType_Debug) + g_Instance->ReportMessage (type, buffer); + } + + // Disable all normal logging. + return false; +} + +ConsoleTestReporter::ConsoleTestReporter () + : m_ShowOnlySummary (false) + , m_TestNameColumnIndex (100) + , m_ResultColumnIndex (256) + , m_IsCurrentlyRunningTest (false) + , m_CurrentTestIsFailure (false) +{ + Assert (g_Instance == NULL); + g_Instance = this; + + // Install our log override. + SetLogEntryHandler (UnitTestLogEntryHandler); +} + +ConsoleTestReporter::~ConsoleTestReporter () +{ + SetLogEntryHandler (NULL); + g_Instance = NULL; +} + +ConsoleTestReporter* ConsoleTestReporter::GetInstance () +{ + return g_Instance; +} + +void ConsoleTestReporter::ExpectLogMessage (LogType type, const char* logFragment) +{ + Assert (type != LogType_Debug); + + if (!IsCurrentlyRunningTest ()) + return; + + m_ExpectedLogMessagesForCurrentTest.push_back (LogMessage (type, logFragment)); +} + +void ConsoleTestReporter::ReportTestStart (TestDetails const& test) +{ + if (!m_ShowOnlySummary) + { + Assert (m_ResultColumnIndex > m_TestNameColumnIndex); + + char buffer[1024]; + const int suiteNameLength = strlen (test.suiteName); + Assert (suiteNameLength < sizeof(buffer) - 4); + + // Create '[TestSuite] ' string padded out with blanks up to the column + // for the test name. + memset (buffer, ' ', sizeof (buffer)); + buffer[0] = '['; + memcpy (&buffer[1], test.suiteName, suiteNameLength); + buffer[suiteNameLength + 1] = ']'; + buffer[std::min<int> (m_TestNameColumnIndex, sizeof (buffer))] = '\0'; + + // Print '[TestSuite] TestName'. + printf_console ("%s%s", buffer, test.testName); + + // Print blanks to pad out to result column. + const int numSpacesToPad = m_ResultColumnIndex - m_TestNameColumnIndex - strlen (test.testName); + memset (buffer, ' ', sizeof (buffer)); + buffer[std::min<int> (numSpacesToPad, sizeof (buffer))] = '\0'; + printf_console (buffer); + } + + m_CurrentTest = test; + m_IsCurrentlyRunningTest = true; + m_CurrentTestIsFailure = false; +} + +void ConsoleTestReporter::ReportFailure (TestDetails const& test, char const* failure) +{ + // Memorize failure. + Failure details; + details.fileName = test.filename; + details.lineNumber = test.lineNumber; + details.text = failure; + m_CurrentTestFailures.push_back (details); + + MarkCurrentTestAsFailure (); +} + +void ConsoleTestReporter::ReportMessage (LogType type, string message) +{ + // Check whether we have expected this message to come in. + for (size_t i = 0; i < m_ExpectedLogMessagesForCurrentTest.size (); ++i) + { + // Skip if type doesn't match. + if (m_ExpectedLogMessagesForCurrentTest[i].first != type) + continue; + + // Check whether the expected fragment is found in the current message. + if (message.find (m_ExpectedLogMessagesForCurrentTest[i].second) != string::npos) + { + // Remove it. We only accept one occurrence. + m_ExpectedLogMessagesForCurrentTest.erase (m_ExpectedLogMessagesForCurrentTest.begin () + i); + + // Yes, so all ok. + return; + } + } + + // Not an expected message. Record. + m_UnexpectedLogMessagesForCurrentTest.push_back (LogMessage (type, message)); + + MarkCurrentTestAsFailure (); +} + +void ConsoleTestReporter::ReportTestFinish (TestDetails const& test, float secondsElapsed) +{ + m_IsCurrentlyRunningTest = false; + + // If we are still expecting messages, fail the test. + if (!m_ExpectedLogMessagesForCurrentTest.empty ()) + MarkCurrentTestAsFailure (); + + if (!m_ShowOnlySummary) + { + // Print status. + if (m_CurrentTestIsFailure) + printf_console ("FAIL!!!!\n"); + else + printf_console ("PASS (%ims)\n", (int) (secondsElapsed * 1000.f)); + + // Print failures. + for (size_t i = 0; i < m_CurrentTestFailures.size (); ++i) + { + const Failure& failure = m_CurrentTestFailures[i]; + printf_console ("\tCHECK FAILURE: %s\n\t\t(%s:%i)\n", + failure.text.c_str (), + failure.fileName.c_str (), + failure.lineNumber); + } + + // Print unexpected messages. + for (size_t i = 0; i < m_UnexpectedLogMessagesForCurrentTest.size (); ++i) + printf_console ("\tUNEXPECTED %s: %s\n", + LogTypeToString (m_UnexpectedLogMessagesForCurrentTest[i].first), + m_UnexpectedLogMessagesForCurrentTest[i].second.c_str ()); + + // Print expected messages that didn't show. + for (size_t i = 0; i < m_ExpectedLogMessagesForCurrentTest.size (); ++i) + printf_console ("\tEXPECTED %s: %s\n", + LogTypeToString (m_ExpectedLogMessagesForCurrentTest[i].first), + m_ExpectedLogMessagesForCurrentTest[i].second.c_str ()); + } + + // Clear state of current test. + m_CurrentTestFailures.clear (); + m_UnexpectedLogMessagesForCurrentTest.clear (); + m_ExpectedLogMessagesForCurrentTest.clear (); + m_CurrentTest = TestDetails (); +} + +void ConsoleTestReporter::ReportSummary (int totalTestCount, int failedTestCount, int failureCount, float secondsElapsed) +{ + // Print counters (rely on our fail test counter since we also count unexpected messages + // as failures). + printf_console ("Ran %i tests with %i failures in %.2f seconds\n", totalTestCount, m_FailedTests.size (), secondsElapsed); + + // Print failures. + for (int i = 0; i < m_FailedTests.size (); ++i) + { + const TestDetails& test = m_FailedTests[i]; + printf_console ("\tFAILED: %s [%s]\n", test.testName, test.suiteName); + } +} + +#endif |