add tests

Change-Id: I0782b614c3028b8e846d995cb5dbb81f04eb563b
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 227118f..21d7b69 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,9 +3,11 @@
 
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
+include(CTest)
 
 find_package (PkgConfig REQUIRED)
 find_package(msgpack REQUIRED QUIET CONFIG NAMES msgpack msgpack-cxx)
+find_package(fmt)
 pkg_check_modules (opendht REQUIRED IMPORTED_TARGET opendht>=2.6.0)
 pkg_check_modules (pjproject REQUIRED IMPORTED_TARGET libpjproject)
 
@@ -30,8 +32,25 @@
 )
 
 add_library(dhtnet ${dhtnet_SOURCES})
-target_link_libraries(dhtnet PUBLIC PkgConfig::opendht msgpack-cxx)
+target_link_libraries(dhtnet PUBLIC PkgConfig::opendht fmt::fmt msgpack-cxx)
 target_include_directories(dhtnet PUBLIC include)
-target_compile_definitions(dhtnet PRIVATE
-    PJ_AUTOCONF=1
-)
+add_compile_definitions(PJ_AUTOCONF=1)
+
+if (BUILD_TESTING AND NOT MSVC)
+    pkg_search_module(Cppunit REQUIRED IMPORTED_TARGET cppunit)
+    add_executable(tests_certstore tests/certstore.cpp)
+    target_link_libraries(tests_certstore PRIVATE dhtnet fmt::fmt PkgConfig::Cppunit)
+    add_test(NAME tests_certstore COMMAND tests_certstore)
+
+    add_executable(tests_connectionManager tests/connectionManager.cpp)
+    target_link_libraries(tests_connectionManager PRIVATE dhtnet fmt::fmt PkgConfig::Cppunit)
+    add_test(NAME tests_connectionManager COMMAND tests_connectionManager)
+
+    #add_executable(tests_fileutils tests/testFileutils.cpp)
+    #target_link_libraries(tests_fileutils PRIVATE dhtnet fmt::fmt PkgConfig::Cppunit)
+    #add_test(NAME tests_fileutils COMMAND tests_fileutils)
+
+    #add_executable(tests_stringutils tests/testString_utils.cpp)
+    #target_link_libraries(tests_stringutils PRIVATE dhtnet fmt::fmt PkgConfig::Cppunit)
+    #add_test(NAME tests_stringutils COMMAND tests_stringutils)
+endif()
\ No newline at end of file
diff --git a/tests/certstore.cpp b/tests/certstore.cpp
new file mode 100644
index 0000000..7d6b75f
--- /dev/null
+++ b/tests/certstore.cpp
@@ -0,0 +1,209 @@
+/*
+ *  Copyright (C) 2004-2023 Savoir-faire Linux Inc.
+ *
+ *  Author: florian Wiesweg <florian.wiesweg@campus.tu-berlin.de>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "test_runner.h"
+#include "certstore.h"
+
+namespace jami {
+namespace test {
+
+class CertStoreTest : public CppUnit::TestFixture
+{
+public:
+    CertStoreTest()
+    {
+    }
+    ~CertStoreTest() { }
+    static std::string name() { return "certstore"; }
+    void setUp();
+    void tearDown();
+
+    std::string aliceId;
+    std::string bobId;
+
+private:
+    void trustStoreTest();
+    void getCertificateWithSplitted();
+
+    CPPUNIT_TEST_SUITE(CertStoreTest);
+    CPPUNIT_TEST(trustStoreTest);
+    CPPUNIT_TEST(getCertificateWithSplitted);
+    CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CertStoreTest, CertStoreTest::name());
+
+void
+CertStoreTest::setUp()
+{
+    /*auto actors = load_actors_and_wait_for_announcement("actors/alice-bob.yml");
+    aliceId = actors["alice"];
+    bobId = actors["bob"];*/
+}
+
+void
+CertStoreTest::tearDown()
+{
+    //wait_for_removal_of({aliceId, bobId});
+}
+
+void
+CertStoreTest::trustStoreTest()
+{
+    //auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+
+    auto ca = dht::crypto::generateIdentity("test CA");
+    auto account = dht::crypto::generateIdentity("test account", ca, 4096, true);
+    auto device = dht::crypto::generateIdentity("test device", account);
+    auto device2 = dht::crypto::generateIdentity("test device 2", account);
+    /*auto storeSize = aliceAccount->certStore().getPinnedCertificates().size();
+    auto id = ca.second->getId().toString();
+    auto pinned = aliceAccount->certStore().getPinnedCertificates();
+    CPPUNIT_ASSERT(std::find_if(pinned.begin(), pinned.end(), [&](auto v) { return v == id; })
+                   == pinned.end());
+
+    // Test certificate status
+    auto certAllowed = aliceAccount->accountManager()->getCertificatesByStatus(
+        jami::tls::TrustStore::PermissionStatus::ALLOWED);
+    CPPUNIT_ASSERT(
+        std::find_if(certAllowed.begin(), certAllowed.end(), [&](auto v) { return v == id; })
+        == certAllowed.end());
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->getCertificateStatus(id)
+                   == jami::tls::TrustStore::PermissionStatus::UNDEFINED);
+    aliceAccount->setCertificateStatus(ca.second, jami::tls::TrustStore::PermissionStatus::ALLOWED);
+    certAllowed = aliceAccount->accountManager()->getCertificatesByStatus(
+        jami::tls::TrustStore::PermissionStatus::ALLOWED);
+    CPPUNIT_ASSERT(
+        std::find_if(certAllowed.begin(), certAllowed.end(), [&](auto v) { return v == id; })
+        != certAllowed.end());
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->getCertificateStatus(id)
+                   == jami::tls::TrustStore::PermissionStatus::ALLOWED);
+    aliceAccount->setCertificateStatus(ca.second, jami::tls::TrustStore::PermissionStatus::UNDEFINED);
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->getCertificateStatus(id)
+                   == jami::tls::TrustStore::PermissionStatus::UNDEFINED);
+    aliceAccount->setCertificateStatus(ca.second, jami::tls::TrustStore::PermissionStatus::ALLOWED);
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->getCertificateStatus(id)
+                   == jami::tls::TrustStore::PermissionStatus::ALLOWED);
+
+    // Test getPinnedCertificates
+    pinned = aliceAccount->certStore().getPinnedCertificates();
+    CPPUNIT_ASSERT(pinned.size() == storeSize + 2);
+    CPPUNIT_ASSERT(std::find_if(pinned.begin(), pinned.end(), [&](auto v) { return v == id; })
+                   != pinned.end());
+
+    // Test findCertificateByUID & findIssuer
+    CPPUNIT_ASSERT(!aliceAccount->certStore().findCertificateByUID("NON_EXISTING_ID"));
+    auto cert = aliceAccount->certStore().findCertificateByUID(id);
+    CPPUNIT_ASSERT(cert);
+    auto issuer = aliceAccount->certStore().findIssuer(cert);
+    CPPUNIT_ASSERT(issuer);
+    CPPUNIT_ASSERT(issuer->getId().toString() == id);
+
+    // Test is allowed
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->isAllowed(*ca.second));
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->isAllowed(*account.second));
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->isAllowed(*device.second));
+
+    // Ban device
+    aliceAccount->setCertificateStatus(device.second, jami::tls::TrustStore::PermissionStatus::BANNED);
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->getCertificateStatus(device.second->getId().toString())
+                   == jami::tls::TrustStore::PermissionStatus::BANNED);
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->getCertificateStatus(id)
+                   == jami::tls::TrustStore::PermissionStatus::ALLOWED);
+
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->isAllowed(*ca.second));
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->isAllowed(*account.second));
+    CPPUNIT_ASSERT(not aliceAccount->accountManager()->isAllowed(*device.second));
+
+    // Ban account
+    aliceAccount->setCertificateStatus(account.second, jami::tls::TrustStore::PermissionStatus::BANNED);
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->getCertificateStatus(account.second->getId().toString())
+                   == jami::tls::TrustStore::PermissionStatus::BANNED);
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->isAllowed(*ca.second));
+    CPPUNIT_ASSERT(not aliceAccount->accountManager()->isAllowed(*account.second));
+    CPPUNIT_ASSERT(not aliceAccount->accountManager()->isAllowed(*device2.second));
+
+    // Unban account
+    aliceAccount->setCertificateStatus(account.second,
+                                    jami::tls::TrustStore::PermissionStatus::ALLOWED);
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->getCertificateStatus(account.second->getId().toString())
+                   == jami::tls::TrustStore::PermissionStatus::ALLOWED);
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->isAllowed(*ca.second));
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->isAllowed(*account.second));
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->isAllowed(*device2.second));
+
+    // Ban CA
+    aliceAccount->setCertificateStatus(ca.second, jami::tls::TrustStore::PermissionStatus::BANNED);
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->getCertificateStatus(ca.second->getId().toString())
+                   == jami::tls::TrustStore::PermissionStatus::BANNED);
+    CPPUNIT_ASSERT(not aliceAccount->accountManager()->isAllowed(*ca.second));
+    CPPUNIT_ASSERT(not aliceAccount->accountManager()->isAllowed(*account.second));
+    CPPUNIT_ASSERT(not aliceAccount->accountManager()->isAllowed(*device2.second));
+
+    aliceAccount->setCertificateStatus(ca.second, jami::tls::TrustStore::PermissionStatus::BANNED);
+    CPPUNIT_ASSERT(aliceAccount->accountManager()->getCertificateStatus(ca.second->getId().toString())
+                   == jami::tls::TrustStore::PermissionStatus::BANNED);
+
+    // Test unpin
+    aliceAccount->certStore().unpinCertificate(id);
+    pinned = aliceAccount->certStore().getPinnedCertificates();
+    CPPUNIT_ASSERT(std::find_if(pinned.begin(), pinned.end(), [&](auto v) { return v == id; })
+                   == pinned.end());
+
+    // Test statusToStr
+    CPPUNIT_ASSERT(strcmp(jami::tls::statusToStr(jami::tls::TrustStatus::TRUSTED),
+                          libjami::Certificate::TrustStatus::TRUSTED)
+                   == 0);
+    CPPUNIT_ASSERT(strcmp(jami::tls::statusToStr(jami::tls::TrustStatus::UNTRUSTED),
+                          libjami::Certificate::TrustStatus::UNTRUSTED)
+                   == 0);*/
+}
+
+void
+CertStoreTest::getCertificateWithSplitted()
+{
+    //auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto ca = dht::crypto::generateIdentity("test CA");
+    auto account = dht::crypto::generateIdentity("test account", ca, 4096, true);
+    auto device = dht::crypto::generateIdentity("test device", account);
+
+    auto caCert = std::make_shared<dht::crypto::Certificate>(ca.second->toString(false));
+    auto accountCert = std::make_shared<dht::crypto::Certificate>(account.second->toString(false));
+    auto devicePartialCert = std::make_shared<dht::crypto::Certificate>(
+        device.second->toString(false));
+
+    /*aliceAccount->certStore().pinCertificate(caCert);
+    aliceAccount->certStore().pinCertificate(accountCert);
+    aliceAccount->certStore().pinCertificate(devicePartialCert);
+
+    auto fullCert = aliceAccount->certStore().getCertificate(device.second->getId().toString());
+    CPPUNIT_ASSERT(fullCert->issuer && fullCert->issuer->getUID() == accountCert->getUID());
+    CPPUNIT_ASSERT(fullCert->issuer->issuer
+                   && fullCert->issuer->issuer->getUID() == caCert->getUID());*/
+}
+
+} // namespace test
+} // namespace jami
+
+JAMI_TEST_RUNNER(jami::test::CertStoreTest::name());
diff --git a/tests/connectionManager.cpp b/tests/connectionManager.cpp
new file mode 100644
index 0000000..504f49f
--- /dev/null
+++ b/tests/connectionManager.cpp
@@ -0,0 +1,1277 @@
+/*
+ *  Copyright (C) 2017-2023 Savoir-faire Linux Inc.
+ *  Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <condition_variable>
+
+#include "connectionmanager.h"
+#include "multiplexed_socket.h"
+#include "test_runner.h"
+
+using namespace std::literals::chrono_literals;
+
+namespace jami {
+namespace test {
+
+class ConnectionManagerTest : public CppUnit::TestFixture
+{
+public:
+    ConnectionManagerTest() {}
+    ~ConnectionManagerTest() { }
+    static std::string name() { return "ConnectionManager"; }
+    void setUp();
+    void tearDown();
+
+    std::string aliceId;
+    std::string bobId;
+
+private:
+    void testConnectDevice();
+    void testAcceptConnection();
+    void testMultipleChannels();
+    void testMultipleChannelsOneDeclined();
+    void testMultipleChannelsSameName();
+    void testDeclineConnection();
+    void testSendReceiveData();
+    void testAcceptsICERequest();
+    void testDeclineICERequest();
+    void testChannelRcvShutdown();
+    void testChannelSenderShutdown();
+    void testCloseConnectionWith();
+    void testShutdownCallbacks();
+    void testFloodSocket();
+    void testDestroyWhileSending();
+    void testIsConnecting();
+    void testCanSendBeacon();
+    void testCannotSendBeacon();
+    void testConnectivityChangeTriggerBeacon();
+    void testOnNoBeaconTriggersShutdown();
+    void testShutdownWhileNegotiating();
+
+    CPPUNIT_TEST_SUITE(ConnectionManagerTest);
+    CPPUNIT_TEST(testConnectDevice);
+    CPPUNIT_TEST(testAcceptConnection);
+    CPPUNIT_TEST(testMultipleChannels);
+    CPPUNIT_TEST(testMultipleChannelsOneDeclined);
+    CPPUNIT_TEST(testMultipleChannelsSameName);
+    CPPUNIT_TEST(testDeclineConnection);
+    CPPUNIT_TEST(testSendReceiveData);
+    CPPUNIT_TEST(testAcceptsICERequest);
+    CPPUNIT_TEST(testDeclineICERequest);
+    CPPUNIT_TEST(testChannelRcvShutdown);
+    CPPUNIT_TEST(testChannelSenderShutdown);
+    CPPUNIT_TEST(testCloseConnectionWith);
+    CPPUNIT_TEST(testShutdownCallbacks);
+    CPPUNIT_TEST(testFloodSocket);
+    CPPUNIT_TEST(testDestroyWhileSending);
+    CPPUNIT_TEST(testIsConnecting);
+    CPPUNIT_TEST(testCanSendBeacon);
+    CPPUNIT_TEST(testCannotSendBeacon);
+    CPPUNIT_TEST(testConnectivityChangeTriggerBeacon);
+    CPPUNIT_TEST(testOnNoBeaconTriggersShutdown);
+    CPPUNIT_TEST(testShutdownWhileNegotiating);
+    CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ConnectionManagerTest, ConnectionManagerTest::name());
+
+void
+ConnectionManagerTest::setUp()
+{
+    /*auto actors = load_actors_and_wait_for_announcement("actors/alice-bob.yml");
+    aliceId = actors["alice"];
+    bobId = actors["bob"];
+
+    // Pin certificate from one to another certstore (because we do not perform any DHT operation in this test)
+    auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    bobAccount->certStore().pinCertificate(aliceAccount->identity().second);
+    aliceAccount->certStore().pinCertificate(bobAccount->identity().second);*/
+}
+
+void
+ConnectionManagerTest::tearDown()
+{
+    //wait_for_removal_of({aliceId, bobId});
+}
+
+void
+ConnectionManagerTest::testConnectDevice()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv, cvReceive;
+    bool successfullyConnected = false;
+    bool successfullyReceive = false;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [&successfullyReceive, &cvReceive](const std::shared_ptr<dht::crypto::Certificate>&,
+                                           const std::string& name) {
+            successfullyReceive = name == "git://*";
+            cvReceive.notify_one();
+            return true;
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    CPPUNIT_ASSERT(cvReceive.wait_for(lk, 60s, [&] { return successfullyReceive; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return successfullyConnected; }));*/
+}
+
+void
+ConnectionManagerTest::testAcceptConnection()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+    bool successfullyReceive = false;
+    bool receiverConnected = false;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [&successfullyReceive](const std::shared_ptr<dht::crypto::Certificate>&,
+                               const std::string& name) {
+            successfullyReceive = name == "git://*";
+            return true;
+        });
+
+    bobAccount->connectionManager().onConnectionReady(
+        [&receiverConnected](const DeviceId&,
+                             const std::string& name,
+                             std::shared_ptr<ChannelSocket> socket) {
+            receiverConnected = socket && (name == "git://*");
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] {
+        return successfullyReceive && successfullyConnected && receiverConnected;
+    }));*/
+}
+
+void
+ConnectionManagerTest::testMultipleChannels()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+    bool successfullyConnected2 = false;
+    int receiverConnected = 0;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
+
+    bobAccount->connectionManager().onConnectionReady(
+        [&receiverConnected](const DeviceId&,
+                             const std::string&,
+                             std::shared_ptr<ChannelSocket> socket) {
+            if (socket)
+                receiverConnected += 1;
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "sip://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected2 = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] {
+        return successfullyConnected && successfullyConnected2 && receiverConnected == 2;
+    }));
+    CPPUNIT_ASSERT(aliceAccount->connectionManager().activeSockets() == 1);*/
+}
+
+void
+ConnectionManagerTest::testMultipleChannelsOneDeclined()
+{
+   /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyNotConnected = false;
+    bool successfullyConnected2 = false;
+    int receiverConnected = 0;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string& name) {
+            if (name == "git://*")
+                return false;
+            return true;
+        });
+
+    bobAccount->connectionManager().onConnectionReady(
+        [&](const DeviceId&, const std::string&, std::shared_ptr<ChannelSocket> socket) {
+            if (socket)
+                receiverConnected += 1;
+            cv.notify_one();
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (!socket)
+                                                            successfullyNotConnected = true;
+                                                        cv.notify_one();
+                                                    });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "sip://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket)
+                                                            successfullyConnected2 = true;
+                                                        cv.notify_one();
+                                                    });
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] {
+        return successfullyNotConnected && successfullyConnected2 && receiverConnected == 1;
+    }));
+    CPPUNIT_ASSERT(aliceAccount->connectionManager().activeSockets() == 1);*/
+}
+
+void
+ConnectionManagerTest::testMultipleChannelsSameName()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+    bool successfullyConnected2 = false;
+    int receiverConnected = 0;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
+
+    bobAccount->connectionManager().onConnectionReady(
+        [&receiverConnected](const DeviceId&,
+                             const std::string&,
+                             std::shared_ptr<ChannelSocket> socket) {
+            if (socket)
+                receiverConnected += 1;
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+
+    // We can open two sockets with the same name, it will be two different channel
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected2 = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] {
+        return successfullyConnected && successfullyConnected2 && receiverConnected == 2;
+    }));*/
+}
+
+void
+ConnectionManagerTest::testSendReceiveData()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    std::atomic_int events(0);
+    bool successfullyConnected = false, successfullyConnected2 = false, successfullyReceive = false,
+         receiverConnected = false;
+    const uint8_t buf_other[] = {0x64, 0x65, 0x66, 0x67};
+    const uint8_t buf_test[] = {0x68, 0x69, 0x70, 0x71};
+    bool dataOk = false, dataOk2 = false;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [&successfullyReceive](const std::shared_ptr<dht::crypto::Certificate>&,
+                               const std::string&) {
+            successfullyReceive = true;
+            return true;
+        });
+
+    bobAccount->connectionManager().onConnectionReady(
+        [&](const DeviceId&, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
+            if (socket && (name == "test" || name == "other")) {
+                receiverConnected = true;
+                std::error_code ec;
+                auto res = socket->waitForData(std::chrono::milliseconds(5000), ec);
+                if (res == 4) {
+                    uint8_t buf[4];
+                    socket->read(&buf[0], 4, ec);
+                    if (name == "test")
+                        dataOk = std::equal(std::begin(buf), std::end(buf), std::begin(buf_test));
+                    else
+                        dataOk2 = std::equal(std::begin(buf), std::end(buf), std::begin(buf_other));
+                    events++;
+                    cv.notify_one();
+                }
+            }
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "test",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected = true;
+                                                            std::error_code ec;
+                                                            socket->write(&buf_test[0], 4, ec);
+                                                        }
+                                                        events++;
+                                                        cv.notify_one();
+                                                    });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "other",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected2 = true;
+                                                            std::error_code ec;
+                                                            socket->write(&buf_other[0], 4, ec);
+                                                        }
+                                                        events++;
+                                                        cv.notify_one();
+                                                    });
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] {
+        return events == 4 && successfullyReceive && successfullyConnected && successfullyConnected2
+               && dataOk && dataOk2;
+    }));*/
+}
+
+void
+ConnectionManagerTest::testDeclineConnection()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+    bool successfullyReceive = false;
+    bool receiverConnected = false;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [&successfullyReceive](const std::shared_ptr<dht::crypto::Certificate>&,
+                               const std::string&) {
+            successfullyReceive = true;
+            return false;
+        });
+
+    bobAccount->connectionManager().onConnectionReady(
+        [&receiverConnected](const DeviceId&,
+                             const std::string&,
+                             std::shared_ptr<ChannelSocket> socket) {
+            if (socket)
+                receiverConnected = true;
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    cv.wait_for(lk, 30s);
+    CPPUNIT_ASSERT(successfullyReceive);
+    CPPUNIT_ASSERT(!successfullyConnected);
+    CPPUNIT_ASSERT(!receiverConnected);*/
+}
+
+void
+ConnectionManagerTest::testAcceptsICERequest()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+    bool successfullyReceive = false;
+    bool receiverConnected = false;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
+    bobAccount->connectionManager().onICERequest([&](const DeviceId&) {
+        successfullyReceive = true;
+        return true;
+    });
+
+    bobAccount->connectionManager().onConnectionReady(
+        [&receiverConnected](const DeviceId&,
+                             const std::string& name,
+                             std::shared_ptr<ChannelSocket> socket) {
+            receiverConnected = socket && (name == "git://*");
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] {
+        return successfullyReceive && successfullyConnected && receiverConnected;
+    }));*/
+}
+
+void
+ConnectionManagerTest::testDeclineICERequest()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+    bool successfullyReceive = false;
+    bool receiverConnected = false;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
+    bobAccount->connectionManager().onICERequest([&](const DeviceId&) {
+        successfullyReceive = true;
+        return false;
+    });
+
+    bobAccount->connectionManager().onConnectionReady(
+        [&receiverConnected](const DeviceId&,
+                             const std::string& name,
+                             std::shared_ptr<ChannelSocket> socket) {
+            receiverConnected = socket && (name == "git://*");
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+
+    cv.wait_for(lk, 30s);
+    CPPUNIT_ASSERT(successfullyReceive);
+    CPPUNIT_ASSERT(!receiverConnected);
+    CPPUNIT_ASSERT(!successfullyConnected);*/
+}
+
+void
+ConnectionManagerTest::testChannelRcvShutdown()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+    bool shutdownReceived = false;
+
+    std::shared_ptr<ChannelSocket> bobSock;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
+
+    bobAccount->connectionManager().onConnectionReady(
+        [&](const DeviceId& did, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
+            if (socket && name == "git://*" && did != bobDeviceId) {
+                bobSock = socket;
+                cv.notify_one();
+            }
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            socket->onShutdown([&] {
+                                                                shutdownReceived = true;
+                                                                cv.notify_one();
+                                                            });
+                                                            successfullyConnected = true;
+                                                            cv.notify_one();
+                                                        }
+                                                    });
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return bobSock && successfullyConnected; }));
+    bobSock->shutdown();
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return shutdownReceived; }));*/
+}
+
+void
+ConnectionManagerTest::testChannelSenderShutdown()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable rcv, scv;
+    bool successfullyConnected = false;
+    bool successfullyReceive = false;
+    bool receiverConnected = false;
+    bool shutdownReceived = false;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [&successfullyReceive](const std::shared_ptr<dht::crypto::Certificate>&,
+                               const std::string& name) {
+            successfullyReceive = name == "git://*";
+            return true;
+        });
+
+    bobAccount->connectionManager().onConnectionReady(
+        [&](const DeviceId&, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
+            if (socket) {
+                socket->onShutdown([&] {
+                    shutdownReceived = true;
+                    scv.notify_one();
+                });
+            }
+            receiverConnected = socket && (name == "git://*");
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected = true;
+                                                            rcv.notify_one();
+                                                            socket->shutdown();
+                                                        }
+                                                    });
+
+    rcv.wait_for(lk, 30s);
+    scv.wait_for(lk, 30s);
+    CPPUNIT_ASSERT(shutdownReceived);
+    CPPUNIT_ASSERT(successfullyReceive);
+    CPPUNIT_ASSERT(successfullyConnected);
+    CPPUNIT_ASSERT(receiverConnected);*/
+}
+
+void
+ConnectionManagerTest::testCloseConnectionWith()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+    auto bobUri = bobAccount->getUsername();
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable rcv, scv;
+    std::atomic_int events(0);
+    bool successfullyConnected = false;
+    bool successfullyReceive = false;
+    bool receiverConnected = false;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [&successfullyReceive](const std::shared_ptr<dht::crypto::Certificate>&,
+                               const std::string& name) {
+            successfullyReceive = name == "git://*";
+            return true;
+        });
+
+    bobAccount->connectionManager().onConnectionReady(
+        [&](const DeviceId&, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
+            if (socket) {
+                socket->onShutdown([&] {
+                    events += 1;
+                    scv.notify_one();
+                });
+            }
+            receiverConnected = socket && (name == "git://*");
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            socket->onShutdown([&] {
+                                                                events += 1;
+                                                                scv.notify_one();
+                                                            });
+                                                            successfullyConnected = true;
+                                                            rcv.notify_one();
+                                                        }
+                                                    });
+
+    rcv.wait_for(lk, 30s);
+    // This should trigger onShutdown
+    aliceAccount->connectionManager().closeConnectionsWith(bobUri);
+    CPPUNIT_ASSERT(scv.wait_for(lk, 60s, [&] {
+        return events == 2 && successfullyReceive && successfullyConnected && receiverConnected;
+    }));*/
+}
+
+void
+ConnectionManagerTest::testShutdownCallbacks()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+    auto aliceUri = aliceAccount->getUsername();
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable rcv, chan2cv;
+    bool successfullyConnected = false;
+    bool successfullyReceive = false;
+    bool receiverConnected = false;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [&successfullyReceive, &chan2cv](const std::shared_ptr<dht::crypto::Certificate>&,
+                                         const std::string& name) {
+            if (name == "1") {
+                successfullyReceive = true;
+            } else {
+                chan2cv.notify_one();
+                // Do not return directly. Let the connection be closed
+                std::this_thread::sleep_for(10s);
+            }
+            return true;
+        });
+
+    bobAccount->connectionManager().onConnectionReady(
+        [&](const DeviceId&, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
+            receiverConnected = socket && (name == "1");
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "1",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected = true;
+                                                            rcv.notify_one();
+                                                        }
+                                                    });
+    // Connect first channel. This will initiate a mx sock
+    CPPUNIT_ASSERT(rcv.wait_for(lk, 30s, [&] {
+        return successfullyReceive && successfullyConnected && receiverConnected;
+    }));
+
+    // Connect another channel, but close the connection
+    bool channel2NotConnected = false;
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "2",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        channel2NotConnected = !socket;
+                                                        rcv.notify_one();
+                                                    });
+    chan2cv.wait_for(lk, 30s);
+
+    // This should trigger onShutdown for second callback
+    bobAccount->connectionManager().closeConnectionsWith(aliceUri);
+    CPPUNIT_ASSERT(rcv.wait_for(lk, 30s, [&] { return channel2NotConnected; }));*/
+}
+
+void
+ConnectionManagerTest::testFloodSocket()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+    bool successfullyReceive = false;
+    bool receiverConnected = false;
+    std::shared_ptr<ChannelSocket> rcvSock1, rcvSock2, rcvSock3, sendSock, sendSock2, sendSock3;
+    bobAccount->connectionManager().onChannelRequest(
+        [&successfullyReceive](const std::shared_ptr<dht::crypto::Certificate>&,
+                               const std::string& name) {
+            successfullyReceive = name == "1";
+            return true;
+        });
+    bobAccount->connectionManager().onConnectionReady(
+        [&](const DeviceId&, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
+            receiverConnected = socket != nullptr;
+            if (name == "1")
+                rcvSock1 = socket;
+            else if (name == "2")
+                rcvSock2 = socket;
+            else if (name == "3")
+                rcvSock3 = socket;
+        });
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "1",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            sendSock = socket;
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] {
+        return successfullyReceive && successfullyConnected && receiverConnected;
+    }));
+    CPPUNIT_ASSERT(receiverConnected);
+    successfullyConnected = false;
+    receiverConnected = false;
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "2",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            sendSock2 = socket;
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return successfullyConnected && receiverConnected; }));
+    successfullyConnected = false;
+    receiverConnected = false;
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "3",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            sendSock3 = socket;
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return successfullyConnected && receiverConnected; }));
+    std::mutex mtxRcv {};
+    std::string alphabet, shouldRcv, rcv1, rcv2, rcv3;
+    for (int i = 0; i < 100; ++i)
+        alphabet += "QWERTYUIOPASDFGHJKLZXCVBNM";
+    rcvSock1->setOnRecv([&](const uint8_t* buf, size_t len) {
+        rcv1 += std::string(buf, buf + len);
+        return len;
+    });
+    rcvSock2->setOnRecv([&](const uint8_t* buf, size_t len) {
+        rcv2 += std::string(buf, buf + len);
+        return len;
+    });
+    rcvSock3->setOnRecv([&](const uint8_t* buf, size_t len) {
+        rcv3 += std::string(buf, buf + len);
+        return len;
+    });
+    for (uint64_t i = 0; i < alphabet.size(); ++i) {
+        auto send = std::string(8000, alphabet[i]);
+        shouldRcv += send;
+        std::error_code ec;
+        sendSock->write(reinterpret_cast<unsigned char*>(send.data()), send.size(), ec);
+        sendSock2->write(reinterpret_cast<unsigned char*>(send.data()), send.size(), ec);
+        sendSock3->write(reinterpret_cast<unsigned char*>(send.data()), send.size(), ec);
+        CPPUNIT_ASSERT(!ec);
+    }
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] {
+        return shouldRcv == rcv1 && shouldRcv == rcv2 && shouldRcv == rcv3;
+    }));*/
+}
+
+void
+ConnectionManagerTest::testDestroyWhileSending()
+{
+    // Same as test before, but destroy the accounts while sending.
+    // This test if a segfault occurs
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+    bool successfullyReceive = false;
+    bool receiverConnected = false;
+    std::shared_ptr<ChannelSocket> rcvSock1, rcvSock2, rcvSock3, sendSock, sendSock2, sendSock3;
+    bobAccount->connectionManager().onChannelRequest(
+        [&successfullyReceive](const std::shared_ptr<dht::crypto::Certificate>&,
+                               const std::string& name) {
+            successfullyReceive = name == "1";
+            return true;
+        });
+    bobAccount->connectionManager().onConnectionReady(
+        [&](const DeviceId&, const std::string& name, std::shared_ptr<ChannelSocket> socket) {
+            receiverConnected = socket != nullptr;
+            if (name == "1")
+                rcvSock1 = socket;
+            else if (name == "2")
+                rcvSock2 = socket;
+            else if (name == "3")
+                rcvSock3 = socket;
+        });
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "1",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            sendSock = socket;
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] {
+        return successfullyReceive && successfullyConnected && receiverConnected;
+    }));
+    successfullyConnected = false;
+    receiverConnected = false;
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "2",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            sendSock2 = socket;
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return successfullyConnected && receiverConnected; }));
+    successfullyConnected = false;
+    receiverConnected = false;
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "3",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            sendSock3 = socket;
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return successfullyConnected && receiverConnected; }));
+    std::mutex mtxRcv {};
+    std::string alphabet;
+    for (int i = 0; i < 100; ++i)
+        alphabet += "QWERTYUIOPASDFGHJKLZXCVBNM";
+    rcvSock1->setOnRecv([&](const uint8_t*, size_t len) { return len; });
+    rcvSock2->setOnRecv([&](const uint8_t*, size_t len) { return len; });
+    rcvSock3->setOnRecv([&](const uint8_t*, size_t len) { return len; });
+    for (uint64_t i = 0; i < alphabet.size(); ++i) {
+        auto send = std::string(8000, alphabet[i]);
+        std::error_code ec;
+        sendSock->write(reinterpret_cast<unsigned char*>(send.data()), send.size(), ec);
+        sendSock2->write(reinterpret_cast<unsigned char*>(send.data()), send.size(), ec);
+        sendSock3->write(reinterpret_cast<unsigned char*>(send.data()), send.size(), ec);
+        CPPUNIT_ASSERT(!ec);
+    }*/
+
+    // No need to wait, immediately destroy, no segfault must occurs
+}
+
+void
+ConnectionManagerTest::testIsConnecting()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false, successfullyReceive = false;
+
+    bobAccount->connectionManager().onChannelRequest(
+        [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) {
+            successfullyReceive = true;
+            cv.notify_one();
+            std::this_thread::sleep_for(2s);
+            return true;
+        });
+
+    CPPUNIT_ASSERT(!aliceAccount->connectionManager().isConnecting(bobDeviceId, "sip"));
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "sip",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    // connectDevice is full async, so isConnecting will be true after a few ms.
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return successfullyReceive; }));
+    CPPUNIT_ASSERT(aliceAccount->connectionManager().isConnecting(bobDeviceId, "sip"));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return successfullyConnected; }));
+    std::this_thread::sleep_for(
+        std::chrono::milliseconds(100)); // Just to wait for the callback to finish
+    CPPUNIT_ASSERT(!aliceAccount->connectionManager().isConnecting(bobDeviceId, "sip"));*/
+}
+
+void
+ConnectionManagerTest::testCanSendBeacon()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+
+    std::shared_ptr<MultiplexedSocket> aliceSocket, bobSocket;
+    bobAccount->connectionManager().onChannelRequest(
+        [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
+    bobAccount->connectionManager().onConnectionReady(
+        [&](const DeviceId&, const std::string&, std::shared_ptr<ChannelSocket> socket) {
+            if (socket && socket->name() == "sip")
+                bobSocket = socket->underlyingSocket();
+            cv.notify_one();
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "sip",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            aliceSocket = socket->underlyingSocket();
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    // connectDevice is full async, so isConnecting will be true after a few ms.
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceSocket && bobSocket && successfullyConnected; }));
+    CPPUNIT_ASSERT(aliceSocket->canSendBeacon());
+
+    // Because onConnectionReady is true before version is sent, we can wait a bit
+    // before canSendBeacon is true.
+    auto start = std::chrono::steady_clock::now();
+    auto aliceCanSendBeacon = false;
+    auto bobCanSendBeacon = false;
+    do {
+        aliceCanSendBeacon = aliceSocket->canSendBeacon();
+        bobCanSendBeacon = bobSocket->canSendBeacon();
+        if (!bobCanSendBeacon || !aliceCanSendBeacon)
+            std::this_thread::sleep_for(1s);
+    } while ((not bobCanSendBeacon or not aliceCanSendBeacon)
+             and std::chrono::steady_clock::now() - start < 5s);
+
+    CPPUNIT_ASSERT(bobCanSendBeacon && aliceCanSendBeacon);*/
+}
+
+void
+ConnectionManagerTest::testCannotSendBeacon()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+
+    std::shared_ptr<MultiplexedSocket> aliceSocket, bobSocket;
+    bobAccount->connectionManager().onChannelRequest(
+        [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
+    bobAccount->connectionManager().onConnectionReady(
+        [&](const DeviceId&, const std::string&, std::shared_ptr<ChannelSocket> socket) {
+            if (socket && socket->name() == "sip")
+                bobSocket = socket->underlyingSocket();
+            cv.notify_one();
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "sip",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            aliceSocket = socket->underlyingSocket();
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    // connectDevice is full async, so isConnecting will be true after a few ms.
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceSocket && bobSocket; }));
+
+    int version = 1412;
+    bobSocket->setOnVersionCb([&](auto v) {
+        version = v;
+        cv.notify_one();
+    });
+    aliceSocket->setVersion(0);
+    aliceSocket->sendVersion();
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return version == 0; }));
+    CPPUNIT_ASSERT(!bobSocket->canSendBeacon());*/
+}
+
+void
+ConnectionManagerTest::testConnectivityChangeTriggerBeacon()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+
+    std::shared_ptr<MultiplexedSocket> aliceSocket, bobSocket;
+    bobAccount->connectionManager().onChannelRequest(
+        [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
+    bobAccount->connectionManager().onConnectionReady(
+        [&](const DeviceId&, const std::string&, std::shared_ptr<ChannelSocket> socket) {
+            if (socket && socket->name() == "sip")
+                bobSocket = socket->underlyingSocket();
+            cv.notify_one();
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "sip",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            aliceSocket = socket->underlyingSocket();
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    // connectDevice is full async, so isConnecting will be true after a few ms.
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceSocket && bobSocket; }));
+
+    bool hasRequest = false;
+    bobSocket->setOnBeaconCb([&](auto p) {
+        if (p)
+            hasRequest = true;
+        cv.notify_one();
+    });
+    aliceAccount->connectionManager().connectivityChanged();
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return hasRequest; }));*/
+}
+
+void
+ConnectionManagerTest::testOnNoBeaconTriggersShutdown()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    bobAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyConnected = false;
+
+    std::shared_ptr<MultiplexedSocket> aliceSocket, bobSocket;
+    bobAccount->connectionManager().onChannelRequest(
+        [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string&) { return true; });
+    bobAccount->connectionManager().onConnectionReady(
+        [&](const DeviceId&, const std::string&, std::shared_ptr<ChannelSocket> socket) {
+            if (socket && socket->name() == "sip")
+                bobSocket = socket->underlyingSocket();
+            cv.notify_one();
+        });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "sip",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        if (socket) {
+                                                            aliceSocket = socket->underlyingSocket();
+                                                            successfullyConnected = true;
+                                                        }
+                                                        cv.notify_one();
+                                                    });
+    // connectDevice is full async, so isConnecting will be true after a few ms.
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return aliceSocket && bobSocket; }));
+
+    bool isClosed = false;
+    aliceSocket->onShutdown([&] {
+        isClosed = true;
+        cv.notify_one();
+    });
+    bobSocket->answerToBeacon(false);
+    aliceAccount->connectionManager().connectivityChanged();
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return isClosed; }));*/
+}
+
+void
+ConnectionManagerTest::testShutdownWhileNegotiating()
+{
+    /*auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobDeviceId = DeviceId(std::string(bobAccount->currentDeviceId()));
+
+    aliceAccount->connectionManager().onICERequest([](const DeviceId&) { return true; });
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    bool successfullyReceive = false;
+    bool notConnected = false;
+
+    bobAccount->connectionManager().onICERequest([&](const DeviceId&) {
+        successfullyReceive = true;
+        cv.notify_one();
+        return true;
+    });
+
+    aliceAccount->connectionManager().connectDevice(bobDeviceId,
+                                                    "git://*",
+                                                    [&](std::shared_ptr<ChannelSocket> socket,
+                                                        const DeviceId&) {
+                                                        notConnected = !socket;
+                                                        cv.notify_one();
+                                                    });
+
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return successfullyReceive; }));
+    Manager::instance().setAccountActive(aliceId, false, true);
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return notConnected; }));*/
+}
+
+} // namespace test
+} // namespace jami
+
+JAMI_TEST_RUNNER(jami::test::ConnectionManagerTest::name())
diff --git a/tests/testFileutils.cpp b/tests/testFileutils.cpp
new file mode 100644
index 0000000..ba95e33
--- /dev/null
+++ b/tests/testFileutils.cpp
@@ -0,0 +1,156 @@
+/*
+ *  Copyright (C) 2004-2023 Savoir-faire Linux Inc.
+ *  Author: Olivier Gregoire <olivier.gregoire@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "test_runner.h"
+#include "fileutils.h"
+
+#include <string>
+#include <iostream>
+#include <cstdlib>
+#include <unistd.h>
+
+namespace jami { namespace fileutils { namespace test {
+
+class FileutilsTest : public CppUnit::TestFixture {
+public:
+    static std::string name() { return "fileutils"; }
+
+    void setUp();
+    void tearDown();
+
+private:
+    void testCheckDir();
+    void testPath();
+    void testReadDirectory();
+    void testLoadFile();
+    void testIsDirectoryWritable();
+
+    CPPUNIT_TEST_SUITE(FileutilsTest);
+    CPPUNIT_TEST(testCheckDir);
+    CPPUNIT_TEST(testPath);
+    CPPUNIT_TEST(testReadDirectory);
+    CPPUNIT_TEST(testLoadFile);
+    CPPUNIT_TEST(testIsDirectoryWritable);
+    CPPUNIT_TEST_SUITE_END();
+
+    static constexpr auto tmpFileName = "temp_file";
+
+    std::string TEST_PATH;
+    std::string NON_EXISTANT_PATH_BASE;
+    std::string NON_EXISTANT_PATH;
+    std::string EXISTANT_FILE;
+};
+
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(FileutilsTest, FileutilsTest::name());
+
+void
+FileutilsTest::setUp()
+{
+    char template_name[] = {"ring_unit_tests_XXXXXX"};
+
+    // Generate a temporary directory with a file inside
+    auto directory = mkdtemp(template_name);
+    CPPUNIT_ASSERT(directory);
+
+    TEST_PATH = directory;
+    EXISTANT_FILE = TEST_PATH + DIR_SEPARATOR_STR + tmpFileName;
+    NON_EXISTANT_PATH_BASE = TEST_PATH + DIR_SEPARATOR_STR + "not_existing_path";
+    NON_EXISTANT_PATH = NON_EXISTANT_PATH_BASE + DIR_SEPARATOR_STR + "test";
+
+    auto* fd = fopen(EXISTANT_FILE.c_str(), "w");
+    fwrite("RING", 1, 4, fd);
+    fclose(fd);
+}
+
+void
+FileutilsTest::tearDown()
+{
+    unlink(EXISTANT_FILE.c_str());
+    rmdir(TEST_PATH.c_str());
+}
+
+void
+FileutilsTest::testCheckDir()
+{
+    // check existed directory
+    CPPUNIT_ASSERT(check_dir(TEST_PATH.c_str()));
+    CPPUNIT_ASSERT(isDirectory(TEST_PATH.c_str()));
+    // check non-existent directory
+    CPPUNIT_ASSERT(!isDirectory(NON_EXISTANT_PATH));
+    CPPUNIT_ASSERT(check_dir(NON_EXISTANT_PATH.c_str()));
+    CPPUNIT_ASSERT(isDirectory(NON_EXISTANT_PATH));
+    CPPUNIT_ASSERT(removeAll(NON_EXISTANT_PATH_BASE) == 0);
+    CPPUNIT_ASSERT(!isDirectory(NON_EXISTANT_PATH_BASE));
+    //remove an non existent directory
+    CPPUNIT_ASSERT(removeAll(NON_EXISTANT_PATH_BASE) == -1);
+}
+
+void
+FileutilsTest::testPath()
+{
+    CPPUNIT_ASSERT(isPathRelative("relativePath"));
+    CPPUNIT_ASSERT(isFile(EXISTANT_FILE));
+    CPPUNIT_ASSERT(!isDirectory(EXISTANT_FILE));
+    CPPUNIT_ASSERT(isDirectory(TEST_PATH));
+}
+
+void
+FileutilsTest::testReadDirectory()
+{
+    CPPUNIT_ASSERT(recursive_mkdir(TEST_PATH + DIR_SEPARATOR_STR + "readDirectory" + DIR_SEPARATOR_STR + "test1"));
+    CPPUNIT_ASSERT(recursive_mkdir(TEST_PATH + DIR_SEPARATOR_STR + "readDirectory" + DIR_SEPARATOR_STR + "test2"));
+    auto dirs = readDirectory(TEST_PATH + DIR_SEPARATOR_STR + "readDirectory");
+    CPPUNIT_ASSERT(dirs.size() == 2);
+    CPPUNIT_ASSERT(
+        (dirs.at(0).compare("test1") == 0 && dirs.at(1).compare("test2") == 0)
+        || (dirs.at(1).compare("test1") == 0 && dirs.at(0).compare("test2") == 0));
+    CPPUNIT_ASSERT(removeAll(TEST_PATH + DIR_SEPARATOR_STR + "readDirectory") == 0);
+}
+
+void
+FileutilsTest::testLoadFile()
+{
+    auto file = loadFile(EXISTANT_FILE);
+    CPPUNIT_ASSERT(file.size() == 4);
+    CPPUNIT_ASSERT(file.at(0) == 'R');
+    CPPUNIT_ASSERT(file.at(1) == 'I');
+    CPPUNIT_ASSERT(file.at(2) == 'N');
+    CPPUNIT_ASSERT(file.at(3) == 'G');
+}
+
+void
+FileutilsTest::testIsDirectoryWritable()
+{
+    CPPUNIT_ASSERT(recursive_mkdir(NON_EXISTANT_PATH_BASE));
+    CPPUNIT_ASSERT(isDirectoryWritable(NON_EXISTANT_PATH_BASE));
+    CPPUNIT_ASSERT(removeAll(NON_EXISTANT_PATH_BASE) == 0);
+    // Create directory with permission: read by owner
+    CPPUNIT_ASSERT(recursive_mkdir(NON_EXISTANT_PATH_BASE, 0400));
+    CPPUNIT_ASSERT(!isDirectoryWritable(NON_EXISTANT_PATH_BASE));
+    CPPUNIT_ASSERT(removeAll(NON_EXISTANT_PATH_BASE) == 0);
+}
+
+}}} // namespace jami::test::fileutils
+
+JAMI_TEST_RUNNER(jami::fileutils::test::FileutilsTest::name());
+
diff --git a/tests/testString_utils.cpp b/tests/testString_utils.cpp
new file mode 100644
index 0000000..ba4b163
--- /dev/null
+++ b/tests/testString_utils.cpp
@@ -0,0 +1,125 @@
+/*
+ *  Copyright (C) 2004-2023 Savoir-faire Linux Inc.
+ *  Author: Olivier Gregoire <olivier.gregoire@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#include <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "string_utils.h"
+#include "test_runner.h"
+
+#include <string>
+#include <string_view>
+
+using namespace std::literals;
+
+namespace jami {
+namespace test {
+
+class StringUtilsTest : public CppUnit::TestFixture
+{
+public:
+    static std::string name() { return "string_utils"; }
+
+private:
+    void bool_to_str_test();
+    void to_string_test();
+    void to_number_test();
+    void split_string_test();
+
+    CPPUNIT_TEST_SUITE(StringUtilsTest);
+    CPPUNIT_TEST(bool_to_str_test);
+    CPPUNIT_TEST(to_string_test);
+    CPPUNIT_TEST(to_number_test);
+    CPPUNIT_TEST(split_string_test);
+    CPPUNIT_TEST_SUITE_END();
+
+    const double DOUBLE = 3.14159265359;
+    const int INT = 42;
+    const std::string PI_DOUBLE = "3.14159265359";
+    const std::string PI_FLOAT = "3.14159265359";
+    const std::string PI_42 = "42";
+};
+
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(StringUtilsTest, StringUtilsTest::name());
+
+void
+StringUtilsTest::bool_to_str_test()
+{
+    CPPUNIT_ASSERT(bool_to_str(true) == TRUE_STR);
+    CPPUNIT_ASSERT(bool_to_str(false) == FALSE_STR);
+}
+
+void
+StringUtilsTest::to_string_test()
+{
+    // test with double
+    CPPUNIT_ASSERT(to_string(DOUBLE) == PI_DOUBLE);
+
+    // test with float
+    float varFloat = 3.14;
+    std::string sVarFloat = to_string(varFloat);
+    CPPUNIT_ASSERT(sVarFloat.at(0) == '3' && sVarFloat.at(1) == '.' && sVarFloat.at(2) == '1'
+                   && sVarFloat.at(3) == '4');
+
+    // test with int
+    CPPUNIT_ASSERT(std::to_string(INT).compare(PI_42) == 0);
+
+    CPPUNIT_ASSERT_EQUAL("0000000000000010"s, to_hex_string(16));
+    CPPUNIT_ASSERT_EQUAL((uint64_t)16, from_hex_string("0000000000000010"s));
+}
+
+void
+StringUtilsTest::to_number_test()
+{
+    // test with int
+    CPPUNIT_ASSERT(jami::stoi(PI_42) == INT);
+
+    // test with double
+    CPPUNIT_ASSERT(jami::stod(PI_DOUBLE) == DOUBLE);
+}
+
+void
+StringUtilsTest::split_string_test()
+{
+    auto data = "*fdg454()**{&xcx*"sv;
+    auto split_string_result = split_string(data, '*');
+    CPPUNIT_ASSERT(split_string_result.size() == 2);
+    CPPUNIT_ASSERT(split_string_result.at(0) == "fdg454()"sv
+                   && split_string_result.at(1) == "{&xcx"sv);
+
+    auto split_string_to_unsigned_result = split_string_to_unsigned("/4545497//45454/", '/');
+    CPPUNIT_ASSERT(split_string_to_unsigned_result.size() == 2);
+    CPPUNIT_ASSERT(split_string_to_unsigned_result.at(0) == 4545497
+                   && split_string_to_unsigned_result.at(1) == 45454);
+
+    std::string_view line;
+    split_string_result.clear();
+    while (jami::getline(data, line, '*')) {
+        split_string_result.emplace_back(line);
+    }
+    CPPUNIT_ASSERT(split_string_result.size() == 2);
+    CPPUNIT_ASSERT(split_string_result.at(0) == "fdg454()"sv
+                   && split_string_result.at(1) == "{&xcx"sv);
+}
+
+} // namespace test
+} // namespace jami
+
+JAMI_TEST_RUNNER(jami::test::StringUtilsTest::name());
diff --git a/tests/test_runner.h b/tests/test_runner.h
new file mode 100644
index 0000000..ecd5081
--- /dev/null
+++ b/tests/test_runner.h
@@ -0,0 +1,31 @@
+#include <iostream>
+
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/ui/text/TestRunner.h>
+#include <cppunit/CompilerOutputter.h>
+
+// This version of the test runner is similar to RING_TEST_RUNNER but
+// can take multiple unit tests.
+// It's practical to run a test for diffrent configs, for instance when
+// running the same test for both Jami and SIP accounts.
+
+// The test will abort if a test fails.
+#define JAMI_TEST_RUNNER(...) \
+    int main() \
+    { \
+        std::vector<std::string> suite_names {__VA_ARGS__}; \
+        for (const std::string& name : suite_names) { \
+            CppUnit::TestFactoryRegistry& registry = CppUnit::TestFactoryRegistry::getRegistry( \
+                name); \
+            CppUnit::Test* suite = registry.makeTest(); \
+            if (suite->countTestCases() == 0) { \
+                std::cout << "No test cases specified for suite \"" << name << "\"\n"; \
+                continue; \
+            } \
+            CppUnit::TextUi::TestRunner runner; \
+            runner.addTest(suite); \
+            if (not runner.run()) \
+                return 1; \
+        } \
+        return 0; \
+    }