Selaa lähdekoodia

LibraryInstaller refactored and tested

everzet 13 vuotta sitten
vanhempi
commit
067007656b

+ 40 - 1
src/Composer/Installer/InstallerInterface.php

@@ -12,14 +12,53 @@
 
 namespace Composer\Installer;
 
+use Composer\DependencyResolver\Operation\OperationInterface;
 use Composer\Package\PackageInterface;
-use Composer\Downloader\DownloaderInterface;
 
 /**
+ * Interface for the package installation manager.
+ *
  * @author Konstantin Kudryashov <ever.zet@gmail.com>
  */
 interface InstallerInterface
 {
+    /**
+     * Executes specific solver operation.
+     *
+     * @param   OperationInterface  $operation  solver operation instance
+     */
+    function executeOperation(OperationInterface $operation);
+
+    /**
+     * Checks that provided package is installed.
+     *
+     * @param   PackageInterface    $package    package instance
+     *
+     * @return  Boolean
+     */
     function isInstalled(PackageInterface $package);
+
+    /**
+     * Installs specific package.
+     *
+     * @param   PackageInterface    $package    package instance
+     */
     function install(PackageInterface $package);
+
+    /**
+     * Updates specific package.
+     *
+     * @param   PackageInterface    $initial    already installed package version
+     * @param   PackageInterface    $target     updated version
+     *
+     * @throws  InvalidArgumentException        if $from package is not installed
+     */
+    function update(PackageInterface $initial, PackageInterface $target);
+
+    /**
+     * Uninstalls specific package.
+     *
+     * @param   PackageInterface    $package    package instance
+     */
+    function uninstall(PackageInterface $package);
 }

+ 91 - 48
src/Composer/Installer/LibraryInstaller.php

@@ -12,24 +12,35 @@
 
 namespace Composer\Installer;
 
-use Composer\Downloader\DownloaderInterface;
+use Composer\Downloader\DownloadManager;
+use Composer\Installer\Registry\RegistryInterface;
+use Composer\Installer\Registry\FilesystemRegistry;
+use Composer\DependencyResolver\Operation\OperationInterface;
 use Composer\Package\PackageInterface;
-use Composer\Downloader\DownloaderInterface;
 
 /**
+ * Package installation manager.
+ *
  * @author Jordi Boggiano <j.boggiano@seld.be>
  * @author Konstantin Kudryashov <ever.zet@gmail.com>
  */
 class LibraryInstaller implements InstallerInterface
 {
     private $dir;
-    private $preferSource;
-    private $downloaders = array();
-
-    public function __construct($dir = 'vendor', $preferSource = false)
+    private $dm;
+    private $registry;
+
+    /**
+     * Initializes library installer.
+     *
+     * @param   string              $dir        relative path for packages home
+     * @param   DownloadManager     $dm         download manager
+     * @param   RegistryInterface   $registry   registry controller
+     */
+    public function __construct($dir, DownloadManager $dm, RegistryInterface $registry = null)
     {
         $this->dir = $dir;
-        $this->preferSource = $preferSource;
+        $this->dm  = $dm;
 
         if (!is_dir($this->dir)) {
             if (file_exists($this->dir)) {
@@ -43,67 +54,99 @@ class LibraryInstaller implements InstallerInterface
                 );
             }
         }
-    }
 
