1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "VideoUtils.h"
7 #include "base/message_loop.h"
8 #include "gtest/gtest.h"
9 #include "mozilla/MozPromise.h"
10 #include "mozilla/SharedThreadPool.h"
11 #include "mozilla/TaskQueue.h"
12 #include "mozilla/Unused.h"
13 #include "nsISupportsImpl.h"
14 
15 using namespace mozilla;
16 
17 typedef MozPromise<int, double, false> TestPromise;
18 typedef MozPromise<int, double, true /* exclusive */> TestPromiseExcl;
19 typedef TestPromise::ResolveOrRejectValue RRValue;
20 
21 class MOZ_STACK_CLASS AutoTaskQueue {
22  public:
AutoTaskQueue()23   AutoTaskQueue()
24       : mTaskQueue(
25             new TaskQueue(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
26                           "TestMozPromise AutoTaskQueue")) {}
27 
~AutoTaskQueue()28   ~AutoTaskQueue() { mTaskQueue->AwaitShutdownAndIdle(); }
29 
Queue()30   TaskQueue* Queue() { return mTaskQueue; }
31 
32  private:
33   RefPtr<TaskQueue> mTaskQueue;
34 };
35 
36 class DelayedResolveOrReject : public Runnable {
37  public:
DelayedResolveOrReject(TaskQueue * aTaskQueue,TestPromise::Private * aPromise,const TestPromise::ResolveOrRejectValue & aValue,int aIterations)38   DelayedResolveOrReject(TaskQueue* aTaskQueue, TestPromise::Private* aPromise,
39                          const TestPromise::ResolveOrRejectValue& aValue,
40                          int aIterations)
41       : mozilla::Runnable("DelayedResolveOrReject"),
42         mTaskQueue(aTaskQueue),
43         mPromise(aPromise),
44         mValue(aValue),
45         mIterations(aIterations) {}
46 
Run()47   NS_IMETHOD Run() override {
48     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
49     if (!mPromise) {
50       // Canceled.
51       return NS_OK;
52     }
53 
54     if (--mIterations == 0) {
55       mPromise->ResolveOrReject(mValue, __func__);
56       return NS_OK;
57     }
58 
59     nsCOMPtr<nsIRunnable> r = this;
60     return mTaskQueue->Dispatch(r.forget());
61   }
62 
Cancel()63   void Cancel() { mPromise = nullptr; }
64 
65  protected:
66   ~DelayedResolveOrReject() = default;
67 
68  private:
69   RefPtr<TaskQueue> mTaskQueue;
70   RefPtr<TestPromise::Private> mPromise;
71   TestPromise::ResolveOrRejectValue mValue;
72   int mIterations;
73 };
74 
75 template <typename FunctionType>
RunOnTaskQueue(TaskQueue * aQueue,FunctionType aFun)76 void RunOnTaskQueue(TaskQueue* aQueue, FunctionType aFun) {
77   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction("RunOnTaskQueue", aFun);
78   Unused << aQueue->Dispatch(r.forget());
79 }
80 
81 // std::function can't come soon enough. :-(
82 #define DO_FAIL                                       \
83   []() {                                              \
84     EXPECT_TRUE(false);                               \
85     return TestPromise::CreateAndReject(0, __func__); \
86   }
87 
TEST(MozPromise,BasicResolve)88 TEST(MozPromise, BasicResolve)
89 {
90   AutoTaskQueue atq;
91   RefPtr<TaskQueue> queue = atq.Queue();
92   RunOnTaskQueue(queue, [queue]() -> void {
93     TestPromise::CreateAndResolve(42, __func__)
94         ->Then(
95             queue, __func__,
96             [queue](int aResolveValue) -> void {
97               EXPECT_EQ(aResolveValue, 42);
98               queue->BeginShutdown();
99             },
100             DO_FAIL);
101   });
102 }
103 
TEST(MozPromise,BasicReject)104 TEST(MozPromise, BasicReject)
105 {
106   AutoTaskQueue atq;
107   RefPtr<TaskQueue> queue = atq.Queue();
108   RunOnTaskQueue(queue, [queue]() -> void {
109     TestPromise::CreateAndReject(42.0, __func__)
110         ->Then(queue, __func__, DO_FAIL, [queue](int aRejectValue) -> void {
111           EXPECT_EQ(aRejectValue, 42.0);
112           queue->BeginShutdown();
113         });
114   });
115 }
116 
TEST(MozPromise,BasicResolveOrRejectResolved)117 TEST(MozPromise, BasicResolveOrRejectResolved)
118 {
119   AutoTaskQueue atq;
120   RefPtr<TaskQueue> queue = atq.Queue();
121   RunOnTaskQueue(queue, [queue]() -> void {
122     TestPromise::CreateAndResolve(42, __func__)
123         ->Then(
124             queue, __func__,
125             [queue](const TestPromise::ResolveOrRejectValue& aValue) -> void {
126               EXPECT_TRUE(aValue.IsResolve());
127               EXPECT_FALSE(aValue.IsReject());
128               EXPECT_FALSE(aValue.IsNothing());
129               EXPECT_EQ(aValue.ResolveValue(), 42);
130               queue->BeginShutdown();
131             });
132   });
133 }
134 
TEST(MozPromise,BasicResolveOrRejectRejected)135 TEST(MozPromise, BasicResolveOrRejectRejected)
136 {
137   AutoTaskQueue atq;
138   RefPtr<TaskQueue> queue = atq.Queue();
139   RunOnTaskQueue(queue, [queue]() -> void {
140     TestPromise::CreateAndReject(42.0, __func__)
141         ->Then(
142             queue, __func__,
143             [queue](const TestPromise::ResolveOrRejectValue& aValue) -> void {
144               EXPECT_TRUE(aValue.IsReject());
145               EXPECT_FALSE(aValue.IsResolve());
146               EXPECT_FALSE(aValue.IsNothing());
147               EXPECT_EQ(aValue.RejectValue(), 42.0);
148               queue->BeginShutdown();
149             });
150   });
151 }
152 
TEST(MozPromise,AsyncResolve)153 TEST(MozPromise, AsyncResolve)
154 {
155   AutoTaskQueue atq;
156   RefPtr<TaskQueue> queue = atq.Queue();
157   RunOnTaskQueue(queue, [queue]() -> void {
158     RefPtr<TestPromise::Private> p = new TestPromise::Private(__func__);
159 
160     // Kick off three racing tasks, and make sure we get the one that finishes
161     // earliest.
162     RefPtr<DelayedResolveOrReject> a =
163         new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(32), 10);
164     RefPtr<DelayedResolveOrReject> b =
165         new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(42), 5);
166     RefPtr<DelayedResolveOrReject> c =
167         new DelayedResolveOrReject(queue, p, RRValue::MakeReject(32.0), 7);
168 
169     nsCOMPtr<nsIRunnable> ref = a.get();
170     Unused << queue->Dispatch(ref.forget());
171     ref = b.get();
172     Unused << queue->Dispatch(ref.forget());
173     ref = c.get();
174     Unused << queue->Dispatch(ref.forget());
175 
176     p->Then(
177         queue, __func__,
178         [queue, a, b, c](int aResolveValue) -> void {
179           EXPECT_EQ(aResolveValue, 42);
180           a->Cancel();
181           b->Cancel();
182           c->Cancel();
183           queue->BeginShutdown();
184         },
185         DO_FAIL);
186   });
187 }
188 
TEST(MozPromise,CompletionPromises)189 TEST(MozPromise, CompletionPromises)
190 {
191   bool invokedPass = false;
192   AutoTaskQueue atq;
193   RefPtr<TaskQueue> queue = atq.Queue();
194   RunOnTaskQueue(queue, [queue, &invokedPass]() -> void {
195     TestPromise::CreateAndResolve(40, __func__)
196         ->Then(
197             queue, __func__,
198             [](int aVal) -> RefPtr<TestPromise> {
199               return TestPromise::CreateAndResolve(aVal + 10, __func__);
200             },
201             DO_FAIL)
202         ->Then(
203             queue, __func__,
204             [&invokedPass](int aVal) {
205               invokedPass = true;
206               return TestPromise::CreateAndResolve(aVal, __func__);
207             },
208             DO_FAIL)
209         ->Then(
210             queue, __func__,
211             [queue](int aVal) -> RefPtr<TestPromise> {
212               RefPtr<TestPromise::Private> p =
213                   new TestPromise::Private(__func__);
214               nsCOMPtr<nsIRunnable> resolver = new DelayedResolveOrReject(
215                   queue, p, RRValue::MakeResolve(aVal - 8), 10);
216               Unused << queue->Dispatch(resolver.forget());
217               return RefPtr<TestPromise>(p);
218             },
219             DO_FAIL)
220         ->Then(
221             queue, __func__,
222             [](int aVal) -> RefPtr<TestPromise> {
223               return TestPromise::CreateAndReject(double(aVal - 42) + 42.0,
224                                                   __func__);
225             },
226             DO_FAIL)
227         ->Then(queue, __func__, DO_FAIL,
228                [queue, &invokedPass](double aVal) -> void {
229                  EXPECT_EQ(aVal, 42.0);
230                  EXPECT_TRUE(invokedPass);
231                  queue->BeginShutdown();
232                });
233   });
234 }
235 
TEST(MozPromise,PromiseAllResolve)236 TEST(MozPromise, PromiseAllResolve)
237 {
238   AutoTaskQueue atq;
239   RefPtr<TaskQueue> queue = atq.Queue();
240   RunOnTaskQueue(queue, [queue]() -> void {
241     nsTArray<RefPtr<TestPromise>> promises;
242     promises.AppendElement(TestPromise::CreateAndResolve(22, __func__));
243     promises.AppendElement(TestPromise::CreateAndResolve(32, __func__));
244     promises.AppendElement(TestPromise::CreateAndResolve(42, __func__));
245 
246     TestPromise::All(queue, promises)
247         ->Then(
248             queue, __func__,
249             [queue](const CopyableTArray<int>& aResolveValues) -> void {
250               EXPECT_EQ(aResolveValues.Length(), 3UL);
251               EXPECT_EQ(aResolveValues[0], 22);
252               EXPECT_EQ(aResolveValues[1], 32);
253               EXPECT_EQ(aResolveValues[2], 42);
254               queue->BeginShutdown();
255             },
256             []() { EXPECT_TRUE(false); });
257   });
258 }
259 
TEST(MozPromise,PromiseAllResolveAsync)260 TEST(MozPromise, PromiseAllResolveAsync)
261 {
262   AutoTaskQueue atq;
263   RefPtr<TaskQueue> queue = atq.Queue();
264   RunOnTaskQueue(queue, [queue]() -> void {
265     nsTArray<RefPtr<TestPromise>> promises;
266     promises.AppendElement(InvokeAsync(queue, __func__, []() {
267       return TestPromise::CreateAndResolve(22, __func__);
268     }));
269     promises.AppendElement(InvokeAsync(queue, __func__, []() {
270       return TestPromise::CreateAndResolve(32, __func__);
271     }));
272     promises.AppendElement(InvokeAsync(queue, __func__, []() {
273       return TestPromise::CreateAndResolve(42, __func__);
274     }));
275 
276     TestPromise::All(queue, promises)
277         ->Then(
278             queue, __func__,
279             [queue](const CopyableTArray<int>& aResolveValues) -> void {
280               EXPECT_EQ(aResolveValues.Length(), 3UL);
281               EXPECT_EQ(aResolveValues[0], 22);
282               EXPECT_EQ(aResolveValues[1], 32);
283               EXPECT_EQ(aResolveValues[2], 42);
284               queue->BeginShutdown();
285             },
286             []() { EXPECT_TRUE(false); });
287   });
288 }
289 
TEST(MozPromise,PromiseAllReject)290 TEST(MozPromise, PromiseAllReject)
291 {
292   AutoTaskQueue atq;
293   RefPtr<TaskQueue> queue = atq.Queue();
294   RunOnTaskQueue(queue, [queue]() -> void {
295     nsTArray<RefPtr<TestPromise>> promises;
296     promises.AppendElement(TestPromise::CreateAndResolve(22, __func__));
297     promises.AppendElement(TestPromise::CreateAndReject(32.0, __func__));
298     promises.AppendElement(TestPromise::CreateAndResolve(42, __func__));
299     // Ensure that more than one rejection doesn't cause a crash (bug #1207312)
300     promises.AppendElement(TestPromise::CreateAndReject(52.0, __func__));
301 
302     TestPromise::All(queue, promises)
303         ->Then(
304             queue, __func__, []() { EXPECT_TRUE(false); },
305             [queue](float aRejectValue) -> void {
306               EXPECT_EQ(aRejectValue, 32.0);
307               queue->BeginShutdown();
308             });
309   });
310 }
311 
TEST(MozPromise,PromiseAllRejectAsync)312 TEST(MozPromise, PromiseAllRejectAsync)
313 {
314   AutoTaskQueue atq;
315   RefPtr<TaskQueue> queue = atq.Queue();
316   RunOnTaskQueue(queue, [queue]() -> void {
317     nsTArray<RefPtr<TestPromise>> promises;
318     promises.AppendElement(InvokeAsync(queue, __func__, []() {
319       return TestPromise::CreateAndResolve(22, __func__);
320     }));
321     promises.AppendElement(InvokeAsync(queue, __func__, []() {
322       return TestPromise::CreateAndReject(32.0, __func__);
323     }));
324     promises.AppendElement(InvokeAsync(queue, __func__, []() {
325       return TestPromise::CreateAndResolve(42, __func__);
326     }));
327     // Ensure that more than one rejection doesn't cause a crash (bug #1207312)
328     promises.AppendElement(InvokeAsync(queue, __func__, []() {
329       return TestPromise::CreateAndReject(52.0, __func__);
330     }));
331 
332     TestPromise::All(queue, promises)
333         ->Then(
334             queue, __func__, []() { EXPECT_TRUE(false); },
335             [queue](float aRejectValue) -> void {
336               EXPECT_EQ(aRejectValue, 32.0);
337               queue->BeginShutdown();
338             });
339   });
340 }
341 
TEST(MozPromise,PromiseAllSettled)342 TEST(MozPromise, PromiseAllSettled)
343 {
344   AutoTaskQueue atq;
345   RefPtr<TaskQueue> queue = atq.Queue();
346   RunOnTaskQueue(queue, [queue]() -> void {
347     nsTArray<RefPtr<TestPromise>> promises;
348     promises.AppendElement(TestPromise::CreateAndResolve(22, __func__));
349     promises.AppendElement(TestPromise::CreateAndReject(32.0, __func__));
350     promises.AppendElement(TestPromise::CreateAndResolve(42, __func__));
351     promises.AppendElement(TestPromise::CreateAndReject(52.0, __func__));
352 
353     TestPromise::AllSettled(queue, promises)
354         ->Then(
355             queue, __func__,
356             [queue](const TestPromise::AllSettledPromiseType::ResolveValueType&
357                         aResolveValues) -> void {
358               EXPECT_EQ(aResolveValues.Length(), 4UL);
359               EXPECT_TRUE(aResolveValues[0].IsResolve());
360               EXPECT_EQ(aResolveValues[0].ResolveValue(), 22);
361               EXPECT_FALSE(aResolveValues[1].IsResolve());
362               EXPECT_EQ(aResolveValues[1].RejectValue(), 32.0);
363               EXPECT_TRUE(aResolveValues[2].IsResolve());
364               EXPECT_EQ(aResolveValues[2].ResolveValue(), 42);
365               EXPECT_FALSE(aResolveValues[3].IsResolve());
366               EXPECT_EQ(aResolveValues[3].RejectValue(), 52.0);
367               queue->BeginShutdown();
368             },
369             []() { EXPECT_TRUE(false); });
370   });
371 }
372 
TEST(MozPromise,PromiseAllSettledAsync)373 TEST(MozPromise, PromiseAllSettledAsync)
374 {
375   AutoTaskQueue atq;
376   RefPtr<TaskQueue> queue = atq.Queue();
377 
378   RunOnTaskQueue(queue, [queue]() -> void {
379     nsTArray<RefPtr<TestPromise>> promises;
380     promises.AppendElement(InvokeAsync(queue, __func__, []() {
381       return TestPromise::CreateAndResolve(22, __func__);
382     }));
383     promises.AppendElement(InvokeAsync(queue, __func__, []() {
384       return TestPromise::CreateAndReject(32.0, __func__);
385     }));
386     promises.AppendElement(InvokeAsync(queue, __func__, []() {
387       return TestPromise::CreateAndResolve(42, __func__);
388     }));
389     promises.AppendElement(InvokeAsync(queue, __func__, []() {
390       return TestPromise::CreateAndReject(52.0, __func__);
391     }));
392 
393     TestPromise::AllSettled(queue, promises)
394         ->Then(
395             queue, __func__,
396             [queue](const TestPromise::AllSettledPromiseType::ResolveValueType&
397                         aResolveValues) -> void {
398               EXPECT_EQ(aResolveValues.Length(), 4UL);
399               EXPECT_TRUE(aResolveValues[0].IsResolve());
400               EXPECT_EQ(aResolveValues[0].ResolveValue(), 22);
401               EXPECT_FALSE(aResolveValues[1].IsResolve());
402               EXPECT_EQ(aResolveValues[1].RejectValue(), 32.0);
403               EXPECT_TRUE(aResolveValues[2].IsResolve());
404               EXPECT_EQ(aResolveValues[2].ResolveValue(), 42);
405               EXPECT_FALSE(aResolveValues[3].IsResolve());
406               EXPECT_EQ(aResolveValues[3].RejectValue(), 52.0);
407               queue->BeginShutdown();
408             },
409             []() { EXPECT_TRUE(false); });
410   });
411 }
412 
413 // Test we don't hit the assertions in MozPromise when exercising promise
414 // chaining upon task queue shutdown.
TEST(MozPromise,Chaining)415 TEST(MozPromise, Chaining)
416 {
417   // We declare this variable before |atq| to ensure
418   // the destructor is run after |holder.Disconnect()|.
419   MozPromiseRequestHolder<TestPromise> holder;
420 
421   AutoTaskQueue atq;
422   RefPtr<TaskQueue> queue = atq.Queue();
423 
424   RunOnTaskQueue(queue, [queue, &holder]() {
425     auto p = TestPromise::CreateAndResolve(42, __func__);
426     const size_t kIterations = 100;
427     for (size_t i = 0; i < kIterations; ++i) {
428       p = p->Then(
429           queue, __func__,
430           [](int aVal) {
431             EXPECT_EQ(aVal, 42);
432             return TestPromise::CreateAndResolve(aVal, __func__);
433           },
434           [](double aVal) {
435             return TestPromise::CreateAndReject(aVal, __func__);
436           });
437 
438       if (i == kIterations / 2) {
439         p->Then(
440             queue, __func__,
441             [queue, &holder]() {
442               holder.Disconnect();
443               queue->BeginShutdown();
444             },
445             DO_FAIL);
446       }
447     }
448     // We will hit the assertion if we don't disconnect the leaf Request
449     // in the promise chain.
450     p->Then(
451          queue, __func__, []() {}, []() {})
452         ->Track(holder);
453   });
454 }
455 
TEST(MozPromise,ResolveOrRejectValue)456 TEST(MozPromise, ResolveOrRejectValue)
457 {
458   using MyPromise = MozPromise<UniquePtr<int>, bool, false>;
459   using RRValue = MyPromise::ResolveOrRejectValue;
460 
461   RRValue val;
462   EXPECT_TRUE(val.IsNothing());
463   EXPECT_FALSE(val.IsResolve());
464   EXPECT_FALSE(val.IsReject());
465 
466   val.SetResolve(MakeUnique<int>(87));
467   EXPECT_FALSE(val.IsNothing());
468   EXPECT_TRUE(val.IsResolve());
469   EXPECT_FALSE(val.IsReject());
470   EXPECT_EQ(87, *val.ResolveValue());
471 
472   // IsResolve() should remain true after std::move().
473   UniquePtr<int> i = std::move(val.ResolveValue());
474   EXPECT_EQ(87, *i);
475   EXPECT_TRUE(val.IsResolve());
476   EXPECT_EQ(val.ResolveValue().get(), nullptr);
477 }
478 
TEST(MozPromise,MoveOnlyType)479 TEST(MozPromise, MoveOnlyType)
480 {
481   using MyPromise = MozPromise<UniquePtr<int>, bool, true>;
482   using RRValue = MyPromise::ResolveOrRejectValue;
483 
484   AutoTaskQueue atq;
485   RefPtr<TaskQueue> queue = atq.Queue();
486 
487   MyPromise::CreateAndResolve(MakeUnique<int>(87), __func__)
488       ->Then(
489           queue, __func__, [](UniquePtr<int> aVal) { EXPECT_EQ(87, *aVal); },
490           []() { EXPECT_TRUE(false); });
491 
492   MyPromise::CreateAndResolve(MakeUnique<int>(87), __func__)
493       ->Then(queue, __func__, [queue](RRValue&& aVal) {
494         EXPECT_FALSE(aVal.IsNothing());
495         EXPECT_TRUE(aVal.IsResolve());
496         EXPECT_FALSE(aVal.IsReject());
497         EXPECT_EQ(87, *aVal.ResolveValue());
498 
499         // std::move() shouldn't change the resolve/reject state of aVal.
500         RRValue val = std::move(aVal);
501         EXPECT_TRUE(aVal.IsResolve());
502         EXPECT_EQ(nullptr, aVal.ResolveValue().get());
503         EXPECT_EQ(87, *val.ResolveValue());
504 
505         queue->BeginShutdown();
506       });
507 }
508 
TEST(MozPromise,HeterogeneousChaining)509 TEST(MozPromise, HeterogeneousChaining)
510 {
511   using Promise1 = MozPromise<UniquePtr<char>, bool, true>;
512   using Promise2 = MozPromise<UniquePtr<int>, bool, true>;
513   using RRValue1 = Promise1::ResolveOrRejectValue;
514   using RRValue2 = Promise2::ResolveOrRejectValue;
515 
516   MozPromiseRequestHolder<Promise2> holder;
517 
518   AutoTaskQueue atq;
519   RefPtr<TaskQueue> queue = atq.Queue();
520 
521   RunOnTaskQueue(queue, [queue, &holder]() {
522     Promise1::CreateAndResolve(MakeUnique<char>(0), __func__)
523         ->Then(queue, __func__,
524                [&holder]() {
525                  holder.Disconnect();
526                  return Promise2::CreateAndResolve(MakeUnique<int>(0),
527                                                    __func__);
528                })
529         ->Then(queue, __func__,
530                []() {
531                  // Shouldn't be called for we've disconnected the request.
532                  EXPECT_FALSE(true);
533                })
534         ->Track(holder);
535   });
536 
537   Promise1::CreateAndResolve(MakeUnique<char>(87), __func__)
538       ->Then(
539           queue, __func__,
540           [](UniquePtr<char> aVal) {
541             EXPECT_EQ(87, *aVal);
542             return Promise2::CreateAndResolve(MakeUnique<int>(94), __func__);
543           },
544           []() {
545             return Promise2::CreateAndResolve(MakeUnique<int>(95), __func__);
546           })
547       ->Then(
548           queue, __func__, [](UniquePtr<int> aVal) { EXPECT_EQ(94, *aVal); },
549           []() { EXPECT_FALSE(true); });
550 
551   Promise1::CreateAndResolve(MakeUnique<char>(87), __func__)
552       ->Then(queue, __func__,
553              [](RRValue1&& aVal) {
554                EXPECT_EQ(87, *aVal.ResolveValue());
555                return Promise2::CreateAndResolve(MakeUnique<int>(94), __func__);
556              })
557       ->Then(queue, __func__, [queue](RRValue2&& aVal) {
558         EXPECT_EQ(94, *aVal.ResolveValue());
559         queue->BeginShutdown();
560       });
561 }
562 
TEST(MozPromise,XPCOMEventTarget)563 TEST(MozPromise, XPCOMEventTarget)
564 {
565   TestPromise::CreateAndResolve(42, __func__)
566       ->Then(
567           GetCurrentSerialEventTarget(), __func__,
568           [](int aResolveValue) -> void { EXPECT_EQ(aResolveValue, 42); },
569           DO_FAIL);
570 
571   // Spin the event loop.
572   NS_ProcessPendingEvents(nullptr);
573 }
574 
TEST(MozPromise,MessageLoopEventTarget)575 TEST(MozPromise, MessageLoopEventTarget)
576 {
577   TestPromise::CreateAndResolve(42, __func__)
578       ->Then(
579           MessageLoop::current()->SerialEventTarget(), __func__,
580           [](int aResolveValue) -> void { EXPECT_EQ(aResolveValue, 42); },
581           DO_FAIL);
582 
583   // Spin the event loop.
584   NS_ProcessPendingEvents(nullptr);
585 }
586 
TEST(MozPromise,ChainTo)587 TEST(MozPromise, ChainTo)
588 {
589   RefPtr<TestPromise> promise1 = TestPromise::CreateAndResolve(42, __func__);
590   RefPtr<TestPromise::Private> promise2 = new TestPromise::Private(__func__);
591   promise2->Then(
592       GetCurrentSerialEventTarget(), __func__,
593       [&](int aResolveValue) -> void { EXPECT_EQ(aResolveValue, 42); },
594       DO_FAIL);
595 
596   promise1->ChainTo(promise2.forget(), __func__);
597 
598   // Spin the event loop.
599   NS_ProcessPendingEvents(nullptr);
600 }
601 
TEST(MozPromise,SynchronousTaskDispatch1)602 TEST(MozPromise, SynchronousTaskDispatch1)
603 {
604   bool value = false;
605   RefPtr<TestPromiseExcl::Private> promise =
606       new TestPromiseExcl::Private(__func__);
607   promise->UseSynchronousTaskDispatch(__func__);
608   promise->Resolve(42, __func__);
609   EXPECT_EQ(value, false);
610   promise->Then(
611       GetCurrentSerialEventTarget(), __func__,
612       [&](int aResolveValue) -> void {
613         EXPECT_EQ(aResolveValue, 42);
614         value = true;
615       },
616       DO_FAIL);
617   EXPECT_EQ(value, true);
618 }
619 
TEST(MozPromise,SynchronousTaskDispatch2)620 TEST(MozPromise, SynchronousTaskDispatch2)
621 {
622   bool value = false;
623   RefPtr<TestPromiseExcl::Private> promise =
624       new TestPromiseExcl::Private(__func__);
625   promise->UseSynchronousTaskDispatch(__func__);
626   promise->Then(
627       GetCurrentSerialEventTarget(), __func__,
628       [&](int aResolveValue) -> void {
629         EXPECT_EQ(aResolveValue, 42);
630         value = true;
631       },
632       DO_FAIL);
633   EXPECT_EQ(value, false);
634   promise->Resolve(42, __func__);
635   EXPECT_EQ(value, true);
636 }
637 
TEST(MozPromise,DirectTaskDispatch)638 TEST(MozPromise, DirectTaskDispatch)
639 {
640   bool value1 = false;
641   bool value2 = false;
642 
643   // For direct task dispatch to be working, we must be within a
644   // nested event loop. So the test itself must be dispatched within
645   // a task.
646   GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction("test", [&]() {
647     GetCurrentSerialEventTarget()->Dispatch(
648         NS_NewRunnableFunction("test", [&]() {
649           EXPECT_EQ(value1, true);
650           value2 = true;
651         }));
652 
653     RefPtr<TestPromise::Private> promise = new TestPromise::Private(__func__);
654     promise->UseDirectTaskDispatch(__func__);
655     promise->Resolve(42, __func__);
656     EXPECT_EQ(value1, false);
657     promise->Then(
658         GetCurrentSerialEventTarget(), __func__,
659         [&](int aResolveValue) -> void {
660           EXPECT_EQ(aResolveValue, 42);
661           EXPECT_EQ(value2, false);
662           value1 = true;
663         },
664         DO_FAIL);
665     EXPECT_EQ(value1, false);
666   }));
667 
668   // Spin the event loop.
669   NS_ProcessPendingEvents(nullptr);
670 }
671 
TEST(MozPromise,ChainedDirectTaskDispatch)672 TEST(MozPromise, ChainedDirectTaskDispatch)
673 {
674   bool value1 = false;
675   bool value2 = false;
676 
677   // For direct task dispatch to be working, we must be within a
678   // nested event loop. So the test itself must be dispatched within
679   // a task.
680   GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction("test", [&]() {
681     GetCurrentSerialEventTarget()->Dispatch(
682         NS_NewRunnableFunction("test", [&]() {
683           EXPECT_EQ(value1, true);
684           value2 = true;
685         }));
686 
687     RefPtr<TestPromise::Private> promise1 = new TestPromise::Private(__func__);
688     promise1->UseDirectTaskDispatch(__func__);
689     promise1->Resolve(42, __func__);
690     EXPECT_EQ(value1, false);
691     promise1
692         ->Then(
693             GetCurrentSerialEventTarget(), __func__,
694             [&](int aResolveValue) -> RefPtr<TestPromise> {
695               EXPECT_EQ(aResolveValue, 42);
696               EXPECT_EQ(value2, false);
697               RefPtr<TestPromise::Private> promise2 =
698                   new TestPromise::Private(__func__);
699               promise2->UseDirectTaskDispatch(__func__);
700               promise2->Resolve(43, __func__);
701               return promise2;
702             },
703             DO_FAIL)
704         ->Then(
705             GetCurrentSerialEventTarget(), __func__,
706             [&](int aResolveValue) -> void {
707               EXPECT_EQ(aResolveValue, 43);
708               EXPECT_EQ(value2, false);
709               value1 = true;
710             },
711             DO_FAIL);
712     EXPECT_EQ(value1, false);
713   }));
714 
715   // Spin the event loop.
716   NS_ProcessPendingEvents(nullptr);
717 }
718 
TEST(MozPromise,ChainToDirectTaskDispatch)719 TEST(MozPromise, ChainToDirectTaskDispatch)
720 {
721   bool value1 = false;
722   bool value2 = false;
723 
724   // For direct task dispatch to be working, we must be within a
725   // nested event loop. So the test itself must be dispatched within
726   // a task.
727   GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction("test", [&]() {
728     GetCurrentSerialEventTarget()->Dispatch(
729         NS_NewRunnableFunction("test", [&]() {
730           EXPECT_EQ(value1, true);
731           value2 = true;
732         }));
733 
734     RefPtr<TestPromise::Private> promise1 = new TestPromise::Private(__func__);
735     promise1->UseDirectTaskDispatch(__func__);
736 
737     RefPtr<TestPromise::Private> promise2 = new TestPromise::Private(__func__);
738     promise2->Then(
739         GetCurrentSerialEventTarget(), __func__,
740         [&](int aResolveValue) -> void {
741           EXPECT_EQ(aResolveValue, 42);
742           EXPECT_EQ(value2, false);
743           value1 = true;
744         },
745         DO_FAIL);
746 
747     promise1->ChainTo(promise2.forget(), __func__);
748     EXPECT_EQ(value1, false);
749     promise1->Resolve(42, __func__);
750   }));
751 
752   // Spin the event loop.
753   NS_ProcessPendingEvents(nullptr);
754 }
755 
756 #undef DO_FAIL
757