// // Autogenerated from Python template. Hands off. // #include #include #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" // clang-format off //----------------------------------------------------------------------------- //===== TEMPLATED ===== ${INCLUDES} //----------------------------------------------------------------------------- // clang-format on 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 args = CommandLine::ForCurrentProcess()->GetLooseValues(); gIPDLUnitTestName = ::strdup(WideToUTF8(args[0]).c_str()); #elif defined(OS_POSIX) vector 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, // clang-format off //----------------------------------------------------------------------------- //===== TEMPLATED ===== ${ENUM_VALUES} LastTest = ${LAST_ENUM} //----------------------------------------------------------------------------- //clang-format on }; IPDLUnitTestType IPDLUnitTestFromString(const char* const aString) { if (!aString) return static_cast(0); // clang-format off //----------------------------------------------------------------------------- //===== TEMPLATED ===== ${STRING_TO_ENUMS} //----------------------------------------------------------------------------- // clang-format on else return static_cast(0); } IPDLUnitTestType IPDLUnitTest() { return IPDLUnitTestFromString(::mozilla::_ipdltest::IPDLUnitTestName()); } } // namespace //----------------------------------------------------------------------------- // parent process only namespace mozilla { namespace _ipdltest { void DeferredParentShutdown(); void IPDLUnitTestThreadMain(char* testString); void IPDLUnitTestMain(void* aData) { char* testString = reinterpret_cast(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); MOZ_CRASH("can't continue"); } gIPDLUnitTestName = testString; // Check whether this test is enabled for processes: switch (test) { // clang-format off //----------------------------------------------------------------------------- //===== TEMPLATED ===== ${PARENT_ENABLED_CASES_PROC} //----------------------------------------------------------------------------- // clang-format on default: fail("not reached"); return; // unreached } printf(MOZ_IPDL_TESTINFO_LABEL "| running test | %s\n", gIPDLUnitTestName); std::vector 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) { // clang-format off //----------------------------------------------------------------------------- //===== TEMPLATED ===== ${PARENT_MAIN_CASES_PROC} //----------------------------------------------------------------------------- // clang-format on 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); MOZ_CRASH("can't continue"); } gIPDLUnitTestName = testString; // Check whether this test is enabled for threads: switch (test) { // clang-format off //----------------------------------------------------------------------------- //===== TEMPLATED ===== ${PARENT_ENABLED_CASES_THREAD} //----------------------------------------------------------------------------- // clang-format on default: fail("not reached"); return; // unreached } printf(MOZ_IPDL_TESTINFO_LABEL "| running test | %s\n", gIPDLUnitTestName); std::vector 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) { // clang-format off //----------------------------------------------------------------------------- //===== TEMPLATED ===== ${PARENT_MAIN_CASES_THREAD} //----------------------------------------------------------------------------- // clang-format on default: fail("not reached"); return; // unreached } } void DeleteParentActor() { if (!gParentActor) return; switch (IPDLUnitTest()) { // clang-format off //----------------------------------------------------------------------------- //===== TEMPLATED ===== ${PARENT_DELETE_CASES} //----------------------------------------------------------------------------- // clang-format on default: ::mozilla::_ipdltest::fail("???"); } } void QuitXPCOM() { DeleteParentActor(); static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); nsCOMPtr appShell(do_GetService(kAppShellCID)); appShell->Exit(); } void DeleteSubprocess(MessageLoop* uiLoop) { // pong to QuitXPCOM gSubprocess->Destroy(); gSubprocess = nullptr; uiLoop->PostTask(NewRunnableFunction("QuitXPCOM", QuitXPCOM)); } void DeferredParentShutdown() { // ping to DeleteSubprocess XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction( "DeleteSubprocess", 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", TryThreadedShutdown)); } else { // defer "real" shutdown to avoid *Channel::Close() racing with the // deletion of the subprocess MessageLoop::current()->PostTask( NewRunnableFunction("DeferredParentShutdown", DeferredParentShutdown)); } } static void ChildDie() { DeleteChildActor(); XRE_ShutdownChildProcess(); } void QuitChild() { if (gChildThread) { // Threaded-mode test gParentMessageLoop->PostTask( NewRunnableFunction("ChildCompleted", ChildCompleted)); } else { // Process-mode test MessageLoop::current()->PostTask(NewRunnableFunction("ChildDie", ChildDie)); } } } // namespace _ipdltest } // namespace mozilla //----------------------------------------------------------------------------- // child process only namespace mozilla { namespace _ipdltest { void DeleteChildActor() { if (!gChildActor) return; switch (IPDLUnitTest()) { // clang-format off //----------------------------------------------------------------------------- //===== TEMPLATED ===== ${CHILD_DELETE_CASES} //----------------------------------------------------------------------------- // clang-format on default: ::mozilla::_ipdltest::fail("???"); } } void IPDLUnitTestChildInit(IPC::Channel* transport, base::ProcessId parentPid, MessageLoop* worker) { switch (IPDLUnitTest()) { // clang-format off //----------------------------------------------------------------------------- //===== TEMPLATED ===== ${CHILD_INIT_CASES} //----------------------------------------------------------------------------- // clang-format on default: fail("not reached"); return; // unreached } } } // namespace _ipdltest } // namespace mozilla