LCOV - code coverage report
Current view: top level - test/unitTest/plugins - plugins.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 373 382 97.6 %
Date: 2024-12-21 08:56:24 Functions: 35 35 100.0 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  This program is free software: you can redistribute it and/or modify
       5             :  *  it under the terms of the GNU General Public License as published by
       6             :  *  the Free Software Foundation, either version 3 of the License, or
       7             :  *  (at your option) any later version.
       8             :  *
       9             :  *  This program is distributed in the hope that it will be useful,
      10             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12             :  *  GNU General Public License for more details.
      13             :  *
      14             :  *  You should have received a copy of the GNU General Public License
      15             :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include <cppunit/TestAssert.h>
      19             : #include <cppunit/TestFixture.h>
      20             : #include <cppunit/extensions/HelperMacros.h>
      21             : 
      22             : #include <condition_variable>
      23             : #include <opendht/crypto.h>
      24             : #include <filesystem>
      25             : #include <memory>
      26             : #include <string>
      27             : 
      28             : #include "manager.h"
      29             : #include "plugin/jamipluginmanager.h"
      30             : #include "plugin/pluginsutils.h"
      31             : #include "jamidht/jamiaccount.h"
      32             : #include "../../test_runner.h"
      33             : #include "jami.h"
      34             : #include "fileutils.h"
      35             : #include "jami/media_const.h"
      36             : #include "account_const.h"
      37             : #include "sip/sipcall.h"
      38             : #include "call_const.h"
      39             : 
      40             : #include "common.h"
      41             : 
      42             : #include <yaml-cpp/yaml.h>
      43             : 
      44             : using namespace libjami::Account;
      45             : 
      46             : namespace jami {
      47             : namespace test {
      48             : 
      49             : struct CallData
      50             : {
      51             :     struct Signal
      52             :     {
      53          12 :         Signal(const std::string& name, const std::string& event = {})
      54          12 :             : name_(std::move(name))
      55          12 :             , event_(std::move(event)) {};
      56             : 
      57             :         std::string name_ {};
      58             :         std::string event_ {};
      59             :     };
      60             : 
      61          20 :     CallData() = default;
      62             :     CallData(CallData&& other) = delete;
      63             :     CallData(const CallData& other)
      64             :     {
      65             :         accountId_ = std::move(other.accountId_);
      66             :         listeningPort_ = other.listeningPort_;
      67             :         userName_ = std::move(other.userName_);
      68             :         alias_ = std::move(other.alias_);
      69             :         callId_ = std::move(other.callId_);
      70             :         signals_ = std::move(other.signals_);
      71             :     };
      72             : 
      73             :     std::string accountId_ {};
      74             :     std::string userName_ {};
      75             :     std::string alias_ {};
      76             :     uint16_t listeningPort_ {0};
      77             :     std::string toUri_ {};
      78             :     std::string callId_ {};
      79             :     std::vector<Signal> signals_;
      80             :     std::condition_variable cv_ {};
      81             :     std::mutex mtx_;
      82             : };
      83             : 
      84             : class PluginsTest : public CppUnit::TestFixture
      85             : {
      86             : public:
      87          10 :     PluginsTest()
      88          10 :     {
      89             :         // Init daemon
      90          10 :         libjami::init(libjami::InitFlag(libjami::LIBJAMI_FLAG_DEBUG | libjami::LIBJAMI_FLAG_CONSOLE_LOG));
      91          10 :         if (not Manager::instance().initialized)
      92           1 :             CPPUNIT_ASSERT(libjami::start("jami-sample.yml"));
      93          10 :     }
      94          20 :     ~PluginsTest() { libjami::fini(); }
      95           2 :     static std::string name() { return "Plugins"; }
      96             :     void setUp();
      97             :     void tearDown();
      98             : 
      99             :     CallData aliceData;
     100             :     CallData bobData;
     101             : 
     102             : private:
     103             :     static bool waitForSignal(CallData& callData,
     104             :                               const std::string& signal,
     105             :                               const std::string& expectedEvent = {});
     106             :     // Event/Signal handlers
     107             :     static void onCallStateChange(const std::string& accountId,
     108             :                                   const std::string& callId,
     109             :                                   const std::string& state,
     110             :                                   CallData& callData);
     111             :     static void onIncomingCallWithMedia(const std::string& accountId,
     112             :                                         const std::string& callId,
     113             :                                         const std::vector<libjami::MediaMap> mediaList,
     114             :                                         CallData& callData);
     115             : 
     116             :     std::string name_{};
     117             :     std::string jplPath_{};
     118             :     std::string certPath_{};
     119             :     std::string pluginCertNotFound_{};
     120             :     std::string pluginNotSign_{};
     121             :     std::string pluginFileNotSign_{};
     122             :     std::string pluginManifestChanged_{};
     123             :     std::string pluginNotSignByIssuer_{};
     124             :     std::string pluginNotFoundPath_{};
     125             :     std::unique_ptr<dht::crypto::Certificate> cert_{};
     126             :     std::string installationPath_{};
     127             :     std::vector<std::string> mediaHandlers_{};
     128             :     std::vector<std::string> chatHandlers_{};
     129             : 
     130             :     void testEnable();
     131             :     void testCertificateVerification();
     132             :     void testSignatureVerification();
     133             :     void testLoad();
     134             :     void testInstallAndLoad();
     135             :     void testHandlers();
     136             :     void testDetailsAndPreferences();
     137             :     void testTranslations();
     138             :     void testCall();
     139             :     void testMessage();
     140             : 
     141           2 :     CPPUNIT_TEST_SUITE(PluginsTest);
     142           1 :     CPPUNIT_TEST(testEnable);
     143           1 :     CPPUNIT_TEST(testCertificateVerification);
     144           1 :     CPPUNIT_TEST(testSignatureVerification);
     145           1 :     CPPUNIT_TEST(testLoad);
     146           1 :     CPPUNIT_TEST(testInstallAndLoad);
     147           1 :     CPPUNIT_TEST(testHandlers);
     148           1 :     CPPUNIT_TEST(testDetailsAndPreferences);
     149           1 :     CPPUNIT_TEST(testTranslations);
     150           1 :     CPPUNIT_TEST(testCall);
     151           1 :     CPPUNIT_TEST(testMessage);
     152           4 :     CPPUNIT_TEST_SUITE_END();
     153             : };
     154             : 
     155             : CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(PluginsTest, PluginsTest::name());
     156             : 
     157             : void
     158           1 : PluginsTest::onIncomingCallWithMedia(const std::string& accountId,
     159             :                                         const std::string& callId,
     160             :                                         const std::vector<libjami::MediaMap> mediaList,
     161             :                                         CallData& callData)
     162             : {
     163           1 :     CPPUNIT_ASSERT_EQUAL(callData.accountId_, accountId);
     164             : 
     165           1 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - media count [%lu]",
     166             :               libjami::CallSignal::IncomingCallWithMedia::name,
     167             :               callData.alias_.c_str(),
     168             :               callId.c_str(),
     169             :               mediaList.size());
     170             : 
     171             :     // NOTE.
     172             :     // We shouldn't access shared_ptr<Call> as this event is supposed to mimic
     173             :     // the client, and the client have no access to this type. But here, we only
     174             :     // needed to check if the call exists. This is the most straightforward and
     175             :     // reliable way to do it until we add a new API (like hasCall(id)).
     176           1 :     if (not Manager::instance().getCallFromCallID(callId)) {
     177           0 :         JAMI_WARN("Call with ID [%s] does not exist!", callId.c_str());
     178           0 :         callData.callId_ = {};
     179           0 :         return;
     180             :     }
     181             : 
     182           1 :     std::unique_lock lock {callData.mtx_};
     183           1 :     callData.callId_ = callId;
     184           1 :     callData.signals_.emplace_back(CallData::Signal(libjami::CallSignal::IncomingCallWithMedia::name));
     185             : 
     186           1 :     callData.cv_.notify_one();
     187           1 : }
     188             : 
     189             : void
     190          11 : PluginsTest::onCallStateChange(const std::string& accountId,
     191             :                                 const std::string& callId,
     192             :                                 const std::string& state,
     193             :                                 CallData& callData)
     194             : {
     195          11 :     JAMI_INFO("Signal [%s] - user [%s] - call [%s] - state [%s]",
     196             :               libjami::CallSignal::StateChange::name,
     197             :               callData.alias_.c_str(),
     198             :               callId.c_str(),
     199             :               state.c_str());
     200             : 
     201          11 :     CPPUNIT_ASSERT(accountId == callData.accountId_);
     202             : 
     203             :     {
     204          11 :         std::unique_lock lock {callData.mtx_};
     205          11 :         callData.signals_.emplace_back(
     206          22 :             CallData::Signal(libjami::CallSignal::StateChange::name, state));
     207          11 :     }
     208             :     // NOTE. Only states that we are interested in will notify the CV.
     209             :     // If this unit test is modified to process other states, they must
     210             :     // be added here.
     211          11 :     if (state == "CURRENT" or state == "OVER" or state == "HUNGUP" or state == "RINGING") {
     212           7 :         callData.cv_.notify_one();
     213             :     }
     214          11 : }
     215             : 
     216             : void
     217          10 : PluginsTest::setUp()
     218             : {
     219          20 :     auto actors = load_actors_and_wait_for_announcement("actors/alice-bob-no-upnp.yml");
     220             : 
     221          10 :     aliceData.accountId_ = actors["alice"];
     222          10 :     bobData.accountId_ = actors["bob"];
     223             : 
     224             :     // Configure Alice
     225             :     {
     226          10 :         CPPUNIT_ASSERT(not aliceData.accountId_.empty());
     227          10 :         auto const& account = Manager::instance().getAccount<Account>(
     228          10 :             aliceData.accountId_);
     229          10 :         aliceData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME];
     230          10 :         aliceData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS];
     231          10 :     }
     232             : 
     233             :     // Configure Bob
     234             :     {
     235          10 :         CPPUNIT_ASSERT(not bobData.accountId_.empty());
     236          10 :         auto const& account = Manager::instance().getAccount<Account>(
     237          10 :             bobData.accountId_);
     238          10 :         bobData.userName_ = account->getAccountDetails()[ConfProperties::USERNAME];
     239          10 :         bobData.alias_ = account->getAccountDetails()[ConfProperties::ALIAS];
     240          10 :     }
     241             : 
     242          10 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> signalHandlers;
     243             : 
     244             :     // Insert needed signal handlers.
     245          10 :     signalHandlers.insert(libjami::exportable_callback<libjami::CallSignal::IncomingCallWithMedia>(
     246           1 :         [&](const std::string& accountId,
     247             :             const std::string& callId,
     248             :             const std::string&,
     249             :             const std::vector<libjami::MediaMap> mediaList) {
     250           1 :             if (aliceData.accountId_ == accountId)
     251           0 :                 onIncomingCallWithMedia(accountId, callId, mediaList, aliceData);
     252           1 :             else if (bobData.accountId_ == accountId)
     253           1 :                 onIncomingCallWithMedia(accountId, callId, mediaList, bobData);
     254           1 :         }));
     255             : 
     256          10 :     signalHandlers.insert(
     257          20 :         libjami::exportable_callback<libjami::CallSignal::StateChange>([&](const std::string& accountId,
     258             :                                                                        const std::string& callId,
     259             :                                                                        const std::string& state,
     260             :                                                                        signed) {
     261          11 :             if (aliceData.accountId_ == accountId)
     262           5 :                 onCallStateChange(accountId, callId, state, aliceData);
     263           6 :             else if (bobData.accountId_ == accountId)
     264           6 :                 onCallStateChange(accountId, callId, state, bobData);
     265          11 :         }));
     266             : 
     267          10 :     libjami::registerSignalHandlers(signalHandlers);
     268          10 :     std::ifstream file("plugins/plugin.yml");
     269          10 :     assert(file.is_open());
     270          10 :     YAML::Node node = YAML::Load(file);
     271             : 
     272          10 :     assert(node.IsMap());
     273             : 
     274          10 :     name_ = node["plugin"].as<std::string>();
     275          10 :     certPath_ = node["cert"].as<std::string>();
     276          10 :     cert_ = std::make_unique<dht::crypto::Certificate>(fileutils::loadFile(node["jplDirectory"].as<std::string>() + DIR_SEPARATOR_CH + certPath_));
     277          10 :     dht::crypto::TrustList trust;
     278          10 :     trust.add(*cert_);
     279          10 :     jplPath_ = node["jplDirectory"].as<std::string>() + DIR_SEPARATOR_CH + name_ + ".jpl";
     280          10 :     installationPath_ = fileutils::get_data_dir() / "plugins" / name_;
     281          10 :     mediaHandlers_ = node["mediaHandlers"].as<std::vector<std::string>>();
     282          10 :     chatHandlers_ = node["chatHandlers"].as<std::vector<std::string>>();
     283          10 :     pluginNotFoundPath_ = node["jplDirectory"].as<std::string>() + DIR_SEPARATOR_CH + "fakePlugin.jpl";
     284          10 :     pluginCertNotFound_ = node["jplDirectory"].as<std::string>() + DIR_SEPARATOR_CH + node["pluginCertNotFound"].as<std::string>() + ".jpl";
     285          10 :     pluginNotSign_ = node["jplDirectory"].as<std::string>() + DIR_SEPARATOR_CH + node["pluginNotSign"].as<std::string>() + ".jpl";
     286          10 :     pluginFileNotSign_ = node["jplDirectory"].as<std::string>() + DIR_SEPARATOR_CH + node["pluginFileNotSign"].as<std::string>() + ".jpl";
     287          10 :     pluginManifestChanged_ = node["jplDirectory"].as<std::string>() + DIR_SEPARATOR_CH + node["pluginManifestChanged"].as<std::string>() + ".jpl";
     288          10 :     pluginNotSignByIssuer_ = node["jplDirectory"].as<std::string>() + DIR_SEPARATOR_CH + node["pluginNotSignByIssuer"].as<std::string>() + ".jpl";
     289          10 : }
     290             : 
     291             : void
     292          10 : PluginsTest::tearDown()
     293             : {
     294          10 :     libjami::unregisterSignalHandlers();
     295          30 :     wait_for_removal_of({aliceData.accountId_, bobData.accountId_});
     296          10 : }
     297             : 
     298             : void
     299           1 : PluginsTest::testEnable()
     300             : {
     301           1 :     Manager::instance().pluginPreferences.setPluginsEnabled(true);
     302           1 :     CPPUNIT_ASSERT(Manager::instance().pluginPreferences.getPluginsEnabled());
     303           1 :     Manager::instance().pluginPreferences.setPluginsEnabled(false);
     304           1 :     CPPUNIT_ASSERT(!Manager::instance().pluginPreferences.getPluginsEnabled());
     305           1 : }
     306             : 
     307             : void
     308           1 : PluginsTest::testSignatureVerification()
     309             : {
     310             :     // Test valid case
     311           1 :     Manager::instance().pluginPreferences.setPluginsEnabled(true);
     312           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().checkPluginSignatureValidity(jplPath_, cert_.get()));
     313           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().checkPluginSignatureFile(jplPath_));
     314           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().checkPluginSignature(jplPath_, cert_.get()));
     315             : 
     316           1 :     std::string pluginNotFoundPath = "fakePlugin.jpl";
     317             :     // Test with a plugin that does not exist
     318           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().checkPluginSignatureFile(pluginNotFoundPath_));
     319           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().checkPluginSignature(pluginNotFoundPath_, cert_.get()));
     320             :     // Test with a plugin that does not have a signature
     321           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().checkPluginSignatureFile(pluginNotSign_));
     322             :     // Test with a plugin that does not have a file not signed
     323           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().checkPluginSignatureFile(pluginFileNotSign_));
     324           1 :     auto notCertSign = std::make_unique<dht::crypto::Certificate>();
     325             :     // Test with wrong certificate
     326           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().checkPluginSignatureValidity(jplPath_, notCertSign.get()));
     327             :     // Test with wrong signature
     328           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().checkPluginSignatureValidity(pluginManifestChanged_, cert_.get()));
     329             : 
     330           1 : }
     331             : 
     332             : void
     333           1 : PluginsTest::testCertificateVerification()
     334             : {
     335             : 
     336           1 :     std::string pluginNotFoundPath = "fakePlugin.jpl";
     337           1 :     Manager::instance().pluginPreferences.setPluginsEnabled(true);
     338           1 :     auto pluginCert = PluginUtils::readPluginCertificateFromArchive(jplPath_);
     339           1 :     Manager::instance().getJamiPluginManager().addPluginAuthority(*pluginCert);
     340           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().checkPluginCertificate(jplPath_, true)->toString() == pluginCert->toString());
     341           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().checkPluginCertificate(jplPath_, false)->toString() == pluginCert->toString());
     342             :     // create a plugin with not the same certificate
     343             : 
     344           1 :     auto pluginCertNotSignByIssuer = PluginUtils::readPluginCertificateFromArchive(pluginNotSignByIssuer_);
     345           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().checkPluginCertificate(pluginNotSignByIssuer_, true)->toString() == pluginCertNotSignByIssuer->toString());
     346             :     // Test with a plugin that does not exist
     347           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().checkPluginCertificate(pluginNotFoundPath, false));
     348             :     // Test with a plugin that does not have a certificate
     349           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().checkPluginCertificate(pluginCertNotFound_, false));
     350           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().checkPluginCertificate(pluginNotSignByIssuer_, false));
     351           1 : }
     352             : 
     353             : void
     354           1 : PluginsTest::testLoad()
     355             : {
     356           1 :     Manager::instance().pluginPreferences.setPluginsEnabled(true);
     357           1 :     auto loadedPlugins = Manager::instance().getJamiPluginManager().getLoadedPlugins();
     358           1 :     CPPUNIT_ASSERT(loadedPlugins.empty());
     359           1 :     Manager::instance().getJamiPluginManager().installPlugin(jplPath_, true);
     360           1 :     Manager::instance().getJamiPluginManager().loadPlugins();
     361           1 :     loadedPlugins = Manager::instance().getJamiPluginManager().getLoadedPlugins();
     362           1 :     CPPUNIT_ASSERT(!loadedPlugins.empty());
     363           1 : }
     364             : 
     365             : void
     366           1 : PluginsTest::testInstallAndLoad()
     367             : {
     368           1 :     Manager::instance().pluginPreferences.setPluginsEnabled(true);
     369             : 
     370           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().installPlugin(jplPath_, true));
     371           1 :     auto installedPlugins = Manager::instance().getJamiPluginManager().getInstalledPlugins();
     372           1 :     CPPUNIT_ASSERT(!installedPlugins.empty());
     373           1 :     CPPUNIT_ASSERT(std::find(installedPlugins.begin(),
     374             :                              installedPlugins.end(),
     375             :                              installationPath_)
     376             :                    != installedPlugins.end());
     377             : 
     378           1 :     auto loadedPlugins = Manager::instance().getJamiPluginManager().getLoadedPlugins();
     379           1 :     CPPUNIT_ASSERT(!loadedPlugins.empty());
     380           1 :     CPPUNIT_ASSERT(std::find(loadedPlugins.begin(),
     381             :                              loadedPlugins.end(),
     382             :                              installationPath_)
     383             :                    != loadedPlugins.end());
     384             : 
     385           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().unloadPlugin(installationPath_));
     386           1 :     loadedPlugins = Manager::instance().getJamiPluginManager().getLoadedPlugins();
     387           1 :     CPPUNIT_ASSERT(std::find(loadedPlugins.begin(),
     388             :                              loadedPlugins.end(),
     389             :                              installationPath_)
     390             :                    == loadedPlugins.end());
     391             : 
     392           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().uninstallPlugin(installationPath_));
     393           1 :     installedPlugins = Manager::instance().getJamiPluginManager().getInstalledPlugins();
     394           1 :     CPPUNIT_ASSERT(std::find(installedPlugins.begin(),
     395             :                              installedPlugins.end(),
     396             :                              installationPath_)
     397             :                    == installedPlugins.end());
     398             : 
     399           1 : }
     400             : 
     401             : void
     402           1 : PluginsTest::testHandlers()
     403             : {
     404           1 :     Manager::instance().pluginPreferences.setPluginsEnabled(true);
     405             : 
     406           1 :     Manager::instance().getJamiPluginManager().installPlugin(jplPath_, true);
     407             : 
     408           1 :     auto mediaHandlers = Manager::instance().getJamiPluginManager().getCallServicesManager().getCallMediaHandlers();
     409           1 :     auto chatHandlers = Manager::instance().getJamiPluginManager().getChatServicesManager().getChatHandlers();
     410             : 
     411           1 :     auto handlerLoaded = mediaHandlers_.size() + chatHandlers_.size(); // number of handlers expected
     412           3 :     for (auto handler : mediaHandlers)
     413             :     {
     414           2 :         auto details = Manager::instance().getJamiPluginManager().getCallServicesManager().getCallMediaHandlerDetails(handler);
     415             :         // check details expected for the test plugin
     416           2 :         if(std::find(mediaHandlers_.begin(),
     417             :                         mediaHandlers_.end(),
     418           4 :                         details["name"])
     419           6 :                    != mediaHandlers_.end()) {
     420           2 :             handlerLoaded--;
     421             :         }
     422           2 :     }
     423           2 :     for (auto handler : chatHandlers)
     424             :     {
     425           1 :         auto details = Manager::instance().getJamiPluginManager().getChatServicesManager().getChatHandlerDetails(handler);
     426             :         // check details expected for the test plugin
     427           1 :         if(std::find(chatHandlers_.begin(),
     428             :                         chatHandlers_.end(),
     429           2 :                         details["name"])
     430           3 :                    != chatHandlers_.end()) {
     431           1 :             handlerLoaded--;
     432             :         }
     433           1 :     }
     434             : 
     435           1 :     CPPUNIT_ASSERT(!handlerLoaded); // All expected handlers were found
     436           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().uninstallPlugin(installationPath_));
     437           1 : }
     438             : 
     439             : void
     440           1 : PluginsTest::testDetailsAndPreferences()
     441             : {
     442           1 :     Manager::instance().pluginPreferences.setPluginsEnabled(true);
     443           1 :     Manager::instance().getJamiPluginManager().installPlugin(jplPath_, true);
     444             :     // Unload now to avoid reloads when changing the preferences
     445           1 :     Manager::instance().getJamiPluginManager().unloadPlugin(installationPath_);
     446             : 
     447             :     // Details
     448           1 :     auto details = Manager::instance().getJamiPluginManager().getPluginDetails(installationPath_);
     449           1 :     CPPUNIT_ASSERT(details["name"] == name_);
     450             : 
     451             :     // Get-set-reset - no account
     452           2 :     auto preferences = Manager::instance().getJamiPluginManager().getPluginPreferences(installationPath_, "");
     453           1 :     CPPUNIT_ASSERT(!preferences.empty());
     454           2 :     auto preferencesValuesOrig = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, "");
     455             : 
     456           1 :     std::string preferenceNewValue = aliceData.accountId_;
     457           2 :     auto key = preferences[0]["key"];
     458           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().setPluginPreference(installationPath_, "", key, preferenceNewValue));
     459             : 
     460             :     // Test global preference change
     461           2 :     auto preferencesValuesNew = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, "");
     462           1 :     CPPUNIT_ASSERT(preferencesValuesOrig[key] != preferencesValuesNew[key]);
     463           1 :     CPPUNIT_ASSERT(preferencesValuesNew[key] == preferenceNewValue);
     464             : 
     465             :     // Test global preference change in an account
     466           1 :     preferencesValuesNew = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, aliceData.accountId_);
     467           1 :     CPPUNIT_ASSERT(preferencesValuesOrig[key] != preferencesValuesNew[key]);
     468           1 :     CPPUNIT_ASSERT(preferencesValuesNew[key] == preferenceNewValue);
     469             : 
     470             :     // Test reset global preference change
     471           1 :     Manager::instance().getJamiPluginManager().resetPluginPreferencesValuesMap(installationPath_, "");
     472           1 :     preferencesValuesNew = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, "");
     473           1 :     CPPUNIT_ASSERT(preferencesValuesOrig[key] == preferencesValuesNew[key]);
     474           1 :     CPPUNIT_ASSERT(preferencesValuesNew[key] != preferenceNewValue);
     475             : 
     476             :     // Get-set-reset - alice account
     477           1 :     preferences = Manager::instance().getJamiPluginManager().getPluginPreferences(installationPath_, aliceData.accountId_);
     478           1 :     CPPUNIT_ASSERT(!preferences.empty());
     479           1 :     preferencesValuesOrig = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, aliceData.accountId_);
     480           1 :     auto preferencesValuesBobOrig = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, bobData.accountId_);
     481             : 
     482           1 :     key = preferences[0]["key"];
     483           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().setPluginPreference(installationPath_, aliceData.accountId_, key, preferenceNewValue));
     484             : 
     485             :     // Test account preference change
     486           1 :     preferencesValuesNew = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, aliceData.accountId_);
     487           1 :     auto preferencesValuesBobNew = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, bobData.accountId_);
     488           1 :     CPPUNIT_ASSERT(preferencesValuesBobNew[key] == preferencesValuesBobOrig[key]);
     489           1 :     CPPUNIT_ASSERT(preferencesValuesOrig[key] != preferencesValuesNew[key]);
     490           1 :     CPPUNIT_ASSERT(preferencesValuesOrig[key] == preferencesValuesBobOrig[key]);
     491           1 :     CPPUNIT_ASSERT(preferencesValuesNew[key] != preferencesValuesBobOrig[key]);
     492           1 :     CPPUNIT_ASSERT(preferencesValuesNew[key] == preferenceNewValue);
     493             : 
     494             :     // Test account preference change with global preference reset
     495           1 :     Manager::instance().getJamiPluginManager().resetPluginPreferencesValuesMap(installationPath_, "");
     496           1 :     preferencesValuesNew = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, aliceData.accountId_);
     497           1 :     preferencesValuesBobNew = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, bobData.accountId_);
     498           1 :     CPPUNIT_ASSERT(preferencesValuesBobNew[key] == preferencesValuesBobOrig[key]);
     499           1 :     CPPUNIT_ASSERT(preferencesValuesOrig[key] != preferencesValuesNew[key]);
     500           1 :     CPPUNIT_ASSERT(preferencesValuesOrig[key] == preferencesValuesBobOrig[key]);
     501           1 :     CPPUNIT_ASSERT(preferencesValuesNew[key] != preferencesValuesBobOrig[key]);
     502           1 :     CPPUNIT_ASSERT(preferencesValuesNew[key] == preferenceNewValue);
     503             : 
     504             :     // Test account preference reset
     505           1 :     Manager::instance().getJamiPluginManager().resetPluginPreferencesValuesMap(installationPath_, aliceData.accountId_);
     506           1 :     preferencesValuesNew = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, aliceData.accountId_);
     507           1 :     preferencesValuesBobNew = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, bobData.accountId_);
     508           1 :     CPPUNIT_ASSERT(preferencesValuesBobNew[key] == preferencesValuesBobOrig[key]);
     509           1 :     CPPUNIT_ASSERT(preferencesValuesOrig[key] == preferencesValuesNew[key]);
     510           1 :     CPPUNIT_ASSERT(preferencesValuesOrig[key] == preferencesValuesBobOrig[key]);
     511           1 :     CPPUNIT_ASSERT(preferencesValuesNew[key] == preferencesValuesBobOrig[key]);
     512           1 :     CPPUNIT_ASSERT(preferencesValuesNew[key] != preferenceNewValue);
     513             : 
     514           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().uninstallPlugin(installationPath_));
     515           1 : }
     516             : 
     517             : void
     518           1 : PluginsTest::testTranslations()
     519             : {
     520           1 :     Manager::instance().pluginPreferences.setPluginsEnabled(true);
     521           1 :     setenv("JAMI_LANG", "en", true);
     522           1 :     Manager::instance().getJamiPluginManager().installPlugin(jplPath_, true);
     523             : 
     524           2 :     auto preferencesValuesEN = Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, "");
     525           1 :     auto detailsValuesEN = Manager::instance().getJamiPluginManager().getPluginDetails(installationPath_, true);
     526             : 
     527           1 :     CPPUNIT_ASSERT(!preferencesValuesEN.empty());
     528           1 :     CPPUNIT_ASSERT(!detailsValuesEN.empty());
     529             : 
     530           1 :     setenv("JAMI_LANG", "fr", true);
     531             : 
     532           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, "") != preferencesValuesEN);
     533           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().getPluginDetails(installationPath_, true) != detailsValuesEN);
     534             : 
     535           1 :     setenv("JAMI_LANG", "en", true);
     536             : 
     537           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().getPluginPreferencesValuesMap(installationPath_, "") == preferencesValuesEN);
     538           1 :     CPPUNIT_ASSERT(Manager::instance().getJamiPluginManager().getPluginDetails(installationPath_, true) == detailsValuesEN);
     539             : 
     540           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().uninstallPlugin(installationPath_));
     541           1 : }
     542             : 
     543             : bool
     544           3 : PluginsTest::waitForSignal(CallData& callData,
     545             :                             const std::string& expectedSignal,
     546             :                             const std::string& expectedEvent)
     547             : {
     548           3 :     const std::chrono::seconds TIME_OUT {30};
     549           3 :     std::unique_lock lock {callData.mtx_};
     550             : 
     551             :     // Combined signal + event (if any).
     552           3 :     std::string sigEvent(expectedSignal);
     553           3 :     if (not expectedEvent.empty())
     554           2 :         sigEvent += "::" + expectedEvent;
     555             : 
     556           3 :     JAMI_INFO("[%s] is waiting for [%s] signal/event", callData.alias_.c_str(), sigEvent.c_str());
     557             : 
     558           3 :     auto res = callData.cv_.wait_for(lock, TIME_OUT, [&] {
     559             :         // Search for the expected signal in list of received signals.
     560           4 :         bool pred = false;
     561          13 :         for (auto it = callData.signals_.begin(); it != callData.signals_.end(); it++) {
     562             :             // The predicate is true if the signal names match, and if the
     563             :             // expectedEvent is not empty, the events must also match.
     564          12 :             if (it->name_ == expectedSignal
     565          12 :                 and (expectedEvent.empty() or it->event_ == expectedEvent)) {
     566           3 :                 pred = true;
     567             :                 // Done with this signal.
     568           3 :                 callData.signals_.erase(it);
     569           3 :                 break;
     570             :             }
     571             :         }
     572             : 
     573           4 :         return pred;
     574             :     });
     575             : 
     576           3 :     if (not res) {
     577           0 :         JAMI_ERR("[%s] waiting for signal/event [%s] timed-out!",
     578             :                  callData.alias_.c_str(),
     579             :                  sigEvent.c_str());
     580             : 
     581           0 :         JAMI_INFO("[%s] currently has the following signals:", callData.alias_.c_str());
     582             : 
     583           0 :         for (auto const& sig : callData.signals_) {
     584           0 :             JAMI_INFO() << "\tSignal [" << sig.name_
     585           0 :                         << (sig.event_.empty() ? "" : ("::" + sig.event_)) << "]";
     586             :         }
     587             :     }
     588             : 
     589           3 :     return res;
     590           3 : }
     591             : 
     592             : void
     593           1 : PluginsTest::testCall()
     594             : {
     595           1 :     Manager::instance().pluginPreferences.setPluginsEnabled(true);
     596           1 :     Manager::instance().getJamiPluginManager().installPlugin(jplPath_, true);
     597             : 
     598             :     // alice calls bob
     599             :     // for handler available, toggle - check status - untoggle - checkstatus
     600             :     // end call
     601             : 
     602           1 :     MediaAttribute defaultAudio(MediaType::MEDIA_AUDIO);
     603           1 :     defaultAudio.label_ = "audio_0";
     604           1 :     defaultAudio.enabled_ = true;
     605             : 
     606           1 :     MediaAttribute defaultVideo(MediaType::MEDIA_VIDEO);
     607           1 :     defaultVideo.label_ = "video_0";
     608           1 :     defaultVideo.enabled_ = true;
     609             : 
     610           1 :     std::vector<MediaAttribute> request;
     611           1 :     std::vector<MediaAttribute> answer;
     612             :     // First offer/answer
     613           1 :     request.emplace_back(MediaAttribute(defaultAudio));
     614           1 :     request.emplace_back(MediaAttribute(defaultVideo));
     615           1 :     answer.emplace_back(MediaAttribute(defaultAudio));
     616           1 :     answer.emplace_back(MediaAttribute(defaultVideo));
     617             : 
     618           1 :     JAMI_INFO("Start call between alice and Bob");
     619           1 :     aliceData.callId_ = libjami::placeCallWithMedia(aliceData.accountId_, bobData.userName_, MediaAttribute::mediaAttributesToMediaMaps(request));
     620           1 :     CPPUNIT_ASSERT(not aliceData.callId_.empty());
     621             : 
     622             :     auto aliceCall = std::static_pointer_cast<SIPCall>(
     623           1 :         Manager::instance().getCallFromCallID(aliceData.callId_));
     624           1 :     CPPUNIT_ASSERT(aliceCall);
     625             : 
     626           1 :     aliceData.callId_ = aliceCall->getCallId();
     627             : 
     628           1 :     JAMI_INFO("ALICE [%s] started a call with BOB [%s] and wait for answer",
     629             :               aliceData.accountId_.c_str(),
     630             :               bobData.accountId_.c_str());
     631             : 
     632             :     // Wait for incoming call signal.
     633           1 :     CPPUNIT_ASSERT(waitForSignal(bobData, libjami::CallSignal::IncomingCallWithMedia::name));
     634             : 
     635             :     // Answer the call.
     636             :     {
     637           1 :         libjami::acceptWithMedia(bobData.accountId_, bobData.callId_, MediaAttribute::mediaAttributesToMediaMaps(answer));
     638             :     }
     639             : 
     640           1 :     CPPUNIT_ASSERT_EQUAL(true,
     641             :                          waitForSignal(bobData,
     642             :                                        libjami::CallSignal::StateChange::name,
     643             :                                        libjami::Call::StateEvent::CURRENT));
     644             : 
     645           1 :     JAMI_INFO("BOB answered the call [%s]", bobData.callId_.c_str());
     646             : 
     647           1 :     std::this_thread::sleep_for(std::chrono::seconds(3));
     648           1 :     auto mediaHandlers = Manager::instance().getJamiPluginManager().getCallServicesManager().getCallMediaHandlers();
     649             : 
     650           3 :     for (auto handler : mediaHandlers)
     651             :     {
     652           2 :         auto details = Manager::instance().getJamiPluginManager().getCallServicesManager().getCallMediaHandlerDetails(handler);
     653             :         // check details expected for the test plugin
     654           2 :         if(std::find(mediaHandlers_.begin(),
     655             :                         mediaHandlers_.end(),
     656           4 :                         details["name"])
     657           6 :                    != mediaHandlers_.end()) {
     658           2 :             Manager::instance().getJamiPluginManager().getCallServicesManager().toggleCallMediaHandler(handler, aliceData.callId_, true);
     659           2 :             auto statusMap = Manager::instance().getJamiPluginManager().getCallServicesManager().getCallMediaHandlerStatus(aliceData.callId_);
     660           2 :             CPPUNIT_ASSERT(std::find(statusMap.begin(), statusMap.end(), handler) != statusMap.end());
     661             : 
     662           2 :             Manager::instance().getJamiPluginManager().getCallServicesManager().toggleCallMediaHandler(handler, aliceData.callId_, false);
     663           2 :             statusMap = Manager::instance().getJamiPluginManager().getCallServicesManager().getCallMediaHandlerStatus(aliceData.callId_);
     664           2 :             CPPUNIT_ASSERT(std::find(statusMap.begin(), statusMap.end(), handler) == statusMap.end());
     665           2 :         }
     666           2 :     }
     667             : 
     668           1 :     std::this_thread::sleep_for(std::chrono::seconds(3));
     669             :     // Bob hang-up.
     670           1 :     JAMI_INFO("Hang up BOB's call and wait for ALICE to hang up");
     671           1 :     libjami::hangUp(bobData.accountId_, bobData.callId_);
     672             : 
     673           1 :     CPPUNIT_ASSERT_EQUAL(true,
     674             :                          waitForSignal(aliceData,
     675             :                                        libjami::CallSignal::StateChange::name,
     676             :                                        libjami::Call::StateEvent::HUNGUP));
     677             : 
     678           1 :     JAMI_INFO("Call terminated on both sides");
     679           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().uninstallPlugin(installationPath_));
     680           1 : }
     681             : 
     682             : void
     683           1 : PluginsTest::testMessage()
     684             : {
     685           1 :     Manager::instance().pluginPreferences.setPluginsEnabled(true);
     686           1 :     Manager::instance().getJamiPluginManager().installPlugin(jplPath_, true);
     687             : 
     688             :     // alice and bob chat
     689             :     // for handler available, toggle - check status - untoggle - checkstatus
     690             :     // end call
     691             : 
     692           1 :     std::mutex mtx;
     693           1 :     std::unique_lock lk {mtx};
     694           1 :     std::condition_variable cv;
     695           1 :     std::map<std::string, std::shared_ptr<libjami::CallbackWrapperBase>> confHandlers;
     696           1 :     auto messageBobReceived = 0, messageAliceReceived = 0;
     697           1 :     bool requestReceived = false;
     698           1 :     bool conversationReady = false;
     699           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::MessageReceived>(
     700           4 :         [&](const std::string& accountId,
     701             :             const std::string& /* conversationId */,
     702             :             std::map<std::string, std::string> /*message*/) {
     703           4 :             if (accountId == bobData.accountId_) {
     704           1 :                 messageBobReceived += 1;
     705             :             } else {
     706           3 :                 messageAliceReceived += 1;
     707             :             }
     708           4 :             cv.notify_one();
     709           4 :         }));
     710           1 :     confHandlers.insert(
     711           2 :         libjami::exportable_callback<libjami::ConversationSignal::ConversationRequestReceived>(
     712           1 :             [&](const std::string& /*accountId*/,
     713             :                 const std::string& /* conversationId */,
     714             :                 std::map<std::string, std::string> /*metadatas*/) {
     715           1 :                 requestReceived = true;
     716           1 :                 cv.notify_one();
     717           1 :             }));
     718           1 :     confHandlers.insert(libjami::exportable_callback<libjami::ConversationSignal::ConversationReady>(
     719           2 :         [&](const std::string& accountId, const std::string& /* conversationId */) {
     720           2 :             if (accountId == bobData.accountId_) {
     721           1 :                 conversationReady = true;
     722           1 :                 cv.notify_one();
     723             :             }
     724           2 :         }));
     725           1 :     libjami::registerSignalHandlers(confHandlers);
     726             : 
     727           1 :     auto convId = libjami::startConversation(aliceData.accountId_);
     728             : 
     729           1 :     libjami::addConversationMember(aliceData.accountId_, convId, bobData.userName_);
     730           4 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; }));
     731             : 
     732           1 :     libjami::acceptConversationRequest(bobData.accountId_, convId);
     733           3 :     CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; }));
     734             : 
     735             :     // Assert that repository exists
     736           2 :     auto repoPath = fileutils::get_data_dir() / bobData.accountId_
     737           4 :                     / "conversations" / convId;
     738           1 :     CPPUNIT_ASSERT(std::filesystem::is_directory(repoPath));
     739             :     // Wait that alice sees Bob
     740           3 :     cv.wait_for(lk, 30s, [&]() { return messageAliceReceived == 2; });
     741             : 
     742           1 :     auto chatHandlers = Manager::instance().getJamiPluginManager().getChatServicesManager().getChatHandlers();
     743             : 
     744           2 :     for (auto handler : chatHandlers)
     745             :     {
     746           1 :         auto details = Manager::instance().getJamiPluginManager().getChatServicesManager().getChatHandlerDetails(handler);
     747             :         // check details expected for the test plugin
     748           1 :         if(std::find(chatHandlers_.begin(),
     749             :                         chatHandlers_.end(),
     750           2 :                         details["name"])
     751           3 :                    != chatHandlers_.end()) {
     752           1 :             Manager::instance().getJamiPluginManager().getChatServicesManager().toggleChatHandler(handler, aliceData.accountId_, convId, true);
     753           1 :             auto statusMap = Manager::instance().getJamiPluginManager().getChatServicesManager().getChatHandlerStatus(aliceData.accountId_, convId);
     754           1 :             CPPUNIT_ASSERT(std::find(statusMap.begin(), statusMap.end(), handler) != statusMap.end());
     755             : 
     756           1 :             libjami::sendMessage(aliceData.accountId_, convId, "hi"s, "");
     757           4 :             cv.wait_for(lk, 30s, [&]() { return messageBobReceived == 1; });
     758             : 
     759           1 :             Manager::instance().getJamiPluginManager().getChatServicesManager().toggleChatHandler(handler, aliceData.accountId_, convId, false);
     760           1 :             statusMap = Manager::instance().getJamiPluginManager().getChatServicesManager().getChatHandlerStatus(aliceData.accountId_, convId);
     761           1 :             CPPUNIT_ASSERT(std::find(statusMap.begin(), statusMap.end(), handler) == statusMap.end());
     762           1 :         }
     763           1 :     }
     764             : 
     765           1 :     CPPUNIT_ASSERT(!Manager::instance().getJamiPluginManager().uninstallPlugin(installationPath_));
     766           1 : }
     767             : 
     768             : } // namespace test
     769             : } // namespace jami
     770             : 
     771           1 : RING_TEST_RUNNER(jami::test::PluginsTest::name())

Generated by: LCOV version 1.14