diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index d14662518..db26fdfed 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -188,7 +188,12 @@ class Installer // output suggestions foreach ($this->suggestedPackages as $suggestion) { - if (!$installedRepo->findPackages($suggestion['target'])) { + $target = $suggestion['target']; + if ($installedRepo->filterPackages(function (PackageInterface $package) use ($target) { + if (in_array($target, $package->getNames())) { + return false; + } + })) { $this->io->write($suggestion['source'].' suggests installing '.$suggestion['target'].' ('.$suggestion['reason'].')'); } } diff --git a/tests/Composer/Test/Fixtures/installer/suggest-installed.test b/tests/Composer/Test/Fixtures/installer/suggest-installed.test new file mode 100644 index 000000000..f46102d0a --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/suggest-installed.test @@ -0,0 +1,29 @@ +--TEST-- +Suggestions are not displayed for installed packages +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0", "suggest": { "b/b": "an obscure reason" } }, + { "name": "b/b", "version": "1.0.0" } + ] + } + ], + "require": { + "a/a": "1.0.0", + "b/b": "1.0.0" + } +} +--RUN-- +install +--EXPECT-OUTPUT-- +Loading composer repositories with package information +Installing dependencies +Writing lock file +Generating autoload files + +--EXPECT-- +Installing a/a (1.0.0) +Installing b/b (1.0.0) \ No newline at end of file diff --git a/tests/Composer/Test/Fixtures/installer/suggest-replaced.test b/tests/Composer/Test/Fixtures/installer/suggest-replaced.test new file mode 100644 index 000000000..d1e8f6102 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/suggest-replaced.test @@ -0,0 +1,29 @@ +--TEST-- +Suggestions are not displayed for packages if they are replaced +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0", "suggest": { "b/b": "an obscure reason" } }, + { "name": "c/c", "version": "1.0.0", "replace": { "b/b": "1.0.0" } } + ] + } + ], + "require": { + "a/a": "1.0.0", + "b/b": "1.0.0" + } +} +--RUN-- +install +--EXPECT-OUTPUT-- +Loading composer repositories with package information +Installing dependencies +Writing lock file +Generating autoload files + +--EXPECT-- +Installing a/a (1.0.0) +Installing c/c (1.0.0) \ No newline at end of file diff --git a/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test b/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test new file mode 100644 index 000000000..d2ea37766 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/suggest-uninstalled.test @@ -0,0 +1,27 @@ +--TEST-- +Suggestions are displayed +--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 +--EXPECT-OUTPUT-- +Loading composer repositories with package information +Installing dependencies +a/a suggests installing b/b (an obscure reason) +Writing lock file +Generating autoload files + +--EXPECT-- +Installing a/a (1.0.0) \ No newline at end of file diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index d4635f246..cca2fd95f 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -123,7 +123,7 @@ class InstallerTest extends TestCase /** * @dataProvider getIntegrationTests */ - public function testIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $installedDev, $run, $expectLock, $expect) + public function testIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $installedDev, $run, $expectLock, $expectOutput, $expect) { if ($condition) { eval('$res = '.$condition.';'); @@ -226,6 +226,10 @@ class InstallerTest extends TestCase $installationManager = $composer->getInstallationManager(); $this->assertSame($expect, implode("\n", $installationManager->getTrace())); + + if ($expectOutput) { + $this->assertEquals($expectOutput, $output); + } } public function getIntegrationTests() @@ -250,6 +254,7 @@ class InstallerTest extends TestCase (?:--INSTALLED-DEV--\s*(?P'.$content.'))?\s* --RUN--\s*(?P.*?)\s* (?:--EXPECT-LOCK--\s*(?P'.$content.'))?\s* + (?:--EXPECT-OUTPUT--\s*(?P'.$content.'))?\s* --EXPECT--\s*(?P.*?)\s* $}xs'; @@ -279,6 +284,7 @@ class InstallerTest extends TestCase if (!empty($match['expectLock'])) { $expectLock = JsonFile::parseJson($match['expectLock']); } + $expectOutput = $match['expectOutput']; $expect = $match['expect']; } catch (\Exception $e) { die(sprintf('Test "%s" is not valid: '.$e->getMessage(), str_replace($fixturesDir.'/', '', $file))); @@ -287,7 +293,7 @@ class InstallerTest extends TestCase die(sprintf('Test "%s" is not valid, did not match the expected format.', str_replace($fixturesDir.'/', '', $file))); } - $tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $installedDev, $run, $expectLock, $expect); + $tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $installedDev, $run, $expectLock, $expectOutput, $expect); } return $tests;