Browse Source

Merge branch 'LIB-11'

# Conflicts:
#	test/Makefile
pull/8/head
heck 4 years ago
parent
commit
ce136d408d
  1. 5
      .clang-format
  2. 31
      .gitignore
  3. 201
      src/Adapter.cc
  4. 69
      src/Adapter.hh
  5. 69
      src/Adapter.hxx
  6. 9
      src/Makefile
  7. 96
      src/adapter_group.cc
  8. 36
      src/callback_dispatcher.cc
  9. 9
      src/callback_dispatcher.hh
  10. 146
      src/group_manager_api.cc
  11. 115
      src/group_manager_api.h
  12. 383
      src/grp_driver_dummy.cc
  13. 69
      src/grp_driver_dummy.hh
  14. 63
      src/grp_driver_engine.cc
  15. 52
      src/grp_driver_engine.hh
  16. 267
      src/grp_driver_replicator.cc
  17. 83
      src/grp_driver_replicator.hh
  18. 66
      src/grp_manager_interface.hh
  19. 12264
      src/internal/sqlite3.h
  20. 367
      src/listmanager_dummy.cc
  21. 63
      src/listmanager_dummy.hh
  22. 129
      src/pEpLog.cc
  23. 211
      src/pEpLog.hh
  24. 167
      src/pEpSQLite.cc
  25. 64
      src/pEpSQLite.hh
  26. 234229
      src/sqlite3.c
  27. 297
      src/std_utils.cc
  28. 94
      src/std_utils.hh
  29. 22
      src/std_utils.hxx
  30. 259
      src/utils.cc
  31. 35
      src/utils.hh
  32. 16
      test/Makefile
  33. 17
      test/framework/framework.cc
  34. 2
      test/framework/framework.hh
  35. 367
      test/framework/utils.cc
  36. 137
      test/framework/utils.hh
  37. 39
      test/pitytest11/Makefile
  38. 456
      test/pitytest11/src/AbstractPityUnit.cc
  39. 135
      test/pitytest11/src/AbstractPityUnit.hh
  40. 32
      test/pitytest11/src/PityModel.cc
  41. 53
      test/pitytest11/src/PityModel.hh
  42. 63
      test/pitytest11/src/PityNode.cc
  43. 55
      test/pitytest11/src/PityNode.hh
  44. 48
      test/pitytest11/src/PityPerspective.cc
  45. 59
      test/pitytest11/src/PityPerspective.hh
  46. 146
      test/pitytest11/src/PitySwarm.cc
  47. 59
      test/pitytest11/src/PitySwarm.hh
  48. 32
      test/pitytest11/src/PityTest.hh
  49. 97
      test/pitytest11/src/PityTransport.cc
  50. 45
      test/pitytest11/src/PityTransport.hh
  51. 88
      test/pitytest11/src/PityTree.hh
  52. 220
      test/pitytest11/src/PityTree.hxx
  53. 75
      test/pitytest11/src/PityUnit.hh
  54. 136
      test/pitytest11/src/PityUnit.hxx
  55. 41
      test/pitytest11/src/fs_mutex.cc
  56. 25
      test/pitytest11/src/fs_mutex.hh
  57. 32
      test/pitytest11/test/test_assert.cc
  58. 94
      test/pitytest11/test/test_execmodes.cc
  59. 68
      test/pitytest11/test/test_linear.cc
  60. 161
      test/pitytest11/test/test_pitytree.cc
  61. 98
      test/pitytest11/test/test_processdirs.cc
  62. 88
      test/pitytest11/test/test_swarm.cc
  63. 29
      test/test_adapter.cc
  64. 47
      test/test_adapter_cxx.cc
  65. 14
      test/test_ensure_passphrase.cc
  66. 185
      test/test_group.cc
  67. 12
      test/test_leave_device_group.cc
  68. 307
      test/test_listmanager_dummy.cc
  69. 5
      test/test_message_cache.cc
  70. 642
      test/test_pEpSQLite.cc
  71. 115
      test/test_pEpSQLite.hh
  72. 5
      test/test_passphrase_cache.cc
  73. 5
      test/test_semaphore.cc
  74. 492
      test/test_swarm_group.cc
  75. 135
      test/test_swarm_tofu.cc
  76. 53
      test/test_sync_init.cc
  77. 65
      test/test_template_swarm_multi.cc
  78. 55
      test/test_template_swarm_single.cc
  79. 152
      test/test_tofu.cc

5
.clang-format

@ -1,6 +1,6 @@
BasedOnStyle: LLVM
Language: Cpp
Standard: c++14
Standard: c++11
DerivePointerAlignment: true
SortIncludes: false
ReflowComments: false
@ -38,4 +38,5 @@ SpaceAfterTemplateKeyword: false
AccessModifierOffset: -4
AllowShortBlocksOnASingleLine: Always
IndentPPDirectives: BeforeHash
Cpp11BracedListStyle: false
Cpp11BracedListStyle: false
BreakStringLiterals: false

31
.gitignore

