Revision control

Copy as Markdown

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QTestEventLoop>
#include <qlist.h>
#include <qtimer.h>
#include <qvarlengtharray.h>
#include <qwineventnotifier.h>
#include <qt_windows.h>
#include <algorithm>
#include <memory>
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<bool>("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(&notifier, &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<EventWithNotifier>;
std::vector<EventWithNotifierPtr> 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<int>;
void tst_QWinEventNotifier::disableNotifiersInActivatedSlot_data()
{
QTest::addColumn<int>("count");
QTest::addColumn<Indices>("notifiersToSignal");
QTest::addColumn<Indices>("notifiersToDisable");
QTest::addColumn<bool>("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<std::unique_ptr<EventWithNotifier>, 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"