New unit_test framework:

* Header-only!
* No external dependencies or other beast modules
* Compilation options allow for:
  - Stand-alone application to run a single test suite
  - Stand-alone application to run a set of test suites
  - Global suite of tests inline with the host application
  - Disable test suite generation completely
* Existing tests reworked to use the new classes
This commit is contained in:
Vinnie Falco
2014-03-20 17:25:39 -07:00
parent 0bb6171a85
commit f63cf33118
114 changed files with 3259 additions and 4312 deletions

View File

@@ -51,8 +51,6 @@
#include "tests/TestPeerLogicAsyncClient.cpp"
#include "tests/TestPeerUnitTests.cpp"
#include "system/BoostUnitTests.cpp"
#include "http/HTTPParser.cpp"
#include "http/HTTPRequestParser.cpp"
#include "http/HTTPResponseParser.cpp"

View File

@@ -19,6 +19,7 @@
#include "../../../beast/asio/wrap_handler.h"
#include "../../../beast/asio/placeholders.h"
#include "../../../beast/unit_test/suite.h"
namespace beast {
namespace asio {
@@ -545,7 +546,7 @@ HTTPClientBase* HTTPClientBase::New (Journal journal,
//------------------------------------------------------------------------------
class HTTPClientTests : public UnitTest
class HTTPClient_test : public unit_test::suite
{
public:
typedef boost::system::error_code error_code;
@@ -592,35 +593,38 @@ public:
//--------------------------------------------------------------------------
void log (HTTPMessage const& m)
void print (HTTPMessage const& m)
{
for (std::size_t i = 0; i < m.headers().size(); ++i)
{
HTTPField const f (m.headers()[i]);
String s;
s = "[ '" + f.name() +
"' , '" + f.value() + "' ]";
logMessage (s);
std::stringstream ss;
log <<
"[ '" << f.name() <<
"' , '" << f.value() + "' ]";
}
}
void log (HTTPClientBase::error_type error, HTTPClientBase::value_type const& response)
void print (HTTPClientBase::error_type error,
HTTPClientBase::value_type const& response)
{
if (error != 0)
{
logMessage (String (
"HTTPClient error: '" + error.message() + "'"));
log <<
"HTTPClient error: '" + error.message() << "'";
}
else if (! response.empty ())
{
logMessage (String ("Status: ") +
String::fromNumber (response->status()));
log <<
"Status: " <<
String::fromNumber (response->status()).toStdString();
log (*response);
print (*response);
}
else
{
logMessage ("HTTPClient: no response");
log <<
"HTTPClient: no response";
}
}
@@ -628,7 +632,7 @@ public:
void handle_get (HTTPClientBase::result_type result)
{
log (result.first, result.second);
print (result.first, result.second);
}
void testSync (String const& s, double timeoutSeconds)
@@ -639,7 +643,7 @@ public:
HTTPClientBase::result_type const& result (
client->get (ParsedURL (s).url ()));
log (result.first, result.second);
print (result.first, result.second);
}
void testAsync (String const& s, double timeoutSeconds)
@@ -649,7 +653,7 @@ public:
HTTPClientBase::New (Journal(), timeoutSeconds));
client->async_get (t.get_io_service (), ParsedURL (s).url (),
beast::bind (&HTTPClientTests::handle_get, this,
beast::bind (&HTTPClient_test::handle_get, this,
beast::_1));
t.start ();
@@ -658,10 +662,8 @@ public:
//--------------------------------------------------------------------------
void runTest ()
void run ()
{
beginTestCase ("HTTPClient::get");
testSync (
"http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference.html",
5);
@@ -676,13 +678,9 @@ public:
pass ();
}
HTTPClientTests () : UnitTest ("HttpClient", "beast", runManual)
{
}
};
static HTTPClientTests httpClientTests;
BEAST_DEFINE_TESTSUITE_MANUAL(HTTPClient,beast_asio,beast);
}
}

View File

@@ -52,33 +52,9 @@
#include <boost/type_traits.hpp>
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/asio/detail/handler_invoke_helpers.hpp>
#include <boost/asio/detail/handler_cont_helpers.hpp>
// work-around for broken <boost/get_pointer.hpp>
#include "../../../beast/boost/get_pointer.h"
// Continuation hooks added in 1.54.0
#ifndef BEAST_ASIO_HAS_CONTINUATION_HOOKS
# if BOOST_VERSION >= 105400
# define BEAST_ASIO_HAS_CONTINUATION_HOOKS 1
# else
# define BEAST_ASIO_HAS_CONTINUATION_HOOKS 0
# endif
#endif
#if BEAST_ASIO_HAS_CONTINUATION_HOOKS
# include <boost/asio/detail/handler_cont_helpers.hpp>
#endif
//------------------------------------------------------------------------------
// Configure some options based on the version of boost
#if BOOST_VERSION >= 105400
# ifndef BEAST_ASIO_HAS_BUFFEREDHANDSHAKE
# define BEAST_ASIO_HAS_BUFFEREDHANDSHAKE 1
# endif
#else
# ifndef BEAST_ASIO_HAS_BUFFEREDHANDSHAKE
# define BEAST_ASIO_HAS_BUFFEREDHANDSHAKE 0
# endif
#endif
#endif

View File

@@ -1,86 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
namespace beast {
/** Test for showing information about the build of boost.
*/
class BoostUnitTests : public UnitTest
{
public:
struct BoostVersion
{
explicit BoostVersion (int value)
: vmajor (value / 100000)
, vminor ((value / 100) % 100)
, vpatch (value % 100)
{
}
String toString () const noexcept
{
return String (vmajor) + "." +
String (vminor).paddedLeft ('0', 2) + "." +
String (vpatch).paddedLeft ('0', 2);
}
int vmajor;
int vminor;
int vpatch;
};
enum
{
minimumVersion = 104700
};
// To prevent constant conditional expression warning
static int getMinimumVersion ()
{
return minimumVersion;
}
void runTest ()
{
beginTestCase ("version");
BoostVersion version (BOOST_VERSION);
logMessage (String ("BOOST_VERSION = " + version.toString ()));
logMessage (String ("BOOST_LIB_VERSION = '") + BOOST_LIB_VERSION + "'");
if (BOOST_VERSION >= getMinimumVersion ())
{
pass ();
}
else
{
fail (String ("Boost version is below ") +
BoostVersion (minimumVersion).toString ());
}
}
BoostUnitTests () : UnitTest ("boost", "beast")
{
}
};
static BoostUnitTests boostUnitTests;
}

View File

@@ -65,11 +65,14 @@ String PeerTest::Result::message () const noexcept
return m_message;
}
bool PeerTest::Result::report (UnitTest& test, bool reportPassingTests) const
bool PeerTest::Result::report (unit_test::suite& suite,
bool reportPassingTests) const
{
bool const success = test.unexpected (failed (), message ());
bool const success = suite.expect (! failed (),
message ().toStdString());
if (reportPassingTests && success)
test.logMessage (String ("pass ") + message());
suite.log <<
"pass " + message().toStdString();
return success;
}
@@ -90,14 +93,15 @@ bool PeerTest::Results::operator!= (Results const& other) const noexcept
return (client != other.client) || (server != other.server);
}
bool PeerTest::Results::report (UnitTest& test, bool beginTestCase) const
bool PeerTest::Results::report (unit_test::suite& suite,
bool beginTestCase) const
{
if (beginTestCase)
test.beginTestCase (name);
suite.testcase (name.toStdString());
bool success = true;
if (! client.report (test))
if (! client.report (suite))
success = false;
if (! server.report (test))
if (! server.report (suite))
success = false;
return success;
}

View File

