How To Use
 All Modules Pages
/// \file
/// \author Gerald Fahrnholz
#pragma once
#include "./CommonSources/commondefinitions.h"
#include <string>
#include <memory>
namespace TestToolBox
{
///-------------------------------------------------------------------------
/// Variadic template helper function to build string of any number of parameters of arbitrary type
/// Precondition: each parameter is streamable
/// Examples:
/// ParamDescription ("SomeConfigFile", 12, 3.14) -> "SomeConfigFile, 12, 3.14"
/// ParamDescription (12, "SomeConfigFile") -> "12, SomeConfigFile"
/// ParamDescription (3.14) -> "3.14"
/// ParamDescription () -> "" (empty string)
///-------------------------------------------------------------------------
/// Non template function with no args
inline std::string ParamDescription()
{
return "";
}
/// Template function with a single arg
/// (this is the only place where streaming is used)
template <typename T>
std::string ParamDescription(T t)
{
std::ostringstream oss;
oss << t;
return oss.str();
}
/// Template function matching for 2 or more args
/// (automatically considers if delimiter is needed or not;
/// if rest = null then function will not be called, instead
/// the function above will be used)
template <typename T, typename... Rest>
std::string ParamDescription(T t, Rest... rest)
{
return ParamDescription(t) + ", " + ParamDescription(rest...);
}
//-------------------------------------------------------------------------
/// Base class from which all user defined "conditional" test fixtures have to be derived
class ConditionalFixtureBase
{
public:
virtual ~ConditionalFixtureBase()
{
}
};
using ConditionalFixtureBaseUp = std::unique_ptr<ConditionalFixtureBase>;
//-----------------------------------------------------------------------------
/// Singleton class which stores the instance of a conditional fixture and supports
/// proper replacement with a new conditional fixture if a test case has new requirements.
class ConditionalFixture
{
public:
USE_AS_SIMPLE_SINGLETON(ConditionalFixture);
/// Destructor
~ConditionalFixture(void);
/// Variadic template function checking whether the given test fixture needs to be created.
/// A test fixture is created if the class type or the description of the params differs from
/// the test fixture created as last. Before creating a new fixture the last fixture will be
/// destroyed.
template <typename FixtureClassType, typename... Params>
bool CreateIfNotExisting(Params... params)
{
std::string paramsString = ParamDescription(params...);
std::string delimiter = paramsString.empty() ? "" : " with args ";
std::string idFixture = typeid(FixtureClassType).name() + delimiter + paramsString;
if (!Exists(idFixture))
{
RemoveCurrent();
Add(idFixture, ConditionalFixtureBaseUp(new FixtureClassType(params...)));
return true;
}
return false;
}
/// Delete the current test fixture if existing
bool RemoveCurrent();
private:
struct ConditionalFixtureEntry;
using ConditionalFixtureEntryUp = std::unique_ptr<ConditionalFixtureEntry>;
struct ConditionalFixtureEntry
{
ConditionalFixtureEntry(
std::string const & in_idFixture,
ConditionalFixtureBaseUp in_upConditionalFixtureBase)
: idFixture(in_idFixture)
, upConditionalFixtureBase(std::move(in_upConditionalFixtureBase))
{
}
std::string idFixture;
ConditionalFixtureBaseUp upConditionalFixtureBase;
// not yet used; possible extension to build nested fixtures
// int level = 1;
// ConditionalFixtureEntryUp upLowerLevelFixture;
};
// Info about current test fixture instance
ConditionalFixtureEntryUp m_upConditionalFixtureEntry;
private:
/// Access/create instance via static method Get()
explicit ConditionalFixture(void) {};
bool Exists(std::string const & in_idFixture, int in_level = 1);
void Add(std::string const & in_idFixture, ConditionalFixtureBaseUp in_upFixture);
void WriteLine(std::string const & info);
};
DEFINE_SIMPLE_SINGLETON_POINTER(ConditionalFixture);
}
#define TTB_COND_FIXTURE(FixtureClassType) \
TestToolBox::TheConditionalFixture()->CreateIfNotExisting<FixtureClassType>();
#define TTB_COND_FIXTURE_1(FixtureClassType, p1) \
TestToolBox::TheConditionalFixture()->CreateIfNotExisting<FixtureClassType>(p1);
#define TTB_COND_FIXTURE_2(FixtureClassType, p1, p2) \
TestToolBox::TheConditionalFixture()->CreateIfNotExisting<FixtureClassType>(p1,p2);
#define TTB_COND_FIXTURE_3(FixtureClassType, p1, p2, p3) \
TestToolBox::TheConditionalFixture()->CreateIfNotExisting<FixtureClassType>(p1,p2,p3);
#define TTB_COND_FIXTURE_4(FixtureClassType, p1, p2, p3, p4) \
TestToolBox::TheConditionalFixture()->CreateIfNotExisting<FixtureClassType>(p1,p2,p3,p4);
#define TTB_COND_FIXTURE_CLEANUP() \
TestToolBox::TheConditionalFixture()->RemoveCurrent();
/*
Possible extensions:
- suppport several levels of fixtures (nested fixtures)
- deleting a fixture of some level will also delete all fixtures with lower level
- it may be necessary to use regular fixtures as a subfixture of a conditional fixture
(e.g. reset some basic settings while not restarting the whole system)
- for now try using regular BOOST fixtures in combination with TTB conditional fixtures
to accomplish this task
*/
//-----------------------------------------------------------------------------