diff --git a/src/Clue/PharComposer/App.php b/src/Clue/PharComposer/App.php index 4e80879..be95e12 100644 --- a/src/Clue/PharComposer/App.php +++ b/src/Clue/PharComposer/App.php @@ -16,9 +16,6 @@ public function __construct() $this->add(new Command\Build()); $this->add(new Command\Search()); $this->add(new Command\Install()); - - // GUI feature disabled for now, see #35 - // $this->add(new Command\Gui()); } /** @@ -32,20 +29,11 @@ protected function getCommandName(InputInterface $input) { if ($input->getFirstArgument() === null && !$input->hasParameterOption(array('--help', '-h'))) { $this->isDefault = true; - return $this->getDefaultCommandName(); + return 'search'; } return parent::getCommandName($input); } - private function getDefaultCommandName() - { - $gui = $this->has('gui') ? $this->get('gui') : null; - if ($gui instanceof Command\Gui && $gui->hasZenity()) { - return 'gui'; - } - return 'search'; - } - /** * Overridden so that the application doesn't expect the command * name to be the first argument. diff --git a/src/Clue/PharComposer/Command/Build.php b/src/Clue/PharComposer/Command/Build.php index c07e2b2..a0d9f1b 100644 --- a/src/Clue/PharComposer/Command/Build.php +++ b/src/Clue/PharComposer/Command/Build.php @@ -2,41 +2,41 @@ namespace Clue\PharComposer\Command; -use Symfony\Component\Console\Formatter\OutputFormatterStyle; -use Symfony\Component\Console\Helper\DialogHelper; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Input\InputArgument; +use Clue\PharComposer\Phar\Packager; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Clue\PharComposer\Phar\PharComposer; -use InvalidArgumentException; -use UnexpectedValueException; -use Symfony\Component\Console\Output\Output; -use Symfony\Component\Process\Process; -use Symfony\Component\Process\ExecutableFinder; -use Clue\PharComposer\Phar\Packager; class Build extends Command { + /** @var Packager */ + private $packager; + + public function __construct(Packager $packager = null) + { + parent::__construct(); + + if ($packager === null) { + $packager = new Packager(); + } + $this->packager = $packager; + } + protected function configure() { $this->setName('build') ->setDescription('Build phar for the given composer project') - ->addArgument('path', InputArgument::OPTIONAL, 'Path to project directory or composer.json', '.') + ->addArgument('project', InputArgument::OPTIONAL, 'Path to project directory or composer.json', '.') ->addArgument('target', InputArgument::OPTIONAL, 'Path to write phar output to (defaults to project name)'); } protected function execute(InputInterface $input, OutputInterface $output) { - $packager = new Packager(); - $packager->setOutput(function ($line) use ($output) { - $output->write($line); - }); - - $packager->coerceWritable(); + $this->packager->setOutput($output); + $this->packager->coerceWritable(); - $pharer = $packager->getPharer($input->getArgument('path')); + $pharer = $this->packager->getPharer($input->getArgument('project')); $target = $input->getArgument('target'); if ($target !== null) { diff --git a/src/Clue/PharComposer/Command/Gui.php b/src/Clue/PharComposer/Command/Gui.php deleted file mode 100644 index 16cadea..0000000 --- a/src/Clue/PharComposer/Command/Gui.php +++ /dev/null @@ -1,283 +0,0 @@ -setName('gui') - ->setDescription('Interactive GUI (requires Zenity, likely only on Linux/etc.)'); - } - - public function hasZenity() - { - return $this->hasBin('zenity'); - } - - private function hasBin($bin) - { - foreach (explode(PATH_SEPARATOR, getenv('PATH')) as $path) { - $path = rtrim($path, '/') . '/' . $bin; - if (file_exists($path)) { - return true; - } - } - return false; - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $loop = Factory::create(); - $launcher = new Launcher($loop); - $builder = new Builder($launcher); - - $packager = new Packager(); - $packager->setOutput(function ($line) use ($builder) { - $builder->info(strip_tags($line))->waitReturn(); - }); - $packager->coerceWritable(0); - - foreach (array('gksudo', 'kdesudo', 'cocoasudo', 'sudo') as $bin) { - if ($this->hasBin($bin)) { - $packager->setBinSudo($bin); - break; - } - } - - $packager->setOutput($output); - - - $menu = $builder->listMenu( - array( - 'search' => 'Search package online', - 'local' => 'Select local package', - 'url' => 'Build from git-Repository', - 'about' => 'About clue/phar-composer' - ), - 'Action' - ); - $menu->setTitle('clue/phar-composer'); - $menu->setWindowIcon('info'); - $menu->setCancelLabel('Quit'); - $selection = $menu->waitReturn(); - - if ($selection === 'search') { - $pharer = $this->doSearch($builder, $packager); - } elseif ($selection === 'local') { - do { - $dir = $builder->directorySelection()->waitReturn(); - if ($dir === false) { - return; - } - try { - $pharer = $packager->getPharer($dir); - break; - } - catch (\Exception $e) { - $builder->error('Could not initialize composer package:' . PHP_EOL . PHP_EOL . $e->getMessage())->waitReturn(); - } - } while(true); - } elseif ($selection === 'url') { - $pharer = $this->doUrl($builder, $packager); - } else { - return; - } - - if ($pharer === null) { - return; - } - - $action = $builder->listMenu( - array( - 'build' => 'Build project', - 'install' => 'Install project system-wide' - ), - 'Action for "' . $pharer->getPackageRoot()->getName() .'"' /*, - 'Quit' */ - )->waitReturn(); - - if ($action === 'build') { - $this->doBuild($builder, $packager, $pharer); - } elseif ($action ==='install') { - $this->doInstall($builder, $packager, $pharer); - } else { - return; - } - - $builder->info('Successfully built ' . $pharer->getTarget() . '!')->waitReturn(); - } - - protected function doSearch(Builder $builder, Packager $packager) - { - $oldname = null; - - do { - $dialog = $builder->entry('Search (partial) project name', $oldname); - $dialog->setTitle('Search project name'); - $name = $dialog->waitReturn(); - if ($name === false) { - return; - } - $oldname = $name; - - $pulsate = $builder->pulsate('Searching for "' . $name . '"...'); - $pulsate->setNoCancel(true); - $pulsate->run(); - - $packagist = new Client(); - - $choices = array(); - foreach ($packagist->search($name) as $package) { - /* @var $package Result */ - - $choices[$package->getName()] = array( - $package->getName(), - mb_strimwidth($package->getDescription(), 0, 80, '…', 'utf-8'), - $package->getDownloads() - ); - } - - $pulsate->close(); - - if (!$choices) { - $builder->warning('No package matching "' . $name .'" found!')->waitReturn(); - $name = false; - continue; - } - - $table = $builder->table($choices, array('Name', 'Description', 'Downloads'), 'Select matching package'); - $table->setTitle('Select matching package'); - $table->setCancelLabel('Back to Search'); - $table->setWidth(1000); - $table->setHeight(600); - - $name = $table->waitReturn(); - } while (is_bool($name)); - - $pulsate = $builder->pulsate('Selected ' . $name . ', listing versions...'); - $pulsate->setNoCancel(true); - $pulsate->run(); - - $package = $packagist->get($name); - /* @var $package Package */ - - $choices = array(); - foreach ($package->getVersions() as $version) { - /* @var $version Version */ - - $time = new \DateTime($version->getTime()); - $time = $time->format('Y-m-d H:i:s'); - - $bin = $version->getBin(); - if ($bin) { - $bin = '☑ ' . array_shift($bin); - } else { - $bin = '☐ no executable bin'; - } - - $choices[$version->getVersion()] = array( - $version->getVersion(), - $time, - $bin - ); - } - - $pulsate->close(); - - if (!$choices) { - $builder->warning('No versions for package "' . $name .'" found!')->waitReturn(); - return; - } - - $dialog = $builder->table($choices, array('Version', 'Date', 'Binary'), 'Select available version'); - $dialog->setWidth(800); - $dialog->setHeight(300); - $version = $dialog->waitReturn(); - - if (is_bool($version)) { - return; - } - - $pulsate = $builder->pulsate('Installing to temporary directory...')->run(); - $pharer = $packager->getPharer($name, $version); - $pulsate->close(); - - return $pharer; - } - - protected function doUrl(Builder $builder, Packager $packager) - { - do { - $url = $builder->entry('Git URL to clone')->waitReturn(); - if ($url === false) { - return; - } - $pulsate = $builder->pulsate('Cloning and installing from git...')->run(); - try { - $pharer = $packager->getPharer($url); - $pulsate->close(); - - return $pharer; - } - catch (\Exception $e) { - $pulsate->close(); - - $builder->error('Unable to clone repository:' . PHP_EOL . PHP_EOL . $e->getMessage())->waitReturn(); - } - } while(true); - } - - protected function doInstall(Builder $builder, Packager $packager, PharComposer $pharer) - { - $pulsate = $builder->pulsate('Installing...')->run(); - - $path = $packager->getSystemBin($pharer); - $packager->install($pharer, $path); - - $pulsate->close(); - } - - protected function doBuild(Builder $builder, Packager $packager, PharComposer $pharer) - { - $pulsate = $builder->pulsate('Waiting for target file name...')->run(); - - $save = $builder->fileSave('Location to write file to', $pharer->getTarget()); - - $target = $save->waitReturn(); - - if ($target === false) { - return; - } - - $pulsate->close(); - $pulsate = $builder->pulsate('Building target file...')->run(); - - $pharer->setTarget($target); - $pharer->build(); - - $pulsate->close(); - } -} diff --git a/src/Clue/PharComposer/Command/Install.php b/src/Clue/PharComposer/Command/Install.php index 1c8d0df..00a4e24 100644 --- a/src/Clue/PharComposer/Command/Install.php +++ b/src/Clue/PharComposer/Command/Install.php @@ -2,43 +2,55 @@ namespace Clue\PharComposer\Command; -use Symfony\Component\Console\Command\Command; use Clue\PharComposer\Phar\Packager; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\DialogHelper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Helper\DialogHelper; class Install extends Command { + /** @var Packager */ + private $packager; + + public function __construct(Packager $packager = null) + { + parent::__construct(); + + if ($packager === null) { + $packager = new Packager(); + } + $this->packager = $packager; + } + protected function configure() { $this->setName('install') ->setDescription('Install phar into system wide binary directory') - ->addArgument('name', InputArgument::OPTIONAL, 'Project name or path', '.') - ->addArgument('path', InputArgument::OPTIONAL, 'Path to install to', '/usr/local/bin'); + ->addArgument('project', InputArgument::OPTIONAL, 'Project name or path', '.') + ->addArgument('target', InputArgument::OPTIONAL, 'Path to install to', '/usr/local/bin'); } protected function execute(InputInterface $input, OutputInterface $output) { - $packager = new Packager(); - $packager->setOutput($output); - $packager->coerceWritable(); + $this->packager->setOutput($output); + $this->packager->coerceWritable(); - $pharer = $packager->getPharer($input->getArgument('name')); + $pharer = $this->packager->getPharer($input->getArgument('project')); - $path = $packager->getSystemBin($pharer, $input->getArgument('path')); + $path = $this->packager->getSystemBin($pharer, $input->getArgument('target')); if (is_file($path)) { - $dialog = $this->getHelperSet()->get('dialog'); - /* @var $dialog DialogHelper */ + $dialog = $this->getHelper('dialog'); + assert($dialog instanceof DialogHelper); if (!$dialog->askConfirmation($output, 'Overwrite existing file ' . $path . '? [y] > ')) { $output->writeln('Aborting'); - return; + return 0; } } - $packager->install($pharer, $path); + $this->packager->install($pharer, $path); } } diff --git a/src/Clue/PharComposer/Command/Search.php b/src/Clue/PharComposer/Command/Search.php index 74bc184..e29a894 100644 --- a/src/Clue/PharComposer/Command/Search.php +++ b/src/Clue/PharComposer/Command/Search.php @@ -3,52 +3,65 @@ namespace Clue\PharComposer\Command; -use Symfony\Component\Console\Formatter\OutputFormatterStyle; +use Clue\PharComposer\Phar\Packager; +use Packagist\Api\Client; +use Packagist\Api\Result\Package; +use Packagist\Api\Result\Package\Version; +use Packagist\Api\Result\Result; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\DialogHelper; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Clue\PharComposer\Phar\PharComposer; -use InvalidArgumentException; -use UnexpectedValueException; -use Symfony\Component\Console\Output\Output; -use Symfony\Component\Process\Process; -use Symfony\Component\Process\ExecutableFinder; -use Packagist\Api\Client; -use Packagist\Api\Result\Result; -use Packagist\Api\Result\Package; -use Packagist\Api\Result\Package\Version; -use Clue\PharComposer\Phar\Packager; class Search extends Command { + /** @var Packager */ + private $packager; + + /** @var Client */ + private $packagist; + + public function __construct(Packager $packager = null, Client $packagist = null) + { + parent::__construct(); + + if ($packager === null) { + $packager = new Packager(); + } + if ($packagist === null) { + $packagist = new Client(); + } + $this->packager = $packager; + $this->packagist = $packagist; + } + protected function configure() { $this->setName('search') ->setDescription('Interactive search for project name') - ->addArgument('name', InputArgument::OPTIONAL, 'Project name or path', null); + ->addArgument('project', InputArgument::OPTIONAL, 'Project name or path', null); } + /** + * @param OutputInterface $output + * @param string $label + * @param array $choices + * @param ?string $abortable + * @return ?string + */ protected function select(OutputInterface $output, $label, array $choices, $abortable = null) { - $dialog = $this->getHelperSet()->get('dialog'); - /* @var $dialog DialogHelper */ + $dialog = $this->getHelper('dialog'); + assert($dialog instanceof DialogHelper); if (!$choices) { $output->writeln('No matching packages found'); - return; + return null; } // TODO: skip dialog, if exact match - if ($abortable === true) { - $abortable = 'Abort'; - } elseif ($abortable === false) { - $abortable = null; - } - $select = array_merge(array(0 => $abortable), array_values($choices)); if ($abortable === null) { unset($select[0]); @@ -56,7 +69,7 @@ protected function select(OutputInterface $output, $label, array $choices, $abor $index = $dialog->select($output, $label, $select); - if ($index == 0) { + if ($index === 0) { return null; } @@ -66,58 +79,52 @@ protected function select(OutputInterface $output, $label, array $choices, $abor protected function execute(InputInterface $input, OutputInterface $output) { - $packager = new Packager(); - $packager->setOutput($output); - $packager->coerceWritable(); + $this->packager->setOutput($output); + $this->packager->coerceWritable(); - $dialog = $this->getHelperSet()->get('dialog'); - /* @var $dialog DialogHelper */ + $dialog = $this->getHelper('dialog'); + assert($dialog instanceof DialogHelper); - $name = $input->getArgument('name'); + $project = $input->getArgument('project'); do { - if ($name === null) { + if ($project === null) { // ask for input - $name = $dialog->ask($output, 'Enter (partial) project name > '); + $project = $dialog->ask($output, 'Enter (partial) project name > '); } else { - $output->writeln('Searching for ' . $name . '...'); + $output->writeln('Searching for ' . $project . '...'); } - $packagist = new Client(); - $choices = array(); - foreach ($packagist->search($name) as $package) { - /* @var $package Result */ + foreach ($this->packagist->search($project) as $result) { + assert($result instanceof Result); - $label = str_pad($package->getName(), 39) . ' '; - $label = str_replace($name, '' . $name . '', $label); - $label .= $package->getDescription(); + $label = str_pad($result->getName(), 39) . ' '; + $label = str_replace($project, '' . $project . '', $label); + $label .= $result->getDescription(); - $label .= ' (⤓' . $package->getDownloads() . ')'; + $label .= ' (⤓' . $result->getDownloads() . ')'; - $choices[$package->getName()] = $label; + $choices[$result->getName()] = $label; } - $name = $this->select($output, 'Select matching package', $choices, 'Start new search'); - } while ($name === null); + $project = $this->select($output, 'Select matching package', $choices, 'Start new search'); + } while ($project === null); - $output->writeln('Selected ' . $name . ', listing versions...'); + $output->writeln('Selected ' . $project . ', listing versions...'); - $package = $packagist->get($name); - /* @var $package Package */ + $package = $this->packagist->get($project); + assert($package instanceof Package); $choices = array(); foreach ($package->getVersions() as $version) { - /* @var $version Version */ + assert($version instanceof Version); $label = $version->getVersion(); + /* @var ?string $bin */ $bin = $version->getBin(); - if ($bin === null) { - $label .= ' (no executable bin)'; - } else { - $label .= ' (☑ executable bin)'; - } + $label .= $bin !== null ? ' (☑ executable bin)' : ' (no executable bin)'; $choices[$version->getVersion()] = $label; } @@ -135,17 +142,14 @@ protected function execute(InputInterface $input, OutputInterface $output) ); if ($action === null) { - return; + return 0; } - - - $pharer = $packager->getPharer($name, $version); - + $pharer = $this->packager->getPharer($project, $version); if ($action === 'install') { - $path = $packager->getSystemBin($pharer); - $packager->install($pharer, $path); + $path = $this->packager->getSystemBin($pharer); + $this->packager->install($pharer, $path); } else { $pharer->build(); } diff --git a/src/Clue/PharComposer/Package/Bundle.php b/src/Clue/PharComposer/Package/Bundle.php index 2face2b..31af0ee 100644 --- a/src/Clue/PharComposer/Package/Bundle.php +++ b/src/Clue/PharComposer/Package/Bundle.php @@ -2,8 +2,8 @@ namespace Clue\PharComposer\Package; -use Symfony\Component\Finder\Finder; use Clue\PharComposer\Logger; +use Symfony\Component\Finder\Finder; /** * A bundle represents all resources from a package that should be bundled into @@ -23,7 +23,7 @@ class Bundle implements \IteratorAggregate * * @param Package $package * @param Logger $logger - * @return Bundle + * @return self */ public static function from(Package $package, Logger $logger) { diff --git a/src/Clue/PharComposer/Package/Bundler/BundlerInterface.php b/src/Clue/PharComposer/Package/Bundler/BundlerInterface.php index e09385b..15008ca 100644 --- a/src/Clue/PharComposer/Package/Bundler/BundlerInterface.php +++ b/src/Clue/PharComposer/Package/Bundler/BundlerInterface.php @@ -10,7 +10,7 @@ interface BundlerInterface /** * returns a bundle * - * @return Bundle + * @return \Clue\PharComposer\Package\Bundle */ public function bundle(); } diff --git a/src/Clue/PharComposer/Package/Package.php b/src/Clue/PharComposer/Package/Package.php index ae74856..9c4a8cd 100644 --- a/src/Clue/PharComposer/Package/Package.php +++ b/src/Clue/PharComposer/Package/Package.php @@ -2,17 +2,20 @@ namespace Clue\PharComposer\Package;; -use Symfony\Component\Finder\SplFileInfo; -use Clue\PharComposer\Package\Bundler\Explicit as ExplicitBundler; +use Clue\PharComposer\Package\Bundler\BundlerInterface; use Clue\PharComposer\Package\Bundler\Complete as CompleteBundler; -use Clue\PharComposer\Package\Autoload; +use Clue\PharComposer\Package\Bundler\Explicit as ExplicitBundler; use Clue\PharComposer\Logger; +use Symfony\Component\Finder\SplFileInfo; /** * The package represents either the main/root package or one of the vendor packages. */ class Package { + private $package; + private $directory; + /** * Instantiate package * @@ -143,7 +146,7 @@ public function getBlacklist() * * Only used for CompleteBundler at the moment * - * @return Closure + * @return \Closure * @uses self::getBlacklist() */ public function getBlacklistFilter() diff --git a/src/Clue/PharComposer/Phar/Packager.php b/src/Clue/PharComposer/Phar/Packager.php index e7ab068..0ae4b39 100644 --- a/src/Clue/PharComposer/Phar/Packager.php +++ b/src/Clue/PharComposer/Phar/Packager.php @@ -32,6 +32,9 @@ public function setBinSudo($bin) $this->binSudo = $bin; } + /** + * @param OutputInterface|bool|callable $fn + */ public function setOutput($fn) { if ($fn instanceof OutputInterface) { @@ -91,6 +94,14 @@ public function assertWritable() } } + /** + * @param string $path + * @param string $version + * @return PharComposer + * @throws UnexpectedValueException + * @throws InvalidArgumentException + * @throws RuntimeException + */ public function getPharer($path, $version = null) { if ($version !== null) { diff --git a/tests/Command/BuildTest.php b/tests/Command/BuildTest.php new file mode 100644 index 0000000..fa2056e --- /dev/null +++ b/tests/Command/BuildTest.php @@ -0,0 +1,59 @@ +setAccessible(true); + $packager = $ref->getValue($command); + + $this->assertInstanceOf('Clue\PharComposer\Phar\Packager', $packager); + } + + public function testExecuteBuildWillBuildPharer() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->exactly(2))->method('getArgument')->withConsecutive( + array('project'), + array('target') + )->willReturnOnConsecutiveCalls('dir', null); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $pharer = $this->getMockBuilder('Clue\PharComposer\Phar\PharComposer')->disableOriginalConstructor()->getMock(); + $pharer->expects($this->never())->method('setTarget'); + $pharer->expects($this->once())->method('build'); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + $packager->expects($this->once())->method('setOutput')->with($output); + $packager->expects($this->once())->method('getPharer')->with('dir')->willReturn($pharer); + + $command = new Build($packager); + $command->run($input, $output); + } + + public function testExecuteBuildWillBuildPharerWithExplicitTarget() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->exactly(2))->method('getArgument')->withConsecutive( + array('project'), + array('target') + )->willReturnOnConsecutiveCalls('dir', 'targetDir'); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $pharer = $this->getMockBuilder('Clue\PharComposer\Phar\PharComposer')->disableOriginalConstructor()->getMock(); + $pharer->expects($this->once())->method('setTarget')->with('targetDir'); + $pharer->expects($this->once())->method('build'); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + $packager->expects($this->once())->method('setOutput')->with($output); + $packager->expects($this->once())->method('getPharer')->with('dir')->willReturn($pharer); + + $command = new Build($packager); + $command->run($input, $output); + } +} diff --git a/tests/Command/InstallTest.php b/tests/Command/InstallTest.php new file mode 100644 index 0000000..34d2d38 --- /dev/null +++ b/tests/Command/InstallTest.php @@ -0,0 +1,119 @@ +setAccessible(true); + $packager = $ref->getValue($command); + + $this->assertInstanceOf('Clue\PharComposer\Phar\Packager', $packager); + } + + public function testExecuteInstallWillInstallPackager() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->exactly(2))->method('getArgument')->withConsecutive( + array('project'), + array('target') + )->willReturnOnConsecutiveCalls('dir', null); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $pharer = $this->getMockBuilder('Clue\PharComposer\Phar\PharComposer')->disableOriginalConstructor()->getMock(); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + $packager->expects($this->once())->method('setOutput')->with($output); + $packager->expects($this->once())->method('getPharer')->with('dir')->willReturn($pharer); + $packager->expects($this->once())->method('getSystemBin')->with($pharer, null)->willReturn('targetPath'); + $packager->expects($this->once())->method('install')->with($pharer, 'targetPath'); + + $command = new Install($packager); + $command->run($input, $output); + } + + public function testExecuteInstallWillInstallPackagerWithExplicitTarget() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->exactly(2))->method('getArgument')->withConsecutive( + array('project'), + array('target') + )->willReturnOnConsecutiveCalls('dir', 'targetDir'); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $pharer = $this->getMockBuilder('Clue\PharComposer\Phar\PharComposer')->disableOriginalConstructor()->getMock(); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + $packager->expects($this->once())->method('setOutput')->with($output); + $packager->expects($this->once())->method('getPharer')->with('dir')->willReturn($pharer); + $packager->expects($this->once())->method('getSystemBin')->with($pharer, 'targetDir')->willReturn('targetPath'); + $packager->expects($this->once())->method('install')->with($pharer, 'targetPath'); + + $command = new Install($packager); + $command->run($input, $output); + } + + public function testExecuteInstallWillInstallPackagerWhenTargetPathAlreadyExistsAndDialogQuestionYieldsYes() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->exactly(2))->method('getArgument')->withConsecutive( + array('project'), + array('target') + )->willReturnOnConsecutiveCalls('dir', null); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $dialogHelper = $this->getMock('Symfony\Component\Console\Helper\DialogHelper'); + $dialogHelper->expects($this->once())->method('askConfirmation')->willReturn(true); + + $helpers = new HelperSet(array( + 'dialog' => $dialogHelper + )); + + $pharer = $this->getMockBuilder('Clue\PharComposer\Phar\PharComposer')->disableOriginalConstructor()->getMock(); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + $packager->expects($this->once())->method('setOutput')->with($output); + $packager->expects($this->once())->method('getPharer')->with('dir')->willReturn($pharer); + $packager->expects($this->once())->method('getSystemBin')->with($pharer, null)->willReturn(__FILE__); + $packager->expects($this->once())->method('install')->with($pharer, __FILE__); + + $command = new Install($packager); + $command->setHelperSet($helpers); + $command->run($input, $output); + } + + public function testExecuteInstallWillNotInstallPackagerWhenTargetPathAlreadyExistsAndDialogQuestionShouldNotOverwrite() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->exactly(2))->method('getArgument')->withConsecutive( + array('project'), + array('target') + )->willReturnOnConsecutiveCalls('dir', null); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + $output->expects($this->once())->method('writeln')->with('Aborting'); + + $dialogHelper = $this->getMock('Symfony\Component\Console\Helper\DialogHelper'); + $dialogHelper->expects($this->once())->method('askConfirmation')->willReturn(false); + + $helpers = new HelperSet(array( + 'dialog' => $dialogHelper + )); + + $pharer = $this->getMockBuilder('Clue\PharComposer\Phar\PharComposer')->disableOriginalConstructor()->getMock(); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + $packager->expects($this->once())->method('setOutput')->with($output); + $packager->expects($this->once())->method('getPharer')->with('dir')->willReturn($pharer); + $packager->expects($this->once())->method('getSystemBin')->with($pharer, null)->willReturn(__FILE__); + $packager->expects($this->never())->method('install'); + + $command = new Install($packager); + $command->setHelperSet($helpers); + $command->run($input, $output); + } +} diff --git a/tests/Command/SearchTest.php b/tests/Command/SearchTest.php new file mode 100644 index 0000000..01dbe3a --- /dev/null +++ b/tests/Command/SearchTest.php @@ -0,0 +1,284 @@ +setAccessible(true); + $packager = $ref->getValue($command); + + $ref = new ReflectionProperty($command, 'packagist'); + $ref->setAccessible(true); + $packagist = $ref->getValue($command); + + $this->assertInstanceOf('Clue\PharComposer\Phar\Packager', $packager); + $this->assertInstanceOf('Packagist\Api\Client', $packagist); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage stop1 + */ + public function testExecuteWithoutProjectWillAskForProjectAndRunSearch() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->once())->method('getArgument')->with('project')->willReturn(null); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $dialogHelper = $this->getMock('Symfony\Component\Console\Helper\DialogHelper'); + $dialogHelper->expects($this->once())->method('ask')->willReturn('foo'); + + $helpers = new HelperSet(array( + 'dialog' => $dialogHelper + )); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + + $packagist = $this->getMock('Packagist\Api\Client'); + $packagist->expects($this->once())->method('search')->with('foo')->willThrowException(new RuntimeException('stop1')); + + $command = new Search($packager, $packagist); + $command->setHelperSet($helpers); + $command->run($input, $output); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage stop1 + */ + public function testExecuteWithProjectWillRunSearchWithoutAskingForProject() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->once())->method('getArgument')->with('project')->willReturn('foo'); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $dialogHelper = $this->getMock('Symfony\Component\Console\Helper\DialogHelper'); + $dialogHelper->expects($this->never())->method('ask'); + + $helpers = new HelperSet(array( + 'dialog' => $dialogHelper + )); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + + $packagist = $this->getMock('Packagist\Api\Client'); + $packagist->expects($this->once())->method('search')->with('foo')->willThrowException(new RuntimeException('stop1')); + + $command = new Search($packager, $packagist); + $command->setHelperSet($helpers); + $command->run($input, $output); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage stop1 + */ + public function testExecuteWithProjectAndSearchReturnsNoMatchesWillReportAndAskForOtherProject() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->once())->method('getArgument')->with('project')->willReturn('foo'); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + $output->expects($this->exactly(2))->method('writeln')->withConsecutive( + array('Searching for foo...'), + array('No matching packages found') + ); + + $dialogHelper = $this->getMock('Symfony\Component\Console\Helper\DialogHelper'); + $dialogHelper->expects($this->once())->method('ask')->willThrowException(new RuntimeException('stop1')); + + $helpers = new HelperSet(array( + 'dialog' => $dialogHelper + )); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + + $packagist = $this->getMock('Packagist\Api\Client'); + $packagist->expects($this->once())->method('search')->with('foo')->willReturn(array()); + + $command = new Search($packager, $packagist); + $command->setHelperSet($helpers); + $command->run($input, $output); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage stop1 + */ + public function testExecuteWithProjectAndSearchReturnsOneMatchWillAskForProject() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->once())->method('getArgument')->with('project')->willReturn('foo'); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $dialogHelper = $this->getMock('Symfony\Component\Console\Helper\DialogHelper'); + $dialogHelper->expects($this->once())->method('select')->willThrowException(new RuntimeException('stop1')); + $dialogHelper->expects($this->never())->method('ask'); + + $helpers = new HelperSet(array( + 'dialog' => $dialogHelper + )); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + + $result = $this->getMock('Packagist\Api\Result\Result'); + + $packagist = $this->getMock('Packagist\Api\Client'); + $packagist->expects($this->once())->method('search')->with('foo')->willReturn(array($result)); + + $command = new Search($packager, $packagist); + $command->setHelperSet($helpers); + $command->run($input, $output); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage stop1 + */ + public function testExecuteWithProjectSelectedWillSearchVersions() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->once())->method('getArgument')->with('project')->willReturn('foo'); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + $output->expects($this->exactly(2))->method('writeln')->withConsecutive( + array('Searching for foo...'), + array('Selected foo/bar, listing versions...') + ); + + $dialogHelper = $this->getMock('Symfony\Component\Console\Helper\DialogHelper'); + $dialogHelper->expects($this->once())->method('select')->willReturn(1); + $dialogHelper->expects($this->never())->method('ask'); + + $helpers = new HelperSet(array( + 'dialog' => $dialogHelper + )); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + + $result = $this->getMock('Packagist\Api\Result\Result'); + $result->expects($this->exactly(2))->method('getName')->willReturn('foo/bar'); + + $packagist = $this->getMock('Packagist\Api\Client'); + $packagist->expects($this->once())->method('search')->with('foo')->willReturn(array($result)); + $packagist->expects($this->once())->method('get')->with('foo/bar')->willThrowException(new RuntimeException('stop1')); + + $command = new Search($packager, $packagist); + $command->setHelperSet($helpers); + $command->run($input, $output); + } + + public function testExecuteWithProjectAndVersionSelectedWillQuitWhenAskedForActionYieldsQuit() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->once())->method('getArgument')->with('project')->willReturn('foo'); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + + $dialogHelper = $this->getMock('Symfony\Component\Console\Helper\DialogHelper'); + $dialogHelper->expects($this->exactly(3))->method('select')->willReturnOnConsecutiveCalls(1, 1, 0); + + $helpers = new HelperSet(array( + 'dialog' => $dialogHelper + )); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + + $result = $this->getMock('Packagist\Api\Result\Result'); + $result->expects($this->exactly(2))->method('getName')->willReturn('foo/bar'); + + $version = $this->getMock('Packagist\Api\Result\Package\Version'); + $version->expects($this->exactly(2))->method('getVersion')->willReturn('dev-master'); + + $package = $this->getMock('Packagist\Api\Result\Package'); + $package->expects($this->once())->method('getVersions')->willReturn(array($version)); + + $packagist = $this->getMock('Packagist\Api\Client'); + $packagist->expects($this->once())->method('search')->with('foo')->willReturn(array($result)); + $packagist->expects($this->once())->method('get')->with('foo/bar')->willReturn($package); + + $command = new Search($packager, $packagist); + $command->setHelperSet($helpers); + $command->run($input, $output); + } + + public function testExecuteWithProjectAndVersionSelectedWillBuildWhenAskedForActionYieldsBuild() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->once())->method('getArgument')->with('project')->willReturn('foo'); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $dialogHelper = $this->getMock('Symfony\Component\Console\Helper\DialogHelper'); + $dialogHelper->expects($this->exactly(3))->method('select')->willReturnOnConsecutiveCalls(1, 1, 1); + + $helpers = new HelperSet(array( + 'dialog' => $dialogHelper + )); + + $pharer = $this->getMockBuilder('Clue\PharComposer\Phar\PharComposer')->disableOriginalConstructor()->getMock(); + $pharer->expects($this->once())->method('build'); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + $packager->expects($this->once())->method('getPharer')->with('foo/bar', 'dev-master')->willReturn($pharer); + + $result = $this->getMock('Packagist\Api\Result\Result'); + $result->expects($this->exactly(2))->method('getName')->willReturn('foo/bar'); + + $version = $this->getMock('Packagist\Api\Result\Package\Version'); + $version->expects($this->exactly(2))->method('getVersion')->willReturn('dev-master'); + + $package = $this->getMock('Packagist\Api\Result\Package'); + $package->expects($this->once())->method('getVersions')->willReturn(array($version)); + + $packagist = $this->getMock('Packagist\Api\Client'); + $packagist->expects($this->once())->method('search')->with('foo')->willReturn(array($result)); + $packagist->expects($this->once())->method('get')->with('foo/bar')->willReturn($package); + + $command = new Search($packager, $packagist); + $command->setHelperSet($helpers); + $command->run($input, $output); + } + + public function testExecuteWithProjectAndVersionSelectedWillInstallWhenAskedForActionYieldsInstall() + { + $input = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $input->expects($this->once())->method('getArgument')->with('project')->willReturn('foo'); + $output = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + $dialogHelper = $this->getMock('Symfony\Component\Console\Helper\DialogHelper'); + $dialogHelper->expects($this->exactly(3))->method('select')->willReturnOnConsecutiveCalls(1, 1, 2); + + $helpers = new HelperSet(array( + 'dialog' => $dialogHelper + )); + + $pharer = $this->getMockBuilder('Clue\PharComposer\Phar\PharComposer')->disableOriginalConstructor()->getMock(); + $pharer->expects($this->never())->method('build'); + + $packager = $this->getMock('Clue\PharComposer\Phar\Packager'); + $packager->expects($this->once())->method('getPharer')->with('foo/bar', 'dev-master')->willReturn($pharer); + $packager->expects($this->once())->method('getSystemBin')->with($pharer)->willReturn('targetPath'); + $packager->expects($this->once())->method('install')->with($pharer, 'targetPath'); + + $result = $this->getMock('Packagist\Api\Result\Result'); + $result->expects($this->exactly(2))->method('getName')->willReturn('foo/bar'); + + $version = $this->getMock('Packagist\Api\Result\Package\Version'); + $version->expects($this->exactly(2))->method('getVersion')->willReturn('dev-master'); + + $package = $this->getMock('Packagist\Api\Result\Package'); + $package->expects($this->once())->method('getVersions')->willReturn(array($version)); + + $packagist = $this->getMock('Packagist\Api\Client'); + $packagist->expects($this->once())->method('search')->with('foo')->willReturn(array($result)); + $packagist->expects($this->once())->method('get')->with('foo/bar')->willReturn($package); + + $command = new Search($packager, $packagist); + $command->setHelperSet($helpers); + $command->run($input, $output); + } +}