Restore the "GPL licensing not permitted" in GLUT license headers.
[haiku.git] / headers / tools / cppunit / ThreadedTestCaller.h
blobd0563c40d76ece1407196eb87051393e44fa38ec
1 #ifndef _beos_threaded_test_caller_h_
2 #define _beos_threaded_test_caller_h_
4 //#include <memory>
5 #include <cppunit/TestCase.h>
6 #include <cppunit/TestResult.h>
7 #include <cppunit/TestCaller.h>
8 #include <TestShell.h>
9 #include <ThreadManager.h>
10 #include <map>
11 #include <vector>
12 #include <stdio.h>
14 class TestResult;
16 template <class TestClass, class ExpectedException = CppUnit::NoExceptionExpected>
17 class CPPUNIT_API BThreadedTestCaller : public CppUnit::TestCase {
18 public:
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);
35 protected:
36 virtual void setUp();
37 virtual void tearDown();
38 virtual std::string toString() const;
40 typedef std::map<std::string, BThreadManager<TestClass, ExpectedException> *> ThreadManagerMap;
42 bool fOwnObject;
43 TestClass *fObject;
44 ThreadManagerMap fThreads;
46 sem_id fThreadSem;
51 template <class TestClass, class ExpectedException>
52 BThreadedTestCaller<TestClass, ExpectedException>::BThreadedTestCaller(std::string name)
53 : TestCase(name)
54 , fOwnObject(true)
55 , fObject(new TestClass())
56 , fThreadSem(-1)
60 template <class TestClass, class ExpectedException>
61 BThreadedTestCaller<TestClass, ExpectedException>::BThreadedTestCaller(std::string name, TestClass &object)
62 : TestCase(name)
63 , fOwnObject(false)
64 , fObject(&object)
65 , fThreadSem(-1)
69 template <class TestClass, class ExpectedException>
70 BThreadedTestCaller<TestClass, ExpectedException>::BThreadedTestCaller(std::string name, TestClass *object)
71 : TestCase(name)
72 , fOwnObject(true)
73 , fObject(object)
74 , fThreadSem(-1)
78 template <class TestClass, class ExpectedException>
79 BThreadedTestCaller<TestClass, ExpectedException>::~BThreadedTestCaller() {
80 if (fOwnObject)
81 delete fObject;
82 for (typename ThreadManagerMap::iterator it = fThreads.begin(); it != fThreads.end (); ++it) {
83 delete it->second;
88 template <class TestClass, class ExpectedException>
89 void
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);
94 } else {
95 // Duplicate name, throw an exception
96 throw CppUnit::Exception("BThreadedTestCaller::addThread() - Attempt to add thread under duplicated name ('"
97 + threadName + "')");
101 template <class TestClass, class ExpectedException>
102 CppUnit::TestResult *
103 BThreadedTestCaller<TestClass, ExpectedException>::run() {
104 CppUnit::TestResult *result = new CppUnit::TestResult;
105 run(result);
106 return result;
109 template <class TestClass, class ExpectedException>
110 void
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()");
117 try {
118 setUp();
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.
125 try {
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 ();
142 ++i)
144 status_t err = i->second->LaunchThread(result);
145 if (err != B_OK)
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.
162 status_t err;
163 do {
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();
170 i != list.end();
171 i++)
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());
177 fflush(stdout);
180 list.clear();
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 :-)
190 printf("\n");
195 // Wait for them all to finish, then clean up
196 for (ThreadManagerMap::iterator i = fThreads.begin();
197 i != fThreads.end ();
198 ++i)
200 // printf("Wait(%s)...", i->second->getName().c_str());
201 fflush(stdout);
202 i->second->WaitForThread();
203 // printf("done\n");
204 delete i->second;
208 fThreads.clear();
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)",
215 e.sourceLine()
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 );
226 catch (...) {
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 );
234 snooze(50000);
236 try {
237 tearDown();
238 } catch (...) {
239 result->addError(this, new CppUnit::Exception("tearDown() failed"));
241 } catch (...) {
242 result->addError(this, new CppUnit::Exception("setUp() failed"));
243 } // setUp() try/catch block
245 result->endTest(this);
248 template <class TestClass, class ExpectedException>
249 void
250 BThreadedTestCaller<TestClass, ExpectedException>::setUp() {
251 // Verify we have a valid object that's not currently in use first.
252 if (!fObject)
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");
257 fObject->setUp();
260 template <class TestClass, class ExpectedException>
261 void
262 BThreadedTestCaller<TestClass, ExpectedException>::tearDown() {
263 fObject->tearDown();
266 template <class TestClass, class ExpectedException>
267 std::string
268 BThreadedTestCaller<TestClass, ExpectedException>::toString() const {
269 return std::string("BThreadedTestCaller for ") + getName();
272 #endif // _beos_threaded_test_caller_h_