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 count;
14
use function explode;
15
use function get_class;
16
use function getmypid;
17
use function ini_get;
18
use function is_bool;
19
use function is_scalar;
20
use function method_exists;
21
use function print_r;
22
use function round;
23
use function str_replace;
24
use function stripos;
25
use PHPUnit\Framework\AssertionFailedError;
26
use PHPUnit\Framework\ExceptionWrapper;
27
use PHPUnit\Framework\ExpectationFailedException;
28
use PHPUnit\Framework\Test;
29
use PHPUnit\Framework\TestCase;
30
use PHPUnit\Framework\TestFailure;
31
use PHPUnit\Framework\TestResult;
32
use PHPUnit\Framework\TestSuite;
33
use PHPUnit\Framework\Warning;
34
use PHPUnit\TextUI\DefaultResultPrinter;
35
use PHPUnit\Util\Exception;
36
use PHPUnit\Util\Filter;
37
use ReflectionClass;
38
use ReflectionException;
39
use SebastianBergmann\Comparator\ComparisonFailure;
40
use Throwable;
41
 
42
/**
43
 * @internal This class is not covered by the backward compatibility promise for PHPUnit
44
 */
45
final class TeamCity extends DefaultResultPrinter
46
{
47
    /**
48
     * @var bool
49
     */
50
    private $isSummaryTestCountPrinted = false;
51
 
52
    /**
53
     * @var string
54
     */
55
    private $startedTestName;
56
 
57
    /**
58
     * @var false|int
59
     */
60
    private $flowId;
61
 
62
    public function printResult(TestResult $result): void
63
    {
64
        $this->printHeader($result);
65
        $this->printFooter($result);
66
    }
67
 
68
    /**
69
     * An error occurred.
70
     */
71
    public function addError(Test $test, Throwable $t, float $time): void
72
    {
73
        $this->printEvent(
74
            'testFailed',
75
            [
76
                'name'     => $test->getName(),
77
                'message'  => self::getMessage($t),
78
                'details'  => self::getDetails($t),
79
                'duration' => self::toMilliseconds($time),
80
            ]
81
        );
82
    }
83
 
84
    /**
85
     * A warning occurred.
86
     */
87
    public function addWarning(Test $test, Warning $e, float $time): void
88
    {
89
        $this->write(self::getMessage($e) . PHP_EOL);
90
    }
91
 
92
    /**
93
     * A failure occurred.
94
     */
95
    public function addFailure(Test $test, AssertionFailedError $e, float $time): void
96
    {
97
        $parameters = [
98
            'name'     => $test->getName(),
99
            'message'  => self::getMessage($e),
100
            'details'  => self::getDetails($e),
101
            'duration' => self::toMilliseconds($time),
102
        ];
103
 
104
        if ($e instanceof ExpectationFailedException) {
105
            $comparisonFailure = $e->getComparisonFailure();
106
 
107
            if ($comparisonFailure instanceof ComparisonFailure) {
108
                $expectedString = $comparisonFailure->getExpectedAsString();
109
 
110
                if ($expectedString === null || empty($expectedString)) {
111
                    $expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected());
112
                }
113
 
114
                $actualString = $comparisonFailure->getActualAsString();
115
 
116
                if ($actualString === null || empty($actualString)) {
117
                    $actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual());
118
                }
119
 
120
                if ($actualString !== null && $expectedString !== null) {
121
                    $parameters['type']     = 'comparisonFailure';
122
                    $parameters['actual']   = $actualString;
123
                    $parameters['expected'] = $expectedString;
124
                }
125
            }
126
        }
127
 
128
        $this->printEvent('testFailed', $parameters);
129
    }
130
 
131
    /**
132
     * Incomplete test.
133
     */
134
    public function addIncompleteTest(Test $test, Throwable $t, float $time): void
135
    {
136
        $this->printIgnoredTest($test->getName(), $t, $time);
137
    }
138
 
139
    /**
140
     * Risky test.
141
     */
142
    public function addRiskyTest(Test $test, Throwable $t, float $time): void
143
    {
144
        $this->addError($test, $t, $time);
145
    }
146
 
147
    /**
148
     * Skipped test.
149
     */
150
    public function addSkippedTest(Test $test, Throwable $t, float $time): void
151
    {
152
        $testName = $test->getName();
153
 
154
        if ($this->startedTestName !== $testName) {
155
            $this->startTest($test);
156
            $this->printIgnoredTest($testName, $t, $time);
157
            $this->endTest($test, $time);
158
        } else {
159
            $this->printIgnoredTest($testName, $t, $time);
160
        }
161
    }
162
 
163
    public function printIgnoredTest(string $testName, Throwable $t, float $time): void
164
    {
165
        $this->printEvent(
166
            'testIgnored',
167
            [
168
                'name'     => $testName,
169
                'message'  => self::getMessage($t),
170
                'details'  => self::getDetails($t),
171
                'duration' => self::toMilliseconds($time),
172
            ]
173
        );
174
    }
175
 
176
    /**
177
     * A testsuite started.
178
     */
179
    public function startTestSuite(TestSuite $suite): void
