Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php declare(strict_types=1);
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
namespace PHPUnit\Util\Log;
11
 
12
use function class_exists;
13
use function get_class;
14
use function method_exists;
15
use function sprintf;
16
use function str_replace;
17
use function trim;
18
use DOMDocument;
19
use DOMElement;
20
use PHPUnit\Framework\AssertionFailedError;
21
use PHPUnit\Framework\ExceptionWrapper;
22
use PHPUnit\Framework\SelfDescribing;
23
use PHPUnit\Framework\Test;
24
use PHPUnit\Framework\TestFailure;
25
use PHPUnit\Framework\TestListener;
26
use PHPUnit\Framework\TestSuite;
27
use PHPUnit\Framework\Warning;
28
use PHPUnit\Util\Exception;
29
use PHPUnit\Util\Filter;
30
use PHPUnit\Util\Printer;
31
use PHPUnit\Util\Xml;
32
use ReflectionClass;
33
use ReflectionException;
34
use Throwable;
35
 
36
/**
37
 * @internal This class is not covered by the backward compatibility promise for PHPUnit
38
 */
39
final class JUnit extends Printer implements TestListener
40
{
41
    /**
42
     * @var DOMDocument
43
     */
44
    private $document;
45
 
46
    /**
47
     * @var DOMElement
48
     */
49
    private $root;
50
 
51
    /**
52
     * @var bool
53
     */
54
    private $reportRiskyTests = false;
55
 
56
    /**
57
     * @var DOMElement[]
58
     */
59
    private $testSuites = [];
60
 
61
    /**
62
     * @var int[]
63
     */
64
    private $testSuiteTests = [0];
65
 
66
    /**
67
     * @var int[]
68
     */
69
    private $testSuiteAssertions = [0];
70
 
71
    /**
72
     * @var int[]
73
     */
74
    private $testSuiteErrors = [0];
75
 
76
    /**
77
     * @var int[]
78
     */
79
    private $testSuiteWarnings = [0];
80
 
81
    /**
82
     * @var int[]
83
     */
84
    private $testSuiteFailures = [0];
85
 
86
    /**
87
     * @var int[]
88
     */
89
    private $testSuiteSkipped = [0];
90
 
91
    /**
92
     * @var int[]
93
     */
94
    private $testSuiteTimes = [0];
95
 
96
    /**
97
     * @var int
98
     */
99
    private $testSuiteLevel = 0;
100
 
101
    /**
102
     * @var DOMElement
103
     */
104
    private $currentTestCase;
105
 
106
    /**
107
     * @param null|mixed $out
108
     */
109
    public function __construct($out = null, bool $reportRiskyTests = false)
110
    {
111
        $this->document               = new DOMDocument('1.0', 'UTF-8');
112
        $this->document->formatOutput = true;
113
 
114
        $this->root = $this->document->createElement('testsuites');
115
        $this->document->appendChild($this->root);
116
 
117
        parent::__construct($out);
118
 
119
        $this->reportRiskyTests = $reportRiskyTests;
120
    }
121
 
122
    /**
123
     * Flush buffer and close output.
124
     */
125
    public function flush(): void
126
    {
127
        $this->write($this->getXML());
128
 
129
        parent::flush();
130
    }
131
 
132
    /**
133
     * An error occurred.
134
     */
135
    public function addError(Test $test, Throwable $t, float $time): void
136
    {
137
        $this->doAddFault($test, $t, 'error');
138
        $this->testSuiteErrors[$this->testSuiteLevel]++;
139
    }
140
 
141
    /**
142
     * A warning occurred.
143
     */
144
    public function addWarning(Test $test, Warning $e, float $time): void
145
    {
146
        $this->doAddFault($test, $e, 'warning');
147
        $this->testSuiteWarnings[$this->testSuiteLevel]++;
148
    }
149
 
150
    /**
151
     * A failure occurred.
152
     */
153
    public function addFailure(Test $test, AssertionFailedError $e, float $time): void
154
    {
155
        $this->doAddFault($test, $e, 'failure');
156
        $this->testSuiteFailures[$this->testSuiteLevel]++;
157
    }
158
 
159
    /**
160
     * Incomplete test.
161
     */
162
    public function addIncompleteTest(Test $test, Throwable $t, float $time): void
163
    {
164
        $this->doAddSkipped();
165
    }
166
 
167
    /**
168
     * Risky test.
169
     */
170
    public function addRiskyTest(Test $test, Throwable $t, float $time): void
171
    {
172
        if (!$this->reportRiskyTests) {
173
            return;
174
        }
175
 
176
        $this->doAddFault($test, $t, 'error');
177
        $this->testSuiteErrors[$this->testSuiteLevel]++;
178
    }
179
 
180
    /**
181
     * Skipped test.
182
     */
183
    public function addSkippedTest(Test $test, Throwable $t, float $time): void
184
    {
185
        $this->doAddSkipped();
186
    }
187
 
188
    /**
189
     * A testsuite started.
190
     */
191
    public function startTestSuite(TestSuite $suite): void
192
    {
193
        $testSuite = $this->document->createElement('testsuite');
194
        $testSuite->setAttribute('name', $suite->getName());
195
 
196
        if (class_exists($suite->getName(), false)) {
197
            try {
198
                $class = new ReflectionClass($suite->getName());
199
 
200
                $testSuite->setAttribute('file', $class->getFileName());
201
            } catch (ReflectionException $e) {
202
            }
203
        }
204
 
205
        if ($this->testSuiteLevel > 0) {
206
            $this->testSuites[$this->testSuiteLevel]->appendChild($testSuite);
207
        } else {
208
            $this->root->appendChild($testSuite);
209
        }
210
 
211
        $this->testSuiteLevel++;
212
        $this->testSuites[$this->testSuiteLevel]          = $testSuite;
213
        $this->testSuiteTests[$this->testSuiteLevel]      = 0;
214
        $this->testSuiteAssertions[$this->testSuiteLevel] = 0;
215
        $this->testSuiteErrors[$this->testSuiteLevel]     = 0;
216
        $this->testSuiteWarnings[$this->testSuiteLevel]   = 0;
217
        $this->testSuiteFailures[$this->testSuiteLevel]   = 0;
218
        $this->testSuiteSkipped[$this->testSuiteLevel]    = 0;
219
        $this->testSuiteTimes[$this->testSuiteLevel]      = 0;
220
    }
221
 
222
    /**
223
     * A testsuite ended.
224
     */
225
    public function endTestSuite(TestSuite $suite): void
226
    {
227
        $this->testSuites[$this->testSuiteLevel]->setAttribute(
228
            'tests',
229
            (string) $this->testSuiteTests[$this->testSuiteLevel]
230
        );
231
 
232
        $this->testSuites[$this->testSuiteLevel]->setAttribute(
233
            'assertions',
234
            (string) $this->testSuiteAssertions[$this->testSuiteLevel]
235
        );
236
 
237
        $this->testSuites[$this->testSuiteLevel]->setAttribute(
238
            'errors',
239
            (string) $this->testSuiteErrors[$this->testSuiteLevel]
240
        );
241
 
242
        $this->testSuites[$this->testSuiteLevel]->setAttribute(
243
            'warnings',
244
            (string) $this->testSuiteWarnings[$this->testSuiteLevel]
245
        );
246
 
247
        $this->testSuites[$this->testSuiteLevel]->setAttribute(
248
            'failures',
249
            (string) $this->testSuiteFailures[$this->testSuiteLevel]
250
        );
251
 
252
        $this->testSuites[$this->testSuiteLevel]->setAttribute(
253
            'skipped',
254
            (string) $this->testSuiteSkipped[$this->testSuiteLevel]
255
        );
256
 
257
        $this->testSuites[$this->testSuiteLevel]->setAttribute(
258
            'time',
259
            sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel])
260
        );
