diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 0fc7c49af..565ff39c4 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -113,6 +113,11 @@ class RootPackageLoader extends ArrayLoader } } + if (isset($links[$config['name']])) { + throw new \InvalidArgumentException(sprintf('Root package \'%s\' cannot require itself in its composer.json' . PHP_EOL . + 'Did you accidentally name your root package after an external package?', $config['name'])); + } + $realPackage->setAliases($aliases); $realPackage->setStabilityFlags($stabilityFlags); $realPackage->setReferences($references); diff --git a/tests/Composer/Test/Fixtures/installer/install-self-from-root.test b/tests/Composer/Test/Fixtures/installer/install-self-from-root.test new file mode 100644 index 000000000..82092c77f --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/install-self-from-root.test @@ -0,0 +1,16 @@ +--TEST-- +Tries to require a package with the same name as the root package +--COMPOSER-- +{ + "name": "foo/bar", + "require": { + "foo/bar": "@dev" + } +} +--RUN-- +install +--EXPECT-EXCEPTION-- +InvalidArgumentException +--EXPECT-- +Root package 'foo/bar' cannot require itself in its composer.json +Did you accidentally name your root package after an external package? diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index b0a51fea7..eaf6caa03 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -140,7 +140,7 @@ class InstallerTest extends TestCase /** * @dataProvider getIntegrationTests */ - public function testIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectExitCode) + public function testIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectResult) { if ($condition) { eval('$res = '.$condition.';'); @@ -150,6 +150,14 @@ class InstallerTest extends TestCase } $io = new BufferIO('', OutputInterface::VERBOSITY_NORMAL, new OutputFormatter(false)); + + // Prepare for exceptions + if (!is_int($expectResult)) { + $normalizedOutput = rtrim(str_replace("\n", PHP_EOL, $expect)); + $this->setExpectedException($expectResult, $normalizedOutput); + } + + // Create Composer mock object according to configuration $composer = FactoryMock::create($io, $composerConfig); $jsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock(); @@ -225,9 +233,14 @@ class InstallerTest extends TestCase $appOutput = fopen('php://memory', 'w+'); $result = $application->run(new StringInput($run), new StreamOutput($appOutput)); fseek($appOutput, 0); - $output = str_replace("\r", '', $io->getOutput()); - $this->assertEquals($expectExitCode, $result, $output . stream_get_contents($appOutput)); + // Shouldn't check output and results if an exception was expected by this point + if (!is_int($expectResult)) { + return; + } + + $output = str_replace("\r", '', $io->getOutput()); + $this->assertEquals($expectResult, $result, $output . stream_get_contents($appOutput)); if ($expectLock) { unset($actualLock['hash']); unset($actualLock['content-hash']); @@ -259,7 +272,7 @@ class InstallerTest extends TestCase $installedDev = array(); $lock = array(); $expectLock = array(); - $expectExitCode = 0; + $expectResult = 0; try { $message = $testData['TEST']; @@ -296,12 +309,21 @@ class InstallerTest extends TestCase } $expectOutput = isset($testData['EXPECT-OUTPUT']) ? $testData['EXPECT-OUTPUT'] : null; $expect = $testData['EXPECT']; - $expectExitCode = isset($testData['EXPECT-EXIT-CODE']) ? (int) $testData['EXPECT-EXIT-CODE'] : 0; + if (!empty($testData['EXPECT-EXCEPTION'])) { + $expectResult = $testData['EXPECT-EXCEPTION']; + if (!empty($testData['EXPECT-EXIT-CODE'])) { + throw new \LogicException('EXPECT-EXCEPTION and EXPECT-EXIT-CODE are mutually exclusive'); + } + } elseif (!empty($testData['EXPECT-EXIT-CODE'])) { + $expectResult = (int) $testData['EXPECT-EXIT-CODE']; + } else { + $expectResult = 0; + } } catch (\Exception $e) { die(sprintf('Test "%s" is not valid: '.$e->getMessage(), str_replace($fixturesDir.'/', '', $file))); } - $tests[basename($file)] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectExitCode); + $tests[basename($file)] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectResult); } return $tests; @@ -321,6 +343,7 @@ class InstallerTest extends TestCase 'EXPECT-LOCK' => false, 'EXPECT-OUTPUT' => false, 'EXPECT-EXIT-CODE' => false, + 'EXPECT-EXCEPTION' => false, 'EXPECT' => true, );