diff --git a/CMakeLists.txt b/CMakeLists.txt index 227118f14f5337ef273f7375d26cbf7bef667880..21d7b691a059a6a86af2b352b12fbc062eb1741e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,9 +3,11 @@ project(dhtnet) 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 @@ list (APPEND dhtnet_HEADERS ) 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 0000000000000000000000000000000000000000..7d6b75ff79d5f53d3001d9558854c4c5695c20f1 --- /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 0000000000000000000000000000000000000000..504f49f608e26c8edb63a1c23510a72a1ca04ba4 --- /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 0000000000000000000000000000000000000000..ba95e33d938aef1093037fe9192346b4afbbafda --- /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 0000000000000000000000000000000000000000..ba4b163c237fd4eccb38170fd367751246b8cbe5 --- /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 0000000000000000000000000000000000000000..ecd508127461757fcee5a4d65f0d10b5487af5a9 --- /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; \ + }