From 8468f15927ec7c83a5fca671bac1a4112883b8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= <sebastien.blin@savoirfairelinux.com> Date: Tue, 27 Jun 2023 11:45:43 -0400 Subject: [PATCH] conversation: move logic of message management in daemon This heavily changes the API for the client. The goal here is to move the logic to construct the history to show in the daemon and not the client. This has several advantages: 1. Logic is common across every platforms, so bugs should not be platform-specific 2. Client got less logic 3. Signal are simplified, if an edition comes, "MessageUpdated" will be triggered instead MessageReceived. 4. Some tests are added for linearizing the history. 5. Search on edition is fixed. Tests got heavily re-written, but the content didn't change (2 tests are added, the rest is simplification). GitLab: #831 Change-Id: Ie7c81077067e9e49db1dd396829c9225c0512c16 --- CMakeLists.txt | 2 +- .../cx.ring.Ring.ConfigurationManager.xml | 169 +- bin/dbus/dbusconfigurationmanager.hpp | 37 + bin/jni/conversation.i | 22 + bin/jni/jni_interface.i | 5 + bin/nodejs/callback.h | 6 + bin/nodejs/conversation.i | 22 + bin/nodejs/nodejs_interface.i | 5 + configure.ac | 2 +- meson.build | 2 +- src/client/conversation_interface.cpp | 20 + src/client/ring_signal.cpp | 5 + src/jami/conversation_interface.h | 60 + src/jamidht/conversation.cpp | 406 ++- src/jamidht/conversation.h | 17 +- src/jamidht/conversation_module.cpp | 38 + src/jamidht/conversation_module.h | 8 + src/jamidht/conversationrepository.cpp | 86 +- src/jamidht/conversationrepository.h | 19 +- test/unitTest/conversation/call.cpp | 112 +- test/unitTest/conversation/conversation.cpp | 2779 ++++------------- .../conversation/conversationMembersEvent.cpp | 2113 ++++--------- .../conversation/conversationRequest.cpp | 1353 +++----- test/unitTest/syncHistory/syncHistory.cpp | 973 ++---- 24 files changed, 2779 insertions(+), 5482 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f536e45a94..d980f58567 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) project(jami-core - VERSION 13.11.0 + VERSION 14.0.0 LANGUAGES C CXX) set(PACKAGE_NAME "Jami Daemon") set (CMAKE_CXX_STANDARD 17) diff --git a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml index c1643d2d84..e7f7aa8973 100644 --- a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml +++ b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml @@ -1848,9 +1848,21 @@ <arg type="u" name="id" direction="out"/> </method> - <method name="loadConversationUntil" tp:name-for-bindings="loadConversationUntil"> - <tp:added version="10.0.0"/> - <tp:docstring> + <method name="loadConversation" tp:name-for-bindings="loadConversation"> + <tp:added version="14.0.0"/> + <tp:docstring> + Load messages from a conversation + </tp:docstring> + <arg type="s" name="accountId" direction="in"/> + <arg type="s" name="conversationId" direction="in"/> + <arg type="s" name="fromMessage" direction="in"/> + <arg type="u" name="n" direction="in"/> + <arg type="u" name="id" direction="out"/> + </method> + + <method name="loadConversationUntil" tp:name-for-bindings="loadConversationUntil"> + <tp:added version="10.0.0"/> + <tp:docstring> Load messages from a conversation </tp:docstring> <arg type="s" name="accountId" direction="in"/> @@ -1873,6 +1885,15 @@ <arg type="u" name="count" direction="out"/> </method> + <method name="clearCache" tp:name-for-bindings="clearCache"> + <tp:added version="14.0.0"/> + <tp:docstring> + Clear interactions loaded from daemon. Used by dbus to reload interactions + </tp:docstring> + <arg type="s" name="accountId" direction="in"/> + <arg type="s" name="conversationId" direction="in"/> + </method> + <method name="searchConversation" tp:name-for-bindings="searchConversation"> <tp:added version="13.4.0"/> <tp:docstring> @@ -1992,9 +2013,37 @@ </arg> </signal> - <signal name="messagesFound" tp:name-for-bindings="messagesFound"> - <tp:added version="10.0.0"/> - <tp:docstring> + <signal name="swarmLoaded" tp:name-for-bindings="swarmLoaded"> + <tp:added version="14.0.0"/> + <tp:docstring> + Notify clients when a conversation is loaded + </tp:docstring> + <arg type="u" name="id"> + <tp:docstring> + Id of the related loadConversationMessages's request + </tp:docstring> + </arg> + <arg type="s" name="account_id"> + <tp:docstring> + Account id related + </tp:docstring> + </arg> + <arg type="s" name="conversation_id"> + <tp:docstring> + Conversation id + </tp:docstring> + </arg> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out3" value="VectorSwarmMessage"/> + <arg type="a(sssa{ss}aa{ss}aa{ss})" name="messages"> + <tp:docstring> + Messages of the conversation + </tp:docstring> + </arg> + </signal> + + <signal name="messagesFound" tp:name-for-bindings="messagesFound"> + <tp:added version="10.0.0"/> + <tp:docstring> Notify clients when messages matching a regex are found </tp:docstring> <arg type="u" name="id"> @@ -2043,9 +2092,111 @@ </arg> </signal> - <signal name="conversationProfileUpdated" tp:name-for-bindings="conversationProfileUpdated"> - <tp:added version="13.4.0"/> - <tp:docstring> + <signal name="swarmMessageReceived" tp:name-for-bindings="swarmMessageReceived"> + <tp:added version="14.0.0"/> + <tp:docstring> + Notify clients when a conversation receives a new message + </tp:docstring> + <arg type="s" name="account_id"> + <tp:docstring> + Account id related + </tp:docstring> + </arg> + <arg type="s" name="conversation_id"> + <tp:docstring> + Conversation id + </tp:docstring> + </arg> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out2" value="SwarmMessage"/> + <arg type="(sssa{ss}aa{ss}aa{ss})" name="message"> + <tp:docstring> + The new message + </tp:docstring> + </arg> + </signal> + + <signal name="swarmMessageUpdated" tp:name-for-bindings="swarmMessageUpdated"> + <tp:added version="14.0.0"/> + <tp:docstring> + Notify clients when a conversation receives a new message + </tp:docstring> + <arg type="s" name="account_id"> + <tp:docstring> + Account id related + </tp:docstring> + </arg> + <arg type="s" name="conversation_id"> + <tp:docstring> + Conversation id + </tp:docstring> + </arg> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out2" value="SwarmMessage"/> + <arg type="(sssa{ss}aa{ss}aa{ss})" name="message"> + <tp:docstring> + The new message + </tp:docstring> + </arg> + </signal> + + <signal name="reactionAdded" tp:name-for-bindings="reactionAdded"> + <tp:added version="14.0.0"/> + <tp:docstring> + Notify clients when a conversation receives a new message + </tp:docstring> + <arg type="s" name="account_id"> + <tp:docstring> + Account id related + </tp:docstring> + </arg> + <arg type="s" name="conversation_id"> + <tp:docstring> + Conversation id + </tp:docstring> + </arg> + <arg type="s" name="message_id"> + <tp:docstring> + Conversation id + </tp:docstring> + </arg> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out3" value="MapStringString"/> + <arg type="a{ss}" name="reaction"> + <tp:docstring> + The new message + </tp:docstring> + </arg> + </signal> + + <signal name="reactionRemoved" tp:name-for-bindings="reactionRemoved"> + <tp:added version="14.0.0"/> + <tp:docstring> + Notify clients when a conversation receives a new message + </tp:docstring> + <arg type="s" name="account_id"> + <tp:docstring> + Account id related + </tp:docstring> + </arg> + <arg type="s" name="conversation_id"> + <tp:docstring> + Conversation id + </tp:docstring> + </arg> + <arg type="s" name="message_id"> + <tp:docstring> + Message's id + </tp:docstring> + </arg> + <arg type="s" name="reaction_id"> + <tp:docstring> + Reaction's id + </tp:docstring> + </arg> + </signal> + + + <signal name="conversationProfileUpdated" tp:name-for-bindings="conversationProfileUpdated"> + <tp:added version="13.4.0"/> + <tp:docstring> Notify clients when a conversation got its profile changed. </tp:docstring> <arg type="s" name="account_id"> diff --git a/bin/dbus/dbusconfigurationmanager.hpp b/bin/dbus/dbusconfigurationmanager.hpp index 8fddb461bf..638d08fad8 100644 --- a/bin/dbus/dbusconfigurationmanager.hpp +++ b/bin/dbus/dbusconfigurationmanager.hpp @@ -29,6 +29,7 @@ class DBusConfigurationManager : public sdbus::AdaptorInterfaces<cx::ring::Ring::ConfigurationManager_adaptor> { public: + using DBusSwarmMessage = sdbus::Struct<std::string, std::string, std::string, std::map<std::string, std::string>, std::vector<std::map<std::string, std::string>>, std::vector<std::map<std::string, std::string>>>; DBusConfigurationManager(sdbus::IConnection& connection) : AdaptorInterfaces(connection, "/cx/ring/Ring/ConfigurationManager") { @@ -931,6 +932,15 @@ public: return libjami::loadConversationMessages(accountId, conversationId, fromMessage, n); } + uint32_t + loadConversation(const std::string& accountId, + const std::string& conversationId, + const std::string& fromMessage, + const uint32_t& n) + { + return libjami::loadConversation(accountId, conversationId, fromMessage, n); + } + uint32_t loadConversationUntil(const std::string& accountId, const std::string& conversationId, @@ -950,6 +960,13 @@ public: return libjami::countInteractions(accountId, conversationId, toId, fromId, authorUri); } + void + clearCache(const std::string& accountId, + const std::string& conversationId) + { + return libjami::clearCache(accountId, conversationId); + } + uint32_t searchConversation(const std::string& accountId, const std::string& conversationId, @@ -1122,10 +1139,30 @@ private: const std::map<std::string, SharedCallback> convEvHandlers = { exportable_serialized_callback<ConversationSignal::ConversationLoaded>( std::bind(&DBusConfigurationManager::emitConversationLoaded, this, _1, _2, _3, _4)), + exportable_serialized_callback<ConversationSignal::SwarmLoaded>([this](const uint32_t& id, const std::string& account_id, const std::string& conversation_id, const std::vector<libjami::SwarmMessage>& messages) { + std::vector<DBusSwarmMessage> msgList; + for (const auto& message: messages) { + DBusSwarmMessage msg {message.id, message.type, message.linearizedParent, message.body, message.reactions, message.editions}; + msgList.push_back(msg); + } + DBusConfigurationManager::emitSwarmLoaded(id, account_id, conversation_id, msgList); + }), exportable_serialized_callback<ConversationSignal::MessagesFound>( std::bind(&DBusConfigurationManager::emitMessagesFound, this, _1, _2, _3, _4)), exportable_serialized_callback<ConversationSignal::MessageReceived>( std::bind(&DBusConfigurationManager::emitMessageReceived, this, _1, _2, _3)), + exportable_serialized_callback<ConversationSignal::SwarmMessageReceived>([this](const std::string& account_id, const std::string& conversation_id, const libjami::SwarmMessage& message) { + DBusSwarmMessage msg {message.id, message.type, message.linearizedParent, message.body, message.reactions, message.editions}; + DBusConfigurationManager::emitSwarmMessageReceived(account_id, conversation_id, msg); + }), + exportable_serialized_callback<ConversationSignal::SwarmMessageUpdated>([this](const std::string& account_id, const std::string& conversation_id, const libjami::SwarmMessage& message) { + DBusSwarmMessage msg {message.id, message.type, message.linearizedParent, message.body, message.reactions, message.editions}; + DBusConfigurationManager::emitSwarmMessageUpdated(account_id, conversation_id, msg); + }), + exportable_serialized_callback<ConversationSignal::ReactionAdded>( + std::bind(&DBusConfigurationManager::emitReactionAdded, this, _1, _2, _3, _4)), + exportable_serialized_callback<ConversationSignal::ReactionRemoved>( + std::bind(&DBusConfigurationManager::emitReactionRemoved, this, _1, _2, _3, _4)), exportable_serialized_callback<ConversationSignal::ConversationProfileUpdated>( std::bind(&DBusConfigurationManager::emitConversationProfileUpdated, this, _1, _2, _3)), exportable_serialized_callback<ConversationSignal::ConversationRequestReceived>( diff --git a/bin/jni/conversation.i b/bin/jni/conversation.i index 30c10efdee..fdecb07e2e 100644 --- a/bin/jni/conversation.i +++ b/bin/jni/conversation.i @@ -26,8 +26,13 @@ class ConversationCallback { public: virtual ~ConversationCallback(){} virtual void conversationLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){} + virtual void swarmLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<libjami::SwarmMessage> /*messages*/){} virtual void messagesFound(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){} virtual void messageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/){} + virtual void swarmMessageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, const libjami::SwarmMessage& /*message*/){} + virtual void swarmMessageUpdated(const std::string& /*accountId*/, const std::string& /* conversationId */, const libjami::SwarmMessage& /*message*/){} + virtual void reactionAdded(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* messageId */, std::map<std::string, std::string> /*reaction*/){} + virtual void reactionRemoved(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* messageId */, const std::string& /* reactionId */){} virtual void conversationProfileUpdated(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*profile*/){} virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){} virtual void conversationRequestDeclined(const std::string& /*accountId*/, const std::string& /* conversationId */){} @@ -43,6 +48,16 @@ public: namespace libjami { +struct SwarmMessage +{ + std::string id; + std::string type; + std::string linearizedParent; + std::map<std::string, std::string> body; + std::vector<std::map<std::string, std::string>> reactions; + std::vector<std::map<std::string, std::string>> editions; +}; + // Conversation management std::string startConversation(const std::string& accountId); void acceptConversationRequest(const std::string& accountId, const std::string& conversationId); @@ -64,8 +79,10 @@ namespace libjami { // Message send/load void sendMessage(const std::string& accountId, const std::string& conversationId, const std::string& message, const std::string& replyTo, const int32_t& flag); uint32_t loadConversationMessages(const std::string& accountId, const std::string& conversationId, const std::string& fromMessage, size_t n); + uint32_t loadConversation(const std::string& accountId, const std::string& conversationId, const std::string& fromMessage, size_t n); uint32_t loadConversationUntil(const std::string& accountId, const std::string& conversationId, const std::string& fromMessage, const std::string& toMessage); uint32_t countInteractions(const std::string& accountId, const std::string& conversationId, const std::string& toId, const std::string& fromId, const std::string& authorUri); + void clearCache(const std::string& accountId, const std::string& conversationId); uint32_t searchConversation(const std::string& accountId, const std::string& conversationId, const std::string& author, @@ -82,8 +99,13 @@ class ConversationCallback { public: virtual ~ConversationCallback(){} virtual void conversationLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){} + virtual void swarmLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<libjami::SwarmMessage> /*messages*/){} virtual void messagesFound(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){} virtual void messageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/){} + virtual void swarmMessageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, const libjami::SwarmMessage& /*message*/){} + virtual void swarmMessageUpdated(const std::string& /*accountId*/, const std::string& /* conversationId */, const libjami::SwarmMessage& /*message*/){} + virtual void reactionAdded(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* messageId */, std::map<std::string, std::string> /*messageId*/){} + virtual void reactionRemoved(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* messageId */, const std::string& /* reactionId */){} virtual void conversationProfileUpdated(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*profile*/){} virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){} virtual void conversationRequestDeclined(const std::string& /*accountId*/, const std::string& /* conversationId */){} diff --git a/bin/jni/jni_interface.i b/bin/jni/jni_interface.i index 6b549324d9..9e54cda758 100644 --- a/bin/jni/jni_interface.i +++ b/bin/jni/jni_interface.i @@ -325,8 +325,13 @@ void init(ConfigurationCallback* confM, Callback* callM, PresenceCallback* presM const std::map<std::string, SharedCallback> conversationHandlers = { exportable_callback<ConversationSignal::ConversationLoaded>(bind(&ConversationCallback::conversationLoaded, convM, _1, _2, _3, _4)), + exportable_callback<ConversationSignal::SwarmLoaded>(bind(&ConversationCallback::swarmLoaded, convM, _1, _2, _3, _4)), exportable_callback<ConversationSignal::MessagesFound>(bind(&ConversationCallback::messagesFound, convM, _1, _2, _3, _4)), exportable_callback<ConversationSignal::MessageReceived>(bind(&ConversationCallback::messageReceived, convM, _1, _2, _3)), + exportable_callback<ConversationSignal::SwarmMessageReceived>(bind(&ConversationCallback::swarmMessageReceived, convM, _1, _2, _3)), + exportable_callback<ConversationSignal::SwarmMessageUpdated>(bind(&ConversationCallback::swarmMessageUpdated, convM, _1, _2, _3)), + exportable_callback<ConversationSignal::ReactionAdded>(bind(&ConversationCallback::reactionAdded, convM, _1, _2, _3, _4)), + exportable_callback<ConversationSignal::ReactionRemoved>(bind(&ConversationCallback::reactionRemoved, convM, _1, _2, _3, _4)), exportable_callback<ConversationSignal::ConversationProfileUpdated>(bind(&ConversationCallback::conversationProfileUpdated, convM, _1, _2, _3)), exportable_callback<ConversationSignal::ConversationRequestReceived>(bind(&ConversationCallback::conversationRequestReceived, convM, _1, _2, _3)), exportable_callback<ConversationSignal::ConversationRequestDeclined>(bind(&ConversationCallback::conversationRequestDeclined, convM, _1, _2)), diff --git a/bin/nodejs/callback.h b/bin/nodejs/callback.h index 7b2a4d69b2..154d6d77d6 100644 --- a/bin/nodejs/callback.h +++ b/bin/nodejs/callback.h @@ -31,8 +31,10 @@ Persistent<Function> incomingMessageCb; Persistent<Function> incomingCallCb; Persistent<Function> incomingCallWithMediaCb; Persistent<Function> conversationLoadedCb; +Persistent<Function> swarmLoadedCb; Persistent<Function> messagesFoundCb; Persistent<Function> messageReceivedCb; +Persistent<Function> swarmMessageReceivedCb; Persistent<Function> conversationProfileUpdatedCb; Persistent<Function> conversationRequestReceivedCb; Persistent<Function> conversationRequestDeclinedCb; @@ -99,10 +101,14 @@ getPresistentCb(std::string_view signal) return &incomingCallWithMediaCb; else if (signal == "ConversationLoaded") return &conversationLoadedCb; + else if (signal == "SwarmLoaded") + return &swarmLoadedCb; else if (signal == "MessagesFound") return &messagesFoundCb; else if (signal == "MessageReceived") return &messageReceivedCb; + else if (signal == "SwarmMessageReceived") + return &swarmMessageReceivedCb; else if (signal == "ConversationProfileUpdated") return &conversationProfileUpdatedCb; else if (signal == "ConversationReady") diff --git a/bin/nodejs/conversation.i b/bin/nodejs/conversation.i index 803fcd3b22..6673a8c4e3 100644 --- a/bin/nodejs/conversation.i +++ b/bin/nodejs/conversation.i @@ -26,8 +26,13 @@ class ConversationCallback { public: virtual ~ConversationCallback(){} virtual void conversationLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){} + virtual void swarmLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<libjami::SwarmMessage> /*messages*/){} virtual void messagesFound(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){} virtual void messageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/){} + virtual void swarmMessageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, const libjami::SwarmMessage& /*message*/){} + virtual void swarmMessageUpdated(const std::string& /*accountId*/, const std::string& /* conversationId */, const libjami::SwarmMessage& /*message*/){} + virtual void reactionAdded(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* messageId */, std::map<std::string, std::string> /*reaction*/){} + virtual void reactionRemoved(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* messageId */, const std::string& /* reactionId */){} virtual void conversationProfileUpdated(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*profile*/){} virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){} virtual void conversationRequestDeclined(const std::string& /*accountId*/, const std::string& /* conversationId */){} @@ -43,6 +48,16 @@ public: namespace libjami { +struct SwarmMessage +{ + std::string id; + std::string type; + std::string linearizedParent; + std::map<std::string, std::string> body; + std::vector<std::map<std::string, std::string>> reactions; + std::vector<std::map<std::string, std::string>> editions; +}; + // Conversation management std::string startConversation(const std::string& accountId); void acceptConversationRequest(const std::string& accountId, const std::string& conversationId); @@ -64,7 +79,9 @@ namespace libjami { // Message send/load void sendMessage(const std::string& accountId, const std::string& conversationId, const std::string& message, const std::string& replyTo, const int32_t& flag); uint32_t loadConversationMessages(const std::string& accountId, const std::string& conversationId, const std::string& fromMessage, size_t n); + uint32_t loadConversation(const std::string& accountId, const std::string& conversationId, const std::string& fromMessage, size_t n); uint32_t loadConversationUntil(const std::string& accountId, const std::string& conversationId, const std::string& fromMessage, const std::string& toMessage); + void clearCache(const std::string& accountId, const std::string& conversationId); uint32_t countInteractions(const std::string& accountId, const std::string& conversationId, const std::string& toId, const std::string& fromId, const std::string& authorUri); uint32_t searchConversation(const std::string& accountId, const std::string& conversationId, @@ -83,8 +100,13 @@ class ConversationCallback { public: virtual ~ConversationCallback(){} virtual void conversationLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){} + virtual void swarmLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<libjami::SwarmMessage> /*messages*/){} virtual void messagesFound(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){} virtual void messageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/){} + virtual void swarmMessageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, const libjami::SwarmMessage& /*message*/){} + virtual void swarmMessageUpdated(const std::string& /*accountId*/, const std::string& /* conversationId */, const libjami::SwarmMessage& /*message*/){} + virtual void reactionAdded(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* messageId */, std::map<std::string, std::string> /*reaction*/){} + virtual void reactionRemoved(const std::string& /*accountId*/, const std::string& /* conversationId */, const std::string& /* messageId */, const std::string& /* reactionId */){} virtual void conversationProfileUpdated(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*profile*/){} virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){} virtual void conversationRequestDeclined(const std::string& /*accountId*/, const std::string& /* conversationId */){} diff --git a/bin/nodejs/nodejs_interface.i b/bin/nodejs/nodejs_interface.i index 13a3d911cf..42a73c39ea 100644 --- a/bin/nodejs/nodejs_interface.i +++ b/bin/nodejs/nodejs_interface.i @@ -150,8 +150,13 @@ void init(const SWIGV8_VALUE& funcMap){ const std::map<std::string, SharedCallback> conversationHandlers = { exportable_callback<ConversationSignal::ConversationLoaded>(bind(&conversationLoaded, _1, _2, _3, _4)), + exportable_callback<ConversationSignal::SwarmLoaded>(bind(&swarmLoaded, _1, _2, _3, _4)), exportable_callback<ConversationSignal::MessagesFound>(bind(&messagesFound, _1, _2, _3, _4)), exportable_callback<ConversationSignal::MessageReceived>(bind(&messageReceived, _1, _2, _3)), + exportable_callback<ConversationSignal::SwarmMessageReceived>(bind(&swarmMessageReceived, _1, _2, _3)), + exportable_callback<ConversationSignal::SwarmMessageUpdated>(bind(&swarmMessageUpdated, _1, _2, _3)), + exportable_callback<ConversationSignal::ReactionAdded>(bind(&reactionAdded, _1, _2, _3, _4)), + exportable_callback<ConversationSignal::ReactionRemoved>(bind(&reactionRemoved, _1, _2, _3, _4)), exportable_callback<ConversationSignal::ConversationProfileUpdated>(bind(&conversationProfileUpdated, _1, _2, _3)), exportable_callback<ConversationSignal::ConversationRequestReceived>(bind(&conversationRequestReceived, _1, _2, _3)), exportable_callback<ConversationSignal::ConversationRequestDeclined>(bind(&conversationRequestDeclined, _1, _2)), diff --git a/configure.ac b/configure.ac index f94bf70f77..5c7467037e 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl Jami - configure.ac dnl Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) -AC_INIT([Jami Daemon],[13.11.0],[jami@gnu.org],[jami]) +AC_INIT([Jami Daemon],[14.0.0],[jami@gnu.org],[jami]) dnl Clear the implicit flags that default to '-g -O2', otherwise they dnl take precedence over the values we set via the diff --git a/meson.build b/meson.build index 73c16b7745..97ae791a12 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('jami-daemon', ['c', 'cpp'], - version: '13.11.0', + version: '14.0.0', license: 'GPL3+', default_options: ['cpp_std=gnu++17', 'buildtype=debugoptimized'], meson_version:'>= 0.56' diff --git a/src/client/conversation_interface.cpp b/src/client/conversation_interface.cpp index 661832b96f..0fb86ab66a 100644 --- a/src/client/conversation_interface.cpp +++ b/src/client/conversation_interface.cpp @@ -195,6 +195,18 @@ loadConversationMessages(const std::string& accountId, return 0; } +uint32_t +loadConversation(const std::string& accountId, + const std::string& conversationId, + const std::string& fromMessage, + size_t n) +{ + if (auto acc = jami::Manager::instance().getAccount<jami::JamiAccount>(accountId)) + if (auto convModule = acc->convModule()) + return convModule->loadConversation(conversationId, fromMessage, n); + return 0; +} + uint32_t loadConversationUntil(const std::string& accountId, const std::string& conversationId, @@ -220,6 +232,14 @@ countInteractions(const std::string& accountId, return 0; } +void +clearCache(const std::string& accountId, const std::string& conversationId) +{ + if (auto acc = jami::Manager::instance().getAccount<jami::JamiAccount>(accountId)) + if (auto convModule = acc->convModule()) + convModule->clearCache(conversationId); +} + uint32_t searchConversation(const std::string& accountId, const std::string& conversationId, diff --git a/src/client/ring_signal.cpp b/src/client/ring_signal.cpp index 5576d32b3d..9d86696ca9 100644 --- a/src/client/ring_signal.cpp +++ b/src/client/ring_signal.cpp @@ -129,8 +129,13 @@ getSignalHandlers() /* Conversation */ exported_callback<libjami::ConversationSignal::ConversationLoaded>(), + exported_callback<libjami::ConversationSignal::SwarmLoaded>(), exported_callback<libjami::ConversationSignal::MessagesFound>(), exported_callback<libjami::ConversationSignal::MessageReceived>(), + exported_callback<libjami::ConversationSignal::SwarmMessageReceived>(), + exported_callback<libjami::ConversationSignal::SwarmMessageUpdated>(), + exported_callback<libjami::ConversationSignal::ReactionAdded>(), + exported_callback<libjami::ConversationSignal::ReactionRemoved>(), exported_callback<libjami::ConversationSignal::ConversationProfileUpdated>(), exported_callback<libjami::ConversationSignal::ConversationRequestReceived>(), exported_callback<libjami::ConversationSignal::ConversationRequestDeclined>(), diff --git a/src/jami/conversation_interface.h b/src/jami/conversation_interface.h index aed802231e..26d932c775 100644 --- a/src/jami/conversation_interface.h +++ b/src/jami/conversation_interface.h @@ -26,11 +26,28 @@ #include <vector> #include <map> #include <string> +#include <list> #include "jami.h" namespace libjami { +struct SwarmMessage +{ + std::string id; + std::string type; + std::string linearizedParent; + std::map<std::string, std::string> body; + std::vector<std::map<std::string, std::string>> reactions; + std::vector<std::map<std::string, std::string>> editions; + + void fromMapStringString(const std::map<std::string, std::string>& commit) { + id = commit.at("id"); + type = commit.at("type"); + body = commit; // TODO erase type/id? + } +}; + // Conversation management LIBJAMI_PUBLIC std::string startConversation(const std::string& accountId); LIBJAMI_PUBLIC void acceptConversationRequest(const std::string& accountId, @@ -79,6 +96,10 @@ LIBJAMI_PUBLIC uint32_t loadConversationMessages(const std::string& accountId, const std::string& conversationId, const std::string& fromMessage, size_t n); +LIBJAMI_PUBLIC uint32_t loadConversation(const std::string& accountId, + const std::string& conversationId, + const std::string& fromMessage, + size_t n); LIBJAMI_PUBLIC uint32_t loadConversationUntil(const std::string& accountId, const std::string& conversationId, const std::string& fromMessage, @@ -88,6 +109,7 @@ LIBJAMI_PUBLIC uint32_t countInteractions(const std::string& accountId, const std::string& toId, const std::string& fromId, const std::string& authorUri); +LIBJAMI_PUBLIC void clearCache(const std::string& accountId, const std::string& conversationId); LIBJAMI_PUBLIC uint32_t searchConversation(const std::string& accountId, const std::string& conversationId, const std::string& author, @@ -110,6 +132,14 @@ struct LIBJAMI_PUBLIC ConversationSignal const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/); }; + struct LIBJAMI_PUBLIC SwarmLoaded + { + constexpr static const char* name = "SwarmLoaded"; + using cb_type = void(uint32_t /* id */, + const std::string& /*accountId*/, + const std::string& /* conversationId */, + std::vector<SwarmMessage> /*messages*/); + }; struct LIBJAMI_PUBLIC MessagesFound { constexpr static const char* name = "MessagesFound"; @@ -125,6 +155,36 @@ struct LIBJAMI_PUBLIC ConversationSignal const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/); }; + struct LIBJAMI_PUBLIC SwarmMessageReceived + { + constexpr static const char* name = "SwarmMessageReceived"; + using cb_type = void(const std::string& /*accountId*/, + const std::string& /* conversationId */, + const SwarmMessage& /*message*/); + }; + struct LIBJAMI_PUBLIC SwarmMessageUpdated + { + constexpr static const char* name = "SwarmMessageUpdated"; + using cb_type = void(const std::string& /*accountId*/, + const std::string& /* conversationId */, + const SwarmMessage& /*message*/); + }; + struct LIBJAMI_PUBLIC ReactionAdded + { + constexpr static const char* name = "ReactionAdded"; + using cb_type = void(const std::string& /*accountId*/, + const std::string& /* conversationId */, + const std::string& /* messageId */, + std::map<std::string, std::string> /*reaction*/); + }; + struct LIBJAMI_PUBLIC ReactionRemoved + { + constexpr static const char* name = "ReactionRemoved"; + using cb_type = void(const std::string& /*accountId*/, + const std::string& /* conversationId */, + const std::string& /* messageId */, + const std::string& /* reactionId */); + }; struct LIBJAMI_PUBLIC ConversationProfileUpdated { constexpr static const char* name = "ConversationProfileUpdated"; diff --git a/src/jamidht/conversation.cpp b/src/jamidht/conversation.cpp index 629c115d83..0d72a0e5be 100644 --- a/src/jamidht/conversation.cpp +++ b/src/jamidht/conversation.cpp @@ -29,12 +29,14 @@ #include <string_view> #include <opendht/thread_pool.h> #include <tuple> +#include <optional> #include "swarm/swarm_manager.h" #ifdef ENABLE_PLUGIN #include "manager.h" #include "plugin/jamipluginmanager.h" #include "plugin/streamdata.h" #endif +#include "jami/conversation_interface.h" namespace jami { @@ -119,6 +121,16 @@ ConversationRequest::toMap() const return result; } +using MessageList = std::list<std::shared_ptr<libjami::SwarmMessage>>; + +struct History +{ + MessageList messageList {}; + std::map<std::string, std::shared_ptr<libjami::SwarmMessage>> quickAccess {}; + std::map<std::string, std::list<std::shared_ptr<libjami::SwarmMessage>>> pendingEditions {}; + std::map<std::string, std::list<std::map<std::string, std::string>>> pendingReactions {}; +}; + class Conversation::Impl { public: @@ -369,6 +381,7 @@ public: auto convId = repository_->id(); auto ok = !commits.empty(); auto lastId = ok ? commits.rbegin()->at(ConversationMapKeys::ID) : ""; + addToHistory(commits, true); if (ok) { bool announceMember = false; for (const auto& c : commits) { @@ -602,6 +615,7 @@ public: std::weak_ptr<JamiAccount> account_; std::atomic_bool isRemoving_ {false}; std::vector<std::map<std::string, std::string>> loadMessages(const LogOptions& options); + std::vector<libjami::SwarmMessage> loadMessages2(const LogOptions& options, History* optHistory = nullptr); void pull(); std::vector<std::map<std::string, std::string>> mergeHistory(const std::string& uri); @@ -642,6 +656,24 @@ public: std::unique_ptr<asio::steady_timer> fallbackTimer_; bool isMobile {false}; + + /** + * Loaded history represents the linearized history to show for clients + */ + mutable History loadedHistory_ {}; + std::vector<libjami::SwarmMessage> addToHistory(const std::vector<std::map<std::string, std::string>>& commits, + bool messageReceived = false, + History* history = nullptr) const; + // While loading the history, we need to avoid: + // - reloading history (can just be ignored) + // - adding new commits (should wait for history to be loaded) + bool isLoadingHistory_ {false}; + mutable std::mutex historyMtx_ {}; + mutable std::condition_variable historyCv_ {}; + + void handleReaction(History& history, const std::shared_ptr<libjami::SwarmMessage>& sharedCommit) const; + void handleEdition(History& history, const std::shared_ptr<libjami::SwarmMessage>& sharedCommit, bool messageReceived) const; + bool handleMessage(History& history, const std::shared_ptr<libjami::SwarmMessage>& sharedCommit, bool messageReceived) const; }; bool @@ -738,9 +770,280 @@ Conversation::Impl::loadMessages(const LogOptions& options) { if (!repository_) return {}; - std::vector<ConversationCommit> convCommits; - convCommits = repository_->log(options); - return repository_->convCommitToMap(convCommits); + std::vector<ConversationCommit> commits; + auto startLogging = options.from == ""; + auto breakLogging = false; + repository_->log([&](const auto& id, const auto& author, const auto& commit) { + if (!commits.empty()) { + // Set linearized parent + commits.rbegin()->linearized_parent = id; + } + if (options.skipMerge && git_commit_parentcount(commit.get()) > 1) { + return CallbackResult::Skip; + } + if ((options.nbOfCommits != 0 + && commits.size() == options.nbOfCommits)) + return CallbackResult::Break; // Stop logging + if (breakLogging) + return CallbackResult::Break; // Stop logging + if (id == options.to) { + if (options.includeTo) + breakLogging = true; // For the next commit + else + return CallbackResult::Break; // Stop logging + } + + if (!startLogging && options.from != "" && options.from == id) + startLogging = true; + if (!startLogging) + return CallbackResult::Skip; // Start logging after this one + + if (options.fastLog) { + if (options.authorUri != "") { + if (options.authorUri == repository_->uriFromDevice(author.email)) { + return CallbackResult::Break; // Found author, stop + } + } + // Used to only count commit + commits.emplace(commits.end(), ConversationCommit {}); + return CallbackResult::Skip; + } + + return CallbackResult::Ok; // Continue + }, + [&](auto&& cc) { + commits.emplace(commits.end(), std::forward<decltype(cc)>(cc)); + }, + [](auto, auto, auto) { return false; }, + options.from, + options.logIfNotFound); + return repository_->convCommitToMap(commits); +} + +std::vector<libjami::SwarmMessage> +Conversation::Impl::loadMessages2(const LogOptions& options, History* optHistory) +{ + { + std::lock_guard<std::mutex> lock(historyMtx_); + if (!repository_ || (!optHistory && isLoadingHistory_)) + return {}; + if (!optHistory) + isLoadingHistory_ = true; + } + + auto startLogging = options.from == ""; + auto breakLogging = false; + auto currentHistorySize = loadedHistory_.messageList.size(); + std::vector<std::string> replies; + std::vector<libjami::SwarmMessage> ret; + repository_->log([&](const auto& id, const auto& author, const auto& commit) { + if (options.skipMerge && git_commit_parentcount(commit.get()) > 1) { + return CallbackResult::Skip; + } + if (replies.empty()) { // This avoid load until + // NOTE: in the future, we may want to add "Reply-Body" in commit to avoid to load until this commit + if ((options.nbOfCommits != 0 + && (loadedHistory_.messageList.size() - currentHistorySize) == options.nbOfCommits)) + return CallbackResult::Break; // Stop logging + if (breakLogging) + return CallbackResult::Break; // Stop logging + if (id == options.to) { + if (options.includeTo) + breakLogging = true; // For the next commit + else + return CallbackResult::Break; // Stop logging + } + } + + if (!startLogging && options.from != "" && options.from == id) + startLogging = true; + if (!startLogging) + return CallbackResult::Skip; // Start logging after this one + + if (options.fastLog) { + if (options.authorUri != "") { + if (options.authorUri == repository_->uriFromDevice(author.email)) { + return CallbackResult::Break; // Found author, stop + } + } + } + + return CallbackResult::Ok; // Continue + }, + [&](auto&& cc) { + std::map<std::string, std::string> map = *repository_->convCommitToMap(cc); + if (map.find("reply-to") != map.end()) { + replies.emplace_back(map.at("reply-to")); + } + auto it = std::find(replies.begin(), replies.end(), map.at("id")); + if (it != replies.end()) { + replies.erase(it); + } + auto added = addToHistory({map}, false, optHistory); + ret.insert(ret.end(), added.begin(), added.end()); + }, + [](auto, auto, auto) { return false; }, + options.from, + options.logIfNotFound); + + if (!optHistory) { + std::lock_guard<std::mutex> lock(historyMtx_); + isLoadingHistory_ = false; + historyCv_.notify_all(); + } + + return ret; +} + +void +Conversation::Impl::handleReaction(History& history, const std::shared_ptr<libjami::SwarmMessage>& sharedCommit) const +{ + auto it = history.quickAccess.find(sharedCommit->body.at("react-to")); + auto peditIt = history.pendingEditions.find(sharedCommit->id); + if (peditIt != history.pendingEditions.end()) { + auto oldBody = sharedCommit->body; + sharedCommit->body["body"] = peditIt->second.front()->body["body"]; + if (sharedCommit->body.at("body").empty()) + return; + history.pendingEditions.erase(peditIt); + } + if (it != history.quickAccess.end()) { + it->second->reactions.emplace_back(sharedCommit->body); + emitSignal<libjami::ConversationSignal::ReactionAdded>(accountId_, repository_->id(), it->second->id, sharedCommit->body); + } else { + history.pendingReactions[sharedCommit->body.at("react-to")].emplace_back(sharedCommit->body); + } +} + +void +Conversation::Impl::handleEdition(History& history, const std::shared_ptr<libjami::SwarmMessage>& sharedCommit, bool messageReceived) const +{ + auto editId = sharedCommit->body.at("edit"); + auto it = history.quickAccess.find(editId); + if (it != history.quickAccess.end()) { + auto baseCommit = it->second; + if (baseCommit) { + auto itReact = baseCommit->body.find("react-to"); + auto body = sharedCommit->body.at("body"); + // Edit reaction + if (itReact != baseCommit->body.end()) { + baseCommit->body["body"] = body; // Replace body if pending + it = history.quickAccess.find(itReact->second); + auto itPending = history.pendingReactions.find(itReact->second); + if (it != history.quickAccess.end()) { + baseCommit = it->second; // Base commit + auto itPreviousReact = std::find_if(baseCommit->reactions.begin(), baseCommit->reactions.end(), [&](const auto& reaction) { + return reaction.at("id") == editId; + }); + if (itPreviousReact != baseCommit->reactions.end()) { + (*itPreviousReact)["body"] = body; + if (body.empty()) { + baseCommit->reactions.erase(itPreviousReact); + emitSignal<libjami::ConversationSignal::ReactionRemoved>(accountId_, repository_->id(), baseCommit->id, editId); + } + } + } else if (itPending != history.pendingReactions.end()) { + // Else edit if pending + auto itReaction = std::find_if(itPending->second.begin(), itPending->second.end(), [&](const auto& reaction) { + return reaction.at("id") == editId; + }); + if (itReaction != itPending->second.end()) { + (*itReaction)["body"] = body; + if (body.empty()) + itPending->second.erase(itReaction); + } + } else { + // Add to pending edtions + messageReceived? history.pendingEditions[editId].emplace_front(sharedCommit) + : history.pendingEditions[editId].emplace_back(sharedCommit); + } + } else { + // Normal message + it->second->editions.emplace(it->second->editions.begin(), it->second->body); + it->second->body["body"] = sharedCommit->body["body"]; + emitSignal<libjami::ConversationSignal::SwarmMessageUpdated>(accountId_, repository_->id(), *it->second); + } + } + } else { + messageReceived? history.pendingEditions[editId].emplace_front(sharedCommit) + : history.pendingEditions[editId].emplace_back(sharedCommit); + } +} + +bool +Conversation::Impl::handleMessage(History& history, const std::shared_ptr<libjami::SwarmMessage>& sharedCommit, bool messageReceived) const +{ + if (messageReceived) { + // For a received message, we place it at the beginning of the list + if (!history.messageList.empty()) + sharedCommit->linearizedParent = (*history.messageList.begin())->id; + history.messageList.emplace_front(sharedCommit); + } else { + // For a loaded message, we load from newest to oldest + // So we change the parent of the last message. + if (!history.messageList.empty()) + (*history.messageList.rbegin())->linearizedParent = sharedCommit->id; + history.messageList.emplace_back(sharedCommit); + } + // Handle pending reactions/editions + auto reactIt = history.pendingReactions.find(sharedCommit->id); + if (reactIt != history.pendingReactions.end()) { + for (const auto& commitBody: reactIt->second) + sharedCommit->reactions.emplace_back(commitBody); + history.pendingReactions.erase(reactIt); + } + auto peditIt = history.pendingEditions.find(sharedCommit->id); + if (peditIt != history.pendingEditions.end()) { + auto oldBody = sharedCommit->body; + sharedCommit->body["body"] = peditIt->second.front()->body["body"]; + peditIt->second.pop_front(); + for (const auto& commit: peditIt->second) { + sharedCommit->editions.emplace_back(commit->body); + } + sharedCommit->editions.emplace_back(oldBody); + history.pendingEditions.erase(peditIt); + } + // Announce to client + if (messageReceived) + emitSignal<libjami::ConversationSignal::SwarmMessageReceived>(accountId_, repository_->id(), *sharedCommit); + return !messageReceived; +} + +std::vector<libjami::SwarmMessage> +Conversation::Impl::addToHistory(const std::vector<std::map<std::string, std::string>>& commits, bool messageReceived, History* optHistory) const +{ + if (messageReceived && (!optHistory && isLoadingHistory_)) { + std::unique_lock<std::mutex> lk(historyMtx_); + historyCv_.wait(lk, [&] { return !isLoadingHistory_; }); + } + std::vector<libjami::SwarmMessage> messages; + auto addCommit = [&](const auto& commit) { + auto* history = optHistory ? optHistory : &loadedHistory_; + auto commitId = commit.at("id"); + if (history->quickAccess.find(commitId) != history->quickAccess.end()) + return; // Already present + auto typeIt = commit.find("type"); + auto reactToIt = commit.find("react-to"); + auto editIt = commit.find("edit"); + // Nothing to show for the client, skip + if (typeIt != commit.end() && typeIt->second == "merge") + return; + + auto sharedCommit = std::make_shared<libjami::SwarmMessage>(); + sharedCommit->fromMapStringString(commit); + history->quickAccess[commitId] = sharedCommit; + + if (reactToIt != commit.end() && !reactToIt->second.empty()) { + handleReaction(*history, sharedCommit); + } else if (editIt != commit.end() && !editIt->second.empty()) { + handleEdition(*history, sharedCommit, messageReceived); + } else if (handleMessage(*history, sharedCommit, messageReceived)) { + messages.emplace_back(*sharedCommit); + } + }; + std::for_each(commits.begin(), commits.end(), addCommit); + + return messages; } Conversation::Conversation(const std::shared_ptr<JamiAccount>& account, @@ -1178,7 +1481,7 @@ Conversation::getCommit(const std::string& commitId) const } void -Conversation::loadMessages(const OnLoadMessages& cb, const LogOptions& options) +Conversation::loadMessages(OnLoadMessages cb, const LogOptions& options) { if (!cb) return; @@ -1189,6 +1492,27 @@ Conversation::loadMessages(const OnLoadMessages& cb, const LogOptions& options) }); } +void +Conversation::loadMessages2(const OnLoadMessages2& cb, const LogOptions& options) +{ + if (!cb) + return; + dht::ThreadPool::io().run([w = weak(), cb = std::move(cb), options] { + if (auto sthis = w.lock()) { + cb(sthis->pimpl_->loadMessages2(options)); + } + }); +} + +void +Conversation::clearCache() +{ + pimpl_->loadedHistory_.messageList.clear(); + pimpl_->loadedHistory_.quickAccess.clear(); + pimpl_->loadedHistory_.pendingEditions.clear(); + pimpl_->loadedHistory_.pendingReactions.clear(); +} + std::string Conversation::lastCommitId() const { @@ -1976,14 +2300,15 @@ Conversation::countInteractions(const std::string& toId, const std::string& fromId, const std::string& authorUri) const { - // Log but without content to avoid costly convertions. LogOptions options; options.to = toId; options.from = fromId; options.authorUri = authorUri; options.logIfNotFound = false; options.fastLog = true; - return pimpl_->repository_->log(options).size(); + History history; + auto res = pimpl_->loadMessages2(options, &history); + return res.size(); } void @@ -1998,7 +2323,74 @@ Conversation::search(uint32_t req, auto acc = sthis->pimpl_->account_.lock(); if (!acc) return; - auto commits = sthis->pimpl_->repository_->search(filter); + + History history; + std::vector<std::map<std::string, std::string>> commits {}; + // std::regex_constants::ECMAScript is the default flag. + auto re = std::regex(filter.regexSearch, + filter.caseSensitive ? std::regex_constants::ECMAScript + : std::regex_constants::icase); + sthis->pimpl_->repository_->log([&](const auto& id, const auto& author, auto& commit) { + if (!filter.author.empty() && filter.author != sthis->uriFromDevice(author.email)) { + // Filter author + return CallbackResult::Skip; + } + auto commitTime = git_commit_time(commit.get()); + if (filter.before && filter.before < commitTime) { + // Only get commits before this date + return CallbackResult::Skip; + } + if (filter.after && filter.after > commitTime) { + // Only get commits before this date + if (git_commit_parentcount(commit.get()) <= 1) + return CallbackResult::Break; + else + return CallbackResult::Skip; // Because we are sorting it with + // GIT_SORT_TOPOLOGICAL | GIT_SORT_TIME + } + + return CallbackResult::Ok; // Continue + }, + [&](auto&& cc) { + sthis->pimpl_->addToHistory({*sthis->pimpl_->repository_->convCommitToMap(cc)}, false, &history); + }, + [&](auto id, auto, auto) { + if (id == filter.lastId) + return true; + return false; + }, + "", + false); + // Search on generated history + for (auto& message : history.messageList) { + auto contentType = message->type; + auto isSearchable = contentType == "text/plain" + || contentType == "application/data-transfer+json"; + if (filter.type.empty() && !isSearchable) { + // Not searchable, at least for now + continue; + } else if (contentType == filter.type || filter.type.empty()) { + if (isSearchable) { + // If it's a text match the body, else the display name + auto body = contentType == "text/plain" ? message->body.at("body") + : message->body.at("displayName"); + std::smatch body_match; + if (std::regex_search(body, body_match, re)) { + auto commit = message->body; + commit["id"] = message->id; + commit["type"] = message->type; + commits.emplace_back(commit); + } + } else { + // Matching type, just add it to the results + commits.emplace_back(message->body); + } + + if (filter.maxResult != 0 && commits.size() == filter.maxResult) + break; + } + } + if (commits.size() > 0) emitSignal<libjami::ConversationSignal::MessagesFound>(req, acc->getAccountID(), diff --git a/src/jamidht/conversation.h b/src/jamidht/conversation.h index 3d883ad5c8..55c4b44c75 100644 --- a/src/jamidht/conversation.h +++ b/src/jamidht/conversation.h @@ -22,6 +22,7 @@ #include "jamidht/conversationrepository.h" #include "conversationrepository.h" #include "swarm/swarm_protocol.h" +#include "jami/conversation_interface.h" #include <json/json.h> #include <msgpack.hpp> @@ -134,6 +135,8 @@ enum class ConversationMode; using OnPullCb = std::function<void(bool fetchOk)>; using OnLoadMessages = std::function<void(std::vector<std::map<std::string, std::string>>&& messages)>; +using OnLoadMessages2 + = std::function<void(std::vector<libjami::SwarmMessage>&& messages)>; using OnCommitCb = std::function<void(const std::string&)>; using OnDoneCb = std::function<void(bool, const std::string&)>; using OnMultiDoneCb = std::function<void(const std::vector<std::string>&)>; @@ -296,7 +299,17 @@ public: * @param cb The callback when loaded * @param options The log options */ - void loadMessages(const OnLoadMessages& cb, const LogOptions& options); + void loadMessages(OnLoadMessages cb, const LogOptions& options); + /** + * Get a range of messages + * @param cb The callback when loaded + * @param options The log options + */ + void loadMessages2(const OnLoadMessages2& cb, const LogOptions& options); + /** + * Clear all cached messages + */ + void clearCache(); /** * Retrieve one commit * @param commitId @@ -474,7 +487,6 @@ public: * @return displayed */ std::map<std::string, std::string> displayed() const; - /** * Retrieve how many interactions there is from HEAD to interactionId * @param toId "" for getting the whole history @@ -485,7 +497,6 @@ public: uint32_t countInteractions(const std::string& toId, const std::string& fromId = "", const std::string& authorUri = "") const; - /** * Search in the conversation via a filter * @param req Id of the request diff --git a/src/jamidht/conversation_module.cpp b/src/jamidht/conversation_module.cpp index 5372378c19..e2712371cf 100644 --- a/src/jamidht/conversation_module.cpp +++ b/src/jamidht/conversation_module.cpp @@ -2009,6 +2009,44 @@ ConversationModule::loadConversationMessages(const std::string& conversationId, return 0; } +void +ConversationModule::clearCache(const std::string& conversationId) +{ + if (auto conv = pimpl_->getConversation(conversationId)) { + std::lock_guard<std::mutex> lk(conv->mtx); + if (conv->conversation) { + conv->conversation->clearCache(); + } + } +} + +uint32_t +ConversationModule::loadConversation(const std::string& conversationId, + const std::string& fromMessage, + size_t n) +{ + auto acc = pimpl_->account_.lock(); + if (auto conv = pimpl_->getConversation(conversationId)) { + std::lock_guard<std::mutex> lk(conv->mtx); + if (conv->conversation) { + const uint32_t id = std::uniform_int_distribution<uint32_t> {}(acc->rand); + LogOptions options; + options.from = fromMessage; + options.nbOfCommits = n; + conv->conversation->loadMessages2( + [accountId = pimpl_->accountId_, conversationId, id](auto&& messages) { + emitSignal<libjami::ConversationSignal::SwarmLoaded>(id, + accountId, + conversationId, + messages); + }, + options); + return id; + } + } + return 0; +} + uint32_t ConversationModule::loadConversationUntil(const std::string& conversationId, const std::string& fromMessage, diff --git a/src/jamidht/conversation_module.h b/src/jamidht/conversation_module.h index 0feaad8963..f7e3cba941 100644 --- a/src/jamidht/conversation_module.h +++ b/src/jamidht/conversation_module.h @@ -226,9 +226,17 @@ public: uint32_t loadConversationMessages(const std::string& conversationId, const std::string& fromMessage = "", size_t n = 0); + uint32_t loadConversation(const std::string& conversationId, + const std::string& fromMessage = "", + size_t n = 0); uint32_t loadConversationUntil(const std::string& conversationId, const std::string& fromMessage, const std::string& to); + /** + * Clear loaded interactions + * @param conversationId + */ + void clearCache(const std::string& conversationId); // File transfer /** diff --git a/src/jamidht/conversationrepository.cpp b/src/jamidht/conversationrepository.cpp index 8812960296..f05b7bc2ed 100644 --- a/src/jamidht/conversationrepository.cpp +++ b/src/jamidht/conversationrepository.cpp @@ -54,13 +54,6 @@ as_view(const GitObject& blob) return as_view(reinterpret_cast<git_blob*>(blob.get())); } -enum class CallbackResult { Skip, Break, Ok }; - -using PreConditionCb - = std::function<CallbackResult(const std::string&, const GitAuthor&, const GitCommit&)>; -using PostConditionCb - = std::function<bool(const std::string&, const GitAuthor&, ConversationCommit&)>; - class ConversationRepository::Impl { public: @@ -158,7 +151,6 @@ public: const std::string& from = "", bool logIfNotFound = true) const; std::vector<ConversationCommit> log(const LogOptions& options) const; - std::vector<std::map<std::string, std::string>> search(const Filter& filter) const; GitObject fileAtTree(const std::string& path, const GitTree& tree) const; GitObject memberCertificate(std::string_view memberUri, const GitTree& tree) const; @@ -2184,74 +2176,6 @@ ConversationRepository::Impl::log(const LogOptions& options) const return commits; } -std::vector<std::map<std::string, std::string>> -ConversationRepository::Impl::search(const Filter& filter) const -{ - std::vector<std::map<std::string, std::string>> commits {}; - // std::regex_constants::ECMAScript is the default flag. - auto re = std::regex(filter.regexSearch, - filter.caseSensitive ? std::regex_constants::ECMAScript - : std::regex_constants::icase); - forEachCommit( - [&](const auto& id, const auto& author, auto& commit) { - if (!commits.empty()) { - // Set linearized parent - commits.rbegin()->at("linearizedParent") = id; - } - - if (!filter.author.empty() && filter.author != uriFromDevice(author.email)) { - // Filter author - return CallbackResult::Skip; - } - auto commitTime = git_commit_time(commit.get()); - if (filter.before && filter.before < commitTime) { - // Only get commits before this date - return CallbackResult::Skip; - } - if (filter.after && filter.after > commitTime) { - // Only get commits before this date - if (git_commit_parentcount(commit.get()) <= 1) - return CallbackResult::Break; - else - return CallbackResult::Skip; // Because we are sorting it with - // GIT_SORT_TOPOLOGICAL | GIT_SORT_TIME - } - - return CallbackResult::Ok; // Continue - }, - [&](auto&& cc) { - auto content = convCommitToMap(cc); - auto contentType = content ? content->at("type") : ""; - auto isSearchable = contentType == "text/plain" - || contentType == "application/data-transfer+json"; - if (filter.type.empty() && !isSearchable) { - // Not searchable, at least for now - return; - } else if (contentType == filter.type || filter.type.empty()) { - if (isSearchable) { - // If it's a text match the body, else the display name - auto body = contentType == "text/plain" ? content->at("body") - : content->at("displayName"); - std::smatch body_match; - if (std::regex_search(body, body_match, re)) { - commits.emplace(commits.end(), std::move(*content)); - } - } else { - // Matching type, just add it to the results - commits.emplace(commits.end(), std::move(*content)); - } - } - }, - [&](auto id, auto, auto) { - if (filter.maxResult != 0 && commits.size() == filter.maxResult) - return true; - if (id == filter.lastId) - return true; - return false; - }); - return commits; -} - GitObject ConversationRepository::Impl::fileAtTree(const std::string& path, const GitTree& tree) const { @@ -3123,10 +3047,14 @@ ConversationRepository::log(const LogOptions& options) const return pimpl_->log(options); } -std::vector<std::map<std::string, std::string>> -ConversationRepository::search(const Filter& filter) const +void +ConversationRepository::log(PreConditionCb&& preCondition, + std::function<void(ConversationCommit&&)>&& emplaceCb, + PostConditionCb&& postCondition, + const std::string& from, + bool logIfNotFound) const { - return pimpl_->search(filter); + pimpl_->forEachCommit(std::move(preCondition), std::move(emplaceCb), std::move(postCondition), from, logIfNotFound); } std::optional<ConversationCommit> diff --git a/src/jamidht/conversationrepository.h b/src/jamidht/conversationrepository.h index 60e585286e..de96ebd8dc 100644 --- a/src/jamidht/conversationrepository.h +++ b/src/jamidht/conversationrepository.h @@ -127,6 +127,13 @@ struct ConversationMember } }; +enum class CallbackResult { Skip, Break, Ok }; + +using PreConditionCb + = std::function<CallbackResult(const std::string&, const GitAuthor&, const GitCommit&)>; +using PostConditionCb + = std::function<bool(const std::string&, const GitAuthor&, ConversationCommit&)>; + /** * This class gives access to the git repository that represents the conversation */ @@ -219,16 +226,14 @@ public: * @return a list of commits */ std::vector<ConversationCommit> log(const LogOptions& options = {}) const; + void log(PreConditionCb&& preCondition, + std::function<void(ConversationCommit&&)>&& emplaceCb, + PostConditionCb&& postCondition, + const std::string& from = "", + bool logIfNotFound = true) const; std::optional<ConversationCommit> getCommit(const std::string& commitId, bool logIfNotFound = true) const; - /** - * Search in the conversation via a filter - * @param filter Parameters for the search - * @return matching commits - */ - std::vector<std::map<std::string, std::string>> search(const Filter& filter) const; - /** * Get parent via topological + date sort in branch main of a commit * @param commitId id to choice diff --git a/test/unitTest/conversation/call.cpp b/test/unitTest/conversation/call.cpp index c25b2ce299..dd92c8fe85 100644 --- a/test/unitTest/conversation/call.cpp +++ b/test/unitTest/conversation/call.cpp @@ -46,7 +46,7 @@ struct ConvData bool conferenceRemoved {false}; std::string hostState {}; std::string state {}; - std::vector<std::map<std::string, std::string>> messages {}; + std::vector<libjami::SwarmMessage> messages {}; }; class ConversationCallTest : public CppUnit::TestFixture @@ -157,10 +157,10 @@ ConversationCallTest::connectSignals() carlaData_.id = conversationId; cv.notify_one(); })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageReceived>( [&](const std::string& accountId, const std::string& conversationId, - std::map<std::string, std::string> message) { + const libjami::SwarmMessage& message) { if (accountId == aliceId && aliceData_.id == conversationId) aliceData_.messages.emplace_back(message); if (accountId == bobId && bobData_.id == conversationId) @@ -295,7 +295,7 @@ ConversationCallTest::testActiveCalls() auto callId = libjami::placeCallWithMedia(aliceId, "swarm:" + aliceData_.id, {}); // should get message cv.wait_for(lk, 30s, [&]() { return !aliceData_.messages.empty(); }); - CPPUNIT_ASSERT(aliceData_.messages[0]["type"] == "application/call-history+json"); + CPPUNIT_ASSERT(aliceData_.messages[0].type == "application/call-history+json"); // get active calls = 1 CPPUNIT_ASSERT(libjami::getActiveCalls(aliceId, aliceData_.id).size() == 1); @@ -306,7 +306,7 @@ ConversationCallTest::testActiveCalls() // should get message cv.wait_for(lk, 30s, [&]() { return !aliceData_.messages.empty(); }); - CPPUNIT_ASSERT(aliceData_.messages[0].find("duration") != aliceData_.messages[0].end()); + CPPUNIT_ASSERT(aliceData_.messages[0].body.find("duration") != aliceData_.messages[0].body.end()); // get active calls = 0 CPPUNIT_ASSERT(libjami::getActiveCalls(aliceId, aliceData_.id).size() == 0); @@ -352,18 +352,18 @@ ConversationCallTest::testActiveCalls3Peers() auto callId = libjami::placeCallWithMedia(aliceId, "swarm:" + aliceData_.id, {}); auto lastCommitIsCall = [&](const auto& data) { return !data.messages.empty() - && data.messages.rbegin()->at("type") == "application/call-history+json"; + && data.messages.rbegin()->body.at("type") == "application/call-history+json"; }; // should get message cv.wait_for(lk, 30s, [&]() { return lastCommitIsCall(aliceData_) && lastCommitIsCall(bobData_) && lastCommitIsCall(carlaData_); }); - auto confId = bobData_.messages.rbegin()->at("confId"); + auto confId = bobData_.messages.rbegin()->body.at("confId"); auto destination = fmt::format("rdv:{}/{}/{}/{}", bobData_.id, - bobData_.messages.rbegin()->at("uri"), - bobData_.messages.rbegin()->at("device"), + bobData_.messages.rbegin()->body.at("uri"), + bobData_.messages.rbegin()->body.at("device"), confId); aliceData_.conferenceChanged = false; @@ -392,9 +392,9 @@ ConversationCallTest::testActiveCalls3Peers() return !aliceData_.messages.empty() && !bobData_.messages.empty() && !carlaData_.messages.empty(); }); - CPPUNIT_ASSERT(aliceData_.messages[0].find("duration") != aliceData_.messages[0].end()); - CPPUNIT_ASSERT(bobData_.messages[0].find("duration") != bobData_.messages[0].end()); - CPPUNIT_ASSERT(carlaData_.messages[0].find("duration") != carlaData_.messages[0].end()); + CPPUNIT_ASSERT(aliceData_.messages[0].body.find("duration") != aliceData_.messages[0].body.end()); + CPPUNIT_ASSERT(bobData_.messages[0].body.find("duration") != bobData_.messages[0].body.end()); + CPPUNIT_ASSERT(carlaData_.messages[0].body.find("duration") != carlaData_.messages[0].body.end()); // get active calls = 0 CPPUNIT_ASSERT(libjami::getActiveCalls(aliceId, aliceData_.id).size() == 0); @@ -440,7 +440,7 @@ ConversationCallTest::testRejoinCall() auto callId = libjami::placeCallWithMedia(aliceId, "swarm:" + aliceData_.id, {}); auto lastCommitIsCall = [&](const auto& data) { return !data.messages.empty() - && data.messages.rbegin()->at("type") == "application/call-history+json"; + && data.messages.rbegin()->body.at("type") == "application/call-history+json"; }; // should get message cv.wait_for(lk, 30s, [&]() { @@ -448,11 +448,11 @@ ConversationCallTest::testRejoinCall() && lastCommitIsCall(carlaData_); }); - auto confId = bobData_.messages.rbegin()->at("confId"); + auto confId = bobData_.messages.rbegin()->body.at("confId"); auto destination = fmt::format("rdv:{}/{}/{}/{}", bobData_.id, - bobData_.messages.rbegin()->at("uri"), - bobData_.messages.rbegin()->at("device"), + bobData_.messages.rbegin()->body.at("uri"), + bobData_.messages.rbegin()->body.at("device"), confId); aliceData_.conferenceChanged = false; @@ -500,9 +500,9 @@ ConversationCallTest::testRejoinCall() return !aliceData_.messages.empty() && !bobData_.messages.empty() && !carlaData_.messages.empty(); }); - CPPUNIT_ASSERT(aliceData_.messages[0].find("duration") != aliceData_.messages[0].end()); - CPPUNIT_ASSERT(bobData_.messages[0].find("duration") != bobData_.messages[0].end()); - CPPUNIT_ASSERT(carlaData_.messages[0].find("duration") != carlaData_.messages[0].end()); + CPPUNIT_ASSERT(aliceData_.messages[0].body.find("duration") != aliceData_.messages[0].body.end()); + CPPUNIT_ASSERT(bobData_.messages[0].body.find("duration") != bobData_.messages[0].body.end()); + CPPUNIT_ASSERT(carlaData_.messages[0].body.find("duration") != carlaData_.messages[0].body.end()); // get active calls = 0 CPPUNIT_ASSERT(libjami::getActiveCalls(aliceId, aliceData_.id).size() == 0); @@ -536,7 +536,7 @@ ConversationCallTest::testParticipantHangupConfNotRemoved() auto callId = libjami::placeCallWithMedia(aliceId, "swarm:" + aliceData_.id, {}); auto lastCommitIsCall = [&](const auto& data) { return !data.messages.empty() - && data.messages.rbegin()->at("type") == "application/call-history+json"; + && data.messages.rbegin()->body.at("type") == "application/call-history+json"; }; // should get message cv.wait_for(lk, 30s, [&]() { @@ -545,9 +545,9 @@ ConversationCallTest::testParticipantHangupConfNotRemoved() auto destination = fmt::format("rdv:{}/{}/{}/{}", bobData_.id, - bobData_.messages.rbegin()->at("uri"), - bobData_.messages.rbegin()->at("device"), - bobData_.messages.rbegin()->at("confId")); + bobData_.messages.rbegin()->body.at("uri"), + bobData_.messages.rbegin()->body.at("device"), + bobData_.messages.rbegin()->body.at("confId")); aliceData_.conferenceChanged = false; auto bobCallId = libjami::placeCallWithMedia(bobId, destination, {}); @@ -600,19 +600,19 @@ ConversationCallTest::testJoinFinishedCall() auto callId = libjami::placeCallWithMedia(aliceId, "swarm:" + aliceData_.id, {}); auto lastCommitIsCall = [&](const auto& data) { return !data.messages.empty() - && data.messages.rbegin()->at("type") == "application/call-history+json"; + && data.messages.rbegin()->body.at("type") == "application/call-history+json"; }; // should get message cv.wait_for(lk, 30s, [&]() { return lastCommitIsCall(aliceData_) && lastCommitIsCall(bobData_) && lastCommitIsCall(carlaData_); }); - auto confId = bobData_.messages.rbegin()->at("confId"); + auto confId = bobData_.messages.rbegin()->body.at("confId"); auto destination = fmt::format("rdv:{}/{}/{}/{}", bobData_.id, - bobData_.messages.rbegin()->at("uri"), - bobData_.messages.rbegin()->at("device"), - bobData_.messages.rbegin()->at("confId")); + bobData_.messages.rbegin()->body.at("uri"), + bobData_.messages.rbegin()->body.at("device"), + bobData_.messages.rbegin()->body.at("confId")); // hangup aliceData_.messages.clear(); bobData_.messages.clear(); @@ -635,7 +635,7 @@ ConversationCallTest::testJoinFinishedCall() return lastCommitIsCall(aliceData_) && lastCommitIsCall(bobData_) && lastCommitIsCall(carlaData_) && bobData_.hostState == "CURRENT"; }); - confId = bobData_.messages.rbegin()->at("confId"); + confId = bobData_.messages.rbegin()->body.at("confId"); CPPUNIT_ASSERT(libjami::getActiveCalls(aliceId, aliceData_.id).size() == 1); // hangup aliceData_.messages.clear(); @@ -647,9 +647,9 @@ ConversationCallTest::testJoinFinishedCall() return !aliceData_.messages.empty() && !bobData_.messages.empty() && !carlaData_.messages.empty(); }); - CPPUNIT_ASSERT(aliceData_.messages[0].find("duration") != aliceData_.messages[0].end()); - CPPUNIT_ASSERT(bobData_.messages[0].find("duration") != bobData_.messages[0].end()); - CPPUNIT_ASSERT(carlaData_.messages[0].find("duration") != carlaData_.messages[0].end()); + CPPUNIT_ASSERT(aliceData_.messages[0].body.find("duration") != aliceData_.messages[0].body.end()); + CPPUNIT_ASSERT(bobData_.messages[0].body.find("duration") != bobData_.messages[0].body.end()); + CPPUNIT_ASSERT(carlaData_.messages[0].body.find("duration") != carlaData_.messages[0].body.end()); // get active calls = 0 CPPUNIT_ASSERT(libjami::getActiveCalls(aliceId, aliceData_.id).size() == 0); } @@ -697,7 +697,7 @@ ConversationCallTest::testJoinFinishedCallForbidden() auto callId = libjami::placeCallWithMedia(aliceId, "swarm:" + aliceData_.id, {}); auto lastCommitIsCall = [&](const auto& data) { return !data.messages.empty() - && data.messages.rbegin()->at("type") == "application/call-history+json"; + && data.messages.rbegin()->body.at("type") == "application/call-history+json"; }; // should get message cv.wait_for(lk, 30s, [&]() { @@ -705,12 +705,12 @@ ConversationCallTest::testJoinFinishedCallForbidden() && lastCommitIsCall(carlaData_); }); - auto confId = bobData_.messages.rbegin()->at("confId"); + auto confId = bobData_.messages.rbegin()->body.at("confId"); auto destination = fmt::format("rdv:{}/{}/{}/{}", bobData_.id, - bobData_.messages.rbegin()->at("uri"), - bobData_.messages.rbegin()->at("device"), - bobData_.messages.rbegin()->at("confId")); + bobData_.messages.rbegin()->body.at("uri"), + bobData_.messages.rbegin()->body.at("device"), + bobData_.messages.rbegin()->body.at("confId")); // hangup aliceData_.messages.clear(); @@ -738,7 +738,7 @@ ConversationCallTest::testJoinFinishedCallForbidden() && lastCommitIsCall(carlaData_) && bobData_.hostState == "CURRENT"; }); - confId = bobData_.messages.rbegin()->at("confId"); + confId = bobData_.messages.rbegin()->body.at("confId"); CPPUNIT_ASSERT(libjami::getActiveCalls(aliceId, aliceData_.id).size() == 1); @@ -753,9 +753,9 @@ ConversationCallTest::testJoinFinishedCallForbidden() return !aliceData_.messages.empty() && !bobData_.messages.empty() && !carlaData_.messages.empty(); }); - CPPUNIT_ASSERT(aliceData_.messages[0].find("duration") != aliceData_.messages[0].end()); - CPPUNIT_ASSERT(bobData_.messages[0].find("duration") != bobData_.messages[0].end()); - CPPUNIT_ASSERT(carlaData_.messages[0].find("duration") != carlaData_.messages[0].end()); + CPPUNIT_ASSERT(aliceData_.messages[0].body.find("duration") != aliceData_.messages[0].body.end()); + CPPUNIT_ASSERT(bobData_.messages[0].body.find("duration") != bobData_.messages[0].body.end()); + CPPUNIT_ASSERT(carlaData_.messages[0].body.find("duration") != carlaData_.messages[0].body.end()); // get active calls = 0 CPPUNIT_ASSERT(libjami::getActiveCalls(aliceId, aliceData_.id).size() == 0); @@ -787,7 +787,7 @@ ConversationCallTest::testUsePreference() bobData_.messages.clear(); auto lastCommitIsProfile = [&](const auto& data) { return !data.messages.empty() - && data.messages.rbegin()->at("type") == "application/update-profile"; + && data.messages.rbegin()->body.at("type") == "application/update-profile"; }; libjami::updateConversationInfos(aliceId, aliceData_.id, @@ -806,13 +806,13 @@ ConversationCallTest::testUsePreference() auto callId = libjami::placeCallWithMedia(bobId, "swarm:" + aliceData_.id, {}); auto lastCommitIsCall = [&](const auto& data) { return !data.messages.empty() - && data.messages.rbegin()->at("type") == "application/call-history+json"; + && data.messages.rbegin()->body.at("type") == "application/call-history+json"; }; // should get message cv.wait_for(lk, 30s, [&]() { return lastCommitIsCall(aliceData_) && lastCommitIsCall(bobData_); }); - auto confId = bobData_.messages.rbegin()->at("confId"); + auto confId = bobData_.messages.rbegin()->body.at("confId"); // Alice should be the host CPPUNIT_ASSERT(aliceAccount->getConference(confId)); @@ -837,12 +837,12 @@ ConversationCallTest::testJoinWhileActiveCall() auto callId = libjami::placeCallWithMedia(aliceId, "swarm:" + aliceData_.id, {}); auto lastCommitIsCall = [&](const auto& data) { return !data.messages.empty() - && data.messages.rbegin()->at("type") == "application/call-history+json"; + && data.messages.rbegin()->body.at("type") == "application/call-history+json"; }; // should get message CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return lastCommitIsCall(aliceData_); })); - auto confId = aliceData_.messages.rbegin()->at("confId"); + auto confId = aliceData_.messages.rbegin()->body.at("confId"); libjami::addConversationMember(aliceId, aliceData_.id, bobUri); CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return bobData_.requestReceived; })); @@ -887,7 +887,7 @@ ConversationCallTest::testCallSelfIfDefaultHost() bobData_.messages.clear(); auto lastCommitIsProfile = [&](const auto& data) { return !data.messages.empty() - && data.messages.rbegin()->at("type") == "application/update-profile"; + && data.messages.rbegin()->body.at("type") == "application/update-profile"; }; libjami::updateConversationInfos(aliceId, aliceData_.id, @@ -906,13 +906,13 @@ ConversationCallTest::testCallSelfIfDefaultHost() auto callId = libjami::placeCallWithMedia(aliceId, "swarm:" + aliceData_.id, {}); auto lastCommitIsCall = [&](const auto& data) { return !data.messages.empty() - && data.messages.rbegin()->at("type") == "application/call-history+json"; + && data.messages.rbegin()->body.at("type") == "application/call-history+json"; }; // should get message CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return lastCommitIsCall(aliceData_) && lastCommitIsCall(bobData_); })); - auto confId = aliceData_.messages.rbegin()->at("confId"); + auto confId = aliceData_.messages.rbegin()->body.at("confId"); // Alice should be the host CPPUNIT_ASSERT(aliceAccount->getConference(confId)); Manager::instance().hangupConference(aliceId, confId); @@ -945,7 +945,7 @@ ConversationCallTest::testNeedsHost() bobData_.messages.clear(); auto lastCommitIsProfile = [&](const auto& data) { return !data.messages.empty() - && data.messages.rbegin()->at("type") == "application/update-profile"; + && data.messages.rbegin()->body.at("type") == "application/update-profile"; }; libjami::updateConversationInfos(aliceId, aliceData_.id, @@ -996,7 +996,7 @@ ConversationCallTest::testAudioOnly() auto callId = libjami::placeCallWithMedia(aliceId, "swarm:" + aliceData_.id, mediaList); // should get message cv.wait_for(lk, 30s, [&]() { return !aliceData_.messages.empty() && !pInfos_.empty(); }); - CPPUNIT_ASSERT(aliceData_.messages[0]["type"] == "application/call-history+json"); + CPPUNIT_ASSERT(aliceData_.messages[0].type == "application/call-history+json"); CPPUNIT_ASSERT(pInfos_.size() == 1); CPPUNIT_ASSERT(pInfos_[0]["videoMuted"] == "true"); @@ -1006,7 +1006,7 @@ ConversationCallTest::testAudioOnly() // should get message cv.wait_for(lk, 30s, [&]() { return !aliceData_.messages.empty(); }); - CPPUNIT_ASSERT(aliceData_.messages[0].find("duration") != aliceData_.messages[0].end()); + CPPUNIT_ASSERT(aliceData_.messages[0].body.find("duration") != aliceData_.messages[0].body.end()); // get active calls = 0 CPPUNIT_ASSERT(libjami::getActiveCalls(aliceId, aliceData_.id).size() == 0); @@ -1052,13 +1052,13 @@ ConversationCallTest::testJoinAfterMuteHost() auto callId = libjami::placeCallWithMedia(aliceId, "swarm:" + aliceData_.id, mediaList); auto lastCommitIsCall = [&](const auto& data) { return !data.messages.empty() - && data.messages.rbegin()->at("type") == "application/call-history+json"; + && data.messages.rbegin()->type == "application/call-history+json"; }; // should get message CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return lastCommitIsCall(aliceData_) && lastCommitIsCall(bobData_) && !pInfos_.empty(); })); - auto confId = bobData_.messages.rbegin()->at("confId"); + auto confId = bobData_.messages.rbegin()->body.at("confId"); // Mute host auto call = std::dynamic_pointer_cast<SIPCall>(aliceAccount->getCall(callId)); @@ -1071,8 +1071,8 @@ ConversationCallTest::testJoinAfterMuteHost() // Bob join, alice must stay muted auto destination = fmt::format("rdv:{}/{}/{}/{}", bobData_.id, - bobData_.messages.rbegin()->at("uri"), - bobData_.messages.rbegin()->at("device"), + bobData_.messages.rbegin()->body.at("uri"), + bobData_.messages.rbegin()->body.at("device"), confId); aliceData_.conferenceChanged = false; diff --git a/test/unitTest/conversation/conversation.cpp b/test/unitTest/conversation/conversation.cpp index a092886c7c..4fbf617afd 100644 --- a/test/unitTest/conversation/conversation.cpp +++ b/test/unitTest/conversation/conversation.cpp @@ -56,6 +56,29 @@ struct ConvInfoTest namespace jami { namespace test { +struct UserData { + std::string conversationId; + bool removed {false}; + bool requestReceived {false}; + bool errorDetected {false}; + bool registered {false}; + bool stopped {false}; + bool deviceAnnounced {false}; + bool composing {false}; + bool sending {false}; + bool sent {false}; + bool searchFinished {false}; + std::string profilePath; + std::string payloadTrustRequest; + std::map<std::string, std::string> profile; + std::vector<libjami::SwarmMessage> messages; + std::vector<libjami::SwarmMessage> messagesUpdated; + std::vector<std::map<std::string, std::string>> reactions; + std::vector<std::map<std::string, std::string>> messagesFound; + std::vector<std::string> reactionRemoved; + std::map<std::string, std::string> preferences; +}; + class ConversationTest : public CppUnit::TestFixture { public: @@ -67,15 +90,22 @@ public: const std::string& fakeCert = ""); std::string aliceId; + UserData aliceData; std::string alice2Id; + UserData alice2Data; std::string bobId; + UserData bobData; std::string bob2Id; + UserData bob2Data; std::string carlaId; + UserData carlaData; std::mutex mtx; std::unique_lock<std::mutex> lk {mtx}; std::condition_variable cv; + void connectSignals(); + private: void testCreateConversation(); void testCreateConversationInvalidDisplayName(); @@ -200,10 +230,266 @@ ConversationTest::setUp() bobId = actors["bob"]; carlaId = actors["carla"]; + aliceData = {}; + alice2Data = {}; + bobData = {}; + bob2Data = {}; + carlaData = {}; + Manager::instance().sendRegister(carlaId, false); wait_for_announcement_of({aliceId, bobId}); } +void +ConversationTest::connectSignals() +{ + std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; + confHandlers.insert( + libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( + [&](const std::string& accountId, const std::map<std::string, std::string>&) { + if (accountId == aliceId) { + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto details = aliceAccount->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + aliceData.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + aliceData.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + aliceData.deviceAnnounced = deviceAnnounced == "true"; + } else if (accountId == bobId) { + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto details = bobAccount->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + bobData.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + bobData.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + bobData.deviceAnnounced = deviceAnnounced == "true"; + } else if (accountId == bob2Id) { + auto bob2Account = Manager::instance().getAccount<JamiAccount>(bob2Id); + auto details = bob2Account->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + bob2Data.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + bob2Data.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + bob2Data.deviceAnnounced = deviceAnnounced == "true"; + } else if (accountId == carlaId) { + auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); + auto details = carlaAccount->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + carlaData.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + carlaData.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + carlaData.deviceAnnounced = deviceAnnounced == "true"; + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( + [&](const std::string& accountId, const std::string& conversationId) { + if (accountId == aliceId) { + aliceData.conversationId = conversationId; + } else if (accountId == alice2Id) { + alice2Data.conversationId = conversationId; + } else if (accountId == bobId) { + bobData.conversationId = conversationId; + } else if (accountId == bob2Id) { + bob2Data.conversationId = conversationId; + } else if (accountId == carlaId) { + carlaData.conversationId = conversationId; + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( + [&](const std::string& account_id, + const std::string& /*from*/, + const std::string& /*conversationId*/, + const std::vector<uint8_t>& payload, + time_t /*received*/) { + auto payloadStr = std::string(payload.data(), payload.data() + payload.size()); + if (account_id == aliceId) + aliceData.payloadTrustRequest = payloadStr; + else if (account_id == bobId) + bobData.payloadTrustRequest = payloadStr; + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + std::map<std::string, std::string> /*metadatas*/) { + if (accountId == aliceId) { + aliceData.requestReceived = true; + } else if (accountId == bobId) { + bobData.requestReceived = true; + } else if (accountId == bob2Id) { + bob2Data.requestReceived = true; + } else if (accountId == carlaId) { + carlaData.requestReceived = true; + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageReceived>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + libjami::SwarmMessage message) { + if (accountId == aliceId) { + aliceData.messages.emplace_back(message); + } else if (accountId == bobId) { + bobData.messages.emplace_back(message); + } else if (accountId == carlaId) { + carlaData.messages.emplace_back(message); + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageUpdated>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + libjami::SwarmMessage message) { + if (accountId == aliceId) { + aliceData.messagesUpdated.emplace_back(message); + } else if (accountId == bobId) { + bobData.messagesUpdated.emplace_back(message); + } else if (accountId == carlaId) { + carlaData.messagesUpdated.emplace_back(message); + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ReactionAdded>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + const std::string& /* messageId */, + std::map<std::string, std::string> reaction) { + if (accountId == aliceId) { + aliceData.reactions.emplace_back(reaction); + } else if (accountId == bobId) { + bobData.reactions.emplace_back(reaction); + } else if (accountId == carlaId) { + carlaData.reactions.emplace_back(reaction); + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ReactionRemoved>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + const std::string& /* messageId */, + const std::string& reactionId) { + if (accountId == aliceId) { + aliceData.reactionRemoved.emplace_back(reactionId); + } else if (accountId == bobId) { + bobData.reactionRemoved.emplace_back(reactionId); + } else if (accountId == carlaId) { + carlaData.reactionRemoved.emplace_back(reactionId); + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + int /*code*/, + const std::string& /* what */) { + if (accountId == aliceId) + aliceData.errorDetected = true; + else if (accountId == bobId) + bobData.errorDetected = true; + else if (accountId == carlaId) + carlaData.errorDetected = true; + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConfigurationSignal::ComposingStatusChanged>( + [&](const std::string& accountId, + const std::string& /*conversationId*/, + const std::string& /*peer*/, + bool state) { + if (accountId == bobId) { + bobData.composing = state; + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConfigurationSignal::AccountMessageStatusChanged>( + [&](const std::string& accountId, + const std::string& /*conversationId*/, + const std::string& /*peer*/, + const std::string& /*msgId*/, + int status) { + if (accountId == aliceId) { + if (status == 2) + aliceData.sending = true; + if (status == 3) + aliceData.sent = true; + } else if (accountId == bobId) { + if (status == 2) + bobData.sending = true; + if (status == 3) + bobData.sent = true; + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationProfileUpdated>( + [&](const auto& accountId, const auto& /* conversationId */, const auto& profile) { + if (accountId == aliceId) { + aliceData.profile = profile; + } else if (accountId == bobId) { + bobData.profile = profile; + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( + [&](const std::string& accountId, const std::string&) { + if (accountId == aliceId) + aliceData.removed = true; + else if (accountId == bobId) + bobData.removed = true; + else if (accountId == bob2Id) + bob2Data.removed = true; + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ProfileReceived>( + [&](const std::string& accountId, const std::string& peerId, const std::string& path) { + if (accountId == bobId) + bobData.profilePath = path; + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationPreferencesUpdated>( + [&](const std::string& accountId, + const std::string& conversationId, + std::map<std::string, std::string> preferences) { + if (accountId == bobId) + bobData.preferences = preferences; + else if (accountId == bob2Id) + bob2Data.preferences = preferences; + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessagesFound>( + [&](uint32_t, + const std::string& accountId, + const std::string& conversationId, + std::vector<std::map<std::string, std::string>> msg) { + if (accountId == aliceId) { + aliceData.messagesFound.insert(aliceData.messagesFound.end(), msg.begin(), msg.end()); + aliceData.searchFinished = conversationId.empty(); + } + cv.notify_one(); + })); + libjami::registerSignalHandlers(confHandlers); +} + void ConversationTest::tearDown() { @@ -226,31 +512,20 @@ void ConversationTest::testCreateConversation() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto aliceDeviceId = aliceAccount->currentDeviceId(); auto uri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == aliceId) { - conversationReady = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - // Start conversation auto convId = libjami::startConversation(aliceId); - cv.wait_for(lk, 30s, [&]() { return conversationReady; }); - CPPUNIT_ASSERT(conversationReady); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); })); ConversationRepository repo(aliceAccount, convId); CPPUNIT_ASSERT(repo.mode() == ConversationMode::INVITES_ONLY); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); // Check created files @@ -341,24 +616,14 @@ void ConversationTest::testGetConversationsAfterRm() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto uri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == aliceId) { - conversationReady = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - // Start conversation auto convId = libjami::startConversation(aliceId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); })); auto conversations = libjami::getConversations(aliceId); CPPUNIT_ASSERT(conversations.size() == 1); @@ -371,24 +636,11 @@ void ConversationTest::testRemoveInvalidConversation() { std::cout << "\nRunning test: " << __func__ << std::endl; - - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); - auto uri = aliceAccount->getUsername(); - - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == aliceId) { - conversationReady = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); + connectSignals(); // Start conversation auto convId = libjami::startConversation(aliceId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); })); auto conversations = libjami::getConversations(aliceId); CPPUNIT_ASSERT(conversations.size() == 1); @@ -401,203 +653,86 @@ void ConversationTest::testSendMessage() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - auto convId = libjami::startConversation(aliceId); - libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / bobAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / bobId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); // Wait that alice sees Bob - cv.wait_for(lk, 30s, [&]() { return messageAliceReceived == 2; }); + cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == 2; }); + auto bobMsgSize = bobData.messages.size(); libjami::sendMessage(aliceId, convId, "hi"s, ""); - cv.wait_for(lk, 30s, [&]() { return messageBobReceived == 1; }); + cv.wait_for(lk, 30s, [&]() { return bobData.messages.size() == bobMsgSize + 1; }); } void ConversationTest::testSendMessageWithBadDisplayName() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - bool aliceRegistered = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = aliceAccount->getVolatileAccountDetails(); - auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; - if (daemonStatus == "REGISTERED") { - aliceRegistered = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - std::map<std::string, std::string> details; details[ConfProperties::DISPLAYNAME] = "<o>"; libjami::setAccountDetails(aliceId, details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceRegistered; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.registered; })); auto convId = libjami::startConversation(aliceId); - libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / bobAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / bobId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); // Wait that alice sees Bob - cv.wait_for(lk, 30s, [&]() { return messageAliceReceived == 2; }); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == 2; })); + auto bobMsgSize = bobData.messages.size(); libjami::sendMessage(aliceId, convId, "hi"s, ""); - cv.wait_for(lk, 30s, [&]() { return messageBobReceived == 1; }); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.messages.size() == bobMsgSize + 1; })); } void ConversationTest::testReplaceWithBadCertificate() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - auto requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - auto conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - auto errorDetected = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - auto convId = libjami::startConversation(aliceId); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); })); // Wait that alice sees Bob - cv.wait_for(lk, 30s, [&]() { return messageAliceReceived == 2; }); + cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == 2; }); // Replace alice's certificate with a bad one. - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() / "conversations" / convId; + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; auto aliceDevicePath = repoPath / "devices" / fmt::format("{}.crt", aliceAccount->currentDeviceId()); auto bobDevicePath = repoPath / "devices" / fmt::format("{}.crt", bobAccount->currentDeviceId()); std::filesystem::copy(bobDevicePath, @@ -613,50 +748,33 @@ ConversationTest::testReplaceWithBadCertificate() wbuilder["commentStyle"] = "None"; wbuilder["indentation"] = ""; auto message = Json::writeString(wbuilder, root); - messageBobReceived = 0; commitInRepo(repoPath, aliceAccount, message); // now we need to sync! + bobData.errorDetected = false; libjami::sendMessage(aliceId, convId, "trigger sync!"s, ""); // We should detect the incorrect commit! - cv.wait_for(lk, 30s, [&]() { return errorDetected; }); + cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; }); } void ConversationTest::testSendMessageTriggerMessageReceived() { std::cout << "\nRunning test: " << __func__ << std::endl; - - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageReceived = 0; - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - messageReceived += 1; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& /* accountId */, const std::string& /* conversationId */) { - conversationReady = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + connectSignals(); auto convId = libjami::startConversation(aliceId); - cv.wait_for(lk, 30s, [&] { return conversationReady; }); + cv.wait_for(lk, 30s, [&] { return !aliceData.conversationId.empty(); }); + auto msgSize = aliceData.messages.size(); libjami::sendMessage(aliceId, convId, "hi"s, ""); - cv.wait_for(lk, 30s, [&] { return messageReceived == 1; }); - CPPUNIT_ASSERT(messageReceived == 1); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceData.messages.size() == msgSize + 1; })); } void ConversationTest::testMergeTwoDifferentHeads() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); @@ -666,33 +784,15 @@ ConversationTest::testMergeTwoDifferentHeads() carlaAccount->trackBuddyPresence(aliceUri, true); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, carlaGotMessage = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == carlaId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /* message */) { - if (accountId == carlaId && conversationId == convId) { - carlaGotMessage = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - + auto msgSize = aliceData.messages.size(); aliceAccount->convModule()->addConversationMember(convId, carlaUri, false); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == msgSize + 1; })); // Cp conversations & convInfo - auto repoPathAlice = fileutils::get_data_dir() / aliceAccount->getAccountID() / "conversations"; + auto repoPathAlice = fileutils::get_data_dir() / aliceId / "conversations"; auto repoPathCarla = fileutils::get_data_dir() / carlaAccount->getAccountID() / "conversations"; std::filesystem::copy(repoPathAlice, repoPathCarla, std::filesystem::copy_options::recursive); - auto ciPathAlice = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto ciPathAlice = fileutils::get_data_dir() / aliceId / "convInfo"; auto ciPathCarla = fileutils::get_data_dir() / carlaAccount->getAccountID() / "convInfo"; @@ -710,9 +810,7 @@ ConversationTest::testMergeTwoDifferentHeads() // Start Carla, should merge and all messages should be there Manager::instance().sendRegister(carlaId, true); - carlaGotMessage = false; - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaGotMessage; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return !carlaData.messages.empty(); })); } void @@ -797,7 +895,7 @@ ConversationTest::testSendMessageToMultipleParticipants() })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / bobAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / bobId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); repoPath = fileutils::get_data_dir() / carlaAccount->getAccountID() @@ -815,288 +913,124 @@ void ConversationTest::testPingPongMessages() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) - messageBobReceived += 1; - if (accountId == aliceId) - messageAliceReceived += 1; - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); auto convId = libjami::startConversation(aliceId); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - messageAliceReceived = 0; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); CPPUNIT_ASSERT( - cv.wait_for(lk, 60s, [&]() { return conversationReady && messageAliceReceived == 1; })); + cv.wait_for(lk, 60s, [&]() { return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / bobAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / bobId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); - messageBobReceived = 0; - messageAliceReceived = 0; + aliceMsgSize = aliceData.messages.size(); + auto bobMsgSize = bobData.messages.size(); libjami::sendMessage(aliceId, convId, "ping"s, ""); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return messageBobReceived == 1 && messageAliceReceived == 1; + return bobMsgSize + 1 == bobData.messages.size() && aliceMsgSize + 1 == aliceData.messages.size(); })); libjami::sendMessage(bobId, convId, "pong"s, ""); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return messageBobReceived == 2 && messageAliceReceived == 2; + return bobMsgSize + 2 == bobData.messages.size() && aliceMsgSize + 2 == aliceData.messages.size(); })); libjami::sendMessage(bobId, convId, "ping"s, ""); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return messageBobReceived == 3 && messageAliceReceived == 3; + return bobMsgSize + 3 == bobData.messages.size() && aliceMsgSize + 3 == aliceData.messages.size(); })); libjami::sendMessage(aliceId, convId, "pong"s, ""); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return messageBobReceived == 4 && messageAliceReceived == 4; + return bobMsgSize + 4 == bobData.messages.size() && aliceMsgSize + 4 == aliceData.messages.size(); })); - libjami::unregisterSignalHandlers(); } void ConversationTest::testIsComposing() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - aliceComposing = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::ComposingStatusChanged>( - [&](const std::string& accountId, - const std::string& conversationId, - const std::string& peer, - bool state) { - if (accountId == bobId && conversationId == convId && peer == aliceUri) { - aliceComposing = state; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); // Check created files auto bobInvited = repoPath / "invited" / bobUri; CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobInvited)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); aliceAccount->setIsComposing("swarm:" + convId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceComposing; })); - + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.composing; })); aliceAccount->setIsComposing("swarm:" + convId, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceComposing; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.composing; })); } void ConversationTest::testMessageStatus() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId) { - if (message["type"] == "member") - memberMessageGenerated = true; - cv.notify_one(); - } - })); - bool sending = false, sent = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::AccountMessageStatusChanged>( - [&](const std::string& accountId, - const std::string& conversationId, - const std::string& peer, - const std::string& msgId, - int status) { - if (accountId == aliceId && convId == conversationId) { - if (status == 2) - sending = true; - if (status == 3) - sent = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); // Check created files auto bobInvited = repoPath / "invited" / bobUri; CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobInvited)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); - sending = false; - sent = false; + aliceData.sending = false; + aliceData.sent = false; libjami::sendMessage(aliceId, convId, "hi"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return sending && sent; })); - - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.sending && aliceData.sent; })); } void ConversationTest::testSetMessageDisplayed() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - msgDisplayed = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId) { - if (message["type"] == "member") - memberMessageGenerated = true; - cv.notify_one(); - } - })); - std::string aliceLastMsg; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::AccountMessageStatusChanged>( - [&](const std::string& accountId, - const std::string& conversationId, - const std::string& peer, - const std::string& msgId, - int status) { - if (conversationId == convId && peer == aliceUri && status == 3) { - if (accountId == bobId && msgId == conversationId) - msgDisplayed = true; - else if (accountId == aliceId) - aliceLastMsg = msgId; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - aliceLastMsg = ""; + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && !aliceLastMsg.empty(); })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); // Check created files auto bobInvited = repoPath / "invited" / bobUri; CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobInvited)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); // Last displayed messages should not be set yet auto membersInfos = libjami::getConversationMembers(bobId, convId); @@ -1112,12 +1046,12 @@ ConversationTest::testSetMessageDisplayed() [&](auto infos) { // Last read for alice is when bob is added to the members return infos["uri"] == aliceUri - && infos["lastDisplayed"] == aliceLastMsg; + && infos["lastDisplayed"] == aliceData.messages[0].id; }) != membersInfos.end()); - + bobData.sent = false; aliceAccount->setMessageDisplayed("swarm:" + convId, convId, 3); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return msgDisplayed; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.sent; })); // Now, the last displayed message should be updated in member's infos (both sides) membersInfos = libjami::getConversationMembers(bobId, convId); @@ -1136,170 +1070,65 @@ ConversationTest::testSetMessageDisplayed() && infos["lastDisplayed"] == convId; }) != membersInfos.end()); - - libjami::unregisterSignalHandlers(); } void ConversationTest::testSetMessageDisplayedTwice() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - msgDisplayed = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId) { - if (message["type"] == "member") - memberMessageGenerated = true; - cv.notify_one(); - } - })); - std::string aliceLastMsg; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::AccountMessageStatusChanged>( - [&](const std::string& accountId, - const std::string& conversationId, - const std::string& peer, - const std::string& msgId, - int status) { - if (conversationId == convId && peer == aliceUri && status == 3) { - if (accountId == bobId && msgId == conversationId) - msgDisplayed = true; - else if (accountId == aliceId) - aliceLastMsg = msgId; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - aliceLastMsg = ""; + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && !aliceLastMsg.empty(); })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); // Check created files auto bobInvited = repoPath / "invited" / bobUri; CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobInvited)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); + bobData.sent = false; aliceAccount->setMessageDisplayed("swarm:" + convId, convId, 3); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return msgDisplayed; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.sent; })); - msgDisplayed = false; + bobData.sent = false; aliceAccount->setMessageDisplayed("swarm:" + convId, convId, 3); - CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return msgDisplayed; })); - - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return bobData.sent; })); } void ConversationTest::testSetMessageDisplayedPreference() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - msgDisplayed = false, aliceRegistered = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - std::string aliceLastMsg; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::AccountMessageStatusChanged>( - [&](const std::string& accountId, - const std::string& conversationId, - const std::string& peer, - const std::string& msgId, - int status) { - if (conversationId == convId && peer == aliceUri && status == 3) { - if (accountId == bobId && msgId == conversationId) - msgDisplayed = true; - else if (accountId == aliceId) - aliceLastMsg = msgId; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = aliceAccount->getVolatileAccountDetails(); - auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; - if (daemonStatus == "REGISTERED") { - aliceRegistered = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); auto details = aliceAccount->getAccountDetails(); CPPUNIT_ASSERT(details[ConfProperties::SENDREADRECEIPT] == "true"); details[ConfProperties::SENDREADRECEIPT] = "false"; libjami::setAccountDetails(aliceId, details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceRegistered; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.registered; })); - aliceLastMsg = ""; + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return requestReceived && memberMessageGenerated && !aliceLastMsg.empty(); - })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size(); })); + libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); // Last displayed messages should not be set yet auto membersInfos = libjami::getConversationMembers(aliceId, convId); @@ -1308,13 +1137,13 @@ ConversationTest::testSetMessageDisplayedPreference() [&](auto infos) { // Last read for alice is when bob is added to the members return infos["uri"] == aliceUri - && infos["lastDisplayed"] == aliceLastMsg; + && infos["lastDisplayed"] == aliceData.messages[0].id; }) != membersInfos.end()); aliceAccount->setMessageDisplayed("swarm:" + convId, convId, 3); // Bob should not receive anything here, as sendMessageDisplayed is disabled for Alice - CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return msgDisplayed; })); + CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return bobData.sent; })); // Assert that message is set as displayed for self (for the read status) membersInfos = libjami::getConversationMembers(aliceId, convId); @@ -1325,85 +1154,25 @@ ConversationTest::testSetMessageDisplayedPreference() && infos["lastDisplayed"] == convId; }) != membersInfos.end()); - libjami::unregisterSignalHandlers(); } void ConversationTest::testSetMessageDisplayedAfterClone() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool requestReceived = false, memberMessageGenerated = false, msgDisplayed = false, - aliceRegistered = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - bool conversationReady = false, conversationAlice2Ready = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - } else if (accountId == alice2Id) { - conversationAlice2Ready = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - std::string aliceLastMsg; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::AccountMessageStatusChanged>( - [&](const std::string& accountId, - const std::string& conversationId, - const std::string& peer, - const std::string& msgId, - int status) { - if (conversationId == convId && peer == aliceUri && status == 3) { - if (accountId == bobId && msgId == conversationId) - msgDisplayed = true; - else if (accountId == aliceId) - aliceLastMsg = msgId; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = aliceAccount->getVolatileAccountDetails(); - auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; - if (daemonStatus == "REGISTERED") { - aliceRegistered = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - aliceLastMsg = ""; + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return requestReceived && memberMessageGenerated && !aliceLastMsg.empty(); - })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size(); })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); aliceAccount->setMessageDisplayed("swarm:" + convId, convId, 3); @@ -1422,7 +1191,7 @@ ConversationTest::testSetMessageDisplayedAfterClone() alice2Id = Manager::instance().addAccount(details); // Disconnect alice2, to create a valid conv betwen Alice and alice1 - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationAlice2Ready; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !alice2Data.conversationId.empty(); })); // Assert that message is set as displayed for self (for the read status) auto membersInfos = libjami::getConversationMembers(aliceId, convId); @@ -1433,8 +1202,6 @@ ConversationTest::testSetMessageDisplayedAfterClone() && infos["lastDisplayed"] == convId; }) != membersInfos.end()); - - libjami::unregisterSignalHandlers(); } void @@ -1510,8 +1277,6 @@ std::string ConversationTest::createFakeConversation(std::shared_ptr<JamiAccount> account, const std::string& fakeCert) { - std::cout << "\nRunning test: " << __func__ << std::endl; - auto repoPath = fileutils::get_data_dir() / account->getAccountID() / "conversations" / "tmp"; @@ -1678,6 +1443,7 @@ void ConversationTest::testVoteNonEmpty() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); @@ -1688,158 +1454,53 @@ ConversationTest::testVoteNonEmpty() auto carlaUri = carlaAccount->getUsername(); aliceAccount->trackBuddyPresence(carlaUri, true); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - voteMessageGenerated = false, messageBobReceived = false, errorDetected = false, - carlaConnected = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = carlaAccount->getVolatileAccountDetails(); - auto deviceAnnounced - = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; - if (deviceAnnounced == "true") { - carlaConnected = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") { - voteMessageGenerated = true; - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "member") { - memberMessageGenerated = true; - } else if (accountId == bobId && conversationId == convId) { - messageBobReceived = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaData.deviceAnnounced; })); + + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size(); })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); - requestReceived = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); + + aliceMsgSize = aliceData.messages.size(); + auto bobMsgSize = bobData.messages.size(); libjami::addConversationMember(aliceId, convId, carlaUri); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; - messageBobReceived = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size() && bobMsgSize + 1 == bobData.messages.size(); })); libjami::acceptConversationRequest(carlaId, convId); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && messageBobReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size() && bobMsgSize + 2 == bobData.messages.size(); })); // Now Alice removes Carla with a non empty file - errorDetected = false; addVote(aliceAccount, convId, carlaUri, "CONTENT"); simulateRemoval(aliceAccount, convId, carlaUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaData.errorDetected; })); } void ConversationTest::testNoBadFileInInitialCommit() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); - auto carlaUri = carlaAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); auto convId = createFakeConversation(carlaAccount); - - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool carlaConnected = false; - bool errorDetected = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = carlaAccount->getVolatileAccountDetails(); - auto deviceAnnounced - = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; - if (deviceAnnounced == "true") { - carlaConnected = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaData.deviceAnnounced; })); libjami::addConversationMember(carlaId, convId, aliceUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.requestReceived; })); libjami::acceptConversationRequest(aliceId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.errorDetected; })); } void ConversationTest::testNoBadCertInInitialCommit() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); @@ -1854,141 +1515,47 @@ ConversationTest::testNoBadCertInInitialCommit() // Create a conversation from Carla with Alice's device auto convId = createFakeConversation(carlaAccount, fakeCert->toString(false)); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool carlaConnected = false; - bool errorDetected = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = carlaAccount->getVolatileAccountDetails(); - auto deviceAnnounced - = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; - if (deviceAnnounced == "true") { - carlaConnected = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaData.deviceAnnounced; })); libjami::addConversationMember(carlaId, convId, aliceUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.requestReceived; })); libjami::acceptConversationRequest(aliceId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.errorDetected; })); } void ConversationTest::testPlainTextNoBadFile() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0; - bool memberMessageGenerated = false; - bool requestReceived = false; - bool conversationReady = false; - bool errorDetected = false; std::string convId = libjami::startConversation(aliceId); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == bobId) { - messageBobReceived += 1; - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "member") { - memberMessageGenerated = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; - + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size(); })); libjami::acceptConversationRequest(bobId, convId); - cv.wait_for(lk, 30s, [&] { return conversationReady && memberMessageGenerated; }); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); addFile(aliceAccount, convId, "BADFILE"); Json::Value root; root["type"] = "text/plain"; root["body"] = "hi"; commit(aliceAccount, convId, root); - errorDetected = false; libjami::sendMessage(aliceId, convId, "hi"s, ""); // Check not received due to the unwanted file - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; })); } void ConversationTest::testVoteNoBadFile() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); @@ -1999,146 +1566,47 @@ ConversationTest::testVoteNoBadFile() auto carlaUri = carlaAccount->getUsername(); aliceAccount->trackBuddyPresence(carlaUri, true); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - voteMessageGenerated = false, messageBobReceived = false, messageCarlaReceived = false, - carlaConnected = false; - ; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = carlaAccount->getVolatileAccountDetails(); - auto deviceAnnounced - = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; - if (deviceAnnounced == "true") { - carlaConnected = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - std::string body = ""; - auto it = message.find("body"); - if (it != message.end()) { - body = it->second; - } - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") { - voteMessageGenerated = true; - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "member") { - memberMessageGenerated = true; - } else if (accountId == bobId && conversationId == convId) { - messageBobReceived = true; - } else if (accountId == carlaId && conversationId == convId && body == "final") { - messageCarlaReceived = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaData.deviceAnnounced; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size(); })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); - requestReceived = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); + + aliceMsgSize = aliceData.messages.size(); + auto bobMsgSize = bobData.messages.size(); libjami::addConversationMember(aliceId, convId, carlaUri); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; - messageBobReceived = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaData.requestReceived && aliceMsgSize + 1 == aliceData.messages.size() && bobMsgSize + 1 == bobData.messages.size(); })); libjami::acceptConversationRequest(carlaId, convId); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && messageBobReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size() && bobMsgSize + 2 == bobData.messages.size(); })); // Now Alice remove Carla without a vote. Bob will not receive the message - messageBobReceived = false; addFile(aliceAccount, convId, "BADFILE"); + aliceMsgSize = aliceData.messages.size(); libjami::removeConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); - messageCarlaReceived = false; + auto carlaMsgSize = carlaData.messages.size(); libjami::sendMessage(bobId, convId, "final"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageCarlaReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaMsgSize + 1 == carlaData.messages.size(); })); } void ConversationTest::testETooBigClone() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - bool errorDetected = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - auto convId = libjami::startConversation(aliceId); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; std::ofstream bad(repoPath / "BADFILE"); CPPUNIT_ASSERT(bad.is_open()); @@ -2147,84 +1615,38 @@ ConversationTest::testETooBigClone() bad.close(); addAll(aliceAccount, convId); - libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - - errorDetected = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; })); } void ConversationTest::testETooBigFetch() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - bool errorDetected = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - auto convId = libjami::startConversation(aliceId); + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - cv.wait_for(lk, 30s, [&]() { return requestReceived; }); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - cv.wait_for(lk, 30s, [&]() { return conversationReady; }); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); })); // Wait that alice sees Bob - cv.wait_for(lk, 30s, [&]() { return messageAliceReceived == 2; }); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; std::ofstream bad(repoPath / "BADFILE"); CPPUNIT_ASSERT(bad.is_open()); - errorDetected = false; for (int i = 0; i < 300 * 1024 * 1024; ++i) bad << "A"; bad.close(); @@ -2236,14 +1658,14 @@ ConversationTest::testETooBigFetch() commit(aliceAccount, convId, json); libjami::sendMessage(aliceId, convId, "hi"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; })); } void ConversationTest::testUnknownModeDetected() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); @@ -2257,405 +1679,144 @@ ConversationTest::testUnknownModeDetected() wbuilder["commentStyle"] = "None"; wbuilder["indentation"] = ""; repo.amend(convId, Json::writeString(wbuilder, json)); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - errorDetected = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 2) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - errorDetected = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; })); } void ConversationTest::testUpdateProfile() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - bool requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - std::map<std::string, std::string> profileAlice, profileBob; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationProfileUpdated>( - [&](const auto& accountId, const auto& /* conversationId */, const auto& profile) { - if (accountId == aliceId) { - profileAlice = profile; - } else if (accountId == bobId) { - profileBob = profile; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - auto convId = libjami::startConversation(aliceId); - + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - - messageAliceReceived = 0; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && messageAliceReceived == 1; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size() && !bobData.conversationId.empty(); })); - messageBobReceived = 0; + auto bobMsgSize = bobData.messages.size(); aliceAccount->convModule()->updateConversationInfos(convId, {{"title", "My awesome swarm"}}); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return messageBobReceived == 1 && !profileAlice.empty() && !profileBob.empty(); + return bobMsgSize + 1 == bobData.messages.size() && !aliceData.profile.empty() && !bobData.profile.empty(); })); auto infos = libjami::conversationInfos(bobId, convId); // Verify that we have the same profile everywhere CPPUNIT_ASSERT(infos["title"] == "My awesome swarm"); - CPPUNIT_ASSERT(profileAlice["title"] == "My awesome swarm"); - CPPUNIT_ASSERT(profileBob["title"] == "My awesome swarm"); + CPPUNIT_ASSERT(aliceData.profile["title"] == "My awesome swarm"); + CPPUNIT_ASSERT(bobData.profile["title"] == "My awesome swarm"); CPPUNIT_ASSERT(infos["description"].empty()); - CPPUNIT_ASSERT(profileAlice["description"].empty()); - CPPUNIT_ASSERT(profileBob["description"].empty()); - - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(aliceData.profile["description"].empty()); + CPPUNIT_ASSERT(bobData.profile["description"].empty()); } void ConversationTest::testGetProfileRequest() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageAliceReceived = 0; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == aliceId) - messageAliceReceived += 1; - cv.notify_one(); - })); - bool requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - auto convId = libjami::startConversation(aliceId); - - messageAliceReceived = 0; + auto aliceMsgSize = aliceData.messages.size(); aliceAccount->convModule()->updateConversationInfos(convId, {{"title", "My awesome swarm"}}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageAliceReceived == 1; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); auto infos = libjami::conversationInfos(bobId, convId); CPPUNIT_ASSERT(infos["title"] == "My awesome swarm"); CPPUNIT_ASSERT(infos["description"].empty()); - - libjami::unregisterSignalHandlers(); } void ConversationTest::testCheckProfileInConversationRequest() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - auto convId = libjami::startConversation(aliceId); aliceAccount->convModule()->updateConversationInfos(convId, {{"title", "My awesome swarm"}}); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); auto requests = libjami::getConversationRequests(bobId); CPPUNIT_ASSERT(requests.size() == 1); CPPUNIT_ASSERT(requests.front()["title"] == "My awesome swarm"); - - libjami::unregisterSignalHandlers(); } void ConversationTest::testCheckProfileInTrustRequest() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false; - std::string convId = ""; std::string vcard = "BEGIN:VCARD\n\ VERSION:2.1\n\ FN:TITLE\n\ DESCRIPTION:DESC\n\ END:VCARD"; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& payload, - time_t /*received*/) { - auto pstr = std::string(payload.begin(), payload.begin() + payload.size()); - if (account_id == bobId - && std::string(payload.data(), payload.data() + payload.size()) == vcard) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); aliceAccount->addContact(bobUri); std::vector<uint8_t> payload(vcard.begin(), vcard.end()); aliceAccount->sendTrustRequest(bobUri, payload); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return !convId.empty() && requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return bobData.payloadTrustRequest == vcard; })); } void ConversationTest::testMemberCannotUpdateProfile() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - bool errorDetected = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& accountId, - const std::string& conversationId, - int code, - const std::string& /* what */) { - if (accountId == bobId && conversationId == convId && code == 4) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - - messageAliceReceived = 0; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && messageAliceReceived == 1; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size() && !bobData.conversationId.empty(); })); - messageBobReceived = 0; bobAccount->convModule()->updateConversationInfos(convId, {{"title", "My awesome swarm"}}); - CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return errorDetected; })); - - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return bobData.errorDetected; })); } void ConversationTest::testUpdateProfileWithBadFile() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto convId = libjami::startConversation(aliceId); - - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - bool errorDetected = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& accountId, - const std::string& conversationId, - int code, - const std::string& /* what */) { - if (accountId == bobId && conversationId == convId && code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + auto convId = libjami::startConversation(aliceId); + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - - messageAliceReceived = 0; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && messageAliceReceived == 1; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size() && !bobData.conversationId.empty(); })); // Update profile but with bad file addFile(aliceAccount, convId, "BADFILE"); @@ -2668,73 +1829,25 @@ END:VCARD"; Json::Value root; root["type"] = "application/update-profile"; commit(aliceAccount, convId, root); - errorDetected = false; libjami::sendMessage(aliceId, convId, "hi"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); - - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; })); } void ConversationTest::testFetchProfileUnauthorized() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto convId = libjami::startConversation(aliceId); - - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - bool errorDetected = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& accountId, - const std::string& conversationId, - int code, - const std::string& /* what */) { - if (accountId == aliceId && conversationId == convId && code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + auto convId = libjami::startConversation(aliceId); + auto aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - - messageAliceReceived = 0; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && messageAliceReceived == 1; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size() && !bobData.conversationId.empty(); })); // Fake realist profile update std::string vcard = "BEGIN:VCARD\n\ @@ -2746,61 +1859,36 @@ END:VCARD"; Json::Value root; root["type"] = "application/update-profile"; commit(bobAccount, convId, root); - errorDetected = false; libjami::sendMessage(bobId, convId, "hi"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); - - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.errorDetected; })); } void ConversationTest::testSyncingWhileAccepting() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); Manager::instance().sendRegister(aliceId, false); // This avoid to sync immediately CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); - auto convInfos = libjami::conversationInfos(bobId, convId); + auto convInfos = libjami::conversationInfos(bobId, aliceData.conversationId); CPPUNIT_ASSERT(convInfos["syncing"] == "true"); CPPUNIT_ASSERT(convInfos.find("created") != convInfos.end()); Manager::instance().sendRegister(aliceId, true); // This avoid to sync immediately - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); })); - convInfos = libjami::conversationInfos(bobId, convId); + convInfos = libjami::conversationInfos(bobId, bobData.conversationId); CPPUNIT_ASSERT(convInfos.find("syncing") == convInfos.end()); } @@ -2843,85 +1931,44 @@ void ConversationTest::testReplayConversation() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - conversationRemoved = false, messageReceived = false; - std::vector<std::string> bobMessages; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == aliceId) - conversationRemoved = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId) { - messageReceived = true; - if (message["type"] == "member") - memberMessageGenerated = true; - } else if (accountId == bobId && message["type"] == "text/plain") { - bobMessages.emplace_back(message["body"]); - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - requestReceived = false; + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { + return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); })); // removeContact aliceAccount->removeContact(bobUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationRemoved; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.removed; })); + std::this_thread::sleep_for(5s); // re-add - CPPUNIT_ASSERT(convId != ""); - auto oldConvId = convId; - convId = ""; + CPPUNIT_ASSERT(bobData.conversationId != ""); + auto oldConvId = bobData.conversationId; + aliceData.conversationId = ""; aliceAccount->addContact(bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !convId.empty(); })); - messageReceived = false; - libjami::sendMessage(aliceId, convId, "foo"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageReceived; })); - messageReceived = false; - libjami::sendMessage(aliceId, convId, "bar"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageReceived; })); - convId = ""; - bobMessages.clear(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); })); + aliceMsgSize = aliceData.messages.size(); + libjami::sendMessage(aliceId, aliceData.conversationId, "foo"s, ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); + libjami::sendMessage(aliceId, aliceData.conversationId, "bar"s, ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); + bobData.messages.clear(); aliceAccount->sendTrustRequest(bobUri, {}); // Should retrieve previous conversation CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return bobMessages.size() == 2 && bobMessages[0] == "foo" && bobMessages[1] == "bar"; + JAMI_ERROR("@@@ {}", bobData.messages.size()); + if (bobData.messages.size() > 0) + JAMI_ERROR("@@@ {}", bobData.messages[0].body["body"]); + return bobData.messages.size() == 2 && bobData.messages[0].body["body"] == "foo" && bobData.messages[1].body["body"] == "bar"; })); } @@ -2929,69 +1976,13 @@ void ConversationTest::testSyncWithoutPinnedCert() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - std::string convId = ""; - auto requestReceived = false, conversationReady = false, memberMessageGenerated = false, - aliceMessageReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId) { - if (message["type"] == "member") - memberMessageGenerated = true; - else if (message["type"] == "text/plain") - aliceMessageReceived = true; - } - cv.notify_one(); - })); - auto bob2Started = false, aliceStopped = false, bob2Stopped = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto bob2Account = Manager::instance().getAccount<JamiAccount>(bob2Id); - if (!bob2Account) - return; - auto details = bob2Account->getVolatileAccountDetails(); - auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; - if (daemonStatus == "REGISTERED") - bob2Started = true; - if (daemonStatus == "UNREGISTERED") - bob2Stopped = true; - details = aliceAccount->getVolatileAccountDetails(); - daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; - if (daemonStatus == "UNREGISTERED") - aliceStopped = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - // Bob creates a second device auto bobArchive = std::filesystem::current_path().string() + "/bob.gz"; std::remove(bobArchive.c_str()); @@ -3007,33 +1998,31 @@ ConversationTest::testSyncWithoutPinnedCert() bob2Id = Manager::instance().addAccount(details); // Disconnect bob2, to create a valid conv betwen Alice and Bob1 - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Started; })); - bob2Stopped = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.registered; })); Manager::instance().sendRegister(bob2Id, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Stopped; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.stopped; })); // Alice adds bob - requestReceived = false; aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { + return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); })); // Bob send a message - libjami::sendMessage(bobId, convId, "hi"s, ""); - cv.wait_for(lk, 30s, [&]() { return aliceMessageReceived; }); + libjami::sendMessage(bobId, bobData.conversationId, "hi"s, ""); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); }); // Alice off, bob2 On - conversationReady = false; - aliceStopped = false; Manager::instance().sendRegister(aliceId, false); - cv.wait_for(lk, 10s, [&]() { return aliceStopped; }); + cv.wait_for(lk, 10s, [&]() { return aliceData.stopped; }); Manager::instance().sendRegister(bob2Id, true); // Sync + validate - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bob2Data.conversationId.empty(); })); } void @@ -3065,6 +2054,7 @@ void ConversationTest::testRemoveReaddMultipleDevice() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); @@ -3086,67 +2076,6 @@ END:VCARD"; file.close(); } - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto requestReceived = false, requestReceivedBob2 = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - else if (accountId == bob2Id) - requestReceivedBob2 = true; - cv.notify_one(); - })); - std::string convId = ""; - auto conversationReadyBob = false, conversationReadyBob2 = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReadyBob = true; - } else if (accountId == bob2Id) { - conversationReadyBob2 = true; - } - cv.notify_one(); - })); - auto memberMessageGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId) { - if (message["type"] == "member") - memberMessageGenerated = true; - } - cv.notify_one(); - })); - auto bob2Started = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (accountId == bob2Id) { - auto daemonStatus = details.at( - libjami::Account::VolatileProperties::DEVICE_ANNOUNCED); - if (daemonStatus == "true") - bob2Started = true; - } - cv.notify_one(); - })); - auto conversationRmBob = false, conversationRmBob2 = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == bobId) - conversationRmBob = true; - else if (accountId == bob2Id) - conversationRmBob2 = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - // Bob creates a second device auto bobArchive = std::filesystem::current_path().string() + "/bob.gz"; std::remove(bobArchive.c_str()); @@ -3161,110 +2090,40 @@ END:VCARD"; details[ConfProperties::ARCHIVE_PATH] = bobArchive; bob2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Started; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.deviceAnnounced; })); // Alice adds bob - requestReceived = false, requestReceivedBob2 = false; aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived && requestReceivedBob2; })); - libjami::acceptConversationRequest(bobId, convId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && bob2Data.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); + libjami::acceptConversationRequest(bobId, aliceData.conversationId); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return conversationReadyBob && conversationReadyBob2 && memberMessageGenerated; + return !bobData.conversationId.empty() && !bob2Data.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); })); // Remove contact bobAccount->removeContact(aliceUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationRmBob && conversationRmBob2; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.removed && bob2Data.removed; })); // wait that connections are closed. - std::this_thread::sleep_for(10s); + std::this_thread::sleep_for(5s); - // Alice send a message. This will not trigger any request, because contact was removed. - requestReceived = false, requestReceivedBob2 = false; - libjami::sendMessage(aliceId, convId, "hi"s, ""); - CPPUNIT_ASSERT(!cv.wait_for(lk, 30s, [&]() { return requestReceived || requestReceivedBob2; })); + // Alice send a message + bobData.requestReceived = false; bob2Data.requestReceived = false; + libjami::sendMessage(aliceId, aliceData.conversationId, "hi"s, ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && bob2Data.requestReceived; })); } void ConversationTest::testCloneFromMultipleDevice() { - std::cout << "\nRunning test: " << __func__ << std::endl; - - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); - auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto requestReceived = false, requestReceivedBob2 = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - else if (accountId == bob2Id) - requestReceivedBob2 = true; - cv.notify_one(); - })); - std::string convId = ""; - auto conversationReadyBob = false, conversationReadyBob2 = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReadyBob = true; - } else if (accountId == bob2Id) { - conversationReadyBob2 = true; - } - cv.notify_one(); - })); - auto memberMessageGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId) { - if (message["type"] == "member") - memberMessageGenerated = true; - } - cv.notify_one(); - })); - auto bob2Started = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (accountId == bob2Id) { - auto daemonStatus = details.at( - libjami::Account::VolatileProperties::DEVICE_ANNOUNCED); - if (daemonStatus == "true") - bob2Started = true; - } - cv.notify_one(); - })); - auto conversationRmAlice = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == aliceId) - conversationRmAlice = true; - cv.notify_one(); - })); - auto errorDetected = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 1) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); + + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto bobUri = bobAccount->getUsername(); // Bob creates a second device auto bobArchive = std::filesystem::current_path().string() + "/bob.gz"; @@ -3280,227 +2139,116 @@ ConversationTest::testCloneFromMultipleDevice() details[ConfProperties::ARCHIVE_PATH] = bobArchive; bob2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Started; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.deviceAnnounced; })); // Alice adds bob - requestReceived = false, requestReceivedBob2 = false; aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived && requestReceivedBob2; })); - libjami::acceptConversationRequest(bobId, convId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && bob2Data.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); + libjami::acceptConversationRequest(bobId, aliceData.conversationId); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return conversationReadyBob && conversationReadyBob2 && memberMessageGenerated; + return !bobData.conversationId.empty() && !bob2Data.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); })); // Remove contact aliceAccount->removeContact(bobUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationRmAlice; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.removed; })); // wait that connections are closed. std::this_thread::sleep_for(10s); // Alice re-adds Bob - auto oldConv = convId; - conversationRmAlice = false; + auto oldConv = bobData.conversationId; aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); // This should retrieve the conversation from Bob and don't show any error - CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return errorDetected; })); - CPPUNIT_ASSERT(conversationRmAlice); - CPPUNIT_ASSERT(oldConv == convId); // Check that convId didn't change and conversation is ready. + CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return aliceData.errorDetected; })); + CPPUNIT_ASSERT(oldConv == aliceData.conversationId); // Check that convId didn't change and conversation is ready. } void ConversationTest::testSendReply() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - std::vector<std::map<std::string, std::string>> messageBobReceived = {}, - messageAliceReceived = {}; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> message) { - if (accountId == bobId) { - messageBobReceived.emplace_back(message); - } else { - messageAliceReceived.emplace_back(message); - } - cv.notify_one(); - })); - bool requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - auto convId = libjami::startConversation(aliceId); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); - - // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / bobAccount->getAccountID() - / "conversations" / convId; - CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); - // Wait that alice sees Bob - cv.wait_for(lk, 30s, [&]() { return messageAliceReceived.size() == 2; }); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); })); - messageBobReceived.clear(); + auto bobMsgSize = bobData.messages.size(); libjami::sendMessage(aliceId, convId, "hi"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageBobReceived.size() == 1; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.messages.size() == bobMsgSize + 1; })); - auto validId = messageBobReceived.at(0).at("id"); + auto validId = bobData.messages.at(0).id; libjami::sendMessage(aliceId, convId, "foo"s, validId); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return messageBobReceived.size() == 2; })); - CPPUNIT_ASSERT(messageBobReceived.rbegin()->at("reply-to") == validId); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return bobData.messages.size() == bobMsgSize + 2; })); + CPPUNIT_ASSERT(bobData.messages.rbegin()->body.at("reply-to") == validId); // Check if parent doesn't exists, no message is generated libjami::sendMessage(aliceId, convId, "foo"s, "invalid"); - CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return messageBobReceived.size() == 3; })); + CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return bobData.messages.size() == bobMsgSize + 3; })); } void ConversationTest::testSearchInConv() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - messageReceived = false; - std::vector<std::string> bobMessages; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId) { - if (message["type"] == "member") - memberMessageGenerated = true; - } else if (accountId == bobId) { - messageReceived = true; - } - cv.notify_one(); - })); - std::vector<std::map<std::string, std::string>> messages; - bool finished = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessagesFound>( - [&](uint32_t, - const std::string&, - const std::string& conversationId, - std::vector<std::map<std::string, std::string>> msg) { - if (conversationId == convId) - messages = msg; - finished = conversationId.empty(); - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - requestReceived = false; + + auto aliceMsgSize = aliceData.messages.size(); aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); })); // Add some messages - messageReceived = false; - libjami::sendMessage(aliceId, convId, "message 1"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageReceived; })); - messageReceived = false; - libjami::sendMessage(aliceId, convId, "message 2"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageReceived; })); - messageReceived = false; - libjami::sendMessage(aliceId, convId, "Message 3"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageReceived; })); - - libjami::searchConversation(aliceId, convId, "", "", "message", "", 0, 0, 0, 0); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messages.size() == 3 && finished; })); - messages.clear(); - finished = false; - libjami::searchConversation(aliceId, convId, "", "", "Message", "", 0, 0, 0, 1); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messages.size() == 1 && finished; })); - messages.clear(); - finished = false; - libjami::searchConversation(aliceId, convId, "", "", "message 2", "", 0, 0, 0, 0); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messages.size() == 1 && finished; })); - messages.clear(); - finished = false; - libjami::searchConversation(aliceId, convId, "", "", "foo", "", 0, 0, 0, 0); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messages.size() == 0 && finished; })); + auto bobMsgSize = bobData.messages.size(); + libjami::sendMessage(aliceId, aliceData.conversationId, "message 1"s, ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); })); + libjami::sendMessage(aliceId, aliceData.conversationId, "message 2"s, ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 2 == bobData.messages.size(); })); + libjami::sendMessage(aliceId, aliceData.conversationId, "Message 3"s, ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 3 == bobData.messages.size(); })); + + libjami::searchConversation(aliceId, aliceData.conversationId, "", "", "message", "", 0, 0, 0, 0); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.messagesFound.size() == 3 && aliceData.searchFinished; })); + aliceData.messagesFound.clear(); + aliceData.searchFinished = false; + libjami::searchConversation(aliceId, aliceData.conversationId, "", "", "Message", "", 0, 0, 0, 1); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.messagesFound.size() == 1 && aliceData.searchFinished; })); + aliceData.messagesFound.clear(); + aliceData.searchFinished = false; + libjami::searchConversation(aliceId, aliceData.conversationId, "", "", "message 2", "", 0, 0, 0, 0); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.messagesFound.size() == 1 && aliceData.searchFinished; })); + aliceData.messagesFound.clear(); + aliceData.searchFinished = false; + libjami::searchConversation(aliceId, aliceData.conversationId, "", "", "foo", "", 0, 0, 0, 0); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.messagesFound.size() == 0 && aliceData.searchFinished; })); } void ConversationTest::testConversationPreferences() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); - auto uri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, conversationRemoved = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == aliceId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == aliceId) - conversationRemoved = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); // Start conversation and set preferences auto convId = libjami::startConversation(aliceId); - cv.wait_for(lk, 30s, [&]() { return conversationReady; }); + cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); }); CPPUNIT_ASSERT(libjami::getConversationPreferences(aliceId, convId).size() == 0); libjami::setConversationPreferences(aliceId, convId, {{"foo", "bar"}}); auto preferences = libjami::getConversationPreferences(aliceId, convId); @@ -3514,7 +2262,7 @@ ConversationTest::testConversationPreferences() CPPUNIT_ASSERT(preferences["bar"] == "foo"); // Remove conversations removes its preferences. CPPUNIT_ASSERT(libjami::removeConversation(aliceId, convId)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationRemoved; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.removed; })); CPPUNIT_ASSERT(libjami::getConversationPreferences(aliceId, convId).size() == 0); } @@ -3522,79 +2270,27 @@ void ConversationTest::testConversationPreferencesBeforeClone() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto requestReceived = false, requestReceivedBob2 = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - else if (accountId == bob2Id) - requestReceivedBob2 = true; - cv.notify_one(); - })); - std::string convId = ""; - auto conversationReadyBob = false, conversationReadyBob2 = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReadyBob = true; - } else if (accountId == bob2Id) { - conversationReadyBob2 = true; - } - cv.notify_one(); - })); - auto bob2Started = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (accountId == bob2Id) { - auto daemonStatus = details.at( - libjami::Account::VolatileProperties::DEVICE_ANNOUNCED); - if (daemonStatus == "true") - bob2Started = true; - } - cv.notify_one(); - })); - std::map<std::string, std::string> preferencesBob, preferencesBob2; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationPreferencesUpdated>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> preferences) { - if (accountId == bobId) - preferencesBob = preferences; - else if (accountId == bob2Id) - preferencesBob2 = preferences; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); // Bob creates a second device auto bobArchive = std::filesystem::current_path().string() + "/bob.gz"; std::remove(bobArchive.c_str()); bobAccount->exportArchive(bobArchive); // Alice adds bob - requestReceived = false; aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReadyBob; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + libjami::acceptConversationRequest(bobId, aliceData.conversationId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); })); // Set preferences Manager::instance().sendRegister(aliceId, false); - libjami::setConversationPreferences(bobId, convId, {{"foo", "bar"}, {"bar", "foo"}}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return preferencesBob.size() == 2; })); - CPPUNIT_ASSERT(preferencesBob["foo"] == "bar" && preferencesBob["bar"] == "foo"); + libjami::setConversationPreferences(bobId, bobData.conversationId, {{"foo", "bar"}, {"bar", "foo"}}); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.preferences.size() == 2; })); + CPPUNIT_ASSERT(bobData.preferences["foo"] == "bar" && bobData.preferences["bar"] == "foo"); // Bob2 should sync preferences std::map<std::string, std::string> details = libjami::getAccountTemplate("RING"); @@ -3607,71 +2303,20 @@ ConversationTest::testConversationPreferencesBeforeClone() details[ConfProperties::ARCHIVE_PATH] = bobArchive; bob2Id = Manager::instance().addAccount(details); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return bob2Started && conversationReadyBob2 && !preferencesBob2.empty(); + return bob2Data.deviceAnnounced && !bob2Data.conversationId.empty() && !bob2Data.preferences.empty(); })); - CPPUNIT_ASSERT(preferencesBob2["foo"] == "bar" && preferencesBob2["bar"] == "foo"); + CPPUNIT_ASSERT(bob2Data.preferences["foo"] == "bar" && bob2Data.preferences["bar"] == "foo"); } void ConversationTest::testConversationPreferencesMultiDevices() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto requestReceived = false, requestReceivedBob2 = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - else if (accountId == bob2Id) - requestReceivedBob2 = true; - cv.notify_one(); - })); - std::string convId = ""; - auto conversationReadyBob = false, conversationReadyBob2 = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReadyBob = true; - } else if (accountId == bob2Id) { - conversationReadyBob2 = true; - } - cv.notify_one(); - })); - auto bob2Started = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (accountId == bob2Id) { - auto daemonStatus = details.at( - libjami::Account::VolatileProperties::DEVICE_ANNOUNCED); - if (daemonStatus == "true") - bob2Started = true; - } - cv.notify_one(); - })); - std::map<std::string, std::string> preferencesBob, preferencesBob2; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationPreferencesUpdated>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> preferences) { - if (accountId == bobId) - preferencesBob = preferences; - else if (accountId == bob2Id) - preferencesBob2 = preferences; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); // Bob creates a second device auto bobArchive = std::filesystem::current_path().string() + "/bob.gz"; std::remove(bobArchive.c_str()); @@ -3685,50 +2330,39 @@ ConversationTest::testConversationPreferencesMultiDevices() details[ConfProperties::ARCHIVE_PIN] = ""; details[ConfProperties::ARCHIVE_PATH] = bobArchive; bob2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Started; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.deviceAnnounced; })); // Alice adds bob - requestReceived = false; aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived && requestReceivedBob2; })); - libjami::acceptConversationRequest(bobId, convId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && bob2Data.requestReceived; })); + libjami::acceptConversationRequest(bobId, aliceData.conversationId); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReadyBob && conversationReadyBob2; })); - libjami::setConversationPreferences(bobId, convId, {{"foo", "bar"}, {"bar", "foo"}}); + cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty() && !bob2Data.conversationId.empty(); })); + libjami::setConversationPreferences(bobId, bobData.conversationId, {{"foo", "bar"}, {"bar", "foo"}}); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return preferencesBob.size() == 2 && preferencesBob2.size() == 2; + return bobData.preferences.size() == 2 && bob2Data.preferences.size() == 2; })); - CPPUNIT_ASSERT(preferencesBob["foo"] == "bar" && preferencesBob["bar"] == "foo"); - CPPUNIT_ASSERT(preferencesBob2["foo"] == "bar" && preferencesBob2["bar"] == "foo"); + CPPUNIT_ASSERT(bobData.preferences["foo"] == "bar" && bobData.preferences["bar"] == "foo"); + CPPUNIT_ASSERT(bob2Data.preferences["foo"] == "bar" && bob2Data.preferences["bar"] == "foo"); } void ConversationTest::testFixContactDetails() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - std::string convId = ""; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - aliceAccount->addContact(bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return !convId.empty(); })); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return !aliceData.conversationId.empty(); })); auto details = aliceAccount->getContactDetails(bobUri); - CPPUNIT_ASSERT(details["conversationId"] == convId); + CPPUNIT_ASSERT(details["conversationId"] == aliceData.conversationId); // Erase convId from contact details, this should be fixed by next reload. - CPPUNIT_ASSERT(aliceAccount->updateConvForContact(bobUri, convId, "")); + CPPUNIT_ASSERT(aliceAccount->updateConvForContact(bobUri, aliceData.conversationId, "")); details = aliceAccount->getContactDetails(bobUri); CPPUNIT_ASSERT(details["conversationId"].empty()); @@ -3737,136 +2371,77 @@ ConversationTest::testFixContactDetails() std::this_thread::sleep_for(5s); // Let the daemon fix the structures details = aliceAccount->getContactDetails(bobUri); - CPPUNIT_ASSERT(details["conversationId"] == convId); + CPPUNIT_ASSERT(details["conversationId"] == aliceData.conversationId); } void ConversationTest::testRemoveOneToOneNotInDetails() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - std::string convId = "", secondConv; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - if (convId.empty()) - convId = conversationId; - else - secondConv = conversationId; - } - cv.notify_one(); - })); - bool conversationRemoved = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string& cid) { - if (accountId == aliceId && cid == secondConv) - conversationRemoved = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - aliceAccount->addContact(bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return !convId.empty(); })); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return !aliceData.conversationId.empty(); })); auto details = aliceAccount->getContactDetails(bobUri); - CPPUNIT_ASSERT(details["conversationId"] == convId); + CPPUNIT_ASSERT(details["conversationId"] == aliceData.conversationId); + auto firstConv = aliceData.conversationId; // Create a duplicate std::this_thread::sleep_for(2s); // Avoid to get same id aliceAccount->convModule()->startConversation(ConversationMode::ONE_TO_ONE, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return !secondConv.empty(); })); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return firstConv != aliceData.conversationId; })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() - / "conversations" / secondConv; + auto repoPath = fileutils::get_data_dir() / aliceId + / "conversations" / aliceData.conversationId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); aliceAccount->convModule()->loadConversations(); // Check that conv is removed - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationRemoved; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.removed; })); } void ConversationTest::testMessageEdition() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - std::vector<std::map<std::string, std::string>> messageBobReceived; - bool conversationReady = false, memberMessageGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> message) { - if (accountId == bobId) { - messageBobReceived.emplace_back(message); - } else if (accountId == aliceId && message["type"] == "member") { - memberMessageGenerated = true; - } - cv.notify_one(); - })); - bool requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - auto errorDetected = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); auto convId = libjami::startConversation(aliceId); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); - auto msgSize = messageBobReceived.size(); + cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty() && aliceData.messages.size() == aliceMsgSize + 1; })); + + auto bobMsgSize = bobData.messages.size(); libjami::sendMessage(aliceId, convId, "hi"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageBobReceived.size() == msgSize + 1; })); - msgSize = messageBobReceived.size(); - auto editedId = messageBobReceived.rbegin()->at("id"); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.messages.size() == bobMsgSize + 1; })); + auto editedId = bobData.messages.rbegin()->id; + // Should trigger MessageUpdated + bobMsgSize = bobData.messagesUpdated.size(); libjami::sendMessage(aliceId, convId, "New body"s, editedId, 1); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return messageBobReceived.size() == msgSize + 1; })); - CPPUNIT_ASSERT(messageBobReceived.rbegin()->at("edit") == editedId); - CPPUNIT_ASSERT(messageBobReceived.rbegin()->at("body") == "New body"); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return bobData.messagesUpdated.size() == bobMsgSize + 1; })); + CPPUNIT_ASSERT(bobData.messagesUpdated.rbegin()->body.at("body") == "New body"); // Not an existing message - msgSize = messageBobReceived.size(); + bobMsgSize = bobData.messagesUpdated.size(); libjami::sendMessage(aliceId, convId, "New body"s, "invalidId", 1); CPPUNIT_ASSERT( - !cv.wait_for(lk, 10s, [&]() { return messageBobReceived.size() == msgSize + 1; })); + !cv.wait_for(lk, 10s, [&]() { return bobData.messagesUpdated.size() == bobMsgSize + 1; })); // Invalid author libjami::sendMessage(aliceId, convId, "New body"s, convId, 1); CPPUNIT_ASSERT( - !cv.wait_for(lk, 10s, [&]() { return messageBobReceived.size() == msgSize + 1; })); + !cv.wait_for(lk, 10s, [&]() { return bobData.messagesUpdated.size() == bobMsgSize + 1; })); // Add invalid edition Json::Value root; root["type"] = "application/edited-message"; @@ -3875,113 +2450,71 @@ ConversationTest::testMessageEdition() Json::StreamWriterBuilder wbuilder; wbuilder["commentStyle"] = "None"; wbuilder["indentation"] = ""; - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; auto message = Json::writeString(wbuilder, root); commitInRepo(repoPath, aliceAccount, message); - errorDetected = false; + bobData.errorDetected = false; libjami::sendMessage(aliceId, convId, "trigger"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; })); + } void ConversationTest::testMessageReaction() { std::cout << "\nRunning test: " << __func__ << std::endl; - - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); - auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - std::vector<std::map<std::string, std::string>> messageAliceReceived; - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> message) { - if (accountId == aliceId) - messageAliceReceived.emplace_back(message); - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == aliceId) - conversationReady = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + connectSignals(); auto convId = libjami::startConversation(aliceId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); - auto msgSize = messageAliceReceived.size(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); })); + auto msgSize = aliceData.messages.size(); libjami::sendMessage(aliceId, convId, "hi"s, ""); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return messageAliceReceived.size() == msgSize + 1; })); - msgSize = messageAliceReceived.size(); - - auto reactId = messageAliceReceived.rbegin()->at("id"); + cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == msgSize + 1; })); + msgSize = aliceData.messages.size(); + // Add reaction + auto reactId = aliceData.messages.rbegin()->id; libjami::sendMessage(aliceId, convId, "👋"s, reactId, 2); CPPUNIT_ASSERT( - cv.wait_for(lk, 10s, [&]() { return messageAliceReceived.size() == msgSize + 1; })); - CPPUNIT_ASSERT(messageAliceReceived.rbegin()->at("react-to") == reactId); - CPPUNIT_ASSERT(messageAliceReceived.rbegin()->at("body") == "👋"); + cv.wait_for(lk, 10s, [&]() { return aliceData.reactions.size() == 1; })); + CPPUNIT_ASSERT(aliceData.reactions.rbegin()->at("react-to") == reactId); + CPPUNIT_ASSERT(aliceData.reactions.rbegin()->at("body") == "👋"); + auto emojiId = aliceData.reactions.rbegin()->at("id"); + + // Remove reaction + libjami::sendMessage(aliceId, convId, ""s, emojiId, 1); + CPPUNIT_ASSERT( + cv.wait_for(lk, 10s, [&]() { return aliceData.reactionRemoved.size() == 1; })); + CPPUNIT_ASSERT(emojiId == aliceData.reactionRemoved[0]); } void ConversationTest::testLoadPartiallyRemovedConversation() { std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - std::string convId = ""; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } - cv.notify_one(); - })); - bool conversationRemoved = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == aliceId) - conversationRemoved = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); // Copy alice's conversation temporary - auto repoPathAlice = fileutils::get_data_dir() / aliceAccount->getAccountID() / "conversations" / convId; - std::filesystem::copy(repoPathAlice, fmt::format("./{}", convId), std::filesystem::copy_options::recursive); + auto repoPathAlice = fileutils::get_data_dir() / aliceId / "conversations" / aliceData.conversationId; + std::filesystem::copy(repoPathAlice, fmt::format("./{}", aliceData.conversationId), std::filesystem::copy_options::recursive); // removeContact aliceAccount->removeContact(bobUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationRemoved; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.removed; })); std::this_thread::sleep_for(10s); // Wait for connection to close and async tasks to finish // Copy back alice's conversation - std::filesystem::copy(fmt::format("./{}", convId), repoPathAlice, std::filesystem::copy_options::recursive); - std::filesystem::remove_all(fmt::format("./{}", convId)); + std::filesystem::copy(fmt::format("./{}", aliceData.conversationId), repoPathAlice, std::filesystem::copy_options::recursive); + std::filesystem::remove_all(fmt::format("./{}", aliceData.conversationId)); // Reloading conversation should remove directory CPPUNIT_ASSERT(std::filesystem::is_directory(repoPathAlice)); diff --git a/test/unitTest/conversation/conversationMembersEvent.cpp b/test/unitTest/conversation/conversationMembersEvent.cpp index e3730f103c..8efab0997e 100644 --- a/test/unitTest/conversation/conversationMembersEvent.cpp +++ b/test/unitTest/conversation/conversationMembersEvent.cpp @@ -44,6 +44,21 @@ using namespace libjami::Account; namespace jami { namespace test { +struct UserData { + std::string conversationId; + bool removed {false}; + bool requestReceived {false}; + bool requestRemoved {false}; + bool errorDetected {false}; + bool registered {false}; + bool stopped {false}; + bool deviceAnnounced {false}; + bool contactRemoved {false}; + std::string payloadTrustRequest; + std::vector<libjami::SwarmMessage> messages; + std::vector<libjami::SwarmMessage> messagesUpdated; +}; + class ConversationMembersEventTest : public CppUnit::TestFixture { public: @@ -90,15 +105,22 @@ public: void testBanHostWhileHosting(); void testRemoveContactTwice(); void testAddContactTwice(); + void testBanFromNewDevice(); std::string aliceId; + UserData aliceData; std::string bobId; + UserData bobData; std::string bob2Id; + UserData bob2Data; std::string carlaId; + UserData carlaData; std::mutex mtx; std::unique_lock<std::mutex> lk {mtx}; std::condition_variable cv; + void connectSignals(); + private: CPPUNIT_TEST_SUITE(ConversationMembersEventTest); CPPUNIT_TEST(testRemoveConversationNoMember); @@ -136,6 +158,7 @@ private: CPPUNIT_TEST(testBanHostWhileHosting); CPPUNIT_TEST(testRemoveContactTwice); CPPUNIT_TEST(testAddContactTwice); + CPPUNIT_TEST(testBanFromNewDevice); CPPUNIT_TEST_SUITE_END(); }; @@ -145,6 +168,8 @@ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ConversationMembersEventTest, void ConversationMembersEventTest::setUp() { + connectSignals(); + // Init daemon libjami::init( libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG)); @@ -156,6 +181,11 @@ ConversationMembersEventTest::setUp() bobId = actors["bob"]; carlaId = actors["carla"]; + aliceData = {}; + bobData = {}; + bob2Data = {}; + carlaData = {}; + Manager::instance().sendRegister(carlaId, false); wait_for_announcement_of({aliceId, bobId}); } @@ -163,6 +193,8 @@ ConversationMembersEventTest::setUp() void ConversationMembersEventTest::tearDown() { + connectSignals(); + auto bobArchive = std::filesystem::current_path().string() + "/bob.gz"; std::remove(bobArchive.c_str()); if (bob2Id.empty()) { @@ -172,6 +204,178 @@ ConversationMembersEventTest::tearDown() } } +void +ConversationMembersEventTest::connectSignals() +{ + std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; + confHandlers.insert( + libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( + [&](const std::string& accountId, const std::map<std::string, std::string>&) { + if (accountId == aliceId) { + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto details = aliceAccount->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + aliceData.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + aliceData.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + aliceData.deviceAnnounced = deviceAnnounced == "true"; + } else if (accountId == bobId) { + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto details = bobAccount->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + bobData.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + bobData.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + bobData.deviceAnnounced = deviceAnnounced == "true"; + } else if (accountId == bob2Id) { + auto bob2Account = Manager::instance().getAccount<JamiAccount>(bob2Id); + auto details = bob2Account->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + bob2Data.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + bob2Data.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + bob2Data.deviceAnnounced = deviceAnnounced == "true"; + } else if (accountId == carlaId) { + auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); + auto details = carlaAccount->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + carlaData.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + carlaData.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + carlaData.deviceAnnounced = deviceAnnounced == "true"; + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( + [&](const std::string& accountId, const std::string& conversationId) { + if (accountId == aliceId) { + aliceData.conversationId = conversationId; + } else if (accountId == bobId) { + bobData.conversationId = conversationId; + } else if (accountId == bob2Id) { + bob2Data.conversationId = conversationId; + } else if (accountId == carlaId) { + carlaData.conversationId = conversationId; + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( + [&](const std::string& account_id, + const std::string& /*from*/, + const std::string& /*conversationId*/, + const std::vector<uint8_t>& payload, + time_t /*received*/) { + auto payloadStr = std::string(payload.data(), payload.data() + payload.size()); + if (account_id == aliceId) + aliceData.payloadTrustRequest = payloadStr; + else if (account_id == bobId) + bobData.payloadTrustRequest = payloadStr; + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + std::map<std::string, std::string> /*metadatas*/) { + if (accountId == aliceId) { + aliceData.requestReceived = true; + } else if (accountId == bobId) { + bobData.requestReceived = true; + } else if (accountId == bob2Id) { + bob2Data.requestReceived = true; + } else if (accountId == carlaId) { + carlaData.requestReceived = true; + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestDeclined>( + [&](const std::string& accountId, const std::string&) { + if (accountId == bobId) { + bobData.requestRemoved = true; + } else if (accountId == bob2Id) { + bob2Data.requestRemoved = true; + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageReceived>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + libjami::SwarmMessage message) { + if (accountId == aliceId) { + aliceData.messages.emplace_back(message); + } else if (accountId == bobId) { + bobData.messages.emplace_back(message); + } else if (accountId == bob2Id) { + bob2Data.messages.emplace_back(message); + } else if (accountId == carlaId) { + carlaData.messages.emplace_back(message); + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageUpdated>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + libjami::SwarmMessage message) { + if (accountId == aliceId) { + aliceData.messagesUpdated.emplace_back(message); + } else if (accountId == bobId) { + bobData.messagesUpdated.emplace_back(message); + } else if (accountId == carlaId) { + carlaData.messagesUpdated.emplace_back(message); + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + int /*code*/, + const std::string& /* what */) { + if (accountId == aliceId) + aliceData.errorDetected = true; + else if (accountId == bobId) + bobData.errorDetected = true; + else if (accountId == carlaId) + carlaData.errorDetected = true; + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( + [&](const std::string& accountId, const std::string&) { + if (accountId == aliceId) + aliceData.removed = true; + else if (accountId == bobId) + bobData.removed = true; + else if (accountId == bob2Id) + bob2Data.removed = true; + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactRemoved>( + [&](const std::string& accountId, const std::string&, bool) { + if (accountId == bobId) { + bobData.contactRemoved = true; + } else if (accountId == bob2Id) { + bob2Data.contactRemoved = true; + } + cv.notify_one(); + })); + libjami::registerSignalHandlers(confHandlers); +} + void ConversationMembersEventTest::generateFakeInvite(std::shared_ptr<JamiAccount> account, const std::string& convId, @@ -221,28 +425,16 @@ ConversationMembersEventTest::generateFakeInvite(std::shared_ptr<JamiAccount> ac void ConversationMembersEventTest::testRemoveConversationNoMember() { - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); - auto uri = aliceAccount->getUsername(); - - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == aliceId) { - conversationReady = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); + connectSignals(); // Start conversation auto convId = libjami::startConversation(aliceId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !aliceData.conversationId.empty(); })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; - auto dataPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto dataPath = fileutils::get_data_dir() / aliceId / "conversation_data" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); CPPUNIT_ASSERT(std::filesystem::is_directory(dataPath)); @@ -260,73 +452,35 @@ ConversationMembersEventTest::testRemoveConversationNoMember() void ConversationMembersEventTest::testRemoveConversationWithMember() { - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + connectSignals(); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - bobSeeAliceRemoved = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - auto itFind = message.find("type"); - if (itFind == message.end()) - return; - if (accountId == aliceId && conversationId == convId && itFind->second == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } else if (accountId == bobId && conversationId == convId - && itFind->second == "member") { - bobSeeAliceRemoved = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); // Check created files auto bobInvitedFile = repoPath / "invited" / bobUri; CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobInvitedFile)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - memberMessageGenerated = false; + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); - auto clonedPath = fileutils::get_data_dir() / bobAccount->getAccountID() + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); + auto clonedPath = fileutils::get_data_dir() / bobId / "conversations" / convId; - CPPUNIT_ASSERT(std::filesystem::is_directory(clonedPath)); bobInvitedFile = clonedPath / "invited" / bobUri; CPPUNIT_ASSERT(!std::filesystem::is_regular_file(bobInvitedFile)); // Remove conversation from alice once member confirmed - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); - bobSeeAliceRemoved = false; + auto bobMsgSize = bobData.messages.size(); libjami::removeConversation(aliceId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobSeeAliceRemoved; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size() && bobData.messages.rbegin()->type == "member"; })); std::this_thread::sleep_for(3s); CPPUNIT_ASSERT(!std::filesystem::is_directory(repoPath)); } @@ -334,50 +488,24 @@ ConversationMembersEventTest::testRemoveConversationWithMember() void ConversationMembersEventTest::testAddMember() { - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + connectSignals(); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); + libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); // Check created files auto bobInvited = repoPath / "invited" / bobUri; CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobInvited)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); - auto clonedPath = fileutils::get_data_dir() / bobAccount->getAccountID() + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); })); + auto clonedPath = fileutils::get_data_dir() / bobId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(clonedPath)); bobInvited = clonedPath / "invited" / bobUri; @@ -389,38 +517,13 @@ ConversationMembersEventTest::testAddMember() void ConversationMembersEventTest::testMemberAddedNoBadFile() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, errorDetected = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& accountId, - const std::string& conversationId, - int code, - const std::string& /* what */) { - if (accountId == bobId && conversationId == convId && code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + addFile(aliceAccount, convId, "BADFILE"); // NOTE: Add certificate because no DHT lookup aliceAccount->certStore().pinCertificate(bobAccount->identity().second); @@ -430,51 +533,31 @@ ConversationMembersEventTest::testMemberAddedNoBadFile() std::string(bobAccount->currentDeviceId()), {{"application/invite+json", "{\"conversationId\":\"" + convId + "\"}"}}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - errorDetected = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; })); } void ConversationMembersEventTest::testAddOfflineMemberThenConnects() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); auto carlaUri = carlaAccount->getUsername(); aliceAccount->trackBuddyPresence(carlaUri, true); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == carlaId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - libjami::addConversationMember(aliceId, convId, carlaUri); Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaData.requestReceived; })); libjami::acceptConversationRequest(carlaId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); - auto clonedPath = fileutils::get_data_dir() / carlaAccount->getAccountID() + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !carlaData.conversationId.empty(); })); + auto clonedPath = fileutils::get_data_dir() / carlaId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(clonedPath)); - libjami::unregisterSignalHandlers(); } void @@ -521,48 +604,19 @@ ConversationMembersEventTest::testAddAcceptOfflineThenConnects() void ConversationMembersEventTest::testGetMembers() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageReceived = false; - bool requestReceived = false; - bool conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == aliceId) { - messageReceived = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); // Start a conversation and add member auto convId = libjami::startConversation(aliceId); - messageReceived = false; libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return messageReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return bobData.requestReceived; })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); @@ -573,74 +627,39 @@ ConversationMembersEventTest::testGetMembers() CPPUNIT_ASSERT(members[1]["uri"] == bobUri); CPPUNIT_ASSERT(members[1]["role"] == "invited"); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - messageReceived = false; + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - cv.wait_for(lk, 30s, [&]() { return conversationReady; }); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); })); members = libjami::getConversationMembers(bobId, convId); CPPUNIT_ASSERT(members.size() == 2); - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return messageReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); members = libjami::getConversationMembers(aliceId, convId); CPPUNIT_ASSERT(members.size() == 2); CPPUNIT_ASSERT(members[0]["uri"] == aliceAccount->getUsername()); CPPUNIT_ASSERT(members[0]["role"] == "admin"); CPPUNIT_ASSERT(members[1]["uri"] == bobUri); CPPUNIT_ASSERT(members[1]["role"] == "member"); - libjami::unregisterSignalHandlers(); } void ConversationMembersEventTest::testRemoveMember() { - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + connectSignals(); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - voteMessageGenerated = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") { - voteMessageGenerated = true; - cv.notify_one(); - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); + libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Now check that alice, has the only admin, can remove bob - memberMessageGenerated = false; - voteMessageGenerated = false; libjami::removeConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == aliceMsgSize + 3 /* vote + ban */; })); auto members = libjami::getConversationMembers(aliceId, convId); auto bobBanned = false; for (auto& member : members) { @@ -648,146 +667,65 @@ ConversationMembersEventTest::testRemoveMember() bobBanned = member["role"] == "banned"; } CPPUNIT_ASSERT(bobBanned); - libjami::unregisterSignalHandlers(); } void ConversationMembersEventTest::testRemovedMemberDoesNotReceiveMessage() { - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + connectSignals(); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - bool memberMessageGenerated = false, voteMessageGenerated = false, messageBobReceived = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") - voteMessageGenerated = true; - else if (accountId == aliceId && conversationId == convId && message["type"] == "member") - memberMessageGenerated = true; - else if (accountId == bobId && conversationId == convId) - messageBobReceived = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Now check that alice, has the only admin, can remove bob - memberMessageGenerated = false; - voteMessageGenerated = false; libjami::removeConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == aliceMsgSize + 3 /* vote + ban */; })); // Now, bob is banned so they shoud not receive any message - messageBobReceived = false; + auto bobMsgSize = bobData.messages.size(); libjami::sendMessage(aliceId, convId, "hi"s, ""); - CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return messageBobReceived; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); })); } void ConversationMembersEventTest::testRemoveInvitedMember() { - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + connectSignals(); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); - auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto carlaUri = carlaAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - bool carlaConnected = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = carlaAccount->getVolatileAccountDetails(); - auto deviceAnnounced - = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; - if (deviceAnnounced == "true") { - carlaConnected = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - bool voteMessageGenerated = false, memberMessageGenerated = false, carlaMessageReceived; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") - voteMessageGenerated = true; - else if (accountId == aliceId && conversationId == convId && message["type"] == "member") - memberMessageGenerated = true; - else if (accountId == carlaId && message["type"] == "text/plain" - && message["body"] == "hi") - carlaMessageReceived = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - // Add carla Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaData.deviceAnnounced; })); libjami::addConversationMember(aliceId, convId, carlaUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(carlaId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Invite Alice - requestReceived = false; libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); auto members = libjami::getConversationMembers(aliceId, convId); CPPUNIT_ASSERT(members.size() == 3); // Now check that alice, has the only admin, can remove bob - memberMessageGenerated = false; - voteMessageGenerated = false; + aliceMsgSize = aliceData.messages.size(); libjami::removeConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); members = libjami::getConversationMembers(aliceId, convId); auto bobBanned = false; for (auto& member : members) { @@ -797,14 +735,16 @@ ConversationMembersEventTest::testRemoveInvitedMember() CPPUNIT_ASSERT(bobBanned); // Check that Carla is still able to sync + auto carlaMsgSize = carlaData.messages.size(); libjami::sendMessage(aliceId, convId, "hi"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaMessageReceived; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return carlaMsgSize + 1 == carlaData.messages.size(); })); } void ConversationMembersEventTest::testMemberBanNoBadFile() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto aliceUri = aliceAccount->getUsername(); @@ -814,190 +754,69 @@ ConversationMembersEventTest::testMemberBanNoBadFile() auto carlaUri = carlaAccount->getUsername(); aliceAccount->trackBuddyPresence(carlaUri, true); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - voteMessageGenerated = false, messageBobReceived = false, errorDetected = false, - carlaConnected = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = carlaAccount->getVolatileAccountDetails(); - auto deviceAnnounced - = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; - if (deviceAnnounced == "true") { - carlaConnected = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") { - voteMessageGenerated = true; - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "member") { - memberMessageGenerated = true; - } else if (accountId == bobId && conversationId == convId) { - messageBobReceived = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& accountId, - const std::string& conversationId, - int code, - const std::string& /* what */) { - if (accountId == bobId && conversationId == convId && code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaData.deviceAnnounced; })); libjami::addConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; + cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); - requestReceived = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); libjami::addConversationMember(aliceId, convId, carlaUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; - messageBobReceived = false; + cv.wait_for(lk, 30s, [&]() { return carlaData.requestReceived; })); + aliceMsgSize = aliceData.messages.size(); + auto bobMsgSize = bobData.messages.size(); libjami::acceptConversationRequest(carlaId, convId); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && messageBobReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size() && bobMsgSize + 1 == bobData.messages.size(); })); - memberMessageGenerated = false; - voteMessageGenerated = false; addFile(aliceAccount, convId, "BADFILE"); libjami::removeConversationMember(aliceId, convId, carlaUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; })); } void ConversationMembersEventTest::testMemberTryToRemoveAdmin() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") - memberMessageGenerated = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); libjami::addConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; + cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Now check that alice, has the only admin, can remove bob - memberMessageGenerated = false; libjami::removeConversationMember(bobId, convId, aliceUri); auto members = libjami::getConversationMembers(aliceId, convId); - CPPUNIT_ASSERT(members.size() == 2 && !memberMessageGenerated); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(members.size() == 2 && aliceMsgSize + 2 != aliceData.messages.size()); } void ConversationMembersEventTest::testBannedMemberCannotSendMessage() { - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + connectSignals(); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - voteMessageGenerated = false, aliceMessageReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") { - voteMessageGenerated = true; - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "member") { - memberMessageGenerated = true; - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "text/plain") { - aliceMessageReceived = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + libjami::addConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; + cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); - memberMessageGenerated = false; - voteMessageGenerated = false; libjami::removeConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 3 == aliceData.messages.size(); })); auto members = libjami::getConversationMembers(aliceId, convId); auto bobBanned = false; @@ -1008,65 +827,33 @@ ConversationMembersEventTest::testBannedMemberCannotSendMessage() CPPUNIT_ASSERT(bobBanned); // Now check that alice doesn't receive a message from Bob - aliceMessageReceived = false; libjami::sendMessage(bobId, convId, "hi"s, ""); - CPPUNIT_ASSERT(!cv.wait_for(lk, 30s, [&]() { return aliceMessageReceived; })); + CPPUNIT_ASSERT(!cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 4 == aliceData.messages.size(); })); libjami::unregisterSignalHandlers(); } void ConversationMembersEventTest::testAdminCanReAddMember() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - voteMessageGenerated = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") { - voteMessageGenerated = true; - cv.notify_one(); - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); + libjami::addConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; + cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Now check that alice, has the only admin, can remove bob - memberMessageGenerated = false; - voteMessageGenerated = false; libjami::removeConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 3 == aliceData.messages.size(); })); auto members = libjami::getConversationMembers(aliceId, convId); @@ -1078,211 +865,96 @@ ConversationMembersEventTest::testAdminCanReAddMember() CPPUNIT_ASSERT(bobBanned); // Then check that bobUri can be re-added - memberMessageGenerated = false, voteMessageGenerated = false; + aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); members = libjami::getConversationMembers(aliceId, convId); CPPUNIT_ASSERT(members.size() == 2); - libjami::unregisterSignalHandlers(); } void ConversationMembersEventTest::testMemberCannotBanOther() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); auto carlaUri = carlaAccount->getUsername(); aliceAccount->trackBuddyPresence(carlaUri, true); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - voteMessageGenerated = false, messageBobReceived = false, errorDetected = false, - carlaConnected = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string&, const std::string&, std::map<std::string, std::string>) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = carlaAccount->getVolatileAccountDetails(); - auto deviceAnnounced - = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; - if (deviceAnnounced == "true") { - carlaConnected = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") { - voteMessageGenerated = true; - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "member") { - memberMessageGenerated = true; - } else if (accountId == bobId && conversationId == convId) { - messageBobReceived = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaData.deviceAnnounced; })); libjami::addConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; + cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); - requestReceived = false; - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); libjami::addConversationMember(aliceId, convId, carlaUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; - messageBobReceived = false; + cv.wait_for(lk, 30s, [&]() { return carlaData.requestReceived; })); + aliceMsgSize = aliceData.messages.size(); + auto bobMsgSize = bobData.messages.size(); libjami::acceptConversationRequest(carlaId, convId); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && messageBobReceived; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size() && bobMsgSize + 1 == bobData.messages.size(); })); // Now Carla remove Bob as a member - errorDetected = false; - messageBobReceived = false; // remove from member & add into banned without voting for the ban simulateRemoval(carlaAccount, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.errorDetected; })); + bobMsgSize = bobData.messages.size(); libjami::sendMessage(aliceId, convId, "hi"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageBobReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); })); } void ConversationMembersEventTest::testMemberCannotUnBanOther() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); auto carlaUri = carlaAccount->getUsername(); aliceAccount->trackBuddyPresence(carlaUri, true); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - voteMessageGenerated = false, messageBobReceived = false, errorDetected = false, - carlaConnected = false, messageCarlaReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string&, const std::string&, std::map<std::string, std::string>) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = carlaAccount->getVolatileAccountDetails(); - auto deviceAnnounced - = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; - if (deviceAnnounced == "true") { - carlaConnected = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") { - voteMessageGenerated = true; - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "member") { - memberMessageGenerated = true; - } else if (accountId == bobId && conversationId == convId) { - messageBobReceived = true; - } else if (accountId == carlaId && conversationId == convId) { - messageCarlaReceived = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaData.deviceAnnounced; })); libjami::addConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; + cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); - requestReceived = false; - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); libjami::addConversationMember(aliceId, convId, carlaUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; - messageBobReceived = false; + cv.wait_for(lk, 30s, [&]() { return carlaData.requestReceived; })); + aliceMsgSize = aliceData.messages.size(); + auto bobMsgSize = bobData.messages.size(); libjami::acceptConversationRequest(carlaId, convId); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && messageBobReceived; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size() && bobMsgSize + 1 == bobData.messages.size(); })); // Now check that alice, has the only admin, can remove bob - memberMessageGenerated = false; - messageCarlaReceived = false; - voteMessageGenerated = false; + auto carlaMsgSize = carlaData.messages.size(); libjami::removeConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return memberMessageGenerated && voteMessageGenerated && messageCarlaReceived; + return carlaMsgSize + 2 == carlaData.messages.size();; })); - memberMessageGenerated = false; - voteMessageGenerated = false; + aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(carlaId, convId, bobUri); CPPUNIT_ASSERT( - !cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + !cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size();; })); auto members = libjami::getConversationMembers(aliceId, convId); auto bobBanned = false; for (auto& member : members) { @@ -1295,134 +967,46 @@ ConversationMembersEventTest::testMemberCannotUnBanOther() void ConversationMembersEventTest::testCheckAdminFakeAVoteIsDetected() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); auto carlaUri = carlaAccount->getUsername(); aliceAccount->trackBuddyPresence(carlaUri, true); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - voteMessageGenerated = false, messageBobReceived = false, errorDetected = false, - carlaConnected = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") { - voteMessageGenerated = true; - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "member") { - memberMessageGenerated = true; - } else if (accountId == bobId && conversationId == convId) { - messageBobReceived = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = carlaAccount->getVolatileAccountDetails(); - auto deviceAnnounced - = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; - if (deviceAnnounced == "true") { - carlaConnected = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaData.deviceAnnounced; })); libjami::addConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; + cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); - requestReceived = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); libjami::addConversationMember(aliceId, convId, carlaUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestReceived && memberMessageGenerated; })); - memberMessageGenerated = false; - messageBobReceived = false; + cv.wait_for(lk, 30s, [&]() { return carlaData.requestReceived; })); + aliceMsgSize = aliceData.messages.size(); + auto bobMsgSize = bobData.messages.size(); libjami::acceptConversationRequest(carlaId, convId); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && messageBobReceived; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size() && bobMsgSize + 1 == bobData.messages.size(); })); // Now Alice remove Carla without a vote. Bob will not receive the message - errorDetected = false; simulateRemoval(aliceAccount, convId, carlaUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; })); } void ConversationMembersEventTest::testAdminCannotKickTheirself() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto aliceUri = aliceAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - voteMessageGenerated = false, aliceMessageReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") { - voteMessageGenerated = true; - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "member") { - memberMessageGenerated = true; - } else if (accountId == aliceId && conversationId == convId - && message["type"] == "text/plain") { - aliceMessageReceived = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); auto members = libjami::getConversationMembers(aliceId, convId); CPPUNIT_ASSERT(members.size() == 1); libjami::removeConversationMember(aliceId, convId, aliceUri); @@ -1433,68 +1017,26 @@ ConversationMembersEventTest::testAdminCannotKickTheirself() void ConversationMembersEventTest::testCommitUnauthorizedUser() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); auto bobUri = bobAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - bool errorDetected = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - auto convId = libjami::startConversation(aliceId); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size();; })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / bobAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / bobId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); - // Wait that alice sees Bob - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageAliceReceived == 2; })); // Add commit from invalid user Json::Value root; @@ -1506,73 +1048,33 @@ ConversationMembersEventTest::testCommitUnauthorizedUser() auto message = Json::writeString(wbuilder, root); commitInRepo(repoPath, carlaAccount, message); - errorDetected = false; libjami::sendMessage(bobId, convId, "hi"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.errorDetected; })); libjami::unregisterSignalHandlers(); } void ConversationMembersEventTest::testMemberJoinsNoBadFile() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); auto carlaUri = carlaAccount->getUsername(); aliceAccount->trackBuddyPresence(carlaUri, true); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, errorDetected = false, carlaConnected = false, - memberMessageGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == carlaId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = carlaAccount->getVolatileAccountDetails(); - auto deviceAnnounced - = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; - if (deviceAnnounced == "true") { - carlaConnected = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - + auto aliceMsgSize = aliceData.messages.size(); aliceAccount->convModule()->addConversationMember(convId, carlaUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Cp conversations & convInfo - auto repoPathAlice = fileutils::get_data_dir() / aliceAccount->getAccountID() / "conversations"; - auto repoPathCarla = fileutils::get_data_dir() / carlaAccount->getAccountID() / "conversations"; + auto repoPathAlice = fileutils::get_data_dir() / aliceId / "conversations"; + auto repoPathCarla = fileutils::get_data_dir() / carlaId / "conversations"; std::filesystem::copy(repoPathAlice, repoPathCarla, std::filesystem::copy_options::recursive); - auto ciPathAlice = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto ciPathAlice = fileutils::get_data_dir() / aliceId / "convInfo"; - auto ciPathCarla = fileutils::get_data_dir() / carlaAccount->getAccountID() + auto ciPathCarla = fileutils::get_data_dir() / carlaId / "convInfo"; std::remove(ciPathCarla.c_str()); std::filesystem::copy(ciPathAlice, ciPathCarla); @@ -1603,76 +1105,35 @@ ConversationMembersEventTest::testMemberJoinsNoBadFile() // Start Carla, should merge and all messages should be there carlaAccount->convModule()->loadConversations(); // Because of the copy Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaData.deviceAnnounced; })); - errorDetected = false; libjami::sendMessage(carlaId, convId, "hi"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return errorDetected; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return aliceData.errorDetected; })); } void ConversationMembersEventTest::testMemberAddedNoCertificate() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); auto carlaUri = carlaAccount->getUsername(); aliceAccount->trackBuddyPresence(carlaUri, true); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, errorDetected = false, carlaConnected = false, - memberMessageGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == carlaId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = carlaAccount->getVolatileAccountDetails(); - auto deviceAnnounced - = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; - if (deviceAnnounced == "true") { - carlaConnected = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - + auto aliceMsgSize = aliceData.messages.size(); aliceAccount->convModule()->addConversationMember(convId, carlaUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Cp conversations & convInfo - auto repoPathAlice = fileutils::get_data_dir() / aliceAccount->getAccountID() / "conversations"; - auto repoPathCarla = fileutils::get_data_dir() / carlaAccount->getAccountID() / "conversations"; + auto repoPathAlice = fileutils::get_data_dir() / aliceId / "conversations"; + auto repoPathCarla = fileutils::get_data_dir() / carlaId / "conversations"; std::filesystem::copy(repoPathAlice, repoPathCarla, std::filesystem::copy_options::recursive); - auto ciPathAlice = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto ciPathAlice = fileutils::get_data_dir() / aliceId / "convInfo"; - auto ciPathCarla = fileutils::get_data_dir() / carlaAccount->getAccountID() + auto ciPathCarla = fileutils::get_data_dir() / carlaId / "convInfo"; std::remove(ciPathCarla.c_str()); std::filesystem::copy(ciPathAlice, ciPathCarla); @@ -1694,75 +1155,34 @@ ConversationMembersEventTest::testMemberAddedNoCertificate() // Start Carla, should merge and all messages should be there carlaAccount->convModule()->loadConversations(); // Because of the copy Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaData.deviceAnnounced; })); libjami::sendMessage(carlaId, convId, "hi"s, ""); - errorDetected = false; - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return errorDetected; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return aliceData.errorDetected; })); } void ConversationMembersEventTest::testMemberJoinsInviteRemoved() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); auto carlaUri = carlaAccount->getUsername(); aliceAccount->trackBuddyPresence(carlaUri, true); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, errorDetected = false, carlaConnected = false, - memberMessageGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == carlaId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = carlaAccount->getVolatileAccountDetails(); - auto deviceAnnounced - = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; - if (deviceAnnounced == "true") { - carlaConnected = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - + auto aliceMsgSize = aliceData.messages.size(); aliceAccount->convModule()->addConversationMember(convId, carlaUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Cp conversations & convInfo - auto repoPathAlice = fileutils::get_data_dir() / aliceAccount->getAccountID() / "conversations"; - auto repoPathCarla = fileutils::get_data_dir() / carlaAccount->getAccountID() / "conversations"; + auto repoPathAlice = fileutils::get_data_dir() / aliceId / "conversations"; + auto repoPathCarla = fileutils::get_data_dir() / carlaId / "conversations"; std::filesystem::copy(repoPathAlice, repoPathCarla, std::filesystem::copy_options::recursive); - auto ciPathAlice = fileutils::get_data_dir() / aliceAccount->getAccountID() / "convInfo"; - auto ciPathCarla = fileutils::get_data_dir() / carlaAccount->getAccountID() / "convInfo"; + auto ciPathAlice = fileutils::get_data_dir() / aliceId / "convInfo"; + auto ciPathCarla = fileutils::get_data_dir() / carlaId / "convInfo"; std::remove(ciPathCarla.c_str()); std::filesystem::copy(ciPathAlice, ciPathCarla); @@ -1792,186 +1212,78 @@ ConversationMembersEventTest::testMemberJoinsInviteRemoved() // Start Carla, should merge and all messages should be there carlaAccount->convModule()->loadConversations(); // Because of the copy Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaData.deviceAnnounced; })); libjami::sendMessage(carlaId, convId, "hi"s, ""); - errorDetected = false; - - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return errorDetected; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return aliceData.errorDetected; })); } void ConversationMembersEventTest::testFailAddMemberInOneToOne() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); auto carlaUri = carlaAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return !convId.empty(); })); - memberMessageGenerated = false; - libjami::addConversationMember(aliceId, convId, carlaUri); - CPPUNIT_ASSERT(!cv.wait_for(lk, 5s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); + libjami::addConversationMember(aliceId, aliceData.conversationId, carlaUri); + CPPUNIT_ASSERT(!cv.wait_for(lk, 5s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); } void ConversationMembersEventTest::testOneToOneFetchWithNewMemberRefused() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); auto carlaUri = carlaAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - messageBob = false, errorDetected = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - } else if (accountId == bobId && conversationId == convId - && message["type"] == "member") { - messageBob = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - int code, - const std::string& /* what */) { - if (code == 3) - errorDetected = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !convId.empty() && requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); - memberMessageGenerated = false; CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size();; })); - errorDetected = false; // NOTE: Add certificate because no DHT lookup aliceAccount->certStore().pinCertificate(carlaAccount->identity().second); - generateFakeInvite(aliceAccount, convId, carlaUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return errorDetected; })); + generateFakeInvite(aliceAccount, aliceData.conversationId, carlaUri); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.errorDetected; })); } void ConversationMembersEventTest::testConversationMemberEvent() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberAddGenerated = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& /* conversationId */) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationMemberEvent>( - [&](const std::string& accountId, - const std::string& conversationId, - const std::string& uri, - int event) { - if (accountId == aliceId && conversationId == convId && uri == bobUri - && event == 0) { - memberAddGenerated = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberAddGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto repoPath = fileutils::get_data_dir() / aliceId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); // Check created files auto bobInvited = repoPath / "invited" / bobUri; CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobInvited)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); - auto clonedPath = fileutils::get_data_dir() / bobAccount->getAccountID() + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); })); + auto clonedPath = fileutils::get_data_dir() / bobId / "conversations" / convId; CPPUNIT_ASSERT(std::filesystem::is_directory(clonedPath)); bobInvited = clonedPath / "invited" / bobUri; @@ -1983,42 +1295,20 @@ ConversationMembersEventTest::testConversationMemberEvent() void ConversationMembersEventTest::testGetConversationsMembersWhileSyncing() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); Manager::instance().sendRegister(aliceId, false); // This avoid to sync immediately CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); - auto members = libjami::getConversationMembers(bobId, convId); + auto members = libjami::getConversationMembers(bobId, aliceData.conversationId); CPPUNIT_ASSERT(std::find_if(members.begin(), members.end(), [&](auto memberInfo) { return memberInfo["uri"] == aliceUri; }) @@ -2032,6 +1322,8 @@ ConversationMembersEventTest::testGetConversationsMembersWhileSyncing() void ConversationMembersEventTest::testGetConversationMembersWithSelfOneOne() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto aliceUri = aliceAccount->getUsername(); std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; @@ -2054,66 +1346,25 @@ ConversationMembersEventTest::testGetConversationMembersWithSelfOneOne() void ConversationMembersEventTest::testAvoidTwoOneToOne() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - cv.notify_one(); - })); - std::string convId = ""; - auto conversationReadyBob = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId && conversationId == convId) { - conversationReadyBob = true; - } - cv.notify_one(); - })); - auto memberMessageGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId) { - if (message["type"] == "member") - memberMessageGenerated = true; - } - cv.notify_one(); - })); - auto conversationRmBob = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == bobId) - conversationRmBob = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - // Alice adds bob - requestReceived = false; aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - libjami::acceptConversationRequest(bobId, convId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); + libjami::acceptConversationRequest(bobId, aliceData.conversationId); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReadyBob && memberMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == aliceMsgSize + 1; })); // Remove contact bobAccount->removeContact(aliceUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationRmBob; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.removed; })); // wait that connections are closed. std::this_thread::sleep_for(10s); @@ -2121,78 +1372,19 @@ ConversationMembersEventTest::testAvoidTwoOneToOne() // Bob add Alice, this should re-add old conversation bobAccount->addContact(aliceUri); bobAccount->sendTrustRequest(aliceUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReadyBob; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.conversationId == aliceData.conversationId; })); } void ConversationMembersEventTest::testAvoidTwoOneToOneMultiDevices() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto requestReceived = false, requestReceivedBob2 = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - else if (accountId == bob2Id) - requestReceivedBob2 = true; - cv.notify_one(); - })); - std::string convId = ""; - auto conversationReadyBob = false, conversationReadyBob2 = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId && conversationId == convId) { - conversationReadyBob = true; - } else if (accountId == bob2Id && conversationId == convId) { - conversationReadyBob2 = true; - } - cv.notify_one(); - })); - auto memberMessageGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId) { - if (message["type"] == "member") - memberMessageGenerated = true; - } - cv.notify_one(); - })); - auto bob2Started = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (accountId == bob2Id) { - auto daemonStatus = details.at( - libjami::Account::VolatileProperties::DEVICE_ANNOUNCED); - if (daemonStatus == "true") - bob2Started = true; - } - cv.notify_one(); - })); - auto conversationRmBob = false, conversationRmBob2 = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == bobId) - conversationRmBob = true; - else if (accountId == bob2Id) - conversationRmBob2 = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - // Bob creates a second device auto bobArchive = std::filesystem::current_path().string() + "/bob.gz"; std::remove(bobArchive.c_str()); @@ -2207,21 +1399,21 @@ ConversationMembersEventTest::testAvoidTwoOneToOneMultiDevices() details[ConfProperties::ARCHIVE_PATH] = bobArchive; bob2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Started; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.deviceAnnounced; })); // Alice adds bob - requestReceived = false; aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived && requestReceivedBob2; })); - libjami::acceptConversationRequest(bobId, convId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && bob2Data.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); + libjami::acceptConversationRequest(bobId, aliceData.conversationId); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return conversationReadyBob && conversationReadyBob2 && memberMessageGenerated; + return !bobData.conversationId.empty() && !bob2Data.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); })); // Remove contact bobAccount->removeContact(aliceUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationRmBob && conversationRmBob2; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.removed && bob2Data.removed; })); // wait that connections are closed. std::this_thread::sleep_for(10s); @@ -2230,52 +1422,19 @@ ConversationMembersEventTest::testAvoidTwoOneToOneMultiDevices() bobAccount->addContact(aliceUri); bobAccount->sendTrustRequest(aliceUri, {}); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReadyBob && conversationReadyBob2; })); + cv.wait_for(lk, 30s, [&]() { return bobData.conversationId == aliceData.conversationId && bob2Data.conversationId == aliceData.conversationId; })); } void ConversationMembersEventTest::testRemoveRequestBannedMultiDevices() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto requestReceived = false, requestReceivedBob2 = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - else if (accountId == bob2Id) - requestReceivedBob2 = true; - cv.notify_one(); - })); - auto bob2Started = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (accountId == bob2Id) { - auto daemonStatus = details.at( - libjami::Account::VolatileProperties::DEVICE_ANNOUNCED); - if (daemonStatus == "true") - bob2Started = true; - } - cv.notify_one(); - })); - auto bob2ContactRemoved = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactRemoved>( - [&](const std::string& accountId, const std::string& uri, bool banned) { - if (accountId == bob2Id && uri == aliceUri && banned) { - bob2ContactRemoved = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - // Bob creates a second device auto bobArchive = std::filesystem::current_path().string() + "/bob.gz"; std::remove(bobArchive.c_str()); @@ -2290,92 +1449,29 @@ ConversationMembersEventTest::testRemoveRequestBannedMultiDevices() details[ConfProperties::ARCHIVE_PATH] = bobArchive; bob2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Started; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.deviceAnnounced; })); // Alice adds bob - requestReceived = false; aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived && requestReceivedBob2; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && bob2Data.requestReceived; })); CPPUNIT_ASSERT(libjami::getConversationRequests(bob2Id).size() == 1); // Bob bans alice, should update bob2 bobAccount->removeContact(aliceUri, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2ContactRemoved; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.contactRemoved; })); CPPUNIT_ASSERT(libjami::getConversationRequests(bob2Id).size() == 0); } void ConversationMembersEventTest::testBanUnbanMultiDevice() { - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + connectSignals(); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto requestReceived = false, requestReceivedBob2 = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - else if (accountId == bob2Id) - requestReceivedBob2 = true; - cv.notify_one(); - })); - auto bob2Started = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (accountId == bob2Id) { - auto daemonStatus = details.at( - libjami::Account::VolatileProperties::DEVICE_ANNOUNCED); - if (daemonStatus == "true") - bob2Started = true; - } - cv.notify_one(); - })); - auto bob2ContactRemoved = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactRemoved>( - [&](const std::string& accountId, const std::string& uri, bool banned) { - if (accountId == bob2Id && uri == aliceUri && banned) { - bob2ContactRemoved = true; - } - cv.notify_one(); - })); - auto memberMessageGenerated = false, voteMessageGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - auto itFind = message.find("type"); - if (itFind == message.end()) - return; - if (accountId == aliceId && conversationId == convId) { - if (itFind->second == "member") - memberMessageGenerated = true; - if (itFind->second == "vote") - voteMessageGenerated = true; - } - cv.notify_one(); - })); - auto conversationReadyBob = false, conversationReadyBob2 = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReadyBob = true; - } else if (accountId == bob2Id && conversationId == convId) { - conversationReadyBob2 = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - // Bob creates a second device auto bobArchive = std::filesystem::current_path().string() + "/bob.gz"; std::remove(bobArchive.c_str()); @@ -2390,117 +1486,40 @@ ConversationMembersEventTest::testBanUnbanMultiDevice() details[ConfProperties::ARCHIVE_PATH] = bobArchive; bob2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Started; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.deviceAnnounced; })); // Alice adds bob - requestReceived = false; libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return memberMessageGenerated && requestReceived && requestReceivedBob2; - })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && bob2Data.requestReceived; })); // Alice kick Bob while invited - memberMessageGenerated = false; - voteMessageGenerated = false; + auto aliceMsgSize = aliceData.messages.size(); libjami::removeConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); // Alice re-add Bob while invited - memberMessageGenerated = false; - voteMessageGenerated = false; + aliceMsgSize = aliceData.messages.size(); libjami::addConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); // bob accepts libjami::acceptConversationRequest(bobId, convId); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReadyBob && conversationReadyBob2; })); + cv.wait_for(lk, 30s, [&]() { return bobData.conversationId == aliceData.conversationId && bob2Data.conversationId == aliceData.conversationId; })); } void ConversationMembersEventTest::testBanUnbanGotFirstConv() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto requestReceived = false, requestReceivedBob2 = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - else if (accountId == bob2Id) - requestReceivedBob2 = true; - cv.notify_one(); - })); - std::string convId; - auto conversationReadyBob = false, conversationReadyBob2 = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId && conversationId == convId) { - conversationReadyBob = true; - } else if (accountId == bob2Id && conversationId == convId) { - conversationReadyBob2 = true; - } - cv.notify_one(); - })); - auto bob2Started = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (accountId == bob2Id) { - auto daemonStatus = details.at( - libjami::Account::VolatileProperties::DEVICE_ANNOUNCED); - if (daemonStatus == "true") - bob2Started = true; - } - cv.notify_one(); - })); - auto bob2ContactRemoved = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactRemoved>( - [&](const std::string& accountId, const std::string& uri, bool banned) { - if (accountId == bob2Id && uri == aliceUri && banned) { - bob2ContactRemoved = true; - } - cv.notify_one(); - })); - auto bobMsgReceived = false, bob2MsgReceived = false, memberMessageGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId) { - auto itFind = message.find("type"); - if (itFind != message.end() && itFind->second == "member") - memberMessageGenerated = true; - } else if (accountId == bobId && conversationId == convId) - bobMsgReceived = true; - else if (accountId == bob2Id && conversationId == convId) - bob2MsgReceived = true; - cv.notify_one(); - })); - auto contactAddedBob = false, contactAddedBob2 = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactAdded>( - [&](const std::string& accountId, const std::string& uri, bool) { - if (accountId == bobId && uri == aliceUri) { - contactAddedBob = true; - } else if (accountId == bob2Id && uri == aliceUri) { - contactAddedBob2 = true; - } - cv.notify_one(); - })); - - libjami::registerSignalHandlers(confHandlers); - // Bob creates a second device auto bobArchive = std::filesystem::current_path().string() + "/bob.gz"; std::remove(bobArchive.c_str()); @@ -2515,105 +1534,72 @@ ConversationMembersEventTest::testBanUnbanGotFirstConv() details[ConfProperties::ARCHIVE_PATH] = bobArchive; bob2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Started; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.deviceAnnounced; })); // Alice adds bob - requestReceived = false; aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived && requestReceivedBob2; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && bob2Data.requestReceived; })); CPPUNIT_ASSERT(libjami::getConversationRequests(bob2Id).size() == 1); // Accepts requests - libjami::acceptConversationRequest(bobId, convId); + auto aliceMsgSize = aliceData.messages.size(); + libjami::acceptConversationRequest(bobId, aliceData.conversationId); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return conversationReadyBob && conversationReadyBob2 && memberMessageGenerated; + return !bobData.conversationId.empty() && !bob2Data.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); })); // Bob bans alice, should update bob2 bobAccount->removeContact(aliceUri, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2ContactRemoved; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.contactRemoved; })); // Alice sends messages, bob & bob2 should not get it! - bobMsgReceived = false, bob2MsgReceived = false; - libjami::sendMessage(aliceId, convId, "hi"s, ""); - CPPUNIT_ASSERT(!cv.wait_for(lk, 30s, [&]() { return bobMsgReceived && bob2MsgReceived; })); + auto bobMsgSize = bobData.messages.size(); + auto bob2MsgSize = bob2Data.messages.size(); + libjami::sendMessage(aliceId, aliceData.conversationId, "hi"s, ""); + CPPUNIT_ASSERT(!cv.wait_for(lk, 30s, [&]() { return bobMsgSize != bobData.messages.size() && bob2MsgSize != bob2Data.messages.size(); })); // Bobs re-add Alice - contactAddedBob = false, contactAddedBob2 = false; bobAccount->addContact(aliceUri); bobAccount->sendTrustRequest(aliceUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return contactAddedBob && contactAddedBob2; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.conversationId == aliceData.conversationId && bob2Data.conversationId == aliceData.conversationId; })); // Alice can sends some messages now - bobMsgReceived = false, bob2MsgReceived = false; - libjami::sendMessage(aliceId, convId, "hi"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgReceived && bob2MsgReceived; })); + bobMsgSize = bobData.messages.size(); + bob2MsgSize = bob2Data.messages.size(); + libjami::sendMessage(aliceId, aliceData.conversationId, "hi"s, ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize != bobData.messages.size() && bob2MsgSize != bob2Data.messages.size(); })); } void ConversationMembersEventTest::testBanHostWhileHosting() { - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + connectSignals(); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto aliceUri = aliceAccount->getUsername(); auto bobUri = bobAccount->getUsername(); auto convId = libjami::startConversation(aliceId); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - bool memberMessageGenerated = false, callMessageGenerated = false, voteMessageGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "vote") { - voteMessageGenerated = true; - cv.notify_one(); - } else if (accountId == aliceId && conversationId == convId) { - if (message["type"] == "application/call-history+json") { - callMessageGenerated = true; - } else if (message["type"] == "member") { - memberMessageGenerated = true; - } - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); + libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Now, Bob starts a call + aliceMsgSize = aliceData.messages.size(); auto callId = libjami::placeCallWithMedia(bobId, "swarm:" + convId, {}); // should get message - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return callMessageGenerated; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize != aliceData.messages.size(); })); // get active calls = 1 CPPUNIT_ASSERT(libjami::getActiveCalls(aliceId, convId).size() == 1); // Now check that alice, has the only admin, can remove bob - memberMessageGenerated = false; - voteMessageGenerated = false; + aliceMsgSize = aliceData.messages.size(); libjami::removeConversationMember(aliceId, convId, bobUri); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated && voteMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); auto members = libjami::getConversationMembers(aliceId, convId); auto bobBanned = false; for (auto& member : members) { @@ -2621,141 +1607,120 @@ ConversationMembersEventTest::testBanHostWhileHosting() bobBanned = member["role"] == "banned"; } CPPUNIT_ASSERT(bobBanned); - - libjami::unregisterSignalHandlers(); } void ConversationMembersEventTest::testRemoveContactTwice() { + connectSignals(); + std::cout << "\nRunning test: " << __func__ << std::endl; auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string&, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - cv.notify_one(); - })); - std::string convId = ""; auto conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) - convId = conversationId; - else if (accountId == bobId) - conversationReady = true; - cv.notify_one(); - })); - auto conversationRemoved = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == bobId) - conversationRemoved = true; - cv.notify_one(); - })); - auto contactRemoved = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactRemoved>( - [&](const std::string& accountId, const std::string& uri, bool) { - if (accountId == bobId && uri == aliceUri) - contactRemoved = true; - cv.notify_one(); - })); - auto memberMessageGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> message) { - if (accountId == aliceId && message["type"] == "member") - memberMessageGenerated = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - requestReceived = false; + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // removeContact bobAccount->removeContact(aliceUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return contactRemoved; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.removed; })); // wait that connections are closed. std::this_thread::sleep_for(10s); // re-add via a new message. Trigger a new request - requestReceived = false; - libjami::sendMessage(aliceId, convId, "foo"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + bobData.requestReceived = false; + libjami::sendMessage(aliceId, aliceData.conversationId, "foo"s, ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); // removeContact again (should remove the trust request/conversation) - contactRemoved = false; + bobData.removed = false; bobAccount->removeContact(aliceUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return contactRemoved; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.removed; })); } void ConversationMembersEventTest::testAddContactTwice() { + connectSignals(); std::cout << "\nRunning test: " << __func__ << std::endl; auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string&, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - cv.notify_one(); - })); - std::string convId = ""; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) - convId = conversationId; - cv.notify_one(); - })); - auto requestDeclined = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestDeclined>( - [&](const std::string& accountId, const std::string&) { - if (accountId == bobId) - requestDeclined = true; - cv.notify_one(); - })); - auto contactRemoved = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactRemoved>( - [&](const std::string& accountId, const std::string& uri, bool) { - if (accountId == aliceId && uri == bobUri) - contactRemoved = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - requestReceived = false; + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return requestReceived; })); - requestReceived = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return bobData.requestReceived; })); aliceAccount->removeContact(bobUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return contactRemoved; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return aliceData.removed; })); // wait that connections are closed. std::this_thread::sleep_for(10s); + bobData.requestReceived = false; aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return requestDeclined && requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&]() { return bobData.requestRemoved && bobData.requestReceived; })); +} + +void +ConversationMembersEventTest::testBanFromNewDevice() +{ + connectSignals(); + + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); + auto aliceUri = aliceAccount->getUsername(); + auto bobUri = bobAccount->getUsername(); + auto carlaUri = carlaAccount->getUsername(); + + Manager::instance().sendRegister(carlaId, true); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return carlaData.deviceAnnounced; })); + + auto convId = libjami::startConversation(bobId); + + libjami::addConversationMember(bobId, convId, aliceUri); + libjami::addConversationMember(bobId, convId, carlaUri); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return carlaData.requestReceived; })); + + libjami::acceptConversationRequest(carlaId, convId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !carlaData.conversationId.empty(); })); + + Manager::instance().sendRegister(carlaId, false); + + // Bob creates a second device + auto bobArchive = std::filesystem::current_path().string() + "/bob.gz"; + std::remove(bobArchive.c_str()); + bobAccount->exportArchive(bobArchive); + std::map<std::string, std::string> details = libjami::getAccountTemplate("RING"); + details[ConfProperties::TYPE] = "RING"; + details[ConfProperties::DISPLAYNAME] = "BOB2"; + details[ConfProperties::ALIAS] = "BOB2"; + details[ConfProperties::UPNP_ENABLED] = "true"; + details[ConfProperties::ARCHIVE_PASSWORD] = ""; + details[ConfProperties::ARCHIVE_PIN] = ""; + details[ConfProperties::ARCHIVE_PATH] = bobArchive; + bob2Id = Manager::instance().addAccount(details); + + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bob2Data.deviceAnnounced; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bob2Data.conversationId.empty(); })); + + + auto bobMsgSize = bobData.messages.size(); + libjami::removeConversationMember(bob2Id, convId, aliceUri); + CPPUNIT_ASSERT( + cv.wait_for(lk, 30s, [&]() { return bobData.messages.size() == bobMsgSize + 2 /* vote + ban */; })); + + Manager::instance().sendRegister(bob2Id, false); + auto carlaMsgSize = carlaData.messages.size(); + Manager::instance().sendRegister(carlaId, true); + // Should sync! + CPPUNIT_ASSERT( + cv.wait_for(lk, 30s, [&]() { return carlaData.messages.size() > carlaMsgSize; })); } } // namespace test diff --git a/test/unitTest/conversation/conversationRequest.cpp b/test/unitTest/conversation/conversationRequest.cpp index 646ddfc7a3..4bb753fd87 100644 --- a/test/unitTest/conversation/conversationRequest.cpp +++ b/test/unitTest/conversation/conversationRequest.cpp @@ -46,6 +46,22 @@ using namespace libjami::Account; namespace jami { namespace test { +struct UserData { + std::string conversationId; + bool removed {false}; + bool requestReceived {false}; + bool requestRemoved {false}; + bool registered {false}; + bool stopped {false}; + bool deviceAnnounced {false}; + bool contactAdded {false}; + bool contactRemoved {false}; + std::string profilePath; + std::string payloadTrustRequest; + std::vector<libjami::SwarmMessage> messages; + std::vector<libjami::SwarmMessage> messagesUpdated; +}; + class ConversationRequestTest : public CppUnit::TestFixture { public: @@ -62,7 +78,6 @@ public: void testDeclineConversationRequestRemoveTrustRequest(); void testMalformedTrustRequest(); void testAddContactDeleteAndReAdd(); - void testInviteFromMessageAfterRemoved(); void testRemoveContact(); void testRemoveContactMultiDevice(); void testRemoveSelfDoesntRemoveConversation(); @@ -79,9 +94,19 @@ public: void testRemoveContactRemoveTrustRequest(); void testAddConversationNoPresenceThenConnects(); std::string aliceId; + UserData aliceData; std::string bobId; + UserData bobData; std::string bob2Id; + UserData bob2Data; std::string carlaId; + UserData carlaData; + + std::mutex mtx; + std::unique_lock<std::mutex> lk {mtx}; + std::condition_variable cv; + + void connectSignals(); private: CPPUNIT_TEST_SUITE(ConversationRequestTest); @@ -93,7 +118,6 @@ private: CPPUNIT_TEST(testDeclineConversationRequestRemoveTrustRequest); CPPUNIT_TEST(testMalformedTrustRequest); CPPUNIT_TEST(testAddContactDeleteAndReAdd); - CPPUNIT_TEST(testInviteFromMessageAfterRemoved); CPPUNIT_TEST(testRemoveContact); CPPUNIT_TEST(testRemoveContactMultiDevice); CPPUNIT_TEST(testRemoveSelfDoesntRemoveConversation); @@ -127,10 +151,178 @@ ConversationRequestTest::setUp() aliceId = actors["alice"]; bobId = actors["bob"]; carlaId = actors["carla"]; + aliceData = {}; + bobData = {}; + bob2Data = {}; + carlaData = {}; Manager::instance().sendRegister(carlaId, false); wait_for_announcement_of({aliceId, bobId}); } +void +ConversationRequestTest::connectSignals() +{ + std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; + confHandlers.insert( + libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( + [&](const std::string& accountId, const std::map<std::string, std::string>&) { + if (accountId == aliceId) { + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto details = aliceAccount->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + aliceData.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + aliceData.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + aliceData.deviceAnnounced = deviceAnnounced == "true"; + } else if (accountId == bobId) { + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto details = bobAccount->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + bobData.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + bobData.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + bobData.deviceAnnounced = deviceAnnounced == "true"; + } else if (accountId == bob2Id) { + auto bob2Account = Manager::instance().getAccount<JamiAccount>(bob2Id); + auto details = bob2Account->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + bob2Data.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + bob2Data.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + bob2Data.deviceAnnounced = deviceAnnounced == "true"; + } else if (accountId == carlaId) { + auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); + auto details = carlaAccount->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + carlaData.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + carlaData.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + carlaData.deviceAnnounced = deviceAnnounced == "true"; + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( + [&](const std::string& accountId, const std::string& conversationId) { + if (accountId == aliceId) { + aliceData.conversationId = conversationId; + } else if (accountId == bobId) { + bobData.conversationId = conversationId; + } else if (accountId == bob2Id) { + bob2Data.conversationId = conversationId; + } else if (accountId == carlaId) { + carlaData.conversationId = conversationId; + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( + [&](const std::string& account_id, + const std::string& /*from*/, + const std::string& /*conversationId*/, + const std::vector<uint8_t>& payload, + time_t /*received*/) { + auto payloadStr = std::string(payload.data(), payload.data() + payload.size()); + if (account_id == aliceId) + aliceData.payloadTrustRequest = payloadStr; + else if (account_id == bobId) + bobData.payloadTrustRequest = payloadStr; + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + std::map<std::string, std::string> /*metadatas*/) { + if (accountId == aliceId) { + aliceData.requestReceived = true; + } else if (accountId == bobId) { + bobData.requestReceived = true; + } else if (accountId == bob2Id) { + bob2Data.requestReceived = true; + } else if (accountId == carlaId) { + carlaData.requestReceived = true; + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestDeclined>( + [&](const std::string& accountId, const std::string&) { + if (accountId == bobId) { + bobData.requestRemoved = true; + } else if (accountId == bob2Id) { + bob2Data.requestRemoved = true; + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageReceived>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + libjami::SwarmMessage message) { + if (accountId == aliceId) { + aliceData.messages.emplace_back(message); + } else if (accountId == bobId) { + bobData.messages.emplace_back(message); + } else if (accountId == carlaId) { + carlaData.messages.emplace_back(message); + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageUpdated>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + libjami::SwarmMessage message) { + if (accountId == aliceId) { + aliceData.messagesUpdated.emplace_back(message); + } else if (accountId == bobId) { + bobData.messagesUpdated.emplace_back(message); + } else if (accountId == carlaId) { + carlaData.messagesUpdated.emplace_back(message); + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( + [&](const std::string& accountId, const std::string&) { + if (accountId == aliceId) + aliceData.removed = true; + else if (accountId == bobId) + bobData.removed = true; + else if (accountId == bob2Id) + bob2Data.removed = true; + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactAdded>( + [&](const std::string& accountId, const std::string&, bool) { + if (accountId == bobId) { + bobData.contactAdded = true; + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactRemoved>( + [&](const std::string& accountId, const std::string&, bool) { + if (accountId == bobId) { + bobData.contactRemoved = true; + } else if (accountId == bob2Id) { + bob2Data.contactRemoved = true; + } + cv.notify_one(); + })); + + libjami::registerSignalHandlers(confHandlers); +} + void ConversationRequestTest::tearDown() @@ -148,224 +340,109 @@ ConversationRequestTest::tearDown() void ConversationRequestTest::testAcceptTrustRemoveConvReq() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); CPPUNIT_ASSERT(bobAccount->getTrustRequests().size() == 1); - libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + libjami::acceptConversationRequest(bobId, aliceData.conversationId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); })); CPPUNIT_ASSERT(bobAccount->getTrustRequests().size() == 0); } void ConversationRequestTest::acceptConvReqAlsoAddContact() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool requestReceived = false, memberMessageGenerated = false; - int conversationReady = 0; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady += 1; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + bobData.requestReceived = false; auto convId2 = libjami::startConversation(aliceId); - requestReceived = false; libjami::addConversationMember(aliceId, convId2, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::acceptConversationRequest(bobId, convId2); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady == 2; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); })); + std::this_thread::sleep_for(5s); CPPUNIT_ASSERT(bobAccount->getTrustRequests().size() == 0); } void ConversationRequestTest::testGetRequests() { - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + connectSignals(); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); auto convId = libjami::startConversation(aliceId); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); auto requests = libjami::getConversationRequests(bobId); CPPUNIT_ASSERT(requests.size() == 1); CPPUNIT_ASSERT(requests.front()["id"] == convId); - libjami::unregisterSignalHandlers(); } void ConversationRequestTest::testDeclineRequest() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - auto convId = libjami::startConversation(aliceId); libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); libjami::declineConversationRequest(bobId, convId); // Decline request auto requests = libjami::getConversationRequests(bobId); CPPUNIT_ASSERT(requests.size() == 0); - libjami::unregisterSignalHandlers(); } void ConversationRequestTest::testAddContact() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return !convId.empty(); })); - ConversationRepository repo(aliceAccount, convId); + CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return !aliceData.conversationId.empty(); })); + ConversationRepository repo(aliceAccount, aliceData.conversationId); // Mode must be one to one CPPUNIT_ASSERT(repo.mode() == ConversationMode::ONE_TO_ONE); // Assert that repository exists - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() - / "conversations" / convId; + auto repoPath = fileutils::get_data_dir() / aliceId + / "conversations" / aliceData.conversationId; CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); - auto clonedPath = fileutils::get_data_dir() / bobAccount->getAccountID() - / "conversations" / convId; + cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); })); + auto clonedPath = fileutils::get_data_dir() / bobId + / "conversations" / aliceData.conversationId; CPPUNIT_ASSERT(std::filesystem::is_directory(clonedPath)); auto bobMember = clonedPath / "members" / (bobUri + ".crt"); CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobMember)); @@ -374,56 +451,22 @@ ConversationRequestTest::testAddContact() void ConversationRequestTest::testDeclineConversationRequestRemoveTrustRequest() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !convId.empty() && requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); // Decline request auto requests = libjami::getConversationRequests(bobId); CPPUNIT_ASSERT(requests.size() == 1); auto trustRequests = libjami::getTrustRequests(bobId); CPPUNIT_ASSERT(trustRequests.size() == 1); - libjami::declineConversationRequest(bobId, convId); + libjami::declineConversationRequest(bobId, aliceData.conversationId); requests = libjami::getConversationRequests(bobId); CPPUNIT_ASSERT(requests.size() == 0); trustRequests = libjami::getTrustRequests(bobId); @@ -433,51 +476,15 @@ ConversationRequestTest::testDeclineConversationRequestRemoveTrustRequest() void ConversationRequestTest::testMalformedTrustRequest() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - requestDeclined = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !convId.empty() && requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); // Decline request auto requests = libjami::getConversationRequests(bobId); @@ -485,7 +492,7 @@ ConversationRequestTest::testMalformedTrustRequest() auto trustRequests = libjami::getTrustRequests(bobId); CPPUNIT_ASSERT(trustRequests.size() == 1); // This will let the trust request (not libjami::declineConversationRequest) - bobAccount->convModule()->declineConversationRequest(convId); + bobAccount->convModule()->declineConversationRequest(aliceData.conversationId); requests = libjami::getConversationRequests(bobId); CPPUNIT_ASSERT(requests.size() == 0); trustRequests = libjami::getTrustRequests(bobId); @@ -497,6 +504,7 @@ ConversationRequestTest::testMalformedTrustRequest() auto start = std::chrono::steady_clock::now(); + auto requestDeclined = false; do { trustRequests = libjami::getTrustRequests(bobId); requestDeclined = trustRequests.size() == 0; @@ -510,227 +518,83 @@ ConversationRequestTest::testMalformedTrustRequest() void ConversationRequestTest::testAddContactDeleteAndReAdd() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - requestReceived = false; + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); })); // removeContact aliceAccount->removeContact(bobUri, false); std::this_thread::sleep_for(5s); // wait a bit that connections are closed // re-add - CPPUNIT_ASSERT(convId != ""); - auto oldConvId = convId; - convId = ""; + CPPUNIT_ASSERT(aliceData.conversationId != ""); + auto oldConvId = aliceData.conversationId; aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); // Should retrieve previous conversation - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return oldConvId == convId; })); -} - -void -ConversationRequestTest::testInviteFromMessageAfterRemoved() -{ - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); - auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string&, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - requestReceived = false; - aliceAccount->addContact(bobUri); - aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); - - // removeContact - bobAccount->removeContact(aliceUri, false); - std::this_thread::sleep_for(10s); // wait a bit that connections are closed - - // bob sends a message, this should generate a new request for Alice - requestReceived = false; - libjami::sendMessage(aliceId, convId, "hi"s, ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - conversationReady = false; - CPPUNIT_ASSERT(bobAccount->getContacts().size() == 0); - libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); - CPPUNIT_ASSERT(bobAccount->getContacts().size() == 1); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return oldConvId == aliceData.conversationId; })); } void ConversationRequestTest::testRemoveContact() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - bool conversationRemovedAlice = false, conversationRemovedBob = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == aliceId) - conversationRemovedAlice = true; - else if (accountId == bobId) - conversationRemovedBob = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); // Check created files - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !convId.empty() && requestReceived; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); - conversationRemovedBob = false; bobAccount->removeContact(aliceUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationRemovedBob; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.removed; })); auto details = bobAccount->getContactDetails(aliceUri); - CPPUNIT_ASSERT(details.size() == 0); + CPPUNIT_ASSERT(details.find("removed") != details.end() && details["removed"] != "0"); - conversationRemovedAlice = false; aliceAccount->removeContact(bobUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]() { return conversationRemovedAlice; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&]() { return aliceData.removed; })); std::this_thread::sleep_for( 10s); // There is no signal, but daemon should then erase the repository - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() - / "conversations" / convId; + auto repoPath = fileutils::get_data_dir() / aliceId + / "conversations" / aliceData.conversationId; CPPUNIT_ASSERT(!std::filesystem::is_directory(repoPath)); - repoPath = fileutils::get_data_dir() / bobAccount->getAccountID() - / "conversations" / convId; + repoPath = fileutils::get_data_dir() / bobId + / "conversations" / bobData.conversationId; CPPUNIT_ASSERT(!std::filesystem::is_directory(repoPath)); } void ConversationRequestTest::testRemoveContactMultiDevice() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; // Add second device for Bob std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; @@ -749,50 +613,102 @@ ConversationRequestTest::testRemoveContactMultiDevice() bob2Id = Manager::instance().addAccount(details); - wait_for_announcement_of(bob2Id); - auto bob2Account = Manager::instance().getAccount<JamiAccount>(bob2Id); - - bool requestB1Removed = false, requestB2Removed = false, requestB1Received = false, requestB2Received = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestB1Received = true; - else if (account_id == bob2Id) - requestB2Received = true; - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestDeclined>( - [&](const std::string& accountId, const std::string&) { - if (accountId == bobId) { - requestB1Removed = true; - } else if (accountId == bob2Id) { - requestB2Removed = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { + return bob2Data.deviceAnnounced; + })); // First, Alice adds Bob aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return requestB1Received && requestB2Received; + return bobData.requestReceived && bob2Data.requestReceived; })); // Bob1 decline via removeContact, both device should remove the request bobAccount->removeContact(aliceUri, false); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return requestB1Removed && requestB2Removed; })); + cv.wait_for(lk, 30s, [&]() { return bobData.requestRemoved && bob2Data.requestRemoved; })); } void ConversationRequestTest::testRemoveSelfDoesntRemoveConversation() +{ + connectSignals(); + + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto bobUri = bobAccount->getUsername(); + auto aliceUri = aliceAccount->getUsername(); + + aliceAccount->addContact(bobUri); + aliceAccount->sendTrustRequest(bobUri, {}); + // Check created files + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); + CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); + CPPUNIT_ASSERT( + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); + + aliceAccount->removeContact(aliceUri, false); + CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return aliceData.removed; })); + auto repoPath = fileutils::get_data_dir() / aliceId + / "conversations" / aliceData.conversationId; + CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); +} + +void +ConversationRequestTest::testRemoveConversationUpdateContactDetails() +{ + connectSignals(); + + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto bobUri = bobAccount->getUsername(); + auto aliceUri = aliceAccount->getUsername(); + + aliceAccount->addContact(bobUri); + aliceAccount->sendTrustRequest(bobUri, {}); + // Check created files + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); + CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); + CPPUNIT_ASSERT( + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); + + libjami::removeConversation(bobId, bobData.conversationId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.removed; })); + + auto details = bobAccount->getContactDetails(aliceUri); + CPPUNIT_ASSERT(details[libjami::Account::TrustRequest::CONVERSATIONID] == ""); +} + +void +ConversationRequestTest::testBanContact() +{ + connectSignals(); + + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto bobUri = bobAccount->getUsername(); + auto aliceUri = aliceAccount->getUsername(); + + aliceAccount->addContact(bobUri); + aliceAccount->sendTrustRequest(bobUri, {}); + // Check created files + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); + CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); + + bobAccount->removeContact(aliceUri, true); + CPPUNIT_ASSERT(!cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 2 == aliceData.messages.size(); })); + auto repoPath = fileutils::get_data_dir() / bobId + / "conversations" / bobData.conversationId; + CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); +} + + +void +ConversationRequestTest::testBanContactRestartAccount() { auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); @@ -802,205 +718,7 @@ ConversationRequestTest::testRemoveSelfDoesntRemoveConversation() std::unique_lock<std::mutex> lk {mtx}; std::condition_variable cv; std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - conversationRemoved = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == bobId) - conversationRemoved = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - aliceAccount->addContact(bobUri); - aliceAccount->sendTrustRequest(bobUri, {}); - // Check created files - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !convId.empty() && requestReceived; })); - memberMessageGenerated = false; - CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); - - conversationRemoved = false; - aliceAccount->removeContact(aliceUri, false); - CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return conversationRemoved; })); - auto repoPath = fileutils::get_data_dir() / aliceAccount->getAccountID() - / "conversations" / convId; - CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); -} - -void -ConversationRequestTest::testRemoveConversationUpdateContactDetails() -{ - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); - auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false, - conversationRemoved = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == bobId) - conversationRemoved = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - aliceAccount->addContact(bobUri); - aliceAccount->sendTrustRequest(bobUri, {}); - // Check created files - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !convId.empty() && requestReceived; })); - memberMessageGenerated = false; - CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); - CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); - - conversationRemoved = false; - libjami::removeConversation(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationRemoved; })); - - auto details = bobAccount->getContactDetails(aliceUri); - CPPUNIT_ASSERT(details[libjami::Account::TrustRequest::CONVERSATIONID] == ""); -} - -void -ConversationRequestTest::testBanContact() -{ - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); - auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - aliceAccount->addContact(bobUri); - aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return !convId.empty(); })); - // Check created files - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - memberMessageGenerated = false; - CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); - - memberMessageGenerated = false; - bobAccount->removeContact(aliceUri, true); - CPPUNIT_ASSERT(!cv.wait_for(lk, 30s, [&]() { return memberMessageGenerated; })); - auto repoPath = fileutils::get_data_dir() / bobAccount->getAccountID() - / "conversations" / convId; - CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath)); -} - - -void -ConversationRequestTest::testBanContactRestartAccount() -{ - auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); - auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; + bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; std::string convId = ""; confHandlers.insert( libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( @@ -1088,50 +806,19 @@ ConversationRequestTest::testBanContactRestartAccount() void ConversationRequestTest::testBanContactRemoveTrustRequest() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, requestDeclined = true; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestDeclined>( - [&](const std::string& accountId, const std::string&) { - if (accountId == bobId) - requestDeclined = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); // Check created files - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); bobAccount->removeContact(aliceUri, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return requestDeclined; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&]() { return bobData.requestRemoved; })); auto requests = libjami::getConversationRequests(bobId); CPPUNIT_ASSERT(requests.size() == 0); } @@ -1139,194 +826,71 @@ ConversationRequestTest::testBanContactRemoveTrustRequest() void ConversationRequestTest::testAddOfflineContactThenConnect() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto carlaAccount = Manager::instance().getAccount<JamiAccount>(carlaId); auto carlaUri = carlaAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); aliceAccount->trackBuddyPresence(carlaUri, true); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == carlaId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == carlaId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); aliceAccount->addContact(carlaUri); aliceAccount->sendTrustRequest(carlaUri, {}); cv.wait_for(lk, 5s); // Wait 5 secs for the put to happen - CPPUNIT_ASSERT(!convId.empty()); Manager::instance().sendRegister(carlaId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(60), [&]() { return requestReceived; })); - memberMessageGenerated = false; + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(60), [&]() { return carlaData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); CPPUNIT_ASSERT(carlaAccount->acceptTrustRequest(aliceUri)); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && memberMessageGenerated; })); + cv.wait_for(lk, 30s, [&]() { return aliceData.messages.size() == aliceMsgSize + 1; })); } void ConversationRequestTest::testDeclineTrustRequestDoNotGenerateAnother() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); aliceAccount->trackBuddyPresence(bobUri, true); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, memberMessageGenerated = false; - std::string convId = ""; - auto bobConnected = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string&, const std::map<std::string, std::string>&) { - auto details = bobAccount->getVolatileAccountDetails(); - auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; - if (daemonStatus == "REGISTERED") { - bobConnected = true; - cv.notify_one(); - } else if (daemonStatus == "UNREGISTERED") { - bobConnected = false; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); CPPUNIT_ASSERT(bobAccount->discardTrustRequest(aliceUri)); cv.wait_for(lk, 10s); // Wait a bit - bobConnected = true; Manager::instance().sendRegister(bobId, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobConnected; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.stopped; })); // Trigger on peer online - requestReceived = false; + bobData.deviceAnnounced = false; bobData.requestReceived = false; Manager::instance().sendRegister(bobId, true); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobConnected; })); - CPPUNIT_ASSERT(!cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.deviceAnnounced; })); + CPPUNIT_ASSERT(!cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); } void ConversationRequestTest::testRemoveContactRemoveSyncing() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, contactAdded = false, requestReceived = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& convId, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId && !convId.empty()) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactAdded>( - [&](const std::string& accountId, const std::string& uri, bool) { - if (accountId == bobId && uri == aliceUri) { - contactAdded = true; - } - cv.notify_one(); - })); - bool contactRemoved = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactRemoved>( - [&](const std::string& accountId, const std::string& uri, bool) { - if (accountId == bobId && uri == aliceUri) { - contactRemoved = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); Manager::instance().sendRegister(aliceId, false); // This avoid to sync immediately CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return contactAdded; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.contactAdded; })); CPPUNIT_ASSERT(libjami::getConversations(bobId).size() == 1); bobAccount->removeContact(aliceUri, false); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return contactRemoved; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.contactRemoved; })); CPPUNIT_ASSERT(libjami::getConversations(bobId).size() == 0); } @@ -1334,65 +898,25 @@ ConversationRequestTest::testRemoveContactRemoveSyncing() void ConversationRequestTest::testRemoveConversationRemoveSyncing() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, contactAdded = false, requestReceived = false, - conversationRemoved = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& convId, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId && !convId.empty()) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == bobId) { - conversationRemoved = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactAdded>( - [&](const std::string& accountId, const std::string& uri, bool) { - if (accountId == bobId && uri == aliceUri) { - contactAdded = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); Manager::instance().sendRegister(aliceId, false); // This avoid to sync immediately CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return contactAdded; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.contactAdded; })); // At this point the conversation should be there and syncing. CPPUNIT_ASSERT(libjami::getConversations(bobId).size() == 1); - libjami::removeConversation(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationRemoved; })); + libjami::removeConversation(bobId, aliceData.conversationId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.removed; })); CPPUNIT_ASSERT(libjami::getConversations(bobId).size() == 0); } @@ -1400,91 +924,47 @@ ConversationRequestTest::testRemoveConversationRemoveSyncing() void ConversationRequestTest::testCacheRequestFromClient() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + aliceAccount->addContact(bobUri); std::vector<uint8_t> payload = {0x64, 0x64, 0x64}; aliceAccount->sendTrustRequest(bobUri, payload); // Random payload, just care with the file - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - auto cachedPath = fileutils::get_cache_dir() / aliceAccount->getAccountID() + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto cachedPath = fileutils::get_cache_dir() / aliceId / "requests" / bobUri; CPPUNIT_ASSERT(std::filesystem::is_regular_file(cachedPath)); CPPUNIT_ASSERT(fileutils::loadFile(cachedPath) == payload); CPPUNIT_ASSERT(bobAccount->getTrustRequests().size() == 1); - libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + libjami::acceptConversationRequest(bobId, aliceData.conversationId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return !bobData.conversationId.empty(); })); CPPUNIT_ASSERT(!std::filesystem::is_regular_file(cachedPath)); } void ConversationRequestTest::testNeedsSyncingWithForCloning() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); auto aliceDevice = std::string(aliceAccount->currentDeviceId()); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool contactAdded = false, requestReceived = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& convId, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId && !convId.empty()) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ContactAdded>( - [&](const std::string& accountId, const std::string& uri, bool) { - if (accountId == bobId && uri == aliceUri) { - contactAdded = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); + CPPUNIT_ASSERT(!bobAccount->convModule()->needsSyncingWith(aliceUri, aliceDevice)); aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); Manager::instance().sendRegister(aliceId, false); // This avoid to sync immediately CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return contactAdded; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.contactAdded; })); // At this point the conversation should be there and syncing. CPPUNIT_ASSERT(libjami::getConversations(bobId).size() == 1); @@ -1494,13 +974,12 @@ ConversationRequestTest::testNeedsSyncingWithForCloning() void ConversationRequestTest::testRemoveContactRemoveTrustRequest() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; // Add second device for Bob std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; @@ -1519,75 +998,29 @@ ConversationRequestTest::testRemoveContactRemoveTrustRequest() bob2Id = Manager::instance().addAccount(details); - wait_for_announcement_of(bob2Id); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { + return bob2Data.deviceAnnounced; + })); auto bob2Account = Manager::instance().getAccount<JamiAccount>(bob2Id); - bool conversationB1Ready = false, conversationB2Ready = false, conversationB1Removed = false, - conversationB2Removed = false, requestB1Received = false, requestB2Received = false, - memberMessageGenerated = false; - std::string convId = ""; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestB1Received = true; - else if (account_id == bob2Id) - requestB2Received = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationB1Ready = true; - } else if (accountId == bob2Id) { - conversationB2Ready = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string&) { - if (accountId == bobId) { - conversationB1Removed = true; - } else if (accountId == bob2Id) { - conversationB2Removed = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> message) { - if (accountId == aliceId && conversationId == convId && message["type"] == "member") { - memberMessageGenerated = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - // First, Alice adds Bob aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return !convId.empty() && requestB1Received && requestB2Received; + return bobData.requestReceived && bob2Data.requestReceived; })); // Bob1 accepts, both device should get it + auto aliceMsgSize = aliceData.messages.size(); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return conversationB1Ready && conversationB2Ready && memberMessageGenerated; + return !bobData.conversationId.empty() && !bob2Data.conversationId.empty() && aliceMsgSize + 1 == aliceData.messages.size(); })); // Bob2 remove Alice ; Bob1 should not have any trust requests bob2Account->removeContact(aliceUri, false); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationB1Removed && conversationB2Removed; })); + cv.wait_for(lk, 30s, [&]() { return bobData.contactRemoved && bob2Data.contactRemoved; })); std::this_thread::sleep_for(10s); // Wait a bit to ensure that everything is update (via synced) CPPUNIT_ASSERT(bobAccount->getTrustRequests().size() == 0); CPPUNIT_ASSERT(bob2Account->getTrustRequests().size() == 0); diff --git a/test/unitTest/syncHistory/syncHistory.cpp b/test/unitTest/syncHistory/syncHistory.cpp index 7c2d52469c..9d58e83888 100644 --- a/test/unitTest/syncHistory/syncHistory.cpp +++ b/test/unitTest/syncHistory/syncHistory.cpp @@ -41,6 +41,24 @@ using namespace std::literals::chrono_literals; namespace jami { namespace test { +struct UserData { + std::string conversationId; + bool removed {false}; + bool requestReceived {false}; + bool requestRemoved {false}; + bool errorDetected {false}; + bool registered {false}; + bool stopped {false}; + bool deviceAnnounced {false}; + bool sending {false}; + bool sent {false}; + std::string profilePath; + std::string payloadTrustRequest; + std::vector<libjami::SwarmMessage> messages; + std::vector<libjami::SwarmMessage> messagesLoaded; + std::vector<libjami::SwarmMessage> messagesUpdated; +}; + class SyncHistoryTest : public CppUnit::TestFixture { public: @@ -57,8 +75,16 @@ public: void tearDown(); std::string aliceId; + UserData aliceData; std::string bobId; + UserData bobData; std::string alice2Id; + UserData alice2Data; + + std::mutex mtx; + std::unique_lock<std::mutex> lk {mtx}; + std::condition_variable cv; + void connectSignals(); private: void testCreateConversationThenSync(); @@ -103,6 +129,9 @@ SyncHistoryTest::setUp() aliceId = actors["alice"]; bobId = actors["bob"]; alice2Id = ""; + aliceData = {}; + bobData = {}; + alice2Data = {}; } void @@ -117,9 +146,201 @@ SyncHistoryTest::tearDown() } } +void +SyncHistoryTest::connectSignals() +{ + std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; + confHandlers.insert( + libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( + [&](const std::string& accountId, const std::map<std::string, std::string>&) { + if (accountId == aliceId) { + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto details = aliceAccount->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + aliceData.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + aliceData.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + aliceData.deviceAnnounced = deviceAnnounced == "true"; + } else if (accountId == bobId) { + auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); + auto details = bobAccount->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + bobData.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + bobData.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + bobData.deviceAnnounced = deviceAnnounced == "true"; + } else if (accountId == alice2Id) { + auto alice2Account = Manager::instance().getAccount<JamiAccount>(alice2Id); + auto details = alice2Account->getVolatileAccountDetails(); + auto daemonStatus = details[libjami::Account::ConfProperties::Registration::STATUS]; + if (daemonStatus == "REGISTERED") { + alice2Data.registered = true; + } else if (daemonStatus == "UNREGISTERED") { + alice2Data.stopped = true; + } + auto deviceAnnounced = details[libjami::Account::VolatileProperties::DEVICE_ANNOUNCED]; + alice2Data.deviceAnnounced = deviceAnnounced == "true"; + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( + [&](const std::string& accountId, const std::string& conversationId) { + if (accountId == aliceId) { + aliceData.conversationId = conversationId; + } else if (accountId == bobId) { + bobData.conversationId = conversationId; + } else if (accountId == alice2Id) { + alice2Data.conversationId = conversationId; + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ProfileReceived>( + [&](const std::string& accountId, const std::string& peerId, const std::string& path) { + if (accountId == bobId) + bobData.profilePath = path; + else if (accountId == aliceId) + aliceData.profilePath = path; + else if (accountId == alice2Id) + alice2Data.profilePath = path; + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConfigurationSignal::AccountMessageStatusChanged>( + [&](const std::string& accountId, + const std::string& /*conversationId*/, + const std::string& /*peer*/, + const std::string& /*msgId*/, + int status) { + if (accountId == aliceId) { + if (status == 2) + aliceData.sending = true; + if (status == 3) + aliceData.sent = true; + } else if (accountId == alice2Id) { + if (status == 2) + alice2Data.sending = true; + if (status == 3) + alice2Data.sent = true; + } else if (accountId == bobId) { + if (status == 2) + bobData.sending = true; + if (status == 3) + bobData.sent = true; + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( + [&](const std::string& account_id, + const std::string& /*from*/, + const std::string& /*conversationId*/, + const std::vector<uint8_t>& payload, + time_t /*received*/) { + auto payloadStr = std::string(payload.data(), payload.data() + payload.size()); + if (account_id == aliceId) + aliceData.payloadTrustRequest = payloadStr; + else if (account_id == bobId) + bobData.payloadTrustRequest = payloadStr; + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + std::map<std::string, std::string> /*metadatas*/) { + if (accountId == aliceId) { + aliceData.requestReceived = true; + } else if (accountId == bobId) { + bobData.requestReceived = true; + } else if (accountId == alice2Id) { + alice2Data.requestReceived = true; + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestDeclined>( + [&](const std::string& accountId, const std::string&) { + if (accountId == bobId) { + bobData.requestRemoved = true; + } else if (accountId == aliceId) { + aliceData.requestRemoved = true; + } else if (accountId == alice2Id) { + alice2Data.requestRemoved = true; + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageReceived>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + libjami::SwarmMessage message) { + if (accountId == aliceId) { + aliceData.messages.emplace_back(message); + } else if (accountId == bobId) { + bobData.messages.emplace_back(message); + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmLoaded>( + [&](uint32_t, const std::string& accountId, + const std::string& /* conversationId */, + std::vector<libjami::SwarmMessage> messages) { + if (accountId == aliceId) { + aliceData.messagesLoaded.insert(aliceData.messagesLoaded.end(), messages.begin(), messages.end()); + } else if (accountId == alice2Id) { + alice2Data.messagesLoaded.insert(alice2Data.messagesLoaded.end(), messages.begin(), messages.end()); + } else if (accountId == bobId) { + bobData.messagesLoaded.insert(bobData.messagesLoaded.end(), messages.begin(), messages.end()); + } + cv.notify_one(); + })); + confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::SwarmMessageUpdated>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + libjami::SwarmMessage message) { + if (accountId == aliceId) { + aliceData.messagesUpdated.emplace_back(message); + } else if (accountId == bobId) { + bobData.messagesUpdated.emplace_back(message); + } + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::OnConversationError>( + [&](const std::string& accountId, + const std::string& /* conversationId */, + int /*code*/, + const std::string& /* what */) { + if (accountId == aliceId) + aliceData.errorDetected = true; + else if (accountId == bobId) + bobData.errorDetected = true; + cv.notify_one(); + })); + confHandlers.insert( + libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( + [&](const std::string& accountId, const std::string&) { + if (accountId == aliceId) + aliceData.removed = true; + else if (accountId == bobId) + bobData.removed = true; + else if (accountId == alice2Id) + alice2Data.removed = true; + cv.notify_one(); + })); + libjami::registerSignalHandlers(confHandlers); +} + void SyncHistoryTest::testCreateConversationThenSync() { + connectSignals(); + auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); // Start conversation auto convId = libjami::startConversation(aliceId); @@ -136,37 +357,14 @@ SyncHistoryTest::testCreateConversationThenSync() details[ConfProperties::ARCHIVE_PIN] = ""; details[ConfProperties::ARCHIVE_PATH] = aliceArchive; - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto conversationReady = false, alice2Ready = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == alice2Id && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (alice2Id != accountId) { - return; - } - alice2Ready = details.at(libjami::Account::VolatileProperties::DEVICE_ANNOUNCED) - == "true"; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); alice2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Ready && conversationReady; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !alice2Data.conversationId.empty(); })); } void SyncHistoryTest::testCreateConversationWithOnlineDevice() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); // Now create alice2 @@ -180,77 +378,26 @@ SyncHistoryTest::testCreateConversationWithOnlineDevice() details[ConfProperties::ARCHIVE_PASSWORD] = ""; details[ConfProperties::ARCHIVE_PIN] = ""; details[ConfProperties::ARCHIVE_PATH] = aliceArchive; - - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - - // Start conversation now auto convId = libjami::startConversation(aliceId); - auto conversationReady = false, alice2Ready = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == alice2Id && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (alice2Id != accountId) { - return; - } - alice2Ready = details.at(libjami::Account::VolatileProperties::DEVICE_ANNOUNCED) - == "true"; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); alice2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return alice2Ready && conversationReady; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !alice2Data.conversationId.empty(); })); } void SyncHistoryTest::testCreateConversationWithMessagesThenAddDevice() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto convId = libjami::startConversation(aliceId); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto conversationReady = false; - auto messageReceived = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - messageReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == alice2Id && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - confHandlers.clear(); - // Start conversation - messageReceived = false; + auto aliceMsgSize = aliceData.messages.size(); libjami::sendMessage(aliceId, convId, std::string("Message 1"), ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; })); - messageReceived = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 1 == aliceData.messages.size(); })); libjami::sendMessage(aliceId, convId, std::string("Message 2"), ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; })); - messageReceived = false; + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 2 == aliceData.messages.size(); })); libjami::sendMessage(aliceId, convId, std::string("Message 3"), ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 3 == aliceData.messages.size(); })); // Now create alice2 auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz"; @@ -264,31 +411,15 @@ SyncHistoryTest::testCreateConversationWithMessagesThenAddDevice() details[ConfProperties::ARCHIVE_PIN] = ""; details[ConfProperties::ARCHIVE_PATH] = aliceArchive; alice2Id = Manager::instance().addAccount(details); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !alice2Data.conversationId.empty(); })); - // Check if conversation is ready - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return conversationReady; })); - std::vector<std::map<std::string, std::string>> messages; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationLoaded>( - [&](uint32_t, - const std::string& accountId, - const std::string& conversationId, - std::vector<std::map<std::string, std::string>> msg) { - if (accountId == alice2Id && conversationId == convId) { - messages = msg; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - libjami::loadConversationMessages(alice2Id, convId, "", 0); - cv.wait_for(lk, 30s); - libjami::unregisterSignalHandlers(); - confHandlers.clear(); + libjami::loadConversation(alice2Id, convId, "", 0); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Data.messagesLoaded.size() == 4; })); // Check messages - CPPUNIT_ASSERT(messages.size() == 4 /* 3 + initial */); - CPPUNIT_ASSERT(messages[0]["body"] == "Message 3"); - CPPUNIT_ASSERT(messages[1]["body"] == "Message 2"); - CPPUNIT_ASSERT(messages[2]["body"] == "Message 1"); + CPPUNIT_ASSERT(alice2Data.messagesLoaded[0].body["body"] == "Message 3"); + CPPUNIT_ASSERT(alice2Data.messagesLoaded[1].body["body"] == "Message 2"); + CPPUNIT_ASSERT(alice2Data.messagesLoaded[2].body["body"] == "Message 1"); } void @@ -346,56 +477,25 @@ SyncHistoryTest::testCreateMultipleConversationThenAddDevice() // Check if conversation is ready CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return conversationReady == 4; })); - libjami::unregisterSignalHandlers(); } void SyncHistoryTest::testReceivesInviteThenAddDevice() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); // Export alice auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz"; aliceAccount->exportArchive(aliceArchive); - auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto uri = aliceAccount->getUsername(); // Start conversation for Alice auto convId = libjami::startConversation(bobId); - // Check that alice receives the request - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto requestReceived = false, memberEvent = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == aliceId && conversationId == convId) { - requestReceived = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationMemberEvent>( - [&](const std::string& /*accountId*/, - const std::string& /*conversationId*/, - const std::string& /*memberUri*/, - int /*event*/) { - memberEvent = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - - memberEvent = false; libjami::addConversationMember(bobId, convId, uri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return memberEvent && requestReceived; })); - libjami::unregisterSignalHandlers(); - confHandlers.clear(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceData.requestReceived; })); // Now create alice2 std::map<std::string, std::string> details = libjami::getAccountTemplate("RING"); @@ -408,26 +508,13 @@ SyncHistoryTest::testReceivesInviteThenAddDevice() details[ConfProperties::ARCHIVE_PATH] = aliceArchive; alice2Id = Manager::instance().addAccount(details); - requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == alice2Id && conversationId == convId) { - requestReceived = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return requestReceived; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Data.requestReceived; })); } void SyncHistoryTest::testRemoveConversationOnAllDevices() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); // Now create alice2 @@ -441,53 +528,18 @@ SyncHistoryTest::testRemoveConversationOnAllDevices() details[ConfProperties::ARCHIVE_PASSWORD] = ""; details[ConfProperties::ARCHIVE_PIN] = ""; details[ConfProperties::ARCHIVE_PATH] = aliceArchive; - - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - - // Start conversation now auto convId = libjami::startConversation(aliceId); - bool alice2Ready = false; - auto conversationReady = false, conversationRemoved = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == alice2Id && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationRemoved>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == alice2Id && conversationId == convId) { - conversationRemoved = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (alice2Id != accountId) { - return; - } - alice2Ready = details.at(libjami::Account::VolatileProperties::DEVICE_ANNOUNCED) - == "true"; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); alice2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return alice2Ready && conversationReady; })); - libjami::removeConversation(aliceId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return conversationRemoved; })); - - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return !alice2Data.conversationId.empty(); })); + libjami::removeConversation(aliceId, aliceData.conversationId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Data.removed; })); } void SyncHistoryTest::testSyncCreateAccountExportDeleteReimportOldBackup() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); @@ -499,65 +551,11 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportOldBackup() // Start conversation auto convId = libjami::startConversation(aliceId); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - bool alice2Ready = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - - if (accountId == alice2Id && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (alice2Id != accountId) { - return; - } - alice2Ready = details.at(libjami::Account::VolatileProperties::DEVICE_ANNOUNCED) - == "true"; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); - + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); - - // Wait that alice sees Bob - cv.wait_for(lk, 30s, [&]() { return messageAliceReceived == 1; }); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size() && !bobData.conversationId.empty(); })); // disable account (same as removed) Manager::instance().sendRegister(aliceId, false); @@ -571,27 +569,25 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportOldBackup() details[ConfProperties::ARCHIVE_PASSWORD] = ""; details[ConfProperties::ARCHIVE_PIN] = ""; details[ConfProperties::ARCHIVE_PATH] = aliceArchive; - requestReceived = false; - conversationReady = false; alice2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Ready; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Data.deviceAnnounced; })); // This will trigger a conversation request. Cause alice2 can't know first conversation libjami::sendMessage(bobId, convId, std::string("hi"), ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Data.requestReceived; })); libjami::acceptConversationRequest(alice2Id, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !alice2Data.conversationId.empty(); })); - messageBobReceived = 0; + auto bobMsgSize = bobData.messages.size(); libjami::sendMessage(alice2Id, convId, std::string("hi"), ""); - cv.wait_for(lk, 30s, [&]() { return messageBobReceived == 1; }); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); })); } void SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvId() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto aliceUri = aliceAccount->getUsername(); @@ -600,76 +596,16 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvId() // Start conversation auto convId = libjami::startConversation(aliceId); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - bool alice2Ready = false; - bool memberAddGenerated = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationMemberEvent>( - [&](const std::string& accountId, - const std::string& conversationId, - const std::string& uri, - int event) { - if (accountId == aliceId && conversationId == convId && uri == bobUri - && event == 0) { - memberAddGenerated = true; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& /*accountId*/, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (conversationId != convId) - return; - if (accountId == bobId || accountId == alice2Id) - conversationReady = true; - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (alice2Id != accountId) { - return; - } - alice2Ready = details.at(libjami::Account::VolatileProperties::DEVICE_ANNOUNCED) - == "true"; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - libjami::addConversationMember(aliceId, convId, bobUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); libjami::acceptConversationRequest(bobId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // We need to track presence to know when to sync bobAccount->trackBuddyPresence(aliceUri, true); - // Wait that alice sees Bob - cv.wait_for(lk, 30s, [&]() { return memberAddGenerated; }); - // Backup alice after startConversation with member auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz"; aliceAccount->exportArchive(aliceArchive); @@ -686,83 +622,27 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvId() details[ConfProperties::ARCHIVE_PASSWORD] = ""; details[ConfProperties::ARCHIVE_PIN] = ""; details[ConfProperties::ARCHIVE_PATH] = aliceArchive; - requestReceived = false; - conversationReady = false; alice2Id = Manager::instance().addAccount(details); // Should retrieve conversation, no need for action as the convInfos is in the archive - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Ready && conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !alice2Data.conversationId.empty(); })); - messageBobReceived = 0; + auto bobMsgSize = bobData.messages.size(); libjami::sendMessage(alice2Id, convId, std::string("hi"), ""); - cv.wait_for(lk, 30s, [&]() { return messageBobReceived == 1; }); - libjami::unregisterSignalHandlers(); + cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); }); } void SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvReq() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); - auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto bobUri = bobAccount->getUsername(); auto aliceUri = aliceAccount->getUsername(); // Start conversation auto convId = libjami::startConversation(bobId); - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; - bool alice2Ready = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*message*/) { - if (accountId == bobId) { - messageBobReceived += 1; - } else { - messageAliceReceived += 1; - } - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == aliceId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId) { - conversationReady = true; - cv.notify_one(); - } - - if (accountId == alice2Id && conversationId == convId) { - conversationReady = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (alice2Id != accountId) { - return; - } - alice2Ready = details.at(libjami::Account::VolatileProperties::DEVICE_ANNOUNCED) - == "true"; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - libjami::addConversationMember(bobId, convId, aliceUri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.requestReceived; })); // Backup alice after startConversation with member auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz"; @@ -780,54 +660,24 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvReq() details[ConfProperties::ARCHIVE_PASSWORD] = ""; details[ConfProperties::ARCHIVE_PIN] = ""; details[ConfProperties::ARCHIVE_PATH] = aliceArchive; - conversationReady = false; alice2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Ready; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Data.deviceAnnounced; })); // Should get the same request as before. - messageBobReceived = 0; + auto bobMsgSize = bobData.messages.size(); libjami::acceptConversationRequest(alice2Id, convId); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && messageBobReceived == 1; })); - libjami::unregisterSignalHandlers(); + cv.wait_for(lk, 30s, [&]() { return bobMsgSize + 1 == bobData.messages.size(); })); } void SyncHistoryTest::testSyncOneToOne() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - // Start conversation - std::string convId; - - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto conversationReady = false, alice2Ready = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) - convId = conversationId; - else if (accountId == alice2Id && conversationId == convId) - conversationReady = true; - cv.notify_one(); - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::VolatileDetailsChanged>( - [&](const std::string& accountId, const std::map<std::string, std::string>& details) { - if (alice2Id != accountId) { - return; - } - alice2Ready = details.at(libjami::Account::VolatileProperties::DEVICE_ANNOUNCED) - == "true"; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); aliceAccount->addContact(bobAccount->getUsername()); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !convId.empty(); })); - // Now create alice2 auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz"; aliceAccount->exportArchive(aliceArchive); @@ -841,47 +691,28 @@ SyncHistoryTest::testSyncOneToOne() details[ConfProperties::ARCHIVE_PATH] = aliceArchive; alice2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Ready && conversationReady; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !alice2Data.conversationId.empty(); })); } void SyncHistoryTest::testConversationRequestRemoved() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); + auto uri = aliceAccount->getUsername(); // Export alice auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz"; aliceAccount->exportArchive(aliceArchive); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); - auto uri = aliceAccount->getUsername(); // Start conversation for Alice auto convId = libjami::startConversation(bobId); // Check that alice receives the request - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == aliceId && conversationId == convId) { - requestReceived = true; - cv.notify_one(); - } - })); - libjami::registerSignalHandlers(confHandlers); - libjami::addConversationMember(bobId, convId, uri); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return requestReceived; })); - libjami::unregisterSignalHandlers(); - confHandlers.clear(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceData.requestReceived; })); // Now create alice2 std::map<std::string, std::string> details = libjami::getAccountTemplate("RING"); @@ -894,43 +725,17 @@ SyncHistoryTest::testConversationRequestRemoved() details[ConfProperties::ARCHIVE_PATH] = aliceArchive; alice2Id = Manager::instance().addAccount(details); - requestReceived = false; - bool requestDeclined = false, requestDeclined2 = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& conversationId, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == alice2Id && conversationId == convId) { - requestReceived = true; - cv.notify_one(); - } - })); - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestDeclined>( - [&](const std::string& accountId, const std::string& conversationId) { - if (conversationId != convId) - return; - if (accountId == aliceId) - requestDeclined = true; - if (accountId == alice2Id) - requestDeclined2 = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceData.requestReceived; })); // Now decline trust request, this should trigger ConversationRequestDeclined both sides for Alice libjami::declineConversationRequest(aliceId, convId); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return requestDeclined && requestDeclined2; })); - - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceData.requestRemoved && alice2Data.requestRemoved; })); } void SyncHistoryTest::testProfileReceivedMultiDevice() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto aliceUri = aliceAccount->getUsername(); @@ -946,9 +751,9 @@ VERSION:2.1\n\ FN:TITLE\n\ DESCRIPTION:DESC\n\ END:VCARD"; - auto alicePath = fileutils::get_data_dir() / aliceAccount->getAccountID() + auto alicePath = fileutils::get_data_dir() / aliceId / "profile.vcf"; - auto bobPath = fileutils::get_data_dir() / bobAccount->getAccountID() + auto bobPath = fileutils::get_data_dir() / bobId / "profile.vcf"; // Save VCard auto p = std::filesystem::path(alicePath); @@ -966,59 +771,15 @@ END:VCARD"; bobFile.close(); } - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - bool conversationReady = false, requestReceived = false, bobProfileReceived = false, - aliceProfileReceived = false, bobProfileReceivedAlice2 = false; - std::string convId = ""; - std::string bobDest = aliceAccount->dataTransfer()->profilePath(bobUri); - confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::IncomingTrustRequest>( - [&](const std::string& account_id, - const std::string& /*from*/, - const std::string& /*conversationId*/, - const std::vector<uint8_t>& /*payload*/, - time_t /*received*/) { - if (account_id == bobId) - requestReceived = true; - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == aliceId) { - convId = conversationId; - } else if (accountId == bobId) { - conversationReady = true; - } - cv.notify_one(); - })); - confHandlers.insert(libjami::exportable_callback<libjami::ConfigurationSignal::ProfileReceived>( - [&](const std::string& accountId, const std::string& peerId, const std::string& path) { - if (accountId == aliceId && peerId == bobUri) { - bobProfileReceived = true; - auto p = std::filesystem::path(bobDest); - dhtnet::fileutils::recursive_mkdir(p.parent_path()); - std::rename(path.c_str(), bobDest.c_str()); - } else if (accountId == bobId && peerId == aliceUri) { - aliceProfileReceived = true; - } else if (accountId == alice2Id && peerId == bobUri) { - bobProfileReceivedAlice2 = true; - } else if (accountId == alice2Id && peerId == aliceUri) { - aliceProfileReceived = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { - return conversationReady && bobProfileReceived && aliceProfileReceived; + return !bobData.profilePath.empty() && !aliceData.profilePath.empty() && !bobData.conversationId.empty(); })); - CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobDest)); + CPPUNIT_ASSERT(std::filesystem::is_regular_file(bobData.profilePath)); // Now create alice2 std::map<std::string, std::string> details = libjami::getAccountTemplate("RING"); @@ -1029,96 +790,44 @@ END:VCARD"; details[ConfProperties::ARCHIVE_PASSWORD] = ""; details[ConfProperties::ARCHIVE_PIN] = ""; details[ConfProperties::ARCHIVE_PATH] = aliceArchive; - bobProfileReceived = false, aliceProfileReceived = false; + bobData.profilePath = {}; + alice2Data.profilePath = {}; alice2Id = Manager::instance().addAccount(details); - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return aliceProfileReceived && bobProfileReceivedAlice2; })); - libjami::unregisterSignalHandlers(); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { + return alice2Data.deviceAnnounced && !bobData.profilePath.empty() && !alice2Data.profilePath.empty(); })); } void SyncHistoryTest::testLastInteractionAfterClone() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto aliceUri = aliceAccount->getUsername(); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::string convId; - - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageReceived = false; - std::string msgId = ""; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& accountId , - const std::string& /* conversationId */, - std::map<std::string, std::string> message) { - if (accountId == bobId) { - messageReceived = true; - msgId = message["id"]; - cv.notify_one(); - } - })); - auto conversationReady = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId || accountId == alice2Id) { - convId = conversationId; - conversationReady = true; - } - cv.notify_one(); - })); - auto requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& /*conversationId*/, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - cv.notify_one(); - })); - auto messageDisplayed = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::AccountMessageStatusChanged>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - const std::string& /* username */, - const std::string& /* msgId */, - int status) { - if (bobId == accountId && status == 3) - messageDisplayed = true; - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - confHandlers.clear(); aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + auto aliceMsgSize = aliceData.messages.size(); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Start conversation - messageReceived = false; - libjami::sendMessage(bobId, convId, std::string("Message 1"), ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; })); - messageReceived = false; - libjami::sendMessage(bobId, convId, std::string("Message 2"), ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; })); - messageReceived = false; - libjami::sendMessage(bobId, convId, std::string("Message 3"), ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; })); - - messageDisplayed = false; - libjami::setMessageDisplayed(aliceId, "swarm:" + convId, msgId, 3); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageDisplayed; })); + libjami::sendMessage(bobId, aliceData.conversationId, std::string("Message 1"), ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 2 == aliceData.messages.size(); })); + libjami::sendMessage(bobId, aliceData.conversationId, std::string("Message 2"), ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 3 == aliceData.messages.size(); })); + libjami::sendMessage(bobId, aliceData.conversationId, std::string("Message 3"), ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 4 == aliceData.messages.size(); })); + + auto msgId = aliceData.messages.rbegin()->id; + libjami::setMessageDisplayed(aliceId, "swarm:" + aliceData.conversationId, msgId, 3); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceData.sent; })); // Now create alice2 - conversationReady = false; auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz"; aliceAccount->exportArchive(aliceArchive); std::map<std::string, std::string> details = libjami::getAccountTemplate("RING"); @@ -1132,9 +841,9 @@ SyncHistoryTest::testLastInteractionAfterClone() alice2Id = Manager::instance().addAccount(details); // Check if conversation is ready - CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return conversationReady; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return !alice2Data.conversationId.empty(); })); // Check that last displayed is synched - auto membersInfos = libjami::getConversationMembers(alice2Id, convId); + auto membersInfos = libjami::getConversationMembers(alice2Id, alice2Data.conversationId); CPPUNIT_ASSERT(std::find_if(membersInfos.begin(), membersInfos.end(), [&](auto infos) { @@ -1147,65 +856,11 @@ SyncHistoryTest::testLastInteractionAfterClone() void SyncHistoryTest::testLastInteractionAfterSomeMessages() { + connectSignals(); auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId); auto aliceUri = aliceAccount->getUsername(); auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId); auto bobUri = bobAccount->getUsername(); - std::string convId; - - std::mutex mtx; - std::unique_lock<std::mutex> lk {mtx}; - std::condition_variable cv; - std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers; - auto messageReceived = false; - std::string msgId = ""; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>( - [&](const std::string& /* accountId */, - const std::string& /* conversationId */, - std::map<std::string, std::string> message) { - messageReceived = true; - msgId = message["id"]; - cv.notify_one(); - })); - auto conversationReady = false, conversationAlice2Ready = false; - confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>( - [&](const std::string& accountId, const std::string& conversationId) { - if (accountId == bobId) { - convId = conversationId; - conversationReady = true; - } else if (accountId == alice2Id) { - conversationAlice2Ready = true; - } - cv.notify_one(); - })); - auto requestReceived = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>( - [&](const std::string& accountId, - const std::string& /*conversationId*/, - std::map<std::string, std::string> /*metadatas*/) { - if (accountId == bobId) - requestReceived = true; - cv.notify_one(); - })); - auto messageDisplayed = false, messageDisplayedAlice2 = false; - confHandlers.insert( - libjami::exportable_callback<libjami::ConfigurationSignal::AccountMessageStatusChanged>( - [&](const std::string& accountId, - const std::string& /* conversationId */, - const std::string& /* username */, - const std::string& msgId, - int status) { - if (status == 3) { - if (accountId == aliceId) - messageDisplayed = true; - else if (accountId == alice2Id) - messageDisplayedAlice2 = true; - } - cv.notify_one(); - })); - libjami::registerSignalHandlers(confHandlers); - confHandlers.clear(); // Creates alice2 auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz"; @@ -1222,30 +877,26 @@ SyncHistoryTest::testLastInteractionAfterSomeMessages() aliceAccount->addContact(bobUri); aliceAccount->sendTrustRequest(bobUri, {}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived && !alice2Data.conversationId.empty(); })); + auto aliceMsgSize = aliceData.messages.size(); CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri)); CPPUNIT_ASSERT( - cv.wait_for(lk, 30s, [&]() { return conversationReady && conversationAlice2Ready; })); + cv.wait_for(lk, 30s, [&]() { return aliceMsgSize + 1 == aliceData.messages.size(); })); // Start conversation - messageReceived = false; - libjami::sendMessage(bobId, convId, std::string("Message 1"), ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; })); - messageReceived = false; - libjami::sendMessage(bobId, convId, std::string("Message 2"), ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; })); - messageReceived = false; - libjami::sendMessage(bobId, convId, std::string("Message 3"), ""); - CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; })); - - messageDisplayed = false; - messageDisplayedAlice2 = false; - auto displayedId = msgId; - libjami::setMessageDisplayed(aliceId, "swarm:" + convId, displayedId, 3); - CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return messageDisplayed && messageDisplayedAlice2; })); - - auto membersInfos = libjami::getConversationMembers(alice2Id, convId); + libjami::sendMessage(bobId, aliceData.conversationId, std::string("Message 1"), ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 2 == aliceData.messages.size(); })); + libjami::sendMessage(bobId, aliceData.conversationId, std::string("Message 2"), ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 3 == aliceData.messages.size(); })); + libjami::sendMessage(bobId, aliceData.conversationId, std::string("Message 3"), ""); + CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return aliceMsgSize + 4 == aliceData.messages.size(); })); + + auto displayedId = aliceData.messages.rbegin()->id; + libjami::setMessageDisplayed(aliceId, "swarm:" + aliceData.conversationId, displayedId, 3); + CPPUNIT_ASSERT(cv.wait_for(lk, 20s, [&] { return aliceData.sent && alice2Data.sent; })); + + auto membersInfos = libjami::getConversationMembers(alice2Id, alice2Data.conversationId); CPPUNIT_ASSERT(std::find_if(membersInfos.begin(), membersInfos.end(), [&](auto infos) { -- GitLab