From 519ad775a3d3f902067ee2bb585a32ea1fcddb89 Mon Sep 17 00:00:00 2001 From: DonLakeFlyer Date: Sat, 18 Jul 2020 14:36:07 -0700 Subject: [PATCH] New standalone mission editor unit test --- qgroundcontrol.pro | 2 + qgroundcontrol.qrc | 1 + .../MissionCommandTreeEditorTest.cc | 64 ++++++++++++++++++ .../MissionCommandTreeEditorTest.h | 27 ++++++++ .../MissionCommandTreeEditorTestWindow.qml | 66 +++++++++++++++++++ src/QGCApplication.cc | 8 +-- src/comm/QGCMAVLink.cc | 36 ++++++++++ src/comm/QGCMAVLink.h | 4 +- src/qgcunittest/UnitTest.cc | 53 +++------------ src/qgcunittest/UnitTest.h | 36 +++++----- src/qgcunittest/UnitTestList.cc | 2 + 11 files changed, 232 insertions(+), 67 deletions(-) create mode 100644 src/MissionManager/MissionCommandTreeEditorTest.cc create mode 100644 src/MissionManager/MissionCommandTreeEditorTest.h create mode 100644 src/MissionManager/MissionCommandTreeEditorTestWindow.qml diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index c5e4e1064..fa2c4b21e 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -474,6 +474,7 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { src/MissionManager/CameraSectionTest.h \ src/MissionManager/CorridorScanComplexItemTest.h \ src/MissionManager/FWLandingPatternTest.h \ + src/MissionManager/MissionCommandTreeEditorTest.h \ src/MissionManager/MissionCommandTreeTest.h \ src/MissionManager/MissionControllerManagerTest.h \ src/MissionManager/MissionControllerTest.h \ @@ -520,6 +521,7 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { src/MissionManager/CameraSectionTest.cc \ src/MissionManager/CorridorScanComplexItemTest.cc \ src/MissionManager/FWLandingPatternTest.cc \ + src/MissionManager/MissionCommandTreeEditorTest.cc \ src/MissionManager/MissionCommandTreeTest.cc \ src/MissionManager/MissionControllerManagerTest.cc \ src/MissionManager/MissionControllerTest.cc \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 9373f9dc5..6136453f7 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -64,6 +64,7 @@ src/AnalyzeView/MAVLinkInspectorPage.qml src/ui/preferences/MavlinkSettings.qml src/Microhard/MicrohardSettings.qml + src/MissionManager/MissionCommandTreeEditorTestWindow.qml src/PlanView/MissionSettingsEditor.qml src/ui/preferences/MockLink.qml src/ui/preferences/MockLinkSettings.qml diff --git a/src/MissionManager/MissionCommandTreeEditorTest.cc b/src/MissionManager/MissionCommandTreeEditorTest.cc new file mode 100644 index 000000000..08e8522b0 --- /dev/null +++ b/src/MissionManager/MissionCommandTreeEditorTest.cc @@ -0,0 +1,64 @@ +/**************************************************************************** + * + * (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 "MissionCommandTreeEditorTest.h" +#include "QGCApplication.h" +#include "QGCCorePlugin.h" +#include "SimpleMissionItem.h" +#include "PlanMasterController.h" + +MissionCommandTreeEditorTest::MissionCommandTreeEditorTest(void) +{ + +} + +void MissionCommandTreeEditorTest::_testEditorsWorker(QGCMAVLink::FirmwareClass_t firmwareClass, QGCMAVLink::VehicleClass_t vehicleClass) +{ + QString firmwareClassString = QGCMAVLink::firmwareClassToString(firmwareClass).replace(" ", ""); + QString vehicleClassString = QGCMAVLink::vehicleClassToString(vehicleClass).replace(" ", ""); + + AppSettings* appSettings = qgcApp()->toolbox()->settingsManager()->appSettings(); + appSettings->offlineEditingFirmwareClass()->setRawValue(firmwareClass); + appSettings->offlineEditingVehicleClass()->setRawValue(vehicleClass); + PlanMasterController* masterController = new PlanMasterController(); + + FirmwarePlugin* firmwarePlugin = qgcApp()->toolbox()->firmwarePluginManager()->firmwarePluginForAutopilot(QGCMAVLink::firmwareClassToAutopilot(firmwareClass), QGCMAVLink::vehicleClassToMavType(vehicleClass)); + if (firmwarePlugin->supportedMissionCommands().count() == 0) { + firmwarePlugin = qgcApp()->toolbox()->firmwarePluginManager()->firmwarePluginForAutopilot(QGCMAVLink::firmwareClassToAutopilot(QGCMAVLink::FirmwareClassPX4), QGCMAVLink::vehicleClassToMavType(vehicleClass)); + } + int cColumns = firmwarePlugin->supportedMissionCommands().count(); + + QVariantList varSimpleItems; + for (MAV_CMD command: firmwarePlugin->supportedMissionCommands()) { + SimpleMissionItem* simpleItem = new SimpleMissionItem(masterController, false /* flyView */, false /* forLoad */, this); + simpleItem->setCommand(command); + varSimpleItems.append(QVariant::fromValue(simpleItem)); + } + + QQmlApplicationEngine* qmlAppEngine = qgcApp()->toolbox()->corePlugin()->createQmlApplicationEngine(this); + qmlAppEngine->rootContext()->setContextProperty("planMasterController", masterController); + qmlAppEngine->rootContext()->setContextProperty("missionItems", varSimpleItems); + qmlAppEngine->rootContext()->setContextProperty("cColumns", cColumns); + qmlAppEngine->rootContext()->setContextProperty("imagePath", QStringLiteral("/home/parallels/Downloads/%1-%2.png").arg(firmwareClassString).arg(vehicleClassString)); + qmlAppEngine->load(QUrl(QStringLiteral("qrc:/qml/MissionCommandTreeEditorTestWindow.qml"))); + + QTest::qWait(1000); + + delete qmlAppEngine; +} + +void MissionCommandTreeEditorTest::testEditors(void) +{ + for (const QGCMAVLink::FirmwareClass_t& firmwareClass: QGCMAVLink::allFirmwareClasses()) { + for (const QGCMAVLink::VehicleClass_t& vehicleClass: QGCMAVLink::allVehicleClasses()) { + _testEditorsWorker(firmwareClass, vehicleClass); + } + } +} + diff --git a/src/MissionManager/MissionCommandTreeEditorTest.h b/src/MissionManager/MissionCommandTreeEditorTest.h new file mode 100644 index 000000000..e34f07527 --- /dev/null +++ b/src/MissionManager/MissionCommandTreeEditorTest.h @@ -0,0 +1,27 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +#pragma once + +#include "UnitTest.h" + +/// This unit test is meant to be used stand-alone to generate images for each mission item editor for review +class MissionCommandTreeEditorTest : public UnitTest +{ + Q_OBJECT + +public: + MissionCommandTreeEditorTest(void); + +private slots: + void testEditors(void); + +private: + void _testEditorsWorker(QGCMAVLink::FirmwareClass_t firmwareClass, QGCMAVLink::VehicleClass_t vehicleClass); +}; diff --git a/src/MissionManager/MissionCommandTreeEditorTestWindow.qml b/src/MissionManager/MissionCommandTreeEditorTestWindow.qml new file mode 100644 index 000000000..d8be76d43 --- /dev/null +++ b/src/MissionManager/MissionCommandTreeEditorTestWindow.qml @@ -0,0 +1,66 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.4 +import QtQuick.Dialogs 1.3 +import QtQuick.Layouts 1.11 +import QtQuick.Window 2.11 + +import QGroundControl 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.FlightDisplay 1.0 +import QGroundControl.FlightMap 1.0 +import QGroundControl.Controllers 1.0 + +ApplicationWindow { + id: _root + visible: true + visibility: Qt.WindowFullScreen + color: qgcPal.window + + property real editorWidth: ScreenTools.defaultFontPixelWidth * 30 + + QGCPalette { id: qgcPal; colorGroupEnabled: true } + + Timer { + id: timer + interval: 100 + onTriggered: { + var success = fullImage.grabToImage(function(result) { result.saveToFile(imagePath) }) + console.log(success) + } + } + + Component.onCompleted: timer.start() + + Flow { + id: fullImage + anchors.fill: parent + + Repeater { + model: missionItems + + Column { + QGCLabel { text: modelData.commandName; color: "black" } + + Loader { + id: editorLoader + source: modelData.editorQml + + property var missionItem: modelData + property var masterController: planMasterController + property real availableWidth: editorWidth + } + } + } + } +} diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index c3b8356e5..27a7d549a 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -590,18 +590,18 @@ void QGCApplication::_initCommon() qmlRegisterSingletonType ("QGroundControl", 1, 0, "QGroundControl", qgroundcontrolQmlGlobalSingletonFactory); qmlRegisterSingletonType ("QGroundControl.ScreenToolsController", 1, 0, "ScreenToolsController", screenToolsControllerSingletonFactory); qmlRegisterSingletonType ("QGroundControl.ShapeFileHelper", 1, 0, "ShapeFileHelper", shapeFileHelperSingletonFactory); -} - -bool QGCApplication::_initForNormalAppBoot() -{ + // Although this should really be in _initForNormalAppBoot putting it here allowws us to create unit tests which pop up more easily if(QFontDatabase::addApplicationFont(":/fonts/opensans") < 0) { qWarning() << "Could not load /fonts/opensans font"; } if(QFontDatabase::addApplicationFont(":/fonts/opensans-demibold") < 0) { qWarning() << "Could not load /fonts/opensans-demibold font"; } +} +bool QGCApplication::_initForNormalAppBoot() +{ QSettings settings; _qmlAppEngine = toolbox()->corePlugin()->createQmlApplicationEngine(this); diff --git a/src/comm/QGCMAVLink.cc b/src/comm/QGCMAVLink.cc index a3d714974..24b2f81dd 100644 --- a/src/comm/QGCMAVLink.cc +++ b/src/comm/QGCMAVLink.cc @@ -9,6 +9,8 @@ #include "QGCMAVLink.h" +#include + constexpr QGCMAVLink::FirmwareClass_t QGCMAVLink::FirmwareClassPX4; constexpr QGCMAVLink::FirmwareClass_t QGCMAVLink::FirmwareClassArduPilot; constexpr QGCMAVLink::FirmwareClass_t QGCMAVLink::FirmwareClassGeneric; @@ -56,6 +58,20 @@ QGCMAVLink::FirmwareClass_t QGCMAVLink::firmwareClass(MAV_AUTOPILOT autopilot) } } +QString QGCMAVLink::firmwareClassToString(FirmwareClass_t firmwareClass) +{ + switch (firmwareClass) { + case FirmwareClassPX4: + return QT_TRANSLATE_NOOP("Firmware Class", "PX4 Pro"); + case FirmwareClassArduPilot: + return QT_TRANSLATE_NOOP("Firmware Class", "ArduPilot"); + case FirmwareClassGeneric: + return QT_TRANSLATE_NOOP("Firmware Class", "Generic"); + default: + return QT_TRANSLATE_NOOP("Firmware Class", "Unknown"); + } +} + bool QGCMAVLink::isFixedWing(MAV_TYPE mavType) { return vehicleClass(mavType) == VehicleClassFixedWing; @@ -109,6 +125,26 @@ QGCMAVLink::VehicleClass_t QGCMAVLink::vehicleClass(MAV_TYPE mavType) } } +QString QGCMAVLink::vehicleClassToString(VehicleClass_t vehicleClass) +{ + switch (vehicleClass) { + case VehicleClassFixedWing: + return QT_TRANSLATE_NOOP("Vehicle Class", "Fixed Wing"); + case VehicleClassRoverBoat: + return QT_TRANSLATE_NOOP("Vehicle Class", "Rover-Boat"); + case VehicleClassSub: + return QT_TRANSLATE_NOOP("Vehicle Class", "Sub"); + case VehicleClassMultiRotor: + return QT_TRANSLATE_NOOP("Vehicle Class", "Multi-Rotor"); + case VehicleClassVTOL: + return QT_TRANSLATE_NOOP("Vehicle Class", "VTOL"); + case VehicleClassGeneric: + return QT_TRANSLATE_NOOP("Vehicle Class", "Generic"); + default: + return QT_TRANSLATE_NOOP("Vehicle Class", "Unknown"); + } +} + QString QGCMAVLink::mavResultToString(MAV_RESULT result) { switch (result) { diff --git a/src/comm/QGCMAVLink.h b/src/comm/QGCMAVLink.h index 78ea810c2..3450173fd 100644 --- a/src/comm/QGCMAVLink.h +++ b/src/comm/QGCMAVLink.h @@ -68,6 +68,7 @@ public: static bool isGenericFirmwareClass (MAV_AUTOPILOT autopilot) { return !isPX4FirmwareClass(autopilot) && ! isArduPilotFirmwareClass(autopilot); } static FirmwareClass_t firmwareClass (MAV_AUTOPILOT autopilot); static MAV_AUTOPILOT firmwareClassToAutopilot(FirmwareClass_t firmwareClass) { return static_cast(firmwareClass); } + static QString firmwareClassToString (FirmwareClass_t firmwareClass); static QList allFirmwareClasses (void); static bool isFixedWing (MAV_TYPE mavType); @@ -77,9 +78,10 @@ public: static bool isVTOL (MAV_TYPE mavType); static VehicleClass_t vehicleClass (MAV_TYPE mavType); static MAV_TYPE vehicleClassToMavType (VehicleClass_t vehicleClass) { return static_cast(vehicleClass); } + static QString vehicleClassToString (VehicleClass_t vehicleClass); static QList allVehicleClasses (void); - static QString mavResultToString (MAV_RESULT result); + static QString mavResultToString (MAV_RESULT result); }; class MavlinkFTP { diff --git a/src/qgcunittest/UnitTest.cc b/src/qgcunittest/UnitTest.cc index 0479092e7..65e202b69 100644 --- a/src/qgcunittest/UnitTest.cc +++ b/src/qgcunittest/UnitTest.cc @@ -37,15 +37,6 @@ enum UnitTest::FileDialogType UnitTest::_fileDialogExpectedType = getOpenFileNam int UnitTest::_missedFileDialogCount = 0; UnitTest::UnitTest(void) - : _linkManager (nullptr) - , _mockLink (nullptr) - , _mainWindow (nullptr) - , _vehicle (nullptr) - , _expectMissedFileDialog (false) - , _expectMissedMessageBox (false) - , _unitTestRun (false) - , _initCalled (false) - , _cleanupCalled (false) { } @@ -59,9 +50,9 @@ UnitTest::~UnitTest() } } -void UnitTest::_addTest(QObject* test) +void UnitTest::_addTest(UnitTest* test) { - QList& tests = _testList(); + QList& tests = _testList(); Q_ASSERT(!tests.contains(test)); @@ -74,9 +65,9 @@ void UnitTest::_unitTestCalled(void) } /// @brief Returns the list of unit tests. -QList& UnitTest::_testList(void) +QList& UnitTest::_testList(void) { - static QList tests; + static QList tests; return tests; } @@ -84,8 +75,11 @@ int UnitTest::run(QString& singleTest) { int ret = 0; - for (QObject* test: _testList()) { + for (UnitTest* test: _testList()) { if (singleTest.isEmpty() || singleTest == test->objectName()) { + if (test->standalone() && singleTest.isEmpty()) { + continue; + } QStringList args; args << "*" << "-maxwarnings" << "0"; ret += QTest::qExec(test, args); @@ -136,7 +130,6 @@ void UnitTest::cleanup(void) _cleanupCalled = true; _disconnectMockLink(); - _closeMainWindow(); // Keep in mind that any code below these QCOMPARE may be skipped if the compare fails if (_expectMissedMessageBox) { @@ -433,36 +426,6 @@ void UnitTest::_linkDeleted(LinkInterface* link) } } -void UnitTest::_createMainWindow(void) -{ - //-- TODO -#if 0 - _mainWindow = MainWindow::_create(); - Q_CHECK_PTR(_mainWindow); -#endif -} - -void UnitTest::_closeMainWindow(bool cancelExpected) -{ - //-- TODO -#if 0 - if (_mainWindow) { - QSignalSpy mainWindowSpy(_mainWindow, SIGNAL(mainWindowClosed())); - - _mainWindow->close(); - - mainWindowSpy.wait(2000); - QCOMPARE(mainWindowSpy.count(), cancelExpected ? 0 : 1); - - // This leaves enough time for any dangling Qml components to get cleaned up. - // This prevents qWarning from bad references in Qml - QTest::qWait(1000); - } -#else - Q_UNUSED(cancelExpected); -#endif -} - QString UnitTest::createRandomFile(uint32_t byteCount) { QTemporaryFile tempFile; diff --git a/src/qgcunittest/UnitTest.h b/src/qgcunittest/UnitTest.h index 5a10245b2..a270ee740 100644 --- a/src/qgcunittest/UnitTest.h +++ b/src/qgcunittest/UnitTest.h @@ -26,13 +26,13 @@ #include "Fact.h" #include "MissionItem.h" -#define UT_REGISTER_TEST(className) static UnitTestWrapper className(#className); +#define UT_REGISTER_TEST(className) static UnitTestWrapper className(#className, false); +#define UT_REGISTER_TEST_STANDALONE(className) static UnitTestWrapper className(#className, true); class QGCMessageBox; class QGCQFileDialog; class LinkManager; class MockLink; -class MainWindow; class Vehicle; class UnitTest : public QObject @@ -82,8 +82,11 @@ public: // @param Expected failure response flags void checkExpectedFileDialog(int expectFailFlags = expectFailNoFailure); + bool standalone(void) { return _standalone; } + void setStandalone(bool standalone) { _standalone = standalone; } + /// @brief Adds a unit test to the list. Should only be called by UnitTestWrapper. - static void _addTest(QObject* test); + static void _addTest(UnitTest* test); /// Creates a file with random contents of the specified size. /// @return Fully qualified path to created file @@ -122,17 +125,14 @@ protected: void _connectMockLink(MAV_AUTOPILOT autopilot = MAV_AUTOPILOT_PX4); void _connectMockLinkNoInitialConnectSequence(void) { _connectMockLink(MAV_AUTOPILOT_INVALID); } void _disconnectMockLink(void); - void _createMainWindow(void); - void _closeMainWindow(bool cancelExpected = false); void _missionItemsEqual(MissionItem& actual, MissionItem& expected); - LinkManager* _linkManager; - MockLink* _mockLink; - MainWindow* _mainWindow; - Vehicle* _vehicle; + LinkManager* _linkManager = nullptr; + MockLink* _mockLink = nullptr; + Vehicle* _vehicle = nullptr; - bool _expectMissedFileDialog; // true: expect a missed file dialog, used for internal testing - bool _expectMissedMessageBox; // true: expect a missed message box, used for internal testing + bool _expectMissedFileDialog = false; // true: expect a missed file dialog, used for internal testing + bool _expectMissedMessageBox = false; // true: expect a missed message box, used for internal testing private slots: void _linkDeleted(LinkInterface* link); @@ -185,7 +185,7 @@ private: friend class QGCQFileDialog; void _unitTestCalled(void); - static QList& _testList(void); + static QList& _testList(void); // Catch QGCMessageBox calls static bool _messageBoxRespondedTo; ///< Message box was responded to @@ -200,18 +200,20 @@ private: static enum FileDialogType _fileDialogExpectedType; ///< type of file dialog expected to show static int _missedFileDialogCount; ///< Count of file dialogs not checked with call to UnitTest::fileDialogWasDisplayed - bool _unitTestRun; ///< true: Unit Test was run - bool _initCalled; ///< true: UnitTest::_init was called - bool _cleanupCalled; ///< true: UnitTest::_cleanup was called + bool _unitTestRun = false; ///< true: Unit Test was run + bool _initCalled = false; ///< true: UnitTest::_init was called + bool _cleanupCalled = false; ///< true: UnitTest::_cleanup was called + bool _standalone = false; ///< true: Only run when requested specifically from command line }; template class UnitTestWrapper { public: - UnitTestWrapper(const QString& name) : - _unitTest(new T) + UnitTestWrapper(const QString& name, bool standalone) + : _unitTest(new T) { _unitTest->setObjectName(name); + _unitTest->setStandalone(standalone); UnitTest::_addTest(_unitTest.data()); } diff --git a/src/qgcunittest/UnitTestList.cc b/src/qgcunittest/UnitTestList.cc index 86139c251..308630892 100644 --- a/src/qgcunittest/UnitTestList.cc +++ b/src/qgcunittest/UnitTestList.cc @@ -48,6 +48,7 @@ #include "RequestMessageTest.h" #include "InitialConnectTest.h" #include "FTPManagerTest.h" +#include "MissionCommandTreeEditorTest.h" UT_REGISTER_TEST(FactSystemTestGeneric) UT_REGISTER_TEST(FactSystemTestPX4) @@ -83,6 +84,7 @@ UT_REGISTER_TEST(TransectStyleComplexItemTest) UT_REGISTER_TEST(QGCMapPolylineTest) UT_REGISTER_TEST(CameraCalcTest) UT_REGISTER_TEST(FWLandingPatternTest) +UT_REGISTER_TEST_STANDALONE(MissionCommandTreeEditorTest) // List of unit test which are currently disabled. // If disabling a new test, include reason in comment. -- 2.22.0