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\TestDox;
11
 
12
use function array_key_exists;
13
use function array_keys;
14
use function array_map;
15
use function array_pop;
16
use function array_values;
17
use function explode;
18
use function get_class;
19
use function gettype;
20
use function implode;
21
use function in_array;
22
use function is_bool;
23
use function is_float;
24
use function is_int;
25
use function is_numeric;
26
use function is_object;
27
use function is_scalar;
28
use function is_string;
29
use function ord;
30
use function preg_quote;
31
use function preg_replace;
32
use function range;
33
use function sprintf;
34
use function str_replace;
35
use function strlen;
36
use function strpos;
37
use function strtolower;
38
use function strtoupper;
39
use function substr;
40
use function trim;
41
use PHPUnit\Framework\TestCase;
42
use PHPUnit\Util\Color;
43
use PHPUnit\Util\Exception as UtilException;
44
use PHPUnit\Util\Test;
45
use ReflectionException;
46
use ReflectionMethod;
47
use ReflectionObject;
48
use SebastianBergmann\Exporter\Exporter;
49
 
50
/**
51
 * @internal This class is not covered by the backward compatibility promise for PHPUnit
52
 */
53
final class NamePrettifier
54
{
55
    /**
56
     * @var string[]
57
     */
58
    private $strings = [];
59
 
60
    /**
61
     * @var bool
62
     */
63
    private $useColor;
64
 
65
    public function __construct(bool $useColor = false)
66
    {
67
        $this->useColor = $useColor;
68
    }
69
 
70
    /**
71
     * Prettifies the name of a test class.
72
     *
73
     * @psalm-param class-string $className
74
     */
75
    public function prettifyTestClass(string $className): string
76
    {
77
        try {
78
            $annotations = Test::parseTestMethodAnnotations($className);
79
 
80
            if (isset($annotations['class']['testdox'][0])) {
81
                return $annotations['class']['testdox'][0];
82
            }
83
        } catch (UtilException $e) {
84
            // ignore, determine className by parsing the provided name
85
        }
86
 
87
        $parts     = explode('\\', $className);
88
        $className = array_pop($parts);
89
 
90
        if (substr($className, -1 * strlen('Test')) === 'Test') {
91
            $className = substr($className, 0, strlen($className) - strlen('Test'));
92
        }
93
 
94
        if (strpos($className, 'Tests') === 0) {
95
            $className = substr($className, strlen('Tests'));
96
        } elseif (strpos($className, 'Test') === 0) {
97
            $className = substr($className, strlen('Test'));
98
        }
99
 
100
        if (empty($className)) {
101
            $className = 'UnnamedTests';
102
        }
103
 
104
        if (!empty($parts)) {
105
            $parts[]            = $className;
106
            $fullyQualifiedName = implode('\\', $parts);
107
        } else {
108
            $fullyQualifiedName = $className;
109
        }
110
 
111
        $result = preg_replace('/(?<=[[:lower:]])(?=[[:upper:]])/u', ' ', $className);
112
 
113
        if ($fullyQualifiedName !== $className) {
114
            return $result . ' (' . $fullyQualifiedName . ')';
115
        }
116
 
117
        return $result;
118
    }
119
 
120
    /**
121
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
122
     */
123
    public function prettifyTestCase(TestCase $test): string
124
    {
125
        $annotations = Test::parseTestMethodAnnotations(
126
            get_class($test),
127
            $test->getName(false)
128
        );
129
 
130
        $annotationWithPlaceholders = false;
131
 
132
        $callback = static function (string $variable): string
133
        {
134
            return sprintf('/%s(?=\b)/', preg_quote($variable, '/'));
135
        };
136
 
137
        if (isset($annotations['method']['testdox'][0])) {
138
            $result = $annotations['method']['testdox'][0];
139
 
140
            if (strpos($result, '$') !== false) {
141
                $annotation   = $annotations['method']['testdox'][0];
142
                $providedData = $this->mapTestMethodParameterNamesToProvidedDataValues($test);
143
                $variables    = array_map($callback, array_keys($providedData));
144
 
145
                $result = trim(preg_replace($variables, $providedData, $annotation));
146
 
147
                $annotationWithPlaceholders = true;
148
            }
149
        } else {
150
            $result = $this->prettifyTestMethod($test->getName(false));
151
        }
152
 
153
        if (!$annotationWithPlaceholders && $test->usesDataProvider()) {
154
            $result .= $this->prettifyDataSet($test);
155
        }
156
 
157
        return $result;
158
    }
159
 
160
    public function prettifyDataSet(TestCase $test): string
161
    {
162
        if (!$this->useColor) {
163
            return $test->getDataSetAsString(false);
164
        }
165
 
166
        if (is_int($test->dataName())) {
167
            $data = Color::dim(' with data set ') . Color::colorize('fg-cyan', (string) $test->dataName());
168
        } else {
169
            $data = Color::dim(' with ') . Color::colorize('fg-cyan', Color::visualizeWhitespace((string) $test->dataName()));
170
        }
171
 
172
        return $data;
173
    }
174
 
175
    /**
176
     * Prettifies the name of a test method.
177
     */
178
    public function prettifyTestMethod(string $name): string
179
    {
180
        $buffer = '';
181
 
182
        if ($name === '') {
183
            return $buffer;
184
        }
185
 
186
        $string = (string) preg_replace('#\d+$#', '', $name, -1, $count);
187
 
188
        if (in_array($string, $this->strings, true)) {
189
            $name = $string;
190
        } elseif ($count === 0) {
191
            $this->strings[] = $string;
192
        }
193
 
194
        if (strpos($name, 'test_') === 0) {
195
            $name = substr($name, 5);
196
        } elseif (strpos($name, 'test') === 0) {
197
            $name = substr($name, 4);
198
        }
199
 
200
        if ($name === '') {
201
            return $buffer;
202
        }
203
 
204
        $name[0] = strtoupper($name[0]);
205
 
206
        if (strpos($name, '_') !== false) {
207
            return trim(str_replace('_', ' ', $name));
208
        }
209
 
210
        $wasNumeric = false;
211
 
212
        foreach (range(0, strlen($name) - 1) as $i) {
213
            if ($i > 0 && ord($name[$i]) >= 65 && ord($name[$i]) <= 90) {
214
                $buffer .= ' ' . strtolower($name[$i]);
215
            } else {
216
                $isNumeric = is_numeric($name[$i]);
217
 
218
                if (!$wasNumeric && $isNumeric) {
219
                    $buffer .= ' ';
220
                    $wasNumeric = true;
221
                }
222
 
223
                if ($wasNumeric && !$isNumeric) {
224
                    $wasNumeric = false;
225
                }
226
 
227
                $buffer .= $name[$i];
228
            }
229
        }
230
 
231
        return $buffer;
232
    }
233
 
234
    /**
235
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
236
     */
237
    private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test): array
