// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include #include #include #include using namespace emscripten; const std::chrono::milliseconds timerTimeout{10}; // Test QWasmSuspendResumeControl suspend/resume and event processing, // via QWasmTimer native timer events. class WasmSuspendResumeControlTest: public QObject { Q_OBJECT private slots: void timer(); void multipleTimers(); void reuseTimer(); void cancelTimer(); void deleteTimer(); void suspendExclusive(); }; // Verify that a single timer fires void WasmSuspendResumeControlTest::timer() { QWasmSuspendResumeControl suspendResume; bool timerFired = false; QWasmTimer timer(&suspendResume, [&timerFired](){ timerFired = true; }); timer.setTimeout(timerTimeout); while (!timerFired) { suspendResume.suspend(); suspendResume.sendPendingEvents(); } QWASMSUCCESS(); } // Verify that multiple parallel timers fire void WasmSuspendResumeControlTest::multipleTimers() { QWasmSuspendResumeControl suspendResume; const int expectedTimers = 10; int compledtedTimers = 0; std::unique_ptr timers[expectedTimers]; for (int i = 0; i < expectedTimers; ++i) { timers[i] = std::make_unique(&suspendResume, [&compledtedTimers](){ ++compledtedTimers; }); timers[i]->setTimeout(timerTimeout * i); } while (compledtedTimers < expectedTimers) { suspendResume.suspend(); suspendResume.sendPendingEvents(); } QWASMSUCCESS(); } // Verify that a reused timer fires again void WasmSuspendResumeControlTest::reuseTimer() { QWasmSuspendResumeControl suspendResume; const int expectedTimers = 10; int compledtedTimers = 0; QWasmTimer timer(&suspendResume, [&compledtedTimers](){ ++compledtedTimers; }); while (compledtedTimers < expectedTimers) { timer.setTimeout(timerTimeout); suspendResume.suspend(); suspendResume.sendPendingEvents(); } QWASMSUCCESS(); } // Verify that a cancelled timer does not fire void WasmSuspendResumeControlTest::cancelTimer() { QWasmSuspendResumeControl suspendResume; // controlTimer checks that the cancelled testTimer didn't fire bool controlFired = false; QWasmTimer controlTimer(&suspendResume, [&controlFired](){ controlFired = true; }); QWasmTimer testTimer(&suspendResume, [](){ QWASMFAIL("Cancelled timer did fire"); }); controlTimer.setTimeout(timerTimeout * 4); testTimer.setTimeout(timerTimeout); testTimer.clearTimeout(); while (!controlFired) { suspendResume.suspend(); suspendResume.sendPendingEvents(); } QWASMSUCCESS(); } // Verify that a deleted timer does not fire void WasmSuspendResumeControlTest::deleteTimer() { QWasmSuspendResumeControl suspendResume; // controlTimer checks that the deleted testTimer didn't fire bool controlFired = false; QWasmTimer controlTimer(&suspendResume, [&controlFired](){ controlFired = true; }); controlTimer.setTimeout(timerTimeout * 4); { QWasmTimer testTimer(&suspendResume, [](){ QWASMFAIL("Deleted timer did fire"); }); testTimer.setTimeout(timerTimeout); } while (!controlFired) { suspendResume.suspend(); suspendResume.sendPendingEvents(); } QWASMSUCCESS(); } // Verify that an exclusive suspend resumes for the exclusive event only void WasmSuspendResumeControlTest::suspendExclusive() { QWasmSuspendResumeControl suspendResume; // (re) implement a native timer - this gives us a unique event handler // index which we can suspend exclusively on. bool exclusiveTimerFired = false; auto exclusiveTimerHandler = [&exclusiveTimerFired](emscripten::val) { exclusiveTimerFired = true; }; uint32_t exlusiveTimerHandlerIndex = suspendResume.registerEventHandler(std::move(exclusiveTimerHandler)); std::chrono::milliseconds exclusiveTimerTimeout = timerTimeout * 4; double timoutValue = static_cast(exclusiveTimerTimeout.count()); val jsHandler = suspendResume.jsEventHandlerAt(exlusiveTimerHandlerIndex); val::global("window").call("setTimeout", jsHandler, timoutValue); // Schedule suppressedTimer to fire before the exclusive timer. Expected // behavior is that it doesn't. bool suppressedTimerFired = false; QWasmTimer suppressedTimer(&suspendResume, [&suppressedTimerFired](){ suppressedTimerFired = true; }); suppressedTimer.setTimeout(timerTimeout); // Suspend exclusively for the exclusive timer, and verify that // the correct timers fired. suspendResume.suspendExclusive(exlusiveTimerHandlerIndex); suspendResume.sendPendingEvents(); // <- also clears exclusive mode if (!exclusiveTimerFired) QWASMFAIL("Exclusive timer did not fire"); if (suppressedTimerFired) QWASMFAIL("Suppressed timer did fire"); // Send (all) events, this should give is the suppressed timer suspendResume.sendPendingEvents(); if (!suppressedTimerFired) QWASMFAIL("Suppressed timer did not fire"); QWASMSUCCESS(); } int main(int argc, char **argv) { auto testObject = std::make_shared(); QtWasmTest::initTestCase(argc, argv, testObject); return 0; } #include "main.moc"