@@ -20,6 +20,8 @@
#ifndef BEAST_ASIO_TESTS_PEERTEST_H_INCLUDED
#define BEAST_ASIO_TESTS_PEERTEST_H_INCLUDED
#include "../../../beast/unit_test/suite.h"
namespace beast {
namespace asio {
@@ -67,14 +69,15 @@ public:
bool timedout () const noexcept;
/** Provides a descriptive message.
This is suitable to pass to UnitTest::fail.
This is suitable to pass to suite::fail.
*/
String message () const noexcept;
/** Report the result to a UnitTest object.
/** Report the result to a testsuite.
A return value of true indicates success.
*/
bool report (UnitTest& test, bool reportPassingTests = false) const;
bool report (unit_test::suite& suite,
bool reportPassingTests = false) const;
private:
boost::system::error_code m_ec;
@@ -97,19 +100,21 @@ public:
bool operator== (Results const& other) const noexcept;
bool operator!= (Results const& other) const noexcept;
/** Report the results to a UnitTest object.
/** Report the results to a suite object.
A return value of true indicates success.
@param beginTestCase `true` to call test.beginTestCase for you
*/
bool report (UnitTest& test, bool beginTestCase = true) const;
bool report (unit_test::suite& suite, bool beginTestCase = true) const;
};
//--------------------------------------------------------------------------
/** Test two peers and return the results.
*/
template <typename Details, typename ClientLogic, typename ServerLogic, typename ClientArg, typename ServerArg>
static Results run (ClientArg const& clientArg, ServerArg const& serverArg, int timeoutSeconds = defaultTimeoutSeconds)
template <class Details, class ClientLogic, class ServerLogic,
class ClientArg, class ServerArg>
static Results run (ClientArg const& clientArg, ServerArg const& serverArg,
int timeoutSeconds = defaultTimeoutSeconds)
{
Results results;
@@ -199,42 +204,40 @@ public:
return results;
}
template <typename Details, typename ClientLogic, typename ServerLogic, class Arg>
template <class Details, class ClientLogic, class ServerLogic, class Arg>
static Results run (Arg const& arg, int timeoutSeconds = defaultTimeoutSeconds)
{
return run <Details, ClientLogic, ServerLogic, Arg, Arg> (arg, arg, timeoutSeconds);
return run <Details, ClientLogic, ServerLogic, Arg, Arg> (
arg, arg, timeoutSeconds);
}
//--------------------------------------------------------------------------
/** Reports tests of Details for all known asynchronous logic combinations to a UnitTest.
*/
template <typename Details, class Arg>
static void report_async (UnitTest& test, Arg const& arg,
template <class Details, class Arg>
static void report_async (unit_test::suite& suite, Arg const& arg,
int timeoutSeconds = defaultTimeoutSeconds,
bool beginTestCase = true)
{
run <Details, TestPeerLogicAsyncClient, TestPeerLogicAsyncServer>
(arg, timeoutSeconds).report (test, beginTestCase);
(arg, timeoutSeconds).report (suite, beginTestCase);
}
/** Reports tests of Details against all known logic combinations to a UnitTest.
*/
template <typename Details, class Arg>
static void report (UnitTest& test, Arg const& arg,
int timeoutSeconds = defaultTimeoutSeconds,
bool beginTestCase = true)
template <class Details, class Arg>
static
void
report (unit_test::suite& suite, Arg const& arg,
int timeoutSeconds = defaultTimeoutSeconds, bool beginTestCase = true)
{
run <Details, TestPeerLogicSyncClient, TestPeerLogicSyncServer>
(arg, timeoutSeconds).report (test, beginTestCase);
(arg, timeoutSeconds).report (suite, beginTestCase);
run <Details, TestPeerLogicAsyncClient, TestPeerLogicSyncServer>
(arg, timeoutSeconds).report (test, beginTestCase);
(arg, timeoutSeconds).report (suite, beginTestCase);
run <Details, TestPeerLogicSyncClient, TestPeerLogicAsyncServer>
(arg, timeoutSeconds).report (test, beginTestCase);
(arg, timeoutSeconds).report (suite, beginTestCase);
report_async <Details> (test, arg, timeoutSeconds, beginTestCase);
report_async <Details> (suite, arg, timeoutSeconds, beginTestCase);
}
};

View File

@@ -17,11 +17,13 @@
*/
//==============================================================================
#include "../../../beast/unit_test/suite.h"
namespace beast {
namespace asio {
/** UnitTest for the TestPeer family of objects. */
class TestPeerUnitTests : public UnitTest
/** Test suite for the TestPeer family of objects. */
class TestPeer_test : public unit_test::suite
{
public:
@@ -31,7 +33,7 @@ public:
PeerTest::report <Details> (*this, arg, timeoutSeconds);
}
void runTest ()
void run ()
{
typedef boost::asio::ip::tcp protocol;
testDetails <TcpDetails, TcpDetails::arg_type> (protocol::v4 ());
@@ -44,13 +46,9 @@ public:
{
timeoutSeconds = 10
};
TestPeerUnitTests () : UnitTest ("TestPeer", "beast")
{
}
};
static TestPeerUnitTests testPeerUnitTests;
BEAST_DEFINE_TESTSUITE(TestPeer,beast_asio,beast);
}
}

View File

@@ -128,7 +128,6 @@
#include "diagnostic/FatalError.cpp"
#include "diagnostic/SemanticVersion.cpp"
#include "diagnostic/UnitTest.cpp"
#include "diagnostic/UnitTestUtilities.cpp"
#include "files/DirectoryIterator.cpp"
@@ -145,7 +144,6 @@
#include "memory/MemoryBlock.cpp"
#include "misc/Main.cpp"
#include "misc/Result.cpp"
#include "streams/FileInputSource.cpp"

View File

@@ -166,7 +166,6 @@ class FileOutputStream;
#include "files/TemporaryFile.h"
#include "logging/Logger.h"
#include "memory/SharedSingleton.h"
#include "misc/Main.h"
#include "misc/WindowsRegistry.h"
#include "streams/MemoryOutputStream.h"
@@ -177,7 +176,6 @@ class FileOutputStream;
#include "threads/HighResolutionTimer.h"
#include "threads/InterProcessLock.h"
#include "threads/Process.h"
#include "diagnostic/UnitTest.h"
#include "xml/XmlDocument.h"
#include "xml/XmlElement.h"
#include "diagnostic/UnitTestUtilities.h"

View File

@@ -17,8 +17,9 @@
*/
//==============================================================================
namespace beast
{
#include "../../../beast/unit_test/suite.h"
namespace beast {
//
// FatalError::Reporter
@@ -112,26 +113,16 @@ FatalError::FatalError (char const* message, char const* fileName, int lineNumbe
//------------------------------------------------------------------------------
// Yes even this class can have a unit test. It's manually triggered though.
//
class FatalErrorTests : public UnitTest
class FatalError_test : public unit_test::suite
{
public:
FatalErrorTests () : UnitTest ("FatalError", "beast", runManual)
void run ()
{
}
void runTest ()
{
beginTestCase ("raise");
// We don't really expect the program to run after this
// but the unit test is here so you can manually test it.
int shouldBeZero (1);
check_invariant (shouldBeZero == 0);
}
};
static FatalErrorTests fatalErrorTests;
BEAST_DEFINE_TESTSUITE_MANUAL(FatalError,beast_core,beast);
} // namespace beast
} // beast

View File

@@ -17,8 +17,9 @@
*/
//==============================================================================
namespace beast
{
#include "../../../beast/unit_test/suite.h"
namespace beast {
SemanticVersion::SemanticVersion ()
: majorVersion (0)
@@ -288,13 +289,9 @@ bool SemanticVersion::chopIdentifiers (StringArray* value, bool allowLeadingZero
//------------------------------------------------------------------------------
class SemanticVersionTests : public UnitTest
class SemanticVersion_test: public unit_test::suite
{
public:
SemanticVersionTests () : UnitTest ("SemanticVersion", "beast")
{
}
void checkPass (String const& input, bool shouldPass = true)
{
SemanticVersion v;
@@ -377,7 +374,7 @@ public:
void testParse ()
{
beginTestCase ("parse");
testcase ("parse");
check ("0.0.0");
check ("1.2.3");
@@ -511,7 +508,7 @@ public:
checkLess ("0.9.9", "1.0.0");
}
void runTest ()
void run ()
{
testParse ();
testValues ();
@@ -519,6 +516,6 @@ public:
}
};
static SemanticVersionTests semanticVersionTests;
BEAST_DEFINE_TESTSUITE(SemanticVersion,beast_core,beast);
} // namespace beast
} // beast

View File

@@ -1,484 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Portions of this file are from JUCE.
Copyright (c) 2013 - Raw Material Software Ltd.
Please visit http://www.juce.com
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
namespace beast
{
UnitTest::UnitTest (String const& className,
String const& packageName,
When when)
: m_className (className.removeCharacters (" "))
, m_packageName (packageName.removeCharacters (" "))
, m_when (when)
, m_runner (nullptr)
{
getAllTests().add (this);
}
UnitTest::~UnitTest()
{
getAllTests().removeFirstMatchingValue (this);
}
String UnitTest::getTestName() const noexcept
{
String s;
s << m_packageName << "." << m_className;
return s;
}
String const& UnitTest::getClassName() const noexcept
{
return m_className;
}
String const& UnitTest::getPackageName() const noexcept
{
return m_packageName;
}
Journal UnitTest::journal () const
{
bassert (m_runner != nullptr);
return m_runner->journal();
}
UnitTest::TestList& UnitTest::getAllTests()
{
static TestList s_tests;
return s_tests;
}
void UnitTest::initialise()
{
}
void UnitTest::shutdown()
{
}
ScopedPointer <UnitTest::Suite>& UnitTest::run (
UnitTests* const runner)
{
bassert (runner != nullptr);
m_runner = runner;
m_random = m_runner->m_random;
m_suite = new Suite (m_className, m_packageName);
initialise();
#if 0
try
{
runTest();
}
catch (...)
{
failException ();
}
#else
runTest ();
#endif
shutdown();
finishCase ();
m_suite->secondsElapsed = RelativeTime (
Time::getCurrentTime () - m_suite->whenStarted).inSeconds ();
return m_suite;
}
void UnitTest::logMessage (String const& message)
{
m_runner->logMessage (message);
}
void UnitTest::logReport (StringArray const& report)
{
m_runner->logReport (report);
}
void UnitTest::beginTestCase (String const& name)
{
finishCase ();
String s;
s << getTestName () << " : " << name;
logMessage (s);
m_case = new Case (name, m_className);
}
bool UnitTest::expect_bool (bool trueCondition, String const& failureMessage)
{
if (trueCondition)
{
pass ();
}
else
{
fail (failureMessage);
}
return trueCondition;
}
bool UnitTest::unexpected_bool (bool falseCondition, String const& failureMessage)
{
return expect (! falseCondition, failureMessage);
}
void UnitTest::pass ()
{
// If this goes off it means you forgot to call beginTestCase()!
bassert (m_case != nullptr);
m_case->items.add (Item (true));
}
void UnitTest::fail (String const& failureMessage)
{
// If this goes off it means you forgot to call beginTestCase()!
bassert (m_case != nullptr);
Item item (false, failureMessage);
m_case->failures++;
int const caseNumber = m_case->items.add (Item (false, failureMessage));
String s;
s << "#" << String (caseNumber) << " failed: " << failureMessage;
logMessage (s);
m_runner->onFailure ();
}
void UnitTest::failException ()
{
Item item (false, "An exception was thrown");
if (m_case != nullptr)
{
m_case->failures++;
}
else
{
// This hack gives us a test case, to handle the condition where an
// exception was thrown before beginTestCase() was called.
//
beginTestCase ("Exception outside test case");
}
int const caseNumber = m_case->items.add (item);
String s;
s << "#" << String (caseNumber) << " threw an exception ";
logMessage (s);
m_runner->onFailure ();
}
Random& UnitTest::random()
{
// This method's only valid while the test is being run!
bassert (m_runner != nullptr);
return m_random;
}
//------------------------------------------------------------------------------
void UnitTest::finishCase ()
{
if (m_case != nullptr)
{
// If this goes off it means you forgot to
// report any passing test case items!
//
bassert (m_case->items.size () > 0);
m_case->secondsElapsed = RelativeTime (
Time::getCurrentTime () - m_case->whenStarted).inSeconds ();
m_suite->tests += m_case->items.size ();
m_suite->failures += m_case->failures;
m_suite->cases.add (m_case.release ());
}
}
//==============================================================================
UnitTests::JournalSink::JournalSink (UnitTests& tests)
: m_tests (tests)
{
}
void UnitTests::JournalSink::write (Journal::Severity, std::string const& text)
{
m_tests.logMessage (text);
}
//==============================================================================
UnitTests::UnitTests()
: m_assertOnFailure (false)
, m_sink (*this)
{
}
UnitTests::~UnitTests()
{
}
void UnitTests::setAssertOnFailure (bool shouldAssert) noexcept
{
m_assertOnFailure = shouldAssert;
}
UnitTests::Results const& UnitTests::getResults () const noexcept
{
return *m_results;
}
bool UnitTests::anyTestsFailed () const noexcept
{
return m_results->failures > 0;
}
UnitTests::TestList UnitTests::selectTests (
String const& match, TestList const& tests) const noexcept
{
TestList list;
list.ensureStorageAllocated (tests.size ());
int const indexOfDot = match.indexOfChar ('.');
String const package = (indexOfDot == -1) ? match : match.substring (0, indexOfDot);
String const testname = (indexOfDot == -1) ? ""
: match.substring (indexOfDot + 1, match.length () + 1);
if (package != String::empty)
{
if (testname != String::empty)
{
// "package.testname" : First test which matches
for (int i = 0; i < tests.size(); ++i)
{
UnitTest* const test = tests [i];
if (package.equalsIgnoreCase (test->getPackageName ()) &&
testname.equalsIgnoreCase (test->getClassName ()))
{
list.add (test);
break;
}
}
}
else
{
// Get all tests in the package
list = selectPackage (package, tests);
// If no trailing slash on package, try tests
if (list.size () == 0 && indexOfDot == -1)
{
// Try "package" as a testname
list = selectTest (package, tests);
}
}
}
else if (testname != String::empty)
{
list = selectTest (testname, tests);
}
else
{
// All non manual tests
for (int i = 0; i < tests.size(); ++i)
{
UnitTest* const test = tests [i];
if (test->getWhen () != UnitTest::runManual)
list.add (test);
}
}
return list;
}
UnitTests::TestList UnitTests::selectPackage (
String const& package, TestList const& tests) const noexcept
{
TestList list;
list.ensureStorageAllocated (tests.size ());
for (int i = 0; i < tests.size(); ++i)
{
UnitTest* const test = tests [i];
if (package.equalsIgnoreCase (test->getPackageName ()) &&
test->getWhen () != UnitTest::runManual)
list.add (test);
}
return list;
}
UnitTests::TestList UnitTests::selectTest (
String const& testname, TestList const& tests) const noexcept
{
TestList list;
for (int i = 0; i < tests.size(); ++i)
{
UnitTest* const test = tests [i];
if (testname.equalsIgnoreCase (test->getClassName ()))
{
list.add (test);
break;
}
}
return list;
}
UnitTests::TestList UnitTests::selectStartupTests (TestList const& tests) const noexcept
{
TestList list;
for (int i = 0; i < tests.size(); ++i)
{
UnitTest* const test = tests [i];
if (test->getWhen () == UnitTest::runStartup)
list.add (test);
}
return list;
}
void UnitTests::runSelectedTests (String const& match, TestList const& tests, int64 randomSeed)
{
runTests (selectTests (match, tests), randomSeed);
}
void UnitTests::runTests (TestList const& tests, int64 randomSeed)
{
if (randomSeed == 0)
randomSeed = Random().nextInt (0x7fffffff);
m_random = Random (randomSeed);
m_results = new Results;
for (int i = 0; i < tests.size (); ++i)
{
if (shouldAbortTests())
break;
runTest (*tests [i]);
}
m_results->secondsElapsed = RelativeTime (
Time::getCurrentTime () - m_results->whenStarted).inSeconds ();
}
void UnitTests::onFailure ()
{
// A failure occurred and the setting to assert on failures is turned on.
bassert (! m_assertOnFailure);
}
bool UnitTests::shouldAbortTests()
{
return false;
}
void UnitTests::logMessage (const String& message)
{
Logger::writeToLog (message);
}
void UnitTests::logReport (StringArray const& report)
{
for (int i = 0; i < report.size (); ++i)
logMessage (report [i]);
}
void UnitTests::runTest (UnitTest& test)
{
#if 0
try
{
#endif
ScopedPointer <UnitTest::Suite> suite (test.run (this).release ());
m_results->cases += suite->cases.size ();
m_results->tests += suite->tests;
m_results->failures += suite->failures;
m_results->suites.add (suite.release ());
#if 0
}
catch (...)
{
// Should never get here.
Throw (std::runtime_error ("unhandled exception during unit tests"));
}
#endif
}
//------------------------------------------------------------------------------
/** A UnitTest that prints the list of available unit tests.
Not an actual test (it always passes) but if you run it manually it
will print a list of the names of all available unit tests in the program.
*/
class UnitTestsPrinter : public UnitTest
{
public:
UnitTestsPrinter () : UnitTest ("print", "print", runManual)
{
}
void runTest ()
{
beginTestCase ("List available unit tests");
TestList const& list (UnitTest::getAllTests ());
for (int i = 0; i < list.size (); ++i)
{
UnitTest const& test (*list [i]);
String s;
switch (test.getWhen ())
{
default:
case UnitTest::runNormal: s << " "; break;
case UnitTest::runManual: s << "[manual] "; break;
case UnitTest::runStartup: s << "[FORCED] "; break;
};
s << test.getTestName ();
logMessage (s);
}
pass ();
}
};
static UnitTestsPrinter unitTestsPrinter;
} // namespace beast

View File

@@ -1,555 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Portions of this file are from JUCE.
Copyright (c) 2013 - Raw Material Software Ltd.
Please visit http://www.juce.com
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_UNITTEST_H_INCLUDED
#define BEAST_UNITTEST_H_INCLUDED
namespace beast
{
class UnitTests;
/** This is a base class for classes that perform a unit test.
To write a test using this class, your code should look something like this:
@code
class MyTest : public UnitTest
{
public:
MyTest() : UnitTest ("Foobar testing", "packageName") { }
void runTest()
{
beginTestCase ("Part 1");
expect (myFoobar.doesSomething());
expect (myFoobar.doesSomethingElse());
beginTestCase ("Part 2");
expect (myOtherFoobar.doesSomething());
expect (myOtherFoobar.doesSomethingElse());
//...
}
};
// This makes the unit test available in the global list
// It doesn't have to be static.
//
static MyTest myTest;
@endcode
To run one or more unit tests, use the UnitTests class.
@see UnitTests
*/
class BEAST_API UnitTest : public Uncopyable
{
public:
/** When the test should be run. */
enum When
{
/** Test will be run when @ref runAllTests is called.
@see runAllTests
*/
runNormal,
/** Test will excluded from @ref runAllTests.
The test can be manually run from @ref runTestsByName.
@see runAllTests, runTestsByName
*/
runManual,
/** Test will be additionlly forced to run on every launch.
If any failures occur, FatalError is called. The tests will
also be run from @ref runAllTests or @ref runTestsByName if
explicitly invoked.
@see FatalError
*/
runStartup
};
/** Describes a single test item.
An item created for each call to the test functions, such as @ref expect
or @expectEquals.
*/
struct Item
{
explicit Item (bool passed_, String failureMessage_ = "")
: passed (passed_)
, failureMessage (failureMessage_)
{
}
bool passed;
String failureMessage;
};
/** Describes a test case.
A test case represents a group of Item objects.
*/
struct Case
{
explicit Case (String const& name_, String const& className_)
: name (name_)
, className (className_)
, whenStarted (Time::getCurrentTime ())
, secondsElapsed (0)
, failures (0)
{
}
String name;
String className;
Time whenStarted;
double secondsElapsed;
int failures;
Array <Item, CriticalSection> items;
};
/** Contains the results of a test.
One of these objects is instantiated each time UnitTest::beginTestCase() is called, and
it contains details of the number of subsequent UnitTest::expect() calls that are
made.
*/
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_)
, whenStarted (Time::getCurrentTime ()) // hack for now
, secondsElapsed (0)
, tests (0)
, failures (0)
{
}
// for convenience
String getSuiteName () const noexcept
{
String s;
s << packageName << "::" << className;
return s;
}
};
/** The type of a list of tests. */
typedef Array <UnitTest*, CriticalSection> TestList;
//--------------------------------------------------------------------------
/** Creates a test with the given name, group, and run option.
The group is used when you want to run all tests in a particular group
instead of all tests in general. The run option allows you to write some
tests that are only available manually. For examplem, a performance unit
test that takes a long time which you might not want to run every time
you run all tests.
*/
/*
suiteName: A name
className: The name of the class that the unit test exercises
packageName: A real or pseudo "namespace" describing the general area of
functionality to which the specified class belongs.
Examples: "network", "core", "ui"
A package name can appear in multiple testsuite instances.
*/
explicit UnitTest (String const& name,
String const& group = "",
When when = runNormal);
/** Destructor. */
virtual ~UnitTest();
/** Returns the fully qualified test name in the form <package>.<class> */
String getTestName() const noexcept;
/** Returns the class name of the test. */
const String& getClassName() const noexcept;
/** Returns the package name of the test. */
String const& getPackageName () const noexcept;
/** Returns the run option of the test. */
When getWhen () const noexcept { return m_when; }
/** Returns a Journal that logs to the UnitTests. */
Journal journal () const;
/** Runs the test, using the specified UnitTests.
You shouldn't need to call this method directly - use
UnitTests::runTests() instead.
*/
ScopedPointer <Suite>& run (UnitTests* runner);
/** Returns the set of all UnitTest objects that currently exist. */
static TestList& getAllTests();
//--------------------------------------------------------------------------
/** You can optionally implement this method to set up your test.
This method will be called before runTest().
*/
virtual void initialise();
/** You can optionally implement this method to clear up after your test has been run.
This method will be called after runTest() has returned.
*/
virtual void shutdown();
/** Implement this method in your subclass to actually run your tests.
The content of your implementation should call beginTestCase() and expect()
to perform the tests.
*/
virtual void runTest() = 0;
/** Tells the system that a new subsection of tests is beginning.
This should be called from your runTest() method, and may be called
as many times as you like, to demarcate different sets of tests.
*/
void beginTestCase (String const& name);
// beginTestCase ()
/** Checks that the result of a test is true, and logs this result.
In your runTest() method, you should call this method for each condition that
you want to check, e.g.
@code
void runTest()
{
beginTestCase ("basic tests");
expect (x + y == 2);
expect (getThing() == someThing);
...etc...
}
@endcode
If Suite is true, a pass is logged; if it's false, a failure is logged.
If the failure message is specified, it will be written to the log if the test fails.
Requirements:
Condition must be explicitly convertible to bool
*/
template <class Condition>
bool expect (Condition trueCondition, String const& failureMessage = String::empty)
{
return expect_bool (!!trueCondition, failureMessage);
}
bool expect_bool (bool trueCondition, String const& failureMessage);
/** Checks that the result of a test is false, and logs this result.
This is basically the opposite of expect().
@see expect
Requirements:
Condition must be explicitly convertible to bool
*/
template <class Condition>
bool unexpected (Condition falseCondition, String const& failureMessage = String::empty)
{
return unexpected_bool (!!falseCondition, failureMessage);
}
bool unexpected_bool (bool falseCondition, String const& failureMessage);
/** Compares two values, and if they don't match, prints out a message containing the
expected and actual result values.
*/
template <class ActualType, class ExpectedType>
bool expectEquals (ActualType actual, ExpectedType expected, String failureMessage = String::empty)
{
const bool result = (actual == expected);
if (! result)
{
if (failureMessage.isNotEmpty())
failureMessage << " -- ";
failureMessage << "Expected value: " << expected << ", Actual value: " << actual;
}
return expect (result, failureMessage);
}
/** Causes the test item to pass. */
void pass ();
/** Causes the test item to fail. */
void fail (String const& failureMessage = String::empty);
/** Records an exception in the test item. */
void failException ();
//==============================================================================
/** Writes a message to the test log.
This can only be called during your runTest() method.
*/
void logMessage (const String& message);
void logReport (StringArray const& report);
/** Returns a shared RNG that all unit tests should use. */
Random& random();
private:
void finishCase ();
private:
//==============================================================================
String const m_className;
String const m_packageName;
When const m_when;
UnitTests* m_runner;
ScopedPointer <Suite> m_suite;
ScopedPointer <Case> m_case;
Random m_random;
};
//==============================================================================
/**
Runs a set of unit tests.
You can instantiate one of these objects and use it to invoke tests on a set of
UnitTest objects.
By using a subclass of UnitTests, you can intercept logging messages and
perform custom behaviour when each test completes.
@see UnitTest
*/
class BEAST_API UnitTests : public Uncopyable
{
public:
typedef UnitTest::TestList TestList;
struct Results
{
Results ()
: whenStarted (Time::getCurrentTime ())
, cases (0)
, tests (0)
, failures (0)
{
}
Time whenStarted;
double secondsElapsed;
int cases;
int tests;
int failures;
OwnedArray <UnitTest::Suite> suites;
};
/** */
UnitTests();
/** Destructor. */
virtual ~UnitTests();
/** Sets a flag to indicate whether an assertion should be triggered if a test fails.
This is true by default.
*/
void setAssertOnFailure (bool shouldAssert) noexcept;
/** Retrieve the information on all the suites that were run.
This is overwritten every time new tests are run.
*/
Results const& getResults () const noexcept;
/** Returns `true` if any test failed. */
bool anyTestsFailed () const noexcept;
//--------------------------------------------------------------------------
/** Selects zero or more tests from specified packages or test names.
The name can be in these formats:
""
<package | testname>
<package> "."
<package> "." <testname>
"." <testname>
""
An empty string will match all tests objects which are not
marked to be run manually.
<package | testname>
Selects all tests which belong to that package, excluding those
which must be run manually. If no package with that name exists,
then this will select the first test from any package which matches
the name. If the test is a manual test, it will be selected.
<package> "."
Selects all tests which belong to that package, excluding those
which must be run manually. If no package with that name exists,
then no tests will be selected.
<package> "." <testname>
Selects only the first test that matches the given testname and
package, regardless of the manual run setting. If no test with a
matching package and test name is found, then no test is selected.
"." <testname>
Selects the first test which matches the testname, even if it
is a manual test.
Some examples of names:
"beast" All unit tests in beast
"beast.File" Just the File beast unit test
".Random" The first test with the name Random
@note Matching is not case-sensitive.
@param match The string used to match tests
@param tests An optional parameter containing a list of tests to match.
*/
TestList selectTests (String const& match = "",
TestList const& tests = UnitTest::getAllTests ()) const noexcept;
/** Selects all tests which match the specified package.
Tests marked to be run manually are not included.
@note Matching is not case-sensitive.
@param match The string used to match tests
@param tests An optional parameter containing a list of tests to match.
*/
TestList selectPackage (String const& package,
TestList const& tests = UnitTest::getAllTests ()) const noexcept;
/** Selects the first test whose name matches, from any package.
This can include tests marked to be run manually.
@note Matching is not case-sensitive.
@param match The name of the test to match.
@param tests An optional parameter containing a list of tests to match.
*/
TestList selectTest (String const& testname,
TestList const& tests = UnitTest::getAllTests ()) const noexcept;
/** Selects the startup tests.
A test marked as runStartup will be forced to run on launch.
Typically these are lightweight tests that ensure the system
environment will not cause the program to exhibit undefined behavior.
@param tests An optional parameter containing a list of tests to match.
*/
TestList selectStartupTests (TestList const& tests = UnitTest::getAllTests ()) const noexcept;
/** Run a list of matching tests.
This first calls selectTests and then runTeests on the resulting list.
@param match The string used for matching.
@param tests An optional parameter containing a list of tests to match.
*/
void runSelectedTests (String const& match = "",
TestList const& tests = UnitTest::getAllTests (),
int64 randomSeed = 0);
/** Runs the specified list of tests.
@note The tests are run regardless of the run settings.
@param tests The list of tests to run.
*/
void runTests (TestList const& tests, int64 randomSeed = 0);
Journal journal ()
{
return Journal (m_sink);
}
protected:
friend class UnitTest;
/** Called on a failure. */
void onFailure ();
/** This can be overridden to let the runner know that it should abort the tests
as soon as possible, e.g. because the thread needs to stop.
*/
virtual bool shouldAbortTests ();
/** Logs a message about the current test progress.
By default this just writes the message to the Logger class, but you could override
this to do something else with the data.
*/
virtual void logMessage (String const& message);
/** Logs a report about the current test progress.
This calls logMessage for each String.
*/
virtual void logReport (StringArray const& report);
private:
void runTest (UnitTest& test);
private:
class JournalSink : public Journal::Sink, public Uncopyable
{
public:
explicit JournalSink (UnitTests& tests);
void write (Journal::Severity severity, std::string const& text);
private:
UnitTests& m_tests;
};
bool m_assertOnFailure;
ScopedPointer <Results> m_results;
Random m_random;
JournalSink m_sink;
};
} // namespace beast
#endif

View File

@@ -17,188 +17,8 @@
*/
//==============================================================================
namespace beast
{
namespace beast {
namespace UnitTestUtilities {
namespace UnitTestUtilities
{
JUnitXMLFormatter::JUnitXMLFormatter (UnitTests const& tests)
: m_tests (tests)
, m_currentTime (timeToString (Time::getCurrentTime ()))
, m_hostName (SystemStats::getComputerName ())
{
}
// This is the closest thing to documentation on JUnit XML I could find:
//
// http://junitpdfreport.sourceforge.net/managedcontent/PdfTranslation
//
String JUnitXMLFormatter::createDocumentString ()
{
UnitTests::Results const& results (m_tests.getResults ());
ScopedPointer <XmlElement> testsuites (new XmlElement ("testsuites"));
testsuites->setAttribute ("tests", String (results.tests));
if (results.failures != 0)
testsuites->setAttribute ("failures", String (results.failures));
testsuites->setAttribute ("time", secondsToString (results.secondsElapsed));
for (int i = 0; i < results.suites.size (); ++i)
{
UnitTest::Suite const& suite (*results.suites [i]);
XmlElement* testsuite (new XmlElement ("testsuite"));;
testsuite->setAttribute ("name", suite.className);
testsuite->setAttribute ("tests", String (suite.tests));
if (suite.failures != 0)
testsuite->setAttribute ("failures", String (suite.failures));
testsuite->setAttribute ("time", secondsToString (suite.secondsElapsed));
testsuite->setAttribute ("timestamp", timeToString (suite.whenStarted));
testsuite->setAttribute ("hostname", m_hostName);
testsuite->setAttribute ("package", suite.packageName);
testsuites->addChildElement (testsuite);
for (int i = 0; i < suite.cases.size (); ++i)
{
UnitTest::Case const& testCase (*suite.cases [i]);
XmlElement* testcase (new XmlElement ("testcase"));
testcase->setAttribute ("name", testCase.name);
testcase->setAttribute ("time", secondsToString (testCase.secondsElapsed));
testcase->setAttribute ("classname", suite.className);
testsuite->addChildElement (testcase);
for (int i = 0; i < testCase.items.size (); ++i)
{
UnitTest::Item const& item (testCase.items.getUnchecked (i));
if (!item.passed)
{
XmlElement* failure (new XmlElement ("failure"));
String s;
s << "#" << String (i+1) << " " << item.failureMessage;
failure->setAttribute ("message", s);
testcase->addChildElement (failure);
}
}
}
}
return testsuites->createDocument (
//"https://svn.jenkins-ci.org/trunk/hudson/dtkit/dtkit-format/dtkit-junit-model/src/main/resources/com/thalesgroup/dtkit/junit/model/xsd/junit-4.xsd",
"",
false,
true,
"UTF-8",
999);
};
String JUnitXMLFormatter::timeToString (Time const& time)
{
return time.toString (true, true, false, true);
}
String JUnitXMLFormatter::secondsToString (double seconds)
{
if (seconds < .01)
return String (seconds, 4);
else if (seconds < 1)
return String (seconds, 2);
else if (seconds < 10)
return String (seconds, 1);
else
return String (int (seconds));
}
//------------------------------------------------------------------------------
/** A unit test that always passes.
This can be useful to diagnose continuous integration systems.
*/
class PassUnitTest : public UnitTest
{
public:
PassUnitTest () : UnitTest ("Pass", "beast", runManual)
{
}
void runTest ()
{
beginTestCase ("pass");
pass ();
}
};
static PassUnitTest passUnitTest;
//------------------------------------------------------------------------------
/** A unit test that always fails.
This can be useful to diagnose continuous integration systems.
*/
class FailUnitTest : public UnitTest
{
public:
FailUnitTest () : UnitTest ("Fail", "beast", runManual)
{
}
void runTest ()
{
beginTestCase ("fail");
fail ("Intentional failure");
}
};
static FailUnitTest failUnitTest;
}
//------------------------------------------------------------------------------
class UnitTestUtilitiesTests : public UnitTest
{
public:
UnitTestUtilitiesTests () : UnitTest ("UnitTestUtilities", "beast")
{
}
void testPayload ()
{
using namespace UnitTestUtilities;
int const maxBufferSize = 4000;
int const minimumBytes = 1;
int const numberOfItems = 100;
int64 const seedValue = 50;
beginTestCase ("Payload");
Payload p1 (maxBufferSize);
Payload p2 (maxBufferSize);
for (int i = 0; i < numberOfItems; ++i)
{
p1.repeatableRandomFill (minimumBytes, maxBufferSize, seedValue);
p2.repeatableRandomFill (minimumBytes, maxBufferSize, seedValue);
expect (p1 == p2, "Should be equal");
}
}
void runTest ()
{
testPayload ();
}
};
static UnitTestUtilitiesTests unitTestUtilitiesTests;
} // namespace beast
} // UnitTestUtilities
} // beast

View File

@@ -20,11 +20,8 @@
#ifndef BEAST_UNITTESTUTILITIES_H_INCLUDED
#define BEAST_UNITTESTUTILITIES_H_INCLUDED
namespace beast
{
namespace UnitTestUtilities
{
namespace beast {
namespace UnitTestUtilities {
/** Fairly shuffle an array pseudo-randomly.
*/
@@ -102,39 +99,7 @@ public:
HeapBlock <char> data;
};
//------------------------------------------------------------------------------
/** Format unit test results in JUnit XML format.
The output can be used directly with the Jenkins CI server with
the appropriate JUnit plugin.
JUnit FAQ: http://junit.sourceforge.net/doc/faq/faq.htm
Jenkins Home: http://jenkins-ci.org/
@see UnitTest, UnitTests
*/
class JUnitXMLFormatter : public Uncopyable
{
public:
JUnitXMLFormatter (UnitTests const& tests);
String createDocumentString ();
private:
static String timeToString (Time const& time);
static String secondsToString (double seconds);
private:
UnitTests const& m_tests;
String const m_currentTime;
String const m_hostName;
};
}
} // namespace beast
} // UnitTestUtilities
} // beast
#endif

View File

@@ -21,8 +21,9 @@
*/
//==============================================================================
namespace beast
{
#include "../../../beast/unit_test/suite.h"
namespace beast {
// We need to make a shared singleton or else there are
// issues with the leak detector and order of detruction.
@@ -904,14 +905,19 @@ File File::createTempFile (const String& fileNameEnding)
//==============================================================================
class FileTests : public UnitTest
class File_test : public unit_test::suite
{
public:
FileTests() : UnitTest ("File", "beast") {}
void runTest()
template <class T1, class T2>
bool
expectEquals (T1 const& t1, T2 const& t2)
{
beginTestCase ("Reading");
return expect (t1 == t2);
}
void run()
{
testcase ("Reading");
const File home (File::getSpecialLocation (File::userHomeDirectory));
const File temp (File::getSpecialLocation (File::tempDirectory));
@@ -950,7 +956,7 @@ public:
expect (numRootsExisting > 0);
}
beginTestCase ("Writing");
testcase ("Writing");
File demoFolder (temp.getChildFile ("Beast UnitTests Temp Folder.folder"));
expect (demoFolder.deleteRecursively());
@@ -991,7 +997,7 @@ public:
expect (tempFile.exists());
expect (tempFile.getSize() == 10);
expect (std::abs ((int) (tempFile.getLastModificationTime().toMilliseconds() - Time::getCurrentTime().toMilliseconds())) < 3000);
expectEquals (tempFile.loadFileAsString(), String ("0123456789"));
expect (tempFile.loadFileAsString() == String ("0123456789"));
expect (! demoFolder.containsSubDirectories());
expectEquals (tempFile.getRelativePathFrom (demoFolder.getParentDirectory()), demoFolder.getFileName() + File::separatorString + tempFile.getFileName());
@@ -1036,7 +1042,7 @@ public:
expect (tempFile.getSize() == 10);
}
beginTestCase ("More writing");
testcase ("More writing");
expect (tempFile.appendData ("abcdefghij", 10));
expect (tempFile.getSize() == 20);
@@ -1058,6 +1064,6 @@ public:
}
};
static FileTests fileTests;
BEAST_DEFINE_TESTSUITE (File,beast_core,beast);
} // namespace beast
} // beast

