Browse Source

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.
LIB-11
heck 4 years ago
parent
commit
aac0603b60
  1. 5
      .gitignore
  2. 80
      test/fw_dist_test/src/fw_dist_test.cc
  3. 43
      test/fw_dist_test/src/fw_dist_test.hh
  4. 40
      test/fw_dist_test/test/test_fw_dist_test.cc
  5. 0
      test/pEpTest/Makefile
  6. 9
      test/pEpTest/src/pEpTestModel.cc
  7. 22
      test/pEpTest/src/pEpTestModel.hh
  8. 289
      test/pEpTest/src/pEpTestUnit.cc
  9. 97
      test/pEpTest/src/pEpTestUnit.hh
  10. 43
      test/pEpTest/test/test_pEpLog_basic.cc

5
.gitignore

@ -29,6 +29,7 @@ test_lm_dummy
test_sqlite3
test_pEpsqlite
test_listmanager_dummy
test_fw_dist_test
test_pEpLog_basic
*.db
*.db

80
test/fw_dist_test/src/fw_dist_test.cc

@ -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

43
test/fw_dist_test/src/fw_dist_test.hh

@ -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

40
test/fw_dist_test/test/test_fw_dist_test.cc

@ -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
test/fw_dist_test/Makefile → test/pEpTest/Makefile

9
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

22
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

289
test/pEpTest/src/pEpTestUnit.cc

@ -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

97
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 <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

43
test/pEpTest/test/test_pEpLog_basic.cc

@ -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…
Cancel
Save