238
    {
239
        try {
240
            $reflector = new ReflectionMethod(get_class($test), $test->getName(false));
241
            // @codeCoverageIgnoreStart
242
        } catch (ReflectionException $e) {
243
            throw new UtilException(
244
                $e->getMessage(),
245
                $e->getCode(),
246
                $e
247
            );
248
        }
249
        // @codeCoverageIgnoreEnd
250
 
251
        $providedData       = [];
252
        $providedDataValues = array_values($test->getProvidedData());
253
        $i                  = 0;
254
 
255
        $providedData['$_dataName'] = $test->dataName();
256
 
257
        foreach ($reflector->getParameters() as $parameter) {
258
            if (!array_key_exists($i, $providedDataValues) && $parameter->isDefaultValueAvailable()) {
259
                try {
260
                    $providedDataValues[$i] = $parameter->getDefaultValue();
261
                    // @codeCoverageIgnoreStart
262
                } catch (ReflectionException $e) {
263
                    throw new UtilException(
264
                        $e->getMessage(),
265
                        $e->getCode(),
266
                        $e
267
                    );
268
                }
269
                // @codeCoverageIgnoreEnd
270
            }
271
 
272
            $value = $providedDataValues[$i++] ?? null;
273
 
274
            if (is_object($value)) {
275
                $reflector = new ReflectionObject($value);
276
 
277
                if ($reflector->hasMethod('__toString')) {
278
                    $value = (string) $value;
279
                } else {
280
                    $value = get_class($value);
281
                }
282
            }
283
 
284
            if (!is_scalar($value)) {
285
                $value = gettype($value);
286
            }
287
 
288
            if (is_bool($value) || is_int($value) || is_float($value)) {
289
                $value = (new Exporter)->export($value);
290
            }
291
 
292
            if (is_string($value) && $value === '') {
293
                if ($this->useColor) {
294
                    $value = Color::colorize('dim,underlined', 'empty');
295
                } else {
296
                    $value = "''";
297
                }
298
            }
299
 
300
            $providedData['$' . $parameter->getName()] = $value;
301
        }
302
 
303
        if ($this->useColor) {
304
            $providedData = array_map(static function ($value)
305
            {
306
                return Color::colorize('fg-cyan', Color::visualizeWhitespace((string) $value, true));
307
            }, $providedData);
308
        }
309
 
310
        return $providedData;
311
    }
312
}