// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include #include #include #include #include #include #include #include #include class tst_QWinEventNotifier : public QObject { Q_OBJECT protected slots: void simple_activated(); void simple_timerSet(); private slots: void simple_data(); void simple(); void blockedWaiting(); void manyNotifiers(); void disableNotifiersInActivatedSlot_data(); void disableNotifiersInActivatedSlot(); private: HANDLE simpleHEvent; bool simpleActivated; }; void tst_QWinEventNotifier::simple_activated() { simpleActivated = true; ResetEvent((HANDLE)simpleHEvent); QTestEventLoop::instance().exitLoop(); } void tst_QWinEventNotifier::simple_timerSet() { SetEvent((HANDLE)simpleHEvent); } void tst_QWinEventNotifier::simple_data() { QTest::addColumn("resetManually"); QTest::newRow("manual_reset") << true; QTest::newRow("auto_reset") << false; } void tst_QWinEventNotifier::simple() { QFETCH(bool, resetManually); simpleHEvent = CreateEvent(0, resetManually, false, 0); QVERIFY(simpleHEvent); QWinEventNotifier n(simpleHEvent); QObject::connect(&n, SIGNAL(activated(HANDLE)), this, SLOT(simple_activated())); simpleActivated = false; SetEvent((HANDLE)simpleHEvent); QTestEventLoop::instance().enterLoop(30); if (QTestEventLoop::instance().timeout()) QFAIL("Timed out"); QVERIFY(simpleActivated); simpleActivated = false; QTimer::singleShot(3000, this, SLOT(simple_timerSet())); QTestEventLoop::instance().enterLoop(30); if (QTestEventLoop::instance().timeout()) QFAIL("Timed out"); QVERIFY(simpleActivated); } void tst_QWinEventNotifier::blockedWaiting() { simpleHEvent = CreateEvent(0, true, false, 0); QWinEventNotifier n(simpleHEvent); QObject::connect(&n, &QWinEventNotifier::activated, this, &tst_QWinEventNotifier::simple_activated); simpleActivated = false; SetEvent(simpleHEvent); QCOMPARE(WaitForSingleObject(simpleHEvent, 1000), WAIT_OBJECT_0); n.setEnabled(false); ResetEvent(simpleHEvent); n.setEnabled(true); QTestEventLoop::instance().enterLoop(1); QVERIFY(QTestEventLoop::instance().timeout()); QVERIFY(!simpleActivated); } class EventWithNotifier : public QObject { Q_OBJECT public: EventWithNotifier() { connect(¬ifier, &QWinEventNotifier::activated, this, &EventWithNotifier::onNotifierActivated); notifier.setHandle(CreateEvent(0, TRUE, FALSE, 0)); notifier.setEnabled(true); } ~EventWithNotifier() { notifier.setEnabled(false); CloseHandle(notifier.handle()); } HANDLE eventHandle() const { return notifier.handle(); } int numberOfTimesActivated() const { return activatedCount; } void setEnabled(bool b) { notifier.setEnabled(b); } bool isEnabled() const { return notifier.isEnabled(); } signals: void activated(); public slots: void onNotifierActivated() { ResetEvent(notifier.handle()); activatedCount++; emit activated(); } private: QWinEventNotifier notifier; int activatedCount = 0; }; void tst_QWinEventNotifier::manyNotifiers() { const size_t maxEvents = 100; const size_t middleEvenEvent = maxEvents / 2; Q_ASSERT(middleEvenEvent % 2 == 0); using EventWithNotifierPtr = std::unique_ptr; std::vector events(maxEvents); std::generate(events.begin(), events.end(), [] () { return EventWithNotifierPtr(new EventWithNotifier); }); QTestEventLoop loop; auto connection = connect(events.at(8).get(), &EventWithNotifier::activated, &loop, &QTestEventLoop::exitLoop); for (const auto &ewn : events) { connect(ewn.get(), &EventWithNotifier::activated, [&events, &loop] () { if (std::all_of(events.cbegin(), events.cend(), [] (const EventWithNotifierPtr &ewn) { return ewn->numberOfTimesActivated() > 0; })) { loop.exitLoop(); } }); } // Activate all even events before running the event loop. for (size_t i = 0; i < events.size(); i += 2) SetEvent(events.at(i)->eventHandle()); // Wait until event notifier with index 8 has been activated. loop.enterLoop(30); QObject::disconnect(connection); // Activate all odd events after the event loop has run for a bit. for (size_t i = 1; i < events.size(); i += 2) SetEvent(events.at(i)->eventHandle()); // Wait until all event notifiers have fired. loop.enterLoop(30); // All notifiers must have been activated exactly once. QVERIFY(std::all_of(events.cbegin(), events.cend(), [] (const EventWithNotifierPtr &ewn) { return ewn->numberOfTimesActivated() == 1; })); } using Indices = QList; void tst_QWinEventNotifier::disableNotifiersInActivatedSlot_data() { QTest::addColumn("count"); QTest::addColumn("notifiersToSignal"); QTest::addColumn("notifiersToDisable"); QTest::addColumn("deleteNotifiers"); QTest::newRow("disable_signaled") << 3 << Indices{1} << Indices{1} << false; QTest::newRow("disable_signaled2") << 3 << Indices{1, 2} << Indices{1} << false; QTest::newRow("disable_before_signaled") << 3 << Indices{1} << Indices{0, 1} << false; QTest::newRow("disable_after_signaled") << 3 << Indices{1} << Indices{1, 2} << false; QTest::newRow("delete_signaled") << 3 << Indices{1} << Indices{1} << true; QTest::newRow("delete_before_signaled1") << 3 << Indices{1} << Indices{0} << true; QTest::newRow("delete_before_signaled2") << 3 << Indices{1} << Indices{0, 1} << true; QTest::newRow("delete_before_signaled3") << 4 << Indices{3, 1} << Indices{0, 1} << true; QTest::newRow("delete_after_signaled1") << 3 << Indices{1} << Indices{1, 2} << true; QTest::newRow("delete_after_signaled2") << 4 << Indices{1, 3} << Indices{1, 2} << true; QTest::newRow("delete_after_signaled3") << 5 << Indices{1} << Indices{1, 4} << true; } void tst_QWinEventNotifier::disableNotifiersInActivatedSlot() { QFETCH(int, count); QFETCH(Indices, notifiersToSignal); QFETCH(Indices, notifiersToDisable); QFETCH(bool, deleteNotifiers); QVarLengthArray, 10> events(count); for (int i = 0; i < count; ++i) events[i].reset(new EventWithNotifier); auto isActivatedOrDisabled = [&events](int i) { return !events.at(i) || !events.at(i)->isEnabled() || events.at(i)->numberOfTimesActivated() > 0; }; for (auto &e : events) { connect(e.get(), &EventWithNotifier::activated, [&]() { for (int i : notifiersToDisable) { if (deleteNotifiers) events[i].reset(); else events.at(i)->setEnabled(false); } if (std::all_of(notifiersToSignal.begin(), notifiersToSignal.end(), isActivatedOrDisabled)) { QTimer::singleShot(0, &QTestEventLoop::instance(), SLOT(exitLoop())); } }); } for (int i : notifiersToSignal) SetEvent(events.at(i)->eventHandle()); QTestEventLoop::instance().enterLoop(30); QVERIFY(!QTestEventLoop::instance().timeout()); } QTEST_MAIN(tst_QWinEventNotifier) #include "tst_qwineventnotifier.moc"