From 067007656bbdc9102dbe605b2dbd7265602ecf2d Mon Sep 17 00:00:00 2001 From: everzet Date: Sat, 24 Sep 2011 00:23:16 +0300 Subject: [PATCH] LibraryInstaller refactored and tested --- src/Composer/Installer/InstallerInterface.php | 41 +++- src/Composer/Installer/LibraryInstaller.php | 139 ++++++++---- .../Test/Installer/LibraryInstallerTest.php | 207 ++++++++++++++++++ 3 files changed, 338 insertions(+), 49 deletions(-) create mode 100644 tests/Composer/Test/Installer/LibraryInstallerTest.php diff --git a/src/Composer/Installer/InstallerInterface.php b/src/Composer/Installer/InstallerInterface.php index 0a3154946..96af2bc93 100644 --- a/src/Composer/Installer/InstallerInterface.php +++ b/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 */ 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); } diff --git a/src/Composer/Installer/LibraryInstaller.php b/src/Composer/Installer/LibraryInstaller.php index 8f55b86eb..6b224fba7 100644 --- a/src/Composer/Installer/LibraryInstaller.php +++ b/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 * @author Konstantin Kudryashov */ 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); } } diff --git a/tests/Composer/Test/Installer/LibraryInstallerTest.php b/tests/Composer/Test/Installer/LibraryInstallerTest.php new file mode 100644 index 000000000..61ffc8d41 --- /dev/null +++ b/tests/Composer/Test/Installer/LibraryInstallerTest.php @@ -0,0 +1,207 @@ + + * Jordi Boggiano + * + * 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(); + } +}