From aac0603b606dc07ee198b6c2d4c8a259b58ebdae Mon Sep 17 00:00:00 2001 From: heck Date: Thu, 20 May 2021 01:00:08 +0200 Subject: [PATCH] Tests: add first draft of pEpTest pEpTest is the new testing framework featuring: - Distributed Testing - TestUnit Tree * TestUnit config is inherited * Hierarchy represents prerequisites - Execution model supporting * Functions * Process serial and parallel * Threads serial and parallel - Lambdas (of course) - DataModel * Roles (1 ident) * Identities * Nodes * Nodes and Roles have n-n relationship - Perspective: Test are written from a 1st person perspective, which can assume any of the ROLES. - Transport: send messages to ROLES, on any node. --- .gitignore | 5 +- test/fw_dist_test/src/fw_dist_test.cc | 80 ------ test/fw_dist_test/src/fw_dist_test.hh | 43 --- test/fw_dist_test/test/test_fw_dist_test.cc | 40 --- test/{fw_dist_test => pEpTest}/Makefile | 0 test/pEpTest/src/pEpTestModel.cc | 9 + test/pEpTest/src/pEpTestModel.hh | 22 ++ test/pEpTest/src/pEpTestUnit.cc | 289 ++++++++++++++++++++ test/pEpTest/src/pEpTestUnit.hh | 97 +++++++ test/pEpTest/test/test_pEpLog_basic.cc | 43 +++ 10 files changed, 463 insertions(+), 165 deletions(-) delete mode 100644 test/fw_dist_test/src/fw_dist_test.cc delete mode 100644 test/fw_dist_test/src/fw_dist_test.hh delete mode 100644 test/fw_dist_test/test/test_fw_dist_test.cc rename test/{fw_dist_test => pEpTest}/Makefile (100%) create mode 100644 test/pEpTest/src/pEpTestModel.cc create mode 100644 test/pEpTest/src/pEpTestModel.hh create mode 100644 test/pEpTest/src/pEpTestUnit.cc create mode 100644 test/pEpTest/src/pEpTestUnit.hh create mode 100644 test/pEpTest/test/test_pEpLog_basic.cc diff --git a/.gitignore b/.gitignore index 3f5c94d..db014cb 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ test_lm_dummy test_sqlite3 test_pEpsqlite test_listmanager_dummy -test_fw_dist_test +test_pEpLog_basic + +*.db -*.db \ No newline at end of file diff --git a/test/fw_dist_test/src/fw_dist_test.cc b/test/fw_dist_test/src/fw_dist_test.cc deleted file mode 100644 index fe63a94..0000000 --- a/test/fw_dist_test/src/fw_dist_test.cc +++ /dev/null @@ -1,80 +0,0 @@ -#include "fw_dist_test.hh" -#include "../../../src/std_utils.hh" -#include "../../framework/utils.hh" -#include -#include -#include -#include - -using namespace std; -namespace pEp { - namespace Test { - string dummy; - - DistTest::DistTest() : mode(TestMode::ALICE) - { - alice_home = data_dir + "/" + "alice/"; - bob_home = data_dir + "/" + "bob/"; - data_dir_recreate(); - } - - void DistTest::run() - { - if (mode == TestMode::ALICE) { - alice_main(); - } else if (mode == TestMode::ALICE_BOB) { - pid_t pid; - pid = fork(); - - if (pid == pid_t(0)) { - cout << "Child PID:" << pid << endl; - run_alice_main(); - } else if (pid > pid_t(0)) { - cout << "Parent PID:" << pid << endl; - run_bob_main(); - } else { - cerr << "error forking" << endl; - } - } - } - - void DistTest::run_alice_main() - { - cout << "New process for Alice" << endl; - setenv("HOME", alice_home.c_str(), 1); - mkdir(alice_home.c_str(), 0770); - cout << "HOME: " << getenv("HOME") << endl; - alice_main(); - } - - void DistTest::run_bob_main() - { - cout << "New process for bob" << endl; - setenv("HOME", bob_home.c_str(), 1); - mkdir(bob_home.c_str(), 0770); - cout << "HOME: " << getenv("HOME") << endl; - bob_main(); - } - - - void DistTest::data_dir_delete() - { - try { - Utils::path_delete_all(data_dir); - } catch (const exception& e) { - TESTLOG("DistTest: - could not delete data dir: " + data_dir); - } - } - - void DistTest::data_dir_create() - { - Utils::dir_create(data_dir); - } - - void DistTest::data_dir_recreate() { - data_dir_delete(); - data_dir_create(); - }; - - } // namespace Test -} // namespace pEp diff --git a/test/fw_dist_test/src/fw_dist_test.hh b/test/fw_dist_test/src/fw_dist_test.hh deleted file mode 100644 index 4ad0b9b..0000000 --- a/test/fw_dist_test/src/fw_dist_test.hh +++ /dev/null @@ -1,43 +0,0 @@ -// This file is under GNU General Public License 3.0 -// see LICENSE.txt - -#ifndef LIBPEPADAPTER_FW_DIST_TEST_HH -#define LIBPEPADAPTER_FW_DIST_TEST_HH - -#include -#include - -namespace pEp { - namespace Test { - - class DistTest { - public: - DistTest(); - - enum class TestMode - { - ALICE, - ALICE_BOB - }; - TestMode mode; - - std::string data_dir = "./data"; - std::string alice_home; - std::string bob_home; - - std::function alice_main{}; - std::function bob_main{}; - void run(); - - private: - void run_alice_main(); - void run_bob_main(); - - void data_dir_delete(); - void data_dir_create(); - void data_dir_recreate(); - }; - }; // namespace Test -}; // namespace pEp - -#endif // LIBPEPADAPTER_FW_DIST_TEST_HH diff --git a/test/fw_dist_test/test/test_fw_dist_test.cc b/test/fw_dist_test/test/test_fw_dist_test.cc deleted file mode 100644 index 91086b6..0000000 --- a/test/fw_dist_test/test/test_fw_dist_test.cc +++ /dev/null @@ -1,40 +0,0 @@ -#include "fw_dist_test.hh" -#include "../../framework/utils.hh" - -#include - -using namespace std; -using namespace pEp; - -Test::DistTest test_fw; - - -void alice_main() -{ - cout << "HYA FROM ALICE" << endl; - while (true) { - cout << "1" << endl; - sleep_millis(1000); - } -} - -void bob_main() -{ - cout << "HYA FROM BOB" << endl; - while (true) { - cout << "2" << endl; - sleep_millis(1000); - } -} - - -int main(int argc, char* argv[]) -{ - test_fw.mode = Test::DistTest::TestMode::ALICE_BOB; - test_fw.alice_main = &::alice_main; - test_fw.bob_main = &::bob_main; - test_fw.run(); - - - cout << "HDUFGD" << endl; -} \ No newline at end of file diff --git a/test/fw_dist_test/Makefile b/test/pEpTest/Makefile similarity index 100% rename from test/fw_dist_test/Makefile rename to test/pEpTest/Makefile diff --git a/test/pEpTest/src/pEpTestModel.cc b/test/pEpTest/src/pEpTestModel.cc new file mode 100644 index 0000000..a828b0e --- /dev/null +++ b/test/pEpTest/src/pEpTestModel.cc @@ -0,0 +1,9 @@ +#include "pEpTestModel.hh" + +//using namespace std; +namespace pEp { + namespace Test { + bool pEpTestModel::log_enabled = true; + + } // namespace Test +} // namespace pEp diff --git a/test/pEpTest/src/pEpTestModel.hh b/test/pEpTest/src/pEpTestModel.hh new file mode 100644 index 0000000..63b3198 --- /dev/null +++ b/test/pEpTest/src/pEpTestModel.hh @@ -0,0 +1,22 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#ifndef LIBPEPADAPTER_PEPTESTMODEL_HH +#define LIBPEPADAPTER_PEPTESTMODEL_HH + +#include "../../../src/pEpLog.hh" + +namespace pEp { + namespace Test { + class pEpTestModel { + + public: + private: + static bool log_enabled; + Adapter::pEpLog::pEpLogger logger{ "pEpTestModel", log_enabled }; + Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; + }; + }; // namespace Test +}; // namespace pEp + +#endif // LIBPEPADAPTER_PEPTESTMODEL_HH diff --git a/test/pEpTest/src/pEpTestUnit.cc b/test/pEpTest/src/pEpTestUnit.cc new file mode 100644 index 0000000..674c24d --- /dev/null +++ b/test/pEpTest/src/pEpTestUnit.cc @@ -0,0 +1,289 @@ +#include "pEpTestUnit.hh" +#include "../../../src/std_utils.hh" +#include "../../framework/utils.hh" +#include +#include +#include +#include +#include + +using namespace std; + +namespace pEp { + namespace Test { + bool pEpTestUnit::log_enabled = true; + string pEpTestUnit::data_dir = "./"; + pEpTestUnit::ExecutionMode pEpTestUnit::exec_mode_default = pEpTestUnit::ExecutionMode::FUNCTION; + + // RootNode factory + //static + pEpTestUnit pEpTestUnit::createRootNode( + pEpTestModel& model, + const std::string& name, + const TestEntryFunc& main_f, + ExecutionMode emode) + { + pEpLog("called"); + pEpTestUnit ret(nullptr, &model, name, main_f, emode); + return ret; + } + + // ChildNode factory + //static + pEpTestUnit pEpTestUnit::createChildNode( + pEpTestUnit& parent, + const std::string& name, + const TestEntryFunc& main_f, + ExecutionMode emode) + { + pEpLog("called"); + pEpTestUnit ret(&parent, nullptr, name, main_f, emode); + return ret; + } + + //private + pEpTestUnit::pEpTestUnit( + pEpTestUnit* const parent, + pEpTestModel* model, + const std::string& name, + const TestEntryFunc& main_f, + ExecutionMode emode) : + parent(parent), + model(model), name(name), main_func(main_f), exec_mode(emode) + { + if (parent != nullptr) { + parent->addChildNode(*this); + } + logger.set_instancename(getFQName()); + // data_dir_recreate(); + } + + + //static + void pEpTestUnit::setDefaultDataDir(const std::string& dir) + { + pEpTestUnit::data_dir = dir; + } + + //static + std::string pEpTestUnit::getDataDir() + { + return pEpTestUnit::data_dir; + } + + // void TestNode::init() const + // { + // pEpLogClassH2("DistTest - init"); + // for (const pair elem : testnodes) { + // string home_dir = getHomeDir(elem.second); + // pEpLogClass("creating home dir for '" + elem.second.getName() + "' - " + home_dir); + // mkdir(home_dir.c_str(), 0770); + // } + // } + + + void pEpTestUnit::waitChildProcesses() const + { + pEpLogClass("called"); + int status; + pid_t pid; + while ((pid = wait(&status)) > 0) { + pEpLogClass( + "PID: " + std::to_string((int)pid) + " exit status: " + std::to_string(status)); + } + } + + void pEpTestUnit::addChildNode(const pEpTestUnit& node) + { + children.insert(pair(node.getName(), node)); + } + + string pEpTestUnit::getName() const + { + return name; + } + + void pEpTestUnit::run(const pEpTestUnit* caller) const + { + // pEpLogClass("called"); + // init(); + executeSelf(); + executeChildren(); + // if called by another TestNode, caller will not be nullptr, so caller will clean up + // if caller is mullptr, there is nobody who will do the cleanup, so we have to. + if (caller == nullptr) { + waitChildProcesses(); + } + } + //private + void pEpTestUnit::executeSelf() const + { + // pEpLogClass("called"); + if (main_func) { + pEpLogClass(string("Starting as: " + to_string(getEffectiveExecutionMode()))); + // Execute in fork and go on, wait for process execution in the edn + if (getEffectiveExecutionMode() == ExecutionMode::PROCESS_PARALLEL) { // fork + executeTestFuncInFork(false); + // Execute in fork and wait here until process ends + } else if (getEffectiveExecutionMode() == ExecutionMode::PROCESS_SERIAL) { + executeTestFuncInFork(true); + // Execute as normal funciton + } else if (getEffectiveExecutionMode() == ExecutionMode::FUNCTION) { + // pEpLogClass("Executing in same process and thread"); + main_func(*this); + // pEpLogClass("Execution ended"); + } else if (getEffectiveExecutionMode() == ExecutionMode::THREAD_PARALLEL) { + throw runtime_error(to_string(getEffectiveExecutionMode()) + " - not implemented"); + } else if (getEffectiveExecutionMode() == ExecutionMode::THREAD_SERIAL) { + throw runtime_error(to_string(getEffectiveExecutionMode()) + " - not implemented"); + } + } else { + pEpLogClass("No function to execute"); + } + } + + //private + void pEpTestUnit::executeChildren() const + { + pEpLogClass("called"); + for (const pair elem : children) { + elem.second.run(this); + } + } + + //private + void pEpTestUnit::executeTestFuncInFork(bool wait) const + { + pid_t pid; + pid = fork(); + if (pid == pid_t(0)) { + pEpLogClass(string("pid: [" + std::to_string(getpid()) + "] - starting...")); + setenv("HOME", getHomeDir().c_str(), 1); + main_func(*this); + pEpLogClass(string("pid: [" + std::to_string(getpid()) + "] - ended.")); + exit(0); + } else if (pid < pid_t(0)) { + pEpLogClass("Error forking"); + } + if (wait) { + waitChildProcesses(); + } + } + + 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 (parent != nullptr) { + 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::getHomeDir() const + { + return getDataDir() + "/" + getName(); + } + + string pEpTestUnit::getFQName() const + { + string ret; + // pEpLogClass("called"); + + if (parent != nullptr) { + ret = parent->getFQName() + "/" + getName(); + } else { + ret = "/" + getName(); + } + return ret; + } + + void pEpTestUnit::data_dir_delete() + { + try { + Utils::path_delete_all(getDataDir()); + } catch (const exception& e) { + pEpLogClass("DistTest: - could not delete data dir: " + getDataDir()); + } + } + + void pEpTestUnit::data_dir_create() + { + Utils::dir_create(getDataDir()); + } + + void pEpTestUnit::data_dir_recreate() + { + data_dir_delete(); + data_dir_create(); + }; + + + void pEpTestUnit::setDefaultExecutionMode(ExecutionMode emode) + { + pEpTestUnit::exec_mode_default = emode; + } + + pEpTestUnit::ExecutionMode pEpTestUnit::getExecutionMode() const + { + return exec_mode; + } + + pEpTestUnit::ExecutionMode pEpTestUnit::getEffectiveExecutionMode() const + { + ExecutionMode ret; + if (getExecutionMode() == ExecutionMode::INHERIT) { + if (parent != nullptr) { + // get from parent until not inherit + ret = parent->getEffectiveExecutionMode(); + } else { + // get from static class config + ret = pEpTestUnit::exec_mode_default; + } + } 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"; + } + } + } // namespace Test +} // namespace pEp diff --git a/test/pEpTest/src/pEpTestUnit.hh b/test/pEpTest/src/pEpTestUnit.hh new file mode 100644 index 0000000..b520b71 --- /dev/null +++ b/test/pEpTest/src/pEpTestUnit.hh @@ -0,0 +1,97 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#ifndef LIBPEPADAPTER_PEPTESTUNIT_HH +#define LIBPEPADAPTER_PEPTESTUNIT_HH + +#include +#include +#include +#include "../../../src/pEpLog.hh" +#include "pEpTestModel.hh" + +namespace pEp { + namespace Test { + class pEpTestUnit { + public: + using TestEntryFunc = std::function; + 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 TestEntryFunc& main_f, + ExecutionMode emode = ExecutionMode::INHERIT); + + static pEpTestUnit createChildNode( + pEpTestUnit& parent, + const std::string& name, + const TestEntryFunc& main_f, + ExecutionMode emode = ExecutionMode::INHERIT); + + + static void setDefaultDataDir(const std::string& dir); + static std::string getDataDir(); + void addChildNode(const pEpTestUnit& node); + void run(const pEpTestUnit* caller = nullptr) const; + std::string getName() const; + std::string getFQName() const; + pEpTestModel& getModel() const; + std::string getHomeDir() const; + static void setDefaultExecutionMode(ExecutionMode emode); + ExecutionMode getExecutionMode() const; + ExecutionMode getEffectiveExecutionMode() const; + + private: + // Constructors + explicit pEpTestUnit( + pEpTestUnit* parent, + pEpTestModel* model, + const std::string& name, + const TestEntryFunc& main_f, + ExecutionMode emode); + + // Methods + void init() const; + const pEpTestUnit& getRoot() const; + void executeSelf() const; + void executeChildren() const; + + void executeTestFuncInFork(bool wait) const; + void waitChildProcesses() const; + void data_dir_delete(); + void data_dir_create(); + void data_dir_recreate(); + + // Fields + const pEpTestUnit* parent; //nullptr if rootnode + const std::string name; + pEpTestModel* model; //nullptr if inherited + const TestEntryFunc& main_func; + ExecutionMode exec_mode; + static ExecutionMode exec_mode_default; + std::map children; + static std::string data_dir; + + // logger + static bool log_enabled; + Adapter::pEpLog::pEpLogger logger{ "pEpTestUnit", log_enabled }; + Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; + }; + }; // namespace Test +}; // namespace pEp + +#endif // LIBPEPADAPTER_PEPTESTUNIT_HH diff --git a/test/pEpTest/test/test_pEpLog_basic.cc b/test/pEpTest/test/test_pEpLog_basic.cc new file mode 100644 index 0000000..aba4f4f --- /dev/null +++ b/test/pEpTest/test/test_pEpLog_basic.cc @@ -0,0 +1,43 @@ +#include "pEpTestUnit.hh" +#include "../../framework/utils.hh" +#include +#include + +using namespace std; +using namespace pEp; +using namespace pEp::Test; + + +void print_and_sleep(pEpTestUnit myself, int sleepmilis, int rep_count) +{ + TESTLOGH1("HYA FROM:" + myself.getFQName()); + int i = 0; + while (i < rep_count) { + TESTLOG(string(myself.getFQName() + " - PID: " + to_string(getpid()) + " - " + to_string(i))); + sleep_millis(sleepmilis); + i++; + } +} + +int main(int argc, char* argv[]) +{ + pEpTestModel model{}; + { + pEpTestUnit::setDefaultExecutionMode(pEpTestUnit::ExecutionMode::PROCESS_PARALLEL); + auto root = pEpTestUnit::createRootNode(model, "rootNode", [](pEpTestUnit mynode) { + print_and_sleep(mynode, 100, 10); + }); + + auto test1 = pEpTestUnit::createChildNode(root, "test1", [](pEpTestUnit mynode) -> void { + print_and_sleep(mynode, 50, 23); + }, pEp::Test::pEpTestUnit::ExecutionMode::PROCESS_SERIAL); + + auto test2 = pEpTestUnit::createChildNode(root, "test2", [](pEpTestUnit mynode) -> void { + print_and_sleep(mynode, 150, 13); + }); + + auto test1_1 = pEpTestUnit::createChildNode(test1, "test1.1", nullptr); + + root.run(); + } +} \ No newline at end of file