Add better device setup status details to api

This commit is contained in:
Michael Zanetti 2020-01-21 22:43:02 +01:00
parent 3015130c8f
commit 6e4a0bc84b
11 changed files with 223 additions and 82 deletions

View File

@ -436,7 +436,7 @@ DeviceSetupInfo *DeviceManagerImplementation::reconfigureDeviceInternal(Device *
plugin->deviceRemoved(device);
// mark setup as incomplete
device->setSetupComplete(false);
device->setSetupStatus(Device::DeviceSetupStatusInProgress, Device::DeviceErrorNoError);
// set new params
foreach (const Param &param, params) {
@ -454,6 +454,7 @@ DeviceSetupInfo *DeviceManagerImplementation::reconfigureDeviceInternal(Device *
if (info->status() != Device::DeviceErrorNoError) {
qCWarning(dcDeviceManager()) << "Device reconfiguration failed for" << info->device()->name() << info->device()->id().toString() << info->status() << info->displayMessage();
info->device()->setSetupStatus(Device::DeviceSetupStatusFailed, info->status(), info->displayMessage());
// TODO: recover old params.??
return;
}
@ -461,7 +462,7 @@ DeviceSetupInfo *DeviceManagerImplementation::reconfigureDeviceInternal(Device *
storeConfiguredDevices();
postSetupDevice(info->device());
info->device()->setupCompleted();
info->device()->setSetupStatus(Device::DeviceSetupStatusComplete, Device::DeviceErrorNoError);
emit deviceChanged(info->device());
@ -640,7 +641,7 @@ DevicePairingInfo *DeviceManagerImplementation::confirmPairing(const PairingTran
}
} else {
device = m_configuredDevices.value(internalInfo->deviceId());
device->setSetupComplete(false);
device->setSetupStatus(Device::DeviceSetupStatusInProgress, Device::DeviceErrorNoError);
qCDebug(dcDeviceManager()) << "Reconfiguring device" << device;
}
@ -654,10 +655,17 @@ DevicePairingInfo *DeviceManagerImplementation::confirmPairing(const PairingTran
externalInfo->finish(info->status(), info->displayMessage());
if (info->status() != Device::DeviceErrorNoError) {
qCWarning(dcDeviceManager()) << "Failed to set up device" << info->device()->name() << info->status() << info->displayMessage();
info->device()->deleteLater();
if (addNewDevice) {
qCWarning(dcDeviceManager()) << "Failed to set up device" << info->device()->name()
<< "Not adding device to the system. Error:"
<< info->status() << info->displayMessage();
info->device()->deleteLater();
}
if (!addNewDevice) {
qCWarning(dcDeviceManager()) << "Failed to reconfigure device" << info->device()->name() <<
"Error:" << info->status() << info->displayMessage();
info->device()->setSetupStatus(Device::DeviceSetupStatusFailed, info->status(), info->displayMessage());
// TODO: restore parameters?
}
@ -665,7 +673,7 @@ DevicePairingInfo *DeviceManagerImplementation::confirmPairing(const PairingTran
}
qCDebug(dcDeviceManager()) << "Setup complete for device" << info->device();
info->device()->setupCompleted();
info->device()->setSetupStatus(Device::DeviceSetupStatusComplete, Device::DeviceErrorNoError);
if (addNewDevice) {
qCDebug(dcDeviceManager()) << "Device added:" << info->device();
@ -749,7 +757,7 @@ DeviceSetupInfo* DeviceManagerImplementation::addConfiguredDeviceInternal(const
return;
}
info->device()->setupCompleted();
info->device()->setSetupStatus(Device::DeviceSetupStatusComplete, Device::DeviceErrorNoError);
qCDebug(dcDeviceManager) << "Device setup complete.";
m_configuredDevices.insert(info->device()->id(), info->device());
@ -1062,7 +1070,25 @@ void DeviceManagerImplementation::loadPlugins()
if (!fi.exists())
continue;
// Check plugin API version compatibility
QLibrary lib(fi.absoluteFilePath());
QFunctionPointer versionFunc = lib.resolve("libnymea_api_version");
if (!versionFunc) {
qCWarning(dcDeviceManager()).nospace() << "Unable to resolve version in plugin " << entry << ". Not loading plugin.";
lib.unload();
continue;
}
QString version = reinterpret_cast<QString(*)()>(versionFunc)();
lib.unload();
QStringList parts = version.split('.');
QStringList coreParts = QString(LIBNYMEA_API_VERSION).split('.');
if (parts.length() != 3 || parts.at(0).toInt() != coreParts.at(0).toInt() || parts.at(1).toInt() > coreParts.at(1).toInt()) {
qCWarning(dcDeviceManager()).nospace() << "Libnymea API mismatch for " << entry << ". Core API: " << LIBNYMEA_API_VERSION << ", Plugin API: " << version;
continue;
}
// Version is ok. Now load the plugin
QPluginLoader loader;
loader.setFileName(fi.absoluteFilePath());
loader.setLoadHints(QLibrary::ResolveAllSymbolsHint);
@ -1073,29 +1099,6 @@ void DeviceManagerImplementation::loadPlugins()
continue;
}
// Check plugin API version compatibility
QLibrary lib(fi.absoluteFilePath());
QFunctionPointer versionFunc = lib.resolve("libnymea_api_version");
if (!versionFunc) {
qCWarning(dcDeviceManager()).nospace() << "Unable to resolve version in plugin " << entry << ". Not loading plugin.";
loader.unload();
lib.unload();
continue;
}
QString version = reinterpret_cast<QString(*)()>(versionFunc)();
// QString *version = reinterpret_cast<QString*>(lib.resolve("libnymea_api_version"));
// if (!version) {
// }
lib.unload();
QStringList parts = version.split('.');
QStringList coreParts = QString(LIBNYMEA_API_VERSION).split('.');
if (parts.length() != 3 || parts.at(0).toInt() != coreParts.at(0).toInt() || parts.at(1).toInt() > coreParts.at(1).toInt()) {
qCWarning(dcDeviceManager()).nospace() << "Libnymea API mismatch for " << entry << ". Core API: " << LIBNYMEA_API_VERSION << ", Plugin API: " << version;
loader.unload();
continue;
}
PluginMetadata metaData(loader.metaData().value("MetaData").toObject());
if (!metaData.isValid()) {
foreach (const QString &error, metaData.validationErrors()) {
@ -1357,6 +1360,7 @@ void DeviceManagerImplementation::loadConfiguredDevices()
}
Q_ASSERT(device != nullptr);
device->setSetupStatus(Device::DeviceSetupStatusInProgress, Device::DeviceErrorNoError);
DeviceSetupInfo *info = setupDevice(device);
// Set receiving object to "device" because at startup we load it in any case, knowing that it worked at
// some point. However, it'll be marked as non-working until the setup succeeds so the user might delete
@ -1365,11 +1369,14 @@ void DeviceManagerImplementation::loadConfiguredDevices()
if (info->status() != Device::DeviceErrorNoError) {
qCWarning(dcDeviceManager()) << "Error setting up device" << info->device()->name() << info->device()->id().toString() << info->status() << info->displayMessage();
info->device()->setSetupStatus(Device::DeviceSetupStatusFailed, info->status(), info->displayMessage());
emit deviceChanged(info->device());
return;
}
qCDebug(dcDeviceManager()) << "Setup complete for device" << info->device();
info->device()->setupCompleted();
info->device()->setSetupStatus(Device::DeviceSetupStatusComplete, Device::DeviceErrorNoError);
emit deviceChanged(info->device());
postSetupDevice(info->device());
});
}
@ -1475,7 +1482,7 @@ void DeviceManagerImplementation::onAutoDevicesAppeared(const DeviceDescriptors
return;
}
info->device()->setupCompleted();
info->device()->setSetupStatus(Device::DeviceSetupStatusComplete, Device::DeviceErrorNoError);
m_configuredDevices.insert(info->device()->id(), info->device());
storeConfiguredDevices();

View File

@ -88,6 +88,7 @@ DeviceHandler::DeviceHandler(QObject *parent) :
{
// Enums
registerEnum<Device::DeviceError>();
registerEnum<Device::DeviceSetupStatus>();
registerEnum<DeviceClass::SetupMethod>();
registerEnum<DeviceClass::CreateMethod, DeviceClass::CreateMethods>();
registerEnum<Types::Unit>();
@ -374,7 +375,7 @@ DeviceHandler::DeviceHandler(QObject *parent) :
registerNotification("DeviceAdded", description, params);
params.clear(); returns.clear();
description = "Emitted whenever the params or name of a Device are changed (by EditDevice or ReconfigureDevice).";
description = "Emitted whenever the params, name or setupStatus of a Device changes.";
params.insert("device", objectRef<Device>());
registerNotification("DeviceChanged", description, params);
@ -627,7 +628,7 @@ JsonReply *DeviceHandler::ConfirmPairing(const QVariantMap &params, const JsonCo
return jsonReply;
}
JsonReply* DeviceHandler::GetConfiguredDevices(const QVariantMap &params) const
JsonReply* DeviceHandler::GetConfiguredDevices(const QVariantMap &params, const JsonContext &context) const
{
QVariantMap returns;
QVariantList configuredDeviceList;
@ -641,7 +642,12 @@ JsonReply* DeviceHandler::GetConfiguredDevices(const QVariantMap &params) const
}
} else {
foreach (Device *device, NymeaCore::instance()->deviceManager()->configuredDevices()) {
configuredDeviceList.append(pack(device));
QVariantMap packedDevice = pack(device).toMap();
QString translatedSetupStatus = NymeaCore::instance()->deviceManager()->translate(device->pluginId(), device->setupDisplayMessage(), context.locale());
if (!translatedSetupStatus.isEmpty()) {
packedDevice["setupDisplayMessage"] = translatedSetupStatus;
}
configuredDeviceList.append(packedDevice);
}
}
returns.insert("devices", configuredDeviceList);
@ -977,6 +983,25 @@ void DeviceHandler::deviceSettingChangedNotification(const DeviceId deviceId, co
emit DeviceSettingChanged(params);
}
QVariantMap DeviceHandler::translateNotification(const QString &notification, const QVariantMap &params, const QLocale &locale)
{
if (notification == "DeviceChanged") {
QVariantMap deviceMap = params.value("device").toMap();
DeviceId deviceId = params.value("device").toMap().value("id").toUuid();
Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(deviceId);
QString setupDisplayMessage = params.value("device").toMap().value("setupDisplayMessage").toString();
QString translatedSetupDisplayMessage = NymeaCore::instance()->deviceManager()->translate(device->pluginId(), setupDisplayMessage, locale);
if (!translatedSetupDisplayMessage.isEmpty()) {
deviceMap["setupDisplayMessage"] = translatedSetupDisplayMessage;
}
QVariantMap translatedParams = params;
translatedParams["device"] = deviceMap;
return translatedParams;
}
return params;
}
QVariantMap DeviceHandler::statusToReply(Device::DeviceError status) const
{
QVariantMap returns;

View File

@ -43,6 +43,7 @@ public:
explicit DeviceHandler(QObject *parent = nullptr);
QString name() const override;
QVariantMap translateNotification(const QString &notification, const QVariantMap &params, const QLocale &locale) override;
Q_INVOKABLE JsonReply *GetSupportedVendors(const QVariantMap &params, const JsonContext &context) const;
Q_INVOKABLE JsonReply *GetSupportedDevices(const QVariantMap &params, const JsonContext &context) const;
@ -54,7 +55,7 @@ public:
Q_INVOKABLE JsonReply *AddConfiguredDevice(const QVariantMap &params, const JsonContext &context);
Q_INVOKABLE JsonReply *PairDevice(const QVariantMap &params, const JsonContext &context);
Q_INVOKABLE JsonReply *ConfirmPairing(const QVariantMap &params, const JsonContext &context);
Q_INVOKABLE JsonReply *GetConfiguredDevices(const QVariantMap &params) const;
Q_INVOKABLE JsonReply *GetConfiguredDevices(const QVariantMap &params, const JsonContext &context) const;
Q_INVOKABLE JsonReply *ReconfigureDevice(const QVariantMap &params, const JsonContext &context);
Q_INVOKABLE JsonReply *EditDevice(const QVariantMap &params);
Q_INVOKABLE JsonReply *RemoveConfiguredDevice(const QVariantMap &params);

View File

@ -738,26 +738,11 @@ void JsonRPCServerImplementation::sendNotification(const QVariantMap &params)
JsonHandler *handler = qobject_cast<JsonHandler *>(sender());
QMetaMethod method = handler->metaObject()->method(senderSignalIndex());
QList<QUuid> clientsToBeNotified;
foreach (const QUuid &clientId, m_clientNotifications.keys()) {
if (m_clientNotifications.value(clientId).contains(handler->name())) {
clientsToBeNotified.append(clientId);
}
}
if (clientsToBeNotified.isEmpty()) {
return;
}
QVariantMap notification;
notification.insert("id", m_notificationId++);
notification.insert("notification", handler->name() + "." + method.name());
notification.insert("params", params);
JsonValidator validator;
Q_ASSERT_X(validator.validateNotificationParams(params, handler->name() + '.' + method.name(), m_api).success(),
validator.result().where().toUtf8(),
validator.result().errorString().toUtf8() + "\nGot:" + QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented));
// Add deprecation warning if necessary
if (m_api.value("notifications").toMap().value(handler->name() + '.' + method.name()).toMap().contains("deprecated")) {
QString deprecationMessage = m_api.value("notifications").toMap().value(handler->name() + '.' + method.name()).toMap().value("deprecated").toString();
qCWarning(dcJsonRpc()) << "Client uses deprecated API. Please update client implementation!";
@ -765,11 +750,28 @@ void JsonRPCServerImplementation::sendNotification(const QVariantMap &params)
notification.insert("deprecationWarning", deprecationMessage);
}
QByteArray data = QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact);
qCDebug(dcJsonRpcTraffic()) << "Notification content:" << data;
foreach (const QUuid &clientId, m_clientNotifications.keys()) {
// Check if this client wants to be notified
if (!m_clientNotifications.value(clientId).contains(handler->name())) {
continue;
}
QLocale locale = m_clientLocales.value(clientId);
QVariantMap translatedParams = handler->translateNotification(method.name(), params, locale);
JsonValidator validator;
Q_ASSERT_X(validator.validateNotificationParams(translatedParams, handler->name() + '.' + method.name(), m_api).success(),
validator.result().where().toUtf8(),
validator.result().errorString().toUtf8() + "\nGot:" + QJsonDocument::fromVariant(translatedParams).toJson(QJsonDocument::Indented));
notification.insert("params", translatedParams);
QByteArray data = QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact);
qCDebug(dcJsonRpc()) << "Sending notification" << handler->name() + "." + method.name() << "to client" << clientId;
qCDebug(dcJsonRpcTraffic()) << "Notification content:" << data;
foreach (const QUuid &clientId, clientsToBeNotified) {
qCDebug(dcJsonRpc()) << "Sending notification:" << handler->name() + "." + method.name();
m_clientTransports.value(clientId)->sendData(clientId, data);
}
}

View File

@ -122,7 +122,7 @@ JsonValidator::Result JsonValidator::validateMap(const QVariantMap &map, const Q
continue;
}
QString trimmedKey = key;
trimmedKey.remove(QRegExp("^(o:|r:|d:)"));
trimmedKey.remove(QRegExp("^(o:|r:|d:)*"));
if (!map.contains(trimmedKey)) {
return Result(false, "Missing required key: " + key, key);
}

View File

@ -138,11 +138,6 @@ Device::Device(DevicePlugin *plugin, const DeviceClass &deviceClass, QObject *pa
}
void Device::setupCompleted()
{
m_setupComplete = true;
}
/*! Returns the id of this Device. */
DeviceId Device::id() const
{
@ -366,10 +361,16 @@ void Device::setParentId(const DeviceId &parentId)
m_parentId = parentId;
}
/*! Returns true, if setup of this Device is already completed. */
/*! Returns true, if setup of this Device is already completed. This method is deprecated, use setupStatus() instead. */
bool Device::setupComplete() const
{
return m_setupComplete;
return m_setupStatus == DeviceSetupStatusComplete;
}
/*! Returns the setup error display message, if any. */
QString Device::setupDisplayMessage() const
{
return m_setupDisplayMessage;
}
/*! Returns true if this device has been auto-created (not created by the user) */
@ -378,9 +379,23 @@ bool Device::autoCreated() const
return m_autoCreated;
}
void Device::setSetupComplete(bool complete)
/* Returns the current device setup status. */
Device::DeviceSetupStatus Device::setupStatus() const
{
m_setupComplete = complete;
return m_setupStatus;
}
Device::DeviceError Device::setupError() const
{
return m_setupError;
}
void Device::setSetupStatus(Device::DeviceSetupStatus status, Device::DeviceError setupError, const QString &displayMessage)
{
m_setupStatus = status;
m_setupError = setupError;
m_setupDisplayMessage = displayMessage;
emit setupStatusChanged();
}
Devices::Devices(const QList<Device*> &other)

