File indexing completed on 2024-04-21 14:47:20

0001 /*
0002     SPDX-FileCopyrightText: 2017 Csaba Kertesz <csaba.kertesz@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include "../testhelpers.h"
0010 #include "kstarsdata.h"
0011 
0012 #include <QMutex>
0013 #include <QTimer>
0014 #include <QApplication>
0015 #include <QSystemTrayIcon>
0016 
0017 class KStars;
0018 
0019 // Helper for on-screen test messages
0020 #define KTELL_BEGIN() do { KStarsUiTests::notifierBegin(); } while(false)
0021 #define KTELL_HIDE() do { KStarsUiTests::notifierHide(); } while(false)
0022 #define KTELL_END() do { KStarsUiTests::notifierEnd(); } while(false)
0023 #define KTELL(message) do { KStarsUiTests::notifierMessage(__FUNCTION__, message); } while(false)
0024 
0025 // We need to call a set of preliminary operations for each UI test, such as the KStars Wizard setup.
0026 // We also need QtCreator to detect our tests - that application scans class definitions for qExec calls.
0027 
0028 // If no KSTARS_UI_TEST is defined, this is our default main
0029 //extern int main(int argc, char *argv[]) __attribute__((weak));
0030 
0031 // The KSTARS_UI_TEST macro is used like QTEST_MAIN, and runs:
0032 // - The Qt UI configuration
0033 // - The KStars environment configuration
0034 // - The KStars wizard, and eventually the Ekos wizard if INDI is available
0035 // - The designated test class
0036 // Execution of the application is asynchronous, and tests are delayed as if an end-user was using the app
0037 
0038 extern void prepare_tests();
0039 extern int run_wizards(int argc, char **argv);
0040 extern void execute_tests();
0041 
0042 #define QTEST_KSTARS_MAIN(klass) \
0043     int main(int argc, char *argv[]) { \
0044         klass tc; \
0045         for (int i = 0; i < argc; i++) \
0046             if (!strcmp("-functions", argv[i])) \
0047                 return QTest::qExec(&tc, argc, argv); \
0048         QApplication* app = new QApplication(argc, argv); \
0049         KTEST_BEGIN(); \
0050         prepare_tests(); \
0051         int failure = 0; \
0052         QTimer::singleShot(1000, app, [&] { \
0053             qDebug("Starting wizard..."); \
0054             failure |= run_wizards(argc, argv); \
0055             if (!failure) { \
0056                 KTELL_BEGIN(); \
0057                 failure |= QTest::qExec(&tc, app->arguments()); \
0058                 KTELL_END(); \
0059             } \
0060             qDebug("Tests are done."); \
0061             app->quit(); \
0062         }); \
0063         execute_tests(); \
0064         KTEST_END(); \
0065         return failure; }
0066 
0067 // on top of QTEST_KSTARS_MAIN, this macro offers a guider selection
0068 // The option "-guider <name>" selects the guider name from the test
0069 // arguments and hands the name (as QString) over to the test case constructor
0070 
0071 #define QTEST_KSTARS_WITH_GUIDER_MAIN(klass) \
0072     int main(int argc, char *argv[]) { \
0073         QString guider = "Internal"; \
0074         std::vector<char *> testargv; \
0075         bool showfunctions = false; \
0076         for (int i = 0; i < argc; i++) { \
0077             if (!strcmp("-functions", argv[i])) \
0078             {testargv.push_back(argv[i]); showfunctions = true;} \
0079             else if (!strcmp("-guider", argv[i]) && i+1 < argc) \
0080             { guider = argv[i+1]; i++; } \
0081             else testargv.push_back(argv[i]); } \
0082         klass tc(guider); \
0083         if (showfunctions) return QTest::qExec(&tc, static_cast<int>(testargv.size()), testargv.data()); \
0084         QApplication* app = new QApplication(argc, argv); \
0085         KTEST_BEGIN(); \
0086         prepare_tests(); \
0087         int failure = 0; \
0088         QTimer::singleShot(1000, app, [&] { \
0089             qDebug("Starting tests..."); \
0090             failure |= run_wizards(static_cast<int>(testargv.size()), testargv.data()); \
0091             if (!failure) { \
0092                 KTELL_BEGIN(); \
0093                 failure |= QTest::qExec(&tc, static_cast<int>(testargv.size()), testargv.data()); \
0094                 KTELL_END(); \
0095             } \
0096             qDebug("Tests are done."); \
0097             app->quit(); }); \
0098         execute_tests(); \
0099         KTEST_END(); \
0100         return failure; }
0101 
0102 
0103 // All QTest features are macros returning with no error code.
0104 // Therefore, in order to bail out at first failure, tests cannot use functions to run sub-tests and are required to use grouping macros too.
0105 // Tests classes in this folder should attempt to provide new macro shortcuts once their details are properly validated
0106 
0107 // This is an example of a test class.
0108 // It inherits from QObject, and defines private slots as test functions
0109 class KStarsUiTests : public QObject
0110 {
0111     Q_OBJECT
0112 public:
0113     explicit KStarsUiTests(QObject *parent = nullptr): QObject(parent) {};
0114 
0115 public:
0116     static QSystemTrayIcon * m_Notifier;
0117     static void notifierBegin();
0118     static void notifierHide();
0119     static void notifierMessage(QString, QString);
0120     static void notifierEnd();
0121 
0122 private slots:
0123 
0124     /** @brief Members "initTestCase" and "cleanupTestCase" trigger when the framework processes this class.
0125      * Member "initTestCase" is executed before any other declared test.
0126      * Member "cleanupTestCase" is executed after all tests are done.
0127      */
0128     /** @{ */
0129     void initTestCase() {};
0130     void cleanupTestCase() {};
0131     /** @} */
0132 
0133     /** @brief Members "init" and "cleanup" trigger when the framework process one test from this class.
0134      * Member "init" is executed before each declared test.
0135      * Member "cleanup" is executed after each test is done.
0136      */
0137     /** @{ */
0138     void init() {};
0139     void cleanup() {};
0140     /** @} */
0141 
0142     /** @brief Tests should be defined as "test<a-particular-feature>" for homogeneity.
0143      * Use QVERIFY and others to validate tests that reply immediately.
0144      * If the application needs to process events while you wait for something, use QTRY_VERIFY_WITH_TIMEOUT.
0145      * If the application needs to process signals while you wait for something, use QTimer::singleShot to run your QVERIFY checks with an arbitrary delay.
0146      * If the application opens dialogs inside signals while you wait for something but you cannot determine the delay, use
0147      * QTimer::singleShot with a lambda, and retrigger the timer until your check can be verified.
0148      */
0149 
0150     void testSomethingThatWorks()
0151     {
0152         QVERIFY(QString("this string contains").contains("string"));
0153     };
0154 
0155     /** @brief Tests that require fixtures can define those in "test<a-particular-feature>_data" as a record list.
0156      * Condition your test with QT_VERSION >= 0x050900 as this is the minimal version that supports this.
0157      * Add data columns to your fixture with QTest::addColum<column-type>("column-name").
0158      * In the same order, add data rows to your fixture with QTest::addRow("<arbitrary-row-identifier>") << column1 << column2 << ...
0159      * Afterwards, in your test, use QFETCH(<column-type>, <column-name>) to obtain values for one row of your fixture.
0160      * Your test will be run as many times as there are rows, so use initTestCase and cleanupTestCase.
0161      */
0162     /** @{ */
0163     void testAnotherThingThatWorks_data()
0164     {
0165 #if QT_VERSION < 0x050900
0166         QSKIP("Skipping fixture-based test on old QT version.");
0167 #else
0168         QTest::addColumn <int> ("A");
0169         QTest::addColumn <int> ("B");
0170         QTest::addColumn <int> ("C");
0171         QTest::addRow("1+1=2") << 1 << 1 << 2;
0172         QTest::addRow("1+4=5") << 1 << 4 << 5;
0173 #endif
0174     };
0175 
0176     void testAnotherThingThatWorks()
0177     {
0178 #if QT_VERSION < 0x050900
0179         QSKIP("Skipping fixture-based test on old QT version.");
0180 #else
0181         QFETCH(int, A);
0182         QFETCH(int, B);
0183         QFETCH(int, C);
0184         QVERIFY(A+B == C);
0185 #endif
0186     };
0187     /** @} */
0188 
0189     /** @brief Tests that are built to reproduce a bug should expect failures with QEXPECT_FAIL.
0190      * When the bug is resolved, that test will trigger a failure to verify the fix and may then be updated to remove the expected failure.
0191      * The first argument of QEXPECT_FAIL provides a way to fail on a particular fixture only.
0192      * If the test is not using a fixture or if the failure should trigger for all fixtures, the first argument must be "".
0193      */
0194     void testSomethingThatFails()
0195     {
0196         QEXPECT_FAIL("", "The next verification will fail, but the test will continue", Continue);
0197         QVERIFY(1+1 == 3);
0198     }
0199 };