Browse Source
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.LIB-11

10 changed files with 463 additions and 165 deletions
@ -1,80 +0,0 @@ |
|||
#include "fw_dist_test.hh" |
|||
#include "../../../src/std_utils.hh" |
|||
#include "../../framework/utils.hh" |
|||
#include <iostream> |
|||
#include <unistd.h> |
|||
#include <stdlib.h> |
|||
#include <sys/stat.h> |
|||
|
|||
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
|
@ -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 <string> |
|||
#include <functional> |
|||
|
|||
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<void(void)> alice_main{}; |
|||
std::function<void(void)> 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
|
@ -1,40 +0,0 @@ |
|||
#include "fw_dist_test.hh" |
|||
#include "../../framework/utils.hh" |
|||
|
|||
#include <iostream> |
|||
|
|||
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; |
|||
} |
@ -0,0 +1,9 @@ |
|||
#include "pEpTestModel.hh" |
|||
|
|||
//using namespace std;
|
|||
namespace pEp { |
|||
namespace Test { |
|||
bool pEpTestModel::log_enabled = true; |
|||
|
|||
} // namespace Test
|
|||
} // namespace pEp
|
@ -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
|
@ -0,0 +1,289 @@ |
|||
#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> |
|||
|
|||
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<string, TestNode> 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<string, pEpTestUnit>(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<string, pEpTestUnit> 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
|
@ -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 <string> |
|||
#include <functional> |
|||
#include <map> |
|||
#include "../../../src/pEpLog.hh" |
|||
#include "pEpTestModel.hh" |
|||
|
|||
namespace pEp { |
|||
namespace Test { |
|||
class pEpTestUnit { |
|||
public: |
|||
using TestEntryFunc = 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 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<const std::string, pEpTestUnit> 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
|
@ -0,0 +1,43 @@ |
|||
#include "pEpTestUnit.hh" |
|||
#include "../../framework/utils.hh" |
|||
#include <iostream> |
|||
#include <unistd.h> |
|||
|
|||
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(); |
|||
} |
|||
} |
Loading…
Reference in new issue