@ -2,6 +2,7 @@
*.a
*.d
*.swp
.DS_Store
ws
test_adapter
.gnupg
@ -24,3 +25,33 @@ test_library
test_message_cache
test_passphrase_cache
test_semaphore
test_lm_dummy
test_sqlite3
test_pEpsqlite
test_listmanager_dummy
test_tofu_react
test_pEpLog_basic
/test/pitytest_data
*.db
/test/pitytest11/test/test_transport
/test/pitytest11/test/test_processdirs
/test/pitytest11/test/test_execmodes
/test/pitytest11/test/test_linear
/test/pitytest11/test/test_model
/test/pitytest11/pitytest_data/*
/test/pitytest11/test/test_swarm
/test/test_update_ident
/test/test_template_swarm_single
/test/test_template_swarm_multi
/test/pitytest11/test/test_copy
/test/pitytest11/test/test_pitytree
/test/test_suite_all
/test/test_sync_init
/test/test_tofu2
/test/pitytest11/test/test_assert
/test/test_tofu
/test/test_tofu_one2many
/test/test_swarm_group
/test/pitytest11/test/pitytest_data/

201
src/Adapter.cc

@ -8,6 +8,9 @@
#include "status_to_string.hh"
#include "pEpLog.hh"
#include "passphrase_cache.hh"
#include "callback_dispatcher.hh"
#include "group_manager_api.h"
#include <iostream>
using namespace std;
@ -36,18 +39,12 @@ namespace pEp {
throw RuntimeError(_status, status);
}
RuntimeError::RuntimeError(const std::string &_text, ::PEP_STATUS _status)
: std::runtime_error(_text.c_str()), text(_text), status(_status)
RuntimeError::RuntimeError(const std::string &_text, ::PEP_STATUS _status) :
std::runtime_error(_text.c_str()), text(_text), status(_status)
{
}
namespace Adapter {
// private
SyncModes _sync_mode = SyncModes::Async;
::messageToSend_t _messageToSend = nullptr;
::notifyHandshake_t _notifyHandshake = nullptr;
bool _adapter_manages_sync_thread = false;
::inject_sync_event_t _inject_action = _inject_sync_event;
std::thread _sync_thread;
::utility::locked_queue<SYNC_EVENT, ::free_Sync_event> sync_evt_q;
std::mutex mut;
@ -58,67 +55,6 @@ namespace pEp {
return _sync_thread.get_id();
}
// public
void sync_initialize(
SyncModes mode,
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake,
bool adapter_manages_sync_thread)
{
_messageToSend = messageToSend;
_notifyHandshake = notifyHandshake;
_adapter_manages_sync_thread = adapter_manages_sync_thread;
set_sync_mode(mode);
return;
}
// public
void set_sync_mode(SyncModes mode)
{
// std::lock_guard<mutex> lock(mut);
_sync_mode = mode;
if (_sync_mode == SyncModes::Sync) {
// init session with inject_sync = process
// stop sync
session(release);
_inject_action = _process_sync_event;
session(init);
::register_sync_callbacks(session(), nullptr, _notifyHandshake, _retrieve_next_sync_event);
if(!_adapter_manages_sync_thread) {
shutdown();
} else {
// The adapter need to shutdown sync thread
}
}
if (_sync_mode == SyncModes::Async) {
// init session with inject_sync = queue
// start sync thread
session(release);
_inject_action = _inject_sync_event;
session(init);
if(!_adapter_manages_sync_thread) {
if (!is_sync_running()) {
startup<void>(_messageToSend, _notifyHandshake, nullptr, nullptr);
}
} else {
// The adapter need to do sync thread start up
}
}
if (_sync_mode == SyncModes::Off) {
// init sesssion with inject_sync = null
// stop sync thread
if(!_adapter_manages_sync_thread) {
shutdown();
} else {
// Adapter needs to shutdown sync thread
}
session(release);
_inject_action = _inject_sync_event;
session(init);
}
return;
}
// private
int _process_sync_event(::SYNC_EVENT ev, void *management)
{
@ -171,34 +107,104 @@ namespace pEp {
return _sync_thread.get_id() == this_thread::get_id();
}
// public
::PEP_SESSION Session::operator()(session_action action)
// ---------------------------------------------------------------------------------------
Session::Session() :
_messageToSend{ nullptr }, _notifyHandshake{ nullptr }, _sync_mode{ SyncModes::Async },
_adapter_manages_sync_thread{ false }
{
}
void Session::initialize(SyncModes sync_mode, bool adapter_manages_sync_thread)
{
pEpLog("Initializing session with CallbackDispatcher...");
_init(
pEp::CallbackDispatcher::messageToSend,
pEp::CallbackDispatcher::notifyHandshake,
sync_mode,
adapter_manages_sync_thread);
}
void Session::initialize(
SyncModes sync_mode,
bool adapter_manages_sync_thread,
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake)
{
pEpLog("Initializing session...");
_init(messageToSend, notifyHandshake, sync_mode, adapter_manages_sync_thread);
}
void Session::_init(
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake,
SyncModes sync_mode,
bool adapter_manages_sync_thread)
{
// cache the values for sync-thread session creation
_messageToSend = messageToSend;
_notifyHandshake = notifyHandshake;
_sync_mode = sync_mode;
_adapter_manages_sync_thread = adapter_manages_sync_thread;
refresh();
::adapter_group_init();
}
void Session::refresh()
{
std::lock_guard<mutex> lock(mut);
release();
::PEP_STATUS status = ::PEP_STATUS_OK;
switch (action) {
case release:
if (_session.get()) {
_session = nullptr;
}
break;
case init:
if (!_session.get()) {
::PEP_SESSION session_;
status = ::init(&session_, _messageToSend, _inject_action, _ensure_passphrase);
throw_status(status);
_session = SessionPtr{session_, ::release};
}
break;
default:
status = ::PEP_ILLEGAL_VALUE;
// Switch to mode "Sync" ensures the sync thread to be shutdown
if (_sync_mode == SyncModes::Sync) {
// process the event directly
_inject_action = _process_sync_event;
if (!_adapter_manages_sync_thread) {
stop_sync();
} else {
// The adapter needs to shutdown sync thread
}
}
// Switch to mode "ASync", sync thread needs to be started using start_sync
if (_sync_mode == SyncModes::Async) {
// put the event on queue
_inject_action = _inject_sync_event;
}
// create
::PEP_SESSION session_;
::PEP_STATUS status;
status = ::init(&session_, _messageToSend, _inject_action, _ensure_passphrase);
throw_status(status);
return _session.get();
status = ::register_sync_callbacks(
session_,
nullptr,
_notifyHandshake,
_retrieve_next_sync_event);
if (status != PEP_STATUS_OK) {
pEpLog("libpEpAdapter: WARNING - session is initialized but without sync/callbacks. "
"This is normal if there are no own identities yet. Call session.init() again to "
"re-initialize the session after creating an own identity.");
}
// store
_session = SessionPtr{ session_, ::release };
}
void Session::release()
{
if (_session.get()) {
_session = nullptr;
}
}
// public
::PEP_SESSION Session::operator()()
{
if (!_session.get()) {
throw std::runtime_error(
"libpEpAdapter: No session! Before use, call session.initialize() for each thread");
} else {
return _session.get();
}
}
// public
@ -207,21 +213,30 @@ namespace pEp {
_inject_sync_event(nullptr, nullptr);
}
void start_sync()
{
startup<pEp::CallbackDispatcher>(
&callback_dispatcher,
&::pEp::CallbackDispatcher::on_startup,
&::pEp::CallbackDispatcher::on_shutdown);
}
// public
void shutdown()
void stop_sync()
{
pEpLog("called");
if (_sync_thread.joinable()) {
pEpLog("sync_is_running - injecting null event");
inject_sync_shutdown();
_sync_thread.join();
pEp::callback_dispatcher.notifyHandshake(nullptr, nullptr, SYNC_NOTIFY_STOP);
}
}
// public
bool is_sync_running()
{
if(!_adapter_manages_sync_thread) {
if (!session._adapter_manages_sync_thread) {
return _sync_thread.joinable();
} else {
return false;

69
src/Adapter.hh

@ -11,6 +11,7 @@
#include <thread>
#include <pEp/sync_api.h>
#include "callback_dispatcher.hh"
namespace pEp {
@ -29,28 +30,19 @@ namespace pEp {
// public
enum class SyncModes
{
Off,
Sync,
Async
};
void sync_initialize(
SyncModes mode,
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake,
bool adapter_manages_sync_thread);
void set_sync_mode(SyncModes mode);
int _inject_sync_event(::SYNC_EVENT ev, void *management);
int _process_sync_event(::SYNC_EVENT ev, void *management);
::PEP_STATUS _ensure_passphrase(::PEP_SESSION session, const char *fpr);
void start_sync();
template<class T = void>
void startup(
messageToSend_t messageToSend,
notifyHandshake_t notifyHandshake,
T *obj = nullptr,
std::function<void(T *)> _startup = nullptr,
std::function<void(T *)> _shutdown = nullptr);
@ -61,18 +53,53 @@ namespace pEp {
// returns the thread id of the sync thread
std::thread::id sync_thread_id();
enum session_action
{
init,
release,
};
class Session {
public:
// TODO: needed because libpEpAdapter provides a static instance
// the session needs to be initialized in order to be usable.
Session();
// Init using CallbackDispatcher
// CAUTION: This may result in a partially initialized session.
// If there are any problem with register_sync_callbacks(), it will still
// succeed. (e.g. due to no own identities yet)
// BUT
// * Sync will not work
// * Group Encryption will not work
// TODO: This needs to be resolved in the engine, new func register_callbacks()
// that is not sync specific, and move the sync-checks to "start-sync()"
void initialize(SyncModes sync_mode = SyncModes::Async, bool adapter_manages_sync_thread = false);
// Arbitrary callbacks
void initialize(
SyncModes sync_mode,
bool adapter_manages_sync_thread,
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake);
// re-creates the session using same values
void refresh();
// Not copyable
Session(const Session &) = delete;
Session operator=(const Session&) = delete;
void release();
PEP_SESSION operator()();
SyncModes _sync_mode;
::messageToSend_t _messageToSend;
::notifyHandshake_t _notifyHandshake;
bool _adapter_manages_sync_thread;
::inject_sync_event_t _inject_action;
private:
void _init(
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake,
SyncModes sync_mode,
bool adapter_manages_sync_thread);
using SessionPtr = std::unique_ptr<_pEpSession, std::function<void(PEP_SESSION)>>;
SessionPtr _session = nullptr;
public:
PEP_SESSION operator()(session_action action = init);
};
extern thread_local Session session;
@ -84,7 +111,7 @@ namespace pEp {
// injects a NULL event into sync_event_queue to denote sync thread to
// shutdown, and joins & removes the sync thread
void shutdown();
void stop_sync();
bool is_sync_running();
bool in_shutdown();

69
src/Adapter.hxx

@ -14,8 +14,6 @@ namespace pEp {
namespace Adapter {
using std::function;
extern ::messageToSend_t _messageToSend;
extern ::notifyHandshake_t _notifyHandshake;
extern std::thread _sync_thread;
extern ::utility::locked_queue<::SYNC_EVENT, ::free_Sync_event> sync_evt_q;
@ -38,12 +36,10 @@ namespace pEp {
*/
// private
template<class T>
void sync_thread(T *obj, function<void(T *)> _startup, function<void(T *)> _shutdown)
void sync_thread(Session *rhs, T *obj, function<void(T *)> _startup, function<void(T *)> _shutdown)
{
pEpLog("called");
_ex = nullptr;
assert(_messageToSend);
assert(_notifyHandshake);
// 1. Execute registered startup function
if (obj && _startup) {
@ -52,29 +48,18 @@ namespace pEp {
pEpLog("creating session for the sync thread");
// 2. Create session for the sync thread
session();
// 3. register_sync_callbacks()
{
// TODO: Do we need to use a passphraseWrap here???
pEpLog("register_sync_callbacks()");
::PEP_STATUS status = ::register_sync_callbacks(
session(),
nullptr,
_notifyHandshake,
_retrieve_next_sync_event);
pEpLog("register_sync_callbacks() return:" << status);
// Convert status into exception and store it
// set register_done AFTER that
try {
throw_status(status);
register_done.store(true);
} catch (...) {
_ex = std::current_exception();
register_done.store(true);
return;
}
// 3. register_sync_callbacks() (in session.initialize())
try {
session.initialize(
rhs->_sync_mode,
rhs->_adapter_manages_sync_thread,
rhs->_messageToSend,
rhs->_notifyHandshake);
register_done.store(true);
} catch (...) {
_ex = std::current_exception();
register_done.store(true);
return;
}
pEpLog("sync protocol loop started");
@ -86,8 +71,7 @@ namespace pEp {
unregister_sync_callbacks(session());
// 6. Release the session
// TODO: Maybe do that AFTER shutdown?
session(release);
session.release();
// 7. Execute registered shutdown function
if (obj && _shutdown) {
@ -97,38 +81,25 @@ namespace pEp {
/*
* Sync Thread Startup
* 1. ensure session for the main thread (registers: messageToSend, _inject_sync_event, _ensure_passphrase)
* 1. throw if main thread session is not initialized
* 2. Start the sync thread
* 3. Defer execution until sync thread register_sync_callbacks() has returned
* 4. Throw pending exception from the sync thread
*/
// private
template<class T>
void startup(
::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake,
T *obj,
function<void(T *)> _startup,
function<void(T *)> _shutdown)
void startup(T *obj, std::function<void(T *)> _startup, std::function<void(T *)> _shutdown)
{
pEpLog("called");
if (messageToSend) {
_messageToSend = messageToSend;
}
if (notifyHandshake) {
_notifyHandshake = notifyHandshake;
}
pEpLog("ensure session for the main thread");
// 1. re-initialize session for the main thread (registers: messageToSend, _inject_sync_event, _ensure_passphrase)
session(release);
session(init);
// refresh the session
// due to partially initialized session, see session.initialize()
session.refresh();
if (!_sync_thread.joinable()) {
register_done.store(false);
pEpLog("creating sync-thread");
// 2. Start the sync thread
_sync_thread = std::thread(sync_thread<T>, obj, _startup, _shutdown);
_sync_thread = std::thread(sync_thread<T>, &session, obj, _startup, _shutdown);
// 3. Defer execution until sync thread register_sync_callbacks() has returned
while (register_done.load() == false) {
pEpLog("waiting for sync-thread to init...");

9
src/Makefile

@ -5,10 +5,13 @@
include ../Makefile.conf
SOURCE=$(wildcard *.cc)
SRC_CXX=$(wildcard *.cc)
SRC_C=$(wildcard *.c)
HEADERS=$(wildcard *.hh *.hxx *.h)
OBJECTS=$(subst .cc,.o,$(SOURCE))
DEPENDS=$(subst .cc,.d,$(SOURCE))
OBJ_CXX=$(subst .cc,.o,$(SRC_CXX))
OBJ_C=$(subst .c,.o,$(SRC_C))
OBJECTS=$(OBJ_CXX) $(OBJ_C)
DEPENDS=$(subst .cc,.d,$(SRC_CXX))
CXXFLAGS+= -MMD -MP
ifneq ($(MAKECMDGOALS),clean)

96
src/adapter_group.cc

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

36
src/callback_dispatcher.cc

@ -5,6 +5,7 @@
#include "passphrase_cache.hh"
#include <stdexcept>
#include <cassert>
#include "Adapter.hh"
pEp::CallbackDispatcher pEp::callback_dispatcher;
@ -51,7 +52,7 @@ namespace pEp {
}
if (targets.empty()) {
stop_sync();
Adapter::stop_sync();
}
}
@ -73,39 +74,6 @@ namespace pEp {
}
}
void CallbackDispatcher::start_sync()
{
pEpLog("called");
callback_dispatcher.semaphore.go();
pEp::Adapter::startup<CallbackDispatcher>(
CallbackDispatcher::messageToSend,
CallbackDispatcher::notifyHandshake,
&callback_dispatcher,
&CallbackDispatcher::on_startup,
&CallbackDispatcher::on_shutdown);
pEpLog("all targets signal: SYNC_NOTIFY_START");
for (auto target : callback_dispatcher.targets) {
if (target.notifyHandshake) {
target.notifyHandshake(nullptr, nullptr, SYNC_NOTIFY_START);
}
}
}
void CallbackDispatcher::stop_sync()
{
callback_dispatcher.semaphore.stop();
Adapter::shutdown();
callback_dispatcher.semaphore.go();
for (auto target : callback_dispatcher.targets) {
if (target.notifyHandshake) {
target.notifyHandshake(nullptr, nullptr, SYNC_NOTIFY_STOP);
}
}
}
PEP_STATUS CallbackDispatcher::_messageToSend(::message *msg)
{
if (Adapter::on_sync_thread() && !msg) {

9
src/callback_dispatcher.hh

@ -7,10 +7,10 @@
#include <vector>
#include <functional>
#include <mutex>
#include "Adapter.hh"
#include "Semaphore.hh"
#include "passphrase_cache.hh"
#include <pEp/sync_api.h>
namespace pEp {
// use this class when implementing a desktop adapter
// register different interfaces with add()
@ -37,18 +37,15 @@ namespace pEp {
proc on_shutdown = nullptr);
void remove(::messageToSend_t messageToSend);
static void start_sync();
static void stop_sync();
static PEP_STATUS messageToSend(::message *msg);
static PEP_STATUS notifyHandshake(
::pEp_identity *me,
::pEp_identity *partner,
::sync_handshake_signal signal);
protected:
void on_startup();
void on_shutdown();
protected:
PEP_STATUS _messageToSend(::message *msg);
PEP_STATUS _notifyHandshake(

146
src/group_manager_api.cc

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

115
src/adapter_group.h → src/group_manager_api.h

@ -1,16 +1,16 @@
// This file is under GNU General Public License 3.0
// see LICENSE.txt
#ifndef LIBPEPADAPTER_GROUP_HH
#define LIBPEPADAPTER_GROUP_HH
#ifndef LIBPEPADAPTER_GROUP_MANAGER_API_H
#define LIBPEPADAPTER_GROUP_MANAGER_API_H
#include <pEp/message_api.h>
#include <pEp/group.h>
#ifdef __cplusplus
extern "C" {
#endif
DYNAMIC_API PEP_STATUS adapter_group_init();
/*************************************************************************************************
* Group management functions
@ -37,9 +37,6 @@ extern "C" {
* a user_id and address, and there must be a default key for the manager
* present in the database
* @param[in,out] member_ident_list list of group member identities
* @param[in,out] group Optional reference for pointer to group object
* representing the created group.
* (When input is NULL, no object is created)
*
* @retval PEP_STATUS_OK on success
* error on failure
@ -58,37 +55,8 @@ DYNAMIC_API PEP_STATUS adapter_group_create(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *manager,
identity_list *memberlist,
pEp_group **group
);
identity_list *memberlist);
/**
* <!-- adapter_group_join() -->
*
* @brief Join a group for which we have received an invitation, marking
* our own membership in the database for the group and sending the manager
* a confirmation of the acceptance of the invitation
*
* @param[in] session associated session object
* @param[in] group_identity the pEp_identity object representing the group. Must contain at least
* a user_id and address
* @param[in] as_member the pEp_identity object representing the own identity we want to use to
* join the group. This must match the identity which was invited to the group.
* Must contain a user_id and address.
*
* @retval PEP_STATUS_OK on success
* error on failure
*
* @ownership FIXME
*
*
*/
DYNAMIC_API PEP_STATUS adapter_group_join(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *as_member
);
/**
* <!-- adapter_group_dissolve() -->
@ -111,11 +79,8 @@ DYNAMIC_API PEP_STATUS adapter_group_join(
* @warning For recipients to accept the dissolution, the sender/manager key used must be a key that they
* have a trust entry for.
*/
DYNAMIC_API PEP_STATUS adapter_group_dissolve(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *manager
);
DYNAMIC_API PEP_STATUS
adapter_group_dissolve(PEP_SESSION session, pEp_identity *group_identity, pEp_identity *manager);
/**
* <!-- adapter_group_invite_member() -->
@ -140,10 +105,9 @@ DYNAMIC_API PEP_STATUS adapter_group_dissolve(
*
*/
DYNAMIC_API PEP_STATUS adapter_group_invite_member(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member
);
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member);
/**
* <!-- adapter_group_remove_member() -->
@ -165,46 +129,46 @@ DYNAMIC_API PEP_STATUS adapter_group_invite_member(
* @todo Revamp implementation and execute key reset
*
*/
PEP_STATUS adapter_group_remove_member(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member
);
DYNAMIC_API PEP_STATUS adapter_group_remove_member(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *group_member);
/**
* <!-- adapter_group_rating() -->
* <!-- group_join() -->
*
* @brief Get the rating for this group - if the caller is the manager, this will return the aggregate rating
* of group members. For members, this will return the rating of the group_identity
* @brief Join a group for which we have received an invitation, marking
* our own membership in the database for the group and sending the manager
* a confirmation of the acceptance of the invitation
*
* @param[in] session associated session object
* @param[in] group_identity the pEp_identity object representing the group. Must contain at least
* a user_id and address
* @param[in] manager the pEp_identity object representing the member to remove. Must contain
* a user_id and address
* @param[out] rating the group rating
* @param[in] as_member the pEp_identity object representing the own identity we want to use to
* join the group. This must match the identity which was invited to the group.
* Must contain a user_id and address.
*
* @retval PEP_STATUS_OK on success
* error on failure
*
* @ownership FIXME
*
*
*/
DYNAMIC_API PEP_STATUS adapter_group_rating(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *manager,
PEP_rating *rating
DYNAMIC_API PEP_STATUS adapter_group_join(
PEP_SESSION session,
pEp_identity *group_identity,
pEp_identity *as_member
);
/*************************************************************************************************
* Group query functions
*************************************************************************************************/
/**
* <!-- group_query_groups() -->
* <!-- adapter_group_query_groups() -->
*
* @brief queries the list manager which groups currently exist.
*
@ -219,13 +183,10 @@ DYNAMIC_API PEP_STATUS adapter_group_rating(
*
*/
//DYNAMIC_API PEP_STATUS group_query_groups(
// PEP_SESSION session,
// identity_list **groups
//);
DYNAMIC_API PEP_STATUS adapter_group_query_groups(PEP_SESSION session, identity_list **groups);
/**
* <!-- group_query_manager() -->
* <!-- adapter_group_query_manager() -->
*
* @brief queries the list manager for the group manager of a given group.
*
@ -240,14 +201,10 @@ DYNAMIC_API PEP_STATUS adapter_group_rating(
*
*/
//DYNAMIC_API PEP_STATUS group_query_manager(
// PEP_SESSION session,
// const pEp_identity *const group,
// pEp_identity **manager
//);
DYNAMIC_API PEP_STATUS adapter_group_query_manager(PEP_SESSION session, const pEp_identity *const group, pEp_identity **manager);
/**
* <!-- group_query_members() -->
* <!-- adapter_group_query_members() -->
*
* @brief queries the list manager for all members of a given group.
*
@ -262,15 +219,11 @@ DYNAMIC_API PEP_STATUS adapter_group_rating(
*
*/
//DYNAMIC_API PEP_STATUS group_query_members(
// PEP_SESSION session,
// const pEp_identity *const group,
// identity_list **members
//);
DYNAMIC_API PEP_STATUS adapter_group_query_members(PEP_SESSION session, const pEp_identity *const group, identity_list **members);
#ifdef __cplusplus
};
#endif
#endif //LIBPEPADAPTER_GROUP_HH
#endif //LIBPEPADAPTER_GROUP_MANAGER_API_H

383
src/grp_driver_dummy.cc

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

69
src/grp_driver_dummy.hh

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

63
src/grp_driver_engine.cc

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

52
src/grp_driver_engine.hh

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

267
src/grp_driver_replicator.cc

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

83
src/grp_driver_replicator.hh

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

66
src/grp_manager_interface.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

12264
src/internal/sqlite3.h

File diff suppressed because it is too large

367
src/listmanager_dummy.cc

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

63
src/listmanager_dummy.hh

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

129
src/pEpLog.cc

@ -6,21 +6,25 @@
#include <sstream>
#include <mutex>
#include <atomic>
#include <cmath>
#include "std_utils.hh"
#ifdef ANDROID
#include <android/log.h>
#endif
using namespace std;
namespace pEp {
namespace Adapter {
namespace pEpLog {
int line_width = 120;
std::mutex mtx;
// NON CLASS
mutex mtx;
atomic_bool is_enabled{ false };
std::atomic_bool is_enabled{ false };
void set_enabled(bool enabled)
void set_enabled(const bool& enabled)
{
is_enabled.store(enabled);
}
@ -30,18 +34,125 @@ namespace pEp {
return is_enabled.load();
}
void log(std::string msg)
// Common "print" function implementing the actual "backends"
void _log(const string& msg, Utils::Color col = Utils::Color::WHITE)
{
if (is_enabled.load()) {
std::lock_guard<std::mutex> l(mtx);
lock_guard<mutex> l(mtx);
#ifdef ANDROID
__android_log_print(ANDROID_LOG_DEBUG, "pEpDebugLog", "%s", msg.c_str());
__android_log_print(ANDROID_LOG_DEBUG, "pEpDebugLog", "%s", msg.c_str());
#else
std::cout << msg << std::endl; //std::endl also flushes
cerr << Utils::to_termcol(col) << msg << Utils::to_termcol(Utils::Color::RESET)
<< endl; //endl also flushes, but cerr is unbuffered anyways
#endif
}
void log(const string& msg, Utils::Color col)
{
_log(msg, col);
}
void logH1(const string& msg, Utils::Color col)
{
log(decorate_three_lines(msg, '='), col);
}
void logH2(const string& msg, Utils::Color col)
{
log( "\n" +decorate_centered(msg, '='), col);
}
void logH3(const string& msg, Utils::Color col)
{
log( decorate_centered(msg, '-'), col);
}
string decorate_three_lines(const string& msg, char decoration)
{
stringstream tmp;
tmp << std::string(line_width, decoration) << endl
<< msg << endl
<< std::string(line_width, decoration);
return tmp.str();
}
string decorate_centered(const string& msg, char decoration)
{
stringstream tmp;
int max_len = line_width - 10;
// truncate msg
string msg_truncated = msg;
if (msg.length() >= max_len) {
msg_truncated = msg.substr(0, max_len);
msg_truncated += "...";
}
// define decolen
int decolen = static_cast<int>(
floor((double(line_width - msg_truncated.length()))) / 2.0);
tmp << std::string(decolen, decoration) << ' ' << msg_truncated << ' '
<< std::string(decolen, decoration);
return tmp.str();
}
} // namespace pEpLog
} // namespace Adapter
} // namespace pEp
namespace pEp {
namespace Adapter {
namespace pEpLog {
// Class pEpLogger
int pEpLogger::auto_instance_nr = 0;
pEpLogger::pEpLogger(const string& classname, const bool& enabled) :
classname(classname), is_enabled(enabled)
{
auto_instance_nr++;
this->set_instancename(to_string(auto_instance_nr));
}
void pEpLogger::log(const string& msg, Utils::Color col) const
{
std::stringstream msg_;
msg_ << "[" << getpid() << " " << std::this_thread::get_id() << "]";
msg_ << " - ";
msg_ << this->get_classname() << "[" << this->get_instancename() << "]";
msg_ << " - " << msg;
this->logRaw(msg_.str(), col);
}
void pEpLogger::logRaw(const string& msg, Utils::Color col) const
{
if (this->is_enabled) {
_log(msg, col);
}
}
void pEpLogger::set_enabled(const bool& enabled)
{
this->is_enabled = enabled;
}
bool pEpLogger::get_enabled() const
{
return this->is_enabled;
}
string pEpLogger::get_classname() const
{
return this->classname;
}
void pEpLogger::set_instancename(const string& name)
{
this->instancename = name;
}
string pEpLogger::get_instancename() const
{
return this->instancename;
}
} // namespace pEpLog
} // namespace Adapter
} // namespace pEp

211
src/pEpLog.hh

@ -4,29 +4,42 @@
#ifndef LIBPEPADAPTER_PEPLOG_HH
#define LIBPEPADAPTER_PEPLOG_HH
// getpid
// Linux - unistd.h
// macOS - unistd.h
// Android - unistd.h
// Win - process.h
#ifdef WIN32
#include <process.h>
#else
#include <unistd.h>
#endif
#include <sstream>
#include <thread>
#include "std_utils.hh"
// pEpLog
// ======
// a "to be kept ultra small and simple" logging unit.
// featuring:
// * pEpLog macro that will be eliminated in release-builds (-DNDEBUG=1)
// * thread safe (no interleave when logging from diff threads)
// * Logging macros that completely eliminate any logging calls in release-builds (NDEBUG)
// * thread safe (no interleave when logging from diff threads) TODO: pEpLogger: REALLY?
// * OS dependent backend switches:
// * android: __android_log_print
// * all other OS: cout
// * runtime enabled/disabled switch (global)
// * all other OS: cerr
// * Logging without any class/object (pEpLog / pEpLogRaw macros)
// * runtime switchable (on/off) only on a global level
// * Class backed Logging macros (pEpLogClass / pEpLogClassRaw)
// * * runtime switchable (on/off) on a class and object level
//
// You might want more and more features, but the feature-policy is very restrictive, and there is a
// primary design goal to keep it simple, maintainable and portable.
// There are already too many features and you might want even more and more.
// But the feature-policy of this logging unit is very restrictive, and there is a
// primary design goal to keep it very simple, maintainable and portable.
//
// How to use:
// include <pEpLog.hh>
// use the macro pEpLog(msg) to do logging
// use NDEBUG=1 to turn logging on/off at compile-time
// use set_enabled(bool) to turn logging on/off at runtime
// use set_enabled_<backend>(bool) to turn logging on/off per backend
// pEpLog - logformat "thread - __FILE__::__FUNTION__ - <message>"
// To be used in a non-class/object context
#ifdef NDEBUG
#define pEpLog(msg) \
do { \
@ -34,23 +47,183 @@
#else
#define pEpLog(msg) \
do { \
std::stringstream msg_ss; \
msg_ss << std::this_thread::get_id() << " - " << __FILE__ << "::" << __FUNCTION__ \
<< " - " << msg; \
pEp::Adapter::pEpLog::log(msg_ss.str()); \
if (pEp::Adapter::pEpLog::get_enabled()) { \
std::stringstream msg_; \
msg_ << "[" << getpid() << " " << std::this_thread::get_id() << "]"; \
msg_ << " - " << __FILE__ << "::" << __FUNCTION__; \
msg_ << " - " << msg; \
pEp::Adapter::pEpLog::log(msg_.str()); \
} \
} while (0)
#endif // NDEBUG
// pEpLogH1 - logformat "Thread - __FILE__::__FUNTION__ - <=============== message ==============>"
#ifdef NDEBUG
#define pEpLogH1(msg) \
do { \
} while (0)
#else
#define pEpLogH1(msg) \
do { \
if (pEp::Adapter::pEpLog::get_enabled()) { \
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 // NDEBUG
// pEpLogH1 - logformat "Thread - __FILE__::__FUNTION__ - <--------------- message -------------->"
#ifdef NDEBUG
#define pEpLogH2(msg) \
do { \
} while (0)
#else
#define pEpLogH2(msg) \
do { \
if (pEp::Adapter::pEpLog::get_enabled()) { \
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 // NDEBUG
// RAW == without prefix of thread, file, function
// pEpLogRaw - logformat "<message>"
#ifdef NDEBUG
#define pEpLogRaw(msg) \
do { \
} while (0)
#else
#define pEpLogRaw(msg) \
do { \
if (pEp::Adapter::pEpLog::get_enabled()) { \
pEp::Adapter::pEpLog::log(msg_.str()); \
} \
} while (0)
#endif // NDEBUG
// pEpLogRawH1 - logformat "<--------------- message -------------->"
#ifdef NDEBUG
#define pEpLogRawH1(msg) \
do { \
} while (0)
#else
#define pEpLogRawH1(msg) \
do { \
if (pEp::Adapter::pEpLog::get_enabled()) { \
pEp::Adapter::pEpLog::logH1(msg_.str()); \
} \
} while (0)
#endif // NDEBUG
// pEpLogRawH2 - logformat <=============== message ==============>"
#ifdef NDEBUG
#define pEpLogRawH2(msg) \
do { \
} while (0)
#else
#define pEpLogRawH2(msg) \
do { \
if (pEp::Adapter::pEpLog::get_enabled()) { \
pEp::Adapter::pEpLog::logH2(msg_.str()); \
} \
} while (0)
#endif // NDEBUG
namespace pEp {
namespace Adapter {
namespace pEpLog {
// Logging functions to control pEpLog() macro
void set_enabled(const bool& is_enabled);
bool get_enabled();
void log(const std::string& msg, Utils::Color col = Utils::Color::WHITE);
void logH1(const std::string& msg, Utils::Color col = Utils::Color::WHITE);
void logH2(const std::string& msg, Utils::Color col = Utils::Color::WHITE);
void logH3(const std::string& msg, Utils::Color col = Utils::Color::WHITE);
std::string decorate_three_lines(const std::string& msg, char decoration = '-');
std::string decorate_centered(const std::string& msg, char decoration = '-');
} // namespace pEpLog
} // namespace Adapter
} // namespace pEp
void log(std::string msg);
// --------------------------------------------------------------------------------------------------
void set_enabled(bool is_enabled);
bool get_enabled();
// pEpLogClass is to be used in a class
// pEpLogger can only print the "thread - file::class::function - <message>" format using this macro
// WARNING: Some magic is needed
// Usage:
// create your logger obj in your class as a public member (usually)
// Adapter::pEpLog::pEpLogger logger{"<CLASSNAME>", enabled: true|false};
// then, create an alias for your logger called "m4gic_logger_n4me" as a private member
// Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger;
// Thats all.
// Now in your implementation, to log a message you just write:
// pEpLogClass("my great logging message");
#ifdef NDEBUG
#define pEpLogClass(msg) \
do { \
} while (0)
#else
#define pEpLogClass(msg) \
do { \
std::stringstream msg_; \
msg_ << "[" << getpid() << " " << std::this_thread::get_id() << "]"; \
msg_ << " - " << this->m4gic_logger_n4me.get_classname(); \
msg_ << "[" << this->m4gic_logger_n4me.get_instancename() << "]"; \
msg_ << "::" << __FUNCTION__; \
msg_ << " - " << (msg); \
this->m4gic_logger_n4me.logRaw(msg_.str()); \
} while (0)
#endif // NDEBUG
// pEpLogClassRaw is the same as pEpLogClass, but does not print anything except the supplied msg
// This can also be achieved without this macro, just use the log method of pEpLogger
// You also need to set up the logger in your class as for pEpLogClass
// The only advantage of this macro is that is compiled away to nothing with NDEBUG
#ifdef NDEBUG
#define pEpLogClassRaw(msg) \
do { \
} while (0)
#else
#define pEpLogClassRaw(msg) \
do { \
this->m4gic_logger_n4me.logRaw(msg); \
} while (0)
#endif // NDEBUG
namespace pEp {
namespace Adapter {
namespace pEpLog {
class pEpLogger {
public:
pEpLogger() = delete;
pEpLogger(const std::string& classname, const bool& enabled);
// Print a logging message in the format "thread - classname[instancename] - <msg>"
void log(const std::string& msg, Utils::Color col = Utils::Color::WHITE) const;
// Prints just "<msg>"
void logRaw(const std::string& msg, Utils::Color col = Utils::Color::WHITE) const;
void set_enabled(const bool& enabled);
bool get_enabled() const;
std::string get_classname() const;
// If never set, the default instancename is a unique number
void set_instancename(const std::string& name);
std::string get_instancename() const;
private:
static int auto_instance_nr;
bool is_enabled;
std::string classname;
std::string instancename;
};
} // namespace pEpLog
} // namespace Adapter
} // namespace pEp

167
src/pEpSQLite.cc

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

64
src/pEpSQLite.hh

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

234229
src/sqlite3.c

File diff suppressed because it is too large

297
src/std_utils.cc

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

94
src/std_utils.hh

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

22
src/std_utils.hxx

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

259
src/utils.cc

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

35
src/utils.hh

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

16
test/Makefile

@ -1,9 +1,7 @@
include ../Makefile.conf
LDFLAGS:=-L../src $(LDFLAGS)
LDLIBS=-lstdc++ -lpEpEngine -lpEpAdapter
LDFLAGS=-L../src $(ENGINE_LIB)
LDLIBS=-lstdc++ -lpEpEngine -lpEpAdapter -lpthread
LDFLAGS:=-L../src $(LDFLAGS) -L./pitytest11/src
LDLIBS=-lstdc++ -lpEpEngine -lpEpAdapter -lPityTest -lpthread -ldl
CXXFLAGS:=-I../src -DENGINE_TEST=$(ENGINE_TEST) $(CXXFLAGS)
# Test
@ -24,13 +22,16 @@ OBJ_FRAMEWORK=$(subst .cc,.o,$(SRC_FRAMEWORK))
# -include $(DEPENDS)
#endif
.PHONY: all clean rmtestdata
.PHONY: all clean rmtestdata pitytest pitytest-clean
.DEFAULT_GOAL := all
all: $(BIN_TEST)
all: pitytest $(BIN_TEST)
$(BIN_TEST): $(OBJ_FRAMEWORK)
pitytest:
$(MAKE) -C pitytest11
clean:
rm -f $(BIN_TEST)
rm -f $(OBJ_FRAMEWORK)
@ -39,5 +40,8 @@ clean:
rm -f *.o
rm -Rf /tmp/test_pEp.*
pitytest-clean:
$(MAKE) -C pitytest11 clean
rmtestdata:
rm -Rf /tmp/test_pEp.*

17
test/framework/framework.cc

@ -8,7 +8,6 @@
#include <utility>
#include <exception>
#include <thread>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
@ -24,14 +23,14 @@
#include "../../src/Adapter.hh"
pEp::Test::Transport pEp::Test::transport;
std::string pEp::Test::path;
extern std::thread pEp::Adapter::_sync_thread;
using namespace pEp;
using namespace std;
namespace pEp {
namespace Test {
string per_user_dir;
void setup(vector<string> &args)
{
#ifdef WIN32
@ -67,8 +66,8 @@ namespace pEp {
#else
setenv("HOME", _path, 1);
#endif
path = _path;
cerr << "test directory: " << path << endl;
per_user_dir = _path;
cerr << "test directory: " << per_user_dir << endl;
}
void setup(int argc, char **argv)
@ -135,7 +134,13 @@ namespace pEp {
stringlist_t *keylist;
::PEP_rating rating;
::PEP_decrypt_flags_t flags = 0;
::PEP_STATUS status = ::decrypt_message(Adapter::session(), msg.get(), &_dst, &keylist, &rating, &flags);
::PEP_STATUS status = ::decrypt_message(
Adapter::session(),
msg.get(),
&_dst,
&keylist,
&rating,
&flags);
throw_status(status);
Message dst;

2
test/framework/framework.hh

@ -55,7 +55,7 @@ namespace pEp {
};
extern Transport transport;
extern std::string path;
extern std::string per_user_dir;
}; // namespace Test
}; // namespace pEp

367
test/framework/utils.cc

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

137
test/framework/utils.hh

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

39
test/pitytest11/Makefile

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

456
test/pitytest11/src/AbstractPityUnit.cc

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

135
test/pitytest11/src/AbstractPityUnit.hh

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

32
test/pitytest11/src/PityModel.cc

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

53
test/pitytest11/src/PityModel.hh

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

63
test/pitytest11/src/PityNode.cc

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

55
test/pitytest11/src/PityNode.hh

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

48
test/pitytest11/src/PityPerspective.cc

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

59
test/pitytest11/src/PityPerspective.hh

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

146
test/pitytest11/src/PitySwarm.cc

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

59
test/pitytest11/src/PitySwarm.hh

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

32
test/pitytest11/src/PityTest.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

97
test/pitytest11/src/PityTransport.cc

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

45
test/pitytest11/src/PityTransport.hh

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

88
test/pitytest11/src/PityTree.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

220
test/pitytest11/src/PityTree.hxx

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

75
test/pitytest11/src/PityUnit.hh

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

136
test/pitytest11/src/PityUnit.hxx

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

41
test/pitytest11/src/fs_mutex.cc

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

25
test/pitytest11/src/fs_mutex.hh

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

32
test/pitytest11/test/test_assert.cc

@ -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) {
}
}

94
test/pitytest11/test/test_execmodes.cc

@ -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();
}
}

68
test/pitytest11/test/test_linear.cc

@ -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();
}

161
test/pitytest11/test/test_pitytree.cc

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

98
test/pitytest11/test/test_processdirs.cc

@ -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();
}

88
test/pitytest11/test/test_swarm.cc

@ -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();
}

29
test/test_adapter.cc

@ -2,39 +2,38 @@
// see LICENSE.txt
#include "framework/framework.hh"
#include "framework/utils.hh"
#include <iostream>
#include <assert.h>
#include <unistd.h>
#include <sys/param.h>
#include <pEp/sync_api.h>
#include <pEp/keymanagement.h>
#include <pEp/message_api.h>
#include "../src/pEpLog.hh"
#include "../src/Adapter.hh"
#include "../src/utils.hh"
using namespace std;
using namespace pEp;
PEP_STATUS messageToSend(struct _message *msg)
{
pEpLog("called()");
TESTLOG("called()");
return PEP_STATUS_OK;
}
PEP_STATUS notifyHandshake(::pEp_identity *me, ::pEp_identity *partner, ::sync_handshake_signal signal)
{
pEpLog("called()");
TESTLOG("called()");
return PEP_STATUS_OK;
}
int main(int argc, char **argv)
{
Test::setup(argc, argv);
Adapter::pEpLog::set_enabled(true);
Adapter::session.initialize(Adapter::SyncModes::Async, false, messageToSend, notifyHandshake);
// Create new identity
pEpLog("updating or creating identity for me");
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);
@ -45,14 +44,14 @@ int main(int argc, char **argv)
useconds_t sleepuSec = 1000 * 100;
unsigned long long int nrIters = 1000 * 1000 * 1000;
for (int i = 0; i < nrIters; i++) {
pEpLog("RUN NR: ");
pEpLog(i);
pEpLog("SYNC START");
pEpLog("starting the adapter including sync");
Adapter::startup(messageToSend, notifyHandshake);
pEpLog("SYNC STOP");
TESTLOG("RUN NR: ");
TESTLOG(i);
TESTLOG("SYNC START");
TESTLOG("starting the adapter including sync");
Adapter::startup();
TESTLOG("SYNC STOP");
usleep(sleepuSec);
Adapter::shutdown();
Adapter::stop_sync();
}
return 0;
}

47
test/test_adapter_cxx.cc

@ -2,27 +2,27 @@
// see LICENSE.txt
#include "framework/framework.hh"
#include "framework/utils.hh"
#include <iostream>
#include <assert.h>
#include <unistd.h>
#include <pEp/keymanagement.h>
#include <pEp/sync_api.h>
#include "../src/Adapter.hh"
#include "../src/pEpLog.hh"
namespace Utils = pEp::Utils;
using namespace pEp;
PEP_STATUS messageToSend(struct _message *msg)
{
pEpLog("called");
TESTLOG("called");
return PEP_STATUS_OK;
}
PEP_STATUS notifyHandshake(pEp_identity *me, pEp_identity *partner, ::sync_handshake_signal signal)
{
pEpLog("called");
TESTLOG("called");
return PEP_STATUS_OK;
}
@ -30,21 +30,22 @@ class JNISync {
public:
void onSyncStartup()
{
pEpLog("called");
TESTLOG("called");
}
void onSyncShutdown()
{
pEpLog("called");
TESTLOG("called");
}
} o;
int main(int argc, char **argv)
int main(int argc, char* argv[])
{
pEp::Test::setup(argc, argv);
Test::setup(argc,argv);
pEp::Adapter::session
.initialize(pEp::Adapter::SyncModes::Async, false, messageToSend, notifyHandshake);
// Create new identity
pEpLog("updating or creating identity for me");
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);
@ -52,22 +53,16 @@ int main(int argc, char **argv)
throw_status(status);
// start and stop sync repeatedly
useconds_t sleepuSec = 1000 * 100;
unsigned long long int nrIters = 1000 * 1000 * 1000;
unsigned long long int nrIters = 3;
for (int i = 0; i < nrIters; i++) {
pEpLog("RUN NR: ");
pEpLog(i);
pEpLog("SYNC START");
pEpLog("starting the adapter including sync");
Adapter::startup<JNISync>(
messageToSend,
notifyHandshake,
&o,
&JNISync::onSyncStartup,
&JNISync::onSyncShutdown);
pEpLog("SYNC STOP");
usleep(sleepuSec);
Adapter::shutdown();
TESTLOG("RUN NR: ");
TESTLOG(i);
TESTLOG("SYNC START");
TESTLOG("starting the adapter including sync");
Adapter::startup<JNISync>(&o, &JNISync::onSyncStartup, &JNISync::onSyncShutdown);
TESTLOG("SYNC STOP");
Utils::sleep_millis(500);
Adapter::stop_sync();
}
return 0;
}
}

14
test/test_ensure_passphrase.cc

@ -3,21 +3,11 @@
#include "framework/framework.hh"
#include <iostream>
#include <fstream>
#include <sstream>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <pEp/message_api.h>
#include <cassert>
#include <pEp/keymanagement.h>
#include <pEp/key_reset.h>
#include "../src/passphrase_cache.hh"
#include "../src/status_to_string.hh"
#include "../src/Adapter.hh"
using namespace pEp;
@ -26,7 +16,7 @@ using namespace std;
int main(int argc, char** argv)
{
Test::setup(argc, argv);
Adapter::session.initialize();
passphrase_cache.add("erwin");
passphrase_cache.add("cathy");
passphrase_cache.add("bob");

185
test/test_group.cc

@ -4,40 +4,64 @@
#include "framework/framework.hh"
#include "framework/utils.hh"
#include <iostream>
//#include <iostream>
#include "../src/Adapter.hh"
#include "../src/adapter_group.h"
#include "../src/utils.hh"
#include "../src/std_utils.hh"
#include "../src/grp_manager_interface.hh"
#include "../src/grp_driver_engine.hh"
#include "../src/grp_driver_dummy.hh"
#include "../src/grp_driver_replicator.hh"
//#include "../src/adapter_group.h"
#include "../src/status_to_string.hh"
#include <pEp/pEpEngine.h>
#include <pEp/message_api.h>
using namespace std;
using namespace pEp;
using namespace pEp::Test::Log;
using namespace pEp::Adapter;
using namespace pEp::Adapter::pEpLog;
bool debug_info_full = true;
// Model
const string lmd_path = "test.db";
::pEp_identity* alice = nullptr;
::pEp_identity* bob = nullptr;
::pEp_identity* carol = nullptr;
::pEp_identity* grp_ident = nullptr;
::PEP_STATUS status;
string dummy_in;
GroupUpdateInterface* gu = nullptr;
GroupQueryInterface* gq = nullptr;
/*
* Callbacks
*/
::PEP_STATUS test_messageToSend(::message* _msg)
{
cout << "called" << endl;
cout << Test::make_pEp_msg(Test::make_message(_msg));
log("called");
log(Test::make_pEp_msg(Test::make_message(_msg)));
return PEP_STATUS_OK;
}
::PEP_STATUS test_notifyHandshake(::pEp_identity* _me, ::pEp_identity* _partner, sync_handshake_signal signal)
{
cout << "called" << endl;
cout << "me: " << Test::Utils::identity_to_string(_me, false) << endl;
cout << "partner: " << Test::Utils::identity_to_string(_partner, false) << endl;
cout << "Signal: " << signal << endl;
log("called");
log("me: " + pEp::Utils::to_string(_me, false));
log("partner: " + pEp::Utils::to_string(_partner, false));
log("Signal: " + to_string(signal));
// log("Signal: " + string{ ::sync_handshake_signal_to_string(signal) });
return PEP_STATUS_OK;
}
@ -54,9 +78,9 @@ void test_create_alice_me()
alice->lang[0] = 'e';
alice->lang[1] = 'n';
status = ::myself(Adapter::session(), alice);
cout << "STATUS: " << status_to_string(status) << endl;
log("STATUS: " + status_to_string(status));
assert(!status);
cout << "Alice:" << Test::Utils::identity_to_string(alice, debug_info_full) << endl;
log("Alice:" + pEp::Utils::to_string(alice, debug_info_full));
}
void test_create_bob_partner()
@ -64,12 +88,13 @@ void test_create_bob_partner()
logH2("test_create_bob_partner");
bob = ::new_identity("bob@peptest.ch", NULL, PEP_OWN_USERID, "Bob");
assert(bob);
bob->lang[0] = 'c';
bob->lang[1] = 'r';
// bob->lang[0] = 'c';
// bob->lang[1] = 'r';
// status = ::myself(Adapter::session(), bob);
status = ::update_identity(Adapter::session(), bob);
cout << "STATUS: " << status_to_string(status) << endl;
log("STATUS: " + status_to_string(status));
assert(!status);
cout << "Bob:" << Test::Utils::identity_to_string(bob, debug_info_full) << endl;
log("Bob:" + pEp::Utils::to_string(bob, debug_info_full));
}
void test_create_carol_partner()
@ -80,80 +105,63 @@ void test_create_carol_partner()
carol->lang[0] = 'f';
carol->lang[1] = 'n';
status = ::update_identity(Adapter::session(), carol);
cout << "STATUS: " << status_to_string(status) << endl;
// status = ::myself(Adapter::session(), carol);
log("STATUS: " + status_to_string(status));
assert(!status);
cout << "Carol:" << Test::Utils::identity_to_string(carol, debug_info_full) << endl;
log("Carol:" + pEp::Utils::to_string(carol, debug_info_full));
}
void test_setup_and_start_sync()
{
logH2("test_setup_and_start_sync");
Adapter::sync_initialize(Adapter::SyncModes::Async, &test_messageToSend, &test_notifyHandshake, false);
Adapter::start_sync();
}
void test_group_create(::identity_list* idl)
void test_group_create()
{
logH2("test_group_create");
cout << "IDL: " << Test::Utils::identitylist_to_string(idl, debug_info_full) << endl;
::identity_list* initial_memberlist = nullptr;
initial_memberlist = new_identity_list(bob);
::identity_list_add(initial_memberlist, carol);
cout << "create group identity" << endl;
log("create group identity");
grp_ident = ::new_identity("group1@peptest.ch", NULL, "432", "group1");
assert(grp_ident);
status = ::myself(Adapter::session(), grp_ident);
cout << "STATUS: " << status_to_string(status) << endl;
assert(!status);
cout << "grp_ident:" << Test::Utils::identity_to_string(grp_ident, debug_info_full) << endl;
log("grp_ident:" + pEp::Utils::to_string(grp_ident, debug_info_full));
cout << "adapter_group_create()" << endl;
::pEp_group* pep_grp1 = nullptr;
status = ::adapter_group_create(Adapter::session(), grp_ident, alice, idl, &pep_grp1);
cout << "STATUS: " << status_to_string(status) << endl;
assert(!status);
assert(pep_grp1);
cout << "GRP: " << Test::Utils::group_to_string(pep_grp1, debug_info_full) << endl;
}
// PEP_STATUS stat = ::myself(Adapter::session(), grp_ident);
// log("STATUS: " + status_to_string(status));
// assert(stat == PEP_STATUS_OK);
// log("grp_ident:" + pEp::Utils::to_string(grp_ident, debug_info_full));
void test_group_invite_member(::pEp_identity* ident)
{
logH2("test_group_invite_member");
status = ::adapter_group_invite_member(Adapter::session(), grp_ident, ident);
cout << "STATUS: " << status_to_string(status) << endl;
log("adapter_group_create()");
status = gu->adapter_group_create(Adapter::session(), grp_ident, alice, nullptr);
log("STATUS: " + status_to_string(status));
assert(!status);
}
void test_group_join(::pEp_identity* ident)
void test_group_invite_member(::pEp_identity& ident)
{
logH2("test_group_join");
status = ::adapter_group_join(Adapter::session(), grp_ident, ident);
cout << "STATUS: " << status_to_string(status) << endl;
logH2("test_group_invite_member");
assert(grp_ident);
status = gu->adapter_group_invite_member(Adapter::session(), grp_ident, &ident);
log("STATUS: " + status_to_string(status));
assert(!status);
}
void test_group_remove_member(::pEp_identity* ident)
void test_group_remove_member(::pEp_identity& ident)
{
logH2("test_group_remove_member");
status = ::adapter_group_remove_member(Adapter::session(), grp_ident, ident);
cout << "STATUS: " << status_to_string(status) << endl;
status = gu->adapter_group_remove_member(Adapter::session(), grp_ident, &ident);
log("STATUS: " + status_to_string(status));
assert(!status);
}
void test_group_rating()
{
logH2("test_group_rating");
// Rating
::PEP_rating* rating = nullptr;
cout << "adapter_group_rating()" << endl;
status = ::group_rating(Adapter::session(), grp_ident, alice, rating);
cout << "STATUS: " << status_to_string(status) << endl;
assert(!status);
cout << "Rating: " << rating << endl;
}
void test_group_dissolve()
{
logH2("test_group_dissolve");
status = ::adapter_group_dissolve(Adapter::session(), grp_ident, alice);
cout << "STATUS: " << status_to_string(status) << endl;
status = gu->adapter_group_dissolve(Adapter::session(), grp_ident, alice);
log("STATUS: " + status_to_string(status));
assert(!status);
}
@ -165,14 +173,14 @@ void test_group_dissolve()
* - group_create(Alice)
* 2. Add Bob
* - group_invite_member(Bob)
* - group_join(Bob)
// * - group_join(Bob)
* 3. Add Carol
* - group_invite_member(Carol)
* - group_join(Carol)
// * - group_join(Carol)
* 4. Remove Carol
* - group_remove_member(Carol)
* 5. Rating
* - group_rating() (Just test once, to see it is generally working)
// * 5. Rating
// * - group_rating() (Just test once, to see it is generally working)
* 6. Dissolve
* - group_dissolve()
*
@ -191,31 +199,56 @@ int main(int argc, char** argv)
Adapter::pEpLog::set_enabled(false);
debug_info_full = true;
// pEpSQLite::log_enabled = true;
// ListManagerDummy::log_enabled = true;
GroupDriverDummy::log_enabled = true;
GroupDriverEngine::log_enabled = true;
GroupDriverReplicator::log_enabled = true;
GroupDriverDummy gdd{ lmd_path };
GroupDriverEngine gde{};
GroupDriverReplicator gdr{};
gu = &gde;
gq = nullptr;
// gu = &gdd;
// gq = &gdd;
// gu = &gdr;
// gq = &gdr;
Adapter::session.initialize();
// Setup Test Context
test_create_alice_me();
test_create_bob_partner();
test_create_carol_partner();
test_setup_and_start_sync();
test_setup_and_start_sync();
logH1("1. Create group");
::identity_list* initial_memberlist = nullptr;
initial_memberlist = new_identity_list(bob);
::identity_list_add(initial_memberlist, carol);
test_group_create(initial_memberlist);
test_group_create();
logH1("2. Add Bob");
// test_group_invite_member(bob); // Fails
// test_group_join(bob); // Fails
test_group_invite_member(*bob); // Fails
logH1("3. Add Carol");
// test_group_invite_member(carol);
// test_group_join(carol);
test_group_invite_member(*carol);
logH1("4. Remove Carol");
// test_group_remove_member(carol);
logH1("5. Rating");
// test_group_rating(); // Failing
test_group_remove_member(*carol);
if (gq != nullptr) {
::pEp_identity* grp_ident = ::new_identity("group1@peptest.ch", NULL, "432", "group1");
::pEp_identity* manager = nullptr;
PEP_STATUS stat = gq->group_query_manager(Adapter::session(), grp_ident, &manager);
log(status_to_string(stat));
log(::Utils::to_string(manager));
}
logH1("6. Dissolve");
test_group_dissolve();
Adapter::shutdown();
Adapter::stop_sync();
return 0;
}

12
test/test_leave_device_group.cc

@ -5,11 +5,11 @@
#include <iostream>
#include <vector>
#include <unistd.h>
#include <pEp/sync_api.h>
#include "../src/callback_dispatcher.hh"
#include "../src/Adapter.hh"
#include "../src/passphrase_cache.hh"
using namespace std;
@ -55,7 +55,7 @@ vector<::sync_handshake_signal> expected_notification = { SYNC_NOTIFY_IN_GROUP,
int main(int argc, char** argv)
{
Test::setup(argc, argv);
Adapter::session();
Adapter::session.initialize(pEp::Adapter::SyncModes::Async, false);
// set up two own identites for sync
@ -96,7 +96,7 @@ int main(int argc, char** argv)
// register at callback_dispatcher and start sync
callback_dispatcher.add(test_messageToSend, test_notifyHandshake);
CallbackDispatcher::start_sync();
Adapter::start_sync();
// leave device group
@ -110,14 +110,14 @@ int main(int argc, char** argv)
// switch off and on again
CallbackDispatcher::start_sync();
Adapter::start_sync();
sleep(2);
assert(Adapter::is_sync_running());
CallbackDispatcher::stop_sync();
Adapter::stop_sync();
Test::join_sync_thread();
assert(!Adapter::is_sync_running());
Adapter::session(Adapter::release);
Adapter::session.release();
return 0;
}

307
test/test_listmanager_dummy.cc

@ -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");
}

5
test/test_message_cache.cc

@ -7,7 +7,7 @@
#include <cassert>
#include <cstring>
#include <sys/param.h>
#include <unistd.h>
#include <cstring>
#include "../src/message_cache.hh"
#include "../src/Adapter.hh"
@ -18,6 +18,7 @@ using namespace pEp;
int main(int argc, char **argv)
{
Test::setup(argc, argv);
Adapter::session.initialize();
::pEp_identity *alice = ::new_identity("alice@mail.com", nullptr, PEP_OWN_USERID, "Alice");
::myself(Adapter::session(), alice);
@ -119,6 +120,6 @@ int main(int argc, char **argv)
::free_identity(bob);
::free_identity(alice);
Adapter::session(Adapter::release);
Adapter::session.release();
return 0;
}

642
test/test_pEpSQLite.cc

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

115
test/test_pEpSQLite.hh

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

5
test/test_passphrase_cache.cc

@ -4,7 +4,6 @@
#include "framework/framework.hh"
#include <iostream>
#include <unistd.h>
#include <assert.h>
#include <sys/param.h>
@ -31,7 +30,7 @@ extern "C" {
int main(int argc, char **argv)
{
Test::setup(argc, argv);
Adapter::session.initialize();
const char *str = "23";
char *bytes = NULL;
int n = 42;
@ -97,6 +96,6 @@ int main(int argc, char **argv)
status = cache.api(api_test2, Adapter::session(), 23, str, bytes, sl);
assert(status == PEP_STATUS_OK);
Adapter::session(Adapter::release);
Adapter::session.release();
return 0;
}

5
test/test_semaphore.cc

@ -3,8 +3,7 @@
#include <iostream>
#include <thread>
#include <unistd.h>
#include "../src/std_utils.hh"
#include "../src/Semaphore.hh"
using namespace std;
@ -24,7 +23,7 @@ int main()
cout << "1: keep going\n";
});
sleep(1);
Utils::sleep_millis(1000);
thread thread2([&]() {
cout << "2: setting go\n";

492
test/test_swarm_group.cc

@ -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();
}

135
test/test_swarm_tofu.cc

@ -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();
}

53
test/test_sync_init.cc

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

65
test/test_template_swarm_multi.cc

@ -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();
}

55
test/test_template_swarm_single.cc

@ -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();
}

152
test/test_tofu.cc

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