bug 518126, part 2: add infrastructure for C++ tests of IPDL-generated code. r=ted,bsmedberg
authorChris Jones <jones.chris.g@gmail.com>
Tue, 06 Oct 2009 13:02:26 -0500
changeset 35963 32b33070fcf5c29229e3e945713db916a5b2bbb3
parent 35962 192af033017183f44b3eb43a1b40f99025e65c15
child 35964 5db6674097fe73b69adef7c694a87118d4cda35c
push idunknown
push userunknown
push dateunknown
reviewersted, bsmedberg
bugs518126
milestone1.9.3a1pre
bug 518126, part 2: add infrastructure for C++ tests of IPDL-generated code. r=ted,bsmedberg
config/autoconf.mk.in
configure.in
ipc/ipdl/Makefile.in
ipc/ipdl/test/Makefile.in
ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp
ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h
ipc/ipdl/test/cxx/IPDLUnitTestThreadChild.cpp
ipc/ipdl/test/cxx/IPDLUnitTestThreadChild.h
ipc/ipdl/test/cxx/IPDLUnitTests.h
ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
ipc/ipdl/test/cxx/Makefile.in
ipc/ipdl/test/cxx/PTestSanity.ipdl
ipc/ipdl/test/cxx/README.txt
ipc/ipdl/test/cxx/TestSanity.cpp
ipc/ipdl/test/cxx/TestSanity.h
ipc/ipdl/test/cxx/app/Makefile.in
ipc/ipdl/test/cxx/app/TestIPDL.cpp
ipc/ipdl/test/cxx/genIPDLUnitTests.py
ipc/ipdl/test/cxx/ipdl.mk
ipc/ipdl/test/ipdl/Makefile.in
toolkit/library/libxul-config.mk
toolkit/toolkit-tiers.mk
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsEmbedFunctions.cpp
xpcom/build/nsXULAppAPI.h
xpcom/system/nsIXULRuntime.idl
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -99,17 +99,18 @@ MOZ_DEBUG_ENABLE_DEFS		= @MOZ_DEBUG_ENAB
 MOZ_DEBUG_DISABLE_DEFS	= @MOZ_DEBUG_DISABLE_DEFS@
 MOZ_DEBUG_FLAGS	= @MOZ_DEBUG_FLAGS@
 MOZ_DEBUG_LDFLAGS=@MOZ_DEBUG_LDFLAGS@
 MOZ_DBGRINFO_MODULES	= @MOZ_DBGRINFO_MODULES@
 MOZ_EXTENSIONS  = @MOZ_EXTENSIONS@
 MOZ_IMG_DECODERS= @MOZ_IMG_DECODERS@
 MOZ_IMG_ENCODERS= @MOZ_IMG_ENCODERS@
 MOZ_JSDEBUGGER  = @MOZ_JSDEBUGGER@
-MOZ_IPC = @MOZ_IPC@
+MOZ_IPC 	= @MOZ_IPC@
+MOZ_IPDL_TESTS 	= @MOZ_IPDL_TESTS@
 MOZ_LEAKY	= @MOZ_LEAKY@
 MOZ_MEMORY      = @MOZ_MEMORY@
 MOZ_JPROF       = @MOZ_JPROF@
 MOZ_SHARK       = @MOZ_SHARK@
 MOZ_CALLGRIND   = @MOZ_CALLGRIND@
 MOZ_VTUNE       = @MOZ_VTUNE@
 MOZ_TRACEVIS    = @MOZ_TRACEVIS@
 DEHYDRA_PATH    = @DEHYDRA_PATH@
--- a/configure.in
+++ b/configure.in
@@ -5239,16 +5239,36 @@ fi
 
 if test -n "$MOZ_IPC"; then
     AC_DEFINE(MOZ_IPC)
 fi
 
 AC_SUBST(MOZ_IPC)
 
 dnl ========================================================
