Big CppUnit Update

+ Initial threaded test support
+ Integrated CppUnitShell code into TestShell


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@77 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Tyler Dauwalder 2002-07-11 03:31:07 +00:00
parent e5c292c0c9
commit 03aa30ce94
5 changed files with 327 additions and 38 deletions

View File

@ -0,0 +1,14 @@
#include <SafetyLock.h>
#include <Locker.h>
SafetyLock::SafetyLock(BLocker *locker)
: fLocker(locker)
{
if (fLocker)
fLocker->Lock();
}
SafetyLock::~SafetyLock() {
if (fLocker)
fLocker->Unlock();
}

View File

@ -1,33 +1,43 @@
#include <TestCase.h>
#include <unistd.h>
#include <stdio.h>
TestCase::TestCase()
: CppUnit::TestCase()
, fValidCWD(false)
{
}
TestCase::TestCase(std::string name)
BTestCase::BTestCase(std::string name)
: CppUnit::TestCase(name)
, fValidCWD(false)
, fSubTestNum(0)
{
}
// Saves the location of the current working directory. To return to the
// last saved working directory, all \ref RestorCWD().
void
TestCase::SaveCWD() {
BTestCase::tearDown() {
NextSubTestBlock();
}
void
BTestCase::NextSubTest() {
printf("[%ld]", fSubTestNum++);
fflush(stdout);
}
void
BTestCase::NextSubTestBlock() {
// printf("\n");
}
/*! To return to the last saved working directory, call RestoreCWD(). */
void
BTestCase::SaveCWD() {
fValidCWD = getcwd(fCurrentWorkingDir, B_PATH_NAME_LENGTH);
}
/* Restores the current working directory to last directory saved by a
call to SaveCWD(). If SaveCWD() has not been called and an alternate
/* If SaveCWD() has not been called and an alternate
directory is specified by alternate, the current working directory is
changed to alternate. If alternate is null, the current working directory
is not modified.
*/
void
TestCase::RestoreCWD(const char *alternate) {
BTestCase::RestoreCWD(const char *alternate) {
if (fValidCWD)
chdir(fCurrentWorkingDir);
else if (alternate != NULL)

View File

@ -0,0 +1,33 @@
#include <TestListener.h>
#include <cppunit/Exception.h>
#include <cppunit/Test.h>
#include <cppunit/TestFailure.h>
#include <iostream>
void
BTestListener::startTest( CppUnit::Test *test ) {
fOkay = true;
cout << test->getName() << endl;
}
void
BTestListener::addFailure( const CppUnit::TestFailure &failure ) {
fOkay = false;
cout << " - ";
cout << (failure.isError() ? "ERROR" : "FAILURE");
cout << " -- ";
cout << (failure.thrownException() != NULL
? failure.thrownException()->what()
: "(unknown error)");
cout << endl;
}
void
BTestListener::endTest( CppUnit::Test *test ) {
if (fOkay)
cout << " + PASSED" << endl;
else
cout << " - FAILED" << endl;
cout << endl;
}

View File

@ -1,36 +1,209 @@
#include <TestShell.h>
#include <iostream>
#include <Path.h>
#include <stdio.h>
TestShell::TestShell(const std::string &description, SyncObject *syncObject)
: CppUnitShell(description, syncObject),
fTestDir(NULL)
{
}
#include <cppunit/Exception.h>
#include <cppunit/Test.h>
#include <cppunit/TestFailure.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestSuite.h>
#include <TestListener.h>
#include <set>
#include <map>
#include <string>
#include <vector>
TestShell::~TestShell()
BTestShell::BTestShell(const std::string &description, SyncObject *syncObject)
: fVerbosityLevel(v2)
, fDescription(description)
, fTestResults(syncObject)
{
delete fTestDir;
};
void
BTestShell::AddSuite(const std::string &name, const SuiteFunction suite) {
if (suite != NULL)
fTests[name] = suite;
}
int
TestShell::Run(int argc, char *argv[])
{
// Let's hope BPath does work. ;-)
BPath path(argv[0]);
if (path.InitCheck() == B_OK) {
fTestDir = new BPath();
if (path.GetParent(fTestDir) != B_OK)
printf("Couldn't get test dir.\n");
} else
printf("Couldn't find the path to the test app.\n");
return CppUnitShell::Run(argc, argv);
BTestShell::Run(int argc, char *argv[]) {
// Parse the command line args
if (!ProcessArguments(argc, argv))
return 0;
// Add the proper tests to our suite (or exit if there
// are no tests installed).
CppUnit::TestSuite suite;
if (fTests.empty()) {
// No installed tests whatsoever, so bail
cout << "ERROR: No installed tests to run!" << endl;
return 0;
} else if (fTestsToRun.empty()) {
// None specified, so run them all
std::map<std::string, SuiteFunction>::iterator i;
for (i = fTests.begin(); i != fTests.end(); ++i)
suite.addTest( i->second() );
} else {
// One or more specified, so only run those
std::set<std::string>::const_iterator i;
for (i = fTestsToRun.begin(); i != fTestsToRun.end(); ++i)
suite.addTest( fTests[*i]() );
}
// Run all the tests
InitOutput();
suite.run(&fTestResults);
PrintResults();
return 0;
}
const char*
TestShell::TestDir() const
{
return (fTestDir ? fTestDir->Path() : NULL);
BTestShell::VerbosityLevel
BTestShell::Verbosity() const {
return fVerbosityLevel;
}
void
BTestShell::PrintDescription(int argc, char *argv[]) {
cout << endl << fDescription;
}
void
BTestShell::PrintHelp() {
const char indent[] = " ";
cout << endl;
cout << "VALID ARGUMENTS: " << endl;
cout << indent << "--help Displays this help text plus some other garbage" << endl;
cout << indent << "--list Lists the names of classes with installed tests" << endl;
cout << indent << "-v0 Sets verbosity level to 0 (concise summary only)" << endl;
cout << indent << "-v1 Sets verbosity level to 1 (complete summary only)" << endl;
cout << indent << "-v2 Sets verbosity level to 2 (*default* -- per-test results plus" << endl;
cout << indent << " complete summary)" << endl;
cout << indent << "-v3 Sets verbosity level to 3 (per-test results and timing info" << endl;
cout << indent << " plus complete summary)" << endl;
cout << indent << "CLASSNAME Instructs the program to run the test for the given class; if" << endl;
cout << indent << " no classes are specified, all tests are run" << endl;
cout << endl;
}
bool
BTestShell::ProcessArguments(int argc, char *argv[]) {
// If we're given no parameters, the default settings
// will do just fine
if (argc < 2)
return true;
// Handle each command line argument (skipping the first
// which is just the app name)
for (int i = 1; i < argc; i++) {
std::string str(argv[i]);
if (str == "--help") {
PrintDescription(argc, argv);
PrintHelp();
return false;
}
else if (str == "--list") {
// Print out the list of installed tests
cout << "------------------------------------------------------------------------------" << endl;
cout << "Available Tests:" << endl;
cout << "------------------------------------------------------------------------------" << endl;
map<std::string, SuiteFunction>::const_iterator i;
for (i = fTests.begin(); i != fTests.end(); ++i)
cout << i->first << endl;
cout << endl;
return false;
}
else if (str == "-v0") {
fVerbosityLevel = v0;
}
else if (str == "-v1") {
fVerbosityLevel = v1;
}
else if (str == "-v2") {
fVerbosityLevel = v2;
}
else if (fTests.find(str) != fTests.end()) {
fTestsToRun.insert(str);
}
else {
cout << endl << "ERROR: Invalid argument \"" << str << "\"" << endl;
PrintHelp();
return false;
}
}
return true;
}
void
BTestShell::InitOutput() {
// For vebosity level 2, we output info about each test
// as we go. This involves a custom CppUnit::TestListener
// class.
if (fVerbosityLevel == v2) {
cout << "------------------------------------------------------------------------------" << endl;
cout << "Tests" << endl;
cout << "------------------------------------------------------------------------------" << endl;
fTestResults.addListener(new BTestListener);
fTestResults.addListener(&fResultsCollector);
}
}
void
BTestShell::PrintResults() {
if (fVerbosityLevel > v0) {
// Print out detailed results for verbosity levels > 0
cout << "------------------------------------------------------------------------------" << endl;
cout << "Results " << endl;
cout << "------------------------------------------------------------------------------" << endl;
// Print failures and errors if there are any, otherwise just say "PASSED"
::CppUnit::TestResultCollector::TestFailures::const_iterator iFailure;
if (fResultsCollector.testFailuresTotal() > 0) {
if (fResultsCollector.testFailures() > 0) {
cout << "- FAILURES: " << fResultsCollector.testFailures() << endl;
for (iFailure = fResultsCollector.failures().begin();
iFailure != fResultsCollector.failures().end();
++iFailure)
{
if (!(*iFailure)->isError())
cout << " " << (*iFailure)->toString() << endl;
}
}
if (fResultsCollector.testErrors() > 0) {
cout << "- ERRORS: " << fResultsCollector.testErrors() << endl;
for (iFailure = fResultsCollector.failures().begin();
iFailure != fResultsCollector.failures().end();
++iFailure)
{
if ((*iFailure)->isError())
cout << " " << (*iFailure)->toString() << endl;
}
}
}
else
cout << "+ PASSED" << endl;
cout << endl;
}
else {
// Print out concise results for verbosity level == 0
if (fResultsCollector.testFailuresTotal() > 0)
cout << "- FAILED" << endl;
else
cout << "+ PASSED" << endl;
}
}

View File

@ -0,0 +1,59 @@
#include <ThreadedTestCase.h>
#include <SafetyLock.h>
BThreadedTestCase::BThreadedTestCase(std::string name, std::string progressSeparator)
: BTestCase(name)
, fProgressSeparator(progressSeparator)
, fNumberMapLock(new BLocker())
{
}
BThreadedTestCase::~BThreadedTestCase() {
// Kill our locker
delete fNumberMapLock;
// Clean up
for (std::map<thread_id, ThreadSubTestInfo*>::iterator i = fNumberMap.begin();
i != fNumberMap.end();
i++)
{
delete i->second;
}
}
void
BThreadedTestCase::NextSubTest() {
// Find out what thread we're in
thread_id id = find_thread(NULL);
{
// Lock the number map
SafetyLock lock(fNumberMapLock);
std::map<thread_id, ThreadSubTestInfo*>::iterator i = fNumberMap.find(id);
if (i != fNumberMap.end() && i->second) {
// Handle multi-threaded case
ThreadSubTestInfo *info = i->second;
cout << "[" << info->subTestNum++ << fProgressSeparator << info->name << "]";
return;
}
}
// Handle single-threaded case
BTestCase::NextSubTest();
}
void
BThreadedTestCase::InitThreadInfo(thread_id id, std::string threadName) {
SafetyLock lock(fNumberMapLock); // Lock the number map
std::map<thread_id, ThreadSubTestInfo*>::iterator i = fNumberMap.find(id);
if (i != fNumberMap.end() && i->second) {
i->second->name = threadName;
i->second->subTestNum = 0;
} else {
// New addition
ThreadSubTestInfo *info = new ThreadSubTestInfo();
info->name = threadName;
info->subTestNum = 0;
fNumberMap[id] = info;
}
}