LeechCraft 0.6.70-16373-g319c272718
Modular cross-platform feature rich live environment.
Loading...
Searching...
No Matches
corotasktest.cpp
Go to the documentation of this file.
1/**********************************************************************
2 * LeechCraft - modular cross-platform feature rich internet client.
3 * Copyright (C) 2006-2014 Georg Rudoy
4 *
5 * Distributed under the Boost Software License, Version 1.0.
6 * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
7 **********************************************************************/
8
9#include "corotasktest.h"
10#include <QtTest>
11#include <coro.h>
12#include <coro/context.h>
13#include <coro/either.h>
14#include <coro/getresult.h>
15#include <coro/inparallel.h>
16#include <coro/networkresult.h>
17#include <coro/throttle.h>
18#include <util/sll/qtutil.h>
19
20QTEST_GUILESS_MAIN (LC::Util::CoroTaskTest)
21
22using namespace std::chrono_literals;
23
24namespace LC::Util
25{
26 void CoroTaskTest::testReturn ()
27 {
28 auto task = [] () -> Task<int> { co_return 42; } ();
29 auto result = GetTaskResult (task);
30 QCOMPARE (result, 42);
31 }
32
33 void CoroTaskTest::testWait ()
34 {
36 timer.start ();
37
38 auto task = [] () -> Task<int>
39 {
40 co_await 50ms;
41 co_await Precisely { 10ms };
42 co_return 42;
43 } ();
44
45 auto result = GetTaskResult (task);
46 QCOMPARE (result, 42);
47 QVERIFY (timer.elapsed () > 50);
48 }
49
50 void CoroTaskTest::testTaskDestr ()
51 {
52 bool continued = false;
53
54 [] (auto& continued) -> Task<void>
55 {
56 co_await 10ms;
57 continued = true;
58 } (continued);
59
61 }
62
63 namespace
64 {
65 // almost the Public Morozov pattern
66 class MockReply : public QNetworkReply
67 {
68 QBuffer Buffer_;
69 public:
70 using QNetworkReply::QNetworkReply;
71
72 using QNetworkReply::setAttribute;
73 using QNetworkReply::setError;
74 using QNetworkReply::setFinished;
75 using QNetworkReply::setHeader;
76 using QNetworkReply::setOperation;
77 using QNetworkReply::setRawHeader;
78 using QNetworkReply::setRequest;
79 using QNetworkReply::setUrl;
80
81 void SetData (const QByteArray& data)
82 {
83 Buffer_.setData (data);
84 Buffer_.open (QIODevice::ReadOnly);
85 open (QIODevice::ReadOnly);
86 }
87 protected:
88 qint64 readData (char *data, qint64 maxSize) override
89 {
90 return Buffer_.read (data, maxSize);
91 }
92
93 void abort () override
94 {
95 }
96 };
97
98 class MockNAM : public QNetworkAccessManager
99 {
100 std::unique_ptr<MockReply> Reply_;
101 public:
102 explicit MockNAM (std::unique_ptr<MockReply> reply)
103 : Reply_ { std::move (reply) }
104 {
105 }
106
107 MockReply& GetReply ()
108 {
109 return *Reply_;
110 }
111 protected:
112 QNetworkReply* createRequest (Operation op, const QNetworkRequest& req, QIODevice*) override
113 {
114 Reply_->setUrl (req.url ());
115 Reply_->setOperation (op);
116 Reply_->setRequest (req);
117 return Reply_.get ();
118 }
119 };
120
121 auto MkSuccessfulReply (const QByteArray& data)
122 {
123 auto reply = std::make_unique<MockReply> ();
124 reply->setAttribute (QNetworkRequest::HttpStatusCodeAttribute, 200);
125 reply->SetData (data);
126 return reply;
127 }
128
129 auto MkErrorReply ()
130 {
131 auto reply = std::make_unique<MockReply> ();
132 reply->setAttribute (QNetworkRequest::HttpStatusCodeAttribute, 404);
133 reply->setError (QNetworkReply::NetworkError::ContentAccessDenied, "well, 404!"_qs);
134 return reply;
135 }
136
137 void TestGoodReply (auto finishMarker)
138 {
139 const QByteArray data { "this is some test data" };
140 MockNAM nam { MkSuccessfulReply (data) };
141 finishMarker (nam.GetReply ());
142
143 auto task = [&nam] () -> Task<QByteArray>
144 {
145 auto reply = co_await *nam.get (QNetworkRequest { QUrl { "http://example.com/foo.txt"_qs } });
146 co_return reply.GetReplyData ();
147 } ();
148
149 auto result = GetTaskResult (task);
150 QCOMPARE (result, data);
151 }
152
153 void TestBadReply (auto finishMarker)
154 {
155 MockNAM nam { MkErrorReply () };
156 finishMarker (nam.GetReply ());
157
158 auto task = [&nam] () -> Task<QByteArray>
159 {
160 auto reply = co_await *nam.get (QNetworkRequest { QUrl { "http://example.com/foo.txt"_qs } });
161 co_return reply.GetReplyData ();
162 } ();
163
165 }
166
167 void ImmediateFinishMarker (MockReply& reply)
168 {
169 reply.setFinished (true);
170 }
171
172 void DelayedFinishMarker (MockReply& reply)
173 {
174 QTimer::singleShot (10ms,
175 [&]
176 {
177 reply.setFinished (true);
178 emit reply.finished ();
179 });
180 }
181 }
182
183 void CoroTaskTest::testNetworkReplyGoodNoWait ()
184 {
186 }
187
188 void CoroTaskTest::testNetworkReplyGoodWait ()
189 {
191 }
192
193 void CoroTaskTest::testNetworkReplyBadNoWait ()
194 {
196 }
197
198 void CoroTaskTest::testNetworkReplyBadWait ()
199 {
201 }
202
203 void CoroTaskTest::testContextDestrBeforeFinish ()
204 {
205 auto context = std::make_unique<QObject> ();
206 auto task = [] (QObject *context) -> Task<int, ContextExtensions>
207 {
208 co_await AddContextObject { *context };
209 co_await 10ms;
210 co_return context->children ().size ();
211 } (&*context);
212 context.reset ();
213
215 }
216
217 void CoroTaskTest::testContextDestrAfterFinish ()
218 {
219 auto context = std::make_unique<QObject> ();
220 auto task = [] (QObject *context) -> Task<int, ContextExtensions>
221 {
222 co_await AddContextObject { *context };
223 co_await 10ms;
224 co_return context->children ().size ();
225 } (&*context);
226
228 }
229
230 void CoroTaskTest::testWaitMany ()
231 {
232 constexpr auto max = 100;
233 auto mkTask = [] (int index) -> Task<int>
234 {
235 co_await Precisely { std::chrono::milliseconds { max - index } };
236 co_return index;
237 };
238
240 timer.start ();
241 QVector<Task<int>> tasks;
242 QVector<int> expected;
243 for (int i = 0; i < max; ++i)
244 {
245 tasks << mkTask (i);
246 expected << i;
247 }
248 const auto creationElapsed = timer.elapsed ();
249
250 timer.restart ();
251 auto result = GetTaskResult (InParallel (std::move (tasks)));
252 const auto executionElapsed = timer.elapsed ();
253
256 constexpr auto tolerance = 1.05;
258 }
259
260 void CoroTaskTest::testWaitManyTuple ()
261 {
262 auto mkTask = [] (int delay) -> Task<int>
263 {
264 co_await Precisely { std::chrono::milliseconds { delay } };
265 co_return delay;
266 };
267
269 timer.start ();
270 auto result = GetTaskResult (InParallel (mkTask (10), mkTask (9), mkTask (2), mkTask (1)));
271 const auto executionElapsed = timer.elapsed ();
272
273 QCOMPARE (result, (std::tuple { 10, 9, 2, 1 }));
274 constexpr auto tolerance = 1.05;
276 }
277
278 void CoroTaskTest::testEither ()
279 {
280 using Result_t = Either<QString, bool>;
281
282 auto immediatelyFailing = [] () -> Task<Result_t>
283 {
284 const auto theInt = co_await Either<QString, int>::Left ("meh");
285 co_return Result_t::Right (theInt > 420);
286 } ();
287 QCOMPARE (GetTaskResult (immediatelyFailing), Result_t::Left ("meh"));
288
289 auto earlyFailing = [] () -> Task<Result_t>
290 {
291 const auto theInt = co_await Either<QString, int>::Left ("meh");
292 co_await 10ms;
293 co_return Result_t::Right (theInt > 420);
294 } ();
295 QCOMPARE (GetTaskResult (earlyFailing), Result_t::Left ("meh"));
296
297 auto successful = [] () -> Task<Result_t>
298 {
299 const auto theInt = co_await Either<QString, int>::Right (42);
300 co_await 10ms;
301 co_return Result_t::Right (theInt > 420);
302 } ();
303 QCOMPARE (GetTaskResult (successful), Result_t::Right (false));
304 }
305
306 void CoroTaskTest::testThrottleSameCoro ()
307 {
308 Throttle t { 10ms };
309 constexpr auto count = 10;
310
312 timer.start ();
313 auto task = [] (auto& t) -> Task<int>
314 {
315 int result = 0;
316 for (int i = 0; i < count; ++i)
317 {
318 co_await t;
319 result += i;
320 }
321 co_return result;
322 } (t);
323 const auto result = GetTaskResult (task);
324 const auto time = timer.elapsed ();
325
326 QCOMPARE (result, count * (count - 1) / 2);
327 QVERIFY (time >= count * t.GetInterval ().count ());
328 }
329
330 void CoroTaskTest::testThrottleSameCoroSlow ()
331 {
332 Throttle t { 10ms };
333 constexpr auto count = 10;
334
336 timer.start ();
337 auto task = [] (auto& t) -> Task<void>
338 {
339 for (int i = 0; i < count; ++i)
340 {
341 co_await t;
342 if (i != count - 1)
343 co_await Precisely { 9ms };
344 }
345 } (t);
347 const auto time = timer.elapsed ();
348
349 const auto expectedMinTime = count * t.GetInterval ().count ();
352 }
353
354 void CoroTaskTest::testThrottleManyCoros ()
355 {
356 Throttle t { 1ms, Qt::TimerType::PreciseTimer };
357 constexpr auto count = 10;
358
360 timer.start ();
361 auto mkTask = [] (auto& t) -> Task<void>
362 {
363 for (int i = 0; i < count; ++i)
364 co_await t;
365 };
366 QVector tasks { mkTask (t), mkTask (t), mkTask (t) };
367 for (auto& task : tasks)
369 const auto time = timer.elapsed ();
370
371 QVERIFY (time >= count * tasks.size () * t.GetInterval ().count ());
372 }
373}
static Either Left(const L &l)
Definition either.h:119
static Either Right(R &&r)
Definition either.h:124
constexpr detail::AggregateType< detail::AggregateFunction::Count, Ptr > count
Definition oral.h:965
constexpr detail::AggregateType< detail::AggregateFunction::Max, Ptr > max
Definition oral.h:971
Task< QVector< T >, Exts... > InParallel(QVector< Task< T, Exts... > > tasks)
Definition inparallel.h:17
Container< T > Filter(const Container< T > &c, F f)
Definition prelude.h:118
WithPrecision< Qt::PreciseTimer > Precisely
Definition timer.h:42
T GetTaskResult(Task< T, Extensions... > task)
Definition getresult.h:18
STL namespace.