source: trunk/tools/qml/qdeclarativetester.cpp@ 987

Last change on this file since 987 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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