diff --git a/doc/03-cli.md b/doc/03-cli.md index 296cb101b..71154fa40 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -106,7 +106,6 @@ resolution. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. -* **--no-suggest:** Skips suggested packages in the output. * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take a bit of time to run so it is currently not done by default. @@ -156,7 +155,6 @@ php composer.phar update "vendor/*" * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. -* **--no-suggest:** Skips suggested packages in the output. * **--with-dependencies:** Add also dependencies of whitelisted packages to the whitelist, except those that are root requirements. * **--with-all-dependencies:** Add also all dependencies of whitelisted packages to the whitelist, including those that are root requirements. * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster @@ -203,7 +201,6 @@ If you do not specify a package, composer will prompt you to search for a packag * **--prefer-dist:** Install packages from `dist` when available. * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. -* **--no-suggest:** Skips suggested packages in the output. * **--no-update:** Disables the automatic update of the dependencies. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--update-no-dev:** Run the dependency update with the `--no-dev` option. @@ -410,16 +407,16 @@ Lists all packages suggested by currently installed set of packages. You can optionally pass one or multiple package names in the format of `vendor/package` to limit output to suggestions made by those packages only. -Use the `--by-package` or `--by-suggestion` flags to group the output by +Use the `--by-package` (default) or `--by-suggestion` flags to group the output by the package offering the suggestions or the suggested packages respectively. -Use the `--verbose (-v)` flag to display the suggesting package and the suggestion reason. -This implies `--by-package --by-suggestion`, showing both lists. +If you only want a list of suggested package names, use `--list`. ### Options -* **--by-package:** Groups output by suggesting package. +* **--by-package:** Groups output by suggesting package (default). * **--by-suggestion:** Groups output by suggested package. +* **--list:** Show only list of suggested package names. * **--no-dev:** Excludes suggestions from `require-dev` packages. ## depends (why) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 1a2c808db..d059928cc 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -44,7 +44,6 @@ class InstallCommand extends BaseCommand new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), - new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), @@ -107,7 +106,6 @@ EOT ->setDevMode(!$input->getOption('no-dev')) ->setDumpAutoloader(!$input->getOption('no-autoloader')) ->setRunScripts(!$input->getOption('no-scripts')) - ->setSkipSuggest($input->getOption('no-suggest')) ->setOptimizeAutoloader($optimize) ->setClassMapAuthoritative($authoritative) ->setApcuAutoloader($apcu) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index a881e7de1..0118aa959 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -55,7 +55,6 @@ class RequireCommand extends InitCommand new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), new InputOption('fixed', null, InputOption::VALUE_NONE, 'Write fixed version to the composer.json.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), - new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'), new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'), @@ -259,7 +258,6 @@ EOT ->setPreferDist($input->getOption('prefer-dist')) ->setDevMode($updateDevMode) ->setRunScripts(!$input->getOption('no-scripts')) - ->setSkipSuggest($input->getOption('no-suggest')) ->setOptimizeAutoloader($optimize) ->setClassMapAuthoritative($authoritative) ->setApcuAutoloader($apcu) diff --git a/src/Composer/Command/SuggestsCommand.php b/src/Composer/Command/SuggestsCommand.php index 6c2619751..07ca1ce00 100644 --- a/src/Composer/Command/SuggestsCommand.php +++ b/src/Composer/Command/SuggestsCommand.php @@ -13,6 +13,9 @@ namespace Composer\Command; use Composer\Repository\PlatformRepository; +use Composer\Repository\RootPackageRepository; +use Composer\Repository\CompositeRepository; +use Composer\Installer\SuggestedPackagesReporter; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -26,8 +29,9 @@ class SuggestsCommand extends BaseCommand ->setName('suggests') ->setDescription('Shows package suggestions.') ->setDefinition(array( - new InputOption('by-package', null, InputOption::VALUE_NONE, 'Groups output by suggesting package'), + new InputOption('by-package', null, InputOption::VALUE_NONE, 'Groups output by suggesting package (default)'), new InputOption('by-suggestion', null, InputOption::VALUE_NONE, 'Groups output by suggested package'), + new InputOption('list', null, InputOption::VALUE_NONE, 'Show only list of suggested package names'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Exclude suggestions from require-dev packages'), new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that you want to list suggestions from.'), )) @@ -36,8 +40,6 @@ class SuggestsCommand extends BaseCommand The %command.name% command shows a sorted list of suggested packages. -Enabling -v implies --by-package --by-suggestion, showing both lists. - Read more at https://getcomposer.org/doc/03-cli.md#suggests EOT ) @@ -49,108 +51,50 @@ EOT */ protected function execute(InputInterface $input, OutputInterface $output) { - $lock = $this->getComposer()->getLocker()->getLockData(); + $composer = $this->getComposer(); - if (empty($lock)) { - throw new \RuntimeException('Lockfile seems to be empty?'); + $installedRepos = array( + new RootPackageRepository(array(clone $composer->getPackage())), + ); + + $locker = $composer->getLocker(); + if ($locker->isLocked()) { + $installedRepos[] = new PlatformRepository(array(), $locker->getPlatformOverrides()); + $installedRepos[] = $locker->getLockedRepository(!$input->getOption('no-dev')); + } else { + $installedRepos[] = new PlatformRepository(array(), $composer->getConfig()->get('platform') ?: array()); + $installedRepos[] = $composer->getRepositoryManager()->getLocalRepository(); } - $packages = $lock['packages']; - - if (!$input->getOption('no-dev')) { - $packages += $lock['packages-dev']; - } + $installedRepo = new CompositeRepository($installedRepos); + $reporter = new SuggestedPackagesReporter($this->getIO()); $filter = $input->getArgument('packages'); - - // First assemble lookup list of packages that are installed, replaced or provided - $installed = array(); - foreach ($packages as $package) { - $installed[] = $package['name']; - - if (!empty($package['provide'])) { - $installed = array_merge($installed, array_keys($package['provide'])); - } - - if (!empty($package['replace'])) { - $installed = array_merge($installed, array_keys($package['replace'])); - } - } - - // Undub and sort the install list into a sorted lookup array - $installed = array_flip($installed); - ksort($installed); - - // Init platform repo - $platform = new PlatformRepository(array(), $this->getComposer()->getConfig()->get('platform') ?: array()); - - // Next gather all suggestions that are not in that list - $suggesters = array(); - $suggested = array(); - foreach ($packages as $package) { - $packageName = $package['name']; - if ((!empty($filter) && !in_array($packageName, $filter)) || empty($package['suggest'])) { + foreach ($installedRepo->getPackages() as $package) { + if (!empty($filter) && !in_array($package->getName(), $filter)) { continue; } - foreach ($package['suggest'] as $suggestion => $reason) { - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $suggestion) && null !== $platform->findPackage($suggestion, '*')) { - continue; - } - if (!isset($installed[$suggestion])) { - $suggesters[$packageName][$suggestion] = $reason; - $suggested[$suggestion][$packageName] = $reason; - } - } - } - ksort($suggesters); - ksort($suggested); - // Determine output mode - $mode = 0; + $reporter->addSuggestionsFromPackage($package); + } + + // Determine output mode, default is by-package + $mode = SuggestedPackagesReporter::MODE_BY_PACKAGE; $io = $this->getIO(); - if ($input->getOption('by-package') || $io->isVerbose()) { - $mode |= 1; - } + // if by-suggestion is given we override the default if ($input->getOption('by-suggestion')) { - $mode |= 2; + $mode = SuggestedPackagesReporter::MODE_BY_SUGGESTION; + } + // unless by-package is also present then we enable both + if ($input->getOption('by-package')) { + $mode |= SuggestedPackagesReporter::MODE_BY_PACKAGE; + } + // list is exclusive and overrides everything else + if ($input->getOption('list')) { + $mode = SuggestedPackagesReporter::MODE_LIST; } - // Simple mode - if ($mode === 0) { - foreach (array_keys($suggested) as $suggestion) { - $io->write(sprintf('%s', $suggestion)); - } - - return 0; - } - - // Grouped by package - if ($mode & 1) { - foreach ($suggesters as $suggester => $suggestions) { - $io->write(sprintf('%s suggests:', $suggester)); - - foreach ($suggestions as $suggestion => $reason) { - $io->write(sprintf(' - %s: %s', $suggestion, $reason ?: '*')); - } - $io->write(''); - } - } - - // Grouped by suggestion - if ($mode & 2) { - // Improve readability in full mode - if ($mode & 1) { - $io->write(str_repeat('-', 78)); - } - foreach ($suggested as $suggestion => $suggesters) { - $io->write(sprintf('%s is suggested by:', $suggestion)); - - foreach ($suggesters as $suggester => $reason) { - $io->write(sprintf(' - %s: %s', $suggester, $reason ?: '*')); - } - $io->write(''); - } - } + $reporter->output($mode, $installedRepo); return 0; } diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 0c3d3e6c7..a6a5dc4f7 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -48,7 +48,6 @@ class UpdateCommand extends BaseCommand new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), - new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'), new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also dependencies of whitelisted packages to the whitelist, except those defined in root package.'), new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist, including those defined in root package.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), @@ -154,7 +153,6 @@ EOT ->setDevMode(!$input->getOption('no-dev')) ->setDumpAutoloader(!$input->getOption('no-autoloader')) ->setRunScripts(!$input->getOption('no-scripts')) - ->setSkipSuggest($input->getOption('no-suggest')) ->setOptimizeAutoloader($optimize) ->setClassMapAuthoritative($authoritative) ->setApcuAutoloader($apcu) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index a1f93695d..627adee76 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -131,7 +131,6 @@ class Installer protected $ignorePlatformReqs = false; protected $preferStable = false; protected $preferLowest = false; - protected $skipSuggest = false; protected $writeLock; protected $executeOperations = true; @@ -257,9 +256,13 @@ class Installer $this->installationManager->notifyInstalls($this->io); } - // output suggestions if we're in dev mode - if ($this->update && $this->devMode && !$this->skipSuggest) { - $this->suggestedPackagesReporter->output($this->locker->getLockedRepository($this->devMode)); + if ($this->update) { + $installedRepos = array( + $this->locker->getLockedRepository($this->devMode), + $this->createPlatformRepo(false), + new RootPackageRepository(array(clone $this->package)), + ); + $this->suggestedPackagesReporter->outputMinimalistic(new CompositeRepository($installedRepos)); } // Find abandoned packages and warn user @@ -1310,19 +1313,6 @@ class Installer return $this; } - /** - * Should suggestions be skipped? - * - * @param bool $skipSuggest - * @return Installer - */ - public function setSkipSuggest($skipSuggest = true) - { - $this->skipSuggest = (bool) $skipSuggest; - - return $this; - } - /** * Disables plugins. * diff --git a/src/Composer/Installer/SuggestedPackagesReporter.php b/src/Composer/Installer/SuggestedPackagesReporter.php index 023aeb91d..c40bbd2c0 100644 --- a/src/Composer/Installer/SuggestedPackagesReporter.php +++ b/src/Composer/Installer/SuggestedPackagesReporter.php @@ -24,6 +24,10 @@ use Symfony\Component\Console\Formatter\OutputFormatter; */ class SuggestedPackagesReporter { + const MODE_LIST = 1; + const MODE_BY_PACKAGE = 2; + const MODE_BY_SUGGESTION = 4; + /** * @var array */ @@ -91,38 +95,105 @@ class SuggestedPackagesReporter /** * Output suggested packages. + * * Do not list the ones already installed if installed repository provided. * - * @param RepositoryInterface $installedRepo Installed packages + * @param int $mode One of the MODE_* constants from this class * @return SuggestedPackagesReporter */ - public function output(RepositoryInterface $lockedRepo = null) + public function output($mode, RepositoryInterface $installedRepo = null) + { + $suggestedPackages = $this->getFilteredSuggestions($installedRepo); + + $suggesters = array(); + $suggested = array(); + foreach ($suggestedPackages as $suggestion) { + $suggesters[$suggestion['source']][$suggestion['target']] = $suggestion['reason']; + $suggested[$suggestion['target']][$suggestion['source']] = $suggestion['reason']; + } + ksort($suggesters); + ksort($suggested); + + // Simple mode + if ($mode & self::MODE_LIST) { + foreach (array_keys($suggested) as $name) { + $this->io->write(sprintf('%s', $name)); + } + + return 0; + } + + // Grouped by package + if ($mode & self::MODE_BY_PACKAGE) { + foreach ($suggesters as $suggester => $suggestions) { + $this->io->write(sprintf('%s suggests:', $suggester)); + + foreach ($suggestions as $suggestion => $reason) { + $this->io->write(sprintf(' - %s' . ($reason ? ': %s' : ''), $suggestion, $this->escapeOutput($reason))); + } + $this->io->write(''); + } + } + + // Grouped by suggestion + if ($mode & self::MODE_BY_SUGGESTION) { + // Improve readability in full mode + if ($mode & self::MODE_BY_PACKAGE) { + $this->io->write(str_repeat('-', 78)); + } + foreach ($suggested as $suggestion => $suggesters) { + $this->io->write(sprintf('%s is suggested by:', $suggestion)); + + foreach ($suggesters as $suggester => $reason) { + $this->io->write(sprintf(' - %s' . ($reason ? ': %s' : ''), $suggester, $this->escapeOutput($reason))); + } + $this->io->write(''); + } + } + + return $this; + } + + /** + * Output number of new suggested packages and a hint to use suggest command. + ** + * Do not list the ones already installed if installed repository provided. + * + * @return SuggestedPackagesReporter + */ + public function outputMinimalistic(RepositoryInterface $installedRepo = null) + { + $suggestedPackages = $this->getFilteredSuggestions($installedRepo); + if ($suggestedPackages) { + $this->io->writeError(count($suggestedPackages).' package suggestions were added by new dependencies, use composer suggest to see details.'); + } + + return $this; + } + + private function getFilteredSuggestions(RepositoryInterface $installedRepo = null) { $suggestedPackages = $this->getPackages(); - $lockedPackages = array(); - if (null !== $lockedRepo && ! empty($suggestedPackages)) { - foreach ($lockedRepo->getPackages() as $package) { - $lockedPackages = array_merge( - $lockedPackages, + $installedNames = array(); + if (null !== $installedRepo && !empty($suggestedPackages)) { + foreach ($installedRepo->getPackages() as $package) { + $installedNames = array_merge( + $installedNames, $package->getNames() ); } } + $suggestions = array(); foreach ($suggestedPackages as $suggestion) { - if (in_array($suggestion['target'], $lockedPackages)) { + if (in_array($suggestion['target'], $installedNames)) { continue; } - $this->io->writeError(sprintf( - '%s suggests installing %s%s', - $suggestion['source'], - $this->escapeOutput($suggestion['target']), - $this->escapeOutput('' !== $suggestion['reason'] ? ' ('.$suggestion['reason'].')' : '') - )); + $suggestions[] = $suggestion; } - return $this; + return $suggestions; } /** diff --git a/tests/Composer/Test/Fixtures/installer/suggest-prod-nolock.test b/tests/Composer/Test/Fixtures/installer/suggest-prod-nolock.test new file mode 100644 index 000000000..82bf8558d --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/suggest-prod-nolock.test @@ -0,0 +1,32 @@ +--TEST-- +Suggestions are displayed even in non-dev mode for new suggesters installed when updating the lock file +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0", "suggest": { "b/b": "an obscure reason" } } + ] + } + ], + "require": { + "a/a": "1.0.0" + } +} +--RUN-- +install --no-dev +--EXPECT-OUTPUT-- +No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file. +Loading composer repositories with package information +Updating dependencies +Lock file operations: 1 install, 0 updates, 0 removals + - Locking a/a (1.0.0) +Writing lock file +Installing dependencies from lock file +Package operations: 1 install, 0 updates, 0 removals +1 package suggestions were added by new dependencies, use composer suggest to see details. +Generating autoload files + +--EXPECT-- +Installing a/a (1.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/suggest-prod.test b/tests/Composer/Test/Fixtures/installer/suggest-prod.test index 6a00b5607..ed2023504 100644 --- a/tests/Composer/Test/Fixtures/installer/suggest-prod.test +++ b/tests/Composer/Test/Fixtures/installer/suggest-prod.test @@ -1,5 +1,5 @@ --TEST-- -Suggestions are not displayed in non-dev mode +Suggestions are not displayed for when not updating the lock file --COMPOSER-- { "repositories": [ @@ -14,16 +14,25 @@ Suggestions are not displayed in non-dev mode "a/a": "1.0.0" } } +--LOCK-- +{ + "packages": [ + { "name": "a/a", "version": "1.0.0", "suggest": { "b/b": "an obscure reason" } } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} --RUN-- -install --no-dev +install --EXPECT-OUTPUT-- -No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file. -Loading composer repositories with package information -Updating dependencies -Lock file operations: 1 install, 0 updates, 0 removals - - Locking a/a (1.0.0) -Writing lock file -Installing dependencies from lock file +Installing dependencies from lock file (including require-dev) +Verifying lock file contents can be installed on current platform. Package operations: 1 install, 0 updates, 0 removals Generating autoload files diff --git a/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test b/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test index 1b88b2d8b..97e05b713 100644 --- a/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test +++ b/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test @@ -25,7 +25,7 @@ Lock file operations: 1 install, 0 updates, 0 removals Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 1 install, 0 updates, 0 removals -a/a suggests installing b/b (an obscure reason) +1 package suggestions were added by new dependencies, use composer suggest to see details. Generating autoload files --EXPECT-- diff --git a/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php b/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php index 03bb0b5fb..286b386d9 100644 --- a/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php +++ b/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php @@ -33,14 +33,13 @@ class SuggestedPackagesReporterTest extends TestCase /** * @covers ::__construct */ - public function testContrsuctor() + public function testConstructor() { $this->io->expects($this->once()) - ->method('writeError'); + ->method('write'); - $suggestedPackagesReporter = new SuggestedPackagesReporter($this->io); - $suggestedPackagesReporter->addPackage('a', 'b', 'c'); - $suggestedPackagesReporter->output(); + $this->suggestedPackagesReporter->addPackage('a', 'b', 'c'); + $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_LIST); } /** @@ -135,25 +134,33 @@ class SuggestedPackagesReporterTest extends TestCase { $this->suggestedPackagesReporter->addPackage('a', 'b', 'c'); - $this->io->expects($this->once()) - ->method('writeError') - ->with('a suggests installing b (c)'); + $this->io->expects($this->at(0)) + ->method('write') + ->with('a suggests:'); - $this->suggestedPackagesReporter->output(); + $this->io->expects($this->at(1)) + ->method('write') + ->with(' - b: c'); + + $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE); } /** * @covers ::output */ - public function testOutputWithNoSuggestedPackage() + public function testOutputWithNoSuggestionReason() { $this->suggestedPackagesReporter->addPackage('a', 'b', ''); - $this->io->expects($this->once()) - ->method('writeError') - ->with('a suggests installing b'); + $this->io->expects($this->at(0)) + ->method('write') + ->with('a suggests:'); - $this->suggestedPackagesReporter->output(); + $this->io->expects($this->at(1)) + ->method('write') + ->with(' - b'); + + $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE); } /** @@ -165,14 +172,18 @@ class SuggestedPackagesReporterTest extends TestCase $this->suggestedPackagesReporter->addPackage('source', 'target2', "Like us on Facebook"); $this->io->expects($this->at(0)) - ->method('writeError') - ->with("source suggests installing target1 ([1;37;42m Like us on Facebook [0m)"); + ->method('write') + ->with('source suggests:'); $this->io->expects($this->at(1)) - ->method('writeError') - ->with('source suggests installing target2 (\\Like us on Facebook\\)'); + ->method('write') + ->with(' - target1: [1;37;42m Like us on Facebook [0m'); - $this->suggestedPackagesReporter->output(); + $this->io->expects($this->at(2)) + ->method('write') + ->with(' - target2: \\Like us on Facebook\\'); + + $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE); } /** @@ -184,14 +195,26 @@ class SuggestedPackagesReporterTest extends TestCase $this->suggestedPackagesReporter->addPackage('source package', 'target', 'because reasons'); $this->io->expects($this->at(0)) - ->method('writeError') - ->with('a suggests installing b (c)'); + ->method('write') + ->with('a suggests:'); $this->io->expects($this->at(1)) - ->method('writeError') - ->with('source package suggests installing target (because reasons)'); + ->method('write') + ->with(' - b: c'); - $this->suggestedPackagesReporter->output(); + $this->io->expects($this->at(2)) + ->method('write') + ->with(''); + + $this->io->expects($this->at(3)) + ->method('write') + ->with('source package suggests:'); + + $this->io->expects($this->at(4)) + ->method('write') + ->with(' - target: because reasons'); + + $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE); } /** @@ -221,11 +244,15 @@ class SuggestedPackagesReporterTest extends TestCase $this->suggestedPackagesReporter->addPackage('a', 'b', 'c'); $this->suggestedPackagesReporter->addPackage('source package', 'target', 'because reasons'); - $this->io->expects($this->once()) - ->method('writeError') - ->with('source package suggests installing target (because reasons)'); + $this->io->expects($this->at(0)) + ->method('write') + ->with('source package suggests:'); - $this->suggestedPackagesReporter->output($repository); + $this->io->expects($this->at(1)) + ->method('write') + ->with(' - target: because reasons'); + + $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE, $repository); } /** @@ -237,7 +264,7 @@ class SuggestedPackagesReporterTest extends TestCase $repository->expects($this->exactly(0)) ->method('getPackages'); - $this->suggestedPackagesReporter->output($repository); + $this->suggestedPackagesReporter->output(SuggestedPackagesReporter::MODE_BY_PACKAGE, $repository); } private function getSuggestedPackageArray()