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