Browse Source

Merge pull request #7625 from naderman/repository-set

Separate RepositorySet from Package Pool, load only metadata of packages directly required
Nils Adermann 6 years ago
parent
commit
5c491ecc9b
43 changed files with 827 additions and 508 deletions
  1. 5 5
      src/Composer/Command/BaseDependencyCommand.php
  2. 4 4
      src/Composer/Command/CreateProjectCommand.php
  3. 10 10
      src/Composer/Command/InitCommand.php
  4. 15 19
      src/Composer/Command/ShowCommand.php
  5. 3 8
      src/Composer/DependencyResolver/DefaultPolicy.php
  6. 18 114
      src/Composer/DependencyResolver/Pool.php
  7. 150 0
      src/Composer/DependencyResolver/PoolBuilder.php
  8. 2 2
      src/Composer/DependencyResolver/Problem.php
  9. 13 2
      src/Composer/DependencyResolver/Rule.php
  10. 10 3
      src/Composer/DependencyResolver/Solver.php
  11. 4 2
      src/Composer/DependencyResolver/SolverProblemsException.php
  12. 8 8
      src/Composer/EventDispatcher/EventDispatcher.php
  13. 39 55
      src/Composer/Installer.php
  14. 9 9
      src/Composer/Installer/InstallerEvent.php
  15. 4 4
      src/Composer/Installer/PackageEvent.php
  16. 2 2
      src/Composer/Package/Locker.php
  17. 5 4
      src/Composer/Package/Version/VersionSelector.php
  18. 1 1
      src/Composer/Plugin/PluginInterface.php
  19. 15 15
      src/Composer/Plugin/PluginManager.php
  20. 38 0
      src/Composer/Repository/AsyncRepositoryInterface.php
  21. 15 0
      src/Composer/Repository/BaseRepository.php
  22. 27 8
      src/Composer/Repository/ComposerRepository.php
  23. 25 0
      src/Composer/Repository/LockArrayRepository.php
  24. 10 0
      src/Composer/Repository/RepositoryInterface.php
  25. 179 0
      src/Composer/Repository/RepositorySet.php
  26. 75 43
      tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php
  27. 15 77
      tests/Composer/Test/DependencyResolver/PoolTest.php
  28. 2 1
      tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php
  29. 7 11
      tests/Composer/Test/DependencyResolver/RuleSetTest.php
  30. 9 12
      tests/Composer/Test/DependencyResolver/RuleTest.php
  31. 25 10
      tests/Composer/Test/DependencyResolver/SolverTest.php
  32. 3 3
      tests/Composer/Test/EventDispatcher/EventDispatcherTest.php
  33. 3 3
      tests/Composer/Test/Installer/InstallerEventTest.php
  34. 5 3
      tests/Composer/Test/InstallerTest.php
  35. 49 47
      tests/Composer/Test/Package/Version/VersionSelectorTest.php
  36. 1 1
      tests/Composer/Test/Plugin/Fixtures/plugin-v1/composer.json
  37. 1 1
      tests/Composer/Test/Plugin/Fixtures/plugin-v2/composer.json
  38. 1 1
      tests/Composer/Test/Plugin/Fixtures/plugin-v3/composer.json
  39. 1 1
      tests/Composer/Test/Plugin/Fixtures/plugin-v4/composer.json
  40. 1 1
      tests/Composer/Test/Plugin/Fixtures/plugin-v8/composer.json
  41. 1 1
      tests/Composer/Test/Plugin/Fixtures/plugin-v9/composer.json
  42. 11 11
      tests/Composer/Test/Plugin/PluginInstallerTest.php
  43. 6 6
      tests/Composer/Test/Repository/ComposerRepositoryTest.php

+ 5 - 5
src/Composer/Command/BaseDependencyCommand.php

@@ -12,7 +12,6 @@
 
 namespace Composer\Command;
 
-use Composer\DependencyResolver\Pool;
 use Composer\Package\Link;
 use Composer\Package\PackageInterface;
 use Composer\Repository\ArrayRepository;
@@ -21,6 +20,7 @@ use Composer\Repository\PlatformRepository;
 use Composer\Repository\RepositoryFactory;
 use Composer\Plugin\CommandEvent;
 use Composer\Plugin\PluginEvents;
+use Composer\Repository\RepositorySet;
 use Symfony\Component\Console\Formatter\OutputFormatterStyle;
 use Composer\Package\Version\VersionParser;
 use Symfony\Component\Console\Helper\Table;
@@ -71,15 +71,15 @@ class BaseDependencyCommand extends BaseCommand
         $commandEvent = new CommandEvent(PluginEvents::COMMAND, $this->getName(), $input, $output);
         $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
 
-        // Prepare repositories and set up a pool
+        // Prepare repositories and set up a repo set
         $platformOverrides = $composer->getConfig()->get('platform') ?: array();
         $repository = new CompositeRepository(array(
             new ArrayRepository(array($composer->getPackage())),
             $composer->getRepositoryManager()->getLocalRepository(),
             new PlatformRepository(array(), $platformOverrides),
         ));
-        $pool = new Pool();
-        $pool->addRepository($repository);
+        $repositorySet = new RepositorySet();
+        $repositorySet->addRepository($repository);
 
         // Parse package name and constraint
         list($needle, $textConstraint) = array_pad(
@@ -89,7 +89,7 @@ class BaseDependencyCommand extends BaseCommand
         );
 
         // Find packages that are or provide the requested package first
-        $packages = $pool->whatProvides(strtolower($needle));
+        $packages = $repositorySet->findPackages(strtolower($needle), null, false);
         if (empty($packages)) {
             throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle));
         }

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

@@ -20,7 +20,6 @@ use Composer\Installer\InstallationManager;
 use Composer\Installer\SuggestedPackagesReporter;
 use Composer\IO\IOInterface;
 use Composer\Package\BasePackage;
-use Composer\DependencyResolver\Pool;
 use Composer\DependencyResolver\Operation\InstallOperation;
 use Composer\Package\Version\VersionSelector;
 use Composer\Package\AliasPackage;
@@ -28,6 +27,7 @@ use Composer\Repository\RepositoryFactory;
 use Composer\Repository\CompositeRepository;
 use Composer\Repository\PlatformRepository;
 use Composer\Repository\InstalledFilesystemRepository;
+use Composer\Repository\RepositorySet;
 use Composer\Script\ScriptEvents;
 use Composer\Util\Silencer;
 use Symfony\Component\Console\Input\InputArgument;
@@ -290,8 +290,8 @@ EOT
             throw new \InvalidArgumentException('Invalid stability provided ('.$stability.'), must be one of: '.implode(', ', array_keys(BasePackage::$stabilities)));
         }
 
-        $pool = new Pool($stability);
-        $pool->addRepository($sourceRepo);
+        $repositorySet = new RepositorySet(array(), $stability);
+        $repositorySet->addRepository($sourceRepo);
 
         $phpVersion = null;
         $prettyPhpVersion = null;
@@ -305,7 +305,7 @@ EOT
         }
 
         // find the latest version if there are multiple
-        $versionSelector = new VersionSelector($pool);
+        $versionSelector = new VersionSelector($repositorySet);
         $package = $versionSelector->findBestCandidate($name, $packageVersion, $phpVersion, $stability);
 
         if (!$package) {

+ 10 - 10
src/Composer/Command/InitCommand.php

@@ -12,7 +12,6 @@
 
 namespace Composer\Command;
 
-use Composer\DependencyResolver\Pool;
 use Composer\Factory;
 use Composer\Json\JsonFile;
 use Composer\Package\BasePackage;
@@ -21,6 +20,7 @@ use Composer\Package\Version\VersionSelector;
 use Composer\Repository\CompositeRepository;
 use Composer\Repository\PlatformRepository;
 use Composer\Repository\RepositoryFactory;
+use Composer\Repository\RepositorySet;
 use Composer\Util\ProcessExecutor;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputOption;
@@ -40,8 +40,8 @@ class InitCommand extends BaseCommand
     /** @var array */
     private $gitConfig;
 
-    /** @var Pool[] */
-    private $pools;
+    /** @var RepositorySet[] */
+    private $repositorySets;
 
     /**
      * {@inheritdoc}
@@ -637,16 +637,16 @@ EOT
         return false !== filter_var($email, FILTER_VALIDATE_EMAIL);
     }
 
-    private function getPool(InputInterface $input, $minimumStability = null)
+    private function getRepositorySet(InputInterface $input, $minimumStability = null)
     {
         $key = $minimumStability ?: 'default';
 
-        if (!isset($this->pools[$key])) {
-            $this->pools[$key] = $pool = new Pool($minimumStability ?: $this->getMinimumStability($input));
-            $pool->addRepository($this->getRepos());
+        if (!isset($this->repositorySets[$key])) {
+            $this->repositorySets[$key] = $repositorySet = new RepositorySet(array(), $minimumStability ?: $this->getMinimumStability($input));
+            $repositorySet->addRepository($this->getRepos());
         }
 
-        return $this->pools[$key];
+        return $this->repositorySets[$key];
     }
 
     private function getMinimumStability(InputInterface $input)
@@ -681,8 +681,8 @@ EOT
      */
     private function findBestVersionAndNameForPackage(InputInterface $input, $name, $phpVersion, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null)
     {
-        // find the latest version allowed in this pool
-        $versionSelector = new VersionSelector($this->getPool($input, $minimumStability));
+        // find the latest version allowed in this repo set
+        $versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability));
         $package = $versionSelector->findBestCandidate($name, $requiredVersion, $phpVersion, $preferredStability);
 
         // retry without phpVersion if platform requirements are ignored in case nothing was found

+ 15 - 19
src/Composer/Command/ShowCommand.php

@@ -14,7 +14,6 @@ namespace Composer\Command;
 
 use Composer\Composer;
 use Composer\DependencyResolver\DefaultPolicy;
-use Composer\DependencyResolver\Pool;
 use Composer\Json\JsonFile;
 use Composer\Package\BasePackage;
 use Composer\Package\CompletePackageInterface;
@@ -29,6 +28,7 @@ use Composer\Repository\CompositeRepository;
 use Composer\Repository\PlatformRepository;
 use Composer\Repository\RepositoryFactory;
 use Composer\Repository\RepositoryInterface;
+use Composer\Repository\RepositorySet;
 use Composer\Semver\Constraint\ConstraintInterface;
 use Composer\Semver\Semver;
 use Composer\Spdx\SpdxLicenses;
@@ -52,8 +52,8 @@ class ShowCommand extends BaseCommand
     protected $versionParser;
     protected $colors;
 
