/**************************************************************************** * * (c) 2009-2020 QGROUNDCONTROL PROJECT * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. * ****************************************************************************/ #include "VehicleLinkManagerTest.h" #include "QGCApplication.h" #include "MockLink.h" #include "LinkManager.h" #include "QGCApplication.h" #include "MultiSignalSpyV2.h" const char* VehicleLinkManagerTest::_primaryLinkChangedSignalName = "primaryLinkChanged"; const char* VehicleLinkManagerTest::_allLinksRemovedSignalName = "allLinksRemoved"; const char* VehicleLinkManagerTest::_communicationLostChangedSignalName = "communicationLostChanged"; const char* VehicleLinkManagerTest::_communicationLostEnabledChangedSignalName = "communicationLostEnabledChanged"; const char* VehicleLinkManagerTest::_linkNamesChangedSignalName = "linkNamesChanged"; const char* VehicleLinkManagerTest::_linkStatusesChangedSignalName = "linkStatusesChanged"; VehicleLinkManagerTest::VehicleLinkManagerTest(void) { } void VehicleLinkManagerTest::init(void) { UnitTest::init(); _multiVehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); QCOMPARE(_linkManager->links().count(), 0); QCOMPARE(_multiVehicleMgr->vehicles()->count(), 0); } void VehicleLinkManagerTest::cleanup(void) { // Disconnect all links if (_linkManager->links().count()) { QSignalSpy spyActiveVehicleChanged(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged); _linkManager->disconnectAll(); QCOMPARE(spyActiveVehicleChanged.wait(1000), true); QCOMPARE(_multiVehicleMgr->vehicles()->count(), 0); QCOMPARE(_linkManager->links().count(), 0); } _multiVehicleMgr = nullptr; UnitTest::cleanup(); } void VehicleLinkManagerTest::_simpleLinkTest(void) { SharedLinkConfigurationPtr mockConfig; SharedLinkInterfacePtr mockLink; QSignalSpy spyVehicleCreate(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged); _startMockLink(1, false /*highLatency*/, true /*incrementVehicleId*/, mockConfig, mockLink); QVERIFY(mockConfig); QVERIFY(mockLink); QSignalSpy spyConfigDelete (mockConfig.get(), &QObject::destroyed); QSignalSpy spyLinkDelete (mockLink.get(), &QObject::destroyed); QVERIFY(spyConfigDelete.isValid()); QVERIFY(spyLinkDelete.isValid()); QCOMPARE(spyVehicleCreate.wait(1000), true); QCOMPARE(_multiVehicleMgr->vehicles()->count(), 1); Vehicle* vehicle = _multiVehicleMgr->activeVehicle(); QVERIFY(vehicle); QSignalSpy spyVehicleDelete(vehicle, &QObject::destroyed); QCOMPARE(mockConfig.use_count(), 2); // Refs: This method, MockLink QCOMPARE(mockLink.use_count(), 3); // Refs: This method, LinkManager, Vehicle // We explicitly don't wait for the Vehicle to finish it's startup sequence before we disconnect. // This helps to wring out possible crashing when the link goes down before the startup sequence is complete. mockLink->disconnect(); // Vehicle should go away due to disconnect QCOMPARE(spyVehicleDelete.wait(500), true); // Config/Link should still be alive due to the last refs being held by this method QCOMPARE(spyConfigDelete.count(), 0); QCOMPARE(spyLinkDelete.count(), 0); QCOMPARE(mockConfig.use_count(), 2); // Refs: This method, MockLink QCOMPARE(mockLink.use_count(), 1); // Refs: This method // Let go of our refs from this method and config and link should go away mockConfig.reset(); mockLink.reset(); QCOMPARE(mockConfig.use_count(), 0); QCOMPARE(mockLink.use_count(), 0); QCOMPARE(spyLinkDelete.count(), 1); QCOMPARE(spyConfigDelete.count(), 1); } void VehicleLinkManagerTest::_simpleCommLossTest(void) { SharedLinkConfigurationPtr mockConfig; SharedLinkInterfacePtr mockLink; QSignalSpy spyVehicleCreate(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged); _startMockLink(1, false /*highLatency*/, true /*incrementVehicleId*/, mockConfig, mockLink); MockLink* pMockLink = qobject_cast(mockLink.get()); QCOMPARE(spyVehicleCreate.wait(1000), true); QCOMPARE(_multiVehicleMgr->vehicles()->count(), 1); Vehicle* vehicle = _multiVehicleMgr->activeVehicle(); QVERIFY(vehicle); QSignalSpy spyCommLostChanged(vehicle->vehicleLinkManager(), &VehicleLinkManager::communicationLostChanged); pMockLink->setCommLost(true); QCOMPARE(spyCommLostChanged.wait(VehicleLinkManager::_heartbeatMaxElpasedMSecs * 2), true); QCOMPARE(spyCommLostChanged.count(), 1); QCOMPARE(spyCommLostChanged[0][0].toBool(), true); spyCommLostChanged.clear(); pMockLink->setCommLost(false); QCOMPARE(spyCommLostChanged.wait(VehicleLinkManager::_heartbeatMaxElpasedMSecs * 2), true); QCOMPARE(spyCommLostChanged.count(), 1); QCOMPARE(spyCommLostChanged[0][0].toBool(), false); spyCommLostChanged.clear(); vehicle->vehicleLinkManager()->setCommunicationLostEnabled(false); pMockLink->setCommLost(true); QCOMPARE(spyCommLostChanged.wait(VehicleLinkManager::_heartbeatMaxElpasedMSecs * 2), false); spyCommLostChanged.clear(); vehicle->vehicleLinkManager()->setCommunicationLostEnabled(true); QCOMPARE(spyCommLostChanged.wait(VehicleLinkManager::_heartbeatMaxElpasedMSecs * 2), true); QCOMPARE(spyCommLostChanged.count(), 1); } void VehicleLinkManagerTest::_multiLinkSingleVehicleTest(void) { SharedLinkConfigurationPtr mockConfig1; SharedLinkInterfacePtr mockLink1; SharedLinkConfigurationPtr mockConfig2; SharedLinkInterfacePtr mockLink2; QSignalSpy spyVehicleCreate(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged); _startMockLink(1, false /*highLatency*/, false /*incrementVehicleId*/, mockConfig1, mockLink1); _startMockLink(2, false /*highLatency*/, false /*incrementVehicleId*/, mockConfig2, mockLink2); MockLink* pMockLink1 = qobject_cast(mockLink1.get()); MockLink* pMockLink2 = qobject_cast(mockLink2.get()); QCOMPARE(spyVehicleCreate.wait(1000), true); QCOMPARE(_multiVehicleMgr->vehicles()->count(), 1); Vehicle* vehicle = _multiVehicleMgr->activeVehicle(); VehicleLinkManager* vehicleLinkManager = vehicle->vehicleLinkManager(); QVERIFY(vehicle); QVERIFY(vehicleLinkManager); QCOMPARE(mockLink1.get(), vehicleLinkManager->primaryLink()); QStringList rgNames = vehicleLinkManager->linkNames(); QStringList rgStatus = vehicleLinkManager->linkStatuses(); QCOMPARE(rgNames.count(), 2); QCOMPARE(rgNames[0], mockConfig1->name()); QCOMPARE(rgNames[1], mockConfig2->name()); QCOMPARE(rgStatus.count(), 2); QVERIFY(rgStatus[0].isEmpty()); QVERIFY(rgStatus[1].isEmpty()); MultiSignalSpyV2 multiSpy; QVERIFY(multiSpy.init(vehicleLinkManager)); // Comm lost on 2: 1 is primary, 2 is secondary so comm loss/regain on 2 should only update status text pMockLink2->setCommLost(true); QCOMPARE(multiSpy.waitForSignal (_linkStatusesChangedSignalName, VehicleLinkManager::_heartbeatMaxElpasedMSecs * 2), true); QVERIFY(multiSpy.checkOnlySignalByMask(multiSpy.signalNameToMask(_linkStatusesChangedSignalName))); rgStatus = vehicleLinkManager->linkStatuses(); QCOMPARE(rgStatus.count(), 2); QVERIFY(rgStatus[0].isEmpty()); QVERIFY(!rgStatus[1].isEmpty()); multiSpy.clearAllSignals(); pMockLink2->setCommLost(false); QCOMPARE(multiSpy.waitForSignal (_linkStatusesChangedSignalName, VehicleLinkManager::_heartbeatMaxElpasedMSecs * 2), true); QVERIFY(multiSpy.checkOnlySignalByMask(multiSpy.signalNameToMask(_linkStatusesChangedSignalName))); rgStatus = vehicleLinkManager->linkStatuses(); QCOMPARE(rgStatus.count(), 2); QVERIFY(rgStatus[0].isEmpty()); QVERIFY(rgStatus[1].isEmpty()); multiSpy.clearAllSignals(); // Comm loss on 1: 1 is primary so should trigger switch of primary to 2 pMockLink1->setCommLost(true); QCOMPARE(multiSpy.waitForSignal (_primaryLinkChangedSignalName, VehicleLinkManager::_heartbeatMaxElpasedMSecs * 2), true); quint32 signalMask = multiSpy.signalNameToMask(_primaryLinkChangedSignalName) | multiSpy.signalNameToMask(_linkStatusesChangedSignalName); QVERIFY(multiSpy.checkOnlySignalByMask(signalMask)); QCOMPARE(pMockLink2, vehicleLinkManager->primaryLink()); rgStatus = vehicleLinkManager->linkStatuses(); QCOMPARE(rgStatus.count(), 2); QVERIFY(!rgStatus[0].isEmpty()); QVERIFY(rgStatus[1].isEmpty()); multiSpy.clearAllSignals(); // Comm regained on 1 should leave 2 as primary and only update status pMockLink1->setCommLost(false); QCOMPARE(multiSpy.waitForSignal (_linkStatusesChangedSignalName, VehicleLinkManager::_heartbeatMaxElpasedMSecs * 2), true); QVERIFY(multiSpy.checkOnlySignalByMask(multiSpy.signalNameToMask(_linkStatusesChangedSignalName))); QCOMPARE(pMockLink2, vehicleLinkManager->primaryLink()); rgStatus = vehicleLinkManager->linkStatuses(); QCOMPARE(rgStatus.count(), 2); QVERIFY(rgStatus[0].isEmpty()); QVERIFY(rgStatus[1].isEmpty()); multiSpy.clearAllSignals(); } void VehicleLinkManagerTest::_connectionRemovedTest(void) { SharedLinkConfigurationPtr mockConfig; SharedLinkInterfacePtr mockLink; QSignalSpy spyVehicleCreate(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged); _startMockLink(1, false /*highLatency*/, true /*incrementVehicleId*/, mockConfig, mockLink); MockLink* pMockLink = qobject_cast(mockLink.get()); QCOMPARE(spyVehicleCreate.wait(1000), true); Vehicle* vehicle = _multiVehicleMgr->activeVehicle(); QVERIFY(vehicle); QSignalSpy spyCommLostChanged(vehicle->vehicleLinkManager(), &VehicleLinkManager::communicationLostChanged); // Connection removed should just signal communication lost pMockLink->simulateConnectionRemoved(); QCOMPARE(spyCommLostChanged.wait(VehicleLinkManager::_heartbeatMaxElpasedMSecs * 2), true); QCOMPARE(spyCommLostChanged.count(), 1); QCOMPARE(spyCommLostChanged[0][0].toBool(), true); } void VehicleLinkManagerTest::_highLatencyLinkTest(void) { SharedLinkConfigurationPtr mockConfig1; SharedLinkInterfacePtr mockLink1; SharedLinkConfigurationPtr mockConfig2; SharedLinkInterfacePtr mockLink2; QSignalSpy spyVehicleCreate(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged); _startMockLink(1, true /*highLatency*/, false /*incrementVehicleId*/, mockConfig1, mockLink1); MockLink* pMockLink1 = qobject_cast(mockLink1.get()); QCOMPARE(spyVehicleCreate.wait(1000), true); QCOMPARE(_multiVehicleMgr->vehicles()->count(), 1); Vehicle* vehicle = _multiVehicleMgr->activeVehicle(); VehicleLinkManager* vehicleLinkManager = vehicle->vehicleLinkManager(); QVERIFY(vehicle); QVERIFY(vehicleLinkManager); MultiSignalSpyV2 multiSpyVLM; QVERIFY(multiSpyVLM.init(vehicleLinkManager)); // Addition of second non high latency link should: // Change primary link from 1 to 2 // Stop high latency transmission on 1 QSignalSpy spyTransmissionEnabledChanged(pMockLink1, &MockLink::highLatencyTransmissionEnabledChanged); QVERIFY(spyTransmissionEnabledChanged.isValid()); _startMockLink(2, false /*highLatency*/, false /*incrementVehicleId*/, mockConfig2, mockLink2); MockLink* pMockLink2 = qobject_cast(mockLink2.get()); QCOMPARE(multiSpyVLM.waitForSignal(_primaryLinkChangedSignalName, 100), true); QCOMPARE(pMockLink2, vehicleLinkManager->primaryLink()); QCOMPARE(spyTransmissionEnabledChanged.count(), 1); QCOMPARE(spyTransmissionEnabledChanged.takeFirst()[0].toBool(), false); multiSpyVLM.clearAllSignals(); spyTransmissionEnabledChanged.clear(); // Comm lost on primary:2 should: // Switch primary to 1 // Re-enable high latency transmission on 1 pMockLink2->setCommLost(true); QCOMPARE(multiSpyVLM.waitForSignal(_primaryLinkChangedSignalName, VehicleLinkManager::_heartbeatMaxElpasedMSecs * 2), true); QCOMPARE(pMockLink1, vehicleLinkManager->primaryLink()); QCOMPARE(spyTransmissionEnabledChanged.count(), 1); QCOMPARE(spyTransmissionEnabledChanged.takeFirst()[0].toBool(), true); spyTransmissionEnabledChanged.clear(); } void VehicleLinkManagerTest::_startMockLink(int mockIndex, bool highLatency, bool incrementVehicleId, SharedLinkConfigurationPtr& mockConfig, SharedLinkInterfacePtr& mockLink) { MockConfiguration* pMockConfig = new MockConfiguration(QStringLiteral("Mock %1").arg(mockIndex)); mockConfig = SharedLinkConfigurationPtr(pMockConfig); pMockConfig->setDynamic (true); pMockConfig->setHighLatency (highLatency); pMockConfig->setIncrementVehicleId (incrementVehicleId); SharedLinkConfigurationPtr sharedConfigmockConfig; QVERIFY(_linkManager->createConnectedLink(mockConfig)); QVERIFY(mockConfig->link()); mockLink = _linkManager->sharedLinkInterfacePointerForLink(mockConfig->link()); QVERIFY(mockLink); }