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:
parent
e5c292c0c9
commit
03aa30ce94
|
@ -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();
|
||||||
|
}
|
|
@ -1,33 +1,43 @@
|
||||||
#include <TestCase.h>
|
#include <TestCase.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
TestCase::TestCase()
|
BTestCase::BTestCase(std::string name)
|
||||||
: CppUnit::TestCase()
|
|
||||||
, fValidCWD(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TestCase::TestCase(std::string name)
|
|
||||||
: CppUnit::TestCase(name)
|
: CppUnit::TestCase(name)
|
||||||
, fValidCWD(false)
|
, fValidCWD(false)
|
||||||
|
, fSubTestNum(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saves the location of the current working directory. To return to the
|
|
||||||
// last saved working directory, all \ref RestorCWD().
|
|
||||||
void
|
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);
|
fValidCWD = getcwd(fCurrentWorkingDir, B_PATH_NAME_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restores the current working directory to last directory saved by a
|
/* If SaveCWD() has not been called and an alternate
|
||||||
call to SaveCWD(). If SaveCWD() has not been called and an alternate
|
|
||||||
directory is specified by alternate, the current working directory is
|
directory is specified by alternate, the current working directory is
|
||||||
changed to alternate. If alternate is null, the current working directory
|
changed to alternate. If alternate is null, the current working directory
|
||||||
is not modified.
|
is not modified.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TestCase::RestoreCWD(const char *alternate) {
|
BTestCase::RestoreCWD(const char *alternate) {
|
||||||
if (fValidCWD)
|
if (fValidCWD)
|
||||||
chdir(fCurrentWorkingDir);
|
chdir(fCurrentWorkingDir);
|
||||||
else if (alternate != NULL)
|
else if (alternate != NULL)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -1,36 +1,209 @@
|
||||||
#include <TestShell.h>
|
#include <TestShell.h>
|
||||||
#include <iostream>
|
|
||||||
#include <Path.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
TestShell::TestShell(const std::string &description, SyncObject *syncObject)
|
#include <cppunit/Exception.h>
|
||||||
: CppUnitShell(description, syncObject),
|
#include <cppunit/Test.h>
|
||||||
fTestDir(NULL)
|
#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
|
int
|
||||||
TestShell::Run(int argc, char *argv[])
|
BTestShell::Run(int argc, char *argv[]) {
|
||||||
{
|
// Parse the command line args
|
||||||
// Let's hope BPath does work. ;-)
|
if (!ProcessArguments(argc, argv))
|
||||||
BPath path(argv[0]);
|
return 0;
|
||||||
if (path.InitCheck() == B_OK) {
|
|
||||||
fTestDir = new BPath();
|
// Add the proper tests to our suite (or exit if there
|
||||||
if (path.GetParent(fTestDir) != B_OK)
|
// are no tests installed).
|
||||||
printf("Couldn't get test dir.\n");
|
CppUnit::TestSuite suite;
|
||||||
} else
|
if (fTests.empty()) {
|
||||||
printf("Couldn't find the path to the test app.\n");
|
|
||||||
return CppUnitShell::Run(argc, argv);
|
// 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*
|
BTestShell::VerbosityLevel
|
||||||
TestShell::TestDir() const
|
BTestShell::Verbosity() const {
|
||||||
{
|
return fVerbosityLevel;
|
||||||
return (fTestDir ? fTestDir->Path() : NULL);
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue