1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtCore/qglobal.h>
31#ifdef Q_OS_WIN
32# include <QtCore/qt_windows.h>
33#ifndef Q_OS_WINRT
34# include <oleacc.h>
35# include <QtWindowsUIAutomationSupport/private/qwindowsuiawrapper_p.h>
36#endif
37# include <servprov.h>
38# include <winuser.h>
39#endif
40#include <QtTest/QtTest>
41#include <QtGui>
42#include <QtWidgets>
43#include <math.h>
44#include <qpa/qplatformnativeinterface.h>
45#include <qpa/qplatformintegration.h>
46#include <qpa/qplatformaccessibility.h>
47#include <QtGui/private/qguiapplication_p.h>
48#include <QtGui/private/qhighdpiscaling_p.h>
49
50#if defined(Q_OS_WIN) && defined(interface)
51# undef interface
52#endif
53
54#include "QtTest/qtestaccessible.h"
55
56#include <algorithm>
57
58#include "accessiblewidgets.h"
59
60#include <QtTest/private/qtesthelpers_p.h>
61
62using namespace QTestPrivate;
63
64static inline bool verifyChild(QWidget *child, QAccessibleInterface *interface,
65 int index, const QRect &domain)
66{
67 if (!child) {
68 qWarning("tst_QAccessibility::verifyChild: null pointer to child.");
69 return false;
70 }
71
72 if (!interface) {
73 qWarning("tst_QAccessibility::verifyChild: null pointer to interface.");
74 return false;
75 }
76
77 // Verify that we get a valid QAccessibleInterface for the child.
78 QAccessibleInterface *childInterface(QAccessible::queryAccessibleInterface(child));
79 if (!childInterface) {
80 qWarning("tst_QAccessibility::verifyChild: Failed to retrieve interface for child.");
81 return false;
82 }
83
84 // QAccessibleInterface::indexOfChild():
85 // Verify that indexOfChild() returns an index equal to the index passed in
86 int indexFromIndexOfChild = interface->indexOfChild(childInterface);
87 if (indexFromIndexOfChild != index) {
88 qWarning("tst_QAccessibility::verifyChild (indexOfChild()):");
89 qWarning() << "Expected:" << index;
90 qWarning() << "Actual: " << indexFromIndexOfChild;
91 return false;
92 }
93
94 // Navigate to child, compare its object and role with the interface from queryAccessibleInterface(child).
95 QAccessibleInterface *navigatedChildInterface(interface->child(index));
96 if (!navigatedChildInterface)
97 return false;
98
99 const QRect rectFromInterface = navigatedChildInterface->rect();
100
101 // QAccessibleInterface::childAt():
102 // Calculate global child position and check that the interface
103 // returns the correct index for that position.
104 QPoint globalChildPos = child->mapToGlobal(QPoint(0, 0));
105 QAccessibleInterface *childAtInterface(interface->childAt(globalChildPos.x(), globalChildPos.y()));
106 if (!childAtInterface) {
107 qWarning("tst_QAccessibility::verifyChild (childAt()):");
108 qWarning() << "Expected:" << childInterface;
109 qWarning() << "Actual: no child";
110 return false;
111 }
112 if (childAtInterface->object() != childInterface->object()) {
113 qWarning("tst_QAccessibility::verifyChild (childAt()):");
114 qWarning() << "Expected:" << childInterface;
115 qWarning() << "Actual: " << childAtInterface;
116 return false;
117 }
118
119 // QAccessibleInterface::rect():
120 // Calculate global child geometry and check that the interface
121 // returns a QRect which is equal to the calculated QRect.
122 const QRect expectedGlobalRect = QRect(globalChildPos, child->size());
123 if (expectedGlobalRect != rectFromInterface) {
124 qWarning("tst_QAccessibility::verifyChild (rect()):");
125 qWarning() << "Expected:" << expectedGlobalRect;
126 qWarning() << "Actual: " << rectFromInterface;
127 return false;
128 }
129
130 // Verify that the child is within its domain.
131 if (!domain.contains(rectFromInterface)) {
132 qWarning("tst_QAccessibility::verifyChild: Child is not within its domain.");
133 return false;
134 }
135
136 return true;
137}
138
139#define EXPECT(cond) \
140 do { \
141 if (!errorAt && !(cond)) { \
142 errorAt = __LINE__; \
143 qWarning("level: %d, role: %d (%s)", treelevel, iface->role(), #cond); \
144 break; \
145 } \
146 } while (0)
147
148static int verifyHierarchy(QAccessibleInterface *iface)
149{
150 int errorAt = 0;
151 static int treelevel = 0; // for error diagnostics
152 QAccessibleInterface *if2 = 0;
153 ++treelevel;
154 for (int i = 0; i < iface->childCount() && !errorAt; ++i) {
155 if2 = iface->child(i);
156 EXPECT(if2 != 0);
157 EXPECT(iface->indexOfChild(if2) == i);
158 // navigate Ancestor
159 QAccessibleInterface *parent = if2->parent();
160 EXPECT(iface->object() == parent->object());
161 EXPECT(iface == parent);
162
163 // verify children
164 if (!errorAt)
165 errorAt = verifyHierarchy(if2);
166 }
167
168 --treelevel;
169 return errorAt;
170}
171
172QRect childRect(QAccessibleInterface *iface, int index = 0)
173{
174 return iface->child(index)->rect();
175}
176
177class tst_QAccessibility : public QObject
178{
179 Q_OBJECT
180public:
181 tst_QAccessibility();
182
183public slots:
184 void initTestCase();
185 void cleanupTestCase();
186 void init();
187 void cleanup();
188private slots:
189 void eventTest();
190 void customWidget();
191 void deletedWidget();
192 void subclassedWidget();
193
194 void statesStructTest();
195 void navigateHierarchy();
196 void sliderTest();
197 void textAttributes_data();
198 void textAttributes();
199 void hideShowTest();
200
201 void actionTest();
202
203 void applicationTest();
204 void mainWindowTest();
205 void subWindowTest();
206 void buttonTest();
207 void scrollBarTest();
208 void tabTest();
209 void tabWidgetTest();
210 void menuTest();
211 void spinBoxTest();
212 void doubleSpinBoxTest();
213 void textEditTest();
214 void textBrowserTest();
215 void mdiAreaTest();
216 void mdiSubWindowTest();
217 void lineEditTest();
218 void lineEditTextFunctions_data();
219 void lineEditTextFunctions();
220 void textInterfaceTest_data();
221 void textInterfaceTest();
222 void groupBoxTest();
223 void dialogButtonBoxTest();
224 void dialTest();
225 void rubberBandTest();
226 void abstractScrollAreaTest();
227 void scrollAreaTest();
228
229 void listTest();
230 void treeTest();
231 void tableTest();
232
233 void calendarWidgetTest();
234 void dockWidgetTest();
235 void comboBoxTest();
236 void accessibleName();
237 void labelTest();
238 void accelerators();
239 void bridgeTest();
240
241protected slots:
242 void onClicked();
243private:
244 int click_count;
245};
246
247QAccessible::State state(QWidget * const widget)
248{
249 QAccessibleInterface *iface(QAccessible::queryAccessibleInterface(widget));
250 if (!iface) {
251 qWarning() << "Cannot get QAccessibleInterface for widget";
252 return QAccessible::State();
253 }
254 return iface->state();
255}
256
257tst_QAccessibility::tst_QAccessibility()
258{
259 click_count = 0;
260}
261
262void tst_QAccessibility::onClicked()
263{
264 click_count++;
265}
266
267void tst_QAccessibility::initTestCase()
268{
269 QTestAccessibility::initialize();
270 QPlatformIntegration *pfIntegration = QGuiApplicationPrivate::platformIntegration();
271 if (!pfIntegration->accessibility())
272 QSKIP("This platform does not support accessibility");
273 pfIntegration->accessibility()->setActive(true);
274}
275
276void tst_QAccessibility::cleanupTestCase()
277{
278 QTestAccessibility::cleanup();
279}
280
281void tst_QAccessibility::init()
282{
283 QTestAccessibility::clearEvents();
284}
285
286void tst_QAccessibility::cleanup()
287{
288 const EventList list = QTestAccessibility::events();
289 if (!list.isEmpty()) {
290 qWarning("%d accessibility event(s) were not handled in testfunction '%s':", list.count(),
291 QString(QTest::currentTestFunction()).toLatin1().constData());
292 for (int i = 0; i < list.count(); ++i)
293 qWarning(" %d: Object: %p Event: '%s' Child: %d", i + 1, list.at(i)->object(),
294 qAccessibleEventString(list.at(i)->type()), list.at(i)->child());
295 }
296 QTestAccessibility::clearEvents();
297 QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty());
298}
299
300void tst_QAccessibility::eventTest()
301{
302 QPushButton* button = new QPushButton(0);
303 QAccessible::queryAccessibleInterface(button);
304 button->setObjectName(QString("Olaf"));
305 setFrameless(button);
306
307 button->show();
308 QAccessibleEvent showEvent(button, QAccessible::ObjectShow);
309 // some platforms might send other events first, (such as state change event active=1)
310 QVERIFY(QTestAccessibility::containsEvent(&showEvent));
311 button->setFocus(Qt::MouseFocusReason);
312 QTestAccessibility::clearEvents();
313 QTest::mouseClick(button, Qt::LeftButton, 0);
314
315 button->setAccessibleName("Olaf the second");
316 QAccessibleEvent nameEvent(button, QAccessible::NameChanged);
317 QVERIFY_EVENT(&nameEvent);
318 button->setAccessibleDescription("This is a button labeled Olaf");
319 QAccessibleEvent descEvent(button, QAccessible::DescriptionChanged);
320 QVERIFY_EVENT(&descEvent);
321
322 button->hide();
323 QAccessibleEvent hideEvent(button, QAccessible::ObjectHide);
324 // some platforms might send other events first, (such as state change event active=1)
325 QVERIFY(QTestAccessibility::containsEvent(&hideEvent));
326
327 delete button;
328
329 // Make sure that invalid events don't bring down the system
330 // these events can be in user code.
331 QWidget *widget = new QWidget();
332 QAccessibleEvent ev1(widget, QAccessible::Focus);
333 QAccessible::updateAccessibility(&ev1);
334
335 QAccessibleEvent ev2(widget, QAccessible::Focus);
336 ev2.setChild(7);
337 QAccessible::updateAccessibility(&ev2);
338 delete widget;
339
340 QObject *object = new QObject();
341 QAccessibleEvent ev3(object, QAccessible::Focus);
342 QAccessible::updateAccessibility(&ev3);
343 delete object;
344
345 QTestAccessibility::clearEvents();
346}
347
348void tst_QAccessibility::customWidget()
349{
350 {
351 QtTestAccessibleWidget* widget = new QtTestAccessibleWidget(0, "Heinz");
352 widget->show();
353 QVERIFY(QTest::qWaitForWindowExposed(widget));
354 // By default we create QAccessibleWidget
355 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(widget);
356 QVERIFY(iface != 0);
357 QVERIFY(iface->isValid());
358 QCOMPARE(iface->object(), (QObject*)widget);
359 QCOMPARE(iface->object()->objectName(), QString("Heinz"));
360 QCOMPARE(iface->rect().height(), widget->height());
361 QCOMPARE(iface->text(QAccessible::Help), QString());
362 QCOMPARE(iface->rect().height(), widget->height());
363 delete widget;
364 }
365 {
366 QAccessible::installFactory(QtTestAccessibleWidgetIface::ifaceFactory);
367 QtTestAccessibleWidget* widget = new QtTestAccessibleWidget(0, "Heinz");
368 widget->show();
369 QVERIFY(QTest::qWaitForWindowExposed(widget));
370 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(widget);
371 QVERIFY(iface != 0);
372 QVERIFY(iface->isValid());
373 QCOMPARE(iface->object(), (QObject*)widget);
374 QCOMPARE(iface->object()->objectName(), QString("Heinz"));
375 QCOMPARE(iface->rect().height(), widget->height());
376 // The help text is only set if our factory works
377 QCOMPARE(iface->text(QAccessible::Help), QString("Help yourself"));
378 delete widget;
379 }
380 {
381 // A subclass of any class should still get the right QAccessibleInterface
382 QtTestAccessibleWidgetSubclass* subclassedWidget = new QtTestAccessibleWidgetSubclass(0, "Hans");
383 QAccessibleInterface *subIface = QAccessible::queryAccessibleInterface(subclassedWidget);
384 QVERIFY(subIface != 0);
385 QVERIFY(subIface->isValid());
386 QCOMPARE(subIface->object(), (QObject*)subclassedWidget);
387 QCOMPARE(subIface->text(QAccessible::Help), QString("Help yourself"));
388 delete subclassedWidget;
389 }
390 QTestAccessibility::clearEvents();
391}
392
393void tst_QAccessibility::deletedWidget()
394{
395 QtTestAccessibleWidget *widget = new QtTestAccessibleWidget(0, "Ralf");
396 QAccessible::installFactory(QtTestAccessibleWidgetIface::ifaceFactory);
397 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(widget);
398 QVERIFY(iface != 0);
399 QVERIFY(iface->isValid());
400 QCOMPARE(iface->object(), (QObject*)widget);
401
402 delete widget;
403 widget = 0;
404 // fixme: QVERIFY(!iface->isValid());
405}
406
407void tst_QAccessibility::subclassedWidget()
408{
409 KFooButton button("Ploink", 0);
410 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&button);
411 QVERIFY(iface);
412 QCOMPARE(iface->object(), (QObject*)&button);
413 QCOMPARE(iface->text(QAccessible::Name), button.text());
414 QTestAccessibility::clearEvents();
415}
416
417void tst_QAccessibility::statesStructTest()
418{
419 QAccessible::State s1;
420 QVERIFY(s1.disabled == 0);
421 QVERIFY(s1.focusable == 0);
422 QVERIFY(s1.modal == 0);
423
424 QAccessible::State s2;
425 QCOMPARE(s2, s1);
426 s2.busy = true;
427 QVERIFY(!(s2 == s1));
428 s1.busy = true;
429 QCOMPARE(s2, s1);
430 s1 = QAccessible::State();
431 QVERIFY(!(s2 == s1));
432 s1 = s2;
433 QCOMPARE(s2, s1);
434 QVERIFY(s1.busy == 1);
435}
436
437void tst_QAccessibility::sliderTest()
438{
439 {
440 QSlider *slider = new QSlider(0);
441 setFrameless(slider);
442 slider->setObjectName(QString("Slidy"));
443 slider->show();
444 QAccessibleInterface *iface(QAccessible::queryAccessibleInterface(slider));
445 QVERIFY(iface);
446 QVERIFY(iface->isValid());
447
448 QCOMPARE(iface->childCount(), 0);
449 QCOMPARE(iface->role(), QAccessible::Slider);
450
451 QAccessibleValueInterface *valueIface = iface->valueInterface();
452 QVERIFY(valueIface != 0);
453 QCOMPARE(valueIface->minimumValue().toInt(), slider->minimum());
454 QCOMPARE(valueIface->maximumValue().toInt(), slider->maximum());
455 QCOMPARE(valueIface->minimumStepSize().toInt(), slider->singleStep());
456 slider->setValue(50);
457 QCOMPARE(valueIface->currentValue().toInt(), slider->value());
458 slider->setValue(0);
459 QCOMPARE(valueIface->currentValue().toInt(), slider->value());
460 slider->setValue(100);
461 QCOMPARE(valueIface->currentValue().toInt(), slider->value());
462 valueIface->setCurrentValue(77);
463 QCOMPARE(77, slider->value());
464 slider->setSingleStep(2);
465 QCOMPARE(valueIface->minimumStepSize().toInt(), 2);
466
467 delete slider;
468 }
469 QTestAccessibility::clearEvents();
470}
471
472void tst_QAccessibility::navigateHierarchy()
473{
474 {
475 QWidget *w = new QWidget(0);
476 w->setObjectName(QString("Hans"));
477 w->show();
478 QWidget *w1 = new QWidget(w);
479 w1->setObjectName(QString("1"));
480 w1->show();
481 QWidget *w2 = new QWidget(w);
482 w2->setObjectName(QString("2"));
483 w2->show();
484 QWidget *w3 = new QWidget(w);
485 w3->setObjectName(QString("3"));
486 w3->show();
487 QWidget *w31 = new QWidget(w3);
488 w31->setObjectName(QString("31"));
489 w31->show();
490
491 QAccessibleInterface *ifaceW(QAccessible::queryAccessibleInterface(w));
492 QVERIFY(ifaceW != 0);
493 QVERIFY(ifaceW->isValid());
494
495 QAccessibleInterface *target = ifaceW->child(14);
496 QVERIFY(!target);
497 target = ifaceW->child(-1);
498 QVERIFY(!target);
499 target = ifaceW->child(0);
500 QAccessibleInterface *interfaceW1(ifaceW->child(0));
501 QVERIFY(target);
502 QVERIFY(target->isValid());
503 QCOMPARE(target->object(), (QObject*)w1);
504 QVERIFY(interfaceW1 != 0);
505 QVERIFY(interfaceW1->isValid());
506 QCOMPARE(interfaceW1->object(), (QObject*)w1);
507
508 target = ifaceW->child(2);
509 QVERIFY(target != 0);
510 QVERIFY(target->isValid());
511 QCOMPARE(target->object(), (QObject*)w3);
512
513 QAccessibleInterface *child = target->child(1);
514 QVERIFY(!child);
515 child = target->child(0);
516 QVERIFY(child != 0);
517 QVERIFY(child->isValid());
518 QCOMPARE(child->object(), (QObject*)w31);
519
520 ifaceW = QAccessible::queryAccessibleInterface(w);
521 QAccessibleInterface *acc3(ifaceW->child(2));
522 target = acc3->child(0);
523 QCOMPARE(target->object(), (QObject*)w31);
524
525 QAccessibleInterface *parent = target->parent();
526 QVERIFY(parent != 0);
527 QVERIFY(parent->isValid());
528 QCOMPARE(parent->object(), (QObject*)w3);
529
530 delete w;
531 }
532 QTestAccessibility::clearEvents();
533}
534
535#define QSETCOMPARE(thetypename, elements, otherelements) \
536 QCOMPARE((QSet<thetypename>() << elements), (QSet<thetypename>() << otherelements))
537
538static QWidget *createWidgets()
539{
540 QWidget *w = new QWidget();
541
542 QHBoxLayout *box = new QHBoxLayout(w);
543
544 int i = 0;
545 box->addWidget(new QComboBox(w));
546 box->addWidget(new QPushButton(QLatin1String("widget text ") + QString::number(i++), w));
547 box->addWidget(new QHeaderView(Qt::Vertical, w));
548 box->addWidget(new QTreeView(w));
549 box->addWidget(new QTreeWidget(w));
550 box->addWidget(new QListView(w));
551 box->addWidget(new QListWidget(w));
552 box->addWidget(new QTableView(w));
553 box->addWidget(new QTableWidget(w));
554 box->addWidget(new QCalendarWidget(w));
555 box->addWidget(new QDialogButtonBox(w));
556 box->addWidget(new QGroupBox(QLatin1String("widget text ") + QString::number(i++), w));
557 box->addWidget(new QFrame(w));
558 box->addWidget(new QLineEdit(QLatin1String("widget text ") + QString::number(i++), w));
559 box->addWidget(new QProgressBar(w));
560 box->addWidget(new QTabWidget(w));
561 box->addWidget(new QCheckBox(QLatin1String("widget text ") + QString::number(i++), w));
562 box->addWidget(new QRadioButton(QLatin1String("widget text ") + QString::number(i++), w));
563 box->addWidget(new QDial(w));
564 box->addWidget(new QScrollBar(w));
565 box->addWidget(new QSlider(w));
566 box->addWidget(new QDateTimeEdit(w));
567 box->addWidget(new QDoubleSpinBox(w));
568 box->addWidget(new QSpinBox(w));
569 box->addWidget(new QLabel(QLatin1String("widget text ") + QString::number(i++), w));
570 box->addWidget(new QLCDNumber(w));
571 box->addWidget(new QStackedWidget(w));
572 box->addWidget(new QToolBox(w));
573 box->addWidget(new QLabel(QLatin1String("widget text ") + QString::number(i++), w));
574 box->addWidget(new QTextEdit(QLatin1String("widget text ") + QString::number(i++), w));
575
576 /* Not in the list
577 * QAbstractItemView, QGraphicsView, QScrollArea,
578 * QToolButton, QDockWidget, QFocusFrame, QMainWindow, QMenu, QMenuBar, QSizeGrip, QSplashScreen, QSplitterHandle,
579 * QStatusBar, QSvgWidget, QTabBar, QToolBar, QSplitter
580 */
581 return w;
582}
583
584void tst_QAccessibility::accessibleName()
585{
586 QWidget *toplevel = createWidgets();
587 toplevel->show();
588 QVERIFY(QTest::qWaitForWindowExposed(toplevel));
589
590 QLayout *lout = toplevel->layout();
591 for (int i = 0; i < lout->count(); i++) {
592 QLayoutItem *item = lout->itemAt(i);
593 QWidget *child = item->widget();
594
595 QString name = tr("Widget Name %1").arg(i);
596 child->setAccessibleName(name);
597 QAccessibleInterface *acc = QAccessible::queryAccessibleInterface(child);
598 QVERIFY(acc);
599 QCOMPARE(acc->text(QAccessible::Name), name);
600
601 QString desc = tr("Widget Description %1").arg(i);
602 child->setAccessibleDescription(desc);
603 QCOMPARE(acc->text(QAccessible::Description), desc);
604 }
605
606 delete toplevel;
607 QTestAccessibility::clearEvents();
608}
609
610// note: color should probably always be part of the attributes
611void tst_QAccessibility::textAttributes_data()
612{
613 QTest::addColumn<QFont>("defaultFont");
614 QTest::addColumn<QString>("text");
615 QTest::addColumn<int>("offset");
616 QTest::addColumn<int>("startOffsetResult");
617 QTest::addColumn<int>("endOffsetResult");
618 QTest::addColumn<QStringList>("attributeResult");
619
620 static QFont defaultFont;
621 defaultFont.setFamily("");
622 defaultFont.setPointSize(13);
623
624 static QFont defaultComplexFont = defaultFont;
625 defaultComplexFont.setFamily("Arial");
626 defaultComplexFont.setPointSize(20);
627 defaultComplexFont.setWeight(QFont::Bold);
628 defaultComplexFont.setStyle(QFont::StyleItalic);
629 defaultComplexFont.setUnderline(true);
630
631 static QStringList defaults = QString("font-style:normal;font-weight:normal;text-align:left;text-position:baseline;font-size:13pt").split(';');
632 static QStringList bold = defaults;
633 bold[1] = QString::fromLatin1("font-weight:bold");
634
635 static QStringList italic = defaults;
636 italic[0] = QString::fromLatin1("font-style:italic");
637
638 static QStringList boldItalic = defaults;
639 boldItalic[0] = QString::fromLatin1("font-style:italic");
640 boldItalic[1] = QString::fromLatin1("font-weight:bold");
641
642 static QStringList monospace = defaults;
643 monospace.append(QLatin1String("font-family:\"monospace\""));
644
645 static QStringList font8pt = defaults;
646 font8pt[4] = (QLatin1String("font-size:8pt"));
647
648 static QStringList color = defaults;
649 color << QLatin1String("color:rgb(240,241,242)") << QLatin1String("background-color:rgb(20,240,30)");
650
651 static QStringList rightAlign = defaults;
652 rightAlign[2] = QStringLiteral("text-align:right");
653
654 static QStringList defaultFontDifferent = defaults;
655 defaultFontDifferent[0] = QString::fromLatin1("font-style:italic");
656 defaultFontDifferent[1] = QString::fromLatin1("font-weight:bold");
657 defaultFontDifferent[4] = QString::fromLatin1("font-size:20pt");
658 defaultFontDifferent.append("text-underline-style:solid");
659 defaultFontDifferent.append("text-underline-type:single");
660 defaultFontDifferent.append("font-family:\"Arial\"");
661
662 static QStringList defaultFontDifferentBoldItalic = defaultFontDifferent;
663 defaultFontDifferentBoldItalic[0] = QString::fromLatin1("font-style:italic");
664 defaultFontDifferentBoldItalic[1] = QString::fromLatin1("font-weight:bold");
665
666 static QStringList defaultFontDifferentMonospace = defaultFontDifferent;
667 defaultFontDifferentMonospace[7] = (QLatin1String("font-family:\"monospace\""));
668
669 static QStringList defaultFontDifferentFont8pt = defaultFontDifferent;
670 defaultFontDifferentFont8pt[4] = (QLatin1String("font-size:8pt"));
671
672 static QStringList defaultFontDifferentColor = defaultFontDifferent;
673 defaultFontDifferentColor << QLatin1String("color:rgb(240,241,242)") << QLatin1String("background-color:rgb(20,240,30)");
674
675 QTest::newRow("defaults 1") << defaultFont << "hello" << 0 << 0 << 5 << defaults;
676 QTest::newRow("defaults 2") << defaultFont << "hello" << 1 << 0 << 5 << defaults;
677 QTest::newRow("defaults 3") << defaultFont << "hello" << 4 << 0 << 5 << defaults;
678 QTest::newRow("defaults 4") << defaultFont << "hello" << 5 << 0 << 5 << defaults;
679 QTest::newRow("offset -1 length") << defaultFont << "hello" << -1 << 0 << 5 << defaults;
680 QTest::newRow("offset -2 cursor pos") << defaultFont << "hello" << -2 << 0 << 5 << defaults;
681 QTest::newRow("offset -3") << defaultFont << "hello" << -3 << -1 << -1 << QStringList();
682 QTest::newRow("invalid offset 2") << defaultFont << "hello" << 6 << -1 << -1 << QStringList();
683 QTest::newRow("invalid offset 3") << defaultFont << "" << 1 << -1 << -1 << QStringList();
684
685 QString boldText = QLatin1String("<html><b>bold</b>text");
686 QTest::newRow("bold 0") << defaultFont << boldText << 0 << 0 << 4 << bold;
687 QTest::newRow("bold 2") << defaultFont << boldText << 2 << 0 << 4 << bold;
688 QTest::newRow("bold 3") << defaultFont << boldText << 3 << 0 << 4 << bold;
689 QTest::newRow("bold 4") << defaultFont << boldText << 4 << 4 << 8 << defaults;
690 QTest::newRow("bold 6") << defaultFont << boldText << 6 << 4 << 8 << defaults;
691
692 QString longText = QLatin1String("<html>"
693 "Hello, <b>this</b> is an <i><b>example</b> text</i>."
694 "<span style=\"font-family: monospace\">Multiple fonts are used.</span>"
695 "Multiple <span style=\"font-size: 8pt\">text sizes</span> are used."
696 "Let's give some color to <span style=\"color:#f0f1f2; background-color:#14f01e\">Qt</span>.");
697
698 QTest::newRow("default 5") << defaultFont << longText << 6 << 0 << 7 << defaults;
699 QTest::newRow("default 6") << defaultFont << longText << 7 << 7 << 11 << bold;
700 QTest::newRow("bold 7") << defaultFont << longText << 10 << 7 << 11 << bold;
701 QTest::newRow("bold 8") << defaultFont << longText << 10 << 7 << 11 << bold;
702 QTest::newRow("bold italic") << defaultFont << longText << 18 << 18 << 25 << boldItalic;
703 QTest::newRow("monospace") << defaultFont << longText << 34 << 31 << 55 << monospace;
704 QTest::newRow("8pt") << defaultFont << longText << 65 << 64 << 74 << font8pt;
705 QTest::newRow("color") << defaultFont << longText << 110 << 109 << 111 << color;
706
707 // make sure unset font properties default to those of document's default font
708 QTest::newRow("defaultFont default 5") << defaultComplexFont << longText << 6 << 0 << 7 << defaultFontDifferent;
709 QTest::newRow("defaultFont default 6") << defaultComplexFont << longText << 7 << 7 << 11 << defaultFontDifferent;
710 QTest::newRow("defaultFont bold 7") << defaultComplexFont << longText << 10 << 7 << 11 << defaultFontDifferent;
711 QTest::newRow("defaultFont bold 8") << defaultComplexFont << longText << 10 << 7 << 11 << defaultFontDifferent;
712 QTest::newRow("defaultFont bold italic") << defaultComplexFont << longText << 18 << 18 << 25 << defaultFontDifferentBoldItalic;
713 QTest::newRow("defaultFont monospace") << defaultComplexFont << longText << 34 << 31 << 55 << defaultFontDifferentMonospace;
714 QTest::newRow("defaultFont 8pt") << defaultComplexFont << longText << 65 << 64 << 74 << defaultFontDifferentFont8pt;
715 QTest::newRow("defaultFont color") << defaultComplexFont << longText << 110 << 109 << 111 << defaultFontDifferentColor;
716
717 QString rightAligned = QLatin1String("<html><p align=\"right\">right</p>");
718 QTest::newRow("right aligned 1") << defaultFont << rightAligned << 0 << 0 << 5 << rightAlign;
719 QTest::newRow("right aligned 2") << defaultFont << rightAligned << 1 << 0 << 5 << rightAlign;
720 QTest::newRow("right aligned 3") << defaultFont << rightAligned << 5 << 0 << 5 << rightAlign;
721
722 // left \n right \n left, make sure bold and alignment borders coincide
723 QString leftRightLeftAligned = QLatin1String("<html><p><b>left</b></p><p align=\"right\">right</p><p><b>left</b></p>");
724 QTest::newRow("left right left aligned 1") << defaultFont << leftRightLeftAligned << 1 << 0 << 4 << bold;
725 QTest::newRow("left right left aligned 3") << defaultFont << leftRightLeftAligned << 3 << 0 << 4 << bold;
726 QTest::newRow("left right left aligned 4") << defaultFont << leftRightLeftAligned << 4 << 4 << 5 << defaults;
727 QTest::newRow("left right left aligned 5") << defaultFont << leftRightLeftAligned << 5 << 5 << 10 << rightAlign;
728 QTest::newRow("left right left aligned 8") << defaultFont << leftRightLeftAligned << 8 << 5 << 10 << rightAlign;
729 QTest::newRow("left right left aligned 9") << defaultFont << leftRightLeftAligned << 9 << 5 << 10 << rightAlign;
730 QTest::newRow("left right left aligned 10") << defaultFont << leftRightLeftAligned << 10 << 10 << 11 << rightAlign;
731 QTest::newRow("left right left aligned 11") << defaultFont << leftRightLeftAligned << 11 << 11 << 15 << bold;
732 QTest::newRow("left right left aligned 15") << defaultFont << leftRightLeftAligned << 15 << 11 << 15 << bold;
733 QTest::newRow("empty with no fragments") << defaultFont << QString::fromLatin1("\n\n\n\n") << 0 << 0 << 1 << defaults;
734}
735
736void tst_QAccessibility::textAttributes()
737{
738 {
739 QFETCH(QFont, defaultFont);
740 QFETCH(QString, text);
741 QFETCH(int, offset);
742 QFETCH(int, startOffsetResult);
743 QFETCH(int, endOffsetResult);
744 QFETCH(QStringList, attributeResult);
745
746 QTextEdit textEdit;
747 textEdit.document()->setDefaultFont(defaultFont);
748 textEdit.setText(text);
749 if (textEdit.document()->characterCount() > 1)
750 textEdit.textCursor().setPosition(1);
751 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&textEdit);
752 QAccessibleTextInterface *textInterface=interface->textInterface();
753 QVERIFY(textInterface);
754 QCOMPARE(textInterface->characterCount(), textEdit.toPlainText().length());
755
756 int startOffset = -1;
757 int endOffset = -1;
758 QString attributes = textInterface->attributes(offset, &startOffset, &endOffset);
759
760 QCOMPARE(startOffset, startOffsetResult);
761 QCOMPARE(endOffset, endOffsetResult);
762 QStringList attrList = attributes.split(QChar(';'), QString::SkipEmptyParts);
763 attributeResult.sort();
764 attrList.sort();
765 QCOMPARE(attrList, attributeResult);
766 }
767 QTestAccessibility::clearEvents();
768}
769
770void tst_QAccessibility::hideShowTest()
771{
772 QWidget * const window = new QWidget();
773 window->resize(200, 200);
774 QWidget * const child = new QWidget(window);
775
776 QVERIFY(state(window).invisible);
777 QVERIFY(state(child).invisible);
778
779 QTestAccessibility::clearEvents();
780
781 // show() and veryfy that both window and child are not invisible and get ObjectShow events.
782 window->show();
783 QVERIFY(!state(window).invisible);
784 QVERIFY(!state(child).invisible);
785
786 QAccessibleEvent show(window, QAccessible::ObjectShow);
787 QVERIFY(QTestAccessibility::containsEvent(&show));
788 QAccessibleEvent showChild(child, QAccessible::ObjectShow);
789 QVERIFY(QTestAccessibility::containsEvent(&showChild));
790 QTestAccessibility::clearEvents();
791
792 // hide() and veryfy that both window and child are invisible and get ObjectHide events.
793 window->hide();
794 QVERIFY(state(window).invisible);
795 QVERIFY(state(child).invisible);
796 QAccessibleEvent hide(window, QAccessible::ObjectHide);
797 QVERIFY(QTestAccessibility::containsEvent(&hide));
798 QAccessibleEvent hideChild(child, QAccessible::ObjectHide);
799 QVERIFY(QTestAccessibility::containsEvent(&hideChild));
800 QTestAccessibility::clearEvents();
801
802 delete window;
803 QTestAccessibility::clearEvents();
804}
805
806
807void tst_QAccessibility::actionTest()
808{
809 {
810 QCOMPARE(QAccessibleActionInterface::pressAction(), QString(QStringLiteral("Press")));
811
812 QWidget *widget = new QWidget;
813 widget->setFocusPolicy(Qt::NoFocus);
814 widget->show();
815
816 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(widget);
817 QVERIFY(interface);
818 QVERIFY(interface->isValid());
819 QAccessibleActionInterface *actions = interface->actionInterface();
820 QVERIFY(actions);
821
822 // no actions by default, except when focusable
823 QCOMPARE(actions->actionNames(), QStringList());
824 widget->setFocusPolicy(Qt::StrongFocus);
825 QCOMPARE(actions->actionNames(), QStringList(QAccessibleActionInterface::setFocusAction()));
826
827 delete widget;
828 }
829 QTestAccessibility::clearEvents();
830
831 {
832 QPushButton *button = new QPushButton;
833 setFrameless(button);
834 button->show();
835 QVERIFY(QTest::qWaitForWindowExposed(button));
836 button->clearFocus();
837 QCOMPARE(button->hasFocus(), false);
838 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(button);
839 QAccessibleActionInterface *actions = interface->actionInterface();
840 QVERIFY(actions);
841
842 // Make sure the "primary action" press comes first!
843 QCOMPARE(actions->actionNames(), QStringList() << QAccessibleActionInterface::pressAction() << QAccessibleActionInterface::setFocusAction());
844
845 actions->doAction(QAccessibleActionInterface::setFocusAction());
846 QTest::qWait(500);
847 QCOMPARE(button->hasFocus(), true);
848
849 connect(button, SIGNAL(clicked()), this, SLOT(onClicked()));
850 QCOMPARE(click_count, 0);
851 actions->doAction(QAccessibleActionInterface::pressAction());
852 QTest::qWait(500);
853 QCOMPARE(click_count, 1);
854
855 delete button;
856 }
857 QTestAccessibility::clearEvents();
858}
859
860void tst_QAccessibility::applicationTest()
861{
862 {
863 QLatin1String name = QLatin1String("My Name");
864 qApp->setApplicationName(name);
865 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(qApp);
866 QCOMPARE(interface->text(QAccessible::Name), name);
867 QCOMPARE(interface->text(QAccessible::Description), qApp->applicationFilePath());
868 QCOMPARE(interface->text(QAccessible::Value), QString());
869 QCOMPARE(interface->role(), QAccessible::Application);
870 QCOMPARE(interface->window(), static_cast<QWindow*>(0));
871 QCOMPARE(interface->parent(), static_cast<QAccessibleInterface*>(0));
872 QCOMPARE(interface->focusChild(), static_cast<QAccessibleInterface*>(0));
873 QCOMPARE(interface->indexOfChild(0), -1);
874 QCOMPARE(interface->child(0), static_cast<QAccessibleInterface*>(0));
875 QCOMPARE(interface->child(-1), static_cast<QAccessibleInterface*>(0));
876 QCOMPARE(interface->child(1), static_cast<QAccessibleInterface*>(0));
877 QCOMPARE(interface->childCount(), 0);
878
879 // Check that asking for the application interface twice returns the same object
880 QAccessibleInterface *app2 = QAccessible::queryAccessibleInterface(qApp);
881 QCOMPARE(interface, app2);
882
883 QWidget widget;
884 widget.show();
885 qApp->setActiveWindow(&widget);
886 QVERIFY(QTest::qWaitForWindowActive(&widget));
887
888 QAccessibleInterface *widgetIface = QAccessible::queryAccessibleInterface(&widget);
889 QCOMPARE(interface->childCount(), 1);
890 QAccessibleInterface *focus = interface->focusChild();
891 QCOMPARE(focus->object(), &widget);
892 QCOMPARE(interface->indexOfChild(0), -1);
893 QCOMPARE(interface->indexOfChild(widgetIface), 0);
894 QAccessibleInterface *child = interface->child(0);
895 QCOMPARE(child->object(), &widget);
896 QCOMPARE(interface->child(-1), static_cast<QAccessibleInterface*>(0));
897 QCOMPARE(interface->child(1), static_cast<QAccessibleInterface*>(0));
898 }
899 QTestAccessibility::clearEvents();
900}
901
902void tst_QAccessibility::mainWindowTest()
903{
904 {
905 QMainWindow *mw = new QMainWindow;
906 mw->resize(300, 200);
907 mw->show(); // triggers layout
908 qApp->setActiveWindow(mw);
909
910 QLatin1String name = QLatin1String("I am the main window");
911 mw->setWindowTitle(name);
912 QVERIFY(QTest::qWaitForWindowActive(mw));
913
914 // The order of events is not really that important.
915 QAccessibleEvent show(mw, QAccessible::ObjectShow);
916 QVERIFY(QTestAccessibility::containsEvent(&show));
917 QAccessible::State activeState;
918 activeState.active = true;
919 QAccessibleStateChangeEvent active(mw, activeState);
920 QVERIFY(QTestAccessibility::containsEvent(&active));
921
922 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(mw);
923 QCOMPARE(iface->text(QAccessible::Name), name);
924 QCOMPARE(iface->role(), QAccessible::Window);
925 QVERIFY(iface->state().active);
926
927
928 delete mw;
929 }
930 QTestAccessibility::clearEvents();
931
932 {
933 QWindow window;
934 window.setGeometry(80, 80, 40, 40);
935 window.show();
936 QTRY_COMPARE(QGuiApplication::focusWindow(), &window);
937
938 // We currently don't have an accessible interface for QWindow
939 // the active state is either in the QMainWindow or QQuickView
940 QAccessibleInterface *windowIface(QAccessible::queryAccessibleInterface(&window));
941 QVERIFY(!windowIface);
942
943 QAccessible::State activeState;
944 activeState.active = true;
945
946 // We should still not crash if we somehow end up sending state change events
947 // Note that we do not QVERIFY_EVENT, as that relies on the updateHandler being
948 // called, which does not happen/make sense when there's no interface for the event.
949 QAccessibleStateChangeEvent active(&window, activeState);
950 QAccessibleStateChangeEvent deactivate(&window, activeState);
951 }
952}
953
954// Dialogs and other sub-windows must appear in the
955// accessibility hierarchy exactly once as top level objects
956void tst_QAccessibility::subWindowTest()
957{
958 {
959 QWidget mainWidget;
960 mainWidget.setGeometry(100, 100, 100, 100);
961 mainWidget.show();
962 QLabel label(QStringLiteral("Window Contents"), &mainWidget);
963 mainWidget.setLayout(new QHBoxLayout());
964 mainWidget.layout()->addWidget(&label);
965
966 QDialog d(&mainWidget);
967 d.show();
968
969 QAccessibleInterface *app = QAccessible::queryAccessibleInterface(qApp);
970 QVERIFY(app);
971 QCOMPARE(app->childCount(), 2);
972
973 QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(&mainWidget);
974 QVERIFY(windowIface);
975 QCOMPARE(windowIface->childCount(), 1);
976 QCOMPARE(app->child(0), windowIface);
977 QCOMPARE(windowIface->parent(), app);
978
979 QAccessibleInterface *dialogIface = QAccessible::queryAccessibleInterface(&d);
980 QVERIFY(dialogIface);
981 QCOMPARE(app->child(1), dialogIface);
982 QCOMPARE(dialogIface->parent(), app);
983 QCOMPARE(dialogIface->parent(), app);
984 }
985
986 {
987 QMainWindow mainWindow;
988 mainWindow.setGeometry(100, 100, 100, 100);
989 mainWindow.show();
990 QLabel label(QStringLiteral("Window Contents"), &mainWindow);
991 mainWindow.setCentralWidget(&label);
992
993 QDialog d(&mainWindow);
994 d.show();
995
996 QAccessibleInterface *app = QAccessible::queryAccessibleInterface(qApp);
997 QVERIFY(app);
998 QCOMPARE(app->childCount(), 2);
999
1000 QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(&mainWindow);
1001 QVERIFY(windowIface);
1002 QCOMPARE(windowIface->childCount(), 1);
1003 QCOMPARE(app->child(0), windowIface);
1004
1005 QAccessibleInterface *dialogIface = QAccessible::queryAccessibleInterface(&d);
1006 QVERIFY(dialogIface);
1007 QCOMPARE(app->child(1), dialogIface);
1008 QCOMPARE(dialogIface->parent(), app);
1009 QCOMPARE(windowIface->parent(), app);
1010 }
1011 QTestAccessibility::clearEvents();
1012}
1013
1014class CounterButton : public QPushButton {
1015 Q_OBJECT
1016public:
1017 CounterButton(const QString& name, QWidget* parent)
1018 : QPushButton(name, parent), clickCount(0)
1019 {
1020 connect(this, SIGNAL(clicked(bool)), SLOT(incClickCount()));
1021 }
1022 int clickCount;
1023public Q_SLOTS:
1024 void incClickCount() {
1025 ++clickCount;
1026 }
1027};
1028
1029void tst_QAccessibility::buttonTest()
1030{
1031 QWidget window;
1032 window.setLayout(new QVBoxLayout);
1033
1034 // Standard push button
1035 CounterButton pushButton("Ok", &window);
1036
1037 // toggle button
1038 QPushButton toggleButton("Toggle", &window);
1039 toggleButton.setCheckable(true);
1040
1041 // standard checkbox
1042 QCheckBox checkBox("Check me!", &window);
1043
1044 // tristate checkbox
1045 QCheckBox tristate("Tristate!", &window);
1046 tristate.setTristate(true);
1047
1048 // radiobutton
1049 QRadioButton radio("Radio me!", &window);
1050
1051 // standard toolbutton
1052 QToolButton toolbutton(&window);
1053 toolbutton.setText("Tool");
1054 toolbutton.setMinimumSize(20,20);
1055
1056 // standard toolbutton
1057 QToolButton toggletool(&window);
1058 toggletool.setCheckable(true);
1059 toggletool.setText("Toggle");
1060 toggletool.setMinimumSize(20,20);
1061
1062 // test push button
1063 QAccessibleInterface* interface = QAccessible::queryAccessibleInterface(&pushButton);
1064 QAccessibleActionInterface* actionInterface = interface->actionInterface();
1065 QVERIFY(actionInterface != 0);
1066 QCOMPARE(interface->role(), QAccessible::PushButton);
1067
1068 // buttons only have a click action
1069 QCOMPARE(actionInterface->actionNames().size(), 2);
1070 QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::pressAction() << QAccessibleActionInterface::setFocusAction());
1071 QCOMPARE(pushButton.clickCount, 0);
1072 actionInterface->doAction(QAccessibleActionInterface::pressAction());
1073 QTest::qWait(500);
1074 QCOMPARE(pushButton.clickCount, 1);
1075
1076 // test toggle button
1077 interface = QAccessible::queryAccessibleInterface(&toggleButton);
1078 actionInterface = interface->actionInterface();
1079 QCOMPARE(interface->role(), QAccessible::CheckBox);
1080 QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::toggleAction() << QAccessibleActionInterface::setFocusAction());
1081 QCOMPARE(actionInterface->localizedActionDescription(QAccessibleActionInterface::toggleAction()), QString("Toggles the state"));
1082 QVERIFY(!toggleButton.isChecked());
1083 QVERIFY(!interface->state().checked);
1084 actionInterface->doAction(QAccessibleActionInterface::toggleAction());
1085 QTest::qWait(500);
1086 QVERIFY(toggleButton.isChecked());
1087 QCOMPARE(actionInterface->actionNames().at(0), QAccessibleActionInterface::toggleAction());
1088 QVERIFY(interface->state().checked);
1089
1090 {
1091 // test menu push button
1092 QAction *foo = new QAction("Foo", 0);
1093 foo->setShortcut(QKeySequence("Ctrl+F"));
1094 QMenu *menu = new QMenu();
1095 menu->addAction(foo);
1096 QPushButton menuButton;
1097 setFrameless(&menuButton);
1098 menuButton.setMenu(menu);
1099 menuButton.show();
1100 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&menuButton);
1101 QCOMPARE(interface->role(), QAccessible::ButtonMenu);
1102 QVERIFY(interface->state().hasPopup);
1103 QCOMPARE(interface->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::showMenuAction() << QAccessibleActionInterface::setFocusAction());
1104 // showing the menu enters a new event loop...
1105// interface->actionInterface()->doAction(QAccessibleActionInterface::showMenuAction());
1106// QTest::qWait(500);
1107 delete menu;
1108 }
1109
1110
1111 QTestAccessibility::clearEvents();
1112 {
1113 // test check box
1114 interface = QAccessible::queryAccessibleInterface(&checkBox);
1115 actionInterface = interface->actionInterface();
1116 QCOMPARE(interface->role(), QAccessible::CheckBox);
1117 QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::toggleAction() << QAccessibleActionInterface::setFocusAction());
1118 QVERIFY(!interface->state().checked);
1119 actionInterface->doAction(QAccessibleActionInterface::toggleAction());
1120
1121 QTest::qWait(500);
1122 QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::toggleAction() << QAccessibleActionInterface::setFocusAction());
1123 QVERIFY(interface->state().checked);
1124 QVERIFY(checkBox.isChecked());
1125 QAccessible::State st;
1126 st.checked = true;
1127 QAccessibleStateChangeEvent ev(&checkBox, st);
1128 QVERIFY_EVENT(&ev);
1129 checkBox.setChecked(false);
1130 QVERIFY_EVENT(&ev);
1131 }
1132
1133 {
1134 // test radiobutton
1135 interface = QAccessible::queryAccessibleInterface(&radio);
1136 actionInterface = interface->actionInterface();
1137 QCOMPARE(interface->role(), QAccessible::RadioButton);
1138 QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::toggleAction() << QAccessibleActionInterface::setFocusAction());
1139 QVERIFY(!interface->state().checked);
1140 actionInterface->doAction(QAccessibleActionInterface::toggleAction());
1141 QTest::qWait(500);
1142 QCOMPARE(actionInterface->actionNames(), QStringList() << QAccessibleActionInterface::toggleAction() << QAccessibleActionInterface::setFocusAction());
1143 QVERIFY(interface->state().checked);
1144 QVERIFY(radio.isChecked());
1145 QAccessible::State st;
1146 st.checked = true;
1147 QAccessibleStateChangeEvent ev(&radio, st);
1148 QVERIFY_EVENT(&ev);
1149 }
1150
1151// // test standard toolbutton
1152// QVERIFY(QAccessible::queryAccessibleInterface(&toolbutton, &test));
1153// QCOMPARE(test->role(), QAccessible::PushButton);
1154// QCOMPARE(test->defaultAction(0), QAccessible::Press);
1155// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Press"));
1156// QCOMPARE(test->state(), (int)QAccessible::Normal);
1157// test->release();
1158
1159// // toggle tool button
1160// QVERIFY(QAccessible::queryAccessibleInterface(&toggletool, &test));
1161// QCOMPARE(test->role(), QAccessible::CheckBox);
1162// QCOMPARE(test->defaultAction(0), QAccessible::Press);
1163// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Check"));
1164// QCOMPARE(test->state(), (int)QAccessible::Normal);
1165// QVERIFY(test->doAction(QAccessible::Press, 0));
1166// QTest::qWait(500);
1167// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Uncheck"));
1168// QCOMPARE(test->state(), (int)QAccessible::Checked);
1169// test->release();
1170
1171// // test menu toolbutton
1172// QVERIFY(QAccessible::queryAccessibleInterface(&menuToolButton, &test));
1173// QCOMPARE(test->role(), QAccessible::ButtonMenu);
1174// QCOMPARE(test->defaultAction(0), 1);
1175// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Open"));
1176// QCOMPARE(test->state(), (int)QAccessible::HasPopup);
1177// QCOMPARE(test->actionCount(0), 1);
1178// QCOMPARE(test->actionText(QAccessible::Press, QAccessible::Name, 0), QString("Press"));
1179// test->release();
1180
1181// // test split menu toolbutton
1182// QVERIFY(QAccessible::queryAccessibleInterface(&splitToolButton, &test));
1183// QCOMPARE(test->childCount(), 2);
1184// QCOMPARE(test->role(), QAccessible::ButtonDropDown);
1185// QCOMPARE(test->role(1), QAccessible::PushButton);
1186// QCOMPARE(test->role(2), QAccessible::ButtonMenu);
1187// QCOMPARE(test->defaultAction(0), QAccessible::Press);
1188// QCOMPARE(test->defaultAction(1), QAccessible::Press);
1189// QCOMPARE(test->defaultAction(2), QAccessible::Press);
1190// QCOMPARE(test->actionText(test->defaultAction(0), QAccessible::Name, 0), QString("Press"));
1191// QCOMPARE(test->state(), (int)QAccessible::HasPopup);
1192// QCOMPARE(test->actionCount(0), 1);
1193// QCOMPARE(test->actionText(1, QAccessible::Name, 0), QString("Open"));
1194// QCOMPARE(test->actionText(test->defaultAction(1), QAccessible::Name, 1), QString("Press"));
1195// QCOMPARE(test->state(1), (int)QAccessible::Normal);
1196// QCOMPARE(test->actionText(test->defaultAction(2), QAccessible::Name, 2), QString("Open"));
1197// QCOMPARE(test->state(2), (int)QAccessible::HasPopup);
1198// test->release();
1199}
1200
1201void tst_QAccessibility::scrollBarTest()
1202{
1203 QScrollBar *scrollBar = new QScrollBar(Qt::Horizontal);
1204 QAccessibleInterface * const scrollBarInterface = QAccessible::queryAccessibleInterface(scrollBar);
1205 QVERIFY(scrollBarInterface);
1206 QVERIFY(scrollBarInterface->state().invisible);
1207 scrollBar->resize(200, 50);
1208 scrollBar->show();
1209 QVERIFY(!scrollBarInterface->state().invisible);
1210 QAccessibleEvent show(scrollBar, QAccessible::ObjectShow);
1211 QVERIFY(QTestAccessibility::containsEvent(&show));
1212 QTestAccessibility::clearEvents();
1213
1214 scrollBar->hide();
1215 QVERIFY(scrollBarInterface->state().invisible);
1216 QAccessibleEvent hide(scrollBar, QAccessible::ObjectHide);
1217 QVERIFY(QTestAccessibility::containsEvent(&hide));
1218 QTestAccessibility::clearEvents();
1219
1220 // Test that the left/right subcontrols are set to unavailable when the scrollBar is at the minimum/maximum.
1221 scrollBar->show();
1222 scrollBar->setMinimum(11);
1223 scrollBar->setMaximum(111);
1224
1225 QAccessibleValueInterface *valueIface = scrollBarInterface->valueInterface();
1226 QVERIFY(valueIface != 0);
1227 QCOMPARE(valueIface->minimumValue().toInt(), scrollBar->minimum());
1228 QCOMPARE(valueIface->maximumValue().toInt(), scrollBar->maximum());
1229 scrollBar->setValue(50);
1230 QCOMPARE(valueIface->currentValue().toInt(), scrollBar->value());
1231 scrollBar->setValue(0);
1232 QCOMPARE(valueIface->currentValue().toInt(), scrollBar->value());
1233 scrollBar->setValue(100);
1234 QCOMPARE(valueIface->currentValue().toInt(), scrollBar->value());
1235 valueIface->setCurrentValue(77);
1236 QCOMPARE(77, scrollBar->value());
1237
1238 const QRect scrollBarRect = scrollBarInterface->rect();
1239 QVERIFY(scrollBarRect.isValid());
1240
1241 delete scrollBar;
1242
1243 QTestAccessibility::clearEvents();
1244}
1245
1246void tst_QAccessibility::tabTest()
1247{
1248 QTabBar *tabBar = new QTabBar();
1249 setFrameless(tabBar);
1250 tabBar->show();
1251
1252 QAccessibleInterface * const interface = QAccessible::queryAccessibleInterface(tabBar);
1253 QVERIFY(interface);
1254 QCOMPARE(interface->childCount(), 2);
1255
1256 // Test that the Invisible bit for the navigation buttons gets set
1257 // and cleared correctly.
1258 QAccessibleInterface *leftButton = interface->child(0);
1259 QCOMPARE(leftButton->role(), QAccessible::PushButton);
1260 QVERIFY(leftButton->state().invisible);
1261
1262 const int lots = 5;
1263 for (int i = 0; i < lots; ++i) {
1264 tabBar->addTab("Foo");
1265 tabBar->setTabToolTip(i, QLatin1String("Cool tool tip"));
1266 tabBar->setTabWhatsThis(i, QLatin1String("I don't know"));
1267 }
1268
1269 QAccessibleInterface *child1 = interface->child(0);
1270 QAccessibleInterface *child2 = interface->child(1);
1271 QVERIFY(child1);
1272 QCOMPARE(child1->role(), QAccessible::PageTab);
1273 QVERIFY(child2);
1274 QCOMPARE(child2->role(), QAccessible::PageTab);
1275
1276 QCOMPARE(child1->text(QAccessible::Name), QLatin1String("Foo"));
1277 QCOMPARE(child1->text(QAccessible::Description), QLatin1String("Cool tool tip"));
1278 QCOMPARE(child1->text(QAccessible::Help), QLatin1String("I don't know"));
1279
1280 QVERIFY(!(child1->state().invisible));
1281 tabBar->hide();
1282
1283 QCoreApplication::processEvents();
1284 QTest::qWait(100);
1285
1286 QVERIFY(child1->state().invisible);
1287
1288 tabBar->show();
1289 tabBar->setCurrentIndex(0);
1290
1291 // Test that sending a focus action to a tab does not select it.
1292// child2->doAction(QAccessible::Focus, 2, QVariantList());
1293 QCOMPARE(tabBar->currentIndex(), 0);
1294
1295 // Test that sending a press action to a tab selects it.
1296 QVERIFY(child2->actionInterface());
1297 QCOMPARE(child2->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::pressAction());
1298 QCOMPARE(tabBar->currentIndex(), 0);
1299 child2->actionInterface()->doAction(QAccessibleActionInterface::pressAction());
1300 QCOMPARE(tabBar->currentIndex(), 1);
1301
1302 // Test that setAccessibleTabName changes a tab's accessible name
1303 tabBar->setAccessibleTabName(0, "AccFoo");
1304 tabBar->setAccessibleTabName(1, "AccBar");
1305 QCOMPARE(child1->text(QAccessible::Name), QLatin1String("AccFoo"));
1306 QCOMPARE(child2->text(QAccessible::Name), QLatin1String("AccBar"));
1307 tabBar->setCurrentIndex(0);
1308 QCOMPARE(interface->text(QAccessible::Name), QLatin1String("AccFoo"));
1309 tabBar->setCurrentIndex(1);
1310 QCOMPARE(interface->text(QAccessible::Name), QLatin1String("AccBar"));
1311
1312 delete tabBar;
1313 QTestAccessibility::clearEvents();
1314}
1315
1316void tst_QAccessibility::tabWidgetTest()
1317{
1318 QTabWidget *tabWidget = new QTabWidget();
1319 tabWidget->show();
1320
1321 // the interface for the tab is just a container for tabbar and stacked widget
1322 QAccessibleInterface * const interface = QAccessible::queryAccessibleInterface(tabWidget);
1323 QVERIFY(interface);
1324 QCOMPARE(interface->childCount(), 2);
1325 QCOMPARE(interface->role(), QAccessible::Client);
1326
1327 // Create pages, check navigation
1328 QLabel *label1 = new QLabel("Page 1", tabWidget);
1329 tabWidget->addTab(label1, "Tab 1");
1330 QLabel *label2 = new QLabel("Page 2", tabWidget);
1331 tabWidget->addTab(label2, "Tab 2");
1332
1333 QCOMPARE(interface->childCount(), 2);
1334
1335 QAccessibleInterface* tabBarInterface = 0;
1336 // there is no special logic to sort the children, so the contents will be 1, the tab bar 2
1337 tabBarInterface = interface->child(1);
1338 QCOMPARE(verifyHierarchy(tabBarInterface), 0);
1339 QVERIFY(tabBarInterface);
1340 QCOMPARE(tabBarInterface->childCount(), 4);
1341 QCOMPARE(tabBarInterface->role(), QAccessible::PageTabList);
1342
1343 QAccessibleInterface* tabButton1Interface = tabBarInterface->child(0);
1344 QVERIFY(tabButton1Interface);
1345 QCOMPARE(tabButton1Interface->role(), QAccessible::PageTab);
1346 QCOMPARE(tabButton1Interface->text(QAccessible::Name), QLatin1String("Tab 1"));
1347
1348 QAccessibleInterface* tabButton2Interface = tabBarInterface->child(1);
1349 QVERIFY(tabButton2Interface);
1350 QCOMPARE(tabButton2Interface->role(), QAccessible::PageTab);
1351 QCOMPARE(tabButton2Interface->text(QAccessible::Name), QLatin1String("Tab 2"));
1352
1353 // Test that setAccessibleTabName changes a tab's accessible name
1354 tabWidget->setCurrentIndex(0);
1355 tabWidget->tabBar()->setAccessibleTabName(0, "Acc Tab");
1356 QCOMPARE(tabButton1Interface->role(), QAccessible::PageTab);
1357 QCOMPARE(tabButton1Interface->text(QAccessible::Name), QLatin1String("Acc Tab"));
1358 QCOMPARE(tabBarInterface->text(QAccessible::Name), QLatin1String("Acc Tab"));
1359
1360 QAccessibleInterface* tabButtonLeft = tabBarInterface->child(2);
1361 QVERIFY(tabButtonLeft);
1362 QCOMPARE(tabButtonLeft->role(), QAccessible::PushButton);
1363 QCOMPARE(tabButtonLeft->text(QAccessible::Name), QLatin1String("Scroll Left"));
1364
1365 QAccessibleInterface* tabButtonRight = tabBarInterface->child(3);
1366 QVERIFY(tabButtonRight);
1367 QCOMPARE(tabButtonRight->role(), QAccessible::PushButton);
1368 QCOMPARE(tabButtonRight->text(QAccessible::Name), QLatin1String("Scroll Right"));
1369
1370 QAccessibleInterface* stackWidgetInterface = interface->child(0);
1371 QVERIFY(stackWidgetInterface);
1372 QCOMPARE(stackWidgetInterface->childCount(), 2);
1373 QCOMPARE(stackWidgetInterface->role(), QAccessible::LayeredPane);
1374
1375 QAccessibleInterface* stackChild1Interface = stackWidgetInterface->child(0);
1376 QVERIFY(stackChild1Interface);
1377#ifndef Q_CC_INTEL
1378 QCOMPARE(stackChild1Interface->childCount(), 0);
1379#endif
1380 QCOMPARE(stackChild1Interface->role(), QAccessible::StaticText);
1381 QCOMPARE(stackChild1Interface->text(QAccessible::Name), QLatin1String("Page 1"));
1382 QCOMPARE(label1, stackChild1Interface->object());
1383
1384 // Navigation in stack widgets should be consistent
1385 QAccessibleInterface* parent = stackChild1Interface->parent();
1386 QVERIFY(parent);
1387#ifndef Q_CC_INTEL
1388 QCOMPARE(parent->childCount(), 2);
1389#endif
1390 QCOMPARE(parent->role(), QAccessible::LayeredPane);
1391
1392 QAccessibleInterface* stackChild2Interface = stackWidgetInterface->child(1);
1393 QVERIFY(stackChild2Interface);
1394 QCOMPARE(stackChild2Interface->childCount(), 0);
1395 QCOMPARE(stackChild2Interface->role(), QAccessible::StaticText);
1396 QCOMPARE(label2, stackChild2Interface->object());
1397 QCOMPARE(label2->text(), stackChild2Interface->text(QAccessible::Name));
1398
1399 parent = stackChild2Interface->parent();
1400 QVERIFY(parent);
1401#ifndef Q_CC_INTEL
1402 QCOMPARE(parent->childCount(), 2);
1403#endif
1404 QCOMPARE(parent->role(), QAccessible::LayeredPane);
1405
1406 delete tabWidget;
1407 QTestAccessibility::clearEvents();
1408}
1409
1410void tst_QAccessibility::menuTest()
1411{
1412 {
1413 QMainWindow mw;
1414 mw.resize(300, 200);
1415 mw.menuBar()->setNativeMenuBar(false);
1416 QMenu *file = mw.menuBar()->addMenu("&File");
1417 QMenu *fileNew = file->addMenu("&New...");
1418 fileNew->menuAction()->setShortcut(tr("Ctrl+N"));
1419 fileNew->addAction("Text file");
1420 fileNew->addAction("Image file");
1421 file->addAction("&Open")->setShortcut(tr("Ctrl+O"));
1422 file->addAction("&Save")->setShortcut(tr("Ctrl+S"));
1423 file->addSeparator();
1424 file->addAction("E&xit")->setShortcut(tr("Alt+F4"));
1425
1426 QMenu *edit = mw.menuBar()->addMenu("&Edit");
1427 edit->addAction("&Undo")->setShortcut(tr("Ctrl+Z"));
1428 edit->addAction("&Redo")->setShortcut(tr("Ctrl+Y"));
1429 edit->addSeparator();
1430 edit->addAction("Cu&t")->setShortcut(tr("Ctrl+X"));
1431 edit->addAction("&Copy")->setShortcut(tr("Ctrl+C"));
1432 edit->addAction("&Paste")->setShortcut(tr("Ctrl+V"));
1433 edit->addAction("&Delete")->setShortcut(tr("Del"));
1434 edit->addSeparator();
1435 edit->addAction("Pr&operties");
1436
1437 mw.menuBar()->addSeparator();
1438
1439 QMenu *help = mw.menuBar()->addMenu("&Help");
1440 help->addAction("&Contents");
1441 help->addAction("&About");
1442
1443 mw.menuBar()->addAction("Action!");
1444
1445 QMenu *childOfMainWindow = new QMenu(QStringLiteral("&Tools"), &mw);
1446 childOfMainWindow->addAction("&Options");
1447 mw.menuBar()->addMenu(childOfMainWindow);
1448
1449 mw.show(); // triggers layout
1450 QTest::qWait(100);
1451
1452 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(&mw);
1453 QCOMPARE(verifyHierarchy(interface), 0);
1454
1455 interface = QAccessible::queryAccessibleInterface(mw.menuBar());
1456
1457 QVERIFY(interface);
1458 QCOMPARE(interface->childCount(), 6);
1459 QCOMPARE(interface->role(), QAccessible::MenuBar);
1460
1461 QAccessibleInterface *iFile = interface->child(0);
1462 QAccessibleInterface *iEdit = interface->child(1);
1463 QAccessibleInterface *iSeparator = interface->child(2);
1464 QAccessibleInterface *iHelp = interface->child(3);
1465 QAccessibleInterface *iAction = interface->child(4);
1466
1467 QCOMPARE(iFile->role(), QAccessible::MenuItem);
1468 QCOMPARE(iEdit->role(), QAccessible::MenuItem);
1469 QCOMPARE(iSeparator->role(), QAccessible::Separator);
1470 QCOMPARE(iHelp->role(), QAccessible::MenuItem);
1471 QCOMPARE(iAction->role(), QAccessible::MenuItem);
1472#ifndef Q_OS_MAC
1473 QCOMPARE(mw.mapFromGlobal(interface->rect().topLeft()), mw.menuBar()->geometry().topLeft());
1474 QCOMPARE(interface->rect().size(), mw.menuBar()->size());
1475
1476 QVERIFY(interface->rect().contains(iFile->rect()));
1477 QVERIFY(interface->rect().contains(iEdit->rect()));
1478 // QVERIFY(interface->rect().contains(childSeparator->rect())); //separator might be invisible
1479 QVERIFY(interface->rect().contains(iHelp->rect()));
1480 QVERIFY(interface->rect().contains(iAction->rect()));
1481#endif
1482
1483 QCOMPARE(iFile->text(QAccessible::Name), QString("File"));
1484 QCOMPARE(iEdit->text(QAccessible::Name), QString("Edit"));
1485 QCOMPARE(iSeparator->text(QAccessible::Name), QString());
1486 QCOMPARE(iHelp->text(QAccessible::Name), QString("Help"));
1487 QCOMPARE(iAction->text(QAccessible::Name), QString("Action!"));
1488
1489// TODO: Currently not working, task to fix is #100019.
1490#ifndef Q_OS_MAC
1491 QCOMPARE(iFile->text(QAccessible::Accelerator), tr("Alt+F"));
1492 QCOMPARE(iEdit->text(QAccessible::Accelerator), tr("Alt+E"));
1493 QCOMPARE(iSeparator->text(QAccessible::Accelerator), QString());
1494 QCOMPARE(iHelp->text(QAccessible::Accelerator), tr("Alt+H"));
1495 QCOMPARE(iAction->text(QAccessible::Accelerator), QString());
1496#endif
1497
1498 QVERIFY(iFile->actionInterface());
1499
1500 QCOMPARE(iFile->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::showMenuAction());
1501 QCOMPARE(iSeparator->actionInterface()->actionNames(), QStringList());
1502 QCOMPARE(iHelp->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::showMenuAction());
1503 QCOMPARE(iAction->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::pressAction());
1504
1505 bool menuFade = qApp->isEffectEnabled(Qt::UI_FadeMenu);
1506 int menuFadeDelay = 300;
1507 iFile->actionInterface()->doAction(QAccessibleActionInterface::showMenuAction());
1508 if(menuFade)
1509 QTest::qWait(menuFadeDelay);
1510 QTRY_VERIFY(file->isVisible() && !edit->isVisible() && !help->isVisible());
1511 iEdit->actionInterface()->doAction(QAccessibleActionInterface::showMenuAction());
1512 if(menuFade)
1513 QTest::qWait(menuFadeDelay);
1514 QTRY_VERIFY(!file->isVisible() && edit->isVisible() && !help->isVisible());
1515 iHelp->actionInterface()->doAction(QAccessibleActionInterface::showMenuAction());
1516 if(menuFade)
1517 QTest::qWait(menuFadeDelay);
1518 QTRY_VERIFY(!file->isVisible() && !edit->isVisible() && help->isVisible());
1519 iAction->actionInterface()->doAction(QAccessibleActionInterface::showMenuAction());
1520 if(menuFade)
1521 QTest::qWait(menuFadeDelay);
1522 QTRY_VERIFY(!file->isVisible() && !edit->isVisible() && !help->isVisible());
1523
1524 QVERIFY(interface->actionInterface());
1525 QCOMPARE(interface->actionInterface()->actionNames(), QStringList());
1526 interface = QAccessible::queryAccessibleInterface(file);
1527 QCOMPARE(interface->childCount(), 5);
1528 QCOMPARE(interface->role(), QAccessible::PopupMenu);
1529
1530 QAccessibleInterface *iFileNew = interface->child(0);
1531 QAccessibleInterface *iFileOpen = interface->child(1);
1532 QAccessibleInterface *iFileSave = interface->child(2);
1533 QAccessibleInterface *iFileSeparator = interface->child(3);
1534 QAccessibleInterface *iFileExit = interface->child(4);
1535
1536 QCOMPARE(iFileNew->role(), QAccessible::MenuItem);
1537 QCOMPARE(iFileOpen->role(), QAccessible::MenuItem);
1538 QCOMPARE(iFileSave->role(), QAccessible::MenuItem);
1539 QCOMPARE(iFileSeparator->role(), QAccessible::Separator);
1540 QCOMPARE(iFileExit->role(), QAccessible::MenuItem);
1541 QCOMPARE(iFileNew->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::showMenuAction());
1542 QCOMPARE(iFileOpen->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::pressAction());
1543 QCOMPARE(iFileSave->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::pressAction());
1544 QCOMPARE(iFileSeparator->actionInterface()->actionNames(), QStringList());
1545 QCOMPARE(iFileExit->actionInterface()->actionNames(), QStringList() << QAccessibleActionInterface::pressAction());
1546
1547 QAccessibleInterface *iface = 0;
1548 QAccessibleInterface *iface2 = 0;
1549
1550 // traverse siblings with navigate(Sibling, ...)
1551 iface = interface->child(0);
1552 QVERIFY(iface);
1553 QCOMPARE(iface->role(), QAccessible::MenuItem);
1554
1555 QAccessible::Role fileRoles[5] = {
1556 QAccessible::MenuItem,
1557 QAccessible::MenuItem,
1558 QAccessible::MenuItem,
1559 QAccessible::Separator,
1560 QAccessible::MenuItem
1561 };
1562 for (int child = 0; child < 5; ++child) {
1563 iface2 = interface->child(child);
1564 QVERIFY(iface2);
1565 QCOMPARE(iface2->role(), fileRoles[child]);
1566 }
1567
1568 // "New" item
1569 iface = interface->child(0);
1570 QVERIFY(iface);
1571 QCOMPARE(iface->role(), QAccessible::MenuItem);
1572
1573 // "New" menu
1574 iface2 = iface->child(0);
1575 iface = iface2;
1576 QVERIFY(iface);
1577 QCOMPARE(iface->role(), QAccessible::PopupMenu);
1578
1579 // "Text file" menu item
1580 iface2 = iface->child(0);
1581 iface = iface2;
1582 QVERIFY(iface);
1583 QCOMPARE(iface->role(), QAccessible::MenuItem);
1584
1585 // move mouse pointer away, since that might influence the
1586 // subsequent tests
1587 QTest::mouseMove(&mw, QPoint(-1, -1));
1588 QTest::qWait(100);
1589 if (menuFade)
1590 QTest::qWait(menuFadeDelay);
1591
1592 iFile->actionInterface()->doAction(QAccessibleActionInterface::showMenuAction());
1593 iFileNew->actionInterface()->doAction(QAccessibleActionInterface::showMenuAction());
1594
1595 QTRY_VERIFY(file->isVisible());
1596 QTRY_VERIFY(fileNew->isVisible());
1597 QVERIFY(!edit->isVisible());
1598 QVERIFY(!help->isVisible());
1599
1600 QTestAccessibility::clearEvents();
1601 mw.hide();
1602
1603 // Do not crash if the menu don't have a parent
1604 QMenu *menu = new QMenu;
1605 menu->addAction(QLatin1String("one"));
1606 menu->addAction(QLatin1String("two"));
1607 menu->addAction(QLatin1String("three"));
1608 iface = QAccessible::queryAccessibleInterface(menu);
1609 iface2 = iface->parent();
1610 QVERIFY(iface2);
1611 QCOMPARE(iface2->role(), QAccessible::Application);
1612 // caused a *crash*
1613 iface2->state();
1614 delete menu;
1615
1616 }
1617 QTestAccessibility::clearEvents();
1618}
1619
1620void tst_QAccessibility::spinBoxTest()
1621{
1622 QSpinBox * const spinBox = new QSpinBox();
1623 setFrameless(spinBox);
1624 spinBox->setValue(3);
1625 spinBox->show();
1626
1627 QAccessibleInterface * const interface = QAccessible::queryAccessibleInterface(spinBox);
1628 QVERIFY(interface);
1629 QCOMPARE(interface->role(), QAccessible::SpinBox);
1630
1631 QVERIFY(QTest::qWaitForWindowExposed(spinBox));
1632
1633 const QRect widgetRect = spinBox->geometry();
1634 const QRect accessibleRect = interface->rect();
1635 QCOMPARE(accessibleRect, widgetRect);
1636 QCOMPARE(interface->text(QAccessible::Value), QLatin1String("3"));
1637
1638 // make sure that the line edit is not there
1639 const int numChildren = interface->childCount();
1640 QCOMPARE(numChildren, 0);
1641 QVERIFY(!interface->child(0));
1642
1643 QVERIFY(interface->valueInterface());
1644 QCOMPARE(interface->valueInterface()->currentValue().toInt(), 3);
1645 interface->valueInterface()->setCurrentValue(23);
1646 QCOMPARE(interface->valueInterface()->currentValue().toInt(), 23);
1647 QCOMPARE(spinBox->value(), 23);
1648
1649 spinBox->setFocus();
1650 QTestAccessibility::clearEvents();
1651 QTest::keyPress(spinBox, Qt::Key_Up);
1652 QTest::qWait(200);
1653 QAccessibleValueChangeEvent expectedEvent(spinBox, spinBox->value());
1654 QVERIFY(QTestAccessibility::containsEvent(&expectedEvent));
1655
1656 QAccessibleTextInterface *textIface = interface->textInterface();
1657 QVERIFY(textIface);
1658
1659 delete spinBox;
1660 QTestAccessibility::clearEvents();
1661}
1662
1663void tst_QAccessibility::doubleSpinBoxTest()
1664{
1665 QDoubleSpinBox *doubleSpinBox = new QDoubleSpinBox;
1666 setFrameless(doubleSpinBox);
1667 doubleSpinBox->show();
1668
1669 QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(doubleSpinBox);
1670 QVERIFY(interface);
1671
1672 QVERIFY(QTest::qWaitForWindowExposed(doubleSpinBox));
1673
1674 const QRect widgetRect = doubleSpinBox->geometry();
1675 const QRect accessibleRect = interface->rect();
1676 QCOMPARE(accessibleRect, widgetRect);
1677
1678 // Test that we get valid rects for all the spinbox child interfaces.
1679 const int numChildren = interface->childCount();
1680 for (int i = 0; i < numChildren; ++i) {
1681 QAccessibleInterface *childIface = interface->child(i);
1682 const QRect childRect = childIface->rect();
1683 QVERIFY(childRect.isValid());
1684 }
1685
1686 delete doubleSpinBox;
1687 QTestAccessibility::clearEvents();
1688}
1689
1690static QRect characterRect(const QTextEdit &edit, int offset)
1691{
1692 QTextBlock block = edit.document()->findBlock(offset);
1693 QTextLayout *layout = block.layout();
1694 QPointF layoutPosition = layout->position();
1695 int relativeOffset = offset - block.position();
1696 QTextLine line = layout->lineForTextPosition(relativeOffset);
1697 QTextBlock::iterator it = block.begin();
1698 while (!it.fragment().contains(offset))
1699 ++it;
1700 QFontMetrics fm(it.fragment().charFormat().font());
1701 QChar ch = edit.document()->characterAt(offset);
1702 int w = fm.horizontalAdvance(ch);
1703 int h = fm.height();
1704
1705 qreal x = line.cursorToX(relativeOffset);
1706 QRect r(layoutPosition.x() + x, layoutPosition.y() + line.y() + line.ascent() + fm.descent() - h, w, h);
1707 r.moveTo(edit.viewport()->mapToGlobal(r.topLeft()));
1708
1709 return r;
1710}
1711
1712/* The rects does not have to be exactly the same. They may be slightly different due to
1713 different ways of calculating them. By having an acceptable delta, this should also
1714 make the test a bit more resilient against any future changes in the behavior of
1715 characterRect().
1716*/
1717static bool fuzzyRectCompare(const QRect &a, const QRect &b)
1718{
1719 static const int MAX_ACCEPTABLE_DELTA = 1;
1720 const QMargins delta(a.left() - b.left(), a.top() - b.top(),
1721 a.right() - b.right(), a.bottom() - b.bottom());
1722
1723 return qAbs(delta.left()) <= MAX_ACCEPTABLE_DELTA && qAbs(delta.top()) <= MAX_ACCEPTABLE_DELTA
1724 && qAbs(delta.right()) <= MAX_ACCEPTABLE_DELTA && qAbs(delta.bottom()) <= MAX_ACCEPTABLE_DELTA;
1725}
1726
1727static QByteArray msgRectMismatch(const QRect &a, const QRect &b)
1728{
1729 QString result;
1730 QDebug(&