diff options
author | Rhys Weatherley <rhys.weatherley@nokia.com> | 2010-12-14 16:13:38 +1000 |
---|---|---|
committer | Rhys Weatherley <rhys.weatherley@nokia.com> | 2010-12-14 17:47:56 +1000 |
commit | 8004db140e85f6ca05227fd73e0c5c682fd4731b (patch) | |
tree | 51614fdfd5684e3a659b0af72faf24a9470dfafd /src | |
parent | 0ad579f7d1b12cffcbce7b46735a3869493fc764 (diff) |
Simulation of keyboard and mouse events
Diffstat (limited to 'src')
-rw-r--r-- | src/imports/testlib/TestCase.qml | 87 | ||||
-rw-r--r-- | src/imports/testlib/main.cpp | 3 | ||||
-rw-r--r-- | src/imports/testlib/testcase.qdoc | 143 | ||||
-rw-r--r-- | src/quicktestlib/quicktestevent.cpp | 227 | ||||
-rw-r--r-- | src/quicktestlib/quicktestevent_p.h | 78 | ||||
-rw-r--r-- | src/quicktestlib/quicktestlib.pro | 2 |
6 files changed, 540 insertions, 0 deletions
diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml index 905bbc9..0212c83 100644 --- a/src/imports/testlib/TestCase.qml +++ b/src/imports/testlib/TestCase.qml @@ -74,6 +74,8 @@ Item { property variant qtest_testCaseResult property variant qtest_results: qtest_results_normal TestResult { id: qtest_results_normal } + property variant qtest_events: qtest_events_normal + TestEvent { id: qtest_events_normal } function fail(msg) { if (msg === undefined) @@ -82,6 +84,13 @@ Item { throw new Error("QtQuickTest::fail") } + function qtest_fail(msg, frame) { + if (msg === undefined) + msg = ""; + qtest_results.fail(msg, Qt.qtest_caller_file(frame), Qt.qtest_caller_line(frame)) + throw new Error("QtQuickTest::fail") + } + function verify(cond, msg) { if (msg === undefined) msg = ""; @@ -202,6 +211,84 @@ Item { qtest_results.sleep(ms) } + function keyPress(key, modifiers, delay) { + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.keyPress(key, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function keyRelease(key, modifiers, delay) { + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.keyRelease(key, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function keyClick(key, modifiers, delay) { + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.keyClick(key, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function mousePress(item, x, y, button, modifiers, delay) { + if (button === undefined) + button = Qt.LeftButton + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.mousePress(item, x, y, button, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function mouseRelease(item, x, y, button, modifiers, delay) { + if (button === undefined) + button = Qt.LeftButton + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.mouseRelease(item, x, y, button, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function mouseClick(item, x, y, button, modifiers, delay) { + if (button === undefined) + button = Qt.LeftButton + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.mouseClick(item, x, y, button, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function mouseDoubleClick(item, x, y, button, modifiers, delay) { + if (button === undefined) + button = Qt.LeftButton + if (modifiers === undefined) + modifiers = Qt.NoModifier + if (delay == undefined) + delay = -1 + if (!qtest_events.mouseDoubleClick(item, x, y, button, modifiers, delay)) + qtest_fail("window not shown", 2) + } + + function mouseMove(item, x, y, delay) { + if (delay == undefined) + delay = -1 + if (!qtest_events.mouseMove(item, x, y, delay)) + qtest_fail("window not shown", 2) + } + // Functions that can be overridden in subclasses for init/cleanup duties. function initTestCase() {} function cleanupTestCase() {} diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp index c91decd..84dc4d8 100644 --- a/src/imports/testlib/main.cpp +++ b/src/imports/testlib/main.cpp @@ -46,10 +46,12 @@ #include <QtScript/qscriptcontextinfo.h> #include <QtScript/qscriptengine.h> #include "quicktestresult_p.h" +#include "quicktestevent_p.h" QT_BEGIN_NAMESPACE QML_DECLARE_TYPE(QuickTestResult) +QML_DECLARE_TYPE(QuickTestEvent) // Copied from qdeclarativedebughelper_p.h in Qt, to avoid a dependency // on a private header from Qt. @@ -105,6 +107,7 @@ public: { Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuickTest")); qmlRegisterType<QuickTestResult>(uri,1,0,"TestResult"); + qmlRegisterType<QuickTestEvent>(uri,1,0,"TestEvent"); } void initializeEngine(QDeclarativeEngine *engine, const char *) { diff --git a/src/imports/testlib/testcase.qdoc b/src/imports/testlib/testcase.qdoc index 699a3bd..ddd2b79 100644 --- a/src/imports/testlib/testcase.qdoc +++ b/src/imports/testlib/testcase.qdoc @@ -152,6 +152,38 @@ To get the effect of the \c{QBENCHMARK_ONCE} macro, prefix the test function name with "benchmark_once_". + + \section1 Simulating keyboard and mouse events + + The keyPress(), keyRelease(), and keyClick() methods can be used + to simulate keyboard events within unit tests. The events are + delivered to the currently focused QML item. + + \code + Rectangle { + width: 50; height: 50 + focus: true + + TestCase { + name: "KeyClick" + when: windowShown + + function test_key_click() { + keyClick(Qt.Key_Left) + ... + } + } + } + \endcode + + The mousePress(), mouseRelease(), mouseClick(), mouseDoubleClick(), + and mouseMove() methods can be used to simulate mouse events in a + similar fashion. + + \bold{Note:} keyboard and mouse events can only be delivered once the + main window has been shown. Attempts to deliver events before then + will fail. Use the \l when and windowShown properties to track + when the main window has been shown. */ /*! @@ -399,6 +431,117 @@ */ /*! + \qmlmethod TestCase::keyClick(key, modifiers = Qt.NoModifier, delay = -1) + + Simulates clicking of \a key with an optional \a modifier on the currently + focused item. If \a delay is larger than 0, the test will wait for + \a delay milliseconds. + + \sa keyPress(), keyRelease() +*/ + +/*! + \qmlmethod TestCase::keyPress(key, modifiers = Qt.NoModifier, delay = -1) + + Simulates pressing a \a key with an optional \a modifier on the currently + focused item. If \a delay is larger than 0, the test will wait for + \a delay milliseconds. + + \bold{Note:} At some point you should release the key using keyRelease(). + + \sa keyRelease(), keyClick() +*/ + +/*! + \qmlmethod TestCase::keyRelease(key, modifiers = Qt.NoModifier, delay = -1) + + Simulates releasing a \a key with an optional \a modifier on the currently + focused item. If \a delay is larger than 0, the test will wait for + \a delay milliseconds. + + \sa keyPress(), keyClick() +*/ + +/*! + \qmlmethod TestCase::mousePress(item, x, y, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) + + Simulates pressing a mouse \a button with an optional \a modifier + on an \a item. The position is defined by \a x and \a y. If \a delay is + specified, the test will wait for the specified amount of milliseconds + before the press. + + The position given by \a x and \a y is transformed from the co-ordinate + system of \a item into window co-ordinates and then delivered. + If \a item is obscured by another item, or a child of \a item occupies + that position, then the event will be delivered to the other item instead. + + \sa mouseRelease(), mouseClick(), mouseDoubleClick(), mouseMove() +*/ + +/*! + \qmlmethod TestCase::mouseRelease(item, x, y, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) + + Simulates releasing a mouse \a button with an optional \a modifier + on an \a item. The position of the release is defined by \a x and \a y. + If \a delay is specified, the test will wait for the specified amount of + milliseconds before releasing the button. + + The position given by \a x and \a y is transformed from the co-ordinate + system of \a item into window co-ordinates and then delivered. + If \a item is obscured by another item, or a child of \a item occupies + that position, then the event will be delivered to the other item instead. + + \sa mousePress(), mouseClick(), mouseDoubleClick(), mouseMove() +*/ + +/*! + \qmlmethod TestCase::mouseClick(item, x, y, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) + + Simulates clicking a mouse \a button with an optional \a modifier + on an \a item. The position of the click is defined by \a x and \a y. + If \a delay is specified, the test will wait for the specified amount of + milliseconds before pressing and before releasing the button. + + The position given by \a x and \a y is transformed from the co-ordinate + system of \a item into window co-ordinates and then delivered. + If \a item is obscured by another item, or a child of \a item occupies + that position, then the event will be delivered to the other item instead. + + \sa mousePress(), mouseRelease(), mouseDoubleClick(), mouseMove() +*/ + +/*! + \qmlmethod TestCase::mouseDoubleClick(item, x, y, button = Qt.LeftButton, modifiers = Qt.NoModifier, delay = -1) + + Simulates double-clicking a mouse \a button with an optional \a modifier + on an \a item. The position of the click is defined by \a x and \a y. + If \a delay is specified, the test will wait for the specified amount of + milliseconds before pressing and before releasing the button. + + The position given by \a x and \a y is transformed from the co-ordinate + system of \a item into window co-ordinates and then delivered. + If \a item is obscured by another item, or a child of \a item occupies + that position, then the event will be delivered to the other item instead. + + \sa mousePress(), mouseRelease(), mouseClick(), mouseMove() +*/ + +/*! + \qmlmethod TestCase::mouseMove(item, x, y, delay = -1) + + Moves the mouse pointer to the position given by \a x and \a y within + \a item. If a \a delay (in milliseconds) is given, the test will wait + before moving the mouse pointer. + + The position given by \a x and \a y is transformed from the co-ordinate + system of \a item into window co-ordinates and then delivered. + If \a item is obscured by another item, or a child of \a item occupies + that position, then the event will be delivered to the other item instead. + + \sa mousePress(), mouseRelease(), mouseClick(), mouseDoubleClick() +*/ + +/*! \qmlmethod TestCase::initTestCase() This function is called before any other test functions in the diff --git a/src/quicktestlib/quicktestevent.cpp b/src/quicktestlib/quicktestevent.cpp new file mode 100644 index 0000000..2decc46 --- /dev/null +++ b/src/quicktestlib/quicktestevent.cpp @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "quicktestevent_p.h" +#include "qtestkeyboard.h" +#include <QtGui/qgraphicsscene.h> +#include <QtGui/qgraphicsview.h> + +QT_BEGIN_NAMESPACE + +QuickTestEvent::QuickTestEvent(QDeclarativeItem *parent) + : QDeclarativeItem(parent) +{ + setVisible(false); +} + +QuickTestEvent::~QuickTestEvent() +{ +} + +bool QuickTestEvent::keyPress(int key, int modifiers, int delay) +{ + QWidget *widget = eventWidget(); + if (!widget) + return false; + QTest::keyPress(widget, Qt::Key(key), Qt::KeyboardModifiers(modifiers), delay); + return true; +} + +bool QuickTestEvent::keyRelease(int key, int modifiers, int delay) +{ + QWidget *widget = eventWidget(); + if (!widget) + return false; + QTest::keyRelease(widget, Qt::Key(key), Qt::KeyboardModifiers(modifiers), delay); + return true; +} + +bool QuickTestEvent::keyClick(int key, int modifiers, int delay) +{ + QWidget *widget = eventWidget(); + if (!widget) + return false; + QTest::keyClick(widget, Qt::Key(key), Qt::KeyboardModifiers(modifiers), delay); + return true; +} + +namespace QTest { + extern int Q_TESTLIB_EXPORT defaultMouseDelay(); +}; + +namespace QtQuickTest +{ + enum MouseAction { MousePress, MouseRelease, MouseClick, MouseDoubleClick, MouseMove }; + + static void mouseEvent(MouseAction action, QGraphicsView *widget, + QDeclarativeItem *item, Qt::MouseButton button, + Qt::KeyboardModifiers stateKey, QPointF _pos, int delay=-1) + { + QTEST_ASSERT(widget); + QTEST_ASSERT(item); + + if (delay == -1 || delay < QTest::defaultMouseDelay()) + delay = QTest::defaultMouseDelay(); + if(delay > 0) + QTest::qWait(delay); + + if (action == MouseClick) { + mouseEvent(MousePress, widget, item, button, stateKey, _pos); + mouseEvent(MouseRelease, widget, item, button, stateKey, _pos); + return; + } + + QPoint pos = widget->mapFromScene(item->mapToScene(_pos)); + + QTEST_ASSERT(button == Qt::NoButton || button & Qt::MouseButtonMask); + QTEST_ASSERT(stateKey == 0 || stateKey & Qt::KeyboardModifierMask); + + stateKey &= static_cast<unsigned int>(Qt::KeyboardModifierMask); + + QMouseEvent me(QEvent::User, QPoint(), Qt::LeftButton, button, stateKey); + switch (action) + { + case MousePress: + me = QMouseEvent(QEvent::MouseButtonPress, pos, widget->mapToGlobal(pos), button, button, stateKey); + break; + case MouseRelease: + me = QMouseEvent(QEvent::MouseButtonRelease, pos, widget->mapToGlobal(pos), button, 0, stateKey); + break; + case MouseDoubleClick: + me = QMouseEvent(QEvent::MouseButtonDblClick, pos, widget->mapToGlobal(pos), button, button, stateKey); + break; + case MouseMove: + QCursor::setPos(widget->mapToGlobal(pos)); + qApp->processEvents(); + return; + default: + QTEST_ASSERT(false); + } + QSpontaneKeyEvent::setSpontaneous(&me); + if (!qApp->notify(widget->viewport(), &me)) { + static const char *mouseActionNames[] = + { "MousePress", "MouseRelease", "MouseClick", "MouseDoubleClick", "MouseMove" }; + QString warning = QString::fromLatin1("Mouse event \"%1\" not accepted by receiving widget"); + QTest::qWarn(warning.arg(QString::fromLatin1(mouseActionNames[static_cast<int>(action)])).toAscii().data()); + } + } +}; + +bool QuickTestEvent::mousePress + (QDeclarativeItem *item, qreal x, qreal y, int button, + int modifiers, int delay) +{ + QGraphicsView *view = qobject_cast<QGraphicsView *>(eventWidget()); + if (!view) + return false; + QtQuickTest::mouseEvent(QtQuickTest::MousePress, view, item, + Qt::MouseButton(button), + Qt::KeyboardModifiers(modifiers), + QPointF(x, y), delay); + return true; +} + +bool QuickTestEvent::mouseRelease + (QDeclarativeItem *item, qreal x, qreal y, int button, + int modifiers, int delay) +{ + QGraphicsView *view = qobject_cast<QGraphicsView *>(eventWidget()); + if (!view) + return false; + QtQuickTest::mouseEvent(QtQuickTest::MouseRelease, view, item, + Qt::MouseButton(button), + Qt::KeyboardModifiers(modifiers), + QPointF(x, y), delay); + return true; +} + +bool QuickTestEvent::mouseClick + (QDeclarativeItem *item, qreal x, qreal y, int button, + int modifiers, int delay) +{ + QGraphicsView *view = qobject_cast<QGraphicsView *>(eventWidget()); + if (!view) + return false; + QtQuickTest::mouseEvent(QtQuickTest::MouseClick, view, item, + Qt::MouseButton(button), + Qt::KeyboardModifiers(modifiers), + QPointF(x, y), delay); + return true; +} + +bool QuickTestEvent::mouseDoubleClick + (QDeclarativeItem *item, qreal x, qreal y, int button, + int modifiers, int delay) +{ + QGraphicsView *view = qobject_cast<QGraphicsView *>(eventWidget()); + if (!view) + return false; + QtQuickTest::mouseEvent(QtQuickTest::MouseDoubleClick, view, item, + Qt::MouseButton(button), + Qt::KeyboardModifiers(modifiers), + QPointF(x, y), delay); + return true; +} + +bool QuickTestEvent::mouseMove + (QDeclarativeItem *item, qreal x, qreal y, int delay) +{ + QGraphicsView *view = qobject_cast<QGraphicsView *>(eventWidget()); + if (!view) + return false; + QtQuickTest::mouseEvent(QtQuickTest::MouseMove, view, item, + Qt::NoButton, Qt::NoModifier, + QPointF(x, y), delay); + return true; +} + +QWidget *QuickTestEvent::eventWidget() +{ + QGraphicsScene *s = scene(); + if (!s) + return 0; + QList<QGraphicsView *> views = s->views(); + if (views.isEmpty()) + return 0; + return views.at(0); +} + +QT_END_NAMESPACE diff --git a/src/quicktestlib/quicktestevent_p.h b/src/quicktestlib/quicktestevent_p.h new file mode 100644 index 0000000..0be70e5 --- /dev/null +++ b/src/quicktestlib/quicktestevent_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QUICKTESTEVENT_P_H +#define QUICKTESTEVENT_P_H + +#include <QtQuickTest/quicktestglobal.h> +#include <QtDeclarative/qdeclarativeitem.h> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_TEST_EXPORT QuickTestEvent : public QDeclarativeItem +{ + Q_OBJECT +public: + QuickTestEvent(QDeclarativeItem *parent = 0); + ~QuickTestEvent(); + +public Q_SLOTS: + bool keyPress(int key, int modifiers, int delay); + bool keyRelease(int key, int modifiers, int delay); + bool keyClick(int key, int modifiers, int delay); + + bool mousePress(QDeclarativeItem *item, qreal x, qreal y, int button, + int modifiers, int delay); + bool mouseRelease(QDeclarativeItem *item, qreal x, qreal y, int button, + int modifiers, int delay); + bool mouseClick(QDeclarativeItem *item, qreal x, qreal y, int button, + int modifiers, int delay); + bool mouseDoubleClick(QDeclarativeItem *item, qreal x, qreal y, int button, + int modifiers, int delay); + bool mouseMove(QDeclarativeItem *item, qreal x, qreal y, int delay); + +private: + QWidget *eventWidget(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quicktestlib/quicktestlib.pro b/src/quicktestlib/quicktestlib.pro index 7e6c6c6..9a1c3e0 100644 --- a/src/quicktestlib/quicktestlib.pro +++ b/src/quicktestlib/quicktestlib.pro @@ -28,11 +28,13 @@ INCLUDEPATH += $$PWD SOURCES += \ quicktest.cpp \ + quicktestevent.cpp \ quicktestresult.cpp HEADERS += \ QtQuickTest/quicktestglobal.h \ QtQuickTest/quicktest.h PRIVATE_HEADERS += \ + quicktestevent_p.h \ quicktestresult_p.h PUBLIC_HEADERS += $$HEADERS |