-    /** @var Pool */
-    private $pool;
+    /** @var RepositorySet */
+    private $repositorySet;
 
     protected function configure()
     {
@@ -523,19 +523,13 @@ EOT
         $constraint = is_string($version) ? $this->versionParser->parseConstraints($version) : $version;
 
         $policy = new DefaultPolicy();
-        $pool = new Pool('dev');
-        $pool->addRepository($repos);
+        $repositorySet = new RepositorySet(array(), 'dev');
+        $repositorySet->addRepository($repos);
 
         $matchedPackage = null;
         $versions = array();
-        $matches = $pool->whatProvides($name, $constraint);
+        $matches = $repositorySet->findPackages($name, $constraint);
         foreach ($matches as $index => $package) {
-            // skip providers/replacers
-            if ($package->getName() !== $name) {
-                unset($matches[$index]);
-                continue;
-            }
-
             // select an exact match if it is in the installed repo and no specific version was required
             if (null === $version && $installedRepo->hasPackage($package)) {
                 $matchedPackage = $package;
@@ -545,6 +539,8 @@ EOT
             $matches[$index] = $package->getId();
         }
 
+        $pool = $repositorySet->createPool();
+
         // select preferred package according to policy rules
         if (!$matchedPackage && $matches && $preferred = $policy->selectPreferredPackages($pool, array(), $matches)) {
             $matchedPackage = $pool->literalToPackage($preferred[0]);
@@ -961,9 +957,9 @@ EOT
      */
     private function findLatestPackage(PackageInterface $package, Composer $composer, $phpVersion, $minorOnly = false)
     {
-        // find the latest version allowed in this pool
+        // find the latest version allowed in this repo set
         $name = $package->getName();
-        $versionSelector = new VersionSelector($this->getPool($composer));
+        $versionSelector = new VersionSelector($this->getRepositorySet($composer));
         $stability = $composer->getPackage()->getMinimumStability();
         $flags = $composer->getPackage()->getStabilityFlags();
         if (isset($flags[$name])) {
@@ -987,13 +983,13 @@ EOT
         return $versionSelector->findBestCandidate($name, $targetVersion, $phpVersion, $bestStability);
     }
 
-    private function getPool(Composer $composer)
+    private function getRepositorySet(Composer $composer)
     {
-        if (!$this->pool) {
-            $this->pool = new Pool($composer->getPackage()->getMinimumStability(), $composer->getPackage()->getStabilityFlags());
-            $this->pool->addRepository(new CompositeRepository($composer->getRepositoryManager()->getRepositories()));
+        if (!$this->repositorySet) {
+            $this->repositorySet = new RepositorySet(array(), $composer->getPackage()->getMinimumStability(), $composer->getPackage()->getStabilityFlags());
+            $this->repositorySet->addRepository(new CompositeRepository($composer->getRepositoryManager()->getRepositories()));
         }
 
-        return $this->pool;
+        return $this->repositorySet;
     }
 }

+ 3 - 8
src/Composer/DependencyResolver/DefaultPolicy.php

@@ -57,11 +57,6 @@ class DefaultPolicy implements PolicyInterface
         return $packages;
     }
 
-    public function getPriority(Pool $pool, PackageInterface $package)
-    {
-        return $pool->getPriority($package->getRepository());
-    }
-
     public function selectPreferredPackages(Pool $pool, array $installedMap, array $literals, $requiredPackage = null)
     {
         $packages = $this->groupLiteralsByNamePreferInstalled($pool, $installedMap, $literals);
@@ -168,7 +163,7 @@ class DefaultPolicy implements PolicyInterface
             return 1;
         }
 
-        return ($this->getPriority($pool, $a) > $this->getPriority($pool, $b)) ? -1 : 1;
+        return ($pool->getPriority($a->id) > $pool->getPriority($b->id)) ? -1 : 1;
     }
 
     /**
@@ -236,10 +231,10 @@ class DefaultPolicy implements PolicyInterface
             }
 
             if (null === $priority) {
-                $priority = $this->getPriority($pool, $package);
+                $priority = $pool->getPriority($package->id);
             }
 
-            if ($this->getPriority($pool, $package) != $priority) {
+            if ($pool->getPriority($package->id) != $priority) {
                 break;
             }
 

+ 18 - 114
src/Composer/DependencyResolver/Pool.php

@@ -15,6 +15,7 @@ namespace Composer\DependencyResolver;
 use Composer\Package\BasePackage;
 use Composer\Package\AliasPackage;
 use Composer\Package\Version\VersionParser;
+use Composer\Repository\RepositorySet;
 use Composer\Semver\Constraint\ConstraintInterface;
 use Composer\Semver\Constraint\Constraint;
 use Composer\Semver\Constraint\EmptyConstraint;
@@ -26,7 +27,7 @@ use Composer\Repository\PlatformRepository;
 use Composer\Package\PackageInterface;
 
 /**
- * A package pool contains repositories that provide packages.
+ * A package pool contains all packages for dependency resolution
  *
  * @author Nils Adermann <naderman@naderman.de>
  * @author Jordi Boggiano <j.boggiano@seld.be>
@@ -40,35 +41,20 @@ class Pool implements \Countable
     const MATCH_REPLACE = 3;
     const MATCH_FILTERED = 4;
 
-    protected $repositories = array();
     protected $providerRepos = array();
     protected $packages = array();
     protected $packageByName = array();
     protected $packageByExactName = array();
-    protected $acceptableStabilities;
-    protected $stabilityFlags;
+    protected $priorities = array();
     protected $versionParser;
     protected $providerCache = array();
     protected $filterRequires;
     protected $whitelist = null;
-    protected $id = 1;
 
-    public function __construct($minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array())
+    public function __construct(array $filterRequires = array())
     {
-        $this->versionParser = new VersionParser;
-        $this->acceptableStabilities = array();
-        foreach (BasePackage::$stabilities as $stability => $value) {
-            if ($value <= BasePackage::$stabilities[$minimumStability]) {
-                $this->acceptableStabilities[$stability] = $value;
-            }
-        }
-        $this->stabilityFlags = $stabilityFlags;
         $this->filterRequires = $filterRequires;
-        foreach ($filterRequires as $name => $constraint) {
-            if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) {
-                unset($this->filterRequires[$name]);
-            }
-        }
+        $this->versionParser = new VersionParser;
     }
 
     public function setWhitelist($whitelist)
@@ -77,76 +63,24 @@ class Pool implements \Countable
         $this->providerCache = array();
     }
 
-    /**
-     * Adds a repository and its packages to this package pool
-     *
-     * @param RepositoryInterface $repo        A package repository
-     * @param array               $rootAliases
-     */
-    public function addRepository(RepositoryInterface $repo, $rootAliases = array())
+    public function setPackages(array $packages, array $priorities = array())
     {
-        if ($repo instanceof CompositeRepository) {
-            $repos = $repo->getRepositories();
-        } else {
-            $repos = array($repo);
-        }
+        $this->priorities = $priorities;
+        $this->packages = $packages;
 
-        foreach ($repos as $repo) {
-            $this->repositories[] = $repo;
-
-            $exempt = $repo instanceof PlatformRepository || $repo instanceof InstalledRepositoryInterface;
-
-            if ($repo instanceof ComposerRepository && $repo->hasProviders()) {
-                $this->providerRepos[] = $repo;
-                $repo->setRootAliases($rootAliases);
-                $repo->resetPackageIds();
-            } else {
-                foreach ($repo->getPackages() as $package) {
-                    $names = $package->getNames();
-                    $stability = $package->getStability();
-                    if ($exempt || $this->isPackageAcceptable($names, $stability)) {
-                        $package->setId($this->id++);
-                        $this->packages[] = $package;
-                        $this->packageByExactName[$package->getName()][$package->id] = $package;
-
-                        foreach ($names as $provided) {
-                            $this->packageByName[$provided][] = $package;
-                        }
-
-                        // handle root package aliases
-                        $name = $package->getName();
-                        if (isset($rootAliases[$name][$package->getVersion()])) {
-                            $alias = $rootAliases[$name][$package->getVersion()];
-                            if ($package instanceof AliasPackage) {
-                                $package = $package->getAliasOf();
-                            }
-                            $aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']);
-                            $aliasPackage->setRootPackageAlias(true);
-                            $aliasPackage->setId($this->id++);
-
-                            $package->getRepository()->addPackage($aliasPackage);
-                            $this->packages[] = $aliasPackage;
-                            $this->packageByExactName[$aliasPackage->getName()][$aliasPackage->id] = $aliasPackage;
-
-                            foreach ($aliasPackage->getNames() as $name) {
-                                $this->packageByName[$name][] = $aliasPackage;
-                            }
-                        }
-                    }
-                }
+        foreach ($this->packages as $package) {
+            $names = $package->getNames();
+            $this->packageByExactName[$package->getName()][$package->id] = $package;
+
+            foreach ($names as $provided) {
+                $this->packageByName[$provided][] = $package;
             }
         }
     }
 
-    public function getPriority(RepositoryInterface $repo)
+    public function getPriority($id)
     {
-        $priority = array_search($repo, $this->repositories, true);
-
-        if (false === $priority) {
-            throw new \RuntimeException("Could not determine repository priority. The repository was not registered in the pool.");
-        }
-
-        return -$priority;
+        return $this->priorities[$id - 1];
     }
 
     /**
@@ -200,25 +134,12 @@ class Pool implements \Countable
     {
         $candidates = array();
 
-        foreach ($this->providerRepos as $repo) {
-            foreach ($repo->whatProvides($this, $name, $bypassFilters) as $candidate) {
-                $candidates[] = $candidate;
-                if ($candidate->id < 1) {
-                    $candidate->setId($this->id++);
-                    $this->packages[$this->id - 2] = $candidate;
-                }
-            }
-        }
-
         if ($mustMatchName) {
-            $candidates = array_filter($candidates, function ($candidate) use ($name) {
-                return $candidate->getName() == $name;
-            });
             if (isset($this->packageByExactName[$name])) {
-                $candidates = array_merge($candidates, $this->packageByExactName[$name]);
+                $candidates = $this->packageByExactName[$name];
             }
         } elseif (isset($this->packageByName[$name])) {
-            $candidates = array_merge($candidates, $this->packageByName[$name]);
+            $candidates = $this->packageByName[$name];
         }
 
         $matches = $provideMatches = array();
@@ -296,23 +217,6 @@ class Pool implements \Countable
         return $prefix.' '.$package->getPrettyString();
     }
 
-    public function isPackageAcceptable($name, $stability)
-    {
-        foreach ((array) $name as $n) {
-            // allow if package matches the global stability requirement and has no exception
-            if (!isset($this->stabilityFlags[$n]) && isset($this->acceptableStabilities[$stability])) {
-                return true;
-            }
-
-            // allow if package matches the package-specific stability flag
-            if (isset($this->stabilityFlags[$n]) && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$n]) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
     /**
      * Checks if the package matches the given constraint directly or through
      * provided or replaced packages

+ 150 - 0
src/Composer/DependencyResolver/PoolBuilder.php

@@ -0,0 +1,150 @@
+<?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\DependencyResolver;
+
+use Composer\Package\AliasPackage;
+use Composer\Package\BasePackage;
+use Composer\Package\PackageInterface;
+use Composer\Repository\AsyncRepositoryInterface;
+use Composer\Repository\ComposerRepository;
+use Composer\Repository\InstalledRepositoryInterface;
+use Composer\Repository\LockArrayRepository;
+use Composer\Repository\PlatformRepository;
+
+/**
+ * @author Nils Adermann <naderman@naderman.de>
+ */
+class PoolBuilder
+{
+    private $isPackageAcceptableCallable;
+    private $filterRequires;
+    private $rootAliases;
+
+    private $loadedNames = array();
+
+    private $id = 1;
+    private $packages = array();
+    private $priorities = array();
+
+    public function __construct($isPackageAcceptableCallable, array $filterRequires = array())
+    {
+        $this->isPackageAcceptableCallable = $isPackageAcceptableCallable;
+        $this->filterRequires = $filterRequires;
+    }
+
+    public function buildPool(array $repositories, array $rootAliases, Request $request)
+    {
+        $this->pool = new Pool($this->filterRequires);
+        $this->rootAliases = $rootAliases;
+
+        // TODO do we really want the request here? kind of want a root requirements thingy instead
+        $loadNames = array();
+        foreach ($request->getJobs() as $job) {
+            switch ($job['cmd']) {
+                case 'install':
+                    $loadNames[$job['packageName']] = true;
+                    break;
+            }
+        }
+
+        foreach ($repositories as $repository) {
+            if ($repository instanceof ComposerRepository && $repository->hasProviders()) {
+                $this->providerRepos[] = $repository;
+                $repository->setRootAliases($this->rootAliases);
+                $repository->resetPackageIds();
+            }
+        }
+
+        while (!empty($loadNames)) {
+            $loadIds = array();
+            foreach ($repositories as $key => $repository) {
+                if ($repository instanceof AsyncRepositoryInterface) {
+                    $loadIds[$key] = $repository->requestPackages($loadNames);
+                }
+            }
+
+            foreach ($loadNames as $name => $void) {
+                $this->loadedNames[$name] = true;
+            }
+
+            $newLoadNames = array();
+            foreach ($repositories as $key => $repository) {
+                if ($repository instanceof PlatformRepository || $repository instanceof InstalledRepositoryInterface) {
+                    continue;
+                }
+
+                if ($repository instanceof AsyncRepositoryInterface) {
+                    // TODO ispackageacceptablecallable in here?
+                    $packages = $repository->returnPackages($loadIds[$key]);
+                } else {
+                    // TODO should we really pass the callable into here?
+                    $packages = $repository->loadPackages($loadNames, $this->isPackageAcceptableCallable);
+                }
+
+                foreach ($packages as $package) {
+                    if (call_user_func($this->isPackageAcceptableCallable, $package->getNames(), $package->getStability())) {
+                        $newLoadNames += $this->loadPackage($package, $key);
+                    }
+                }
+            }
+
+            $loadNames = $newLoadNames;
+        }
+
+        foreach ($repositories as $key => $repository) {
+            if ($repository instanceof PlatformRepository ||
+                $repository instanceof InstalledRepositoryInterface) {
+                foreach ($repository->getPackages() as $package) {
+                    $this->loadPackage($package, $key);
+                }
+            }
+        }
+
+        $this->pool->setPackages($this->packages, $this->priorities);
+
+        return $this->pool;
+    }
+
+    private function loadPackage(PackageInterface $package, $repoIndex)
+    {
+        $package->setId($this->id++);
+        $this->packages[] = $package;
+        $this->priorities[$this->id - 2] = -$repoIndex;
+
+        // handle root package aliases
+        $name = $package->getName();
+        if (isset($this->rootAliases[$name][$package->getVersion()])) {
+            $alias = $this->rootAliases[$name][$package->getVersion()];
+            if ($package instanceof AliasPackage) {
+                $package = $package->getAliasOf();
+            }
+            $aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']);
+            $aliasPackage->setRootPackageAlias(true);
+            $aliasPackage->setId($this->id++);
+
+            $package->getRepository()->addPackage($aliasPackage); // TODO do we need this?
+            $this->packages[] = $aliasPackage;
+        }
+
+        $loadNames = array();
+        foreach ($package->getRequires() as $link) {
+            $require = $link->getTarget();
+            if (!isset($this->loadedNames[$require])) {
+                $loadNames[$require] = true;
+            }
+        }
+
+        return $loadNames;
+    }
+}
+

+ 2 - 2
src/Composer/DependencyResolver/Problem.php

@@ -71,7 +71,7 @@ class Problem
      * @param  array  $installedMap A map of all installed packages
      * @return string
      */
-    public function getPrettyString(array $installedMap = array())
+    public function getPrettyString(array $installedMap = array(), array $learnedPool = array())
     {
         $reasons = call_user_func_array('array_merge', array_reverse($this->reasons));
 
@@ -168,7 +168,7 @@ class Problem
                 $messages[] = $this->jobToText($job);
             } elseif ($rule) {
                 if ($rule instanceof Rule) {
-                    $messages[] = $rule->getPrettyString($this->pool, $installedMap);
+                    $messages[] = $rule->getPrettyString($this->pool, $installedMap, $learnedPool);
                 }
             }
         }

+ 13 - 2
src/Composer/DependencyResolver/Rule.php

@@ -126,7 +126,7 @@ abstract class Rule
 
     abstract public function isAssertion();
 
-    public function getPrettyString(Pool $pool, array $installedMap = array())
+    public function getPrettyString(Pool $pool, array $installedMap = array(), array $learnedPool = array())
     {
         $literals = $this->getLiterals();
 
@@ -230,7 +230,18 @@ abstract class Rule
             case self::RULE_PACKAGE_IMPLICIT_OBSOLETES:
                 return $ruleText;
             case self::RULE_LEARNED:
-                return 'Conclusion: '.$ruleText;
+                // TODO not sure this is a good idea, most of these rules should be listed in the problem anyway
+                $learnedString = '(learned rule, ';
+                if (isset($learnedPool[$this->reasonData])) {
+                    foreach ($learnedPool[$this->reasonData] as $learnedRule) {
+                        $learnedString .= $learnedRule->getPrettyString($pool, $installedMap, $learnedPool);
+                    }
+                } else {
+                    $learnedString .= 'reasoning unavailable';
+                }
+                $learnedString .= ')';
+
+                return 'Conclusion: '.$ruleText.' '.$learnedString;
             case self::RULE_PACKAGE_ALIAS:
                 return $ruleText;
             default:

+ 10 - 3
src/Composer/DependencyResolver/Solver.php

@@ -15,6 +15,7 @@ namespace Composer\DependencyResolver;
 use Composer\IO\IOInterface;
 use Composer\Repository\RepositoryInterface;
 use Composer\Repository\PlatformRepository;
+use Composer\Repository\RepositorySet;
 
 /**
  * @author Nils Adermann <naderman@naderman.de>
@@ -27,7 +28,7 @@ class Solver
     /** @var PolicyInterface */
     protected $policy;
     /** @var Pool */
-    protected $pool;
+    protected $pool = null;
     /** @var RepositoryInterface */
     protected $installed;
     /** @var RuleSet */
@@ -72,7 +73,6 @@ class Solver
         $this->policy = $policy;
         $this->pool = $pool;
         $this->installed = $installed;
-        $this->ruleSetGenerator = new RuleSetGenerator($policy, $pool);
     }
 
     /**
@@ -83,6 +83,11 @@ class Solver
         return count($this->rules);
     }
 
+    public function getPool()
+    {
+        return $this->pool;
+    }
+
     // aka solver_makeruledecisions
 
     private function makeAssertionRuleDecisions()
@@ -211,6 +216,8 @@ class Solver
         $this->jobs = $request->getJobs();
 
         $this->setupInstalledMap();
+
+        $this->ruleSetGenerator = new RuleSetGenerator($this->policy, $this->pool);
         $this->rules = $this->ruleSetGenerator->getRulesFor($this->jobs, $this->installedMap, $ignorePlatformReqs);
         $this->checkForRootRequireProblems($ignorePlatformReqs);
         $this->decisions = new Decisions($this->pool);
@@ -237,7 +244,7 @@ class Solver
         }
 
         if ($this->problems) {
-            throw new SolverProblemsException($this->problems, $this->installedMap);
+            throw new SolverProblemsException($this->problems, $this->installedMap, $this->learnedPool);
         }
 
         $transaction = new Transaction($this->policy, $this->pool, $this->installedMap, $this->decisions);

+ 4 - 2
src/Composer/DependencyResolver/SolverProblemsException.php

@@ -21,11 +21,13 @@ class SolverProblemsException extends \RuntimeException
 {
     protected $problems;
     protected $installedMap;
+    protected $learnedPool;
 
-    public function __construct(array $problems, array $installedMap)
+    public function __construct(array $problems, array $installedMap, array $learnedPool)
     {
         $this->problems = $problems;
         $this->installedMap = $installedMap;
+        $this->learnedPool = $learnedPool;
 
         parent::__construct($this->createMessage(), 2);
     }
@@ -35,7 +37,7 @@ class SolverProblemsException extends \RuntimeException
         $text = "\n";
         $hasExtensionProblems = false;
         foreach ($this->problems as $i => $problem) {
-            $text .= "  Problem ".($i + 1).$problem->getPrettyString($this->installedMap)."\n";
+            $text .= "  Problem ".($i + 1).$problem->getPrettyString($this->installedMap, $this->learnedPool)."\n";
 
             if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) {
                 $hasExtensionProblems = true;

+ 8 - 8
src/Composer/EventDispatcher/EventDispatcher.php

@@ -13,13 +13,13 @@
 namespace Composer\EventDispatcher;
 
 use Composer\DependencyResolver\PolicyInterface;
-use Composer\DependencyResolver\Pool;
 use Composer\DependencyResolver\Request;
 use Composer\Installer\InstallerEvent;
 use Composer\IO\IOInterface;
 use Composer\Composer;
 use Composer\DependencyResolver\Operation\OperationInterface;
 use Composer\Repository\CompositeRepository;
+use Composer\Repository\RepositorySet;
 use Composer\Script;
 use Composer\Installer\PackageEvent;
 use Composer\Installer\BinaryInstaller;
@@ -102,7 +102,7 @@ class EventDispatcher
      * @param string              $eventName     The constant in PackageEvents
      * @param bool                $devMode       Whether or not we are in dev mode
      * @param PolicyInterface     $policy        The policy
-     * @param Pool                $pool          The pool
+     * @param RepositorySet       $repositorySet The repository set
      * @param CompositeRepository $installedRepo The installed repository
      * @param Request             $request       The request
      * @param array               $operations    The list of operations
@@ -111,9 +111,9 @@ class EventDispatcher
      * @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, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation)
+    public function dispatchPackageEvent($eventName, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation)
     {
-        return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $policy, $pool, $installedRepo, $request, $operations, $operation));
+        return $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $policy, $repositorySet, $installedRepo, $request, $operations, $operation));
     }
 
     /**
@@ -122,7 +122,7 @@ class EventDispatcher
      * @param string              $eventName     The constant in InstallerEvents
      * @param bool                $devMode       Whether or not we are in dev mode
      * @param PolicyInterface     $policy        The policy
-     * @param Pool                $pool          The pool
+     * @param RepositorySet       $repositorySet The repository set
      * @param CompositeRepository $installedRepo The installed repository
      * @param Request             $request       The request
      * @param array               $operations    The list of operations
@@ -130,9 +130,9 @@ class EventDispatcher
      * @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, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array())
+    public function dispatchInstallerEvent($eventName, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, CompositeRepository $installedRepo, Request $request, array $operations = array())
     {
-        return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $devMode, $policy, $pool, $installedRepo, $request, $operations));
+        return $this->doDispatch(new InstallerEvent($eventName, $this->composer, $this->io, $devMode, $policy, $repositorySet, $installedRepo, $request, $operations));
     }
 
     /**
@@ -340,7 +340,7 @@ class EventDispatcher
                 $event->getIO(),
                 $event->isDevMode(),
                 $event->getPolicy(),
-                $event->getPool(),
+                $event->getRepositorySet(),
                 $event->getInstalledRepo(),
                 $event->getRequest(),
                 $event->getOperations(),

+ 39 - 55
src/Composer/Installer.php

@@ -37,6 +37,7 @@ use Composer\Package\CompletePackage;
 use Composer\Package\Link;
 use Composer\Package\Loader\ArrayLoader;
 use Composer\Package\Dumper\ArrayDumper;
+use Composer\Repository\RepositorySet;
 use Composer\Semver\Constraint\Constraint;
 use Composer\Package\Locker;
 use Composer\Package\PackageInterface;
@@ -126,7 +127,7 @@ class Installer
      * @var array|null
      */
     protected $updateWhitelist = null;
-    protected $whitelistDependencies = false; // TODO 2.0 rename to whitelistTransitiveDependencies
+    protected $whitelistTransitiveDependencies = false;
     protected $whitelistAllDependencies = false;
 
     /**
@@ -368,21 +369,21 @@ class Installer
 
         $this->io->writeError('<info>Loading composer repositories with package information</info>');
 
-        // creating repository pool
+        // creating repository set
         $policy = $this->createPolicy();
-        $pool = $this->createPool($this->update ? null : $lockedRepository);
-        $pool->addRepository($installedRepo, $aliases);
+        $repositorySet = $this->createRepositorySet($aliases, $this->update ? null : $lockedRepository);
+        $repositorySet->addRepository($installedRepo);
         if ($this->update) {
             $repositories = $this->repositoryManager->getRepositories();
             foreach ($repositories as $repository) {
-                $pool->addRepository($repository, $aliases);
+                $repositorySet->addRepository($repository);
             }
         }
         // Add the locked repository after the others in case we are doing a
         // partial update so missing packages can be found there still.
         // For installs from lock it's the only one added so it is first
         if ($lockedRepository) {
-            $pool->addRepository($lockedRepository, $aliases);
+            $repositorySet->addRepository($lockedRepository);
         }
 
         // creating requirements request
@@ -393,7 +394,7 @@ class Installer
             $removedUnstablePackages = array();
             foreach ($localRepo->getPackages() as $package) {
                 if (
-                    !$pool->isPackageAcceptable($package->getNames(), $package->getStability())
+                    !$repositorySet->isPackageAcceptable($package->getNames(), $package->getStability())
                     && $this->installationManager->isPackageInstalled($localRepo, $package)
                 ) {
                     $removedUnstablePackages[$package->getName()] = true;
@@ -464,11 +465,14 @@ class Installer
             }
         }
 
+        $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $this->devMode, $policy, $repositorySet, $installedRepo, $request);
+
+        $pool = $repositorySet->createPool($request);
+
         // force dev packages to have the latest links if we update or install from a (potentially new) lock
         $this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, 'force-links');
 
         // solve dependencies
-        $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, $this->devMode, $policy, $pool, $installedRepo, $request);
         $solver = new Solver($policy, $pool, $installedRepo, $this->io);
         try {
             $operations = $solver->solve($request, $this->ignorePlatformReqs);
@@ -485,9 +489,8 @@ class Installer
         // force dev packages to be updated if we update or install from a (potentially new) lock
         $operations = $this->processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, 'force-updates', $operations);
 
-        $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $this->devMode, $policy, $pool, $installedRepo, $request, $operations);
+        $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, $this->devMode, $policy, $repositorySet, $installedRepo, $request, $operations);
 
-        $this->io->writeError("Analyzed ".count($pool)." packages to resolve dependencies", true, IOInterface::VERBOSE);
         $this->io->writeError("Analyzed ".$solver->getRuleSetSize()." rules to resolve dependencies", true, IOInterface::VERBOSE);
 
         // execute operations
@@ -581,7 +584,7 @@ class Installer
 
             $event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($jobType);
             if (defined($event) && $this->runScripts) {
-                $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $pool, $installedRepo, $request, $operations, $operation);
+                $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $repositorySet, $installedRepo, $request, $operations, $operation);
             }
 
             // output non-alias ops when not executing operations (i.e. dry run), output alias ops in debug verbosity
@@ -612,7 +615,7 @@ class Installer
 
             $event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($jobType);
             if (defined($event) && $this->runScripts) {
-                $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $pool, $installedRepo, $request, $operations, $operation);
+                $this->eventDispatcher->dispatchPackageEvent(constant($event), $this->devMode, $policy, $repositorySet, $installedRepo, $request, $operations, $operation);
             }
 
             if ($this->executeOperations || $this->writeLock) {
@@ -685,9 +688,9 @@ class Installer
         unset($tempLocalRepo, $loader, $dumper);
 
         $policy = $this->createPolicy();
-        $pool = $this->createPool();
+        $repositorySet = $this->createRepositorySet($aliases);
         $installedRepo = $this->createInstalledRepo($localRepo, $platformRepo);
-        $pool->addRepository($installedRepo, $aliases);
+        $repositorySet->addRepository($installedRepo);
 
         // creating requirements request without dev requirements
         $request = $this->createRequest($this->package, $platformRepo);
@@ -697,10 +700,10 @@ class Installer
         }
 
         // solve deps to see which get removed
-        $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request);
-        $solver = new Solver($policy, $pool, $installedRepo, $this->io);
+        $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, false, $policy, $repositorySet, $installedRepo, $request);
+        $solver = new Solver($policy, $repositorySet->createPool($request), $installedRepo, $this->io);
         $ops = $solver->solve($request, $this->ignorePlatformReqs);
-        $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, false, $policy, $pool, $installedRepo, $request, $ops);
+        $this->eventDispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, false, $policy, $repositorySet, $installedRepo, $request, $ops);
 
         $devPackages = array();
         foreach ($ops as $op) {
@@ -845,10 +848,11 @@ class Installer
     }
 
     /**
-     * @param  RepositoryInterface|null $lockedRepository
-     * @return Pool
+     * @param array $rootAliases
+     * @param RepositoryInterface|null $lockedRepository
+     * @return RepositorySet
      */
-    private function createPool(RepositoryInterface $lockedRepository = null)
+    private function createRepositorySet(array $rootAliases = array(), $lockedRepository = null)
     {
         if ($this->update) {
             $minimumStability = $this->package->getMinimumStability();
@@ -880,7 +884,7 @@ class Installer
             }
         }
 
-        return new Pool($minimumStability, $stabilityFlags, $rootConstraints);
+        return new RepositorySet($rootAliases, $minimumStability, $stabilityFlags, $rootConstraints);
     }
 
     /**
@@ -955,7 +959,7 @@ class Installer
      * @param  array|null                  $operations
      * @return array
      */
-    private function processDevPackages($localRepo, $pool, $policy, $repositories, $installedRepo, $lockedRepository, $task, array $operations = null)
+    private function processDevPackages($localRepo, Pool $pool, $policy, $repositories, $installedRepo, $lockedRepository, $task, array $operations = null)
     {
         if ($task === 'force-updates' && null === $operations) {
             throw new \InvalidArgumentException('Missing operations argument');
@@ -1010,7 +1014,7 @@ class Installer
                 }
 
                 // find similar packages (name/version) in all repositories
-                $matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion()));
+                $matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion()), true);
                 foreach ($matches as $index => $match) {
                     // skip local packages
                     if (!in_array($match->getRepository(), $repositories, true)) {
@@ -1018,12 +1022,6 @@ class Installer
                         continue;
                     }
 
-                    // skip providers/replacers
-                    if ($match->getName() !== $package->getName()) {
-                        unset($matches[$index]);
-                        continue;
-                    }
-
                     $matches[$index] = $match->getId();
                 }
 
