
79 changed files with 254556 additions and 716 deletions
@ -1,96 +0,0 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
#include <pEp/group.h> |
|||
|
|||
#include "adapter_group.h" |
|||
#include "pEpLog.hh" |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif |
|||
|
|||
/*************************************************************************************************
|
|||
* Group management functions |
|||
*************************************************************************************************/ |
|||
|
|||
DYNAMIC_API PEP_STATUS adapter_group_create( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *manager, |
|||
identity_list *memberlist, |
|||
pEp_group **group) |
|||
{ |
|||
pEpLog("called"); |
|||
return ::group_create(session, group_identity, manager, memberlist, group); |
|||
} |
|||
|
|||
DYNAMIC_API PEP_STATUS |
|||
adapter_group_join(PEP_SESSION session, pEp_identity *group_identity, pEp_identity *as_member) |
|||
{ |
|||
pEpLog("called"); |
|||
return ::group_join(session, group_identity, as_member); |
|||
} |
|||
|
|||
DYNAMIC_API PEP_STATUS |
|||
adapter_group_dissolve(PEP_SESSION session, pEp_identity *group_identity, pEp_identity *manager) |
|||
{ |
|||
pEpLog("called"); |
|||
return ::group_dissolve(session, group_identity, manager); |
|||
} |
|||
|
|||
DYNAMIC_API PEP_STATUS adapter_group_invite_member( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *group_member) |
|||
{ |
|||
pEpLog("called"); |
|||
return ::group_invite_member(session, group_identity, group_member); |
|||
} |
|||
|
|||
PEP_STATUS adapter_group_remove_member( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *group_member) |
|||
{ |
|||
pEpLog("called"); |
|||
return ::group_remove_member(session, group_identity, group_member); |
|||
} |
|||
|
|||
DYNAMIC_API PEP_STATUS adapter_group_rating( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *manager, |
|||
PEP_rating *rating) |
|||
{ |
|||
pEpLog("called"); |
|||
return ::group_rating(session, group_identity, manager, rating); |
|||
} |
|||
|
|||
/*************************************************************************************************
|
|||
* Group query functions |
|||
*************************************************************************************************/ |
|||
|
|||
|
|||
//DYNAMIC_API PEP_STATUS group_query_groups(PEP_SESSION session, identity_list **groups)
|
|||
//{
|
|||
// pEpLog("called");
|
|||
// return PEP_STATUS_OK;
|
|||
//}
|
|||
//
|
|||
//DYNAMIC_API PEP_STATUS
|
|||
//group_query_manager(PEP_SESSION session, const pEp_identity *const group, pEp_identity **manager)
|
|||
//{
|
|||
// pEpLog("called");
|
|||
// return PEP_STATUS_OK;
|
|||
//}
|
|||
//
|
|||
//DYNAMIC_API PEP_STATUS
|
|||
//group_query_members(PEP_SESSION session, const pEp_identity *const group, identity_list **members)
|
|||
//{
|
|||
// pEpLog("called");
|
|||
// return PEP_STATUS_OK;
|
|||
//}
|
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif |
@ -0,0 +1,146 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
//#include <pEp/group.h>
|
|||
// clang-format off
|
|||
#include "group_manager_api.h" |
|||
#include "grp_driver_replicator.hh" |
|||
#include "pEpLog.hh" |
|||
#include "grp_driver_dummy.hh" |
|||
#include "grp_driver_engine.hh" |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif |
|||
|
|||
using namespace std; |
|||
using namespace pEp; |
|||
|
|||
Adapter::GroupDriverReplicator adapter_grp_manager{}; |
|||
shared_ptr<Adapter::GroupDriverDummy> grp_drv_dummy; |
|||
shared_ptr<Adapter::GroupDriverEngine> grp_drv_engine; |
|||
|
|||
DYNAMIC_API PEP_STATUS adapter_group_init() |
|||
{ |
|||
PEP_STATUS status; |
|||
try { |
|||
const string lm_dummy_db_filename = "groups.db"; |
|||
#ifdef WIN32 |
|||
const string lm_dummy_db_path = string(::per_user_directory()) + "\\" + lm_dummy_db_filename; |
|||
#else |
|||
const string lm_dummy_db_path = string(::per_user_directory()) + "/" + lm_dummy_db_filename; |
|||
#endif |
|||
|
|||
if(!grp_drv_dummy) { |
|||
grp_drv_dummy = make_shared<Adapter::GroupDriverDummy>(lm_dummy_db_path); |
|||
} |
|||
if(!grp_drv_engine) { |
|||
grp_drv_engine = make_shared<Adapter::GroupDriverEngine>(); |
|||
} |
|||
adapter_grp_manager.set_replication_source(*grp_drv_dummy.get()); |
|||
adapter_grp_manager.set_replication_destination(*grp_drv_engine.get()); |
|||
} catch (const std::exception &e) { |
|||
pEpLog(Utils::nested_exception_to_string(e)); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} catch (...) { |
|||
pEpLog("unknown exception"); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} |
|||
return status; |
|||
} |
|||
|
|||
/*************************************************************************************************
|
|||
* Group management functions |
|||
*************************************************************************************************/ |
|||
DYNAMIC_API PEP_STATUS adapter_group_create( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *manager, |
|||
identity_list *memberlist) |
|||
{ |
|||
pEpLog("called"); |
|||
PEP_STATUS status = adapter_grp_manager.adapter_group_create( |
|||
session, |
|||
group_identity, |
|||
manager, |
|||
memberlist); |
|||
return status; |
|||
} |
|||
|
|||
|
|||
DYNAMIC_API PEP_STATUS adapter_group_dissolve( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *manager) |
|||
{ |
|||
pEpLog("called"); |
|||
PEP_STATUS status = adapter_grp_manager.adapter_group_dissolve(session, group_identity, manager); |
|||
return status; |
|||
} |
|||
|
|||
DYNAMIC_API PEP_STATUS adapter_group_invite_member( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *group_member) |
|||
{ |
|||
pEpLog("called"); |
|||
PEP_STATUS status = adapter_grp_manager.adapter_group_invite_member( |
|||
session, |
|||
group_identity, |
|||
group_member); |
|||
return status; |
|||
} |
|||
|
|||
DYNAMIC_API PEP_STATUS adapter_group_remove_member( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *group_member) |
|||
{ |
|||
pEpLog("called"); |
|||
PEP_STATUS status = adapter_grp_manager.adapter_group_remove_member( |
|||
session, |
|||
group_identity, |
|||
group_member); |
|||
return status; |
|||
} |
|||
|
|||
DYNAMIC_API PEP_STATUS adapter_group_join( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *as_member) |
|||
{ |
|||
pEpLog("called"); |
|||
PEP_STATUS status = adapter_grp_manager.adapter_group_join(session, group_identity, as_member); |
|||
return status; |
|||
} |
|||
|
|||
/*************************************************************************************************
|
|||
* Group query functions |
|||
*************************************************************************************************/ |
|||
DYNAMIC_API PEP_STATUS adapter_group_query_groups(PEP_SESSION session, identity_list **groups) |
|||
{ |
|||
pEpLog("called"); |
|||
PEP_STATUS status = adapter_grp_manager.group_query_groups(session, groups); |
|||
return status; |
|||
} |
|||
|
|||
DYNAMIC_API PEP_STATUS |
|||
adapter_group_query_manager(PEP_SESSION session, const pEp_identity *const group, pEp_identity **manager) |
|||
{ |
|||
pEpLog("called"); |
|||
PEP_STATUS status = adapter_grp_manager.group_query_manager(session, group, manager); |
|||
return status; |
|||
} |
|||
|
|||
DYNAMIC_API PEP_STATUS adapter_group_query_members( |
|||
PEP_SESSION session, |
|||
const pEp_identity *const group, |
|||
identity_list **members) |
|||
{ |
|||
pEpLog("called"); |
|||
PEP_STATUS status = adapter_grp_manager.group_query_members(session, group, members); |
|||
return status; |
|||
} |
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif |
@ -0,0 +1,383 @@ |
|||
#include "grp_driver_dummy.hh" |
|||
#include "pEpLog.hh" |
|||
#include "utils.hh" |
|||
#include "std_utils.hh" |
|||
#include <pEp/message_api.h> |
|||
#include "listmanager_dummy.hh" |
|||
|
|||
using namespace std; |
|||
|
|||
namespace pEp { |
|||
namespace Adapter { |
|||
bool GroupDriverDummy::log_enabled = false; |
|||
|
|||
GroupDriverDummy::GroupDriverDummy(const std::string &db_path) : |
|||
lmd(ListManagerDummy(db_path)) |
|||
{ |
|||
pEpLogClass("called"); |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverDummy::adapter_group_create( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *manager, |
|||
identity_list *memberlist) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
PEP_STATUS status = PEP_UNKNOWN_ERROR; |
|||
|
|||
if (!group_identity || !manager) { |
|||
status = PEP_ILLEGAL_VALUE; |
|||
} else { |
|||
if (Utils::is_c_str_empty(group_identity->address) || |
|||
Utils::is_c_str_empty(manager->address)) { |
|||
status = PEP_ILLEGAL_VALUE; |
|||
} else { |
|||
const string addr_list{ group_identity->address }; |
|||
const string addr_manager{ manager->address }; |
|||
|
|||
try { |
|||
lmd.list_add(addr_list, addr_manager); |
|||
status = PEP_STATUS_OK; |
|||
} catch (const AlreadyExistsException &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_GROUP_EXISTS; |
|||
} catch (const exception &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} catch (...) { |
|||
pEpLogClass("unknown exception"); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} |
|||
|
|||
if (status == PEP_STATUS_OK) { |
|||
// Add the memberlist (if given)
|
|||
// Fail totally on the first member_invite() that fails
|
|||
const vector<pEp_identity *> cxx_memberlist = Utils::to_cxx(*memberlist); |
|||
for (pEp_identity *const member : cxx_memberlist) { |
|||
status = this->adapter_group_invite_member(session, group_identity, member); |
|||
if (status != PEP_STATUS_OK) { |
|||
status = status; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return status; |
|||
} |
|||
|
|||
|
|||
// The engine checks if the manager is correct for the group given
|
|||
// But the list manager does not require that
|
|||
// So, we verify that first, too. using moderator()
|
|||
PEP_STATUS GroupDriverDummy::adapter_group_dissolve( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *manager) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
PEP_STATUS status = PEP_UNKNOWN_ERROR; |
|||
|
|||
if (!group_identity || !manager) { |
|||
status = PEP_ILLEGAL_VALUE; |
|||
} else { |
|||
if (Utils::is_c_str_empty(group_identity->address) || |
|||
Utils::is_c_str_empty(manager->address)) { |
|||
status = PEP_ILLEGAL_VALUE; |
|||
} else { |
|||
const string addr_list{ group_identity->address }; |
|||
const string addr_manager{ manager->address }; |
|||
|
|||
// Check if given manager is correct for the given group
|
|||
string addr_manager_queried; |
|||
try { |
|||
addr_manager_queried = lmd.moderator(addr_list); |
|||
status = PEP_STATUS_OK; |
|||
} catch (const ListDoesNotExistException &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_GROUP_NOT_FOUND; |
|||
} catch (const exception &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} catch (...) { |
|||
pEpLogClass("unknown exception"); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} |
|||
|
|||
if (status == PEP_STATUS_OK) { |
|||
if (addr_manager_queried != addr_manager) { |
|||
status = PEP_CANNOT_DISABLE_GROUP; |
|||
} else { |
|||
try { |
|||
lmd.list_delete(addr_list); |
|||
status = PEP_STATUS_OK; |
|||
} catch (const MemberDoesNotExistException &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
// TODO: Silently succeed???
|
|||
status = PEP_STATUS_OK; |
|||
} catch (const ListDoesNotExistException &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_GROUP_NOT_FOUND; |
|||
} catch (const exception &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} catch (...) { |
|||
pEpLogClass("unknown exception"); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return status; |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverDummy::adapter_group_invite_member( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *group_member) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
|
|||
PEP_STATUS status = PEP_UNKNOWN_ERROR; |
|||
|
|||
if (!group_identity || !group_member) { |
|||
status = PEP_ILLEGAL_VALUE; |
|||
} else { |
|||
if (Utils::is_c_str_empty(group_identity->address) || |
|||
Utils::is_c_str_empty(group_member->address)) { |
|||
status = PEP_ILLEGAL_VALUE; |
|||
} else { |
|||
const string addr_list{ group_identity->address }; |
|||
const string addr_member{ group_member->address }; |
|||
|
|||
try { |
|||
lmd.member_add(addr_list, addr_member); |
|||
status = PEP_STATUS_OK; |
|||
} catch (const AlreadyExistsException &e) { |
|||
// TODO: Silently succeed???
|
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_STATUS_OK; |
|||
} catch (const ListDoesNotExistException &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
return PEP_GROUP_NOT_FOUND; |
|||
} catch (const exception &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} catch (...) { |
|||
pEpLogClass("unknown exception"); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} |
|||
} |
|||
} |
|||
return status; |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverDummy::adapter_group_remove_member( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *group_member) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
PEP_STATUS status = PEP_UNKNOWN_ERROR; |
|||
|
|||
if (!group_identity || !group_member) { |
|||
status = PEP_ILLEGAL_VALUE; |
|||
} else { |
|||
if (Utils::is_c_str_empty(group_identity->address) || |
|||
Utils::is_c_str_empty(group_member->address)) { |
|||
status = PEP_ILLEGAL_VALUE; |
|||
} else { |
|||
const string addr_list{ group_identity->address }; |
|||
const string addr_member{ group_member->address }; |
|||
|
|||
try { |
|||
lmd.member_remove(addr_list, addr_member); |
|||
status = PEP_STATUS_OK; |
|||
} catch (const MemberDoesNotExistException &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
// TODO: Silently succeed???
|
|||
status = PEP_STATUS_OK; |
|||
} catch (const ListDoesNotExistException &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_GROUP_NOT_FOUND; |
|||
} catch (const exception &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} catch (...) { |
|||
pEpLogClass("unknown exception"); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} |
|||
} |
|||
} |
|||
return status; |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverDummy::adapter_group_join( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *as_member) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
// TODO: listmanager member db list_join()
|
|||
// PEP_STATUS status = PEP_UNKNOWN_ERROR;
|
|||
|
|||
// if (!group_identity || !group_member) {
|
|||
// status = PEP_ILLEGAL_VALUE;
|
|||
// } else {
|
|||
// if (Utils::is_c_str_empty(group_identity->address) ||
|
|||
// Utils::is_c_str_empty(group_member->address)) {
|
|||
// status = PEP_ILLEGAL_VALUE;
|
|||
// } else {
|
|||
// const string addr_list{ group_identity->address };
|
|||
// const string addr_member{ group_member->address };
|
|||
//
|
|||
// try {
|
|||
// lmd.member_remove(addr_list, addr_member);
|
|||
// status = PEP_STATUS_OK;
|
|||
// } catch (const MemberDoesNotExistException &e) {
|
|||
// pEpLogClass(Utils::nested_exception_to_string(e));
|
|||
// // TODO: Silently succeed???
|
|||
// status = PEP_STATUS_OK;
|
|||
// } catch (const ListDoesNotExistException &e) {
|
|||
// pEpLogClass(Utils::nested_exception_to_string(e));
|
|||
// status = PEP_GROUP_NOT_FOUND;
|
|||
// } catch (const exception &e) {
|
|||
// pEpLogClass(Utils::nested_exception_to_string(e));
|
|||
// status = PEP_UNKNOWN_ERROR;
|
|||
// } catch (...) {
|
|||
// pEpLogClass("unknown exception");
|
|||
// status = PEP_UNKNOWN_ERROR;
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
return PEP_STATUS_OK; |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverDummy::group_query_groups(PEP_SESSION session, identity_list **groups) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
PEP_STATUS status = PEP_UNKNOWN_ERROR; |
|||
|
|||
if (!session) { |
|||
status = PEP_ILLEGAL_VALUE; |
|||
} else { |
|||
vector<string> lists_queried; |
|||
try { |
|||
lists_queried = lmd.lists(); |
|||
status = PEP_STATUS_OK; |
|||
} catch (const exception &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} catch (...) { |
|||
pEpLogClass("unknown exception"); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} |
|||
|
|||
if (status == PEP_STATUS_OK) { |
|||
::identity_list *idl_groups = ::new_identity_list(nullptr); |
|||
for (const string &addr_list : lists_queried) { |
|||
::pEp_identity *grp_ident = ::new_identity( |
|||
addr_list.c_str(), |
|||
nullptr, |
|||
nullptr, |
|||
nullptr); |
|||
::update_identity(session, grp_ident); |
|||
identity_list_add(idl_groups, grp_ident); |
|||
} |
|||
*groups = idl_groups; |
|||
} |
|||
} |
|||
return status; |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverDummy::group_query_manager( |
|||
PEP_SESSION session, |
|||
const pEp_identity *const group, |
|||
pEp_identity **manager) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
PEP_STATUS status = PEP_UNKNOWN_ERROR; |
|||
|
|||
if (!session || !group) { |
|||
status = PEP_ILLEGAL_VALUE; |
|||
} else { |
|||
const string addr_list{ group->address }; |
|||
string addr_manager{}; |
|||
try { |
|||
addr_manager = lmd.moderator(addr_list); |
|||
status = PEP_STATUS_OK; |
|||
} catch (const ListDoesNotExistException &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_GROUP_NOT_FOUND; |
|||
} catch (const exception &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} catch (...) { |
|||
pEpLogClass("unknown exception"); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} |
|||
|
|||
if (status == PEP_STATUS_OK) { |
|||
::pEp_identity *manager_queried = ::new_identity( |
|||
addr_manager.c_str(), |
|||
nullptr, |
|||
nullptr, |
|||
nullptr); |
|||
::update_identity(session, manager_queried); |
|||
*manager = manager_queried; |
|||
} |
|||
} |
|||
return status; |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverDummy::group_query_members( |
|||
PEP_SESSION session, |
|||
const pEp_identity *const group, |
|||
identity_list **members) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
PEP_STATUS status = PEP_UNKNOWN_ERROR; |
|||
|
|||
if (!session || !group) { |
|||
status = PEP_ILLEGAL_VALUE; |
|||
} else { |
|||
if (Utils::is_c_str_empty(group->address)) { |
|||
status = PEP_ILLEGAL_VALUE; |
|||
} else { |
|||
const string addr_grp{ group->address }; |
|||
vector<string> members_queried; |
|||
try { |
|||
members_queried = lmd.members(addr_grp); |
|||
status = PEP_STATUS_OK; |
|||
} catch (const ListDoesNotExistException &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_GROUP_NOT_FOUND; |
|||
} catch (const exception &e) { |
|||
pEpLogClass(Utils::nested_exception_to_string(e)); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} catch (...) { |
|||
pEpLogClass("unknown exception"); |
|||
status = PEP_UNKNOWN_ERROR; |
|||
} |
|||
|
|||
if (status == PEP_STATUS_OK) { |
|||
::identity_list *idl_members = ::new_identity_list(nullptr); |
|||
for (const string &addr_member : members_queried) { |
|||
::pEp_identity *member_ident = ::new_identity( |
|||
addr_member.c_str(), |
|||
nullptr, |
|||
nullptr, |
|||
nullptr); |
|||
::update_identity(session, member_ident); |
|||
identity_list_add(idl_members, member_ident); |
|||
} |
|||
*members = idl_members; |
|||
} |
|||
} |
|||
} |
|||
return status; |
|||
} |
|||
} // namespace Adapter
|
|||
} // namespace pEp
|
@ -0,0 +1,69 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
#ifndef LIBPEPADAPTER_GRP_DRIVER_DUMMY_HH |
|||
#define LIBPEPADAPTER_GRP_DRIVER_DUMMY_HH |
|||
|
|||
#include "grp_manager_interface.hh" |
|||
#include "pEpLog.hh" |
|||
#include "listmanager_dummy.hh" |
|||
#include <pEp/message_api.h> |
|||
|
|||
namespace pEp { |
|||
namespace Adapter { |
|||
class GroupDriverDummy : public GroupManagerInterface { |
|||
public: |
|||
GroupDriverDummy() = delete; |
|||
explicit GroupDriverDummy(const std::string &db_path); |
|||
|
|||
// GroupUpdateInterface
|
|||
PEP_STATUS adapter_group_create( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *manager, |
|||
::identity_list *memberlist) noexcept override; |
|||
|
|||
PEP_STATUS adapter_group_dissolve( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *manager) noexcept override; |
|||
|
|||
PEP_STATUS adapter_group_invite_member( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *group_member) noexcept override; |
|||
|
|||
PEP_STATUS adapter_group_remove_member( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *group_member) noexcept override; |
|||
|
|||
PEP_STATUS adapter_group_join( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *as_member) noexcept override; |
|||
|
|||
// GroupQueryInterface
|
|||
PEP_STATUS group_query_groups(::PEP_SESSION session, ::identity_list **groups) noexcept override; |
|||
|
|||
PEP_STATUS group_query_manager( |
|||
::PEP_SESSION session, |
|||
const ::pEp_identity *const group, |
|||
::pEp_identity **manager) noexcept override; |
|||
|
|||
PEP_STATUS group_query_members( |
|||
::PEP_SESSION session, |
|||
const ::pEp_identity *const group, |
|||
::identity_list **members) noexcept override; |
|||
|
|||
// Logging
|
|||
static bool log_enabled; |
|||
Adapter::pEpLog::pEpLogger logger{ "GroupDriverDummy", log_enabled }; |
|||
|
|||
private: |
|||
ListManagerDummy lmd; |
|||
Adapter::pEpLog::pEpLogger &m4gic_logger_n4me = logger; |
|||
}; |
|||
} // namespace Adapter
|
|||
} // namespace pEp
|
|||
|
|||
#endif // LIBPEPADAPTER_GRP_DRIVER_DUMMY_HH
|
@ -0,0 +1,63 @@ |
|||
#include "grp_driver_engine.hh" |
|||
#include "pEpLog.hh" |
|||
#include <pEp/message_api.h> |
|||
#include <pEp/group.h> |
|||
|
|||
using namespace std; |
|||
|
|||
namespace pEp { |
|||
namespace Adapter { |
|||
bool GroupDriverEngine::log_enabled = false; |
|||
|
|||
GroupDriverEngine::GroupDriverEngine() |
|||
{ |
|||
pEpLogClass("called"); |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverEngine::adapter_group_create( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *manager, |
|||
identity_list *memberlist) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
return ::group_create(session, group_identity, manager, memberlist, nullptr); |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverEngine::adapter_group_dissolve( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *manager) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
return ::group_dissolve(session, group_identity, manager); |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverEngine::adapter_group_invite_member( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *group_member) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
return ::group_invite_member(session, group_identity, group_member); |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverEngine::adapter_group_remove_member( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *group_member) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
return ::group_remove_member(session, group_identity, group_member); |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverEngine::adapter_group_join( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *as_member) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
return ::group_join(session, group_identity, as_member); |
|||
} |
|||
} // namespace Adapter
|
|||
} // namespace pEp
|
@ -0,0 +1,52 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
#ifndef LIBPEPADAPTER_GRP_DRIVER_ENGINE_HH |
|||
#define LIBPEPADAPTER_GRP_DRIVER_ENGINE_HH |
|||
|
|||
#include "grp_manager_interface.hh" |
|||
#include "pEpLog.hh" |
|||
#include <pEp/message_api.h> |
|||
|
|||
namespace pEp { |
|||
namespace Adapter { |
|||
class GroupDriverEngine : public GroupUpdateInterface { |
|||
public: |
|||
GroupDriverEngine(); |
|||
|
|||
PEP_STATUS adapter_group_create( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *manager, |
|||
::identity_list *memberlist) noexcept override; |
|||
|
|||
PEP_STATUS adapter_group_dissolve( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *manager) noexcept override; |
|||
|
|||
PEP_STATUS adapter_group_invite_member( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *group_member) noexcept override; |
|||
|
|||
PEP_STATUS adapter_group_remove_member( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *group_member) noexcept override; |
|||
|
|||
PEP_STATUS adapter_group_join( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *as_member) noexcept override; |
|||
|
|||
// Logging
|
|||
static bool log_enabled; |
|||
Adapter::pEpLog::pEpLogger logger{ "GroupDriverEngine", log_enabled }; |
|||
|
|||
private: |
|||
Adapter::pEpLog::pEpLogger &m4gic_logger_n4me = logger; |
|||
}; |
|||
} // namespace Adapter
|
|||
} // namespace pEp
|
|||
|
|||
#endif // LIBPEPADAPTER_GRP_DRIVER_ENGINE_HH
|
@ -0,0 +1,267 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
#include "grp_driver_replicator.hh" |
|||
|
|||
using namespace std; |
|||
|
|||
|
|||
namespace pEp { |
|||
namespace Adapter { |
|||
bool GroupDriverReplicator::log_enabled = false; |
|||
|
|||
GroupDriverReplicator::GroupDriverReplicator() |
|||
{ |
|||
pEpLogClass("called"); |
|||
/* const string lm_dummy_db_filename = "listman_dummy.db";
|
|||
#ifdef WIN32 |
|||
const string lm_dummy_db_path = string(::per_user_directory()) + "\\" + |
|||
lm_dummy_db_filename; |
|||
#else |
|||
const string lm_dummy_db_path = string(::per_user_directory()) + "/" + |
|||
lm_dummy_db_filename; |
|||
#endif |
|||
default_repl_src = make_shared<GroupDriverDummy>(lm_dummy_db_path); |
|||
set_replication_source(*default_repl_src); |
|||
|
|||
default_repl_dst = make_shared<GroupDriverEngine>(); |
|||
set_replication_destination(*default_repl_dst); |
|||
*/ |
|||
} |
|||
|
|||
void GroupDriverReplicator::set_replication_source(GroupManagerInterface &src) |
|||
{ |
|||
pEpLogClass("called"); |
|||
repl_src = &src; |
|||
} |
|||
|
|||
void GroupDriverReplicator::set_replication_destination(GroupUpdateInterface &dst) |
|||
{ |
|||
pEpLogClass("called"); |
|||
repl_dst = &dst; |
|||
} |
|||
|
|||
// GroupUpdateInterface
|
|||
PEP_STATUS GroupDriverReplicator::adapter_group_create( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *manager, |
|||
identity_list *memberlist) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
if (!has_repl_src_and_dst()) { |
|||
return PEP_UNKNOWN_ERROR; |
|||
} |
|||
|
|||
// Do listmanager
|
|||
PEP_STATUS status = repl_src->adapter_group_create( |
|||
session, |
|||
group_identity, |
|||
manager, |
|||
memberlist); |
|||
if (status != PEP_STATUS_OK) { |
|||
return status; |
|||
} |
|||
|
|||
// Do engine
|
|||
status = repl_dst->adapter_group_create(session, group_identity, manager, memberlist); |
|||
if (status != PEP_STATUS_OK) { |
|||
// Rollback listman
|
|||
PEP_STATUS rb_stat = repl_src->adapter_group_dissolve(session, group_identity, manager); |
|||
if (rb_stat != PEP_STATUS_OK) { |
|||
//FATAL ERROR ON ROLLBACK
|
|||
status = (PEP_STATUS)-9999; |
|||
} |
|||
} |
|||
return status; |
|||
} |
|||
|
|||
|
|||
PEP_STATUS GroupDriverReplicator::adapter_group_dissolve( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *manager) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
if (!has_repl_src_and_dst()) { |
|||
return PEP_UNKNOWN_ERROR; |
|||
} |
|||
// Do listmanager
|
|||
PEP_STATUS status = repl_src->adapter_group_dissolve(session, group_identity, manager); |
|||
if (status != PEP_STATUS_OK) { |
|||
return status; |
|||
} |
|||
|
|||
// Do engine
|
|||
status = repl_dst->adapter_group_dissolve(session, group_identity, manager); |
|||
if (status != PEP_STATUS_OK) { |
|||
// Rollback listman
|
|||
// TODO: ????
|
|||
// PEP_STATUS rb_stat = gu_listman->adapter_group_dissolve(session, group_identity, manager);
|
|||
// if (rb_stat != PEP_STATUS_OK) {
|
|||
// //FATAL ERROR ON ROLLBACK
|
|||
// status = (PEP_STATUS)-9999;
|
|||
// }
|
|||
} |
|||
return status; |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverReplicator::adapter_group_invite_member( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *group_member) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
if (!has_repl_src_and_dst()) { |
|||
return PEP_UNKNOWN_ERROR; |
|||
} |
|||
|
|||
// Do listmanager
|
|||
PEP_STATUS status = repl_src->adapter_group_invite_member( |
|||
session, |
|||
group_identity, |
|||
group_member); |
|||
if (status != PEP_STATUS_OK) { |
|||
return status; |
|||
} |
|||
|
|||
// Do engine
|
|||
status = repl_dst->adapter_group_invite_member(session, group_identity, group_member); |
|||
if (status != PEP_STATUS_OK) { |
|||
// Rollback
|
|||
PEP_STATUS rb_stat = repl_src->adapter_group_remove_member( |
|||
session, |
|||
group_identity, |
|||
group_member); |
|||
if (rb_stat != PEP_STATUS_OK) { |
|||
//FATAL ERROR ON ROLLBACK
|
|||
status = (PEP_STATUS)-9999; |
|||
} |
|||
} |
|||
return status; |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverReplicator::adapter_group_remove_member( |
|||
PEP_SESSION session, |
|||
pEp_identity *group_identity, |
|||
pEp_identity *group_member) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
if (!has_repl_src_and_dst()) { |
|||
return PEP_UNKNOWN_ERROR; |
|||
} |
|||
|
|||
// Do listmanager
|
|||
PEP_STATUS status = repl_src->adapter_group_remove_member( |
|||
session, |
|||
group_identity, |
|||
group_member); |
|||
if (status != PEP_STATUS_OK) { |
|||
return status; |
|||
} |
|||
|
|||
// Do engine
|
|||
status = repl_dst->adapter_group_remove_member(session, group_identity, group_member); |
|||
if (status != PEP_STATUS_OK) { |
|||
// Rollback
|
|||
PEP_STATUS rb_stat = repl_src->adapter_group_invite_member( |
|||
session, |
|||
group_identity, |
|||
group_member); |
|||
if (rb_stat != PEP_STATUS_OK) { |
|||
//FATAL ERROR ON ROLLBACK
|
|||
status = (PEP_STATUS)-9999; |
|||
} |
|||
} |
|||
return status; |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverReplicator::adapter_group_join( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *as_member) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
if (!has_repl_src_and_dst()) { |
|||
return PEP_UNKNOWN_ERROR; |
|||
} |
|||
|
|||
// Do listmanager
|
|||
PEP_STATUS status = repl_src->adapter_group_join( |
|||
session, |
|||
group_identity, |
|||
as_member); |
|||
if (status != PEP_STATUS_OK) { |
|||
return status; |
|||
} |
|||
|
|||
// Do engine
|
|||
status = repl_dst->adapter_group_join(session, group_identity, as_member); |
|||
if (status != PEP_STATUS_OK) { |
|||
// Rollback
|
|||
// TODO: need group_leave
|
|||
// PEP_STATUS rb_stat = repl_src->adapter_group(
|
|||
// session,
|
|||
// group_identity,
|
|||
// as_member);
|
|||
// if (rb_stat != PEP_STATUS_OK) {
|
|||
// //FATAL ERROR ON ROLLBACK
|
|||
// status = (PEP_STATUS)-9999;
|
|||
// }
|
|||
} |
|||
return status; |
|||
} |
|||
|
|||
// GroupQueryInterface
|
|||
PEP_STATUS GroupDriverReplicator::group_query_groups( |
|||
PEP_SESSION session, |
|||
identity_list **groups) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
if (!has_repl_src_and_dst()) { |
|||
return PEP_UNKNOWN_ERROR; |
|||
} |
|||
|
|||
return repl_src->group_query_groups(session, groups); |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverReplicator::group_query_manager( |
|||
PEP_SESSION session, |
|||
const pEp_identity *const group, |
|||
pEp_identity **manager) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
if (!has_repl_src_and_dst()) { |
|||
return PEP_UNKNOWN_ERROR; |
|||
} |
|||
|
|||
return repl_src->group_query_manager(session, group, manager); |
|||
} |
|||
|
|||
PEP_STATUS GroupDriverReplicator::group_query_members( |
|||
PEP_SESSION session, |
|||
const pEp_identity *const group, |
|||
identity_list **members) noexcept |
|||
{ |
|||
pEpLogClass("called"); |
|||
if (!has_repl_src_and_dst()) { |
|||
return PEP_UNKNOWN_ERROR; |
|||
} |
|||
|
|||
return repl_src->group_query_members(session, group, members); |
|||
} |
|||
|
|||
bool GroupDriverReplicator::has_repl_src_and_dst() |
|||
{ |
|||
bool ret = true; |
|||
if (!repl_src) { |
|||
ret = false; |
|||
pEpLogClass("Abort: no replication source (listmanager)"); |
|||
} |
|||
if (!repl_dst) { |
|||
ret = false; |
|||
pEpLogClass("Abort: no replication destination (pEpEngine)"); |
|||
} |
|||
return ret; |
|||
} |
|||
} // namespace Adapter
|
|||
} // namespace pEp
|
@ -0,0 +1,83 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
#ifndef LIBPEPADAPTER_GRP_DRIVER_REPLICATOR_HH |
|||
#define LIBPEPADAPTER_GRP_DRIVER_REPLICATOR_HH |
|||
|
|||
#include "grp_manager_interface.hh" |
|||
#include "grp_driver_engine.hh" |
|||
#include "grp_driver_dummy.hh" |
|||
#include "pEpLog.hh" |
|||
#include <pEp/message_api.h> |
|||
|
|||
namespace pEp { |
|||
namespace Adapter { |
|||
class GroupDriverReplicator : public GroupManagerInterface { |
|||
public: |
|||
GroupDriverReplicator(); |
|||
|
|||
void set_replication_source(GroupManagerInterface &src); |
|||
void set_replication_destination(GroupUpdateInterface &dst); |
|||
|
|||
// GroupUpdateInterface
|
|||
PEP_STATUS adapter_group_create( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *manager, |
|||
::identity_list *memberlist) noexcept override; |
|||
|
|||
PEP_STATUS adapter_group_dissolve( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *manager) noexcept override; |
|||
|
|||
PEP_STATUS adapter_group_invite_member( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *group_member) noexcept override; |
|||
|
|||
PEP_STATUS adapter_group_remove_member( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *group_member) noexcept override; |
|||
|
|||
PEP_STATUS adapter_group_join( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *as_member) noexcept override; |
|||
|
|||
// GroupQueryInterface
|
|||
PEP_STATUS group_query_groups(::PEP_SESSION session, ::identity_list **groups) noexcept override; |
|||
|
|||
PEP_STATUS group_query_manager( |
|||
::PEP_SESSION session, |
|||
const ::pEp_identity *const group, |
|||
::pEp_identity **manager) noexcept override; |
|||
|
|||
PEP_STATUS group_query_members( |
|||
::PEP_SESSION session, |
|||
const ::pEp_identity *const group, |
|||
::identity_list **members) noexcept override; |
|||
|
|||
// Logging
|
|||
static bool log_enabled; |
|||
Adapter::pEpLog::pEpLogger logger{ "GroupDriverReplicator", log_enabled }; |
|||
|
|||
private: |
|||
// Group replication roles
|
|||
// Default replication source and destination
|
|||
std::shared_ptr<GroupManagerInterface> default_repl_src; |
|||
std::shared_ptr<GroupUpdateInterface> default_repl_dst; |
|||
|
|||
// Current replication source and destination
|
|||
GroupManagerInterface *repl_src = nullptr; // Source needs full interface
|
|||
GroupUpdateInterface *repl_dst = nullptr; // Destination needs update interface only
|
|||
|
|||
// Helpers
|
|||
bool has_repl_src_and_dst(); |
|||
// Logging
|
|||
Adapter::pEpLog::pEpLogger &m4gic_logger_n4me = logger; |
|||
}; |
|||
} // namespace Adapter
|
|||
} // namespace pEp
|
|||
|
|||
#endif // LIBPEPADAPTER_GRP_DRIVER_REPLICATOR_HH
|
@ -0,0 +1,66 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef LIBPEPADAPTER_GRP_MANAGER_INTERFACE_HH |
|||
#define LIBPEPADAPTER_GRP_MANAGER_INTERFACE_HH |
|||
|
|||
#include <pEp/message_api.h> |
|||
|
|||
namespace pEp { |
|||
namespace Adapter { |
|||
class GroupUpdateInterface { |
|||
public: |
|||
virtual PEP_STATUS adapter_group_create( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *manager, |
|||
::identity_list *memberlist) noexcept = 0; |
|||
|
|||
virtual PEP_STATUS adapter_group_dissolve( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *manager) noexcept = 0; |
|||
|
|||
virtual PEP_STATUS adapter_group_invite_member( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *group_member) noexcept = 0; |
|||
|
|||
virtual PEP_STATUS adapter_group_remove_member( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *group_member) noexcept = 0; |
|||
|
|||
virtual PEP_STATUS adapter_group_join( |
|||
::PEP_SESSION session, |
|||
::pEp_identity *group_identity, |
|||
::pEp_identity *as_member) noexcept = 0; |
|||
|
|||
private: |
|||
}; |
|||
|
|||
class GroupQueryInterface { |
|||
public: |
|||
virtual PEP_STATUS group_query_groups( |
|||
::PEP_SESSION session, |
|||
::identity_list **groups) noexcept = 0; |
|||
|
|||
virtual PEP_STATUS group_query_manager( |
|||
::PEP_SESSION session, |
|||
const ::pEp_identity *const group, |
|||
::pEp_identity **manager) noexcept = 0; |
|||
|
|||
virtual PEP_STATUS group_query_members( |
|||
::PEP_SESSION session, |
|||
const ::pEp_identity *const group, |
|||
::identity_list **members) noexcept = 0; |
|||
|
|||
private: |
|||
}; |
|||
|
|||
class GroupManagerInterface : public GroupUpdateInterface, public GroupQueryInterface { |
|||
}; |
|||
} // namespace Adapter
|
|||
} // namespace pEp
|
|||
|
|||
#endif // LIBPEPADAPTER_GRP_MANAGER_INTERFACE_HH
|
File diff suppressed because it is too large
@ -0,0 +1,367 @@ |
|||
#include "listmanager_dummy.hh" |
|||
#include "pEpSQLite.hh" |
|||
#include <exception> |
|||
|
|||
using namespace std; |
|||
|
|||
namespace pEp { |
|||
bool ListManagerDummy::log_enabled = false; |
|||
|
|||
// public
|
|||
ListManagerDummy::ListManagerDummy(const string& db_path) : db(pEpSQLite(db_path)) |
|||
{ |
|||
pEpLogClass("called"); |
|||
} |
|||
|
|||
// private
|
|||
void ListManagerDummy::ensure_db_initialized() |
|||
{ |
|||
if (!db.is_open()) { |
|||
is_db_initialized = false; |
|||
try { |
|||
db.create_or_open_db(); |
|||
} catch (...) { |
|||
db.close_db(); |
|||
DBException e{ "ListManagerDummy - error opening db" }; |
|||
throw_with_nested(e); |
|||
} |
|||
} |
|||
if (!is_db_initialized) { |
|||
try { |
|||
db_config(); |
|||
create_tables(); |
|||
} catch (...) { |
|||
db.close_db(); |
|||
DBException e{ "ListManagerDummy - db init failed" }; |
|||
throw_with_nested(e); |
|||
} |
|||
is_db_initialized = true; |
|||
} |
|||
} |
|||
|
|||
// private
|
|||
void ListManagerDummy::db_config() |
|||
{ |
|||
try { |
|||
string sql; |
|||
sql = "PRAGMA foreign_keys=ON"; |
|||
db.execute(sql); |
|||
} catch (...) { |
|||
DBException e{ "ListManagerDummy - db config failed" }; |
|||
throw_with_nested(e); |
|||
} |
|||
} |
|||
|
|||
// private
|
|||
void ListManagerDummy::create_tables() |
|||
{ |
|||
try { |
|||
string sql; |
|||
|
|||
sql = "CREATE TABLE IF NOT EXISTS lists(" |
|||
"address TEXT NOT NULL," |
|||
"moderator_address TEXT NOT NULL," |
|||
"PRIMARY KEY(address));"; |
|||
db.execute(sql); |
|||
|
|||
sql = "CREATE TABLE IF NOT EXISTS member_of(" |
|||
"address TEXT NOT NULL," |
|||
"list_address TEXT NOT NULL," |
|||
"PRIMARY KEY (address, list_address)," |
|||
"FOREIGN KEY(list_address) REFERENCES lists(address) ON DELETE CASCADE);"; |
|||
db.execute(sql); |
|||
} catch (...) { |
|||
DBException e("ListManagerDummy - create tables failed"); |
|||
throw_with_nested(e); |
|||
} |
|||
} |
|||
|
|||
// public
|
|||
void ListManagerDummy::close_db() |
|||
{ |
|||
pEpLogClass("called"); |
|||
db.close_db(); |
|||
} |
|||
|
|||
// public
|
|||
void ListManagerDummy::delete_db() |
|||
{ |
|||
pEpLogClass("called"); |
|||
try { |
|||
db.delete_db(); |
|||
} catch (...) { |
|||
DBException e{ "ListManagerDummy: delete_db() failed" }; |
|||
throw_with_nested(e); |
|||
} |
|||
} |
|||
|
|||
// public
|
|||
void ListManagerDummy::list_add(const std::string& addr_list, const std::string& addr_mgr) |
|||
{ |
|||
pEpLogClass("list_add(addr_list: \"" + addr_list + "\", addr_mgr: \"" + addr_mgr + "\")"); |
|||
if (list_exists(addr_list)) { |
|||
AlreadyExistsException e{ "list_add(addr_list: \"" + addr_list + "\", addr_mgr: \"" + |
|||
addr_mgr + "\") - List already exists" }; |
|||
throw e; |
|||
} |
|||
|
|||
try { |
|||
string sql = "INSERT INTO lists(address, moderator_address) VALUES ('" + addr_list + |
|||
"','" + addr_mgr + "');"; |
|||
|
|||
db.execute(sql); |
|||
} catch (...) { |
|||
DBException e{ "ListManagerDummy: list_add(addr_list: \"" + addr_list + |
|||
"\"\taddr_mgr: \"" + addr_mgr + "\") - failed with exception" }; |
|||
throw_with_nested(e); |
|||
} |
|||
} |
|||
|
|||
// public
|
|||
void ListManagerDummy::list_delete(const std::string& addr_list) |
|||
{ |
|||
pEpLogClass("list_delete(addr_list: \"" + addr_list + "\")"); |
|||
if (!list_exists(addr_list)) { |
|||
ListDoesNotExistException e{ "list_delete(addr_list: \"" + addr_list + |
|||
"\") - List does not exist" }; |
|||
throw e; |
|||
} |
|||
|
|||
try { |
|||
string sql; |
|||
sql = "DELETE FROM lists WHERE lists.address = '" + addr_list + "';"; |
|||
db.execute(sql); |
|||
} catch (...) { |
|||
DBException e{ "ListManagerDummy: list_delete(addr_list: \"" + addr_list + |
|||
"\") - failed with exception" }; |
|||
throw_with_nested(e); |
|||
} |
|||
} |
|||
|
|||
// public
|
|||
void ListManagerDummy::member_add(const std::string& addr_list, const std::string& addr_member) |
|||
{ |
|||
pEpLogClass( |
|||
"member_add(addr_list: \"" + addr_list + "\", addr_member: \"" + addr_member + "\")"); |
|||
if (!list_exists(addr_list)) { |
|||
ListDoesNotExistException e{ "member_add(addr_list: \"" + addr_list + "\", addr_member: \"" + |
|||
addr_member + "\") - list does not exist" }; |
|||
throw e; |
|||
} |
|||
|
|||
if (member_exists(addr_list, addr_member)) { |
|||
AlreadyExistsException e{ "member_add(addr_list: \"" + addr_list + "\", addr_member: \"" + |
|||
addr_member + "\") - member already exists" }; |
|||
throw e; |
|||
} |
|||
|
|||
try { |
|||
string sql = "INSERT INTO member_of(address, list_address) VALUES ('" + addr_member + |
|||
"', '" + addr_list + "');"; |
|||
db.execute(sql); |
|||
} catch (...) { |
|||
DBException e{ "ListManagerDummy: member_add(addr_list: \"" + addr_list + |
|||
"\", addr_member: \"" + addr_member + "\") - failed with exception" }; |
|||
throw_with_nested(e); |
|||
} |
|||
} |
|||
|
|||
// public
|
|||
void ListManagerDummy::member_remove(const std::string& addr_list, const std::string& addr_member) |
|||
{ |
|||
pEpLogClass( |
|||
"member_remove(addr_list: \"" + addr_list + "\", addr_member: '\"" + addr_member + "\")"); |
|||
if (!list_exists(addr_list)) { |
|||
ListDoesNotExistException e{ "member_remove(addr_list: \"" + addr_list + |
|||
"\", addr_member: '\"" + addr_member + |
|||
"\") - list does not exist" }; |
|||
throw e; |
|||
} |
|||
|
|||
if (!member_exists(addr_list, addr_member)) { |
|||
MemberDoesNotExistException e{ "member_remove(addr_list: \"" + addr_list + |
|||
"\", addr_member: '\"" + addr_member + |
|||
"\") - member does not exist" }; |
|||
throw e; |
|||
} |
|||
|
|||
try { |
|||
string sql; |
|||
sql = "DELETE FROM member_of WHERE (member_of.address = '" + addr_member + |
|||
"') AND (member_of.list_address = '" + addr_list + "');"; |
|||
db.execute(sql); |
|||
} catch (...) { |
|||
DBException e{ "ListManagerDummy: member_remove(" + addr_list + ", " + addr_member + |
|||
") - failed with exception" }; |
|||
throw_with_nested(e); |
|||
} |
|||
} |
|||
|
|||
// public
|
|||
std::vector<std::string> ListManagerDummy::lists() |
|||
{ |
|||
pEpLogClass("called"); |
|||
ensure_db_initialized(); |
|||
vector<string> ret; |
|||
ResultSet rs; |
|||
|
|||
try { |
|||
string sql; |
|||
sql = "SELECT address FROM lists"; |
|||
rs = db.execute(sql); |
|||
} catch (...) { |
|||
DBException e{ "ListManagerDummy: lists() failed" }; |
|||
throw_with_nested(e); |
|||
} |
|||
|
|||
for (const RSRecord& rec : rs) { |
|||
ret.push_back(rec.at("address")); |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
// public
|
|||
std::string ListManagerDummy::moderator(const std::string& addr_list) |
|||
{ |
|||
pEpLogClass("moderator(list_address:\"" + addr_list + "\")"); |
|||
if (!list_exists(addr_list)) { |
|||
ListDoesNotExistException e{ "moderator(list_address:\"" + addr_list + |
|||
"\") - List does not exist" }; |
|||
throw e; |
|||
} |
|||
|
|||
string ret; |
|||
ResultSet rs; |
|||
|
|||
try { |
|||
string sql; |
|||
sql = "SELECT moderator_address FROM lists " |
|||
"WHERE lists.address = '" + |
|||
addr_list + "';"; |
|||
rs = db.execute(sql); |
|||
} catch (...) { |
|||
DBException e{ "ListManagerDummy: moderator(list_address:\"" + addr_list + |
|||
"\") - failed" }; |
|||
throw_with_nested(e); |
|||
} |
|||
|
|||
if (!rs.empty()) { |
|||
for (const RSRecord& rec : rs) { |
|||
ret = rec.at("moderator_address"); |
|||
} |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
// public
|
|||
std::vector<std::string> ListManagerDummy::members(const std::string& addr_list) |
|||
{ |
|||
pEpLogClass("members(list_address:\"" + addr_list + "\")"); |
|||
if (!list_exists(addr_list)) { |
|||
ListDoesNotExistException e{ "members(list_address:\"" + addr_list + |
|||
"\") - List does not exist" }; |
|||
throw e; |
|||
} |
|||
|
|||
vector<string> ret; |
|||
ResultSet rs; |
|||
|
|||
try { |
|||
string sql; |
|||
sql = "SELECT address FROM member_of " |
|||
"WHERE list_address = '" + |
|||
addr_list + "'"; |
|||
rs = db.execute(sql); |
|||
} catch (...) { |
|||
DBException e{ "ListManagerDummy: members(list_address:\"" + addr_list + "\")" }; |
|||
throw_with_nested(e); |
|||
} |
|||
|
|||
if (!rs.empty()) { |
|||
for (const RSRecord& rec : rs) { |
|||
ret.push_back(rec.at("address")); |
|||
} |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
bool ListManagerDummy::list_exists(const std::string& addr_list) |
|||
{ |
|||
pEpLogClass("list_exists(addr_list:\"" + addr_list + "\")"); |
|||
bool ret{ false }; |
|||
ensure_db_initialized(); |
|||
|
|||
ResultSet rs; |
|||
int rescount = 0; |
|||
try { |
|||
string sql; |
|||
sql = "SELECT COUNT(address) AS rescount " |
|||
"FROM lists " |
|||
"WHERE address = '" + |
|||
addr_list + "';"; |
|||
rs = db.execute(sql); |
|||
rescount = pEpSQLite::eval_sql_count(rs, "rescount"); |
|||
} catch (...) { |
|||
DBException e{ "ListManagerDummy: list_exists(addr_list:\"" + addr_list + "\")" }; |
|||
throw_with_nested(e); |
|||
} |
|||
// Check FATAL inconsistency
|
|||
if (rescount > 1) { |
|||
DBException e{ "ListManagerDummy: list_exists(addr_list:\"" + addr_list + |
|||
"\") - FATAL DB CONSTRAINT ERROR: list exists more than once" }; |
|||
throw_with_nested(e); |
|||
} |
|||
|
|||
if (rescount == 1) { |
|||
ret = true; |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
bool ListManagerDummy::member_exists(const std::string& addr_list, const std::string& addr_member) |
|||
{ |
|||
pEpLogClass( |
|||
"member_exists(addr_list:\"" + addr_list + "\", addr_member:\"" + addr_member + "\")"); |
|||
bool ret{ false }; |
|||
ensure_db_initialized(); |
|||
|
|||
ResultSet rs; |
|||
int rescount = 0; |
|||
try { |
|||
string sql; |
|||
sql = "SELECT COUNT(address) AS rescount " |
|||
"FROM member_of " |
|||
"WHERE (address = '" + |
|||
addr_member + "' AND list_address = '" + addr_list + "');"; |
|||
rs = db.execute(sql); |
|||
rescount = pEpSQLite::eval_sql_count(rs, "rescount"); |
|||
} catch (...) { |
|||
DBException e{ "member_exists(addr_list:\"" + addr_list + "\", addr_member:\"" + |
|||
addr_member + "\")" }; |
|||
throw_with_nested(e); |
|||
} |
|||
// Check FATAL inconsistency
|
|||
if (rescount > 1) { |
|||
DBException e{ "member_exists(addr_list:\"" + addr_list + "\", addr_member:\"" + |
|||
addr_member + |
|||
"\") - FATAL DB CONSTRAINT ERROR: list exists more than once" }; |
|||
throw_with_nested(e); |
|||
} |
|||
|
|||
if (rescount == 1) { |
|||
ret = true; |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
|
|||
// public
|
|||
ListManagerDummy::~ListManagerDummy() |
|||
{ |
|||
pEpLogClass("called"); |
|||
db.close_db(); |
|||
} |
|||
} // namespace pEp
|
@ -0,0 +1,63 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef LIBPEPADAPTER_LISTMANAGER_DUMMY_HH |
|||
#define LIBPEPADAPTER_LISTMANAGER_DUMMY_HH |
|||
|
|||
#include "pEpSQLite.hh" |
|||
#include "pEpLog.hh" |
|||
#include <vector> |
|||
#include <string> |
|||
|
|||
namespace pEp { |
|||
class ListManagerDummy { |
|||
public: |
|||
ListManagerDummy() = delete; |
|||
explicit ListManagerDummy(const std::string& db_path); |
|||
// Update
|
|||
void list_add(const std::string& addr_list, const std::string& addr_mgr); |
|||
void list_delete(const std::string& addr_list); |
|||
void member_add(const std::string& addr_list, const std::string& addr_member); |
|||
void member_remove(const std::string& addr_list, const std::string& addr_member); |
|||
// Query
|
|||
std::vector<std::string> lists(); |
|||
std::string moderator(const std::string& addr_list); |
|||
std::vector<std::string> members(const std::string& addr_list); |
|||
bool list_exists(const std::string& addr_list); |
|||
bool member_exists(const std::string& addr_list, const std::string& addr_member); |
|||
// db
|
|||
void close_db(); |
|||
void delete_db(); |
|||
// Logging
|
|||
static bool log_enabled; |
|||
Adapter::pEpLog::pEpLogger logger{ "ListManagerDummy", log_enabled }; |
|||
~ListManagerDummy(); |
|||
|
|||
private: |
|||
pEpSQLite db; |
|||
bool is_db_initialized{ false }; |
|||
void ensure_db_initialized(); |
|||
void db_config(); |
|||
void create_tables(); |
|||
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; |
|||
}; |
|||
|
|||
class DBException : public std::runtime_error { |
|||
public: |
|||
explicit DBException(const std::string& string) : runtime_error(string) {} |
|||
}; |
|||
class ListDoesNotExistException : public std::runtime_error { |
|||
public: |
|||
explicit ListDoesNotExistException(const std::string& string) : runtime_error(string) {} |
|||
}; |
|||
class MemberDoesNotExistException : public std::runtime_error { |
|||
public: |
|||
explicit MemberDoesNotExistException(const std::string& string) : runtime_error(string) {} |
|||
}; |
|||
class AlreadyExistsException : public std::runtime_error { |
|||
public: |
|||
explicit AlreadyExistsException(const std::string& string) : runtime_error(string) {} |
|||
}; |
|||
} // namespace pEp
|
|||
|
|||
#endif // LIBPEPADAPTER_LISTMANAGER_DUMMY_HH
|
@ -0,0 +1,167 @@ |
|||
#include "pEpSQLite.hh" |
|||
#include "pEpLog.hh" |
|||
#include <iostream> |
|||
#include <cstdio> |
|||
#include <stdexcept> |
|||
#include <string> |
|||
#include <cstring> |
|||
|
|||
using namespace std; |
|||
|
|||
namespace pEp { |
|||
bool pEpSQLite::log_enabled = false; |
|||
|
|||
pEpSQLite::pEpSQLite(const std::string &db_path) : db_path(db_path) |
|||
{ |
|||
pEpLogClass("called with db_path: " + db_path + ""); |
|||
} |
|||
|
|||
void pEpSQLite::create_or_open_db() |
|||
{ |
|||
pEpLogClass("called"); |
|||
int rc{ ::sqlite3_open(db_path.c_str(), &db) }; |
|||
|
|||
if (rc) { |
|||
runtime_error e{ "pEpSQLite: create_or_open_db(\"" + db_path + |
|||
"\") - failed with sqlite3 error: " + std::to_string(rc) + " - " + |
|||
::sqlite3_errmsg(db) }; |
|||
close_db(); |
|||
throw(e); |
|||
} |
|||
} |
|||
|
|||
string pEpSQLite::get_db_path() const |
|||
{ |
|||
pEpLogClass("called"); |
|||
return db_path; |
|||
} |
|||
|
|||
void pEpSQLite::close_db() |
|||
{ |
|||
pEpLogClass("called"); |
|||
if (db != nullptr) { |
|||
::sqlite3_close(db); |
|||
db = nullptr; |
|||
} |
|||
} |
|||
|
|||
bool pEpSQLite::is_open() const |
|||
{ |
|||
if (db == nullptr) { |
|||
return false; |
|||
} else { |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
|
|||
void pEpSQLite::delete_db() |
|||
{ |
|||
pEpLogClass("called"); |
|||
close_db(); |
|||
int status = remove(db_path.c_str()); |
|||
if (status) { |
|||
runtime_error e{ "pEpSQLite: delete_db(\"" + db_path + "\"): failed with error: " + |
|||
std::to_string(status) + " - " + strerror(errno) }; |
|||
throw(e); |
|||
} |
|||
} |
|||
|
|||
int pEpSQLite::callback(void *obj, int argc, char **argv, char **azColName) |
|||
{ |
|||
RSRecord record; |
|||
for (int col = 0; col < argc; col++) { |
|||
const string key = string{ azColName[col] }; |
|||
// TODO: NULL is not correct, could be a valid value
|
|||
const string val = string{ argv[col] ? argv[col] : "NULL" }; |
|||
record.insert({ key, val }); |
|||
} |
|||
(static_cast<pEpSQLite *>(obj))->resultset.push_back(record); |
|||
return 0; |
|||
} |
|||
|
|||
ResultSet pEpSQLite::execute(const string &stmt) |
|||
{ |
|||
if (!is_open()) { |
|||
DBNotOpenException e{ "pEpSQLite: execute() failed - db is not open:" }; |
|||
throw(e); |
|||
} else { |
|||
pEpLogClass("called"); |
|||
this->resultset.clear(); |
|||
char *zErrMsg = nullptr; |
|||
int rc = ::sqlite3_exec( |
|||
db, |
|||
stmt.c_str(), |
|||
(int (*)(void *, int, char **, char **)) & callback, |
|||
this, |
|||
&zErrMsg); |
|||
if (rc != SQLITE_OK) { |
|||
if (rc == SQLITE_CONSTRAINT) { |
|||
ConstraintException e{ "pEpSQLite: execute() failed with sqlite error: " + |
|||
std::to_string(rc) + " - " + string(zErrMsg) }; |
|||
::sqlite3_free(zErrMsg); |
|||
throw(e); |
|||
} |
|||
runtime_error e{ "pEpSQLite: execute() failed with sqlite error: " + |
|||
std::to_string(rc) + " - " + string(zErrMsg) }; |
|||
::sqlite3_free(zErrMsg); |
|||
throw(e); |
|||
} |
|||
} |
|||
return resultset; |
|||
} |
|||
|
|||
// Utils
|
|||
string pEpSQLite::to_string(const RSRecord &rec) |
|||
{ |
|||
stringstream ss; |
|||
for (const auto &col : rec) { |
|||
ss << "[\"" << col.first << "\"] = \"" << col.second << "\"" << endl; |
|||
} |
|||
return ss.str(); |
|||
} |
|||
|
|||
string pEpSQLite::to_string(const ResultSet &rs) |
|||
{ |
|||
stringstream ss; |
|||
ss << "ROWCOUNT: " << rs.size() << endl; |
|||
int i = 0; |
|||
for (const RSRecord &rec : rs) { |
|||
ss << "ROW[" << i << "]" << endl << to_string(rec); |
|||
i++; |
|||
} |
|||
return ss.str(); |
|||
} |
|||
|
|||
//Helper
|
|||
int pEpSQLite::eval_sql_count(const ResultSet& rs, const string& countfieldname) |
|||
{ |
|||
int rescount = 0; |
|||
// Get row
|
|||
RSRecord rec{}; |
|||
if (rs.size() != 1) { |
|||
runtime_error e{ "ListManagerDummy: eval_sql_count() - row count != 1" }; |
|||
throw_with_nested(e); |
|||
} |
|||
try { |
|||
rec = rs.at(0); |
|||
} catch (...) { |
|||
runtime_error e{ "ListManagerDummy: eval_sql_count() - cant get row nr 0" }; |
|||
throw_with_nested(e); |
|||
} |
|||
// Get field
|
|||
try { |
|||
rescount = stoi(rec.at(countfieldname)); |
|||
} catch (...) { |
|||
runtime_error e{ "ListManagerDummy: eval_sql_count() - field not existing" }; |
|||
throw_with_nested(e); |
|||
} |
|||
return rescount; |
|||
} |
|||
|
|||
pEpSQLite::~pEpSQLite() |
|||
{ |
|||
pEpLogClass("called"); |
|||
close_db(); |
|||
} |
|||
} // namespace pEp
|
@ -0,0 +1,64 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
#ifndef LIBPEPADAPTER_PEPSQLITE_HH |
|||
#define LIBPEPADAPTER_PEPSQLITE_HH |
|||
|
|||
#include "internal/sqlite3.h" |
|||
#include "pEpLog.hh" |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
|
|||
|
|||
using ResultSet = std::vector<std::map<std::string, std::string>>; |
|||
using RSRecord = std::map<std::string, std::string>; |
|||
|
|||
namespace pEp { |
|||
class pEpSQLite { |
|||
public: |
|||
pEpSQLite() = delete; |
|||
|
|||
// The database file as a constant for the obj lifetime
|
|||
explicit pEpSQLite(const std::string& db_path); |
|||
std::string get_db_path() const; |
|||
|
|||
// Creates the database file not existsing
|
|||
// Will not create any dirs
|
|||
void create_or_open_db(); |
|||
void close_db(); |
|||
bool is_open() const; |
|||
|
|||
// Delete the database file
|
|||
void delete_db(); |
|||
ResultSet execute(const std::string& stmt); |
|||
|
|||
// Utils
|
|||
static std::string to_string(const RSRecord& rec); |
|||
static std::string to_string(const ResultSet& rs); |
|||
static int eval_sql_count(const ResultSet& rs, const std::string& countfieldname); |
|||
|
|||
// Logging
|
|||
static bool log_enabled; |
|||
Adapter::pEpLog::pEpLogger logger{ "pEpSQLite", log_enabled }; |
|||
~pEpSQLite(); |
|||
|
|||
private: |
|||
::sqlite3* db = nullptr; |
|||
std::string db_path; |
|||
ResultSet resultset; |
|||
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; |
|||
static int callback(void* obj, int argc, char** argv, char** azColName); |
|||
}; |
|||
|
|||
class DBNotOpenException : public std::runtime_error { |
|||
public: |
|||
DBNotOpenException(const std::string& string) : runtime_error(string) {} |
|||
}; |
|||
|
|||
class ConstraintException : public std::runtime_error { |
|||
public: |
|||
ConstraintException(const std::string& string) : runtime_error(string) {} |
|||
}; |
|||
} // namespace pEp
|
|||
|
|||
#endif // LIBPEPADAPTER_PEPSQLITE_HH
|
File diff suppressed because it is too large
@ -0,0 +1,297 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#include "std_utils.hh" |
|||
#include <iostream> |
|||
#include <fstream> |
|||
#include <cstdio> |
|||
#include <cerrno> |
|||
#include <cmath> |
|||
#include <algorithm> |
|||
#include <thread> |
|||
#include <random> |
|||
#include <cstring> |
|||
#ifndef WIN32 |
|||
#include <dirent.h> |
|||
#include <sys/stat.h> |
|||
#endif |
|||
|
|||
using namespace std; |
|||
using namespace pEp; |
|||
|
|||
namespace pEp { |
|||
namespace Utils { |
|||
bool is_c_str_empty(const char *str) |
|||
{ |
|||
if (str == nullptr) { |
|||
return true; |
|||
} |
|||
string tmp{ str }; |
|||
if (tmp.empty()) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
string nested_exception_to_string(const exception &e, int level, string src) |
|||
{ |
|||
src += string(level, ' ') + "exception: " + e.what() + "\n"; |
|||
try { |
|||
rethrow_if_nested(e); |
|||
} catch (const exception &e) { |
|||
src = nested_exception_to_string(e, level + 1, src); |
|||
} catch (...) { |
|||
} |
|||
return src; |
|||
} |
|||
|
|||
// File utils
|
|||
bool path_exists(const string &filename) |
|||
{ |
|||
ifstream ifile(filename.c_str()); |
|||
return (bool)ifile; |
|||
} |
|||
|
|||
void path_delete(const string &filename) |
|||
{ |
|||
int status = remove(filename.c_str()); |
|||
if (status) { |
|||
runtime_error e{ string("path_delete(\"" + filename + "\") - " + strerror(errno)) }; |
|||
throw(e); |
|||
} |
|||
} |
|||
|
|||
#ifndef WIN32 |
|||
bool path_is_dir(const string &path) |
|||
{ |
|||
bool ret = false; |
|||
struct stat statbuf; |
|||
if (stat(path.c_str(), &statbuf) != 0) { |
|||
runtime_error e{ "path_is_dir(\"" + path + "\") - " + strerror(errno) }; |
|||
throw(e); |
|||
} |
|||
if (S_ISDIR(statbuf.st_mode)) { |
|||
ret = true; |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
void path_delete_all(const string &path) |
|||
{ |
|||
try { |
|||
if (!path_is_dir(path)) { |
|||
path_delete(path); |
|||
} else { |
|||
vector<string> dirlist = dir_list_all(path); |
|||
if (dirlist.empty()) { |
|||
path_delete(path); |
|||
} else { |
|||
for (const string &filename : dirlist) { |
|||
string newpath = path + "/" + filename; |
|||
path_delete_all(newpath); |
|||
} |
|||
path_delete(path); |
|||
} |
|||
} |
|||
} catch (...) { |
|||
runtime_error e{ "path_delete_all(\"" + path + "\")" }; |
|||
throw_with_nested(e); |
|||
} |
|||
} |
|||
#endif |
|||
|
|||
ofstream file_create(const string &filename) |
|||
{ |
|||
ofstream outfile{ filename }; |
|||
return outfile; |
|||
} |
|||
|
|||
std::string file_read(const std::string &filename) |
|||
{ |
|||
auto ss = ostringstream{}; |
|||
ifstream input_file(filename); |
|||
if (!input_file.is_open()) { |
|||
runtime_error e{ "Could not open the file: " + filename }; |
|||
exit(EXIT_FAILURE); |
|||
} |
|||
ss << input_file.rdbuf(); |
|||
return ss.str(); |
|||
} |
|||
|
|||
void path_ensure_not_existing(const string &path) |
|||
{ |
|||
while (path_exists(path)) { |
|||
path_delete_all(path); |
|||
} |
|||
} |
|||
|
|||
#ifndef WIN32 |
|||
void dir_create(const string &dirname, const mode_t mode) |
|||
{ |
|||
if (mkdir(dirname.c_str(), mode) != 0) { |
|||
runtime_error e{ string("dir_create(\"" + dirname + "\") - " + strerror(errno)) }; |
|||
throw(e); |
|||
} |
|||
} |
|||
|
|||
void dir_ensure(const std::string &path) |
|||
{ |
|||
if (!Utils::path_exists(path)) { |
|||
Utils::dir_create(path); |
|||
} |
|||
} |
|||
|
|||
void dir_recreate(const std::string &path) |
|||
{ |
|||
Utils::path_ensure_not_existing(path); |
|||
Utils::dir_ensure(path); |
|||
} |
|||
|
|||
vector<string> dir_list_all(const std::string &path, const bool incl_dot_and_dotdot) |
|||
{ |
|||
vector<string> ret; |
|||
if (!path_exists(path)) { |
|||
runtime_error e{ "dir_list_all(\"" + path + "\") - Error: does not exist" }; |
|||
throw(e); |
|||
} |
|||
|
|||
if (!path_is_dir(path)) { |
|||
runtime_error e{ "dir_list_all(\"" + path + "\") - Error: is not a directory" }; |
|||
throw(e); |
|||
} |
|||
|
|||
DIR *dirp = opendir(path.c_str()); |
|||
if (dirp == nullptr) { |
|||
runtime_error e{ "dir_list_all(\"" + path + "\") - Error opening dir" }; |
|||
throw e; |
|||
} |
|||
|
|||
struct dirent *dp; |
|||
while ((dp = readdir(dirp)) != NULL) { |
|||
ret.push_back(string(dp->d_name)); |
|||
} |
|||
|
|||
if (!incl_dot_and_dotdot) { |
|||
ret.erase( |
|||
remove_if( |
|||
ret.begin(), |
|||
ret.end(), |
|||
[](string elem) { return (elem == "." || elem == ".."); }), |
|||
ret.end()); |
|||
} |
|||
|
|||
closedir(dirp); |
|||
return ret; |
|||
} |
|||
|
|||
vector<string> dir_list_dirs(const string &dirname, const bool incl_dot_and_dotdot) |
|||
{ |
|||
vector<string> ret = dir_list_all(dirname, incl_dot_and_dotdot); |
|||
ret.erase( |
|||
remove_if( |
|||
ret.begin(), |
|||
ret.end(), |
|||
[dirname](string elem) { return !path_is_dir(dirname + "/" + elem); }), |
|||
ret.end()); |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
vector<string> dir_list_files(const string &dirname) |
|||
{ |
|||
vector<string> ret = dir_list_all(dirname); |
|||
ret.erase( |
|||
remove_if( |
|||
ret.begin(), |
|||
ret.end(), |
|||
[dirname](string elem) { return path_is_dir(dirname + "/" + elem); }), |
|||
ret.end()); |
|||
return ret; |
|||
} |
|||
#endif |
|||
|
|||
// Attention, it pads left...
|
|||
string padTo(const string &str, const size_t num, const char paddingChar) |
|||
{ |
|||
string ret{ str }; |
|||
if (num > ret.size()) { |
|||
ret.insert(0, num - ret.size(), paddingChar); |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
std::string clip(const std::string &str, const size_t len) |
|||
{ |
|||
std::string ret{ str }; |
|||
if (str.length() > len) { |
|||
ret = str.substr(0, len) + "..."; |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
// prints the beginning and the end of the string and clips out
|
|||
// content in the middle replaced by "... tldr ..."
|
|||
std::string tldr(const std::string &str, const size_t len) |
|||
{ |
|||
std::string decoration = "..."; |
|||
int trunclen = len - decoration.length(); |
|||
|
|||
std::string ret{ str }; |
|||
if (str.length() > len) { |
|||
ret = "\n" + str.substr(0, (int)floor(trunclen / 2.0)) + "\n... tldr ...\n"; |
|||
ret += str.substr(str.length() - (int)floor(trunclen / 2.0), str.length()); |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
string to_termcol(const Color &col) |
|||
{ |
|||
switch (col) { |
|||
case Color::RESET: |
|||
return "\033[0m"; |
|||
case Color::BLACK: |
|||
return "\033[30m"; |
|||
case Color::RED: |
|||
return "\033[31m"; |
|||
case Color::GREEN: |
|||
return "\033[32m"; |
|||
case Color::YELLOW: |
|||
return "\033[33m"; |
|||
case Color::BLUE: |
|||
return "\033[34m"; |
|||
case Color::MAGENTA: |
|||
return "\033[35m"; |
|||
case Color::CYAN: |
|||
return "\033[36m"; |
|||
case Color::WHITE: |
|||
return "\033[37m"; |
|||
default: |
|||
return "\033[0m"; |
|||
} |
|||
} |
|||
|
|||
void sleep_millis(int milis) |
|||
{ |
|||
std::chrono::milliseconds timespan(milis); |
|||
std::this_thread::sleep_for(timespan); |
|||
} |
|||
|
|||
unsigned char random_char(unsigned char min, unsigned char max) |
|||
{ |
|||
std::random_device rd; |
|||
std::mt19937 gen(rd()); |
|||
std::uniform_int_distribution<short> dis(static_cast<short>(min), static_cast<short>(max)); |
|||
return static_cast<unsigned char>(dis(gen)); |
|||
} |
|||
|
|||
std::string random_string(unsigned char min, unsigned char max, int len) |
|||
{ |
|||
std::stringstream ret; |
|||
for (int i = 0; i < len; i++) { |
|||
ret << random_char(97, 122); |
|||
} |
|||
return ret.str(); |
|||
} |
|||
|
|||
} // namespace Utils
|
|||
} // namespace pEp
|
@ -0,0 +1,94 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef LIBPEPADAPTER_STD_UTILS_HH |
|||
#define LIBPEPADAPTER_STD_UTILS_HH |
|||
|
|||
#include <string> |
|||
#include <exception> |
|||
#include <vector> |
|||
|
|||
namespace pEp { |
|||
namespace Utils { |
|||
// C-types helpers
|
|||
bool is_c_str_empty(const char *str); |
|||
|
|||
// C++/STL data types to string
|
|||
template<typename T> |
|||
std::string to_string(const std::vector<T> &v); |
|||
|
|||
// exception utils
|
|||
std::string nested_exception_to_string( |
|||
const std::exception &e, |
|||
int level = 0, |
|||
std::string src = ""); |
|||
void print_exception(const std::exception &e, int level = 0); |
|||
|
|||
// File utils
|
|||
// ----------
|
|||
// path (file & dir)
|
|||
bool path_exists(const std::string &filename); |
|||
void path_delete(const std::string &filename); |
|||
bool path_is_dir(const std::string &path); |
|||
|
|||
#ifndef WIN32 |
|||
void path_delete_all(const std::string &path); |
|||
#endif |
|||
|
|||
void path_ensure_not_existing(const std::string &path); |
|||
|
|||
// file
|
|||
std::ofstream file_create(const std::string &filename); |
|||
std::string file_read(const std::string &filename); |
|||
|
|||
// dir
|
|||
#ifndef WIN32 |
|||
void dir_create(const std::string &dirname, const mode_t mode = 0775); |
|||
void dir_ensure(const std::string &path); |
|||
void dir_recreate(const std::string &path); |
|||
|
|||
std::vector<std::string> dir_list_all( |
|||
const std::string &path, |
|||
const bool incl_dot_and_dotdot = false); |
|||
|
|||
std::vector<std::string> dir_list_dirs( |
|||
const std::string &dirname, |
|||
const bool incl_dot_and_dotdot = false); |
|||
|
|||
std::vector<std::string> dir_list_files(const std::string &dirname); |
|||
#endif |
|||
|
|||
//String formatting
|
|||
std::string padTo(const std::string &str, const size_t num, const char paddingChar); |
|||
std::string clip(const std::string &str, const size_t len); |
|||
std::string tldr(const std::string &str, const size_t len); |
|||
|
|||
enum class Color |
|||
{ |
|||
RESET, |
|||
BLACK, |
|||
RED, |
|||
GREEN, |
|||
YELLOW, |
|||
BLUE, |
|||
MAGENTA, |
|||
CYAN, |
|||
WHITE, |
|||
}; |
|||
|
|||
std::string to_termcol(const Color &col); |
|||
|
|||
|
|||
// Time
|
|||
void sleep_millis(int milis); |
|||
|
|||
// Random
|
|||
unsigned char random_char(unsigned char min, unsigned char max); |
|||
std::string random_string(unsigned char min, unsigned char max, int len); |
|||
|
|||
} // namespace Utils
|
|||
} // namespace pEp
|
|||
|
|||
#include "std_utils.hxx" |
|||
|
|||
#endif // LIBPEPADAPTER_STD_UTILS_HH
|
@ -0,0 +1,22 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef LIBPEPADAPTER_STD_UTILS_HXX |
|||
#define LIBPEPADAPTER_STD_UTILS_HXX |
|||
|
|||
#include <sstream> |
|||
|
|||
namespace pEp { |
|||
namespace Utils { |
|||
template<typename T> |
|||
std::string to_string(const std::vector<T>& v) |
|||
{ |
|||
std::stringstream ss; |
|||
for (const T& elem : v) { |
|||
ss << elem << std::endl; |
|||
} |
|||
return ss.str(); |
|||
} |
|||
} // namespace Utils
|
|||
} // namespace pEp
|
|||
#endif // LIBPEPADAPTER_STD_UTILS_HXX
|
@ -0,0 +1,259 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#include "utils.hh" |
|||
#include <pEp/identity_list.h> |
|||
#include <iostream> |
|||
|
|||
using namespace std; |
|||
using namespace pEp; |
|||
|
|||
namespace pEp { |
|||
namespace Utils { |
|||
std::vector<::pEp_identity *> to_cxx(const ::identity_list &idl) |
|||
{ |
|||
vector<pEp_identity *> ret{}; |
|||
for (const ::identity_list *curr = &idl; curr != nullptr; curr = curr->next) { |
|||
if (curr->ident) { |
|||
ret.push_back(curr->ident); |
|||
} |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
string to_string(const ::pEp_identity *const ident, bool full, int indent) |
|||
{ |
|||
stringstream builder; |
|||
if (ident != nullptr) { |
|||
if (full) { |
|||
builder << endl; |
|||
builder << std::string(indent, '\t') << "{" << endl; |
|||
indent++; |
|||
builder << std::string(indent, '\t') << "address: '" |
|||
<< (ident->address != nullptr ? ident->address : "NULL") << "'" << endl; |
|||
builder << std::string(indent, '\t') << "user_id: '" |
|||
<< (ident->user_id != nullptr ? ident->user_id : "NULL") << "'" << endl; |
|||
builder << std::string(indent, '\t') << "username: '" |
|||
<< (ident->username != nullptr ? ident->username : "NULL") << "'" |
|||
<< endl; |
|||
builder << std::string(indent, '\t') << "fpr: '" |
|||
<< (ident->fpr != nullptr ? ident->fpr : "NULL") << "'" << endl; |
|||
builder << std::string(indent, '\t') << "comm_type: " << ident->comm_type << endl; |
|||
builder << std::string(indent, '\t') << "lang: '" |
|||
<< static_cast<string>(ident->lang) << "'" << endl; |
|||
builder << std::string(indent, '\t') << "me: " << ident->me << endl; |
|||
builder << std::string(indent, '\t') << "major_ver: " << ident->major_ver << endl; |
|||
builder << std::string(indent, '\t') << "minor_ver: " << ident->minor_ver << endl; |
|||
builder << std::string(indent, '\t') << "enc_format: " << ident->enc_format |
|||
<< endl; |
|||
builder << std::string(indent, '\t') << "flags: " << ident->flags << endl; |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "}"; |
|||
} else { |
|||
builder << std::string(indent, '\t') << "{ '" |
|||
<< (ident->address != nullptr ? ident->address : "NULL") << "' / '" |
|||
<< (ident->user_id != nullptr ? ident->user_id : "NULL") << "' / '" |
|||
<< (ident->username != nullptr ? ident->username : "NULL") << "' / '" |
|||
<< (ident->fpr != nullptr ? ident->fpr : "NULL") << "' }"; |
|||
} |
|||
} else { |
|||
builder << "NULL"; |
|||
} |
|||
|
|||
return builder.str(); |
|||
} |
|||
|
|||
std::string to_string(const ::bloblist_t *const blob, bool full, int indent) |
|||
{ |
|||
stringstream builder; |
|||
if (blob != nullptr) { |
|||
builder << endl; |
|||
builder << std::string(indent, '\t') << "[" << endl; |
|||
indent++; |
|||
for (const ::bloblist_t *curr = blob; curr != nullptr; curr = curr->next) { |
|||
if (full) { |
|||
builder << std::string(indent, '\t') << "{" << endl; |
|||
indent++; |
|||
builder << std::string(indent, '\t') << "mime_type: '" |
|||
<< (curr->mime_type != nullptr ? std::string(curr->mime_type) : "NULL") |
|||
<< "'" << endl; |
|||
builder << std::string(indent, '\t') << "filename: '" |
|||
<< (curr->filename != nullptr ? curr->filename : "NULL") << "'" |
|||
<< endl; |
|||
builder << std::string(indent, '\t') << "size: " << curr->size << endl; |
|||
builder << std::string(indent, '\t') << "value: '" |
|||
<< (curr->value != nullptr |
|||
? Utils::tldr(std::string(curr->value), 300) |
|||
: "NULL") |
|||
<< "'" << endl; |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "}" << endl; |
|||
} else { |
|||
builder << std::string(indent, '\t'); |
|||
builder << "{ '" |
|||
<< (curr->mime_type != nullptr ? std::string(curr->mime_type) |
|||
: "NULL"); |
|||
builder << "' / '" << (curr->filename != nullptr ? curr->filename : "NULL"); |
|||
builder << "' / '" << curr->size << "'"; |
|||
builder << " }" << endl; |
|||
} |
|||
} |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "]" << endl; |
|||
} else { |
|||
builder << "NULL"; |
|||
} |
|||
|
|||
return builder.str(); |
|||
} |
|||
|
|||
std::string to_string(const ::stringpair_list_t *const spl, bool full, int indent) |
|||
{ |
|||
stringstream builder; |
|||
if (spl != nullptr) { |
|||
builder << endl; |
|||
builder << std::string(indent, '\t') << "[" << endl; |
|||
indent++; |
|||
for (const ::stringpair_list_t *curr = spl; curr != nullptr; curr = curr->next) { |
|||
builder << std::string(indent, '\t') << "{ '"; |
|||
if (curr->value != nullptr) { |
|||
builder << (curr->value->key ? curr->value->key : "NULL"); |
|||
builder << "' : '"; |
|||
builder << (curr->value->value ? curr->value->value : "NULL"); |
|||
} |
|||
builder << "' }" << endl; |
|||
} |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "]" << endl; |
|||
} else { |
|||
builder << "NULL"; |
|||
} |
|||
|
|||
return builder.str(); |
|||
} |
|||
|
|||
std::string to_string(const ::message *const msg, bool full, int indent) |
|||
{ |
|||
stringstream builder; |
|||
if (msg != nullptr) { |
|||
builder << endl; |
|||
builder << std::string(indent, '\t') << "{" << endl; |
|||
indent++; |
|||
builder << std::string(indent, '\t') << "from: " |
|||
<< (msg->from != nullptr ? to_string(msg->from, full, indent) : "NULL") |
|||
<< endl; |
|||
builder << std::string(indent, '\t') << "to: " |
|||
<< (msg->to != nullptr ? to_string(msg->to, full, indent) : "NULL") << endl; |
|||
builder << std::string(indent, '\t') << "shortmsg: '" |
|||
<< (msg->shortmsg != nullptr ? msg->shortmsg : "NULL") << "'" << endl; |
|||
builder << std::string(indent, '\t') << "longmsg: '" |
|||
<< (msg->longmsg != nullptr ? msg->longmsg : "NULL") << "'" << endl; |
|||
builder << std::string(indent, '\t') << "enc_format: " << msg->enc_format << endl; |
|||
builder << std::string(indent, '\t') |
|||
<< "dir: " << (msg->dir == 0 ? "incomming" : "outgoing") << endl; |
|||
builder << std::string(indent, '\t') << "id: '" |
|||
<< (msg->id != nullptr ? msg->id : "NULL") << "'" << endl; |
|||
builder << std::string(indent, '\t') << "opt_fields: " |
|||
<< (msg->opt_fields ? to_string(msg->opt_fields, full, indent) : "NULL") |
|||
<< endl; |
|||
builder << std::string(indent, '\t') << "attachments: " |
|||
<< (msg->attachments ? to_string(msg->attachments, full, indent) : "NULL") |
|||
<< endl; |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "}" << endl; |
|||
} else { |
|||
builder << "NULL"; |
|||
} |
|||
|
|||
return builder.str(); |
|||
} |
|||
|
|||
std::string to_string(const ::identity_list *const idl, bool full, int indent) |
|||
{ |
|||
stringstream builder; |
|||
if (idl != nullptr) { |
|||
builder << endl; |
|||
builder << std::string(indent, '\t') << "[" << endl; |
|||
indent++; |
|||
for (const ::identity_list *curr = idl; curr != nullptr; curr = curr->next) { |
|||
builder << to_string(curr->ident, full, indent) << endl; |
|||
} |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "]"; |
|||
} else { |
|||
builder << "NULL"; |
|||
} |
|||
|
|||
return builder.str(); |
|||
} |
|||
|
|||
string to_string(const ::pEp_member *const member, bool full, int indent) |
|||
{ |
|||
stringstream builder; |
|||
if (member != nullptr) { |
|||
builder << std::string(indent, '\t') << "{" << endl; |
|||
indent++; |
|||
builder << std::string(indent, '\t') |
|||
<< "ident: " << to_string(member->ident, full, indent) << endl; |
|||
builder << std::string(indent, '\t') << "joined: " << member->joined << endl; |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "}"; |
|||
} else { |
|||
builder << "NULL"; |
|||
} |
|||
|
|||
return builder.str(); |
|||
} |
|||
|
|||
string to_string(const ::member_list *const mbl, bool full, int indent) |
|||
{ |
|||
stringstream builder; |
|||
if (mbl != nullptr) { |
|||
builder << endl; |
|||
builder << std::string(indent, '\t') << "[" << endl; |
|||
indent++; |
|||
for (const member_list *curr = mbl; curr != nullptr; curr = curr->next) { |
|||
builder << to_string(curr->member, full, indent) << endl; |
|||
} |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "]"; |
|||
} else { |
|||
builder << "NULL"; |
|||
} |
|||
|
|||
return builder.str(); |
|||
} |
|||
|
|||
string to_string(const ::pEp_group *const group, bool full, int indent) |
|||
{ |
|||
stringstream builder; |
|||
if (group != nullptr) { |
|||
builder << endl; |
|||
builder << std::string(indent, '\t') << "{" << endl; |
|||
indent++; |
|||
builder << std::string(indent, '\t') |
|||
<< "group_identity: " << to_string(group->group_identity, full, indent) |
|||
<< endl; |
|||
builder << std::string(indent, '\t') |
|||
<< "manager: " << to_string(group->manager, full, indent) << endl; |
|||
builder << std::string(indent, '\t') << "active: " << group->active << endl; |
|||
builder << std::string(indent, '\t') |
|||
<< "members: " << to_string(group->members, full, indent) << endl; |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "]"; |
|||
} else { |
|||
builder << "NULL"; |
|||
} |
|||
|
|||
return builder.str(); |
|||
} |
|||
|
|||
// TODO: Move to std_utils
|
|||
std::string readKey() |
|||
{ |
|||
std::string ret; |
|||
std::cin >> ret; |
|||
return ret; |
|||
} |
|||
} // namespace Utils
|
|||
} // namespace pEp
|
@ -0,0 +1,35 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef LIBPEPADAPTER_UTILS_HH |
|||
#define LIBPEPADAPTER_UTILS_HH |
|||
|
|||
#include "pEpLog.hh" |
|||
#include <pEp/message.h> |
|||
#include <pEp/identity_list.h> |
|||
#include <pEp/group.h> |
|||
#include <exception> |
|||
#include <vector> |
|||
#include <string> |
|||
|
|||
namespace pEp { |
|||
namespace Utils { |
|||
// C-types to C++ types
|
|||
std::vector<::pEp_identity *> to_cxx(const ::identity_list &idl); |
|||
|
|||
// pEpEngine datatypes to string
|
|||
std::string to_string(const ::pEp_identity *const ident, bool full = true, int indent = 0); |
|||
std::string to_string(const ::bloblist_t *const ident, bool full = true, int indent = 0); |
|||
std::string to_string(const ::stringpair_list_t *const spl, bool full = true, int indent = 0); |
|||
std::string to_string(const ::message *const msg, bool full = true, int indent = 0); |
|||
std::string to_string(const ::identity_list *const idl, bool full = true, int indent = 0); |
|||
std::string to_string(const ::pEp_member *const member, bool full = true, int indent = 0); |
|||
std::string to_string(const ::member_list *const mbl, bool full = true, int indent = 0); |
|||
std::string to_string(const ::pEp_group *const group, bool full = true, int indent = 0); |
|||
|
|||
// Misc
|
|||
std::string readKey(); // TODO: Move to std_utils
|
|||
|
|||
} // namespace Utils
|
|||
} // namespace pEp
|
|||
#endif // LIBPEPADAPTER_UTILS_HH
|
@ -1,165 +1,270 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#include "utils.hh" |
|||
|
|||
#include <iostream> |
|||
|
|||
#include <pEp/pEpEngine.h> |
|||
#include <pEp/message_api.h> |
|||
#include <pEp/keymanagement.h> |
|||
#include <pEp/identity_list.h> |
|||
|
|||
#include "../../src/Adapter.hh" |
|||
#include "../../src/adapter_group.h" |
|||
|
|||
using namespace std; |
|||
using namespace pEp; |
|||
#include <pEp/Adapter.hh> |
|||
#include <pEp/status_to_string.hh> |
|||
#include <pEp/mime.h> |
|||
#include <tuple> |
|||
|
|||
namespace pEp { |
|||
namespace Test { |
|||
namespace Log { |
|||
void logH1(string msg) |
|||
namespace Utils { |
|||
|
|||
//Ident
|
|||
pEpIdent wrap(::pEp_identity *const ident) |
|||
{ |
|||
char decoration{ '=' }; |
|||
cout << endl |
|||
<< endl |
|||
<< std::string(30, decoration) << ' ' << msg << ' ' |
|||
<< std::string(30, decoration) << endl; |
|||
assert(ident); |
|||
auto ret = pEpIdent(ident, [](::pEp_identity *) {}); |
|||
return ret; |
|||
} |
|||
|
|||
void logH2(string msg) |
|||
pEpIdent appropriate(::pEp_identity *const ident) |
|||
{ |
|||
char decoration{ '-' }; |
|||
cout << endl |
|||
<< std::string(10, decoration) << ' ' << msg << ' ' |
|||
<< std::string(10, decoration) << endl; |
|||
assert(ident); |
|||
auto ret = pEpIdent(ident, ::free_identity); |
|||
return ret; |
|||
} |
|||
} // namespace Log
|
|||
|
|||
namespace Utils { |
|||
string identity_to_string(::pEp_identity* ident, bool full, int indent) |
|||
{ |
|||
|
|||
stringstream builder; |
|||
if (ident != nullptr) { |
|||
if (full) { |
|||
builder << endl; |
|||
builder << std::string(indent, '\t') << "{" << endl; |
|||
indent++; |
|||
builder << std::string(indent, '\t') << "address: " |
|||
<< (ident->address != nullptr ? ident->address : "NULL") << endl; |
|||
builder << std::string(indent, '\t') << "user_id: " |
|||
<< (ident->user_id != nullptr ? ident->user_id : "NULL") << endl; |
|||
builder << std::string(indent, '\t') << "username: " |
|||
<< (ident->username != nullptr ? ident->username : "NULL") << endl; |
|||
builder << std::string(indent, '\t') |
|||
<< "fpr: " << (ident->fpr != nullptr ? ident->fpr : "NULL") << endl; |
|||
builder << std::string(indent, '\t') << "comm_type: " << ident->comm_type |
|||
<< endl; |
|||
builder << std::string(indent, '\t') |
|||
<< "lang: " << static_cast<string>(ident->lang) << endl; |
|||
builder << std::string(indent, '\t') << "me: " << ident->me << endl; |
|||
builder << std::string(indent, '\t') << "major_ver: " << ident->major_ver |
|||
<< endl; |
|||
builder << std::string(indent, '\t') << "minor_ver: " << ident->minor_ver |
|||
<< endl; |
|||
builder << std::string(indent, '\t') << "enc_format: " << ident->enc_format |
|||
<< endl; |
|||
builder << std::string(indent, '\t') << "flags: " << ident->flags << endl; |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "}"; |
|||
} else { |
|||
builder << "{ " << (ident->address != nullptr ? ident->address : "NULL") |
|||
<< "/" << (ident->user_id != nullptr ? ident->user_id : "NULL") |
|||
<< "/" << (ident->username != nullptr ? ident->username : "NULL") |
|||
<< "/" << (ident->fpr != nullptr ? ident->fpr : "NULL") << " }"; |
|||
} |
|||
} else { |
|||
builder << "NULL"; |
|||
} |
|||
pEpIdent dup(const ::pEp_identity *const ident) |
|||
{ |
|||
assert(ident); |
|||
auto ret = pEpIdent(::identity_dup(ident), ::free_identity); |
|||
return ret; |
|||
} |
|||
|
|||
return builder.str(); |
|||
pEpIdent kill(::pEp_identity *const ident) |
|||
{ |
|||
assert(ident); |
|||
auto ret = pEpIdent(::identity_dup(ident), ::free_identity); |
|||
::free_identity(ident); |
|||
return ret; |
|||
} |
|||
|
|||
std::string identitylist_to_string(::identity_list* idl, bool full, int indent) |
|||
//IdentityList
|
|||
pEpIdentList wrap(::identity_list *const ident) |
|||
{ |
|||
stringstream builder; |
|||
if (idl != nullptr) { |
|||
builder << endl; |
|||
builder << std::string(indent, '\t') << "[" << endl; |
|||
indent++; |
|||
for (::identity_list* curr = idl; curr != nullptr; curr = curr->next) { |
|||
builder << identity_to_string(curr->ident, full, indent) << endl; |
|||
} |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "]"; |
|||
} else { |
|||
builder << "NULL"; |
|||
} |
|||
assert(ident); |
|||
auto ret = pEpIdentList(ident, [](::identity_list *) {}); |
|||
return ret; |
|||
} |
|||
|
|||
return builder.str(); |
|||
pEpIdentList appropriate(::identity_list *const ident) |
|||
{ |
|||
assert(ident); |
|||
auto ret = pEpIdentList(ident, ::free_identity_list); |
|||
return ret; |
|||
} |
|||
|
|||
string member_to_string(::pEp_member* member, bool full, int indent) |
|||
pEpIdentList dup(const ::identity_list *const ident) |
|||
{ |
|||
stringstream builder; |
|||
if (member != nullptr) { |
|||
builder << std::string(indent, '\t') << "{" << endl; |
|||
indent++; |
|||
builder << std::string(indent, '\t') |
|||
<< "ident: " << identity_to_string(member->ident, full, indent) << endl; |
|||
builder << std::string(indent, '\t') << "joined: " << member->joined << endl; |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "}"; |
|||
} else { |
|||
builder << "NULL"; |
|||
assert(ident); |
|||
auto ret = pEpIdentList(::identity_list_dup(ident), ::free_identity_list); |
|||
return ret; |
|||
} |
|||
|
|||
pEpIdentList kill(::identity_list *const ident) |
|||
{ |
|||
assert(ident); |
|||
auto ret = pEpIdentList(::identity_list_dup(ident), ::free_identity_list); |
|||
::free_identity_list(ident); |
|||
return ret; |
|||
} |
|||
|
|||
//Message
|
|||
pEpMessage wrap(::message *const msg) |
|||
{ |
|||
assert(msg); |
|||
auto ret = pEpMessage(msg, [](::message *) {}); |
|||
return ret; |
|||
} |
|||
|
|||
pEpMessage appropriate(::message *const msg) |
|||
{ |
|||
assert(msg); |
|||
auto ret = pEpMessage(msg, ::free_message); |
|||
return ret; |
|||
} |
|||
|
|||
pEpMessage dup(const ::message *const msg) |
|||
{ |
|||
assert(msg); |
|||
auto ret = pEpMessage(::message_dup(msg), ::free_message); |
|||
return ret; |
|||
} |
|||
|
|||
pEpMessage kill(::message *const msg) |
|||
{ |
|||
assert(msg); |
|||
auto ret = pEpMessage(::message_dup(msg), ::free_message); |
|||
::free_message(msg); |
|||
return ret; |
|||
} |
|||
|
|||
// helpers
|
|||
pEpIdent createOwnIdent(const std::string &address) |
|||
{ |
|||
std::string name; |
|||
::pEp_identity *ident = nullptr; |
|||
ident = ::new_identity( |
|||
strdup(address.c_str()), |
|||
"", |
|||
PEP_OWN_USERID, |
|||
("myself " + address).c_str()); |
|||
ident->me = true; |
|||
|
|||
return appropriate(ident); |
|||
} |
|||
|
|||
pEpIdent createCptIdent(const std::string &address) |
|||
{ |
|||
std::string name; |
|||
::pEp_identity *ident = nullptr; |
|||
ident = ::new_identity( |
|||
strdup(address.c_str()), |
|||
"", |
|||
"", |
|||
("partner " + address).c_str()); |
|||
ident->me = false; |
|||
|
|||
return appropriate(ident); |
|||
} |
|||
|
|||
pEpIdent createRawIdent(const std::string &address) |
|||
{ |
|||
std::string name; |
|||
::pEp_identity *ident = nullptr; |
|||
ident = ::new_identity(strdup(address.c_str()), "", "", ""); |
|||
ident->me = false; |
|||
|
|||
return appropriate(ident); |
|||
} |
|||
|
|||
pEpIdentList createIdentityList(const std::vector<std::string> &addresses) |
|||
{ |
|||
::identity_list *list; |
|||
list = ::new_identity_list(nullptr); |
|||
for (std::string addr : addresses) { |
|||
::identity_list_add(list, ::identity_dup(createCptIdent(addr).get())); |
|||
} |
|||
return appropriate(list); |
|||
} |
|||
|
|||
pEpMessage createMessage(pEpIdent from, pEpIdent to, const std::string &longmsg) |
|||
{ |
|||
// create and fill in msg
|
|||
::message *msg = ::new_message(PEP_dir_outgoing); |
|||
msg->from = ::identity_dup(from.get()); |
|||
msg->to = ::new_identity_list(::identity_dup(to.get())); |
|||
msg->longmsg = strdup(longmsg.c_str()); |
|||
|
|||
return builder.str(); |
|||
pEpMessage ret = appropriate(msg); |
|||
return ret; |
|||
} |
|||
|
|||
string memberlist_to_string(::member_list* mbl, bool full, int indent) |
|||
pEpMessage createMessage(pEpIdent from, const std::string &to_addr, const std::string &longmsg) |
|||
{ |
|||
stringstream builder; |
|||
if (mbl != nullptr) { |
|||
builder << endl; |
|||
builder << std::string(indent, '\t') << "[" << endl; |
|||
indent++; |
|||
for (member_list* curr_member = mbl; curr_member != nullptr; |
|||
curr_member = curr_member->next) { |
|||
builder << member_to_string(curr_member->member, full, indent) << endl; |
|||
} |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "]"; |
|||
pEpIdent to_ident = createCptIdent(to_addr); |
|||
return createMessage(from, to_ident, longmsg); |
|||
} |
|||
|
|||
std::string mimeEncode(const pEpMessage msg) |
|||
{ |
|||
char *mimetext; |
|||
PEP_STATUS status = ::mime_encode_message(msg.get(), false, &mimetext, false); |
|||
throw_status(status); |
|||
std::string text{ mimetext }; |
|||
free(mimetext); |
|||
return text; |
|||
} |
|||
|
|||
pEpMessage mimeDecode(const std::string &mime_text) |
|||
{ |
|||
::message *msg; |
|||
bool has_possible_pEp_msg; |
|||
::PEP_STATUS status = ::mime_decode_message( |
|||
mime_text.c_str(), |
|||
mime_text.length(), |
|||
&msg, |
|||
&has_possible_pEp_msg); |
|||
throw_status(status); |
|||
return pEpMessage(msg, ::free_message); |
|||
} |
|||
|
|||
EncryptResult encryptMessage(const pEpMessage msg) |
|||
{ |
|||
pEpMessage msg_out; |
|||
bool could_encrypt = false; |
|||
::message *msgenc = nullptr; |
|||
PEP_STATUS status = ::encrypt_message( |
|||
Adapter::session(), |
|||
msg.get(), |
|||
nullptr, |
|||
&msgenc, |
|||
PEP_enc_PEP, |
|||
0); |
|||
throw_status(status); |
|||
::message *msg_out_p = nullptr; |
|||
if (msgenc != nullptr) { |
|||
could_encrypt = true; |
|||
msg_out = appropriate(msgenc); |
|||
} else { |
|||
builder << "NULL"; |
|||
could_encrypt = false; |
|||
msg_out = msg; |
|||
} |
|||
return EncryptResult(msg_out, "", could_encrypt); |
|||
} |
|||
|
|||
|
|||
DecryptResult decryptMessage(const pEpMessage msg, ::PEP_decrypt_flags_t *flags) |
|||
{ |
|||
pEpMessage msg_out; |
|||
bool was_encrypted = false; |
|||
|
|||
return builder.str(); |
|||
} |
|||
|
|||
string group_to_string(::pEp_group* group, bool full, int indent) |
|||
{ |
|||
stringstream builder; |
|||
if (group != nullptr) { |
|||
builder << endl; |
|||
builder << std::string(indent, '\t') << "{" << endl; |
|||
indent++; |
|||
builder << std::string(indent, '\t') << "group_identity: " |
|||
<< identity_to_string(group->group_identity, full, indent) << endl; |
|||
builder << std::string(indent, '\t') |
|||
<< "manager: " << identity_to_string(group->manager, full, indent) |
|||
<< endl; |
|||
builder << std::string(indent, '\t') << "active: " << group->active << endl; |
|||
builder << std::string(indent, '\t') |
|||
<< "members: " << memberlist_to_string(group->members, full, indent) |
|||
<< endl; |
|||
indent--; |
|||
builder << std::string(indent, '\t') << "]"; |
|||
::message *dec{ nullptr }; |
|||
::stringlist_t *kl = ::new_stringlist(""); |
|||
::PEP_rating rating; |
|||
PEP_STATUS status = ::decrypt_message( |
|||
Adapter::session(), |
|||
msg.get(), |
|||
&dec, |
|||
&kl, |
|||
&rating, |
|||
flags); |
|||
throw_status(status); |
|||
if (dec != nullptr) { |
|||
was_encrypted = true; |
|||
msg_out = appropriate(dec); |
|||
} else { |
|||
builder << "NULL"; |
|||
was_encrypted = false; |
|||
msg_out = msg; |
|||
} |
|||
return DecryptResult(msg_out, rating, kl, flags, was_encrypted); |
|||
} |
|||
|
|||
DecryptResult decryptMessage(const pEpMessage msg) |
|||
{ |
|||
::PEP_decrypt_flags_t dummy{ 0 }; |
|||
return decryptMessage(msg, &dummy); |
|||
} |
|||
|
|||
return builder.str(); |
|||
EncryptResult encryptAndEncode(const pEpMessage msg) |
|||
{ |
|||
EncryptResult ret = encryptMessage(msg); |
|||
std::string mime_text = mimeEncode(std::get<0>(ret)); |
|||
std::get<1>(ret) = mime_text; |
|||
return ret; |
|||
} |
|||
|
|||
DecryptResult decryptAndDecode(const std::string &mime_data) |
|||
{ |
|||
DecryptResult ret; |
|||
pEpMessage rx_msg = mimeDecode(mime_data); |
|||
ret = decryptMessage(rx_msg); |
|||
return ret; |
|||
} |
|||
} // namespace Utils
|
|||
} // namespace Test
|
|||
} // namespace pEp
|
|||
} // namespace pEp
|
@ -1,27 +1,132 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef LIBPEPADAPTER_UTILS_HH |
|||
#define LIBPEPADAPTER_UTILS_HH |
|||
#ifndef LIBPEPADAPTER_TEST_UTILS_HH |
|||
#define LIBPEPADAPTER_TEST_UTILS_HH |
|||
|
|||
#include "../../src/pEpLog.hh" |
|||
#include <string> |
|||
#include <pEp/message.h> |
|||
#include <exception> |
|||
#include <chrono> |
|||
#include <thread> |
|||
#include <cstring> |
|||
#include <tuple> |
|||
#include <pEp/pEpEngine.h> |
|||
#include <pEp/identity_list.h> |
|||
#include <pEp/group.h> |
|||
#include <pEp/message.h> |
|||
#include <pEp/message_api.h> |
|||
|
|||
// ------------------------------------------------------------------------------------------------
|
|||
|
|||
#ifndef ASSERT_EXCEPT |
|||
#define ASSERT_EXCEPT(func) \ |
|||
do { \ |
|||
try { \ |
|||
(func); \ |
|||
assert(false); \ |
|||
} catch (const exception &e) { \ |
|||
pEp::Adapter::pEpLog::log(nested_exception_to_string(e)); \ |
|||
} \ |
|||
} while (0) |
|||
#endif |
|||
|
|||
// ------------------------------------------------------------------------------------------------
|
|||
// Logging macros for testing
|
|||
// ------------------------------------------------------------------------------------------------
|
|||
// Use the macros if you need the message to be prefixed with "thread - __FILE__::__FUNTION__"
|
|||
// OTHERWISE, just use the logging functions from pEp::Adapter::pEpLog
|
|||
|
|||
// TESTLOG - logformat "thread - __FILE__::__FUNTION__ - <message>"
|
|||
// To be used in a non-class/object context
|
|||
#ifndef TESTLOG |
|||
#define TESTLOG(msg) \ |
|||
do { \ |
|||
std::stringstream msg_; \ |
|||
msg_ << "[" << getpid() << " " << std::this_thread::get_id() << "]"; \ |
|||
msg_ << " - " << __FILE__ << "::" << __FUNCTION__; \ |
|||
msg_ << " - " << msg; \ |
|||
pEp::Adapter::pEpLog::log(msg_.str()); \ |
|||
} while (0) |
|||
#endif // TESTLOG
|
|||
|
|||
// TESTLOGH1 - logformat "Thread - __FILE__::__FUNTION__ - <=============== message ==============>"
|
|||
#ifndef TESTLOGH1 |
|||
#define TESTLOGH1(msg) \ |
|||
do { \ |
|||
std::stringstream msg_; \ |
|||
msg_ << "[" << getpid() << " " << std::this_thread::get_id() << "]"; \ |
|||
msg_ << " - " << __FILE__ << "::" << __FUNCTION__; \ |
|||
msg_ << " - " << pEp::Adapter::pEpLog::decorateH1(msg); \ |
|||
pEp::Adapter::pEpLog::log(msg_.str()); \ |
|||
} while (0) |
|||
#endif // TESTLOGH1
|
|||
|
|||
// TESTLOGH2 - logformat "Thread - __FILE__::__FUNTION__ - <--------------- message -------------->"
|
|||
#ifndef TESTLOGH2 |
|||
#define TESTLOGH2(msg) \ |
|||
do { \ |
|||
std::stringstream msg_; \ |
|||
msg_ << "[" << getpid() << " " << std::this_thread::get_id() << "]"; \ |
|||
msg_ << " - " << __FILE__ << "::" << __FUNCTION__; \ |
|||
msg_ << " - " << pEp::Adapter::pEpLog::decorateH2(msg); \ |
|||
pEp::Adapter::pEpLog::log(msg_.str()); \ |
|||
} while (0) |
|||
#endif // TESTLOGH2
|
|||
|
|||
// ------------------------------------------------------------------------------------------------
|
|||
|
|||
namespace pEp { |
|||
namespace Test { |
|||
namespace Log { |
|||
void logH1(std::string msg); |
|||
void logH2(std::string msg); |
|||
} |
|||
namespace Utils { |
|||
std::string identity_to_string(::pEp_identity* ident, bool full = true, int indent = 0); |
|||
std::string identitylist_to_string(::identity_list * idl, bool full = true, int indent = 0); |
|||
std::string member_to_string(::pEp_member* member, bool full = true, int indent = 0); |
|||
std::string memberlist_to_string(::member_list* mbl, bool full = true, int indent = 0); |
|||
std::string group_to_string(::pEp_group* group, bool full = true, int indent = 0); |
|||
} |
|||
} // namespace Test
|
|||
using pEpIdent = std::shared_ptr<::pEp_identity>; |
|||
using pEpIdentList = std::shared_ptr<::identity_list>; |
|||
using pEpMessage = std::shared_ptr<::message>; |
|||
// [ DecryptedMessage, Rating, KeyList, Flags, WasEncrypted ]
|
|||
using DecryptResult = std:: |
|||
tuple<pEpMessage, ::PEP_rating, ::stringlist_t *, ::PEP_decrypt_flags_t *, bool>; |
|||
// [ EncryptedMessage, MimeText, couldEncrypt ]
|
|||
using EncryptResult = std::tuple<pEpMessage, std::string, bool>; |
|||
|
|||
// Datatypes
|
|||
//Ident
|
|||
pEpIdent wrap(::pEp_identity *const ident); |
|||
pEpIdent appropriate(::pEp_identity *const ident); |
|||
pEpIdent dup(const ::pEp_identity *const ident); |
|||
pEpIdent kill(::pEp_identity *const ident); |
|||
|
|||
//IdentityList
|
|||
pEpIdentList wrap(::identity_list *const ident); |
|||
pEpIdentList appropriate(::identity_list *const ident); |
|||
pEpIdentList dup(const ::identity_list *const ident); |
|||
pEpIdentList kill(::identity_list *const ident); |
|||
|
|||
//Message
|
|||
pEpMessage wrap(::message *const msg); |
|||
pEpMessage appropriate(::message *const msg); |
|||
pEpMessage dup(const ::message *const msg); |
|||
pEpMessage kill(::message *const msg); |
|||
|
|||
// helpers
|
|||
pEpIdent createOwnIdent(const std::string &address); |
|||
pEpIdent createCptIdent(const std::string &address); |
|||
pEpIdent createRawIdent(const std::string &address); |
|||
pEpIdentList createIdentityList(const std::vector<std::string> &addresses); |
|||
pEpMessage createMessage(pEpIdent from, pEpIdent to, const std::string &longmsg); |
|||
pEpMessage createMessage(pEpIdent from, const std::string &to_addr, const std::string &longmsg); |
|||
|
|||
|
|||
std::string mimeEncode(const pEpMessage msg); |
|||
pEpMessage mimeDecode(const std::string &mime_text); |
|||
|
|||
EncryptResult encryptMessage(const pEpMessage msg); |
|||
DecryptResult decryptMessage(const pEpMessage msg, ::PEP_decrypt_flags_t *flags); |
|||
DecryptResult decryptMessage(const pEpMessage msg); |
|||
|
|||
EncryptResult encryptAndEncode(const pEpMessage msg); |
|||
DecryptResult decryptAndDecode(const std::string &mime_data); |
|||
|
|||
} // namespace Utils
|
|||
} // namespace Test
|
|||
} // namespace pEp
|
|||
#endif |
|||
|
|||
#endif // LIBPEPADAPTER_TEST_UTILS_HH
|
@ -0,0 +1,39 @@ |
|||
include ../../Makefile.conf |
|||
|
|||
TARGET=src/libPityTest.a |
|||
LDFLAGS:=-L../../src/ $(LDFLAGS) -L../framework/ |
|||
LDLIBS=-lstdc++ -lpEpAdapter -lpEpEngine |
|||
CXXFLAGS:=-std=c++11 -g -I./src $(CXXFLAGS) |
|||
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: $(TARGET) test |
|||
|
|||
$(TARGET): $(OBJ) $(TEST_EXTRA_OBJS) |
|||
$(AR) -rc $@ $^ |
|||
|
|||
|
|||
test : $(TEST_OBJ) |
|||
|
|||
$(TEST_OBJ): $(OBJ) $(TEST_EXTRA_OBJS) |
|||
|
|||
|
|||
clean: |
|||
rm -f $(TARGET) |
|||
rm -f $(OBJ) |
|||
rm -f $(TEST_OBJ) |
|||
rm -rf src/*.dSYM |
|||
rm -rf test/*.dSYM |
|||
|
@ -0,0 +1,456 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#include "AbstractPityUnit.hh" |
|||
#include "../../../src/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 = "./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
|
|||
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 << "\"" << getProcessDir() << "\""; |
|||
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 "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"; |
|||
} |
|||
} |
|||
|
|||
|
|||
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) 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(); |
|||
} |
|||
} |
|||
|
|||
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
|
@ -0,0 +1,135 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef PITYTEST_ABSTRACTPITYUNIT_HH |
|||
#define PITYTEST_ABSTRACTPITYUNIT_HH |
|||
|
|||
#include "../../../src/pEpLog.hh" |
|||
#include "../../../src/std_utils.hh" |
|||
#include "fs_mutex.hh" |
|||
#include "PityTree.hh" |
|||
#include "PityTransport.hh" |
|||
#include <string> |
|||
#include <memory> |
|||
#include <functional> |
|||
|
|||
// Yes, the mem mgmt is purely static on purpose (so far)
|
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
class AbstractPityUnit : public PityTree<AbstractPityUnit> { |
|||
public: |
|||
enum class ExecutionMode |
|||
{ |
|||
FUNCTION, |
|||
PROCESS_SEQUENTIAL, |
|||
PROCESS_PARALLEL, |
|||
THREAD_SEQUENTIAL, // unimplemented
|
|||
THREAD_PARALLEL, // unimplemented
|
|||
INHERIT |
|||
}; |
|||
|
|||
// Constructors
|
|||
// ------------
|
|||
// RootNode
|
|||
explicit AbstractPityUnit( |
|||
const std::string& name, |
|||
ExecutionMode exec_mode = ExecutionMode::FUNCTION); |
|||
|
|||
// LeafNode
|
|||
explicit AbstractPityUnit( |
|||
AbstractPityUnit& parent, |
|||
const std::string& name, |
|||
ExecutionMode exec_mode = ExecutionMode::FUNCTION); |
|||
|
|||
// Copy
|
|||
explicit AbstractPityUnit(const AbstractPityUnit& rhs, AbstractPityUnit& self); |
|||
|
|||
// copy-assign
|
|||
AbstractPityUnit& operator=(const AbstractPityUnit& rhs); |
|||
|
|||
AbstractPityUnit* clone() override = 0; |
|||
|
|||
// Read-Write
|
|||
static void setGlobalRootDir(const std::string& dir); |
|||
static std::string getGlobalRootDir(); |
|||
void setExecMode(ExecutionMode execMode); |
|||
|
|||
// Read-Only
|
|||
std::string getPathShort() const; |
|||
std::string getProcessDir(); // own process dir
|
|||
std::string getTransportDir(); |
|||
std::string getRootUnitDir(); |
|||
bool isProcessUnit() const; // true if it forks
|
|||
const AbstractPityUnit& getParentProcessUnit() const; |
|||
|
|||
|
|||
// Main funcs
|
|||
void run(bool init_tree = true); |
|||
|
|||
std::string to_string(bool recursive = true, int indent = 0); |
|||
static std::string to_string(const ExecutionMode& emode); |
|||
|
|||
// logging service
|
|||
void log(const std::string& msg) const; |
|||
void logH1(const std::string& msg) const; |
|||
void logH2(const std::string& msg) const; |
|||
void logH3(const std::string& msg) const; |
|||
|
|||
//Transport
|
|||
PityTransport* transport() const; |
|||
void registerAsTransportEndpoint(); |
|||
Endpoints& transportEndpoints(); |
|||
|
|||
// internal logging
|
|||
static bool debug_log_enabled; |
|||
Adapter::pEpLog::pEpLogger logger_debug{ "PityUnit", debug_log_enabled }; |
|||
|
|||
protected: |
|||
std::string _status_string(const std::string& msg) const; |
|||
static Utils::Color _colForProcUnitNr(int procUnitNr); |
|||
Utils::Color _color() const; |
|||
void _logRaw(const std::string& msg) const; |
|||
|
|||
// internal logging
|
|||
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger_debug; |
|||
|
|||
private: |
|||
// METHODS
|
|||
void _init(); |
|||
// Execution
|
|||
void _initProcUnitNrRecurse(); |
|||
void _initTransportRecurse(); |
|||
void _initDirsRecursive(); |
|||
void _runRecurse(); |
|||
virtual void _runSelf() = 0; |
|||
void _executeInFork(std::function<void(void)> func, bool wait_child) const; |
|||
void _waitChildProcesses() const; |
|||
|
|||
// Transport
|
|||
void _createTransport(); |
|||
|
|||
// Fields
|
|||
// ------
|
|||
static std::string _global_root_dir; |
|||
ExecutionMode _exec_mode; |
|||
int _procUnitNr; |
|||
static int _procUnitsCount; // will be increased in every constructor
|
|||
|
|||
// transport
|
|||
std::shared_ptr<PityTransport> _transport; //only ever read via transport()
|
|||
// TODO move endpoints into PityTransport
|
|||
Endpoints _transport_endpoints; // only ever access via transportEndpoints()
|
|||
|
|||
// fs-mutex to sync across processes
|
|||
std::shared_ptr<fs_mutex> _log_mutex = nullptr; |
|||
}; |
|||
|
|||
class PityAssertException : public std::runtime_error { |
|||
public: |
|||
PityAssertException(const std::string& string) : runtime_error(string) {} |
|||
}; |
|||
}; // namespace PityTest11
|
|||
}; // namespace pEp
|
|||
|
|||
#endif |
@ -0,0 +1,32 @@ |
|||
#include "PityModel.hh" |
|||
#include "PityNode.hh" |
|||
#include <random> |
|||
#include <memory> |
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
bool PityModel::debug_log_enabled = false; |
|||
|
|||
PityModel::PityModel(const std::string& name, int nodeCount) : _name{ name } |
|||
{ |
|||
for (int i = 0; i < nodeCount; i++) { |
|||
_nodes.emplace_back(std::make_shared<PityNode>(i)); |
|||
} |
|||
} |
|||
|
|||
std::string PityModel::getName() const |
|||
{ |
|||
return _name; |
|||
} |
|||
|
|||
std::vector<std::shared_ptr<PityNode>> PityModel::nodes() const |
|||
{ |
|||
return _nodes; |
|||
} |
|||
|
|||
PityNode* PityModel::nodeNr(int nr) const |
|||
{ |
|||
return nodes().at(nr).get(); |
|||
} |
|||
} // namespace PityTest11
|
|||
} // namespace pEp
|
@ -0,0 +1,53 @@ |
|||
// 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" |
|||
#include "PityNode.hh" |
|||
#include <vector> |
|||
#include <memory> |
|||
|
|||
// The Model currently is as follows:
|
|||
// The Model creates the TestTree using PityUnits.
|
|||
// When creating the model you specify how many nodes you want
|
|||
//
|
|||
// ATTENTION - TODO:
|
|||
// Currently there is a strict 1-1 relationship of nodes and identities.
|
|||
// One Node has exactly one identity, and this identity is only on this node.
|
|||
// This needs to be enhanced to be a n-n relationship
|
|||
// The Transport only addresses nodes, not idents, therefore
|
|||
// If you have one ident on n nodes, the transport needs to check the model for all nodes the
|
|||
// ident is on and send the message to all these nodes.
|
|||
// If you have a node that has n identities, the persepective needs to specify node AND ident.
|
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
class PityModel { |
|||
public: |
|||
// Constructors
|
|||
PityModel() = delete; |
|||
PityModel(const std::string& name, int nodeCount); |
|||
|
|||
// Getters
|
|||
std::string getName() const; |
|||
std::vector<std::shared_ptr<PityNode>> nodes() const; |
|||
PityNode* nodeNr(int nr) const; |
|||
|
|||
//internal logging
|
|||
static bool debug_log_enabled; |
|||
Adapter::pEpLog::pEpLogger logger_debug{ "PityModel", debug_log_enabled }; |
|||
|
|||
private: |
|||
std::vector<std::shared_ptr<PityNode>> _nodes; |
|||
std::string _name; |
|||
|
|||
//internal logging
|
|||
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger_debug; |
|||
}; |
|||
}; // namespace PityTest11
|
|||
|
|||
}; // namespace pEp
|
|||
|
|||
#endif // PITYTEST_PITYMODEL_HH
|
@ -0,0 +1,63 @@ |
|||
#include "PityNode.hh" |
|||
#include <memory> |
|||
#include <sstream> |
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
TestIdent::TestIdent(const std::string& addr) : |
|||
addr{ addr }, did_rx_encrypted{ false }, did_tx_encrypted{ false } |
|||
{ |
|||
ident = Test::Utils::createCptIdent(addr); |
|||
} |
|||
|
|||
TestIdent::TestIdent(const TestIdent& rhs) |
|||
{ |
|||
did_rx_encrypted = rhs.did_rx_encrypted; |
|||
did_tx_encrypted = rhs.did_tx_encrypted; |
|||
addr = rhs.addr; |
|||
ident = Test::Utils::dup(rhs.ident.get()); |
|||
} |
|||
|
|||
bool TestIdent::tofu_done() const |
|||
{ |
|||
return did_tx_encrypted && did_rx_encrypted; |
|||
} |
|||
|
|||
|
|||
// ---------------------------------------------------------------------------------
|
|||
|
|||
bool PityNode::debug_log_enabled = false; |
|||
|
|||
PityNode::PityNode(int nodeNr) : _node_nr{ nodeNr } |
|||
{ |
|||
logger_debug.set_instancename(getName()); |
|||
std::stringstream ss; |
|||
ss << this; |
|||
pEpLogClass(std::string("called with: " + std::to_string(_node_nr) + "AT: " + ss.str())); |
|||
ident = std::make_shared<TestIdent>(TestIdent(getName())); |
|||
} |
|||
|
|||
TestIdent& PityNode::getIdent() { |
|||
return *ident.get(); |
|||
} |
|||
|
|||
int PityNode::getNr() const |
|||
{ |
|||
return _node_nr; |
|||
} |
|||
|
|||
std::string PityNode::getName() const |
|||
{ |
|||
std::string ret; |
|||
ret += "node_" + std::to_string(_node_nr) + "@peptest.org"; |
|||
return ret; |
|||
} |
|||
|
|||
std::string PityNode::to_string() const |
|||
{ |
|||
std::string ret; |
|||
ret += "name: " + getName(); |
|||
return ret; |
|||
} |
|||
} // namespace PityTest11
|
|||
} // namespace pEp
|
@ -0,0 +1,55 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef PITYTEST_PITYNODE_HH |
|||
#define PITYTEST_PITYNODE_HH |
|||
|
|||
#include "../../../src/pEpLog.hh" |
|||
#include "../../framework/utils.hh" |
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
class TestIdent { |
|||
public: |
|||
TestIdent() = delete; |
|||
explicit TestIdent(const std::string& addr); |
|||
TestIdent(const TestIdent& rhs); |
|||
bool tofu_done() const; |
|||
|
|||
std::string addr; |
|||
pEp::Test::Utils::pEpIdent ident{}; |
|||
|
|||
// state
|
|||
bool did_tx_encrypted; |
|||
bool did_rx_encrypted; |
|||
}; |
|||
|
|||
|
|||
class PityNode { |
|||
public: |
|||
// Constructors
|
|||
PityNode() = delete; |
|||
explicit PityNode(int nodeNr); |
|||
|
|||
// Getters
|
|||
TestIdent& getIdent(); |
|||
int getNr() const; |
|||
std::string getName() const; |
|||
std::string to_string() const; |
|||
|
|||
//internal logging
|
|||
static bool debug_log_enabled; |
|||
Adapter::pEpLog::pEpLogger logger_debug{ "PityNode", debug_log_enabled }; |
|||
|
|||
private: |
|||
//fields
|
|||
const int _node_nr; |
|||
std::shared_ptr<TestIdent> ident; |
|||
|
|||
//internal logging
|
|||
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger_debug; |
|||
}; |
|||
}; // namespace PityTest11
|
|||
}; // namespace pEp
|
|||
|
|||
#endif // PITYTEST_PITYNODE_HH
|
@ -0,0 +1,48 @@ |
|||
#include "PityPerspective.hh" |
|||
#include "PityModel.hh" |
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
bool PityPerspective::debug_log_enabled = false; |
|||
|
|||
PityPerspective::PityPerspective(const PityModel& model) : model{ model }, peerNrAsCpt{ 0 } |
|||
{ |
|||
pEpLogClass("called"); |
|||
} |
|||
|
|||
TestIdent* PityPerspective::getPeer(const std::string& addr) |
|||
{ |
|||
for (int i = 0; i < peers.size(); i++) { |
|||
if (peers.at(i).addr == addr) { |
|||
return &peers.at(i); |
|||
} |
|||
} |
|||
throw std::invalid_argument("getPeer(+" + addr + ") - not found"); |
|||
} |
|||
|
|||
void PityPerspective::setPeerNrAsCpt(int nr) |
|||
{ |
|||
if (nr < peers.size()) { |
|||
peerNrAsCpt = nr; |
|||
} else { |
|||
throw std::invalid_argument("setPeerNrAsCpt(" + std::to_string(nr) + ") - out of range"); |
|||
} |
|||
} |
|||
|
|||
TestIdent& PityPerspective::getCpt() |
|||
{ |
|||
return peers.at(peerNrAsCpt); |
|||
} |
|||
|
|||
Group* PityPerspective::getGroup(const std::string& addr) |
|||
{ |
|||
for (int i = 0; i < groups.size(); i++) { |
|||
if (groups.at(i).addr == addr) { |
|||
return &groups.at(i); |
|||
} |
|||
} |
|||
throw std::invalid_argument("getGroup(" + addr + ") - not found"); |
|||
} |
|||
|
|||
} // namespace PityTest11
|
|||
} // namespace pEp
|
@ -0,0 +1,59 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef PITYTEST_PITYPERSPECTIVE_HH |
|||
#define PITYTEST_PITYPERSPECTIVE_HH |
|||
|
|||
#include "../../../src/pEpLog.hh" |
|||
#include "../../framework/utils.hh" |
|||
#include "PityModel.hh" |
|||
#include <map> |
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
|
|||
// Group
|
|||
struct Group { |
|||
std::string addr; |
|||
std::string moderator; |
|||
std::vector<TestIdent> members; |
|||
|
|||
}; |
|||
|
|||
class PityPerspective { |
|||
public: |
|||
// Constructors
|
|||
PityPerspective(const PityModel& model); |
|||
|
|||
// Lets grant access to the whole model too
|
|||
const PityModel& model; |
|||
|
|||
TestIdent* getPeer(const std::string& addr); |
|||
// Perspective
|
|||
std::string own_name; |
|||
// TestIdent* cpt = nullptr;
|
|||
void setPeerNrAsCpt(int nr); |
|||
TestIdent& getCpt(); |
|||
std::vector<TestIdent> peers; |
|||
|
|||
Test::Utils::pEpIdent own_ident; |
|||
// Test::Utils::pEpIdent cpt_ident;
|
|||
|
|||
// Groups
|
|||
Group* getGroup(const std::string& addr); |
|||
std::vector<Group> groups; |
|||
|
|||
//Callbacks
|
|||
//internal logging
|
|||
static bool debug_log_enabled; |
|||
Adapter::pEpLog::pEpLogger logger_debug{ "PityNode", debug_log_enabled }; |
|||
|
|||
private: |
|||
int peerNrAsCpt; |
|||
//internal logging
|
|||
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger_debug; |
|||
}; |
|||
}; // namespace PityTest11
|
|||
}; // namespace pEp
|
|||
|
|||
#endif // PITYTEST_PITYPERSPECTIVE_HH
|
@ -0,0 +1,146 @@ |
|||
#include "PitySwarm.hh" |
|||
#include "PityModel.hh" |
|||
#include "PityPerspective.hh" |
|||
#include "PityUnit.hh" |
|||
|
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <functional> |
|||
#include <memory> |
|||
#include <stdlib.h> |
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
bool PitySwarm::debug_log_enabled = false; |
|||
|
|||
PitySwarm::PitySwarm(const std::string& name, PityModel& model) : |
|||
_model{ model }, _swarmUnit{ name, |
|||
nullptr, |
|||
nullptr, |
|||
PityUnit<>::ExecutionMode::PROCESS_SEQUENTIAL } |
|||
{ |
|||
logger_debug.set_instancename(name); |
|||
pEpLogClass("called"); |
|||
|
|||
for (auto n : _model.nodes()) { |
|||
TestUnit* tmp = &_swarmUnit.addNew<TestUnit>( |
|||
n->getName(), |
|||
std::bind( |
|||
&PitySwarm::_init_process, |
|||
this, |
|||
std::placeholders::_1, |
|||
std::placeholders::_2), |
|||
nullptr, |
|||
TestUnit::ExecutionMode::PROCESS_PARALLEL); |
|||
// By value copies the context into the TestUnit
|
|||
tmp->setContext(_createPerspective(_model, n->getNr())); |
|||
_nodeUnits.insert(std::pair<int, TestUnit*>(n->getNr(), tmp)); |
|||
} |
|||
} |
|||
|
|||
PitySwarm::PitySwarm(const PitySwarm& rhs, const std::string& new_name) : |
|||
_model{ rhs._model }, _swarmUnit{ new_name } |
|||
{ |
|||
logger_debug.set_instancename(new_name); |
|||
_swarmUnit = TestUnit(rhs._swarmUnit); |
|||
// TODO: Hack for some reason ExecMode is getting copied,
|
|||
// Copy of Swarm is _b0rken
|
|||
_swarmUnit.setExecMode(PityUnit<>::ExecutionMode::PROCESS_SEQUENTIAL); |
|||
_swarmUnit.setName(new_name); |
|||
for (auto n : rhs._nodeUnits) { |
|||
TestUnit* tmp = &_swarmUnit.addCopy(TestUnit(*n.second)); |
|||
_nodeUnits.insert(std::pair<int, TestUnit*>(n.first, tmp)); |
|||
} |
|||
} |
|||
|
|||
PitySwarm::TestUnit& PitySwarm::getSwarmUnit() |
|||
{ |
|||
return _swarmUnit; |
|||
} |
|||
|
|||
PitySwarm::TestUnit& PitySwarm::getLeafUnit(int nodeNr) |
|||
{ |
|||
TestUnit* ret = nullptr; |
|||
TestUnit* current = _nodeUnits.at(nodeNr); |
|||
do { |
|||
if (current == nullptr) { |
|||
throw std::runtime_error("bad fatal cast in the ugly hack"); |
|||
} |
|||
if (current->getChildCount() == 0) { |
|||
ret = current; |
|||
} else { |
|||
current = dynamic_cast<TestUnit*>( |
|||
&(current->getChildRefs().begin()->second)); // random child
|
|||
} |
|||
} while (ret == nullptr); |
|||
return *ret; |
|||
} |
|||
|
|||
PitySwarm::TestUnit& PitySwarm::addTestUnit(int nodeNr, const TestUnit& unit) |
|||
{ |
|||
TestUnit& ret = getLeafUnit(nodeNr).addCopy(std::move(unit)); |
|||
return ret; |
|||
} |
|||
|
|||
void PitySwarm::run() |
|||
{ |
|||
_swarmUnit.run(); |
|||
} |
|||
|
|||
// The perspective currently is completely defined by specifying a node,
|
|||
// since there is a 1-1 node/ident relationship currently
|
|||
PityPerspective PitySwarm::_createPerspective(const PityModel& model, int node_nr) |
|||
{ |
|||
PityPerspective psp{ model }; |
|||
psp.own_name = model.nodeNr(node_nr)->getName(); |
|||
|
|||
// Create peers, everyone but me
|
|||
for (int i = 0; i < model.nodes().size(); i++) { |
|||
if (i != node_nr) { |
|||
psp.peers.push_back(TestIdent(model.nodes().at(i)->getIdent())); |
|||
} |
|||
} |
|||
|
|||
// Default partner is next node, its a circle
|
|||
// int partner_node_index = (node_nr + 1) % model.nodes().size();
|
|||
// psp.cpt_name = model.nodes().at(partner_node_index)->getName();
|
|||
|
|||
//Default partner is node 0
|
|||
if(model.nodes().size() > 1) { |
|||
if (node_nr == 0) { |
|||
psp.setPeerNrAsCpt(0); |
|||
} else { |
|||
for (int i = 0; i < psp.peers.size(); i++) { |
|||
if (psp.peers.at(i).addr == model.nodeNr(0)->getIdent().addr) { |
|||
psp.setPeerNrAsCpt(i); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Groups
|
|||
int grpOwneNode = 0; |
|||
Group grp1 = Group{}; |
|||
grp1.addr = "grp_" + model.nodeNr(grpOwneNode)->getName(); |
|||
grp1.moderator = model.nodeNr(grpOwneNode)->getName(); |
|||
// Create peers, everyone but me
|
|||
for (int i = 0; i < model.nodes().size(); i++) { |
|||
if (i != grpOwneNode) { |
|||
grp1.members.push_back(TestIdent(model.nodes().at(i)->getIdent())); |
|||
} |
|||
} |
|||
psp.groups.push_back(grp1); |
|||
|
|||
return psp; |
|||
} |
|||
|
|||
int PitySwarm::_init_process(TestUnit& unit, PityPerspective* ctx) |
|||
{ |
|||
// This should not be needed
|
|||
// std::cout << "Node _initProcUnitNrRecurse, setting $HOME" << std::endl;
|
|||
// std::string home = unit.processDir();
|
|||
// setenv("HOME", home.c_str(), true);
|
|||
return 0; |
|||
} |
|||
} // namespace PityTest11
|
|||
} // namespace pEp
|
@ -0,0 +1,59 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef PITYTEST_PITYSWARM_HH |
|||
#define PITYTEST_PITYSWARM_HH |
|||
|
|||
#include "PityModel.hh" |
|||
#include "PityUnit.hh" |
|||
#include "PityPerspective.hh" |
|||
#include "../../../src/pEpLog.hh" |
|||
#include <vector> |
|||
#include <memory> |
|||
#include <functional> |
|||
|
|||
// PitySwarm creates a swarm of independent process nodes.
|
|||
// Each node has its own perspective
|
|||
// The perspective is a derivative of the model
|
|||
// The model is the objective reality
|
|||
// The perspective is the subjective reality
|
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
class PitySwarm { |
|||
public: |
|||
using TestUnit = PityUnit<PityPerspective>; |
|||
// Constructors
|
|||
explicit PitySwarm(const std::string& name, PityModel& model); |
|||
|
|||
PitySwarm(const PitySwarm& rhs, const std::string& new_name); |
|||
|
|||
TestUnit& addTestUnit(int nodeNr, const TestUnit& unit); |
|||
|
|||
TestUnit& getSwarmUnit(); |
|||
PitySwarm::TestUnit& getLeafUnit(int nodeNr); |
|||
void run(); |
|||
|
|||
|
|||
|
|||
//internal logging
|
|||
static bool debug_log_enabled; |
|||
Adapter::pEpLog::pEpLogger logger_debug{ "PitySwarm", debug_log_enabled }; |
|||
|
|||
private: |
|||
// methods
|
|||
PityPerspective _createPerspective(const PityModel& model, int node_nr); |
|||
int _init_process(TestUnit& unit, PityPerspective* ctx); |
|||
|
|||
// fields
|
|||
PityModel& _model; |
|||
TestUnit _swarmUnit; |
|||
// each node has
|
|||
std::map<int, TestUnit*> _nodeUnits; |
|||
//internal logging
|
|||
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger_debug; |
|||
}; |
|||
}; // namespace PityTest11
|
|||
}; // namespace pEp
|
|||
|
|||
#endif // PITYTEST_PITYSWARM_HH
|
@ -0,0 +1,32 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef PITYTEST_PITYTEST_HH |
|||
#define PITYTEST_PITYTEST_HH |
|||
|
|||
#include "PityUnit.hh" |
|||
#include "PityModel.hh" |
|||
#include "PitySwarm.hh" |
|||
#include "PityPerspective.hh" |
|||
|
|||
#ifndef PITYASSERT |
|||
#define PITYASSERT(condition, msg) \ |
|||
do { \ |
|||
if (!(condition)) { \ |
|||
throw PityAssertException(msg); \ |
|||
} \ |
|||
} while (0); |
|||
#endif |
|||
|
|||
#define PITYASSERT_THROWS(func, msg) \ |
|||
do { \ |
|||
try { \ |
|||
(func); \ |
|||
PITYASSERT(false, msg); \ |
|||
} catch (const PityAssertException& pae) { \ |
|||
throw(pae); \ |
|||
} catch (const std::exception& e) { \ |
|||
} catch (...) { \ |
|||
} \ |
|||
} while (0); |
|||
#endif |
@ -0,0 +1,97 @@ |
|||
#include "PityTransport.hh" |
|||
#include "PityUnit.hh" |
|||
#include "../../../src/std_utils.hh" |
|||
#include "iostream" |
|||
#include <random> |
|||
#include <fstream> |
|||
#include <memory> |
|||
|
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
bool PityTransport::debug_log_enabled = false; |
|||
|
|||
PityTransport::PityTransport(std::string inboxDir, Endpoints& endpoints) : |
|||
_inboxDir{ inboxDir }, _endpoints{ endpoints } |
|||
{ |
|||
} |
|||
|
|||
void PityTransport::sendMsg(const std::string nodename, const std::string& msg) const |
|||
{ |
|||
pEpLogClass("Address: " + nodename + " msg: " + msg); |
|||
|
|||
// HACK TODO
|
|||
std::string nodename_normalized = AbstractPityUnit::_normalizeName(nodename); |
|||
|
|||
bool found = false; |
|||
std::string dir; |
|||
try { |
|||
dir = _endpoints.at(nodename_normalized); |
|||
} catch (std::out_of_range&) { |
|||
throw std::runtime_error("no such nodename: " + nodename_normalized); |
|||
} |
|||
Utils::dir_ensure(dir); |
|||
std::stringstream filename; |
|||
// collision detect
|
|||
do { |
|||
filename << dir << Utils::random_string(97, 122, 16) << ".pitymsg"; |
|||
} while (Utils::path_exists(filename.str())); |
|||
// create
|
|||
std::ofstream msgfile = Utils::file_create(filename.str()); |
|||
// write
|
|||
msgfile << msg; |
|||
} |
|||
|
|||
bool PityTransport::hasMsg() const |
|||
{ |
|||
bool ret = false; |
|||
pEpLogClass("called"); |
|||
Utils::dir_ensure(_inboxDir); |
|||
auto msg_filenames = Utils::dir_list_files(_inboxDir); |
|||
ret = msg_filenames.size() > 0; |
|||
return ret; |
|||
} |
|||
|
|||
// Non-blocking
|
|||
// throws underflow_error if inbox empty
|
|||
std::string PityTransport::pollMsg() const |
|||
{ |
|||
pEpLogClass("called"); |
|||
std::string ret; |
|||
Utils::dir_ensure(_inboxDir); |
|||
auto msg_filenames = Utils::dir_list_files(_inboxDir); |
|||
if (!msg_filenames.empty()) { |
|||
std::string msg_filename = msg_filenames.at(0); |
|||
std::string msg_path = _inboxDir + "/" + msg_filename; |
|||
pEpLogClass("Reading file: " + msg_filename); |
|||
ret = Utils::file_read(msg_path); |
|||
Utils::path_delete(msg_path); |
|||
} else { |
|||
throw std::underflow_error("inbox empty: " + _inboxDir); |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
// Blocking
|
|||
// Returns when a msg has been received
|
|||
std::string PityTransport::receiveMsg(int poll_interval) const |
|||
{ |
|||
pEpLogClass("called"); |
|||
std::string ret; |
|||
bool retry = false; |
|||
do { |
|||
try { |
|||
ret = pollMsg(); |
|||
retry = false; |
|||
} catch (const std::underflow_error&) { |
|||
pEpLogClass("polling again in [ms]: " + std::to_string(poll_interval) + "..."); |
|||
Utils::sleep_millis(poll_interval); |
|||
retry = true; |
|||
} |
|||
} while (retry); |
|||
return ret; |
|||
} |
|||
|
|||
} // namespace PityTest11
|
|||
} // namespace pEp
|
@ -0,0 +1,45 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef PITYTEST_PITYTRANSPORT_HH |
|||
#define PITYTEST_PITYTRANSPORT_HH |
|||
|
|||
#include "../../../src/pEpLog.hh" |
|||
#include <vector> |
|||
#include <memory> |
|||
#include <unordered_map> |
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
// Address - Dir
|
|||
using Endpoints = std::unordered_map<std::string, std::string>; |
|||
|
|||
class PityTransport { |
|||
public: |
|||
// Constructors
|
|||
PityTransport() = delete; |
|||
explicit PityTransport(std::string inboxDir, Endpoints& endpoints); |
|||
|
|||
// Getters
|
|||
//Transport
|
|||
bool hasMsg() const; |
|||
void sendMsg(const std::string nodename, const std::string& msg) const; |
|||
std::string pollMsg() const; |
|||
std::string receiveMsg(int poll_interval = 100) const; |
|||
|
|||
//internal logging
|
|||
static bool debug_log_enabled; |
|||
Adapter::pEpLog::pEpLogger logger_debug{ "PityTransport", debug_log_enabled }; |
|||
|
|||
private: |
|||
std::string _inboxDir; |
|||
Endpoints& _endpoints; |
|||
|
|||
//internal logging
|
|||
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger_debug; |
|||
}; |
|||
}; // namespace PityTest11
|
|||
|
|||
}; // namespace pEp
|
|||
|
|||
#endif // PITYTEST_PITYTRANSPORT_HH
|
@ -0,0 +1,88 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef PITYTEST_PITYTREE_HH |
|||
#define PITYTEST_PITYTREE_HH |
|||
|
|||
//#include "../../../src/pEpLog.hh"
|
|||
#include <string> |
|||
#include <map> |
|||
#include <memory> |
|||
#include <functional> |
|||
#include <type_traits> |
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
template<class T> |
|||
class PityTree { |
|||
// TODO: NEEEEED THIS
|
|||
// static_assert(std::is_base_of<PityTree<T>, T>::value, "PityTree<T> must be a base of T");
|
|||
public: |
|||
using ChildObj = std::shared_ptr<T>; |
|||
using ChildObjs = std::vector<ChildObj>; |
|||
using ChildRef = std::pair<const std::string, T&>; |
|||
using ChildRefs = std::map<const std::string, T&>; |
|||
|
|||
// Constructors
|
|||
explicit PityTree(T& self, const std::string& name); |
|||
explicit PityTree(T& self, const std::string& name, T& parent); |
|||
explicit PityTree(const PityTree& rhs, T& owner); |
|||
|
|||
// copy-assign
|
|||
// PityTree& operator=(const PityTree<T>& rhs);
|
|||
|
|||
// clone
|
|||
virtual PityTree* clone() = 0; |
|||
|
|||
// Append
|
|||
// creates a new instance of CT, add the new instance as child and returns a ref to it
|
|||
template<typename CT, typename... Args> |
|||
CT& addNew(Args&&... args); |
|||
|
|||
// Creates a copy, adds the copy as child and returns a ref to it
|
|||
template<typename CT> |
|||
CT& addCopy(const CT&& child, const std::string& new_name = ""); |
|||
|
|||
template<typename CT> |
|||
CT& addCopy(const CT& child, const std::string& new_name = ""); |
|||
|
|||
// Just adds child as a non-owned reference.
|
|||
T& addRef(T& child); |
|||
|
|||
// Query
|
|||
virtual T& getSelf() = 0; |
|||
T* getParent() const; |
|||
ChildRefs getChildRefs() const; |
|||
int getChildCount() const; |
|||
T& getChild(const std::string& name); |
|||
T& getRoot(); |
|||
|
|||
void setName(const std::string& name); |
|||
std::string getName() const; |
|||
std::string getPath() const; |
|||
bool isRoot() const; // true if has no parent
|
|||
|
|||
std::string to_string(bool recursive = true, int indent = 0); |
|||
|
|||
//TODO HACK in PityTransport, this should be private
|
|||
static std::string _normalizeName(std::string name); |
|||
|
|||
virtual ~PityTree() = default; |
|||
|
|||
protected: |
|||
void setParent(T* const parent); |
|||
|
|||
private: |
|||
void _copyChildRefs(const PityTree<T>& rhs); |
|||
// Fields
|
|||
std::string _nodename; |
|||
T& _self; |
|||
T* _parent; //nullptr if RootUnit
|
|||
ChildRefs _childrefs; // map to guarantee uniqueness of sibling-names
|
|||
ChildObjs _childobjs; |
|||
}; |
|||
}; // namespace PityTest11
|
|||
}; // namespace pEp
|
|||
|
|||
#include "PityTree.hxx" |
|||
#endif |
@ -0,0 +1,220 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef PITYTEST_PITYTREE_HXX |
|||
#define PITYTEST_PITYTREE_HXX |
|||
|
|||
#include "PityTree.hh" |
|||
#include <memory> |
|||
#include <sstream> |
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
|
|||
// RootNode
|
|||
template<class T> |
|||
PityTree<T>::PityTree(T& self, const std::string& name) : |
|||
_self{ self }, _parent{ nullptr }, _nodename{ _normalizeName(name) }, _childrefs{}, |
|||
_childobjs{} |
|||
{ |
|||
} |
|||
|
|||
// LeafNode
|
|||
template<class T> |
|||
PityTree<T>::PityTree(T& self, const std::string& name, T& parent) : |
|||
_self(self), _parent{ nullptr }, _nodename{ _normalizeName(name) }, _childrefs{}, |
|||
_childobjs{} |
|||
{ |
|||
parent.addRef(_self); |
|||
} |
|||
|
|||
// Copy
|
|||
template<class T> |
|||
PityTree<T>::PityTree(const PityTree<T>& rhs, T& owner) : _self{ owner } |
|||
{ |
|||
_nodename = rhs._nodename; |
|||
_parent = nullptr; |
|||
_copyChildRefs(rhs); |
|||
} |
|||
|
|||
// template<class T>
|
|||
// PityTree<T>& PityTree<T>::operator=(const PityTree<T>& rhs)
|
|||
// {
|
|||
// _nodename = rhs._nodename;
|
|||
// _parent = nullptr;
|
|||
// _copyChildRefs(rhs);
|
|||
// return *this;
|
|||
// }
|
|||
//
|
|||
template<typename T> |
|||
template<typename CT, typename... Args> |
|||
CT& PityTree<T>::addNew(Args&&... args) |
|||
{ |
|||
static_assert(std::is_base_of<T, CT>::value, "T must be base of CT"); |
|||
std::shared_ptr<CT> tmp = std::make_shared<CT>(std::forward<Args>(args)...); |
|||
_childobjs.push_back(tmp); |
|||
addRef(*tmp.get()); |
|||
return *tmp.get(); |
|||
} |
|||
|
|||
template<typename T> |
|||
template<typename CT> |
|||
CT& PityTree<T>::addCopy(const CT&& child, const std::string& new_name) |
|||
{ |
|||
static_assert(std::is_base_of<T, CT>::value, "PityTree<T> must be a base of T"); |
|||
CT* tmpraw = new CT(child); |
|||
_childobjs.push_back(ChildObj(tmpraw)); |
|||
if (new_name != "") { |
|||
tmpraw->setName(new_name); |
|||
} |
|||
addRef(*tmpraw); |
|||
return *tmpraw; |
|||
} |
|||
|
|||
template<typename T> |
|||
template<typename CT> |
|||
CT& PityTree<T>::addCopy(const CT& child, const std::string& new_name) |
|||
{ |
|||
return addCopy(std::move(child)); |
|||
} |
|||
|
|||
template<class T> |
|||
T& PityTree<T>::addRef(T& child) |
|||
{ |
|||
child.setParent(&_self); |
|||
_childrefs.insert(ChildRef(child.getName(), child)); |
|||
return child; |
|||
} |
|||
|
|||
template<class T> |
|||
void PityTree<T>::setParent(T* parent) |
|||
{ |
|||
_parent = parent; |
|||
} |
|||
|
|||
template<class T> |
|||
T* PityTree<T>::getParent() const |
|||
{ |
|||
return _parent; |
|||
} |
|||
|
|||
template<class T> |
|||
bool PityTree<T>::isRoot() const |
|||
{ |
|||
if (_parent == nullptr) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
template<class T> |
|||
void PityTree<T>::setName(const std::string& name) |
|||
{ |
|||
_nodename = name; |
|||
} |
|||
|
|||
template<class T> |
|||
std::string PityTree<T>::getName() const |
|||
{ |
|||
return _nodename; |
|||
} |
|||
|
|||
template<class T> |
|||
std::string PityTree<T>::getPath() const |
|||
{ |
|||
std::string ret; |
|||
|
|||
if (!isRoot()) { |
|||
ret = _parent->getPath() + "/" + getName(); |
|||
} else { |
|||
ret = getName(); |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
template<class T> |
|||
std::string PityTree<T>::to_string(bool recursive, int indent) |
|||
{ |
|||
std::string ret; |
|||
std::stringstream builder; |
|||
builder << std::string(indent * 4, ' '); |
|||
builder << getName(); |
|||
builder << std::endl; |
|||
ret = builder.str(); |
|||
|
|||
if (recursive) { |
|||
if (!getChildRefs().empty()) { |
|||
indent++; |
|||
for (ChildRef child : getChildRefs()) { |
|||
ret += child.second.to_string(true, indent); |
|||
} |
|||
indent--; |
|||
} |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
template<class T> |
|||
T& PityTree<T>::getRoot() |
|||
{ |
|||
if (!isRoot()) { |
|||
return _parent->getRoot(); |
|||
} else { |
|||
return _self; |
|||
} |
|||
} |
|||
|
|||
template<class T> |
|||
typename PityTree<T>::ChildRefs PityTree<T>::getChildRefs() const |
|||
{ |
|||
return _childrefs; |
|||
} |
|||
|
|||
template<class T> |
|||
T& PityTree<T>::getChild(const std::string& name) |
|||
{ |
|||
T* ret = nullptr; |
|||
try { |
|||
ret = &getChildRefs().at(name); |
|||
} catch (const std::exception& e) { |
|||
throw std::invalid_argument("PityNode not found: '" + name + "'"); |
|||
} |
|||
return *ret; |
|||
} |
|||
|
|||
// name is alphanumeric only (everything else will be replaced by an underscore)
|
|||
// static
|
|||
template<class T> |
|||
std::string PityTree<T>::_normalizeName(std::string name) |
|||
{ |
|||
replace_if( |
|||
name.begin(), |
|||
name.end(), |
|||
[](char c) -> bool { return !isalnum(c); }, |
|||
'_'); |
|||
|
|||
return name; |
|||
} |
|||
|
|||
// When you copy a treenode, you need to create a copy of all children
|
|||
// and take ownership
|
|||
template<class T> |
|||
void PityTree<T>::_copyChildRefs(const PityTree<T>& rhs) |
|||
{ |
|||
for (const ChildRef& cr : rhs.getChildRefs()) { |
|||
_childobjs.push_back(ChildObj(cr.second.clone())); |
|||
T& ret = *_childobjs.back().get(); |
|||
addRef(ret); |
|||
} |
|||
} |
|||
|
|||
template<class T> |
|||
int PityTree<T>::getChildCount() const |
|||
{ |
|||
return _childrefs.size(); |
|||
} |
|||
} // namespace PityTest11
|
|||
} // namespace pEp
|
|||
|
|||
#endif |
@ -0,0 +1,75 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef PITYTEST_PITYUNIT_HH |
|||
#define PITYTEST_PITYUNIT_HH |
|||
|
|||
#include "../../../src/pEpLog.hh" |
|||
#include "../../../src/std_utils.hh" |
|||
#include "AbstractPityUnit.hh" |
|||
#include "fs_mutex.hh" |
|||
#include "PityTransport.hh" |
|||
#include <string> |
|||
#include <memory> |
|||
#include <functional> |
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
//TODO We need a context basetype
|
|||
class PityBaseCTX { |
|||
}; |
|||
|
|||
template<class TestContext = PityBaseCTX> |
|||
class PityUnit : public AbstractPityUnit { |
|||
public: |
|||
// Test success if TestFunction:
|
|||
// * does not throw
|
|||
// * returns 0
|
|||
using TestFunction = std::function<int(PityUnit<TestContext>&, TestContext*)>; |
|||
|
|||
// Constructors
|
|||
PityUnit() = delete; |
|||
explicit PityUnit<TestContext>( |
|||
const std::string& name, |
|||
TestFunction test_func = nullptr, |
|||
TestContext* ctx = nullptr, |
|||
ExecutionMode exec_mode = ExecutionMode::FUNCTION); |
|||
|
|||
explicit PityUnit<TestContext>( |
|||
AbstractPityUnit& parent, |
|||
const std::string& name, |
|||
TestFunction test_func = nullptr, |
|||
TestContext* ctx = nullptr, |
|||
ExecutionMode exec_mode = ExecutionMode::FUNCTION); |
|||
|
|||
PityUnit<TestContext>(const PityUnit<TestContext>& rhs); |
|||
|
|||
// copy-assign
|
|||
PityUnit<TestContext>& operator=(const PityUnit<TestContext>& rhs); |
|||
|
|||
PityUnit<TestContext>& getSelf() override; |
|||
// clone
|
|||
PityUnit<TestContext>* clone() override; |
|||
|
|||
void setContext(TestContext* ctx); |
|||
void setContext(TestContext ctx); |
|||
TestContext* getContext() const; |
|||
|
|||
protected: |
|||
void _runSelf() override; |
|||
|
|||
private: |
|||
void _copyContext(const PityUnit<TestContext>& rhs); |
|||
|
|||
// Fields
|
|||
// nullptr if inherited
|
|||
TestContext* _ctx; |
|||
std::shared_ptr<TestContext> _owned_ctx; // if you copy
|
|||
TestFunction _test_func; |
|||
}; |
|||
}; // namespace PityTest11
|
|||
}; // namespace pEp
|
|||
|
|||
#include "PityUnit.hxx" |
|||
|
|||
#endif // PITYTEST_PITYUNIT_HH
|
@ -0,0 +1,136 @@ |
|||
// 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 "PityUnit.hh" |
|||
|
|||
#include <iostream> |
|||
#include <unistd.h> |
|||
#include <cstdlib> |
|||
#include <sys/stat.h> |
|||
#include <functional> |
|||
#include <algorithm> |
|||
#include <exception> |
|||
#include <memory> |
|||
#include <sys/wait.h> |
|||
|
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
|
|||
template<class TestContext> |
|||
PityUnit<TestContext>::PityUnit( |
|||
const std::string &name, |
|||
TestFunction test_func, |
|||
TestContext *ctx, |
|||
ExecutionMode exec_mode) : |
|||
AbstractPityUnit(name, exec_mode), |
|||
_ctx{ ctx }, _test_func{ test_func } |
|||
{ |
|||
} |
|||
|
|||
template<class TestContext> |
|||
PityUnit<TestContext>::PityUnit( |
|||
AbstractPityUnit &parent, |
|||
const std::string &name, |
|||
TestFunction test_func, |
|||
TestContext *ctx, |
|||
ExecutionMode exec_mode) : |
|||
AbstractPityUnit(parent, name, exec_mode), |
|||
_ctx{ ctx }, _test_func{ test_func } |
|||
{ |
|||
} |
|||
|
|||
template<class TestContext> |
|||
PityUnit<TestContext>::PityUnit(const PityUnit<TestContext> &rhs) : |
|||
AbstractPityUnit(rhs, *this) |
|||
{ |
|||
_copyContext(rhs); |
|||
_test_func = TestFunction(rhs._test_func); |
|||
} |
|||
|
|||
template<class TestContext> |
|||
PityUnit<TestContext> &PityUnit<TestContext>::operator=(const PityUnit<TestContext> &rhs) |
|||
{ |
|||
_copyContext(rhs); |
|||
_test_func = TestFunction(rhs._test_func); |
|||
return *this; |
|||
} |
|||
|
|||
template<class TestContext> |
|||
PityUnit<TestContext> &PityUnit<TestContext>::getSelf() |
|||
{ |
|||
return *this; |
|||
} |
|||
|
|||
template<class TestContext> |
|||
PityUnit<TestContext> *PityUnit<TestContext>::clone() |
|||
{ |
|||
return new PityUnit<TestContext>(*this); |
|||
} |
|||
|
|||
template<class TestContext> |
|||
void PityUnit<TestContext>::_runSelf() |
|||
{ |
|||
if (_test_func != nullptr) { |
|||
try { |
|||
_test_func(*this, getContext()); |
|||
logH3(_status_string("\033[1m\033[32mSUCCESS" + Utils::to_termcol(_color()))); |
|||
} catch (const std::exception &e) { |
|||
_logRaw("reason: " + std::string(e.what())); |
|||
logH3(_status_string("\033[1m\033[31mFAILED" + Utils::to_termcol(_color()))); |
|||
} |
|||
} else { |
|||
_logRaw("No function to execute"); |
|||
} |
|||
} |
|||
|
|||
// Inherited (if null see parent recursively)
|
|||
template<class TestContext> |
|||
TestContext *PityUnit<TestContext>::getContext() const |
|||
{ |
|||
pEpLogClass("called"); |
|||
TestContext *ret = nullptr; |
|||
|
|||
if (_ctx != nullptr) { |
|||
ret = _ctx; |
|||
} else { |
|||
if (!isRoot()) { |
|||
ret = (dynamic_cast<PityUnit<TestContext> *>(getParent()))->getContext(); |
|||
} |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
template<class TestContext> |
|||
void PityUnit<TestContext>::_copyContext(const PityUnit<TestContext> &rhs) |
|||
{ |
|||
auto *tmp = rhs.getContext(); |
|||
if (tmp != nullptr) { |
|||
_owned_ctx = std::shared_ptr<TestContext>(new TestContext(*tmp)); |
|||
_ctx = tmp; |
|||
} else { |
|||
_ctx = nullptr; |
|||
} |
|||
} |
|||
|
|||
template<class TestContext> |
|||
void PityUnit<TestContext>::setContext(TestContext *ctx) |
|||
{ |
|||
_ctx = ctx; |
|||
} |
|||
|
|||
template<class TestContext> |
|||
void PityUnit<TestContext>::setContext(TestContext ctx) |
|||
{ |
|||
_owned_ctx = std::shared_ptr<TestContext>(new TestContext(ctx)); |
|||
_ctx = _owned_ctx.get(); |
|||
} |
|||
} // namespace PityTest11
|
|||
} // namespace pEp
|
|||
|
|||
|
|||
#endif // PITYTEST_PITYUNIT_HXX
|
@ -0,0 +1,41 @@ |
|||
#include "fs_mutex.hh" |
|||
#include "../../../src/std_utils.hh" |
|||
#include<fstream> |
|||
|
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
fs_mutex::fs_mutex(std::string mutexpath) : mutexpath{ mutexpath } {} |
|||
|
|||
void fs_mutex::aquire() const |
|||
{ |
|||
if (mutexpath.empty()) { |
|||
throw std::runtime_error("no mutexpath set"); |
|||
} else { |
|||
std::string mutex_file = mutexpath; |
|||
while (Utils::path_exists(mutex_file)) { |
|||
Utils::sleep_millis(2); |
|||
} |
|||
std::ofstream msgfile = Utils::file_create(mutexpath); |
|||
} |
|||
} |
|||
|
|||
void fs_mutex::release() const |
|||
{ |
|||
if (mutexpath.empty()) { |
|||
throw std::runtime_error("no mutexpath set"); |
|||
} else { |
|||
|
|||
try { |
|||
Utils::path_delete(mutexpath); |
|||
// Give others a chance to pickup
|
|||
Utils::sleep_millis(4); |
|||
} catch (...) { |
|||
// pEpLogClass("Error releasing fsmutex");
|
|||
} |
|||
} |
|||
} |
|||
|
|||
} // namespace PityTest
|
|||
} // namespace pEp
|
|||
|
@ -0,0 +1,25 @@ |
|||
#ifndef FS_MUTEX |
|||
#define FS_MUTEX |
|||
|
|||
#include <iostream> |
|||
|
|||
namespace pEp { |
|||
namespace PityTest11 { |
|||
// a very primitive IPC sync method
|
|||
// also unreliable
|
|||
// but good enough for what i just needed it for
|
|||
class fs_mutex { |
|||
public: |
|||
fs_mutex() = delete; |
|||
fs_mutex(std::string mutexpath); |
|||
|
|||
void aquire() const; |
|||
void release() const; |
|||
|
|||
private: |
|||
const std::string mutexpath; |
|||
}; |
|||
} // namespace PityTest11
|
|||
} // namespace pEp
|
|||
|
|||
#endif // FS_MUTEX
|
@ -0,0 +1,32 @@ |
|||
#include "../src/PityTest.hh" |
|||
#include <iostream> |
|||
#include <exception> |
|||
|
|||
using namespace pEp::PityTest11; |
|||
void not_throwing() {} |
|||
|
|||
void throwing() |
|||
{ |
|||
throw std::runtime_error{ "Fsd" }; |
|||
} |
|||
|
|||
int main(int argc, char* argv[]) |
|||
{ |
|||
|
|||
PITYASSERT(true, "thats wrong"); |
|||
try { |
|||
PITYASSERT(false, "thats correct"); |
|||
throw std::runtime_error("PITYASSERT(false) does not throw"); |
|||
} catch (const PityAssertException& pae) { |
|||
} |
|||
|
|||
PITYASSERT(true, "thats wrong"); |
|||
|
|||
PITYASSERT_THROWS(throwing(), "is actually throwing "); |
|||
|
|||
try { |
|||
PITYASSERT_THROWS(not_throwing(), "is actually not throwing"); |
|||
throw std::runtime_error("PITYASSERT(false) does not throw"); |
|||
} catch (const PityAssertException& pae) { |
|||
} |
|||
} |
@ -0,0 +1,94 @@ |
|||
#include "../src/PityTest.hh" |
|||
#include "../../../src/utils.hh" |
|||
#include <iostream> |
|||
|
|||
using namespace std; |
|||
using namespace pEp; |
|||
using namespace pEp::PityTest11; |
|||
|
|||
|
|||
struct CTXExecmodes { |
|||
int sleepmilis; |
|||
int rep_count; |
|||
}; |
|||
|
|||
using TestContext = CTXExecmodes; |
|||
using TestUnit = PityUnit<TestContext>; |
|||
|
|||
int do_some_work(TestUnit& pity, TestContext* ctx) |
|||
{ |
|||
int i = 0; |
|||
while (i < ctx->rep_count) { |
|||
pity.log(pity.getName() + " - " + to_string(i)); |
|||
Utils::sleep_millis(ctx->sleepmilis); |
|||
i++; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
int main(int argc, char* argv[]) |
|||
{ |
|||
// DEBUG Logging of PityTestUnit itself
|
|||
TestUnit::debug_log_enabled = false; |
|||
CTXExecmodes ctxe; |
|||
ctxe.sleepmilis = 100; |
|||
ctxe.rep_count = 10; |
|||
|
|||
// NEW API
|
|||
{ |
|||
// Suite
|
|||
TestUnit suite = TestUnit("Test Execution Model"); |
|||
|
|||
// Groups
|
|||
auto grp1 = suite.addNew<TestUnit>( |
|||
"grp1", |
|||
do_some_work, |
|||
&ctxe, |
|||
TestUnit::ExecutionMode::PROCESS_PARALLEL); |
|||
|
|||
auto grp2 = suite.addNew<TestUnit>( |
|||
"grp2", |
|||
do_some_work, |
|||
&ctxe, |
|||
TestUnit::ExecutionMode::PROCESS_PARALLEL); |
|||
|
|||
// Units
|
|||
grp1.addNew<TestUnit>("test1.1", do_some_work); |
|||
grp1.addNew<TestUnit>("test1.2", do_some_work); |
|||
|
|||
// Units
|
|||
grp2.addNew<TestUnit>("unit_2.1", do_some_work); |
|||
grp2.addNew<TestUnit>("unit_2.2", do_some_work); |
|||
|
|||
suite.run(); |
|||
} |
|||
// Old API
|
|||
{ |
|||
// The RootNode is the
|
|||
TestUnit root_old = TestUnit{ "Test Execution Model" }; |
|||
|
|||
// Subprocess 1
|
|||
TestUnit test1 = TestUnit{ root_old, |
|||
"node1", |
|||
do_some_work, |
|||
&ctxe, |
|||
TestUnit::ExecutionMode::PROCESS_PARALLEL }; |
|||
|
|||
TestUnit test1_1 = TestUnit{ test1, "test1.1", do_some_work }; |
|||
|
|||
TestUnit test1_2 = TestUnit{ test1, "test1.2", do_some_work }; |
|||
|
|||
// Subprocess 2
|
|||
TestUnit test2 = TestUnit{ root_old, |
|||
"node2", |
|||
do_some_work, |
|||
&ctxe, |
|||
TestUnit::ExecutionMode::PROCESS_PARALLEL }; |
|||
|
|||
TestUnit test2_1 = TestUnit{ test2, "test2.1", do_some_work }; |
|||
|
|||
TestUnit test2_2 = TestUnit{ test2, "test2.2", do_some_work }; |
|||
|
|||
root_old.run(); |
|||
} |
|||
} |
@ -0,0 +1,68 @@ |
|||
#include "../src/PityTest.hh" |
|||
#include <iostream> |
|||
#include <algorithm> |
|||
|
|||
using namespace std; |
|||
using namespace pEp; |
|||
using namespace pEp::PityTest11; |
|||
|
|||
// eeks-test
|
|||
// The test framework for geeks without the g
|
|||
|
|||
|
|||
class Car { |
|||
private: |
|||
int gear_nr = 0; |
|||
|
|||
public: |
|||
void setGear(int nr) |
|||
{ |
|||
gear_nr = nr; |
|||
} |
|||
|
|||
void drive() |
|||
{ |
|||
if (gear_nr > 0 && gear_nr <= 6) { |
|||
cout << "cruising" << endl; |
|||
} else { |
|||
throw runtime_error{ "invalid gear" }; |
|||
} |
|||
} |
|||
}; |
|||
|
|||
class CarTestModel { |
|||
public: |
|||
CarTestModel(const string& name) : name{ name }, car{} {} |
|||
|
|||
string name{}; |
|||
Car car; |
|||
}; |
|||
|
|||
int test_setGear(PityUnit<CarTestModel>& node, CarTestModel* ctx) |
|||
{ |
|||
int gear = 1; |
|||
node.log("Setting gear to: " + to_string(gear)); |
|||
ctx->car.setGear(gear); |
|||
return 0; |
|||
} |
|||
|
|||
int test_drive(PityUnit<CarTestModel>& node, CarTestModel* ctx) |
|||
{ |
|||
ctx->car.drive(); |
|||
return 0; |
|||
} |
|||
|
|||
int main(int argc, char* argv[]) |
|||
{ |
|||
// Linear Test
|
|||
CarTestModel model{ "CarTestModel" }; |
|||
PityUnit<CarTestModel> testnode_car{ "test car", nullptr, &model }; |
|||
PityUnit<CarTestModel> testnode_setGear{ testnode_car, "test car setGear()", test_setGear }; |
|||
PityUnit<CarTestModel> testnode_driv{ testnode_setGear, "test car drive()", &test_drive }; |
|||
|
|||
PityUnit<CarTestModel> testnode_driv_before_gear{ testnode_car, |
|||
"testnode_driv_before_gear", |
|||
&test_drive }; |
|||
|
|||
testnode_car.run(); |
|||
} |
@ -0,0 +1,161 @@ |
|||
#include "../src/PityTree.hh" |
|||
#include "../src/PityTest.hh" |
|||
#include "../../../src/std_utils.hh" |
|||
#include "../../../src/utils.hh" |
|||
#include <iostream> |
|||
#include <exception> |
|||
using namespace pEp; |
|||
using namespace pEp::PityTest11; |
|||
|
|||
// -----------------------------------------------------------------------------------------------
|
|||
|
|||
class AbstractNode : public PityTree<AbstractNode> { |
|||
public: |
|||
// Constructors
|
|||
AbstractNode() = delete; |
|||
explicit AbstractNode(const std::string &name); |
|||
explicit AbstractNode(const std::string &name, AbstractNode &parent); |
|||
AbstractNode(const AbstractNode &rhs, AbstractNode &self); |
|||
|
|||
// methods
|
|||
virtual int implMe(int magic_nr) = 0; |
|||
AbstractNode& getSelf() override = 0; |
|||
AbstractNode *clone() override = 0; |
|||
|
|||
// fields
|
|||
std::string color; |
|||
int age{}; |
|||
}; |
|||
|
|||
AbstractNode::AbstractNode(const std::string &name) : PityTree<AbstractNode>(*this, name) {} |
|||
|
|||
AbstractNode::AbstractNode(const std::string &name, AbstractNode &parent) : |
|||
PityTree<AbstractNode>(*this, name, parent) |
|||
{ |
|||
} |
|||
|
|||
AbstractNode::AbstractNode(const AbstractNode &rhs, AbstractNode &self) : |
|||
PityTree<AbstractNode>(rhs, self) |
|||
{ |
|||
color = rhs.color; |
|||
age = rhs.age; |
|||
} |
|||
|
|||
|
|||
// -----------------------------------------------------------------------------------------------
|
|||
|
|||
class ANode : public AbstractNode { |
|||
public: |
|||
explicit ANode(const std::string &name); |
|||
explicit ANode(const std::string &name, AbstractNode &parent); |
|||
ANode(const ANode &rhs); |
|||
ANode &getSelf() override; |
|||
ANode *clone() override; |
|||
int implMe(int magic_nr) override; |
|||
}; |
|||
|
|||
ANode::ANode(const std::string &name) : AbstractNode(name) {} |
|||
ANode::ANode(const std::string &name, AbstractNode &parent) : AbstractNode(name, parent) {} |
|||
ANode::ANode(const ANode &rhs) : AbstractNode(rhs, *this) {} |
|||
|
|||
int ANode::implMe(int magic_nr) |
|||
{ |
|||
return 23; |
|||
} |
|||
|
|||
ANode &ANode::getSelf() |
|||
{ |
|||
return *this; |
|||
} |
|||
|
|||
ANode *ANode::clone() |
|||
{ |
|||
return new ANode(*this); |
|||
} |
|||
|
|||
// -----------------------------------------------------------------------------------------------
|
|||
|
|||
class BNode : public AbstractNode { |
|||
public: |
|||
explicit BNode(const std::string &name); |
|||
explicit BNode(const std::string &name, AbstractNode &parent); |
|||
BNode(const BNode &rhs); |
|||
BNode &getSelf() override; |
|||
BNode *clone() override; |
|||
int implMe(int magic_nr) override; |
|||
}; |
|||
|
|||
BNode::BNode(const std::string &name) : AbstractNode(name) {} |
|||
BNode::BNode(const std::string &name, AbstractNode &parent) : AbstractNode(name, parent) {} |
|||
BNode::BNode(const BNode &rhs) : AbstractNode(rhs, *this) {} |
|||
|
|||
int BNode::implMe(int magic_nr) |
|||
{ |
|||
return 42; |
|||
} |
|||
|
|||
BNode &BNode::getSelf() |
|||
{ |
|||
return *this; |
|||
} |
|||
|
|||
BNode *BNode::clone() |
|||
{ |
|||
return new BNode(*this); |
|||
} |
|||
|
|||
// -----------------------------------------------------------------------------------------------
|
|||
|
|||
void not_throwing() |
|||
{ |
|||
throw std::runtime_error{ "Fsd" }; |
|||
} |
|||
|
|||
void throwing() |
|||
{ |
|||
throw std::runtime_error{ "Fsd" }; |
|||
} |
|||
|
|||
int main(int argc, char *argv[]) |
|||
{ |
|||
// Create lone node
|
|||
ANode a{ "a" }; |
|||
std::cout << a.getPath() << std::endl; |
|||
PITYASSERT(a.isRoot() == true, "a"); |
|||
PITYASSERT(a.getName() == "a", "b"); |
|||
PITYASSERT(&(a.getRoot()) == &a, "c"); |
|||
PITYASSERT(a.getParent() == nullptr, "d"); |
|||
PITYASSERT(a.getChildRefs().size() == 0, "e"); |
|||
|
|||
// Create node here, and make it a child of another node
|
|||
BNode b{ "b", a }; |
|||
std::cout << b.getPath() << std::endl; |
|||
PITYASSERT(a.isRoot() == true, "f"); |
|||
PITYASSERT(&(a.getRoot()) == &a, "g"); |
|||
PITYASSERT(a.getParent() == nullptr, "1"); |
|||
PITYASSERT(a.getChildRefs().size() == 1, "2"); |
|||
|
|||
PITYASSERT(&b == &(a.getChild("b")), "3"); |
|||
PITYASSERT_THROWS(a.getChild("invalid"), "4"); |
|||
PITYASSERT(b.isRoot() == false, "5"); |
|||
PITYASSERT(&(b.getRoot()) == &a, "6"); |
|||
PITYASSERT(b.getParent() == &a, "7"); |
|||
PITYASSERT(b.getChildRefs().size() == 0, "8"); |
|||
|
|||
// Create a copy of the node in the parent node
|
|||
b.addNew<ANode>("c").addNew<ANode>("d"); |
|||
std::cout << a.to_string() << std::endl; |
|||
|
|||
b.addNew<BNode>("c1").addNew<BNode>("e"); |
|||
std::cout << a.to_string() << std::endl; |
|||
|
|||
b.getChild("c1").getChild("e").addCopy(ANode(a), "a1"); |
|||
std::cout << a.to_string() << std::endl; |
|||
|
|||
a.getChild("b").addCopy(ANode(a), a.getName() + "1"); |
|||
std::cout << a.to_string() << std::endl; |
|||
|
|||
ANode a2 = ANode(a); |
|||
a2.setName("a2"); |
|||
std::cout << a2.to_string() << std::endl; |
|||
} |
@ -0,0 +1,98 @@ |
|||
#include "../src/PityTest.hh" |
|||
#include <iostream> |
|||
#include <algorithm> |
|||
|
|||
using namespace std; |
|||
using namespace pEp::PityTest11; |
|||
|
|||
|
|||
using TestContext = void; |
|||
using TestUnit = PityUnit<>; |
|||
|
|||
//TODO: Add HOME testing
|
|||
void printHomeDir(TestUnit& myself) |
|||
{ |
|||
// TESTLOG(string(myself.getFQName() + " - PID: " + to_string(getpid())));
|
|||
// cout << "[" << to_string(getpid()) << "/" << myself.getFQName() << "] - " << endl;
|
|||
setenv("HOME", myself.getProcessDir().c_str(), 1); |
|||
myself.log("HOME=" + string(getenv("HOME"))); |
|||
} |
|||
|
|||
int main(int argc, char* argv[]) |
|||
{ |
|||
PityUnit<>::debug_log_enabled = false; |
|||
// Suite
|
|||
TestUnit suite = TestUnit{ "test_processdirs" }; |
|||
|
|||
// 1
|
|||
suite |
|||
.addNew<TestUnit>( |
|||
"node 1", |
|||
[](TestUnit& pity, TestContext* ctx) { |
|||
PITYASSERT(pity.getProcessDir() == "./pitytest_data/test_processdirs/", "node 1"); |
|||
return 0; |
|||
}) |
|||
.addNew<TestUnit>("node 1.1", [](TestUnit& pity, TestContext* ctx) { |
|||
PITYASSERT(pity.getProcessDir() == "./pitytest_data/test_processdirs/", "node 1.1"); |
|||
return 0; |
|||
}); |
|||
|
|||
// 2
|
|||
suite |
|||
.addNew<TestUnit>( |
|||
"node 2", |
|||
[](TestUnit& pity, TestContext* ctx) { |
|||
PITYASSERT(pity.getProcessDir() == "./pitytest_data/test_processdirs/", "node 2"); |
|||
return 0; |
|||
}) |
|||
.addNew<TestUnit>( |
|||
"node 2.1", |
|||
[](TestUnit& pity, TestContext* ctx) { |
|||
PITYASSERT(pity.getProcessDir() == "./pitytest_data/test_processdirs/node_2_1/", ""); |
|||
return 0; |
|||
}, |
|||
nullptr, |
|||
PityUnit<>::ExecutionMode::PROCESS_PARALLEL) |
|||
.addNew<TestUnit>("node 2.1.1", [](TestUnit& pity, TestContext* ctx) { |
|||
PITYASSERT(pity.getProcessDir() == "./pitytest_data/test_processdirs/node_2_1/", ""); |
|||
return 0; |
|||
}); |
|||
|
|||
|
|||
// 3
|
|||
suite |
|||
.addNew<TestUnit>( |
|||
"node 3", |
|||
[](TestUnit& pity, TestContext* ctx) { |
|||
PITYASSERT(pity.getProcessDir() == "./pitytest_data/test_processdirs/node_3/", ""); |
|||
return 0; |
|||
}, |
|||
nullptr, |
|||
PityUnit<>::ExecutionMode::PROCESS_PARALLEL) |
|||
.addNew<TestUnit>( |
|||
"node 3.1", |
|||
[](TestUnit& pity, TestContext* ctx) { |
|||
PITYASSERT(pity.getProcessDir() == "./pitytest_data/test_processdirs/node_3/", ""); |
|||
return 0; |
|||
}) |
|||
.addNew<TestUnit>( |
|||
"node 3.1.1", |
|||
[](TestUnit& pity, TestContext* ctx) { |
|||
PITYASSERT(pity.getProcessDir() == "./pitytest_data/test_processdirs/node_3/", ""); |
|||
return 0; |
|||
}) |
|||
.addNew<TestUnit>( |
|||
"node 3.1.1", |
|||
[](TestUnit& pity, TestContext* ctx) { |
|||
PITYASSERT(pity.getProcessDir() == "./pitytest_data/test_processdirs/node_3_1_1/", ""); |
|||
return 0; |
|||
}, |
|||
nullptr, |
|||
PityUnit<>::ExecutionMode::PROCESS_PARALLEL) |
|||
.addNew<TestUnit>("node 3.1.1.1", [](TestUnit& pity, TestContext* ctx) { |
|||
PITYASSERT(pity.getProcessDir() == "./pitytest_data/test_processdirs/node_3_1_1/", ""); |
|||
return 0; |
|||
}); |
|||
|
|||
suite.run(); |
|||
} |
@ -0,0 +1,88 @@ |
|||
#include "../src/PityTest.hh" |
|||
#include "../../../src/utils.hh" |
|||
using namespace pEp; |
|||
using namespace pEp::Adapter; |
|||
using namespace pEp::PityTest11; |
|||
|
|||
using TextCTX = PityModel; |
|||
using TestUnit = PityUnit<TextCTX>; |
|||
|
|||
using TextCTXSwarm = PityPerspective; |
|||
using TestUnitSwarm = PityUnit<TextCTXSwarm>; |
|||
|
|||
int test_init(PityUnit<PityPerspective>& unit, PityPerspective* ctx) |
|||
{ |
|||
unit.log("GlobalRoot:" + unit.getGlobalRootDir()); |
|||
unit.log("Path:" + unit.getPath()); |
|||
unit.log("ProcessDir:" + unit.getProcessDir()); |
|||
unit.log("TransportDir:" + unit.getTransportDir()); |
|||
|
|||
PITYASSERT( |
|||
unit.getProcessDir() == |
|||
unit.getGlobalRootDir() + |
|||
AbstractPityUnit::_normalizeName(unit.getParentProcessUnit().getPath()) + "/", |
|||
"ProcessDir"); |
|||
PITYASSERT(std::string(getenv("HOME")) == unit.getProcessDir(), "HOME"); |
|||
PITYASSERT(unit.getTransportDir() == unit.getProcessDir() + "inbox/", "TransportDir"); |
|||
return 0; |
|||
} |
|||
|
|||
int test_run(PityUnit<PityPerspective>& unit, PityPerspective* ctx) |
|||
{ |
|||
std::string msg = "Message from: " + unit.getPath(); |
|||
int throttle = 1000; |
|||
int cycles = 3; |
|||
for (int i = 0; i < cycles; i++) { |
|||
Utils::sleep_millis(throttle); |
|||
unit.log(std::to_string(ctx->peers.size())); |
|||
for (const auto& peer : ctx->peers) { |
|||
unit.log("sending to" + peer.addr); |
|||
unit.transport()->sendMsg(peer.addr, msg); |
|||
} |
|||
|
|||
while (unit.transport()->hasMsg()) { |
|||
unit.log(unit.getPath() + " - MSG RX:" + unit.transport()->receiveMsg()); |
|||
} |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
int test_finish(PityUnit<PityPerspective>& unit, PityPerspective* ctx) |
|||
{ |
|||
unit.log(unit.getPath() + " - DONE"); |
|||
return 0; |
|||
} |
|||
|
|||
int main(int argc, char* argv[]) |
|||
{ |
|||
int nodesCount = 3; |
|||
PityModel model{ "model_swarm", nodesCount }; |
|||
TestUnit suite{ "suite_swarm" }; |
|||
|
|||
PitySwarm swarm1{ "swarm1", model }; |
|||
for (int i = 0; i < nodesCount; i++) { |
|||
swarm1.addTestUnit(i, TestUnitSwarm("init", &test_init)); |
|||
swarm1.addTestUnit(i, TestUnitSwarm("run", &test_run)); |
|||
} |
|||
std::cout << swarm1.getSwarmUnit().to_string() << std::endl; |
|||
|
|||
// swarm2 copy of swarm1
|
|||
PitySwarm swarm2{ swarm1, "swarm2" }; |
|||
// modify
|
|||
for (int i = 0; i < nodesCount; i++) { |
|||
swarm2.addTestUnit(i, TestUnitSwarm("finish", &test_finish)); |
|||
} |
|||
// swarm2.getSwarmUnit().getChildRefs().begin()->second.setName("FDAGAFG");
|
|||
// swarm2.getSwarmUnit().getChildRefs().begin()->second.getChildRefs().begin()->second.setName("fsadAG");
|
|||
std::cout << swarm1.getSwarmUnit().to_string() << std::endl; |
|||
std::cout << swarm2.getSwarmUnit().to_string() << std::endl; |
|||
|
|||
suite.addRef(swarm1.getSwarmUnit()); |
|||
// TODO this is broken, will not be run
|
|||
suite.addRef(swarm2.getSwarmUnit()); |
|||
suite.run(); |
|||
|
|||
// swarm1.run();
|
|||
// Utils::readKey();
|
|||
// swarm2.run();
|
|||
} |
@ -0,0 +1,307 @@ |
|||
#include "../src/listmanager_dummy.hh" |
|||
#include "../src/utils.hh" |
|||
#include "framework/utils.hh" |
|||
#include <exception> |
|||
#include <map> |
|||
#include <cassert> |
|||
|
|||
|
|||
using namespace std; |
|||
using namespace pEp; |
|||
using namespace pEp::Adapter::pEpLog; |
|||
using namespace pEp::Utils; |
|||
|
|||
struct lm_list { |
|||
string addr; |
|||
string mod; |
|||
vector<string> members; |
|||
}; |
|||
|
|||
struct model_test_lmd { |
|||
string alice; |
|||
string bob; |
|||
string carol; |
|||
string joe; |
|||
string db_path; |
|||
vector<lm_list> lists; |
|||
vector<string> lists_addr() const |
|||
{ |
|||
vector<string> ret; |
|||
for (const lm_list& l : this->lists) { |
|||
ret.push_back(l.addr); |
|||
} |
|||
return ret; |
|||
} |
|||
}; |
|||
|
|||
void apply_model(ListManagerDummy& lmd, const model_test_lmd& model) |
|||
{ |
|||
// log("apply_model()");
|
|||
for (const lm_list& l : model.lists) { |
|||
lmd.list_add(l.addr, l.mod); |
|||
for (const string& m : l.members) { |
|||
lmd.member_add(l.addr, m); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void verify_model(ListManagerDummy& lmd, const model_test_lmd& model) |
|||
{ |
|||
// log("verify_model()");
|
|||
assert((model.lists_addr()) == lmd.lists()); |
|||
for (const lm_list& l : model.lists) { |
|||
assert(l.members == lmd.members(l.addr)); |
|||
} |
|||
for (const lm_list& l : model.lists) { |
|||
assert(l.mod == lmd.moderator(l.addr)); |
|||
} |
|||
} |
|||
|
|||
void recreate_apply_and_verify_model(ListManagerDummy& lmd, const model_test_lmd& model) |
|||
{ |
|||
try { |
|||
lmd.delete_db(); |
|||
} catch (const exception& e) { |
|||
} |
|||
assert(!path_exists(model.db_path)); |
|||
apply_model(lmd, model); |
|||
verify_model(lmd, model); |
|||
} |
|||
|
|||
model_test_lmd create_default_model() |
|||
{ |
|||
model_test_lmd model; |
|||
model.db_path = "test_lmd.db"; |
|||
model.alice = "alice@peptest.org"; |
|||
model.bob = "bob@peptest.org"; |
|||
model.carol = "carol@peptest.org"; |
|||
model.joe = "joe@peptest.org"; |
|||
|
|||
lm_list l1; |
|||
l1.addr = "list1@peptest.org"; |
|||
l1.mod = model.alice; |
|||
l1.members.push_back(model.bob); |
|||
l1.members.push_back(model.carol); |
|||
|
|||
lm_list l2; |
|||
l2.addr = "list2@peptest.org"; |
|||
l2.mod = model.alice; |
|||
l2.members.push_back(model.bob); |
|||
l2.members.push_back(model.carol); |
|||
l2.members.push_back(model.joe); |
|||
|
|||
lm_list l3; |
|||
l3.addr = "list3@peptest.org"; |
|||
l3.mod = model.bob; |
|||
l3.members.push_back(model.carol); |
|||
l3.members.push_back(model.joe); |
|||
|
|||
model.lists.push_back(l1); |
|||
model.lists.push_back(l2); |
|||
model.lists.push_back(l3); |
|||
return model; |
|||
} |
|||
|
|||
model_test_lmd create_model_bad_path() |
|||
{ |
|||
model_test_lmd model = create_default_model(); |
|||
model.db_path = "/wont_create_dirs/bad.db"; |
|||
return model; |
|||
} |
|||
|
|||
int main(int argc, char* argv[]) |
|||
{ |
|||
// pEpSQLite::log_enabled = true;
|
|||
ListManagerDummy::log_enabled = false; |
|||
|
|||
logH1("Testing SUCCESS conditions"); |
|||
{ |
|||
logH2("Test create new db"); |
|||
model_test_lmd model = create_default_model(); |
|||
ListManagerDummy lmd(model.db_path); |
|||
recreate_apply_and_verify_model(lmd, model); |
|||
} |
|||
{ |
|||
logH2("Test re-open db"); |
|||
model_test_lmd model = create_default_model(); |
|||
assert(path_exists(model.db_path)); |
|||
ListManagerDummy lmd(model.db_path); |
|||
verify_model(lmd, model); |
|||
|
|||
logH2("Test list_delete"); |
|||
lmd.list_delete(model.lists.at(2).addr); |
|||
model.lists.pop_back(); |
|||
verify_model(lmd, model); |
|||
|
|||
logH2("Test auto reopen after close()"); |
|||
lmd.close_db(); |
|||
|
|||
logH2("Test member_remove"); |
|||
lmd.member_remove(model.lists.at(0).addr, model.lists.at(0).members.at(1)); |
|||
model.lists.at(0).members.pop_back(); |
|||
verify_model(lmd, model); |
|||
|
|||
logH2("Test list_exists() - true"); |
|||
assert(lmd.list_exists(model.lists.at(0).addr)); |
|||
|
|||
logH2("Test list_exists() - false"); |
|||
assert(!lmd.list_exists("does_not_exist_for_sure")); |
|||
|
|||
logH2("Test member_exists() - true"); |
|||
assert(lmd.member_exists(model.lists.at(0).addr, model.lists.at(0).members.at(0))); |
|||
|
|||
logH2("Test member_exists() - false"); |
|||
assert(!lmd.member_exists(model.lists.at(0).addr, "does_not_exist_for_sure")); |
|||
|
|||
logH2("Test delete_db"); |
|||
lmd.delete_db(); |
|||
assert(!path_exists(model.db_path)); |
|||
} |
|||
|
|||
logH1("Testing ERROR conditions"); |
|||
{ |
|||
logH2("Testing success on close_db() on model_bad_path"); |
|||
model_test_lmd model = create_model_bad_path(); |
|||
ListManagerDummy lmd(model.db_path); |
|||
lmd.close_db(); |
|||
} |
|||
{ |
|||
logH2("Testing exception on delete_db() on model_bad_path"); |
|||
model_test_lmd model = create_model_bad_path(); |
|||
ListManagerDummy lmd(model.db_path); |
|||
ASSERT_EXCEPT(lmd.delete_db()); |
|||
} |
|||
{ |
|||
logH2("Testing exception on lists() on: on model_bad_path"); |
|||
model_test_lmd model = create_default_model(); |
|||
model.db_path = "/wont_create_dirs/bad.db"; |
|||
ListManagerDummy lmd(model.db_path); |
|||
ASSERT_EXCEPT(lmd.lists()); |
|||
} |
|||
// ------------------------------------------------------------------------------------
|
|||
logH1("list_add() Error conditions"); |
|||
{ |
|||
logH2("Testing list_add() AlreadyExistsException"); |
|||
model_test_lmd model = create_default_model(); |
|||
ListManagerDummy lmd(model.db_path); |
|||
recreate_apply_and_verify_model(lmd, model); |
|||
try { |
|||
lmd.list_add(model.lists.at(0).addr, "any"); |
|||
assert(false); |
|||
} catch (const AlreadyExistsException& e) { |
|||
log(nested_exception_to_string(e)); |
|||
} catch (...) { |
|||
assert(false); |
|||
} |
|||
} |
|||
// ------------------------------------------------------------------------------------
|
|||
logH1("list_delete() Error conditions"); |
|||
{ |
|||
logH2("Testing list_delete() DoesNotExistException"); |
|||
model_test_lmd model = create_default_model(); |
|||
ListManagerDummy lmd(model.db_path); |
|||
recreate_apply_and_verify_model(lmd, model); |
|||
try { |
|||
lmd.list_delete("does_not_exist_for_sure"); |
|||
assert(false); |
|||
} catch (const ListDoesNotExistException& e) { |
|||
log(nested_exception_to_string(e)); |
|||
} catch (...) { |
|||
assert(false); |
|||
} |
|||
} |
|||
// ------------------------------------------------------------------------------------
|
|||
logH1("member_add() Error conditions"); |
|||
{ |
|||
logH2("Testing member_add() AlreadyExistsException"); |
|||
model_test_lmd model = create_default_model(); |
|||
ListManagerDummy lmd(model.db_path); |
|||
recreate_apply_and_verify_model(lmd, model); |
|||
try { |
|||
lmd.member_add(model.lists.at(0).addr, model.lists.at(0).members.at(0)); |
|||
assert(false); |
|||
} catch (const AlreadyExistsException& e) { |
|||
log(nested_exception_to_string(e)); |
|||
} catch (...) { |
|||
assert(false); |
|||
} |
|||
} |
|||
{ |
|||
logH2("Testing member_add() to not existing list - DoesNotExistException"); |
|||
model_test_lmd model = create_default_model(); |
|||
ListManagerDummy lmd(model.db_path); |
|||
recreate_apply_and_verify_model(lmd, model); |
|||
try { |
|||
lmd.member_add("does_not_exist_for_sure", model.lists.at(0).members.at(0)); |
|||
assert(false); |
|||
} catch (const ListDoesNotExistException& e) { |
|||
log(nested_exception_to_string(e)); |
|||
} catch (...) { |
|||
assert(false); |
|||
} |
|||
} |
|||
// ------------------------------------------------------------------------------------
|
|||
logH1("member_remove() Error conditions"); |
|||
{ |
|||
logH2("Testing member_remove() not existing member - DoesNotExistException"); |
|||
model_test_lmd model = create_default_model(); |
|||
ListManagerDummy lmd(model.db_path); |
|||
recreate_apply_and_verify_model(lmd, model); |
|||
try { |
|||
lmd.member_remove(model.lists.at(0).addr, "does_not_exist_for_sure"); |
|||
assert(false); |
|||
} catch (const MemberDoesNotExistException& e) { |
|||
log(nested_exception_to_string(e)); |
|||
} catch (...) { |
|||
assert(false); |
|||
} |
|||
} |
|||
{ |
|||
logH2("Testing member_remove() not existing list - DoesNotExistException"); |
|||
model_test_lmd model = create_default_model(); |
|||
ListManagerDummy lmd(model.db_path); |
|||
recreate_apply_and_verify_model(lmd, model); |
|||
try { |
|||
lmd.member_remove("does_not_exist_for_sure", model.lists.at(0).members.at(0)); |
|||
assert(false); |
|||
} catch (const ListDoesNotExistException& e) { |
|||
log(nested_exception_to_string(e)); |
|||
} catch (...) { |
|||
assert(false); |
|||
} |
|||
} |
|||
// ------------------------------------------------------------------------------------
|
|||
logH1("moderator() Error conditions"); |
|||
{ |
|||
logH2("Testing moderator() DoesNotExistException"); |
|||
model_test_lmd model = create_default_model(); |
|||
ListManagerDummy lmd(model.db_path); |
|||
recreate_apply_and_verify_model(lmd, model); |
|||
try { |
|||
lmd.moderator("does_not_exist_for_sure"); |
|||
assert(false); |
|||
} catch (const ListDoesNotExistException& e) { |
|||
log(nested_exception_to_string(e)); |
|||
} catch (...) { |
|||
assert(false); |
|||
} |
|||
} |
|||
// ------------------------------------------------------------------------------------
|
|||
logH1("members() Error conditions"); |
|||
{ |
|||
logH2("Testing members() DoesNotExistException"); |
|||
model_test_lmd model = create_default_model(); |
|||
ListManagerDummy lmd(model.db_path); |
|||
recreate_apply_and_verify_model(lmd, model); |
|||
try { |
|||
lmd.members("does_not_exist_for_sure"); |
|||
assert(false); |
|||
} catch (const ListDoesNotExistException& e) { |
|||
log(nested_exception_to_string(e)); |
|||
} catch (...) { |
|||
assert(false); |
|||
} |
|||
} |
|||
|
|||
logH1("All Tests SUCCESSFUL"); |
|||
} |
@ -0,0 +1,642 @@ |
|||
#include "test_pEpSQLite.hh" |
|||
#include "../src/pEpSQLite.hh" |
|||
#include "../src/utils.hh" |
|||
#include "../src/std_utils.hh" |
|||
#include "framework/utils.hh" |
|||
|
|||
#include <fstream> |
|||
#include <cassert> |
|||
|
|||
using namespace std; |
|||
using namespace pEp; |
|||
using namespace pEp::Test; |
|||
using namespace pEp::Utils; |
|||
|
|||
namespace pEp { |
|||
namespace Test { |
|||
// --------------------------------- FIXTURES ---------------------------------
|
|||
// filenames
|
|||
string fixture_db_filename_new() |
|||
{ |
|||
// TESTLOG("called");
|
|||
string path = "new.db"; |
|||
return path; |
|||
} |
|||
|
|||
string fixture_db_filename_bad() |
|||
{ |
|||
// TESTLOG("called");
|
|||
string db_path_bad = "/will_not_create_dirs/bad.db"; |
|||
return db_path_bad; |
|||
} |
|||
|
|||
string fixture_db_filename_existing_and_verified() |
|||
{ |
|||
// TESTLOG("called");
|
|||
string path = "new.db"; |
|||
return path; |
|||
} |
|||
|
|||
string fixture_db_filename_corrupt() |
|||
{ |
|||
// TESTLOG("called");
|
|||
string path = "corrupt.db"; |
|||
return path; |
|||
} |
|||
|
|||
// prepared db's
|
|||
string fixture_init_db_new() |
|||
{ |
|||
// TESTLOG("called");
|
|||
string path = fixture_db_filename_new(); |
|||
path_ensure_not_existing(path); |
|||
return path; |
|||
} |
|||
|
|||
string fixture_init_db_existing_and_verified() |
|||
{ |
|||
// TESTLOG("called");
|
|||
string path = "existing.db"; |
|||
path_ensure_not_existing(path); |
|||
return path; |
|||
} |
|||
|
|||
string fixture_init_db_corrupt() |
|||
{ |
|||
// TESTLOG("called");
|
|||
string path = fixture_db_filename_corrupt(); |
|||
path_ensure_not_existing(path); |
|||
ofstream db_corrupt = file_create(path); |
|||
db_corrupt << "G4rbage" << endl; |
|||
db_corrupt.close(); |
|||
return path; |
|||
} |
|||
|
|||
// instance
|
|||
pEpSQLite fixture_instance_of_new() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_create_instance_on_new(); |
|||
} |
|||
|
|||
pEpSQLite fixture_instance_of_existing_and_verified() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_create_instance_on_existing(); |
|||
} |
|||
|
|||
pEpSQLite fixture_instance_of_bad() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_create_instance_on_path_bad(); |
|||
} |
|||
|
|||
pEpSQLite fixture_instance_of_corrupt() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_create_instance_on_path_corrupt(); |
|||
} |
|||
|
|||
// open
|
|||
pEpSQLite fixture_db_open_of_new() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_createopen_db_new(); |
|||
} |
|||
|
|||
pEpSQLite fixture_db_open_of_existing_and_verified() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_db_verify_content_after_insert_on_tables_exist(); |
|||
} |
|||
|
|||
pEpSQLite fixture_db_open_of_bad() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_createopen_db_bad(); |
|||
} |
|||
|
|||
pEpSQLite fixture_db_open_of_corrupt() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_createopen_db_corrupt(); |
|||
} |
|||
|
|||
pEpSQLite fixture_db_open_after_close() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_close_after_open(); |
|||
} |
|||
|
|||
// tables
|
|||
pEpSQLite fixture_db_open_with_tables_of_new() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_db_create_tables_on_open_new(); |
|||
} |
|||
|
|||
pEpSQLite fixture_db_open_with_tables_of_corrupt() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_db_create_tables_on_open_corrupt(); |
|||
} |
|||
|
|||
// content
|
|||
pEpSQLite fixture_db_open_with_tables_and_content() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_db_insert_on_tables_exist(); |
|||
} |
|||
|
|||
// delete
|
|||
pEpSQLite fixture_db_open_after_delete() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_delete_file_gone_after_open_existing(); |
|||
} |
|||
|
|||
pEpSQLite fixture_db_open_after_close_after_delete() |
|||
{ |
|||
// TESTLOG("called");
|
|||
return test_delete_file_gone_after_close_new(); |
|||
} |
|||
|
|||
// --------------------------------- TESTS ---------------------------------
|
|||
|
|||
// instance creation
|
|||
// OK
|
|||
pEpSQLite test_create_instance_on_new() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db(fixture_init_db_new()); |
|||
return db; |
|||
} |
|||
|
|||
// OK
|
|||
pEpSQLite test_create_instance_on_existing() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db(fixture_init_db_existing_and_verified()); |
|||
return db; |
|||
} |
|||
|
|||
// OK
|
|||
pEpSQLite test_create_instance_on_path_bad() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db(fixture_db_filename_bad()); |
|||
return db; |
|||
} |
|||
|
|||
// OK
|
|||
pEpSQLite test_create_instance_on_path_corrupt() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db(fixture_init_db_corrupt()); |
|||
return db; |
|||
} |
|||
|
|||
// db create_open (create_or_open())
|
|||
// OK, new db
|
|||
pEpSQLite test_createopen_db_new() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_instance_of_new(); |
|||
assert(!path_exists(fixture_db_filename_new())); |
|||
db.create_or_open_db(); |
|||
assert(path_exists(fixture_db_filename_new())); |
|||
return db; |
|||
} |
|||
|
|||
// OK, open db
|
|||
pEpSQLite test_createopen_db_existing() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_instance_of_existing_and_verified(); |
|||
assert(path_exists(fixture_db_filename_existing_and_verified())); |
|||
db.create_or_open_db(); |
|||
return db; |
|||
} |
|||
|
|||
// ERR, cant create
|
|||
pEpSQLite test_createopen_db_bad() |
|||
{ |
|||
TESTLOG("called"); |
|||
assert(!path_exists(fixture_db_filename_bad())); |
|||
pEpSQLite db = fixture_instance_of_bad(); |
|||
assert(!path_exists(fixture_db_filename_bad())); |
|||
ASSERT_EXCEPT(db.create_or_open_db()); |
|||
assert(!path_exists(fixture_db_filename_bad())); |
|||
return db; |
|||
} |
|||
|
|||
// OK(cant detect corruption)
|
|||
pEpSQLite test_createopen_db_corrupt() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_instance_of_corrupt(); |
|||
assert(path_exists(fixture_db_filename_corrupt())); |
|||
db.create_or_open_db(); |
|||
assert(path_exists(fixture_db_filename_corrupt())); |
|||
return db; |
|||
} |
|||
|
|||
// close()
|
|||
// OK
|
|||
pEpSQLite test_close_before_open() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_instance_of_new(); |
|||
db.close_db(); |
|||
return db; |
|||
} |
|||
|
|||
// OK
|
|||
pEpSQLite test_close_after_open() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_new(); |
|||
db.close_db(); |
|||
return db; |
|||
} |
|||
|
|||
// OK
|
|||
pEpSQLite test_close_idempotent() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_new(); |
|||
db.close_db(); |
|||
db.close_db(); |
|||
db.close_db(); |
|||
db.close_db(); |
|||
return db; |
|||
} |
|||
|
|||
// create tables (execute())
|
|||
// OK
|
|||
pEpSQLite test_db_create_tables_on_open_new() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_new(); |
|||
db.execute("CREATE TABLE test(i,i_squared);"); |
|||
return db; |
|||
} |
|||
|
|||
// ERR, Tables already exist
|
|||
pEpSQLite test_db_create_tables_open_existing() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_existing_and_verified(); |
|||
ASSERT_EXCEPT(db.execute("CREATE TABLE test(i,i_squared);")); |
|||
return db; |
|||
} |
|||
|
|||
// ERR, db closed
|
|||
pEpSQLite test_db_create_tables_on_open_bad() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_bad(); |
|||
|
|||
ASSERT_EXCEPT(db.execute("CREATE TABLE test(i,i_squared);")); |
|||
return db; |
|||
} |
|||
|
|||
// ERR, db corrupt
|
|||
pEpSQLite test_db_create_tables_on_open_corrupt() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_corrupt(); |
|||
ASSERT_EXCEPT(db.execute("CREATE TABLE test(i,i_squared);")); |
|||
return db; |
|||
} |
|||
|
|||
|
|||
// insert (execute())
|
|||
void insert_operation(pEpSQLite& db) |
|||
{ |
|||
TESTLOG("called"); |
|||
for (int i = 0; i < 9; i = (i + 2)) { |
|||
db.execute( |
|||
"INSERT INTO test(i,i_squared) VALUES ('" + to_string(i) + "', '" + |
|||
to_string(i * i) + "');"); |
|||
} |
|||
} |
|||
|
|||
// OK
|
|||
pEpSQLite test_db_insert_on_tables_exist() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_with_tables_of_new(); |
|||
insert_operation(db); |
|||
return db; |
|||
} |
|||
|
|||
// ERR, Tables missing
|
|||
pEpSQLite test_db_insert_on_tables_dont_exist() |
|||
{ |
|||
pEpSQLite db = fixture_db_open_of_new(); |
|||
ASSERT_EXCEPT(insert_operation(db)); |
|||
return db; |
|||
} |
|||
|
|||
// ERR, Tables missing
|
|||
pEpSQLite test_db_insert_before_open() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_instance_of_new(); |
|||
ASSERT_EXCEPT(insert_operation(db)); |
|||
return db; |
|||
} |
|||
|
|||
// ERR, Tables missing
|
|||
pEpSQLite test_db_insert_after_close() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_after_close(); |
|||
ASSERT_EXCEPT(insert_operation(db)); |
|||
return db; |
|||
} |
|||
|
|||
// verify contents (execute())
|
|||
void verify_operation(pEpSQLite& db) |
|||
{ |
|||
TESTLOG("called"); |
|||
for (int i = 0; i < 9; i++) { |
|||
ResultSet rs = db.execute( |
|||
"SELECT i, i_squared FROM test " |
|||
"WHERE (test.i == '" + |
|||
to_string(i) + "');"); |
|||
if (i % 2) { |
|||
if (!rs.empty()) { |
|||
runtime_error e{ "Exception verifying database content" }; |
|||
throw(e); |
|||
} |
|||
} else { |
|||
if (rs.size() != 1) { |
|||
runtime_error e{ "Exception verifying database content" }; |
|||
throw(e); |
|||
} |
|||
for (const RSRecord& r : rs) { |
|||
const int x = stoi(r.at("i")); |
|||
const int x_squared = stoi(r.at("i_squared")); |
|||
if ((x * x) != x_squared) { |
|||
runtime_error e{ "Exception verifying database content" }; |
|||
throw(e); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// OK
|
|||
pEpSQLite test_db_verify_content_existing_open_db() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_existing_and_verified(); |
|||
verify_operation(db); |
|||
return db; |
|||
} |
|||
|
|||
// OK
|
|||
pEpSQLite test_db_verify_content_after_insert_on_tables_exist() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_with_tables_and_content(); |
|||
verify_operation(db); |
|||
return db; |
|||
} |
|||
|
|||
// ERR - no tables
|
|||
pEpSQLite test_db_verify_content_no_tables() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_new(); |
|||
|
|||
ASSERT_EXCEPT(verify_operation(db)); |
|||
return db; |
|||
} |
|||
|
|||
// ERR - err no data
|
|||
pEpSQLite test_db_verify_content_after_create_tables() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_with_tables_of_new(); |
|||
ASSERT_EXCEPT(verify_operation(db)); |
|||
return db; |
|||
} |
|||
|
|||
|
|||
// get_path()
|
|||
// OK
|
|||
pEpSQLite test_get_path_on_instance_good() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_instance_of_new(); |
|||
assert(db.get_db_path() == fixture_db_filename_new()); |
|||
return db; |
|||
} |
|||
|
|||
// OK
|
|||
pEpSQLite test_get_path_on_instance_bad() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_instance_of_bad(); |
|||
assert(db.get_db_path() == fixture_db_filename_bad()); |
|||
return db; |
|||
} |
|||
|
|||
// delete_db()
|
|||
// ERR, file not found
|
|||
pEpSQLite test_delete_file_gone_before_open_new() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_instance_of_new(); |
|||
ASSERT_EXCEPT(db.delete_db()); |
|||
assert(!path_exists(fixture_db_filename_new())); |
|||
return db; |
|||
} |
|||
|
|||
// ERR, file not found
|
|||
pEpSQLite test_delete_file_gone_before_open_existing() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_instance_of_existing_and_verified(); |
|||
ASSERT_EXCEPT(db.delete_db()); |
|||
assert(!path_exists(fixture_db_filename_existing_and_verified())); |
|||
return db; |
|||
} |
|||
|
|||
// OK
|
|||
pEpSQLite test_delete_file_gone_after_close_new() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_after_close(); |
|||
db.delete_db(); |
|||
assert(!path_exists(fixture_db_filename_new())); |
|||
return db; |
|||
} |
|||
|
|||
// OK
|
|||
pEpSQLite test_delete_file_gone_after_open_existing() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_existing_and_verified(); |
|||
db.delete_db(); |
|||
assert(!path_exists(fixture_db_filename_existing_and_verified())); |
|||
return db; |
|||
} |
|||
|
|||
// OK
|
|||
pEpSQLite test_delete_file_gone_after_open_corrupt() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_corrupt(); |
|||
db.delete_db(); |
|||
assert(!path_exists(fixture_db_filename_corrupt())); |
|||
return db; |
|||
} |
|||
|
|||
// ERR
|
|||
pEpSQLite test_delete_file_gone_after_open_bad() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_bad(); |
|||
ASSERT_EXCEPT(db.delete_db()); |
|||
assert(!path_exists(fixture_db_filename_bad())); |
|||
return db; |
|||
} |
|||
|
|||
// is_open()
|
|||
// false
|
|||
pEpSQLite test_is_open_before_open_new() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_instance_of_new(); |
|||
assert(!db.is_open()); |
|||
return db; |
|||
} |
|||
|
|||
// true
|
|||
pEpSQLite test_is_open_after_open_new() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_new(); |
|||
assert(db.is_open()); |
|||
return db; |
|||
} |
|||
|
|||
// true
|
|||
pEpSQLite test_is_open_after_open_existing() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_existing_and_verified(); |
|||
assert(db.is_open()); |
|||
return db; |
|||
} |
|||
|
|||
// false
|
|||
pEpSQLite test_is_open_after_open_bad() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_bad(); |
|||
assert(!db.is_open()); |
|||
return db; |
|||
} |
|||
|
|||
// true (cant detect corruption)
|
|||
pEpSQLite test_is_open_after_open_corrupt() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_of_corrupt(); |
|||
assert(db.is_open()); |
|||
return db; |
|||
} |
|||
|
|||
// false
|
|||
pEpSQLite test_is_open_after_close() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_after_close(); |
|||
assert(!db.is_open()); |
|||
return db; |
|||
} |
|||
|
|||
// false
|
|||
pEpSQLite test_is_open_after_delete_on_open() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_after_delete(); |
|||
assert(!db.is_open()); |
|||
return db; |
|||
} |
|||
|
|||
// false
|
|||
pEpSQLite test_is_open_after_delete_on_closed() |
|||
{ |
|||
TESTLOG("called"); |
|||
pEpSQLite db = fixture_db_open_after_close_after_delete(); |
|||
assert(!db.is_open()); |
|||
return db; |
|||
} |
|||
} // namespace Test
|
|||
} // namespace pEp
|
|||
|
|||
int main(int argc, char* argv[]) |
|||
{ |
|||
// Enable logging for all instances of pEpSQLite
|
|||
pEpSQLite::log_enabled = true; |
|||
Adapter::pEpLog::set_enabled(true); |
|||
// instance creation
|
|||
test_create_instance_on_new(); |
|||
test_create_instance_on_existing(); |
|||
test_create_instance_on_path_bad(); |
|||
test_create_instance_on_path_corrupt(); |
|||
// db create_open (create_or_open())
|
|||
test_createopen_db_new(); |
|||
test_createopen_db_existing(); |
|||
test_createopen_db_bad(); |
|||
test_createopen_db_corrupt(); |
|||
// close()
|
|||
test_close_before_open(); |
|||
test_close_after_open(); |
|||
test_close_idempotent(); |
|||
// create tables (execute())
|
|||
test_db_create_tables_on_open_new(); |
|||
test_db_create_tables_open_existing(); |
|||
test_db_create_tables_on_open_bad(); |
|||
test_db_create_tables_on_open_corrupt(); |
|||
// insert (execute())
|
|||
test_db_insert_on_tables_exist(); |
|||
test_db_insert_on_tables_dont_exist(); |
|||
test_db_insert_before_open(); |
|||
test_db_insert_after_close(); |
|||
// verify contents (execute())
|
|||
test_db_verify_content_existing_open_db(); |
|||
test_db_verify_content_after_insert_on_tables_exist(); |
|||
test_db_verify_content_no_tables(); |
|||
test_db_verify_content_after_create_tables(); |
|||
// get_path()
|
|||
test_get_path_on_instance_good(); |
|||
test_get_path_on_instance_bad(); |
|||
// delete_db()
|
|||
test_delete_file_gone_before_open_new(); |
|||
test_delete_file_gone_before_open_existing(); |
|||
test_delete_file_gone_after_close_new(); |
|||
test_delete_file_gone_after_open_existing(); |
|||
test_delete_file_gone_after_open_corrupt(); |
|||
test_delete_file_gone_after_open_bad(); |
|||
// is_open()
|
|||
test_is_open_before_open_new(); |
|||
test_is_open_after_open_new(); |
|||
test_is_open_after_open_existing(); |
|||
test_is_open_after_open_bad(); |
|||
test_is_open_after_open_corrupt(); |
|||
test_is_open_after_close(); |
|||
test_is_open_after_delete_on_open(); |
|||
test_is_open_after_delete_on_closed(); |
|||
|
|||
path_ensure_not_existing(fixture_db_filename_new()); |
|||
path_ensure_not_existing(fixture_db_filename_corrupt()); |
|||
path_ensure_not_existing(fixture_db_filename_existing_and_verified()); |
|||
return 0; |
|||
} |
@ -0,0 +1,115 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#ifndef LIBPEPADAPTER_TEST_PEPSQLITE_HH |
|||
#define LIBPEPADAPTER_TEST_PEPSQLITE_HH |
|||
|
|||
#include <string> |
|||
#include "../src/pEpSQLite.hh" |
|||
|
|||
// ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION
|
|||
//
|
|||
// THIS IS TERRIBLE, JUST TERRIBLE, THIS IS A ANTI-PATTERN
|
|||
// Thats the single most stupid way of writing unit test ever!!!
|
|||
// DONT EVER DO THIS!
|
|||
//
|
|||
// The idea is as follows:
|
|||
// * Tests start simple and become increasingly complex
|
|||
// * There are fixtures and tests, as usual
|
|||
// * You start with basic fixtures to write the first test
|
|||
// * A test becomes a fixture, if the operation is needed for another test
|
|||
// * You systematically cover all the states possible in which a test can be executed
|
|||
//
|
|||
// THATS JUST TERRIBLE!!!
|
|||
// I WILL NEVER DO THAT AGAIN!
|
|||
//
|
|||
// ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION ATTENTION
|
|||
|
|||
namespace pEp { |
|||
namespace Test { |
|||
// FIXTURES
|
|||
// --------
|
|||
// filenames
|
|||
std::string fixture_db_filename_new(); |
|||
std::string fixture_db_filename_existing_and_verified(); |
|||
std::string fixture_db_filename_bad(); |
|||
std::string fixture_db_filename_corrupt(); |
|||
// prepared db's
|
|||
std::string fixture_init_db_new(); |
|||
std::string fixture_init_db_existing_and_verified(); |
|||
std::string fixture_init_db_corrupt(); |
|||
// instance
|
|||
pEpSQLite fixture_instance_of_new(); |
|||
pEpSQLite fixture_instance_of_existing_and_verified(); |
|||
pEpSQLite fixture_instance_of_bad(); |
|||
pEpSQLite fixture_instance_of_corrupt(); |
|||
// open
|
|||
pEpSQLite fixture_db_open_of_new(); |
|||
pEpSQLite fixture_db_open_of_existing_and_verified(); |
|||
pEpSQLite fixture_db_open_of_bad(); |
|||
pEpSQLite fixture_db_open_of_corrupt(); |
|||
pEpSQLite fixture_db_open_after_close(); |
|||
// tables
|
|||
pEpSQLite fixture_db_open_with_tables_of_new(); |
|||
// content
|
|||
pEpSQLite fixture_db_open_with_tables_and_content(); |
|||
// delete
|
|||
pEpSQLite fixture_db_open_after_delete(); |
|||
pEpSQLite fixture_db_open_after_close_after_delete(); |
|||
|
|||
|
|||
// TESTS
|
|||
// -----
|
|||
// instance creation
|
|||
pEpSQLite test_create_instance_on_new(); // OK
|
|||
pEpSQLite test_create_instance_on_existing(); // OK
|
|||
pEpSQLite test_create_instance_on_path_bad(); // OK
|
|||
pEpSQLite test_create_instance_on_path_corrupt(); // OK
|
|||
// db create_open (create_or_open())
|
|||
pEpSQLite test_createopen_db_new(); // OK, new db
|
|||
pEpSQLite test_createopen_db_existing(); // OK, open db
|
|||
pEpSQLite test_createopen_db_bad(); // ERR, cant create
|
|||
pEpSQLite test_createopen_db_corrupt(); // OK (cant detect corruption)
|
|||
// close()
|
|||
pEpSQLite test_close_before_open(); // OK
|
|||
pEpSQLite test_close_after_open(); // OK
|
|||
pEpSQLite test_close_idempotent(); // OK
|
|||
// create tables (execute())
|
|||
pEpSQLite test_db_create_tables_on_open_new(); // OK
|
|||
pEpSQLite test_db_create_tables_open_existing(); // ERR, Tables already exist
|
|||
pEpSQLite test_db_create_tables_on_open_bad(); // ERR, db closed
|
|||
pEpSQLite test_db_create_tables_on_open_corrupt(); // ERR, db corrupt
|
|||
// insert (execute())
|
|||
pEpSQLite test_db_insert_on_tables_exist(); // OK
|
|||
pEpSQLite test_db_insert_on_tables_dont_exist(); // ERR, Tables missing
|
|||
pEpSQLite test_db_insert_before_open(); // ERR, Tables missing
|
|||
pEpSQLite test_db_insert_after_close(); // ERR, Tables missing
|
|||
// verify contents (execute())
|
|||
pEpSQLite test_db_verify_content_existing_open_db(); // OK
|
|||
pEpSQLite test_db_verify_content_after_insert_on_tables_exist(); // OK
|
|||
pEpSQLite test_db_verify_content_no_tables(); // ERR - no tables
|
|||
pEpSQLite test_db_verify_content_after_create_tables(); // ERR - err no data
|
|||
// get_path()
|
|||
pEpSQLite test_get_path_on_instance_good(); // OK
|
|||
pEpSQLite test_get_path_on_instance_bad(); // OK
|
|||
// delete_db()
|
|||
pEpSQLite test_delete_file_gone_before_open_new(); // OK?
|
|||
pEpSQLite test_delete_file_gone_before_open_existing(); // OK
|
|||
pEpSQLite test_delete_file_gone_after_close_new(); // OK
|
|||
pEpSQLite test_delete_file_gone_after_open_existing(); // OK
|
|||
pEpSQLite test_delete_file_gone_after_open_corrupt(); // OK
|
|||
pEpSQLite test_delete_file_gone_after_open_bad(); // ERR
|
|||
// is_open()
|
|||
pEpSQLite test_is_open_before_open_new(); // false
|
|||
pEpSQLite test_is_open_after_open_new(); // true
|
|||
pEpSQLite test_is_open_after_open_existing(); // true
|
|||
pEpSQLite test_is_open_after_open_bad(); // false
|
|||
pEpSQLite test_is_open_after_open_corrupt(); // true (cant detect corruption)
|
|||
pEpSQLite test_is_open_after_close(); // false
|
|||
pEpSQLite test_is_open_after_delete_on_open(); // false
|
|||
pEpSQLite test_is_open_after_delete_on_closed(); // false
|
|||
|
|||
} // namespace Test
|
|||
} // end of namespace pEp
|
|||
|
|||
#endif // LIBPEPADAPTER_TEST_PEPSQLITE_HH
|
@ -0,0 +1,492 @@ |
|||
#include "pitytest11/src/PityTest.hh" |
|||
#include "../src/utils.hh" |
|||
#include "framework/framework.hh" |
|||
#include "framework/utils.hh" |
|||
|
|||
// libpEpAdapter
|
|||
#include "../src/Adapter.hh" |
|||
#include "../src/group_manager_api.h" |
|||
#include "../src/pEpLog.hh" |
|||
#include "../src/status_to_string.hh" |
|||
#include "../src/grp_driver_replicator.hh" |
|||
|
|||
// engine
|
|||
#include <pEp/pEpEngine.h> |
|||
#include <pEp/message_api.h> |
|||
#include <pEp/keymanagement.h> |
|||
#include <pEp/identity_list.h> |
|||
#include <pEp/mime.h> |
|||
|
|||
using namespace pEp; |
|||
using namespace pEp::Adapter; |
|||
using namespace pEp::Test::Utils; |
|||
using namespace pEp::PityTest11; |
|||
using namespace pEp::Adapter::pEpLog; |
|||
|
|||
|
|||
using TextContext = PityPerspective; |
|||
using TestUnitSwarm = PityUnit<TextContext>; |
|||
|
|||
|
|||
TextContext *local_ctx; |
|||
PityUnit<TextContext> *local_pity; |
|||
|
|||
// Either group manager or group member, not both
|
|||
pEpIdent group_ident; |
|||
|
|||
bool grp_did_tx_encrypted = false; |
|||
bool grp_did_rx_encrypted = false; |
|||
bool signal_received = false; |
|||
// The most minimal received msg contains of:
|
|||
// * from address
|
|||
// * content
|
|||
using MinMsgRx = std::tuple<std::string, std::string>; |
|||
|
|||
// CALLBACKS
|
|||
// ------------------------------------------------------------------------------------------------
|
|||
|
|||
::PEP_STATUS test_messageToSend(::message *_msg) |
|||
{ |
|||
local_pity->log("MESSAGE TO SEND:" + Utils::to_string(_msg, false)); |
|||
std::string mime_text = mimeEncode(wrap(_msg)); |
|||
local_pity->transport()->sendMsg(_msg->to->ident->address, mime_text); |
|||
return PEP_STATUS_OK; |
|||
} |
|||
|
|||
::PEP_STATUS test_notifyHandshake(::pEp_identity *me, ::pEp_identity *partner, sync_handshake_signal signal) |
|||
{ |
|||
local_pity->log("NOTFY_HANDSHAKE: signal: " + std::string(std::to_string(signal))); |
|||
local_pity->log("NOTFY_HANDSHAKE: me:" + Utils::to_string(me, false)); |
|||
local_pity->log("NOTFY_HANDSHAKE: partner: " + Utils::to_string(partner, false)); |
|||
if (signal == ::SYNC_NOTIFY_GROUP_INVITATION) { |
|||
signal_received = true; |
|||
group_ident = dup(me); |
|||
local_pity->log("SYNC_NOTIFY_GROUP_INVITATION"); |
|||
PEP_STATUS status = ::adapter_group_join( |
|||
Adapter::session(), |
|||
group_ident.get(), |
|||
local_ctx->own_ident.get()); |
|||
throw_status(status); |
|||
} |
|||
return PEP_STATUS_OK; |
|||
} |
|||
|
|||
// HELPERS
|
|||
// ------------------------------------------------------------------------------------------------
|
|||
|
|||
// Blocking
|
|||
void processMessage() |
|||
{ |
|||
local_pity->log("waiting for message..."); |
|||
std::string mime_data_rx = local_pity->transport()->receiveMsg(); |
|||
// local_pity->log("mime_text:" + Utils::clip(mime_data_rx, 300));
|
|||
|
|||
// Decode
|
|||
pEpMessage rx_msg = mimeDecode(mime_data_rx); |
|||
// local_pity->log("decode: " + Utils::to_string(rx_msg.get(), false));
|
|||
|
|||
// Decrypt
|
|||
::PEP_decrypt_flags_t flags = ::PEP_decrypt_flag_dont_trigger_sync; |
|||
// ::PEP_decrypt_flags_t flags = 0;
|
|||
DecryptResult decres = decryptMessage(rx_msg, &flags); |
|||
pEpMessage msg_decrypt = std::get<0>(decres); |
|||
// local_pity->log("Decrypt: " + Utils::to_string(msg_decrypt.get(), false));
|
|||
local_pity->log("message processed..."); |
|||
} |
|||
|
|||
// Non-Blocking
|
|||
void processsAllMessages() |
|||
{ |
|||
while (local_pity->transport()->hasMsg()) { |
|||
Utils::sleep_millis(100); |
|||
processMessage(); |
|||
} |
|||
} |
|||
|
|||
void tofu_send(TestUnitSwarm &pity, TextContext *ctx, const std::string &addr, const std::string longmessage) |
|||
{ |
|||
pEpMessage msg = createMessage(ctx->own_ident, addr, longmessage); |
|||
EncryptResult msg_enc = encryptAndEncode(msg); |
|||
std::string mime_text = std::get<1>(msg_enc); |
|||
ctx->getPeer(addr)->did_tx_encrypted = std::get<2>(msg_enc); |
|||
pity.log("Sending Encrypted[" + std::to_string(std::get<2>(msg_enc)) + "] to: " + addr); |
|||
pity.transport()->sendMsg(addr, mime_text); |
|||
} |
|||
|
|||
MinMsgRx tofu_receive(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
MinMsgRx ret; |
|||
const std::string mime_data_rx = pity.transport()->receiveMsg(); |
|||
|
|||
DecryptResult msg_rx = decryptAndDecode(mime_data_rx); |
|||
pEpMessage msg_rx_dec = std::get<0>(msg_rx); |
|||
ctx->getPeer(msg_rx_dec->from->address)->did_rx_encrypted = std::get<4>(msg_rx); |
|||
pity.log( |
|||
"Received Encrypted[" + std::to_string(std::get<4>(msg_rx)) + |
|||
"] from: " + std::string(msg_rx_dec->from->address)); |
|||
// pity.log("IN:\n " + Utils::to_string(mimeDecode(mime_data_rx).get(), false));
|
|||
pity.log("DECRYPTED:\n " + Utils::to_string(msg_rx_dec.get(), false)); |
|||
std::get<0>(ret) = std::string(msg_rx_dec->from->address); |
|||
std::get<1>(ret) = std::string(msg_rx_dec->longmsg); |
|||
return ret; |
|||
} |
|||
|
|||
void tofu_receiveAndReply(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
MinMsgRx rx_msg = tofu_receive(pity, ctx); |
|||
|
|||
std::string addr = std::get<0>(rx_msg); |
|||
std::string longmsg_orig = std::get<1>(rx_msg); |
|||
pEpMessage msg = createMessage( |
|||
ctx->own_ident, |
|||
addr, |
|||
"REPLY_FROM:'" + std::string(addr) + "' - " + longmsg_orig); |
|||
|
|||
tofu_send(pity, ctx, addr, "REPLY_FROM:'" + std::string(addr) + "' - " + longmsg_orig); |
|||
} |
|||
|
|||
// TESTUNITS
|
|||
// ------------------------------------------------------------------------------------------------
|
|||
|
|||
int test_pEp_init(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
local_ctx = ctx; |
|||
local_pity = &pity; |
|||
pity.log("Model : " + ctx->model.getName()); |
|||
pity.log("myself : " + ctx->own_name); |
|||
pity.log("partner: " + ctx->getCpt().addr); |
|||
pity.log("HOME : " + std::string(getenv("HOME"))); |
|||
pity.log("PUD : " + std::string(::per_user_directory())); |
|||
pity.log("PMD : " + std::string(::per_machine_directory())); |
|||
pEp::Adapter::session.initialize( |
|||
pEp::Adapter::SyncModes::Async, |
|||
false, |
|||
test_messageToSend, |
|||
test_notifyHandshake); |
|||
return 0; |
|||
} |
|||
|
|||
int test_create_myself(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
// Create new identity
|
|||
pity.log("updating or creating identity for me"); |
|||
ctx->own_ident = createOwnIdent(ctx->own_name); |
|||
::PEP_STATUS status = ::myself(Adapter::session(), ctx->own_ident.get()); |
|||
pEp::throw_status(status); |
|||
return 0; |
|||
} |
|||
|
|||
int test_start_sync(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
Adapter::session |
|||
.initialize(Adapter::SyncModes::Async, false, test_messageToSend, test_notifyHandshake); |
|||
processsAllMessages(); |
|||
return 0; |
|||
} |
|||
|
|||
int test_tofu_init_all_peers(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
for (auto &peer : ctx->peers) { |
|||
tofu_send(pity, ctx, peer.addr, "INIT TOFU"); |
|||
tofu_receiveAndReply(pity, ctx); |
|||
PITYASSERT(peer.tofu_done(), "TOFU failed for " + peer.addr); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
int test_tofu_react(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
tofu_receiveAndReply(pity, ctx); |
|||
tofu_receive(pity, ctx); |
|||
// std::cout << pity.getParentProcessUnit().getName() << "GET:" << &ctx->getCpt() << std::endl;
|
|||
PITYASSERT(ctx->getCpt().tofu_done(), "TOFU failed for" + ctx->getCpt().addr); |
|||
return 0; |
|||
} |
|||
|
|||
int test_group_create(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
if (ctx->groups.size() > 0) { |
|||
group_ident = createCptIdent(ctx->groups.front().addr); |
|||
PEP_STATUS status = ::adapter_group_create( |
|||
Adapter::session(), |
|||
group_ident.get(), |
|||
ctx->own_ident.get(), |
|||
nullptr); |
|||
pEp::throw_status(status); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
int test_group_invite_members(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
PEP_STATUS status; |
|||
if (ctx->groups.size() > 0) { |
|||
Group &my_grp = ctx->groups.at(0); |
|||
auto grp_ident = createRawIdent(my_grp.addr); |
|||
status = ::update_identity(Adapter::session(), grp_ident.get()); |
|||
throw_status(status); |
|||
|
|||
pity.log(Utils::to_string(grp_ident.get(), false)); |
|||
for (TestIdent mb : my_grp.members) { |
|||
auto mb_ident = createRawIdent(mb.addr); |
|||
status = ::update_identity(Adapter::session(), mb_ident.get()); |
|||
throw_status(status); |
|||
pity.log(Utils::to_string(mb_ident.get(), false)); |
|||
|
|||
status = ::adapter_group_invite_member(Adapter::session(), grp_ident.get(), mb_ident.get()); |
|||
throw_status(status); |
|||
} |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
int test_group_join(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
processMessage(); |
|||
PITYASSERT(signal_received, "test_group_join - no signal received"); |
|||
return 0; |
|||
} |
|||
|
|||
int test_receive_joined(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
PEP_STATUS status; |
|||
if (ctx->groups.size() > 0) { |
|||
Group &my_grp = ctx->groups.at(0); |
|||
for (TestIdent mb : my_grp.members) { |
|||
processMessage(); |
|||
} |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int test_send_groupmessage(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
const std::string addr = group_ident->address; |
|||
pEpMessage msg = createMessage(ctx->own_ident, addr, "GROUP MESSAGE"); |
|||
EncryptResult msg_enc = encryptAndEncode(msg); |
|||
std::string mime_text = std::get<1>(msg_enc); |
|||
grp_did_tx_encrypted = std::get<2>(msg_enc); |
|||
pity.log("Sending to GROUP Encrypted[" + std::to_string(std::get<2>(msg_enc)) + "] to: " + addr); |
|||
|
|||
// Send to members and moderator (except self)
|
|||
for (const auto &member : ctx->getGroup(addr)->members) { |
|||
if (member.addr != ctx->own_name) { |
|||
pity.log("To: " + member.addr); |
|||
pity.transport()->sendMsg(member.addr, mime_text); |
|||
} |
|||
} |
|||
if (ctx->getGroup(addr)->moderator != ctx->own_name) { |
|||
pity.log("To: " + ctx->getGroup(addr)->moderator); |
|||
pity.transport()->sendMsg(ctx->getGroup(addr)->moderator, mime_text); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int test_receive(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
MinMsgRx ret; |
|||
const std::string mime_data_rx = pity.transport()->receiveMsg(); |
|||
|
|||
DecryptResult msg_rx = decryptAndDecode(mime_data_rx); |
|||
pEpMessage msg_rx_dec = std::get<0>(msg_rx); |
|||
ctx->getPeer(msg_rx_dec->from->address)->did_rx_encrypted = std::get<4>(msg_rx); |
|||
pity.log( |
|||
"Received Encrypted[" + std::to_string(std::get<4>(msg_rx)) + |
|||
"] from: " + std::string(msg_rx_dec->from->address)); |
|||
// pity.log("IN:\n " + Utils::to_string(mimeDecode(mime_data_rx).get(), false));
|
|||
// pity.log("DECRYPTED:\n " + Utils::to_string(msg_rx_dec.get(), false));
|
|||
std::get<0>(ret) = std::string(msg_rx_dec->from->address); |
|||
std::get<1>(ret) = std::string(msg_rx_dec->longmsg); |
|||
return 0; |
|||
} |
|||
|
|||
int test_receive_all(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
while (local_pity->transport()->hasMsg()) { |
|||
Utils::sleep_millis(100); |
|||
test_receive(pity, ctx); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
int test_group_remove_member_one_by_one(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
bool is_empty = false; |
|||
do { |
|||
::identity_list *idl = nullptr; |
|||
::PEP_STATUS status = ::adapter_group_query_members(Adapter::session(), group_ident.get(), &idl); |
|||
throw_status(status); |
|||
pity.log(Utils::to_string(idl, false)); |
|||
if (idl->ident != nullptr) { |
|||
status = ::adapter_group_remove_member(Adapter::session(), group_ident.get(), idl->ident); |
|||
throw_status(status); |
|||
} else { |
|||
is_empty = true; |
|||
} |
|||
} while (!is_empty); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
int test_group_query1(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
{ |
|||
::identity_list *idl = nullptr; |
|||
::PEP_STATUS status = ::adapter_group_query_groups(Adapter::session(), &idl); |
|||
throw_status(status); |
|||
pity.log(Utils::to_string(idl, false)); |
|||
PITYASSERT(idl->ident == nullptr, "adapter_group_query_groups"); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
int test_group_query2(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
{ |
|||
::identity_list *idl = nullptr; |
|||
::PEP_STATUS status = ::adapter_group_query_groups(Adapter::session(), &idl); |
|||
throw_status(status); |
|||
// pity.log(Utils::to_string(idl, false));
|
|||
PITYASSERT(idl->ident->address == ctx->groups.begin()->addr, "adapter_group_query_groups"); |
|||
} |
|||
{ |
|||
pEpIdent mgr = createRawIdent(""); |
|||
::pEp_identity *mgr_ptr = mgr.get(); |
|||
::PEP_STATUS status = ::adapter_group_query_manager( |
|||
Adapter::session(), |
|||
group_ident.get(), |
|||
&mgr_ptr); |
|||
throw_status(status); |
|||
// pity.log(Utils::to_string(mgr_ptr, false));
|
|||
// pity.log(Utils::to_string(ctx->own_ident.get(), false));
|
|||
PITYASSERT( |
|||
*mgr_ptr->address == *ctx->own_ident->address, |
|||
"adapter_group_query_manager - wrong manager"); |
|||
} |
|||
{ |
|||
::identity_list *idl = nullptr; |
|||
::PEP_STATUS status = ::adapter_group_query_members(Adapter::session(), group_ident.get(), &idl); |
|||
throw_status(status); |
|||
// pity.log(Utils::to_string(idl, false));
|
|||
PITYASSERT(idl->ident == nullptr, "adapter_group_query_members"); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int test_group_query3(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
{ |
|||
::identity_list *idl = nullptr; |
|||
::PEP_STATUS status = ::adapter_group_query_groups(Adapter::session(), &idl); |
|||
throw_status(status); |
|||
// pity.log(Utils::to_string(idl, false));
|
|||
PITYASSERT(idl->ident->address == ctx->groups.begin()->addr, "adapter_group_query_groups"); |
|||
} |
|||
{ |
|||
pEpIdent mgr = createRawIdent(""); |
|||
::pEp_identity *mgr_ptr = mgr.get(); |
|||
::PEP_STATUS status = ::adapter_group_query_manager( |
|||
Adapter::session(), |
|||
group_ident.get(), |
|||
&mgr_ptr); |
|||
throw_status(status); |
|||
// pity.log(Utils::to_string(mgr_ptr, false));
|
|||
// pity.log(Utils::to_string(ctx->own_ident.get(), false));
|
|||
PITYASSERT( |
|||
*mgr_ptr->address == *ctx->own_ident->address, |
|||
"adapter_group_query_manager - wrong manager"); |
|||
} |
|||
{ |
|||
::identity_list *idl = nullptr; |
|||
::PEP_STATUS status = ::adapter_group_query_members(Adapter::session(), group_ident.get(), &idl); |
|||
throw_status(status); |
|||
pity.log(Utils::to_string(idl, false)); |
|||
// PITYASSERT(idl->ident->address == ctx->getCpt().addr, "adapter_group_query_members");
|
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
int test_group_query4(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
{ |
|||
::identity_list *idl = nullptr; |
|||
::PEP_STATUS status = ::adapter_group_query_groups(Adapter::session(), &idl); |
|||
throw_status(status); |
|||
// pity.log(Utils::to_string(idl, false));
|
|||
PITYASSERT(idl->ident->address == ctx->groups.begin()->addr, "adapter_group_query_groups"); |
|||
} |
|||
{ |
|||
pEpIdent mgr = createRawIdent(""); |
|||
::pEp_identity *mgr_ptr = mgr.get(); |
|||
::PEP_STATUS status = ::adapter_group_query_manager( |
|||
Adapter::session(), |
|||
group_ident.get(), |
|||
&mgr_ptr); |
|||
throw_status(status); |
|||
// pity.log(Utils::to_string(mgr_ptr, false));
|
|||
// pity.log(Utils::to_string(ctx->own_ident.get(), false));
|
|||
PITYASSERT( |
|||
*mgr_ptr->address == *ctx->own_ident->address, |
|||
"adapter_group_query_manager - wrong manager"); |
|||
} |
|||
{ |
|||
::identity_list *idl = nullptr; |
|||
::PEP_STATUS status = ::adapter_group_query_members(Adapter::session(), group_ident.get(), &idl); |
|||
throw_status(status); |
|||
// pity.log(Utils::to_string(idl, false));
|
|||
// PITYASSERT(idl->ident->address == ctx->getCpt().addr, "adapter_group_query_members");
|
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int main(int argc, char *argv[]) |
|||
{ |
|||
// Adapter::pEpLog::set_enabled(true);
|
|||
// Adapter::GroupDriverReplicator::log_enabled = true;
|
|||
// Adapter::GroupDriverEngine::log_enabled = true;
|
|||
// Adapter::GroupDriverDummy::log_enabled = true;
|
|||
// TestUnit::debug_log_enabled = false;
|
|||
// PityTransport::debug_log_enabled = true;
|
|||
int nodesCount = 23; |
|||
PityModel model{ "model_tofu2", nodesCount }; |
|||
TestUnitSwarm suite = TestUnitSwarm("suite_tofu2"); |
|||
PitySwarm swarm{ "swarm_tofu2", model }; |
|||
suite.addRef(swarm.getSwarmUnit()); |
|||
// ------------------------------------------------------------------------------------
|
|||
swarm.addTestUnit(0, TestUnitSwarm("test_pEp_init", test_pEp_init)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_create_myself", test_create_myself)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_tofu_init_all_peers", test_tofu_init_all_peers)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_start_sync", test_start_sync)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_group_query1", test_group_query1)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_group_create", test_group_create)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_group_query2", test_group_query2)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_group_invite_members", test_group_invite_members)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_group_query3", test_group_query3)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_receive_joined", test_receive_joined)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_group_query3", test_group_query3)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_send_groupmessage", test_send_groupmessage)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_receive", test_receive)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_receive_all", test_receive_all)); |
|||
swarm.addTestUnit( |
|||
0, |
|||
TestUnitSwarm("test_group_remove_member_one_by_one", test_group_remove_member_one_by_one)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_group_query4", test_group_query4)); |
|||
|
|||
//------------------------------------------------------------------------------------
|
|||
for (int i = 1; i < nodesCount; i++) { |
|||
swarm.addTestUnit(i, TestUnitSwarm("test_pEp_init", test_pEp_init)); |
|||
swarm.addTestUnit(i, TestUnitSwarm("test_create_myself", test_create_myself)); |
|||
swarm.addTestUnit(i, TestUnitSwarm("test_tofu_react", test_tofu_react)); |
|||
swarm.addTestUnit(i, TestUnitSwarm("test_start_sync", test_start_sync)); |
|||
swarm.addTestUnit(i, TestUnitSwarm("test_group_join", test_group_join)); |
|||
swarm.addTestUnit(i, TestUnitSwarm("test_receive", test_receive)); |
|||
swarm.addTestUnit(i, TestUnitSwarm("test_send_groupmessage", test_send_groupmessage)); |
|||
swarm.addTestUnit(i, TestUnitSwarm("test_receive_all", test_receive_all)); |
|||
} |
|||
|
|||
suite.run(); |
|||
} |
@ -0,0 +1,135 @@ |
|||
#include "pitytest11/src/PityTest.hh" |
|||
#include "../src/utils.hh" |
|||
#include "framework/framework.hh" |
|||
#include "framework/utils.hh" |
|||
|
|||
// libpEpAdapter
|
|||
#include "../src/Adapter.hh" |
|||
#include "../src/group_manager_api.h" |
|||
#include "../src/pEpLog.hh" |
|||
#include "../src/status_to_string.hh" |
|||
#include "../src/grp_driver_replicator.hh" |
|||
|
|||
// engine
|
|||
#include <pEp/pEpEngine.h> |
|||
#include <pEp/message_api.h> |
|||
#include <pEp/keymanagement.h> |
|||
#include <pEp/identity_list.h> |
|||
#include <pEp/mime.h> |
|||
|
|||
using namespace pEp; |
|||
using namespace pEp::Adapter; |
|||
using namespace pEp::Test::Utils; |
|||
using namespace pEp::PityTest11; |
|||
using namespace pEp::Adapter::pEpLog; |
|||
|
|||
using TextContext = PityPerspective; |
|||
using TestUnitSwarm = PityUnit<TextContext>; |
|||
|
|||
TextContext *local_ctx; |
|||
PityUnit<TextContext> *local_pity; |
|||
|
|||
// The most minimal received msg contains of:
|
|||
// * from address
|
|||
// * content
|
|||
using MinMsgRx = std::tuple<std::string, std::string>; |
|||
|
|||
void tofu_send(TestUnitSwarm &pity, TextContext *ctx, const std::string &addr, const std::string longmessage) |
|||
{ |
|||
pEpMessage msg = createMessage(ctx->own_ident, addr, longmessage); |
|||
EncryptResult msg_enc = encryptAndEncode(msg); |
|||
|
|||
std::string mime_text = std::get<1>(msg_enc); |
|||
ctx->getPeer(addr)->did_tx_encrypted = std::get<2>(msg_enc); |
|||
pity.log("Sending Encrypted[" + std::to_string(std::get<2>(msg_enc)) + "] to: " + addr); |
|||
pity.transport()->sendMsg(addr, mime_text); |
|||
} |
|||
|
|||
MinMsgRx tofu_receive(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
MinMsgRx ret; |
|||
const std::string mime_data_rx = pity.transport()->receiveMsg(); |
|||
|
|||
DecryptResult msg_rx = decryptAndDecode(mime_data_rx); |
|||
pEpMessage msg_rx_dec = std::get<0>(msg_rx); |
|||
ctx->getPeer(msg_rx_dec->from->address)->did_rx_encrypted = std::get<4>(msg_rx); |
|||
pity.log( |
|||
"Received Encrypted[" + std::to_string(std::get<4>(msg_rx)) + |
|||
"] from: " + std::string(msg_rx_dec->from->address)); |
|||
// pity.log("IN:\n " + Utils::to_string(mimeDecode(mime_data_rx).get(), false));
|
|||
pity.log("DECRYPTED:\n " + Utils::to_string(msg_rx_dec.get(), false)); |
|||
std::get<0>(ret) = std::string(msg_rx_dec->from->address); |
|||
std::get<1>(ret) = std::string(msg_rx_dec->longmsg); |
|||
return ret; |
|||
} |
|||
|
|||
void tofu_receiveAndReply(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
MinMsgRx rx_msg = tofu_receive(pity, ctx); |
|||
|
|||
std::string addr = std::get<0>(rx_msg); |
|||
std::string longmsg_orig = std::get<1>(rx_msg); |
|||
pEpMessage msg = createMessage( |
|||
ctx->own_ident, |
|||
addr, |
|||
"REPLY_FROM:'" + std::string(addr) + "' - " + longmsg_orig); |
|||
|
|||
tofu_send(pity, ctx, addr, "REPLY_FROM:'" + std::string(addr) + "' - " + longmsg_orig); |
|||
} |
|||
|
|||
// TESTUNITS
|
|||
// ------------------------------------------------------------------------------------------------
|
|||
|
|||
int test_create_myself(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
Adapter::session.initialize(); |
|||
// Create new identity
|
|||
pity.log("updating or creating identity for me"); |
|||
ctx->own_ident = createOwnIdent(ctx->own_name); |
|||
::PEP_STATUS status = ::myself(Adapter::session(), ctx->own_ident.get()); |
|||
pEp::throw_status(status); |
|||
return 0; |
|||
} |
|||
|
|||
int test_tofu_init_all_peers(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
for (auto &peer : ctx->peers) { |
|||
tofu_send(pity, ctx, peer.addr, "INIT TOFU"); |
|||
tofu_receiveAndReply(pity, ctx); |
|||
PITYASSERT(peer.tofu_done(), "TOFU failed for " + peer.addr); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
int test_tofu_react(TestUnitSwarm &pity, TextContext *ctx) |
|||
{ |
|||
tofu_receiveAndReply(pity, ctx); |
|||
tofu_receive(pity, ctx); |
|||
PITYASSERT(ctx->getCpt().tofu_done(), "TOFU failed for" + ctx->getCpt().addr); |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int main(int argc, char *argv[]) |
|||
{ |
|||
// Adapter::pEpLog::set_enabled(true);
|
|||
// Adapter::GroupDriverReplicator::log_enabled = true;
|
|||
// Adapter::GroupDriverEngine::log_enabled = true;
|
|||
// Adapter::GroupDriverDummy::log_enabled = true;
|
|||
// TestUnit::debug_log_enabled = false;
|
|||
// PityTransport::debug_log_enabled = true;
|
|||
int nodesCount = 23; |
|||
PityModel model{ "model_tofu2", nodesCount }; |
|||
TestUnitSwarm suite = TestUnitSwarm("suite_tofu2"); |
|||
PitySwarm swarm{ "swarm_tofu2", model }; |
|||
suite.addRef(swarm.getSwarmUnit()); |
|||
// ------------------------------------------------------------------------------------
|
|||
swarm.addTestUnit(0, TestUnitSwarm("test_create_myself", test_create_myself)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("test_tofu_init_all_peers", test_tofu_init_all_peers)); |
|||
|
|||
for (int i = 1; i < nodesCount; i++) { |
|||
swarm.addTestUnit(i, TestUnitSwarm("test_create_myself", test_create_myself)); |
|||
swarm.addTestUnit(i, TestUnitSwarm("test_tofu_react", test_tofu_react)); |
|||
} |
|||
suite.run(); |
|||
} |
@ -0,0 +1,53 @@ |
|||
// This file is under GNU General Public License 3.0
|
|||
// see LICENSE.txt
|
|||
|
|||
#include "framework/framework.hh" |
|||
#include "framework/utils.hh" |
|||
#include <iostream> |
|||
#include <assert.h> |
|||
|
|||
#include <pEp/keymanagement.h> |
|||
#include <pEp/sync_api.h> |
|||
|
|||
#include "../src/Adapter.hh" |
|||
|
|||
using namespace pEp; |
|||
|
|||
PEP_STATUS messageToSend(struct _message *msg) |
|||
{ |
|||
TESTLOG("called"); |
|||
return PEP_STATUS_OK; |
|||
} |
|||
|
|||
PEP_STATUS notifyHandshake(pEp_identity *me, pEp_identity *partner, ::sync_handshake_signal signal) |
|||
{ |
|||
TESTLOG("called"); |
|||
return PEP_STATUS_OK; |
|||
} |
|||
|
|||
int main(int argc, char **argv) |
|||
{ |
|||
pEp::Test::setup(argc, argv); |
|||
Adapter::session.initialize(Adapter::SyncModes::Async, false); |
|||
// Create new identity
|
|||
TESTLOG("updating or creating identity for me"); |
|||
::pEp_identity *me = ::new_identity("alice@peptest.ch", NULL, "23", "Who the F* is Alice"); |
|||
assert(me); |
|||
::PEP_STATUS status = ::myself(Adapter::session(), me); |
|||
::free_identity(me); |
|||
throw_status(status); |
|||
|
|||
// start and stop sync repeatedly
|
|||
unsigned long long int nrIters = 1000 * 1000 * 1000; |
|||
for (int i = 0; i < nrIters; i++) { |
|||
TESTLOG("RUN NR: "); |
|||
TESTLOG(i); |
|||
TESTLOG("SYNC START"); |
|||
TESTLOG("starting the adapter including sync"); |
|||
Adapter::start_sync(); |
|||
TESTLOG("SYNC STOP"); |
|||
Utils::sleep_millis(500); |
|||
Adapter::stop_sync(); |
|||
} |
|||
return 0; |
|||
} |
@ -0,0 +1,65 @@ |
|||
#include "pitytest11/src/PityTest.hh" |
|||
#include "../src/utils.hh" |
|||
#include "framework/framework.hh" |
|||
#include "framework/utils.hh" |
|||
|
|||
#include <pEp/pEpEngine.h> |
|||
#include <pEp/message_api.h> |
|||
#include <pEp/keymanagement.h> |
|||
#include <pEp/identity_list.h> |
|||
#include <pEp/Adapter.hh> |
|||
#include <pEp/status_to_string.hh> |
|||
#include <pEp/mime.h> |
|||
|
|||
using namespace pEp; |
|||
using namespace pEp::Adapter; |
|||
using namespace pEp::Test::Utils; |
|||
using namespace pEp::PityTest11; |
|||
|
|||
// Test template for 3 nodes (processes) running the same 2 tests each in their own environment in parallel
|
|||
using TestUnitSwarm = PityUnit<PityPerspective>; |
|||
|
|||
// This is the 1st test unit
|
|||
int test_func1(PityUnit<PityPerspective> &pity, PityPerspective *ctx) |
|||
{ |
|||
pity.log(ctx->own_name); |
|||
pity.log("getName: " + pity.getName()); |
|||
pity.log("getPath: " + pity.getPath()); |
|||
pity.log("getPathShort: " + pity.getPathShort()); |
|||
pity.log("getTransportDir: " + pity.getTransportDir()); |
|||
pity.log("getProcessDir: " + pity.getProcessDir()); |
|||
pity.log("getGlobalRootDir: " + pity.getGlobalRootDir()); |
|||
pity.log("to_string: " + pity.to_string()); |
|||
|
|||
PITYASSERT(true, ""); |
|||
return 0; |
|||
} |
|||
|
|||
// This is the 2nd test unit
|
|||
int test_func2(PityUnit<PityPerspective> &pity, PityPerspective *ctx) |
|||
{ |
|||
pity.log(ctx->own_name); |
|||
PITYASSERT(false, ""); |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int main(int argc, char *argv[]) |
|||
{ |
|||
int nodecount = 3; |
|||
PityModel model{ "templ_swarm_multi", nodecount }; |
|||
PitySwarm swarm{ "swarm_multi", model }; |
|||
|
|||
// 1. Create the swarm process TestUnit
|
|||
// 2. Append a PityUnit to process 1 unit
|
|||
swarm.addTestUnit(0, TestUnitSwarm("unit1", test_func1)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("unit1_1", test_func2)); |
|||
|
|||
swarm.addTestUnit(1, TestUnitSwarm("unit2", test_func1)); |
|||
swarm.addTestUnit(1, TestUnitSwarm("unit2_1", test_func2)); |
|||
|
|||
swarm.addTestUnit(2, TestUnitSwarm("unit3", test_func1)); |
|||
swarm.addTestUnit(2, TestUnitSwarm("unit3_1", test_func2)); |
|||
|
|||
swarm.run(); |
|||
} |
@ -0,0 +1,55 @@ |
|||
#include "pitytest11/src/PityTest.hh" |
|||
#include "../src/utils.hh" |
|||
#include "framework/framework.hh" |
|||
#include "framework/utils.hh" |
|||
|
|||
#include <pEp/pEpEngine.h> |
|||
#include <pEp/message_api.h> |
|||
#include <pEp/keymanagement.h> |
|||
#include <pEp/identity_list.h> |
|||
#include <pEp/Adapter.hh> |
|||
#include <pEp/status_to_string.hh> |
|||
#include <pEp/mime.h> |
|||
|
|||
using namespace pEp; |
|||
using namespace pEp::Adapter; |
|||
using namespace pEp::Test::Utils; |
|||
using namespace pEp::PityTest11; |
|||
|
|||
using TestUnitSwarm = PityUnit<PityPerspective>; |
|||
// Test template for a single process with 2 test units that run in sequence
|
|||
|
|||
// This is the 1st test unit
|
|||
int test_func1(PityUnit<PityPerspective> &pity, PityPerspective *ctx) |
|||
{ |
|||
// here some example values from the PityUnit
|
|||
pity.log("getName: " + pity.getName()); |
|||
pity.log("getPath: " + pity.getPath()); |
|||
pity.log("getPathShort: " + pity.getPathShort()); |
|||
pity.log("getTransportDir: " + pity.getTransportDir()); |
|||
pity.log("getProcessDir: " + pity.getProcessDir()); |
|||
pity.log("getGlobalRootDir: " + pity.getGlobalRootDir()); |
|||
pity.log("to_string: " + pity.to_string(false)); |
|||
PITYASSERT(true, ""); |
|||
return 0; |
|||
} |
|||
|
|||
// This is the 2nd test unit
|
|||
int test_func2(PityUnit<PityPerspective> &pity, PityPerspective *ctx) |
|||
{ |
|||
pity.log(ctx->own_name); |
|||
PITYASSERT(false, ""); |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int main(int argc, char *argv[]) |
|||
{ |
|||
PityModel model{ "templ_swarm_single", 1 }; |
|||
PitySwarm swarm{ "swarm_single", model }; |
|||
|
|||
swarm.addTestUnit(0, TestUnitSwarm("unit1", test_func1)); |
|||
swarm.addTestUnit(0, TestUnitSwarm("unit1_1", test_func2)); |
|||
|
|||
swarm.run(); |
|||
} |
@ -0,0 +1,152 @@ |
|||
#include "pitytest11/src/PityTest.hh" |
|||
#include "../src/utils.hh" |
|||
#include "framework/framework.hh" |
|||
#include "framework/utils.hh" |
|||
|
|||
#include <pEp/pEpEngine.h> |
|||
#include <pEp/keymanagement.h> |
|||
#include <pEp/Adapter.hh> |
|||
#include <pEp/status_to_string.hh> |
|||
|
|||
using namespace pEp; |
|||
using namespace pEp::Adapter; |
|||
using namespace pEp::Test::Utils; |
|||
using namespace pEp::PityTest11; |
|||
|
|||
using TestUnitSwarm = PityUnit<PityPerspective>; |
|||
|
|||
|
|||
bool did_tx_encrypted = false; |
|||
bool did_rx_encrypted = false; |
|||
|
|||
// The most minimal received msg contains of:
|
|||
// * from addres
|
|||
// * content
|
|||
using MinMsgRx = std::tuple<std::string, std::string>; |
|||
|
|||
void send(PityUnit<PityPerspective> &pity, PityPerspective *ctx) |
|||
{ |
|||
// Create Message
|
|||
pity.log("Initiating TOFU..."); |
|||
pEpMessage msg = createMessage(ctx->own_ident, ctx->getCpt().addr, "INIT TOFU"); |
|||
pity.log("BEFORE encrypt: \n" + Utils::to_string(msg.get())); |
|||
|
|||
//Encrypt
|
|||
EncryptResult msg_encrypted = encryptMessage(msg); |
|||
did_tx_encrypted = std::get<2>(msg_encrypted); |
|||
pity.log("TX COULD ENCRYPT: " + std::to_string(did_tx_encrypted)); |
|||
pity.log("AFTER encrypt: \n" + Utils::to_string(std::get<0>(msg_encrypted).get())); |
|||
|
|||
// Encode
|
|||
std::string mime_text = mimeEncode(std::get<0>(msg_encrypted)); |
|||
|
|||
// Send
|
|||
pity.transport()->sendMsg(ctx->getCpt().addr, mime_text); |
|||
} |
|||
|
|||
MinMsgRx tofu_receive(PityUnit<PityPerspective> &pity, PityPerspective *ctx) |
|||
{ |
|||
MinMsgRx ret; |
|||
// Receive
|
|||
std::string mime_data_rx = pity.transport()->receiveMsg(); |
|||
|
|||
// Decode
|
|||
pEpMessage mime_decoded = mimeDecode(mime_data_rx); |
|||
pity.log("RX message - BEFORE decrypt: \n" + Utils::to_string(mime_decoded.get())); |
|||
|
|||
// Decrypt
|
|||
DecryptResult msg_decrypted = decryptMessage(mime_decoded); |
|||
pEpMessage msg_rx_dec = std::get<0>(msg_decrypted); |
|||
did_rx_encrypted = std::get<4>(msg_decrypted); |
|||
pity.log("RX WAS ENCRYPTED: " + std::to_string(did_rx_encrypted)); |
|||
pity.log("RX message - AFTER decrypt: \n" + Utils::to_string(msg_rx_dec.get())); |
|||
|
|||
// Return result
|
|||
std::get<0>(ret) = std::string(msg_rx_dec->from->address); |
|||
std::get<1>(ret) = std::string(msg_rx_dec->longmsg); |
|||
return ret; |
|||
} |
|||
|
|||
void receiveAndReply(PityUnit<PityPerspective> &pity, PityPerspective *ctx, MinMsgRx msg_orig) |
|||
{ |
|||
// Create Message
|
|||
std::string addr_orig = std::get<0>(msg_orig); |
|||
std::string longmsg_orig = std::get<1>(msg_orig); |
|||
|
|||
pEpMessage msg = createMessage( |
|||
ctx->own_ident, |
|||
addr_orig, |
|||
"REPLY[ " + std::string(addr_orig) + " ] " + longmsg_orig); |
|||
|
|||
// Encrypt
|
|||
pity.log("TX message - BEFORE encrypt: \n" + Utils::to_string(msg.get())); |
|||
|
|||
EncryptResult eres = encryptMessage(msg); |
|||
pEpMessage msg_encrypted = std::get<0>(eres); |
|||
did_tx_encrypted = std::get<2>(eres); |
|||
pity.log("TX COULD ENCRYPT: " + std::to_string(did_tx_encrypted)); |
|||
pity.log("TX message - AFTER encrypt: \n" + Utils::to_string(msg_encrypted.get())); |
|||
|
|||
// Encode
|
|||
std::string mime_data_tx = mimeEncode(msg_encrypted); |
|||
|
|||
// Send
|
|||
pity.transport()->sendMsg(addr_orig, mime_data_tx); |
|||
} |
|||
|
|||
int tofu(PityUnit<PityPerspective> &pity, PityPerspective *ctx, bool init) |
|||
{ |
|||
pity.log("Model : " + ctx->model.getName()); |
|||
pity.log("myself : " + ctx->own_name); |
|||
pity.log("partner: " + ctx->getCpt().addr); |
|||
pity.log("HOME : " + std::string(getenv("HOME"))); |
|||
pity.log("PUD : " + std::string(::per_user_directory())); |
|||
Adapter::session.initialize(); |
|||
|
|||
// Create new identity
|
|||
pity.log("updating or creating identity for me"); |
|||
ctx->own_ident = createOwnIdent(ctx->own_name); |
|||
::PEP_STATUS status = ::myself(Adapter::session(), ctx->own_ident.get()); |
|||
pEp::throw_status(status); |
|||
if (init) { |
|||
send(pity, ctx); |
|||
} |
|||
|
|||
MinMsgRx rx_msg = tofu_receive(pity, ctx); |
|||
receiveAndReply(pity, ctx, rx_msg); |
|||
|
|||
if (!init) { |
|||
tofu_receive(pity, ctx); |
|||
} |
|||
|
|||
PITYASSERT(did_tx_encrypted, "could never tofu_send encrypted"); |
|||
PITYASSERT(did_rx_encrypted, "no encrypted msg received"); |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int main(int argc, char *argv[]) |
|||
{ |
|||
PityUnit<PityPerspective>::debug_log_enabled = false; |
|||
|
|||
int nodesCount = 2; |
|||
PityModel model{ "test_tofu_react", nodesCount }; |
|||
|
|||
TestUnitSwarm suite("suite_tofu"); |
|||
PitySwarm swarm{ "swarm_tofu", model }; |
|||
suite.addRef(swarm.getSwarmUnit()); |
|||
|
|||
swarm.addTestUnit( |
|||
0, |
|||
TestUnitSwarm("tofu1", [](PityUnit<PityPerspective> &unit, PityPerspective *ctx) { |
|||
return tofu(unit, ctx, true); |
|||
})); |
|||
|
|||
swarm.addTestUnit( |
|||
1, |
|||
TestUnitSwarm("tofu2", [](PityUnit<PityPerspective> &unit, PityPerspective *ctx) { |
|||
return tofu(unit, ctx, false); |
|||
})); |
|||
|
|||
suite.run(); |
|||
} |
Loading…
Reference in new issue