From 4ce9fcf5d8cd20729a6a9b13ffdc4511dd63b1a7 Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Tue, 20 Oct 2020 17:18:10 +0200 Subject: [PATCH 01/16] Bugfix cleaning up the interference of flats creation with the modal dialog for primary scope check --- kstars/ekos/capture/capture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kstars/ekos/capture/capture.cpp b/kstars/ekos/capture/capture.cpp index 68b56fb6f..7771b9ac4 100644 --- a/kstars/ekos/capture/capture.cpp +++ b/kstars/ekos/capture/capture.cpp @@ -665,7 +665,7 @@ void Capture::start() }); KSMessageBox::Instance()->questionYesNo(i18n("Are you imaging with %1 using your primary telescope?", - currentCCD->getDeviceName()), + currentCCD->getDeviceName()), i18n("Telescope Type"), 10, true); } else -- GitLab From ff11934300b3dda93d087068c85fcf3175f2d8bc Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Thu, 22 Oct 2020 21:58:47 +0200 Subject: [PATCH 02/16] Initial version of capture counting test cases --- Tests/kstars_ui/CMakeLists.txt | 8 +- Tests/kstars_ui/test_ekos_capture_count.cpp | 148 ++++++++ Tests/kstars_ui/test_ekos_capture_count.h | 82 +++++ Tests/kstars_ui/test_ekos_capture_helper.cpp | 230 ++++++++++++ Tests/kstars_ui/test_ekos_capture_helper.h | 343 ++++++++++++++++++ Tests/kstars_ui/test_ekos_meridianflip_base.h | 237 +----------- 6 files changed, 810 insertions(+), 238 deletions(-) create mode 100644 Tests/kstars_ui/test_ekos_capture_count.cpp create mode 100644 Tests/kstars_ui/test_ekos_capture_count.h create mode 100644 Tests/kstars_ui/test_ekos_capture_helper.cpp create mode 100644 Tests/kstars_ui/test_ekos_capture_helper.h diff --git a/Tests/kstars_ui/CMakeLists.txt b/Tests/kstars_ui/CMakeLists.txt index 9f0a7b762..5cfabb5bf 100644 --- a/Tests/kstars_ui/CMakeLists.txt +++ b/Tests/kstars_ui/CMakeLists.txt @@ -42,7 +42,11 @@ ADD_EXECUTABLE(test_ekos_capture ${KSTARS_UI_EKOS_SRC} test_ekos_capture.cpp) TARGET_LINK_LIBRARIES(test_ekos_capture ${KSTARS_UI_EKOS_LIBS}) ADD_TEST(NAME TestEkosCapture COMMAND test_ekos_capture) -ADD_EXECUTABLE(test_ekos_meridianflip ${KSTARS_UI_EKOS_SRC} test_ekos_meridianflip_base.cpp test_ekos_meridianflip.cpp) +ADD_EXECUTABLE(test_ekos_capture_count ${KSTARS_UI_EKOS_SRC} test_ekos_capture_helper.cpp test_ekos_capture_count.cpp) +TARGET_LINK_LIBRARIES(test_ekos_capture_count ${KSTARS_UI_EKOS_LIBS}) +ADD_TEST(NAME TestEkosCaptureCount COMMAND test_ekos_capture_count) + +ADD_EXECUTABLE(test_ekos_meridianflip ${KSTARS_UI_EKOS_SRC} test_ekos_capture_helper.cpp test_ekos_meridianflip_base.cpp test_ekos_meridianflip.cpp) TARGET_LINK_LIBRARIES(test_ekos_meridianflip ${KSTARS_UI_EKOS_LIBS}) # excluded to avoid CI timelimit failure # ADD_TEST(NAME TestEkosMeridianFlip COMMAND test_ekos_meridianflip) @@ -51,7 +55,7 @@ ADD_CUSTOM_COMMAND( TARGET test_ekos_meridianflip POST_BUILD ${CMAKE_CURRENT_SOURCE_DIR}/phd2_Simulators_mf.PHDGuidingV2 ${CMAKE_CURRENT_BINARY_DIR}/.PHDGuidingV2_mf) -ADD_EXECUTABLE(test_ekos_meridianflip_specials ${KSTARS_UI_EKOS_SRC} test_ekos_meridianflip_base.cpp test_ekos_meridianflip_specials.cpp) +ADD_EXECUTABLE(test_ekos_meridianflip_specials ${KSTARS_UI_EKOS_SRC} test_ekos_capture_helper.cpp test_ekos_meridianflip_base.cpp test_ekos_meridianflip_specials.cpp) TARGET_LINK_LIBRARIES(test_ekos_meridianflip_specials ${KSTARS_UI_EKOS_LIBS}) # excluded to avoid CI timelimit failure # ADD_TEST(NAME TestEkosMeridianFlipSpecials COMMAND test_ekos_meridianflip_specials) diff --git a/Tests/kstars_ui/test_ekos_capture_count.cpp b/Tests/kstars_ui/test_ekos_capture_count.cpp new file mode 100644 index 000000000..00e4002b8 --- /dev/null +++ b/Tests/kstars_ui/test_ekos_capture_count.cpp @@ -0,0 +1,148 @@ +/* + KStars UI tests for verifying correct counting of the capture module + + Copyright (C) 2020 + Wolfgang Reissenberger + + 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 "test_ekos_capture_count.h" + +#include "kstars_ui_tests.h" +#include "test_ekos.h" + +#include "test_ekos_capture_helper.h" + +TestEkosCaptureCount::TestEkosCaptureCount(QObject *parent) : QObject(parent) { + m_CaptureHelper = new TestEkosCaptureHelper(); +} + + +void TestEkosCaptureCount::testCaptureWithCaptureFramesMap() +{ + + // setup capture sequence, fill captured frames map and set expectations + prepareCapture(2.0, {"Red", "Green", "Blue"}, {2, 2, 2}, + {qMakePair(QString("Red"), 2), qMakePair(QString("Green"), 1), qMakePair(QString("Blue"), 2)}, + {qMakePair(calculateSignature(QString("Green")), 1)}); + + // capture + QVERIFY(m_CaptureHelper->startCapturing()); + + // verify whether all frames are captured as expected + bool success = true; + for (QMap::iterator it = expectedImages.begin(); it != expectedImages.end(); ++it) + if (it.value() != 0) + { + QWARN(QString("Capture count for signature %1 does not match: %2 frames too %3 captured.").arg(it.key()).arg(abs(it.value())).arg(it.value() < 0 ? "much" : "few").toStdString().c_str()); + success = false; + } + QVERIFY2(success, "Capturing did not produce the expected amount of frames."); +} + +void TestEkosCaptureCount::initTestCase() +{ + KVERIFY_EKOS_IS_HIDDEN(); + KTRY_OPEN_EKOS(); + KVERIFY_EKOS_IS_OPENED(); + // start the profile + QVERIFY(startEkosProfile()); + m_CaptureHelper->initTestCase(); + destination = new QTemporaryDir(); +} + +void TestEkosCaptureCount::cleanupTestCase() +{ + m_CaptureHelper->cleanupTestCase(); + QVERIFY(shutdownEkosProfile()); + KTRY_CLOSE_EKOS(); + KVERIFY_EKOS_IS_HIDDEN(); +} + +void TestEkosCaptureCount::init() +{ + connect(Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::newImage, this, &TestEkosCaptureCount::newImageReceived); +} + +void TestEkosCaptureCount::cleanup() +{ + QVERIFY(m_CaptureHelper->stopCapturing()); + + // clean up capture module + Ekos::Manager::Instance()->captureModule()->clearSequenceQueue(); + KTRY_GADGET(Ekos::Manager::Instance()->captureModule(), QTableWidget, queueTable); + QTRY_VERIFY_WITH_TIMEOUT(queueTable->rowCount() == 0, 2000); + disconnect(Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::newImage, this, &TestEkosCaptureCount::newImageReceived); +} + + +bool TestEkosCaptureCount::startEkosProfile() +{ + Ekos::Manager * const ekos = Ekos::Manager::Instance(); + + KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(ekos->setupTab, 1000)); + + KWRAP_SUB(QVERIFY(m_CaptureHelper->setupEkosProfile("Simulators", false))); + // start the profile + KTRY_EKOS_CLICK(processINDIB); + // wait for the devices to come up + QTest::qWait(15000); + + // Everything completed successfully + return true; +} + +bool TestEkosCaptureCount::shutdownEkosProfile() +{ + qCInfo(KSTARS_EKOS_TEST) << "Stopping profile ..."; + KWRAP_SUB(KTRY_EKOS_STOP_SIMULATORS()); + qCInfo(KSTARS_EKOS_TEST) << "Stopping profile ... (done)"; + // Everything completed successfully + return true; +} + +bool TestEkosCaptureCount::prepareCapture(double exptime, QList filters, QList countsList, + QList> capturedFramesMap, QList> expected) +{ + // switch to capture module + Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule(); + KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000)); + + // create the capture sequences + KWRAP_SUB(QVERIFY(destination->isValid())); + KWRAP_SUB(QVERIFY(destination->autoRemove())); + qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << destination->path(); + + // check that for each filter we need a frame count + KVERIFY_SUB(filters.size() == countsList.size()); + + // create capture sequences + for (int i = 0; i < filters.size(); i++) + KWRAP_SUB(KTRY_CAPTURE_ADD_LIGHT(exptime, countsList[i], 0, filters[i], destination->path())); + // fill the captured frames map that hold the numbers of already taken frames + for (int i = 0; i < capturedFramesMap.size(); i++) + capture->setCapturedFramesMap(calculateSignature(capturedFramesMap[i].first), capturedFramesMap[i].second); + // fill the map of expected frames + for (QList>::iterator it = expected.begin(); it != expected.end(); ++it) + expectedImages.insert(it->first, it->second); + + return true; +} + +void TestEkosCaptureCount::newImageReceived(Ekos::SequenceJob *job) +{ + // reduce the for the job's signature the number of expected images + expectedImages.insert(job->getSignature(), expectedImages.value(job->getSignature(), 0) - 1); +} + +QString TestEkosCaptureCount::calculateSignature(QString filter) +{ + return destination->path() + "/Light/" + filter + "/Light"; +} + + +QTEST_KSTARS_MAIN(TestEkosCaptureCount) diff --git a/Tests/kstars_ui/test_ekos_capture_count.h b/Tests/kstars_ui/test_ekos_capture_count.h new file mode 100644 index 000000000..a50498271 --- /dev/null +++ b/Tests/kstars_ui/test_ekos_capture_count.h @@ -0,0 +1,82 @@ +/* + KStars UI tests for verifying correct counting of the capture module + + Copyright (C) 2020 + Wolfgang Reissenberger + + 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 +#include + +#include "config-kstars.h" +#include "test_ekos_debug.h" + +#if defined(HAVE_INDI) +#include "ekos/ekos.h" +#include "ekos/capture/sequencejob.h" +#include "test_ekos_capture_helper.h" + +class TestEkosCaptureCount : public QObject +{ + Q_OBJECT +public: + explicit TestEkosCaptureCount(QObject *parent = nullptr); + +protected: + + // destination where images will be located + QTemporaryDir *destination; + + /** + * @brief Start a test EKOS profile. + */ + bool startEkosProfile(); + + /** + * @brief Shutdown the current test EKOS profile. + */ + bool shutdownEkosProfile(); + + bool prepareCapture(double exptime, QList filters, QList countsList, QList> capturedFramesMap, QList> expected); + + // mapping between image signature and number of images expected to be captured for this signature + QMap expectedImages; + /** + * @brief Register that a new image has been captured + * @param job capture sequence job holding the image meta data + */ + void newImageReceived(Ekos::SequenceJob *job); + + +protected slots: + void initTestCase(); + void cleanupTestCase(); + + void init(); + void cleanup(); + + QString calculateSignature(QString filter); + +private: + // current capture status + Ekos::CaptureState m_CaptureStatus; + + // helper class + TestEkosCaptureHelper *m_CaptureHelper = nullptr; + +private slots: + /** + * @brief Test whether the capture module produces exactly the diff between the capture frames map and the defined frame counts. + */ + void testCaptureWithCaptureFramesMap(); + +}; + +#endif // HAVE_INDI diff --git a/Tests/kstars_ui/test_ekos_capture_helper.cpp b/Tests/kstars_ui/test_ekos_capture_helper.cpp new file mode 100644 index 000000000..9035018a4 --- /dev/null +++ b/Tests/kstars_ui/test_ekos_capture_helper.cpp @@ -0,0 +1,230 @@ +/* + Helper class of KStars UI capture tests + + Copyright (C) 2020 + Wolfgang Reissenberger + + 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 "test_ekos_capture_helper.h" + +#include "test_ekos.h" + +TestEkosCaptureHelper::TestEkosCaptureHelper() {} + +void TestEkosCaptureHelper::initTestCase() +{ + // connect to the capture process to receive capture status changes + connect(Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::newStatus, this, &TestEkosCaptureHelper::captureStatusChanged, + Qt::UniqueConnection); +} + +void TestEkosCaptureHelper::cleanupTestCase() +{ + // disconnect to the capture process to receive capture status changes + disconnect(Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::newStatus, this, &TestEkosCaptureHelper::captureStatusChanged); +} + +void TestEkosCaptureHelper::fillProfile(bool *isDone) +{ + qCInfo(KSTARS_EKOS_TEST) << "Fill profile: starting..."; + ProfileEditor* profileEditor = Ekos::Manager::Instance()->findChild("profileEditorDialog"); + + // Select the mount device + KTRY_PROFILEEDITOR_GADGET(QComboBox, mountCombo); + setTreeviewCombo(mountCombo, m_MountDevice); + qCInfo(KSTARS_EKOS_TEST) << "Fill profile: Mount selected."; + + // Selet the CCD device + KTRY_PROFILEEDITOR_GADGET(QComboBox, ccdCombo); + setTreeviewCombo(ccdCombo, m_CCDDevice); + qCInfo(KSTARS_EKOS_TEST) << "Fill profile: CCD selected."; + + // Select the focuser device + KTRY_PROFILEEDITOR_GADGET(QComboBox, focuserCombo); + setTreeviewCombo(focuserCombo, m_FocuserDevice); + qCInfo(KSTARS_EKOS_TEST) << "Fill profile: Focuser selected."; + + // Select the guider device + KTRY_PROFILEEDITOR_GADGET(QComboBox, guiderCombo); + setTreeviewCombo(guiderCombo, m_GuiderDevice); + qCInfo(KSTARS_EKOS_TEST) << "Fill profile: Guider selected."; + + // wait a short time to make the setup visible + QTest::qWait(1000); + // Save the profile using the "Save" button + QDialogButtonBox* buttons = profileEditor->findChild("dialogButtons"); + QVERIFY(nullptr != buttons); + QTest::mouseClick(buttons->button(QDialogButtonBox::Save), Qt::LeftButton); + + qCInfo(KSTARS_EKOS_TEST) << "Fill profile: Selections saved."; + + *isDone = true; +} + +void TestEkosCaptureHelper::createEkosProfile(QString name, bool isPHD2, bool *isDone) +{ + ProfileEditor* profileEditor = Ekos::Manager::Instance()->findChild("profileEditorDialog"); + + // Set the profile name + KTRY_SET_LINEEDIT(profileEditor, profileIN, name); + // select the guider type + KTRY_SET_COMBO(profileEditor, guideTypeCombo, isPHD2 ? "PHD2" : "Internal"); + if (isPHD2) + { + // Write PHD2 server specs + KTRY_SET_LINEEDIT(profileEditor, externalGuideHost, "localhost"); + KTRY_SET_LINEEDIT(profileEditor, externalGuidePort, "4400"); + } + + qCInfo(KSTARS_EKOS_TEST) << "Ekos profile " << name << " created."; + // and now continue with filling the profile + fillProfile(isDone); +} + +bool TestEkosCaptureHelper::setupEkosProfile(QString name, bool isPHD2) +{ + qCInfo(KSTARS_EKOS_TEST) << "Setting up Ekos profile..."; + bool isDone = false; + Ekos::Manager * const ekos = Ekos::Manager::Instance(); + // check if the profile with the given name exists + KTRY_GADGET_SUB(ekos, QComboBox, profileCombo); + if (profileCombo->findText(name) >= 0) + { + KTRY_GADGET_SUB(ekos, QPushButton, editProfileB); + + // edit Simulators profile + KWRAP_SUB(KTRY_EKOS_SELECT_PROFILE(name)); + + // edit only editable profiles + if (editProfileB->isEnabled()) + { + // start with a delay of 1 sec a new thread that edits the profile + QTimer::singleShot(1000, ekos, [&]{fillProfile(&isDone);}); + KTRY_CLICK_SUB(ekos, editProfileB); + } + else + { + qCInfo(KSTARS_EKOS_TEST) << "Profile " << name << " not editable, setup finished."; + isDone = true; + return true; + } + } + else + { + // start with a delay of 1 sec a new thread that edits the profile + qCInfo(KSTARS_EKOS_TEST) << "Creating new profile " << name << " ..."; + QTimer::singleShot(1000, ekos, [&]{createEkosProfile(name, isPHD2, &isDone);}); + // create new profile addProfileB + KTRY_CLICK_SUB(ekos, addProfileB); + } + + + // Cancel the profile editor if test did not close the editor dialog within 10 sec + QTimer * closeDialog = new QTimer(this); + closeDialog->setSingleShot(true); + closeDialog->setInterval(10000); + ekos->connect(closeDialog, &QTimer::timeout, [&] + { + ProfileEditor* profileEditor = ekos->findChild("profileEditorDialog"); + if (profileEditor != nullptr) + { + profileEditor->reject(); + qWarning(KSTARS_EKOS_TEST) << "Editing profile aborted."; + } + }); + + + // Click handler returned, stop the timer closing the dialog on failure + closeDialog->stop(); + delete closeDialog; + + // Verification of the first test step + return isDone; + +} + + +bool TestEkosCaptureHelper::startCapturing() +{ + // switch to the capture module + KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->captureModule(), 1000)); + + // check if capture is in a stopped state + KWRAP_SUB(QVERIFY(getCaptureStatus() == Ekos::CAPTURE_IDLE || getCaptureStatus() == Ekos::CAPTURE_ABORTED + || getCaptureStatus() == Ekos::CAPTURE_COMPLETE)); + + // press start + expectedCaptureStates.enqueue(Ekos::CAPTURE_CAPTURING); + KTRY_GADGET_SUB(Ekos::Manager::Instance()->captureModule(), QPushButton, startB); + KTRY_CLICK_SUB(Ekos::Manager::Instance()->captureModule(), startB); + + // check if capturing has started + KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(expectedCaptureStates, 5000); + // all checks succeeded + return true; +} + +bool TestEkosCaptureHelper::stopCapturing() +{ + // check if capture is in a stopped state + if (getCaptureStatus() == Ekos::CAPTURE_IDLE || getCaptureStatus() == Ekos::CAPTURE_ABORTED || getCaptureStatus() == Ekos::CAPTURE_COMPLETE) + return true; + + // switch to the capture module + KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->captureModule(), 1000)); + + // else press stop + expectedCaptureStates.enqueue(Ekos::CAPTURE_ABORTED); + KTRY_GADGET_SUB(Ekos::Manager::Instance()->captureModule(), QPushButton, startB); + KTRY_CLICK_SUB(Ekos::Manager::Instance()->captureModule(), startB); + + // check if capturing has stopped + KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(expectedCaptureStates, 5000); + // all checks succeeded + return true; +} + +/* ********************************************************************************* + * + * Helper functions + * + * ********************************************************************************* */ +void TestEkosCaptureHelper::setTreeviewCombo(QComboBox *combo, QString lookup) +{ + // Match the text recursively in the model, this results in a model index with a parent + QModelIndexList const list = combo->model()->match(combo->model()->index(0, 0), Qt::DisplayRole, QVariant::fromValue(lookup), 1, Qt::MatchRecursive); + QVERIFY(0 < list.count()); + QModelIndex const &index = list.first(); + QCOMPARE(list.value(0).data().toString(), lookup); + QVERIFY(!index.parent().parent().isValid()); + // Now set the combobox model root to the match's parent + combo->setRootModelIndex(index.parent()); + combo->setModelColumn(index.column()); + combo->setCurrentIndex(index.row()); + + // Now reset + combo->setRootModelIndex(QModelIndex()); + combo->view()->setCurrentIndex(index); + + // Check, if everything went well + QCOMPARE(combo->currentText(), lookup); +} + +/* ********************************************************************************* + * + * Slots for catching state changes + * + * ********************************************************************************* */ + +void TestEkosCaptureHelper::captureStatusChanged(Ekos::CaptureState status) { + m_CaptureStatus = status; + // check if the new state is the next one expected, then remove it from the stack + if (!expectedCaptureStates.isEmpty() && expectedCaptureStates.head() == status) + expectedCaptureStates.dequeue(); +} + diff --git a/Tests/kstars_ui/test_ekos_capture_helper.h b/Tests/kstars_ui/test_ekos_capture_helper.h new file mode 100644 index 000000000..2cca09b19 --- /dev/null +++ b/Tests/kstars_ui/test_ekos_capture_helper.h @@ -0,0 +1,343 @@ +/* + Helper class of KStars UI capture tests + + Copyright (C) 2020 + Wolfgang Reissenberger + + 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" +#include "test_ekos_debug.h" +#include "test_ekos_simulator.h" +#include "test_ekos_capture.h" + +#include "ekos/profileeditor.h" + +#include + +#pragma once + +/** + * @brief Helper function to verify the execution of a statement. + * + * If the result is false, this function immediately returns with false, + * otherwise simply continues. + * Use this function in subroutines of test cases which should return bool. + * @param statement expression to be verified + * @return false if statement equals false, otherwise continuing + */ +#define KVERIFY_SUB(statement) \ +do {\ + if (!QTest::qVerify(static_cast(statement), #statement, "", __FILE__, __LINE__))\ + return false;\ +} while (false) + +/** + * @brief Subroutine version of QVERIFY2 + * @return false if statement equals false, otherwise continuing + */ +#define KVERIFY2_SUB(statement, description) \ +do {\ + if (statement) {\ + if (!QTest::qVerify(true, #statement, (description), __FILE__, __LINE__))\ + return false;\ + } else {\ + if (!QTest::qVerify(false, #statement, (description), __FILE__, __LINE__))\ + return false;\ + }\ +} while (false) + +/** + * @brief Helper macro to wrap statements with test macros that returns false if + * the given statement preliminary invokes return (due to a test failure inside). + * @return false if statement equals false, otherwise continuing + */ +#define KWRAP_SUB(statement) \ +{ bool passed = false; \ + [&]() { statement; passed = true;}(); \ + if (!passed) return false; \ +} while (false); + +/** + * @brief Subroutine version of QTRY_TIMEOUT_DEBUG_IMPL + * @return false if expression equals false, otherwise continuing + */ +#define KTRY_TIMEOUT_DEBUG_IMPL_SUB(expr, timeoutValue, step)\ + if (!(expr)) { \ + QTRY_LOOP_IMPL((expr), (2 * timeoutValue), step);\ + if (expr) { \ + QString msg = QString::fromUtf8("QTestLib: This test case check (\"%1\") failed because the requested timeout (%2 ms) was too short, %3 ms would have been sufficient this time."); \ + msg = msg.arg(QString::fromUtf8(#expr)).arg(timeoutValue).arg(timeoutValue + qt_test_i); \ + KVERIFY2_SUB(false, qPrintable(msg)); \ + } \ + } + +/** + * @brief Subroutine version of QTRY_IMPL + * @return false if expression equals false, otherwise continuing + */ +#define KTRY_IMPL_SUB(expr, timeout)\ + const int qt_test_step = 50; \ + const int qt_test_timeoutValue = timeout; \ + QTRY_LOOP_IMPL((expr), qt_test_timeoutValue, qt_test_step); \ + KTRY_TIMEOUT_DEBUG_IMPL_SUB((expr), qt_test_timeoutValue, qt_test_step)\ + +/** + * @brief Subroutine version of QTRY_VERIFY_WITH_TIMEOUT + * @param expr expression to be verified + * @param timeout max time until the expression must become true + * @return false if statement equals false, otherwise continuing + */ +#define KTRY_VERIFY_WITH_TIMEOUT_SUB(expr, timeout) \ + do { \ + KTRY_IMPL_SUB((expr), timeout);\ + KVERIFY_SUB(expr); \ + } while (false) + +/** @brief Helper to retrieve a gadget from a certain module view. + * @param module KStars module that holds the checkox + * @param klass is the class of the gadget to look for. + * @param name is the gadget name to look for in the UI configuration. + * @warning Fails the test if the gadget "name" of class "klass" does not exist in the Mount module + */ +#define KTRY_GADGET(module, klass, name) klass * const name = module->findChild(#name); \ + QVERIFY2(name != nullptr, QString(#klass " '%1' does not exist and cannot be used").arg(#name).toStdString().c_str()) + +/** @brief Helper to retrieve a gadget from a certain module view (subroutine version). + * @param module KStars module that holds the checkox + * @param klass is the class of the gadget to look for. + * @param name is the gadget name to look for in the UI configuration. + * @warning Fails the test if the gadget "name" of class "klass" does not exist in the Mount module + */ +#define KTRY_GADGET_SUB(module, klass, name) klass * const name = module->findChild(#name); \ + KVERIFY2_SUB(name != nullptr, QString(#klass " '%1' does not exist and cannot be used").arg(#name).toStdString().c_str()) + +/** @brief Helper to click a button from a certain module view. + * @param module KStars module that holds the checkox + * @param button is the gadget name of the button to look for in the UI configuration. + * @warning Fails the test if the button is not currently enabled. + */ +#define KTRY_CLICK(module, button) do { \ + QTimer::singleShot(100, Ekos::Manager::Instance(), []() { \ + KTRY_GADGET(module, QPushButton, button); \ + QVERIFY2(button->isEnabled(), QString("QPushButton '%1' is disabled and cannot be clicked").arg(#button).toStdString().c_str()); \ + QTest::mouseClick(button, Qt::LeftButton); }); \ + QTest::qWait(200); } while(false) + +/** @brief Helper to click a button from a certain module view (subroutine version for KTRY_CLICK). + * @param module KStars module that holds the checkox + * @param button is the gadget name of the button to look for in the UI configuration. + * @warning Fails the test if the button is not currently enabled. + */ +#define KTRY_CLICK_SUB(module, button) do { \ + bool success = false; \ + QTimer::singleShot(100, Ekos::Manager::Instance(), [&]() { \ + KTRY_GADGET(module, QPushButton, button); \ + QVERIFY2(button->isEnabled(), QString("QPushButton '%1' is disabled and cannot be clicked").arg(#button).toStdString().c_str()); \ + QTest::mouseClick(button, Qt::LeftButton); success = true;}); \ + KTRY_VERIFY_WITH_TIMEOUT_SUB(success, 1000);} while(false) + +/** @brief Helper to set a checkbox and verify whether it succeeded (subroutine version) + * @param module KStars module that holds the checkox + * @param checkbox object name of the checkbox + * @param value value the checkbox should be set + */ +#define KTRY_SET_CHECKBOX_SUB(module, checkbox, value) \ + KWRAP_SUB(KTRY_GADGET(module, QCheckBox, checkbox); checkbox->setChecked(value); QVERIFY(checkbox->isChecked() == value)) + +/** @brief Helper to set a radiobutton and verify whether it succeeded + * @param module KStars module that holds the radiobutton + * @param checkbox object name of the radiobutton + * @param value value the radiobutton should be set + */ +#define KTRY_SET_RADIOBUTTON_SUB(module, radiobutton, value) \ + KWRAP_SUB(KTRY_GADGET(module, QRadioButton, radiobutton); radiobutton->setChecked(value); QVERIFY(radiobutton->isChecked() == value)) + +/** @brief Helper to set a spinbox and verify whether it succeeded + * @param module KStars module that holds the spinbox + * @param spinbox object name of the spinbox + * @param value value the spinbox should be set + */ +#define KTRY_SET_SPINBOX_SUB(module, spinbox, x) \ + KWRAP_SUB(KTRY_GADGET(module, QSpinBox, spinbox); spinbox->setValue(x); QVERIFY(spinbox->value() == x)) + +/** @brief Helper to set a doublespinbox and verify whether it succeeded + * @param module KStars module that holds the spinbox + * @param spinbox object name of the spinbox + * @param value value the spinbox should be set + */ +#define KTRY_SET_DOUBLESPINBOX_SUB(module, spinbox, x) \ + KWRAP_SUB(KTRY_GADGET(module, QDoubleSpinBox, spinbox); spinbox->setValue(x); QVERIFY((spinbox->value() - x < 0.001))) + +/** @brief Helper to set a combo box and verify whether it succeeded + * @param module KStars module that holds the combo box + * @param combo object name of the combo box + * @param value value the combo box should be set + */ +#define KTRY_SET_COMBO(module, combo, value) \ + KTRY_GADGET(module, QComboBox, combo); combo->setCurrentText(value); QVERIFY(combo->currentText() == value) + +/** @brief Subroutine version of @see KTRY_SET_COMBO + * @param module KStars module that holds the combo box + * @param combo object name of the combo box + * @param value value the combo box should be set + */ +#define KTRY_SET_COMBO_SUB(module, combo, value) \ + KWRAP_SUB(KTRY_GADGET(module, QComboBox, combo); combo->setCurrentText(value); QVERIFY(combo->currentText() == value)) + +/** @brief Helper to set a combo box by indexand verify whether it succeeded + * @param module KStars module that holds the combo box + * @param combo object name of the combo box + * @param value index the combo box should be selected + */ +#define KTRY_SET_COMBO_INDEX_SUB(module, combo, value) \ + KWRAP_SUB(KTRY_GADGET(module, QComboBox, combo); combo->setCurrentIndex(value); QVERIFY(combo->currentIndex() == value)) + +/** @brief Helper to set a line edit box and verify whether it succeeded + * @param module KStars module that holds the line edit box + * @param spinbox object name of the line edit box + * @param value value the line edit box should be set + */ +#define KTRY_SET_LINEEDIT(module, lineedit, value) \ + KTRY_GADGET(module, QLineEdit, lineedit); lineedit->setText(value); QVERIFY((lineedit->text() == value)) + +/** @brief Subroutine version of @see KTRY_SET_LINEEDIT + * @param module KStars module that holds the line edit box + * @param spinbox object name of the line edit box + * @param value value the line edit box should be set + */ +#define KTRY_SET_LINEEDIT_SUB(module, lineedit, value) \ + KWRAP_SUB(KTRY_GADGET(module, QLineEdit, lineedit); lineedit->setText(value); QVERIFY((lineedit->text() == value))) + +/** + * @brief Helper to check whether a state queue is empty after the given delay + * @param queue event queue + * @param delay in milliseconds + */ +#define KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(queue, delay) \ + if (! QTest::qWaitFor([&](){return queue.isEmpty();}, delay)) { \ + QString result("States not reached: "); \ + QTextStream stream(&result); \ + while (!(queue).isEmpty()) stream << (queue).dequeue(); \ + QFAIL(qPrintable(result));} + +/** + * @brief Helper to check whether a state queue is empty after the given delay + * @param queue event queue + * @param delay in milliseconds + */ +#define KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(queue, delay) \ + if (! QTest::qWaitFor([&](){return queue.isEmpty();}, delay)) { \ + QString result("States not reached: "); \ + QTextStream stream(&result); \ + while (!(queue).isEmpty()) stream << (queue).dequeue(); \ + QWARN(qPrintable(result)); return false;} + +/** + * @brief Helper to verify if the text in the text field starts with the given text + * @param field UI Text field + * @param text Text the text field should start with + * @param timeout in ms + */ +#define KTRY_VERIFY_TEXTFIELD_STARTS_WITH_TIMEOUT_SUB(field, title, timeout) \ + KTRY_VERIFY_WITH_TIMEOUT_SUB(field->text().length() >= QString(title).length() && \ + field->text().left(QString(title).length()).compare(title) == 0, timeout) + +/** + * @brief Helper function for switching to a certain module + * @param module target module + * @param timeout in ms + */ +#define KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(module, timeout) do {\ + KTRY_EKOS_GADGET(QTabWidget, toolsWidget); \ + toolsWidget->setCurrentWidget(module); \ + QTRY_COMPARE_WITH_TIMEOUT(toolsWidget->currentWidget(), module, timeout);} while (false) + + +class TestEkosCaptureHelper : public QObject +{ + Q_OBJECT + +public: + + explicit TestEkosCaptureHelper(); + + // Mount device + QString m_MountDevice = "Telescope Simulator"; + // CCD device + QString m_CCDDevice = "CCD Simulator"; + // Guiding device + QString m_GuiderDevice = "Guide Simulator"; + // Focusing device + QString m_FocuserDevice = "Focuser Simulator"; + + /** + * @brief Initialization ahead of executing the test cases. + */ + void initTestCase(); + + /** + * @brief Cleanup after test cases have been executed. + */ + void cleanupTestCase(); + + /** + * @brief Configure the EKOS profile + * @param name of the profile + * @param isPHD2 use internal guider or PHD2 + */ + bool setupEkosProfile(QString name, bool isPHD2); + + /** + * @brief create a new EKOS profile + * @param name name of the profile + * @param isPHD2 use internal guider or PHD2 + * @param isDone will be true if everything succeeds + */ + void createEkosProfile(QString name, bool isPHD2, bool *isDone); + + /** + * @brief Fill mount, guider, CCD and focuser of an EKOS profile + * @param isDone will be true if everything succeeds + */ + void fillProfile(bool *isDone); + + /** + * @brief Helper function for start of capturing + */ + bool startCapturing(); + + /** + * @brief Helper function to stop capturing + */ + bool stopCapturing(); + + /** + * @brief Set a tree view combo to a given value + * @param combo box with tree view + * @param lookup target value + */ + void setTreeviewCombo(QComboBox *combo, const QString lookup); + + + // sequence of capture states that are expected + QQueue expectedCaptureStates; + + /** + * @brief Slot to track the capture status + * @param status new capture status + */ + void captureStatusChanged(Ekos::CaptureState status); + + // current capture status + Ekos::CaptureState m_CaptureStatus; + + /** + * @brief Retrieve the current capture status. + */ + inline Ekos::CaptureState getCaptureStatus() {return m_CaptureStatus;} +}; diff --git a/Tests/kstars_ui/test_ekos_meridianflip_base.h b/Tests/kstars_ui/test_ekos_meridianflip_base.h index f64b6bd00..e315bde15 100644 --- a/Tests/kstars_ui/test_ekos_meridianflip_base.h +++ b/Tests/kstars_ui/test_ekos_meridianflip_base.h @@ -30,242 +30,7 @@ #include "test_ekos_simulator.h" #include "test_ekos_capture.h" - -/** - * @brief Helper function to verify the execution of a statement. - * - * If the result is false, this function immediately returns with false, - * otherwise simply continues. - * Use this function in subroutines of test cases which should return bool. - * @param statement expression to be verified - * @return false if statement equals false, otherwise continuing - */ -#define KVERIFY_SUB(statement) \ -do {\ - if (!QTest::qVerify(static_cast(statement), #statement, "", __FILE__, __LINE__))\ - return false;\ -} while (false) - -/** - * @brief Subroutine version of QVERIFY2 - * @return false if statement equals false, otherwise continuing - */ -#define KVERIFY2_SUB(statement, description) \ -do {\ - if (statement) {\ - if (!QTest::qVerify(true, #statement, (description), __FILE__, __LINE__))\ - return false;\ - } else {\ - if (!QTest::qVerify(false, #statement, (description), __FILE__, __LINE__))\ - return false;\ - }\ -} while (false) - -/** - * @brief Helper macro to wrap statements with test macros that returns false if - * the given statement preliminary invokes return (due to a test failure inside). - * @return false if statement equals false, otherwise continuing - */ -#define KWRAP_SUB(statement) \ -{ bool passed = false; \ - [&]() { statement; passed = true;}(); \ - if (!passed) return false; \ -} while (false); - -/** - * @brief Subroutine version of QTRY_TIMEOUT_DEBUG_IMPL - * @return false if expression equals false, otherwise continuing - */ -#define KTRY_TIMEOUT_DEBUG_IMPL_SUB(expr, timeoutValue, step)\ - if (!(expr)) { \ - QTRY_LOOP_IMPL((expr), (2 * timeoutValue), step);\ - if (expr) { \ - QString msg = QString::fromUtf8("QTestLib: This test case check (\"%1\") failed because the requested timeout (%2 ms) was too short, %3 ms would have been sufficient this time."); \ - msg = msg.arg(QString::fromUtf8(#expr)).arg(timeoutValue).arg(timeoutValue + qt_test_i); \ - KVERIFY2_SUB(false, qPrintable(msg)); \ - } \ - } - -/** - * @brief Subroutine version of QTRY_IMPL - * @return false if expression equals false, otherwise continuing - */ -#define KTRY_IMPL_SUB(expr, timeout)\ - const int qt_test_step = 50; \ - const int qt_test_timeoutValue = timeout; \ - QTRY_LOOP_IMPL((expr), qt_test_timeoutValue, qt_test_step); \ - KTRY_TIMEOUT_DEBUG_IMPL_SUB((expr), qt_test_timeoutValue, qt_test_step)\ - -/** - * @brief Subroutine version of QTRY_VERIFY_WITH_TIMEOUT - * @param expr expression to be verified - * @param timeout max time until the expression must become true - * @return false if statement equals false, otherwise continuing - */ -#define KTRY_VERIFY_WITH_TIMEOUT_SUB(expr, timeout) \ - do { \ - KTRY_IMPL_SUB((expr), timeout);\ - KVERIFY_SUB(expr); \ - } while (false) - -/** @brief Helper to retrieve a gadget from a certain module view. - * @param module KStars module that holds the checkox - * @param klass is the class of the gadget to look for. - * @param name is the gadget name to look for in the UI configuration. - * @warning Fails the test if the gadget "name" of class "klass" does not exist in the Mount module - */ -#define KTRY_GADGET(module, klass, name) klass * const name = module->findChild(#name); \ - QVERIFY2(name != nullptr, QString(#klass " '%1' does not exist and cannot be used").arg(#name).toStdString().c_str()) - -/** @brief Helper to retrieve a gadget from a certain module view (subroutine version). - * @param module KStars module that holds the checkox - * @param klass is the class of the gadget to look for. - * @param name is the gadget name to look for in the UI configuration. - * @warning Fails the test if the gadget "name" of class "klass" does not exist in the Mount module - */ -#define KTRY_GADGET_SUB(module, klass, name) klass * const name = module->findChild(#name); \ - KVERIFY2_SUB(name != nullptr, QString(#klass " '%1' does not exist and cannot be used").arg(#name).toStdString().c_str()) - -/** @brief Helper to click a button from a certain module view. - * @param module KStars module that holds the checkox - * @param button is the gadget name of the button to look for in the UI configuration. - * @warning Fails the test if the button is not currently enabled. - */ -#define KTRY_CLICK(module, button) do { \ - QTimer::singleShot(100, Ekos::Manager::Instance(), []() { \ - KTRY_GADGET(module, QPushButton, button); \ - QVERIFY2(button->isEnabled(), QString("QPushButton '%1' is disabled and cannot be clicked").arg(#button).toStdString().c_str()); \ - QTest::mouseClick(button, Qt::LeftButton); }); \ - QTest::qWait(200); } while(false) - -/** @brief Helper to click a button from a certain module view (subroutine version for KTRY_CLICK). - * @param module KStars module that holds the checkox - * @param button is the gadget name of the button to look for in the UI configuration. - * @warning Fails the test if the button is not currently enabled. - */ -#define KTRY_CLICK_SUB(module, button) do { \ - bool success = false; \ - QTimer::singleShot(100, Ekos::Manager::Instance(), [&]() { \ - KTRY_GADGET(module, QPushButton, button); \ - QVERIFY2(button->isEnabled(), QString("QPushButton '%1' is disabled and cannot be clicked").arg(#button).toStdString().c_str()); \ - QTest::mouseClick(button, Qt::LeftButton); success = true;}); \ - KTRY_VERIFY_WITH_TIMEOUT_SUB(success, 1000);} while(false) - -/** @brief Helper to set a checkbox and verify whether it succeeded (subroutine version) - * @param module KStars module that holds the checkox - * @param checkbox object name of the checkbox - * @param value value the checkbox should be set - */ -#define KTRY_SET_CHECKBOX_SUB(module, checkbox, value) \ - KWRAP_SUB(KTRY_GADGET(module, QCheckBox, checkbox); checkbox->setChecked(value); QVERIFY(checkbox->isChecked() == value)) - -/** @brief Helper to set a radiobutton and verify whether it succeeded - * @param module KStars module that holds the radiobutton - * @param checkbox object name of the radiobutton - * @param value value the radiobutton should be set - */ -#define KTRY_SET_RADIOBUTTON_SUB(module, radiobutton, value) \ - KWRAP_SUB(KTRY_GADGET(module, QRadioButton, radiobutton); radiobutton->setChecked(value); QVERIFY(radiobutton->isChecked() == value)) - -/** @brief Helper to set a spinbox and verify whether it succeeded - * @param module KStars module that holds the spinbox - * @param spinbox object name of the spinbox - * @param value value the spinbox should be set - */ -#define KTRY_SET_SPINBOX_SUB(module, spinbox, x) \ - KWRAP_SUB(KTRY_GADGET(module, QSpinBox, spinbox); spinbox->setValue(x); QVERIFY(spinbox->value() == x)) - -/** @brief Helper to set a doublespinbox and verify whether it succeeded - * @param module KStars module that holds the spinbox - * @param spinbox object name of the spinbox - * @param value value the spinbox should be set - */ -#define KTRY_SET_DOUBLESPINBOX_SUB(module, spinbox, x) \ - KWRAP_SUB(KTRY_GADGET(module, QDoubleSpinBox, spinbox); spinbox->setValue(x); QVERIFY((spinbox->value() - x < 0.001))) - -/** @brief Helper to set a combo box and verify whether it succeeded - * @param module KStars module that holds the combo box - * @param combo object name of the combo box - * @param value value the combo box should be set - */ -#define KTRY_SET_COMBO(module, combo, value) \ - KTRY_GADGET(module, QComboBox, combo); combo->setCurrentText(value); QVERIFY(combo->currentText() == value) - -/** @brief Subroutine version of @see KTRY_SET_COMBO - * @param module KStars module that holds the combo box - * @param combo object name of the combo box - * @param value value the combo box should be set - */ -#define KTRY_SET_COMBO_SUB(module, combo, value) \ - KWRAP_SUB(KTRY_GADGET(module, QComboBox, combo); combo->setCurrentText(value); QVERIFY(combo->currentText() == value)) - -/** @brief Helper to set a combo box by indexand verify whether it succeeded - * @param module KStars module that holds the combo box - * @param combo object name of the combo box - * @param value index the combo box should be selected - */ -#define KTRY_SET_COMBO_INDEX_SUB(module, combo, value) \ - KWRAP_SUB(KTRY_GADGET(module, QComboBox, combo); combo->setCurrentIndex(value); QVERIFY(combo->currentIndex() == value)) - -/** @brief Helper to set a line edit box and verify whether it succeeded - * @param module KStars module that holds the line edit box - * @param spinbox object name of the line edit box - * @param value value the line edit box should be set - */ -#define KTRY_SET_LINEEDIT(module, lineedit, value) \ - KTRY_GADGET(module, QLineEdit, lineedit); lineedit->setText(value); QVERIFY((lineedit->text() == value)) - -/** @brief Subroutine version of @see KTRY_SET_LINEEDIT - * @param module KStars module that holds the line edit box - * @param spinbox object name of the line edit box - * @param value value the line edit box should be set - */ -#define KTRY_SET_LINEEDIT_SUB(module, lineedit, value) \ - KWRAP_SUB(KTRY_GADGET(module, QLineEdit, lineedit); lineedit->setText(value); QVERIFY((lineedit->text() == value))) - -/** - * @brief Helper to verify if the text in the text field starts with the given text - * @param field UI Text field - * @param text Text the text field should start with - * @param timeout in ms - */ -#define KTRY_VERIFY_TEXTFIELD_STARTS_WITH_TIMEOUT_SUB(field, title, timeout) \ - KTRY_VERIFY_WITH_TIMEOUT_SUB(field->text().length() >= QString(title).length() && \ - field->text().left(QString(title).length()).compare(title) == 0, timeout) - -/** - * @brief Helper to check whether a state queue is empty after the given delay - * @param queue event queue - * @param delay in milliseconds - */ -#define KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(queue, delay) \ - if (! QTest::qWaitFor([&](){return queue.isEmpty();}, delay)) { \ - QString result("States not reached: "); \ - QTextStream stream(&result); \ - while (!(queue).isEmpty()) stream << (queue).dequeue(); \ - QFAIL(qPrintable(result));} - -/** - * @brief Helper to check whether a state queue is empty after the given delay - * @param queue event queue - * @param delay in milliseconds - */ -#define KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(queue, delay) \ - if (! QTest::qWaitFor([&](){return queue.isEmpty();}, delay)) { \ - QString result("States not reached: "); \ - QTextStream stream(&result); \ - while (!(queue).isEmpty()) stream << (queue).dequeue(); \ - QWARN(qPrintable(result)); return false;} - -/** - * @brief Helper function for switching to a certain module - * @param module target module - * @param timeout in ms - */ -#define KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(module, timeout) do {\ - KTRY_EKOS_GADGET(QTabWidget, toolsWidget); \ - toolsWidget->setCurrentWidget(module); \ - QTRY_COMPARE_WITH_TIMEOUT(toolsWidget->currentWidget(), module, timeout);} while (false) +#include "test_ekos_capture_helper.h" #define QTEST_KSTARS_MAIN_GUIDERSELECT(klass) \ -- GitLab From 12b8069e7ab48032567a936b8d746afb58a902da Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Thu, 22 Oct 2020 23:13:02 +0200 Subject: [PATCH 03/16] Refactoring for test data series --- Tests/kstars_ui/test_ekos_capture_count.cpp | 30 ++++++++++++++++----- Tests/kstars_ui/test_ekos_capture_count.h | 2 +- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/Tests/kstars_ui/test_ekos_capture_count.cpp b/Tests/kstars_ui/test_ekos_capture_count.cpp index 00e4002b8..e6199be67 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.cpp +++ b/Tests/kstars_ui/test_ekos_capture_count.cpp @@ -26,9 +26,7 @@ void TestEkosCaptureCount::testCaptureWithCaptureFramesMap() { // setup capture sequence, fill captured frames map and set expectations - prepareCapture(2.0, {"Red", "Green", "Blue"}, {2, 2, 2}, - {qMakePair(QString("Red"), 2), qMakePair(QString("Green"), 1), qMakePair(QString("Blue"), 2)}, - {qMakePair(calculateSignature(QString("Green")), 1)}); + prepareCapture(2.0, {"Red", "Green", "Blue"}, {2, 2, 2}, {"Red:2", "Green:1", "Blue:2"}, {"Green:1"}); // capture QVERIFY(m_CaptureHelper->startCapturing()); @@ -106,8 +104,26 @@ bool TestEkosCaptureCount::shutdownEkosProfile() } bool TestEkosCaptureCount::prepareCapture(double exptime, QList filters, QList countsList, - QList> capturedFramesMap, QList> expected) + QList capturedFramesMap, QList expected) { + // parse captured frames map + QList> cfm; + for (QString value : capturedFramesMap) + { + KVERIFY_SUB(value.indexOf(":") > -1); + cfm.append(qMakePair(value.left(value.indexOf(":")), + value.right(value.length()-value.indexOf(":")-1).toInt())); + } + + // parse expected captures list + QList> target; + for (QString value : expected) + { + KVERIFY_SUB(value.indexOf(":") > -1); + target.append(qMakePair(calculateSignature(value.left(value.indexOf(":"))), + value.right(value.length()-value.indexOf(":")-1).toInt())); + } + // switch to capture module Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule(); KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000)); @@ -124,10 +140,10 @@ bool TestEkosCaptureCount::prepareCapture(double exptime, QList filters for (int i = 0; i < filters.size(); i++) KWRAP_SUB(KTRY_CAPTURE_ADD_LIGHT(exptime, countsList[i], 0, filters[i], destination->path())); // fill the captured frames map that hold the numbers of already taken frames - for (int i = 0; i < capturedFramesMap.size(); i++) - capture->setCapturedFramesMap(calculateSignature(capturedFramesMap[i].first), capturedFramesMap[i].second); + for (int i = 0; i < cfm.size(); i++) + capture->setCapturedFramesMap(calculateSignature(cfm[i].first), cfm[i].second); // fill the map of expected frames - for (QList>::iterator it = expected.begin(); it != expected.end(); ++it) + for (QList>::iterator it = target.begin(); it != target.end(); ++it) expectedImages.insert(it->first, it->second); return true; diff --git a/Tests/kstars_ui/test_ekos_capture_count.h b/Tests/kstars_ui/test_ekos_capture_count.h index a50498271..e917d6021 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.h +++ b/Tests/kstars_ui/test_ekos_capture_count.h @@ -44,7 +44,7 @@ protected: */ bool shutdownEkosProfile(); - bool prepareCapture(double exptime, QList filters, QList countsList, QList> capturedFramesMap, QList> expected); + bool prepareCapture(double exptime, QList filters, QList countsList, QList capturedFramesMap, QList expected); // mapping between image signature and number of images expected to be captured for this signature QMap expectedImages; -- GitLab From a91a3aaba74efb1ba908006bc0747ac78b1dc9d2 Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Sat, 24 Oct 2020 18:15:37 +0200 Subject: [PATCH 04/16] Refactored of capture count test cases for using QTest test data structures --- Tests/kstars_ui/test_ekos_capture_count.cpp | 76 ++++++++++++++------- Tests/kstars_ui/test_ekos_capture_count.h | 19 +++++- 2 files changed, 68 insertions(+), 27 deletions(-) diff --git a/Tests/kstars_ui/test_ekos_capture_count.cpp b/Tests/kstars_ui/test_ekos_capture_count.cpp index e6199be67..23f3b14a5 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.cpp +++ b/Tests/kstars_ui/test_ekos_capture_count.cpp @@ -26,14 +26,14 @@ void TestEkosCaptureCount::testCaptureWithCaptureFramesMap() { // setup capture sequence, fill captured frames map and set expectations - prepareCapture(2.0, {"Red", "Green", "Blue"}, {2, 2, 2}, {"Red:2", "Green:1", "Blue:2"}, {"Green:1"}); + prepareCapture(); // capture QVERIFY(m_CaptureHelper->startCapturing()); // verify whether all frames are captured as expected bool success = true; - for (QMap::iterator it = expectedImages.begin(); it != expectedImages.end(); ++it) + for (QMap::iterator it = m_expectedImages.begin(); it != m_expectedImages.end(); ++it) if (it.value() != 0) { QWARN(QString("Capture count for signature %1 does not match: %2 frames too %3 captured.").arg(it.key()).arg(abs(it.value())).arg(it.value() < 0 ? "much" : "few").toStdString().c_str()); @@ -42,6 +42,12 @@ void TestEkosCaptureCount::testCaptureWithCaptureFramesMap() QVERIFY2(success, "Capturing did not produce the expected amount of frames."); } +void TestEkosCaptureCount::testCaptureWithCaptureFramesMap_data() +{ + prepareTestData(2.0, "Red:2,Green:2,Blue:2", "Red:2,Green:1,Blue:2", "Green:1"); + +} + void TestEkosCaptureCount::initTestCase() { KVERIFY_EKOS_IS_HIDDEN(); @@ -103,56 +109,76 @@ bool TestEkosCaptureCount::shutdownEkosProfile() return true; } -bool TestEkosCaptureCount::prepareCapture(double exptime, QList filters, QList countsList, - QList capturedFramesMap, QList expected) +bool TestEkosCaptureCount::prepareCapture() { - // parse captured frames map - QList> cfm; - for (QString value : capturedFramesMap) - { - KVERIFY_SUB(value.indexOf(":") > -1); - cfm.append(qMakePair(value.left(value.indexOf(":")), - value.right(value.length()-value.indexOf(":")-1).toInt())); - } + QFETCH(double, exptime); + QFETCH(QString, sequence); + QFETCH(QString, capturedFramesMap); + QFETCH(QString, expectedFrames); // parse expected captures list QList> target; - for (QString value : expected) + for (QString value : expectedFrames.split(",")) { KVERIFY_SUB(value.indexOf(":") > -1); target.append(qMakePair(calculateSignature(value.left(value.indexOf(":"))), value.right(value.length()-value.indexOf(":")-1).toInt())); } - // switch to capture module Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule(); KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000)); - // create the capture sequences + // create the destination for images KWRAP_SUB(QVERIFY(destination->isValid())); KWRAP_SUB(QVERIFY(destination->autoRemove())); qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << destination->path(); - // check that for each filter we need a frame count - KVERIFY_SUB(filters.size() == countsList.size()); - // create capture sequences - for (int i = 0; i < filters.size(); i++) - KWRAP_SUB(KTRY_CAPTURE_ADD_LIGHT(exptime, countsList[i], 0, filters[i], destination->path())); + for (QString value : sequence.split(",")) + { + KVERIFY_SUB(value.indexOf(":") > -1); + QString filter = value.left(value.indexOf(":")); + int count = value.right(value.length()-value.indexOf(":")-1).toInt(); + KWRAP_SUB(KTRY_CAPTURE_ADD_LIGHT(exptime, count, 0, filter, destination->path())); + } + // fill the captured frames map that hold the numbers of already taken frames - for (int i = 0; i < cfm.size(); i++) - capture->setCapturedFramesMap(calculateSignature(cfm[i].first), cfm[i].second); + // parse captured frames map + for (QString value : capturedFramesMap.split(",")) + { + KVERIFY_SUB(value.indexOf(":") > -1); + QString filter = value.left(value.indexOf(":")); + int count = value.right(value.length()-value.indexOf(":")-1).toInt(); + capture->setCapturedFramesMap(calculateSignature(filter), count); + } + // fill the map of expected frames - for (QList>::iterator it = target.begin(); it != target.end(); ++it) - expectedImages.insert(it->first, it->second); + for (QString value : expectedFrames.split(",")) + { + KVERIFY_SUB(value.indexOf(":") > -1); + QString filter = value.left(value.indexOf(":")); + int count = value.right(value.length()-value.indexOf(":")-1).toInt(); + m_expectedImages.insert(calculateSignature(filter), count); + } return true; } +void TestEkosCaptureCount::prepareTestData(double exptime, QString sequence, QString capturedFramesMap, QString expectedFrames) +{ + + QTest::addColumn("exptime"); /*!< exposure time */ + QTest::addColumn("sequence"); /*!< list of filters */ + QTest::addColumn("capturedFramesMap"); /*!< list of frame counts */ + QTest::addColumn("expectedFrames"); /*!< list of frames per filter that are expected */ + + QTest::newRow(QString("seq=%1x, existing=%2").arg(sequence).arg(capturedFramesMap).toStdString().c_str()) << exptime << sequence << capturedFramesMap << expectedFrames; + } + void TestEkosCaptureCount::newImageReceived(Ekos::SequenceJob *job) { // reduce the for the job's signature the number of expected images - expectedImages.insert(job->getSignature(), expectedImages.value(job->getSignature(), 0) - 1); + m_expectedImages.insert(job->getSignature(), m_expectedImages.value(job->getSignature(), 0) - 1); } QString TestEkosCaptureCount::calculateSignature(QString filter) diff --git a/Tests/kstars_ui/test_ekos_capture_count.h b/Tests/kstars_ui/test_ekos_capture_count.h index e917d6021..5ee691ae8 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.h +++ b/Tests/kstars_ui/test_ekos_capture_count.h @@ -44,10 +44,22 @@ protected: */ bool shutdownEkosProfile(); - bool prepareCapture(double exptime, QList filters, QList countsList, QList capturedFramesMap, QList expected); + /** + * @brief Setup capturing + */ + bool prepareCapture(); + + /** + * @brief Helper function translating simple QString input into QTest test data rows + * @param exptime exposure time of the sequence + * @param sequence filter and count as QString("::: expectedImages; + QMap m_expectedImages; /** * @brief Register that a new image has been captured * @param job capture sequence job holding the image meta data @@ -77,6 +89,9 @@ private slots: */ void testCaptureWithCaptureFramesMap(); + /** @brief Test data for @see testCaptureWithCaptureFramesMap() */ + void testCaptureWithCaptureFramesMap_data(); + }; #endif // HAVE_INDI -- GitLab From 30675171393e5b967a37eb46d5a4c6ab38b7960d Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Sun, 25 Oct 2020 00:13:50 +0200 Subject: [PATCH 05/16] Capture counting test cases for tests without scheduler created --- Tests/kstars_ui/test_ekos_capture_count.cpp | 71 ++++++++++++-------- Tests/kstars_ui/test_ekos_capture_count.h | 3 +- Tests/kstars_ui/test_ekos_capture_helper.cpp | 6 +- Tests/kstars_ui/test_ekos_capture_helper.h | 3 +- 4 files changed, 51 insertions(+), 32 deletions(-) diff --git a/Tests/kstars_ui/test_ekos_capture_count.cpp b/Tests/kstars_ui/test_ekos_capture_count.cpp index 23f3b14a5..1427a7330 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.cpp +++ b/Tests/kstars_ui/test_ekos_capture_count.cpp @@ -26,10 +26,19 @@ void TestEkosCaptureCount::testCaptureWithCaptureFramesMap() { // setup capture sequence, fill captured frames map and set expectations - prepareCapture(); + QVERIFY(prepareCapture()); + + // verify if at least one capture is expected + int framesCount = 0; + for(int value: m_expectedImages.values()) + framesCount += value; // capture - QVERIFY(m_CaptureHelper->startCapturing()); + QVERIFY(m_CaptureHelper->startCapturing(framesCount > 0)); + + // wait for finish capturing + QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_COMPLETE || m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_IDLE || + m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_ABORTED, framesCount * 10000); // verify whether all frames are captured as expected bool success = true; @@ -44,8 +53,12 @@ void TestEkosCaptureCount::testCaptureWithCaptureFramesMap() void TestEkosCaptureCount::testCaptureWithCaptureFramesMap_data() { - prepareTestData(2.0, "Red:2,Green:2,Blue:2", "Red:2,Green:1,Blue:2", "Green:1"); - + prepareTestData(1.0, "Red:2,Green:2,Blue:2", "Red:2,Green:1,Blue:2", "Green:1"); + prepareTestData(1.0, "Red:2,Green:2,Blue:2", "Red:2,Green:2,Blue:2", ""); + prepareTestData(1.0, "Red:1,Green:1,Red:1", "", "Red:2,Green:1"); + prepareTestData(1.0, "Red:2,Green:2,Red:2", "Red:2,Green:2", "Red:2"); + prepareTestData(1.0, "Red:2,Green:2,Red:1", "Red:3,Green:1", "Green:1"); + prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:5,Red:1,Green:1", "Blue:1"); } void TestEkosCaptureCount::initTestCase() @@ -69,7 +82,7 @@ void TestEkosCaptureCount::cleanupTestCase() void TestEkosCaptureCount::init() { - connect(Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::newImage, this, &TestEkosCaptureCount::newImageReceived); + connect(Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::captureComplete, this, &TestEkosCaptureCount::captureComplete); } void TestEkosCaptureCount::cleanup() @@ -80,7 +93,10 @@ void TestEkosCaptureCount::cleanup() Ekos::Manager::Instance()->captureModule()->clearSequenceQueue(); KTRY_GADGET(Ekos::Manager::Instance()->captureModule(), QTableWidget, queueTable); QTRY_VERIFY_WITH_TIMEOUT(queueTable->rowCount() == 0, 2000); - disconnect(Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::newImage, this, &TestEkosCaptureCount::newImageReceived); + disconnect(Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::captureComplete, this, &TestEkosCaptureCount::captureComplete); + + // clean up expected images + m_expectedImages.clear(); } @@ -116,14 +132,6 @@ bool TestEkosCaptureCount::prepareCapture() QFETCH(QString, capturedFramesMap); QFETCH(QString, expectedFrames); - // parse expected captures list - QList> target; - for (QString value : expectedFrames.split(",")) - { - KVERIFY_SUB(value.indexOf(":") > -1); - target.append(qMakePair(calculateSignature(value.left(value.indexOf(":"))), - value.right(value.length()-value.indexOf(":")-1).toInt())); - } // switch to capture module Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule(); KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000)); @@ -139,28 +147,37 @@ bool TestEkosCaptureCount::prepareCapture() KVERIFY_SUB(value.indexOf(":") > -1); QString filter = value.left(value.indexOf(":")); int count = value.right(value.length()-value.indexOf(":")-1).toInt(); + KTRY_SET_CHECKBOX_SUB(capture, fileTimestampS, true); KWRAP_SUB(KTRY_CAPTURE_ADD_LIGHT(exptime, count, 0, filter, destination->path())); + // ensure that no old values are present + capture->setCapturedFramesMap(calculateSignature(filter), 0); } // fill the captured frames map that hold the numbers of already taken frames - // parse captured frames map - for (QString value : capturedFramesMap.split(",")) + if (capturedFramesMap != "") { - KVERIFY_SUB(value.indexOf(":") > -1); - QString filter = value.left(value.indexOf(":")); - int count = value.right(value.length()-value.indexOf(":")-1).toInt(); - capture->setCapturedFramesMap(calculateSignature(filter), count); + for (QString value : capturedFramesMap.split(",")) + { + KVERIFY_SUB(value.indexOf(":") > -1); + QString filter = value.left(value.indexOf(":")); + int count = value.right(value.length()-value.indexOf(":")-1).toInt(); + capture->setCapturedFramesMap(calculateSignature(filter), count); + } } // fill the map of expected frames - for (QString value : expectedFrames.split(",")) + if (expectedFrames != "") { - KVERIFY_SUB(value.indexOf(":") > -1); - QString filter = value.left(value.indexOf(":")); - int count = value.right(value.length()-value.indexOf(":")-1).toInt(); - m_expectedImages.insert(calculateSignature(filter), count); + for (QString value : expectedFrames.split(",")) + { + KVERIFY_SUB(value.indexOf(":") > -1); + QString filter = value.left(value.indexOf(":")); + int count = value.right(value.length()-value.indexOf(":")-1).toInt(); + m_expectedImages.insert(filter, count); + } } + // everything successfully completed return true; } @@ -175,10 +192,10 @@ void TestEkosCaptureCount::prepareTestData(double exptime, QString sequence, QSt QTest::newRow(QString("seq=%1x, existing=%2").arg(sequence).arg(capturedFramesMap).toStdString().c_str()) << exptime << sequence << capturedFramesMap << expectedFrames; } -void TestEkosCaptureCount::newImageReceived(Ekos::SequenceJob *job) +void TestEkosCaptureCount::captureComplete(const QString &filename, double exposureSeconds, const QString &filter, double hfr) { // reduce the for the job's signature the number of expected images - m_expectedImages.insert(job->getSignature(), m_expectedImages.value(job->getSignature(), 0) - 1); + m_expectedImages.insert(filter, m_expectedImages.value(filter, 0) - 1); } QString TestEkosCaptureCount::calculateSignature(QString filter) diff --git a/Tests/kstars_ui/test_ekos_capture_count.h b/Tests/kstars_ui/test_ekos_capture_count.h index 5ee691ae8..168a93999 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.h +++ b/Tests/kstars_ui/test_ekos_capture_count.h @@ -62,9 +62,8 @@ protected: QMap m_expectedImages; /** * @brief Register that a new image has been captured - * @param job capture sequence job holding the image meta data */ - void newImageReceived(Ekos::SequenceJob *job); + void captureComplete(const QString &filename, double exposureSeconds, const QString &filter, double hfr); protected slots: diff --git a/Tests/kstars_ui/test_ekos_capture_helper.cpp b/Tests/kstars_ui/test_ekos_capture_helper.cpp index 9035018a4..824d4119f 100644 --- a/Tests/kstars_ui/test_ekos_capture_helper.cpp +++ b/Tests/kstars_ui/test_ekos_capture_helper.cpp @@ -149,7 +149,7 @@ bool TestEkosCaptureHelper::setupEkosProfile(QString name, bool isPHD2) } -bool TestEkosCaptureHelper::startCapturing() +bool TestEkosCaptureHelper::startCapturing(bool checkCapturing) { // switch to the capture module KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->captureModule(), 1000)); @@ -158,8 +158,10 @@ bool TestEkosCaptureHelper::startCapturing() KWRAP_SUB(QVERIFY(getCaptureStatus() == Ekos::CAPTURE_IDLE || getCaptureStatus() == Ekos::CAPTURE_ABORTED || getCaptureStatus() == Ekos::CAPTURE_COMPLETE)); + // ensure at least one capture is started if requested + if (checkCapturing) + expectedCaptureStates.enqueue(Ekos::CAPTURE_CAPTURING); // press start - expectedCaptureStates.enqueue(Ekos::CAPTURE_CAPTURING); KTRY_GADGET_SUB(Ekos::Manager::Instance()->captureModule(), QPushButton, startB); KTRY_CLICK_SUB(Ekos::Manager::Instance()->captureModule(), startB); diff --git a/Tests/kstars_ui/test_ekos_capture_helper.h b/Tests/kstars_ui/test_ekos_capture_helper.h index 2cca09b19..d7d3aaa27 100644 --- a/Tests/kstars_ui/test_ekos_capture_helper.h +++ b/Tests/kstars_ui/test_ekos_capture_helper.h @@ -308,8 +308,9 @@ public: /** * @brief Helper function for start of capturing + * @param checkCapturing set to true if check of capturing should be included */ - bool startCapturing(); + bool startCapturing(bool checkCapturing = true); /** * @brief Helper function to stop capturing -- GitLab From 388a734d3432fbb6cee6a27c9f675c56cd4ba4ce Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Sun, 25 Oct 2020 18:54:51 +0100 Subject: [PATCH 06/16] Saving sequence queue made externally accessible --- kstars/ekos/capture/capture.h | 7 ++++++- kstars/org.kde.kstars.Ekos.Capture.xml | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/kstars/ekos/capture/capture.h b/kstars/ekos/capture/capture.h index e28f086f1..6ef105a09 100644 --- a/kstars/ekos/capture/capture.h +++ b/kstars/ekos/capture/capture.h @@ -175,6 +175,12 @@ class Capture : public QWidget, public Ui::Capture */ Q_SCRIPTABLE bool loadSequenceQueue(const QString &fileURL); + /** DBUS interface function. + * Saves the Sequence Queue to the Ekos Sequence Queue file. + * @param fileURL full URL of the filename + */ + Q_SCRIPTABLE bool saveSequenceQueue(const QString &path); + /** DBUS interface function. * Enables or disables the maximum guiding deviation and sets its value. * @param enable If true, enable the guiding deviation check, otherwise, disable it. @@ -814,7 +820,6 @@ class Capture : public QWidget, public Ui::Capture void syncGUIToJob(SequenceJob *job); bool processJobInfo(XMLEle *root); void processJobCompletion(); - bool saveSequenceQueue(const QString &path); void constructPrefix(QString &imagePrefix); double setCurrentADU(double value); void llsq(QVector x, QVector y, double &a, double &b); diff --git a/kstars/org.kde.kstars.Ekos.Capture.xml b/kstars/org.kde.kstars.Ekos.Capture.xml index 8f3c908b2..1f092a50e 100644 --- a/kstars/org.kde.kstars.Ekos.Capture.xml +++ b/kstars/org.kde.kstars.Ekos.Capture.xml @@ -41,6 +41,10 @@ + + + + -- GitLab From a363ac21e435426dc2bbb4f639654b4ac311d4a2 Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Sun, 25 Oct 2020 18:58:13 +0100 Subject: [PATCH 07/16] Scheduler made externally accessible --- kstars/ekos/manager.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kstars/ekos/manager.h b/kstars/ekos/manager.h index b1f6ab087..daaebcc0f 100644 --- a/kstars/ekos/manager.h +++ b/kstars/ekos/manager.h @@ -103,6 +103,10 @@ class Manager : public QDialog, public Ui::Manager } void addObjectToScheduler(SkyObject *object); + Scheduler *schedulerModule() + { + return schedulerProcess.get(); + } Guide *guideModule() { return guideProcess.get(); -- GitLab From e813885f505f6f34e7492adce19697e46e4bb66b Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Sun, 25 Oct 2020 21:39:56 +0100 Subject: [PATCH 08/16] External setting capture sequence file in the scheduler added --- kstars/ekos/scheduler/scheduler.cpp | 16 +++++++++++----- kstars/ekos/scheduler/scheduler.h | 10 +++++++++- kstars/org.kde.kstars.Ekos.Scheduler.xml | 5 +++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/kstars/ekos/scheduler/scheduler.cpp b/kstars/ekos/scheduler/scheduler.cpp index 36f274246..0965b7a28 100644 --- a/kstars/ekos/scheduler/scheduler.cpp +++ b/kstars/ekos/scheduler/scheduler.cpp @@ -551,13 +551,12 @@ void Scheduler::processFITSSelection() } } -void Scheduler::selectSequence() +void Scheduler::setSequence(const QString &sequenceFileURL) { - sequenceURL = - QFileDialog::getOpenFileUrl(this, i18n("Select Sequence Queue"), dirPath, i18n("Ekos Sequence Queue (*.esq)")); - if (sequenceURL.isEmpty()) - return; + sequenceURL = QUrl::fromLocalFile(sequenceFileURL); + if (sequenceFileURL.isEmpty()) + return; dirPath = QUrl(sequenceURL.url(QUrl::RemoveFilename)); sequenceEdit->setText(sequenceURL.toLocalFile()); @@ -574,6 +573,13 @@ void Scheduler::selectSequence() setDirty(); } +void Scheduler::selectSequence() +{ + QString file = QFileDialog::getOpenFileName(this, i18n("Select Sequence Queue"), dirPath.toLocalFile(), i18n("Ekos Sequence Queue (*.esq)")); + + setSequence(file); +} + void Scheduler::selectStartupScript() { startupScriptURL = QFileDialog::getOpenFileUrl(this, i18n("Select Startup Script"), dirPath, i18n("Script (*)")); diff --git a/kstars/ekos/scheduler/scheduler.h b/kstars/ekos/scheduler/scheduler.h index 6bd34646e..38f56fcad 100644 --- a/kstars/ekos/scheduler/scheduler.h +++ b/kstars/ekos/scheduler/scheduler.h @@ -210,6 +210,12 @@ class Scheduler : public QWidget, public Ui::Scheduler */ Q_SCRIPTABLE bool loadScheduler(const QString &fileURL); + /** DBUS interface function. + * @brief Set the file URL pointing to the capture sequence file + * @param sequenceFileURL URL of the capture sequence file + */ + Q_SCRIPTABLE void setSequence(const QString &sequenceFileURL); + /** DBUS interface function. * @brief Resets all jobs to IDLE */ @@ -244,10 +250,12 @@ class Scheduler : public QWidget, public Ui::Scheduler */ void setErrorHandlingStrategy (ErrorHandlingStrategy strategy); + /** @}*/ /** @{ */ - private: + +private: /** @internal Safeguard flag to avoid registering signals from widgets multiple times. */ bool jobChangesAreWatched { false }; diff --git a/kstars/org.kde.kstars.Ekos.Scheduler.xml b/kstars/org.kde.kstars.Ekos.Scheduler.xml index feb788bcc..5169a6f2c 100644 --- a/kstars/org.kde.kstars.Ekos.Scheduler.xml +++ b/kstars/org.kde.kstars.Ekos.Scheduler.xml @@ -17,6 +17,11 @@ + + + + + -- GitLab From 648e7d7f630c46fcecc22999cdfe2edcfdc37115 Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Tue, 27 Oct 2020 00:42:33 +0100 Subject: [PATCH 09/16] Capture counting test cases for tests for scheduler created --- Tests/kstars_ui/test_ekos_capture_count.cpp | 251 ++++++++++++++++---- Tests/kstars_ui/test_ekos_capture_count.h | 61 ++++- Tests/kstars_ui/test_ekos_capture_helper.h | 32 ++- 3 files changed, 297 insertions(+), 47 deletions(-) diff --git a/Tests/kstars_ui/test_ekos_capture_count.cpp b/Tests/kstars_ui/test_ekos_capture_count.cpp index 1427a7330..29d0c82fb 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.cpp +++ b/Tests/kstars_ui/test_ekos_capture_count.cpp @@ -14,6 +14,7 @@ #include "kstars_ui_tests.h" #include "test_ekos.h" +#include "Options.h" #include "test_ekos_capture_helper.h" @@ -24,33 +25,78 @@ TestEkosCaptureCount::TestEkosCaptureCount(QObject *parent) : QObject(parent) { void TestEkosCaptureCount::testCaptureWithCaptureFramesMap() { + // clean up capture module + Ekos::Manager::Instance()->captureModule()->clearSequenceQueue(); + KTRY_GADGET(Ekos::Manager::Instance()->captureModule(), QTableWidget, queueTable); + QTRY_VERIFY_WITH_TIMEOUT(queueTable->rowCount() == 0, 2000); // setup capture sequence, fill captured frames map and set expectations QVERIFY(prepareCapture()); // verify if at least one capture is expected + QVERIFY(executeCapturing()); +} + +void TestEkosCaptureCount::testSchedulerCapture() +{ + // prepare captured frames + QVERIFY(prepareScheduledCapture()); + + // save current capture sequence to Ekos sequence file + QString sequenceFile = destination->filePath("test.esq"); + qCInfo(KSTARS_EKOS_TEST) << "Sequence file" << sequenceFile << "created."; + QVERIFY(Ekos::Manager::Instance()->captureModule()->saveSequenceQueue(sequenceFile)); + + // setup scheduler + Ekos::Scheduler *scheduler = Ekos::Manager::Instance()->schedulerModule(); + KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(scheduler, 1000); + // set sequence file + scheduler->setSequence(sequenceFile); + // set Kocab as target + KTRY_SET_LINEEDIT(scheduler, nameEdit, schedulerTarget); + KTRY_SET_LINEEDIT(scheduler, raBox, "14 50 42"); + KTRY_SET_LINEEDIT(scheduler, decBox, "74 09 20"); + // disable all step checks + KTRY_SET_CHECKBOX(scheduler, trackStepCheck, false); + KTRY_SET_CHECKBOX(scheduler, focusStepCheck, false); + KTRY_SET_CHECKBOX(scheduler, alignStepCheck, false); + KTRY_SET_CHECKBOX(scheduler, guideStepCheck, false); + // ignore twilight + KTRY_SET_CHECKBOX(scheduler, twilightCheck, false); + // set remember job progress + Options::setRememberJobProgress(true); + // repeat the job for a fixed amount + KTRY_SET_RADIOBUTTON(scheduler, repeatCompletionR, true); + KTRY_SET_SPINBOX(scheduler, repeatsSpin, 1); + // add scheduler job + KTRY_CLICK(scheduler, addToQueueB); + + // start scheduler job + KTRY_CLICK(scheduler, startB); + + // ensure that the scheduler has started capturing + m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_CAPTURING); + QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.size() == 0, 10000); + + // calculate frame counts int framesCount = 0; for(int value: m_expectedImages.values()) framesCount += value; - // capture - QVERIFY(m_CaptureHelper->startCapturing(framesCount > 0)); - // wait for finish capturing QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_COMPLETE || m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_IDLE || m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_ABORTED, framesCount * 10000); // verify whether all frames are captured as expected - bool success = true; - for (QMap::iterator it = m_expectedImages.begin(); it != m_expectedImages.end(); ++it) - if (it.value() != 0) - { - QWARN(QString("Capture count for signature %1 does not match: %2 frames too %3 captured.").arg(it.key()).arg(abs(it.value())).arg(it.value() < 0 ? "much" : "few").toStdString().c_str()); - success = false; - } - QVERIFY2(success, "Capturing did not produce the expected amount of frames."); + QVERIFY2(checkCapturedFrames(), "Capturing did not produce the expected amount of frames."); } +/* ********************************************************************************* + * + * Test data + * + * ********************************************************************************* */ + void TestEkosCaptureCount::testCaptureWithCaptureFramesMap_data() { prepareTestData(1.0, "Red:2,Green:2,Blue:2", "Red:2,Green:1,Blue:2", "Green:1"); @@ -61,6 +107,17 @@ void TestEkosCaptureCount::testCaptureWithCaptureFramesMap_data() prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:5,Red:1,Green:1", "Blue:1"); } +void TestEkosCaptureCount::testSchedulerCapture_data() +{ + prepareTestData(1.0, "Red:2,Green:2,Blue:2", "Red:2,Green:1,Blue:2", "Green:1"); +} + +/* ********************************************************************************* + * + * Test infrastructure + * + * ********************************************************************************* */ + void TestEkosCaptureCount::initTestCase() { KVERIFY_EKOS_IS_HIDDEN(); @@ -69,7 +126,10 @@ void TestEkosCaptureCount::initTestCase() // start the profile QVERIFY(startEkosProfile()); m_CaptureHelper->initTestCase(); - destination = new QTemporaryDir(); + QStandardPaths::setTestModeEnabled(true); + QFileInfo test_dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation), "test"); + destination = new QTemporaryDir(test_dir.absolutePath()); + destination->setAutoRemove(true); } void TestEkosCaptureCount::cleanupTestCase() @@ -78,6 +138,8 @@ void TestEkosCaptureCount::cleanupTestCase() QVERIFY(shutdownEkosProfile()); KTRY_CLOSE_EKOS(); KVERIFY_EKOS_IS_HIDDEN(); + destination->remove(); + delete destination; } void TestEkosCaptureCount::init() @@ -89,10 +151,6 @@ void TestEkosCaptureCount::cleanup() { QVERIFY(m_CaptureHelper->stopCapturing()); - // clean up capture module - Ekos::Manager::Instance()->captureModule()->clearSequenceQueue(); - KTRY_GADGET(Ekos::Manager::Instance()->captureModule(), QTableWidget, queueTable); - QTRY_VERIFY_WITH_TIMEOUT(queueTable->rowCount() == 0, 2000); disconnect(Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::captureComplete, this, &TestEkosCaptureCount::captureComplete); // clean up expected images @@ -100,6 +158,43 @@ void TestEkosCaptureCount::cleanup() } +/* ********************************************************************************* + * + * Helper functions + * + * ********************************************************************************* */ + +bool TestEkosCaptureCount::checkCapturedFrames() +{ + bool success = true; + for (QMap::iterator it = m_expectedImages.begin(); it != m_expectedImages.end(); ++it) + if (it.value() != 0) + { + QWARN(QString("Capture count for signature %1 does not match: %2 frames too %3 captured.").arg(it.key()).arg(abs(it.value())).arg(it.value() < 0 ? "much" : "few").toStdString().c_str()); + success = false; + } + + return success; +} + +bool TestEkosCaptureCount::executeCapturing() +{ + int framesCount = 0; + for(int value: m_expectedImages.values()) + framesCount += value; + + // capture + KWRAP_SUB(QVERIFY(m_CaptureHelper->startCapturing(framesCount > 0))); + + // wait for finish capturing + KWRAP_SUB(QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_COMPLETE || m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_IDLE || + m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_ABORTED, framesCount * 10000)); + + // verify whether all frames are captured as expected + KWRAP_SUB(QVERIFY2(checkCapturedFrames(), "Capturing did not produce the expected amount of frames.")); + return true; +} + bool TestEkosCaptureCount::startEkosProfile() { Ekos::Manager * const ekos = Ekos::Manager::Instance(); @@ -137,23 +232,94 @@ bool TestEkosCaptureCount::prepareCapture() KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000)); // create the destination for images - KWRAP_SUB(QVERIFY(destination->isValid())); - KWRAP_SUB(QVERIFY(destination->autoRemove())); + KVERIFY_SUB(destination->isValid()); + KVERIFY_SUB(destination->autoRemove()); qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << destination->path(); // create capture sequences + KVERIFY_SUB(fillCaptureSequences(sequence, exptime, destination->path())); + + // fill the captured frames map that hold the numbers of already taken frames + KVERIFY_SUB(fillCapturedFramesMap(capturedFramesMap)); + + // fill the map of expected frames + KVERIFY_SUB(setExpectedFrames(expectedFrames)); + + // everything successfully completed + return true; +} + +bool TestEkosCaptureCount::prepareScheduledCapture() +{ + QFETCH(double, exptime); + QFETCH(QString, sequence); + QFETCH(QString, capturedFramesMap); + QFETCH(QString, expectedFrames); + + // switch to capture module + Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule(); + KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000)); + + // create the destination for images + KVERIFY_SUB(destination->isValid()); + KVERIFY_SUB(destination->autoRemove()); + qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << destination->path(); + + // step 1: create the frames due to the captured frames map + KVERIFY_SUB(fillCaptureSequences(capturedFramesMap, exptime, destination->filePath(schedulerTarget))); + KVERIFY_SUB(fillCapturedFramesMap("")); + KVERIFY_SUB(setExpectedFrames(capturedFramesMap)); + + // create the expected frames + KVERIFY_SUB(executeCapturing()); + + // clean up + capture->clearSequenceQueue(); + + // step 2: create the frames due to the captured frames map + KVERIFY_SUB(fillCaptureSequences(sequence, exptime, destination->path())); + KVERIFY_SUB(fillCapturedFramesMap("")); + KVERIFY_SUB(setExpectedFrames(expectedFrames)); + + // everything successfully completed + return true; +} + +void TestEkosCaptureCount::prepareTestData(double exptime, QString sequence, QString capturedFramesMap, QString expectedFrames) +{ + + QTest::addColumn("exptime"); /*!< exposure time */ + QTest::addColumn("sequence"); /*!< list of filters */ + QTest::addColumn("capturedFramesMap"); /*!< list of frame counts */ + QTest::addColumn("expectedFrames"); /*!< list of frames per filter that are expected */ + + QTest::newRow(QString("seq=%1x, existing=%2").arg(sequence).arg(capturedFramesMap).toStdString().c_str()) << exptime << sequence << capturedFramesMap << expectedFrames; + } + +QString TestEkosCaptureCount::calculateSignature(QString filter) +{ + return destination->path() + "/Light/" + filter + "/Light"; +} + +bool TestEkosCaptureCount::fillCaptureSequences(QString sequence, double exptime, QString fitsDirectory) +{ for (QString value : sequence.split(",")) { KVERIFY_SUB(value.indexOf(":") > -1); QString filter = value.left(value.indexOf(":")); int count = value.right(value.length()-value.indexOf(":")-1).toInt(); - KTRY_SET_CHECKBOX_SUB(capture, fileTimestampS, true); - KWRAP_SUB(KTRY_CAPTURE_ADD_LIGHT(exptime, count, 0, filter, destination->path())); + KTRY_SET_CHECKBOX_SUB(Ekos::Manager::Instance()->captureModule(), fileTimestampS, true); + KTRY_SET_LINEEDIT_SUB(Ekos::Manager::Instance()->captureModule(), filePrefixT, schedulerTarget); + KWRAP_SUB(KTRY_CAPTURE_ADD_LIGHT(exptime, count, 0, filter, fitsDirectory)); // ensure that no old values are present - capture->setCapturedFramesMap(calculateSignature(filter), 0); + Ekos::Manager::Instance()->captureModule()->setCapturedFramesMap(calculateSignature(filter), 0); } - // fill the captured frames map that hold the numbers of already taken frames + return true; +} + +bool TestEkosCaptureCount::fillCapturedFramesMap(QString capturedFramesMap) +{ if (capturedFramesMap != "") { for (QString value : capturedFramesMap.split(",")) @@ -161,11 +327,15 @@ bool TestEkosCaptureCount::prepareCapture() KVERIFY_SUB(value.indexOf(":") > -1); QString filter = value.left(value.indexOf(":")); int count = value.right(value.length()-value.indexOf(":")-1).toInt(); - capture->setCapturedFramesMap(calculateSignature(filter), count); + Ekos::Manager::Instance()->captureModule()->setCapturedFramesMap(calculateSignature(filter), count); } } - // fill the map of expected frames + return true; +} + +bool TestEkosCaptureCount::setExpectedFrames(QString expectedFrames) +{ if (expectedFrames != "") { for (QString value : expectedFrames.split(",")) @@ -176,32 +346,31 @@ bool TestEkosCaptureCount::prepareCapture() m_expectedImages.insert(filter, count); } } + else + m_expectedImages.clear(); - // everything successfully completed return true; } - -void TestEkosCaptureCount::prepareTestData(double exptime, QString sequence, QString capturedFramesMap, QString expectedFrames) -{ - - QTest::addColumn("exptime"); /*!< exposure time */ - QTest::addColumn("sequence"); /*!< list of filters */ - QTest::addColumn("capturedFramesMap"); /*!< list of frame counts */ - QTest::addColumn("expectedFrames"); /*!< list of frames per filter that are expected */ - - QTest::newRow(QString("seq=%1x, existing=%2").arg(sequence).arg(capturedFramesMap).toStdString().c_str()) << exptime << sequence << capturedFramesMap << expectedFrames; - } +/* ********************************************************************************* + * + * Slots + * + * ********************************************************************************* */ void TestEkosCaptureCount::captureComplete(const QString &filename, double exposureSeconds, const QString &filter, double hfr) { + Q_UNUSED(filename); + Q_UNUSED(exposureSeconds); + Q_UNUSED(hfr); + // reduce the for the job's signature the number of expected images m_expectedImages.insert(filter, m_expectedImages.value(filter, 0) - 1); } -QString TestEkosCaptureCount::calculateSignature(QString filter) -{ - return destination->path() + "/Light/" + filter + "/Light"; -} - +/* ********************************************************************************* + * + * Main function + * + * ********************************************************************************* */ QTEST_KSTARS_MAIN(TestEkosCaptureCount) diff --git a/Tests/kstars_ui/test_ekos_capture_count.h b/Tests/kstars_ui/test_ekos_capture_count.h index 168a93999..9b89a9843 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.h +++ b/Tests/kstars_ui/test_ekos_capture_count.h @@ -46,9 +46,22 @@ protected: /** * @brief Setup capturing + * @return true iff preparation was successful */ bool prepareCapture(); + /** + * @brief Setup capturing for tests with the scheduler + * @return true iff preparation was successful + */ + bool prepareScheduledCapture(); + + /** + * @brief Execute capturing + * @return true iff exactly the expected frames have been taken + */ + bool executeCapturing(); + /** * @brief Helper function translating simple QString input into QTest test data rows * @param exptime exposure time of the sequence @@ -73,8 +86,6 @@ protected slots: void init(); void cleanup(); - QString calculateSignature(QString filter); - private: // current capture status Ekos::CaptureState m_CaptureStatus; @@ -82,6 +93,45 @@ private: // helper class TestEkosCaptureHelper *m_CaptureHelper = nullptr; + QString schedulerTarget = "test"; + + /** + * @brief Fill the capture sequences in the Capture GUI + * @param expectedFrames comma separated list of : + * @param exptime exposure time + * @param fitsDirectory directory where the captures will be placed + * @return true if everything was successful + */ + bool fillCaptureSequences(QString sequence, double exptime, QString fitsDirectory); + + /** + * @brief Fill the map of frames that have already been captured + * @param expectedFrames comma separated list of : + * @return true if everything was successful + */ + bool fillCapturedFramesMap(QString capturedFramesMap); + + /** + * @brief Fill the map of frames that are expected to be captured + * @param expectedFrames comma separated list of : + * @return true if everything was successful + */ + bool setExpectedFrames(QString expectedFrames); + + + /** + * @brief Check if the expected number of frames are captured + * @return true if yes + */ + bool checkCapturedFrames(); + + /** + * @brief calculateSignature Calculate the signature of a given filter + * @param filter filter name + * @return signature + */ + QString calculateSignature(QString filter); + private slots: /** * @brief Test whether the capture module produces exactly the diff between the capture frames map and the defined frame counts. @@ -91,6 +141,13 @@ private slots: /** @brief Test data for @see testCaptureWithCaptureFramesMap() */ void testCaptureWithCaptureFramesMap_data(); + /** + * @brief Test of appropriate captures controlled by the scheduler + */ + void testSchedulerCapture(); + + /** @brief Test data for @see testSchedulerCapture() */ + void testSchedulerCapture_data(); }; #endif // HAVE_INDI diff --git a/Tests/kstars_ui/test_ekos_capture_helper.h b/Tests/kstars_ui/test_ekos_capture_helper.h index d7d3aaa27..50c39b381 100644 --- a/Tests/kstars_ui/test_ekos_capture_helper.h +++ b/Tests/kstars_ui/test_ekos_capture_helper.h @@ -122,7 +122,7 @@ do {\ * @warning Fails the test if the button is not currently enabled. */ #define KTRY_CLICK(module, button) do { \ - QTimer::singleShot(100, Ekos::Manager::Instance(), []() { \ + QTimer::singleShot(100, Ekos::Manager::Instance(), [&]() { \ KTRY_GADGET(module, QPushButton, button); \ QVERIFY2(button->isEnabled(), QString("QPushButton '%1' is disabled and cannot be clicked").arg(#button).toStdString().c_str()); \ QTest::mouseClick(button, Qt::LeftButton); }); \ @@ -141,27 +141,51 @@ do {\ QTest::mouseClick(button, Qt::LeftButton); success = true;}); \ KTRY_VERIFY_WITH_TIMEOUT_SUB(success, 1000);} while(false) -/** @brief Helper to set a checkbox and verify whether it succeeded (subroutine version) +/** @brief Helper to set a checkbox and verify whether it succeeded + * @param module KStars module that holds the checkox + * @param checkbox object name of the checkbox + * @param value value the checkbox should be set + */ +#define KTRY_SET_CHECKBOX(module, checkbox, value) \ + KTRY_GADGET(module, QCheckBox, checkbox); checkbox->setChecked(value); QVERIFY(checkbox->isChecked() == value) + +/** @brief Subroutine version of @see KTRY_SET_CHECKBOX * @param module KStars module that holds the checkox * @param checkbox object name of the checkbox * @param value value the checkbox should be set */ #define KTRY_SET_CHECKBOX_SUB(module, checkbox, value) \ - KWRAP_SUB(KTRY_GADGET(module, QCheckBox, checkbox); checkbox->setChecked(value); QVERIFY(checkbox->isChecked() == value)) + KWRAP_SUB(KTRY_SET_CHECKBOX(module, checkbox, value)) /** @brief Helper to set a radiobutton and verify whether it succeeded * @param module KStars module that holds the radiobutton * @param checkbox object name of the radiobutton * @param value value the radiobutton should be set */ +#define KTRY_SET_RADIOBUTTON(module, radiobutton, value) \ + KTRY_GADGET(module, QRadioButton, radiobutton); radiobutton->setChecked(value); QVERIFY(radiobutton->isChecked() == value) + +/** @brief Subroutine version of @see KTRY_SET_RADIOBUTTON + * @param module KStars module that holds the radiobutton + * @param checkbox object name of the radiobutton + * @param value value the radiobutton should be set + */ #define KTRY_SET_RADIOBUTTON_SUB(module, radiobutton, value) \ - KWRAP_SUB(KTRY_GADGET(module, QRadioButton, radiobutton); radiobutton->setChecked(value); QVERIFY(radiobutton->isChecked() == value)) + KWRAP_SUB(KTRY_SET_RADIOBUTTON(module, radiobutton, value)) /** @brief Helper to set a spinbox and verify whether it succeeded * @param module KStars module that holds the spinbox * @param spinbox object name of the spinbox * @param value value the spinbox should be set */ +#define KTRY_SET_SPINBOX(module, spinbox, x) \ + KTRY_GADGET(module, QSpinBox, spinbox); spinbox->setValue(x); QVERIFY(spinbox->value() == x) + +/** @brief Subroutine version of @see KTRY_SET_SPINBOX + * @param module KStars module that holds the spinbox + * @param spinbox object name of the spinbox + * @param value value the spinbox should be set + */ #define KTRY_SET_SPINBOX_SUB(module, spinbox, x) \ KWRAP_SUB(KTRY_GADGET(module, QSpinBox, spinbox); spinbox->setValue(x); QVERIFY(spinbox->value() == x)) -- GitLab From 6d331eec9ccc494a4e19c087d796721782654e08 Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Wed, 28 Oct 2020 00:09:22 +0100 Subject: [PATCH 10/16] removeAllJobs() added to the scheduler to improve testability --- kstars/ekos/scheduler/scheduler.cpp | 21 +++++++++++++-------- kstars/ekos/scheduler/scheduler.h | 5 +++++ kstars/org.kde.kstars.Ekos.Scheduler.xml | 3 +++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/kstars/ekos/scheduler/scheduler.cpp b/kstars/ekos/scheduler/scheduler.cpp index 0965b7a28..d2e3851c5 100644 --- a/kstars/ekos/scheduler/scheduler.cpp +++ b/kstars/ekos/scheduler/scheduler.cpp @@ -3726,6 +3726,18 @@ void Scheduler::load() startJobEvaluation(); } +void Scheduler::removeAllJobs() +{ + if (jobUnderEdit >= 0) + resetJobEdit(); + + while (queueTable->rowCount() > 0) + queueTable->removeRow(0); + + qDeleteAll(jobs); + jobs.clear(); +} + bool Scheduler::loadScheduler(const QString &fileURL) { SchedulerState const old_state = state; @@ -3742,14 +3754,7 @@ bool Scheduler::loadScheduler(const QString &fileURL) return false; } - if (jobUnderEdit >= 0) - resetJobEdit(); - - while (queueTable->rowCount() > 0) - queueTable->removeRow(0); - - qDeleteAll(jobs); - jobs.clear(); + removeAllJobs(); LilXML *xmlParser = newLilXML(); char errmsg[MAXRBUF]; diff --git a/kstars/ekos/scheduler/scheduler.h b/kstars/ekos/scheduler/scheduler.h index 38f56fcad..912e0a4ba 100644 --- a/kstars/ekos/scheduler/scheduler.h +++ b/kstars/ekos/scheduler/scheduler.h @@ -203,6 +203,11 @@ class Scheduler : public QWidget, public Ui::Scheduler */ Q_SCRIPTABLE Q_NOREPLY void stop(); + /** DBUS interface function. + * @brief Remove all scheduler jobs + */ + Q_SCRIPTABLE Q_NOREPLY void removeAllJobs(); + /** DBUS interface function. * @brief Loads the Ekos Scheduler List (.esl) file. * @param fileURL path to a file diff --git a/kstars/org.kde.kstars.Ekos.Scheduler.xml b/kstars/org.kde.kstars.Ekos.Scheduler.xml index 5169a6f2c..621f0dfd5 100644 --- a/kstars/org.kde.kstars.Ekos.Scheduler.xml +++ b/kstars/org.kde.kstars.Ekos.Scheduler.xml @@ -12,6 +12,9 @@ + + + -- GitLab From 95034b2049624211d14ddf200687f7c9f64e1b45 Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Wed, 28 Oct 2020 00:11:47 +0100 Subject: [PATCH 11/16] Scheduler test case for LLLRGBLL added showing a counting bug in the scheduler --- Tests/kstars_ui/test_ekos_capture_count.cpp | 93 ++++++++++++++++----- Tests/kstars_ui/test_ekos_capture_count.h | 24 ++++++ 2 files changed, 94 insertions(+), 23 deletions(-) diff --git a/Tests/kstars_ui/test_ekos_capture_count.cpp b/Tests/kstars_ui/test_ekos_capture_count.cpp index 29d0c82fb..9517a4458 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.cpp +++ b/Tests/kstars_ui/test_ekos_capture_count.cpp @@ -37,6 +37,14 @@ void TestEkosCaptureCount::testCaptureWithCaptureFramesMap() QVERIFY(executeCapturing()); } +void TestEkosCaptureCount::cleanupScheduler() +{ + Ekos::Manager::Instance()->schedulerModule()->stop(); + QTest::qWait(5000); + // remove jobs + Ekos::Manager::Instance()->schedulerModule()->removeAllJobs(); +} + void TestEkosCaptureCount::testSchedulerCapture() { // prepare captured frames @@ -65,6 +73,8 @@ void TestEkosCaptureCount::testSchedulerCapture() KTRY_SET_CHECKBOX(scheduler, twilightCheck, false); // set remember job progress Options::setRememberJobProgress(true); + // disable INDI stopping after scheduler finished + Options::setStopEkosAfterShutdown(false); // repeat the job for a fixed amount KTRY_SET_RADIOBUTTON(scheduler, repeatCompletionR, true); KTRY_SET_SPINBOX(scheduler, repeatsSpin, 1); @@ -73,19 +83,15 @@ void TestEkosCaptureCount::testSchedulerCapture() // start scheduler job KTRY_CLICK(scheduler, startB); + // expect a idle signal at the end + expectedSchedulerStates.enqueue(Ekos::SCHEDULER_IDLE); // ensure that the scheduler has started capturing m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_CAPTURING); QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.size() == 0, 10000); - // calculate frame counts - int framesCount = 0; - for(int value: m_expectedImages.values()) - framesCount += value; - // wait for finish capturing - QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_COMPLETE || m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_IDLE || - m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_ABORTED, framesCount * 10000); + KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(expectedSchedulerStates, 60000); // verify whether all frames are captured as expected QVERIFY2(checkCapturedFrames(), "Capturing did not produce the expected amount of frames."); @@ -104,12 +110,13 @@ void TestEkosCaptureCount::testCaptureWithCaptureFramesMap_data() prepareTestData(1.0, "Red:1,Green:1,Red:1", "", "Red:2,Green:1"); prepareTestData(1.0, "Red:2,Green:2,Red:2", "Red:2,Green:2", "Red:2"); prepareTestData(1.0, "Red:2,Green:2,Red:1", "Red:3,Green:1", "Green:1"); - prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:5,Red:1,Green:1", "Blue:1"); + prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:5,Green:1,Blue:1", "Red:1"); } void TestEkosCaptureCount::testSchedulerCapture_data() { prepareTestData(1.0, "Red:2,Green:2,Blue:2", "Red:2,Green:1,Blue:2", "Green:1"); + prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:5,Green:1,Blue:1", "Red:1"); } /* ********************************************************************************* @@ -129,7 +136,8 @@ void TestEkosCaptureCount::initTestCase() QStandardPaths::setTestModeEnabled(true); QFileInfo test_dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation), "test"); destination = new QTemporaryDir(test_dir.absolutePath()); - destination->setAutoRemove(true); + QVERIFY(destination->isValid()); + QVERIFY(destination->autoRemove()); } void TestEkosCaptureCount::cleanupTestCase() @@ -138,6 +146,8 @@ void TestEkosCaptureCount::cleanupTestCase() QVERIFY(shutdownEkosProfile()); KTRY_CLOSE_EKOS(); KVERIFY_EKOS_IS_HIDDEN(); + + // remove destination directory destination->remove(); delete destination; } @@ -145,16 +155,27 @@ void TestEkosCaptureCount::cleanupTestCase() void TestEkosCaptureCount::init() { connect(Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::captureComplete, this, &TestEkosCaptureCount::captureComplete); + connect(Ekos::Manager::Instance()->schedulerModule(), &Ekos::Scheduler::newStatus, this, &TestEkosCaptureCount::schedulerStateChanged); + QStandardPaths::setTestModeEnabled(true); + // clear image directory + QVERIFY(getImageLocation()->removeRecursively()); } void TestEkosCaptureCount::cleanup() { QVERIFY(m_CaptureHelper->stopCapturing()); + disconnect(Ekos::Manager::Instance()->schedulerModule(), &Ekos::Scheduler::newStatus, this, &TestEkosCaptureCount::schedulerStateChanged); disconnect(Ekos::Manager::Instance()->captureModule(), &Ekos::Capture::captureComplete, this, &TestEkosCaptureCount::captureComplete); + // clean up capture page + Ekos::Manager::Instance()->captureModule()->clearSequenceQueue(); + // clean up expected images m_expectedImages.clear(); + + // cleanup scheduler + cleanupScheduler(); } @@ -179,6 +200,8 @@ bool TestEkosCaptureCount::checkCapturedFrames() bool TestEkosCaptureCount::executeCapturing() { + + // calculate frame counts int framesCount = 0; for(int value: m_expectedImages.values()) framesCount += value; @@ -186,12 +209,19 @@ bool TestEkosCaptureCount::executeCapturing() // capture KWRAP_SUB(QVERIFY(m_CaptureHelper->startCapturing(framesCount > 0))); + // expect receiving a new CAPTURE_COMPLETE signal + if (framesCount > 0) + m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_COMPLETE); + // wait for finish capturing - KWRAP_SUB(QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_COMPLETE || m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_IDLE || - m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_ABORTED, framesCount * 10000)); + // ensure that the scheduler has started capturing + KWRAP_SUB(QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.size() == 0, framesCount * 10000)); // verify whether all frames are captured as expected KWRAP_SUB(QVERIFY2(checkCapturedFrames(), "Capturing did not produce the expected amount of frames.")); + + // wait for shutdown + QTest::qWait(5000); return true; } @@ -205,7 +235,7 @@ bool TestEkosCaptureCount::startEkosProfile() // start the profile KTRY_EKOS_CLICK(processINDIB); // wait for the devices to come up - QTest::qWait(15000); + QTest::qWait(10000); // Everything completed successfully return true; @@ -232,12 +262,10 @@ bool TestEkosCaptureCount::prepareCapture() KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000)); // create the destination for images - KVERIFY_SUB(destination->isValid()); - KVERIFY_SUB(destination->autoRemove()); - qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << destination->path(); + qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << getImageLocation()->path(); // create capture sequences - KVERIFY_SUB(fillCaptureSequences(sequence, exptime, destination->path())); + KVERIFY_SUB(fillCaptureSequences(sequence, exptime, getImageLocation()->path())); // fill the captured frames map that hold the numbers of already taken frames KVERIFY_SUB(fillCapturedFramesMap(capturedFramesMap)); @@ -261,12 +289,10 @@ bool TestEkosCaptureCount::prepareScheduledCapture() KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000)); // create the destination for images - KVERIFY_SUB(destination->isValid()); - KVERIFY_SUB(destination->autoRemove()); - qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << destination->path(); + qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << getImageLocation()->path(); // step 1: create the frames due to the captured frames map - KVERIFY_SUB(fillCaptureSequences(capturedFramesMap, exptime, destination->filePath(schedulerTarget))); + KVERIFY_SUB(fillCaptureSequences(capturedFramesMap, exptime, getImageLocation()->filePath(schedulerTarget))); KVERIFY_SUB(fillCapturedFramesMap("")); KVERIFY_SUB(setExpectedFrames(capturedFramesMap)); @@ -277,7 +303,7 @@ bool TestEkosCaptureCount::prepareScheduledCapture() capture->clearSequenceQueue(); // step 2: create the frames due to the captured frames map - KVERIFY_SUB(fillCaptureSequences(sequence, exptime, destination->path())); + KVERIFY_SUB(fillCaptureSequences(sequence, exptime, getImageLocation()->path())); KVERIFY_SUB(fillCapturedFramesMap("")); KVERIFY_SUB(setExpectedFrames(expectedFrames)); @@ -296,9 +322,20 @@ void TestEkosCaptureCount::prepareTestData(double exptime, QString sequence, QSt QTest::newRow(QString("seq=%1x, existing=%2").arg(sequence).arg(capturedFramesMap).toStdString().c_str()) << exptime << sequence << capturedFramesMap << expectedFrames; } +QDir *TestEkosCaptureCount::getImageLocation() +{ + if (imageLocation == nullptr || imageLocation->exists()) + imageLocation = new QDir(destination->path() + "/images"); + + return imageLocation; +} + QString TestEkosCaptureCount::calculateSignature(QString filter) { - return destination->path() + "/Light/" + filter + "/Light"; + if (schedulerTarget == "") + return getImageLocation()->path() + "/Light/" + filter + "/Light"; + else + return getImageLocation()->path() + "/" + schedulerTarget + "/Light/" + filter + "/" + schedulerTarget + "_Light"; } bool TestEkosCaptureCount::fillCaptureSequences(QString sequence, double exptime, QString fitsDirectory) @@ -310,7 +347,8 @@ bool TestEkosCaptureCount::fillCaptureSequences(QString sequence, double exptime int count = value.right(value.length()-value.indexOf(":")-1).toInt(); KTRY_SET_CHECKBOX_SUB(Ekos::Manager::Instance()->captureModule(), fileTimestampS, true); KTRY_SET_LINEEDIT_SUB(Ekos::Manager::Instance()->captureModule(), filePrefixT, schedulerTarget); - KWRAP_SUB(KTRY_CAPTURE_ADD_LIGHT(exptime, count, 0, filter, fitsDirectory)); + if (count > 0) + KWRAP_SUB(KTRY_CAPTURE_ADD_LIGHT(exptime, count, 0, filter, fitsDirectory)); // ensure that no old values are present Ekos::Manager::Instance()->captureModule()->setCapturedFramesMap(calculateSignature(filter), 0); } @@ -367,6 +405,15 @@ void TestEkosCaptureCount::captureComplete(const QString &filename, double expos m_expectedImages.insert(filter, m_expectedImages.value(filter, 0) - 1); } +void TestEkosCaptureCount::schedulerStateChanged(Ekos::SchedulerState status) +{ + m_SchedulerStatus = status; + // check if the new state is the next one expected, then remove it from the stack + if (!expectedSchedulerStates.isEmpty() && expectedSchedulerStates.head() == status) + expectedSchedulerStates.dequeue(); + +} + /* ********************************************************************************* * * Main function diff --git a/Tests/kstars_ui/test_ekos_capture_count.h b/Tests/kstars_ui/test_ekos_capture_count.h index 9b89a9843..48b04e2a0 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.h +++ b/Tests/kstars_ui/test_ekos_capture_count.h @@ -33,6 +33,7 @@ protected: // destination where images will be located QTemporaryDir *destination; + QDir *imageLocation = nullptr; /** * @brief Start a test EKOS profile. @@ -56,6 +57,11 @@ protected: */ bool prepareScheduledCapture(); + /** + * @brief Stop and clean up scheduler + */ + void cleanupScheduler(); + /** * @brief Execute capturing * @return true iff exactly the expected frames have been taken @@ -78,6 +84,22 @@ protected: */ void captureComplete(const QString &filename, double exposureSeconds, const QString &filter, double hfr); + // sequence of scheduler states that are expected + QQueue expectedSchedulerStates; + /** + * @brief Slot to receive a new scheduler state + * @param status new capture status + */ + void schedulerStateChanged(Ekos::SchedulerState status); + + // current scheduler status + Ekos::SchedulerState m_SchedulerStatus; + + /** + * @brief Retrieve the current capture status. + */ + inline Ekos::SchedulerState getSchedulerStatus() {return m_SchedulerStatus;} + protected slots: void initTestCase(); @@ -132,6 +154,8 @@ private: */ QString calculateSignature(QString filter); + QDir *getImageLocation(); + private slots: /** * @brief Test whether the capture module produces exactly the diff between the capture frames map and the defined frame counts. -- GitLab From e90ced6fd90f7fa87bc7101f414aef989cc463a4 Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Wed, 28 Oct 2020 10:18:03 +0100 Subject: [PATCH 12/16] Capture count test cases corrected simulating scheduler behavior when not using it --- Tests/kstars_ui/test_ekos_capture_count.cpp | 19 +++++++++++-------- Tests/kstars_ui/test_ekos_capture_count.h | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Tests/kstars_ui/test_ekos_capture_count.cpp b/Tests/kstars_ui/test_ekos_capture_count.cpp index 9517a4458..d27596021 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.cpp +++ b/Tests/kstars_ui/test_ekos_capture_count.cpp @@ -61,7 +61,7 @@ void TestEkosCaptureCount::testSchedulerCapture() // set sequence file scheduler->setSequence(sequenceFile); // set Kocab as target - KTRY_SET_LINEEDIT(scheduler, nameEdit, schedulerTarget); + KTRY_SET_LINEEDIT(scheduler, nameEdit, target); KTRY_SET_LINEEDIT(scheduler, raBox, "14 50 42"); KTRY_SET_LINEEDIT(scheduler, decBox, "74 09 20"); // disable all step checks @@ -215,7 +215,7 @@ bool TestEkosCaptureCount::executeCapturing() // wait for finish capturing // ensure that the scheduler has started capturing - KWRAP_SUB(QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.size() == 0, framesCount * 10000)); + KWRAP_SUB(QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.size() == 0, 120000)); // verify whether all frames are captured as expected KWRAP_SUB(QVERIFY2(checkCapturedFrames(), "Capturing did not produce the expected amount of frames.")); @@ -261,11 +261,14 @@ bool TestEkosCaptureCount::prepareCapture() Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule(); KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(capture, 1000)); + // add target to path to emulate the behavior of the scheduler + QString imagepath = getImageLocation()->path() + "/" + target; + // create the destination for images - qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << getImageLocation()->path(); + qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << imagepath; // create capture sequences - KVERIFY_SUB(fillCaptureSequences(sequence, exptime, getImageLocation()->path())); + KVERIFY_SUB(fillCaptureSequences(sequence, exptime, imagepath)); // fill the captured frames map that hold the numbers of already taken frames KVERIFY_SUB(fillCapturedFramesMap(capturedFramesMap)); @@ -292,7 +295,7 @@ bool TestEkosCaptureCount::prepareScheduledCapture() qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << getImageLocation()->path(); // step 1: create the frames due to the captured frames map - KVERIFY_SUB(fillCaptureSequences(capturedFramesMap, exptime, getImageLocation()->filePath(schedulerTarget))); + KVERIFY_SUB(fillCaptureSequences(capturedFramesMap, exptime, getImageLocation()->filePath(target))); KVERIFY_SUB(fillCapturedFramesMap("")); KVERIFY_SUB(setExpectedFrames(capturedFramesMap)); @@ -332,10 +335,10 @@ QDir *TestEkosCaptureCount::getImageLocation() QString TestEkosCaptureCount::calculateSignature(QString filter) { - if (schedulerTarget == "") + if (target == "") return getImageLocation()->path() + "/Light/" + filter + "/Light"; else - return getImageLocation()->path() + "/" + schedulerTarget + "/Light/" + filter + "/" + schedulerTarget + "_Light"; + return getImageLocation()->path() + "/" + target + "/Light/" + filter + "/" + target + "_Light"; } bool TestEkosCaptureCount::fillCaptureSequences(QString sequence, double exptime, QString fitsDirectory) @@ -346,7 +349,7 @@ bool TestEkosCaptureCount::fillCaptureSequences(QString sequence, double exptime QString filter = value.left(value.indexOf(":")); int count = value.right(value.length()-value.indexOf(":")-1).toInt(); KTRY_SET_CHECKBOX_SUB(Ekos::Manager::Instance()->captureModule(), fileTimestampS, true); - KTRY_SET_LINEEDIT_SUB(Ekos::Manager::Instance()->captureModule(), filePrefixT, schedulerTarget); + KTRY_SET_LINEEDIT_SUB(Ekos::Manager::Instance()->captureModule(), filePrefixT, target); if (count > 0) KWRAP_SUB(KTRY_CAPTURE_ADD_LIGHT(exptime, count, 0, filter, fitsDirectory)); // ensure that no old values are present diff --git a/Tests/kstars_ui/test_ekos_capture_count.h b/Tests/kstars_ui/test_ekos_capture_count.h index 48b04e2a0..1c8dd6e0d 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.h +++ b/Tests/kstars_ui/test_ekos_capture_count.h @@ -115,7 +115,7 @@ private: // helper class TestEkosCaptureHelper *m_CaptureHelper = nullptr; - QString schedulerTarget = "test"; + QString target = "test"; /** * @brief Fill the capture sequences in the Capture GUI -- GitLab From 30472fb715af83398f65ffee7b991061281a06fc Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Thu, 29 Oct 2020 05:28:02 +0100 Subject: [PATCH 13/16] Test cases for capture counting harmonized - tests with and without the scheduler use the same test data sets - new test data added showing bugs using the scheduler for 3 x LLLRGBLL --- Tests/kstars_ui/test_ekos_capture_count.cpp | 112 +++++++++++--------- Tests/kstars_ui/test_ekos_capture_count.h | 3 +- Tests/kstars_ui/test_ekos_capture_helper.h | 2 +- 3 files changed, 64 insertions(+), 53 deletions(-) diff --git a/Tests/kstars_ui/test_ekos_capture_count.cpp b/Tests/kstars_ui/test_ekos_capture_count.cpp index d27596021..2f7aeebf4 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.cpp +++ b/Tests/kstars_ui/test_ekos_capture_count.cpp @@ -50,39 +50,8 @@ void TestEkosCaptureCount::testSchedulerCapture() // prepare captured frames QVERIFY(prepareScheduledCapture()); - // save current capture sequence to Ekos sequence file - QString sequenceFile = destination->filePath("test.esq"); - qCInfo(KSTARS_EKOS_TEST) << "Sequence file" << sequenceFile << "created."; - QVERIFY(Ekos::Manager::Instance()->captureModule()->saveSequenceQueue(sequenceFile)); - - // setup scheduler - Ekos::Scheduler *scheduler = Ekos::Manager::Instance()->schedulerModule(); - KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(scheduler, 1000); - // set sequence file - scheduler->setSequence(sequenceFile); - // set Kocab as target - KTRY_SET_LINEEDIT(scheduler, nameEdit, target); - KTRY_SET_LINEEDIT(scheduler, raBox, "14 50 42"); - KTRY_SET_LINEEDIT(scheduler, decBox, "74 09 20"); - // disable all step checks - KTRY_SET_CHECKBOX(scheduler, trackStepCheck, false); - KTRY_SET_CHECKBOX(scheduler, focusStepCheck, false); - KTRY_SET_CHECKBOX(scheduler, alignStepCheck, false); - KTRY_SET_CHECKBOX(scheduler, guideStepCheck, false); - // ignore twilight - KTRY_SET_CHECKBOX(scheduler, twilightCheck, false); - // set remember job progress - Options::setRememberJobProgress(true); - // disable INDI stopping after scheduler finished - Options::setStopEkosAfterShutdown(false); - // repeat the job for a fixed amount - KTRY_SET_RADIOBUTTON(scheduler, repeatCompletionR, true); - KTRY_SET_SPINBOX(scheduler, repeatsSpin, 1); - // add scheduler job - KTRY_CLICK(scheduler, addToQueueB); - // start scheduler job - KTRY_CLICK(scheduler, startB); + KTRY_CLICK(Ekos::Manager::Instance()->schedulerModule(), startB); // expect a idle signal at the end expectedSchedulerStates.enqueue(Ekos::SCHEDULER_IDLE); @@ -91,7 +60,7 @@ void TestEkosCaptureCount::testSchedulerCapture() QTRY_VERIFY_WITH_TIMEOUT(m_CaptureHelper->expectedCaptureStates.size() == 0, 10000); // wait for finish capturing - KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(expectedSchedulerStates, 60000); + KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT(expectedSchedulerStates, 120000); // verify whether all frames are captured as expected QVERIFY2(checkCapturedFrames(), "Capturing did not produce the expected amount of frames."); @@ -103,20 +72,20 @@ void TestEkosCaptureCount::testSchedulerCapture() * * ********************************************************************************* */ -void TestEkosCaptureCount::testCaptureWithCaptureFramesMap_data() +void TestEkosCaptureCount::testSchedulerCapture_data() { prepareTestData(1.0, "Red:2,Green:2,Blue:2", "Red:2,Green:1,Blue:2", "Green:1"); - prepareTestData(1.0, "Red:2,Green:2,Blue:2", "Red:2,Green:2,Blue:2", ""); - prepareTestData(1.0, "Red:1,Green:1,Red:1", "", "Red:2,Green:1"); - prepareTestData(1.0, "Red:2,Green:2,Red:2", "Red:2,Green:2", "Red:2"); - prepareTestData(1.0, "Red:2,Green:2,Red:1", "Red:3,Green:1", "Green:1"); + prepareTestData(1.0, "Red:1,Green:1,Blue:1", "Red:3,Green:1,Blue:3", "Green:2", 3); prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:5,Green:1,Blue:1", "Red:1"); + prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "", "Luminance:10,Red:2,Green:2,Blue:2", 2); + prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:15,Red:2,Green:3,Blue:3", "Red:1", 3); + prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:15,Red:3,Green:3,Blue:2", "Blue:1", 3); } -void TestEkosCaptureCount::testSchedulerCapture_data() +void TestEkosCaptureCount::testCaptureWithCaptureFramesMap_data() { - prepareTestData(1.0, "Red:2,Green:2,Blue:2", "Red:2,Green:1,Blue:2", "Green:1"); - prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:5,Green:1,Blue:1", "Red:1"); + // use the same test set + testSchedulerCapture_data(); } /* ********************************************************************************* @@ -256,6 +225,7 @@ bool TestEkosCaptureCount::prepareCapture() QFETCH(QString, sequence); QFETCH(QString, capturedFramesMap); QFETCH(QString, expectedFrames); + QFETCH(int, iterations); // switch to capture module Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule(); @@ -268,7 +238,8 @@ bool TestEkosCaptureCount::prepareCapture() qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << imagepath; // create capture sequences - KVERIFY_SUB(fillCaptureSequences(sequence, exptime, imagepath)); + for (int i = 0; i < iterations; i++) + KVERIFY_SUB(fillCaptureSequences(sequence, exptime, imagepath)); // fill the captured frames map that hold the numbers of already taken frames KVERIFY_SUB(fillCapturedFramesMap(capturedFramesMap)); @@ -286,6 +257,7 @@ bool TestEkosCaptureCount::prepareScheduledCapture() QFETCH(QString, sequence); QFETCH(QString, capturedFramesMap); QFETCH(QString, expectedFrames); + QFETCH(int, iterations); // switch to capture module Ekos::Capture *capture = Ekos::Manager::Instance()->captureModule(); @@ -295,34 +267,69 @@ bool TestEkosCaptureCount::prepareScheduledCapture() qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << getImageLocation()->path(); // step 1: create the frames due to the captured frames map - KVERIFY_SUB(fillCaptureSequences(capturedFramesMap, exptime, getImageLocation()->filePath(target))); - KVERIFY_SUB(fillCapturedFramesMap("")); - KVERIFY_SUB(setExpectedFrames(capturedFramesMap)); + if (capturedFramesMap != "") + { + KVERIFY_SUB(fillCaptureSequences(capturedFramesMap, exptime, getImageLocation()->filePath(target))); + KVERIFY_SUB(fillCapturedFramesMap("")); + KVERIFY_SUB(setExpectedFrames(capturedFramesMap)); - // create the expected frames - KVERIFY_SUB(executeCapturing()); + // create the expected frames + KVERIFY_SUB(executeCapturing()); - // clean up - capture->clearSequenceQueue(); + // clean up + capture->clearSequenceQueue(); + } // step 2: create the frames due to the captured frames map KVERIFY_SUB(fillCaptureSequences(sequence, exptime, getImageLocation()->path())); KVERIFY_SUB(fillCapturedFramesMap("")); KVERIFY_SUB(setExpectedFrames(expectedFrames)); + // save current capture sequence to Ekos sequence file + QString sequenceFile = destination->filePath("test.esq"); + qCInfo(KSTARS_EKOS_TEST) << "Sequence file" << sequenceFile << "created."; + KVERIFY_SUB(Ekos::Manager::Instance()->captureModule()->saveSequenceQueue(sequenceFile)); + + // setup scheduler + Ekos::Scheduler *scheduler = Ekos::Manager::Instance()->schedulerModule(); + KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(scheduler, 1000)); + // set sequence file + scheduler->setSequence(sequenceFile); + // set Kocab as target + KTRY_SET_LINEEDIT_SUB(scheduler, nameEdit, target); + KTRY_SET_LINEEDIT_SUB(scheduler, raBox, "14 50 42"); + KTRY_SET_LINEEDIT_SUB(scheduler, decBox, "74 09 20"); + // disable all step checks + KTRY_SET_CHECKBOX_SUB(scheduler, trackStepCheck, false); + KTRY_SET_CHECKBOX_SUB(scheduler, focusStepCheck, false); + KTRY_SET_CHECKBOX_SUB(scheduler, alignStepCheck, false); + KTRY_SET_CHECKBOX_SUB(scheduler, guideStepCheck, false); + // ignore twilight + KTRY_SET_CHECKBOX_SUB(scheduler, twilightCheck, false); + // set remember job progress + Options::setRememberJobProgress(true); + // disable INDI stopping after scheduler finished + Options::setStopEkosAfterShutdown(false); + // repeat the job for a fixed amount + KTRY_SET_RADIOBUTTON_SUB(scheduler, repeatCompletionR, true); + KTRY_SET_SPINBOX_SUB(scheduler, repeatsSpin, iterations); + // add scheduler job + KTRY_CLICK_SUB(scheduler, addToQueueB); // everything successfully completed return true; } -void TestEkosCaptureCount::prepareTestData(double exptime, QString sequence, QString capturedFramesMap, QString expectedFrames) +void TestEkosCaptureCount::prepareTestData(double exptime, QString sequence, QString capturedFramesMap, QString expectedFrames, int iterations) { QTest::addColumn("exptime"); /*!< exposure time */ QTest::addColumn("sequence"); /*!< list of filters */ QTest::addColumn("capturedFramesMap"); /*!< list of frame counts */ QTest::addColumn("expectedFrames"); /*!< list of frames per filter that are expected */ + QTest::addColumn("iterations"); /*!< how often should the sequence be repeated */ - QTest::newRow(QString("seq=%1x, existing=%2").arg(sequence).arg(capturedFramesMap).toStdString().c_str()) << exptime << sequence << capturedFramesMap << expectedFrames; + QTest::newRow(QString("seq=%1x, ex=%2, it=%3").arg(sequence).arg(capturedFramesMap).arg(iterations).toStdString().c_str()) + << exptime << sequence << capturedFramesMap << expectedFrames << iterations; } QDir *TestEkosCaptureCount::getImageLocation() @@ -343,6 +350,9 @@ QString TestEkosCaptureCount::calculateSignature(QString filter) bool TestEkosCaptureCount::fillCaptureSequences(QString sequence, double exptime, QString fitsDirectory) { + if (sequence == "") + return true; + for (QString value : sequence.split(",")) { KVERIFY_SUB(value.indexOf(":") > -1); diff --git a/Tests/kstars_ui/test_ekos_capture_count.h b/Tests/kstars_ui/test_ekos_capture_count.h index 1c8dd6e0d..54c116623 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.h +++ b/Tests/kstars_ui/test_ekos_capture_count.h @@ -74,8 +74,9 @@ protected: * @param sequence filter and count as QString("::: m_expectedImages; diff --git a/Tests/kstars_ui/test_ekos_capture_helper.h b/Tests/kstars_ui/test_ekos_capture_helper.h index 50c39b381..07d6bd626 100644 --- a/Tests/kstars_ui/test_ekos_capture_helper.h +++ b/Tests/kstars_ui/test_ekos_capture_helper.h @@ -187,7 +187,7 @@ do {\ * @param value value the spinbox should be set */ #define KTRY_SET_SPINBOX_SUB(module, spinbox, x) \ - KWRAP_SUB(KTRY_GADGET(module, QSpinBox, spinbox); spinbox->setValue(x); QVERIFY(spinbox->value() == x)) + KWRAP_SUB(KTRY_SET_SPINBOX(module, spinbox, x)) /** @brief Helper to set a doublespinbox and verify whether it succeeded * @param module KStars module that holds the spinbox -- GitLab From 8961d4d5809f25a409a6961b5335248b0e0a986a Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Fri, 30 Oct 2020 08:25:59 +0100 Subject: [PATCH 14/16] Capture counting optimization avoiding twilight warning and display of images --- Tests/kstars_ui/test_ekos_capture_count.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tests/kstars_ui/test_ekos_capture_count.cpp b/Tests/kstars_ui/test_ekos_capture_count.cpp index 2f7aeebf4..e59235bda 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.cpp +++ b/Tests/kstars_ui/test_ekos_capture_count.cpp @@ -107,6 +107,10 @@ void TestEkosCaptureCount::initTestCase() destination = new QTemporaryDir(test_dir.absolutePath()); QVERIFY(destination->isValid()); QVERIFY(destination->autoRemove()); + // do not show images + Options::setUseFITSViewer(false); + // disable twilight warning + KMessageBox::saveDontShowAgainYesNo("astronomical_twilight_warning", KMessageBox::ButtonCode::No); } void TestEkosCaptureCount::cleanupTestCase() -- GitLab From 4684f9fc41fef753f0af4457661908cd26b6da5f Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Mon, 2 Nov 2020 20:00:57 +0100 Subject: [PATCH 15/16] Verification of counts display in the scheduler job table --- Tests/kstars_ui/test_ekos_capture_count.cpp | 33 +++++++++++++++++++++ Tests/kstars_ui/test_ekos_capture_count.h | 6 ++++ 2 files changed, 39 insertions(+) diff --git a/Tests/kstars_ui/test_ekos_capture_count.cpp b/Tests/kstars_ui/test_ekos_capture_count.cpp index e59235bda..80fe376f7 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.cpp +++ b/Tests/kstars_ui/test_ekos_capture_count.cpp @@ -75,9 +75,12 @@ void TestEkosCaptureCount::testSchedulerCapture() void TestEkosCaptureCount::testSchedulerCapture_data() { prepareTestData(1.0, "Red:2,Green:2,Blue:2", "Red:2,Green:1,Blue:2", "Green:1"); + prepareTestData(1.0, "Red:1,Red:1,Green:1,Green:1,Blue:1,Blue:1", "Red:2,Green:1,Blue:2", "Green:1"); + prepareTestData(1.0, "Red:1,Green:1,Blue:1,Red:1,Green:1,Blue:1", "Red:2,Green:1,Blue:2", "Green:1"); prepareTestData(1.0, "Red:1,Green:1,Blue:1", "Red:3,Green:1,Blue:3", "Green:2", 3); prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:5,Green:1,Blue:1", "Red:1"); prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "", "Luminance:10,Red:2,Green:2,Blue:2", 2); + prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:15,Red:1,Green:2,Blue:2", "Red:1", 2); prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:15,Red:2,Green:3,Blue:3", "Red:1", 3); prepareTestData(1.0, "Luminance:3,Red:1,Green:1,Blue:1,Luminance:2", "Luminance:15,Red:3,Green:3,Blue:2", "Blue:1", 3); } @@ -319,6 +322,24 @@ bool TestEkosCaptureCount::prepareScheduledCapture() KTRY_SET_SPINBOX_SUB(scheduler, repeatsSpin, iterations); // add scheduler job KTRY_CLICK_SUB(scheduler, addToQueueB); + KTRY_GADGET_SUB(scheduler, QTableWidget, queueTable); + KVERIFY_SUB(queueTable->rowCount() == 1); + KVERIFY_SUB(queueTable->columnCount() > 3); + + // verify the displayed capture counts + QString displayedCounts = queueTable->item(0, 2)->text(); + KVERIFY2_SUB(displayedCounts.indexOf("/") > 0, "Scheduler job table does not display in style captured/total."); + // check display of already captured + int captured = displayedCounts.left(displayedCounts.indexOf("/")).toInt(); + int captured_expected = totalCount(capturedFramesMap); + KVERIFY2_SUB(captured == captured_expected, + QString("Scheduler job table shows %1 captured frames instead of %2.").arg(captured).arg(captured_expected).toStdString().c_str()); + // check display of expected frames + int total = displayedCounts.right(displayedCounts.length() - displayedCounts.indexOf("/") - 1).toInt(); + int total_expected = totalCount(sequence) * iterations; + KVERIFY2_SUB(total == total_expected, + QString("Scheduler job table shows %1 expected frames instead of %2.").arg(total).arg(total_expected).toStdString().c_str()); + // everything successfully completed return true; } @@ -406,6 +427,18 @@ bool TestEkosCaptureCount::setExpectedFrames(QString expectedFrames) return true; } + +int TestEkosCaptureCount::totalCount(QString sequence) +{ + if (sequence == "") + return 0; + + int total = 0; + for (QString value : sequence.split(",")) + total += value.right(value.length()-value.indexOf(":")-1).toInt(); + + return total; +} /* ********************************************************************************* * * Slots diff --git a/Tests/kstars_ui/test_ekos_capture_count.h b/Tests/kstars_ui/test_ekos_capture_count.h index 54c116623..563f11d45 100644 --- a/Tests/kstars_ui/test_ekos_capture_count.h +++ b/Tests/kstars_ui/test_ekos_capture_count.h @@ -134,6 +134,12 @@ private: */ bool fillCapturedFramesMap(QString capturedFramesMap); + /** + * @brief Determine the total count from a comma separated sequence of : + * @return sum of + */ + int totalCount(QString sequence); + /** * @brief Fill the map of frames that are expected to be captured * @param expectedFrames comma separated list of : -- GitLab From 142385f5469545114f9556645f9b7e3c70e61325 Mon Sep 17 00:00:00 2001 From: Wolfgang Reissenberger Date: Tue, 3 Nov 2020 19:54:44 +0100 Subject: [PATCH 16/16] Bugfix for capture counting handling multiple occurences in capture sequences --- kstars/ekos/scheduler/scheduler.cpp | 92 +++++++++++++++-------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/kstars/ekos/scheduler/scheduler.cpp b/kstars/ekos/scheduler/scheduler.cpp index d2e3851c5..2b4f3d7b9 100644 --- a/kstars/ekos/scheduler/scheduler.cpp +++ b/kstars/ekos/scheduler/scheduler.cpp @@ -4951,13 +4951,41 @@ bool Scheduler::estimateJobTime(SchedulerJob *schedJob) SchedulerJob::CapturedFramesMap capture_map; bool const rememberJobProgress = Options::rememberJobProgress(); - int totalSequenceCount = 0, totalCompletedCount = 0; + int totalCompletedCount = 0; double totalImagingTime = 0; // Determine number of captures in the scheduler job int capturesPerRepeat = 0; + QMap expected; foreach (SequenceJob *seqJob, seqJobs) + { capturesPerRepeat += seqJob->getCount(); + QString const signature = seqJob->getSignature(); + expected[signature] = seqJob->getCount() + (expected.contains(signature) ? expected[signature] : 0); + } + + // fill the captured frames map + for (QString key: expected.keys()) + { + if (rememberJobProgress) + { + int diff = expected[key] * schedJob->getRepeatsRequired() - capturedFramesCount[key]; + // captured more than required? + if (diff <= 0) + capture_map[key] = expected[key]; + // need more frames than one cycle could capture? + else if (diff >= expected[key]) + capture_map[key] = 0; + // else we know that 0 < diff < expected[key] + else + capture_map[key] = expected[key] - diff; + } + else + capture_map[key] = 0; + + // collect all captured frames counts + totalCompletedCount += capturedFramesCount[key]; + } // Loop through sequence jobs to calculate the number of required frames and estimate duration. foreach (SequenceJob *seqJob, seqJobs) @@ -4976,10 +5004,12 @@ bool Scheduler::estimateJobTime(SchedulerJob *schedJob) } // Note that looping jobs will have zero repeats required. - int const captures_required = seqJob->getCount() * schedJob->getRepeatsRequired(); + QString const signature = seqJob->getSignature(); + QString const signature_path = QFileInfo(signature).path(); + int captures_required = seqJob->getCount(); + int captures_completed = capturedFramesCount[signature]; - int captures_completed = 0; - if (rememberJobProgress) + if (rememberJobProgress && schedJob->getCompletionCondition() != SchedulerJob::FINISH_LOOP) { /* Enumerate sequence jobs associated to this scheduler job, and assign them a completed count. * @@ -5009,13 +5039,11 @@ bool Scheduler::estimateJobTime(SchedulerJob *schedJob) * This is why it is important to manage the repeat count of the scheduler job, as stated earlier. */ - // Retrieve cached count of completed captures for the output folder of this seqJob - QString const signature = seqJob->getSignature(); - QString const signature_path = QFileInfo(signature).path(); - captures_completed = capturedFramesCount[signature]; + // we start with the total value + captures_required = expected[seqJob->getSignature()] * schedJob->getRepeatsRequired(); qCInfo(KSTARS_EKOS_SCHEDULER) << QString("%1 sees %2 captures in output folder '%3'.").arg(seqName).arg( - captures_completed).arg(signature_path); + captures_completed).arg(QFileInfo(signature).path()); // Enumerate sequence jobs to check how many captures are completed overall in the same storage as the current one foreach (SequenceJob *prevSeqJob, seqJobs) @@ -5025,50 +5053,27 @@ bool Scheduler::estimateJobTime(SchedulerJob *schedJob) break; // If the previous sequence signature matches the current, reduce completion count to take duplicates into account - if (!signature.compare(prevSeqJob->getLocalDir() + prevSeqJob->getDirectoryPostfix())) + if (!signature.compare(prevSeqJob->getSignature())) { // Note that looping jobs will have zero repeats required. - int const previous_captures_required = prevSeqJob->getCount() * schedJob->getRepeatsRequired(); + int const previous_captures_required = prevSeqJob->getCount(); qCInfo(KSTARS_EKOS_SCHEDULER) << QString("%1 has a previous duplicate sequence job requiring %2 captures.").arg( seqName).arg(previous_captures_required); - captures_completed -= previous_captures_required; + captures_required -= previous_captures_required; } - // Now completed count can be needlessly negative for this job, so clamp to zero - if (captures_completed < 0) - captures_completed = 0; + // Now required count can be needlessly negative for this job, so clamp to zero + if (captures_required < 0) + captures_required = 0; - // And break if no captures remain, this job has to execute - if (captures_completed == 0) + // And break if no captures remain, this job does not need to be executed + if (captures_required == 0) break; } - // Finally we're only interested in the number of captures required for this sequence item - if (0 < captures_required && captures_required < captures_completed) - captures_completed = captures_required; - qCInfo(KSTARS_EKOS_SCHEDULER) << QString("%1 has completed %2/%3 of its required captures in output folder '%4'.").arg( seqName).arg(captures_completed).arg(captures_required).arg(signature_path); - // Update the completion count for this signature in the frame map if we still have captures to take. - // That frame map will be transferred to the Capture module, for which the sequence is a single batch of the scheduler job. - // For instance, consider a scheduler job repeated 3 times and using a 3xLum sequence, so we want 9xLum in the end. - // - If no captures are already processed, the frame map contains Lum=0 - // - If 1xLum are already processed, the frame map contains Lum=0 when the batch executes, so that 3xLum may be taken. - // - If 3xLum are already processed, the frame map contains Lum=0 when the batch executes, as we still need more than what the sequence provides. - // - If 7xLum are already processed, the frame map contains Lum=1 when the batch executes, because we now only need 2xLum to finish the job. - // Therefore we need to specify a number of existing captures only for the last batch of the scheduler job. - // In the last batch, we only need the remainder of frames to get to the required total. - if (captures_completed < captures_required) - { - if (captures_required - captures_completed < seqJob->getCount()) - capture_map[signature] = captures_completed % seqJob->getCount(); - else - capture_map[signature] = 0; - } - else capture_map[signature] = captures_required; - - // From now on, 'captures_completed' is the number of frames completed for the *current* sequence job } // Else rely on the captures done during this session else @@ -5094,9 +5099,6 @@ bool Scheduler::estimateJobTime(SchedulerJob *schedJob) qCInfo(KSTARS_EKOS_SCHEDULER) << QString("%1 captures calibration frames.").arg(seqName); } - totalSequenceCount += captures_required; - totalCompletedCount += captures_completed; - /* If captures are not complete, we have imaging time left */ if (!areJobCapturesComplete) { @@ -5126,7 +5128,7 @@ bool Scheduler::estimateJobTime(SchedulerJob *schedJob) } schedJob->setCapturedFramesMap(capture_map); - schedJob->setSequenceCount(totalSequenceCount); + schedJob->setSequenceCount(capturesPerRepeat * schedJob->getRepeatsRequired()); // only in case we remember the job progress, we change the completion count if (rememberJobProgress) @@ -5176,7 +5178,7 @@ bool Scheduler::estimateJobTime(SchedulerJob *schedJob) schedJob->setEstimatedTime(0); qCDebug(KSTARS_EKOS_SCHEDULER) << QString("Job '%1' will not run, complete with %2/%3 captures.") - .arg(schedJob->getName()).arg(totalCompletedCount).arg(totalSequenceCount); + .arg(schedJob->getName()).arg(schedJob->getCompletedCount()).arg(schedJob->getSequenceCount()); } // Else consolidate with step durations else -- GitLab