File indexing completed on 2025-02-21 10:12:23
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
0029 class QObject;
0030
0031
0032 using EventList = QList<QAccessibleEvent*>;
0033
0034 bool operator==(const QAccessibleEvent &l, const QAccessibleEvent &r)
0035 {
0036 if (l.type() != r.type()) {
0037
0038 return false;
0039 }
0040 if (l.object() != r.object() ||
0041 l.child() != r.child()) {
0042
0043 return false;
0044 }
0045
0046 if (l.type() == QAccessible::StateChanged) {
0047 return static_cast<const QAccessibleStateChangeEvent*>(&l)->changedStates()
0048 == static_cast<const QAccessibleStateChangeEvent*>(&r)->changedStates();
0049 } else if (l.type() == QAccessible::TextCaretMoved) {
0050 return static_cast<const QAccessibleTextCursorEvent*>(&l)->cursorPosition()
0051 == static_cast<const QAccessibleTextCursorEvent*>(&r)->cursorPosition();
0052 } else if (l.type() == QAccessible::TextSelectionChanged) {
0053 const QAccessibleTextSelectionEvent *le = static_cast<const QAccessibleTextSelectionEvent*>(&l);
0054 const QAccessibleTextSelectionEvent *re = static_cast<const QAccessibleTextSelectionEvent*>(&r);
0055 return le->cursorPosition() == re->cursorPosition() &&
0056 le->selectionStart() == re->selectionStart() &&
0057 le->selectionEnd() == re->selectionEnd();
0058 } else if (l.type() == QAccessible::TextInserted) {
0059 const QAccessibleTextInsertEvent *le = static_cast<const QAccessibleTextInsertEvent*>(&l);
0060 const QAccessibleTextInsertEvent *re = static_cast<const QAccessibleTextInsertEvent*>(&r);
0061 return le->cursorPosition() == re->cursorPosition() &&
0062 le->changePosition() == re->changePosition() &&
0063 le->textInserted() == re->textInserted();
0064 } else if (l.type() == QAccessible::TextRemoved) {
0065 const QAccessibleTextRemoveEvent *le = static_cast<const QAccessibleTextRemoveEvent*>(&l);
0066 const QAccessibleTextRemoveEvent *re = static_cast<const QAccessibleTextRemoveEvent*>(&r);
0067 return le->cursorPosition() == re->cursorPosition() &&
0068 le->changePosition() == re->changePosition() &&
0069 le->textRemoved() == re->textRemoved();
0070 } else if (l.type() == QAccessible::TextUpdated) {
0071 const QAccessibleTextUpdateEvent *le = static_cast<const QAccessibleTextUpdateEvent*>(&l);
0072 const QAccessibleTextUpdateEvent *re = static_cast<const QAccessibleTextUpdateEvent*>(&r);
0073 return le->cursorPosition() == re->cursorPosition() &&
0074 le->changePosition() == re->changePosition() &&
0075 le->textInserted() == re->textInserted() &&
0076 le->textRemoved() == re->textRemoved();
0077 } else if (l.type() == QAccessible::ValueChanged) {
0078 const QAccessibleValueChangeEvent *le = static_cast<const QAccessibleValueChangeEvent*>(&l);
0079 const QAccessibleValueChangeEvent *re = static_cast<const QAccessibleValueChangeEvent*>(&r);
0080 return le->value() == re->value();
0081 }
0082 return true;
0083 }
0084
0085 class QTestAccessibility
0086 {
0087 public:
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 const bool res = *eventList().constFirst() == *ev;
0112 if (!res)
0113 qWarning("%s", qPrintable(msgAccessibilityEventListMismatch(eventList(), ev)));
0114 delete eventList().takeFirst();
0115 return res;
0116 }
0117 static bool containsEvent(QAccessibleEvent *event) {
0118 for (const QAccessibleEvent *ev : std::as_const(eventList())) {
0119 if (*ev == *event)
0120 return true;
0121 }
0122 return false;
0123 }
0124
0125 private:
0126 QTestAccessibility()
0127 {
0128 QAccessible::installUpdateHandler(updateHandler);
0129 QAccessible::installRootObjectHandler(rootObjectHandler);
0130 }
0131
0132 ~QTestAccessibility()
0133 {
0134 QAccessible::installUpdateHandler(nullptr);
0135 QAccessible::installRootObjectHandler(nullptr);
0136 }
0137
0138 static void rootObjectHandler(QObject *object)
0139 {
0140
0141 if (object) {
0142 QGuiApplication* app = qobject_cast<QGuiApplication*>(object);
0143 if ( !app )
0144 qWarning("root Object is not a QGuiApplication!");
0145 } else {
0146 qWarning("root Object called with 0 pointer");
0147 }
0148 }
0149
0150 static void updateHandler(QAccessibleEvent *event)
0151 {
0152 auto ev = copyEvent(event);
0153 if (auto obj = ev->object()) {
0154 QObject::connect(obj, &QObject::destroyed, obj, [&, ev](){
0155 auto index= eventList().indexOf(ev);
0156 if (index == -1)
0157 return;
0158 eventList().at(index)->m_object = nullptr;
0159 });
0160 }
0161 eventList().append(ev);
0162 }
0163 static QAccessibleEvent *copyEvent(QAccessibleEvent *event)
0164 {
0165 QAccessibleEvent *ev;
0166 if (event->type() == QAccessible::StateChanged) {
0167 if (event->object())
0168 ev = new QAccessibleStateChangeEvent(event->object(),
0169 static_cast<QAccessibleStateChangeEvent*>(event)->changedStates());
0170 else
0171 ev = new QAccessibleStateChangeEvent(event->accessibleInterface(),
0172 static_cast<QAccessibleStateChangeEvent*>(event)->changedStates());
0173 } else if (event->type() == QAccessible::TextCaretMoved) {
0174 if (event->object())
0175 ev = new QAccessibleTextCursorEvent(event->object(), static_cast<QAccessibleTextCursorEvent*>(event)->cursorPosition());
0176 else
0177 ev = new QAccessibleTextCursorEvent(event->accessibleInterface(), static_cast<QAccessibleTextCursorEvent*>(event)->cursorPosition());
0178 } else if (event->type() == QAccessible::TextSelectionChanged) {
0179 const QAccessibleTextSelectionEvent *original = static_cast<QAccessibleTextSelectionEvent*>(event);
0180 QAccessibleTextSelectionEvent *sel;
0181 if (event->object())
0182 sel = new QAccessibleTextSelectionEvent(event->object(), original->selectionStart(), original->selectionEnd());
0183 else
0184 sel = new QAccessibleTextSelectionEvent(event->accessibleInterface(), original->selectionStart(), original->selectionEnd());
0185 sel->setCursorPosition(original->cursorPosition());
0186 ev = sel;
0187 } else if (event->type() == QAccessible::TextInserted) {
0188 const QAccessibleTextInsertEvent *original = static_cast<QAccessibleTextInsertEvent*>(event);
0189 QAccessibleTextInsertEvent *ins;
0190 if (original->object())
0191 ins = new QAccessibleTextInsertEvent(event->object(), original->changePosition(), original->textInserted());
0192 else
0193 ins = new QAccessibleTextInsertEvent(event->accessibleInterface(), original->changePosition(), original->textInserted());
0194 ins->setCursorPosition(original->cursorPosition());
0195 ev = ins;
0196 } else if (event->type() == QAccessible::TextRemoved) {
0197 const QAccessibleTextRemoveEvent *original = static_cast<QAccessibleTextRemoveEvent*>(event);
0198 QAccessibleTextRemoveEvent *rem;
0199 if (event->object())
0200 rem = new QAccessibleTextRemoveEvent(event->object(), original->changePosition(), original->textRemoved());
0201 else
0202 rem = new QAccessibleTextRemoveEvent(event->accessibleInterface(), original->changePosition(), original->textRemoved());
0203 rem->setCursorPosition(original->cursorPosition());
0204 ev = rem;
0205 } else if (event->type() == QAccessible::TextUpdated) {
0206 const QAccessibleTextUpdateEvent *original = static_cast<QAccessibleTextUpdateEvent*>(event);
0207 QAccessibleTextUpdateEvent *upd;
0208 if (event->object())
0209 upd = new QAccessibleTextUpdateEvent(event->object(), original->changePosition(), original->textRemoved(), original->textInserted());
0210 else
0211 upd = new QAccessibleTextUpdateEvent(event->accessibleInterface(), original->changePosition(), original->textRemoved(), original->textInserted());
0212 upd->setCursorPosition(original->cursorPosition());
0213 ev = upd;
0214 } else if (event->type() == QAccessible::ValueChanged) {
0215 if (event->object())
0216 ev = new QAccessibleValueChangeEvent(event->object(), static_cast<QAccessibleValueChangeEvent*>(event)->value());
0217 else
0218 ev = new QAccessibleValueChangeEvent(event->accessibleInterface(), static_cast<QAccessibleValueChangeEvent*>(event)->value());
0219 } else if (event->type() == QAccessible::TableModelChanged) {
0220 QAccessibleTableModelChangeEvent *oldEvent = static_cast<QAccessibleTableModelChangeEvent*>(event);
0221 QAccessibleTableModelChangeEvent *newEvent;
0222 if (event->object())
0223 newEvent = new QAccessibleTableModelChangeEvent(event->object(), oldEvent->modelChangeType());
0224 else
0225 newEvent = new QAccessibleTableModelChangeEvent(event->accessibleInterface(), oldEvent->modelChangeType());
0226 newEvent->setFirstRow(oldEvent->firstRow());
0227 newEvent->setFirstColumn(oldEvent->firstColumn());
0228 newEvent->setLastRow(oldEvent->lastRow());
0229 newEvent->setLastColumn(oldEvent->lastColumn());
0230 ev = newEvent;
0231 } else {
0232 if (event->object())
0233 ev = new QAccessibleEvent(event->object(), event->type());
0234 else
0235 ev = new QAccessibleEvent(event->accessibleInterface(), event->type());
0236 }
0237 ev->setChild(event->child());
0238 return ev;
0239 }
0240
0241 static EventList &eventList()
0242 {
0243 static EventList list;
0244 return list;
0245 }
0246
0247 static QTestAccessibility *&instance()
0248 {
0249 static QTestAccessibility *ta = nullptr;
0250 return ta;
0251 }
0252
0253 private:
0254 static QString msgAccessibilityEventListMismatch(const EventList &haystack,
0255 const QAccessibleEvent *needle)
0256 {
0257 QString rc;
0258 QDebug str = QDebug(&rc).nospace();
0259 str << "Event " << *needle
0260 << " not found at head of event list of size " << haystack.size() << " :";
0261 for (const QAccessibleEvent *e : haystack)
0262 str << ' ' << *e;
0263 return rc;
0264 }
0265
0266 };
0267
0268 QT_END_NAMESPACE
0269
0270 #endif
0271 #endif