View File

@@ -17,8 +17,9 @@
*/
//==============================================================================
namespace beast
{
#include "../../../beast/unit_test/suite.h"
namespace beast {
RandomAccessFile::RandomAccessFile () noexcept
: fileHandle (nullptr)
@@ -101,13 +102,9 @@ Result RandomAccessFile::flush ()
//------------------------------------------------------------------------------
class RandomAccessFileTests : public UnitTest
class RandomAccessFile_test : public unit_test::suite
{
public:
RandomAccessFileTests () : UnitTest ("RandomAccessFile", "beast")
{
}
enum
{
maxPayload = 8192
@@ -221,7 +218,9 @@ public:
int const seedValue = 50;
beginTestCase (String ("numRecords=") + String (numRecords));
std::stringstream ss;
ss << numRecords << " records";
testcase (ss.str());
// Calculate the path
File const path (File::createTempFile ("RandomAccessFile"));
@@ -264,14 +263,12 @@ public:
}
}
void runTest ()
void run ()
{
testFile (10000);
}
private:
};
static RandomAccessFileTests randomAccessFileTests;
BEAST_DEFINE_TESTSUITE(RandomAccessFile,beast_core,beast);
} // namespace beast
} // beast

View File

@@ -21,8 +21,9 @@
*/
//==============================================================================
namespace beast
{
#include "../../../beast/unit_test/suite.h"
namespace beast {
Random::Random (const int64 seedValue) noexcept
: seed (seedValue)
@@ -122,15 +123,11 @@ void Random::fillBitsRandomly (void* const buffer, size_t bytes)
//==============================================================================
class RandomTests : public UnitTest
class Random_test : public unit_test::suite
{
public:
RandomTests() : UnitTest ("Random", "beast") {}
void runTest()
void run()
{
beginTestCase ("Random");
for (int j = 10; --j >= 0;)
{
Random r;
@@ -153,6 +150,6 @@ public:
}
};
static RandomTests randomTests;
BEAST_DEFINE_TESTSUITE(Random,beast_core,beast);
} // namespace beast
} // beast

View File

@@ -1,131 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
namespace beast
{
Main* Main::s_instance;
Main::Main ()
{
// If this happens it means there are two instances of Main!
check_precondition (s_instance == nullptr);
s_instance = this;
}
Main::~Main ()
{
s_instance = nullptr;
}
Main& Main::getInstance ()
{
bassert (s_instance != nullptr);
return *s_instance;
}
int Main::runStartupUnitTests ()
{
int exitCode = EXIT_SUCCESS;
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.runTests (tests.selectStartupTests ());
if (tests.anyTestsFailed ())
{
tests.reportResults ();
tests.log ("Terminating with an error due to failed startup tests.");
exitCode = EXIT_FAILURE;
}
return exitCode;
}
int Main::runFromMain (int argc, char const* const* argv)
{
int exitCode (runStartupUnitTests ());
if (exitCode == EXIT_SUCCESS)
exitCode = run (argc, argv);
return exitCode;
}
} // namespace beast

View File

@@ -1,84 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_CORE_MAIN_H_INCLUDED
#define BEAST_CORE_MAIN_H_INCLUDED
namespace beast
{
/** Represents a command line program's entry point
To use this, derive your class from @ref Main and implement the
function run ();
*/
class Main : public Uncopyable
{
public:
Main ();
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:
/** Entry point for running the program.
Subclasses provide the implementation.
*/
virtual int run (int argc, char const* const* argv) = 0;
private:
int runStartupUnitTests ();
private:
static Main* s_instance;
};
} // namespace beast
#endif

View File

@@ -17,8 +17,9 @@
*/
//==============================================================================
namespace beast
{
#include "../../../beast/unit_test/suite.h"
namespace beast {
unsigned char const LexicalCastUtilities::s_digitTable [256] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xFF - 0x07
@@ -57,13 +58,9 @@ unsigned char const LexicalCastUtilities::s_digitTable [256] = {
//------------------------------------------------------------------------------
class LexicalCastTests : public UnitTest
class LexicalCast_test : public unit_test::suite
{
public:
LexicalCastTests () : UnitTest ("LexicalCast", "beast")
{
}
template <class IntType>
static IntType nextRandomInt (Random& r)
{
@@ -74,7 +71,7 @@ public:
void testInteger (IntType in)
{
String s;
IntType out;
IntType out (in+1);
expect (lexicalCastChecked (s, in));
expect (lexicalCastChecked (out, s));
@@ -85,9 +82,10 @@ public:
void testIntegers (Random& r)
{
{
String s;
s << "random " << typeid (IntType).name ();
beginTestCase (s);
std::stringstream ss;
ss <<
"random " << typeid (IntType).name ();
testcase (ss.str());
for (int i = 0; i < 1000; ++i)
{
@@ -97,16 +95,17 @@ public:
}
{
String s;
s << "numeric_limits <" << typeid (IntType).name () << ">";
beginTestCase (s);
std::stringstream ss;
ss <<
"numeric_limits <" << typeid (IntType).name () << ">";
testcase (ss.str());
testInteger (std::numeric_limits <IntType>::min ());
testInteger (std::numeric_limits <IntType>::max ());
}
}
void runTest ()
void run()
{
int64 const seedValue = 50;
@@ -123,6 +122,6 @@ public:
}
};
static LexicalCastTests lexicalCastTests;
BEAST_DEFINE_TESTSUITE(LexicalCast,beast_core,beast);
} // namespace beast
} // beast

View File

@@ -17,8 +17,9 @@
*/
//==============================================================================
namespace beast
{
#include "../../../beast/unit_test/suite.h"
namespace beast {
Workers::Workers (Callback& callback, String const& threadNames, int numberOfThreads)
: m_callback (callback)
@@ -222,12 +223,9 @@ void Workers::Worker::run ()
//------------------------------------------------------------------------------
class WorkersTests : public UnitTest
class Workers_test : public unit_test::suite
{
public:
WorkersTests () : UnitTest ("Workers", "beast")
{
}
struct TestCallback : Workers::Callback
{
@@ -247,11 +245,19 @@ public:
Atomic <int> count;
};
template <class T1, class T2>
bool
expectEquals (T1 const& t1, T2 const& t2)
{
return expect (t1 == t2);
}
void testThreads (int const threadCount)
{
String s;
s << "threadCount = " << String (threadCount);
beginTestCase (s);
std::stringstream ss;
ss <<
"threadCount = " << threadCount;
testcase (ss.str());
TestCallback cb (threadCount);
@@ -277,7 +283,7 @@ public:
expectEquals (count, 0);
}
void runTest ()
void run ()
{
testThreads (0);
testThreads (1);
@@ -288,6 +294,6 @@ public:
}
};
static WorkersTests workersTests;
BEAST_DEFINE_TESTSUITE(Workers,beast_core,beast);
} // namespace beast
} // beast

View File

@@ -17,11 +17,8 @@
*/
//==============================================================================
namespace beast
{
namespace detail
{
namespace beast {
namespace detail {
// Example:
//
@@ -74,7 +71,7 @@ TrackedMutexBasics::Lists& TrackedMutexBasics::getLists ()
//------------------------------------------------------------------------------
} // namespace detail
} // detail
//==============================================================================
@@ -478,88 +475,4 @@ String TrackedMutex::makeSourceLocation (char const* fileName, int lineNumber) n
return sourceLocation;
}
//==============================================================================
namespace detail
{
class TrackedMutexUnitTests : public UnitTest
{
public:
typedef TrackedMutexType <CriticalSection> Mutex;
struct LockingThread : public Thread
{
Mutex& m_m1;
Mutex& m_m2;
WaitableEvent m_start;
explicit LockingThread (String name, Mutex& m1, Mutex& m2)
: Thread (name)
, m_m1 (m1)
, m_m2 (m2)
{
startThread ();
}
void waitForStart ()
{
m_start.wait ();
}
void run ()
{
Mutex::ScopedLockType l2 (m_m2, __FILE__, __LINE__);
{
Mutex::ScopedLockType l1 (m_m1, __FILE__, __LINE__);
m_start.signal ();
{
Mutex::ScopedUnlockType ul1 (m_m1, __FILE__, __LINE__);
this->wait ();
}
}
}
};
//--------------------------------------------------------------------------
void report (String name)
{
beginTestCase (name);
StringArray report;
TrackedMutex::generateGlobalBlockedReport (report);
logReport (report);
pass ();
}
void runTest ()
{
Mutex m1 ("M1", __FILE__, __LINE__);
Mutex m2 ("M2", __FILE__, __LINE__);
{
Mutex::ScopedLockType l1 (m1, __FILE__, __LINE__);
LockingThread t1 ("T1", m1, m2);
{
Mutex::ScopedUnlockType ul1 (m1, __FILE__, __LINE__);
t1.waitForStart ();
}
report ("#1");
{
t1.notify ();
Mutex::ScopedUnlockType ul1 (m1, __FILE__, __LINE__);
t1.waitForThreadToExit ();
}
}
}
TrackedMutexUnitTests () : UnitTest ("TrackedMutex", "beast", runManual)
{
}
};
static TrackedMutexUnitTests trackedMutexUnitTests;
} // namespace detail
} // namespace beast
} // beast

View File

@@ -61,13 +61,11 @@ String ChildProcess::readAllProcessOutput()
//==============================================================================
class ChildProcessTests : public UnitTest
class ChildProcess_test : public unit_test::suite
{
public:
void runTest()
void run()
{
beginTestCase ("Child Processes");
#if BEAST_WINDOWS || BEAST_MAC || BEAST_LINUX
ChildProcess p;
@@ -87,17 +85,13 @@ public:
//expect (output.isNotEmpty());
#endif
}
// VFALCO NOTE I had to disable this test because it was leaving
// behind a zombie process and making other unit tests fail.
// It doesnt happen with a debugger attached, or if the
// unit test is run individually.
//
ChildProcessTests() : UnitTest ("ChildProcess", "beast", runManual)
{
}
};
static ChildProcessTests childProcessTests;
// VFALCO NOTE I had to disable this test because it was leaving
// behind a zombie process and making other unit tests fail.
// It doesnt happen with a debugger attached, or if the
// unit test is run individually.
//
BEAST_DEFINE_TESTSUITE_MANUAL(ChildProcess,beast_core,beast);
} // namespace beast
} // beast

View File

@@ -1,28 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright Patrick Dehne <patrick@mysonicweb.de> (www.sonicweb-radio.de)
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include "beast_vflib.h"
#include "threads/ThreadWithServiceQueue.cpp"
/** Unit tests for header only classes
*/
static beast::detail::CallQueueTests callQueueTests;
static beast::detail::ManualServiceQueueTests manualServiceQueueTests;

View File

@@ -1,29 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright Patrick Dehne <patrick@mysonicweb.de> (www.sonicweb-radio.de)
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_VFLIB_H_INCLUDED
#define BEAST_VFLIB_H_INCLUDED
#include "functor/BindHelper.h"
#include "threads/CallQueue.h"
#include "threads/ThreadWithServiceQueue.h"
#include "threads/ManualServiceQueue.h"
#include "threads/GuiServiceQueue.h"
#endif

View File

@@ -1,115 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Copyright Patrick Dehne <patrick@mysonicweb.de> (www.sonicweb-radio.de)
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_VFLIB_BINDHELPER_H_INCLUDED
#define BEAST_VFLIB_BINDHELPER_H_INCLUDED
namespace beast {
/** Calls bind() for you.
The UnaryFunction will be called with this signature:
@code
template <typename Functor>
void operator() (Functor const& f);
@endcode
Where Functor is the result of the bind.
*/
template <class UnaryFunction>
class BindHelper
{
private:
// Gets called with the bind
UnaryFunction m_f;
public:
#if BEAST_VARIADIC_MAX >= 1
template <typename Arg>
explicit BindHelper (Arg& arg)
: m_f (arg)
{ }
template <typename Arg>
explicit BindHelper (Arg const& arg)
: m_f (arg)
{ }
template <typename F>
void operator() (F const& f) const
{ m_f (f); }
#endif
#if BEAST_VARIADIC_MAX >= 2
template <typename F, class P1>
void operator() (F const& f, P1 const& p1) const
{ m_f (bind (f, p1)); }
#endif
#if BEAST_VARIADIC_MAX >= 3
template <typename F, class P1, class P2>
void operator() (F const& f, P1 const& p1, P2 const& p2) const
{ m_f (bind (f, p1, p2)); }
#endif
#if BEAST_VARIADIC_MAX >= 4
template <typename F, class P1, class P2, class P3>
void operator() (F const& f, P1 const& p1, P2 const& p2, P3 const& p3) const
{ m_f (bind (f, p1, p2, p3)); }
#endif
#if BEAST_VARIADIC_MAX >= 5
template <typename F, class P1, class P2, class P3, class P4>
void operator() (F const& f, P1 const& p1, P2 const& p2, P3 const& p3, P4 const& p4) const
{ m_f (bind (f, p1, p2, p3, p4)); }
#endif
#if BEAST_VARIADIC_MAX >= 6
template <typename F, class P1, class P2, class P3, class P4, class P5>
void operator() (F const& f, P1 const& p1, P2 const& p2, P3 const& p3, P4 const& p4, P5 const& p5) const
{ m_f (bind (f, p1, p2, p3, p4, p5)); }
#endif
#if BEAST_VARIADIC_MAX >= 7
template <typename F, class P1, class P2, class P3, class P4, class P5, class P6>
void operator() (F const& f, P1 const& p1, P2 const& p2, P3 const& p3, P4 const& p4, P5 const& p5, P6 const& p6) const
{ m_f (bind (f, p1, p2, p3, p4, p5, p6)); }
#endif
#if BEAST_VARIADIC_MAX >= 8
template <typename F, class P1, class P2, class P3, class P4, class P5, class P6, class P7>
void operator() (F const& f, P1 const& p1, P2 const& p2, P3 const& p3, P4 const& p4, P5 const& p5, P6 const& p6, P7 const& p7) const
{ m_f (bind (f, p1, p2, p3, p4, p5, p6, p7)); }
#endif
#if BEAST_VARIADIC_MAX >= 9
template <typename F, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8>
void operator() (F const& f, P1 const& p1, P2 const& p2, P3 const& p3, P4 const& p4, P5 const& p5, P6 const& p6, P7 const& p7, P8 const& p8) const
{ m_f (bind (f, p1, p2, p3, p4, p5, p6, p7, p8)); }
#endif
#if BEAST_VARIADIC_MAX >= 10
template <typename F, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9>
void operator() (F const& f, P1 const& p1, P2 const& p2, P3 const& p3, P4 const& p4, P5 const& p5, P6 const& p6, P7 const& p7, P8 const& p8, P9 const& p9) const
{ m_f (bind (f, p1, p2, p3, p4, p5, p6, p7, p8, p9)); }
#endif
};
}
#endif

View File

@@ -1,382 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Copyright Patrick Dehne <patrick@mysonicweb.de> (www.sonicweb-radio.de)
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_VFLIB_CALLQUEUE_H_INCLUDED
#define BEAST_VFLIB_CALLQUEUE_H_INCLUDED
#include "beast/Threads.h"
#include "../functor/BindHelper.h"
namespace beast {
template <class Allocator = std::allocator <char> >
class CallQueueType
: public ServiceQueueType <Allocator>
{
public:
explicit CallQueueType (const String& name,
int expectedConcurrency = 1,
Allocator alloc = Allocator())
: ServiceQueueType<Allocator>(expectedConcurrency, alloc)
, m_name (name)
, queue(*this)
, call(*this)
{
}
~CallQueueType ()
{
// Someone forget to close the queue.
bassert (m_closed.isSignaled ());
// Can't destroy queue with unprocessed calls.
bassert (ServiceQueueBase::empty ());
}
void enqueue (ServiceQueueBase::Item* item)
{
// If this goes off someone added calls
// after the queue has been closed.
bassert (!m_closed.isSignaled ());
ServiceQueueType <Allocator>::enqueue (item);
}
/** Close the queue.
Functors may not be added after this routine is called. This is used for
diagnostics, to track down spurious calls during application shutdown
or exit. Derived classes may call this if the appropriate time is known.
The queue is synchronized after it is closed.
Can still have pending calls, just can't put new ones in.
*/
virtual void close ()
{
m_closed.signal ();
ServiceQueueType <Allocator>::stop ();
}
struct BindHelperPost
{
CallQueueType<Allocator>& m_queue;
explicit BindHelperPost (CallQueueType<Allocator>& queue)
: m_queue (queue)
{ }
template <typename F>
void operator() (F const& f) const
{ m_queue.post ( F (f) ); }
};
struct BindHelperDispatch
{
CallQueueType<Allocator>& m_queue;
explicit BindHelperDispatch (CallQueueType<Allocator>& queue)
: m_queue (queue)
{ }
template <typename F>
void operator() (F const& f) const
{ m_queue.dispatch ( F (f) ); }
};
BindHelper <BindHelperPost> const queue;
BindHelper <BindHelperDispatch> const call;
private:
String m_name;
AtomicFlag m_closed;
};
typedef CallQueueType <std::allocator <char> > CallQueue;
namespace detail
{
//------------------------------------------------------------------------------
class CallQueueTests
: public UnitTest
{
public:
struct CallTracker
{
CallQueueTests *unitTest;
int c0, c1, c2, c3, c4, c5, c6, c7, c8;
int q0, q1, q2, q3, q4, q5, q6, q7, q8;
CallTracker(CallQueueTests *parent)
: unitTest(parent)
, c0(0), c1(0), c2(0), c3(0), c4(0), c5(0), c6(0), c7(0), c8(0)
, q0(0), q1(0), q2(0), q3(0), q4(0), q5(0), q6(0), q7(0), q8(0)
{
}
void doQ0() { q0++; }
void doQ1(const String& p1)
{
unitTest->expect(p1 == "p1");
q1++;
}
void doQ2(const String& p1, const String& p2)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
q2++;
}
void doQ3(const String& p1, const String& p2, const String& p3)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
unitTest->expect(p3 == "p3");
q3++;
}
void doQ4(const String& p1, const String& p2, const String& p3, const String& p4)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
unitTest->expect(p3 == "p3");
unitTest->expect(p4 == "p4");
q4++;
}
void doQ5(const String& p1, const String& p2, const String& p3, const String& p4, const String& p5)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
unitTest->expect(p3 == "p3");
unitTest->expect(p4 == "p4");
unitTest->expect(p5 == "p5");
q5++;
}
void doQ6(const String& p1, const String& p2, const String& p3, const String& p4, const String& p5, const String& p6)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
unitTest->expect(p3 == "p3");
unitTest->expect(p4 == "p4");
unitTest->expect(p5 == "p5");
unitTest->expect(p6 == "p6");
q6++;
}
void doQ7(const String& p1, const String& p2, const String& p3, const String& p4, const String& p5, const String& p6, const String& p7)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
unitTest->expect(p3 == "p3");
unitTest->expect(p4 == "p4");
unitTest->expect(p5 == "p5");
unitTest->expect(p6 == "p6");
unitTest->expect(p7 == "p7");
q7++;
}
void doQ8(const String& p1, const String& p2, const String& p3, const String& p4, const String& p5, const String& p6, const String& p7, const String& p8)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
unitTest->expect(p3 == "p3");
unitTest->expect(p4 == "p4");
unitTest->expect(p5 == "p5");
unitTest->expect(p6 == "p6");
unitTest->expect(p7 == "p7");
unitTest->expect(p8 == "p8");
q8++;
}
void doC0() { c0++; }
void doC1(const String& p1)
{
unitTest->expect(p1 == "p1");
c1++;
}
void doC2(const String& p1, const String& p2)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
c2++;
}
void doC3(const String& p1, const String& p2, const String& p3)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
unitTest->expect(p3 == "p3");
c3++;
}
void doC4(const String& p1, const String& p2, const String& p3, const String& p4)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
unitTest->expect(p3 == "p3");
unitTest->expect(p4 == "p4");
c4++;
}
void doC5(const String& p1, const String& p2, const String& p3, const String& p4, const String& p5)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
unitTest->expect(p3 == "p3");
unitTest->expect(p4 == "p4");
unitTest->expect(p5 == "p5");
c5++;
}
void doC6(const String& p1, const String& p2, const String& p3, const String& p4, const String& p5, const String& p6)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
unitTest->expect(p3 == "p3");
unitTest->expect(p4 == "p4");
unitTest->expect(p5 == "p5");
unitTest->expect(p6 == "p6");
c6++;
}
void doC7(const String& p1, const String& p2, const String& p3, const String& p4, const String& p5, const String& p6, const String& p7)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
unitTest->expect(p3 == "p3");
unitTest->expect(p4 == "p4");
unitTest->expect(p5 == "p5");
unitTest->expect(p6 == "p6");
unitTest->expect(p7 == "p7");
c7++;
}
void doC8(const String& p1, const String& p2, const String& p3, const String& p4, const String& p5, const String& p6, const String& p7, const String& p8)
{
unitTest->expect(p1 == "p1");
unitTest->expect(p2 == "p2");
unitTest->expect(p3 == "p3");
unitTest->expect(p4 == "p4");
unitTest->expect(p5 == "p5");
unitTest->expect(p6 == "p6");
unitTest->expect(p7 == "p7");
unitTest->expect(p8 == "p8");
c8++;
}
};
CallTracker m_callTracker;
void testArities ()
{
beginTestCase("Arities");
int calls = 0;
#if BEAST_VARIADIC_MAX >= 2
m_queue.queue(&CallTracker::doQ0, &m_callTracker); calls++;
m_queue.queue(&CallTracker::doC0, &m_callTracker); calls++;
#endif
#if BEAST_VARIADIC_MAX >= 3
m_queue.queue(&CallTracker::doQ1, &m_callTracker, "p1"); calls++;
m_queue.queue(&CallTracker::doC1, &m_callTracker, "p1"); calls++;
#endif
#if BEAST_VARIADIC_MAX >= 4
m_queue.queue(&CallTracker::doQ2, &m_callTracker, "p1", "p2"); calls++;
m_queue.queue(&CallTracker::doC2, &m_callTracker, "p1", "p2"); calls++;
#endif
#if BEAST_VARIADIC_MAX >= 5
m_queue.queue(&CallTracker::doQ3, &m_callTracker, "p1", "p2", "p3"); calls++;
m_queue.queue(&CallTracker::doC3, &m_callTracker, "p1", "p2", "p3"); calls++;
#endif
#if BEAST_VARIADIC_MAX >= 6
m_queue.queue(&CallTracker::doQ4, &m_callTracker, "p1", "p2", "p3", "p4"); calls++;
m_queue.queue(&CallTracker::doC4, &m_callTracker, "p1", "p2", "p3", "p4"); calls++;
#endif
#if BEAST_VARIADIC_MAX >= 7
m_queue.queue(&CallTracker::doQ5, &m_callTracker, "p1", "p2", "p3", "p4", "p5"); calls++;
m_queue.queue(&CallTracker::doC5, &m_callTracker, "p1", "p2", "p3", "p4", "p5"); calls++;
#endif
#if BEAST_VARIADIC_MAX >= 8
m_queue.queue(&CallTracker::doQ6, &m_callTracker, "p1", "p2", "p3", "p4", "p5", "p6"); calls++;
m_queue.queue(&CallTracker::doC6, &m_callTracker, "p1", "p2", "p3", "p4", "p5", "p6"); calls++;
#endif
#if BEAST_VARIADIC_MAX >= 9
m_queue.queue(&CallTracker::doQ7, &m_callTracker, "p1", "p2", "p3", "p4", "p5", "p6", "p7"); calls++;
m_queue.queue(&CallTracker::doC7, &m_callTracker, "p1", "p2", "p3", "p4", "p5", "p6", "p7"); calls++;
#endif
#if BEAST_VARIADIC_MAX >= 10
m_queue.queue(&CallTracker::doQ8, &m_callTracker, "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8"); calls++;
m_queue.queue(&CallTracker::doC8, &m_callTracker, "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8"); calls++;
#endif
std::size_t performedCalls = m_queue.poll();
m_queue.close();
expect (performedCalls == calls);
expect (m_callTracker.c0 == 1);
expect (m_callTracker.c1 == 1);
expect (m_callTracker.c2 == 1);
expect (m_callTracker.c3 == 1);
expect (m_callTracker.c4 == 1);
expect (m_callTracker.c5 == 1);
expect (m_callTracker.q0 == 1);
expect (m_callTracker.q1 == 1);
expect (m_callTracker.q2 == 1);
expect (m_callTracker.q3 == 1);
expect (m_callTracker.q4 == 1);
expect (m_callTracker.q5 == 1);
}
void runTest()
{
testArities();
}
CallQueueTests ()
: UnitTest ("CallQueue", "beast")
, m_queue("CallQueue Test Queue")
, m_callTracker(this)
{
}
CallQueue m_queue;
};
}
}
#endif

