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