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 17 CppUnit::Test* 18 RegistrarThreadManagerTest::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 33 class TestThread : public RegistrarThread { 34 public: 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 45 private: 46 int64 fIntVal; 47 }; 48 49 // Test thread that terminates quickly 50 class TerminatingThread : public TestThread { 51 public: 52 TerminatingThread(const char *name, int32 priority, BMessenger managerMessenger) 53 : TestThread(name, priority, managerMessenger) 54 { 55 } 56 57 protected: 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 67 class WellBehavedInfiniteThread : public TestThread { 68 public: 69 WellBehavedInfiniteThread(const char *name, int32 priority, BMessenger managerMessenger) 70 : TestThread(name, priority, managerMessenger) 71 { 72 } 73 74 protected: 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 88 class NaughtyInfiniteThread : public TestThread { 89 public: 90 NaughtyInfiniteThread(const char *name, int32 priority, BMessenger managerMessenger) 91 : TestThread(name, priority, managerMessenger) 92 { 93 } 94 95 protected: 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 108 void 109 RegistrarThreadManagerTest::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 124 void 125 RegistrarThreadManagerTest::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 138 void 139 RegistrarThreadManagerTest::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... 155 if (err != B_OK) { 156 fprintf(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 228 void 229 RegistrarThreadManagerTest::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... 240 if (err != B_OK) { 241 fprintf(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