libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / tools / cppunit / TestShell.cpp
blob9d48238cfab4e9a31e93324e65e7fac3d2014baa
1 #include <iostream>
2 #include <map>
3 #include <set>
4 #include <string>
5 #include <vector>
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>
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 using std::cout;
26 using std::endl;
27 using std::set;
29 _EXPORT BTestShell *BTestShell::fGlobalShell = NULL;
30 const char BTestShell::indent[] = " ";
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 BLocker)
41 , fPatchGroup(NULL)
42 , fOldDebuggerHook(NULL)
43 , fOldLoadAddOnHook(NULL)
44 , fOldUnloadAddOnHook(NULL)
45 #endif // ! NO_ELF_SYMBOL_PATCHING
47 fTLSDebuggerCall = tls_allocate();
50 _EXPORT
51 BTestShell::~BTestShell() {
52 delete fTestDir;
53 #ifndef NO_ELF_SYMBOL_PATCHING
54 delete fPatchGroupLocker;
55 #endif // ! NO_ELF_SYMBOL_PATCHING
59 _EXPORT
60 status_t
61 BTestShell::AddSuite(BTestSuite *suite) {
62 if (suite) {
63 if (Verbosity() >= v3)
64 cout << "Adding suite '" << suite->getName() << "'" << endl;
66 // Add the suite
67 fSuites[suite->getName()] = suite;
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;
79 return B_OK;
80 } else
81 return B_BAD_VALUE;
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);
93 _EXPORT
94 int32
95 BTestShell::LoadSuitesFrom(BDirectory *libDir) {
96 if (!libDir || libDir->InitCheck() != B_OK)
97 return 0;
99 BEntry addonEntry;
100 BPath addonPath;
101 image_id addonImage;
102 int count = 0;
104 typedef BTestSuite* (*suiteFunc)(void);
105 suiteFunc func;
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() << "..." << endl;
112 addonImage = load_add_on(addonPath.Path());
113 err = (addonImage >= 0 ? B_OK : B_ERROR);
115 if (err == B_OK) {
116 // cout << "..." << endl;
117 err = get_image_symbol(addonImage, "getTestSuite",
118 B_SYMBOL_TYPE_TEXT, reinterpret_cast<void **>(&func));
119 } else {
120 // cout << " !!! err == " << err << endl;
122 if (err == B_OK)
123 err = AddSuite(func());
124 if (err == B_OK)
125 count++;
127 return count;
130 _EXPORT
132 BTestShell::Run(int argc, char *argv[]) {
133 // Make note of which directory we started in
134 UpdateTestDir(argv);
136 // Parse the command line args
137 if (!ProcessArguments(argc, argv))
138 return 0;
140 // Load any dynamically loadable tests we can find
141 LoadDynamicSuites();
143 // See if the user requested a list of tests. If so,
144 // print and bail.
145 if (fListTestsAndExit) {
146 PrintInstalledTests();
147 return 0;
150 // Add the proper tests to our suite (or exit if there
151 // are no tests installed).
152 CppUnit::TestSuite suite;
153 if (fTests.empty()) {
155 // No installed tests whatsoever, so bail
156 cout << "ERROR: No installed tests to run!" << endl;
157 return 0;
159 } else if (fSuitesToRun.empty() && fTestsToRun.empty()) {
161 // None specified, so run them all
162 TestMap::iterator i;
163 for (i = fTests.begin(); i != fTests.end(); ++i)
164 suite.addTest( i->second );
166 } else {
167 set<string>::const_iterator i;
168 set<string> suitesToRemove;
170 // Add all the tests from any specified suites to the list of
171 // tests to run (since we use a set, this eliminates the concern
172 // of having duplicate entries).
173 for (i = fTestsToRun.begin(); i != fTestsToRun.end(); ++i) {
174 // See if it's a suite (since it may just be a single test)
175 if (fSuites.find(*i) != fSuites.end()) {
176 // Note the suite name for later removal unless the
177 // name is also the name of an available individual test
178 if (fTests.find(*i) == fTests.end()) {
179 suitesToRemove.insert(*i);
181 const TestMap &tests = fSuites[*i]->getTests();
182 TestMap::const_iterator j;
183 for (j = tests.begin(); j != tests.end(); j++) {
184 fTestsToRun.insert( j->first );
189 // Remove the names of all of the suites we discovered from the
190 // list of tests to run (unless there's also an installed individual
191 // test of the same name).
192 for (i = suitesToRemove.begin(); i != suitesToRemove.end(); i++) {
193 fTestsToRun.erase(*i);
196 // Everything still in fTestsToRun must then be an explicit test
197 for (i = fTestsToRun.begin(); i != fTestsToRun.end(); ++i) {
198 // Make sure it's a valid test
199 if (fTests.find(*i) != fTests.end()) {
200 suite.addTest( fTests[*i] );
201 } else {
202 cout << endl << "ERROR: Invalid argument \"" << *i << "\"" << endl;
203 PrintHelp();
204 return 0;
210 // Run all the tests
211 InitOutput();
212 InstallPatches();
213 suite.run(&fTestResults);
214 UninstallPatches();
215 PrintResults();
217 return 0;
220 _EXPORT
221 BTestShell::VerbosityLevel
222 BTestShell::Verbosity() const {
223 return fVerbosityLevel;
226 _EXPORT
227 const char*
228 BTestShell::TestDir() const {
229 return (fTestDir ? fTestDir->Path() : NULL);
232 // ExpectDebuggerCall
233 /*! \brief Marks the current thread as ready for a debugger() call.
235 A subsequent call of debugger() will be intercepted and a respective
236 flag will be set. WasDebuggerCalled() will then return \c true.
238 _EXPORT
239 void
240 BTestShell::ExpectDebuggerCall()
242 void *var = tls_get(fTLSDebuggerCall);
243 ::CppUnit::Asserter::failIf(var, "ExpectDebuggerCall(): Already expecting "
244 "a debugger() call.");
245 tls_set(fTLSDebuggerCall, (void*)1);
248 // WasDebuggerCalled
249 /*! \brief Returns whether the current thread has invoked debugger() since
250 the last ExpectDebuggerCall() invocation and resets the mode so
251 that subsequent debugger() calls will hit the debugger.
252 \return \c true, if debugger() has been called by the current thread since
253 the last invocation of ExpectDebuggerCall(), \c false otherwise.
255 _EXPORT
256 bool
257 BTestShell::WasDebuggerCalled()
259 void *var = tls_get(fTLSDebuggerCall);
260 tls_set(fTLSDebuggerCall, NULL);
261 return ((addr_t)var > 1);
264 _EXPORT
265 void
266 BTestShell::PrintDescription(int argc, char *argv[]) {
267 cout << endl << fDescription;
270 _EXPORT
271 void
272 BTestShell::PrintHelp() {
273 cout << endl;
274 cout << "VALID ARGUMENTS: " << endl;
275 PrintValidArguments();
276 cout << endl;
280 _EXPORT
281 void
282 BTestShell::PrintValidArguments() {
283 cout << indent << "--help Displays this help text plus some other garbage" << endl;
284 cout << indent << "--list Lists the names of classes with installed tests" << endl;
285 cout << indent << "-v0 Sets verbosity level to 0 (concise summary only)" << endl;
286 cout << indent << "-v1 Sets verbosity level to 1 (complete summary only)" << endl;
287 cout << indent << "-v2 Sets verbosity level to 2 (*default* -- per-test results plus" << endl;
288 cout << indent << " complete summary)" << endl;
289 cout << indent << "-v3 Sets verbosity level to 3 (partial dynamic loading information, " << endl;
290 cout << indent << " per-test results and timing info, plus complete summary)" << endl;
291 cout << indent << "-v4 Sets verbosity level to 4 (complete dynamic loading information, " << endl;
292 cout << indent << " per-test results and timing info, plus complete summary)" << endl;
293 cout << indent << "NAME Instructs the program to run the test for the given class or all" << endl;
294 cout << indent << " the tests for the given suite. If some bonehead adds both a class" << endl;
295 cout << indent << " and a suite with the same name, the suite will be run, not the class" << endl;
296 cout << indent << " (unless the class is part of the suite with the same name :-). If no" << endl;
297 cout << indent << " classes or suites are specified, all available tests are run" << endl;
298 cout << indent << "-lPATH Adds PATH to the search path for dynamically loadable test" << endl;
299 cout << indent << " libraries" << endl;
302 _EXPORT
303 void
304 BTestShell::PrintInstalledTests() {
305 // Print out the list of installed suites
306 cout << "------------------------------------------------------------------------------" << endl;
307 cout << "Available Suites:" << endl;
308 cout << "------------------------------------------------------------------------------" << endl;
309 SuiteMap::const_iterator j;
310 for (j = fSuites.begin(); j != fSuites.end(); ++j)
311 cout << j->first << endl;
312 cout << endl;
314 // Print out the list of installed tests
315 cout << "------------------------------------------------------------------------------" << endl;
316 cout << "Available Tests:" << endl;
317 cout << "------------------------------------------------------------------------------" << endl;
318 TestMap::const_iterator i;
319 for (i = fTests.begin(); i != fTests.end(); ++i)
320 cout << i->first << endl;
321 cout << endl;
324 _EXPORT
325 bool
326 BTestShell::ProcessArguments(int argc, char *argv[]) {
327 // If we're given no parameters, the default settings
328 // will do just fine
329 if (argc < 2)
330 return true;
332 // Handle each command line argument (skipping the first
333 // which is just the app name)
334 for (int i = 1; i < argc; i++) {
335 string str(argv[i]);
337 if (!ProcessArgument(str, argc, argv))
338 return false;
341 return true;
344 _EXPORT
345 bool
346 BTestShell::ProcessArgument(string arg, int argc, char *argv[]) {
347 if (arg == "--help") {
348 PrintDescription(argc, argv);
349 PrintHelp();
350 return false;
351 } else if (arg == "--list") {
352 fListTestsAndExit = true;
353 } else if (arg == "-v0") {
354 fVerbosityLevel = v0;
355 } else if (arg == "-v1") {
356 fVerbosityLevel = v1;
357 } else if (arg == "-v2") {
358 fVerbosityLevel = v2;
359 } else if (arg == "-v3") {
360 fVerbosityLevel = v3;
361 } else if (arg == "-v4") {
362 fVerbosityLevel = v4;
363 } else if (arg.length() >= 2 && arg[0] == '-' && arg[1] == 'l') {
364 fLibDirs.insert(arg.substr(2, arg.size()-2));
365 } else {
366 fTestsToRun.insert(arg);
368 return true;
371 _EXPORT
372 void
373 BTestShell::InitOutput() {
374 // For vebosity level 2, we output info about each test
375 // as we go. This involves a custom CppUnit::TestListener
376 // class.
377 if (fVerbosityLevel >= v2) {
378 cout << "------------------------------------------------------------------------------" << endl;
379 cout << "Tests" << endl;
380 cout << "------------------------------------------------------------------------------" << endl;
381 fTestResults.addListener(new BTestListener);
383 fTestResults.addListener(&fResultsCollector);
386 _EXPORT
387 void
388 BTestShell::PrintResults() {
390 if (fVerbosityLevel > v0) {
391 // Print out detailed results for verbosity levels > 0
392 cout << "------------------------------------------------------------------------------" << endl;
393 cout << "Results " << endl;
394 cout << "------------------------------------------------------------------------------" << endl;
396 // Print failures and errors if there are any, otherwise just say "PASSED"
397 ::CppUnit::TestResultCollector::TestFailures::const_iterator iFailure;
398 if (fResultsCollector.testFailuresTotal() > 0) {
399 if (fResultsCollector.testFailures() > 0) {
400 cout << "- FAILURES: " << fResultsCollector.testFailures() << endl;
401 for (iFailure = fResultsCollector.failures().begin();
402 iFailure != fResultsCollector.failures().end();
403 ++iFailure)
405 if (!(*iFailure)->isError())
406 cout << " " << (*iFailure)->toString() << endl;
409 if (fResultsCollector.testErrors() > 0) {
410 cout << "- ERRORS: " << fResultsCollector.testErrors() << endl;
411 for (iFailure = fResultsCollector.failures().begin();
412 iFailure != fResultsCollector.failures().end();
413 ++iFailure)
415 if ((*iFailure)->isError())
416 cout << " " << (*iFailure)->toString() << endl;
421 else
422 cout << "+ PASSED" << endl;
424 cout << endl;
427 else {
428 // Print out concise results for verbosity level == 0
429 if (fResultsCollector.testFailuresTotal() > 0)
430 cout << "- FAILED" << endl;
431 else
432 cout << "+ PASSED" << endl;
437 _EXPORT
438 void
439 BTestShell::LoadDynamicSuites() {
440 if (Verbosity() >= v3) {
441 cout << "------------------------------------------------------------------------------" << endl;
442 cout << "Loading " << endl;
443 cout << "------------------------------------------------------------------------------" << endl;
446 set<string>::iterator i;
447 for (i = fLibDirs.begin(); i != fLibDirs.end(); i++) {
448 BDirectory libDir((*i).c_str());
449 if (Verbosity() >= v3)
450 cout << "Checking " << *i << endl;
451 /* int count =*/ LoadSuitesFrom(&libDir);
452 if (Verbosity() >= v3) {
453 // cout << "Loaded " << count << " suite" << (count == 1 ? "" : "s");
454 // cout << " from " << *i << endl;
458 if (Verbosity() >= v3)
459 cout << endl;
461 // Look for suites and tests with the same name and give a
462 // warning, as this is only asking for trouble... :-)
463 for (SuiteMap::const_iterator i = fSuites.begin(); i != fSuites.end(); i++) {
464 if (fTests.find(i->first) != fTests.end() && Verbosity() > v0) {
465 cout << "WARNING: '" << i->first << "' refers to both a test suite *and* an individual" <<
466 endl << " test. Both will be executed, but it is reccommended you rename" <<
467 endl << " one of them to resolve the conflict." <<
468 endl << endl;
474 _EXPORT
475 void
476 BTestShell::UpdateTestDir(char *argv[]) {
477 BPath path(argv[0]);
478 if (path.InitCheck() == B_OK) {
479 delete fTestDir;
480 fTestDir = new BPath();
481 if (path.GetParent(fTestDir) != B_OK)
482 cout << "Couldn't get test dir." << endl;
483 } else
484 cout << "Couldn't find the path to the test app." << endl;
487 // InstallPatches
488 /*! \brief Patches the debugger() function.
490 load_add_on() and unload_add_on() are patches as well, to keep the
491 patch group up to date, when images are loaded/unloaded.
493 _EXPORT
494 void
495 BTestShell::InstallPatches()
497 #ifndef NO_ELF_SYMBOL_PATCHING
498 if (fPatchGroup) {
499 std::cerr << "BTestShell::InstallPatches(): Patch group already exist!"
500 << endl;
501 return;
503 BAutolock locker(fPatchGroupLocker);
504 if (!locker.IsLocked()) {
505 std::cerr << "BTestShell::InstallPatches(): Failed to acquire patch "
506 "group lock!" << endl;
507 return;
509 fPatchGroup = new(std::nothrow) ElfSymbolPatchGroup;
510 // init the symbol patch group
511 if (!fPatchGroup) {
512 std::cerr << "BTestShell::InstallPatches(): Failed to allocate patch "
513 "group!" << endl;
514 return;
516 if (// debugger()
517 fPatchGroup->AddPatch("debugger", (void*)&_DebuggerHook,
518 (void**)&fOldDebuggerHook) == B_OK
519 // load_add_on()
520 && fPatchGroup->AddPatch("load_add_on", (void*)&_LoadAddOnHook,
521 (void**)&fOldLoadAddOnHook) == B_OK
522 // unload_add_on()
523 && fPatchGroup->AddPatch("unload_add_on", (void*)&_UnloadAddOnHook,
524 (void**)&fOldUnloadAddOnHook) == B_OK
526 // everything went fine
527 fPatchGroup->Patch();
528 } else {
529 std::cerr << "BTestShell::InstallPatches(): Failed to patch all "
530 "symbols!" << endl;
531 UninstallPatches();
533 #endif // ! NO_ELF_SYMBOL_PATCHING
536 // UninstallPatches
537 /*! \brief Undoes the patches applied by InstallPatches().
539 _EXPORT
540 void
541 BTestShell::UninstallPatches()
543 #ifndef NO_ELF_SYMBOL_PATCHING
544 BAutolock locker(fPatchGroupLocker);
545 if (!locker.IsLocked()) {
546 std::cerr << "BTestShell::UninstallPatches(): "
547 "Failed to acquire patch group lock!" << endl;
548 return;
550 if (fPatchGroup) {
551 fPatchGroup->Restore();
552 delete fPatchGroup;
553 fPatchGroup = NULL;
555 #endif // ! NO_ELF_SYMBOL_PATCHING
558 #ifndef NO_ELF_SYMBOL_PATCHING
560 // _Debugger
561 _EXPORT
562 void
563 BTestShell::_Debugger(const char *message)
565 if (!this || !fPatchGroup) {
566 debugger(message);
567 return;
569 BAutolock locker(fPatchGroupLocker);
570 if (!locker.IsLocked() || !fPatchGroup) {
571 debugger(message);
572 return;
574 cout << "debugger() called: " << message << endl;
575 void *var = tls_get(fTLSDebuggerCall);
576 if (var)
577 tls_set(fTLSDebuggerCall, (void*)((int)var + 1));
578 else
579 (*fOldDebuggerHook)(message);
582 // _LoadAddOn
583 _EXPORT
584 image_id
585 BTestShell::_LoadAddOn(const char *path)
587 if (!this || !fPatchGroup)
588 return load_add_on(path);
589 BAutolock locker(fPatchGroupLocker);
590 if (!locker.IsLocked() || !fPatchGroup)
591 return load_add_on(path);
592 image_id result = (*fOldLoadAddOnHook)(path);
593 fPatchGroup->Update();
594 return result;
597 // _UnloadAddOn
598 _EXPORT
599 status_t
600 BTestShell::_UnloadAddOn(image_id image)
602 if (!this || !fPatchGroup)
603 return unload_add_on(image);
604 BAutolock locker(fPatchGroupLocker);
605 if (!locker.IsLocked() || !fPatchGroup)
606 return unload_add_on(image);
608 if (!this || !fPatchGroup)
609 return unload_add_on(image);
610 status_t result = (*fOldUnloadAddOnHook)(image);
611 fPatchGroup->Update();
612 return result;
615 // _DebuggerHook
616 _EXPORT
617 void
618 BTestShell::_DebuggerHook(const char *message)
620 fGlobalShell->_Debugger(message);
623 // _LoadAddOnHook
624 _EXPORT
625 image_id
626 BTestShell::_LoadAddOnHook(const char *path)
628 return fGlobalShell->_LoadAddOn(path);
631 // _UnloadAddOnHook
632 _EXPORT
633 status_t
634 BTestShell::_UnloadAddOnHook(image_id image)
636 return fGlobalShell->_UnloadAddOn(image);
639 #endif // ! NO_ELF_SYMBOL_PATCHING