Browse Source

Execute all operations at once which lets us download all packages in parallel then install only once all downloads succeeded, fixes #2847

This also changes the PRE/POST_PACKAGE_INSTALL/UPDATE/UNINSTALL events to have less information available on them, repositorySet, request and policy are gone
Jordi Boggiano 5 years ago
parent
commit
006985a0ea

+ 1 - 1
src/Composer/Command/CreateProjectCommand.php

@@ -356,7 +356,7 @@ EOT
             ->setPreferDist($preferDist);
 
         $projectInstaller = new ProjectInstaller($directory, $dm);
-        $im = $factory->createInstallationManager(new Loop($httpDownloader));
+        $im = $factory->createInstallationManager(new Loop($httpDownloader), $io, $composer->getEventDispatcher);
         $im->addInstaller($projectInstaller);
         $im->execute(new InstalledFilesystemRepository(new JsonFile('php://memory')), new InstallOperation($package));
         $im->notifyInstalls($io);

+ 10 - 13
src/Composer/EventDispatcher/EventDispatcher.php

@@ -100,21 +100,18 @@ class EventDispatcher
     /**
      * Dispatch a package event.
      *
-     * @param string              $eventName     The constant in PackageEvents
-     * @param bool                $devMode       Whether or not we are in dev mode
-     * @param PolicyInterface     $policy        The policy
-     * @param RepositorySet       $repositorySet The repository set
-     * @param CompositeRepository $installedRepo The installed repository
-     * @param Request             $request       The request
-     * @param array               $operations    The list of operations
-     * @param OperationInterface  $operation     The package being installed/updated/removed
+     * @param string              $eventName  The constant in PackageEvents
+     * @param bool                $devMode    Whether or not we are in dev mode
+     * @param RepositoryInterface $localRepo  The installed repository
+     * @param array               $operations The list of operations
+     * @param OperationInterface  $operation  The package being installed/updated/removed
      *
      * @return int return code of the executed script if any, for php scripts a false return
      *             value is changed to 1, anything else to 0
      */
-    public function dispatchPackageEvent($eventName, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation)
+    public function dispatchPackageEvent($eventName, $devMode, RepositoryInterface $localRepo, array $operations, OperationInterface $operation)
     {
-        return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $policy, $repositorySet, $installedRepo, $request, $operations, $operation));
+        return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $localRepo, $operations, $operation));
     }
 
     /**
@@ -124,16 +121,16 @@ class EventDispatcher
      * @param bool                $devMode       Whether or not we are in dev mode
      * @param PolicyInterface     $policy        The policy
      * @param RepositorySet       $repositorySet The repository set
-     * @param CompositeRepository $installedRepo The installed repository
+     * @param RepositoryInterface $localRepo     The installed repository
      * @param Request             $request       The request
      * @param array               $operations    The list of operations
      *
      * @return int return code of the executed script if any, for php scripts a false return
      *             value is changed to 1, anything else to 0
      */
-    public function dispatchInstallerEvent($eventName, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, RepositoryInterface $lockedRepo, Request $request, array $operations = array())
+    public function dispatchInstallerEvent($eventName, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, RepositoryInterface $localRepo, Request $request, array $operations = array())
     {
-        return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $devMode, $policy, $repositorySet, $lockedRepo, $request, $operations));
+        return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $devMode, $policy, $repositorySet, $localRepo, $request, $operations));
     }
 
     /**

+ 3 - 3
src/Composer/Factory.php

@@ -354,7 +354,7 @@ class Factory
         $composer->setPackage($package);
 
         // initialize installation manager
-        $im = $this->createInstallationManager($loop);
+        $im = $this->createInstallationManager($loop, $io, $dispatcher);
         $composer->setInstallationManager($im);
 
         if ($fullLoad) {
@@ -527,9 +527,9 @@ class Factory
     /**
      * @return Installer\InstallationManager
      */
