Run startup unit tests from Main

This commit is contained in:
Vinnie Falco
2013-08-01 15:07:25 -07:00
parent 290627d741
commit 77e0ee9006
4 changed files with 161 additions and 14 deletions

View File

@@ -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().

View File

@@ -141,6 +141,16 @@ public:
*/
struct Suite
{
String className;
String packageName;
Time whenStarted;
double secondsElapsed;
int tests;
int failures;
OwnedArray <Case, CriticalSection> 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 <Case, CriticalSection> cases;
// for convenience
String getSuiteName () const noexcept
{
String s;
s << packageName << "::" << className;
return s;
}
};
/** The type of a list of tests.

View File

@@ -17,8 +17,103 @@
*/
//==============================================================================
Main::Main (int argc, char const* const* argv)
: m_argc (argc)
, m_argv (argv)
Static::Storage <Atomic <Main*>, 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);
}

View File

@@ -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 <Atomic <Main*>, Main> s_instance;
};
#endif