mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
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:
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user