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