@@ -1130,12 +1128,12 @@ class Installer
     }
 
     /**
-     * @param Pool                        $pool
+     * @param Pool               $pool
      * @param PolicyInterface             $policy
      * @param WritableRepositoryInterface $localRepo
      * @param array                       $repositories
      */
-    private function processPackageUrls($pool, $policy, $localRepo, $repositories)
+    private function processPackageUrls(Pool $pool, $policy, $localRepo, $repositories)
     {
         if (!$this->update) {
             return;
@@ -1144,8 +1142,8 @@ class Installer
         $rootRefs = $this->package->getReferences();
 
         foreach ($localRepo->getCanonicalPackages() as $package) {
-            // find similar packages (name/version) in all repositories
-            $matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion()));
+            // find similar packages (name/version) in pool
+            $matches = $pool->whatProvides($package->getName(), new Constraint('=', $package->getVersion()), true);
             foreach ($matches as $index => $match) {
                 // skip local packages
                 if (!in_array($match->getRepository(), $repositories, true)) {
@@ -1153,12 +1151,6 @@ class Installer
                     continue;
                 }
 
-                // skip providers/replacers
-                if ($match->getName() !== $package->getName()) {
-                    unset($matches[$index]);
-                    continue;
-                }
-
                 $matches[$index] = $match->getId();
             }
 
@@ -1325,8 +1317,8 @@ class Installer
             }
         }
 
