From e50ed656d7cc8e4e403136eb14702bfca9705daa Mon Sep 17 00:00:00 2001 From: heck Date: Thu, 3 Jun 2021 23:46:48 +0200 Subject: [PATCH] Test: PityTest11 dir rename --- Makefile | 29 +++ src/PityModel.cc | 16 ++ src/PityModel.hh | 26 +++ src/PityUnit.hh | 113 ++++++++++++ src/PityUnit.hxx | 390 +++++++++++++++++++++++++++++++++++++++ test/test_execmodes.cc | 65 +++++++ test/test_processdirs.cc | 55 ++++++ 7 files changed, 694 insertions(+) create mode 100644 Makefile create mode 100644 src/PityModel.cc create mode 100644 src/PityModel.hh create mode 100644 src/PityUnit.hh create mode 100644 src/PityUnit.hxx create mode 100644 test/test_execmodes.cc create mode 100644 test/test_processdirs.cc diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..770fbc2 --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +LDLIBS=-lstdc++ -lpEpAdapter +LDFLAGS=-L../../src/ +CXXFLAGS=-std=c++11 -g +CXXFLAGS+=-I./src +TEST_EXTRA_OBJS=../framework/utils.o + +# Src +SRC=$(wildcard src/*.cc) +OBJ=$(subst .cc,.o,$(SRC)) + +# Tests +TEST_SRC=$(wildcard test/*.cc) +TEST_OBJ=$(subst .cc,,$(TEST_SRC)) + + +.PHONY: all clean rmtestdata +.DEFAULT_GOAL := all + +all: $(TEST_OBJ) + +$(TEST_OBJ): $(OBJ) $(TEST_EXTRA_OBJS) + + +clean: + rm -f $(OBJ) + rm -f $(TEST_OBJ) + rm -rf src/*.dSYM + rm -rf test/*.dSYM + diff --git a/src/PityModel.cc b/src/PityModel.cc new file mode 100644 index 0000000..1fcb546 --- /dev/null +++ b/src/PityModel.cc @@ -0,0 +1,16 @@ +#include "PityModel.hh" +#include "iostream" + +using namespace std; +namespace pEp { + namespace PityTest11 { + bool PityModel::log_enabled = true; + + PityModel::PityModel(const string& name) : name(name) {} + + const std::string& PityModel::getName() const + { + return name; + } + } // namespace Test +} // namespace pEp diff --git a/src/PityModel.hh b/src/PityModel.hh new file mode 100644 index 0000000..a660a26 --- /dev/null +++ b/src/PityModel.hh @@ -0,0 +1,26 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#ifndef PITYTEST_PITYMODEL_HH +#define PITYTEST_PITYMODEL_HH + +#include "../../../src/pEpLog.hh" + +namespace pEp { + namespace PityTest11 { + class PityModel { + public: + PityModel() = delete; + PityModel(const std::string& name); + virtual const std::string& getName() const; + + private: + const std::string name; + static bool log_enabled; + Adapter::pEpLog::pEpLogger logger{ "PityModel", log_enabled }; + Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; + }; + }; // namespace Test +}; // namespace pEp + +#endif // PITYTEST_PITYMODEL_HH diff --git a/src/PityUnit.hh b/src/PityUnit.hh new file mode 100644 index 0000000..580f10d --- /dev/null +++ b/src/PityUnit.hh @@ -0,0 +1,113 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#ifndef PITYTEST_PITYUNIT_HH +#define PITYTEST_PITYUNIT_HH + +#include +#include +#include +#include "../../../src/pEpLog.hh" + +// Yes, the mem mgmt is purely static on purpose (so far) + +namespace pEp { + namespace PityTest11 { + template + class PityUnit { + public: + using NodeFunc = std::function; + + enum class ExecutionMode + { + FUNCTION, + PROCESS_SEQUENTIAL, + PROCESS_PARALLEL, + THREAD_SEQUENTIAL, // unimplemented + THREAD_PARALLEL, // unimplemented + INHERIT + }; + + // Constructors are private + PityUnit() = delete; + explicit PityUnit( + PityUnit* parent, + const std::string& name, + const NodeFunc test_func = nullptr, + T* model = nullptr, + ExecutionMode exec_mode = ExecutionMode::FUNCTION); + + // Read-Only + std::string getNodeName() const; + std::string getNodePath() const; + std::string getNodePathShort() const; + T* getModel() const; + std::string rootNodeDir() const; + std::string processDir() const; // own process dir + + // Read-Write + void setExecutionMode(ExecutionMode mode); + static void setGlobalRootDir(const std::string& dir); + static std::string getGlobalRootDir(); + + + // Main funcs + void run() 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{ "PityUnit", debug_log_enabled }; + + private: + // CONSTRUCTORS + + // METHODS + // Execution + void _runRootNode() const; + void _run() const; + void _runSelf() const; + void _runChildren() const; + void _executeInFork(std::function func, bool wait_child) const; + void _waitChildProcesses() const; + + // Modify + void _addChildNode(PityUnit& node); + + // Query + bool _isProcessNode() const; + bool _isRootNode() const; + const PityUnit& _rootNode() const; + const PityUnit& _parentingProcessNode() 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 PityUnit* _parent; //nullptr if RootUnit + T* _model; //nullptr if inherited + const NodeFunc _test_func; + ExecutionMode _exec_mode; + static std::string _global_root_dir; + + std::map _children; // map to guarantee uniqueness of sibling-names + + // internal logging + Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger_debug; + }; + }; // namespace Test +}; // namespace pEp + +#include "PityUnit.hxx" + +#endif // PITYTEST_PITYUNIT_HH diff --git a/src/PityUnit.hxx b/src/PityUnit.hxx new file mode 100644 index 0000000..6911dd8 --- /dev/null +++ b/src/PityUnit.hxx @@ -0,0 +1,390 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#ifndef PITYTEST_PITYUNIT_HXX +#define PITYTEST_PITYUNIT_HXX + +#include "../../../src/std_utils.hh" +#include "../../framework/utils.hh" +#include +#include +#include +#include +#include +#include +#include + + +using namespace pEp::Adapter::pEpLog; + +namespace pEp { + namespace PityTest11 { + template + std::string PityUnit::_global_root_dir = "./pitytest"; + template + bool PityUnit::debug_log_enabled = false; + + // PUBLIC CONCSTRUCTORS / FACTORY ----------------------------------------------------------- + template + PityUnit::PityUnit( + PityUnit* const parent, + const std::string& name, + const NodeFunc test_func, + T* model, + 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); + } + } + + template + std::string PityUnit::getNodeName() const + { + return _name; + } + + template + std::string PityUnit::getNodePath() const + { + std::string ret; + + if (!_isRootNode()) { + ret = _parent->getNodePath() + "/" + getNodeName(); + } else { + ret = getNodeName(); + } + return ret; + } + + // For: + // RootNode - "" + // ProcessNode - ".../" + // When Process as dir. parent - "...//name" + // When no process as dir. parent - "...//.../name" + template + std::string PityUnit::getNodePathShort() const + { + std::string ret; + if (_isRootNode()) { + ret = getNodeName(); + } else { + if (_isProcessNode()) { + ret += ".../" + getNodeName(); + } else { + if (&(_parentingProcessNode()) == (_parent)) { + ret = _parentingProcessNode().getNodePathShort() + "/" + getNodeName(); + } else { + ret = _parentingProcessNode().getNodePathShort() + "/.../" + getNodeName(); + } + } + } + return ret; + } + + // Inherited (if null see parent recursively) + template + T* PityUnit::getModel() const + { + pEpLogClass("called"); + T* ret = nullptr; + + if (_model != nullptr) { + ret = _model; + } else { + if (!_isRootNode()) { + ret = _parent->getModel(); + } + } + return ret; + } + + // Every RootNode has its own dir + template + std::string PityUnit::rootNodeDir() const + { + return getGlobalRootDir() + _rootNode().getNodeName() + "/"; + } + + // Every process has its own dir inside its rootNodeDir + // All other nodes inherit processDir from their Root/ProcessNode + template + std::string PityUnit::processDir() const + { + if (_isRootNode()) { + return rootNodeDir(); + } else { + if (_isProcessNode()) { + return rootNodeDir() + getNodeName() + "/"; + } else{ + return _parent->processDir(); + } + } + } + + template + void PityUnit::setExecutionMode(ExecutionMode mode) + { + _exec_mode = mode; + } + + // static + template<> + void PityUnit::setGlobalRootDir(const std::string& dir) + { + PityUnit::_global_root_dir = dir; + } + + // static + template + std::string PityUnit::getGlobalRootDir() + { + return PityUnit::_global_root_dir; + } + + template + void PityUnit::run() const + { + pEpLogClass("called"); + // caller is never nullptr if called by another PityUnit + if (_isRootNode()) { + logH1("Starting PityUnit from node: " + getNodePathShort()); + log(to_string()); + } + + // Execute in fork and wait here until process ends + if (_exec_mode == ExecutionMode::PROCESS_SEQUENTIAL) { // fork + logH2("[ " + to_string(_exec_mode) + " / " + getNodePathShort() + " ]"); + _executeInFork(std::bind(&PityUnit::_run, this), true); + // Execute in fork and go on, wait for process execution in the end + } else if (_exec_mode == ExecutionMode::PROCESS_PARALLEL) { + logH2("[ " + to_string(_exec_mode) + " / " + getNodePathShort() + " ]"); + _executeInFork(std::bind(&PityUnit::_run, this), false); + // Execute as normal funciton + } else if (_exec_mode == ExecutionMode::FUNCTION) { + logH3("[ " + to_string(_exec_mode) + " / " + getNodePathShort() + " ]"); + _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(_isRootNode()) { + _waitChildProcesses(); + } + } + + template + std::string PityUnit::to_string(bool recursive, int indent) const + { + std::string ret; + std::stringstream builder; + builder << std::string(indent * 4, ' '); + + builder << getNodeName(); + builder << " [ "; + builder << to_string(_exec_mode) << " - "; + builder << "\"" << processDir() << "\""; + builder << " ]"; + builder << std::endl; + ret = builder.str(); + + if (recursive) { + indent++; + for (const std::pair child : _children) { + ret += child.second.to_string(true, indent); + } + indent--; + } + return ret; + } + + template + std::string PityUnit::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"; + } + } + + //Well, ok, lets just add some little convenience logging service in here, too + template + void PityUnit::log(const std::string& msg) const + { + std::stringstream builder; + builder << "["; + builder << std::to_string(getpid()); + builder << " - "; + builder << getNodePathShort(); + builder << "] - "; + builder << msg; + builder << std::endl; + std::cout << builder.str(); + } + + // PRIVATE --------------------------------------------------------------------------------- + + template + void PityUnit::_run() const + { + _runSelf(); + _runChildren(); + } + + template + void PityUnit::_runSelf() const + { + if (_test_func != nullptr) { + _test_func(*this); + } else { + log("No function to execute"); + } + } + + template + void PityUnit::_runChildren() const + { + if (!_children.empty()) { + for (const std::pair child : _children) { + child.second.run(); + } + } + } + + template + void PityUnit::_executeInFork(std::function 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 + void PityUnit::_waitChildProcesses() const + { + int status; + pid_t pid; + while ((pid = wait(&status)) > 0) { + log( + "process[" + std::to_string((int)pid) + + "] terminated with status: " + std::to_string(status)); + } + } + + template + void PityUnit::_addChildNode(PityUnit& node) + { + _children.insert(std::pair(node.getNodeName(), node)); + } + + template + bool PityUnit::_isProcessNode() const + { + bool ret = false; + if (_exec_mode == ExecutionMode::PROCESS_SEQUENTIAL || + _exec_mode == ExecutionMode::PROCESS_PARALLEL) { + ret = true; + } + return ret; + } + + template + bool PityUnit::_isRootNode() const + { + if (_parent == nullptr) { + return true; + } else { + return false; + } + } + + template + const PityUnit& PityUnit::_rootNode() const + { + const PityUnit* ret = nullptr; + if (!_isRootNode()) { + ret = &(_parent->_rootNode()); + } 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; + } + + template + const PityUnit& PityUnit::_parentingProcessNode() const + { + if (_isRootNode() || _isProcessNode()) { + return *this; + } else { + return _parent->_parentingProcessNode(); + } + } + + // name is alphanumeric only (everything else will be replaced by an underscore) + template + std::string PityUnit::_normalizeName(std::string name) const + { + replace_if( + name.begin(), + name.end(), + [](char c) -> bool { return !isalnum(c); }, + '_'); + + return name; + } + + template + void PityUnit::_data_dir_create() + { + // Utils::dir_create(dataDir()); + log("creating dir:" + getGlobalRootDir()); + } + + template + void PityUnit::_data_dir_delete() + { + try { + Utils::path_delete_all(getGlobalRootDir()); + } catch (const std::exception& e) { + log("DistTest: - could not delete data dir: " + getGlobalRootDir()); + } + } + + template + void PityUnit::_data_dir_recreate() + { + _data_dir_delete(); + _data_dir_create(); + }; + } // namespace Test +} // namespace pEp + +#endif // PITYTEST_PITYUNIT_HXX \ No newline at end of file diff --git a/test/test_execmodes.cc b/test/test_execmodes.cc new file mode 100644 index 0000000..393c358 --- /dev/null +++ b/test/test_execmodes.cc @@ -0,0 +1,65 @@ +#include "../src/PityUnit.hh" +#include + +using namespace std; +using namespace pEp::PityTest11; + +void printHomeDir(const PityUnit<>& myself, int sleepmilis, int rep_count) +{ + int i = 0; + while (i < rep_count) { + myself.log(myself.getNodeName() + " - " + to_string(i)); + sleep_millis(sleepmilis); + i++; + } +} + +int main(int argc, char* argv[]) +{ + { + // DEBUG Logging of PityTestUnit itself + PityUnit<>::debug_log_enabled = false; + + // Configure DataRoot for all TestNodes + PityUnit<>::setGlobalRootDir("./basic_data_root"); + + // The RootNode is the + PityUnit<> root = PityUnit<>{ nullptr, "Test Execution Model" }; + + // Subprocess 1 + PityUnit<> test1 = PityUnit<>{ &root, + "node1", + [](const PityUnit<>& mynode) { + printHomeDir(mynode, 200, 10); + }, + nullptr, + pEp::PityTest11::PityUnit<>::ExecutionMode::PROCESS_PARALLEL }; + + PityUnit<> test1_1 = PityUnit<>{ &test1, "test1.1", [](const PityUnit<>& mynode) { + printHomeDir(mynode, 200, 10); + } }; + + PityUnit<> test1_2 = PityUnit<>{ &test1, "test1.2", [](const PityUnit<>& mynode) { + printHomeDir(mynode, 200, 10); + } }; + + // Subprocess 2 + PityUnit<> test2 = PityUnit<>{ &root, + "node2", + [](const PityUnit<>& mynode) { + printHomeDir(mynode, 200, 10); + }, + nullptr, + pEp::PityTest11::PityUnit<>::ExecutionMode::PROCESS_PARALLEL }; + + PityUnit<> test2_1 = PityUnit<>{ &test2, "test2.1", [](const PityUnit<>& mynode) { + printHomeDir(mynode, 200, 10); + } }; + + PityUnit<> test2_2 = PityUnit<>{ &test2, "test2.2", [](const PityUnit<>& mynode) { + printHomeDir(mynode, 200, 10); + } }; + + test1.run(); + } +} \ No newline at end of file diff --git a/test/test_processdirs.cc b/test/test_processdirs.cc new file mode 100644 index 0000000..f1f35ee --- /dev/null +++ b/test/test_processdirs.cc @@ -0,0 +1,55 @@ +#include "../src/PityUnit.hh" +#include "../../framework/utils.hh" +#include +#include + +using namespace std; +using namespace pEp::PityTest11; + +void printHomeDir(const PityUnit<>& myself) +{ + // TESTLOG(string(myself.getFQName() + " - PID: " + to_string(getpid()))); + // cout << "[" << to_string(getpid()) << "/" << myself.getFQName() << "] - " << endl; + setenv("HOME", myself.processDir().c_str(), 1); + myself.log("HOME=" + string(getenv("HOME"))); +} + +void ptAssert(bool condition) { + if(!condition) { + + } +} + +int main(int argc, char* argv[]) +{ + PityUnit<>::debug_log_enabled = false; + PityUnit<>::setGlobalRootDir("./"); + PityUnit<> root = PityUnit<>{ nullptr, "rootnode" }; + + PityUnit<> test1 = PityUnit<>{ &root, "node 1", [](const PityUnit<>& mynode) { + assert(mynode.processDir() == "./rootnode/"); + } }; + + PityUnit<> test1_1 = PityUnit<>{ &test1, "node 1.1", [](const PityUnit<>& mynode) { + assert(mynode.processDir() == "./rootnode/"); + } }; + + PityUnit<> test2 = PityUnit<>{ &root, "node 2", [](const PityUnit<>& mynode) { + assert(mynode.processDir() == "./rootnode/"); + } }; + + PityUnit<> test2_1 = PityUnit<>{ &test2, + "node 2.1", + [](const PityUnit<>& mynode) { + assert(mynode.processDir() == "./rootnode/"); + }, + nullptr, + pEp::PityTest11::PityUnit<>::ExecutionMode::PROCESS_PARALLEL }; + + + PityUnit<> test2_1_1 = PityUnit<>{ &test2_1, "node 2.1.1", [](PityUnit<> mynode) { + assert(mynode.processDir() == "./rootnode/"); + } }; + + root.run(); +} \ No newline at end of file