View File

@@ -1,72 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Copyright Patrick Dehne <patrick@mysonicweb.de> (www.sonicweb-radio.de)
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_VFLIB_GUISERVICEQUEUE_H_INCLUDED
#define BEAST_VFLIB_GUISERVICEQUEUE_H_INCLUDED
#include "AppConfig.h"
#include "modules/juce_core/juce_core.h"
#include "modules/juce_events/juce_events.h"
#include "CallQueue.h"
namespace beast {
class GuiServiceQueue
: public CallQueue
, private juce::AsyncUpdater
, private ThreadWithServiceQueue::EntryPoints
{
public:
explicit GuiServiceQueue (const String& name)
: CallQueue(name)
, m_thread(name)
{
bassert (juce::MessageManager::getInstance()->isThisTheMessageThread());
m_thread.start (this);
}
void close ()
{
m_thread.stop (true);
CallQueue::close ();
}
void enqueue (ServiceQueueBase::Item* item)
{
CallQueue::enqueue (item);
m_thread.call (&juce::AsyncUpdater::triggerAsyncUpdate, (AsyncUpdater*)this);
}
void handleAsyncUpdate()
{
poll();
}
private:
ThreadWithServiceQueue m_thread;
};
}
#endif

View File

@@ -1,165 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright Patrick Dehne <patrick@mysonicweb.de> (www.sonicweb-radio.de)
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_VFLIB_MANUALSERVICEQUEUE_H_INCLUDED
#define BEAST_VFLIB_MANUALSERVICEQUEUE_H_INCLUDED
#include "CallQueue.h"
namespace beast {
class ManualServiceQueue
: public CallQueue
{
public:
explicit ManualServiceQueue (const String& name)
: CallQueue(name)
{
}
/** Calls all functors in the queue. Returns if there are no
more functors available to run
*/
bool synchronize ()
{
if(poll() > 0)
return true;
return false;
}
};
//------------------------------------------------------------------------------
namespace detail
{
//------------------------------------------------------------------------------
class ManualServiceQueueTests
: public UnitTest
{
public:
struct CallTracker
{
ManualServiceQueueTests *unitTest;
int c0, c1;
int q0, q1;
CallTracker(ManualServiceQueueTests *parent)
: unitTest(parent)
, c0(0), c1(0)
, q0(0), q1(0)
{
}
void doQ0() { q0++; }
void doQ1(const String& p1)
{
unitTest->expect(p1 == "p1");
q1++;
}
void doC0() { c0++; }
void doC1(const String& p1)
{
unitTest->expect(p1 == "p1");
c1++;
}
};
void performEmptyQueue()
{
beginTestCase("Empty queue");
ManualServiceQueue queue("ManualServiceQueueTests");
bool doneSomething = queue.synchronize();
expect(!doneSomething);
queue.close();
}
void performCalls()
{
beginTestCase("Calls");
Random r;
r.setSeedRandomly();
ManualServiceQueue queue("ManualServiceQueueTests");
static int const batches = 1000;
for(std::size_t i=0; i<batches; i++)
{
CallTracker ct(this);
std::size_t batchSize = r.nextLargeNumber(10).toInteger();
if(batchSize > 0)
{
for(std::size_t y=0; y<batchSize; y++)
{
int callType = r.nextLargeNumber(10).toInteger();
if(callType % 2)
{
queue.queue(&CallTracker::doQ0, &ct);
queue.call(&CallTracker::doC0, &ct);
}
else
{
queue.queue(&CallTracker::doQ1, &ct, "p1");
queue.call(&CallTracker::doC1, &ct, "p1");
}
}
bool doneSomething = queue.synchronize();
expect(doneSomething);
expect ((ct.q0 + ct.q1) == batchSize);
expect ((ct.c0 + ct.c1) == batchSize);
doneSomething = queue.synchronize();
expect(!doneSomething);
}
}
queue.close ();
}
void runTest()
{
performEmptyQueue ();
performCalls ();
}
ManualServiceQueueTests () : UnitTest ("ManualServiceQueue", "beast")
{
}
};
}
}
#endif

