Browse Source

Merge pull request #321 from naderman/error-reporting

Basic error reporting for solver problems
Jordi Boggiano 13 years ago
parent
commit
c97a33577b

+ 11 - 2
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();
     }
 
@@ -1113,6 +1117,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 +1129,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 +1174,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)
         );
     }
 

+ 65 - 0
src/Composer/DependencyResolver/SolverProblemsException.php

@@ -0,0 +1,65 @@
+<?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;
+
+/**
+ * @author Nils Adermann <naderman@naderman.de>
+ */
+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;
+    }
+}

+ 44 - 1
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,49 @@ 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) {
+            // @todo: assert problem properties
+        }
+    }
+
+    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) {
+            // @todo: assert problem properties
+        }
+    }
+
     protected function reposComplete()
     {
         $this->pool->addRepository($this->repoInstalled);
@@ -513,5 +557,4 @@ class SolverTest extends TestCase
 
         $this->assertEquals($expected, $result);
     }
-
 }