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