+dnl = Enable IPDL's "expensive" unit tests
+dnl ========================================================
+MOZ_IPDL_TESTS=
+
+MOZ_ARG_ENABLE_BOOL(ipdl-tests,
+[  --enable-ipdl-tests     Enable expensive IPDL tests],
+    MOZ_IPDL_TESTS=1,
+    MOZ_IPDL_TESTS=)
+
+if test -z "$MOZ_IPC" -a -n "$MOZ_IPDL_TESTS"; then
+    AC_MSG_ERROR([--enable-ipdl-tests requires --enable-ipc])
+fi
+
+if test -n "$MOZ_IPDL_TESTS"; then
+    AC_DEFINE(MOZ_IPDL_TESTS)
+fi
+
+AC_SUBST(MOZ_IPDL_TESTS)
+
+dnl ========================================================
 dnl = Disable plugin support
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(plugins,
 [  --disable-plugins       Disable plugins support],
     MOZ_PLUGINS=,
     MOZ_PLUGINS=1)
 
 dnl ========================================================
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -35,28 +35,26 @@
 # ***** END LICENSE BLOCK *****
 
 DEPTH = ../..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
-
-DIRS += test
-
 include $(topsrcdir)/config/rules.mk
 
-IPDLDIRS = \
-  dom/plugins \
-  dom/ipc \
-  netwerk/ipc \
-  netwerk/protocol/http/src \
-  ipc/test-harness \
-  ipc/testshell \
+IPDLDIRS =  \
+  dom/plugins  \
+  dom/ipc  \
+  netwerk/ipc  \
+  netwerk/protocol/http/src  \
+  ipc/ipdl/test/cxx  \
+  ipc/test-harness  \
+  ipc/testshell  \
   $(NULL)
 
 vpath %.ipdl $(topsrcdir)
 
 define ADD_IPDLDIR
 include $(topsrcdir)/$(IPDLDIR)/ipdl.mk
 ALL_IPDLSRCS += $$(IPDLSRCS:%=$(IPDLDIR)/%)
 endef
--- a/ipc/ipdl/test/Makefile.in
+++ b/ipc/ipdl/test/Makefile.in
@@ -36,11 +36,17 @@
 
 DEPTH = ../../..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-DIRS += ipdl cxx
