1<?php
2/*
3 * This file is part of PHPUnit.
4 *
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11/**
12 * A TestListener that generates a logfile of the
13 * test execution using the Test Anything Protocol (TAP).
14 */
15class PHPUnit_Util_Log_TAP extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
16{
17    /**
18     * @var int
19     */
20    protected $testNumber = 0;
21
22    /**
23     * @var int
24     */
25    protected $testSuiteLevel = 0;
26
27    /**
28     * @var bool
29     */
30    protected $testSuccessful = true;
31
32    /**
33     * Constructor.
34     *
35     * @param mixed $out
36     *
37     * @throws PHPUnit_Framework_Exception
38     */
39    public function __construct($out = null)
40    {
41        parent::__construct($out);
42        $this->write("TAP version 13\n");
43    }
44
45    /**
46     * An error occurred.
47     *
48     * @param PHPUnit_Framework_Test $test
49     * @param Exception              $e
50     * @param float                  $time
51     */
52    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
53    {
54        $this->writeNotOk($test, 'Error');
55    }
56
57    /**
58     * A warning occurred.
59     *
60     * @param PHPUnit_Framework_Test    $test
61     * @param PHPUnit_Framework_Warning $e
62     * @param float                     $time
63     */
64    public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
65    {
66        $this->writeNotOk($test, 'Warning');
67    }
68
69    /**
70     * A failure occurred.
71     *
72     * @param PHPUnit_Framework_Test                 $test
73     * @param PHPUnit_Framework_AssertionFailedError $e
74     * @param float                                  $time
75     */
76    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
77    {
78        $this->writeNotOk($test, 'Failure');
79
80        $message = explode(
81            "\n",
82            PHPUnit_Framework_TestFailure::exceptionToString($e)
83        );
84
85        $diagnostic = [
86          'message'  => $message[0],
87          'severity' => 'fail'
88        ];
89
90        if ($e instanceof PHPUnit_Framework_ExpectationFailedException) {
91            $cf = $e->getComparisonFailure();
92
93            if ($cf !== null) {
94                $diagnostic['data'] = [
95                  'got'      => $cf->getActual(),
96                  'expected' => $cf->getExpected()
97                ];
98            }
99        }
100
101        $yaml = new Symfony\Component\Yaml\Dumper;
102
103        $this->write(
104            sprintf(
105                "  ---\n%s  ...\n",
106                $yaml->dump($diagnostic, 2, 2)
107            )
108        );
109    }
110
111    /**
112     * Incomplete test.
113     *
114     * @param PHPUnit_Framework_Test $test
115     * @param Exception              $e
116     * @param float                  $time
117     */
118    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
119    {
120        $this->writeNotOk($test, '', 'TODO Incomplete Test');
121    }
122
123    /**
124     * Risky test.
125     *
126     * @param PHPUnit_Framework_Test $test
127     * @param Exception              $e
128     * @param float                  $time
129     */
130    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
131    {
132        $this->write(
133            sprintf(
134                "ok %d - # RISKY%s\n",
135                $this->testNumber,
136                $e->getMessage() != '' ? ' ' . $e->getMessage() : ''
137            )
138        );
139
140        $this->testSuccessful = false;
141    }
142
143    /**
144     * Skipped test.
145     *
146     * @param PHPUnit_Framework_Test $test
147     * @param Exception              $e
148     * @param float                  $time
149     */
150    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
151    {
152        $this->write(
153            sprintf(
154                "ok %d - # SKIP%s\n",
155                $this->testNumber,
156                $e->getMessage() != '' ? ' ' . $e->getMessage() : ''
157            )
158        );
159
160        $this->testSuccessful = false;
161    }
162
163    /**
164     * A testsuite started.
165     *
166     * @param PHPUnit_Framework_TestSuite $suite
167     */
168    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
169    {
170        $this->testSuiteLevel++;
171    }
172
173    /**
174     * A testsuite ended.
175     *
176     * @param PHPUnit_Framework_TestSuite $suite
177     */
178    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
179    {
180        $this->testSuiteLevel--;
181
182        if ($this->testSuiteLevel == 0) {
183            $this->write(sprintf("1..%d\n", $this->testNumber));
184        }
185    }
186
187    /**
188     * A test started.
189     *
190     * @param PHPUnit_Framework_Test $test
191     */
192    public function startTest(PHPUnit_Framework_Test $test)
193    {
194        $this->testNumber++;
195        $this->testSuccessful = true;
196    }
197
198    /**
199     * A test ended.
200     *
201     * @param PHPUnit_Framework_Test $test
202     * @param float                  $time
203     */
204    public function endTest(PHPUnit_Framework_Test $test, $time)
205    {
206        if ($this->testSuccessful === true) {
207            $this->write(
208                sprintf(
209                    "ok %d - %s\n",
210                    $this->testNumber,
211                    PHPUnit_Util_Test::describe($test)
212                )
213            );
214        }
215
216        $this->writeDiagnostics($test);
217    }
218
219    /**
220     * @param PHPUnit_Framework_Test $test
221     * @param string                 $prefix
222     * @param string                 $directive
223     */
224    protected function writeNotOk(PHPUnit_Framework_Test $test, $prefix = '', $directive = '')
225    {
226        $this->write(
227            sprintf(
228                "not ok %d - %s%s%s\n",
229                $this->testNumber,
230                $prefix != '' ? $prefix . ': ' : '',
231                PHPUnit_Util_Test::describe($test),
232                $directive != '' ? ' # ' . $directive : ''
233            )
234        );
235
236        $this->testSuccessful = false;
237    }
238
239    /**
240     * @param PHPUnit_Framework_Test $test
241     */
242    private function writeDiagnostics(PHPUnit_Framework_Test $test)
243    {
244        if (!$test instanceof PHPUnit_Framework_TestCase) {
245            return;
246        }
247
248        if (!$test->hasOutput()) {
249            return;
250        }
251
252        foreach (explode("\n", trim($test->getActualOutput())) as $line) {
253            $this->write(
254                sprintf(
255                    "# %s\n",
256                    $line
257                )
258            );
259        }
260    }
261}
262