-    public function setDownloader($type, DownloaderInterface $downloader = null)
-    {
-        if (null === $downloader) {
-            unset($this->downloaders[$type]);
-
-            return;
+        if (null === $registry) {
+            $registry = new FilesystemRegistry('.composer', str_replace('/', '_', $dir));
         }
 
-        $this->downloaders[$type] = $downloader;
+        $this->registry = $registry;
+        $this->registry->open();
     }
 
-    public function getDownloader($type)
+    /**
+     * Closes packages registry.
+     */
+    public function __destruct()
     {
-        if (!isset($this->downloaders[$type])) {
-            throw new \UnexpectedValueException('Unknown source type: '.$type);
-        }
-
-        return $this->downloaders[$type];
+        $this->registry->close();
     }
 
-    public function install(PackageInterface $package)
+    /**
+     * Executes specific solver operation.
+     *
+     * @param   OperationInterface  $operation  solver operation instance
+     */
+    public function executeOperation(OperationInterface $operation)
     {
-        if (!($this->preferSource && $package->getSourceType()) && $package->getDistType()) {
-            $downloader = $this->getDownloader($package->getDistType());
-
-            return $downloader->download(
-                $package, $this->dir, $package->getDistUrl(), $package->getDistSha1Checksum()
-            );
-        }
-
-        if ($package->getSourceType()) {
-            $downloader = $this->getDownloader($package->getSourceType());
+        $method = $operation->getJobType();
 
-            return $downloader->download(
-                $package, $this->dir, $package->getSourceUrl()
-            );
+        if ('update' === $method) {
+            $this->$method($operation->getPackage(), $operation->getTargetPackage());
+        } else {
+            $this->$method($operation->getPackage());
         }
-
-        throw new \InvalidArgumentException('Package should have dist or source specified');
     }
 
+    /**
+     * Checks that specific package is installed.
+     *
+     * @param   PackageInterface    $package    package instance
+     *
+     * @return  Boolean
+     */
     public function isInstalled(PackageInterface $package)
     {
-        if ($package->getSourceType()) {
-            $downloader = $this->getDownloader($package->getSourceType());
+        return $this->registry->isPackageRegistered($package);
+    }
 
-            if ($downloader->isDownloaded($package, $this->dir)) {
-                return true;
-            }
+    /**
+     * Installs specific package.
+     *
+     * @param   PackageInterface    $package    package instance
+     *
+     * @throws  InvalidArgumentException        if provided package have no urls to download from
+     */
+    public function install(PackageInterface $package)
+    {
+        $type = $this->dm->download($package, $this->dir);
+        $this->registry->registerPackage($package, $type);
+    }
+
+    /**
+     * Updates specific package.
+     *
+     * @param   PackageInterface    $initial    already installed package version
+     * @param   PackageInterface    $target     updated version
+     *
+     * @throws  InvalidArgumentException        if $from package is not installed
+     */
+    public function update(PackageInterface $initial, PackageInterface $target)
+    {
+        if (!$this->registry->isPackageRegistered($initial)) {
+            throw new \UnexpectedValueException('Package is not installed: '.$initial);
         }
 
-        if ($package->getDistType()) {
-            $downloader = $this->getDownloader($package->getDistType());
+        $type = $this->registry->getRegisteredPackageInstallerType($initial);
+        $this->dm->update($initial, $target, $this->dir, $type);
+        $this->registry->unregisterPackage($initial);
+        $this->registry->registerPackage($target, $type);
+    }
 
-            if ($downloader->isDownloaded($package, $this->dir)) {
-                return true;
-            }
+    /**
+     * Uninstalls specific package.
+     *
+     * @param   PackageInterface    $package    package instance
+     *
+     * @throws  InvalidArgumentException        if package is not installed
+     */
+    public function uninstall(PackageInterface $package)
+    {
+        if (!$this->registry->isPackageRegistered($package)) {
+            throw new \UnexpectedValueException('Package is not installed: '.$package);
         }
 
-        return false;
+        $type = $this->registry->getRegisteredPackageInstallerType($package);
+        $this->dm->remove($package, $this->dir, $type);
+        $this->registry->unregisterPackage($package);
     }
 }

+ 207 - 0
tests/Composer/Test/Installer/LibraryInstallerTest.php

@@ -0,0 +1,207 @@
+<?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\Installer;
+
+use Composer\Installer\LibraryInstaller;
+use Composer\DependencyResolver\Operation;
+
+class LibraryInstallerTest extends \PHPUnit_Framework_TestCase
+{
+    private $dir;
+    private $dm;
+    private $registry;
+    private $library;
+
+    protected function setUp()
+    {
+        $this->dir = sys_get_temp_dir().'/composer';
+        if (is_dir($this->dir)) {
+            rmdir($this->dir);
+        }
+
+        $this->dm = $this->getMockBuilder('Composer\Downloader\DownloadManager')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->registry = $this->getMockBuilder('Composer\Installer\Registry\RegistryInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+    }
+
+    public function testInstallerCreation()
+    {
+        $this->registry
+            ->expects($this->once())
+            ->method('open');
+
+        $this->registry
+            ->expects($this->once())
+            ->method('close');
+
+        $library = new LibraryInstaller($this->dir, $this->dm, $this->registry);
+        $this->assertTrue(is_dir($this->dir));
+
+        $file = sys_get_temp_dir().'/file';
+        touch($file);
+
+        $this->setExpectedException('UnexpectedValueException');
+        $library = new LibraryInstaller($file, $this->dm, $this->registry);
+    }
+
+    public function testExecuteOperation()
+    {
+        $library = $this->getMockBuilder('Composer\Installer\LibraryInstaller')
+            ->setConstructorArgs(array($this->dir, $this->dm, $this->registry))
+            ->setMethods(array('install', 'update', 'uninstall'))
+            ->getMock();
+
+        $packageToInstall = $this->createPackageMock();
+        $packageToRemove  = $this->createPackageMock();
+        $packageToUpdate  = $this->createPackageMock();
+        $updatedPackage   = $this->createPackageMock();
+
+        $library
+            ->expects($this->once())
+            ->method('install')
+            ->with($packageToInstall);
+
+        $library
+            ->expects($this->once())
+            ->method('uninstall')
+            ->with($packageToRemove);
+
+        $library
+            ->expects($this->once())
+            ->method('update')
+            ->with($packageToUpdate, $updatedPackage);
+
+        $library->executeOperation(new Operation\InstallOperation($packageToInstall));
+        $library->executeOperation(new Operation\UninstallOperation($packageToRemove));
+        $library->executeOperation(new Operation\UpdateOperation($packageToUpdate, $updatedPackage));
+    }
+
+    public function testIsInstalled()
+    {
+        $library = new LibraryInstaller($this->dir, $this->dm, $this->registry);
+        $package = $this->createPackageMock();
+
+        $this->registry
+            ->expects($this->exactly(2))
+            ->method('isPackageRegistered')
+            ->with($package)
+            ->will($this->onConsecutiveCalls(true, false));
+
+        $this->assertTrue($library->isInstalled($package));
+        $this->assertFalse($library->isInstalled($package));
+    }
+
+    public function testInstall()
+    {
+        $library = new LibraryInstaller($this->dir, $this->dm, $this->registry);
+        $package = $this->createPackageMock();
+
+        $this->dm
+            ->expects($this->once())
+            ->method('download')
+            ->with($package, $this->dir)
+            ->will($this->returnValue('source'));
+
+        $this->registry
+            ->expects($this->once())
+            ->method('registerPackage')
+            ->with($package, 'source');
+
+        $library->install($package);
+    }
+
+    public function testUpdate()
+    {
+        $library = new LibraryInstaller($this->dir, $this->dm, $this->registry);
+        $initial = $this->createPackageMock();
+        $target  = $this->createPackageMock();
+
+        $this->registry
+            ->expects($this->exactly(2))
+            ->method('isPackageRegistered')
+            ->with($initial)
+            ->will($this->onConsecutiveCalls(true, false));
+
+        $this->registry
+            ->expects($this->once())
+            ->method('getRegisteredPackageInstallerType')
+            ->with($initial)
+            ->will($this->returnValue('dist'));
+
+        $this->dm
+            ->expects($this->once())
+            ->method('update')
+            ->with($initial, $target, $this->dir, 'dist');
+
+        $this->registry
+            ->expects($this->once())
+            ->method('unregisterPackage')
+            ->with($initial);
+
+        $this->registry
+            ->expects($this->once())
+            ->method('registerPackage')
+            ->with($target, 'dist');
+
+        $library->update($initial, $target);
+
+        $this->setExpectedException('UnexpectedValueException');
+
+        $library->update($initial, $target);
+    }
+
+    public function testUninstall()
+    {
+        $library = new LibraryInstaller($this->dir, $this->dm, $this->registry);
+        $package = $this->createPackageMock();
+
+        $this->registry
+            ->expects($this->exactly(2))
+            ->method('isPackageRegistered')
+            ->with($package)
+            ->will($this->onConsecutiveCalls(true, false));
+
+        $this->registry
+            ->expects($this->once())
+            ->method('getRegisteredPackageInstallerType')
+            ->with($package)
+            ->will($this->returnValue('source'));
+
+        $this->dm
+            ->expects($this->once())
+            ->method('remove')
+            ->with($package, $this->dir, 'source');
+
+        $this->registry
+            ->expects($this->once())
+            ->method('unregisterPackage')
+            ->with($package);
+
+        $library->uninstall($package);
+
+        $this->setExpectedException('UnexpectedValueException');
+
+        $library->uninstall($package);
+    }
+
+    private function createPackageMock()
+    {
+        return $this->getMockBuilder('Composer\Package\MemoryPackage')
+            ->setConstructorArgs(array(md5(rand()), '1.0.0'))
+            ->getMock();
+    }
+}