diff options
Diffstat (limited to 'src/remote/slave/lib/qtestwidgets.cpp')
-rw-r--r-- | src/remote/slave/lib/qtestwidgets.cpp | 623 |
1 files changed, 623 insertions, 0 deletions
diff --git a/src/remote/slave/lib/qtestwidgets.cpp b/src/remote/slave/lib/qtestwidgets.cpp new file mode 100644 index 0000000..f2e9077 --- /dev/null +++ b/src/remote/slave/lib/qtestwidgets.cpp @@ -0,0 +1,623 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of QtSystemTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <qtestwidgets.h> +#include <QtSystemTest/QstDebug> +#include <QRect> + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + #include <QGuiApplication> + #include <QWindow> +#else + #include <QApplication> + #include <QWidget> +#endif + +#include <QPlatformNativeInterface> + +#define qLog() if (1); else QtUiTest::debug() + +uint qHash(QPointer<QObject> o) +{ return qHash((QObject*)o); } + +/*! + Returns a unique string identifier for \a object. +*/ +QString QTestWidgets::signature( const QObject *o ) +{ + if (!o) { + return QLatin1String("ERROR_NO_OBJECT"); + } + QString ret = QActiveTestWidget::instance()->signature(o); + if (!ret.isEmpty()) { + return ret; + } + + QtUiTest::Object* w = qtuitest_cast<QtUiTest::Object*>(o); + QString typeName; + if (w) { + typeName = w->typeOf(); + } else { + typeName = o->metaObject()->className(); + } + + return QString("%1[%2]").arg(typeName).arg((long)(void*)o,0,32).replace(QLatin1Char(' '), QLatin1Char('_')); +} + +/*! + Returns the testWidget that is associated with the given \a signature. +*/ +QObject *QTestWidgets::testWidget( const QString &signature ) +{ + QTT_TRACE_FUNCTION(); + return QActiveTestWidget::instance()->testWidget(signature); +} + +QObject *QActiveTestWidget::testWidget( const QString &signature ) +{ + QTT_TRACE_FUNCTION(); + QObject *rec = d->testWidgetsBySignature.value( signature, 0 ); + return rec; +} + +QString QActiveTestWidget::signature( const QObject *o ) const +{ + return d->signaturesByObject.value(o); +} + +void QActiveTestWidget::registerTestWidget( QObject *ao ) +{ + QTT_TRACE_FUNCTION(); + QString new_signature = QTestWidgets::signature( ao ); + if (d->testWidgetsBySignature.contains(new_signature)) { + return; + } + d->testWidgetsBySignature[new_signature] = ao; + d->signaturesByObject[ao] = new_signature; + connect( ao, SIGNAL(destroyed(QObject*)), this, SLOT(onDestroyed(QObject*)), Qt::DirectConnection ); +} + +void QActiveTestWidget::onDestroyed(QObject *o) +{ + QTT_TRACE_FUNCTION(); + if (o) { + d->removeTestWidget(o); + } +} + +QVariant QTestWidgets::windowProperty(const QString &property) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + QTT_TRACE_FUNCTION(); + QWindow *rootWindow = qobject_cast<QWindow*>(activeWidget()); + if (qGuiApp && rootWindow && qGuiApp->platformNativeInterface() && rootWindow->handle()) { + return qGuiApp->platformNativeInterface()->windowProperty(rootWindow->handle(), property); + } +#endif + return QVariant(); +} + +/*! + Returns the current activeWindow, activePopupWidget or activeModalWidget. +*/ +QObject* QTestWidgets::activeWidget() +{ + QTT_TRACE_FUNCTION(); + QObject *w = 0; + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + w = qApp->focusWindow(); + + if (!w && !qApp->topLevelWindows().isEmpty()) + w = qApp->topLevelWindows().first(); +#else + if (w == 0) w = qApp->activeModalWidget(); + if (w == 0) w = qApp->activePopupWidget(); + if (w == 0) w = qApp->activeWindow(); + if (w == 0) w = qApp->focusWidget(); +#endif + + return w; +} + +QStringList QTestWidgets::objectListToSignatures(const QObjectList& ol) +{ + QTT_TRACE_FUNCTION(); + QStringList ret; + foreach (QObject *obj, ol) { + ret << QTestWidgets::signature(obj); + } + return ret; +} + + +QActiveTestWidgetData::QActiveTestWidgetData() +{ + QTT_TRACE_FUNCTION(); +} + +QActiveTestWidgetData::~QActiveTestWidgetData() +{ + QTT_TRACE_FUNCTION(); +} + +void QActiveTestWidgetData::clear() +{ + QTT_TRACE_FUNCTION(); + testWidgetsBySignature.clear(); + signaturesByObject.clear(); + m_scannedItems.clear(); +} + +void QActiveTestWidgetData::removeTestWidget( QObject *w ) +{ + QTT_TRACE_FUNCTION(); + m_scannedItems.remove(w); + testWidgetsBySignature.remove( QTestWidgets::signature(w) ); + signaturesByObject.remove(w); +} + +bool QActiveTestWidgetData::scan( QObject *ao, QtUiTest::QueryFlags flags ) +{ + QTT_TRACE_FUNCTION(); + if (!ao) return false; + + QActiveTestWidget::instance()->registerTestWidget(ao); + QtUiTest::Object* aw = qtuitest_cast<QtUiTest::Object*>(ao); + if (aw == 0) return false; + bool any_appended = false; + foreach(QObject *o, aw->children()) { + QActiveTestWidget::instance()->registerTestWidget(o); + + QtUiTest::Widget* qw = qtuitest_cast<QtUiTest::Widget*>(o); + + if (!qw->isVisible() && !(flags & QtUiTest::ScanInvisible)) { + continue; + } + + if (!qw->ignoreScan() || (flags & QtUiTest::ScanDisabledItems)) { + // Recursively scan child widgets + any_appended |= scan(o, flags); + } + + if (!m_scannedItems.contains(o)) { + any_appended = true; + m_scannedItems.insert(o); + } + } + + return any_appended; +} + + +QActiveTestWidget::QActiveTestWidget() +{ + QTT_TRACE_FUNCTION(); + d = new QActiveTestWidgetData(); + auto_scan = true; +} + +QActiveTestWidget::~QActiveTestWidget() +{ + QTT_TRACE_FUNCTION(); + delete d; +} + +QActiveTestWidget* QActiveTestWidget::instance() +{ + static QActiveTestWidget qatw; + return &qatw; +} + +void QActiveTestWidget::clear() +{ + QTT_TRACE_FUNCTION(); + if (d == 0) { + d = new QActiveTestWidgetData(); + } else + d->clear(); +} + +const QLatin1String QActiveTestWidget::NoActiveWidgetError("ERROR: No active widget available"); + +bool QActiveTestWidget::rescan(QtUiTest::QueryFlags flags) +{ + QTT_TRACE_FUNCTION(); + if (!auto_scan) return true; + + if (d == 0) d = new QActiveTestWidgetData(); + d->m_scannedItems.clear(); + + QObject *activeWidget = QTestWidgets::activeWidget(); + if (activeWidget == 0) { + QtUiTest::setErrorString(NoActiveWidgetError); + return false; + } + + d->scan(qApp, flags); + return true; +} + +QStringList QActiveTestWidget::descendantTree(QObject *obj) +{ + QStringList ret; + QtUiTest::Object *tw = qtuitest_cast<QtUiTest::Object*>(obj); + if (!tw) { + return ret; + } + ret << QString("%1 [ %2 ]").arg(QTestWidgets::signature(obj)).arg(tw->debugString()); + QObjectList children = tw->children(); + int length = children.length(); + for (int i=0; i<length; ++i) { + QObject *child = children[i]; + QStringList childTree = descendantTree(child); + ret << QString("+- %1").arg(childTree.takeFirst()); + foreach (const QString &row, childTree) { + if (i < length-1) { + ret << QString("| %1").arg(row); + } else { + ret << QString(" %1").arg(row); + } + } + } + return ret; +} + +void QActiveTestWidget::setAutoScan(bool doScan) +{ + auto_scan = doScan; +} + +bool QActiveTestWidget::forceRescan() +{ + bool prev = auto_scan; + auto_scan = true; + bool ret = rescan(); + auto_scan = prev; + return ret; +} + +bool QActiveTestWidget::findWidget( const QString &signature, QObject *&buddy, QString &error ) +{ + QTT_TRACE_FUNCTION(); + if (signature.isEmpty()) { + error = QLatin1String("ERROR: No widget specified\n"); + return false; + } + buddy = QTestWidgets::testWidget(signature); + return (buddy != 0); +} + +/*! + Returns a QVariantMap containing details of object specified by \a signature + and descendants. This is used by the Object Browser. +*/ +QVariantMap QActiveTestWidget::getObjects(const QString &signature) +{ + QTT_TRACE_FUNCTION(); + QVariantMap map; + QObject *baseObject = QTestWidgets::testWidget(signature); + + //FIXME: For now: + baseObject = qApp; + + if (!baseObject) { + return map; + } + + QtUiTest::Object *tw = qtuitest_cast<QtUiTest::Object*>(baseObject); + if (!tw) { + return map; + } + + QObjectList objects; + objects << baseObject << tw->descendants(); + + QVariantMap idMap; + QVariantMap geometryMap; + QVariantMap childMap; + QVariantMap typeMap; + QVariantMap objectNameMap; + QVariantMap visibleMap; + QVariantMap textMap; + QVariantMap windowMap; + + foreach (QObject *obj, objects) { + QActiveTestWidget::instance()->registerTestWidget(obj); + QtUiTest::Object *to = qtuitest_cast<QtUiTest::Object*>(obj); + + if (to) { + QString sig = QTestWidgets::signature(obj); + QObjectList children = to->children(); + if (!children.isEmpty()) { + childMap[sig] = QTestWidgets::objectListToSignatures(to->children()); + } + objectNameMap[sig] = to->getProperty("objectName"); + typeMap[sig] = to->typeOf(); + QtUiTest::Widget *tw = qtuitest_cast<QtUiTest::Widget*>(obj); + if (tw) { + geometryMap[sig] = tw->geometry(); + idMap[sig] = to->getProperty("QMLID"); + visibleMap[sig] = to->getProperty("visible"); + textMap[sig] = to->getProperty("text"); + windowMap[sig] = QTestWidgets::signature(tw->window()); + } + } + } + + map["geometry"] = geometryMap; + map["qmlid"] = idMap; + map["children"] = childMap; + map["type"] = typeMap; + map["objectName"] = objectNameMap; + map["visible"] = visibleMap; + map["text"] = textMap; + map["window"] = windowMap; + + return map; +} + +const QLatin1String QActiveTestWidget::childOfProperty("CHILD_OF"); +const QLatin1String QActiveTestWidget::descendantOfProperty("DESCENDANT_OF"); +const QLatin1String QActiveTestWidget::ancestorOfProperty("ANCESTOR_OF"); +const QLatin1String QActiveTestWidget::parentOfProperty("PARENT_OF"); +const QLatin1String QActiveTestWidget::resourceOfProperty("RESOURCE_OF"); + +QObjectList QActiveTestWidget::findObjectsByProperty( const QString &property, const QVariant &searchValue, const QObjectList &searchList ) +{ + QTT_TRACE_FUNCTION(); + QObjectList foundList; + QString propertyUpper = property.toUpper(); + + bool ok; + PropertyMatcher matcher(searchValue, ok); + if (!ok) { + QtUiTest::setErrorString(QString("Unable to match for property '%1'").arg(property)); + return foundList; + } + + foreach (QObject *obj, searchList) { + QtUiTest::Object *qo = qtuitest_cast<QtUiTest::Object*>(obj); + if (!qo) { + continue; + } + + QVariant value = qo->getProperty(property); + if (value.isValid()) { + if (QObject *obj = value.value<QObject*>()) { + value = QTestWidgets::signature(obj); + } + + // Allow enums to match by value or by string + if (value.type() == QVariant::Map + && value.toMap().contains(QLatin1String("_value_"))) { + if (searchValue.type() == QVariant::String + || searchValue.type() == QVariant::ByteArray) { + value = value.toMap().value(QLatin1String("_enum_")); + } else { + value = value.toMap().value(QLatin1String("_value_")); + } + } + + if (matcher.match(value)) { + foundList << obj; + } + continue; + } + + if (qo->matchProperty(propertyUpper, searchValue)) { + foundList << obj; + continue; + } + } + + return foundList; +} + +QObjectList QActiveTestWidget::queryProperties( const QVariantMap &searchValues, QtUiTest::QueryFlags flags ) +{ + QTT_TRACE_FUNCTION(); + QSet<QObject*> initObjects; + QObjectList searchList; + + if (!((flags & QtUiTest::NoScan) || (flags & QtUiTest::IgnoreScanned))) { + if (!rescan(flags)) { + return searchList; + } + } + + if (!(flags & QtUiTest::IgnoreScanned)) { + initObjects << QTestWidgets::activeWidget(); + initObjects.unite(d->m_scannedItems); + } + + static QStringList filterProperties(QStringList() + << ancestorOfProperty << childOfProperty << descendantOfProperty + << parentOfProperty << resourceOfProperty); + + QVariantMap::const_iterator i = searchValues.constBegin(); + while (i != searchValues.constEnd()) { + QString property = i.key().toUpper(); + if (filterProperties.contains(property)) { + QObjectList filterList; + QString signature; + if (i.value().type() == QVariant::Map) { + signature = i.value().toMap().value(QLatin1String("_signature_")).toString(); + } else { + signature = i.value().toString(); + } + + QString error; + QObject *w; + + if (findWidget(signature, w, error)) { + QtUiTest::Object *qw = qtuitest_cast<QtUiTest::Object*>(w); + if (!qw) { + QtUiTest::setErrorString(QString("Can't find widget '%1' for property '%2'").arg(signature).arg(property)); + return searchList; + } + if (property == childOfProperty) { + filterList << qw->children(); + } else if (property == descendantOfProperty) { + filterList << qw->descendants(); + } else if (property == ancestorOfProperty) { + filterList << qw->ancestors(); + } else if (property == parentOfProperty) { + filterList << qw->parent(); + } else if (property == resourceOfProperty) { + QVariantList resources = qw->resources(); + foreach (const QVariant resource, resources) { + if (QObject *obj = resource.value<QObject*>()) { + filterList << obj; + } + } + } + } + + if (initObjects.isEmpty()) { + initObjects = QSet<QObject*>::fromList(filterList); + } else { + foreach (QObject *obj, initObjects) { + if (!filterList.contains(obj)) { + initObjects.remove(obj); + } + } + if (initObjects.isEmpty()) { + return searchList; + } + } + } + ++i; + } + + searchList = initObjects.toList(); + + i = searchValues.constBegin(); + while (i != searchValues.constEnd()) { + if (!filterProperties.contains(i.key().toUpper())) { + searchList = findObjectsByProperty(i.key(), i.value(), searchList); + if (searchList.isEmpty()) + break; + } + ++i; + } + + return searchList; +} + +PropertyMatcher::PropertyMatcher(const QVariant &searchValue, bool &ok) + : m_matchFunction(0) + , m_searchDouble(0) +{ + ok = true; + if (searchValue.type() == QVariant::Map) { + QVariantMap map = searchValue.toMap(); + m_searchValue = map["_value_"]; + QString op = map["_op_"].toString(); + if (op == QLatin1String("eq")) { + m_matchFunction = &PropertyMatcher::equals; + } else if (op == QLatin1String("ne")) { + m_matchFunction = &PropertyMatcher::notEquals; + } else if (op == QLatin1String("in")) { + m_matchFunction = &PropertyMatcher::containedIn; + ok = m_searchValue.canConvert(QVariant::StringList); + if (ok) + m_searchList = m_searchValue.toStringList(); + } else if (op == QLatin1String("gt")) { + m_matchFunction = &PropertyMatcher::greaterThan; + ok = m_searchValue.canConvert(QVariant::Double); + if (ok) + m_searchDouble = m_searchValue.toDouble(); + } else if (op == QLatin1String("lt")) { + m_matchFunction = &PropertyMatcher::lessThan; + ok = m_searchValue.canConvert(QVariant::Double); + if (ok) + m_searchDouble = m_searchValue.toDouble(); + } else { + ok = false; + } + return; + } else if (searchValue.type() == QVariant::RegExp) { + m_searchRegExp = searchValue.toRegExp(); + m_matchFunction = &PropertyMatcher::regExpMatch; + ok = m_searchRegExp.isValid(); + return; + } + + m_matchFunction = &PropertyMatcher::equals; + m_searchValue = searchValue; +} + +bool PropertyMatcher::match(const QVariant &value) const +{ + return (const_cast<const PropertyMatcher*>(this)->*m_matchFunction)(value); +} + +bool PropertyMatcher::equals(const QVariant &value) const +{ + return value == m_searchValue; +} + +bool PropertyMatcher::notEquals(const QVariant &value) const +{ + return value != m_searchValue; +} + +bool PropertyMatcher::containedIn(const QVariant &value) const +{ + return m_searchList.contains(value.toString()); +} + +bool PropertyMatcher::greaterThan(const QVariant &value) const +{ + return value.toDouble() > m_searchDouble; +} + +bool PropertyMatcher::lessThan(const QVariant &value) const +{ + return value.toDouble() < m_searchDouble; +} + +bool PropertyMatcher::regExpMatch(const QVariant &value) const +{ + return m_searchRegExp.exactMatch(value.toString()); +} |