1<?php
2/**
3 * $Id: 65affc464b743a2ef7a41d714b1acb1fa5b8bd3b $
4 *
5 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 *
17 * This software consists of voluntary contributions made by many individuals
18 * and is licensed under the LGPL. For more information please see
19 * <http://phing.info>.
20 */
21
22require_once 'PHPUnit/Autoload.php';
23require_once 'phing/tasks/ext/coverage/CoverageMerger.php';
24require_once 'phing/system/util/Timer.php';
25
26/**
27 * Simple Testrunner for PHPUnit that runs all tests of a testsuite.
28 *
29 * @author Michiel Rook <mrook@php.net>
30 * @version $Id: 65affc464b743a2ef7a41d714b1acb1fa5b8bd3b $
31 * @package phing.tasks.ext.phpunit
32 * @since 2.1.0
33 */
34class PHPUnitTestRunner extends PHPUnit_Runner_BaseTestRunner implements PHPUnit_Framework_TestListener
35{
36    const SUCCESS = 0;
37    const FAILURES = 1;
38    const ERRORS = 2;
39    const INCOMPLETES = 3;
40    const SKIPPED = 4;
41
42    private $retCode = 0;
43    private $lastErrorMessage = '';
44    private $lastFailureMessage = '';
45    private $lastIncompleteMessage = '';
46    private $lastSkippedMessage = '';
47    private $formatters = array();
48
49    private $codecoverage = null;
50
51    private $project = NULL;
52
53    private $groups = array();
54    private $excludeGroups = array();
55
56    private $processIsolation = false;
57
58    private $useCustomErrorHandler = true;
59
60    public function __construct(Project $project, $groups = array(), $excludeGroups = array(), $processIsolation = false)
61    {
62        $this->project = $project;
63        $this->groups = $groups;
64        $this->excludeGroups = $excludeGroups;
65        $this->processIsolation = $processIsolation;
66        $this->retCode = self::SUCCESS;
67    }
68
69    public function setCodecoverage($codecoverage)
70    {
71        $this->codecoverage = $codecoverage;
72    }
73
74    public function setUseCustomErrorHandler($useCustomErrorHandler)
75    {
76        $this->useCustomErrorHandler = $useCustomErrorHandler;
77    }
78
79    public function addFormatter($formatter)
80    {
81        $this->formatters[] = $formatter;
82    }
83
84    public function handleError($level, $message, $file, $line)
85    {
86        return PHPUnit_Util_ErrorHandler::handleError($level, $message, $file, $line);
87    }
88
89    /**
90     * Run a test
91     */
92    public function run(PHPUnit_Framework_TestSuite $suite)
93    {
94        $res = new PHPUnit_Framework_TestResult();
95
96        if ($this->codecoverage)
97        {
98            $whitelist = CoverageMerger::getWhiteList($this->project);
99
100            $this->codecoverage->filter()->addFilesToWhiteList($whitelist);
101
102            $res->setCodeCoverage($this->codecoverage);
103        }
104
105        $res->addListener($this);
106
107        foreach ($this->formatters as $formatter)
108        {
109            $res->addListener($formatter);
110        }
111
112        /* Set PHPUnit error handler */
113        if ($this->useCustomErrorHandler)
114        {
115            $oldErrorHandler = set_error_handler(array($this, 'handleError'), E_ALL | E_STRICT);
116        }
117
118        $suite->run($res, false, $this->groups, $this->excludeGroups, $this->processIsolation);
119
120        foreach ($this->formatters as $formatter)
121        {
122            $formatter->processResult($res);
123        }
124
125        /* Restore Phing error handler */
126        if ($this->useCustomErrorHandler)
127        {
128            restore_error_handler();
129        }
130
131        if ($this->codecoverage)
132        {
133            CoverageMerger::merge($this->project, $this->codecoverage->getData());
134        }
135
136        if ($res->errorCount() != 0)
137        {
138            $this->retCode = self::ERRORS;
139        }
140        else if ($res->failureCount() != 0)
141        {
142            $this->retCode = self::FAILURES;
143        }
144        else if ($res->notImplementedCount() != 0)
145        {
146            $this->retCode = self::INCOMPLETES;
147        }
148        else if ($res->skippedCount() != 0)
149        {
150            $this->retCode = self::SKIPPED;
151        }
152    }
153
154    public function getRetCode()
155    {
156        return $this->retCode;
157    }
158
159    public function getLastErrorMessage()
160    {
161        return $this->lastErrorMessage;
162    }
163
164    public function getLastFailureMessage()
165    {
166        return $this->lastFailureMessage;
167    }
168
169    public function getLastIncompleteMessage()
170    {
171        return $this->lastIncompleteMessage;
172    }
173
174    public function getLastSkippedMessage()
175    {
176        return $this->lastSkippedMessage;
177    }
178
179    protected function composeMessage($message, PHPUnit_Framework_Test $test, Exception $e)
180    {
181        $message = "Test $message (" . $test->getName() . " in class " . get_class($test) . "): " . $e->getMessage();
182
183        if ($e instanceof PHPUnit_Framework_ExpectationFailedException && $e->getComparisonFailure()) {
184            $message .= "\n" . $e->getComparisonFailure()->getDiff();
185        }
186
187        return $message;
188    }
189
190    /**
191     * An error occurred.
192     *
193     * @param  PHPUnit_Framework_Test $test
194     * @param  Exception              $e
195     * @param  float                  $time
196     */
197    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
198    {
199        $this->lastErrorMessage = $this->composeMessage("ERROR", $test, $e);
200    }
201
202    /**
203     * A failure occurred.
204     *
205     * @param  PHPUnit_Framework_Test                 $test
206     * @param  PHPUnit_Framework_AssertionFailedError $e
207     * @param  float                                  $time
208     */
209    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
210    {
211        $this->lastFailureMessage = $this->composeMessage("FAILURE", $test, $e);
212    }
213
214    /**
215     * Incomplete test.
216     *
217     * @param  PHPUnit_Framework_Test $test
218     * @param  Exception              $e
219     * @param  float                  $time
220     */
221    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
222    {
223        $this->lastIncompleteMessage = $this->composeMessage("INCOMPLETE", $test, $e);
224    }
225
226    /**
227     * Skipped test.
228     *
229     * @param  PHPUnit_Framework_Test $test
230     * @param  Exception              $e
231     * @param  float                  $time
232     * @since  Method available since Release 3.0.0
233     */
234    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
235    {
236        $this->lastSkippedMessage = $this->composeMessage("SKIPPED", $test, $e);
237    }
238
239    /**
240     * A test started.
241     *
242     * @param  string  $testName
243     */
244    public function testStarted($testName)
245    {
246    }
247
248    /**
249     * A test ended.
250     *
251     * @param  string  $testName
252     */
253    public function testEnded($testName)
254    {
255    }
256
257    /**
258     * A test failed.
259     *
260     * @param  integer                                 $status
261     * @param  PHPUnit_Framework_Test                 $test
262     * @param  PHPUnit_Framework_AssertionFailedError $e
263     */
264    public function testFailed($status, PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e)
265    {
266    }
267
268    /**
269     * Override to define how to handle a failed loading of
270     * a test suite.
271     *
272     * @param  string  $message
273     */
274    protected function runFailed($message)
275    {
276        throw new BuildException($message);
277    }
278
279    /**
280     * A test suite started.
281     *
282     * @param  PHPUnit_Framework_TestSuite $suite
283     * @since  Method available since Release 2.2.0
284     */
285    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
286    {
287    }
288
289    /**
290     * A test suite ended.
291     *
292     * @param  PHPUnit_Framework_TestSuite $suite
293     * @since  Method available since Release 2.2.0
294     */
295    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
296    {
297    }
298
299    /**
300     * A test started.
301     *
302     * @param  PHPUnit_Framework_Test $test
303     */
304    public function startTest(PHPUnit_Framework_Test $test)
305    {
306    }
307
308    /**
309     * A test ended.
310     *
311     * @param  PHPUnit_Framework_Test $test
312     * @param  float                  $time
313     */
314    public function endTest(PHPUnit_Framework_Test $test, $time)
315    {
316        if ($test instanceof PHPUnit_Framework_TestCase) {
317            if (!$test->hasPerformedExpectationsOnOutput()) {
318                echo $test->getActualOutput();
319            }
320        }
321    }
322}
323
324