I have a C++ application that executes test cases. It is possible that some test cases will depend on output from other test cases.
All test cases implement a basic interface:
/// base class for all test cases
class ITest
{
public:
virtual void Execute() = 0;
};
Test cases that produce some object that may be useful to other test cases implement this interface:
/// implemented by test cases that provide data to other test cases
template< class Obj >
class IDependency
{
public:
virtual Obj Get() = 0;
};
Test cases that require data from other test cases implement this interface:
/// implemented by test cases that require data from other test cases
template< class Obj >
class IDependent
{
public:
void SetDependency( IDependency< Obj >* dependency )
{
dependency_ = dependency;
};
protected:
Obj GetDependency() const
{
return dependency_->Get();
};
private:
IDependency< Obj >* dependency_;
};
Two example test cases. One requires a const wchar_t
object; one produces that object:
/// A test case that provides a "const wchar_t*" object to other test cases
class Foo : public ITest,
public IDependency< const wchar_t* >
{
public:
const wchar_t* Get()
{
if( object_.length() == 0 )
Execute();
return object_.c_str();
};
virtual void Execute()
{
printf( "Execute Foo\n" );
object_ = L"Object produced by Foo";
};
private:
std::wstring object_;
};
/// A test case that first requires a "const wchar_t*" object
class Bar : public ITest,
public IDependent< const wchar_t* >
{
public:
virtual void Execute()
{
const wchar_t* needed_object = GetDependency();
printf( "Execute Bar with %S\n", needed_object );
};
};
The test cases are stored in a list. Cases are added to the list by a registration process:
/// List of test cases to execute
std::vector< ITest* > list_;
/// Register a test case to execute with the system
void Register( ITest* test_case )
{
list_.push_back( test_case );
}
Here's my problem. I wanted to implement an overload of the 'Register()' function that also accepts dependencies. But, because dependencies can be of any type (not just the 'const wchar_t*' from this example), I'm not sure how to manage this. Below is an example of more or less what I'm looking for, but I'm not sure how to make it work.
/// Register a test case with dependencies with the system
void Register( ITest* test_case, ITest* dependency, ... )
{
IDependent< ??? >* dependent = dynamic_cast< IDependent< ??? >* >( test_case );
IDependency< ??? >* dep = dynamic_cast< IDependency< ??? >* >( dependency );
va_list dep_list;
for( va_start( dep_list, dependency );
NULL != dep;
dep = dynamic_cast< IDependency< ??? >* >( va_arg( dep_list, ITest* ) ) )
{
dependent->SetDependency( dep );
}
va_end( dep_list );
Register( test_case );
}
An example usage:
int _tmain( int argc, _TCHAR* argv[] )
{
/// Test case Foo
Foo foo;
/// Test case bar (depends on Foo)
Bar bar;
/// Register test case Bar with a dependency on Foo
Register( &bar, &foo );
/// Execute Bar. Because it depends on Foo, that will be executed first
list_->begin()->Execute();
return 0;
}
Expected output:
Execute Foo
Execute Bar with Object produced by Foo
Does anybody have any suggestions on how I can successfully implement this architecture? (or a better architecture that actually works?)
Thanks, PaulH