From 8fd4e560292c68db8322b2a5fe3d14c083f86551 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 00:13:21 +0100 Subject: [PATCH 1/3] Deciding to install a package and wanting to install it, is not a conflict --- src/Composer/DependencyResolver/Solver.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 9ff6b0764..281685a34 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -1113,6 +1113,8 @@ class Solver protected function addDecision(Literal $l, $level) { + assert($this->decisionMap[$l->getPackageId()] == 0); + if ($l->isWanted()) { $this->decisionMap[$l->getPackageId()] = $level; } else { @@ -1123,6 +1125,9 @@ class Solver protected function addDecisionId($literalId, $level) { $packageId = abs($literalId); + + assert($this->decisionMap[$packageId] == 0); + if ($literalId > 0) { $this->decisionMap[$packageId] = $level; } else { @@ -1165,8 +1170,8 @@ class Solver { $packageId = abs($literalId); return ( - $this->decisionMap[$packageId] > 0 && !($literalId < 0) || - $this->decisionMap[$packageId] < 0 && $literalId > 0 + ($this->decisionMap[$packageId] > 0 && $literalId < 0) || + ($this->decisionMap[$packageId] < 0 && $literalId > 0) ); } From 52d876e11e65a6b4b3780ac5c6ff1083b916d6aa Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 00:15:23 +0100 Subject: [PATCH 2/3] Add SolverProblemsException and test basic solver failures --- src/Composer/DependencyResolver/Solver.php | 4 ++ .../SolverProblemsException.php | 65 +++++++++++++++++++ .../Test/DependencyResolver/SolverTest.php | 43 +++++++++++- 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/Composer/DependencyResolver/SolverProblemsException.php diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 281685a34..4359f3102 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -1035,6 +1035,10 @@ class Solver //findrecommendedsuggested(solv); //solver_prepare_solutions(solv); + if ($this->problems) { + throw new SolverProblemsException($this->problems, $this->learnedPool); + } + return $this->createTransaction(); } diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php new file mode 100644 index 000000000..cbc4fd571 --- /dev/null +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -0,0 +1,65 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\DependencyResolver; + +/** + * @author Nils Adermann + */ +class SolverProblemsException extends \RuntimeException +{ + protected $problems; + + public function __construct(array $problems, array $learnedPool) + { + $message = ''; + foreach ($problems as $i => $problem) { + $message .= '['; + foreach ($problem as $why) { + + if (is_int($why) && isset($learnedPool[$why])) { + $rules = $learnedPool[$why]; + } else { + $rules = $why; + } + + if (isset($rules['packages'])) { + $message .= $this->jobToText($rules); + } else { + $message .= '('; + foreach ($rules as $rule) { + if ($rule instanceof Rule) { + if ($rule->getType() == RuleSet::TYPE_LEARNED) { + $message .= 'learned: '; + } + $message .= $rule . ', '; + } else { + $message .= 'String(' . $rule . '), '; + } + } + $message .= ')'; + } + $message .= ', '; + } + $message .= "]\n"; + } + + parent::__construct($message); + } + + public function jobToText($job) + { + //$output = serialize($job); + $output = 'Job(cmd='.$job['cmd'].', target='.$job['packageName'].', packages=['.implode(', ', $job['packages']).'])'; + return $output; + } +} diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index a71feafce..bea9d989b 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -19,6 +19,7 @@ use Composer\DependencyResolver\DefaultPolicy; use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Request; use Composer\DependencyResolver\Solver; +use Composer\DependencyResolver\SolverProblemsException; use Composer\Package\Link; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Test\TestCase; @@ -484,6 +485,47 @@ class SolverTest extends TestCase )); } + public function testConflictResultEmpty() + { + $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); + $this->repo->addPackage($packageB = $this->getPackage('B', '1.0'));; + + $packageA->setConflicts(array( + new Link('A', 'B', new VersionConstraint('>=', '1.0'), 'conflicts'), + )); + + $this->reposComplete(); + + $this->request->install('A'); + $this->request->install('B'); + + try { + $transaction = $this->solver->solve($this->request); + $this->fail('Unsolvable conflict did not resolve in exception.'); + } catch (SolverProblemsException $e) { + } + } + + public function testUnsatisfiableRequires() + { + $this->repo->addPackage($packageA = $this->getPackage('A', '1.0')); + $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); + + $packageA->setRequires(array( + new Link('A', 'B', new VersionConstraint('>=', '2.0'), 'requires'), + )); + + $this->reposComplete(); + + $this->request->install('A'); + + try { + $transaction = $this->solver->solve($this->request); + $this->fail('Unsolvable conflict did not resolve in exception.'); + } catch (SolverProblemsException $e) { + } + } + protected function reposComplete() { $this->pool->addRepository($this->repoInstalled); @@ -513,5 +555,4 @@ class SolverTest extends TestCase $this->assertEquals($expected, $result); } - } From e6143d1584a1bd9d344d966b2bd5ebb6b8b175bb Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 19 Feb 2012 00:21:39 +0100 Subject: [PATCH 3/3] Add todos to explain why try/catch is inside the test --- tests/Composer/Test/DependencyResolver/SolverTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index bea9d989b..18cb9f4a1 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -503,6 +503,7 @@ class SolverTest extends TestCase $transaction = $this->solver->solve($this->request); $this->fail('Unsolvable conflict did not resolve in exception.'); } catch (SolverProblemsException $e) { + // @todo: assert problem properties } } @@ -523,6 +524,7 @@ class SolverTest extends TestCase $transaction = $this->solver->solve($this->request); $this->fail('Unsolvable conflict did not resolve in exception.'); } catch (SolverProblemsException $e) { + // @todo: assert problem properties } }