From c6a0cecb0f30e499927b8bf6e1bd1f574895ff18 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 15 Jul 2019 17:00:36 +0200 Subject: [PATCH] Fix parenting mechanism for discovered devices --- .../devices/devicemanagerimplementation.cpp | 5 +- .../devices/devicemanagerimplementation.h | 2 +- plugins/mock/devicepluginmock.cpp | 21 +++++ plugins/mock/devicepluginmock.json | 4 +- plugins/mock/mock.pro | 2 + tests/auto/devices/testdevices.cpp | 76 +++++++++++++++++++ 6 files changed, 105 insertions(+), 5 deletions(-) diff --git a/libnymea-core/devices/devicemanagerimplementation.cpp b/libnymea-core/devices/devicemanagerimplementation.cpp index e9c5e9c0..44f06a09 100644 --- a/libnymea-core/devices/devicemanagerimplementation.cpp +++ b/libnymea-core/devices/devicemanagerimplementation.cpp @@ -361,7 +361,7 @@ Device::DeviceError DeviceManagerImplementation::addConfiguredDevice(const Devic } } - return addConfiguredDeviceInternal(deviceClassId, name, finalParams, deviceId); + return addConfiguredDeviceInternal(deviceClassId, name, finalParams, deviceId, descriptor.parentDeviceId()); } @@ -619,7 +619,7 @@ Device::DeviceError DeviceManagerImplementation::confirmPairing(const PairingTra /*! This method will only be used from the DeviceManagerImplementation in order to add a \l{Device} with the given \a deviceClassId, \a name, \a params and \ id. * Returns \l{DeviceError} to inform about the result. */ -Device::DeviceError DeviceManagerImplementation::addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms, const DeviceId id) +Device::DeviceError DeviceManagerImplementation::addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms, const DeviceId id, const DeviceId &parentDeviceId) { DeviceClass deviceClass = findDeviceClass(deviceClassId); if (deviceClass.id().isNull()) { @@ -648,6 +648,7 @@ Device::DeviceError DeviceManagerImplementation::addConfiguredDeviceInternal(con } Device *device = new Device(plugin, deviceClass, id, this); + device->setParentId(parentDeviceId); if (name.isEmpty()) { device->setName(deviceClass.name()); } else { diff --git a/libnymea-core/devices/devicemanagerimplementation.h b/libnymea-core/devices/devicemanagerimplementation.h index 608e5a70..5b4aa43d 100644 --- a/libnymea-core/devices/devicemanagerimplementation.h +++ b/libnymea-core/devices/devicemanagerimplementation.h @@ -125,7 +125,7 @@ private slots: void slotDeviceSettingChanged(const ParamTypeId ¶mTypeId, const QVariant &value); private: - Device::DeviceError addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms, const DeviceId id = DeviceId::createDeviceId()); + Device::DeviceError addConfiguredDeviceInternal(const DeviceClassId &deviceClassId, const QString &name, const ParamList ¶ms, const DeviceId id = DeviceId::createDeviceId(), const DeviceId &parentDeviceId = DeviceId()); Device::DeviceSetupStatus setupDevice(Device *device); void postSetupDevice(Device *device); void storeDeviceStates(Device *device); diff --git a/plugins/mock/devicepluginmock.cpp b/plugins/mock/devicepluginmock.cpp index 8ae7a89d..2c4f94e4 100644 --- a/plugins/mock/devicepluginmock.cpp +++ b/plugins/mock/devicepluginmock.cpp @@ -75,7 +75,28 @@ Device::DeviceError DevicePluginMock::discoverDevices(const DeviceClassId &devic m_discoveredDeviceCount = params.paramValue(mockDisplayPinDiscoveryResultCountParamTypeId).toInt(); QTimer::singleShot(1000, this, SLOT(emitDisplayPinDevicesDiscovered())); return Device::DeviceErrorAsync; + } else if (deviceClassId == mockParentDeviceClassId) { + qCDebug(dcMockDevice()) << "Starting discovery for mock device parent"; + QTimer::singleShot(1000, this, [this](){ + DeviceDescriptor descriptor(mockParentDeviceClassId, "Mock Parent (Discovered)"); + emit devicesDiscovered(mockParentDeviceClassId, {descriptor}); + + }); + return Device::DeviceErrorAsync; + } else if (deviceClassId == mockChildDeviceClassId) { + QTimer::singleShot(1000, this, [this](){ + QList descriptors; + if (!myDevices().filterByDeviceClassId(mockParentDeviceClassId).isEmpty()) { + Device *parent = myDevices().filterByDeviceClassId(mockParentDeviceClassId).first(); + DeviceDescriptor descriptor(mockChildDeviceClassId, "Mock Child (Discovered)", QString(), parent->id()); + descriptors.append(descriptor); + } + emit devicesDiscovered(mockChildDeviceClassId, descriptors); + }); + return Device::DeviceErrorAsync; } + + qCWarning(dcMockDevice()) << "Cannot discover for deviceClassId" << deviceClassId; return Device::DeviceErrorDeviceClassNotFound; } diff --git a/plugins/mock/devicepluginmock.json b/plugins/mock/devicepluginmock.json index f00f0a34..0ea2282b 100644 --- a/plugins/mock/devicepluginmock.json +++ b/plugins/mock/devicepluginmock.json @@ -501,7 +501,7 @@ "name": "mockParent", "displayName": "Mock Device (Parent)", "interfaces": ["system"], - "createMethods": ["user"], + "createMethods": ["user", "discovery"], "paramTypes": [ ], "stateTypes": [ { @@ -520,7 +520,7 @@ "id": "40893c9f-bc47-40c1-8bf7-b390c7c1b4fc", "name": "mockChild", "displayName": "Mock Device (Child)", - "createMethods": ["auto"], + "createMethods": ["auto", "discovery"], "paramTypes": [], "stateTypes": [ { diff --git a/plugins/mock/mock.pro b/plugins/mock/mock.pro index a9eecc28..9613a6cc 100644 --- a/plugins/mock/mock.pro +++ b/plugins/mock/mock.pro @@ -4,6 +4,8 @@ QT+= network TARGET = $$qtLibraryTarget(nymea_devicepluginmock) +OTHER_FILES += devicepluginmock.json + SOURCES += \ devicepluginmock.cpp \ httpdaemon.cpp diff --git a/tests/auto/devices/testdevices.cpp b/tests/auto/devices/testdevices.cpp index 1a316d17..fe7e708e 100644 --- a/tests/auto/devices/testdevices.cpp +++ b/tests/auto/devices/testdevices.cpp @@ -29,6 +29,9 @@ class TestDevices : public NymeaTestBase { Q_OBJECT +protected slots: + void initTestCase(); + private slots: void getPlugins(); @@ -99,8 +102,20 @@ private slots: void removeDevice(); void removeAutoDevice(); + + void discoverDeviceParenting(); }; +void TestDevices::initTestCase() +{ + NymeaTestBase::initTestCase(); + QLoggingCategory::setFilterRules("*.debug=false\n" + "Tests.debug=true\n" + "MockDevice.debug=true\n" + ); + +} + void TestDevices::getPlugins() { QVariant response = injectAndWait("Devices.GetPlugins"); @@ -1466,6 +1481,67 @@ void TestDevices::removeAutoDevice() QVERIFY2(NymeaCore::instance()->deviceManager()->findConfiguredDevices(mockDeviceAutoDeviceClassId).count() == 0, "Mock device has not disappeared even though it should have."); } +void TestDevices::discoverDeviceParenting() +{ + // Try to discover a mock child device. We don't have a mockParent yet, so it should fail + QSignalSpy spy(NymeaCore::instance()->deviceManager(), &DeviceManager::devicesDiscovered); + Device::DeviceError status = NymeaCore::instance()->deviceManager()->discoverDevices(mockChildDeviceClassId, ParamList()); + QCOMPARE(status, Device::DeviceErrorAsync); + spy.wait(); + QCOMPARE(spy.first().at(0).value().toString(), mockChildDeviceClassId.toString()); + QList descriptors = spy.first().at(1).value >(); + QVERIFY(descriptors.count() == 0); + + + // Now create a mock parent by discovering... + spy.clear(); + status = NymeaCore::instance()->deviceManager()->discoverDevices(mockParentDeviceClassId, ParamList()); + QCOMPARE(status, Device::DeviceErrorAsync); + spy.wait(); + QVERIFY(spy.count() == 1); + QCOMPARE(spy.first().at(0).value().toString(), mockParentDeviceClassId.toString()); + descriptors = spy.first().at(1).value >(); + QVERIFY(descriptors.count() == 1); + DeviceDescriptorId descriptorId = descriptors.first().id(); + + QSignalSpy addSpy(NymeaCore::instance()->deviceManager(), &DeviceManager::deviceAdded); + status = NymeaCore::instance()->deviceManager()->addConfiguredDevice(mockParentDeviceClassId, "Mock Parent (Discovered)", descriptorId); + QCOMPARE(status, Device::DeviceErrorNoError); + QCOMPARE(addSpy.count(), 2); // Mock device parent will also auto-create a child instantly + + Device *parentDevice = addSpy.at(1).first().value(); + qCDebug(dcTests()) << "Added device:" << parentDevice->name(); + QVERIFY(parentDevice->deviceClassId() == mockParentDeviceClassId); + + + // Ok we have our parent device, let's discover for childs again + spy.clear(); + status = NymeaCore::instance()->deviceManager()->discoverDevices(mockChildDeviceClassId, ParamList()); + QCOMPARE(status, Device::DeviceErrorAsync); + spy.wait(); + QCOMPARE(spy.first().at(0).value().toString(), mockChildDeviceClassId.toString()); + descriptors = spy.first().at(1).value >(); + QVERIFY(descriptors.count() == 1); + descriptorId = descriptors.first().id(); + + // Found one! Adding it... + addSpy.clear(); + status = NymeaCore::instance()->deviceManager()->addConfiguredDevice(mockChildDeviceClassId, "Mock Child (Discovered)", descriptorId); + QCOMPARE(status, Device::DeviceErrorNoError); + QCOMPARE(addSpy.count(), 1); + + Device *childDevice = addSpy.at(0).first().value(); + qCDebug(dcTests()) << "Added device:" << childDevice->name(); + QVERIFY(childDevice->deviceClassId() == mockChildDeviceClassId); + + // Now delete the parent and make sure the child will be deleted too + QSignalSpy removeSpy(NymeaCore::instance(), &NymeaCore::deviceRemoved); + QPair > ret = NymeaCore::instance()->removeConfiguredDevice(parentDevice->id(), QHash()); + QCOMPARE(ret.first, Device::DeviceErrorNoError); + QCOMPARE(removeSpy.count(), 3); // The parent, the auto-mock and the discovered mock + +} + #include "testdevices.moc" QTEST_MAIN(TestDevices)