diff --git a/Tests/kstars_ui/CMakeLists.txt b/Tests/kstars_ui/CMakeLists.txt index c794abd3b..d47edb9a0 100644 --- a/Tests/kstars_ui/CMakeLists.txt +++ b/Tests/kstars_ui/CMakeLists.txt @@ -1,22 +1,24 @@ -SET(KSTARS_UI_TESTS_SRC kstars_ui_tests.cpp) +SET(KSTARS_UI_TESTS_SRC + test_ekos.cpp + kstars_ui_tests.cpp) include_directories(${CFITSIO_INCLUDE_DIR}) ECM_ADD_APP_ICON(KSTARS_UI_TESTS_SRC ICONS ../../kstars/icons/16-apps-kstars.png ../../kstars/icons/32-apps-kstars.png ../../kstars/icons/48-apps-kstars.png ../../kstars/icons/64-apps-kstars.png ../../kstars/icons/128-apps-kstars.png ) QT5_ADD_RESOURCES(KSTARS_UI_TESTS_SRC ../../kstars/data/kstars.qrc) ADD_EXECUTABLE(kstars_ui_tests ${KSTARS_UI_TESTS_SRC}) TARGET_LINK_LIBRARIES(kstars_ui_tests ${TEST_LIBRARIES} ${CFITSIO_LIBRARIES}) IF (INDI_FOUND) INCLUDE_DIRECTORIES(${INDI_INCLUDE_DIR}) TARGET_LINK_LIBRARIES(kstars_ui_tests ${INDI_CLIENT_LIBRARIES} ${NOVA_LIBRARIES} z) ENDIF () diff --git a/Tests/kstars_ui/kstars_ui_tests.cpp b/Tests/kstars_ui/kstars_ui_tests.cpp index 7ddb7045b..85720ee40 100644 --- a/Tests/kstars_ui/kstars_ui_tests.cpp +++ b/Tests/kstars_ui/kstars_ui_tests.cpp @@ -1,299 +1,114 @@ /* KStars UI tests Copyright (C) 2017 Csaba Kertesz This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ - #include "kstars_ui_tests.h" #include "auxiliary/kspaths.h" #if defined(HAVE_INDI) #include "ekos/manager.h" #include "ekos/profileeditor.h" #endif #include "kstars.h" #include #include #include #include #include #include #include #include -namespace -{ -KStars *kstarsInstance = nullptr; -QString testProfileName; -} - -void waitForKStars() -{ - while (!kstarsInstance->isGUIReady()) - { - QThread::msleep(1000); - } - while (!kstarsInstance->isActiveWindow()) - { - QThread::msleep(1000); - } - QThread::msleep(2000); -} - KStarsUiTests::KStarsUiTests() { - srand((int)time(nullptr)); + srand((unsigned int)time(nullptr)); // Create writable data dir if it does not exist QDir writableDir; writableDir.mkdir(KSPaths::writableLocation(QStandardPaths::GenericDataLocation)); KCrash::initialize(); } void KStarsUiTests::initTestCase() { KTipDialog::setShowOnStart(false); - kstarsInstance = KStars::createInstance(true); + K = KStars::createInstance(true); } void KStarsUiTests::cleanupTestCase() { - kstarsInstance->close(); - delete kstarsInstance; - kstarsInstance = nullptr; + K->close(); + delete K; + K = nullptr; } -#if defined(HAVE_INDI) -void openEkosTest() +void KStarsUiTests::init() { - waitForKStars(); - // Show Ekos Manager by clicking on the toolbar icon - QAction *action = kstarsInstance->actionCollection()->action("show_ekos"); + KStars* const K = KStars::Instance(); + QVERIFY(K != nullptr); + KTRY_SHOW_KSTARS(K); - QCOMPARE(action != nullptr, true); - action->trigger(); - QThread::msleep(1000); - QCOMPARE(Ekos::Manager::Instance() != nullptr, true); - QCOMPARE(Ekos::Manager::Instance()->isVisible(), true); - QCOMPARE(Ekos::Manager::Instance()->isActiveWindow(), true); - // Hide Ekos Manager by clicking on the toolbar icon - action->trigger(); - QThread::msleep(1000); - QCOMPARE(!Ekos::Manager::Instance()->isVisible(), true); - QCOMPARE(!Ekos::Manager::Instance()->isActiveWindow(), true); +#if defined(HAVE_INDI) + initEkos(); +#endif } -void KStarsUiTests::openEkos() +void KStarsUiTests::cleanup() { - QFuture testFuture = QtConcurrent::run(openEkosTest); + foreach (QDialog * d, KStars::Instance()->findChildren()) + if (d->isVisible()) + d->hide(); - while (!testFuture.isFinished()) - { - QCoreApplication::instance()->processEvents(); - usleep(20*1000); - } +#if defined(HAVE_INDI) + cleanupEkos(); +#endif } -void addEkosProfileTest() -{ - waitForKStars(); - // Show/verify Ekos dialog - QAction *action = kstarsInstance->actionCollection()->action("show_ekos"); - - QCOMPARE(action != nullptr, true); - action->trigger(); - QThread::msleep(500); - Ekos::Manager *ekos = Ekos::Manager::Instance(); - - QCOMPARE(ekos != nullptr, true); - QCOMPARE(ekos->isVisible(), true); - QCOMPARE(ekos->isActiveWindow(), true); - // Click on "Add profile" button - QPushButton* addButton = ekos->findChild("addProfileB"); - - QCOMPARE(addButton != nullptr, true); - QTest::mouseClick(addButton, Qt::LeftButton); - QThread::msleep(1000); - // Verify the Profile Editor dialog - ProfileEditor* profileEditor = ekos->findChild("profileEditorDialog"); - - QCOMPARE(profileEditor != nullptr, true); - // Create a test profile - QLineEdit* profileNameLE = ekos->findChild("profileIN"); - QComboBox* mountCBox = ekos->findChild("mountCombo"); - QComboBox* ccdCBox = ekos->findChild("ccdCombo"); - QDialogButtonBox* buttons = profileEditor->findChild("dialogButtons"); - - QCOMPARE(profileNameLE != nullptr, true); - QCOMPARE(mountCBox != nullptr, true); - QCOMPARE(ccdCBox != nullptr, true); - QCOMPARE(buttons != nullptr, true); - testProfileName = QString("testUI%1").arg(rand() % 100000); - profileNameLE->setText(testProfileName); - QThread::msleep(500); - QCOMPARE(profileNameLE->text(), testProfileName); - mountCBox->setCurrentText("EQMod Mount"); - QThread::msleep(500); - QCOMPARE(mountCBox->currentIndex() != 0, true); - ccdCBox->setCurrentText("Canon DSLR"); - QThread::msleep(500); - QCOMPARE(ccdCBox->currentIndex() != 0, true); - // Save the profile - emit buttons->accepted(); - QThread::msleep(2000); - // Hide Ekos Manager by clicking on the toolbar icon - action->trigger(); - QThread::msleep(1000); - QCOMPARE(!Ekos::Manager::Instance()->isVisible(), true); - QCOMPARE(!Ekos::Manager::Instance()->isActiveWindow(), true); -} +// All QTest features are macros returning with no error code. +// Therefore, in order to bail out at first failure, tests cannot use functions to run sub-tests and are required to use grouping macros too. -void KStarsUiTests::addEkosProfile() + void KStarsUiTests::basicTest() { - QFuture testFuture = QtConcurrent::run(addEkosProfileTest); - - while (!testFuture.isFinished()) - { - QCoreApplication::instance()->processEvents(); - usleep(20*1000); - } + QVERIFY(true); } -void verifyEkosProfileTest() +void KStarsUiTests::warnTest() { - waitForKStars(); - // Show/verify Ekos dialog - QAction *action = kstarsInstance->actionCollection()->action("show_ekos"); - - QCOMPARE(action != nullptr, true); - action->trigger(); - QThread::msleep(1000); - Ekos::Manager *ekos = Ekos::Manager::Instance(); - - QCOMPARE(ekos != nullptr, true); - QCOMPARE(ekos->isVisible(), true); - QCOMPARE(ekos->isActiveWindow(), true); - // Verify that the test profile exists - QComboBox* profileCBox = ekos->findChild("profileCombo"); - - QCOMPARE(profileCBox != nullptr, true); - qDebug() << "Compare:" << profileCBox->currentText() << "<->" << testProfileName; - QCOMPARE(profileCBox->currentText() == testProfileName, true); - QThread::msleep(1000); - // Click on "Edit profile" button - QPushButton* editButton = ekos->findChild("editProfileB"); - - QCOMPARE(editButton != nullptr, true); - QTest::mouseClick(editButton, Qt::LeftButton); - QThread::msleep(1000); - // Verify the Profile Editor dialog - ProfileEditor* profileEditor = ekos->findChild("profileEditorDialog"); - - QCOMPARE(profileEditor != nullptr, true); - // Verify the selected drivers - QLineEdit* profileNameLE = ekos->findChild("profileIN"); - QComboBox* mountCBox = ekos->findChild("mountCombo"); - QComboBox* ccdCBox = ekos->findChild("ccdCombo"); - - QCOMPARE(profileNameLE != nullptr, true); - QCOMPARE(mountCBox != nullptr, true); - QCOMPARE(ccdCBox != nullptr, true); - QCOMPARE(profileNameLE->text(), testProfileName); - QCOMPARE(mountCBox->currentText() == "EQMod Mount", true); - QCOMPARE(ccdCBox->currentText() == "Canon DSLR", true); - // Cancel the dialog - profileEditor->reject(); - QThread::msleep(2000); - // Hide Ekos Manager by clicking on the toolbar icon - action->trigger(); - QThread::msleep(1000); - QCOMPARE(!Ekos::Manager::Instance()->isVisible(), true); - QCOMPARE(!Ekos::Manager::Instance()->isActiveWindow(), true); + QWARN("This is a test expected to print a warning message."); } -void KStarsUiTests::verifyEkosProfile() +void KStarsUiTests::raiseKStarsTest() { - QFuture testFuture = QtConcurrent::run(verifyEkosProfileTest); - - while (!testFuture.isFinished()) - { - QCoreApplication::instance()->processEvents(); - usleep(20*1000); - } + KTRY_SHOW_KSTARS(K); } -void removeEkosProfileTest() -{ - waitForKStars(); - // Show/verify Ekos dialog - QAction *action = kstarsInstance->actionCollection()->action("show_ekos"); - - QCOMPARE(action != nullptr, true); - action->trigger(); - QThread::msleep(500); - Ekos::Manager *ekos = Ekos::Manager::Instance(); - - QCOMPARE(ekos != nullptr, true); - QCOMPARE(ekos->isVisible(), true); - QCOMPARE(ekos->isActiveWindow(), true); - // Verify that the test profile exists - QComboBox* profileCBox = ekos->findChild("profileCombo"); - - QCOMPARE(profileCBox != nullptr, true); - QCOMPARE(profileCBox->currentText() == testProfileName, true); - QThread::msleep(500); - // Click on "Remove profile" button - QPushButton* removeButton = ekos->findChild("deleteProfileB"); - - QCOMPARE(removeButton != nullptr, true); - QTest::mouseClick(removeButton, Qt::LeftButton); - QThread::msleep(1000); - // Verify the confirmation dialog - // This trick is from https://stackoverflow.com/questions/38596785 - QWidget* dialog = QApplication::activeModalWidget(); - - QCOMPARE(dialog != nullptr, true); - QDialogButtonBox* buttons = ekos->findChild(); +// We want to launch the application before running our tests +// Thus we want to explicitely call QApplication::exec(), and run our tests in parallel of the event loop +// We then reimplement QTEST_MAIN(KStarsUiTests); +// The same will have to be done when interacting with a modal dialog: exec() in main thread, tests in timer-based thread - QCOMPARE(buttons != nullptr, true); - QTest::mouseClick(buttons->button(QDialogButtonBox::Yes), Qt::LeftButton); - QThread::msleep(500); - // Verify that the test profile does not exist - bool found = false; +QT_BEGIN_NAMESPACE +QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS +QT_END_NAMESPACE - for (int i = 0; i < profileCBox->count(); ++i) - { - qDebug() << "Compare: " << profileCBox->itemText(i) << "<->" << testProfileName; - if (profileCBox->itemText(i) == testProfileName) - { - found = true; - } - } - QCOMPARE(found, false); - // Hide Ekos Manager by clicking on the toolbar icon - action->trigger(); - QThread::msleep(1000); - QCOMPARE(!Ekos::Manager::Instance()->isVisible(), true); - QCOMPARE(!Ekos::Manager::Instance()->isActiveWindow(), true); -} - -void KStarsUiTests::removeEkosProfile() +int main(int argc, char *argv[]) { - QFuture testFuture = QtConcurrent::run(removeEkosProfileTest); - - while (!testFuture.isFinished()) - { - QCoreApplication::instance()->processEvents(); - usleep(20*1000); - } + QApplication app(argc, argv); + app.setAttribute(Qt::AA_Use96Dpi, true); + QTEST_ADD_GPU_BLACKLIST_SUPPORT + QApplication::processEvents(); + KStarsUiTests * tc = new KStarsUiTests(); + QTEST_SET_MAIN_SOURCE_PATH + QTimer::singleShot(1000, &app, [=]() { return QTest::qExec(tc, argc, argv); }); + QTimer::singleShot(30000, &app, &QCoreApplication::quit); + app.exec(); } -#endif -QTEST_MAIN(KStarsUiTests); diff --git a/Tests/kstars_ui/kstars_ui_tests.h b/Tests/kstars_ui/kstars_ui_tests.h index 49117fc4f..fa37b4c3a 100644 --- a/Tests/kstars_ui/kstars_ui_tests.h +++ b/Tests/kstars_ui/kstars_ui_tests.h @@ -1,36 +1,64 @@ /* KStars UI tests Copyright (C) 2017 Csaba Kertesz This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "config-kstars.h" #include #include +#include +#include +#include + +#include "kstars.h" class KStars; +#define KTRY_SHOW_KSTARS(K) do { \ + QTRY_VERIFY_WITH_TIMEOUT((K)->isGUIReady(), 1000); \ + (K)->raise(); \ + QTRY_VERIFY_WITH_TIMEOUT((K)->isActiveWindow(), 1000); } while(false) + class KStarsUiTests : public QObject { Q_OBJECT - public: +public: KStarsUiTests(); ~KStarsUiTests() override = default; - private slots: +private: + KStars* K {nullptr}; + +private slots: void initTestCase(); void cleanupTestCase(); + + void init(); + void cleanup(); + + void basicTest(); + void warnTest(); + void raiseKStarsTest(); + #if defined(HAVE_INDI) - void openEkos(); - void addEkosProfile(); - void verifyEkosProfile(); - void removeEkosProfile(); +private: + QString testProfileName; + void initEkos(); + void cleanupEkos(); + +private slots: + // UI + void openEkosTest(); + + // Profiles + void manipulateEkosProfiles(); #endif }; diff --git a/Tests/kstars_ui/test_ekos.cpp b/Tests/kstars_ui/test_ekos.cpp new file mode 100644 index 000000000..0ca58db72 --- /dev/null +++ b/Tests/kstars_ui/test_ekos.cpp @@ -0,0 +1,275 @@ +/* KStars UI tests + Copyright (C) 218, 2020 + Csaba Kertesz + Jasem Mutlaq + Eric Dejouhanet + + This application is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + */ + +#include "config-kstars.h" + +#if defined(HAVE_INDI) + +#include "kstars_ui_tests.h" + +#include "ekos/manager.h" +#include "ekos/profileeditor.h" +#include "kstars.h" +#include "auxiliary/ksmessagebox.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define KTRY_ACTION(action_text) do { \ + QAction * const action = KStars::Instance()->actionCollection()->action(action_text); \ + QVERIFY(action != nullptr); \ + action->trigger(); } while(false) + +#define KVERIFY_EKOS_IS_HIDDEN() do { \ + if (Ekos::Manager::Instance() != nullptr) { \ + QVERIFY(!Ekos::Manager::Instance()->isVisible()); \ + QVERIFY(!Ekos::Manager::Instance()->isActiveWindow()); }} while(false) + +#define KVERIFY_EKOS_IS_OPENED() do { \ + QVERIFY(Ekos::Manager::Instance() != nullptr); \ + QVERIFY(Ekos::Manager::Instance()->isVisible()); \ + QVERIFY(Ekos::Manager::Instance()->isActiveWindow()); } while(false) + +#define KTRY_OPEN_EKOS() do { \ + if (Ekos::Manager::Instance() == nullptr || !Ekos::Manager::Instance()->isVisible()) { \ + KTRY_ACTION("show_ekos"); \ + QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance() != nullptr, 200); \ + QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->isVisible(), 200); \ + QTRY_VERIFY_WITH_TIMEOUT(Ekos::Manager::Instance()->isActiveWindow(), 200); }} while(false) + +#define KTRY_CLOSE_EKOS() do { \ + if (Ekos::Manager::Instance() != nullptr && Ekos::Manager::Instance()->isVisible()) { \ + KTRY_ACTION("show_ekos"); \ + QTRY_VERIFY_WITH_TIMEOUT(!Ekos::Manager::Instance()->isActiveWindow(), 200); \ + QTRY_VERIFY_WITH_TIMEOUT(!Ekos::Manager::Instance()->isVisible(), 200); }} while(false) + +void KStarsUiTests::initEkos() +{ + /* No-op */ +} + +void KStarsUiTests::cleanupEkos() +{ + KTRY_CLOSE_EKOS(); +} + +void KStarsUiTests::openEkosTest() +{ + KVERIFY_EKOS_IS_HIDDEN(); + KTRY_OPEN_EKOS(); + KVERIFY_EKOS_IS_OPENED(); + KTRY_CLOSE_EKOS(); + KVERIFY_EKOS_IS_HIDDEN(); +} + +void KStarsUiTests::manipulateEkosProfiles() +{ + KVERIFY_EKOS_IS_HIDDEN(); + KTRY_OPEN_EKOS(); + KVERIFY_EKOS_IS_OPENED(); + + // Because we don't want to manager the order of tests, we do the profile manipulation in three steps of the same test + // We use that poor man's shared variable to hold the result of the first (creation) and second (edition) test step. + // The ProfileEditor is exec()'d, so test code must be made asynchronous, and QTimer::singleShot is an easy way to do that. + // We use two timers, one to run the end-user test, which eventually will close the dialog, and a second one to really close if the test step fails. + bool testIsSuccessful = false; + + Ekos::Manager * const ekos = Ekos::Manager::Instance(); + + // --------- First step: creating the profile + + // Because the dialog is modal, the remainder of the test is made asynchronous + QTimer::singleShot(200, ekos, [&]() + { + // Find the Profile Editor dialog + ProfileEditor* profileEditor = ekos->findChild("profileEditorDialog"); + /* + // If the name of the widget is unknown, use this snippet to look it up from its class + if (profileEditor == nullptr) + foreach (QWidget *w, QApplication::topLevelWidgets()) + if (w->inherits("ProfileEditor")) + profileEditor = qobject_cast (w); + */ + QVERIFY(profileEditor != nullptr); + + // Create a test profile + QLineEdit * const profileNameLE = profileEditor->findChild("profileIN"); + QVERIFY(nullptr != profileNameLE); + testProfileName = QString("testUI%1").arg(rand() % 100000); // FIXME: Move this to fixtures + profileNameLE->setText(testProfileName); + QCOMPARE(profileNameLE->text(), testProfileName); + + // Setting an item programmatically in a treeview combobox... + QComboBox * const mountCBox = profileEditor->findChild("mountCombo"); + QVERIFY(nullptr != mountCBox); + QString lookup("Telescope Simulator"); // FIXME: Move this to fixtures + // Match the text recursively in the model, this results in a model index with a parent + QModelIndexList const list = mountCBox->model()->match(mountCBox->model()->index(0,0),Qt::DisplayRole,QVariant::fromValue(lookup),1,Qt::MatchRecursive); + QVERIFY(0 < list.count()); + QModelIndex const &item = list.first(); + //QWARN(QString("Found text '%1' at #%2, parent at #%3").arg(item.data().toString()).arg(item.row()).arg(item.parent().row()).toStdString().data()); + QCOMPARE(list.value(0).data().toString(), lookup); + QVERIFY(!item.parent().parent().isValid()); + // Now set the combobox model root to the match's parent + mountCBox->setRootModelIndex(item.parent()); + // And set the text as if the end-user had selected it + mountCBox->setCurrentText(lookup); + QCOMPARE(mountCBox->currentText(), lookup); + + QComboBox * const ccdCBox = profileEditor->findChild("ccdCombo"); + QVERIFY(nullptr != ccdCBox); + lookup = "CCD Simulator"; + ccdCBox->setCurrentText(lookup); // FIXME: Move this to fixtures + QCOMPARE(ccdCBox->currentText(), lookup); + + // Save the profile using the "Save" button + QDialogButtonBox* buttons = profileEditor->findChild("dialogButtons"); + QVERIFY(nullptr != buttons); + QTest::mouseClick(buttons->button(QDialogButtonBox::Save), Qt::LeftButton); + + testIsSuccessful = true; + }); + + // Cancel the Profile Editor dialog if the test failed - this will happen after pushing the add button below + QTimer * closeDialog = new QTimer(this); + closeDialog->setSingleShot(true); + closeDialog->setInterval(1000); + ekos->connect(closeDialog, &QTimer::timeout, [&]() + { + ProfileEditor* profileEditor = ekos->findChild("profileEditorDialog"); + if (profileEditor != nullptr) + profileEditor->reject(); + }); + + // Click on "Add profile" button, and let the async tests run on the modal dialog + QPushButton* addButton = ekos->findChild("addProfileB"); + QVERIFY(addButton != nullptr); + QTest::mouseClick(addButton, Qt::LeftButton); + + // Click handler returned, stop the timer closing the dialog on failure + closeDialog->stop(); + delete closeDialog; + + // Verification of the first test step + QVERIFY(testIsSuccessful); + testIsSuccessful = false; + + // --------- Second step: editing and verifying the profile + + // Verify that the test profile exists, and select it + QComboBox* profileCBox = ekos->findChild("profileCombo"); + QVERIFY(profileCBox != nullptr); + profileCBox->setCurrentText(testProfileName); + QCOMPARE(profileCBox->currentText(), testProfileName); + + // Because the dialog is modal, the remainder of the test is made asynchronous + QTimer::singleShot(200, ekos, [&]() + { + // Find the Profile Editor dialog + ProfileEditor* profileEditor = ekos->findChild("profileEditorDialog"); + QVERIFY(profileEditor != nullptr); + + // Verify the values set by addEkosProfileTest + QLineEdit* profileNameLE = ekos->findChild("profileIN"); + QVERIFY(profileNameLE != nullptr); + QCOMPARE(profileNameLE->text(), profileCBox->currentText()); + + QComboBox* mountCBox = ekos->findChild("mountCombo"); + QVERIFY(mountCBox != nullptr); + QCOMPARE(mountCBox->currentText(),"Telescope Simulator"); + + QComboBox* ccdCBox = ekos->findChild("ccdCombo"); + QVERIFY(ccdCBox != nullptr); + QCOMPARE(ccdCBox->currentText(), "CCD Simulator"); + + // Cancel the dialog using the "Close" button + QDialogButtonBox* buttons = profileEditor->findChild("dialogButtons"); + QVERIFY(nullptr != buttons); + QTest::mouseClick(buttons->button(QDialogButtonBox::Close), Qt::LeftButton); + + testIsSuccessful = true; + }); + + // Cancel the Profile Editor dialog if the test failed - this will happen after pushing the edit button below + closeDialog = new QTimer(this); + closeDialog->setSingleShot(true); + closeDialog->setInterval(1000); + ekos->connect(closeDialog, &QTimer::timeout, [&]() + { + ProfileEditor* profileEditor = ekos->findChild("profileEditorDialog"); + if (profileEditor != nullptr) + profileEditor->reject(); + }); + + // Click on "Edit profile" button, and let the async tests run on the modal dialog + QPushButton* editButton = ekos->findChild("editProfileB"); + QVERIFY(editButton != nullptr); + QTest::mouseClick(editButton, Qt::LeftButton); + + // Click handler returned, stop the timer closing the dialog on failure + closeDialog->stop(); + delete closeDialog; + + // Verification of the second test step + QVERIFY(testIsSuccessful); + testIsSuccessful = false; + + // Verify that the test profile still exists, and select it + profileCBox = ekos->findChild("profileCombo"); + QVERIFY(profileCBox != nullptr); + profileCBox->setCurrentText(testProfileName); + QCOMPARE(profileCBox->currentText(), testProfileName); + + // --------- Third step: deleting the profile + + // The yes/no modal dialog is not really used as a blocking question, but let's keep the remainder of the test is made asynchronous + QTimer::singleShot(200, ekos, [&]() + { + // This trick is from https://stackoverflow.com/questions/38596785 + QTRY_VERIFY_WITH_TIMEOUT(QApplication::activeModalWidget() != nullptr, 1000); + // This is not a regular dialog but a KStars-customized dialog, so we can't search for a yes button from QDialogButtonBox + KSMessageBox * const dialog = qobject_cast (QApplication::activeModalWidget()); + QVERIFY(dialog != nullptr); + emit dialog->accept(); + + testIsSuccessful = true; + }); + + // Click on "Remove profile" button - this will display a modal yes/no dialog + QPushButton* removeButton = ekos->findChild("deleteProfileB"); + QVERIFY(removeButton != nullptr); + QTest::mouseClick(removeButton, Qt::LeftButton); + // Pressing delete-profile triggers the open() function of the yes/no modal dialog, which returns immediately, so wait for it to disappear + QTRY_VERIFY_WITH_TIMEOUT(QApplication::activeModalWidget() == nullptr, 1000); + + // Verification of the third test step + QVERIFY(testIsSuccessful); + testIsSuccessful = false; + + // Verify that the test profile doesn't exist anymore + profileCBox = ekos->findChild("profileCombo"); + QVERIFY(profileCBox != nullptr); + profileCBox->setCurrentText(testProfileName); + QVERIFY(profileCBox->currentText() != testProfileName); +} + +#endif