View File

@@ -1,235 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Copyright Patrick Dehne <patrick@mysonicweb.de> (www.sonicweb-radio.de)
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include "ThreadWithServiceQueue.h"
namespace beast {
ThreadWithServiceQueue::ThreadWithServiceQueue (const String& name)
: CallQueue(name)
, m_thread(name, this)
, m_entryPoints(nullptr)
, m_calledStart(false)
, m_calledStop(false)
{
}
ThreadWithServiceQueue::~ThreadWithServiceQueue ()
{
stop(true);
}
void ThreadWithServiceQueue::start (EntryPoints* const entryPoints)
{
// start() MUST be called.
bassert (!m_calledStart);
m_calledStart = true;
m_entryPoints = entryPoints;
m_thread.startThread ();
}
void ThreadWithServiceQueue::stop (bool const wait)
{
// start() MUST be called.
bassert (m_calledStart);
if (!m_calledStop)
{
m_calledStop = true;
m_thread.signalThreadShouldExit();
// something could slip in here
close ();
}
if (wait)
m_thread.waitForThreadToExit ();
}
void ThreadWithServiceQueue::runThread ()
{
m_entryPoints->threadInit ();
while (! m_thread.threadShouldExit ())
run ();
// Perform the remaining calls in the queue
reset ();
poll ();
m_entryPoints->threadExit();
}
//------------------------------------------------------------------------------
namespace detail
{
//------------------------------------------------------------------------------
class ThreadWithServiceQueueTests
: public UnitTest
{
public:
struct ThreadWithServiceQueueRunner
: public ThreadWithServiceQueue::EntryPoints
{
ThreadWithServiceQueue m_worker;
int cCallCount, c1CallCount;
int qCallCount, q1CallCount;
int initCalled, exitCalled;
ThreadWithServiceQueueRunner()
: m_worker("ThreadWithServiceQueueRunner")
, cCallCount(0)
, c1CallCount(0)
, qCallCount(0)
, q1CallCount(0)
, initCalled(0)
, exitCalled(0)
{
}
void start()
{
m_worker.start(this);
}
void stop()
{
m_worker.stop(true);
}
void c()
{
m_worker.call(&ThreadWithServiceQueueRunner::cImpl, this);
}
void cImpl()
{
cCallCount++;
}
void c1(int p1)
{
m_worker.call(&ThreadWithServiceQueueRunner::c1Impl, this, p1);
}
void c1Impl(int p1)
{
c1CallCount++;
}
void q()
{
m_worker.queue(&ThreadWithServiceQueueRunner::qImpl, this);
}
void qImpl()
{
qCallCount++;
}
void q1(int p1)
{
m_worker.queue(&ThreadWithServiceQueueRunner::q1Impl, this, p1);
}
void q1Impl(int p1)
{
q1CallCount++;
}
void threadInit ()
{
initCalled++;
}
void threadExit ()
{
exitCalled++;
}
};
static int const calls = 1000;
void performCalls()
{
Random r;
r.setSeedRandomly();
ThreadWithServiceQueueRunner runner;
runner.start();
for(std::size_t i=0; i<calls; i++)
{
int wait = r.nextLargeNumber(10).toInteger();
if(wait % 2)
runner.q();
else
runner.q1Impl(wait);
}
for(std::size_t i=0; i<calls; i++)
{
int wait = r.nextLargeNumber(10).toInteger();
if(wait % 2)
runner.c();
else
runner.c1Impl(wait);
}
runner.stop();
expect ((runner.cCallCount + runner.c1CallCount) == calls);
expect ((runner.qCallCount + runner.q1CallCount) == calls);
expect (runner.initCalled == 1);
expect (runner.exitCalled == 1);
}
void runTest()
{
beginTestCase("Calls");
for(int i=0; i<100; i++)
performCalls ();
}
ThreadWithServiceQueueTests () : UnitTest ("ThreadWithServiceQueue", "beast")
{
}
};
static CallQueueTests callQueueTests;
static ThreadWithServiceQueueTests bindableServiceQueueTests;
}
}