-    public function createInstallationManager(Loop $loop)
+    public function createInstallationManager(Loop $loop, IOInterface $io, EventDispatcher $eventDispatcher = null)
     {
-        return new Installer\InstallationManager($loop);
+        return new Installer\InstallationManager($loop, $io, $eventDispatcher);
     }
 
     /**

+ 8 - 22
src/Composer/Installer.php

@@ -212,7 +212,6 @@ class Installer
             $this->executeOperations = false;
             $this->writeLock = false;
             $this->dumpAutoloader = false;
-            $this->installationManager->addInstaller(new NoopInstaller);
             $this->mockLocalRepositories($this->repositoryManager);
         }
 
@@ -664,27 +663,14 @@ class Installer
             }
         }
 
-        foreach ($localRepoTransaction->getOperations() as $operation) {
-            $jobType = $operation->getJobType();
-            $event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($jobType);
-            if (defined($event) && $this->runScripts) {
-                //$this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $repositorySet, $installedRepo, $request, $operations, $operation);
-            }
-
-            // output op, but alias op only in debug verbosity
-            if ((!$this->executeOperations && false === strpos($operation->getJobType(), 'Alias')) || $this->io->isDebug()) {
-                $this->io->writeError('  - ' . $operation->show(false));
-            }
-
-            $this->installationManager->execute($localRepo, $operation);
-
-            if ($this->executeOperations) {
-                $localRepo->write($this->devMode, $this->installationManager);
-            }
-
-            $event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($jobType);
-            if (defined($event) && $this->runScripts) {
-                //$this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $repositorySet, $installedRepo, $request, $operations, $operation);
+        if ($this->executeOperations) {
+            $this->installationManager->execute($localRepo, $localRepoTransaction->getOperations(), $this->devMode);
+        } else {
+            foreach ($localRepoTransaction->getOperations() as $operation) {
+                // output op, but alias op only in debug verbosity
+                if (false === strpos($operation->getJobType(), 'Alias') || $this->io->isDebug()) {
+                    $this->io->writeError('  - ' . $operation->show(false));
+                }
             }
         }
 

+ 77 - 42
src/Composer/Installer/InstallationManager.php

@@ -23,6 +23,7 @@ use Composer\DependencyResolver\Operation\UpdateOperation;
 use Composer\DependencyResolver\Operation\UninstallOperation;
 use Composer\DependencyResolver\Operation\MarkAliasInstalledOperation;
 use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation;
+use Composer\EventDispatcher\EventDispatcher;
 use Composer\Util\StreamContextFactory;
 use Composer\Util\Loop;
 
@@ -39,10 +40,14 @@ class InstallationManager
     private $cache = array();
     private $notifiablePackages = array();
     private $loop;
+    private $io;
+    private $eventDispatcher;
 
-    public function __construct(Loop $loop)
+    public function __construct(Loop $loop, IOInterface $io, EventDispatcher $eventDispatcher = null)
     {
         $this->loop = $loop;
+        $this->io = $io;
+        $this->eventDispatcher = $eventDispatcher;
     }
 
     public function reset()
@@ -158,70 +163,100 @@ class InstallationManager
     /**
      * Executes solver operation.
      *
-     * @param RepositoryInterface $repo      repository in which to check
-     * @param OperationInterface  $operation operation instance
+     * @param RepositoryInterface  $repo       repository in which to add/remove/update packages
+     * @param OperationInterface[] $operations operations to execute
+     * @param bool                 $devMode    whether the install is being run in dev mode
+     * @param bool                 $operation  whether to dispatch script events
      */
