1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/exec/exec.cpp
3 // Purpose: test wxExecute()
4 // Author: Francesco Montorsi
5 // (based on console sample TestExecute() function)
7 // Copyright: (c) 2009 Francesco Montorsi
8 // (c) 2013 Rob Bresalier, Vadim Zeitlin
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ----------------------------------------------------------------------------
14 // ----------------------------------------------------------------------------
23 #include "wx/process.h"
24 #include "wx/sstream.h"
25 #include "wx/evtloop.h"
27 #include "wx/filename.h"
28 #include "wx/mstream.h"
29 #include "wx/scopeguard.h"
30 #include "wx/txtstrm.h"
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:"
46 #error "no command to exec"
49 #define SLEEP_END_STRING "Done sleeping"
53 enum AsyncExecLoopExitEnum
55 AsyncExec_DontExitLoop
,
58 } // anonymous namespace
60 // ----------------------------------------------------------------------------
62 // ----------------------------------------------------------------------------
64 class ExecTestCase
: public CppUnit::TestCase
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();
83 void TestAsyncRedirect();
84 void TestOverlappedSyncExecute();
86 // Helper: create an executable file sleeping for the given amount of
87 // seconds with the specified base name.
89 // Returns the name of the file.
90 static wxString
CreateSleepFile(const wxString
& basename
, int seconds
);
92 // Return the full command, to be passed to wxExecute(), launching the
93 // specified script file.
94 static wxString
MakeShellCommand(const wxString
& filename
);
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
};
101 void DoTestAsyncRedirect(const wxString
& command
,
103 const char* expectedContaining
);
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
110 AsyncInEventLoop() { }
112 long DoExecute(AsyncExecLoopExitEnum forceExitLoop_
,
113 const wxString
& command_
,
114 int flags_
= wxEXEC_ASYNC
,
115 wxProcess
* callback_
= NULL
)
117 forceExitLoop
= forceExitLoop_
;
120 callback
= callback_
;
124 // Trigger the timer to go off inside the event loop
125 // so that we can run wxExecute there.
128 // Run the event loop.
131 return wxExecuteReturnCode
;
136 // Run wxExecute inside the event loop.
137 wxExecuteReturnCode
= wxExecute(command
, flags
, callback
);
139 if (forceExitLoop
== AsyncExec_ExitLoop
)
141 wxEventLoop::GetActive()->Exit();
146 AsyncExecLoopExitEnum forceExitLoop
;
150 long wxExecuteReturnCode
;
153 DECLARE_NO_COPY_CLASS(ExecTestCase
)
156 // register in the unnamed registry so that these tests are run by default
157 CPPUNIT_TEST_SUITE_REGISTRATION( ExecTestCase
);
159 // also include in its own registry so that these tests can be run alone
160 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ExecTestCase
, "ExecTestCase" );
163 void ExecTestCase::TestShell()
165 CPPUNIT_ASSERT( wxShell(SHELL_COMMAND
) );
168 void ExecTestCase::TestExecute()
170 AsyncInEventLoop asyncInEventLoop
;
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
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 );
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 );
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
)
199 int execFlags
= wxEXEC_SYNC
;
203 execFlags
|= wxEXEC_NOEVENTS
;
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 );
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] );
216 // test running COMMAND_STDERR with redirection and the expected data
218 wxArrayString stderr_arr
;
220 CPPUNIT_ASSERT( wxExecute(COMMAND_STDERR
, stdout_arr
, stderr_arr
, execFlags
) != 0 );
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
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") );
233 void ExecTestCase::TestProcess()
235 AsyncInEventLoop asyncInEventLoop
;
237 // test wxExecute with wxProcess
238 wxProcess
*proc
= new wxProcess
;
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
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 );
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 );
257 // test wxExecute with wxProcess and REDIRECTION
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.
263 // First the default case, dispatching the events while waiting.
267 CPPUNIT_ASSERT_EQUAL( 0, wxExecute(COMMAND
, wxEXEC_SYNC
, &proc2
) );
269 wxStringOutputStream procOutput
;
270 CPPUNIT_ASSERT( proc2
.GetInputStream() );
271 CPPUNIT_ASSERT_EQUAL( wxSTREAM_EOF
,
272 proc2
.GetInputStream()->Read(procOutput
).GetLastError() );
274 wxString output
= procOutput
.GetString();
275 CPPUNIT_ASSERT_EQUAL( "hi", output
.Trim() );
278 // And now without event dispatching.
282 CPPUNIT_ASSERT_EQUAL( 0,
283 wxExecute(COMMAND
, wxEXEC_SYNC
| wxEXEC_NOEVENTS
, &proc2
) );
285 wxStringOutputStream procOutput
;
286 CPPUNIT_ASSERT( proc2
.GetInputStream() );
287 CPPUNIT_ASSERT_EQUAL( wxSTREAM_EOF
,
288 proc2
.GetInputStream()->Read(procOutput
).GetLastError() );
290 wxString output
= procOutput
.GetString();
291 CPPUNIT_ASSERT_EQUAL( "hi", output
.Trim() );
296 // This class exits the event loop associated with it when the child process
298 class TestAsyncProcess
: public wxProcess
301 wxEXPLICIT
TestAsyncProcess()
305 // may be overridden to be notified about process termination
306 virtual void OnTerminate(int WXUNUSED(pid
), int WXUNUSED(status
))
308 wxEventLoop::GetActive()->ScheduleExit();
312 wxDECLARE_NO_COPY_CLASS(TestAsyncProcess
);
315 void ExecTestCase::TestAsync()
317 // Test asynchronous execution with no redirection, just to make sure we
318 // get the OnTerminate() call.
319 TestAsyncProcess proc
;
320 AsyncInEventLoop asyncInEventLoop
;
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 );
329 ExecTestCase::DoTestAsyncRedirect(const wxString
& command
,
331 const char* expectedContaining
)
333 AsyncInEventLoop asyncInEventLoop
;
334 TestAsyncProcess proc
;
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 );
343 wxInputStream
*streamToCheck
= NULL
;
347 streamToCheck
= proc
.GetInputStream();
351 streamToCheck
= proc
.GetErrorStream();
355 wxTextInputStream
tis(*streamToCheck
);
357 // Check that the first line of output contains what we expect.
358 CPPUNIT_ASSERT( tis
.ReadLine().Contains(expectedContaining
) );
361 void ExecTestCase::TestAsyncRedirect()
363 // Test redirection with reading from the input stream after process termination.
364 DoTestAsyncRedirect(COMMAND
, Check_Stdout
, "hi");
366 // Test redirection with reading from the error stream after process termination.
367 DoTestAsyncRedirect(COMMAND_STDERR
, Check_Stderr
, "file");
371 wxString
ExecTestCase::CreateSleepFile(const wxString
& basename
, int seconds
)
374 static const char* const scriptExt
= ".sh";
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
=
380 "echo " SLEEP_END_STRING
"\n";
381 #elif defined(__WINDOWS__)
382 static const char* const scriptExt
= ".bat";
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";
391 #error "Need code to create sleep file for this platform"
394 const wxString fnSleep
= wxFileName(".", basename
, scriptExt
).GetFullPath();
399 fileSleep
.Create(fnSleep
, true, wxS_IRUSR
| wxS_IWUSR
| wxS_IXUSR
)
402 fileSleep
.Write(wxString::Format(scriptText
, seconds
));
408 wxString
ExecTestCase::MakeShellCommand(const wxString
& filename
)
413 command
= "/bin/sh " + filename
;
414 #elif defined(__WINDOWS__)
415 command
= wxString::Format("cmd.exe /c \"%s\"", filename
);
417 #error "Need to code to launch shell for this platform"
423 void ExecTestCase::TestOverlappedSyncExecute()
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.
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.
436 // Too bad you can't just register one test case of a test suite as a
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
444 DelayedExecuteTimer(const wxString
& command
, wxArrayString
& outputArray
)
445 : m_command(command
),
446 m_outputArray(outputArray
)
448 // The exact delay doesn't matter, anything short enough will do.
452 virtual void Notify()
454 wxExecute(m_command
, m_outputArray
);
459 wxArrayString
& m_outputArray
;
462 // Create two scripts with one of them taking longer than the other one to
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
);
469 const wxString shortSleepCommand
= MakeShellCommand(shortSleepFile
);
470 const wxString longSleepCommand
= MakeShellCommand(longSleepFile
);
472 // Collect the child process output
473 wxArrayString shortSleepOutput
,
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() );
484 CPPUNIT_ASSERT( !longSleepOutput
.empty() );
485 CPPUNIT_ASSERT_EQUAL( SLEEP_END_STRING
, longSleepOutput
.Last() );
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
490 DelayedExecuteTimer
delayShortSleep(shortSleepCommand
, shortSleepOutput
);
491 wxExecute(longSleepCommand
, longSleepOutput
);
492 CPPUNIT_ASSERT( !shortSleepOutput
.empty() );
493 CPPUNIT_ASSERT_EQUAL( SLEEP_END_STRING
, shortSleepOutput
.Last() );
495 CPPUNIT_ASSERT( !longSleepOutput
.empty() );
496 CPPUNIT_ASSERT_EQUAL( SLEEP_END_STRING
, longSleepOutput
.Last() );
497 #endif // !__WINDOWS__