1 #ifndef _beos_threaded_test_caller_h_
2 #define _beos_threaded_test_caller_h_
5 #include <cppunit/TestCase.h>
6 #include <cppunit/TestResult.h>
7 #include <cppunit/TestCaller.h>
9 #include <ThreadManager.h>
16 template <class TestClass
, class ExpectedException
= CppUnit::NoExceptionExpected
>
17 class CPPUNIT_API BThreadedTestCaller
: public CppUnit::TestCase
{
19 /*! \brief Pointer to a test function in the given class.
20 Each ThreadMethod added with addThread() is run in its own thread.
22 typedef void (TestClass::*ThreadMethod
)();
24 BThreadedTestCaller(std::string name
);
25 BThreadedTestCaller(std::string name
, TestClass
&object
);
26 BThreadedTestCaller(std::string name
, TestClass
*object
);
27 virtual ~BThreadedTestCaller();
29 virtual CppUnit::TestResult
*run();
30 virtual void run(CppUnit::TestResult
*result
);
32 //! Adds a thread to the test. \c threadName must be unique to this BThreadedTestCaller.
33 void addThread(std::string threadName
, ThreadMethod method
);
37 virtual void tearDown();
38 virtual std::string
toString() const;
40 typedef std::map
<std::string
, BThreadManager
<TestClass
, ExpectedException
> *> ThreadManagerMap
;
44 ThreadManagerMap fThreads
;
51 template <class TestClass
, class ExpectedException
>
52 BThreadedTestCaller
<TestClass
, ExpectedException
>::BThreadedTestCaller(std::string name
)
55 , fObject(new TestClass())
60 template <class TestClass
, class ExpectedException
>
61 BThreadedTestCaller
<TestClass
, ExpectedException
>::BThreadedTestCaller(std::string name
, TestClass
&object
)
69 template <class TestClass
, class ExpectedException
>
70 BThreadedTestCaller
<TestClass
, ExpectedException
>::BThreadedTestCaller(std::string name
, TestClass
*object
)
78 template <class TestClass
, class ExpectedException
>
79 BThreadedTestCaller
<TestClass
, ExpectedException
>::~BThreadedTestCaller() {
82 for (typename
ThreadManagerMap::iterator it
= fThreads
.begin(); it
!= fThreads
.end (); ++it
) {
88 template <class TestClass
, class ExpectedException
>
90 BThreadedTestCaller
<TestClass
, ExpectedException
>::addThread(std::string threadName
, ThreadMethod method
) {
91 if (fThreads
.find(threadName
) == fThreads
.end()) {
92 // Unused name, go ahead and add
93 fThreads
[threadName
] = new BThreadManager
<TestClass
, ExpectedException
>(threadName
, fObject
, method
, fThreadSem
);
95 // Duplicate name, throw an exception
96 throw CppUnit::Exception("BThreadedTestCaller::addThread() - Attempt to add thread under duplicated name ('"
101 template <class TestClass
, class ExpectedException
>
102 CppUnit::TestResult
*
103 BThreadedTestCaller
<TestClass
, ExpectedException
>::run() {
104 CppUnit::TestResult
*result
= new CppUnit::TestResult
;
109 template <class TestClass
, class ExpectedException
>
111 BThreadedTestCaller
<TestClass
, ExpectedException
>::run(CppUnit::TestResult
*result
) {
112 result
->startTest(this);
114 if (fThreads
.size() <= 0)
115 throw CppUnit::Exception("BThreadedTestCaller::run() -- No threads added to BThreadedTestCaller()");
120 // This try/catch block should never actually have to catch
121 // anything (unless some bonehead passes in a NULL pointer to
122 // the constructor). Each BThreadManager object catches and
123 // handles exceptions for its respective thread, so as not
124 // to disrupt the others.
126 // Create our thread semaphore. This semaphore is used to
127 // determine when all the threads have finished executing,
128 // while still allowing *this* thread to handle printing
129 // out NextSubTest() info (since other threads don't appear
130 // to be able to output text while the main thread is
131 // blocked; their output appears later...).
133 // Each thread will acquire the semaphore once when launched,
134 // thus the initial thread count is equal the number of threads.
135 fThreadSem
= create_sem(fThreads
.size(), "ThreadSem");
136 if (fThreadSem
< B_OK
)
137 throw CppUnit::Exception("BThreadedTestCaller::run() -- Error creating fThreadSem");
139 // Launch all the threads.
140 for (typename
ThreadManagerMap::iterator i
= fThreads
.begin();
141 i
!= fThreads
.end ();
144 status_t err
= i
->second
->LaunchThread(result
);
146 result
->addError(this, new CppUnit::Exception("Error launching thread '" + i
->second
->getName() + "'"));
147 // printf("Launch(%s)\n", i->second->getName().c_str());
150 // Now we loop. Before you faint, there is a reason for this:
151 // Calls to NextSubTest() from other threads don't actually
152 // print anything while the main thread is blocked waiting
153 // for another thread. Thus, we have NextSubTest() add the
154 // information to be printed into a queue. The main thread
155 // (this code right here), blocks on a semaphore that it
156 // can only acquire after all the test threads have terminated.
157 // If it times out, it checks the NextSubTest() queue, prints
158 // any pending updates, and tries to acquire the semaphore
159 // again. When it finally manages to acquire it, all the
160 // test threads have terminated, and it's safe to clean up.
164 // Try to acquire the semaphore
165 err
= acquire_sem_etc(fThreadSem
, fThreads
.size(), B_RELATIVE_TIMEOUT
, 500000);
167 // Empty the UpdateList
168 std::vector
<std::string
> &list
= fObject
->AcquireUpdateList();
169 for (std::vector
<std::string
>::iterator i
= list
.begin();
173 // Only print to standard out if the current global shell
174 // lets us (or if no global shell is designated).
175 if (BTestShell::GlobalBeVerbose()) {
176 printf("%s", (*i
).c_str());
181 fObject
->ReleaseUpdateList();
183 } while (err
!= B_OK
);
185 // If we get this far, we actually managed to acquire the semaphore,
186 // so we should release it now.
187 release_sem_etc(fThreadSem
, fThreads
.size(), 0);
189 // Print out a newline for asthetics :-)
195 // Wait for them all to finish, then clean up
196 for (ThreadManagerMap::iterator i = fThreads.begin();
197 i != fThreads.end ();
200 // printf("Wait(%s)...", i->second->getName().c_str());
202 i->second->WaitForThread();
210 } catch ( CppUnit::Exception
&e
) {
211 // Add on the a note that this exception was caught by the
212 // thread caller (which is a bad thing), then note the exception
213 CppUnit::Exception
*threadException
= new CppUnit::Exception(
214 std::string(e
.what()) + " (NOTE: caught by BThreadedTestCaller)",
217 result
->addFailure( fObject
, threadException
);
219 catch ( std::exception
&e
) {
220 // Add on the thread name, then note the exception
221 CppUnit::Exception
*threadException
= new CppUnit::Exception(
222 std::string(e
.what()) + " (NOTE: caught by BThreadedTestCaller)"
224 result
->addError( fObject
, threadException
);
227 // Add on the thread name, then note the exception
228 CppUnit::Exception
*threadException
= new CppUnit::Exception(
229 "caught unknown exception (NOTE: caught by BThreadedTestCaller)"
231 result
->addError( fObject
, threadException
);
239 result
->addError(this, new CppUnit::Exception("tearDown() failed"));
242 result
->addError(this, new CppUnit::Exception("setUp() failed"));
243 } // setUp() try/catch block
245 result
->endTest(this);
248 template <class TestClass
, class ExpectedException
>
250 BThreadedTestCaller
<TestClass
, ExpectedException
>::setUp() {
251 // Verify we have a valid object that's not currently in use first.
253 throw CppUnit::Exception("BThreadedTestCaller::runTest() -- NULL fObject pointer");
254 if (!fObject
->RegisterForUse())
255 throw CppUnit::Exception("BThreadedTestCaller::runTest() -- Attempt to reuse ThreadedTestCase object already in use");
260 template <class TestClass
, class ExpectedException
>
262 BThreadedTestCaller
<TestClass
, ExpectedException
>::tearDown() {
266 template <class TestClass
, class ExpectedException
>
268 BThreadedTestCaller
<TestClass
, ExpectedException
>::toString() const {
269 return std::string("BThreadedTestCaller for ") + getName();
272 #endif // _beos_threaded_test_caller_h_