Browse Source

Test: PityTest - Abstract base for PityUnit

This enables PityUnits of different context types in a single tree.
LIB-11
heck 11 months ago
parent
commit
d1de14487c
5 changed files with 642 additions and 608 deletions
  1. +468
    -0
      test/pitytest11/src/AbstractPityUnit.cc
  2. +156
    -0
      test/pitytest11/src/AbstractPityUnit.hh
  3. +3
    -3
      test/pitytest11/src/PityTransport.cc
  4. +4
    -118
      test/pitytest11/src/PityUnit.hh
  5. +11
    -487
      test/pitytest11/src/PityUnit.hxx

+ 468
- 0
test/pitytest11/src/AbstractPityUnit.cc View File

@ -0,0 +1,468 @@
// This file is under GNU General Public License 3.0
// see LICENSE.txt
#include "AbstractPityUnit.hh"
#include "../../../src/std_utils.hh"
#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <sys/stat.h>
#include <functional>
#include <algorithm>
#include <sstream>
#include <exception>
#include <memory>
#include <unordered_map>
#include <sys/wait.h>
namespace pEp {
namespace PityTest11 {
// static
std::string AbstractPityUnit::_global_root_dir = "./pitytest_data/";
// static
bool AbstractPityUnit::debug_log_enabled = false;
// static
int AbstractPityUnit::procUnitsCount = 0;
AbstractPityUnit::AbstractPityUnit(
AbstractPityUnit *const parent,
const std::string &name,
ExecutionMode exec_mode) :
_parent{ parent },
_name{ _normalizeName(name) }, _exec_mode{ exec_mode }
{
logger_debug.set_instancename(getPath());
if (!_isRootUnit()) {
parent->_addChildUnit(*this);
// Inherit
procUnitNr = _parent->procUnitNr;
//Or update if procUnit
if (_isProcessUnit()) {
_createTransport();
procUnitsCount++;
procUnitNr = procUnitsCount;
}
} else {
procUnitNr = procUnitsCount;
}
}
std::string AbstractPityUnit::getName() const
{
return _name;
}
// name is alphanumeric only (everything else will be replaced by an underscore)
// static
std::string AbstractPityUnit::_normalizeName(std::string name)
{
replace_if(
name.begin(),
name.end(),
[](char c) -> bool { return !isalnum(c); },
'_');
return name;
}
std::string AbstractPityUnit::getPath() const
{
std::string ret;
if (!_isRootUnit()) {
ret = _parent->getPath() + "/" + getName();
} else {
ret = getName();
}
return ret;
}
// For:
// RootUnit - "<name>"
// ProcessUnit - ".../<proc>"
// When Process as dir. parent - ".../<proc>/name"
// When no process as dir. parent - ".../<proc>/.../name"
std::string AbstractPityUnit::getPathShort() const
{
std::string ret;
if (_isRootUnit()) {
ret = getName();
} else {
if (_isProcessUnit()) {
ret += ".../" + getName();
} else {
if (&(parentingProcessUnit()) == (_parent)) {
ret = parentingProcessUnit().getPathShort() + "/" + getName();
} else {
ret = parentingProcessUnit().getPathShort() + "/.../" + getName();
}
}
}
return ret;
}
AbstractPityUnit *AbstractPityUnit::getParent() const
{
return _parent;
}
// Every process has its own dir inside its rootUnitDir
// All other units inherit processDir from their Root/ProcessUnit
std::string AbstractPityUnit::processDir()
{
if (_isRootUnit()) {
return _rootUnitDir();
} else {
if (_isProcessUnit()) {
return _rootUnitDir() + getName() + "/";
} else {
return _parent->processDir();
}
}
}
// Every RootUnit has its own dir
std::string AbstractPityUnit::_rootUnitDir()
{
return getGlobalRootDir() + rootUnit()->getName() + "/";
}
// Every process has its own dir inside its rootUnitDir
// All other units inherit transportDir from their Root/ProcessUnit
std::string AbstractPityUnit::transportDir()
{
if (_isRootUnit()) {
throw std::runtime_error("No transport dir");
} else {
if (_isProcessUnit()) {
return processDir() + "inbox/";
} else {
return _parent->transportDir();
}
}
}
// static
void AbstractPityUnit::setGlobalRootDir(const std::string &dir)
{
AbstractPityUnit::_global_root_dir = dir;
}
// static
std::string AbstractPityUnit::getGlobalRootDir()
{
return AbstractPityUnit::_global_root_dir;
}
void AbstractPityUnit::run()
{
pEpLogClass("called");
_log_mutex = std::make_shared<fs_mutex>("log.mutex");
_log_mutex->release();
setenv("HOME", processDir().c_str(), true);
if (_isRootUnit()) {
_init();
}
// Execute in fork and wait here until process ends
if (_exec_mode == ExecutionMode::PROCESS_SEQUENTIAL) { // fork
_executeInFork(std::bind(&AbstractPityUnit::_run, this), true);
// Execute in fork and go on, wait for process execution in the end
} else if (_exec_mode == ExecutionMode::PROCESS_PARALLEL) {
_executeInFork(std::bind(&AbstractPityUnit::_run, this), false);
// Execute as normal function
} else if (_exec_mode == ExecutionMode::FUNCTION) {
_run();
} else if (_exec_mode == ExecutionMode::THREAD_PARALLEL) {
throw std::invalid_argument(to_string(_exec_mode) + " - not implemented");
} else if (_exec_mode == ExecutionMode::THREAD_SEQUENTIAL) {
throw std::invalid_argument(to_string(_exec_mode) + " - not implemented");
}
if (_isRootUnit()) {
_waitChildProcesses();
}
}
std::string AbstractPityUnit::to_string(bool recursive, int indent)
{
std::string ret;
std::stringstream builder;
builder << std::string(indent * 4, ' ');
builder << getName();
builder << " [ ";
builder << to_string(_exec_mode) << " - ";
builder << "\"" << processDir() << "\"";
builder << " ]";
builder << std::endl;
ret = builder.str();
if (recursive) {
if (!_children.empty()) {
indent++;
for (const std::pair<std::string, AbstractPityUnit &> child : _children) {
ret += child.second.to_string(true, indent);
}
indent--;
}
}
return ret;
}
std::string AbstractPityUnit::to_string(const ExecutionMode &emode)
{
switch (emode) {
case ExecutionMode::FUNCTION:
return "FUNCTION";
case ExecutionMode::PROCESS_SEQUENTIAL:
return "PROC_SEQ";
case ExecutionMode::PROCESS_PARALLEL:
return "PROC_PAR";
case ExecutionMode::THREAD_SEQUENTIAL:
return "THREAD_S";
case ExecutionMode::THREAD_PARALLEL:
return "THREAD_P";
case ExecutionMode::INHERIT:
return "INHERIT";
default:
return "UNDEFINED EXECUTION MODE";
}
}
void AbstractPityUnit::recreateDirsRecursively()
{
Utils::dir_recreate(processDir());
if (!_children.empty()) {
for (const std::pair<std::string, AbstractPityUnit &> child : _children) {
child.second.recreateDirsRecursively();
}
}
}
void AbstractPityUnit::registerAsTransportEndpoint()
{
transportEndpoints().insert({ getName(), transportDir() });
}
Endpoints &AbstractPityUnit::transportEndpoints()
{
if (_isRootUnit()) {
return _transport_endpoints;
} else {
return rootUnit()->transportEndpoints();
}
}
void AbstractPityUnit::log(const std::string &msg) const
{
std::stringstream builder;
builder << "[ ";
builder << std::to_string(getpid());
builder << " - ";
builder << getPathShort();
builder << " ] - ";
builder << msg;
_logRaw(builder.str());
}
void AbstractPityUnit::logH1(const std::string &msg) const
{
Adapter::pEpLog::logH1(msg, _termColor());
}
void AbstractPityUnit::logH2(const std::string &msg) const
{
Adapter::pEpLog::logH2(msg, _termColor());
}
void AbstractPityUnit::logH3(const std::string &msg) const
{
Adapter::pEpLog::logH3(msg, _termColor());
}
// PRIVATE ---------------------------------------------------------------------------------
void AbstractPityUnit::_init()
{
logH1("PityTest Starting...");
_logRaw("RootUnit: " + getPathShort());
_logRaw("GlobalRootDir: " + getGlobalRootDir());
_logRaw("\nTestTree");
_logRaw("--------");
_logRaw(to_string());
logH3("INIT");
Utils::dir_ensure(getGlobalRootDir());
recreateDirsRecursively();
logH3("INIT DONE");
}
void AbstractPityUnit::_run()
{
logH2(_status_string("STARTING"));
_runSelf();
_runChildren();
}
void AbstractPityUnit::_runChildren() const
{
if (!_children.empty()) {
for (const std::pair<std::string, AbstractPityUnit &> child : _children) {
child.second.run();
}
}
}
void AbstractPityUnit::_executeInFork(std::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 std::runtime_error("Error forking");
}
if (wait_child) {
_waitChildProcesses();
}
}
void AbstractPityUnit::_waitChildProcesses() const
{
int status;
pid_t pid;
while ((pid = wait(&status)) > 0) {
std::string color;
if (status == 0) {
color = "\033[1m\033[32m"; // Green
} else {
color = "\033[1m\033[31m"; // Red
}
logH3(
color + "PROCESS [ " + std::to_string((int)pid) +
" ] EXITED with status code: " + std::to_string(status) +
Utils::to_termcol(_termColor()));
}
}
void AbstractPityUnit::_addChildUnit(AbstractPityUnit &unit)
{
_children.insert(std::pair<std::string, AbstractPityUnit &>(unit.getName(), unit));
}
bool AbstractPityUnit::_isProcessUnit() const
{
bool ret = false;
if (_exec_mode == ExecutionMode::PROCESS_SEQUENTIAL ||
_exec_mode == ExecutionMode::PROCESS_PARALLEL) {
ret = true;
}
return ret;
}
bool AbstractPityUnit::_isRootUnit() const
{
if (_parent == nullptr) {
return true;
} else {
return false;
}
}
AbstractPityUnit *AbstractPityUnit::rootUnit()
{
if (!_isRootUnit()) {
return _parent->rootUnit();
} else {
return this;
}
}
const AbstractPityUnit &AbstractPityUnit::parentingProcessUnit() const
{
if (_isRootUnit() || _isProcessUnit()) {
return *this;
} else {
return _parent->parentingProcessUnit();
}
}
// Inherited (if null see parent recursively)
void AbstractPityUnit::_createTransport()
{
registerAsTransportEndpoint();
_transport = std::make_shared<PityTransport>(transportDir(), transportEndpoints());
}
// Inherited (if null see parent recursively)
PityTransport *AbstractPityUnit::transport() const
{
// pEpLogClass("called");
PityTransport *ret = nullptr;
if (_transport != nullptr) {
ret = _transport.get();
} else {
if (!_isRootUnit()) {
ret = _parent->transport();
}
}
return ret;
}
std::string AbstractPityUnit::_status_string(const std::string &msg) const
{
std::string ret;
ret = "[ " + to_string(_exec_mode) + ":" + std::to_string(getpid()) + " ] [ " +
getPathShort() + " ] [ " + msg + " ]";
return ret;
}
//static
Utils::Color AbstractPityUnit::_colForProcUnitNr(int procUnitNr)
{
int nrColors = 7;
switch (procUnitNr % nrColors) {
case 0:
return Utils::Color::WHITE;
case 1:
return Utils::Color::GREEN;
case 2:
return Utils::Color::YELLOW;
case 3:
return Utils::Color::CYAN;
case 4:
return Utils::Color::BLUE;
case 5:
return Utils::Color::MAGENTA;
case 6:
return Utils::Color::RED;
default:
return Utils::Color::WHITE;
}
}
Utils::Color AbstractPityUnit::_termColor() const
{
return _colForProcUnitNr(procUnitNr);
}
void AbstractPityUnit::_logRaw(const std::string &msg) const
{
// fs-mutex to sync across processes
_log_mutex->aquire();
Adapter::pEpLog::log(msg, _termColor());
_log_mutex->release();
}
} // namespace PityTest11
} // namespace pEp