+# we ignore MOZ_IPDL_TESTS for the IPDL-compiler-only tests, since they're
+# quick and painless
+DIRS += ipdl
+
+ifdef MOZ_IPDL_TESTS
+DIRS += cxx
+endif
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPDL Test Harness.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.com>.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "IPDLUnitTestSubprocess.h"
+
+using mozilla::ipc::GeckoChildProcessHost;
+
+namespace mozilla {
+namespace _ipdltest {
+
+IPDLUnitTestSubprocess::IPDLUnitTestSubprocess() :
+    GeckoChildProcessHost(GeckoProcessType_IPDLUnitTest)
+{
+}
+
+IPDLUnitTestSubprocess::~IPDLUnitTestSubprocess()
+{
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestSubprocess.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPDL Test Harness.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.com>.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#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
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestThreadChild.cpp
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.com>.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "IPDLUnitTestThreadChild.h"
+
+#include "IPDLUnitTests.h"
+
+using mozilla::ipc::GeckoThread;
+
+namespace mozilla {
+namespace _ipdltest {
+
+IPDLUnitTestThreadChild::IPDLUnitTestThreadChild() :
+    GeckoThread()
+{
+}
+
+IPDLUnitTestThreadChild::~IPDLUnitTestThreadChild()
+{
+}
+
+void
+IPDLUnitTestThreadChild::Init()
+{
+    GeckoThread::Init();
+    IPDLUnitTestChildInit(channel(), owner_loop());
+}
+
+void
+IPDLUnitTestThreadChild::CleanUp()
+{
+    GeckoThread::CleanUp();
+    IPDLUnitTestChildCleanUp();
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestThreadChild.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.com>.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla__ipdltest_IPDLUnitTestThreadChild_h
+#define mozilla__ipdltest_IPDLUnitTestThreadChild_h 1
+
+#include "mozilla/ipc/GeckoThread.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+class IPDLUnitTestThreadChild : public mozilla::ipc::GeckoThread
+{
+public:
+  IPDLUnitTestThreadChild();
+  ~IPDLUnitTestThreadChild();
+
+protected:
+  virtual void Init();
+  virtual void CleanUp();
+};
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+#endif // ifndef mozilla__ipdltest_IPDLUnitTestThreadChild_h
new file mode 100644
--- /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 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.com>.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla__ipdltest_IPDLUnitTests_h
+#define mozilla__ipdltest_IPDLUnitTests_h 1
+
+#include "base/message_loop.h"
+#include "chrome/common/ipc_channel.h"
+
+
+#define MOZ_IPDL_TESTFAIL_LABEL "TEST-UNEXPECTED-FAIL"
+#define MOZ_IPDL_TESTPASS_LABEL "TEST-PASS"
+
+// NB: these are named like the similar functions in
+// xpcom/test/TestHarness.h.  The names should nominally be kept in
+// sync.
+
+#define fail(fmt, ...)                                                  \
+    do {                                                                \
+        fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL " | %s | " fmt "\n",    \
+                IPDLUnitTestName(), ## __VA_ARGS__);                    \
+        NS_RUNTIMEABORT("failed test");                                 \
+    } while (0)
+
+#define passed(fmt, ...)                                                \
+    fprintf(stderr, MOZ_IPDL_TESTPASS_LABEL " | %s | " fmt "\n",        \
+            IPDLUnitTestName(), ## __VA_ARGS__)
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+//-----------------------------------------------------------------------------
+// both processes
+const char* const IPDLUnitTestName();
+
+
+//-----------------------------------------------------------------------------
+// parent process only
+
+void IPDLUnitTestMain(void* aData);
+
+// TODO: could clean up parent actor/subprocess
+
+
+//-----------------------------------------------------------------------------
+// child process only
+
+void IPDLUnitTestChildInit(IPC::Channel* transport, MessageLoop* worker);
+void IPDLUnitTestChildCleanUp();
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_IPDLUnitTests_h
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
@@ -0,0 +1,198 @@
+//
+// Autogenerated from Python template.  Hands off.
+//
+
+#include "IPDLUnitTests.h"
+
+#include <string.h>
+
+#include "base/command_line.h"
+#include "base/string_util.h"
+
+#include "IPDLUnitTestSubprocess.h"
+#include "IPDLUnitTestThreadChild.h"
+
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${INCLUDES}
+//-----------------------------------------------------------------------------
+
+using mozilla::_ipdltest::IPDLUnitTestSubprocess;
+using mozilla::_ipdltest::IPDLUnitTestThreadChild;
+
+//-----------------------------------------------------------------------------
+// data/functions accessed by both parent and child processes
+
+namespace {
+char* gIPDLUnitTestName = NULL; // "leaks"
+}
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+const char* const
+IPDLUnitTestName()
+{
+    if (!gIPDLUnitTestName) {
+        std::vector<std::wstring> args =
+            CommandLine::ForCurrentProcess()->GetLooseValues();
+        gIPDLUnitTestName = strdup(WideToUTF8(args[args.size()-1]).c_str());
+    }
+    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* const
+IPDLUnitTestToString(IPDLUnitTestType aTest)
+{
+    switch (aTest) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${ENUM_TO_STRINGS}
+//-----------------------------------------------------------------------------
+
+    default:
+        return NULL;
+    }
+}
+
+
+IPDLUnitTestType
+IPDLUnitTest()
+{
+    return IPDLUnitTestFromString(mozilla::_ipdltest::IPDLUnitTestName());
+}
+
+
+} // namespace <anon>
+
+
+//-----------------------------------------------------------------------------
+// parent process only
+
+namespace {
+void* gParentActor = NULL;
+}
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+void
+IPDLUnitTestMain(void* aData)
+{
+    char* testString = reinterpret_cast<char*>(aData);
+    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;
+
+    std::wstring testWString = UTF8ToWide(testString);
+    std::vector<std::wstring> testCaseArgs;
+    testCaseArgs.push_back(testWString);
+
+    IPDLUnitTestSubprocess* subprocess = new IPDLUnitTestSubprocess();
+    if (!subprocess->SyncLaunch(testCaseArgs))
+        fail("problem launching subprocess");
+
+    IPC::Channel* transport = subprocess->GetChannel();
+    if (!transport)
+        fail("no transport");
+
+    switch (test) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${PARENT_MAIN_CASES}
+//-----------------------------------------------------------------------------
+
+    default:
+        fail("not reached");
+        return;                 // unreached
+    }
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+//-----------------------------------------------------------------------------
+// child process only
+
+namespace {
+void* gChildActor = NULL;
+}
+
+
+namespace mozilla {
+namespace _ipdltest {
+
+void
+IPDLUnitTestChildInit(IPC::Channel* transport, MessageLoop* worker)
+{
+    switch (IPDLUnitTest()) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${CHILD_INIT_CASES}
+//-----------------------------------------------------------------------------
+
+    default:
+        fail("not reached");
+        return;                 // unreached
+    }
+}
+
+void IPDLUnitTestChildCleanUp()
+{
+    if (!gChildActor)
+        return;
+
+    switch (IPDLUnitTest()) {
+//-----------------------------------------------------------------------------
+//===== TEMPLATED =====
+${CHILD_CLEANUP_CASES}
+//-----------------------------------------------------------------------------
+
+    default:
+        fail("not reached");
+        return;                 // unreached
+    }
+}
+
+} // namespace _ipdltest
+} // namespace mozilla
--- a/ipc/ipdl/test/cxx/Makefile.in
+++ b/ipc/ipdl/test/cxx/Makefile.in
@@ -35,13 +35,59 @@
 # ***** END LICENSE BLOCK *****
 
 DEPTH = ../../../..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
+
+TOOL_DIRS += app
+
+MODULE = ipdlunittest
+
+EXPORTS_NAMESPACES = mozilla/_ipdltest
+EXPORTS_mozilla/_ipdltest =  \
+  IPDLUnitTests.h  \
+  IPDLUnitTestThreadChild.h  \
+  $(NULL)
+
+LIBRARY_NAME = $(MODULE)_s
+LIBXUL_LIBRARY = 1
+FORCE_STATIC_LIB = 1
+EXPORT_LIBRARY = 1
+
+
+IPDLTESTS =  \
+  TestSanity  \
+  $(NULL)
+
+IPDLTESTSRCS = $(addsuffix .cpp,$(IPDLTESTS))
+IPDLTESTHDRS = $(addprefix $(srcdir)/,$(addsuffix .h,$(IPDLTESTS)))
+
+TESTER_TEMPLATE := $(srcdir)/IPDLUnitTests.template.cpp
+GENTESTER := $(srcdir)/genIPDLUnitTests.py
+
+CPPSRCS =  \
+  IPDLUnitTests.cpp  \
+  IPDLUnitTestSubprocess.cpp  \
+  IPDLUnitTestThreadChild.cpp  \
+  $(IPDLTESTSRCS)  \
+  $(NULL)
+
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 
+RUNIPDLTEST := $(RUN_TEST_PROGRAM) $(DEPTH)/dist/bin/ipdlunittest$(BIN_SUFFIX)
+
+
+IPDLUnitTests.cpp : $(GENTESTER) $(TESTER_TEMPLATE) $(IPDLTESTHDRS)
+	$(PYTHON) $< $(TESTER_TEMPLATE) $(IPDLTESTS) > $@
+
 check::
-	echo "TODO: C++/IPDL unit tests"
+	@$(EXIT_ON_ERROR)  \
+	for test in $(IPDLTESTS); do  \
+		 $(RUNIPDLTEST) $$test ;  \
+	done
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/PTestSanity.ipdl
@@ -0,0 +1,31 @@
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+protocol PTestSanity {
+
+child:
+    Ping(int zero);
+
+parent:
+    Pong(int one);
+
+both:
+    UNREACHED();
+
+
+state PING:
+    send Ping goto PONG;
+
+state PONG:
+    recv Pong goto DEAD;
+
+    // hmm ... maybe support this idiom natively?
+state DEAD:
+    send UNREACHED goto DEAD;
+};
+
+
+} // namespace mozilla
+} // namespace _ipdltest
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/README.txt
@@ -0,0 +1,47 @@
+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
+
+Next
+
+  - add PTestFoo.ipdl to ipdl.mk
+
+  - append TestFoo to the variable IPDLTESTS in Makefile.in
+
+The IPDL test harness will try to execute |testFooParentActor->Main()|
+to kick off your test.  Make sure you define |TestFooParent::Main()|.
+
+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
+
+  make -C $OBJDIR/ipc/ipdl/test/cxx check
+
+If you want to launch only your test, run
+
+  cd $OBJDIR/dist/bin
+  ./run-mozilla.sh ./ipdlunittest TestFoo
+
+
+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.
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSanity.cpp
@@ -0,0 +1,91 @@
+#include "TestSanity.h"
+
+#include "nsIAppShell.h"
+
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h" // do_GetService()
+#include "nsWidgetsCID.h"       // NS_APPSHELL_CID
+
+#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))
+        fail("sending Ping");
+}
+
+
+bool
+TestSanityParent::RecvPong(const int& one)
+{
+    if (1 != one)
+        fail("invalid argument `%d', should have been `1'", one);
+
+    passed("sent ping/received pong");
+
+    static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+    nsCOMPtr<nsIAppShell> appShell (do_GetService(kAppShellCID));
+    appShell->Exit();
+
+    return true;
+}
+
+bool
+TestSanityParent::RecvUNREACHED()
+{
+    fail("unreached");
+    return false;               // not reached
+}
+
+
+//-----------------------------------------------------------------------------
+// child
+
+TestSanityChild::TestSanityChild()
+{
+    MOZ_COUNT_CTOR(TestSanityChild);
+}
+
+TestSanityChild::~TestSanityChild()
+{
+    MOZ_COUNT_DTOR(TestSanityChild);
+}
+
+bool
+TestSanityChild::RecvPing(const int& zero)
+{
+    if (0 != zero)
+        fail("invalid argument `%d', should have been `0'", zero);
+
+    if (!SendPong(1))
+        fail("sending Pong");
+    return true;
+}
+
+bool
+TestSanityChild::RecvUNREACHED()
+{
+    fail("unreached");
+    return false;               // not reached
+}
+
+
+} // namespace _ipdltest
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/TestSanity.h
@@ -0,0 +1,44 @@
+#ifndef mozilla__ipdltest_TestSanity_h
+#define mozilla__ipdltest_TestSanity_h 1
+
+
+#include "mozilla/_ipdltest/PTestSanityParent.h"
+#include "mozilla/_ipdltest/PTestSanityChild.h"
+
+namespace mozilla {
+namespace _ipdltest {
+
+
+class TestSanityParent :
+    public PTestSanityParent
+{
+public:
+    TestSanityParent();
+    virtual ~TestSanityParent();
+
+    void Main();
+
+protected:    
+    virtual bool RecvPong(const int& one);
+    virtual bool RecvUNREACHED();
+};
+
+
+class TestSanityChild :
+    public PTestSanityChild
+{
+public:
+    TestSanityChild();
+    virtual ~TestSanityChild();
+
+protected:
+    virtual bool RecvPing(const int& zero);
+    virtual bool RecvUNREACHED();
+};
+
+
+} // namespace _ipdltest
+} // namespace mozilla
+
+
+#endif // ifndef mozilla__ipdltest_TestSanity_h
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/app/Makefile.in
@@ -0,0 +1,72 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla IPC.
+#
+# The Initial Developer of the Original Code is
+#   The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = ipdlunittest
+PROGRAM = $(MODULE)$(BIN_SUFFIX)
+ENABLE_CXX_EXCEPTIONS = 1
+
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
+
+CPPSRCS = \
+  TestIPDL.cpp \
+  $(NULL)
+
+LIBS = \
+  $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
+  $(LIBXUL_LIBS) \
+  $(MOZ_JS_LIBS) \
+  $(NSPR_LIBS) \
+  $(NULL)
+
+include $(topsrcdir)/config/config.mk
+
+ifdef _MSC_VER
+ifdef WINCE
+WIN32_EXE_LDFLAGS += -ENTRY:mainWCRTStartup
+else
+WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
+endif
+endif
+
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/app/TestIPDL.cpp
@@ -0,0 +1,52 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPC.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#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);
+}
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/genIPDLUnitTests.py
@@ -0,0 +1,107 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla Firefox.
+#
+# The Initial Developer of the Original Code is
+# The Mozilla Foundation <http://www.mozilla.org/>.
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import string, sys
+
+def main(argv):
+    template = argv[1]
+    unittests = argv[2:]
+
+    includes = '\n'.join([
+        '#include "%s.h"'% (t) for t in unittests ])
+
+
+    enum_values = '\n'.join([
+        '    %s,'% (t) for t in unittests ])
+    last_enum = unittests[-1]
+
+
+    string_to_enums = '\n'.join([
+        '''    else if (!strcmp(aString, "%s"))
+        return %s;'''% (t, t) for t in unittests ])
+
+    enum_to_strings = '\n'.join([
+        '''    case %s:
+        return "%s";'''%(t, t) for t in unittests ])
+
+
+    parent_main_cases = '\n'.join([
+'''    case %s: {
+        %sParent** parent =
+        reinterpret_cast<%sParent**>(&gParentActor);
+        *parent = new %sParent();
+        (*parent)->Open(transport);
+        return (*parent)->Main();
+        }
+'''% (t, t, t, t) for t in unittests ])
+
+
+    child_init_cases = '\n'.join([
+'''    case %s: {
+        %sChild** child =
+            reinterpret_cast<%sChild**>(&gChildActor);
+        *child = new %sChild();
+        (*child)->Open(transport, worker);
+        return;
+    }
+'''% (t, t, t, t) for t in unittests ])
+
+    child_cleanup_cases = '\n'.join([
+'''    case %s: {
+        %sChild** child =
+            reinterpret_cast<%sChild**>(&gChildActor);
+        delete *child;
+        *child = 0;
+        return;
+    }
+'''% (t, t, t) for t in unittests ])
+
+
+    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_MAIN_CASES=parent_main_cases,
+            CHILD_INIT_CASES=child_init_cases,
+            CHILD_CLEANUP_CASES=child_cleanup_cases))
+    templatefile.close()
+
+if __name__ == '__main__':
+    main(sys.argv)
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/cxx/ipdl.mk
@@ -0,0 +1,3 @@
+IPDLSRCS =					\
+  PTestSanity.ipdl				\
+  $(NULL)
--- a/ipc/ipdl/test/ipdl/Makefile.in
+++ b/ipc/ipdl/test/ipdl/Makefile.in
@@ -41,15 +41,15 @@ VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 OKTESTS := $(wildcard $(srcdir)/ok/*.ipdl)
 ERRORTESTS := $(wildcard $(srcdir)/error/*.ipdl)
 
 check::
-	@$(PYTHON) $(srcdir)/runtests.py			\
-		$(srcdir)/ok $(srcdir)/error			\
-		$(PYTHON) $(topsrcdir)/config/pythonpath.py	\
-		-I$(topsrcdir)/other-licenses/ply		\
-		$(topsrcdir)/ipc/ipdl/ipdl.py			\
-		OKTESTS $(OKTESTS)				\
+	@$(PYTHON) $(srcdir)/runtests.py  \
+		$(srcdir)/ok $(srcdir)/error  \
+		$(PYTHON) $(topsrcdir)/config/pythonpath.py  \
+		-I$(topsrcdir)/other-licenses/ply  \
+		$(topsrcdir)/ipc/ipdl/ipdl.py  \
+		OKTESTS $(OKTESTS)  \
 		ERRORTESTS $(ERRORTESTS)
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -96,16 +96,20 @@ STATIC_LIBS += \
   domipc_s \
   domplugins_s \
   mozipc_s \
   chromium_s \
   ipcshell_s \
   ipctestharness_s \
   $(NULL)
 
+ifdef MOZ_IPDL_TESTS
+STATIC_LIBS += ipdlunittest_s
+endif
+
 ifeq (Linux,$(OS_ARCH))
 OS_LIBS += -lrt
 endif
 ifeq (WINNT,$(OS_ARCH))
 OS_LIBS += psapi.lib dbghelp.lib
 endif
 endif
 
--- a/toolkit/toolkit-tiers.mk
+++ b/toolkit/toolkit-tiers.mk
@@ -165,16 +165,20 @@ tier_gecko_dirs	+= \
 ifdef MOZ_UNIVERSALCHARDET
 tier_gecko_dirs += extensions/universalchardet
 endif
 
 ifdef ACCESSIBILITY
 tier_gecko_dirs    += accessible
 endif
 
+ifdef MOZ_IPDL_TESTS
+tier_gecko_dirs += ipc/ipdl/test
+endif
+
 # 
 # tier "toolkit" - xpfe & toolkit
 #
 # The division of "gecko" and "toolkit" is somewhat arbitrary, and related
 # to history where "gecko" wasn't forked between seamonkey/firefox but
 # "toolkit" was.
 #
 
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -722,19 +722,20 @@ nsXULAppInfo::GetWidgetToolkit(nsACStrin
 #define SYNC_ENUMS(a,b) \
   PR_STATIC_ASSERT(nsIXULRuntime::PROCESS_TYPE_ ## a == \
                    static_cast<int>(GeckoProcessType_ ## b));
 
 SYNC_ENUMS(DEFAULT, Default)
 SYNC_ENUMS(PLUGIN, Plugin)
 SYNC_ENUMS(CONTENT, Content)
 SYNC_ENUMS(TESTHARNESS, TestHarness)
+SYNC_ENUMS(IPDLUNITTEST, IPDLUnitTest)
 
 // .. and ensure that that is all of them:
-PR_STATIC_ASSERT(GeckoProcessType_TestHarness + 1 == GeckoProcessType_End);
+PR_STATIC_ASSERT(GeckoProcessType_IPDLUnitTest + 1 == GeckoProcessType_End);
 
 NS_IMETHODIMP
 nsXULAppInfo::GetProcessType(PRUint32* aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
   *aResult = XRE_GetProcessType();
   return NS_OK;
 }
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -78,16 +78,23 @@
 
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/XPCShellEnvironment.h"
 #include "mozilla/test/TestParent.h"
 #include "mozilla/test/TestProcessParent.h"
 #include "mozilla/test/TestThreadChild.h"
 #include "mozilla/Monitor.h"
 
+#ifdef MOZ_IPDL_TESTS
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+#include "mozilla/_ipdltest/IPDLUnitTestThreadChild.h"
+
+using mozilla::_ipdltest::IPDLUnitTestThreadChild;
+#endif  // ifdef MOZ_IPDL_TESTS
+
 using mozilla::ipc::GeckoChildProcessHost;
 using mozilla::ipc::GeckoThread;
 using mozilla::ipc::BrowserProcessSubThread;
 using mozilla::ipc::ScopedXREEmbed;
 
 using mozilla::plugins::PluginThreadChild;
 using mozilla::dom::ContentProcessThread;
 using mozilla::dom::ContentProcessParent;
@@ -278,16 +285,24 @@ XRE_InitChildProcess(int aArgc,
     case GeckoProcessType_Content:
       mainThread = new ContentProcessThread();
       break;
 
     case GeckoProcessType_TestHarness:
       mainThread = new TestThreadChild();
       break;
 
+    case GeckoProcessType_IPDLUnitTest:
+#ifdef MOZ_IPDL_TESTS
+      mainThread = new IPDLUnitTestThreadChild();
+#else
+      NS_RUNTIMEABORT("rebuild with --enable-ipdl-tests");
+#endif
+      break;
+
     default:
       NS_RUNTIMEABORT("Unknown main thread class");
     }
 
     sChildProcessType = aProcess;
     ChildProcess process(mainThread);
 
     // Do IPC event loop
@@ -405,16 +420,39 @@ XRE_RunIPCTestHarness(int aArgc, char* a
 {
     nsresult rv =
         XRE_InitParentProcess(
             aArgc, aArgv, IPCTestHarnessMain, NULL);
     NS_ENSURE_SUCCESS(rv, 1);
     return 0;
 }
 
+#ifdef MOZ_IPDL_TESTS
+//-----------------------------------------------------------------------------
+// IPDL unit test
+
+int
+XRE_RunIPDLTest(int aArgc, char** aArgv)
+{
+    if (aArgc < 2) {
+        fprintf(stderr, "TEST-UNEXPECTED-FAIL | <---> | insufficient #args, need at least 2\n");
+        return 1;
+    }
+
+    void* data = reinterpret_cast<void*>(aArgv[aArgc-1]);
+
+    nsresult rv =
+        XRE_InitParentProcess(
+            --aArgc, aArgv, mozilla::_ipdltest::IPDLUnitTestMain, data);
+    NS_ENSURE_SUCCESS(rv, 1);
+
+    return 0;
+}
+#endif  // ifdef MOZ_IPDL_TESTS
+
 nsresult
 XRE_RunAppShell()
 {
     nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
     NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
 
     return appShell->Run();
 }
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -421,26 +421,28 @@ XRE_API(void,
 
 enum GeckoProcessType {
   GeckoProcessType_Default = 0,
 
   GeckoProcessType_Plugin,
   GeckoProcessType_Content,
 
   GeckoProcessType_TestHarness,
+  GeckoProcessType_IPDLUnitTest,
 
   GeckoProcessType_End,
   GeckoProcessType_Invalid = GeckoProcessType_End
 };
 
 static const char* const kGeckoProcessTypeString[] = {
   "default",
   "plugin",
   "tab",
   "testharness",
+  "ipdlunittest"
 };
 
 PR_STATIC_ASSERT(sizeof(kGeckoProcessTypeString) /
                  sizeof(kGeckoProcessTypeString[0]) ==
                  GeckoProcessType_End);
 
 
 XRE_API(const char*,
@@ -464,16 +466,20 @@ XRE_API(nsresult,
                                 char* aArgv[],
                                 MainFunction aMainFunction,
                                 void* aMainFunctionExtraData))
 
 XRE_API(int,
         XRE_RunIPCTestHarness, (int aArgc,
                                 char* aArgv[]))
 
+XRE_API(int,
+        XRE_RunIPDLTest, (int aArgc,
+                          char* aArgv[]))
+
 XRE_API(nsresult,
         XRE_RunAppShell, ())
 
 class MessageLoop;
 
 XRE_API(void,
         XRE_ShutdownChildProcess, (MessageLoop* aUILoop))
 
--- a/xpcom/system/nsIXULRuntime.idl
+++ b/xpcom/system/nsIXULRuntime.idl
@@ -89,14 +89,15 @@ interface nsIXULRuntime : nsISupports
 
   /**
    * The legal values of processType.
    */
   const unsigned long PROCESS_TYPE_DEFAULT = 0;
   const unsigned long PROCESS_TYPE_PLUGIN = 1;
   const unsigned long PROCESS_TYPE_CONTENT = 2;
   const unsigned long PROCESS_TYPE_TESTHARNESS = 3;
+  const unsigned long PROCESS_TYPE_IPDLUNITTEST = 4;
 
   /**
    * The type of the caller's process.  Returns one of the values above.
    */
   readonly attribute unsigned long processType;
 };