-    public function execute(RepositoryInterface $repo, OperationInterface $operation)
+    public function execute(RepositoryInterface $repo, array $operations, $devMode = true, $runScripts = true)
     {
-        // TODO this should take all operations in one go
-        $method = $operation->getJobType();
+        $promises = array();
 
-        if ($method === 'install') {
-            $package = $operation->getPackage();
-            $installer = $this->getInstaller($package->getType());
-            $promise = $installer->download($package);
-        } elseif ($method === 'update') {
-            $target = $operation->getTargetPackage();
-            $targetType = $target->getType();
-            $installer = $this->getInstaller($targetType);
-            $promise = $installer->download($target, $operation->getInitialPackage());
+        foreach ($operations as $operation) {
+            $method = $operation->getJobType();
+            $promise = null;
+
+            if ($method === 'install') {
+                $package = $operation->getPackage();
+                $installer = $this->getInstaller($package->getType());
+                $promise = $installer->download($package);
+            } elseif ($method === 'update') {
+                $target = $operation->getTargetPackage();
+                $targetType = $target->getType();
+                $installer = $this->getInstaller($targetType);
+                $promise = $installer->download($target, $operation->getInitialPackage());
+            }
+
+            if ($promise) {
+                $promises[] = $promise;
+            }
         }
 
-        if (!empty($promise)) {
-            $this->loop->wait(array($promise));
+        if (!empty($promises)) {
+            $this->loop->wait($promises);
         }
 
-        $e = null;
-        try {
+        foreach ($operations as $operation) {
+            $method = $operation->getJobType();
+            $event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($method);
+            if (defined($event) && $runScripts && $this->eventDispatcher) {
+                $this->eventDispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation);
+            }
+
+            // output alias ops in debug verbosity as they have no output otherwise
+            if ($this->io->isDebug()) {
+                $this->io->writeError('  - ' . $operation->show(false));
+            }
+
+            $e = null;
+            try {
+                if ($method === 'install' || $method === 'uninstall') {
+                    $package = $operation->getPackage();
+                    $installer = $this->getInstaller($package->getType());
+                    $promise = $installer->prepare($method, $package);
+                } elseif ($method === 'update') {
+                    $target = $operation->getTargetPackage();
+                    $targetType = $target->getType();
+                    $installer = $this->getInstaller($targetType);
+                    $promise = $installer->prepare('update', $target, $operation->getInitialPackage());
+                }
+
+                if (!empty($promise)) {
+                    $this->loop->wait(array($promise));
+                }
+
+                $promise = $this->$method($repo, $operation);
+                if (!empty($promise)) {
+                    $this->loop->wait(array($promise));
+                }
+            } catch (\Exception $e) {
+            }
+
             if ($method === 'install' || $method === 'uninstall') {
                 $package = $operation->getPackage();
                 $installer = $this->getInstaller($package->getType());
-                $promise = $installer->prepare($method, $package);
+                $promise = $installer->cleanup($method, $package);
             } elseif ($method === 'update') {
                 $target = $operation->getTargetPackage();
                 $targetType = $target->getType();
                 $installer = $this->getInstaller($targetType);
-                $promise = $installer->prepare('update', $target, $operation->getInitialPackage());
+                $promise = $installer->cleanup('update', $target, $operation->getInitialPackage());
             }
 
             if (!empty($promise)) {
                 $this->loop->wait(array($promise));
             }
 
-            $promise = $this->$method($repo, $operation);
-            if (!empty($promise)) {
-                $this->loop->wait(array($promise));
+            if ($e) {
+                throw $e;
             }
-        } catch (\Exception $e) {
-        }
-
-        if ($method === 'install' || $method === 'uninstall') {
-            $package = $operation->getPackage();
-            $installer = $this->getInstaller($package->getType());
-            $promise = $installer->cleanup($method, $package);
-        } elseif ($method === 'update') {
-            $target = $operation->getTargetPackage();
-            $targetType = $target->getType();
-            $installer = $this->getInstaller($targetType);
-            $promise = $installer->cleanup('update', $target, $operation->getInitialPackage());
-        }
 
-        if (!empty($promise)) {
-            $this->loop->wait(array($promise));
-        }
+            $repo->write($devMode, $this);
 
-        if ($e) {
-            throw $e;
+            $event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($method);
+            if (defined($event) && $runScripts && $this->eventDispatcher) {
+                $this->eventDispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation);
+            }
         }
     }
 

+ 9 - 9
src/Composer/Installer/InstallerEvent.php

@@ -18,7 +18,7 @@ use Composer\DependencyResolver\Operation\OperationInterface;
 use Composer\DependencyResolver\Request;
 use Composer\EventDispatcher\Event;
 use Composer\IO\IOInterface;
-use Composer\Repository\CompositeRepository;
+use Composer\Repository\RepositoryInterface;
 use Composer\Repository\RepositorySet;
 
 /**
@@ -54,9 +54,9 @@ class InstallerEvent extends Event
     private $repositorySet;
 
     /**
-     * @var CompositeRepository
+     * @var RepositoryInterface
      */
-    private $installedRepo;
+    private $localRepo;
 
     /**
      * @var Request
@@ -77,11 +77,11 @@ class InstallerEvent extends Event
      * @param bool                 $devMode
      * @param PolicyInterface      $policy
      * @param RepositorySet        $repositorySet
-     * @param CompositeRepository  $installedRepo
+     * @param RepositoryInterface  $localRepo
      * @param Request              $request
      * @param OperationInterface[] $operations
      */
-    public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, CompositeRepository $installedRepo, Request $request, array $operations = array())
+    public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, RepositoryInterface $localRepo, Request $request, array $operations = array())
     {
         parent::__construct($eventName);
 
@@ -90,7 +90,7 @@ class InstallerEvent extends Event
         $this->devMode = $devMode;
         $this->policy = $policy;
         $this->repositorySet = $repositorySet;
-        $this->installedRepo = $installedRepo;
+        $this->localRepo = $localRepo;
         $this->request = $request;
         $this->operations = $operations;
     }
