Browse Source

Add functional tests for the installer, fixes #580

Jordi Boggiano 13 years ago
parent
commit
4505df29df

+ 14 - 0
tests/Composer/Test/Fixtures/installer/SAMPLE

@@ -0,0 +1,14 @@
+--TEST--
+<test description>
+--CONDITION--
+<php code that checks if test can run, must return a Boolean>
+--COMPOSER--
+<composer.json file definition>
+--LOCK--
+<composer.lock file definition>
+--INSTALLED--
+<installed.json file definition>
+--INSTALLED:DEV--
+<installed_dev.json file definition>
+--EXPECT-- or --EXPECT:UPDATE-- or --EXPECT:DEV-- or --EXPECT:UPDATE:DEV--
+<output (stringified operations)>

+ 23 - 0
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)

+ 18 - 0
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)

+ 41 - 0
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)

+ 128 - 1
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<test>.*?)\s*
+                (?:--CONDITION--\s*(?P<condition>.*?))?\s*
+                --COMPOSER--\s*(?P<composer>.*?)\s*
+                (?:--LOCK--\s*(?P<lock>.*?))?\s*
+                (?:--INSTALLED--\s*(?P<installed>.*?))?\s*
+                (?:--INSTALLED:DEV--\s*(?P<installedDev>.*?))?\s*
+                --EXPECT(?P<update>:UPDATE)?(?P<dev>:DEV)?--\s*(?P<expect>.*?)\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;
+    }
 }

+ 50 - 0
tests/Composer/Test/Mock/FactoryMock.php

@@ -0,0 +1,50 @@
+<?php
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * 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)
+    {
+    }
+}

+ 13 - 0
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()

+ 25 - 0
tests/Composer/Test/Mock/InstalledFilesystemRepositoryMock.php

@@ -0,0 +1,25 @@
+<?php
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * 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()
+    {
+    }
+}

+ 15 - 10
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());