PageRenderTime 55ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/symfony/symfony/src/Symfony/Component/Process/Tests/AbstractProcessTest.php

https://bitbucket.org/gruenwaldt/loquitur-web
PHP | 660 lines | 482 code | 111 blank | 67 comment | 25 complexity | b6f8e56d98b5611ea4c7ed2d9de9efcd MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Process\Tests;
  11. use Symfony\Component\Process\Process;
  12. use Symfony\Component\Process\Exception\RuntimeException;
  13. /**
  14. * @author Robert SchĂśnthal <seroscho@googlemail.com>
  15. */
  16. abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase
  17. {
  18. /**
  19. * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
  20. */
  21. public function testNegativeTimeoutFromConstructor()
  22. {
  23. $this->getProcess('', null, null, null, -1);
  24. }
  25. /**
  26. * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException
  27. */
  28. public function testNegativeTimeoutFromSetter()
  29. {
  30. $p = $this->getProcess('');
  31. $p->setTimeout(-1);
  32. }
  33. public function testNullTimeout()
  34. {
  35. $p = $this->getProcess('');
  36. $p->setTimeout(10);
  37. $p->setTimeout(null);
  38. $this->assertNull($p->getTimeout());
  39. }
  40. public function testStopWithTimeoutIsActuallyWorking()
  41. {
  42. $this->verifyPosixIsEnabled();
  43. // exec is mandatory here since we send a signal to the process
  44. // see https://github.com/symfony/symfony/issues/5030 about prepending
  45. // command with exec
  46. $p = $this->getProcess('exec php '.__DIR__.'/NonStopableProcess.php 3');
  47. $p->start();
  48. usleep(100000);
  49. $start = microtime(true);
  50. $p->stop(1.1, SIGKILL);
  51. while ($p->isRunning()) {
  52. usleep(1000);
  53. }
  54. $duration = microtime(true) - $start;
  55. $this->assertLessThan(1.8, $duration);
  56. }
  57. public function testCallbacksAreExecutedWithStart()
  58. {
  59. $data = '';
  60. $process = $this->getProcess('echo foo && php -r "sleep(1);" && echo foo');
  61. $process->start(function ($type, $buffer) use (&$data) {
  62. $data .= $buffer;
  63. });
  64. while ($process->isRunning()) {
  65. usleep(10000);
  66. }
  67. $this->assertEquals(2, preg_match_all('/foo/', $data, $matches));
  68. }
  69. /**
  70. * tests results from sub processes
  71. *
  72. * @dataProvider responsesCodeProvider
  73. */
  74. public function testProcessResponses($expected, $getter, $code)
  75. {
  76. $p = $this->getProcess(sprintf('php -r %s', escapeshellarg($code)));
  77. $p->run();
  78. $this->assertSame($expected, $p->$getter());
  79. }
  80. /**
  81. * tests results from sub processes
  82. *
  83. * @dataProvider pipesCodeProvider
  84. */
  85. public function testProcessPipes($code, $size)
  86. {
  87. $expected = str_repeat(str_repeat('*', 1024), $size) . '!';
  88. $expectedLength = (1024 * $size) + 1;
  89. $p = $this->getProcess(sprintf('php -r %s', escapeshellarg($code)));
  90. $p->setStdin($expected);
  91. $p->run();
  92. $this->assertEquals($expectedLength, strlen($p->getOutput()));
  93. $this->assertEquals($expectedLength, strlen($p->getErrorOutput()));
  94. }
  95. public function chainedCommandsOutputProvider()
  96. {
  97. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  98. return array(
  99. array("2 \r\n2\r\n", '&&', '2')
  100. );
  101. }
  102. return array(
  103. array("1\n1\n", ';', '1'),
  104. array("2\n2\n", '&&', '2'),
  105. );
  106. }
  107. /**
  108. *
  109. * @dataProvider chainedCommandsOutputProvider
  110. */
  111. public function testChainedCommandsOutput($expected, $operator, $input)
  112. {
  113. $process = $this->getProcess(sprintf('echo %s %s echo %s', $input, $operator, $input));
  114. $process->run();
  115. $this->assertEquals($expected, $process->getOutput());
  116. }
  117. public function testCallbackIsExecutedForOutput()
  118. {
  119. $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('echo \'foo\';')));
  120. $called = false;
  121. $p->run(function ($type, $buffer) use (&$called) {
  122. $called = $buffer === 'foo';
  123. });
  124. $this->assertTrue($called, 'The callback should be executed with the output');
  125. }
  126. public function testGetErrorOutput()
  127. {
  128. $p = new Process(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
  129. $p->run();
  130. $this->assertEquals(3, preg_match_all('/ERROR/', $p->getErrorOutput(), $matches));
  131. }
  132. public function testGetIncrementalErrorOutput()
  133. {
  134. $p = new Process(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { usleep(50000); file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }')));
  135. $p->start();
  136. while ($p->isRunning()) {
  137. $this->assertLessThanOrEqual(1, preg_match_all('/ERROR/', $p->getIncrementalErrorOutput(), $matches));
  138. usleep(20000);
  139. }
  140. }
  141. public function testGetOutput()
  142. {
  143. $p = new Process(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++; usleep(500); }')));
  144. $p->run();
  145. $this->assertEquals(3, preg_match_all('/foo/', $p->getOutput(), $matches));
  146. }
  147. public function testGetIncrementalOutput()
  148. {
  149. $p = new Process(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) { echo \' foo \'; usleep(50000); $n++; }')));
  150. $p->start();
  151. while ($p->isRunning()) {
  152. $this->assertLessThanOrEqual(1, preg_match_all('/foo/', $p->getIncrementalOutput(), $matches));
  153. usleep(20000);
  154. }
  155. }
  156. public function testExitCodeCommandFailed()
  157. {
  158. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  159. $this->markTestSkipped('Windows does not support POSIX exit code');
  160. }
  161. // such command run in bash return an exitcode 127
  162. $process = $this->getProcess('nonexistingcommandIhopeneversomeonewouldnameacommandlikethis');
  163. $process->run();
  164. $this->assertGreaterThan(0, $process->getExitCode());
  165. }
  166. public function testTTYCommand()
  167. {
  168. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  169. $this->markTestSkipped('Windows does have /dev/tty support');
  170. }
  171. $process = $this->getProcess('echo "foo" >> /dev/null');
  172. $process->setTTY(true);
  173. $process->run();
  174. $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
  175. }
  176. public function testExitCodeText()
  177. {
  178. $process = $this->getProcess('');
  179. $r = new \ReflectionObject($process);
  180. $p = $r->getProperty('exitcode');
  181. $p->setAccessible(true);
  182. $p->setValue($process, 2);
  183. $this->assertEquals('Misuse of shell builtins', $process->getExitCodeText());
  184. }
  185. public function testStartIsNonBlocking()
  186. {
  187. $process = $this->getProcess('php -r "sleep(4);"');
  188. $start = microtime(true);
  189. $process->start();
  190. $end = microtime(true);
  191. $this->assertLessThan(1 , $end-$start);
  192. }
  193. public function testUpdateStatus()
  194. {
  195. $process = $this->getProcess('php -h');
  196. $process->run();
  197. $this->assertTrue(strlen($process->getOutput()) > 0);
  198. }
  199. public function testGetExitCodeIsNullOnStart()
  200. {
  201. $process = $this->getProcess('php -r "usleep(200000);"');
  202. $this->assertNull($process->getExitCode());
  203. $process->start();
  204. $this->assertNull($process->getExitCode());
  205. $process->wait();
  206. $this->assertEquals(0, $process->getExitCode());
  207. }
  208. public function testGetExitCodeIsNullOnWhenStartingAgain()
  209. {
  210. $process = $this->getProcess('php -r "usleep(200000);"');
  211. $process->run();
  212. $this->assertEquals(0, $process->getExitCode());
  213. $process->start();
  214. $this->assertNull($process->getExitCode());
  215. $process->wait();
  216. $this->assertEquals(0, $process->getExitCode());
  217. }
  218. public function testGetExitCode()
  219. {
  220. $process = $this->getProcess('php -m');
  221. $process->run();
  222. $this->assertEquals(0, $process->getExitCode());
  223. }
  224. public function testStatus()
  225. {
  226. $process = $this->getProcess('php -r "usleep(500000);"');
  227. $this->assertFalse($process->isRunning());
  228. $this->assertFalse($process->isStarted());
  229. $this->assertFalse($process->isTerminated());
  230. $this->assertSame(Process::STATUS_READY, $process->getStatus());
  231. $process->start();
  232. $this->assertTrue($process->isRunning());
  233. $this->assertTrue($process->isStarted());
  234. $this->assertFalse($process->isTerminated());
  235. $this->assertSame(Process::STATUS_STARTED, $process->getStatus());
  236. $process->wait();
  237. $this->assertFalse($process->isRunning());
  238. $this->assertTrue($process->isStarted());
  239. $this->assertTrue($process->isTerminated());
  240. $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus());
  241. }
  242. public function testStop()
  243. {
  244. $process = $this->getProcess('php -r "sleep(4);"');
  245. $process->start();
  246. $this->assertTrue($process->isRunning());
  247. $process->stop();
  248. $this->assertFalse($process->isRunning());
  249. }
  250. public function testIsSuccessful()
  251. {
  252. $process = $this->getProcess('php -m');
  253. $process->run();
  254. $this->assertTrue($process->isSuccessful());
  255. }
  256. public function testIsSuccessfulOnlyAfterTerminated()
  257. {
  258. $process = $this->getProcess('php -r "sleep(1);"');
  259. $process->start();
  260. while ($process->isRunning()) {
  261. $this->assertFalse($process->isSuccessful());
  262. usleep(300000);
  263. }
  264. $this->assertTrue($process->isSuccessful());
  265. }
  266. public function testIsNotSuccessful()
  267. {
  268. $process = $this->getProcess('php -r "sleep(4);"');
  269. $process->start();
  270. $this->assertTrue($process->isRunning());
  271. $process->stop();
  272. $this->assertFalse($process->isSuccessful());
  273. }
  274. public function testProcessIsNotSignaled()
  275. {
  276. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  277. $this->markTestSkipped('Windows does not support POSIX signals');
  278. }
  279. $process = $this->getProcess('php -m');
  280. $process->run();
  281. $this->assertFalse($process->hasBeenSignaled());
  282. }
  283. public function testProcessWithoutTermSignalIsNotSignaled()
  284. {
  285. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  286. $this->markTestSkipped('Windows does not support POSIX signals');
  287. }
  288. $process = $this->getProcess('php -m');
  289. $process->run();
  290. $this->assertFalse($process->hasBeenSignaled());
  291. }
  292. public function testProcessWithoutTermSignal()
  293. {
  294. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  295. $this->markTestSkipped('Windows does not support POSIX signals');
  296. }
  297. $process = $this->getProcess('php -m');
  298. $process->run();
  299. $this->assertEquals(0, $process->getTermSignal());
  300. }
  301. public function testProcessIsSignaledIfStopped()
  302. {
  303. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  304. $this->markTestSkipped('Windows does not support POSIX signals');
  305. }
  306. $process = $this->getProcess('php -r "sleep(4);"');
  307. $process->start();
  308. $process->stop();
  309. $this->assertTrue($process->hasBeenSignaled());
  310. }
  311. public function testProcessWithTermSignal()
  312. {
  313. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  314. $this->markTestSkipped('Windows does not support POSIX signals');
  315. }
  316. // SIGTERM is only defined if pcntl extension is present
  317. $termSignal = defined('SIGTERM') ? SIGTERM : 15;
  318. $process = $this->getProcess('php -r "sleep(4);"');
  319. $process->start();
  320. $process->stop();
  321. $this->assertEquals($termSignal, $process->getTermSignal());
  322. }
  323. public function testProcessThrowsExceptionWhenExternallySignaled()
  324. {
  325. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  326. $this->markTestSkipped('Windows does not support POSIX signals');
  327. }
  328. if (!function_exists('posix_kill')) {
  329. $this->markTestSkipped('posix_kill is required for this test');
  330. }
  331. $termSignal = defined('SIGKILL') ? SIGKILL : 9;
  332. $process = $this->getProcess('exec php -r "while (true) {}"');
  333. $process->start();
  334. posix_kill($process->getPid(), $termSignal);
  335. $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'The process has been signaled with signal "9".');
  336. $process->wait();
  337. }
  338. public function testRestart()
  339. {
  340. $process1 = $this->getProcess('php -r "echo getmypid();"');
  341. $process1->run();
  342. $process2 = $process1->restart();
  343. usleep(300000); // wait for output
  344. // Ensure that both processed finished and the output is numeric
  345. $this->assertFalse($process1->isRunning());
  346. $this->assertFalse($process2->isRunning());
  347. $this->assertTrue(is_numeric($process1->getOutput()));
  348. $this->assertTrue(is_numeric($process2->getOutput()));
  349. // Ensure that restart returned a new process by check that the output is different
  350. $this->assertNotEquals($process1->getOutput(), $process2->getOutput());
  351. }
  352. public function testPhpDeadlock()
  353. {
  354. $this->markTestSkipped('Can course php to hang');
  355. // Sleep doesn't work as it will allow the process to handle signals and close
  356. // file handles from the other end.
  357. $process = $this->getProcess('php -r "while (true) {}"');
  358. $process->start();
  359. // PHP will deadlock when it tries to cleanup $process
  360. }
  361. public function testRunProcessWithTimeout()
  362. {
  363. $timeout = 0.5;
  364. $process = $this->getProcess('php -r "sleep(3);"');
  365. $process->setTimeout($timeout);
  366. $start = microtime(true);
  367. try {
  368. $process->run();
  369. $this->fail('A RuntimeException should have been raised');
  370. } catch (RuntimeException $e) {
  371. }
  372. $duration = microtime(true) - $start;
  373. $this->assertLessThan($timeout + Process::TIMEOUT_PRECISION, $duration);
  374. }
  375. public function testCheckTimeoutOnStartedProcess()
  376. {
  377. $timeout = 0.5;
  378. $precision = 100000;
  379. $process = $this->getProcess('php -r "sleep(3);"');
  380. $process->setTimeout($timeout);
  381. $start = microtime(true);
  382. $process->start();
  383. try {
  384. while ($process->isRunning()) {
  385. $process->checkTimeout();
  386. usleep($precision);
  387. }
  388. $this->fail('A RuntimeException should have been raised');
  389. } catch (RuntimeException $e) {
  390. }
  391. $duration = microtime(true) - $start;
  392. $this->assertLessThan($timeout + $precision, $duration);
  393. $this->assertFalse($process->isSuccessful());
  394. }
  395. public function testStartAfterATimeout()
  396. {
  397. $process = $this->getProcess('php -r "while(true) {echo \'\'; usleep(1000); }"');
  398. $process->setTimeout(0.1);
  399. try {
  400. $process->run();
  401. $this->fail('An exception should have been raised.');
  402. } catch (\Exception $e) {
  403. }
  404. $process->start();
  405. usleep(10000);
  406. $process->stop();
  407. }
  408. public function testGetPid()
  409. {
  410. $process = $this->getProcess('php -r "sleep(1);"');
  411. $process->start();
  412. $this->assertGreaterThan(0, $process->getPid());
  413. $process->stop();
  414. }
  415. public function testGetPidIsNullBeforeStart()
  416. {
  417. $process = $this->getProcess('php -r "sleep(1);"');
  418. $this->assertNull($process->getPid());
  419. }
  420. public function testGetPidIsNullAfterRun()
  421. {
  422. $process = $this->getProcess('php -m');
  423. $process->run();
  424. $this->assertNull($process->getPid());
  425. }
  426. public function testSignal()
  427. {
  428. $this->verifyPosixIsEnabled();
  429. $process = $this->getProcess('exec php -f ' . __DIR__ . '/SignalListener.php');
  430. $process->start();
  431. usleep(500000);
  432. $process->signal(SIGUSR1);
  433. while ($process->isRunning() && false === strpos($process->getoutput(), 'Caught SIGUSR1')) {
  434. usleep(10000);
  435. }
  436. $this->assertEquals('Caught SIGUSR1', $process->getOutput());
  437. }
  438. public function testExitCodeIsAvailableAfterSignal()
  439. {
  440. $this->verifyPosixIsEnabled();
  441. $process = $this->getProcess('sleep 4');
  442. $process->start();
  443. $process->signal(SIGKILL);
  444. while ($process->isRunning()) {
  445. usleep(10000);
  446. }
  447. $this->assertFalse($process->isRunning());
  448. $this->assertTrue($process->hasBeenSignaled());
  449. $this->assertFalse($process->isSuccessful());
  450. $this->assertEquals(137, $process->getExitCode());
  451. }
  452. /**
  453. * @expectedException Symfony\Component\Process\Exception\LogicException
  454. */
  455. public function testSignalProcessNotRunning()
  456. {
  457. $this->verifyPosixIsEnabled();
  458. $process = $this->getProcess('php -m');
  459. $process->signal(SIGHUP);
  460. }
  461. private function verifyPosixIsEnabled()
  462. {
  463. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  464. $this->markTestSkipped('POSIX signals do not work on windows');
  465. }
  466. if (!defined('SIGUSR1')) {
  467. $this->markTestSkipped('The pcntl extension is not enabled');
  468. }
  469. }
  470. /**
  471. * @expectedException Symfony\Component\Process\Exception\RuntimeException
  472. */
  473. public function testSignalWithWrongIntSignal()
  474. {
  475. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  476. $this->markTestSkipped('POSIX signals do not work on windows');
  477. }
  478. $process = $this->getProcess('php -r "sleep(3);"');
  479. $process->start();
  480. $process->signal(-4);
  481. }
  482. /**
  483. * @expectedException Symfony\Component\Process\Exception\RuntimeException
  484. */
  485. public function testSignalWithWrongNonIntSignal()
  486. {
  487. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  488. $this->markTestSkipped('POSIX signals do not work on windows');
  489. }
  490. $process = $this->getProcess('php -r "sleep(3);"');
  491. $process->start();
  492. $process->signal('CĂŠphalopodes');
  493. }
  494. public function responsesCodeProvider()
  495. {
  496. return array(
  497. //expected output / getter / code to execute
  498. //array(1,'getExitCode','exit(1);'),
  499. //array(true,'isSuccessful','exit();'),
  500. array('output', 'getOutput', 'echo \'output\';'),
  501. );
  502. }
  503. public function pipesCodeProvider()
  504. {
  505. $variations = array(
  506. 'fwrite(STDOUT, $in = file_get_contents(\'php://stdin\')); fwrite(STDERR, $in);',
  507. 'include \''.__DIR__.'/PipeStdinInStdoutStdErrStreamSelect.php\';',
  508. );
  509. if (defined('PHP_WINDOWS_VERSION_BUILD')) {
  510. // Avoid XL buffers on Windows because of https://bugs.php.net/bug.php?id=65650
  511. $sizes = array(1, 2, 4, 8);
  512. } else {
  513. $sizes = array(1, 16, 64, 1024, 4096);
  514. }
  515. $codes = array();
  516. foreach ($sizes as $size) {
  517. foreach ($variations as $code) {
  518. $codes[] = array($code, $size);
  519. }
  520. }
  521. return $codes;
  522. }
  523. /**
  524. * provides default method names for simple getter/setter
  525. */
  526. public function methodProvider()
  527. {
  528. $defaults = array(
  529. array('CommandLine'),
  530. array('Timeout'),
  531. array('WorkingDirectory'),
  532. array('Env'),
  533. array('Stdin'),
  534. array('Options')
  535. );
  536. return $defaults;
  537. }
  538. /**
  539. * @param string $commandline
  540. * @param null $cwd
  541. * @param array $env
  542. * @param null $stdin
  543. * @param integer $timeout
  544. * @param array $options
  545. *
  546. * @return Process
  547. */
  548. abstract protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array());
  549. }