261
 
262
        if ($this->testSuiteLevel > 1) {
263
            $this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel];
264
            $this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel];
265
            $this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel];
266
            $this->testSuiteWarnings[$this->testSuiteLevel - 1] += $this->testSuiteWarnings[$this->testSuiteLevel];
267
            $this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel];
268
            $this->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel];
269
            $this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel];
270
        }
271
 
272
        $this->testSuiteLevel--;
273
    }
274
 
275
    /**
276
     * A test started.
277
     */
278
    public function startTest(Test $test): void
279
    {
280
        $usesDataprovider = false;
281
 
282
        if (method_exists($test, 'usesDataProvider')) {
283
            $usesDataprovider = $test->usesDataProvider();
284
        }
285
 
286
        $testCase = $this->document->createElement('testcase');
287
        $testCase->setAttribute('name', $test->getName());
288
 
289
        try {
290
            $class = new ReflectionClass($test);
291
            // @codeCoverageIgnoreStart
292
        } catch (ReflectionException $e) {
293
            throw new Exception(
294
                $e->getMessage(),
295
                $e->getCode(),
296
                $e
297
            );
298
        }
299
        // @codeCoverageIgnoreEnd
300
 
301
        $methodName = $test->getName(!$usesDataprovider);
302
 
303
        if ($class->hasMethod($methodName)) {
304
            try {
305
                $method = $class->getMethod($methodName);
306
                // @codeCoverageIgnoreStart
307
            } catch (ReflectionException $e) {
308
                throw new Exception(
309
                    $e->getMessage(),
310
                    $e->getCode(),
311
                    $e
312
                );
313
            }
314
            // @codeCoverageIgnoreEnd
315
 
316
            $testCase->setAttribute('class', $class->getName());
317
            $testCase->setAttribute('classname', str_replace('\\', '.', $class->getName()));
318
            $testCase->setAttribute('file', $class->getFileName());
319
            $testCase->setAttribute('line', (string) $method->getStartLine());
320
        }
321
 
322
        $this->currentTestCase = $testCase;
323
    }
