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