Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include <qdeclarativetester.h> |
43 | #include <QDebug> |
44 | #include <QApplication> |
45 | #include <qdeclarativeview.h> |
46 | #include <QFile> |
47 | #include <QDeclarativeComponent> |
48 | #include <QDir> |
49 | #include <QCryptographicHash> |
50 | #include <private/qabstractanimation_p.h> |
51 | #include <QGraphicsObject> |
52 | #ifndef Q_OS_SYMBIAN |
53 | #include <private/qdeclarativeitem_p.h> |
54 | #endif |
55 | |
56 | QT_BEGIN_NAMESPACE |
57 | |
58 | extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled; |
59 | |
60 | QDeclarativeTester::QDeclarativeTester(const QString &script, QDeclarativeViewer::ScriptOptions opts, |
61 | QDeclarativeView *parent) |
62 | : QAbstractAnimation(parent), m_script(script), m_view(parent), filterEvents(true), options(opts), |
63 | testscript(0), hasCompleted(false), hasFailed(false) |
64 | { |
65 | parent->viewport()->installEventFilter(this); |
66 | parent->installEventFilter(this); |
67 | QUnifiedTimer::instance()->setConsistentTiming(true); |
68 | |
69 | //Font antialiasing makes tests system-specific, so disable it |
70 | QFont noAA = QApplication::font(); |
71 | noAA.setStyleStrategy(QFont::NoAntialias); |
72 | QApplication::setFont(noAA); |
73 | |
74 | if (options & QDeclarativeViewer::Play) |
75 | this->run(); |
76 | start(); |
77 | } |
78 | |
79 | QDeclarativeTester::~QDeclarativeTester() |
80 | { |
81 | if (!hasFailed && |
82 | options & QDeclarativeViewer::Record && |
83 | options & QDeclarativeViewer::SaveOnExit) |
84 | save(); |
85 | } |
86 | |
87 | int QDeclarativeTester::duration() const |
88 | { |
89 | return -1; |
90 | } |
91 | |
92 | void QDeclarativeTester::addMouseEvent(Destination dest, QMouseEvent *me) |
93 | { |
94 | MouseEvent e(me); |
95 | e.destination = dest; |
96 | m_mouseEvents << e; |
97 | } |
98 | |
99 | void QDeclarativeTester::addKeyEvent(Destination dest, QKeyEvent *ke) |
100 | { |
101 | KeyEvent e(ke); |
102 | e.destination = dest; |
103 | m_keyEvents << e; |
104 | } |
105 | |
106 | bool QDeclarativeTester::eventFilter(QObject *o, QEvent *e) |
107 | { |
108 | if (!filterEvents) |
109 | return false; |
110 | |
111 | Destination destination; |
112 | if (o == m_view) { |
113 | destination = View; |
114 | } else if (o == m_view->viewport()) { |
115 | destination = ViewPort; |
116 | } else { |
117 | return false; |
118 | } |
119 | |
120 | switch (e->type()) { |
121 | case QEvent::KeyPress: |
122 | case QEvent::KeyRelease: |
123 | addKeyEvent(destination, (QKeyEvent *)e); |
124 | return true; |
125 | case QEvent::MouseButtonPress: |
126 | case QEvent::MouseButtonRelease: |
127 | case QEvent::MouseMove: |
128 | case QEvent::MouseButtonDblClick: |
129 | addMouseEvent(destination, (QMouseEvent *)e); |
130 | return true; |
131 | default: |
132 | break; |
133 | } |
134 | return false; |
135 | } |
136 | |
137 | void QDeclarativeTester::executefailure() |
138 | { |
139 | hasFailed = true; |
140 | |
141 | if (options & QDeclarativeViewer::ExitOnFailure) |
142 | exit(-1); |
143 | } |
144 | |
145 | void QDeclarativeTester::imagefailure() |
146 | { |
147 | hasFailed = true; |
148 | |
149 | if (options & QDeclarativeViewer::ExitOnFailure){ |
150 | testSkip(); |
151 | exit(hasFailed?-1:0); |
152 | } |
153 | } |
154 | |
155 | void QDeclarativeTester::testSkip() |
156 | { |
157 | if (options & QDeclarativeViewer::TestSkipProperty){ |
158 | QString e = m_view->rootObject()->property("skip").toString(); |
159 | if (!e.isEmpty()) { |
160 | if(hasFailed){ |
161 | qWarning() << "Test failed, but skipping it: "<< e; |
162 | }else{ |
163 | qWarning() << "Test skipped: "<< e; |
164 | } |
165 | hasFailed = 0; |
166 | } |
167 | } |
168 | } |
169 | |
170 | void QDeclarativeTester::complete() |
171 | { |
172 | if ((options & QDeclarativeViewer::TestErrorProperty) && !hasFailed) { |
173 | QString e = m_view->rootObject()->property("error").toString(); |
174 | if (!e.isEmpty()) { |
175 | qWarning() << "Test failed:"<< e; |
176 | hasFailed = true; |
177 | } |
178 | } |
179 | |
180 | |
181 | testSkip(); |
182 | if (options & QDeclarativeViewer::ExitOnComplete) |
183 | QApplication::exit(hasFailed?-1:0); |
184 | |
185 | if (hasCompleted) |
186 | return; |
187 | hasCompleted = true; |
188 | |
189 | if (options & QDeclarativeViewer::Play) |
190 | qWarning("Script playback complete"); |
191 | } |
192 | |
193 | void QDeclarativeTester::run() |
194 | { |
195 | QDeclarativeComponent c(m_view->engine(), m_script + QLatin1String(".qml")); |
196 | |
197 | testscript = qobject_cast<QDeclarativeVisualTest *>(c.create()); |
198 | if (testscript) testscript->setParent(this); |
199 | else { executefailure(); exit(-1); } |
200 | testscriptidx = 0; |
201 | } |
202 | |
203 | void QDeclarativeTester::save() |
204 | { |
205 | QString filename = m_script + QLatin1String(".qml"); |
206 | QFileInfo filenameInfo(filename); |
207 | QDir saveDir = filenameInfo.absoluteDir(); |
208 | saveDir.mkpath(QLatin1String(".")); |
209 | |
210 | QFile file(filename); |
211 | file.open(QIODevice::WriteOnly); |
212 | QTextStream ts(&file); |
213 | |
214 | ts << "import Qt.VisualTest 4.7\n\n"; |
215 | ts << "VisualTest {\n"; |
216 | |
217 | int imgCount = 0; |
218 | QList<KeyEvent> keyevents = m_savedKeyEvents; |
219 | QList<MouseEvent> mouseevents = m_savedMouseEvents; |
220 | for (int ii = 0; ii < m_savedFrameEvents.count(); ++ii) { |
221 | const FrameEvent &fe = m_savedFrameEvents.at(ii); |
222 | ts << " Frame {\n"; |
223 | ts << " msec: "<< fe.msec << "\n"; |
224 | if (!fe.hash.isEmpty()) { |
225 | ts << " hash: \""<< fe.hash.toHex() << "\"\n"; |
226 | } else if (!fe.image.isNull()) { |
227 | QString filename = filenameInfo.baseName() + QLatin1String(".") + QString::number(imgCount) + QLatin1String( ".png"); |
228 | fe.image.save(m_script + QLatin1String(".") + QString::number(imgCount) + QLatin1String( ".png")); |
229 | imgCount++; |
230 | ts << " image: \""<< filename << "\"\n"; |
231 | } |
232 | ts << " }\n"; |
233 | |
234 | while (!mouseevents.isEmpty() && |
235 | mouseevents.first().msec == fe.msec) { |
236 | MouseEvent me = mouseevents.takeFirst(); |
237 | |
238 | ts << " Mouse {\n"; |
239 | ts << " type: "<< me.type << "\n"; |
240 | ts << " button: "<< me.button << "\n"; |
241 | ts << " buttons: "<< me.buttons << "\n"; |
242 | ts << " x: "<< me.pos.x() << "; y: "<< me.pos.y() << "\n"; |
243 | ts << " modifiers: "<< me.modifiers << "\n"; |
244 | if (me.destination == ViewPort) |
245 | ts << " sendToViewport: true\n"; |
246 | ts << " }\n"; |
247 | } |
248 | |
249 | while (!keyevents.isEmpty() && |
250 | keyevents.first().msec == fe.msec) { |
251 | KeyEvent ke = keyevents.takeFirst(); |
252 | |
253 | ts << " Key {\n"; |
254 | ts << " type: "<< ke.type << "\n"; |
255 | ts << " key: "<< ke.key << "\n"; |
256 | ts << " modifiers: "<< ke.modifiers << "\n"; |
257 | ts << " text: \""<< ke.text.toUtf8().toHex() << "\"\n"; |
258 | ts << " autorep: "<< (ke.autorep? "true": "false") << "\n"; |
259 | ts << " count: "<< ke.count << "\n"; |
260 | if (ke.destination == ViewPort) |
261 | ts << " sendToViewport: true\n"; |
262 | ts << " }\n"; |
263 | } |
264 | } |
265 | |
266 | ts << "}\n"; |
267 | file.close(); |
268 | } |
269 | |
270 | void QDeclarativeTester::updateCurrentTime(int msec) |
271 | { |
272 | #ifndef Q_OS_SYMBIAN |
273 | QDeclarativeItemPrivate::setConsistentTime(msec); |
274 | #endif |
275 | if (!testscript && msec > 16 && options & QDeclarativeViewer::Snapshot) |
276 | return; |
277 | |
278 | QImage img(m_view->width(), m_view->height(), QImage::Format_RGB32); |
279 | |
280 | if (options & QDeclarativeViewer::TestImages) { |
281 | img.fill(qRgb(255,255,255)); |
282 | |
283 | #ifdef Q_WS_MAC |
284 | bool oldSmooth = qt_applefontsmoothing_enabled; |
285 | qt_applefontsmoothing_enabled = false; |
286 | #endif |
287 | QPainter p(&img); |
288 | #ifdef Q_WS_MAC |
289 | qt_applefontsmoothing_enabled = oldSmooth; |
290 | #endif |
291 | |
292 | m_view->render(&p); |
293 | } |
294 | |
295 | bool snapshot = msec == 16 && (options & QDeclarativeViewer::Snapshot |
296 | || (testscript && testscript->count() == 2)); |
297 | |
298 | FrameEvent fe; |
299 | fe.msec = msec; |
300 | if (msec == 0 || !(options & QDeclarativeViewer::TestImages)) { |
301 | // Skip first frame, skip if not doing images |
302 | } else if (0 == ((m_savedFrameEvents.count()-1) % 60) || snapshot) { |
303 | fe.image = img; |
304 | } else { |
305 | QCryptographicHash hash(QCryptographicHash::Md5); |
306 | hash.addData((const char *)img.bits(), img.bytesPerLine() * img.height()); |
307 | fe.hash = hash.result(); |
308 | } |
309 | m_savedFrameEvents.append(fe); |
310 | |
311 | // Deliver mouse events |
312 | filterEvents = false; |
313 | |
314 | if (!testscript) { |
315 | for (int ii = 0; ii < m_mouseEvents.count(); ++ii) { |
316 | MouseEvent &me = m_mouseEvents[ii]; |
317 | me.msec = msec; |
318 | QMouseEvent event(me.type, me.pos, me.button, me.buttons, me.modifiers); |
319 | |
320 | if (me.destination == View) { |
321 | QCoreApplication::sendEvent(m_view, &event); |
322 | } else { |
323 | QCoreApplication::sendEvent(m_view->viewport(), &event); |
324 | } |
325 | } |
326 | |
327 | for (int ii = 0; ii < m_keyEvents.count(); ++ii) { |
328 | KeyEvent &ke = m_keyEvents[ii]; |
329 | ke.msec = msec; |
330 | QKeyEvent event(ke.type, ke.key, ke.modifiers, ke.text, ke.autorep, ke.count); |
331 | |
332 | if (ke.destination == View) { |
333 | QCoreApplication::sendEvent(m_view, &event); |
334 | } else { |
335 | QCoreApplication::sendEvent(m_view->viewport(), &event); |
336 | } |
337 | } |
338 | m_savedMouseEvents.append(m_mouseEvents); |
339 | m_savedKeyEvents.append(m_keyEvents); |
340 | } |
341 | |
342 | m_mouseEvents.clear(); |
343 | m_keyEvents.clear(); |
344 | |
345 | // Advance test script |
346 | while (testscript && testscript->count() > testscriptidx) { |
347 | |
348 | QObject *event = testscript->event(testscriptidx); |
349 | |
350 | if (QDeclarativeVisualTestFrame *frame = qobject_cast<QDeclarativeVisualTestFrame *>(event)) { |
351 | if (frame->msec() < msec) { |
352 | if (options & QDeclarativeViewer::TestImages && !(options & QDeclarativeViewer::Record)) { |
353 | qWarning() << "QDeclarativeTester("<< m_script << "): Extra frame. Seen:" |
354 | << msec << "Expected:"<< frame->msec(); |
355 | imagefailure(); |
356 | } |
357 | } else if (frame->msec() == msec) { |
358 | if (!frame->hash().isEmpty() && frame->hash().toUtf8() != fe.hash.toHex()) { |
359 | if (options & QDeclarativeViewer::TestImages && !(options & QDeclarativeViewer::Record)) { |
360 | qWarning() << "QDeclarativeTester("<< m_script << "): Mismatched frame hash at"<< msec |
361 | << ". Seen:"<< fe.hash.toHex() |
362 | << "Expected:"<< frame->hash().toUtf8(); |
363 | imagefailure(); |
364 | } |
365 | } |
366 | } else if (frame->msec() > msec) { |
367 | break; |
368 | } |
369 | |
370 | if (options & QDeclarativeViewer::TestImages && !(options & QDeclarativeViewer::Record) && !frame->image().isEmpty()) { |
371 | QImage goodImage(frame->image().toLocalFile()); |
372 | if (frame->msec() == 16 && goodImage.size() != img.size()){ |
373 | //Also an image mismatch, but this warning is more informative. Only checked at start though. |
374 | qWarning() << "QDeclarativeTester("<< m_script << "): Size mismatch. This test must be run at "<< goodImage.size(); |
375 | imagefailure(); |
376 | } |
377 | if (goodImage != img) { |
378 | QString reject(frame->image().toLocalFile() + QLatin1String(".reject.png")); |
379 | qWarning() << "QDeclarativeTester("<< m_script << "): Image mismatch. Reject saved to:" |
380 | << reject; |
381 | img.save(reject); |
382 | bool doDiff = (goodImage.size() == img.size()); |
383 | if (doDiff) { |
384 | QImage diffimg(m_view->width(), m_view->height(), QImage::Format_RGB32); |
385 | diffimg.fill(qRgb(255,255,255)); |
386 | QPainter p(&diffimg); |
387 | int diffCount = 0; |
388 | for (int x = 0; x < img.width(); ++x) { |
389 | for (int y = 0; y < img.height(); ++y) { |
390 | if (goodImage.pixel(x,y) != img.pixel(x,y)) { |
391 | ++diffCount; |
392 | p.drawPoint(x,y); |
393 | } |
394 | } |
395 | } |
396 | QString diff(frame->image().toLocalFile() + QLatin1String(".diff.png")); |
397 | diffimg.save(diff); |
398 | qWarning().nospace() << " Diff ("<< diffCount << " pixels differed) saved to: "<< diff; |
399 | } |
400 | imagefailure(); |
401 | } |
402 | } |
403 | } else if (QDeclarativeVisualTestMouse *mouse = qobject_cast<QDeclarativeVisualTestMouse *>(event)) { |
404 | QPoint pos(mouse->x(), mouse->y()); |
405 | QPoint globalPos = m_view->mapToGlobal(QPoint(0, 0)) + pos; |
406 | QMouseEvent event((QEvent::Type)mouse->type(), pos, globalPos, (Qt::MouseButton)mouse->button(), (Qt::MouseButtons)mouse->buttons(), (Qt::KeyboardModifiers)mouse->modifiers()); |
407 | |
408 | MouseEvent me(&event); |
409 | me.msec = msec; |
410 | if (!mouse->sendToViewport()) { |
411 | QCoreApplication::sendEvent(m_view, &event); |
412 | me.destination = View; |
413 | } else { |
414 | QCoreApplication::sendEvent(m_view->viewport(), &event); |
415 | me.destination = ViewPort; |
416 | } |
417 | m_savedMouseEvents.append(me); |
418 | } else if (QDeclarativeVisualTestKey *key = qobject_cast<QDeclarativeVisualTestKey *>(event)) { |
419 | |
420 | QKeyEvent event((QEvent::Type)key->type(), key->key(), (Qt::KeyboardModifiers)key->modifiers(), QString::fromUtf8(QByteArray::fromHex(key->text().toUtf8())), key->autorep(), key->count()); |
421 | |
422 | KeyEvent ke(&event); |
423 | ke.msec = msec; |
424 | if (!key->sendToViewport()) { |
425 | QCoreApplication::sendEvent(m_view, &event); |
426 | ke.destination = View; |
427 | } else { |
428 | QCoreApplication::sendEvent(m_view->viewport(), &event); |
429 | ke.destination = ViewPort; |
430 | } |
431 | m_savedKeyEvents.append(ke); |
432 | } |
433 | testscriptidx++; |
434 | } |
435 | |
436 | filterEvents = true; |
437 | |
438 | if (testscript && testscript->count() <= testscriptidx) { |
439 | //if (msec == 16) //for a snapshot, leave it up long enough to see |
440 | // (void)::sleep(1); |
441 | complete(); |
442 | } |
443 | } |
444 | |
445 | void QDeclarativeTester::registerTypes() |
446 | { |
447 | qmlRegisterType<QDeclarativeVisualTest>("Qt.VisualTest", 4,7, "VisualTest"); |
448 | qmlRegisterType<QDeclarativeVisualTestFrame>("Qt.VisualTest", 4,7, "Frame"); |
449 | qmlRegisterType<QDeclarativeVisualTestMouse>("Qt.VisualTest", 4,7, "Mouse"); |
450 | qmlRegisterType<QDeclarativeVisualTestKey>("Qt.VisualTest", 4,7, "Key"); |
451 | } |
452 | |
453 | QT_END_NAMESPACE |
454 |
Warning: That file was not part of the compilation database. It may have many parsing errors.