@@ -136,11 +136,11 @@ class InstallerEvent extends Event
     }
 
     /**
-     * @return CompositeRepository
+     * @return RepositoryInterface
      */
-    public function getInstalledRepo()
+    public function getLocalRepo()
     {
-        return $this->installedRepo;
+        return $this->localRepo;
     }
 
     /**

+ 77 - 8
src/Composer/Installer/PackageEvent.php

@@ -17,18 +17,44 @@ use Composer\IO\IOInterface;
 use Composer\DependencyResolver\Operation\OperationInterface;
 use Composer\DependencyResolver\PolicyInterface;
 use Composer\DependencyResolver\Request;
-use Composer\Repository\CompositeRepository;
+use Composer\Repository\RepositoryInterface;
 use Composer\Repository\RepositorySet;
+use Composer\EventDispatcher\Event;
 
 /**
  * The Package Event.
  *
  * @author Jordi Boggiano <j.boggiano@seld.be>
  */
-class PackageEvent extends InstallerEvent
+class PackageEvent extends Event
 {
     /**
-     * @var OperationInterface The package instance
+     * @var Composer
+     */
+    private $composer;
+
+    /**
+     * @var IOInterface
+     */
+    private $io;
+
+    /**
+     * @var bool
+     */
+    private $devMode;
+
+    /**
+     * @var RepositoryInterface
+     */
+    private $localRepo;
+
+    /**
+     * @var OperationInterface[]
+     */
+    private $operations;
+
+    /**
+     * @var OperationInterface The operation instance which is being executed
      */
     private $operation;
 
@@ -39,20 +65,63 @@ class PackageEvent extends InstallerEvent
      * @param Composer             $composer
      * @param IOInterface          $io
      * @param bool                 $devMode
-     * @param PolicyInterface      $policy
-     * @param RepositorySet        $repositorySet
-     * @param CompositeRepository  $installedRepo
+     * @param RepositoryInterface  $localRepo
      * @param Request              $request
      * @param OperationInterface[] $operations
      * @param OperationInterface   $operation
      */
-    public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation)
+    public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, RepositoryInterface $localRepo, array $operations = array(), OperationInterface $operation)
     {
-        parent::__construct($eventName, $composer, $io, $devMode, $policy, $repositorySet, $installedRepo, $request, $operations);
+        parent::__construct($eventName);
 
+        $this->composer = $composer;
+        $this->io = $io;
+        $this->devMode = $devMode;
+        $this->localRepo = $localRepo;
+        $this->operations = $operations;
         $this->operation = $operation;
     }
 
