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>
22 #include <TestShell.h>
23 #include <TestListener.h>
25 #ifndef NO_ELF_SYMBOL_PATCHING
26 # include <ElfSymbolPatcher.h>
33 _EXPORT BTestShell
*BTestShell::fGlobalShell
= NULL
;
34 const char BTestShell::indent
[] = " ";
37 BTestShell::BTestShell(const string
&description
, SyncObject
*syncObject
)
39 , fTestResults(syncObject
)
40 , fDescription(description
)
41 , fListTestsAndExit(false)
43 #ifndef NO_ELF_SYMBOL_PATCHING
44 , fPatchGroupLocker(new(nothrow
) BLocker
)
46 , fOldDebuggerHook(NULL
)
47 , fOldLoadAddOnHook(NULL
)
48 , fOldUnloadAddOnHook(NULL
)
49 #endif // ! NO_ELF_SYMBOL_PATCHING
51 fTLSDebuggerCall
= tls_allocate();
55 BTestShell::~BTestShell() {
57 #ifndef NO_ELF_SYMBOL_PATCHING
58 delete fPatchGroupLocker
;
59 #endif // ! NO_ELF_SYMBOL_PATCHING
65 BTestShell::AddSuite(BTestSuite
*suite
) {
67 if (Verbosity() >= v3
)
68 cout
<< "Adding suite '" << suite
->getName() << "'" << endl
;
71 fSuites
[suite
->getName()] = suite
;
74 const TestMap
&map
= suite
->getTests();
75 for (TestMap::const_iterator i
= map
.begin();
78 AddTest(i
->first
, i
->second
);
79 if (Verbosity() >= v4
&& i
->second
)
80 cout
<< " " << i
->first
<< endl
;
90 BTestShell::AddTest(const string
&name
, CppUnit::Test
*test
) {
99 BTestShell::LoadSuitesFrom(BDirectory
*libDir
) {
100 if (!libDir
|| libDir
->InitCheck() != B_OK
)
108 typedef BTestSuite
* (*suiteFunc
)(void);
111 while (libDir
->GetNextEntry(&addonEntry
, true) == B_OK
) {
113 err
= addonEntry
.GetPath(&addonPath
);
115 // cout << "Checking " << addonPath.Path() << "..." << endl;
116 addonImage
= load_add_on(addonPath
.Path());
117 err
= (addonImage
>= 0 ? B_OK
: B_ERROR
);
120 // cout << "..." << endl;
121 err
= get_image_symbol(addonImage
, "getTestSuite",
122 B_SYMBOL_TYPE_TEXT
, reinterpret_cast<void **>(&func
));
124 // cout << " !!! err == " << err << endl;
127 err
= AddSuite(func());
136 BTestShell::Run(int argc
, char *argv
[]) {
137 // Make note of which directory we started in
140 // Parse the command line args
141 if (!ProcessArguments(argc
, argv
))
144 // Load any dynamically loadable tests we can find
147 // See if the user requested a list of tests. If so,
149 if (fListTestsAndExit
) {
150 PrintInstalledTests();
154 // Add the proper tests to our suite (or exit if there
155 // are no tests installed).
156 CppUnit::TestSuite suite
;
157 if (fTests
.empty()) {
159 // No installed tests whatsoever, so bail
160 cout
<< "ERROR: No installed tests to run!" << endl
;
163 } else if (fSuitesToRun
.empty() && fTestsToRun
.empty()) {
165 // None specified, so run them all
167 for (i
= fTests
.begin(); i
!= fTests
.end(); ++i
)
168 suite
.addTest( i
->second
);
171 set
<string
>::const_iterator i
;
172 set
<string
> suitesToRemove
;
174 // Add all the tests from any specified suites to the list of
175 // tests to run (since we use a set, this eliminates the concern
176 // of having duplicate entries).
177 for (i
= fTestsToRun
.begin(); i
!= fTestsToRun
.end(); ++i
) {
178 // See if it's a suite (since it may just be a single test)
179 if (fSuites
.find(*i
) != fSuites
.end()) {
180 // Note the suite name for later removal unless the
181 // name is also the name of an available individual test
182 if (fTests
.find(*i
) == fTests
.end()) {
183 suitesToRemove
.insert(*i
);
185 const TestMap
&tests
= fSuites
[*i
]->getTests();
186 TestMap::const_iterator j
;
187 for (j
= tests
.begin(); j
!= tests
.end(); j
++) {
188 fTestsToRun
.insert( j
->first
);
193 // Remove the names of all of the suites we discovered from the
194 // list of tests to run (unless there's also an installed individual
195 // test of the same name).
196 for (i
= suitesToRemove
.begin(); i
!= suitesToRemove
.end(); i
++) {
197 fTestsToRun
.erase(*i
);
200 // Everything still in fTestsToRun must then be an explicit test
201 for (i
= fTestsToRun
.begin(); i
!= fTestsToRun
.end(); ++i
) {
202 // Make sure it's a valid test
203 if (fTests
.find(*i
) != fTests
.end()) {
204 suite
.addTest( fTests
[*i
] );
206 cout
<< endl
<< "ERROR: Invalid argument \"" << *i
<< "\"" << endl
;
217 suite
.run(&fTestResults
);
225 BTestShell::VerbosityLevel
226 BTestShell::Verbosity() const {
227 return fVerbosityLevel
;
232 BTestShell::TestDir() const {
233 return (fTestDir
? fTestDir
->Path() : NULL
);
236 // ExpectDebuggerCall
237 /*! \brief Marks the current thread as ready for a debugger() call.
239 A subsequent call of debugger() will be intercepted and a respective
240 flag will be set. WasDebuggerCalled() will then return \c true.
244 BTestShell::ExpectDebuggerCall()
246 void *var
= tls_get(fTLSDebuggerCall
);
247 ::CppUnit::Asserter::failIf(var
, "ExpectDebuggerCall(): Already expecting "
248 "a debugger() call.");
249 tls_set(fTLSDebuggerCall
, (void*)1);
253 /*! \brief Returns whether the current thread has invoked debugger() since
254 the last ExpectDebuggerCall() invocation and resets the mode so
255 that subsequent debugger() calls will hit the debugger.
256 \return \c true, if debugger() has been called by the current thread since
257 the last invocation of ExpectDebuggerCall(), \c false otherwise.
261 BTestShell::WasDebuggerCalled()
263 void *var
= tls_get(fTLSDebuggerCall
);
264 tls_set(fTLSDebuggerCall
, NULL
);
265 return ((addr_t
)var
> 1);
270 BTestShell::PrintDescription(int argc
, char *argv
[]) {
271 cout
<< endl
<< fDescription
;
276 BTestShell::PrintHelp() {
278 cout
<< "VALID ARGUMENTS: " << endl
;
279 PrintValidArguments();
286 BTestShell::PrintValidArguments() {
287 cout
<< indent
<< "--help Displays this help text plus some other garbage" << endl
;
288 cout
<< indent
<< "--list Lists the names of classes with installed tests" << endl
;
289 cout
<< indent
<< "-v0 Sets verbosity level to 0 (concise summary only)" << endl
;
290 cout
<< indent
<< "-v1 Sets verbosity level to 1 (complete summary only)" << endl
;
291 cout
<< indent
<< "-v2 Sets verbosity level to 2 (*default* -- per-test results plus" << endl
;
292 cout
<< indent
<< " complete summary)" << endl
;
293 cout
<< indent
<< "-v3 Sets verbosity level to 3 (partial dynamic loading information, " << endl
;
294 cout
<< indent
<< " per-test results and timing info, plus complete summary)" << endl
;
295 cout
<< indent
<< "-v4 Sets verbosity level to 4 (complete dynamic loading information, " << endl
;
296 cout
<< indent
<< " per-test results and timing info, plus complete summary)" << endl
;
297 cout
<< indent
<< "NAME Instructs the program to run the test for the given class or all" << endl
;
298 cout
<< indent
<< " the tests for the given suite. If some bonehead adds both a class" << endl
;
299 cout
<< indent
<< " and a suite with the same name, the suite will be run, not the class" << endl
;
300 cout
<< indent
<< " (unless the class is part of the suite with the same name :-). If no" << endl
;
301 cout
<< indent
<< " classes or suites are specified, all available tests are run" << endl
;
302 cout
<< indent
<< "-lPATH Adds PATH to the search path for dynamically loadable test" << endl
;
303 cout
<< indent
<< " libraries" << endl
;
308 BTestShell::PrintInstalledTests() {
309 // Print out the list of installed suites
310 cout
<< "------------------------------------------------------------------------------" << endl
;
311 cout
<< "Available Suites:" << endl
;
312 cout
<< "------------------------------------------------------------------------------" << endl
;
313 SuiteMap::const_iterator j
;
314 for (j
= fSuites
.begin(); j
!= fSuites
.end(); ++j
)
315 cout
<< j
->first
<< endl
;
318 // Print out the list of installed tests
319 cout
<< "------------------------------------------------------------------------------" << endl
;
320 cout
<< "Available Tests:" << endl
;
321 cout
<< "------------------------------------------------------------------------------" << endl
;
322 TestMap::const_iterator i
;
323 for (i
= fTests
.begin(); i
!= fTests
.end(); ++i
)
324 cout
<< i
->first
<< endl
;
330 BTestShell::ProcessArguments(int argc
, char *argv
[]) {
331 // If we're given no parameters, the default settings
336 // Handle each command line argument (skipping the first
337 // which is just the app name)
338 for (int i
= 1; i
< argc
; i
++) {
341 if (!ProcessArgument(str
, argc
, argv
))
350 BTestShell::ProcessArgument(string arg
, int argc
, char *argv
[]) {
351 if (arg
== "--help") {
352 PrintDescription(argc
, argv
);
355 } else if (arg
== "--list") {
356 fListTestsAndExit
= true;
357 } else if (arg
== "-v0") {
358 fVerbosityLevel
= v0
;
359 } else if (arg
== "-v1") {
360 fVerbosityLevel
= v1
;
361 } else if (arg
== "-v2") {
362 fVerbosityLevel
= v2
;
363 } else if (arg
== "-v3") {
364 fVerbosityLevel
= v3
;
365 } else if (arg
== "-v4") {
366 fVerbosityLevel
= v4
;
367 } else if (arg
.length() >= 2 && arg
[0] == '-' && arg
[1] == 'l') {
368 fLibDirs
.insert(arg
.substr(2, arg
.size()-2));
370 fTestsToRun
.insert(arg
);
377 BTestShell::InitOutput() {
378 // For vebosity level 2, we output info about each test
379 // as we go. This involves a custom CppUnit::TestListener
381 if (fVerbosityLevel
>= v2
) {
382 cout
<< "------------------------------------------------------------------------------" << endl
;
383 cout
<< "Tests" << endl
;
384 cout
<< "------------------------------------------------------------------------------" << endl
;
385 fTestResults
.addListener(new BTestListener
);
387 fTestResults
.addListener(&fResultsCollector
);
392 BTestShell::PrintResults() {
394 if (fVerbosityLevel
> v0
) {
395 // Print out detailed results for verbosity levels > 0
396 cout
<< "------------------------------------------------------------------------------" << endl
;
397 cout
<< "Results " << endl
;
398 cout
<< "------------------------------------------------------------------------------" << endl
;
400 // Print failures and errors if there are any, otherwise just say "PASSED"
401 ::CppUnit::TestResultCollector::TestFailures::const_iterator iFailure
;
402 if (fResultsCollector
.testFailuresTotal() > 0) {
403 if (fResultsCollector
.testFailures() > 0) {
404 cout
<< "- FAILURES: " << fResultsCollector
.testFailures() << endl
;
405 for (iFailure
= fResultsCollector
.failures().begin();
406 iFailure
!= fResultsCollector
.failures().end();
409 if (!(*iFailure
)->isError())
410 cout
<< " " << (*iFailure
)->toString() << endl
;
413 if (fResultsCollector
.testErrors() > 0) {
414 cout
<< "- ERRORS: " << fResultsCollector
.testErrors() << endl
;
415 for (iFailure
= fResultsCollector
.failures().begin();
416 iFailure
!= fResultsCollector
.failures().end();
419 if ((*iFailure
)->isError())
420 cout
<< " " << (*iFailure
)->toString() << endl
;
426 cout
<< "+ PASSED" << endl
;
432 // Print out concise results for verbosity level == 0
433 if (fResultsCollector
.testFailuresTotal() > 0)
434 cout
<< "- FAILED" << endl
;
436 cout
<< "+ PASSED" << endl
;
443 BTestShell::LoadDynamicSuites() {
444 if (Verbosity() >= v3
) {
445 cout
<< "------------------------------------------------------------------------------" << endl
;
446 cout
<< "Loading " << endl
;
447 cout
<< "------------------------------------------------------------------------------" << endl
;
450 set
<string
>::iterator i
;
451 for (i
= fLibDirs
.begin(); i
!= fLibDirs
.end(); i
++) {
452 BDirectory
libDir((*i
).c_str());
453 if (Verbosity() >= v3
)
454 cout
<< "Checking " << *i
<< endl
;
455 /* int count =*/ LoadSuitesFrom(&libDir
);
456 if (Verbosity() >= v3
) {
457 // cout << "Loaded " << count << " suite" << (count == 1 ? "" : "s");
458 // cout << " from " << *i << endl;
462 if (Verbosity() >= v3
)
465 // Look for suites and tests with the same name and give a
466 // warning, as this is only asking for trouble... :-)
467 for (SuiteMap::const_iterator i
= fSuites
.begin(); i
!= fSuites
.end(); i
++) {
468 if (fTests
.find(i
->first
) != fTests
.end() && Verbosity() > v0
) {
469 cout
<< "WARNING: '" << i
->first
<< "' refers to both a test suite *and* an individual" <<
470 endl
<< " test. Both will be executed, but it is reccommended you rename" <<
471 endl
<< " one of them to resolve the conflict." <<
480 BTestShell::UpdateTestDir(char *argv
[]) {
482 if (path
.InitCheck() == B_OK
) {
484 fTestDir
= new BPath();
485 if (path
.GetParent(fTestDir
) != B_OK
)
486 cout
<< "Couldn't get test dir." << endl
;
488 cout
<< "Couldn't find the path to the test app." << endl
;
492 /*! \brief Patches the debugger() function.
494 load_add_on() and unload_add_on() are patches as well, to keep the
495 patch group up to date, when images are loaded/unloaded.
499 BTestShell::InstallPatches()
501 #ifndef NO_ELF_SYMBOL_PATCHING
503 cerr
<< "BTestShell::InstallPatches(): Patch group already exist!"
507 BAutolock
locker(fPatchGroupLocker
);
508 if (!locker
.IsLocked()) {
509 cerr
<< "BTestShell::InstallPatches(): Failed to acquire patch "
510 "group lock!" << endl
;
513 fPatchGroup
= new(nothrow
) ElfSymbolPatchGroup
;
514 // init the symbol patch group
516 cerr
<< "BTestShell::InstallPatches(): Failed to allocate patch "
521 fPatchGroup
->AddPatch("debugger", (void*)&_DebuggerHook
,
522 (void**)&fOldDebuggerHook
) == B_OK
524 && fPatchGroup
->AddPatch("load_add_on", (void*)&_LoadAddOnHook
,
525 (void**)&fOldLoadAddOnHook
) == B_OK
527 && fPatchGroup
->AddPatch("unload_add_on", (void*)&_UnloadAddOnHook
,
528 (void**)&fOldUnloadAddOnHook
) == B_OK
530 // everything went fine
531 fPatchGroup
->Patch();
533 cerr
<< "BTestShell::InstallPatches(): Failed to patch all symbols!"
537 #endif // ! NO_ELF_SYMBOL_PATCHING
541 /*! \brief Undoes the patches applied by InstallPatches().
545 BTestShell::UninstallPatches()
547 #ifndef NO_ELF_SYMBOL_PATCHING
548 BAutolock
locker(fPatchGroupLocker
);
549 if (!locker
.IsLocked()) {
550 cerr
<< "BTestShell::UninstallPatches(): "
551 "Failed to acquire patch group lock!" << endl
;
555 fPatchGroup
->Restore();
559 #endif // ! NO_ELF_SYMBOL_PATCHING
562 #ifndef NO_ELF_SYMBOL_PATCHING
567 BTestShell::_Debugger(const char *message
)
569 if (!this || !fPatchGroup
) {
573 BAutolock
locker(fPatchGroupLocker
);
574 if (!locker
.IsLocked() || !fPatchGroup
) {
578 cout
<< "debugger() called: " << message
<< endl
;
579 void *var
= tls_get(fTLSDebuggerCall
);
581 tls_set(fTLSDebuggerCall
, (void*)((int)var
+ 1));
583 (*fOldDebuggerHook
)(message
);
589 BTestShell::_LoadAddOn(const char *path
)
591 if (!this || !fPatchGroup
)
592 return load_add_on(path
);
593 BAutolock
locker(fPatchGroupLocker
);
594 if (!locker
.IsLocked() || !fPatchGroup
)
595 return load_add_on(path
);
596 image_id result
= (*fOldLoadAddOnHook
)(path
);
597 fPatchGroup
->Update();
604 BTestShell::_UnloadAddOn(image_id image
)
606 if (!this || !fPatchGroup
)
607 return unload_add_on(image
);
608 BAutolock
locker(fPatchGroupLocker
);
609 if (!locker
.IsLocked() || !fPatchGroup
)
610 return unload_add_on(image
);
612 if (!this || !fPatchGroup
)
613 return unload_add_on(image
);
614 status_t result
= (*fOldUnloadAddOnHook
)(image
);
615 fPatchGroup
->Update();
622 BTestShell::_DebuggerHook(const char *message
)
624 fGlobalShell
->_Debugger(message
);
630 BTestShell::_LoadAddOnHook(const char *path
)
632 return fGlobalShell
->_LoadAddOn(path
);
638 BTestShell::_UnloadAddOnHook(image_id image
)
640 return fGlobalShell
->_UnloadAddOn(image
);
643 #endif // ! NO_ELF_SYMBOL_PATCHING