Passed
Push — stable ( 85ed36...e7655b )
by Nuno
12:43 queued 11s
created

Section   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 274
Duplicated Lines 14.6 %

Coupling/Cohesion

Components 1

Test Coverage

Coverage 6.67%

Importance

Changes 0
Metric Value
wmc 21
lcom 1
dl 40
loc 274
ccs 6
cts 90
cp 0.0667
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A create() 0 4 1
A runs() 0 11 2
A pass() 0 6 2
A fail() 8 8 1
A incomplete() 8 8 1
A risky() 8 8 1
A skipped() 8 8 1
A warn() 8 8 1
A end() 0 6 3
A updateTest() 0 21 3
A update() 0 5 1
A getTestCaseDescription() 0 19 1
A title() 0 23 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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\TestSuite;
16
use PHPUnit\Framework\Warning;
17
use Symfony\Component\Console\Output\ConsoleOutput;
18
use Symfony\Component\Console\Output\ConsoleSectionOutput;
19
use Throwable;
20
21
/**
22
 * @internal
23
 */
24
final class Section
25
{
26
    /**
27
     * Holds an instance of the section.
28
     *
29
     * @var ConsoleSectionOutput
30
     */
31
    private $section;
32
33
    /**
34
     * Holds an instance of the test suite.
35
     *
36
     * @var TestSuite
37
     */
38
    private $testSuite;
39
40
    /**
41
     * If the current testSuite is dirty
42
     *
43
     * @var bool
44
     */
45
    private $dirty = false;
46
47
    /**
48
     * Holds an instance of the test case.
49
     *
50
     * @var TestCase
51
     */
52
    private $testCase;
53
54
    /**
55
     * If the current testCase should pass.
56
     *
57
     * @var bool
58
     */
59
    private $shouldPass = true;
60
61
    /**
62
     * Holds the content of the section.
63
     *
64
     * @var array<int, string>
65
     */
66
    private $tests = [];
67
68
    /**
69
     * Section constructor.
70
     *
71
     * @param  ConsoleSectionOutput  $section
72
     * @param  TestSuite  $testSuite
73
     */
74 3
    public function __construct(ConsoleSectionOutput $section, TestSuite $testSuite)
75
    {
76 3
        $this->section = $section;
77 3
        $this->testSuite = $testSuite;
78
        $this->testCase = new class extends TestCase {
79
        };
80 3
    }
81
82
    /**
83
     * @param  ConsoleOutput  $output
84
     * @param  TestSuite  $testSuite
85
     *
86
     * @return Section
87
     */
88 3
    public static function create(ConsoleOutput $output, TestSuite $testSuite): Section
89
    {
90 3
        return new self($output->section(), $testSuite);
91
    }
92
93
    /**
94
     * Runs the given test.
95
     *
96
     * @param  TestCase  $test
97
     *
98
     * @return void
99
     */
100
    public function runs(TestCase $test): void
101
    {
102
        $this->testCase = $test;
103
        $this->shouldPass = true;
104
105
        if (count($this->tests) === 0) {
106
            $this->title('RUNS', 'yellow');
107
        }
108
109
        $this->updateTest('•', 'yellow', true);
110
    }
111
112
113
    /**
114
     * Passes the current test case.
115
     *
116
     * @return void
117
     */
118
    public function pass(): void
119
    {
120
        if ($this->shouldPass) {
121
            $this->updateTest('✓', 'green');
122
        }
123
    }
124
125
    /**
126
     * Marks the current test case as failed.
127
     *
128
     * @return void
129
     */
130 View Code Duplication
    public function fail(): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
131
    {
132
        $this->title('FAIL', 'red');
133
        $this->updateTest('✕', 'red');
134
135
        $this->dirty = true;
136
        $this->shouldPass = false;
137
    }
138
139
    /**
140
     * Marks the current test case as incomplete.
141
     *
142
     * @param  Throwable  $throwable
143
     *
144
     * @return void
145
     */
146 View Code Duplication
    public function incomplete(Throwable $throwable): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
147
    {
148
        $this->updateTest('i', 'yellow', false, $throwable->getMessage());
149
        $this->title('WARN', 'yellow');
150
151
        $this->dirty = true;
152
        $this->shouldPass = false;
153
    }
154
155
    /**
156
     * Marks the current test case as risky.
157
     *
158
     * @return void
159
     */
160 View Code Duplication
    public function risky(): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
161
    {
162
        $this->updateTest('r', 'yellow');
163
        $this->title('WARN', 'yellow');
164
165
        $this->dirty = true;
166
        $this->shouldPass = false;
167
    }
168
169
    /**
170
     * Marks the current test case as risky.
171
     *
172
     * @param  Throwable  $throwable
173
     *
174
     * @return void
175
     */
176 View Code Duplication
    public function skipped(Throwable $throwable): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
177
    {
178
        $this->updateTest('s', 'yellow', false, $throwable->getMessage());
179
        $this->title('WARN', 'yellow');
180
181
        $this->dirty = true;
182
        $this->shouldPass = false;
183
    }
184
185
    /**
186
     * Marks the current test case as risky.
187
     *
188
     * @return void
189
     */
190 View Code Duplication
    public function warn(Warning $warning): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
191
    {
192
        $this->updateTest('w', 'yellow', false, $warning->getMessage());
193
        $this->title('WARN', 'yellow');
194
195
        $this->dirty = true;
196
        $this->shouldPass = false;
197
    }
198
199
    public function end(): void
200
    {
201
        if (! $this->dirty && count($this->tests)) {
202
            $this->title('PASS', 'green');
203
        }
204
    }
205
206
    /**
207
     * Updates the console with the current state.
208
     *
209
     * @param  string  $icon
210
     * @param  string  $color
211
     *
212
     * @param  bool  $create
213
     *
214
     * @return void
215
     */
216
    private function updateTest(string $icon, string $color, bool $create = false, string $note = null): void
217
    {
218
        $value = sprintf(
219
            '  <fg=%s;options=bold>%s</><fg=default> %s</>',
220
            $color,
221
            $icon,
222
            $name = $this->getTestCaseDescription()
223
        );
224
225
        if ($note) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $note of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
226
            $value .= sprintf(" → <fg=%s>%s</>", $color, trim((string) preg_replace("/\r|\n/", ' ', $note)));
227
        }
228
229
        if ($create) {
230
            $this->tests[] = $value;
231
        } else {
232
            $this->tests[count($this->tests) - 1] = $value;
233
        }
234
235
        $this->update();
236
    }
