diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e015ba8652508db8e7a19a6f1ed0915e888e3b3..da3f5d5f92e99038968e2926fa44c1a10bbb3d1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -903,6 +903,8 @@ IF((${ENABLE_TEST} MATCHES true)) test/conversationmodeltester.cpp test/contactmodeltester.h test/contactmodeltester.cpp + test/datatransfertester.h + test/datatransfertester.cpp test/newcallmodeltester.h test/newcallmodeltester.cpp test/newaccountmodeltester.h diff --git a/src/api/datatransfermodel.h b/src/api/datatransfermodel.h index 02f9bda53b4473611524b18970e3d33898a7c858..d636122b371cea536596c6cabddf70631c5bd961 100644 --- a/src/api/datatransfermodel.h +++ b/src/api/datatransfermodel.h @@ -72,6 +72,12 @@ public: long long getDringIdFromInteractionId(int interactionId); + /** + * Used when images < 20 Mb are automatically accepted and downloaded + * Should contains the full directory with the end marker (/ on linux for example) + */ + std::string downloadDirectory; + Q_SIGNALS: /** * Connect this signal to know when a data transfer is incoming. diff --git a/src/conversationmodel.cpp b/src/conversationmodel.cpp index df79dd73004d1bb982512dfc74e8c50befd3cfa1..500c0daeec30e3f4eb552d5808872a0438e6f880 100644 --- a/src/conversationmodel.cpp +++ b/src/conversationmodel.cpp @@ -148,6 +148,14 @@ public: bool usefulDataFromDataTransfer(long long dringId, const datatransfer::Info& info, int& interactionId, std::string& convId); + /** + * accept a file transfer + * @param convUid + * @param interactionId + * @param final name of the file + */ + void acceptTransfer(const std::string& convUid, uint64_t interactionId, const std::string& path); + const ConversationModel& linked; Lrc& lrc; Database& db; @@ -1488,30 +1496,7 @@ ConversationModel::sendFile(const std::string& convUid, void ConversationModel::acceptTransfer(const std::string& convUid, uint64_t interactionId, const std::string& path) { - pimpl_->lrc.getDataTransferModel().accept(interactionId, path, 0); - database::updateInteractionBody(pimpl_->db, interactionId, path); - database::updateInteractionStatus(pimpl_->db, interactionId, interaction::Status::TRANSFER_ACCEPTED); - - // prepare interaction Info and emit signal for the client - auto conversationIdx = pimpl_->indexOf(convUid); - interaction::Info itCopy; - bool emitUpdated = false; - if (conversationIdx != -1) { - std::lock_guard<std::mutex> lk(pimpl_->interactionsLocks[convUid]); - auto& interactions = pimpl_->conversations[conversationIdx].interactions; - auto it = interactions.find(interactionId); - if (it != interactions.end()) { - it->second.body = path; - it->second.status = interaction::Status::TRANSFER_ACCEPTED; - emitUpdated = true; - itCopy = it->second; - } - } - if (emitUpdated) { - pimpl_->sendContactRequest(pimpl_->conversations[conversationIdx].participants.front()); - pimpl_->dirtyConversations = {true, true}; - emit interactionStatusUpdated(convUid, interactionId, itCopy); - } + pimpl_->acceptTransfer(convUid, interactionId, path); } void @@ -1689,10 +1674,50 @@ ConversationModelPimpl::slotTransferStatusAwaitingHost(long long dringId, datatr if (emitUpdated) { dirtyConversations = {true, true}; emit linked.interactionStatusUpdated(convId, interactionId, itCopy); + // If it's an image < 20 Mb, accept transfer. + auto extensionIdx = info.displayName.find_last_of("."); + if (extensionIdx == std::string::npos) return; + auto extension = info.displayName.substr(extensionIdx); + std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); + auto imageExtensions = {".gif", ".jpg", ".jpeg", ".png"}; + auto isImage = std::find(imageExtensions.begin(), imageExtensions.end(), extension) != imageExtensions.end(); + auto destinationDir = lrc.getDataTransferModel().downloadDirectory; + if (info.totalSize < 20 * 1024 * 1024 && isImage && !destinationDir.empty()) { + acceptTransfer(convId, interactionId, destinationDir + info.displayName); + } } } } +void +ConversationModelPimpl::acceptTransfer(const std::string& convUid, uint64_t interactionId, const std::string& path) +{ + lrc.getDataTransferModel().accept(interactionId, path, 0); + database::updateInteractionBody(db, interactionId, path); + database::updateInteractionStatus(db, interactionId, interaction::Status::TRANSFER_ACCEPTED); + + // prepare interaction Info and emit signal for the client + auto conversationIdx = indexOf(convUid); + interaction::Info itCopy; + bool emitUpdated = false; + if (conversationIdx != -1) { + std::lock_guard<std::mutex> lk(interactionsLocks[convUid]); + auto& interactions = conversations[conversationIdx].interactions; + auto it = interactions.find(interactionId); + if (it != interactions.end()) { + it->second.body = path; + it->second.status = interaction::Status::TRANSFER_ACCEPTED; + emitUpdated = true; + itCopy = it->second; + } + } + if (emitUpdated) { + sendContactRequest(conversations[conversationIdx].participants.front()); + dirtyConversations = {true, true}; + emit linked.interactionStatusUpdated(convUid, interactionId, itCopy); + } +} + void ConversationModelPimpl::slotTransferStatusOngoing(long long dringId, datatransfer::Info info) { diff --git a/test/datatransfertester.cpp b/test/datatransfertester.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6036b11f9a52f0e485af5b65ca8e45395f410f42 --- /dev/null +++ b/test/datatransfertester.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2017-2018 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "datatransfertester.h" + +// std +#include <algorithm> + +// Qt +#include <QString> +#include "utils/waitforsignalhelper.h" + +// Lrc +#include <api/newaccountmodel.h> +#include <api/contact.h> +#include <api/conversationmodel.h> +#include <dbus/configurationmanager.h> +#include <api/datatransfer.h> +#include <api/datatransfermodel.h> +#include <datatransfer_interface.h> + +namespace ring +{ +namespace test +{ + +CPPUNIT_TEST_SUITE_REGISTRATION(DataTransferTester); + +DataTransferTester::DataTransferTester() +: lrc_(new lrc::api::Lrc()) +, accInfo_(lrc_->getAccountModel().getAccountInfo("ring0")) +{ + +} + +void +DataTransferTester::setUp() +{ + +} + +void +DataTransferTester::testReceivesMusic() +{ + auto conversations = accInfo_.conversationModel->allFilteredConversations(); + CPPUNIT_ASSERT(conversations.size() != 0); + auto firstConversation = accInfo_.conversationModel->filteredConversation(0); + auto intBaseSize = accInfo_.conversationModel->filteredConversation(0).interactions.size(); + + lrc::api::datatransfer::Info info { + "1", lrc::api::datatransfer::Status::on_connection, 0, 10 * 1024 * 1024, 0, + "./", "glados.mp3", "ring0", firstConversation.participants[0] + }; + ConfigurationManager::instance().setDataTransferInfo(1, info); + ConfigurationManager::instance().emitDataTransferEvent(1, DRing::DataTransferEventCode::created); + auto intFinalSize = accInfo_.conversationModel->filteredConversation(0).interactions.size(); + CPPUNIT_ASSERT_EQUAL(intFinalSize, intBaseSize + 1); + // base conversation + file transfer +} + +void +DataTransferTester::testReceivesImage5MbNoPref() +{ + auto conversations = accInfo_.conversationModel->allFilteredConversations(); + CPPUNIT_ASSERT(conversations.size() != 0); + auto firstConversation = accInfo_.conversationModel->filteredConversation(0); + lrc::api::datatransfer::Info info { + "1", lrc::api::datatransfer::Status::on_connection, 0, 5 * 1024 * 1024, 0, + "./", "glados.jpg", "ring0", firstConversation.participants[0] + }; + ConfigurationManager::instance().setDataTransferInfo(2, info); + ConfigurationManager::instance().emitDataTransferEvent(2, DRing::DataTransferEventCode::created); + ConfigurationManager::instance().emitDataTransferEvent(2, DRing::DataTransferEventCode::wait_host_acceptance); + WaitForSignalHelper(*accInfo_.conversationModel, + SIGNAL(interactionStatusUpdated(const std::string&, uint64_t, + const api::interaction::Info&))).wait(1000); + auto lastIt = accInfo_.conversationModel->filteredConversation(0).interactions.rbegin()->second; + CPPUNIT_ASSERT(lastIt.status == lrc::api::interaction::Status::TRANSFER_AWAITING_HOST); +} + +void +DataTransferTester::testReceivesImage5Mb() +{ + lrc_->getDataTransferModel().downloadDirectory = "/"; + auto conversations = accInfo_.conversationModel->allFilteredConversations(); + CPPUNIT_ASSERT(conversations.size() != 0); + auto firstConversation = accInfo_.conversationModel->filteredConversation(0); + lrc::api::datatransfer::Info info { + "1", lrc::api::datatransfer::Status::on_connection, 0, 5 * 1024 * 1024, 0, + "./", "glados.jpg", "ring0", firstConversation.participants[0] + }; + ConfigurationManager::instance().setDataTransferInfo(3, info); + ConfigurationManager::instance().emitDataTransferEvent(3, DRing::DataTransferEventCode::created); + ConfigurationManager::instance().emitDataTransferEvent(3, DRing::DataTransferEventCode::wait_host_acceptance); + WaitForSignalHelper(*accInfo_.conversationModel, + SIGNAL(interactionStatusUpdated(const std::string&, uint64_t, + const api::interaction::Info&))).wait(1000); + auto lastIt = accInfo_.conversationModel->filteredConversation(0).interactions.rbegin()->second; + CPPUNIT_ASSERT(lastIt.status == lrc::api::interaction::Status::TRANSFER_ACCEPTED); +} + +void +DataTransferTester::testReceivesImage50Mb() +{ + lrc_->getDataTransferModel().downloadDirectory = "/"; + auto conversations = accInfo_.conversationModel->allFilteredConversations(); + CPPUNIT_ASSERT(conversations.size() != 0); + auto firstConversation = accInfo_.conversationModel->filteredConversation(0); + lrc::api::datatransfer::Info info { + "1", lrc::api::datatransfer::Status::on_connection, 0, 50 * 1024 * 1024, 0, + "./", "glados.jpg", "ring0", firstConversation.participants[0] + }; + ConfigurationManager::instance().setDataTransferInfo(3, info); + ConfigurationManager::instance().emitDataTransferEvent(3, DRing::DataTransferEventCode::created); + ConfigurationManager::instance().emitDataTransferEvent(3, DRing::DataTransferEventCode::wait_host_acceptance); + WaitForSignalHelper(*accInfo_.conversationModel, + SIGNAL(interactionStatusUpdated(const std::string&, uint64_t, + const api::interaction::Info&))).wait(1000); + auto lastIt = accInfo_.conversationModel->filteredConversation(0).interactions.rbegin()->second; + CPPUNIT_ASSERT(lastIt.status == lrc::api::interaction::Status::TRANSFER_AWAITING_HOST); +} + +void +DataTransferTester::tearDown() +{ + +} + +} // namespace test +} // namespace ring diff --git a/test/datatransfertester.h b/test/datatransfertester.h new file mode 100644 index 0000000000000000000000000000000000000000..6baf455c0c721e77732322375fce749a656a3650 --- /dev/null +++ b/test/datatransfertester.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2017-2018 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +// cppunit +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +// std +#include <memory> + +// Qt +#include <QObject> + +// lrc +#include "api/lrc.h" +#include "api/account.h" + +namespace ring +{ +namespace test +{ + +class DataTransferTester : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(DataTransferTester); + CPPUNIT_TEST(testReceivesMusic); + CPPUNIT_TEST(testReceivesImage5MbNoPref); + CPPUNIT_TEST(testReceivesImage5Mb); + CPPUNIT_TEST_SUITE_END(); + +public: + DataTransferTester(); + /** + * Method automatically called before each test by CppUnit + */ + void setUp(); + /** + * Receives a new file, should be in conversations + */ + void testReceivesMusic(); + /** + * Receives a new image without any prefedred dir, should be awaiting peer + */ + void testReceivesImage5MbNoPref(); + /** + * Receives a new image with a prefedred dir, should accept tranfer + */ + void testReceivesImage5Mb(); + /** + * Receives a new image with a prefedred dir but too big, awaiting peer + */ + void testReceivesImage50Mb(); + /** + * Method automatically called after each test by CppUnit + */ + void tearDown(); + +protected: + std::unique_ptr<lrc::api::Lrc> lrc_; + const lrc::api::account::Info& accInfo_; +}; + +} // namespace test +} // namespace ring diff --git a/test/mocks/configurationmanager_mock.h b/test/mocks/configurationmanager_mock.h index 48e508188312ad739a386baca37efab699ef7374..e7c883e1bd3dca1555eb6cfb15465112d8988223 100644 --- a/test/mocks/configurationmanager_mock.h +++ b/test/mocks/configurationmanager_mock.h @@ -35,6 +35,9 @@ #include "typedefs.h" #include "qtwrapper/conversions_wrap.hpp" +#include <api/datatransfer.h> +#include <datatransfer_interface.h> + /* * Proxy class for interface org.ring.Ring.ConfigurationManager */ @@ -82,6 +85,23 @@ public: emit incomingAccountMessage(accountId, from, payloads); } + + std::map<long long, lrc::api::datatransfer::Info> transferInfos_; + std::map<long long, uint32_t> transferInfosEvent_; + void setDataTransferInfo(long long ringId, lrc::api::datatransfer::Info lrc_info) { + transferInfos_.emplace(std::make_pair(ringId, lrc_info)); + } + + void emitDataTransferEvent(uint64_t transfer_id, DRing::DataTransferEventCode eventCode) { + auto code = static_cast<uint32_t>(eventCode); + auto it = transferInfosEvent_.find('c'); + if (it != transferInfosEvent_.end()) + it->second = code; + else + transferInfosEvent_.emplace(std::make_pair(transfer_id, code)); + emit dataTransferEvent(transfer_id, code); + } + public Q_SLOTS: // METHODS QString addAccount(MapStringString details) { @@ -666,8 +686,16 @@ public Q_SLOTS: // METHODS } uint32_t dataTransferInfo(uint64_t transfer_id, DataTransferInfo& lrc_info) { - (void)transfer_id; - (void)lrc_info; + auto dring_info = transferInfos_.find(transfer_id)->second; + lrc_info.accountId = QString::fromStdString(dring_info.accountId); + lrc_info.lastEvent = quint32(transferInfosEvent_.find(transfer_id)->second); + lrc_info.flags = {}; + lrc_info.totalSize = dring_info.totalSize; + lrc_info.bytesProgress = dring_info.progress; + lrc_info.peer = QString::fromStdString(dring_info.peerUri); + lrc_info.displayName = QString::fromStdString(dring_info.displayName); + lrc_info.path = QString::fromStdString(dring_info.path); + lrc_info.mimetype = QString::fromStdString(std::string("")); return 0; }