324
 
325
    /**
326
     * A test ended.
327
     */
328
    public function endTest(Test $test, float $time): void
329
    {
330
        $numAssertions = 0;
331
 
332
        if (method_exists($test, 'getNumAssertions')) {
333
            $numAssertions = $test->getNumAssertions();
334
        }
335
 
336
        $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions;
337
 
338
        $this->currentTestCase->setAttribute(
339
            'assertions',
340
            (string) $numAssertions
341
        );
342
 
343
        $this->currentTestCase->setAttribute(
344
            'time',
345
            sprintf('%F', $time)
346
        );
347
 
348
        $this->testSuites[$this->testSuiteLevel]->appendChild(
349
            $this->currentTestCase
350
        );
351
 
352
        $this->testSuiteTests[$this->testSuiteLevel]++;
353
        $this->testSuiteTimes[$this->testSuiteLevel] += $time;
354
 
355
        $testOutput = '';
356
 
357
        if (method_exists($test, 'hasOutput') && method_exists($test, 'getActualOutput')) {
358
            $testOutput = $test->hasOutput() ? $test->getActualOutput() : '';
359
        }
360
 
361
        if (!empty($testOutput)) {
362
            $systemOut = $this->document->createElement(
363
                'system-out',
364
                Xml::prepareString($testOutput)
365
            );
366
 
367
            $this->currentTestCase->appendChild($systemOut);
368
        }
369
 
370
        $this->currentTestCase = null;
371
    }
372
 
373
    /**
374
     * Returns the XML as a string.
375
     */
376
    public function getXML(): string
377
    {
378
        return $this->document->saveXML();
379
    }
380
 
381
    private function doAddFault(Test $test, Throwable $t, string $type): void
382
    {
383
        if ($this->currentTestCase === null) {
384
            return;
385
        }
386
 
387
        if ($test instanceof SelfDescribing) {
388
            $buffer = $test->toString() . "\n";
389
        } else {
390
            $buffer = '';
391
        }
392
 
393
        $buffer .= trim(
394
            TestFailure::exceptionToString($t) . "\n" .
395
            Filter::getFilteredStacktrace($t)
396
        );
397
 
398
        $fault = $this->document->createElement(
399
            $type,
400
            Xml::prepareString($buffer)
401
        );
402
 
403
        if ($t instanceof ExceptionWrapper) {
404
            $fault->setAttribute('type', $t->getClassName());
405
        } else {
406
            $fault->setAttribute('type', get_class($t));
407
        }
408
 
409
        $this->currentTestCase->appendChild($fault);
410
    }
411
 
412
    private function doAddSkipped(): void
413
    {
414
        if ($this->currentTestCase === null) {
415
            return;
416
        }
417
 
418
        $skipped = $this->document->createElement('skipped');
419
 
420
        $this->currentTestCase->appendChild($skipped);
421
 
422
        $this->testSuiteSkipped[$this->testSuiteLevel]++;
423
    }
424
}