diff --git a/src/Composer/DependencyResolver/Operation/MarkAliasInstalledOperation.php b/src/Composer/DependencyResolver/Operation/MarkAliasInstalledOperation.php new file mode 100644 index 000000000..3846dd52c --- /dev/null +++ b/src/Composer/DependencyResolver/Operation/MarkAliasInstalledOperation.php @@ -0,0 +1,66 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver\Operation; + +use Composer\Package\AliasPackage; + +/** + * Solver install operation. + * + * @author Konstantin Kudryashov + */ +class MarkAliasInstalledOperation extends SolverOperation +{ + protected $package; + + /** + * Initializes operation. + * + * @param PackageInterface $package package instance + * @param string $reason operation reason + */ + public function __construct(AliasPackage $package, $reason = null) + { + parent::__construct($reason); + + $this->package = $package; + } + + /** + * Returns package instance. + * + * @return PackageInterface + */ + public function getPackage() + { + return $this->package; + } + + /** + * Returns job type. + * + * @return string + */ + public function getJobType() + { + return 'markAliasInstalled'; + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + return 'Marking '.$this->package->getPrettyName().' ('.$this->package->getPrettyVersion().') as installed, alias of '.$this->package->getAliasOf()->getPrettyName().' ('.$this->package->getAliasOf()->getPrettyVersion().')'; + } +} diff --git a/src/Composer/DependencyResolver/Operation/MarkAliasUninstalledOperation.php b/src/Composer/DependencyResolver/Operation/MarkAliasUninstalledOperation.php new file mode 100644 index 000000000..648356df5 --- /dev/null +++ b/src/Composer/DependencyResolver/Operation/MarkAliasUninstalledOperation.php @@ -0,0 +1,66 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver\Operation; + +use Composer\Package\AliasPackage; + +/** + * Solver install operation. + * + * @author Nils Adermann + */ +class MarkAliasUninstalledOperation extends SolverOperation +{ + protected $package; + + /** + * Initializes operation. + * + * @param PackageInterface $package package instance + * @param string $reason operation reason + */ + public function __construct(AliasPackage $package, $reason = null) + { + parent::__construct($reason); + + $this->package = $package; + } + + /** + * Returns package instance. + * + * @return PackageInterface + */ + public function getPackage() + { + return $this->package; + } + + /** + * Returns job type. + * + * @return string + */ + public function getJobType() + { + return 'markAliasUninstalled'; + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + return 'Marking '.$this->package->getPrettyName().' ('.$this->package->getPrettyVersion().') as uninstalled, alias of '.$this->package->getAliasOf()->getPrettyName().' ('.$this->package->getAliasOf()->getPrettyVersion().')'; + } +} diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index cd674ef0f..3b9ffcc28 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -29,6 +29,7 @@ class Rule const RULE_PACKAGE_SAME_NAME = 10; const RULE_PACKAGE_IMPLICIT_OBSOLETES = 11; const RULE_LEARNED = 12; + const RULE_PACKAGE_ALIAS = 13; protected $disabled; protected $literals; @@ -235,6 +236,8 @@ class Rule return $ruleText; case self::RULE_LEARNED: return 'learned: '.$ruleText; + case self::RULE_PACKAGE_ALIAS: + return $ruleText; } } diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index afa8f6996..66c17f7fa 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -14,6 +14,7 @@ namespace Composer\DependencyResolver; use Composer\Repository\RepositoryInterface; use Composer\Package\PackageInterface; +use Composer\Package\AliasPackage; use Composer\DependencyResolver\Operation; /** @@ -255,8 +256,10 @@ class Solver continue; } - $reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES; - $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link)); + if (!$this->obsoleteImpossibleForAlias($package, $provider)) { + $reason = ($isInstalled) ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES; + $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link)); + } } } @@ -271,17 +274,31 @@ class Solver continue; } - if ($isInstalled && !isset($this->installedMap[$provider->getId()])) { - continue; + if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) { + $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, (string) $package)); + } else if (!$this->obsoleteImpossibleForAlias($package, $provider)) { + $reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES; + $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, (string) $package)); } - - $reason = ($package->getName() == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES; - $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, (string) $package)); } } } } + protected function obsoleteImpossibleForAlias($package, $provider) + { + $packageIsAlias = $package instanceof AliasPackage; + $providerIsAlias = $provider instanceof AliasPackage; + + $impossible = ( + ($packageIsAlias && $package->getAliasOf() === $provider) || + ($providerIsAlias && $provider->getAliasOf() === $package) || + ($packageIsAlias && $providerIsAlias && $provider->getAliasOf() === $package->getAliasOf()) + ); + + return $impossible; + } + /** * Adds all rules for all update packages of a given package * @@ -646,7 +663,15 @@ class Solver } if ($literal->isWanted()) { + if ($package instanceof AliasPackage) { + $transaction[] = new Operation\MarkAliasInstalledOperation( + $package, $this->decisionQueueWhy[$i] + ); + continue; + } + if (isset($installMeansUpdateMap[$literal->getPackageId()])) { + $source = $installMeansUpdateMap[$literal->getPackageId()]; $transaction[] = new Operation\UpdateOperation( @@ -662,9 +687,47 @@ class Solver ); } } else if (!isset($ignoreRemove[$package->getId()])) { - $transaction[] = new Operation\UninstallOperation( - $package, $this->decisionQueueWhy[$i] - ); + if ($package instanceof AliasPackage) { + $transaction[] = new Operation\MarkAliasInstalledOperation( + $package, $this->decisionQueueWhy[$i] + ); + } else { + $transaction[] = new Operation\UninstallOperation( + $package, $this->decisionQueueWhy[$i] + ); + } + } + } + + $allDecidedMap = $this->decisionMap; + foreach ($this->decisionMap as $packageId => $decision) { + if ($decision != 0) { + $package = $this->pool->packageById($packageId); + if ($package instanceof AliasPackage) { + $allDecidedMap[$package->getAliasOf()->getId()] = $decision; + } + } + } + + foreach ($allDecidedMap as $packageId => $decision) { + if ($packageId === 0) { + continue; + } + + if (0 == $decision && isset($this->installedMap[$packageId])) { + $package = $this->pool->packageById($packageId); + + if ($package instanceof AliasPackage) { + $transaction[] = new Operation\MarkAliasInstalledOperation( + $package, null + ); + } else { + $transaction[] = new Operation\UninstallOperation( + $package, null + ); + } + + $this->decisionMap[$packageId] = -1; } } @@ -674,9 +737,19 @@ class Solver } if (0 == $decision && isset($this->installedMap[$packageId])) { - $transaction[] = new Operation\UninstallOperation( - $this->pool->packageById($packageId), null - ); + $package = $this->pool->packageById($packageId); + + if ($package instanceof AliasPackage) { + $transaction[] = new Operation\MarkAliasInstalledOperation( + $package, null + ); + } else { + $transaction[] = new Operation\UninstallOperation( + $package, null + ); + } + + $this->decisionMap[$packageId] = -1; } } @@ -733,7 +806,7 @@ class Solver protected function decisionsSatisfy(Literal $l) { return ($l->isWanted() && $this->decisionMap[$l->getPackageId()] > 0) || - (!$l->isWanted() && $this->decisionMap[$l->getPackageId()] <= 0); + (!$l->isWanted() && $this->decisionMap[$l->getPackageId()] < 0); } protected function decisionsConflict(Literal $l) @@ -876,11 +949,6 @@ class Solver break; } - /** TODO: implement recommendations - *if (v > 0 && solv->recommendations.count && v == solv->recommendations.elements[solv->recommendations.count - 1]) - * solv->recommendations.count--; - */ - $this->decisionMap[$literal->getPackageId()] = 0; array_pop($this->decisionQueue); array_pop($this->decisionQueueWhy); @@ -1060,14 +1128,19 @@ class Solver $l1num++; $l1retry = true; } - - $rule = $this->decisionQueueWhy[$decisionId]; } + + $rule = $this->decisionQueueWhy[$decisionId]; } $why = count($this->learnedPool) - 1; - assert($learnedLiterals[0] !== null); - $newRule = new Rule($learnedLiterals, Rule::RULE_LEARNED, $why); + + if (count($learnedLiterals) === 1 && $learnedLiterals[0] === null) { + $newRule = new Rule(array(), Rule::RULE_LEARNED, $why); + } else { + assert($learnedLiterals[0] !== null); + $newRule = new Rule($learnedLiterals, Rule::RULE_LEARNED, $why); + } return array($learnedLiterals[0], $ruleLevel, $newRule, $why); } @@ -1178,7 +1251,7 @@ class Solver $this->disableProblem($why); $this->resetSolver(); - return true; + return 1; } if ($disableRules) { @@ -1191,10 +1264,10 @@ class Solver } $this->resetSolver(); - return true; + return 1; } - return false; + return 0; } private function disableProblem($why) @@ -1266,8 +1339,8 @@ class Solver // * here's the main loop: // * 1) propagate new decisions (only needed once) // * 2) fulfill jobs - // * 4) fulfill all unresolved rules - // * 6) minimalize solution if we had choices + // * 3) fulfill all unresolved rules + // * 4) minimalize solution if we had choices // * if we encounter a problem, we rewind to a safe level and restart // * with step 1 // */ @@ -1403,18 +1476,14 @@ class Solver return; } - // open suse sat-solver uses this, but why is $level == 1 trouble? - // SYSTEMSOLVABLE related? we don't have that, so should work - //if ($level < $systemLevel || $level == 1) { - - if ($level < $systemLevel) { - break; // trouble - } - // something changed, so look at all rules again $n = -1; } + if ($level < $systemLevel) { + continue; + } + // minimization step if (count($this->branches)) { diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 772c7bc3c..08d56819d 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -326,14 +326,6 @@ class Installer } } - // anti-alias local repository to allow updates to work fine - foreach ($localRepo->getPackages() as $package) { - if ($package instanceof AliasPackage) { - $package->getRepository()->addPackage(clone $package->getAliasOf()); - $package->getRepository()->removePackage($package); - } - } - // execute operations if (!$operations) { $this->io->write('Nothing to install or update'); @@ -356,7 +348,10 @@ class Installer } if (!$this->dryRun) { - $this->eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType())), $operation); + $event = 'Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType()); + if (defined($event)) { + $this->eventDispatcher->dispatchPackageEvent(constant($event), $operation); + } // if installing from lock, restore dev packages' references to their locked state if ($installFromLock) { @@ -378,17 +373,15 @@ class Installer } $this->installationManager->execute($localRepo, $operation); - $this->eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation); + $event = 'Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType()); + if (defined($event)) { + $this->eventDispatcher->dispatchPackageEvent(constant($event), $operation); + } $localRepo->write(); } } - // reload local repository for the dev pass to work ok with aliases since it was anti-aliased above - if (!$devMode) { - $localRepo->reload(); - } - return true; } diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 32587a8db..81891b5e1 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -21,6 +21,8 @@ use Composer\DependencyResolver\Operation\OperationInterface; use Composer\DependencyResolver\Operation\InstallOperation; use Composer\DependencyResolver\Operation\UpdateOperation; use Composer\DependencyResolver\Operation\UninstallOperation; +use Composer\DependencyResolver\Operation\MarkAliasInstalledOperation; +use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation; use Composer\Util\Filesystem; /** @@ -104,6 +106,10 @@ class InstallationManager */ public function isPackageInstalled(InstalledRepositoryInterface $repo, PackageInterface $package) { + if ($package instanceof AliasPackage) { + return $repo->hasPackage($package); + } + return $this->getInstaller($package->getType())->isInstalled($repo, $package); } @@ -127,7 +133,7 @@ class InstallationManager */ public function install(RepositoryInterface $repo, InstallOperation $operation) { - $package = $this->antiAlias($operation->getPackage()); + $package = $operation->getPackage(); $installer = $this->getInstaller($package->getType()); $installer->install($repo, $package); $this->notifyInstall($package); @@ -141,8 +147,8 @@ class InstallationManager */ public function update(RepositoryInterface $repo, UpdateOperation $operation) { - $initial = $this->antiAlias($operation->getInitialPackage()); - $target = $this->antiAlias($operation->getTargetPackage()); + $initial = $operation->getInitialPackage(); + $target = $operation->getTargetPackage(); $initialType = $initial->getType(); $targetType = $target->getType(); @@ -165,11 +171,41 @@ class InstallationManager */ public function uninstall(RepositoryInterface $repo, UninstallOperation $operation) { - $package = $this->antiAlias($operation->getPackage()); + $package = $operation->getPackage(); $installer = $this->getInstaller($package->getType()); $installer->uninstall($repo, $package); } + /** + * Executes markAliasInstalled operation. + * + * @param RepositoryInterface $repo repository in which to check + * @param MarkAliasInstalledOperation $operation operation instance + */ + public function markAliasInstalled(RepositoryInterface $repo, MarkAliasInstalledOperation $operation) + { + $package = $operation->getPackage(); + + if (!$repo->hasPackage($package)) { + $repo->addPackage(clone $package); + } + + $this->notifyInstall($package); + } + + /** + * Executes markAlias operation. + * + * @param RepositoryInterface $repo repository in which to check + * @param MarkAliasUninstalledOperation $operation operation instance + */ + public function markAliasUninstalled(RepositoryInterface $repo, MarkAliasUninstalledOperation $operation) + { + $package = $operation->getPackage(); + + $repo->removePackage($package); + } + /** * Returns the installation path of a package * @@ -203,17 +239,4 @@ class InstallationManager $package->getRepository()->notifyInstall($package); } } - - private function antiAlias(PackageInterface $package) - { - if ($package instanceof AliasPackage) { - $alias = $package; - $package = $package->getAliasOf(); - $package->setInstalledAsAlias(true); - $package->setAlias($alias->getVersion()); - $package->setPrettyAlias($alias->getPrettyVersion()); - } - - return $package; - } } diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index ed25e656b..08eb9c32e 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -41,8 +41,9 @@ class ArrayDumper $data = array(); $data['name'] = $package->getPrettyName(); + $data['version'] = $package->getPrettyVersion(); - $data['version_normalized'] = $package->getVersion(); + $data['version_normalized'] = $package->getVersion();; if ($package->getTargetDir()) { $data['target-dir'] = $package->getTargetDir(); diff --git a/src/Composer/Package/Locker.php b/src/Composer/Package/Locker.php index a7b5bdcd1..44247c5b8 100644 --- a/src/Composer/Package/Locker.php +++ b/src/Composer/Package/Locker.php @@ -89,7 +89,7 @@ class Locker $repo = $dev ? $this->repositoryManager->getLocalDevRepository() : $this->repositoryManager->getLocalRepository(); foreach ($lockedPackages as $info) { - $resolvedVersion = !empty($info['alias']) ? $info['alias'] : $info['version']; + $resolvedVersion = !empty($info['alias-version']) ? $info['alias-version'] : $info['version']; // try to find the package in the local repo (best match) $package = $repo->findPackage($info['package'], $resolvedVersion); @@ -100,10 +100,10 @@ class Locker } // try to find the package in any repo (second pass without alias + rebuild alias since it disappeared) - if (!$package && !empty($info['alias'])) { + if (!$package && !empty($info['alias-version'])) { $package = $this->repositoryManager->findPackage($info['package'], $info['version']); if ($package) { - $alias = new AliasPackage($package, $info['alias'], $info['alias']); + $alias = new AliasPackage($package, $info['alias-version'], $info['alias-pretty-version']); $package->getRepository()->addPackage($alias); $package = $alias; } @@ -179,7 +179,10 @@ class Locker $locked = array(); foreach ($packages as $package) { + $alias = null; + if ($package instanceof AliasPackage) { + $alias = $package; $package = $package->getAliasOf(); } @@ -198,8 +201,9 @@ class Locker $spec['source-reference'] = $package->getSourceReference(); } - if ($package->getAlias() && $package->isInstalledAsAlias()) { - $spec['alias'] = $package->getAlias(); + if ($alias) { + $spec['alias-pretty-version'] = $alias->getPrettyVersion(); + $spec['alias-version'] = $alias->getVersion(); } $locked[] = $spec; diff --git a/src/Composer/Package/MemoryPackage.php b/src/Composer/Package/MemoryPackage.php index 4a8f55cde..147f94ed7 100644 --- a/src/Composer/Package/MemoryPackage.php +++ b/src/Composer/Package/MemoryPackage.php @@ -46,7 +46,6 @@ class MemoryPackage extends BasePackage protected $aliases = array(); protected $alias; protected $prettyAlias; - protected $installedAsAlias; protected $dev; protected $requires = array(); @@ -211,24 +210,6 @@ class MemoryPackage extends BasePackage return $this->prettyAlias; } - /** - * Enabled if the package is installed from its alias package - * - * @param string $installedAsAlias - */ - public function setInstalledAsAlias($installedAsAlias) - { - $this->installedAsAlias = $installedAsAlias; - } - - /** - * @return string - */ - public function isInstalledAsAlias() - { - return $this->installedAsAlias; - } - /** * {@inheritDoc} */ diff --git a/src/Composer/Repository/ArrayRepository.php b/src/Composer/Repository/ArrayRepository.php index 7a6336393..e75e3b18a 100644 --- a/src/Composer/Repository/ArrayRepository.php +++ b/src/Composer/Repository/ArrayRepository.php @@ -103,8 +103,8 @@ class ArrayRepository implements RepositoryInterface $package->setRepository($this); $this->packages[] = $package; - // create alias package on the fly if needed (installed repos manage aliases themselves) - if ($package->getAlias() && !$this instanceof InstalledRepositoryInterface) { + // create alias package on the fly if needed + if ($package->getAlias()) { $this->addPackage($this->createAliasPackage($package)); } } diff --git a/src/Composer/Repository/FilesystemRepository.php b/src/Composer/Repository/FilesystemRepository.php index 8bbeb6ad3..973e56062 100644 --- a/src/Composer/Repository/FilesystemRepository.php +++ b/src/Composer/Repository/FilesystemRepository.php @@ -14,6 +14,7 @@ namespace Composer\Repository; use Composer\Json\JsonFile; use Composer\Package\PackageInterface; +use Composer\Package\AliasPackage; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Dumper\ArrayDumper; @@ -54,21 +55,32 @@ class FilesystemRepository extends ArrayRepository implements WritableRepository throw new \UnexpectedValueException('Could not parse package list from the '.$this->file->getPath().' repository'); } + $aliases = array(); + $loader = new ArrayLoader(); foreach ($packages as $packageData) { $package = $loader->load($packageData); - // package was installed as alias, so we only add the alias - if ($this instanceof InstalledRepositoryInterface && !empty($packageData['installed-as-alias'])) { - $alias = $packageData['installed-as-alias']; - $package->setAlias($alias); - $package->setPrettyAlias($alias); - $package->setInstalledAsAlias(true); - $this->addPackage($this->createAliasPackage($package, $alias, $alias)); - } else { - // only add regular package - if it's not an installed repo the alias will be created on the fly - $this->addPackage($package); + // aliases need to be looked up in the end to set up references correctly + if ($this instanceof InstalledRepositoryInterface && !empty($packageData['alias'])) { + $aliases[] = array( + 'package' => $package, + 'alias' => $packageData['alias'], + 'alias_pretty' => $packageData['alias_pretty'] + ); } + + $this->addPackage($package); + } + + foreach ($aliases as $aliasData) { + $temporaryPackage = $aliasData['package']; + + $package = $this->findPackage($package->getName(), $package->getVersion()); + + $package->setAlias($aliasData['alias']); + $package->setPrettyAlias($aliasData['alias_pretty']); + $this->addPackage($this->createAliasPackage($package, $aliasData['alias'], $aliasData['alias_pretty'])); } } @@ -86,11 +98,10 @@ class FilesystemRepository extends ArrayRepository implements WritableRepository $packages = array(); $dumper = new ArrayDumper(); foreach ($this->getPackages() as $package) { - $data = $dumper->dump($package); - if ($this instanceof InstalledRepositoryInterface && $package->isInstalledAsAlias()) { - $data['installed-as-alias'] = $package->getAlias(); + if (!$package instanceof AliasPackage) { + $data = $dumper->dump($package); + $packages[] = $data; } - $packages[] = $data; } $this->file->write($packages); diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 2bc5a928a..d361b66b0 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -670,6 +670,55 @@ class SolverTest extends TestCase )); } + public function testInstallRecursiveAliasDependencies() + { + $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); + $this->repo->addPackage($packageB = $this->getPackage('B', '2.0')); + $this->repo->addPackage($packageA2 = $this->getPackage('A', '2.0')); + + $packageA2->setRequires(array( + new Link('A', 'B', $this->getVersionConstraint('==', '2.0'), 'requires', '== 2.0'), + )); + $packageB->setRequires(array( + new Link('B', 'A', $this->getVersionConstraint('>=', '2.0'), 'requires'), + )); + + $this->repo->addPackage($packageA2Alias = $this->getAliasPackage($packageA2, '1.1')); + + $this->reposComplete(); + + $this->request->install('A', $this->getVersionConstraint('==', '1.1.0.0')); + + $this->checkSolverResult(array( + array('job' => 'install', 'package' => $packageB), + array('job' => 'install', 'package' => $packageA2), + array('job' => 'install', 'package' => $packageA2Alias), + )); + } + + public function testInstallDevAlias() + { + $this->repo->addPackage($packageA = $this->getPackage('A', '2.0')); + $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); + + $packageB->setRequires(array( + new Link('B', 'A', $this->getVersionConstraint('<', '2.0'), 'requires'), + )); + + $this->repo->addPackage($packageAAlias = $this->getAliasPackage($packageA, '1.1')); + + $this->reposComplete(); + + $this->request->install('A', $this->getVersionConstraint('==', '2.0')); + $this->request->install('B'); + + $this->checkSolverResult(array( + array('job' => 'install', 'package' => $packageAAlias), + array('job' => 'install', 'package' => $packageB), + array('job' => 'install', 'package' => $packageA), + )); + } + protected function reposComplete() { $this->pool->addRepository($this->repoInstalled); diff --git a/tests/Composer/Test/TestCase.php b/tests/Composer/Test/TestCase.php index 3756e9fa3..bbceb3875 100644 --- a/tests/Composer/Test/TestCase.php +++ b/tests/Composer/Test/TestCase.php @@ -14,6 +14,7 @@ namespace Composer\Test; use Composer\Package\Version\VersionParser; use Composer\Package\MemoryPackage; +use Composer\Package\AliasPackage; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Util\Filesystem; @@ -45,6 +46,12 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase return new MemoryPackage($name, $normVersion, $version); } + protected function getAliasPackage($package, $version) + { + $normVersion = self::getVersionParser()->normalize($version); + return new AliasPackage($package, $normVersion, $version); + } + protected function ensureDirectoryExistsAndClear($directory) { $fs = new Filesystem();