
6 changed files with 530 additions and 518 deletions
@ -0,0 +1,360 @@ |
|||||
|
#include "pEpTestTree.hh" |
||||
|
#include "../../../src/std_utils.hh" |
||||
|
#include "../../framework/utils.hh" |
||||
|
#include <iostream> |
||||
|
#include <unistd.h> |
||||
|
#include <cstdlib> |
||||
|
#include <sys/stat.h> |
||||
|
#include <functional> |
||||
|
#include <algorithm> |
||||
|
#include <sstream> |
||||
|
|
||||
|
|
||||
|
using namespace std; |
||||
|
using namespace pEp::Adapter; |
||||
|
using namespace pEp::Utils; |
||||
|
|
||||
|
namespace pEp { |
||||
|
namespace Test { |
||||
|
string pEpTestTree::_data_root = "./peptest"; |
||||
|
bool pEpTestTree::debug_log_enabled = false; |
||||
|
|
||||
|
// PUBIC CONCSTRUCTORS / FACTORY -----------------------------------------------------------
|
||||
|
// static
|
||||
|
pEpTestTree pEpTestTree::createRootNode( |
||||
|
pEpTestModel& model, |
||||
|
const string& name, |
||||
|
const NodeFunc& test_func, |
||||
|
ExecutionMode exec_mode) |
||||
|
{ |
||||
|
pEpTestTree ret(nullptr, &model, name, test_func, exec_mode); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
// static
|
||||
|
pEpTestTree pEpTestTree::createChildNode( |
||||
|
pEpTestTree& parent, |
||||
|
const string& name, |
||||
|
const NodeFunc& test_func, |
||||
|
ExecutionMode exec_mode) |
||||
|
{ |
||||
|
pEpTestTree ret(&parent, nullptr, name, test_func, exec_mode); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
string pEpTestTree::getNodeName() const |
||||
|
{ |
||||
|
return _name; |
||||
|
} |
||||
|
|
||||
|
string pEpTestTree::getNodePath() const |
||||
|
{ |
||||
|
pEpLogClass("called"); |
||||
|
string ret; |
||||
|
|
||||
|
if (!_isRootNode()) { |
||||
|
ret = _parent->getNodePath() + "/" + getNodeName(); |
||||
|
} else { |
||||
|
ret = getNodeName(); |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
// For:
|
||||
|
// RootNode - "<name>"
|
||||
|
// ProcessNode - ".../<proc>"
|
||||
|
// When Process as dir. parent - ".../<proc>/name"
|
||||
|
// When no process as dir. parent - ".../<proc>/.../name"
|
||||
|
std::string pEpTestTree::getNodePathShort() const |
||||
|
{ |
||||
|
string ret; |
||||
|
if (_isRootNode()) { |
||||
|
ret = getNodeName(); |
||||
|
} else { |
||||
|
if (_isProcessNode()) { |
||||
|
ret += ".../" + getNodeName(); |
||||
|
} else { |
||||
|
if (&(_getParentingProcessNode()) == (_parent)) { |
||||
|
ret = _getParentingProcessNode().getNodePathShort() + "/" + getNodeName(); |
||||
|
} else { |
||||
|
ret = _getParentingProcessNode().getNodePathShort() + "/.../" + getNodeName(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
// Inherited (if null see parent recursively)
|
||||
|
pEpTestModel& pEpTestTree::getModel() const |
||||
|
{ |
||||
|
pEpLogClass("called"); |
||||
|
pEpTestModel* ret = nullptr; |
||||
|
if (_model == nullptr) { |
||||
|
ret = &(_parent->getModel()); |
||||
|
} else { |
||||
|
ret = _model; |
||||
|
} |
||||
|
assert(ret != nullptr); |
||||
|
// cant be null because for createChildNode() you have to provide TestNode& instance,
|
||||
|
// and the only other way to get one is by createRootNode() which in turn requires a TestModel&
|
||||
|
return *ret; |
||||
|
} |
||||
|
|
||||
|
// RootNodes have their own data_dir
|
||||
|
// ProcessNodes have their own data_dir inside their RootNote dir (nameclash prossible)
|
||||
|
// All other nodes inherit data_dir from their Root/ProcessNode
|
||||
|
string pEpTestTree::getDataDir() const |
||||
|
{ |
||||
|
pEpLogClass("called"); |
||||
|
string ret; |
||||
|
if (_isRootNode()) { |
||||
|
ret = getDataRoot() + getNodeName() + "/"; |
||||
|
} else { |
||||
|
if (_parent->_exec_mode == ExecutionMode::PROCESS_SERIAL || |
||||
|
_parent->_exec_mode == ExecutionMode::PROCESS_PARALLEL) { |
||||
|
|
||||
|
ret = _parent->getDataDir() + getNodeName(); |
||||
|
} else { |
||||
|
// inherit
|
||||
|
ret = _parent->getDataDir(); |
||||
|
} |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
// static
|
||||
|
void pEpTestTree::setDataRoot(const string& dir) |
||||
|
{ |
||||
|
pEpTestTree::_data_root = dir; |
||||
|
} |
||||
|
|
||||
|
// static
|
||||
|
string pEpTestTree::getDataRoot() |
||||
|
{ |
||||
|
return pEpTestTree::_data_root; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void pEpTestTree::run(const pEpTestTree* caller) const |
||||
|
{ |
||||
|
pEpLogClass("called"); |
||||
|
// caller is never nullptr if called by another pEpTestTree
|
||||
|
if (caller == nullptr) { |
||||
|
pEpLog::logH1("Starting pEpTestTree from node: " + getNodePathShort()); |
||||
|
pEpLog::log(to_string()); |
||||
|
} |
||||
|
|
||||
|
pEpLog::logH2( |
||||
|
"[ " + to_string(_exec_mode) + " / " + getNodePathShort() + "]"); |
||||
|
// Execute in fork and wait here until process ends
|
||||
|
if (_exec_mode == ExecutionMode::PROCESS_SERIAL) { // fork
|
||||
|
_executeInFork(bind(&pEpTestTree::_run, this), true); |
||||
|
// Execute in fork and go on, wait for process execution in the end
|
||||
|
} else if (_exec_mode == ExecutionMode::PROCESS_PARALLEL) { |
||||
|
_executeInFork(bind(&pEpTestTree::_run, this), false); |
||||
|
// Execute as normal funciton
|
||||
|
} else if (_exec_mode == ExecutionMode::FUNCTION) { |
||||
|
_run(); |
||||
|
} else if (_exec_mode == ExecutionMode::THREAD_PARALLEL) { |
||||
|
throw invalid_argument(to_string(_exec_mode) + " - not implemented"); |
||||
|
} else if (_exec_mode == ExecutionMode::THREAD_SERIAL) { |
||||
|
throw invalid_argument(to_string(_exec_mode) + " - not implemented"); |
||||
|
} |
||||
|
|
||||
|
if (caller == nullptr) { |
||||
|
_waitChildProcesses(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
string pEpTestTree::to_string(bool recursive, int indent) const |
||||
|
{ |
||||
|
string ret; |
||||
|
stringstream builder; |
||||
|
builder << string(indent, '\t'); |
||||
|
builder << getNodeName(); |
||||
|
builder << "[" << to_string(_exec_mode) << "]"; |
||||
|
builder << endl; |
||||
|
ret = builder.str(); |
||||
|
|
||||
|
if (recursive) { |
||||
|
indent++; |
||||
|
for (const pair<string, const pEpTestTree&> child : _children) { |
||||
|
ret += child.second.to_string(true, indent); |
||||
|
} |
||||
|
indent--; |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
string pEpTestTree::to_string(const ExecutionMode& emode) |
||||
|
{ |
||||
|
switch (emode) { |
||||
|
case ExecutionMode::FUNCTION: |
||||
|
return "FUNCTION"; |
||||
|
case ExecutionMode::PROCESS_SERIAL: |
||||
|
return "PROCESS_SERIAL"; |
||||
|
case ExecutionMode::PROCESS_PARALLEL: |
||||
|
return "PROCESS_PARALLEL"; |
||||
|
case ExecutionMode::THREAD_SERIAL: |
||||
|
return "THREAD_SERIAL"; |
||||
|
case ExecutionMode::THREAD_PARALLEL: |
||||
|
return "THREAD_PARALLEL"; |
||||
|
case ExecutionMode::INHERIT: |
||||
|
return "INHERIT"; |
||||
|
default: |
||||
|
return "UNDEFINED EXECUTION MODE"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//Well, ok, lets just add some little convenience logging service in here, too
|
||||
|
void pEpTestTree::log(const string& msg) const |
||||
|
{ |
||||
|
stringstream builder; |
||||
|
builder << "["; |
||||
|
builder << std::to_string(getpid()); |
||||
|
builder << " - "; |
||||
|
builder << getNodePathShort(); |
||||
|
builder << "] - "; |
||||
|
builder << msg; |
||||
|
builder << endl; |
||||
|
cout << builder.str(); |
||||
|
} |
||||
|
|
||||
|
// PRIVATE ---------------------------------------------------------------------------------
|
||||
|
|
||||
|
pEpTestTree::pEpTestTree( |
||||
|
pEpTestTree* const parent, |
||||
|
pEpTestModel* model, |
||||
|
const string& name, |
||||
|
const NodeFunc& test_func, |
||||
|
ExecutionMode exec_mode) : |
||||
|
_parent(parent), |
||||
|
_model(model), _name(_normalizeName(name)), _test_func(test_func), _exec_mode(exec_mode) |
||||
|
{ |
||||
|
logger_debug.set_instancename(getNodePath()); |
||||
|
if (!_isRootNode()) { |
||||
|
parent->_addChildNode(*this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void pEpTestTree::_run() const |
||||
|
{ |
||||
|
if (_test_func) { |
||||
|
_test_func(*this); |
||||
|
} else { |
||||
|
pEpLog::log("No function to execute"); |
||||
|
} |
||||
|
if (!_children.empty()) { |
||||
|
for (const pair<string, pEpTestTree&> child : _children) { |
||||
|
child.second.run(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void pEpTestTree::_executeInFork(function<void(void)> func, bool wait_child) const |
||||
|
{ |
||||
|
pid_t pid; |
||||
|
pid = fork(); |
||||
|
if (pid == pid_t(0)) { |
||||
|
func(); |
||||
|
exit(0); |
||||
|
} else if (pid < pid_t(0)) { |
||||
|
throw runtime_error("Error forking"); |
||||
|
} |
||||
|
if (wait_child) { |
||||
|
_waitChildProcesses(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void pEpTestTree::_waitChildProcesses() const |
||||
|
{ |
||||
|
int status; |
||||
|
pid_t pid; |
||||
|
while ((pid = wait(&status)) > 0) { |
||||
|
pEpLog::log( |
||||
|
"process[" + std::to_string((int)pid) + |
||||
|
"] terminated with status: " + std::to_string(status)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void pEpTestTree::_addChildNode(pEpTestTree& node) |
||||
|
{ |
||||
|
_children.insert(pair<string, pEpTestTree&>(node.getNodeName(), node)); |
||||
|
} |
||||
|
|
||||
|
bool pEpTestTree::_isProcessNode() const |
||||
|
{ |
||||
|
bool ret = false; |
||||
|
if (_exec_mode == ExecutionMode::PROCESS_SERIAL || |
||||
|
_exec_mode == ExecutionMode::PROCESS_PARALLEL) { |
||||
|
ret = true; |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
bool pEpTestTree::_isRootNode() const |
||||
|
{ |
||||
|
bool ret = false; |
||||
|
if (_parent == nullptr) { |
||||
|
ret = true; |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
const pEpTestTree& pEpTestTree::_getRootNode() const |
||||
|
{ |
||||
|
const pEpTestTree* ret = nullptr; |
||||
|
if (!_isRootNode()) { |
||||
|
ret = &(_parent->_getRootNode()); |
||||
|
} else { |
||||
|
ret = this; |
||||
|
} |
||||
|
assert(ret != nullptr); |
||||
|
// cant be null because for createChildNode() you need to provide a TestNode& and
|
||||
|
// the only other way is using createRootNode() which has parent == nullptr
|
||||
|
return *ret; |
||||
|
} |
||||
|
|
||||
|
const pEpTestTree& pEpTestTree::_getParentingProcessNode() const |
||||
|
{ |
||||
|
if (_isRootNode() || _isProcessNode()) { |
||||
|
return *this; |
||||
|
} else { |
||||
|
return _parent->_getParentingProcessNode(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// name is alphanumeric only (everything else will be replaced by an underscore)
|
||||
|
string pEpTestTree::_normalizeName(string name) const |
||||
|
{ |
||||
|
replace_if( |
||||
|
name.begin(), |
||||
|
name.end(), |
||||
|
[](char c) -> bool { return !isalnum(c); }, |
||||
|
'_'); |
||||
|
|
||||
|
return name; |
||||
|
} |
||||
|
|
||||
|
void pEpTestTree::_data_dir_create() |
||||
|
{ |
||||
|
// Utils::dir_create(getDataDir());
|
||||
|
pEpLog::log("creating dir:" + getDataRoot()); |
||||
|
} |
||||
|
|
||||
|
void pEpTestTree::_data_dir_delete() |
||||
|
{ |
||||
|
try { |
||||
|
Utils::path_delete_all(getDataRoot()); |
||||
|
} catch (const exception& e) { |
||||
|
pEpLog::log("DistTest: - could not delete data dir: " + getDataRoot()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void pEpTestTree::_data_dir_recreate() |
||||
|
{ |
||||
|
_data_dir_delete(); |
||||
|
_data_dir_create(); |
||||
|
}; |
||||
|
} // namespace Test
|
||||
|
} // namespace pEp
|
@ -0,0 +1,118 @@ |
|||||
|
// This file is under GNU General Public License 3.0
|
||||
|
// see LICENSE.txt
|
||||
|
|
||||
|
#ifndef LIBPEPADAPTER_PEPTEST_PEPTESTTREE_HH |
||||
|
#define LIBPEPADAPTER_PEPTEST_PEPTESTTREE_HH |
||||
|
|
||||
|
#include <string> |
||||
|
#include <functional> |
||||
|
#include <map> |
||||
|
#include "../../../src/pEpLog.hh" |
||||
|
#include "pEpTestModel.hh" |
||||
|
|
||||
|
// Yes, the mem mgmt is purely static on purpose (so far)
|
||||
|
|
||||
|
namespace pEp { |
||||
|
namespace Test { |
||||
|
class pEpTestTree { |
||||
|
public: |
||||
|
using NodeFunc = std::function<void(const pEpTestTree&)>; |
||||
|
|
||||
|
enum class ExecutionMode |
||||
|
{ |
||||
|
FUNCTION, |
||||
|
PROCESS_SERIAL, |
||||
|
PROCESS_PARALLEL, |
||||
|
THREAD_SERIAL, // unimplemented
|
||||
|
THREAD_PARALLEL, // unimplemented
|
||||
|
INHERIT |
||||
|
}; |
||||
|
|
||||
|
// Constructors are private
|
||||
|
pEpTestTree() = delete; |
||||
|
|
||||
|
// Modify
|
||||
|
static pEpTestTree createRootNode( |
||||
|
pEpTestModel& model, |
||||
|
const std::string& name, |
||||
|
const NodeFunc& test_func, |
||||
|
ExecutionMode exec_mode = ExecutionMode::FUNCTION); |
||||
|
|
||||
|
static pEpTestTree createChildNode( |
||||
|
pEpTestTree& parent, |
||||
|
const std::string& name, |
||||
|
const NodeFunc& test_func, |
||||
|
ExecutionMode exec_mode = ExecutionMode::FUNCTION); |
||||
|
|
||||
|
// Read-Only
|
||||
|
std::string getNodeName() const; // name only
|
||||
|
std::string getNodePath() const; // full path (is an ID)
|
||||
|
std::string getNodePathShort() const; // parent process + name
|
||||
|
pEpTestModel& getModel() const; |
||||
|
std::string getDataDir() const; |
||||
|
|
||||
|
// Read-Write
|
||||
|
static void setDataRoot(const std::string& dir); |
||||
|
static std::string getDataRoot(); |
||||
|
|
||||
|
// Main funcs
|
||||
|
void run(const pEpTestTree* caller = nullptr) const; |
||||
|
std::string to_string(bool recursive = true, int indent = 0) const; |
||||
|
static std::string to_string(const ExecutionMode& emode); |
||||
|
|
||||
|
// logging service
|
||||
|
void log(const std::string& msg) const; |
||||
|
|
||||
|
// internal logging
|
||||
|
static bool debug_log_enabled; |
||||
|
Adapter::pEpLog::pEpLogger logger_debug{ "pEpTestTree", debug_log_enabled }; |
||||
|
|
||||
|
private: |
||||
|
// CONSTRUCGTORS
|
||||
|
explicit pEpTestTree( |
||||
|
pEpTestTree* parent, |
||||
|
pEpTestModel* model, |
||||
|
const std::string& name, |
||||
|
const NodeFunc& test_func, |
||||
|
ExecutionMode exec_mode); |
||||
|
|
||||
|
// METHODS
|
||||
|
// Execution
|
||||
|
void _run() const; |
||||
|
void _executeInFork(std::function<void(void)> func, bool wait_child) const; |
||||
|
void _waitChildProcesses() const; |
||||
|
|
||||
|
// Modify
|
||||
|
void _addChildNode(pEpTestTree& node); |
||||
|
|
||||
|
// Query
|
||||
|
bool _isProcessNode() const; |
||||
|
bool _isRootNode() const; |
||||
|
const pEpTestTree& _getRootNode() const; |
||||
|
const pEpTestTree& _getParentingProcessNode() const; |
||||
|
|
||||
|
// Util
|
||||
|
std::string _normalizeName(std::string name) const; |
||||
|
|
||||
|
// TODO
|
||||
|
void _data_dir_delete(); |
||||
|
void _data_dir_create(); |
||||
|
void _data_dir_recreate(); |
||||
|
|
||||
|
// Fields
|
||||
|
const std::string _name; |
||||
|
const pEpTestTree* _parent; //nullptr if RootUnit
|
||||
|
pEpTestModel* _model; //nullptr if inherited
|
||||
|
const NodeFunc& _test_func; |
||||
|
const ExecutionMode _exec_mode; |
||||
|
static std::string _data_root; |
||||
|
|
||||
|
std::map<const std::string, pEpTestTree&> _children; // map to guarantee uniqueness of sibling-names
|
||||
|
|
||||
|
// internal logging
|
||||
|
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger_debug; |
||||
|
}; |
||||
|
}; // namespace Test
|
||||
|
}; // namespace pEp
|
||||
|
|
||||
|
#endif // LIBPEPADAPTER_PEPTEST_PEPTESTTREE_HH
|
@ -1,379 +0,0 @@ |
|||||
#include "pEpTestUnit.hh" |
|
||||
#include "../../../src/std_utils.hh" |
|
||||
#include "../../framework/utils.hh" |
|
||||
#include <iostream> |
|
||||
#include <unistd.h> |
|
||||
#include <cstdlib> |
|
||||
#include <sys/stat.h> |
|
||||
#include <functional> |
|
||||
#include <algorithm> |
|
||||
#include <sstream> |
|
||||
|
|
||||
using namespace std; |
|
||||
|
|
||||
namespace pEp { |
|
||||
namespace Test { |
|
||||
bool pEpTestUnit::log_enabled = true; |
|
||||
string pEpTestUnit::data_root = "./peptest"; |
|
||||
pEpTestUnit::ExecutionMode pEpTestUnit::emode_default = pEpTestUnit::ExecutionMode::FUNCTION; |
|
||||
|
|
||||
// RootNode factory
|
|
||||
// static
|
|
||||
pEpTestUnit pEpTestUnit::createRootNode( |
|
||||
pEpTestModel& model, |
|
||||
const std::string& name, |
|
||||
const TestUnitFunction& test_func, |
|
||||
ExecutionMode emode_children) |
|
||||
{ |
|
||||
pEpTestUnit ret(nullptr, &model, name, test_func, emode_children); |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
// ChildNode factory
|
|
||||
// static
|
|
||||
pEpTestUnit pEpTestUnit::createChildNode( |
|
||||
pEpTestUnit& parent, |
|
||||
const std::string& name, |
|
||||
const TestUnitFunction& test_func, |
|
||||
ExecutionMode emode_children) |
|
||||
{ |
|
||||
pEpTestUnit ret(&parent, nullptr, name, test_func, emode_children); |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
// private
|
|
||||
pEpTestUnit::pEpTestUnit( |
|
||||
pEpTestUnit* const parent, |
|
||||
pEpTestModel* model, |
|
||||
const std::string& name, |
|
||||
const TestUnitFunction& test_func, |
|
||||
ExecutionMode emode_children) : |
|
||||
parent(parent), |
|
||||
model(model), name(normalizeName(name)), test_func(test_func), emode_chld(emode_children) |
|
||||
{ |
|
||||
logger.set_instancename(getFQName()); |
|
||||
if (!isRootNode()) { |
|
||||
parent->addChildNode(*this); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// static
|
|
||||
void pEpTestUnit::setDefaultDataRoot(const std::string& dir) |
|
||||
{ |
|
||||
pEpTestUnit::data_root = dir; |
|
||||
} |
|
||||
|
|
||||
// static
|
|
||||
std::string pEpTestUnit::getDataRoot() |
|
||||
{ |
|
||||
return pEpTestUnit::data_root; |
|
||||
} |
|
||||
|
|
||||
void pEpTestUnit::init(const pEpTestUnit* caller) const |
|
||||
{ |
|
||||
//caller is never nullptr if called from another unit
|
|
||||
if (caller == nullptr) {} |
|
||||
|
|
||||
// for (const pair<string, pEpTestUnit&> elem : testnodes) {
|
|
||||
// string home_dir = evalHomeDir(elem.second);
|
|
||||
// pEpLogClass("creating home dir for '" + elem.second.getName() + "' - " + home_dir); mkdir(home_dir.c_str(), 0770);
|
|
||||
// }
|
|
||||
} |
|
||||
|
|
||||
void pEpTestUnit::addChildNode(pEpTestUnit& node) |
|
||||
{ |
|
||||
children.insert(pair<string, pEpTestUnit&>(node.getName(), node)); |
|
||||
} |
|
||||
|
|
||||
// name is alphanumeric only (everything else will be replaced by an underscore)
|
|
||||
// private
|
|
||||
string pEpTestUnit::normalizeName(string name) const |
|
||||
{ |
|
||||
replace_if( |
|
||||
name.begin(), |
|
||||
name.end(), |
|
||||
[](char c) -> bool { return !isalnum(c); }, |
|
||||
'_'); |
|
||||
|
|
||||
return name; |
|
||||
} |
|
||||
|
|
||||
string pEpTestUnit::getName() const |
|
||||
{ |
|
||||
return name; |
|
||||
} |
|
||||
|
|
||||
// RootNodes have their own data_dir
|
|
||||
// ProcessNodes have their own data_dir inside their RootNote dir (nameclash prossible)
|
|
||||
// All other nodes inherit data_dir from their Root/ProcessNode
|
|
||||
string pEpTestUnit::getDataDir() const |
|
||||
{ |
|
||||
string ret; |
|
||||
if (isRootNode()) { |
|
||||
ret = getDataRoot() + getName() + "/"; |
|
||||
} else { |
|
||||
if (parent->getEffectiveExecutionMode() == ExecutionMode::PROCESS_SERIAL || |
|
||||
parent->getEffectiveExecutionMode() == ExecutionMode::PROCESS_PARALLEL) { |
|
||||
|
|
||||
ret = parent->getDataDir() + getName(); |
|
||||
} else { |
|
||||
// inherit
|
|
||||
ret = parent->getDataDir(); |
|
||||
} |
|
||||
} |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
void pEpTestUnit::run(const pEpTestUnit* caller) |
|
||||
{ |
|
||||
pEpLogClass("called"); |
|
||||
// caller is never nullptr if called by another pEpTestUnit
|
|
||||
if (caller == nullptr) { |
|
||||
pEpLogClass("\n" + to_string()); |
|
||||
} |
|
||||
|
|
||||
if (test_func) { |
|
||||
test_func(*this); |
|
||||
} else { |
|
||||
pEpLogClass("No function to execute"); |
|
||||
} |
|
||||
executeChildren(); |
|
||||
} |
|
||||
|
|
||||
// private
|
|
||||
void pEpTestUnit::executeChildren() const |
|
||||
{ |
|
||||
if (!children.empty()) { |
|
||||
const ExecutionMode& emode = getEffectiveExecutionMode(); |
|
||||
for (const pair<string, pEpTestUnit&> elem : children) { |
|
||||
// Execute in fork and wait here until process ends
|
|
||||
if (emode == ExecutionMode::PROCESS_SERIAL) { // fork
|
|
||||
executeForked(elem.second); |
|
||||
waitChildProcesses(); |
|
||||
// Execute in fork and go on, wait for process execution in the end
|
|
||||
} else if (emode == ExecutionMode::PROCESS_PARALLEL) { |
|
||||
executeForked(elem.second); |
|
||||
// Execute as normal funciton
|
|
||||
} else if (emode == ExecutionMode::FUNCTION) { |
|
||||
elem.second.run(this); |
|
||||
} else if (emode == ExecutionMode::THREAD_PARALLEL) { |
|
||||
throw runtime_error(to_string(emode) + " - not implemented"); |
|
||||
} else if (emode == ExecutionMode::THREAD_SERIAL) { |
|
||||
throw runtime_error(to_string(emode) + " - not implemented"); |
|
||||
} |
|
||||
} |
|
||||
if (emode == ExecutionMode::PROCESS_PARALLEL) { |
|
||||
waitChildProcesses(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// private
|
|
||||
void pEpTestUnit::executeForked(pEpTestUnit& unit) const |
|
||||
{ |
|
||||
pid_t pid; |
|
||||
pid = fork(); |
|
||||
if (pid == pid_t(0)) { |
|
||||
// setenv("HOME", evalHomeDir().c_str(), 1);
|
|
||||
unit.run(this); |
|
||||
exit(0); |
|
||||
} else if (pid < pid_t(0)) { |
|
||||
pEpLogClass("Error forking"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// private
|
|
||||
void pEpTestUnit::waitChildProcesses() const |
|
||||
{ |
|
||||
int status; |
|
||||
pid_t pid; |
|
||||
while ((pid = wait(&status)) > 0) { |
|
||||
pEpLogClass( |
|
||||
"process[" + to_string((int)pid) + |
|
||||
"] terminated with status: " + to_string(status)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Inherited (if null see parent recursively)
|
|
||||
pEpTestModel& pEpTestUnit::getModel() const |
|
||||
{ |
|
||||
// pEpLogClass("called");
|
|
||||
pEpTestModel* ret = nullptr; |
|
||||
if (model == nullptr) { |
|
||||
ret = &parent->getModel(); |
|
||||
} else { |
|
||||
ret = model; |
|
||||
} |
|
||||
assert(ret != nullptr); |
|
||||
// cant be null because for createChildNode() you have to provide TestNode& instance,
|
|
||||
// and the only other way to get one is by createRootNode() which in turn requires a TestModel&
|
|
||||
return *ret; |
|
||||
} |
|
||||
|
|
||||
const pEpTestUnit& pEpTestUnit::getRoot() const |
|
||||
{ |
|
||||
// pEpLogClass("called");
|
|
||||
const pEpTestUnit* ret = nullptr; |
|
||||
if (!isRootNode()) { |
|
||||
ret = &parent->getRoot(); |
|
||||
} else { |
|
||||
ret = this; |
|
||||
} |
|
||||
assert(ret != nullptr); |
|
||||
// cant be null because for createChildNode() you need to provide a TestNode& and
|
|
||||
// the only other way is using createRootNode() which has parent == nullptr
|
|
||||
return *ret; |
|
||||
} |
|
||||
|
|
||||
string pEpTestUnit::getFQNameNormalized() const |
|
||||
{ |
|
||||
return normalizeName(getFQName()); |
|
||||
} |
|
||||
|
|
||||
string pEpTestUnit::getFQName() const |
|
||||
{ |
|
||||
// pEpLogClass("called");
|
|
||||
string ret; |
|
||||
|
|
||||
if (!isRootNode()) { |
|
||||
ret = parent->getFQName() + "/" + getName(); |
|
||||
} else { |
|
||||
ret = getName(); |
|
||||
} |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
// // A process node is a node that spawns its own process
|
|
||||
// bool pEpTestUnit::isProcessNode() const
|
|
||||
// {
|
|
||||
// bool ret = false;
|
|
||||
// if (isRootNode() ||
|
|
||||
// parent->getEffectiveExecutionMode() == ExecutionMode::PROCESS_PARALLEL ||
|
|
||||
// parent->getEffectiveExecutionMode() == ExecutionMode::PROCESS_SERIAL) {
|
|
||||
// ret = true;
|
|
||||
// }
|
|
||||
// return ret;
|
|
||||
// }
|
|
||||
|
|
||||
bool pEpTestUnit::isRootNode() const |
|
||||
{ |
|
||||
bool ret = false; |
|
||||
if (parent == nullptr) { |
|
||||
ret = true; |
|
||||
} |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
void pEpTestUnit::data_dir_delete() |
|
||||
{ |
|
||||
try { |
|
||||
Utils::path_delete_all(getDataRoot()); |
|
||||
} catch (const exception& e) { |
|
||||
pEpLogClass("DistTest: - could not delete data dir: " + getDataRoot()); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void pEpTestUnit::data_dir_create() |
|
||||
{ |
|
||||
// Utils::dir_create(getDataDir());
|
|
||||
pEpLogClass("creating dir:" + getDataRoot()); |
|
||||
} |
|
||||
|
|
||||
void pEpTestUnit::data_dir_recreate() |
|
||||
{ |
|
||||
data_dir_delete(); |
|
||||
data_dir_create(); |
|
||||
}; |
|
||||
|
|
||||
// static
|
|
||||
void pEpTestUnit::setDefaultExecutionMode(ExecutionMode emode) |
|
||||
{ |
|
||||
pEpTestUnit::emode_default = emode; |
|
||||
} |
|
||||
|
|
||||
pEpTestUnit::ExecutionMode pEpTestUnit::getExecutionMode() const |
|
||||
{ |
|
||||
return emode_chld; |
|
||||
} |
|
||||
|
|
||||
pEpTestUnit::ExecutionMode pEpTestUnit::getEffectiveExecutionMode() const |
|
||||
{ |
|
||||
ExecutionMode ret{ emode_default }; |
|
||||
if (getExecutionMode() == ExecutionMode::INHERIT) { |
|
||||
if (!isRootNode()) { |
|
||||
// get from parent until not inherit
|
|
||||
ret = parent->getEffectiveExecutionMode(); |
|
||||
} |
|
||||
} else { |
|
||||
ret = getExecutionMode(); |
|
||||
} |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
string pEpTestUnit::to_string(const ExecutionMode& emode) |
|
||||
{ |
|
||||
switch (emode) { |
|
||||
case ExecutionMode::FUNCTION: |
|
||||
return "FUNCTION"; |
|
||||
case ExecutionMode::PROCESS_SERIAL: |
|
||||
return "PROCESS_SERIAL"; |
|
||||
case ExecutionMode::PROCESS_PARALLEL: |
|
||||
return "PROCESS_PARALLEL"; |
|
||||
case ExecutionMode::THREAD_SERIAL: |
|
||||
return "THREAD_SERIAL"; |
|
||||
case ExecutionMode::THREAD_PARALLEL: |
|
||||
return "THREAD_PARALLEL"; |
|
||||
case ExecutionMode::INHERIT: |
|
||||
return "INHERIT"; |
|
||||
default: |
|
||||
return "UNDEFINED EXECUTION MODE"; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
string pEpTestUnit::to_string(bool recursive, int indent) const |
|
||||
{ |
|
||||
string ret; |
|
||||
stringstream builder; |
|
||||
builder << string(indent, '\t') << getName(); |
|
||||
builder << "[children=" << to_string(getEffectiveExecutionMode()) + "]"; |
|
||||
// builder << "/" << getModel().getName();
|
|
||||
builder << endl; |
|
||||
ret = builder.str(); |
|
||||
|
|
||||
if (recursive) { |
|
||||
indent++; |
|
||||
for (const pair<string, const pEpTestUnit&> elem : children) { |
|
||||
ret += elem.second.to_string(true, indent); |
|
||||
} |
|
||||
indent--; |
|
||||
} |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
const pEpTestUnit& pEpTestUnit::getParentProcessUnit() const |
|
||||
{ |
|
||||
if (!isRootNode()) { |
|
||||
if (parent->getEffectiveExecutionMode() == ExecutionMode::PROCESS_PARALLEL || |
|
||||
parent->getEffectiveExecutionMode() == ExecutionMode::PROCESS_SERIAL || |
|
||||
parent->isRootNode()) { |
|
||||
return *this; |
|
||||
} else { |
|
||||
return parent->getParentProcessUnit(); |
|
||||
} |
|
||||
} else { |
|
||||
return *this; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
//Well, ok, lets just add some little convenience logging service in here, too
|
|
||||
void pEpTestUnit::log(const string& msg) const |
|
||||
{ |
|
||||
stringstream builder; |
|
||||
builder << "[" << std::to_string(getpid()) << "/" << getParentProcessUnit().getFQName() |
|
||||
<< "] - "; |
|
||||
builder << msg << endl; |
|
||||
cout << builder.str(); |
|
||||
} |
|
||||
} // namespace Test
|
|
||||
} // namespace pEp
|
|
@ -1,106 +0,0 @@ |
|||||
// This file is under GNU General Public License 3.0
|
|
||||
// see LICENSE.txt
|
|
||||
|
|
||||
#ifndef LIBPEPADAPTER_PEPTESTUNIT_HH |
|
||||
#define LIBPEPADAPTER_PEPTESTUNIT_HH |
|
||||
|
|
||||
#include <string> |
|
||||
#include <functional> |
|
||||
#include <map> |
|
||||
#include "../../../src/pEpLog.hh" |
|
||||
#include "pEpTestModel.hh" |
|
||||
|
|
||||
namespace pEp { |
|
||||
namespace Test { |
|
||||
class pEpTestUnit { |
|
||||
public: |
|
||||
using TestUnitFunction = std::function<void(const pEpTestUnit&)>; |
|
||||
|
|
||||
enum class ExecutionMode |
|
||||
{ |
|
||||
FUNCTION, |
|
||||
PROCESS_SERIAL, |
|
||||
PROCESS_PARALLEL, |
|
||||
THREAD_SERIAL, // unimplemented
|
|
||||
THREAD_PARALLEL, // unimplemented
|
|
||||
INHERIT |
|
||||
}; |
|
||||
static std::string to_string(const ExecutionMode& emode); |
|
||||
|
|
||||
// Constructors
|
|
||||
pEpTestUnit() = delete; |
|
||||
|
|
||||
// Methods
|
|
||||
static pEpTestUnit createRootNode( |
|
||||
pEpTestModel& model, |
|
||||
const std::string& name, |
|
||||
const TestUnitFunction& test_func, |
|
||||
ExecutionMode emode_children = emode_default); |
|
||||
|
|
||||
static pEpTestUnit createChildNode( |
|
||||
pEpTestUnit& parent, |
|
||||
const std::string& name, |
|
||||
const TestUnitFunction& test_func, |
|
||||
ExecutionMode emode_children = emode_default); |
|
||||
|
|
||||
void init(const pEpTestUnit* caller = nullptr) const; |
|
||||
static void setDefaultDataRoot(const std::string& dir); |
|
||||
static std::string getDataRoot(); |
|
||||
void addChildNode(pEpTestUnit& node); |
|
||||
void run(const pEpTestUnit* caller = nullptr); |
|
||||
std::string getName() const; |
|
||||
std::string getFQName() const; |
|
||||
std::string getFQNameNormalized() const; |
|
||||
pEpTestModel& getModel() const; |
|
||||
std::string getDataDir() const; |
|
||||
const pEpTestUnit& getParentProcessUnit() const; |
|
||||
std::string to_string(bool recursive = true, int indent = 0) const; |
|
||||
static void setDefaultExecutionMode(ExecutionMode emode); |
|
||||
|
|
||||
//logging service
|
|
||||
void log(const std::string& msg) const; |
|
||||
|
|
||||
// internal logging
|
|
||||
static bool log_enabled; |
|
||||
Adapter::pEpLog::pEpLogger logger{ "pEpTestUnit", log_enabled }; |
|
||||
|
|
||||
private: |
|
||||
// Constructors
|
|
||||
explicit pEpTestUnit( |
|
||||
pEpTestUnit* parent, |
|
||||
pEpTestModel* model, |
|
||||
const std::string& name, |
|
||||
const TestUnitFunction& test_func, |
|
||||
ExecutionMode emode_children); |
|
||||
|
|
||||
// Methods
|
|
||||
const pEpTestUnit& getRoot() const; |
|
||||
void executeChildren() const; |
|
||||
void executeForked(pEpTestUnit& unit) const; |
|
||||
void waitChildProcesses() const; |
|
||||
ExecutionMode getExecutionMode() const; |
|
||||
ExecutionMode getEffectiveExecutionMode() const; |
|
||||
void data_dir_delete(); |
|
||||
void data_dir_create(); |
|
||||
void data_dir_recreate(); |
|
||||
bool isRootNode() const; |
|
||||
// bool isProcessNode() const;
|
|
||||
std::string normalizeName(std::string name) const; |
|
||||
|
|
||||
// Fields
|
|
||||
const pEpTestUnit* parent; //nullptr if rootnode
|
|
||||
const std::string name; |
|
||||
pEpTestModel* model; //nullptr if inherited
|
|
||||
const TestUnitFunction& test_func; |
|
||||
const ExecutionMode emode_chld; |
|
||||
static ExecutionMode emode_default; |
|
||||
std::map<const std::string, pEpTestUnit&> children; |
|
||||
static std::string data_root; |
|
||||
|
|
||||
// logger
|
|
||||
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; |
|
||||
}; |
|
||||
}; // namespace Test
|
|
||||
}; // namespace pEp
|
|
||||
|
|
||||
#endif // LIBPEPADAPTER_PEPTESTUNIT_HH
|
|
Loading…
Reference in new issue