public function testControlledExit() { if (!extension_loaded('pcntl')) { $this->markTestSkipped('PCNTL extension is not loaded.'); } $proc = new Process('exec ' . PHP_BINARY . ' ' . escapeshellarg(__DIR__ . '/console') . ' jms-job-queue:run --worker-name=test --verbose --max-runtime=999999'); $proc->start(); usleep(500000.0); $this->assertTrue($proc->isRunning(), 'Process exited prematurely: ' . $proc->getOutput() . $proc->getErrorOutput()); $this->assertTrueWithin(3, function () use($proc) { return false !== strpos($proc->getOutput(), 'Signal Handlers have been installed'); }, function () use($proc) { $this->fail('Signal handlers were not installed: ' . $proc->getOutput() . $proc->getErrorOutput()); }); $proc->signal(SIGTERM); $this->assertTrueWithin(3, function () use($proc) { return false !== strpos($proc->getOutput(), 'Received SIGTERM'); }, function () use($proc) { $this->fail('Signal was not received by process within 3 seconds: ' . $proc->getOutput() . $proc->getErrorOutput()); }); $this->assertTrueWithin(3, function () use($proc) { return !$proc->isRunning(); }, function () use($proc) { $this->fail('Process did not terminate within 3 seconds: ' . $proc->getOutput() . $proc->getErrorOutput()); }); $this->assertContains('All jobs finished, exiting.', $proc->getOutput()); }
/** * @Then I should be able to cancel the command */ public function iShouldBeAbleToCancelTheCommand() { // How many seconds to allow it to stop $tolerance = 2; $this->process->run(function ($type, $buffer) use($tolerance) { static $cancelled = false; if (!$cancelled) { $this->process->signal(15); $cancelled = time(); } else { if (time() - $cancelled > $tolerance) { throw new \Exception('Process did not cancel in time, still sending output: ' . $buffer); } } }); if (!$this->process) { throw new LogicException('No process started'); } }
/** * Sends a signal to the process, this method prevents the signal from being sent twice * * @return void */ private function sendTERMSignal() { if (!$this->hasReceivedHUP) { $this->hasReceivedHUP = true; try { $this->process->signal(SIGTERM); } catch (\Symfony\Component\Process\Exception\LogicException $e) { //In case the process ends between checking and actually sending the signal } } }
private function startProcess($port, $respond, $callback) { $env = array('PORT' => $port, 'RESPOND' => $respond); $process = new Process('php tests/bin/server.php', null, $env); $process->start(); // give it time to start while ($process->getIncrementalOutput() !== 'started') { usleep(100); } try { $callback(); } catch (Exception $e) { $process->signal(SIGKILL); $process->stop(); throw $e; } }
/** * Stops the process. */ public function stop() { $this->process->signal(SIGTERM); $this->process->stop(3, SIGKILL); }
/** * Execute behat command for featurename and return exit status. * * @param string $featurename name of the feature * @param string $featurepath path of feature file * @return int status code. */ protected function execute_behat_generator($featurename, $featurepath) { $cmd = "vendor/bin/behat --config " . util::get_tool_dir() . DIRECTORY_SEPARATOR . 'behat.yml ' . $featurepath; $process = new symfonyprocess($cmd); $process->setWorkingDirectory(__DIR__ . "/../../moodle"); $process->setTimeout(null); $process->start(); if ($process->getStatus() !== 'started') { echo "Error starting process: {$featurename}"; $process->signal(SIGKILL); exit(1); } while ($process->isRunning()) { $output = $process->getIncrementalOutput(); // Don't show start data everytime. $output = preg_replace('/[a-z0-9.\\(\\)].*/im', '', $output); $op = trim($output); if (!empty($op)) { echo $output; } } return $process->getExitCode(); }
protected function execute(InputInterface $input, OutputInterface $output) { set_time_limit(0); $container = $this->getContainer(); $env = array('APP_INCLUDE' => $this->getContainer()->getParameter('zym_resque.resque.vendor_dir') . '/autoload.php', 'VVERBOSE' => $input->getOption('verbose'), 'QUEUE' => $input->getArgument('queues'), 'INTERVAL' => (int) $input->getOption('interval'), 'REDIS_BACKEND' => sprintf('%s:%s', $container->getParameter('zym_resque.resque.redis.host'), $container->getParameter('zym_resque.resque.redis.port'))); $vendorDir = $container->getParameter('zym_resque.resque.vendor_dir'); // Handle breaking changes in new version of php-resque if (file_exists($vendorDir . '/chrisboulton/php-resque/resque.php')) { $workerCommand = 'php ' . $vendorDir . '/chrisboulton/php-resque/resque.php'; } else { $workerCommand = $vendorDir . '/chrisboulton/php-resque/bin/resque'; } $process = new Process($workerCommand, $container->getParameter('kernel.root_dir'), $env, null, null); $process->start(); if (function_exists('pcntl_signal')) { $worker = $this; $signalHandler = function ($signal) use($process, $output, $worker) { switch ($signal) { case \SIGTERM: $signalName = 'SIGTERM'; break; case \SIGINT: $signalName = 'SIGINT'; break; case \SIGQUIT: $signalName = 'SIGQUIT'; break; case \SIGUSR1: $signalName = 'SIGUSR1'; break; case \SIGUSR2: $signalName = 'SIGUSR2'; break; case \SIGCONT: $signalName = 'SIGCONT'; break; case \SIGPIPE: $signalName = 'SIGPIPE'; break; default: $signalName = $signal; } $output->writeln(sprintf('<error>%s signal caught</error>', $signalName)); $worker->signaled = true; $process->signal($signal); }; pcntl_signal(\SIGTERM, $signalHandler); pcntl_signal(\SIGINT, $signalHandler); pcntl_signal(\SIGQUIT, $signalHandler); pcntl_signal(\SIGUSR1, $signalHandler); pcntl_signal(\SIGUSR2, $signalHandler); pcntl_signal(\SIGCONT, $signalHandler); pcntl_signal(\SIGPIPE, $signalHandler); } $output->writeln(\sprintf('Starting worker <info>%s</info>', $process->getCommandLine())); $output->writeln(''); try { $process->wait(function ($type, $buffer) use($output) { // Color level $buffer = preg_replace('/^(\\[info\\])/', '<info>$1</info>', $buffer); $buffer = preg_replace('/^(\\[debug\\])/', '<fg=white>$1</fg=white>', $buffer); $buffer = preg_replace('/^(\\[notice\\])/', '<comment>$1</comment>', $buffer); $buffer = preg_replace('/^(\\[warning\\])/', '<error>$1</error>', $buffer); $buffer = preg_replace('/^(\\[critical\\])/', '<error>$1</error>', $buffer); // Color timestamp $buffer = preg_replace('/(\\*\\* \\[\\d{2}:\\d{2}:\\d{2} \\d{4}-\\d{2}-\\d{2}\\])/', '<comment>$1</comment>', $buffer); $buffer = preg_replace('/(\\[\\d{2}:\\d{2}:\\d{2} \\d{4}-\\d{2}-\\d{2}\\])/', '<comment>$1</comment>', $buffer); // Color $buffer = preg_replace('/\\(Job(.*?)\\|(.*?)\\|(.*?)\\|/', '(Job$1|$2|<info>$3</info>|', $buffer); $buffer = preg_replace('/Job{(.*?)}/', '<info>Job{</info><comment>$1</comment><info>}</info>', $buffer); $buffer = preg_replace('/(ID:)/', '<info>$1</info>', $buffer); // Color failed $buffer = preg_replace('/failed/', '<error>$1</error>', $buffer); $output->write($buffer); }); } catch (\Symfony\Component\Process\Exception\RuntimeException $e) { if (!$this->signaled && !$process->getStopSignal() && !$process->getTermSignal()) { throw $e; } } $process->stop(); $output->writeln(''); $output->writeln('<info>Worker stopped...</info>'); }
/** * Execute behat command for featurename and return exit status. * * @return int status code. */ protected function execute_behat_generator() { $cmd = "vendor/bin/behat --config " . util::get_tool_dir() . DIRECTORY_SEPARATOR . 'behat.yml '; $process = new Process($cmd); $process->setWorkingDirectory(__DIR__ . "/../../../../../"); $process->setTimeout(null); $process->start(); if ($process->getStatus() !== 'started') { echo "Error starting process"; $process->signal(SIGKILL); exit(1); } while ($process->isRunning()) { $output = $process->getIncrementalOutput(); $op = trim($output); if (!empty($op)) { echo $output; } } if ($process->getExitCode() !== 0) { echo $process->getErrorOutput(); } return $process->getExitCode(); }