View File

@ -50,12 +50,15 @@ class LIBNYMEA_EXPORT Device: public QObject
Q_OBJECT
Q_PROPERTY(QUuid id READ id)
Q_PROPERTY(QUuid deviceClassId READ deviceClassId)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(ParamList params READ params WRITE setParams)
Q_PROPERTY(ParamList settings READ settings WRITE setSettings)
Q_PROPERTY(States states READ states WRITE setStates)
Q_PROPERTY(bool setupComplete READ setupComplete WRITE setSetupComplete)
Q_PROPERTY(QUuid parentId READ parentId WRITE setParentId USER true)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged USER true)
Q_PROPERTY(ParamList params READ params)
Q_PROPERTY(ParamList settings READ settings WRITE setSettings USER true)
Q_PROPERTY(States states READ states)
Q_PROPERTY(bool setupComplete READ setupComplete NOTIFY setupStatusChanged REVISION 1)
Q_PROPERTY(DeviceSetupStatus setupStatus READ setupStatus NOTIFY setupStatusChanged)
Q_PROPERTY(QString setupDisplayMessage READ setupDisplayMessage NOTIFY setupStatusChanged USER true)
Q_PROPERTY(DeviceError setupError READ setupError NOTIFY setupStatusChanged)
Q_PROPERTY(QUuid parentId READ parentId USER true)
public:
enum DeviceError {
@ -89,6 +92,14 @@ public:
};
Q_ENUM(DeviceError)
enum DeviceSetupStatus {
DeviceSetupStatusNone,
DeviceSetupStatusInProgress,
DeviceSetupStatusComplete,
DeviceSetupStatusFailed,
};
Q_ENUM(DeviceSetupStatus)
DeviceId id() const;
DeviceClassId deviceClassId() const;
PluginId pluginId() const;
@ -125,13 +136,19 @@ public:
DeviceId parentId() const;
void setParentId(const DeviceId &parentId);
// Deprecated
bool setupComplete() const;
bool autoCreated() const;
DeviceSetupStatus setupStatus() const;
DeviceError setupError() const;
QString setupDisplayMessage() const;
signals:
void stateValueChanged(const StateTypeId &stateTypeId, const QVariant &value);
void settingChanged(const ParamTypeId &paramTypeId, const QVariant &value);
void nameChanged();
void setupStatusChanged();
private:
friend class DeviceManager;
@ -139,8 +156,7 @@ private:
Device(DevicePlugin *plugin, const DeviceClass &deviceClass, const DeviceId &id, QObject *parent = nullptr);
Device(DevicePlugin *plugin, const DeviceClass &deviceClass, QObject *parent = nullptr);
void setupCompleted();
void setSetupComplete(bool complete);
void setSetupStatus(Device::DeviceSetupStatus status, Device::DeviceError setupError, const QString &displayMessage = QString());
private:
DeviceClass m_deviceClass;
@ -152,8 +168,11 @@ private:
ParamList m_params;
ParamList m_settings;
States m_states;
bool m_setupComplete = false;
bool m_autoCreated = false;
DeviceSetupStatus m_setupStatus = DeviceSetupStatusNone;
DeviceError m_setupError = DeviceErrorNoError;
QString m_setupDisplayMessage;
};
QDebug operator<<(QDebug dbg, Device *device);

