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