4#ifndef QTESTACCESSIBLE_H
5#define QTESTACCESSIBLE_H
9#pragma qt_no_master_include
12#include <QtCore/qglobal.h>
14#define QVERIFY_EVENT(event)
15 QVERIFY(QTestAccessibility::verifyEvent(event))
17#include <QtCore/qlist.h>
18#include <QtCore/qdebug.h>
19#include <QtGui/qaccessible.h>
20#include <QtGui/qguiapplication.h>
21#include <QtTest/qttestglobal.h>
22#include <QtTest/qtestsystem.h>
24#if QT_CONFIG(accessibility)
31using EventList = QList<QAccessibleEvent*>;
33bool operator==(
const QAccessibleEvent &l,
const QAccessibleEvent &r)
35 if (l.type() != r.type()) {
39 if (l.object() != r.object() ||
40 l.child() != r.child()) {
45 if (l.type() == QAccessible::StateChanged) {
46 return static_cast<
const QAccessibleStateChangeEvent*>(&l)->changedStates()
47 ==
static_cast<
const QAccessibleStateChangeEvent*>(&r)->changedStates();
48 }
else if (l.type() == QAccessible::TextCaretMoved) {
49 return static_cast<
const QAccessibleTextCursorEvent*>(&l)->cursorPosition()
50 ==
static_cast<
const QAccessibleTextCursorEvent*>(&r)->cursorPosition();
51 }
else if (l.type() == QAccessible::TextSelectionChanged) {
52 const QAccessibleTextSelectionEvent *le =
static_cast<
const QAccessibleTextSelectionEvent*>(&l);
53 const QAccessibleTextSelectionEvent *re =
static_cast<
const QAccessibleTextSelectionEvent*>(&r);
54 return le->cursorPosition() == re->cursorPosition() &&
55 le->selectionStart() == re->selectionStart() &&
56 le->selectionEnd() == re->selectionEnd();
57 }
else if (l.type() == QAccessible::TextInserted) {
58 const QAccessibleTextInsertEvent *le =
static_cast<
const QAccessibleTextInsertEvent*>(&l);
59 const QAccessibleTextInsertEvent *re =
static_cast<
const QAccessibleTextInsertEvent*>(&r);
60 return le->cursorPosition() == re->cursorPosition() &&
61 le->changePosition() == re->changePosition() &&
62 le->textInserted() == re->textInserted();
63 }
else if (l.type() == QAccessible::TextRemoved) {
64 const QAccessibleTextRemoveEvent *le =
static_cast<
const QAccessibleTextRemoveEvent*>(&l);
65 const QAccessibleTextRemoveEvent *re =
static_cast<
const QAccessibleTextRemoveEvent*>(&r);
66 return le->cursorPosition() == re->cursorPosition() &&
67 le->changePosition() == re->changePosition() &&
68 le->textRemoved() == re->textRemoved();
69 }
else if (l.type() == QAccessible::TextUpdated) {
70 const QAccessibleTextUpdateEvent *le =
static_cast<
const QAccessibleTextUpdateEvent*>(&l);
71 const QAccessibleTextUpdateEvent *re =
static_cast<
const QAccessibleTextUpdateEvent*>(&r);
72 return le->cursorPosition() == re->cursorPosition() &&
73 le->changePosition() == re->changePosition() &&
74 le->textInserted() == re->textInserted() &&
75 le->textRemoved() == re->textRemoved();
76 }
else if (l.type() == QAccessible::ValueChanged) {
77 const QAccessibleValueChangeEvent *le =
static_cast<
const QAccessibleValueChangeEvent*>(&l);
78 const QAccessibleValueChangeEvent *re =
static_cast<
const QAccessibleValueChangeEvent*>(&r);
79 return le->value() == re->value();
84class QTestAccessibility
87 static void initialize()
90 instance() =
new QTestAccessibility;
91 qAddPostRoutine(cleanup);
100 static void clearEvents() { eventList().clear(); }
101 static EventList events() {
return eventList(); }
102 static bool verifyEvent(QAccessibleEvent *ev)
104 for (
int i = 0; eventList().isEmpty() && i < 5; ++i)
106 if (eventList().isEmpty()) {
107 qWarning(
"Timeout waiting for accessibility event.");
111 for (
int i = 0; i < eventList().size(); ++i) {
112 if (*eventList()[i] == *ev) {
114 qWarning() <<
" Found event at position " << i;
115 qWarning(
"%s", qPrintable(msgAccessibilityEventListMismatch(eventList(), ev)));
117 delete eventList().takeAt(i);
122 qWarning(
"%s", qPrintable(msgAccessibilityEventListMismatch(eventList(), ev)));
125 static bool containsEvent(QAccessibleEvent *event) {
126 for (
const QAccessibleEvent *ev : std::as_const(eventList())) {
132 static void setUpdateHandler(std::function<
void(QAccessibleEvent *event)> updateHandler)
134 instance()->m_updateHandler = updateHandler;
140 QAccessible::installUpdateHandler(updateHandler);
141 QAccessible::installRootObjectHandler(rootObjectHandler);
144 ~QTestAccessibility()
146 QAccessible::installUpdateHandler(
nullptr);
147 QAccessible::installRootObjectHandler(
nullptr);
150 static void rootObjectHandler(QObject *object)
154 QGuiApplication* app = qobject_cast<QGuiApplication*>(object);
156 qWarning(
"root Object is not a QGuiApplication!");
158 qWarning(
"root Object called with 0 pointer");
162 static void updateHandler(QAccessibleEvent *event)
164 instance()->m_updateHandler(event);
166 auto ev = copyEvent(event);
167 if (
auto obj = ev->object()) {
168 QObject::connect(obj, &QObject::destroyed, obj, [&, ev](){
169 auto index= eventList().indexOf(ev);
172 eventList().at(index)->m_object =
nullptr;
175 eventList().append(ev);
177 static QAccessibleEvent *copyEvent(QAccessibleEvent *event)
179 QAccessibleEvent *ev;
180 if (event->type() == QAccessible::StateChanged) {
182 ev =
new QAccessibleStateChangeEvent(event->object(),
183 static_cast<QAccessibleStateChangeEvent*>(event)->changedStates());
185 ev =
new QAccessibleStateChangeEvent(event->accessibleInterface(),
186 static_cast<QAccessibleStateChangeEvent*>(event)->changedStates());
187 }
else if (event->type() == QAccessible::TextCaretMoved) {
189 ev =
new QAccessibleTextCursorEvent(event->object(),
static_cast<QAccessibleTextCursorEvent*>(event)->cursorPosition());
191 ev =
new QAccessibleTextCursorEvent(event->accessibleInterface(),
static_cast<QAccessibleTextCursorEvent*>(event)->cursorPosition());
192 }
else if (event->type() == QAccessible::TextSelectionChanged) {
193 const QAccessibleTextSelectionEvent *original =
static_cast<QAccessibleTextSelectionEvent*>(event);
194 QAccessibleTextSelectionEvent *sel;
196 sel =
new QAccessibleTextSelectionEvent(event->object(), original->selectionStart(), original->selectionEnd());
198 sel =
new QAccessibleTextSelectionEvent(event->accessibleInterface(), original->selectionStart(), original->selectionEnd());
199 sel->setCursorPosition(original->cursorPosition());
201 }
else if (event->type() == QAccessible::TextInserted) {
202 const QAccessibleTextInsertEvent *original =
static_cast<QAccessibleTextInsertEvent*>(event);
203 QAccessibleTextInsertEvent *ins;
204 if (original->object())
205 ins =
new QAccessibleTextInsertEvent(event->object(), original->changePosition(), original->textInserted());
207 ins =
new QAccessibleTextInsertEvent(event->accessibleInterface(), original->changePosition(), original->textInserted());
208 ins->setCursorPosition(original->cursorPosition());
210 }
else if (event->type() == QAccessible::TextRemoved) {
211 const QAccessibleTextRemoveEvent *original =
static_cast<QAccessibleTextRemoveEvent*>(event);
212 QAccessibleTextRemoveEvent *rem;
214 rem =
new QAccessibleTextRemoveEvent(event->object(), original->changePosition(), original->textRemoved());
216 rem =
new QAccessibleTextRemoveEvent(event->accessibleInterface(), original->changePosition(), original->textRemoved());
217 rem->setCursorPosition(original->cursorPosition());
219 }
else if (event->type() == QAccessible::TextUpdated) {
220 const QAccessibleTextUpdateEvent *original =
static_cast<QAccessibleTextUpdateEvent*>(event);
221 QAccessibleTextUpdateEvent *upd;
223 upd =
new QAccessibleTextUpdateEvent(event->object(), original->changePosition(), original->textRemoved(), original->textInserted());
225 upd =
new QAccessibleTextUpdateEvent(event->accessibleInterface(), original->changePosition(), original->textRemoved(), original->textInserted());
226 upd->setCursorPosition(original->cursorPosition());
228 }
else if (event->type() == QAccessible::ValueChanged) {
230 ev =
new QAccessibleValueChangeEvent(event->object(),
static_cast<QAccessibleValueChangeEvent*>(event)->value());
232 ev =
new QAccessibleValueChangeEvent(event->accessibleInterface(),
static_cast<QAccessibleValueChangeEvent*>(event)->value());
233 }
else if (event->type() == QAccessible::TableModelChanged) {
234 QAccessibleTableModelChangeEvent *oldEvent =
static_cast<QAccessibleTableModelChangeEvent*>(event);
235 QAccessibleTableModelChangeEvent *newEvent;
237 newEvent =
new QAccessibleTableModelChangeEvent(event->object(), oldEvent->modelChangeType());
239 newEvent =
new QAccessibleTableModelChangeEvent(event->accessibleInterface(), oldEvent->modelChangeType());
240 newEvent->setFirstRow(oldEvent->firstRow());
241 newEvent->setFirstColumn(oldEvent->firstColumn());
242 newEvent->setLastRow(oldEvent->lastRow());
243 newEvent->setLastColumn(oldEvent->lastColumn());
245 }
else if (event->type() == QAccessible::Announcement) {
246 QAccessibleAnnouncementEvent *oldEvent =
247 static_cast<QAccessibleAnnouncementEvent *>(event);
248 QAccessibleAnnouncementEvent *newEvent;
250 newEvent =
new QAccessibleAnnouncementEvent(event->object(), oldEvent->message());
252 newEvent =
new QAccessibleAnnouncementEvent(event->accessibleInterface(),
253 oldEvent->message());
254 newEvent->setPoliteness(oldEvent->politeness());
258 ev =
new QAccessibleEvent(event->object(), event->type());
260 ev =
new QAccessibleEvent(event->accessibleInterface(), event->type());
263 if (ev->type() != QAccessible::ObjectDestroyed)
264 ev->setChild(event->child());
268 static EventList &eventList()
270 static EventList list;
274 static QTestAccessibility *&instance()
276 static QTestAccessibility *ta =
nullptr;
281 static QString msgAccessibilityEventListMismatch(
const EventList &haystack,
282 const QAccessibleEvent *needle)
285 QDebug str = QDebug(&rc).nospace();
286 str <<
"Event " << *needle <<
"\n"
287 <<
" not found at head of event list of size " << haystack.size() <<
" :\n";
288 for (
const QAccessibleEvent *e : haystack)
289 str <<
' ' << *e <<
"\n";
292 std::function<
void(QAccessibleEvent *event)> m_updateHandler = [](QAccessibleEvent *) { ; };