| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498 |
- ///////////////////////////////////////////////////////////////////////////////
- // Name: tests/exec/exec.cpp
- // Purpose: test wxExecute()
- // Author: Francesco Montorsi
- // (based on console sample TestExecute() function)
- // Created: 2009-01-10
- // Copyright: (c) 2009 Francesco Montorsi
- // (c) 2013 Rob Bresalier, Vadim Zeitlin
- // Licence: wxWindows licence
- ///////////////////////////////////////////////////////////////////////////////
- // ----------------------------------------------------------------------------
- // headers
- // ----------------------------------------------------------------------------
- #include "testprec.h"
- #ifdef __BORLANDC__
- #pragma hdrstop
- #endif
- #include "wx/utils.h"
- #include "wx/process.h"
- #include "wx/sstream.h"
- #include "wx/evtloop.h"
- #include "wx/file.h"
- #include "wx/filename.h"
- #include "wx/mstream.h"
- #include "wx/scopeguard.h"
- #include "wx/txtstrm.h"
- #include "wx/timer.h"
- #ifdef __UNIX__
- #define COMMAND "echo hi"
- #define COMMAND_STDERR "cat nonexistentfile"
- #define ASYNC_COMMAND "xclock"
- #define SHELL_COMMAND "echo hi from shell>/dev/null"
- #define COMMAND_NO_OUTPUT "echo -n"
- #elif defined(__WINDOWS__)
- #define COMMAND "cmd.exe /c \"echo hi\""
- #define COMMAND_STDERR "cmd.exe /c \"type nonexistentfile\""
- #define ASYNC_COMMAND "notepad"
- #define SHELL_COMMAND "echo hi > nul:"
- #define COMMAND_NO_OUTPUT COMMAND " > nul:"
- #else
- #error "no command to exec"
- #endif // OS
- #define SLEEP_END_STRING "Done sleeping"
- namespace
- {
- enum AsyncExecLoopExitEnum
- {
- AsyncExec_DontExitLoop,
- AsyncExec_ExitLoop
- };
- } // anonymous namespace
- // ----------------------------------------------------------------------------
- // test class
- // ----------------------------------------------------------------------------
- class ExecTestCase : public CppUnit::TestCase
- {
- public:
- ExecTestCase() { }
- private:
- CPPUNIT_TEST_SUITE( ExecTestCase );
- CPPUNIT_TEST( TestShell );
- CPPUNIT_TEST( TestExecute );
- CPPUNIT_TEST( TestProcess );
- CPPUNIT_TEST( TestAsync );
- CPPUNIT_TEST( TestAsyncRedirect );
- CPPUNIT_TEST( TestOverlappedSyncExecute );
- CPPUNIT_TEST_SUITE_END();
- void TestShell();
- void TestExecute();
- void TestProcess();
- void TestAsync();
- void TestAsyncRedirect();
- void TestOverlappedSyncExecute();
- // Helper: create an executable file sleeping for the given amount of
- // seconds with the specified base name.
- //
- // Returns the name of the file.
- static wxString CreateSleepFile(const wxString& basename, int seconds);
- // Return the full command, to be passed to wxExecute(), launching the
- // specified script file.
- static wxString MakeShellCommand(const wxString& filename);
- // Helper of TestAsyncRedirect(): tests that the output of the given
- // command on the given stream contains the expected string.
- enum CheckStream { Check_Stdout, Check_Stderr };
- void DoTestAsyncRedirect(const wxString& command,
- CheckStream check,
- const char* expectedContaining);
- // This class is used as a helper in order to run wxExecute(ASYNC)
- // inside of an event loop.
- class AsyncInEventLoop : public wxTimer
- {
- public:
- AsyncInEventLoop() { }
- long DoExecute(AsyncExecLoopExitEnum forceExitLoop_,
- const wxString& command_,
- int flags_ = wxEXEC_ASYNC,
- wxProcess* callback_ = NULL)
- {
- forceExitLoop = forceExitLoop_;
- command = command_;
- flags = flags_;
- callback = callback_;
- wxEventLoop loop;
- // Trigger the timer to go off inside the event loop
- // so that we can run wxExecute there.
- StartOnce(10);
- // Run the event loop.
- loop.Run();
- return wxExecuteReturnCode;
- }
- void Notify()
- {
- // Run wxExecute inside the event loop.
- wxExecuteReturnCode = wxExecute(command, flags, callback);
- if (forceExitLoop == AsyncExec_ExitLoop)
- {
- wxEventLoop::GetActive()->Exit();
- }
- }
- private:
- AsyncExecLoopExitEnum forceExitLoop;
- wxString command;
- int flags;
- wxProcess* callback;
- long wxExecuteReturnCode;
- };
- DECLARE_NO_COPY_CLASS(ExecTestCase)
- };
- // register in the unnamed registry so that these tests are run by default
- CPPUNIT_TEST_SUITE_REGISTRATION( ExecTestCase );
- // also include in its own registry so that these tests can be run alone
- CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ExecTestCase, "ExecTestCase" );
- void ExecTestCase::TestShell()
- {
- CPPUNIT_ASSERT( wxShell(SHELL_COMMAND) );
- }
- void ExecTestCase::TestExecute()
- {
- AsyncInEventLoop asyncInEventLoop;
- // test asynch exec
- //
- // asyncInEventLoop.DoExecute will perform the
- // call to wxExecute(ASYNC) in an event loop, as required by
- // console test (and this same event loop will also
- // be used in GUI test too, even though not required, just to have
- // common code).
- long pid = asyncInEventLoop.DoExecute(AsyncExec_ExitLoop, // Force exit of event loop right
- // after the call to wxExecute()
- ASYNC_COMMAND, wxEXEC_ASYNC);
- CPPUNIT_ASSERT( pid != 0 );
- // NOTE: under Windows the first wxKill() invocation with wxSIGTERM
- // may fail if the system is fast and the ASYNC_COMMAND app
- // doesn't manage to create its HWND before our wxKill() is
- // executed; in that case we "fall back" to the second invocation
- // with wxSIGKILL (which should always succeed)
- CPPUNIT_ASSERT( wxKill(pid, wxSIGTERM) == 0 ||
- wxKill(pid, wxSIGKILL) == 0 );
- int useNoeventsFlag;
- // Test the sync execution case with/without wxEXEC_NOEVENTS flag
- // because we use either an event loop or wxSelectDispatcher
- // depending on this flag, and we want to test both cases.
- for (useNoeventsFlag = 0; useNoeventsFlag <=1 ; ++useNoeventsFlag )
- {
- int execFlags = wxEXEC_SYNC;
- if (useNoeventsFlag)
- {
- execFlags |= wxEXEC_NOEVENTS;
- }
- // test sync exec (with a command not producing any output to avoid
- // interfering with the test):
- CPPUNIT_ASSERT( wxExecute(COMMAND_NO_OUTPUT, execFlags) == 0 );
- // test running COMMAND again, but this time with redirection:
- // and the expected data is on stdout.
- wxArrayString stdout_arr;
- CPPUNIT_ASSERT_EQUAL( 0, wxExecute(COMMAND, stdout_arr, execFlags) );
- CPPUNIT_ASSERT_EQUAL( "hi", stdout_arr[0] );
- // test running COMMAND_STDERR with redirection and the expected data
- // is on stderr.
- wxArrayString stderr_arr;
- stdout_arr.Empty();
- CPPUNIT_ASSERT( wxExecute(COMMAND_STDERR, stdout_arr, stderr_arr, execFlags) != 0 );
- // Check that there is something on stderr.
- // In Unix systems, the 'cat' command has the name of the file it could not
- // find in the error output.
- // In Windows, the 'type' command outputs the following when it can't find
- // a file:
- // "The system cannot find the file specified"
- // In both cases, we expect the word 'file' to be in the stderr.
- CPPUNIT_ASSERT( stderr_arr[0].Contains("file") );
- }
- }
- void ExecTestCase::TestProcess()
- {
- AsyncInEventLoop asyncInEventLoop;
- // test wxExecute with wxProcess
- wxProcess *proc = new wxProcess;
- // asyncInEventLoop.DoExecute will perform the
- // call to wxExecute(ASYNC) in an event loop, as required by
- // console test (and this same event loop will also
- // be used in GUI test too, even though not required, just to have
- // common code).
- long pid = asyncInEventLoop.DoExecute(AsyncExec_ExitLoop, // Force exit of event loop right
- // after the call to wxExecute()
- ASYNC_COMMAND, wxEXEC_ASYNC, proc);
- CPPUNIT_ASSERT( proc->GetPid() == pid && pid != 0 );
- // we're not going to process the wxEVT_END_PROCESS event,
- // so the proc instance will auto-delete itself after we kill
- // the asynch process:
- CPPUNIT_ASSERT( wxKill(pid, wxSIGTERM) == 0 ||
- wxKill(pid, wxSIGKILL) == 0 );
- // test wxExecute with wxProcess and REDIRECTION
- // Test the sync execution case with/without wxEXEC_NOEVENTS flag
- // because we use either an event loop or wxSelectDispatcher
- // depending on this flag, and we want to test both cases.
- // First the default case, dispatching the events while waiting.
- {
- wxProcess proc2;
- proc2.Redirect();
- CPPUNIT_ASSERT_EQUAL( 0, wxExecute(COMMAND, wxEXEC_SYNC, &proc2) );
- wxStringOutputStream procOutput;
- CPPUNIT_ASSERT( proc2.GetInputStream() );
- CPPUNIT_ASSERT_EQUAL( wxSTREAM_EOF,
- proc2.GetInputStream()->Read(procOutput).GetLastError() );
- wxString output = procOutput.GetString();
- CPPUNIT_ASSERT_EQUAL( "hi", output.Trim() );
- }
- // And now without event dispatching.
- {
- wxProcess proc2;
- proc2.Redirect();
- CPPUNIT_ASSERT_EQUAL( 0,
- wxExecute(COMMAND, wxEXEC_SYNC | wxEXEC_NOEVENTS, &proc2) );
- wxStringOutputStream procOutput;
- CPPUNIT_ASSERT( proc2.GetInputStream() );
- CPPUNIT_ASSERT_EQUAL( wxSTREAM_EOF,
- proc2.GetInputStream()->Read(procOutput).GetLastError() );
- wxString output = procOutput.GetString();
- CPPUNIT_ASSERT_EQUAL( "hi", output.Trim() );
- }
- }
- // This class exits the event loop associated with it when the child process
- // terminates.
- class TestAsyncProcess : public wxProcess
- {
- public:
- wxEXPLICIT TestAsyncProcess()
- {
- }
- // may be overridden to be notified about process termination
- virtual void OnTerminate(int WXUNUSED(pid), int WXUNUSED(status))
- {
- wxEventLoop::GetActive()->ScheduleExit();
- }
- private:
- wxDECLARE_NO_COPY_CLASS(TestAsyncProcess);
- };
- void ExecTestCase::TestAsync()
- {
- // Test asynchronous execution with no redirection, just to make sure we
- // get the OnTerminate() call.
- TestAsyncProcess proc;
- AsyncInEventLoop asyncInEventLoop;
- CPPUNIT_ASSERT( asyncInEventLoop.DoExecute(
- AsyncExec_DontExitLoop, // proc is expected (inside of its OnTerminate())
- // to trigger the exit of the event loop.
- COMMAND_NO_OUTPUT, wxEXEC_ASYNC, &proc) != 0 );
- }
- void
- ExecTestCase::DoTestAsyncRedirect(const wxString& command,
- CheckStream check,
- const char* expectedContaining)
- {
- AsyncInEventLoop asyncInEventLoop;
- TestAsyncProcess proc;
- proc.Redirect();
- CPPUNIT_ASSERT( asyncInEventLoop.DoExecute(
- AsyncExec_DontExitLoop, // proc is expected (inside of its OnTerminate())
- // to trigger the exit of the event loop.
- command, wxEXEC_ASYNC, &proc) != 0 );
- wxInputStream *streamToCheck = NULL;
- switch ( check )
- {
- case Check_Stdout:
- streamToCheck = proc.GetInputStream();
- break;
- case Check_Stderr:
- streamToCheck = proc.GetErrorStream();
- break;
- }
- wxTextInputStream tis(*streamToCheck);
- // Check that the first line of output contains what we expect.
- CPPUNIT_ASSERT( tis.ReadLine().Contains(expectedContaining) );
- }
- void ExecTestCase::TestAsyncRedirect()
- {
- // Test redirection with reading from the input stream after process termination.
- DoTestAsyncRedirect(COMMAND, Check_Stdout, "hi");
- // Test redirection with reading from the error stream after process termination.
- DoTestAsyncRedirect(COMMAND_STDERR, Check_Stderr, "file");
- }
- // static
- wxString ExecTestCase::CreateSleepFile(const wxString& basename, int seconds)
- {
- #ifdef __UNIX__
- static const char* const scriptExt = ".sh";
- // The script text is a format string with a single "%d" appearing in it
- // which will be replaced by the number of seconds to sleep below.
- static const char* const scriptText =
- "sleep %d\n"
- "echo " SLEEP_END_STRING "\n";
- #elif defined(__WINDOWS__)
- static const char* const scriptExt = ".bat";
- // Notice that we need to ping N+1 times for it to take N seconds as the
- // first ping is sent out immediately, without waiting a second.
- static const char* const scriptText =
- "@ ping 127.0.0.1 -n 1 > nul\n"
- "@ ping 127.0.0.1 -n %d > nul\n"
- "@ echo " SLEEP_END_STRING "\n";
- #else
- #error "Need code to create sleep file for this platform"
- #endif
- const wxString fnSleep = wxFileName(".", basename, scriptExt).GetFullPath();
- wxFile fileSleep;
- CPPUNIT_ASSERT
- (
- fileSleep.Create(fnSleep, true, wxS_IRUSR | wxS_IWUSR | wxS_IXUSR)
- );
- fileSleep.Write(wxString::Format(scriptText, seconds));
- return fnSleep;
- }
- // static
- wxString ExecTestCase::MakeShellCommand(const wxString& filename)
- {
- wxString command;
- #ifdef __UNIX__
- command = "/bin/sh " + filename;
- #elif defined(__WINDOWS__)
- command = wxString::Format("cmd.exe /c \"%s\"", filename);
- #else
- #error "Need to code to launch shell for this platform"
- #endif
- return command;
- }
- void ExecTestCase::TestOverlappedSyncExecute()
- {
- // Windows Synchronous wxExecute implementation does not currently
- // support overlapped event loops. It is still using wxYield, which is
- // not nestable. Therefore, this test would fail in Windows.
- // If someday somebody changes that in Windows, they could use this
- // test to verify it.
- //
- // Because MSW is not yet ready for this test, it may make sense to
- // separate it out to its own test suite, so we could register it under
- // "fixme" for Windows, but a real test for Unix. But that is more work,
- // so just #ifndefing it here for now.
- //
- // Too bad you can't just register one test case of a test suite as a
- // "fixme".
- #ifndef __WINDOWS__
- // Simple helper delaying the call to wxExecute(): instead of running it
- // immediately, it runs it when we re-enter the event loop.
- class DelayedExecuteTimer : public wxTimer
- {
- public:
- DelayedExecuteTimer(const wxString& command, wxArrayString& outputArray)
- : m_command(command),
- m_outputArray(outputArray)
- {
- // The exact delay doesn't matter, anything short enough will do.
- StartOnce(10);
- }
- virtual void Notify()
- {
- wxExecute(m_command, m_outputArray);
- }
- private:
- wxString m_command;
- wxArrayString& m_outputArray;
- };
- // Create two scripts with one of them taking longer than the other one to
- // execute.
- const wxString shortSleepFile = CreateSleepFile("shortsleep", 1);
- wxON_BLOCK_EXIT1( wxRemoveFile, shortSleepFile );
- const wxString longSleepFile = CreateSleepFile("longsleep", 2);
- wxON_BLOCK_EXIT1( wxRemoveFile, longSleepFile );
- const wxString shortSleepCommand = MakeShellCommand(shortSleepFile);
- const wxString longSleepCommand = MakeShellCommand(longSleepFile);
- // Collect the child process output
- wxArrayString shortSleepOutput,
- longSleepOutput;
- // Test that launching a process taking a longer time to run while the
- // shorter process is running works, i.e. that our outer wxExecute()
- // doesn't return until both process terminate.
- DelayedExecuteTimer delayLongSleep(longSleepCommand, longSleepOutput);
- wxExecute(shortSleepCommand, shortSleepOutput);
- CPPUNIT_ASSERT( !shortSleepOutput.empty() );
- CPPUNIT_ASSERT_EQUAL( SLEEP_END_STRING, shortSleepOutput.Last() );
- CPPUNIT_ASSERT( !longSleepOutput.empty() );
- CPPUNIT_ASSERT_EQUAL( SLEEP_END_STRING, longSleepOutput.Last() );
- // And also that, vice versa, running a short-lived child process that both
- // starts and ends while a longer-lived parent process is still running
- // works too.
- DelayedExecuteTimer delayShortSleep(shortSleepCommand, shortSleepOutput);
- wxExecute(longSleepCommand, longSleepOutput);
- CPPUNIT_ASSERT( !shortSleepOutput.empty() );
- CPPUNIT_ASSERT_EQUAL( SLEEP_END_STRING, shortSleepOutput.Last() );
- CPPUNIT_ASSERT( !longSleepOutput.empty() );
- CPPUNIT_ASSERT_EQUAL( SLEEP_END_STRING, longSleepOutput.Last() );
- #endif // !__WINDOWS__
- }
|