237
238
    /**
239
     * Updates the console with the current state.
240
     *
241
     * @return void
242
     */
243
    private function update(): void
244
    {
245
        $this->section->clear();
246
        $this->section->writeln($this->tests);
0 ignored issues
show
Documentation introduced by
$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...
247
    }
248
249
    /**
250
     * Get the current test case description.
251
     *
252
     * @return string
253
     */
254
    private function getTestCaseDescription(): string
255
    {
256
        $name = $this->testCase->getName(false);
257
258
        // First, lets replace underscore by spaces.
259
        $name = str_replace('_', ' ', $name);
260
261
        // Then, replace upper cases by spaces.
262
        $name = (string) preg_replace('/([A-Z])/', ' $1', $name);
263
264
        // Finally, if it starts with `test`, we remove it.
265
        $name = (string) preg_replace('/^test/', '', $name);
266
267
        // Removes spaces
268
        $name = (string) trim($name);
269
270
        // Finally, lower case everything
271
        return (string) mb_strtolower($name);
272
    }
273
274
    private function title(string $title, string $color): void
275
    {
276
        $fg = $title === 'FAIL' ? 'default' : 'black';
277
278
        $classParts = explode('\\', get_class($this->testCase));
279
280
        // Removes `Tests` part
281
        array_shift($classParts);
282
283
        $highlightedPart = array_pop($classParts);
284
        $nonHighlightedPart = implode('\\', $classParts);
285
        $class = sprintf("\e[2m%s\e[22m<fg=white;options=bold>%s</>", "$nonHighlightedPart\\", $highlightedPart);
286
287
        $this->tests[0] = sprintf(
288
            "\n  <fg=%s;bg=%s;options=bold> %s </><fg=default> %s</>",
289
            $fg,
290
            $color,
291
            $title,
292
            $class
293
        );
294
295
        $this->update();
296
    }
297
}
298
299