-        $pool = new Pool('dev');
-        $pool->addRepository($localOrLockRepo);
+        $repositorySet = new RepositorySet(array(), 'dev');
+        $repositorySet->addRepository($localOrLockRepo);
 
         $seen = array();
 
@@ -1335,7 +1327,7 @@ class Installer
         foreach ($this->updateWhitelist as $packageName => $void) {
             $packageQueue = new \SplQueue;
 
-            $depPackages = $pool->whatProvides($packageName);
+            $depPackages = $repositorySet->findPackages($packageName, null, false);
 
             $nameMatchesRequiredPackage = in_array($packageName, $requiredPackageNames, true);
 
@@ -1360,21 +1352,21 @@ class Installer
 
             while (!$packageQueue->isEmpty()) {
                 $package = $packageQueue->dequeue();
-                if (isset($seen[$package->getId()])) {
+                if (isset($seen[spl_object_hash($package)])) {
                     continue;
                 }
 
-                $seen[$package->getId()] = true;
+                $seen[spl_object_hash($package)] = true;
                 $this->updateWhitelist[$package->getName()] = true;
 
-                if (!$this->whitelistDependencies && !$this->whitelistAllDependencies) {
+                if (!$this->whitelistTransitiveDependencies && !$this->whitelistAllDependencies) {
                     continue;
                 }
 
                 $requires = $package->getRequires();
 
                 foreach ($requires as $require) {
-                    $requirePackages = $pool->whatProvides($require->getTarget());
+                    $requirePackages = $repositorySet->findPackages($require->getTarget(), null, false);
 
                     foreach ($requirePackages as $requirePackage) {
                         if (isset($this->updateWhitelist[$requirePackage->getName()])) {
@@ -1667,14 +1659,6 @@ class Installer
         return $this;
     }
 
-    /**
-     * @deprecated use setWhitelistTransitiveDependencies instead
-     */
-    public function setWhitelistDependencies($updateDependencies = true)
-    {
-        return $this->setWhitelistTransitiveDependencies($updateDependencies);
-    }
-
     /**
      * Should dependencies of whitelisted packages (but not direct dependencies) be updated?
      *
@@ -1686,7 +1670,7 @@ class Installer
      */
     public function setWhitelistTransitiveDependencies($updateTransitiveDependencies = true)
     {
-        $this->whitelistDependencies = (bool) $updateTransitiveDependencies;
+        $this->whitelistTransitiveDependencies = (bool) $updateTransitiveDependencies;
 
         return $this;
     }

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

@@ -15,11 +15,11 @@ namespace Composer\Installer;
 use Composer\Composer;
 use Composer\DependencyResolver\PolicyInterface;
 use Composer\DependencyResolver\Operation\OperationInterface;
-use Composer\DependencyResolver\Pool;
 use Composer\DependencyResolver\Request;
 use Composer\EventDispatcher\Event;
 use Composer\IO\IOInterface;
 use Composer\Repository\CompositeRepository;
+use Composer\Repository\RepositorySet;
 
 /**
  * An event for all installer.
@@ -49,9 +49,9 @@ class InstallerEvent extends Event
     private $policy;
 
     /**
-     * @var Pool
+     * @var RepositorySet
      */
-    private $pool;
+    private $repositorySet;
 
     /**
      * @var CompositeRepository
@@ -76,12 +76,12 @@ class InstallerEvent extends Event
      * @param IOInterface          $io
      * @param bool                 $devMode
      * @param PolicyInterface      $policy
-     * @param Pool                 $pool
+     * @param RepositorySet        $repositorySet
      * @param CompositeRepository  $installedRepo
      * @param Request              $request
      * @param OperationInterface[] $operations
      */
-    public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations = array())
+    public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, CompositeRepository $installedRepo, Request $request, array $operations = array())
     {
         parent::__construct($eventName);
 
@@ -89,7 +89,7 @@ class InstallerEvent extends Event
         $this->io = $io;
         $this->devMode = $devMode;
         $this->policy = $policy;
-        $this->pool = $pool;
+        $this->repositorySet = $repositorySet;
         $this->installedRepo = $installedRepo;
         $this->request = $request;
         $this->operations = $operations;
@@ -128,11 +128,11 @@ class InstallerEvent extends Event
     }
 
     /**
-     * @return Pool
+     * @return RepositorySet
      */
-    public function getPool()
+    public function getRepositorySet()
     {
-        return $this->pool;
+        return $this->repositorySet;
     }
 
     /**

+ 4 - 4
src/Composer/Installer/PackageEvent.php

@@ -16,9 +16,9 @@ use Composer\Composer;
 use Composer\IO\IOInterface;
 use Composer\DependencyResolver\Operation\OperationInterface;
 use Composer\DependencyResolver\PolicyInterface;
-use Composer\DependencyResolver\Pool;
 use Composer\DependencyResolver\Request;
 use Composer\Repository\CompositeRepository;
+use Composer\Repository\RepositorySet;
 
 /**
  * The Package Event.
@@ -40,15 +40,15 @@ class PackageEvent extends InstallerEvent
      * @param IOInterface          $io
      * @param bool                 $devMode
      * @param PolicyInterface      $policy
-     * @param Pool                 $pool
+     * @param RepositorySet        $repositorySet
      * @param CompositeRepository  $installedRepo
      * @param Request              $request
      * @param OperationInterface[] $operations
      * @param OperationInterface   $operation
      */
-    public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, Pool $pool, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation)
+    public function __construct($eventName, Composer $composer, IOInterface $io, $devMode, PolicyInterface $policy, RepositorySet $repositorySet, CompositeRepository $installedRepo, Request $request, array $operations, OperationInterface $operation)
     {
-        parent::__construct($eventName, $composer, $io, $devMode, $policy, $pool, $installedRepo, $request, $operations);
+        parent::__construct($eventName, $composer, $io, $devMode, $policy, $repositorySet, $installedRepo, $request, $operations);
 
         $this->operation = $operation;
     }

+ 2 - 2
src/Composer/Package/Locker.php

@@ -14,9 +14,9 @@ namespace Composer\Package;
 
 use Composer\Json\JsonFile;
 use Composer\Installer\InstallationManager;
+use Composer\Repository\LockArrayRepository;
 use Composer\Repository\RepositoryManager;
 use Composer\Util\ProcessExecutor;
-use Composer\Repository\ArrayRepository;
 use Composer\Package\Dumper\ArrayDumper;
 use Composer\Package\Loader\ArrayLoader;
 use Composer\Util\Git as GitUtil;
@@ -150,7 +150,7 @@ class Locker
     public function getLockedRepository($withDevReqs = false)
     {
         $lockData = $this->getLockData();
-        $packages = new ArrayRepository();
+        $packages = new LockArrayRepository();
 
         $lockedPackages = $lockData['packages'];
         if ($withDevReqs) {

+ 5 - 4
src/Composer/Package/Version/VersionSelector.php

@@ -17,6 +17,7 @@ use Composer\Package\BasePackage;
 use Composer\Package\PackageInterface;
 use Composer\Package\Loader\ArrayLoader;
 use Composer\Package\Dumper\ArrayDumper;
+use Composer\Repository\RepositorySet;
 use Composer\Semver\Constraint\Constraint;
 
 /**
@@ -27,13 +28,13 @@ use Composer\Semver\Constraint\Constraint;
  */
 class VersionSelector
 {
-    private $pool;
+    private $repositorySet;
 
     private $parser;
 
-    public function __construct(Pool $pool)
+    public function __construct(RepositorySet $repositorySet)
     {
-        $this->pool = $pool;
+        $this->repositorySet = $repositorySet;
     }
 
     /**
@@ -49,7 +50,7 @@ class VersionSelector
     public function findBestCandidate($packageName, $targetPackageVersion = null, $targetPhpVersion = null, $preferredStability = 'stable')
     {
         $constraint = $targetPackageVersion ? $this->getParser()->parseConstraints($targetPackageVersion) : null;
-        $candidates = $this->pool->whatProvides(strtolower($packageName), $constraint, true);
+        $candidates = $this->repositorySet->findPackages(strtolower($packageName), $constraint);
 
         if ($targetPhpVersion) {
             $phpConstraint = new Constraint('==', $this->getParser()->normalize($targetPhpVersion));

+ 1 - 1
src/Composer/Plugin/PluginInterface.php

@@ -27,7 +27,7 @@ interface PluginInterface
      *
      * @var string
      */
-    const PLUGIN_API_VERSION = '1.1.0';
+    const PLUGIN_API_VERSION = '2.0.0';
 
     /**
      * Apply plugin modifications to Composer

+ 15 - 15
src/Composer/Plugin/PluginManager.php

@@ -21,8 +21,8 @@ use Composer\Repository\RepositoryInterface;
 use Composer\Package\AliasPackage;
 use Composer\Package\PackageInterface;
 use Composer\Package\Link;
+use Composer\Repository\RepositorySet;
 use Composer\Semver\Constraint\Constraint;
-use Composer\DependencyResolver\Pool;
 use Composer\Plugin\Capability\Capability;
 
 /**
@@ -157,14 +157,14 @@ class PluginManager
         $localRepo = $this->composer->getRepositoryManager()->getLocalRepository();
         $globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null;
 
-        $pool = new Pool('dev');
-        $pool->addRepository($localRepo);
+        $repositorySet = new RepositorySet(array(), 'dev');
+        $repositorySet->addRepository($localRepo);
         if ($globalRepo) {
-            $pool->addRepository($globalRepo);
+            $repositorySet->addRepository($globalRepo);
         }
 
         $autoloadPackages = array($package->getName() => $package);
-        $autoloadPackages = $this->collectDependencies($pool, $autoloadPackages, $package);
+        $autoloadPackages = $this->collectDependencies($repositorySet, $autoloadPackages, $package);
 
         $generator = $this->composer->getAutoloadGenerator();
         $autoloads = array();
@@ -269,13 +269,13 @@ class PluginManager
     /**
      * Recursively generates a map of package names to packages for all deps
      *
-     * @param Pool             $pool      Package pool of installed packages
-     * @param array            $collected Current state of the map for recursion
-     * @param PackageInterface $package   The package to analyze
+     * @param RepositorySet    $repositorySet Repository set of installed packages
+     * @param array            $collected     Current state of the map for recursion
+     * @param PackageInterface $package       The package to analyze
      *
      * @return array Map of package names to packages
      */
-    private function collectDependencies(Pool $pool, array $collected, PackageInterface $package)
+    private function collectDependencies(RepositorySet $repositorySet, array $collected, PackageInterface $package)
     {
         $requires = array_merge(
             $package->getRequires(),
@@ -283,10 +283,10 @@ class PluginManager
         );
 
         foreach ($requires as $requireLink) {
-            $requiredPackage = $this->lookupInstalledPackage($pool, $requireLink);
+            $requiredPackage = $this->lookupInstalledPackage($repositorySet, $requireLink);
             if ($requiredPackage && !isset($collected[$requiredPackage->getName()])) {
                 $collected[$requiredPackage->getName()] = $requiredPackage;
-                $collected = $this->collectDependencies($pool, $collected, $requiredPackage);
+                $collected = $this->collectDependencies($repositorySet, $collected, $requiredPackage);
             }
         }
 
@@ -294,18 +294,18 @@ class PluginManager
     }
 
     /**
-     * Resolves a package link to a package in the installed pool
+     * Resolves a package link to a package in the installed repo set
      *
      * Since dependencies are already installed this should always find one.
      *
-     * @param Pool $pool Pool of installed packages only
+     * @param RepositorySet $repositorySet Repository set of installed packages only
      * @param Link $link Package link to look up
      *
      * @return PackageInterface|null The found package
      */
-    private function lookupInstalledPackage(Pool $pool, Link $link)
+    private function lookupInstalledPackage(RepositorySet $repositorySet, Link $link)
     {
-        $packages = $pool->whatProvides($link->getTarget(), $link->getConstraint());
+        $packages = $repositorySet->findPackages($link->getTarget(), $link->getConstraint(), false);
 
         return !empty($packages) ? $packages[0] : null;
     }

+ 38 - 0
src/Composer/Repository/AsyncRepositoryInterface.php

@@ -0,0 +1,38 @@
+<?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\Repository;
+
+use Composer\Package\PackageInterface;
+
+/**
+ * Repository interface.
+ *
+ * @author Nils Adermann <naderman@naderman.de>
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+interface AsyncRepositoryInterface
+{
+    /**
+     * @param array $names Names of packages to retrieve data for
+     * @return scalar Id to be passed to later loadPackages call
+     */
+    public function requestPackages(array $names);
+
+    /**
+     * @param array $names
+     * @return scalar id for load call
+     */
+    public function returnPackages($loadId);
+}
+

+ 15 - 0
src/Composer/Repository/BaseRepository.php

@@ -24,6 +24,21 @@ use Composer\Package\Link;
  */
 abstract class BaseRepository implements RepositoryInterface
 {
+    // TODO should this stay here? some repos need a better implementation
+    public function loadPackages(array $packageNameMap, $isPackageAcceptableCallable)
+    {
+        $packages = $this->getPackages();
+
+        $result = array();
+        foreach ($packages as $package) {
+            if (isset($packageNameMap[$package->getName()]) && call_user_func($isPackageAcceptableCallable, $package->getNames(), $package->getStability())) {
+                $result[] = $package;
+            }
+        }
+
+        return $result;
+    }
+
     /**
      * Returns a list of links causing the requested needle packages to be installed, as an associative array with the
      * dependent's name as key, and an array containing in order the PackageInterface and Link describing the relationship

+ 27 - 8
src/Composer/Repository/ComposerRepository.php

@@ -16,7 +16,6 @@ use Composer\Package\Loader\ArrayLoader;
 use Composer\Package\PackageInterface;
 use Composer\Package\AliasPackage;
 use Composer\Package\Version\VersionParser;
-use Composer\DependencyResolver\Pool;
 use Composer\Json\JsonFile;
 use Composer\Cache;
 use Composer\Config;
@@ -136,7 +135,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
 
         foreach ($this->getProviderNames() as $providerName) {
             if ($name === $providerName) {
-                $packages = $this->whatProvides(new Pool('dev'), $providerName);
+                $packages = $this->whatProvides($providerName);
                 foreach ($packages as $package) {
                     if ($name === $package->getName()) {
                         $pkgConstraint = new Constraint('==', $package->getVersion());
@@ -170,7 +169,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
 
         foreach ($this->getProviderNames() as $providerName) {
             if ($name === $providerName) {
-                $candidates = $this->whatProvides(new Pool('dev'), $providerName);
+                $candidates = $this->whatProvides($providerName);
                 foreach ($candidates as $package) {
                     if ($name === $package->getName()) {
                         $pkgConstraint = new Constraint('==', $package->getVersion());
@@ -195,6 +194,26 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
         return parent::getPackages();
     }
 
+    public function loadPackages(array $packageNameMap, $isPackageAcceptableCallable)
+    {
+        if (!$this->hasProviders()) {
+            // TODO build more efficient version of this
+            return parent::loadPackages($packageNameMap, $isPackageAcceptableCallable);
+        }
+
+        $packages = array();
+        foreach ($packageNameMap as $name => $void) {
+            $matches = array();
+            foreach ($this->whatProvides($name, false, $isPackageAcceptableCallable) as $match) {
+                if ($match->getName() === $name) {
+                    $matches[] = $match;
+                }
+            }
+            $packages = array_merge($packages, $matches);
+        }
+        return $packages;
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -289,12 +308,12 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
     }
 
     /**
-     * @param  Pool        $pool
-     * @param  string      $name          package name
-     * @param  bool        $bypassFilters If set to true, this bypasses the stability filtering, and forces a recompute without cache
+     * @param string $name package name
+     * @param bool $bypassFilters If set to true, this bypasses the stability filtering, and forces a recompute without cache
+     * @param callable $isPackageAcceptableCallable
      * @return array|mixed
      */
-    public function whatProvides(Pool $pool, $name, $bypassFilters = false)
+    public function whatProvides($name, $bypassFilters = false, $isPackageAcceptableCallable = null)
     {
         if (isset($this->providers[$name]) && !$bypassFilters) {
             return $this->providers[$name];
@@ -395,7 +414,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
                         }
                     }
                 } else {
-                    if (!$bypassFilters && !$pool->isPackageAcceptable(strtolower($version['name']), VersionParser::parseStability($version['version']))) {
+                    if (!$bypassFilters && $isPackageAcceptableCallable && !call_user_func($isPackageAcceptableCallable, strtolower($version['name']), VersionParser::parseStability($version['version']))) {
                         continue;
                     }
 

+ 25 - 0
src/Composer/Repository/LockArrayRepository.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\Repository;
+
+/**
+ * Lock array repository.
+ *
+ * Regular array repository, only uses a different type to identify the lock file as the source of info
+ *
+ * @author Nils Adermann <naderman@naderman.de>
+ */
+class LockArrayRepository extends ArrayRepository implements RepositoryInterface
+{
+}
+

+ 10 - 0
src/Composer/Repository/RepositoryInterface.php

@@ -55,6 +55,7 @@ interface RepositoryInterface extends \Countable
      */
     public function findPackages($name, $constraint = null);
 
+    // TODO this should really not be in this generic interface anymore
     /**
      * Returns list of registered packages.
      *
@@ -62,6 +63,15 @@ interface RepositoryInterface extends \Countable
      */
     public function getPackages();
 
+    /**
+     * Returns list of registered packages with the supplied name
+     *
+     * @param bool[] $packageNameMap
+     * @param $isPackageAcceptableCallable
+     * @return PackageInterface[]
+     */
+    public function loadPackages(array $packageNameMap, $isPackageAcceptableCallable);
+
     /**
      * Searches the repository for packages containing the query
      *

+ 179 - 0
src/Composer/Repository/RepositorySet.php

@@ -0,0 +1,179 @@
+<?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\Repository;
+
+use Composer\DependencyResolver\Pool;
+use Composer\DependencyResolver\PoolBuilder;
+use Composer\DependencyResolver\Request;
+use Composer\Package\BasePackage;
+use Composer\Package\Version\VersionParser;
+use Composer\Repository\CompositeRepository;
+use Composer\Repository\PlatformRepository;
+use Composer\Semver\Constraint\ConstraintInterface;
+use Composer\Test\DependencyResolver\PoolTest;
+
+/**
+ * @author Nils Adermann <naderman@naderman.de>
+ */
+class RepositorySet
+{
+    /** @var array */
+    private $rootAliases;
+
+    /** @var RepositoryInterface[] */
+    private $repositories = array();
+
+    private $acceptableStabilities;
+    private $stabilityFlags;
+    protected $filterRequires;
+
+    /** @var Pool */
+    private $pool;
+
+    public function __construct(array $rootAliases = array(), $minimumStability = 'stable', array $stabilityFlags = array(), array $filterRequires = array())
+    {
+        $this->rootAliases = $rootAliases;
+
+        $this->acceptableStabilities = array();
+        foreach (BasePackage::$stabilities as $stability => $value) {
+            if ($value <= BasePackage::$stabilities[$minimumStability]) {
+                $this->acceptableStabilities[$stability] = $value;
+            }
+        }
+        $this->stabilityFlags = $stabilityFlags;
+        $this->filterRequires = $filterRequires;
+        foreach ($filterRequires as $name => $constraint) {
+            if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) {
+                unset($this->filterRequires[$name]);
+            }
+        }
+    }
+
+    /**
+     * Adds a repository to this repository set
+     *
+     * @param RepositoryInterface $repo        A package repository
+     */
+    public function addRepository(RepositoryInterface $repo)
+    {
+        if ($this->pool) {
+            throw new \RuntimeException("Pool has already been created from this repository set, it cannot be modified anymore.");
+        }
+
+        if ($repo instanceof CompositeRepository) {
+            $repos = $repo->getRepositories();
+        } else {
+            $repos = array($repo);
+        }
+
+        foreach ($repos as $repo) {
+            $this->repositories[] = $repo;
+        }
+    }
+
+    public function isPackageAcceptable($name, $stability)
+    {
+        foreach ((array) $name as $n) {
+            // allow if package matches the global stability requirement and has no exception
+            if (!isset($this->stabilityFlags[$n]) && isset($this->acceptableStabilities[$stability])) {
+                return true;
+            }
+
+            // allow if package matches the package-specific stability flag
+            if (isset($this->stabilityFlags[$n]) && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$n]) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Find packages providing or matching a name and optionally meeting a constraint in all repositories
+     *
+     * @param string $name
+     * @param ConstraintInterface|null $constraint
+     * @param bool $exactMatch
+     * @return array
+     */
+    public function findPackages($name, ConstraintInterface $constraint = null, $exactMatch = true)
+    {
+        $packages = array();
+        foreach ($this->repositories as $repository) {
+            $packages[] = $repository->findPackages($name, $constraint) ?: array();
+        }
+
+        $candidates = $packages ? call_user_func_array('array_merge', $packages) : array();
+
+        $result = array();
+        foreach ($candidates as $candidate) {
+            if ($exactMatch && $candidate->getName() !== $name) {
+                continue;
+            }
+
+            if ($this->isPackageAcceptable($candidate->getNames(), $candidate->getStability())) {
+                $result[] = $candidate;
+            }
+        }
+
+        return $candidates;
+    }
+
+    public function getPriority(RepositoryInterface $repo)
+    {
+        $priority = array_search($repo, $this->repositories, true);
+
+        if (false === $priority) {
+            throw new \RuntimeException("Could not determine repository priority. The repository was not registered in the pool.");
+        }
+
+        return -$priority;
+    }
+
+    /**
+     * Create a pool for dependency resolution from the packages in this repository set.
+     *
+     * @return Pool
+     */
+    public function createPool(Request $request)
+    {
+        $poolBuilder = new PoolBuilder(array($this, 'isPackageAcceptable'), $this->filterRequires);
+
+        return $this->pool = $poolBuilder->buildPool($this->repositories, $this->rootAliases, $request);
+    }
+
+    // TODO unify this with above in some simpler version without "request"?
+    public function createPoolForPackage($packageName)
+    {
+        return $this->createPoolForPackages(array($packageName));
+    }
+
+    public function createPoolForPackages($packageNames)
+    {
+        $request = new Request();
+        foreach ($packageNames as $packageName) {
+            $request->install($packageName);
+        }
+
+        return $this->createPool($request);
+    }
+
+    /**
+     * Access the pool object after it has been created, relevant for plugins which need to read info from the pool
+     * @return Pool
+     */
+    public function getPool()
+    {
+        return $this->pool;
+    }
+}

+ 75 - 43
tests/Composer/Test/DependencyResolver/DefaultPolicyTest.php

@@ -18,13 +18,15 @@ use Composer\DependencyResolver\DefaultPolicy;
 use Composer\DependencyResolver\Pool;
 use Composer\Package\Link;
 use Composer\Package\AliasPackage;
+use Composer\Repository\RepositorySet;
 use Composer\Semver\Constraint\Constraint;
 use Composer\TestCase;
+use http\Env\Request;
 
 class DefaultPolicyTest extends TestCase
 {
-    /** @var Pool */
-    protected $pool;
+    /** @var RepositorySet */
+    protected $repositorySet;
     /** @var ArrayRepository */
     protected $repo;
     /** @var ArrayRepository */
@@ -34,7 +36,7 @@ class DefaultPolicyTest extends TestCase
 
     public function setUp()
     {
-        $this->pool = new Pool('dev');
+        $this->repositorySet = new RepositorySet(array(), 'dev');
         $this->repo = new ArrayRepository;
         $this->repoInstalled = new ArrayRepository;
 
@@ -44,12 +46,14 @@ class DefaultPolicyTest extends TestCase
     public function testSelectSingle()
     {
         $this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
-        $this->pool->addRepository($this->repo);
+        $this->repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackage('A');
 
         $literals = array($packageA->getId());
         $expected = array($packageA->getId());
 
-        $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+        $selected = $this->policy->selectPreferredPackages($pool, array(), $literals);
 
         $this->assertSame($expected, $selected);
     }
@@ -58,12 +62,14 @@ class DefaultPolicyTest extends TestCase
     {
         $this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0'));
         $this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0'));
-        $this->pool->addRepository($this->repo);
+        $this->repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackage('A');
 
         $literals = array($packageA1->getId(), $packageA2->getId());
         $expected = array($packageA2->getId());
 
-        $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+        $selected = $this->policy->selectPreferredPackages($pool, array(), $literals);
 
         $this->assertSame($expected, $selected);
     }
@@ -72,12 +78,14 @@ class DefaultPolicyTest extends TestCase
     {
         $this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0.0'));
         $this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.1-alpha'));
-        $this->pool->addRepository($this->repo);
+        $this->repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackage('A');
 
         $literals = array($packageA1->getId(), $packageA2->getId());
         $expected = array($packageA2->getId());
 
-        $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+        $selected = $this->policy->selectPreferredPackages($pool, array(), $literals);
 
         $this->assertSame($expected, $selected);
     }
@@ -86,13 +94,15 @@ class DefaultPolicyTest extends TestCase
     {
         $this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0.0'));
         $this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.1-alpha'));
-        $this->pool->addRepository($this->repo);
+        $this->repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackage('A');
 
         $literals = array($packageA1->getId(), $packageA2->getId());
         $expected = array($packageA1->getId());
 
         $policy = new DefaultPolicy(true);
-        $selected = $policy->selectPreferredPackages($this->pool, array(), $literals);
+        $selected = $policy->selectPreferredPackages($pool, array(), $literals);
 
         $this->assertSame($expected, $selected);
     }
@@ -101,12 +111,14 @@ class DefaultPolicyTest extends TestCase
     {
         $this->repo->addPackage($packageA1 = $this->getPackage('A', 'dev-foo'));
         $this->repo->addPackage($packageA2 = $this->getPackage('A', '1.0.0'));
-        $this->pool->addRepository($this->repo);
+        $this->repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackage('A');
 
         $literals = array($packageA1->getId(), $packageA2->getId());
         $expected = array($packageA2->getId());
 
-        $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+        $selected = $this->policy->selectPreferredPackages($pool, array(), $literals);
 
         $this->assertSame($expected, $selected);
     }
@@ -115,13 +127,15 @@ class DefaultPolicyTest extends TestCase
     {
         $this->repo->addPackage($packageA = $this->getPackage('A', '2.0'));
         $this->repoInstalled->addPackage($packageAInstalled = $this->getPackage('A', '1.0'));
-        $this->pool->addRepository($this->repoInstalled);
-        $this->pool->addRepository($this->repo);
+        $this->repositorySet->addRepository($this->repoInstalled);
+        $this->repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackage('A');
 
         $literals = array($packageA->getId(), $packageAInstalled->getId());
         $expected = array($packageA->getId());
 
-        $selected = $this->policy->selectPreferredPackages($this->pool, $this->mapFromRepo($this->repoInstalled), $literals);
+        $selected = $this->policy->selectPreferredPackages($pool, $this->mapFromRepo($this->repoInstalled), $literals);
 
         $this->assertSame($expected, $selected);
     }
@@ -133,14 +147,16 @@ class DefaultPolicyTest extends TestCase
         $this->repo->addPackage($packageA = $this->getPackage('A', '1.0'));
         $otherRepository->addPackage($packageAImportant = $this->getPackage('A', '1.0'));
 
-        $this->pool->addRepository($this->repoInstalled);
-        $this->pool->addRepository($otherRepository);
-        $this->pool->addRepository($this->repo);
+        $this->repositorySet->addRepository($this->repoInstalled);
+        $this->repositorySet->addRepository($otherRepository);
+        $this->repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackage('A');
 
         $literals = array($packageA->getId(), $packageAImportant->getId());
         $expected = array($packageAImportant->getId());
 
-        $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+        $selected = $this->policy->selectPreferredPackages($pool, array(), $literals);
 
         $this->assertSame($expected, $selected);
     }
@@ -155,21 +171,25 @@ class DefaultPolicyTest extends TestCase
         $repo2->addPackage($package3 = $this->getPackage('A', '1.1'));
         $repo2->addPackage($package4 = $this->getPackage('A', '1.2'));
 
-        $this->pool->addRepository($repo1);
-        $this->pool->addRepository($repo2);
+        $this->repositorySet->addRepository($repo1);
+        $this->repositorySet->addRepository($repo2);
+
+        $pool = $this->repositorySet->createPoolForPackage('A');
 
         $literals = array($package1->getId(), $package2->getId(), $package3->getId(), $package4->getId());
         $expected = array($package2->getId());
-        $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+        $selected = $this->policy->selectPreferredPackages($pool, array(), $literals);
 
         $this->assertSame($expected, $selected);
 
-        $this->pool = new Pool('dev');
-        $this->pool->addRepository($repo2);
-        $this->pool->addRepository($repo1);
+        $this->repositorySet = new RepositorySet(array(), 'dev');
+        $this->repositorySet->addRepository($repo2);
+        $this->repositorySet->addRepository($repo1);
+
+        $pool = $this->repositorySet->createPoolForPackage('A');
 
         $expected = array($package4->getId());
-        $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+        $selected = $this->policy->selectPreferredPackages($pool, array(), $literals);
 
         $this->assertSame($expected, $selected);
     }
@@ -186,11 +206,13 @@ class DefaultPolicyTest extends TestCase
         $repoImportant->addPackage($packageA2AliasImportant = new AliasPackage($packageA2Important, '2.1.9999999.9999999-dev', '2.1.x-dev'));
         $packageAAliasImportant->setRootPackageAlias(true);
 
-        $this->pool->addRepository($this->repoInstalled);
-        $this->pool->addRepository($repoImportant);
-        $this->pool->addRepository($this->repo);
+        $this->repositorySet->addRepository($this->repoInstalled);
+        $this->repositorySet->addRepository($repoImportant);
+        $this->repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackage('A');
 
-        $packages = $this->pool->whatProvides('a', new Constraint('=', '2.1.9999999.9999999-dev'));
+        $packages = $pool->whatProvides('a', new Constraint('=', '2.1.9999999.9999999-dev'));
         $literals = array();
         foreach ($packages as $package) {
             $literals[] = $package->getId();
@@ -198,7 +220,7 @@ class DefaultPolicyTest extends TestCase
 
         $expected = array($packageAAliasImportant->getId());
 
-        $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+        $selected = $this->policy->selectPreferredPackages($pool, array(), $literals);
 
         $this->assertSame($expected, $selected);
     }
@@ -211,12 +233,14 @@ class DefaultPolicyTest extends TestCase
         $packageA->setProvides(array(new Link('A', 'X', new Constraint('==', '1.0'), 'provides')));
         $packageB->setProvides(array(new Link('B', 'X', new Constraint('==', '1.0'), 'provides')));
 
-        $this->pool->addRepository($this->repo);
+        $this->repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackages(array('A', 'B'));
 
         $literals = array($packageA->getId(), $packageB->getId());
         $expected = $literals;
 
-        $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+        $selected = $this->policy->selectPreferredPackages($pool, array(), $literals);
 
         $this->assertSame($expected, $selected);
     }
@@ -228,12 +252,14 @@ class DefaultPolicyTest extends TestCase
 
         $packageB->setReplaces(array(new Link('B', 'A', new Constraint('==', '1.0'), 'replaces')));
 
-        $this->pool->addRepository($this->repo);
+        $this->repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackages(array('A', 'B'));
 
         $literals = array($packageA->getId(), $packageB->getId());
         $expected = $literals;
 
-        $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals);
+        $selected = $this->policy->selectPreferredPackages($pool, array(), $literals);
 
         $this->assertSame($expected, $selected);
     }
@@ -247,12 +273,14 @@ class DefaultPolicyTest extends TestCase
         $packageA->setReplaces(array(new Link('vendor-a/replacer', 'vendor-a/package', new Constraint('==', '1.0'), 'replaces')));
         $packageB->setReplaces(array(new Link('vendor-b/replacer', 'vendor-a/package', new Constraint('==', '1.0'), 'replaces')));
 
-        $this->pool->addRepository($this->repo);
+        $this->repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackages(array('vendor-a/replacer', 'vendor-b/replacer'));
 
         $literals = array($packageA->getId(), $packageB->getId());
         $expected = $literals;
 
-        $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals, 'vendor-a/package');
+        $selected = $this->policy->selectPreferredPackages($pool, array(), $literals, 'vendor-a/package');
         $this->assertEquals($expected, $selected);
 
         // test with reversed order in repo
@@ -260,13 +288,15 @@ class DefaultPolicyTest extends TestCase
         $repo->addPackage($packageA = clone $packageA);
         $repo->addPackage($packageB = clone $packageB);
 
-        $pool = new Pool('dev');
-        $pool->addRepository($this->repo);
+        $repositorySet = new RepositorySet(array(), 'dev');
+        $repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackages(array('vendor-a/replacer', 'vendor-b/replacer'));
 
         $literals = array($packageA->getId(), $packageB->getId());
         $expected = $literals;
 
-        $selected = $this->policy->selectPreferredPackages($this->pool, array(), $literals, 'vendor-a/package');
+        $selected = $this->policy->selectPreferredPackages($pool, array(), $literals, 'vendor-a/package');
         $this->assertSame($expected, $selected);
     }
 
@@ -286,12 +316,14 @@ class DefaultPolicyTest extends TestCase
 
         $this->repo->addPackage($packageA1 = $this->getPackage('A', '1.0'));
         $this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0'));
-        $this->pool->addRepository($this->repo);
+        $this->repositorySet->addRepository($this->repo);
+
+        $pool = $this->repositorySet->createPoolForPackage('A');
 
         $literals = array($packageA1->getId(), $packageA2->getId());
         $expected = array($packageA1->getId());
 
-        $selected = $policy->selectPreferredPackages($this->pool, array(), $literals);
+        $selected = $policy->selectPreferredPackages($pool, array(), $literals);
 
         $this->assertSame($expected, $selected);
     }

+ 15 - 77
tests/Composer/Test/DependencyResolver/PoolTest.php

@@ -21,91 +21,26 @@ class PoolTest extends TestCase
 {
     public function testPool()
     {
-        $pool = new Pool;
-        $repo = new ArrayRepository;
+        $pool = $this->createPool();
         $package = $this->getPackage('foo', '1');
 
-        $repo->addPackage($package);
-        $pool->addRepository($repo);
+        $pool->setPackages(array($package));
 
         $this->assertEquals(array($package), $pool->whatProvides('foo'));
         $this->assertEquals(array($package), $pool->whatProvides('foo'));
     }
 
-    public function testPoolIgnoresIrrelevantPackages()
-    {
-        $pool = new Pool('stable', array('bar' => BasePackage::STABILITY_BETA));
-        $repo = new ArrayRepository;
-        $repo->addPackage($package = $this->getPackage('bar', '1'));
-        $repo->addPackage($betaPackage = $this->getPackage('bar', '1-beta'));
-        $repo->addPackage($alphaPackage = $this->getPackage('bar', '1-alpha'));
-        $repo->addPackage($package2 = $this->getPackage('foo', '1'));
-        $repo->addPackage($rcPackage2 = $this->getPackage('foo', '1rc'));
-
-        $pool->addRepository($repo);
-
-        $this->assertEquals(array($package, $betaPackage), $pool->whatProvides('bar'));
-        $this->assertEquals(array($package2), $pool->whatProvides('foo'));
-    }
-
-    /**
-     * @expectedException \RuntimeException
-     */
-    public function testGetPriorityForNotRegisteredRepository()
-    {
-        $pool = new Pool;
-        $repository = new ArrayRepository;
-
-        $pool->getPriority($repository);
-    }
-
-    public function testGetPriorityWhenRepositoryIsRegistered()
-    {
-        $pool = new Pool;
-        $firstRepository = new ArrayRepository;
-        $pool->addRepository($firstRepository);
-        $secondRepository = new ArrayRepository;
-        $pool->addRepository($secondRepository);
-
-        $firstPriority = $pool->getPriority($firstRepository);
-        $secondPriority = $pool->getPriority($secondRepository);
-
-        $this->assertEquals(0, $firstPriority);
-        $this->assertEquals(-1, $secondPriority);
-    }
-
-    public function testWhatProvidesSamePackageForDifferentRepositories()
-    {
-        $pool = new Pool;
-        $firstRepository = new ArrayRepository;
-        $secondRepository = new ArrayRepository;
-
-        $firstPackage = $this->getPackage('foo', '1');
-        $secondPackage = $this->getPackage('foo', '1');
-        $thirdPackage = $this->getPackage('foo', '2');
-
-        $firstRepository->addPackage($firstPackage);
-        $secondRepository->addPackage($secondPackage);
-        $secondRepository->addPackage($thirdPackage);
-
-        $pool->addRepository($firstRepository);
-        $pool->addRepository($secondRepository);
-
-        $this->assertEquals(array($firstPackage, $secondPackage, $thirdPackage), $pool->whatProvides('foo'));
-    }
-
     public function testWhatProvidesPackageWithConstraint()
     {
-        $pool = new Pool;
-        $repository = new ArrayRepository;
+        $pool = $this->createPool();
 
         $firstPackage = $this->getPackage('foo', '1');
         $secondPackage = $this->getPackage('foo', '2');
 
-        $repository->addPackage($firstPackage);
-        $repository->addPackage($secondPackage);
-
-        $pool->addRepository($repository);
+        $pool->setPackages(array(
+            $firstPackage,
+            $secondPackage,
+        ));
 
         $this->assertEquals(array($firstPackage, $secondPackage), $pool->whatProvides('foo'));
         $this->assertEquals(array($secondPackage), $pool->whatProvides('foo', $this->getVersionConstraint('==', '2')));
@@ -113,20 +48,23 @@ class PoolTest extends TestCase
 
     public function testPackageById()
     {
-        $pool = new Pool;
-        $repository = new ArrayRepository;
+        $pool = $this->createPool();
         $package = $this->getPackage('foo', '1');
 
-        $repository->addPackage($package);
-        $pool->addRepository($repository);
+        $pool->setPackages(array($package));
 
         $this->assertSame($package, $pool->packageById(1));
     }
 
     public function testWhatProvidesWhenPackageCannotBeFound()
     {
-        $pool = new Pool;
+        $pool = $this->createPool();
 
         $this->assertEquals(array(), $pool->whatProvides('foo'));
     }
+
+    protected function createPool()
+    {
+        return new Pool(array('stable' => BasePackage::STABILITY_STABLE));
+    }
 }

+ 2 - 1
tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php

@@ -17,6 +17,7 @@ use Composer\DependencyResolver\Rule;
 use Composer\DependencyResolver\RuleSet;
 use Composer\DependencyResolver\RuleSetIterator;
 use Composer\DependencyResolver\Pool;
+use Composer\Package\BasePackage;
 use PHPUnit\Framework\TestCase;
 
 class RuleSetIteratorTest extends TestCase
@@ -26,7 +27,7 @@ class RuleSetIteratorTest extends TestCase
 
     protected function setUp()
     {
-        $this->pool = new Pool;
+        $this->pool = new Pool(array('stable' => BasePackage::STABILITY_STABLE));
 
         $this->rules = array(
             RuleSet::TYPE_JOB => array(

+ 7 - 11
tests/Composer/Test/DependencyResolver/RuleSetTest.php

@@ -16,18 +16,12 @@ use Composer\DependencyResolver\GenericRule;
 use Composer\DependencyResolver\Rule;
 use Composer\DependencyResolver\RuleSet;
 use Composer\DependencyResolver\Pool;
+use Composer\Package\BasePackage;
 use Composer\Repository\ArrayRepository;
 use Composer\TestCase;
 
 class RuleSetTest extends TestCase
 {
-    protected $pool;
-
-    public function setUp()
-    {
-        $this->pool = new Pool;
-    }
-
     public function testAdd()
     {
         $rules = array(
@@ -145,9 +139,11 @@ class RuleSetTest extends TestCase
 
     public function testPrettyString()
     {
-        $repo = new ArrayRepository;
-        $repo->addPackage($p = $this->getPackage('foo', '2.1'));
-        $this->pool->addRepository($repo);
+        $pool = new Pool(array('stable' => BasePackage::STABILITY_STABLE));
+        $pool->setPackages(array(
+            $p = $this->getPackage('foo', '2.1'),
+        ));
+        $p->setId(1);
 
         $ruleSet = new RuleSet;
         $literal = $p->getId();
@@ -155,7 +151,7 @@ class RuleSetTest extends TestCase
 
         $ruleSet->add($rule, RuleSet::TYPE_JOB);
 
-        $this->assertContains('JOB     : Install command rule (install foo 2.1)', $ruleSet->getPrettyString($this->pool));
+        $this->assertContains('JOB     : Install command rule (install foo 2.1)', $ruleSet->getPrettyString($pool));
     }
 
     private function getRuleMock()

+ 9 - 12
tests/Composer/Test/DependencyResolver/RuleTest.php

@@ -16,18 +16,12 @@ use Composer\DependencyResolver\GenericRule;
 use Composer\DependencyResolver\Rule;
 use Composer\DependencyResolver\RuleSet;
 use Composer\DependencyResolver\Pool;
+use Composer\Package\BasePackage;
 use Composer\Repository\ArrayRepository;
 use Composer\TestCase;
 
 class RuleTest extends TestCase
 {
-    protected $pool;
-
-    public function setUp()
-    {
-        $this->pool = new Pool;
-    }
-
     public function testGetHash()
     {
         $rule = new GenericRule(array(123), Rule::RULE_JOB_INSTALL, null);
@@ -99,13 +93,16 @@ class RuleTest extends TestCase
 
     public function testPrettyString()
     {
-        $repo = new ArrayRepository;
-        $repo->addPackage($p1 = $this->getPackage('foo', '2.1'));
-        $repo->addPackage($p2 = $this->getPackage('baz', '1.1'));
-        $this->pool->addRepository($repo);
+        $pool = new Pool(array('stable' => BasePackage::STABILITY_STABLE));
+        $pool->setPackages(array(
+            $p1 = $this->getPackage('foo', '2.1'),
+            $p2 = $this->getPackage('baz', '1.1'),
+        ));
+        $p1->setId(1);
+        $p2->setId(2);
 
         $rule = new GenericRule(array($p1->getId(), -$p2->getId()), Rule::RULE_JOB_INSTALL, null);
 
-        $this->assertEquals('Install command rule (don\'t install baz 1.1|install foo 2.1)', $rule->getPrettyString($this->pool));
+        $this->assertEquals('Install command rule (don\'t install baz 1.1|install foo 2.1)', $rule->getPrettyString($pool));
     }
 }

+ 25 - 10
tests/Composer/Test/DependencyResolver/SolverTest.php

@@ -20,26 +20,28 @@ use Composer\DependencyResolver\Request;
 use Composer\DependencyResolver\Solver;
 use Composer\DependencyResolver\SolverProblemsException;
 use Composer\Package\Link;
+use Composer\Repository\InstalledArrayRepository;
+use Composer\Repository\RepositorySet;
 use Composer\TestCase;
 use Composer\Semver\Constraint\MultiConstraint;
 
 class SolverTest extends TestCase
 {
-    protected $pool;
+    protected $repoSet;
     protected $repo;
     protected $repoInstalled;
     protected $request;
     protected $policy;
+    protected $solver;
 
     public function setUp()
     {
-        $this->pool = new Pool;
+        $this->repoSet = new RepositorySet(array());
         $this->repo = new ArrayRepository;
-        $this->repoInstalled = new ArrayRepository;
+        $this->repoInstalled = new InstalledArrayRepository;
 
-        $this->request = new Request($this->pool);
+        $this->request = new Request($this->repoSet);
         $this->policy = new DefaultPolicy;
-        $this->solver = new Solver($this->policy, $this->pool, $this->repoInstalled, new NullIO());
     }
 
     public function testSolverInstallSingle()
@@ -71,6 +73,7 @@ class SolverTest extends TestCase
 
         $this->request->install('B', $this->getVersionConstraint('==', '1'));
 
+        $this->createSolver();
         try {
             $transaction = $this->solver->solve($this->request);
             $this->fail('Unsolvable conflict did not result in exception.');
@@ -90,9 +93,9 @@ class SolverTest extends TestCase
         $repo1->addPackage($foo1 = $this->getPackage('foo', '1'));
         $repo2->addPackage($foo2 = $this->getPackage('foo', '1'));
 
-        $this->pool->addRepository($this->repoInstalled);
-        $this->pool->addRepository($repo1);
-        $this->pool->addRepository($repo2);
+        $this->repoSet->addRepository($this->repoInstalled);
+        $this->repoSet->addRepository($repo1);
+        $this->repoSet->addRepository($repo2);
 
         $this->request->install('foo');
 
@@ -445,6 +448,7 @@ class SolverTest extends TestCase
 
         // must explicitly pick the provider, so error in this case
         $this->setExpectedException('Composer\DependencyResolver\SolverProblemsException');
+        $this->createSolver();
         $this->solver->solve($this->request);
     }
 
@@ -478,6 +482,7 @@ class SolverTest extends TestCase
         $this->request->install('A');
 
         $this->setExpectedException('Composer\DependencyResolver\SolverProblemsException');
+        $this->createSolver();
         $this->solver->solve($this->request);
     }
 
@@ -650,6 +655,7 @@ class SolverTest extends TestCase
 
         $this->setExpectedException('Composer\DependencyResolver\SolverProblemsException');
 
+        $this->createSolver();
         $this->solver->solve($this->request);
     }
 
@@ -666,6 +672,7 @@ class SolverTest extends TestCase
         $this->request->install('A');
         $this->request->install('B');
 
+        $this->createSolver();
         try {
             $transaction = $this->solver->solve($this->request);
             $this->fail('Unsolvable conflict did not result in exception.');
@@ -695,6 +702,7 @@ class SolverTest extends TestCase
 
         $this->request->install('A');
 
+        $this->createSolver();
         try {
             $transaction = $this->solver->solve($this->request);
             $this->fail('Unsolvable conflict did not result in exception.');
@@ -742,6 +750,7 @@ class SolverTest extends TestCase
 
         $this->request->install('A');
 
+        $this->createSolver();
         try {
             $transaction = $this->solver->solve($this->request);
             $this->fail('Unsolvable conflict did not result in exception.');
@@ -839,12 +848,18 @@ class SolverTest extends TestCase
 
     protected function reposComplete()
     {
-        $this->pool->addRepository($this->repoInstalled);
-        $this->pool->addRepository($this->repo);
+        $this->repoSet->addRepository($this->repoInstalled);
+        $this->repoSet->addRepository($this->repo);
+    }
+
+    protected function createSolver()
+    {
+        $this->solver = new Solver($this->policy, $this->repoSet->createPool($this->request), $this->repoInstalled, new NullIO());
     }
 
     protected function checkSolverResult(array $expected)
     {
+        $this->createSolver();
         $transaction = $this->solver->solve($this->request);
 
         $result = array();

+ 3 - 3
tests/Composer/Test/EventDispatcher/EventDispatcherTest.php

@@ -411,12 +411,12 @@ class EventDispatcherTest extends TestCase
             ->will($this->returnValue(array()));
 
         $policy = $this->getMockBuilder('Composer\DependencyResolver\PolicyInterface')->getMock();
-        $pool = $this->getMockBuilder('Composer\DependencyResolver\Pool')->disableOriginalConstructor()->getMock();
+        $repositorySet = $this->getMockBuilder('Composer\Repository\RepositorySet')->disableOriginalConstructor()->getMock();
         $installedRepo = $this->getMockBuilder('Composer\Repository\CompositeRepository')->disableOriginalConstructor()->getMock();
         $request = $this->getMockBuilder('Composer\DependencyResolver\Request')->disableOriginalConstructor()->getMock();
 
-        $dispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, true, $policy, $pool, $installedRepo, $request);
-        $dispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, true, $policy, $pool, $installedRepo, $request, array());
+        $dispatcher->dispatchInstallerEvent(InstallerEvents::PRE_DEPENDENCIES_SOLVING, true, $policy, $repositorySet, $installedRepo, $request);
+        $dispatcher->dispatchInstallerEvent(InstallerEvents::POST_DEPENDENCIES_SOLVING, true, $policy, $repositorySet, $installedRepo, $request, array());
     }
 
     public static function call()

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

@@ -22,18 +22,18 @@ class InstallerEventTest extends TestCase
         $composer = $this->getMockBuilder('Composer\Composer')->getMock();
         $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
         $policy = $this->getMockBuilder('Composer\DependencyResolver\PolicyInterface')->getMock();
-        $pool = $this->getMockBuilder('Composer\DependencyResolver\Pool')->disableOriginalConstructor()->getMock();
+        $repositorySet = $this->getMockBuilder('Composer\Repository\RepositorySet')->disableOriginalConstructor()->getMock();
         $installedRepo = $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, $pool, $installedRepo, $request, $operations);
+        $event = new InstallerEvent('EVENT_NAME', $composer, $io, true, $policy, $repositorySet, $installedRepo, $request, $operations);
 
         $this->assertSame('EVENT_NAME', $event->getName());
         $this->assertInstanceOf('Composer\Composer', $event->getComposer());
         $this->assertInstanceOf('Composer\IO\IOInterface', $event->getIO());
         $this->assertTrue($event->isDevMode());
         $this->assertInstanceOf('Composer\DependencyResolver\PolicyInterface', $event->getPolicy());
-        $this->assertInstanceOf('Composer\DependencyResolver\Pool', $event->getPool());
+        $this->assertInstanceOf('Composer\Repository\RepositorySet', $event->getRepositorySet());
         $this->assertInstanceOf('Composer\Repository\CompositeRepository', $event->getInstalledRepo());
         $this->assertInstanceOf('Composer\DependencyResolver\Request', $event->getRequest());
         $this->assertCount(1, $event->getOperations());

+ 5 - 3
tests/Composer/Test/InstallerTest.php

@@ -14,6 +14,7 @@ namespace Composer\Test;
 
 use Composer\Installer;
 use Composer\Console\Application;
+use Composer\IO\BufferIO;
 use Composer\Json\JsonFile;
 use Composer\Util\Filesystem;
 use Composer\Repository\ArrayRepository;
@@ -30,7 +31,6 @@ use Symfony\Component\Console\Output\StreamOutput;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Formatter\OutputFormatter;
 use Composer\TestCase;
-use Composer\IO\BufferIO;
 
 class InstallerTest extends TestCase
 {
@@ -57,7 +57,7 @@ class InstallerTest extends TestCase
      */
     public function testInstaller(RootPackageInterface $rootPackage, $repositories, array $options)
     {
-        $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
+        $io = new BufferIO('', OutputInterface::VERBOSITY_NORMAL, new OutputFormatter(false));
 
         $downloadManager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
             ->setConstructorArgs(array($io))
@@ -82,7 +82,9 @@ class InstallerTest extends TestCase
 
         $installer = new Installer($io, $config, clone $rootPackage, $downloadManager, $repositoryManager, $locker, $installationManager, $eventDispatcher, $autoloadGenerator);
         $result = $installer->run();
-        $this->assertSame(0, $result);
+
+        $output = str_replace("\r", '', $io->getOutput());
+        $this->assertEquals(0, $result, $output);
 
         $expectedInstalled = isset($options['install']) ? $options['install'] : array();
         $expectedUpdated = isset($options['update']) ? $options['update'] : array();

+ 49 - 47
tests/Composer/Test/Package/Version/VersionSelectorTest.php

@@ -21,7 +21,7 @@ use PHPUnit\Framework\TestCase;
 class VersionSelectorTest extends TestCase
 {
     // A) multiple versions, get the latest one
-    // B) targetPackageVersion will pass to pool
+    // B) targetPackageVersion will pass to repo set
     // C) No results, throw exception
 
     public function testLatestVersionIsReturned()
@@ -33,13 +33,13 @@ class VersionSelectorTest extends TestCase
         $package3 = $this->createPackage('1.2.0');
         $packages = array($package1, $package2, $package3);
 
-        $pool = $this->createMockPool();
-        $pool->expects($this->once())
-            ->method('whatProvides')
-            ->with($packageName, null, true)
+        $repositorySet = $this->createMockRepositorySet();
+        $repositorySet->expects($this->once())
+            ->method('findPackages')
+            ->with($packageName, null)
             ->will($this->returnValue($packages));
 
-        $versionSelector = new VersionSelector($pool);
+        $versionSelector = new VersionSelector($repositorySet);
         $best = $versionSelector->findBestCandidate($packageName);
 
         // 1.2.2 should be returned because it's the latest of the returned versions
@@ -57,13 +57,13 @@ class VersionSelectorTest extends TestCase
         $package2->setRequires(array('php' => new Link($packageName, 'php', $parser->parseConstraints('>=5.6'), 'requires', '>=5.6')));
         $packages = array($package1, $package2);
 
-        $pool = $this->createMockPool();
-        $pool->expects($this->once())
-            ->method('whatProvides')
-            ->with($packageName, null, true)
+        $repositorySet = $this->createMockRepositorySet();
+        $repositorySet->expects($this->once())
+            ->method('findPackages')
+            ->with($packageName, null)
             ->will($this->returnValue($packages));
 
-        $versionSelector = new VersionSelector($pool);
+        $versionSelector = new VersionSelector($repositorySet);
         $best = $versionSelector->findBestCandidate($packageName, null, '5.5.0');
 
         $this->assertSame($package1, $best, 'Latest version supporting php 5.5 should be returned (1.0.0)');
@@ -77,13 +77,13 @@ class VersionSelectorTest extends TestCase
         $package2 = $this->createPackage('1.1.0-beta');
         $packages = array($package1, $package2);
 
-        $pool = $this->createMockPool();
-        $pool->expects($this->once())
-            ->method('whatProvides')
-            ->with($packageName, null, true)
+        $repositorySet = $this->createMockRepositorySet();
+        $repositorySet->expects($this->once())
+            ->method('findPackages')
+            ->with($packageName, null)
             ->will($this->returnValue($packages));
 
-        $versionSelector = new VersionSelector($pool);
+        $versionSelector = new VersionSelector($repositorySet);
         $best = $versionSelector->findBestCandidate($packageName);
 
         $this->assertSame($package1, $best, 'Latest most stable version should be returned (1.0.0)');
@@ -97,18 +97,18 @@ class VersionSelectorTest extends TestCase
         $package2 = $this->createPackage('2.0.0-beta3');
         $packages = array($package1, $package2);
 
-        $pool = $this->createMockPool();
-        $pool->expects($this->at(0))
-            ->method('whatProvides')
-            ->with($packageName, null, true)
+        $repositorySet = $this->createMockRepositorySet();
+        $repositorySet->expects($this->at(0))
+            ->method('findPackages')
+            ->with($packageName, null)
             ->will($this->returnValue($packages));
 
-        $pool->expects($this->at(1))
-            ->method('whatProvides')
-            ->with($packageName, null, true)
+        $repositorySet->expects($this->at(1))
+            ->method('findPackages')
+            ->with($packageName, null)
             ->will($this->returnValue(array_reverse($packages)));
 
-        $versionSelector = new VersionSelector($pool);
+        $versionSelector = new VersionSelector($repositorySet);
         $best = $versionSelector->findBestCandidate($packageName, null, null);
         $this->assertSame($package2, $best, 'Expecting 2.0.0-beta3, cause beta is more stable than dev');
 
@@ -124,13 +124,13 @@ class VersionSelectorTest extends TestCase
         $package2 = $this->createPackage('1.1.0-beta');
         $packages = array($package1, $package2);
 
-        $pool = $this->createMockPool();
-        $pool->expects($this->once())
-            ->method('whatProvides')
-            ->with($packageName, null, true)
+        $repositorySet = $this->createMockRepositorySet();
+        $repositorySet->expects($this->once())
+            ->method('findPackages')
+            ->with($packageName, null)
             ->will($this->returnValue($packages));
 
-        $versionSelector = new VersionSelector($pool);
+        $versionSelector = new VersionSelector($repositorySet);
         $best = $versionSelector->findBestCandidate($packageName, null, null, 'dev');
 
         $this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)');
@@ -145,13 +145,13 @@ class VersionSelectorTest extends TestCase
         $package3 = $this->createPackage('1.2.0-alpha');
         $packages = array($package1, $package2, $package3);
 
-        $pool = $this->createMockPool();
-        $pool->expects($this->once())
-            ->method('whatProvides')
-            ->with($packageName, null, true)
+        $repositorySet = $this->createMockRepositorySet();
+        $repositorySet->expects($this->once())
+            ->method('findPackages')
+            ->with($packageName, null)
             ->will($this->returnValue($packages));
 
-        $versionSelector = new VersionSelector($pool);
+        $versionSelector = new VersionSelector($repositorySet);
         $best = $versionSelector->findBestCandidate($packageName, null, null, 'beta');
 
         $this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)');
@@ -165,13 +165,13 @@ class VersionSelectorTest extends TestCase
         $package3 = $this->createPackage('1.2.0-alpha');
         $packages = array($package2, $package3);
 
-        $pool = $this->createMockPool();
-        $pool->expects($this->once())
-            ->method('whatProvides')
-            ->with($packageName, null, true)
+        $repositorySet = $this->createMockRepositorySet();
+        $repositorySet->expects($this->once())
+            ->method('findPackages')
+            ->with($packageName, null)
             ->will($this->returnValue($packages));
 
-        $versionSelector = new VersionSelector($pool);
+        $versionSelector = new VersionSelector($repositorySet);
         $best = $versionSelector->findBestCandidate($packageName, null, null, 'stable');
 
         $this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)');
@@ -179,12 +179,12 @@ class VersionSelectorTest extends TestCase
 
     public function testFalseReturnedOnNoPackages()
     {
-        $pool = $this->createMockPool();
-        $pool->expects($this->once())
-            ->method('whatProvides')
+        $repositorySet = $this->createMockRepositorySet();
+        $repositorySet->expects($this->once())
+            ->method('findPackages')
             ->will($this->returnValue(array()));
 
-        $versionSelector = new VersionSelector($pool);
+        $versionSelector = new VersionSelector($repositorySet);
         $best = $versionSelector->findBestCandidate('foobaz');
         $this->assertFalse($best, 'No versions are available returns false');
     }
@@ -194,8 +194,8 @@ class VersionSelectorTest extends TestCase
      */
     public function testFindRecommendedRequireVersion($prettyVersion, $isDev, $stability, $expectedVersion, $branchAlias = null)
     {
-        $pool = $this->createMockPool();
-        $versionSelector = new VersionSelector($pool);
+        $repositorySet = $this->createMockRepositorySet();
+        $versionSelector = new VersionSelector($repositorySet);
         $versionParser = new VersionParser();
 
         $package = $this->getMockBuilder('\Composer\Package\PackageInterface')->getMock();
@@ -273,8 +273,10 @@ class VersionSelectorTest extends TestCase
         return new Package('foo', $parser->normalize($version), $version);
     }
 
-    private function createMockPool()
+    private function createMockRepositorySet()
     {
-        return $this->getMockBuilder('Composer\DependencyResolver\Pool')->getMock();
+        return $this->getMockBuilder('Composer\Repository\RepositorySet')
+            ->disableOriginalConstructor()
+            ->getMock();
     }
 }

+ 1 - 1
tests/Composer/Test/Plugin/Fixtures/plugin-v1/composer.json

@@ -7,6 +7,6 @@
         "class": "Installer\\Plugin"
     },
     "require": {
-        "composer-plugin-api": "^1.0"
+        "composer-plugin-api": "^2.0"
     }
 }

+ 1 - 1
tests/Composer/Test/Plugin/Fixtures/plugin-v2/composer.json

@@ -7,6 +7,6 @@
         "class": "Installer\\Plugin2"
     },
     "require": {
-        "composer-plugin-api": "^1.0"
+        "composer-plugin-api": "^2.0"
     }
 }

+ 1 - 1
tests/Composer/Test/Plugin/Fixtures/plugin-v3/composer.json

@@ -7,6 +7,6 @@
         "class": "Installer\\Plugin2"
     },
     "require": {
-        "composer-plugin-api": "^1.0"
+        "composer-plugin-api": "^2.0"
     }
 }

+ 1 - 1
tests/Composer/Test/Plugin/Fixtures/plugin-v4/composer.json

@@ -10,6 +10,6 @@
         ]
     },
     "require": {
-        "composer-plugin-api": "^1.0"
+        "composer-plugin-api": "^2.0"
     }
 }

