mirror of
https://github.com/worldforge/worldforge
synced 2026-08-17 16:26:05 -04:00
This is a mainly automatic reformat of all code so that it uses the same format as Ember. The idea is to make development easier by removing any differences in formatting. In addition to formatting there has been a couple of fixes to smaller code issues.
300 lines
8.1 KiB
C++
300 lines
8.1 KiB
C++
#include <cppunit/extensions/TestFactoryRegistry.h>
|
|
#include <cppunit/ui/text/TestRunner.h>
|
|
#include <cppunit/extensions/HelperMacros.h>
|
|
#include <cppunit/BriefTestProgressListener.h>
|
|
#include <cppunit/TestResult.h>
|
|
|
|
#include "framework/tasks/TaskQueue.h"
|
|
#include "framework/tasks/ITask.h"
|
|
#include "framework/tasks/ITaskExecutionListener.h"
|
|
#include "framework/tasks/TaskExecutionContext.h"
|
|
#include "framework/Exception.h"
|
|
|
|
#include <Eris/EventService.h>
|
|
|
|
#include <wfmath/timestamp.h>
|
|
|
|
#include <boost/asio.hpp>
|
|
|
|
#define _GLIBCXX_USE_NANOSLEEP 1
|
|
|
|
#include <thread>
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
|
|
namespace Ember {
|
|
|
|
class CounterTask : public Tasks::ITask {
|
|
public:
|
|
|
|
int& mCounter;
|
|
|
|
int mSleep;
|
|
|
|
CounterTask(int& counter, int sleep = 0) :
|
|
mCounter(counter), mSleep(sleep) {
|
|
mCounter += 2;
|
|
}
|
|
|
|
void executeTaskInBackgroundThread(Tasks::TaskExecutionContext& context) override {
|
|
if (mSleep) {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(mSleep));
|
|
}
|
|
mCounter--;
|
|
}
|
|
|
|
/**
|
|
* @brief Executes the task in the main thread, after executeTaskInBackgroundThread() has been called.
|
|
* Since this will happen in the main thread you shouldn't do any time consuming things here, since it will lock up the rendering.
|
|
*/
|
|
bool executeTaskInMainThread() override {
|
|
mCounter--;
|
|
return true;
|
|
}
|
|
|
|
std::string getName() const override {
|
|
return "CounterTask";
|
|
}
|
|
};
|
|
|
|
struct TimeHolder {
|
|
public:
|
|
WFMath::TimeStamp time;
|
|
|
|
};
|
|
|
|
struct TimeTask : public Tasks::ITask {
|
|
|
|
TimeHolder& timeHolder;
|
|
std::unique_ptr <ITask> subtask;
|
|
Tasks::ITaskExecutionListener* listener;
|
|
|
|
explicit TimeTask(TimeHolder& timeHolder, std::unique_ptr <ITask> subtask = {}, Tasks::ITaskExecutionListener* listener = nullptr)
|
|
: timeHolder(timeHolder), subtask(std::move(subtask)), listener(listener) {
|
|
}
|
|
|
|
void executeTaskInBackgroundThread(Tasks::TaskExecutionContext& context) override {
|
|
if (subtask) {
|
|
//sleep a little so that we get different times on the tasks
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
context.executeTask(std::move(subtask), listener);
|
|
}
|
|
//sleep a little so that we get different times on the tasks
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
}
|
|
|
|
bool executeTaskInMainThread() override {
|
|
//sleep a little so that we get different times on the tasks
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
timeHolder.time = WFMath::TimeStamp::now();
|
|
return true;
|
|
}
|
|
|
|
std::string getName() const override {
|
|
return "TimeTask";
|
|
}
|
|
};
|
|
|
|
class CounterTaskBackgroundException : public CounterTask {
|
|
public:
|
|
CounterTaskBackgroundException(int& counter) : CounterTask(counter) {
|
|
}
|
|
|
|
virtual void executeTaskInBackgroundThread(Tasks::TaskExecutionContext& context) {
|
|
throw Ember::Exception();
|
|
}
|
|
};
|
|
|
|
class SimpleListener : public Tasks::ITaskExecutionListener {
|
|
public:
|
|
|
|
bool started;
|
|
bool ended;
|
|
bool error;
|
|
WFMath::TimeStamp startedTime;
|
|
WFMath::TimeStamp endedTime;
|
|
WFMath::TimeStamp errorTime;
|
|
|
|
SimpleListener() : started(false), ended(false), error(false) {
|
|
|
|
}
|
|
|
|
virtual void executionStarted() {
|
|
startedTime = WFMath::TimeStamp::now();
|
|
started = true;
|
|
}
|
|
|
|
virtual void executionEnded() {
|
|
endedTime = WFMath::TimeStamp::now();
|
|
ended = true;
|
|
}
|
|
|
|
virtual void executionError(const Ember::Exception& exception) {
|
|
errorTime = WFMath::TimeStamp::now();
|
|
error = true;
|
|
}
|
|
};
|
|
|
|
class TaskTestCase : public CppUnit::TestFixture {
|
|
CPPUNIT_TEST_SUITE(TaskTestCase);
|
|
CPPUNIT_TEST(testSimpleTaskRun);
|
|
CPPUNIT_TEST(testSimpleTaskRunAndPoll);
|
|
CPPUNIT_TEST(testSimpleTaskRunTwoExecs);
|
|
CPPUNIT_TEST(testSimpleTaskRunTwoTasks);
|
|
CPPUNIT_TEST(testSimpleTaskRunTwoTasksTwoExecs);
|
|
CPPUNIT_TEST(testListener);
|
|
CPPUNIT_TEST(testBackgroundException);
|
|
CPPUNIT_TEST(testTaskOrder);
|
|
CPPUNIT_TEST(testSubTaskOrder);
|
|
|
|
CPPUNIT_TEST_SUITE_END();
|
|
|
|
boost::asio::io_service io_service;
|
|
public:
|
|
void testSimpleTaskRun() {
|
|
int counter = 0;
|
|
{
|
|
Eris::EventService es(io_service);
|
|
Tasks::TaskQueue taskQueue(1, es);
|
|
taskQueue.enqueueTask(std::make_unique<CounterTask>(counter));
|
|
}
|
|
CPPUNIT_ASSERT(counter == 0);
|
|
}
|
|
|
|
void testSimpleTaskRunAndPoll() {
|
|
int counter = 0;
|
|
{
|
|
Eris::EventService es(io_service);
|
|
Tasks::TaskQueue taskQueue(1, es);
|
|
taskQueue.enqueueTask(std::make_unique<CounterTask>(counter));
|
|
//200 ms should be enough... This isn't deterministic though.
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
es.processAllHandlers();
|
|
CPPUNIT_ASSERT(counter == 0);
|
|
}
|
|
|
|
}
|
|
|
|
void testSimpleTaskRunTwoExecs() {
|
|
int counter = 0;
|
|
{
|
|
Eris::EventService es(io_service);
|
|
Tasks::TaskQueue taskQueue(2, es);
|
|
taskQueue.enqueueTask(std::make_unique<CounterTask>(counter));
|
|
}
|
|
CPPUNIT_ASSERT(counter == 0);
|
|
}
|
|
|
|
void testSimpleTaskRunTwoTasks() {
|
|
int counter = 0;
|
|
{
|
|
Eris::EventService es(io_service);
|
|
Tasks::TaskQueue taskQueue(1, es);
|
|
taskQueue.enqueueTask(std::make_unique<CounterTask>(counter, 200));
|
|
taskQueue.enqueueTask(std::make_unique<CounterTask>(counter));
|
|
}
|
|
CPPUNIT_ASSERT(counter == 0);
|
|
}
|
|
|
|
void testSimpleTaskRunTwoTasksTwoExecs() {
|
|
int counter = 0;
|
|
{
|
|
Eris::EventService es(io_service);
|
|
Tasks::TaskQueue taskQueue(2, es);
|
|
taskQueue.enqueueTask(std::make_unique<CounterTask>(counter, 200));
|
|
taskQueue.enqueueTask(std::make_unique<CounterTask>(counter));
|
|
}
|
|
CPPUNIT_ASSERT(counter == 0);
|
|
}
|
|
|
|
void testListener() {
|
|
SimpleListener listener;
|
|
int counter = 0;
|
|
{
|
|
Eris::EventService es(io_service);
|
|
Tasks::TaskQueue taskQueue(1, es);
|
|
taskQueue.enqueueTask(std::make_unique<CounterTask>(counter), &listener);
|
|
}
|
|
CPPUNIT_ASSERT(counter == 0);
|
|
CPPUNIT_ASSERT(listener.started);
|
|
CPPUNIT_ASSERT(listener.ended);
|
|
}
|
|
|
|
void testBackgroundException() {
|
|
SimpleListener listener;
|
|
int counter = 0;
|
|
{
|
|
Eris::EventService es(io_service);
|
|
Tasks::TaskQueue taskQueue(1, es);
|
|
taskQueue.enqueueTask(std::make_unique<CounterTaskBackgroundException>(counter), &listener);
|
|
}
|
|
CPPUNIT_ASSERT(counter == 1);
|
|
CPPUNIT_ASSERT(listener.error);
|
|
}
|
|
|
|
void testTaskOrder() {
|
|
SimpleListener listener1;
|
|
SimpleListener listener2;
|
|
SimpleListener listener3;
|
|
TimeHolder time1;
|
|
TimeHolder time2;
|
|
TimeHolder time3;
|
|
{
|
|
Eris::EventService es(io_service);
|
|
Tasks::TaskQueue taskQueue(1, es);
|
|
taskQueue.enqueueTask(std::make_unique<TimeTask>(time1), &listener1);
|
|
taskQueue.enqueueTask(std::make_unique<TimeTask>(time2), &listener2);
|
|
taskQueue.enqueueTask(std::make_unique<TimeTask>(time3), &listener3);
|
|
}
|
|
CPPUNIT_ASSERT(listener1.startedTime < listener2.startedTime);
|
|
CPPUNIT_ASSERT(listener2.startedTime < listener3.startedTime);
|
|
CPPUNIT_ASSERT(listener1.endedTime < listener2.endedTime);
|
|
CPPUNIT_ASSERT(listener2.endedTime < listener3.endedTime);
|
|
CPPUNIT_ASSERT(time1.time < time2.time);
|
|
CPPUNIT_ASSERT(time2.time < time3.time);
|
|
}
|
|
|
|
void testSubTaskOrder() {
|
|
SimpleListener listener1;
|
|
SimpleListener listener2;
|
|
SimpleListener listener3;
|
|
TimeHolder time1;
|
|
TimeHolder time2;
|
|
TimeHolder time3;
|
|
{
|
|
Eris::EventService es(io_service);
|
|
Tasks::TaskQueue taskQueue(1, es);
|
|
taskQueue.enqueueTask(std::make_unique<TimeTask>(time1, std::make_unique<TimeTask>(time2), &listener2), &listener1);
|
|
taskQueue.enqueueTask(std::make_unique<TimeTask>(time3), &listener3);
|
|
}
|
|
//task 1 should start before task 2
|
|
CPPUNIT_ASSERT(listener1.startedTime < listener2.startedTime);
|
|
CPPUNIT_ASSERT(listener2.startedTime < listener3.startedTime);
|
|
//However, task 2 should end before task 1
|
|
CPPUNIT_ASSERT(listener2.endedTime < listener1.endedTime);
|
|
CPPUNIT_ASSERT(listener1.endedTime < listener3.endedTime);
|
|
//And task 2 should be run in the main thread before task 1
|
|
CPPUNIT_ASSERT(time2.time < time1.time);
|
|
CPPUNIT_ASSERT(time1.time < time3.time);
|
|
}
|
|
|
|
|
|
};
|
|
|
|
}
|
|
|
|
CPPUNIT_TEST_SUITE_REGISTRATION(Ember::TaskTestCase);
|
|
|
|
int main(int argc, char** argv) {
|
|
CppUnit::TextUi::TestRunner runner;
|
|
CppUnit::TestFactoryRegistry& registry = CppUnit::TestFactoryRegistry::getRegistry();
|
|
runner.addTest(registry.makeTest());
|
|
|
|
// Shows a message as each test starts
|
|
CppUnit::BriefTestProgressListener listener;
|
|
runner.eventManager().addListener(&listener);
|
|
|
|
bool wasSuccessful = runner.run("", false);
|
|
return !wasSuccessful;
|
|
}
|
|
|