summaryrefslogtreecommitdiff
path: root/ipc/ipdl/test/cxx
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/ipdl/test/cxx')
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp32
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h32
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp23
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h37
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestTypes.h44
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTestUtils.h27
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTests.h93
-rw-r--r--ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp390
-rw-r--r--ipc/ipdl/test/cxx/Makefile.in46
-rw-r--r--ipc/ipdl/test/cxx/PTestActorPunning.ipdl39
-rw-r--r--ipc/ipdl/test/cxx/PTestActorPunningPunned.ipdl15
-rw-r--r--ipc/ipdl/test/cxx/PTestActorPunningSub.ipdl16
-rw-r--r--ipc/ipdl/test/cxx/PTestBadActor.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestBadActorSub.ipdl17
-rw-r--r--ipc/ipdl/test/cxx/PTestBridgeMain.ipdl26
-rw-r--r--ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl33
-rw-r--r--ipc/ipdl/test/cxx/PTestBridgeSub.ipdl25
-rw-r--r--ipc/ipdl/test/cxx/PTestCancel.ipdl36
-rw-r--r--ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl22
-rw-r--r--ipc/ipdl/test/cxx/PTestDataStructures.ipdl132
-rw-r--r--ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh107
-rw-r--r--ipc/ipdl/test/cxx/PTestDataStructuresSub.ipdl15
-rw-r--r--ipc/ipdl/test/cxx/PTestDemon.ipdl21
-rw-r--r--ipc/ipdl/test/cxx/PTestDesc.ipdl31
-rw-r--r--ipc/ipdl/test/cxx/PTestDescSub.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestDescSubsub.ipdl15
-rw-r--r--ipc/ipdl/test/cxx/PTestEndpointBridgeMain.ipdl23
-rw-r--r--ipc/ipdl/test/cxx/PTestEndpointBridgeMainSub.ipdl25
-rw-r--r--ipc/ipdl/test/cxx/PTestEndpointBridgeSub.ipdl22
-rw-r--r--ipc/ipdl/test/cxx/PTestEndpointOpens.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl30
-rw-r--r--ipc/ipdl/test/cxx/PTestFailedCtor.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestFailedCtorSubsub.ipdl15
-rw-r--r--ipc/ipdl/test/cxx/PTestHandle.ipdl14
-rw-r--r--ipc/ipdl/test/cxx/PTestHangs.ipdl40
-rw-r--r--ipc/ipdl/test/cxx/PTestHighestPrio.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestIndirectProtocolParam.ipdlh15
-rw-r--r--ipc/ipdl/test/cxx/PTestIndirectProtocolParamFirst.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestIndirectProtocolParamManage.ipdl17
-rw-r--r--ipc/ipdl/test/cxx/PTestIndirectProtocolParamSecond.ipdl13
-rw-r--r--ipc/ipdl/test/cxx/PTestInterruptErrorCleanup.ipdl11
-rw-r--r--ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl82
-rw-r--r--ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl34
-rw-r--r--ipc/ipdl/test/cxx/PTestJSON.ipdl54
-rw-r--r--ipc/ipdl/test/cxx/PTestLatency.ipdl75
-rw-r--r--ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl34
-rw-r--r--ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl18
-rw-r--r--ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl24
-rw-r--r--ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl24
-rw-r--r--ipc/ipdl/test/cxx/PTestNestedLoops.ipdl34
-rw-r--r--ipc/ipdl/test/cxx/PTestOpens.ipdl25
-rw-r--r--ipc/ipdl/test/cxx/PTestOpensOpened.ipdl28
-rw-r--r--ipc/ipdl/test/cxx/PTestPriority.ipdl14
-rw-r--r--ipc/ipdl/test/cxx/PTestRPC.ipdl21
-rw-r--r--ipc/ipdl/test/cxx/PTestRaceDeadlock.ipdl20
-rw-r--r--ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl45
-rw-r--r--ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl41
-rw-r--r--ipc/ipdl/test/cxx/PTestRacyReentry.ipdl21
-rw-r--r--ipc/ipdl/test/cxx/PTestRacyUndefer.ipdl28
-rw-r--r--ipc/ipdl/test/cxx/PTestSanity.ipdl28
-rw-r--r--ipc/ipdl/test/cxx/PTestSelfManage.ipdl22
-rw-r--r--ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl23
-rw-r--r--ipc/ipdl/test/cxx/PTestShmem.ipdl22
-rw-r--r--ipc/ipdl/test/cxx/PTestShutdown.ipdl37
-rw-r--r--ipc/ipdl/test/cxx/PTestShutdownSub.ipdl30
-rw-r--r--ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl17
-rw-r--r--ipc/ipdl/test/cxx/PTestStackHooks.ipdl56
-rw-r--r--ipc/ipdl/test/cxx/PTestSyncError.ipdl28
-rw-r--r--ipc/ipdl/test/cxx/PTestSyncHang.ipdl15
-rw-r--r--ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl41
-rw-r--r--ipc/ipdl/test/cxx/PTestUrgency.ipdl19
-rw-r--r--ipc/ipdl/test/cxx/PTestUrgentHangs.ipdl28
-rw-r--r--ipc/ipdl/test/cxx/README.txt61
-rw-r--r--ipc/ipdl/test/cxx/TestActorPunning.cpp128
-rw-r--r--ipc/ipdl/test/cxx/TestActorPunning.h110
-rw-r--r--ipc/ipdl/test/cxx/TestBadActor.cpp51
-rw-r--r--ipc/ipdl/test/cxx/TestBadActor.h91
-rw-r--r--ipc/ipdl/test/cxx/TestBridgeMain.cpp227
-rw-r--r--ipc/ipdl/test/cxx/TestBridgeMain.h149
-rw-r--r--ipc/ipdl/test/cxx/TestCancel.cpp168
-rw-r--r--ipc/ipdl/test/cxx/TestCancel.h66
-rw-r--r--ipc/ipdl/test/cxx/TestCrashCleanup.cpp116
-rw-r--r--ipc/ipdl/test/cxx/TestCrashCleanup.h58
-rw-r--r--ipc/ipdl/test/cxx/TestDataStructures.cpp984
-rw-r--r--ipc/ipdl/test/cxx/TestDataStructures.h234
-rw-r--r--ipc/ipdl/test/cxx/TestDemon.cpp417
-rw-r--r--ipc/ipdl/test/cxx/TestDemon.h106
-rw-r--r--ipc/ipdl/test/cxx/TestDesc.cpp105
-rw-r--r--ipc/ipdl/test/cxx/TestDesc.h128
-rw-r--r--ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp257
-rw-r--r--ipc/ipdl/test/cxx/TestEndpointBridgeMain.h133
-rw-r--r--ipc/ipdl/test/cxx/TestEndpointOpens.cpp271
-rw-r--r--ipc/ipdl/test/cxx/TestEndpointOpens.h101
-rw-r--r--ipc/ipdl/test/cxx/TestFailedCtor.cpp137
-rw-r--r--ipc/ipdl/test/cxx/TestFailedCtor.h136
-rw-r--r--ipc/ipdl/test/cxx/TestHangs.cpp146
-rw-r--r--ipc/ipdl/test/cxx/TestHangs.h87
-rw-r--r--ipc/ipdl/test/cxx/TestHighestPrio.cpp118
-rw-r--r--ipc/ipdl/test/cxx/TestHighestPrio.h68
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp155
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h60
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptRaces.cpp220
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptRaces.h131
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp134
-rw-r--r--ipc/ipdl/test/cxx/TestInterruptShutdownRace.h64
-rw-r--r--ipc/ipdl/test/cxx/TestJSON.cpp129
-rw-r--r--ipc/ipdl/test/cxx/TestJSON.h111
-rw-r--r--ipc/ipdl/test/cxx/TestLatency.cpp258
-rw-r--r--ipc/ipdl/test/cxx/TestLatency.h111
-rw-r--r--ipc/ipdl/test/cxx/TestManyChildAllocs.cpp104
-rw-r--r--ipc/ipdl/test/cxx/TestManyChildAllocs.h94
-rw-r--r--ipc/ipdl/test/cxx/TestMultiMgrs.cpp104
-rw-r--r--ipc/ipdl/test/cxx/TestMultiMgrs.h250
-rw-r--r--ipc/ipdl/test/cxx/TestNestedLoops.cpp98
-rw-r--r--ipc/ipdl/test/cxx/TestNestedLoops.h67
-rw-r--r--ipc/ipdl/test/cxx/TestOpens.cpp251
-rw-r--r--ipc/ipdl/test/cxx/TestOpens.h107
-rw-r--r--ipc/ipdl/test/cxx/TestRPC.cpp152
-rw-r--r--ipc/ipdl/test/cxx/TestRPC.h74
-rw-r--r--ipc/ipdl/test/cxx/TestRaceDeadlock.cpp131
-rw-r--r--ipc/ipdl/test/cxx/TestRaceDeadlock.h77
-rw-r--r--ipc/ipdl/test/cxx/TestRaceDeferral.cpp118
-rw-r--r--ipc/ipdl/test/cxx/TestRaceDeferral.h76
-rw-r--r--ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp119
-rw-r--r--ipc/ipdl/test/cxx/TestRacyInterruptReplies.h73
-rw-r--r--ipc/ipdl/test/cxx/TestRacyReentry.cpp84
-rw-r--r--ipc/ipdl/test/cxx/TestRacyReentry.h67
-rw-r--r--ipc/ipdl/test/cxx/TestRacyUndefer.cpp115
-rw-r--r--ipc/ipdl/test/cxx/TestRacyUndefer.h70
-rw-r--r--ipc/ipdl/test/cxx/TestSanity.cpp75
-rw-r--r--ipc/ipdl/test/cxx/TestSanity.h63
-rw-r--r--ipc/ipdl/test/cxx/TestSelfManageRoot.cpp63
-rw-r--r--ipc/ipdl/test/cxx/TestSelfManageRoot.h141
-rw-r--r--ipc/ipdl/test/cxx/TestShmem.cpp115
-rw-r--r--ipc/ipdl/test/cxx/TestShmem.h66
-rw-r--r--ipc/ipdl/test/cxx/TestShutdown.cpp235
-rw-r--r--ipc/ipdl/test/cxx/TestShutdown.h225
-rw-r--r--ipc/ipdl/test/cxx/TestStackHooks.cpp168
-rw-r--r--ipc/ipdl/test/cxx/TestStackHooks.h130
-rw-r--r--ipc/ipdl/test/cxx/TestSyncError.cpp61
-rw-r--r--ipc/ipdl/test/cxx/TestSyncError.h71
-rw-r--r--ipc/ipdl/test/cxx/TestSyncHang.cpp70
-rw-r--r--ipc/ipdl/test/cxx/TestSyncHang.h58
-rw-r--r--ipc/ipdl/test/cxx/TestSyncWakeup.cpp134
-rw-r--r--ipc/ipdl/test/cxx/TestSyncWakeup.h74
-rw-r--r--ipc/ipdl/test/cxx/TestUrgency.cpp162
-rw-r--r--ipc/ipdl/test/cxx/TestUrgency.h72
-rw-r--r--ipc/ipdl/test/cxx/TestUrgentHangs.cpp217
-rw-r--r--ipc/ipdl/test/cxx/TestUrgentHangs.h79
-rw-r--r--ipc/ipdl/test/cxx/app/Makefile.in5
-rw-r--r--ipc/ipdl/test/cxx/app/TestIPDL.cpp20
-rw-r--r--ipc/ipdl/test/cxx/app/moz.build20
-rw-r--r--ipc/ipdl/test/cxx/genIPDLUnitTests.py141
-rw-r--r--ipc/ipdl/test/cxx/moz.build134
157 files changed, 13510 insertions, 0 deletions
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp
new file mode 100644
index 0000000000..7123e3db80
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.cpp
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 "mozilla/ipc/IOThreadChild.h"
+
+#include "IPDLUnitTestProcessChild.h"
+#include "IPDLUnitTests.h"
+
+#include "nsRegion.h"
+
+using mozilla::ipc::IOThreadChild;
+
+namespace mozilla {
+namespace _ipdltest {
+
+bool
+IPDLUnitTestProcessChild::Init()
+{
+ IPDLUnitTestChildInit(IOThreadChild::channel(),
+ ParentPid(),
+ IOThreadChild::message_loop());
+
+ if (NS_FAILED(nsRegion::InitStatic()))
+ return false;
+
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h
new file mode 100644
index 0000000000..693cc7360f
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestProcessChild.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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/. */
+
+#ifndef mozilla__ipdltest_IPDLUnitTestThreadChild_h
+#define mozilla__ipdltest_IPDLUnitTestThreadChild_h 1
+
+#include "mozilla/ipc/ProcessChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class IPDLUnitTestProcessChild : public mozilla::ipc::ProcessChild
+{
+ typedef mozilla::ipc::ProcessChild ProcessChild;
+
+public:
+ explicit IPDLUnitTestProcessChild(ProcessId aParentPid) :
+ ProcessChild(aParentPid)
+ { }
+
+ ~IPDLUnitTestProcessChild()
+ { }
+
+ virtual bool Init();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // ifndef mozilla__ipdltest_IPDLUnitTestThreadChild_h
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp
new file mode 100644
index 0000000000..87fe0ddc7c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 "IPDLUnitTestSubprocess.h"
+
+using mozilla::ipc::GeckoChildProcessHost;
+
+namespace mozilla {
+namespace _ipdltest {
+
+IPDLUnitTestSubprocess::IPDLUnitTestSubprocess() :
+ GeckoChildProcessHost(GeckoProcessType_IPDLUnitTest)
+{
+}
+
+IPDLUnitTestSubprocess::~IPDLUnitTestSubprocess()
+{
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h
new file mode 100644
index 0000000000..cdb212df20
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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/. */
+
+#ifndef mozilla__ipdltest_IPDLUnitTestTestSubprocess_h
+#define mozilla__ipdltest_IPDLUnitTestTestSubprocess_h 1
+
+
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
+namespace mozilla {
+namespace _ipdltest {
+//-----------------------------------------------------------------------------
+
+class IPDLUnitTestSubprocess : public mozilla::ipc::GeckoChildProcessHost
+{
+public:
+ IPDLUnitTestSubprocess();
+ ~IPDLUnitTestSubprocess();
+
+ /**
+ * Asynchronously launch the plugin process.
+ */
+ // Could override parent Launch, but don't need to here
+ //bool Launch();
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(IPDLUnitTestSubprocess);
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_IPDLUnitTestTestSubprocess_h
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h b/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h
new file mode 100644
index 0000000000..6da135a0ae
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestTypes.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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/. */
+
+#ifndef mozilla__ipdltest_IPDLUnitTestTypes_h
+#define mozilla__ipdltest_IPDLUnitTestTypes_h
+
+#include "mozilla/ipc/ProtocolUtils.h" // ActorDestroyReason
+
+namespace mozilla {
+namespace _ipdltest {
+
+struct DirtyRect
+{
+ int x; int y; int w; int h;
+};
+
+}
+}
+
+namespace IPC {
+template<>
+struct ParamTraits<mozilla::_ipdltest::DirtyRect>
+{
+ typedef mozilla::_ipdltest::DirtyRect paramType;
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.x);
+ WriteParam(aMsg, aParam.y);
+ WriteParam(aMsg, aParam.w);
+ WriteParam(aMsg, aParam.h);
+ }
+ static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->x) &&
+ ReadParam(aMsg, aIter, &aResult->y) &&
+ ReadParam(aMsg, aIter, &aResult->w) &&
+ ReadParam(aMsg, aIter, &aResult->h));
+ }
+};
+}
+
+
+#endif // ifndef mozilla__ipdltest_IPDLUnitTestTypes_h
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTestUtils.h b/ipc/ipdl/test/cxx/IPDLUnitTestUtils.h
new file mode 100644
index 0000000000..ad9da374df
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestUtils.h
@@ -0,0 +1,27 @@
+
+#ifndef mozilla__ipdltest_IPDLUnitTestUtils
+#define mozilla__ipdltest_IPDLUnitTestUtils 1
+
+namespace mozilla {
+namespace _ipdltest {
+
+struct Bad {};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::_ipdltest::Bad>
+{
+ typedef mozilla::_ipdltest::Bad paramType;
+
+ // Defined in TestActorPunning.cpp.
+ static void Write(Message* aMsg, const paramType& aParam);
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult);
+};
+
+} // namespace IPC
+
+#endif // mozilla__ipdltest_IPDLUnitTestUtils
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTests.h b/ipc/ipdl/test/cxx/IPDLUnitTests.h
new file mode 100644
index 0000000000..544bc0ffb8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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/. */
+
+#ifndef mozilla__ipdltest_IPDLUnitTests_h
+#define mozilla__ipdltest_IPDLUnitTests_h 1
+
+#include "base/message_loop.h"
+#include "base/process.h"
+#include "chrome/common/ipc_channel.h"
+
+#include "nsIAppShell.h"
+
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsServiceManagerUtils.h" // do_GetService()
+#include "nsWidgetsCID.h" // NS_APPSHELL_CID
+#include "nsXULAppAPI.h"
+
+
+#define MOZ_IPDL_TESTFAIL_LABEL "TEST-UNEXPECTED-FAIL"
+#define MOZ_IPDL_TESTPASS_LABEL "TEST-PASS"
+#define MOZ_IPDL_TESTINFO_LABEL "TEST-INFO"
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// both processes
+const char* IPDLUnitTestName();
+
+// NB: these are named like the similar functions in
+// xpcom/test/TestHarness.h. The names should nominally be kept in
+// sync.
+
+inline void fail(const char* fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL " | %s | ", IPDLUnitTestName());
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fputc('\n', stderr);
+
+ NS_RUNTIMEABORT("failed test");
+}
+
+inline void passed(const char* fmt, ...)
+{
+ va_list ap;
+
+ printf(MOZ_IPDL_TESTPASS_LABEL " | %s | ", IPDLUnitTestName());
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+
+ fputc('\n', stdout);
+}
+
+//-----------------------------------------------------------------------------
+// parent process only
+
+class IPDLUnitTestSubprocess;
+
+extern void* gParentActor;
+extern IPDLUnitTestSubprocess* gSubprocess;
+
+void IPDLUnitTestMain(void* aData);
+
+void QuitParent();
+
+//-----------------------------------------------------------------------------
+// child process only
+
+extern void* gChildActor;
+
+void IPDLUnitTestChildInit(IPC::Channel* transport,
+ base::ProcessId parentPid,
+ MessageLoop* worker);
+
+void QuitChild();
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_IPDLUnitTests_h
diff --git a/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
new file mode 100644
index 0000000000..6a91033bf4
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
@@ -0,0 +1,390 @@
+//
+// Autogenerated from Python template. Hands off.
+//
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "IPDLUnitTests.h"
+
+#include "base/command_line.h"
+#include "base/string_util.h"
+#include "base/task.h"
+#include "base/thread.h"
+
+#include "nsRegion.h"
+
+#include "IPDLUnitTestSubprocess.h"
+
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${INCLUDES}
+//-----------------------------------------------------------------------------
+
+using namespace std;
+
+using base::Thread;
+
+namespace mozilla {
+namespace _ipdltest {
+
+void* gParentActor;
+IPDLUnitTestSubprocess* gSubprocess;
+
+void* gChildActor;
+
+// Note: in threaded mode, this will be non-null (for both parent and
+// child, since they share one set of globals).
+Thread* gChildThread;
+MessageLoop *gParentMessageLoop;
+bool gParentDone;
+bool gChildDone;
+
+void
+DeleteChildActor();
+
+//-----------------------------------------------------------------------------
+// data/functions accessed by both parent and child processes
+
+char* gIPDLUnitTestName = nullptr;
+
+const char*
+IPDLUnitTestName()
+{
+ if (!gIPDLUnitTestName) {
+#if defined(OS_WIN)
+ vector<wstring> args =
+ CommandLine::ForCurrentProcess()->GetLooseValues();
+ gIPDLUnitTestName = ::strdup(WideToUTF8(args[0]).c_str());
+#elif defined(OS_POSIX)
+ vector<string> argv = CommandLine::ForCurrentProcess()->argv();
+ gIPDLUnitTestName = ::moz_xstrdup(argv[1].c_str());
+#else
+# error Sorry
+#endif
+ }
+ return gIPDLUnitTestName;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+namespace {
+
+enum IPDLUnitTestType {
+ NoneTest = 0,
+
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${ENUM_VALUES}
+
+ LastTest = ${LAST_ENUM}
+//-----------------------------------------------------------------------------
+};
+
+
+IPDLUnitTestType
+IPDLUnitTestFromString(const char* const aString)
+{
+ if (!aString)
+ return static_cast<IPDLUnitTestType>(0);
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${STRING_TO_ENUMS}
+//-----------------------------------------------------------------------------
+ else
+ return static_cast<IPDLUnitTestType>(0);
+}
+
+
+const char*
+IPDLUnitTestToString(IPDLUnitTestType aTest)
+{
+ switch (aTest) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${ENUM_TO_STRINGS}
+//-----------------------------------------------------------------------------
+
+ default:
+ return nullptr;
+ }
+}
+
+
+IPDLUnitTestType
+IPDLUnitTest()
+{
+ return IPDLUnitTestFromString(::mozilla::_ipdltest::IPDLUnitTestName());
+}
+
+
+} // namespace <anon>
+
+
+//-----------------------------------------------------------------------------
+// parent process only
+
+namespace mozilla {
+namespace _ipdltest {
+
+void
+DeferredParentShutdown();
+
+void
+IPDLUnitTestThreadMain(char *testString);
+
+void
+IPDLUnitTestMain(void* aData)
+{
+ char* testString = reinterpret_cast<char*>(aData);
+
+ // Check if we are to run the test using threads instead:
+ const char *prefix = "thread:";
+ const int prefixLen = strlen(prefix);
+ if (!strncmp(testString, prefix, prefixLen)) {
+ IPDLUnitTestThreadMain(testString + prefixLen);
+ return;
+ }
+
+ IPDLUnitTestType test = IPDLUnitTestFromString(testString);
+ if (!test) {
+ // use this instead of |fail()| because we don't know what the test is
+ fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL "| %s | unknown unit test %s\n",
+ "<--->", testString);
+ NS_RUNTIMEABORT("can't continue");
+ }
+ gIPDLUnitTestName = testString;
+
+ // Check whether this test is enabled for processes:
+ switch (test) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_ENABLED_CASES_PROC}
+//-----------------------------------------------------------------------------
+
+ default:
+ fail("not reached");
+ return; // unreached
+ }
+
+ printf(MOZ_IPDL_TESTINFO_LABEL "| running test | %s\n", gIPDLUnitTestName);
+
+ std::vector<std::string> testCaseArgs;
+ testCaseArgs.push_back(testString);
+
+ gSubprocess = new IPDLUnitTestSubprocess();
+ if (!gSubprocess->SyncLaunch(testCaseArgs))
+ fail("problem launching subprocess");
+
+ IPC::Channel* transport = gSubprocess->GetChannel();
+ if (!transport)
+ fail("no transport");
+
+ base::ProcessId child = base::GetProcId(gSubprocess->GetChildProcessHandle());
+
+ switch (test) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_MAIN_CASES_PROC}
+//-----------------------------------------------------------------------------
+
+ default:
+ fail("not reached");
+ return; // unreached
+ }
+}
+
+void
+IPDLUnitTestThreadMain(char *testString)
+{
+ IPDLUnitTestType test = IPDLUnitTestFromString(testString);
+ if (!test) {
+ // use this instead of |fail()| because we don't know what the test is
+ fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL "| %s | unknown unit test %s\n",
+ "<--->", testString);
+ NS_RUNTIMEABORT("can't continue");
+ }
+ gIPDLUnitTestName = testString;
+
+ // Check whether this test is enabled for threads:
+ switch (test) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_ENABLED_CASES_THREAD}
+//-----------------------------------------------------------------------------
+
+ default:
+ fail("not reached");
+ return; // unreached
+ }
+
+ printf(MOZ_IPDL_TESTINFO_LABEL "| running test | %s\n", gIPDLUnitTestName);
+
+ std::vector<std::string> testCaseArgs;
+ testCaseArgs.push_back(testString);
+
+ gChildThread = new Thread("ParentThread");
+ if (!gChildThread->Start())
+ fail("starting parent thread");
+
+ gParentMessageLoop = MessageLoop::current();
+ MessageLoop *childMessageLoop = gChildThread->message_loop();
+
+ switch (test) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_MAIN_CASES_THREAD}
+//-----------------------------------------------------------------------------
+
+ default:
+ fail("not reached");
+ return; // unreached
+ }
+}
+
+void
+DeleteParentActor()
+{
+ if (!gParentActor)
+ return;
+
+ switch (IPDLUnitTest()) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_DELETE_CASES}
+//-----------------------------------------------------------------------------
+ default: ::mozilla::_ipdltest::fail("???");
+ }
+}
+
+void
+QuitXPCOM()
+{
+ DeleteParentActor();
+
+ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+ nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
+ appShell->Exit();
+}
+
+void
+DeleteSubprocess(MessageLoop* uiLoop)
+{
+ // pong to QuitXPCOM
+ delete gSubprocess;
+ uiLoop->PostTask(NewRunnableFunction(QuitXPCOM));
+}
+
+void
+DeferredParentShutdown()
+{
+ // ping to DeleteSubprocess
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(DeleteSubprocess, MessageLoop::current()));
+}
+
+void
+TryThreadedShutdown()
+{
+ // Stop if either:
+ // - the child has not finished,
+ // - the parent has not finished,
+ // - or this code has already executed.
+ // Remember: this TryThreadedShutdown() task is enqueued
+ // by both parent and child (though always on parent's msg loop).
+ if (!gChildDone || !gParentDone || !gChildThread)
+ return;
+
+ delete gChildThread;
+ gChildThread = 0;
+ DeferredParentShutdown();
+}
+
+void
+ChildCompleted()
+{
+ // Executes on the parent message loop once child has completed.
+ gChildDone = true;
+ TryThreadedShutdown();
+}
+
+void
+QuitParent()
+{
+ if (gChildThread) {
+ gParentDone = true;
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction(TryThreadedShutdown));
+ } else {
+ // defer "real" shutdown to avoid *Channel::Close() racing with the
+ // deletion of the subprocess
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction(DeferredParentShutdown));
+ }
+}
+
+static void
+ChildDie()
+{
+ DeleteChildActor();
+ XRE_ShutdownChildProcess();
+}
+
+void
+QuitChild()
+{
+ if (gChildThread) { // Threaded-mode test
+ gParentMessageLoop->PostTask(
+ NewRunnableFunction(ChildCompleted));
+ } else { // Process-mode test
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction(ChildDie));
+ }
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+//-----------------------------------------------------------------------------
+// child process only
+
+namespace mozilla {
+namespace _ipdltest {
+
+void
+DeleteChildActor()
+{
+ if (!gChildActor)
+ return;
+
+ switch (IPDLUnitTest()) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${CHILD_DELETE_CASES}
+//-----------------------------------------------------------------------------
+ default: ::mozilla::_ipdltest::fail("???");
+ }
+}
+
+void
+IPDLUnitTestChildInit(IPC::Channel* transport,
+ base::ProcessId parentPid,
+ MessageLoop* worker)
+{
+ switch (IPDLUnitTest()) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${CHILD_INIT_CASES}
+//-----------------------------------------------------------------------------
+
+ default:
+ fail("not reached");
+ return; // unreached
+ }
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/Makefile.in b/ipc/ipdl/test/cxx/Makefile.in
new file mode 100644
index 0000000000..8c9cdaa99a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/Makefile.in
@@ -0,0 +1,46 @@
+# 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/.
+
+IPDLTESTSRCS = $(filter Test%,$(CPPSRCS))
+IPDLTESTS = $(IPDLTESTSRCS:.cpp=)
+
+EXTRA_PROTOCOLS = \
+ TestBridgeSub \
+ TestEndpointBridgeSub \
+ $(NULL)
+
+IPDLTESTHDRS = $(addprefix $(srcdir)/,$(addsuffix .h,$(IPDLTESTS)))
+
+TESTER_TEMPLATE := $(srcdir)/IPDLUnitTests.template.cpp
+GENTESTER := $(srcdir)/genIPDLUnitTests.py
+
+include $(topsrcdir)/config/rules.mk
+
+
+IPDLUNITTEST_BIN = $(DEPTH)/dist/bin/ipdlunittest$(BIN_SUFFIX)
+
+IPDLUnitTests.cpp : Makefile.in moz.build $(GENTESTER) $(TESTER_TEMPLATE) $(IPDLTESTHDRS)
+ $(PYTHON) $(GENTESTER) $(TESTER_TEMPLATE) -t $(IPDLTESTS) -e $(EXTRA_PROTOCOLS) > $@
+
+check-proc::
+ @$(EXIT_ON_ERROR) \
+ for test in $(IPDLTESTS); do \
+ $(RUN_TEST_PROGRAM) $(IPDLUNITTEST_BIN) $$test ; \
+ done
+
+check-thread::
+ @$(EXIT_ON_ERROR) \
+ for test in $(IPDLTESTS); do \
+ $(RUN_TEST_PROGRAM) $(IPDLUNITTEST_BIN) thread:$$test ; \
+ done
+
+check:: check-proc check-thread
+
+check-valgrind::
+ @$(EXIT_ON_ERROR) \
+ for test in $(IPDLTESTS); do \
+ $(RUN_TEST_PROGRAM) -g -d \
+ valgrind -a '--leak-check=full --trace-children=yes -q' \
+ $(IPDLUNITTEST_BIN) $$test ; \
+ done
diff --git a/ipc/ipdl/test/cxx/PTestActorPunning.ipdl b/ipc/ipdl/test/cxx/PTestActorPunning.ipdl
new file mode 100644
index 0000000000..54deb277a7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestActorPunning.ipdl
@@ -0,0 +1,39 @@
+
+include protocol PTestActorPunningPunned;
+include protocol PTestActorPunningSub;
+include "mozilla/_ipdltest/IPDLUnitTestUtils.h";
+
+using struct mozilla::_ipdltest::Bad from "mozilla/_ipdltest/IPDLUnitTestUtils.h";
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestActorPunning {
+ manages PTestActorPunningPunned;
+ manages PTestActorPunningSub;
+
+child:
+ async Start();
+
+parent:
+ async PTestActorPunningPunned();
+ async PTestActorPunningSub();
+ async Pun(PTestActorPunningSub a, Bad bad);
+ async __delete__();
+
+
+state PING:
+ send Start goto CONSTRUCTING;
+
+state CONSTRUCTING:
+ recv PTestActorPunningPunned goto CONSTRUCTING;
+ recv PTestActorPunningSub goto CONSTRUCTING;
+ recv Pun goto DEAD;
+ // We never make it past this transition, --> error.
+
+state DEAD:
+ recv __delete__;
+};
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestActorPunningPunned.ipdl b/ipc/ipdl/test/cxx/PTestActorPunningPunned.ipdl
new file mode 100644
index 0000000000..a6b875920e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestActorPunningPunned.ipdl
@@ -0,0 +1,15 @@
+
+include protocol PTestActorPunning;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestActorPunningPunned {
+ manager PTestActorPunning;
+
+child:
+ async __delete__();
+};
+
+} // namespace mozilla
+} // namespace _ipdltes
diff --git a/ipc/ipdl/test/cxx/PTestActorPunningSub.ipdl b/ipc/ipdl/test/cxx/PTestActorPunningSub.ipdl
new file mode 100644
index 0000000000..1441219c38
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestActorPunningSub.ipdl
@@ -0,0 +1,16 @@
+
+include protocol PTestActorPunning;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestActorPunningSub {
+ manager PTestActorPunning;
+
+child:
+ async Bad();
+ async __delete__();
+};
+
+} // namespace mozilla
+} // namespace _ipdltes
diff --git a/ipc/ipdl/test/cxx/PTestBadActor.ipdl b/ipc/ipdl/test/cxx/PTestBadActor.ipdl
new file mode 100644
index 0000000000..841d89ff63
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBadActor.ipdl
@@ -0,0 +1,18 @@
+include protocol PTestBadActorSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+// Test that a parent sending a reentrant __delete__ message
+// is not killed if a child's message races with the reply.
+
+intr protocol PTestBadActor {
+ manages PTestBadActorSub;
+
+child:
+ async PTestBadActorSub();
+ async __delete__();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestBadActorSub.ipdl b/ipc/ipdl/test/cxx/PTestBadActorSub.ipdl
new file mode 100644
index 0000000000..99c78f4ac9
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBadActorSub.ipdl
@@ -0,0 +1,17 @@
+include protocol PTestBadActor;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestBadActorSub {
+ manager PTestBadActor;
+
+child:
+ intr __delete__();
+
+parent:
+ async Ping();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestBridgeMain.ipdl b/ipc/ipdl/test/cxx/PTestBridgeMain.ipdl
new file mode 100644
index 0000000000..229820b172
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBridgeMain.ipdl
@@ -0,0 +1,26 @@
+include protocol PTestBridgeMainSub;
+include protocol PTestBridgeSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestBridgeMain {
+ child spawns PTestBridgeSub;
+ child opens PTestBridgeMainSub;
+
+child:
+ async Start();
+
+parent:
+ async __delete__();
+
+state START:
+ send Start goto DEAD;
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl b/ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl
new file mode 100644
index 0000000000..67e50fdd46
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBridgeMainSub.ipdl
@@ -0,0 +1,33 @@
+include protocol PTestBridgeMain;
+include protocol PTestBridgeSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+// (Bridge protocols can have different semantics than the endpoints
+// they bridge)
+intr protocol PTestBridgeMainSub {
+ bridges PTestBridgeMain, PTestBridgeSub;
+
+child:
+ async Hi();
+ intr HiRpc();
+
+parent:
+ async Hello();
+ sync HelloSync();
+ intr HelloRpc();
+ async __delete__();
+
+state START: recv Hello goto HI;
+state HI: send Hi goto HELLO_SYNC;
+state HELLO_SYNC: recv HelloSync goto HELLO_RPC;
+state HELLO_RPC: answer HelloRpc goto HI_RPC;
+state HI_RPC: call HiRpc goto DEAD;
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestBridgeSub.ipdl b/ipc/ipdl/test/cxx/PTestBridgeSub.ipdl
new file mode 100644
index 0000000000..6c798c84e5
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestBridgeSub.ipdl
@@ -0,0 +1,25 @@
+include protocol PTestBridgeMainSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestBridgeSub {
+child:
+ async Ping();
+
+parent:
+ async BridgeEm();
+ async __delete__();
+
+state START:
+ send Ping goto BRIDGEEM;
+state BRIDGEEM:
+ recv BridgeEm goto DEAD;
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestCancel.ipdl b/ipc/ipdl/test/cxx/PTestCancel.ipdl
new file mode 100644
index 0000000000..3a6b46b437
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestCancel.ipdl
@@ -0,0 +1,36 @@
+namespace mozilla {
+namespace _ipdltest {
+
+nested(upto inside_sync) sync protocol PTestCancel
+{
+// Test1
+child:
+ nested(inside_sync) sync Test1_1();
+parent:
+ async Done1();
+
+// Test2
+child:
+ async Start2();
+ nested(inside_sync) sync Test2_2();
+parent:
+ sync Test2_1();
+
+// Test3
+child:
+ nested(inside_sync) sync Test3_1();
+parent:
+ async Start3();
+ nested(inside_sync) sync Test3_2();
+
+parent:
+ async Done();
+
+child:
+ nested(inside_sync) sync CheckChild() returns (uint32_t reply);
+parent:
+ nested(inside_sync) sync CheckParent() returns (uint32_t reply);
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl b/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl
new file mode 100644
index 0000000000..eae428211f
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestCrashCleanup.ipdl
@@ -0,0 +1,22 @@
+// See bug 538586: if the top-level protocol's actor is deleted before
+// the "connection error" notification comes in from the IO thread,
+// IPDL teardown never occurs, even if Channel::Close() is called
+// after the error.
+
+namespace mozilla {
+namespace _ipdltest {
+
+// NB: needs to be RPC so that the parent blocks on the child's crash.
+intr protocol PTestCrashCleanup {
+child:
+ intr DIEDIEDIE();
+ async __delete__();
+
+state ALIVE:
+ call DIEDIEDIE goto CRASH;
+state CRASH:
+ send __delete__;
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestDataStructures.ipdl b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
new file mode 100644
index 0000000000..59bf74652f
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
@@ -0,0 +1,132 @@
+include protocol PTestDataStructuresSub;
+include PTestDataStructuresCommon;
+
+include "mozilla/GfxMessageUtils.h";
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestDataStructures {
+ manages PTestDataStructuresSub;
+
+child:
+ async PTestDataStructuresSub(int i);
+
+ async Start();
+
+parent:
+ async __delete__();
+
+ sync Test1(int[] i1)
+ returns (int[] o1);
+
+ sync Test2(PTestDataStructuresSub[] i1)
+ returns (PTestDataStructuresSub[] o1);
+
+ sync Test3(IntDouble i1,
+ IntDouble i2)
+ returns (IntDouble o1,
+ IntDouble o2);
+
+ sync Test4(IntDouble[] i1)
+ returns (IntDouble[] o1);
+
+ sync Test5(IntDoubleArrays i1,
+ IntDoubleArrays i2,
+ IntDoubleArrays i3)
+ returns (IntDoubleArrays o1,
+ IntDoubleArrays o2,
+ IntDoubleArrays o3);
+
+ sync Test6(IntDoubleArrays[] i1)
+ returns (IntDoubleArrays[] o1);
+
+ sync Test7_0(ActorWrapper a1)
+ returns (ActorWrapper o1);
+
+ sync Test7(Actors i1,
+ Actors i2,
+ Actors i3)
+ returns (Actors o1,
+ Actors o2,
+ Actors o3);
+
+ sync Test8(Actors[] i1)
+ returns (Actors[] o1);
+
+ sync Test9(Unions i1,
+ Unions i2,
+ Unions i3,
+ Unions i4)
+ returns (Unions o1,
+ Unions o2,
+ Unions o3,
+ Unions o4);
+
+ sync Test10(Unions[] i1)
+ returns (Unions[] o1);
+
+ sync Test11(SIntDouble i)
+ returns (SIntDouble o);
+
+ sync Test12(SIntDoubleArrays i)
+ returns (SIntDoubleArrays o);
+
+ sync Test13(SActors i)
+ returns (SActors o);
+
+ sync Test14(Structs i)
+ returns (Structs o);
+
+ sync Test15(WithStructs i1,
+ WithStructs i2,
+ WithStructs i3,
+ WithStructs i4,
+ WithStructs i5)
+ returns (WithStructs o1,
+ WithStructs o2,
+ WithStructs o3,
+ WithStructs o4,
+ WithStructs o5);
+
+ sync Test16(WithUnions i)
+ returns (WithUnions o);
+
+ sync Test17(Op[] ops);
+
+ // test that the ParamTraits<nsTArray>::Read() workaround for
+ // nsTArray's incorrect memmove() semantics works properly
+ // (nsIntRegion isn't memmove()able)
+ sync Test18(nsIntRegion[] ops);
+
+ sync Dummy(ShmemUnion su) returns (ShmemUnion rsu);
+
+state CONSTRUCTING:
+ send PTestDataStructuresSub goto CONSTRUCTING;
+ send Start goto TEST1;
+state TEST1: recv Test1 goto TEST2;
+state TEST2: recv Test2 goto TEST3;
+state TEST3: recv Test3 goto TEST4;
+state TEST4: recv Test4 goto TEST5;
+state TEST5: recv Test5 goto TEST6;
+state TEST6: recv Test6 goto TEST7;
+state TEST7: recv Test7 goto TEST8;
+state TEST8: recv Test8 goto TEST9;
+state TEST9: recv Test9 goto TEST10;
+state TEST10: recv Test10 goto TEST11;
+state TEST11: recv Test11 goto TEST12;
+state TEST12: recv Test12 goto TEST13;
+state TEST13: recv Test13 goto TEST14;
+state TEST14: recv Test14 goto TEST15;
+state TEST15: recv Test15 goto TEST16;
+state TEST16: recv Test16 goto TEST17;
+state TEST17: recv Test17 goto TEST18;
+state TEST18: recv Test18 goto DEAD;
+
+state DEAD:
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
diff --git a/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh b/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh
new file mode 100644
index 0000000000..891b0c991d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh
@@ -0,0 +1,107 @@
+include protocol PTestDataStructuresSub;
+
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+using nsIntRegion from "nsRegion.h";
+
+namespace mozilla {
+namespace _foo {
+
+union IntDouble {
+ int;
+ double;
+};
+
+struct SIntDouble {
+ int i;
+ double d;
+};
+
+union IntDoubleArrays {
+ int;
+ int[];
+ double[];
+};
+
+struct SIntDoubleArrays {
+ int i;
+ int[] ai;
+ double[] ad;
+};
+
+struct ActorWrapper {
+ PTestDataStructuresSub actor;
+};
+
+union Actors {
+ int;
+ int[];
+ PTestDataStructuresSub[];
+};
+
+struct SActors {
+ int i;
+ int[] ai;
+ PTestDataStructuresSub[] ap;
+};
+
+union Unions {
+ int;
+ int[];
+ PTestDataStructuresSub[];
+ Actors[];
+};
+
+struct Structs {
+ int i;
+ int[] ai;
+ PTestDataStructuresSub[] ap;
+ SActors[] aa;
+};
+
+union WithStructs {
+ int;
+ int[];
+ PTestDataStructuresSub[];
+ SActors[];
+ Structs[];
+};
+
+struct WithUnions {
+ int i;
+ int[] ai;
+ PTestDataStructuresSub[] ap;
+ Actors[] aa;
+ Unions[] au;
+};
+
+struct CommonAttrs { bool dummy; };
+struct FooAttrs { int dummy; };
+struct BarAttrs { float dummy; };
+union SpecificAttrs {
+ FooAttrs;
+ BarAttrs;
+};
+struct Attrs {
+ CommonAttrs common;
+ SpecificAttrs specific;
+};
+struct SetAttrs {
+ PTestDataStructuresSub x;
+ Attrs attrs;
+};
+union Op { null_t; SetAttrs; };
+
+struct ShmemStruct {
+ int i;
+ Shmem mem;
+};
+
+union ShmemUnion {
+ int;
+ Shmem;
+};
+
+struct Empty { };
+
+} // namespace _foo
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestDataStructuresSub.ipdl b/ipc/ipdl/test/cxx/PTestDataStructuresSub.ipdl
new file mode 100644
index 0000000000..7a4e87d83a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDataStructuresSub.ipdl
@@ -0,0 +1,15 @@
+include PTestDataStructuresCommon;
+include protocol PTestDataStructures;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestDataStructuresSub {
+ manager PTestDataStructures;
+
+parent:
+ sync __delete__();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestDemon.ipdl b/ipc/ipdl/test/cxx/PTestDemon.ipdl
new file mode 100644
index 0000000000..25fa5d0f92
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDemon.ipdl
@@ -0,0 +1,21 @@
+namespace mozilla {
+namespace _ipdltest {
+
+nested(upto inside_cpow) sync protocol PTestDemon
+{
+child:
+ async Start();
+
+both:
+ async AsyncMessage(int n);
+ nested(inside_sync) sync HiPrioSyncMessage();
+
+parent:
+ sync SyncMessage(int n);
+
+ nested(inside_cpow) async UrgentAsyncMessage(int n);
+ nested(inside_cpow) sync UrgentSyncMessage(int n);
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestDesc.ipdl b/ipc/ipdl/test/cxx/PTestDesc.ipdl
new file mode 100644
index 0000000000..835c8c62d2
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDesc.ipdl
@@ -0,0 +1,31 @@
+include protocol PTestDescSub;
+include protocol PTestDescSubsub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestDesc {
+ manages PTestDescSub;
+child:
+ intr PTestDescSub(nullable PTestDescSubsub dummy);
+
+ async Test(PTestDescSubsub a);
+
+ async __delete__();
+
+parent:
+ async Ok(PTestDescSubsub a);
+
+
+state CONSTRUCT:
+ call PTestDescSub goto TEST;
+state TEST:
+ send Test goto ACK;
+state ACK:
+ recv Ok goto DEAD;
+state DEAD:
+ send __delete__;
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestDescSub.ipdl b/ipc/ipdl/test/cxx/PTestDescSub.ipdl
new file mode 100644
index 0000000000..67d4fe1660
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDescSub.ipdl
@@ -0,0 +1,18 @@
+include protocol PTestDesc;
+include protocol PTestDescSubsub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestDescSub {
+ manager PTestDesc;
+ manages PTestDescSubsub;
+
+child:
+ async __delete__();
+
+ intr PTestDescSubsub();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestDescSubsub.ipdl b/ipc/ipdl/test/cxx/PTestDescSubsub.ipdl
new file mode 100644
index 0000000000..c449f27441
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestDescSubsub.ipdl
@@ -0,0 +1,15 @@
+
+include protocol PTestDescSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestDescSubsub {
+ manager PTestDescSub;
+
+child:
+ intr __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestEndpointBridgeMain.ipdl b/ipc/ipdl/test/cxx/PTestEndpointBridgeMain.ipdl
new file mode 100644
index 0000000000..e8026a0d0f
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestEndpointBridgeMain.ipdl
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+include protocol PTestEndpointBridgeMainSub;
+include protocol PTestEndpointBridgeSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestEndpointBridgeMain {
+ child spawns PTestEndpointBridgeSub;
+
+child:
+ async Start();
+
+parent:
+ async Bridged(Endpoint<PTestEndpointBridgeMainSubParent> endpoint);
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestEndpointBridgeMainSub.ipdl b/ipc/ipdl/test/cxx/PTestEndpointBridgeMainSub.ipdl
new file mode 100644
index 0000000000..7364057acc
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestEndpointBridgeMainSub.ipdl
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+include protocol PTestEndpointBridgeMain;
+include protocol PTestEndpointBridgeSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+// (Bridge protocols can have different semantics than the endpoints
+// they bridge)
+intr protocol PTestEndpointBridgeMainSub {
+child:
+ async Hi();
+ intr HiRpc();
+
+parent:
+ async Hello();
+ sync HelloSync();
+ intr HelloRpc();
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestEndpointBridgeSub.ipdl b/ipc/ipdl/test/cxx/PTestEndpointBridgeSub.ipdl
new file mode 100644
index 0000000000..0bc09b70e1
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestEndpointBridgeSub.ipdl
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+include protocol PTestEndpointBridgeMainSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestEndpointBridgeSub {
+child:
+ async Ping();
+
+ async Bridged(Endpoint<PTestEndpointBridgeMainSubChild> endpoint);
+
+parent:
+ async BridgeEm();
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestEndpointOpens.ipdl b/ipc/ipdl/test/cxx/PTestEndpointOpens.ipdl
new file mode 100644
index 0000000000..7be99ddd2b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestEndpointOpens.ipdl
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+include protocol PTestEndpointOpensOpened;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestEndpointOpens {
+child:
+ async Start();
+
+parent:
+ async StartSubprotocol(Endpoint<PTestEndpointOpensOpenedParent> endpoint);
+
+ async __delete__();
+};
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl b/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl
new file mode 100644
index 0000000000..e14c0cb8a6
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestEndpointOpensOpened.ipdl
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+namespace mozilla {
+namespace _ipdltest2 {
+
+// (Opens protocols can have different semantics than the endpoints
+// that opened them)
+intr protocol PTestEndpointOpensOpened {
+child:
+ async Hi();
+ intr HiRpc();
+
+parent:
+ async Hello();
+ sync HelloSync();
+ intr HelloRpc();
+ async __delete__();
+
+state START: recv Hello goto HI;
+state HI: send Hi goto HELLO_SYNC;
+state HELLO_SYNC: recv HelloSync goto HELLO_RPC;
+state HELLO_RPC: answer HelloRpc goto HI_RPC;
+state HI_RPC: call HiRpc goto DEAD;
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest2
diff --git a/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl b/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl
new file mode 100644
index 0000000000..94b9da0811
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestFailedCtor.ipdl
@@ -0,0 +1,19 @@
+include protocol PTestFailedCtorSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestFailedCtor {
+ manages PTestFailedCtorSub;
+child:
+ intr PTestFailedCtorSub();
+ async __delete__();
+
+state CONSTRUCT:
+ call PTestFailedCtorSub goto DEAD;
+state DEAD:
+ send __delete__;
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl b/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl
new file mode 100644
index 0000000000..b1d18a05fd
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestFailedCtorSub.ipdl
@@ -0,0 +1,18 @@
+include protocol PTestFailedCtor;
+include protocol PTestFailedCtorSubsub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestFailedCtorSub {
+ manager PTestFailedCtor;
+ manages PTestFailedCtorSubsub;
+
+parent:
+ async PTestFailedCtorSubsub();
+ sync Sync();
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestFailedCtorSubsub.ipdl b/ipc/ipdl/test/cxx/PTestFailedCtorSubsub.ipdl
new file mode 100644
index 0000000000..654170d976
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestFailedCtorSubsub.ipdl
@@ -0,0 +1,15 @@
+
+include protocol PTestFailedCtorSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestFailedCtorSubsub {
+ manager PTestFailedCtorSub;
+
+parent:
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestHandle.ipdl b/ipc/ipdl/test/cxx/PTestHandle.ipdl
new file mode 100644
index 0000000000..aad92bae18
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestHandle.ipdl
@@ -0,0 +1,14 @@
+include protocol PTestJSON;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestHandle {
+ manager PTestJSON;
+
+child:
+ async __delete__();
+};
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestHangs.ipdl b/ipc/ipdl/test/cxx/PTestHangs.ipdl
new file mode 100644
index 0000000000..b1b43fa754
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestHangs.ipdl
@@ -0,0 +1,40 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestHangs {
+both:
+ intr StackFrame();
+
+parent:
+ async Nonce();
+
+child:
+ async Start();
+ intr Hang();
+ async __delete__();
+
+
+state START:
+ send Start goto RACE;
+
+state RACE:
+ recv Nonce goto RACE1;
+ call StackFrame goto RACE2;
+state RACE1:
+ call StackFrame goto FRAME2;
+state RACE2:
+ recv Nonce goto FRAME2;
+
+// So as to test unwinding the RPC stack
+state FRAME2: answer StackFrame goto FRAME3;
+state FRAME3: call StackFrame goto FRAME4;
+state FRAME4: answer StackFrame goto HANG;
+state HANG: call Hang goto DEATH;
+
+state DEATH:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestHighestPrio.ipdl b/ipc/ipdl/test/cxx/PTestHighestPrio.ipdl
new file mode 100644
index 0000000000..0192f59b21
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestHighestPrio.ipdl
@@ -0,0 +1,18 @@
+namespace mozilla {
+namespace _ipdltest {
+
+nested(upto inside_cpow) sync protocol PTestHighestPrio
+{
+parent:
+ nested(inside_cpow) async Msg1();
+ nested(inside_sync) sync Msg2();
+ nested(inside_cpow) async Msg3();
+ nested(inside_cpow) sync Msg4();
+
+child:
+ async Start();
+ nested(inside_sync) sync StartInner();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestIndirectProtocolParam.ipdlh b/ipc/ipdl/test/cxx/PTestIndirectProtocolParam.ipdlh
new file mode 100644
index 0000000000..a81fcdee46
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestIndirectProtocolParam.ipdlh
@@ -0,0 +1,15 @@
+include protocol PTestIndirectProtocolParamSecond;
+
+namespace mozilla {
+namespace _ipdltest {
+
+struct IndirectParamStruct {
+ PTestIndirectProtocolParamSecond actor;
+};
+
+union IndirectParamUnion {
+ IndirectParamStruct;
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestIndirectProtocolParamFirst.ipdl b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamFirst.ipdl
new file mode 100644
index 0000000000..228ec04e88
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamFirst.ipdl
@@ -0,0 +1,19 @@
+include protocol PTestIndirectProtocolParamManage;
+// FIXME/bug 792908 protocol PTestIndirectProtocolParamSecond is
+// already included in PTestIndirectProtocolParam.ipdlh
+include protocol PTestIndirectProtocolParamSecond;
+include PTestIndirectProtocolParam;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestIndirectProtocolParamFirst {
+ manager PTestIndirectProtocolParamManage;
+parent:
+ sync Test(IndirectParamUnion actor);
+both:
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestIndirectProtocolParamManage.ipdl b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamManage.ipdl
new file mode 100644
index 0000000000..db7c828a22
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamManage.ipdl
@@ -0,0 +1,17 @@
+include protocol PTestIndirectProtocolParamFirst;
+include protocol PTestIndirectProtocolParamSecond;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestIndirectProtocolParamManage {
+ manages PTestIndirectProtocolParamFirst;
+ manages PTestIndirectProtocolParamSecond;
+both:
+ async PTestIndirectProtocolParamFirst();
+ async PTestIndirectProtocolParamSecond();
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestIndirectProtocolParamSecond.ipdl b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamSecond.ipdl
new file mode 100644
index 0000000000..ed21f58f88
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestIndirectProtocolParamSecond.ipdl
@@ -0,0 +1,13 @@
+include protocol PTestIndirectProtocolParamManage;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestIndirectProtocolParamSecond {
+ manager PTestIndirectProtocolParamManage;
+both:
+ async __delete__();
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestInterruptErrorCleanup.ipdl b/ipc/ipdl/test/cxx/PTestInterruptErrorCleanup.ipdl
new file mode 100644
index 0000000000..95f933bba7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestInterruptErrorCleanup.ipdl
@@ -0,0 +1,11 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestInterruptErrorCleanup {
+child:
+ intr Error();
+ intr __delete__();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl b/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl
new file mode 100644
index 0000000000..bf71a251c7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestInterruptRaces.ipdl
@@ -0,0 +1,82 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestInterruptRaces {
+both:
+ intr Race() returns (bool hasReply);
+ intr StackFrame() returns ();
+ intr StackFrame3() returns ();
+
+parent:
+ sync StartRace();
+ intr Parent();
+ sync GetAnsweredParent() returns (bool answeredParent);
+
+child:
+ async Start();
+ async Wakeup();
+ async Wakeup3();
+ intr Child();
+ async __delete__();
+
+state START:
+ send Start goto TEST1;
+
+// First test: race while no other messages are on the Interrupt stack
+state TEST1:
+ recv StartRace goto RACE1;
+state RACE1:
+ call Race goto DUMMY1_1;
+ answer Race goto DUMMY1_2;
+state DUMMY1_1:
+ answer Race goto TEST2;
+state DUMMY1_2:
+ call Race goto TEST2;
+
+// Second test: race while other messages are on the Interrupt stack
+state TEST2:
+ call StackFrame goto MORESTACK;
+state MORESTACK:
+ answer StackFrame goto STARTRACE;
+state STARTRACE:
+ send Wakeup goto RACE2;
+state RACE2:
+ call Race goto DUMMY2_1;
+ answer Race goto DUMMY2_2;
+state DUMMY2_1:
+ answer Race goto TEST3;
+state DUMMY2_2:
+ call Race goto TEST3;
+
+// Third test: resolve race using custom policy
+state TEST3:
+ call StackFrame3 goto MORESTACK3;
+state MORESTACK3:
+ answer StackFrame3 goto STARTRACE3;
+state STARTRACE3:
+ send Wakeup3 goto RACE3;
+state RACE3:
+ call Child goto DUMMY3_1;
+ answer Parent goto DUMMY3_2;
+state DUMMY3_1:
+ // the parent receives this from the child in this state
+ recv GetAnsweredParent goto CHECK;
+ // this transition is never taken (if the custom race resolution
+ // works correctly)
+ answer Parent goto CHECK;
+state DUMMY3_2:
+ call Child goto CHECK;
+state CHECK:
+ // the child sends this from this state
+ recv GetAnsweredParent goto DYING;
+ // because of deferred processing, the parent receives the child's
+ // message here
+ answer Parent goto DYING;
+
+
+state DYING:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl b/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl
new file mode 100644
index 0000000000..10b377487a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestInterruptShutdownRace.ipdl
@@ -0,0 +1,34 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestInterruptShutdownRace {
+parent:
+ sync StartDeath();
+ async Orphan();
+
+child:
+ async Start();
+ intr Exit();
+ async __delete__();
+
+state START:
+ send Start goto START_DEATH;
+
+state START_DEATH:
+ recv StartDeath goto EXITING;
+
+state EXITING:
+ recv Orphan goto QUITTING1;
+ call Exit goto QUITTING2;
+
+state QUITTING1:
+ call Exit goto DEAD;
+state QUITTING2:
+ recv Orphan goto DEAD;
+
+state DEAD:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestJSON.ipdl b/ipc/ipdl/test/cxx/PTestJSON.ipdl
new file mode 100644
index 0000000000..6e8f3c1dea
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestJSON.ipdl
@@ -0,0 +1,54 @@
+include protocol PTestHandle;
+
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace _ipdltest {
+
+union Key {
+// int;
+// double;
+ nsString;
+};
+
+struct KeyValue {
+ Key key;
+ JSONVariant value;
+};
+
+union JSONVariant {
+ void_t;
+ null_t;
+ bool;
+ int;
+ double;
+ nsString;
+ PTestHandle;
+ KeyValue[];
+ JSONVariant[];
+};
+
+sync protocol PTestJSON {
+ manages PTestHandle;
+
+child:
+ async Start();
+
+parent:
+ async PTestHandle();
+ sync Test(JSONVariant i)
+ returns (JSONVariant o);
+ async __delete__();
+
+state START:
+ send Start goto TEST;
+
+state TEST:
+ recv PTestHandle goto TEST;
+ recv Test goto TEST;
+ recv __delete__;
+};
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestLatency.ipdl b/ipc/ipdl/test/cxx/PTestLatency.ipdl
new file mode 100644
index 0000000000..d0c9750d8e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestLatency.ipdl
@@ -0,0 +1,75 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+intr protocol PTestLatency {
+
+child:
+ async __delete__();
+ async Ping();
+ async Ping5();
+ intr Rpc();
+ async Spam();
+ intr Synchro();
+ async CompressedSpam(uint32_t seqno) compress;
+ intr Synchro2() returns (uint32_t lastSeqno,
+ uint32_t numMessagesDispatched);
+
+parent:
+ async Pong();
+ async Pong5();
+
+state START:
+ // if the timing resolution is too low, abort the test
+ send __delete__;
+ // otherwise, kick off the ping/pong trials
+ send Ping goto PONG;
+
+ // Trial 1: single ping/pong latency
+state PING:
+ send Ping goto PONG;
+ send Ping5 goto PING4;
+
+state PONG:
+ recv Pong goto PING;
+
+ // Trial 2: "overlapped" ping/pong latency
+state PING5:
+ send Ping5 goto PING4;
+ call Rpc goto RPC;
+
+state PING4: send Ping5 goto PING3;
+state PING3: send Ping5 goto PING2;
+state PING2: send Ping5 goto PING1;
+state PING1: send Ping5 goto PONG1;
+
+state PONG1: recv Pong5 goto PONG2;
+state PONG2: recv Pong5 goto PONG3;
+state PONG3: recv Pong5 goto PONG4;
+state PONG4: recv Pong5 goto PONG5;
+state PONG5: recv Pong5 goto PING5;
+
+ // Trial 3: lotsa RPC
+state RPC:
+ call Rpc goto RPC;
+ send Spam goto SPAM;
+
+ // Trial 4: lots of sequential asyn messages, which tests pipelining
+state SPAM:
+ send Spam goto SPAM;
+ call Synchro goto COMPRESSED_SPAM;
+
+ // Trial 5: lots of async spam, but compressed to cut down on
+ // dispatch overhead
+state COMPRESSED_SPAM: // compressed spam, mmm
+ send CompressedSpam goto COMPRESSED_SPAM;
+ call Synchro2 goto DONE;
+
+state DONE:
+ send __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl b/ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl
new file mode 100644
index 0000000000..767af85a20
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestManyChildAllocs.ipdl
@@ -0,0 +1,19 @@
+include protocol PTestManyChildAllocsSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestManyChildAllocs {
+ manages PTestManyChildAllocsSub;
+
+child:
+ async Go(); // start allocating
+
+parent:
+ async Done();
+
+ async PTestManyChildAllocsSub();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl b/ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl
new file mode 100644
index 0000000000..e3d9c98df9
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestManyChildAllocsSub.ipdl
@@ -0,0 +1,19 @@
+include protocol PTestManyChildAllocs;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestManyChildAllocsSub {
+ manager PTestManyChildAllocs;
+
+child:
+ async __delete__();
+
+parent:
+ async Hello();
+
+ // empty
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl b/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl
new file mode 100644
index 0000000000..6322bb1a3e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrs.ipdl
@@ -0,0 +1,34 @@
+include protocol PTestMultiMgrsLeft;
+include protocol PTestMultiMgrsRight;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestMultiMgrs {
+ manages PTestMultiMgrsLeft;
+ manages PTestMultiMgrsRight;
+
+parent:
+ async OK();
+
+child:
+ async PTestMultiMgrsLeft();
+ async PTestMultiMgrsRight();
+ async Check();
+ async __delete__();
+
+state START:
+ send PTestMultiMgrsLeft goto CONSTRUCT_RIGHT;
+state CONSTRUCT_RIGHT:
+ send PTestMultiMgrsRight goto CHILD_CHECK;
+state CHILD_CHECK:
+ send Check goto CHILD_ACK;
+state CHILD_ACK:
+ recv OK goto DONE;
+
+state DONE:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl b/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl
new file mode 100644
index 0000000000..9c474f446f
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrsBottom.ipdl
@@ -0,0 +1,18 @@
+include protocol PTestMultiMgrsLeft;
+include protocol PTestMultiMgrsRight;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestMultiMgrsBottom {
+ manager PTestMultiMgrsLeft or PTestMultiMgrsRight;
+
+child:
+ async __delete__();
+
+state DOA:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl b/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl
new file mode 100644
index 0000000000..8301762724
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrsLeft.ipdl
@@ -0,0 +1,24 @@
+include protocol PTestMultiMgrs;
+include protocol PTestMultiMgrsBottom;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestMultiMgrsLeft {
+ manager PTestMultiMgrs;
+
+ manages PTestMultiMgrsBottom;
+
+child:
+ async PTestMultiMgrsBottom();
+ async __delete__();
+
+state START:
+ send PTestMultiMgrsBottom goto DONE;
+
+state DONE:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl b/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl
new file mode 100644
index 0000000000..36b0771b77
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestMultiMgrsRight.ipdl
@@ -0,0 +1,24 @@
+include protocol PTestMultiMgrs;
+include protocol PTestMultiMgrsBottom;
+
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestMultiMgrsRight {
+ manager PTestMultiMgrs;
+
+ manages PTestMultiMgrsBottom;
+
+child:
+ async PTestMultiMgrsBottom();
+ async __delete__();
+
+state START:
+ send PTestMultiMgrsBottom goto DONE;
+
+state DONE:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl b/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl
new file mode 100644
index 0000000000..f0f067e0b2
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestNestedLoops.ipdl
@@ -0,0 +1,34 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+intr protocol PTestNestedLoops {
+
+child:
+ async Start();
+ intr R();
+ async __delete__();
+
+parent:
+ async Nonce();
+
+
+state START:
+ send Start goto RACE;
+
+state RACE:
+ recv Nonce goto RACE1;
+ call R goto RACE2;
+state RACE1:
+ call R goto DEAD;
+state RACE2:
+ recv Nonce goto DEAD;
+
+state DEAD:
+ send __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestOpens.ipdl b/ipc/ipdl/test/cxx/PTestOpens.ipdl
new file mode 100644
index 0000000000..2717328faa
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestOpens.ipdl
@@ -0,0 +1,25 @@
+include protocol PTestOpensOpened;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestOpens {
+ // This channel is opened and parked on a non-main thread
+ child opens PTestOpensOpened;
+
+child:
+ async Start();
+
+parent:
+ async __delete__();
+
+state START:
+ send Start goto DEAD;
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestOpensOpened.ipdl b/ipc/ipdl/test/cxx/PTestOpensOpened.ipdl
new file mode 100644
index 0000000000..04b633634a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestOpensOpened.ipdl
@@ -0,0 +1,28 @@
+namespace mozilla {
+namespace _ipdltest2 {
+
+// (Opens protocols can have different semantics than the endpoints
+// that opened them)
+intr protocol PTestOpensOpened {
+child:
+ async Hi();
+ intr HiRpc();
+
+parent:
+ async Hello();
+ sync HelloSync();
+ intr HelloRpc();
+ async __delete__();
+
+state START: recv Hello goto HI;
+state HI: send Hi goto HELLO_SYNC;
+state HELLO_SYNC: recv HelloSync goto HELLO_RPC;
+state HELLO_RPC: answer HelloRpc goto HI_RPC;
+state HI_RPC: call HiRpc goto DEAD;
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest2
diff --git a/ipc/ipdl/test/cxx/PTestPriority.ipdl b/ipc/ipdl/test/cxx/PTestPriority.ipdl
new file mode 100644
index 0000000000..edb98365b7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestPriority.ipdl
@@ -0,0 +1,14 @@
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestPriority {
+parent:
+ prio(high) async Msg1();
+ prio(high) sync Msg2();
+
+child:
+ prio(high) async Msg3();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestRPC.ipdl b/ipc/ipdl/test/cxx/PTestRPC.ipdl
new file mode 100644
index 0000000000..f51ee2735d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRPC.ipdl
@@ -0,0 +1,21 @@
+namespace mozilla {
+namespace _ipdltest {
+
+nested(upto inside_sync) sync protocol PTestRPC
+{
+parent:
+ nested(inside_sync) sync Test1_Start() returns (uint32_t result);
+ nested(inside_sync) sync Test1_InnerEvent() returns (uint32_t result);
+ async Test2_Start();
+ nested(inside_sync) sync Test2_OutOfOrder();
+
+child:
+ async Start();
+ nested(inside_sync) sync Test1_InnerQuery() returns (uint32_t result);
+ nested(inside_sync) sync Test1_NoReenter() returns (uint32_t result);
+ nested(inside_sync) sync Test2_FirstUrgent();
+ nested(inside_sync) sync Test2_SecondUrgent();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestRaceDeadlock.ipdl b/ipc/ipdl/test/cxx/PTestRaceDeadlock.ipdl
new file mode 100644
index 0000000000..1e4f574509
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRaceDeadlock.ipdl
@@ -0,0 +1,20 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestRaceDeadlock {
+both:
+ async StartRace();
+
+parent:
+ intr Lose();
+
+child:
+ intr Win();
+ intr Rpc();
+ async __delete__();
+
+/* Tests that race resolution does not cause deadlocks */
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl b/ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl
new file mode 100644
index 0000000000..ada6df4c6d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRaceDeferral.ipdl
@@ -0,0 +1,45 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestRaceDeferral {
+parent:
+ intr Lose();
+
+child:
+ async StartRace();
+ intr Win();
+ intr Rpc();
+ async __delete__();
+
+// Test that messages deferred due to race resolution are
+// re-considered when the winner makes later RPCs
+
+// IPDL's type system can't express this protocol because the race
+// resolution causes state to diverge for multiple steps, so we'll
+// leave it "stateless"
+/*
+state START:
+ send StartRace goto DEFER;
+state DEFER:
+ call Win goto PARENT;
+ answer Lose goto CHILD;
+
+state PARENT:
+ // 'Lose' is received here but deferred
+ call Rpc goto PARENT_LOSE;
+state PARENT_LOSE:
+ // Calling 'Rpc' undefers 'Lose', and it wins the "race" with 'Rpc'
+ answer Lose goto DONE;
+
+state CHILD:
+ call Win goto CHILD_RPC;
+state CHILD_RPC:
+ call Rpc goto DONE;
+
+state DONE:
+ send __delete__;
+*/
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl b/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl
new file mode 100644
index 0000000000..56e700b85c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRacyInterruptReplies.ipdl
@@ -0,0 +1,41 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestRacyInterruptReplies {
+child:
+ intr R_() returns (int replyNum);
+ async _A();
+ async ChildTest();
+ async __delete__();
+
+parent:
+ intr _R() returns (int replyNum);
+ async A_();
+
+state PARENT_START:
+ call R_ goto PARENT_S1;
+
+state PARENT_S1:
+ recv A_ goto PARENT_S2;
+
+state PARENT_S2:
+ call R_ goto CHILD_TEST;
+
+state CHILD_TEST:
+ send ChildTest goto CHILD_START;
+
+state CHILD_START:
+ answer _R goto CHILD_S1;
+
+state CHILD_S1:
+ send _A goto CHILD_S2;
+
+state CHILD_S2:
+ answer _R goto DYING;
+
+state DYING:
+ send __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestRacyReentry.ipdl b/ipc/ipdl/test/cxx/PTestRacyReentry.ipdl
new file mode 100644
index 0000000000..4dd5fe54fb
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRacyReentry.ipdl
@@ -0,0 +1,21 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+intr protocol PTestRacyReentry {
+
+parent:
+ intr E();
+ async __delete__();
+
+child:
+ async Start();
+
+ async N();
+ intr H();
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestRacyUndefer.ipdl b/ipc/ipdl/test/cxx/PTestRacyUndefer.ipdl
new file mode 100644
index 0000000000..8863d61795
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestRacyUndefer.ipdl
@@ -0,0 +1,28 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+intr protocol PTestRacyUndefer {
+
+child:
+ async Start();
+
+ async AwakenSpam();
+ async AwakenRaceWinTwice();
+
+ intr Race();
+
+ async __delete__();
+
+parent:
+
+ intr Spam();
+ intr RaceWinTwice();
+
+ async Done();
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestSanity.ipdl b/ipc/ipdl/test/cxx/PTestSanity.ipdl
new file mode 100644
index 0000000000..50249b7414
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSanity.ipdl
@@ -0,0 +1,28 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestSanity {
+
+child:
+ async Ping(int zero, float zeroPtFive, int8_t dummy);
+ async __delete__();
+
+parent:
+ async Pong(int one, float zeroPtTwoFive, uint8_t dummy);
+
+
+state PING:
+ send Ping goto PONG;
+
+state PONG:
+ recv Pong goto DEAD;
+
+state DEAD:
+ send __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestSelfManage.ipdl b/ipc/ipdl/test/cxx/PTestSelfManage.ipdl
new file mode 100644
index 0000000000..69eb4f55c5
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSelfManage.ipdl
@@ -0,0 +1,22 @@
+include protocol PTestSelfManageRoot;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestSelfManage {
+ manager PTestSelfManageRoot or PTestSelfManage;
+ manages PTestSelfManage;
+
+child:
+ async PTestSelfManage();
+ async __delete__();
+
+state LIVE:
+ send PTestSelfManage goto LIVE;
+ send __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl b/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl
new file mode 100644
index 0000000000..429f8bfc80
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSelfManageRoot.ipdl
@@ -0,0 +1,23 @@
+include protocol PTestSelfManage;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestSelfManageRoot {
+ manages PTestSelfManage;
+
+child:
+ async PTestSelfManage();
+ async __delete__();
+
+state LIVE:
+ send PTestSelfManage goto DEAD;
+
+state DEAD:
+ send __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestShmem.ipdl b/ipc/ipdl/test/cxx/PTestShmem.ipdl
new file mode 100644
index 0000000000..46a1958e28
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShmem.ipdl
@@ -0,0 +1,22 @@
+namespace mozilla {
+namespace _ipdltest {
+
+protocol PTestShmem {
+child:
+ async Give(Shmem mem, Shmem unsafe, size_t expectedSize);
+
+parent:
+ async Take(Shmem mem, Shmem unsafe, size_t expectedSize);
+ async __delete__();
+
+
+state GIVING:
+ send Give goto TAKING;
+
+state TAKING:
+ recv Take goto TAKING;
+ recv __delete__;
+};
+
+}
+}
diff --git a/ipc/ipdl/test/cxx/PTestShutdown.ipdl b/ipc/ipdl/test/cxx/PTestShutdown.ipdl
new file mode 100644
index 0000000000..e4d8d0dbeb
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShutdown.ipdl
@@ -0,0 +1,37 @@
+include protocol PTestShutdownSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestShutdown {
+ manages PTestShutdownSub;
+
+child:
+ async Start();
+
+parent:
+ // NB: we test deletion and crashing only, not shutdown, because
+ // crashing is the same code path as shutdown, and other IPDL unit
+ // tests check shutdown semantics
+ async PTestShutdownSub(bool expectCrash);
+
+ // Used to synchronize between parent and child, to avoid races
+ // around flushing socket write queues
+ sync Sync();
+
+ async __delete__();
+
+
+state START:
+ send Start goto TESTING;
+
+state TESTING:
+ recv PTestShutdownSub goto TESTING;
+ recv Sync goto DYING;
+
+state DYING:
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl b/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl
new file mode 100644
index 0000000000..39d45d9ed8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShutdownSub.ipdl
@@ -0,0 +1,30 @@
+include protocol PTestShutdown;
+include protocol PTestShutdownSubsub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestShutdownSub {
+ manager PTestShutdown;
+ manages PTestShutdownSubsub;
+
+both:
+ intr StackFrame();
+
+parent:
+ async PTestShutdownSubsub(bool expectParentDeleted);
+ sync __delete__();
+
+state CREATING:
+ recv PTestShutdownSubsub goto CREATING;
+ answer StackFrame goto DUMMYFRAME;
+
+state DUMMYFRAME:
+ call StackFrame goto DEAD;
+
+state DEAD:
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl b/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl
new file mode 100644
index 0000000000..91031ab413
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestShutdownSubsub.ipdl
@@ -0,0 +1,17 @@
+include protocol PTestShutdownSub;
+
+namespace mozilla {
+namespace _ipdltest {
+
+sync protocol PTestShutdownSubsub {
+ manager PTestShutdownSub;
+
+parent:
+ sync __delete__();
+
+state LIVE:
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestStackHooks.ipdl b/ipc/ipdl/test/cxx/PTestStackHooks.ipdl
new file mode 100644
index 0000000000..1f3af068fb
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestStackHooks.ipdl
@@ -0,0 +1,56 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+intr protocol PTestStackHooks {
+child:
+ async Start();
+
+ // These tests are more fruitful running child->parent, because
+ // children can send |sync| messages
+parent:
+ async Async();
+ sync Sync();
+ intr Rpc();
+
+both:
+ intr StackFrame();
+
+parent:
+ async __delete__();
+
+
+state START:
+ send Start goto TEST1;
+
+state TEST1:
+ recv Async goto TEST2;
+
+state TEST2:
+ recv Sync goto TEST3;
+
+state TEST3:
+ answer Rpc goto TEST4;
+
+state TEST4:
+ answer StackFrame goto TEST4_2;
+state TEST4_2:
+ call StackFrame goto TEST4_3;
+state TEST4_3:
+ recv Async goto TEST5;
+
+state TEST5:
+ answer StackFrame goto TEST5_2;
+state TEST5_2:
+ call StackFrame goto TEST5_3;
+state TEST5_3:
+ recv Sync goto DEAD;
+
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestSyncError.ipdl b/ipc/ipdl/test/cxx/PTestSyncError.ipdl
new file mode 100644
index 0000000000..05b68ff350
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSyncError.ipdl
@@ -0,0 +1,28 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+sync protocol PTestSyncError {
+
+child:
+ async Start();
+
+parent:
+ sync Error();
+ async __delete__();
+
+
+state START:
+ send Start goto SYNC_ERROR;
+
+state SYNC_ERROR:
+ recv Error goto DEAD;
+
+state DEAD:
+ recv __delete__;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestSyncHang.ipdl b/ipc/ipdl/test/cxx/PTestSyncHang.ipdl
new file mode 100644
index 0000000000..eefcf2c5e9
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSyncHang.ipdl
@@ -0,0 +1,15 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestSyncHang {
+
+child:
+ async __delete__();
+
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl b/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl
new file mode 100644
index 0000000000..3d873cdf54
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSyncWakeup.ipdl
@@ -0,0 +1,41 @@
+namespace mozilla {
+namespace _ipdltest {
+
+intr protocol PTestSyncWakeup {
+both:
+ intr StackFrame();
+
+child:
+ async Start();
+ async Note1();
+ async Note2();
+
+parent:
+ sync Sync1();
+ sync Sync2();
+ async __delete__();
+
+
+state START:
+ send Start goto TEST1;
+
+state TEST1:
+ recv Sync1 goto TEST1_P2;
+state TEST1_P2:
+ send Note1 goto TEST2;
+
+state TEST2:
+ answer StackFrame goto TEST2_P2;
+state TEST2_P2:
+ call StackFrame goto TEST2_P3;
+state TEST2_P3:
+ recv Sync2 goto TEST2_P4;
+state TEST2_P4:
+ send Note2 goto DONE;
+
+state DONE:
+ recv __delete__;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestUrgency.ipdl b/ipc/ipdl/test/cxx/PTestUrgency.ipdl
new file mode 100644
index 0000000000..79b479ca2e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestUrgency.ipdl
@@ -0,0 +1,19 @@
+namespace mozilla {
+namespace _ipdltest {
+
+nested(upto inside_cpow) sync protocol PTestUrgency
+{
+parent:
+ nested(inside_sync) sync Test1() returns (uint32_t result);
+ async Test2();
+ sync Test3() returns (uint32_t result);
+ sync FinalTest_Begin();
+
+child:
+ async Start();
+ nested(inside_sync) sync Reply1() returns (uint32_t result);
+ nested(inside_sync) sync Reply2() returns (uint32_t result);
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/PTestUrgentHangs.ipdl b/ipc/ipdl/test/cxx/PTestUrgentHangs.ipdl
new file mode 100644
index 0000000000..8ce8d17473
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestUrgentHangs.ipdl
@@ -0,0 +1,28 @@
+namespace mozilla {
+namespace _ipdltest {
+
+nested(upto inside_cpow) sync protocol PTestUrgentHangs
+{
+parent:
+ nested(inside_sync) sync Test1_2();
+
+ nested(inside_sync) sync TestInner();
+ nested(inside_cpow) sync TestInnerUrgent();
+
+child:
+ nested(inside_sync) sync Test1_1();
+ nested(inside_sync) sync Test1_3();
+
+ nested(inside_sync) sync Test2();
+
+ nested(inside_sync) sync Test3();
+
+ async Test4();
+ nested(inside_sync) sync Test4_1();
+
+ async Test5();
+ nested(inside_sync) sync Test5_1();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/README.txt b/ipc/ipdl/test/cxx/README.txt
new file mode 100644
index 0000000000..0fe6c07320
--- /dev/null
+++ b/ipc/ipdl/test/cxx/README.txt
@@ -0,0 +1,61 @@
+To add a new IPDL C++ unit test, you need to create (at least) the
+following files (for a test "TestFoo"):
+
+ - PTestFoo.ipdl, specifying the top-level protocol used for the test
+
+ - TestFoo.h, declaring the top-level parent/child actors used for
+ the test
+
+ - TestFoo.cpp, defining the top-level actors
+
+ - (make sure all are in the namespace mozilla::_ipdltest)
+
+Next
+
+ - add PTestFoo.ipdl to ipdl.mk
+
+ - append TestFoo to the variable IPDLTESTS in Makefile.in
+
+You must define three methods in your |TestFooParent| class:
+
+ - static methods |bool RunTestInProcesses()| and
+ |bool RunTestInThreads()|. These methods control whether
+ to execute the test using actors in separate processes and
+ threads respectively. Generally, both should return true.
+
+ - an instance method |void Main()|. The test harness wil first
+ initialize the processes or threads, create and open both actors,
+ and then kick off the test using |Main()|. Make sure you define
+ it.
+
+If your test passes its criteria, please call
+|MOZ_IPDL_TESTPASS("msg")| and "exit gracefully".
+
+If your tests fails, please call |MOZ_IPDL_TESTFAIL("msg")| and "exit
+ungracefully", preferably by abort()ing.
+
+
+If all goes well, running
+
+ make -C $OBJDIR/ipc/ipdl/test/cxx
+
+will update the file IPDLUnitTests.cpp (the test launcher), and your
+new code will be built automatically.
+
+
+You can launch your new test by invoking one of
+
+ make -C $OBJDIR/ipc/ipdl/test/cxx check-proc (test process-based tests)
+ make -C $OBJDIR/ipc/ipdl/test/cxx check-threads (test thread-based tests)
+ make -C $OBJDIR/ipc/ipdl/test/cxx check (tests both)
+
+If you want to launch only your test, run
+
+ cd $OBJDIR/dist/bin
+ ./run-mozilla.sh ./ipdlunittest TestFoo (test in two processes, if appl.)
+ ./run-mozilla.sh ./ipdlunittest thread:TestFoo (test in two threads, if appl.)
+
+
+For a bare-bones example of adding a test, take a look at
+PTestSanity.ipdl, TestSanity.h, TestSanity.cpp, and how "TestSanity"
+is included in ipdl.mk and Makefile.in.
diff --git a/ipc/ipdl/test/cxx/TestActorPunning.cpp b/ipc/ipdl/test/cxx/TestActorPunning.cpp
new file mode 100644
index 0000000000..eb6fa412fc
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestActorPunning.cpp
@@ -0,0 +1,128 @@
+#include "TestActorPunning.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+void
+TestActorPunningParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestActorPunningParent::RecvPun(PTestActorPunningSubParent* a, const Bad& bad)
+{
+ if (a->SendBad())
+ fail("bad!");
+ fail("shouldn't have received this message in the first place");
+ return true;
+}
+
+PTestActorPunningPunnedParent*
+TestActorPunningParent::AllocPTestActorPunningPunnedParent()
+{
+ return new TestActorPunningPunnedParent();
+}
+
+bool
+TestActorPunningParent::DeallocPTestActorPunningPunnedParent(PTestActorPunningPunnedParent* a)
+{
+ delete a;
+ return true;
+}
+
+PTestActorPunningSubParent*
+TestActorPunningParent::AllocPTestActorPunningSubParent()
+{
+ return new TestActorPunningSubParent();
+}
+
+bool
+TestActorPunningParent::DeallocPTestActorPunningSubParent(PTestActorPunningSubParent* a)
+{
+ delete a;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+PTestActorPunningPunnedChild*
+TestActorPunningChild::AllocPTestActorPunningPunnedChild()
+{
+ return new TestActorPunningPunnedChild();
+}
+
+bool
+TestActorPunningChild::DeallocPTestActorPunningPunnedChild(PTestActorPunningPunnedChild*)
+{
+ fail("should have died by now");
+ return true;
+}
+
+PTestActorPunningSubChild*
+TestActorPunningChild::AllocPTestActorPunningSubChild()
+{
+ return new TestActorPunningSubChild();
+}
+
+bool
+TestActorPunningChild::DeallocPTestActorPunningSubChild(PTestActorPunningSubChild*)
+{
+ fail("should have died by now");
+ return true;
+}
+
+bool
+TestActorPunningChild::RecvStart()
+{
+ SendPTestActorPunningSubConstructor();
+ SendPTestActorPunningPunnedConstructor();
+ PTestActorPunningSubChild* a = SendPTestActorPunningSubConstructor();
+ // We can't assert whether this succeeds or fails, due to race
+ // conditions.
+ SendPun(a, Bad());
+ return true;
+}
+
+bool
+TestActorPunningSubChild::RecvBad()
+{
+ fail("things are going really badly right now");
+ return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+namespace IPC {
+using namespace mozilla::_ipdltest;
+using namespace mozilla::ipc;
+
+/*static*/ void
+ParamTraits<Bad>::Write(Message* aMsg, const paramType& aParam)
+{
+ // Skip past the sentinel for the actor as well as the actor.
+ int32_t* ptr = aMsg->GetInt32PtrForTest(2 * sizeof(int32_t));
+ ActorHandle* ah = reinterpret_cast<ActorHandle*>(ptr);
+ if (ah->mId != -3)
+ fail("guessed wrong offset (value is %d, should be -3)", ah->mId);
+ ah->mId = -2;
+}
+
+/*static*/ bool
+ParamTraits<Bad>::Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+{
+ return true;
+}
+
+} // namespace IPC
+
diff --git a/ipc/ipdl/test/cxx/TestActorPunning.h b/ipc/ipdl/test/cxx/TestActorPunning.h
new file mode 100644
index 0000000000..5ebf2e7f42
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestActorPunning.h
@@ -0,0 +1,110 @@
+#ifndef mozilla__ipdltest_TestActorPunning_h
+#define mozilla__ipdltest_TestActorPunning_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestActorPunningParent.h"
+#include "mozilla/_ipdltest/PTestActorPunningPunnedParent.h"
+#include "mozilla/_ipdltest/PTestActorPunningSubParent.h"
+#include "mozilla/_ipdltest/PTestActorPunningChild.h"
+#include "mozilla/_ipdltest/PTestActorPunningPunnedChild.h"
+#include "mozilla/_ipdltest/PTestActorPunningSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestActorPunningParent :
+ public PTestActorPunningParent
+{
+public:
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ PTestActorPunningPunnedParent* AllocPTestActorPunningPunnedParent() override;
+ bool DeallocPTestActorPunningPunnedParent(PTestActorPunningPunnedParent* a) override;
+
+ PTestActorPunningSubParent* AllocPTestActorPunningSubParent() override;
+ bool DeallocPTestActorPunningSubParent(PTestActorPunningSubParent* a) override;
+
+ virtual bool RecvPun(PTestActorPunningSubParent* a, const Bad& bad) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown == why)
+ fail("should have died from error!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+class TestActorPunningPunnedParent :
+ public PTestActorPunningPunnedParent
+{
+public:
+ TestActorPunningPunnedParent() {}
+ virtual ~TestActorPunningPunnedParent() {}
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+class TestActorPunningSubParent :
+ public PTestActorPunningSubParent
+{
+public:
+ TestActorPunningSubParent() {}
+ virtual ~TestActorPunningSubParent() {}
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+
+class TestActorPunningChild :
+ public PTestActorPunningChild
+{
+public:
+ TestActorPunningChild() {}
+ virtual ~TestActorPunningChild() {}
+
+protected:
+ PTestActorPunningPunnedChild* AllocPTestActorPunningPunnedChild() override;
+ bool DeallocPTestActorPunningPunnedChild(PTestActorPunningPunnedChild* a) override;
+
+ PTestActorPunningSubChild* AllocPTestActorPunningSubChild() override;
+ bool DeallocPTestActorPunningSubChild(PTestActorPunningSubChild* a) override;
+
+ virtual bool RecvStart() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ fail("should have been killed off!");
+ }
+};
+
+class TestActorPunningPunnedChild :
+ public PTestActorPunningPunnedChild
+{
+public:
+ TestActorPunningPunnedChild() {}
+ virtual ~TestActorPunningPunnedChild() {}
+};
+
+class TestActorPunningSubChild :
+ public PTestActorPunningSubChild
+{
+public:
+ TestActorPunningSubChild() {}
+ virtual ~TestActorPunningSubChild() {}
+
+ virtual bool RecvBad() override;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestActorPunning_h
diff --git a/ipc/ipdl/test/cxx/TestBadActor.cpp b/ipc/ipdl/test/cxx/TestBadActor.cpp
new file mode 100644
index 0000000000..bc1cf6f6dd
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBadActor.cpp
@@ -0,0 +1,51 @@
+#include "TestBadActor.h"
+#include "IPDLUnitTests.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+void
+TestBadActorParent::Main()
+{
+ // This test is designed to test a race condition where the child sends us
+ // a message on an actor that we've already destroyed. The child process
+ // should die, and the parent process should not abort.
+
+ PTestBadActorSubParent* child = SendPTestBadActorSubConstructor();
+ if (!child)
+ fail("Sending constructor");
+
+ Unused << child->Call__delete__(child);
+}
+
+PTestBadActorSubParent*
+TestBadActorParent::AllocPTestBadActorSubParent()
+{
+ return new TestBadActorSubParent();
+}
+
+bool
+TestBadActorSubParent::RecvPing()
+{
+ fail("Shouldn't have received ping.");
+ return false;
+}
+
+PTestBadActorSubChild*
+TestBadActorChild::AllocPTestBadActorSubChild()
+{
+ return new TestBadActorSubChild();
+}
+
+bool
+TestBadActorChild::RecvPTestBadActorSubConstructor(PTestBadActorSubChild* actor)
+{
+ if (!actor->SendPing()) {
+ fail("Couldn't send ping to an actor which supposedly isn't dead yet.");
+ }
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestBadActor.h b/ipc/ipdl/test/cxx/TestBadActor.h
new file mode 100644
index 0000000000..9b1258ff68
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBadActor.h
@@ -0,0 +1,91 @@
+#ifndef mozilla__ipdltest_TestBadActor_h
+#define mozilla__ipdltest_TestBadActor_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestBadActorParent.h"
+#include "mozilla/_ipdltest/PTestBadActorChild.h"
+
+#include "mozilla/_ipdltest/PTestBadActorSubParent.h"
+#include "mozilla/_ipdltest/PTestBadActorSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestBadActorParent
+ : public PTestBadActorParent
+{
+public:
+ TestBadActorParent() { }
+ virtual ~TestBadActorParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction");
+ passed("ok");
+ QuitParent();
+ }
+
+ virtual PTestBadActorSubParent*
+ AllocPTestBadActorSubParent();
+
+ virtual bool
+ DeallocPTestBadActorSubParent(PTestBadActorSubParent* actor) {
+ delete actor;
+ return true;
+ }
+};
+
+class TestBadActorSubParent
+ : public PTestBadActorSubParent
+{
+public:
+ TestBadActorSubParent() { }
+ virtual ~TestBadActorSubParent() { }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+ virtual bool RecvPing();
+};
+
+class TestBadActorChild
+ : public PTestBadActorChild
+{
+public:
+ TestBadActorChild() { }
+ virtual ~TestBadActorChild() { }
+
+protected:
+ virtual PTestBadActorSubChild*
+ AllocPTestBadActorSubChild();
+
+ virtual bool
+ DeallocPTestBadActorSubChild(PTestBadActorSubChild* actor)
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual bool
+ RecvPTestBadActorSubConstructor(PTestBadActorSubChild* actor);
+};
+
+class TestBadActorSubChild
+ : public PTestBadActorSubChild
+{
+public:
+ TestBadActorSubChild() { }
+ virtual ~TestBadActorSubChild() { }
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // mozilla__ipdltest_TestBadActor_h
diff --git a/ipc/ipdl/test/cxx/TestBridgeMain.cpp b/ipc/ipdl/test/cxx/TestBridgeMain.cpp
new file mode 100644
index 0000000000..9b1590a46c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBridgeMain.cpp
@@ -0,0 +1,227 @@
+#include "TestBridgeMain.h"
+
+#include "base/task.h"
+#include "IPDLUnitTests.h" // fail etc.
+#include "IPDLUnitTestSubprocess.h"
+
+#include "nsAutoPtr.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+//-----------------------------------------------------------------------------
+// main process
+void
+TestBridgeMainParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+PTestBridgeMainSubParent*
+TestBridgeMainParent::AllocPTestBridgeMainSubParent(Transport* transport,
+ ProcessId otherPid)
+{
+ nsAutoPtr<TestBridgeMainSubParent> a(new TestBridgeMainSubParent(transport));
+ if (!a->Open(transport, otherPid, XRE_GetIOMessageLoop(), ipc::ParentSide)) {
+ return nullptr;
+ }
+ return a.forget();
+}
+
+void
+TestBridgeMainParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+}
+
+bool
+TestBridgeMainSubParent::RecvHello()
+{
+ return SendHi();
+}
+
+bool
+TestBridgeMainSubParent::RecvHelloSync()
+{
+ return true;
+}
+
+bool
+TestBridgeMainSubParent::AnswerHelloRpc()
+{
+ return CallHiRpc();
+}
+
+void
+TestBridgeMainSubParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ MessageLoop::current()->PostTask(
+ do_AddRef(new DeleteTask<TestBridgeMainSubParent>(this)));
+}
+
+//-----------------------------------------------------------------------------
+// sub process --- child of main
+TestBridgeMainChild* gBridgeMainChild;
+
+TestBridgeMainChild::TestBridgeMainChild()
+ : mSubprocess(nullptr)
+{
+ gBridgeMainChild = this;
+}
+
+bool
+TestBridgeMainChild::RecvStart()
+{
+ vector<string> subsubArgs;
+ subsubArgs.push_back("TestBridgeSub");
+
+ mSubprocess = new IPDLUnitTestSubprocess();
+ if (!mSubprocess->SyncLaunch(subsubArgs))
+ fail("problem launching subprocess");
+
+ IPC::Channel* transport = mSubprocess->GetChannel();
+ if (!transport)
+ fail("no transport");
+
+ TestBridgeSubParent* bsp = new TestBridgeSubParent();
+ bsp->Open(transport, base::GetProcId(mSubprocess->GetChildProcessHandle()));
+
+ bsp->Main();
+ return true;
+}
+
+void
+TestBridgeMainChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ // NB: this is kosher because QuitChild() joins with the IO thread
+ XRE_GetIOMessageLoop()->PostTask(
+ do_AddRef(new DeleteTask<IPDLUnitTestSubprocess>(mSubprocess)));
+ QuitChild();
+}
+
+void
+TestBridgeSubParent::Main()
+{
+ if (!SendPing())
+ fail("sending Ping");
+}
+
+bool
+TestBridgeSubParent::RecvBridgeEm()
+{
+ if (NS_FAILED(PTestBridgeMainSub::Bridge(gBridgeMainChild, this)))
+ fail("bridging Main and Sub");
+ return true;
+}
+
+void
+TestBridgeSubParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ gBridgeMainChild->Close();
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ MessageLoop::current()->PostTask(
+ do_AddRef(new DeleteTask<TestBridgeSubParent>(this)));
+}
+
+//-----------------------------------------------------------------------------
+// subsub process --- child of sub
+
+static TestBridgeSubChild* gBridgeSubChild;
+
+TestBridgeSubChild::TestBridgeSubChild()
+{
+ gBridgeSubChild = this;
+}
+
+bool
+TestBridgeSubChild::RecvPing()
+{
+ if (!SendBridgeEm())
+ fail("sending BridgeEm");
+ return true;
+}
+
+PTestBridgeMainSubChild*
+TestBridgeSubChild::AllocPTestBridgeMainSubChild(Transport* transport,
+ ProcessId otherPid)
+{
+ nsAutoPtr<TestBridgeMainSubChild> a(new TestBridgeMainSubChild(transport));
+ if (!a->Open(transport, otherPid, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
+ return nullptr;
+ }
+
+ if (!a->SendHello())
+ fail("sending Hello");
+
+ return a.forget();
+}
+
+void
+TestBridgeSubChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+}
+
+bool
+TestBridgeMainSubChild::RecvHi()
+{
+ if (!SendHelloSync())
+ fail("sending HelloSync");
+ if (!CallHelloRpc())
+ fail("calling HelloRpc");
+ if (!mGotHi)
+ fail("didn't answer HiRpc");
+
+ // Need to close the channel without message-processing frames on
+ // the C++ stack
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestBridgeMainSubChild::Close));
+ return true;
+}
+
+bool
+TestBridgeMainSubChild::AnswerHiRpc()
+{
+ mGotHi = true; // d00d
+ return true;
+}
+
+void
+TestBridgeMainSubChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ gBridgeSubChild->Close();
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ MessageLoop::current()->PostTask(
+ do_AddRef(new DeleteTask<TestBridgeMainSubChild>(this)));
+}
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/TestBridgeMain.h b/ipc/ipdl/test/cxx/TestBridgeMain.h
new file mode 100644
index 0000000000..a5c7c10cbc
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestBridgeMain.h
@@ -0,0 +1,149 @@
+#ifndef mozilla__ipdltest_TestBridgeMain_h
+#define mozilla__ipdltest_TestBridgeMain_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestBridgeMainParent.h"
+#include "mozilla/_ipdltest/PTestBridgeMainChild.h"
+
+#include "mozilla/_ipdltest/PTestBridgeSubParent.h"
+#include "mozilla/_ipdltest/PTestBridgeSubChild.h"
+
+#include "mozilla/_ipdltest/PTestBridgeMainSubParent.h"
+#include "mozilla/_ipdltest/PTestBridgeMainSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// "Main" process
+//
+class TestBridgeMainParent :
+ public PTestBridgeMainParent
+{
+public:
+ TestBridgeMainParent() {}
+ virtual ~TestBridgeMainParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual PTestBridgeMainSubParent*
+ AllocPTestBridgeMainSubParent(Transport* transport,
+ ProcessId otherProcess) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+class TestBridgeMainSubParent :
+ public PTestBridgeMainSubParent
+{
+public:
+ explicit TestBridgeMainSubParent(Transport* aTransport)
+ : mTransport(aTransport)
+ {}
+ virtual ~TestBridgeMainSubParent() {}
+
+protected:
+ virtual bool RecvHello() override;
+ virtual bool RecvHelloSync() override;
+ virtual bool AnswerHelloRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ Transport* mTransport;
+};
+
+//-----------------------------------------------------------------------------
+// "Sub" process --- child of "main"
+//
+class TestBridgeSubParent;
+
+class TestBridgeMainChild :
+ public PTestBridgeMainChild
+{
+public:
+ TestBridgeMainChild();
+ virtual ~TestBridgeMainChild() {}
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual PTestBridgeMainSubChild*
+ AllocPTestBridgeMainSubChild(Transport* transport,
+ ProcessId otherProcess) override
+ {
+ // This shouldn't be called. It's just a byproduct of testing that
+ // the right code is generated for a bridged protocol that's also
+ // opened, but we only test bridging here.
+ MOZ_CRASH();
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ IPDLUnitTestSubprocess* mSubprocess;
+};
+
+class TestBridgeSubParent :
+ public PTestBridgeSubParent
+{
+public:
+ TestBridgeSubParent() {}
+ virtual ~TestBridgeSubParent() {}
+
+ void Main();
+
+protected:
+ virtual bool RecvBridgeEm() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+//-----------------------------------------------------------------------------
+// "Subsub" process --- child of "sub"
+//
+class TestBridgeSubChild :
+ public PTestBridgeSubChild
+{
+public:
+ TestBridgeSubChild();
+ virtual ~TestBridgeSubChild() {}
+
+protected:
+ virtual bool RecvPing() override;
+
+ virtual PTestBridgeMainSubChild*
+ AllocPTestBridgeMainSubChild(Transport* transport,
+ ProcessId otherProcess) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+class TestBridgeMainSubChild :
+ public PTestBridgeMainSubChild
+{
+public:
+ explicit TestBridgeMainSubChild(Transport* aTransport)
+ : mGotHi(false)
+ , mTransport(aTransport)
+ {}
+ virtual ~TestBridgeMainSubChild() {}
+
+protected:
+ virtual bool RecvHi() override;
+ virtual bool AnswerHiRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ bool mGotHi;
+ Transport* mTransport;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestBridgeMain_h
diff --git a/ipc/ipdl/test/cxx/TestCancel.cpp b/ipc/ipdl/test/cxx/TestCancel.cpp
new file mode 100644
index 0000000000..8f983b3de8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestCancel.cpp
@@ -0,0 +1,168 @@
+#include "TestCancel.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestCancelParent::TestCancelParent()
+{
+ MOZ_COUNT_CTOR(TestCancelParent);
+}
+
+TestCancelParent::~TestCancelParent()
+{
+ MOZ_COUNT_DTOR(TestCancelParent);
+}
+
+void
+TestCancelParent::Main()
+{
+ if (SendTest1_1())
+ fail("sending Test1_1");
+
+ uint32_t value = 0;
+ if (!SendCheckChild(&value))
+ fail("Test1 CheckChild");
+
+ if (value != 12)
+ fail("Test1 CheckChild reply");
+}
+
+bool
+TestCancelParent::RecvDone1()
+{
+ if (!SendStart2())
+ fail("sending Start2");
+
+ return true;
+}
+
+bool
+TestCancelParent::RecvTest2_1()
+{
+ if (SendTest2_2())
+ fail("sending Test2_2");
+
+ return true;
+}
+
+bool
+TestCancelParent::RecvStart3()
+{
+ if (SendTest3_1())
+ fail("sending Test3_1");
+
+ uint32_t value = 0;
+ if (!SendCheckChild(&value))
+ fail("Test1 CheckChild");
+
+ if (value != 12)
+ fail("Test1 CheckChild reply");
+
+ return true;
+}
+
+bool
+TestCancelParent::RecvTest3_2()
+{
+ GetIPCChannel()->CancelCurrentTransaction();
+ return true;
+}
+
+bool
+TestCancelParent::RecvDone()
+{
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestCancelParent::Close));
+ return true;
+}
+
+bool
+TestCancelParent::RecvCheckParent(uint32_t *reply)
+{
+ *reply = 12;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+bool
+TestCancelChild::RecvTest1_1()
+{
+ GetIPCChannel()->CancelCurrentTransaction();
+
+ uint32_t value = 0;
+ if (!SendCheckParent(&value))
+ fail("Test1 CheckParent");
+
+ if (value != 12)
+ fail("Test1 CheckParent reply");
+
+ if (!SendDone1())
+ fail("Test1 CheckParent");
+
+ return true;
+}
+
+bool
+TestCancelChild::RecvStart2()
+{
+ if (!SendTest2_1())
+ fail("sending Test2_1");
+
+ if (!SendStart3())
+ fail("sending Start3");
+
+ return true;
+}
+
+bool
+TestCancelChild::RecvTest2_2()
+{
+ GetIPCChannel()->CancelCurrentTransaction();
+ return true;
+}
+
+bool
+TestCancelChild::RecvTest3_1()
+{
+ if (SendTest3_2())
+ fail("sending Test3_2");
+
+ uint32_t value = 0;
+ if (!SendCheckParent(&value))
+ fail("Test1 CheckParent");
+
+ if (value != 12)
+ fail("Test1 CheckParent reply");
+
+ if (!SendDone())
+ fail("sending Done");
+
+ return true;
+}
+
+bool
+TestCancelChild::RecvCheckChild(uint32_t *reply)
+{
+ *reply = 12;
+ return true;
+}
+
+TestCancelChild::TestCancelChild()
+{
+ MOZ_COUNT_CTOR(TestCancelChild);
+}
+
+TestCancelChild::~TestCancelChild()
+{
+ MOZ_COUNT_DTOR(TestCancelChild);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestCancel.h b/ipc/ipdl/test/cxx/TestCancel.h
new file mode 100644
index 0000000000..8cd51acd16
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestCancel.h
@@ -0,0 +1,66 @@
+#ifndef mozilla__ipdltest_TestCancel_h
+#define mozilla__ipdltest_TestCancel_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestCancelParent.h"
+#include "mozilla/_ipdltest/PTestCancelChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestCancelParent :
+ public PTestCancelParent
+{
+public:
+ TestCancelParent();
+ virtual ~TestCancelParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ virtual bool RecvDone1() override;
+ virtual bool RecvTest2_1() override;
+ virtual bool RecvStart3() override;
+ virtual bool RecvTest3_2() override;
+ virtual bool RecvDone() override;
+
+ virtual bool RecvCheckParent(uint32_t *reply) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestCancelChild :
+ public PTestCancelChild
+{
+public:
+ TestCancelChild();
+ virtual ~TestCancelChild();
+
+ virtual bool RecvTest1_1() override;
+ virtual bool RecvStart2() override;
+ virtual bool RecvTest2_2() override;
+ virtual bool RecvTest3_1() override;
+
+ virtual bool RecvCheckChild(uint32_t *reply) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestCancel_h
diff --git a/ipc/ipdl/test/cxx/TestCrashCleanup.cpp b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp
new file mode 100644
index 0000000000..2f335daae0
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestCrashCleanup.cpp
@@ -0,0 +1,116 @@
+#include "TestCrashCleanup.h"
+
+#include "base/task.h"
+#include "mozilla/CondVar.h"
+#include "mozilla/Mutex.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#include "IPDLUnitTestSubprocess.h"
+
+using mozilla::CondVar;
+using mozilla::Mutex;
+using mozilla::MutexAutoLock;
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+namespace {
+
+// NB: this test does its own shutdown, rather than going through
+// QuitParent(), because it's testing degenerate edge cases
+
+void DeleteSubprocess(Mutex* mutex, CondVar* cvar)
+{
+ MutexAutoLock lock(*mutex);
+
+ delete gSubprocess;
+ gSubprocess = nullptr;
+
+ cvar->Notify();
+}
+
+void DeleteTheWorld()
+{
+ delete static_cast<TestCrashCleanupParent*>(gParentActor);
+ gParentActor = nullptr;
+
+ // needs to be synchronous to avoid affecting event ordering on
+ // the main thread
+ Mutex mutex("TestCrashCleanup.DeleteTheWorld.mutex");
+ CondVar cvar(mutex, "TestCrashCleanup.DeleteTheWorld.cvar");
+
+ MutexAutoLock lock(mutex);
+
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(DeleteSubprocess, &mutex, &cvar));
+
+ cvar.Wait();
+}
+
+void Done()
+{
+ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+ nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
+ appShell->Exit();
+
+ passed(__FILE__);
+}
+
+} // namespace <anon>
+
+TestCrashCleanupParent::TestCrashCleanupParent() : mCleanedUp(false)
+{
+ MOZ_COUNT_CTOR(TestCrashCleanupParent);
+}
+
+TestCrashCleanupParent::~TestCrashCleanupParent()
+{
+ MOZ_COUNT_DTOR(TestCrashCleanupParent);
+
+ if (!mCleanedUp)
+ fail("should have been ActorDestroy()d!");
+}
+
+void
+TestCrashCleanupParent::Main()
+{
+ // NB: has to be enqueued before IO thread's error notification
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction(DeleteTheWorld));
+
+ if (CallDIEDIEDIE())
+ fail("expected an error!");
+
+ Close();
+
+ MessageLoop::current()->PostTask(NewRunnableFunction(Done));
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+TestCrashCleanupChild::TestCrashCleanupChild()
+{
+ MOZ_COUNT_CTOR(TestCrashCleanupChild);
+}
+
+TestCrashCleanupChild::~TestCrashCleanupChild()
+{
+ MOZ_COUNT_DTOR(TestCrashCleanupChild);
+}
+
+bool
+TestCrashCleanupChild::AnswerDIEDIEDIE()
+{
+ _exit(0);
+ NS_RUNTIMEABORT("unreached");
+ return false;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestCrashCleanup.h b/ipc/ipdl/test/cxx/TestCrashCleanup.h
new file mode 100644
index 0000000000..b64b2c628b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestCrashCleanup.h
@@ -0,0 +1,58 @@
+#ifndef mozilla__ipdltest_TestCrashCleanup_h
+#define mozilla__ipdltest_TestCrashCleanup_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestCrashCleanupParent.h"
+#include "mozilla/_ipdltest/PTestCrashCleanupChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestCrashCleanupParent :
+ public PTestCrashCleanupParent
+{
+public:
+ TestCrashCleanupParent();
+ virtual ~TestCrashCleanupParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction!");
+ mCleanedUp = true;
+ }
+
+ bool mCleanedUp;
+};
+
+
+class TestCrashCleanupChild :
+ public PTestCrashCleanupChild
+{
+public:
+ TestCrashCleanupChild();
+ virtual ~TestCrashCleanupChild();
+
+protected:
+ virtual bool AnswerDIEDIEDIE() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ fail("should have 'crashed'!");
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestCrashCleanup_h
diff --git a/ipc/ipdl/test/cxx/TestDataStructures.cpp b/ipc/ipdl/test/cxx/TestDataStructures.cpp
new file mode 100644
index 0000000000..fe0d5918a7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDataStructures.cpp
@@ -0,0 +1,984 @@
+#include "TestDataStructures.h"
+
+#include "mozilla/Unused.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+typedef InfallibleTArray<nsIntRegion> RegionArray;
+
+namespace mozilla {
+namespace _ipdltest {
+
+static const uint32_t nactors = 10;
+
+#define test_assert(_cond, _msg) \
+ if (!(_cond)) fail(_msg)
+
+template<typename T>
+static void
+assert_arrays_equal(const InfallibleTArray<T>& a, const InfallibleTArray<T>& b)
+{
+ test_assert(a == b, "arrays equal");
+}
+
+inline static TestDataStructuresSub&
+Cast(PTestDataStructuresSubParent* a)
+{
+ return *static_cast<TestDataStructuresSub*>(a);
+}
+
+inline static TestDataStructuresSub&
+Cast(PTestDataStructuresSubChild* a)
+{
+ return *static_cast<TestDataStructuresSub*>(a);
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestDataStructuresParent::TestDataStructuresParent()
+{
+ MOZ_COUNT_CTOR(TestDataStructuresParent);
+}
+
+TestDataStructuresParent::~TestDataStructuresParent()
+{
+ MOZ_COUNT_DTOR(TestDataStructuresParent);
+}
+
+void
+TestDataStructuresParent::Main()
+{
+ for (uint32_t i = 0; i < nactors; ++i)
+ if (!SendPTestDataStructuresSubConstructor(i))
+ fail("can't alloc actor");
+
+ if (!SendStart())
+ fail("can't send Start()");
+}
+
+bool
+TestDataStructuresParent::DeallocPTestDataStructuresSubParent(PTestDataStructuresSubParent* actor)
+{
+ test_assert(Cast(actor).mI == Cast(mKids[0]).mI,
+ "dtor sent to wrong actor");
+ mKids.RemoveElementAt(0);
+ delete actor;
+ if (mKids.Length() > 0)
+ return true;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest1(
+ InfallibleTArray<int>&& ia,
+ InfallibleTArray<int>* oa)
+{
+ test_assert(5 == ia.Length(), "wrong length");
+ for (int i = 0; i < 5; ++i)
+ test_assert(i == ia[i], "wrong value");
+
+ *oa = ia;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest2(
+ InfallibleTArray<PTestDataStructuresSubParent*>&& i1,
+ InfallibleTArray<PTestDataStructuresSubParent*>* o1)
+{
+ test_assert(nactors == i1.Length(), "wrong #actors");
+ for (uint32_t i = 0; i < i1.Length(); ++i)
+ test_assert(i == Cast(i1[i]).mI, "wrong mI value");
+ *o1 = i1;
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest3(
+ const IntDouble& i1,
+ const IntDouble& i2,
+ IntDouble* o1,
+ IntDouble* o2)
+{
+ test_assert(42 == i1.get_int(), "wrong value");
+ test_assert(4.0 == i2.get_double(), "wrong value");
+
+ *o1 = i1;
+ *o2 = i2;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest4(
+ InfallibleTArray<IntDouble>&& i1,
+ InfallibleTArray<IntDouble>* o1)
+{
+ test_assert(4 == i1.Length(), "wrong length");
+ test_assert(1 == i1[0].get_int(), "wrong value");
+ test_assert(2.0 == i1[1].get_double(), "wrong value");
+ test_assert(3 == i1[2].get_int(), "wrong value");
+ test_assert(4.0 == i1[3].get_double(), "wrong value");
+
+ *o1 = i1;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest5(
+ const IntDoubleArrays& i1,
+ const IntDoubleArrays& i2,
+ const IntDoubleArrays& i3,
+ IntDoubleArrays* o1,
+ IntDoubleArrays* o2,
+ IntDoubleArrays* o3)
+{
+ test_assert(42 == i1.get_int(), "wrong value");
+
+ const InfallibleTArray<int>& i2a = i2.get_ArrayOfint();
+ test_assert(3 == i2a.Length(), "wrong length");
+ test_assert(1 == i2a[0], "wrong value");
+ test_assert(2 == i2a[1], "wrong value");
+ test_assert(3 == i2a[2], "wrong value");
+
+ const InfallibleTArray<double>& i3a = i3.get_ArrayOfdouble();
+ test_assert(3 == i3a.Length(), "wrong length");
+ test_assert(1.0 == i3a[0], "wrong value");
+ test_assert(2.0 == i3a[1], "wrong value");
+ test_assert(3.0 == i3a[2], "wrong value");
+
+ *o1 = i1;
+ *o2 = i2a;
+ *o3 = i3a;
+
+ return true;
+}
+
+bool
+TestDataStructuresParent::RecvTest7_0(const ActorWrapper& i1,
+ ActorWrapper* o1)
+{
+ if (i1.actorChild() != nullptr)
+ fail("child side actor should always be null");
+
+ if (i1.actorParent() != mKids[0])
+ fail("should have got back same actor on parent side");
+
+ o1->actorParent() = mKids[0];
+ // malicious behavior
+ o1->actorChild() =
+ reinterpret_cast<PTestDataStructuresSubChild*>(uintptr_t(0xdeadbeef));
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest6(
+ InfallibleTArray<IntDoubleArrays>&& i1,
+ InfallibleTArray<IntDoubleArrays>* o1)
+{
+ test_assert(3 == i1.Length(), "wrong length");
+
+ IntDoubleArrays id1(i1[0]);
+ test_assert(42 == id1.get_int(), "wrong value");
+
+ InfallibleTArray<int> i2a(i1[1].get_ArrayOfint());
+ test_assert(3 == i2a.Length(), "wrong length");
+ test_assert(1 == i2a[0], "wrong value");
+ test_assert(2 == i2a[1], "wrong value");
+ test_assert(3 == i2a[2], "wrong value");
+
+ InfallibleTArray<double> i3a(i1[2].get_ArrayOfdouble());
+ test_assert(3 == i3a.Length(), "wrong length");
+ test_assert(1.0 == i3a[0], "wrong value");
+ test_assert(2.0 == i3a[1], "wrong value");
+ test_assert(3.0 == i3a[2], "wrong value");
+
+ o1->AppendElement(id1);
+ o1->AppendElement(IntDoubleArrays(i2a));
+ o1->AppendElement(IntDoubleArrays(i3a));
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest7(
+ const Actors& i1,
+ const Actors& i2,
+ const Actors& i3,
+ Actors* o1,
+ Actors* o2,
+ Actors* o3)
+{
+ test_assert(42 == i1.get_int(), "wrong value");
+
+ InfallibleTArray<int> i2a(i2.get_ArrayOfint());
+ test_assert(3 == i2a.Length(), "wrong length");
+ test_assert(1 == i2a[0], "wrong value");
+ test_assert(2 == i2a[1], "wrong value");
+ test_assert(3 == i2a[2], "wrong value");
+
+ assert_arrays_equal(mKids, i3.get_ArrayOfPTestDataStructuresSubParent());
+
+ *o1 = 42;
+ *o2 = i2a;
+ *o3 = mKids;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest8(
+ InfallibleTArray<Actors>&& i1,
+ InfallibleTArray<Actors>* o1)
+{
+ test_assert(3 == i1.Length(), "wrong length");
+ test_assert(42 == i1[0].get_int(), "wrong value");
+
+ const InfallibleTArray<int>& i2a = i1[1].get_ArrayOfint();
+ test_assert(3 == i2a.Length(), "wrong length");
+ test_assert(1 == i2a[0], "wrong value");
+ test_assert(2 == i2a[1], "wrong value");
+ test_assert(3 == i2a[2], "wrong value");
+
+ assert_arrays_equal(mKids, i1[2].get_ArrayOfPTestDataStructuresSubParent());
+
+ *o1 = i1;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest9(
+ const Unions& i1,
+ const Unions& i2,
+ const Unions& i3,
+ const Unions& i4,
+ Unions* o1,
+ Unions* o2,
+ Unions* o3,
+ Unions* o4)
+{
+ test_assert(42 == i1.get_int(), "wrong value");
+
+ const InfallibleTArray<int>& i2a = i2.get_ArrayOfint();
+ test_assert(3 == i2a.Length(), "wrong length");
+ test_assert(1 == i2a[0], "wrong value");
+ test_assert(2 == i2a[1], "wrong value");
+ test_assert(3 == i2a[2], "wrong value");
+
+ assert_arrays_equal(mKids, i3.get_ArrayOfPTestDataStructuresSubParent());
+
+ const InfallibleTArray<PTestDataStructuresSubParent*>& i4a =
+ i4.get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubParent();
+ assert_arrays_equal(mKids, i4a);
+
+ *o1 = i1;
+ *o2 = i2;
+ *o3 = i3;
+ *o4 = i4;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest10(
+ InfallibleTArray<Unions>&& i1,
+ InfallibleTArray<Unions>* o1)
+{
+ test_assert(42 == i1[0].get_int(), "wrong value");
+
+ const InfallibleTArray<int>& i2a = i1[1].get_ArrayOfint();
+ test_assert(3 == i2a.Length(), "wrong length");
+ test_assert(1 == i2a[0], "wrong value");
+ test_assert(2 == i2a[1], "wrong value");
+ test_assert(3 == i2a[2], "wrong value");
+
+ assert_arrays_equal(mKids, i1[2].get_ArrayOfPTestDataStructuresSubParent());
+
+ const InfallibleTArray<PTestDataStructuresSubParent*>& i4a =
+ i1[3].get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubParent();
+ assert_arrays_equal(mKids, i4a);
+
+ *o1 = i1;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest11(
+ const SIntDouble& i,
+ SIntDouble* o)
+{
+ test_assert(1 == i.i(), "wrong value");
+ test_assert(2.0 == i.d(), "wrong value");
+ *o = i;
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest12(
+ const SIntDoubleArrays& i,
+ SIntDoubleArrays* o)
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1);
+ ai.AppendElement(2);
+ ai.AppendElement(3);
+
+ InfallibleTArray<double> ad;
+ ad.AppendElement(.5);
+ ad.AppendElement(1.0);
+ ad.AppendElement(2.0);
+
+ test_assert(42 == i.i(), "wrong value");
+ assert_arrays_equal(ai, i.ai());
+ assert_arrays_equal(ad, i.ad());
+
+ *o = i;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest13(
+ const SActors& i,
+ SActors* o)
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ test_assert(42 == i.i(), "wrong value");
+ assert_arrays_equal(ai, i.ai());
+ assert_arrays_equal(mKids, i.apParent());
+
+ *o = i;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest14(
+ const Structs& i,
+ Structs* o)
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ test_assert(42 == i.i(), "wrong value");
+ assert_arrays_equal(ai, i.ai());
+ assert_arrays_equal(mKids, i.apParent());
+
+ const SActors& ia = i.aa()[0];
+ test_assert(42 == ia.i(), "wrong value");
+ assert_arrays_equal(ai, ia.ai());
+ assert_arrays_equal(mKids, ia.apParent());
+
+ *o = i;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest15(
+ const WithStructs& i1,
+ const WithStructs& i2,
+ const WithStructs& i3,
+ const WithStructs& i4,
+ const WithStructs& i5,
+ WithStructs* o1,
+ WithStructs* o2,
+ WithStructs* o3,
+ WithStructs* o4,
+ WithStructs* o5)
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ test_assert(i1 == int(42), "wrong value");
+ assert_arrays_equal(i2.get_ArrayOfint(), ai);
+ assert_arrays_equal(i3.get_ArrayOfPTestDataStructuresSubParent(), mKids);
+
+ const SActors& ia = i4.get_ArrayOfSActors()[0];
+ test_assert(42 == ia.i(), "wrong value");
+ assert_arrays_equal(ai, ia.ai());
+ assert_arrays_equal(mKids, ia.apParent());
+
+ const Structs& is = i5.get_ArrayOfStructs()[0];
+ test_assert(42 == is.i(), "wrong value");
+ assert_arrays_equal(ai, is.ai());
+ assert_arrays_equal(mKids, is.apParent());
+
+ const SActors& isa = is.aa()[0];
+ test_assert(42 == isa.i(), "wrong value");
+ assert_arrays_equal(ai, isa.ai());
+ assert_arrays_equal(mKids, isa.apParent());
+
+ *o1 = i1;
+ *o2 = i2;
+ *o3 = i3;
+ *o4 = i4;
+ *o5 = i5;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest16(
+ const WithUnions& i,
+ WithUnions* o)
+{
+ test_assert(i.i() == 42, "wrong value");
+
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+ assert_arrays_equal(ai, i.ai());
+
+ assert_arrays_equal(i.apParent(), mKids);
+
+ assert_arrays_equal(mKids, i.aa()[0].get_ArrayOfPTestDataStructuresSubParent());
+
+ const InfallibleTArray<Unions>& iau = i.au();
+ test_assert(iau[0] == 42, "wrong value");
+ assert_arrays_equal(ai, iau[1].get_ArrayOfint());
+ assert_arrays_equal(mKids, iau[2].get_ArrayOfPTestDataStructuresSubParent());
+ assert_arrays_equal(mKids,
+ iau[3].get_ArrayOfActors()[0]
+ .get_ArrayOfPTestDataStructuresSubParent());
+
+ *o = i;
+
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest17(InfallibleTArray<Op>&& sa)
+{
+ test_assert(sa.Length() == 1 && Op::TSetAttrs == sa[0].type(),
+ "wrong value");
+ return true;
+}
+
+bool TestDataStructuresParent::RecvTest18(RegionArray&& ra)
+{
+ for (RegionArray::index_type i = 0; i < ra.Length(); ++i) {
+ // if |ra| has been realloc()d and given a different allocator
+ // chunk, this loop will nondeterministically crash or iloop.
+ for (auto iter = ra[i].RectIter(); !iter.Done(); iter.Next()) {
+ Unused << iter.Get();
+ }
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestDataStructuresChild::TestDataStructuresChild()
+{
+ MOZ_COUNT_CTOR(TestDataStructuresChild);
+}
+
+TestDataStructuresChild::~TestDataStructuresChild()
+{
+ MOZ_COUNT_DTOR(TestDataStructuresChild);
+}
+
+bool
+TestDataStructuresChild::RecvStart()
+{
+ puts("[TestDataStructuresChild] starting");
+
+ Test1();
+ Test2();
+ Test3();
+ Test4();
+ Test5();
+ Test6();
+ Test7_0();
+ Test7();
+ Test8();
+ Test9();
+ Test10();
+ Test11();
+ Test12();
+ Test13();
+ Test14();
+ Test15();
+ Test16();
+ Test17();
+ if (OtherPid() != base::GetCurrentProcId()) {
+ //FIXME/bug 703317 allocation of nsIntRegion uses a global
+ //region pool which breaks threads
+ Test18();
+ }
+
+ for (uint32_t i = 0; i < nactors; ++i)
+ if (!PTestDataStructuresSubChild::Send__delete__(mKids[i]))
+ fail("can't send dtor");
+
+ Close();
+
+ return true;
+}
+
+void
+TestDataStructuresChild::Test1()
+{
+ InfallibleTArray<int> ia;
+
+ for (int i = 0; i < 5; ++i)
+ ia.AppendElement(i);
+
+ InfallibleTArray<int> oa;
+ if (!SendTest1(ia, &oa))
+ fail("can't send Test1");
+
+ assert_arrays_equal(ia, oa);
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test2()
+{
+ InfallibleTArray<PTestDataStructuresSubChild*> oa;
+ if (!SendTest2(mKids, &oa))
+ fail("can't send Test2");
+ assert_arrays_equal(mKids, oa);
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test3()
+{
+ int i1i = 42;
+ double i2d = 4.0;
+ IntDouble i1(i1i);
+ IntDouble i2(i2d);
+ IntDouble o1, o2;
+
+ SendTest3(i1, i2, &o1, &o2);
+
+ test_assert(i1i == o1.get_int(), "wrong value");
+ test_assert(i2d == o2.get_double(), "wrong value");
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test4()
+{
+ InfallibleTArray<IntDouble> i1;
+ i1.AppendElement(IntDouble(int(1)));
+ i1.AppendElement(IntDouble(2.0));
+ i1.AppendElement(IntDouble(int(3)));
+ i1.AppendElement(IntDouble(4.0));
+
+ InfallibleTArray<IntDouble> o1;
+ if (!SendTest4(i1, &o1))
+ fail("can't send Test4");
+
+ // TODO Union::operator==()
+ test_assert(i1.Length() == o1.Length(), "wrong length");
+ test_assert(1 == o1[0].get_int(), "wrong value");
+ test_assert(2.0 == o1[1].get_double(), "wrong value");
+ test_assert(3 == o1[2].get_int(), "wrong value");
+ test_assert(4.0 == o1[3].get_double(), "wrong value");
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test5()
+{
+ IntDoubleArrays i1(int(42));
+ InfallibleTArray<int> i2;
+ i2.AppendElement(1); i2.AppendElement(2); i2.AppendElement(3);
+ InfallibleTArray<double> i3;
+ i3.AppendElement(1.0); i3.AppendElement(2.0); i3.AppendElement(3.0);
+
+ IntDoubleArrays o1, o2, o3;
+ if (!SendTest5(i1, IntDoubleArrays(i2), IntDoubleArrays(i3),
+ &o1, &o2, &o3))
+ fail("can't send Test5");
+
+ test_assert(42 == o1.get_int(), "wrong value");
+ assert_arrays_equal(i2, o2.get_ArrayOfint());
+ assert_arrays_equal(i3, o3.get_ArrayOfdouble());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test6()
+{
+ IntDoubleArrays id1(int(42));
+ InfallibleTArray<int> id2;
+ id2.AppendElement(1); id2.AppendElement(2); id2.AppendElement(3);
+ InfallibleTArray<double> id3;
+ id3.AppendElement(1.0); id3.AppendElement(2.0); id3.AppendElement(3.0);
+
+ InfallibleTArray<IntDoubleArrays> i1;
+ i1.AppendElement(id1);
+ i1.AppendElement(IntDoubleArrays(id2));
+ i1.AppendElement(IntDoubleArrays(id3));
+
+ InfallibleTArray<IntDoubleArrays> o1;
+ if (!SendTest6(i1, &o1))
+ fail("can't send Test6");
+
+ test_assert(3 == o1.Length(), "wrong length");
+ IntDoubleArrays od1(o1[0]);
+ InfallibleTArray<int> od2(o1[1].get_ArrayOfint());
+ InfallibleTArray<double> od3(o1[2].get_ArrayOfdouble());
+
+ test_assert(42 == od1.get_int(), "wrong value");
+ assert_arrays_equal(id2, od2);
+ assert_arrays_equal(id3, od3);
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test7_0()
+{
+ ActorWrapper iaw;
+ if (iaw.actorChild() != nullptr || iaw.actorParent() != nullptr)
+ fail("actor members should be null initially");
+
+ iaw.actorChild() = mKids[0];
+ if (iaw.actorParent() != nullptr)
+ fail("parent should be null on child side after set");
+
+ ActorWrapper oaw;
+ if (!SendTest7_0(iaw, &oaw))
+ fail("sending Test7_0");
+
+ if (oaw.actorParent() != nullptr)
+ fail("parent accessor on actor-struct members should always be null in child");
+
+ if (oaw.actorChild() != mKids[0])
+ fail("should have got back same child-side actor");
+}
+
+void
+TestDataStructuresChild::Test7()
+{
+ Actors i1(42);
+ InfallibleTArray<int> i2a;
+ i2a.AppendElement(1); i2a.AppendElement(2); i2a.AppendElement(3);
+
+ Actors o1, o2, o3;
+ if (!SendTest7(i1, Actors(i2a), Actors(mKids), &o1, &o2, &o3))
+ fail("can't send Test7");
+
+ test_assert(42 == o1.get_int(), "wrong value");
+ assert_arrays_equal(i2a, o2.get_ArrayOfint());
+ assert_arrays_equal(mKids, o3.get_ArrayOfPTestDataStructuresSubChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test8()
+{
+ Actors i1e(42);
+ InfallibleTArray<int> i2a;
+ i2a.AppendElement(1); i2a.AppendElement(2); i2a.AppendElement(3);
+
+ InfallibleTArray<Actors> i1;
+ i1.AppendElement(i1e);
+ i1.AppendElement(i2a);
+ i1.AppendElement(mKids);
+
+ InfallibleTArray<Actors> o1;
+ if (!SendTest8(i1, &o1))
+ fail("can't send Test8");
+
+ test_assert(3 == o1.Length(), "wrong length");
+ test_assert(42 == o1[0].get_int(), "wrong value");
+ assert_arrays_equal(i2a, o1[1].get_ArrayOfint());
+ assert_arrays_equal(mKids, o1[2].get_ArrayOfPTestDataStructuresSubChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test9()
+{
+ Unions i1(int(42));
+
+ InfallibleTArray<int> i2a;
+ i2a.AppendElement(1);
+ i2a.AppendElement(2);
+ i2a.AppendElement(3);
+
+ InfallibleTArray<Actors> i4a;
+ i4a.AppendElement(mKids);
+
+ Unions o1, o2, o3, o4;
+ if (!SendTest9(i1, Unions(i2a), Unions(mKids), Unions(i4a),
+ &o1, &o2, &o3, &o4))
+ fail("can't send Test9");
+
+ test_assert(42 == o1.get_int(), "wrong value");
+ assert_arrays_equal(i2a, o2.get_ArrayOfint());
+ assert_arrays_equal(mKids, o3.get_ArrayOfPTestDataStructuresSubChild());
+ assert_arrays_equal(
+ mKids,
+ o4.get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test10()
+{
+ Unions i1a(int(42));
+
+ InfallibleTArray<int> i2a;
+ i2a.AppendElement(1);
+ i2a.AppendElement(2);
+ i2a.AppendElement(3);
+
+ InfallibleTArray<Actors> i4a;
+ i4a.AppendElement(mKids);
+
+ InfallibleTArray<Unions> i1;
+ i1.AppendElement(i1a);
+ i1.AppendElement(Unions(i2a));
+ i1.AppendElement(Unions(mKids));
+ i1.AppendElement(Unions(i4a));
+
+ InfallibleTArray<Unions> o1;
+ if (!SendTest10(i1, &o1))
+ fail("can't send Test10");
+
+ test_assert(4 == o1.Length(), "wrong length");
+ test_assert(42 == o1[0].get_int(), "wrong value");
+ assert_arrays_equal(i2a, o1[1].get_ArrayOfint());
+ assert_arrays_equal(mKids, o1[2].get_ArrayOfPTestDataStructuresSubChild());
+ assert_arrays_equal(
+ mKids,
+ o1[3].get_ArrayOfActors()[0].get_ArrayOfPTestDataStructuresSubChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test11()
+{
+ SIntDouble i(1, 2.0);
+ SIntDouble o;
+
+ if (!SendTest11(i, &o))
+ fail("sending Test11");
+
+ test_assert(1 == o.i() && 2.0 == o.d(), "wrong values");
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test12()
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ InfallibleTArray<double> ad;
+ ad.AppendElement(.5); ad.AppendElement(1.0); ad.AppendElement(2.0);
+
+ SIntDoubleArrays i(42, ai, ad);
+ SIntDoubleArrays o;
+
+ if (!SendTest12(i, &o))
+ fail("sending Test12");
+
+ test_assert(42 == o.i(), "wrong value");
+ assert_arrays_equal(ai, o.ai());
+ assert_arrays_equal(ad, o.ad());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test13()
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ SActors i;
+ i.i() = 42;
+ i.ai() = ai;
+ i.apChild() = mKids;
+
+ SActors o;
+ if (!SendTest13(i, &o))
+ fail("can't send Test13");
+
+ test_assert(42 == o.i(), "wrong value");
+ assert_arrays_equal(ai, o.ai());
+ assert_arrays_equal(mKids, o.apChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test14()
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ SActors ia;
+ ia.i() = 42;
+ ia.ai() = ai;
+ ia.apChild() = mKids;
+ InfallibleTArray<SActors> aa; aa.AppendElement(ia);
+
+ Structs i;
+ i.i() = 42;
+ i.ai() = ai;
+ i.apChild() = mKids;
+ i.aa() = aa;
+
+ Structs o;
+ if (!SendTest14(i, &o))
+ fail("can't send Test14");
+
+ test_assert(42 == o.i(), "wrong value");
+ assert_arrays_equal(ai, o.ai());
+ assert_arrays_equal(mKids, o.apChild());
+
+ const SActors& os = o.aa()[0];
+ test_assert(42 == os.i(), "wrong value");
+ assert_arrays_equal(ai, os.ai());
+ assert_arrays_equal(mKids, os.apChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test15()
+{
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+
+ SActors ia;
+ ia.i() = 42;
+ ia.ai() = ai;
+ ia.apChild() = mKids;
+ InfallibleTArray<SActors> iaa; iaa.AppendElement(ia);
+
+ Structs is;
+ is.i() = 42;
+ is.ai() = ai;
+ is.apChild() = mKids;
+ is.aa() = iaa;
+ InfallibleTArray<Structs> isa; isa.AppendElement(is);
+
+ WithStructs o1, o2, o3, o4, o5;
+ if (!SendTest15(WithStructs(42),
+ WithStructs(ai),
+ WithStructs(mKids),
+ WithStructs(iaa),
+ WithStructs(isa),
+ &o1, &o2, &o3, &o4, &o5))
+ fail("sending Test15");
+
+ test_assert(o1 == int(42), "wrong value");
+ assert_arrays_equal(o2.get_ArrayOfint(), ai);
+ assert_arrays_equal(o3.get_ArrayOfPTestDataStructuresSubChild(), mKids);
+
+ const SActors& oa = o4.get_ArrayOfSActors()[0];
+ test_assert(42 == oa.i(), "wrong value");
+ assert_arrays_equal(ai, oa.ai());
+ assert_arrays_equal(mKids, oa.apChild());
+
+ const Structs& os = o5.get_ArrayOfStructs()[0];
+ test_assert(42 == os.i(), "wrong value");
+ assert_arrays_equal(ai, os.ai());
+ assert_arrays_equal(mKids, os.apChild());
+
+ const SActors& osa = os.aa()[0];
+ test_assert(42 == osa.i(), "wrong value");
+ assert_arrays_equal(ai, osa.ai());
+ assert_arrays_equal(mKids, osa.apChild());
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test16()
+{
+ WithUnions i;
+
+ i.i() = 42;
+
+ InfallibleTArray<int> ai;
+ ai.AppendElement(1); ai.AppendElement(2); ai.AppendElement(3);
+ i.ai() = ai;
+
+ i.apChild() = mKids;
+
+ InfallibleTArray<Actors> iaa;
+ iaa.AppendElement(mKids);
+ i.aa() = iaa;
+
+ InfallibleTArray<Unions> iau;
+ iau.AppendElement(int(42));
+ iau.AppendElement(ai);
+ iau.AppendElement(mKids);
+ iau.AppendElement(iaa);
+ i.au() = iau;
+
+ WithUnions o;
+ if (!SendTest16(i, &o))
+ fail("sending Test16");
+
+ test_assert(42 == o.i(), "wrong value");
+ assert_arrays_equal(o.ai(), ai);
+ assert_arrays_equal(o.apChild(), mKids);
+
+ const Actors& oaa = o.aa()[0];
+ assert_arrays_equal(oaa.get_ArrayOfPTestDataStructuresSubChild(), mKids);
+
+ const InfallibleTArray<Unions>& oau = o.au();
+ test_assert(oau[0] == 42, "wrong value");
+ assert_arrays_equal(oau[1].get_ArrayOfint(), ai);
+ assert_arrays_equal(oau[2].get_ArrayOfPTestDataStructuresSubChild(),
+ mKids);
+ assert_arrays_equal(oau[3].get_ArrayOfActors()[0]
+ .get_ArrayOfPTestDataStructuresSubChild(),
+ mKids);
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test17()
+{
+ Attrs attrs;
+ attrs.common() = CommonAttrs(true);
+ attrs.specific() = BarAttrs(1.0f);
+
+ InfallibleTArray<Op> ops;
+ ops.AppendElement(SetAttrs(nullptr, mKids[0], attrs));
+
+ if (!SendTest17(ops))
+ fail("sending Test17");
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+void
+TestDataStructuresChild::Test18()
+{
+ const int nelements = 1000;
+ RegionArray ra;
+ // big enough to hopefully force a realloc to a different chunk of
+ // memory on the receiving side, if the workaround isn't working
+ // correctly. But SetCapacity() here because we don't want to
+ // crash on the sending side.
+ ra.SetCapacity(nelements);
+ for (int i = 0; i < nelements; ++i) {
+ nsIntRegion r;
+ r.Or(nsIntRect(0, 0, 10, 10), nsIntRect(10, 10, 10, 10));
+ ra.AppendElement(r);
+ }
+
+ if (!SendTest18(ra))
+ fail("sending Test18");
+
+ printf(" passed %s\n", __FUNCTION__);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestDataStructures.h b/ipc/ipdl/test/cxx/TestDataStructures.h
new file mode 100644
index 0000000000..f77cfa100a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDataStructures.h
@@ -0,0 +1,234 @@
+#ifndef mozilla__ipdltest_TestDataStructures_h
+#define mozilla__ipdltest_TestDataStructures_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestDataStructuresParent.h"
+#include "mozilla/_ipdltest/PTestDataStructuresChild.h"
+
+#include "mozilla/_ipdltest/PTestDataStructuresSubParent.h"
+#include "mozilla/_ipdltest/PTestDataStructuresSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Subprotocol actors
+
+class TestDataStructuresSub :
+ public PTestDataStructuresSubParent,
+ public PTestDataStructuresSubChild
+{
+public:
+ explicit TestDataStructuresSub(uint32_t i) : mI(i)
+ { }
+ virtual ~TestDataStructuresSub()
+ { }
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (Deletion != why)
+ fail("unexpected destruction!");
+ }
+ uint32_t mI;
+};
+
+//-----------------------------------------------------------------------------
+// Main actors
+
+class TestDataStructuresParent :
+ public PTestDataStructuresParent
+{
+public:
+ TestDataStructuresParent();
+ virtual ~TestDataStructuresParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual PTestDataStructuresSubParent* AllocPTestDataStructuresSubParent(const int& i) override
+ {
+ PTestDataStructuresSubParent* actor = new TestDataStructuresSub(i);
+ mKids.AppendElement(actor);
+ return actor;
+ }
+
+ virtual bool DeallocPTestDataStructuresSubParent(PTestDataStructuresSubParent* actor) override;
+
+ virtual bool RecvTest1(
+ InfallibleTArray<int>&& i1,
+ InfallibleTArray<int>* o1) override;
+
+ virtual bool RecvTest2(
+ InfallibleTArray<PTestDataStructuresSubParent*>&& i1,
+ InfallibleTArray<PTestDataStructuresSubParent*>* o1) override;
+
+ virtual bool RecvTest3(
+ const IntDouble& i1,
+ const IntDouble& i2,
+ IntDouble* o1,
+ IntDouble* o2) override;
+
+ virtual bool RecvTest4(
+ InfallibleTArray<IntDouble>&& i1,
+ InfallibleTArray<IntDouble>* o1) override;
+
+ virtual bool RecvTest5(
+ const IntDoubleArrays& i1,
+ const IntDoubleArrays& i2,
+ const IntDoubleArrays& i3,
+ IntDoubleArrays* o1,
+ IntDoubleArrays* o2,
+ IntDoubleArrays* o3) override;
+
+ virtual bool RecvTest6(
+ InfallibleTArray<IntDoubleArrays>&& i1,
+ InfallibleTArray<IntDoubleArrays>* o1) override;
+
+
+ virtual bool RecvTest7_0(const ActorWrapper& i1,
+ ActorWrapper* o1) override;
+
+ virtual bool RecvTest7(
+ const Actors& i1,
+ const Actors& i2,
+ const Actors& i3,
+ Actors* o1,
+ Actors* o2,
+ Actors* o3) override;
+
+ virtual bool RecvTest8(
+ InfallibleTArray<Actors>&& i1,
+ InfallibleTArray<Actors>* o1) override;
+
+ virtual bool RecvTest9(
+ const Unions& i1,
+ const Unions& i2,
+ const Unions& i3,
+ const Unions& i4,
+ Unions* o1,
+ Unions* o2,
+ Unions* o3,
+ Unions* o4) override;
+
+ virtual bool RecvTest10(
+ InfallibleTArray<Unions>&& i1,
+ InfallibleTArray<Unions>* o1) override;
+
+ virtual bool RecvTest11(
+ const SIntDouble& i,
+ SIntDouble* o) override;
+
+ virtual bool RecvTest12(
+ const SIntDoubleArrays& i,
+ SIntDoubleArrays* o) override;
+
+ virtual bool RecvTest13(
+ const SActors& i,
+ SActors* o) override;
+
+ virtual bool RecvTest14(
+ const Structs& i,
+ Structs* o) override;
+
+ virtual bool RecvTest15(
+ const WithStructs& i1,
+ const WithStructs& i2,
+ const WithStructs& i3,
+ const WithStructs& i4,
+ const WithStructs& i5,
+ WithStructs* o1,
+ WithStructs* o2,
+ WithStructs* o3,
+ WithStructs* o4,
+ WithStructs* o5) override;
+
+ virtual bool RecvTest16(
+ const WithUnions& i,
+ WithUnions* o) override;
+
+ virtual bool RecvTest17(InfallibleTArray<Op>&& sa) override;
+
+ virtual bool RecvTest18(InfallibleTArray<nsIntRegion>&& ra) override;
+
+ virtual bool RecvDummy(const ShmemUnion& su, ShmemUnion* rsu) override
+ {
+ *rsu = su;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ InfallibleTArray<PTestDataStructuresSubParent*> mKids;
+};
+
+
+class TestDataStructuresChild :
+ public PTestDataStructuresChild
+{
+public:
+ TestDataStructuresChild();
+ virtual ~TestDataStructuresChild();
+
+protected:
+ virtual PTestDataStructuresSubChild* AllocPTestDataStructuresSubChild(const int& i) override
+ {
+ PTestDataStructuresSubChild* actor = new TestDataStructuresSub(i);
+ mKids.AppendElement(actor);
+ return actor;
+ }
+
+ virtual bool DeallocPTestDataStructuresSubChild(PTestDataStructuresSubChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual bool RecvStart() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
+private:
+ void Test1();
+ void Test2();
+ void Test3();
+ void Test4();
+ void Test5();
+ void Test6();
+ void Test7_0();
+ void Test7();
+ void Test8();
+ void Test9();
+ void Test10();
+ void Test11();
+ void Test12();
+ void Test13();
+ void Test14();
+ void Test15();
+ void Test16();
+ void Test17();
+ void Test18();
+
+ InfallibleTArray<PTestDataStructuresSubChild*> mKids;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestDataStructures_h
diff --git a/ipc/ipdl/test/cxx/TestDemon.cpp b/ipc/ipdl/test/cxx/TestDemon.cpp
new file mode 100644
index 0000000000..c349aafbe8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDemon.cpp
@@ -0,0 +1,417 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=4 ts=4 et :
+ */
+#include "TestDemon.h"
+
+#include <stdlib.h>
+
+#include "IPDLUnitTests.h" // fail etc.
+#if defined(OS_POSIX)
+#include <sys/time.h>
+#include <unistd.h>
+#else
+#include <time.h>
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace _ipdltest {
+
+const int kMaxStackHeight = 4;
+
+static LazyLogModule sLogModule("demon");
+
+#define DEMON_LOG(...) MOZ_LOG(sLogModule, LogLevel::Debug, (__VA_ARGS__))
+
+static int gStackHeight = 0;
+static bool gFlushStack = false;
+
+static int
+Choose(int count)
+{
+#if defined(OS_POSIX)
+ return random() % count;
+#else
+ return rand() % count;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestDemonParent::TestDemonParent()
+ : mDone(false),
+ mIncoming(),
+ mOutgoing()
+{
+ MOZ_COUNT_CTOR(TestDemonParent);
+}
+
+TestDemonParent::~TestDemonParent()
+{
+ MOZ_COUNT_DTOR(TestDemonParent);
+}
+
+void
+TestDemonParent::Main()
+{
+ if (!getenv("MOZ_TEST_IPC_DEMON")) {
+ QuitParent();
+ return;
+ }
+#if defined(OS_POSIX)
+ srandom(time(nullptr));
+#else
+ srand(time(nullptr));
+#endif
+
+ DEMON_LOG("Start demon");
+
+ if (!SendStart())
+ fail("sending Start");
+
+ RunUnlimitedSequence();
+}
+
+#ifdef DEBUG
+bool
+TestDemonParent::ShouldContinueFromReplyTimeout()
+{
+ return Choose(2) == 0;
+}
+
+bool
+TestDemonParent::ArtificialTimeout()
+{
+ return Choose(5) == 0;
+}
+
+void
+TestDemonParent::ArtificialSleep()
+{
+ if (Choose(2) == 0) {
+ // Sleep for anywhere from 0 to 100 milliseconds.
+ unsigned micros = Choose(100) * 1000;
+#ifdef OS_POSIX
+ usleep(micros);
+#else
+ Sleep(micros / 1000);
+#endif
+ }
+}
+#endif
+
+bool
+TestDemonParent::RecvAsyncMessage(const int& n)
+{
+ DEMON_LOG("Start RecvAsync [%d]", n);
+
+ MOZ_ASSERT(n == mIncoming[0]);
+ mIncoming[0]++;
+
+ RunLimitedSequence();
+
+ DEMON_LOG("End RecvAsync [%d]", n);
+ return true;
+}
+
+bool
+TestDemonParent::RecvHiPrioSyncMessage()
+{
+ DEMON_LOG("Start RecvHiPrioSyncMessage");
+ RunLimitedSequence();
+ DEMON_LOG("End RecvHiPrioSyncMessage");
+ return true;
+}
+
+bool
+TestDemonParent::RecvSyncMessage(const int& n)
+{
+ DEMON_LOG("Start RecvSync [%d]", n);
+
+ MOZ_ASSERT(n == mIncoming[0]);
+ mIncoming[0]++;
+
+ RunLimitedSequence(ASYNC_ONLY);
+
+ DEMON_LOG("End RecvSync [%d]", n);
+ return true;
+}
+
+bool
+TestDemonParent::RecvUrgentAsyncMessage(const int& n)
+{
+ DEMON_LOG("Start RecvUrgentAsyncMessage [%d]", n);
+
+ MOZ_ASSERT(n == mIncoming[2]);
+ mIncoming[2]++;
+
+ RunLimitedSequence(ASYNC_ONLY);
+
+ DEMON_LOG("End RecvUrgentAsyncMessage [%d]", n);
+ return true;
+}
+
+bool
+TestDemonParent::RecvUrgentSyncMessage(const int& n)
+{
+ DEMON_LOG("Start RecvUrgentSyncMessage [%d]", n);
+
+ MOZ_ASSERT(n == mIncoming[2]);
+ mIncoming[2]++;
+
+ RunLimitedSequence(ASYNC_ONLY);
+
+ DEMON_LOG("End RecvUrgentSyncMessage [%d]", n);
+ return true;
+}
+
+void
+TestDemonParent::RunUnlimitedSequence()
+{
+ if (mDone) {
+ return;
+ }
+
+ gFlushStack = false;
+ DoAction();
+
+ MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(this, &TestDemonParent::RunUnlimitedSequence));
+}
+
+void
+TestDemonParent::RunLimitedSequence(int flags)
+{
+ if (gStackHeight >= kMaxStackHeight) {
+ return;
+ }
+ gStackHeight++;
+
+ int count = Choose(20);
+ for (int i = 0; i < count; i++) {
+ if (!DoAction(flags)) {
+ gFlushStack = true;
+ }
+ if (gFlushStack) {
+ gStackHeight--;
+ return;
+ }
+ }
+
+ gStackHeight--;
+}
+
+static bool
+AllowAsync(int outgoing, int incoming)
+{
+ return incoming >= outgoing - 5;
+}
+
+bool
+TestDemonParent::DoAction(int flags)
+{
+ if (flags & ASYNC_ONLY) {
+ if (AllowAsync(mOutgoing[0], mIncoming[0])) {
+ DEMON_LOG("SendAsyncMessage [%d]", mOutgoing[0]);
+ return SendAsyncMessage(mOutgoing[0]++);
+ } else {
+ return true;
+ }
+ } else {
+ switch (Choose(3)) {
+ case 0:
+ if (AllowAsync(mOutgoing[0], mIncoming[0])) {
+ DEMON_LOG("SendAsyncMessage [%d]", mOutgoing[0]);
+ return SendAsyncMessage(mOutgoing[0]++);
+ } else {
+ return true;
+ }
+
+ case 1: {
+ DEMON_LOG("Start SendHiPrioSyncMessage");
+ bool r = SendHiPrioSyncMessage();
+ DEMON_LOG("End SendHiPrioSyncMessage result=%d", r);
+ return r;
+ }
+
+ case 2:
+ DEMON_LOG("Cancel");
+ GetIPCChannel()->CancelCurrentTransaction();
+ return true;
+ }
+ }
+ MOZ_CRASH();
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+
+TestDemonChild::TestDemonChild()
+ : mIncoming(),
+ mOutgoing()
+{
+ MOZ_COUNT_CTOR(TestDemonChild);
+}
+
+TestDemonChild::~TestDemonChild()
+{
+ MOZ_COUNT_DTOR(TestDemonChild);
+}
+
+bool
+TestDemonChild::RecvStart()
+{
+#ifdef OS_POSIX
+ srandom(time(nullptr));
+#else
+ srand(time(nullptr));
+#endif
+
+ DEMON_LOG("RecvStart");
+
+ RunUnlimitedSequence();
+ return true;
+}
+
+#ifdef DEBUG
+void
+TestDemonChild::ArtificialSleep()
+{
+ if (Choose(2) == 0) {
+ // Sleep for anywhere from 0 to 100 milliseconds.
+ unsigned micros = Choose(100) * 1000;
+#ifdef OS_POSIX
+ usleep(micros);
+#else
+ Sleep(micros / 1000);
+#endif
+ }
+}
+#endif
+
+bool
+TestDemonChild::RecvAsyncMessage(const int& n)
+{
+ DEMON_LOG("Start RecvAsyncMessage [%d]", n);
+
+ MOZ_ASSERT(n == mIncoming[0]);
+ mIncoming[0]++;
+
+ RunLimitedSequence();
+
+ DEMON_LOG("End RecvAsyncMessage [%d]", n);
+ return true;
+}
+
+bool
+TestDemonChild::RecvHiPrioSyncMessage()
+{
+ DEMON_LOG("Start RecvHiPrioSyncMessage");
+ RunLimitedSequence();
+ DEMON_LOG("End RecvHiPrioSyncMessage");
+ return true;
+}
+
+void
+TestDemonChild::RunUnlimitedSequence()
+{
+ gFlushStack = false;
+ DoAction();
+
+ MessageLoop::current()->PostTask(NewNonOwningRunnableMethod(this, &TestDemonChild::RunUnlimitedSequence));
+}
+
+void
+TestDemonChild::RunLimitedSequence()
+{
+ if (gStackHeight >= kMaxStackHeight) {
+ return;
+ }
+ gStackHeight++;
+
+ int count = Choose(20);
+ for (int i = 0; i < count; i++) {
+ if (!DoAction()) {
+ gFlushStack = true;
+ }
+ if (gFlushStack) {
+ gStackHeight--;
+ return;
+ }
+ }
+
+ gStackHeight--;
+}
+
+bool
+TestDemonChild::DoAction()
+{
+ switch (Choose(6)) {
+ case 0:
+ if (AllowAsync(mOutgoing[0], mIncoming[0])) {
+ DEMON_LOG("SendAsyncMessage [%d]", mOutgoing[0]);
+ return SendAsyncMessage(mOutgoing[0]++);
+ } else {
+ return true;
+ }
+
+ case 1: {
+ DEMON_LOG("Start SendHiPrioSyncMessage");
+ bool r = SendHiPrioSyncMessage();
+ DEMON_LOG("End SendHiPrioSyncMessage result=%d", r);
+ return r;
+ }
+
+ case 2: {
+ DEMON_LOG("Start SendSyncMessage [%d]", mOutgoing[0]);
+ bool r = SendSyncMessage(mOutgoing[0]++);
+ switch (GetIPCChannel()->LastSendError()) {
+ case SyncSendError::PreviousTimeout:
+ case SyncSendError::SendingCPOWWhileDispatchingSync:
+ case SyncSendError::SendingCPOWWhileDispatchingUrgent:
+ case SyncSendError::NotConnectedBeforeSend:
+ case SyncSendError::CancelledBeforeSend:
+ mOutgoing[0]--;
+ break;
+ default:
+ break;
+ }
+ DEMON_LOG("End SendSyncMessage result=%d", r);
+ return r;
+ }
+
+ case 3:
+ DEMON_LOG("SendUrgentAsyncMessage [%d]", mOutgoing[2]);
+ return SendUrgentAsyncMessage(mOutgoing[2]++);
+
+ case 4: {
+ DEMON_LOG("Start SendUrgentSyncMessage [%d]", mOutgoing[2]);
+ bool r = SendUrgentSyncMessage(mOutgoing[2]++);
+ switch (GetIPCChannel()->LastSendError()) {
+ case SyncSendError::PreviousTimeout:
+ case SyncSendError::SendingCPOWWhileDispatchingSync:
+ case SyncSendError::SendingCPOWWhileDispatchingUrgent:
+ case SyncSendError::NotConnectedBeforeSend:
+ case SyncSendError::CancelledBeforeSend:
+ mOutgoing[2]--;
+ break;
+ default:
+ break;
+ }
+ DEMON_LOG("End SendUrgentSyncMessage result=%d", r);
+ return r;
+ }
+
+ case 5:
+ DEMON_LOG("Cancel");
+ GetIPCChannel()->CancelCurrentTransaction();
+ return true;
+ }
+ MOZ_CRASH();
+ return false;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestDemon.h b/ipc/ipdl/test/cxx/TestDemon.h
new file mode 100644
index 0000000000..d481039a60
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDemon.h
@@ -0,0 +1,106 @@
+#ifndef mozilla__ipdltest_TestDemon_h
+#define mozilla__ipdltest_TestDemon_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestDemonParent.h"
+#include "mozilla/_ipdltest/PTestDemonChild.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestDemonParent :
+ public PTestDemonParent
+{
+public:
+ TestDemonParent();
+ virtual ~TestDemonParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+#ifdef DEBUG
+ bool ShouldContinueFromReplyTimeout() override;
+ bool ArtificialTimeout() override;
+
+ bool NeedArtificialSleep() override { return true; }
+ void ArtificialSleep() override;
+#endif
+
+ bool RecvAsyncMessage(const int& n) override;
+ bool RecvHiPrioSyncMessage() override;
+
+ bool RecvSyncMessage(const int& n) override;
+ bool RecvUrgentAsyncMessage(const int& n) override;
+ bool RecvUrgentSyncMessage(const int& n) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ mDone = true;
+ printf("Parent ActorDestroy\n");
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ bool mDone;
+ int mIncoming[3];
+ int mOutgoing[3];
+
+ enum {
+ ASYNC_ONLY = 1,
+ };
+
+ void RunUnlimitedSequence();
+ void RunLimitedSequence(int flags = 0);
+ bool DoAction(int flags = 0);
+};
+
+
+class TestDemonChild :
+ public PTestDemonChild
+{
+public:
+ TestDemonChild();
+ virtual ~TestDemonChild();
+
+ bool RecvStart() override;
+
+#ifdef DEBUG
+ bool NeedArtificialSleep() override { return true; }
+ void ArtificialSleep() override;
+#endif
+
+ bool RecvAsyncMessage(const int& n) override;
+ bool RecvHiPrioSyncMessage() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ _exit(0);
+ }
+
+ virtual void IntentionalCrash() override
+ {
+ _exit(0);
+ }
+
+private:
+ int mIncoming[3];
+ int mOutgoing[3];
+
+ void RunUnlimitedSequence();
+ void RunLimitedSequence();
+ bool DoAction();
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestDemon_h
diff --git a/ipc/ipdl/test/cxx/TestDesc.cpp b/ipc/ipdl/test/cxx/TestDesc.cpp
new file mode 100644
index 0000000000..696e5a4c5c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDesc.cpp
@@ -0,0 +1,105 @@
+#include "TestDesc.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+void
+TestDescParent::Main()
+{
+ PTestDescSubParent* p = CallPTestDescSubConstructor(0);
+ if (!p)
+ fail("can't allocate Sub");
+
+ PTestDescSubsubParent* pp = p->CallPTestDescSubsubConstructor();
+ if (!pp)
+ fail("can't allocate Subsub");
+
+ if (!SendTest(pp))
+ fail("can't send Subsub");
+}
+
+bool
+TestDescParent::RecvOk(PTestDescSubsubParent* a)
+{
+ if (!a)
+ fail("didn't receive Subsub");
+
+ if (!PTestDescSubsubParent::Call__delete__(a))
+ fail("deleting Subsub");
+
+ Close();
+
+ return true;
+}
+
+
+PTestDescSubParent*
+TestDescParent::AllocPTestDescSubParent(PTestDescSubsubParent* dummy) {
+ if (dummy)
+ fail("actor supposed to be null");
+ return new TestDescSubParent();
+}
+bool
+TestDescParent::DeallocPTestDescSubParent(PTestDescSubParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+PTestDescSubsubParent*
+TestDescSubParent::AllocPTestDescSubsubParent()
+{
+ return new TestDescSubsubParent();
+}
+bool
+TestDescSubParent::DeallocPTestDescSubsubParent(PTestDescSubsubParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+bool
+TestDescChild::RecvTest(PTestDescSubsubChild* a)
+{
+ if (!a)
+ fail("didn't receive Subsub");
+ if (!SendOk(a))
+ fail("couldn't send Ok()");
+ return true;
+}
+
+PTestDescSubChild*
+TestDescChild::AllocPTestDescSubChild(PTestDescSubsubChild* dummy) {
+ if (dummy)
+ fail("actor supposed to be null");
+ return new TestDescSubChild();
+}
+bool
+TestDescChild::DeallocPTestDescSubChild(PTestDescSubChild* actor)
+{
+ delete actor;
+ return true;
+}
+
+PTestDescSubsubChild*
+TestDescSubChild::AllocPTestDescSubsubChild()
+{
+ return new TestDescSubsubChild();
+}
+bool
+TestDescSubChild::DeallocPTestDescSubsubChild(PTestDescSubsubChild* actor)
+{
+ delete actor;
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestDesc.h b/ipc/ipdl/test/cxx/TestDesc.h
new file mode 100644
index 0000000000..876de72011
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestDesc.h
@@ -0,0 +1,128 @@
+#ifndef mozilla_ipdltest_TestDesc_h
+#define mozilla_ipdltest_TestDesc_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestDescParent.h"
+#include "mozilla/_ipdltest/PTestDescChild.h"
+
+#include "mozilla/_ipdltest/PTestDescSubParent.h"
+#include "mozilla/_ipdltest/PTestDescSubChild.h"
+
+#include "mozilla/_ipdltest/PTestDescSubsubParent.h"
+#include "mozilla/_ipdltest/PTestDescSubsubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Top-level
+//
+class TestDescParent :
+ public PTestDescParent
+{
+public:
+ TestDescParent() { }
+ virtual ~TestDescParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+ virtual bool RecvOk(PTestDescSubsubParent* a) override;
+
+protected:
+ virtual PTestDescSubParent* AllocPTestDescSubParent(PTestDescSubsubParent*) override;
+ virtual bool DeallocPTestDescSubParent(PTestDescSubParent* actor) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestDescChild :
+ public PTestDescChild
+{
+public:
+ TestDescChild() { }
+ virtual ~TestDescChild() { }
+
+protected:
+ virtual PTestDescSubChild* AllocPTestDescSubChild(PTestDescSubsubChild*) override;
+
+ virtual bool DeallocPTestDescSubChild(PTestDescSubChild* actor) override;
+
+ virtual bool RecvTest(PTestDescSubsubChild* a) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// First descendent
+//
+class TestDescSubParent :
+ public PTestDescSubParent
+{
+public:
+ TestDescSubParent() { }
+ virtual ~TestDescSubParent() { }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+ virtual PTestDescSubsubParent* AllocPTestDescSubsubParent() override;
+ virtual bool DeallocPTestDescSubsubParent(PTestDescSubsubParent* actor) override;
+};
+
+
+class TestDescSubChild :
+ public PTestDescSubChild
+{
+public:
+ TestDescSubChild() { }
+ virtual ~TestDescSubChild() { }
+
+protected:
+ virtual PTestDescSubsubChild* AllocPTestDescSubsubChild() override;
+ virtual bool DeallocPTestDescSubsubChild(PTestDescSubsubChild* actor) override;
+};
+
+
+//-----------------------------------------------------------------------------
+// Grand-descendent
+//
+class TestDescSubsubParent :
+ public PTestDescSubsubParent
+{
+public:
+ TestDescSubsubParent() { }
+ virtual ~TestDescSubsubParent() { }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+class TestDescSubsubChild :
+ public PTestDescSubsubChild
+{
+public:
+ TestDescSubsubChild() { }
+ virtual ~TestDescSubsubChild() { }
+};
+
+
+}
+}
+
+#endif // ifndef mozilla_ipdltest_TestDesc_h
diff --git a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp
new file mode 100644
index 0000000000..1b646f4257
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+#include "TestEndpointBridgeMain.h"
+
+#include "base/task.h"
+#include "IPDLUnitTests.h" // fail etc.
+#include "IPDLUnitTestSubprocess.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+//-----------------------------------------------------------------------------
+// main process
+void
+TestEndpointBridgeMainParent::Main()
+{
+ if (!SendStart()) {
+ fail("sending Start");
+ }
+}
+
+bool
+TestEndpointBridgeMainParent::RecvBridged(Endpoint<PTestEndpointBridgeMainSubParent>&& endpoint)
+{
+ TestEndpointBridgeMainSubParent* a = new TestEndpointBridgeMainSubParent();
+ if (!endpoint.Bind(a)) {
+ fail("Bind failed");
+ }
+ return true;
+}
+
+void
+TestEndpointBridgeMainParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+ passed("ok");
+ QuitParent();
+}
+
+bool
+TestEndpointBridgeMainSubParent::RecvHello()
+{
+ return SendHi();
+}
+
+bool
+TestEndpointBridgeMainSubParent::RecvHelloSync()
+{
+ return true;
+}
+
+bool
+TestEndpointBridgeMainSubParent::AnswerHelloRpc()
+{
+ return CallHiRpc();
+}
+
+void
+TestEndpointBridgeMainSubParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ MessageLoop::current()->PostTask(
+ do_AddRef(new DeleteTask<TestEndpointBridgeMainSubParent>(this)));
+}
+
+//-----------------------------------------------------------------------------
+// sub process --- child of main
+TestEndpointBridgeMainChild* gEndpointBridgeMainChild;
+
+TestEndpointBridgeMainChild::TestEndpointBridgeMainChild()
+ : mSubprocess(nullptr)
+{
+ gEndpointBridgeMainChild = this;
+}
+
+bool
+TestEndpointBridgeMainChild::RecvStart()
+{
+ vector<string> subsubArgs;
+ subsubArgs.push_back("TestEndpointBridgeSub");
+
+ mSubprocess = new IPDLUnitTestSubprocess();
+ if (!mSubprocess->SyncLaunch(subsubArgs)) {
+ fail("problem launching subprocess");
+ }
+
+ IPC::Channel* transport = mSubprocess->GetChannel();
+ if (!transport) {
+ fail("no transport");
+ }
+
+ TestEndpointBridgeSubParent* bsp = new TestEndpointBridgeSubParent();
+ bsp->Open(transport, base::GetProcId(mSubprocess->GetChildProcessHandle()));
+
+ bsp->Main();
+ return true;
+}
+
+void
+TestEndpointBridgeMainChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+ // NB: this is kosher because QuitChild() joins with the IO thread
+ XRE_GetIOMessageLoop()->PostTask(
+ do_AddRef(new DeleteTask<IPDLUnitTestSubprocess>(mSubprocess)));
+ QuitChild();
+}
+
+void
+TestEndpointBridgeSubParent::Main()
+{
+ if (!SendPing()) {
+ fail("sending Ping");
+ }
+}
+
+bool
+TestEndpointBridgeSubParent::RecvBridgeEm()
+{
+ Endpoint<PTestEndpointBridgeMainSubParent> parent;
+ Endpoint<PTestEndpointBridgeMainSubChild> child;
+ nsresult rv;
+ rv = PTestEndpointBridgeMainSub::CreateEndpoints(
+ gEndpointBridgeMainChild->OtherPid(), OtherPid(),
+ &parent, &child);
+ if (NS_FAILED(rv)) {
+ fail("opening PTestEndpointOpensOpened");
+ }
+
+ if (!gEndpointBridgeMainChild->SendBridged(mozilla::Move(parent))) {
+ fail("SendBridge failed for parent");
+ }
+ if (!SendBridged(mozilla::Move(child))) {
+ fail("SendBridge failed for child");
+ }
+
+ return true;
+}
+
+void
+TestEndpointBridgeSubParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+ gEndpointBridgeMainChild->Close();
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ MessageLoop::current()->PostTask(
+ do_AddRef(new DeleteTask<TestEndpointBridgeSubParent>(this)));
+}
+
+//-----------------------------------------------------------------------------
+// subsub process --- child of sub
+
+static TestEndpointBridgeSubChild* gBridgeSubChild;
+
+TestEndpointBridgeSubChild::TestEndpointBridgeSubChild()
+{
+ gBridgeSubChild = this;
+}
+
+bool
+TestEndpointBridgeSubChild::RecvPing()
+{
+ if (!SendBridgeEm()) {
+ fail("sending BridgeEm");
+ }
+ return true;
+}
+
+bool
+TestEndpointBridgeSubChild::RecvBridged(Endpoint<PTestEndpointBridgeMainSubChild>&& endpoint)
+{
+ TestEndpointBridgeMainSubChild* a = new TestEndpointBridgeMainSubChild();
+
+ if (!endpoint.Bind(a)) {
+ fail("failed to Bind");
+ }
+
+ if (!a->SendHello()) {
+ fail("sending Hello");
+ }
+
+ return true;
+}
+
+void
+TestEndpointBridgeSubChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+ QuitChild();
+}
+
+bool
+TestEndpointBridgeMainSubChild::RecvHi()
+{
+ if (!SendHelloSync()) {
+ fail("sending HelloSync");
+ }
+ if (!CallHelloRpc()) {
+ fail("calling HelloRpc");
+ }
+ if (!mGotHi) {
+ fail("didn't answer HiRpc");
+ }
+
+ // Need to close the channel without message-processing frames on
+ // the C++ stack
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestEndpointBridgeMainSubChild::Close));
+ return true;
+}
+
+bool
+TestEndpointBridgeMainSubChild::AnswerHiRpc()
+{
+ mGotHi = true; // d00d
+ return true;
+}
+
+void
+TestEndpointBridgeMainSubChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (NormalShutdown != why) {
+ fail("unexpected destruction!");
+ }
+
+ gBridgeSubChild->Close();
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ MessageLoop::current()->PostTask(
+ do_AddRef(new DeleteTask<TestEndpointBridgeMainSubChild>(this)));
+}
+
+} // namespace mozilla
+} // namespace _ipdltest
diff --git a/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h
new file mode 100644
index 0000000000..a180ba8ebc
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestEndpointBridgeMain.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+#ifndef mozilla__ipdltest_TestEndpointBridgeMain_h
+#define mozilla__ipdltest_TestEndpointBridgeMain_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestEndpointBridgeMainParent.h"
+#include "mozilla/_ipdltest/PTestEndpointBridgeMainChild.h"
+
+#include "mozilla/_ipdltest/PTestEndpointBridgeSubParent.h"
+#include "mozilla/_ipdltest/PTestEndpointBridgeSubChild.h"
+
+#include "mozilla/_ipdltest/PTestEndpointBridgeMainSubParent.h"
+#include "mozilla/_ipdltest/PTestEndpointBridgeMainSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// "Main" process
+//
+class TestEndpointBridgeMainParent :
+ public PTestEndpointBridgeMainParent
+{
+public:
+ TestEndpointBridgeMainParent() {}
+ virtual ~TestEndpointBridgeMainParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ bool RecvBridged(mozilla::ipc::Endpoint<PTestEndpointBridgeMainSubParent>&& endpoint) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+class TestEndpointBridgeMainSubParent :
+ public PTestEndpointBridgeMainSubParent
+{
+public:
+ explicit TestEndpointBridgeMainSubParent()
+ {}
+ virtual ~TestEndpointBridgeMainSubParent() {}
+
+protected:
+ virtual bool RecvHello() override;
+ virtual bool RecvHelloSync() override;
+ virtual bool AnswerHelloRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+//-----------------------------------------------------------------------------
+// "Sub" process --- child of "main"
+//
+class TestEndpointBridgeSubParent;
+
+class TestEndpointBridgeMainChild :
+ public PTestEndpointBridgeMainChild
+{
+public:
+ TestEndpointBridgeMainChild();
+ virtual ~TestEndpointBridgeMainChild() {}
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ IPDLUnitTestSubprocess* mSubprocess;
+};
+
+class TestEndpointBridgeSubParent :
+ public PTestEndpointBridgeSubParent
+{
+public:
+ TestEndpointBridgeSubParent() {}
+ virtual ~TestEndpointBridgeSubParent() {}
+
+ void Main();
+
+protected:
+ virtual bool RecvBridgeEm() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+//-----------------------------------------------------------------------------
+// "Subsub" process --- child of "sub"
+//
+class TestEndpointBridgeSubChild :
+ public PTestEndpointBridgeSubChild
+{
+public:
+ TestEndpointBridgeSubChild();
+ virtual ~TestEndpointBridgeSubChild() {}
+
+protected:
+ virtual bool RecvPing() override;
+
+ bool RecvBridged(Endpoint<PTestEndpointBridgeMainSubChild>&& endpoint) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+class TestEndpointBridgeMainSubChild :
+ public PTestEndpointBridgeMainSubChild
+{
+public:
+ explicit TestEndpointBridgeMainSubChild()
+ : mGotHi(false)
+ {}
+ virtual ~TestEndpointBridgeMainSubChild() {}
+
+protected:
+ virtual bool RecvHi() override;
+ virtual bool AnswerHiRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ bool mGotHi;
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestEndpointBridgeMain_h
diff --git a/ipc/ipdl/test/cxx/TestEndpointOpens.cpp b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp
new file mode 100644
index 0000000000..820273ad77
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestEndpointOpens.cpp
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+#include "base/task.h"
+#include "base/thread.h"
+
+#include "TestEndpointOpens.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using namespace mozilla::ipc;
+
+using base::ProcessHandle;
+using base::Thread;
+
+namespace mozilla {
+// NB: this is generally bad style, but I am lazy.
+using namespace _ipdltest;
+using namespace _ipdltest2;
+
+static MessageLoop* gMainThread;
+
+static void
+AssertNotMainThread()
+{
+ if (!gMainThread) {
+ fail("gMainThread is not initialized");
+ }
+ if (MessageLoop::current() == gMainThread) {
+ fail("unexpectedly called on the main thread");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+// Thread on which TestEndpointOpensOpenedParent runs
+static Thread* gParentThread;
+
+void
+TestEndpointOpensParent::Main()
+{
+ if (!SendStart()) {
+ fail("sending Start");
+ }
+}
+
+static void
+OpenParent(TestEndpointOpensOpenedParent* aParent,
+ Endpoint<PTestEndpointOpensOpenedParent>&& aEndpoint)
+{
+ AssertNotMainThread();
+
+ // Open the actor on the off-main thread to park it there.
+ // Messages will be delivered to this thread's message loop
+ // instead of the main thread's.
+ if (!aEndpoint.Bind(aParent)) {
+ fail("binding Parent");
+ }
+}
+
+bool
+TestEndpointOpensParent::RecvStartSubprotocol(
+ mozilla::ipc::Endpoint<PTestEndpointOpensOpenedParent>&& endpoint)
+{
+ gMainThread = MessageLoop::current();
+
+ gParentThread = new Thread("ParentThread");
+ if (!gParentThread->Start()) {
+ fail("starting parent thread");
+ }
+
+ TestEndpointOpensOpenedParent* a = new TestEndpointOpensOpenedParent();
+ gParentThread->message_loop()->PostTask(
+ NewRunnableFunction(OpenParent, a, mozilla::Move(endpoint)));
+
+ return true;
+}
+
+void
+TestEndpointOpensParent::ActorDestroy(ActorDestroyReason why)
+{
+ // Stops the thread and joins it
+ delete gParentThread;
+
+ if (NormalShutdown != why) {
+ fail("unexpected destruction A!");
+ }
+ passed("ok");
+ QuitParent();
+}
+
+bool
+TestEndpointOpensOpenedParent::RecvHello()
+{
+ AssertNotMainThread();
+ return SendHi();
+}
+
+bool
+TestEndpointOpensOpenedParent::RecvHelloSync()
+{
+ AssertNotMainThread();
+ return true;
+}
+
+bool
+TestEndpointOpensOpenedParent::AnswerHelloRpc()
+{
+ AssertNotMainThread();
+ return CallHiRpc();
+}
+
+static void
+ShutdownTestEndpointOpensOpenedParent(TestEndpointOpensOpenedParent* parent,
+ Transport* transport)
+{
+ delete parent;
+}
+
+void
+TestEndpointOpensOpenedParent::ActorDestroy(ActorDestroyReason why)
+{
+ AssertNotMainThread();
+
+ if (NormalShutdown != why) {
+ fail("unexpected destruction B!");
+ }
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ gParentThread->message_loop()->PostTask(
+ NewRunnableFunction(ShutdownTestEndpointOpensOpenedParent,
+ this, GetTransport()));
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+static TestEndpointOpensChild* gOpensChild;
+// Thread on which TestEndpointOpensOpenedChild runs
+static Thread* gChildThread;
+
+TestEndpointOpensChild::TestEndpointOpensChild()
+{
+ gOpensChild = this;
+}
+
+static void
+OpenChild(TestEndpointOpensOpenedChild* aChild,
+ Endpoint<PTestEndpointOpensOpenedChild>&& endpoint)
+{
+ AssertNotMainThread();
+
+ // Open the actor on the off-main thread to park it there.
+ // Messages will be delivered to this thread's message loop
+ // instead of the main thread's.
+ if (!endpoint.Bind(aChild)) {
+ fail("binding child endpoint");
+ }
+
+ // Kick off the unit tests
+ if (!aChild->SendHello()) {
+ fail("sending Hello");
+ }
+}
+
+bool
+TestEndpointOpensChild::RecvStart()
+{
+ Endpoint<PTestEndpointOpensOpenedParent> parent;
+ Endpoint<PTestEndpointOpensOpenedChild> child;
+ nsresult rv;
+ rv = PTestEndpointOpensOpened::CreateEndpoints(OtherPid(), base::GetCurrentProcId(),
+ &parent, &child);
+ if (NS_FAILED(rv)) {
+ fail("opening PTestEndpointOpensOpened");
+ }
+
+ gMainThread = MessageLoop::current();
+
+ gChildThread = new Thread("ChildThread");
+ if (!gChildThread->Start()) {
+ fail("starting child thread");
+ }
+
+ TestEndpointOpensOpenedChild* a = new TestEndpointOpensOpenedChild();
+ gChildThread->message_loop()->PostTask(
+ NewRunnableFunction(OpenChild, a, mozilla::Move(child)));
+
+ if (!SendStartSubprotocol(parent)) {
+ fail("send StartSubprotocol");
+ }
+
+ return true;
+}
+
+void
+TestEndpointOpensChild::ActorDestroy(ActorDestroyReason why)
+{
+ // Stops the thread and joins it
+ delete gChildThread;
+
+ if (NormalShutdown != why) {
+ fail("unexpected destruction C!");
+ }
+ QuitChild();
+}
+
+bool
+TestEndpointOpensOpenedChild::RecvHi()
+{
+ AssertNotMainThread();
+
+ if (!SendHelloSync()) {
+ fail("sending HelloSync");
+ }
+ if (!CallHelloRpc()) {
+ fail("calling HelloRpc");
+ }
+ if (!mGotHi) {
+ fail("didn't answer HiRpc");
+ }
+
+ // Need to close the channel without message-processing frames on
+ // the C++ stack
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestEndpointOpensOpenedChild::Close));
+ return true;
+}
+
+bool
+TestEndpointOpensOpenedChild::AnswerHiRpc()
+{
+ AssertNotMainThread();
+
+ mGotHi = true; // d00d
+ return true;
+}
+
+static void
+ShutdownTestEndpointOpensOpenedChild(TestEndpointOpensOpenedChild* child,
+ Transport* transport)
+{
+ delete child;
+
+ // Kick off main-thread shutdown.
+ gMainThread->PostTask(
+ NewNonOwningRunnableMethod(gOpensChild, &TestEndpointOpensChild::Close));
+}
+
+void
+TestEndpointOpensOpenedChild::ActorDestroy(ActorDestroyReason why)
+{
+ AssertNotMainThread();
+
+ if (NormalShutdown != why) {
+ fail("unexpected destruction D!");
+ }
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up. Defer shutdown to
+ // let cleanup finish.
+ gChildThread->message_loop()->PostTask(
+ NewRunnableFunction(ShutdownTestEndpointOpensOpenedChild,
+ this, GetTransport()));
+}
+
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestEndpointOpens.h b/ipc/ipdl/test/cxx/TestEndpointOpens.h
new file mode 100644
index 0000000000..6bfaace2a0
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestEndpointOpens.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+
+#ifndef mozilla__ipdltest_TestEndpointOpens_h
+#define mozilla__ipdltest_TestEndpointOpens_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestEndpointOpensParent.h"
+#include "mozilla/_ipdltest/PTestEndpointOpensChild.h"
+
+#include "mozilla/_ipdltest2/PTestEndpointOpensOpenedParent.h"
+#include "mozilla/_ipdltest2/PTestEndpointOpensOpenedChild.h"
+
+namespace mozilla {
+
+// parent process
+
+namespace _ipdltest {
+
+class TestEndpointOpensParent : public PTestEndpointOpensParent
+{
+public:
+ TestEndpointOpensParent() {}
+ virtual ~TestEndpointOpensParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual bool RecvStartSubprotocol(mozilla::ipc::Endpoint<PTestEndpointOpensOpenedParent>&& endpoint);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest
+
+namespace _ipdltest2 {
+
+class TestEndpointOpensOpenedParent : public PTestEndpointOpensOpenedParent
+{
+public:
+ explicit TestEndpointOpensOpenedParent()
+ {}
+ virtual ~TestEndpointOpensOpenedParent() {}
+
+protected:
+ virtual bool RecvHello() override;
+ virtual bool RecvHelloSync() override;
+ virtual bool AnswerHelloRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest2
+
+// child process
+
+namespace _ipdltest {
+
+class TestEndpointOpensChild : public PTestEndpointOpensChild
+{
+public:
+ TestEndpointOpensChild();
+ virtual ~TestEndpointOpensChild() {}
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest
+
+namespace _ipdltest2 {
+
+class TestEndpointOpensOpenedChild : public PTestEndpointOpensOpenedChild
+{
+public:
+ explicit TestEndpointOpensOpenedChild()
+ : mGotHi(false)
+ {}
+ virtual ~TestEndpointOpensOpenedChild() {}
+
+protected:
+ virtual bool RecvHi() override;
+ virtual bool AnswerHiRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ bool mGotHi;
+};
+
+} // namespace _ipdltest2
+
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestEndpointOpens_h
diff --git a/ipc/ipdl/test/cxx/TestFailedCtor.cpp b/ipc/ipdl/test/cxx/TestFailedCtor.cpp
new file mode 100644
index 0000000000..12ea8fdc11
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestFailedCtor.cpp
@@ -0,0 +1,137 @@
+#include "TestFailedCtor.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+void
+TestFailedCtorParent::Main()
+{
+ PTestFailedCtorSubParent* p = CallPTestFailedCtorSubConstructor();
+ if (p)
+ fail("expected ctor to fail");
+
+ Close();
+}
+
+PTestFailedCtorSubParent*
+TestFailedCtorParent::AllocPTestFailedCtorSubParent()
+{
+ return new TestFailedCtorSubParent();
+}
+bool
+TestFailedCtorParent::DeallocPTestFailedCtorSubParent(PTestFailedCtorSubParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+PTestFailedCtorSubsubParent*
+TestFailedCtorSubParent::AllocPTestFailedCtorSubsubParent()
+{
+ TestFailedCtorSubsub* a = new TestFailedCtorSubsub();
+ if (!mOne) {
+ return mOne = a;
+ } else if (!mTwo) {
+ return mTwo = a;
+ } else if (!mThree) {
+ return mThree = a;
+ } else {
+ fail("unexpected Alloc()");
+ return nullptr;
+ }
+}
+bool
+TestFailedCtorSubParent::DeallocPTestFailedCtorSubsubParent(PTestFailedCtorSubsubParent* actor)
+{
+ static_cast<TestFailedCtorSubsub*>(actor)->mDealloced = true;
+ return true;
+}
+
+void
+TestFailedCtorSubParent::ActorDestroy(ActorDestroyReason why)
+{
+
+ if (mOne->mWhy != Deletion)
+ fail("Subsub one got wrong ActorDestroyReason");
+ if (mTwo->mWhy != AncestorDeletion)
+ fail("Subsub two got wrong ActorDestroyReason");
+ if (mThree->mWhy != AncestorDeletion)
+ fail("Subsub three got wrong ActorDestroyReason");
+
+ if (FailedConstructor != why)
+ fail("unexpected destruction!");
+}
+
+TestFailedCtorSubParent::~TestFailedCtorSubParent()
+{
+ if (!(mOne->mDealloced && mTwo->mDealloced && mThree->mDealloced))
+ fail("Not all subsubs were Dealloc'd");
+ delete mOne;
+ delete mTwo;
+ delete mThree;
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+PTestFailedCtorSubChild*
+TestFailedCtorChild::AllocPTestFailedCtorSubChild()
+{
+ return new TestFailedCtorSubChild();
+}
+
+bool
+TestFailedCtorChild::AnswerPTestFailedCtorSubConstructor(PTestFailedCtorSubChild* actor)
+{
+ PTestFailedCtorSubsubChild* c1 = actor->SendPTestFailedCtorSubsubConstructor();
+ PTestFailedCtorSubsubChild::Send__delete__(c1);
+
+ if (!actor->SendPTestFailedCtorSubsubConstructor() ||
+ !actor->SendPTestFailedCtorSubsubConstructor() ||
+ !actor->SendSync())
+ fail("setting up test");
+
+ // This causes our process to die
+ return false;
+}
+
+bool
+TestFailedCtorChild::DeallocPTestFailedCtorSubChild(PTestFailedCtorSubChild* actor)
+{
+ delete actor;
+ return true;
+}
+
+void
+TestFailedCtorChild::ProcessingError(Result aCode, const char* aReason)
+{
+ if (OtherPid() != base::GetCurrentProcId()) // thread-mode
+ _exit(0);
+}
+
+PTestFailedCtorSubsubChild*
+TestFailedCtorSubChild::AllocPTestFailedCtorSubsubChild()
+{
+ return new TestFailedCtorSubsub();
+}
+
+bool
+TestFailedCtorSubChild::DeallocPTestFailedCtorSubsubChild(PTestFailedCtorSubsubChild* actor)
+{
+ delete actor;
+ return true;
+}
+
+void
+TestFailedCtorSubChild::ActorDestroy(ActorDestroyReason why)
+{
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestFailedCtor.h b/ipc/ipdl/test/cxx/TestFailedCtor.h
new file mode 100644
index 0000000000..97f9013ccc
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestFailedCtor.h
@@ -0,0 +1,136 @@
+#ifndef mozilla_ipdltest_TestFailedCtor_h
+#define mozilla_ipdltest_TestFailedCtor_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestFailedCtorParent.h"
+#include "mozilla/_ipdltest/PTestFailedCtorChild.h"
+
+#include "mozilla/_ipdltest/PTestFailedCtorSubParent.h"
+#include "mozilla/_ipdltest/PTestFailedCtorSubChild.h"
+
+#include "mozilla/_ipdltest/PTestFailedCtorSubsubParent.h"
+#include "mozilla/_ipdltest/PTestFailedCtorSubsubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Top-level
+//
+class TestFailedCtorParent :
+ public PTestFailedCtorParent
+{
+public:
+ TestFailedCtorParent() { }
+ virtual ~TestFailedCtorParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+
+ // FIXME/bug 703322 Disabled because child calls exit() to end
+ // test, not clear how to handle failed ctor in
+ // threaded mode.
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual PTestFailedCtorSubParent* AllocPTestFailedCtorSubParent() override;
+ virtual bool DeallocPTestFailedCtorSubParent(PTestFailedCtorSubParent* actor) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestFailedCtorChild :
+ public PTestFailedCtorChild
+{
+public:
+ TestFailedCtorChild() { }
+ virtual ~TestFailedCtorChild() { }
+
+protected:
+ virtual PTestFailedCtorSubChild* AllocPTestFailedCtorSubChild() override;
+
+ virtual bool AnswerPTestFailedCtorSubConstructor(PTestFailedCtorSubChild* actor) override;
+
+ virtual bool DeallocPTestFailedCtorSubChild(PTestFailedCtorSubChild* actor) override;
+
+ virtual void ProcessingError(Result aCode, const char* aReason) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ fail("should have _exit()ed");
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+// First descendent
+//
+class TestFailedCtorSubsub;
+
+class TestFailedCtorSubParent :
+ public PTestFailedCtorSubParent
+{
+public:
+ TestFailedCtorSubParent() : mOne(nullptr), mTwo(nullptr), mThree(nullptr) { }
+ virtual ~TestFailedCtorSubParent();
+
+protected:
+ virtual PTestFailedCtorSubsubParent* AllocPTestFailedCtorSubsubParent() override;
+
+ virtual bool DeallocPTestFailedCtorSubsubParent(PTestFailedCtorSubsubParent* actor) override;
+ virtual bool RecvSync() override { return true; }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ TestFailedCtorSubsub* mOne;
+ TestFailedCtorSubsub* mTwo;
+ TestFailedCtorSubsub* mThree;
+};
+
+
+class TestFailedCtorSubChild :
+ public PTestFailedCtorSubChild
+{
+public:
+ TestFailedCtorSubChild() { }
+ virtual ~TestFailedCtorSubChild() { }
+
+protected:
+ virtual PTestFailedCtorSubsubChild* AllocPTestFailedCtorSubsubChild() override;
+ virtual bool DeallocPTestFailedCtorSubsubChild(PTestFailedCtorSubsubChild* actor) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+
+//-----------------------------------------------------------------------------
+// Grand-descendent
+//
+class TestFailedCtorSubsub :
+ public PTestFailedCtorSubsubParent,
+ public PTestFailedCtorSubsubChild
+{
+public:
+ TestFailedCtorSubsub() : mWhy(ActorDestroyReason(-1)), mDealloced(false) {}
+ virtual ~TestFailedCtorSubsub() {}
+
+ virtual void ActorDestroy(ActorDestroyReason why) override { mWhy = why; }
+
+ ActorDestroyReason mWhy;
+ bool mDealloced;
+};
+
+
+}
+}
+
+#endif // ifndef mozilla_ipdltest_TestFailedCtor_h
diff --git a/ipc/ipdl/test/cxx/TestHangs.cpp b/ipc/ipdl/test/cxx/TestHangs.cpp
new file mode 100644
index 0000000000..b96823aee3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestHangs.cpp
@@ -0,0 +1,146 @@
+#include "base/process_util.h"
+
+#include "TestHangs.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using base::KillProcess;
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestHangsParent::TestHangsParent() : mDetectedHang(false)
+{
+ MOZ_COUNT_CTOR(TestHangsParent);
+}
+
+TestHangsParent::~TestHangsParent()
+{
+ MOZ_COUNT_DTOR(TestHangsParent);
+}
+
+void
+TestHangsParent::Main()
+{
+ // Here we try to set things up to test the following sequence of events:
+ //
+ // - subprocess causes an OnMaybeDequeueOne() task to be posted to
+ // this thread
+ //
+ // - subprocess hangs just long enough for the hang timer to expire
+ //
+ // - hang-kill code in the parent starts running
+ //
+ // - subprocess replies to message while hang code runs
+ //
+ // - reply is processed in OnMaybeDequeueOne() before Close() has
+ // been called or the channel error notification has been posted
+
+ // this tells the subprocess to send us Nonce()
+ if (!SendStart())
+ fail("sending Start");
+
+ // now we sleep here for a while awaiting the Nonce() message from
+ // the child. since we're not blocked on anything, the IO thread
+ // will enqueue an OnMaybeDequeueOne() task to process that
+ // message
+ //
+ // NB: PR_Sleep is exactly what we want, only the current thread
+ // sleeping
+ PR_Sleep(5000);
+
+ // when we call into this, we'll pull the Nonce() message out of
+ // the mPending queue, but that doesn't matter ... the
+ // OnMaybeDequeueOne() event will remain
+ if (CallStackFrame() && mDetectedHang)
+ fail("should have timed out!");
+
+ // the Close() task in the queue will shut us down
+}
+
+bool
+TestHangsParent::ShouldContinueFromReplyTimeout()
+{
+ mDetectedHang = true;
+
+ // so we've detected a timeout after 2 ms ... now we cheat and
+ // sleep for a long time, to allow the subprocess's reply to come
+ // in
+
+ PR_Sleep(5000);
+
+ // reply should be here; we'll post a task to shut things down.
+ // This must be after OnMaybeDequeueOne() in the event queue.
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestHangsParent::CleanUp));
+
+ GetIPCChannel()->CloseWithTimeout();
+
+ return false;
+}
+
+bool
+TestHangsParent::AnswerStackFrame()
+{
+ if (PTestHangs::HANG != state()) {
+ if (CallStackFrame())
+ fail("should have timed out!");
+ }
+ else {
+ // minimum possible, 2 ms. We want to detecting a hang to race
+ // with the reply coming in, as reliably as possible
+ SetReplyTimeoutMs(2);
+
+ if (CallHang())
+ fail("should have timed out!");
+ }
+
+ return true;
+}
+
+void
+TestHangsParent::CleanUp()
+{
+ ipc::ScopedProcessHandle otherProcessHandle;
+ if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle.rwget())) {
+ fail("couldn't open child process");
+ } else {
+ if (!KillProcess(otherProcessHandle, 0, false)) {
+ fail("terminating child process");
+ }
+ }
+ Close();
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+TestHangsChild::TestHangsChild()
+{
+ MOZ_COUNT_CTOR(TestHangsChild);
+}
+
+TestHangsChild::~TestHangsChild()
+{
+ MOZ_COUNT_DTOR(TestHangsChild);
+}
+
+bool
+TestHangsChild::AnswerHang()
+{
+ puts(" (child process is 'hanging' now)");
+
+ // just sleep until we're reasonably confident the 1ms hang
+ // detector fired in the parent process and it's sleeping in
+ // ShouldContinueFromReplyTimeout()
+ PR_Sleep(1000);
+
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestHangs.h b/ipc/ipdl/test/cxx/TestHangs.h
new file mode 100644
index 0000000000..676215172b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestHangs.h
@@ -0,0 +1,87 @@
+#ifndef mozilla__ipdltest_TestHangs_h
+#define mozilla__ipdltest_TestHangs_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestHangsParent.h"
+#include "mozilla/_ipdltest/PTestHangsChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestHangsParent :
+ public PTestHangsParent
+{
+public:
+ TestHangsParent();
+ virtual ~TestHangsParent();
+
+ static bool RunTestInProcesses() { return true; }
+
+ // FIXME/bug 703320 Disabled because parent kills child proc, not
+ // clear how that should work in threads.
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual bool ShouldContinueFromReplyTimeout() override;
+
+ virtual bool RecvNonce() {
+ return true;
+ }
+
+ virtual bool AnswerStackFrame() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ void CleanUp();
+
+ bool mDetectedHang;
+};
+
+
+class TestHangsChild :
+ public PTestHangsChild
+{
+public:
+ TestHangsChild();
+ virtual ~TestHangsChild();
+
+protected:
+ virtual bool RecvStart() override {
+ if (!SendNonce())
+ fail("sending Nonce");
+ return true;
+ }
+
+ virtual bool AnswerStackFrame() override
+ {
+ if (CallStackFrame())
+ fail("should have failed");
+ return true;
+ }
+
+ virtual bool AnswerHang() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestHangs_h
diff --git a/ipc/ipdl/test/cxx/TestHighestPrio.cpp b/ipc/ipdl/test/cxx/TestHighestPrio.cpp
new file mode 100644
index 0000000000..524943de42
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestHighestPrio.cpp
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ */
+/* 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 "TestHighestPrio.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#if defined(OS_POSIX)
+#include <unistd.h>
+#else
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestHighestPrioParent::TestHighestPrioParent()
+ : msg_num_(0)
+{
+ MOZ_COUNT_CTOR(TestHighestPrioParent);
+}
+
+TestHighestPrioParent::~TestHighestPrioParent()
+{
+ MOZ_COUNT_DTOR(TestHighestPrioParent);
+}
+
+void
+TestHighestPrioParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestHighestPrioParent::RecvMsg1()
+{
+ MOZ_ASSERT(msg_num_ == 0);
+ msg_num_ = 1;
+ return true;
+}
+
+bool
+TestHighestPrioParent::RecvMsg2()
+{
+
+ MOZ_ASSERT(msg_num_ == 1);
+ msg_num_ = 2;
+
+ if (!SendStartInner())
+ fail("sending StartInner");
+
+ return true;
+}
+
+bool
+TestHighestPrioParent::RecvMsg3()
+{
+ MOZ_ASSERT(msg_num_ == 2);
+ msg_num_ = 3;
+ return true;
+}
+
+bool
+TestHighestPrioParent::RecvMsg4()
+{
+ MOZ_ASSERT(msg_num_ == 3);
+ msg_num_ = 4;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+
+TestHighestPrioChild::TestHighestPrioChild()
+{
+ MOZ_COUNT_CTOR(TestHighestPrioChild);
+}
+
+TestHighestPrioChild::~TestHighestPrioChild()
+{
+ MOZ_COUNT_DTOR(TestHighestPrioChild);
+}
+
+bool
+TestHighestPrioChild::RecvStart()
+{
+ if (!SendMsg1())
+ fail("sending Msg1");
+
+ if (!SendMsg2())
+ fail("sending Msg2");
+
+ Close();
+ return true;
+}
+
+bool
+TestHighestPrioChild::RecvStartInner()
+{
+ if (!SendMsg3())
+ fail("sending Msg3");
+
+ if (!SendMsg4())
+ fail("sending Msg4");
+
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestHighestPrio.h b/ipc/ipdl/test/cxx/TestHighestPrio.h
new file mode 100644
index 0000000000..e6dc8c4277
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestHighestPrio.h
@@ -0,0 +1,68 @@
+#ifndef mozilla__ipdltest_TestHighestPrio_h
+#define mozilla__ipdltest_TestHighestPrio_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestHighestPrioParent.h"
+#include "mozilla/_ipdltest/PTestHighestPrioChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestHighestPrioParent :
+ public PTestHighestPrioParent
+{
+public:
+ TestHighestPrioParent();
+ virtual ~TestHighestPrioParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ bool RecvMsg1() override;
+ bool RecvMsg2() override;
+ bool RecvMsg3() override;
+ bool RecvMsg4() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ if (msg_num_ != 4)
+ fail("missed IPC call");
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ int msg_num_;
+};
+
+
+class TestHighestPrioChild :
+ public PTestHighestPrioChild
+{
+public:
+ TestHighestPrioChild();
+ virtual ~TestHighestPrioChild();
+
+ bool RecvStart() override;
+ bool RecvStartInner() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestHighestPrio_h
diff --git a/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp
new file mode 100644
index 0000000000..21a5bc5924
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.cpp
@@ -0,0 +1,155 @@
+#include "TestInterruptErrorCleanup.h"
+
+#include "base/task.h"
+#include "mozilla/CondVar.h"
+#include "mozilla/Mutex.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#include "IPDLUnitTestSubprocess.h"
+
+using mozilla::CondVar;
+using mozilla::Mutex;
+using mozilla::MutexAutoLock;
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+namespace {
+
+// NB: this test does its own shutdown, rather than going through
+// QuitParent(), because it's testing degenerate edge cases
+
+void DeleteSubprocess(Mutex* mutex, CondVar* cvar)
+{
+ MutexAutoLock lock(*mutex);
+
+ delete gSubprocess;
+ gSubprocess = nullptr;
+
+ cvar->Notify();
+}
+
+void DeleteTheWorld()
+{
+ delete static_cast<TestInterruptErrorCleanupParent*>(gParentActor);
+ gParentActor = nullptr;
+
+ // needs to be synchronous to avoid affecting event ordering on
+ // the main thread
+ Mutex mutex("TestInterruptErrorCleanup.DeleteTheWorld.mutex");
+ CondVar cvar(mutex, "TestInterruptErrorCleanup.DeleteTheWorld.cvar");
+
+ MutexAutoLock lock(mutex);
+
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(DeleteSubprocess, &mutex, &cvar));
+
+ cvar.Wait();
+}
+
+void Done()
+{
+ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+ nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
+ appShell->Exit();
+
+ passed(__FILE__);
+}
+
+} // namespace <anon>
+
+TestInterruptErrorCleanupParent::TestInterruptErrorCleanupParent()
+ : mGotProcessingError(false)
+{
+ MOZ_COUNT_CTOR(TestInterruptErrorCleanupParent);
+}
+
+TestInterruptErrorCleanupParent::~TestInterruptErrorCleanupParent()
+{
+ MOZ_COUNT_DTOR(TestInterruptErrorCleanupParent);
+}
+
+void
+TestInterruptErrorCleanupParent::Main()
+{
+ // This test models the following sequence of events
+ //
+ // (1) Parent: Interrupt out-call
+ // (2) Child: crash
+ // --[Parent-only hereafter]--
+ // (3) Interrupt out-call return false
+ // (4) Close()
+ // --[event loop]--
+ // (5) delete parentActor
+ // (6) delete childProcess
+ // --[event loop]--
+ // (7) Channel::OnError notification
+ // --[event loop]--
+ // (8) Done, quit
+ //
+ // See bug 535298 and friends; this seqeunce of events captures
+ // three differnent potential errors
+ // - Close()-after-error (semantic error previously)
+ // - use-after-free of parentActor
+ // - use-after-free of channel
+ //
+ // Because of legacy constraints related to nsNPAPI* code, we need
+ // to ensure that this sequence of events can occur without
+ // errors/crashes.
+
+ MessageLoop::current()->PostTask(
+ NewRunnableFunction(DeleteTheWorld));
+
+ // it's a failure if this *succeeds*
+ if (CallError())
+ fail("expected an error!");
+
+ if (!mGotProcessingError)
+ fail("expected a ProcessingError() notification");
+
+ // it's OK to Close() a channel after an error, because nsNPAPI*
+ // wants to do this
+ Close();
+
+ // we know that this event *must* be after the MaybeError
+ // notification enqueued by AsyncChannel, because that event is
+ // enqueued within the same mutex that ends up signaling the
+ // wakeup-on-error of |CallError()| above
+ MessageLoop::current()->PostTask(NewRunnableFunction(Done));
+}
+
+void
+TestInterruptErrorCleanupParent::ProcessingError(Result aCode, const char* aReason)
+{
+ if (aCode != MsgDropped)
+ fail("unexpected processing error");
+ mGotProcessingError = true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestInterruptErrorCleanupChild::TestInterruptErrorCleanupChild()
+{
+ MOZ_COUNT_CTOR(TestInterruptErrorCleanupChild);
+}
+
+TestInterruptErrorCleanupChild::~TestInterruptErrorCleanupChild()
+{
+ MOZ_COUNT_DTOR(TestInterruptErrorCleanupChild);
+}
+
+bool
+TestInterruptErrorCleanupChild::AnswerError()
+{
+ _exit(0);
+ NS_RUNTIMEABORT("unreached");
+ return false;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h
new file mode 100644
index 0000000000..bd45b2c987
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptErrorCleanup.h
@@ -0,0 +1,60 @@
+#ifndef mozilla__ipdltest_TestInterruptErrorCleanup_h
+#define mozilla__ipdltest_TestInterruptErrorCleanup_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestInterruptErrorCleanupParent.h"
+#include "mozilla/_ipdltest/PTestInterruptErrorCleanupChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestInterruptErrorCleanupParent :
+ public PTestInterruptErrorCleanupParent
+{
+public:
+ TestInterruptErrorCleanupParent();
+ virtual ~TestInterruptErrorCleanupParent();
+
+ static bool RunTestInProcesses() { return true; }
+ // FIXME/bug 703323 Could work if modified
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction!");
+ }
+
+ virtual void ProcessingError(Result aCode, const char* aReason) override;
+
+ bool mGotProcessingError;
+};
+
+
+class TestInterruptErrorCleanupChild :
+ public PTestInterruptErrorCleanupChild
+{
+public:
+ TestInterruptErrorCleanupChild();
+ virtual ~TestInterruptErrorCleanupChild();
+
+protected:
+ virtual bool AnswerError() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ fail("should have 'crashed'!");
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestInterruptErrorCleanup_h
diff --git a/ipc/ipdl/test/cxx/TestInterruptRaces.cpp b/ipc/ipdl/test/cxx/TestInterruptRaces.cpp
new file mode 100644
index 0000000000..6986506bb8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptRaces.cpp
@@ -0,0 +1,220 @@
+#include "TestInterruptRaces.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using mozilla::ipc::MessageChannel;
+
+namespace mozilla {
+namespace _ipdltest {
+
+ipc::RacyInterruptPolicy
+MediateRace(const MessageChannel::MessageInfo& parent,
+ const MessageChannel::MessageInfo& child)
+{
+ return (PTestInterruptRaces::Msg_Child__ID == parent.type()) ?
+ ipc::RIPParentWins : ipc::RIPChildWins;
+}
+
+//-----------------------------------------------------------------------------
+// parent
+void
+TestInterruptRacesParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start()");
+}
+
+bool
+TestInterruptRacesParent::RecvStartRace()
+{
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestInterruptRacesParent::OnRaceTime));
+ return true;
+}
+
+void
+TestInterruptRacesParent::OnRaceTime()
+{
+ if (!CallRace(&mChildHasReply))
+ fail("problem calling Race()");
+
+ if (!mChildHasReply)
+ fail("child should have got a reply already");
+
+ mHasReply = true;
+
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestInterruptRacesParent::Test2));
+}
+
+bool
+TestInterruptRacesParent::AnswerRace(bool* hasReply)
+{
+ if (mHasReply)
+ fail("apparently the parent won the Interrupt race!");
+ *hasReply = hasReply;
+ return true;
+}
+
+void
+TestInterruptRacesParent::Test2()
+{
+ puts(" passed");
+ puts("Test 2");
+
+ mHasReply = false;
+ mChildHasReply = false;
+
+ if (!CallStackFrame())
+ fail("can't set up a stack frame");
+
+ puts(" passed");
+
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestInterruptRacesParent::Test3));
+}
+
+bool
+TestInterruptRacesParent::AnswerStackFrame()
+{
+ if (!SendWakeup())
+ fail("can't wake up the child");
+
+ if (!CallRace(&mChildHasReply))
+ fail("can't set up race condition");
+ mHasReply = true;
+
+ if (!mChildHasReply)
+ fail("child should have got a reply already");
+
+ return true;
+}
+
+void
+TestInterruptRacesParent::Test3()
+{
+ puts("Test 3");
+
+ if (!CallStackFrame3())
+ fail("can't set up a stack frame");
+
+ puts(" passed");
+
+ Close();
+}
+
+bool
+TestInterruptRacesParent::AnswerStackFrame3()
+{
+ if (!SendWakeup3())
+ fail("can't wake up the child");
+
+ if (!CallChild())
+ fail("can't set up race condition");
+
+ return true;
+}
+
+bool
+TestInterruptRacesParent::AnswerParent()
+{
+ mAnsweredParent = true;
+ return true;
+}
+
+bool
+TestInterruptRacesParent::RecvGetAnsweredParent(bool* answeredParent)
+{
+ *answeredParent = mAnsweredParent;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+bool
+TestInterruptRacesChild::RecvStart()
+{
+ puts("Test 1");
+
+ if (!SendStartRace())
+ fail("problem sending StartRace()");
+
+ bool dontcare;
+ if (!CallRace(&dontcare))
+ fail("problem calling Race()");
+
+ mHasReply = true;
+ return true;
+}
+
+bool
+TestInterruptRacesChild::AnswerRace(bool* hasReply)
+{
+ if (!mHasReply)
+ fail("apparently the child lost the Interrupt race!");
+
+ *hasReply = mHasReply;
+
+ return true;
+}
+
+bool
+TestInterruptRacesChild::AnswerStackFrame()
+{
+ // reset for the second test
+ mHasReply = false;
+
+ if (!CallStackFrame())
+ fail("can't set up stack frame");
+
+ if (!mHasReply)
+ fail("should have had reply by now");
+
+ return true;
+}
+
+bool
+TestInterruptRacesChild::RecvWakeup()
+{
+ bool dontcare;
+ if (!CallRace(&dontcare))
+ fail("can't set up race condition");
+
+ mHasReply = true;
+ return true;
+}
+
+bool
+TestInterruptRacesChild::AnswerStackFrame3()
+{
+ if (!CallStackFrame3())
+ fail("can't set up stack frame");
+ return true;
+}
+
+bool
+TestInterruptRacesChild::RecvWakeup3()
+{
+ if (!CallParent())
+ fail("can't set up race condition");
+ return true;
+}
+
+bool
+TestInterruptRacesChild::AnswerChild()
+{
+ bool parentAnsweredParent;
+ // the parent is supposed to win the race, which means its
+ // message, Child(), is supposed to be processed before the
+ // child's message, Parent()
+ if (!SendGetAnsweredParent(&parentAnsweredParent))
+ fail("sending GetAnsweredParent");
+
+ if (parentAnsweredParent)
+ fail("parent was supposed to win the race!");
+
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestInterruptRaces.h b/ipc/ipdl/test/cxx/TestInterruptRaces.h
new file mode 100644
index 0000000000..f83c49fd11
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptRaces.h
@@ -0,0 +1,131 @@
+#ifndef mozilla__ipdltest_TestInterruptRaces_h
+#define mozilla__ipdltest_TestInterruptRaces_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestInterruptRacesParent.h"
+#include "mozilla/_ipdltest/PTestInterruptRacesChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+mozilla::ipc::RacyInterruptPolicy
+MediateRace(const mozilla::ipc::MessageChannel::MessageInfo& parent,
+ const mozilla::ipc::MessageChannel::MessageInfo& child);
+
+class TestInterruptRacesParent :
+ public PTestInterruptRacesParent
+{
+public:
+ TestInterruptRacesParent() : mHasReply(false),
+ mChildHasReply(false),
+ mAnsweredParent(false)
+ { }
+ virtual ~TestInterruptRacesParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool
+ RecvStartRace() override;
+
+ virtual bool
+ AnswerRace(bool* hasRace) override;
+
+ virtual bool
+ AnswerStackFrame() override;
+
+ virtual bool
+ AnswerStackFrame3() override;
+
+ virtual bool
+ AnswerParent() override;
+
+ virtual bool
+ RecvGetAnsweredParent(bool* answeredParent) override;
+
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override
+ {
+ return MediateRace(parent, child);
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ if (!(mHasReply && mChildHasReply))
+ fail("both sides should have replies!");
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ void OnRaceTime();
+
+ void Test2();
+ void Test3();
+
+ bool mHasReply;
+ bool mChildHasReply;
+ bool mAnsweredParent;
+};
+
+
+class TestInterruptRacesChild :
+ public PTestInterruptRacesChild
+{
+public:
+ TestInterruptRacesChild() : mHasReply(false) { }
+ virtual ~TestInterruptRacesChild() { }
+
+protected:
+ virtual bool
+ RecvStart() override;
+
+ virtual bool
+ AnswerRace(bool* hasRace) override;
+
+ virtual bool
+ AnswerStackFrame() override;
+
+ virtual bool
+ AnswerStackFrame3() override;
+
+ virtual bool
+ RecvWakeup() override;
+
+ virtual bool
+ RecvWakeup3() override;
+
+ virtual bool
+ AnswerChild() override;
+
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override
+ {
+ return MediateRace(parent, child);
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
+private:
+ bool mHasReply;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestInterruptRaces_h
diff --git a/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp
new file mode 100644
index 0000000000..38f7993da7
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.cpp
@@ -0,0 +1,134 @@
+#include "TestInterruptShutdownRace.h"
+
+#include "base/task.h"
+#include "IPDLUnitTests.h" // fail etc.
+#include "IPDLUnitTestSubprocess.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+namespace {
+
+// NB: this test does its own shutdown, rather than going through
+// QuitParent(), because it's testing degenerate edge cases
+
+void DeleteSubprocess()
+{
+ delete gSubprocess;
+ gSubprocess = nullptr;
+}
+
+void Done()
+{
+ passed(__FILE__);
+ QuitParent();
+}
+
+} // namespace <anon>
+
+TestInterruptShutdownRaceParent::TestInterruptShutdownRaceParent()
+{
+ MOZ_COUNT_CTOR(TestInterruptShutdownRaceParent);
+}
+
+TestInterruptShutdownRaceParent::~TestInterruptShutdownRaceParent()
+{
+ MOZ_COUNT_DTOR(TestInterruptShutdownRaceParent);
+}
+
+void
+TestInterruptShutdownRaceParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestInterruptShutdownRaceParent::RecvStartDeath()
+{
+ // this will be ordered before the OnMaybeDequeueOne event of
+ // Orphan in the queue
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this,
+ &TestInterruptShutdownRaceParent::StartShuttingDown));
+ return true;
+}
+
+void
+TestInterruptShutdownRaceParent::StartShuttingDown()
+{
+ // NB: we sleep here to try and avoid receiving the Orphan message
+ // while waiting for the CallExit() reply. if we fail at that, it
+ // will cause the test to pass spuriously, because there won't be
+ // an OnMaybeDequeueOne task for Orphan
+ PR_Sleep(2000);
+
+ if (CallExit())
+ fail("connection was supposed to be interrupted");
+
+ Close();
+
+ delete static_cast<TestInterruptShutdownRaceParent*>(gParentActor);
+ gParentActor = nullptr;
+
+ XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction(DeleteSubprocess));
+
+ // this is ordered after the OnMaybeDequeueOne event in the queue
+ MessageLoop::current()->PostTask(NewRunnableFunction(Done));
+
+ // |this| has been deleted, be mindful
+}
+
+bool
+TestInterruptShutdownRaceParent::RecvOrphan()
+{
+ // it would be nice to fail() here, but we'll process this message
+ // while waiting for the reply CallExit(). The OnMaybeDequeueOne
+ // task will still be in the queue, it just wouldn't have had any
+ // work to do, if we hadn't deleted ourself
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestInterruptShutdownRaceChild::TestInterruptShutdownRaceChild()
+{
+ MOZ_COUNT_CTOR(TestInterruptShutdownRaceChild);
+}
+
+TestInterruptShutdownRaceChild::~TestInterruptShutdownRaceChild()
+{
+ MOZ_COUNT_DTOR(TestInterruptShutdownRaceChild);
+}
+
+bool
+TestInterruptShutdownRaceChild::RecvStart()
+{
+ if (!SendStartDeath())
+ fail("sending StartDeath");
+
+ // See comment in StartShuttingDown(): we want to send Orphan()
+ // while the parent is in its PR_Sleep()
+ PR_Sleep(1000);
+
+ if (!SendOrphan())
+ fail("sending Orphan");
+
+ return true;
+}
+
+bool
+TestInterruptShutdownRaceChild::AnswerExit()
+{
+ _exit(0);
+ NS_RUNTIMEABORT("unreached");
+ return false;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestInterruptShutdownRace.h b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.h
new file mode 100644
index 0000000000..c291b14a6d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestInterruptShutdownRace.h
@@ -0,0 +1,64 @@
+#ifndef mozilla__ipdltest_TestInterruptShutdownRace_h
+#define mozilla__ipdltest_TestInterruptShutdownRace_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestInterruptShutdownRaceParent.h"
+#include "mozilla/_ipdltest/PTestInterruptShutdownRaceChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestInterruptShutdownRaceParent :
+ public PTestInterruptShutdownRaceParent
+{
+public:
+ TestInterruptShutdownRaceParent();
+ virtual ~TestInterruptShutdownRaceParent();
+
+ static bool RunTestInProcesses() { return true; }
+ // FIXME/bug 703323 Could work if modified
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ virtual bool RecvStartDeath() override;
+
+ virtual bool RecvOrphan() override;
+
+protected:
+ void StartShuttingDown();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (AbnormalShutdown != why)
+ fail("unexpected destruction!");
+ }
+};
+
+
+class TestInterruptShutdownRaceChild :
+ public PTestInterruptShutdownRaceChild
+{
+public:
+ TestInterruptShutdownRaceChild();
+ virtual ~TestInterruptShutdownRaceChild();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual bool AnswerExit() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ fail("should have 'crashed'!");
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestInterruptShutdownRace_h
diff --git a/ipc/ipdl/test/cxx/TestJSON.cpp b/ipc/ipdl/test/cxx/TestJSON.cpp
new file mode 100644
index 0000000000..ea742b7059
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestJSON.cpp
@@ -0,0 +1,129 @@
+#include "TestJSON.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+#define test_assert(_cond, _msg) \
+ if (!(_cond)) fail(_msg)
+
+namespace mozilla {
+namespace _ipdltest {
+
+static nsString
+String(const char* const str)
+{
+ return NS_ConvertUTF8toUTF16(str);
+}
+
+static void
+Array123(InfallibleTArray<JSONVariant>& a123)
+{
+ a123.AppendElement(1); a123.AppendElement(2); a123.AppendElement(3);
+
+ test_assert(a123 == a123, "operator== is broken");
+}
+
+template<class HandleT>
+JSONVariant
+MakeTestVariant(HandleT* handle)
+{
+ // In JS syntax:
+ //
+ // return [
+ // undefined, null, true, 1.25, "test string",
+ // handle,
+ // [ 1, 2, 3 ],
+ // { "undefined" : undefined,
+ // "null" : null,
+ // "true" : true,
+ // "1.25" : 1.25,
+ // "string" : "string"
+ // "handle" : handle,
+ // "array" : [ 1, 2, 3 ]
+ // }
+ // ]
+ //
+ InfallibleTArray<JSONVariant> outer;
+
+ outer.AppendElement(void_t());
+ outer.AppendElement(null_t());
+ outer.AppendElement(true);
+ outer.AppendElement(1.25);
+ outer.AppendElement(String("test string"));
+
+ outer.AppendElement(handle);
+
+ InfallibleTArray<JSONVariant> tmp;
+ Array123(tmp);
+ outer.AppendElement(tmp);
+
+ InfallibleTArray<KeyValue> obj;
+ obj.AppendElement(KeyValue(String("undefined"), void_t()));
+ obj.AppendElement(KeyValue(String("null"), null_t()));
+ obj.AppendElement(KeyValue(String("true"), true));
+ obj.AppendElement(KeyValue(String("1.25"), 1.25));
+ obj.AppendElement(KeyValue(String("string"), String("value")));
+ obj.AppendElement(KeyValue(String("handle"), handle));
+ InfallibleTArray<JSONVariant> tmp2;
+ Array123(tmp2);
+ obj.AppendElement(KeyValue(String("array"), tmp2));
+
+ outer.AppendElement(obj);
+
+ test_assert(outer == outer, "operator== is broken");
+
+ return JSONVariant(outer);
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+void
+TestJSONParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+
+bool
+TestJSONParent::RecvTest(const JSONVariant& i,
+ JSONVariant* o)
+{
+ test_assert(i == i, "operator== is broken");
+ test_assert(i == MakeTestVariant(mKid), "inparam mangled en route");
+
+ *o = i;
+
+ test_assert(i == *o, "operator= is broken");
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+bool
+TestJSONChild::RecvStart()
+{
+ if (!SendPTestHandleConstructor())
+ fail("sending Handle ctor");
+
+ JSONVariant i(MakeTestVariant(mKid));
+ test_assert(i == i, "operator== is broken");
+ test_assert(i == MakeTestVariant(mKid), "copy ctor is broken");
+
+ JSONVariant o;
+ if (!SendTest(i, &o))
+ fail("sending Test");
+
+ test_assert(i == o, "round-trip mangled input data");
+ test_assert(o == MakeTestVariant(mKid), "outparam mangled en route");
+
+ Close();
+ return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestJSON.h b/ipc/ipdl/test/cxx/TestJSON.h
new file mode 100644
index 0000000000..55d6e041b6
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestJSON.h
@@ -0,0 +1,111 @@
+#ifndef mozilla__ipdltest_TestJSON_h
+#define mozilla__ipdltest_TestJSON_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestJSONParent.h"
+#include "mozilla/_ipdltest/PTestJSONChild.h"
+
+#include "mozilla/_ipdltest/PTestHandleParent.h"
+#include "mozilla/_ipdltest/PTestHandleChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestHandleParent :
+ public PTestHandleParent
+{
+public:
+ TestHandleParent() { }
+ virtual ~TestHandleParent() { }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+class TestJSONParent :
+ public PTestJSONParent
+{
+public:
+ TestJSONParent() { }
+ virtual ~TestJSONParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool
+ RecvTest(const JSONVariant& i,
+ JSONVariant* o) override;
+
+ virtual PTestHandleParent* AllocPTestHandleParent() override
+ {
+ return mKid = new TestHandleParent();
+ }
+
+ virtual bool DeallocPTestHandleParent(PTestHandleParent* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ PTestHandleParent* mKid;
+};
+
+
+class TestHandleChild :
+ public PTestHandleChild
+{
+public:
+ TestHandleChild() { }
+ virtual ~TestHandleChild() { }
+};
+
+class TestJSONChild :
+ public PTestJSONChild
+{
+public:
+ TestJSONChild() { }
+ virtual ~TestJSONChild() { }
+
+protected:
+ virtual bool
+ RecvStart() override;
+
+ virtual PTestHandleChild* AllocPTestHandleChild() override
+ {
+ return mKid = new TestHandleChild();
+ }
+
+ virtual bool DeallocPTestHandleChild(PTestHandleChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
+ PTestHandleChild* mKid;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestJSON_h
diff --git a/ipc/ipdl/test/cxx/TestLatency.cpp b/ipc/ipdl/test/cxx/TestLatency.cpp
new file mode 100644
index 0000000000..094622b823
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestLatency.cpp
@@ -0,0 +1,258 @@
+#include "TestLatency.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+// A ping/pong trial takes O(100us) or more, so if we don't have 10us
+// resolution or better, the results will not be terribly useful
+static const double kTimingResolutionCutoff = 0.00001; // 10us
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestLatencyParent::TestLatencyParent() :
+ mStart(),
+ mPPTimeTotal(),
+ mPP5TimeTotal(),
+ mRpcTimeTotal(),
+ mPPTrialsToGo(NR_TRIALS),
+ mPP5TrialsToGo(NR_TRIALS),
+ mNumChildProcessedCompressedSpams(0)
+{
+ MOZ_COUNT_CTOR(TestLatencyParent);
+}
+
+TestLatencyParent::~TestLatencyParent()
+{
+ MOZ_COUNT_DTOR(TestLatencyParent);
+}
+
+void
+TestLatencyParent::Main()
+{
+ TimeDuration resolution = TimeDuration::Resolution();
+ if (resolution.ToSeconds() > kTimingResolutionCutoff) {
+ puts(" (skipping TestLatency, timing resolution is too poor)");
+ Close();
+ return;
+ }
+
+ printf(" timing resolution: %g seconds\n",
+ resolution.ToSecondsSigDigits());
+
+ if (mozilla::ipc::LoggingEnabled())
+ NS_RUNTIMEABORT("you really don't want to log all IPC messages during this test, trust me");
+
+ PingPongTrial();
+}
+
+void
+TestLatencyParent::PingPongTrial()
+{
+ mStart = TimeStamp::Now();
+ if (!SendPing())
+ fail("sending Ping()");
+}
+
+void
+TestLatencyParent::Ping5Pong5Trial()
+{
+ mStart = TimeStamp::Now();
+
+ if (!SendPing5() ||
+ !SendPing5() ||
+ !SendPing5() ||
+ !SendPing5() ||
+ !SendPing5())
+ fail("sending Ping5()");
+}
+
+bool
+TestLatencyParent::RecvPong()
+{
+ TimeDuration thisTrial = (TimeStamp::Now() - mStart);
+ mPPTimeTotal += thisTrial;
+
+ if (0 == (mPPTrialsToGo % 1000))
+ printf(" PP trial %d: %g\n",
+ mPPTrialsToGo, thisTrial.ToSecondsSigDigits());
+
+ if (--mPPTrialsToGo > 0)
+ PingPongTrial();
+ else
+ Ping5Pong5Trial();
+ return true;
+}
+
+bool
+TestLatencyParent::RecvPong5()
+{
+ if (PTestLatency::PING5 != state())
+ return true;
+
+ TimeDuration thisTrial = (TimeStamp::Now() - mStart);
+ mPP5TimeTotal += thisTrial;
+
+ if (0 == (mPP5TrialsToGo % 1000))
+ printf(" PP5 trial %d: %g\n",
+ mPP5TrialsToGo, thisTrial.ToSecondsSigDigits());
+
+ if (0 < --mPP5TrialsToGo)
+ Ping5Pong5Trial();
+ else
+ RpcTrials();
+
+ return true;
+}
+
+void
+TestLatencyParent::RpcTrials()
+{
+ TimeStamp start = TimeStamp::Now();
+ for (int i = 0; i < NR_TRIALS; ++i) {
+ if (!CallRpc())
+ fail("can't call Rpc()");
+ if (0 == (i % 1000))
+ printf(" Rpc trial %d\n", i);
+ }
+ mRpcTimeTotal = (TimeStamp::Now() - start);
+
+ SpamTrial();
+}
+
+void
+TestLatencyParent::SpamTrial()
+{
+ TimeStamp start = TimeStamp::Now();
+ for (int i = 0; i < NR_SPAMS - 1; ++i) {
+ if (!SendSpam())
+ fail("sending Spam()");
+ if (0 == (i % 10000))
+ printf(" Spam trial %d\n", i);
+ }
+
+ // Synchronize with the child process to ensure all messages have
+ // been processed. This adds the overhead of a reply message from
+ // child-->here, but should be insignificant compared to >>
+ // NR_SPAMS.
+ if (!CallSynchro())
+ fail("calling Synchro()");
+
+ mSpamTimeTotal = (TimeStamp::Now() - start);
+
+ CompressedSpamTrial();
+}
+
+void
+TestLatencyParent::CompressedSpamTrial()
+{
+ for (int i = 0; i < NR_SPAMS; ++i) {
+ if (!SendCompressedSpam(i + 1))
+ fail("sending CompressedSpam()");
+ if (0 == (i % 10000))
+ printf(" CompressedSpam trial %d\n", i);
+ }
+
+ uint32_t lastSeqno;
+ if (!CallSynchro2(&lastSeqno, &mNumChildProcessedCompressedSpams))
+ fail("calling Synchro2()");
+
+ if (lastSeqno != NR_SPAMS)
+ fail("last seqno was %u, expected %u", lastSeqno, NR_SPAMS);
+
+ // NB: since this is testing an optimization, it's somewhat bogus.
+ // Need to make a warning if it actually intermittently fails in
+ // practice, which is doubtful.
+ if (!(mNumChildProcessedCompressedSpams < NR_SPAMS))
+ fail("Didn't compress any messages?");
+
+ Exit();
+}
+
+void
+TestLatencyParent::Exit()
+{
+ Close();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestLatencyChild::TestLatencyChild()
+ : mLastSeqno(0)
+ , mNumProcessedCompressedSpams(0)
+{
+ MOZ_COUNT_CTOR(TestLatencyChild);
+}
+
+TestLatencyChild::~TestLatencyChild()
+{
+ MOZ_COUNT_DTOR(TestLatencyChild);
+}
+
+bool
+TestLatencyChild::RecvPing()
+{
+ SendPong();
+ return true;
+}
+
+bool
+TestLatencyChild::RecvPing5()
+{
+ if (PTestLatency::PONG1 != state())
+ return true;
+
+ if (!SendPong5() ||
+ !SendPong5() ||
+ !SendPong5() ||
+ !SendPong5() ||
+ !SendPong5())
+ fail("sending Pong5()");
+
+ return true;
+}
+
+bool
+TestLatencyChild::AnswerRpc()
+{
+ return true;
+}
+
+bool
+TestLatencyChild::RecvSpam()
+{
+ // no-op
+ return true;
+}
+
+bool
+TestLatencyChild::AnswerSynchro()
+{
+ return true;
+}
+
+bool
+TestLatencyChild::RecvCompressedSpam(const uint32_t& seqno)
+{
+ if (seqno <= mLastSeqno)
+ fail("compressed seqnos must monotonically increase");
+
+ mLastSeqno = seqno;
+ ++mNumProcessedCompressedSpams;
+ return true;
+}
+
+bool
+TestLatencyChild::AnswerSynchro2(uint32_t* lastSeqno,
+ uint32_t* numMessagesDispatched)
+{
+ *lastSeqno = mLastSeqno;
+ *numMessagesDispatched = mNumProcessedCompressedSpams;
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestLatency.h b/ipc/ipdl/test/cxx/TestLatency.h
new file mode 100644
index 0000000000..d179213db1
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestLatency.h
@@ -0,0 +1,111 @@
+#ifndef mozilla__ipdltest_TestLatency_h
+#define mozilla__ipdltest_TestLatency_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestLatencyParent.h"
+#include "mozilla/_ipdltest/PTestLatencyChild.h"
+
+#include "mozilla/TimeStamp.h"
+
+#define NR_TRIALS 10000
+#define NR_SPAMS 25000
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestLatencyParent :
+ public PTestLatencyParent
+{
+private:
+ typedef mozilla::TimeStamp TimeStamp;
+ typedef mozilla::TimeDuration TimeDuration;
+
+public:
+ TestLatencyParent();
+ virtual ~TestLatencyParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvPong() override;
+ virtual bool RecvPong5() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ passed("\n"
+ " average #ping-pong/sec: %g\n"
+ " average #ping5-pong5/sec: %g\n"
+ " average #RPC call-answer/sec: %g\n"
+ " average #spams/sec: %g\n"
+ " pct. spams compressed away: %g\n",
+ double(NR_TRIALS) / mPPTimeTotal.ToSecondsSigDigits(),
+ double(NR_TRIALS) / mPP5TimeTotal.ToSecondsSigDigits(),
+ double(NR_TRIALS) / mRpcTimeTotal.ToSecondsSigDigits(),
+ double(NR_SPAMS) / mSpamTimeTotal.ToSecondsSigDigits(),
+ 100.0 * (double(NR_SPAMS - mNumChildProcessedCompressedSpams) /
+ double(NR_SPAMS)));
+
+ QuitParent();
+ }
+
+private:
+ void PingPongTrial();
+ void Ping5Pong5Trial();
+ void RpcTrials();
+ void SpamTrial();
+ void CompressedSpamTrial();
+ void Exit();
+
+ TimeStamp mStart;
+ TimeDuration mPPTimeTotal;
+ TimeDuration mPP5TimeTotal;
+ TimeDuration mRpcTimeTotal;
+ TimeDuration mSpamTimeTotal;
+
+ int mPPTrialsToGo;
+ int mPP5TrialsToGo;
+ uint32_t mNumChildProcessedCompressedSpams;
+};
+
+
+class TestLatencyChild :
+ public PTestLatencyChild
+{
+public:
+ TestLatencyChild();
+ virtual ~TestLatencyChild();
+
+protected:
+ virtual bool RecvPing() override;
+ virtual bool RecvPing5() override;
+ virtual bool AnswerRpc() override;
+ virtual bool RecvSpam() override;
+ virtual bool AnswerSynchro() override;
+ virtual bool RecvCompressedSpam(const uint32_t& seqno) override;
+ virtual bool AnswerSynchro2(uint32_t* lastSeqno,
+ uint32_t* numMessagesDispatched) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
+ uint32_t mLastSeqno;
+ uint32_t mNumProcessedCompressedSpams;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestLatency_h
diff --git a/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp b/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp
new file mode 100644
index 0000000000..7dee30e632
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.cpp
@@ -0,0 +1,104 @@
+#include "TestManyChildAllocs.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+
+#define NALLOCS 10
+
+namespace mozilla {
+namespace _ipdltest {
+
+// parent code
+
+TestManyChildAllocsParent::TestManyChildAllocsParent()
+{
+ MOZ_COUNT_CTOR(TestManyChildAllocsParent);
+}
+
+TestManyChildAllocsParent::~TestManyChildAllocsParent()
+{
+ MOZ_COUNT_DTOR(TestManyChildAllocsParent);
+}
+
+void
+TestManyChildAllocsParent::Main()
+{
+ if (!SendGo())
+ fail("can't send Go()");
+}
+
+bool
+TestManyChildAllocsParent::RecvDone()
+{
+ // explicitly *not* cleaning up, so we can sanity-check IPDL's
+ // auto-shutdown/cleanup handling
+ Close();
+
+ return true;
+}
+
+bool
+TestManyChildAllocsParent::DeallocPTestManyChildAllocsSubParent(
+ PTestManyChildAllocsSubParent* __a)
+{
+ delete __a; return true;
+}
+
+PTestManyChildAllocsSubParent*
+TestManyChildAllocsParent::AllocPTestManyChildAllocsSubParent()
+{
+ return new TestManyChildAllocsSubParent();
+}
+
+
+// child code
+
+TestManyChildAllocsChild::TestManyChildAllocsChild()
+{
+ MOZ_COUNT_CTOR(TestManyChildAllocsChild);
+}
+
+TestManyChildAllocsChild::~TestManyChildAllocsChild()
+{
+ MOZ_COUNT_DTOR(TestManyChildAllocsChild);
+}
+
+bool TestManyChildAllocsChild::RecvGo()
+{
+ for (int i = 0; i < NALLOCS; ++i) {
+ PTestManyChildAllocsSubChild* child =
+ SendPTestManyChildAllocsSubConstructor();
+
+ if (!child)
+ fail("can't send ctor()");
+
+ if (!child->SendHello())
+ fail("can't send Hello()");
+ }
+
+ size_t len = ManagedPTestManyChildAllocsSubChild().Count();
+ if (NALLOCS != len)
+ fail("expected %lu kids, got %lu", NALLOCS, len);
+
+ if (!SendDone())
+ fail("can't send Done()");
+
+ return true;
+}
+
+bool
+TestManyChildAllocsChild::DeallocPTestManyChildAllocsSubChild(
+ PTestManyChildAllocsSubChild* __a)
+{
+ delete __a; return true;
+}
+
+PTestManyChildAllocsSubChild*
+TestManyChildAllocsChild::AllocPTestManyChildAllocsSubChild()
+{
+ return new TestManyChildAllocsSubChild();
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestManyChildAllocs.h b/ipc/ipdl/test/cxx/TestManyChildAllocs.h
new file mode 100644
index 0000000000..d29a35cb82
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestManyChildAllocs.h
@@ -0,0 +1,94 @@
+#ifndef mozilla__ipdltest_TestManyChildAllocs_h
+#define mozilla__ipdltest_TestManyChildAllocs_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestManyChildAllocsParent.h"
+#include "mozilla/_ipdltest/PTestManyChildAllocsChild.h"
+
+#include "mozilla/_ipdltest/PTestManyChildAllocsSubParent.h"
+#include "mozilla/_ipdltest/PTestManyChildAllocsSubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+// top-level protocol
+
+class TestManyChildAllocsParent :
+ public PTestManyChildAllocsParent
+{
+public:
+ TestManyChildAllocsParent();
+ virtual ~TestManyChildAllocsParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvDone() override;
+ virtual bool DeallocPTestManyChildAllocsSubParent(PTestManyChildAllocsSubParent* __a) override;
+ virtual PTestManyChildAllocsSubParent* AllocPTestManyChildAllocsSubParent() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestManyChildAllocsChild :
+ public PTestManyChildAllocsChild
+{
+public:
+ TestManyChildAllocsChild();
+ virtual ~TestManyChildAllocsChild();
+
+protected:
+ virtual bool RecvGo() override;
+ virtual bool DeallocPTestManyChildAllocsSubChild(PTestManyChildAllocsSubChild* __a) override;
+ virtual PTestManyChildAllocsSubChild* AllocPTestManyChildAllocsSubChild() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+// do-nothing sub-protocol actors
+
+class TestManyChildAllocsSubParent :
+ public PTestManyChildAllocsSubParent
+{
+public:
+ TestManyChildAllocsSubParent() { }
+ virtual ~TestManyChildAllocsSubParent() { }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+ virtual bool RecvHello() override { return true; }
+};
+
+
+class TestManyChildAllocsSubChild :
+ public PTestManyChildAllocsSubChild
+{
+public:
+ TestManyChildAllocsSubChild() { }
+ virtual ~TestManyChildAllocsSubChild() { }
+};
+
+
+
+} // namepsace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestManyChildAllocs_h
diff --git a/ipc/ipdl/test/cxx/TestMultiMgrs.cpp b/ipc/ipdl/test/cxx/TestMultiMgrs.cpp
new file mode 100644
index 0000000000..422b3fea33
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestMultiMgrs.cpp
@@ -0,0 +1,104 @@
+#include "TestMultiMgrs.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#include "mozilla/ipc/ProtocolUtils.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+void
+TestMultiMgrsParent::Main()
+{
+ TestMultiMgrsLeftParent* leftie = new TestMultiMgrsLeftParent();
+ if (!SendPTestMultiMgrsLeftConstructor(leftie))
+ fail("error sending ctor");
+
+ TestMultiMgrsRightParent* rightie = new TestMultiMgrsRightParent();
+ if (!SendPTestMultiMgrsRightConstructor(rightie))
+ fail("error sending ctor");
+
+ TestMultiMgrsBottomParent* bottomL = new TestMultiMgrsBottomParent();
+ if (!leftie->SendPTestMultiMgrsBottomConstructor(bottomL))
+ fail("error sending ctor");
+
+ TestMultiMgrsBottomParent* bottomR = new TestMultiMgrsBottomParent();
+ if (!rightie->SendPTestMultiMgrsBottomConstructor(bottomR))
+ fail("error sending ctor");
+
+ if (!leftie->HasChild(bottomL))
+ fail("leftie didn't have a child it was supposed to!");
+ if (leftie->HasChild(bottomR))
+ fail("leftie had rightie's child!");
+
+ if (!rightie->HasChild(bottomR))
+ fail("rightie didn't have a child it was supposed to!");
+ if (rightie->HasChild(bottomL))
+ fail("rightie had rightie's child!");
+
+ if (!SendCheck())
+ fail("couldn't kick off the child-side check");
+}
+
+bool
+TestMultiMgrsParent::RecvOK()
+{
+ Close();
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+bool
+TestMultiMgrsLeftChild::RecvPTestMultiMgrsBottomConstructor(
+ PTestMultiMgrsBottomChild* actor)
+{
+ static_cast<TestMultiMgrsChild*>(Manager())->mBottomL = actor;
+ return true;
+}
+
+bool
+TestMultiMgrsRightChild::RecvPTestMultiMgrsBottomConstructor(
+ PTestMultiMgrsBottomChild* actor)
+{
+ static_cast<TestMultiMgrsChild*>(Manager())->mBottomR = actor;
+ return true;
+}
+
+bool
+TestMultiMgrsChild::RecvCheck()
+{
+ if (1 != ManagedPTestMultiMgrsLeftChild().Count())
+ fail("where's leftie?");
+ if (1 != ManagedPTestMultiMgrsRightChild().Count())
+ fail("where's rightie?");
+
+ TestMultiMgrsLeftChild* leftie =
+ static_cast<TestMultiMgrsLeftChild*>(
+ LoneManagedOrNullAsserts(ManagedPTestMultiMgrsLeftChild()));
+ TestMultiMgrsRightChild* rightie =
+ static_cast<TestMultiMgrsRightChild*>(
+ LoneManagedOrNullAsserts(ManagedPTestMultiMgrsRightChild()));
+
+ if (!leftie->HasChild(mBottomL))
+ fail("leftie didn't have a child it was supposed to!");
+ if (leftie->HasChild(mBottomR))
+ fail("leftie had rightie's child!");
+
+ if (!rightie->HasChild(mBottomR))
+ fail("rightie didn't have a child it was supposed to!");
+ if (rightie->HasChild(mBottomL))
+ fail("rightie had leftie's child!");
+
+ if (!SendOK())
+ fail("couldn't send OK()");
+
+ return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestMultiMgrs.h b/ipc/ipdl/test/cxx/TestMultiMgrs.h
new file mode 100644
index 0000000000..3dfab7430b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestMultiMgrs.h
@@ -0,0 +1,250 @@
+#ifndef mozilla__ipdltest_TestMultiMgrs_h
+#define mozilla__ipdltest_TestMultiMgrs_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestMultiMgrsParent.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsChild.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsBottomParent.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsBottomChild.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsLeftParent.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsLeftChild.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsRightParent.h"
+#include "mozilla/_ipdltest/PTestMultiMgrsRightChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent side
+//
+
+class TestMultiMgrsBottomParent :
+ public PTestMultiMgrsBottomParent
+{
+public:
+ TestMultiMgrsBottomParent() { }
+ virtual ~TestMultiMgrsBottomParent() { }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+class TestMultiMgrsLeftParent :
+ public PTestMultiMgrsLeftParent
+{
+public:
+ TestMultiMgrsLeftParent() { }
+ virtual ~TestMultiMgrsLeftParent() { }
+
+ bool HasChild(TestMultiMgrsBottomParent* c)
+ {
+ return ManagedPTestMultiMgrsBottomParent().Contains(c);
+ }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+
+ virtual PTestMultiMgrsBottomParent* AllocPTestMultiMgrsBottomParent() override
+ {
+ return new TestMultiMgrsBottomParent();
+ }
+
+ virtual bool DeallocPTestMultiMgrsBottomParent(PTestMultiMgrsBottomParent* actor) override
+ {
+ delete actor;
+ return true;
+ }
+};
+
+class TestMultiMgrsRightParent :
+ public PTestMultiMgrsRightParent
+{
+public:
+ TestMultiMgrsRightParent() { }
+ virtual ~TestMultiMgrsRightParent() { }
+
+ bool HasChild(TestMultiMgrsBottomParent* c)
+ {
+ return ManagedPTestMultiMgrsBottomParent().Contains(c);
+ }
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override {}
+
+ virtual PTestMultiMgrsBottomParent* AllocPTestMultiMgrsBottomParent() override
+ {
+ return new TestMultiMgrsBottomParent();
+ }
+
+ virtual bool DeallocPTestMultiMgrsBottomParent(PTestMultiMgrsBottomParent* actor) override
+ {
+ delete actor;
+ return true;
+ }
+};
+
+class TestMultiMgrsParent :
+ public PTestMultiMgrsParent
+{
+public:
+ TestMultiMgrsParent() { }
+ virtual ~TestMultiMgrsParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvOK() override;
+
+ virtual PTestMultiMgrsLeftParent* AllocPTestMultiMgrsLeftParent() override
+ {
+ return new TestMultiMgrsLeftParent();
+ }
+
+ virtual bool DeallocPTestMultiMgrsLeftParent(PTestMultiMgrsLeftParent* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual PTestMultiMgrsRightParent* AllocPTestMultiMgrsRightParent() override
+ {
+ return new TestMultiMgrsRightParent();
+ }
+
+ virtual bool DeallocPTestMultiMgrsRightParent(PTestMultiMgrsRightParent* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Child side
+//
+
+class TestMultiMgrsBottomChild :
+ public PTestMultiMgrsBottomChild
+{
+public:
+ TestMultiMgrsBottomChild() { }
+ virtual ~TestMultiMgrsBottomChild() { }
+};
+
+class TestMultiMgrsLeftChild :
+ public PTestMultiMgrsLeftChild
+{
+public:
+ TestMultiMgrsLeftChild() { }
+ virtual ~TestMultiMgrsLeftChild() { }
+
+ bool HasChild(PTestMultiMgrsBottomChild* c)
+ {
+ return ManagedPTestMultiMgrsBottomChild().Contains(c);
+ }
+
+protected:
+ virtual bool RecvPTestMultiMgrsBottomConstructor(PTestMultiMgrsBottomChild* actor) override;
+
+ virtual PTestMultiMgrsBottomChild* AllocPTestMultiMgrsBottomChild() override
+ {
+ return new TestMultiMgrsBottomChild();
+ }
+
+ virtual bool DeallocPTestMultiMgrsBottomChild(PTestMultiMgrsBottomChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+};
+
+class TestMultiMgrsRightChild :
+ public PTestMultiMgrsRightChild
+{
+public:
+ TestMultiMgrsRightChild() { }
+ virtual ~TestMultiMgrsRightChild() { }
+
+ bool HasChild(PTestMultiMgrsBottomChild* c)
+ {
+ return ManagedPTestMultiMgrsBottomChild().Contains(c);
+ }
+
+protected:
+ virtual bool RecvPTestMultiMgrsBottomConstructor(PTestMultiMgrsBottomChild* actor) override;
+
+ virtual PTestMultiMgrsBottomChild* AllocPTestMultiMgrsBottomChild() override
+ {
+ return new TestMultiMgrsBottomChild();
+ }
+
+ virtual bool DeallocPTestMultiMgrsBottomChild(PTestMultiMgrsBottomChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+};
+
+class TestMultiMgrsChild :
+ public PTestMultiMgrsChild
+{
+public:
+ TestMultiMgrsChild() { }
+ virtual ~TestMultiMgrsChild() { }
+
+ void Main();
+
+ PTestMultiMgrsBottomChild* mBottomL;
+ PTestMultiMgrsBottomChild* mBottomR;
+
+protected:
+ virtual bool RecvCheck() override;
+
+ virtual PTestMultiMgrsLeftChild* AllocPTestMultiMgrsLeftChild() override
+ {
+ return new TestMultiMgrsLeftChild();
+ }
+
+ virtual bool DeallocPTestMultiMgrsLeftChild(PTestMultiMgrsLeftChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual PTestMultiMgrsRightChild* AllocPTestMultiMgrsRightChild() override
+ {
+ return new TestMultiMgrsRightChild();
+ }
+
+ virtual bool DeallocPTestMultiMgrsRightChild(PTestMultiMgrsRightChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestMultiMgrs_h
diff --git a/ipc/ipdl/test/cxx/TestNestedLoops.cpp b/ipc/ipdl/test/cxx/TestNestedLoops.cpp
new file mode 100644
index 0000000000..0e0ba496de
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestNestedLoops.cpp
@@ -0,0 +1,98 @@
+#include "base/basictypes.h"
+
+#include "nsThreadUtils.h"
+
+#include "TestNestedLoops.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestNestedLoopsParent::TestNestedLoopsParent() : mBreakNestedLoop(false)
+{
+ MOZ_COUNT_CTOR(TestNestedLoopsParent);
+}
+
+TestNestedLoopsParent::~TestNestedLoopsParent()
+{
+ MOZ_COUNT_DTOR(TestNestedLoopsParent);
+}
+
+void
+TestNestedLoopsParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+
+ // sigh ... spin for a while to let Nonce arrive
+ puts(" (sleeping to wait for nonce ... sorry)");
+ PR_Sleep(5000);
+
+ // while waiting for the reply to R, we'll receive Nonce
+ if (!CallR())
+ fail("calling R");
+
+ Close();
+}
+
+bool
+TestNestedLoopsParent::RecvNonce()
+{
+ // if we have an OnMaybeDequeueOne waiting for us (we may not, due
+ // to the inherent race condition in this test, then this event
+ // must be ordered after it in the queue
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestNestedLoopsParent::BreakNestedLoop));
+
+ // sigh ... spin for a while to let the reply to R arrive
+ puts(" (sleeping to wait for reply to R ... sorry)");
+ PR_Sleep(5000);
+
+ // sigh ... we have no idea when code might do this
+ do {
+ if (!NS_ProcessNextEvent(nullptr, false))
+ fail("expected at least one pending event");
+ } while (!mBreakNestedLoop);
+
+ return true;
+}
+
+void
+TestNestedLoopsParent::BreakNestedLoop()
+{
+ mBreakNestedLoop = true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestNestedLoopsChild::TestNestedLoopsChild()
+{
+ MOZ_COUNT_CTOR(TestNestedLoopsChild);
+}
+
+TestNestedLoopsChild::~TestNestedLoopsChild()
+{
+ MOZ_COUNT_DTOR(TestNestedLoopsChild);
+}
+
+bool
+TestNestedLoopsChild::RecvStart()
+{
+ if (!SendNonce())
+ fail("sending Nonce");
+ return true;
+}
+
+bool
+TestNestedLoopsChild::AnswerR()
+{
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestNestedLoops.h b/ipc/ipdl/test/cxx/TestNestedLoops.h
new file mode 100644
index 0000000000..a91258a434
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestNestedLoops.h
@@ -0,0 +1,67 @@
+#ifndef mozilla__ipdltest_TestNestedLoops_h
+#define mozilla__ipdltest_TestNestedLoops_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestNestedLoopsParent.h"
+#include "mozilla/_ipdltest/PTestNestedLoopsChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestNestedLoopsParent :
+ public PTestNestedLoopsParent
+{
+public:
+ TestNestedLoopsParent();
+ virtual ~TestNestedLoopsParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvNonce() override;
+
+ void BreakNestedLoop();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ bool mBreakNestedLoop;
+};
+
+
+class TestNestedLoopsChild :
+ public PTestNestedLoopsChild
+{
+public:
+ TestNestedLoopsChild();
+ virtual ~TestNestedLoopsChild();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual bool AnswerR() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestNestedLoops_h
diff --git a/ipc/ipdl/test/cxx/TestOpens.cpp b/ipc/ipdl/test/cxx/TestOpens.cpp
new file mode 100644
index 0000000000..cb1ed00b59
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestOpens.cpp
@@ -0,0 +1,251 @@
+#include "base/task.h"
+#include "base/thread.h"
+
+#include "TestOpens.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using namespace mozilla::ipc;
+
+using base::ProcessHandle;
+using base::Thread;
+
+namespace mozilla {
+// NB: this is generally bad style, but I am lazy.
+using namespace _ipdltest;
+using namespace _ipdltest2;
+
+static MessageLoop* gMainThread;
+
+static void
+AssertNotMainThread()
+{
+ if (!gMainThread)
+ fail("gMainThread is not initialized");
+ if (MessageLoop::current() == gMainThread)
+ fail("unexpectedly called on the main thread");
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+// Thread on which TestOpensOpenedParent runs
+static Thread* gParentThread;
+
+void
+TestOpensParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+static void
+OpenParent(TestOpensOpenedParent* aParent,
+ Transport* aTransport, base::ProcessId aOtherPid)
+{
+ AssertNotMainThread();
+
+ // Open the actor on the off-main thread to park it there.
+ // Messages will be delivered to this thread's message loop
+ // instead of the main thread's.
+ if (!aParent->Open(aTransport, aOtherPid,
+ XRE_GetIOMessageLoop(), ipc::ParentSide))
+ fail("opening Parent");
+}
+
+PTestOpensOpenedParent*
+TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport,
+ ProcessId otherPid)
+{
+ gMainThread = MessageLoop::current();
+
+ gParentThread = new Thread("ParentThread");
+ if (!gParentThread->Start())
+ fail("starting parent thread");
+
+ TestOpensOpenedParent* a = new TestOpensOpenedParent(transport);
+ gParentThread->message_loop()->PostTask(
+ NewRunnableFunction(OpenParent, a, transport, otherPid));
+
+ return a;
+}
+
+void
+TestOpensParent::ActorDestroy(ActorDestroyReason why)
+{
+ // Stops the thread and joins it
+ delete gParentThread;
+
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+}
+
+bool
+TestOpensOpenedParent::RecvHello()
+{
+ AssertNotMainThread();
+ return SendHi();
+}
+
+bool
+TestOpensOpenedParent::RecvHelloSync()
+{
+ AssertNotMainThread();
+ return true;
+}
+
+bool
+TestOpensOpenedParent::AnswerHelloRpc()
+{
+ AssertNotMainThread();
+ return CallHiRpc();
+}
+
+static void
+ShutdownTestOpensOpenedParent(TestOpensOpenedParent* parent,
+ Transport* transport)
+{
+ delete parent;
+}
+
+void
+TestOpensOpenedParent::ActorDestroy(ActorDestroyReason why)
+{
+ AssertNotMainThread();
+
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up.
+ gParentThread->message_loop()->PostTask(
+ NewRunnableFunction(ShutdownTestOpensOpenedParent,
+ this, mTransport));
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+static TestOpensChild* gOpensChild;
+// Thread on which TestOpensOpenedChild runs
+static Thread* gChildThread;
+
+TestOpensChild::TestOpensChild()
+{
+ gOpensChild = this;
+}
+
+bool
+TestOpensChild::RecvStart()
+{
+ if (!PTestOpensOpened::Open(this))
+ fail("opening PTestOpensOpened");
+ return true;
+}
+
+static void
+OpenChild(TestOpensOpenedChild* aChild,
+ Transport* aTransport, base::ProcessId aOtherPid)
+{
+ AssertNotMainThread();
+
+ // Open the actor on the off-main thread to park it there.
+ // Messages will be delivered to this thread's message loop
+ // instead of the main thread's.
+ if (!aChild->Open(aTransport, aOtherPid,
+ XRE_GetIOMessageLoop(), ipc::ChildSide))
+ fail("opening Child");
+
+ // Kick off the unit tests
+ if (!aChild->SendHello())
+ fail("sending Hello");
+}
+
+PTestOpensOpenedChild*
+TestOpensChild::AllocPTestOpensOpenedChild(Transport* transport,
+ ProcessId otherPid)
+{
+ gMainThread = MessageLoop::current();
+
+ gChildThread = new Thread("ChildThread");
+ if (!gChildThread->Start())
+ fail("starting child thread");
+
+ TestOpensOpenedChild* a = new TestOpensOpenedChild(transport);
+ gChildThread->message_loop()->PostTask(
+ NewRunnableFunction(OpenChild, a, transport, otherPid));
+
+ return a;
+}
+
+void
+TestOpensChild::ActorDestroy(ActorDestroyReason why)
+{
+ // Stops the thread and joins it
+ delete gChildThread;
+
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+}
+
+bool
+TestOpensOpenedChild::RecvHi()
+{
+ AssertNotMainThread();
+
+ if (!SendHelloSync())
+ fail("sending HelloSync");
+ if (!CallHelloRpc())
+ fail("calling HelloRpc");
+ if (!mGotHi)
+ fail("didn't answer HiRpc");
+
+ // Need to close the channel without message-processing frames on
+ // the C++ stack
+ MessageLoop::current()->PostTask(
+ NewNonOwningRunnableMethod(this, &TestOpensOpenedChild::Close));
+ return true;
+}
+
+bool
+TestOpensOpenedChild::AnswerHiRpc()
+{
+ AssertNotMainThread();
+
+ mGotHi = true; // d00d
+ return true;
+}
+
+static void
+ShutdownTestOpensOpenedChild(TestOpensOpenedChild* child,
+ Transport* transport)
+{
+ delete child;
+
+ // Kick off main-thread shutdown.
+ gMainThread->PostTask(
+ NewNonOwningRunnableMethod(gOpensChild, &TestOpensChild::Close));
+}
+
+void
+TestOpensOpenedChild::ActorDestroy(ActorDestroyReason why)
+{
+ AssertNotMainThread();
+
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ // ActorDestroy() is just a callback from IPDL-generated code,
+ // which needs the top-level actor (this) to stay alive a little
+ // longer so other things can be cleaned up. Defer shutdown to
+ // let cleanup finish.
+ gChildThread->message_loop()->PostTask(
+ NewRunnableFunction(ShutdownTestOpensOpenedChild,
+ this, mTransport));
+}
+
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestOpens.h b/ipc/ipdl/test/cxx/TestOpens.h
new file mode 100644
index 0000000000..b83645a823
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestOpens.h
@@ -0,0 +1,107 @@
+#ifndef mozilla__ipdltest_TestOpens_h
+#define mozilla__ipdltest_TestOpens_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestOpensParent.h"
+#include "mozilla/_ipdltest/PTestOpensChild.h"
+
+#include "mozilla/_ipdltest2/PTestOpensOpenedParent.h"
+#include "mozilla/_ipdltest2/PTestOpensOpenedChild.h"
+
+namespace mozilla {
+
+// parent process
+
+namespace _ipdltest {
+
+class TestOpensParent : public PTestOpensParent
+{
+public:
+ TestOpensParent() {}
+ virtual ~TestOpensParent() {}
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual PTestOpensOpenedParent*
+ AllocPTestOpensOpenedParent(Transport* transport, ProcessId otherProcess) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest
+
+namespace _ipdltest2 {
+
+class TestOpensOpenedParent : public PTestOpensOpenedParent
+{
+public:
+ explicit TestOpensOpenedParent(Transport* aTransport)
+ : mTransport(aTransport)
+ {}
+ virtual ~TestOpensOpenedParent() {}
+
+protected:
+ virtual bool RecvHello() override;
+ virtual bool RecvHelloSync() override;
+ virtual bool AnswerHelloRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ Transport* mTransport;
+};
+
+} // namespace _ipdltest2
+
+// child process
+
+namespace _ipdltest {
+
+class TestOpensChild : public PTestOpensChild
+{
+public:
+ TestOpensChild();
+ virtual ~TestOpensChild() {}
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual PTestOpensOpenedChild*
+ AllocPTestOpensOpenedChild(Transport* transport, ProcessId otherProcess) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace _ipdltest
+
+namespace _ipdltest2 {
+
+class TestOpensOpenedChild : public PTestOpensOpenedChild
+{
+public:
+ explicit TestOpensOpenedChild(Transport* aTransport)
+ : mGotHi(false)
+ , mTransport(aTransport)
+ {}
+ virtual ~TestOpensOpenedChild() {}
+
+protected:
+ virtual bool RecvHi() override;
+ virtual bool AnswerHiRpc() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ bool mGotHi;
+ Transport* mTransport;
+};
+
+} // namespace _ipdltest2
+
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestOpens_h
diff --git a/ipc/ipdl/test/cxx/TestRPC.cpp b/ipc/ipdl/test/cxx/TestRPC.cpp
new file mode 100644
index 0000000000..2dfc51d33b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRPC.cpp
@@ -0,0 +1,152 @@
+#include "TestRPC.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#if defined(OS_POSIX)
+#include <unistd.h>
+#else
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestRPCParent::TestRPCParent()
+ : reentered_(false),
+ resolved_first_cpow_(false)
+{
+ MOZ_COUNT_CTOR(TestRPCParent);
+}
+
+TestRPCParent::~TestRPCParent()
+{
+ MOZ_COUNT_DTOR(TestRPCParent);
+}
+
+void
+TestRPCParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestRPCParent::RecvTest1_Start(uint32_t* aResult)
+{
+ uint32_t result;
+ if (!SendTest1_InnerQuery(&result))
+ fail("SendTest1_InnerQuery");
+ if (result != 300)
+ fail("Wrong result (expected 300)");
+
+ *aResult = 100;
+ return true;
+}
+
+bool
+TestRPCParent::RecvTest1_InnerEvent(uint32_t* aResult)
+{
+ uint32_t result;
+ if (!SendTest1_NoReenter(&result))
+ fail("SendTest1_NoReenter");
+ if (result != 400)
+ fail("Wrong result (expected 400)");
+
+ *aResult = 200;
+ return true;
+}
+
+bool
+TestRPCParent::RecvTest2_Start()
+{
+ // Send a CPOW. During this time, we must NOT process the RPC message, as
+ // we could start receiving CPOW replies out-of-order.
+ if (!SendTest2_FirstUrgent())
+ fail("SendTest2_FirstUrgent");
+
+ MOZ_ASSERT(!reentered_);
+ resolved_first_cpow_ = true;
+ return true;
+}
+
+bool
+TestRPCParent::RecvTest2_OutOfOrder()
+{
+ // Send a CPOW. If this RPC call was initiated while waiting for the first
+ // CPOW to resolve, replies will be processed out of order, and we'll crash.
+ if (!SendTest2_SecondUrgent())
+ fail("SendTest2_SecondUrgent");
+
+ reentered_ = true;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+
+TestRPCChild::TestRPCChild()
+{
+ MOZ_COUNT_CTOR(TestRPCChild);
+}
+
+TestRPCChild::~TestRPCChild()
+{
+ MOZ_COUNT_DTOR(TestRPCChild);
+}
+
+bool
+TestRPCChild::RecvStart()
+{
+ uint32_t result;
+ if (!SendTest1_Start(&result))
+ fail("SendTest1_Start");
+ if (result != 100)
+ fail("Wrong result (expected 100)");
+
+ if (!SendTest2_Start())
+ fail("SendTest2_Start");
+
+ if (!SendTest2_OutOfOrder())
+ fail("SendTest2_OutOfOrder");
+
+ Close();
+ return true;
+}
+
+bool
+TestRPCChild::RecvTest1_InnerQuery(uint32_t* aResult)
+{
+ uint32_t result;
+ if (!SendTest1_InnerEvent(&result))
+ fail("SendTest1_InnerEvent");
+ if (result != 200)
+ fail("Wrong result (expected 200)");
+
+ *aResult = 300;
+ return true;
+}
+
+bool
+TestRPCChild::RecvTest1_NoReenter(uint32_t* aResult)
+{
+ *aResult = 400;
+ return true;
+}
+
+bool
+TestRPCChild::RecvTest2_FirstUrgent()
+{
+ return true;
+}
+
+bool
+TestRPCChild::RecvTest2_SecondUrgent()
+{
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestRPC.h b/ipc/ipdl/test/cxx/TestRPC.h
new file mode 100644
index 0000000000..0a11b3e761
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRPC.h
@@ -0,0 +1,74 @@
+#ifndef mozilla__ipdltest_TestRPC_h
+#define mozilla__ipdltest_TestRPC_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestRPCParent.h"
+#include "mozilla/_ipdltest/PTestRPCChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestRPCParent :
+ public PTestRPCParent
+{
+public:
+ TestRPCParent();
+ virtual ~TestRPCParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ bool RecvTest1_Start(uint32_t* aResult) override;
+ bool RecvTest1_InnerEvent(uint32_t* aResult) override;
+ bool RecvTest2_Start() override;
+ bool RecvTest2_OutOfOrder() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ if (!reentered_)
+ fail("never processed raced RPC call!");
+ if (!resolved_first_cpow_)
+ fail("never resolved first CPOW!");
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ bool reentered_;
+ bool resolved_first_cpow_;
+};
+
+
+class TestRPCChild :
+ public PTestRPCChild
+{
+public:
+ TestRPCChild();
+ virtual ~TestRPCChild();
+
+ bool RecvStart() override;
+ bool RecvTest1_InnerQuery(uint32_t* aResult) override;
+ bool RecvTest1_NoReenter(uint32_t* aResult) override;
+ bool RecvTest2_FirstUrgent() override;
+ bool RecvTest2_SecondUrgent() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestRPC_h
diff --git a/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp b/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp
new file mode 100644
index 0000000000..7402f30d3a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRaceDeadlock.cpp
@@ -0,0 +1,131 @@
+#include "TestRaceDeadlock.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+// #define TEST_TIMEOUT 5000
+
+using namespace mozilla::ipc;
+typedef mozilla::ipc::MessageChannel::Message Message;
+typedef mozilla::ipc::MessageChannel::MessageInfo MessageInfo;
+
+namespace mozilla {
+namespace _ipdltest {
+
+static RacyInterruptPolicy
+MediateRace(const MessageInfo& parent, const MessageInfo& child)
+{
+ return (PTestRaceDeadlock::Msg_Win__ID == parent.type()) ?
+ RIPParentWins : RIPChildWins;
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestRaceDeadlockParent::TestRaceDeadlockParent()
+{
+ MOZ_COUNT_CTOR(TestRaceDeadlockParent);
+}
+
+TestRaceDeadlockParent::~TestRaceDeadlockParent()
+{
+ MOZ_COUNT_DTOR(TestRaceDeadlockParent);
+}
+
+void
+TestRaceDeadlockParent::Main()
+{
+ Test1();
+
+ Close();
+}
+
+bool
+TestRaceDeadlockParent::ShouldContinueFromReplyTimeout()
+{
+ fail("This test should not hang");
+ GetIPCChannel()->CloseWithTimeout();
+ return false;
+}
+
+void
+TestRaceDeadlockParent::Test1()
+{
+#if defined(TEST_TIMEOUT)
+ SetReplyTimeoutMs(TEST_TIMEOUT);
+#endif
+ if (!SendStartRace()) {
+ fail("sending StartRace");
+ }
+ if (!CallRpc()) {
+ fail("calling Rpc");
+ }
+}
+
+bool
+TestRaceDeadlockParent::AnswerLose()
+{
+ return true;
+}
+
+RacyInterruptPolicy
+TestRaceDeadlockParent::MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child)
+{
+ return MediateRace(parent, child);
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRaceDeadlockChild::TestRaceDeadlockChild()
+{
+ MOZ_COUNT_CTOR(TestRaceDeadlockChild);
+}
+
+TestRaceDeadlockChild::~TestRaceDeadlockChild()
+{
+ MOZ_COUNT_DTOR(TestRaceDeadlockChild);
+}
+
+bool
+TestRaceDeadlockParent::RecvStartRace()
+{
+ if (!CallWin()) {
+ fail("calling Win");
+ }
+ return true;
+}
+
+bool
+TestRaceDeadlockChild::RecvStartRace()
+{
+ if (!SendStartRace()) {
+ fail("calling SendStartRace");
+ }
+ if (!CallLose()) {
+ fail("calling Lose");
+ }
+ return true;
+}
+
+bool
+TestRaceDeadlockChild::AnswerWin()
+{
+ return true;
+}
+
+bool
+TestRaceDeadlockChild::AnswerRpc()
+{
+ return true;
+}
+
+RacyInterruptPolicy
+TestRaceDeadlockChild::MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child)
+{
+ return MediateRace(parent, child);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestRaceDeadlock.h b/ipc/ipdl/test/cxx/TestRaceDeadlock.h
new file mode 100644
index 0000000000..7723c7629a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRaceDeadlock.h
@@ -0,0 +1,77 @@
+#ifndef mozilla__ipdltest_TestRaceDeadlock_h
+#define mozilla__ipdltest_TestRaceDeadlock_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestRaceDeadlockParent.h"
+#include "mozilla/_ipdltest/PTestRaceDeadlockChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestRaceDeadlockParent :
+ public PTestRaceDeadlockParent
+{
+public:
+ TestRaceDeadlockParent();
+ virtual ~TestRaceDeadlockParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool ShouldContinueFromReplyTimeout() override;
+
+ void Test1();
+
+ virtual bool RecvStartRace() override;
+ virtual bool AnswerLose() override;
+
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestRaceDeadlockChild :
+ public PTestRaceDeadlockChild
+{
+public:
+ TestRaceDeadlockChild();
+ virtual ~TestRaceDeadlockChild();
+
+protected:
+ virtual bool RecvStartRace() override;
+
+ virtual bool AnswerWin() override;
+
+ virtual bool AnswerRpc() override;
+
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestRaceDeadlock_h
diff --git a/ipc/ipdl/test/cxx/TestRaceDeferral.cpp b/ipc/ipdl/test/cxx/TestRaceDeferral.cpp
new file mode 100644
index 0000000000..b62d0e2f3c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRaceDeferral.cpp
@@ -0,0 +1,118 @@
+#include "TestRaceDeferral.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using namespace mozilla::ipc;
+typedef mozilla::ipc::MessageChannel::Message Message;
+typedef mozilla::ipc::MessageChannel::MessageInfo MessageInfo;
+
+namespace mozilla {
+namespace _ipdltest {
+
+static RacyInterruptPolicy
+MediateRace(const MessageInfo& parent, const MessageInfo& child)
+{
+ return (PTestRaceDeferral::Msg_Win__ID == parent.type()) ?
+ RIPParentWins : RIPChildWins;
+}
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestRaceDeferralParent::TestRaceDeferralParent()
+ : mProcessedLose(false)
+{
+ MOZ_COUNT_CTOR(TestRaceDeferralParent);
+}
+
+TestRaceDeferralParent::~TestRaceDeferralParent()
+{
+ MOZ_COUNT_DTOR(TestRaceDeferralParent);
+
+ if (!mProcessedLose)
+ fail("never processed Lose");
+}
+
+void
+TestRaceDeferralParent::Main()
+{
+ Test1();
+
+ Close();
+}
+
+void
+TestRaceDeferralParent::Test1()
+{
+ if (!SendStartRace())
+ fail("sending StartRace");
+
+ if (!CallWin())
+ fail("calling Win");
+ if (mProcessedLose)
+ fail("Lose didn't lose");
+
+ if (!CallRpc())
+ fail("calling Rpc");
+ if (!mProcessedLose)
+ fail("didn't resolve Rpc vs. Lose 'race' correctly");
+}
+
+bool
+TestRaceDeferralParent::AnswerLose()
+{
+ if (mProcessedLose)
+ fail("processed Lose twice");
+ mProcessedLose = true;
+ return true;
+}
+
+RacyInterruptPolicy
+TestRaceDeferralParent::MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child)
+{
+ return MediateRace(parent, child);
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRaceDeferralChild::TestRaceDeferralChild()
+{
+ MOZ_COUNT_CTOR(TestRaceDeferralChild);
+}
+
+TestRaceDeferralChild::~TestRaceDeferralChild()
+{
+ MOZ_COUNT_DTOR(TestRaceDeferralChild);
+}
+
+bool
+TestRaceDeferralChild::RecvStartRace()
+{
+ if (!CallLose())
+ fail("calling Lose");
+ return true;
+}
+
+bool
+TestRaceDeferralChild::AnswerWin()
+{
+ return true;
+}
+
+bool
+TestRaceDeferralChild::AnswerRpc()
+{
+ return true;
+}
+
+RacyInterruptPolicy
+TestRaceDeferralChild::MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child)
+{
+ return MediateRace(parent, child);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestRaceDeferral.h b/ipc/ipdl/test/cxx/TestRaceDeferral.h
new file mode 100644
index 0000000000..e465cf77c8
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRaceDeferral.h
@@ -0,0 +1,76 @@
+#ifndef mozilla__ipdltest_TestRaceDeferral_h
+#define mozilla__ipdltest_TestRaceDeferral_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestRaceDeferralParent.h"
+#include "mozilla/_ipdltest/PTestRaceDeferralChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class TestRaceDeferralParent :
+ public PTestRaceDeferralParent
+{
+public:
+ TestRaceDeferralParent();
+ virtual ~TestRaceDeferralParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ void Test1();
+
+ virtual bool AnswerLose() override;
+
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ bool mProcessedLose;
+};
+
+
+class TestRaceDeferralChild :
+ public PTestRaceDeferralChild
+{
+public:
+ TestRaceDeferralChild();
+ virtual ~TestRaceDeferralChild();
+
+protected:
+ virtual bool RecvStartRace() override;
+
+ virtual bool AnswerWin() override;
+
+ virtual bool AnswerRpc() override;
+
+ virtual mozilla::ipc::RacyInterruptPolicy
+ MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestRaceDeferral_h
diff --git a/ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp
new file mode 100644
index 0000000000..513b84f73e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.cpp
@@ -0,0 +1,119 @@
+#include "TestRacyInterruptReplies.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestRacyInterruptRepliesParent::TestRacyInterruptRepliesParent() : mReplyNum(0)
+{
+ MOZ_COUNT_CTOR(TestRacyInterruptRepliesParent);
+}
+
+TestRacyInterruptRepliesParent::~TestRacyInterruptRepliesParent()
+{
+ MOZ_COUNT_DTOR(TestRacyInterruptRepliesParent);
+}
+
+void
+TestRacyInterruptRepliesParent::Main()
+{
+ int replyNum = -1;
+ if (!CallR_(&replyNum))
+ fail("calling R()");
+
+ if (1 != replyNum)
+ fail("this should have been the first reply to R()");
+
+ if (!SendChildTest())
+ fail("sending ChildStart");
+}
+
+bool
+TestRacyInterruptRepliesParent::RecvA_()
+{
+ int replyNum = -1;
+ // this R() call races with the reply being generated by the other
+ // side to the R() call from Main(). This is a pretty nasty edge
+ // case for which one could argue we're breaking in-order message
+ // delivery, since this side will process the second reply to R()
+ // before the first.
+ if (!CallR_(&replyNum))
+ fail("calling R()");
+
+ if (2 != replyNum)
+ fail("this should have been the second reply to R()");
+
+ return true;
+}
+
+bool
+TestRacyInterruptRepliesParent::Answer_R(int* replyNum)
+{
+ *replyNum = ++mReplyNum;
+
+ if (1 == *replyNum)
+ if (!Send_A())
+ fail("sending _A()");
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRacyInterruptRepliesChild::TestRacyInterruptRepliesChild() : mReplyNum(0)
+{
+ MOZ_COUNT_CTOR(TestRacyInterruptRepliesChild);
+}
+
+TestRacyInterruptRepliesChild::~TestRacyInterruptRepliesChild()
+{
+ MOZ_COUNT_DTOR(TestRacyInterruptRepliesChild);
+}
+
+bool
+TestRacyInterruptRepliesChild::AnswerR_(int* replyNum)
+{
+ *replyNum = ++mReplyNum;
+
+ if (1 == *replyNum)
+ SendA_();
+
+ return true;
+}
+
+bool
+TestRacyInterruptRepliesChild::RecvChildTest()
+{
+ int replyNum = -1;
+ if (!Call_R(&replyNum))
+ fail("calling R()");
+
+ if (1 != replyNum)
+ fail("this should have been the first reply to R()");
+
+ Close();
+
+ return true;
+}
+
+bool
+TestRacyInterruptRepliesChild::Recv_A()
+{
+ int replyNum = -1;
+
+ if (!Call_R(&replyNum))
+ fail("calling _R()");
+
+ if (2 != replyNum)
+ fail("this should have been the second reply to R()");
+
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestRacyInterruptReplies.h b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.h
new file mode 100644
index 0000000000..94f5141886
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyInterruptReplies.h
@@ -0,0 +1,73 @@
+#ifndef mozilla__ipdltest_TestRacyInterruptReplies_h
+#define mozilla__ipdltest_TestRacyInterruptReplies_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestRacyInterruptRepliesParent.h"
+#include "mozilla/_ipdltest/PTestRacyInterruptRepliesChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestRacyInterruptRepliesParent :
+ public PTestRacyInterruptRepliesParent
+{
+public:
+ TestRacyInterruptRepliesParent();
+ virtual ~TestRacyInterruptRepliesParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvA_() override;
+
+ virtual bool Answer_R(int* replyNum) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ int mReplyNum;
+};
+
+
+class TestRacyInterruptRepliesChild :
+ public PTestRacyInterruptRepliesChild
+{
+public:
+ TestRacyInterruptRepliesChild();
+ virtual ~TestRacyInterruptRepliesChild();
+
+protected:
+ virtual bool AnswerR_(int* replyNum) override;
+
+ virtual bool RecvChildTest() override;
+
+ virtual bool Recv_A() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
+private:
+ int mReplyNum;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestRacyInterruptReplies_h
diff --git a/ipc/ipdl/test/cxx/TestRacyReentry.cpp b/ipc/ipdl/test/cxx/TestRacyReentry.cpp
new file mode 100644
index 0000000000..00996cc395
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyReentry.cpp
@@ -0,0 +1,84 @@
+#include "TestRacyReentry.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestRacyReentryParent::TestRacyReentryParent() : mRecvdE(false)
+{
+ MOZ_COUNT_CTOR(TestRacyReentryParent);
+}
+
+TestRacyReentryParent::~TestRacyReentryParent()
+{
+ MOZ_COUNT_DTOR(TestRacyReentryParent);
+}
+
+void
+TestRacyReentryParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+
+ if (!SendN())
+ fail("sending N");
+}
+
+bool
+TestRacyReentryParent::AnswerE()
+{
+ if (!mRecvdE) {
+ mRecvdE = true;
+ return true;
+ }
+
+ if (!CallH())
+ fail("calling H");
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRacyReentryChild::TestRacyReentryChild()
+{
+ MOZ_COUNT_CTOR(TestRacyReentryChild);
+}
+
+TestRacyReentryChild::~TestRacyReentryChild()
+{
+ MOZ_COUNT_DTOR(TestRacyReentryChild);
+}
+
+bool
+TestRacyReentryChild::RecvStart()
+{
+ if (!CallE())
+ fail("calling E");
+
+ Close();
+
+ return true;
+}
+
+bool
+TestRacyReentryChild::RecvN()
+{
+ if (!CallE())
+ fail("calling E");
+ return true;
+}
+
+bool
+TestRacyReentryChild::AnswerH()
+{
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestRacyReentry.h b/ipc/ipdl/test/cxx/TestRacyReentry.h
new file mode 100644
index 0000000000..f496ef7bfa
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyReentry.h
@@ -0,0 +1,67 @@
+#ifndef mozilla__ipdltest_TestRacyReentry_h
+#define mozilla__ipdltest_TestRacyReentry_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestRacyReentryParent.h"
+#include "mozilla/_ipdltest/PTestRacyReentryChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestRacyReentryParent :
+ public PTestRacyReentryParent
+{
+public:
+ TestRacyReentryParent();
+ virtual ~TestRacyReentryParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool AnswerE() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ bool mRecvdE;
+};
+
+
+class TestRacyReentryChild :
+ public PTestRacyReentryChild
+{
+public:
+ TestRacyReentryChild();
+ virtual ~TestRacyReentryChild();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual bool RecvN() override;
+
+ virtual bool AnswerH() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestRacyReentry_h
diff --git a/ipc/ipdl/test/cxx/TestRacyUndefer.cpp b/ipc/ipdl/test/cxx/TestRacyUndefer.cpp
new file mode 100644
index 0000000000..8bdd804cc3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyUndefer.cpp
@@ -0,0 +1,115 @@
+#include "base/basictypes.h"
+
+#include "TestRacyUndefer.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestRacyUndeferParent::TestRacyUndeferParent()
+{
+ MOZ_COUNT_CTOR(TestRacyUndeferParent);
+}
+
+TestRacyUndeferParent::~TestRacyUndeferParent()
+{
+ MOZ_COUNT_DTOR(TestRacyUndeferParent);
+}
+
+void
+TestRacyUndeferParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestRacyUndeferParent::AnswerSpam()
+{
+ static bool spammed = false;
+ static bool raced = false;
+ if (!spammed) {
+ spammed = true;
+
+ if (!SendAwakenSpam())
+ fail("sending AwakenSpam");
+ }
+ else if (!raced) {
+ raced = true;
+
+ if (!SendAwakenRaceWinTwice())
+ fail("sending WinRaceTwice");
+
+ if (!CallRace())
+ fail("calling Race1");
+ }
+ return true;
+}
+
+bool
+TestRacyUndeferParent::AnswerRaceWinTwice()
+{
+ return true;
+}
+
+bool
+TestRacyUndeferParent::RecvDone()
+{
+ Close();
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+TestRacyUndeferChild::TestRacyUndeferChild()
+{
+ MOZ_COUNT_CTOR(TestRacyUndeferChild);
+}
+
+TestRacyUndeferChild::~TestRacyUndeferChild()
+{
+ MOZ_COUNT_DTOR(TestRacyUndeferChild);
+}
+
+bool
+TestRacyUndeferChild::RecvStart()
+{
+ if (!CallSpam())
+ fail("calling Spam");
+
+ if (!SendDone())
+ fail("sending Done");
+
+ return true;
+}
+
+bool
+TestRacyUndeferChild::RecvAwakenSpam()
+{
+ if (!CallSpam())
+ fail("calling Spam");
+ return true;
+}
+
+bool
+TestRacyUndeferChild::RecvAwakenRaceWinTwice()
+{
+ if (!CallRaceWinTwice())
+ fail("calling RaceWinTwice");
+ return true;
+}
+
+bool
+TestRacyUndeferChild::AnswerRace()
+{
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestRacyUndefer.h b/ipc/ipdl/test/cxx/TestRacyUndefer.h
new file mode 100644
index 0000000000..eee06a86c3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestRacyUndefer.h
@@ -0,0 +1,70 @@
+#ifndef mozilla__ipdltest_TestRacyUndefer_h
+#define mozilla__ipdltest_TestRacyUndefer_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestRacyUndeferParent.h"
+#include "mozilla/_ipdltest/PTestRacyUndeferChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestRacyUndeferParent :
+ public PTestRacyUndeferParent
+{
+public:
+ TestRacyUndeferParent();
+ virtual ~TestRacyUndeferParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool AnswerSpam() override;
+
+ virtual bool AnswerRaceWinTwice() override;
+
+ virtual bool RecvDone() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestRacyUndeferChild :
+ public PTestRacyUndeferChild
+{
+public:
+ TestRacyUndeferChild();
+ virtual ~TestRacyUndeferChild();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual bool RecvAwakenSpam() override;
+ virtual bool RecvAwakenRaceWinTwice() override;
+
+ virtual bool AnswerRace() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestRacyUndefer_h
diff --git a/ipc/ipdl/test/cxx/TestSanity.cpp b/ipc/ipdl/test/cxx/TestSanity.cpp
new file mode 100644
index 0000000000..64ffceed6f
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSanity.cpp
@@ -0,0 +1,75 @@
+#include "TestSanity.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestSanityParent::TestSanityParent()
+{
+ MOZ_COUNT_CTOR(TestSanityParent);
+}
+
+TestSanityParent::~TestSanityParent()
+{
+ MOZ_COUNT_DTOR(TestSanityParent);
+}
+
+void
+TestSanityParent::Main()
+{
+ if (!SendPing(0, 0.5f, 0))
+ fail("sending Ping");
+}
+
+
+bool
+TestSanityParent::RecvPong(const int& one, const float& zeroPtTwoFive,
+ const uint8_t&/*unused*/)
+{
+ if (1 != one)
+ fail("invalid argument `%d', should have been `1'", one);
+
+ if (0.25f != zeroPtTwoFive)
+ fail("invalid argument `%g', should have been `0.25'", zeroPtTwoFive);
+
+ Close();
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSanityChild::TestSanityChild()
+{
+ MOZ_COUNT_CTOR(TestSanityChild);
+}
+
+TestSanityChild::~TestSanityChild()
+{
+ MOZ_COUNT_DTOR(TestSanityChild);
+}
+
+bool
+TestSanityChild::RecvPing(const int& zero, const float& zeroPtFive,
+ const int8_t&/*unused*/)
+{
+ if (0 != zero)
+ fail("invalid argument `%d', should have been `0'", zero);
+
+ if (0.5f != zeroPtFive)
+ fail("invalid argument `%g', should have been `0.5'", zeroPtFive);
+
+ if (!SendPong(1, 0.25f, 0))
+ fail("sending Pong");
+ return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestSanity.h b/ipc/ipdl/test/cxx/TestSanity.h
new file mode 100644
index 0000000000..6e5b8cb59b
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSanity.h
@@ -0,0 +1,63 @@
+#ifndef mozilla__ipdltest_TestSanity_h
+#define mozilla__ipdltest_TestSanity_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestSanityParent.h"
+#include "mozilla/_ipdltest/PTestSanityChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestSanityParent :
+ public PTestSanityParent
+{
+public:
+ TestSanityParent();
+ virtual ~TestSanityParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvPong(const int& one, const float& zeroPtTwoFive,
+ const uint8_t& dummy) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestSanityChild :
+ public PTestSanityChild
+{
+public:
+ TestSanityChild();
+ virtual ~TestSanityChild();
+
+protected:
+ virtual bool RecvPing(const int& zero, const float& zeroPtFive,
+ const int8_t& dummy) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestSanity_h
diff --git a/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp b/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp
new file mode 100644
index 0000000000..e8b2330ba1
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSelfManageRoot.cpp
@@ -0,0 +1,63 @@
+#include "TestSelfManageRoot.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+#define ASSERT(c) \
+ do { \
+ if (!(c)) \
+ fail(#c); \
+ } while (0)
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+void
+TestSelfManageRootParent::Main()
+{
+ TestSelfManageParent* a =
+ static_cast<TestSelfManageParent*>(SendPTestSelfManageConstructor());
+ if (!a)
+ fail("constructing PTestSelfManage");
+
+ ASSERT(1 == ManagedPTestSelfManageParent().Count());
+
+ TestSelfManageParent* aa =
+ static_cast<TestSelfManageParent*>(a->SendPTestSelfManageConstructor());
+ if (!aa)
+ fail("constructing PTestSelfManage");
+
+ ASSERT(1 == ManagedPTestSelfManageParent().Count() &&
+ 1 == a->ManagedPTestSelfManageParent().Count());
+
+ if (!PTestSelfManageParent::Send__delete__(aa))
+ fail("destroying PTestSelfManage");
+ ASSERT(Deletion == aa->mWhy &&
+ 1 == ManagedPTestSelfManageParent().Count() &&
+ 0 == a->ManagedPTestSelfManageParent().Count());
+ delete aa;
+
+ aa =
+ static_cast<TestSelfManageParent*>(a->SendPTestSelfManageConstructor());
+ if (!aa)
+ fail("constructing PTestSelfManage");
+
+ ASSERT(1 == ManagedPTestSelfManageParent().Count() &&
+ 1 == a->ManagedPTestSelfManageParent().Count());
+
+ if (!PTestSelfManageParent::Send__delete__(a))
+ fail("destroying PTestSelfManage");
+ ASSERT(Deletion == a->mWhy &&
+ AncestorDeletion == aa->mWhy &&
+ 0 == ManagedPTestSelfManageParent().Count() &&
+ 0 == a->ManagedPTestSelfManageParent().Count());
+ delete a;
+ delete aa;
+
+ Close();
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestSelfManageRoot.h b/ipc/ipdl/test/cxx/TestSelfManageRoot.h
new file mode 100644
index 0000000000..2a94fa3929
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSelfManageRoot.h
@@ -0,0 +1,141 @@
+#ifndef mozilla__ipdltest_TestSelfManageRoot_h
+#define mozilla__ipdltest_TestSelfManageRoot_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestSelfManageRootParent.h"
+#include "mozilla/_ipdltest/PTestSelfManageRootChild.h"
+#include "mozilla/_ipdltest/PTestSelfManageParent.h"
+#include "mozilla/_ipdltest/PTestSelfManageChild.h"
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent side
+
+class TestSelfManageParent :
+ public PTestSelfManageParent
+{
+public:
+ TestSelfManageParent() {
+ MOZ_COUNT_CTOR(TestSelfManageParent);
+ }
+ virtual ~TestSelfManageParent() {
+ MOZ_COUNT_DTOR(TestSelfManageParent);
+ }
+
+ ActorDestroyReason mWhy;
+
+protected:
+ virtual PTestSelfManageParent* AllocPTestSelfManageParent() override {
+ return new TestSelfManageParent();
+ }
+
+ virtual bool DeallocPTestSelfManageParent(PTestSelfManageParent* a) override {
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override {
+ mWhy = why;
+ }
+};
+
+class TestSelfManageRootParent :
+ public PTestSelfManageRootParent
+{
+public:
+ TestSelfManageRootParent() {
+ MOZ_COUNT_CTOR(TestSelfManageRootParent);
+ }
+ virtual ~TestSelfManageRootParent() {
+ MOZ_COUNT_DTOR(TestSelfManageRootParent);
+ }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual PTestSelfManageParent* AllocPTestSelfManageParent() override {
+ return new TestSelfManageParent();
+ }
+
+ virtual bool DeallocPTestSelfManageParent(PTestSelfManageParent* a) override {
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Child side
+
+class TestSelfManageChild :
+ public PTestSelfManageChild
+{
+public:
+ TestSelfManageChild() {
+ MOZ_COUNT_CTOR(TestSelfManageChild);
+ }
+ virtual ~TestSelfManageChild() {
+ MOZ_COUNT_DTOR(TestSelfManageChild);
+ }
+
+protected:
+ virtual PTestSelfManageChild* AllocPTestSelfManageChild() override {
+ return new TestSelfManageChild();
+ }
+
+ virtual bool DeallocPTestSelfManageChild(PTestSelfManageChild* a) override {
+ delete a;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override { }
+};
+
+class TestSelfManageRootChild :
+ public PTestSelfManageRootChild
+{
+public:
+ TestSelfManageRootChild() {
+ MOZ_COUNT_CTOR(TestSelfManageRootChild);
+ }
+ virtual ~TestSelfManageRootChild() {
+ MOZ_COUNT_DTOR(TestSelfManageRootChild);
+ }
+
+ void Main();
+
+protected:
+ virtual PTestSelfManageChild* AllocPTestSelfManageChild() override {
+ return new TestSelfManageChild();
+ }
+
+ virtual bool DeallocPTestSelfManageChild(PTestSelfManageChild* a) override {
+ delete a;
+ return true;
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestSelfManageRoot_h
diff --git a/ipc/ipdl/test/cxx/TestShmem.cpp b/ipc/ipdl/test/cxx/TestShmem.cpp
new file mode 100644
index 0000000000..1ff62bc3a9
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShmem.cpp
@@ -0,0 +1,115 @@
+#include "TestShmem.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent
+
+void
+TestShmemParent::Main()
+{
+ Shmem mem;
+ Shmem unsafe;
+
+ size_t size = 12345;
+ if (!AllocShmem(size, SharedMemory::TYPE_BASIC, &mem))
+ fail("can't alloc shmem");
+ if (!AllocUnsafeShmem(size, SharedMemory::TYPE_BASIC, &unsafe))
+ fail("can't alloc shmem");
+
+ if (mem.Size<char>() != size)
+ fail("shmem is wrong size: expected %lu, got %lu",
+ size, mem.Size<char>());
+ if (unsafe.Size<char>() != size)
+ fail("shmem is wrong size: expected %lu, got %lu",
+ size, unsafe.Size<char>());
+
+ char* ptr = mem.get<char>();
+ memcpy(ptr, "Hello!", sizeof("Hello!"));
+
+ char* unsafeptr = unsafe.get<char>();
+ memcpy(unsafeptr, "Hello!", sizeof("Hello!"));
+
+ Shmem unsafecopy = unsafe;
+ if (!SendGive(mem, unsafe, size))
+ fail("can't send Give()");
+
+ // uncomment the following line for a (nondeterministic) surprise!
+ //char c1 = *ptr; (void)c1;
+
+ // uncomment the following line for a deterministic surprise!
+ //char c2 = *mem.get<char>(); (void)c2;
+
+ // unsafe shmem gets rid of those checks
+ char uc1 = *unsafeptr; (void)uc1;
+ char uc2 = *unsafecopy.get<char>(); (void)uc2;
+}
+
+
+bool
+TestShmemParent::RecvTake(Shmem&& mem, Shmem&& unsafe,
+ const size_t& expectedSize)
+{
+ if (mem.Size<char>() != expectedSize)
+ fail("expected shmem size %lu, but it has size %lu",
+ expectedSize, mem.Size<char>());
+ if (unsafe.Size<char>() != expectedSize)
+ fail("expected shmem size %lu, but it has size %lu",
+ expectedSize, unsafe.Size<char>());
+
+ if (strcmp(mem.get<char>(), "And yourself!"))
+ fail("expected message was not written");
+ if (strcmp(unsafe.get<char>(), "And yourself!"))
+ fail("expected message was not written");
+
+ if (!DeallocShmem(mem))
+ fail("DeallocShmem");
+ if (!DeallocShmem(unsafe))
+ fail("DeallocShmem");
+
+ Close();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Child
+
+bool
+TestShmemChild::RecvGive(Shmem&& mem, Shmem&& unsafe, const size_t& expectedSize)
+{
+ if (mem.Size<char>() != expectedSize)
+ fail("expected shmem size %lu, but it has size %lu",
+ expectedSize, mem.Size<char>());
+ if (unsafe.Size<char>() != expectedSize)
+ fail("expected shmem size %lu, but it has size %lu",
+ expectedSize, unsafe.Size<char>());
+
+ if (strcmp(mem.get<char>(), "Hello!"))
+ fail("expected message was not written");
+ if (strcmp(unsafe.get<char>(), "Hello!"))
+ fail("expected message was not written");
+
+ char* unsafeptr = unsafe.get<char>();
+
+ memcpy(mem.get<char>(), "And yourself!", sizeof("And yourself!"));
+ memcpy(unsafeptr, "And yourself!", sizeof("And yourself!"));
+
+ Shmem unsafecopy = unsafe;
+ if (!SendTake(mem, unsafe, expectedSize))
+ fail("can't send Take()");
+
+ // these checks also shouldn't fail in the child
+ char uc1 = *unsafeptr; (void)uc1;
+ char uc2 = *unsafecopy.get<char>(); (void)uc2;
+
+ return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestShmem.h b/ipc/ipdl/test/cxx/TestShmem.h
new file mode 100644
index 0000000000..d583fe978a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShmem.h
@@ -0,0 +1,66 @@
+#ifndef mozilla__ipdltest_TestShmem_h
+#define mozilla__ipdltest_TestShmem_h
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestShmemParent.h"
+#include "mozilla/_ipdltest/PTestShmemChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestShmemParent :
+ public PTestShmemParent
+{
+public:
+ TestShmemParent() { }
+ virtual ~TestShmemParent() { }
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvTake(
+ Shmem&& mem,
+ Shmem&& unsafe,
+ const size_t& expectedSize) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestShmemChild :
+ public PTestShmemChild
+{
+public:
+ TestShmemChild() { }
+ virtual ~TestShmemChild() { }
+
+protected:
+ virtual bool RecvGive(
+ Shmem&& mem,
+ Shmem&& unsafe,
+ const size_t& expectedSize) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // ifndef mozilla__ipdltest_TestShmem_h
diff --git a/ipc/ipdl/test/cxx/TestShutdown.cpp b/ipc/ipdl/test/cxx/TestShutdown.cpp
new file mode 100644
index 0000000000..95a242bff3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShutdown.cpp
@@ -0,0 +1,235 @@
+#include "TestShutdown.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent side
+void
+TestShutdownParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start()");
+}
+
+void
+TestShutdownParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (AbnormalShutdown != why)
+ fail("should have ended test with crash!");
+
+ passed("ok");
+
+ QuitParent();
+}
+
+void
+TestShutdownSubParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (Manager()->ManagedPTestShutdownSubParent().Count() == 0)
+ fail("manager should still have managees!");
+
+ if (mExpectCrash && AbnormalShutdown != why)
+ fail("expected crash!");
+ else if (!mExpectCrash && AbnormalShutdown == why)
+ fail("wasn't expecting crash!");
+
+ if (mExpectCrash && 0 == ManagedPTestShutdownSubsubParent().Count())
+ fail("expected to *still* have kids");
+}
+
+void
+TestShutdownSubsubParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (Manager()->ManagedPTestShutdownSubsubParent().Count() == 0)
+ fail("manager should still have managees!");
+
+ if (mExpectParentDeleted && AncestorDeletion != why)
+ fail("expected ParentDeleted == why");
+ else if (!mExpectParentDeleted && AncestorDeletion == why)
+ fail("wasn't expecting parent delete");
+}
+
+//-----------------------------------------------------------------------------
+// Child side
+
+bool
+TestShutdownChild::RecvStart()
+{
+ // test 1: alloc some actors and subactors, delete in
+ // managee-before-manager order
+ {
+ bool expectCrash = false, expectParentDeleted = false;
+
+ PTestShutdownSubChild* c1 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c1)
+ fail("problem sending ctor");
+
+ PTestShutdownSubChild* c2 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c1s1 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c1s2 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c2s1 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c2s2 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s2)
+ fail("problem sending ctor");
+
+ if (!PTestShutdownSubsubChild::Send__delete__(c1s1))
+ fail("problem sending dtor");
+ if (!PTestShutdownSubsubChild::Send__delete__(c1s2))
+ fail("problem sending dtor");
+ if (!PTestShutdownSubsubChild::Send__delete__(c2s1))
+ fail("problem sending dtor");
+ if (!PTestShutdownSubsubChild::Send__delete__(c2s2))
+ fail("problem sending dtor");
+
+ if (!c1->CallStackFrame())
+ fail("problem creating dummy stack frame");
+ if (!c2->CallStackFrame())
+ fail("problem creating dummy stack frame");
+ }
+
+ // test 2: alloc some actors and subactors, delete managers first
+ {
+ bool expectCrash = false, expectParentDeleted = true;
+
+ PTestShutdownSubChild* c1 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c1)
+ fail("problem sending ctor");
+
+ PTestShutdownSubChild* c2 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c1s1 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c1s2 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c2s1 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c2s2 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s2)
+ fail("problem sending ctor");
+
+ // delete parents without deleting kids
+ if (!c1->CallStackFrame())
+ fail("problem creating dummy stack frame");
+ if (!c2->CallStackFrame())
+ fail("problem creating dummy stack frame");
+ }
+
+ // test 3: alloc some actors and subactors, then crash
+ {
+ bool expectCrash = true, expectParentDeleted = false;
+
+ PTestShutdownSubChild* c1 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c1)
+ fail("problem sending ctor");
+
+ PTestShutdownSubChild* c2 =
+ SendPTestShutdownSubConstructor(expectCrash);
+ if (!c2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c1s1 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c1s2 =
+ c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c1s2)
+ fail("problem sending ctor");
+
+ PTestShutdownSubsubChild* c2s1 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s1)
+ fail("problem sending ctor");
+ PTestShutdownSubsubChild* c2s2 =
+ c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
+ if (!c2s2)
+ fail("problem sending ctor");
+
+ // make sure the ctors have been processed by the other side;
+ // the write end of the socket may temporarily be unwriteable
+ if (!SendSync())
+ fail("can't synchronize with parent");
+
+ // "crash", but without tripping tinderbox assert/abort
+ // detectors
+ _exit(0);
+ }
+}
+
+void
+TestShutdownChild::ActorDestroy(ActorDestroyReason why)
+{
+ fail("hey wait ... we should have crashed!");
+}
+
+bool
+TestShutdownSubChild::AnswerStackFrame()
+{
+ if (!PTestShutdownSubChild::Send__delete__(this))
+ fail("problem sending dtor");
+
+ // WATCH OUT! |this| has just deleted
+
+ return true;
+}
+
+void
+TestShutdownSubChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (Manager()->ManagedPTestShutdownSubChild().Count() == 0)
+ fail("manager should still have managees!");
+
+ if (mExpectCrash && AbnormalShutdown != why)
+ fail("expected crash!");
+ else if (!mExpectCrash && AbnormalShutdown == why)
+ fail("wasn't expecting crash!");
+
+ if (mExpectCrash && 0 == ManagedPTestShutdownSubsubChild().Count())
+ fail("expected to *still* have kids");
+}
+
+void
+TestShutdownSubsubChild::ActorDestroy(ActorDestroyReason why)
+{
+ if (Manager()->ManagedPTestShutdownSubsubChild().Count() == 0)
+ fail("manager should still have managees!");
+
+ if (mExpectParentDeleted && AncestorDeletion != why)
+ fail("expected ParentDeleted == why");
+ else if (!mExpectParentDeleted && AncestorDeletion == why)
+ fail("wasn't expecting parent delete");
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestShutdown.h b/ipc/ipdl/test/cxx/TestShutdown.h
new file mode 100644
index 0000000000..c79cbb8fc6
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestShutdown.h
@@ -0,0 +1,225 @@
+#ifndef mozilla__ipdltest_TestShutdown_h
+#define mozilla__ipdltest_TestShutdown_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestShutdownParent.h"
+#include "mozilla/_ipdltest/PTestShutdownChild.h"
+
+#include "mozilla/_ipdltest/PTestShutdownSubParent.h"
+#include "mozilla/_ipdltest/PTestShutdownSubChild.h"
+
+#include "mozilla/_ipdltest/PTestShutdownSubsubParent.h"
+#include "mozilla/_ipdltest/PTestShutdownSubsubChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// Parent side
+
+class TestShutdownSubsubParent :
+ public PTestShutdownSubsubParent
+{
+public:
+ explicit TestShutdownSubsubParent(bool expectParentDeleted) :
+ mExpectParentDeleted(expectParentDeleted)
+ {
+ }
+
+ virtual ~TestShutdownSubsubParent()
+ {
+ }
+
+protected:
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+
+private:
+ bool mExpectParentDeleted;
+};
+
+
+class TestShutdownSubParent :
+ public PTestShutdownSubParent
+{
+public:
+ explicit TestShutdownSubParent(bool expectCrash) :
+ mExpectCrash(expectCrash),
+ mDeletedCount(0)
+ {
+ }
+
+ virtual ~TestShutdownSubParent()
+ {
+ if (2 != mDeletedCount)
+ fail("managees outliving manager!");
+ }
+
+protected:
+ virtual bool
+ AnswerStackFrame() override
+ {
+ return CallStackFrame();
+ }
+
+ virtual PTestShutdownSubsubParent*
+ AllocPTestShutdownSubsubParent(const bool& expectParentDelete) override
+ {
+ return new TestShutdownSubsubParent(expectParentDelete);
+ }
+
+ virtual bool
+ DeallocPTestShutdownSubsubParent(PTestShutdownSubsubParent* actor) override
+ {
+ delete actor;
+ ++mDeletedCount;
+ return true;
+ }
+
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+
+private:
+ bool mExpectCrash;
+ int mDeletedCount;
+};
+
+
+class TestShutdownParent :
+ public PTestShutdownParent
+{
+public:
+ TestShutdownParent()
+ {
+ }
+ virtual ~TestShutdownParent()
+ {
+ }
+
+ static bool RunTestInProcesses() { return true; }
+ // FIXME/bug 703323 Could work if modified
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual bool RecvSync() override { return true; }
+
+ virtual PTestShutdownSubParent*
+ AllocPTestShutdownSubParent(const bool& expectCrash) override
+ {
+ return new TestShutdownSubParent(expectCrash);
+ }
+
+ virtual bool
+ DeallocPTestShutdownSubParent(PTestShutdownSubParent* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+};
+
+
+//-----------------------------------------------------------------------------
+// Child side
+
+class TestShutdownSubsubChild :
+ public PTestShutdownSubsubChild
+{
+public:
+ explicit TestShutdownSubsubChild(bool expectParentDeleted) :
+ mExpectParentDeleted(expectParentDeleted)
+ {
+ }
+ virtual ~TestShutdownSubsubChild()
+ {
+ }
+
+protected:
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+
+private:
+ bool mExpectParentDeleted;
+};
+
+
+class TestShutdownSubChild :
+ public PTestShutdownSubChild
+{
+public:
+ explicit TestShutdownSubChild(bool expectCrash) : mExpectCrash(expectCrash)
+ {
+ }
+
+ virtual ~TestShutdownSubChild()
+ {
+ }
+
+protected:
+ virtual bool AnswerStackFrame() override;
+
+ virtual PTestShutdownSubsubChild*
+ AllocPTestShutdownSubsubChild(const bool& expectParentDelete) override
+ {
+ return new TestShutdownSubsubChild(expectParentDelete);
+ }
+
+ virtual bool
+ DeallocPTestShutdownSubsubChild(PTestShutdownSubsubChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+
+private:
+ bool mExpectCrash;
+};
+
+
+class TestShutdownChild :
+ public PTestShutdownChild
+{
+public:
+ TestShutdownChild()
+ {
+ }
+ virtual ~TestShutdownChild()
+ {
+ }
+
+protected:
+ virtual bool
+ RecvStart();
+
+ virtual PTestShutdownSubChild*
+ AllocPTestShutdownSubChild(
+ const bool& expectCrash) override
+ {
+ return new TestShutdownSubChild(expectCrash);
+ }
+
+ virtual bool
+ DeallocPTestShutdownSubChild(PTestShutdownSubChild* actor) override
+ {
+ delete actor;
+ return true;
+ }
+
+ virtual void
+ ActorDestroy(ActorDestroyReason why) override;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestShutdown_h
diff --git a/ipc/ipdl/test/cxx/TestStackHooks.cpp b/ipc/ipdl/test/cxx/TestStackHooks.cpp
new file mode 100644
index 0000000000..b4181985c5
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestStackHooks.cpp
@@ -0,0 +1,168 @@
+#include "TestStackHooks.h"
+
+#include "base/task.h"
+#include "IPDLUnitTests.h" // fail etc.
+
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestStackHooksParent::TestStackHooksParent() :
+ mOnStack(false), mIncallDepth(0)
+{
+ MOZ_COUNT_CTOR(TestStackHooksParent);
+}
+
+TestStackHooksParent::~TestStackHooksParent()
+{
+ MOZ_COUNT_DTOR(TestStackHooksParent);
+}
+
+void
+TestStackHooksParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start()");
+}
+
+
+bool
+TestStackHooksParent::AnswerStackFrame()
+{
+ if (!mOnStack)
+ fail("not on C++ stack?!");
+
+ if (!CallStackFrame())
+ fail("calling StackFrame()");
+
+ if (!mOnStack)
+ fail("not on C++ stack?!");
+
+ if (1 != mIncallDepth)
+ fail("missed EnteredCall or ExitedCall hook");
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestStackHooksChild::TestStackHooksChild() :
+ mOnStack(false),
+ mEntered(0),
+ mExited(0),
+ mIncallDepth(0)
+{
+ MOZ_COUNT_CTOR(TestStackHooksChild);
+}
+
+TestStackHooksChild::~TestStackHooksChild()
+{
+ MOZ_COUNT_DTOR(TestStackHooksChild);
+}
+
+namespace {
+void RunTestsFn() {
+ static_cast<TestStackHooksChild*>(gChildActor)->RunTests();
+}
+}
+
+bool
+TestStackHooksChild::RecvStart()
+{
+ if (!mOnStack)
+ fail("missed stack notification");
+
+ if (0 != mIncallDepth)
+ fail("EnteredCall/ExitedCall malfunction");
+
+ // kick off tests from a runnable so that we can start with
+ // MessageChannel code on the C++ stack
+ MessageLoop::current()->PostTask(NewRunnableFunction(RunTestsFn));
+
+ return true;
+}
+
+bool
+TestStackHooksChild::AnswerStackFrame()
+{
+ if (!mOnStack)
+ fail("missed stack notification");
+
+ if (1 != mIncallDepth)
+ fail("missed EnteredCall or ExitedCall hook");
+
+ if (PTestStackHooks::TEST4_3 == state()) {
+ if (!SendAsync())
+ fail("sending Async()");
+ }
+ else if (PTestStackHooks::TEST5_3 == state()) {
+ if (!SendSync())
+ fail("sending Sync()");
+ }
+ else {
+ fail("unexpected state");
+ }
+
+ if (!mOnStack)
+ fail("bad stack exit notification");
+
+ return true;
+}
+
+void
+TestStackHooksChild::RunTests()
+{
+ // 1 because of RecvStart()
+ if (1 != mEntered)
+ fail("missed stack notification");
+ if (mOnStack)
+ fail("spurious stack notification");
+ if (0 != mIncallDepth)
+ fail("EnteredCall/ExitedCall malfunction");
+
+ if (!SendAsync())
+ fail("sending Async()");
+ if (mOnStack)
+ fail("spurious stack notification");
+ if (0 != mIncallDepth)
+ fail("EnteredCall/ExitedCall malfunction");
+ if (2 != mEntered)
+ fail("missed stack notification");
+
+ if (!SendSync())
+ fail("sending Sync()");
+ if (mOnStack)
+ fail("spurious stack notification");
+ if (0 != mIncallDepth)
+ fail("EnteredCall/ExitedCall malfunction");
+ if (3 != mEntered)
+ fail("missed stack notification");
+
+ if (!CallRpc())
+ fail("calling RPC()");
+ if (mOnStack)
+ fail("spurious stack notification");
+ if (0 != mIncallDepth)
+ fail("EnteredCall/ExitedCall malfunction");
+ if (4 != mEntered)
+ fail("missed stack notification");
+
+ if (!CallStackFrame())
+ fail("calling StackFrame()");
+ if (mOnStack)
+ fail("spurious stack notification");
+ if (0 != mIncallDepth)
+ fail("EnteredCall/ExitedCall malfunction");
+ if (5 != mEntered)
+ fail("missed stack notification");
+
+ Close();
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestStackHooks.h b/ipc/ipdl/test/cxx/TestStackHooks.h
new file mode 100644
index 0000000000..c26d5f937e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestStackHooks.h
@@ -0,0 +1,130 @@
+#ifndef mozilla__ipdltest_TestStackHooks_h
+#define mozilla__ipdltest_TestStackHooks_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestStackHooksParent.h"
+#include "mozilla/_ipdltest/PTestStackHooksChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestStackHooksParent :
+ public PTestStackHooksParent
+{
+public:
+ TestStackHooksParent();
+ virtual ~TestStackHooksParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvAsync() override {
+ if (!mOnStack)
+ fail("not on C++ stack?!");
+ return true;
+ }
+
+ virtual bool RecvSync() override {
+ if (!mOnStack)
+ fail("not on C++ stack?!");
+ return true;
+ }
+
+ virtual bool AnswerRpc() override {
+ if (!mOnStack)
+ fail("not on C++ stack?!");
+ return true;
+ }
+
+ virtual bool AnswerStackFrame() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+
+ virtual void EnteredCxxStack() override {
+ mOnStack = true;
+ }
+ virtual void ExitedCxxStack() override {
+ mOnStack = false;
+ }
+
+ virtual void EnteredCall() override {
+ ++mIncallDepth;
+ }
+ virtual void ExitedCall() override {
+ --mIncallDepth;
+ }
+
+private:
+ bool mOnStack;
+ int mIncallDepth;
+};
+
+
+class TestStackHooksChild :
+ public PTestStackHooksChild
+{
+public:
+ TestStackHooksChild();
+ virtual ~TestStackHooksChild();
+
+ void RunTests();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual bool AnswerStackFrame() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+
+ if (mEntered != mExited)
+ fail("unbalanced enter/exit notifications");
+
+ if (mOnStack)
+ fail("computing mOnStack went awry; should have failed above assertion");
+
+ QuitChild();
+ }
+
+ virtual void EnteredCxxStack() override {
+ ++mEntered;
+ mOnStack = true;
+ }
+ virtual void ExitedCxxStack() override {
+ ++mExited;
+ mOnStack = false;
+ }
+
+ virtual void EnteredCall() override {
+ ++mIncallDepth;
+ }
+ virtual void ExitedCall() override {
+ --mIncallDepth;
+ }
+
+private:
+ bool mOnStack;
+ int mEntered;
+ int mExited;
+ int mIncallDepth;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestStackHooks_h
diff --git a/ipc/ipdl/test/cxx/TestSyncError.cpp b/ipc/ipdl/test/cxx/TestSyncError.cpp
new file mode 100644
index 0000000000..b9d8ec9385
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncError.cpp
@@ -0,0 +1,61 @@
+#include "TestSyncError.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestSyncErrorParent::TestSyncErrorParent()
+{
+ MOZ_COUNT_CTOR(TestSyncErrorParent);
+}
+
+TestSyncErrorParent::~TestSyncErrorParent()
+{
+ MOZ_COUNT_DTOR(TestSyncErrorParent);
+}
+
+void
+TestSyncErrorParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestSyncErrorParent::RecvError()
+{
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSyncErrorChild::TestSyncErrorChild()
+{
+ MOZ_COUNT_CTOR(TestSyncErrorChild);
+}
+
+TestSyncErrorChild::~TestSyncErrorChild()
+{
+ MOZ_COUNT_DTOR(TestSyncErrorChild);
+}
+
+bool
+TestSyncErrorChild::RecvStart()
+{
+ if (SendError())
+ fail("Error() should have return false");
+
+ Close();
+
+ return true;
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestSyncError.h b/ipc/ipdl/test/cxx/TestSyncError.h
new file mode 100644
index 0000000000..c39402a87c
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncError.h
@@ -0,0 +1,71 @@
+#ifndef mozilla__ipdltest_TestSyncError_h
+#define mozilla__ipdltest_TestSyncError_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestSyncErrorParent.h"
+#include "mozilla/_ipdltest/PTestSyncErrorChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestSyncErrorParent :
+ public PTestSyncErrorParent
+{
+public:
+ TestSyncErrorParent();
+ virtual ~TestSyncErrorParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool RecvError() override;
+
+ virtual void ProcessingError(Result aCode, const char* aReason) override
+ {
+ // Ignore errors
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestSyncErrorChild :
+ public PTestSyncErrorChild
+{
+public:
+ TestSyncErrorChild();
+ virtual ~TestSyncErrorChild();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual void ProcessingError(Result aCode, const char* aReason) override
+ {
+ // Ignore errors
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestSyncError_h
diff --git a/ipc/ipdl/test/cxx/TestSyncHang.cpp b/ipc/ipdl/test/cxx/TestSyncHang.cpp
new file mode 100644
index 0000000000..62b9ae723a
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncHang.cpp
@@ -0,0 +1,70 @@
+#include "TestSyncHang.h"
+#include "base/task.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+using std::vector;
+using std::string;
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+mozilla::ipc::GeckoChildProcessHost* gSyncHangSubprocess;
+
+TestSyncHangParent::TestSyncHangParent()
+{
+ MOZ_COUNT_CTOR(TestSyncHangParent);
+}
+
+TestSyncHangParent::~TestSyncHangParent()
+{
+ MOZ_COUNT_DTOR(TestSyncHangParent);
+}
+
+void
+DeleteSyncHangSubprocess(MessageLoop* uiLoop)
+{
+ delete gSyncHangSubprocess;
+}
+
+void
+DeferredSyncHangParentShutdown()
+{
+ // ping to DeleteSubprocess
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(DeleteSyncHangSubprocess, MessageLoop::current()));
+}
+
+void
+TestSyncHangParent::Main()
+{
+ vector<string> args;
+ args.push_back("fake/path");
+ gSyncHangSubprocess = new mozilla::ipc::GeckoChildProcessHost(GeckoProcessType_Plugin);
+ bool launched = gSyncHangSubprocess->SyncLaunch(args, 2);
+ if (launched)
+ fail("Calling SyncLaunch with an invalid path should return false");
+
+ MessageLoop::current()->PostTask(NewRunnableFunction(DeferredSyncHangParentShutdown));
+ Close();
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSyncHangChild::TestSyncHangChild()
+{
+ MOZ_COUNT_CTOR(TestSyncHangChild);
+}
+
+TestSyncHangChild::~TestSyncHangChild()
+{
+ MOZ_COUNT_DTOR(TestSyncHangChild);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestSyncHang.h b/ipc/ipdl/test/cxx/TestSyncHang.h
new file mode 100644
index 0000000000..87533d6617
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncHang.h
@@ -0,0 +1,58 @@
+#ifndef mozilla__ipdltest_TestSyncHang_h
+#define mozilla__ipdltest_TestSyncHang_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestSyncHangParent.h"
+#include "mozilla/_ipdltest/PTestSyncHangChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestSyncHangParent :
+ public PTestSyncHangParent
+{
+public:
+ TestSyncHangParent();
+ virtual ~TestSyncHangParent();
+
+ static bool RunTestInProcesses() { return true; }
+ // FIXME/bug 703323 Could work if modified
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestSyncHangChild :
+ public PTestSyncHangChild
+{
+public:
+ TestSyncHangChild();
+ virtual ~TestSyncHangChild();
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestSyncHang_h
diff --git a/ipc/ipdl/test/cxx/TestSyncWakeup.cpp b/ipc/ipdl/test/cxx/TestSyncWakeup.cpp
new file mode 100644
index 0000000000..cce0ca34fa
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncWakeup.cpp
@@ -0,0 +1,134 @@
+#if defined(OS_POSIX)
+#include <unistd.h> // sleep()
+#endif
+
+#include "TestSyncWakeup.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestSyncWakeupParent::TestSyncWakeupParent()
+{
+ MOZ_COUNT_CTOR(TestSyncWakeupParent);
+}
+
+TestSyncWakeupParent::~TestSyncWakeupParent()
+{
+ MOZ_COUNT_DTOR(TestSyncWakeupParent);
+}
+
+void
+TestSyncWakeupParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start()");
+}
+
+bool
+TestSyncWakeupParent::AnswerStackFrame()
+{
+ if (!CallStackFrame())
+ fail("calling StackFrame()");
+ return true;
+}
+
+bool
+TestSyncWakeupParent::RecvSync1()
+{
+ if (!SendNote1())
+ fail("sending Note1()");
+
+ // XXX ugh ... need to ensure that the async message and sync
+ // reply come in "far enough" apart that this test doesn't pass on
+ // accident
+#if defined(OS_POSIX)
+ // NB: can't use PR_Sleep (i.e. Sleep() on windows) because it's
+ // only spec'd to block the current thread, not the current
+ // process. We need the IO thread to sleep as well.
+ puts(" (sleeping for 5 seconds. sorry!)");
+ sleep(5);
+#endif
+
+ return true;
+}
+
+bool
+TestSyncWakeupParent::RecvSync2()
+{
+ if (!SendNote2())
+ fail("sending Note2()");
+
+#if defined(OS_POSIX)
+ // see above
+ sleep(5);
+ puts(" (sleeping for 5 seconds. sorry!)");
+#endif
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSyncWakeupChild::TestSyncWakeupChild() : mDone(false)
+{
+ MOZ_COUNT_CTOR(TestSyncWakeupChild);
+}
+
+TestSyncWakeupChild::~TestSyncWakeupChild()
+{
+ MOZ_COUNT_DTOR(TestSyncWakeupChild);
+}
+
+bool
+TestSyncWakeupChild::RecvStart()
+{
+ // First test: the parent fires back an async message while
+ // replying to a sync one
+ if (!SendSync1())
+ fail("sending Sync()");
+
+ // drop back into the event loop to get Note1(), then kick off the
+ // second test
+ return true;
+}
+
+bool
+TestSyncWakeupChild::RecvNote1()
+{
+ // Second test: the parent fires back an async message while
+ // replying to a sync one, with a frame on the RPC stack
+ if (!CallStackFrame())
+ fail("calling StackFrame()");
+
+ if (!mDone)
+ fail("should have received Note2()!");
+
+ Close();
+
+ return true;
+}
+
+bool
+TestSyncWakeupChild::AnswerStackFrame()
+{
+ if (!SendSync2())
+ fail("sending Sync()");
+
+ return true;
+}
+
+bool
+TestSyncWakeupChild::RecvNote2()
+{
+ mDone = true;
+ return true;
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestSyncWakeup.h b/ipc/ipdl/test/cxx/TestSyncWakeup.h
new file mode 100644
index 0000000000..dac3f43122
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSyncWakeup.h
@@ -0,0 +1,74 @@
+#ifndef mozilla__ipdltest_TestSyncWakeup_h
+#define mozilla__ipdltest_TestSyncWakeup_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestSyncWakeupParent.h"
+#include "mozilla/_ipdltest/PTestSyncWakeupChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestSyncWakeupParent :
+ public PTestSyncWakeupParent
+{
+public:
+ TestSyncWakeupParent();
+ virtual ~TestSyncWakeupParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return true; }
+
+ void Main();
+
+protected:
+ virtual bool AnswerStackFrame() override;
+
+ virtual bool RecvSync1() override;
+
+ virtual bool RecvSync2() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ passed("ok");
+ QuitParent();
+ }
+};
+
+
+class TestSyncWakeupChild :
+ public PTestSyncWakeupChild
+{
+public:
+ TestSyncWakeupChild();
+ virtual ~TestSyncWakeupChild();
+
+protected:
+ virtual bool RecvStart() override;
+
+ virtual bool RecvNote1() override;
+
+ virtual bool AnswerStackFrame() override;
+
+ virtual bool RecvNote2() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (NormalShutdown != why)
+ fail("unexpected destruction!");
+ QuitChild();
+ }
+
+private:
+ bool mDone;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestSyncWakeup_h
diff --git a/ipc/ipdl/test/cxx/TestUrgency.cpp b/ipc/ipdl/test/cxx/TestUrgency.cpp
new file mode 100644
index 0000000000..b9b05bceaf
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUrgency.cpp
@@ -0,0 +1,162 @@
+#include "TestUrgency.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#if defined(OS_POSIX)
+#include <unistd.h>
+#else
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace _ipdltest {
+
+#if defined(OS_POSIX)
+static void Sleep(int ms)
+{
+ sleep(ms / 1000);
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestUrgencyParent::TestUrgencyParent()
+ : inreply_(false)
+{
+ MOZ_COUNT_CTOR(TestUrgencyParent);
+}
+
+TestUrgencyParent::~TestUrgencyParent()
+{
+ MOZ_COUNT_DTOR(TestUrgencyParent);
+}
+
+void
+TestUrgencyParent::Main()
+{
+ if (!SendStart())
+ fail("sending Start");
+}
+
+bool
+TestUrgencyParent::RecvTest1(uint32_t *value)
+{
+ if (!SendReply1(value))
+ fail("sending Reply1");
+ if (*value != 99)
+ fail("bad value");
+ return true;
+}
+
+bool
+TestUrgencyParent::RecvTest2()
+{
+ uint32_t value;
+ inreply_ = true;
+ if (!SendReply2(&value))
+ fail("sending Reply2");
+ inreply_ = false;
+ if (value != 500)
+ fail("bad value");
+ return true;
+}
+
+bool
+TestUrgencyParent::RecvTest3(uint32_t *value)
+{
+ if (inreply_)
+ fail("nested non-urgent on top of urgent rpc");
+ *value = 1000;
+ return true;
+}
+
+bool
+TestUrgencyParent::RecvFinalTest_Begin()
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+enum {
+ kFirstTestBegin = 1,
+ kFirstTestGotReply,
+ kSecondTestBegin,
+ kSecondTestGotReply,
+};
+
+bool
+TestUrgencyChild::RecvStart()
+{
+ uint32_t result;
+
+ // Send a synchronous message, expect to get an urgent message while
+ // blocked.
+ test_ = kFirstTestBegin;
+ if (!SendTest1(&result))
+ fail("calling SendTest1");
+ if (result != 99)
+ fail("bad result in RecvStart");
+ if (test_ != kFirstTestGotReply)
+ fail("never received urgent message");
+
+ // Initiate the next test by sending an asynchronous message, then becoming
+ // blocked. This tests that the urgent message is still delivered properly,
+ // and that the parent does not try to service the sync
+ test_ = kSecondTestBegin;
+ if (!SendTest2())
+ fail("calling SendTest2");
+ if (!SendTest3(&result))
+ fail("calling SendTest3");
+ if (test_ != kSecondTestGotReply)
+ fail("never received urgent message #2");
+ if (result != 1000)
+ fail("wrong value from test3");
+
+ if (!SendFinalTest_Begin())
+ fail("Final test should have succeeded");
+
+ Close();
+
+ return true;
+}
+
+bool
+TestUrgencyChild::RecvReply1(uint32_t *reply)
+{
+ if (test_ != kFirstTestBegin)
+ fail("wrong test # in RecvReply1");
+
+ *reply = 99;
+ test_ = kFirstTestGotReply;
+ return true;
+}
+
+bool
+TestUrgencyChild::RecvReply2(uint32_t *reply)
+{
+ if (test_ != kSecondTestBegin)
+ fail("wrong test # in RecvReply2");
+
+ // sleep for 5 seconds so the parent process tries to deliver more messages.
+ Sleep(5000);
+
+ *reply = 500;
+ test_ = kSecondTestGotReply;
+ return true;
+}
+
+TestUrgencyChild::TestUrgencyChild()
+ : test_(0)
+{
+ MOZ_COUNT_CTOR(TestUrgencyChild);
+}
+
+TestUrgencyChild::~TestUrgencyChild()
+{
+ MOZ_COUNT_DTOR(TestUrgencyChild);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestUrgency.h b/ipc/ipdl/test/cxx/TestUrgency.h
new file mode 100644
index 0000000000..07a31b1c1e
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUrgency.h
@@ -0,0 +1,72 @@
+#ifndef mozilla__ipdltest_TestUrgency_h
+#define mozilla__ipdltest_TestUrgency_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestUrgencyParent.h"
+#include "mozilla/_ipdltest/PTestUrgencyChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestUrgencyParent :
+ public PTestUrgencyParent
+{
+public:
+ TestUrgencyParent();
+ virtual ~TestUrgencyParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+
+ bool RecvTest1(uint32_t *value);
+ bool RecvTest2();
+ bool RecvTest3(uint32_t *value);
+ bool RecvTest4_Begin();
+ bool RecvTest4_NestedSync();
+ bool RecvFinalTest_Begin();
+
+ bool ShouldContinueFromReplyTimeout() override
+ {
+ return false;
+ }
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ bool inreply_;
+};
+
+
+class TestUrgencyChild :
+ public PTestUrgencyChild
+{
+public:
+ TestUrgencyChild();
+ virtual ~TestUrgencyChild();
+
+ bool RecvStart();
+ bool RecvReply1(uint32_t *reply);
+ bool RecvReply2(uint32_t *reply);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ QuitChild();
+ }
+
+private:
+ uint32_t test_;
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestUrgency_h
diff --git a/ipc/ipdl/test/cxx/TestUrgentHangs.cpp b/ipc/ipdl/test/cxx/TestUrgentHangs.cpp
new file mode 100644
index 0000000000..b798ae18d9
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUrgentHangs.cpp
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ */
+#include "TestUrgentHangs.h"
+
+#include "IPDLUnitTests.h" // fail etc.
+#include "prthread.h"
+#if defined(OS_POSIX)
+#include <unistd.h>
+#else
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace _ipdltest {
+
+//-----------------------------------------------------------------------------
+// parent
+
+TestUrgentHangsParent::TestUrgentHangsParent()
+ : mInnerCount(0),
+ mInnerUrgentCount(0)
+{
+ MOZ_COUNT_CTOR(TestUrgentHangsParent);
+}
+
+TestUrgentHangsParent::~TestUrgentHangsParent()
+{
+ MOZ_COUNT_DTOR(TestUrgentHangsParent);
+}
+
+void
+TestUrgentHangsParent::Main()
+{
+ SetReplyTimeoutMs(1000);
+
+ // Should succeed despite the nested sleep call because the content process
+ // responded to the transaction.
+ if (!SendTest1_1())
+ fail("sending Test1_1");
+
+ // Fails with a timeout.
+ if (SendTest2())
+ fail("sending Test2");
+
+ // Also fails since we haven't gotten a response for Test2 yet.
+ if (SendTest3())
+ fail("sending Test3");
+
+ // Do a second round of testing once the reply to Test2 comes back.
+ MessageLoop::current()->PostDelayedTask(
+ NewNonOwningRunnableMethod(this, &TestUrgentHangsParent::SecondStage),
+ 3000);
+}
+
+void
+TestUrgentHangsParent::SecondStage()
+{
+ // Send an async message that waits 2 seconds and then sends a sync message
+ // (which should be processed).
+ if (!SendTest4())
+ fail("sending Test4");
+
+ // Send a sync message that will time out because the child is waiting
+ // inside RecvTest4.
+ if (SendTest4_1())
+ fail("sending Test4_1");
+
+ MessageLoop::current()->PostDelayedTask(
+ NewNonOwningRunnableMethod(this, &TestUrgentHangsParent::ThirdStage),
+ 3000);
+}
+
+void
+TestUrgentHangsParent::ThirdStage()
+{
+ // The third stage does the same thing as the second stage except that the
+ // child sends an urgent message to us. In this case, we actually answer
+ // that message unconditionally.
+
+ // Send an async message that waits 2 seconds and then sends a sync message
+ // (which should be processed).
+ if (!SendTest5())
+ fail("sending Test5");
+
+ // Send a sync message that will time out because the child is waiting
+ // inside RecvTest5.
+ if (SendTest5_1())
+ fail("sending Test5_1");
+
+ // Close the channel after the child finishes its work in RecvTest5.
+ MessageLoop::current()->PostDelayedTask(
+ NewNonOwningRunnableMethod(this, &TestUrgentHangsParent::Close),
+ 3000);
+}
+
+bool
+TestUrgentHangsParent::RecvTest1_2()
+{
+ if (!SendTest1_3())
+ fail("sending Test1_3");
+ return true;
+}
+
+bool
+TestUrgentHangsParent::RecvTestInner()
+{
+ mInnerCount++;
+ return true;
+}
+
+bool
+TestUrgentHangsParent::RecvTestInnerUrgent()
+{
+ mInnerUrgentCount++;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// child
+
+bool
+TestUrgentHangsChild::RecvTest1_1()
+{
+ if (!SendTest1_2())
+ fail("sending Test1_2");
+
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest1_3()
+{
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest2()
+{
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ // Should fail because of the timeout.
+ if (SendTestInner())
+ fail("sending TestInner");
+
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest3()
+{
+ fail("RecvTest3 should never be called");
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest4()
+{
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ // This won't fail because we should handle Test4_1 here before actually
+ // sending TestInner to the parent.
+ if (!SendTestInner())
+ fail("sending TestInner");
+
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest4_1()
+{
+ // This should fail because Test4_1 timed out and hasn't gotten a response
+ // yet.
+ if (SendTestInner())
+ fail("sending TestInner");
+
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest5()
+{
+ PR_Sleep(PR_SecondsToInterval(2));
+
+ // This message will actually be handled by the parent even though it's in
+ // the timeout state.
+ if (!SendTestInnerUrgent())
+ fail("sending TestInner");
+
+ return true;
+}
+
+bool
+TestUrgentHangsChild::RecvTest5_1()
+{
+ // This message will actually be handled by the parent even though it's in
+ // the timeout state.
+ if (!SendTestInnerUrgent())
+ fail("sending TestInner");
+
+ return true;
+}
+
+TestUrgentHangsChild::TestUrgentHangsChild()
+{
+ MOZ_COUNT_CTOR(TestUrgentHangsChild);
+}
+
+TestUrgentHangsChild::~TestUrgentHangsChild()
+{
+ MOZ_COUNT_DTOR(TestUrgentHangsChild);
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
diff --git a/ipc/ipdl/test/cxx/TestUrgentHangs.h b/ipc/ipdl/test/cxx/TestUrgentHangs.h
new file mode 100644
index 0000000000..bbe63d11e3
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestUrgentHangs.h
@@ -0,0 +1,79 @@
+#ifndef mozilla__ipdltest_TestUrgentHangs_h
+#define mozilla__ipdltest_TestUrgentHangs_h 1
+
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+
+#include "mozilla/_ipdltest/PTestUrgentHangsParent.h"
+#include "mozilla/_ipdltest/PTestUrgentHangsChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestUrgentHangsParent :
+ public PTestUrgentHangsParent
+{
+public:
+ TestUrgentHangsParent();
+ virtual ~TestUrgentHangsParent();
+
+ static bool RunTestInProcesses() { return true; }
+ static bool RunTestInThreads() { return false; }
+
+ void Main();
+ void SecondStage();
+ void ThirdStage();
+
+ bool RecvTest1_2();
+ bool RecvTestInner();
+ bool RecvTestInnerUrgent();
+
+ bool ShouldContinueFromReplyTimeout() override
+ {
+ return false;
+ }
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ if (mInnerCount != 1) {
+ fail("wrong mInnerCount");
+ }
+ if (mInnerUrgentCount != 2) {
+ fail("wrong mInnerUrgentCount");
+ }
+ passed("ok");
+ QuitParent();
+ }
+
+private:
+ size_t mInnerCount, mInnerUrgentCount;
+};
+
+
+class TestUrgentHangsChild :
+ public PTestUrgentHangsChild
+{
+public:
+ TestUrgentHangsChild();
+ virtual ~TestUrgentHangsChild();
+
+ bool RecvTest1_1();
+ bool RecvTest1_3();
+ bool RecvTest2();
+ bool RecvTest3();
+ bool RecvTest4();
+ bool RecvTest4_1();
+ bool RecvTest5();
+ bool RecvTest5_1();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override
+ {
+ QuitChild();
+ }
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestUrgentHangs_h
diff --git a/ipc/ipdl/test/cxx/app/Makefile.in b/ipc/ipdl/test/cxx/app/Makefile.in
new file mode 100644
index 0000000000..3738d44d39
--- /dev/null
+++ b/ipc/ipdl/test/cxx/app/Makefile.in
@@ -0,0 +1,5 @@
+# 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/.
+
+NSDISTMODE = copy
diff --git a/ipc/ipdl/test/cxx/app/TestIPDL.cpp b/ipc/ipdl/test/cxx/app/TestIPDL.cpp
new file mode 100644
index 0000000000..a34cca0809
--- /dev/null
+++ b/ipc/ipdl/test/cxx/app/TestIPDL.cpp
@@ -0,0 +1,20 @@
+/* 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 "nsXULAppAPI.h"
+
+#if defined(XP_WIN)
+#include <windows.h>
+#include "nsWindowsWMain.cpp"
+#endif
+
+int
+main(int argc, char** argv)
+{
+ // the first argument specifies which IPDL test case/suite to load
+ if (argc < 2)
+ return 1;
+
+ return XRE_RunIPDLTest(argc, argv);
+}
diff --git a/ipc/ipdl/test/cxx/app/moz.build b/ipc/ipdl/test/cxx/app/moz.build
new file mode 100644
index 0000000000..d464e78a67
--- /dev/null
+++ b/ipc/ipdl/test/cxx/app/moz.build
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+GeckoProgram('ipdlunittest', linkage='dependent')
+
+SOURCES += [
+ 'TestIPDL.cpp',
+]
+include('/ipc/chromium/chromium-config.mozbuild')
+
+LOCAL_INCLUDES += [
+ '/toolkit/xre',
+ '/xpcom/base',
+]
+
+if CONFIG['_MSC_VER']:
+ WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
diff --git a/ipc/ipdl/test/cxx/genIPDLUnitTests.py b/ipc/ipdl/test/cxx/genIPDLUnitTests.py
new file mode 100644
index 0000000000..24026451bc
--- /dev/null
+++ b/ipc/ipdl/test/cxx/genIPDLUnitTests.py
@@ -0,0 +1,141 @@
+# 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/.
+
+import string, sys
+
+def usage():
+ print >>sys.stderr, """
+%s template_file -t unit_tests... -e extra_protocols...
+
+ TEMPLATE_FILE is used to generate to generate the unit-tester .cpp
+ UNIT_TESTS are the top-level protocols defining unit tests
+ EXTRA_PROTOCOLS are top-level protocols for subprocesses that can be
+ spawned in tests but are not unit tests in and of
+ themselves
+"""% (sys.argv[0])
+ sys.exit(1)
+
+def main(argv):
+ template = argv[1]
+
+ if argv[2] != '-t': usage()
+ i = 3
+ unittests = []
+ while argv[i] != '-e':
+ unittests.append(argv[i])
+ i += 1
+
+ extras = argv[(i+1):]
+
+ includes = '\n'.join([
+ '#include "%s.h"'% (t) for t in unittests ])
+
+
+ enum_values = '\n'.join([
+ ' %s,'% (t) for t in unittests+extras ])
+ last_enum = unittests[-1]
+
+
+ string_to_enums = '\n'.join([
+ ''' else if (!strcmp(aString, "%s"))
+ return %s;'''% (t, t) for t in unittests+extras ])
+
+ enum_to_strings = '\n'.join([
+ ''' case %s:
+ return "%s";'''%(t, t) for t in unittests+extras ])
+
+ parent_delete_cases = '\n'.join([
+''' case %s: {
+ delete reinterpret_cast<%sParent*>(gParentActor);
+ return;
+ }
+'''% (t, t) for t in unittests ])
+
+ parent_enabled_cases_proc = '\n'.join([
+''' case %s: {
+ if (!%sParent::RunTestInProcesses()) {
+ passed("N/A to proc");
+ DeferredParentShutdown();
+ return;
+ }
+ break;
+ }
+''' % (t, t) for t in unittests ])
+
+ parent_main_cases_proc = '\n'.join([
+''' case %s: {
+ %sParent** parent =
+ reinterpret_cast<%sParent**>(&gParentActor);
+ *parent = new %sParent();
+ (*parent)->Open(transport, child);
+ return (*parent)->Main();
+ }
+'''% (t, t, t, t) for t in unittests ])
+
+ parent_enabled_cases_thread = '\n'.join([
+''' case %s: {
+ if (!%sParent::RunTestInThreads()) {
+ passed("N/A to threads");
+ DeferredParentShutdown();
+ return;
+ }
+ break;
+ }
+''' % (t, t) for t in unittests ])
+
+ parent_main_cases_thread = '\n'.join([
+''' case %s: {
+ %sParent** parent =
+ reinterpret_cast<%sParent**>(&gParentActor);
+ *parent = new %sParent();
+
+ %sChild** child =
+ reinterpret_cast<%sChild**>(&gChildActor);
+ *child = new %sChild();
+
+ ::mozilla::ipc::MessageChannel *childChannel = (*child)->GetIPCChannel();
+ ::mozilla::ipc::Side parentSide =
+ ::mozilla::ipc::ParentSide;
+
+ (*parent)->Open(childChannel, childMessageLoop, parentSide);
+ return (*parent)->Main();
+ }
+'''% (t, t, t, t, t, t, t) for t in unittests ])
+
+ child_delete_cases = '\n'.join([
+''' case %s: {
+ delete reinterpret_cast<%sChild*>(gChildActor);
+ return;
+ }
+'''% (t, t) for t in unittests+extras ])
+
+
+ child_init_cases = '\n'.join([
+''' case %s: {
+ %sChild** child =
+ reinterpret_cast<%sChild**>(&gChildActor);
+ *child = new %sChild();
+ (*child)->Open(transport, parentPid, worker);
+ return;
+ }
+'''% (t, t, t, t) for t in unittests+extras ])
+
+ templatefile = open(template, 'r')
+ sys.stdout.write(
+ string.Template(templatefile.read()).substitute(
+ INCLUDES=includes,
+ ENUM_VALUES=enum_values, LAST_ENUM=last_enum,
+ STRING_TO_ENUMS=string_to_enums,
+ ENUM_TO_STRINGS=enum_to_strings,
+ PARENT_DELETE_CASES=parent_delete_cases,
+ PARENT_ENABLED_CASES_PROC=parent_enabled_cases_proc,
+ PARENT_MAIN_CASES_PROC=parent_main_cases_proc,
+ PARENT_ENABLED_CASES_THREAD=parent_enabled_cases_thread,
+ PARENT_MAIN_CASES_THREAD=parent_main_cases_thread,
+ CHILD_DELETE_CASES=child_delete_cases,
+ CHILD_INIT_CASES=child_init_cases))
+ templatefile.close()
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/ipc/ipdl/test/cxx/moz.build b/ipc/ipdl/test/cxx/moz.build
new file mode 100644
index 0000000000..c87455e02d
--- /dev/null
+++ b/ipc/ipdl/test/cxx/moz.build
@@ -0,0 +1,134 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += ['app']
+
+EXPORTS.mozilla._ipdltest += [
+ 'IPDLUnitTestProcessChild.h',
+ 'IPDLUnitTests.h',
+ 'IPDLUnitTestTypes.h',
+ 'IPDLUnitTestUtils.h',
+]
+
+SOURCES += [
+ 'TestActorPunning.cpp',
+ 'TestBadActor.cpp',
+ 'TestBridgeMain.cpp',
+ 'TestCancel.cpp',
+ 'TestCrashCleanup.cpp',
+ 'TestDataStructures.cpp',
+ 'TestDemon.cpp',
+ 'TestDesc.cpp',
+ 'TestEndpointBridgeMain.cpp',
+ 'TestEndpointOpens.cpp',
+ 'TestFailedCtor.cpp',
+ 'TestHangs.cpp',
+ 'TestHighestPrio.cpp',
+ 'TestInterruptErrorCleanup.cpp',
+ 'TestInterruptRaces.cpp',
+ 'TestInterruptShutdownRace.cpp',
+ 'TestJSON.cpp',
+ 'TestLatency.cpp',
+ 'TestManyChildAllocs.cpp',
+ 'TestMultiMgrs.cpp',
+ 'TestNestedLoops.cpp',
+ 'TestOpens.cpp',
+ 'TestRaceDeadlock.cpp',
+ 'TestRaceDeferral.cpp',
+ 'TestRacyInterruptReplies.cpp',
+ 'TestRacyReentry.cpp',
+ 'TestRacyUndefer.cpp',
+ 'TestRPC.cpp',
+ 'TestSanity.cpp',
+ 'TestSelfManageRoot.cpp',
+ 'TestShmem.cpp',
+ 'TestShutdown.cpp',
+ 'TestStackHooks.cpp',
+ 'TestSyncError.cpp',
+ 'TestSyncHang.cpp',
+ 'TestSyncWakeup.cpp',
+ 'TestUrgency.cpp',
+ 'TestUrgentHangs.cpp',
+]
+
+SOURCES += [
+ '!IPDLUnitTests.cpp',
+ 'IPDLUnitTestProcessChild.cpp',
+ 'IPDLUnitTestSubprocess.cpp',
+]
+
+IPDL_SOURCES += [
+ 'PTestActorPunning.ipdl',
+ 'PTestActorPunningPunned.ipdl',
+ 'PTestActorPunningSub.ipdl',
+ 'PTestBadActor.ipdl',
+ 'PTestBadActorSub.ipdl',
+ 'PTestBridgeMain.ipdl',
+ 'PTestBridgeMainSub.ipdl',
+ 'PTestBridgeSub.ipdl',
+ 'PTestCancel.ipdl',
+ 'PTestCrashCleanup.ipdl',
+ 'PTestDataStructures.ipdl',
+ 'PTestDataStructuresCommon.ipdlh',
+ 'PTestDataStructuresSub.ipdl',
+ 'PTestDemon.ipdl',
+ 'PTestDesc.ipdl',
+ 'PTestDescSub.ipdl',
+ 'PTestDescSubsub.ipdl',
+ 'PTestEndpointBridgeMain.ipdl',
+ 'PTestEndpointBridgeMainSub.ipdl',
+ 'PTestEndpointBridgeSub.ipdl',
+ 'PTestEndpointOpens.ipdl',
+ 'PTestEndpointOpensOpened.ipdl',
+ 'PTestFailedCtor.ipdl',
+ 'PTestFailedCtorSub.ipdl',
+ 'PTestFailedCtorSubsub.ipdl',
+ 'PTestHandle.ipdl',
+ 'PTestHangs.ipdl',
+ 'PTestHighestPrio.ipdl',
+ 'PTestIndirectProtocolParam.ipdlh',
+ 'PTestIndirectProtocolParamFirst.ipdl',
+ 'PTestIndirectProtocolParamManage.ipdl',
+ 'PTestIndirectProtocolParamSecond.ipdl',
+ 'PTestInterruptErrorCleanup.ipdl',
+ 'PTestInterruptRaces.ipdl',
+ 'PTestInterruptShutdownRace.ipdl',
+ 'PTestJSON.ipdl',
+ 'PTestLatency.ipdl',
+ 'PTestManyChildAllocs.ipdl',
+ 'PTestManyChildAllocsSub.ipdl',
+ 'PTestMultiMgrs.ipdl',
+ 'PTestMultiMgrsBottom.ipdl',
+ 'PTestMultiMgrsLeft.ipdl',
+ 'PTestMultiMgrsRight.ipdl',
+ 'PTestNestedLoops.ipdl',
+ 'PTestOpens.ipdl',
+ 'PTestOpensOpened.ipdl',
+ 'PTestPriority.ipdl',
+ 'PTestRaceDeadlock.ipdl',
+ 'PTestRaceDeferral.ipdl',
+ 'PTestRacyInterruptReplies.ipdl',
+ 'PTestRacyReentry.ipdl',
+ 'PTestRacyUndefer.ipdl',
+ 'PTestRPC.ipdl',
+ 'PTestSanity.ipdl',
+ 'PTestSelfManage.ipdl',
+ 'PTestSelfManageRoot.ipdl',
+ 'PTestShmem.ipdl',
+ 'PTestShutdown.ipdl',
+ 'PTestShutdownSub.ipdl',
+ 'PTestShutdownSubsub.ipdl',
+ 'PTestStackHooks.ipdl',
+ 'PTestSyncError.ipdl',
+ 'PTestSyncHang.ipdl',
+ 'PTestSyncWakeup.ipdl',
+ 'PTestUrgency.ipdl',
+ 'PTestUrgentHangs.ipdl',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'