summaryrefslogtreecommitdiffstats
path: root/src/remote/slave/lib/qtuitestwidgets.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/remote/slave/lib/qtuitestwidgets.cpp')
-rw-r--r--src/remote/slave/lib/qtuitestwidgets.cpp648
1 files changed, 648 insertions, 0 deletions
diff --git a/src/remote/slave/lib/qtuitestwidgets.cpp b/src/remote/slave/lib/qtuitestwidgets.cpp
new file mode 100644
index 0000000..b27e6f5
--- /dev/null
+++ b/src/remote/slave/lib/qtuitestwidgets.cpp
@@ -0,0 +1,648 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of QtSystemTest.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qinputgenerator_p.h"
+#include "qtuitestwidgetinterface.h"
+#include "qtuitestwidgets_p.h"
+#include "testquickfactory.h"
+
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
+ #include "qinputgenerator_qwindow_p.h"
+#else
+ #include "qinputgenerator_generic_p.h"
+#endif
+
+#include <QtSystemTest/QstDebug>
+
+/*
+ If the QTUITEST_INPUT_DELAY environment variable is set, all calls to keyClick etc will
+ immediately return but the resulting event won't be generated until QTUITEST_INPUT_DELAY
+ milliseconds have passed.
+ This can be used to simulate a slow system to catch race conditions.
+ For example, setting QTUITEST_INPUT_DELAY to 500 roughly simulates running with a remote X
+ server over a link with a round trip time of 250ms.
+*/
+
+#include <QDir>
+#include <QPluginLoader>
+#include <QPointer>
+#include <QStringList>
+#include <QTimer>
+#include <QPoint>
+
+/*!
+ \internal
+ \class QtUiTestWidgets
+ \brief The QtUiTestWidgets class provides an interface for creating and managing QtUiTest widgets.
+
+ QtUiTestWidgets manages the lifetime of all test widgets and allows
+ test widgets to simulate user interaction with the application.
+*/
+
+enum QtUiTestInputType { QtUiTestPress, QtUiTestRelease, QtUiTestClick, QtUiTestDClick, QtUiTestMove };
+struct QtUiTestKeyEvent
+{
+ QtUiTestInputType type;
+ Qt::Key key;
+ Qt::KeyboardModifiers mod;
+ QtUiTest::InputOption opt;
+};
+struct QtUiTestMouseEvent
+{
+ QtUiTestInputType type;
+ QPoint pos;
+ Qt::MouseButtons state;
+ QtUiTest::InputOption opt;
+};
+struct QtUiTestTouchEvent
+{
+ QtUiTestInputType type;
+ QVariantList points;
+};
+static int qtUiTestGetInputDelay()
+{
+ bool ok;
+ QByteArray value(qgetenv("QTUITEST_INPUT_DELAY"));
+ int ret = value.toInt(&ok);
+ if (!ok || ret < 0) ret = 0;//-1;
+ return ret;
+}
+static int qtUiTestInputDelay()
+{ static int ret = qtUiTestGetInputDelay(); return ret; }
+
+
+class QtUiTestWidgetsPrivate
+{
+public:
+ QtUiTestWidgetsPrivate(QtUiTestWidgets* parent);
+
+ static QList<QByteArray> allImplementedInterfaces(QObject *o);
+
+ void _q_objectDestroyed();
+
+ QString currentInputMethod() const;
+
+ QtUiTestWidgets* q;
+
+ QHash< QByteArray, QSet<QtUiTest::WidgetFactory*> > factories;
+ QHash< QObject*, QHash<QByteArray, QPointer<QObject> > > testWidgets;
+ QSet< QtUiTest::WidgetFactory* > factorySet;
+
+ int inputOptions;
+
+ QString errorString;
+ QString debugBuffer;
+
+ QInputGenerator *input;
+
+ QList<QtUiTestKeyEvent> pendingKeyEvents;
+ QList<QtUiTestMouseEvent> pendingMouseEvents;
+ QList<QtUiTestTouchEvent> pendingTouchEvents;
+ void _q_postNextKeyEvent();
+ void _q_postNextMouseEvent();
+ void _q_postNextTouchEvent();
+};
+
+class QTWOptStack
+{
+public:
+ QTWOptStack(QtUiTestWidgetsPrivate* obj,
+ QtUiTest::InputOption opt)
+ : d(obj), option(opt)
+ {
+ if (opt && (!(d->inputOptions & opt))) {
+ d->inputOptions |= opt;
+ } else {
+ d = 0;
+ }
+ }
+
+ ~QTWOptStack()
+ {
+ if (d) {
+ d->inputOptions &= ~option;
+ }
+ }
+
+ QtUiTestWidgetsPrivate *d;
+ QtUiTest::InputOption option;
+};
+
+QtUiTestWidgetsPrivate::QtUiTestWidgetsPrivate(QtUiTestWidgets* parent)
+ : q(parent),input(0),
+ inputOptions(QtUiTest::NoOptions)
+{
+ QTT_TRACE_FUNCTION();
+}
+
+QtUiTestWidgets::QtUiTestWidgets()
+ : QObject(),
+ d(new QtUiTestWidgetsPrivate(this))
+{
+ QTT_TRACE_FUNCTION();
+ d->input = new QInputGeneratorGeneric();
+ refreshPlugins();
+}
+
+QtUiTestWidgets::~QtUiTestWidgets()
+{
+ QTT_TRACE_FUNCTION();
+ delete d->input;
+ delete d;
+ d = 0;
+}
+
+/*!
+ Returns a static instance of QtUiTestWidgets.
+*/
+QtUiTestWidgets* QtUiTestWidgets::instance()
+{
+ static QtUiTestWidgets instance;
+ return &instance;
+}
+
+/*!
+ When an object is destroyed, deletes all test widgets pointing
+ to that object.
+*/
+void QtUiTestWidgetsPrivate::_q_objectDestroyed()
+{
+ QTT_TRACE_FUNCTION();
+ QHash< QByteArray, QPointer<QObject> > toDestroy
+ = testWidgets.take(q->sender());
+
+ foreach (QPointer<QObject> tw, toDestroy.values()) {
+ if (tw) delete tw;
+ }
+}
+
+/*!
+ \internal
+ Destroy all test widgets and unregister all factories.
+ After calling this, refreshPlugins() must be called to be able to
+ construct testwidgets from factories.
+
+ For testing purposes only.
+*/
+void QtUiTestWidgets::clear()
+{
+ QTT_TRACE_FUNCTION();
+ d->factories.clear();
+ d->factorySet.clear();
+
+ foreach (QObject *o, d->testWidgets.keys()) {
+ foreach (QPointer<QObject> tw, d->testWidgets[o].values()) {
+ if (tw && tw != o) delete tw;
+ }
+ }
+ d->testWidgets.clear();
+}
+
+void QtUiTestWidgets::refreshPlugins()
+{
+ registerFactory(new TestQuickFactory());
+}
+
+/*!
+ Registers \a factory as a factory class for constructing test widgets.
+
+ It is not necessary to explicitly call this from QtUiTest widget plugins.
+ This function should only be called if a QtUiTest::WidgetFactory has been
+ created without using the standard plugin interface.
+*/
+void
+QtUiTestWidgets::registerFactory(QtUiTest::WidgetFactory* factory)
+{
+ QTT_TRACE_FUNCTION();
+ if (!factory) return;
+
+ d->factorySet << factory;
+ foreach(QString k, factory->keys()) {
+ d->factories[k.toLatin1()].insert(factory);
+ }
+}
+
+/*!
+ Returns a human-readable error string describing the last error which
+ occurred while accessing a testwidget.
+
+ The error string is used to report directly to a tester any unexpected
+ errors. The string will typically be used as a test failure message.
+
+ \sa setErrorString()
+*/
+QString QtUiTestWidgets::errorString() const
+{ return d->errorString; }
+
+/*!
+ Sets the human-readable \a error string describing the last error which
+ occurred while accessing a testwidget.
+
+ \sa errorString()
+*/
+void QtUiTestWidgets::setErrorString(const QString& error)
+{
+ if (error == d->errorString) return;
+ d->errorString = error;
+}
+
+/*!
+ Returns a QDebug object which can be used as an output stream for debugging
+ information. The debug information is sent to the test runner for output.
+ This ensures a consistent handling of debugging information on different
+ test platforms.
+*/
+QDebug QtUiTestWidgets::debug() const
+{
+ return QDebug(&d->debugBuffer) << '\n';
+}
+
+/*!
+ \internal
+*/
+QString QtUiTestWidgets::debugOutput()
+{
+ QString ret = d->debugBuffer;
+ d->debugBuffer.clear();
+ return ret;
+}
+
+/*!
+ Returns a list of all QtUiTest widget interfaces implemented by \a o .
+*/
+QList<QByteArray> QtUiTestWidgetsPrivate::allImplementedInterfaces(QObject *o)
+{
+ QTT_TRACE_FUNCTION();
+ // FIXME this function should not have to be explicitly implemented.
+ // Find some way to automatically handle all interfaces.
+ QList<QByteArray> ret;
+ if (qobject_cast<QtUiTest::Object*>(o)) ret << "Object";
+ if (qobject_cast<QtUiTest::Widget*>(o)) ret << "Widget";
+ return ret;
+}
+
+/*!
+ Returns a test widget wrapper for \a object implementing the given
+ \a interface. If a test widget implementing \a interface is already
+ wrapping \a object, that test widget will be returned. Otherwise,
+ a new test widget may be constructed using registered factories.
+
+ Returns 0 if the given \a interface is not implemented on \a object
+ or on any test widget which wraps \a object.
+
+ The returned object should not be deleted by the caller. QtUiTestWidgets
+ retains ownership of the returned test widget and deletes it when \a object
+ is destroyed.
+
+ \sa registerFactory()
+*/
+QObject* QtUiTestWidgets::testWidget(QObject* object, const QByteArray &interface)
+{
+ if (!object) return 0;
+
+ QMetaObject const *mo = object->metaObject();
+ QObject *ret = d->testWidgets.value( object ).value( interface );
+
+ bool watchingDestroyed = false;
+
+ if (!ret) {
+ QSet<QtUiTest::WidgetFactory*> usedFactories;
+ while (mo) {
+ foreach (QtUiTest::WidgetFactory *factory,
+ d->factories.value(mo->className()) - usedFactories) {
+
+ QObject *testWidget = factory->create(object);
+ usedFactories.insert(factory);
+ if (testWidget) {
+ bool isValuable = false;
+ foreach (const QByteArray& thisIface,
+ d->allImplementedInterfaces(testWidget)) {
+ QHash<QByteArray, QPointer<QObject> > &subhash
+ = d->testWidgets[object];
+ if (!subhash[thisIface]) {
+ isValuable = true;
+ subhash.insert( thisIface, testWidget );
+ watchingDestroyed = watchingDestroyed
+ || connect(object, SIGNAL(destroyed()),
+ this, SLOT(_q_objectDestroyed()));
+ }
+ }
+ if (!isValuable) {
+ delete testWidget;
+ }
+ }
+ }
+ mo = mo->superClass();
+ }
+ ret = d->testWidgets.value( object ).value( interface );
+ }
+ return ret;
+}
+
+/*!
+ Simulate a mouse press event at the global co-ordinates given by \a pos,
+ for the buttons in \a state. \a opt is applied to the simulated event.
+*/
+void QtUiTestWidgets::mousePress(const QPoint &pos, Qt::MouseButtons state,
+ QtUiTest::InputOption opt)
+{
+ QTT_TRACE_FUNCTION();
+ int delay;
+ if ((delay = qtUiTestInputDelay()) != -1) {
+ QtUiTestMouseEvent event = {QtUiTestPress, pos, state, opt};
+ d->pendingMouseEvents << event;
+ QTimer::singleShot(delay, this, SLOT(_q_postNextMouseEvent()));
+ return;
+ }
+
+ QTWOptStack st(d, opt);
+ d->input->mousePress(pos, state);
+}
+
+void QtUiTestWidgets::touchPress(const QVariantList &points)
+{
+ QTT_TRACE_FUNCTION();
+ int delay;
+ if ((delay = qtUiTestInputDelay()) != -1) {
+ QtUiTestTouchEvent event = {QtUiTestPress, points };
+ d->pendingTouchEvents << event;
+ QTimer::singleShot(delay, this, SLOT(_q_postNextTouchEvent()));
+ return;
+ }
+
+ d->input->touchPress(points);
+}
+
+void QtUiTestWidgets::touchUpdate(const QVariantList &points)
+{
+ QTT_TRACE_FUNCTION();
+ int delay;
+ if ((delay = qtUiTestInputDelay()) != -1) {
+ QtUiTestTouchEvent event = {QtUiTestMove, points};
+ d->pendingTouchEvents << event;
+ QTimer::singleShot(delay, this, SLOT(_q_postNextTouchEvent()));
+ return;
+ }
+
+ d->input->touchUpdate(points);
+}
+
+void QtUiTestWidgets::touchRelease(const QVariantList &points)
+{
+ QTT_TRACE_FUNCTION();
+ int delay;
+ if ((delay = qtUiTestInputDelay()) != -1) {
+ QtUiTestTouchEvent event = {QtUiTestRelease, points};
+ d->pendingTouchEvents << event;
+ QTimer::singleShot(delay, this, SLOT(_q_postNextTouchEvent()));
+ return;
+ }
+
+ d->input->touchRelease(points);
+}
+
+/*!
+ Simulate a mouse release event at the global co-ordinates given by \a pos,
+ for the buttons in \a state. \a opt is applied to the simulated event.
+*/
+void QtUiTestWidgets::mouseRelease(const QPoint &pos, Qt::MouseButtons state,
+ QtUiTest::InputOption opt)
+{
+ QTT_TRACE_FUNCTION();
+ int delay;
+ if ((delay = qtUiTestInputDelay()) != -1) {
+ QtUiTestMouseEvent event = {QtUiTestRelease, pos, state, opt};
+ d->pendingMouseEvents << event;
+ QTimer::singleShot(delay, this, SLOT(_q_postNextMouseEvent()));
+ return;
+ }
+
+ QTWOptStack st(d, opt);
+ d->input->mouseRelease(pos, state);
+}
+
+/*!
+ Simulate a mouse click event at the global co-ordinates given by \a pos,
+ for the buttons in \a state. \a opt is applied to the simulated event.
+*/
+void QtUiTestWidgets::mouseClick(const QPoint &pos, Qt::MouseButtons state,
+ QtUiTest::InputOption opt)
+{
+ QTT_TRACE_FUNCTION();
+ int delay;
+ if ((delay = qtUiTestInputDelay()) != -1) {
+ QtUiTestMouseEvent event = {QtUiTestClick, pos, state, opt};
+ d->pendingMouseEvents << event;
+ QTimer::singleShot(delay, this, SLOT(_q_postNextMouseEvent()));
+ return;
+ }
+
+ QTWOptStack st(d, opt);
+ d->input->mouseClick(pos, state);
+}
+
+/*!
+ Simulate a mouse double-click event at the global co-ordinates given by \a pos,
+ for the buttons in \a state. \a opt is applied to the simulated event.
+*/
+void QtUiTestWidgets::mouseDClick(const QPoint &pos, Qt::MouseButtons state,
+ QtUiTest::InputOption opt)
+{
+ QTT_TRACE_FUNCTION();
+ int delay;
+ if ((delay = qtUiTestInputDelay()) != -1) {
+ QtUiTestMouseEvent event = {QtUiTestDClick, pos, state, opt};
+ d->pendingMouseEvents << event;
+ QTimer::singleShot(delay, this, SLOT(_q_postNextMouseEvent()));
+ return;
+ }
+
+ QTWOptStack st(d, opt);
+ d->input->mouseDClick(pos, state);
+}
+
+/*!
+ Simulate moving the mouse pointer to global co-ordinates given by \a pos,
+ \a opt is applied to the simulated event.
+*/
+void QtUiTestWidgets::mouseMove(const QPoint &pos, QtUiTest::InputOption opt)
+{
+ QTT_TRACE_FUNCTION();
+ int delay;
+ if ((delay = qtUiTestInputDelay()) != -1) {
+ QtUiTestMouseEvent event = {QtUiTestMove, pos, 0, opt};
+ d->pendingMouseEvents << event;
+ QTimer::singleShot(delay, this, SLOT(_q_postNextMouseEvent()));
+ return;
+ }
+
+ QTWOptStack st(d, opt);
+ d->input->mouseMove(pos);
+}
+
+/*!
+ Simulate a key press event, using the given \a key and \a mod.
+ \a opt is applied to the simulated event.
+*/
+void QtUiTestWidgets::keyPress(Qt::Key key, Qt::KeyboardModifiers mod,
+ QtUiTest::InputOption opt)
+{
+ QTT_TRACE_FUNCTION();
+ int delay;
+ if ((delay = qtUiTestInputDelay()) != -1) {
+ QtUiTestKeyEvent event = {QtUiTestPress, key, mod, opt};
+ d->pendingKeyEvents << event;
+ QTimer::singleShot(delay, this, SLOT(_q_postNextKeyEvent()));
+ return;
+ }
+
+ QTWOptStack st(d, opt);
+ d->input->keyPress(key, mod, opt & QtUiTest::KeyRepeat);
+}
+
+/*!
+ Simulate a key release event, using the given \a key and \a mod.
+ \a opt is applied to the simulated event.
+*/
+void QtUiTestWidgets::keyRelease(Qt::Key key, Qt::KeyboardModifiers mod,
+ QtUiTest::InputOption opt)
+{
+ QTT_TRACE_FUNCTION();
+ int delay;
+ if ((delay = qtUiTestInputDelay()) != -1) {
+ QtUiTestKeyEvent event = {QtUiTestRelease, key, mod, opt};
+ d->pendingKeyEvents << event;
+ QTimer::singleShot(delay, this, SLOT(_q_postNextKeyEvent()));
+ return;
+ }
+
+ QTWOptStack st(d, opt);
+ d->input->keyRelease(key, mod);
+}
+
+/*!
+ Simulate a key click event, using the given \a key and \a mod.
+ \a opt is applied to the simulated event.
+*/
+void QtUiTestWidgets::keyClick(Qt::Key key, Qt::KeyboardModifiers mod,
+ QtUiTest::InputOption opt)
+{
+ QTT_TRACE_FUNCTION();
+ int delay;
+ if ((delay = qtUiTestInputDelay()) != -1) {
+ QtUiTestKeyEvent event = {QtUiTestClick, key, mod, opt};
+ d->pendingKeyEvents << event;
+ QTimer::singleShot(delay, this, SLOT(_q_postNextKeyEvent()));
+ return;
+ }
+
+ QTWOptStack st(d, opt);
+ d->input->keyClick(key, mod);
+}
+
+void QtUiTestWidgetsPrivate::_q_postNextMouseEvent()
+{
+ QTT_TRACE_FUNCTION();
+ QtUiTestMouseEvent const event = pendingMouseEvents.takeAt(0);
+ QTWOptStack st(this, event.opt);
+ if (event.type == QtUiTestPress)
+ input->mousePress(event.pos, event.state);
+ else if (event.type == QtUiTestRelease)
+ input->mouseRelease(event.pos, event.state);
+ else if (event.type == QtUiTestClick)
+ input->mouseClick(event.pos, event.state);
+ else if (event.type == QtUiTestDClick)
+ input->mouseDClick(event.pos, event.state);
+ else if (event.type == QtUiTestMove)
+ input->mouseMove(event.pos);
+}
+
+void QtUiTestWidgetsPrivate::_q_postNextTouchEvent()
+{
+ QTT_TRACE_FUNCTION();
+ QtUiTestTouchEvent const event = pendingTouchEvents.takeAt(0);
+ if (event.type == QtUiTestPress)
+ input->touchPress(event.points);
+ else if (event.type == QtUiTestRelease)
+ input->touchRelease(event.points);
+ else if (event.type == QtUiTestMove)
+ input->touchUpdate(event.points);
+}
+
+void QtUiTestWidgetsPrivate::_q_postNextKeyEvent()
+{
+ QTT_TRACE_FUNCTION();
+ QtUiTestKeyEvent const event = pendingKeyEvents.takeAt(0);
+ QTWOptStack st(this, event.opt);
+ if (event.type == QtUiTestPress)
+ input->keyPress(event.key, event.mod);
+ else if (event.type == QtUiTestRelease)
+ input->keyRelease(event.key, event.mod);
+ else if (event.type == QtUiTestClick)
+ input->keyClick(event.key, event.mod);
+}
+
+/*!
+ Set or clear the specified \a option for subsequent simulated input
+ events. The option is set if \a on is true, otherwise it is cleared.
+*/
+void QtUiTestWidgets::setInputOption(QtUiTest::InputOption option, bool on)
+{
+ QTT_TRACE_FUNCTION();
+ if (on)
+ d->inputOptions |= option;
+ else
+ d->inputOptions &= (~option);
+ qttDebug() << "Set input options to" << d->inputOptions;
+}
+
+/*!
+ Returns true if \a option is currently set.
+*/
+bool QtUiTestWidgets::testInputOption(QtUiTest::InputOption option) const
+{
+ QTT_TRACE_FUNCTION();
+ return (option == d->inputOptions)
+ || (option & d->inputOptions);
+}
+
+#include "moc_qtuitestwidgets_p.cpp"