#ifndef _beos_threaded_test_caller_h_
#define _beos_threaded_test_caller_h_
#include <cppunit/TestCase.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestCaller.h>
#include <TestShell.h>
#include <ThreadManager.h>
#include <map>
#include <vector>
#include <stdio.h>
class TestResult;
template <class TestClass, class ExpectedException = CppUnit::NoExceptionExpected>
class CPPUNIT_API BThreadedTestCaller : public CppUnit::TestCase {
public:
Each ThreadMethod added with addThread() is run in its own thread.
*/
typedef void (TestClass::*ThreadMethod)();
BThreadedTestCaller(std::string name);
BThreadedTestCaller(std::string name, TestClass &object);
BThreadedTestCaller(std::string name, TestClass *object);
virtual ~BThreadedTestCaller();
virtual CppUnit::TestResult *run();
virtual void run(CppUnit::TestResult *result);
void addThread(std::string threadName, ThreadMethod method);
protected:
virtual void setUp();
virtual void tearDown();
virtual std::string toString() const;
typedef std::map<std::string, BThreadManager<TestClass, ExpectedException> *> ThreadManagerMap;
bool fOwnObject;
TestClass *fObject;
ThreadManagerMap fThreads;
sem_id fThreadSem;
};
template <class TestClass, class ExpectedException>
BThreadedTestCaller<TestClass, ExpectedException>::BThreadedTestCaller(std::string name)
: TestCase(name)
, fOwnObject(true)
, fObject(new TestClass())
, fThreadSem(-1)
{
}
template <class TestClass, class ExpectedException>
BThreadedTestCaller<TestClass, ExpectedException>::BThreadedTestCaller(std::string name, TestClass &object)
: TestCase(name)
, fOwnObject(false)
, fObject(&object)
, fThreadSem(-1)
{
}
template <class TestClass, class ExpectedException>
BThreadedTestCaller<TestClass, ExpectedException>::BThreadedTestCaller(std::string name, TestClass *object)
: TestCase(name)
, fOwnObject(true)
, fObject(object)
, fThreadSem(-1)
{
}
template <class TestClass, class ExpectedException>
BThreadedTestCaller<TestClass, ExpectedException>::~BThreadedTestCaller() {
if (fOwnObject)
delete fObject;
for (typename ThreadManagerMap::iterator it = fThreads.begin(); it != fThreads.end (); ++it) {
delete it->second;
}
}
template <class TestClass, class ExpectedException>
void
BThreadedTestCaller<TestClass, ExpectedException>::addThread(std::string threadName, ThreadMethod method) {
if (fThreads.find(threadName) == fThreads.end()) {
fThreads[threadName] = new BThreadManager<TestClass, ExpectedException>(threadName, fObject, method, fThreadSem);
} else {
throw CppUnit::Exception("BThreadedTestCaller::addThread() - Attempt to add thread under duplicated name ('"
+ threadName + "')");
}
}
template <class TestClass, class ExpectedException>
CppUnit::TestResult *
BThreadedTestCaller<TestClass, ExpectedException>::run() {
CppUnit::TestResult *result = new CppUnit::TestResult;
run(result);
return result;
}
template <class TestClass, class ExpectedException>
void
BThreadedTestCaller<TestClass, ExpectedException>::run(CppUnit::TestResult *result) {
result->startTest(this);
if (fThreads.size() <= 0)
throw CppUnit::Exception("BThreadedTestCaller::run() -- No threads added to BThreadedTestCaller()");
try {
setUp();
try {
fThreadSem = create_sem(fThreads.size(), "ThreadSem");
if (fThreadSem < B_OK)
throw CppUnit::Exception("BThreadedTestCaller::run() -- Error creating fThreadSem");
for (typename ThreadManagerMap::iterator i = fThreads.begin();
i != fThreads.end ();
++i)
{
status_t err = i->second->LaunchThread(result);
if (err != B_OK)
result->addError(this, new CppUnit::Exception("Error launching thread '" + i->second->getName() + "'"));
}
status_t err;
do {
err = acquire_sem_etc(fThreadSem, fThreads.size(), B_RELATIVE_TIMEOUT, 500000);
std::vector<std::string> &list = fObject->AcquireUpdateList();
for (std::vector<std::string>::iterator i = list.begin();
i != list.end();
i++)
{
if (BTestShell::GlobalBeVerbose()) {
printf("%s", (*i).c_str());
fflush(stdout);
}
}
list.clear();
fObject->ReleaseUpdateList();
} while (err != B_OK);
release_sem_etc(fThreadSem, fThreads.size(), 0);
printf("\n");
// Wait for them all to finish, then clean up
for (ThreadManagerMap::iterator i = fThreads.begin();
i != fThreads.end ();
++i)
{
// printf("Wait(%s)...", i->second->getName().c_str());
fflush(stdout);
i->second->WaitForThread();
// printf("done\n");
delete i->second;
}
*/
fThreads.clear();
} catch ( CppUnit::Exception &e ) {
CppUnit::Exception *threadException = new CppUnit::Exception(
std::string(e.what()) + " (NOTE: caught by BThreadedTestCaller)",
e.sourceLine()
);
result->addFailure( fObject, threadException );
}
catch ( std::exception &e ) {
CppUnit::Exception *threadException = new CppUnit::Exception(
std::string(e.what()) + " (NOTE: caught by BThreadedTestCaller)"
);
result->addError( fObject, threadException );
}
catch (...) {
CppUnit::Exception *threadException = new CppUnit::Exception(
"caught unknown exception (NOTE: caught by BThreadedTestCaller)"
);
result->addError( fObject, threadException );
}
snooze(50000);
try {
tearDown();
} catch (...) {
result->addError(this, new CppUnit::Exception("tearDown() failed"));
}
} catch (...) {
result->addError(this, new CppUnit::Exception("setUp() failed"));
}
result->endTest(this);
}
template <class TestClass, class ExpectedException>
void
BThreadedTestCaller<TestClass, ExpectedException>::setUp() {
if (!fObject)
throw CppUnit::Exception("BThreadedTestCaller::runTest() -- NULL fObject pointer");
if (!fObject->RegisterForUse())
throw CppUnit::Exception("BThreadedTestCaller::runTest() -- Attempt to reuse ThreadedTestCase object already in use");
fObject->setUp();
}
template <class TestClass, class ExpectedException>
void
BThreadedTestCaller<TestClass, ExpectedException>::tearDown() {
fObject->tearDown();
}
template <class TestClass, class ExpectedException>
std::string
BThreadedTestCaller<TestClass, ExpectedException>::toString() const {
return std::string("BThreadedTestCaller for ") + getName();
}
#endif