diff --git a/test/unitTest/Makefile.am b/test/unitTest/Makefile.am index a5acff7bb..579f29ff8 100644 --- a/test/unitTest/Makefile.am +++ b/test/unitTest/Makefile.am @@ -110,12 +110,16 @@ ut_video_scaler_SOURCES = media/video/test_video_scaler.cpp check_PROGRAMS += ut_audio_frame_resizer ut_audio_frame_resizer_SOURCES = media/audio/test_audio_frame_resizer.cpp +# +# call +# +check_PROGRAMS += ut_call +ut_call_SOURCES = call/call.cpp # # connectionManager # -#check_PROGRAMS += ut_connectionManager -check_PROGRAMS = ut_connectionManager +check_PROGRAMS += ut_connectionManager ut_connectionManager_SOURCES = connectionManager/connectionManager.cpp TESTS = $(check_PROGRAMS) \ No newline at end of file diff --git a/test/unitTest/call/call.cpp b/test/unitTest/call/call.cpp new file mode 100644 index 000000000..6b86b98d3 --- /dev/null +++ b/test/unitTest/call/call.cpp @@ -0,0 +1,246 @@ + /* + * Copyright (C) 2020 Savoir-faire Linux Inc. + * Author: Sébastien Blin + * + * 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 . + */ + +#include +#include +#include + +#include +#include + +#include "manager.h" +#include "jamidht/connectionmanager.h" +#include "jamidht/jamiaccount.h" +#include "../../test_runner.h" +#include "dring.h" +#include "account_const.h" + +using namespace DRing::Account; + +namespace jami { namespace test { + +class CallTest : public CppUnit::TestFixture { +public: + CallTest() { + // Init daemon + DRing::init(DRing::InitFlag(DRing::DRING_FLAG_DEBUG | DRing::DRING_FLAG_CONSOLE_LOG)); + if (not Manager::instance().initialized) + CPPUNIT_ASSERT(DRing::start("dring-sample.yml")); + } + ~CallTest() { + DRing::fini(); + } + static std::string name() { return "Call"; } + void setUp(); + void tearDown(); + + std::string aliceId; + std::string bobId; + +private: + void testCall(); + void testCachedCall(); + + CPPUNIT_TEST_SUITE(CallTest); + CPPUNIT_TEST(testCall); + CPPUNIT_TEST(testCachedCall); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CallTest, CallTest::name()); + +void +CallTest::setUp() +{ + std::map details = DRing::getAccountTemplate("RING"); + details[ConfProperties::TYPE] = "RING"; + details[ConfProperties::DISPLAYNAME] = "ALICE"; + details[ConfProperties::ALIAS] = "ALICE"; + details[ConfProperties::UPNP_ENABLED] = "true"; + details[ConfProperties::ARCHIVE_PASSWORD] = ""; + details[ConfProperties::ARCHIVE_PIN] = ""; + details[ConfProperties::ARCHIVE_PATH] = ""; + aliceId = Manager::instance().addAccount(details); + + details = DRing::getAccountTemplate("RING"); + details[ConfProperties::TYPE] = "RING"; + details[ConfProperties::DISPLAYNAME] = "BOB"; + details[ConfProperties::ALIAS] = "BOB"; + details[ConfProperties::UPNP_ENABLED] = "true"; + details[ConfProperties::ARCHIVE_PASSWORD] = ""; + details[ConfProperties::ARCHIVE_PIN] = ""; + details[ConfProperties::ARCHIVE_PATH] = ""; + bobId = Manager::instance().addAccount(details); + + JAMI_INFO("Initialize account..."); + auto aliceAccount = Manager::instance().getAccount(aliceId); + auto bobAccount = Manager::instance().getAccount(bobId); + std::map> confHandlers; + std::mutex mtx; + std::unique_lock lk{ mtx }; + std::condition_variable cv; + std::atomic_bool accountsReady {false}; + confHandlers.insert(DRing::exportable_callback( + [&](const std::string&, const std::map&) { + bool ready = false; + auto details = aliceAccount->getVolatileAccountDetails(); + auto daemonStatus = details[DRing::Account::ConfProperties::Registration::STATUS]; + ready = (daemonStatus == "REGISTERED"); + details = bobAccount->getVolatileAccountDetails(); + daemonStatus = details[DRing::Account::ConfProperties::Registration::STATUS]; + ready &= (daemonStatus == "REGISTERED"); + if (ready) { + accountsReady = true; + cv.notify_one(); + } + })); + DRing::registerSignalHandlers(confHandlers); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]{ return accountsReady.load(); })); + DRing::unregisterSignalHandlers(); +} + +void +CallTest::tearDown() +{ + JAMI_INFO("Remove created accounts..."); + + std::map> confHandlers; + std::mutex mtx; + std::unique_lock lk{ mtx }; + std::condition_variable cv; + auto currentAccSize = Manager::instance().getAccountList().size(); + std::atomic_bool accountsRemoved {false}; + confHandlers.insert(DRing::exportable_callback( + [&]() { + if (Manager::instance().getAccountList().size() <= currentAccSize - 2) { + accountsRemoved = true; + cv.notify_one(); + } + })); + DRing::registerSignalHandlers(confHandlers); + + Manager::instance().removeAccount(aliceId, true); + Manager::instance().removeAccount(bobId, true); + // Because cppunit is not linked with dbus, just poll if removed + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]{ return accountsRemoved.load(); })); + + DRing::unregisterSignalHandlers(); +} + +void +CallTest::testCall() +{ + // TODO remove. This sleeps is because it take some time for the DHT to be connected + // and account announced + JAMI_INFO("Waiting...."); + std::this_thread::sleep_for(std::chrono::seconds(5)); + auto aliceAccount = Manager::instance().getAccount(aliceId); + auto bobAccount = Manager::instance().getAccount(bobId); + auto bobUri = bobAccount->getAccountDetails()[ConfProperties::USERNAME]; + auto aliceUri = aliceAccount->getAccountDetails()[ConfProperties::USERNAME]; + + std::mutex mtx; + std::unique_lock lk{ mtx }; + std::condition_variable cv; + std::map> confHandlers; + std::atomic_bool callReceived {false}; + std::atomic callStopped {0}; + // Watch signals + confHandlers.insert(DRing::exportable_callback( + [&](const std::string&, const std::string&, const std::string&) { + callReceived = true; + cv.notify_one(); + })); + confHandlers.insert(DRing::exportable_callback( + [&](const std::string&, const std::string& state, signed) { + if (state == "OVER") { + callStopped += 1; + if (callStopped == 2) + cv.notify_one(); + } + })); + DRing::registerSignalHandlers(confHandlers); + + JAMI_INFO("Start call between alice and Bob"); + auto call = aliceAccount->newOutgoingCall(bobUri, {}); + + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]{ return callReceived.load(); })); + + JAMI_INFO("Stop call between alice and Bob"); + callStopped = 0; + Manager::instance().hangupCall(call->getCallId()); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]{ return callStopped == 2; })); +} + +void +CallTest::testCachedCall() +{ + std::this_thread::sleep_for(std::chrono::seconds(5)); + // TODO remove. This sleeps is because it take some time for the DHT to be connected + // and account announced + JAMI_INFO("Waiting...."); + auto aliceAccount = Manager::instance().getAccount(aliceId); + auto bobAccount = Manager::instance().getAccount(bobId); + auto bobUri = bobAccount->getAccountDetails()[ConfProperties::USERNAME]; + auto bobDeviceId = bobAccount->getAccountDetails()[ConfProperties::RING_DEVICE_ID]; + auto aliceUri = aliceAccount->getAccountDetails()[ConfProperties::USERNAME]; + + std::mutex mtx; + std::unique_lock lk{ mtx }; + std::condition_variable cv; + std::map> confHandlers; + std::atomic_bool callReceived {false}, successfullyConnected {false}; + std::atomic callStopped {0}; + // Watch signals + confHandlers.insert(DRing::exportable_callback( + [&](const std::string&, const std::string&, const std::string&) { + callReceived = true; + cv.notify_one(); + })); + confHandlers.insert(DRing::exportable_callback( + [&](const std::string&, const std::string& state, signed) { + if (state == "OVER") { + callStopped += 1; + if (callStopped == 2) + cv.notify_one(); + } + })); + DRing::registerSignalHandlers(confHandlers); + + JAMI_INFO("Connect Alice's device and Bob's device"); + aliceAccount->connectionManager().connectDevice(bobDeviceId, "sip", + [&cv, &successfullyConnected](std::shared_ptr socket) { + if (socket) + successfullyConnected = true; + cv.notify_one(); + }); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]{ return successfullyConnected.load(); })); + + JAMI_INFO("Start call between alice and Bob"); + auto call = aliceAccount->newOutgoingCall(bobUri, {}); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]{ return callReceived.load(); })); + + callStopped = 0; + JAMI_INFO("Stop call between alice and Bob"); + Manager::instance().hangupCall(call->getCallId()); + CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]{ return callStopped == 2; })); +} + +}} // namespace test + +RING_TEST_RUNNER(jami::test::CallTest::name()) \ No newline at end of file diff --git a/test/unitTest/connectionManager/connectionManager.cpp b/test/unitTest/connectionManager/connectionManager.cpp index e6a4a0215..39bfba91b 100644 --- a/test/unitTest/connectionManager/connectionManager.cpp +++ b/test/unitTest/connectionManager/connectionManager.cpp @@ -24,6 +24,7 @@ #include "manager.h" #include "jamidht/connectionmanager.h" +#include "jamidht/multiplexed_socket.h" #include "jamidht/jamiaccount.h" #include "../../test_runner.h" #include "dring.h"