diff --git a/bin/idephix.phar b/bin/idephix.phar index f0bbbde..3e619d6 100755 Binary files a/bin/idephix.phar and b/bin/idephix.phar differ diff --git a/docs/writing_tasks.rst b/docs/writing_tasks.rst index 177c792..aaf03a2 100644 --- a/docs/writing_tasks.rst +++ b/docs/writing_tasks.rst @@ -324,7 +324,7 @@ Writing output to the console ----------------------------- Idephix is based on Symfony console component so you can send output to the user using the -``\Symfony\Component\Console\Output\OutputInterface``. You can get the full ``OutputInterface`` component +``\Symfony\Component\Console\Style\SymfonyStyle``. You can get the full ``SymfonyStyle`` component through the ``\Idephix\TaskExecutor::output`` method or you can use the shortcut methods: ``\Idephix\TaskExecutor::write`` and ``\Idephix\TaskExecutor::writeln``. @@ -343,11 +343,31 @@ Here is an example of you you can send some output to the console. { $context->writeln(strtoupper($what)); $context->write(strtoupper($what) . PHP_EOL); - $context->output()->write(strtoupper($what) . PHP_EOL); - $context->output()->writeln(strtoupper($what)); + + $output = $idx->output(); + + // common output elements + $output->title($what); + $output->section($what); + $output->text($what); + $output->comment($what); + $output->note($what); + $output->caution($what); + $output->listing([$what, $what, $what]); + $output->success($what); + $output->error($what); + $output->warning($what); + + //table + $headers = ['Parameter', 'Value', 'Value 3']; + $rows = [ + ['Param1', 'Value1', 'Value 3'], + ['Param2', 'Value2', 'Value 3'] + ]; + $output->table($headers, $rows); } .. hint:: - For more information about ``OutputInterface`` read the official - component `documentation `_ + For more information about ``SymfonyStyle`` read the official + component `documentation `_ diff --git a/src/Idephix/Builder.php b/src/Idephix/Builder.php index 8e7150d..5d85885 100644 --- a/src/Idephix/Builder.php +++ b/src/Idephix/Builder.php @@ -3,6 +3,7 @@ namespace Idephix; use Idephix\Task\Task; +use Idephix\Extension\Extension; interface Builder { diff --git a/src/Idephix/Config.php b/src/Idephix/Config.php index a33bb4e..f1b190c 100644 --- a/src/Idephix/Config.php +++ b/src/Idephix/Config.php @@ -20,7 +20,7 @@ private function __construct(Dictionary $dictionary) $this->dictionary = $dictionary; } - public static function fromArray($data) + public static function fromArray(array $data) { $resolver = new OptionsResolver(); $resolver->setDefaults(array( @@ -47,7 +47,11 @@ function ($envData) { throw new InvalidConfigurationException("Each env must be an array \"$envData\" given'"); } - $envData['hosts'] = empty($envData['hosts']) ? array(null) : $envData['hosts']; + if (empty($envData['hosts'])) { + $envData['hosts'] = new \ArrayIterator(); + } else { + $envData['hosts'] = new \ArrayIterator($envData['hosts']); + } $sshParamsResolver = new OptionsResolver(); $sshParamsResolver->setDefaults( @@ -60,7 +64,7 @@ function ($envData) { 'ssh_port' => '22' ) ); - + $envData['ssh_params'] = $sshParamsResolver->resolve( empty($envData['ssh_params']) ? array() : $envData['ssh_params'] ); @@ -84,7 +88,7 @@ public static function parseFile($configFile) if (is_null($configFile)) { return static::dry(); } - + try { new \SplFileObject($configFile); } catch (\RuntimeException $e) { @@ -136,6 +140,18 @@ public function get($offset, $default = null) return $this->dictionary->get($offset, $default); } + /** + * Add trailing slash to the path if it is omitted + * + * @param string $name + * @param string $default + * @return string fixed path + */ + public function getAsPath($name, $default = '') + { + return rtrim($this->get($name, $default), '/').'/'; + } + public function set($key, $value) { $this->dictionary->set($key, $value); diff --git a/src/Idephix/Console/Application.php b/src/Idephix/Console/Application.php index bdf949f..0d7e195 100644 --- a/src/Idephix/Console/Application.php +++ b/src/Idephix/Console/Application.php @@ -2,12 +2,17 @@ namespace Idephix\Console; +use Idephix\Context; +use Idephix\TaskExecutor; +use Idephix\Task\Task; +use Idephix\Task\TaskCollection; +use Idephix\Exception\FailedCommandException; use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Command\HelpCommand; use Idephix\Console\Command\ListCommand; -class Application extends BaseApplication +class Application extends BaseApplication implements TaskExecutor { private $logo = <<<'EOD' @@ -23,13 +28,25 @@ class Application extends BaseApplication private $releaseDate; + private $output; + + private $input; + + private $tasks; + public function __construct( $name = 'UNKNOWN', $version = 'UNKNOWN', - $releaseDate = 'UNKNOWN') + $releaseDate = 'UNKNOWN', + $input, + $output) { parent::__construct($name, $version); + $this->input = $input; + $this->output = $output; + $this->tasks = TaskCollection::dry(); + $this->releaseDate = $releaseDate; $this->setAutoExit(false); @@ -65,4 +82,97 @@ protected function getDefaultCommands() { return array(new HelpCommand(), new ListCommand()); } + + public function addTask(Task $task, Context $ctx) + { + $this->tasks[] = $task; + $this->add(Command::fromTask($task, $ctx)); + + return $this; + } + + public function hasTask($name) + { + return $this->tasks->has($name) && $this->has($name); + } + + public function runContext(Context $ctx) + { + $this->selectEnvironment($ctx); + + if (!$ctx->getEnv()) { + return $this->runNoEnv(); + } + + return $this->runEnv($ctx); + } + + public function runTask($name, $arguments = array()) + { + $inputFactory = new InputFactory(); + + $input = $inputFactory->buildFromUserArgsForTask( + $arguments, + $this->tasks->get($name) + ); + + return $this->get($name)->run($input, $this->output); + } + + private function runNoEnv() + { + $returnValue = $this->run($this->input, $this->output); + + $hasErrors = !(is_null($returnValue) || ($returnValue == 0)); + + if ($hasErrors) { + throw new FailedCommandException(); + } + } + + private function runEnv(Context $ctx) + { + $hosts = $ctx->getHosts(); + + $hasErrors = false; + + while ($hosts->valid()) { + $ctx->openRemoteConnection($hosts->current()); + $returnValue = $this->run($this->input, $this->output); + $hasErrors = $hasErrors || !(is_null($returnValue) || ($returnValue == 0)); + $ctx->closeRemoteConnection(); + + $hosts->next(); + } + + if ($hasErrors) { + throw new FailedCommandException(); + } + } + + protected function selectEnvironment(Context $context) + { + $environments = $context->getConfig()->environments(); + + if (!$this->input->hasParameterOption('--env')) { + return; + } + + $userDefinedEnv = $this->input->getParameterOption(array('--env')); + + if (!isset($environments[$userDefinedEnv])) { + $msg = sprintf( + 'Wrong environment "%s". Available [%s]', + $userDefinedEnv, + implode(', ', array_keys($environments)) + ); + + $this->output + ->writeln(''.$msg.''); + + exit(1); + } + + $context->setEnv($userDefinedEnv); + } } diff --git a/src/Idephix/Console/Command.php b/src/Idephix/Console/Command.php index 392dae1..7e57325 100644 --- a/src/Idephix/Console/Command.php +++ b/src/Idephix/Console/Command.php @@ -1,9 +1,9 @@ name()); $command->task = $task; - $command->idx = $idx; + $command->ctx = $ctx; $command->setDescription($task->description()); @@ -51,14 +44,15 @@ public static function fromTask(Task $task, TaskExecutor $idx) ); } - $command->idxTaskCode = $task->code(); - return $command; } protected function execute(InputInterface $input, OutputInterface $output) { - return call_user_func_array($this->idxTaskCode, $this->extractArgumentsFrom($input)); + return call_user_func_array( + $this->task->code(), + $this->extractArgumentsFrom($input) + ); } /** @@ -78,7 +72,7 @@ protected function extractArgumentsFrom(InputInterface $input) /** @var Parameter\UserDefined $parameter */ foreach ($this->task->parameters() as $parameter) { if ($parameter instanceof Parameter\Context) { - $args[] = $this->idx->getContext(); + $args[] = $this->ctx; continue; } diff --git a/src/Idephix/Console/InputFactory.php b/src/Idephix/Console/InputFactory.php index 07d2dfa..be7a407 100644 --- a/src/Idephix/Console/InputFactory.php +++ b/src/Idephix/Console/InputFactory.php @@ -13,7 +13,8 @@ class InputFactory */ public function buildFromUserArgsForTask($arguments, Task $task) { - $defaultArguments = array('command' => null); + $defaultArguments = array(); + $inputArguments = array(); foreach ($task->userDefinedParameters() as $parameter) { if ($parameter->isFlagOption()) { @@ -24,7 +25,11 @@ public function buildFromUserArgsForTask($arguments, Task $task) } $values = array_replace(array_values($defaultArguments), $arguments); - $inputArguments = array_combine(array_keys($defaultArguments), $values); + + // @todo: remove in php 5.3 + if (!empty($values)) { + $inputArguments = array_combine(array_keys($defaultArguments), $values); + } $input = new ArrayInput($inputArguments); diff --git a/src/Idephix/Context.php b/src/Idephix/Context.php index 221a728..5e56644 100644 --- a/src/Idephix/Context.php +++ b/src/Idephix/Context.php @@ -1,90 +1,121 @@ data = $data; - $this->idx = $idx; + $this->executor = $executor; + $this->operations = $op; + $this->config = $config; } - public static function dry(TaskExecutor $idx) + public static function create(TaskExecutor $executor, Config $config, Operations $op) { - return new static(Dictionary::dry(), $idx); + return new static($executor, $config, $op); } - public function env($name, Dictionary $contextData) + public function getConfig() { - $context = clone $this; - $contextData['env'] = array('name' => $name); - $context->data = $contextData; - - return $context; + return $this->config; } - public function currentEnvName() + public function setEnv($env) { - return $this->data['env.name']; + $this->currentEnv = $env; } - public function currentHost() + public function getEnv() { - return $this->data['env.host']; + return $this->currentEnv; } - /** - * Add trailing slash to the path if it is omitted - * - * @param string $name - * @param string $default - * @return string fixed path - */ - public function getAsPath($name, $default = '') + public function getHosts() { - return rtrim($this->get($name, $default), '/').'/'; + $this->assertEnv(); + + return $this->config + ->get("envs.{$this->currentEnv}.hosts"); } - public function offsetExists($offset) + public function getCurrentHost() { - return $this->data->offsetExists($offset); + $this->assertEnv(); + + return $this->config + ->get("envs.{$this->currentEnv}.hosts") + ->current(); } - public function offsetGet($offset) + public function getSshParams() { - return $this->data->offsetGet($offset); + $this->assertEnv(); + + return $this->config + ->get("envs.{$this->currentEnv}.ssh_params"); } - public function offsetSet($offset, $value) + public function openRemoteConnection($host) { - $this->data->offsetSet($offset, $value); + $this->assertEnv(); + + $sshParams = $this->config + ->get("envs.{$this->currentEnv}.ssh_params"); + + $this->operations + ->openRemoteConnection($this->getCurrentHost(), $sshParams); } - public function offsetUnset($offset) + public function closeRemoteConnection() { - $this->data->offsetUnset($offset); + $this->operations + ->closeRemoteConnection(); } - public function get($offset, $default = null) + public function __call($name, $arguments) { - return $this->data->get($offset, $default); + if ($this->executor->hasTask($name)) { + return $this->executor->runTask($name, $arguments); + } + + return $this->operations->execute($name, $arguments); } - public function set($key, $value) + /** + * @param $name + * @return integer 0 success, 1 fail + */ + public function execute($name) { - $this->data->offsetSet($key, $value); + $args = func_get_args(); + array_shift($args); + return call_user_func_array(array($this, '__call'), array($name, $args)); } /** * @param $name * @return integer 0 success, 1 fail */ - public function execute($name) + public function executeOnce($name) { - call_user_func_array(array($this->idx, 'execute'), func_get_args()); + if (array_key_exists($name, $this->executed)) { + return $this->executed[$name]; + } + + $args = func_get_args(); + array_shift($args); + $this->executed[$name] = call_user_func_array(array($this, '__call'), array($name, $args)); + + return $this->executed[$name]; } /** @@ -96,7 +127,7 @@ public function execute($name) */ public function remote($cmd, $dryRun = false) { - $this->idx->remote($cmd, $dryRun); + $this->operations->remote($cmd, $dryRun); } /** @@ -110,96 +141,28 @@ public function remote($cmd, $dryRun = false) */ public function local($cmd, $dryRun = false, $timeout = 60) { - return $this->idx->local($cmd, $dryRun, $timeout); + return $this->operations->local($cmd, $dryRun, $timeout); } public function output() { - return $this->idx->output(); - } - - public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) - { - $this->idx->write($messages, $newline, $type); - } - - public function writeln($messages, $type = self::OUTPUT_NORMAL) - { - $this->idx->writeln($messages, $type); - } - - public function sshClient() - { - return $this->idx->sshClient(); - } - - public function __call($name, $arguments) - { - return call_user_func_array(array($this->idx, $name), $arguments); - } - - /** - * Return the current element - * @link http://php.net/manual/en/iterator.current.php - * @return mixed Can return any type. - * @since 5.0.0 - */ - public function current() - { - $newContextData = clone $this->data; - $newContextData['env'] = array( - 'name' => $this->currentEnvName(), - 'host' => current($this->hosts) - ); - - $newContext = new static($newContextData, $this->idx); - - return $newContext; + return $this->operations->output(); } - /** - * Move forward to next element - * @link http://php.net/manual/en/iterator.next.php - * @return void Any returned value is ignored. - * @since 5.0.0 - */ - public function next() + public function write($messages, $newline = false, $type = OutputInterface::OUTPUT_NORMAL) { - next($this->hosts); + $this->operations->write($messages, $newline, $type); } - /** - * Return the key of the current element - * @link http://php.net/manual/en/iterator.key.php - * @return mixed scalar on success, or null on failure. - * @since 5.0.0 - */ - public function key() + public function writeln($messages, $type = OutputInterface::OUTPUT_NORMAL) { - return key($this->hosts); + $this->operations->writeln($messages, $type); } - /** - * Checks if current position is valid - * @link http://php.net/manual/en/iterator.valid.php - * @return boolean The return value will be casted to boolean and then evaluated. - * Returns true on success or false on failure. - * @since 5.0.0 - */ - public function valid() - { - return $this->key() !== null; - } - - /** - * Rewind the Iterator to the first element - * @link http://php.net/manual/en/iterator.rewind.php - * @return void Any returned value is ignored. - * @since 5.0.0 - */ - public function rewind() + private function assertEnv() { - $this->hosts = $this->data->get('hosts', array(null)); - reset($this->hosts); + if (!$this->currentEnv) { + throw new \RunTimeException('Missing env param'); + } } } diff --git a/src/Idephix/Extension.php b/src/Idephix/Extension.php deleted file mode 100644 index 4a66c6f..0000000 --- a/src/Idephix/Extension.php +++ /dev/null @@ -1,18 +0,0 @@ -callable = $callable; } - public function name() { return $this->name; diff --git a/src/Idephix/Extension/ContextAwareInterface.php b/src/Idephix/Extension/ContextAwareInterface.php new file mode 100644 index 0000000..ea1503c --- /dev/null +++ b/src/Idephix/Extension/ContextAwareInterface.php @@ -0,0 +1,10 @@ +getInnerIterator() as $method) { + if ($method->name() == $methodName) { return call_user_func_array($method, $args); } diff --git a/src/Idephix/Extension/MethodProvider.php b/src/Idephix/Extension/MethodProvider.php new file mode 100644 index 0000000..f6af59d --- /dev/null +++ b/src/Idephix/Extension/MethodProvider.php @@ -0,0 +1,11 @@ +idx = $idx; + $this->ctx = $ctx; } public function name() @@ -33,32 +33,17 @@ public function methods() { return MethodCollection::ofCallables( array( - new Extension\CallableMethod('rsyncProject', array($this, 'rsyncProject')) + new CallableMethod('rsyncProject', array($this, 'rsyncProject')) ) ); } - /** @return TaskCollection */ - public function tasks() - { - return TaskCollection::dry(); - } - public function rsyncProject($remoteDir, $localDir = null, $exclude = null, $extraOpts = null) { if (substr($remoteDir, -1) != '/') { $remoteDir .= '/'; } - $context = $this->idx->getContext(); - - if ($context->currentHost() === null) { - throw new \InvalidArgumentException('A host must be selected, invalid Context provided'); - } - - - $port = $context->get('ssh_params.port'); - if (file_exists($exclude)) { $extraOpts .= ' --exclude-from='.escapeshellarg($exclude); } elseif (!empty($exclude)) { @@ -69,15 +54,23 @@ public function rsyncProject($remoteDir, $localDir = null, $exclude = null, $ext } $sshCmd = 'ssh'; + + $sshParams = $this->ctx->getSshParams(); + $port = isset($sshParams['port']) ? $sshParams['port'] : 22; + $user = isset($sshParams['user']) ? $sshParams['user'] : null; + if ($port) { $sshCmd .= ' -p ' . $port; } - $remoteConnection = $this->connectionString($context->currentHost(), $context->get('ssh_params.user')); + $remoteConnection = $this->connectionString( + $this->ctx->getCurrentHost(), + $user + ); $cmd = "rsync -rlDcz --force --delete --progress $extraOpts -e '$sshCmd' $localDir $remoteConnection:$remoteDir"; - return $this->idx->local($cmd); + return $this->ctx->local($cmd); } /** diff --git a/src/Idephix/Extension/Slack/Slack.php b/src/Idephix/Extension/Slack/Slack.php index c2e3e44..737f4d2 100644 --- a/src/Idephix/Extension/Slack/Slack.php +++ b/src/Idephix/Extension/Slack/Slack.php @@ -2,23 +2,19 @@ namespace Idephix\Extension\Slack; -use Idephix\Extension; +use Idephix\Extension\MethodProvider; use Idephix\Extension\MethodCollection; -use Idephix\Idephix; -use Idephix\Extension\IdephixAwareInterface; -use Idephix\Task\TaskCollection; +use Idephix\Extension\ContextAwareInterface; +use Idephix\Context; /** * Description of Slack wrapper * * @author dymissy */ -class Slack implements IdephixAwareInterface, Extension +class Slack implements ContextAwareInterface, MethodProvider { - /** - * @var \Idephix\Context - */ - private $idx; + private $ctx; private $settings; @@ -34,17 +30,6 @@ public function __construct($args = array()) $this->settings = array_merge($defaults, $args); } - /** @return TaskCollection */ - public function tasks() - { - return TaskCollection::dry(); - } - - public function name() - { - return 'slack'; - } - public function sendToSlack($message, $attachments = array(), $channel = '', $icon_url = '', $username = '') { if (!$channel) { @@ -75,7 +60,7 @@ public function sendToSlack($message, $attachments = array(), $channel = '', $ic throw new \Exception('Unable to send the message to Slack. The error returned is: ' . $response); } - $this->idx->local("echo 'Message sent to slack channel'"); + $this->ctx->local("echo 'Message sent to slack channel'"); return $response; } @@ -92,9 +77,14 @@ protected function send($data, $url) return $result; } - public function setIdephix(Idephix $idx) + public function setContext(Context $ctx) + { + $this->ctx = $ctx; + } + + public function name() { - $this->idx = $idx; + return 'slack'; } /** @return MethodCollection */ diff --git a/src/Idephix/Extension/TaskProvider.php b/src/Idephix/Extension/TaskProvider.php new file mode 100644 index 0000000..1956f5e --- /dev/null +++ b/src/Idephix/Extension/TaskProvider.php @@ -0,0 +1,13 @@ +inputOrDefault($input); + $output = $this->outputOrDefault($input, $output); $this->config = $config; - $this->context = Context::dry($this); - $this->tasks = TaskCollection::dry(); - $this->extensionsMethods = MethodCollection::dry(); + $this->operations = new Operations($config['ssh_client'], $output); - $this->application = new Application( + $this->executor = new Application( 'Idephix', self::VERSION, - self::RELEASE_DATE - ); - - $this->sshClient = $config['ssh_client']; - - $this->output = $this->outputOrDefault($output); - $this->input = $this->inputOrDefault($input); - - $this->addSelfUpdateCommand(); - $this->addInitIdxFileCommand(); + self::RELEASE_DATE, + $input, + $output + ); - foreach ($config->extensions() as $extension) { - $this->addExtension($extension); - } - } + $this->context = new Context($this->executor, $this->operations, $this->config); - public static function create(TaskCollection $tasks, Config $config) - { - $idephix = new static($config); + $this->addSelfUpdateCommand($this->context); + $this->addInitIdxFileCommand($this->context); foreach ($tasks as $task) { - $idephix->addTask($task); - } - - return $idephix; - } - - public function output() - { - return $this->output; - } - - public function __call($name, $arguments = array()) - { - if ($this->has($name)) { - return call_user_func_array(array($this, 'execute'), array_merge(array($name), $arguments)); + $this->addTask($task); } - try { - return $this->extensionsMethods->execute($name, $arguments); - } catch (MissingMethodException $e) { - throw new \BadMethodCallException('Call to undefined method: "' . $name . '"'); + foreach ($config->extensions() as $extension) { + $this->addExtension($extension, $this->context); } } - public function __get($name) + public function run() { - if ($name === 'output' || $name === 'sshClient') { - return $this->$name; - } - - $trace = debug_backtrace(); - trigger_error( - 'Undefined property: '.$name. - ' in '.$trace[0]['file']. - ' on line '.$trace[0]['line'], - E_USER_NOTICE - ); - - return null; + $this->executor->runContext($this->context); } - /** - * @inheritdoc - */ public function addTask(Task $task) { - $this->tasks[] = $task; - $this->application->add(Command::fromTask($task, $this)); - - return $this; - } - - /** - * @param InputInterface $input - * @throws \Exception - */ - protected function buildEnvironment(InputInterface $input) - { - $environments = $this->config->environments(); - - $userDefinedEnv = $input->getParameterOption(array('--env')); - - if (false !== $userDefinedEnv && !empty($userDefinedEnv)) { - if (!isset($environments[$userDefinedEnv])) { - throw new \Exception( - sprintf( - 'Wrong environment "%s". Available [%s]', - $userDefinedEnv, - implode(', ', array_keys($environments)) - ) - ); - } - - $this->context = Context::dry($this) - ->env( - $userDefinedEnv, - Dictionary::fromArray( - array_merge( - array('hosts' => array()), - $environments[$userDefinedEnv] - ) - ) - ); - } - } - - protected function openRemoteConnection($host) - { - if (!is_null($host)) { - $this->sshClient->setParameters($this->context['ssh_params']); - $this->sshClient->setHost($host); - $this->sshClient->connect(); - } - } - - protected function closeRemoteConnection() - { - if ($this->sshClient->isConnected()) { - $this->sshClient->disconnect(); - } - } - - public function getContext() - { - return $this->context; - } - - public function run() - { - try { - $this->buildEnvironment($this->input); - } catch (\Exception $e) { - $this->output->writeln(''.$e->getMessage().''); - - return; - } - - $hasErrors = false; - foreach ($this->context as $hostContext) { - $this->context = $hostContext; - $this->openRemoteConnection($hostContext->currentHost()); - $returnValue = $this->application->run($this->input, $this->output); - $hasErrors = $hasErrors || !(is_null($returnValue) || ($returnValue == 0)); - $this->closeRemoteConnection(); - } - - if ($hasErrors) { - throw new FailedCommandException(); - } + $this->executor->addTask($task, $this->context); } - /** - * @inheritdoc - */ public function addExtension(Extension $extension) { - if ($extension instanceof IdephixAwareInterface) { - $extension->setIdephix($this); + if ($extension instanceof ContextAwareInterface) { + $extension->setContext($this->context); } - $this->extensionsMethods = $this->extensionsMethods->merge($extension->methods()); - - foreach ($extension->tasks() as $task) { - if (!$this->has($task->name())) { - $this->addTask($task); + if ($extension instanceof TaskProvider) { + foreach ($extension->tasks() as $task) { + $this->addTask($task, $this->context); } } - } - /** - * @param $name - * @param $extension - * @deprecated should use addExtension instead - */ - public function addLibrary($name, $extension) - { - $this->addExtension($name, $extension); - } - - /** - * @param string $name - * @return bool - */ - public function has($name) - { - return $this->tasks->has($name) && $this->application->has($name); - } - - /** - * RunTask. - * - * @param string $name the name of the task you want to call - * @param (...) arbitrary number of parameter matching the target task interface - * @return integer - * @deprecated should call directly tasks as Idephix methods - */ - public function execute($name) - { - if (!$this->has($name)) { - throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name)); + if ($extension instanceof MethodProvider) { + $this->operations->addMethods($extension->methods()); } - - $inputFactory = new InputFactory(); - - return $this->application->get($name)->run( - $inputFactory->buildFromUserArgsForTask(func_get_args(), $this->tasks->get($name)), - $this->output - ); } - public function addSelfUpdateCommand() + public function addSelfUpdateCommand($ctx) { if ('phar:' === substr(__FILE__, 0, 5)) { $selfUpdate = new SelfUpdate(); - $selfUpdate->setIdephix($this); - $this->addTask($selfUpdate); - } - } - - public function addInitIdxFileCommand() - { - $init = InitIdxFile::fromDeployRecipe(); - $init->setIdephix($this); - $this->addTask($init); - } - - /** - * Execute remote command. - * - * @param string $cmd command - * @param boolean $dryRun - * @throws \Exception - */ - public function remote($cmd, $dryRun = false) - { - if (!$this->sshClient->isConnected()) { - throw new \Exception('Remote function need a valid environment. Specify --env parameter.'); - } - $this->output->writeln('Remote: '.$cmd); - - if (!$dryRun && !$this->sshClient->exec($cmd)) { - throw new \Exception('Remote command fail: '.$this->sshClient->getLastError()); - } - $this->output->writeln($this->sshClient->getLastOutput()); - } - - /** - * Execute local command. - * - * @param string $cmd Command - * @param bool $dryRun - * @param int $timeout - * - * @return string the command output - * @throws \Exception - */ - public function local($cmd, $dryRun = false, $timeout = 600) - { - $output = $this->output; - $output->writeln("Local: $cmd"); + $selfUpdate->setContext($ctx); - if ($dryRun) { - return $cmd; + $this->executor->addTask($selfUpdate, $ctx); } - - $process = $this->buildInvoker($cmd, null, null, null, $timeout); - - $result = $process->run(function ($type, $buffer) use ($output) { - $output->write($buffer); - }); - if (0 != $result) { - throw new \Exception('Local command fail: '.$process->getErrorOutput()); - } - - return $process->getOutput(); } - /** - * Set local command invoker - * @param string $invokerClassName class name of the local command invoker - */ - public function setInvoker($invokerClassName) + public function addInitIdxFileCommand($ctx) { - $this->invokerClassName = $invokerClassName; - } - - /** - * Build command invoker - * @param string $cmd The command line to run - * @param string $cwd The working directory - * @param array $env The environment variables or null to inherit - * @param string $stdin The STDIN content - * @param integer $timeout The timeout in seconds - * @param array $options An array of options for proc_open - * - * @return string cmd output - */ - public function buildInvoker($cmd, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array()) - { - $invoker = $this->invokerClassName ?: '\Symfony\Component\Process\Process'; + $init = InitIdxFile::fromDeployRecipe(); + $init->setContext($ctx); - return new $invoker($cmd, $cwd, $env, $stdin, $timeout, $options); - } - - /** - * Get application - * - * @return Application - */ - public function getApplication() - { - return $this->application; + $this->executor->addTask($init, $ctx); } protected function removeIdxCustomFileParams() @@ -384,48 +121,30 @@ protected function removeIdxCustomFileParams() } /** - * @return SshClient + * @param InputInterface $input + * @return ArgvInput|InputInterface */ - public function sshClient() + private function inputOrDefault(InputInterface $input = null) { - return $this->sshClient; - } + $this->removeIdxCustomFileParams(); - public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL) - { - $this->output()->write($messages, $newline, $type); - } + if (null === $input) { + $input = new ArgvInput(); + } - public function writeln($messages, $type = self::OUTPUT_NORMAL) - { - $this->output()->writeln($messages, $type); + return $input; } /** * @param OutputInterface $output - * @return ConsoleOutput|OutputInterface + * @return SymfonyStyle|OutputInterface */ - private function outputOrDefault(OutputInterface $output = null) + private function outputOrDefault($input, OutputInterface $output = null) { if (null === $output) { - $output = new ConsoleOutput(); + $output = new SymfonyStyle($input, new ConsoleOutput()); } return $output; } - - /** - * @param InputInterface $input - * @return ArgvInput|InputInterface - */ - private function inputOrDefault(InputInterface $input = null) - { - $this->removeIdxCustomFileParams(); - - if (null === $input) { - $input = new ArgvInput(); - } - - return $input; - } } diff --git a/src/Idephix/Operations.php b/src/Idephix/Operations.php new file mode 100644 index 0000000..c2448a0 --- /dev/null +++ b/src/Idephix/Operations.php @@ -0,0 +1,143 @@ +sshClient = $sshClient; + $this->output = $output; + $this->methods = MethodCollection::dry(); + } + + public function openRemoteConnection($host, $params) + { + if (!is_null($host)) { + $this->sshClient->setParameters($params); + $this->sshClient->setHost($host); + $this->sshClient->connect(); + } + } + + public function closeRemoteConnection() + { + if ($this->sshClient->isConnected()) { + $this->sshClient->disconnect(); + } + } + + /** + * Execute remote command. + * + * @param string $cmd command + * @param boolean $dryRun + * @throws \Exception + */ + public function remote($cmd, $dryRun = false) + { + if (!$this->sshClient->isConnected()) { + throw new \Exception('Remote function need a valid environment. Specify --env parameter.'); + } + $this->output->writeln('Remote: '.$cmd); + + if (!$dryRun && !$this->sshClient->exec($cmd)) { + throw new \Exception('Remote command fail: '.$this->sshClient->getLastError()); + } + $this->output->writeln($this->sshClient->getLastOutput()); + } + + /** + * Execute local command. + * + * @param string $cmd Command + * @param bool $dryRun + * @param int $timeout + * + * @return string the command output + * @throws \Exception + */ + public function local($cmd, $dryRun = false, $timeout = 600) + { + $output = $this->output; + $output->writeln("Local: $cmd"); + + if ($dryRun) { + return $cmd; + } + + $process = $this->buildInvoker($cmd, null, null, null, $timeout); + + $result = $process->run(function ($type, $buffer) use ($output) { + $output->write($buffer); + }); + if (0 != $result) { + throw new \Exception('Local command fail: '.$process->getErrorOutput()); + } + + return $process->getOutput(); + } + + /** + * Set local command invoker + * @param string $invokerClassName class name of the local command invoker + */ + public function setInvoker($invokerClassName) + { + $this->invokerClassName = $invokerClassName; + } + + /** + * Build command invoker + * @param string $cmd The command line to run + * @param string $cwd The working directory + * @param array $env The environment variables or null to inherit + * @param string $stdin The STDIN content + * @param integer $timeout The timeout in seconds + * @param array $options An array of options for proc_open + * + * @return string cmd output + */ + public function buildInvoker($cmd, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array()) + { + $invoker = $this->invokerClassName ?: '\Symfony\Component\Process\Process'; + + return new $invoker($cmd, $cwd, $env, $stdin, $timeout, $options); + } + + public function output() + { + return $this->output; + } + + public function write($messages, $newline = false, $type = OutputInterface::OUTPUT_NORMAL) + { + $this->output->write($messages, $newline, $type); + } + + public function writeln($messages, $type = OutputInterface::OUTPUT_NORMAL) + { + $this->output->writeln($messages, $type); + } + + public function addMethods(MethodCollection $methods) + { + $this->methods = $this->methods->merge($methods); + } + + public function execute($name, $arguments) + { + return $this->methods->execute($name, $arguments); + } +} diff --git a/src/Idephix/Task/Builtin/InitIdxFile.php b/src/Idephix/Task/Builtin/InitIdxFile.php index 1ad0bef..1ae4dff 100644 --- a/src/Idephix/Task/Builtin/InitIdxFile.php +++ b/src/Idephix/Task/Builtin/InitIdxFile.php @@ -1,15 +1,14 @@ ctx = $ctx; + } + public function name() { return 'initFile'; @@ -46,12 +50,12 @@ public function description() public function parameters() { - return Collection::dry(); + return Parameter\Collection::dry(); } public function userDefinedParameters() { - return new UserDefinedCollection($this->parameters()); + return new Parameter\UserDefinedCollection($this->parameters()); } public function code() @@ -59,9 +63,6 @@ public function code() return array($this, 'initFile'); } - /** - * Based by composer self-update - */ public function initFile() { $this->initIdxFile(); @@ -84,25 +85,17 @@ private function writeFile($filename, $data) { $idxFile = $this->baseDir . DIRECTORY_SEPARATOR . $filename; if (file_exists($idxFile)) { - $this->idx->output->writeln("An {$filename} already exists, generation skipped."); + $this->ctx->writeln("An {$filename} already exists, generation skipped."); return; } - $this->idx->output->writeln("Creating basic {$filename} file..."); + $this->ctx->writeln("Creating basic {$filename} file..."); if (!is_writable($this->baseDir) || false === file_put_contents($idxFile, $data)) { throw new \Exception("Cannot write {$filename}, check your permission configuration."); } - $this->idx->output->writeln("{$filename} file created."); - } - - /** - * @param Idephix $idx - */ - public function setIdephix(Idephix $idx) - { - $this->idx = $idx; + $this->ctx->writeln("{$filename} file created."); } } diff --git a/src/Idephix/Task/Builtin/SelfUpdate.php b/src/Idephix/Task/Builtin/SelfUpdate.php index 0495ae8..0a4eea4 100644 --- a/src/Idephix/Task/Builtin/SelfUpdate.php +++ b/src/Idephix/Task/Builtin/SelfUpdate.php @@ -4,16 +4,17 @@ use Idephix\Task\Parameter; use Idephix\Idephix; -use Idephix\Extension\IdephixAwareInterface; use Idephix\Task\Task; +use Idephix\Extension\ContextAwareInterface; +use Idephix\Context; -class SelfUpdate implements IdephixAwareInterface, Task +class SelfUpdate implements ContextAwareInterface, Task { - private $idx; + private $ctx; - public function setIdephix(Idephix $idx) + public function setContext(Context $ctx) { - $this->idx = $idx; + $this->ctx = $ctx; } public function name() @@ -70,11 +71,11 @@ public function update() if (!$e instanceof \UnexpectedValueException && !$e instanceof \PharException) { throw $e; } - $this->idx->output->writeln('The download is corrupted ('.$e->getMessage().').'); - $this->idx->output->writeln('Please re-run the self-update command to try again.'); + $this->ctx->writeln('The download is corrupted ('.$e->getMessage().').'); + $this->ctx->writeln('Please re-run the self-update command to try again.'); } } else { - $this->idx->output->writeln('You are using the latest idephix version.'); + $this->ctx->writeln('You are using the latest idephix version.'); } } } diff --git a/src/Idephix/TaskExecutor.php b/src/Idephix/TaskExecutor.php index 952d049..2b4bd23 100644 --- a/src/Idephix/TaskExecutor.php +++ b/src/Idephix/TaskExecutor.php @@ -3,41 +3,9 @@ interface TaskExecutor { - const OUTPUT_NORMAL = 0; - const OUTPUT_RAW = 1; - const OUTPUT_PLAIN = 2; + public function runContext(Context $ctx); - /** - * @param $name - * @return integer 0 success, 1 fail - */ - public function execute($name); + public function runTask($name, $arguments = array()); - /** - * Execute remote command. - * - * @param string $cmd command - * @param boolean $dryRun - * @return void - */ - public function remote($cmd, $dryRun = false); - - /** - * Execute local command. - * - * @param string $cmd Command - * @param boolean $dryRun - * @param integer $timeout - * - * @return string the command output - */ - public function local($cmd, $dryRun = false, $timeout = 60); - - public function output(); - - public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL); - - public function writeln($messages, $type = self::OUTPUT_NORMAL); - - public function sshClient(); + public function hasTask($name); } diff --git a/src/Idephix/Test/DummyExtension.php b/src/Idephix/Test/DummyExtension.php index 490dc42..11da681 100644 --- a/src/Idephix/Test/DummyExtension.php +++ b/src/Idephix/Test/DummyExtension.php @@ -2,14 +2,15 @@ namespace Idephix\Test; -use Idephix\Extension; -use Idephix\Extension\IdephixAwareInterface; -use Idephix\Idephix; +use Idephix\Extension\MethodProvider; +use Idephix\Extension\TaskProvider; +use Idephix\Extension\ContextAwareInterface; +use Idephix\Context; use Idephix\Task\Parameter\Collection; use Idephix\Task\CallableTask; use Idephix\Task\TaskCollection; -class DummyExtension implements IdephixAwareInterface, Extension +class DummyExtension implements ContextAwareInterface, MethodProvider, TaskProvider { private $tester; private $name; @@ -20,9 +21,9 @@ public function __construct(\PHPUnit_Framework_TestCase $tester, $name) $this->name = $name; } - public function setIdephix(Idephix $idx) + public function setContext(Context $idx) { - $this->tester->assertTrue(true, 'Set Idephix'); + $this->tester->assertTrue(true, 'Set Context'); } public function test($return) @@ -35,10 +36,6 @@ public function update($what) return $this->doTest($what); } - /** - * @param $return - * @return mixed - */ private function doTest($return) { return $return; diff --git a/src/Idephix/bootstrap.php b/src/Idephix/bootstrap.php index f2e5bf9..b9bd34f 100644 --- a/src/Idephix/bootstrap.php +++ b/src/Idephix/bootstrap.php @@ -20,7 +20,11 @@ function run() if (is_file($idxFile)) { try { - $idx = Idephix::create(TaskCollection::parseFile($idxFile), Config::parseFile($configFile)); + $idx = new Idephix( + Config::parseFile($configFile), + TaskCollection::parseFile($idxFile) + ); + $idx->run(); return 0; @@ -34,6 +38,7 @@ function run() return 1; } - $idx = new Idephix(Config::dry()); + $idx = new Idephix(Config::dry(), TaskCollection::dry()); + return $idx->run(); } diff --git a/tests/idxfile.php b/tests/idxfile.php index dcccb4a..c099601 100644 --- a/tests/idxfile.php +++ b/tests/idxfile.php @@ -5,9 +5,9 @@ function _echo($value) echo $value; } -function greet(\Idephix\Context $idx, $name) +function greet(\Idephix\Context $ctx, $name) { - $idx->execute('echo', 'Ciao ' . $name); + $ctx->write('Ciao ' . $name); } function testParams($param1, $param2, $param3 = 'default') diff --git a/tests/unit/Idephix/ConfigTest.php b/tests/unit/Idephix/ConfigTest.php index f767245..74425c2 100644 --- a/tests/unit/Idephix/ConfigTest.php +++ b/tests/unit/Idephix/ConfigTest.php @@ -8,6 +8,7 @@ class ConfigTest extends \PHPUnit_Framework_TestCase public function it_should_have_defaults_for_config() { $config = Config::dry(); + $this->assertInstanceOf('Idephix\SSH\SshClient', $config['ssh_client']); $this->assertEquals(array(), $config['envs']); $this->assertEquals(array(), $config['extensions']); @@ -64,7 +65,7 @@ public function an_env_must_be_an_array_of_data() public function an_env_should_have_default_values() { $defaultEnvData = array( - 'hosts' => array(null), + 'hosts' => new \ArrayIterator(), 'ssh_params' => array( 'user' => '', 'password' => '', @@ -76,9 +77,11 @@ public function an_env_should_have_default_values() ); $config = Config::fromArray(array('envs' => array('prod' => array()))); + $this->assertEquals($defaultEnvData, $config['envs.prod']); $config = Config::fromArray(array('envs' => array('prod' => array('hosts' => array())))); + $this->assertEquals($defaultEnvData, $config['envs.prod']); } @@ -163,4 +166,24 @@ public function it_should_throw_exception_for_invalid_file() $configFile = '/tmp/foo-non-existing-file'; Config::parseFile($configFile); } + + /** + * @test + */ + public function it_should_allow_to_retrieve_value_as_path() + { + $conf = array( + 'envs' => array( + 'prod' => array( + 'foo' => '/var/www/', + 'bar' => '/var/www', + ) + ) + ); + + $config = Config::fromArray($conf); + + $this->assertEquals('/var/www/', $config->getAsPath('envs.prod.foo')); + $this->assertEquals('/var/www/', $config->getAsPath('envs.prod.bar')); + } } diff --git a/tests/unit/Idephix/Console/CommandTest.php b/tests/unit/Idephix/Console/CommandTest.php index 58e09cc..e78ada1 100644 --- a/tests/unit/Idephix/Console/CommandTest.php +++ b/tests/unit/Idephix/Console/CommandTest.php @@ -15,27 +15,34 @@ class CommandTest extends \PHPUnit_Framework_TestCase public function it_should_build_command() { $argumentsSpy = new \stdClass(); - $idephixTaskCode = function (Context $idx, $bar, $foo = 'foo-value', $go = false) use ($argumentsSpy) { + + $taskCode = function (Context $ctx, $bar, $foo = 'foo-value', $go = false) use ($argumentsSpy) { + $argumentsSpy->args = func_get_args(); - $idx->output()->write('task executed'); + + $ctx->output()->write('task executed'); }; $context = $this->mockContext(); - $task = $this->createTaskDefinition($idephixTaskCode); - $command = Command::fromTask($task->reveal(), $this->mockIdephix($context)); + $task = $this->createTaskDefinition($taskCode); + + $command = Command::fromTask($task->reveal(), $context); + + $def = $command->getDefinition(); $this->assertInstanceOf('\Symfony\Component\Console\Command\Command', $command); $this->assertEquals('yell', $command->getName()); $this->assertEquals('A command that yells at you', $command->getDescription()); - $this->assertEquals(2, $command->getDefinition()->getArgumentCount()); - $this->assertEquals(1, count($command->getDefinition()->getOptions())); + $this->assertEquals(2, $def->getArgumentCount()); + $this->assertEquals(1, count($def->getOptions())); + + $this->assertEquals('what you want me to yell', $def->getArgument('what')->getDescription()); + $this->assertEquals('The exclamation mark to use', $def->getArgument('exclamationMark')->getDescription()); + $this->assertEquals('!', $def->getArgument('exclamationMark')->getDefault()); + $this->assertEquals('Do you really want to yell out loud?', $def->getOption('loud')->getDescription()); + $this->assertFalse($def->getOption('loud')->getDefault()); - $this->assertEquals('what you want me to yell', $command->getDefinition()->getArgument('what')->getDescription()); - $this->assertEquals('The exclamation mark to use', $command->getDefinition()->getArgument('exclamationMark')->getDescription()); - $this->assertEquals('!', $command->getDefinition()->getArgument('exclamationMark')->getDefault()); - $this->assertEquals('Do you really want to yell out loud?', $command->getDefinition()->getOption('loud')->getDescription()); - $this->assertFalse($command->getDefinition()->getOption('loud')->getDefault()); $testApplication = new Application(); $testApplication->add($command); @@ -45,6 +52,7 @@ public function it_should_build_command() $commandTester->execute(array('command' => $command->getName(), 'what' => 'Say my name')); $expectedArguments = array($context, 'Say my name', '!', false); + $this->assertEquals('task executed', $commandTester->getDisplay()); $this->assertEquals($expectedArguments, $argumentsSpy->args); } @@ -81,12 +89,4 @@ private function createTaskDefinition($idephixTaskCode) $task->code()->willReturn($idephixTaskCode); return $task; } - - private function mockIdephix($context) - { - $idx = $this->prophesize('\Idephix\Idephix'); - $idx->getContext()->willReturn($context); - - return $idx->reveal(); - } } diff --git a/tests/unit/Idephix/Console/TaskExecutorTest.php b/tests/unit/Idephix/Console/TaskExecutorTest.php new file mode 100644 index 0000000..e754ce3 --- /dev/null +++ b/tests/unit/Idephix/Console/TaskExecutorTest.php @@ -0,0 +1,106 @@ +context = $this->prophesize('\Idephix\Context'); + $this->executor = new Application( + null, + null, + null, + $input, + $output + ); + } + + /** + * @test + */ + public function it_should_be_able_to_add_task_from_closure() + { + $task = CallableTask::buildFromClosure('commandName', function () {}); + + $this->executor->addTask($task, $this->context->reveal()); + + $this->assertTrue($this->executor->has('commandName')); + } + + /** + * @test + */ + public function it_should_be_able_to_add_task_from_object() + { + $task = new CallableTask('fooCommand', 'A dummy command', function () {}, Parameter\Collection::dry()); + + $this->executor->addTask($task, $this->context->reveal()); + + $this->assertTrue($this->executor->has('fooCommand')); + + $registeredCommands = $this->executor->all(); + + $this->assertArrayHasKey('fooCommand', $registeredCommands); + $this->assertInstanceOf('\Idephix\Console\Command', $registeredCommands['fooCommand']); + $this->assertEquals('fooCommand', $registeredCommands['fooCommand']->getName()); + } + + /** + * @test + */ + public function it_should_allow_to_invoke_tasks() + { + $task = CallableTask::buildFromClosure( + 'test', + function (Context $idx, $what, $go = false) { + if ($go) { + return $what * 2; + } + return 0; + } + ); + + $this->executor->addTask($task, $this->context->reveal()); + + $this->assertEquals(84, $this->executor->runTask('test', array(42, true))); + $this->assertEquals(0, $this->executor->runTask('test', array(42))); + } + + /** + * @test + */ + public function it_should_inject_context() + { + $task = CallableTask::buildFromClosure( + 'foo', + function (Context $ctx) { + $ctx->local('sleep 2', false); + } + ); + + $this->context->local('sleep 2', false)->shouldBeCalled(); + + $this->executor->addTask($task, $this->context->reveal()); + + $this->executor->runTask('foo', array()); + } +} \ No newline at end of file diff --git a/tests/unit/Idephix/ContextTest.php b/tests/unit/Idephix/ContextTest.php index a0f0a37..10658d7 100644 --- a/tests/unit/Idephix/ContextTest.php +++ b/tests/unit/Idephix/ContextTest.php @@ -3,139 +3,209 @@ class ContextTest extends \PHPUnit_Framework_TestCase { + public function setUp() + { + $this->executor = $this->prophesize('\Idephix\TaskExecutor'); + $this->operations = $this->prophesize('\Idephix\Operations'); + $this->config = $this->prophesize('\Idephix\Config'); + + $this->context = new Context( + $this->executor->reveal(), + $this->operations->reveal(), + $this->config->reveal() + ); + } + /** @test */ public function it_should_not_have_a_default_target_name() { - $context = $this->buildContext(); - - $this->assertNull($context['env.name']); - $this->assertNull($context['env.host']); - $this->assertNull($context->currentEnvName()); - $this->assertNull($context->currentHost()); + $this->assertNull($this->context->currentEnvName()); + $this->assertNull($this->context->currentHost()); } /** @test */ public function it_should_allow_to_define_a_target_name() { - $idx = $this->prophesize('\Idephix\Context')->reveal(); - $context = Context::dry($idx) - ->env( - 'prod', - Dictionary::fromArray( - array( - 'hosts' => array('127.0.0.1', 'localhost', '10.10.10.10') - ) - ) - ); + $this->context->setEnv('prod'); - $this->assertEquals('prod', $context['env.name']); - $this->assertNull($context['env.host']); - $this->assertEquals('prod', $context->currentEnvName()); - $this->assertNull($context->currentHost()); + $this->assertEquals('prod', $this->context->getEnv()); + $this->assertNull($this->context->getHosts()); } /** @test */ public function it_should_allow_to_iterate_over_hosts() { - $idx = $this->prophesize('\Idephix\Context')->reveal(); - $targetData = array( - 'hosts' => array('127.0.0.1', 'localhost', '10.10.10.10') - ); + $this->config + ->get('envs.prod.hosts') + ->willReturn(array('127.0.0.1', 'localhost', '10.10.10.10')); - $context = Context::dry($idx) - ->env( - 'prod', - Dictionary::fromArray( - $targetData - ) - ); + $this->context + ->setEnv('prod'); - foreach ($context as $hostCount => $hostRelatedContext) { - $this->assertInstanceOf('\Idephix\Context', $hostRelatedContext); + $this->assertCount(3, $this->context->getHosts()); + } - $this->assertEquals('prod', $context['env.name']); - $this->assertEquals('prod', $context->currentEnvName()); + /** @test */ + public function it_should_allow_to_iterate_over_missing_hosts() + { + $this->config + ->get('envs.prod.hosts') + ->shouldBeCalled(); - $this->assertEquals($targetData['hosts'][$hostCount], $hostRelatedContext['env.host']); - $this->assertEquals($targetData['hosts'][$hostCount], $hostRelatedContext->currentHost()); - } + $this->context + ->setEnv('prod'); - $this->assertEquals(2, $hostCount); + $this->assertEquals(0, $this->context->getHosts()); } /** @test */ - public function it_should_allow_to_iterate_over_missing_hosts() + public function it_should_run_task_sending_multiple_arguments() + { + $this->executor + ->hasTask('mycommand') + ->willReturn(true); + + $this->executor + ->hasTask('mycommand') + ->shouldBeCalled(); + + $this->executor + ->runTask('mycommand', array('foo', 'bar')) + ->shouldBeCalled(); + + $this->context->mycommand('foo', 'bar'); + } + + /** @test */ + public function it_should_return_local_output() + { + $this->operations + ->local('foo', false, 60) + ->willReturn('bar'); + + $this->assertEquals('bar', $this->context->local('foo')); + } + + /** + * @test + * @expectedException RunTimeException + */ + public function it_should_throw_exception_if_env_not_selected() { - $idx = $this->prophesize('\Idephix\Context')->reveal(); - $targetData = array('foo' => 'bar'); - - $context = Context::dry($idx) - ->env( - 'prod', - Dictionary::fromArray( - $targetData + $this->context->getHosts(); + } + + /** @test */ + public function it_should_return_current_host() + { + $conf = array( + 'envs' => array( + 'prod' => array( + 'hosts' => array('1', '2', '3') ) - ); + ) + ); + + $config = Config::fromArray($conf); + $executor = $this->prophesize('\Idephix\TaskExecutor'); + $operations = $this->prophesize('\Idephix\Operations'); - foreach ($context as $hostCount => $hostRelatedContext) { - $this->assertInstanceOf('\Idephix\Context', $hostRelatedContext); + $context = new Context( + $executor->reveal(), + $operations->reveal(), + $config + ); + + $context->setEnv('prod'); + $hosts = $context->getHosts(); - $this->assertEquals('prod', $context['env.name']); - $this->assertEquals('prod', $context->currentEnvName()); + $this->assertEquals('1', $context->getCurrentHost()); + $hosts->next(); - $this->assertNull($hostRelatedContext['env.host']); - $this->assertNull($hostRelatedContext->currentHost()); - } + $this->assertEquals('2', $context->getCurrentHost()); + $hosts->next(); - $this->assertEquals(0, $hostCount); + $this->assertEquals('3', $context->getCurrentHost()); + $hosts->next(); + + $this->assertNull($context->getCurrentHost()); } /** * @test */ - public function it_should_allow_to_retrieve_value_as_path() + public function it_should_invoke_task() { - $idx = $this->prophesize('\Idephix\Context')->reveal(); + $this->executor + ->hasTask('mycommand') + ->shouldBeCalled(); + + $this->executor + ->hasTask('mycommand') + ->willReturn(true); - $context = Context::dry($idx) - ->env('prod', Dictionary::fromArray(array('foo' => '/var/www', 'bar' => '/var/www/'))); + $this->executor + ->runTask('mycommand', array('foo', 'bar')) + ->shouldBeCalled(); - $this->assertEquals('/var/www/', $context->getAsPath('foo')); - $this->assertEquals('/var/www/', $context->getAsPath('bar')); + $this->context->mycommand('foo', 'bar'); } - /** @test */ - public function it_should_run_task_sending_multiple_arguments() + /** + * @test + */ + public function it_should_invoke_methods() { - $idx = $this->prophesize('\Idephix\Context'); - $idx->execute('mycommand', 'foo', 'bar')->shouldBeCalled(); + $this->executor + ->hasTask('mycommand') + ->shouldBeCalled(); + + $this->executor + ->hasTask('mycommand') + ->willReturn(false); + + $this->operations + ->execute('mycommand', array('foo', 'bar')) + ->shouldBeCalled(); - $context = Context::dry($idx->reveal()); - $context->execute('mycommand', 'foo', 'bar'); + $this->context->mycommand('foo', 'bar'); } - /** @test */ - public function it_should_return_local_output() + /** + * @test + */ + public function it_should_invoke_methods_through_execute() { - $idx = $this->prophesize('\Idephix\Context'); - $idx->local('foo', false, 60)->willReturn('bar'); + $this->executor + ->hasTask('mycommand') + ->shouldBeCalled(); - $context = Context::dry($idx->reveal()); + $this->executor + ->hasTask('mycommand') + ->willReturn(false); - $this->assertEquals('bar', $context->local('foo')); + $this->operations + ->execute('mycommand', array('foo', 'bar')) + ->shouldBeCalled(); + + $this->context->execute('mycommand', 'foo', 'bar'); } + /** - * @return Context + * @test */ - private function buildContext() + public function it_should_allow_to_invoke_tasks_only_once() { - $idx = $this->prophesize('\Idephix\Context')->reveal(); - $contextData = array( - 'hosts' => array('127.0.0.1', 'localhost', '10.10.10.10') - ); - $context = new Context(Dictionary::fromArray($contextData), $idx); + $this->executor + ->hasTask('mycommand') + ->willReturn(true); + + $this->executor + ->runTask('mycommand', array('foo', 'bar')) + ->shouldBeCalledTimes(1); - return $context; + $this->context->executeOnce('mycommand', 'foo', 'bar'); + $this->context->executeOnce('mycommand', 'foo', 'bar'); } } diff --git a/tests/unit/Idephix/Extension/Project/ProjectTest.php b/tests/unit/Idephix/Extension/Project/ProjectTest.php index ae38848..bddcda0 100644 --- a/tests/unit/Idephix/Extension/Project/ProjectTest.php +++ b/tests/unit/Idephix/Extension/Project/ProjectTest.php @@ -3,93 +3,50 @@ namespace Idephix\Extension\Project; use Idephix\Context; -use Idephix\Dictionary; class ProjectTest extends \PHPUnit_Framework_TestCase { public function setUp() { - $this->idx = $this->getMockBuilder('\Idephix\Idephix') - ->disableOriginalConstructor() - ->getMock(); - - $this->idx->expects($this->exactly(1)) - ->method('local') - ->will($this->returnArgument(0)); + $this->context = $this->prophesize('\Idephix\Context'); $this->project = new Rsync(); - $this->project->setIdephix($this->idx); + $this->project->setContext($this->context->reveal()); } public function testRsyncProject() { - $this->idx->expects($this->exactly(1)) - ->method('getContext') - ->will( - $this->returnValue( - new Context( - Dictionary::fromArray( - array( - 'env' => array('name' => 'prod', 'host' => 'banana.com'), - 'hosts' => array('mela.com', 'banana.com'), - 'ssh_params' => array('user' => 'kea') - ) - ), - $this->idx - ) - ) - ); + $expected = "rsync -rlDcz --force --delete --progress -e 'ssh -p 22' ./from kea@banana.com:/a/remote/"; - $result = $this->project->rsyncProject('/a/remote', './from'); + $this->context->local($expected)->shouldBeCalled(); + $this->context->getSshParams()->willReturn(array('user' => 'kea')); + $this->context->getHosts()->willReturn(array('mela.com', 'banana.com')); + $this->context->getCurrentHost()->willReturn('banana.com'); - $this->assertEquals("rsync -rlDcz --force --delete --progress -e 'ssh' ./from kea@banana.com:/a/remote/", $result); + $result = $this->project->rsyncProject('/a/remote', './from'); } public function testRysncWithoutUser() { - $this->idx->expects($this->exactly(1)) - ->method('getContext') - ->will( - $this->returnValue( - new Context( - Dictionary::fromArray( - array( - 'env' => array('name' => 'prod', 'host' => 'banana.com'), - 'hosts' => array('mela.com', 'banana.com'), - 'ssh_params' => array() - ) - ), - $this->idx - ) - ) - ); + $expected = "rsync -rlDcz --force --delete --progress -e 'ssh -p 22' ./from banana.com:/a/remote/"; - $result = $this->project->rsyncProject('/a/remote', './from'); + $this->context->local($expected)->shouldBeCalled(); + $this->context->getSshParams()->willReturn(array()); + $this->context->getHosts()->willReturn(array('mela.com', 'banana.com')); + $this->context->getCurrentHost()->willReturn('banana.com'); - $this->assertEquals("rsync -rlDcz --force --delete --progress -e 'ssh' ./from banana.com:/a/remote/", $result); + $result = $this->project->rsyncProject('/a/remote', './from'); } public function testRsyncProjectWithCustomPort() { - $this->idx->expects($this->exactly(1)) - ->method('getContext') - ->will( - $this->returnValue( - new Context( - Dictionary::fromArray( - array( - 'env' => array('name' => 'prod', 'host' => 'banana.com'), - 'hosts' => array('mela.com', 'banana.com'), - 'ssh_params' => array('user' => 'kea', 'port' => 20817) - ) - ), - $this->idx - ) - ) - ); + $expected = "rsync -rlDcz --force --delete --progress -e 'ssh -p 20817' ./from kea@banana.com:/a/remote/"; - $result = $this->project->rsyncProject('/a/remote', './from'); + $this->context->local($expected)->shouldBeCalled(); + $this->context->getSshParams()->willReturn(array('user' => 'kea', 'port' => 20817)); + $this->context->getHosts()->willReturn(array('mela.com', 'banana.com')); + $this->context->getCurrentHost()->willReturn('banana.com'); - $this->assertEquals("rsync -rlDcz --force --delete --progress -e 'ssh -p 20817' ./from kea@banana.com:/a/remote/", $result); + $result = $this->project->rsyncProject('/a/remote', './from'); } } diff --git a/tests/unit/Idephix/Extension/Slack/SlackTest.php b/tests/unit/Idephix/Extension/Slack/SlackTest.php index 52ea348..eddba83 100644 --- a/tests/unit/Idephix/Extension/Slack/SlackTest.php +++ b/tests/unit/Idephix/Extension/Slack/SlackTest.php @@ -2,6 +2,8 @@ namespace Idephix\Extension\Slack; +use Idephix\Context; + //mock curl_exec function curl_exec($ch, $error = false) { @@ -16,21 +18,20 @@ class SlackTest extends \PHPUnit_Framework_TestCase { public function setUp() { - $this->idx = $this->getMockBuilder('Idephix\Idephix') - ->disableOriginalConstructor() - ->getMock(); - - $this->idx->expects($this->exactly(1)) - ->method('local') - ->will($this->returnArgument(0)); + $this->context = $this->prophesize('\Idephix\Context'); $this->slack = new Slack(); - $this->slack->setIdephix($this->idx); + $this->slack->setContext($this->context->reveal()); } public function testSendMessage() { + $this->context + ->local("echo 'Message sent to slack channel'") + ->willReturn(0); + $response = $this->slack->sendToSlack('ciao'); + $this->assertEquals($response, 'ok'); } } diff --git a/tests/unit/Idephix/ExtensionTest.php b/tests/unit/Idephix/ExtensionTest.php index c830273..f94173d 100644 --- a/tests/unit/Idephix/ExtensionTest.php +++ b/tests/unit/Idephix/ExtensionTest.php @@ -3,84 +3,75 @@ use Idephix\Task\CallableTask; use Idephix\Test\DummyExtension; -use Symfony\Component\Console\Output\StreamOutput; +use Idephix\Task\TaskCollection; +use Idephix\Extension\MethodCollection; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Input\StringInput; class ExtensionTest extends \PHPUnit_Framework_TestCase { - private $output; - - protected function setUp() + /** + * @test + */ + public function it_should_register_methods_from_config() { - $this->output = fopen('php://memory', 'r+'); - $output = new StreamOutput($this->output); + $ext = $this->prophesize('\Idephix\Extension\MethodProvider'); + $ext->methods()->willReturn(MethodCollection::dry()); - $this->idx = new Idephix( - Config::fromArray( - array('envs' => array(), 'ssh_client' => new SSH\SshClient(new Test\SSH\StubProxy())) - ), $output + $conf = array( + Config::EXTENSIONS => array($ext->reveal()) ); - } - /** @test */ - public function it_should_register_extensions_from_config() - { - $extension = new DummyExtension($this, 'deploy'); $idx = new Idephix( - Config::fromArray( - array( - Config::EXTENSIONS => array($extension) - ) - ) + Config::fromArray($conf), + TaskCollection::dry(), + new NullOutput(), + new StringInput('') ); - $this->assertEquals(42, $idx->test(42)); + $ext->methods()->shouldHaveBeenCalled(); } /** * @test */ - public function it_should_allow_to_use_extension() + public function it_should_register_tasks_from_config() { - $extension = new DummyExtension($this, 'deploy'); - $this->idx->addExtension($extension); - $this->assertEquals(42, $this->idx->test(42)); - } + $ext = $this->prophesize('\Idephix\Extension\TaskProvider'); + $ext->tasks()->willReturn(TaskCollection::dry()); - /** - * @test - */ - public function it_should_allow_to_override_extension_method() - { - $extension = new DummyExtension($this, 'myExtension'); - $this->idx->addExtension($extension); - $this->idx->addTask(CallableTask::buildFromClosure('test', function ($what) { return $what * 2;})); - $this->assertEquals(84, $this->idx->test(42)); - } + $conf = array( + Config::EXTENSIONS => array($ext->reveal()) + ); - /** - * @test - */ - public function it_should_add_task_from_extesions() - { - $extension = new DummyExtension($this, 'deploy'); - $this->idx->addExtension($extension); + $idx = new Idephix( + Config::fromArray($conf), + TaskCollection::dry(), + new NullOutput(), + new StringInput('') + ); - $registeredCommands = $this->idx->getApplication()->all(); - $this->assertArrayHasKey('update', $registeredCommands); - $this->assertInstanceOf('\Idephix\Console\Command', $registeredCommands['update']); - $this->assertEquals('update', $registeredCommands['update']->getName()); - $this->assertEquals(42, $this->idx->update(42)); + $ext->tasks()->shouldHaveBeenCalled(); } /** * @test - * @expectedException \BadMethodCallException */ - public function it_should_throw_exception_calling_unregistered_method() + public function it_should_inject_context() { - $extension = new DummyExtension($this, 'deploy'); - $this->idx->addExtension($extension); + $ext = $this->prophesize('\Idephix\Extension\ContextAwareInterface'); + + $conf = array( + Config::EXTENSIONS => array($ext->reveal()) + ); + + $idx = new Idephix( + Config::fromArray($conf), + TaskCollection::dry(), + new NullOutput(), + new StringInput('') + ); - $this->assertEquals(42, $this->idx->unregisteredMethod(42)); + $ext->setContext(\Prophecy\Argument::any())->shouldHaveBeenCalled(); } -} +} \ No newline at end of file diff --git a/tests/unit/Idephix/IdephixTest.php b/tests/unit/Idephix/IdephixTest.php index c6b1403..0b0f53b 100644 --- a/tests/unit/Idephix/IdephixTest.php +++ b/tests/unit/Idephix/IdephixTest.php @@ -3,6 +3,7 @@ use Idephix\Exception\FailedCommandException; use Idephix\SSH\SshClient; +use Idephix\Task\TaskCollection; use Idephix\Task\Parameter; use Idephix\Task\CallableTask; use Idephix\Test\SSH\StubProxy; @@ -12,84 +13,24 @@ class IdephixTest extends \PHPUnit_Framework_TestCase { - /** - * @var Idephix - */ - protected $idx; - - protected $output; - - protected function setUp() - { - $this->output = fopen('php://memory', 'r+'); - $output = new StreamOutput($this->output); - - $this->idx = new Idephix( - Config::fromArray( - array('envs' => array(), 'ssh_client' => new SSH\SshClient(new Test\SSH\StubProxy())) - ), $output - ); - } - - /** - * @expectedException PHPUnit_Framework_Error - * @expectedExceptionMessage Undefined property: application in - */ - public function test__get() - { - $this->assertInstanceOf('\Idephix\SSH\SshClient', $this->idx->sshClient); - $this->assertInstanceOf('\Symfony\Component\Console\Output\OutputInterface', $this->idx->output); - - $this->idx->application; - } - - /** - * @test - */ - public function it_should_be_able_to_add_task_from_closure() - { - $this->idx->addTask( - CallableTask::buildFromClosure( - 'commandName', - function () { - } - ) - ); - - $this->assertTrue($this->idx->has('commandName')); - } - - /** - * @test - */ - public function it_should_be_able_to_add_task_from_object() - { - $task = new CallableTask('fooCommand', 'A dummy command', function () {}, Parameter\Collection::dry()); - $this->idx->addTask($task); - - $this->assertTrue($this->idx->has('fooCommand')); - - $registeredCommands = $this->idx->getApplication()->all(); - $this->assertArrayHasKey('fooCommand', $registeredCommands); - $this->assertInstanceOf('\Idephix\Console\Command', $registeredCommands['fooCommand']); - $this->assertEquals('fooCommand', $registeredCommands['fooCommand']->getName()); - } - public function getArgvAndTargets() { return array( array( - array('idx', 'foo'), + "bar", array(), - "Local: echo \"Hello World from \"\nHello World from \n" + "Local: echo \"Hello World from bar task\"\nHello World from bar task\n" ), array( - array('idx', 'foo', '--env=env'), - array('env' => array('hosts' => array('localhost'), 'ssh_params' => array('user' => 'test'))), + "foo --env=env", + array('env' => array( + 'hosts' => array('localhost'), + 'ssh_params' => array('user' => 'test')) + ), "Local: echo \"Hello World from localhost\"\nHello World from localhost\n" ), array( - array('idx', 'foo', '--env=env'), + "foo --env=env", array( 'env' => array( 'hosts' => array('localhost', '1.2.3.4'), @@ -104,131 +45,45 @@ public function getArgvAndTargets() /** * @dataProvider getArgvAndTargets + * @test */ - public function testRunALocalTask($argv, $targets, $expected) + public function it_should_run_a_task_on_every_host($args, $targets, $expected) { - $_SERVER['argv'] = $argv; - - $sshClient = new SSH\SshClient( - new Test\SSH\StubProxy() - ); - $output = fopen('php://memory', 'r+'); - $idx = new Idephix( - Config::fromArray( - array(\Idephix\Config::ENVS => $targets, 'ssh_client' => $sshClient) - ), - new StreamOutput($output) + $conf = array( + \Idephix\Config::ENVS => $targets, + 'ssh_client' => new SSH\SshClient(new Test\SSH\StubProxy()) ); - $idx->getApplication()->setAutoExit(false); - $idx->addTask( - CallableTask::buildFromClosure( - 'foo', - function (Context $idx) { - $idx->local('echo "Hello World from ' . $idx['env.host'] . '"'); - } - ) - ); - - $idx->run(); - - rewind($output); - - $this->assertEquals($expected, stream_get_contents($output)); - } + $output = new BufferedOutput(fopen('php://memory', 'r+')); - /** @test */ - public function it_should_inject_context_to_task() - { $idx = new Idephix( - Config::fromArray(array( - Config::SSHCLIENT => new SshClient(new StubProxy()), - Config::ENVS => array( - 'prod' => array( - 'hosts' => array('127.0.0.1'), - 'ssh_params' => array('user' => 'ftassi'), - 'foo' => 'bar' - ) - ))), - new StreamOutput(fopen('php://memory', 'r+')), - new StringInput('myTask --env=prod') + Config::fromArray($conf), + TaskCollection::dry(), + $output, + new StringInput($args) ); - $spy = new \stdClass(); $idx->addTask( CallableTask::buildFromClosure( - 'myTask', - function (Context $context) use ($spy) { - $spy->args = func_get_args(); + 'foo', + function (Context $ctx) { + $ctx->local("echo \"Hello World from {$ctx->getCurrentHost()}\""); } ) ); - $idx->run(); - - /** @var Context $context */ - $context = $spy->args[0]; - - $this->assertInstanceOf('\Idephix\Context', $context); - $this->assertEquals('bar', $context['foo']); - $this->assertEquals('prod', $context['env.name']); - $this->assertEquals('127.0.0.1', $context['env.host']); - } - - /** - * @test - */ - public function it_should_allow_to_define_custom_timeout() - { - $output = fopen('php://memory', 'r+'); - $idx = new Idephix( - Config::dry(), - new StreamOutput($output), - new StringInput('foo') - ); - - $idx->getApplication()->setAutoExit(false); - $idx->addTask( CallableTask::buildFromClosure( - 'foo', - function (Context $idx) { - $idx->local('sleep 2', false, 1); + 'bar', + function (Context $ctx) { + $ctx->local("echo \"Hello World from bar task\""); } ) ); - try { - $idx->run(); - } catch (FailedCommandException $e) { - // do nothing, is expected to fail - } - - rewind($output); + $idx->run(); - $this->assertContains('ProcessTimedOutException', stream_get_contents($output)); - } - - /** - * @test - */ - public function it_should_allow_to_invoke_tasks() - { - $this->idx->addTask( - CallableTask::buildFromClosure( - 'test', - function (Context $idx, $what, $go = false) { - if ($go) { - return $what * 2; - } - return 0; - } - ) - ); - $this->assertEquals(84, $this->idx->test(42, true)); - $this->assertEquals(84, $this->idx->execute('test', 42, true)); - $this->assertEquals(0, $this->idx->test(42)); - $this->assertEquals(0, $this->idx->execute('test', 42)); + $this->assertEquals($expected, $output->fetch()); } /** @@ -239,6 +94,7 @@ public function it_should_print_user_tasks_separately() $output = fopen('php://memory', 'r+'); $idx = new Idephix( Config::dry(), + TaskCollection::dry(), new StreamOutput($output), new StringInput('') ); @@ -257,59 +113,4 @@ public function it_should_print_user_tasks_separately() } - /** - * Exception: Remote function need a valid environment. Specify --env parameter. - */ - public function testRemote() - { - $output = new BufferedOutput($this->output); - $sshClient = new SSH\SshClient(new Test\SSH\StubProxy('Remote output from ')); - $this->idx = new Idephix( - Config::fromArray(array('envs' => array('test_target' => array()), 'ssh_client' => $sshClient)), - $output - ); - - $this->idx->sshClient->setHost('host'); - $this->idx->sshClient->connect(); - $this->idx->remote('echo foo'); - - $rows = explode("\n", $output->fetch()); - $this->assertCount(3, $rows); - $this->assertEquals('Remote: echo foo', $rows[0]); - $this->assertEquals('Remote output from echo foo', $rows[1]); - } - - /** - * @expectedException \Exception - * @expectedExceptionMessage Remote function need a valid environment. Specify --env parameter. - */ - public function testRemoteException() - { - $output = new StreamOutput($this->output); - $sshClient = new SSH\SshClient(new Test\SSH\StubProxy()); - $this->idx = new Idephix( - Config::fromArray(array('envs' => array('test_target' => array()), 'ssh_client' => $sshClient)), - $output - ); - $this->idx->remote('echo foo'); - } - - public function testLocal() - { - $this->idx->local('echo foo'); - rewind($this->output); - $this->assertRegExp('/echo foo/m', stream_get_contents($this->output)); - } -} - -class TaskSpy -{ - public $executed = false; - public $lastCallArguments = array(); - - public function execute($myParam) - { - $this->executed = true; - $this->lastCallArguments = func_get_args(); - } -} +} \ No newline at end of file diff --git a/tests/unit/Idephix/OperationsTest.php b/tests/unit/Idephix/OperationsTest.php new file mode 100644 index 0000000..f30274b --- /dev/null +++ b/tests/unit/Idephix/OperationsTest.php @@ -0,0 +1,98 @@ +output = new BufferedOutput(fopen('php://memory', 'r+')); + $this->sshClient = new SSH\SshClient(new Test\SSH\StubProxy('Remote output from ')); + + $this->operations = new Operations($this->sshClient, $this->output); + } + + /** + * @test + */ + public function it_should_run_local_commands() + { + $this->operations->local('echo foo'); + + $rows = explode("\n", $this->output->fetch()); + + $this->assertCount(3, $rows); + $this->assertEquals('Local: echo foo', $rows[0]); + $this->assertEquals('foo', $rows[1]); + } + + /** + * @expectedException Symfony\Component\Process\Exception\ProcessTimedOutException + * @expectedExceptionMessage The process "sleep 3" exceeded the timeout of 1 seconds. + * + * @test + */ + public function it_should_allow_to_define_custom_timeout() + { + $this->operations->local('sleep 3', false, 1); + } + + /** + * @test + */ + public function it_should_run_remote_commands() + { + $this->sshClient->setHost('host'); + $this->sshClient->connect(); + + $this->operations->remote('echo foo'); + + $rows = explode("\n", $this->output->fetch()); + + $this->assertCount(3, $rows); + $this->assertEquals('Remote: echo foo', $rows[0]); + $this->assertEquals('Remote output from echo foo', $rows[1]); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Remote function need a valid environment. Specify --env parameter. + * + * @test + */ + public function it_should_throw_exception_if_not_connected() + { + $this->operations->remote('echo foo'); + } + + /** + * @test + */ + public function it_should_allow_to_register_methods() + { + $methods = MethodCollection::ofCallables( + array( + new CallableMethod('mul', function($x, $y) { return $x * $y;}), + new CallableMethod('div', function($x, $y) { return $x / $y;}), + ) + ); + + $this->operations->addMethods($methods); + + $this->assertEquals(10, $this->operations->execute('mul', array(5, 2))); + $this->assertEquals(3, $this->operations->execute('div', array(9, 3))); + + } +} diff --git a/tests/unit/Idephix/Task/CallableTaskTest.php b/tests/unit/Idephix/Task/CallableTaskTest.php index 8bc0989..7e6341c 100644 --- a/tests/unit/Idephix/Task/CallableTaskTest.php +++ b/tests/unit/Idephix/Task/CallableTaskTest.php @@ -1,8 +1,6 @@ prophesize('\Idephix\Context'); + /** * A fake task * @param Context $context @@ -23,8 +23,6 @@ public function it_should_build_from_closure() return $foo; }; - $context = Context::dry(Idephix::create(TaskCollection::dry(), Config::dry())); - $task = CallableTask::buildFromClosure( 'fooTask', $taskCode @@ -37,6 +35,6 @@ public function it_should_build_from_closure() $this->assertCount(2, $task->userDefinedParameters()); $closure = $task->code(); - $this->assertEquals('foo', $closure($context, 'foo')); + $this->assertEquals('foo', $closure($context->reveal(), 'foo')); } } diff --git a/tests/unit/Idephix/Task/InitIdxFileTest.php b/tests/unit/Idephix/Task/InitIdxFileTest.php index 6e4e31b..fbff493 100644 --- a/tests/unit/Idephix/Task/InitIdxFileTest.php +++ b/tests/unit/Idephix/Task/InitIdxFileTest.php @@ -5,13 +5,13 @@ use org\bovigo\vfs\vfsStreamFile; use org\bovigo\vfs\vfsStreamWrapper; use Idephix\Task\Builtin\InitIdxFile; -use Symfony\Component\Console\Output\NullOutput; class InitIdxFileTest extends \PHPUnit_Framework_TestCase { public function setUp() { @include_once 'vfsStream/vfsStream.php'; + $structure = array( 'Deploy' => array( 'idxfile.php' => 'function foo(){ echo "bar"};', @@ -22,17 +22,22 @@ public function setUp() vfsStream::setup('root', null, $structure); } - public function testInitIdxFile() + /** + * @test + */ + public function it_should_create_files() { - $idx = $this->getMockBuilder('\Idephix\Idephix') - ->disableOriginalConstructor() - ->getMock(); - $idx->output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); - $idx->output->expects($this->exactly(4)) - ->method('writeln'); + $context = $this->prophesize('\Idephix\Context'); + $context->writeln(\Prophecy\Argument::any())->shouldBeCalled(); + - $initIdxFile = new InitIdxFile('vfs://root', 'vfs://root/Deploy/idxfile.php', 'vfs://root/Deploy/idxrc.php'); - $initIdxFile->setIdephix($idx); + $initIdxFile = new InitIdxFile( + 'vfs://root', + 'vfs://root/Deploy/idxfile.php', + 'vfs://root/Deploy/idxrc.php' + ); + + $initIdxFile->setContext($context->reveal()); $initIdxFile->initFile(); $this->assertTrue(file_exists('vfs://root/idxfile.php')); @@ -42,40 +47,43 @@ public function testInitIdxFile() $this->assertEquals('return \Idephix\Config::dry();', file_get_contents('vfs://root/idxrc.php')); } - public function testInitWithExistingIdxFile() + /** + * @test + */ + public function it_should_return_error_if_file_exists() { vfsStreamWrapper::getRoot()->addChild(new vfsStreamFile('idxfile.php')); - $idx = $this->getMockBuilder('\Idephix\Idephix') - ->disableOriginalConstructor() - ->getMock(); - $idx->output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); - $idx->output->expects($this->at(0)) - ->method('writeln') - ->with('An idxfile.php already exists, generation skipped.') - ; - - $initIdxFile = new InitIdxFile('vfs://root', 'vfs://root/Deploy/idxfile.php', 'vfs://root/Deploy/idxrc.php'); - $initIdxFile->setIdephix($idx); + $context = $this->prophesize('\Idephix\Context'); + $context->writeln('Creating basic idxrc.php file...')->shouldBeCalled(); + $context->writeln('idxrc.php file created.')->shouldBeCalled(); + $context->writeln('An idxfile.php already exists, generation skipped.')->shouldBeCalled(); + + + $initIdxFile = new InitIdxFile( + 'vfs://root', + 'vfs://root/Deploy/idxfile.php', + 'vfs://root/Deploy/idxrc.php' + ); + + $initIdxFile->setContext($context->reveal()); $initIdxFile->initFile(); } public function testInitFromDeployRecipe() { - $idx = $this->getMockBuilder('\Idephix\Idephix') + $context = $this->getMockBuilder('\Idephix\Context') ->disableOriginalConstructor() ->getMock(); - - $idx->output = new NullOutput(); + $initIdxFile = InitIdxFile::fromDeployRecipe('vfs://root'); - $initIdxFile->setIdephix($idx); + $initIdxFile->setContext($context); $initIdxFile->initFile(); $this->assertTrue(file_exists('vfs://root/idxfile.php')); $this->assertTrue(file_exists('vfs://root/idxrc.php')); - $this->assertEquals(file_get_contents(__DIR__ . '/../../../../src/Idephix/Cookbook/Deploy/idxfile.php'), file_get_contents('vfs://root/idxfile.php') ); - $this->assertEquals(file_get_contents(__DIR__ . '/../../../../src/Idephix/Cookbook/Deploy/idxrc.php'), file_get_contents('vfs://root/idxrc.php') ); + $this->assertEquals(file_get_contents(__DIR__ . '/../../../../src/Idephix/Cookbook/Deploy/idxfile.php'), file_get_contents('vfs://root/idxfile.php')); + $this->assertEquals(file_get_contents(__DIR__ . '/../../../../src/Idephix/Cookbook/Deploy/idxrc.php'), file_get_contents('vfs://root/idxrc.php')); } - }