30 void CoroSignalTest::testNoArgs ()
32 SignalEmitter emitter;
33 auto task = [] (SignalEmitter& emitter) -> Task<bool>
38 emit emitter.NoArgs ();
42 void CoroSignalTest::testOneArg ()
44 SignalEmitter emitter;
45 auto task = [] (SignalEmitter& emitter) -> Task<int>
48 static_assert (std::is_same_v<
decltype (value), std::optional<int>>);
49 co_return value.value ();
52 emit emitter.OneArg (42);
56 void CoroSignalTest::testTwoArgsYieldTuple ()
58 SignalEmitter emitter;
59 auto task = [] (SignalEmitter& emitter) -> Task<std::tuple<int, QString>>
62 static_assert (std::is_same_v<
decltype (value), std::optional<std::tuple<int, QString>>>);
63 co_return value.value ();
66 emit emitter.TwoArgs (7,
"seven"_qs);
69 QCOMPARE (name,
"seven"_qs);
72 void CoroSignalTest::testRefArgYieldsOwnedValue ()
74 SignalEmitter emitter;
75 auto task = [] (SignalEmitter& emitter) -> Task<QString>
78 static_assert (std::is_same_v<
decltype (value), std::optional<QString>>);
79 co_return value.value ();
82 emit emitter.RefArg (QString::number (42));
86 void CoroSignalTest::testOverloaded ()
88 SignalEmitter emitter;
89 auto task = [] (SignalEmitter& emitter) -> Task<QString>
94 emit emitter.Overloaded (1);
96 emit emitter.Overloaded (
"string"_qs);
100 void CoroSignalTest::testPrivateTagStripped ()
103 timer.setSingleShot (
true);
104 auto timedOut = [] (QTimer& timer) -> Task<bool>
106 co_return co_await Signal { timer, &QTimer::timeout };
112 auto renamed = [] (QObject& object) -> Task<QString>
114 auto name =
co_await Signal { object, &QObject::objectNameChanged };
115 static_assert (std::is_same_v<
decltype (name), std::optional<QString>>);
116 co_return name.value ();
118 object.setObjectName (
"renamed"_qs);
122 void CoroSignalTest::testBaseClassThroughDerived ()
124 auto emitter = std::make_unique<SignalEmitter> ();
125 auto task = [] (SignalEmitter& emitter) -> Task<std::optional<QObject*>>
127 co_return co_await Signal { emitter, &QObject::destroyed };
130 QObject *expected = emitter.get ();
133 QVERIFY (result.has_value ());
134 QCOMPARE (*result, expected);
137 void CoroSignalTest::testResumesAfterEmitReturns ()
139 SignalEmitter emitter;
141 int stageAtResume = -1;
142 auto task = [] (SignalEmitter& emitter,
const int& stage,
int& stageAtResume) -> Task<void>
145 stageAtResume = stage;
146 } (emitter, stage, stageAtResume);
148 emit emitter.NoArgs ();
151 QCOMPARE (stageAtResume, 1);
154 void CoroSignalTest::testContinuationMayDestroySender ()
156 auto emitter = std::make_unique<SignalEmitter> ();
157 auto task = [] (std::unique_ptr<SignalEmitter>& emitter) -> Task<void>
163 emit emitter->NoArgs ();
169 void CoroSignalTest::testFirstEmissionWins ()
171 SignalEmitter emitter;
173 auto task = [] (SignalEmitter& emitter,
int& resumes) -> Task<int>
178 } (emitter, resumes);
180 emit emitter.OneArg (1);
181 emit emitter.OneArg (2);
184 emit emitter.OneArg (3);
186 QCOMPARE (resumes, 1);
189 void CoroSignalTest::testConnectedOnlyWhileSuspended ()
191 SignalEmitter emitter;
193 QVERIFY (!emitter.IsConnected (&QObject::destroyed));
195 auto task = [] (SignalEmitter& emitter) -> Task<int>
200 QVERIFY (emitter.IsConnected (&QObject::destroyed));
202 emit emitter.OneArg (5);
205 QVERIFY (!emitter.IsConnected (&QObject::destroyed));
208 void CoroSignalTest::testManyAwaitersOneEmission ()
210 SignalEmitter emitter;
211 const auto awaitOneArg = [] (SignalEmitter& emitter) -> Task<int>
215 auto first = awaitOneArg (emitter);
216 auto second = awaitOneArg (emitter);
218 emit emitter.OneArg (9);
223 void CoroSignalTest::testReawaitSame ()
225 SignalEmitter emitter;
228 ticker.callOnTimeout ([&] { emit emitter.OneArg (++counter); });
231 auto task = [] (SignalEmitter& emitter) -> Task<QList<int>>
235 while (values.size () < 3)
236 values << (
co_await signal).value ();
242 QCOMPARE (values.size (), 3);
243 QVERIFY (std::ranges::adjacent_find (values, std::greater_equal {}) == values.end ());
246 void CoroSignalTest::testHandlerResultIsAwaited ()
248 SignalEmitter emitter;
249 auto task = [] (SignalEmitter& emitter) -> Task<double>
252 static_assert (std::is_same_v<
decltype (value), std::optional<double>>);
253 co_return value.value ();
256 emit emitter.OneArg (21);
260 void CoroSignalTest::testHandlerRunsInsideEmit ()
262 SignalEmitter emitter;
264 int stageInHandler = -1;
265 int stageAtResume = -1;
266 auto task = [] (SignalEmitter& emitter,
const int& stage,
int& stageInHandler,
int& stageAtResume) -> Task<void>
269 stageAtResume = stage;
270 } (emitter, stage, stageInHandler, stageAtResume);
272 emit emitter.NoArgs ();
275 QCOMPARE (stageInHandler, 0);
276 QCOMPARE (stageAtResume, 1);
279 void CoroSignalTest::testHandlerSnapshotsDyingSender ()
281 auto emitter = std::make_unique<SignalEmitter> ();
282 emitter->setObjectName (
"snapshot"_qs);
283 auto task = [] (SignalEmitter& emitter) -> Task<std::optional<QString>>
288 emit emitter->NoArgs ();
291 QVERIFY (result.has_value ());
292 QCOMPARE (*result,
"snapshot"_qs);
295 void CoroSignalTest::testHandlerTupleArgs ()
297 SignalEmitter emitter;
298 auto task = [] (SignalEmitter& emitter) -> Task<QString>
300 const auto handler = [] (
int number,
const QString& name) {
return name + u
':' + QString::number (number); };
304 emit emitter.TwoArgs (7,
"seven"_qs);
308 void CoroSignalTest::testHandlerPrivateTagStripped ()
311 timer.setSingleShot (
true);
312 auto timedOut = [] (QTimer& timer) -> Task<std::optional<int>>
314 co_return co_await Signal { timer, &QTimer::timeout, [] {
return 42; } };
320 auto renamed = [] (QObject& object) -> Task<QString>
322 co_return (
co_await Signal { object, &QObject::objectNameChanged, [] (
const QString& name) {
return name.toUpper (); } }).value ();
324 object.setObjectName (
"renamed"_qs);
328 void CoroSignalTest::testHandlerRunsOnce ()
330 SignalEmitter emitter;
332 auto task = [] (SignalEmitter& emitter,
int& calls) -> Task<int>
337 emit emitter.OneArg (1);
338 emit emitter.OneArg (2);
343 void CoroSignalTest::testHandlerSenderDeathYieldsNullopt ()
345 auto emitter = std::make_unique<SignalEmitter> ();
346 bool handlerRan =
false;
347 auto task = [] (SignalEmitter& emitter,
bool& handlerRan) -> Task<std::optional<int>>
350 } (*emitter, handlerRan);
354 QVERIFY (!handlerRan);
357 void CoroSignalTest::testHandlerExceptionRethrownAtAwait ()
359 auto emitter = std::make_unique<SignalEmitter> ();
362 auto task = [] (SignalEmitter& emitter,
int& resumes, QString& caught) -> Task<void>
368 catch (
const std::runtime_error& e)
370 caught = QString::fromUtf8 (e.what ());
373 } (*emitter, resumes, caught);
375 emit emitter->OneArg (1);
378 QCOMPARE (caught,
"boom"_qs);
380 QCOMPARE (resumes, 1);
383 void CoroSignalTest::testHandlerCopiedPerAwait ()
385 SignalEmitter emitter;
387 ticker.callOnTimeout ([&] { emit emitter.OneArg (10); });
390 auto task = [] (SignalEmitter& emitter) -> Task<QList<int>>
394 while (values.size () < 3)
395 values << (
co_await signal).value ();
401 QCOMPARE (values, (QList<int> { 10, 10, 10 }));
404 void CoroSignalTest::testSenderDeathYieldsNullopt ()
406 auto emitter = std::make_unique<SignalEmitter> ();
407 bool resumed =
false;
408 auto noArgs = [] (SignalEmitter& emitter) -> Task<bool>
412 auto oneArg = [] (SignalEmitter& emitter,
bool& resumed) -> Task<std::optional<int>>
417 } (*emitter, resumed);
418 auto twoArgs = [] (SignalEmitter& emitter) -> Task<std::optional<std::tuple<int, QString>>>
431 void CoroSignalTest::testEmissionBeforeDeathKeepsValue ()
433 auto emitter = std::make_unique<SignalEmitter> ();
435 auto task = [] (SignalEmitter& emitter,
int& resumes) -> Task<std::optional<int>>
440 } (*emitter, resumes);
442 emit emitter->OneArg (5);
446 QVERIFY (result.has_value ());
447 QCOMPARE (*result, 5);
449 QCOMPARE (resumes, 1);
452 void CoroSignalTest::testContextDeathDisconnects ()
454 SignalEmitter emitter;
455 auto context = std::make_unique<CoroContext> ();
456 auto task = [] (CoroContext *context, SignalEmitter& emitter) ->
ContextTask<int>
458 co_await AddContext { *context };
460 } (&*context, emitter);
465 QVERIFY (!emitter.IsConnected (&QObject::destroyed));
467 emit emitter.OneArg (42);
468 QVERIFY_THROWS_EXCEPTION (ContextDeadException,
GetTaskResult (task));
471 void CoroSignalTest::testEmittedFromAnotherThread ()
476 SignalEmitter emitter;
477 emitter.moveToThread (&worker);
479 QThread *resumedOn =
nullptr;
480 auto task = [] (SignalEmitter& emitter, QThread*& resumedOn) -> Task<int>
483 resumedOn = QThread::currentThread ();
485 } (emitter, resumedOn);
487 const auto mainThread = QThread::currentThread ();
488 QThread *emittedOn =
nullptr;
489 QMetaObject::invokeMethod (&emitter,
492 emittedOn = QThread::currentThread ();
493 emit emitter.OneArg (11);
494 emitter.moveToThread (mainThread);
496 Qt::QueuedConnection);
499 QCOMPARE (resumedOn, mainThread);
502 QVERIFY (worker.wait ());
503 QCOMPARE (emittedOn, &worker);
void RefArg(const QString &)
void TwoArgs(int, QString)
Task< R, ContextExtension > ContextTask
Signal(Derived &, void(Base::*)(SignalArgs...)) -> Signal< Base, detail::TuplePacker, void(Base::*)(SignalArgs...), Typelist< SignalArgs... > >
T GetTaskResult(Task< T, Extensions... > task)