+    /**
+     * @return Composer
+     */
+    public function getComposer()
+    {
+        return $this->composer;
+    }
+
+    /**
+     * @return IOInterface
+     */
+    public function getIO()
+    {
+        return $this->io;
+    }
+
+    /**
+     * @return bool
+     */
+    public function isDevMode()
+    {
+        return $this->devMode;
+    }
+
+    /**
+     * @return RepositoryInterface
+     */
+    public function getLocalRepo()
+    {
+        return $this->localRepo;
+    }
+
+    /**
+     * @return OperationInterface[]
+     */
+    public function getOperations()
+    {
+        return $this->operations;
+    }
+
     /**
      * Returns the package instance.
      *

+ 11 - 2
tests/Composer/Test/Fixtures/installer/update-all-dry-run.test

@@ -35,6 +35,15 @@ Updates updateable packages in dry-run mode
 ]
 --RUN--
 update --dry-run
+--EXPECT-OUTPUT--
+Loading composer repositories with package information
+Updating dependencies
+Lock file operations: 3 installs, 0 updates, 0 removals
+  - Locking a/a (1.0.1)
+  - Locking a/b (2.0.0)
+  - Locking a/c (1.0.0)
+Installing dependencies from lock file (including require-dev)
+Package operations: 0 installs, 2 updates, 0 removals
+  - Updating a/a (1.0.0) to a/a (1.0.1)
+  - Updating a/b (1.0.0) to a/b (2.0.0)
 --EXPECT--
-Updating a/a (1.0.0) to a/a (1.0.1)
-Updating a/b (1.0.0) to a/b (2.0.0)

+ 11 - 11
tests/Composer/Test/Installer/InstallationManagerTest.php

@@ -23,11 +23,13 @@ class InstallationManagerTest extends TestCase
 {
     protected $repository;
     protected $loop;
+    protected $io;
 
     public function setUp()
     {
         $this->loop = $this->getMockBuilder('Composer\Util\Loop')->disableOriginalConstructor()->getMock();
         $this->repository = $this->getMockBuilder('Composer\Repository\InstalledRepositoryInterface')->getMock();
+        $this->io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
     }
 
     public function testAddGetInstaller()
@@ -41,7 +43,7 @@ class InstallationManagerTest extends TestCase
                 return $arg === 'vendor';
             }));
 
-        $manager = new InstallationManager($this->loop);
+        $manager = new InstallationManager($this->loop, $this->io);
 
         $manager->addInstaller($installer);
         $this->assertSame($installer, $manager->getInstaller('vendor'));
@@ -70,7 +72,7 @@ class InstallationManagerTest extends TestCase
                 return $arg === 'vendor';
             }));
 
-        $manager = new InstallationManager($this->loop);
+        $manager = new InstallationManager($this->loop, $this->io);
 
         $manager->addInstaller($installer);
         $this->assertSame($installer, $manager->getInstaller('vendor'));
@@ -83,7 +85,7 @@ class InstallationManagerTest extends TestCase
     public function testExecute()
     {
         $manager = $this->getMockBuilder('Composer\Installer\InstallationManager')
-            ->setConstructorArgs(array($this->loop))
+            ->setConstructorArgs(array($this->loop, $this->io))
             ->setMethods(array('install', 'update', 'uninstall'))
             ->getMock();
 
@@ -112,15 +114,13 @@ class InstallationManagerTest extends TestCase
             ->with($this->repository, $updateOperation);
 
         $manager->addInstaller(new NoopInstaller());
-        $manager->execute($this->repository, $installOperation);
-        $manager->execute($this->repository, $removeOperation);
-        $manager->execute($this->repository, $updateOperation);
+        $manager->execute($this->repository, [$installOperation, $removeOperation, $updateOperation]);
     }
 
     public function testInstall()
     {
         $installer = $this->createInstallerMock();
-        $manager = new InstallationManager($this->loop);
+        $manager = new InstallationManager($this->loop, $this->io);
         $manager->addInstaller($installer);
 
         $package = $this->createPackageMock();
@@ -148,7 +148,7 @@ class InstallationManagerTest extends TestCase
     public function testUpdateWithEqualTypes()
     {
         $installer = $this->createInstallerMock();
-        $manager = new InstallationManager($this->loop);
+        $manager = new InstallationManager($this->loop, $this->io);
         $manager->addInstaller($installer);
 
         $initial = $this->createPackageMock();
@@ -182,7 +182,7 @@ class InstallationManagerTest extends TestCase
     {
         $libInstaller = $this->createInstallerMock();
         $bundleInstaller = $this->createInstallerMock();
-        $manager = new InstallationManager($this->loop);
+        $manager = new InstallationManager($this->loop, $this->io);
         $manager->addInstaller($libInstaller);
         $manager->addInstaller($bundleInstaller);
 
@@ -228,7 +228,7 @@ class InstallationManagerTest extends TestCase
     public function testUninstall()
     {
         $installer = $this->createInstallerMock();
-        $manager = new InstallationManager($this->loop);
+        $manager = new InstallationManager($this->loop, $this->io);
         $manager->addInstaller($installer);
 
         $package = $this->createPackageMock();
@@ -258,7 +258,7 @@ class InstallationManagerTest extends TestCase
         $installer = $this->getMockBuilder('Composer\Installer\LibraryInstaller')
             ->disableOriginalConstructor()
             ->getMock();
-        $manager = new InstallationManager($this->loop);
+        $manager = new InstallationManager($this->loop, $this->io);
         $manager->addInstaller($installer);
 
         $package = $this->createPackageMock();

+ 3 - 3
tests/Composer/Test/Installer/InstallerEventTest.php

@@ -23,10 +23,10 @@ class InstallerEventTest extends TestCase
         $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
         $policy = $this->getMockBuilder('Composer\DependencyResolver\PolicyInterface')->getMock();
         $repositorySet = $this->getMockBuilder('Composer\Repository\RepositorySet')->disableOriginalConstructor()->getMock();
-        $installedRepo = $this->getMockBuilder('Composer\Repository\CompositeRepository')->disableOriginalConstructor()->getMock();
+        $localRepo = $this->getMockBuilder('Composer\Repository\CompositeRepository')->disableOriginalConstructor()->getMock();
         $request = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock();
         $operations = array($this->getMockBuilder('Composer\DependencyResolver\Operation\OperationInterface')->getMock());
-        $event = new InstallerEvent('EVENT_NAME', $composer, $io, true, $policy, $repositorySet, $installedRepo, $request, $operations);
+        $event = new InstallerEvent('EVENT_NAME', $composer, $io, true, $policy, $repositorySet, $localRepo, $request, $operations);
 
         $this->assertSame('EVENT_NAME', $event->getName());
         $this->assertInstanceOf('Composer\Composer', $event->getComposer());
@@ -34,7 +34,7 @@ class InstallerEventTest extends TestCase
         $this->assertTrue($event->isDevMode());
         $this->assertInstanceOf('Composer\DependencyResolver\PolicyInterface', $event->getPolicy());
         $this->assertInstanceOf('Composer\Repository\RepositorySet', $event->getRepositorySet());
-        $this->assertInstanceOf('Composer\Repository\CompositeRepository', $event->getInstalledRepo());
+        $this->assertInstanceOf('Composer\Repository\RepositoryInterface', $event->getLocalRepo());
         $this->assertInstanceOf('Composer\DependencyResolver\Request', $event->getRequest());
         $this->assertCount(1, $event->getOperations());
     }

+ 2 - 1
tests/Composer/Test/Mock/FactoryMock.php

@@ -18,6 +18,7 @@ use Composer\Factory;
 use Composer\Repository\RepositoryManager;
 use Composer\Repository\WritableRepositoryInterface;
 use Composer\Installer;
+use Composer\EventDispatcher\EventDispatcher;
 use Composer\IO\IOInterface;
 use Composer\Test\TestCase;
 use Composer\Util\Loop;
@@ -40,7 +41,7 @@ class FactoryMock extends Factory
     {
     }
 
-    public function createInstallationManager(Loop $loop)
+    public function createInstallationManager(Loop $loop, IOInterface $io, EventDispatcher $dispatcher = null)
     {
         return new InstallationManagerMock();
     }

+ 6 - 4
tests/Composer/Test/Mock/InstallationManagerMock.php

@@ -35,11 +35,13 @@ class InstallationManagerMock extends InstallationManager
 
     }
 
-    public function execute(RepositoryInterface $repo, OperationInterface $operation)
+    public function execute(RepositoryInterface $repo, array $operations, $devMode = true, $runScripts = true)
     {
-        $method = $operation->getJobType();
-        // skipping download() step here for tests
-        $this->$method($repo, $operation);
+        foreach ($operations as $operation) {
+            $method = $operation->getJobType();
+            // skipping download() step here for tests
+            $this->$method($repo, $operation);
+        }
     }
 
     public function getInstallPath(PackageInterface $package)