]> git.saurik.com Git - wxWidgets.git/blob - tests/exec/exec.cpp
f67f30eff5e8d5628717159fad85f1ae1ef1bab1
[wxWidgets.git] / tests / exec / exec.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/exec/exec.cpp
3 // Purpose: test wxExecute()
4 // Author: Francesco Montorsi
5 // (based on console sample TestExecute() function)
6 // Created: 2009-01-10
7 // RCS-ID: $Id$
8 // Copyright: (c) 2009 Francesco Montorsi
9 // (c) 2013 Rob Bresalier, Vadim Zeitlin
10 // Licence: wxWindows licence
11 ///////////////////////////////////////////////////////////////////////////////
12
13 // ----------------------------------------------------------------------------
14 // headers
15 // ----------------------------------------------------------------------------
16
17 #include "testprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #include "wx/utils.h"
24 #include "wx/process.h"
25 #include "wx/sstream.h"
26 #include "wx/evtloop.h"
27 #include "wx/file.h"
28 #include "wx/filename.h"
29 #include "wx/mstream.h"
30 #include "wx/scopeguard.h"
31 #include "wx/txtstrm.h"
32 #include "wx/timer.h"
33
34 #ifdef __UNIX__
35 #define COMMAND "echo hi"
36 #define COMMAND_STDERR "cat nonexistentfile"
37 #define ASYNC_COMMAND "xclock"
38 #define SHELL_COMMAND "echo hi from shell>/dev/null"
39 #define COMMAND_NO_OUTPUT "echo -n"
40 #elif defined(__WINDOWS__)
41 #define COMMAND "cmd.exe /c \"echo hi\""
42 #define COMMAND_STDERR "cmd.exe /c \"type nonexistentfile\""
43 #define ASYNC_COMMAND "notepad"
44 #define SHELL_COMMAND "echo hi > nul:"
45 #define COMMAND_NO_OUTPUT COMMAND " > nul:"
46 #else
47 #error "no command to exec"
48 #endif // OS
49
50 #define SLEEP_END_STRING "Done sleeping"
51
52 namespace
53 {
54 enum AsyncExecLoopExitEnum
55 {
56 AsyncExec_DontExitLoop,
57 AsyncExec_ExitLoop
58 };
59 } // anonymous namespace
60
61 // ----------------------------------------------------------------------------
62 // test class
63 // ----------------------------------------------------------------------------
64
65 class ExecTestCase : public CppUnit::TestCase
66 {
67 public:
68 ExecTestCase() { }
69
70 private:
71 CPPUNIT_TEST_SUITE( ExecTestCase );
72 CPPUNIT_TEST( TestShell );
73 CPPUNIT_TEST( TestExecute );
74 CPPUNIT_TEST( TestProcess );
75 CPPUNIT_TEST( TestAsync );
76 CPPUNIT_TEST( TestAsyncRedirect );
77 CPPUNIT_TEST( TestOverlappedSyncExecute );
78 CPPUNIT_TEST_SUITE_END();
79
80 void TestShell();
81 void TestExecute();
82 void TestProcess();
83 void TestAsync();
84 void TestAsyncRedirect();
85 void TestOverlappedSyncExecute();
86
87 // Helper: create an executable file sleeping for the given amount of
88 // seconds with the specified base name.
89 //
90 // Returns the name of the file.
91 static wxString CreateSleepFile(const wxString& basename, int seconds);
92
93 // Return the full command, to be passed to wxExecute(), launching the
94 // specified script file.
95 static wxString MakeShellCommand(const wxString& filename);
96
97
98 // Helper of TestAsyncRedirect(): tests that the output of the given
99 // command on the given stream contains the expected string.
100 enum CheckStream { Check_Stdout, Check_Stderr };
101
102 void DoTestAsyncRedirect(const wxString& command,
103 CheckStream check,
104 const char* expectedContaining);
105
106 // This class is used as a helper in order to run wxExecute(ASYNC)
107 // inside of an event loop.
108 class AsyncInEventLoop : public wxTimer
109 {
110 public:
111 AsyncInEventLoop() { }
112
113 long DoExecute(AsyncExecLoopExitEnum forceExitLoop_,
114 const wxString& command_,
115 int flags_ = wxEXEC_ASYNC,
116 wxProcess* callback_ = NULL)
117 {
118 forceExitLoop = forceExitLoop_;
119 command = command_;
120 flags = flags_;
121 callback = callback_;
122
123 wxEventLoop loop;
124
125 // Trigger the timer to go off inside the event loop
126 // so that we can run wxExecute there.
127 StartOnce(10);
128
129 // Run the event loop.
130 loop.Run();
131
132 return wxExecuteReturnCode;
133 }
134
135 void Notify()
136 {
137 // Run wxExecute inside the event loop.
138 wxExecuteReturnCode = wxExecute(command, flags, callback);
139
140 if (forceExitLoop == AsyncExec_ExitLoop)
141 {
142 wxEventLoop::GetActive()->Exit();
143 }
144 }
145
146 private:
147 AsyncExecLoopExitEnum forceExitLoop;
148 wxString command;
149 int flags;
150 wxProcess* callback;
151 long wxExecuteReturnCode;
152 };
153
154 DECLARE_NO_COPY_CLASS(ExecTestCase)
155 };
156
157 // register in the unnamed registry so that these tests are run by default
158 CPPUNIT_TEST_SUITE_REGISTRATION( ExecTestCase );
159
160 // also include in its own registry so that these tests can be run alone
161 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ExecTestCase, "ExecTestCase" );
162
163
164 void ExecTestCase::TestShell()
165 {
166 CPPUNIT_ASSERT( wxShell(SHELL_COMMAND) );
167 }
168
169 void ExecTestCase::TestExecute()
170 {
171 AsyncInEventLoop asyncInEventLoop;
172
173 // test asynch exec
174 //
175 // asyncInEventLoop.DoExecute will perform the
176 // call to wxExecute(ASYNC) in an event loop, as required by
177 // console test (and this same event loop will also
178 // be used in GUI test too, even though not required, just to have
179 // common code).
180 long pid = asyncInEventLoop.DoExecute(AsyncExec_ExitLoop, // Force exit of event loop right
181 // after the call to wxExecute()
182 ASYNC_COMMAND, wxEXEC_ASYNC);
183 CPPUNIT_ASSERT( pid != 0 );
184
185 // NOTE: under Windows the first wxKill() invocation with wxSIGTERM
186 // may fail if the system is fast and the ASYNC_COMMAND app
187 // doesn't manage to create its HWND before our wxKill() is
188 // executed; in that case we "fall back" to the second invocation
189 // with wxSIGKILL (which should always succeed)
190 CPPUNIT_ASSERT( wxKill(pid, wxSIGTERM) == 0 ||
191 wxKill(pid, wxSIGKILL) == 0 );
192
193 int useNoeventsFlag;
194
195 // Test the sync execution case with/without wxEXEC_NOEVENTS flag
196 // because we use either an event loop or wxSelectDispatcher
197 // depending on this flag, and we want to test both cases.
198 for (useNoeventsFlag = 0; useNoeventsFlag <=1 ; ++useNoeventsFlag )
199 {
200 int execFlags = wxEXEC_SYNC;
201
202 if (useNoeventsFlag)
203 {
204 execFlags |= wxEXEC_NOEVENTS;
205 }
206
207 // test sync exec (with a command not producing any output to avoid
208 // interfering with the test):
209 CPPUNIT_ASSERT( wxExecute(COMMAND_NO_OUTPUT, execFlags) == 0 );
210
211 // test running COMMAND again, but this time with redirection:
212 // and the expected data is on stdout.
213 wxArrayString stdout_arr;
214 CPPUNIT_ASSERT_EQUAL( 0, wxExecute(COMMAND, stdout_arr, execFlags) );
215 CPPUNIT_ASSERT_EQUAL( "hi", stdout_arr[0] );
216
217 // test running COMMAND_STDERR with redirection and the expected data
218 // is on stderr.
219 wxArrayString stderr_arr;
220 stdout_arr.Empty();
221 CPPUNIT_ASSERT( wxExecute(COMMAND_STDERR, stdout_arr, stderr_arr, execFlags) != 0 );
222
223 // Check that there is something on stderr.
224 // In Unix systems, the 'cat' command has the name of the file it could not
225 // find in the error output.
226 // In Windows, the 'type' command outputs the following when it can't find
227 // a file:
228 // "The system cannot find the file specified"
229 // In both cases, we expect the word 'file' to be in the stderr.
230 CPPUNIT_ASSERT( stderr_arr[0].Contains("file") );
231 }
232 }
233
234 void ExecTestCase::TestProcess()
235 {
236 AsyncInEventLoop asyncInEventLoop;
237
238 // test wxExecute with wxProcess
239 wxProcess *proc = new wxProcess;
240
241 // asyncInEventLoop.DoExecute will perform the
242 // call to wxExecute(ASYNC) in an event loop, as required by
243 // console test (and this same event loop will also
244 // be used in GUI test too, even though not required, just to have
245 // common code).
246 long pid = asyncInEventLoop.DoExecute(AsyncExec_ExitLoop, // Force exit of event loop right
247 // after the call to wxExecute()
248 ASYNC_COMMAND, wxEXEC_ASYNC, proc);
249 CPPUNIT_ASSERT( proc->GetPid() == pid && pid != 0 );
250
251 // we're not going to process the wxEVT_END_PROCESS event,
252 // so the proc instance will auto-delete itself after we kill
253 // the asynch process:
254 CPPUNIT_ASSERT( wxKill(pid, wxSIGTERM) == 0 ||
255 wxKill(pid, wxSIGKILL) == 0 );
256
257
258 // test wxExecute with wxProcess and REDIRECTION
259
260 // Test the sync execution case with/without wxEXEC_NOEVENTS flag
261 // because we use either an event loop or wxSelectDispatcher
262 // depending on this flag, and we want to test both cases.
263
264 // First the default case, dispatching the events while waiting.
265 {
266 wxProcess proc2;
267 proc2.Redirect();
268 CPPUNIT_ASSERT_EQUAL( 0, wxExecute(COMMAND, wxEXEC_SYNC, &proc2) );
269
270 wxStringOutputStream procOutput;
271 CPPUNIT_ASSERT( proc2.GetInputStream() );
272 CPPUNIT_ASSERT_EQUAL( wxSTREAM_EOF,
273 proc2.GetInputStream()->Read(procOutput).GetLastError() );
274
275 wxString output = procOutput.GetString();
276 CPPUNIT_ASSERT_EQUAL( "hi", output.Trim() );
277 }
278
279 // And now without event dispatching.
280 {
281 wxProcess proc2;
282 proc2.Redirect();
283 CPPUNIT_ASSERT_EQUAL( 0,
284 wxExecute(COMMAND, wxEXEC_SYNC | wxEXEC_NOEVENTS, &proc2) );
285
286 wxStringOutputStream procOutput;
287 CPPUNIT_ASSERT( proc2.GetInputStream() );
288 CPPUNIT_ASSERT_EQUAL( wxSTREAM_EOF,
289 proc2.GetInputStream()->Read(procOutput).GetLastError() );
290
291 wxString output = procOutput.GetString();
292 CPPUNIT_ASSERT_EQUAL( "hi", output.Trim() );
293 }
294 }
295
296
297 // This class exits the event loop associated with it when the child process
298 // terminates.
299 class TestAsyncProcess : public wxProcess
300 {
301 public:
302 wxEXPLICIT TestAsyncProcess()
303 {
304 }
305
306 // may be overridden to be notified about process termination
307 virtual void OnTerminate(int WXUNUSED(pid), int WXUNUSED(status))
308 {
309 wxEventLoop::GetActive()->ScheduleExit();
310 }
311
312 private:
313 wxDECLARE_NO_COPY_CLASS(TestAsyncProcess);
314 };
315
316 void ExecTestCase::TestAsync()
317 {
318 // Test asynchronous execution with no redirection, just to make sure we
319 // get the OnTerminate() call.
320 TestAsyncProcess proc;
321 AsyncInEventLoop asyncInEventLoop;
322
323 CPPUNIT_ASSERT( asyncInEventLoop.DoExecute(
324 AsyncExec_DontExitLoop, // proc is expected (inside of its OnTerminate())
325 // to trigger the exit of the event loop.
326 COMMAND_NO_OUTPUT, wxEXEC_ASYNC, &proc) != 0 );
327 }
328
329 void
330 ExecTestCase::DoTestAsyncRedirect(const wxString& command,
331 CheckStream check,
332 const char* expectedContaining)
333 {
334 AsyncInEventLoop asyncInEventLoop;
335 TestAsyncProcess proc;
336
337 proc.Redirect();
338
339 CPPUNIT_ASSERT( asyncInEventLoop.DoExecute(
340 AsyncExec_DontExitLoop, // proc is expected (inside of its OnTerminate())
341 // to trigger the exit of the event loop.
342 command, wxEXEC_ASYNC, &proc) != 0 );
343
344 wxInputStream *streamToCheck = NULL;
345 switch ( check )
346 {
347 case Check_Stdout:
348 streamToCheck = proc.GetInputStream();
349 break;
350
351 case Check_Stderr:
352 streamToCheck = proc.GetErrorStream();
353 break;
354 }
355
356 wxTextInputStream tis(*streamToCheck);
357
358 // Check that the first line of output contains what we expect.
359 CPPUNIT_ASSERT( tis.ReadLine().Contains(expectedContaining) );
360 }
361
362 void ExecTestCase::TestAsyncRedirect()
363 {
364 // Test redirection with reading from the input stream after process termination.
365 DoTestAsyncRedirect(COMMAND, Check_Stdout, "hi");
366
367 // Test redirection with reading from the error stream after process termination.
368 DoTestAsyncRedirect(COMMAND_STDERR, Check_Stderr, "file");
369 }
370
371 // static
372 wxString ExecTestCase::CreateSleepFile(const wxString& basename, int seconds)
373 {
374 #ifdef __UNIX__
375 static const char* const scriptExt = ".sh";
376
377 // The script text is a format string with a single "%d" appearing in it
378 // which will be replaced by the number of seconds to sleep below.
379 static const char* const scriptText =
380 "sleep %d\n"
381 "echo " SLEEP_END_STRING "\n";
382 #elif defined(__WINDOWS__)
383 static const char* const scriptExt = ".bat";
384
385 // Notice that we need to ping N+1 times for it to take N seconds as the
386 // first ping is sent out immediately, without waiting a second.
387 static const char* const scriptText =
388 "@ ping 127.0.0.1 -n 1 > nul\n"
389 "@ ping 127.0.0.1 -n %d > nul\n"
390 "@ echo " SLEEP_END_STRING "\n";
391 #else
392 #error "Need code to create sleep file for this platform"
393 #endif
394
395 const wxString fnSleep = wxFileName(".", basename, scriptExt).GetFullPath();
396
397 wxFile fileSleep;
398 CPPUNIT_ASSERT
399 (
400 fileSleep.Create(fnSleep, true, wxS_IRUSR | wxS_IWUSR | wxS_IXUSR)
401 );
402
403 fileSleep.Write(wxString::Format(scriptText, seconds));
404
405 return fnSleep;
406 }
407
408 // static
409 wxString ExecTestCase::MakeShellCommand(const wxString& filename)
410 {
411 wxString command;
412
413 #ifdef __UNIX__
414 command = "/bin/sh " + filename;
415 #elif defined(__WINDOWS__)
416 command = wxString::Format("cmd.exe /c \"%s\"", filename);
417 #else
418 #error "Need to code to launch shell for this platform"
419 #endif
420
421 return command;
422 }
423
424 void ExecTestCase::TestOverlappedSyncExecute()
425 {
426 // Windows Synchronous wxExecute implementation does not currently
427 // support overlapped event loops. It is still using wxYield, which is
428 // not nestable. Therefore, this test would fail in Windows.
429 // If someday somebody changes that in Windows, they could use this
430 // test to verify it.
431 //
432 // Because MSW is not yet ready for this test, it may make sense to
433 // separate it out to its own test suite, so we could register it under
434 // "fixme" for Windows, but a real test for Unix. But that is more work,
435 // so just #ifndefing it here for now.
436 //
437 // Too bad you can't just register one test case of a test suite as a
438 // "fixme".
439 #ifndef __WINDOWS__
440 // Simple helper delaying the call to wxExecute(): instead of running it
441 // immediately, it runs it when we re-enter the event loop.
442 class DelayedExecuteTimer : public wxTimer
443 {
444 public:
445 DelayedExecuteTimer(const wxString& command, wxArrayString& outputArray)
446 : m_command(command),
447 m_outputArray(outputArray)
448 {
449 // The exact delay doesn't matter, anything short enough will do.
450 StartOnce(10);
451 }
452
453 virtual void Notify()
454 {
455 wxExecute(m_command, m_outputArray);
456 }
457
458 private:
459 wxString m_command;
460 wxArrayString& m_outputArray;
461 };
462
463 // Create two scripts with one of them taking longer than the other one to
464 // execute.
465 const wxString shortSleepFile = CreateSleepFile("shortsleep", 1);
466 wxON_BLOCK_EXIT1( wxRemoveFile, shortSleepFile );
467 const wxString longSleepFile = CreateSleepFile("longsleep", 2);
468 wxON_BLOCK_EXIT1( wxRemoveFile, longSleepFile );
469
470 const wxString shortSleepCommand = MakeShellCommand(shortSleepFile);
471 const wxString longSleepCommand = MakeShellCommand(longSleepFile);
472
473 // Collect the child process output
474 wxArrayString shortSleepOutput,
475 longSleepOutput;
476
477 // Test that launching a process taking a longer time to run while the
478 // shorter process is running works, i.e. that our outer wxExecute()
479 // doesn't return until both process terminate.
480 DelayedExecuteTimer delayLongSleep(longSleepCommand, longSleepOutput);
481 wxExecute(shortSleepCommand, shortSleepOutput);
482 CPPUNIT_ASSERT( !shortSleepOutput.empty() );
483 CPPUNIT_ASSERT_EQUAL( SLEEP_END_STRING, shortSleepOutput.Last() );
484
485 CPPUNIT_ASSERT( !longSleepOutput.empty() );
486 CPPUNIT_ASSERT_EQUAL( SLEEP_END_STRING, longSleepOutput.Last() );
487
488 // And also that, vice versa, running a short-lived child process that both
489 // starts and ends while a longer-lived parent process is still running
490 // works too.
491 DelayedExecuteTimer delayShortSleep(shortSleepCommand, shortSleepOutput);
492 wxExecute(longSleepCommand, longSleepOutput);
493 CPPUNIT_ASSERT( !shortSleepOutput.empty() );
494 CPPUNIT_ASSERT_EQUAL( SLEEP_END_STRING, shortSleepOutput.Last() );
495
496 CPPUNIT_ASSERT( !longSleepOutput.empty() );
497 CPPUNIT_ASSERT_EQUAL( SLEEP_END_STRING, longSleepOutput.Last() );
498 #endif // !__WINDOWS__
499 }