1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <fbl/array.h>
6 #include <fbl/function.h>
7 #include <lib/async-testutils/test_loop.h>
8 #include <lib/async/cpp/task.h>
9 #include <lib/async/cpp/time.h>
10 #include <lib/async/cpp/wait.h>
11 #include <lib/async/default.h>
12 #include <lib/zx/event.h>
13 #include <lib/zx/time.h>
14 #include <unittest/unittest.h>
15 #include <zircon/syscalls.h>
16 
17 #include <memory>
18 #include <utility>
19 
20 namespace {
21 
22 // Initializes |wait| to wait on |event| to call |closure| once |trigger| is
23 // signaled.
InitWait(async::Wait * wait,fbl::Closure closure,const zx::event & event,zx_signals_t trigger)24 void InitWait(async::Wait* wait, fbl::Closure closure, const zx::event& event,
25               zx_signals_t trigger) {
26     wait->set_handler(
27         [closure = std::move(closure)] (async_dispatcher_t*, async::Wait*,
28                                         zx_status_t,
29                                         const zx_packet_signal_t*) {
30         closure();
31     });
32     wait->set_object(event.get());
33     wait->set_trigger(trigger);
34 }
35 
36 
DefaultDispatcherIsSetAndUnset()37 bool DefaultDispatcherIsSetAndUnset() {
38     BEGIN_TEST;
39 
40     EXPECT_NULL(async_get_default_dispatcher());
41     {
42         async::TestLoop loop;;
43         EXPECT_EQ(loop.dispatcher(), async_get_default_dispatcher());
44     }
45     EXPECT_NULL(async_get_default_dispatcher());
46 
47     END_TEST;
48 }
49 
FakeClockTimeIsCorrect()50 bool FakeClockTimeIsCorrect() {
51     BEGIN_TEST;
52 
53     async::TestLoop loop;
54 
55     EXPECT_EQ(0, loop.Now().get());
56     EXPECT_EQ(0, async::Now(loop.dispatcher()).get());
57 
58     loop.RunUntilIdle();
59     EXPECT_EQ(0, loop.Now().get());
60     EXPECT_EQ(0, async::Now(loop.dispatcher()).get());
61 
62     loop.RunFor(zx::nsec(1));
63     EXPECT_EQ(1, loop.Now().get());
64     EXPECT_EQ(1, async::Now(loop.dispatcher()).get());
65 
66     loop.RunUntil(zx::time() + zx::nsec(3));
67     EXPECT_EQ(3, loop.Now().get());
68     EXPECT_EQ(3, async::Now(loop.dispatcher()).get());
69 
70     loop.RunFor(zx::nsec(7));
71     EXPECT_EQ(10, loop.Now().get());
72     EXPECT_EQ(10, async::Now(loop.dispatcher()).get());
73 
74     loop.RunUntil(zx::time() + zx::nsec(12));
75     EXPECT_EQ(12, loop.Now().get());
76     EXPECT_EQ(12, async::Now(loop.dispatcher()).get());
77 
78     // t = 12, so nothing should happen in trying to reset the clock to t = 10.
79     loop.RunUntil(zx::time() + zx::nsec(10));
80     EXPECT_EQ(12, loop.Now().get());
81     EXPECT_EQ(12, async::Now(loop.dispatcher()).get());
82 
83     END_TEST;
84 }
85 
TasksAreDispatched()86 bool TasksAreDispatched() {
87     BEGIN_TEST;
88 
89     async::TestLoop loop;
90     bool called = false;
91     async::PostDelayedTask(loop.dispatcher(), [&called] { called = true; }, zx::sec(2));
92 
93     // t = 1: nothing should happen.
94     loop.RunFor(zx::sec(1));
95     EXPECT_FALSE(called);
96 
97     // t = 2: task should be dispatched.
98     loop.RunFor(zx::sec(1));
99     EXPECT_TRUE(called);
100 
101     called = false;
102     async::PostTask(loop.dispatcher(), [&called] { called = true; });
103     loop.RunUntilIdle();
104     EXPECT_TRUE(called);
105 
106     END_TEST;
107 }
108 
SameDeadlinesDispatchInPostingOrder()109 bool SameDeadlinesDispatchInPostingOrder() {
110     BEGIN_TEST;
111 
112     async::TestLoop loop;
113     bool calledA = false;
114     bool calledB = false;
115 
116     async::PostTask(loop.dispatcher(), [&] {
117         EXPECT_FALSE(calledB);
118         calledA = true;
119     });
120     async::PostTask(loop.dispatcher(), [&] {
121       EXPECT_TRUE(calledA);
122       calledB = true;
123     });
124 
125     loop.RunUntilIdle();
126     EXPECT_TRUE(calledA);
127     EXPECT_TRUE(calledB);
128 
129     calledA = false;
130     calledB = false;
131     async::PostDelayedTask(
132         loop.dispatcher(),
133         [&] {
134             EXPECT_FALSE(calledB);
135             calledA = true;
136         },
137         zx::sec(5));
138     async::PostDelayedTask(
139         loop.dispatcher(),
140         [&] {
141             EXPECT_TRUE(calledA);
142             calledB = true;
143         },
144         zx::sec(5));
145 
146     loop.RunFor(zx::sec(5));
147     EXPECT_TRUE(calledA);
148     EXPECT_TRUE(calledB);
149 
150     END_TEST;
151 }
152 
153 // Test tasks that post tasks.
NestedTasksAreDispatched()154 bool NestedTasksAreDispatched() {
155     BEGIN_TEST;
156 
157     async::TestLoop loop;
158     bool called = false;
159 
160     async::PostTask(loop.dispatcher(), [&] {
161         async::PostDelayedTask(
162             loop.dispatcher(),
163             [&] {
164                 async::PostDelayedTask(
165                       loop.dispatcher(),
166                       [&] { called = true; },
167                       zx::min(25));
168             },
169             zx::min(35));
170     });
171 
172     loop.RunFor(zx::hour(1));
173     EXPECT_TRUE(called);
174 
175     END_TEST;
176 }
177 
TimeIsCorrectWhileDispatching()178 bool TimeIsCorrectWhileDispatching() {
179     BEGIN_TEST;
180 
181     async::TestLoop loop;
182     bool called = false;
183 
184     async::PostTask(loop.dispatcher(), [&] {
185         EXPECT_EQ(0, loop.Now().get());
186 
187         async::PostDelayedTask(
188             loop.dispatcher(),
189             [&] {
190                 EXPECT_EQ(10, loop.Now().get());
191                 async::PostDelayedTask(
192                       loop.dispatcher(),
193                       [&] {
194                           EXPECT_EQ(15, loop.Now().get());
195                           async::PostTask(loop.dispatcher(), [&] {
196                               EXPECT_EQ(15, loop.Now().get());
197                               called = true;
198                           });
199                       },
200                       zx::nsec(5));
201             },
202             zx::nsec(10));
203     });
204 
205     loop.RunFor(zx::nsec(15));
206     EXPECT_TRUE(called);
207 
208     END_TEST;
209 }
210 
TasksAreCanceled()211 bool TasksAreCanceled() {
212     BEGIN_TEST;
213 
214     async::TestLoop loop;
215     bool calledA = false;
216     bool calledB = false;
217     bool calledC = false;
218 
219     async::TaskClosure taskA([&calledA] { calledA = true; });
220     async::TaskClosure taskB([&calledB] { calledB = true; });
221     async::TaskClosure taskC([&calledC] { calledC = true; });
222 
223     ASSERT_EQ(ZX_OK, taskA.Post(loop.dispatcher()));
224     ASSERT_EQ(ZX_OK, taskB.Post(loop.dispatcher()));
225     ASSERT_EQ(ZX_OK, taskC.Post(loop.dispatcher()));
226 
227     ASSERT_EQ(ZX_OK, taskA.Cancel());
228     ASSERT_EQ(ZX_OK, taskC.Cancel());
229 
230     loop.RunUntilIdle();
231 
232     EXPECT_FALSE(calledA);
233     EXPECT_TRUE(calledB);
234     EXPECT_FALSE(calledC);
235 
236     END_TEST;
237 }
238 
TimeIsAdvanced()239 bool TimeIsAdvanced() {
240     BEGIN_TEST;
241     async::TestLoop loop;
242 
243     bool called = false;
244     async::TaskClosure task([&called] { called = true; });
245     auto time1 = async::Now(loop.dispatcher());
246 
247     ASSERT_EQ(ZX_OK, task.PostDelayed(loop.dispatcher(), zx::duration(1)));
248 
249     loop.RunUntilIdle();
250 
251     EXPECT_FALSE(called);
252     EXPECT_EQ(time1.get(), async::Now(loop.dispatcher()).get());
253 
254     loop.AdvanceTimeByEpsilon();
255 
256     auto time2 = async::Now(loop.dispatcher());
257 
258     EXPECT_FALSE(called);
259     EXPECT_GT(time2.get(), time1.get());
260 
261     loop.RunUntilIdle();
262 
263     EXPECT_TRUE(called);
264     EXPECT_EQ(time2.get(), async::Now(loop.dispatcher()).get());
265 
266     END_TEST;
267 }
268 
WaitsAreDispatched()269 bool WaitsAreDispatched() {
270     BEGIN_TEST;
271 
272     async::TestLoop loop;
273     async::Wait wait;
274     zx::event event;
275     bool called = false;
276 
277     ASSERT_EQ(ZX_OK, zx::event::create(0u, &event));
278     InitWait(&wait, [&called] { called = true; }, event, ZX_USER_SIGNAL_0);
279     ASSERT_EQ(ZX_OK, wait.Begin(loop.dispatcher()));
280 
281     // |wait| has not yet been triggered.
282     loop.RunUntilIdle();
283     EXPECT_FALSE(called);
284 
285     ASSERT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_1));
286 
287     // |wait| will only be triggered by |ZX_USER_SIGNAL_0|.
288     loop.RunUntilIdle();
289     EXPECT_FALSE(called);
290 
291     ASSERT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_0));
292 
293     loop.RunUntilIdle();
294     EXPECT_TRUE(called);
295 
296     END_TEST;
297 }
298 
299 // Test waits that trigger waits.
NestedWaitsAreDispatched()300 bool NestedWaitsAreDispatched() {
301     BEGIN_TEST;
302 
303     async::TestLoop loop;
304     zx::event event;
305     async::Wait waitA;
306     async::Wait waitB;
307     async::Wait waitC;
308     bool calledA = false;
309     bool calledB = false;
310     bool calledC = false;
311 
312     ASSERT_EQ(ZX_OK, zx::event::create(0u, &event));
313     InitWait(
314         &waitA,
315         [&] {
316             InitWait(
317                 &waitB,
318                 [&] {
319                     InitWait(&waitC, [&] { calledC = true; }, event, ZX_USER_SIGNAL_2);
320                     waitC.Begin(loop.dispatcher());
321                     calledB = true;
322                 },
323                 event,
324                 ZX_USER_SIGNAL_1);
325             waitB.Begin(loop.dispatcher());
326             calledA = true;
327         },
328         event,
329         ZX_USER_SIGNAL_0);
330 
331     ASSERT_EQ(ZX_OK, waitA.Begin(loop.dispatcher()));
332 
333     loop.RunUntilIdle();
334     EXPECT_FALSE(calledA);
335     EXPECT_FALSE(calledB);
336     EXPECT_FALSE(calledC);
337 
338     ASSERT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_0));
339 
340     loop.RunUntilIdle();
341     EXPECT_TRUE(calledA);
342     EXPECT_FALSE(calledB);
343     EXPECT_FALSE(calledC);
344 
345     ASSERT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_1));
346 
347     loop.RunUntilIdle();
348     EXPECT_TRUE(calledA);
349     EXPECT_TRUE(calledB);
350     EXPECT_FALSE(calledC);
351 
352     ASSERT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_2));
353 
354     loop.RunUntilIdle();
355     EXPECT_TRUE(calledA);
356     EXPECT_TRUE(calledB);
357     EXPECT_TRUE(calledC);
358 
359     END_TEST;
360 }
361 
WaitsAreCanceled()362 bool WaitsAreCanceled() {
363     BEGIN_TEST;
364 
365     async::TestLoop loop;
366     zx::event event;
367     async::Wait waitA;
368     async::Wait waitB;
369     async::Wait waitC;
370     bool calledA = false;
371     bool calledB = false;
372     bool calledC = false;
373 
374     ASSERT_EQ(ZX_OK, zx::event::create(0u, &event));
375 
376     InitWait(&waitA, [&calledA] { calledA = true; }, event, ZX_USER_SIGNAL_0);
377     InitWait(&waitB, [&calledB] { calledB = true; }, event, ZX_USER_SIGNAL_0);
378     InitWait(&waitC, [&calledC] { calledC = true; }, event, ZX_USER_SIGNAL_0);
379 
380     ASSERT_EQ(ZX_OK, waitA.Begin(loop.dispatcher()));
381     ASSERT_EQ(ZX_OK, waitB.Begin(loop.dispatcher()));
382     ASSERT_EQ(ZX_OK, waitC.Begin(loop.dispatcher()));
383 
384     ASSERT_EQ(ZX_OK, waitA.Cancel());
385     ASSERT_EQ(ZX_OK, waitC.Cancel());
386     ASSERT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_0));
387 
388     loop.RunUntilIdle();
389     EXPECT_FALSE(calledA);
390     EXPECT_TRUE(calledB);
391     EXPECT_FALSE(calledC);
392 
393     END_TEST;
394 }
395 
396 // Test a task that begins a wait to post a task.
NestedTasksAndWaitsAreDispatched()397 bool NestedTasksAndWaitsAreDispatched() {
398     BEGIN_TEST;
399 
400     async::TestLoop loop;
401     zx::event event;
402     async::Wait wait;
403     bool wait_begun = false;
404     bool wait_dispatched = false;
405     bool inner_task_dispatched = false;
406 
407     ASSERT_EQ(ZX_OK, zx::event::create(0u, &event));
408     InitWait(
409         &wait,
410         [&] {
411             async::PostDelayedTask(loop.dispatcher(),
412                                    [&] { inner_task_dispatched = true; },
413                                    zx::min(2));
414             wait_dispatched = true;
415         },
416         event,
417         ZX_USER_SIGNAL_0);
418     async::PostDelayedTask(loop.dispatcher(),
419                            [&] {
420                                wait.Begin(loop.dispatcher());
421                                wait_begun = true;
422                            },
423                            zx::min(3));
424 
425     loop.RunFor(zx::min(3));
426     EXPECT_TRUE(wait_begun);
427     EXPECT_FALSE(wait_dispatched);
428     EXPECT_FALSE(inner_task_dispatched);
429 
430     ASSERT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_0));
431 
432     loop.RunUntilIdle();
433     EXPECT_TRUE(wait_begun);
434     EXPECT_TRUE(wait_dispatched);
435     EXPECT_FALSE(inner_task_dispatched);
436 
437     loop.RunFor(zx::min(2));
438     EXPECT_TRUE(wait_begun);
439     EXPECT_TRUE(wait_dispatched);
440     EXPECT_TRUE(inner_task_dispatched);
441 
442     END_TEST;
443 }
444 
TasksAreDispatchedOnManyLoops()445 bool TasksAreDispatchedOnManyLoops() {
446     BEGIN_TEST;
447 
448     async::TestLoop loop;
449     auto loopA = loop.StartNewLoop();
450     auto loopB = loop.StartNewLoop();
451     auto loopC = loop.StartNewLoop();
452 
453     bool called = false;
454     bool calledA = false;
455     bool calledB = false;
456     bool calledC = false;
457     async::TaskClosure taskC([&calledC] { calledC = true; });
458 
459     async::PostTask(loopB->dispatcher(), [&calledB] { calledB = true; });
460     async::PostDelayedTask(loop.dispatcher(), [&called] { called = true; }, zx::sec(1));
461     ASSERT_EQ(ZX_OK, taskC.PostDelayed(loopC->dispatcher(), zx::sec(1)));
462     async::PostDelayedTask(loopA->dispatcher(), [&calledA] { calledA = true; }, zx::sec(2));
463 
464     loop.RunUntilIdle();
465     EXPECT_FALSE(called);
466     EXPECT_FALSE(calledA);
467     EXPECT_TRUE(calledB);
468     EXPECT_FALSE(calledC);
469 
470     taskC.Cancel();
471     loop.RunFor(zx::sec(1));
472     EXPECT_TRUE(called);
473     EXPECT_FALSE(calledA);
474     EXPECT_TRUE(calledB);
475     EXPECT_FALSE(calledC);
476 
477     loop.RunFor(zx::sec(1));
478     EXPECT_TRUE(called);
479     EXPECT_TRUE(calledA);
480     EXPECT_TRUE(calledB);
481     EXPECT_FALSE(calledC);
482 
483     END_TEST;
484 }
485 
WaitsAreDispatchedOnManyLoops()486 bool WaitsAreDispatchedOnManyLoops() {
487     BEGIN_TEST;
488 
489     async::TestLoop loop;
490     auto loopA = loop.StartNewLoop();
491     auto loopB = loop.StartNewLoop();
492     auto loopC = loop.StartNewLoop();
493     async::Wait wait;
494     async::Wait waitA;
495     async::Wait waitB;
496     async::Wait waitC;
497     bool called = false;
498     bool calledA = false;
499     bool calledB = false;
500     bool calledC = false;
501     zx::event event;
502 
503     ASSERT_EQ(ZX_OK, zx::event::create(0u, &event));
504 
505     InitWait(&wait, [&called] { called = true; }, event, ZX_USER_SIGNAL_0);
506     InitWait(&waitA, [&calledA] { calledA = true; }, event, ZX_USER_SIGNAL_0);
507     InitWait(&waitB, [&calledB] { calledB = true; }, event, ZX_USER_SIGNAL_0);
508     InitWait(&waitC, [&calledC] { calledC = true; }, event, ZX_USER_SIGNAL_0);
509 
510     ASSERT_EQ(ZX_OK, wait.Begin(loop.dispatcher()));
511     ASSERT_EQ(ZX_OK, waitA.Begin(loopA->dispatcher()));
512     ASSERT_EQ(ZX_OK, waitB.Begin(loopB->dispatcher()));
513     ASSERT_EQ(ZX_OK, waitC.Begin(loopC->dispatcher()));
514 
515     ASSERT_EQ(ZX_OK, waitB.Cancel());
516     ASSERT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_0));
517 
518     loop.RunUntilIdle();
519     EXPECT_TRUE(called);
520     EXPECT_TRUE(calledA);
521     EXPECT_FALSE(calledB);
522     EXPECT_TRUE(calledC);
523 
524     END_TEST;
525 }
526 
527 
528 // Populates |order| with the order in which two tasks and two waits on four
529 // loops were dispatched, given a |loop|.
DetermineDispatchOrder(std::unique_ptr<async::TestLoop> loop,int (* order)[4])530     bool DetermineDispatchOrder(std::unique_ptr<async::TestLoop> loop, int (*order)[4]) {
531     BEGIN_HELPER;
532 
533     auto loopA = loop->StartNewLoop();
534     auto loopB = loop->StartNewLoop();
535     auto loopC = loop->StartNewLoop();
536     async::Wait wait;
537     async::Wait waitB;
538     zx::event event;
539     int i = 0;
540 
541     ASSERT_EQ(ZX_OK, zx::event::create(0u, &event));
542 
543     InitWait(&wait, [&] { (*order)[0] = ++i; }, event, ZX_USER_SIGNAL_0);
544     async::PostTask(loopA->dispatcher(), [&] { (*order)[1] = ++i; });
545     InitWait(&waitB, [&] { (*order)[2] = ++i; }, event, ZX_USER_SIGNAL_0);
546     async::PostTask(loopC->dispatcher(), [&] { (*order)[3] = ++i; });
547 
548     ASSERT_EQ(ZX_OK, wait.Begin(loop->dispatcher()));
549     ASSERT_EQ(ZX_OK, waitB.Begin(loopB->dispatcher()));
550     ASSERT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_0));
551 
552     loop->RunUntilIdle();
553 
554     EXPECT_EQ(4, i);
555     EXPECT_NE(0, (*order)[0]);
556     EXPECT_NE(0, (*order)[1]);
557     EXPECT_NE(0, (*order)[2]);
558     EXPECT_NE(0, (*order)[3]);
559 
560     END_HELPER;
561 }
562 
SeedTestLoopWithEnv(uint32_t random_seed,std::unique_ptr<async::TestLoop> * loop)563 bool SeedTestLoopWithEnv(uint32_t random_seed, std::unique_ptr<async::TestLoop>* loop) {
564     BEGIN_HELPER;
565 
566     char buf[12];
567     snprintf(buf, sizeof(buf), "%u", random_seed);
568     EXPECT_EQ(0, setenv("TEST_LOOP_RANDOM_SEED", buf, 1));
569     *loop = std::make_unique<async::TestLoop>();
570     EXPECT_EQ(0, unsetenv("TEST_LOOP_RANDOM_SEED"));
571 
572     END_HELPER;
573 }
574 
DispatchOrderIsDeterministicFor(uint32_t random_seed)575 bool DispatchOrderIsDeterministicFor(uint32_t random_seed) {
576     BEGIN_HELPER;
577 
578     int expected_order[4] = {0, 0, 0, 0};
579     std::unique_ptr<async::TestLoop> loop;
580 
581     EXPECT_TRUE(SeedTestLoopWithEnv(random_seed, &loop));
582     EXPECT_TRUE(DetermineDispatchOrder(std::move(loop), &expected_order));
583 
584     for (int i = 0; i < 5; ++i) {
585         for (int j = 0; j < 2; j++) {
586             int actual_order[4] = {0, 0, 0, 0};
587             if (j == 0) {
588                 EXPECT_TRUE(SeedTestLoopWithEnv(random_seed, &loop));
589             } else {
590                 loop = std::make_unique<async::TestLoop>(random_seed);
591             }
592             EXPECT_TRUE(DetermineDispatchOrder(std::move(loop), &actual_order));
593             EXPECT_EQ(expected_order[0], actual_order[0]);
594             EXPECT_EQ(expected_order[1], actual_order[1]);
595             EXPECT_EQ(expected_order[2], actual_order[2]);
596             EXPECT_EQ(expected_order[3], actual_order[3]);
597         }
598     }
599 
600     END_HELPER;
601 }
602 
603 
DispatchOrderIsDeterministic()604 bool DispatchOrderIsDeterministic() {
605     BEGIN_TEST;
606 
607     EXPECT_TRUE(DispatchOrderIsDeterministicFor(1));
608     EXPECT_TRUE(DispatchOrderIsDeterministicFor(43));
609     EXPECT_TRUE(DispatchOrderIsDeterministicFor(893));
610     EXPECT_TRUE(DispatchOrderIsDeterministicFor(39408));
611     EXPECT_TRUE(DispatchOrderIsDeterministicFor(844018));
612     EXPECT_TRUE(DispatchOrderIsDeterministicFor(83018299));
613     EXPECT_TRUE(DispatchOrderIsDeterministicFor(3213));
614     EXPECT_TRUE(DispatchOrderIsDeterministicFor(139133113));
615     EXPECT_TRUE(DispatchOrderIsDeterministicFor(1323234373));
616 
617     END_TEST;
618 }
619 
620 } // namespace
621 
622 BEGIN_TEST_CASE(SingleLoopTests)
623 RUN_TEST(DefaultDispatcherIsSetAndUnset)
624 RUN_TEST(FakeClockTimeIsCorrect)
625 RUN_TEST(TasksAreDispatched)
626 RUN_TEST(SameDeadlinesDispatchInPostingOrder)
627 RUN_TEST(NestedTasksAreDispatched)
628 RUN_TEST(TimeIsCorrectWhileDispatching)
629 RUN_TEST(TasksAreCanceled)
630 RUN_TEST(TimeIsAdvanced)
631 RUN_TEST(WaitsAreDispatched)
632 RUN_TEST(NestedWaitsAreDispatched)
633 RUN_TEST(WaitsAreCanceled)
634 RUN_TEST(NestedTasksAndWaitsAreDispatched)
635 END_TEST_CASE(SingleLoopTests)
636 
637 BEGIN_TEST_CASE(MultiLoopTests)
638 RUN_TEST(TasksAreDispatchedOnManyLoops)
639 RUN_TEST(WaitsAreDispatchedOnManyLoops)
640 RUN_TEST(DispatchOrderIsDeterministic)
641 END_TEST_CASE(MultiLoopTests)
642