From d1a178a43cb62137f2ebe4262ef1b740df2e140c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 18 Dec 2015 21:16:23 +0100 Subject: [PATCH] [Process] More robustness and deterministic tests --- src/Symfony/Component/Process/PhpProcess.php | 6 + src/Symfony/Component/Process/Process.php | 89 ++++--- .../Process/Tests/NonStopableProcess.php | 8 +- .../Component/Process/Tests/ProcessTest.php | 217 ++++++++---------- 4 files changed, 147 insertions(+), 173 deletions(-) diff --git a/src/Symfony/Component/Process/PhpProcess.php b/src/Symfony/Component/Process/PhpProcess.php index 4a2a2625ff..42ecb66f2b 100644 --- a/src/Symfony/Component/Process/PhpProcess.php +++ b/src/Symfony/Component/Process/PhpProcess.php @@ -46,6 +46,12 @@ class PhpProcess extends Process $php .= ' '.ProcessUtils::escapeArgument($file); $script = null; } + if ('\\' !== DIRECTORY_SEPARATOR && null !== $php) { + // exec is mandatory to deal with sending a signal to the process + // see https://github.com/symfony/symfony/issues/5030 about prepending + // command with exec + $php = 'exec '.$php; + } parent::__construct($php, $cwd, $env, $script, $timeout, $options); } diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index d2af621d43..636ddb9513 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -66,12 +66,12 @@ class Process private static $sigchild; private static $posixSignals = array( - 1 => 1, // SIGHUP - 2 => 2, // SIGINT - 3 => 3, // SIGQUIT - 6 => 6, // SIGABRT - 14 => 14, // SIGALRM - 15 => 15, // SIGTERM + 1, // SIGHUP + 2, // SIGINT + 3, // SIGQUIT + 6, // SIGABRT + 14, // SIGALRM + 15, // SIGTERM ); /** @@ -242,11 +242,19 @@ class Process if (!isset($this->options['bypass_shell'])) { $this->options['bypass_shell'] = true; } - } + } elseif (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { + // last exit code is output on the fourth pipe and caught to work around --enable-sigchild + $descriptors[3] = array('pipe', 'w'); - $ptsWorkaround = null; + $commandline = ''; + foreach (self::$posixSignals as $s) { + $commandline .= "trap 'echo s$s >&3' $s;"; + } + + // See https://unix.stackexchange.com/questions/71205/background-process-pipe-input + $commandline .= '{ ('.$this->commandline.') <&3 3<&- 3>/dev/null & } 3<&0;'; + $commandline .= 'pid=$!; echo $pid >&3; wait $pid; code=$?; echo x$code >&3; exit $code'; - if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { // Workaround for the bug, when PTS functionality is enabled. // @see : https://bugs.php.net/69442 $ptsWorkaround = fopen(__FILE__, 'r'); @@ -254,15 +262,14 @@ class Process $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options); - if ($ptsWorkaround) { - fclose($ptsWorkaround); - } - if (!is_resource($this->process)) { throw new RuntimeException('Unable to launch a new process.'); } $this->status = self::STATUS_STARTED; + if (isset($descriptors[3])) { + $this->fallbackStatus['pid'] = (int) fgets($this->processPipes->pipes[3]); + } $this->processPipes->unblock(); if ($this->tty) { @@ -468,8 +475,6 @@ class Process public function getExitCode() { if (!$this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { - $this->stop(0); - throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.'); } @@ -523,8 +528,6 @@ class Process $this->requireProcessIsTerminated(__FUNCTION__); if (!$this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { - $this->stop(0); - throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); } @@ -546,8 +549,6 @@ class Process $this->requireProcessIsTerminated(__FUNCTION__); if ($this->isSigchildEnabled() && (!$this->enhanceSigchildCompatibility || -1 === $this->processInformation['termsig'])) { - $this->stop(0); - throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); } @@ -990,22 +991,8 @@ class Process private function getDescriptors() { $this->processPipes = new ProcessPipes($this->useFileHandles, $this->tty); - $descriptors = $this->processPipes->getDescriptors(); - if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { - // last exit code is output on the fourth pipe and caught to work around --enable-sigchild - $descriptors[3] = array('pipe', 'w'); - - $trap = ''; - foreach (self::$posixSignals as $s) { - $trap .= "trap 'echo s$s >&3' $s;"; - } - - $this->commandline = $trap.'{ ('.$this->commandline.') <&3 3<&- 3>/dev/null & } 3<&0;'; - $this->commandline .= 'pid=$!; echo p$pid >&3; wait $pid; code=$?; echo x$code >&3; exit $code'; - } - - return $descriptors; + return $this->processPipes->getDescriptors(); } /** @@ -1106,8 +1093,11 @@ class Process $this->fallbackStatus['signaled'] = true; $this->fallbackStatus['exitcode'] = -1; $this->fallbackStatus['termsig'] = (int) substr($data, 1); - } elseif ('x' === $data[0] && !isset($this->fallbackStatus['signaled'])) { - $this->fallbackStatus['exitcode'] = (int) substr($data, 1); + } elseif ('x' === $data[0]) { + $this->fallbackStatus['running'] = false; + if (!isset($this->fallbackStatus['signaled'])) { + $this->fallbackStatus['exitcode'] = (int) substr($data, 1); + } } } } else { @@ -1189,14 +1179,6 @@ class Process return false; } - if ($this->enhanceSigchildCompatibility && $this->isSigchildEnabled() && !isset(self::$posixSignals[$signal]) && !(function_exists('posix_kill') && @posix_kill($this->getPid(), $signal))) { - if ($throwException) { - throw new RuntimeException('This PHP has been compiled with --enable-sigchild and posix_kill() is not available.'); - } - - return false; - } - if ('\\' === DIRECTORY_SEPARATOR) { exec(sprintf('taskkill /F /T /PID %d 2>&1', $this->getPid()), $output, $exitCode); if ($exitCode && $this->isRunning()) { @@ -1206,14 +1188,21 @@ class Process return false; } - } - - if (true !== @proc_terminate($this->process, $signal) && '\\' !== DIRECTORY_SEPARATOR) { - if ($throwException) { - throw new RuntimeException(sprintf('Error while sending signal `%s`.', $signal)); + } else { + if (!$this->enhanceSigchildCompatibility || !$this->isSigchildEnabled()) { + $ok = @proc_terminate($this->process, $signal); + } elseif (function_exists('posix_kill')) { + $ok = @posix_kill($this->getPid(), $signal); + } elseif ($ok = proc_open(sprintf('kill -%d %d', $signal, $this->getPid()), array(2 => array('pipe', 'w')), $pipes)) { + $ok = false === fgets($pipes[2]); } + if (!$ok) { + if ($throwException) { + throw new RuntimeException(sprintf('Error while sending signal `%s`.', $signal)); + } - return false; + return false; + } } $this->latestSignal = (int) $signal; diff --git a/src/Symfony/Component/Process/Tests/NonStopableProcess.php b/src/Symfony/Component/Process/Tests/NonStopableProcess.php index 54510c16a3..5643259657 100644 --- a/src/Symfony/Component/Process/Tests/NonStopableProcess.php +++ b/src/Symfony/Component/Process/Tests/NonStopableProcess.php @@ -30,16 +30,18 @@ function handleSignal($signal) break; } - echo "received signal $name\n"; + echo "signal $name\n"; } -declare (ticks = 1); pcntl_signal(SIGTERM, 'handleSignal'); pcntl_signal(SIGINT, 'handleSignal'); +echo 'received '; + $duration = isset($argv[1]) ? (int) $argv[1] : 3; $start = microtime(true); while ($duration > (microtime(true) - $start)) { - usleep(1000); + usleep(10000); + pcntl_signal_dispatch(); } diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php index a49ededd7d..4b1d4d640b 100644 --- a/src/Symfony/Component/Process/Tests/ProcessTest.php +++ b/src/Symfony/Component/Process/Tests/ProcessTest.php @@ -23,12 +23,23 @@ use Symfony\Component\Process\ProcessPipes; class ProcessTest extends \PHPUnit_Framework_TestCase { private static $phpBin; + private static $sigchild; private static $notEnhancedSigchild = false; public static function setUpBeforeClass() { $phpBin = new PhpExecutableFinder(); self::$phpBin = 'phpdbg' === PHP_SAPI ? 'php' : $phpBin->find(); + if ('\\' !== DIRECTORY_SEPARATOR) { + // exec is mandatory to deal with sending a signal to the process + // see https://github.com/symfony/symfony/issues/5030 about prepending + // command with exec + self::$phpBin = 'exec '.self::$phpBin; + } + + ob_start(); + phpinfo(INFO_GENERAL); + self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild'); } public function testThatProcessDoesNotThrowWarningDuringRun() @@ -77,19 +88,18 @@ class ProcessTest extends \PHPUnit_Framework_TestCase */ public function testStopWithTimeoutIsActuallyWorking() { - // exec is mandatory here since we send a signal to the process - // see https://github.com/symfony/symfony/issues/5030 about prepending - // command with exec - $p = $this->getProcess('exec '.self::$phpBin.' '.__DIR__.'/NonStopableProcess.php 3'); + $p = $this->getProcess(self::$phpBin.' '.__DIR__.'/NonStopableProcess.php 30'); $p->start(); - usleep(100000); - $start = microtime(true); - $p->stop(1.1, SIGKILL); - while ($p->isRunning()) { + + while (false === strpos($p->getOutput(), 'received')) { usleep(1000); } + $start = microtime(true); + $p->stop(0.1); - $this->assertLessThan(4, microtime(true) - $start); + $p->wait(); + + $this->assertLessThan(15, microtime(true) - $start); } public function testAllOutputIsActuallyReadOnTermination() @@ -107,12 +117,16 @@ class ProcessTest extends \PHPUnit_Framework_TestCase $p = $this->getProcess(sprintf('%s -r %s', self::$phpBin, escapeshellarg($code))); $p->start(); - // Let's wait enough time for process to finish... - // Here we don't call Process::run or Process::wait to avoid any read of pipes - usleep(500000); - if ($p->isRunning()) { - $this->markTestSkipped('Process execution did not complete in the required time frame'); + // Don't call Process::run nor Process::wait to avoid any read of pipes + $h = new \ReflectionProperty($p, 'process'); + $h->setAccessible(true); + $h = $h->getValue($p); + $s = proc_get_status($h); + + while ($s['running']) { + usleep(1000); + $s = proc_get_status($h); } $o = $p->getOutput(); @@ -122,18 +136,14 @@ class ProcessTest extends \PHPUnit_Framework_TestCase public function testCallbacksAreExecutedWithStart() { - $data = ''; - - $process = $this->getProcess('echo foo && '.self::$phpBin.' -r "sleep(1);" && echo foo'); + $process = $this->getProcess('echo foo'); $process->start(function ($type, $buffer) use (&$data) { $data .= $buffer; }); - while ($process->isRunning()) { - usleep(10000); - } + $process->wait(); - $this->assertEquals(2, preg_match_all('/foo/', $data, $matches)); + $this->assertSame('foo'.PHP_EOL, $data); } /** @@ -173,7 +183,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase */ public function testSetStdinWhileRunningThrowsAnException() { - $process = $this->getProcess(self::$phpBin.' -r "usleep(500000);"'); + $process = $this->getProcess(self::$phpBin.' -r "sleep(30);"'); $process->start(); try { $process->setStdin('foobar'); @@ -193,7 +203,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase */ public function testInvalidStdin($value) { - $process = $this->getProcess(self::$phpBin.' -v'); + $process = $this->getProcess('foo'); $process->setStdin($value); } @@ -211,7 +221,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase */ public function testValidStdin($expected, $value) { - $process = $this->getProcess(self::$phpBin.' -v'); + $process = $this->getProcess('foo'); $process->setStdin($value); $this->assertSame($expected, $process->getStdin()); } @@ -413,7 +423,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase public function testTTYCommand() { if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('Windows does have /dev/tty support'); + $this->markTestSkipped('Windows does not have /dev/tty support'); } $process = $this->getProcess('echo "foo" >> /dev/null && '.self::$phpBin.' -r "usleep(100000);"'); @@ -481,13 +491,13 @@ class ProcessTest extends \PHPUnit_Framework_TestCase $start = microtime(true); $process->start(); $end = microtime(true); - $this->assertLessThan(1, $end - $start); - $process->wait(); + $this->assertLessThan(0.4, $end - $start); + $process->stop(); } public function testUpdateStatus() { - $process = $this->getProcess(self::$phpBin.' -v'); + $process = $this->getProcess('echo foo'); $process->run(); $this->assertTrue(strlen($process->getOutput()) > 0); } @@ -496,7 +506,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase { $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess(self::$phpBin.' -r "usleep(200000);"'); + $process = $this->getProcess(self::$phpBin.' -r "usleep(100000);"'); $this->assertNull($process->getExitCode()); $process->start(); $this->assertNull($process->getExitCode()); @@ -508,7 +518,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase { $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess(self::$phpBin.' -r "usleep(200000);"'); + $process = $this->getProcess(self::$phpBin.' -r "usleep(100000);"'); $process->run(); $this->assertEquals(0, $process->getExitCode()); $process->start(); @@ -521,14 +531,14 @@ class ProcessTest extends \PHPUnit_Framework_TestCase { $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess(self::$phpBin.' -v'); + $process = $this->getProcess('echo foo'); $process->run(); $this->assertSame(0, $process->getExitCode()); } public function testStatus() { - $process = $this->getProcess(self::$phpBin.' -r "usleep(500000);"'); + $process = $this->getProcess(self::$phpBin.' -r "usleep(100000);"'); $this->assertFalse($process->isRunning()); $this->assertFalse($process->isStarted()); $this->assertFalse($process->isTerminated()); @@ -547,7 +557,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase public function testStop() { - $process = $this->getProcess(self::$phpBin.' -r "sleep(4);"'); + $process = $this->getProcess(self::$phpBin.' -r "sleep(31);"'); $process->start(); $this->assertTrue($process->isRunning()); $process->stop(); @@ -558,7 +568,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase { $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess(self::$phpBin.' -v'); + $process = $this->getProcess('echo foo'); $process->run(); $this->assertTrue($process->isSuccessful()); } @@ -567,14 +577,12 @@ class ProcessTest extends \PHPUnit_Framework_TestCase { $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess(self::$phpBin.' -r "sleep(1);"'); + $process = $this->getProcess(self::$phpBin.' -r "usleep(100000);"'); $process->start(); $this->assertFalse($process->isSuccessful()); - while ($process->isRunning()) { - usleep(300000); - } + $process->wait(); $this->assertTrue($process->isSuccessful()); } @@ -583,10 +591,8 @@ class ProcessTest extends \PHPUnit_Framework_TestCase { $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess(self::$phpBin.' -r "usleep(500000);throw new \Exception(\'BOUM\');"'); - $process->start(); - $this->assertTrue($process->isRunning()); - $process->wait(); + $process = $this->getProcess(self::$phpBin.' -r "throw new \Exception(\'BOUM\');"'); + $process->run(); $this->assertFalse($process->isSuccessful()); } @@ -597,19 +603,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase } $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess(self::$phpBin.' -v'); - $process->run(); - $this->assertFalse($process->hasBeenSignaled()); - } - - public function testProcessWithoutTermSignalIsNotSignaled() - { - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('Windows does not support POSIX signals'); - } - $this->skipIfNotEnhancedSigchild(); - - $process = $this->getProcess(self::$phpBin.' -v'); + $process = $this->getProcess('echo foo'); $process->run(); $this->assertFalse($process->hasBeenSignaled()); } @@ -621,7 +615,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase } $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess(self::$phpBin.' -v'); + $process = $this->getProcess('echo foo'); $process->run(); $this->assertEquals(0, $process->getTermSignal()); } @@ -633,23 +627,10 @@ class ProcessTest extends \PHPUnit_Framework_TestCase } $this->skipIfNotEnhancedSigchild(); - $process = $this->getProcess(self::$phpBin.' -r "sleep(4);"'); + $process = $this->getProcess(self::$phpBin.' -r "sleep(32);"'); $process->start(); $process->stop(); $this->assertTrue($process->hasBeenSignaled()); - } - - public function testProcessWithTermSignal() - { - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('Windows does not support POSIX signals'); - } - $this->skipIfNotEnhancedSigchild(); - - $process = $this->getProcess(self::$phpBin.' -r "sleep(4);"'); - $process->start(); - $process->stop(); - $this->assertEquals(15, $process->getTermSignal()); // SIGTERM } @@ -664,11 +645,9 @@ class ProcessTest extends \PHPUnit_Framework_TestCase } $this->skipIfNotEnhancedSigchild(false); - $termSignal = defined('SIGKILL') ? SIGKILL : 9; - - $process = $this->getProcess('exec '.self::$phpBin.' -r "while (true) {}"'); + $process = $this->getProcess(self::$phpBin.' -r "while (true) usleep(100);"'); $process->start(); - posix_kill($process->getPid(), $termSignal); + posix_kill($process->getPid(), 9); // SIGKILL $process->wait(); } @@ -697,8 +676,8 @@ class ProcessTest extends \PHPUnit_Framework_TestCase */ public function testRunProcessWithTimeout() { - $timeout = 0.5; - $process = $this->getProcess(self::$phpBin.' -r "usleep(600000);"'); + $timeout = 0.1; + $process = $this->getProcess(self::$phpBin.' -r "sleep(1);"'); $process->setTimeout($timeout); $start = microtime(true); try { @@ -719,15 +698,15 @@ class ProcessTest extends \PHPUnit_Framework_TestCase public function testCheckTimeoutOnNonStartedProcess() { - $process = $this->getProcess(self::$phpBin.' -r "sleep(3);"'); - $process->checkTimeout(); + $process = $this->getProcess('echo foo'); + $this->assertNull($process->checkTimeout()); } public function testCheckTimeoutOnTerminatedProcess() { - $process = $this->getProcess(self::$phpBin.' -v'); + $process = $this->getProcess('echo foo'); $process->run(); - $process->checkTimeout(); + $this->assertNull($process->checkTimeout()); } /** @@ -736,8 +715,8 @@ class ProcessTest extends \PHPUnit_Framework_TestCase */ public function testCheckTimeoutOnStartedProcess() { - $process = $this->getProcess(self::$phpBin.' -r "sleep(3);"'); - $process->setTimeout(0.5); + $process = $this->getProcess(self::$phpBin.' -r "sleep(33);"'); + $process->setTimeout(0.1); $process->start(); $start = microtime(true); @@ -763,37 +742,37 @@ class ProcessTest extends \PHPUnit_Framework_TestCase */ public function testStartAfterATimeout() { - $process = $this->getProcess(sprintf('%s -r %s', self::$phpBin, escapeshellarg('$n = 1000; while ($n--) {echo \'\'; usleep(1000); }'))); + $process = $this->getProcess(self::$phpBin.' -r "sleep(35);"'); $process->setTimeout(0.1); try { $process->run(); $this->fail('A RuntimeException should have been raised.'); } catch (RuntimeException $e) { } + $this->assertFalse($process->isRunning()); $process->start(); - usleep(10000); - $process->stop(); + $process->stop(0); throw $e; } public function testGetPid() { - $process = $this->getProcess(self::$phpBin.' -r "usleep(500000);"'); + $process = $this->getProcess(self::$phpBin.' -r "sleep(36);"'); $process->start(); $this->assertGreaterThan(0, $process->getPid()); - $process->wait(); + $process->stop(0); } public function testGetPidIsNullBeforeStart() { - $process = $this->getProcess(self::$phpBin.' -r "sleep(1);"'); + $process = $this->getProcess('foo'); $this->assertNull($process->getPid()); } public function testGetPidIsNullAfterRun() { - $process = $this->getProcess(self::$phpBin.' -v'); + $process = $this->getProcess('echo foo'); $process->run(); $this->assertNull($process->getPid()); } @@ -803,7 +782,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase */ public function testSignal() { - $process = $this->getProcess('exec '.self::$phpBin.' '.__DIR__.'/SignalListener.php'); + $process = $this->getProcess(self::$phpBin.' '.__DIR__.'/SignalListener.php'); $process->start(); while (false === strpos($process->getOutput(), 'Caught')) { @@ -842,7 +821,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase */ public function testSignalProcessNotRunning() { - $process = $this->getProcess(self::$phpBin.' -v'); + $process = $this->getProcess('foo'); $process->signal(1); // SIGHUP } @@ -851,7 +830,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase */ public function testMethodsThatNeedARunningProcess($method) { - $process = $this->getProcess(self::$phpBin.' -v'); + $process = $this->getProcess('foo'); $this->setExpectedException('Symfony\Component\Process\Exception\LogicException', sprintf('Process must be started before calling %s.', $method)); $process->{$method}(); } @@ -874,7 +853,7 @@ class ProcessTest extends \PHPUnit_Framework_TestCase */ public function testMethodsThatNeedATerminatedProcess($method) { - $process = $this->getProcess(self::$phpBin.' -r "sleep(1);"'); + $process = $this->getProcess(self::$phpBin.' -r "sleep(37);"'); $process->start(); try { $process->{$method}(); @@ -898,36 +877,38 @@ class ProcessTest extends \PHPUnit_Framework_TestCase } /** + * @dataProvider provideWrongSignal * @expectedException \Symfony\Component\Process\Exception\RuntimeException */ - public function testSignalWithWrongIntSignal() + public function testWrongSignal($signal) { if ('\\' === DIRECTORY_SEPARATOR) { $this->markTestSkipped('POSIX signals do not work on Windows'); } - $process = $this->getProcess(self::$phpBin.' -r "sleep(3);"'); + $process = $this->getProcess(self::$phpBin.' -r "sleep(38);"'); $process->start(); - $process->signal(-4); + try { + $process->signal($signal); + $this->fail('A RuntimeException must have been thrown'); + } catch (RuntimeException $e) { + $process->stop(0); + } + + throw $e; } - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - */ - public function testSignalWithWrongNonIntSignal() + public function provideWrongSignal() { - if ('\\' === DIRECTORY_SEPARATOR) { - $this->markTestSkipped('POSIX signals do not work on Windows'); - } - - $process = $this->getProcess(self::$phpBin.' -r "sleep(3);"'); - $process->start(); - $process->signal('Céphalopodes'); + return array( + array(-4), + array('Céphalopodes'), + ); } public function testStopTerminatesProcessCleanly() { - $process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"'); + $process = $this->getProcess(self::$phpBin.' -r "echo 123; sleep(42);"'); $process->run(function () use ($process) { $process->stop(); }); @@ -936,22 +917,18 @@ class ProcessTest extends \PHPUnit_Framework_TestCase public function testKillSignalTerminatesProcessCleanly() { - $process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"'); + $process = $this->getProcess(self::$phpBin.' -r "echo 123; sleep(43);"'); $process->run(function () use ($process) { - if ($process->isRunning()) { - $process->signal(9); // SIGKILL - } + $process->signal(9); // SIGKILL }); $this->assertTrue(true, 'A call to signal() is not expected to cause wait() to throw a RuntimeException'); } public function testTermSignalTerminatesProcessCleanly() { - $process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"'); + $process = $this->getProcess(self::$phpBin.' -r "echo 123; sleep(44);"'); $process->run(function () use ($process) { - if ($process->isRunning()) { - $process->signal(15); // SIGTERM - } + $process->signal(15); // SIGTERM }); $this->assertTrue(true, 'A call to signal() is not expected to cause wait() to throw a RuntimeException'); } @@ -1041,11 +1018,11 @@ class ProcessTest extends \PHPUnit_Framework_TestCase private function skipIfNotEnhancedSigchild($expectException = true) { - if (self::$notEnhancedSigchild) { - if ($expectException) { + if (self::$sigchild) { + if (!$expectException) { + $this->markTestSkipped('PHP is compiled with --enable-sigchild.'); + } elseif (self::$notEnhancedSigchild) { $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild.'); - } else { - $this->markTestSkipped('PHP is compiled with --enable-sigchild and enhanced mode is disabled.'); } } }