1 /* 2 SPDX-FileCopyrightText: 2017 Csaba Kertesz <csaba.kertesz@gmail.com> 3 4 SPDX-License-Identifier: GPL-2.0-or-later 5 */ 6 7 #pragma once 8 9 #include "../testhelpers.h" 10 #include "kstarsdata.h" 11 12 #include <QMutex> 13 #include <QTimer> 14 #include <QApplication> 15 #include <QSystemTrayIcon> 16 17 class KStars; 18 19 // Helper for on-screen test messages 20 #define KTELL_BEGIN() do { KStarsUiTests::notifierBegin(); } while(false) 21 #define KTELL_HIDE() do { KStarsUiTests::notifierHide(); } while(false) 22 #define KTELL_END() do { KStarsUiTests::notifierEnd(); } while(false) 23 #define KTELL(message) do { KStarsUiTests::notifierMessage(__FUNCTION__, message); } while(false) 24 25 // We need to call a set of preliminary operations for each UI test, such as the KStars Wizard setup. 26 // We also need QtCreator to detect our tests - that application scans class definitions for qExec calls. 27 28 // If no KSTARS_UI_TEST is defined, this is our default main 29 //extern int main(int argc, char *argv[]) __attribute__((weak)); 30 31 // The KSTARS_UI_TEST macro is used like QTEST_MAIN, and runs: 32 // - The Qt UI configuration 33 // - The KStars environment configuration 34 // - The KStars wizard, and eventually the Ekos wizard if INDI is available 35 // - The designated test class 36 // Execution of the application is asynchronous, and tests are delayed as if an end-user was using the app 37 38 extern void prepare_tests(); 39 extern int run_wizards(int argc, char **argv); 40 extern void execute_tests(); 41 42 #define QTEST_KSTARS_MAIN(klass) \ 43 int main(int argc, char *argv[]) { \ 44 klass tc; \ 45 for (int i = 0; i < argc; i++) \ 46 if (!strcmp("-functions", argv[i])) \ 47 return QTest::qExec(&tc, argc, argv); \ 48 QApplication* app = new QApplication(argc, argv); \ 49 KTEST_BEGIN(); \ 50 prepare_tests(); \ 51 int failure = 0; \ 52 QTimer::singleShot(1000, app, [&] { \ 53 qDebug("Starting wizard..."); \ 54 failure |= run_wizards(argc, argv); \ 55 if (!failure) { \ 56 KTELL_BEGIN(); \ 57 failure |= QTest::qExec(&tc, app->arguments()); \ 58 KTELL_END(); \ 59 } \ 60 qDebug("Tests are done."); \ 61 app->quit(); \ 62 }); \ 63 execute_tests(); \ 64 KTEST_END(); \ 65 return failure; } 66 67 // All QTest features are macros returning with no error code. 68 // 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. 69 // Tests classes in this folder should attempt to provide new macro shortcuts once their details are properly validated 70 71 // This is an example of a test class. 72 // It inherits from QObject, and defines private slots as test functions 73 class KStarsUiTests : public QObject 74 { 75 Q_OBJECT 76 public: QObject(parent)77 explicit KStarsUiTests(QObject *parent = nullptr): QObject(parent) {}; 78 79 public: 80 static QSystemTrayIcon * m_Notifier; 81 static void notifierBegin(); 82 static void notifierHide(); 83 static void notifierMessage(QString, QString); 84 static void notifierEnd(); 85 86 private slots: 87 88 /** @brief Members "initTestCase" and "cleanupTestCase" trigger when the framework processes this class. 89 * Member "initTestCase" is executed before any other declared test. 90 * Member "cleanupTestCase" is executed after all tests are done. 91 */ 92 /** @{ */ initTestCase()93 void initTestCase() {}; cleanupTestCase()94 void cleanupTestCase() {}; 95 /** @} */ 96 97 /** @brief Members "init" and "cleanup" trigger when the framework process one test from this class. 98 * Member "init" is executed before each declared test. 99 * Member "cleanup" is executed after each test is done. 100 */ 101 /** @{ */ init()102 void init() {}; cleanup()103 void cleanup() {}; 104 /** @} */ 105 106 /** @brief Tests should be defined as "test<a-particular-feature>" for homogeneity. 107 * Use QVERIFY and others to validate tests that reply immediately. 108 * If the application needs to process events while you wait for something, use QTRY_VERIFY_WITH_TIMEOUT. 109 * If the application needs to process signals while you wait for something, use QTimer::singleShot to run your QVERIFY checks with an arbitrary delay. 110 * If the application opens dialogs inside signals while you wait for something but you cannot determine the delay, use 111 * QTimer::singleShot with a lambda, and retrigger the timer until your check can be verified. 112 */ 113 testSomethingThatWorks()114 void testSomethingThatWorks() 115 { 116 QVERIFY(QString("this string contains").contains("string")); 117 }; 118 119 /** @brief Tests that require fixtures can define those in "test<a-particular-feature>_data" as a record list. 120 * Condition your test with QT_VERSION >= 0x050900 as this is the minimal version that supports this. 121 * Add data columns to your fixture with QTest::addColum<column-type>("column-name"). 122 * In the same order, add data rows to your fixture with QTest::addRow("<arbitrary-row-identifier>") << column1 << column2 << ... 123 * Afterwards, in your test, use QFETCH(<column-type>, <column-name>) to obtain values for one row of your fixture. 124 * Your test will be run as many times as there are rows, so use initTestCase and cleanupTestCase. 125 */ 126 /** @{ */ testAnotherThingThatWorks_data()127 void testAnotherThingThatWorks_data() 128 { 129 #if QT_VERSION < 0x050900 130 QSKIP("Skipping fixture-based test on old QT version."); 131 #else 132 QTest::addColumn <int> ("A"); 133 QTest::addColumn <int> ("B"); 134 QTest::addColumn <int> ("C"); 135 QTest::addRow("1+1=2") << 1 << 1 << 2; 136 QTest::addRow("1+4=5") << 1 << 4 << 5; 137 #endif 138 }; 139 testAnotherThingThatWorks()140 void testAnotherThingThatWorks() 141 { 142 #if QT_VERSION < 0x050900 143 QSKIP("Skipping fixture-based test on old QT version."); 144 #else 145 QFETCH(int, A); 146 QFETCH(int, B); 147 QFETCH(int, C); 148 QVERIFY(A+B == C); 149 #endif 150 }; 151 /** @} */ 152 153 /** @brief Tests that are built to reproduce a bug should expect failures with QEXPECT_FAIL. 154 * 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. 155 * The first argument of QEXPECT_FAIL provides a way to fail on a particular fixture only. 156 * If the test is not using a fixture or if the failure should trigger for all fixtures, the first argument must be "". 157 */ testSomethingThatFails()158 void testSomethingThatFails() 159 { 160 QEXPECT_FAIL("", "The next verification will fail, but the test will continue", Continue); 161 QVERIFY(1+1 == 3); 162 } 163 }; 164