+ 156
- 0
test/pitytest11/src/AbstractPityUnit.hh View File

@ -0,0 +1,156 @@
// This file is under GNU General Public License 3.0
// see LICENSE.txt
#ifndef PITYTEST_ABSTRACTPITYUNIT_HH
#define PITYTEST_ABSTRACTPITYUNIT_HH
#include "../../../src/pEpLog.hh"
#include "../../../src/std_utils.hh"
#include "fs_mutex.hh"
#include "PityTransport.hh"
#include <string>
#include <map>
#include <memory>
#include <unordered_map>
#include <functional>
// Yes, the mem mgmt is purely static on purpose (so far)
namespace pEp {
namespace PityTest11 {
class AbstractPityUnit {
public:
enum class ExecutionMode
{
FUNCTION,
PROCESS_SEQUENTIAL,
PROCESS_PARALLEL,
THREAD_SEQUENTIAL, // unimplemented
THREAD_PARALLEL, // unimplemented
INHERIT
};
AbstractPityUnit(
AbstractPityUnit* const parent,
const std::string& name,
ExecutionMode exec_mode = ExecutionMode::FUNCTION);
// Read-Write
static void setGlobalRootDir(const std::string& dir);
static std::string getGlobalRootDir();
// Read-Only
std::string getName() const;
std::string getPath() const;
std::string getPathShort() const;
std::string processDir(); // own process dir
std::string transportDir();
AbstractPityUnit* getParent() const;
bool _isRootUnit() const; // true if has no parent
// Main funcs
void run();
std::string to_string(bool recursive = true, int indent = 0);
static std::string to_string(const ExecutionMode& emode);
// logging service
void log(const std::string& msg) const;
void logH1(const std::string& msg) const;
void logH2(const std::string& msg) const;
void logH3(const std::string& msg) const;
// Util
static std::string _normalizeName(
std::string name); //TODO HACK in PityTransport this should be private
void recreateDirsRecursively();
//Transport
PityTransport* transport() const;
void registerAsTransportEndpoint();
Endpoints& transportEndpoints();
// internal logging
static bool debug_log_enabled;
Adapter::pEpLog::pEpLogger logger_debug{ "PityUnit", debug_log_enabled };
protected:
std::string _status_string(const std::string& msg) const;
static Utils::Color _colForProcUnitNr(int procUnitNr);
Utils::Color _termColor() const;
void _logRaw(const std::string& msg) const;
// internal logging
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger_debug;
private:
// METHODS
// Execution
void _init();
void _run();
virtual void _runSelf() = 0;
void _runChildren() const;
void _executeInFork(std::function<void(void)> func, bool wait_child) const;
void _waitChildProcesses() const;
// Modify
void _addChildUnit(AbstractPityUnit& unit);
AbstractPityUnit* rootUnit();
const AbstractPityUnit& parentingProcessUnit() const;
// Query
bool _isProcessUnit() const; // true if it forks
std::string _rootUnitDir();
// Transport
void _createTransport();
// Fields
// ------
AbstractPityUnit* _parent; //nullptr if RootUnit
std::map<const std::string, AbstractPityUnit&> _children; // map to guarantee uniqueness of sibling-names
static std::string _global_root_dir;
const std::string _name;
int procUnitNr;
ExecutionMode _exec_mode;
static int procUnitsCount; // will be increased in every constructor
// transport
std::shared_ptr<PityTransport> _transport; //only ever read via transport()
Endpoints _transport_endpoints; // only ever access via transportEndpoints()
// fs-mutex to sync across processes
std::shared_ptr<fs_mutex> _log_mutex = nullptr;
};
class PityAssertException : public std::runtime_error {
public:
PityAssertException(const std::string& string) : runtime_error(string) {}
};
#ifndef PTASSERT
#define PTASSERT(condition, msg) \
do { \
if (!(condition)) { \
throw PityAssertException(msg); \
} \
} while (0)
#endif
#ifndef PTASSERT_EXCEPT
#define PTASSERT_EXCEPT(func) \
do { \
try { \
(func); \
PTASSERT(false); \
} catch (const exception& e) { \
} \
} while (0)
#endif
}; // namespace PityTest11
}; // namespace pEp
#endif

