From 83c3beb2ed7a75070fc44ffb4e99e2d5c1dd7353 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Thu, 1 Aug 2013 15:07:25 -0700 Subject: [PATCH] Run startup unit tests from Main --- Builds/VisualStudio2012/BeastConfig.h | 2 + .../beast_core/diagnostic/beast_UnitTest.h | 24 +++-- modules/beast_core/misc/beast_Main.cpp | 101 +++++++++++++++++- modules/beast_core/misc/beast_Main.h | 48 ++++++++- 4 files changed, 161 insertions(+), 14 deletions(-) diff --git a/Builds/VisualStudio2012/BeastConfig.h b/Builds/VisualStudio2012/BeastConfig.h index 0a19d0402d..602786b529 100644 --- a/Builds/VisualStudio2012/BeastConfig.h +++ b/Builds/VisualStudio2012/BeastConfig.h @@ -110,6 +110,8 @@ #define BEAST_BOOST_IS_AVAILABLE 0 #endif +//------------------------------------------------------------------------------ + /** Bind source configuration. Set one of these to manually force a particular implementation of bind(). diff --git a/modules/beast_core/diagnostic/beast_UnitTest.h b/modules/beast_core/diagnostic/beast_UnitTest.h index e1c1527fdf..25bce4aa91 100644 --- a/modules/beast_core/diagnostic/beast_UnitTest.h +++ b/modules/beast_core/diagnostic/beast_UnitTest.h @@ -141,6 +141,16 @@ public: */ struct Suite { + String className; + String packageName; + Time whenStarted; + double secondsElapsed; + int tests; + int failures; + OwnedArray cases; + + //---- + Suite (String const& className_, String const& packageName_) : className (className_) , packageName (packageName_) @@ -151,13 +161,13 @@ public: { } - String className; - String packageName; - Time whenStarted; - double secondsElapsed; - int tests; - int failures; - OwnedArray cases; + // for convenience + String getSuiteName () const noexcept + { + String s; + s << packageName << "::" << className; + return s; + } }; /** The type of a list of tests. diff --git a/modules/beast_core/misc/beast_Main.cpp b/modules/beast_core/misc/beast_Main.cpp index 9db4c19802..2286286efa 100644 --- a/modules/beast_core/misc/beast_Main.cpp +++ b/modules/beast_core/misc/beast_Main.cpp @@ -17,8 +17,103 @@ */ //============================================================================== -Main::Main (int argc, char const* const* argv) - : m_argc (argc) - , m_argv (argv) +Static::Storage , Main> Main::s_instance; + +Main::Main () { + bool const replaced = s_instance->compareAndSetBool (this, nullptr); + + // If this happens it means there are two instances of Main! + if (! replaced) + FatalError ("Multiple instances of Main", __FILE__, __LINE__); +} + +Main::~Main () +{ + s_instance->set (nullptr); +} + +Main& Main::getInstance () +{ + bassert (s_instance->get () != nullptr); + + return *s_instance->get (); +} + +void Main::runStartupUnitTests () +{ + struct StartupUnitTests : UnitTests + { + void logMessage (String const&) + { + // Intentionally do nothing, we don't want + // to see the extra output for startup tests. + } + + void log (String const& message) + { +#if BEAST_MSVC + if (beast_isRunningUnderDebugger ()) + Logger::outputDebugString (message); +#endif + + std::cerr << message.toStdString () << std::endl; + } + + void reportCase (String const& suiteName, UnitTest::Case const& testcase) + { + String s; + s << suiteName << " (" << testcase.name << ") produced " << + String (testcase.failures) << + ((testcase.failures == 1) ? " failure." : " failures."); + log (s); + } + + void reportSuite (UnitTest::Suite const& suite) + { + if (suite.failures > 0) + { + String const suiteName = suite.getSuiteName (); + + for (int i = 0; i < suite.cases.size (); ++i) + { + UnitTest::Case const& testcase (*suite.cases [i]); + + if (testcase.failures > 0) + reportCase (suiteName, testcase); + } + } + } + + void reportSuites (UnitTests::Results const& results) + { + for (int i = 0; i < results.suites.size (); ++i) + reportSuite (*results.suites [i]); + } + + void reportResults () + { + reportSuites (getResults ()); + } + }; + + StartupUnitTests tests; + + tests.runStartupTests (); + + if (tests.anyTestsFailed ()) + { + tests.reportResults (); + + tests.log ("Terminating due to failed startup tests"); + + Process::terminate (); + } +} + +int Main::runFromMain (int argc, char const* const* argv) +{ + runStartupUnitTests (); + + return run (argc, argv); } diff --git a/modules/beast_core/misc/beast_Main.h b/modules/beast_core/misc/beast_Main.h index a2951c65a3..7a98c8579a 100644 --- a/modules/beast_core/misc/beast_Main.h +++ b/modules/beast_core/misc/beast_Main.h @@ -21,19 +21,59 @@ #define BEAST_MAIN_H_INCLUDED /** Represents a command line program's entry point. + + To use this, derive your class from @ref Main and implement the + function run (); */ class BEAST_API Main : Uncopyable { public: - Main (int argc, char const* const* argv); + Main (); - int getExitCode () const; + virtual ~Main (); + /** Run the program. + + You should call this from your @ref main function. Don't put + anything else in there. Instead, do it in your class derived + from Main. For example: + + @code + + struct MyProgram : Main + { + int run (int argc, char const* const* argv) + { + std::cout << "Hello, world!" << std::endl; + return 0; + } + }; + + int main (int argc, char const* const* argv) + { + MyProgram program; + + return program.runFromMain (argc, argv); + } + + @endcode + */ + int runFromMain (int argc, char const* const* argv); + + /** Retrieve the instance of the program. */ static Main& getInstance (); protected: - int const m_argc; - char const* const* const m_argv; + /** Entry point for running the program. + Subclasses provide the implementation. + */ + virtual int run (int argc, char const* const* argv) = 0; + +private: + void runStartupUnitTests (); + +private: + static Static::Storage , Main> s_instance; }; #endif