+ 1 - 1
tests/Composer/Test/Plugin/Fixtures/plugin-v8/composer.json

@@ -9,6 +9,6 @@
         ]
     },
     "require": {
-        "composer-plugin-api": "1.1.0"
+        "composer-plugin-api": "2.0.0"
     }
 }

+ 1 - 1
tests/Composer/Test/Plugin/Fixtures/plugin-v9/composer.json

@@ -7,6 +7,6 @@
         "class": "Installer\\Plugin"
     },
     "require": {
-        "composer-plugin-api": "^1.0"
+        "composer-plugin-api": "^2.0"
     }
 }

+ 11 - 11
tests/Composer/Test/Plugin/PluginInstallerTest.php

@@ -130,7 +130,7 @@ class PluginInstallerTest extends TestCase
     public function testInstallNewPlugin()
     {
         $this->repository
-            ->expects($this->exactly(2))
+            ->expects($this->once())
             ->method('getPackages')
             ->will($this->returnValue(array()));
         $installer = new PluginInstaller($this->io, $this->composer);
@@ -145,7 +145,7 @@ class PluginInstallerTest extends TestCase
     public function testInstallMultiplePlugins()
     {
         $this->repository
-            ->expects($this->exactly(2))
+            ->expects($this->once())
             ->method('getPackages')
             ->will($this->returnValue(array($this->packages[3])));
         $installer = new PluginInstaller($this->io, $this->composer);
@@ -163,7 +163,7 @@ class PluginInstallerTest extends TestCase
     public function testUpgradeWithNewClassName()
     {
         $this->repository
-            ->expects($this->exactly(3))
+            ->expects($this->once())
             ->method('getPackages')
             ->will($this->returnValue(array($this->packages[0])));
         $this->repository
@@ -182,7 +182,7 @@ class PluginInstallerTest extends TestCase
     public function testUpgradeWithSameClassName()
     {
         $this->repository
-            ->expects($this->exactly(3))
+            ->expects($this->once())
             ->method('getPackages')
             ->will($this->returnValue(array($this->packages[1])));
         $this->repository
@@ -201,7 +201,7 @@ class PluginInstallerTest extends TestCase
     public function testRegisterPluginOnlyOneTime()
     {
         $this->repository
-            ->expects($this->exactly(2))
+            ->expects($this->once())
             ->method('getPackages')
             ->will($this->returnValue(array()));
         $installer = new PluginInstaller($this->io, $this->composer);
@@ -240,11 +240,11 @@ class PluginInstallerTest extends TestCase
 
         // Add the plugins to the repo along with the internal Plugin package on which they all rely.
         $this->repository
-             ->expects($this->any())
-             ->method('getPackages')
-             ->will($this->returnCallback(function () use ($plugApiInternalPackage, $plugins) {
-                 return array_merge(array($plugApiInternalPackage), $plugins);
-             }));
+            ->expects($this->any())
+            ->method('getPackages')
+            ->will($this->returnCallback(function () use ($plugApiInternalPackage, $plugins) {
+                return array_merge(array($plugApiInternalPackage), $plugins);
+            }));
 
         $this->pm->loadInstalledPlugins();
     }
@@ -300,7 +300,7 @@ class PluginInstallerTest extends TestCase
     public function testCommandProviderCapability()
     {
         $this->repository
-            ->expects($this->exactly(2))
+            ->expects($this->once())
             ->method('getPackages')
             ->will($this->returnValue(array($this->packages[7])));
         $installer = new PluginInstaller($this->io, $this->composer);

+ 6 - 6
tests/Composer/Test/Repository/ComposerRepositoryTest.php

@@ -142,11 +142,6 @@ class ComposerRepositoryTest extends TestCase
                 ),
             )));
 
-        $pool = $this->getMockBuilder('Composer\DependencyResolver\Pool')->getMock();
-        $pool->expects($this->any())
-            ->method('isPackageAcceptable')
-            ->will($this->returnValue(true));
-
         $versionParser = new VersionParser();
         $repo->setRootAliases(array(
             'a' => array(
@@ -155,7 +150,7 @@ class ComposerRepositoryTest extends TestCase
             ),
         ));
 
-        $packages = $repo->whatProvides($pool, 'a');
+        $packages = $repo->whatProvides('a', false, array($this, 'isPackageAcceptableReturnTrue'));
 
         $this->assertCount(7, $packages);
         $this->assertEquals(array('1', '1-alias', '2', '2-alias', '2-root', '3', '3-root'), array_keys($packages));
@@ -164,6 +159,11 @@ class ComposerRepositoryTest extends TestCase
         $this->assertSame($packages['2'], $packages['2-alias']->getAliasOf());
     }
 
+    public function isPackageAcceptableReturnTrue()
+    {
+        return true;
+    }
+
     public function testSearchWithType()
     {
         $repoConfig = array(