Bug 1366072 - MozPromise tests (r=jwwang)
authorBill McCloskey <billm@mozilla.com>
Fri, 09 Jun 2017 14:10:21 -0700
changeset 363842 1d1e7a8da3106b41d193b76d8fa39eef0f336afa
parent 363841 ce865686934279c15e496b402c6f73a6127a3589
child 363843 651cebf4986c1dfad9468f6db9ca5dc342ad58d5
push id32027
push usercbook@mozilla.com
push dateWed, 14 Jun 2017 12:45:45 +0000
treeherdermozilla-central@45fde181a497 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwwang
bugs1366072
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1366072 - MozPromise tests (r=jwwang) MozReview-Commit-ID: 69OQb1f3UTd
dom/media/gtest/TestMozPromise.cpp
dom/media/gtest/moz.build
ipc/ipdl/test/cxx/TestAsyncReturns.cpp
xpcom/tests/gtest/TestMozPromise.cpp
xpcom/tests/gtest/moz.build
deleted file mode 100644
--- a/dom/media/gtest/TestMozPromise.cpp
+++ /dev/null
@@ -1,410 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "gtest/gtest.h"
-
-#include "mozilla/TaskQueue.h"
-#include "mozilla/MozPromise.h"
-
-#include "nsISupportsImpl.h"
-#include "mozilla/SharedThreadPool.h"
-#include "VideoUtils.h"
-
-using namespace mozilla;
-
-typedef MozPromise<int, double, false> TestPromise;
-typedef TestPromise::ResolveOrRejectValue RRValue;
-
-class MOZ_STACK_CLASS AutoTaskQueue
-{
-public:
-  AutoTaskQueue()
-    : mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK)))
-  {}
-
-  ~AutoTaskQueue()
-  {
-    mTaskQueue->AwaitShutdownAndIdle();
-  }
-
-  TaskQueue* Queue() { return mTaskQueue; }
-private:
-  RefPtr<TaskQueue> mTaskQueue;
-};
-
-class DelayedResolveOrReject : public Runnable
-{
-public:
-  DelayedResolveOrReject(TaskQueue* aTaskQueue,
-                         TestPromise::Private* aPromise,
-                         const TestPromise::ResolveOrRejectValue& aValue,
-                         int aIterations)
-  : mTaskQueue(aTaskQueue)
-  , mPromise(aPromise)
-  , mValue(aValue)
-  , mIterations(aIterations)
-  {}
-
-  NS_IMETHOD Run() override
-  {
-    MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
-    if (!mPromise) {
-      // Canceled.
-      return NS_OK;
-    }
-
-    if (--mIterations == 0) {
-      mPromise->ResolveOrReject(mValue, __func__);
-    } else {
-      nsCOMPtr<nsIRunnable> r = this;
-      mTaskQueue->Dispatch(r.forget());
-    }
-
-    return NS_OK;
-  }
-
-  void Cancel() {
-    mPromise = nullptr;
-  }
-
-protected:
-  ~DelayedResolveOrReject() {}
-
-private:
-  RefPtr<TaskQueue> mTaskQueue;
-  RefPtr<TestPromise::Private> mPromise;
-  TestPromise::ResolveOrRejectValue mValue;
-  int mIterations;
-};
-
-template<typename FunctionType>
-void
-RunOnTaskQueue(TaskQueue* aQueue, FunctionType aFun)
-{
-  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(aFun);
-  aQueue->Dispatch(r.forget());
-}
-
-// std::function can't come soon enough. :-(
-#define DO_FAIL []() { EXPECT_TRUE(false); return TestPromise::CreateAndReject(0, __func__); }
-
-TEST(MozPromise, BasicResolve)
-{
-  AutoTaskQueue atq;
-  RefPtr<TaskQueue> queue = atq.Queue();
-  RunOnTaskQueue(queue, [queue] () -> void {
-    TestPromise::CreateAndResolve(42, __func__)->Then(queue, __func__,
-      [queue] (int aResolveValue) -> void { EXPECT_EQ(aResolveValue, 42); queue->BeginShutdown(); },
-      DO_FAIL);
-  });
-}
-
-TEST(MozPromise, BasicReject)
-{
-  AutoTaskQueue atq;
-  RefPtr<TaskQueue> queue = atq.Queue();
-  RunOnTaskQueue(queue, [queue] () -> void {
-    TestPromise::CreateAndReject(42.0, __func__)->Then(queue, __func__,
-      DO_FAIL,
-      [queue] (int aRejectValue) -> void { EXPECT_EQ(aRejectValue, 42.0); queue->BeginShutdown(); });
-  });
-}
-
-TEST(MozPromise, BasicResolveOrRejectResolved)
-{
-  AutoTaskQueue atq;
-  RefPtr<TaskQueue> queue = atq.Queue();
-  RunOnTaskQueue(queue, [queue] () -> void {
-    TestPromise::CreateAndResolve(42, __func__)->Then(queue, __func__,
-      [queue] (const TestPromise::ResolveOrRejectValue& aValue) -> void
-      {
-        EXPECT_TRUE(aValue.IsResolve());
-        EXPECT_FALSE(aValue.IsReject());
-        EXPECT_FALSE(aValue.IsNothing());
-        EXPECT_EQ(aValue.ResolveValue(), 42);
-        queue->BeginShutdown();
-      });
-  });
-}
-
-TEST(MozPromise, BasicResolveOrRejectRejected)
-{
-  AutoTaskQueue atq;
-  RefPtr<TaskQueue> queue = atq.Queue();
-  RunOnTaskQueue(queue, [queue] () -> void {
-    TestPromise::CreateAndReject(42.0, __func__)->Then(queue, __func__,
-      [queue] (const TestPromise::ResolveOrRejectValue& aValue) -> void
-      {
-        EXPECT_TRUE(aValue.IsReject());
-        EXPECT_FALSE(aValue.IsResolve());
-        EXPECT_FALSE(aValue.IsNothing());
-        EXPECT_EQ(aValue.RejectValue(), 42.0);
-        queue->BeginShutdown();
-      });
-  });
-}
-
-TEST(MozPromise, AsyncResolve)
-{
-  AutoTaskQueue atq;
-  RefPtr<TaskQueue> queue = atq.Queue();
-  RunOnTaskQueue(queue, [queue] () -> void {
-    RefPtr<TestPromise::Private> p = new TestPromise::Private(__func__);
-
-    // Kick off three racing tasks, and make sure we get the one that finishes earliest.
-    RefPtr<DelayedResolveOrReject> a = new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(32), 10);
-    RefPtr<DelayedResolveOrReject> b = new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(42), 5);
-    RefPtr<DelayedResolveOrReject> c = new DelayedResolveOrReject(queue, p, RRValue::MakeReject(32.0), 7);
-
-    nsCOMPtr<nsIRunnable> ref = a.get();
-    queue->Dispatch(ref.forget());
-    ref = b.get();
-    queue->Dispatch(ref.forget());
-    ref = c.get();
-    queue->Dispatch(ref.forget());
-
-    p->Then(queue, __func__, [queue, a, b, c] (int aResolveValue) -> void {
-      EXPECT_EQ(aResolveValue, 42);
-      a->Cancel();
-      b->Cancel();
-      c->Cancel();
-      queue->BeginShutdown();
-    }, DO_FAIL);
-  });
-}
-
-TEST(MozPromise, CompletionPromises)
-{
-  bool invokedPass = false;
-  AutoTaskQueue atq;
-  RefPtr<TaskQueue> queue = atq.Queue();
-  RunOnTaskQueue(queue, [queue, &invokedPass] () -> void {
-    TestPromise::CreateAndResolve(40, __func__)
-    ->Then(queue, __func__,
-      [] (int aVal) -> RefPtr<TestPromise> { return TestPromise::CreateAndResolve(aVal + 10, __func__); },
-      DO_FAIL)
-    ->Then(queue, __func__,
-           [&invokedPass] (int aVal) {
-             invokedPass = true;
-             return TestPromise::CreateAndResolve(aVal, __func__);
-           }, DO_FAIL)
-    ->Then(queue, __func__,
-      [queue] (int aVal) -> RefPtr<TestPromise> {
-        RefPtr<TestPromise::Private> p = new TestPromise::Private(__func__);
-        nsCOMPtr<nsIRunnable> resolver = new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(aVal - 8), 10);
-        queue->Dispatch(resolver.forget());
-        return RefPtr<TestPromise>(p);
-      },
-      DO_FAIL)
-    ->Then(queue, __func__,
-      [] (int aVal) -> RefPtr<TestPromise> { return TestPromise::CreateAndReject(double(aVal - 42) + 42.0, __func__); },
-      DO_FAIL)
-    ->Then(queue, __func__,
-      DO_FAIL,
-      [queue, &invokedPass] (double aVal) -> void { EXPECT_EQ(aVal, 42.0); EXPECT_TRUE(invokedPass); queue->BeginShutdown(); });
-  });
-}
-
-TEST(MozPromise, PromiseAllResolve)
-{
-  AutoTaskQueue atq;
-  RefPtr<TaskQueue> queue = atq.Queue();
-  RunOnTaskQueue(queue, [queue] () -> void {
-
-    nsTArray<RefPtr<TestPromise>> promises;
-    promises.AppendElement(TestPromise::CreateAndResolve(22, __func__));
-    promises.AppendElement(TestPromise::CreateAndResolve(32, __func__));
-    promises.AppendElement(TestPromise::CreateAndResolve(42, __func__));
-
-    TestPromise::All(queue, promises)->Then(queue, __func__,
-      [queue] (const nsTArray<int>& aResolveValues) -> void {
-        EXPECT_EQ(aResolveValues.Length(), 3UL);
-        EXPECT_EQ(aResolveValues[0], 22);
-        EXPECT_EQ(aResolveValues[1], 32);
-        EXPECT_EQ(aResolveValues[2], 42);
-        queue->BeginShutdown();
-      },
-      []() { EXPECT_TRUE(false); }
-    );
-  });
-}
-
-TEST(MozPromise, PromiseAllReject)
-{
-  AutoTaskQueue atq;
-  RefPtr<TaskQueue> queue = atq.Queue();
-  RunOnTaskQueue(queue, [queue] () -> void {
-
-    nsTArray<RefPtr<TestPromise>> promises;
-    promises.AppendElement(TestPromise::CreateAndResolve(22, __func__));
-    promises.AppendElement(TestPromise::CreateAndReject(32.0, __func__));
-    promises.AppendElement(TestPromise::CreateAndResolve(42, __func__));
-   // Ensure that more than one rejection doesn't cause a crash (bug #1207312)
-    promises.AppendElement(TestPromise::CreateAndReject(52.0, __func__));
-
-    TestPromise::All(queue, promises)->Then(queue, __func__,
-      []() { EXPECT_TRUE(false); },
-      [queue] (float aRejectValue) -> void {
-        EXPECT_EQ(aRejectValue, 32.0);
-        queue->BeginShutdown();
-      }
-    );
-  });
-}
-
-// Test we don't hit the assertions in MozPromise when exercising promise
-// chaining upon task queue shutdown.
-TEST(MozPromise, Chaining)
-{
-  // We declare this variable before |atq| to ensure
-  // the destructor is run after |holder.Disconnect()|.
-  MozPromiseRequestHolder<TestPromise> holder;
-
-  AutoTaskQueue atq;
-  RefPtr<TaskQueue> queue = atq.Queue();
-
-  RunOnTaskQueue(queue, [queue, &holder] () {
-    auto p = TestPromise::CreateAndResolve(42, __func__);
-    const size_t kIterations = 100;
-    for (size_t i = 0; i < kIterations; ++i) {
-      p = p->Then(queue, __func__,
-        [] (int aVal) {
-          EXPECT_EQ(aVal, 42);
-          return TestPromise::CreateAndResolve(aVal, __func__);
-        },
-        [] (double aVal) {
-          return TestPromise::CreateAndReject(aVal, __func__);
-        }
-      );
-
-      if (i == kIterations / 2) {
-        p->Then(queue, __func__,
-          [queue, &holder] () {
-            holder.Disconnect();
-            queue->BeginShutdown();
-          },
-          DO_FAIL);
-      }
-    }
-    // We will hit the assertion if we don't disconnect the leaf Request
-    // in the promise chain.
-    p->Then(queue, __func__, [] () {}, [] () {})->Track(holder);
-  });
-}
-
-TEST(MozPromise, ResolveOrRejectValue)
-{
-  using MyPromise = MozPromise<UniquePtr<int>, bool, false>;
-  using RRValue = MyPromise::ResolveOrRejectValue;
-
-  RRValue val;
-  EXPECT_TRUE(val.IsNothing());
-  EXPECT_FALSE(val.IsResolve());
-  EXPECT_FALSE(val.IsReject());
-
-  val.SetResolve(MakeUnique<int>(87));
-  EXPECT_FALSE(val.IsNothing());
-  EXPECT_TRUE(val.IsResolve());
-  EXPECT_FALSE(val.IsReject());
-  EXPECT_EQ(87, *val.ResolveValue());
-
-  // IsResolve() should remain true after Move().
-  UniquePtr<int> i = Move(val.ResolveValue());
-  EXPECT_EQ(87, *i);
-  EXPECT_TRUE(val.IsResolve());
-  EXPECT_EQ(val.ResolveValue().get(), nullptr);
-}
-
-TEST(MozPromise, MoveOnlyType)
-{
-  using MyPromise = MozPromise<UniquePtr<int>, bool, true>;
-  using RRValue = MyPromise::ResolveOrRejectValue;
-
-  AutoTaskQueue atq;
-  RefPtr<TaskQueue> queue = atq.Queue();
-
-  MyPromise::CreateAndResolve(MakeUnique<int>(87), __func__)
-  ->Then(queue, __func__,
-    [](UniquePtr<int> aVal) {
-      EXPECT_EQ(87, *aVal);
-    },
-    []() { EXPECT_TRUE(false); });
-
-  MyPromise::CreateAndResolve(MakeUnique<int>(87), __func__)
-  ->Then(queue, __func__,
-    [queue](RRValue&& aVal) {
-      EXPECT_FALSE(aVal.IsNothing());
-      EXPECT_TRUE(aVal.IsResolve());
-      EXPECT_FALSE(aVal.IsReject());
-      EXPECT_EQ(87, *aVal.ResolveValue());
-
-      // Move() shouldn't change the resolve/reject state of aVal.
-      RRValue val = Move(aVal);
-      EXPECT_TRUE(aVal.IsResolve());
-      EXPECT_EQ(nullptr, aVal.ResolveValue().get());
-      EXPECT_EQ(87, *val.ResolveValue());
-
-      queue->BeginShutdown();
-    });
-}
-
-TEST(MozPromise, HeterogeneousChaining)
-{
-  using Promise1 = MozPromise<UniquePtr<char>, bool, true>;
-  using Promise2 = MozPromise<UniquePtr<int>, bool, true>;
-  using RRValue1 = Promise1::ResolveOrRejectValue;
-  using RRValue2 = Promise2::ResolveOrRejectValue;
-
-  MozPromiseRequestHolder<Promise2> holder;
-
-  AutoTaskQueue atq;
-  RefPtr<TaskQueue> queue = atq.Queue();
-
-  RunOnTaskQueue(queue, [queue, &holder]() {
-    Promise1::CreateAndResolve(MakeUnique<char>(0), __func__)
-      ->Then(queue,
-             __func__,
-             [&holder]() {
-               holder.Disconnect();
-               return Promise2::CreateAndResolve(MakeUnique<int>(0), __func__);
-             })
-      ->Then(queue,
-             __func__,
-             []() {
-               // Shouldn't be called for we've disconnected the request.
-               EXPECT_FALSE(true);
-             })
-      ->Track(holder);
-  });
-
-  Promise1::CreateAndResolve(MakeUnique<char>(87), __func__)
-    ->Then(queue,
-           __func__,
-           [](UniquePtr<char> aVal) {
-             EXPECT_EQ(87, *aVal);
-             return Promise2::CreateAndResolve(MakeUnique<int>(94), __func__);
-           },
-           []() {
-             return Promise2::CreateAndResolve(MakeUnique<int>(95), __func__);
-           })
-    ->Then(queue,
-           __func__,
-           [](UniquePtr<int> aVal) { EXPECT_EQ(94, *aVal); },
-           []() { EXPECT_FALSE(true); });
-
-  Promise1::CreateAndResolve(MakeUnique<char>(87), __func__)
-    ->Then(queue,
-           __func__,
-           [](RRValue1&& aVal) {
-             EXPECT_EQ(87, *aVal.ResolveValue());
-             return Promise2::CreateAndResolve(MakeUnique<int>(94), __func__);
-           })
-    ->Then(queue, __func__, [queue](RRValue2&& aVal) {
-      EXPECT_EQ(94, *aVal.ResolveValue());
-      queue->BeginShutdown();
-    });
-}
-
-#undef DO_FAIL
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -14,17 +14,16 @@ UNIFIED_SOURCES += [
     'TestBlankVideoDataCreator.cpp',
     'TestGMPCrossOrigin.cpp',
     'TestGMPRemoveAndDelete.cpp',
     'TestGMPUtils.cpp',
     'TestIntervalSet.cpp',
     'TestMediaDataDecoder.cpp',
     'TestMediaEventSource.cpp',
     'TestMediaMIMETypes.cpp',
-    'TestMozPromise.cpp',
     'TestMP3Demuxer.cpp',
     'TestMP4Demuxer.cpp',
     'TestRust.cpp',
     'TestTrackEncoder.cpp',
     'TestVideoSegment.cpp',
     'TestVideoUtils.cpp',
     'TestVPXDecoding.cpp',
     'TestWebMBuffered.cpp',
--- a/ipc/ipdl/test/cxx/TestAsyncReturns.cpp
+++ b/ipc/ipdl/test/cxx/TestAsyncReturns.cpp
@@ -22,32 +22,29 @@ TestAsyncReturnsParent::TestAsyncReturns
 TestAsyncReturnsParent::~TestAsyncReturnsParent()
 {
   MOZ_COUNT_DTOR(TestAsyncReturnsParent);
 }
 
 void
 TestAsyncReturnsParent::Main()
 {
-  if (!AbstractThread::MainThread()) {
-    fail("AbstractThread not initalized");
-  }
-  SendNoReturn()->Then(AbstractThread::MainThread(), __func__,
+  SendNoReturn()->Then(MessageLoop::current()->SerialEventTarget(), __func__,
                        [](bool unused) {
                          fail("resolve handler should not be called");
                        },
                        [](PromiseRejectReason aReason) {
                          // MozPromise asserts in debug build if the
                          // handler is not called
                          if (aReason != PromiseRejectReason::ChannelClosed) {
                            fail("reject with wrong reason");
                          }
                          passed("reject handler called on channel close");
                        });
-  SendPing()->Then(AbstractThread::MainThread(), __func__,
+  SendPing()->Then(MessageLoop::current()->SerialEventTarget(), __func__,
                    [this](bool one) {
                      if (one) {
                        passed("take one argument");
                      } else {
                        fail("get one argument but has wrong value");
                      }
                      Close();
                    },
@@ -83,20 +80,17 @@ TestAsyncReturnsChild::RecvNoReturn(NoRe
 {
   // Not resolving the promise intentionally
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TestAsyncReturnsChild::RecvPing(PingResolver&& aResolve)
 {
-  if (!AbstractThread::MainThread()) {
-    fail("AbstractThread not initalized");
-  }
-  SendPong()->Then(AbstractThread::MainThread(), __func__,
+  SendPong()->Then(MessageLoop::current()->SerialEventTarget(), __func__,
                    [aResolve](const Tuple<uint32_t, uint32_t>& aParam) {
                      if (Get<0>(aParam) == sMagic1 && Get<1>(aParam) == sMagic2) {
                        passed("take two arguments");
                      } else {
                        fail("get two argument but has wrong value");
                      }
                      aResolve(true);
                    },
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/gtest/TestMozPromise.cpp
@@ -0,0 +1,432 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "base/message_loop.h"
+
+#include "mozilla/TaskQueue.h"
+#include "mozilla/MozPromise.h"
+
+#include "nsISupportsImpl.h"
+#include "mozilla/SharedThreadPool.h"
+#include "VideoUtils.h"
+
+using namespace mozilla;
+
+typedef MozPromise<int, double, false> TestPromise;
+typedef TestPromise::ResolveOrRejectValue RRValue;
+
+class MOZ_STACK_CLASS AutoTaskQueue
+{
+public:
+  AutoTaskQueue()
+    : mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK)))
+  {}
+
+  ~AutoTaskQueue()
+  {
+    mTaskQueue->AwaitShutdownAndIdle();
+  }
+
+  TaskQueue* Queue() { return mTaskQueue; }
+private:
+  RefPtr<TaskQueue> mTaskQueue;
+};
+
+class DelayedResolveOrReject : public Runnable
+{
+public:
+  DelayedResolveOrReject(TaskQueue* aTaskQueue,
+                         TestPromise::Private* aPromise,
+                         const TestPromise::ResolveOrRejectValue& aValue,
+                         int aIterations)
+  : mTaskQueue(aTaskQueue)
+  , mPromise(aPromise)
+  , mValue(aValue)
+  , mIterations(aIterations)
+  {}
+
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+    if (!mPromise) {
+      // Canceled.
+      return NS_OK;
+    }
+
+    if (--mIterations == 0) {
+      mPromise->ResolveOrReject(mValue, __func__);
+    } else {
+      nsCOMPtr<nsIRunnable> r = this;
+      mTaskQueue->Dispatch(r.forget());
+    }
+
+    return NS_OK;
+  }
+
+  void Cancel() {
+    mPromise = nullptr;
+  }
+
+protected:
+  ~DelayedResolveOrReject() {}
+
+private:
+  RefPtr<TaskQueue> mTaskQueue;
+  RefPtr<TestPromise::Private> mPromise;
+  TestPromise::ResolveOrRejectValue mValue;
+  int mIterations;
+};
+
+template<typename FunctionType>
+void
+RunOnTaskQueue(TaskQueue* aQueue, FunctionType aFun)
+{
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(aFun);
+  aQueue->Dispatch(r.forget());
+}
+
+// std::function can't come soon enough. :-(
+#define DO_FAIL []() { EXPECT_TRUE(false); return TestPromise::CreateAndReject(0, __func__); }
+
+TEST(MozPromise, BasicResolve)
+{
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+  RunOnTaskQueue(queue, [queue] () -> void {
+    TestPromise::CreateAndResolve(42, __func__)->Then(queue, __func__,
+      [queue] (int aResolveValue) -> void { EXPECT_EQ(aResolveValue, 42); queue->BeginShutdown(); },
+      DO_FAIL);
+  });
+}
+
+TEST(MozPromise, BasicReject)
+{
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+  RunOnTaskQueue(queue, [queue] () -> void {
+    TestPromise::CreateAndReject(42.0, __func__)->Then(queue, __func__,
+      DO_FAIL,
+      [queue] (int aRejectValue) -> void { EXPECT_EQ(aRejectValue, 42.0); queue->BeginShutdown(); });
+  });
+}
+
+TEST(MozPromise, BasicResolveOrRejectResolved)
+{
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+  RunOnTaskQueue(queue, [queue] () -> void {
+    TestPromise::CreateAndResolve(42, __func__)->Then(queue, __func__,
+      [queue] (const TestPromise::ResolveOrRejectValue& aValue) -> void
+      {
+        EXPECT_TRUE(aValue.IsResolve());
+        EXPECT_FALSE(aValue.IsReject());
+        EXPECT_FALSE(aValue.IsNothing());
+        EXPECT_EQ(aValue.ResolveValue(), 42);
+        queue->BeginShutdown();
+      });
+  });
+}
+
+TEST(MozPromise, BasicResolveOrRejectRejected)
+{
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+  RunOnTaskQueue(queue, [queue] () -> void {
+    TestPromise::CreateAndReject(42.0, __func__)->Then(queue, __func__,
+      [queue] (const TestPromise::ResolveOrRejectValue& aValue) -> void
+      {
+        EXPECT_TRUE(aValue.IsReject());
+        EXPECT_FALSE(aValue.IsResolve());
+        EXPECT_FALSE(aValue.IsNothing());
+        EXPECT_EQ(aValue.RejectValue(), 42.0);
+        queue->BeginShutdown();
+      });
+  });
+}
+
+TEST(MozPromise, AsyncResolve)
+{
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+  RunOnTaskQueue(queue, [queue] () -> void {
+    RefPtr<TestPromise::Private> p = new TestPromise::Private(__func__);
+
+    // Kick off three racing tasks, and make sure we get the one that finishes earliest.
+    RefPtr<DelayedResolveOrReject> a = new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(32), 10);
+    RefPtr<DelayedResolveOrReject> b = new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(42), 5);
+    RefPtr<DelayedResolveOrReject> c = new DelayedResolveOrReject(queue, p, RRValue::MakeReject(32.0), 7);
+
+    nsCOMPtr<nsIRunnable> ref = a.get();
+    queue->Dispatch(ref.forget());
+    ref = b.get();
+    queue->Dispatch(ref.forget());
+    ref = c.get();
+    queue->Dispatch(ref.forget());
+
+    p->Then(queue, __func__, [queue, a, b, c] (int aResolveValue) -> void {
+      EXPECT_EQ(aResolveValue, 42);
+      a->Cancel();
+      b->Cancel();
+      c->Cancel();
+      queue->BeginShutdown();
+    }, DO_FAIL);
+  });
+}
+
+TEST(MozPromise, CompletionPromises)
+{
+  bool invokedPass = false;
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+  RunOnTaskQueue(queue, [queue, &invokedPass] () -> void {
+    TestPromise::CreateAndResolve(40, __func__)
+    ->Then(queue, __func__,
+      [] (int aVal) -> RefPtr<TestPromise> { return TestPromise::CreateAndResolve(aVal + 10, __func__); },
+      DO_FAIL)
+    ->Then(queue, __func__,
+           [&invokedPass] (int aVal) {
+             invokedPass = true;
+             return TestPromise::CreateAndResolve(aVal, __func__);
+           }, DO_FAIL)
+    ->Then(queue, __func__,
+      [queue] (int aVal) -> RefPtr<TestPromise> {
+        RefPtr<TestPromise::Private> p = new TestPromise::Private(__func__);
+        nsCOMPtr<nsIRunnable> resolver = new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(aVal - 8), 10);
+        queue->Dispatch(resolver.forget());
+        return RefPtr<TestPromise>(p);
+      },
+      DO_FAIL)
+    ->Then(queue, __func__,
+      [] (int aVal) -> RefPtr<TestPromise> { return TestPromise::CreateAndReject(double(aVal - 42) + 42.0, __func__); },
+      DO_FAIL)
+    ->Then(queue, __func__,
+      DO_FAIL,
+      [queue, &invokedPass] (double aVal) -> void { EXPECT_EQ(aVal, 42.0); EXPECT_TRUE(invokedPass); queue->BeginShutdown(); });
+  });
+}
+
+TEST(MozPromise, PromiseAllResolve)
+{
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+  RunOnTaskQueue(queue, [queue] () -> void {
+
+    nsTArray<RefPtr<TestPromise>> promises;
+    promises.AppendElement(TestPromise::CreateAndResolve(22, __func__));
+    promises.AppendElement(TestPromise::CreateAndResolve(32, __func__));
+    promises.AppendElement(TestPromise::CreateAndResolve(42, __func__));
+
+    TestPromise::All(queue, promises)->Then(queue, __func__,
+      [queue] (const nsTArray<int>& aResolveValues) -> void {
+        EXPECT_EQ(aResolveValues.Length(), 3UL);
+        EXPECT_EQ(aResolveValues[0], 22);
+        EXPECT_EQ(aResolveValues[1], 32);
+        EXPECT_EQ(aResolveValues[2], 42);
+        queue->BeginShutdown();
+      },
+      []() { EXPECT_TRUE(false); }
+    );
+  });
+}
+
+TEST(MozPromise, PromiseAllReject)
+{
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+  RunOnTaskQueue(queue, [queue] () -> void {
+
+    nsTArray<RefPtr<TestPromise>> promises;
+    promises.AppendElement(TestPromise::CreateAndResolve(22, __func__));
+    promises.AppendElement(TestPromise::CreateAndReject(32.0, __func__));
+    promises.AppendElement(TestPromise::CreateAndResolve(42, __func__));
+   // Ensure that more than one rejection doesn't cause a crash (bug #1207312)
+    promises.AppendElement(TestPromise::CreateAndReject(52.0, __func__));
+
+    TestPromise::All(queue, promises)->Then(queue, __func__,
+      []() { EXPECT_TRUE(false); },
+      [queue] (float aRejectValue) -> void {
+        EXPECT_EQ(aRejectValue, 32.0);
+        queue->BeginShutdown();
+      }
+    );
+  });
+}
+
+// Test we don't hit the assertions in MozPromise when exercising promise
+// chaining upon task queue shutdown.
+TEST(MozPromise, Chaining)
+{
+  // We declare this variable before |atq| to ensure
+  // the destructor is run after |holder.Disconnect()|.
+  MozPromiseRequestHolder<TestPromise> holder;
+
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+
+  RunOnTaskQueue(queue, [queue, &holder] () {
+    auto p = TestPromise::CreateAndResolve(42, __func__);
+    const size_t kIterations = 100;
+    for (size_t i = 0; i < kIterations; ++i) {
+      p = p->Then(queue, __func__,
+        [] (int aVal) {
+          EXPECT_EQ(aVal, 42);
+          return TestPromise::CreateAndResolve(aVal, __func__);
+        },
+        [] (double aVal) {
+          return TestPromise::CreateAndReject(aVal, __func__);
+        }
+      );
+
+      if (i == kIterations / 2) {
+        p->Then(queue, __func__,
+          [queue, &holder] () {
+            holder.Disconnect();
+            queue->BeginShutdown();
+          },
+          DO_FAIL);
+      }
+    }
+    // We will hit the assertion if we don't disconnect the leaf Request
+    // in the promise chain.
+    p->Then(queue, __func__, [] () {}, [] () {})->Track(holder);
+  });
+}
+
+TEST(MozPromise, ResolveOrRejectValue)
+{
+  using MyPromise = MozPromise<UniquePtr<int>, bool, false>;
+  using RRValue = MyPromise::ResolveOrRejectValue;
+
+  RRValue val;
+  EXPECT_TRUE(val.IsNothing());
+  EXPECT_FALSE(val.IsResolve());
+  EXPECT_FALSE(val.IsReject());
+
+  val.SetResolve(MakeUnique<int>(87));
+  EXPECT_FALSE(val.IsNothing());
+  EXPECT_TRUE(val.IsResolve());
+  EXPECT_FALSE(val.IsReject());
+  EXPECT_EQ(87, *val.ResolveValue());
+
+  // IsResolve() should remain true after Move().
+  UniquePtr<int> i = Move(val.ResolveValue());
+  EXPECT_EQ(87, *i);
+  EXPECT_TRUE(val.IsResolve());
+  EXPECT_EQ(val.ResolveValue().get(), nullptr);
+}
+
+TEST(MozPromise, MoveOnlyType)
+{
+  using MyPromise = MozPromise<UniquePtr<int>, bool, true>;
+  using RRValue = MyPromise::ResolveOrRejectValue;
+
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+
+  MyPromise::CreateAndResolve(MakeUnique<int>(87), __func__)
+  ->Then(queue, __func__,
+    [](UniquePtr<int> aVal) {
+      EXPECT_EQ(87, *aVal);
+    },
+    []() { EXPECT_TRUE(false); });
+
+  MyPromise::CreateAndResolve(MakeUnique<int>(87), __func__)
+  ->Then(queue, __func__,
+    [queue](RRValue&& aVal) {
+      EXPECT_FALSE(aVal.IsNothing());
+      EXPECT_TRUE(aVal.IsResolve());
+      EXPECT_FALSE(aVal.IsReject());
+      EXPECT_EQ(87, *aVal.ResolveValue());
+
+      // Move() shouldn't change the resolve/reject state of aVal.
+      RRValue val = Move(aVal);
+      EXPECT_TRUE(aVal.IsResolve());
+      EXPECT_EQ(nullptr, aVal.ResolveValue().get());
+      EXPECT_EQ(87, *val.ResolveValue());
+
+      queue->BeginShutdown();
+    });
+}
+
+TEST(MozPromise, HeterogeneousChaining)
+{
+  using Promise1 = MozPromise<UniquePtr<char>, bool, true>;
+  using Promise2 = MozPromise<UniquePtr<int>, bool, true>;
+  using RRValue1 = Promise1::ResolveOrRejectValue;
+  using RRValue2 = Promise2::ResolveOrRejectValue;
+
+  MozPromiseRequestHolder<Promise2> holder;
+
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+
+  RunOnTaskQueue(queue, [queue, &holder]() {
+    Promise1::CreateAndResolve(MakeUnique<char>(0), __func__)
+      ->Then(queue,
+             __func__,
+             [&holder]() {
+               holder.Disconnect();
+               return Promise2::CreateAndResolve(MakeUnique<int>(0), __func__);
+             })
+      ->Then(queue,
+             __func__,
+             []() {
+               // Shouldn't be called for we've disconnected the request.
+               EXPECT_FALSE(true);
+             })
+      ->Track(holder);
+  });
+
+  Promise1::CreateAndResolve(MakeUnique<char>(87), __func__)
+    ->Then(queue,
+           __func__,
+           [](UniquePtr<char> aVal) {
+             EXPECT_EQ(87, *aVal);
+             return Promise2::CreateAndResolve(MakeUnique<int>(94), __func__);
+           },
+           []() {
+             return Promise2::CreateAndResolve(MakeUnique<int>(95), __func__);
+           })
+    ->Then(queue,
+           __func__,
+           [](UniquePtr<int> aVal) { EXPECT_EQ(94, *aVal); },
+           []() { EXPECT_FALSE(true); });
+
+  Promise1::CreateAndResolve(MakeUnique<char>(87), __func__)
+    ->Then(queue,
+           __func__,
+           [](RRValue1&& aVal) {
+             EXPECT_EQ(87, *aVal.ResolveValue());
+             return Promise2::CreateAndResolve(MakeUnique<int>(94), __func__);
+           })
+    ->Then(queue, __func__, [queue](RRValue2&& aVal) {
+      EXPECT_EQ(94, *aVal.ResolveValue());
+      queue->BeginShutdown();
+    });
+}
+
+TEST(MozPromise, XPCOMEventTarget)
+{
+  TestPromise::CreateAndResolve(42, __func__)->Then(GetCurrentThreadSerialEventTarget(), __func__,
+    [] (int aResolveValue) -> void { EXPECT_EQ(aResolveValue, 42); },
+    DO_FAIL);
+
+  // Spin the event loop.
+  NS_ProcessPendingEvents(nullptr);
+}
+
+TEST(MozPromise, MessageLoopEventTarget)
+{
+  TestPromise::CreateAndResolve(42, __func__)->Then(MessageLoop::current()->SerialEventTarget(), __func__,
+    [] (int aResolveValue) -> void { EXPECT_EQ(aResolveValue, 42); },
+    DO_FAIL);
+
+  // Spin the event loop.
+  NS_ProcessPendingEvents(nullptr);
+}
+
+#undef DO_FAIL
--- a/xpcom/tests/gtest/moz.build
+++ b/xpcom/tests/gtest/moz.build
@@ -17,16 +17,17 @@ UNIFIED_SOURCES += [
     'TestCRT.cpp',
     'TestEncoding.cpp',
     'TestEscapeURL.cpp',
     'TestEventTargetQI.cpp',
     'TestExpirationTracker.cpp',
     'TestFile.cpp',
     'TestGCPostBarriers.cpp',
     'TestID.cpp',
+    'TestMozPromise.cpp',
     'TestMultiplexInputStream.cpp',
     'TestNsDeque.cpp',
     'TestNSPRLogModulesParser.cpp',
     'TestObserverArray.cpp',
     'TestObserverService.cpp',
     'TestPipes.cpp',
     'TestPLDHash.cpp',
     'TestPriorityQueue.cpp',
@@ -80,8 +81,10 @@ SOURCES += [
     'TestNsRefPtr.cpp', # Redefines Foo
 ]
 
 LOCAL_INCLUDES += [
     '../../base',
 ]
 
 FINAL_LIBRARY = 'xul-gtest'
+
+include('/ipc/chromium/chromium-config.mozbuild')