PityTest11 is a very flexible C++11 peer-to-peer test framework supporting hierarchically structured test suites, multi-processing, transport system, logging, IPC, synchronization and more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

457 lines
15 KiB

// This file is under GNU General Public License 3.0
// see LICENSE.txt
#include "AbstractPityUnit.hh"
#include <pEp/std_utils.hh>
#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <sys/stat.h>
#include <exception>
#include <memory>
#include <sys/wait.h>
namespace pEp {
namespace PityTest11 {
// static
std::string AbstractPityUnit::_global_root_dir = Utils::path_get_abs("pitytest_data/");
// static
bool AbstractPityUnit::debug_log_enabled = false;
// static
int AbstractPityUnit::_procUnitsCount = 0;
AbstractPityUnit::AbstractPityUnit(const std::string &name, ExecutionMode exec_mode) :
PityTree<AbstractPityUnit>(*this, name), _exec_mode{ exec_mode }, _procUnitNr{ 0 }
{
_init();
}
AbstractPityUnit::AbstractPityUnit(
AbstractPityUnit &parent,
const std::string &name,
ExecutionMode exec_mode) :
PityTree<AbstractPityUnit>(*this, name, parent),
_exec_mode{ exec_mode }, _procUnitNr{ 0 }
{
_init();
}
AbstractPityUnit::AbstractPityUnit(const AbstractPityUnit &rhs, AbstractPityUnit &self) :
PityTree<AbstractPityUnit>(rhs, self)
{
_procUnitNr = rhs._procUnitNr;
_exec_mode = rhs._exec_mode;
_transport = rhs._transport; // Will re-initialized in run()
_transport_endpoints = rhs._transport_endpoints; // Will re-initialized in run()
_init();
}
// AbstractPityUnit &AbstractPityUnit::operator=(const AbstractPityUnit &rhs)
// {
// _procUnitNr = rhs._procUnitNr;
// _exec_mode = rhs._exec_mode;
// _transport = rhs._transport;
// _transport_endpoints = rhs._transport_endpoints;
// return *this;
// }
void AbstractPityUnit::_init()
{
_log_mutex = std::make_shared<fs_mutex>("log.mutex");
_log_mutex->release();
}
// 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::setExecMode(AbstractPityUnit::ExecutionMode execMode)
{
_exec_mode = execMode;
}
// 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 (isRoot()) {
ret = getName();
} else {
if (isProcessUnit()) {
ret += ".../" + getName();
} else {
if (&(getParentProcessUnit()) == (getParent())) {
ret = getParentProcessUnit().getPathShort() + "/" + getName();
} else {
ret = getParentProcessUnit().getPathShort() + "/.../" + getName();
}
}
}
return ret;
}
// Every process has its own dir inside its rootUnitDir
// All other units inherit processDir from their Root/ProcessUnit
std::string AbstractPityUnit::getProcessDir()
{
if (isRoot()) {
return getRootUnitDir();
} else {
if (isProcessUnit()) {
return getGlobalRootDir() + _normalizeName(getPath()) + "/";
} else {
return getParent()->getProcessDir();
}
}
}
// Every RootUnit has its own dir
std::string AbstractPityUnit::getRootUnitDir()
{
return getGlobalRootDir() + getRoot()->getName() + "/";
}
// Every process has its own dir inside its rootUnitDir
// All other units inherit transportDir from their Root/ProcessUnit
std::string AbstractPityUnit::getTransportDir()
{
return getProcessDir() + "inbox/";
}
void AbstractPityUnit::_initProcUnitNrRecurse()
{
if (!isRoot()) {
// Inherit
_procUnitNr = getParent()->_procUnitNr;
//Or update if procUnit
if (isProcessUnit()) {
_procUnitsCount++;
_procUnitNr = _procUnitsCount;
}
} else {
_procUnitNr = _procUnitsCount;
}
// Recurse
for (const auto &chld : getChildRefs()) {
chld.second._initProcUnitNrRecurse();
}
}
void AbstractPityUnit::_initTransportRecurse()
{
if (!isRoot()) {
if (isProcessUnit()) {
_createTransport();
}
}
// Recurse
for (const auto &chld : getChildRefs()) {
chld.second._initTransportRecurse();
}
}
void AbstractPityUnit::_initDirsRecursive()
{
Utils::dir_recreate(getProcessDir());
// Recurse
for (const auto &child : getChildRefs()) {
child.second._initDirsRecursive();
}
}
void AbstractPityUnit::run(bool init_tree)
{
pEpLogClass("called");
if (init_tree) {
logH1("PityTest Starting...");
_logRaw("RootUnit: " + getPath());
_logRaw("GlobalRootDir: " + getGlobalRootDir());
_logRaw("Ensuring GlobalRootDir...");
Utils::dir_ensure(getGlobalRootDir());
_logRaw("Recreating process dirs recursively...");
_initDirsRecursive();
//TODO:HACK wait for dir
Utils::sleep_millis(500);
_logRaw("Initializing Transport recursively...");
_initTransportRecurse();
_logRaw("\n\nTestTree");
_logRaw("--------");
_logRaw(to_string() + "\n");
_procUnitsCount = 0;
_initProcUnitNrRecurse();
}
// TODO: hack move to _executeOnFork()
setenv("HOME", getProcessDir().c_str(), true);
// Execute in fork and wait here until process ends
if (_exec_mode == ExecutionMode::PROCESS_SEQUENTIAL) { // fork
_executeInFork(std::bind(&AbstractPityUnit::_runRecurse, 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::_runRecurse, this), false);
// Execute as normal function
} else if (_exec_mode == ExecutionMode::FUNCTION) {
_runRecurse();
} 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 (init_tree) {
_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 << " ]";
builder << std::endl;
ret = builder.str();
if (recursive) {
if (!getChildRefs().empty()) {
indent++;
for (const auto child : getChildRefs()) {
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 "PROCESS_SEQ";
case ExecutionMode::PROCESS_PARALLEL:
return "PROCESS_PAR";
case ExecutionMode::THREAD_SEQUENTIAL:
return "THREAD_SEQ";
case ExecutionMode::THREAD_PARALLEL:
return "THREAD_PAR";
case ExecutionMode::INHERIT:
return "INHERIT";
default:
return "UNDEFINED EXECUTION MODE";
}
}
void AbstractPityUnit::registerAsTransportEndpoint()
{
transportEndpoints().insert({ getName(), getTransportDir() });
}
Endpoints &AbstractPityUnit::transportEndpoints()
{
if (isRoot()) {
return _transport_endpoints;
} else {
return getRoot()->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, _color());
}
void AbstractPityUnit::logH2(const std::string &msg) const
{
Adapter::pEpLog::logH2(msg, _color());
}
void AbstractPityUnit::logH3(const std::string &msg) const
{
Adapter::pEpLog::logH3(msg, _color());
}
// PRIVATE ---------------------------------------------------------------------------------
void AbstractPityUnit::_runRecurse()
{
logH2(_status_string("STARTING"));
_runSelf();
if (!getChildRefs().empty()) {
for (const auto child : getChildRefs()) {
child.second.run(false);
}
}
// This should be fine
_waitChildProcesses();
}
void AbstractPityUnit::_executeInFork(std::function<void(void)> func, bool wait_child)
{
pid_t pid;
pid = fork();
if (pid == pid_t(0)) {
// Setup process env.
Utils::dir_set_cwd(getProcessDir());
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(_color()));
}
}
bool AbstractPityUnit::isProcessUnit() const
{
bool ret = false;
if (_exec_mode == ExecutionMode::PROCESS_SEQUENTIAL ||
_exec_mode == ExecutionMode::PROCESS_PARALLEL) {
ret = true;
}
return ret;
}
const AbstractPityUnit &AbstractPityUnit::getParentProcessUnit() const
{
if (isRoot() || isProcessUnit()) {
return *this;
} else {
return getParent()->getParentProcessUnit();
}
}
// Inherited (if null see parent recursively)
void AbstractPityUnit::_createTransport()
{
registerAsTransportEndpoint();
_transport = std::make_shared<PityTransport>(getTransportDir(), transportEndpoints());
}
// Inherited (if null see parent recursively)
PityTransport *AbstractPityUnit::transport() const
{
// pEpLogClass("called");
PityTransport *ret = nullptr;
if (_transport != nullptr) {
ret = _transport.get();
} else {
if (!isRoot()) {
ret = getParent()->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 = 6;
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;
default:
return Utils::Color::WHITE;
}
}
Utils::Color AbstractPityUnit::_color() 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, _color());
_log_mutex->release();
}
} // namespace PityTest11
} // namespace pEp