180
    {
181
        if (stripos(ini_get('disable_functions'), 'getmypid') === false) {
182
            $this->flowId = getmypid();
183
        } else {
184
            $this->flowId = false;
185
        }
186
 
187
        if (!$this->isSummaryTestCountPrinted) {
188
            $this->isSummaryTestCountPrinted = true;
189
 
190
            $this->printEvent(
191
                'testCount',
192
                ['count' => count($suite)]
193
            );
194
        }
195
 
196
        $suiteName = $suite->getName();
197
 
198
        if (empty($suiteName)) {
199
            return;
200
        }
201
 
202
        $parameters = ['name' => $suiteName];
203
 
204
        if (class_exists($suiteName, false)) {
205
            $fileName                   = self::getFileName($suiteName);
206
            $parameters['locationHint'] = "php_qn://{$fileName}::\\{$suiteName}";
207
        } else {
208
            $split = explode('::', $suiteName);
209
 
210
            if (count($split) === 2 && class_exists($split[0]) && method_exists($split[0], $split[1])) {
211
                $fileName                   = self::getFileName($split[0]);
212
                $parameters['locationHint'] = "php_qn://{$fileName}::\\{$suiteName}";
213
                $parameters['name']         = $split[1];
214
            }
215
        }
216
 
217
        $this->printEvent('testSuiteStarted', $parameters);
218
    }
219
 
220
    /**
221
     * A testsuite ended.
222
     */
223
    public function endTestSuite(TestSuite $suite): void
224
    {
225
        $suiteName = $suite->getName();
226
 
227
        if (empty($suiteName)) {
228
            return;
229
        }
230
 
231
        $parameters = ['name' => $suiteName];
232
 
233
        if (!class_exists($suiteName, false)) {
234
            $split = explode('::', $suiteName);
235
 
236
            if (count($split) === 2 && class_exists($split[0]) && method_exists($split[0], $split[1])) {
237
                $parameters['name'] = $split[1];
238
            }
239
        }
240
 
241
        $this->printEvent('testSuiteFinished', $parameters);
242
    }
243
 
244
    /**
245
     * A test started.
246
     */
247
    public function startTest(Test $test): void
248
    {
249
        $testName              = $test->getName();
250
        $this->startedTestName = $testName;
251
        $params                = ['name' => $testName];
252
 
253
        if ($test instanceof TestCase) {
254
            $className              = get_class($test);
255
            $fileName               = self::getFileName($className);
256
            $params['locationHint'] = "php_qn://{$fileName}::\\{$className}::{$testName}";
257
        }
258
 
259
        $this->printEvent('testStarted', $params);
260
    }
261
 
262
    /**
263
     * A test ended.
264
     */
265
    public function endTest(Test $test, float $time): void
266
    {
267
        parent::endTest($test, $time);
268
 
269
        $this->printEvent(
270
            'testFinished',
271
            [
272
                'name'     => $test->getName(),
273
                'duration' => self::toMilliseconds($time),
274
            ]
275
        );
276
    }
277
 
278
    protected function writeProgress(string $progress): void
279
    {
280
    }
281
 
282
    private function printEvent(string $eventName, array $params = []): void
283
    {
284
        $this->write("\n##teamcity[{$eventName}");
285
 
286
        if ($this->flowId) {
287
            $params['flowId'] = $this->flowId;
288
        }
289
 
290
        foreach ($params as $key => $value) {
291
            $escapedValue = self::escapeValue((string) $value);
292
            $this->write(" {$key}='{$escapedValue}'");
293
        }
294
 
295
        $this->write("]\n");
296
    }
297
 
298
    private static function getMessage(Throwable $t): string
299
    {
300
        $message = '';
301
 
302
        if ($t instanceof ExceptionWrapper) {
303
            if ($t->getClassName() !== '') {
304
                $message .= $t->getClassName();
305
            }
306
 
307
            if ($message !== '' && $t->getMessage() !== '') {
308
                $message .= ' : ';
309
            }
310
        }
311
 
312
        return $message . $t->getMessage();
313
    }
314
 
315
    private static function getDetails(Throwable $t): string
316
    {
317
        $stackTrace = Filter::getFilteredStacktrace($t);
318
        $previous   = $t instanceof ExceptionWrapper ? $t->getPreviousWrapped() : $t->getPrevious();
319
 
320
        while ($previous) {
321
            $stackTrace .= "\nCaused by\n" .
322
                TestFailure::exceptionToString($previous) . "\n" .
323
                Filter::getFilteredStacktrace($previous);
324
 
325
            $previous = $previous instanceof ExceptionWrapper ?
326
                $previous->getPreviousWrapped() : $previous->getPrevious();
327
        }
328
 
329
        return ' ' . str_replace("\n", "\n ", $stackTrace);
330
    }
331
 
332
    private static function getPrimitiveValueAsString($value): ?string
333
    {
334
        if ($value === null) {
335
            return 'null';
336
        }
337
 
338
        if (is_bool($value)) {
339
            return $value ? 'true' : 'false';
340
        }
341
 
342
        if (is_scalar($value)) {
343
            return print_r($value, true);
344
        }
345
 
346
        return null;
347
    }
348
 
349
    private static function escapeValue(string $text): string
350
    {
351
        return str_replace(
352
            ['|', "'", "\n", "\r", ']', '['],
353
            ['||', "|'", '|n', '|r', '|]', '|['],
354
            $text
355
        );
356
    }
357
 
358
    /**
359
     * @param string $className
360
     */
361
    private static function getFileName($className): string
362
    {
363
        try {
364
            return (new ReflectionClass($className))->getFileName();
365
            // @codeCoverageIgnoreStart
366
        } catch (ReflectionException $e) {
367
            throw new Exception(
368
                $e->getMessage(),
369
                $e->getCode(),
370
                $e
371
            );
372
        }
373
        // @codeCoverageIgnoreEnd
374
    }
375
 
376
    /**
377
     * @param float $time microseconds
378
     */
379
    private static function toMilliseconds(float $time): int
380
    {
381
        return (int) round($time * 1000);
382
    }
383
}