Added support for asserting debugger() calls. The macro CPPUNIT_ASSERT_DEBUGGER shall be used in that case.
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5298 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -7,14 +7,18 @@
|
|||||||
#include <cppunit/TestListener.h>
|
#include <cppunit/TestListener.h>
|
||||||
#include <cppunit/TestResult.h>
|
#include <cppunit/TestResult.h>
|
||||||
#include <cppunit/TestResultCollector.h>
|
#include <cppunit/TestResultCollector.h>
|
||||||
|
#include <image.h>
|
||||||
#include <TestSuite.h>
|
#include <TestSuite.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class BDirectory;
|
class BDirectory;
|
||||||
|
class BLocker;
|
||||||
class BPath;
|
class BPath;
|
||||||
|
|
||||||
|
class ElfSymbolPatchGroup;
|
||||||
|
|
||||||
// Defines SuiteFunction to be a pointer to a function that
|
// Defines SuiteFunction to be a pointer to a function that
|
||||||
// takes no arguments and returns a pointer to a CppUnit::Test
|
// takes no arguments and returns a pointer to a CppUnit::Test
|
||||||
typedef CppUnit::Test* (*SuiteFunction)(void);
|
typedef CppUnit::Test* (*SuiteFunction)(void);
|
||||||
@@ -22,6 +26,19 @@ typedef CppUnit::Test* (*SuiteFunction)(void);
|
|||||||
// This is just absurd to have to type...
|
// This is just absurd to have to type...
|
||||||
typedef CppUnit::SynchronizedObject::SynchronizationObject SyncObject;
|
typedef CppUnit::SynchronizedObject::SynchronizationObject SyncObject;
|
||||||
|
|
||||||
|
/*! \brief Executes a statement that is supposed to call debugger().
|
||||||
|
An exception is thrown if the debugger is not invoked by the
|
||||||
|
statement.
|
||||||
|
*/
|
||||||
|
#define CPPUNIT_ASSERT_DEBUGGER(statement) \
|
||||||
|
BTestShell::GlobalShell()->ExpectDebuggerCall(); \
|
||||||
|
statement; \
|
||||||
|
::CppUnit::Asserter::failIf( \
|
||||||
|
!BTestShell::GlobalShell()->WasDebuggerCalled(), \
|
||||||
|
(#statement), \
|
||||||
|
CPPUNIT_SOURCELINE() );
|
||||||
|
|
||||||
|
|
||||||
//! BeOS savvy command line interface for the CppUnit testing framework.
|
//! BeOS savvy command line interface for the CppUnit testing framework.
|
||||||
/*! This class provides a fully functional command-line testing interface
|
/*! This class provides a fully functional command-line testing interface
|
||||||
built on top of the CppUnit testing library. You add named test suites
|
built on top of the CppUnit testing library. You add named test suites
|
||||||
@@ -83,6 +100,8 @@ public:
|
|||||||
const char* TestDir() const;
|
const char* TestDir() const;
|
||||||
static const char* GlobalTestDir() { return (fGlobalShell ? fGlobalShell->TestDir() : NULL); };
|
static const char* GlobalTestDir() { return (fGlobalShell ? fGlobalShell->TestDir() : NULL); };
|
||||||
|
|
||||||
|
void ExpectDebuggerCall();
|
||||||
|
bool WasDebuggerCalled();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef std::map<std::string, CppUnit::Test*> TestMap;
|
typedef std::map<std::string, CppUnit::Test*> TestMap;
|
||||||
@@ -101,6 +120,13 @@ protected:
|
|||||||
static const char indent[];
|
static const char indent[];
|
||||||
bool fListTestsAndExit;
|
bool fListTestsAndExit;
|
||||||
BPath *fTestDir;
|
BPath *fTestDir;
|
||||||
|
BLocker *fPatchGroupLocker;
|
||||||
|
int32 fTLSDebuggerCall;
|
||||||
|
ElfSymbolPatchGroup *fPatchGroup;
|
||||||
|
void (*fOldDebuggerHook)(const char*);
|
||||||
|
image_id (*fOldLoadAddOnHook)(const char*);
|
||||||
|
status_t (*fOldUnloadAddOnHook)(image_id);
|
||||||
|
|
||||||
|
|
||||||
//! Prints a brief description of the program.
|
//! Prints a brief description of the program.
|
||||||
virtual void PrintDescription(int argc, char *argv[]);
|
virtual void PrintDescription(int argc, char *argv[]);
|
||||||
@@ -141,6 +167,8 @@ protected:
|
|||||||
//! Sets the current test directory.
|
//! Sets the current test directory.
|
||||||
void UpdateTestDir(char *argv[]);
|
void UpdateTestDir(char *argv[]);
|
||||||
|
|
||||||
|
void InstallPatches();
|
||||||
|
void UninstallPatches();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//! Prevents the use of the copy constructor.
|
//! Prevents the use of the copy constructor.
|
||||||
@@ -149,6 +177,14 @@ private:
|
|||||||
//! Prevents the use of the copy operator.
|
//! Prevents the use of the copy operator.
|
||||||
void operator =( const BTestShell © );
|
void operator =( const BTestShell © );
|
||||||
|
|
||||||
|
void _Debugger(const char* message);
|
||||||
|
image_id _LoadAddOn(const char* path);
|
||||||
|
status_t _UnloadAddOn(image_id image);
|
||||||
|
|
||||||
|
static void _DebuggerHook(const char* message);
|
||||||
|
static image_id _LoadAddOnHook(const char* path);
|
||||||
|
static status_t _UnloadAddOnHook(image_id image);
|
||||||
|
|
||||||
}; // class BTestShell
|
}; // class BTestShell
|
||||||
|
|
||||||
#endif // _beos_test_shell_h_
|
#endif // _beos_test_shell_h_
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
SubDir OBOS_TOP src tools cppunit ;
|
SubDir OBOS_TOP src tools cppunit ;
|
||||||
|
|
||||||
|
UseHeaders [ FDirName $(OBOS_TOP) headers tools elfsymbolpatcher ] ;
|
||||||
|
|
||||||
rule CppUnitLibrary
|
rule CppUnitLibrary
|
||||||
{
|
{
|
||||||
# CppUnitLibrary <sources> ;
|
# CppUnitLibrary <sources> ;
|
||||||
@@ -49,4 +51,5 @@ CppUnitLibrary
|
|||||||
LinkSharedOSLibs libcppunit.so :
|
LinkSharedOSLibs libcppunit.so :
|
||||||
stdc++.r4
|
stdc++.r4
|
||||||
/boot/develop/lib/x86/libbe.so
|
/boot/develop/lib/x86/libbe.so
|
||||||
|
libelfsymbolpatcher.a
|
||||||
;
|
;
|
||||||
|
|||||||
+196
-11
@@ -1,19 +1,27 @@
|
|||||||
#include <TestShell.h>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <cppunit/Exception.h>
|
#include <Autolock.h>
|
||||||
#include <cppunit/Test.h>
|
|
||||||
#include <cppunit/TestFailure.h>
|
|
||||||
#include <cppunit/TestResult.h>
|
|
||||||
#include <cppunit/TestSuite.h>
|
|
||||||
#include <Directory.h>
|
#include <Directory.h>
|
||||||
#include <Entry.h>
|
#include <Entry.h>
|
||||||
#include <image.h>
|
#include <image.h>
|
||||||
|
#include <Locker.h>
|
||||||
#include <Path.h>
|
#include <Path.h>
|
||||||
|
#include <TLS.h>
|
||||||
|
|
||||||
|
#include <cppunit/Exception.h>
|
||||||
|
#include <cppunit/Test.h>
|
||||||
|
#include <cppunit/TestAssert.h>
|
||||||
|
#include <cppunit/TestFailure.h>
|
||||||
|
#include <cppunit/TestResult.h>
|
||||||
|
#include <cppunit/TestSuite.h>
|
||||||
|
|
||||||
|
#include <TestShell.h>
|
||||||
#include <TestListener.h>
|
#include <TestListener.h>
|
||||||
#include <set>
|
|
||||||
#include <map>
|
#include <ElfSymbolPatcher.h>
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
BTestShell *BTestShell::fGlobalShell = NULL;
|
BTestShell *BTestShell::fGlobalShell = NULL;
|
||||||
const char BTestShell::indent[] = " ";
|
const char BTestShell::indent[] = " ";
|
||||||
@@ -24,11 +32,18 @@ BTestShell::BTestShell(const std::string &description, SyncObject *syncObject)
|
|||||||
, fDescription(description)
|
, fDescription(description)
|
||||||
, fListTestsAndExit(false)
|
, fListTestsAndExit(false)
|
||||||
, fTestDir(NULL)
|
, fTestDir(NULL)
|
||||||
|
, fPatchGroupLocker(new(nothrow) BLocker)
|
||||||
|
, fPatchGroup(NULL)
|
||||||
|
, fOldDebuggerHook(NULL)
|
||||||
|
, fOldLoadAddOnHook(NULL)
|
||||||
|
, fOldUnloadAddOnHook(NULL)
|
||||||
{
|
{
|
||||||
};
|
fTLSDebuggerCall = tls_allocate();
|
||||||
|
}
|
||||||
|
|
||||||
BTestShell::~BTestShell() {
|
BTestShell::~BTestShell() {
|
||||||
delete fTestDir;
|
delete fTestDir;
|
||||||
|
delete fPatchGroupLocker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -183,7 +198,9 @@ BTestShell::Run(int argc, char *argv[]) {
|
|||||||
|
|
||||||
// Run all the tests
|
// Run all the tests
|
||||||
InitOutput();
|
InitOutput();
|
||||||
|
InstallPatches();
|
||||||
suite.run(&fTestResults);
|
suite.run(&fTestResults);
|
||||||
|
UninstallPatches();
|
||||||
PrintResults();
|
PrintResults();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -199,6 +216,36 @@ BTestShell::TestDir() const {
|
|||||||
return (fTestDir ? fTestDir->Path() : NULL);
|
return (fTestDir ? fTestDir->Path() : NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExpectDebuggerCall
|
||||||
|
/*! \brief Marks the current thread as ready for a debugger() call.
|
||||||
|
|
||||||
|
A subsequent call of debugger() will be intercepted and a respective
|
||||||
|
flag will be set. WasDebuggerCalled() will then return \c true.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
BTestShell::ExpectDebuggerCall()
|
||||||
|
{
|
||||||
|
void *var = tls_get(fTLSDebuggerCall);
|
||||||
|
::CppUnit::Asserter::failIf(var, "ExpectDebuggerCall(): Already expecting "
|
||||||
|
"a debugger() call.");
|
||||||
|
tls_set(fTLSDebuggerCall, (void*)1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// WasDebuggerCalled
|
||||||
|
/*! \brief Returns whether the current thread has invoked debugger() since
|
||||||
|
the last ExpectDebuggerCall() invocation and resets the mode so
|
||||||
|
that subsequent debugger() calls will hit the debugger.
|
||||||
|
\return \c true, if debugger() has been called by the current thread since
|
||||||
|
the last invocation of ExpectDebuggerCall(), \c false otherwise.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
BTestShell::WasDebuggerCalled()
|
||||||
|
{
|
||||||
|
void *var = tls_get(fTLSDebuggerCall);
|
||||||
|
tls_set(fTLSDebuggerCall, NULL);
|
||||||
|
return ((int)var > 1);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BTestShell::PrintDescription(int argc, char *argv[]) {
|
BTestShell::PrintDescription(int argc, char *argv[]) {
|
||||||
cout << endl << fDescription;
|
cout << endl << fDescription;
|
||||||
@@ -412,3 +459,141 @@ BTestShell::UpdateTestDir(char *argv[]) {
|
|||||||
cout << "Couldn't find the path to the test app." << endl;
|
cout << "Couldn't find the path to the test app." << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InstallPatches
|
||||||
|
/*! \brief Patches the debugger() function.
|
||||||
|
|
||||||
|
load_add_on() and unload_add_on() are patches as well, to keep the
|
||||||
|
patch group up to date, when images are loaded/unloaded.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
BTestShell::InstallPatches()
|
||||||
|
{
|
||||||
|
if (fPatchGroup) {
|
||||||
|
cerr << "BTestShell::InstallPatches(): Patch group already exist!"
|
||||||
|
<< endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BAutolock locker(fPatchGroupLocker);
|
||||||
|
if (!locker.IsLocked()) {
|
||||||
|
cerr << "BTestShell::InstallPatches(): Failed to acquire patch "
|
||||||
|
"group lock!" << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fPatchGroup = new(nothrow) ElfSymbolPatchGroup;
|
||||||
|
// init the symbol patch group
|
||||||
|
if (!fPatchGroup) {
|
||||||
|
cerr << "BTestShell::InstallPatches(): Failed to allocate patch "
|
||||||
|
"group!" << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (// debugger()
|
||||||
|
fPatchGroup->AddPatch("debugger", (void*)&_DebuggerHook,
|
||||||
|
(void**)&fOldDebuggerHook) == B_OK
|
||||||
|
// load_add_on()
|
||||||
|
&& fPatchGroup->AddPatch("load_add_on", (void*)&_LoadAddOnHook,
|
||||||
|
(void**)&fOldLoadAddOnHook) == B_OK
|
||||||
|
// unload_add_on()
|
||||||
|
&& fPatchGroup->AddPatch("unload_add_on", (void*)&_UnloadAddOnHook,
|
||||||
|
(void**)&fOldUnloadAddOnHook) == B_OK
|
||||||
|
) {
|
||||||
|
// everything went fine
|
||||||
|
fPatchGroup->Patch();
|
||||||
|
} else {
|
||||||
|
cerr << "BTestShell::InstallPatches(): Failed to patch all symbols!"
|
||||||
|
<< endl;
|
||||||
|
UninstallPatches();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UninstallPatches
|
||||||
|
/*! \brief Undoes the patches applied by InstallPatches().
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
BTestShell::UninstallPatches()
|
||||||
|
{
|
||||||
|
BAutolock locker(fPatchGroupLocker);
|
||||||
|
if (!locker.IsLocked()) {
|
||||||
|
cerr << "BTestShell::UninstallPatches(): "
|
||||||
|
"Failed to acquire patch group lock!" << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fPatchGroup) {
|
||||||
|
fPatchGroup->Restore();
|
||||||
|
delete fPatchGroup;
|
||||||
|
fPatchGroup = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _Debugger
|
||||||
|
void
|
||||||
|
BTestShell::_Debugger(const char *message)
|
||||||
|
{
|
||||||
|
if (!this || !fPatchGroup) {
|
||||||
|
debugger(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BAutolock locker(fPatchGroupLocker);
|
||||||
|
if (!locker.IsLocked() || !fPatchGroup) {
|
||||||
|
debugger(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cout << "debugger() called: " << message << endl;
|
||||||
|
void *var = tls_get(fTLSDebuggerCall);
|
||||||
|
if (var)
|
||||||
|
tls_set(fTLSDebuggerCall, (void*)((int)var + 1));
|
||||||
|
else
|
||||||
|
(*fOldDebuggerHook)(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _LoadAddOn
|
||||||
|
image_id
|
||||||
|
BTestShell::_LoadAddOn(const char *path)
|
||||||
|
{
|
||||||
|
if (!this || !fPatchGroup)
|
||||||
|
return load_add_on(path);
|
||||||
|
BAutolock locker(fPatchGroupLocker);
|
||||||
|
if (!locker.IsLocked() || !fPatchGroup)
|
||||||
|
return load_add_on(path);
|
||||||
|
image_id result = (*fOldLoadAddOnHook)(path);
|
||||||
|
fPatchGroup->Update();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _UnloadAddOn
|
||||||
|
status_t
|
||||||
|
BTestShell::_UnloadAddOn(image_id image)
|
||||||
|
{
|
||||||
|
if (!this || !fPatchGroup)
|
||||||
|
return unload_add_on(image);
|
||||||
|
BAutolock locker(fPatchGroupLocker);
|
||||||
|
if (!locker.IsLocked() || !fPatchGroup)
|
||||||
|
return unload_add_on(image);
|
||||||
|
|
||||||
|
if (!this || !fPatchGroup)
|
||||||
|
return unload_add_on(image);
|
||||||
|
status_t result = (*fOldUnloadAddOnHook)(image);
|
||||||
|
fPatchGroup->Update();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _DebuggerHook
|
||||||
|
void
|
||||||
|
BTestShell::_DebuggerHook(const char *message)
|
||||||
|
{
|
||||||
|
fGlobalShell->_Debugger(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _LoadAddOnHook
|
||||||
|
image_id
|
||||||
|
BTestShell::_LoadAddOnHook(const char *path)
|
||||||
|
{
|
||||||
|
return fGlobalShell->_LoadAddOn(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _UnloadAddOnHook
|
||||||
|
status_t
|
||||||
|
BTestShell::_UnloadAddOnHook(image_id image)
|
||||||
|
{
|
||||||
|
return fGlobalShell->_UnloadAddOn(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user