exec.cpp 16 KB

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