/vendor/alchemy/binary-driver/README.md |
@@ -0,0 +1,190 @@ |
# Binary Driver |
|
Binary-Driver is a set of PHP tools to build binary drivers. |
|
[![Build Status](https://travis-ci.org/alchemy-fr/BinaryDriver.png?branch=master)](https://travis-ci.org/alchemy-fr/BinaryDriver) |
|
## Why ? |
|
You may wonder *Why building a library while I can use `exec` or |
[symfony/process](https://github.com/symfony/Process) ?*. |
|
Here is a simple answer : |
|
- If you use `exec`, `passthru`, `system`, `proc_open` or any low level process |
handling in PHP, you should have a look to [symfony/process](https://github.com/symfony/Process) |
component that will provide an OO portable, testable and secure interface to |
deal with this. It seems easy at first approach, but if you look at this |
component [unit tests](https://github.com/symfony/Process/tree/master/Tests), |
you will see that handling process in a simple interface can easily become a |
nightmare. |
|
- If you already use symfony/process, and want to build binary drivers, you |
will always have the same common set of methods and objects to configure, log, |
debug, and generate processes. |
This library is a base to implement any binary driver with this common set of |
needs. |
|
## AbstractBinary |
|
`AbstractBinary` provides an abstract class to build a binary driver. It implements |
`BinaryInterface`. |
|
Implementation example : |
|
```php |
use Alchemy\BinaryDriver\AbstractBinary; |
|
class LsDriver extends AbstractBinary |
{ |
public function getName() |
{ |
return 'ls driver'; |
} |
} |
|
$parser = new LsParser(); |
|
$driver = Driver::load('ls'); |
// will return the output of `ls -a -l` |
$parser->parse($driver->command(array('-a', '-l'))); |
``` |
|
### Binary detection troubleshooting |
|
If you are using Nginx with PHP-fpm, executable detection may not work because of an empty `$_ENV['path']`. |
To avoid having an empty `PATH` environment variable, add the following line to your `fastcgi_params` |
config file (replace `/your/current/path/` with the output of `printenv PATH`) : |
|
``` |
fastcgi_param PATH /your/current/path |
``` |
|
## Logging |
|
You can log events with a `Psr\Log\LoggerInterface` by passing it in the load |
method as second argument : |
|
```php |
$logger = new Monolog\Logger('driver'); |
$driver = Driver::load('ls', $logger); |
``` |
|
## Listeners |
|
You can add custom listeners on processes. |
Listeners are built on top of [Evenement](https://github.com/igorw/evenement) |
and must implement `Alchemy\BinaryDriver\ListenerInterface`. |
|
```php |
use Symfony\Component\Process\Process; |
|
class DebugListener extends EventEmitter implements ListenerInterface |
{ |
public function handle($type, $data) |
{ |
foreach (explode(PHP_EOL, $data) as $line) { |
$this->emit($type === Process::ERR ? 'error' : 'out', array($line)); |
} |
} |
|
public function forwardedEvents() |
{ |
// forward 'error' events to the BinaryInterface |
return array('error'); |
} |
} |
|
$listener = new DebugListener(); |
|
$driver = CustomImplementation::load('php'); |
|
// adds listener |
$driver->listen($listener); |
|
$driver->on('error', function ($line) { |
echo '[ERROR] ' . $line . PHP_EOL; |
}); |
|
// removes listener |
$driver->unlisten($listener); |
``` |
|
### Bundled listeners |
|
The debug listener is a simple listener to catch `stderr` and `stdout` outputs ; |
read the implementation for customization. |
|
```php |
use Alchemy\BinaryDriver\Listeners\DebugListener; |
|
$driver = CustomImplementation::load('php'); |
$driver->listen(new DebugListener()); |
|
$driver->on('debug', function ($line) { |
echo $line; |
}); |
``` |
|
## ProcessBuilderFactory |
|
ProcessBuilderFactory ease spawning processes by generating Symfony [Process] |
(http://symfony.com/doc/master/components/process.html) objects. |
|
```php |
use Alchemy\BinaryDriver\ProcessBuilderFactory; |
|
$factory = new ProcessBuilderFactory('/usr/bin/php'); |
|
// return a Symfony\Component\Process\Process |
$process = $factory->create('-v'); |
|
// echoes '/usr/bin/php' '-v' |
echo $process->getCommandLine(); |
|
$process = $factory->create(array('-r', 'echo "Hello !";')); |
|
// echoes '/usr/bin/php' '-r' 'echo "Hello !";' |
echo $process->getCommandLine(); |
``` |
|
## Configuration |
|
A simple configuration object, providing an `ArrayAccess` and `IteratorAggregate` |
interface. |
|
```php |
use Alchemy\BinaryDriver\Configuration; |
|
$conf = new Configuration(array('timeout' => 0)); |
|
echo $conf->get('timeout'); |
|
if ($conf->has('param')) { |
$conf->remove('param'); |
} |
|
$conf->set('timeout', 20); |
|
$conf->all(); |
``` |
|
Same example using the `ArrayAccess` interface : |
|
```php |
use Alchemy\BinaryDriver\Configuration; |
|
$conf = new Configuration(array('timeout' => 0)); |
|
echo $conf['timeout']; |
|
if (isset($conf['param'])) { |
unset($conf['param']); |
} |
|
$conf['timeout'] = 20; |
``` |
|
## License |
|
This project is released under the MIT license. |
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/AbstractBinary.php |
@@ -0,0 +1,220 @@ |
<?php |
|
/* |
* This file is part of Alchemy\BinaryDriver. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace Alchemy\BinaryDriver; |
|
use Alchemy\BinaryDriver\Exception\ExecutableNotFoundException; |
use Alchemy\BinaryDriver\Exception\ExecutionFailureException; |
use Alchemy\BinaryDriver\Listeners\Listeners; |
use Alchemy\BinaryDriver\Listeners\ListenerInterface; |
use Evenement\EventEmitter; |
use Monolog\Logger; |
use Monolog\Handler\NullHandler; |
use Psr\Log\LoggerInterface; |
use Symfony\Component\Process\ExecutableFinder; |
use Symfony\Component\Process\Process; |
|
abstract class AbstractBinary extends EventEmitter implements BinaryInterface |
{ |
/** @var ConfigurationInterface */ |
protected $configuration; |
|
/** @var ProcessBuilderFactoryInterface */ |
protected $factory; |
|
/** @var ProcessRunner */ |
private $processRunner; |
|
/** @var Listeners */ |
private $listenersManager; |
|
public function __construct(ProcessBuilderFactoryInterface $factory, LoggerInterface $logger, ConfigurationInterface $configuration) |
{ |
$this->factory = $factory; |
$this->configuration = $configuration; |
$this->processRunner = new ProcessRunner($logger, $this->getName()); |
$this->listenersManager = new Listeners(); |
$this->applyProcessConfiguration(); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function listen(ListenerInterface $listener) |
{ |
$this->listenersManager->register($listener, $this); |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function unlisten(ListenerInterface $listener) |
{ |
$this->listenersManager->unregister($listener, $this); |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getConfiguration() |
{ |
return $this->configuration; |
} |
|
/** |
* {@inheritdoc} |
* |
* @return BinaryInterface |
*/ |
public function setConfiguration(ConfigurationInterface $configuration) |
{ |
$this->configuration = $configuration; |
$this->applyProcessConfiguration(); |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getProcessBuilderFactory() |
{ |
return $this->factory; |
} |
|
/** |
* {@inheritdoc} |
* |
* @return BinaryInterface |
*/ |
public function setProcessBuilderFactory(ProcessBuilderFactoryInterface $factory) |
{ |
$this->factory = $factory; |
$this->applyProcessConfiguration(); |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function getProcessRunner() |
{ |
return $this->processRunner; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function setProcessRunner(ProcessRunnerInterface $runner) |
{ |
$this->processRunner = $runner; |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function command($command, $bypassErrors = false, $listeners = null) |
{ |
if (!is_array($command)) { |
$command = array($command); |
} |
|
return $this->run($this->factory->create($command), $bypassErrors, $listeners); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public static function load($binaries, LoggerInterface $logger = null, $configuration = array()) |
{ |
$finder = new ExecutableFinder(); |
$binary = null; |
$binaries = is_array($binaries) ? $binaries : array($binaries); |
|
foreach ($binaries as $candidate) { |
if (file_exists($candidate) && is_executable($candidate)) { |
$binary = $candidate; |
break; |
} |
if (null !== $binary = $finder->find($candidate)) { |
break; |
} |
} |
|
if (null === $binary) { |
throw new ExecutableNotFoundException(sprintf( |
'Executable not found, proposed : %s', implode(', ', $binaries) |
)); |
} |
|
if (null === $logger) { |
$logger = new Logger(__NAMESPACE__ . ' logger'); |
$logger->pushHandler(new NullHandler()); |
} |
|
$configuration = $configuration instanceof ConfigurationInterface ? $configuration : new Configuration($configuration); |
|
return new static(new ProcessBuilderFactory($binary), $logger, $configuration); |
} |
|
/** |
* Returns the name of the driver |
* |
* @return string |
*/ |
abstract public function getName(); |
|
/** |
* Executes a process, logs events |
* |
* @param Process $process |
* @param Boolean $bypassErrors Set to true to disable throwing ExecutionFailureExceptions |
* @param ListenerInterface|array $listeners A listener or an array of listener to register for this unique run |
* |
* @return string The Process output |
* |
* @throws ExecutionFailureException in case of process failure. |
*/ |
protected function run(Process $process, $bypassErrors = false, $listeners = null) |
{ |
if (null !== $listeners) { |
if (!is_array($listeners)) { |
$listeners = array($listeners); |
} |
|
$listenersManager = clone $this->listenersManager; |
|
foreach ($listeners as $listener) { |
$listenersManager->register($listener, $this); |
} |
} else { |
$listenersManager = $this->listenersManager; |
} |
|
return $this->processRunner->run($process, $listenersManager->storage, $bypassErrors); |
} |
|
private function applyProcessConfiguration() |
{ |
if ($this->configuration->has('timeout')) { |
$this->factory->setTimeout($this->configuration->get('timeout')); |
} |
|
return $this; |
} |
} |
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/ProcessBuilderFactory.php |
@@ -0,0 +1,177 @@ |
<?php |
|
/* |
* This file is part of Alchemy\BinaryDriver. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace Alchemy\BinaryDriver; |
|
use Alchemy\BinaryDriver\Exception\InvalidArgumentException; |
use Symfony\Component\Process\ProcessBuilder; |
|
class ProcessBuilderFactory implements ProcessBuilderFactoryInterface |
{ |
/** |
* The binary path |
* |
* @var String |
*/ |
protected $binary; |
|
/** |
* The timeout for the generated processes |
* |
* @var integer|float |
*/ |
private $timeout; |
|
/** |
* An internal ProcessBuilder. |
* |
* Note that this one is used only if Symfony ProcessBuilder has method |
* setPrefix (2.3) |
* |
* @var ProcessBuilder |
*/ |
private $builder; |
|
/** |
* Tells whether Symfony LTS ProcessBuilder should be emulated or not. |
* |
* This symfony version provided a brand new ::setPrefix method. |
* |
* @var Boolean |
*/ |
public static $emulateSfLTS; |
|
/** |
* Constructor |
* |
* @param String $binary The path to the binary |
* |
* @throws InvalidArgumentException In case binary path is invalid |
*/ |
public function __construct($binary) |
{ |
$this->detectEmulation(); |
|
if (!self::$emulateSfLTS) { |
$this->builder = new ProcessBuilder(); |
} |
|
$this->useBinary($binary); |
} |
|
/** |
* Covenient method for unit testing |
* |
* @return type |
*/ |
public function getBuilder() |
{ |
return $this->builder; |
} |
|
/** |
* Covenient method for unit testing |
* |
* @param ProcessBuilder $builder |
* @return ProcessBuilderFactory |
*/ |
public function setBuilder(ProcessBuilder $builder) |
{ |
$this->builder = $builder; |
|
return $this; |
} |
|
/** |
* @inheritdoc |
*/ |
public function getBinary() |
{ |
return $this->binary; |
} |
|
/** |
* @inheritdoc |
*/ |
public function useBinary($binary) |
{ |
if (!is_executable($binary)) { |
throw new InvalidArgumentException(sprintf('`%s` is not an executable binary', $binary)); |
} |
|
$this->binary = $binary; |
|
if (!static::$emulateSfLTS) { |
$this->builder->setPrefix($binary); |
} |
|
return $this; |
} |
|
/** |
* @inheritdoc |
*/ |
public function setTimeout($timeout) |
{ |
$this->timeout = $timeout; |
|
if (!static::$emulateSfLTS) { |
$this->builder->setTimeout($this->timeout); |
} |
|
return $this; |
} |
|
/** |
* @inheritdoc |
*/ |
public function getTimeout() |
{ |
return $this->timeout; |
} |
|
/** |
* @inheritdoc |
*/ |
public function create($arguments = array()) |
{ |
if (null === $this->binary) { |
throw new InvalidArgumentException('No binary set'); |
} |
|
if (!is_array($arguments)) { |
$arguments = array($arguments); |
} |
|
if (static::$emulateSfLTS) { |
array_unshift($arguments, $this->binary); |
|
return ProcessBuilder::create($arguments) |
->setTimeout($this->timeout) |
->getProcess(); |
} else { |
return $this->builder |
->setArguments($arguments) |
->getProcess(); |
} |
} |
|
private function detectEmulation() |
{ |
if (null !== static::$emulateSfLTS) { |
return $this; |
} |
|
static::$emulateSfLTS = !method_exists('Symfony\Component\Process\ProcessBuilder', 'setPrefix'); |
|
return $this; |
} |
} |
/vendor/alchemy/binary-driver/src/Alchemy/BinaryDriver/ProcessRunner.php |
@@ -0,0 +1,104 @@ |
<?php |
|
/* |
* This file is part of Alchemy\BinaryDriver. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace Alchemy\BinaryDriver; |
|
use Alchemy\BinaryDriver\Exception\ExecutionFailureException; |
use Psr\Log\LoggerInterface; |
use SplObjectStorage; |
use Symfony\Component\Process\Exception\RuntimeException; |
use Symfony\Component\Process\Process; |
|
class ProcessRunner implements ProcessRunnerInterface |
{ |
/** @var LoggerInterface */ |
private $logger; |
|
/** @var string */ |
private $name; |
|
public function __construct(LoggerInterface $logger, $name) |
{ |
$this->logger = $logger; |
$this->name = $name; |
} |
|
/** |
* {@inheritdoc} |
* |
* @return ProcessRunner |
*/ |
public function setLogger(LoggerInterface $logger) |
{ |
$this->logger = $logger; |
|
return $this; |
} |
|
/** |
* @return LoggerInterface |
*/ |
public function getLogger() |
{ |
return $this->logger; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function run(Process $process, SplObjectStorage $listeners, $bypassErrors) |
{ |
$this->logger->info(sprintf( |
'%s running command %s', $this->name, $process->getCommandLine() |
)); |
|
try { |
$process->run($this->buildCallback($listeners)); |
} catch (RuntimeException $e) { |
if (!$bypassErrors) { |
$this->doExecutionFailure($process->getCommandLine(), $e); |
} |
} |
|
if (!$bypassErrors && !$process->isSuccessful()) { |
$this->doExecutionFailure($process->getCommandLine()); |
} elseif (!$process->isSuccessful()) { |
$this->logger->error(sprintf( |
'%s failed to execute command %s', $this->name, $process->getCommandLine() |
)); |
|
return; |
} else { |
$this->logger->info(sprintf('%s executed command successfully', $this->name)); |
|
return $process->getOutput(); |
} |
} |
|
private function buildCallback(SplObjectStorage $listeners) |
{ |
return function ($type, $data) use ($listeners) { |
foreach ($listeners as $listener) { |
$listener->handle($type, $data); |
} |
}; |
} |
|
private function doExecutionFailure($command, \Exception $e = null) |
{ |
$this->logger->error(sprintf( |
'%s failed to execute command %s', $this->name, $command |
)); |
throw new ExecutionFailureException(sprintf( |
'%s failed to execute command %s', $this->name, $command |
), $e ? $e->getCode() : null, $e ?: null); |
} |
} |
/vendor/alchemy/binary-driver/tests/Alchemy/Tests/BinaryDriver/AbstractBinaryTest.php |
@@ -0,0 +1,300 @@ |
<?php |
|
namespace Alchemy\Tests\BinaryDriver; |
|
use Alchemy\BinaryDriver\AbstractBinary; |
use Alchemy\BinaryDriver\BinaryDriverTestCase; |
use Alchemy\BinaryDriver\Configuration; |
use Alchemy\BinaryDriver\Exception\ExecutableNotFoundException; |
use Alchemy\BinaryDriver\Listeners\ListenerInterface; |
use Symfony\Component\Process\ExecutableFinder; |
|
class AbstractBinaryTest extends BinaryDriverTestCase |
{ |
protected function getPhpBinary() |
{ |
$finder = new ExecutableFinder(); |
$php = $finder->find('php'); |
|
if (null === $php) { |
$this->markTestSkipped('Unable to find a php binary'); |
} |
|
return $php; |
} |
|
public function testSimpleLoadWithBinaryPath() |
{ |
$php = $this->getPhpBinary(); |
$imp = Implementation::load($php); |
$this->assertInstanceOf('Alchemy\Tests\BinaryDriver\Implementation', $imp); |
|
$this->assertEquals($php, $imp->getProcessBuilderFactory()->getBinary()); |
} |
|
public function testMultipleLoadWithBinaryPath() |
{ |
$php = $this->getPhpBinary(); |
$imp = Implementation::load(array('/zz/path/to/unexisting/command', $php)); |
$this->assertInstanceOf('Alchemy\Tests\BinaryDriver\Implementation', $imp); |
|
$this->assertEquals($php, $imp->getProcessBuilderFactory()->getBinary()); |
} |
|
public function testSimpleLoadWithBinaryName() |
{ |
$php = $this->getPhpBinary(); |
$imp = Implementation::load('php'); |
$this->assertInstanceOf('Alchemy\Tests\BinaryDriver\Implementation', $imp); |
|
$this->assertEquals($php, $imp->getProcessBuilderFactory()->getBinary()); |
} |
|
public function testMultipleLoadWithBinaryName() |
{ |
$php = $this->getPhpBinary(); |
$imp = Implementation::load(array('bachibouzouk', 'php')); |
$this->assertInstanceOf('Alchemy\Tests\BinaryDriver\Implementation', $imp); |
|
$this->assertEquals($php, $imp->getProcessBuilderFactory()->getBinary()); |
} |
|
public function testLoadWithMultiplePathExpectingAFailure() |
{ |
$this->setExpectedException(ExecutableNotFoundException::class); |
|
Implementation::load(array('bachibouzouk', 'moribon')); |
} |
|
public function testLoadWithUniquePathExpectingAFailure() |
{ |
$this->setExpectedException(ExecutableNotFoundException::class); |
|
Implementation::load('bachibouzouk'); |
} |
|
public function testLoadWithCustomLogger() |
{ |
$logger = $this->getMock('Psr\Log\LoggerInterface'); |
$imp = Implementation::load('php', $logger); |
|
$this->assertEquals($logger, $imp->getProcessRunner()->getLogger()); |
} |
|
public function testLoadWithCustomConfigurationAsArray() |
{ |
$conf = array('timeout' => 200); |
$imp = Implementation::load('php', null, $conf); |
|
$this->assertEquals($conf, $imp->getConfiguration()->all()); |
} |
|
public function testLoadWithCustomConfigurationAsObject() |
{ |
$conf = $this->getMock('Alchemy\BinaryDriver\ConfigurationInterface'); |
$imp = Implementation::load('php', null, $conf); |
|
$this->assertEquals($conf, $imp->getConfiguration()); |
} |
|
public function testProcessBuilderFactoryGetterAndSetters() |
{ |
$imp = Implementation::load('php'); |
$factory = $this->getMock('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface'); |
|
$imp->setProcessBuilderFactory($factory); |
$this->assertEquals($factory, $imp->getProcessBuilderFactory()); |
} |
|
public function testConfigurationGetterAndSetters() |
{ |
$imp = Implementation::load('php'); |
$conf = $this->getMock('Alchemy\BinaryDriver\ConfigurationInterface'); |
|
$imp->setConfiguration($conf); |
$this->assertEquals($conf, $imp->getConfiguration()); |
} |
|
public function testTimeoutIsSetOnConstruction() |
{ |
$imp = Implementation::load('php', null, array('timeout' => 42)); |
$this->assertEquals(42, $imp->getProcessBuilderFactory()->getTimeout()); |
} |
|
public function testTimeoutIsSetOnConfigurationSetting() |
{ |
$imp = Implementation::load('php', null); |
$imp->setConfiguration(new Configuration(array('timeout' => 42))); |
$this->assertEquals(42, $imp->getProcessBuilderFactory()->getTimeout()); |
} |
|
public function testTimeoutIsSetOnProcessBuilderSetting() |
{ |
$imp = Implementation::load('php', null, array('timeout' => 42)); |
|
$factory = $this->getMock('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface'); |
$factory->expects($this->once()) |
->method('setTimeout') |
->with(42); |
|
$imp->setProcessBuilderFactory($factory); |
} |
|
public function testListenRegistersAListener() |
{ |
$imp = Implementation::load('php'); |
|
$listeners = $this->getMockBuilder('Alchemy\BinaryDriver\Listeners\Listeners') |
->disableOriginalConstructor() |
->getMock(); |
|
$listener = $this->getMock('Alchemy\BinaryDriver\Listeners\ListenerInterface'); |
|
$listeners->expects($this->once()) |
->method('register') |
->with($this->equalTo($listener), $this->equalTo($imp)); |
|
$reflexion = new \ReflectionClass('Alchemy\BinaryDriver\AbstractBinary'); |
$prop = $reflexion->getProperty('listenersManager'); |
$prop->setAccessible(true); |
$prop->setValue($imp, $listeners); |
|
$imp->listen($listener); |
} |
|
/** |
* @dataProvider provideCommandParameters |
*/ |
public function testCommandRunsAProcess($parameters, $bypassErrors, $expectedParameters, $output) |
{ |
$imp = Implementation::load('php'); |
$factory = $this->getMock('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface'); |
$processRunner = $this->getMock('Alchemy\BinaryDriver\ProcessRunnerInterface'); |
|
$process = $this->getMockBuilder('Symfony\Component\Process\Process') |
->disableOriginalConstructor() |
->getMock(); |
|
$processRunner->expects($this->once()) |
->method('run') |
->with($this->equalTo($process), $this->isInstanceOf('SplObjectStorage'), $this->equalTo($bypassErrors)) |
->will($this->returnValue($output)); |
|
$factory->expects($this->once()) |
->method('create') |
->with($expectedParameters) |
->will($this->returnValue($process)); |
|
$imp->setProcessBuilderFactory($factory); |
$imp->setProcessRunner($processRunner); |
|
$this->assertEquals($output, $imp->command($parameters, $bypassErrors)); |
} |
|
/** |
* @dataProvider provideCommandWithListenersParameters |
*/ |
public function testCommandWithTemporaryListeners($parameters, $bypassErrors, $expectedParameters, $output, $count, $listeners) |
{ |
$imp = Implementation::load('php'); |
$factory = $this->getMock('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface'); |
$processRunner = $this->getMock('Alchemy\BinaryDriver\ProcessRunnerInterface'); |
|
$process = $this->getMockBuilder('Symfony\Component\Process\Process') |
->disableOriginalConstructor() |
->getMock(); |
|
$firstStorage = $secondStorage = null; |
|
$processRunner->expects($this->exactly(2)) |
->method('run') |
->with($this->equalTo($process), $this->isInstanceOf('SplObjectStorage'), $this->equalTo($bypassErrors)) |
->will($this->returnCallback(function ($process, $storage, $errors) use ($output, &$firstStorage, &$secondStorage) { |
if (null === $firstStorage) { |
$firstStorage = $storage; |
} else { |
$secondStorage = $storage; |
} |
|
return $output; |
})); |
|
$factory->expects($this->exactly(2)) |
->method('create') |
->with($expectedParameters) |
->will($this->returnValue($process)); |
|
$imp->setProcessBuilderFactory($factory); |
$imp->setProcessRunner($processRunner); |
|
$this->assertEquals($output, $imp->command($parameters, $bypassErrors, $listeners)); |
$this->assertCount($count, $firstStorage); |
$this->assertEquals($output, $imp->command($parameters, $bypassErrors)); |
$this->assertCount(0, $secondStorage); |
} |
|
public function provideCommandWithListenersParameters() |
{ |
return array( |
array('-a', false, array('-a'), 'loubda', 2, array($this->getMockListener(), $this->getMockListener())), |
array('-a', false, array('-a'), 'loubda', 1, array($this->getMockListener())), |
array('-a', false, array('-a'), 'loubda', 1, $this->getMockListener()), |
array('-a', false, array('-a'), 'loubda', 0, array()), |
); |
} |
|
public function provideCommandParameters() |
{ |
return array( |
array('-a', false, array('-a'), 'loubda'), |
array('-a', true, array('-a'), 'loubda'), |
array('-a -b', false, array('-a -b'), 'loubda'), |
array(array('-a'), false, array('-a'), 'loubda'), |
array(array('-a'), true, array('-a'), 'loubda'), |
array(array('-a', '-b'), false, array('-a', '-b'), 'loubda'), |
); |
} |
|
public function testUnlistenUnregistersAListener() |
{ |
$imp = Implementation::load('php'); |
|
$listeners = $this->getMockBuilder('Alchemy\BinaryDriver\Listeners\Listeners') |
->disableOriginalConstructor() |
->getMock(); |
|
$listener = $this->getMock('Alchemy\BinaryDriver\Listeners\ListenerInterface'); |
|
$listeners->expects($this->once()) |
->method('unregister') |
->with($this->equalTo($listener), $this->equalTo($imp)); |
|
$reflexion = new \ReflectionClass('Alchemy\BinaryDriver\AbstractBinary'); |
$prop = $reflexion->getProperty('listenersManager'); |
$prop->setAccessible(true); |
$prop->setValue($imp, $listeners); |
|
$imp->unlisten($listener); |
} |
|
/** |
* @return \PHPUnit_Framework_MockObject_MockObject |
*/ |
private function getMockListener() |
{ |
$listener = $this->getMock(ListenerInterface::class); |
$listener->expects($this->any()) |
->method('forwardedEvents') |
->willReturn(array()); |
|
return $listener; |
} |
} |
|
class Implementation extends AbstractBinary |
{ |
public function getName() |
{ |
return 'Implementation'; |
} |
} |
/vendor/alchemy/binary-driver/tests/Alchemy/Tests/BinaryDriver/AbstractProcessBuilderFactoryTest.php |
@@ -0,0 +1,97 @@ |
<?php |
|
namespace Alchemy\Tests\BinaryDriver; |
|
use Symfony\Component\Process\ExecutableFinder; |
use Alchemy\BinaryDriver\ProcessBuilderFactory; |
|
abstract class AbstractProcessBuilderFactoryTest extends \PHPUnit_Framework_TestCase |
{ |
public static $phpBinary; |
|
private $original; |
/** |
* @return ProcessBuilderFactory |
*/ |
abstract protected function getProcessBuilderFactory($binary); |
|
public function setUp() |
{ |
ProcessBuilderFactory::$emulateSfLTS = null; |
if (null === static::$phpBinary) { |
$this->markTestSkipped('Unable to detect php binary, skipping'); |
} |
} |
|
public static function setUpBeforeClass() |
{ |
$finder = new ExecutableFinder(); |
static::$phpBinary = $finder->find('php'); |
} |
|
public function testThatBinaryIsSetOnConstruction() |
{ |
$factory = $this->getProcessBuilderFactory(static::$phpBinary); |
$this->assertEquals(static::$phpBinary, $factory->getBinary()); |
} |
|
public function testGetSetBinary() |
{ |
$finder = new ExecutableFinder(); |
$phpUnit = $finder->find('phpunit'); |
|
if (null === $phpUnit) { |
$this->markTestSkipped('Unable to detect phpunit binary, skipping'); |
} |
|
$factory = $this->getProcessBuilderFactory(static::$phpBinary); |
$factory->useBinary($phpUnit); |
$this->assertEquals($phpUnit, $factory->getBinary()); |
} |
|
/** |
* @expectedException Alchemy\BinaryDriver\Exception\InvalidArgumentException |
*/ |
public function testUseNonExistantBinary() |
{ |
$factory = $this->getProcessBuilderFactory(static::$phpBinary); |
$factory->useBinary('itissureitdoesnotexist'); |
} |
|
public function testCreateShouldReturnAProcess() |
{ |
$factory = $this->getProcessBuilderFactory(static::$phpBinary); |
$process = $factory->create(); |
|
$this->assertInstanceOf('Symfony\Component\Process\Process', $process); |
$this->assertEquals("'".static::$phpBinary."'", $process->getCommandLine()); |
} |
|
public function testCreateWithStringArgument() |
{ |
$factory = $this->getProcessBuilderFactory(static::$phpBinary); |
$process = $factory->create('-v'); |
|
$this->assertInstanceOf('Symfony\Component\Process\Process', $process); |
$this->assertEquals("'".static::$phpBinary."' '-v'", $process->getCommandLine()); |
} |
|
public function testCreateWithArrayArgument() |
{ |
$factory = $this->getProcessBuilderFactory(static::$phpBinary); |
$process = $factory->create(array('-r', 'echo "Hello !";')); |
|
$this->assertInstanceOf('Symfony\Component\Process\Process', $process); |
$this->assertEquals("'".static::$phpBinary."' '-r' 'echo \"Hello !\";'", $process->getCommandLine()); |
} |
|
public function testCreateWithTimeout() |
{ |
$factory = $this->getProcessBuilderFactory(static::$phpBinary); |
$factory->setTimeout(200); |
$process = $factory->create(array('-i')); |
|
$this->assertInstanceOf('Symfony\Component\Process\Process', $process); |
$this->assertEquals(200, $process->getTimeout()); |
} |
} |
/vendor/alchemy/binary-driver/tests/Alchemy/Tests/BinaryDriver/Listeners/ListenersTest.php |
@@ -0,0 +1,92 @@ |
<?php |
|
namespace Alchemy\Tests\BinaryDriver\Listeners; |
|
use Alchemy\BinaryDriver\Listeners\Listeners; |
use Evenement\EventEmitter; |
use Alchemy\BinaryDriver\Listeners\ListenerInterface; |
|
class ListenersTest extends \PHPUnit_Framework_TestCase |
{ |
public function testRegister() |
{ |
$listener = new MockListener(); |
|
$listeners = new Listeners(); |
$listeners->register($listener); |
|
$n = 0; |
$listener->on('received', function ($type, $data) use (&$n, &$capturedType, &$capturedData) { |
$n++; |
$capturedData = $data; |
$capturedType = $type; |
}); |
|
$type = 'type'; |
$data = 'data'; |
|
$listener->handle($type, $data); |
$listener->handle($type, $data); |
|
$listeners->unregister($listener); |
|
$listener->handle($type, $data); |
|
$this->assertEquals(3, $n); |
$this->assertEquals($type, $capturedType); |
$this->assertEquals($data, $capturedData); |
} |
|
public function testRegisterAndForwardThenUnregister() |
{ |
$listener = new MockListener(); |
$target = new EventEmitter(); |
|
$n = 0; |
$target->on('received', function ($type, $data) use (&$n, &$capturedType, &$capturedData) { |
$n++; |
$capturedData = $data; |
$capturedType = $type; |
}); |
|
$m = 0; |
$listener->on('received', function ($type, $data) use (&$m, &$capturedType2, &$capturedData2) { |
$m++; |
$capturedData2 = $data; |
$capturedType2 = $type; |
}); |
|
$listeners = new Listeners(); |
$listeners->register($listener, $target); |
|
$type = 'type'; |
$data = 'data'; |
|
$listener->handle($type, $data); |
$listener->handle($type, $data); |
|
$listeners->unregister($listener, $target); |
|
$listener->handle($type, $data); |
|
$this->assertEquals(2, $n); |
$this->assertEquals(3, $m); |
$this->assertEquals($type, $capturedType); |
$this->assertEquals($data, $capturedData); |
$this->assertEquals($type, $capturedType2); |
$this->assertEquals($data, $capturedData2); |
} |
} |
|
class MockListener extends EventEmitter implements ListenerInterface |
{ |
public function handle($type, $data) |
{ |
$this->emit('received', array($type, $data)); |
} |
|
public function forwardedEvents() |
{ |
return array('received'); |
} |
} |
/vendor/alchemy/binary-driver/tests/Alchemy/Tests/BinaryDriver/ProcessRunnerTest.php |
@@ -0,0 +1,208 @@ |
<?php |
|
/* |
* This file is part of Alchemy\BinaryDriver. |
* |
* (c) Alchemy <info@alchemy.fr> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace Alchemy\Tests\BinaryDriver; |
|
use Alchemy\BinaryDriver\ProcessRunner; |
use Alchemy\BinaryDriver\BinaryDriverTestCase; |
use Alchemy\BinaryDriver\Exception\ExecutionFailureException; |
use Alchemy\BinaryDriver\Listeners\ListenerInterface; |
use Evenement\EventEmitter; |
use Symfony\Component\Process\Exception\RuntimeException as ProcessRuntimeException; |
|
class ProcessRunnerTest extends BinaryDriverTestCase |
{ |
public function getProcessRunner($logger) |
{ |
return new ProcessRunner($logger, 'test-runner'); |
} |
|
public function testRunSuccessFullProcess() |
{ |
$logger = $this->createLoggerMock(); |
$runner = $this->getProcessRunner($logger); |
|
$process = $this->createProcessMock(1, true, '--helloworld--', "Kikoo Romain", null, true); |
|
$logger |
->expects($this->never()) |
->method('error'); |
$logger |
->expects($this->exactly(2)) |
->method('info'); |
|
$this->assertEquals('Kikoo Romain', $runner->run($process, new \SplObjectStorage(), false)); |
} |
|
public function testRunSuccessFullProcessBypassingErrors() |
{ |
$logger = $this->createLoggerMock(); |
$runner = $this->getProcessRunner($logger); |
|
$process = $this->createProcessMock(1, true, '--helloworld--', "Kikoo Romain", null, true); |
|
$logger |
->expects($this->never()) |
->method('error'); |
$logger |
->expects($this->exactly(2)) |
->method('info'); |
|
$this->assertEquals('Kikoo Romain', $runner->run($process, new \SplObjectStorage(), true)); |
} |
|
public function testRunFailingProcess() |
{ |
$logger = $this->createLoggerMock(); |
$runner = $this->getProcessRunner($logger); |
|
$process = $this->createProcessMock(1, false, '--helloworld--', null, null, true); |
|
$logger |
->expects($this->once()) |
->method('error'); |
$logger |
->expects($this->once()) |
->method('info'); |
|
try { |
$runner->run($process, new \SplObjectStorage(), false); |
$this->fail('An exception should have been raised'); |
} catch (ExecutionFailureException $e) { |
|
} |
} |
|
public function testRunFailingProcessWithException() |
{ |
$logger = $this->createLoggerMock(); |
$runner = $this->getProcessRunner($logger); |
|
$exception = new ProcessRuntimeException('Process Failed'); |
$process = $this->getMockBuilder('Symfony\Component\Process\Process') |
->disableOriginalConstructor() |
->getMock(); |
$process->expects($this->once()) |
->method('run') |
->will($this->throwException($exception)); |
|
$logger |
->expects($this->once()) |
->method('error'); |
$logger |
->expects($this->once()) |
->method('info'); |
|
try { |
$runner->run($process, new \SplObjectStorage(), false); |
$this->fail('An exception should have been raised'); |
} catch (ExecutionFailureException $e) { |
$this->assertEquals($exception, $e->getPrevious()); |
} |
} |
|
public function testRunfailingProcessBypassingErrors() |
{ |
$logger = $this->createLoggerMock(); |
$runner = $this->getProcessRunner($logger); |
|
$process = $this->createProcessMock(1, false, '--helloworld--', 'Hello output', null, true); |
|
$logger |
->expects($this->once()) |
->method('error'); |
$logger |
->expects($this->once()) |
->method('info'); |
|
$this->assertNull($runner->run($process, new \SplObjectStorage(), true)); |
} |
|
public function testRunFailingProcessWithExceptionBypassingErrors() |
{ |
$logger = $this->createLoggerMock(); |
$runner = $this->getProcessRunner($logger); |
|
$exception = new ProcessRuntimeException('Process Failed'); |
$process = $this->getMockBuilder('Symfony\Component\Process\Process') |
->disableOriginalConstructor() |
->getMock(); |
$process->expects($this->once()) |
->method('run') |
->will($this->throwException($exception)); |
|
$logger |
->expects($this->once()) |
->method('error'); |
$logger |
->expects($this->once()) |
->method('info'); |
|
$this->assertNull($runner->run($process, new \SplObjectStorage(), true)); |
} |
|
public function testRunSuccessFullProcessWithHandlers() |
{ |
$logger = $this->createLoggerMock(); |
$runner = $this->getProcessRunner($logger); |
|
$capturedCallback = null; |
|
$process = $this->createProcessMock(1, true, '--helloworld--', "Kikoo Romain", null, true); |
$process->expects($this->once()) |
->method('run') |
->with($this->isInstanceOf('Closure')) |
->will($this->returnCallback(function ($callback) use (&$capturedCallback) { |
$capturedCallback = $callback; |
})); |
|
$logger |
->expects($this->never()) |
->method('error'); |
$logger |
->expects($this->exactly(2)) |
->method('info'); |
|
$listener = new TestListener(); |
$storage = new \SplObjectStorage(); |
$storage->attach($listener); |
|
$capturedType = $capturedData = null; |
|
$listener->on('received', function ($type, $data) use (&$capturedType, &$capturedData) { |
$capturedData = $data; |
$capturedType = $type; |
}); |
|
$this->assertEquals('Kikoo Romain', $runner->run($process, $storage, false)); |
|
$type = 'err'; |
$data = 'data'; |
|
$capturedCallback($type, $data); |
|
$this->assertEquals($data, $capturedData); |
$this->assertEquals($type, $capturedType); |
} |
} |
|
class TestListener extends EventEmitter implements ListenerInterface |
{ |
public function handle($type, $data) |
{ |
return $this->emit('received', array($type, $data)); |
} |
|
public function forwardedEvents() |
{ |
return array(); |
} |
} |