diff options
Diffstat (limited to 'old/libqtuitest')
34 files changed, 9934 insertions, 0 deletions
diff --git a/old/libqtuitest/DESCRIPTION b/old/libqtuitest/DESCRIPTION new file mode 100644 index 0000000..289743a --- /dev/null +++ b/old/libqtuitest/DESCRIPTION @@ -0,0 +1 @@ +Support library for QtUitest plugins. diff --git a/old/libqtuitest/libqtuitest.pri b/old/libqtuitest/libqtuitest.pri new file mode 100644 index 0000000..5d52454 --- /dev/null +++ b/old/libqtuitest/libqtuitest.pri @@ -0,0 +1,19 @@ +INCLUDEPATH += $$SRCROOT/libqtuitest + +symbian { + LIBS+=-L$$OUT_PWD/ -lqtuitest +} + +mac { + CONFIG(debug,debug|release): LIBS += -L$$BUILDROOT/lib -lqtuitest_debug + CONFIG(release,debug|release): LIBS += -L$$BUILDROOT/lib -lqtuitest +} + +win32 { + CONFIG(debug,debug|release): LIBS+= -L$$BUILDROOT/libqtuitest -lqtuitestd + CONFIG(release,debug|release):LIBS+= -L$$BUILDROOT/libqtuitest -lqtuitest +} + +unix:!symbian { + LIBS += -L$$BUILDROOT/lib -lqtuitest +} diff --git a/old/libqtuitest/libqtuitest.pro b/old/libqtuitest/libqtuitest.pro new file mode 100644 index 0000000..3f96f64 --- /dev/null +++ b/old/libqtuitest/libqtuitest.pro @@ -0,0 +1,89 @@ +DEFINES+=QTUITEST_TARGET + +enable_valgrind:DEFINES+=QTUITEST_HAVE_VALGRIND + +PRIVATE_HEADERS += \ + qeventwatcher_p.h \ + qinputgenerator_p.h \ + qtuitestconnectionmanager_p.h \ + qtuitestwidgets_p.h + +SEMI_PRIVATE_HEADERS += \ + qalternatestack_p.h \ + qtuitestelapsedtimer_p.h \ + qtestprotocol_p.h \ + recordevent_p.h + +HEADERS += \ + qtuitestglobal.h \ + qtuitestnamespace.h \ + qtuitestrecorder.h \ + qtuitestwidgetinterface.h \ + testwidget.h + +SOURCES += \ + qtuitestelapsedtimer.cpp \ + qeventwatcher.cpp \ + qinputgenerator.cpp \ + qtestprotocol.cpp \ + qtuitestconnectionmanager.cpp \ + qtuitestnamespace.cpp \ + qtuitestrecorder.cpp \ + qtuitestwidgetinterface.cpp \ + qtuitestwidgets.cpp \ + testwidget.cpp + +unix:!symbian { + SOURCES+=qalternatestack_unix.cpp + embedded:SOURCES += qinputgenerator_qws.cpp + !embedded:!mac { + SOURCES += qinputgenerator_x11.cpp + LIBS += -lXtst + } + MOC_DIR=$$OUT_PWD/.moc + OBJECTS_DIR=$$OUT_PWD/.obj + DESTDIR=$$BUILDROOT/lib + target.path=$$[QT_INSTALL_LIBS] + INSTALLS+=target +} + +symbian { + TARGET.EPOCALLOWDLLDATA=1 + TARGET.CAPABILITY += AllFiles ReadDeviceData ReadUserData SwEvent WriteUserData NetworkServices + SOURCES += qalternatestack_symbian.cpp qinputgenerator_symbian.cpp + LIBS += -lws32 + LIBS += -leuser -lcone +} + +mac { + SOURCES += qinputgenerator_mac.cpp + LIBS += -framework ApplicationServices -framework Carbon +} + +win32 { + SOURCES += qalternatestack_win.cpp \ + qinputgenerator_win.cpp + LIBS += -luser32 + target.path=$$[QT_INSTALL_BINS] + INSTALLS+=target +} + +symbian { + MOC_DIR=$$OUT_PWD/moc + OBJECTS_DIR=$$OUT_PWD/obj + CONFIG+=staticlib +} + +TEMPLATE=lib +QT+=network +HEADERS+=$$SEMI_PRIVATE_HEADERS $$PRIVATE_HEADERS +VPATH+=$$PWD +INCLUDEPATH+=$$PWD +TARGET=qtuitest +TARGET=$$qtLibraryTarget($$TARGET) +CONFIG-=debug_and_release_target + +maemo5|maemo6 { + target.path=$$[QT_INSTALL_LIBS] + INSTALLS+=target +} diff --git a/old/libqtuitest/qalternatestack_p.h b/old/libqtuitest/qalternatestack_p.h new file mode 100644 index 0000000..701c797 --- /dev/null +++ b/old/libqtuitest/qalternatestack_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 QALTERNATESTACK_P_H +#define QALTERNATESTACK_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtUiTest API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QObject> +#include <QVariant> +#include <qtuitestglobal.h> + +class QAlternateStack; +typedef void (*QAlternateStackEntryPoint)(QAlternateStack*,QVariant const&); + +struct QAlternateStackPrivate; + +class QTUITEST_EXPORT QAlternateStack : public QObject +{ + Q_OBJECT + +public: + QAlternateStack(quint32 = 65536, QObject* = 0); + virtual ~QAlternateStack(); + + bool isActive() const; + bool isCurrentStack() const; + + static QList<QAlternateStack*> instances(); + static bool isAvailable(); + +public slots: + void start(QAlternateStackEntryPoint, const QVariant& = QVariant()); + void switchTo(); + void switchFrom(); + +private: + QAlternateStackPrivate* d; + friend struct QAlternateStackPrivate; +}; + +#endif + diff --git a/old/libqtuitest/qalternatestack_symbian.cpp b/old/libqtuitest/qalternatestack_symbian.cpp new file mode 100644 index 0000000..1fad1db --- /dev/null +++ b/old/libqtuitest/qalternatestack_symbian.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qalternatestack_p.h" +struct QAlternateStackPrivate +{ + // All instances of QAlternateStack. +// static QList<QAlternateStack*> instances; +}; + +//QList<QAlternateStack*> QAlternateStackPrivate::instances; + +QAlternateStack::QAlternateStack(quint32 size, QObject* parent) + : QObject(parent), + d(new QAlternateStackPrivate) +{ + Q_UNUSED(size); + Q_UNUSED(parent); + qFatal("QAlternateStack: not available on this platform"); +} + +QAlternateStack::~QAlternateStack() +{ +} + +void QAlternateStack::start(QAlternateStackEntryPoint entry, const QVariant& data) +{ + Q_UNUSED(entry); + Q_UNUSED(data); +} + +void QAlternateStack::switchTo() +{ +} + +void QAlternateStack::switchFrom() +{ +} + +bool QAlternateStack::isActive() const +{ return false; } + +bool QAlternateStack::isCurrentStack() const +{ return false; } + +QList<QAlternateStack*> QAlternateStack::instances() +{ static QList<QAlternateStack*> l; + return l; + //QAlternateStackPrivate::instances; } +} + +bool QAlternateStack::isAvailable() +{ return false; } + diff --git a/old/libqtuitest/qalternatestack_unix.cpp b/old/libqtuitest/qalternatestack_unix.cpp new file mode 100644 index 0000000..016102b --- /dev/null +++ b/old/libqtuitest/qalternatestack_unix.cpp @@ -0,0 +1,525 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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$ +** +****************************************************************************/ +#undef _FORTIFY_SOURCE + +#include "qalternatestack_p.h" + +#include <errno.h> +#include <setjmp.h> +#include <signal.h> +#include <sys/syscall.h> +#include <unistd.h> + +#include <QDebug> + +#ifdef QTUITEST_HAVE_VALGRIND +# include <valgrind/valgrind.h> +/* Make sure we have the right version of valgrind.h */ +# if !defined(VALGRIND_STACK_REGISTER) || !defined(VALGRIND_STACK_DEREGISTER) || !defined(VALGRIND_STACK_CHANGE) +# warn You have Valgrind but are missing stack annotation macros, disabling Valgrind features. +# undef QTUITEST_HAVE_VALGRIND +# endif +#endif + +struct QAlternateStackPrivate +{ + QAlternateStack* q; + + // The memory containing the alternate stack. + QByteArray stackBuffer; + char* stackBegin; + int stackSize; + +#ifdef QTUITEST_HAVE_VALGRIND + int stackValgrindId; +#endif + + void allocate(int); + + // Alternate and original stack objects. + stack_t alternateStack; + stack_t originalStack; + + // Sigaction used to jump to new stack, and original sigaction. + struct sigaction alternateAction; + struct sigaction originalAction; + + // jmp_buf for jumping between stacks. + sigjmp_buf alternateJmp; + sigjmp_buf originalJmp; + + // Starting point of the new stack. + QAlternateStackEntryPoint entryFunction; + + // Argument to be passed to entryFunction. + QVariant entryData; + + // All instances of QAlternateStack. + static QList<QAlternateStack*> instances; +}; +QList<QAlternateStack*> QAlternateStackPrivate::instances; + +QTUITEST_EXPORT char*& qalternatestack_stackbuf() +{ static char* ret = 0; return ret; } +QTUITEST_EXPORT int& qalternatestack_stackbuf_len() +{ static int ret = 0; return ret; } + +#ifndef QT_NO_DEBUG +QString qobject_to_string(QObject* object) +{ + QString ret; + { + QDebug dbg(&ret); + dbg << object; + } + return ret; +} +#endif + +void qt_sigaltstack(stack_t const* ss, stack_t* oss) +{ + if (0 != sigaltstack(ss, oss)) { + qFatal("QAlternateStack: error in sigaltstack: %s", strerror(errno)); + } +} + +void qt_sigaction(int signum, struct sigaction const* act, struct sigaction* oldact) +{ + if (0 != sigaction(signum,act,oldact)) { + qFatal("QAlternateStack: error in sigaction: %s", strerror(errno)); + } +} + +void qt_raise(int signum) +{ + if (-1 == raise(signum)) { + qFatal("QAlternateStack: error in raise: %s", strerror(errno)); + } +} + +QAlternateStackPrivate* qt_next_stack; + +static const int JMP_SWITCHFROM = 2; +static const int JMP_SWITCHTO = 3; +static const int JMP_FINISH = 4; +static const int QALTERNATESTACK_SIGNAL = SIGUSR2; + +/* + This wrapper function is called when we first enter an alternate stack, + via the raising of QALTERNATESTACK_SIGNAL. +*/ +void qt_entry_function_wrapper(int) +{ + // Establish this context into the alternate jmp_buf. + // We cannot have the whole stack run during a signal handler, + // so we store our context when we are first called, then return and allow + // the original stack to jump back to us. + + int value = sigsetjmp(qt_next_stack->alternateJmp, 0); + + // We might be here for these reasons: + // + // 1. We are handling the signal and just called setjmp. + // value == 0. + if (0 == value) { + // Return from the signal handler; the original stack will jump back + // to us via longjmp. + return; + } + // 2. We are switched to from the original stack. + // value == JMP_SWITCHTO. + else if (JMP_SWITCHTO == value) { + } + // Whoops, programmer error. + else Q_ASSERT(0); + + QAlternateStackPrivate* stack = qt_next_stack; + stack->entryFunction(stack->q, stack->entryData); + + // OK, jump back to the main stack and let it know that we're finished. + // We can't just return here; if we did, we'd jump back to the signal + // handler frame, but the signal handler is already finished. + siglongjmp(stack->originalJmp, JMP_FINISH); +} + +/*! + \internal + \class QAlternateStack + \inpublicgroup QtUiTestModule + \brief The QAlternateStack class provides a call stack. + + QAlternateStack can be used to switch between two call stacks without + using threads. + + There is one anticipated use for this class, and that is to wait for a + specific amount of time, while processing events, without hanging if a + nested event loop occurs. + + Example: + \code + void runsInAlternateStack(QAlternateStack* stack, const QVariant& data) + { + int foo = data.toInt(); + // ... + // Need to wait for about 1500 milliseconds here, while processing events. + // Process events in the main stack to avoid hanging on nested event loops. + QTimer::singleShot(1500, stack, SLOT(switchTo())); + stack->switchFrom(); + + // Execution resumes here in ~1500ms, as long as the main stack is + // processing events, even if there was a nested event loop. + // ... + + // Don't need the stack any more + stack->deleteLater(); + } + + void runsInMainStack() + { + QAlternateStack* stack = new QAlternateStack; + stack->start(runsInAlternateStack, 123); + } + \endcode + + Using this class makes code more difficult to understand. Use an + alternative where possible. +*/ + +/*! + \relates QAlternateStack + \typedef QAlternateStackEntryPoint + + Typedef for a pointer to a function with the signature + \c{void my_function(QAlternateStack*,const QVariant&)}. + + Used as an entry point to a new stack. +*/ + +/*! + Creates an alternate stack of \a size bytes with the given \a parent. + + Behavior is undefined if the alternate stack exceeds \a size. +*/ +QAlternateStack::QAlternateStack(quint32 size, QObject* parent) + : QObject(parent), + d(new QAlternateStackPrivate) +{ + Q_ASSERT_X(QAlternateStack::isAvailable(), "QAlternateStack", + "QAlternateStack is not available on this platform!"); + + d->q = this; + d->entryFunction = 0; + + d->allocate(size); + +#ifdef QTUITEST_HAVE_VALGRIND + d->stackValgrindId = VALGRIND_STACK_REGISTER(d->stackBegin, d->stackBegin+d->stackSize); +#endif + + QAlternateStackPrivate::instances << this; +} + +void QAlternateStackPrivate::allocate(int size) +{ + if (!qalternatestack_stackbuf()) { + // Allocate space for the new stack. + stackBuffer.resize(size); + stackBegin = stackBuffer.data(); + stackSize = size; + return; + } + + /* + On some systems, it has been found that due to limitations in the kernel and/or threading + implementation, setting the stack pointer to point to memory allocated anywhere other than + the main stack causes a crash (bug 209341). + + This code works around this bug by using a pool of memory which was earlier allocated on + the stack. qalternatestack_stackbuf() and qalternatestack_stackbuf_len() must be + explicitly initialized to point to a block of memory and to tell us its length. + */ + + // Find the first chunk of unused memory. + char* begin = qalternatestack_stackbuf(); + char* end = qalternatestack_stackbuf() + qalternatestack_stackbuf_len(); + char* found = 0; + int total_used = 0; + + for (char* buf = begin; buf <= end - size && !found; buf += size) { + char* buf_end = buf + size; + // Is this block of memory definitely unused? + bool used = false; + foreach (QAlternateStack* st, QAlternateStackPrivate::instances) { + char* st_buf = st->d->stackBegin; + char* st_buf_end = st->d->stackBegin + st->d->stackSize; + // If we start after this stack ends, or we end before it finishes, we're OK. + if (buf >= st_buf_end || buf_end <= st_buf) { + } else { + used = true; + total_used += (st_buf_end - st_buf); + break; + } + } + if (!used) found = buf; + } + + if (!found) { + qFatal( "QtUiTest: QAlternateStack ran out of memory! There seem to be %d stacks using a " + "total of %d bytes (out of %d), and a stack of size %d was requested. " + "Please increase the size of qalternatestack_stackbuf!", + QAlternateStackPrivate::instances.count(), total_used, + qalternatestack_stackbuf_len(), size); + } + stackBegin = found; + stackSize = size; +} + +/*! + Destroys the alternate stack. + + Behavior is undefined if the stack is destroyed while currently active. +*/ +QAlternateStack::~QAlternateStack() +{ +#ifdef QTUITEST_HAVE_VALGRIND + VALGRIND_STACK_DEREGISTER(d->stackValgrindId); +#endif + + delete d; + d = 0; + QAlternateStackPrivate::instances.removeAll(this); +} + +/*! + Switch from the original stack to the alternate stack, and start + executing \a entry in the alternate stack. + This stack and \a data will be passed as an argument to \a entry. + + This function should be called when first switching to an alternate + stack. When resuming a stack that is already active, use switchTo(). + + \sa switchTo(), switchFrom() +*/ +void QAlternateStack::start(QAlternateStackEntryPoint entry, const QVariant& data) +{ + Q_ASSERT_X(!isActive(), "QAlternateStack::start", + qPrintable(QString("`start' called while already active. sender(): %1") + .arg(qobject_to_string(sender())))); + + // Set up the alternate stack to be jumped to. + d->alternateStack.ss_sp = d->stackBegin; + d->alternateStack.ss_flags = 0; + d->alternateStack.ss_size = d->stackSize; + qt_sigaltstack(&d->alternateStack, &d->originalStack); + + // Set up signal handler to jump to entry function wrapper in alternate stack. + d->alternateAction.sa_handler = qt_entry_function_wrapper; + sigemptyset(&d->alternateAction.sa_mask); + d->alternateAction.sa_flags = SA_ONSTACK; + qt_sigaction(QALTERNATESTACK_SIGNAL, &d->alternateAction, &d->originalAction); + + // Raise the signal, jumping to the alternate stack. + qt_next_stack = d; + qt_raise(QALTERNATESTACK_SIGNAL); + + // signal handler returns immediately after storing its context into + // alternateJmp. Restore the old signal handler. + qt_sigaltstack(&d->originalStack, 0); + qt_sigaction(QALTERNATESTACK_SIGNAL, &d->originalAction, 0); + + // OK, now store the entry function and data and jump to the new stack. + d->entryFunction = entry; + d->entryData = data; + + switchTo(); +} + +/*! + Switch from the original stack to the alternate stack. + + This function can be called to resume execution in an alternate stack. + + If execution has been suspended in the alternate stack by a call to + switchFrom(), switchTo() will resume executing at that point. + If the alternate stack has completed execution or hasn't started, this + function does nothing and returns immediately. + + It is an error to call this function from the alternate stack. +*/ +void QAlternateStack::switchTo() +{ + // We must not be the currently active stack. + Q_ASSERT_X(!isCurrentStack(), "QAlternateStack::switchTo", + qPrintable(QString("`switchTo' called in currently active stack. sender(): %1") + .arg(qobject_to_string(sender())))); + + // Store where we currently are. + int value = sigsetjmp(d->originalJmp, 1); + + // Now it really gets tricky. + // At this particular point, we could be here for these reasons: + // + // 1. switchTo() was actually called from the original stack + // and we just returned from setjmp. + // value == 0. + // + // 2. The alternate stack switched to the original stack + // by calling switchFrom(). + // value == JMP_SWITCHFROM. + // + // 3. The alternate stack finished execution and jumped back to us. + // value == JMP_FINISH. + + // Case 1: just returned from setjmp. + // Jump to the other stack. + if (0 == value) { + // If there is no entry function, that means `start' has not been called or we have + // finished already. In other words, there's no more to process on this stack, so + // just return immediately. + if (!d->entryFunction) return; + + // This function never returns; from here we will jump back to + // the above call to setjmp() via case 2 or 3. + siglongjmp(d->alternateJmp, JMP_SWITCHTO); + } + + // Case 2: switchFrom() called in alternate stack. + if (value == JMP_SWITCHFROM) { + // Don't need to do anything, just return. + return; + } + + // Case 3: The alternate stack finished execution. + // Just need to do a little cleanup. + if (value == JMP_FINISH) { + d->entryFunction = 0; + d->entryData = QVariant(); + return; + } + + // Whoops, bad programmer. + Q_ASSERT(0); +} + +/*! + Switch from the alternate stack to the original stack. + + Once execution takes place in the alternate stack, there are two ways + to return to the original stack. + + The first is simply to return from the entry function passed to the + constructor, at which point the switchTo() function in the original + stack will return. + + The second is to call switchFrom(). This will cause the switchTo() + function to return in the original stack. switchFrom() will not + return until switchTo() is called again in the original stack, which + is not guaranteed to happen. + + It is an error to call this function from the original stack. +*/ +void QAlternateStack::switchFrom() +{ + // We must be the currently active stack. + Q_ASSERT_X(isCurrentStack(), "QAlternateStack::switchFrom", + qPrintable(QString("`switchFrom' called from wrong stack. sender(): %1") + .arg(qobject_to_string(sender())))); + + int value = sigsetjmp(d->alternateJmp, 1); + + // Two possibilities: + // + // 1. We just called setjmp and will now jump to main stack. + // value == 0 + if (0 == value) { + siglongjmp(d->originalJmp, JMP_SWITCHFROM); + } + // 2. We just jumped to here from main stack. + // value == JMP_SWITCHTO + else if (JMP_SWITCHTO == value) { + } + // Whoops, bad programmer. + else Q_ASSERT(0); +} + +/*! + Returns true if the stack has started (entry function has been called) + and not yet finished (entry function has not returned). + + \sa isCurrentStack() +*/ +bool QAlternateStack::isActive() const +{ return d->entryFunction; } + +/*! + Returns true if the currently used stack is this QAlternateStack. + + \sa isActive() +*/ +bool QAlternateStack::isCurrentStack() const +{ + // Test if a stack-allocated variable resides within the alternate + // stack buffer. + char dummy = 1; + quint32 diff = &dummy - d->stackBegin; + return (diff < (quint32)d->stackSize); +} + +/*! + Returns all created QAlternateStack objects. +*/ +QList<QAlternateStack*> QAlternateStack::instances() +{ return QAlternateStackPrivate::instances; } + +/*! + Returns true if QAlternateStack is usable on this platform. + + Usage of QAlternateStack when this function returns false will typically + result in a fatal error at runtime. +*/ +bool QAlternateStack::isAvailable() +{ +#ifdef Q_OS_MAC + return false; +#endif + return true; +} + diff --git a/old/libqtuitest/qalternatestack_win.cpp b/old/libqtuitest/qalternatestack_win.cpp new file mode 100644 index 0000000..e0e600e --- /dev/null +++ b/old/libqtuitest/qalternatestack_win.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qalternatestack_p.h" + +#include <QCoreApplication> +#include <QThread> + +#include <windows.h> + +struct QAlternateStackPrivate +{ + // All instances of QAlternateStack. + static QList<QAlternateStack*> instances; + + // Callback used to start each fiber. + static void CALLBACK startFiber(PVOID); + + static LPVOID mainFiber; + + QAlternateStackEntryPoint entryFunction; + QVariant entryData; + quint32 size; + + LPVOID fiber; +}; +QList<QAlternateStack*> QAlternateStackPrivate::instances; +LPVOID QAlternateStackPrivate::mainFiber = 0; + +/* + Given a Windows error code, return a C string with the corresponding error text. + The returned pointer points to static storage, hence this function is not thread safe. +*/ +const char* qstrwinerror(DWORD errorcode) +{ + static char out[256]; + DWORD ok = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM, + 0, + errorcode, + 0, + out, + sizeof(out)/sizeof(TCHAR), + 0 + ); + if (!ok) { + QByteArray realError = QByteArray::number((int)errorcode); + QByteArray nextError = QByteArray::number((int)GetLastError()); + QByteArray outString = "(error "; + outString += realError; + outString += "; additionally, error "; + outString += nextError; + outString += " while looking up error string)"; + qstrncpy(out, outString.constData(), sizeof(out)); + } + return out; +} + +void CALLBACK QAlternateStackPrivate::startFiber(PVOID _q) +{ + QAlternateStack* q = static_cast<QAlternateStack*>(_q); + QAlternateStackPrivate* d = q->d; + + d->entryFunction(q, d->entryData); + + /* Setting entryFunction to 0 tells main fiber we are done */ + d->entryFunction = 0; + d->entryData = QVariant(); + + q->switchFrom(); +} + +QAlternateStack::QAlternateStack(quint32 size, QObject* parent) + : QObject(parent), + d(new QAlternateStackPrivate) +{ + Q_ASSERT_X(QAlternateStack::isAvailable(), "QAlternateStack", + "QAlternateStack is not available on this platform!"); + + /* + Make sure we belong to the main thread to avoid keeping track of whether each thread has + been converted to a fiber. + */ + Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread(), + "QAlternateStack", "Alternate stacks must be created in the main thread!"); + + /* + The main thread must be a fiber before we can manage any other fibers. + Unfortunately, earlier than Windows Vista, it is not possible to test if a thread is + a fiber. So it is possible ConvertThreadToFiber will be called more than once for + the main thread. + */ + if (!QAlternateStackPrivate::mainFiber) { + QAlternateStackPrivate::mainFiber = ConvertThreadToFiber(0); + if (!QAlternateStackPrivate::mainFiber) { + qFatal("QAlternateStack: failed to convert thread to fiber: %s", + qstrwinerror(GetLastError())); + } + } + + d->entryFunction = 0; + d->size = size; + d->fiber = 0; + + QAlternateStackPrivate::instances << this; +} + +QAlternateStack::~QAlternateStack() +{ + delete d; + d = 0; + QAlternateStackPrivate::instances.removeAll(this); +} + +void QAlternateStack::start(QAlternateStackEntryPoint entry, const QVariant& data) +{ + Q_ASSERT_X(!isActive(), "QAlternateStack::start", + qPrintable(QString("`start' called while already active."))); + + d->fiber = CreateFiber(d->size, QAlternateStackPrivate::startFiber, this); + if (!d->fiber) { + qFatal("QAlternateStack: failed to create fiber: %s", qstrwinerror(GetLastError())); + } + + d->entryFunction = entry; + d->entryData = data; + SwitchToFiber(d->fiber); + + if (!d->entryFunction) { + DeleteFiber(d->fiber); + d->fiber = 0; + } +} + +void QAlternateStack::switchTo() +{ + // We must not be the currently active stack. + Q_ASSERT_X(!isCurrentStack(), "QAlternateStack::switchTo", + qPrintable(QString("`switchTo' called in currently active stack."))); + + // If not active, nothing to do + if (!isActive()) return; + + SwitchToFiber(d->fiber); + + if (!d->entryFunction) { + DeleteFiber(d->fiber); + d->fiber = 0; + } +} + +void QAlternateStack::switchFrom() +{ + // We must be the currently active stack. + Q_ASSERT_X(isCurrentStack(), "QAlternateStack::switchFrom", + qPrintable(QString("`switchFrom' called from wrong stack."))); + + SwitchToFiber(QAlternateStackPrivate::mainFiber); +} + +bool QAlternateStack::isActive() const +{ return d->fiber; } + +bool QAlternateStack::isCurrentStack() const +{ return GetFiberData() == this; } + +QList<QAlternateStack*> QAlternateStack::instances() +{ return QAlternateStackPrivate::instances; } + +bool QAlternateStack::isAvailable() +{ return true; } + diff --git a/old/libqtuitest/qeventwatcher.cpp b/old/libqtuitest/qeventwatcher.cpp new file mode 100644 index 0000000..938c2a4 --- /dev/null +++ b/old/libqtuitest/qeventwatcher.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qeventwatcher_p.h" + +#include <QSet> +#include <QPointer> +#include <QCoreApplication> + +struct QEventWatcherPrivate +{ + QSet<QObject*> objects; + QList<QEventWatcherFilter*> filters; + int count; +}; + +QEventWatcher::QEventWatcher(QObject* parent) + : QObject(parent), + d(new QEventWatcherPrivate) +{ + d->count = 0; +} + +QEventWatcher::~QEventWatcher() +{ + qDeleteAll(d->filters); + delete d; + d = 0; +} + +void QEventWatcher::addObject(QObject* obj) +{ + d->objects << obj; + qApp->installEventFilter(this); +} + +void QEventWatcher::addFilter(QEventWatcherFilter* filter) +{ d->filters << filter; } + +int QEventWatcher::count() const +{ return d->count; } + +QString QEventWatcher::toString() const +{ + QString ret; + QString sep; + for (int i = d->filters.count()-1; i >= 0; --i) { + ret += sep + d->filters.at(i)->toString(); + sep = ", "; + } + return ret; +} + +bool QEventWatcher::eventFilter(QObject* obj, QEvent* e) +{ + if (!d->objects.contains(obj)) return false; + + bool accept = (d->filters.count() ? false : true); + for (int i = d->filters.count()-1; i >= 0 && !accept; --i) { + accept = d->filters.at(i)->accept(obj,e); + } + + if (accept) { + ++d->count; + emit event(obj, e->type()); + } + + return false; +} + diff --git a/old/libqtuitest/qeventwatcher_p.h b/old/libqtuitest/qeventwatcher_p.h new file mode 100644 index 0000000..96b941b --- /dev/null +++ b/old/libqtuitest/qeventwatcher_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 QEVENTWATCHER_H +#define QEVENTWATCHER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtUiTest API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QObject> +#include <QEvent> + +struct QEventWatcherPrivate; +class QEventWatcherFilter; + +class QEventWatcher : public QObject +{ + Q_OBJECT + +public: + explicit QEventWatcher(QObject* parent =0); + virtual ~QEventWatcher(); + + void addObject(QObject*); + void addFilter(QEventWatcherFilter*); + + int count() const; + + QString toString() const; + +signals: + void event(QObject*,int); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +private: + QEventWatcherPrivate* d; +}; + +class QEventWatcherFilter +{ +public: + virtual ~QEventWatcherFilter() {} + +protected: + virtual bool accept(QObject*,QEvent*) const =0; + virtual QString toString() const =0; + + friend class QEventWatcher; +}; + +#endif + diff --git a/old/libqtuitest/qinputgenerator.cpp b/old/libqtuitest/qinputgenerator.cpp new file mode 100644 index 0000000..aa4b091 --- /dev/null +++ b/old/libqtuitest/qinputgenerator.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qinputgenerator_p.h" + +#include <QApplication> +#include <QPoint> +#include <QWidget> + +#define QINPUTGENERATOR_DEBUG() if (1); else qDebug() << "QInputGenerator:" + +Qt::KeyboardModifier const QInputGenerator::AllModifiers[] = + { Qt::ShiftModifier, Qt::ControlModifier, Qt::AltModifier, Qt::MetaModifier }; + +/* + Given a co-ordinate relative to the application's currently active window, + maps it to this input generator's co-ordinate system. +*/ +QPoint QInputGenerator::mapFromActiveWindow(QPoint const& pos) const +{ + QPoint ret(pos); + + QWidget *w = QApplication::activeWindow(); + if (!w) w = QApplication::activePopupWidget(); + if (!w) w = QApplication::activeModalWidget(); + + if (w) { + ret = w->mapToGlobal(pos); + } + + return ret; +} + +Qt::Key QInputGenerator::modifierToKey(Qt::KeyboardModifier mod) +{ + switch (mod) { + case Qt::ShiftModifier: + return Qt::Key_Shift; + case Qt::ControlModifier: + return Qt::Key_Control; + case Qt::AltModifier: + return Qt::Key_Alt; + case Qt::MetaModifier: + return Qt::Key_Meta; + default: + break; + } + return Qt::Key(0); +} + diff --git a/old/libqtuitest/qinputgenerator_mac.cpp b/old/libqtuitest/qinputgenerator_mac.cpp new file mode 100644 index 0000000..a4adaab --- /dev/null +++ b/old/libqtuitest/qinputgenerator_mac.cpp @@ -0,0 +1,480 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qinputgenerator_p.h" + +#include <QtCore> +#include <QtGui> + +#include <ApplicationServices/ApplicationServices.h> +#include <Carbon/Carbon.h> + +#include "qtuitestnamespace.h" +#include "private/qcore_mac_p.h" + +#define QINPUTGENERATOR_DEBUG() if (1); else qDebug() << "QInputGenerator:" + +QMap<int,QPair<int,Qt::KeyboardModifiers> > qt_key_to_mac_keycode_make() +{ + QMap<int,QPair<int,Qt::KeyboardModifiers> > m; + + static UInt32 deadKeyState = 0L; + const UCKeyboardLayout *keyLayout = 0; + +#if defined(Q_OS_MAC32) + KeyboardLayoutRef keyLayoutRef = 0; + KLGetCurrentKeyboardLayout(&keyLayoutRef); + OSStatus err; + if (keyLayoutRef != 0) { + err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData, + (reinterpret_cast<const void **>(&keyLayout))); + if (err != noErr) { + qWarning("Qt::internal::unable to get keyboardlayout %ld %s:%d", + long(err), __FILE__, __LINE__); + } + } +#else + QCFType<TISInputSourceRef> inputSource = TISCopyCurrentKeyboardInputSource(); + Q_ASSERT(inputSource != 0); + CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(inputSource, + kTISPropertyUnicodeKeyLayoutData)); + keyLayout = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0; +#endif + + if (keyLayout) { + UniChar string[4]; + UniCharCount actualLength; + + for (UInt32 keyCode = 0; keyCode < 128; ++keyCode) { + if (noErr == UCKeyTranslate(keyLayout, keyCode, kUCKeyActionDisplay, 0, LMGetKbdType(), + kUCKeyTranslateNoDeadKeysMask, &deadKeyState, 4, &actualLength, string)) + m.insert(int(string[0]), qMakePair(int(keyCode), Qt::KeyboardModifiers(Qt::NoModifier))); + if (noErr == UCKeyTranslate(keyLayout, keyCode, kUCKeyActionDisplay, ((shiftKey >> 8) & 0xff), LMGetKbdType(), + kUCKeyTranslateNoDeadKeysMask, &deadKeyState, 4, &actualLength, string)) + m.insert(int(string[0]), qMakePair(int(keyCode), Qt::KeyboardModifiers(Qt::ShiftModifier))); + } + } +#ifdef Q_OS_MAC32 + else { + const void *keyboard_layout; + KeyboardLayoutRef keyLayoutRef = 0; + KLGetCurrentKeyboardLayout(&keyLayoutRef); + err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData, + reinterpret_cast<const void **>(&keyboard_layout)); + + for (UInt32 keyCode = 0; keyCode < 128; ++keyCode) { + int translatedChar = KeyTranslate(keyboard_layout, keyCode, &deadKeyState); + if (translatedChar >= Qt::Key_Space && translatedChar <= Qt::Key_AsciiTilde) { + m.insert(translatedChar, qMakePair(int(keyCode), Qt::KeyboardModifiers(Qt::NoModifier))); + } + translatedChar = KeyTranslate(keyboard_layout, keyCode | shiftKey, &deadKeyState); + if (translatedChar >= Qt::Key_Space && translatedChar <= Qt::Key_AsciiTilde) { + m.insert(translatedChar, qMakePair(int(keyCode), Qt::KeyboardModifiers(Qt::ShiftModifier))); + } + + } + } +#endif + + m.insert( Qt::Key_Escape, qMakePair(53, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Tab, qMakePair(48, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Backspace, qMakePair(51, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Return, qMakePair(36, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Enter, qMakePair(76, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Insert, qMakePair(114, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Delete, qMakePair(117, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Print, qMakePair(105, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Space, qMakePair(49, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_PageUp, qMakePair(116, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_PageDown, qMakePair(121, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_End, qMakePair(119, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Home, qMakePair(115, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Left, qMakePair(123, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Up, qMakePair(126, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Right, qMakePair(124, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Down, qMakePair(125, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_CapsLock, qMakePair(57, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_NumLock, qMakePair(71, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F1, qMakePair(122, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F2, qMakePair(120, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F3, qMakePair(99, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F4, qMakePair(118, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F5, qMakePair(96, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F6, qMakePair(97, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F7, qMakePair(98, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F8, qMakePair(100, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F9, qMakePair(101, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F10, qMakePair(109, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F11, qMakePair(103, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F12, qMakePair(111, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F13, qMakePair(105, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F14, qMakePair(107, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F15, qMakePair(113, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Menu, qMakePair(110, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Help, qMakePair(114, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_division, qMakePair(75, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_multiply, qMakePair(67, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Minus, qMakePair(78, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Plus, qMakePair(69, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Period, qMakePair(65, Qt::KeyboardModifiers(Qt::NoModifier)) ); + + // Modifiers + m.insert( Qt::ShiftModifier, qMakePair(56, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Shift, qMakePair(56, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::ControlModifier, qMakePair(55, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Control, qMakePair(55, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::AltModifier, qMakePair(58, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Alt, qMakePair(58, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::MetaModifier, qMakePair(59, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Meta, qMakePair(55, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_CapsLock, qMakePair(57, Qt::KeyboardModifiers(Qt::NoModifier)) ); + + return m; +} + +QMap<int,uint> qt_button_to_mac_button_down_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, kCGEventLeftMouseDown ); + m.insert( Qt::MidButton, kCGEventOtherMouseDown ); + m.insert( Qt::RightButton, kCGEventRightMouseDown ); + return m; +} + +QMap<int,uint> qt_button_to_mac_button_up_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, kCGEventLeftMouseUp ); + m.insert( Qt::MidButton, kCGEventOtherMouseUp ); + m.insert( Qt::RightButton, kCGEventRightMouseUp ); + return m; +} + +QMap<int,uint> qt_button_to_mac_button_drag_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, kCGEventLeftMouseDragged ); + m.insert( Qt::MidButton, kCGEventOtherMouseDragged ); + m.insert( Qt::RightButton, kCGEventRightMouseDragged ); + return m; +} + +QMap<int,uint> qt_button_to_mac_button_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, kCGMouseButtonLeft ); + m.insert( Qt::MidButton, kCGMouseButtonCenter ); + m.insert( Qt::RightButton, kCGMouseButtonRight ); + return m; +} + +class QInputGeneratorPrivate +{ +public: + QInputGeneratorPrivate(); + QInputGenerator* q; + + void keyEvent(Qt::Key, bool); + void mouseEvent(QPoint const&, Qt::MouseButtons); + + void ensureModifiers(Qt::KeyboardModifiers); + Qt::KeyboardModifiers currentModifiers() const; + + QMap<int,QPair<int,Qt::KeyboardModifiers> > const keyMap; + QMap<int,uint> const buttonMap; + QMap<int,uint> const buttonDownMap; + QMap<int,uint> const buttonUpMap; + QMap<int,uint> const buttonDragMap; + + QPoint currentPos; + Qt::MouseButtons currentButtons; +}; + +QInputGeneratorPrivate::QInputGeneratorPrivate() + : keyMap(qt_key_to_mac_keycode_make()), + buttonMap(qt_button_to_mac_button_make()), + buttonDownMap(qt_button_to_mac_button_down_make()), + buttonUpMap(qt_button_to_mac_button_up_make()), + buttonDragMap(qt_button_to_mac_button_drag_make()), + currentPos(), + currentButtons(0) +{ + //Following line is workaround for a bug in Tiger... + CFRelease(CGEventCreate(NULL)); +} + +QInputGenerator::QInputGenerator(QObject* parent) + : QObject(parent), + d(new QInputGeneratorPrivate) +{ + d->q = this; + QINPUTGENERATOR_DEBUG() << "constructor"; +} + +QInputGenerator::~QInputGenerator() +{ + QINPUTGENERATOR_DEBUG() << "destructor"; + + /* + Restore all keyboard modifiers to off. + If we don't do this, the current modifiers stay activated + even when this application is closed. + Note that there is no guarantee this code actually gets run. + */ + d->ensureModifiers(0); + if (d->currentButtons) { + d->mouseEvent(d->currentPos, 0); + } + + d->q = 0; + delete d; + d = 0; +} + +/* + Returns the Qt keyboard modifiers which are currently pressed. +*/ +Qt::KeyboardModifiers QInputGeneratorPrivate::currentModifiers() const +{ + quint32 modifiers = GetCurrentKeyModifiers(); + + int state = 0; + state |= (modifiers & shiftKey ? Qt::ShiftModifier : Qt::NoModifier); + state |= (modifiers & controlKey ? Qt::ControlModifier : Qt::NoModifier); + state |= (modifiers & optionKey ? Qt::AltModifier : Qt::NoModifier); + state |= (modifiers & cmdKey ? Qt::MetaModifier : Qt::NoModifier); + + return Qt::KeyboardModifier(state); +} + +void QInputGeneratorPrivate::ensureModifiers(Qt::KeyboardModifiers desiredMod) +{ + Qt::KeyboardModifiers currentMod = currentModifiers(); + + // For each modifier.. + for (unsigned i = 0; i < sizeof(q->AllModifiers)/sizeof(Qt::KeyboardModifier); ++i) { + Qt::KeyboardModifier thisMod = q->AllModifiers[i]; + // If the modifier is currently disabled but we want it enabled, or vice-versa... + if ((desiredMod & thisMod) && !(currentMod & thisMod)) { + QINPUTGENERATOR_DEBUG() << "Enabling modifier" << (void*)thisMod << "by press"; + // press to enable + keyEvent(q->modifierToKey(thisMod), true); + } else if (!(desiredMod & thisMod) && (currentMod & thisMod)) { + QINPUTGENERATOR_DEBUG() << "Disabling modifier" << (void*)thisMod << "by release"; + // release to disable + keyEvent(q->modifierToKey(thisMod), false); + } + } + + if (currentMod != desiredMod) { + currentMod = desiredMod; + for (int i = 0; + i < 1000 && QApplication::keyboardModifiers() & Qt::KeypadModifier != desiredMod & Qt::KeypadModifier; + i += 50, QtUiTest::wait(50)) + {} + + if (QApplication::keyboardModifiers() & Qt::KeypadModifier != desiredMod & Qt::KeypadModifier) + qWarning() << "QtUitest: couldn't set all modifiers to desired state! " + "Current state:" << (void*)(int)QApplication::keyboardModifiers() << + "Desired:" << (void*)(int)desiredMod; + } + +} + +void QInputGeneratorPrivate::keyEvent(Qt::Key key, bool is_press) +{ + int sym = 0; + Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers(Qt::NoModifier); + do { + QMap<int,QPair<int,Qt::KeyboardModifiers> >::const_iterator found = keyMap.find(key); + if (found != keyMap.end()) { + sym = (*found).first; + if (key < Qt::Key_A || key > Qt::Key_Z) + modifiers = (*found).second; + break; + } + + qWarning() << "QInputGenerator: don't know how to translate Qt key" + << (void*)key << "into Mac keycode"; + return; + + } while(0); + + if (modifiers) { + ensureModifiers(modifiers); + } + + //TODO: CGPostKeyboardEvent is deprecated in Snow Leopard, however the alternative (using CGEventPost) + // does not work as reliably (at least on Tiger/PPC). Further investigation required. + CGPostKeyboardEvent(0, (CGKeyCode)sym, is_press); + +#if 0 + CGEventRef event = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)sym, is_press); + CGEventPost(kCGHIDEventTap, event); + CFRelease(event); +#endif + + QINPUTGENERATOR_DEBUG() << (is_press ? "press" : "release") << sym; +} + +void QInputGenerator::keyPress(Qt::Key key, Qt::KeyboardModifiers mod, bool autoRepeat) +{ + Q_UNUSED(autoRepeat); + d->ensureModifiers(mod); + d->keyEvent(key, true); + QtUiTest::wait(1); +} + +void QInputGenerator::keyRelease(Qt::Key key, Qt::KeyboardModifiers mod) +{ + d->ensureModifiers(mod); + d->keyEvent(key, false); + QtUiTest::wait(1); +} + +void QInputGenerator::keyClick(Qt::Key key, Qt::KeyboardModifiers mod) +{ + keyPress(key,mod); + QtUiTest::wait(20); + keyRelease(key,mod); +} + +void QInputGeneratorPrivate::mouseEvent(QPoint const& pos, Qt::MouseButtons state) +{ + CGPoint p; + p.x = (float)(pos.x()); + p.y = (float)(pos.y()); + + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + + //TODO: CGPostMouseEvent is deprecated in Snow Leopard, however the alternative (using CGEventPost) + // does not work as reliably (at least on Tiger/PPC). Further investigation required. + CGPostMouseEvent(p, true, 3, Qt::LeftButton & state, Qt::RightButton & state, Qt::MidButton & state); + +#if 0 + if (currentPos != pos) { + currentPos = pos; + QINPUTGENERATOR_DEBUG() << "Mouse is at: " << QCursor::pos(); + + CGEventType eventType = kCGEventMouseMoved; + CGEventRef eventRef = CGEventCreateMouseEvent(NULL, eventType, p, kCGMouseButtonLeft); + //This shouldn't be necessary, but it is... + CGEventSetType(eventRef, eventType); + CGEventPostToPSN(&psn, eventRef); + CFRelease(eventRef); + + for (int i = 0; + i < 1000 && QCursor::pos() != pos; + i += 50, QtUiTest::wait(50)) + {} + + QINPUTGENERATOR_DEBUG() << "Mouse is now at: " << QCursor::pos(); + + if (QCursor::pos() != pos) + qWarning() << "QtUitest: couldn't move cursor to desired point! " + "Current position:" << QCursor::pos() << + "Desired:" << pos; + } + + typedef QPair<uint,uint> ButtonEvent; + QList<ButtonEvent> buttonEvents; + + foreach (int button, buttonMap.keys()) { + bool desired = button & state; + bool current = button & currentButtons; + + // Do we need to release? + if (!desired && current) { + buttonEvents << qMakePair(buttonUpMap[button], buttonMap[button]); + QINPUTGENERATOR_DEBUG() << "releasing " << buttonMap[button]; + } + + // Do we need to press? + if (desired && !current) { + buttonEvents << qMakePair(buttonDownMap[button], buttonMap[button]); + QINPUTGENERATOR_DEBUG() << "pressing " << buttonMap[button]; + } + + // Do we need to drag? Is this necessary? + if (desired && current) { + buttonEvents << qMakePair(buttonDragMap[button], buttonMap[button]); + QINPUTGENERATOR_DEBUG() << "dragging " << buttonMap[button]; + } + } + + foreach (ButtonEvent const& event, buttonEvents) { + CGEventType eventType = event.first; + QINPUTGENERATOR_DEBUG() << "posting Quartz event " << eventType << ", " << event.second; + CGEventRef eventRef = CGEventCreateMouseEvent(NULL, eventType, p, event.second); + CGEventSetType(eventRef, eventType); + CGEventPostToPSN(&psn, eventRef); + CFRelease(eventRef); + } +#endif + + currentButtons = state; +} + +void QInputGenerator::mousePress(QPoint const& pos, Qt::MouseButtons state) +{ + QINPUTGENERATOR_DEBUG() << "Mouse press" << pos << (void*)(int)state; + d->mouseEvent(pos, d->currentButtons | state); +} + +void QInputGenerator::mouseRelease(QPoint const& pos, Qt::MouseButtons state) +{ + QINPUTGENERATOR_DEBUG() << "Mouse release" << pos << (void*)(int)(d->currentButtons & ~state); + d->mouseEvent(pos, d->currentButtons & ~state); +} + +void QInputGenerator::mouseClick(QPoint const& pos, Qt::MouseButtons state) +{ + mousePress (pos,state); + QtUiTest::wait(1); + mouseRelease(pos,state); + QtUiTest::wait(1); +} + diff --git a/old/libqtuitest/qinputgenerator_p.h b/old/libqtuitest/qinputgenerator_p.h new file mode 100644 index 0000000..c18bde6 --- /dev/null +++ b/old/libqtuitest/qinputgenerator_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 QKEYGENERATOR_P_H +#define QKEYGENERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtUiTest API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGlobal> +#include <QObject> +#include <qtuitestglobal.h> + +class QPoint; +class QInputGeneratorPrivate; + +class QTUITEST_EXPORT QInputGenerator : public QObject +{ +Q_OBJECT +public: + explicit QInputGenerator(QObject* =0); + virtual ~QInputGenerator(); + + void keyPress (Qt::Key, Qt::KeyboardModifiers, bool=false); + void keyRelease(Qt::Key, Qt::KeyboardModifiers); + void keyClick (Qt::Key, Qt::KeyboardModifiers); + + void mousePress (QPoint const&, Qt::MouseButtons); + void mouseRelease(QPoint const&, Qt::MouseButtons); + void mouseClick (QPoint const&, Qt::MouseButtons); + + QPoint mapFromActiveWindow(QPoint const&) const; + +private: + QInputGeneratorPrivate* d; + friend class QInputGeneratorPrivate; + + static Qt::Key modifierToKey(Qt::KeyboardModifier); + static Qt::KeyboardModifier const AllModifiers[4]; +}; + +#endif + diff --git a/old/libqtuitest/qinputgenerator_qws.cpp b/old/libqtuitest/qinputgenerator_qws.cpp new file mode 100644 index 0000000..657f34e --- /dev/null +++ b/old/libqtuitest/qinputgenerator_qws.cpp @@ -0,0 +1,333 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qinputgenerator_p.h" + +#include <QCopChannel> +#include <QDebug> +#include <QKeySequence> +#include <QScreen> +#include <QWSServer> +#include <QWidget> + +#include "qtuitestnamespace.h" + +#ifdef QTOPIA_TARGET +# include <qtopialog.h> +# define QINPUTGENERATOR_DEBUG() qLog(QtUitest) +#else +# define QINPUTGENERATOR_DEBUG() if (1); else qDebug() << "QInputGenerator:" +#endif + +#define QINPUTGENERATOR_QCOP "QtUiTest/QInputGenerator" +#define QINPUTGENERATOR_QCOP_KEYEVENT "keyEvent(int,int,int,bool)" +#define QINPUTGENERATOR_QCOP_VERSION QDataStream::Qt_4_0 + +struct QInputGeneratorPrivate +{ + QInputGeneratorPrivate(); + QInputGenerator* q; + + enum KeyEventType { KeyPress, KeyRelease, KeyClick }; + enum MouseEventType { MousePress, MouseRelease, MouseClick }; + + void keyEvent (Qt::Key, Qt::KeyboardModifiers, KeyEventType, bool = false); + void mouseEvent(QPoint const&, Qt::MouseButtons, MouseEventType); + + void ensureModifiers(Qt::KeyboardModifiers); + + Qt::KeyboardModifiers currentModifiers; +}; + +struct QInputGeneratorService : public QCopChannel +{ + QInputGeneratorService(QInputGeneratorPrivate*,QObject* =0); + + virtual void receive(QString const&,QByteArray const&); + + QInputGeneratorPrivate* d; +}; + +QInputGeneratorService::QInputGeneratorService(QInputGeneratorPrivate* _d, QObject* parent) + : QCopChannel(QINPUTGENERATOR_QCOP, parent), + d(_d) +{ + Q_ASSERT(qApp->type() == qApp->GuiServer); +} + +void QInputGeneratorService::receive(QString const& message, QByteArray const& data) +{ + if (message == QLatin1String(QINPUTGENERATOR_QCOP_KEYEVENT)) { + int key = 0; + int mod = 0; + int type = 0; + bool autoRepeat = false; + + QByteArray copy(data); + QDataStream ds(©, QIODevice::ReadOnly); + ds.setVersion(QINPUTGENERATOR_QCOP_VERSION); + ds >> key >> mod >> type >> autoRepeat; + + if (ds.status() != QDataStream::Ok) { + qWarning() << "QtUitest: error deserializing " QINPUTGENERATOR_QCOP_KEYEVENT + " message, a key event might be lost."; + } else { + Qt::KeyboardModifiers qtMod(mod); + d->ensureModifiers(qtMod); + d->keyEvent(Qt::Key(key), qtMod, static_cast<QInputGeneratorPrivate::KeyEventType>(type), autoRepeat); + } + } else { + qWarning() << "QtUitest: QInputGenerator received unknown message" << message; + } +} + +QInputGeneratorPrivate::QInputGeneratorPrivate() + : q(0) + , currentModifiers(0) +{} + +QInputGenerator::QInputGenerator(QObject* parent) + : QObject(parent), + d(new QInputGeneratorPrivate) +{ + d->q = this; + if (qApp->type() == qApp->GuiServer) { + new QInputGeneratorService(d, this); + } +} + +QInputGenerator::~QInputGenerator() +{ + d->ensureModifiers(0); + d->q = 0; + delete d; + d = 0; +} + +void QInputGeneratorPrivate::keyEvent(Qt::Key key, Qt::KeyboardModifiers mod, KeyEventType type, bool autoRepeat) +{ + if (qApp->type() != qApp->GuiServer) { + QByteArray data; + { + QDataStream ds(&data,QIODevice::WriteOnly); + ds.setVersion(QINPUTGENERATOR_QCOP_VERSION); + ds << int(key) << int(mod) << int(type) << autoRepeat; + if (ds.status() != QDataStream::Ok) { + qWarning() << "QtUitest: error serializing " QINPUTGENERATOR_QCOP_KEYEVENT + " message, a key event might be lost."; + } + } + if (!QCopChannel::send(QINPUTGENERATOR_QCOP, QINPUTGENERATOR_QCOP_KEYEVENT, data)) { + qWarning() << "QtUitest: error sending " QINPUTGENERATOR_QCOP_KEYEVENT + " message, a key event might be lost."; + } + return; + } + + QINPUTGENERATOR_DEBUG() << "about to simulate key" + << ((type==KeyPress) ? "press" : ((type==KeyRelease) ? "release" : "click")) + << QKeySequence(key|mod).toString() << "autorepeat" << QString("%1").arg(autoRepeat); + + ushort unicode = 0; + if (key <= 0xff) { + QChar ch(key); + // FIXME this should be less of a hack. The case should not implicitly depend on mod. + if (mod & Qt::ShiftModifier) ch = ch.toUpper(); + else ch = ch.toLower(); + unicode = ch.unicode(); + } + + /* + The screensaver may consume key events. + There is no way to query the server to determine if the next key event will be consumed, + or even if the screensaver is currently active. All we can do is force the screensaver + (if any) to wake up before every key event to ensure none of them are consumed. + */ + if (!QMetaObject::invokeMethod(QWSServer::instance(), "_q_screenSaverWake")) + Q_ASSERT(0); + + QWSServer::processKeyEvent(unicode, key, mod, (type != KeyRelease), autoRepeat); + + if (type == KeyClick) { + // Key press/release should not occur in same millisecond + QtUiTest::wait(10); + QWSServer::processKeyEvent(unicode, key, mod, false, autoRepeat); + } + + QINPUTGENERATOR_DEBUG() << "simulated key" + << ((type==KeyPress) ? "press" : ((type==KeyRelease) ? "release" : "click")) + << QKeySequence(key|mod).toString() << "autorepeat" << QString("%1").arg(autoRepeat); +} + +void QInputGenerator::keyPress(Qt::Key key, Qt::KeyboardModifiers mod, bool autoRepeat) +{ + if (qApp->type() == qApp->GuiServer) { + d->ensureModifiers(mod); + } + d->keyEvent(key, mod, d->KeyPress, autoRepeat); +} + +void QInputGenerator::keyRelease(Qt::Key key, Qt::KeyboardModifiers mod) +{ + if (qApp->type() == qApp->GuiServer) { + d->ensureModifiers(mod); + } + d->keyEvent(key, mod, d->KeyRelease); +} + +void QInputGenerator::keyClick(Qt::Key key, Qt::KeyboardModifiers mod) +{ + if (qApp->type() == qApp->GuiServer) { + d->ensureModifiers(mod); + } + d->keyEvent(key, mod, d->KeyClick); +} + +void QInputGeneratorPrivate::mouseEvent(QPoint const& local, Qt::MouseButtons state, MouseEventType type) +{ + QPoint pos(q->mapFromActiveWindow(local)); + + QINPUTGENERATOR_DEBUG() << "about to simulate mouse" + << ((type==MousePress) ? "press" : ((type==MouseRelease) ? "release" : "click")); + + Q_ASSERT(pos.x() >= 0 && pos.x() < QScreen::instance()->width() + && pos.y() >= 0 && pos.y() < QScreen::instance()->height()); + + // When a mouse click occurs while an input method is currently active, + // and the click goes to a non-qpe window, there's a fair chance it will + // cause the current IM to change. + // We'd better wait for it, because it can cause widgets to be resized + // and hence screw up subsequent clicks. + struct InputMethods { + static QWidget* widget() + { + static QWidget* inputMethods = 0; + if (!inputMethods) { + QWidgetList wl(QApplication::topLevelWidgets()); + foreach (QWidget *w, wl) { + inputMethods = w->findChild<QWidget*>("InputMethods"); + if (inputMethods) break; + } + } + return inputMethods; + } + + static QString current() + { + QString ret; + if (InputMethods::widget()) { + QMetaObject::invokeMethod(InputMethods::widget(), "currentShown", + Qt::DirectConnection, Q_RETURN_ARG(QString, ret)); + } + return ret; + + } + }; + + QWSWindow *win = QWSServer::instance()->windowAt(pos); + QString client_before_click = (win ? win->client()->identity() : QString()); + QString im_before_click = InputMethods::current(); + + QWSServer::sendMouseEvent(pos, (MouseRelease == type) ? Qt::MouseButtons(0) : state, 0); + + if (type == MouseClick) { + // This wait is to avoid the mouse press and release occurring in + // the same millisecond, which surely won't happen with real + // hardware and wouldn't be unlikely to confuse some apps. + QtUiTest::wait(10); + QWSServer::sendMouseEvent(pos, 0, 0); + } + + if (type == MouseClick && client_before_click != "qpe" + && !im_before_click.isEmpty()) { + for (int i = 0; + i < 500 && InputMethods::current() == im_before_click; + i += 50, QtUiTest::wait(50)) {} + } + + QINPUTGENERATOR_DEBUG() << "simulated mouse" + << ((type==MousePress) ? "press" : ((type==MouseRelease) ? "release" : "click")); +} + +void QInputGenerator::mousePress(QPoint const& pos, Qt::MouseButtons state) +{ d->mouseEvent(pos, state, d->MousePress); } + +void QInputGenerator::mouseRelease(QPoint const& pos, Qt::MouseButtons state) +{ d->mouseEvent(pos, state, d->MouseRelease); } + +void QInputGenerator::mouseClick(QPoint const& pos, Qt::MouseButtons state) +{ d->mouseEvent(pos, state, d->MouseClick); } + +void QInputGeneratorPrivate::ensureModifiers(Qt::KeyboardModifiers desiredMod) +{ + if (currentModifiers == desiredMod) return; + + Qt::KeyboardModifiers incrementalMod = currentModifiers; + // For each modifier.. + for (unsigned i = 0; i < sizeof(q->AllModifiers)/sizeof(Qt::KeyboardModifier); ++i) { + Qt::KeyboardModifier thisMod = q->AllModifiers[i]; + // If the modifier is currently disabled but we want it enabled, or vice-versa... + if ((desiredMod & thisMod) && !(currentModifiers & thisMod)) { + QINPUTGENERATOR_DEBUG() << "Enabling modifier" << (void*)thisMod << "by press"; + incrementalMod |= thisMod; + keyEvent(q->modifierToKey(thisMod), incrementalMod, KeyPress, false); + } else if (!(desiredMod & thisMod) && (currentModifiers & thisMod)) { + QINPUTGENERATOR_DEBUG() << "Disabling modifier" << (void*)thisMod << "by release"; + incrementalMod &= ~thisMod; + keyEvent(q->modifierToKey(thisMod), incrementalMod, KeyRelease, false); + } + } + + /* + On QWS, we cannot check if QApplication::keyboardModifiers() changes to the desired + state. That function returns whatever modifiers were set on the last spontaneous + input event, and the modifiers set on input events are the modifiers _before_ the + event occurred (e.g. the Shift key press event does not have the Shift modifier set, + but the next key event does). + + So QApplication::keyboardModifiers() is off-by-one and thus can't be checked until + the next event occurs - we're just assuming success. + */ + currentModifiers = desiredMod; + +} + + diff --git a/old/libqtuitest/qinputgenerator_symbian.cpp b/old/libqtuitest/qinputgenerator_symbian.cpp new file mode 100644 index 0000000..2adce33 --- /dev/null +++ b/old/libqtuitest/qinputgenerator_symbian.cpp @@ -0,0 +1,425 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qinputgenerator_p.h" + +#include <QtCore> +#include <QtGui> + +#include <w32std.h> +#include <e32event.h> +#include <e32std.h> + +#include <coemain.h> + +#include "qtuitestnamespace.h" + +#define QINPUTGENERATOR_DEBUG() if (1); else qDebug() << "QInputGenerator:" + +QMap<int,int> qt_key_to_symbian_keycode_make() +{ + QMap<int,int> m; + + m.insert( Qt::Key_Backspace, EKeyBackspace ); + m.insert( Qt::Key_Tab, EKeyTab ); + m.insert( Qt::Key_Enter, EKeyEnter ); + m.insert( Qt::Key_Escape, EKeyEscape ); + m.insert( Qt::Key_Space, EKeySpace ); + m.insert( Qt::Key_Delete, EKeyDelete ); + m.insert( Qt::Key_SysReq, EKeyPrintScreen ); + m.insert( Qt::Key_Pause, EKeyPause ); + m.insert( Qt::Key_Home, EKeyHome ); + m.insert( Qt::Key_End, EKeyEnd ); + m.insert( Qt::Key_PageUp, EKeyPageUp ); + m.insert( Qt::Key_PageDown, EKeyPageDown ); + m.insert( Qt::Key_Insert, EKeyInsert ); + m.insert( Qt::Key_Left, EKeyLeftArrow ); + m.insert( Qt::Key_Right, EKeyRightArrow ); + m.insert( Qt::Key_Up, EKeyUpArrow ); + m.insert( Qt::Key_Down, EKeyDownArrow ); + + m.insert( Qt::Key_Shift, EKeyLeftShift ); + m.insert( Qt::Key_Control, EKeyLeftCtrl ); + m.insert( Qt::Key_Alt, EKeyLeftAlt ); + m.insert( Qt::Key_AltGr, EKeyRightAlt ); + m.insert( Qt::Key_Super_L, EKeyLeftFunc ); + m.insert( Qt::Key_Super_R, EKeyRightFunc ); + m.insert( Qt::Key_CapsLock, EKeyCapsLock ); + m.insert( Qt::Key_NumLock, EKeyNumLock ); + m.insert( Qt::Key_ScrollLock, EKeyScrollLock ); + + m.insert( Qt::Key_F1, EKeyF1 ); + m.insert( Qt::Key_F2, EKeyF2 ); + m.insert( Qt::Key_F3, EKeyF3 ); + m.insert( Qt::Key_F4, EKeyF4 ); + m.insert( Qt::Key_F5, EKeyF5 ); + m.insert( Qt::Key_F6, EKeyF6 ); + m.insert( Qt::Key_F7, EKeyF7 ); + m.insert( Qt::Key_F8, EKeyF8 ); + m.insert( Qt::Key_F9, EKeyF9 ); + m.insert( Qt::Key_F10, EKeyF10 ); + m.insert( Qt::Key_F11, EKeyF11 ); + m.insert( Qt::Key_F12, EKeyF12 ); + m.insert( Qt::Key_F13, EKeyF13 ); + m.insert( Qt::Key_F14, EKeyF14 ); + m.insert( Qt::Key_F15, EKeyF15 ); + m.insert( Qt::Key_F16, EKeyF16 ); + m.insert( Qt::Key_F17, EKeyF17 ); + m.insert( Qt::Key_F18, EKeyF18 ); + m.insert( Qt::Key_F19, EKeyF19 ); + m.insert( Qt::Key_F20, EKeyF20 ); + m.insert( Qt::Key_F21, EKeyF21 ); + m.insert( Qt::Key_F22, EKeyF22 ); + m.insert( Qt::Key_F23, EKeyF23 ); + m.insert( Qt::Key_F24, EKeyF24 ); + + m.insert( Qt::Key_Menu, EKeyMenu ); + m.insert( Qt::Key_Help, EKeyHelp ); + m.insert( Qt::Key_Call, EKeyDial ); + + m.insert( Qt::Key_Context1, EKeyDevice0 ); + m.insert( Qt::Key_Context2, EKeyDevice1 ); + m.insert( Qt::Key_Select, EKeyDevice3 ); + + m.insert( Qt::Key_Launch0, EKeyApplication0 ); + m.insert( Qt::Key_Launch1, EKeyApplication1 ); + m.insert( Qt::Key_Launch2, EKeyApplication2 ); + m.insert( Qt::Key_Launch3, EKeyApplication3 ); + m.insert( Qt::Key_Launch4, EKeyApplication4 ); + m.insert( Qt::Key_Launch5, EKeyApplication5 ); + m.insert( Qt::Key_Launch6, EKeyApplication6 ); + m.insert( Qt::Key_Launch7, EKeyApplication7 ); + m.insert( Qt::Key_Launch8, EKeyApplication8 ); + m.insert( Qt::Key_Launch9, EKeyApplication9 ); + m.insert( Qt::Key_LaunchA, EKeyApplicationA ); + m.insert( Qt::Key_LaunchB, EKeyApplicationB ); + m.insert( Qt::Key_LaunchC, EKeyApplicationC ); + m.insert( Qt::Key_LaunchD, EKeyApplicationD ); + m.insert( Qt::Key_LaunchE, EKeyApplicationE ); + m.insert( Qt::Key_LaunchF, EKeyApplicationF ); + + m.insert( Qt::Key_Yes, EKeyYes ); + m.insert( Qt::Key_No, EKeyNo ); + + m.insert( Qt::Key_Call, EKeyYes ); + m.insert( Qt::Key_Hangup, EKeyNo ); + + return m; +} + +QMap<int,uint> qt_modifier_to_symbian_modifier_make() +{ + QMap<int,uint> m; + m.insert( Qt::ShiftModifier, EModifierShift ); + m.insert( Qt::ControlModifier, EModifierCtrl ); + m.insert( Qt::AltModifier, EModifierAlt ); + return m; +} + +QMap<int,uint> qt_button_to_symbian_button_down_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, TRawEvent::EButton1Down ); + m.insert( Qt::MidButton, TRawEvent::EButton2Down ); + m.insert( Qt::RightButton, TRawEvent::EButton3Down ); + return m; +} + +QMap<int,uint> qt_button_to_symbian_button_up_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, TRawEvent::EButton1Up ); + m.insert( Qt::MidButton, TRawEvent::EButton2Up ); + m.insert( Qt::RightButton, TRawEvent::EButton3Up ); + return m; +} + +class QInputGeneratorPrivate +{ +public: + QInputGeneratorPrivate(); + QInputGenerator* q; + + void keyEvent(Qt::Key, bool); + void mouseEvent(QPoint const&, Qt::MouseButtons); + + void ensureModifiers(Qt::KeyboardModifiers); + Qt::KeyboardModifiers currentModifiers() const; + + QMap<int,int> const keyMap; + QMap<int,uint> const buttonDownMap; + QMap<int,uint> const buttonUpMap; + QMap<int,uint> const buttonDragMap; + + QPoint currentPos; + Qt::MouseButtons currentButtons; +}; + +QInputGeneratorPrivate::QInputGeneratorPrivate() + : keyMap(qt_key_to_symbian_keycode_make()), + buttonDownMap(qt_button_to_symbian_button_down_make()), + buttonUpMap(qt_button_to_symbian_button_up_make()), + currentPos(), + currentButtons(0) +{ +} + +QInputGenerator::QInputGenerator(QObject* parent) + : QObject(parent), + d(new QInputGeneratorPrivate) +{ + d->q = this; + QINPUTGENERATOR_DEBUG() << "constructor"; +} + +QInputGenerator::~QInputGenerator() +{ + QINPUTGENERATOR_DEBUG() << "destructor"; + + /* + Restore all keyboard modifiers to off. + If we don't do this, the current modifiers stay activated + even when this application is closed. + Note that there is no guarantee this code actually gets run. + */ + d->ensureModifiers(0); + if (d->currentButtons) { + d->mouseEvent(d->currentPos, 0); + } + + d->q = 0; + delete d; + d = 0; +} + +/* + Returns the Qt keyboard modifiers which are currently pressed. +*/ +Qt::KeyboardModifiers QInputGeneratorPrivate::currentModifiers() const +{ + quint32 modifiers = 0;//GetCurrentKeyModifiers(); + + int state = 0; +/* + state |= (modifiers & shiftKey ? Qt::ShiftModifier : Qt::NoModifier); + state |= (modifiers & controlKey ? Qt::ControlModifier : Qt::NoModifier); + state |= (modifiers & optionKey ? Qt::AltModifier : Qt::NoModifier); + state |= (modifiers & cmdKey ? Qt::MetaModifier : Qt::NoModifier); +*/ + return Qt::KeyboardModifier(state); +} + +void QInputGeneratorPrivate::ensureModifiers(Qt::KeyboardModifiers desiredMod) +{ + Qt::KeyboardModifiers currentMod = currentModifiers(); + + // For each modifier.. + for (unsigned i = 0; i < sizeof(q->AllModifiers)/sizeof(Qt::KeyboardModifier); ++i) { + Qt::KeyboardModifier thisMod = q->AllModifiers[i]; + // If the modifier is currently disabled but we want it enabled, or vice-versa... + if ((desiredMod & thisMod) && !(currentMod & thisMod)) { + QINPUTGENERATOR_DEBUG() << "Enabling modifier" << (void*)thisMod << "by press"; + // press to enable + keyEvent(q->modifierToKey(thisMod), true); + } else if (!(desiredMod & thisMod) && (currentMod & thisMod)) { + QINPUTGENERATOR_DEBUG() << "Disabling modifier" << (void*)thisMod << "by release"; + // release to disable + keyEvent(q->modifierToKey(thisMod), false); + } + } + + if (currentMod != desiredMod) { + currentMod = desiredMod; + for (int i = 0; + i < 1000 && QApplication::keyboardModifiers() != desiredMod; + i += 50, QtUiTest::wait(50)) + {} + + if (QApplication::keyboardModifiers() != desiredMod) + qWarning() << "QtUitest: couldn't set all modifiers to desired state! " + "Current state:" << (void*)(int)QApplication::keyboardModifiers() << + "Desired:" << (void*)(int)desiredMod; + } +} + +void QInputGeneratorPrivate::keyEvent(Qt::Key key, bool is_press) +{ + int sym = 0; + + do { + if (key >= Qt::Key_Space && key <= Qt::Key_AsciiTilde) { + sym = QChar(key).toUpper().toAscii(); + break; + } + + QMap<int,int>::const_iterator found = keyMap.find(key); + if (found != keyMap.end()) { + sym = *found; + break; + } + + qWarning() << "QInputGenerator: don't know how to translate Qt key" + << (void*)key << "into Symbian keycode"; + return; + + } while(0); + + RWsSession wsSession=CCoeEnv::Static()->WsSession(); + TRawEvent keyEvent; + keyEvent.Set(is_press ? TRawEvent::EKeyDown : TRawEvent::EKeyUp, sym); + wsSession.SimulateRawEvent(keyEvent); + wsSession.Flush(); + + QINPUTGENERATOR_DEBUG() << (is_press ? "press" : "release") << sym; +} + +void QInputGenerator::keyPress(Qt::Key key, Qt::KeyboardModifiers mod, bool autoRepeat) +{ + Q_UNUSED(autoRepeat); + d->ensureModifiers(mod); + d->keyEvent(key, true); + QtUiTest::wait(1); +} + +void QInputGenerator::keyRelease(Qt::Key key, Qt::KeyboardModifiers mod) +{ + d->ensureModifiers(mod); + d->keyEvent(key, false); + QtUiTest::wait(1); +} + +void QInputGenerator::keyClick(Qt::Key key, Qt::KeyboardModifiers mod) +{ + // Using RWsSession::SimulateKeyEvent seems to work more reliably than using + // TRawEvent but it only simulates keyclicks (press and release) + + int sym = 0; + + do { + if (key >= Qt::Key_Space && key <= Qt::Key_AsciiTilde) { + sym = QChar(key).toUpper().toAscii(); + if (mod == Qt::NoModifier) { + sym = sym = QChar(key).toLower().toAscii(); + } + break; + } + + QMap<int,int>::const_iterator found = d->keyMap.find(key); + if (found != d->keyMap.end()) { + sym = *found; + break; + } + + qWarning() << "QInputGenerator: don't know how to translate Qt key" + << (void*)key << "into Symbian keycode"; + return; + + } while(0); + + RWsSession wsSession=CCoeEnv::Static()->WsSession(); + + TKeyEvent keyEvent; + keyEvent.iCode = sym; + keyEvent.iScanCode = 0; // don't think Qt requires this + keyEvent.iModifiers = 0; // should set this + keyEvent.iRepeats = 0; + wsSession.SimulateKeyEvent(keyEvent); + wsSession.Flush(); + QtUiTest::wait(10); +} + +void QInputGeneratorPrivate::mouseEvent(QPoint const& pos, Qt::MouseButtons state) +{ + typedef QPair<uint,uint> ButtonEvent; + QList<ButtonEvent> buttonEvents; + + RWsSession wsSession=CCoeEnv::Static()->WsSession(); + + // FIXME: This doesn't handle screen orientation yet + int x = pos.x(); + int y = pos.y(); + + foreach (int button, buttonDownMap.keys()) { + bool desired = button & state; + bool current = button & currentButtons; + + // Do we need to release? + if (!desired && current) { + TRawEvent event; + event.Set(TRawEvent::EButton1Up, x, y); + wsSession.SimulateRawEvent(event); + wsSession.Flush(); + } + + // Do we need to press? + if (desired && !current) { + TRawEvent event; + event.Set(TRawEvent::EButton1Down, x, y); + wsSession.SimulateRawEvent(event); + wsSession.Flush(); + } + } + + currentButtons = state; +} + +void QInputGenerator::mousePress(QPoint const& pos, Qt::MouseButtons state) +{ + QINPUTGENERATOR_DEBUG() << "Mouse press" << pos << (void*)(int)state; + d->mouseEvent(pos, d->currentButtons | state); + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); +} + +void QInputGenerator::mouseRelease(QPoint const& pos, Qt::MouseButtons state) +{ + QINPUTGENERATOR_DEBUG() << "Mouse release" << pos << (void*)(int)(d->currentButtons & ~state); + d->mouseEvent(pos, d->currentButtons & ~state); + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); +} + +void QInputGenerator::mouseClick(QPoint const& pos, Qt::MouseButtons state) +{ + mousePress (pos,state); + QtUiTest::wait(20); + mouseRelease(pos,state); +} diff --git a/old/libqtuitest/qinputgenerator_win.cpp b/old/libqtuitest/qinputgenerator_win.cpp new file mode 100644 index 0000000..991755b --- /dev/null +++ b/old/libqtuitest/qinputgenerator_win.cpp @@ -0,0 +1,496 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qinputgenerator_p.h" + +#include <QtCore> +#include <QtGui> + +#include <qt_windows.h> +#include <windows.h> +#ifdef __GNUC__ + #include <winable.h> +#endif + +#include "qtuitestnamespace.h" + +#define QINPUTGENERATOR_DEBUG() if (1); else qDebug() << "QInputGenerator:" + +enum WindowsNativeModifiers { + ShiftLeft = 0x00000001, + ControlLeft = 0x00000002, + AltLeft = 0x00000004, + MetaLeft = 0x00000008, + ShiftRight = 0x00000010, + ControlRight = 0x00000020, + AltRight = 0x00000040, + MetaRight = 0x00000080, + CapsLock = 0x00000100, + NumLock = 0x00000200, + ScrollLock = 0x00000400, + ExtendedKey = 0x01000000, + + // Convenience mappings + ShiftAny = 0x00000011, + ControlAny = 0x00000022, + AltAny = 0x00000044, + MetaAny = 0x00000088, + LockAny = 0x00000700 +}; + +QMap<int,int> qt_key_to_win_vk_make() +{ + QMap<int,int> m; + + m.insert( Qt::Key_Escape, VK_ESCAPE ); + m.insert( Qt::Key_Tab, VK_TAB ); + m.insert( Qt::Key_Backspace, VK_BACK ); + m.insert( Qt::Key_Return, VK_RETURN ); + m.insert( Qt::Key_Enter, VK_RETURN ); + m.insert( Qt::Key_Insert, VK_INSERT ); + m.insert( Qt::Key_Delete, VK_DELETE ); + m.insert( Qt::Key_Pause, VK_PAUSE ); + m.insert( Qt::Key_Print, VK_SNAPSHOT ); + m.insert( Qt::Key_Mode_switch, VK_MODECHANGE ); + m.insert( Qt::Key_PageUp, VK_PRIOR ); + m.insert( Qt::Key_PageDown, VK_NEXT ); + m.insert( Qt::Key_End, VK_END ); + m.insert( Qt::Key_Home, VK_HOME ); + m.insert( Qt::Key_Sleep, VK_SLEEP ); + m.insert( Qt::Key_Left, VK_LEFT ); + m.insert( Qt::Key_Up, VK_UP ); + m.insert( Qt::Key_Right, VK_RIGHT ); + m.insert( Qt::Key_Down, VK_DOWN ); + m.insert( Qt::Key_Select, VK_SELECT ); + m.insert( Qt::Key_Printer, VK_PRINT ); + m.insert( Qt::Key_Execute, VK_EXECUTE ); + m.insert( Qt::Key_CapsLock, VK_CAPITAL ); + m.insert( Qt::Key_NumLock, VK_NUMLOCK ); + m.insert( Qt::Key_ScrollLock, VK_SCROLL ); + m.insert( Qt::Key_F1, VK_F1 ); + m.insert( Qt::Key_F2, VK_F2 ); + m.insert( Qt::Key_F3, VK_F3 ); + m.insert( Qt::Key_F4, VK_F4 ); + m.insert( Qt::Key_F5, VK_F5 ); + m.insert( Qt::Key_F6, VK_F6 ); + m.insert( Qt::Key_F7, VK_F7 ); + m.insert( Qt::Key_F8, VK_F8 ); + m.insert( Qt::Key_F9, VK_F9 ); + m.insert( Qt::Key_F10, VK_F10 ); + m.insert( Qt::Key_F11, VK_F11 ); + m.insert( Qt::Key_F12, VK_F12 ); + m.insert( Qt::Key_F13, VK_F13 ); + m.insert( Qt::Key_F14, VK_F14 ); + m.insert( Qt::Key_F15, VK_F15 ); + m.insert( Qt::Key_F16, VK_F16 ); + m.insert( Qt::Key_F17, VK_F17 ); + m.insert( Qt::Key_F18, VK_F18 ); + m.insert( Qt::Key_F19, VK_F19 ); + m.insert( Qt::Key_F20, VK_F20 ); + m.insert( Qt::Key_F21, VK_F21 ); + m.insert( Qt::Key_F22, VK_F22 ); + m.insert( Qt::Key_F23, VK_F23 ); + m.insert( Qt::Key_F24, VK_F24 ); + m.insert( Qt::Key_Menu, VK_APPS ); + m.insert( Qt::Key_Help, VK_HELP ); + m.insert( Qt::Key_Cancel, VK_CANCEL ); + m.insert( Qt::Key_Clear, VK_CLEAR ); + m.insert( Qt::Key_Play, VK_PLAY ); + m.insert( Qt::Key_Zoom, VK_ZOOM ); + +#if (_WIN32_WINNT >= 0x0500) +#if !defined(VK_OEM_BACKTAB) +# define VK_OEM_BACKTAB 0xF5 +#endif + m.insert( Qt::Key_Backtab, VK_OEM_BACKTAB ); + m.insert( Qt::Key_Back, VK_BROWSER_BACK ); + m.insert( Qt::Key_Forward, VK_BROWSER_FORWARD ); + m.insert( Qt::Key_Refresh, VK_BROWSER_REFRESH ); + m.insert( Qt::Key_Stop, VK_BROWSER_STOP ); + m.insert( Qt::Key_Search, VK_BROWSER_SEARCH ); + m.insert( Qt::Key_Favorites, VK_BROWSER_FAVORITES ); + m.insert( Qt::Key_HomePage, VK_BROWSER_HOME ); + m.insert( Qt::Key_VolumeMute, VK_VOLUME_MUTE ); + m.insert( Qt::Key_VolumeDown, VK_VOLUME_DOWN ); + m.insert( Qt::Key_VolumeUp, VK_VOLUME_UP ); + m.insert( Qt::Key_MediaNext, VK_MEDIA_NEXT_TRACK ); + m.insert( Qt::Key_MediaPrevious, VK_MEDIA_PREV_TRACK ); + m.insert( Qt::Key_MediaStop, VK_MEDIA_STOP ); + m.insert( Qt::Key_MediaPlay, VK_MEDIA_PLAY_PAUSE ); + m.insert( Qt::Key_LaunchMail, VK_LAUNCH_MAIL ); + m.insert( Qt::Key_LaunchMedia, VK_LAUNCH_MEDIA_SELECT ); + m.insert( Qt::Key_Launch0, VK_LAUNCH_APP1 ); + m.insert( Qt::Key_Launch1, VK_LAUNCH_APP2 ); +#endif + + + // Modifiers + m.insert( Qt::ShiftModifier, VK_SHIFT ); + m.insert( Qt::Key_Shift, VK_SHIFT ); + m.insert( Qt::ControlModifier, VK_CONTROL ); + m.insert( Qt::Key_Control, VK_CONTROL ); + m.insert( Qt::AltModifier, VK_MENU ); + m.insert( Qt::Key_Alt, VK_MENU ); + m.insert( Qt::MetaModifier, VK_LWIN ); + m.insert( Qt::Key_Meta, VK_LWIN ); + + return m; +} + +QMap<int,uint> qt_button_to_win_button_down_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, MOUSEEVENTF_LEFTDOWN ); + m.insert( Qt::MidButton, MOUSEEVENTF_MIDDLEDOWN ); + m.insert( Qt::RightButton, MOUSEEVENTF_RIGHTDOWN ); + +#if (_WIN32_WINNT >= 0x0500) + m.insert( Qt::XButton1, MOUSEEVENTF_XDOWN ); + m.insert( Qt::XButton2, MOUSEEVENTF_XDOWN ); +#endif + + return m; +} + +QMap<int,uint> qt_button_to_win_button_up_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, MOUSEEVENTF_LEFTUP ); + m.insert( Qt::MidButton, MOUSEEVENTF_MIDDLEUP ); + m.insert( Qt::RightButton, MOUSEEVENTF_RIGHTUP ); + +#if (_WIN32_WINNT >= 0x0500) + m.insert( Qt::XButton1, MOUSEEVENTF_XUP ); + m.insert( Qt::XButton2, MOUSEEVENTF_XUP ); +#endif + + return m; +} + +QMap<int,uint> qt_button_to_win_mousedata_make() +{ + QMap<int,uint> m; + +#if (_WIN32_WINNT >= 0x0500) + m.insert( Qt::XButton1, XBUTTON1 ); + m.insert( Qt::XButton2, XBUTTON2 ); +#endif + + return m; +} + +class QInputGeneratorPrivate +{ +public: + QInputGeneratorPrivate(); + QInputGenerator* q; + + void keyEvent(Qt::Key, bool); + void mouseEvent(QPoint const&, Qt::MouseButtons); + + void ensureModifiers(Qt::KeyboardModifiers); + Qt::KeyboardModifiers currentModifiers() const; + + QMap<int,int> const keyMap; + QMap<int,uint> const buttonDownMap; + QMap<int,uint> const buttonUpMap; + QMap<int,uint> const buttonMouseDataMap; + + QPoint currentPos; + Qt::MouseButtons currentButtons; +}; + +QInputGeneratorPrivate::QInputGeneratorPrivate() + : keyMap(qt_key_to_win_vk_make()), + buttonDownMap(qt_button_to_win_button_down_make()), + buttonUpMap(qt_button_to_win_button_up_make()), + buttonMouseDataMap(qt_button_to_win_mousedata_make()), + currentPos(), + currentButtons(0) +{} + +QInputGenerator::QInputGenerator(QObject* parent) + : QObject(parent), + d(new QInputGeneratorPrivate) +{ + d->q = this; + QINPUTGENERATOR_DEBUG() << "constructor"; +} + +QInputGenerator::~QInputGenerator() +{ + QINPUTGENERATOR_DEBUG() << "destructor"; + + /* + Restore all keyboard modifiers to off. + Note that there is no guarantee this code actually gets run. + */ + d->ensureModifiers(0); + if (d->currentButtons) { + d->mouseEvent(d->currentPos, 0); + } + + d->q = 0; + delete d; + d = 0; +} + +/* + Returns the Qt keyboard modifiers which are currently pressed. +*/ +Qt::KeyboardModifiers QInputGeneratorPrivate::currentModifiers() const +{ + quint32 nModifiers = 0; + + if (QSysInfo::WindowsVersion < QSysInfo::WV_NT || QSysInfo::WindowsVersion & QSysInfo::WV_CE_based) { + nModifiers |= (GetKeyState(VK_SHIFT ) < 0 ? ShiftAny : 0); + nModifiers |= (GetKeyState(VK_CONTROL) < 0 ? ControlAny : 0); + nModifiers |= (GetKeyState(VK_MENU ) < 0 ? AltAny : 0); + nModifiers |= (GetKeyState(VK_LWIN ) < 0 ? MetaLeft : 0); + nModifiers |= (GetKeyState(VK_RWIN ) < 0 ? MetaRight : 0); + } else { + // Map native modifiers to some bit representation + nModifiers |= (GetKeyState(VK_LSHIFT ) & 0x80 ? ShiftLeft : 0); + nModifiers |= (GetKeyState(VK_RSHIFT ) & 0x80 ? ShiftRight : 0); + nModifiers |= (GetKeyState(VK_LCONTROL) & 0x80 ? ControlLeft : 0); + nModifiers |= (GetKeyState(VK_RCONTROL) & 0x80 ? ControlRight : 0); + nModifiers |= (GetKeyState(VK_LMENU ) & 0x80 ? AltLeft : 0); + nModifiers |= (GetKeyState(VK_RMENU ) & 0x80 ? AltRight : 0); + nModifiers |= (GetKeyState(VK_LWIN ) & 0x80 ? MetaLeft : 0); + nModifiers |= (GetKeyState(VK_RWIN ) & 0x80 ? MetaRight : 0); + // Add Lock keys to the same bits + nModifiers |= (GetKeyState(VK_CAPITAL ) & 0x01 ? CapsLock : 0); + nModifiers |= (GetKeyState(VK_NUMLOCK ) & 0x01 ? NumLock : 0); + nModifiers |= (GetKeyState(VK_SCROLL ) & 0x01 ? ScrollLock : 0); + } + + int state = 0; + state |= (nModifiers & ShiftAny ? Qt::ShiftModifier : 0); + state |= (nModifiers & ControlAny ? Qt::ControlModifier : 0); + state |= (nModifiers & AltAny ? Qt::AltModifier : 0); + state |= (nModifiers & MetaAny ? Qt::MetaModifier : 0); + + return Qt::KeyboardModifier(state); +} + +void QInputGeneratorPrivate::ensureModifiers(Qt::KeyboardModifiers desiredMod) +{ + Qt::KeyboardModifiers currentMod = currentModifiers(); + + // For each modifier.. + for (unsigned i = 0; i < sizeof(q->AllModifiers)/sizeof(Qt::KeyboardModifier); ++i) { + Qt::KeyboardModifier thisMod = q->AllModifiers[i]; + // If the modifier is currently disabled but we want it enabled, or vice-versa... + if ((desiredMod & thisMod) && !(currentMod & thisMod)) { + QINPUTGENERATOR_DEBUG() << "Enabling modifier" << (void*)thisMod << "by press"; + // press to enable + keyEvent(q->modifierToKey(thisMod), true); + } else if (!(desiredMod & thisMod) && (currentMod & thisMod)) { + QINPUTGENERATOR_DEBUG() << "Disabling modifier" << (void*)thisMod << "by release"; + // release to disable + keyEvent(q->modifierToKey(thisMod), false); + } + } + + if (currentMod != desiredMod) { + currentMod = desiredMod; + for (int i = 0; + i < 1000 && QApplication::keyboardModifiers() != desiredMod; + i += 50, QtUiTest::wait(50)) + {} + + if (QApplication::keyboardModifiers() != desiredMod) + qWarning() << "QtUitest: couldn't set all modifiers to desired state! " + "Current state:" << (void*)(int)QApplication::keyboardModifiers() << + "Desired:" << (void*)(int)desiredMod; + } + +} + +void QInputGeneratorPrivate::keyEvent(Qt::Key key, bool is_press) +{ + int sym = 0; + Qt::KeyboardModifiers mod = 0; + + do { + if (key >= Qt::Key_0 && key <= Qt::Key_9 || + key >= Qt::Key_A && key <= Qt::Key_Z) { + sym = QChar(key).toUpper().toAscii(); + break; + } + + if (key >= Qt::Key_Space && key <= Qt::Key_AsciiTilde) { + sym = VkKeyScan(QChar(key).toAscii()); + if (sym & 0x0100) mod |= Qt::ShiftModifier; + if (sym & 0x0200) mod |= Qt::ControlModifier; + if (sym & 0x0400) mod |= Qt::AltModifier; + break; + } + + QMap<int,int>::const_iterator found = keyMap.find(key); + if (found != keyMap.end()) { + sym = *found; + break; + } + + qWarning() << "QInputGenerator: don't know how to translate Qt key" + << (void*)key << "into Windows virtual key"; + return; + + } while(0); + + if (mod) { + ensureModifiers(mod); + } + + QINPUTGENERATOR_DEBUG() << (is_press ? "press" : "release") << sym; + + KEYBDINPUT kbi = {0}; + INPUT input = {0}; + + kbi.wVk = sym; + if (!is_press) kbi.dwFlags = KEYEVENTF_KEYUP; + + input.ki = kbi; + input.type = INPUT_KEYBOARD; + SendInput(1, &input, sizeof(input)); +} + +void QInputGenerator::keyPress(Qt::Key key, Qt::KeyboardModifiers mod, bool autoRepeat) +{ + Q_UNUSED(autoRepeat); + d->ensureModifiers(mod); + d->keyEvent(key, true); +} + +void QInputGenerator::keyRelease(Qt::Key key, Qt::KeyboardModifiers mod) +{ + d->ensureModifiers(mod); + d->keyEvent(key, false); +} + +void QInputGenerator::keyClick(Qt::Key key, Qt::KeyboardModifiers mod) +{ + keyPress(key,mod); + keyRelease(key,mod); +} + +void QInputGeneratorPrivate::mouseEvent(QPoint const& pos, Qt::MouseButtons state) +{ + if (currentPos != pos) { + currentPos = pos; + + MOUSEINPUT mi = {0}; + INPUT input = {0}; + + QRect rect = QApplication::desktop()->screenGeometry(); // FIXME: Uses default screen for now + + mi.dx = LONG(pos.x() * 65535.0 / rect.width() + 8); + mi.dy = LONG(pos.y() * 65535.0 / rect.height() + 8); + mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; + input.mi = mi; + input.type = INPUT_MOUSE; + SendInput(1, &input, sizeof(input)); + + for (int i = 0; + i < 1000 && QCursor::pos() != pos; + i += 50, QtUiTest::wait(50)) + {} + + if (QCursor::pos() != pos) + qWarning() << "QtUitest: couldn't move cursor to desired point! " + "Current position:" << QCursor::pos() << + "Desired:" << pos; + + } + + typedef QPair<uint,uint> ButtonEvent; + QList<ButtonEvent> buttonEvents; + + foreach (int button, buttonDownMap.keys()) { + bool desired = button & state; + bool current = button & currentButtons; + + // Do we need to release? + if (!desired && current) { + buttonEvents << qMakePair(buttonUpMap[button], buttonMouseDataMap[button]); + } + + // Do we need to press? + if (desired && !current) { + buttonEvents << qMakePair(buttonDownMap[button], buttonMouseDataMap[button]); + } + } + + foreach (ButtonEvent const& event, buttonEvents) { + MOUSEINPUT mi = {0}; + INPUT input = {0}; + + mi.dwFlags = event.first; + mi.mouseData = event.second; + input.mi = mi; + input.type = INPUT_MOUSE; + SendInput(1, &input, sizeof(input)); + } + + currentButtons = state; +} + +void QInputGenerator::mousePress(QPoint const& pos, Qt::MouseButtons state) +{ + QINPUTGENERATOR_DEBUG() << "Mouse press" << pos << (void*)(int)state; + d->mouseEvent(pos, d->currentButtons | state); +} + +void QInputGenerator::mouseRelease(QPoint const& pos, Qt::MouseButtons state) +{ + QINPUTGENERATOR_DEBUG() << "Mouse release" << pos << (void*)(int)(d->currentButtons & ~state); + d->mouseEvent(pos, d->currentButtons & ~state); +} + +void QInputGenerator::mouseClick(QPoint const& pos, Qt::MouseButtons state) +{ + mousePress (pos,state); + mouseRelease(pos,state); +} + diff --git a/old/libqtuitest/qinputgenerator_x11.cpp b/old/libqtuitest/qinputgenerator_x11.cpp new file mode 100644 index 0000000..11bb7e1 --- /dev/null +++ b/old/libqtuitest/qinputgenerator_x11.cpp @@ -0,0 +1,514 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qinputgenerator_p.h" + +#include <QtCore> +#include <QtGui> +#include <QX11Info> +#include <X11/extensions/XTest.h> +#include <X11/keysym.h> + +#include "qtuitestnamespace.h" + +#define QINPUTGENERATOR_DEBUG() if (1); else qDebug() << "QInputGenerator:" + + +QMap<int,int> qt_key_to_keysym_make() +{ + QMap<int,int> m; + +#define QT_K(Qt,X) m.insert(Qt,X) + QT_K( '\n', XK_Return ); + QT_K( Qt::Key_Escape, XK_Escape ); + QT_K( Qt::Key_Tab, XK_Tab ); + QT_K( Qt::Key_Backtab, XK_ISO_Left_Tab ); + QT_K( Qt::Key_Backspace, XK_BackSpace ); + QT_K( Qt::Key_Return, XK_Return ); + QT_K( Qt::Key_Enter, XK_KP_Enter ); + QT_K( Qt::Key_Insert, XK_Insert ); + QT_K( Qt::Key_Delete, XK_Delete ); + QT_K( Qt::Key_Pause, XK_Pause ); + QT_K( Qt::Key_Print, XK_Print ); + QT_K( Qt::Key_SysReq, XK_Sys_Req ); + QT_K( Qt::Key_Home, XK_Home ); + QT_K( Qt::Key_End, XK_End ); + QT_K( Qt::Key_Left, XK_Left ); + QT_K( Qt::Key_Up, XK_Up ); + QT_K( Qt::Key_Right, XK_Right ); + QT_K( Qt::Key_Down, XK_Down ); + QT_K( Qt::Key_CapsLock, XK_Caps_Lock ); + QT_K( Qt::Key_NumLock, XK_Num_Lock ); + QT_K( Qt::Key_ScrollLock, XK_Scroll_Lock ); + QT_K( Qt::Key_F1, XK_F1 ); + QT_K( Qt::Key_F2, XK_F2 ); + QT_K( Qt::Key_F3, XK_F3 ); + QT_K( Qt::Key_F4, XK_F4 ); + QT_K( Qt::Key_F5, XK_F5 ); + QT_K( Qt::Key_F6, XK_F6 ); + QT_K( Qt::Key_F7, XK_F7 ); + QT_K( Qt::Key_F8, XK_F8 ); + QT_K( Qt::Key_F9, XK_F9 ); + QT_K( Qt::Key_F10, XK_F10 ); + QT_K( Qt::Key_F11, XK_F11 ); + QT_K( Qt::Key_F12, XK_F12 ); + QT_K( Qt::Key_F13, XK_F13 ); + QT_K( Qt::Key_F14, XK_F14 ); + QT_K( Qt::Key_F15, XK_F15 ); + QT_K( Qt::Key_F16, XK_F16 ); + QT_K( Qt::Key_F17, XK_F17 ); + QT_K( Qt::Key_F18, XK_F18 ); + QT_K( Qt::Key_F19, XK_F19 ); + QT_K( Qt::Key_F20, XK_F20 ); + QT_K( Qt::Key_F21, XK_F21 ); + QT_K( Qt::Key_F22, XK_F22 ); + QT_K( Qt::Key_F23, XK_F23 ); + QT_K( Qt::Key_F24, XK_F24 ); + QT_K( Qt::Key_F25, XK_F25 ); + QT_K( Qt::Key_F26, XK_F26 ); + QT_K( Qt::Key_F27, XK_F27 ); + QT_K( Qt::Key_F28, XK_F28 ); + QT_K( Qt::Key_F29, XK_F29 ); + QT_K( Qt::Key_F30, XK_F30 ); + QT_K( Qt::Key_F31, XK_F31 ); + QT_K( Qt::Key_F32, XK_F32 ); + QT_K( Qt::Key_F33, XK_F33 ); + QT_K( Qt::Key_F34, XK_F34 ); + QT_K( Qt::Key_F35, XK_F35 ); + QT_K( Qt::Key_Super_L, XK_Super_L ); + QT_K( Qt::Key_Super_R, XK_Super_R ); + QT_K( Qt::Key_Menu, XK_Menu ); + QT_K( Qt::Key_Hyper_L, XK_Hyper_L ); + QT_K( Qt::Key_Hyper_R, XK_Hyper_R ); + QT_K( Qt::Key_Help, XK_Help ); + QT_K( '/', XK_KP_Divide ); +// QT_K( '*', XK_KP_Multiply ); + QT_K( '-', XK_KP_Subtract ); + QT_K( '+', XK_KP_Add ); + QT_K( Qt::Key_Return, XK_KP_Enter ); + QT_K( Qt::Key_Kanji, XK_Kanji ); + QT_K( Qt::Key_Muhenkan, XK_Muhenkan ); + QT_K( Qt::Key_Henkan, XK_Henkan ); + QT_K( Qt::Key_Romaji, XK_Romaji ); + QT_K( Qt::Key_Hiragana, XK_Hiragana ); + QT_K( Qt::Key_Katakana, XK_Katakana ); + QT_K( Qt::Key_Hiragana_Katakana, XK_Hiragana_Katakana ); + QT_K( Qt::Key_Zenkaku, XK_Zenkaku ); + QT_K( Qt::Key_Hankaku, XK_Hankaku ); + QT_K( Qt::Key_Zenkaku_Hankaku, XK_Zenkaku_Hankaku ); + QT_K( Qt::Key_Touroku, XK_Touroku ); + QT_K( Qt::Key_Massyo, XK_Massyo ); + QT_K( Qt::Key_Kana_Lock, XK_Kana_Lock ); + QT_K( Qt::Key_Kana_Shift, XK_Kana_Shift ); + QT_K( Qt::Key_Eisu_Shift, XK_Eisu_Shift ); + QT_K( Qt::Key_Eisu_toggle,XK_Eisu_toggle ); + QT_K( Qt::Key_Hangul, XK_Hangul ); + QT_K( Qt::Key_Hangul_Start, XK_Hangul_Start ); + QT_K( Qt::Key_Hangul_End, XK_Hangul_End ); + QT_K( Qt::Key_Hangul_Hanja, XK_Hangul_Hanja ); + QT_K( Qt::Key_Hangul_Jamo, XK_Hangul_Jamo ); + QT_K( Qt::Key_Hangul_Romaja, XK_Hangul_Romaja ); + QT_K( Qt::Key_Hangul_Jeonja, XK_Hangul_Jeonja ); + QT_K( Qt::Key_Hangul_Banja, XK_Hangul_Banja ); + QT_K( Qt::Key_Hangul_PreHanja, XK_Hangul_PreHanja ); + QT_K( Qt::Key_Hangul_PostHanja,XK_Hangul_PostHanja ); + QT_K( Qt::Key_Hangul_Special, XK_Hangul_Special ); + + // Modifiers + QT_K( Qt::ShiftModifier, XK_Shift_L ); + QT_K( Qt::Key_Shift, XK_Shift_L ); + QT_K( Qt::ControlModifier,XK_Control_L ); + QT_K( Qt::Key_Control, XK_Control_L ); +// QT_K( Qt::AltModifier, XK_Alt_L ); +// QT_K( Qt::Key_Alt, XK_Alt_L ); + QT_K( Qt::MetaModifier, XK_Meta_L ); + QT_K( Qt::Key_Meta, XK_Meta_L ); + + QT_K( Qt::AltModifier, XK_ISO_Level3_Shift ); + QT_K( Qt::Key_Alt, XK_ISO_Level3_Shift ); + // FIXME support Qt::KeypadModifier + +#undef QT_K + + return m; +} + +QMap<int,uint> qt_button_to_x_button_make() +{ + QMap<int,uint> m; + + m.insert(Qt::LeftButton, 1); + m.insert(Qt::MidButton, 2); + m.insert(Qt::RightButton, 3); + + return m; +} + +QMap<int,int> qt_modifier_to_x_modmask_make() +{ + QMap<int,int> m; + + m.insert( Qt::ShiftModifier, ShiftMask ); + m.insert( Qt::ControlModifier, ControlMask ); +// m.insert( Qt::AltModifier, Mod1Mask ); + m.insert( Qt::MetaModifier, Mod4Mask ); + m.insert( Qt::AltModifier, Mod5Mask ); + + return m; +} + +struct QInputGeneratorPrivate +{ + QInputGeneratorPrivate(); + QInputGenerator* q; + + void keyEvent(Qt::Key, bool); + void mouseEvent(QPoint const&, Qt::MouseButtons); + + void ensureModifiers(Qt::KeyboardModifiers); + Qt::KeyboardModifiers currentModifiers() const; + + QMap<int,int> const keymap; + QMap<int,uint> const buttonmap; + + QPoint currentPos; + Qt::MouseButtons currentButtons; +}; + +QInputGeneratorPrivate::QInputGeneratorPrivate() + : keymap(qt_key_to_keysym_make()), + buttonmap(qt_button_to_x_button_make()), + currentPos(), + currentButtons(0) +{} + +QInputGenerator::QInputGenerator(QObject* parent) + : QObject(parent), + d(new QInputGeneratorPrivate) +{ + d->q = this; + QINPUTGENERATOR_DEBUG() << "constructor"; +} + +QInputGenerator::~QInputGenerator() +{ + QINPUTGENERATOR_DEBUG() << "destructor"; + + /* + Restore all keyboard modifiers to off. + If we don't do this, the current modifiers stay activated for the current X server + even when this application is closed. + Note that there is no guarantee this code actually gets run. + */ + d->ensureModifiers(0); + if (d->currentButtons) { + d->mouseEvent(d->currentPos, 0); + } + + d->q = 0; + delete d; + d = 0; +} + +/* + Returns the Qt keyboard modifiers which are currently pressed. +*/ +Qt::KeyboardModifiers QInputGeneratorPrivate::currentModifiers() const +{ + Display* dpy = QX11Info::display(); + if (!dpy) { + /* No X connection */ + return 0; + } + + Window root = 0; + Window child = 0; + int root_x = 0; + int root_y = 0; + int win_x = 0; + int win_y = 0; + uint buttons = 0; + + // Grab all of the pointer info, though all we really care about is the modifiers. + bool ok = false; + for (int i = 0; i < ScreenCount(dpy) && !ok; ++i) { + if (XQueryPointer(dpy, (Window)QX11Info::appRootWindow(i), &root, &child, &root_x, &root_y, + &win_x, &win_y, &buttons)) + ok = true; + } + + if (!ok) { + qWarning() << + "QInputGenerator: could not determine current state of keyboard modifiers. " + "Simulated key events may be incorrect."; + return 0; + } + + // Convert to Qt::KeyboardModifiers. + static const QMap<int,int> modmap = qt_modifier_to_x_modmask_make(); + + Qt::KeyboardModifiers ret(0); + for (unsigned i = 0; i < sizeof(q->AllModifiers)/sizeof(Qt::KeyboardModifier); ++i) { + Qt::KeyboardModifier thisMod = q->AllModifiers[i]; + int mask = modmap.value(thisMod); + if (buttons & mask) { + QINPUTGENERATOR_DEBUG() << "mod enabled:" << (void*)(int)thisMod << (void*)mask; + ret |= thisMod; + } + } + + QINPUTGENERATOR_DEBUG() << "current modifiers:" << (void*)buttons << (void*)(int)ret; + + return ret; +} + +void QInputGeneratorPrivate::ensureModifiers(Qt::KeyboardModifiers desiredMod) +{ + Qt::KeyboardModifiers currentMod = currentModifiers(); + + // For each modifier.. + for (unsigned i = 0; i < sizeof(q->AllModifiers)/sizeof(Qt::KeyboardModifier); ++i) { + Qt::KeyboardModifier thisMod = q->AllModifiers[i]; + // If the modifier is currently disabled but we want it enabled, or vice-versa... + if ((desiredMod & thisMod) && !(currentMod & thisMod)) { + QINPUTGENERATOR_DEBUG() << "Enabling modifier" << (void*)thisMod << "by press"; + // press to enable + keyEvent(q->modifierToKey(thisMod), true); + } else if (!(desiredMod & thisMod) && (currentMod & thisMod)) { + QINPUTGENERATOR_DEBUG() << "Disabling modifier" << (void*)thisMod << "by release"; + // release to disable + keyEvent(q->modifierToKey(thisMod), false); + } + } + +// if (currentMod != desiredMod) { +// currentMod = desiredMod; +// for (int i = 0; +// i < 1000 && QApplication::keyboardModifiers() != desiredMod; +// i += 50, QtUiTest::wait(50)) +// {} +// +// if (QApplication::keyboardModifiers() != desiredMod) +// qWarning() << "QtUitest: couldn't set all modifiers to desired state! " +// "Current state:" << (void*)(int)QApplication::keyboardModifiers() << +// "Desired:" << (void*)(int)desiredMod; +// } + if (currentMod != desiredMod) { + currentMod = desiredMod; + for (int i = 0; + i < 1000 && currentModifiers() != desiredMod; + i += 50, QtUiTest::wait(50)) + {} + + if (currentModifiers() != desiredMod) + qWarning() << "QtUitest: couldn't set all modifiers to desired state! " + "Current state:" << (void*)(int)currentModifiers() << + "Desired:" << (void*)(int)desiredMod; + } + +} + +void QInputGeneratorPrivate::keyEvent(Qt::Key key, bool is_press) +{ + Display* dpy = QX11Info::display(); + if (!dpy) { + /* No X connection */ + return; + } + + KeySym sym = 0; + + do { +// if ((key >= Qt::Key_0 && key <= Qt::Key_9) || +// (key >= Qt::Key_A && key <= Qt::Key_Z)) { +// sym = QChar(key).toLower().unicode(); +// break; +// } + + if (key >= Qt::Key_A && key <= Qt::Key_Z) { + sym = QChar(key).toLower().unicode(); + break; + } + + + QMap<int,int>::const_iterator found = keymap.find(key); + if (found != keymap.end()) { + sym = *found; + break; + } + + if ((key < 0x1000 && key >= 0x20 )|| (key >= Qt::Key_0 && key <= Qt::Key_9) ){ + sym = QChar(key).unicode(); + KeyCode kc = XKeysymToKeycode(dpy, sym); + int syms_per_keycode; + KeySym *keymap = XGetKeyboardMapping(dpy, kc, 1, &syms_per_keycode); + for(int i=0; i<syms_per_keycode;i++) + { + QINPUTGENERATOR_DEBUG()<<"keymap["<<i<<"]is:"<<XKeysymToString(keymap[i]); + } + + if (sym == keymap[1] && sym != keymap[0]) { + ensureModifiers(Qt::ShiftModifier); + } + + else if (sym == keymap[4]) { + ensureModifiers(Qt::AltModifier); + } + + XFree(keymap); + break; + } + + qWarning() << "QInputGenerator: don't know how to translate Qt key" + << (void*)key << "into X11 keysym"; + return; + + } while(0); + + QINPUTGENERATOR_DEBUG() << (is_press ? "press" : "release") << XKeysymToString(sym); + + XTestFakeKeyEvent( + dpy, + XKeysymToKeycode(dpy, sym), + is_press, + 0 + ); +} + +void QInputGenerator::keyPress(Qt::Key key, Qt::KeyboardModifiers mod, bool autoRepeat) +{ + Q_UNUSED(autoRepeat); + d->ensureModifiers(mod); + d->keyEvent(key, true); +} + +void QInputGenerator::keyRelease(Qt::Key key, Qt::KeyboardModifiers mod) +{ + d->ensureModifiers(mod); + d->keyEvent(key, false); +} + +void QInputGenerator::keyClick(Qt::Key key, Qt::KeyboardModifiers mod) +{ + keyPress(key,mod); + keyRelease(key,mod); +} + +void QInputGeneratorPrivate::mouseEvent(QPoint const& pos, Qt::MouseButtons state) +{ + Display* dpy = QX11Info::display(); + if (!dpy) { + /* No X connection */ + return; + } + + if (currentPos != pos) { + currentPos = pos; + + XTestFakeMotionEvent( + dpy, + QX11Info::appScreen(), + pos.x(), + pos.y(), + 0 + ); + + for (int i = 0; + i < 1000 && QCursor::pos() != pos; + i += 50, QtUiTest::wait(50)) + {} + + if (QCursor::pos() != pos) + qWarning() << "QtUitest: couldn't move cursor to desired point! " + "Current position:" << QCursor::pos() << + "Desired:" << pos; + + } + + typedef QPair<uint,bool> ButtonEvent; + QList<ButtonEvent> buttonEvents; + + foreach (int button, buttonmap.keys()) { + bool desired = button & state; + bool current = button & currentButtons; + + // Do we need to press? + if (desired && !current) { + buttonEvents << qMakePair(buttonmap[button], true); + } + + // Do we need to release? + if (!desired && current) { + buttonEvents << qMakePair(buttonmap[button], false); + } + } + + foreach (ButtonEvent const& event, buttonEvents) { + QINPUTGENERATOR_DEBUG() << "Button event at" << pos << ":" << event.first << event.second; + XTestFakeButtonEvent( + dpy, + event.first, + event.second, + 0 + ); + } + + currentButtons = state; +} + +void QInputGenerator::mousePress(QPoint const& pos, Qt::MouseButtons state) +{ + QINPUTGENERATOR_DEBUG() << "Mouse press" << pos << (void*)(int)state; + d->mouseEvent(pos, d->currentButtons | state); +} + +void QInputGenerator::mouseRelease(QPoint const& pos, Qt::MouseButtons state) +{ + QINPUTGENERATOR_DEBUG() << "Mouse release" << pos << (void*)(int)(d->currentButtons & ~state); + d->mouseEvent(pos, d->currentButtons & ~state); +} + +void QInputGenerator::mouseClick(QPoint const& pos, Qt::MouseButtons state) +{ + mousePress (pos,state); + mouseRelease(pos,state); +} + diff --git a/old/libqtuitest/qtestprotocol.cpp b/old/libqtuitest/qtestprotocol.cpp new file mode 100644 index 0000000..713d217 --- /dev/null +++ b/old/libqtuitest/qtestprotocol.cpp @@ -0,0 +1,1162 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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$ +** +****************************************************************************/ + +/* + This cpp file contains a number of socket related classes that are used heavily in QtUiTest. + The classes are documented later in the file. +*/ + +#include "qtestprotocol_p.h" +#include "qtuitestnamespace.h" + +#include <QApplication> +#include <QString> +#include <QTimer> +#include <QUuid> +#include <QFileInfo> +#include <QDir> +#include <QTcpSocket> +#include <QTcpServer> +#include <QHostAddress> +#include <QHostInfo> +#include <QPointer> + +#include <QDebug> +#define qLog(A) if (1); else qDebug() + +#if defined(Q_OS_WIN32) && !defined(Q_OS_TEMP) +# include <io.h> +#endif + +static const int CONNECT_TIMEOUT = 20000; + +static const quint32 TEST_MSG_SIGNATURE = 0xEDBAEDBA; +static const quint32 TEST_MSG_END = 0xEDBAABDE; +static const quint8 TEST_MSG_VERSION = 3; +static uint g_unique_id = 0; + +bool waitForSignal(QObject* object, const char* signal, int timeout) +{ +#ifdef QTUITEST_TARGET + return QtUiTest::waitForSignal(object, signal, timeout); +#else + QEventLoop loop; + QTimer dummy; + dummy.setInterval(1000); + if (!QObject::connect(object, signal, &dummy, SLOT(start()))) + return false; + if (!QObject::connect(object, signal, &loop, SLOT(quit()))) + return false; + QTimer::singleShot(timeout, &loop, SLOT(quit())); + loop.exec(); + return dummy.isActive(); +#endif +} + +void wait(int timeout) +{ +#ifdef QTUITEST_TARGET + QtUiTest::wait(timeout); +#else + QEventLoop loop; + QTimer::singleShot(timeout, &loop, SLOT(quit())); + loop.exec(); +#endif +} + +// ******************************************************************************** +// ****************************** QTestMessage ************************************ +// ******************************************************************************** + +/* + \class QTestMessage QTestMessage.h + \inpublicgroup QtUiTestModule + + \brief The QTestMessage class can be used for exchanging messages between separate + processes. The class is never used directly but instead is used by QTestProtocol. + + The class basically wraps a number of strings (i.e. 'event' and 'message') and binary data + (i.e. a bytearray or a file) into a datastream that is sent to a peer using a socket + connection. On the receiving side a QTestMessage instance is decoding the datastream + and performs a number of sanity checks to make it a bit more robost. + + A TCP connection 'should' be reliable but in exceptional cases bytes may be lost. This + would result in the connection becoming useless because all future data reception would + be out of sync. To solve this a 'resync' function is implemented that can re-sync the datastream + by throwing away bytes until the data seems in sync again. The obvious downside is that + at least one message will be lost. + + Message format is as follows: + + Field: Length: Remarks: + + Start signature 4 Fixed value - 0xEDBAEDBA + Protocol version 1 Fixed value - 3 + Message number 2 + Event length 4 Length of following string + Event string Event length QString value + Message length 4 Length of following string + Messaga string Message length QString value + Data length 4 Length of following binary data + File data Data length Binary data + End signature 4 Fixed value - 0xEDBAABDE +*/ + +/* + \internal + + Constructs a default (empty) message. +*/ + +QTestMessage::QTestMessage(QString const &event, QVariantMap const &map) + : m_phase(0) + , m_msg_id(0) + , m_event(event) + , m_map(map) +{ +} + +QTestMessage::QTestMessage(QString const &event, const QTestMessage &other ) + : m_phase(0) + , m_msg_id(other.m_msg_id) + , m_map(other.m_map) +{ + m_event = event; +} + +QTestMessage::QTestMessage(QString const &event, QString const &queryApp, QString const &queryPath ) + : m_phase(0) + , m_msg_id(0) + , m_event(event) + , m_map(QVariantMap()) +{ + m_map["queryapp"] = queryApp; + m_map["querypath"] = queryPath; +} + +/* + \internal + + Copy constructor. +*/ + +QTestMessage::QTestMessage(const QTestMessage &other) + : m_phase(0) + , m_msg_id(other.m_msg_id) + , m_event(other.m_event) + , m_map(other.m_map) +{ +} + +/* + \internal + + Destroys the message. +*/ + +QTestMessage::~QTestMessage() +{ +} + +/* + \internal + + Assignment operator. +*/ + +QTestMessage& QTestMessage::operator=(const QTestMessage &other) +{ + m_msg_id = other.m_msg_id; + m_event = other.m_event; + m_map = other.m_map; + + return *this; +} + +QVariant &QTestMessage::operator[](QString const &key) +{ + return m_map[key.toLower()]; +} + +QVariant const QTestMessage::operator[](QString const &key) const +{ + return m_map[key.toLower()]; +} + +bool QTestMessage::contains(QString const &key) const +{ + return m_map.contains(key.toLower()); +} + +QList<QString> QTestMessage::keys() const +{ + return m_map.keys(); +} + +QString QTestMessage::toString() const +{ + QString ret; + foreach(QString k, m_map.keys()) { + if (!m_map[k].isValid()) continue; + ret += k + ": "; + if (m_map[k].canConvert<QStringList>()) ret += "'" + m_map[k].toStringList().join("','") + "'"; + else if (m_map[k].canConvert<QString>()) ret += "'" + m_map[k].toString() + "'"; + else ret += "(data)"; + ret += "\n"; + } + if (ret.endsWith("\n")) ret.chop(1); + return ret; +} + +/* + \internal + Returns the event that was received. +*/ + +QString QTestMessage::event() const +{ + return m_event; +} + +/* + \internal + Returns the message number. +*/ + +quint16 QTestMessage::msgId() const +{ + return m_msg_id; +} + +bool QTestMessage::statusOK() const +{ + return m_map.contains("status") && m_map["status"].toString() == "OK"; +} + +bool QTestMessage::isNull() const +{ + return m_map.isEmpty(); +} + + +// ******************************************************************************** +// ************************* QTestServerSocket ************************************ +// ******************************************************************************** + +/* ! + \class QTestServerSocket qtestserversocket.h + \inpublicgroup QtUiTestModule + + \brief The QTestServerSocket class provides a TCP-based server. + + This class is a convenience class for accepting incoming TCP + connections. You can specify the port or have QTestServerSocket pick + one, and listen on just one address or on all the machine's + addresses. + + Using the API is very simple: subclass QTestServerSocket, call the + constructor of your choice, and implement onNewConnection() to + handle new incoming connections. There is nothing more to do. + + (Note that due to lack of support in the underlying APIs, + QTestServerSocket cannot accept or reject connections conditionally.) + + \sa QTcpSocket, QTcpServer, QHostAddress, QSocketNotifier +*/ + + +/* ! + Creates a server socket object, that will serve the given \a port + on all the addresses of this host. If \a port is 0, QTestServerSocket + will pick a suitable port in a system-dependent manner. Use \a + backlog to specify how many pending connections the server can + have. + + \warning On Tru64 Unix systems a value of 0 for \a backlog means + that you don't accept any connections at all; you should specify a + value larger than 0. +*/ + +QTestServerSocket::QTestServerSocket( quint16 port, int backlog ) + : QTcpServer() +{ + setMaxPendingConnections( backlog ); + listen( QHostAddress::Any, port ); + + if (this->serverPort() == 0) { + qWarning( QString("ERROR: port '%1' is already in use, application is aborted.").arg(port).toAscii() ); + QApplication::exit(777); + } +} + +/* ! + Destroys the socket. + + This causes any backlogged connections (connections that have + reached the host, but not yet been completely set up + to be severed. + + Existing connections continue to exist; this only affects the + acceptance of new connections. +*/ +QTestServerSocket::~QTestServerSocket() +{ +} + +/* ! + Returns true if the construction succeeded; otherwise returns false. +*/ +bool QTestServerSocket::ok() const +{ + return serverPort() > 0; +} + +/* ! + Returns the port number on which this server socket listens. This + is always non-zero; if you specify 0 in the constructor, + QTestServerSocket will pick a non-zero port itself. ok() must be true + before calling this function. + + \sa address() +*/ +quint16 QTestServerSocket::port() const +{ + return serverPort(); +} + +/* ! + Returns the address on which this object listens, or 0.0.0.0 if + this object listens on more than one address. ok() must be true + before calling this function. + + \sa port() +*/ +QString QTestServerSocket::address() const +{ + return serverAddress().toString(); +} + +void QTestServerSocket::incomingConnection( int socket ) +{ + onNewConnection( socket ); +} + +// ******************************************************************************** +// *************************** QTestProtocol ************************************ +// ******************************************************************************** + +/* + \class QTestProtocol qtestprotocol.h + \inpublicgroup QtUiTestModule + + \brief The QTestProtocol class can be used for exchanging messages between separate + processes. + + It is intended to be fast but at the same time ultra reliable and robust communication protocol. + + The main function that is used on the sending side is: + \code + myConnection.postMessage( "My-Event", "My-Message", ptr_to_my_data ); + \endcode + + On the receiving side the main function is a re-implemented 'processMessage': + \code + void MyTestConnection::processMessage( QTestMessage *msg ) + { + if (msg->event() == "My-Event") { + print( msg->message() ); + } + } + \endcode +*/ + +#include <stdio.h> + +QTestProtocol::QTestProtocol() + : QTcpSocket() + , tx_msg_id(1) + , host() + , port(0) + , onData_busy(false) + , enable_reconnect(false) + , reconnect_interval(10000) + , connect_timer() + , last_data_received(false) + , connection_valid(false) + , ping_enabled(false) + , ping_interval(10000) + , ping_timer() + , ping_count(0) + , ping_timeout_warning_issued(false) + , last_send_cmd("") + , unique_id() + , debug_on(false) +{ + static int id1 = qRegisterMetaType<QTestMessage>(); Q_UNUSED(id1); + static int id2 = qRegisterMetaType<QTestMessage*>(); Q_UNUSED(id2); + static int id3 = qRegisterMetaType<const QTestMessage*>(); Q_UNUSED(id3); + + unique_id = QString("%1").arg(++g_unique_id); + if (debug_on) { + qLog(QtUitest) << QString("%1 QTestProtocol::QTestProtocol()").arg(uniqueId()).toLatin1(); + } + cur_message = 0; + rx_busy = false; + + QObject::connect( &connect_timer, SIGNAL(timeout()), this, SLOT(connectTimeout()), Qt::DirectConnection ); + + QObject::connect( this,SIGNAL(connected()),this,SLOT(onSocketConnected()), Qt::DirectConnection ); + QObject::connect( this,SIGNAL(disconnected()),this,SLOT(onSocketClosed()), Qt::DirectConnection ); + QObject::connect( this,SIGNAL(readyRead()),this,SLOT(onData()), Qt::DirectConnection ); + + // initialize. Any time is better than no time. + rx_timer.start(); +} + +/*! + Destructs the instance of QTestProtocol. +*/ + +QTestProtocol::~QTestProtocol() +{ + if (debug_on) { + qLog(QtUitest) << QString("%1 QTestProtocol::~QTestProtocol()").arg(uniqueId()).toLatin1(); + } + enableReconnect( false, 0 ); + + // we can't send any more messages so disable pinging + enablePing( false ); + + // anything that is still in the tx buffer gets lost + abort(); + close(); + + while (send_msg_replies.count() > 0) + delete send_msg_replies.takeFirst(); + while (unhandled_msg.count() > 0) + delete unhandled_msg.takeFirst(); +} + +void QTestProtocol::setSocket( int socket ) +{ + if (debug_on) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::setSocket(socket=%2)"). + arg(uniqueId()). + arg(socket).toLatin1()); + } + setSocketDescriptor( socket ); + + rx_timer.start(); + testConnection(); +} + +void QTestProtocol::enableReconnect( bool enable, uint reconnectInterval ) +{ + if (debug_on) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::enableReconnect( enable=%2, interval=%3)"). + arg(uniqueId()). + arg(enable). + arg(reconnectInterval).toLatin1()); + } + enable_reconnect = enable; + reconnect_interval = reconnectInterval; +} + +/*! + Opens a socket connection to the specified \a hostname and \a port. + + After a connection is successfully opened the instance will listen for and process + incoming commands received from the remote host. +*/ +void QTestProtocol::connect( const QString& hostname, int port ) +{ + if (debug_on) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::connect(%2:%3)"). + arg(uniqueId()). + arg(hostname). + arg(port).toLatin1()); + } + + if (state() == ConnectedState) { + if (hostname == this->host && port == this->port) + return; + disconnect(); + } + + rx_timer.start(); + + this->host = hostname; + this->port = port; + + reconnect(); +} + +void QTestProtocol::disconnect( bool disableReconnect ) +{ + if (state() == ConnectedState) { + if (debug_on) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::disconnect(disableReconnect=%2)"). + arg(uniqueId()). + arg(disableReconnect).toLatin1()); + } + // be polite and tell the other side we are closing + postMessage( QTestMessage("QTEST_CLOSING_CONNECTION") ); + + // we are closing ourselves, so we don't want to reconnect + if (disableReconnect) enable_reconnect = false; + + onSocketClosed(); + } +} + +bool QTestProtocol::isConnected() +{ + return (state() == ConnectedState && connection_valid); +} + +bool QTestProtocol::waitForConnected( int timeout ) +{ + QtUiTestElapsedTimer t; + if (QTcpSocket::waitForConnected(timeout)) { + if (debug_on) { + qLog(QtUitest) << QString("%1 QTestProtocol::waitForConnected() ... testing connection").arg(uniqueId()).toLatin1(); + } + while (t.elapsed() < timeout && !isConnected()) { + wait(500); + postMessage( QTestMessage("QTEST_NEW_CONNECTION") ); + } + } + bool ok = isConnected(); + if (debug_on) { + qLog(QtUitest) << QString("%1 QTestProtocol::waitForConnected() ... %2").arg(uniqueId()).arg(ok ? "OK" : "FAILED" ).toLatin1(); + } + return ok; +} + +/*! + \internal + Posts (e.g. non blocking) an \a event, \a message and contents of \a fileName to the remote host. +*/ + +uint QTestProtocol::postMessage(QTestMessage const &message ) +{ + if (debug_on && message.event() != "PING" && message.event() != "PONG") { + qLog(QtUitest) << ( QString("%1 QTestProtocol::postMessage(%2)"). + arg(uniqueId()). + arg(message.event()).toLatin1()); + } + if (state() != ConnectedState) + return 0; + QTestMessage msg(message); + msg.m_msg_id = tx_msg_id++; + send( msg ); + return msg.m_msg_id; +} + +void QTestProtocol::onReplyReceived( QTestMessage* /*reply*/ ) +{ +} + +/*! + \internal + Sends an \a event, \a message and \a data to the remote host and waits for up to + \a timeout milliseconds for a reply. If a reply is received, the reply's message + string is placed in \a reply. +*/ +bool QTestProtocol::sendMessage( QTestMessage const &message, QTestMessage &reply, int timeout ) +{ + QTestMessage msg(message); + QPointer<QTestProtocol> safeThis(this); + bool safeDebugOn(debug_on); + QString safeUniqueId(uniqueId()); + + last_send_cmd = message.event(); + + if (state() == ConnectingState) { + wait(4000); + } + + if (state() == ConnectedState) { + msg.m_msg_id = tx_msg_id++; + + if (debug_on) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::sendMessage(%2) msgid=%3)"). + arg(uniqueId()). + arg(msg.event()). + arg(msg.msgId()). + toLatin1()); + } + + send( msg ); + + QtUiTestElapsedTimer t; + QtUiTestElapsedTimer t2; + bool first_time = true; + while ( (state() == ConnectedState) && (timeout < 0 || t.elapsed() < timeout) ) { + + if (debug_on) { + if (first_time || t2.elapsed() > 1000) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::sendMessage(%2) ... waiting for reply"). + arg(uniqueId()). + arg(message.event()).toLatin1()); + t2.start(); + first_time = false; + } + } + + + waitForSignal(this, SIGNAL(replyReceived()), 500); + + if (!safeThis) { + if (message["expectClose"].isValid()) { + return true; + } + if (safeDebugOn) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::sendMessage(%2) ... object deleted unexpectedly"). + arg(safeUniqueId). + arg(message.event()).toLatin1()); + } + reply["status"] = "ERROR: Connection was terminated unexpectedly. This may be caused by an application crash."; + reply["_q_inResponseTo"] = QString("%1\n%2").arg(message.event()).arg(message.toString()); + return false; + } else { + if (send_msg_replies.count() > 0) { + if (debug_on) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::sendMessage(%2) ... check replies"). + arg(uniqueId()). + arg(message.event()).toLatin1()); + } + for (int i=0; i<send_msg_replies.size(); i++) { + QTestMessage * possible_reply = send_msg_replies.at(i); + if (possible_reply && possible_reply->m_msg_id == msg.m_msg_id) { + + reply = *possible_reply; + delete send_msg_replies.takeAt( i ); + + if (debug_on) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::sendMessage(%2) ... reply received"). + arg(uniqueId()). + arg(message.event()).toLatin1()); + } + + onReplyReceived(&reply); + return true; + } + } + } + } + } + if (state() != ConnectedState) { + reply["status"] = "ERROR: Connection lost. This is likely caused by a crash in the Application Under Test."; + reply["_q_inResponseTo"] = QString("%1\n%2").arg(message.event()).arg(message.toString()); + } + else { + reply["status"] = "ERROR_REPLY_TIMEOUT"; + reply["_q_inResponseTo"] = QString("%1\n%2").arg(message.event()).arg(message.toString()); + } + reply["location"] = QString("%1:%2").arg(__FILE__).arg(__LINE__); + } else { + reply["status"] = "ERROR_NO_CONNECTION"; + reply["_q_inResponseTo"] = QString("%1\n%2").arg(message.event()).arg(message.toString()); + reply["location"] = QString("%1:%2").arg(__FILE__).arg(__LINE__); + } + + if (debug_on) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::sendMessage(%2) ... done. Status: %3"). + arg(uniqueId()). + arg(message.event()).arg(reply["status"].toString()).toLatin1()); + } + + return false; +} + +/*! + Send the string \a result as a reply to \a originalMsg. +*/ + +void QTestProtocol::replyMessage( QTestMessage *originalMsg, QTestMessage const &message ) +{ + if (debug_on) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::replyMessage(%2)"). + arg(uniqueId()). + arg(originalMsg->event()).toLatin1()); + } + + QTestMessage msg(message); + msg.m_msg_id = originalMsg->msgId(); + msg.m_event = "@REPLY@"; + send( msg ); +} + +bool QTestProtocol::lastDataReceived() +{ + return last_data_received; +} + +QString QTestProtocol::errorStr() +{ + QString S = "Connection error: "; + switch (error()) { + case ConnectionRefusedError: S += "A connection attempt was rejected by the peer"; break; + case HostNotFoundError: S += "Host not found"; break; + case RemoteHostClosedError: S += "RemoteHostClosedError"; break; + case SocketAccessError: S += "SocketAccessError"; break; + case SocketResourceError: S += "SocketResourceError"; break; + case SocketTimeoutError: S += "SocketTimeoutError"; break; + case DatagramTooLargeError: S += "DatagramTooLargeError"; break; + case NetworkError: S += "NetworkError"; break; + case AddressInUseError: S += "AddressInUseError"; break; + case SocketAddressNotAvailableError: S += "SocketAddressNotAvailableError"; break; + case UnsupportedSocketOperationError: S += "UnsupportedSocketOperationError"; break; + case UnknownSocketError: S += "UnknownSocketError"; break; + default: S += " Unknown error"; + } + + return S; +} + +void QTestProtocol::onConnectionFailed( const QString &reason ) +{ + emit connectionFailed( this, reason ); +} + +void QTestProtocol::testConnection() +{ + if (debug_on) { + qLog(QtUitest) << QString("%1 QTestProtocol::testConnection()").arg(uniqueId()).toLatin1(); + } + + while (send_msg_replies.count() > 0) + delete send_msg_replies.takeFirst(); + + enablePing( true ); + postMessage( QTestMessage("QTEST_NEW_CONNECTION") ); +} + +void QTestProtocol::send( QTestMessage const &message ) +{ + QByteArray data; + if (!message.m_map.isEmpty()) { + QDataStream s(&data, QIODevice::WriteOnly); + s << message.m_map; + } + + QDataStream tmp(this); + sendPreamble( &tmp, message.msgId(), message.event() ); + + quint32 len = data.count(); + tmp << len; // phase 2 + if (len > 0) { + tmp.writeRawData( data.data(), (int)len ); // phase 3 + } + + tmp << TEST_MSG_END; // phase 4 + + flush(); // Force socket to send data now +} + +void QTestProtocol::sendPreamble( QDataStream *ds, quint16 msgId, const QString &event ) +{ + quint32 len; + *ds << TEST_MSG_SIGNATURE; // phase 0 + *ds << TEST_MSG_VERSION; + *ds << msgId; + + len = (event.length() *2) + 4; + *ds << len; + *ds << event; // phase 1 +} + +bool QTestProtocol::receive( QTestMessage *msg, bool &syncError ) +{ + syncError = false; + + QDataStream stream(this); + + quint8 rx_version; + if (msg->m_phase == uint(0)) { + msg->m_len = 0; + quint32 sig; + if (bytesAvailable() >= sizeof( sig ) + sizeof( rx_version ) + sizeof( msg->m_msg_id ) + sizeof( msg->m_len )) { + stream >> sig; + if (sig != TEST_MSG_SIGNATURE) { + qWarning( QString("QTestMessage::receive(), Invalid start signature (0x%1)").arg(sig,8,16).toLatin1() ); + syncError = true; + return false; + } else { + stream >> rx_version; // FIXME: something should be done to verify the version. + stream >> msg->m_msg_id; + stream >> msg->m_len; + msg->m_phase++; + } + } + } + + if (msg->m_phase == uint(1)) { + if (bytesAvailable() >= msg->m_len) { + stream >> msg->m_event; + msg->m_phase++; + } + } + + if (msg->m_phase == uint(2)) { + if (bytesAvailable() >= sizeof( msg->m_len )) { + stream >> msg->m_len; + msg->m_phase++; + } + } + + if (msg->m_phase == uint(3)) { + if (msg->m_len > 0) { + QByteArray buf; + quint32 len = msg->m_len; + uint bytes_available = bytesAvailable(); + if (bytes_available < len) + len = bytes_available; + buf.resize( len ); + stream.readRawData( buf.data(), len ); + + static QMap<QTestMessage*, QByteArray> data; + data[msg].append(buf); + + msg->m_len -= len; + if (msg->m_len == 0) { + QDataStream s(&data[msg], QIODevice::ReadOnly); + s >> msg->m_map; + data.remove(msg); + msg->m_phase++; + // received OK + } else { + // waiting for more data + return false; + } + } else { + msg->m_phase++; + } + } + + if (msg->m_phase == uint(4)) { + quint32 id2; + if (bytesAvailable() >= sizeof( id2 )) { + stream >> id2; + msg->m_phase = 0; + if (id2 != TEST_MSG_END) { + qWarning( QString("QTestMessage::receive(), Invalid end signature (0x%2)").arg(id2,8,16).toLatin1() ); + syncError = true; + return false; + } else { + return true; + } + } + } + + return false; +} + +bool QTestProtocol::rxBusy() +{ + return rx_busy; +} + +/*! + Reads the remote control connection and responds to received commands. +*/ + +void QTestProtocol::onData() +{ + if (onData_busy) return; + onData_busy = true; + + int sync_error_count = 0; + bool msg_received = true; + while (msg_received && bytesAvailable() > 0) { + msg_received = false; + // set the time to now :-) + rx_timer.start(); + ping_timeout_warning_issued = false; + + bool sync_error; + if (cur_message == 0) + cur_message = new QTestMessage(); + + if (receive( cur_message, sync_error )) { + msg_received = true; + QString last_event = cur_message->event(); + + if (debug_on) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::onData(%2) msgid = %3"). + arg(uniqueId()). + arg(last_event). + arg(cur_message->m_msg_id). + toLatin1()); + } + + // We received a full message + if (last_event == "@REPLY@") { + send_msg_replies.append( cur_message ); // add the reply to a list + int id = cur_message->m_msg_id; + QTestMessage *received_message(cur_message); + cur_message = 0; // and make sure we create a new one + emit replyReceived( id, received_message ); + } else if (last_event == "QTEST_NEW_CONNECTION") { + postMessage( QTestMessage("QTEST_ACK_CONNECTION") ); // Acknowledge the other side we can receive data + } else if (last_event == "QTEST_ACK_CONNECTION") { + connection_valid = true; // we don't assume we have a connection until both sides have actually received data from the other side + onConnected(); + connect_timer.stop(); + } else if (last_event == "QTEST_CLOSING_CONNECTION") { + last_data_received = true; + QTimer::singleShot( 0, this, SLOT(onSocketClosed())); + } else if (last_event == "PONG") { + // Do nothing + } else if (last_event == "TEST") { + if (!debug_on) { + // don't show the same information twice + qLog(QtUitest) << QString("%1 Test message received").arg(uniqueId()).toLatin1(); + } + } else if (last_event == "PING") { + postMessage( QTestMessage("PONG") ); + } else { + unhandled_msg.append( cur_message ); // add the msg to a list + cur_message = 0; + QTimer::singleShot(0,this,SLOT(processMessages())); + } + + delete cur_message; + cur_message = 0; + } else { + // We didn't receive a full message + if (sync_error) { + sync_error_count++; + if (sync_error_count > 10) + return; + // receiving garbage messages - nothing can be done but closing the connection and try again. + delete cur_message; + cur_message = 0; + disconnect(!enable_reconnect); + reconnect(); + } + // else we are waiting on more fragments to arrive + } + } + onData_busy = false; +} + +void QTestProtocol::processMessages() +{ + while (!rx_busy && unhandled_msg.count() > 0) { + QTestMessage *tmp = unhandled_msg.takeFirst(); + if (tmp) { + rx_busy = true; + processMessage( tmp ); + delete tmp; + rx_busy = false; + } + } +} + +/*! + Signals the instance that the other side has closed the connection. +*/ +void QTestProtocol::onSocketClosed() +{ + if (debug_on) { + qLog(QtUitest) << QString("%1 QTestProtocol::onSocketClosed()").arg(uniqueId()).toLatin1(); + } + + // we can't send any more messages so disable pinging + enablePing( false ); + + // anything that is still in the tx buffer gets lost + abort(); + + close(); + + connection_valid = false; + + // if the close was spontaneous and we want to keep the connection alive, we try to reconnect + if (enable_reconnect) { + if (debug_on) { + qLog(QtUitest) << + QString("%1 QTestProtocol::onSocketClosed() singleshot reconnect in .5 seconds").arg(uniqueId()).toLatin1(); + } + QTimer::singleShot(500,this,SLOT(reconnect())); + } + + // tell the world we are closed + QTimer::singleShot(0, this, SLOT(emitConnectionClosed())); +} + +/*! + Signals the instance that a connection is established with a remote control host. +*/ +void QTestProtocol::onSocketConnected() +{ + connect_timer.stop(); + if (debug_on) { + qLog(QtUitest) << QString("%1 QTestProtocol::onSocketConnected()").arg(uniqueId()).toLatin1(); + } + testConnection(); +} + +void QTestProtocol::reconnect() +{ + if (host != "" && state() != ConnectedState) { + if (debug_on) { + qLog(QtUitest) << QString("%1 QTestProtocol::reconnect()").arg(uniqueId()).toLatin1(); + } + + connect_timer.stop(); + connect_timer.start( CONNECT_TIMEOUT ); + + // if we are trying to connect to the local machine, always use 127.0.0.1 + // (and avoid the need for dns) + QString hostname = QHostInfo::localHostName().toUpper(); + if (hostname == host.toUpper() || hostname.startsWith( host.toUpper() + "." )) + host = "127.0.0.1"; + + close(); + connectToHost( host, port ); + } else { + if (host == "") { + qWarning( "QTestProtocol::reconnect() FAILED, no host specified" ); + enable_reconnect = false; + } + } +} + +void QTestProtocol::connectTimeout() +{ + if (debug_on) { + qLog(QtUitest) << QString("%1 QTestProtocol::connectTimeout()").arg(uniqueId()).toLatin1(); + } + + connect_timer.stop(); + if (enable_reconnect) { + reconnect(); + } else { + onConnectionFailed( errorStr() ); + } +} + +void QTestProtocol::pingTimeout() +{ + if (!ping_enabled) return; + + if (state() == ConnectedState) { + int elapsed = rx_timer.elapsed(); + if (state() == ClosingState) { + if (elapsed > 30000) { // no activity for x seconds when we are closing? + enablePing( false ); + if (enable_reconnect) { + disconnect(); + // close has reset the enable_reconnect value, so enable it again + enableReconnect( true, reconnect_interval ); + reconnect(); + } else { + disconnect(); + } + } else if (elapsed > 2000 ) { + postMessage( QTestMessage("PING") ); + } + } else { + if (elapsed > 10000) { + postMessage( QTestMessage("PING") ); + if (elapsed > 300000) { // no activity for 5 minutes in 'normal' cases?? + if (!ping_timeout_warning_issued) + qWarning( QString("%1 QTestProtocol::pingTimeout() WARNING: Did not receive a msg for %2 ms").arg(uniqueId()).arg(elapsed).toLatin1() ); + ping_timeout_warning_issued = true; + } + } + } + } else { + if (enable_reconnect) { + // Connection seems to be closed, try to reconnect + disconnect(); + + // disconnect has reset the enable_reconnect value, so enable it again + enableReconnect( true, reconnect_interval ); + reconnect(); + } + } +} + +void QTestProtocol::emitConnectionClosed() +{ + if (debug_on) { + qLog(QtUitest) << ( QString("%1 QTestProtocol::emitConnectionClosed()").arg(uniqueId()).toLatin1()); + } + + emit replyReceived(); // force sendMessage to quit + emit connectionClosed( this ); +} + +void QTestProtocol::enablePing( bool enable ) +{ + if (ping_enabled != enable) { + ping_timer.stop(); + ping_enabled = enable; + if (enable) { + QObject::connect( &ping_timer, SIGNAL(timeout()), this, SLOT(pingTimeout()), Qt::DirectConnection ); + ping_timer.start( ping_interval ); + } else { + QObject::disconnect( &ping_timer, SIGNAL(timeout()), this, SLOT(pingTimeout()) ); + } + } +} + +QString QTestProtocol::uniqueId() +{ + return QString("%1 %2").arg(unique_id).arg(qApp->applicationName()); +} + +void QTestProtocol::enableDebug( bool debugOn ) +{ + debug_on = debugOn; + qLog(QtUitest) << QString("Debugging is switched %1 for Test Protocol %2").arg(debugOn ? "ON" : "OFF").arg(uniqueId()).toLatin1() ; +} + +void QTestProtocol::disableDebug() +{ + debug_on = false; + qLog(QtUitest) << QString("Debugging is switched %1 for Test Protocol %2").arg(debug_on ? "ON" : "OFF").arg(uniqueId()).toLatin1() ; +} + diff --git a/old/libqtuitest/qtestprotocol_p.h b/old/libqtuitest/qtestprotocol_p.h new file mode 100644 index 0000000..ca274f3 --- /dev/null +++ b/old/libqtuitest/qtestprotocol_p.h @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 QTESTPROTOCOL_P_H +#define QTESTPROTOCOL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtUiTest API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QTimer> +#include <QTime> +#include <QObject> +#include <QString> +#include <QFile> +#include <QVariant> +#include <QMap> +#include <QtNetwork/QTcpSocket> +#include <QtNetwork/QTcpServer> +#include <qtuitestglobal.h> + +#include "qtuitestelapsedtimer_p.h" + +#define REMOTE_CONNECT_ERROR 99 + +class QTUITEST_EXPORT QTestMessage +{ +public: + QTestMessage(QString const &event = QString(), QVariantMap const &map = QVariantMap() ); + QTestMessage(QString const &event, const QTestMessage &other ); + QTestMessage(QString const &event, QString const &queryApp, QString const &queryPath = QString() ); + QTestMessage(const QTestMessage &other); + virtual ~QTestMessage(); + + QTestMessage& operator=(const QTestMessage &other); + + QString event() const; + quint16 msgId() const; + + QVariant const operator[](QString const &key) const; + QVariant &operator[](QString const &key); + bool contains(QString const &key) const; + QList<QString> keys() const; + + QString toString() const; + + bool statusOK() const; + bool isNull() const; + +protected: + uint m_phase; + quint32 m_len; + + quint16 m_msg_id; + QString m_event; + + QVariantMap m_map; + + friend class QTestProtocol; +}; +Q_DECLARE_METATYPE( QTestMessage ) +Q_DECLARE_METATYPE( QTestMessage* ) +Q_DECLARE_METATYPE( const QTestMessage* ) + +class QTUITEST_EXPORT QTestServerSocket : public QTcpServer +{ + Q_OBJECT +public: + QTestServerSocket( quint16 port, int backlog = 1 ); + virtual ~QTestServerSocket(); + + bool ok() const; + quint16 port() const; + QString address() const; + + virtual void onNewConnection( int socket ) = 0; + +private: + virtual void incomingConnection( int socket ); +}; + +class QTUITEST_EXPORT QTestProtocol : public QTcpSocket +{ + Q_OBJECT +public: + + QTestProtocol(); + virtual ~QTestProtocol(); + + virtual void setSocket( int socket ); + + void enableReconnect( bool enable, uint reconnectInterval = 5000 ); + + void connect( const QString& hostname, int port ); + void disconnect( bool disableReconnect = true ); + bool isConnected(); + virtual bool waitForConnected( int timeout = 10000 ); + + virtual uint postMessage( QTestMessage const &message ); + + virtual bool sendMessage( QTestMessage const &message, QTestMessage &reply, int timeout ); + virtual void replyMessage( QTestMessage *originalMsg, QTestMessage const &message ); + + bool lastDataReceived(); + bool rxBusy(); + virtual void onReplyReceived( QTestMessage *reply ); + + QString errorStr(); + + virtual void onConnectionFailed( const QString &reason ); + virtual void onConnected() {}; // re-implement in derived class + + QString uniqueId(); + void enableDebug( bool debugOn ); + +public slots: + void reconnect(); + void disableDebug(); + +protected: + virtual void processMessage( QTestMessage *msg ) = 0; + + void send( QTestMessage const &message ); + static void sendPreamble( QDataStream *ds, quint16 msgId, const QString &event ); + + bool receive( QTestMessage *msg, bool &syncError ); + +signals: + void connectionClosed( QTestProtocol *socket ); + void connectionFailed( QTestProtocol *socket, const QString &reason ); + void replyReceived(int id = -1, QTestMessage const *message = 0); + void replyConfirmed(); + +protected slots: + void onData(); + void onSocketConnected(); + void onSocketClosed(); + void connectTimeout(); + void pingTimeout(); + void emitConnectionClosed(); + void processMessages(); + void testConnection(); + +private: + void enablePing( bool enable ); + + quint16 tx_msg_id; + QString host; + int port; + bool onData_busy; + bool enable_reconnect; + uint reconnect_interval; + QTimer connect_timer; + QtUiTestElapsedTimer rx_timer; + bool last_data_received; + bool rx_busy; + bool connection_valid; + + QList<QTestMessage*> send_msg_replies; + QList<QTestMessage*> unhandled_msg; + QTestMessage *cur_message; + + bool ping_enabled; + uint ping_interval; + QTimer ping_timer; + uint ping_count; + bool ping_timeout_warning_issued; + QString last_send_cmd; + QString unique_id; + bool debug_on; +}; + +#define TAB_BAR_ALIAS ">@TAB_BAR@<" +#define OPTIONS_MENU_ALIAS ">@OPTIONS_MENU@<" +#define LAUNCHER_MENU_ALIAS ">@LAUNCHER_MENU@<" // launcher menu _can_ be a grid menu, but also a wheel menu, etc +#define SOFT_MENU_ALIAS ">@SOFT_MENU@<" +#define PROGRESS_BAR_ALIAS ">@PROGRESS_BAR@<" +#define CALL_ACCEPT_ALIAS ">@CALL_ACCEPT@<" +#define CALL_HANGUP_ALIAS ">@CALL_HANGUP@<" +#define MENU_BAR_ALIAS ">@MENU_BAR@<" + +#endif + diff --git a/old/libqtuitest/qtuitestconnectionmanager.cpp b/old/libqtuitest/qtuitestconnectionmanager.cpp new file mode 100644 index 0000000..f7ae638 --- /dev/null +++ b/old/libqtuitest/qtuitestconnectionmanager.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qtuitestconnectionmanager_p.h" + +#include <QCoreApplication> +#include <QObject> +#include <QThread> + +// from qobject_p.h +struct QSignalSpyCallbackSet +{ + typedef void (*BeginCallback)(QObject*,int,void**); + typedef void (*EndCallback)(QObject*,int); + BeginCallback signal_begin_callback, + slot_begin_callback; + EndCallback signal_end_callback, + slot_end_callback; +}; +extern void Q_CORE_EXPORT qt_register_signal_spy_callbacks(QSignalSpyCallbackSet const&); +void qtuitest_signal_begin(QObject*,int,void**); + +Q_GLOBAL_STATIC(QtUiTestConnectionManager,_q_qtUiTestConnectionManager); + +QtUiTestConnectionManager::QtUiTestConnectionManager() +{ + QSignalSpyCallbackSet callbacks = { qtuitest_signal_begin, 0, 0, 0 }; + qt_register_signal_spy_callbacks(callbacks); +} + +QtUiTestConnectionManager::~QtUiTestConnectionManager() +{ + QSignalSpyCallbackSet callbacks = { 0, 0, 0, 0 }; + qt_register_signal_spy_callbacks(callbacks); +} + +void qtuitestconnectionmanager_cleanup() +{ delete _q_qtUiTestConnectionManager(); } + +QtUiTestConnectionManager* QtUiTestConnectionManager::instance() +{ + return _q_qtUiTestConnectionManager(); +} + +void qtuitest_signal_begin(QObject* sender, int signal, void** argv) +{ + QtUiTestConnectionManager* cm = QtUiTestConnectionManager::instance(); + + // Should only ever be null when the application is shutting down, but in that case, + // this callback should already have been unregistered. + Q_ASSERT(cm); + + // During the destructor it isn't safe to check which thread we are. + // We'll just ignore all signals during this time. + if (QCoreApplication::closingDown()) return; + + // Connections are only supported in the main thread. + if (QThread::currentThread() != QCoreApplication::instance()->thread()) return; + + cm->activateConnections(sender,signal,argv); +} + +void QtUiTestConnectionManager::connect(QObject const* sender, int senderMethod, + QObject const* receiver, int receiverMethod) +{ + Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread()); + + Connection c = { + const_cast<QObject*>(sender), senderMethod, + const_cast<QObject*>(receiver), receiverMethod + }; + m_connections << c; +} + +bool QtUiTestConnectionManager::disconnect(QObject const* sender, int senderMethod, + QObject const* receiver, int receiverMethod) +{ + Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread()); + + bool ret = false; + + QList<Connection>::iterator iter = m_connections.begin(); + while (iter != m_connections.end()) + { + bool remove = true; + + if (sender && (sender != iter->sender)) remove = false; + if ((senderMethod != -1) && (senderMethod != iter->senderMethod)) remove = false; + if (receiver && (receiver != iter->receiver)) remove = false; + if ((receiverMethod != -1) && (receiverMethod != iter->receiverMethod)) remove = false; + + if (remove) { + ret = true; + iter = m_connections.erase(iter); + } + else { + ++iter; + } + } + + return ret; +} + +void QtUiTestConnectionManager::activateConnections(QObject* sender, int senderMethod, void** argv) +{ + static const int destroyedMethod = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); + const bool destroyed = (senderMethod == destroyedMethod); + + // Find all of the connections we need to activate. + QList<Connection> toActivate; + QList<Connection>::iterator iter = m_connections.begin(); + while (iter != m_connections.end()) + { + if (sender == iter->sender && senderMethod == iter->senderMethod) + toActivate << *iter; + + // Remove this connection if either the sender or receiver is being destroyed + if (destroyed && (sender == iter->sender || sender == iter->receiver)) + iter = m_connections.erase(iter); + else + ++iter; + } + + foreach (Connection const& c, toActivate) + { c.receiver->qt_metacall(QMetaObject::InvokeMetaMethod, c.receiverMethod, argv); } +} + diff --git a/old/libqtuitest/qtuitestconnectionmanager_p.h b/old/libqtuitest/qtuitestconnectionmanager_p.h new file mode 100644 index 0000000..13a1017 --- /dev/null +++ b/old/libqtuitest/qtuitestconnectionmanager_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 QTUITESTCONNECTIONMANAGER_P_H +#define QTUITESTCONNECTIONMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtUiTest API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QList> +#include <qtuitestglobal.h> + +class QObject; + +class QTUITEST_EXPORT QtUiTestConnectionManager +{ +public: + static QtUiTestConnectionManager* instance(); + + void connect (QObject const*,int,QObject const*,int); + bool disconnect(QObject const*,int,QObject const*,int); + + QtUiTestConnectionManager(); + ~QtUiTestConnectionManager(); + + void activateConnections(QObject*,int,void**); + + struct Connection { + QObject* sender; + int senderMethod; + QObject* receiver; + int receiverMethod; + }; + QList<Connection> m_connections; +}; + +#endif + diff --git a/old/libqtuitest/qtuitestelapsedtimer.cpp b/old/libqtuitest/qtuitestelapsedtimer.cpp new file mode 100644 index 0000000..5e9a593 --- /dev/null +++ b/old/libqtuitest/qtuitestelapsedtimer.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qtuitestelapsedtimer_p.h" + +#include <time.h> +#include <errno.h> +#include <string.h> +#include <QtGlobal> + +/*! + \internal + \class QtUiTestElapsedTimer + \inpublicgroup QtUiTestModule + + QtUiTestElapsedTimer provides a lightweight + subset of the QTime API while keeping correct time even if the system time + is changed. +*/ + +int qtuitestelapsedtimer_gettime() { +#if _POSIX_TIMERS + struct timespec tms; + if (-1 == clock_gettime( +#ifdef _POSIX_MONOTONIC_CLOCK + CLOCK_MONOTONIC +#else + CLOCK_REALTIME +#endif + , &tms)) { + qFatal("QtUitest: clock_gettime failed: %s", + strerror(errno)); + } + return tms.tv_sec*1000 + tms.tv_sec/1000000; +#else + /* + FIXME: won't keep correct time if system time is changed during test. + Should warn about this? + */ + return time(0)*1000; +#endif +} + +/*! + Creates a new timer. + start() is automatically called on the new timer. +*/ +QtUiTestElapsedTimer::QtUiTestElapsedTimer() +{ start(); } + +/*! + Starts or restarts the timer. +*/ +void QtUiTestElapsedTimer::start() +{ start_ms = qtuitestelapsedtimer_gettime(); } + +/*! + Returns the elapsed time (in milliseconds) since start() was called. + + This function returns the correct value even if the system time has + been changed. The value may overflow, however. + */ +int QtUiTestElapsedTimer::elapsed() +{ return qtuitestelapsedtimer_gettime() - start_ms; } + diff --git a/old/libqtuitest/qtuitestelapsedtimer_p.h b/old/libqtuitest/qtuitestelapsedtimer_p.h new file mode 100644 index 0000000..1536d88 --- /dev/null +++ b/old/libqtuitest/qtuitestelapsedtimer_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 QTUITESTELAPSEDTIMER_P_H +#define QTUITESTELAPSEDTIMER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtUiTest API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qtuitestglobal.h> + +class QTUITEST_EXPORT QtUiTestElapsedTimer +{ +public: + QtUiTestElapsedTimer(); + + void start(); + int elapsed(); + +private: + int start_ms; +}; + +#endif + diff --git a/old/libqtuitest/qtuitestglobal.h b/old/libqtuitest/qtuitestglobal.h new file mode 100644 index 0000000..cb46581 --- /dev/null +++ b/old/libqtuitest/qtuitestglobal.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 QTUITEST_GLOBAL_H +#define QTUITEST_GLOBAL_H + +#include <QtGlobal> + +#if defined(Q_OS_WIN32) +# if defined(QTUITEST_TARGET) +# define QTUITEST_EXPORT Q_DECL_EXPORT +# else +# define QTUITEST_EXPORT Q_DECL_IMPORT +# endif +# if defined(QTSLAVE_TARGET) +# define QTSLAVE_EXPORT Q_DECL_EXPORT +# else +# define QTSLAVE_EXPORT Q_DECL_IMPORT +# endif +#else +# define QTUITEST_EXPORT Q_DECL_EXPORT +# define QTSLAVE_EXPORT Q_DECL_EXPORT +#endif + +#if defined(Q_OS_WIN32) || defined(Q_OS_SYMBIAN) +# if defined(QSYSTEMTEST_TARGET) +# define QSYSTEMTEST_EXPORT Q_DECL_EXPORT +# else +# define QSYSTEMTEST_EXPORT Q_DECL_IMPORT +# endif +# if defined(QTUITESTRUNNER_TARGET) +# define QTUITESTRUNNER_EXPORT Q_DECL_EXPORT +# else +# define QTUITESTRUNNER_EXPORT Q_DECL_IMPORT +# endif +#else +# define QSYSTEMTEST_EXPORT Q_DECL_EXPORT +# define QTUITESTRUNNER_EXPORT Q_DECL_EXPORT +#endif + +#endif + diff --git a/old/libqtuitest/qtuitestnamespace.cpp b/old/libqtuitest/qtuitestnamespace.cpp new file mode 100644 index 0000000..09d838f --- /dev/null +++ b/old/libqtuitest/qtuitestnamespace.cpp @@ -0,0 +1,1085 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qtuitestnamespace.h" + +#include "qalternatestack_p.h" +#include "qtuitestelapsedtimer_p.h" +#include "qeventwatcher_p.h" +#include "qtuitestconnectionmanager_p.h" +#include "qtuitestwidgets_p.h" + +#include <QAction> +#include <QDebug> +#include <QEventLoop> +#include <QKeyEvent> +#include <QKeySequence> +#include <QPointer> +#include <QTimer> + +/* + A simple auto pointer class which deletes the pointed-to QObject + later via deleteLater() +*/ +template <typename T> +class QDelayedAutoPointer +{ +public: + inline QDelayedAutoPointer(T* thing) + : raw(thing) + {} + inline ~QDelayedAutoPointer() + { + QObject::disconnect(raw, 0, 0, 0); + raw->deleteLater(); + } + + inline T* operator->() + { return raw; } + + inline operator T*() + { return raw; } + +private: + T* raw; +}; + +/* + A helper class to encapsulate generation of input events +*/ +struct QtUiTestInput +{ + virtual ~QtUiTestInput() {} + virtual void post() const =0; + virtual QString toString() const =0; +}; + + +namespace QtUiTest { + QString objectName(QObject* obj) + { + QString ret = obj->objectName(); + QAction* act(0); + if (ret.isEmpty() && (act = qobject_cast<QAction*>(obj))) { + ret = act->text(); + if (ret.isEmpty()) ret = act->iconText(); + } + return ret; + } + QString toString(QObject* obj) + { + if (!obj) return "QObject(0x0)"; + return QString("%1(0x%2 \"%3\")") + .arg(obj->metaObject()->className()) + .arg(QString::number(qptrdiff(obj), 16)) + .arg(objectName(obj)) + ; + } + + QString toString(QEvent::Type type) + { +#define DO(A) if (type == QEvent::A) return #A + DO(KeyPress); + DO(KeyRelease); + DO(MouseButtonPress); + DO(MouseButtonRelease); + DO(Show); + DO(Hide); + DO(FocusIn); + DO(FocusOut); +#ifdef QT_KEYPAD_NAVIGATION + DO(EnterEditFocus); + DO(LeaveEditFocus); +#endif + DO(WindowBlocked); +#undef DO + return QString::number(int(type)); + } + + QString toString(QList<QEvent::Type> const& types) + { + QString ret; + QString sep; + foreach (QEvent::Type type, types) { + ret += sep + toString(type); + sep = ","; + } + return ret; + } + + QString toString(Qt::Key key) + { return QKeySequence(key).toString(); } + + QString toString(QPoint const& pos) + { return QString("(%1,%2)").arg(pos.x()).arg(pos.y()); } + + QString toString(Qt::MouseButtons const& buttons) + { + QStringList ret; +#define DO(A) if (buttons & Qt::A) ret << #A + DO(LeftButton); + DO(RightButton); + DO(MidButton); + DO(XButton1); + DO(XButton2); +#undef DO + return ret.join(","); + } + + bool keyClick(QObject*, QList<QEvent::Type> const&, int, Qt::KeyboardModifiers, + QtUiTest::InputOption); + bool mouseClick(QObject*, QList<QEvent::Type> const&, QPoint const&, Qt::MouseButtons, + QtUiTest::InputOption); + bool inputWithEvent(QObject*, QList<QEventWatcherFilter*> const&, QtUiTestInput const&); + bool inputWithSignal(QObject*, QByteArray const&, QtUiTestInput const&); +}; + +struct QtUiTestKeyClick : public QtUiTestInput +{ + QtUiTestKeyClick(Qt::Key key, Qt::KeyboardModifiers modifiers, QtUiTest::InputOption options) + : m_key(key), m_modifiers(modifiers), m_options(options) + {} + + virtual void post() const + { QtUiTest::keyClick(m_key, m_modifiers, m_options); } + + virtual QString toString() const + { return QString("Key click \"%1\"").arg(QtUiTest::toString(m_key)); } + + Qt::Key m_key; + Qt::KeyboardModifiers m_modifiers; + QtUiTest::InputOption m_options; +}; + +struct QtUiTestMouseClick : public QtUiTestInput +{ + QtUiTestMouseClick(QPoint const& pos, Qt::MouseButtons buttons, QtUiTest::InputOption options) + : m_pos(pos), m_buttons(buttons), m_options(options) + {} + + virtual void post() const + { QtUiTest::mouseClick(m_pos, m_buttons, m_options); } + + virtual QString toString() const + { + return QString("Mouse click \"%1\" at %2") + .arg(QtUiTest::toString(m_buttons)) + .arg(QtUiTest::toString(m_pos)) + ; + } + + QPoint const& m_pos; + Qt::MouseButtons m_buttons; + QtUiTest::InputOption m_options; +}; + +/* + Filter which implements watching for events of a specific type. +*/ +class QEventWatcherTypeFilter : public QEventWatcherFilter +{ +public: + QEventWatcherTypeFilter(QEvent::Type type) + : m_type(type) + {} + +protected: + virtual bool accept(QObject*,QEvent* e) const + { return e->type() == m_type; } + + virtual QString toString() const + { return QString("event of type %1").arg(QtUiTest::toString(m_type)); } + +private: + QEvent::Type m_type; +}; + +/* + Filter which implements watching for specific key events. +*/ + +class QEventWatcherKeyFilter : public QEventWatcherTypeFilter +{ +public: + QEventWatcherKeyFilter(Qt::Key key, QEvent::Type type) + : QEventWatcherTypeFilter(type) + , m_key(key) + {} + + static QEvent::Type keyPressType() + { static int ret = QEvent::registerEventType(); return QEvent::Type(ret); } + + static QEvent::Type keyReleaseType() + { static int ret = QEvent::registerEventType(); return QEvent::Type(ret); } + +protected: + virtual bool accept(QObject* o, QEvent* e) const + { + if (!QEventWatcherTypeFilter::accept(o,e)) + return false; + if (e->type() != QEvent::KeyPress && e->type() != QEvent::KeyRelease) + return false; + return static_cast<QKeyEvent*>(e)->key() == m_key; + } + + virtual QString toString() const + { + return QString("%1 (key:%2)") + .arg(QEventWatcherTypeFilter::toString()) + .arg(QtUiTest::toString(m_key)); + } + +private: + Qt::Key m_key; +}; + + +/*! + \preliminary + \namespace QtUiTest + \inpublicgroup QtUiTestModule + + \brief The QtUiTest namespace provides the plugin interfaces used for + customizing the behaviour of QtUiTest. + + When running a \l{QSystemTest}{QtUiTest system test}, functions such as + \l{QSystemTest::}{select()} and \l{QSystemTest::}{getText()} are used to + perform actions and retrieve information from widgets. + This is implemented by wrapping each conceptual widget (which is not + necessarily a QWidget) with a test widget class. + + These test widgets each implement one or more of the widget interfaces + in the QtUiTest namespace. The interfaces are used to determine + what actions can be taken on a particular widget, and how to perform + them. + + For example, when the following system test code executes: + \code + select("Dog", "Favorite Animal"); + \endcode + + QtUiTest will first look up the QWidget which is labelled by the + text "Favorite Animal". It will then use qtuitest_cast to cast + this widget to a QtUiTest::SelectWidget. If this is successful, it + will then call \l{QtUiTest::SelectWidget::select()}{select("Dog")} + on the list widget. + + It is possible to customize the behavior of QtUiTest for particular + widgets by creating custom test widget classes and a + QtUiTest::WidgetFactory factory class to wrap QObject instances in + test widgets. +*/ + +/*! + \fn T QtUiTest::qtuitest_cast_helper(QObject* object,T dummy) + \internal +*/ + +/*! + \relates QtUiTest + \fn T qtuitest_cast(const QObject *object) + + Casts \a object to the specified QtUiTest test widget interface \c{T}. + + If \a object already implements \c{T}, it is simply casted and returned. + Otherwise, QtUiTest will attempt to find or create a test widget to + wrap \a object, using all loaded QtUiTest::WidgetFactory plugins. + If a test widget cannot be created to wrap \a object, 0 is returned. + + In either case, the returned value must not be deleted by the caller. + + Example: + \code + // Attempt to select the item "Foo" from the given widget + bool selectFoo(QWidget *widget) { + QtUiTest::SelectWidget* sw + = qtuitest_cast<QtUiTest::SelectWidget*>(widget); + if (!sw || !sw->canSelect("Foo")) { + return false; + } + return sw->select("Foo"); + } + \endcode +*/ + + +/*! + \enum QtUiTest::InputOption + + This enum type specifies the options to be used when simulating key + and mouse events. + + \value NoOptions no options. + \value DemoMode when simulating, force artificial delays between key + and mouse events, and animate some events. + \value KeyRepeat when simulating key press events, simulate auto-repeat + key press events. The default is to simulate regular key + press events. +*/ + +/*! + \enum QtUiTest::WidgetType + + This enum type specifies different types of widgets which can be retrieved + via QtUiTest::findWidget(). + + \value Focus The widget which currently has keyboard focus. Note that this + need not be located in the current application. + \value InputMethod A currently active input method widget. + \value SoftMenu A currently displayed \l{QSoftMenuBar}{soft menu bar}. + \value OptionsMenu The context/options menu which is currently shown, or + would be shown if the user attempted to raise a context + menu (typically by pressing Qt::Key_Context1). + \value TabBar The \l{QTabBar}{tab bar} for the currently active + \l{QTabWidget}{tab widget}, if one exists. + QtUiTest is not designed to handle multiple nested + tab widgets. + \value HomeScreen The home screen widget. + \value Launcher The widget (typically a grid-like menu) in the server + process used for launching applications. +*/ + +/*! + \enum QtUiTest::Key + + This enum provides mappings for high-level conceptual keys to platform-specific + values of Qt::Key. + + \value Key_Activate Key used to activate generic UI elements. + \value Key_ActivateButton Key used to activate buttons. + \value Key_Select Key used to select an item from lists. +*/ + +/*! + 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 QtUiTest::setInputOption(QtUiTest::InputOption option, bool on) +{ QtUiTestWidgets::instance()->setInputOption(option, on); } + +/*! + Returns true if \a option is currently set. +*/ +bool QtUiTest::testInputOption(QtUiTest::InputOption option) +{ return QtUiTestWidgets::instance()->testInputOption(option); } + +/*! + Simulate a mouse press event at the co-ordinates given by \a pos, + for the specified \a buttons. \a options are applied to the simulated + event. + + \a pos is interpreted as local co-ordinates for the currently active + window in this application. +*/ +void QtUiTest::mousePress(QPoint const& pos, Qt::MouseButtons buttons, + QtUiTest::InputOption options) +{ QtUiTestWidgets::instance()->mousePress(pos, buttons, options); } + + +/*! + Simulate a mouse release event at the global co-ordinates given by \a pos, + for the specified \a buttons. \a options are applied to the simulated + event. + + \a pos is interpreted as local co-ordinates for the currently active + window in this application. +*/ +void QtUiTest::mouseRelease(QPoint const& pos, Qt::MouseButtons buttons, + QtUiTest::InputOption options) +{ QtUiTestWidgets::instance()->mouseRelease(pos, buttons, options); } + +/*! + Simulate a mouse click event at the global co-ordinates given by \a pos, + for the specified \a buttons. \a options are applied to the simulated + event. + + \a pos is interpreted as local co-ordinates for the currently active + window in this application. +*/ +void QtUiTest::mouseClick(QPoint const& pos, Qt::MouseButtons buttons, + QtUiTest::InputOption options) +{ QtUiTestWidgets::instance()->mouseClick(pos, buttons, options); } + +/*! + \overload + Simulate a mouse click event. + Returns true if the event appears to be delivered to \a object within maximumUiTimeout() + milliseconds. + If it does not, the errorString() will be set accordingly. +*/ +bool QtUiTest::mouseClick(QObject* object, QPoint const& pos, Qt::MouseButtons buttons, + QtUiTest::InputOption options) +{ + return mouseClick(object, + QList<QEvent::Type>() << QEvent::MouseButtonRelease << QEvent::Hide << QEvent::WindowBlocked, + pos, buttons, options + ); +} + +/*! + \internal +*/ +bool QtUiTest::mouseClick(QObject* object, QList<QEvent::Type> const& types, QPoint const& pos, + Qt::MouseButtons buttons, QtUiTest::InputOption options) +{ + QList<QEventWatcherFilter*> filters; + foreach (QEvent::Type type, types) { + filters << new QEventWatcherTypeFilter(type); + } + return inputWithEvent(object, filters, QtUiTestMouseClick(pos, buttons, options)); +} + +/*! + \overload + Simulate a mouse click event. + Returns true if the event causes \a object to emit \a signal within maximumUiTimeout() + milliseconds. + If it does not, the errorString() will be set accordingly. +*/ +bool QtUiTest::mouseClick(QObject* object, QByteArray const& signal, QPoint const& pos, + Qt::MouseButtons buttons, QtUiTest::InputOption options) +{ return inputWithSignal(object, signal, QtUiTestMouseClick(pos, buttons, options)); } + +/*! + Simulate a key press event, using the given \a key and \a modifiers. + \a key must be a valid Qt::Key or QtUiTest::Key. + \a options are applied to the simulated event. +*/ +void QtUiTest::keyPress(int key, Qt::KeyboardModifiers modifiers, + QtUiTest::InputOption options) +{ QtUiTestWidgets::instance()->keyPress(static_cast<Qt::Key>(key), modifiers, options); } + +/*! + Simulate a key release event, using the given \a key and \a modifiers. + \a key must be a valid Qt::Key or QtUiTest::Key. + \a options are applied to the simulated event. +*/ +void QtUiTest::keyRelease(int key, Qt::KeyboardModifiers modifiers, + QtUiTest::InputOption options) +{ QtUiTestWidgets::instance()->keyRelease(static_cast<Qt::Key>(key), modifiers, options); } + +/*! + Simulate a key click event, using the given \a key and \a modifiers. + \a key must be a valid Qt::Key or QtUiTest::Key. + \a options are applied to the simulated event. +*/ +void QtUiTest::keyClick(int key, Qt::KeyboardModifiers modifiers, + QtUiTest::InputOption options) +{ QtUiTestWidgets::instance()->keyClick(static_cast<Qt::Key>(key), modifiers, options); } + +/*! + \overload + Simulate a key click event. + Returns true if the event appears to be delivered to \a object within maximumUiTimeout() + milliseconds. + If it does not, the errorString() will be set accordingly. +*/ +bool QtUiTest::keyClick(QObject* object, int key, Qt::KeyboardModifiers modifiers, + QtUiTest::InputOption options) +{ + return keyClick(object, + QList<QEvent::Type>() << QEventWatcherKeyFilter::keyReleaseType() + << QEvent::Hide << QEvent::WindowBlocked << QEvent::Show, + key, modifiers, options + ); +} + +/*! + \internal +*/ +bool QtUiTest::inputWithEvent(QObject* object, QList<QEventWatcherFilter*> const& filters, + QtUiTestInput const& event) +{ + QPointer<QObject> sender = object; + QDelayedAutoPointer<QEventWatcher> w = new QEventWatcher; + w->addObject(sender); + foreach (QEventWatcherFilter* filter, filters) + w->addFilter(filter); + + event.post(); + + if (!w->count() && !QtUiTest::waitForSignal(w, SIGNAL(event(QObject*,int)))) { + setErrorString(QString( + "%1 was expected to result in %2 receiving an event matching one of the following, " + "but it didn't:\n%3") + .arg(event.toString()) + .arg(toString(sender)) + .arg(w->toString())); + return false; + } + return true; +} + +/*! + \internal +*/ +bool QtUiTest::inputWithSignal(QObject* object, QByteArray const& signal, + QtUiTestInput const& event) +{ + if (signal.isEmpty()) return false; + QPointer<QObject> sender = object; + + QTimer dummy; + dummy.setInterval(1000); + if (!QtUiTest::connectFirst(sender, signal, &dummy, SLOT(start()))) { + setErrorString(QString("Object %1 has no signal %2").arg(toString(sender)).arg(&signal.constData()[1])); + return false; + } + + // Ensure connectNotify is called + if (!QObject::connect(sender, signal, &dummy, SLOT(start()))) + Q_ASSERT(0); + + event.post(); + + if (!dummy.isActive() && !QtUiTest::waitForSignal(sender, signal)) { + setErrorString(QString( + "%1 was expected to result in %2 emitting the signal %3, " + "but it didn't.") + .arg(event.toString()) + .arg(toString(sender)) + .arg(&signal.constData()[1])); + return false; + } + return true; +} + +/*! + \internal + \overload + Simulate a key click event. + Returns true if \a object receives any event of the given \a types within maximumUiTimeout() + milliseconds. + If it does not, the errorString() will be set accordingly. +*/ +bool QtUiTest::keyClick(QObject* object, QList<QEvent::Type> const& types, int key, + Qt::KeyboardModifiers modifiers, QtUiTest::InputOption options) +{ + QList<QEventWatcherFilter*> filters; + foreach (QEvent::Type type, types) { + // These cases result in waiting for specific key events rather than just "any key press". + if (type == QEventWatcherKeyFilter::keyPressType()) { + filters << new QEventWatcherKeyFilter(Qt::Key(key), QEvent::KeyPress); + } else if (type == QEventWatcherKeyFilter::keyReleaseType()) { + filters << new QEventWatcherKeyFilter(Qt::Key(key), QEvent::KeyRelease); + } else { + filters << new QEventWatcherTypeFilter(type); + } + } + return inputWithEvent(object, filters, QtUiTestKeyClick(Qt::Key(key), modifiers, options)); +} + +/*! + \overload + Simulate a key click event. + Returns true if \a object emits \a signal within maximumUiTimeout() + milliseconds. + If it does not, the errorString() will be set accordingly. +*/ +bool QtUiTest::keyClick(QObject* object, QByteArray const& signal, int key, Qt::KeyboardModifiers modifiers, + QtUiTest::InputOption options) +{ return inputWithSignal(object, signal, QtUiTestKeyClick(Qt::Key(key), modifiers, options)); } + +/*! + Returns true if widget interaction should prefer mouse events over + key events. + + On most platforms the default returns true, unless the environment variable + QTUITEST_NO_MOUSE is set. +*/ +bool QtUiTest::mousePreferred() +{ + return QtUiTestWidgets::instance()->mousePreferred(); +} + +void QtUiTest::setMousePreferred(bool useMouse) +{ + QtUiTestWidgets::instance()->setMousePreferred(useMouse); +} + +QtUiTest::LabelOrientation QtUiTest::labelOrientation() +{ + return QtUiTestWidgets::instance()->labelOrientation(); +} + +void QtUiTest::setLabelOrientation(QtUiTest::LabelOrientation orientation) +{ + QtUiTestWidgets::instance()->setLabelOrientation(orientation); +} + +/*! + Returns the maximum amount of time, in milliseconds, the user interface is allowed to take + to generate some response to a user's action. + + This value is useful to determine how long test widgets should wait for certain events to occur + after simulating key/mouse events. The value may be device-specific. +*/ +int QtUiTest::maximumUiTimeout() +{ return 2000; } + +/*! + Returns the Qt::Key corresponding to \a c. + + This function is commonly used in conjunction with keyClick() to enter + a string of characters using the keypad. + + Example: + \code + using namespace QtUiTest; + QString text = "hello world"; + // ... + foreach (QChar c, text) { + keyClick( asciiToKey(c.toLatin1()), asciiToModifiers(c.toLatin1()) ); + } + \endcode +*/ +Qt::Key QtUiTest::asciiToKey(char c) +{ return static_cast<Qt::Key>(QKeySequence(QString(QChar(c)))[0]); } + +/*! + Returns any Qt::KeyboardModifiers which would be required to input \a c. + + This function is commonly used in conjunction with keyClick() to enter + a string of characters using the keypad. + + Example: + \code + using namespace QtUiTest; + QString text = "hello world"; + // ... + foreach (QChar c, text) { + keyClick( asciiToKey(c.toLatin1()), asciiToModifiers(c.toLatin1()) ); + } + \endcode +*/ +Qt::KeyboardModifiers QtUiTest::asciiToModifiers(char c) +{ + Qt::KeyboardModifiers ret = Qt::NoModifier; + if (QChar(c).isUpper()) ret |= Qt::ShiftModifier; + return ret; +} + +/*! + 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 QtUiTest::errorString() +{ return QtUiTestWidgets::instance()->errorString(); } + +/*! + Sets the human-readable \a error string describing the last error which + occurred while accessing a testwidget. + + \sa errorString() +*/ +void QtUiTest::setErrorString(QString const& error) +{ QtUiTestWidgets::instance()->setErrorString(error); } + +/*! + Returns a widget or test widget wrapper of \a type. + + QtUiTest will attempt to find widgets by calling + QtUiTest::WidgetFactory::find() on all loaded widget factories. + + Note that it is possible to call this function from an application where + the only widget of \a type exists in the server. In this case, the + reference implementation returns a special wrapper which transparently + communicates with the server when calling functions. However, if custom + factories are used which do not do this, this function is likely to return + 0 in this case. + + Example: + \code + using namespace QtUiTest; + bool MyTextEdit::enter(QVariant const& item) { + // Instead of explicitly simulating mouse or key clicks, + // enter the text via the current input method. + // Note that this works whether or not the input method is located in + // the current process. + InputWidget *iw = qtuitest_cast<InputWidget*>(findWidget(InputMethod)); + return (iw && iw->enter(item)); + } + \endcode +*/ +QObject* QtUiTest::findWidget(WidgetType type) +{ + return QtUiTestWidgets::instance()->findWidget(type); +} + +/*! + \internal + Returns a test widget wrapper for \a object which implements + \a interface. +*/ +QObject* QtUiTest::testWidget(QObject* object, const char* interface) +{ + return QtUiTestWidgets::instance()->testWidget(object, interface); +} + +/*! + Causes the process to wait for \a ms milliseconds. While waiting, events will be processed. +*/ +void QtUiTest::wait(int ms) +{ + // If we are currently in an alternate stack, make sure we wait on the main + // stack instead. This avoids hanging due to nested event loops (bug 194361). + foreach (QAlternateStack* stack, QAlternateStack::instances()) { + if (!stack->isCurrentStack()) continue; + + // OK, we are running in this stack. + + // We are about to switch from this stack back to the main stack. + // Arrange an event to occur so that we switch back to this stack + // after the given timeout. + + // Must be created on the heap and destroyed with deleteLater(), + // because when we switch back to this stack, QCoreApplication::notify + // still holds a pointer to timer. + + QDelayedAutoPointer<QTimer> timer = new QTimer; + timer->setObjectName("qtuitest_wait_timer"); + timer->setSingleShot(true); + timer->setInterval(ms); + + QObject::connect(timer, SIGNAL(timeout()), stack, SLOT(switchTo())); + + // Now switch back to the main stack. + timer->start(); + while (timer->isActive()) + stack->switchFrom(); + + // OK, we've returned from the main stack to this stack, so we've + // waited for the given timeout and can now return. + return; + } + + // If we get here, we are running in the main stack, so just do a usual + // possibly-hanging wait. + QEventLoop loop; + QTimer::singleShot(ms, &loop, SLOT(quit())); + loop.exec(); +} + +#include <QDebug> + +/*! + Causes the process to wait for \a ms milliseconds or until \a signal is + emitted from \a object, whichever comes first. + + While waiting, events will be processed. + + Returns true if \a signal was emitted from \a object before timing out. + When false is returned, errorString() is set accordingly. + + If \a connectionType specifies a direct connection, this function will return + immediately when the signal occurs, possibly before some objects have + received the signal. If \a connectionType specifies a queued connection, this + function will return once the event loop has run following the emit. +*/ +bool QtUiTest::waitForSignal(QObject* object, const char* signal, int ms, Qt::ConnectionType connectionType) +{ + if (ms < 0) return false; + if (!signal || !signal[0]) return false; + + QPointer<QObject> sender = object; + + // Dummy variable to detect signal emission. + QTimer dummy; + dummy.setInterval(1000); + if (!QtUiTest::connectFirst(sender, signal, &dummy, SLOT(start()))) + return false; + + // Ensure connectNotify is called + if (!QObject::connect(sender, signal, &dummy, SLOT(start()))) + Q_ASSERT(0); + + // If we are currently in an alternate stack, make sure we wait on the main + // stack instead. This avoids hanging due to nested event loops (bug 194361). + foreach (QAlternateStack* stack, QAlternateStack::instances()) { + if (!stack->isCurrentStack()) continue; + + // OK, we are running in this stack. + + // We are about to switch from this stack back to the main stack. + // Arrange an event to occur so that we switch back to this stack + // after the given timeout. + + // Must be created on the heap and destroyed with deleteLater(), + // because when we switch back to this stack, QCoreApplication::notify + // still holds a pointer to timer. + + QDelayedAutoPointer<QTimer> timer = new QTimer; + timer->setObjectName("qtuitest_waitForSignal_timer"); + timer->setInterval(ms); + timer->setSingleShot(true); + QObject::connect(timer, SIGNAL(timeout()), stack, SLOT(switchTo())); + timer->start(); + + while (timer->isActive() && !dummy.isActive()) { + // Arrange it so that we switch back to this stack if the + // desired signal is emitted. + if (!QtUiTest::connectFirst(sender, signal, stack, SLOT(switchTo()))) { + return false; + } + // Now switch back to the main stack. + stack->switchFrom(); + if (sender && !QtUiTest::disconnectFirst(sender, signal, stack, SLOT(switchTo()))) + Q_ASSERT(0); + } + + // OK, we've returned from the main stack to this stack, so we've + // timed out or got the signal. Check dummy to figure out which one. + if (!dummy.isActive()) { + setErrorString(QString("Object %1 was expected to emit %2 within %3 milliseconds, " + "but it didn't.") + .arg(toString(sender)) + .arg(&signal[1]) + .arg(ms) + ); + return false; + } + + // If we asked for a queued connection, fake it. + if (connectionType == Qt::QueuedConnection) { + QtUiTest::wait(0); + } + return true; + } + + // We are running on the main stack. Use a (dangerous) nested event loop + // to wait. + QEventLoop loop; + if (!QObject::connect(sender, signal, &loop, SLOT(quit()))) + return false; + QTimer::singleShot(ms, &loop, SLOT(quit())); + + loop.exec(); + + return dummy.isActive(); +} + +/*! + Causes the process to wait for \a ms milliseconds or until an event of + any of the given \a types is received by \a object, whichever comes first. + + While waiting, events will be processed. + + Returns true if the event was received by \a object before timing out. + When false is returned, errorString() is set accordingly. + + If \a connectionType specifies a direct connection, this function will return + immediately before the event is processed by \a object. + If \a connectionType specifies a queued connection, this function will return + once the event loop has run following the processing of the event. +*/ +bool QtUiTest::waitForEvent(QObject* object, QList<QEvent::Type> const& types, int ms, Qt::ConnectionType connectionType) +{ + QPointer<QObject> sender = object; + QDelayedAutoPointer<QEventWatcher> w = new QEventWatcher; + w->setObjectName("qtuitest_waitForEvent_watcher"); + w->addObject(sender); + foreach (QEvent::Type type, types) + w->addFilter(new QEventWatcherTypeFilter(type)); + + if (!QtUiTest::waitForSignal(w, SIGNAL(event(QObject*,int)), ms, connectionType)) { + setErrorString(QString("Object %1 was expected to receive an event of type(s) %2 within " + "%3 milliseconds, but it didn't.") + .arg(toString(sender)) + .arg(toString(types)) + .arg(ms) + ); + return false; + } + return true; +} + +/*! + \overload + Waits for an event of the given \a type. +*/ +bool QtUiTest::waitForEvent(QObject* object, QEvent::Type type, int ms, Qt::ConnectionType connectionType) +{ return waitForEvent(object, QList<QEvent::Type>() << type, ms, connectionType); } + +/*! + Creates a connection from the \a signal in the \a sender object to + the \a method in the \a receiver object. Returns true if the connection succeeds; + otherwise returns false. + + This function behaves similarly to QObject::connect() with the following + important differences. + \list + \o The connection is guaranteed to be activated before + any connections made with QObject::connect(). + \o The connection type is always Qt::DirectConnection. + \o The connection cannot be disconnected using QObject::disconnect() + (QtUiTest::disconnectFirst() must be used instead). + \o The connection does not affect the return value of QObject::receivers(). + \o While \a method is being executed, the return value of + QObject::sender() is undefined. + \o QObject::connectNotify() is not called on the sending object. + \endlist + + This function is primarily used in conjunction with QtUiTestRecorder to + ensure events are recorded in the correct order. + + Note that this function cannot be used in a program which uses QSignalSpy. + + \sa QObject::connect(), QtUiTest::disconnectFirst() +*/ +bool QtUiTest::connectFirst(const QObject* sender, const char* signal, + const QObject* receiver, const char* method) +{ + // On failure, we use QObject::connect to get the same error message as + // we normally would. + if (sender == 0 || receiver == 0 || signal == 0 || method == 0) { + return QObject::connect(sender,signal,receiver,method); + } + if (qstrlen(signal) < 1 || qstrlen(method) < 1) { + return QObject::connect(sender,signal,receiver,method); + } + + const QMetaObject* const senderMo = sender->metaObject(); + + QByteArray normalSignal = QByteArray::fromRawData(signal+1, qstrlen(signal)-1); + int signal_index = senderMo->indexOfSignal(normalSignal); + + if (signal_index < 0) { + // See if we can find the signal after normalizing. + normalSignal = QMetaObject::normalizedSignature(normalSignal); + signal_index = senderMo->indexOfSignal(normalSignal); + } + if (signal_index < 0) { + // Nope, bail out. + return QObject::connect(sender,signal,receiver,method); + } + + const QMetaObject* const receiverMo = receiver->metaObject(); + + QByteArray normalMethod = QByteArray::fromRawData(method+1, qstrlen(method)-1); + int method_index = receiverMo->indexOfMethod(normalMethod); + + if (method_index < 0) { + // See if we can find the method after normalizing. + normalMethod = QMetaObject::normalizedSignature(normalMethod); + method_index = senderMo->indexOfMethod(normalMethod); + } + if (method_index < 0) { + // Nope, bail out. + return QObject::connect(sender,signal,receiver,method); + } + + // Ensure signal and slot are compatible. + if (!QMetaObject::checkConnectArgs(normalSignal.constData(), normalMethod.constData())) { + return QObject::connect(sender,signal,receiver,method); + } + + // If we get here, then everything is valid. + QtUiTestConnectionManager::instance()->connect(sender, signal_index, receiver, method_index); + return true; +} + +/*! + Disconnects \a signal in object \a sender from \a method in object \a receiver. Returns true if the connection is successfully broken; otherwise returns false. + + The connection must have been established with QtUiTest::connectFirst(). + + Passing null arguments has the same wildcard effects as documented in QObject::disconnect(). + + If the same connection has been established multiple times, disconnectFirst() will disconnect + all instances of the connection. There is no way to disconnect a single instance of a + connection. This behavior matches QObject::disconnect(). + + \sa QObject::disconnect(), QtUiTest::connectFirst() +*/ +bool QtUiTest::disconnectFirst(const QObject* sender, const char* signal, + const QObject* receiver, const char* method) +{ + // On failure, we use QObject::disconnect to get the same error message as + // we normally would. + if (sender == 0) { + return QObject::disconnect(sender,signal,receiver,method); + } + + const QMetaObject* const senderMo = sender->metaObject(); + + QByteArray normalSignal = (signal) + ? QByteArray::fromRawData(signal+1, qstrlen(signal)-1) + : QByteArray(); + int signal_index = (signal) ? senderMo->indexOfSignal(normalSignal) : -1; + if (signal && (signal_index < 0)) { + // See if we can find the signal after normalizing. + normalSignal = QMetaObject::normalizedSignature(signal); + signal_index = senderMo->indexOfSignal(normalSignal); + } + if (signal && (signal_index < 0)) { + // Nope, bail out. + return QObject::disconnect(sender,signal,receiver,method); + } + + if (method && !receiver) { + return QObject::disconnect(sender,signal,receiver,method); + } + + const QMetaObject* const receiverMo = (receiver) ? receiver->metaObject() : 0; + + QByteArray normalMethod = (method) + ? QByteArray::fromRawData(method+1, qstrlen(method)-1) + : QByteArray(); + int method_index = (method) ? receiverMo->indexOfMethod(normalMethod) : -1; + + if (method && (method_index < 0)) { + // See if we can find the method after normalizing. + normalMethod = QMetaObject::normalizedSignature(method); + method_index = senderMo->indexOfMethod(normalMethod); + } + if (method && (method_index < 0)) { + // Nope, bail out. + return QObject::disconnect(sender,signal,receiver,method); + } + + return QtUiTestConnectionManager::instance()->disconnect(sender,signal_index,receiver,method_index); +} + diff --git a/old/libqtuitest/qtuitestnamespace.h b/old/libqtuitest/qtuitestnamespace.h new file mode 100644 index 0000000..41d88c6 --- /dev/null +++ b/old/libqtuitest/qtuitestnamespace.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 QTUITESTNAMESPACE_H +#define QTUITESTNAMESPACE_H + +#include <qtuitestglobal.h> + +#include <QEvent> +#include <QObject> +#include <Qt> + +class QPoint; + +namespace QtUiTest +{ + enum InputOption { + NoOptions = 0x0, + DemoMode = 0x1, + KeyRepeat = 0x2 + }; + + enum WidgetType { + InputMethod, + TabBar + }; + + enum Key { +#ifdef Q_WS_QWS + Key_Activate = Qt::Key_Select, +#else + Key_Activate = Qt::Key_Enter, +#endif + +#ifdef Q_WS_QWS + Key_Select = Qt::Key_Select, +#else + Key_Select = Qt::Key_Space, +#endif + +#if defined(Q_WS_QWS) || defined (Q_OS_SYMBIAN) + Key_ActivateButton = Qt::Key_Select +#else + Key_ActivateButton = Qt::Key_Space +#endif + }; + + enum LabelOrientation { + LabelLeft = 0x01, + LabelRight = 0x02, + LabelAbove = 0x03, + LabelBelow = 0x04 + }; + + QTUITEST_EXPORT void setInputOption(InputOption,bool = true); + QTUITEST_EXPORT bool testInputOption(InputOption); + + QTUITEST_EXPORT QString errorString(); + QTUITEST_EXPORT void setErrorString(QString const&); + + QTUITEST_EXPORT void mousePress (QPoint const&,Qt::MouseButtons = Qt::LeftButton, + InputOption = NoOptions); + QTUITEST_EXPORT void mouseRelease(QPoint const&,Qt::MouseButtons = Qt::LeftButton, + InputOption = NoOptions); + QTUITEST_EXPORT void mouseClick (QPoint const&,Qt::MouseButtons = Qt::LeftButton, + InputOption = NoOptions); + QTUITEST_EXPORT bool mouseClick (QObject*,QPoint const&,Qt::MouseButtons = Qt::LeftButton, + InputOption = NoOptions); + QTUITEST_EXPORT bool mouseClick (QObject*,QByteArray const&,QPoint const&, + Qt::MouseButtons = Qt::LeftButton, InputOption = NoOptions); + + QTUITEST_EXPORT void keyPress (int,Qt::KeyboardModifiers = 0, + InputOption = NoOptions); + QTUITEST_EXPORT void keyRelease(int,Qt::KeyboardModifiers = 0, + InputOption = NoOptions); + QTUITEST_EXPORT void keyClick (int,Qt::KeyboardModifiers = 0, + InputOption = NoOptions); + QTUITEST_EXPORT bool keyClick (QObject*,int,Qt::KeyboardModifiers = 0, + InputOption = NoOptions); + QTUITEST_EXPORT bool keyClick (QObject*,QByteArray const&,int,Qt::KeyboardModifiers = 0, + InputOption = NoOptions); + + QTUITEST_EXPORT bool mousePreferred(); + QTUITEST_EXPORT void setMousePreferred(bool); + + QTUITEST_EXPORT LabelOrientation labelOrientation(); + QTUITEST_EXPORT void setLabelOrientation(LabelOrientation); + + QTUITEST_EXPORT int maximumUiTimeout(); + + QTUITEST_EXPORT Qt::Key asciiToKey(char); + QTUITEST_EXPORT Qt::KeyboardModifiers asciiToModifiers(char); + + QTUITEST_EXPORT QObject* findWidget(WidgetType); + + QTUITEST_EXPORT QObject* testWidget(QObject*,const char*); + + QTUITEST_EXPORT bool connectFirst (const QObject*, const char*, const QObject*, const char*); + QTUITEST_EXPORT bool disconnectFirst(const QObject*, const char*, const QObject*, const char*); + + template<class T> inline T qtuitest_cast_helper(QObject* object, T) + { + T ret; + if ((ret = qobject_cast<T>(object))) {} + else { + ret = qobject_cast<T>(QtUiTest::testWidget(object, + static_cast<T>(0)->_q_interfaceName())); + } + return ret; + } + + QTUITEST_EXPORT void wait(int); + QTUITEST_EXPORT bool waitForSignal(QObject*, const char*, int = QtUiTest::maximumUiTimeout(), Qt::ConnectionType = Qt::QueuedConnection); + QTUITEST_EXPORT bool waitForEvent(QObject*, QEvent::Type, int = QtUiTest::maximumUiTimeout(), Qt::ConnectionType = Qt::QueuedConnection); + QTUITEST_EXPORT bool waitForEvent(QObject*, QList<QEvent::Type> const&, int = QtUiTest::maximumUiTimeout(), Qt::ConnectionType = Qt::QueuedConnection); +}; + +template<class T> inline +T qtuitest_cast(const QObject* object) +{ + return QtUiTest::qtuitest_cast_helper<T>(const_cast<QObject*>(object),0); +} + +#include <qtuitestwidgetinterface.h> + +#endif + diff --git a/old/libqtuitest/qtuitestrecorder.cpp b/old/libqtuitest/qtuitestrecorder.cpp new file mode 100644 index 0000000..0a847b1 --- /dev/null +++ b/old/libqtuitest/qtuitestrecorder.cpp @@ -0,0 +1,367 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qtuitestrecorder.h" +#include "qtuitestnamespace.h" + +#include <QSet> +#include <QApplication> +#include <QWidget> +#include <QMetaType> +#include <QPointer> + +Q_DECLARE_METATYPE(QPointer<QObject>) + +/*! + \preliminary + \class QtUiTestRecorder + \inpublicgroup QtUiTestModule + \brief The QtUiTestRecorder class provides an interface for observing + application-wide high level user actions. + + QtUiTestRecorder aggregates the signals declared in the + \l{QtUiTest}{QtUiTest widget interfaces} to provide a single object + capable of watching for user input. + + Note that using this class is expensive because it involves watching + for signals on a large number of objects. When you create a + QtUiTestRecorder and connect to its signals, this will result in every + existing widget being wrapped in a \l{QtUiTest}{test widget}. + To avoid this, do not connect to the signals of a QtUiTestRecorder + unless you will definitely use the result. +*/ + +class WidgetWatcher; + +class QtUiTestRecorderPrivate : public QObject +{ + Q_OBJECT +public: + static WidgetWatcher* watcher; + static void createOrDestroyWatcher(); + +public slots: + void on_gotFocus(); + void on_activated(); + void on_stateChanged(int); + void on_entered(QVariant const&); + void on_selected(QString const&); +}; + +WidgetWatcher* QtUiTestRecorderPrivate::watcher = 0; + +Q_GLOBAL_STATIC(QtUiTestRecorderPrivate, qtuitestRecorderPrivate); +Q_GLOBAL_STATIC(QSet<QtUiTestRecorder*>, qtuitestRecorderInstances); + +/*! \internal */ +void QtUiTestRecorder::emitKeyEvent(int key, int mod, bool press, bool repeat) +{ + foreach(QtUiTestRecorder* r, *qtuitestRecorderInstances()) { + emit r->keyEvent(key, mod, press, repeat); + } +} + +/*! \internal */ +void QtUiTestRecorder::emitMouseEvent(int x, int y, int state) +{ + foreach(QtUiTestRecorder* r, *qtuitestRecorderInstances()) { + emit r->mouseEvent(x, y, state); + } +} + +void QtUiTestRecorderPrivate::on_gotFocus() +{ + foreach(QtUiTestRecorder* r, *qtuitestRecorderInstances()) { + emit r->gotFocus(sender()); + } +} + +void QtUiTestRecorderPrivate::on_activated() +{ + foreach(QtUiTestRecorder* r, *qtuitestRecorderInstances()) { + emit r->activated(sender()); + } +} + +void QtUiTestRecorderPrivate::on_stateChanged(int state) +{ + foreach(QtUiTestRecorder* r, *qtuitestRecorderInstances()) { + emit r->stateChanged(sender(), state); + } +} + +void QtUiTestRecorderPrivate::on_entered(QVariant const& item) +{ + foreach(QtUiTestRecorder* r, *qtuitestRecorderInstances()) { + emit r->entered(sender(), item); + } +} + +void QtUiTestRecorderPrivate::on_selected(QString const& item) +{ + foreach(QtUiTestRecorder* r, *qtuitestRecorderInstances()) { + emit r->selected(sender(), item); + } +} + +/* + This helper class attempts to ensure that all created objects immediately + have wrappers constructed around them. This is necessary for event + recording to work properly, as it relies on test widgets emitting signals. + + Since it is so expensive, an instance of this class should not be left + around whenever we don't have any QtUiTestRecorder instances. +*/ +class WidgetWatcher : public QObject +{ + Q_OBJECT +public: + WidgetWatcher(); + void castAllChildren(QObject*); + +public slots: + bool eventFilter(QObject*,QEvent*); + void castAllChildren(QPointer<QObject>); +}; + +WidgetWatcher::WidgetWatcher() +{ + castAllChildren(qApp); + foreach(QWidget* w, qApp->topLevelWidgets()) { + castAllChildren(w); + } +} + +bool WidgetWatcher::eventFilter(QObject* o, QEvent* e) +{ + if (e->type() == QEvent::ChildAdded) { + static struct Registrar { + Registrar() { + qRegisterMetaType< QPointer<QObject> >(); + } + } r; + QPointer<QObject> ptr(o); + QMetaObject::invokeMethod(this, "castAllChildren", + Qt::QueuedConnection, + Q_ARG(QPointer<QObject>, ptr)); + } + return false; +} + +void WidgetWatcher::castAllChildren(QPointer<QObject> o) +{ castAllChildren(static_cast<QObject*>(o)); } + +void WidgetWatcher::castAllChildren(QObject* o) +{ + if (!o) return; + qtuitest_cast<QtUiTest::Widget*>(o); + o->installEventFilter(this); + foreach (QObject* child, o->children()) + castAllChildren(child); +} + +void QtUiTestRecorderPrivate::createOrDestroyWatcher() +{ + QSet<QtUiTestRecorder*> const *recorders = qtuitestRecorderInstances(); + + bool need_watcher = false; + foreach (QtUiTestRecorder* rec, *recorders) { + if (rec->receivers(SIGNAL(gotFocus(QObject*))) + || rec->receivers(SIGNAL(activated(QObject*))) + || rec->receivers(SIGNAL(stateChanged(QObject*,int))) + || rec->receivers(SIGNAL(entered(QObject*,QVariant))) + || rec->receivers(SIGNAL(selected(QObject*,QString)))) + need_watcher = true; + + if (need_watcher) break; + } + + if (watcher && !need_watcher) { + delete watcher; + watcher = 0; + } else if (!watcher && need_watcher) { + watcher = new WidgetWatcher; + } +} + + +/*! + Constructs a QtUiTestRecorder with the given \a parent. +*/ +QtUiTestRecorder::QtUiTestRecorder(QObject* parent) + : QObject(parent) +{ + *qtuitestRecorderInstances() << this; +} + +/*! + Destroys the QtUiTestRecorder. +*/ +QtUiTestRecorder::~QtUiTestRecorder() +{ + qtuitestRecorderInstances()->remove(this); + QtUiTestRecorderPrivate::createOrDestroyWatcher(); +} + +/*! + \internal + May cause a WidgetWatcher to be created. +*/ +void QtUiTestRecorder::connectNotify(char const*) +{ + QtUiTestRecorderPrivate::createOrDestroyWatcher(); +} + +/*! + \internal + May cause a WidgetWatcher to be destroyed. +*/ +void QtUiTestRecorder::disconnectNotify(char const*) +{ + QtUiTestRecorderPrivate::createOrDestroyWatcher(); +} + +/*! + \fn void QtUiTestRecorder::gotFocus(QObject* widget) + + Emitted when \a widget obtains focus by any means. + + \sa QtUiTest::Widget::gotFocus() +*/ + +/*! + \fn void QtUiTestRecorder::activated(QObject* widget) + + Emitted when \a widget is activated by any means. + + In this context, "activated" means, for example, clicking a button. + + \sa QtUiTest::ActivateWidget::activated() +*/ + +/*! + \fn void QtUiTestRecorder::stateChanged(QObject* widget, int state) + + Emitted when the check state changes to \a state in \a widget. + + \sa QtUiTest::CheckWidget::stateChanged() +*/ + +/*! + \fn void QtUiTestRecorder::entered(QObject* widget, QVariant const& item) + + Emitted when \a item is entered into \a widget. + + \sa QtUiTest::InputWidget::entered() +*/ + +/*! + \fn void QtUiTestRecorder::selected(QObject* widget, QString const& item) + + Emitted when \a item is selected from \a widget. + + \sa QtUiTest::SelectWidget::selected() +*/ + +/*! + \fn void QtUiTestRecorder::keyEvent(int key, int modifiers, bool isPress, bool isAutoRepeat) + + Emitted when \a key (with \a modifiers) is pressed or released. + + \a key is compatible with Qt::Key and \a modifiers is compatible with + Qt::KeyboardModifiers. + + If the event is a press \a isPress is true, otherwise it is a release. + \a isAutoRepeat is true if the event is generated due to autorepeat. + + This signal is only emitted within the server process, because individual + applications cannot monitor system-wide key events. +*/ + +/*! + \fn void QtUiTestRecorder::mouseEvent(int x, int y, int state) + + Emitted when the mouse changes state. + + \a x and \a y are the global co-ordinates of the event. + \a state is the current state of the mouse buttons, compatible with Qt::MouseButtons. + + This signal is only emitted within the server process, because individual + applications cannot monitor system-wide mouse events. +*/ + +/*! + \internal + Connects all testwidget signals on \a object to all test recorder + instances. +*/ +void QtUiTestRecorder::connectToAll(QObject* o) +{ + if (!o) return; + + QMetaObject const* mo = o->metaObject(); + QSet<QByteArray> _signals; + +#define _DO(Iface, Signal) \ + if (qobject_cast<QtUiTest::Iface*>(o) && (-1 != mo->indexOfSignal(Signal))) { \ + _signals << Signal; \ + } + + _DO(Widget, "gotFocus()"); + _DO(ActivateWidget, "activated()"); + _DO(CheckWidget, "stateChanged(int)"); + _DO(InputWidget, "entered(QVariant)"); + _DO(SelectWidget, "selected(QString)"); + +#undef _DO + + foreach (QByteArray sig, _signals) { + QByteArray recorder_slot = SLOT(on_) + sig; + sig.prepend(QSIGNAL_CODE); + + /* Avoid multiple connections */ + disconnect(o, sig, qtuitestRecorderPrivate(), recorder_slot); + connect( o, sig, qtuitestRecorderPrivate(), recorder_slot); + } +} + +#include "qtuitestrecorder.moc" + diff --git a/old/libqtuitest/qtuitestrecorder.h b/old/libqtuitest/qtuitestrecorder.h new file mode 100644 index 0000000..6d984bd --- /dev/null +++ b/old/libqtuitest/qtuitestrecorder.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 QTUITESTRECORDER_H +#define QTUITESTRECORDER_H + +#include <QObject> +#include <qtuitestglobal.h> + +class QtUiTestRecorderPrivate; +class QtUiTestWidgets; + +class QTUITEST_EXPORT QtUiTestRecorder : public QObject +{ + Q_OBJECT + +public: + QtUiTestRecorder(QObject* =0); + ~QtUiTestRecorder(); + + static void emitKeyEvent(int,int,bool,bool); + static void emitMouseEvent(int,int,int); + +protected: + virtual void connectNotify(char const*); + virtual void disconnectNotify(char const*); + +signals: + void gotFocus(QObject*); + void activated(QObject*); + void stateChanged(QObject*,int); + void entered(QObject*,QVariant const&); + void selected(QObject*,QString const&); + + void keyEvent(int,int,bool,bool); + void mouseEvent(int,int,int); + +private: + static void connectToAll(QObject*); + friend class QtUiTestWidgets; + friend class QtUiTestRecorderPrivate; +}; + +#endif + diff --git a/old/libqtuitest/qtuitestwidgetinterface.cpp b/old/libqtuitest/qtuitestwidgetinterface.cpp new file mode 100644 index 0000000..7a42a00 --- /dev/null +++ b/old/libqtuitest/qtuitestwidgetinterface.cpp @@ -0,0 +1,1102 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qtuitestwidgetinterface.h" +#include "qtuitestnamespace.h" + +#include <QRect> +#include <QRegion> +#include <QApplication> +#include <QWidget> +#include <QVariant> + +/*! + \preliminary + \class QtUiTest::WidgetFactory + \inpublicgroup QtUiTestModule + \brief The WidgetFactory class provides a factory interface + for QtUiTest widget wrapper classes. + + QtUiTest::WidgetFactory is an abstract base class which enables the + creation of QtUiTest wrapper objects around Qt widgets. + + Customizing QtUiTest behaviour for particular widgets is achieved by + implementing one or more test widget classes which inherit from + one or more QtUiTest widget interfaces, + subclassing QtUiTest::WidgetFactory, reimplementing the pure virtual + keys() and create() functions to create instances of the custom test + widget classes, and exporting the factory class using the + Q_EXPORT_PLUGIN2() macro. +*/ + +/*! + \fn QtUiTest::WidgetFactory::create(QObject* object) + + Attempts to create a test widget to wrap \a object. Returns the created + test widget. Returns 0 if this factory does not support wrapping + \a object. + + The returned object is suitable for use with + \l{QtUiTest}{qtuitest_cast}. + + This function will only be called for objects which inherit one of the + classes returned by keys(). +*/ + +/*! + Returns a widget or test widget of \a type, or 0 if none can be found. + + Reimplement this function to provide custom behaviour for + QtUiTest::findWidget(). For example, if a custom soft menu widget is + being used rather than the shipped ContextLabel class, this function + must be reimplemented to return a pointer to the custom widget. + + The base implementation always returns 0. +*/ +QObject* QtUiTest::WidgetFactory::find(QtUiTest::WidgetType type) +{ Q_UNUSED(type); return 0; } + +/*! + \fn QtUiTest::WidgetFactory::keys() const + + Returns the list of C++ class names this factory can generate test widgets + for. + + Note that returning a class from this function does not guarantee that the + factory will always be able to generate a test widget for that class. +*/ + +/*! + \preliminary + \class QtUiTest::Widget + \inpublicgroup QtUiTestModule + \brief The Widget class provides an abstract base class + for all test widgets. + + QtUiTest::Widget contains functionality which maps + closely to QWidget. It encapsulates important information + and functionality related to two-dimensional GUI elements. + + All test widgets should implement the QtUiTest::Widget interface, + using multiple inheritance to implement other QtUiTest interfaces + where suitable. +*/ + +/*! + \fn const QRect& QtUiTest::Widget::geometry() const + + Returns the geometry of this widget in parent coordinates. + + \sa QWidget::geometry() +*/ + +/*! + Returns the bounding rect of this widget in widget coordinates. + + The base implementation returns geometry(), transformed to widget + coordinates. + + \sa QWidget::rect() +*/ +QRect QtUiTest::Widget::rect() const +{ + QRect ret(geometry()); + ret.moveTopLeft(QPoint(0,0)); + return ret; +} + +/*! + Returns the left of the widget, in global coordinates. + + \sa mapToGlobal() +*/ +int QtUiTest::Widget::x() const +{ + return mapToGlobal(QPoint()).x(); +} + +/*! + Returns the top of the widget, in global coordinates. + + \sa mapToGlobal() +*/ +int QtUiTest::Widget::y() const +{ + return mapToGlobal(QPoint()).y(); +} + +/*! + Returns the width of the widget. + + \sa geometry() +*/ +int QtUiTest::Widget::width() const +{ + return geometry().width(); +} + +/*! + Returns the height of the widget. + + \sa geometry() +*/ +int QtUiTest::Widget::height() const +{ + return geometry().height(); +} + +/*! + \fn bool QtUiTest::Widget::isVisible() const + + Returns true if this widget is currently visible. + + In this context, "visible" has the same meaning as in QWidget::isVisible(). + Therefore, this function returning \c{true} is not a guarantee that this + widget is currently on screen and unobscured. To test this, + visibleRegion() can be used. + + \sa QWidget::isVisible(), visibleRegion() +*/ + +/*! + \fn QRegion QtUiTest::Widget::visibleRegion() const + + Returns the currently on-screen, unobscured region of this widget, + in widget coordinates. + + \sa QWidget::visibleRegion() +*/ + +/*! + \fn bool QtUiTest::Widget::ensureVisibleRegion(const QRegion& region) + + Simulate whatever user input is necessary to ensure that \a region + (in local coordinates) is on-screen and unobscured. + + Returns true if \a region was successfully made visible. + + \sa visibleRegion() +*/ + +/*! + Simulate whatever user input is necessary to ensure that \a point + (in local coordinates) is on-screen and unobscured. + + This convenience function calls ensureVisibleRegion() with a region + containing only \a point. + + Returns true if \a point was successfully made visible. + + \sa visibleRegion(), ensureVisibleRegion() +*/ +bool QtUiTest::Widget::ensureVisiblePoint(const QPoint& point) +{ + return ensureVisibleRegion( QRegion(point.x(), point.y(), 1, 1) ); +} + +/*! + \fn QObject* QtUiTest::Widget::parent() const + + Returns the parent of this widget, or 0 if this widget has no parent. + + The returned object may be an actual QWidget, or may be a wrapping + test widget. Therefore, the only safe way to use the returned value + of this function is to cast it to the desired QtUiTest interface + using \l{QtUiTest}{qtuitest_cast}. + + \sa QObject::parent(), QWidget::parentWidget(), children() +*/ + +/*! + Returns the window title (caption). + + The returned string will typically be empty for all widgets other than + top-level widgets. + + The default implementation returns an empty string. + + \sa QWidget::windowTitle() +*/ +QString QtUiTest::Widget::windowTitle() const +{ return QString(); } + +/*! + \fn const QObjectList& QtUiTest::Widget::children() const + + Returns all children of this widget. + + The returned objects may be actual QWidget instances, or may be wrapping + test widgets. Therefore, the only safe way to use the returned objects + are to cast them to the desired QtUiTest interface using + \l{QtUiTest}{qtuitest_cast}. + + Reimplementing this function allows widgets which are conceptually + widgets but are not QObject subclasses to be wrapped. This can be + achieved by returning a list of test widgets which do not necessarily + have underlying QWidget instances. + + \sa QObject::children(), parent() +*/ + +/*! + Returns all descendants of this widget. + + \sa children(), parent() +*/ +void QtUiTest::Widget::descendants(QObjectList &list) const +{ + foreach (QObject *child, children()) { + if (!list.contains(child)) { + list << child; + QtUiTest::Widget* qw = qtuitest_cast<QtUiTest::Widget*>(child); + if (qw) + qw->descendants(list); + } + } +} + +/*! + Returns the currently on-screen, unobscured region of all + child widgets of this widget, in widget coordinates. This does not + include the visible region of this widget. + + The base implementation calculates the visible region by calling + visibleRegion() and childrenVisibleRegion() on all children(). + + \sa QWidget::visibleRegion(), children(), visibleRegion() +*/ +QRegion QtUiTest::Widget::childrenVisibleRegion() const +{ + QRegion ret; + foreach (QObject *o, children()) { + Widget *w = qtuitest_cast<Widget*>(o); + if (w) ret |= ((w->childrenVisibleRegion() | w->visibleRegion()) + .translated( -w->geometry().topLeft() ) ); + } + return ret; +} + +/*! + \fn QPoint QtUiTest::Widget::mapToGlobal(const QPoint& pos) const + + Maps \a pos from widget coordinates to global screen coordinates and + returns the result. + + \sa QWidget::mapToGlobal() +*/ + +/*! + \fn QPoint QtUiTest::Widget::mapFromGlobal(const QPoint& pos) const + + Maps \a pos from global screen coordinates to widget coordinates and + returns the result. + + \sa QWidget::mapFromGlobal() +*/ + +/*! + Returns the center point of the widget. The base implementation + returns geometry().center(). + + \sa QtUiTest::Widget::geometry() +*/ +QPoint QtUiTest::Widget::center() const +{ + QPoint ret; + ret = mapToGlobal( geometry().center() ); + return ret; +} + +/*! + Simulate the user input necessary to move keyboard focus to this + widget. + + The base implementation uses the result of hasFocus() to determine if the + widget currently has focus. If in keypad mode, a sequence of Up or Down + key presses will be used to move focus until this widget has focus. + If in mouse mode, the center of this widget will be clicked once. + + Due to the way this function works in keypad mode, it is very important + that focusOutEvent() is correctly implemented for all widgets to dismiss + any "grab" effects on keyboard focus. + + When reimplementing this function, it is necessary to call focusOutEvent() + on any widget before taking any action which could cause that widget to + lose focus. + + Returns true if focus was successfully given to this widget, false + otherwise. + + \sa QWidget::setFocus(), focusOutEvent() +*/ +bool QtUiTest::Widget::setFocus() +{ + if (hasFocus()) return true; + + // xxx Try using findWidget(Focus) here instead. + struct Focus { + static QWidget* focusWidget() { + QWidget *w = QApplication::focusWidget(); + if (!w) w = QApplication::activeWindow(); + if (!w) w = QApplication::activePopupWidget(); + if (!w) w = QApplication::activeModalWidget(); + return w; + } + }; + + using namespace QtUiTest; + + if (!mousePreferred()) { + bool backwards = false; + + QWidget *w = Focus::focusWidget(); + QWidget *initialFocus = w; + Widget *tw = qtuitest_cast<Widget*>(w); + if (!tw) { + setErrorString("Can't find currently focused widget!"); + return false; + } + + if (y() < tw->y() || (y() == tw->y() && x() < tw->x())) { + backwards = true; + } + + const int maxtries = 100; + int i = 0; + QWidget *pw = 0; + + while (!hasFocus() && i < maxtries) { + tw->focusOutEvent(); + +#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) + if (backwards) { + keyClick(Qt::Key_Up); + } else { + keyClick(Qt::Key_Down); + } +#else + if (backwards) { + keyClick(Qt::Key_Tab, Qt::ShiftModifier); + } else { + keyClick(Qt::Key_Tab); + } +#endif + pw = w; + if (w != pw && !QtUiTest::waitForSignal(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)))) { + setErrorString("Could not change widget focus using the keyboard"); + return false; + } + + //FIXME: Additional wait for Greenphone + QtUiTest::wait(200); + ++i; + w = Focus::focusWidget(); + if (w == initialFocus && w != pw) { + // Back to square one... + break; + } + tw = qtuitest_cast<Widget*>(w); + if (!tw) { + setErrorString("Can't find currently focused widget!"); + return false; + } + } + + if (!hasFocus()) { + setErrorString(QString("Failed to give focus to widget after %1 keyclicks") + .arg(maxtries)); + } + } else { + Widget* tw = qtuitest_cast<Widget*>(Focus::focusWidget()); + if (tw) tw->focusOutEvent(); + + QPoint center = rect().center(); + ensureVisiblePoint(center); + + center = mapToGlobal(center); + + // In the past, bugs have existed which make one click + // be consumed somewhere. + // Keep working in that case, but give a warning. + int i = 0; + do { + mouseClick( center ); + QtUiTest::waitForSignal(qApp, SIGNAL(focusChanged(QWidget*,QWidget*))); + if (hasFocus()) break; + } while(++i < 2); + + if (hasFocus() && 1 == i) + qWarning("QtUitest: possible bug, took more than one click to " + "give focus to a widget."); + } + + return hasFocus(); +} + +/*! + \fn bool QtUiTest::Widget::setEditFocus(bool enable) + Simulate the user input necessary to \a enable or disable edit focus for + this widget. Enabling edit focus will implicitly call setFocus() when + necessary. + + The concept of edit focus only exists in Qt Embedded. This function must + be implemented when using Qt Embedded. On other platforms, + this function will behave the same as setFocus() when enable is true, and + will have no effect when enable is false. + + Returns true if edit focus was successfully enabled or disabled for this + widget, false otherwise. + + \sa QWidget::setEditFocus(), hasEditFocus() +*/ +#ifndef Q_WS_QWS +bool QtUiTest::Widget::setEditFocus(bool enable) +{ + if (!enable) return true; + + return hasFocus() || setFocus(); +} +#endif + +/*! + \fn bool QtUiTest::Widget::hasEditFocus() const + Returns true if this widget currently has edit focus. + + The concept of edit focus only exists in Qt Embedded. This function must + be implemented on Qt Embedded. On other platforms, the base implementation + will return the same as hasFocus(). + + \sa QWidget::hasEditFocus(), setEditFocus() +*/ +#ifndef Q_WS_QWS +bool QtUiTest::Widget::hasEditFocus() const +{ return hasFocus(); } +#endif + +/*! + This function is called when this widget is about to lose keyboard focus. + The base implementation does nothing. + + This function should be reimplemented to put this widget in a state where + subsequent up/down key clicks will result in non-destructive navigation + between widgets. + + For example, if this function is called on a combo box which + currently has a list popped up, it should dismiss the list + so that subsequent key clicks will navigate between widgets rather + than navigating within the list. + + \code + void MyComboBox::focusOutEvent() { + // Assuming q is a QComboBox... + // If the list is currently popped up... + if (q->view()->isVisible()) { + // Press the Select key to commit the currently highlighted + // item and ensure we are ready to navigate by keys. + QtUiTest::keyClick(Qt::Key_Select); + } + } + \endcode + + \sa QWidget::focusOutEvent(), setFocus(), hasFocus() +*/ +void QtUiTest::Widget::focusOutEvent() +{} + +/*! + \fn bool QtUiTest::Widget::hasFocus() const + Returns true if this widget currently has keyboard focus. + + \sa QWidget::hasFocus() +*/ + +/*! + Returns the focus proxy of this widget, or 0 if this widget has no focus proxy. + A widget may "have focus", but have a child widget that actually handles the + focus. + + The returned object may be an actual QWidget, or may be a wrapping + test widget. Therefore, the only safe way to use the returned value + of this function is to cast it to the desired QtUiTest interface + using \l{QtUiTest}{qtuitest_cast}. + + \sa QWidget::focusProxy() +*/ +QObject* QtUiTest::Widget::focusProxy() const +{ + return 0; +} + +/*! + Returns the focus policy of this widget. + + The default implementation returns Qt::NoFocus, indicating that the widget + does not accept focus. + + \sa QWidget::focusPolicy() +*/ +Qt::FocusPolicy QtUiTest::Widget::focusPolicy () const +{ + return Qt::NoFocus; +} + +/*! + Returns the flags set on this widget. + + The default implementation returns 0, which is equivalent to a plain + Qt::Widget with no special flags set. +*/ +Qt::WindowFlags QtUiTest::Widget::windowFlags() const +{ return 0; } + +/*! + Returns true if this widget is of the given \a type. + + The base implementation always returns false. +*/ +bool QtUiTest::Widget::inherits(QtUiTest::WidgetType type) const +{ Q_UNUSED(type); return false; } + +/*! + Sets \a pixmap to image of the widget. Returns true on success. + + The base implementation always returns false. +*/ +bool QtUiTest::Widget::grabPixmap(QPixmap &pixmap) const +{ Q_UNUSED(pixmap); return false; } + +/*! + Returns true if this widget should be ignored by QtUiTest. If + a widget is ignored, any child widgets will still be processed. + + The base implementation always returns false. + + \sa {Query Paths}, {Querying Objects} +*/ +bool QtUiTest::Widget::ignoreScan() const +{ return false; } + +/*! + Returns true if this widget should not considered a possible + buddy for a label when resolving query paths. + + Note that if the widget is returned as a buddy by + QtUiTest::Label::buddy() then this value will be ignored. + + The base implementation always returns false. + + \sa QtUiTest::Label::buddy(), {Query Paths}, {Querying Objects} +*/ +bool QtUiTest::Widget::ignoreBuddy() const +{ return false; } + +/*! + \fn void QtUiTest::Widget::gotFocus() + + This signal is emitted when this widget gains focus by any means. + + \sa QApplication::focusChanged() +*/ + +QVariant QtUiTest::Widget::getProperty(const QString&) const +{ + return QVariant(); +} + +bool QtUiTest::Widget::setProperty(const QString&, const QVariant&) +{ + return false; +} + +/*! + \preliminary + \class QtUiTest::ActivateWidget + \inpublicgroup QtUiTestModule + \brief The ActivateWidget class provides an abstract base class + for all test widgets which can conceptually be "activated" by a user. + + "Activation" occurs when user input causes an action, possibly + non-reversible, on a widget which exists solely for the purpose of + causing that action. + + Examples of widgets suitable for this interface include QAbstractButton. +*/ + +/*! + \fn bool QtUiTest::ActivateWidget::activate() + Simulate the user input necessary to activate this widget. + + Returns true if this widget was successfully activated. + + For example, a button would reimplement this function to simulate + a click on itself, or to navigate to itself and hit the "Select" key. +*/ + +/*! + \fn void QtUiTest::ActivateWidget::activated() + + This signal is emitted when this widget is activated. +*/ + +/*! + \preliminary + \class QtUiTest::LabelWidget + \inpublicgroup QtUiTestModule + \brief The LabelWidget class provides an abstract base class + for all test widgets which are conceptually labels. + + Some widgets may act as labels for themselves while also providing + other functionality. For example, a button widget labels itself + and is also an ActivateWidget. + + Examples of widgets suitable for this interface include QLabel. +*/ + +/*! + \fn QString QtUiTest::LabelWidget::labelText() const + + Returns the text displayed on this label. + + Most label widgets will also implement \l{QtUiTest::TextWidget}. + Most commonly, labelText() returns the same value as \l{QtUiTest::TextWidget::text()}. +*/ + +/*! + Returns the label's buddy widget, or 0 if this widget has no buddy. + The buddy-label relationship is used by QtUiTest as part of query path + resolution. + + The returned object may be an actual QWidget, or may be a wrapping + test widget. Therefore, the only safe way to use the returned value + of this function is to cast it to the desired QtUiTest interface + using \l{QtUiTest}{qtuitest_cast}. + + \sa QLabel::buddy(), {Query Paths}, {Querying Objects} +*/ +QObject* QtUiTest::LabelWidget::buddy() const +{ + return 0; +} + +/*! + \preliminary + \class QtUiTest::CheckWidget + \inpublicgroup QtUiTestModule + \brief The CheckWidget class provides an abstract base class + for all test widgets which support 'checked' and 'unchecked' states. + + QtUiTest::CheckWidget exposes the current check state of widgets + which can be checked or unchecked. + + Examples of widgets suitable for this interface include QCheckBox + and QAbstractButton. +*/ + +/*! + Returns true if this widget has three possible states, i.e. the widget + can be in state Qt::PartiallyChecked. + + The base implementation returns false. + + \sa QCheckBox::isTristate() +*/ +bool QtUiTest::CheckWidget::isTristate() const +{ return false; } + +/*! + \fn Qt::CheckState QtUiTest::CheckWidget::checkState() const + + Returns the current check state of this widget. + + \sa QCheckBox::checkState(), setCheckState() +*/ + +/*! + Simulates the user input necessary to set the current check state + to \a state, returning true on success. + + The default implementation does nothing and returns false. + + \sa QCheckBox::setCheckState(), checkState() +*/ +bool QtUiTest::CheckWidget::setCheckState(Qt::CheckState state) +{ Q_UNUSED(state); return false; } + +/*! + \fn void QtUiTest::CheckWidget::stateChanged(int state) + + This signal is emitted when the check state of this widget changes + to \a state. \a state is compatible with Qt::CheckState. +*/ + +/*! + \preliminary + \class QtUiTest::TextWidget + \inpublicgroup QtUiTestModule + \brief The TextWidget class provides an abstract base class + for all test widgets which display text to the user. + + The QtUiTest::TextWidget interface should be implemented on any widget + which shows any text at all. This is the primary interface QtUiTest + uses to determine text->widget mappings, and it is used to implement + \l{QSystemTest::}{getText()}, a heavily used verification mechanism. + + This interface is closely related to QtUiTest::InputWidget, which + provides an interface for entering text into a widget. Any widgets + which contain user-editable text will typically implement both + QtUiTest::InputWidget and QtUiTest::TextWidget. + + Examples of widgets suitable for this interface include QLabel, + QAbstractButton, QLineEdit, QTextEdit and many more. + + \sa QtUiTest::InputWidget +*/ + +/*! + Returns the text in this widget which is currently selected / highlighted. + If the widget does not support the concept of selected text, this function + should return the same as text(). + + The base implementation calls text(). + + \sa QLineEdit::selectedText() +*/ +QString QtUiTest::TextWidget::selectedText() const +{ return text(); } + +/*! + Returns the value in this widget which is currently selected / highlighted. + If the widget does not support the concept of a selected value, this function + should return the same as value(). + + The base implementation calls selectedText(). +*/ +QVariant QtUiTest::TextWidget::selectedValue() const +{ return selectedText(); } + +/*! + \fn QString QtUiTest::TextWidget::text() const + + Returns all of the text this widget is currently presenting to the user. +*/ + +/*! + Returns the value this widget is currently presenting to the user. + + The returned value will be of whatever type is most appropriate for this widget. + For example, a QLineEdit would return a literal copy of its text (giving the same + result as the text() function), while a QTimeEdit may return a QTime. +*/ +QVariant QtUiTest::TextWidget::value() const +{ return text(); } + +/*! + \preliminary + \class QtUiTest::ListWidget + \inpublicgroup QtUiTestModule + \brief The ListWidget class provides an abstract base class + for all test widgets which display a list of items to the user. + + QtUiTest::ListWidget allows a widget which is conceptually a list to + be enumerated. This is closely related to QtUiTest::SelectWidget, + which may be implemented to allow a user to select an item from a list. + + Examples of widgets suitable for this interface include QAbstractItemView, + QComboBox and QMenu. + + \sa QtUiTest::SelectWidget +*/ + +/*! + \fn QStringList QtUiTest::ListWidget::list() const + + Returns a list containing a text representation of each item in this list + widget. +*/ + +/*! + \fn QRect QtUiTest::ListWidget::visualRect(const QString& item) const + + Returns the bounding rect of \a item, in widget coordinates. + If \a item isn't currently shown in this widget, returns a null rect. + + \sa QAbstractItemView::visualRect() +*/ + +/*! + Simulates the user input necessary to navigate this widget until \a item + is currently visible and return true on success. + + For example, in a QAbstractItemView with vertical scrollbars, if \a item + exists further down the list than currently shown, this function might + simulate 'Down' key clicks until it becomes visible. + + The base implementation does nothing and returns true. +*/ +bool QtUiTest::ListWidget::ensureVisible(const QString& item) +{ Q_UNUSED(item); return true; } + +/*! + \preliminary + \class QtUiTest::InputWidget + \inpublicgroup QtUiTestModule + \brief The InputWidget class provides an abstract base class + for all test widgets which allow the user to input text. + + QtUiTest::InputWidget encapsulates a widget in which a user may enter + and remove text. This is closely related to QtUiTest::TextWidget, which + provides an interface for retrieving text from a widget. Any widgets + which contain user-editable text will typically implement both + QtUiTest::InputWidget and QtUiTest::TextWidget. + + Examples of widgets suitable for this interface include QLineEdit and + QTextEdit. + + \sa QtUiTest::TextWidget +*/ + +/*! + \fn bool QtUiTest::InputWidget::canEnter(const QVariant& item) const + + Returns true if \a item can possibly be entered into this widget. + + For example, for a QLineEdit, QLineEdit::validator() would be used to + ensure that \l{QVariant::toString()}{item.toString()} constitutes + valid input for this line edit. +*/ + +/*! + \fn bool QtUiTest::InputWidget::enter(const QVariant& item, bool noCommit) + + Simulates the user input necessary to enter \a item into this widget. + Returns true on success. + + \a item can potentially be any type representable in a QVariant. + Most widgets will only be interested in text, in which case + \l{QVariant::toString()}{item.toString()} can be called to retrieve + a string. Examples of widgets which may handle types other than text + are QDateEdit and QTimeEdit, which may handle dates and times. + + If \a noCommit is true, no attempt is made to commit the input (for example, + by clicking the Select key). Normally this value will be false, which will + result in the input being committed. This value is not applicable to all + widget types. + + If canEnter() returns true and this function returns false, an error + has occurred and this widget is left in an undefined state. +*/ + +/*! + Returns the input proxy of this widget, or 0 if this widget has no input proxy. + + The input proxy is an object that receives input on behalf of the widget (for + example, an input method). It must be cast to the desired QtUiTest interface + using \l{QtUiTest}{qtuitest_cast}. +*/ +QObject* QtUiTest::InputWidget::inputProxy() const +{ + return 0; +} + +/*! + \fn void QtUiTest::InputWidget::entered(const QVariant& item) + This signal is emitted when \a item is entered into the widget + by the user. +*/ + +/*! + \preliminary + \class QtUiTest::SelectWidget + \inpublicgroup QtUiTestModule + \brief The SelectWidget class provides an abstract base class + for all test widgets which allow the user to select from a range of items. + + QtUiTest::SelectWidget encapsulates a widget which provides the user + with a choice from a (possibly unlimited) range. This is closely related + to QtUiTest::ListWidget, which may be implemented to allow a user to + enumerate all items from a list. + + Examples of widgets suitable for this interface include QAbstractItemView, + QComboBox and QMenu. + + \sa QtUiTest::ListWidget +*/ + +/*! + Returns true if this widget supports the selection of multiple items at + the same time. + + The base implementation returns false. +*/ +bool QtUiTest::SelectWidget::isMultiSelection() const +{ return false; } + +/*! + \fn bool QtUiTest::SelectWidget::canSelect(const QString& item) const + + Returns true if \a item can possibly be selected from this widget. +*/ + +/*! + Returns true if all of the given \a items can be selected from this widget + at the same time. + + The base implementation returns true if isMultiSelection() returns true + and canSelect() returns true for every item in \a items. +*/ +bool QtUiTest::SelectWidget::canSelectMulti(const QStringList& items) const +{ + if (!isMultiSelection()) return false; + foreach (QString item, items) + if (!canSelect(item)) return false; + return true; +} + +/*! + \fn bool QtUiTest::SelectWidget::select(const QString& item) + + Simulates the user input necessary to select \a item from this widget. + + Returns true if \a item was successfully selected. + + If canSelect() returns true and this function returns false, an error + has occurred and this widget's state is undefined. +*/ + +/*! + Simulates the user input necessary to select all \a items from this widget + at the same time. + + Returns true if \a items were all successfully selected. + + If canSelectMulti() returns true and this function returns false, an error + has occurred and this widget's state is undefined. + + The base implementation calls canSelectMulti() to check if \a items can + be selected, then calls select() on each item in \a items. +*/ +bool QtUiTest::SelectWidget::selectMulti(const QStringList& items) +{ + if (!canSelectMulti(items)) return false; + foreach (QString item, items) + if (!select(item)) return false; + return true; +} + +/*! + \fn void QtUiTest::SelectWidget::selected(const QString& item) + This signal is emitted when \a item is selected from this widget. +*/ + + +/*! + \preliminary + \class QtUiTest::CheckItemWidget + \inpublicgroup QtUiTestModule + \brief The CheckItemWidget class provides an abstract base class for all + test widgets which include items that support 'checked' and 'unchecked' states. + + Examples of widgets suitable for this interface include QMenu. +*/ + +/*! + Returns true if \a item can be checked (has a selectable on/off state). +*/ +bool QtUiTest::CheckItemWidget::isCheckable(const QString& item) +{ Q_UNUSED(item); return false; } + +/*! + \fn bool QtUiTest::CheckItemWidget::isChecked(const QString& item) + Returns the check state of the \a item specified. +*/ + +/*! + \fn bool QtUiTest::CheckItemWidget::setChecked(const QString& item, bool state) + Set the check state of the \a item. Returns false if the check state cannot be set. +*/ + + +/*! + \preliminary + \class QtUiTest::IndexedWidget + \inpublicgroup QtUiTestModule + \brief The IndexedWidget class provides an abstract base class + for all test widgets which allow the user to select items based on index. + + Examples of widgets suitable for this interface include QAbstractItemView. + + \sa QtUiTest::ListWidget, QtUiTest::SelectWidget +*/ + +/*! + Selects the item with the specified \a index from an item view. The index should + be specified in QtScript as an array, in the form [parentRow, parentColumn, ..., row, column]. + + This function can be used in situations where \l{QSystemTest::}{select()} would be inappropriate, + typically in larger, more complex item views or those containing items with duplicate names. + + \code + // Select row 3, column 5 from tableView + selectIndex([3, 5], tableView); + + // Select row 2, column 4 beneath parent item at row 1, column 0 from treeView + selectIndex([1, 0, 2, 4], treeView); + \endcode + + \sa select(), selectedIndex() +*/ +bool QtUiTest::IndexedWidget::selectIndex(const QVariantList& index) +{ + Q_UNUSED(index); + return false; +} + + +/*! + Returns the index of the item currently selected in an item view. This is returned + as a QVariantList, as required by selectIndex(). + + \sa selectIndex() +*/ +QVariantList QtUiTest::IndexedWidget::selectedIndex() const +{ + return QVariantList(); +} diff --git a/old/libqtuitest/qtuitestwidgetinterface.h b/old/libqtuitest/qtuitestwidgetinterface.h new file mode 100644 index 0000000..2bbecff --- /dev/null +++ b/old/libqtuitest/qtuitestwidgetinterface.h @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 QTUITESTWIDGETINTERFACE_H +#define QTUITESTWIDGETINTERFACE_H + +#include <QStringList> +#include <QVariantList> +#include <QtGlobal> +#include <qtuitestnamespace.h> +#include <qtuitestglobal.h> + +class QRect; +class QRegion; +class QPoint; +class QPixmap; + +namespace QtUiTest +{ +#ifndef Q_QDOC + template<class T> T qtuitest_cast_helper(QObject*,T); +#endif + +#define QTUITEST_INTERFACE(Klass) \ + public: \ + virtual ~Klass() {} \ + static const char* _q_interfaceName() { return #Klass; } \ + private: \ + template<class T> friend T qtuitest_cast_helper(QObject*,T); + + class QTUITEST_EXPORT WidgetFactory + { + QTUITEST_INTERFACE(WidgetFactory) + public: + virtual QObject* create(QObject*) = 0; + virtual QObject* find(QtUiTest::WidgetType); + virtual QStringList keys() const = 0; + }; + + class QTUITEST_EXPORT Widget + { + QTUITEST_INTERFACE(Widget) + public: + virtual const QRect& geometry() const = 0; + virtual QRect rect() const; + virtual int x() const; + virtual int y() const; + virtual int width() const; + virtual int height() const; + virtual bool isVisible() const = 0; + virtual QRegion visibleRegion() const = 0; + virtual QObject* parent() const = 0; + virtual QString windowTitle() const; + virtual const QObjectList &children() const = 0; + virtual void descendants(QObjectList &list) const; + virtual QRegion childrenVisibleRegion() const; + virtual QPoint mapToGlobal(const QPoint&) const = 0; + virtual QPoint mapFromGlobal(const QPoint&) const = 0; + virtual QPoint center() const; + virtual bool ensureVisibleRegion(const QRegion&) = 0; + bool ensureVisiblePoint(const QPoint&); + virtual bool setFocus(); + virtual void focusOutEvent(); + virtual bool hasFocus() const = 0; + virtual QObject* focusProxy() const; + virtual Qt::FocusPolicy focusPolicy () const; + virtual Qt::WindowFlags windowFlags() const; +#ifdef Q_WS_QWS + virtual bool hasEditFocus() const = 0; + virtual bool setEditFocus(bool) = 0; +#else + virtual bool hasEditFocus() const; + virtual bool setEditFocus(bool); +#endif + virtual bool inherits(QtUiTest::WidgetType) const; + virtual bool grabPixmap(QPixmap &pixmap) const; + virtual bool ignoreScan() const; + virtual bool ignoreBuddy() const; + virtual QVariant getProperty(const QString&) const; + virtual bool setProperty(const QString&, const QVariant&); + +#ifdef Q_QDOC + signals: + void gotFocus(); +#endif + }; + + class QTUITEST_EXPORT ActivateWidget + { + QTUITEST_INTERFACE(ActivateWidget) + public: + virtual bool activate() = 0; + +#ifdef Q_QDOC + signals: + void activated(); +#endif + }; + + class QTUITEST_EXPORT LabelWidget + { + QTUITEST_INTERFACE(LabelWidget) + public: + virtual QString labelText() const = 0; + virtual QObject* buddy() const; + }; + + class QTUITEST_EXPORT CheckWidget + { + QTUITEST_INTERFACE(CheckWidget) + public: + virtual bool isTristate() const; + virtual Qt::CheckState checkState() const = 0; + virtual bool setCheckState(Qt::CheckState); + +#ifdef Q_QDOC + signals: + void stateChanged(int); +#endif + }; + + class QTUITEST_EXPORT TextWidget + { + QTUITEST_INTERFACE(TextWidget) + public: + virtual QString selectedText() const; + virtual QVariant selectedValue() const; + virtual QString text() const = 0; + virtual QVariant value() const; + }; + + class QTUITEST_EXPORT ListWidget + { + QTUITEST_INTERFACE(ListWidget) + public: + virtual QStringList list() const = 0; + virtual QRect visualRect(const QString&) const = 0; + virtual bool ensureVisible(const QString&); + }; + + class QTUITEST_EXPORT InputWidget + { + QTUITEST_INTERFACE(InputWidget) + public: + virtual bool canEnter(const QVariant&) const = 0; + virtual bool enter(const QVariant&,bool) = 0; + virtual QObject *inputProxy() const; + +#ifdef Q_QDOC + signals: + void entered(const QVariant&); +#endif + }; + + class QTUITEST_EXPORT SelectWidget + { + QTUITEST_INTERFACE(SelectWidget) + public: + virtual bool isMultiSelection() const; + + virtual bool canSelect(const QString&) const = 0; + virtual bool canSelectMulti(const QStringList&) const; + virtual bool select(const QString&) = 0; + virtual bool selectMulti(const QStringList&); + +#ifdef Q_QDOC + signals: + void selected(const QString&); +#endif + }; + + class QTUITEST_EXPORT CheckItemWidget + { + QTUITEST_INTERFACE(CheckItemWidget) + public: + virtual bool isCheckable(const QString&); + virtual bool isChecked(const QString&) const = 0; + virtual bool setChecked(const QString&, bool) = 0; + }; + + class QTUITEST_EXPORT IndexedWidget + { + QTUITEST_INTERFACE(IndexedWidget) + public: + virtual bool selectIndex(const QVariantList&); + virtual QVariantList selectedIndex() const; + }; + +}; + +Q_DECLARE_INTERFACE( + QtUiTest::WidgetFactory, + "com.nokia.qt.QtUiTest.WidgetFactory/1.0") +Q_DECLARE_INTERFACE( + QtUiTest::Widget, + "com.nokia.qt.QtUiTest.Widget/1.0") +Q_DECLARE_INTERFACE( + QtUiTest::ActivateWidget, + "com.nokia.qt.QtUiTest.ActivateWidget/1.0") +Q_DECLARE_INTERFACE( + QtUiTest::LabelWidget, + "com.nokia.qt.QtUiTest.LabelWidget/1.0") +Q_DECLARE_INTERFACE( + QtUiTest::CheckWidget, + "com.nokia.qt.QtUiTest.CheckWidget/1.0") +Q_DECLARE_INTERFACE( + QtUiTest::TextWidget, + "com.nokia.qt.QtUiTest.TextWidget/1.0") +Q_DECLARE_INTERFACE( + QtUiTest::ListWidget, + "com.nokia.qt.QtUiTest.ListWidget/1.0") +Q_DECLARE_INTERFACE( + QtUiTest::InputWidget, + "com.nokia.qt.QtUiTest.InputWidget/1.0") +Q_DECLARE_INTERFACE( + QtUiTest::SelectWidget, + "com.nokia.qt.QtUiTest.SelectWidget/1.0") +Q_DECLARE_INTERFACE( + QtUiTest::CheckItemWidget, + "com.nokia.qt.QtUiTest.CheckItemWidget/1.0") +Q_DECLARE_INTERFACE( + QtUiTest::IndexedWidget, + "com.nokia.qt.QtUiTest.IndexedWidget/1.0") +#endif + diff --git a/old/libqtuitest/qtuitestwidgets.cpp b/old/libqtuitest/qtuitestwidgets.cpp new file mode 100644 index 0000000..9d163ef --- /dev/null +++ b/old/libqtuitest/qtuitestwidgets.cpp @@ -0,0 +1,657 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "qinputgenerator_p.h" +#include "qtuitestrecorder.h" +#include "qtuitestwidgetinterface.h" +#include "qtuitestwidgets_p.h" + +#include <QDebug> +#define qLog(A) if(1); else qDebug() << #A + +/* + 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. + + Disable this define to disable the race condition testing code. +*/ +#define QTUITEST_RACE_TEST + +#include <QApplication> +#include <QDir> +#include <QPluginLoader> +#include <QPointer> +#include <QStringList> +#include <QTimer> + +/*! + \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. +*/ + +#ifdef QTUITEST_RACE_TEST +enum QtUiTestInputType { QtUiTestPress, QtUiTestRelease, QtUiTestClick }; +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; +}; +static int qtUiTestGetInputDelay() +{ + bool ok; + QByteArray value(qgetenv("QTUITEST_INPUT_DELAY")); + int ret = value.toInt(&ok); + if (!ok || ret < 0) ret = -1; + return ret; +} +static int qtUiTestInputDelay() +{ static int ret = qtUiTestGetInputDelay(); return ret; } +#endif + + +class QtUiTestWidgetsPrivate +{ +public: + QtUiTestWidgetsPrivate(QtUiTestWidgets* parent); + + static QList<QByteArray> allImplementedInterfaces(QObject *o); + + void _q_objectDestroyed(); + + QWidget *inputMethodsWidget() const; + QString currentInputMethod() const; + + QtUiTestWidgets* q; + + QHash< QByteArray, QSet<QtUiTest::WidgetFactory*> > factories; + QHash< QObject*, QHash<QByteArray, QPointer<QObject> > > testWidgets; + QSet< QtUiTest::WidgetFactory* > factorySet; + + int inputOptions; + bool mousePreferred; + QtUiTest::LabelOrientation labelOrientation; + + QString errorString; + + QInputGenerator input; + +#ifdef QTUITEST_RACE_TEST + QList<QtUiTestKeyEvent> pendingKeyEvents; + QList<QtUiTestMouseEvent> pendingMouseEvents; + void _q_postNextKeyEvent(); + void _q_postNextMouseEvent(); +#else + inline void _q_postNextKeyEvent() {} + inline void _q_postNextMouseEvent() {} +#endif +}; + +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), + inputOptions(QtUiTest::NoOptions) +{ + mousePreferred = true; + mousePreferred = qgetenv("QTUITEST_NO_MOUSE").isEmpty(); + + labelOrientation = QtUiTest::LabelLeft; + if (qApp && qApp->layoutDirection() == Qt::RightToLeft) + labelOrientation = QtUiTest::LabelRight; +} + +QtUiTestWidgets::QtUiTestWidgets() + : QObject(), + d(new QtUiTestWidgetsPrivate(this)) +{ + refreshPlugins(); +} + +QtUiTestWidgets::~QtUiTestWidgets() +{ + 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() +{ + 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() +{ + 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(); +} + +/*! + 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) +{ + 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(QString const& error) +{ + if (error == d->errorString) return; + d->errorString = error; +} + +/*! + Returns a list of all QtUiTest widget interfaces implemented by \a o . +*/ +QList<QByteArray> QtUiTestWidgetsPrivate::allImplementedInterfaces(QObject *o) +{ + // 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::Widget*>(o)) ret << "Widget"; + if (qobject_cast<QtUiTest::ActivateWidget*>(o)) ret << "ActivateWidget"; + if (qobject_cast<QtUiTest::LabelWidget*>(o)) ret << "LabelWidget"; + if (qobject_cast<QtUiTest::CheckWidget*>(o)) ret << "CheckWidget"; + if (qobject_cast<QtUiTest::CheckItemWidget*>(o)) ret << "CheckItemWidget"; + if (qobject_cast<QtUiTest::TextWidget*>(o)) ret << "TextWidget"; + if (qobject_cast<QtUiTest::ListWidget*>(o)) ret << "ListWidget"; + if (qobject_cast<QtUiTest::InputWidget*>(o)) ret << "InputWidget"; + if (qobject_cast<QtUiTest::SelectWidget*>(o)) ret << "SelectWidget"; + if (qobject_cast<QtUiTest::IndexedWidget*>(o)) ret << "IndexedWidget"; + 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, QByteArray const &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 (QByteArray const& 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; + } // if (!isValuable) + else { + QtUiTestRecorder::connectToAll(testWidget); + } // if (isValuable) + } // if (testWidget) + } // foreach factory + mo = mo->superClass(); + } + ret = d->testWidgets.value( object ).value( interface ); + } + return ret; +} + +/*! + Returns a widget of \a type using factories. +*/ +QObject* QtUiTestWidgets::findWidget(QtUiTest::WidgetType type) +{ + foreach (QtUiTest::WidgetFactory *factory, d->factorySet) { + if (QObject *ret = factory->find(type)) + return ret; + } + + return 0; +} + +/*! + \internal + Load factories from all existing qtuitest_widgets plugins. +*/ +void QtUiTestWidgets::refreshPlugins() +{ + /* First, handle static plugins. */ + foreach (QObject *o, QPluginLoader::staticInstances()) { + registerFactory(qobject_cast<QtUiTest::WidgetFactory*>(o)); + } + + QSet<QString> pluginsToLoad; + + QList<QDir> pluginDirs; + foreach (QByteArray const& split, qgetenv("QTUITEST_WIDGETS_PATH").split(':')) { + if (split.isEmpty()) continue; + QDir dir(split); + if (dir.exists()) pluginDirs << dir; + } + + QString const pluginType("qtuitest_widgets"); + QSet<QString> libPaths = QCoreApplication::libraryPaths().toSet(); + + foreach (QString const& libPath, libPaths) { + QDir dir(libPath + "/" + pluginType); + if (!dir.exists()) { + continue; + } + pluginDirs << dir; + } + + foreach (QDir const& dir, pluginDirs) { + foreach (QString const& file, dir.entryList(QDir::Files|QDir::NoDotAndDotDot)) { + QString filename = dir.canonicalPath() + "/" + file; + if (!QLibrary::isLibrary(filename)) continue; + pluginsToLoad << filename; + } + } + +#ifdef Q_OS_SYMBIAN +// This is a temporary hack for S60 + pluginsToLoad << "qtwidgets.dll"; +#endif + + QPluginLoader pluginLoader; + QSet<QString> lastPluginsToLoad; + QStringList errors; + + // dumb dependency handling: keep trying to load plugins until we + // definitely can't progress. + while (lastPluginsToLoad != pluginsToLoad) { + lastPluginsToLoad = pluginsToLoad; + errors.clear(); + foreach (QString const& plugin, pluginsToLoad) { + pluginLoader.setFileName(plugin); + // enable RTLD_GLOBAL, so plugins can access each other's symbols. + pluginLoader.setLoadHints(QLibrary::ExportExternalSymbolsHint); + pluginLoader.load(); + + QObject *instance = pluginLoader.instance(); + QString error; + if (!instance) + error = "cannot resolve 'qt_plugin_instance': " + pluginLoader.errorString(); + + QtUiTest::WidgetFactory* fact = qobject_cast<QtUiTest::WidgetFactory*>(instance); + if (!fact) { + if (error.isEmpty()) error = pluginLoader.errorString(); + QString formattedError; + QDebug(&formattedError) + << "QtUitest: failed to load qtuitest widgets plugin" + << "\n plugin" << plugin + << "\n instance" << instance + << "\n error" << error; + errors << formattedError; + } else { + pluginsToLoad -= plugin; + registerFactory(fact); + } + } + } + + foreach (QString const& error, errors) + qWarning() << qPrintable(error); +} + +/*! + 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(QPoint const &pos, Qt::MouseButtons state, + QtUiTest::InputOption opt) +{ +#ifdef QTUITEST_RACE_TEST + int delay; + if ((delay = qtUiTestInputDelay()) != -1) { + QtUiTestMouseEvent event = {QtUiTestPress, pos, state, opt}; + d->pendingMouseEvents << event; + QTimer::singleShot(delay, this, SLOT(_q_postNextMouseEvent())); + return; + } +#endif + QTWOptStack st(d, opt); + d->input.mousePress(pos, state); +} + +/*! + 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(QPoint const &pos, Qt::MouseButtons state, + QtUiTest::InputOption opt) +{ +#ifdef QTUITEST_RACE_TEST + int delay; + if ((delay = qtUiTestInputDelay()) != -1) { + QtUiTestMouseEvent event = {QtUiTestRelease, pos, state, opt}; + d->pendingMouseEvents << event; + QTimer::singleShot(delay, this, SLOT(_q_postNextMouseEvent())); + return; + } +#endif + 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(QPoint const &pos, Qt::MouseButtons state, + QtUiTest::InputOption opt) +{ +#ifdef QTUITEST_RACE_TEST + int delay; + if ((delay = qtUiTestInputDelay()) != -1) { + QtUiTestMouseEvent event = {QtUiTestClick, pos, state, opt}; + d->pendingMouseEvents << event; + QTimer::singleShot(delay, this, SLOT(_q_postNextMouseEvent())); + return; + } +#endif + QTWOptStack st(d, opt); + d->input.mouseClick(pos, state); +} + +/*! + 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) +{ +#ifdef QTUITEST_RACE_TEST + int delay; + if ((delay = qtUiTestInputDelay()) != -1) { + QtUiTestKeyEvent event = {QtUiTestPress, key, mod, opt}; + d->pendingKeyEvents << event; + QTimer::singleShot(delay, this, SLOT(_q_postNextKeyEvent())); + return; + } +#endif + 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) +{ +#ifdef QTUITEST_RACE_TEST + int delay; + if ((delay = qtUiTestInputDelay()) != -1) { + QtUiTestKeyEvent event = {QtUiTestRelease, key, mod, opt}; + d->pendingKeyEvents << event; + QTimer::singleShot(delay, this, SLOT(_q_postNextKeyEvent())); + return; + } +#endif + 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) +{ +#ifdef QTUITEST_RACE_TEST + int delay; + if ((delay = qtUiTestInputDelay()) != -1) { + QtUiTestKeyEvent event = {QtUiTestClick, key, mod, opt}; + d->pendingKeyEvents << event; + QTimer::singleShot(delay, this, SLOT(_q_postNextKeyEvent())); + return; + } +#endif + QTWOptStack st(d, opt); + d->input.keyClick(key, mod); +} + +#ifdef QTUITEST_RACE_TEST +void QtUiTestWidgetsPrivate::_q_postNextMouseEvent() +{ + 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); +} + +void QtUiTestWidgetsPrivate::_q_postNextKeyEvent() +{ + 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); +} +#endif + +/*! + 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) +{ + if (on) + d->inputOptions |= option; + else + d->inputOptions &= (~option); + qLog(QtUitest) << "set input options to" << d->inputOptions; +} + +/*! + Returns true if \a option is currently set. +*/ +bool QtUiTestWidgets::testInputOption(QtUiTest::InputOption option) const +{ + return (option == d->inputOptions) + || (option & d->inputOptions); +} + +/*! + Returns true if the system will use mouse events in preference to key + events for widget and item selection. +*/ +bool QtUiTestWidgets::mousePreferred() const +{ + return d->mousePreferred; +} + +/*! + Set system to prefer mouse/touchscreen or keyboard for widget and item + selection. If \a useMouse is true then the mouse will be used, if false + then the keyboard will be used. +*/ +void QtUiTestWidgets::setMousePreferred(bool useMouse) +{ + d->mousePreferred = useMouse; +} + +QtUiTest::LabelOrientation QtUiTestWidgets::labelOrientation() const +{ + return d->labelOrientation; +} + +void QtUiTestWidgets::setLabelOrientation(QtUiTest::LabelOrientation orientation) +{ + d->labelOrientation = orientation; +} +#include "moc_qtuitestwidgets_p.cpp" + diff --git a/old/libqtuitest/qtuitestwidgets_p.h b/old/libqtuitest/qtuitestwidgets_p.h new file mode 100644 index 0000000..784b760 --- /dev/null +++ b/old/libqtuitest/qtuitestwidgets_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 QTUITESTWIDGETS_P_H +#define QTUITESTWIDGETS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtUiTest API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QObject> +#include <QMetaType> +#include <QHash> +#include <Qt> +#include <qtuitestnamespace.h> + +class QtUiTestWidgetsPrivate; +class QPoint; + +class QTUITEST_EXPORT QtUiTestWidgets : public QObject +{ + Q_OBJECT + +public: + static QtUiTestWidgets* instance(); + + virtual ~QtUiTestWidgets(); + + void setInputOption(QtUiTest::InputOption,bool = true); + bool testInputOption(QtUiTest::InputOption) const; + + bool mousePreferred() const; + void setMousePreferred(bool); + + QtUiTest::LabelOrientation labelOrientation() const; + void setLabelOrientation(QtUiTest::LabelOrientation); + + QString errorString() const; + void setErrorString(QString const&); + + void registerFactory(QtUiTest::WidgetFactory*); + + void mousePress (QPoint const&,Qt::MouseButtons = Qt::LeftButton, + QtUiTest::InputOption = QtUiTest::NoOptions); + void mouseRelease(QPoint const&,Qt::MouseButtons = Qt::LeftButton, + QtUiTest::InputOption = QtUiTest::NoOptions); + void mouseClick (QPoint const&,Qt::MouseButtons = Qt::LeftButton, + QtUiTest::InputOption = QtUiTest::NoOptions); + + void keyPress (Qt::Key,Qt::KeyboardModifiers = 0,QtUiTest::InputOption = QtUiTest::NoOptions); + void keyRelease(Qt::Key,Qt::KeyboardModifiers = 0,QtUiTest::InputOption = QtUiTest::NoOptions); + void keyClick (Qt::Key,Qt::KeyboardModifiers = 0,QtUiTest::InputOption = QtUiTest::NoOptions); + +private: + Q_DISABLE_COPY(QtUiTestWidgets) + + friend class QtUiTestWidgetsPrivate; + + QtUiTestWidgetsPrivate* d; + + QtUiTestWidgets(); + QObject* findWidget(QtUiTest::WidgetType); + QObject* testWidget(QObject*,QByteArray const&); + + void refreshPlugins(); + void clear(); + + Q_PRIVATE_SLOT(d, void _q_objectDestroyed()) + Q_PRIVATE_SLOT(d, void _q_postNextKeyEvent()) + Q_PRIVATE_SLOT(d, void _q_postNextMouseEvent()) + + friend QObject* QtUiTest::testWidget(QObject*,const char*); + friend QObject* QtUiTest::findWidget(QtUiTest::WidgetType); +}; + +#endif + diff --git a/old/libqtuitest/recordevent_p.h b/old/libqtuitest/recordevent_p.h new file mode 100644 index 0000000..f8ad42a --- /dev/null +++ b/old/libqtuitest/recordevent_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 RECORDEVENT_P_H +#define RECORDEVENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtUiTest API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QString> +#include <QVariant> + +struct RecordEvent { + enum Type { + GotFocus, + Entered, + Selected, + Activated, + CheckStateChanged, + TitleChanged, + MessageBoxShown + }; + Type type; + QString widget; + QString focusWidget; + QVariant data; +}; + +#define Q_DECLARE_METATYPE_STREAM(TYPE) \ + QT_BEGIN_NAMESPACE \ + template <> \ + struct QMetaTypeId< TYPE > \ + { \ + enum { Defined = 1 }; \ + static int qt_metatype_id() \ + { \ + static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \ + if (!metatype_id) { \ + metatype_id = qRegisterMetaType< TYPE >(#TYPE, \ + reinterpret_cast< TYPE *>(quintptr(-1))); \ + qRegisterMetaTypeStreamOperators< TYPE >(#TYPE); \ + } \ + return metatype_id; \ + } \ + }; \ + QT_END_NAMESPACE + +Q_DECLARE_METATYPE_STREAM(RecordEvent); +Q_DECLARE_METATYPE_STREAM(QList<RecordEvent>); + +inline bool operator==(RecordEvent const& a, RecordEvent const& b) +{ return a.type == b.type && a.widget == b.widget && a.focusWidget == b.focusWidget && a.data == b.data; } + +inline bool operator!=(RecordEvent const& a, RecordEvent const& b) +{ return !(a == b); } + +inline QDataStream &operator<<(QDataStream &out, const RecordEvent &re) +{ return (out << static_cast<int>(re.type) << re.widget << re.focusWidget << re.data); } + +inline QDataStream &operator>>(QDataStream &in, RecordEvent &re) +{ + int reType; + QDataStream &ret = (in >> reType >> re.widget >> re.focusWidget >> re.data); + re.type = static_cast<RecordEvent::Type>(reType); + return ret; +} + +inline QDataStream &operator<<(QDataStream &out, const QList<RecordEvent> &l) +{ + out << l.count(); + foreach (RecordEvent re, l) { + out << re; + } + return out; +} +inline QDataStream &operator>>(QDataStream &in, QList<RecordEvent> &l) +{ + int count = 0; + in >> count; + RecordEvent re; + for (int i = 0; i < count; ++i) { + in >> re; + l << re; + } + return in; +} + +#endif + diff --git a/old/libqtuitest/testwidget.cpp b/old/libqtuitest/testwidget.cpp new file mode 100644 index 0000000..15f5043 --- /dev/null +++ b/old/libqtuitest/testwidget.cpp @@ -0,0 +1,298 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 "testwidget.h" + +#include <QAbstractScrollArea> +#include <QApplication> +#include <QScrollBar> +#include <QVariant> +#include <QWidget> +#include <QGraphicsProxyWidget> +#include <QInputContext> + +namespace QtUiTest { + +TestWidget::TestWidget(QObject* _q) + : q(qobject_cast<QWidget*>(_q)) +{ q->installEventFilter(this); } + +bool TestWidget::eventFilter(QObject*,QEvent* e) +{ + if (e->type() == QEvent::FocusIn) { + emit gotFocus(); + } + return false; +} + +const QRect& TestWidget::geometry() const +{ return q->geometry(); } + +QRect TestWidget::rect() const +{ return q->rect(); } + +bool TestWidget::isVisible() const +{ return q->isVisible(); } + +QRegion TestWidget::visibleRegion() const +{ return q->visibleRegion(); } + +bool TestWidget::ensureVisibleRegion(const QRegion& region) +{ + if (region.intersected(visibleRegion()) == region) return true; + + if (!QtUiTest::mousePreferred()) { + /* + FIXME this is technically not correct, since we're assuming + that giving the widget keyboard focus makes the entire widget + visible. A low priority fix until we come across a case where + this matters. + */ + return setFocus(); + } + + /* Try to find a scroll area which contains this widget, then scroll. */ + + QAbstractScrollArea *sa = 0; + QWidget *parent = q->parentWidget(); + while (parent && !sa) { + sa = qobject_cast<QAbstractScrollArea*>(parent); + parent = parent->parentWidget(); + } + + if (!sa) return false; + + /* Figure out the points to click for scrolling in each direction */ + QScrollBar *vbar = sa->verticalScrollBar(); + QScrollBar *hbar = sa->horizontalScrollBar(); + QPoint up = vbar->mapToGlobal(QPoint(vbar->width()/2,5)); + QPoint down = vbar->mapToGlobal(QPoint(vbar->width()/2,vbar->height()-5)); + QPoint left = hbar->mapToGlobal(QPoint(5, hbar->height()/2)); + QPoint right = hbar->mapToGlobal(QPoint(hbar->width()-5,hbar->height()/2)); + + QRect brect_origin = region.boundingRect(); + QRect brect = brect_origin; + brect.moveTopLeft(q->mapTo(sa, brect.topLeft())); + + static const int MAX_CLICKS = 200; + int clicks = 0; + + /* Handle up... */ + while (brect.top() < 0) { + if (!vbar->isVisible()) return false; + QtUiTest::mouseClick(up); + QtUiTest::waitForSignal(vbar, SIGNAL(valueChanged(int))); + brect = brect_origin; + brect.moveTopLeft(q->mapTo(sa, brect.topLeft())); + if (++clicks > MAX_CLICKS) return false; + } + /* Handle down... */ + while (brect.bottom() > sa->height()) { + if (!vbar->isVisible()) return false; + QtUiTest::mouseClick(down); + QtUiTest::waitForSignal(vbar, SIGNAL(valueChanged(int))); + brect = brect_origin; + brect.moveTopLeft(q->mapTo(sa, brect.topLeft())); + if (++clicks > MAX_CLICKS) return false; + } + /* Handle left... */ + while (brect.left() < 0) { + if (!hbar->isVisible()) return false; + QtUiTest::mouseClick(left); + QtUiTest::waitForSignal(hbar, SIGNAL(valueChanged(int))); + brect = brect_origin; + brect.moveTopLeft(q->mapTo(sa, brect.topLeft())); + if (++clicks > MAX_CLICKS) return false; + } + /* Handle right... */ + while (brect.right() > sa->width()) { + if (!hbar->isVisible()) return false; + QtUiTest::mouseClick(right); + QtUiTest::waitForSignal(hbar, SIGNAL(valueChanged(int))); + brect = brect_origin; + brect.moveTopLeft(q->mapTo(sa, brect.topLeft())); + if (++clicks > MAX_CLICKS) return false; + } + return true; +} + +const QObjectList &TestWidget::children() const +{ return q->children(); } + +QObject* TestWidget::parent() const +{ return q->graphicsProxyWidget() ? q->graphicsProxyWidget() : q->parent(); } + +QString TestWidget::windowTitle() const +{ return q->windowTitle(); } + +QPoint TestWidget::mapToGlobal(QPoint const &local) const +{ + QGraphicsProxyWidget *pw; + if ( (pw = q->graphicsProxyWidget()) ) { + QtUiTest::Widget *w = qtuitest_cast<QtUiTest::Widget*>(pw); + if (w) { + return w->mapToGlobal(local); + } + } + return q->mapToGlobal(local); +} + +QPoint TestWidget::mapFromGlobal(QPoint const &global) const +{ + return q->mapFromGlobal(global); +} + +bool TestWidget::hasFocus() const +{ + return q->hasFocus(); +} + +Qt::WindowFlags TestWidget::windowFlags() const +{ + return q->windowFlags(); +} + +bool TestWidget::canEnter(QVariant const&) const +{ + return false; +} + +bool TestWidget::enter(QVariant const& item, bool noCommit) +{ + Q_UNUSED(noCommit); + if (!hasFocus() && !setFocus()) return false; + + using namespace QtUiTest; + + /* If there's text currently in the field then erase it first */ + if (QtUiTest::mousePreferred()) { + return false; + } + + foreach (QChar const& c, item.toString()) { + keyClick( asciiToKey(c.toLatin1()), asciiToModifiers(c.toLatin1()) ); + } + return true; +} + +void TestWidget::focusOutEvent() +{ +} + +#ifdef Q_WS_QWS +bool TestWidget::hasEditFocus() const +{ return q->hasEditFocus(); } +#endif + +bool TestWidget::setEditFocus(bool enable) +{ + if (hasEditFocus() == enable) return true; + + if (!hasFocus()) { + if (!setFocus()) return false; + } + + // It is possible that giving us regular focus also gave us edit focus. + if (hasEditFocus() == enable) return true; + +#ifdef Q_WS_QWS + if (!QtUiTest::keyClick(q, QtUiTest::Key_Activate)) return false; + if (hasEditFocus() != enable && !QtUiTest::waitForEvent(q, enable ? QEvent::EnterEditFocus : QEvent::LeaveEditFocus)) { + return false; + } + return (hasEditFocus() == enable); +#else + return true; +#endif +} + +QObject* TestWidget::focusProxy() const +{ + return q->focusProxy(); +} + +Qt::FocusPolicy TestWidget::focusPolicy () const +{ + return q->focusPolicy(); +} + +bool TestWidget::canWrap(QObject *o) +{ return qobject_cast<QWidget*>(o); } + +QString TestWidget::printable(QString const& str) +{ + QString ret(str); + + ret.replace(QChar::Nbsp, " "); + ret.remove(QChar(0x200E)); + ret.remove(QChar(0x200F)); + ret.remove(QChar(0x00AD)); + + return ret; +} + +bool TestWidget::grabPixmap(QPixmap &pixmap) const +{ + pixmap = QPixmap::grabWidget( q ); + return true; +} + +QString TestWidget::labelText(const QString& text) +{ + return text.trimmed().replace(QRegExp("&(.)"), "\\1"); +} + +QVariant TestWidget::getProperty(const QString& name) const +{ + return q->property(name.toAscii()); +} + +bool TestWidget::setProperty(const QString& name, const QVariant& value) +{ + return q->setProperty(name.toAscii(), value); +} + +QObject* TestWidget::inputProxy() const +{ + return q->inputContext(); +} + +}
\ No newline at end of file diff --git a/old/libqtuitest/testwidget.h b/old/libqtuitest/testwidget.h new file mode 100644 index 0000000..6fb0dc5 --- /dev/null +++ b/old/libqtuitest/testwidget.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** 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 QtUiTest. +** +** $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 TESTWIDGET_H +#define TESTWIDGET_H + +#include <QObject> +#include <qtuitestwidgetinterface.h> + +class QWidget; + +namespace QtUiTest { + +class QTUITEST_EXPORT TestWidget : public QObject, public QtUiTest::Widget, + public QtUiTest::InputWidget +{ + Q_OBJECT + Q_INTERFACES(QtUiTest::Widget QtUiTest::InputWidget) + +public: + TestWidget(QObject*); + + virtual const QRect& geometry() const; + virtual QRect rect() const; + virtual bool isVisible() const; + virtual QRegion visibleRegion() const; + virtual const QObjectList &children() const; + virtual QObject* parent() const; + virtual QString windowTitle() const; + virtual QPoint mapToGlobal(QPoint const&) const; + virtual QPoint mapFromGlobal(QPoint const&) const; + virtual bool hasFocus() const; + virtual Qt::WindowFlags windowFlags() const; + virtual bool ensureVisibleRegion(QRegion const&); + virtual bool canEnter(QVariant const&) const; + virtual bool enter(QVariant const&,bool); + virtual void focusOutEvent(); + +#ifdef Q_WS_QWS + virtual bool hasEditFocus() const; +#endif + virtual bool setEditFocus(bool); + virtual QObject* focusProxy() const; + virtual Qt::FocusPolicy focusPolicy () const; + virtual bool grabPixmap(QPixmap &pixmap) const; + + static bool canWrap(QObject*); + static QString printable(QString const&); + static QString labelText(QString const&); + virtual QVariant getProperty(const QString&) const; + virtual bool setProperty(const QString&, const QVariant&); + virtual QObject *inputProxy() const; + +signals: + void gotFocus(); + +protected: + bool eventFilter(QObject*,QEvent*); + +private: + QWidget *q; +}; + +} + +#endif + |