View File

@ -41,6 +41,13 @@ JsonHandler::JsonHandler(QObject *parent) : QObject(parent)
registerEnum<BasicType>();
}
QVariantMap JsonHandler::translateNotification(const QString &notification, const QVariantMap &params, const QLocale &locale)
{
Q_UNUSED(notification)
Q_UNUSED(locale)
return params;
}
QVariantMap JsonHandler::jsonEnums() const
{
return m_enums;
@ -186,6 +193,9 @@ void JsonHandler::registerObject(const QMetaObject &metaObject)
if (!metaProperty.isWritable()) {
name.prepend("r:");
}
if (metaProperty.revision() == 1) {
name.prepend("d:");
}
QVariant typeName;
if (metaProperty.type() == QVariant::UserType) {
if (metaProperty.typeName() == QStringLiteral("QVariant::Type")) {

View File

@ -65,6 +65,8 @@ public:
virtual QString name() const = 0;
virtual QVariantMap translateNotification(const QString &notification, const QVariantMap &params, const QLocale &locale);
QVariantMap jsonEnums() const;
QVariantMap jsonFlags() const;
QVariantMap jsonObjects() const;

View File

@ -76,6 +76,12 @@
"DeviceErrorUnsupportedFeature",
"DeviceErrorTimeout"
],
"DeviceSetupStatus": [
"DeviceSetupStatusNone",
"DeviceSetupStatusInProgress",
"DeviceSetupStatusComplete",
"DeviceSetupStatusFailed"
],
"InputType": [
"InputTypeNone",
"InputTypeTextLine",
@ -1609,7 +1615,7 @@
}
},
"Devices.DeviceChanged": {
"description": "Emitted whenever the params or name of a Device are changed (by EditDevice or ReconfigureDevice).",
"description": "Emitted whenever the params, name or setupStatus of a Device changes.",
"params": {
"device": "$ref:Device"
}
@ -1904,14 +1910,17 @@
"$ref:CalendarItem"
],
"Device": {
"name": "String",
"o:parentId": "Uuid",
"params": "$ref:ParamList",
"d:r:setupComplete": "Bool",
"o:name": "String",
"o:settings": "$ref:ParamList",
"r:deviceClassId": "Uuid",
"r:id": "Uuid",
"settings": "$ref:ParamList",
"setupComplete": "Bool",
"states": "$ref:States"
"r:o:parentId": "Uuid",
"r:o:setupDisplayMessage": "String",
"r:params": "$ref:ParamList",
"r:setupError": "$ref:DeviceError",
"r:setupStatus": "$ref:DeviceSetupStatus",
"r:states": "$ref:States"
},
"DeviceClass": {
"r:actionTypes": "$ref:ActionTypes",

View File

@ -136,6 +136,8 @@ private slots:
void params();
void asyncSetupEmitsSetupStatusUpdate();
// Keep those at last as they will remove devices
void removeDevice_data();
void removeDevice();
@ -1971,6 +1973,55 @@ void TestDevices::params()
QVERIFY(!event.param(ParamTypeId::createParamTypeId()).value().isValid());
}
void TestDevices::asyncSetupEmitsSetupStatusUpdate()
{
QVariantMap configuredDevices = injectAndWait("Devices.GetConfiguredDevices").toMap();
foreach (const QVariant &deviceVariant, configuredDevices.value("params").toMap().value("devices").toList()) {
QVariantMap device = deviceVariant.toMap();
qCDebug(dcTests()) << "confdiguredd device" << device.value("setupStatus");
}
// Restart the core instance to check if settings are loaded at startup
restartServer();
enableNotifications({"Devices"});
QSignalSpy notificationSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
configuredDevices = injectAndWait("Devices.GetConfiguredDevices").toMap();
QList<QUuid> devicesWithSetupInProgress;
foreach (const QVariant &deviceVariant, configuredDevices.value("params").toMap().value("devices").toList()) {
QVariantMap device = deviceVariant.toMap();
qCDebug(dcTests()) << "Configured device" << device.value("name").toString() << "with setup status" << device.value("setupStatus").toString();
if (device.value("setupStatus").toString() == "DeviceSetupStatusInProgress") {
devicesWithSetupInProgress << device.value("id").toUuid();
}
}
QVERIFY2(devicesWithSetupInProgress.count() > 0, "This test requires at least one device that is still being set up at this point.");
QDateTime maxTime = QDateTime::currentDateTime().addSecs(10);
while (QDateTime::currentDateTime() < maxTime && devicesWithSetupInProgress.count() > 0) {
QList<QList<QVariant>> notifications = notificationSpy;
while (notifications.count() > 0) {
QByteArray notificationData = notifications.takeFirst().at(1).toByteArray();
QVariantMap notification = QJsonDocument::fromJson(notificationData).toVariant().toMap();
if (notification.value("notification").toString() == "Devices.DeviceChanged") {
QString setupStatus = notification.value("params").toMap().value("device").toMap().value("setupStatus").toString();
if (setupStatus == "DeviceSetupStatusComplete") {
qCDebug(dcTests()) << "Device setup completed for" << notification.value("params").toMap().value("device").toMap().value("name").toString();
DeviceId deviceId = notification.value("params").toMap().value("device").toMap().value("id").toUuid();
devicesWithSetupInProgress.removeAll(deviceId);
}
}
}
notificationSpy.clear();
if (devicesWithSetupInProgress.count() > 0) {
notificationSpy.wait();
}
}
QVERIFY2(devicesWithSetupInProgress.isEmpty(), "Some devices did not finish the setup!");
}
#include "testdevices.moc"
QTEST_MAIN(TestDevices)