1 #ifndef _beos_threaded_test_caller_h_ 2 #define _beos_threaded_test_caller_h_ 3 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> 13 14 class TestResult; 15 16 template <class TestClass, class ExpectedException = CppUnit::NoExceptionExpected> 17 class 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. 21 */ 22 typedef void (TestClass::*ThreadMethod)(); 23 24 BThreadedTestCaller(std::string name); 25 BThreadedTestCaller(std::string name, TestClass &object); 26 BThreadedTestCaller(std::string name, TestClass *object); 27 virtual ~BThreadedTestCaller(); 28 29 virtual CppUnit::TestResult *run(); 30 virtual void run(CppUnit::TestResult *result); 31 32 //! Adds a thread to the test. \c threadName must be unique to this BThreadedTestCaller. 33 void addThread(std::string threadName, ThreadMethod method); 34 35 protected: 36 virtual void setUp(); 37 virtual void tearDown(); 38 virtual std::string toString() const; 39 40 typedef std::map<std::string, BThreadManager<TestClass, ExpectedException> *> ThreadManagerMap; 41 42 bool fOwnObject; 43 TestClass *fObject; 44 ThreadManagerMap fThreads; 45 46 sem_id fThreadSem; 47 48 }; 49 50 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) 57 { 58 } 59 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) 66 { 67 } 68 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) 75 { 76 } 77 78 template <class TestClass, class ExpectedException> 79 BThreadedTestCaller<TestClass, ExpectedException>::~BThreadedTestCaller() { 80 if (fOwnObject) 81 delete fObject; 82 for (ThreadManagerMap::iterator it = fThreads.begin(); it != fThreads.end (); ++it) { 83 delete it->second; 84 } 85 } 86 87 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 + "')"); 98 } 99 } 100 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; 107 } 108 109 template <class TestClass, class ExpectedException> 110 void 111 BThreadedTestCaller<TestClass, ExpectedException>::run(CppUnit::TestResult *result) { 112 result->startTest(this); 113 114 if (fThreads.size() <= 0) 115 throw CppUnit::Exception("BThreadedTestCaller::run() -- No threads added to BThreadedTestCaller()"); 116 117 try { 118 setUp(); 119 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...). 132 // 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"); 138 139 // Launch all the threads. 140 for (ThreadManagerMap::iterator i = fThreads.begin(); 141 i != fThreads.end (); 142 ++i) 143 { 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()); 148 } 149 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. 161 162 status_t err; 163 do { 164 // Try to acquire the semaphore 165 err = acquire_sem_etc(fThreadSem, fThreads.size(), B_RELATIVE_TIMEOUT, 500000); 166 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++) 172 { 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); 178 } 179 } 180 list.clear(); 181 fObject->ReleaseUpdateList(); 182 183 } while (err != B_OK); 184 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); 188 189 // Print out a newline for asthetics :-) 190 printf("\n"); 191 192 /* 193 194 195 // Wait for them all to finish, then clean up 196 for (ThreadManagerMap::iterator i = fThreads.begin(); 197 i != fThreads.end (); 198 ++i) 199 { 200 // printf("Wait(%s)...", i->second->getName().c_str()); 201 fflush(stdout); 202 i->second->WaitForThread(); 203 // printf("done\n"); 204 delete i->second; 205 } 206 */ 207 208 fThreads.clear(); 209 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() 216 ); 217 result->addFailure( fObject, threadException ); 218 } 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)" 223 ); 224 result->addError( fObject, threadException ); 225 } 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)" 230 ); 231 result->addError( fObject, threadException ); 232 } 233 234 snooze(50000); 235 236 try { 237 tearDown(); 238 } catch (...) { 239 result->addError(this, new CppUnit::Exception("tearDown() failed")); 240 } 241 } catch (...) { 242 result->addError(this, new CppUnit::Exception("setUp() failed")); 243 } // setUp() try/catch block 244 245 result->endTest(this); 246 } 247 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"); 256 257 fObject->setUp(); 258 } 259 260 template <class TestClass, class ExpectedException> 261 void 262 BThreadedTestCaller<TestClass, ExpectedException>::tearDown() { 263 fObject->tearDown(); 264 } 265 266 template <class TestClass, class ExpectedException> 267 std::string 268 BThreadedTestCaller<TestClass, ExpectedException>::toString() const { 269 return std::string("BThreadedTestCaller for ") + getName(); 270 } 271 272 #endif // _beos_threaded_test_caller_h_ 273