+ 3
- 3
test/pitytest11/src/PityTransport.cc View File

@ -22,14 +22,14 @@ namespace pEp {
pEpLogClass("Address: " + nodename + " msg: " + msg);
// HACK TODO
std::string nodename_normalize = PityUnit<>::_normalizeName(nodename);
std::string nodename_normalized = AbstractPityUnit::_normalizeName(nodename);
bool found = false;
std::string dir;
try {
dir = _endpoints.at(nodename_normalize);
dir = _endpoints.at(nodename_normalized);
} catch (std::out_of_range&) {
throw std::runtime_error("no such nodename: " + nodename_normalize);
throw std::runtime_error("no such nodename: " + nodename_normalized);
}
Utils::dir_ensure(dir);
std::stringstream filename;


+ 4
- 118
test/pitytest11/src/PityUnit.hh View File

@ -6,6 +6,7 @@
#include "../../../src/pEpLog.hh"
#include "../../../src/std_utils.hh"
#include "AbstractPityUnit.hh"
#include "fs_mutex.hh"
#include "PityTransport.hh"
#include <string>
@ -14,150 +15,35 @@
#include <unordered_map>
#include <functional>
// Yes, the mem mgmt is purely static on purpose (so far)
namespace pEp {
namespace PityTest11 {
template<class TestContext = void>
class PityUnit {
class PityUnit : public AbstractPityUnit {
public:
// Test success if TestFunction:
// * does not throw
// * returns 0
using TestFunction = const std::function<int(PityUnit<TestContext>&, TestContext*)>;
enum class ExecutionMode
{
FUNCTION,
PROCESS_SEQUENTIAL,
PROCESS_PARALLEL,
THREAD_SEQUENTIAL, // unimplemented
THREAD_PARALLEL, // unimplemented
INHERIT
};
// Constructors are private
PityUnit() = delete;
explicit PityUnit<TestContext>(
PityUnit<TestContext>* parent,
AbstractPityUnit* const parent,
const std::string& name,
TestFunction test_func = nullptr,
TestContext* perspective = nullptr,
ExecutionMode exec_mode = ExecutionMode::FUNCTION);
// Read-Only
std::string getName() const;
std::string getPath() const;
std::string getPathShort() const;
std::string processDir(); // own process dir
std::string transportDir();
PityUnit<TestContext>* rootUnit();
TestContext* getPerspective() const;
const PityUnit<TestContext>& parentingProcessUnit() const;
// Read-Write
static void setGlobalRootDir(const std::string& dir);
static std::string getGlobalRootDir();
// Main funcs
void run();
std::string to_string(bool recursive = true, int indent = 0);
static std::string to_string(const ExecutionMode& emode);
// Util
void recreateDirsRecursively();
static std::string _normalizeName(
std::string name); //TODO HACK in PityTransport this should be private
//Transport
PityTransport* transport() const;
void registerAsTransportEndpoint();
Endpoints& transportEndpoints();
// logging service
void log(const std::string& msg) const;
void logH1(const std::string& msg) const;
void logH2(const std::string& msg) const;
void logH3(const std::string& msg) const;
// internal logging
static bool debug_log_enabled;
Adapter::pEpLog::pEpLogger logger_debug{ "PityUnit", debug_log_enabled };
private:
// METHODS
// Execution
void _init();
void _run();
void _runSelf();
void _runChildren() const;
void _executeInFork(std::function<void(void)> func, bool wait_child) const;
void _waitChildProcesses() const;
// Modify
void _addChildUnit(PityUnit<TestContext>& unit);
// Transport
void _createTransport();
// Query
bool _isProcessUnit() const; // true if it forks
bool _isRootUnit() const; // true if has no parent
std::string _rootUnitDir();
// Util
std::string _status_string(const std::string& msg) const;
Utils::Color _colForProcUnitNr(int procUnitNr) const;
Utils::Color _termColor() const;
void _logRaw(const std::string& msg) const;
// Fields
const std::string _name;
PityUnit<TestContext>* _parent; //nullptr if RootUnit
TestContext* _perspective; //nullptr if inherited
TestContext* _perspective; //nullptr if inherited
TestFunction _test_func;
ExecutionMode _exec_mode;
static std::string _global_root_dir;
std::map<const std::string, PityUnit<TestContext>&> _children; // map to guarantee uniqueness of sibling-names
int procUnitNr;
static int procUnitsCount; // will be increased in every constructor
std::shared_ptr<PityTransport> _transport; //only ever read via transport()
Endpoints _transport_endpoints; // only ever access via transportEndpoints()
// fs-mutex to sync across processes
std::shared_ptr<fs_mutex> _log_mutex = nullptr;
// internal logging
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger_debug;
};
class PityAssertException : public std::runtime_error {
public:
PityAssertException(const std::string& string) : runtime_error(string) {}
};
#ifndef PTASSERT
#define PTASSERT(condition, msg) \
do { \
if (!(condition)) { \
throw PityAssertException(msg); \
} \
} while (0)
#endif
#ifndef PTASSERT_EXCEPT
#define PTASSERT_EXCEPT(func) \
do { \
try { \
(func); \
PTASSERT(false); \
} catch (const exception& e) { \
} \
} while (0)
#endif
}; // namespace PityTest11
}; // namespace pEp


+ 11
- 487
test/pitytest11/src/PityUnit.hxx View File

@ -20,308 +20,17 @@
namespace pEp {
namespace PityTest11 {
// static
template<class TestContext>
std::string PityUnit<TestContext>::_global_root_dir = "./pitytest_data/";
// static
template<class TestContext>
bool PityUnit<TestContext>::debug_log_enabled = false;
// static
template<class TestContext>
int PityUnit<TestContext>::procUnitsCount = 0;
// CONSTRUCTOR
template<class TestContext>
PityUnit<TestContext>::PityUnit(
PityUnit<TestContext> *const parent,
const std::string &name,
TestFunction test_func,
TestContext *perspective,
ExecutionMode exec_mode) :
_parent{ parent },
_perspective{ perspective }, _name{ _normalizeName(name) }, _test_func{ test_func },
_exec_mode{ exec_mode }
{
logger_debug.set_instancename(getPath());
if (!_isRootUnit()) {
parent->_addChildUnit(*this);
// Inherit
procUnitNr = _parent->procUnitNr;
//Or update if procUnit
if (_isProcessUnit()) {
_createTransport();
procUnitsCount++;
procUnitNr = procUnitsCount;
}
} else {
procUnitNr = procUnitsCount;
}
}
template<class TestContext>
std::string PityUnit<TestContext>::getName() const
{
return _name;
}
template<class TestContext>
std::string PityUnit<TestContext>::getPath() const
{
std::string ret;
if (!_isRootUnit()) {
ret = _parent->getPath() + "/" + getName();
} else {
ret = getName();
}
return ret;
}
// For:
// RootUnit - "<name>"
// ProcessUnit - ".../<proc>"
// When Process as dir. parent - ".../<proc>/name"
// When no process as dir. parent - ".../<proc>/.../name"
template<class TestContext>
std::string PityUnit<TestContext>::getPathShort() const
{
std::string ret;
if (_isRootUnit()) {
ret = getName();
} else {
if (_isProcessUnit()) {
ret += ".../" + getName();
} else {
if (&(parentingProcessUnit()) == (_parent)) {
ret = parentingProcessUnit().getPathShort() + "/" + getName();
} else {
ret = parentingProcessUnit().getPathShort() + "/.../" + getName();
}
}
}
return ret;
}
// Every RootUnit has its own dir
template<class TestContext>
std::string PityUnit<TestContext>::_rootUnitDir()
{
return getGlobalRootDir() + rootUnit()->getName() + "/";
}
// Every process has its own dir inside its rootUnitDir
// All other units inherit processDir from their Root/ProcessUnit
template<class TestContext>
std::string PityUnit<TestContext>::processDir()
// CONSTRUCTOR
template<class TestContext>
PityUnit<TestContext>::PityUnit(
AbstractPityUnit *const parent,
const std::string &name,
TestFunction test_func,
TestContext *perspective,
ExecutionMode exec_mode) :
AbstractPityUnit(parent, name, exec_mode),
_perspective{ perspective }, _test_func{ test_func }
{
if (_isRootUnit()) {
return _rootUnitDir();
} else {
if (_isProcessUnit()) {
return _rootUnitDir() + getName() + "/";
} else {
return _parent->processDir();
}
}
}
// Every process has its own dir inside its rootUnitDir
// All other units inherit transportDir from their Root/ProcessUnit
template<class TestContext>
std::string PityUnit<TestContext>::transportDir()
{
if (_isRootUnit()) {
throw std::runtime_error("No transport dir");
} else {
if (_isProcessUnit()) {
return processDir() + "inbox/";
} else {
return _parent->transportDir();
}
}
}
// static
template<class TestContext>
void PityUnit<TestContext>::setGlobalRootDir(const std::string &dir)
{
PityUnit<TestContext>::_global_root_dir = dir;
}
// static
template<class TestContext>
std::string PityUnit<TestContext>::getGlobalRootDir()
{
return PityUnit::_global_root_dir;
}
template<class TestContext>
void PityUnit<TestContext>::run()
{
pEpLogClass("called");
_log_mutex = std::make_shared<fs_mutex>("log.mutex");
_log_mutex->release();
setenv("HOME", processDir().c_str(), true);
if (_isRootUnit()) {
_init();
}
// Execute in fork and wait here until process ends
if (_exec_mode == ExecutionMode::PROCESS_SEQUENTIAL) { // fork
_executeInFork(std::bind(&PityUnit<TestContext>::_run, this), true);
// Execute in fork and go on, wait for process execution in the end
} else if (_exec_mode == ExecutionMode::PROCESS_PARALLEL) {
_executeInFork(std::bind(&PityUnit<TestContext>::_run, this), false);
// Execute as normal funciton
} else if (_exec_mode == ExecutionMode::FUNCTION) {
_run();
} else if (_exec_mode == ExecutionMode::THREAD_PARALLEL) {
throw std::invalid_argument(to_string(_exec_mode) + " - not implemented");
} else if (_exec_mode == ExecutionMode::THREAD_SEQUENTIAL) {
throw std::invalid_argument(to_string(_exec_mode) + " - not implemented");
}
if (_isRootUnit()) {
_waitChildProcesses();
}
}
template<class TestContext>
std::string PityUnit<TestContext>::to_string(bool recursive, int indent)
{
std::string ret;
std::stringstream builder;
builder << std::string(indent * 4, ' ');
builder << getName();
builder << " [ ";
builder << to_string(_exec_mode) << " - ";
builder << "\"" << processDir() << "\"";
builder << " ]";
builder << std::endl;
ret = builder.str();
if (recursive) {
if (!_children.empty()) {
indent++;
for (const std::pair<std::string, PityUnit<TestContext> &> child : _children) {
ret += child.second.to_string(true, indent);
}
indent--;
}
}
return ret;
}
template<class TestContext>
std::string PityUnit<TestContext>::to_string(const ExecutionMode &emode)
{
switch (emode) {
case ExecutionMode::FUNCTION:
return "FUNCTION";
case ExecutionMode::PROCESS_SEQUENTIAL:
return "PROC_SEQ";
case ExecutionMode::PROCESS_PARALLEL:
return "PROC_PAR";
case ExecutionMode::THREAD_SEQUENTIAL:
return "THREAD_S";
case ExecutionMode::THREAD_PARALLEL:
return "THREAD_P";
case ExecutionMode::INHERIT:
return "INHERIT";
default:
return "UNDEFINED EXECUTION MODE";
}
}
template<class TestContext>
void PityUnit<TestContext>::recreateDirsRecursively()
{
Utils::dir_recreate(processDir());
if (!_children.empty()) {
for (const std::pair<std::string, PityUnit<TestContext> &> child : _children) {
child.second.recreateDirsRecursively();
}
}
}
template<class TestContext>
void PityUnit<TestContext>::registerAsTransportEndpoint()
{
transportEndpoints().insert({ getName(), transportDir() });
}
template<class TestContext>
Endpoints &PityUnit<TestContext>::transportEndpoints()
{
if (_isRootUnit()) {
return _transport_endpoints;
} else {
return rootUnit()->transportEndpoints();
}
}
template<class TestContext>
void PityUnit<TestContext>::log(const std::string &msg) const
{
std::stringstream builder;
builder << "[ ";
builder << std::to_string(getpid());
builder << " - ";
builder << getPathShort();
builder << " ] - ";
builder << msg;
_logRaw(builder.str());
}
template<class TestContext>
void PityUnit<TestContext>::logH1(const std::string &msg) const
{
Adapter::pEpLog::logH1(msg, _termColor());
}
template<class TestContext>
void PityUnit<TestContext>::logH2(const std::string &msg) const
{
Adapter::pEpLog::logH2(msg, _termColor());
}
template<class TestContext>
void PityUnit<TestContext>::logH3(const std::string &msg) const
{
Adapter::pEpLog::logH3(msg, _termColor());
}
// PRIVATE ---------------------------------------------------------------------------------
template<class TestContext>
void PityUnit<TestContext>::_init()
{
logH1("PityTest Starting...");
_logRaw("RootUnit: " + getPathShort());
_logRaw("GlobalRootDir: " + getGlobalRootDir());
_logRaw("\nTestTree");
_logRaw("--------");
_logRaw(to_string());
logH3("INIT");
Utils::dir_ensure(getGlobalRootDir());
recreateDirsRecursively();
logH3("INIT DONE");
}
template<class TestContext>
void PityUnit<TestContext>::_run()
{
logH2(_status_string("STARTING"));
_runSelf();
_runChildren();
}
template<class TestContext>
@ -340,103 +49,6 @@ namespace pEp {
}
}
template<class TestContext>
void PityUnit<TestContext>::_runChildren() const
{
if (!_children.empty()) {
for (const std::pair<std::string, PityUnit<TestContext> &> child : _children) {
child.second.run();
}
}
}
template<class TestContext>
void PityUnit<TestContext>::_executeInFork(std::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 std::runtime_error("Error forking");
}
if (wait_child) {
_waitChildProcesses();
}
}
template<class TestContext>
void PityUnit<TestContext>::_waitChildProcesses() const
{
int status;
pid_t pid;
while ((pid = wait(&status)) > 0) {
std::string color;
if(status == 0) {
color ="\033[1m\033[32m"; // Green
} else {
color ="\033[1m\033[31m"; // Red
}
logH3(
color + "PROCESS [ " + std::to_string((int)pid) +
" ] EXITED with status code: " + std::to_string(status) +
Utils::to_termcol(_termColor()));
}
}
template<class TestContext>
void PityUnit<TestContext>::_addChildUnit(PityUnit<TestContext> &unit)
{
_children.insert(std::pair<std::string, PityUnit<TestContext> &>(unit.getName(), unit));
}
template<class TestContext>
bool PityUnit<TestContext>::_isProcessUnit() const
{
bool ret = false;
if (_exec_mode == ExecutionMode::PROCESS_SEQUENTIAL ||
_exec_mode == ExecutionMode::PROCESS_PARALLEL) {
ret = true;
}
return ret;
}
template<class TestContext>
bool PityUnit<TestContext>::_isRootUnit() const
{
if (_parent == nullptr) {
return true;
} else {
return false;
}
}
template<class TestContext>
PityUnit<TestContext> *PityUnit<TestContext>::rootUnit()
{
// const PityUnit<TestContext>* ret = nullptr;
if (!_isRootUnit()) {
return _parent->rootUnit();
} else {
return this;
}
// assert(ret != nullptr);
// cant be null because for createChildUnit() you need to provide a TestUnit& and
// the only other way is using createRootUnit() which has parent == nullptr
// return *ret;
}
template<class TestContext>
const PityUnit<TestContext> &PityUnit<TestContext>::parentingProcessUnit() const
{
if (_isRootUnit() || _isProcessUnit()) {
return *this;
} else {
return _parent->parentingProcessUnit();
}
}
// Inherited (if null see parent recursively)
template<class TestContext>
TestContext *PityUnit<TestContext>::getPerspective() const
@ -448,99 +60,11 @@ namespace pEp {
ret = _perspective;
} else {
if (!_isRootUnit()) {
ret = _parent->getPerspective();
ret = (dynamic_cast<PityUnit<TestContext> *>(getParent()))->getPerspective();
}
}
return ret;
}
// Inherited (if null see parent recursively)
template<class TestContext>
void PityUnit<TestContext>::_createTransport()
{
registerAsTransportEndpoint();
_transport = std::make_shared<PityTransport>(transportDir(), transportEndpoints());
}
// Inherited (if null see parent recursively)
template<class TestContext>
PityTransport *PityUnit<TestContext>::transport() const
{
pEpLogClass("called");
PityTransport *ret = nullptr;
if (_transport.get() != nullptr) {
ret = _transport.get();
} else {
if (!_isRootUnit()) {
ret = _parent->transport();
}
}
return ret;
}
// name is alphanumeric only (everything else will be replaced by an underscore)
// static
template<class TestContext>
std::string PityUnit<TestContext>::_normalizeName(std::string name)
{
replace_if(
name.begin(),
name.end(),
[](char c) -> bool { return !isalnum(c); },
'_');
return name;
}
template<class TestContext>
std::string PityUnit<TestContext>::_status_string(const std::string &msg) const
{
std::string ret;
ret = "[ " + to_string(_exec_mode) + ":" + std::to_string(getpid()) + " ] [ " +
getPathShort() + " ] [ " + msg + " ]";
return ret;
}
template<class TestContext>
Utils::Color PityUnit<TestContext>::_colForProcUnitNr(int procUnitNr) const
{
int nrColors = 7;
switch (procUnitNr % nrColors) {
case 0:
return Utils::Color::WHITE;
case 1:
return Utils::Color::GREEN;
case 2:
return Utils::Color::YELLOW;
case 3:
return Utils::Color::CYAN;
case 4:
return Utils::Color::BLUE;
case 5:
return Utils::Color::MAGENTA;
case 6:
return Utils::Color::RED;
default:
return Utils::Color::WHITE;
}
}
template<class TestContext>
Utils::Color PityUnit<TestContext>::_termColor() const
{
return _colForProcUnitNr(procUnitNr);
}
template<class TestContext>
void PityUnit<TestContext>::_logRaw(const std::string &msg) const
{
// fs-mutex to sync across processes
_log_mutex->aquire();
Adapter::pEpLog::log(msg, _termColor());
_log_mutex->release();
}
} // namespace PityTest11
} // namespace pEp


Loading…
Cancel
Save