BPicture: Fix archive constructor.
[haiku.git] / src / tools / cppunit / TestShell.cpp
blob61abf51f614186ee746f3da1737395287cb14e89
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 #ifndef NO_ELF_SYMBOL_PATCHING
26 # include <ElfSymbolPatcher.h>
27 #endif
29 using std::cout;
30 using std::endl;
31 using std::set;
33 _EXPORT BTestShell *BTestShell::fGlobalShell = NULL;
34 const char BTestShell::indent[] = " ";
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
51 fTLSDebuggerCall = tls_allocate();
54 _EXPORT
55 BTestShell::~BTestShell() {
56 delete fTestDir;
57 #ifndef NO_ELF_SYMBOL_PATCHING
58 delete fPatchGroupLocker;
59 #endif // ! NO_ELF_SYMBOL_PATCHING
63 _EXPORT
64 status_t
65 BTestShell::AddSuite(BTestSuite *suite) {
66 if (suite) {
67 if (Verbosity() >= v3)
68 cout << "Adding suite '" << suite->getName() << "'" << endl;
70 // Add the suite
71 fSuites[suite->getName()] = suite;
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;
83 return B_OK;
84 } else
85 return B_BAD_VALUE;
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);
97 _EXPORT
98 int32
99 BTestShell::LoadSuitesFrom(BDirectory *libDir) {
100 if (!libDir || libDir->InitCheck() != B_OK)
101 return 0;
103 BEntry addonEntry;
104 BPath addonPath;
105 image_id addonImage;
106 int count = 0;
108 typedef BTestSuite* (*suiteFunc)(void);
109 suiteFunc func;
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() << "..." << endl;
116 addonImage = load_add_on(addonPath.Path());
117 err = (addonImage >= 0 ? B_OK : B_ERROR);
119 if (err == B_OK) {
120 // cout << "..." << endl;
121 err = get_image_symbol(addonImage, "getTestSuite",
122 B_SYMBOL_TYPE_TEXT, reinterpret_cast<void **>(&func));
123 } else {
124 // cout << " !!! err == " << err << endl;
126 if (err == B_OK)
127 err = AddSuite(func());
128 if (err == B_OK)
129 count++;
131 return count;
134 _EXPORT
136 BTestShell::Run(int argc, char *argv[]) {
137 // Make note of which directory we started in
138 UpdateTestDir(argv);
140 // Parse the command line args
141 if (!ProcessArguments(argc, argv))
142 return 0;
144 // Load any dynamically loadable tests we can find
145 LoadDynamicSuites();
147 // See if the user requested a list of tests. If so,
148 // print and bail.
149 if (fListTestsAndExit) {
150 PrintInstalledTests();
151 return 0;
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;
161 return 0;
163 } else if (fSuitesToRun.empty() && fTestsToRun.empty()) {
165 // None specified, so run them all
166 TestMap::iterator i;
167 for (i = fTests.begin(); i != fTests.end(); ++i)
168 suite.addTest( i->second );
170 } else {
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] );
205 } else {
206 cout << endl << "ERROR: Invalid argument \"" << *i << "\"" << endl;
207 PrintHelp();
208 return 0;
214 // Run all the tests
215 InitOutput();
216 InstallPatches();
217 suite.run(&fTestResults);
218 UninstallPatches();
219 PrintResults();
221 return 0;
224 _EXPORT
225 BTestShell::VerbosityLevel
226 BTestShell::Verbosity() const {
227 return fVerbosityLevel;
230 _EXPORT
231 const char*
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.
242 _EXPORT
243 void
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);
252 // WasDebuggerCalled
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.
259 _EXPORT
260 bool
261 BTestShell::WasDebuggerCalled()
263 void *var = tls_get(fTLSDebuggerCall);
264 tls_set(fTLSDebuggerCall, NULL);
265 return ((addr_t)var > 1);
268 _EXPORT
269 void
270 BTestShell::PrintDescription(int argc, char *argv[]) {
271 cout << endl << fDescription;
274 _EXPORT
275 void
276 BTestShell::PrintHelp() {
277 cout << endl;
278 cout << "VALID ARGUMENTS: " << endl;
279 PrintValidArguments();
280 cout << endl;
284 _EXPORT
285 void
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;
306 _EXPORT
307 void
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;
316 cout << 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;
325 cout << endl;
328 _EXPORT
329 bool
330 BTestShell::ProcessArguments(int argc, char *argv[]) {
331 // If we're given no parameters, the default settings
332 // will do just fine
333 if (argc < 2)
334 return true;
336 // Handle each command line argument (skipping the first
337 // which is just the app name)
338 for (int i = 1; i < argc; i++) {
339 string str(argv[i]);
341 if (!ProcessArgument(str, argc, argv))
342 return false;
345 return true;
348 _EXPORT
349 bool
350 BTestShell::ProcessArgument(string arg, int argc, char *argv[]) {
351 if (arg == "--help") {
352 PrintDescription(argc, argv);
353 PrintHelp();
354 return false;
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));
369 } else {
370 fTestsToRun.insert(arg);
372 return true;
375 _EXPORT
376 void
377 BTestShell::InitOutput() {
378 // For vebosity level 2, we output info about each test
379 // as we go. This involves a custom CppUnit::TestListener
380 // class.
381 if (fVerbosityLevel >= v2) {
382 cout << "------------------------------------------------------------------------------" << endl;
383 cout << "Tests" << endl;
384 cout << "------------------------------------------------------------------------------" << endl;
385 fTestResults.addListener(new BTestListener);
387 fTestResults.addListener(&fResultsCollector);
390 _EXPORT
391 void
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();
407 ++iFailure)
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();
417 ++iFailure)
419 if ((*iFailure)->isError())
420 cout << " " << (*iFailure)->toString() << endl;
425 else
426 cout << "+ PASSED" << endl;
428 cout << endl;
431 else {
432 // Print out concise results for verbosity level == 0
433 if (fResultsCollector.testFailuresTotal() > 0)
434 cout << "- FAILED" << endl;
435 else
436 cout << "+ PASSED" << endl;
441 _EXPORT
442 void
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)
463 cout << endl;
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." <<
472 endl << endl;
478 _EXPORT
479 void
480 BTestShell::UpdateTestDir(char *argv[]) {
481 BPath path(argv[0]);
482 if (path.InitCheck() == B_OK) {
483 delete fTestDir;
484 fTestDir = new BPath();
485 if (path.GetParent(fTestDir) != B_OK)
486 cout << "Couldn't get test dir." << endl;
487 } else
488 cout << "Couldn't find the path to the test app." << endl;
491 // InstallPatches
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.
497 _EXPORT
498 void
499 BTestShell::InstallPatches()
501 #ifndef NO_ELF_SYMBOL_PATCHING
502 if (fPatchGroup) {
503 cerr << "BTestShell::InstallPatches(): Patch group already exist!"
504 << endl;
505 return;
507 BAutolock locker(fPatchGroupLocker);
508 if (!locker.IsLocked()) {
509 cerr << "BTestShell::InstallPatches(): Failed to acquire patch "
510 "group lock!" << endl;
511 return;
513 fPatchGroup = new(nothrow) ElfSymbolPatchGroup;
514 // init the symbol patch group
515 if (!fPatchGroup) {
516 cerr << "BTestShell::InstallPatches(): Failed to allocate patch "
517 "group!" << endl;
518 return;
520 if (// debugger()
521 fPatchGroup->AddPatch("debugger", (void*)&_DebuggerHook,
522 (void**)&fOldDebuggerHook) == B_OK
523 // load_add_on()
524 && fPatchGroup->AddPatch("load_add_on", (void*)&_LoadAddOnHook,
525 (void**)&fOldLoadAddOnHook) == B_OK
526 // unload_add_on()
527 && fPatchGroup->AddPatch("unload_add_on", (void*)&_UnloadAddOnHook,
528 (void**)&fOldUnloadAddOnHook) == B_OK
530 // everything went fine
531 fPatchGroup->Patch();
532 } else {
533 cerr << "BTestShell::InstallPatches(): Failed to patch all symbols!"
534 << endl;
535 UninstallPatches();
537 #endif // ! NO_ELF_SYMBOL_PATCHING
540 // UninstallPatches
541 /*! \brief Undoes the patches applied by InstallPatches().
543 _EXPORT
544 void
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;
552 return;
554 if (fPatchGroup) {
555 fPatchGroup->Restore();
556 delete fPatchGroup;
557 fPatchGroup = NULL;
559 #endif // ! NO_ELF_SYMBOL_PATCHING
562 #ifndef NO_ELF_SYMBOL_PATCHING
564 // _Debugger
565 _EXPORT
566 void
567 BTestShell::_Debugger(const char *message)
569 if (!this || !fPatchGroup) {
570 debugger(message);
571 return;
573 BAutolock locker(fPatchGroupLocker);
574 if (!locker.IsLocked() || !fPatchGroup) {
575 debugger(message);
576 return;
578 cout << "debugger() called: " << message << endl;
579 void *var = tls_get(fTLSDebuggerCall);
580 if (var)
581 tls_set(fTLSDebuggerCall, (void*)((int)var + 1));
582 else
583 (*fOldDebuggerHook)(message);
586 // _LoadAddOn
587 _EXPORT
588 image_id
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();
598 return result;
601 // _UnloadAddOn
602 _EXPORT
603 status_t
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();
616 return result;
619 // _DebuggerHook
620 _EXPORT
621 void
622 BTestShell::_DebuggerHook(const char *message)
624 fGlobalShell->_Debugger(message);
627 // _LoadAddOnHook
628 _EXPORT
629 image_id
630 BTestShell::_LoadAddOnHook(const char *path)
632 return fGlobalShell->_LoadAddOn(path);
635 // _UnloadAddOnHook
636 _EXPORT
637 status_t
638 BTestShell::_UnloadAddOnHook(image_id image)
640 return fGlobalShell->_UnloadAddOn(image);
643 #endif // ! NO_ELF_SYMBOL_PATCHING