Big CppUnit update
+ Initial threaded test support + Integrated CppUnitShell code into TestShell git-svn-id: file:///srv/svn/repos/haiku/trunk/current@75 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -4,25 +4,28 @@
|
|||||||
#include <cppunit/SynchronizedObject.h>
|
#include <cppunit/SynchronizedObject.h>
|
||||||
#include <Locker.h>
|
#include <Locker.h>
|
||||||
|
|
||||||
/*! \brief BLocker based implementation of CppUnit::SynchronizedObject::SynchronizationObject
|
//! BLocker based implementation of CppUnit::SynchronizedObject::SynchronizationObject
|
||||||
|
/*! This class is used to serialize access to a TestResult object. You should
|
||||||
|
not need to explicitly use it anywhere in your testing code.
|
||||||
*/
|
*/
|
||||||
class LockerSyncObject : public CppUnit::SynchronizedObject::SynchronizationObject {
|
class LockerSyncObject : public CppUnit::SynchronizedObject::SynchronizationObject {
|
||||||
public:
|
public:
|
||||||
/* LockerSyncObject() : fLock(new BLocker()) {}
|
|
||||||
virtual ~LockerSyncObject() { cout << 1 << endl; delete fLock; cout << 2 << endl; }
|
|
||||||
|
|
||||||
virtual void lock() { fLock->Lock(); }
|
|
||||||
virtual void unlock() { fLock->Unlock(); }
|
|
||||||
protected:
|
|
||||||
BLocker *fLock;
|
|
||||||
*/
|
|
||||||
LockerSyncObject() {}
|
LockerSyncObject() {}
|
||||||
virtual ~LockerSyncObject() {}
|
virtual ~LockerSyncObject() {}
|
||||||
|
|
||||||
virtual void lock() { fLock.Lock(); }
|
virtual void lock() { fLock.Lock(); }
|
||||||
virtual void unlock() { fLock.Unlock(); }
|
virtual void unlock() { fLock.Unlock(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
BLocker fLock;
|
BLocker fLock;
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! Prevents the use of the copy constructor.
|
||||||
|
LockerSyncObject( const LockerSyncObject © );
|
||||||
|
|
||||||
|
//! Prevents the use of the copy operator.
|
||||||
|
void operator =( const LockerSyncObject © );
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _beos_synchronization_object_h_
|
#endif // _beos_synchronization_object_h_
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef _beos_safety_lock_
|
||||||
|
#define _beos_safety_lock_
|
||||||
|
|
||||||
|
class BLocker;
|
||||||
|
|
||||||
|
//
|
||||||
|
// The SafetyLock class is a utility class for use in actual tests
|
||||||
|
// of the BLocker interfaces. It is used to make sure that if the
|
||||||
|
// test fails and an exception is thrown with the lock held, that
|
||||||
|
// lock will be released. Without this SafetyLock, there could be
|
||||||
|
// deadlocks if one thread in a test has a failure while holding the
|
||||||
|
// lock. It should be used like so:
|
||||||
|
//
|
||||||
|
// template<class Locker> void myTestClass<Locker>::myTestFunc(void)
|
||||||
|
// {
|
||||||
|
// SafetyLock<Locker> mySafetyLock(theLocker);
|
||||||
|
// ...perform tests without worrying about holding the lock on assert...
|
||||||
|
//
|
||||||
|
class SafetyLock {
|
||||||
|
public:
|
||||||
|
SafetyLock(BLocker *locker);
|
||||||
|
virtual ~SafetyLock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
BLocker *fLocker;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _beos_safety_lock_
|
||||||
|
|
||||||
@@ -3,16 +3,30 @@
|
|||||||
|
|
||||||
#include <cppunit/TestCase.h>
|
#include <cppunit/TestCase.h>
|
||||||
#include <StorageDefs.h>
|
#include <StorageDefs.h>
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
class TestCase : public CppUnit::TestCase {
|
//! Base class for single threaded unit tests
|
||||||
|
class BTestCase : public CppUnit::TestCase {
|
||||||
public:
|
public:
|
||||||
TestCase();
|
BTestCase(std::string Name = "");
|
||||||
TestCase(std::string Name);
|
|
||||||
|
//! Displays the next sub test progress indicator (i.e. [0][1][2][3]...).
|
||||||
|
virtual void NextSubTest();
|
||||||
|
|
||||||
|
//! Starts a new sub test block (i.e. prints a newline :-)
|
||||||
|
virtual void NextSubTestBlock();
|
||||||
|
|
||||||
|
//! Saves the location of the current working directory.
|
||||||
void SaveCWD();
|
void SaveCWD();
|
||||||
|
|
||||||
|
virtual void tearDown();
|
||||||
|
|
||||||
|
//! Restores the current working directory to last directory saved by a call to SaveCWD().
|
||||||
void RestoreCWD(const char *alternate = NULL);
|
void RestoreCWD(const char *alternate = NULL);
|
||||||
protected:
|
protected:
|
||||||
bool fValidCWD;
|
bool fValidCWD;
|
||||||
char fCurrentWorkingDir[B_PATH_NAME_LENGTH+1];
|
char fCurrentWorkingDir[B_PATH_NAME_LENGTH+1];
|
||||||
|
int32 fSubTestNum;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _beos_test_case_h_
|
#endif // _beos_test_case_h_
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef _beos_test_listener_h_
|
||||||
|
#define _beos_test_listener_h_
|
||||||
|
|
||||||
|
#include <cppunit/TestListener.h>
|
||||||
|
|
||||||
|
class CppUnit::Test;
|
||||||
|
class CppUnit::TestFailure;
|
||||||
|
class CppUnit::Exception;
|
||||||
|
|
||||||
|
//! Handles printing of test information
|
||||||
|
/*! Receives notification of the beginning and end of each test,
|
||||||
|
and notification of all failures and errors. Prints out said
|
||||||
|
information in a standard format to standard output.
|
||||||
|
|
||||||
|
You should not need to explicitly use this class in any
|
||||||
|
of your tests.
|
||||||
|
*/
|
||||||
|
class BTestListener : public CppUnit::TestListener {
|
||||||
|
public:
|
||||||
|
virtual void startTest( CppUnit::Test *test );
|
||||||
|
virtual void addFailure( const CppUnit::TestFailure &failure );
|
||||||
|
virtual void endTest( CppUnit::Test *test );
|
||||||
|
protected:
|
||||||
|
bool fOkay;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _beos_test_listener_h_
|
||||||
@@ -1,17 +1,93 @@
|
|||||||
#ifndef _beos_test_shell_h_
|
#ifndef _beos_test_shell_h_
|
||||||
#define _beos_test_shell_h_
|
#define _beos_test_shell_h_
|
||||||
|
|
||||||
#include <CppUnitShell.h>
|
#include <LockerSyncObject.h>
|
||||||
|
#include <cppunit/Exception.h>
|
||||||
|
#include <cppunit/Test.h>
|
||||||
|
#include <cppunit/TestListener.h>
|
||||||
|
#include <cppunit/TestResult.h>
|
||||||
|
#include <cppunit/TestResultCollector.h>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class TestShell : public CppUnitShell {
|
// Defines SuiteFunction to be a pointer to a function that
|
||||||
|
// takes no arguments and returns a pointer to a CppUnit::Test
|
||||||
|
typedef CppUnit::Test* (*SuiteFunction)(void);
|
||||||
|
|
||||||
|
// This is just absurd to have to type...
|
||||||
|
typedef CppUnit::SynchronizedObject::SynchronizationObject SyncObject;
|
||||||
|
|
||||||
|
//! BeOS savvy command line interface for the CppUnit testing framework.
|
||||||
|
/*! This class provides a fully functional command-line testing interface
|
||||||
|
built on top of the CppUnit testing library. You add named test suites
|
||||||
|
via AddSuite(), and then call Run(), which does all the dirty work. The
|
||||||
|
user can get a list of each test installed via AddSuite(), and optionally
|
||||||
|
can opt to run only a specified set of them.
|
||||||
|
*/
|
||||||
|
class BTestShell {
|
||||||
public:
|
public:
|
||||||
TestShell(const std::string &description = "", SyncObject *syncObject = 0);
|
BTestShell(const std::string &description = "", SyncObject *syncObject = 0);
|
||||||
~TestShell();
|
|
||||||
|
// This function is used to add test suites to the list of available
|
||||||
|
// tests. A SuiteFunction is just a function that takes no parameters
|
||||||
|
// and returns a pointer to a CppUnit::Test object. Return NULL at
|
||||||
|
// your own risk :-). The name given is the name that will be presented
|
||||||
|
// when the program is run with "--list" as an argument. Usually the
|
||||||
|
// given suite would be a test suite for an entire class, but that's
|
||||||
|
// not a requirement.
|
||||||
|
void AddSuite(const std::string &name, const SuiteFunction suite);
|
||||||
|
|
||||||
|
// This is the function you call after you've added all your test
|
||||||
|
// suites with calls to AddSuite(). It runs the test, or displays
|
||||||
|
// help, or lists installed tests, or whatever, depending on the
|
||||||
|
// command-line arguments passed in.
|
||||||
int Run(int argc, char *argv[]);
|
int Run(int argc, char *argv[]);
|
||||||
// virtual void PrintDescription(int argc, char *argv[]);
|
|
||||||
const char* TestDir() const;
|
// Verbosity Level enumeration and accessor function
|
||||||
|
enum VerbosityLevel { v0, v1, v2, v3 };
|
||||||
|
VerbosityLevel Verbosity() const;
|
||||||
|
|
||||||
|
// Returns true if verbosity is high enough that individual tests are
|
||||||
|
// allowed to make noise.
|
||||||
|
bool BeVerbose() const { return Verbosity() >= v2; };
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
VerbosityLevel fVerbosityLevel;
|
||||||
|
std::set<std::string> fTestsToRun;
|
||||||
|
std::map<std::string, SuiteFunction> fTests;
|
||||||
|
CppUnit::TestResult fTestResults;
|
||||||
|
CppUnit::TestResultCollector fResultsCollector;
|
||||||
|
std::string fDescription;
|
||||||
|
|
||||||
|
// Prints a brief description of the program and a guess as to
|
||||||
|
// which Storage Kit library the app was linked with based on
|
||||||
|
// the filename of the app
|
||||||
|
virtual void PrintDescription(int argc, char *argv[]);
|
||||||
|
|
||||||
|
// Prints out command line argument instructions
|
||||||
|
void PrintHelp();
|
||||||
|
|
||||||
|
// Handles command line arguments; returns true if everything goes
|
||||||
|
// okay, false if not (or if the program just needs to terminate without
|
||||||
|
// running any tests). Modifies settings in "settings" as necessary.
|
||||||
|
bool ProcessArguments(int argc, char *argv[]);
|
||||||
|
|
||||||
|
// Makes any necessary pre-test preparations
|
||||||
|
void InitOutput();
|
||||||
|
|
||||||
|
// Prints out the test results in the proper format per
|
||||||
|
// the specified verbosity level.
|
||||||
|
void PrintResults();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BPath *fTestDir;
|
//! Prevents the use of the copy constructor.
|
||||||
};
|
BTestShell( const BTestShell © );
|
||||||
|
|
||||||
|
//! Prevents the use of the copy operator.
|
||||||
|
void operator =( const BTestShell © );
|
||||||
|
|
||||||
|
}; // class BTestShell
|
||||||
|
|
||||||
#endif // _beos_test_shell_h_
|
#endif // _beos_test_shell_h_
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#include <TestShell.h>
|
||||||
|
|
||||||
|
#define CHK CPPUNIT_ASSERT
|
||||||
|
#define RES DecodeResult
|
||||||
|
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
#ifndef _beos_thread_manager_h_
|
||||||
|
#define _beos_thread_manager_h_
|
||||||
|
|
||||||
|
#include <cppunit/Exception.h>
|
||||||
|
#include <cppunit/TestResult.h>
|
||||||
|
#include <kernel/OS.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
//class ThreadedTestCase;
|
||||||
|
class CppUnit::TestResult;
|
||||||
|
|
||||||
|
// Pointer to a function that takes no parameters and
|
||||||
|
// returns no result. All threads must be implemented
|
||||||
|
// in a function of this type.
|
||||||
|
|
||||||
|
// Helper class to handle thread management
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
class BThreadManager {
|
||||||
|
public:
|
||||||
|
typedef void (TestClass::*ThreadMethod)();
|
||||||
|
|
||||||
|
BThreadManager(std::string threadName, TestClass *object, ThreadMethod method);
|
||||||
|
~BThreadManager();
|
||||||
|
|
||||||
|
status_t LaunchThread(CppUnit::TestResult *result);
|
||||||
|
|
||||||
|
// Thread management methods
|
||||||
|
int32 Stop();
|
||||||
|
int32 WaitForThread();
|
||||||
|
bool IsRunning();
|
||||||
|
|
||||||
|
std::string getName() const { return fName; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string fName;
|
||||||
|
TestClass *fObject;
|
||||||
|
ThreadMethod fMethod;
|
||||||
|
thread_id fID;
|
||||||
|
CppUnit::TestResult *fTestResult;
|
||||||
|
|
||||||
|
static long EntryFunction(BThreadManager<TestClass, ExpectedException>* manager);
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
BThreadManager<TestClass, ExpectedException>::BThreadManager(
|
||||||
|
std::string threadName,
|
||||||
|
TestClass *object,
|
||||||
|
ThreadMethod method
|
||||||
|
)
|
||||||
|
: fName(threadName)
|
||||||
|
, fObject(object)
|
||||||
|
, fMethod(method)
|
||||||
|
, fID(0)
|
||||||
|
, fTestResult(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
BThreadManager<TestClass, ExpectedException>::~BThreadManager() {
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
int32
|
||||||
|
BThreadManager<TestClass, ExpectedException>::WaitForThread() {
|
||||||
|
int32 result = 0;
|
||||||
|
if (find_thread(NULL) != fID)
|
||||||
|
wait_for_thread(fID, &result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
int32
|
||||||
|
BThreadManager<TestClass, ExpectedException>::Stop() {
|
||||||
|
int32 result = 0;
|
||||||
|
if (find_thread(NULL) != fID) {
|
||||||
|
while (IsRunning()) {
|
||||||
|
kill(fID, SIGINT);
|
||||||
|
snooze(1000000);
|
||||||
|
}
|
||||||
|
result = WaitForThread();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
bool
|
||||||
|
BThreadManager<TestClass, ExpectedException>::IsRunning(void) {
|
||||||
|
if (fID != 0) {
|
||||||
|
thread_info info;
|
||||||
|
if (get_thread_info(fID, &info) == B_OK)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
fID = 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
status_t
|
||||||
|
BThreadManager<TestClass, ExpectedException>::LaunchThread(CppUnit::TestResult *result) {
|
||||||
|
if (IsRunning())
|
||||||
|
return B_ALREADY_RUNNING;
|
||||||
|
|
||||||
|
fTestResult = result;
|
||||||
|
fID = spawn_thread((thread_entry)(BThreadManager::EntryFunction),
|
||||||
|
fName.c_str(), B_NORMAL_PRIORITY, this);
|
||||||
|
|
||||||
|
status_t err;
|
||||||
|
if (fID == B_NO_MORE_THREADS || fID == B_NO_MEMORY) {
|
||||||
|
err = fID;
|
||||||
|
fID = 0;
|
||||||
|
} else {
|
||||||
|
err = resume_thread(fID);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
long
|
||||||
|
BThreadManager<TestClass, ExpectedException>::EntryFunction(BThreadManager<TestClass, ExpectedException> *manager) {
|
||||||
|
manager->Run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
void
|
||||||
|
BThreadManager<TestClass, ExpectedException>::Run(void) {
|
||||||
|
// These outer try/catch blocks handle unexpected exceptions.
|
||||||
|
// Said exceptions are caught and noted in the TestResult
|
||||||
|
// class, but not allowed to escape and disrupt the other
|
||||||
|
// threads that are assumingly running concurrently.
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Our parent ThreadedTestCaller should check fObject to be non-NULL,
|
||||||
|
// but we'll do it here too just to be sure.
|
||||||
|
if (!fObject)
|
||||||
|
throw CppUnit::Exception("BThreadManager::Run() -- NULL fObject pointer");
|
||||||
|
|
||||||
|
// Before running, we need to add this thread's name to
|
||||||
|
// the object's id->(name,subtestnum) map.
|
||||||
|
fObject->InitThreadInfo(fID, fName);
|
||||||
|
|
||||||
|
// This inner try/catch block is for expected exceptions.
|
||||||
|
// If we get through it without an exception, we have to
|
||||||
|
// raise a different exception that makes note of the fact
|
||||||
|
// that the exception we were expecting didn't arrive. If
|
||||||
|
// no exception is expected, then nothing is done (see
|
||||||
|
// "cppunit/TestCaller.h" for detail on the classes used
|
||||||
|
// to handle this behaviour).
|
||||||
|
try {
|
||||||
|
(fObject->*fMethod)();
|
||||||
|
} catch ( ExpectedException & ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CppUnit::ExpectedExceptionTraits<ExpectedException>::expectedException();
|
||||||
|
|
||||||
|
} catch ( CppUnit::Exception &e ) {
|
||||||
|
// Add on the thread name, then note the exception
|
||||||
|
CppUnit::Exception *threadException = new CppUnit::Exception(
|
||||||
|
std::string(e.what()) + " (thread: " + fName + ")",
|
||||||
|
e.sourceLine()
|
||||||
|
);
|
||||||
|
fTestResult->addFailure( fObject, threadException );
|
||||||
|
}
|
||||||
|
catch ( std::exception &e ) {
|
||||||
|
// Add on the thread name, then note the exception
|
||||||
|
CppUnit::Exception *threadException = new CppUnit::Exception(
|
||||||
|
std::string(e.what()) + " (thread: " + fName + ")"
|
||||||
|
);
|
||||||
|
fTestResult->addError( fObject, threadException );
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
// Add on the thread name, then note the exception
|
||||||
|
CppUnit::Exception *threadException = new CppUnit::Exception(
|
||||||
|
"caught unknown exception (thread: " + fName + ")"
|
||||||
|
);
|
||||||
|
fTestResult->addError( fObject, threadException );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
#ifndef _beos_threaded_test_caller_h_
|
||||||
|
#define _beos_threaded_test_caller_h_
|
||||||
|
|
||||||
|
//#include <memory>
|
||||||
|
#include <cppunit/TestCase.h>
|
||||||
|
#include <cppunit/TestResult.h>
|
||||||
|
#include <cppunit/TestCaller.h>
|
||||||
|
#include <ThreadManager.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
class TestResult;
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException = CppUnit::NoExceptionExpected>
|
||||||
|
class BThreadedTestCaller : public CppUnit::TestCase {
|
||||||
|
public:
|
||||||
|
/*! \brief Pointer to a test function in the given class.
|
||||||
|
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 void run(CppUnit::TestResult *result);
|
||||||
|
|
||||||
|
//! Adds a thread to the test. \c threadName must be unique to this BThreadedTestCaller.
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
BThreadedTestCaller<TestClass, ExpectedException>::BThreadedTestCaller(std::string name)
|
||||||
|
: TestCase(name)
|
||||||
|
, fOwnObject(true)
|
||||||
|
, fObject(new TestClass())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
BThreadedTestCaller<TestClass, ExpectedException>::BThreadedTestCaller(std::string name, TestClass &object)
|
||||||
|
: TestCase(name)
|
||||||
|
, fOwnObject(false)
|
||||||
|
, fObject(&object)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
BThreadedTestCaller<TestClass, ExpectedException>::BThreadedTestCaller(std::string name, TestClass *object)
|
||||||
|
: TestCase(name)
|
||||||
|
, fOwnObject(true)
|
||||||
|
, fObject(object)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
BThreadedTestCaller<TestClass, ExpectedException>::~BThreadedTestCaller() {
|
||||||
|
if (fOwnObject)
|
||||||
|
delete fObject;
|
||||||
|
for (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()) {
|
||||||
|
// Unused name, go ahead and add
|
||||||
|
fThreads[threadName] = new BThreadManager<TestClass, ExpectedException>(threadName, fObject, method);
|
||||||
|
} else {
|
||||||
|
// Duplicate name, throw an exception
|
||||||
|
throw CppUnit::Exception("BThreadedTestCaller::addThread() - Attempt to add thread under duplicated name ('"
|
||||||
|
+ threadName + "')");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
void
|
||||||
|
BThreadedTestCaller<TestClass, ExpectedException>::run(CppUnit::TestResult *result) {
|
||||||
|
result->startTest(this);
|
||||||
|
|
||||||
|
try {
|
||||||
|
setUp();
|
||||||
|
|
||||||
|
// This try/catch block should never actually have to catch
|
||||||
|
// anything (unless some bonehead passes in a NULL pointer to
|
||||||
|
// the constructor). Each BThreadManager object catches and
|
||||||
|
// handles exceptions for its respective thread, so as not
|
||||||
|
// to disrupt the others.
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Verify we have a valid object first.
|
||||||
|
if (!fObject)
|
||||||
|
throw CppUnit::Exception("BThreadedTestCaller::runTest() -- NULL fObject pointer");
|
||||||
|
|
||||||
|
// Launch all the threads.
|
||||||
|
for (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() + "'"));
|
||||||
|
// printf("Launch(%s)\n", i->second->getName().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 ) {
|
||||||
|
// Add on the a note that this exception was caught by the
|
||||||
|
// thread caller (which is a bad thing), then note the exception
|
||||||
|
CppUnit::Exception *threadException = new CppUnit::Exception(
|
||||||
|
std::string(e.what()) + " (NOTE: caught by BThreadedTestCaller)",
|
||||||
|
e.sourceLine()
|
||||||
|
);
|
||||||
|
result->addFailure( fObject, threadException );
|
||||||
|
}
|
||||||
|
catch ( std::exception &e ) {
|
||||||
|
// Add on the thread name, then note the exception
|
||||||
|
CppUnit::Exception *threadException = new CppUnit::Exception(
|
||||||
|
std::string(e.what()) + " (NOTE: caught by BThreadedTestCaller)"
|
||||||
|
);
|
||||||
|
result->addError( fObject, threadException );
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
// Add on the thread name, then note the exception
|
||||||
|
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"));
|
||||||
|
} // setUp() try/catch block
|
||||||
|
|
||||||
|
result->endTest(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TestClass, class ExpectedException>
|
||||||
|
void
|
||||||
|
BThreadedTestCaller<TestClass, ExpectedException>::setUp() {
|
||||||
|
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 "BThreadedTestCaller for " + getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _beos_threaded_test_caller_h_
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef _beos_threaded_test_case_h_
|
||||||
|
#define _beos_threaded_test_case_h_
|
||||||
|
|
||||||
|
#include <Locker.h>
|
||||||
|
#include <kernel/OS.h>
|
||||||
|
#include <TestCase.h>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
//! Base class for single threaded unit tests
|
||||||
|
class BThreadedTestCase : public BTestCase {
|
||||||
|
public:
|
||||||
|
BThreadedTestCase(std::string Name = "", std::string progressSeparator = ".");
|
||||||
|
virtual ~BThreadedTestCase();
|
||||||
|
|
||||||
|
/*! \brief Displays the next sub test progress indicator for the
|
||||||
|
thread in which it's called (i.e. [A.0][B.0][A.1][A.2][B.1]...). */
|
||||||
|
virtual void NextSubTest();
|
||||||
|
|
||||||
|
//! Saves the location of the current working directory.
|
||||||
|
void SaveCWD();
|
||||||
|
|
||||||
|
//! Restores the current working directory to last directory saved by a call to SaveCWD().
|
||||||
|
void RestoreCWD(const char *alternate = NULL);
|
||||||
|
void InitThreadInfo(thread_id id, std::string threadName);
|
||||||
|
protected:
|
||||||
|
// friend class ThreadManager<BThreadedTestCase>;
|
||||||
|
std::string fProgressSeparator;
|
||||||
|
|
||||||
|
struct ThreadSubTestInfo {
|
||||||
|
std::string name;
|
||||||
|
int32 subTestNum;
|
||||||
|
};
|
||||||
|
std::map<thread_id, ThreadSubTestInfo*> fNumberMap;
|
||||||
|
BLocker *fNumberMapLock;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _beos_threaded_test_case_h_
|
||||||
Reference in New Issue
Block a user