From f30eca6722a3ebc8d0f7461da7fe3cd90a822fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= <sebastien.blin@savoirfairelinux.com> Date: Wed, 9 May 2018 17:47:39 -0400 Subject: [PATCH] newdevicemodel: deprecate ringdevicemodel in favor of newdevicemodel Ring devices are currently managed via a Qt model. With this patch, this class is now deprecated in favor of NewDeviceModel linked to the AccountModel used by clients. This patch add the ability to revoke devices and rename current used device for LRC based clients. Add some unit-tests in test/newdevicemodeltester.* Change-Id: Ifcec4e573f92967d7e0ee8759f1159328957c7ba Reviewed-by: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> --- CMakeLists.txt | 2 + src/api/account.h | 2 + src/api/newdevicemodel.h | 123 ++++++++++++ src/callbackshandler.cpp | 29 +++ src/callbackshandler.h | 39 ++++ src/newaccountmodel.cpp | 10 +- src/newdevicemodel.cpp | 234 ++++++++++++++++++++++ src/qtwrapper/configurationmanager_wrap.h | 5 + test/mocks/configurationmanager_mock.h | 41 +++- test/newdevicemodeltester.cpp | 197 ++++++++++++++++++ test/newdevicemodeltester.h | 103 ++++++++++ 11 files changed, 776 insertions(+), 9 deletions(-) create mode 100644 src/api/newdevicemodel.h create mode 100644 src/newdevicemodel.cpp create mode 100644 test/newdevicemodeltester.cpp create mode 100644 test/newdevicemodeltester.h diff --git a/CMakeLists.txt b/CMakeLists.txt index da3f5d5f..2dfb1b42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -327,6 +327,7 @@ SET( libringclient_LIB_SRCS src/bannedcontactmodel.cpp src/contactmodel.cpp src/newcallmodel.cpp + src/newdevicemodel.cpp src/conversationmodel.cpp src/database.cpp src/authority/daemon.cpp @@ -498,6 +499,7 @@ SET(libringclient_api_LIB_HDRS src/api/lrc.h src/api/newaccountmodel.h src/api/newcallmodel.h + src/api/newdevicemodel.h src/api/contactmodel.h src/api/conversationmodel.h src/api/profile.h diff --git a/src/api/account.h b/src/api/account.h index e7151d15..d36abdd0 100644 --- a/src/api/account.h +++ b/src/api/account.h @@ -35,6 +35,7 @@ class NewCallModel; class ContactModel; class ConversationModel; class NewAccountModel; +class NewDeviceModel; namespace account { @@ -85,6 +86,7 @@ struct Info std::unique_ptr<lrc::api::NewCallModel> callModel; std::unique_ptr<lrc::api::ContactModel> contactModel; std::unique_ptr<lrc::api::ConversationModel> conversationModel; + std::unique_ptr<lrc::api::NewDeviceModel> deviceModel; NewAccountModel* accountModel {nullptr}; }; diff --git a/src/api/newdevicemodel.h b/src/api/newdevicemodel.h new file mode 100644 index 00000000..1aac83a0 --- /dev/null +++ b/src/api/newdevicemodel.h @@ -0,0 +1,123 @@ +/**************************************************************************** + * Copyright (C) 2017-2018 Savoir-faire Linux * + * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser 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 <http://www.gnu.org/licenses/>. * + ***************************************************************************/ +#pragma once + +// Std +#include <memory> +#include <string> +#include <list> + +// Qt +#include <qobject.h> +#include <QObject> + +// Lrc +#include "api/account.h" +#include "typedefs.h" + +namespace lrc +{ + +class CallbacksHandler; +class NewDeviceModelPimpl; + +namespace api +{ + +namespace account { struct Info; } + +struct Device +{ + std::string id = ""; + std::string name = ""; + bool isCurrent = false; +}; + +/** + * @brief Class that manages ring devices for an account + */ +class LIB_EXPORT NewDeviceModel : public QObject { + Q_OBJECT + +public: + /** + * Used by deviceRevoked's status + */ + enum class Status { + SUCCESS = 0, + WRONG_PASSWORD = 1, + UNKNOWN_DEVICE = 2 + }; + const account::Info& owner; + + NewDeviceModel(const account::Info& owner, const CallbacksHandler& callbacksHandler); + ~NewDeviceModel(); + + /** + * Get ring devices of an account + * @return a copy of current devices + */ + std::list<Device> getAllDevices() const; + + /** + * Retrieve a device by its id + * @param id of the device + * @return the device if found else a device with a null id + */ + Device getDevice(const std::string& id) const; + + /** + * Revoke a ring device + * @param id of the device to revoke + * @param password of the account's archive + * @note will emit deviceRevoked when finished + */ + void revokeDevice(const std::string& id, const std::string& password); + + /** + * Change the name of the current device + * @param newName + * @note will emit deviceUpdated when finished + * @note ring can't change the name of another device + */ + void setCurrentDeviceName(const std::string& newName); + +Q_SIGNALS: + /** + * Link to this signal to know when a new device is added + * @param id added device + */ + void deviceAdded(const std::string& id) const; + /** + * Link to this signal to know when a device is removed + * @param id removed device + * @param Status (SUCCESS, WRONG_PASSWORD, UNKNOWN_DEVICE) + */ + void deviceRevoked(const std::string& id, const Status status) const; + /** + * Link to this signal when a device get a new name + * @param id + */ + void deviceUpdated(const std::string& id) const; + +private: + std::unique_ptr<NewDeviceModelPimpl> pimpl_; +}; + +} // namespace api +} // namespace lrc diff --git a/src/callbackshandler.cpp b/src/callbackshandler.cpp index 1ed6fcef..88739629 100644 --- a/src/callbackshandler.cpp +++ b/src/callbackshandler.cpp @@ -118,6 +118,16 @@ CallbacksHandler::CallbacksHandler(const Lrc& parent) &ConfigurationManagerInterface::dataTransferEvent, this, &CallbacksHandler::slotDataTransferEvent); + + connect(&ConfigurationManager::instance(), + &ConfigurationManagerInterface::knownDevicesChanged, + this, + &CallbacksHandler::slotKnownDevicesChanged); + + connect(&ConfigurationManager::instance(), + &ConfigurationManagerInterface::deviceRevocationEnded, + this, + &CallbacksHandler::slotDeviceRevokationEnded); } CallbacksHandler::~CallbacksHandler() @@ -327,4 +337,23 @@ CallbacksHandler::slotDataTransferEvent(qulonglong dringId, uint codeStatus) } } +void +CallbacksHandler::slotKnownDevicesChanged(const QString& accountId, + const QMap<QString, QString>& devices) +{ + std::map<std::string, std::string> stdDevices; + for (auto item : devices.keys()) + stdDevices[item.toStdString()] = devices.value(item).toStdString(); + auto accountId2 = accountId.toStdString(); + emit knownDevicesChanged(accountId2, stdDevices); +} + +void +CallbacksHandler::slotDeviceRevokationEnded(const QString& accountId, + const QString& deviceId, + const int status) +{ + emit deviceRevocationEnded(accountId.toStdString(), deviceId.toStdString(), status); +} + } // namespace lrc diff --git a/src/callbackshandler.h b/src/callbackshandler.h index 8630cab8..f9dda0de 100644 --- a/src/callbackshandler.h +++ b/src/callbackshandler.h @@ -20,6 +20,8 @@ // Std #include <memory> +#include <string> +#include <map> // Qt #include <qobject.h> @@ -171,6 +173,25 @@ Q_SIGNALS: void transferStatusTimeoutExpired(long long dringId, api::datatransfer::Info info); void transferStatusUnjoinable(long long dringId, api::datatransfer::Info info); + + /** + * Connect this signal to get when a device name changed or a device is added + * @param accountId interaction receiver. + * @param devices A map of device IDs with corresponding labels. + */ + void knownDevicesChanged(std::string& accountId, + std::map<std::string,std::string> devices); + + /** + * Emit deviceRevocationEnded + * @param accountId + * @param deviceId + * @param status SUCCESS = 0, WRONG_PASSWORD = 1, UNKNOWN_DEVICE = 2 + */ + void deviceRevocationEnded(const std::string& accountId, + const std::string& deviceId, + const int status); + private Q_SLOTS: /** * Emit newAccountMessage @@ -291,6 +312,24 @@ private Q_SLOTS: void slotDataTransferEvent(qulonglong id, uint code); + /** + * Emit knownDevicesChanged + * @param accountId + * @param devices A map of device IDs and corresponding labels + */ + void slotKnownDevicesChanged(const QString& accountId, + const QMap<QString, QString>& devices); + + /** + * Emit deviceRevocationEnded + * @param accountId + * @param deviceId + * @param status SUCCESS = 0, WRONG_PASSWORD = 1, UNKNOWN_DEVICE = 2 + */ + void slotDeviceRevokationEnded(const QString& accountId, + const QString& deviceId, + const int status); + private: const api::Lrc& parent; }; diff --git a/src/newaccountmodel.cpp b/src/newaccountmodel.cpp index 14244153..862d4249 100644 --- a/src/newaccountmodel.cpp +++ b/src/newaccountmodel.cpp @@ -20,12 +20,13 @@ // LRC -#include "api/lrc.h" -#include "api/newcallmodel.h" -#include "api/contactmodel.h" -#include "api/conversationmodel.h" #include "api/account.h" #include "api/behaviorcontroller.h" +#include "api/contactmodel.h" +#include "api/conversationmodel.h" +#include "api/lrc.h" +#include "api/newcallmodel.h" +#include "api/newdevicemodel.h" #include "authority/databasehelper.h" #include "callbackshandler.h" #include "database.h" @@ -219,6 +220,7 @@ NewAccountModelPimpl::addToAccounts(const std::string& accountId) owner.callModel = std::make_unique<NewCallModel>(owner, callbacksHandler); owner.contactModel = std::make_unique<ContactModel>(owner, database, callbacksHandler); owner.conversationModel = std::make_unique<ConversationModel>(owner, lrc, database, callbacksHandler, behaviorController); + owner.deviceModel = std::make_unique<NewDeviceModel>(owner, callbacksHandler); owner.accountModel = &linked; } diff --git a/src/newdevicemodel.cpp b/src/newdevicemodel.cpp new file mode 100644 index 00000000..dc916a71 --- /dev/null +++ b/src/newdevicemodel.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** + * Copyright (C) 2017-2018 Savoir-faire Linux * + * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library 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 * + * Lesser 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 <http://www.gnu.org/licenses/>. * + ***************************************************************************/ +#include "api/newdevicemodel.h" + +// std +#include <list> +#include <mutex> + +// LRC +#include "callbackshandler.h" +#include "dbus/configurationmanager.h" + +// Daemon +#include <account_const.h> + +// Qt +#include <QObject> + +namespace lrc +{ + + +using namespace api; + +class NewDeviceModelPimpl: public QObject +{ + Q_OBJECT +public: + NewDeviceModelPimpl(const NewDeviceModel& linked, const CallbacksHandler& callbacksHandler); + ~NewDeviceModelPimpl(); + + const CallbacksHandler& callbacksHandler; + const NewDeviceModel& linked; + + std::mutex devicesMtx_; + std::string currentDeviceId_; + std::list<Device> devices_; +public Q_SLOTS: + /** + * Listen from CallbacksHandler to get when a device name changed or a device is added + * @param accountId interaction receiver. + * @param devices A map of device IDs with corresponding labels. + */ + void slotKnownDevicesChanged(const std::string& accountId, + const std::map<std::string, std::string> devices); + + /** + * update devices_ when a device is revoked + * @param accountId + * @param deviceId + * @param status SUCCESS = 0, WRONG_PASSWORD = 1, UNKNOWN_DEVICE = 2 + */ + void slotDeviceRevocationEnded(const std::string& accountId, + const std::string& deviceId, + const int status); +}; + +NewDeviceModel::NewDeviceModel(const account::Info& owner, const CallbacksHandler& callbacksHandler) +: owner(owner) +, pimpl_(std::make_unique<NewDeviceModelPimpl>(*this, callbacksHandler)) +{ } + +NewDeviceModel::~NewDeviceModel() {} + +std::list<Device> +NewDeviceModel::getAllDevices() const +{ + return pimpl_->devices_; +} + +Device +NewDeviceModel::getDevice(const std::string& id) const +{ + std::lock_guard<std::mutex> lock(pimpl_->devicesMtx_); + auto i = std::find_if( + pimpl_->devices_.begin(), pimpl_->devices_.end(), + [id](const Device& d) { + return d.id == id; + }); + + if (i == pimpl_->devices_.end()) return {}; + + return *i; +} + +void +NewDeviceModel::revokeDevice(const std::string& id, const std::string& password) +{ + ConfigurationManager::instance().revokeDevice(owner.id.c_str(), password.c_str(), id.c_str()); +} + +void +NewDeviceModel::setCurrentDeviceName(const std::string& newName) +{ + MapStringString details = {}; + details[DRing::Account::ConfProperties::RING_DEVICE_NAME] = newName.c_str(); + ConfigurationManager::instance().setAccountDetails(owner.id.c_str(), details); +} + +NewDeviceModelPimpl::NewDeviceModelPimpl(const NewDeviceModel& linked, const CallbacksHandler& callbacksHandler) +: linked(linked) +, callbacksHandler(callbacksHandler) +, devices_({}) +{ + const MapStringString aDetails = ConfigurationManager::instance().getAccountDetails(linked.owner.id.c_str()); + currentDeviceId_ = aDetails.value(DRing::Account::ConfProperties::RING_DEVICE_ID).toStdString(); + const MapStringString accountDevices = ConfigurationManager::instance().getKnownRingDevices(linked.owner.id.c_str()); + auto it = accountDevices.begin(); + while (it != accountDevices.end()) { + { + std::lock_guard<std::mutex> lock(devicesMtx_); + auto device = Device { + /* id= */it.key().toStdString(), + /* name= */it.value().toStdString(), + /* isCurrent= */it.key().toStdString() == currentDeviceId_ + }; + if (device.isCurrent) { + devices_.emplace_front(device); + } else { + devices_.emplace_back(device); + } + } + ++it; + } + + connect(&callbacksHandler, &CallbacksHandler::knownDevicesChanged, this, + &NewDeviceModelPimpl::slotKnownDevicesChanged); + connect(&callbacksHandler, &CallbacksHandler::deviceRevocationEnded, this, + &NewDeviceModelPimpl::slotDeviceRevocationEnded); +} + +NewDeviceModelPimpl::~NewDeviceModelPimpl() +{ + disconnect(&callbacksHandler, &CallbacksHandler::knownDevicesChanged, this, + &NewDeviceModelPimpl::slotKnownDevicesChanged); + disconnect(&callbacksHandler, &CallbacksHandler::deviceRevocationEnded, this, + &NewDeviceModelPimpl::slotDeviceRevocationEnded); +} + +void +NewDeviceModelPimpl::slotKnownDevicesChanged(const std::string& accountId, + const std::map<std::string, std::string> devices) +{ + if (accountId != linked.owner.id) return; + auto devicesMap = devices; + // Update current devices + std::list<std::string> updatedDevices; + { + std::lock_guard<std::mutex> lock(devicesMtx_); + for (auto& device : devices_) { + if (devicesMap.find(device.id) != devicesMap.end()) { + if (device.name != devicesMap[device.id]) { + updatedDevices.emplace_back(device.id); + device.name = devicesMap[device.id]; + } + devicesMap.erase(device.id); + } + } + } + for (const auto& device : updatedDevices) + emit linked.deviceUpdated(device); + + // Add new devices + std::list<std::string> addedDevices; + { + std::lock_guard<std::mutex> lock(devicesMtx_); + auto it = devicesMap.begin(); + while (it != devicesMap.end()) { + devices_.emplace_back(Device { + /* id= */it->first, + /* name= */it->second, + /* isCurrent= */false + }); + addedDevices.emplace_back(it->first); + ++it; + } + } + for (const auto& device : addedDevices) + emit linked.deviceAdded(device); +} + + +void +NewDeviceModelPimpl::slotDeviceRevocationEnded(const std::string& accountId, + const std::string& deviceId, + const int status) +{ + if (accountId != linked.owner.id) return; + if (status == 0) { + std::lock_guard<std::mutex> lock(devicesMtx_); + auto it = std::find_if( + devices_.begin(), devices_.end(), + [deviceId](const Device& d) { + return d.id == deviceId; + }); + + if (it != devices_.end()) + devices_.erase(it); + } + + switch (status) { + case 0: + emit linked.deviceRevoked(deviceId, NewDeviceModel::Status::SUCCESS); + break; + case 1: + emit linked.deviceRevoked(deviceId, NewDeviceModel::Status::WRONG_PASSWORD); + break; + case 2: + emit linked.deviceRevoked(deviceId, NewDeviceModel::Status::UNKNOWN_DEVICE); + break; + default: + break; + } +} + +} // namespace lrc + +#include "newdevicemodel.moc" +#include "api/moc_newdevicemodel.cpp" diff --git a/src/qtwrapper/configurationmanager_wrap.h b/src/qtwrapper/configurationmanager_wrap.h index 5846d5f9..3d4e0ca6 100644 --- a/src/qtwrapper/configurationmanager_wrap.h +++ b/src/qtwrapper/configurationmanager_wrap.h @@ -566,6 +566,10 @@ public Q_SLOTS: // METHODS DRing::removeContact(accountId.toStdString(), uri.toStdString(), ban); } + void revokeDevice(const QString &accountId, const QString &password, const QString &deviceId) { + DRing::revokeDevice(accountId.toStdString(), password.toStdString(), deviceId.toStdString()); + } + void addContact(const QString &accountId, const QString &uri) { DRing::addContact(accountId.toStdString(), uri.toStdString()); } @@ -676,6 +680,7 @@ Q_SIGNALS: // SIGNALS void contactAdded(const QString &accountID, const QString &uri, bool banned); void contactRemoved(const QString &accountID, const QString &uri, bool banned); void dataTransferEvent(qulonglong transfer_id, uint code); + void deviceRevocationEnded(const QString& accountId, const QString& deviceId, int status); }; namespace org { namespace ring { namespace Ring { diff --git a/test/mocks/configurationmanager_mock.h b/test/mocks/configurationmanager_mock.h index f23d418b..76abe21b 100644 --- a/test/mocks/configurationmanager_mock.h +++ b/test/mocks/configurationmanager_mock.h @@ -53,6 +53,7 @@ private: QMap<QString, VectorMapStringString> accountToContactsMap; QStringList availableContacts_; std::mutex contactsMtx_; + QMap<QString, QMap<QString, QString>> devices; public: @@ -86,11 +87,22 @@ public: } } accountToContactsMap.insert(account, contacts); + // Init devices + MapStringString devicesForAccount; + devicesForAccount["device0"] = "pc"; + if (account.toStdString() == "ring3") + devicesForAccount["device1"] = "tel"; + devices[account] = devicesForAccount; } } ~ConfigurationManagerInterface() {} + void addNewDevice(const QString& accountId, const QString& deviceId, const QString& name) { + devices[accountId][deviceId] = name; + emit knownDevicesChanged(accountId, devices[accountId]); + } + void emitIncomingAccountMessage(const QString& accountId, const QString& from, const QMap<QString,QString>& payloads) { emit incomingAccountMessage(accountId, from, payloads); @@ -137,9 +149,7 @@ public Q_SLOTS: // METHODS MapStringString getKnownRingDevices(const QString& accountId) { - Q_UNUSED(accountId) - MapStringString temp; - return temp; + return devices[accountId]; } bool lookupName(const QString& accountId, const QString& nameServiceURL, const QString& name) @@ -174,6 +184,7 @@ public Q_SLOTS: // METHODS } else { result.insert("Account.type", "SIP"); } + result.insert("Account.deviceID", "device0"); return result; } @@ -183,6 +194,7 @@ public Q_SLOTS: // METHODS accountList << QString("ring0"); // Used in conversationmodeltester accountList << QString("ring1"); // Used in contactmodeltester accountList << QString("ring2"); // Used in newcallmodeltester + accountList << QString("ring3"); // Used in newdevicemodeltester accountList << QString("sip0"); accountList << QString("sip1"); return accountList; @@ -448,8 +460,13 @@ public Q_SLOTS: // METHODS void setAccountDetails(const QString& accountId, MapStringString details) { - Q_UNUSED(accountId) - Q_UNUSED(details) + if (accountId.toStdString() == "ring3") { + // testSetCurrentDeviceName + if (details.contains(DRing::Account::ConfProperties::RING_DEVICE_NAME)) { + devices["ring3"]["device0"] = details[DRing::Account::ConfProperties::RING_DEVICE_NAME]; + emit knownDevicesChanged(accountId, devices[accountId]); + } + } } void setAccountsOrder(const QString& order) @@ -648,6 +665,19 @@ public Q_SLOTS: // METHODS emit contactRemoved(accountId, uri, ban); } + void revokeDevice(const QString &accountId, const QString &password, const QString &deviceId) { + if (password == "") { + if (devices[accountId].contains(deviceId)) { + devices[accountId].remove(deviceId); + emit deviceRevocationEnded(accountId, deviceId, 0); + } else { + emit deviceRevocationEnded(accountId, deviceId, 2); + } + } else { + emit deviceRevocationEnded(accountId, deviceId, 1); + } + } + void addContact(const QString &accountId, const QString &uri) { if (getAccountList().indexOf(accountId) == -1) return; @@ -803,6 +833,7 @@ Q_SIGNALS: // SIGNALS void contactAdded(const QString &accountId, const QString &uri, bool banned); void contactRemoved(const QString &accountId, const QString &uri, bool banned); void dataTransferEvent(uint64_t transfer_id, uint32_t code); + void deviceRevocationEnded(const QString& accountId, const QString& deviceId, int status); }; namespace org { diff --git a/test/newdevicemodeltester.cpp b/test/newdevicemodeltester.cpp new file mode 100644 index 00000000..4a585944 --- /dev/null +++ b/test/newdevicemodeltester.cpp @@ -0,0 +1,197 @@ +/* + * 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 "newdevicemodeltester.h" + +// std +#include <string> + +// Qt +#include "utils/waitforsignalhelper.h" + +// Lrc +#include <api/newaccountmodel.h> +#include <api/newdevicemodel.h> +#include <dbus/configurationmanager.h> + + +namespace ring +{ +namespace test +{ + +CPPUNIT_TEST_SUITE_REGISTRATION(NewDeviceModelTester); + +NewDeviceModelTester::NewDeviceModelTester() +: lrc_(new lrc::api::Lrc()) +, accInfo_(lrc_->getAccountModel().getAccountInfo("ring3")) +{ + +} + +void +NewDeviceModelTester::setUp() +{ + +} + +void +NewDeviceModelTester::testGetAllDevices() +{ + // See mocked ConfigurationManager::getKnownRingDevices() and getAccountDetails() + auto devices = accInfo_.deviceModel->getAllDevices(); + // Here, we should have 2 devices (device0 pc) and (device1 tel) + CPPUNIT_ASSERT_EQUAL(static_cast<int>(devices.size()), 2); + auto device0 = devices.front(); + CPPUNIT_ASSERT_EQUAL(device0.isCurrent, true); + CPPUNIT_ASSERT_EQUAL(device0.id, std::string("device0")); + CPPUNIT_ASSERT_EQUAL(device0.name, std::string("pc")); + auto device1 = devices.back(); + CPPUNIT_ASSERT_EQUAL(device1.isCurrent, false); + CPPUNIT_ASSERT_EQUAL(device1.id, std::string("device1")); + CPPUNIT_ASSERT_EQUAL(device1.name, std::string("tel")); +} + +void +NewDeviceModelTester::testGetValidDevice() +{ + // device0 defined in mocked ConfigurationManager + auto device0 = accInfo_.deviceModel->getDevice("device0"); + CPPUNIT_ASSERT_EQUAL(device0.isCurrent, true); + CPPUNIT_ASSERT_EQUAL(device0.id, std::string("device0")); + CPPUNIT_ASSERT_EQUAL(device0.name, std::string("pc")); +} + +void +NewDeviceModelTester::testGetInvalidDevice() +{ + // notADevice not defined in mocked ConfigurationManager + auto device0 = accInfo_.deviceModel->getDevice("notADevice"); + CPPUNIT_ASSERT_EQUAL(device0.id, std::string("")); +} + +void +NewDeviceModelTester::testNewDeviceAdded() +{ + // this will add a new device for ring3 (see mock) + ConfigurationManager::instance().addNewDevice("ring3", "device2", "tv"); + // Wait for deviceAdded + WaitForSignalHelper(*accInfo_.deviceModel, + SIGNAL(deviceAdded(const std::string& id))).wait(1000); + auto device0 = accInfo_.deviceModel->getDevice("device2"); + CPPUNIT_ASSERT_EQUAL(device0.isCurrent, false); + CPPUNIT_ASSERT_EQUAL(device0.id, std::string("device2")); + CPPUNIT_ASSERT_EQUAL(device0.name, std::string("tv")); + // Revoke device for other tests + // NOTE: should be removed when test will not depends from each others + // See mock + ConfigurationManager::instance().revokeDevice("ring3", "", "device2"); +} + +void +NewDeviceModelTester::testRevokeDevice() +{ + // this will add a new device for ring3 (see mock) + ConfigurationManager::instance().addNewDevice("ring3", "device2", "tv"); + // Wait for deviceAdded + WaitForSignalHelper(*accInfo_.deviceModel, + SIGNAL(deviceAdded(const std::string& id))).wait(1000); + // Then revoke device + accInfo_.deviceModel->revokeDevice("device2", ""); // empty password = correct + // Wait for deviceAdded + WaitForSignalHelper(*accInfo_.deviceModel, + SIGNAL(deviceRevoked(const std::string& id, + const lrc::api::NewDeviceModel::Status status))) + .wait(1000); + // Should not exists anymore + auto device2 = accInfo_.deviceModel->getDevice("device2"); + CPPUNIT_ASSERT_EQUAL(device2.id, std::string("")); +} + +void +NewDeviceModelTester::testRevokeDeviceInvalidDevice() +{ + // this will add a new device for ring3 (see mock) + ConfigurationManager::instance().addNewDevice("ring3", "device2", "tv"); + // Wait for deviceAdded + WaitForSignalHelper(*accInfo_.deviceModel, + SIGNAL(deviceAdded(const std::string& id))).wait(1000); + // Then revoke device + accInfo_.deviceModel->revokeDevice("device3", ""); // empty password = correct + // Wait for deviceAdded + WaitForSignalHelper(*accInfo_.deviceModel, + SIGNAL(deviceRevoked(const std::string& id, + const lrc::api::NewDeviceModel::Status status))) + .wait(1000); + // device2 still exists + auto device0 = accInfo_.deviceModel->getDevice("device2"); + CPPUNIT_ASSERT_EQUAL(device0.id, std::string("device2")); + // Revoke device for other tests + // NOTE: should be removed when test will not depends from each others + // See mock + ConfigurationManager::instance().revokeDevice("ring3", "", "device2"); +} + +void +NewDeviceModelTester::testRevokeDeviceInvalidPassword() +{ + // this will add a new device for ring3 (see mock) + ConfigurationManager::instance().addNewDevice("ring3", "device2", "tv"); + // Wait for deviceAdded + WaitForSignalHelper(*accInfo_.deviceModel, + SIGNAL(deviceAdded(const std::string& id))).wait(1000); + // Then revoke device + accInfo_.deviceModel->revokeDevice("device2", "notAPass"); // !empty password = incorrect + // Wait for deviceAdded + WaitForSignalHelper(*accInfo_.deviceModel, + SIGNAL(deviceRevoked(const std::string& id, + const lrc::api::NewDeviceModel::Status status))) + .wait(1000); + // device2 still exists + auto device0 = accInfo_.deviceModel->getDevice("device2"); + CPPUNIT_ASSERT_EQUAL(device0.id, std::string("device2")); + // Revoke device for other tests + // NOTE: should be removed when test will not depends from each others + // See mock + ConfigurationManager::instance().revokeDevice("ring3", "", "device2"); +} + +void +NewDeviceModelTester::testSetCurrentDeviceName() +{ + // Will change the name of device0 + accInfo_.deviceModel->setCurrentDeviceName("NewDeviceName"); + // Will call mocked ConfigurationManager::setAccountDetails() + // Because known devices changed, NewDeviceModel::deviceUpdated will be emitted + WaitForSignalHelper(*accInfo_.deviceModel, + SIGNAL(deviceUpdated(const std::string& id))).wait(1000); + // device0 should have a new name now. + auto device0 = accInfo_.deviceModel->getDevice("device0"); + CPPUNIT_ASSERT_EQUAL(device0.isCurrent, true); + CPPUNIT_ASSERT_EQUAL(device0.id, std::string("device0")); + CPPUNIT_ASSERT_EQUAL(device0.name, std::string("NewDeviceName")); +} + +void +NewDeviceModelTester::tearDown() +{ + accInfo_.deviceModel->setCurrentDeviceName("pc"); +} + +} // namespace test +} // namespace ring diff --git a/test/newdevicemodeltester.h b/test/newdevicemodeltester.h new file mode 100644 index 00000000..be1ee76e --- /dev/null +++ b/test/newdevicemodeltester.h @@ -0,0 +1,103 @@ +/* + * 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 NewDeviceModelTester : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(NewDeviceModelTester); + CPPUNIT_TEST(testGetAllDevices); + CPPUNIT_TEST(testGetValidDevice); + CPPUNIT_TEST(testGetInvalidDevice); + CPPUNIT_TEST(testNewDeviceAdded); + CPPUNIT_TEST(testRevokeDevice); + CPPUNIT_TEST(testRevokeDeviceInvalidDevice); + CPPUNIT_TEST(testRevokeDeviceInvalidPassword); + CPPUNIT_TEST(testSetCurrentDeviceName); + CPPUNIT_TEST_SUITE_END(); + +public: + NewDeviceModelTester(); + /** + * Method automatically called before each test by CppUnit + */ + void setUp(); + /** + * Retrieve all devices of an account + */ + void testGetAllDevices(); + /** + * Test getting an existing device + */ + void testGetValidDevice(); + /** + * Test getting a non existing device + */ + void testGetInvalidDevice(); + /** + * Test new device added + */ + void testNewDeviceAdded(); + /** + * Test to remove a device (valid device, valid password) + */ + void testRevokeDevice(); + /** + * Test to remove a device (invalid device, valid password) + */ + void testRevokeDeviceInvalidDevice(); + /** + * Test to remove a device (valid device, invalid password) + */ + void testRevokeDeviceInvalidPassword(); + /** + * Test to change the current device name + */ + void testSetCurrentDeviceName(); + /** + * 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 -- GitLab