1 #include <iostream> 2 #include <map> 3 #include <set> 4 #include <string> 5 #include <vector> 6 7 #include <Autolock.h> 8 #include <Directory.h> 9 #include <Entry.h> 10 #include <image.h> 11 #include <Locker.h> 12 #include <Path.h> 13 #include <TLS.h> 14 15 #include <cppunit/Exception.h> 16 #include <cppunit/Test.h> 17 #include <cppunit/TestAssert.h> 18 #include <cppunit/TestFailure.h> 19 #include <cppunit/TestResult.h> 20 #include <cppunit/TestSuite.h> 21 22 #include <TestShell.h> 23 #include <TestListener.h> 24 25 #ifndef NO_ELF_SYMBOL_PATCHING 26 # include <ElfSymbolPatcher.h> 27 #endif 28 29 _EXPORT BTestShell *BTestShell::fGlobalShell = NULL; 30 const char BTestShell::indent[] = " "; 31 32 _EXPORT 33 BTestShell::BTestShell(const string &description, SyncObject *syncObject) 34 : fVerbosityLevel(v2) 35 , fTestResults(syncObject) 36 , fDescription(description) 37 , fListTestsAndExit(false) 38 , fTestDir(NULL) 39 #ifndef NO_ELF_SYMBOL_PATCHING 40 , fPatchGroupLocker(new(nothrow) BLocker) 41 , fPatchGroup(NULL) 42 , fOldDebuggerHook(NULL) 43 , fOldLoadAddOnHook(NULL) 44 , fOldUnloadAddOnHook(NULL) 45 #endif // ! NO_ELF_SYMBOL_PATCHING 46 { 47 fTLSDebuggerCall = tls_allocate(); 48 } 49 50 _EXPORT 51 BTestShell::~BTestShell() { 52 delete fTestDir; 53 #ifndef NO_ELF_SYMBOL_PATCHING 54 delete fPatchGroupLocker; 55 #endif // ! NO_ELF_SYMBOL_PATCHING 56 } 57 58 59 _EXPORT 60 status_t 61 BTestShell::AddSuite(BTestSuite *suite) { 62 if (suite) { 63 if (Verbosity() >= v3) 64 cout << "Adding suite '" << suite->getName() << "'" << endl; 65 66 // Add the suite 67 fSuites[suite->getName()] = suite; 68 69 // Add its tests 70 const TestMap &map = suite->getTests(); 71 for (TestMap::const_iterator i = map.begin(); 72 i != map.end(); 73 i++) { 74 AddTest(i->first, i->second); 75 if (Verbosity() >= v4 && i->second) 76 cout << " " << i->first << endl; 77 } 78 79 return B_OK; 80 } else 81 return B_BAD_VALUE; 82 } 83 84 _EXPORT 85 void 86 BTestShell::AddTest(const string &name, CppUnit::Test *test) { 87 if (test != NULL) 88 fTests[name] = test; 89 else 90 fTests.erase(name); 91 } 92 93 _EXPORT 94 int32 95 BTestShell::LoadSuitesFrom(BDirectory *libDir) { 96 if (!libDir || libDir->InitCheck() != B_OK) 97 return 0; 98 99 BEntry addonEntry; 100 BPath addonPath; 101 image_id addonImage; 102 int count = 0; 103 104 typedef BTestSuite* (*suiteFunc)(void); 105 suiteFunc func; 106 107 while (libDir->GetNextEntry(&addonEntry, true) == B_OK) { 108 status_t err; 109 err = addonEntry.GetPath(&addonPath); 110 if (!err) { 111 // cout << "Checking " << addonPath.Path() << "..." << flush; 112 addonImage = load_add_on(addonPath.Path()); 113 err = (addonImage > 0 ? B_OK : B_ERROR); 114 } 115 if (!err) { 116 // cout << "..." << endl; 117 err = get_image_symbol(addonImage, 118 "getTestSuite", 119 B_SYMBOL_TYPE_TEXT, 120 reinterpret_cast<void **>(&func)); 121 } else { 122 // cout << " !!! err == " << err << endl; 123 } 124 if (!err) 125 err = AddSuite(func()); 126 if (!err) 127 count++; 128 } 129 return count; 130 } 131 132 _EXPORT 133 int 134 BTestShell::Run(int argc, char *argv[]) { 135 // Make note of which directory we started in 136 UpdateTestDir(argv); 137 138 // Parse the command line args 139 if (!ProcessArguments(argc, argv)) 140 return 0; 141 142 // Load any dynamically loadable tests we can find 143 LoadDynamicSuites(); 144 145 // See if the user requested a list of tests. If so, 146 // print and bail. 147 if (fListTestsAndExit) { 148 PrintInstalledTests(); 149 return 0; 150 } 151 152 // Add the proper tests to our suite (or exit if there 153 // are no tests installed). 154 CppUnit::TestSuite suite; 155 if (fTests.empty()) { 156 157 // No installed tests whatsoever, so bail 158 cout << "ERROR: No installed tests to run!" << endl; 159 return 0; 160 161 } else if (fSuitesToRun.empty() && fTestsToRun.empty()) { 162 163 // None specified, so run them all 164 TestMap::iterator i; 165 for (i = fTests.begin(); i != fTests.end(); ++i) 166 suite.addTest( i->second ); 167 168 } else { 169 set<string>::const_iterator i; 170 set<string> suitesToRemove; 171 172 // Add all the tests from any specified suites to the list of 173 // tests to run (since we use a set, this eliminates the concern 174 // of having duplicate entries). 175 for (i = fTestsToRun.begin(); i != fTestsToRun.end(); ++i) { 176 // See if it's a suite (since it may just be a single test) 177 if (fSuites.find(*i) != fSuites.end()) { 178 // Note the suite name for later removal unless the 179 // name is also the name of an available individual test 180 if (fTests.find(*i) == fTests.end()) { 181 suitesToRemove.insert(*i); 182 } 183 const TestMap &tests = fSuites[*i]->getTests(); 184 TestMap::const_iterator j; 185 for (j = tests.begin(); j != tests.end(); j++) { 186 fTestsToRun.insert( j->first ); 187 } 188 } 189 } 190 191 // Remove the names of all of the suites we discovered from the 192 // list of tests to run (unless there's also an installed individual 193 // test of the same name). 194 for (i = suitesToRemove.begin(); i != suitesToRemove.end(); i++) { 195 fTestsToRun.erase(*i); 196 } 197 198 // Everything still in fTestsToRun must then be an explicit test 199 for (i = fTestsToRun.begin(); i != fTestsToRun.end(); ++i) { 200 // Make sure it's a valid test 201 if (fTests.find(*i) != fTests.end()) { 202 suite.addTest( fTests[*i] ); 203 } else { 204 cout << endl << "ERROR: Invalid argument \"" << *i << "\"" << endl; 205 PrintHelp(); 206 return 0; 207 } 208 } 209 210 } 211 212 // Run all the tests 213 InitOutput(); 214 InstallPatches(); 215 suite.run(&fTestResults); 216 UninstallPatches(); 217 PrintResults(); 218 219 return 0; 220 } 221 222 _EXPORT 223 BTestShell::VerbosityLevel 224 BTestShell::Verbosity() const { 225 return fVerbosityLevel; 226 } 227 228 _EXPORT 229 const char* 230 BTestShell::TestDir() const { 231 return (fTestDir ? fTestDir->Path() : NULL); 232 } 233 234 // ExpectDebuggerCall 235 /*! \brief Marks the current thread as ready for a debugger() call. 236 237 A subsequent call of debugger() will be intercepted and a respective 238 flag will be set. WasDebuggerCalled() will then return \c true. 239 */ 240 _EXPORT 241 void 242 BTestShell::ExpectDebuggerCall() 243 { 244 void *var = tls_get(fTLSDebuggerCall); 245 ::CppUnit::Asserter::failIf(var, "ExpectDebuggerCall(): Already expecting " 246 "a debugger() call."); 247 tls_set(fTLSDebuggerCall, (void*)1); 248 } 249 250 // WasDebuggerCalled 251 /*! \brief Returns whether the current thread has invoked debugger() since 252 the last ExpectDebuggerCall() invocation and resets the mode so 253 that subsequent debugger() calls will hit the debugger. 254 \return \c true, if debugger() has been called by the current thread since 255 the last invocation of ExpectDebuggerCall(), \c false otherwise. 256 */ 257 _EXPORT 258 bool 259 BTestShell::WasDebuggerCalled() 260 { 261 void *var = tls_get(fTLSDebuggerCall); 262 tls_set(fTLSDebuggerCall, NULL); 263 return ((int)var > 1); 264 } 265 266 _EXPORT 267 void 268 BTestShell::PrintDescription(int argc, char *argv[]) { 269 cout << endl << fDescription; 270 } 271 272 _EXPORT 273 void 274 BTestShell::PrintHelp() { 275 cout << endl; 276 cout << "VALID ARGUMENTS: " << endl; 277 PrintValidArguments(); 278 cout << endl; 279 280 } 281 282 _EXPORT 283 void 284 BTestShell::PrintValidArguments() { 285 cout << indent << "--help Displays this help text plus some other garbage" << endl; 286 cout << indent << "--list Lists the names of classes with installed tests" << endl; 287 cout << indent << "-v0 Sets verbosity level to 0 (concise summary only)" << endl; 288 cout << indent << "-v1 Sets verbosity level to 1 (complete summary only)" << endl; 289 cout << indent << "-v2 Sets verbosity level to 2 (*default* -- per-test results plus" << endl; 290 cout << indent << " complete summary)" << endl; 291 cout << indent << "-v3 Sets verbosity level to 3 (partial dynamic loading information, " << endl; 292 cout << indent << " per-test results and timing info, plus complete summary)" << endl; 293 cout << indent << "-v4 Sets verbosity level to 4 (complete dynamic loading information, " << endl; 294 cout << indent << " per-test results and timing info, plus complete summary)" << endl; 295 cout << indent << "NAME Instructs the program to run the test for the given class or all" << endl; 296 cout << indent << " the tests for the given suite. If some bonehead adds both a class" << endl; 297 cout << indent << " and a suite with the same name, the suite will be run, not the class" << endl; 298 cout << indent << " (unless the class is part of the suite with the same name :-). If no" << endl; 299 cout << indent << " classes or suites are specified, all available tests are run" << endl; 300 cout << indent << "-lPATH Adds PATH to the search path for dynamically loadable test" << endl; 301 cout << indent << " libraries" << endl; 302 } 303 304 _EXPORT 305 void 306 BTestShell::PrintInstalledTests() { 307 // Print out the list of installed suites 308 cout << "------------------------------------------------------------------------------" << endl; 309 cout << "Available Suites:" << endl; 310 cout << "------------------------------------------------------------------------------" << endl; 311 SuiteMap::const_iterator j; 312 for (j = fSuites.begin(); j != fSuites.end(); ++j) 313 cout << j->first << endl; 314 cout << endl; 315 316 // Print out the list of installed tests 317 cout << "------------------------------------------------------------------------------" << endl; 318 cout << "Available Tests:" << endl; 319 cout << "------------------------------------------------------------------------------" << endl; 320 TestMap::const_iterator i; 321 for (i = fTests.begin(); i != fTests.end(); ++i) 322 cout << i->first << endl; 323 cout << endl; 324 } 325 326 _EXPORT 327 bool 328 BTestShell::ProcessArguments(int argc, char *argv[]) { 329 // If we're given no parameters, the default settings 330 // will do just fine 331 if (argc < 2) 332 return true; 333 334 // Handle each command line argument (skipping the first 335 // which is just the app name) 336 for (int i = 1; i < argc; i++) { 337 string str(argv[i]); 338 339 if (!ProcessArgument(str, argc, argv)) 340 return false; 341 } 342 343 return true; 344 } 345 346 _EXPORT 347 bool 348 BTestShell::ProcessArgument(string arg, int argc, char *argv[]) { 349 if (arg == "--help") { 350 PrintDescription(argc, argv); 351 PrintHelp(); 352 return false; 353 } else if (arg == "--list") { 354 fListTestsAndExit = true; 355 } else if (arg == "-v0") { 356 fVerbosityLevel = v0; 357 } else if (arg == "-v1") { 358 fVerbosityLevel = v1; 359 } else if (arg == "-v2") { 360 fVerbosityLevel = v2; 361 } else if (arg == "-v3") { 362 fVerbosityLevel = v3; 363 } else if (arg == "-v4") { 364 fVerbosityLevel = v4; 365 } else if (arg.length() >= 2 && arg[0] == '-' && arg[1] == 'l') { 366 fLibDirs.insert(arg.substr(2, arg.size()-2)); 367 } else { 368 fTestsToRun.insert(arg); 369 } 370 return true; 371 } 372 373 _EXPORT 374 void 375 BTestShell::InitOutput() { 376 // For vebosity level 2, we output info about each test 377 // as we go. This involves a custom CppUnit::TestListener 378 // class. 379 if (fVerbosityLevel >= v2) { 380 cout << "------------------------------------------------------------------------------" << endl; 381 cout << "Tests" << endl; 382 cout << "------------------------------------------------------------------------------" << endl; 383 fTestResults.addListener(new BTestListener); 384 } 385 fTestResults.addListener(&fResultsCollector); 386 } 387 388 _EXPORT 389 void 390 BTestShell::PrintResults() { 391 392 if (fVerbosityLevel > v0) { 393 // Print out detailed results for verbosity levels > 0 394 cout << "------------------------------------------------------------------------------" << endl; 395 cout << "Results " << endl; 396 cout << "------------------------------------------------------------------------------" << endl; 397 398 // Print failures and errors if there are any, otherwise just say "PASSED" 399 ::CppUnit::TestResultCollector::TestFailures::const_iterator iFailure; 400 if (fResultsCollector.testFailuresTotal() > 0) { 401 if (fResultsCollector.testFailures() > 0) { 402 cout << "- FAILURES: " << fResultsCollector.testFailures() << endl; 403 for (iFailure = fResultsCollector.failures().begin(); 404 iFailure != fResultsCollector.failures().end(); 405 ++iFailure) 406 { 407 if (!(*iFailure)->isError()) 408 cout << " " << (*iFailure)->toString() << endl; 409 } 410 } 411 if (fResultsCollector.testErrors() > 0) { 412 cout << "- ERRORS: " << fResultsCollector.testErrors() << endl; 413 for (iFailure = fResultsCollector.failures().begin(); 414 iFailure != fResultsCollector.failures().end(); 415 ++iFailure) 416 { 417 if ((*iFailure)->isError()) 418 cout << " " << (*iFailure)->toString() << endl; 419 } 420 } 421 422 } 423 else 424 cout << "+ PASSED" << endl; 425 426 cout << endl; 427 428 } 429 else { 430 // Print out concise results for verbosity level == 0 431 if (fResultsCollector.testFailuresTotal() > 0) 432 cout << "- FAILED" << endl; 433 else 434 cout << "+ PASSED" << endl; 435 } 436 437 } 438 439 _EXPORT 440 void 441 BTestShell::LoadDynamicSuites() { 442 if (Verbosity() >= v3) { 443 cout << "------------------------------------------------------------------------------" << endl; 444 cout << "Loading " << endl; 445 cout << "------------------------------------------------------------------------------" << endl; 446 } 447 448 set<string>::iterator i; 449 for (i = fLibDirs.begin(); i != fLibDirs.end(); i++) { 450 BDirectory libDir((*i).c_str()); 451 if (Verbosity() >= v3) 452 cout << "Checking " << *i << endl; 453 /* int count =*/ LoadSuitesFrom(&libDir); 454 if (Verbosity() >= v3) { 455 // cout << "Loaded " << count << " suite" << (count == 1 ? "" : "s"); 456 // cout << " from " << *i << endl; 457 } 458 } 459 460 if (Verbosity() >= v3) 461 cout << endl; 462 463 // Look for suites and tests with the same name and give a 464 // warning, as this is only asking for trouble... :-) 465 for (SuiteMap::const_iterator i = fSuites.begin(); i != fSuites.end(); i++) { 466 if (fTests.find(i->first) != fTests.end() && Verbosity() > v0) { 467 cout << "WARNING: '" << i->first << "' refers to both a test suite *and* an individual" << 468 endl << " test. Both will be executed, but it is reccommended you rename" << 469 endl << " one of them to resolve the conflict." << 470 endl << endl; 471 } 472 } 473 474 } 475 476 _EXPORT 477 void 478 BTestShell::UpdateTestDir(char *argv[]) { 479 BPath path(argv[0]); 480 if (path.InitCheck() == B_OK) { 481 delete fTestDir; 482 fTestDir = new BPath(); 483 if (path.GetParent(fTestDir) != B_OK) 484 cout << "Couldn't get test dir." << endl; 485 } else 486 cout << "Couldn't find the path to the test app." << endl; 487 } 488 489 // InstallPatches 490 /*! \brief Patches the debugger() function. 491 492 load_add_on() and unload_add_on() are patches as well, to keep the 493 patch group up to date, when images are loaded/unloaded. 494 */ 495 _EXPORT 496 void 497 BTestShell::InstallPatches() 498 { 499 #ifndef NO_ELF_SYMBOL_PATCHING 500 if (fPatchGroup) { 501 cerr << "BTestShell::InstallPatches(): Patch group already exist!" 502 << endl; 503 return; 504 } 505 BAutolock locker(fPatchGroupLocker); 506 if (!locker.IsLocked()) { 507 cerr << "BTestShell::InstallPatches(): Failed to acquire patch " 508 "group lock!" << endl; 509 return; 510 } 511 fPatchGroup = new(nothrow) ElfSymbolPatchGroup; 512 // init the symbol patch group 513 if (!fPatchGroup) { 514 cerr << "BTestShell::InstallPatches(): Failed to allocate patch " 515 "group!" << endl; 516 return; 517 } 518 if (// debugger() 519 fPatchGroup->AddPatch("debugger", (void*)&_DebuggerHook, 520 (void**)&fOldDebuggerHook) == B_OK 521 // load_add_on() 522 && fPatchGroup->AddPatch("load_add_on", (void*)&_LoadAddOnHook, 523 (void**)&fOldLoadAddOnHook) == B_OK 524 // unload_add_on() 525 && fPatchGroup->AddPatch("unload_add_on", (void*)&_UnloadAddOnHook, 526 (void**)&fOldUnloadAddOnHook) == B_OK 527 ) { 528 // everything went fine 529 fPatchGroup->Patch(); 530 } else { 531 cerr << "BTestShell::InstallPatches(): Failed to patch all symbols!" 532 << endl; 533 UninstallPatches(); 534 } 535 #endif // ! NO_ELF_SYMBOL_PATCHING 536 } 537 538 // UninstallPatches 539 /*! \brief Undoes the patches applied by InstallPatches(). 540 */ 541 _EXPORT 542 void 543 BTestShell::UninstallPatches() 544 { 545 #ifndef NO_ELF_SYMBOL_PATCHING 546 BAutolock locker(fPatchGroupLocker); 547 if (!locker.IsLocked()) { 548 cerr << "BTestShell::UninstallPatches(): " 549 "Failed to acquire patch group lock!" << endl; 550 return; 551 } 552 if (fPatchGroup) { 553 fPatchGroup->Restore(); 554 delete fPatchGroup; 555 fPatchGroup = NULL; 556 } 557 #endif // ! NO_ELF_SYMBOL_PATCHING 558 } 559 560 #ifndef NO_ELF_SYMBOL_PATCHING 561 562 // _Debugger 563 _EXPORT 564 void 565 BTestShell::_Debugger(const char *message) 566 { 567 if (!this || !fPatchGroup) { 568 debugger(message); 569 return; 570 } 571 BAutolock locker(fPatchGroupLocker); 572 if (!locker.IsLocked() || !fPatchGroup) { 573 debugger(message); 574 return; 575 } 576 cout << "debugger() called: " << message << endl; 577 void *var = tls_get(fTLSDebuggerCall); 578 if (var) 579 tls_set(fTLSDebuggerCall, (void*)((int)var + 1)); 580 else 581 (*fOldDebuggerHook)(message); 582 } 583 584 // _LoadAddOn 585 _EXPORT 586 image_id 587 BTestShell::_LoadAddOn(const char *path) 588 { 589 if (!this || !fPatchGroup) 590 return load_add_on(path); 591 BAutolock locker(fPatchGroupLocker); 592 if (!locker.IsLocked() || !fPatchGroup) 593 return load_add_on(path); 594 image_id result = (*fOldLoadAddOnHook)(path); 595 fPatchGroup->Update(); 596 return result; 597 } 598 599 // _UnloadAddOn 600 _EXPORT 601 status_t 602 BTestShell::_UnloadAddOn(image_id image) 603 { 604 if (!this || !fPatchGroup) 605 return unload_add_on(image); 606 BAutolock locker(fPatchGroupLocker); 607 if (!locker.IsLocked() || !fPatchGroup) 608 return unload_add_on(image); 609 610 if (!this || !fPatchGroup) 611 return unload_add_on(image); 612 status_t result = (*fOldUnloadAddOnHook)(image); 613 fPatchGroup->Update(); 614 return result; 615 } 616 617 // _DebuggerHook 618 _EXPORT 619 void 620 BTestShell::_DebuggerHook(const char *message) 621 { 622 fGlobalShell->_Debugger(message); 623 } 624 625 // _LoadAddOnHook 626 _EXPORT 627 image_id 628 BTestShell::_LoadAddOnHook(const char *path) 629 { 630 return fGlobalShell->_LoadAddOn(path); 631 } 632 633 // _UnloadAddOnHook 634 _EXPORT 635 status_t 636 BTestShell::_UnloadAddOnHook(image_id image) 637 { 638 return fGlobalShell->_UnloadAddOn(image); 639 } 640 641 #endif // ! NO_ELF_SYMBOL_PATCHING 642