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 };