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)
30inline bool QTestAccessibility_cmpEvent(
const QAccessibleEvent &l,
const QAccessibleEvent &r)
32 if (l.type() != r.type()) {
36 if (l.object() != r.object() ||
37 l.child() != r.child()) {
42 if (l.type() == QAccessible::StateChanged) {
43 return static_cast<
const QAccessibleStateChangeEvent*>(&l)->changedStates()
44 ==
static_cast<
const QAccessibleStateChangeEvent*>(&r)->changedStates();
45 }
else if (l.type() == QAccessible::TextCaretMoved) {
46 return static_cast<
const QAccessibleTextCursorEvent*>(&l)->cursorPosition()
47 ==
static_cast<
const QAccessibleTextCursorEvent*>(&r)->cursorPosition();
48 }
else if (l.type() == QAccessible::TextSelectionChanged) {
49 const QAccessibleTextSelectionEvent *le =
static_cast<
const QAccessibleTextSelectionEvent*>(&l);
50 const QAccessibleTextSelectionEvent *re =
static_cast<
const QAccessibleTextSelectionEvent*>(&r);
51 return le->cursorPosition() == re->cursorPosition() &&
52 le->selectionStart() == re->selectionStart() &&
53 le->selectionEnd() == re->selectionEnd();
54 }
else if (l.type() == QAccessible::TextInserted) {
55 const QAccessibleTextInsertEvent *le =
static_cast<
const QAccessibleTextInsertEvent*>(&l);
56 const QAccessibleTextInsertEvent *re =
static_cast<
const QAccessibleTextInsertEvent*>(&r);
57 return le->cursorPosition() == re->cursorPosition() &&
58 le->changePosition() == re->changePosition() &&
59 le->textInserted() == re->textInserted();
60 }
else if (l.type() == QAccessible::TextRemoved) {
61 const QAccessibleTextRemoveEvent *le =
static_cast<
const QAccessibleTextRemoveEvent*>(&l);
62 const QAccessibleTextRemoveEvent *re =
static_cast<
const QAccessibleTextRemoveEvent*>(&r);
63 return le->cursorPosition() == re->cursorPosition() &&
64 le->changePosition() == re->changePosition() &&
65 le->textRemoved() == re->textRemoved();
66 }
else if (l.type() == QAccessible::TextUpdated) {
67 const QAccessibleTextUpdateEvent *le =
static_cast<
const QAccessibleTextUpdateEvent*>(&l);
68 const QAccessibleTextUpdateEvent *re =
static_cast<
const QAccessibleTextUpdateEvent*>(&r);
69 return le->cursorPosition() == re->cursorPosition() &&
70 le->changePosition() == re->changePosition() &&
71 le->textInserted() == re->textInserted() &&
72 le->textRemoved() == re->textRemoved();
73 }
else if (l.type() == QAccessible::ValueChanged) {
74 const QAccessibleValueChangeEvent *le =
static_cast<
const QAccessibleValueChangeEvent*>(&l);
75 const QAccessibleValueChangeEvent *re =
static_cast<
const QAccessibleValueChangeEvent*>(&r);
76 return le->value() == re->value();
81class QTestAccessibility
83 Q_DISABLE_COPY_MOVE(QTestAccessibility)
86 using EventList = QList<QAccessibleEvent*>;
88 static void initialize()
91 instance() =
new QTestAccessibility;
92 qAddPostRoutine(cleanup);
101 static void clearEvents() { eventList().clear(); }
102 static EventList events() {
return eventList(); }
103 static bool verifyEvent(QAccessibleEvent *ev)
105 for (
int i = 0; eventList().isEmpty() && i < 5; ++i)
107 if (eventList().isEmpty()) {
108 qWarning(
"Timeout waiting for accessibility event.");
112 for (qsizetype i = 0; i < eventList().size(); ++i) {
113 if (QTestAccessibility_cmpEvent(*eventList().at(i), *ev)) {
115 qWarning() <<
" Found event at position " << i;
116 qWarning(
"%s", qPrintable(msgAccessibilityEventListMismatch(eventList(), ev)));
118 delete eventList().takeAt(i);
123 qWarning(
"%s", qPrintable(msgAccessibilityEventListMismatch(eventList(), ev)));
126 static bool containsEvent(QAccessibleEvent *event) {
127 for (
const QAccessibleEvent *ev : std::as_const(eventList())) {
128 if (QTestAccessibility_cmpEvent(*ev, *event))
133 static bool containsEventOfType(QAccessible::Event evtype) {
134 for (
const QAccessibleEvent *ev : std::as_const(eventList())) {
135 if (ev->type() == evtype)
140 static void setUpdateHandler(std::function<
void(QAccessibleEvent *event)> updateHandler)
142 Q_ASSERT_X(updateHandler,
__FUNCTION__,
"Update handler cannot be nullptr");
143 instance()->m_updateHandler = std::move(updateHandler);
149 QAccessible::installUpdateHandler(updateHandler);
150 QAccessible::installRootObjectHandler(rootObjectHandler);
153 ~QTestAccessibility()
155 QAccessible::installUpdateHandler(
nullptr);
156 QAccessible::installRootObjectHandler(
nullptr);
159 static void rootObjectHandler(QObject *object)
163 QGuiApplication* app = qobject_cast<QGuiApplication*>(object);
165 qWarning(
"root Object is not a QGuiApplication!");
167 qWarning(
"root Object called with 0 pointer");
171 static void updateHandler(QAccessibleEvent *event)
173 instance()->m_updateHandler(event);
175 auto ev = copyEvent(event);
176 if (
auto obj = ev->object()) {
177 QObject::connect(obj, &QObject::destroyed, obj, [&, ev](){
178 auto index= eventList().indexOf(ev);
181 eventList().at(index)->m_object =
nullptr;
184 eventList().append(ev);
186 static QAccessibleEvent *copyEvent(QAccessibleEvent *event)
188 QAccessibleEvent *ev;
189 if (event->type() == QAccessible::StateChanged) {
191 ev =
new QAccessibleStateChangeEvent(event->object(),
192 static_cast<QAccessibleStateChangeEvent*>(event)->changedStates());
194 ev =
new QAccessibleStateChangeEvent(event->accessibleInterface(),
195 static_cast<QAccessibleStateChangeEvent*>(event)->changedStates());
196 }
else if (event->type() == QAccessible::TextCaretMoved) {
198 ev =
new QAccessibleTextCursorEvent(event->object(),
static_cast<QAccessibleTextCursorEvent*>(event)->cursorPosition());
200 ev =
new QAccessibleTextCursorEvent(event->accessibleInterface(),
static_cast<QAccessibleTextCursorEvent*>(event)->cursorPosition());
201 }
else if (event->type() == QAccessible::TextSelectionChanged) {
202 const QAccessibleTextSelectionEvent *original =
static_cast<QAccessibleTextSelectionEvent*>(event);
203 QAccessibleTextSelectionEvent *sel;
205 sel =
new QAccessibleTextSelectionEvent(event->object(), original->selectionStart(), original->selectionEnd());
207 sel =
new QAccessibleTextSelectionEvent(event->accessibleInterface(), original->selectionStart(), original->selectionEnd());
208 sel->setCursorPosition(original->cursorPosition());
210 }
else if (event->type() == QAccessible::TextInserted) {
211 const QAccessibleTextInsertEvent *original =
static_cast<QAccessibleTextInsertEvent*>(event);
212 QAccessibleTextInsertEvent *ins;
213 if (original->object())
214 ins =
new QAccessibleTextInsertEvent(event->object(), original->changePosition(), original->textInserted());
216 ins =
new QAccessibleTextInsertEvent(event->accessibleInterface(), original->changePosition(), original->textInserted());
217 ins->setCursorPosition(original->cursorPosition());
219 }
else if (event->type() == QAccessible::TextRemoved) {
220 const QAccessibleTextRemoveEvent *original =
static_cast<QAccessibleTextRemoveEvent*>(event);
221 QAccessibleTextRemoveEvent *rem;
223 rem =
new QAccessibleTextRemoveEvent(event->object(), original->changePosition(), original->textRemoved());
225 rem =
new QAccessibleTextRemoveEvent(event->accessibleInterface(), original->changePosition(), original->textRemoved());
226 rem->setCursorPosition(original->cursorPosition());
228 }
else if (event->type() == QAccessible::TextUpdated) {
229 const QAccessibleTextUpdateEvent *original =
static_cast<QAccessibleTextUpdateEvent*>(event);
230 QAccessibleTextUpdateEvent *upd;
232 upd =
new QAccessibleTextUpdateEvent(event->object(), original->changePosition(), original->textRemoved(), original->textInserted());
234 upd =
new QAccessibleTextUpdateEvent(event->accessibleInterface(), original->changePosition(), original->textRemoved(), original->textInserted());
235 upd->setCursorPosition(original->cursorPosition());
237 }
else if (event->type() == QAccessible::ValueChanged) {
239 ev =
new QAccessibleValueChangeEvent(event->object(),
static_cast<QAccessibleValueChangeEvent*>(event)->value());
241 ev =
new QAccessibleValueChangeEvent(event->accessibleInterface(),
static_cast<QAccessibleValueChangeEvent*>(event)->value());
242 }
else if (event->type() == QAccessible::TableModelChanged) {
243 QAccessibleTableModelChangeEvent *oldEvent =
static_cast<QAccessibleTableModelChangeEvent*>(event);
244 QAccessibleTableModelChangeEvent *newEvent;
246 newEvent =
new QAccessibleTableModelChangeEvent(event->object(), oldEvent->modelChangeType());
248 newEvent =
new QAccessibleTableModelChangeEvent(event->accessibleInterface(), oldEvent->modelChangeType());
249 newEvent->setFirstRow(oldEvent->firstRow());
250 newEvent->setFirstColumn(oldEvent->firstColumn());
251 newEvent->setLastRow(oldEvent->lastRow());
252 newEvent->setLastColumn(oldEvent->lastColumn());
254 }
else if (event->type() == QAccessible::Announcement) {
255 QAccessibleAnnouncementEvent *oldEvent =
256 static_cast<QAccessibleAnnouncementEvent *>(event);
257 QAccessibleAnnouncementEvent *newEvent;
259 newEvent =
new QAccessibleAnnouncementEvent(event->object(), oldEvent->message());
261 newEvent =
new QAccessibleAnnouncementEvent(event->accessibleInterface(),
262 oldEvent->message());
263 newEvent->setPoliteness(oldEvent->politeness());
267 ev =
new QAccessibleEvent(event->object(), event->type());
269 ev =
new QAccessibleEvent(event->accessibleInterface(), event->type());
272 if (ev->type() != QAccessible::ObjectDestroyed)
273 ev->setChild(event->child());
277 Q_TESTLIB_EXPORT
static EventList &eventList();
278 Q_TESTLIB_EXPORT
static QTestAccessibility *&instance();
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 *) { ; };
296#if QT_DEPRECATED_SINCE(6
, 11
)
297using EventList QT_DEPRECATED_VERSION_X_6_11(
"Use QTestAccessibility::EventList")
298 = QTestAccessibility::EventList;
300QT_DEPRECATED_VERSION_X_6_11(
"This was an internal function for QTestAccessibility. "
301 "Only QAccessibleEvent is allowed to define operator== for itself, "
302 "and it doesn't, so use a named function instead")
303inline bool operator==(
const QAccessibleEvent &l,
const QAccessibleEvent &r)
305 return QTestAccessibility_cmpEvent(l, r);