From 9f1aa866bda7678261f2f441d4cfd5bb524c2411 Mon Sep 17 00:00:00 2001 From: Jo Asplin Date: Thu, 20 Oct 2011 13:17:26 +0200 Subject: Moved tests into integrationtests/ and widgets/ Task-number: QTBUG-19013 Change-Id: Ibb776f5967c0645ce6d22ef7afdc40657c575461 Reviewed-by: Holger Ihrig --- .../qaccessibility/tst_qaccessibility.cpp | 3372 ++++++++++++++++++++ 1 file changed, 3372 insertions(+) create mode 100644 tests/auto/integrationtests/qaccessibility/tst_qaccessibility.cpp (limited to 'tests/auto/integrationtests/qaccessibility/tst_qaccessibility.cpp') diff --git a/tests/auto/integrationtests/qaccessibility/tst_qaccessibility.cpp b/tests/auto/integrationtests/qaccessibility/tst_qaccessibility.cpp new file mode 100644 index 0000000000..e1bd968534 --- /dev/null +++ b/tests/auto/integrationtests/qaccessibility/tst_qaccessibility.cpp @@ -0,0 +1,3372 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +#include +#include +#include + +#if defined(Q_OS_WIN) && defined(interface) +# undef interface +#endif + + +#include "QtTest/qtestaccessible.h" + +#if defined(Q_OS_WINCE) +extern "C" bool SystemParametersInfo(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni); +#define SPI_GETPLATFORMTYPE 257 +inline bool IsValidCEPlatform() { + wchar_t tszPlatform[64]; + if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(tszPlatform) / sizeof(*tszPlatform), tszPlatform, 0)) { + QString platform = QString::fromWCharArray(tszPlatform); + if ((platform == QLatin1String("PocketPC")) || (platform == QLatin1String("Smartphone"))) + return false; + } + return true; +} +#endif + +static inline bool verifyChild(QWidget *child, QAccessibleInterface *interface, + int index, const QRect &domain) +{ + if (!child) { + qWarning("tst_QAccessibility::verifyChild: null pointer to child."); + return false; + } + + if (!interface) { + qWarning("tst_QAccessibility::verifyChild: null pointer to interface."); + return false; + } + + // Verify that we get a valid QAccessibleInterface for the child. + QAccessibleInterface *childInterface = QAccessible::queryAccessibleInterface(child); + if (!childInterface) { + qWarning("tst_QAccessibility::verifyChild: Failed to retrieve interface for child."); + return false; + } + + // QAccessibleInterface::indexOfChild(): + // Verify that indexOfChild() returns an index equal to the index passed in + int indexFromIndexOfChild = interface->indexOfChild(childInterface); + delete childInterface; + if (indexFromIndexOfChild != index) { + qWarning("tst_QAccessibility::verifyChild (indexOfChild()):"); + qWarning() << "Expected:" << index; + qWarning() << "Actual: " << indexFromIndexOfChild; + return false; + } + + // Navigate to child, compare its object and role with the interface from queryAccessibleInterface(child). + QAccessibleInterface *navigatedChildInterface = interface->child(index - 1); + if (navigatedChildInterface == 0) + return false; + + const QRect rectFromInterface = navigatedChildInterface->rect(); + delete navigatedChildInterface; + + // QAccessibleInterface::childAt(): + // Calculate global child position and check that the interface + // returns the correct index for that position. + QPoint globalChildPos = child->mapToGlobal(QPoint(0, 0)); + int indexFromChildAt = interface->childAt(globalChildPos.x(), globalChildPos.y()); + if (indexFromChildAt != index) { + qWarning("tst_QAccessibility::verifyChild (childAt()):"); + qWarning() << "Expected:" << index; + qWarning() << "Actual: " << indexFromChildAt; + return false; + } + + // QAccessibleInterface::rect(): + // Calculate global child geometry and check that the interface + // returns a QRect which is equal to the calculated QRect. + const QRect expectedGlobalRect = QRect(globalChildPos, child->size()); + if (expectedGlobalRect != rectFromInterface) { + qWarning("tst_QAccessibility::verifyChild (rect()):"); + qWarning() << "Expected:" << expectedGlobalRect; + qWarning() << "Actual: " << rectFromInterface; + return false; + } + + // Verify that the child is within its domain. + if (!domain.contains(rectFromInterface)) { + qWarning("tst_QAccessibility::verifyChild: Child is not within its domain."); + return false; + } + + return true; +} + +static inline int indexOfChild(QAccessibleInterface *parentInterface, QWidget *childWidget) +{ + if (!parentInterface || !childWidget) + return -1; + QAccessibleInterface *childInterface = QAccessibleInterface::queryAccessibleInterface(childWidget); + if (!childInterface) + return -1; + int index = parentInterface->indexOfChild(childInterface); + delete childInterface; + return index; +} + +#define EXPECT(cond) \ + do { \ + if (!errorAt && !(cond)) { \ + errorAt = __LINE__; \ + qWarning("level: %d, middle: %d, role: %d (%s)", treelevel, middle, iface->role(), #cond); \ + } \ + } while (0) + +static int verifyHierarchy(QAccessibleInterface *iface) +{ + int errorAt = 0; + static int treelevel = 0; // for error diagnostics + QAccessibleInterface *middleChild, *if2; + middleChild = 0; + ++treelevel; + int middle = iface->childCount()/2 + 1; + if (iface->childCount() >= 2) { + middleChild = iface->child(middle - 1); + } + for (int i = 0; i < iface->childCount() && !errorAt; ++i) { + if2 = iface->child(i); + EXPECT(if2 != 0); + // navigate Ancestor... + QAccessibleInterface *parent = 0; + parent = if2->parent(); + EXPECT(iface->object() == parent->object()); + delete parent; + + // navigate Sibling... +// if (middleChild) { +// entry = if2->navigate(QAccessible::Sibling, middle, &if3); +// EXPECT(entry == 0 && if3->object() == middleChild->object()); +// if (entry == 0) +// delete if3; +// EXPECT(iface->indexOfChild(middleChild) == middle); +// } + + // verify children... + if (!errorAt) + errorAt = verifyHierarchy(if2); + delete if2; + } + delete middleChild; + + --treelevel; + return errorAt; +} + + +//TESTED_FILES= + +class tst_QAccessibility : public QObject +{ + Q_OBJECT +public: + tst_QAccessibility(); + virtual ~tst_QAccessibility(); + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); +private slots: + void eventTest(); + void customWidget(); + void deletedWidget(); + + void navigateGeometric(); + void navigateHierarchy(); + void sliderTest(); + void navigateCovered(); + void textAttributes(); + void hideShowTest(); + + void actionTest(); + + void applicationTest(); + void mainWindowTest(); + void buttonTest(); + void scrollBarTest(); + void tabTest(); + void tabWidgetTest(); + void menuTest(); + void spinBoxTest(); + void doubleSpinBoxTest(); + void textEditTest(); + void textBrowserTest(); + void mdiAreaTest(); + void mdiSubWindowTest(); + void lineEditTest(); + void workspaceTest(); + void dialogButtonBoxTest(); + void dialTest(); + void rubberBandTest(); + void abstractScrollAreaTest(); + void scrollAreaTest(); + + // Accessible table1 interface is no longer supported on X11, + // where it has been replaced by table2 interface. +#ifndef Q_OS_UNIX + void listViewTest(); + void treeWidgetTest(); + void tableWidgetTest(); + void tableViewTest(); +#else + void table2ListTest(); + void table2TreeTest(); + void table2TableTest(); +#endif + + void calendarWidgetTest(); + void dockWidgetTest(); + void comboBoxTest(); + void accessibleName(); + void labelTest(); + void accelerators(); +}; + +const double Q_PI = 3.14159265358979323846; + +QString eventName(const int ev) +{ + switch(ev) { + case 0x0001: return "SoundPlayed"; + case 0x0002: return "Alert"; + case 0x0003: return "ForegroundChanged"; + case 0x0004: return "MenuStart"; + case 0x0005: return "MenuEnd"; + case 0x0006: return "PopupMenuStart"; + case 0x0007: return "PopupMenuEnd"; + case 0x000C: return "ContextHelpStart"; + case 0x000D: return "ContextHelpEnd"; + case 0x000E: return "DragDropStart"; + case 0x000F: return "DragDropEnd"; + case 0x0010: return "DialogStart"; + case 0x0011: return "DialogEnd"; + case 0x0012: return "ScrollingStart"; + case 0x0013: return "ScrollingEnd"; + case 0x0018: return "MenuCommand"; + + case 0x0116: return "TableModelChanged"; + case 0x011B: return "TextCaretMoved"; + + case 0x8000: return "ObjectCreated"; + case 0x8001: return "ObjectDestroyed"; + case 0x8002: return "ObjectShow"; + case 0x8003: return "ObjectHide"; + case 0x8004: return "ObjectReorder"; + case 0x8005: return "Focus"; + case 0x8006: return "Selection"; + case 0x8007: return "SelectionAdd"; + case 0x8008: return "SelectionRemove"; + case 0x8009: return "SelectionWithin"; + case 0x800A: return "StateChanged"; + case 0x800B: return "LocationChanged"; + case 0x800C: return "NameChanged"; + case 0x800D: return "DescriptionChanged"; + case 0x800E: return "ValueChanged"; + case 0x800F: return "ParentChanged"; + case 0x80A0: return "HelpChanged"; + case 0x80B0: return "DefaultActionChanged"; + case 0x80C0: return "AcceleratorChanged"; + default: return "Unknown Event"; + } +} + +QAccessible::State state(QWidget * const widget) +{ + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(widget); + if (!iface) + qWarning() << "Cannot get QAccessibleInterface for widget"; + QAccessible::State state = (iface ? iface->state() : static_cast(0)); + delete iface; + return state; +} + +class QtTestAccessibleWidget: public QWidget +{ + Q_OBJECT +public: + QtTestAccessibleWidget(QWidget *parent, const char *name): QWidget(parent) + { + setObjectName(name); + QPalette pal; + pal.setColor(backgroundRole(), Qt::black);//black is beautiful + setPalette(pal); + setFixedSize(5, 5); + } +}; + +class QtTestAccessibleWidgetIface: public QAccessibleWidget +{ +public: + QtTestAccessibleWidgetIface(QtTestAccessibleWidget *w): QAccessibleWidget(w) {} + QString text(Text t, int control = 0) const + { + if (t == Help) + return QString::fromLatin1("Help yourself"); + return QAccessibleWidget::text(t, control); + } + static QAccessibleInterface *ifaceFactory(const QString &key, QObject *o) + { + if (key == "QtTestAccessibleWidget") + return new QtTestAccessibleWidgetIface(static_cast(o)); + return 0; + } +}; + +tst_QAccessibility::tst_QAccessibility() +{ +} + +tst_QAccessibility::~tst_QAccessibility() +{ +} + +void tst_QAccessibility::initTestCase() +{ + QTestAccessibility::initialize(); + QAccessible::installFactory(QtTestAccessibleWidgetIface::ifaceFactory); +} + +void tst_QAccessibility::cleanupTestCase() +{ + QTestAccessibility::cleanup(); +} + +void tst_QAccessibility::init() +{ + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::cleanup() +{ + const EventList list = QTestAccessibility::events(); + if (!list.isEmpty()) { + qWarning("%d accessibility event(s) were not handled in testfunction '%s':", list.count(), + QString(QTest::currentTestFunction()).toAscii().constData()); + for (int i = 0; i < list.count(); ++i) + qWarning(" %d: Object: %p Event: '%s' (%d) Child: %d", i + 1, list.at(i).object, + eventName(list.at(i).event).toAscii().constData(), list.at(i).event, list.at(i).child); + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::eventTest() +{ + QPushButton* button = new QPushButton(0); + button->setObjectName(QString("Olaf")); + + button->show(); + QVERIFY_EVENT(button, 0, QAccessible::ObjectShow); + button->setFocus(Qt::MouseFocusReason); + QTestAccessibility::clearEvents(); + QTest::mouseClick(button, Qt::LeftButton, 0); + QVERIFY_EVENT(button, 0, QAccessible::StateChanged); + QVERIFY_EVENT(button, 0, QAccessible::StateChanged); + + button->setAccessibleName("Olaf the second"); + QVERIFY_EVENT(button, 0, QAccessible::NameChanged); + button->setAccessibleDescription("This is a button labeled Olaf"); + QVERIFY_EVENT(button, 0, QAccessible::DescriptionChanged); + + button->hide(); + QVERIFY_EVENT(button, 0, QAccessible::ObjectHide); + + delete button; +} + +void tst_QAccessibility::customWidget() +{ + QtTestAccessibleWidget* widget = new QtTestAccessibleWidget(0, "Heinz"); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(widget); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + QCOMPARE(iface->object(), (QObject*)widget); + QCOMPARE(iface->object()->objectName(), QString("Heinz")); + QCOMPARE(iface->text(QAccessible::Help, 0), QString("Help yourself")); + + delete iface; + delete widget; +} + +void tst_QAccessibility::deletedWidget() +{ + QtTestAccessibleWidget *widget = new QtTestAccessibleWidget(0, "Ralf"); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(widget); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + QCOMPARE(iface->object(), (QObject*)widget); + + delete widget; + widget = 0; + QVERIFY(!iface->isValid()); + delete iface; +} + +void tst_QAccessibility::navigateGeometric() +{ + { + static const int skip = 20; //speed the test up significantly + static const double step = Q_PI / 180; + QWidget *w = new QWidget(0); + w->setObjectName(QString("Josef")); + w->setFixedSize(400, 400); + + // center widget + QtTestAccessibleWidget *center = new QtTestAccessibleWidget(w, "Sol"); + center->move(200, 200); + + // arrange 360 widgets around it in a circle + QtTestAccessibleWidget *aw = 0; + for (int i = 0; i < 360; i += skip) { + aw = new QtTestAccessibleWidget(w, QString::number(i).toLatin1()); + aw->move( int(200.0 + 100.0 * sin(step * (double)i)), int(200.0 + 100.0 * cos(step * (double)i)) ); + } + + aw = new QtTestAccessibleWidget(w, "Earth"); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(center); + QAccessibleInterface *target = 0; + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + + w->show(); + QCoreApplication::processEvents(); + QTest::qWait(100); + + // let one widget rotate around center + for (int i = 0; i < 360; i+=skip) { + aw->move( int(200.0 + 75.0 * sin(step * (double)i)), int(200.0 + 75.0 * cos(step * (double)i)) ); + + if (i < 45 || i > 315) { + QCOMPARE(iface->navigate(QAccessible::Down, 0, &target), 0); + } else if ( i < 135 ) { + QCOMPARE(iface->navigate(QAccessible::Right, 0, &target), 0); + } else if ( i < 225 ) { + QCOMPARE(iface->navigate(QAccessible::Up, 0, &target), 0); + } else { + QCOMPARE(iface->navigate(QAccessible::Left, 0, &target), 0); + } + + QVERIFY(target); + QVERIFY(target->isValid()); + QVERIFY(target->object()); + QCOMPARE(target->object()->objectName(), aw->objectName()); + delete target; target = 0; + } + + // test invisible widget + target = QAccessible::queryAccessibleInterface(aw); + QVERIFY(!(target->state() & QAccessible::Invisible)); + aw->hide(); + QVERIFY(target->state() & QAccessible::Invisible); + delete target; target = 0; + + aw->move(center->x() + 10, center->y()); + QCOMPARE(iface->navigate(QAccessible::Right, 0, &target), 0); + QVERIFY(target); + QVERIFY(target->isValid()); + QVERIFY(target->object()); + QVERIFY(QString(target->object()->objectName()) != "Earth"); + delete target; target = 0; + + aw->move(center->x() - 10, center->y()); + QCOMPARE(iface->navigate(QAccessible::Left, 0, &target), 0); + QVERIFY(target); + QVERIFY(target->isValid()); + QVERIFY(target->object()); + QVERIFY(QString(target->object()->objectName()) != "Earth"); + delete target; target = 0; + + aw->move(center->x(), center->y() + 10); + QCOMPARE(iface->navigate(QAccessible::Down, 0, &target), 0); + QVERIFY(target); + QVERIFY(target->isValid()); + QVERIFY(target->object()); + QVERIFY(QString(target->object()->objectName()) != "Earth"); + delete target; target = 0; + + aw->move(center->x(), center->y() - 10); + QCOMPARE(iface->navigate(QAccessible::Up, 0, &target), 0); + QVERIFY(target); + QVERIFY(target->isValid()); + QVERIFY(target->object()); + QVERIFY(QString(target->object()->objectName()) != "Earth"); + delete target; target = 0; + + delete iface; + delete w; + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::sliderTest() +{ + { + QSlider *slider = new QSlider(0); + slider->setObjectName(QString("Slidy")); + slider->show(); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(slider); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + + QCOMPARE(iface->childCount(), 0); + QCOMPARE(iface->role(), QAccessible::Slider); + + QAccessibleValueInterface *valueIface = iface->valueInterface(); + QVERIFY(valueIface != 0); + QCOMPARE(valueIface->minimumValue().toInt(), slider->minimum()); + QCOMPARE(valueIface->maximumValue().toInt(), slider->maximum()); + slider->setValue(50); + QCOMPARE(valueIface->currentValue().toInt(), slider->value()); + slider->setValue(0); + QCOMPARE(valueIface->currentValue().toInt(), slider->value()); + slider->setValue(100); + QCOMPARE(valueIface->currentValue().toInt(), slider->value()); + valueIface->setCurrentValue(77); + QCOMPARE(77, slider->value()); + + delete iface; + delete slider; + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::navigateCovered() +{ + { + QWidget *w = new QWidget(0); + w->setObjectName(QString("Harry")); + QWidget *w1 = new QWidget(w); + w1->setObjectName(QString("1")); + QWidget *w2 = new QWidget(w); + w2->setObjectName(QString("2")); + w->show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + + w->setFixedSize(6, 6); + w1->setFixedSize(5, 5); + w2->setFixedSize(5, 5); + w2->move(0, 0); + w1->raise(); + + QAccessibleInterface *iface1 = QAccessible::queryAccessibleInterface(w1); + QVERIFY(iface1 != 0); + QVERIFY(iface1->isValid()); + QAccessibleInterface *iface2 = QAccessible::queryAccessibleInterface(w2); + QVERIFY(iface2 != 0); + QVERIFY(iface2->isValid()); + QAccessibleInterface *iface3 = 0; + + QCOMPARE(iface1->navigate(QAccessible::Covers, -42, &iface3), -1); + QVERIFY(iface3 == 0); + QCOMPARE(iface1->navigate(QAccessible::Covers, 0, &iface3), -1); + QVERIFY(iface3 == 0); + QCOMPARE(iface1->navigate(QAccessible::Covers, 2, &iface3), -1); + QVERIFY(iface3 == 0); + + for (int loop = 0; loop < 2; ++loop) { + for (int x = 0; x < w->width(); ++x) { + for (int y = 0; y < w->height(); ++y) { + w1->move(x, y); + if (w1->geometry().intersects(w2->geometry())) { + QVERIFY(iface1->relationTo(0, iface2, 0) & QAccessible::Covers); + QVERIFY(iface2->relationTo(0, iface1, 0) & QAccessible::Covered); + QCOMPARE(iface1->navigate(QAccessible::Covered, 1, &iface3), 0); + QVERIFY(iface3 != 0); + QVERIFY(iface3->isValid()); + QCOMPARE(iface3->object(), iface2->object()); + delete iface3; iface3 = 0; + QCOMPARE(iface2->navigate(QAccessible::Covers, 1, &iface3), 0); + QVERIFY(iface3 != 0); + QVERIFY(iface3->isValid()); + QCOMPARE(iface3->object(), iface1->object()); + delete iface3; iface3 = 0; + } else { + QVERIFY(!(iface1->relationTo(0, iface2, 0) & QAccessible::Covers)); + QVERIFY(!(iface2->relationTo(0, iface1, 0) & QAccessible::Covered)); + QCOMPARE(iface1->navigate(QAccessible::Covered, 1, &iface3), -1); + QVERIFY(iface3 == 0); + QCOMPARE(iface1->navigate(QAccessible::Covers, 1, &iface3), -1); + QVERIFY(iface3 == 0); + QCOMPARE(iface2->navigate(QAccessible::Covered, 1, &iface3), -1); + QVERIFY(iface3 == 0); + QCOMPARE(iface2->navigate(QAccessible::Covers, 1, &iface3), -1); + QVERIFY(iface3 == 0); + } + } + } + if (!loop) { + // switch children for second loop + w2->raise(); + QAccessibleInterface *temp = iface1; + iface1 = iface2; + iface2 = temp; + } + } + delete iface1; iface1 = 0; + delete iface2; iface2 = 0; + iface1 = QAccessible::queryAccessibleInterface(w1); + QVERIFY(iface1 != 0); + QVERIFY(iface1->isValid()); + iface2 = QAccessible::queryAccessibleInterface(w2); + QVERIFY(iface2 != 0); + QVERIFY(iface2->isValid()); + + w1->move(0,0); + w2->move(0,0); + w1->raise(); + QVERIFY(iface1->relationTo(0, iface2, 0) & QAccessible::Covers); + QVERIFY(iface2->relationTo(0, iface1, 0) & QAccessible::Covered); + QVERIFY(!(iface1->state() & QAccessible::Invisible)); + w1->hide(); + QVERIFY(iface1->state() & QAccessible::Invisible); + QVERIFY(!(iface1->relationTo(0, iface2, 0) & QAccessible::Covers)); + QVERIFY(!(iface2->relationTo(0, iface1, 0) & QAccessible::Covered)); + QCOMPARE(iface2->navigate(QAccessible::Covered, 1, &iface3), -1); + QVERIFY(iface3 == 0); + QCOMPARE(iface1->navigate(QAccessible::Covers, 1, &iface3), -1); + QVERIFY(iface3 == 0); + + delete iface1; iface1 = 0; + delete iface2; iface2 = 0; + delete w; + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::navigateHierarchy() +{ + { + QWidget *w = new QWidget(0); + w->setObjectName(QString("Hans")); + w->show(); + QWidget *w1 = new QWidget(w); + w1->setObjectName(QString("1")); + w1->show(); + QWidget *w2 = new QWidget(w); + w2->setObjectName(QString("2")); + w2->show(); + QWidget *w3 = new QWidget(w); + w3->setObjectName(QString("3")); + w3->show(); + QWidget *w31 = new QWidget(w3); + w31->setObjectName(QString("31")); + w31->show(); + + QAccessibleInterface *target = 0; + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(w); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + + QCOMPARE(iface->navigate(QAccessible::Sibling, -42, &target), -1); + QVERIFY(target == 0); + QCOMPARE(iface->navigate(QAccessible::Sibling, 42, &target), -1); + QVERIFY(target == 0); + + target = iface->child(14); + QVERIFY(target == 0); + target = iface->child(-1); + QVERIFY(target == 0); + target = iface->child(0); + QAccessibleInterface *interfaceW1 = iface->child(0); + QVERIFY(target); + QVERIFY(target->isValid()); + QCOMPARE(target->object(), (QObject*)w1); + QVERIFY(interfaceW1 != 0); + QVERIFY(interfaceW1->isValid()); + QCOMPARE(interfaceW1->object(), (QObject*)w1); + delete interfaceW1; + delete iface; iface = 0; + + QCOMPARE(target->navigate(QAccessible::Sibling, 0, &iface), -1); + QVERIFY(iface == 0); + QCOMPARE(target->navigate(QAccessible::Sibling, 42, &iface), -1); + QVERIFY(iface == 0); + QCOMPARE(target->navigate(QAccessible::Sibling, -42, &iface), -1); + QVERIFY(iface == 0); + QCOMPARE(target->navigate(QAccessible::Sibling, 2, &iface), 0); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + QCOMPARE(iface->object(), (QObject*)w2); + delete target; target = 0; + QCOMPARE(iface->navigate(QAccessible::Sibling, 3, &target), 0); + QVERIFY(target != 0); + QVERIFY(target->isValid()); + QCOMPARE(target->object(), (QObject*)w3); + delete iface; iface = 0; + + iface = target->child(0); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + QCOMPARE(iface->object(), (QObject*)w31); + delete target; target = 0; + + QCOMPARE(iface->navigate(QAccessible::Sibling, -1, &target), -1); + QVERIFY(target == 0); + QCOMPARE(iface->navigate(QAccessible::Sibling, 0, &target), -1); + QVERIFY(target == 0); + QCOMPARE(iface->navigate(QAccessible::Sibling, 1, &target), 0); + QVERIFY(target != 0); + QVERIFY(target->isValid()); + QCOMPARE(target->object(), (QObject*)w31); + delete iface; iface = 0; + + iface = target->parent(); + QVERIFY(iface != 0); + QVERIFY(iface->isValid()); + QCOMPARE(iface->object(), (QObject*)w3); + delete iface; iface = 0; + delete target; target = 0; + + delete w; + } + QTestAccessibility::clearEvents(); +} + +#define QSETCOMPARE(thetypename, elements, otherelements) \ + QCOMPARE((QSet() << elements), (QSet() << otherelements)) + +static QWidget *createWidgets() +{ + QWidget *w = new QWidget(); + + QHBoxLayout *box = new QHBoxLayout(w); + + int i = 0; + box->addWidget(new QComboBox(w)); + box->addWidget(new QPushButton(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QHeaderView(Qt::Vertical, w)); + box->addWidget(new QTreeView(w)); + box->addWidget(new QTreeWidget(w)); + box->addWidget(new QListView(w)); + box->addWidget(new QListWidget(w)); + box->addWidget(new QTableView(w)); + box->addWidget(new QTableWidget(w)); + box->addWidget(new QCalendarWidget(w)); + box->addWidget(new QDialogButtonBox(w)); + box->addWidget(new QGroupBox(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QFrame(w)); + box->addWidget(new QLineEdit(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QProgressBar(w)); + box->addWidget(new QTabWidget(w)); + box->addWidget(new QCheckBox(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QRadioButton(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QDial(w)); + box->addWidget(new QScrollBar(w)); + box->addWidget(new QSlider(w)); + box->addWidget(new QDateTimeEdit(w)); + box->addWidget(new QDoubleSpinBox(w)); + box->addWidget(new QSpinBox(w)); + box->addWidget(new QLabel(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QLCDNumber(w)); + box->addWidget(new QStackedWidget(w)); + box->addWidget(new QToolBox(w)); + box->addWidget(new QLabel(QString::fromAscii("widget text %1").arg(i++), w)); + box->addWidget(new QTextEdit(QString::fromAscii("widget text %1").arg(i++), w)); + + /* Not in the list + * QAbstractItemView, QGraphicsView, QScrollArea, + * QToolButton, QDockWidget, QFocusFrame, QMainWindow, QMenu, QMenuBar, QSizeGrip, QSplashScreen, QSplitterHandle, + * QStatusBar, QSvgWidget, QTabBar, QToolBar, QWorkspace, QSplitter + */ + return w; +} + +void tst_QAccessibility::accessibleName() +{ + QWidget *toplevel = createWidgets(); + toplevel->show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + QLayout *lout = toplevel->layout(); + for (int i = 0; i < lout->count(); i++) { + QLayoutItem *item = lout->itemAt(i); + QWidget *child = item->widget(); + + QString name = tr("Widget Name %1").arg(i); + child->setAccessibleName(name); + QAccessibleInterface *acc = QAccessible::queryAccessibleInterface(child); + QCOMPARE(acc->text(QAccessible::Name), name); + + QString desc = tr("Widget Description %1").arg(i); + child->setAccessibleDescription(desc); + QCOMPARE(acc->text(QAccessible::Description), desc); + + } + + delete toplevel; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::textAttributes() +{ + QTextEdit textEdit; + int startOffset; + int endOffset; + QString attributes; + QString text("" + "Hello, this is an example text." + "Multiple fonts are used." + "Multiple text sizes are used." + "Let's give some color to Qt." + ""); + + textEdit.setText(text); + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&textEdit); + + QAccessibleTextInterface *textInterface=interface->textInterface(); + + QVERIFY(textInterface); + QCOMPARE(textInterface->characterCount(), 112); + + attributes = textInterface->attributes(10, &startOffset, &endOffset); + QCOMPARE(startOffset, 7); + QCOMPARE(endOffset, 11); + attributes.prepend(';'); + QVERIFY(attributes.contains(QLatin1String(";font-weight:bold;"))); + + attributes = textInterface->attributes(18, &startOffset, &endOffset); + QCOMPARE(startOffset, 18); + QCOMPARE(endOffset, 25); + attributes.prepend(';'); + QVERIFY(attributes.contains(QLatin1String(";font-weight:bold;"))); + QVERIFY(attributes.contains(QLatin1String(";font-style:italic;"))); + + attributes = textInterface->attributes(34, &startOffset, &endOffset); + QCOMPARE(startOffset, 31); + QCOMPARE(endOffset, 55); + attributes.prepend(';'); + QVERIFY(attributes.contains(QLatin1String(";font-family:\"monospace\";"))); + + attributes = textInterface->attributes(65, &startOffset, &endOffset); + QCOMPARE(startOffset, 64); + QCOMPARE(endOffset, 74); + attributes.prepend(';'); + QVERIFY(attributes.contains(QLatin1String(";font-size:8pt;"))); + + attributes = textInterface->attributes(110, &startOffset, &endOffset); + QCOMPARE(startOffset, 109); + QCOMPARE(endOffset, 111); + attributes.prepend(';'); + QVERIFY(attributes.contains(QLatin1String(";background-color:rgb(20,240,30);"))); + QVERIFY(attributes.contains(QLatin1String(";color:rgb(240,241,242);"))); +} + +void tst_QAccessibility::hideShowTest() +{ + QWidget * const window = new QWidget(); + QWidget * const child = new QWidget(window); + + QVERIFY(state(window) & QAccessible::Invisible); + QVERIFY(state(child) & QAccessible::Invisible); + + QTestAccessibility::clearEvents(); + + // show() and veryfy that both window and child are not invisible and get ObjectShow events. + window->show(); + QVERIFY(state(window) ^ QAccessible::Invisible); + QVERIFY(state(child) ^ QAccessible::Invisible); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(window, 0, QAccessible::ObjectShow))); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(child, 0, QAccessible::ObjectShow))); + QTestAccessibility::clearEvents(); + + // hide() and veryfy that both window and child are invisible and get ObjectHide events. + window->hide(); + QVERIFY(state(window) & QAccessible::Invisible); + QVERIFY(state(child) & QAccessible::Invisible); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(window, 0, QAccessible::ObjectHide))); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(child, 0, QAccessible::ObjectHide))); + QTestAccessibility::clearEvents(); + + delete window; + QTestAccessibility::clearEvents(); +} + + +void tst_QAccessibility::actionTest() +{ + QCOMPARE(QAccessibleActionInterface::PressAction, QString("Press")); + + QWidget *widget = new QWidget; + widget->show(); + + QAccessibleInterface *test = QAccessible::queryAccessibleInterface(widget); + QVERIFY(test); + QVERIFY(test->isValid()); + +// QCOMPARE(test->actionText(1, QAccessible::Name, 0), QString()); +// QCOMPARE(test->actionText(0, QAccessible::Name, 1), QString()); +// QCOMPARE(test->actionText(1, QAccessible::Name, 1), QString()); +// QCOMPARE(test->actionText(QAccessible::SetFocus, QAccessible::Name, -1), QString()); + +// QCOMPARE(test->actionText(QAccessible::DefaultAction, QAccessible::Name, 0), QString("SetFocus")); +// QCOMPARE(test->actionText(QAccessible::SetFocus, QAccessible::Name, 0), QString("SetFocus")); + + delete test; + delete widget; + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::applicationTest() +{ + QLatin1String name = QLatin1String("My Name"); + qApp->setApplicationName(name); + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(qApp); + QCOMPARE(interface->text(QAccessible::Name, 0), name); + QCOMPARE(interface->role(), QAccessible::Application); + delete interface; +} + +void tst_QAccessibility::mainWindowTest() +{ + QMainWindow *mw = new QMainWindow; + mw->resize(300, 200); + mw->show(); // triggers layout + + QLatin1String name = QLatin1String("I am the main window"); + mw->setWindowTitle(name); + QTest::qWaitForWindowShown(mw); + QVERIFY_EVENT(mw, 0, QAccessible::ObjectShow); + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(mw); + QCOMPARE(interface->text(QAccessible::Name, 0), name); + QCOMPARE(interface->role(), QAccessible::Window); + delete interface; + delete mw; + QTestAccessibility::clearEvents(); +} + +class CounterButton : public QPushButton { + Q_OBJECT +public: + CounterButton(const QString& name, QWidget* parent) + : QPushButton(name, parent), clickCount(0) + { + connect(this, SIGNAL(clicked(bool)), SLOT(incClickCount())); + } + int clickCount; +public Q_SLOTS: + void incClickCount() { + ++clickCount; + } +}; + +void tst_QAccessibility::buttonTest() +{ + QWidget window; + window.setLayout(new QVBoxLayout); + + // Standard push button + CounterButton pushButton("Ok", &window); + + // toggle button + QPushButton toggleButton("Toggle", &window); + toggleButton.setCheckable(true); + + // standard checkbox + QCheckBox checkBox("Check me!", &window); + + // tristate checkbox + QCheckBox tristate("Tristate!", &window); + tristate.setTristate(TRUE); + + // radiobutton + QRadioButton radio("Radio me!", &window); + + // standard toolbutton + QToolButton toolbutton(&window); + toolbutton.setText("Tool"); + toolbutton.setMinimumSize(20,20); + + // standard toolbutton + QToolButton toggletool(&window); + toggletool.setCheckable(true); + toggletool.setText("Toggle"); + toggletool.setMinimumSize(20,20); + + // test push button + QAccessibleInterface* interface = QAccessible::queryAccessibleInterface(&pushButton); + QAccessibleActionInterface* actionInterface = interface->actionInterface(); + QVERIFY(actionInterface != 0); + QCOMPARE(interface->role(), QAccessible::PushButton); + + // currently our buttons only have click as action, press and release are missing + QCOMPARE(actionInterface->actionNames().size(), 1); + QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::PressAction); + QCOMPARE(pushButton.clickCount, 0); + actionInterface->doAction(QAccessibleActionInterface::PressAction); + QTest::qWait(500); + QCOMPARE(pushButton.clickCount, 1); + delete interface; + + // test toggle button + interface = QAccessible::queryAccessibleInterface(&toggleButton); + actionInterface = interface->actionInterface(); + QCOMPARE(interface->role(), QAccessible::CheckBox); + QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::CheckAction); + QCOMPARE(actionInterface->localizedActionDescription(QAccessibleActionInterface::CheckAction), QString("Checks the checkbox")); + QVERIFY(!toggleButton.isChecked()); + QVERIFY((interface->state() & QAccessible::Checked) == 0); + actionInterface->doAction(QAccessibleActionInterface::CheckAction); + QTest::qWait(500); + QVERIFY(toggleButton.isChecked()); + QCOMPARE(actionInterface->actionNames().at(0), QAccessibleActionInterface::UncheckAction); + QVERIFY(interface->state() & QAccessible::Checked); + delete interface; + + { + // test menu push button + QAction *foo = new QAction("Foo", 0); + foo->setShortcut(QKeySequence("Ctrl+F")); + QMenu *menu = new QMenu(); + menu->addAction(foo); + QPushButton menuButton; + menuButton.setMenu(menu); + menuButton.show(); + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&menuButton); + QCOMPARE(interface->role(), QAccessible::ButtonMenu); + QVERIFY(interface->state() & QAccessible::HasPopup); + QCOMPARE(interface->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::ShowMenuAction); + // showing the menu enters a new event loop... +// interface->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); +// QTest::qWait(500); + delete interface; + delete menu; + } + + // test check box + interface = QAccessible::queryAccessibleInterface(&checkBox); + actionInterface = interface->actionInterface(); + QCOMPARE(interface->role(), QAccessible::CheckBox); + QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::CheckAction); + QVERIFY((interface->state() & QAccessible::Checked) == 0); + actionInterface->doAction(QAccessibleActionInterface::CheckAction); + QTest::qWait(500); + QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::UncheckAction); + QVERIFY(interface->state() & QAccessible::Checked); + QVERIFY(checkBox.isChecked()); + delete interface; + + // test radiobutton + interface = QAccessible::queryAccessibleInterface(&radio); + actionInterface = interface->actionInterface(); + QCOMPARE(interface->role(), QAccessible::RadioButton); + QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::CheckAction); + QVERIFY((interface->state() & QAccessible::Checked) == 0); + actionInterface->doAction(QAccessibleActionInterface::CheckAction); + QTest::qWait(500); + QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::CheckAction); + QVERIFY(interface->state() & QAccessible::Checked); + QVERIFY(checkBox.isChecked()); + delete interface; + +// // test standard toolbutton +// QVERIFY(QAccessible::queryAccessibleInterface(&toolbutton, &test)); +// QCOMPARE(test->role(), QAccessible::PushButton); +// QCOMPARE(test->defaultAction(0), QAccessible::Press); +// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Press")); +// QCOMPARE(test->state(), (int)QAccessible::Normal); +// test->release(); + +// // toggle tool button +// QVERIFY(QAccessible::queryAccessibleInterface(&toggletool, &test)); +// QCOMPARE(test->role(), QAccessible::CheckBox); +// QCOMPARE(test->defaultAction(0), QAccessible::Press); +// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Check")); +// QCOMPARE(test->state(), (int)QAccessible::Normal); +// QVERIFY(test->doAction(QAccessible::Press, 0)); +// QTest::qWait(500); +// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Uncheck")); +// QCOMPARE(test->state(), (int)QAccessible::Checked); +// test->release(); + +// // test menu toolbutton +// QVERIFY(QAccessible::queryAccessibleInterface(&menuToolButton, &test)); +// QCOMPARE(test->role(), QAccessible::ButtonMenu); +// QCOMPARE(test->defaultAction(0), 1); +// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Open")); +// QCOMPARE(test->state(), (int)QAccessible::HasPopup); +// QCOMPARE(test->actionCount(0), 1); +// QCOMPARE(test->actionText(QAccessible::Press, QAccessible::Name, 0), QString("Press")); +// test->release(); + +// // test split menu toolbutton +// QVERIFY(QAccessible::queryAccessibleInterface(&splitToolButton, &test)); +// QCOMPARE(test->childCount(), 2); +// QCOMPARE(test->role(), QAccessible::ButtonDropDown); +// QCOMPARE(test->role(1), QAccessible::PushButton); +// QCOMPARE(test->role(2), QAccessible::ButtonMenu); +// QCOMPARE(test->defaultAction(0), QAccessible::Press); +// QCOMPARE(test->defaultAction(1), QAccessible::Press); +// QCOMPARE(test->defaultAction(2), QAccessible::Press); +// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Press")); +// QCOMPARE(test->state(), (int)QAccessible::HasPopup); +// QCOMPARE(test->actionCount(0), 1); +// QCOMPARE(test->actionText(1, QAccessible::Name, 0), QString("Open")); +// QCOMPARE(test->actionText(test->defaultAction(1), QAccessible::Name, 1), QString("Press")); +// QCOMPARE(test->state(1), (int)QAccessible::Normal); +// QCOMPARE(test->actionText(test->defaultAction(2), QAccessible::Name, 2), QString("Open")); +// QCOMPARE(test->state(2), (int)QAccessible::HasPopup); +// test->release(); + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::scrollBarTest() +{ + QScrollBar *scrollBar = new QScrollBar(Qt::Horizontal); + QAccessibleInterface * const scrollBarInterface = QAccessible::queryAccessibleInterface(scrollBar); + QVERIFY(scrollBarInterface); + QVERIFY(scrollBarInterface->state() & QAccessible::Invisible); + scrollBar->resize(200, 50); + scrollBar->show(); + QVERIFY(scrollBarInterface->state() ^ QAccessible::Invisible); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(scrollBar, 0, QAccessible::ObjectShow))); + QTestAccessibility::clearEvents(); + + scrollBar->hide(); + QVERIFY(scrollBarInterface->state() & QAccessible::Invisible); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(scrollBar, 0, QAccessible::ObjectHide))); + QTestAccessibility::clearEvents(); + + // Test that the left/right subcontrols are set to unavailable when the scrollBar is at the minimum/maximum. + scrollBar->show(); + scrollBar->setMinimum(11); + scrollBar->setMaximum(111); + + QAccessibleValueInterface *valueIface = scrollBarInterface->valueInterface(); + QVERIFY(valueIface != 0); + QCOMPARE(valueIface->minimumValue().toInt(), scrollBar->minimum()); + QCOMPARE(valueIface->maximumValue().toInt(), scrollBar->maximum()); + scrollBar->setValue(50); + QCOMPARE(valueIface->currentValue().toInt(), scrollBar->value()); + scrollBar->setValue(0); + QCOMPARE(valueIface->currentValue().toInt(), scrollBar->value()); + scrollBar->setValue(100); + QCOMPARE(valueIface->currentValue().toInt(), scrollBar->value()); + valueIface->setCurrentValue(77); + QCOMPARE(77, scrollBar->value()); + + const QRect scrollBarRect = scrollBarInterface->rect(); + QVERIFY(scrollBarRect.isValid()); + + delete scrollBarInterface; + delete scrollBar; + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::tabTest() +{ + QTabBar *tabBar = new QTabBar(); + tabBar->show(); + + QAccessibleInterface * const interface = QAccessible::queryAccessibleInterface(tabBar); + QVERIFY(interface); + QCOMPARE(interface->childCount(), 2); + + // Test that the Invisible bit for the navigation buttons gets set + // and cleared correctly. + QAccessibleInterface *leftButton = interface->child(0); + QCOMPARE(leftButton->role(), QAccessible::PushButton); + QVERIFY(leftButton->state() & QAccessible::Invisible); + delete leftButton; + + const int lots = 5; + for (int i = 0; i < lots; ++i) + tabBar->addTab("Foo"); + + QAccessibleInterface *child1 = interface->child(0); + QAccessibleInterface *child2 = interface->child(1); + QVERIFY(child1); + QCOMPARE(child1->role(), QAccessible::PageTab); + QVERIFY(child2); + QCOMPARE(child2->role(), QAccessible::PageTab); + + QVERIFY((child1->state() & QAccessible::Invisible) == false); + tabBar->hide(); + + QCoreApplication::processEvents(); + QTest::qWait(100); + + QVERIFY(child1->state() & QAccessible::Invisible); + + tabBar->show(); + tabBar->setCurrentIndex(0); + + // Test that sending a focus action to a tab does not select it. +// child2->doAction(QAccessible::Focus, 2, QVariantList()); + QCOMPARE(tabBar->currentIndex(), 0); + + // Test that sending a press action to a tab selects it. + QVERIFY(child2->actionInterface()); + QCOMPARE(child2->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::PressAction); + QCOMPARE(tabBar->currentIndex(), 0); + child2->actionInterface()->doAction(QAccessibleActionInterface::PressAction); + QCOMPARE(tabBar->currentIndex(), 1); + + delete tabBar; + delete interface; + delete child1; + delete child2; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::tabWidgetTest() +{ + QTabWidget *tabWidget = new QTabWidget(); + tabWidget->show(); + + // the interface for the tab is just a container for tabbar and stacked widget + QAccessibleInterface * const interface = QAccessible::queryAccessibleInterface(tabWidget); + QVERIFY(interface); + QCOMPARE(interface->childCount(), 2); + QCOMPARE(interface->role(), QAccessible::Client); + + // Create pages, check navigation + QLabel *label1 = new QLabel("Page 1", tabWidget); + tabWidget->addTab(label1, "Tab 1"); + QLabel *label2 = new QLabel("Page 2", tabWidget); + tabWidget->addTab(label2, "Tab 2"); + + QCOMPARE(interface->childCount(), 2); + + QAccessibleInterface* tabBarInterface = 0; + // there is no special logic to sort the children, so the contents will be 1, the tab bar 2 + tabBarInterface = interface->child(1); + QVERIFY(tabBarInterface); + QCOMPARE(tabBarInterface->childCount(), 4); + QCOMPARE(tabBarInterface->role(), QAccessible::PageTabList); + + QAccessibleInterface* tabButton1Interface = tabBarInterface->child(0); + QVERIFY(tabButton1Interface); + QCOMPARE(tabButton1Interface->role(), QAccessible::PageTab); + QCOMPARE(tabButton1Interface->text(QAccessible::Name), QLatin1String("Tab 1")); + + QAccessibleInterface* tabButton2Interface = tabBarInterface->child(1); + QVERIFY(tabButton1Interface); + QCOMPARE(tabButton2Interface->role(), QAccessible::PageTab); + QCOMPARE(tabButton2Interface->text(QAccessible::Name), QLatin1String("Tab 2")); + + QAccessibleInterface* tabButtonLeft = tabBarInterface->child(2); + QVERIFY(tabButtonLeft); + QCOMPARE(tabButtonLeft->role(), QAccessible::PushButton); + QCOMPARE(tabButtonLeft->text(QAccessible::Name), QLatin1String("Scroll Left")); + + QAccessibleInterface* tabButtonRight = tabBarInterface->child(3); + QVERIFY(tabButtonRight); + QCOMPARE(tabButtonRight->role(), QAccessible::PushButton); + QCOMPARE(tabButtonRight->text(QAccessible::Name), QLatin1String("Scroll Right")); + delete tabButton1Interface; + delete tabButton2Interface; + delete tabButtonLeft; + delete tabButtonRight; + + QAccessibleInterface* stackWidgetInterface = interface->child(0); + QVERIFY(stackWidgetInterface); + QCOMPARE(stackWidgetInterface->childCount(), 2); + QCOMPARE(stackWidgetInterface->role(), QAccessible::LayeredPane); + + QAccessibleInterface* stackChild1Interface = stackWidgetInterface->child(0); + QVERIFY(stackChild1Interface); +#ifndef Q_CC_INTEL + QCOMPARE(stackChild1Interface->childCount(), 0); +#endif + QCOMPARE(stackChild1Interface->role(), QAccessible::StaticText); + QCOMPARE(stackChild1Interface->text(QAccessible::Name), QLatin1String("Page 1")); + QCOMPARE(label1, stackChild1Interface->object()); + + // Navigation in stack widgets should be consistent + QAccessibleInterface* parent = stackChild1Interface->parent(); + QVERIFY(parent); +#ifndef Q_CC_INTEL + QCOMPARE(parent->childCount(), 2); +#endif + QCOMPARE(parent->role(), QAccessible::LayeredPane); + delete parent; + + QAccessibleInterface* stackChild2Interface = stackWidgetInterface->child(1); + QVERIFY(stackChild2Interface); + QCOMPARE(stackChild2Interface->childCount(), 0); + QCOMPARE(stackChild2Interface->role(), QAccessible::StaticText); + QCOMPARE(label2, stackChild2Interface->object()); + QCOMPARE(label2->text(), stackChild2Interface->text(QAccessible::Name)); + + parent = stackChild2Interface->parent(); + QVERIFY(parent); +#ifndef Q_CC_INTEL + QCOMPARE(parent->childCount(), 2); +#endif + QCOMPARE(parent->role(), QAccessible::LayeredPane); + delete parent; + + delete tabBarInterface; + delete stackChild1Interface; + delete stackChild2Interface; + delete stackWidgetInterface; + delete interface; + delete tabWidget; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::menuTest() +{ + { + QMainWindow mw; + mw.resize(300, 200); + QMenu *file = mw.menuBar()->addMenu("&File"); + QMenu *fileNew = file->addMenu("&New..."); + fileNew->menuAction()->setShortcut(tr("Ctrl+N")); + fileNew->addAction("Text file"); + fileNew->addAction("Image file"); + file->addAction("&Open")->setShortcut(tr("Ctrl+O")); + file->addAction("&Save")->setShortcut(tr("Ctrl+S")); + file->addSeparator(); + file->addAction("E&xit")->setShortcut(tr("Alt+F4")); + + QMenu *edit = mw.menuBar()->addMenu("&Edit"); + edit->addAction("&Undo")->setShortcut(tr("Ctrl+Z")); + edit->addAction("&Redo")->setShortcut(tr("Ctrl+Y")); + edit->addSeparator(); + edit->addAction("Cu&t")->setShortcut(tr("Ctrl+X")); + edit->addAction("&Copy")->setShortcut(tr("Ctrl+C")); + edit->addAction("&Paste")->setShortcut(tr("Ctrl+V")); + edit->addAction("&Delete")->setShortcut(tr("Del")); + edit->addSeparator(); + edit->addAction("Pr&operties"); + + mw.menuBar()->addSeparator(); + + QMenu *help = mw.menuBar()->addMenu("&Help"); + help->addAction("&Contents"); + help->addAction("&About"); + + mw.menuBar()->addAction("Action!"); + + mw.show(); // triggers layout + QTest::qWait(100); + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(mw.menuBar()); + QCOMPARE(verifyHierarchy(interface), 0); + + QVERIFY(interface); + QCOMPARE(interface->childCount(), 5); + QCOMPARE(interface->role(), QAccessible::MenuBar); + + QAccessibleInterface *iFile = interface->child(0); + QAccessibleInterface *iEdit = interface->child(1); + QAccessibleInterface *iSeparator = interface->child(2); + QAccessibleInterface *iHelp = interface->child(3); + QAccessibleInterface *iAction = interface->child(4); + + QCOMPARE(iFile->role(), QAccessible::MenuItem); + QCOMPARE(iEdit->role(), QAccessible::MenuItem); + QCOMPARE(iSeparator->role(), QAccessible::Separator); + QCOMPARE(iHelp->role(), QAccessible::MenuItem); + QCOMPARE(iAction->role(), QAccessible::MenuItem); +#ifndef Q_WS_MAC +#ifdef Q_OS_WINCE + if (!IsValidCEPlatform()) { + QSKIP("Tests do not work on Mobile platforms due to native menus", SkipAll); + } +#endif + QCOMPARE(mw.mapFromGlobal(interface->rect().topLeft()), mw.menuBar()->geometry().topLeft()); + QCOMPARE(interface->rect().size(), mw.menuBar()->size()); + + QVERIFY(interface->rect().contains(iFile->rect())); + QVERIFY(interface->rect().contains(iEdit->rect())); + // QVERIFY(interface->rect().contains(childSeparator->rect())); //separator might be invisible + QVERIFY(interface->rect().contains(iHelp->rect())); + QVERIFY(interface->rect().contains(iAction->rect())); +#endif + + QCOMPARE(iFile->text(QAccessible::Name, 0), QString("File")); + QCOMPARE(iEdit->text(QAccessible::Name, 0), QString("Edit")); + QCOMPARE(iSeparator->text(QAccessible::Name, 0), QString()); + QCOMPARE(iHelp->text(QAccessible::Name, 0), QString("Help")); + QCOMPARE(iAction->text(QAccessible::Name, 0), QString("Action!")); + +// TODO: Currently not working, task to fix is #100019. +#ifndef Q_OS_MAC + QCOMPARE(iFile->text(QAccessible::Accelerator, 0), tr("Alt+F")); + QCOMPARE(iEdit->text(QAccessible::Accelerator, 0), tr("Alt+E")); + QCOMPARE(iSeparator->text(QAccessible::Accelerator, 0), QString()); + QCOMPARE(iHelp->text(QAccessible::Accelerator, 0), tr("Alt+H")); + QCOMPARE(iAction->text(QAccessible::Accelerator, 0), QString()); +#endif + + QVERIFY(iFile->actionInterface()); + + QCOMPARE(iFile->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::ShowMenuAction); + QCOMPARE(iSeparator->actionInterface()->actionNames(), QStringList()); + QCOMPARE(iHelp->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::ShowMenuAction); + QCOMPARE(iAction->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::PressAction); + + bool menuFade = qApp->isEffectEnabled(Qt::UI_FadeMenu); + int menuFadeDelay = 300; + iFile->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + if(menuFade) + QTest::qWait(menuFadeDelay); + QVERIFY(file->isVisible() && !edit->isVisible() && !help->isVisible()); + iEdit->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + if(menuFade) + QTest::qWait(menuFadeDelay); + QVERIFY(!file->isVisible() && edit->isVisible() && !help->isVisible()); + iHelp->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + if(menuFade) + QTest::qWait(menuFadeDelay); + QVERIFY(!file->isVisible() && !edit->isVisible() && help->isVisible()); + iAction->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + if(menuFade) + QTest::qWait(menuFadeDelay); + QVERIFY(!file->isVisible() && !edit->isVisible() && !help->isVisible()); + + QVERIFY(!interface->actionInterface()); + delete interface; + interface = QAccessible::queryAccessibleInterface(file); + QCOMPARE(interface->childCount(), 5); + QCOMPARE(interface->role(), QAccessible::PopupMenu); + + QAccessibleInterface *iFileNew = interface->child(0); + QAccessibleInterface *iFileOpen = interface->child(1); + QAccessibleInterface *iFileSave = interface->child(2); + QAccessibleInterface *iFileSeparator = interface->child(3); + QAccessibleInterface *iFileExit = interface->child(4); + + QCOMPARE(iFileNew->role(), QAccessible::MenuItem); + QCOMPARE(iFileOpen->role(), QAccessible::MenuItem); + QCOMPARE(iFileSave->role(), QAccessible::MenuItem); + QCOMPARE(iFileSeparator->role(), QAccessible::Separator); + QCOMPARE(iFileExit->role(), QAccessible::MenuItem); + QCOMPARE(iFileNew->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::ShowMenuAction); + QCOMPARE(iFileOpen->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::PressAction); + QCOMPARE(iFileSave->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::PressAction); + QCOMPARE(iFileSeparator->actionInterface()->actionNames(), QStringList()); + QCOMPARE(iFileExit->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::PressAction); + + QAccessibleInterface *iface = 0; + QAccessibleInterface *iface2 = 0; + + // traverse siblings with navigate(Sibling, ...) + int entry = interface->navigate(QAccessible::Child, 1, &iface); + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::MenuItem); + + QAccessible::Role fileRoles[5] = { + QAccessible::MenuItem, + QAccessible::MenuItem, + QAccessible::MenuItem, + QAccessible::Separator, + QAccessible::MenuItem + }; + for (int child = 0; child < 5; ++child) { + entry = iface->navigate(QAccessible::Sibling, child + 1, &iface2); + QCOMPARE(entry, 0); + QVERIFY(iface2); + QCOMPARE(iface2->role(), fileRoles[child]); + delete iface2; + } + delete iface; + + // traverse menu items with navigate(Down, ...) + entry = interface->navigate(QAccessible::Child, 1, &iface); + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::MenuItem); + + for (int child = 0; child < 4; ++child) { + entry = iface->navigate(QAccessible::Down, 1, &iface2); + delete iface; + iface = iface2; + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), fileRoles[child + 1]); + } + delete iface; + + // traverse menu items with navigate(Up, ...) + entry = interface->navigate(QAccessible::Child, interface->childCount(), &iface); + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::MenuItem); + + for (int child = 3; child >= 0; --child) { + entry = iface->navigate(QAccessible::Up, 1, &iface2); + delete iface; + iface = iface2; + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), fileRoles[child]); + } + delete iface; + + // "New" item + entry = interface->navigate(QAccessible::Child, 1, &iface); + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::MenuItem); + + // "New" menu + entry = iface->navigate(QAccessible::Child, 1, &iface2); + delete iface; + iface = iface2; + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::PopupMenu); + + // "Text file" menu item + entry = iface->navigate(QAccessible::Child, 1, &iface2); + delete iface; + iface = iface2; + QCOMPARE(entry, 0); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::MenuItem); + + delete iface; + + // move mouse pointer away, since that might influence the + // subsequent tests + QTest::mouseMove(&mw, QPoint(-1, -1)); + QTest::qWait(100); + if (menuFade) + QTest::qWait(menuFadeDelay); + + iFile->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + iFileNew->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + + QVERIFY(file->isVisible()); + QVERIFY(fileNew->isVisible()); + QVERIFY(!edit->isVisible()); + QVERIFY(!help->isVisible()); + + QTestAccessibility::clearEvents(); + mw.hide(); + + delete iFile; + delete iFileNew; + delete iFileOpen; + delete iFileSave; + delete iFileSeparator; + delete iFileExit; + + // Do not crash if the menu don't have a parent + QMenu *menu = new QMenu; + menu->addAction(QLatin1String("one")); + menu->addAction(QLatin1String("two")); + menu->addAction(QLatin1String("three")); + iface = QAccessible::queryAccessibleInterface(menu); + iface2 = iface->parent(); + QVERIFY(iface2); + QCOMPARE(iface2->role(), QAccessible::Application); + // caused a *crash* + iface2->state(); + delete iface2; + delete iface; + delete menu; + + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::spinBoxTest() +{ + QSpinBox * const spinBox = new QSpinBox(); + spinBox->setValue(3); + spinBox->show(); + + QAccessibleInterface * const interface = QAccessible::queryAccessibleInterface(spinBox); + QVERIFY(interface); + QCOMPARE(interface->role(), QAccessible::SpinBox); + + const QRect widgetRect = spinBox->geometry(); + const QRect accessibleRect = interface->rect(); + QCOMPARE(accessibleRect, widgetRect); + QCOMPARE(interface->text(QAccessible::Value, 0), QLatin1String("3")); + + // one child, the line edit + const int numChildren = interface->childCount(); + QCOMPARE(numChildren, 1); + QAccessibleInterface *lineEdit = interface->child(0); + + QCOMPARE(lineEdit->role(), QAccessible::EditableText); + QCOMPARE(lineEdit->text(QAccessible::Value, 0), QLatin1String("3")); + delete lineEdit; + + QVERIFY(interface->valueInterface()); + QCOMPARE(interface->valueInterface()->currentValue().toInt(), 3); + interface->valueInterface()->setCurrentValue(23); + QCOMPARE(interface->valueInterface()->currentValue().toInt(), 23); + QCOMPARE(spinBox->value(), 23); + + spinBox->setFocus(); + QTestAccessibility::clearEvents(); + QTest::keyPress(spinBox, Qt::Key_Up); + QTest::qWait(200); + EventList events = QTestAccessibility::events(); + QTestAccessibilityEvent expectedEvent(spinBox, 0, (int)QAccessible::ValueChanged); + QVERIFY(events.contains(expectedEvent)); + delete spinBox; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::doubleSpinBoxTest() +{ + QDoubleSpinBox *doubleSpinBox = new QDoubleSpinBox; + doubleSpinBox->show(); + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(doubleSpinBox); + QVERIFY(interface); + + const QRect widgetRect = doubleSpinBox->geometry(); + const QRect accessibleRect = interface->rect(); + QCOMPARE(accessibleRect, widgetRect); + + // Test that we get valid rects for all the spinbox child interfaces. + const int numChildren = interface->childCount(); + for (int i = 1; i <= numChildren; ++i) { + const QRect childRect = interface->rect(i); + QVERIFY(childRect.isValid()); + } + + delete doubleSpinBox; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::textEditTest() +{ + { + QTextEdit edit; + int startOffset; + int endOffset; + QString text = "hello world\nhow are you today?\n"; + edit.setText(text); + edit.show(); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&edit); + QCOMPARE(iface->text(QAccessible::Value, 0), text); + QCOMPARE(iface->textInterface()->textAtOffset(8, QAccessible2::WordBoundary, &startOffset, &endOffset), QString("world")); + QCOMPARE(startOffset, 6); + QCOMPARE(endOffset, 11); + QCOMPARE(iface->textInterface()->textAtOffset(14, QAccessible2::LineBoundary, &startOffset, &endOffset), QString("how are you today?")); + QCOMPARE(startOffset, 12); + QCOMPARE(endOffset, 30); + QCOMPARE(iface->textInterface()->characterCount(), 31); + QFontMetrics fm(edit.font()); + QCOMPARE(iface->textInterface()->characterRect(0, QAccessible2::RelativeToParent).size(), QSize(fm.width("h"), fm.height())); + QCOMPARE(iface->textInterface()->characterRect(5, QAccessible2::RelativeToParent).size(), QSize(fm.width(" "), fm.height())); + QCOMPARE(iface->textInterface()->characterRect(6, QAccessible2::RelativeToParent).size(), QSize(fm.width("w"), fm.height())); + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::textBrowserTest() +{ + { + QTextBrowser textBrowser; + QString text = QLatin1String("Hello world\nhow are you today?\n"); + textBrowser.setText(text); + textBrowser.show(); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&textBrowser); + QVERIFY(iface); + QCOMPARE(iface->role(), QAccessible::StaticText); + QCOMPARE(iface->text(QAccessible::Value, 0), text); + int startOffset; + int endOffset; + QCOMPARE(iface->textInterface()->textAtOffset(8, QAccessible2::WordBoundary, &startOffset, &endOffset), QString("world")); + QCOMPARE(startOffset, 6); + QCOMPARE(endOffset, 11); + QCOMPARE(iface->textInterface()->textAtOffset(14, QAccessible2::LineBoundary, &startOffset, &endOffset), QString("how are you today?")); + QCOMPARE(startOffset, 12); + QCOMPARE(endOffset, 30); + QCOMPARE(iface->textInterface()->characterCount(), 31); + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::mdiAreaTest() +{ + { + QMdiArea mdiArea; + mdiArea.resize(400,300); + mdiArea.show(); + const int subWindowCount = 3; + for (int i = 0; i < subWindowCount; ++i) + mdiArea.addSubWindow(new QWidget, Qt::Dialog)->show(); + + QList subWindows = mdiArea.subWindowList(); + QCOMPARE(subWindows.count(), subWindowCount); + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&mdiArea); + QVERIFY(interface); + QCOMPARE(interface->childCount(), subWindowCount); + + // Right, right, right, ... + for (int i = 0; i < subWindowCount; ++i) { + QAccessibleInterface *destination = 0; + int index = interface->navigate(QAccessible::Right, i + 1, &destination); + if (i == subWindowCount - 1) { + QVERIFY(!destination); + QCOMPARE(index, -1); + } else { + QVERIFY(destination); + QCOMPARE(index, 0); + QCOMPARE(destination->object(), (QObject*)subWindows.at(i + 1)); + delete destination; + } + } + + // Left, left, left, ... + for (int i = subWindowCount; i > 0; --i) { + QAccessibleInterface *destination = 0; + int index = interface->navigate(QAccessible::Left, i, &destination); + if (i == 1) { + QVERIFY(!destination); + QCOMPARE(index, -1); + } else { + QVERIFY(destination); + QCOMPARE(index, 0); + QCOMPARE(destination->object(), (QObject*)subWindows.at(i - 2)); + delete destination; + } + } + // ### Add test for Up and Down. + + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::mdiSubWindowTest() +{ + { + QMdiArea mdiArea; + mdiArea.show(); + qApp->setActiveWindow(&mdiArea); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(150); +#endif + + bool isSubWindowsPlacedNextToEachOther = false; + const int subWindowCount = 5; + for (int i = 0; i < subWindowCount; ++i) { + QMdiSubWindow *window = mdiArea.addSubWindow(new QPushButton("QAccessibilityTest")); + window->show(); + // Parts of this test requires that the sub windows are placed next + // to each other. In order to achieve that QMdiArea must have + // a width which is larger than subWindow->width() * subWindowCount. + if (i == 0) { + int minimumWidth = window->width() * subWindowCount + 20; + mdiArea.resize(mdiArea.size().expandedTo(QSize(minimumWidth, 0))); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + if (mdiArea.width() >= minimumWidth) + isSubWindowsPlacedNextToEachOther = true; + } + } + + QList subWindows = mdiArea.subWindowList(); + QCOMPARE(subWindows.count(), subWindowCount); + + QMdiSubWindow *testWindow = subWindows.at(3); + QVERIFY(testWindow); + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(testWindow); + + // childCount + QVERIFY(interface); + QCOMPARE(interface->childCount(), 1); + + // setText / text + QCOMPARE(interface->text(QAccessible::Name, 0), QString()); + QCOMPARE(interface->text(QAccessible::Name, 1), QString()); + testWindow->setWindowTitle(QLatin1String("ReplaceMe")); + QCOMPARE(interface->text(QAccessible::Name, 0), QLatin1String("ReplaceMe")); + QCOMPARE(interface->text(QAccessible::Name, 1), QLatin1String("ReplaceMe")); + interface->setText(QAccessible::Name, 0, QLatin1String("TitleSetOnWindow")); + QCOMPARE(interface->text(QAccessible::Name, 0), QLatin1String("TitleSetOnWindow")); + interface->setText(QAccessible::Name, 1, QLatin1String("TitleSetOnChild")); + QCOMPARE(interface->text(QAccessible::Name, 0), QLatin1String("TitleSetOnChild")); + + mdiArea.setActiveSubWindow(testWindow); + + // state + QAccessible::State state = QAccessible::Normal | QAccessible::Focusable | QAccessible::Focused + | QAccessible::Movable | QAccessible::Sizeable; + QCOMPARE(interface->state(), state); + const QRect originalGeometry = testWindow->geometry(); + testWindow->showMaximized(); + state &= ~QAccessible::Sizeable; + state &= ~QAccessible::Movable; + QCOMPARE(interface->state(), state); + testWindow->showNormal(); + testWindow->move(-10, 0); + QVERIFY(interface->state() & QAccessible::Offscreen); + testWindow->setVisible(false); + QVERIFY(interface->state() & QAccessible::Invisible); + testWindow->setVisible(true); + testWindow->setEnabled(false); + QVERIFY(interface->state() & QAccessible::Unavailable); + testWindow->setEnabled(true); + qApp->setActiveWindow(&mdiArea); + mdiArea.setActiveSubWindow(testWindow); + testWindow->setFocus(); + QVERIFY(testWindow->isAncestorOf(qApp->focusWidget())); + QVERIFY(interface->state() & QAccessible::Focused); + testWindow->setGeometry(originalGeometry); + + if (isSubWindowsPlacedNextToEachOther) { + // This part of the test can only be run if the sub windows are + // placed next to each other. + QAccessibleInterface *destination = 0; + QCOMPARE(interface->navigate(QAccessible::Child, 1, &destination), 0); + QVERIFY(destination); + QCOMPARE(destination->object(), (QObject*)testWindow->widget()); + delete destination; + QCOMPARE(interface->navigate(QAccessible::Left, 0, &destination), 0); + QVERIFY(destination); + QCOMPARE(destination->object(), (QObject*)subWindows.at(2)); + delete destination; + QCOMPARE(interface->navigate(QAccessible::Right, 0, &destination), 0); + QVERIFY(destination); + QCOMPARE(destination->object(), (QObject*)subWindows.at(4)); + delete destination; + } + + // rect + const QPoint globalPos = testWindow->mapToGlobal(QPoint(0, 0)); + QCOMPARE(interface->rect(), QRect(globalPos, testWindow->size())); + testWindow->hide(); + QCOMPARE(interface->rect(), QRect()); + QCOMPARE(interface->rect(1), QRect()); + testWindow->showMinimized(); + QCOMPARE(interface->rect(1), QRect()); + testWindow->showNormal(); + testWindow->widget()->hide(); + QCOMPARE(interface->rect(1), QRect()); + testWindow->widget()->show(); + const QRect widgetGeometry = testWindow->contentsRect(); + const QPoint globalWidgetPos = QPoint(globalPos.x() + widgetGeometry.x(), + globalPos.y() + widgetGeometry.y()); + QCOMPARE(interface->rect(1), QRect(globalWidgetPos, widgetGeometry.size())); + + // childAt + QCOMPARE(interface->childAt(-10, 0), -1); + QCOMPARE(interface->childAt(globalPos.x(), globalPos.y()), 0); + QCOMPARE(interface->childAt(globalWidgetPos.x(), globalWidgetPos.y()), 1); + testWindow->widget()->hide(); + QCOMPARE(interface->childAt(globalWidgetPos.x(), globalWidgetPos.y()), 0); + + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::lineEditTest() +{ + QLineEdit *le = new QLineEdit; + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(le); + QVERIFY(iface); + le->show(); + + QApplication::processEvents(); + QCOMPARE(iface->childCount(), 0); + QVERIFY(iface->state() & QAccessible::Sizeable); + QVERIFY(iface->state() & QAccessible::Movable); + QCOMPARE(bool(iface->state() & QAccessible::Focusable), le->isActiveWindow()); + QVERIFY(iface->state() & QAccessible::Selectable); + QVERIFY(iface->state() & QAccessible::HasPopup); + QCOMPARE(bool(iface->state() & QAccessible::Focused), le->hasFocus()); + + QString secret(QLatin1String("secret")); + le->setText(secret); + le->setEchoMode(QLineEdit::Normal); + QVERIFY(!(iface->state() & QAccessible::Protected)); + QCOMPARE(iface->text(QAccessible::Value, 0), secret); + le->setEchoMode(QLineEdit::NoEcho); + QVERIFY(iface->state() & QAccessible::Protected); + QVERIFY(iface->text(QAccessible::Value, 0).isEmpty()); + le->setEchoMode(QLineEdit::Password); + QVERIFY(iface->state() & QAccessible::Protected); + QVERIFY(iface->text(QAccessible::Value, 0).isEmpty()); + le->setEchoMode(QLineEdit::PasswordEchoOnEdit); + QVERIFY(iface->state() & QAccessible::Protected); + QVERIFY(iface->text(QAccessible::Value, 0).isEmpty()); + le->setEchoMode(QLineEdit::Normal); + QVERIFY(!(iface->state() & QAccessible::Protected)); + QCOMPARE(iface->text(QAccessible::Value, 0), secret); + + QWidget *toplevel = new QWidget; + le->setParent(toplevel); + toplevel->show(); + QApplication::processEvents(); + QVERIFY(!(iface->state() & QAccessible::Sizeable)); + QVERIFY(!(iface->state() & QAccessible::Movable)); + QCOMPARE(bool(iface->state() & QAccessible::Focusable), le->isActiveWindow()); + QVERIFY(iface->state() & QAccessible::Selectable); + QVERIFY(iface->state() & QAccessible::HasPopup); + QCOMPARE(bool(iface->state() & QAccessible::Focused), le->hasFocus()); + + QLineEdit *le2 = new QLineEdit(toplevel); + le2->show(); + QTest::qWait(100); + le2->activateWindow(); + QTest::qWait(100); + le->setFocus(Qt::TabFocusReason); + QTestAccessibility::clearEvents(); + le2->setFocus(Qt::TabFocusReason); + QTRY_VERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(le2, 0, QAccessible::Focus))); + + le->setText(QLatin1String("500")); + le->setValidator(new QIntValidator()); + iface->setText(QAccessible::Value, 0, QLatin1String("This text is not a number")); + QCOMPARE(le->text(), QLatin1String("500")); + + delete iface; + delete le; + delete le2; + QTestAccessibility::clearEvents(); + + // IA2 + QString cite = "I always pass on good advice. It is the only thing to do with it. It is never of any use to oneself. --Oscar Wilde"; + QLineEdit *le3 = new QLineEdit(cite, toplevel); + iface = QAccessible::queryAccessibleInterface(le3); + QAccessibleTextInterface* textIface = iface->textInterface(); + le3->deselect(); + le3->setCursorPosition(3); + QCOMPARE(textIface->cursorPosition(), 3); + QTRY_VERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(le3, 0, QAccessible::TextCaretMoved))); + QCOMPARE(textIface->selectionCount(), 0); + QTestAccessibility::clearEvents(); + + int start, end; + QCOMPARE(textIface->text(0, 8), QString::fromLatin1("I always")); + QCOMPARE(textIface->textAtOffset(0, QAccessible2::CharBoundary,&start,&end), QString::fromLatin1("I")); + QCOMPARE(start, 0); + QCOMPARE(end, 1); + QCOMPARE(textIface->textBeforeOffset(0, QAccessible2::CharBoundary,&start,&end), QString()); + QCOMPARE(textIface->textAfterOffset(0, QAccessible2::CharBoundary,&start,&end), QString::fromLatin1(" ")); + QCOMPARE(start, 1); + QCOMPARE(end, 2); + + QCOMPARE(textIface->textAtOffset(5, QAccessible2::CharBoundary,&start,&end), QString::fromLatin1("a")); + QCOMPARE(start, 5); + QCOMPARE(end, 6); + QCOMPARE(textIface->textBeforeOffset(5, QAccessible2::CharBoundary,&start,&end), QString::fromLatin1("w")); + QCOMPARE(textIface->textAfterOffset(5, QAccessible2::CharBoundary,&start,&end), QString::fromLatin1("y")); + + QCOMPARE(textIface->textAtOffset(5, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1("always")); + QCOMPARE(start, 2); + QCOMPARE(end, 8); + + QCOMPARE(textIface->textAtOffset(2, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1("always")); + QCOMPARE(textIface->textAtOffset(7, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1("always")); + QCOMPARE(textIface->textAtOffset(8, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1(" ")); + QCOMPARE(textIface->textAtOffset(25, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1("advice")); + QCOMPARE(textIface->textAtOffset(92, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1("oneself")); + + QCOMPARE(textIface->textBeforeOffset(5, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1(" ")); + QCOMPARE(textIface->textAfterOffset(5, QAccessible2::WordBoundary,&start,&end), QString::fromLatin1(" ")); + QCOMPARE(textIface->textAtOffset(5, QAccessible2::SentenceBoundary,&start,&end), QString::fromLatin1("I always pass on good advice. ")); + QCOMPARE(start, 0); + QCOMPARE(end, 30); + + QCOMPARE(textIface->textBeforeOffset(40, QAccessible2::SentenceBoundary,&start,&end), QString::fromLatin1("I always pass on good advice. ")); + QCOMPARE(textIface->textAfterOffset(5, QAccessible2::SentenceBoundary,&start,&end), QString::fromLatin1("It is the only thing to do with it. ")); + + QCOMPARE(textIface->textAtOffset(5, QAccessible2::ParagraphBoundary,&start,&end), cite); + QCOMPARE(start, 0); + QCOMPARE(end, cite.length()); + QCOMPARE(textIface->textAtOffset(5, QAccessible2::LineBoundary,&start,&end), cite); + QCOMPARE(textIface->textAtOffset(5, QAccessible2::NoBoundary,&start,&end), cite); + + delete iface; + delete toplevel; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::workspaceTest() +{ + { + QWorkspace workspace; + workspace.resize(400,300); + workspace.show(); + const int subWindowCount = 3; + for (int i = 0; i < subWindowCount; ++i) { + QWidget *window = workspace.addWindow(new QWidget); + if (i > 0) + window->move(window->x() + 1, window->y()); + window->show(); + window->resize(70, window->height()); + } + + QWidgetList subWindows = workspace.windowList(); + QCOMPARE(subWindows.count(), subWindowCount); + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&workspace); + QVERIFY(interface); + QCOMPARE(interface->childCount(), subWindowCount); + + // Right, right, right, ... + for (int i = 0; i < subWindowCount; ++i) { + QAccessibleInterface *destination = 0; + int index = interface->navigate(QAccessible::Right, i + 1, &destination); + if (i == subWindowCount - 1) { + QVERIFY(!destination); + QCOMPARE(index, -1); + } else { + QVERIFY(destination); + QCOMPARE(index, 0); + QCOMPARE(destination->object(), (QObject*)subWindows.at(i + 1)); + delete destination; + } + } + + // Left, left, left, ... + for (int i = subWindowCount; i > 0; --i) { + QAccessibleInterface *destination = 0; + int index = interface->navigate(QAccessible::Left, i, &destination); + if (i == 1) { + QVERIFY(!destination); + QCOMPARE(index, -1); + } else { + QVERIFY(destination); + QCOMPARE(index, 0); + QCOMPARE(destination->object(), (QObject*)subWindows.at(i - 2)); + delete destination; + } + } + // ### Add test for Up and Down. + + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::dialogButtonBoxTest() +{ + { + QDialogButtonBox box(QDialogButtonBox::Reset | + QDialogButtonBox::Help | + QDialogButtonBox::Ok, Qt::Horizontal); + + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&box); + QVERIFY(iface); + box.show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + + QApplication::processEvents(); + QCOMPARE(iface->childCount(), 3); + QCOMPARE(iface->role(), QAccessible::Grouping); + QCOMPARE(iface->role(1), QAccessible::PushButton); + QCOMPARE(iface->role(2), QAccessible::PushButton); + QCOMPARE(iface->role(3), QAccessible::PushButton); + QStringList actualOrder; + QAccessibleInterface *child; + QAccessibleInterface *leftmost; + child = iface->child(0); + // first find the leftmost button + while (child->navigate(QAccessible::Left, 1, &leftmost) != -1) { + delete child; + child = leftmost; + } + leftmost = child; + + // then traverse from left to right to find the correct order of the buttons + int right = 0; + while (right != -1) { + actualOrder << leftmost->text(QAccessible::Name, 0); + right = leftmost->navigate(QAccessible::Right, 1, &child); + delete leftmost; + leftmost = child; + } + + QStringList expectedOrder; + QDialogButtonBox::ButtonLayout btnlout = + QDialogButtonBox::ButtonLayout(QApplication::style()->styleHint(QStyle::SH_DialogButtonLayout)); + switch (btnlout) { + case QDialogButtonBox::WinLayout: + expectedOrder << QDialogButtonBox::tr("Reset") + << QDialogButtonBox::tr("OK") + << QDialogButtonBox::tr("Help"); + break; + case QDialogButtonBox::GnomeLayout: + case QDialogButtonBox::KdeLayout: + case QDialogButtonBox::MacLayout: + expectedOrder << QDialogButtonBox::tr("Help") + << QDialogButtonBox::tr("Reset") + << QDialogButtonBox::tr("OK"); + break; + } + QCOMPARE(actualOrder, expectedOrder); + delete iface; + QApplication::processEvents(); + QTestAccessibility::clearEvents(); + } + + { + QDialogButtonBox box(QDialogButtonBox::Reset | + QDialogButtonBox::Help | + QDialogButtonBox::Ok, Qt::Horizontal); + + + // Test up and down navigation + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&box); + QVERIFY(iface); + box.setOrientation(Qt::Vertical); + box.show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + + QApplication::processEvents(); + QAccessibleInterface *child; + QStringList actualOrder; + child = iface->child(0); + // first find the topmost button + QAccessibleInterface *other; + while (child->navigate(QAccessible::Up, 1, &other) != -1) { + delete child; + child = other; + } + other = child; + + // then traverse from top to bottom to find the correct order of the buttons + actualOrder.clear(); + int right = 0; + while (right != -1) { + actualOrder << other->text(QAccessible::Name, 0); + right = other->navigate(QAccessible::Down, 1, &child); + delete other; + other = child; + } + + QStringList expectedOrder; + expectedOrder << QDialogButtonBox::tr("OK") + << QDialogButtonBox::tr("Reset") + << QDialogButtonBox::tr("Help"); + + QCOMPARE(actualOrder, expectedOrder); + delete iface; + QApplication::processEvents(); + + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::dialTest() +{ + { + QDial dial; + dial.setMinimum(23); + dial.setMaximum(121); + dial.setValue(42); + QCOMPARE(dial.value(), 42); + dial.show(); + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&dial); + QVERIFY(interface); + QCOMPARE(interface->childCount(), 0); + + QCOMPARE(interface->text(QAccessible::Value, 0), QString::number(dial.value())); + QCOMPARE(interface->rect(), dial.geometry()); + + QAccessibleValueInterface *valueIface = interface->valueInterface(); + QVERIFY(valueIface != 0); + QCOMPARE(valueIface->minimumValue().toInt(), dial.minimum()); + QCOMPARE(valueIface->maximumValue().toInt(), dial.maximum()); + QCOMPARE(valueIface->currentValue().toInt(), 42); + dial.setValue(50); + QCOMPARE(valueIface->currentValue().toInt(), dial.value()); + dial.setValue(0); + QCOMPARE(valueIface->currentValue().toInt(), dial.value()); + dial.setValue(100); + QCOMPARE(valueIface->currentValue().toInt(), dial.value()); + valueIface->setCurrentValue(77); + QCOMPARE(77, dial.value()); + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::rubberBandTest() +{ + QRubberBand rubberBand(QRubberBand::Rectangle); + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&rubberBand); + QVERIFY(interface); + QCOMPARE(interface->role(), QAccessible::Border); + delete interface; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::abstractScrollAreaTest() +{ + { + QAbstractScrollArea abstractScrollArea; + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&abstractScrollArea); + QVERIFY(interface); + QVERIFY(!interface->rect().isValid()); + QCOMPARE(interface->childAt(200, 200), -1); + + abstractScrollArea.resize(400, 400); + abstractScrollArea.show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + const QRect globalGeometry = QRect(abstractScrollArea.mapToGlobal(QPoint(0, 0)), + abstractScrollArea.size()); + + // Viewport. + QCOMPARE(interface->childCount(), 1); + QWidget *viewport = abstractScrollArea.viewport(); + QVERIFY(viewport); + QVERIFY(verifyChild(viewport, interface, 1, globalGeometry)); + + // Horizontal scrollBar. + abstractScrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + QCOMPARE(interface->childCount(), 2); + QWidget *horizontalScrollBar = abstractScrollArea.horizontalScrollBar(); + QWidget *horizontalScrollBarContainer = horizontalScrollBar->parentWidget(); + QVERIFY(verifyChild(horizontalScrollBarContainer, interface, 2, globalGeometry)); + + // Horizontal scrollBar widgets. + QLabel *secondLeftLabel = new QLabel(QLatin1String("L2")); + abstractScrollArea.addScrollBarWidget(secondLeftLabel, Qt::AlignLeft); + QCOMPARE(interface->childCount(), 2); + + QLabel *firstLeftLabel = new QLabel(QLatin1String("L1")); + abstractScrollArea.addScrollBarWidget(firstLeftLabel, Qt::AlignLeft); + QCOMPARE(interface->childCount(), 2); + + QLabel *secondRightLabel = new QLabel(QLatin1String("R2")); + abstractScrollArea.addScrollBarWidget(secondRightLabel, Qt::AlignRight); + QCOMPARE(interface->childCount(), 2); + + QLabel *firstRightLabel = new QLabel(QLatin1String("R1")); + abstractScrollArea.addScrollBarWidget(firstRightLabel, Qt::AlignRight); + QCOMPARE(interface->childCount(), 2); + + // Vertical scrollBar. + abstractScrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + QCOMPARE(interface->childCount(), 3); + QWidget *verticalScrollBar = abstractScrollArea.verticalScrollBar(); + QWidget *verticalScrollBarContainer = verticalScrollBar->parentWidget(); + QVERIFY(verifyChild(verticalScrollBarContainer, interface, 3, globalGeometry)); + + // Vertical scrollBar widgets. + QLabel *secondTopLabel = new QLabel(QLatin1String("T2")); + abstractScrollArea.addScrollBarWidget(secondTopLabel, Qt::AlignTop); + QCOMPARE(interface->childCount(), 3); + + QLabel *firstTopLabel = new QLabel(QLatin1String("T1")); + abstractScrollArea.addScrollBarWidget(firstTopLabel, Qt::AlignTop); + QCOMPARE(interface->childCount(), 3); + + QLabel *secondBottomLabel = new QLabel(QLatin1String("B2")); + abstractScrollArea.addScrollBarWidget(secondBottomLabel, Qt::AlignBottom); + QCOMPARE(interface->childCount(), 3); + + QLabel *firstBottomLabel = new QLabel(QLatin1String("B1")); + abstractScrollArea.addScrollBarWidget(firstBottomLabel, Qt::AlignBottom); + QCOMPARE(interface->childCount(), 3); + + // CornerWidget. + abstractScrollArea.setCornerWidget(new QLabel(QLatin1String("C"))); + QCOMPARE(interface->childCount(), 4); + QWidget *cornerWidget = abstractScrollArea.cornerWidget(); + QVERIFY(verifyChild(cornerWidget, interface, 4, globalGeometry)); + + // Test navigate. + QAccessibleInterface *target = 0; + + // viewport -> Up -> NOTHING + const int viewportIndex = indexOfChild(interface, viewport); + QVERIFY(viewportIndex != -1); + QCOMPARE(interface->navigate(QAccessible::Up, viewportIndex, &target), -1); + QVERIFY(!target); + + // viewport -> Left -> NOTHING + QCOMPARE(interface->navigate(QAccessible::Left, viewportIndex, &target), -1); + QVERIFY(!target); + + // viewport -> Down -> horizontalScrollBarContainer + const int horizontalScrollBarContainerIndex = indexOfChild(interface, horizontalScrollBarContainer); + QVERIFY(horizontalScrollBarContainerIndex != -1); + QCOMPARE(interface->navigate(QAccessible::Down, viewportIndex, &target), 0); + QVERIFY(target); + QCOMPARE(target->object(), static_cast(horizontalScrollBarContainer)); + delete target; + target = 0; + + // horizontalScrollBarContainer -> Left -> NOTHING + QCOMPARE(interface->navigate(QAccessible::Left, horizontalScrollBarContainerIndex, &target), -1); + QVERIFY(!target); + + // horizontalScrollBarContainer -> Down -> NOTHING + QVERIFY(horizontalScrollBarContainerIndex != -1); + QCOMPARE(interface->navigate(QAccessible::Down, horizontalScrollBarContainerIndex, &target), -1); + QVERIFY(!target); + + // horizontalScrollBarContainer -> Right -> cornerWidget + const int cornerWidgetIndex = indexOfChild(interface, cornerWidget); + QVERIFY(cornerWidgetIndex != -1); + QCOMPARE(interface->navigate(QAccessible::Right, horizontalScrollBarContainerIndex, &target), 0); + QVERIFY(target); + QCOMPARE(target->object(), static_cast(cornerWidget)); + delete target; + target = 0; + + // cornerWidget -> Down -> NOTHING + QCOMPARE(interface->navigate(QAccessible::Down, cornerWidgetIndex, &target), -1); + QVERIFY(!target); + + // cornerWidget -> Right -> NOTHING + QVERIFY(cornerWidgetIndex != -1); + QCOMPARE(interface->navigate(QAccessible::Right, cornerWidgetIndex, &target), -1); + QVERIFY(!target); + + // cornerWidget -> Up -> verticalScrollBarContainer + const int verticalScrollBarContainerIndex = indexOfChild(interface, verticalScrollBarContainer); + QVERIFY(verticalScrollBarContainerIndex != -1); + QCOMPARE(interface->navigate(QAccessible::Up, cornerWidgetIndex, &target), 0); + QVERIFY(target); + QCOMPARE(target->object(), static_cast(verticalScrollBarContainer)); + delete target; + target = 0; + + // verticalScrollBarContainer -> Right -> NOTHING + QCOMPARE(interface->navigate(QAccessible::Right, verticalScrollBarContainerIndex, &target), -1); + QVERIFY(!target); + + // verticalScrollBarContainer -> Up -> NOTHING + QCOMPARE(interface->navigate(QAccessible::Up, verticalScrollBarContainerIndex, &target), -1); + QVERIFY(!target); + + // verticalScrollBarContainer -> Left -> viewport + QCOMPARE(interface->navigate(QAccessible::Left, verticalScrollBarContainerIndex, &target), 0); + QVERIFY(target); + QCOMPARE(target->object(), static_cast(viewport)); + delete target; + target = 0; + + QCOMPARE(verifyHierarchy(interface), 0); + + delete interface; + } + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::scrollAreaTest() +{ + { + QScrollArea scrollArea; + scrollArea.show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&scrollArea); + QVERIFY(interface); + QCOMPARE(interface->childCount(), 1); // The viewport. + delete interface; + } + QTestAccessibility::clearEvents(); +} + +// Accessible table1 interface is no longer supported on X11, +// where it has been replaced by table2 interface. +#ifndef Q_OS_UNIX + +void tst_QAccessibility::listViewTest() +{ + { + QListView listView; + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&listView); + QVERIFY(iface); + QCOMPARE(iface->childCount(), 1); + delete iface; + } + { + QListWidget listView; + listView.addItem(tr("A")); + listView.addItem(tr("B")); + listView.addItem(tr("C")); + listView.resize(400,400); + listView.show(); + QTest::qWait(1); // Need this for indexOfchild to work. + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&listView); + QCOMPARE((int)iface->role(), (int)QAccessible::Client); + QCOMPARE((int)iface->role(1), (int)QAccessible::List); + QCOMPARE(iface->childCount(), 1); + QAccessibleInterface *child; + iface->navigate(QAccessible::Child, 1, &child); + delete iface; + iface = child; + QCOMPARE(iface->text(QAccessible::Name, 1), QString("A")); + QCOMPARE(iface->text(QAccessible::Name, 2), QString("B")); + QCOMPARE(iface->text(QAccessible::Name, 3), QString("C")); + + QCOMPARE(iface->childCount(), 3); + + QAccessibleInterface *childA = 0; + QCOMPARE(iface->navigate(QAccessible::Child, 1, &childA), 0); + QVERIFY(childA); + QCOMPARE(iface->indexOfChild(childA), 1); + QCOMPARE(childA->text(QAccessible::Name, 1), QString("A")); + delete childA; + + QAccessibleInterface *childB = 0; + QCOMPARE(iface->navigate(QAccessible::Child, 2, &childB), 0); + QVERIFY(childB); + QCOMPARE(iface->indexOfChild(childB), 2); + QCOMPARE(childB->text(QAccessible::Name, 1), QString("B")); + delete childB; + + QAccessibleInterface *childC = 0; + QCOMPARE(iface->navigate(QAccessible::Child, 3, &childC), 0); + QVERIFY(childC); + QCOMPARE(iface->indexOfChild(childC), 3); + QCOMPARE(childC->text(QAccessible::Name, 1), QString("C")); + delete childC; + QTestAccessibility::clearEvents(); + + // Check for events + QTest::mouseClick(listView.viewport(), Qt::LeftButton, 0, listView.visualItemRect(listView.item(1)).center()); + QTest::mouseClick(listView.viewport(), Qt::LeftButton, 0, listView.visualItemRect(listView.item(2)).center()); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(listView.viewport(), 2, QAccessible::Selection))); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(listView.viewport(), 3, QAccessible::Selection))); + delete iface; + } + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::treeWidgetTest() +{ + QWidget *w = new QWidget; + QTreeWidget *tree = new QTreeWidget(w); + QHBoxLayout *l = new QHBoxLayout(w); + l->addWidget(tree); + for (int i = 0; i < 10; ++i) { + QStringList strings = QStringList() << QString::fromAscii("row: %1").arg(i) + << QString("column 1") << QString("column 2"); + + tree->addTopLevelItem(new QTreeWidgetItem(strings)); + } + w->show(); + + QAccessibleInterface *acc = QAccessible::queryAccessibleInterface(tree); + QAccessibleInterface *accViewport = 0; + int entry = acc->navigate(QAccessible::Child, 1, &accViewport); + QVERIFY(accViewport); + QCOMPARE(entry, 0); + QAccessibleInterface *accTreeItem = 0; + entry = accViewport->navigate(QAccessible::Child, 1, &accTreeItem); + QCOMPARE(entry, 0); + + QAccessibleInterface *accTreeItem2 = 0; + entry = accTreeItem->navigate(QAccessible::Sibling, 3, &accTreeItem2); + QCOMPARE(entry, 0); + QCOMPARE(accTreeItem2->text(QAccessible::Name, 0), QLatin1String("row: 1")); + + // test selected/focused state + QItemSelectionModel *selModel = tree->selectionModel(); + QVERIFY(selModel); + selModel->select(QItemSelection(tree->model()->index(0, 0), tree->model()->index(3, 0)), QItemSelectionModel::Select); + selModel->setCurrentIndex(tree->model()->index(1, 0), QItemSelectionModel::Current); + + for (int i = 1; i < 10 ; ++i) { + QAccessible::State expected; + if (i <= 5 && i >= 2) + expected = QAccessible::Selected; + if (i == 3) + expected |= QAccessible::Focused; + + QCOMPARE(accViewport->state(i) & (QAccessible::Focused | QAccessible::Selected), expected); + } + + // Test sanity of its navigation functions + QCOMPARE(verifyHierarchy(acc), 0); + + delete accTreeItem2; + delete accTreeItem; + delete accViewport; + delete acc; + delete w; + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::tableWidgetTest() +{ + { + QWidget *topLevel = new QWidget; + QTableWidget *w = new QTableWidget(8,4,topLevel); + for (int r = 0; r < 8; ++r) { + for (int c = 0; c < 4; ++c) { + w->setItem(r, c, new QTableWidgetItem(tr("%1,%2").arg(c).arg(r))); + } + } + w->resize(100, 100); + topLevel->show(); + + QAccessibleInterface *client = QAccessible::queryAccessibleInterface(w); + QCOMPARE(client->role(), QAccessible::Client); + QCOMPARE(client->childCount(), 3); + QAccessibleInterface *view = 0; + view = client->child(0); + QCOMPARE(view->role(), QAccessible::Table); + QAccessibleInterface *ifRow; + ifRow = view->child(1); + QCOMPARE(ifRow->role(), QAccessible::Row); + QAccessibleInterface *item; + int entry = ifRow->navigate(QAccessible::Child, 1, &item); + QCOMPARE(entry, 1); + QCOMPARE(item , (QAccessibleInterface*)0); + QCOMPARE(ifRow->text(QAccessible::Name, 2), QLatin1String("0,0")); + QCOMPARE(ifRow->text(QAccessible::Name, 3), QLatin1String("1,0")); + + QCOMPARE(verifyHierarchy(client), 0); + + delete ifRow; + delete view; + delete client; + delete w; + delete topLevel; + } + QTestAccessibility::clearEvents(); +} + +class QtTestTableModel: public QAbstractTableModel +{ + Q_OBJECT + +signals: + void invalidIndexEncountered() const; + +public: + QtTestTableModel(int rows = 0, int columns = 0, QObject *parent = 0) + : QAbstractTableModel(parent), + row_count(rows), + column_count(columns) {} + + int rowCount(const QModelIndex& = QModelIndex()) const { return row_count; } + int columnCount(const QModelIndex& = QModelIndex()) const { return column_count; } + + QVariant data(const QModelIndex &idx, int role) const + { + if (!idx.isValid() || idx.row() >= row_count || idx.column() >= column_count) { + qWarning() << "Invalid modelIndex [%d,%d,%p]" << idx; + emit invalidIndexEncountered(); + return QVariant(); + } + + if (role == Qt::DisplayRole || role == Qt::EditRole) + return QString("[%1,%2,%3]").arg(idx.row()).arg(idx.column()).arg(0); + + return QVariant(); + } + + void removeLastRow() + { + beginRemoveRows(QModelIndex(), row_count - 1, row_count - 1); + --row_count; + endRemoveRows(); + } + + void removeAllRows() + { + beginRemoveRows(QModelIndex(), 0, row_count - 1); + row_count = 0; + endRemoveRows(); + } + + void removeLastColumn() + { + beginRemoveColumns(QModelIndex(), column_count - 1, column_count - 1); + --column_count; + endRemoveColumns(); + } + + void removeAllColumns() + { + beginRemoveColumns(QModelIndex(), 0, column_count - 1); + column_count = 0; + endRemoveColumns(); + } + + void reset() + { + QAbstractTableModel::reset(); + } + + int row_count; + int column_count; +}; + +class QtTestDelegate : public QItemDelegate +{ +public: + QtTestDelegate(QWidget *parent = 0) : QItemDelegate(parent) {} + + virtual QSize sizeHint(const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/) const + { + return QSize(100,50); + } +}; + +void tst_QAccessibility::tableViewTest() +{ + { + QtTestTableModel *model = new QtTestTableModel(3, 4); + QTableView *w = new QTableView(); + w->setModel(model); + w->setItemDelegate(new QtTestDelegate(w)); + w->resize(450,200); + w->resizeColumnsToContents(); + w->resizeRowsToContents(); + w->show(); + + QAccessibleInterface *client = QAccessible::queryAccessibleInterface(w); + QAccessibleInterface *table2; + client->navigate(QAccessible::Child, 1, &table2); + QVERIFY(table2); + QCOMPARE(table2->role(1), QAccessible::Row); + QAccessibleInterface *toprow = 0; + table2->navigate(QAccessible::Child, 1, &toprow); + QVERIFY(toprow); + QCOMPARE(toprow->role(1), QAccessible::RowHeader); + QCOMPARE(toprow->role(2), QAccessible::ColumnHeader); + delete toprow; + + // call childAt() for each child until we reach the bottom, + // and do it for each row in the table + for (int y = 1; y < 5; ++y) { // this includes the special header + for (int x = 1; x < 6; ++x) { + QCOMPARE(client->role(), QAccessible::Client); + QRect globalRect = client->rect(); + QVERIFY(globalRect.isValid()); + // make sure we don't hit the vertical header ##### + QPoint p = globalRect.topLeft() + QPoint(8, 8); + p.ry() += 50 * (y - 1); + p.rx() += 100 * (x - 1); + int index = client->childAt(p.x(), p.y()); + QCOMPARE(index, 1); + QCOMPARE(client->role(index), QAccessible::Table); + + // navigate to table/viewport + QAccessibleInterface *table; + client->navigate(QAccessible::Child, index, &table); + QVERIFY(table); + index = table->childAt(p.x(), p.y()); + QCOMPARE(index, y); + QCOMPARE(table->role(index), QAccessible::Row); + QAccessibleInterface *row; + QCOMPARE(table->role(1), QAccessible::Row); + + // navigate to the row + table->navigate(QAccessible::Child, index, &row); + QVERIFY(row); + QCOMPARE(row->role(1), QAccessible::RowHeader); + index = row->childAt(p.x(), p.y()); + QVERIFY(index > 0); + if (x == 1 && y == 1) { + QCOMPARE(row->role(index), QAccessible::RowHeader); + QCOMPARE(row->text(QAccessible::Name, index), QLatin1String("")); + } else if (x > 1 && y > 1) { + QCOMPARE(row->role(index), QAccessible::Cell); + QCOMPARE(row->text(QAccessible::Name, index), QString::fromAscii("[%1,%2,0]").arg(y - 2).arg(x - 2)); + } else if (x == 1) { + QCOMPARE(row->role(index), QAccessible::RowHeader); + QCOMPARE(row->text(QAccessible::Name, index), QString::fromAscii("%1").arg(y - 1)); + } else if (y == 1) { + QCOMPARE(row->role(index), QAccessible::ColumnHeader); + QCOMPARE(row->text(QAccessible::Name, index), QString::fromAscii("%1").arg(x - 1)); + } + delete table; + delete row; + } + } + delete table2; + delete client; + delete w; + delete model; + } + QTestAccessibility::clearEvents(); +} + +#else +// Test accessible table2 interface on unix + +void tst_QAccessibility::table2ListTest() +{ + QListWidget *listView = new QListWidget; + listView->addItem("Oslo"); + listView->addItem("Berlin"); + listView->addItem("Brisbane"); + listView->resize(400,400); + listView->show(); + QTest::qWait(1); // Need this for indexOfchild to work. + QCoreApplication::processEvents(); + QTest::qWait(100); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(listView); + QCOMPARE(verifyHierarchy(iface), 0); + + QCOMPARE((int)iface->role(), (int)QAccessible::List); + QCOMPARE(iface->childCount(), 3); + + QAccessibleInterface *child1 = 0; + child1 = iface->child(0); + QVERIFY(child1); + QCOMPARE(iface->indexOfChild(child1), 1); + QCOMPARE(child1->text(QAccessible::Name, 0), QString("Oslo")); + QCOMPARE(child1->role(), QAccessible::ListItem); + delete child1; + + QAccessibleInterface *child2 = 0; + child2 = iface->child(1); + QVERIFY(child2); + QCOMPARE(iface->indexOfChild(child2), 2); + QCOMPARE(child2->text(QAccessible::Name, 0), QString("Berlin")); + delete child2; + + QAccessibleInterface *child3 = 0; + child3 = iface->child(2); + QVERIFY(child3); + QCOMPARE(iface->indexOfChild(child3), 3); + QCOMPARE(child3->text(QAccessible::Name, 0), QString("Brisbane")); + delete child3; + QTestAccessibility::clearEvents(); + + // Check for events + QTest::mouseClick(listView->viewport(), Qt::LeftButton, 0, listView->visualItemRect(listView->item(1)).center()); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(listView, 2, QAccessible::Selection))); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(listView, 2, QAccessible::Focus))); + QTest::mouseClick(listView->viewport(), Qt::LeftButton, 0, listView->visualItemRect(listView->item(2)).center()); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(listView, 3, QAccessible::Selection))); + QVERIFY(QTestAccessibility::events().contains(QTestAccessibilityEvent(listView, 3, QAccessible::Focus))); + + listView->addItem("Munich"); + QCOMPARE(iface->childCount(), 4); + + // table 2 + QAccessibleTable2Interface *table2 = iface->table2Interface(); + QVERIFY(table2); + QCOMPARE(table2->columnCount(), 1); + QCOMPARE(table2->rowCount(), 4); + QAccessibleTable2CellInterface *cell1; + QVERIFY(cell1 = table2->cellAt(0,0)); + QCOMPARE(cell1->text(QAccessible::Name, 0), QString("Oslo")); + QAccessibleTable2CellInterface *cell4; + QVERIFY(cell4 = table2->cellAt(3,0)); + QCOMPARE(cell4->text(QAccessible::Name, 0), QString("Munich")); + QCOMPARE(cell4->role(), QAccessible::ListItem); + QCOMPARE(cell4->rowIndex(), 3); + QCOMPARE(cell4->columnIndex(), 0); + QVERIFY(!cell4->isExpandable()); + + delete cell4; + delete cell1; + delete iface; + delete listView; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::table2TreeTest() +{ + QTreeWidget *treeView = new QTreeWidget; + treeView->setColumnCount(2); + QTreeWidgetItem *header = new QTreeWidgetItem; + header->setText(0, "Artist"); + header->setText(1, "Work"); + treeView->setHeaderItem(header); + + QTreeWidgetItem *root1 = new QTreeWidgetItem; + root1->setText(0, "Spain"); + treeView->addTopLevelItem(root1); + + QTreeWidgetItem *item1 = new QTreeWidgetItem; + item1->setText(0, "Picasso"); + item1->setText(1, "Guernica"); + root1->addChild(item1); + + QTreeWidgetItem *item2 = new QTreeWidgetItem; + item2->setText(0, "Tapies"); + item2->setText(1, "Ambrosia"); + root1->addChild(item2); + + QTreeWidgetItem *root2 = new QTreeWidgetItem; + root2->setText(0, "Austria"); + treeView->addTopLevelItem(root2); + + QTreeWidgetItem *item3 = new QTreeWidgetItem; + item3->setText(0, "Klimt"); + item3->setText(1, "The Kiss"); + root2->addChild(item3); + + treeView->resize(400,400); + treeView->show(); + + QCoreApplication::processEvents(); + QTest::qWait(100); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(treeView); + QCOMPARE(verifyHierarchy(iface), 0); + + QCOMPARE((int)iface->role(), (int)QAccessible::Tree); + // header and 2 rows (the others are not expanded, thus not visible) + QCOMPARE(iface->childCount(), 6); + + QAccessibleInterface *header1 = 0; + header1 = iface->child(0); + QVERIFY(header1); + QCOMPARE(iface->indexOfChild(header1), 1); + QCOMPARE(header1->text(QAccessible::Name, 0), QString("Artist")); + QCOMPARE(header1->role(), QAccessible::ColumnHeader); + delete header1; + + QAccessibleInterface *child1 = 0; + child1 = iface->child(2); + QVERIFY(child1); + QCOMPARE(iface->indexOfChild(child1), 3); + QCOMPARE(child1->text(QAccessible::Name, 0), QString("Spain")); + QCOMPARE(child1->role(), QAccessible::TreeItem); + QVERIFY(!(child1->state() & QAccessible::Expanded)); + delete child1; + + QAccessibleInterface *child2 = 0; + child2 = iface->child(4); + QVERIFY(child2); + QCOMPARE(iface->indexOfChild(child2), 5); + QCOMPARE(child2->text(QAccessible::Name, 0), QString("Austria")); + delete child2; + + QTestAccessibility::clearEvents(); + + // table 2 + QAccessibleTable2Interface *table2 = iface->table2Interface(); + QVERIFY(table2); + QCOMPARE(table2->columnCount(), 2); + QCOMPARE(table2->rowCount(), 2); + QAccessibleTable2CellInterface *cell1; + QVERIFY(cell1 = table2->cellAt(0,0)); + QCOMPARE(cell1->text(QAccessible::Name, 0), QString("Spain")); + QAccessibleTable2CellInterface *cell2; + QVERIFY(cell2 = table2->cellAt(1,0)); + QCOMPARE(cell2->text(QAccessible::Name, 0), QString("Austria")); + QCOMPARE(cell2->role(), QAccessible::TreeItem); + QCOMPARE(cell2->rowIndex(), 1); + QCOMPARE(cell2->columnIndex(), 0); + QVERIFY(cell2->isExpandable()); + QCOMPARE(iface->indexOfChild(cell2), 5); + QVERIFY(!(cell2->state() & QAccessible::Expanded)); + QCOMPARE(table2->columnDescription(1), QString("Work")); + delete cell2; + delete cell1; + + treeView->expandAll(); + + // Need this for indexOfchild to work. + QCoreApplication::processEvents(); + QTest::qWait(100); + + QCOMPARE(table2->columnCount(), 2); + QCOMPARE(table2->rowCount(), 5); + cell1 = table2->cellAt(1,0); + QCOMPARE(cell1->text(QAccessible::Name, 0), QString("Picasso")); + QCOMPARE(iface->indexOfChild(cell1), 5); // 1 based + 2 header + 2 for root item + + cell2 = table2->cellAt(4,0); + QCOMPARE(cell2->text(QAccessible::Name, 0), QString("Klimt")); + QCOMPARE(cell2->role(), QAccessible::TreeItem); + QCOMPARE(cell2->rowIndex(), 4); + QCOMPARE(cell2->columnIndex(), 0); + QVERIFY(!cell2->isExpandable()); + QCOMPARE(iface->indexOfChild(cell2), 11); + + QCOMPARE(table2->columnDescription(0), QString("Artist")); + QCOMPARE(table2->columnDescription(1), QString("Work")); + + delete iface; + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::table2TableTest() +{ + QTableWidget *tableView = new QTableWidget(3, 3); + tableView->setColumnCount(3); + QStringList hHeader; + hHeader << "h1" << "h2" << "h3"; + tableView->setHorizontalHeaderLabels(hHeader); + + QStringList vHeader; + vHeader << "v1" << "v2" << "v3"; + tableView->setVerticalHeaderLabels(vHeader); + + for (int i = 0; i<9; ++i) { + QTableWidgetItem *item = new QTableWidgetItem; + item->setText(QString::number(i/3) + QString(".") + QString::number(i%3)); + tableView->setItem(i/3, i%3, item); + } + + tableView->resize(600,600); + tableView->show(); + QTest::qWait(1); // Need this for indexOfchild to work. + QCoreApplication::processEvents(); + QTest::qWait(100); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(tableView); + QCOMPARE(verifyHierarchy(iface), 0); + + QCOMPARE((int)iface->role(), (int)QAccessible::Table); + // header and 2 rows (the others are not expanded, thus not visible) + QCOMPARE(iface->childCount(), 9+3+3+1); // cell+headers+topleft button + + QAccessibleInterface *cornerButton = iface->child(0); + QVERIFY(cornerButton); + QCOMPARE(iface->indexOfChild(cornerButton), 1); + QCOMPARE(cornerButton->role(), QAccessible::Pane); + delete cornerButton; + + QAccessibleInterface *child1 = iface->child(2); + QVERIFY(child1); + QCOMPARE(iface->indexOfChild(child1), 3); + QCOMPARE(child1->text(QAccessible::Name, 0), QString("h2")); + QCOMPARE(child1->role(), QAccessible::ColumnHeader); + QVERIFY(!(child1->state() & QAccessible::Expanded)); + delete child1; + + QAccessibleInterface *child2 = iface->child(10); + QVERIFY(child2); + QCOMPARE(iface->indexOfChild(child2), 11); + QCOMPARE(child2->text(QAccessible::Name, 0), QString("1.1")); + QAccessibleTable2CellInterface *cell2Iface = static_cast(child2); + QCOMPARE(cell2Iface->rowIndex(), 1); + QCOMPARE(cell2Iface->columnIndex(), 1); + delete child2; + + QAccessibleInterface *child3 = iface->child(11); + QCOMPARE(iface->indexOfChild(child3), 12); + QCOMPARE(child3->text(QAccessible::Name, 0), QString("1.2")); + delete child3; + + QTestAccessibility::clearEvents(); + + // table 2 + QAccessibleTable2Interface *table2 = iface->table2Interface(); + QVERIFY(table2); + QCOMPARE(table2->columnCount(), 3); + QCOMPARE(table2->rowCount(), 3); + QAccessibleTable2CellInterface *cell1; + QVERIFY(cell1 = table2->cellAt(0,0)); + QCOMPARE(cell1->text(QAccessible::Name, 0), QString("0.0")); + QCOMPARE(iface->indexOfChild(cell1), 6); + + QAccessibleTable2CellInterface *cell2; + QVERIFY(cell2 = table2->cellAt(0,1)); + QCOMPARE(cell2->text(QAccessible::Name, 0), QString("0.1")); + QCOMPARE(cell2->role(), QAccessible::Cell); + QCOMPARE(cell2->rowIndex(), 0); + QCOMPARE(cell2->columnIndex(), 1); + QCOMPARE(iface->indexOfChild(cell2), 7); + delete cell2; + + QAccessibleTable2CellInterface *cell3; + QVERIFY(cell3 = table2->cellAt(1,2)); + QCOMPARE(cell3->text(QAccessible::Name, 0), QString("1.2")); + QCOMPARE(cell3->role(), QAccessible::Cell); + QCOMPARE(cell3->rowIndex(), 1); + QCOMPARE(cell3->columnIndex(), 2); + QCOMPARE(iface->indexOfChild(cell3), 12); + delete cell3; + + QCOMPARE(table2->columnDescription(0), QString("h1")); + QCOMPARE(table2->columnDescription(1), QString("h2")); + QCOMPARE(table2->columnDescription(2), QString("h3")); + QCOMPARE(table2->rowDescription(0), QString("v1")); + QCOMPARE(table2->rowDescription(1), QString("v2")); + QCOMPARE(table2->rowDescription(2), QString("v3")); + + delete iface; + + delete tableView; + + QTestAccessibility::clearEvents(); +} +#endif + +void tst_QAccessibility::calendarWidgetTest() +{ +#ifndef QT_NO_CALENDARWIDGET + { + QCalendarWidget calendarWidget; + + QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&calendarWidget); + QVERIFY(interface); + QCOMPARE(interface->role(), QAccessible::Table); + QVERIFY(!interface->rect().isValid()); + QVERIFY(!interface->rect(1).isValid()); + QCOMPARE(interface->childAt(200, 200), -1); + + calendarWidget.resize(400, 300); + calendarWidget.show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + + // 1 = navigationBar, 2 = view. + QCOMPARE(interface->childCount(), 2); + + const QRect globalGeometry = QRect(calendarWidget.mapToGlobal(QPoint(0, 0)), + calendarWidget.size()); + QCOMPARE(interface->rect(), globalGeometry); + + QWidget *navigationBar = 0; + foreach (QObject *child, calendarWidget.children()) { + if (child->objectName() == QLatin1String("qt_calendar_navigationbar")) { + navigationBar = static_cast(child); + break; + } + } + QVERIFY(navigationBar); + QVERIFY(verifyChild(navigationBar, interface, 1, globalGeometry)); + + QAbstractItemView *calendarView = 0; + foreach (QObject *child, calendarWidget.children()) { + if (child->objectName() == QLatin1String("qt_calendar_calendarview")) { + calendarView = static_cast(child); + break; + } + } + QVERIFY(calendarView); + QVERIFY(verifyChild(calendarView, interface, 2, globalGeometry)); + + // Hide navigation bar. + calendarWidget.setNavigationBarVisible(false); + QCOMPARE(interface->childCount(), 1); + QVERIFY(!navigationBar->isVisible()); + + QVERIFY(verifyChild(calendarView, interface, 1, globalGeometry)); + + // Show navigation bar. + calendarWidget.setNavigationBarVisible(true); + QCOMPARE(interface->childCount(), 2); + QVERIFY(navigationBar->isVisible()); + + // Navigate to the navigation bar via Child. + QAccessibleInterface *navigationBarInterface = interface->child(0); + QVERIFY(navigationBarInterface); + QCOMPARE(navigationBarInterface->object(), (QObject*)navigationBar); + delete navigationBarInterface; + navigationBarInterface = 0; + + // Navigate to the view via Child. + QAccessibleInterface *calendarViewInterface = interface->child(1); + QVERIFY(calendarViewInterface); + QCOMPARE(calendarViewInterface->object(), (QObject*)calendarView); + delete calendarViewInterface; + calendarViewInterface = 0; + + QVERIFY(!interface->child(-1)); + + // Navigate from navigation bar -> view (Down). + QCOMPARE(interface->navigate(QAccessible::Down, 1, &calendarViewInterface), 0); + QVERIFY(calendarViewInterface); + QCOMPARE(calendarViewInterface->object(), (QObject*)calendarView); + delete calendarViewInterface; + calendarViewInterface = 0; + + // Navigate from view -> navigation bar (Up). + QCOMPARE(interface->navigate(QAccessible::Up, 2, &navigationBarInterface), 0); + QVERIFY(navigationBarInterface); + QCOMPARE(navigationBarInterface->object(), (QObject*)navigationBar); + delete navigationBarInterface; + navigationBarInterface = 0; + + } + QTestAccessibility::clearEvents(); +#endif // QT_NO_CALENDARWIDGET +} + +void tst_QAccessibility::dockWidgetTest() +{ +#ifndef QT_NO_DOCKWIDGET + // Set up a proper main window with two dock widgets + QMainWindow *mw = new QMainWindow(); + QFrame *central = new QFrame(mw); + mw->setCentralWidget(central); + QMenuBar *mb = new QMenuBar(mw); + mb->addAction(tr("&File")); + mw->setMenuBar(mb); + + QDockWidget *dock1 = new QDockWidget(mw); + mw->addDockWidget(Qt::LeftDockWidgetArea, dock1); + QPushButton *pb1 = new QPushButton(tr("Push me"), dock1); + dock1->setWidget(pb1); + + QDockWidget *dock2 = new QDockWidget(mw); + mw->addDockWidget(Qt::BottomDockWidgetArea, dock2); + QPushButton *pb2 = new QPushButton(tr("Push me"), dock2); + dock2->setWidget(pb2); + + mw->resize(600,400); + mw->show(); +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); + QTest::qWait(100); +#endif + + QAccessibleInterface *accMainWindow = QAccessible::queryAccessibleInterface(mw); + // 4 children: menu bar, dock1, dock2, and central widget + QCOMPARE(accMainWindow->childCount(), 4); + QAccessibleInterface *accDock1 = 0; + for (int i = 1; i <= 4; ++i) { + if (accMainWindow->role(i) == QAccessible::Window) { + accDock1 = accMainWindow->child(i-1); + if (accDock1 && qobject_cast(accDock1->object()) == dock1) { + break; + } else { + delete accDock1; + } + } + } + QVERIFY(accDock1); + QCOMPARE(accDock1->role(), QAccessible::Window); + QCOMPARE(accDock1->role(1), QAccessible::TitleBar); + QVERIFY(accDock1->rect().contains(accDock1->rect(1))); + + QPoint globalPos = dock1->mapToGlobal(QPoint(0,0)); + globalPos.rx()+=5; //### query style + globalPos.ry()+=5; + int entry = accDock1->childAt(globalPos.x(), globalPos.y()); //### + QCOMPARE(entry, 1); + QAccessibleInterface *accTitleBar = accDock1->child(entry - 1); + + QCOMPARE(accTitleBar->role(), QAccessible::TitleBar); + QCOMPARE(accDock1->indexOfChild(accTitleBar), 1); + QAccessibleInterface *acc; + acc = accTitleBar->parent(); + QVERIFY(acc); + QCOMPARE(acc->role(), QAccessible::Window); + + + delete accTitleBar; + delete accDock1; + delete pb1; + delete pb2; + delete dock1; + delete dock2; + delete mw; + QTestAccessibility::clearEvents(); +#endif // QT_NO_DOCKWIDGET +} + +void tst_QAccessibility::comboBoxTest() +{ +#if defined(Q_OS_WINCE) + if (!IsValidCEPlatform()) { + QSKIP("Test skipped on Windows Mobile test hardware", SkipAll); + } +#endif + { // not editable combobox + QComboBox combo; + combo.addItems(QStringList() << "one" << "two" << "three"); + combo.show(); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&combo); + QCOMPARE(verifyHierarchy(iface), 0); + + QCOMPARE(iface->role(), QAccessible::ComboBox); + QCOMPARE(iface->childCount(), 1); + +#ifdef Q_OS_UNIX + QCOMPARE(iface->text(QAccessible::Name), QLatin1String("one")); +#endif + QCOMPARE(iface->text(QAccessible::Value), QLatin1String("one")); + combo.setCurrentIndex(2); +#ifdef Q_OS_UNIX + QCOMPARE(iface->text(QAccessible::Name), QLatin1String("three")); +#endif + QCOMPARE(iface->text(QAccessible::Value), QLatin1String("three")); + + QAccessibleInterface *listIface = iface->child(0); + QCOMPARE(listIface->role(), QAccessible::List); + QCOMPARE(listIface->childCount(), 3); + + QVERIFY(!combo.view()->isVisible()); + QVERIFY(iface->actionInterface()); + QCOMPARE(iface->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::ShowMenuAction); + iface->actionInterface()->doAction(QAccessibleActionInterface::ShowMenuAction); + QVERIFY(combo.view()->isVisible()); + + delete iface; + } + + { // editable combobox + QComboBox editableCombo; + editableCombo.show(); + editableCombo.setEditable(true); + editableCombo.addItems(QStringList() << "foo" << "bar" << "baz"); + + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&editableCombo); + QCOMPARE(verifyHierarchy(iface), 0); + + QCOMPARE(iface->role(), QAccessible::ComboBox); + QCOMPARE(iface->childCount(), 2); + + QAccessibleInterface *listIface = iface->child(0); + QCOMPARE(listIface->role(), QAccessible::List); + QAccessibleInterface *editIface = iface->child(1); + QCOMPARE(editIface->role(), QAccessible::EditableText); + + delete listIface; + delete editIface; + delete iface; + } + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::labelTest() +{ + QString text = "Hello World"; + QLabel *label = new QLabel(text); + label->show(); + +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); +#endif + QTest::qWait(100); + + QAccessibleInterface *acc_label = QAccessible::queryAccessibleInterface(label); + QVERIFY(acc_label); + + QCOMPARE(acc_label->text(QAccessible::Name, 0), text); + + delete acc_label; + delete label; + QTestAccessibility::clearEvents(); + + QPixmap testPixmap(50, 50); + testPixmap.fill(); + + QLabel imageLabel; + imageLabel.setPixmap(testPixmap); + imageLabel.setToolTip("Test Description"); + + acc_label = QAccessible::queryAccessibleInterface(&imageLabel); + QVERIFY(acc_label); + + QAccessibleImageInterface *imageInterface = acc_label->imageInterface(); + QVERIFY(imageInterface); + + QCOMPARE(imageInterface->imageSize(), testPixmap.size()); + QCOMPARE(imageInterface->imageDescription(), QString::fromLatin1("Test Description")); + QCOMPARE(imageInterface->imagePosition(QAccessible2::RelativeToParent), imageLabel.geometry()); + + delete acc_label; + + QTestAccessibility::clearEvents(); +} + +void tst_QAccessibility::accelerators() +{ + QWidget *window = new QWidget; + QHBoxLayout *lay = new QHBoxLayout(window); + QLabel *label = new QLabel(tr("&Line edit"), window); + QLineEdit *le = new QLineEdit(window); + lay->addWidget(label); + lay->addWidget(le); + label->setBuddy(le); + + window->show(); + + QAccessibleInterface *accLineEdit = QAccessible::queryAccessibleInterface(le); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + QLatin1String("L")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + QLatin1String("L")); + label->setText(tr("Q &")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QString()); + label->setText(tr("Q &&")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QString()); + label->setText(tr("Q && A")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QString()); + label->setText(tr("Q &&&A")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + QLatin1String("A")); + label->setText(tr("Q &&A")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QString()); + +#if !defined(QT_NO_DEBUG) && !defined(Q_WS_MAC) + QTest::ignoreMessage(QtWarningMsg, "QKeySequence::mnemonic: \"Q &A&B\" contains multiple occurrences of '&'"); +#endif + label->setText(tr("Q &A&B")); + QCOMPARE(accLineEdit->text(QAccessible::Accelerator, 0), QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + QLatin1String("A")); + +#if defined(Q_OS_UNIX) + QCoreApplication::processEvents(); +#endif + QTest::qWait(100); + delete window; + QTestAccessibility::clearEvents(); +} + +QTEST_MAIN(tst_QAccessibility) +#include "tst_qaccessibility.moc" -- cgit v1.2.3