From 519ad775a3d3f902067ee2bb585a32ea1fcddb89 Mon Sep 17 00:00:00 2001
From: DonLakeFlyer <don@thegagnes.com>
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 c5e4e10645..fa2c4b21ee 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 9373f9dc59..6136453f7f 100644
--- a/qgroundcontrol.qrc
+++ b/qgroundcontrol.qrc
@@ -64,6 +64,7 @@
         <file alias="MAVLinkInspectorPage.qml">src/AnalyzeView/MAVLinkInspectorPage.qml</file>
         <file alias="MavlinkSettings.qml">src/ui/preferences/MavlinkSettings.qml</file>
         <file alias="MicrohardSettings.qml">src/Microhard/MicrohardSettings.qml</file>
+        <file alias="MissionCommandTreeEditorTestWindow.qml">src/MissionManager/MissionCommandTreeEditorTestWindow.qml</file>
         <file alias="MissionSettingsEditor.qml">src/PlanView/MissionSettingsEditor.qml</file>
         <file alias="MockLink.qml">src/ui/preferences/MockLink.qml</file>
         <file alias="MockLinkSettings.qml">src/ui/preferences/MockLinkSettings.qml</file>
diff --git a/src/MissionManager/MissionCommandTreeEditorTest.cc b/src/MissionManager/MissionCommandTreeEditorTest.cc
new file mode 100644
index 0000000000..08e8522b00
--- /dev/null
+++ b/src/MissionManager/MissionCommandTreeEditorTest.cc
@@ -0,0 +1,64 @@
+/****************************************************************************
+ *
+ * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
+ *
+ * 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 0000000000..e34f075279
--- /dev/null
+++ b/src/MissionManager/MissionCommandTreeEditorTest.h
@@ -0,0 +1,27 @@
+/****************************************************************************
+ *
+ * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
+ *
+ * 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 0000000000..d8be76d438
--- /dev/null
+++ b/src/MissionManager/MissionCommandTreeEditorTestWindow.qml
@@ -0,0 +1,66 @@
+/****************************************************************************
+ *
+ * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
+ *
+ * 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 c3b8356e5a..27a7d549a6 100644
--- a/src/QGCApplication.cc
+++ b/src/QGCApplication.cc
@@ -590,18 +590,18 @@ void QGCApplication::_initCommon()
     qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
     qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
     qmlRegisterSingletonType<ShapeFileHelper>           ("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 a3d714974a..24b2f81dd5 100644
--- a/src/comm/QGCMAVLink.cc
+++ b/src/comm/QGCMAVLink.cc
@@ -9,6 +9,8 @@
 
 #include "QGCMAVLink.h"
 
+#include <QtGlobal>
+
 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 78ea810c2d..3450173fd9 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<MAV_AUTOPILOT>(firmwareClass); }
+    static QString                  firmwareClassToString   (FirmwareClass_t firmwareClass);
     static QList<FirmwareClass_t>   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<MAV_TYPE>(vehicleClass); }
+    static QString                  vehicleClassToString    (VehicleClass_t vehicleClass);
     static QList<VehicleClass_t>    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 0479092e7d..65e202b695 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<QObject*>& tests = _testList();
+    QList<UnitTest*>& tests = _testList();
 
     Q_ASSERT(!tests.contains(test));
     
@@ -74,9 +65,9 @@ void UnitTest::_unitTestCalled(void)
 }
 
 /// @brief Returns the list of unit tests.
-QList<QObject*>& UnitTest::_testList(void)
+QList<UnitTest*>& UnitTest::_testList(void)
 {
-	static QList<QObject*> tests;
+    static QList<UnitTest*> 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 5a10245b27..a270ee7402 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(#className);
+#define UT_REGISTER_TEST(className)             static UnitTestWrapper<className> className(#className, false);
+#define UT_REGISTER_TEST_STANDALONE(className)  static UnitTestWrapper<className> 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<QObject*>& _testList(void);
+    static QList<UnitTest*>& _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 T>
 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 86139c2513..3086308928 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.
-- 
GitLab