]> git.saurik.com Git - wxWidgets.git/blame - tests/exec/exec.cpp
Misc validity fixes to samples/xrc/rc/*.xrc.
[wxWidgets.git] / tests / exec / exec.cpp
CommitLineData
ca5016d4
FM
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
ca5016d4 7// Copyright: (c) 2009 Francesco Montorsi
821d856a
VZ
8// (c) 2013 Rob Bresalier, Vadim Zeitlin
9// Licence: wxWindows licence
ca5016d4
FM
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"
d6655d44
VZ
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"
ca5016d4
FM
32
33#ifdef __UNIX__
34 #define COMMAND "echo hi"
d6655d44 35 #define COMMAND_STDERR "cat nonexistentfile"
ca5016d4 36 #define ASYNC_COMMAND "xclock"
561ff470
VZ
37 #define SHELL_COMMAND "echo hi from shell>/dev/null"
38 #define COMMAND_NO_OUTPUT "echo -n"
bb5a9514 39#elif defined(__WINDOWS__)
ca5016d4 40 #define COMMAND "cmd.exe /c \"echo hi\""
d6655d44 41 #define COMMAND_STDERR "cmd.exe /c \"type nonexistentfile\""
ca5016d4 42 #define ASYNC_COMMAND "notepad"
561ff470
VZ
43 #define SHELL_COMMAND "echo hi > nul:"
44 #define COMMAND_NO_OUTPUT COMMAND " > nul:"
ca5016d4
FM
45#else
46 #error "no command to exec"
47#endif // OS
48
d6655d44
VZ
49#define SLEEP_END_STRING "Done sleeping"
50
5737bab7
VZ
51namespace
52{
53 enum AsyncExecLoopExitEnum
54 {
55 AsyncExec_DontExitLoop,
56 AsyncExec_ExitLoop
57 };
58} // anonymous namespace
59
ca5016d4
FM
60// ----------------------------------------------------------------------------
61// test class
62// ----------------------------------------------------------------------------
63
64class ExecTestCase : public CppUnit::TestCase
65{
66public:
67 ExecTestCase() { }
68
69private:
70 CPPUNIT_TEST_SUITE( ExecTestCase );
71 CPPUNIT_TEST( TestShell );
72 CPPUNIT_TEST( TestExecute );
73 CPPUNIT_TEST( TestProcess );
d6655d44
VZ
74 CPPUNIT_TEST( TestAsync );
75 CPPUNIT_TEST( TestAsyncRedirect );
76 CPPUNIT_TEST( TestOverlappedSyncExecute );
ca5016d4
FM
77 CPPUNIT_TEST_SUITE_END();
78
79 void TestShell();
80 void TestExecute();
81 void TestProcess();
d6655d44
VZ
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
d6655d44
VZ
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 };
ca5016d4
FM
152
153 DECLARE_NO_COPY_CLASS(ExecTestCase)
154};
155
156// register in the unnamed registry so that these tests are run by default
157CPPUNIT_TEST_SUITE_REGISTRATION( ExecTestCase );
158
e3778b4d 159// also include in its own registry so that these tests can be run alone
ca5016d4
FM
160CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ExecTestCase, "ExecTestCase" );
161
162
163void ExecTestCase::TestShell()
164{
165 CPPUNIT_ASSERT( wxShell(SHELL_COMMAND) );
166}
167
168void ExecTestCase::TestExecute()
169{
d6655d44 170 AsyncInEventLoop asyncInEventLoop;
ca5016d4
FM
171
172 // test asynch exec
d6655d44
VZ
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);
ca5016d4 182 CPPUNIT_ASSERT( pid != 0 );
3162be2b
FM
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
561ff470 187 // executed; in that case we "fall back" to the second invocation
3162be2b
FM
188 // with wxSIGKILL (which should always succeed)
189 CPPUNIT_ASSERT( wxKill(pid, wxSIGTERM) == 0 ||
190 wxKill(pid, wxSIGKILL) == 0 );
ca5016d4 191
d6655d44
VZ
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 }
ca5016d4
FM
231}
232
233void ExecTestCase::TestProcess()
234{
d6655d44
VZ
235 AsyncInEventLoop asyncInEventLoop;
236
ca5016d4
FM
237 // test wxExecute with wxProcess
238 wxProcess *proc = new wxProcess;
d6655d44
VZ
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);
ca5016d4 248 CPPUNIT_ASSERT( proc->GetPid() == pid && pid != 0 );
561ff470 249
ca5016d4
FM
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:
3162be2b
FM
253 CPPUNIT_ASSERT( wxKill(pid, wxSIGTERM) == 0 ||
254 wxKill(pid, wxSIGKILL) == 0 );
ca5016d4 255
561ff470 256
ca5016d4 257 // test wxExecute with wxProcess and REDIRECTION
561ff470 258
d6655d44
VZ
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.
298class TestAsyncProcess : public wxProcess
299{
300public:
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
311private:
312 wxDECLARE_NO_COPY_CLASS(TestAsyncProcess);
313};
561ff470 314
d6655d44
VZ
315void 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 );
ca5016d4
FM
326}
327
d6655d44
VZ
328void
329ExecTestCase::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
361void 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
371wxString 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
408wxString 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
423void 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".
431b8364 438#ifndef __WINDOWS__
d6655d44
VZ
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() );
431b8364 497#endif // !__WINDOWS__
d6655d44 498}