summaryrefslogtreecommitdiffstats
path: root/tools/qmlviewer
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2009-09-21 07:09:33 (GMT)
committerAaron Kennedy <aaron.kennedy@nokia.com>2009-09-21 07:09:33 (GMT)
commita14765de5b1bb26b4cbc6ebbdee71145e54650a8 (patch)
tree5ce3cd3acb9448a3d86eef96ac1f4129ce3b8936 /tools/qmlviewer
parent26bd84ec3625c825c04228b76dd37cd5e75fb36b (diff)
downloadQt-a14765de5b1bb26b4cbc6ebbdee71145e54650a8.zip
Qt-a14765de5b1bb26b4cbc6ebbdee71145e54650a8.tar.gz
Qt-a14765de5b1bb26b4cbc6ebbdee71145e54650a8.tar.bz2
Basic implementation of visual "test scripts"
Test scripts allow the qmlviewer to record a set of input actions, and the resulting visual frames.
Diffstat (limited to 'tools/qmlviewer')
-rw-r--r--tools/qmlviewer/main.cpp87
-rw-r--r--tools/qmlviewer/qfxtester.cpp306
-rw-r--r--tools/qmlviewer/qfxtester.h230
-rw-r--r--tools/qmlviewer/qmlviewer.cpp28
-rw-r--r--tools/qmlviewer/qmlviewer.h17
-rw-r--r--tools/qmlviewer/qmlviewer.pro8
6 files changed, 663 insertions, 13 deletions
diff --git a/tools/qmlviewer/main.cpp b/tools/qmlviewer/main.cpp
index c5bf868..7df1179 100644
--- a/tools/qmlviewer/main.cpp
+++ b/tools/qmlviewer/main.cpp
@@ -43,11 +43,28 @@ void usage()
qWarning(" -translation <translationfile> ........... set the language to run in");
qWarning(" -L <directory> ........................... prepend to the library search path");
qWarning(" -opengl .................................. use a QGLWidget for the viewport");
+ qWarning(" -script <path> ........................... set the script to use");
+ qWarning(" -scriptopts <options>|help ............... set the script options to use");
+
qWarning(" ");
qWarning(" Press F1 for interactive help");
exit(1);
}
+void scriptOptsUsage()
+{
+ qWarning("Usage: qmlviewer -scriptopts <option>[,<option>...] ...");
+ qWarning(" options:");
+ qWarning(" record ................................... record a new script");
+ qWarning(" play ..................................... playback an existing script");
+ qWarning(" testimages ............................... compare images on playback");
+ qWarning(" exitoncomplete ........................... cleanly exit the viewer on script completion");
+ qWarning(" exitonfailure ............................ immediately exit the viewer on script failure");
+ qWarning(" ");
+ qWarning(" One of record or play must be specified.");
+ exit(1);
+}
+
int main(int argc, char ** argv)
{
//### default to using raster graphics backend for now
@@ -78,6 +95,9 @@ int main(int argc, char ** argv)
QStringList recordargs;
QStringList libraries;
QString skin;
+ QString script;
+ QString scriptopts;
+ bool runScript = false;
bool devkeys = false;
int cache = 0;
QString translationFile;
@@ -85,26 +105,34 @@ int main(int argc, char ** argv)
bool fullScreen = false;
for (int i = 1; i < argc; ++i) {
+ bool lastArg = (i == argc - 1);
QString arg = argv[i];
if (arg == "-frameless") {
frameless = true;
} else if (arg == "-fullscreen") {
fullScreen = true;
} else if (arg == "-skin") {
+ if (lastArg) usage();
skin = QString(argv[++i]);
} else if (arg == "-resizeview") {
resizeview = true;
} else if (arg == "-netcache") {
+ if (lastArg) usage();
cache = QString(argv[++i]).toInt();
} else if (arg == "-recordrate") {
+ if (lastArg) usage();
fps = QString(argv[++i]).toDouble();
} else if (arg == "-recordfile") {
+ if (lastArg) usage();
recordfile = QString(argv[++i]);
} else if (arg == "-record") {
+ if (lastArg) usage();
recordargs << QString(argv[++i]);
} else if (arg == "-recorddither") {
+ if (lastArg) usage();
dither = QString(argv[++i]);
} else if (arg == "-autorecord") {
+ if (lastArg) usage();
QString range = QString(argv[++i]);
int dash = range.indexOf('-');
if (dash > 0)
@@ -116,14 +144,27 @@ int main(int argc, char ** argv)
fprintf(stderr, "Qt Declarative UI Viewer version %s\n", QT_VERSION_STR);
return 0;
} else if (arg == "-translation") {
- if(i + 1 >= argc)
- usage();
- translationFile = argv[i + 1];
- ++i;
+ if (lastArg) usage();
+ translationFile = argv[++i];
} else if (arg == "-opengl") {
useGL = true;
} else if (arg == "-L") {
+ if (lastArg) usage();
libraries << QString(argv[++i]);
+ } else if (arg == "-script") {
+ if (lastArg) usage();
+ script = QString(argv[++i]);
+ } else if (arg == "-scriptopts") {
+ if (lastArg) usage();
+ scriptopts = QString(argv[++i]);
+ } else if (arg == "-savescript") {
+ if (lastArg) usage();
+ script = QString(argv[++i]);
+ runScript = false;
+ } else if (arg == "-playscript") {
+ if (lastArg) usage();
+ script = QString(argv[++i]);
+ runScript = true;
} else if (arg[0] != '-') {
fileName = arg;
} else if (1 || arg == "-help") {
@@ -138,6 +179,44 @@ int main(int argc, char ** argv)
}
QmlViewer viewer(0, frameless ? Qt::FramelessWindowHint : Qt::Widget);
+ if (!scriptopts.isEmpty()) {
+
+ QStringList options =
+ scriptopts.split(QLatin1Char(','), QString::SkipEmptyParts);
+
+ QmlViewer::ScriptOptions scriptOptions = 0;
+ for (int i = 0; i < options.count(); ++i) {
+ const QString &option = options.at(i);
+ if (option == QLatin1String("help")) {
+ scriptOptsUsage();
+ } else if (option == QLatin1String("play")) {
+ if (scriptOptions & QmlViewer::Record) scriptOptsUsage();
+ scriptOptions |= QmlViewer::Play;
+ } else if (option == QLatin1String("record")) {
+ if (scriptOptions & QmlViewer::Play) scriptOptsUsage();
+ scriptOptions |= QmlViewer::Record;
+ } else if (option == QLatin1String("testimages")) {
+ scriptOptions |= QmlViewer::TestImages;
+ } else if (option == QLatin1String("exitoncomplete")) {
+ scriptOptions |= QmlViewer::ExitOnComplete;
+ } else if (option == QLatin1String("exitonfailure")) {
+ scriptOptions |= QmlViewer::ExitOnFailure;
+ } else {
+ scriptOptsUsage();
+ }
+ }
+
+ if (script.isEmpty())
+ usage();
+
+ if (!(scriptOptions & QmlViewer::Record) && !(scriptOptions & QmlViewer::Play))
+ scriptOptsUsage();
+ viewer.setScriptOptions(scriptOptions);
+ viewer.setScript(script);
+ } else if (!script.isEmpty()) {
+ usage();
+ }
+
viewer.setUseGL(useGL);
foreach (QString lib, libraries)
viewer.addLibraryPath(lib);
diff --git a/tools/qmlviewer/qfxtester.cpp b/tools/qmlviewer/qfxtester.cpp
new file mode 100644
index 0000000..3732aaf
--- /dev/null
+++ b/tools/qmlviewer/qfxtester.cpp
@@ -0,0 +1,306 @@
+/****************************************************************************
+**
+** Copyright (C) 1992-$THISYEAR$ $TROLLTECH$. All rights reserved.
+**
+** This file is part of the $MODULE$ of the Qt Toolkit.
+**
+** $TROLLTECH_DUAL_LICENSE$
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#include <qfxtester.h>
+#include <QDebug>
+#include <QApplication>
+#include <QFxView>
+#include <QFile>
+#include <QmlComponent>
+#include <QCryptographicHash>
+#include <private/qabstractanimation_p.h>
+#include <private/qfxitem_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QML_DEFINE_TYPE(Qt.VisualTest, 4, 6, (QT_VERSION&0x00ff00)>>8, VisualTest, QFxVisualTest);
+QML_DEFINE_TYPE(Qt.VisualTest, 4, 6, (QT_VERSION&0x00ff00)>>8, Frame, QFxVisualTestFrame);
+QML_DEFINE_TYPE(Qt.VisualTest, 4, 6, (QT_VERSION&0x00ff00)>>8, Mouse, QFxVisualTestMouse);
+QML_DEFINE_TYPE(Qt.VisualTest, 4, 6, (QT_VERSION&0x00ff00)>>8, Key, QFxVisualTestKey);
+
+QFxTester::QFxTester(const QString &script, QmlViewer::ScriptOptions opts,
+ QFxView *parent)
+: QAbstractAnimation(parent), m_view(parent), filterEvents(true), options(opts),
+ testscript(0), hasFailed(false)
+{
+ parent->viewport()->installEventFilter(this);
+ parent->installEventFilter(this);
+ QUnifiedTimer::instance()->setConsistentTiming(true);
+ if (options & QmlViewer::Play)
+ this->run(script);
+ start();
+}
+
+int QFxTester::duration() const
+{
+ return -1;
+}
+
+void QFxTester::addMouseEvent(Destination dest, QMouseEvent *me)
+{
+ MouseEvent e(me);
+ e.destination = dest;
+ m_mouseEvents << e;
+}
+
+void QFxTester::addKeyEvent(Destination dest, QKeyEvent *ke)
+{
+ KeyEvent e(ke);
+ e.destination = dest;
+ m_keyEvents << e;
+}
+
+bool QFxTester::eventFilter(QObject *o, QEvent *e)
+{
+ if (!filterEvents)
+ return false;
+
+ Destination destination;
+ if (o == m_view) {
+ destination = View;
+ } else if (o == m_view->viewport()) {
+ destination = ViewPort;
+ } else {
+ return false;
+ }
+
+ switch (e->type()) {
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ addKeyEvent(destination, (QKeyEvent *)e);
+ return true;
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseMove:
+ case QEvent::MouseButtonDblClick:
+ addMouseEvent(destination, (QMouseEvent *)e);
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+void QFxTester::executefailure()
+{
+ hasFailed = true;
+
+ if (options & QmlViewer::ExitOnFailure)
+ exit(-1);
+}
+
+void QFxTester::imagefailure()
+{
+ hasFailed = true;
+
+ if (options & QmlViewer::ExitOnFailure)
+ exit(-1);
+}
+
+void QFxTester::complete()
+{
+ if (options & QmlViewer::ExitOnComplete)
+ QApplication::exit(hasFailed?-1:0);
+}
+
+void QFxTester::run(const QString &name)
+{
+ QmlComponent c(m_view->engine(), name + QLatin1String(".qml"));
+
+ testscript = qobject_cast<QFxVisualTest *>(c.create());
+ if (testscript) testscript->setParent(this);
+ testscriptidx = 0;
+}
+
+void QFxTester::save(const QString &name)
+{
+ QFile file(name + QLatin1String(".qml"));
+ file.open(QIODevice::WriteOnly);
+ QTextStream ts(&file);
+
+ ts << "import Qt.VisualTest 4.6\n\n";
+ ts << "VisualTest {\n";
+
+ int imgCount = 0;
+ for (int ii = 0; ii < m_savedFrameEvents.count(); ++ii) {
+ const FrameEvent &fe = m_savedFrameEvents.at(ii);
+ ts << " Frame {\n";
+ ts << " msec: " << fe.msec << "\n";
+ if (!fe.hash.isEmpty()) {
+ ts << " hash: \"" << fe.hash.toHex() << "\"\n";
+ } else if (!fe.image.isNull()) {
+ QString filename = name + "." + QString::number(imgCount++) + ".png";
+ fe.image.save(filename);
+ ts << " image: \"" << filename << "\"\n";
+ }
+ ts << " }\n";
+
+ while (!m_savedMouseEvents.isEmpty() &&
+ m_savedMouseEvents.first().msec == fe.msec) {
+ MouseEvent me = m_savedMouseEvents.takeFirst();
+
+ ts << " Mouse {\n";
+ ts << " type: " << me.type << "\n";
+ ts << " button: " << me.button << "\n";
+ ts << " buttons: " << me.buttons << "\n";
+ ts << " x: " << me.pos.x() << "; y: " << me.pos.y() << "\n";
+ ts << " modifiers: " << me.modifiers << "\n";
+ if (me.destination == ViewPort)
+ ts << " sendToViewport: true\n";
+ ts << " }\n";
+ }
+
+ while (!m_savedKeyEvents.isEmpty() &&
+ m_savedKeyEvents.first().msec == fe.msec) {
+ KeyEvent ke = m_savedKeyEvents.takeFirst();
+
+ ts << " Key {\n";
+ ts << " type: " << ke.type << "\n";
+ ts << " key: " << ke.key << "\n";
+ ts << " modifiers: " << ke.modifiers << "\n";
+ ts << " text: \"" << ke.text.toUtf8().toHex() << "\"\n";
+ ts << " autorep: " << (ke.autorep?"true":"false") << "\n";
+ ts << " count: " << ke.count << "\n";
+ if (ke.destination == ViewPort)
+ ts << " sendToViewport: true\n";
+ ts << " }\n";
+ }
+ }
+ m_savedFrameEvents.clear();
+
+ ts << "}\n";
+ file.close();
+}
+
+void QFxTester::updateCurrentTime(int msec)
+{
+ QFxItemPrivate::setConsistentTime(msec);
+
+ QImage img(m_view->width(), m_view->height(), QImage::Format_RGB32);
+
+ QPainter p(&img);
+ m_view->render(&p);
+
+ FrameEvent fe;
+ fe.msec = msec;
+ if (msec == 0) {
+ // Skip first frame
+ } else if (0 == (m_savedFrameEvents.count() % 60)) {
+ fe.image = img;
+ } else {
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ hash.addData((const char *)img.bits(), img.bytesPerLine() * img.height());
+ fe.hash = hash.result();
+ }
+ m_savedFrameEvents.append(fe);
+
+ // Deliver mouse events
+ filterEvents = false;
+
+ if (!testscript) {
+ for (int ii = 0; ii < m_mouseEvents.count(); ++ii) {
+ MouseEvent &me = m_mouseEvents[ii];
+ me.msec = msec;
+ QMouseEvent event(me.type, me.pos, me.button, me.buttons, me.modifiers);
+
+ if (me.destination == View) {
+ QCoreApplication::sendEvent(m_view, &event);
+ } else {
+ QCoreApplication::sendEvent(m_view->viewport(), &event);
+ }
+ }
+
+ for (int ii = 0; ii < m_keyEvents.count(); ++ii) {
+ KeyEvent &ke = m_keyEvents[ii];
+ ke.msec = msec;
+ QKeyEvent event(ke.type, ke.key, ke.modifiers, ke.text, ke.autorep, ke.count);
+
+ if (ke.destination == View) {
+ QCoreApplication::sendEvent(m_view, &event);
+ } else {
+ QCoreApplication::sendEvent(m_view->viewport(), &event);
+ }
+ }
+ m_savedMouseEvents.append(m_mouseEvents);
+ m_savedKeyEvents.append(m_keyEvents);
+ }
+
+ m_mouseEvents.clear();
+ m_keyEvents.clear();
+
+ // Advance test script
+ static int imgCount = 0;
+ while (testscript && testscript->count() > testscriptidx) {
+
+ QObject *event = testscript->event(testscriptidx);
+
+ if (QFxVisualTestFrame *frame = qobject_cast<QFxVisualTestFrame *>(event)) {
+ if (frame->msec() < msec) {
+ if (options & QmlViewer::TestImages) {
+ qWarning() << "QFxTester: Extra frame. Seen:"
+ << msec << "Expected:" << frame->msec();
+ imagefailure();
+ }
+ } else if (frame->msec() == msec) {
+ if (frame->hash().toUtf8() != fe.hash.toHex()) {
+ if (options & QmlViewer::TestImages) {
+ qWarning() << "QFxTester: Mismatched frame hash. Seen:"
+ << fe.hash.toHex() << "Expected:"
+ << frame->hash().toUtf8();
+ imagefailure();
+ }
+ }
+ } else if (frame->msec() > msec) {
+ break;
+ }
+
+ if (options & QmlViewer::TestImages && !frame->image().isEmpty()) {
+ QImage goodImage(frame->image());
+ if (goodImage != img) {
+ QString reject(frame->image() + ".reject.png");
+ qWarning() << "QFxTester: Image mismatch. Reject saved to:"
+ << reject;
+ img.save(reject);
+ imagefailure();
+ }
+ }
+ } else if (QFxVisualTestMouse *mouse = qobject_cast<QFxVisualTestMouse *>(event)) {
+ QPoint pos(mouse->x(), mouse->y());
+ QPoint globalPos = m_view->mapToGlobal(QPoint(0, 0)) + pos;
+ QMouseEvent event((QEvent::Type)mouse->type(), pos, globalPos, (Qt::MouseButton)mouse->button(), (Qt::MouseButtons)mouse->buttons(), (Qt::KeyboardModifiers)mouse->modifiers());
+
+ if (!mouse->sendToViewport()) {
+ QCoreApplication::sendEvent(m_view, &event);
+ } else {
+ QCoreApplication::sendEvent(m_view->viewport(), &event);
+ }
+ } else if (QFxVisualTestKey *key = qobject_cast<QFxVisualTestKey *>(event)) {
+
+ QKeyEvent event((QEvent::Type)key->type(), key->key(), (Qt::KeyboardModifiers)key->modifiers(), QString::fromUtf8(QByteArray::fromHex(key->text().toUtf8())), key->autorep(), key->count());
+
+ if (!key->sendToViewport()) {
+ QCoreApplication::sendEvent(m_view, &event);
+ } else {
+ QCoreApplication::sendEvent(m_view->viewport(), &event);
+ }
+ }
+ testscriptidx++;
+ }
+
+ filterEvents = true;
+
+ if (testscript && testscript->count() <= testscriptidx)
+ complete();
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qmlviewer/qfxtester.h b/tools/qmlviewer/qfxtester.h
new file mode 100644
index 0000000..e37d6aa
--- /dev/null
+++ b/tools/qmlviewer/qfxtester.h
@@ -0,0 +1,230 @@
+/****************************************************************************
+**
+** Copyright (C) 1992-$THISYEAR$ $TROLLTECH$. All rights reserved.
+**
+** This file is part of the $MODULE$ of the Qt Toolkit.
+**
+** $TROLLTECH_DUAL_LICENSE$
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+****************************************************************************/
+
+#ifndef QFXTESTER_H
+#define QFXTESTER_H
+
+#include <QEvent>
+#include <QMouseEvent>
+#include <QKeyEvent>
+#include <qmlviewer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFxVisualTest : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QList<QObject *>* events READ events CONSTANT)
+ Q_CLASSINFO("DefaultProperty", "events")
+public:
+ QFxVisualTest() {}
+
+ QList<QObject *> *events() { return &m_events; }
+
+ int count() const { return m_events.count(); }
+ QObject *event(int idx) { return m_events.at(idx); }
+
+private:
+ QList<QObject *> m_events;
+};
+QML_DECLARE_TYPE(QFxVisualTest)
+
+class QFxVisualTestFrame : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int msec READ msec WRITE setMsec)
+ Q_PROPERTY(QString hash READ hash WRITE setHash)
+ Q_PROPERTY(QString image READ image WRITE setImage)
+public:
+ QFxVisualTestFrame() : m_msec(-1) {}
+
+ int msec() const { return m_msec; }
+ void setMsec(int m) { m_msec = m; }
+
+ QString hash() const { return m_hash; }
+ void setHash(const QString &hash) { m_hash = hash; }
+
+ QString image() const { return m_image; }
+ void setImage(const QString &image) { m_image = image; }
+
+private:
+ int m_msec;
+ QString m_hash;
+ QString m_image;
+};
+QML_DECLARE_TYPE(QFxVisualTestFrame)
+
+class QFxVisualTestMouse : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int type READ type WRITE setType)
+ Q_PROPERTY(int button READ button WRITE setButton)
+ Q_PROPERTY(int buttons READ buttons WRITE setButtons)
+ Q_PROPERTY(int x READ x WRITE setX)
+ Q_PROPERTY(int y READ y WRITE setY)
+ Q_PROPERTY(int modifiers READ modifiers WRITE setModifiers)
+ Q_PROPERTY(bool sendToViewport READ sendToViewport WRITE setSendToViewport)
+public:
+ QFxVisualTestMouse() : m_type(0), m_button(0), m_buttons(0), m_x(0), m_y(0), m_modifiers(0), m_viewport(false) {}
+
+ int type() const { return m_type; }
+ void setType(int t) { m_type = t; }
+
+ int button() const { return m_button; }
+ void setButton(int b) { m_button = b; }
+
+ int buttons() const { return m_buttons; }
+ void setButtons(int b) { m_buttons = b; }
+
+ int x() const { return m_x; }
+ void setX(int x) { m_x = x; }
+
+ int y() const { return m_y; }
+ void setY(int y) { m_y = y; }
+
+ int modifiers() const { return m_modifiers; }
+ void setModifiers(int modifiers) { m_modifiers = modifiers; }
+
+ bool sendToViewport() const { return m_viewport; }
+ void setSendToViewport(bool v) { m_viewport = v; }
+private:
+ int m_type;
+ int m_button;
+ int m_buttons;
+ int m_x;
+ int m_y;
+ int m_modifiers;
+ bool m_viewport;
+};
+QML_DECLARE_TYPE(QFxVisualTestMouse)
+
+class QFxVisualTestKey : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int type READ type WRITE setType)
+ Q_PROPERTY(int key READ key WRITE setKey)
+ Q_PROPERTY(int modifiers READ modifiers WRITE setModifiers)
+ Q_PROPERTY(QString text READ text WRITE setText)
+ Q_PROPERTY(bool autorep READ autorep WRITE setAutorep)
+ Q_PROPERTY(int count READ count WRITE setCount)
+ Q_PROPERTY(bool sendToViewport READ sendToViewport WRITE setSendToViewport)
+public:
+ QFxVisualTestKey() : m_type(0), m_key(0), m_modifiers(0), m_autorep(false), m_count(0), m_viewport(false) {}
+
+ int type() const { return m_type; }
+ void setType(int t) { m_type = t; }
+
+ int key() const { return m_key; }
+ void setKey(int k) { m_key = k; }
+
+ int modifiers() const { return m_modifiers; }
+ void setModifiers(int m) { m_modifiers = m; }
+
+ QString text() const { return m_text; }
+ void setText(const QString &t) { m_text = t; }
+
+ bool autorep() const { return m_autorep; }
+ void setAutorep(bool a) { m_autorep = a; }
+
+ int count() const { return m_count; }
+ void setCount(int c) { m_count = c; }
+
+ bool sendToViewport() const { return m_viewport; }
+ void setSendToViewport(bool v) { m_viewport = v; }
+private:
+ int m_type;
+ int m_key;
+ int m_modifiers;
+ QString m_text;
+ bool m_autorep;
+ int m_count;
+ bool m_viewport;
+};
+QML_DECLARE_TYPE(QFxVisualTestKey)
+
+class QFxTester : public QAbstractAnimation
+{
+public:
+ QFxTester(const QString &script, QmlViewer::ScriptOptions options, QFxView *parent);
+
+ virtual int duration() const;
+
+ void run(const QString &);
+ void save(const QString &);
+
+ void executefailure();
+protected:
+ virtual void updateCurrentTime(int msecs);
+ virtual bool eventFilter(QObject *, QEvent *);
+
+private:
+ void imagefailure();
+ void complete();
+
+ enum Destination { View, ViewPort };
+ void addKeyEvent(Destination, QKeyEvent *);
+ void addMouseEvent(Destination, QMouseEvent *);
+ QFxView *m_view;
+
+ struct MouseEvent {
+ MouseEvent(QMouseEvent *e)
+ : type(e->type()), button(e->button()), buttons(e->buttons()),
+ pos(e->pos()), modifiers(e->modifiers()), destination(View) {}
+
+ QEvent::Type type;
+ Qt::MouseButton button;
+ Qt::MouseButtons buttons;
+ QPoint pos;
+ Qt::KeyboardModifiers modifiers;
+ Destination destination;
+
+ int msec;
+ };
+ struct KeyEvent {
+ KeyEvent(QKeyEvent *e)
+ : type(e->type()), key(e->key()), modifiers(e->modifiers()), text(e->text()),
+ autorep(e->isAutoRepeat()), count(e->count()), destination(View) {}
+ QEvent::Type type;
+ int key;
+ Qt::KeyboardModifiers modifiers;
+ QString text;
+ bool autorep;
+ ushort count;
+ Destination destination;
+
+ int msec;
+ };
+ struct FrameEvent {
+ QImage image;
+ QByteArray hash;
+ int msec;
+ };
+ QList<MouseEvent> m_mouseEvents;
+ QList<KeyEvent> m_keyEvents;
+
+ QList<MouseEvent> m_savedMouseEvents;
+ QList<KeyEvent> m_savedKeyEvents;
+ QList<FrameEvent> m_savedFrameEvents;
+ bool filterEvents;
+
+ QmlViewer::ScriptOptions options;
+ int testscriptidx;
+ QFxVisualTest *testscript;
+
+ bool hasFailed;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QFXTESTER_H
diff --git a/tools/qmlviewer/qmlviewer.cpp b/tools/qmlviewer/qmlviewer.cpp
index 1c0f496..2894f1d 100644
--- a/tools/qmlviewer/qmlviewer.cpp
+++ b/tools/qmlviewer/qmlviewer.cpp
@@ -19,8 +19,8 @@
#include <QtDeclarative/qmlengine.h>
#include "qml.h"
#include <private/qperformancelog_p.h>
-#include <QAbstractAnimation>
#include <private/qabstractanimation_p.h>
+#include <QAbstractAnimation>
#include "deviceskin.h"
#include <QSettings>
@@ -51,6 +51,8 @@
#include <QGLWidget>
#endif
+#include <qfxtester.h>
+
QT_BEGIN_NAMESPACE
class PreviewDeviceSkin : public DeviceSkin
@@ -251,7 +253,8 @@ QString QmlViewer::getVideoFileName()
QmlViewer::QmlViewer(QWidget *parent, Qt::WindowFlags flags)
- : QWidget(parent, flags), frame_stream(0), scaleSkin(true), mb(0)
+ : QWidget(parent, flags), frame_stream(0), scaleSkin(true), mb(0), m_scriptOptions(0),
+ tester(0)
{
devicemode = false;
skin = 0;
@@ -291,6 +294,7 @@ QmlViewer::QmlViewer(QWidget *parent, Qt::WindowFlags flags)
canvas->setFocus();
QObject::connect(canvas, SIGNAL(sceneResized(QSize)), this, SLOT(sceneResized(QSize)));
+ QObject::connect(canvas, SIGNAL(errors(QList<QmlError>)), this, SLOT(executeErrors()));
QVBoxLayout *layout = new QVBoxLayout;
layout->setMargin(0);
@@ -589,10 +593,18 @@ void QmlViewer::open()
}
}
+void QmlViewer::executeErrors()
+{
+ if (tester) tester->executefailure();
+}
+
void QmlViewer::openQml(const QString& fileName)
{
setWindowTitle(tr("%1 - Qt Declarative UI Viewer").arg(fileName));
+ if (!m_script.isEmpty())
+ tester = new QFxTester(m_script, m_scriptOptions, canvas);
+
canvas->reset();
currentFileName = fileName;
@@ -617,6 +629,7 @@ void QmlViewer::openQml(const QString& fileName)
foreach (const QmlError &error, errors) {
qWarning() << error;
}
+ if (tester) tester->executefailure();
}
if (dummyData) {
@@ -762,17 +775,19 @@ void QmlViewer::keyPressEvent(QKeyEvent *event)
exit(0);
else if (event->key() == Qt::Key_F1 || (event->key() == Qt::Key_1 && devicemode)) {
qDebug() << "F1 - help\n"
- << "F2 - toggle video recording\n"
+ << "F2 - save test script\n"
<< "F3 - take PNG snapshot\n"
<< "F4 - show items and state\n"
<< "F5 - reload QML\n"
<< "F6 - show object tree\n"
<< "F7 - show timing\n"
<< "F8 - show performance (if available)\n"
+ << "F9 - toggle video recording\n"
<< "device keys: 0=quit, 1..8=F1..F8"
;
- } else if (event->key() == Qt::Key_F2 || (event->key() == Qt::Key_2 && devicemode)) {
- toggleRecording();
+ } else if (event->key() == Qt::Key_F2 || (event->key() == Qt::Key_3 && devicemode)) {
+ if (tester && m_scriptOptions & Record)
+ tester->save(m_script);
} else if (event->key() == Qt::Key_F3 || (event->key() == Qt::Key_3 && devicemode)) {
takeSnapShot();
} else if (event->key() == Qt::Key_F5 || (event->key() == Qt::Key_5 && devicemode)) {
@@ -780,6 +795,8 @@ void QmlViewer::keyPressEvent(QKeyEvent *event)
} else if (event->key() == Qt::Key_F8 || (event->key() == Qt::Key_8 && devicemode)) {
QPerformanceLog::displayData();
QPerformanceLog::clear();
+ } else if (event->key() == Qt::Key_F9 || (event->key() == Qt::Key_9 && devicemode)) {
+ toggleRecording();
}
QWidget::keyPressEvent(event);
@@ -1051,7 +1068,6 @@ void QmlViewer::setUseGL(bool useGL)
}
#endif
}
-
QT_END_NAMESPACE
#include "qmlviewer.moc"
diff --git a/tools/qmlviewer/qmlviewer.h b/tools/qmlviewer/qmlviewer.h
index e85acfa..f0e997c 100644
--- a/tools/qmlviewer/qmlviewer.h
+++ b/tools/qmlviewer/qmlviewer.h
@@ -26,6 +26,7 @@ class PreviewDeviceSkin;
class QFxTestEngine;
class QProcess;
class RecordingDialog;
+class QFxTester;
class QmlViewer : public QWidget
{
@@ -33,6 +34,16 @@ Q_OBJECT
public:
QmlViewer(QWidget *parent=0, Qt::WindowFlags flags=0);
+ enum ScriptOption {
+ Play = 0x00000001,
+ Record = 0x00000002,
+ TestImages = 0x00000004,
+ ExitOnComplete = 0x00000008,
+ ExitOnFailure = 0x00000010
+ };
+ Q_DECLARE_FLAGS(ScriptOptions, ScriptOption)
+ void setScript(const QString &s) { m_script = s; }
+ void setScriptOptions(ScriptOptions opt) { m_scriptOptions = opt; }
void setRecordDither(const QString& s) { record_dither = s; }
void setRecordRate(int fps);
void setRecordFile(const QString&);
@@ -62,6 +73,7 @@ public slots:
void showProxySettings ();
void proxySettingsChanged ();
void setScaleView();
+ void executeErrors();
protected:
virtual void keyPressEvent(QKeyEvent *);
@@ -109,7 +121,12 @@ private:
QWidget *ffmpegHelpWindow;
bool ffmpegAvailable;
bool convertAvailable;
+
+ QString m_script;
+ ScriptOptions m_scriptOptions;
+ QFxTester *tester;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QmlViewer::ScriptOptions)
QT_END_NAMESPACE
diff --git a/tools/qmlviewer/qmlviewer.pro b/tools/qmlviewer/qmlviewer.pro
index 77cae97..899a3ab 100644
--- a/tools/qmlviewer/qmlviewer.pro
+++ b/tools/qmlviewer/qmlviewer.pro
@@ -14,10 +14,12 @@ contains(QT_CONFIG, opengl) {
# Input
HEADERS += qmlviewer.h \
- proxysettings.h
+ proxysettings.h \
+ qfxtester.h
SOURCES += main.cpp \
- qmlviewer.cpp \
- proxysettings.cpp
+ qmlviewer.cpp \
+ proxysettings.cpp \
+ qfxtester.cpp
FORMS = recopts.ui \
proxysettings.ui
include($$QT_SOURCE_TREE/tools/shared/deviceskin/deviceskin.pri)