diff options
Diffstat (limited to 'ipc/ipdl/test/cxx')
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' |