1 /**
2 * UGENE - Integrated Bioinformatics Tools.
3 * Copyright (C) 2008-2017 UniPro <ugene@unipro.ru>
4 * http://ugene.net
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #include <core/GUITest.h>
23
24 #include <QApplication>
25 #include <QDateTime>
26 #include <QMessageBox>
27 #include <QProcess>
28 #include <QPushButton>
29 #include <QTimer>
30
31 #include "GTUtilsDialog.h"
32 #include "drivers/GTMouseDriver.h"
33 #include "primitives/GTWidget.h"
34 #include "utils/GTThread.h"
35 #include "utils/GTUtilsMac.h"
36
37 namespace HI {
38
39 #define GT_CLASS_NAME "GUIDialogWaiter"
40
GUIDialogWaiter(GUITestOpStatus & _os,Runnable * _r,const WaitSettings & _settings)41 GUIDialogWaiter::GUIDialogWaiter(GUITestOpStatus &_os, Runnable *_r, const WaitSettings &_settings)
42 : isFinished(false), waiterId(-1), os(_os), runnable(_r), settings(_settings), timer(NULL), waitingTime(0) {
43 static int totalWaiterCount = 0;
44 waiterId = totalWaiterCount++;
45
46 timer = new QTimer();
47
48 timer->connect(timer, SIGNAL(timeout()), this, SLOT(checkDialog()));
49 timer->start(timerPeriod);
50 }
51
~GUIDialogWaiter()52 GUIDialogWaiter::~GUIDialogWaiter() {
53 finishWaiting();
54 }
55
finishWaiting()56 void GUIDialogWaiter::finishWaiting() {
57 delete timer;
58 timer = nullptr;
59 delete runnable;
60 runnable = nullptr;
61 }
62
stopTimer()63 void GUIDialogWaiter::stopTimer() {
64 if (timer != nullptr) {
65 timer->stop();
66 }
67 }
68
isExpectedName(const QString & widgetObjectName,const QString & expectedObjectName)69 bool GUIDialogWaiter::isExpectedName(const QString &widgetObjectName, const QString &expectedObjectName) {
70 if (expectedObjectName.isNull()) {
71 qWarning("GT_DEBUG_MESSAGE GUIDialogWaiter Warning!! Checking name, widget name '%s', but expected any, saying it's expected", widgetObjectName.toLocal8Bit().constData());
72 return true;
73 }
74
75 qDebug("GT_DEBUG_MESSAGE GUIDialogWaiter Checking name, widget name '%s', expected '%s'", widgetObjectName.toLocal8Bit().constData(), expectedObjectName.toLocal8Bit().constData());
76 return widgetObjectName == expectedObjectName;
77 }
78
79 #define GT_METHOD_NAME "checkDialog"
checkDialog()80 void GUIDialogWaiter::checkDialog() {
81 try {
82 QWidget *widget = NULL;
83 GT_CHECK_NO_MESSAGE(runnable != NULL, "Runnable is NULL");
84
85 switch (settings.dialogType) {
86 case Modal:
87 widget = QApplication::activeModalWidget();
88 break;
89 case Popup:
90 widget = QApplication::activePopupWidget();
91 break;
92 default:
93 break;
94 }
95
96 if (widget != nullptr && !isFinished && isExpectedName(widget->objectName(), settings.objectName)) {
97 timer->stop();
98 qDebug("-------------------------");
99 qDebug("GT_DEBUG_MESSAGE GUIDialogWaiter::wait ID = %d, name = '%s' going to RUN", waiterId, settings.objectName.toLocal8Bit().constData());
100 qDebug("-------------------------");
101
102 GT_CHECK(settings.destiny != MustNotBeRun,
103 QString("Dialog appears which mustn't appear: %1")
104 .arg(settings.objectName.isEmpty() ? "(an unnamed dialog)" : settings.objectName));
105
106 try {
107 GTThread::waitForMainThread();
108 runnable->run();
109 isFinished = true;
110 } catch (GUITestOpStatus *) {
111 QWidget *popupWidget = QApplication::activePopupWidget();
112 while (popupWidget != NULL) {
113 GTWidget::close(os, popupWidget);
114 popupWidget = QApplication::activePopupWidget();
115 }
116
117 QWidget *modalWidget = QApplication::activeModalWidget();
118 while (modalWidget != NULL) {
119 GTWidget::close(os, modalWidget);
120 modalWidget = QApplication::activeModalWidget();
121 }
122 }
123 } else {
124 waitingTime += timerPeriod;
125 if (waitingTime > settings.timeout) {
126 qDebug("-------------------------");
127 qDebug("GT_DEBUG_MESSAGE !!! GUIDialogWaiter::TIMEOUT Id = %d, going to finish waiting", waiterId);
128 qDebug("-------------------------");
129
130 finishWaiting();
131 GT_CHECK(false, "TIMEOUT, waiterId = " + QString::number(waiterId));
132 }
133 }
134
135 } catch (GUITestOpStatus *) {
136 }
137 }
138 #undef GT_METHOD_NAME
139
140 #undef GT_CLASS_NAME
141
142 #define GT_CLASS_NAME "GTUtilsDialog"
143
HangChecker(GUITestOpStatus & _os)144 HangChecker::HangChecker(GUITestOpStatus &_os)
145 : os(_os), mightHung(false) {
146 timer = new QTimer();
147 }
148
startChecking()149 void HangChecker::startChecking() {
150 timer->connect(timer, SIGNAL(timeout()), this, SLOT(sl_check()));
151 timer->start(GTUtilsDialog::timerPeriod * 100);
152 }
153
154 #define GT_METHOD_NAME "sl_check"
sl_check()155 void HangChecker::sl_check() {
156 QWidget *dialog = QApplication::activeModalWidget();
157 try {
158 if (dialog != NULL) {
159 bool found = false;
160 foreach (GUIDialogWaiter *waiter, GTUtilsDialog::pool) {
161 if (!waiter->isFinished && waiter->isExpectedName(dialog->objectName(), waiter->getSettings().objectName)) {
162 found = true;
163 mightHung = false;
164 }
165 }
166
167 if (!found) {
168 if (mightHung) {
169 GT_CHECK(false, "dialog " + QString(dialog->metaObject()->className()) + " name: " + dialog->objectName() + " hang up");
170 }
171 }
172
173 if (!found) {
174 if (!mightHung) {
175 mightHung = true;
176 qWarning("GT_DEBUG_MESSAGE dialog mignt hang up");
177 }
178 }
179
180 } else {
181 mightHung = false;
182 }
183 } catch (GUITestOpStatus *) {
184 GTGlobals::takeScreenShot(GUITest::screenshotDir + QDateTime::currentDateTime().toString() + ".jpg");
185 QWidget *w = QApplication::activeModalWidget();
186 while (w != NULL) {
187 w->close();
188 w = QApplication::activeModalWidget();
189 }
190 w = QApplication::activePopupWidget();
191 while (w != NULL) {
192 w->close();
193 w = QApplication::activePopupWidget();
194 }
195 }
196 }
197 #undef GT_METHOD_NAME
198
199 #undef GT_CLASS_NAME
200
201 #define GT_CLASS_NAME "GTUtilsDialog"
202
203 QList<GUIDialogWaiter *> GTUtilsDialog::pool = QList<GUIDialogWaiter *>();
204 HangChecker *GTUtilsDialog::hangChecker = NULL;
205
startHangChecking(GUITestOpStatus & os)206 void GTUtilsDialog::startHangChecking(GUITestOpStatus &os) {
207 hangChecker = new HangChecker(os);
208 hangChecker->startChecking();
209 }
210
stopHangChecking()211 void GTUtilsDialog::stopHangChecking() {
212 if (hangChecker != NULL) {
213 hangChecker->timer->stop();
214 }
215 }
216
217 #define GT_METHOD_NAME "buttonBox"
buttonBox(GUITestOpStatus & os,QWidget * dialog)218 QDialogButtonBox *GTUtilsDialog::buttonBox(GUITestOpStatus &os, QWidget *dialog) {
219 return qobject_cast<QDialogButtonBox *>(GTWidget::findWidget(os, "buttonBox", dialog));
220 }
221 #undef GT_METHOD_NAME
222
223 #define GT_METHOD_NAME "clickButtonBox"
clickButtonBox(GUITestOpStatus & os,QDialogButtonBox::StandardButton button)224 void GTUtilsDialog::clickButtonBox(GUITestOpStatus &os, QDialogButtonBox::StandardButton button) {
225 clickButtonBox(os, QApplication::activeModalWidget(), button);
226 }
227 #undef GT_METHOD_NAME
228
229 #define GT_METHOD_NAME "clickButtonBox"
clickButtonBox(GUITestOpStatus & os,QWidget * dialog,QDialogButtonBox::StandardButton button)230 void GTUtilsDialog::clickButtonBox(GUITestOpStatus &os, QWidget *dialog, QDialogButtonBox::StandardButton button) {
231 #ifdef Q_OS_DARWIN
232 QMessageBox *mbox = qobject_cast<QMessageBox *>(dialog);
233 GTUtilsMac fakeClock;
234 fakeClock.startWorkaroundForMacCGEvents(16000, false);
235 if (mbox != NULL && (button == QDialogButtonBox::Yes || button == QDialogButtonBox::No || button == QDialogButtonBox::NoToAll)) {
236 QMessageBox::StandardButton btn =
237 button == QDialogButtonBox::Yes ? QMessageBox::Yes
238 : button == QDialogButtonBox::NoToAll ? QMessageBox::NoToAll
239 : QMessageBox::No;
240 QAbstractButton *pushButton = mbox->button(btn);
241 GT_CHECK(pushButton != NULL, "pushButton is NULL");
242 GTWidget::click(os, pushButton);
243 } else {
244 QDialogButtonBox *box = buttonBox(os, dialog);
245 GT_CHECK(box != NULL, "buttonBox is NULL");
246 QPushButton *pushButton = box->button(button);
247 GT_CHECK(pushButton != NULL, "pushButton is NULL");
248 GTWidget::click(os, pushButton);
249 }
250 #else
251 QDialogButtonBox *box = buttonBox(os, dialog);
252 GT_CHECK(box != NULL, "buttonBox is NULL");
253 QPushButton *pushButton = box->button(button);
254 GT_CHECK(pushButton != NULL, "pushButton is NULL");
255 GTWidget::click(os, pushButton);
256 #endif
257 }
258 #undef GT_METHOD_NAME
259
waitForDialog(GUITestOpStatus & os,Runnable * r,const GUIDialogWaiter::WaitSettings & settings)260 void GTUtilsDialog::waitForDialog(GUITestOpStatus &os, Runnable *r, const GUIDialogWaiter::WaitSettings &settings) {
261 pool.prepend(new GUIDialogWaiter(os, r, settings));
262 }
263
waitForDialog(GUITestOpStatus & os,Runnable * r,int timeout)264 void GTUtilsDialog::waitForDialog(GUITestOpStatus &os, Runnable *r, int timeout) {
265 GUIDialogWaiter::WaitSettings settings;
266 Filler *f = dynamic_cast<Filler *>(r);
267 if (f) {
268 settings = f->getSettings();
269 if (timeout > 0) {
270 settings.timeout = timeout;
271 }
272 }
273 waitForDialog(os, r, settings);
274 }
275
waitForDialogWhichMustNotBeRun(GUITestOpStatus & os,Runnable * r)276 void GTUtilsDialog::waitForDialogWhichMustNotBeRun(GUITestOpStatus &os, Runnable *r) {
277 GUIDialogWaiter::WaitSettings settings;
278 Filler *f = dynamic_cast<Filler *>(r);
279 if (f) {
280 settings = f->getSettings();
281 }
282
283 settings.destiny = GUIDialogWaiter::MustNotBeRun;
284 waitForDialog(os, r, settings);
285 }
286
waitForDialogWhichMayRunOrNot(GUITestOpStatus & os,Runnable * r)287 void GTUtilsDialog::waitForDialogWhichMayRunOrNot(GUITestOpStatus &os, Runnable *r) {
288 GUIDialogWaiter::WaitSettings settings;
289 Filler *f = dynamic_cast<Filler *>(r);
290 if (f) {
291 settings = f->getSettings();
292 }
293
294 settings.destiny = GUIDialogWaiter::NoMatter;
295 settings.timeout = 480000;
296 waitForDialog(os, r, settings);
297 }
298
299 #define GT_METHOD_NAME "waitAllFinished"
waitAllFinished(GUITestOpStatus & os,int timeoutMillis)300 void GTUtilsDialog::waitAllFinished(GUITestOpStatus &os, int timeoutMillis) {
301 bool isAllFinished = pool.isEmpty();
302 for (int time = 0; time < timeoutMillis && !isAllFinished; time += GT_OP_CHECK_MILLIS) {
303 GTGlobals::sleep(time > 0 ? GT_OP_CHECK_MILLIS : 0);
304 isAllFinished = true;
305 foreach (GUIDialogWaiter *waiter, pool) {
306 if (!waiter->isFinished && waiter->getSettings().destiny == GUIDialogWaiter::MustBeRun) {
307 isAllFinished = false;
308 break;
309 }
310 }
311 }
312 if (!isAllFinished && !os.hasError()) {
313 GUIDialogWaiter *nonFinishedWaiter = nullptr;
314 foreach (GUIDialogWaiter *waiter, pool) {
315 if (!waiter->isFinished && waiter->getSettings().destiny == GUIDialogWaiter::MustBeRun) {
316 nonFinishedWaiter = waiter;
317 break;
318 }
319 }
320 os.setError(QString("There are active waiters after: %1ms. First waiter details: %2")
321 .arg(timeoutMillis)
322 .arg(nonFinishedWaiter == nullptr ? "nullptr?" : nonFinishedWaiter->getSettings().objectName));
323 }
324 }
325 #undef GT_METHOD_NAME
326
removeRunnable(Runnable const * const runnable)327 void GTUtilsDialog::removeRunnable(Runnable const *const runnable) {
328 foreach (GUIDialogWaiter *waiter, pool) {
329 if (waiter->getRunnable() == runnable) {
330 pool.removeOne(waiter);
331 delete waiter;
332 }
333 }
334 }
335
336 #define GT_METHOD_NAME "checkAllFinished"
checkAllFinished(GUITestOpStatus & os)337 void GTUtilsDialog::checkAllFinished(GUITestOpStatus &os) {
338 Q_UNUSED(os);
339
340 foreach (GUIDialogWaiter *waiter, pool) {
341 GT_CHECK(waiter != nullptr, "GUIDialogWaiter is null");
342 switch (waiter->getSettings().destiny) {
343 case GUIDialogWaiter::MustBeRun:
344 GT_CHECK(waiter->isFinished, QString("\"%1\" not run but should be").arg((waiter->getSettings().objectName)));
345 break;
346 case GUIDialogWaiter::MustNotBeRun:
347 GT_CHECK(!waiter->isFinished, QString("\"%1\" had run but should not").arg((waiter->getSettings().objectName)));
348 break;
349 case GUIDialogWaiter::NoMatter:
350 break;
351 }
352 }
353 }
354 #undef GT_METHOD_NAME
355
cleanup(GUITestOpStatus & os,CleanupSettings s)356 void GTUtilsDialog::cleanup(GUITestOpStatus &os, CleanupSettings s) {
357 foreach (GUIDialogWaiter *waiter, pool) {
358 waiter->stopTimer();
359 }
360
361 if (s == FailOnUnfinished) {
362 checkAllFinished(os);
363 }
364
365 stopHangChecking();
366
367 qDeleteAll(pool);
368 pool.clear();
369 }
370
371 #undef GT_CLASS_NAME
372
Filler(GUITestOpStatus & os,const GUIDialogWaiter::WaitSettings & settings,CustomScenario * scenario)373 Filler::Filler(GUITestOpStatus &os, const GUIDialogWaiter::WaitSettings &settings, CustomScenario *scenario)
374 : os(os), settings(settings), scenario(scenario) {
375 }
376
Filler(GUITestOpStatus & os,const QString & objectName,CustomScenario * scenario)377 Filler::Filler(GUITestOpStatus &os, const QString &objectName, CustomScenario *scenario)
378 : os(os), settings(GUIDialogWaiter::WaitSettings(objectName)), scenario(scenario) {
379 }
380
~Filler()381 Filler::~Filler() {
382 delete scenario;
383 }
384
getSettings() const385 GUIDialogWaiter::WaitSettings Filler::getSettings() const {
386 return settings;
387 }
388
run()389 void Filler::run() {
390 if (scenario == nullptr) {
391 commonScenario();
392 } else {
393 scenario->run(os);
394 }
395 GTThread::waitForMainThread();
396 }
397
releaseMouseButtons()398 void Filler::releaseMouseButtons() {
399 Qt::MouseButtons buttons = QGuiApplication::mouseButtons();
400
401 if (buttons | Qt::LeftButton) {
402 GTMouseDriver::release(Qt::LeftButton);
403 }
404
405 if (buttons | Qt::RightButton) {
406 GTMouseDriver::release(Qt::RightButton);
407 }
408 }
409
410 } // namespace HI
411