diff --git a/Tests/kstars_ui/test_ekos.cpp b/Tests/kstars_ui/test_ekos.cpp --- a/Tests/kstars_ui/test_ekos.cpp +++ b/Tests/kstars_ui/test_ekos.cpp @@ -82,69 +82,78 @@ profileCBox->setCurrentText(p); QTRY_COMPARE(profileCBox->currentText(), p); - // --------- Second step: starting Ekos with the Simulators profile + for (int loop = 0; loop < 20; loop++) + { + // --------- Second step: starting Ekos with the Simulators profile - QString const buttonReadyToStart("media-playback-start"); - QString const buttonReadyToStop("media-playback-stop"); + QString const buttonReadyToStart("media-playback-start"); + QString const buttonReadyToStop("media-playback-stop"); - // Check the start button icon as visual feedback about Ekos state, and click to start Ekos - QPushButton* startEkos = ekos->findChild("processINDIB"); - QVERIFY(startEkos != nullptr); - QVERIFY(!buttonReadyToStart.compare(startEkos->icon().name())); - QTest::mouseClick(startEkos, Qt::LeftButton); + // Check the start button icon as visual feedback about Ekos state, and click to start Ekos + QPushButton* startEkos = ekos->findChild("processINDIB"); + QVERIFY(startEkos != nullptr); + QVERIFY(!buttonReadyToStart.compare(startEkos->icon().name())); + QTest::mouseClick(startEkos, Qt::LeftButton); - // --------- Third step: waiting for Ekos to finish startup + // --------- Third step: waiting for Ekos to finish startup - // The INDI property pages automatically raised on top, but as we got a handle to the button, we continue to test as is + // The INDI property pages automatically raised on top, but as we got a handle to the button, we continue to test as is - // Wait until Ekos gives feedback on the INDI client startup - button changes to symbol "stop" - QTRY_VERIFY_WITH_TIMEOUT(!buttonReadyToStop.compare(startEkos->icon().name()), 7000); + // Wait until Ekos gives feedback on the INDI client startup - button changes to symbol "stop" + QTRY_VERIFY_WITH_TIMEOUT(!buttonReadyToStop.compare(startEkos->icon().name()), 7000); - // It might be that our Simulators profile is not auto-connecting and we should do that manually - // We assume that by default it's not the case + // It might be that our Simulators profile is not auto-connecting and we should do that manually + // We assume that by default it's not the case - // We have no feedback on whether all our devices are connected, so we need to hack a delay in... - QWARN("HACK HACK HACK adding delay here for devices to connect"); - QTest::qWait(5000); +#if 0 + // We have no feedback on whether all our devices are connected, so we need to hack a delay in... + QWARN("HACK HACK HACK adding delay here for devices to connect"); + QTest::qWait(5000); +#else + QWARN("HACK HACK HACK using DBus Ekos status instead of UI hint to determine whether devices are connected"); + QTRY_VERIFY_WITH_TIMEOUT(Ekos::CommunicationStatus::Success == ekos->indiStatus(), 5000); + QTRY_VERIFY_WITH_TIMEOUT(Ekos::CommunicationStatus::Success == ekos->ekosStatus(), 5000); +#endif - // Verify the device connection button is unavailable - QPushButton * connectDevices = ekos->findChild("connectB"); - QVERIFY(connectDevices != nullptr); - QVERIFY(!connectDevices->isEnabled()); + // Verify the device connection button is unavailable + QPushButton * connectDevices = ekos->findChild("connectB"); + QVERIFY(connectDevices != nullptr); + QVERIFY(!connectDevices->isEnabled()); #if QT_VERSION >= 0x050800 - QEXPECT_FAIL("", "Ekos resets the simulation clock when starting a profile.", Continue); - QCOMPARE(llround(KStars::Instance()->data()->clock()->utc().toLocalTime().toMSecsSinceEpoch()/1000.0), TestKStarsStartup::m_InitialConditions.dateTime.toSecsSinceEpoch()); + QEXPECT_FAIL("", "Ekos resets the simulation clock when starting a profile.", Continue); + QCOMPARE(llround(KStars::Instance()->data()->clock()->utc().toLocalTime().toMSecsSinceEpoch()/1000.0), TestKStarsStartup::m_InitialConditions.dateTime.toSecsSinceEpoch()); #endif - QEXPECT_FAIL("", "Ekos resumes the simulation clock when starting a profile.", Continue); - QVERIFY(!KStars::Instance()->data()->clock()->isActive()); + QEXPECT_FAIL("", "Ekos resumes the simulation clock when starting a profile.", Continue); + QVERIFY(!KStars::Instance()->data()->clock()->isActive()); - // --------- Fourth step: waiting for Ekos to finish stopping + // --------- Fourth step: waiting for Ekos to finish stopping - // Start button that became a stop button is now disabled - we need to disconnect devices first - QVERIFY(!startEkos->isEnabled()); + // Start button that became a stop button is now disabled - we need to disconnect devices first + QVERIFY(!startEkos->isEnabled()); - // Verify the device disconnection button is available, disconnect devices - QPushButton * const b = ekos->findChild("disconnectB"); - QVERIFY(b != nullptr); - QVERIFY(b->isEnabled()); - QTimer::singleShot(200, Ekos::Manager::Instance(), [&] - { - QTest::mouseClick(b, Qt::LeftButton); - }); + // Verify the device disconnection button is available, disconnect devices + QPushButton * const b = ekos->findChild("disconnectB"); + QVERIFY(b != nullptr); + QVERIFY(b->isEnabled()); + QTimer::singleShot(10, Ekos::Manager::Instance(), [&] + { + QTest::mouseClick(b, Qt::LeftButton); + }); - // --------- Fifth step: waiting for Ekos to finish stopping + // --------- Fifth step: waiting for Ekos to finish stopping - // Start button that became a stop button has to be available - QTRY_VERIFY_WITH_TIMEOUT(startEkos->isEnabled(), 10000); + // Start button that became a stop button has to be available + QTRY_VERIFY_WITH_TIMEOUT(startEkos->isEnabled(), 10000); - // Hang INDI client up - QTimer::singleShot(200, ekos, [&] - { - QTest::mouseClick(startEkos, Qt::LeftButton); - }); - QTRY_VERIFY_WITH_TIMEOUT(!buttonReadyToStart.compare(startEkos->icon().name()), 10000); + // Hang INDI client up + QTimer::singleShot(10, ekos, [&] + { + QTest::mouseClick(startEkos, Qt::LeftButton); + }); + QTRY_VERIFY_WITH_TIMEOUT(!buttonReadyToStart.compare(startEkos->icon().name()), 10000); + } } void TestEkos::testManipulateProfiles() diff --git a/Tests/kstars_ui/test_ekos_focus.h b/Tests/kstars_ui/test_ekos_focus.h --- a/Tests/kstars_ui/test_ekos_focus.h +++ b/Tests/kstars_ui/test_ekos_focus.h @@ -87,16 +87,28 @@ * @param fieldout is the upper radius of the annulus filtering stars. * @warning Fails the test if detection, algorithm, full-field checkbox or annulus fields cannot be used. */ -#define KTRY_FOCUS_CONFIGURE(detection, algorithm, fieldin, fieldout) do { \ +#define KTRY_FOCUS_CONFIGURE(detection, algorithm, fieldin, fieldout, autostar, start_ticks) do { \ KTRY_FOCUS_GADGET(QCheckBox, useFullField); \ useFullField->setCheckState(fieldin < fieldout ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); \ KTRY_FOCUS_GADGET(QDoubleSpinBox, fullFieldInnerRing); \ fullFieldInnerRing->setValue(fieldin); \ KTRY_FOCUS_GADGET(QDoubleSpinBox, fullFieldOuterRing); \ fullFieldOuterRing->setValue(fieldout); \ + KTRY_FOCUS_GADGET(QCheckBox, useAutoStar); \ + useAutoStar->setCheckState(autostar? Qt::CheckState::Checked : Qt::CheckState::Unchecked); \ + KTRY_FOCUS_GADGET(QSpinBox, absTicksSpin); \ + absTicksSpin->setValue(start_ticks); \ + KTRY_FOCUS_CLICK(startGotoB); \ + KTRY_FOCUS_GADGET(QLineEdit, absTicksLabel); \ + QTRY_VERIFY_WITH_TIMEOUT(absTicksLabel->text().toInt() == (start_ticks), 5000); \ KTRY_FOCUS_COMBO_SET(focusDetectionCombo, detection); \ KTRY_FOCUS_COMBO_SET(focusAlgorithmCombo, algorithm); } while (false) +#define KTRY_FOCUS_MECHANICS(initial_step, max_travel, max_step) do { \ + KTRY_FOCUS_GADGET(QSpinBox, stepIN); stepIN->setValue(initial_step); \ + KTRY_FOCUS_GADGET(QSpinBox, maxTravelIN); maxTravelIN->setValue(max_travel); \ + KTRY_FOCUS_GADGET(QSpinBox, maxSingleStepIN); maxSingleStepIN->setValue(max_step); } while(false); + class TestEkosFocus : public QObject { Q_OBJECT @@ -113,6 +125,8 @@ void testStarDetection_data(); void testStarDetection(); + void testAutoFocus_data(); + void testAutoFocus(); }; #endif // HAVE_INDI diff --git a/Tests/kstars_ui/test_ekos_focus.cpp b/Tests/kstars_ui/test_ekos_focus.cpp --- a/Tests/kstars_ui/test_ekos_focus.cpp +++ b/Tests/kstars_ui/test_ekos_focus.cpp @@ -49,6 +49,124 @@ } +void TestEkosFocus::testAutoFocus_data() +{ +#if QT_VERSION < 0x050900 + QSKIP("Skipping fixture-based test on old QT version."); +#else + QTest::addColumn("NAME"); + QTest::addColumn("RA"); + QTest::addColumn("DEC"); + + // Altitude computation taken from SchedulerJob::findAltitude + GeoLocation * const geo = KStarsData::Instance()->geo(); + KStarsDateTime const now(KStarsData::Instance()->lt()); + KSNumbers const numbers(now.djd()); + CachingDms const LST = geo->GSTtoLST(geo->LTtoUT(now).gst()); + + std::list Objects = { "Mizar", "M 13", "Dubhe" }; + size_t count = 0; + + foreach (char const *name, Objects) + { + SkyObject const * const so = KStars::Instance()->data()->objectNamed(name); + if (so != nullptr) + { + SkyObject o(*so); + o.updateCoordsNow(&numbers); + o.EquatorialToHorizontal(&LST, geo->lat()); + if (10.0 < o.alt().Degrees()) + { + QTest::addRow("%s", name) + << name + << o.ra().toHMSString() + << o.dec().toDMSString(); + count++; + } + else QWARN(QString("Fixture '%1' altitude is '%2' degrees, discarding.").arg(name).arg(so->alt().Degrees()).toStdString().c_str()); + } + } + + if (!count) + QSKIP("No usable fixture objects, bypassing test."); +#endif +} + +void TestEkosFocus::testAutoFocus() +{ +#if QT_VERSION < 0x050900 + QSKIP("Skipping fixture-based test on old QT version."); +#else + Ekos::Manager * const ekos = Ekos::Manager::Instance(); + + QFETCH(QString, NAME); + QFETCH(QString, RA); + QFETCH(QString, DEC); + qDebug("Test focusing on '%s' RA '%s' DEC '%s'", + NAME.toStdString().c_str(), + RA.toStdString().c_str(), + DEC.toStdString().c_str()); + + // Just sync to RA/DEC to make the mount teleport to the object + QWARN("During this test, the mount is not tracking - we leave it as is for the feature in the CCD simulator to trigger a failure."); + QTRY_VERIFY_WITH_TIMEOUT(ekos->mountModule() != nullptr, 5000); + QVERIFY(ekos->mountModule()->sync(RA, DEC)); + + // Wait for Focus to come up, switch to Focus tab + QTRY_VERIFY_WITH_TIMEOUT(ekos->focusModule() != nullptr, 5000); + KTRY_EKOS_GADGET(QTabWidget, toolsWidget); + toolsWidget->setCurrentWidget(ekos->focusModule()); + QTRY_COMPARE_WITH_TIMEOUT(toolsWidget->currentWidget(), ekos->focusModule(), 1000); + + QWARN("The Focus capture button toggles after Ekos is started, leave a bit of time for it to settle."); + QTest::qWait(500); + + KTRY_FOCUS_MECHANICS(5000, 100000, 100000); + + // Run the focus procedure for SEP + { + KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, false, 50000); + KTRY_FOCUS_DETECT(0.1, 1); + KTRY_FOCUS_CLICK(startGotoB); + KTRY_FOCUS_GADGET(QPushButton, startFocusB); + KTRY_EKOS_CLICK(startFocusB); + QTRY_VERIFY_WITH_TIMEOUT(!startFocusB->isEnabled(), 2000); + QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 20000); + } + + // Run the focus procedure for Centroid + { + KTRY_FOCUS_CONFIGURE("Centroid", "Iterative", 0.0, 100.0, false, 50000); + KTRY_FOCUS_DETECT(0.1, 1); + KTRY_FOCUS_CLICK(startGotoB); + KTRY_FOCUS_GADGET(QPushButton, startFocusB); + KTRY_EKOS_CLICK(startFocusB); + QTRY_VERIFY_WITH_TIMEOUT(!startFocusB->isEnabled(), 2000); + QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 20000); + } + + // Run the focus procedure for Threshold - disable full-field + { + KTRY_FOCUS_CONFIGURE("Threshold", "Iterative", 0.0, 0.0, true, 50000); + KTRY_FOCUS_DETECT(0.1, 1); + KTRY_FOCUS_GADGET(QPushButton, startFocusB); + KTRY_EKOS_CLICK(startFocusB); + QTRY_VERIFY_WITH_TIMEOUT(!startFocusB->isEnabled(), 2000); + QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 20000); + } + + // Run the focus procedure for Gradient - disable full-field + { + KTRY_FOCUS_CONFIGURE("Gradient", "Iterative", 0.0, 0.0, true, 50000); + KTRY_FOCUS_DETECT(0.1, 1); + KTRY_FOCUS_GADGET(QPushButton, startFocusB); + KTRY_EKOS_CLICK(startFocusB); + QTRY_VERIFY_WITH_TIMEOUT(!startFocusB->isEnabled(), 2000); + QTRY_VERIFY_WITH_TIMEOUT(startFocusB->isEnabled(), 20000); + } +#endif +} + void TestEkosFocus::testStarDetection_data() { #if QT_VERSION < 0x050900 @@ -124,27 +242,27 @@ KTRY_FOCUS_GADGET(QLineEdit, starsOut); // Run the focus procedure for SEP - KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0); + KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, false, 50000); KTRY_FOCUS_DETECT(1, 3); QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000); // Run the focus procedure for Centroid - KTRY_FOCUS_CONFIGURE("Centroid", "Iterative", 0.0, 100.0); + KTRY_FOCUS_CONFIGURE("Centroid", "Iterative", 0.0, 100.0, false, 50000); KTRY_FOCUS_DETECT(1, 3); QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000); // Run the focus procedure for Threshold - disable full-field - KTRY_FOCUS_CONFIGURE("Threshold", "Iterative", 0.0, 0.0); + KTRY_FOCUS_CONFIGURE("Threshold", "Iterative", 0.0, 0.0, false, 50000); KTRY_FOCUS_DETECT(1, 3); QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000); // Run the focus procedure for Gradient - disable full-field - KTRY_FOCUS_CONFIGURE("Gradient", "Iterative", 0.0, 0.0); + KTRY_FOCUS_CONFIGURE("Gradient", "Iterative", 0.0, 0.0, false, 50000); KTRY_FOCUS_DETECT(1, 3); QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000); // Longer exposure - KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0); + KTRY_FOCUS_CONFIGURE("SEP", "Iterative", 0.0, 100.0, false, 50000); KTRY_FOCUS_DETECT(8, 1); QTRY_VERIFY_WITH_TIMEOUT(starsOut->text().toInt() >= 1, 5000); @@ -155,7 +273,7 @@ { for (double outer = 100.0; inner < outer; outer -= 42.0) { - KTRY_FOCUS_CONFIGURE("SEP", "Iterative", inner, outer); + KTRY_FOCUS_CONFIGURE("SEP", "Iterative", inner, outer, false, 50000); KTRY_FOCUS_DETECT(0.1, 2); } } @@ -165,7 +283,7 @@ { KTRY_FOCUS_GADGET(QDoubleSpinBox, thresholdSpin); thresholdSpin->setValue(threshold); - KTRY_FOCUS_CONFIGURE("Threshold", "Iterative", 0, 0.0); + KTRY_FOCUS_CONFIGURE("Threshold", "Iterative", 0, 0.0, false, 50000); KTRY_FOCUS_DETECT(0.1, 1); } #endif