From 4505df29dfbb762e0254210e55c2d9b0db1335e0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 13 May 2012 21:40:42 +0200 Subject: [PATCH] Add functional tests for the installer, fixes #580 --- tests/Composer/Test/Fixtures/installer/SAMPLE | 14 ++ .../Test/Fixtures/installer/install-dev.test | 23 ++++ .../Fixtures/installer/install-simple.test | 18 +++ .../Test/Fixtures/installer/update-all.test | 41 ++++++ tests/Composer/Test/InstallerTest.php | 129 +++++++++++++++++- tests/Composer/Test/Mock/FactoryMock.php | 50 +++++++ .../Test/Mock/InstallationManagerMock.php | 13 ++ .../InstalledFilesystemRepositoryMock.php | 25 ++++ .../Test/Repository/Vcs/GitHubDriverTest.php | 25 ++-- 9 files changed, 327 insertions(+), 11 deletions(-) create mode 100644 tests/Composer/Test/Fixtures/installer/SAMPLE create mode 100644 tests/Composer/Test/Fixtures/installer/install-dev.test create mode 100644 tests/Composer/Test/Fixtures/installer/install-simple.test create mode 100644 tests/Composer/Test/Fixtures/installer/update-all.test create mode 100644 tests/Composer/Test/Mock/FactoryMock.php create mode 100644 tests/Composer/Test/Mock/InstalledFilesystemRepositoryMock.php diff --git a/tests/Composer/Test/Fixtures/installer/SAMPLE b/tests/Composer/Test/Fixtures/installer/SAMPLE new file mode 100644 index 000000000..618f6edf2 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/SAMPLE @@ -0,0 +1,14 @@ +--TEST-- + +--CONDITION-- + +--COMPOSER-- + +--LOCK-- + +--INSTALLED-- + +--INSTALLED:DEV-- + +--EXPECT-- or --EXPECT:UPDATE-- or --EXPECT:DEV-- or --EXPECT:UPDATE:DEV-- + \ No newline at end of file diff --git a/tests/Composer/Test/Fixtures/installer/install-dev.test b/tests/Composer/Test/Fixtures/installer/install-dev.test new file mode 100644 index 000000000..0dfa1bec1 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/install-dev.test @@ -0,0 +1,23 @@ +--TEST-- +Installs a package in dev env +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0" }, + { "name": "a/b", "version": "1.0.0" } + ] + } + ], + "require": { + "a/a": "1.0.0" + }, + "require-dev": { + "a/b": "1.0.0" + } +} +--EXPECT:DEV-- +Installing a/a (1.0.0) +Installing a/b (1.0.0) \ No newline at end of file diff --git a/tests/Composer/Test/Fixtures/installer/install-simple.test b/tests/Composer/Test/Fixtures/installer/install-simple.test new file mode 100644 index 000000000..6cc46d3aa --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/install-simple.test @@ -0,0 +1,18 @@ +--TEST-- +Installs a simple package with exact match requirement +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0" } + ] + } + ], + "require": { + "a/a": "1.0.0" + } +} +--EXPECT-- +Installing a/a (1.0.0) \ No newline at end of file diff --git a/tests/Composer/Test/Fixtures/installer/update-all.test b/tests/Composer/Test/Fixtures/installer/update-all.test new file mode 100644 index 000000000..e7641dbb9 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-all.test @@ -0,0 +1,41 @@ +--TEST-- +Updates updateable packages +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0" }, + { "name": "a/a", "version": "1.0.1" }, + { "name": "a/a", "version": "1.1.0" }, + + { "name": "a/b", "version": "1.0.0" }, + { "name": "a/b", "version": "1.0.1" }, + { "name": "a/b", "version": "2.0.0" }, + + { "name": "a/c", "version": "1.0.0" }, + { "name": "a/c", "version": "2.0.0" } + ] + } + ], + "require": { + "a/a": "1.0.*", + "a/c": "1.*" + }, + "require-dev": { + "a/b": "*" + } +} +--INSTALLED-- +[ + { "name": "a/a", "version": "1.0.0" }, + { "name": "a/c", "version": "1.0.0" } +] +--INSTALLED:DEV-- +[ + { "name": "a/b", "version": "1.0.0" } +] +--EXPECT:UPDATE:DEV-- +Updating a/a (1.0.0) to a/a (1.0.1) +Updating a/b (1.0.0) to a/b (2.0.0) \ No newline at end of file diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 478815851..b6a809dc1 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -12,13 +12,18 @@ namespace Composer\Test; use Composer\Installer; +use Composer\Config; +use Composer\Json\JsonFile; use Composer\Repository\ArrayRepository; use Composer\Repository\RepositoryManager; use Composer\Repository\RepositoryInterface; use Composer\Package\PackageInterface; use Composer\Package\Link; -use Composer\Test\Mock\WritableRepositoryMock; +use Composer\Package\Locker; +use Composer\Test\Mock\FactoryMock; +use Composer\Test\Mock\InstalledFilesystemRepositoryMock; use Composer\Test\Mock\InstallationManagerMock; +use Composer\Test\Mock\WritableRepositoryMock; class InstallerTest extends TestCase { @@ -112,4 +117,126 @@ class InstallerTest extends TestCase return $cases; } + + /** + * @dataProvider getIntegrationTests + */ + public function testIntegration($file, $message, $condition, $composer, $lock, $installed, $installedDev, $update, $dev, $expect) + { + if ($condition) { + eval('$res = '.$condition.';'); + if (!$res) { + $this->markTestSkipped($condition); + } + } + + $io = $this->getMock('Composer\IO\IOInterface'); + + $composer = FactoryMock::create($io, $composer); + + $jsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock(); + $jsonMock->expects($this->any()) + ->method('read') + ->will($this->returnValue($installed)); + $jsonMock->expects($this->any()) + ->method('exists') + ->will($this->returnValue(true)); + + $devJsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock(); + $devJsonMock->expects($this->any()) + ->method('read') + ->will($this->returnValue($installedDev)); + $devJsonMock->expects($this->any()) + ->method('exists') + ->will($this->returnValue(true)); + + $repositoryManager = $composer->getRepositoryManager(); + $repositoryManager->setLocalRepository(new InstalledFilesystemRepositoryMock($jsonMock)); + $repositoryManager->setLocalDevRepository(new InstalledFilesystemRepositoryMock($devJsonMock)); + + $lockJsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock(); + $lockJsonMock->expects($this->any()) + ->method('read') + ->will($this->returnValue($lock)); + + $locker = new Locker($lockJsonMock, $repositoryManager, isset($lock['hash']) ? $lock['hash'] : ''); + $composer->setLocker($locker); + + $autoloadGenerator = $this->getMock('Composer\Autoload\AutoloadGenerator'); + + $installer = Installer::create( + $io, + $composer, + null, + $autoloadGenerator + ); + + $installer->setDevMode($dev)->setUpdate($update); + + $result = $installer->run(); + $this->assertTrue($result); + + $expectedInstalled = isset($options['install']) ? $options['install'] : array(); + $expectedUpdated = isset($options['update']) ? $options['update'] : array(); + $expectedUninstalled = isset($options['uninstall']) ? $options['uninstall'] : array(); + + $installationManager = $composer->getInstallationManager(); + $this->assertSame($expect, implode("\n", $installationManager->getTrace())); + } + + public function getIntegrationTests() + { + $fixturesDir = realpath(__DIR__.'/Fixtures/installer/'); + $tests = array(); + + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($fixturesDir), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if (!preg_match('/\.test$/', $file)) { + continue; + } + + $test = file_get_contents($file->getRealpath()); + + $pattern = '{^ + --TEST--\s*(?P.*?)\s* + (?:--CONDITION--\s*(?P.*?))?\s* + --COMPOSER--\s*(?P.*?)\s* + (?:--LOCK--\s*(?P.*?))?\s* + (?:--INSTALLED--\s*(?P.*?))?\s* + (?:--INSTALLED:DEV--\s*(?P.*?))?\s* + --EXPECT(?P:UPDATE)?(?P:DEV)?--\s*(?P.*?)\s* + $}xs'; + + $installed = array(); + $installedDev = array(); + $lock = array(); + + if (preg_match($pattern, $test, $match)) { + try { + $message = $match['test']; + $condition = !empty($match['condition']) ? $match['condition'] : null; + $composer = JsonFile::parseJson($match['composer']); + if (!empty($match['lock'])) { + $lock = JsonFile::parseJson($match['lock']); + } + if (!empty($match['installed'])) { + $installed = JsonFile::parseJson($match['installed']); + } + if (!empty($match['installedDev'])) { + $installedDev = JsonFile::parseJson($match['installedDev']); + } + $update = !empty($match['update']); + $dev = !empty($match['dev']); + $expect = $match['expect']; + } catch (\Exception $e) { + die(sprintf('Test "%s" is not valid: '.$e->getMessage(), str_replace($fixturesDir.'/', '', $file))); + } + } else { + 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, $update, $dev, $expect); + } + + return $tests; + } } diff --git a/tests/Composer/Test/Mock/FactoryMock.php b/tests/Composer/Test/Mock/FactoryMock.php new file mode 100644 index 000000000..b2c087239 --- /dev/null +++ b/tests/Composer/Test/Mock/FactoryMock.php @@ -0,0 +1,50 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Mock; + +use Composer\Config; +use Composer\Factory; +use Composer\Repository; +use Composer\Repository\RepositoryManager; +use Composer\Installer; +use Composer\Downloader; +use Composer\IO\IOInterface; + +class FactoryMock extends Factory +{ + public static function createConfig() + { + $config = new Config(); + + $config->merge(array('config' => array('home' => sys_get_temp_dir().'/composer-test'))); + + return $config; + } + + protected function addLocalRepository(RepositoryManager $rm, $vendorDir) + { + } + + protected function addPackagistRepository(array $localConfig) + { + return $localConfig; + } + + protected function createInstallationManager(Repository\RepositoryManager $rm, Downloader\DownloadManager $dm, $vendorDir, $binDir, IOInterface $io) + { + return new InstallationManagerMock; + } + + protected function purgePackages(Repository\RepositoryManager $rm, Installer\InstallationManager $im) + { + } +} diff --git a/tests/Composer/Test/Mock/InstallationManagerMock.php b/tests/Composer/Test/Mock/InstallationManagerMock.php index 9a7af786b..6474d1637 100644 --- a/tests/Composer/Test/Mock/InstallationManagerMock.php +++ b/tests/Composer/Test/Mock/InstallationManagerMock.php @@ -23,20 +23,33 @@ class InstallationManagerMock extends InstallationManager private $installed = array(); private $updated = array(); private $uninstalled = array(); + private $trace = array(); public function install(RepositoryInterface $repo, InstallOperation $operation) { $this->installed[] = $operation->getPackage(); + $this->trace[] = (string) $operation; + $repo->addPackage(clone $operation->getPackage()); } public function update(RepositoryInterface $repo, UpdateOperation $operation) { $this->updated[] = array($operation->getInitialPackage(), $operation->getTargetPackage()); + $this->trace[] = (string) $operation; + $repo->removePackage($operation->getInitialPackage()); + $repo->addPackage(clone $operation->getTargetPackage()); } public function uninstall(RepositoryInterface $repo, UninstallOperation $operation) { $this->uninstalled[] = $operation->getPackage(); + $this->trace[] = (string) $operation; + $repo->removePackage($operation->getPackage()); + } + + public function getTrace() + { + return $this->trace; } public function getInstalledPackages() diff --git a/tests/Composer/Test/Mock/InstalledFilesystemRepositoryMock.php b/tests/Composer/Test/Mock/InstalledFilesystemRepositoryMock.php new file mode 100644 index 000000000..020b5eeda --- /dev/null +++ b/tests/Composer/Test/Mock/InstalledFilesystemRepositoryMock.php @@ -0,0 +1,25 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Mock; + +use Composer\Repository\InstalledFilesystemRepository; + +class InstalledFilesystemRepositoryMock extends InstalledFilesystemRepository +{ + public function reload() + { + } + + public function write() + { + } +} diff --git a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php index bbd2d3896..f800a499f 100644 --- a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php @@ -19,6 +19,18 @@ use Composer\Config; class GitHubDriverTest extends \PHPUnit_Framework_TestCase { + private $config; + + public function setUp() + { + $this->config = new Config(); + $this->config->merge(array( + 'config' => array( + 'home' => sys_get_temp_dir() . '/composer-test', + ), + )); + } + public function testPrivateRepository() { $scheme = extension_loaded('openssl') ? 'https' : 'http'; @@ -62,7 +74,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false)) ->will($this->returnValue('{"master_branch": "test_master"}')); - $gitHubDriver = new GitHubDriver($repoUrl, $io, new Config(), null, $remoteFilesystem); + $gitHubDriver = new GitHubDriver($repoUrl, $io, $this->config, null, $remoteFilesystem); $gitHubDriver->initialize(); $this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha)); @@ -112,7 +124,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false)) ->will($this->returnValue('{"master_branch": "test_master"}')); - $gitHubDriver = new GitHubDriver($repoUrl, $io, new Config(), null, $remoteFilesystem); + $gitHubDriver = new GitHubDriver($repoUrl, $io, $this->config, null, $remoteFilesystem); $gitHubDriver->initialize(); $this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha)); @@ -171,13 +183,6 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase $fs = new Filesystem(); $fs->removeDirectory(sys_get_temp_dir() . '/composer-test'); - $config = new Config(); - $config->merge(array( - 'config' => array( - 'home' => sys_get_temp_dir() . '/composer-test', - ), - )); - $process->expects($this->at(0)) ->method('execute') ->with($this->stringContains($repoSshUrl)) @@ -207,7 +212,7 @@ class GitHubDriverTest extends \PHPUnit_Framework_TestCase ->method('splitLines') ->will($this->returnValue(array('* test_master'))); - $gitHubDriver = new GitHubDriver($repoUrl, $io, $config, $process, $remoteFilesystem); + $gitHubDriver = new GitHubDriver($repoUrl, $io, $this->config, $process, $remoteFilesystem); $gitHubDriver->initialize(); $this->assertEquals('test_master', $gitHubDriver->getRootIdentifier());