Test Failed
Push — stable ( 52f02e...8e9716 )
by Nuno
04:37
created

src/Adapters/Phpunit/Section.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * This file is part of Collision.
5
 *
6
 * (c) Nuno Maduro <[email protected]>
7
 *
8
 *  For the full copyright and license information, please view the LICENSE
9
 *  file that was distributed with this source code.
10
 */
11
12
namespace NunoMaduro\Collision\Adapters\Phpunit;
13
14
use PHPUnit\Framework\TestCase;
15
use PHPUnit\Framework\Warning;
16
use Symfony\Component\Console\Output\ConsoleOutput;
17
use Symfony\Component\Console\Output\ConsoleSectionOutput;
18
use Throwable;
19
20
/**
21
 * @internal
22
 */
23
final class Section
24
{
25
    /**
26
     * Holds an instance of the console output.
27
     *
28
     * @var ConsoleOutput
29
     */
30
    private $output;
31
32
    /**
33
     * Holds an instance of the console section.
34
     *
35
     * @var ConsoleSectionOutput
36
     */
37
    private $section;
38
39
    /**
40
     * Holds an instance of the console section.
41
     *
42
     * @var ConsoleSectionOutput
43
     */
44
    private $footer;
45
46
    /**
47
     * If the current testSuite is dirty.
48
     *
49
     * @var bool
50
     */
51
    private $dirty = false;
52
53
    /**
54
     * Holds an instance of the test case.
55
     *
56
     * @var TestCase
57
     */
58
    private $testCase;
59
60
    /**
61
     * If the current testCase should pass.
62
     *
63
     * @var bool
64
     */
65
    public $shouldPass = true;
66
67
    /**
68
     * Holds the content of the section.
69
     *
70
     * @var array<int, string>
71
     */
72
    private $tests = [];
73
74
    /**
75
     * Section constructor.
76
     *
77
     * @param  ConsoleOutput  $output
78
     */
79 3
    public function __construct(ConsoleOutput $output)
80
    {
81 3
        $this->output = $output;
82 3
        $this->section = $output->section();
83 3
        $this->footer = $output->section();
84
        $this->testCase = new class extends TestCase {
85
        };
86 3
    }
87
88
    /**
89
     * Runs the given test.
90
     *
91
     * @param  TestCase  $test
92
     *
93
     * @return void
94
     */
95
    public function runs(TestCase $test): void
96
    {
97
        if (get_class($this->testCase) !== get_class($test)) {
98
            $this->end();
99
            $this->section = $this->output->section();
100
            $this->tests = [];
101
            $this->dirty = false;
102
        }
103
104
        $this->shouldPass = true;
105
        $this->testCase = $test;
106
        $this->updateTest('•', 'yellow', true);
107
    }
108
109
    /**
110
     * Passes the current test case.
111
     *
112
     * @return void
113
     */
114
    public function pass(): void
115
    {
116
        if ($this->shouldPass) {
117
            $this->updateTest('✓', 'green');
118
        }
119
    }
120
121
    /**
122
     * Marks the current test case as failed.
123
     *
124
     * @return void
125
     */
126
    public function fail(): void
127
    {
128
        $this->updateTest('✕', 'red');
129
        $this->title('FAIL', 'red');
130
131
        $this->footer->clear();
132
        $this->section->write($this->tests);
133
134
        $this->dirty = true;
135
        $this->shouldPass = false;
136
    }
137
138
    /**
139
     * Marks the current test case as incomplete.
140
     *
141
     * @param  Throwable  $throwable
142
     *
143
     * @return void
144
     */
145 View Code Duplication
    public function incomplete(Throwable $throwable): void
146
    {
147
        $this->updateTest('i', 'yellow', false, $throwable->getMessage());
148
149
        $this->dirty = true;
150
        $this->shouldPass = false;
151
    }
152
153
    /**
154
     * Marks the current test case as risky.
155
     *
156
     * @return void
157
     */
158
    public function risky(): void
159
    {
160
        $this->updateTest('r', 'yellow');
161
162
        $this->dirty = true;
163
        $this->shouldPass = false;
164
    }
165
166
    /**
167
     * Marks the current test case as risky.
168
     *
169
     * @param  Throwable  $throwable
170
     *
171
     * @return void
172
     */
173 View Code Duplication
    public function skipped(Throwable $throwable): void
174
    {
175
        $this->updateTest('s', 'yellow', false, $throwable->getMessage());
176
177
        $this->dirty = true;
178
        $this->shouldPass = false;
179
    }
180
181
    /**
182
     * Marks the current test case as risky.
183
     *
184
     * @return void
185
     */
186 View Code Duplication
    public function warn(Warning $warning): void
187
    {
188
        $this->updateTest('w', 'yellow', false, $warning->getMessage());
189
190
        $this->dirty = true;
191
        $this->shouldPass = false;
192
    }
193
194
    /**
195
     * Ends the current test suite.
196
     *
197
     * Here we do 3 things:
198
     *
199
     * 0. Remove the footer.
200
     * 1. Display the title.
201
     * 2. Display the tests results.
202
     */
203
    public function end(): void
204
    {
205
        if (count($this->tests)) {
206
            $this->footer->clear();
207
208
            if (! $this->dirty) {
209
                $this->title('PASS', 'green');
210
            } else {
211
                $this->title('WARN', 'yellow');
212
            }
213
214
            $this->section->write($this->tests);
0 ignored issues
show
$this->tests is of type array, but the function expects a string|object<Symfony\Co...onsole\Output\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
215
        }
216
217
    }
218
219
    /**
220
     * Updates the console with the current state.
221
     *
222
     * @param  string  $icon
223
     * @param  string  $color
224
     *
225
     * @param  bool  $create
226
     *
227
     * @return void
228
     */
229
    private function updateTest(string $icon, string $color, bool $create = false, string $note = null): void
230
    {
231
        $value = sprintf(
232
            '  <fg=%s;options=bold>%s</><fg=default> %s</>',
233
            $color,
234
            $icon,
235
            $name = $this->getTestCaseDescription()
236
        );
237
238
        if ($note) {
239
            $value .= sprintf(' → <fg=%s>%s</>', $color, trim((string) preg_replace("/\r|\n/", ' ', $note)));
240
        }
241
242
        if ($create) {
243
            $this->tests[] = $value;
244
        }
245
246
        $this->tests[count($this->tests) - 1] = $value;
247
248
        $this->footer('RUNS', 'yellow');
249
        $this->footer->write($value);
250
    }
251
252
    /**
253
     * Get the current test case description.
254
     *
255
     * @return string
256
     */
257
    private function getTestCaseDescription(): string
258
    {
259
        $name = $this->testCase->getName(true);
260
261
        // First, lets replace underscore by spaces.
262
        $name = str_replace('_', ' ', $name);
263
264
        // Then, replace upper cases by spaces.
265
        $name = (string) preg_replace('/([A-Z])/', ' $1', $name);
266
267
        // Finally, if it starts with `test`, we remove it.
268
        $name = (string) preg_replace('/^test/', '', $name);
269
270
        // Removes spaces
271
        $name = (string) trim($name);
272
273
        // Finally, lower case everything
274
        return (string) mb_strtolower($name);
275
    }
276
277
    private function footer(string $title, string $color): void
278
    {
279
        $fg = $title === 'FAIL' ? 'default' : 'black';
280
281
        $classParts = explode('\\', get_class($this->testCase));
282
283
        // Removes `Tests` part
284
        array_shift($classParts);
285
286
        $highlightedPart = array_pop($classParts);
287
        $nonHighlightedPart = implode('\\', $classParts);
288
        $class = sprintf("\e[2m%s\e[22m<fg=white;options=bold>%s</>", "$nonHighlightedPart\\", $highlightedPart);
289
290
        $this->footer->clear();
291
        $this->footer = $this->output->section();
292
        $this->footer->write(sprintf(
293
            "\n  <fg=%s;bg=%s;options=bold> %s </><fg=default> %s</>",
294
            $fg,
295
            $color,
296
            $title,
297
            $class
298
        ));
299
    }
300
301
    private function title(string $title, string $color): void
302
    {
303
        $fg = $title === 'FAIL' ? 'default' : 'black';
304
        $classParts = explode('\\', get_class($this->testCase));
305
306
        // Removes `Tests` part
307
        array_shift($classParts);
308
309
        $highlightedPart = array_pop($classParts);
310
        $nonHighlightedPart = implode('\\', $classParts);
311
        $class = sprintf("\e[2m%s\e[22m<fg=white;options=bold>%s</>", "$nonHighlightedPart\\", $highlightedPart);
312
313
        $this->section->write(sprintf(
314
            "\n  <fg=%s;bg=%s;options=bold> %s </><fg=default> %s</>",
315
            $fg,
316
            $color,
317
            $title,
318
            $class
319
        ));
320
    }
321
}
322