1 //
2 // Autogenerated from Python template.  Hands off.
3 //
4 
5 #include <stdlib.h>
6 #include <string.h>
7 
8 #include "IPDLUnitTests.h"
9 
10 #include "base/command_line.h"
11 #include "base/string_util.h"
12 #include "base/task.h"
13 #include "base/thread.h"
14 
15 #include "nsRegion.h"
16 
17 #include "IPDLUnitTestSubprocess.h"
18 
19 // clang-format off
20 //-----------------------------------------------------------------------------
21 //===== TEMPLATED =====
22 ${INCLUDES}
23 //-----------------------------------------------------------------------------
24 // clang-format on
25 
26 using namespace std;
27 
28 using base::Thread;
29 
30 namespace mozilla {
31 namespace _ipdltest {
32 
33 void* gParentActor;
34 IPDLUnitTestSubprocess* gSubprocess;
35 
36 void* gChildActor;
37 
38 // Note: in threaded mode, this will be non-null (for both parent and
39 // child, since they share one set of globals).
40 Thread* gChildThread;
41 MessageLoop* gParentMessageLoop;
42 bool gParentDone;
43 bool gChildDone;
44 
45 void DeleteChildActor();
46 
47 //-----------------------------------------------------------------------------
48 // data/functions accessed by both parent and child processes
49 
50 char* gIPDLUnitTestName = nullptr;
51 
IPDLUnitTestName()52 const char* IPDLUnitTestName() {
53   if (!gIPDLUnitTestName) {
54 #if defined(OS_WIN)
55     vector<wstring> args = CommandLine::ForCurrentProcess()->GetLooseValues();
56     gIPDLUnitTestName = ::strdup(WideToUTF8(args[0]).c_str());
57 #elif defined(OS_POSIX)
58     vector<string> argv = CommandLine::ForCurrentProcess()->argv();
59     gIPDLUnitTestName = ::moz_xstrdup(argv[1].c_str());
60 #else
61 #  error Sorry
62 #endif
63   }
64   return gIPDLUnitTestName;
65 }
66 
67 }  // namespace _ipdltest
68 }  // namespace mozilla
69 
70 namespace {
71 
72 enum IPDLUnitTestType {
73   NoneTest = 0,
74 
75   // clang-format off
76 //-----------------------------------------------------------------------------
77 //===== TEMPLATED =====
78 ${ENUM_VALUES}
79 
80   LastTest = ${LAST_ENUM}
81 //-----------------------------------------------------------------------------
82 //clang-format on
83 };
84 
IPDLUnitTestFromString(const char * const aString)85 IPDLUnitTestType IPDLUnitTestFromString(const char* const aString) {
86   if (!aString) return static_cast<IPDLUnitTestType>(0);
87 // clang-format off
88 //-----------------------------------------------------------------------------
89 //===== TEMPLATED =====
90 ${STRING_TO_ENUMS}
91 //-----------------------------------------------------------------------------
92   // clang-format on
93   else return static_cast<IPDLUnitTestType>(0);
94 }
95 
IPDLUnitTest()96 IPDLUnitTestType IPDLUnitTest() {
97   return IPDLUnitTestFromString(::mozilla::_ipdltest::IPDLUnitTestName());
98 }
99 
100 }  // namespace
101 
102 //-----------------------------------------------------------------------------
103 // parent process only
104 
105 namespace mozilla {
106 namespace _ipdltest {
107 
108 void DeferredParentShutdown();
109 
110 void IPDLUnitTestThreadMain(char* testString);
111 
IPDLUnitTestMain(void * aData)112 void IPDLUnitTestMain(void* aData) {
113   char* testString = reinterpret_cast<char*>(aData);
114 
115   // Check if we are to run the test using threads instead:
116   const char* prefix = "thread:";
117   const int prefixLen = strlen(prefix);
118   if (!strncmp(testString, prefix, prefixLen)) {
119     IPDLUnitTestThreadMain(testString + prefixLen);
120     return;
121   }
122 
123   IPDLUnitTestType test = IPDLUnitTestFromString(testString);
124   if (!test) {
125     // use this instead of |fail()| because we don't know what the test is
126     fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL "| %s | unknown unit test %s\n",
127             "<--->", testString);
128     MOZ_CRASH("can't continue");
129   }
130   gIPDLUnitTestName = testString;
131 
132   // Check whether this test is enabled for processes:
133   switch (test) {
134     // clang-format off
135 //-----------------------------------------------------------------------------
136 //===== TEMPLATED =====
137 ${PARENT_ENABLED_CASES_PROC}
138 //-----------------------------------------------------------------------------
139       // clang-format on
140 
141     default:
142       fail("not reached");
143       return;  // unreached
144   }
145 
146   printf(MOZ_IPDL_TESTINFO_LABEL "| running test | %s\n", gIPDLUnitTestName);
147 
148   std::vector<std::string> testCaseArgs;
149   testCaseArgs.push_back(testString);
150 
151   gSubprocess = new IPDLUnitTestSubprocess();
152   if (!gSubprocess->SyncLaunch(testCaseArgs))
153     fail("problem launching subprocess");
154 
155   IPC::Channel* transport = gSubprocess->GetChannel();
156   if (!transport) fail("no transport");
157 
158   base::ProcessId child = base::GetProcId(gSubprocess->GetChildProcessHandle());
159 
160   switch (test) {
161     // clang-format off
162 //-----------------------------------------------------------------------------
163 //===== TEMPLATED =====
164 ${PARENT_MAIN_CASES_PROC}
165 //-----------------------------------------------------------------------------
166       // clang-format on
167 
168     default:
169       fail("not reached");
170       return;  // unreached
171   }
172 }
173 
IPDLUnitTestThreadMain(char * testString)174 void IPDLUnitTestThreadMain(char* testString) {
175   IPDLUnitTestType test = IPDLUnitTestFromString(testString);
176   if (!test) {
177     // use this instead of |fail()| because we don't know what the test is
178     fprintf(stderr, MOZ_IPDL_TESTFAIL_LABEL "| %s | unknown unit test %s\n",
179             "<--->", testString);
180     MOZ_CRASH("can't continue");
181   }
182   gIPDLUnitTestName = testString;
183 
184   // Check whether this test is enabled for threads:
185   switch (test) {
186     // clang-format off
187 //-----------------------------------------------------------------------------
188 //===== TEMPLATED =====
189 ${PARENT_ENABLED_CASES_THREAD}
190 //-----------------------------------------------------------------------------
191       // clang-format on
192 
193     default:
194       fail("not reached");
195       return;  // unreached
196   }
197 
198   printf(MOZ_IPDL_TESTINFO_LABEL "| running test | %s\n", gIPDLUnitTestName);
199 
200   std::vector<std::string> testCaseArgs;
201   testCaseArgs.push_back(testString);
202 
203   gChildThread = new Thread("ParentThread");
204   if (!gChildThread->Start()) fail("starting parent thread");
205 
206   gParentMessageLoop = MessageLoop::current();
207   MessageLoop* childMessageLoop = gChildThread->message_loop();
208 
209   switch (test) {
210     // clang-format off
211 //-----------------------------------------------------------------------------
212 //===== TEMPLATED =====
213 ${PARENT_MAIN_CASES_THREAD}
214 //-----------------------------------------------------------------------------
215       // clang-format on
216 
217     default:
218       fail("not reached");
219       return;  // unreached
220   }
221 }
222 
DeleteParentActor()223 void DeleteParentActor() {
224   if (!gParentActor) return;
225 
226   switch (IPDLUnitTest()) {
227     // clang-format off
228 //-----------------------------------------------------------------------------
229 //===== TEMPLATED =====
230 ${PARENT_DELETE_CASES}
231 //-----------------------------------------------------------------------------
232       // clang-format on
233     default:
234       ::mozilla::_ipdltest::fail("???");
235   }
236 }
237 
QuitXPCOM()238 void QuitXPCOM() {
239   DeleteParentActor();
240 
241   static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
242   nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
243   appShell->Exit();
244 }
245 
DeleteSubprocess(MessageLoop * uiLoop)246 void DeleteSubprocess(MessageLoop* uiLoop) {
247   // pong to QuitXPCOM
248   gSubprocess->Destroy();
249   gSubprocess = nullptr;
250   uiLoop->PostTask(NewRunnableFunction("QuitXPCOM", QuitXPCOM));
251 }
252 
DeferredParentShutdown()253 void DeferredParentShutdown() {
254   // ping to DeleteSubprocess
255   XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction(
256       "DeleteSubprocess", DeleteSubprocess, MessageLoop::current()));
257 }
258 
TryThreadedShutdown()259 void TryThreadedShutdown() {
260   // Stop if either:
261   // - the child has not finished,
262   // - the parent has not finished,
263   // - or this code has already executed.
264   // Remember: this TryThreadedShutdown() task is enqueued
265   // by both parent and child (though always on parent's msg loop).
266   if (!gChildDone || !gParentDone || !gChildThread) return;
267 
268   delete gChildThread;
269   gChildThread = 0;
270   DeferredParentShutdown();
271 }
272 
ChildCompleted()273 void ChildCompleted() {
274   // Executes on the parent message loop once child has completed.
275   gChildDone = true;
276   TryThreadedShutdown();
277 }
278 
QuitParent()279 void QuitParent() {
280   if (gChildThread) {
281     gParentDone = true;
282     MessageLoop::current()->PostTask(
283         NewRunnableFunction("TryThreadedShutdown", TryThreadedShutdown));
284   } else {
285     // defer "real" shutdown to avoid *Channel::Close() racing with the
286     // deletion of the subprocess
287     MessageLoop::current()->PostTask(
288         NewRunnableFunction("DeferredParentShutdown", DeferredParentShutdown));
289   }
290 }
291 
ChildDie()292 static void ChildDie() {
293   DeleteChildActor();
294   XRE_ShutdownChildProcess();
295 }
296 
QuitChild()297 void QuitChild() {
298   if (gChildThread) {  // Threaded-mode test
299     gParentMessageLoop->PostTask(
300         NewRunnableFunction("ChildCompleted", ChildCompleted));
301   } else {  // Process-mode test
302     MessageLoop::current()->PostTask(NewRunnableFunction("ChildDie", ChildDie));
303   }
304 }
305 
306 }  // namespace _ipdltest
307 }  // namespace mozilla
308 
309 //-----------------------------------------------------------------------------
310 // child process only
311 
312 namespace mozilla {
313 namespace _ipdltest {
314 
DeleteChildActor()315 void DeleteChildActor() {
316   if (!gChildActor) return;
317 
318   switch (IPDLUnitTest()) {
319     // clang-format off
320 //-----------------------------------------------------------------------------
321 //===== TEMPLATED =====
322 ${CHILD_DELETE_CASES}
323 //-----------------------------------------------------------------------------
324       // clang-format on
325     default:
326       ::mozilla::_ipdltest::fail("???");
327   }
328 }
329 
IPDLUnitTestChildInit(IPC::Channel * transport,base::ProcessId parentPid,MessageLoop * worker)330 void IPDLUnitTestChildInit(IPC::Channel* transport, base::ProcessId parentPid,
331                            MessageLoop* worker) {
332   switch (IPDLUnitTest()) {
333     // clang-format off
334 //-----------------------------------------------------------------------------
335 //===== TEMPLATED =====
336 ${CHILD_INIT_CASES}
337 //-----------------------------------------------------------------------------
338       // clang-format on
339 
340     default:
341       fail("not reached");
342       return;  // unreached
343   }
344 }
345 
346 }  // namespace _ipdltest
347 }  // namespace mozilla
348