New unit_test framework:

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

View File

@@ -19,8 +19,6 @@
#include "../ServiceQueue.h"
#include "../../../modules/beast_core/beast_core.h" // for UnitTest
namespace beast {
class ServiceQueueBase::ScopedServiceThread : public List <ScopedServiceThread>::Node
@@ -185,275 +183,4 @@ bool ServiceQueueBase::empty()
//
ThreadLocalValue <ServiceQueueBase*> ServiceQueueBase::s_service;
//------------------------------------------------------------------------------
namespace detail {
//------------------------------------------------------------------------------
class ServiceQueueTimingTests
: public UnitTest
{
public:
class Stopwatch
{
public:
Stopwatch () { start(); }
void start () { m_startTime = Time::getHighResolutionTicks (); }
double getElapsed ()
{
int64 const now = Time::getHighResolutionTicks();
return Time::highResolutionTicksToSeconds (now - m_startTime);
}
private:
int64 m_startTime;
};
static int const callsPerThread = 50000;
//--------------------------------------------------------------------------
template <typename ServiceType>
struct Consumer : Thread
{
ServiceType& m_service;
Random m_random;
String m_string;
Consumer (int id, int64 seedValue, ServiceType& service)
: Thread ("C#" + String::fromNumber (id))
, m_service (service)
, m_random (seedValue)
{ startThread(); }
~Consumer ()
{ stopThread(); }
static Consumer*& thread()
{
static ThreadLocalValue <Consumer*> local;
return local.get();
}
static void stop_one ()
{ thread()->signalThreadShouldExit(); }
static void handler ()
{ thread()->do_handler(); }
void do_handler()
{
String const s (String::fromNumber (m_random.nextInt()));
m_string += s;
if (m_string.length() > 100)
m_string = String::empty;
}
void run ()
{
thread() = this;
while (! threadShouldExit())
m_service.run_one();
}
};
//--------------------------------------------------------------------------
template <typename ServiceType>
struct Producer : Thread
{
ServiceType& m_service;
Random m_random;
String m_string;
Producer (int id, int64 seedValue, ServiceType& service)
: Thread ("P#" + String::fromNumber (id))
, m_service (service)
, m_random (seedValue)
{ }
~Producer ()
{ stopThread(); }
void run ()
{
for (std::size_t i = 0; i < callsPerThread; ++i)
{
String const s (String::fromNumber (m_random.nextInt()));
m_string += s;
if (m_string.length() > 100)
m_string = String::empty;
m_service.dispatch (bind (&Consumer<ServiceType>::handler));
}
}
};
//--------------------------------------------------------------------------
template <typename Allocator>
void testThreads (std::size_t nConsumers, std::size_t nProducers)
{
beginTestCase (String::fromNumber (nConsumers) + " consumers, " +
String::fromNumber (nProducers) + " producers, " +
"Allocator = " + std::string(typeid(Allocator).name()));
typedef ServiceQueueType <Allocator> ServiceType;
ServiceType service (nConsumers);
std::vector <ScopedPointer <Consumer <ServiceType> > > consumers;
std::vector <ScopedPointer <Producer <ServiceType> > > producers;
consumers.reserve (nConsumers);
producers.reserve (nProducers);
for (std::size_t i = 0; i < nConsumers; ++i)
consumers.push_back (new Consumer <ServiceType> (i + 1,
random().nextInt64(), service));
for (std::size_t i = 0; i < nProducers; ++i)
producers.push_back (new Producer <ServiceType> (i + 1,
random().nextInt64(), service));
Stopwatch t;
for (std::size_t i = 0; i < producers.size(); ++i)
producers[i]->startThread();
for (std::size_t i = 0; i < producers.size(); ++i)
producers[i]->waitForThreadToExit();
for (std::size_t i = 0; i < consumers.size(); ++i)
service.dispatch (bind (&Consumer <ServiceType>::stop_one));
for (std::size_t i = 0; i < consumers.size(); ++i)
consumers[i]->waitForThreadToExit();
double const seconds (t.getElapsed());
logMessage (String (seconds, 2) + " seconds");
pass();
}
void runTest()
{
#if 1
testThreads <std::allocator<char> > (1, 1);
testThreads <std::allocator<char> > (1, 4);
testThreads <std::allocator<char> > (1, 16);
testThreads <std::allocator<char> > (4, 1);
testThreads <std::allocator<char> > (8, 16);
#endif
#if 0
testThreads <detail::ServiceQueueAllocator<char> > (1, 1);
testThreads <detail::ServiceQueueAllocator<char> > (1, 4);
testThreads <detail::ServiceQueueAllocator<char> > (1, 16);
testThreads <detail::ServiceQueueAllocator<char> > (4, 1);
testThreads <detail::ServiceQueueAllocator<char> > (8, 16);
#endif
}
ServiceQueueTimingTests () : UnitTest ("ServiceQueueTiming", "beast", runManual)
{
}
};
static ServiceQueueTimingTests serviceQueueTimingTests;
//------------------------------------------------------------------------------
class ServiceQueueTests
: public UnitTest
{
public:
struct ServiceThread : Thread
{
Random m_random;
ServiceQueue& m_service;
String m_string;
ServiceThread (int id, int64 seedValue,
ServiceQueue& service)
: Thread ("#" + String::fromNumber (id))
, m_random (seedValue)
, m_service (service)
{
startThread();
}
~ServiceThread ()
{
stopThread();
}
static ServiceThread*& thread()
{
static ThreadLocalValue <ServiceThread*> local;
return local.get();
}
static void stop_one ()
{
thread()->signalThreadShouldExit();
}
static void handler ()
{
thread()->do_handler();
}
void do_handler()
{
#if 1
String const s (String::fromNumber (m_random.nextInt()));
m_string += s;
if (m_string.length() > 100)
m_string = String::empty;
#endif
}
void run ()
{
thread() = this;
while (! threadShouldExit())
m_service.run_one();
}
};
static std::size_t const totalCalls = 10000;
void testThreads (std::size_t n)
{
std::size_t const callsPerThread (totalCalls / n);
beginTestCase (String::fromNumber (n) + " threads");
ServiceQueue service (n);
std::vector <ScopedPointer <ServiceThread> > threads;
threads.reserve (n);
for (std::size_t i = 0; i < n; ++i)
threads.push_back (new ServiceThread (i + 1,
random().nextInt64(), service));
for (std::size_t i = n * callsPerThread; i; --i)
service.dispatch (bind (&ServiceThread::handler));
for (std::size_t i = 0; i < threads.size(); ++i)
service.dispatch (bind (&ServiceThread::stop_one));
for (std::size_t i = 0; i < threads.size(); ++i)
threads[i]->waitForThreadToExit();
pass();
}
void runTest()
{
testThreads (1);
testThreads (4);
testThreads (16);
}
ServiceQueueTests () : UnitTest ("ServiceQueue", "beast")
{
}
};
static ServiceQueueTests serviceQueueTests;
}
}