View File

@@ -1,92 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Copyright Patrick Dehne <patrick@mysonicweb.de> (www.sonicweb-radio.de)
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_VFLIB_THREADWITHSERVICEQUEUE_H_INCLUDED
#define BEAST_VFLIB_THREADWITHSERVICEQUEUE_H_INCLUDED
#include "CallQueue.h"
namespace beast {
class ThreadWithServiceQueue
: public CallQueue
{
public:
/** Entry points for a ThreadWithCallQueue.
*/
class EntryPoints
{
public:
virtual ~EntryPoints () { }
virtual void threadInit () { }
virtual void threadExit () { }
};
explicit ThreadWithServiceQueue (const String& name);
~ThreadWithServiceQueue();
void start (EntryPoints* const entryPoints);
void stop (bool const wait);
/** Calls all functors in the queue. Blocks if there are no
functors available to run until more functors become
available or the queue is stopped
*/
bool synchronize ();
/** Helper class to work around ThreadWithServiceQueue and Thread both
having a run member
*/
class Worker
: public Thread
{
public:
Worker(const String& name, ThreadWithServiceQueue *parent)
: Thread(name)
, m_parent(parent)
{
}
void run()
{
m_parent->runThread();
}
private:
ThreadWithServiceQueue *m_parent;
};
void runThread ();
private:
EntryPoints* m_entryPoints;
bool m_calledStart;
bool m_calledStop;
Worker m_thread;
};
}
#endif