diff options
author | Frederik Gladhorn <frederik.gladhorn@nokia.com> | 2012-05-10 23:52:05 +0200 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-08-14 01:40:06 +0200 |
commit | 8e1ff45e74f529c7f49688242ea0fd25ce2913f3 (patch) | |
tree | ae13e233a4b9da58d2f4cf5ae0b733e86fe0d5a9 /tests | |
parent | 85b624a969c2674f726b6e80af1418bdd9f91969 (diff) |
Add Linux Accessibility Bridge
This is a plugin that bridges the QAccessible world
to AT-SPI 2 on Linux.
Change-Id: I7af22621ee6a3cefc723b137b7f227a611cf6641
Reviewed-by: Jan-Arve Sæther <jan-arve.saether@nokia.com>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/other/other.pro | 5 | ||||
-rw-r--r-- | tests/auto/other/qaccessibilitylinux/qaccessibilitylinux.pro | 12 | ||||
-rw-r--r-- | tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp | 467 |
3 files changed, 483 insertions, 1 deletions
diff --git a/tests/auto/other/other.pro b/tests/auto/other/other.pro index b172bbf8dd..0d96c7d0cd 100644 --- a/tests/auto/other/other.pro +++ b/tests/auto/other/other.pro @@ -16,6 +16,7 @@ SUBDIRS=\ modeltest \ networkselftest \ qaccessibility \ + qaccessibilitylinux \ qcomplextext \ qfocusevent \ qnetworkaccessmanager_and_qprogressdialog \ @@ -45,7 +46,9 @@ cross_compile: SUBDIRS -= \ atwrapper \ compiler -wince*|!contains(QT_CONFIG, accessibility):SUBDIRS -= qaccessibility +wince*|!contains(QT_CONFIG, accessibility): SUBDIRS -= qaccessibility + +!contains(QT_CONFIG, accessibility)|!contains(QT_CONFIG, xcb): SUBDIRS -= qaccessibilitylinux !mac: SUBDIRS -= \ macgui \ diff --git a/tests/auto/other/qaccessibilitylinux/qaccessibilitylinux.pro b/tests/auto/other/qaccessibilitylinux/qaccessibilitylinux.pro new file mode 100644 index 0000000000..db7001eded --- /dev/null +++ b/tests/auto/other/qaccessibilitylinux/qaccessibilitylinux.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +TARGET = tst_qaccessibilitylinux +SOURCES += tst_qaccessibilitylinux.cpp \ + ../../../../src/platformsupport/linuxaccessibility/dbusconnection.cpp \ + ../../../../src/platformsupport/linuxaccessibility/struct_marshallers.cpp + +CONFIG += gui +CONFIG += link_pkgconfig + +QT += gui widgets dbus testlib + +PKGCONFIG += atspi-2 diff --git a/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp b/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp new file mode 100644 index 0000000000..8cf9354e0c --- /dev/null +++ b/tests/auto/other/qaccessibilitylinux/tst_qaccessibilitylinux.cpp @@ -0,0 +1,467 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $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 <QtTest/QtTest> +#include <QtGui> +#include <QtWidgets/QHBoxLayout> +#include <QtWidgets/QLabel> +#include <QtWidgets/QLineEdit> +#include <QtWidgets/QListWidget> +#include <QtWidgets/QTreeWidget> +#include <QtWidgets/QTextEdit> + +#include <QDBusArgument> +#include <QDBusConnection> +#include <QDBusMessage> +#include <QDBusInterface> +#include <QDBusReply> + +#include <atspi/atspi-constants.h> + +#include "../../../../src/plugins/platforms/linuxaccessibility/dbusconnection.h" +#include "../../../../src/plugins/platforms/linuxaccessibility/struct_marshallers.h" + +#define COMPARE3(v1, v2, v3) QCOMPARE(v1, v3); QCOMPARE(v2, v3); + +class AccessibleTestWindow : public QWidget +{ + Q_OBJECT +public: + AccessibleTestWindow() + { + DBusConnection c; + m_address = c.connection().baseService().toLatin1().data(); + new QHBoxLayout(this); + } + QString dbusAddress() const { return m_address; } + + void addWidget(QWidget* widget) + { + layout()->addWidget(widget); + widget->show(); + QTest::qWaitForWindowShown(widget); + } + + void clearChildren() + { + qDeleteAll(children()); + new QHBoxLayout(this); + } + +private: + QString m_address; + QString m_bus; +}; + + +class tst_QtAtSpi : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void testLabel(); + void testLineEdit(); + void testListWidget(); + void testTreeWidget(); + void testTextEdit(); + void testSlider(); + + void cleanupTestCase(); + +private: + void registerDbus(); + static QString getParent(QDBusInterface *interface); + static QStringList getChildren(QDBusInterface *interface); + QDBusInterface *getInterface(const QString &path, const QString &interfaceName); + + AccessibleTestWindow *m_window; + + QString bus; + QString address; + QDBusInterface *root; // the root object on dbus (for the app) + QDBusInterface *rootApplication; + QDBusInterface *mainWindow; + + DBusConnection dbus; +}; + +// helper to find children of a dbus object +QStringList tst_QtAtSpi::getChildren(QDBusInterface *interface) +{ + QSpiObjectReferenceArray list; + interface->call(QDBus::Block, "GetChildren").arguments().first().value<QDBusArgument>() >> list; + + Q_ASSERT(interface->property("ChildCount").toInt() == list.count()); + + QStringList children; + Q_FOREACH (const QSpiObjectReference &ref, list) + children << ref.path.path(); + + return children; +} + +QString tst_QtAtSpi::getParent(QDBusInterface *interface) +{ + if (!interface->isValid()) + return QString(); + + QVariant var = interface->property("Parent"); + if (!var.canConvert<QSpiObjectReference>()) { + qWarning() << "Invalid parent"; + return QString(); + } + QSpiObjectReference parent = var.value<QSpiObjectReference>(); + return parent.path.path(); +} + +// helper to get dbus object +QDBusInterface *tst_QtAtSpi::getInterface(const QString &path, const QString &interfaceName) +{ + return new QDBusInterface(address, path, interfaceName, dbus.connection(), this); +} + + +void tst_QtAtSpi::initTestCase() +{ + // Oxygen style creates many extra items, it's simply unusable here + qDebug() << "Using plastique style..."; + qApp->setStyle("plastique"); + qApp->setApplicationName("tst_QtAtSpi app"); + dbus = DBusConnection(); + + m_window = new AccessibleTestWindow(); + m_window->show(); + + // this has the side-effect of immediately activating accessibility + qDebug() << "Explicitly activating accessibility..."; + delete QAccessible::queryAccessibleInterface(m_window); + + QTest::qWaitForWindowShown(m_window); + + address = m_window->dbusAddress(); + registerDbus(); + + QStringList appChildren = getChildren(root); + QString window = appChildren.at(0); + mainWindow = getInterface(window, "org.a11y.atspi.Accessible"); +} + +void tst_QtAtSpi::cleanupTestCase() +{ + delete mainWindow; + delete rootApplication; + delete root; + delete m_window; +} + +void tst_QtAtSpi::registerDbus() +{ + QVERIFY(dbus.connection().isConnected()); + + root = getInterface("/org/a11y/atspi/accessible/root", + "org.a11y.atspi.Accessible"); + + rootApplication = getInterface("/org/a11y/atspi/accessible/root", + "org.a11y.atspi.Application"); + QVERIFY(root->isValid()); + QVERIFY(rootApplication->isValid()); + + QStringList appChildren = getChildren(root); + QString window = appChildren.at(0); + mainWindow = getInterface(window, "org.a11y.atspi.Accessible"); +} + +#define ROOTPATH "/org/a11y/atspi/accessible" + +void tst_QtAtSpi::testLabel() +{ + QLabel *l = new QLabel(m_window); + l->setText("Hello A11y"); + m_window->addWidget(l); + + // Application + QCOMPARE(getParent(mainWindow), QLatin1String(ATSPI_DBUS_PATH_ROOT)); + QStringList children = getChildren(mainWindow); + + QDBusInterface *labelInterface = getInterface(children.at(0), "org.a11y.atspi.Accessible"); + QVERIFY(labelInterface->isValid()); + QCOMPARE(labelInterface->property("Name").toString(), QLatin1String("Hello A11y")); + QCOMPARE(getChildren(labelInterface).count(), 0); + QCOMPARE(labelInterface->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("label")); + QCOMPARE(labelInterface->call(QDBus::Block, "GetRole").arguments().first().toUInt(), 29u); + QCOMPARE(getParent(labelInterface), mainWindow->path()); + + l->setText("New text"); + QCOMPARE(labelInterface->property("Name").toString(), l->text()); + + m_window->clearChildren(); + delete labelInterface; +} + +void tst_QtAtSpi::testLineEdit() +{ + QLineEdit *lineEdit = new QLineEdit(m_window); + lineEdit->setText("a11y test QLineEdit"); + m_window->addWidget(lineEdit); + + QStringList children = getChildren(mainWindow); + + QDBusInterface *accessibleInterface = getInterface(children.at(0), "org.a11y.atspi.Accessible"); + QDBusInterface *editableTextInterface = getInterface(children.at(0), "org.a11y.atspi.EditableText"); + QDBusInterface *textInterface = getInterface(children.at(0), "org.a11y.atspi.Text"); + QVERIFY(accessibleInterface->isValid()); + QVERIFY(editableTextInterface->isValid()); + QVERIFY(textInterface->isValid()); + + QCOMPARE(accessibleInterface->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("text")); + QCOMPARE(textInterface->call(QDBus::Block,"GetText", 5, -1).arguments().first().toString(), QLatin1String("test QLineEdit")); + QString newText = "Text has changed!"; + editableTextInterface->call(QDBus::Block, "SetTextContents", newText); + COMPARE3(lineEdit->text(), textInterface->call(QDBus::Block, "GetText", 0, -1).arguments().first().toString(), newText); + QCOMPARE(textInterface->call(QDBus::Block, "GetText", 0, 4).arguments().first().toString(), QLatin1String("Text")); + editableTextInterface->call(QDBus::Block, "DeleteText", 4, 8); + COMPARE3(lineEdit->text(), "Te" + textInterface->call(QDBus::Block, "GetText", 2, 10).arguments().first().toString() + "ed!", QLatin1String("Text changed!")); + editableTextInterface->call(QDBus::Block, "InsertText", 12, " again ", 6); + QCOMPARE(lineEdit->text(), QLatin1String("Text changed again!")); + COMPARE3(lineEdit->text().length(), textInterface->property("CharacterCount").toInt(), 19); + + textInterface->call(QDBus::Block, "SetCaretOffset", 4); + COMPARE3(lineEdit->cursorPosition(), textInterface->property("CaretOffset").toInt(), 4); + + textInterface->call(QDBus::Block, "AddSelection", 1, 4); + QList<QVariant> data = textInterface->call(QDBus::Block, "GetSelection", 0).arguments(); + COMPARE3(data.at(0).toInt(), lineEdit->selectionStart(), 1); + QCOMPARE(data.at(1).toInt(), 4); + QCOMPARE(lineEdit->selectedText().length(), 3); + QCOMPARE(textInterface->call(QDBus::Block, "GetNSelections").arguments().first().toInt(), 1); + textInterface->call(QDBus::Block, "SetSelection", 0, 0, 5); + data = textInterface->call(QDBus::Block, "GetSelection", 0).arguments(); + COMPARE3(data.at(0).toInt(), lineEdit->selectionStart(), 0); + COMPARE3(data.at(1).toInt(), lineEdit->selectedText().length(), 5); + textInterface->call(QDBus::Block, "RemoveSelection", 0); + QCOMPARE(lineEdit->selectionStart(), -1); + QCOMPARE(textInterface->call(QDBus::Block, "GetNSelections").arguments().first().toInt(), 0); + + m_window->clearChildren(); + delete accessibleInterface; + delete textInterface; + delete editableTextInterface; +} + +void tst_QtAtSpi::testListWidget() +{ + QListWidget *lw = new QListWidget; + lw->addItem("Hello"); + lw->addItem("Good morning"); + lw->addItem("Good bye"); + m_window->addWidget(lw); + + QStringList children = getChildren(mainWindow); + QDBusInterface *listIface = getInterface(children.at(0), "org.a11y.atspi.Accessible"); + QCOMPARE(listIface->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("list")); + QStringList tableChildren = getChildren(listIface); + QCOMPARE(tableChildren.size(), 3); + + QDBusInterface *cell1 = getInterface(tableChildren.at(0), "org.a11y.atspi.Accessible"); + QCOMPARE(cell1->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("list item")); + QCOMPARE(cell1->property("Name").toString(), QLatin1String("Hello")); + + QDBusInterface *cell2 = getInterface(tableChildren.at(1), "org.a11y.atspi.Accessible"); + QCOMPARE(cell2->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("list item")); + QCOMPARE(cell2->property("Name").toString(), QLatin1String("Good morning")); + + QDBusInterface *cell3 = getInterface(tableChildren.at(2), "org.a11y.atspi.Accessible"); + QCOMPARE(cell3->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("list item")); + QCOMPARE(cell3->property("Name").toString(), QLatin1String("Good bye")); + + delete cell1; delete cell2; delete cell3; + m_window->clearChildren(); + delete listIface; +} + +void tst_QtAtSpi::testTreeWidget() +{ + QTreeWidget *tree = new QTreeWidget; + tree->setColumnCount(2); + tree->setHeaderLabels(QStringList() << "Header 1" << "Header 2"); + + QTreeWidgetItem *top1 = new QTreeWidgetItem(QStringList() << "0.0" << "0.1"); + tree->addTopLevelItem(top1); + + QTreeWidgetItem *top2 = new QTreeWidgetItem(QStringList() << "1.0" << "1.1"); + tree->addTopLevelItem(top2); + + QTreeWidgetItem *child1 = new QTreeWidgetItem(QStringList() << "1.0 0.0" << "1.0 0.1"); + top2->addChild(child1); + + m_window->addWidget(tree); + + QStringList children = getChildren(mainWindow); + QDBusInterface *treeIface = getInterface(children.at(0), "org.a11y.atspi.Accessible"); + QCOMPARE(treeIface->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("tree")); + QStringList tableChildren = getChildren(treeIface); + + QCOMPARE(tableChildren.size(), 6); + + QDBusInterface *cell1 = getInterface(tableChildren.at(0), "org.a11y.atspi.Accessible"); + QCOMPARE(cell1->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("column header")); + QCOMPARE(cell1->property("Name").toString(), QLatin1String("Header 1")); + + QDBusInterface *cell2 = getInterface(tableChildren.at(1), "org.a11y.atspi.Accessible"); + QCOMPARE(cell2->call(QDBus::Block, "GetRoleName").arguments().first().toString(), QLatin1String("column header")); + QCOMPARE(cell2->property("Name").toString(), QLatin1String("Header 2")); + + QDBusInterface *cell3 = getInterface(tableChildren.at(2), "org.a11y.atspi.Accessible"); + QCOMPARE(cell3->property("Name").toString(), QLatin1String("0.0")); + + QDBusInterface *cell4 = getInterface(tableChildren.at(3), "org.a11y.atspi.Accessible"); + QCOMPARE(cell4->property("Name").toString(), QLatin1String("0.1")); + + tree->expandItem(top2); + tableChildren = getChildren(treeIface); + QCOMPARE(tableChildren.size(), 8); + + QDBusInterface *cell5 = getInterface(tableChildren.at(6), "org.a11y.atspi.Accessible"); + QCOMPARE(cell5->property("Name").toString(), QLatin1String("1.0 0.0")); + + QDBusInterface *cell6 = getInterface(tableChildren.at(7), "org.a11y.atspi.Accessible"); + QCOMPARE(cell6->property("Name").toString(), QLatin1String("1.0 0.1")); + + + QDBusInterface *treeTableIface = getInterface(children.at(0), "org.a11y.atspi.Table"); + + QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 0).arguments().first().toInt(), -1); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 1).arguments().first().toInt(), -1); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 2).arguments().first().toInt(), 0); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 3).arguments().first().toInt(), 0); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 4).arguments().first().toInt(), 1); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 5).arguments().first().toInt(), 1); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 6).arguments().first().toInt(), 2); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetRowAtIndex", 7).arguments().first().toInt(), 2); + + QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 0).arguments().first().toInt(), 0); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 1).arguments().first().toInt(), 1); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 2).arguments().first().toInt(), 0); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 3).arguments().first().toInt(), 1); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 4).arguments().first().toInt(), 0); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 5).arguments().first().toInt(), 1); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 6).arguments().first().toInt(), 0); + QCOMPARE(treeTableIface->call(QDBus::Block, "GetColumnAtIndex", 7).arguments().first().toInt(), 1); + + delete treeTableIface; + delete cell1; delete cell2; delete cell3; delete cell4; delete cell5; delete cell6; + m_window->clearChildren(); + delete treeIface; +} + +void tst_QtAtSpi::testTextEdit() +{ + QTextEdit *textEdit = new QTextEdit(m_window); + textEdit->setText("<html><head></head><body>This is a <b>sample</b> text.<br />" + "How are you today</body></html>"); + textEdit->show(); + m_window->addWidget(textEdit); + + QStringList children = getChildren(mainWindow); + QDBusInterface *accessibleInterface = getInterface(children.at(0), "org.a11y.atspi.Accessible"); + QDBusInterface *editableTextInterface = getInterface(children.at(0), "org.a11y.atspi.EditableText"); + QDBusInterface *textInterface = getInterface(children.at(0), "org.a11y.atspi.Text"); + QVERIFY(accessibleInterface->isValid()); + QVERIFY(editableTextInterface->isValid()); + QVERIFY(textInterface->isValid()); + + QList<QVariant> callResult; + + QDBusMessage msg = textInterface->call(QDBus::Block, "GetText", 0, 5); + callResult = msg.arguments(); + QCOMPARE(callResult.at(0).toString(), QLatin1String("This ")); + + msg = textInterface->call(QDBus::Block, "GetTextAtOffset", 12, (uint) ATSPI_TEXT_BOUNDARY_WORD_START); + callResult = msg.arguments(); + + QEXPECT_FAIL("", "Word should contain space at end according to atspi.", Continue); + QCOMPARE(callResult.at(0).toString(), QLatin1String("sample ")); + QCOMPARE(callResult.at(1).toInt(), 10); + QEXPECT_FAIL("", "Due to missing space the count is off by one.", Continue); + QCOMPARE(callResult.at(2).toInt(), 17); + + // Check if at least CharacterExtents and RangeExtents give a consistent result + QDBusReply<QRect> replyRect20 = textInterface->call(QDBus::Block, "GetCharacterExtents", 20, ATSPI_COORD_TYPE_SCREEN); + QVERIFY(replyRect20.isValid()); + QRect r1 = replyRect20.value(); + QDBusReply<QRect> replyRect21 = textInterface->call(QDBus::Block, "GetCharacterExtents", 21, ATSPI_COORD_TYPE_SCREEN); + QRect r2 = replyRect21.value(); + QDBusReply<QRect> reply = textInterface->call(QDBus::Block, "GetRangeExtents", 20, 21, ATSPI_COORD_TYPE_SCREEN); + QRect rect = reply.value(); + QCOMPARE(rect, r1|r2); + + m_window->clearChildren(); + delete textInterface; +} + +void tst_QtAtSpi::testSlider() +{ + QSlider *slider = new QSlider(m_window); + slider->setMinimum(2); + slider->setMaximum(5); + slider->setValue(3); + m_window->addWidget(slider); + + QStringList children = getChildren(mainWindow); + + QDBusInterface *accessibleInterface = getInterface(children.at(0), "org.a11y.atspi.Accessible"); + QDBusInterface *valueInterface = getInterface(children.at(0), "org.a11y.atspi.Value"); + QVERIFY(accessibleInterface->isValid()); + QVERIFY(valueInterface->isValid()); + + QCOMPARE(valueInterface->property("CurrentValue").toInt(), 3); + QCOMPARE(valueInterface->property("MinimumValue").toInt(), 2); + QCOMPARE(valueInterface->property("MaximumValue").toInt(), 5); + + valueInterface->setProperty("CurrentValue", 4); + QCOMPARE(valueInterface->property("CurrentValue").toInt(), 4); +} + +QTEST_MAIN(tst_QtAtSpi) +#include "tst_qaccessibilitylinux.moc" + |