1#include "RegistrarThreadManagerTest.h" 2 3#include <cppunit/Test.h> 4#include <cppunit/TestCaller.h> 5#include <cppunit/TestSuite.h> 6#include <TestApp.h> 7#include <TestUtils.h> 8 9#ifndef TEST_R5 10#include "RegistrarThread.h" 11#include "RegistrarThreadManager.h" 12#endif // !TEST_R5 13 14#include <stdio.h> 15 16// Suite 17CppUnit::Test* 18RegistrarThreadManagerTest::Suite() { 19 CppUnit::TestSuite *suite = new CppUnit::TestSuite(); 20 typedef CppUnit::TestCaller<RegistrarThreadManagerTest> TC; 21 22 suite->addTest( new TC("RegistrarThreadManager::Shutdown Test", 23 &RegistrarThreadManagerTest::ShutdownTest) ); 24 suite->addTest( new TC("RegistrarThreadManager::Thread Limit Test", 25 &RegistrarThreadManagerTest::ThreadLimitTest) ); 26 27 28 return suite; 29} 30 31#ifndef TEST_R5 32// Base test thread class 33class TestThread : public RegistrarThread { 34public: 35 TestThread(const char *name, int32 priority, BMessenger managerMessenger) 36 : RegistrarThread(name, priority, managerMessenger) 37 { 38 } 39 40 void DoSomethingUseless() { 41 fIntVal++; 42 snooze(1000); 43 } 44 45private: 46 int64 fIntVal; 47}; 48 49// Test thread that terminates quickly 50class TerminatingThread : public TestThread { 51public: 52 TerminatingThread(const char *name, int32 priority, BMessenger managerMessenger) 53 : TestThread(name, priority, managerMessenger) 54 { 55 } 56 57protected: 58 virtual status_t ThreadFunction() { 59 DoSomethingUseless(); 60 fIsFinished = true; 61 return B_OK; 62 } 63}; 64 65// Test thread that never terminates, but pays attention 66// to its fShouldExit member 67class WellBehavedInfiniteThread : public TestThread { 68public: 69 WellBehavedInfiniteThread(const char *name, int32 priority, BMessenger managerMessenger) 70 : TestThread(name, priority, managerMessenger) 71 { 72 } 73 74protected: 75 virtual status_t ThreadFunction() { 76 while (true) { 77 DoSomethingUseless(); 78 if (fShouldExit) 79 break; 80 } 81 fIsFinished = true; 82 return B_OK; 83 } 84}; 85 86// Test thread that never terminates and completely ignores 87// its fShouldExit member 88class NaughtyInfiniteThread : public TestThread { 89public: 90 NaughtyInfiniteThread(const char *name, int32 priority, BMessenger managerMessenger) 91 : TestThread(name, priority, managerMessenger) 92 { 93 } 94 95protected: 96 virtual status_t ThreadFunction() { 97 while (true) { 98 DoSomethingUseless(); 99 } 100 fIsFinished = true; 101 return B_OK; 102 } 103}; 104#endif // !TEST_R5 105 106 107// setUp 108void 109RegistrarThreadManagerTest::setUp() 110{ 111 BTestCase::setUp(); 112#ifndef TEST_R5 113 // Setup our application 114 fApplication = new BTestApp("application/x-vnd.obos.RegistrarThreadManagerTest"); 115 if (fApplication->Init() != B_OK) { 116 fprintf(stderr, "Failed to initialize application (perhaps the Haiku registrar isn't running?).\n"); 117 delete fApplication; 118 fApplication = NULL; 119 } 120#endif // !TEST_R5 121} 122 123// tearDown 124void 125RegistrarThreadManagerTest::tearDown() 126{ 127#ifndef TEST_R5 128 // Terminate the Application 129 if (fApplication) { 130 fApplication->Terminate(); 131 delete fApplication; 132 fApplication = NULL; 133 } 134#endif // !TEST_R5 135 BTestCase::tearDown(); 136} 137 138void 139RegistrarThreadManagerTest::ShutdownTest() 140{ 141#ifdef TEST_R5 142 Outputf("(no tests performed for R5 version)\n"); 143#else 144 NextSubTest(); 145 status_t err = B_OK; 146 NextSubTest(); 147 RegistrarThreadManager manager; 148 NextSubTest(); 149 CHK(fApplication && fApplication->InitCheck() == B_OK); 150 NextSubTest(); 151// fApplication->AddHandler(&manager); 152 NextSubTest(); 153 BMessenger managerMessenger(NULL, fApplication, &err); 154// TODO: Do something about this... 155if (err != B_OK) { 156fprintf(stderr, "Fails because we try to init an Haiku BMessenger with a " 157"BLooper from R5's libbe (more precisely a BTestApp living in libcppunit, " 158"which is only linked against R5's libbe).\n"); 159} 160 NextSubTest(); 161 CHK(err == B_OK && managerMessenger.IsValid()); 162 NextSubTest(); 163 164 // Launch a bunch of threads 165 const uint termThreads = 2; 166 const uint niceThreads = 2; 167 const uint evilThreads = 2; 168 169 for (uint i = 0; i < termThreads; i++) { 170 NextSubTest(); 171 char name[1024]; 172 sprintf(name, "terminating #%d", i); 173 RegistrarThread *thread = new TerminatingThread(name, B_NORMAL_PRIORITY, managerMessenger); 174 CHK(thread != NULL); 175 CHK(thread->InitCheck() == B_OK); 176 CHK(manager.LaunchThread(thread) == B_OK); 177 } 178 179 for (uint i = 0; i < niceThreads; i++) { 180 NextSubTest(); 181 char name[1024]; 182 sprintf(name, "nice #%d", i); 183 RegistrarThread *thread = new WellBehavedInfiniteThread(name, B_NORMAL_PRIORITY, managerMessenger); 184 CHK(thread != NULL); 185 CHK(thread->InitCheck() == B_OK); 186 CHK(manager.LaunchThread(thread) == B_OK); 187 } 188 189 for (uint i = 0; i < evilThreads; i++) { 190 NextSubTest(); 191 char name[1024]; 192 sprintf(name, "evil #%d", i); 193 RegistrarThread *thread = new NaughtyInfiniteThread(name, B_NORMAL_PRIORITY, managerMessenger); 194 CHK(thread != NULL); 195 CHK(thread->InitCheck() == B_OK); 196 CHK(manager.LaunchThread(thread) == B_OK); 197 } 198 199 // Check the number of threads before doing a cleanup 200 NextSubTest(); // <= 13 201 CHK(manager.ThreadCount() == (termThreads + niceThreads + evilThreads)); 202 203 // Do the cleanup and check again (the terminating threads 204 // should be gone) 205 NextSubTest(); 206 snooze(500000); // give them time to terminate 207 CHK(manager.CleanupThreads() == B_OK); 208 CHK(manager.ThreadCount() == (niceThreads + evilThreads)); 209 210 // Now do a shutdown and check again (the nice infinite threads 211 // should be gone) 212 NextSubTest(); 213 CHK(manager.ShutdownThreads() == B_OK); 214 snooze(1000000); // give them time to quit nicely 215 CHK(manager.CleanupThreads() == B_OK); 216 CHK(manager.ThreadCount() == evilThreads); 217 218 219 // Now finally kill any remaining threads (which should rid us of 220 // the naughty infinite threads) 221 NextSubTest(); 222 CHK(manager.KillThreads() == B_OK); 223 CHK(manager.ThreadCount() == 0); 224 225#endif // !TEST_R5 226} 227 228void 229RegistrarThreadManagerTest::ThreadLimitTest() 230{ 231#ifdef TEST_R5 232 Outputf("(no tests performed for R5 version)\n"); 233#else 234 NextSubTest(); 235 status_t err = B_OK; 236 RegistrarThreadManager manager; 237 CHK(fApplication && fApplication->InitCheck() == B_OK); 238 BMessenger managerMessenger(NULL, fApplication, &err); 239// TODO: Do something about this... 240if (err != B_OK) { 241fprintf(stderr, "Fails because we try to init an Haiku BMessenger with a " 242"BLooper from R5's libbe (more precisely a BTestApp living in libcppunit, " 243"which is only linked against R5's libbe).\n"); 244} 245 CHK(err == B_OK && managerMessenger.IsValid()); 246 247 const uint termThreads = 2; 248 249 // This test is only useful if the thread limit of the manager 250 // class is > kTermThreads 251 CHK(termThreads < RegistrarThreadManager::kThreadLimit); 252 253 // Launch some terminating threads 254 uint i; 255 for (i = 0; i < termThreads; i++) { 256 NextSubTest(); 257 char name[1024]; 258 sprintf(name, "terminating #%d", i); 259 RegistrarThread *thread = new TerminatingThread(name, B_NORMAL_PRIORITY, managerMessenger); 260 CHK(thread != NULL); 261 CHK(thread->InitCheck() == B_OK); 262 CHK(manager.LaunchThread(thread) == B_OK); 263 } 264 265 // Now fill up the manager with non-terminating threads 266 for (; i < RegistrarThreadManager::kThreadLimit; i++) { 267 NextSubTest(); 268 char name[1024]; 269 sprintf(name, "nice #%d", i); 270 RegistrarThread *thread = new WellBehavedInfiniteThread(name, B_NORMAL_PRIORITY, managerMessenger); 271 CHK(thread != NULL); 272 CHK(thread->InitCheck() == B_OK); 273 CHK(manager.LaunchThread(thread) == B_OK); 274 } 275 CHK(manager.ThreadCount() == RegistrarThreadManager::kThreadLimit); 276 277 // Now try to launch just one more... 278 NextSubTest(); 279 { 280 char *name = "hopeless thread"; 281 RegistrarThread *thread = new WellBehavedInfiniteThread(name, B_NORMAL_PRIORITY, managerMessenger); 282 CHK(thread != NULL); 283 CHK(thread->InitCheck() == B_OK); 284 CHK(manager.LaunchThread(thread) == B_NO_MORE_THREADS); 285 delete thread; 286 } 287 288 // Now wait a little bit for our terminating threads to quit, 289 // cleanup after them, and make sure we can now launch that 290 // many threads again 291 NextSubTest(); 292 snooze(500000); 293 manager.CleanupThreads(); 294 295 for (i = 0; i < termThreads; i++) { 296 NextSubTest(); 297 char name[1024]; 298 sprintf(name, "2nd round nice #%d", i); 299 RegistrarThread *thread = new TerminatingThread(name, B_NORMAL_PRIORITY, managerMessenger); 300 CHK(thread != NULL); 301 CHK(thread->InitCheck() == B_OK); 302 CHK(manager.LaunchThread(thread) == B_OK); 303 } 304 305 // Now try once more to launch just one more... 306 NextSubTest(); 307 { 308 char *name = "hopeless thread"; 309 RegistrarThread *thread = new WellBehavedInfiniteThread(name, B_NORMAL_PRIORITY, managerMessenger); 310 CHK(thread != NULL); 311 CHK(thread->InitCheck() == B_OK); 312 CHK(manager.LaunchThread(thread) == B_NO_MORE_THREADS); 313 delete thread; 314 } 315 316 // Cleanup 317 NextSubTest(); 318 manager.ShutdownThreads(); 319 snooze(500000); 320 321#endif // !TEST_R5 322} 323