File indexing completed on 2026-08-05 09:29:46
0001
0002
0003
0004 #ifndef QTESTACCESSIBLE_H
0005 #define QTESTACCESSIBLE_H
0006
0007 #if 0
0008
0009 #pragma qt_no_master_include
0010 #endif
0011
0012 #include <QtCore/qglobal.h>
0013
0014 #define QVERIFY_EVENT(event) \
0015 QVERIFY(QTestAccessibility::verifyEvent(event))
0016
0017 #include <QtCore/qlist.h>
0018 #include <QtCore/qdebug.h>
0019 #include <QtGui/qaccessible.h>
0020 #include <QtGui/qguiapplication.h>
0021 #include <QtTest/qttestglobal.h>
0022 #include <QtTest/qtestsystem.h>
0023
0024 #if QT_CONFIG(accessibility)
0025
0026 QT_BEGIN_NAMESPACE
0027
0028 class QObject;
0029
0030 inline bool QTestAccessibility_cmpEvent(const QAccessibleEvent &l, const QAccessibleEvent &r)
0031 {
0032 if (l.type() != r.type()) {
0033
0034 return false;
0035 }
0036 if (l.object() != r.object() ||
0037 l.child() != r.child()) {
0038
0039 return false;
0040 }
0041
0042 if (l.type() == QAccessible::StateChanged) {
0043 return static_cast<const QAccessibleStateChangeEvent*>(&l)->changedStates()
0044 == static_cast<const QAccessibleStateChangeEvent*>(&r)->changedStates();
0045 } else if (l.type() == QAccessible::TextCaretMoved) {
0046 return static_cast<const QAccessibleTextCursorEvent*>(&l)->cursorPosition()
0047 == static_cast<const QAccessibleTextCursorEvent*>(&r)->cursorPosition();
0048 } else if (l.type() == QAccessible::TextSelectionChanged) {
0049 const QAccessibleTextSelectionEvent *le = static_cast<const QAccessibleTextSelectionEvent*>(&l);
0050 const QAccessibleTextSelectionEvent *re = static_cast<const QAccessibleTextSelectionEvent*>(&r);
0051 return le->cursorPosition() == re->cursorPosition() &&
0052 le->selectionStart() == re->selectionStart() &&
0053 le->selectionEnd() == re->selectionEnd();
0054 } else if (l.type() == QAccessible::TextInserted) {
0055 const QAccessibleTextInsertEvent *le = static_cast<const QAccessibleTextInsertEvent*>(&l);
0056 const QAccessibleTextInsertEvent *re = static_cast<const QAccessibleTextInsertEvent*>(&r);
0057 return le->cursorPosition() == re->cursorPosition() &&
0058 le->changePosition() == re->changePosition() &&
0059 le->textInserted() == re->textInserted();
0060 } else if (l.type() == QAccessible::TextRemoved) {
0061 const QAccessibleTextRemoveEvent *le = static_cast<const QAccessibleTextRemoveEvent*>(&l);
0062 const QAccessibleTextRemoveEvent *re = static_cast<const QAccessibleTextRemoveEvent*>(&r);
0063 return le->cursorPosition() == re->cursorPosition() &&
0064 le->changePosition() == re->changePosition() &&
0065 le->textRemoved() == re->textRemoved();
0066 } else if (l.type() == QAccessible::TextUpdated) {
0067 const QAccessibleTextUpdateEvent *le = static_cast<const QAccessibleTextUpdateEvent*>(&l);
0068 const QAccessibleTextUpdateEvent *re = static_cast<const QAccessibleTextUpdateEvent*>(&r);
0069 return le->cursorPosition() == re->cursorPosition() &&
0070 le->changePosition() == re->changePosition() &&
0071 le->textInserted() == re->textInserted() &&
0072 le->textRemoved() == re->textRemoved();
0073 } else if (l.type() == QAccessible::ValueChanged) {
0074 const QAccessibleValueChangeEvent *le = static_cast<const QAccessibleValueChangeEvent*>(&l);
0075 const QAccessibleValueChangeEvent *re = static_cast<const QAccessibleValueChangeEvent*>(&r);
0076 return le->value() == re->value();
0077 }
0078 return true;
0079 }
0080
0081 class QTestAccessibility
0082 {
0083 Q_DISABLE_COPY_MOVE(QTestAccessibility)
0084 public:
0085
0086 using EventList = QList<QAccessibleEvent*>;
0087
0088 static void initialize()
0089 {
0090 if (!instance()) {
0091 instance() = new QTestAccessibility;
0092 qAddPostRoutine(cleanup);
0093 }
0094 }
0095
0096 static void cleanup()
0097 {
0098 delete instance();
0099 instance() = nullptr;
0100 }
0101 static void clearEvents() { eventList().clear(); }
0102 static EventList events() { return eventList(); }
0103 static bool verifyEvent(QAccessibleEvent *ev)
0104 {
0105 for (int i = 0; eventList().isEmpty() && i < 5; ++i)
0106 QTest::qWait(50);
0107 if (eventList().isEmpty()) {
0108 qWarning("Timeout waiting for accessibility event.");
0109 return false;
0110 }
0111
0112 for (qsizetype i = 0; i < eventList().size(); ++i) {
0113 if (QTestAccessibility_cmpEvent(*eventList().at(i), *ev)) {
0114 if (i != 0) {
0115 qWarning() << " Found event at position " << i;
0116 qWarning("%s", qPrintable(msgAccessibilityEventListMismatch(eventList(), ev)));
0117 }
0118 delete eventList().takeAt(i);
0119 return true;
0120 }
0121 }
0122
0123 qWarning("%s", qPrintable(msgAccessibilityEventListMismatch(eventList(), ev)));
0124 return false;
0125 }
0126 static bool containsEvent(QAccessibleEvent *event) {
0127 for (const QAccessibleEvent *ev : std::as_const(eventList())) {
0128 if (QTestAccessibility_cmpEvent(*ev, *event))
0129 return true;
0130 }
0131 return false;
0132 }
0133 static bool containsEventOfType(QAccessible::Event evtype) {
0134 for (const QAccessibleEvent *ev : std::as_const(eventList())) {
0135 if (ev->type() == evtype)
0136 return true;
0137 }
0138 return false;
0139 }
0140 static void setUpdateHandler(std::function<void(QAccessibleEvent *event)> updateHandler)
0141 {
0142 Q_ASSERT_X(updateHandler, __FUNCTION__, "Update handler cannot be nullptr");
0143 instance()->m_updateHandler = std::move(updateHandler);
0144 }
0145
0146 private:
0147 QTestAccessibility()
0148 {
0149 QAccessible::installUpdateHandler(updateHandler);
0150 QAccessible::installRootObjectHandler(rootObjectHandler);
0151 }
0152
0153 ~QTestAccessibility()
0154 {
0155 QAccessible::installUpdateHandler(nullptr);
0156 QAccessible::installRootObjectHandler(nullptr);
0157 }
0158
0159 static void rootObjectHandler(QObject *object)
0160 {
0161
0162 if (object) {
0163 QGuiApplication* app = qobject_cast<QGuiApplication*>(object);
0164 if ( !app )
0165 qWarning("root Object is not a QGuiApplication!");
0166 } else {
0167 qWarning("root Object called with 0 pointer");
0168 }
0169 }
0170
0171 static void updateHandler(QAccessibleEvent *event)
0172 {
0173 instance()->m_updateHandler(event);
0174
0175 auto ev = copyEvent(event);
0176 if (auto obj = ev->object()) {
0177 QObject::connect(obj, &QObject::destroyed, obj, [&, ev](){
0178 auto index= eventList().indexOf(ev);
0179 if (index == -1)
0180 return;
0181 eventList().at(index)->m_object = nullptr;
0182 });
0183 }
0184 eventList().append(ev);
0185 }
0186 static QAccessibleEvent *copyEvent(QAccessibleEvent *event)
0187 {
0188 QAccessibleEvent *ev;
0189 if (event->type() == QAccessible::StateChanged) {
0190 if (event->object())
0191 ev = new QAccessibleStateChangeEvent(event->object(),
0192 static_cast<QAccessibleStateChangeEvent*>(event)->changedStates());
0193 else
0194 ev = new QAccessibleStateChangeEvent(event->accessibleInterface(),
0195 static_cast<QAccessibleStateChangeEvent*>(event)->changedStates());
0196 } else if (event->type() == QAccessible::TextCaretMoved) {
0197 if (event->object())
0198 ev = new QAccessibleTextCursorEvent(event->object(), static_cast<QAccessibleTextCursorEvent*>(event)->cursorPosition());
0199 else
0200 ev = new QAccessibleTextCursorEvent(event->accessibleInterface(), static_cast<QAccessibleTextCursorEvent*>(event)->cursorPosition());
0201 } else if (event->type() == QAccessible::TextSelectionChanged) {
0202 const QAccessibleTextSelectionEvent *original = static_cast<QAccessibleTextSelectionEvent*>(event);
0203 QAccessibleTextSelectionEvent *sel;
0204 if (event->object())
0205 sel = new QAccessibleTextSelectionEvent(event->object(), original->selectionStart(), original->selectionEnd());
0206 else
0207 sel = new QAccessibleTextSelectionEvent(event->accessibleInterface(), original->selectionStart(), original->selectionEnd());
0208 sel->setCursorPosition(original->cursorPosition());
0209 ev = sel;
0210 } else if (event->type() == QAccessible::TextInserted) {
0211 const QAccessibleTextInsertEvent *original = static_cast<QAccessibleTextInsertEvent*>(event);
0212 QAccessibleTextInsertEvent *ins;
0213 if (original->object())
0214 ins = new QAccessibleTextInsertEvent(event->object(), original->changePosition(), original->textInserted());
0215 else
0216 ins = new QAccessibleTextInsertEvent(event->accessibleInterface(), original->changePosition(), original->textInserted());
0217 ins->setCursorPosition(original->cursorPosition());
0218 ev = ins;
0219 } else if (event->type() == QAccessible::TextRemoved) {
0220 const QAccessibleTextRemoveEvent *original = static_cast<QAccessibleTextRemoveEvent*>(event);
0221 QAccessibleTextRemoveEvent *rem;
0222 if (event->object())
0223 rem = new QAccessibleTextRemoveEvent(event->object(), original->changePosition(), original->textRemoved());
0224 else
0225 rem = new QAccessibleTextRemoveEvent(event->accessibleInterface(), original->changePosition(), original->textRemoved());
0226 rem->setCursorPosition(original->cursorPosition());
0227 ev = rem;
0228 } else if (event->type() == QAccessible::TextUpdated) {
0229 const QAccessibleTextUpdateEvent *original = static_cast<QAccessibleTextUpdateEvent*>(event);
0230 QAccessibleTextUpdateEvent *upd;
0231 if (event->object())
0232 upd = new QAccessibleTextUpdateEvent(event->object(), original->changePosition(), original->textRemoved(), original->textInserted());
0233 else
0234 upd = new QAccessibleTextUpdateEvent(event->accessibleInterface(), original->changePosition(), original->textRemoved(), original->textInserted());
0235 upd->setCursorPosition(original->cursorPosition());
0236 ev = upd;
0237 } else if (event->type() == QAccessible::ValueChanged) {
0238 if (event->object())
0239 ev = new QAccessibleValueChangeEvent(event->object(), static_cast<QAccessibleValueChangeEvent*>(event)->value());
0240 else
0241 ev = new QAccessibleValueChangeEvent(event->accessibleInterface(), static_cast<QAccessibleValueChangeEvent*>(event)->value());
0242 } else if (event->type() == QAccessible::TableModelChanged) {
0243 QAccessibleTableModelChangeEvent *oldEvent = static_cast<QAccessibleTableModelChangeEvent*>(event);
0244 QAccessibleTableModelChangeEvent *newEvent;
0245 if (event->object())
0246 newEvent = new QAccessibleTableModelChangeEvent(event->object(), oldEvent->modelChangeType());
0247 else
0248 newEvent = new QAccessibleTableModelChangeEvent(event->accessibleInterface(), oldEvent->modelChangeType());
0249 newEvent->setFirstRow(oldEvent->firstRow());
0250 newEvent->setFirstColumn(oldEvent->firstColumn());
0251 newEvent->setLastRow(oldEvent->lastRow());
0252 newEvent->setLastColumn(oldEvent->lastColumn());
0253 ev = newEvent;
0254 } else if (event->type() == QAccessible::Announcement) {
0255 QAccessibleAnnouncementEvent *oldEvent =
0256 static_cast<QAccessibleAnnouncementEvent *>(event);
0257 QAccessibleAnnouncementEvent *newEvent;
0258 if (event->object())
0259 newEvent = new QAccessibleAnnouncementEvent(event->object(), oldEvent->message());
0260 else
0261 newEvent = new QAccessibleAnnouncementEvent(event->accessibleInterface(),
0262 oldEvent->message());
0263 newEvent->setPoliteness(oldEvent->politeness());
0264 ev = newEvent;
0265 } else {
0266 if (event->object())
0267 ev = new QAccessibleEvent(event->object(), event->type());
0268 else
0269 ev = new QAccessibleEvent(event->accessibleInterface(), event->type());
0270 }
0271
0272 if (ev->type() != QAccessible::ObjectDestroyed)
0273 ev->setChild(event->child());
0274 return ev;
0275 }
0276
0277 Q_TESTLIB_EXPORT static EventList &eventList();
0278 Q_TESTLIB_EXPORT static QTestAccessibility *&instance();
0279
0280 private:
0281 static QString msgAccessibilityEventListMismatch(const EventList &haystack,
0282 const QAccessibleEvent *needle)
0283 {
0284 QString rc;
0285 QDebug str = QDebug(&rc).nospace();
0286 str << "Event " << *needle << "\n"
0287 << " not found at head of event list of size " << haystack.size() << " :\n";
0288 for (const QAccessibleEvent *e : haystack)
0289 str << ' ' << *e << "\n";
0290 return rc;
0291 }
0292 std::function<void(QAccessibleEvent *event)> m_updateHandler = [](QAccessibleEvent *) { ; };
0293
0294 };
0295
0296 #if QT_DEPRECATED_SINCE(6, 11)
0297 using EventList QT_DEPRECATED_VERSION_X_6_11("Use QTestAccessibility::EventList")
0298 = QTestAccessibility::EventList;
0299
0300 QT_DEPRECATED_VERSION_X_6_11("This was an internal function for QTestAccessibility. "
0301 "Only QAccessibleEvent is allowed to define operator== for itself, "
0302 "and it doesn't, so use a named function instead")
0303 inline bool operator==(const QAccessibleEvent &l, const QAccessibleEvent &r)
0304 {
0305 return QTestAccessibility_cmpEvent(l, r);
0306 }
0307 #endif
0308
0309 QT_END_NAMESPACE
0310
0311 #endif
0312 #endif