ResultPrinter   C
last analyzed

Complexity

Total Complexity 58

Size/Duplication

Total Lines 531
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 82.18%

Importance

Changes 0
Metric Value
wmc 58
lcom 1
cbo 5
dl 0
loc 531
ccs 143
cts 174
cp 0.8218
rs 6.3005
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A addTest() 0 8 1
B start() 0 20 5
A println() 0 5 1
A flush() 0 5 1
A printResults() 0 8 1
A printFeedback() 0 18 2
A getHeader() 0 4 1
A addWarnings() 0 4 1
A getWarnings() 0 4 1
A isSuccessful() 0 4 2
A getFooter() 0 6 2
A getFailures() 0 6 1
A getErrors() 0 6 1
A getTotalCases() 0 4 1
A processReaderFeedback() 0 19 4
A printTestWarnings() 0 10 3
A isSkippedIncompleTestCanBeTracked() 0 5 3
A processTestOverhead() 0 13 3
A printSkippedAndIncomplete() 0 9 3
A printFeedbackItem() 0 10 2
B getDefects() 0 20 5
A getProgress() 0 9 2
A getFailedFooter() 0 14 1
B getSuccessFooter() 0 27 4
A green() 0 10 2
A red() 0 10 2
A clearLogs() 0 6 2

How to fix   Complexity   

Complex Class

Complex classes like ResultPrinter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ResultPrinter, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace ParaTest\Runners\PHPUnit;
6
7
use ParaTest\Logging\JUnit\Reader;
8
use ParaTest\Logging\LogInterpreter;
9
10
/**
11
 * Class ResultPrinter.
12
 *
13
 * Used for outputing ParaTest results
14
 */
15
class ResultPrinter
16
{
17
    /**
18
     * A collection of ExecutableTest objects.
19
     *
20
     * @var array
21
     */
22
    protected $suites = [];
23
24
    /**
25
     * @var \ParaTest\Logging\LogInterpreter
26
     */
27
    protected $results;
28
29
    /**
30
     * The number of tests results currently printed.
31
     * Used to determine when to tally current results
32
     * and start a new row.
33
     *
34
     * @var int
35
     */
36
    protected $numTestsWidth;
37
38
    /**
39
     * Used for formatting results to a given width.
40
     *
41
     * @var int
42
     */
43
    protected $maxColumn;
44
45
    /**
46
     * The total number of cases to be run.
47
     *
48
     * @var int
49
     */
50
    protected $totalCases = 0;
51
52
    /**
53
     * The current column being printed to.
54
     *
55
     * @var int
56
     */
57
    protected $column = 0;
58
59
    /**
60
     * @var \PHP_Timer
61
     */
62
    protected $timer;
63
64
    /**
65
     * The total number of cases printed so far.
66
     *
67
     * @var int
68
     */
69
    protected $casesProcessed = 0;
70
71
    /**
72
     * Whether to display a red or green bar.
73
     *
74
     * @var bool
75
     */
76
    protected $colors;
77
78
    /**
79
     * Warnings generated by the cases.
80
     *
81
     * @var array
82
     */
83
    protected $warnings = [];
84
85
    /**
86
     * Number of columns.
87
     *
88
     * @var int
89
     */
90
    protected $numberOfColumns = 80;
91
92
    /**
93
     * Number of skipped or incomplete tests.
94
     *
95
     * @var int
96
     */
97
    protected $totalSkippedOrIncomplete = 0;
98
99
    /**
100
     * Do we need to try to process skipped/incompleted tests.
101
     *
102
     * @var bool
103
     */
104
    protected $processSkipped = false;
105
106 27
    public function __construct(LogInterpreter $results)
107
    {
108 27
        $this->results = $results;
109 27
        $this->timer = new \PHP_Timer();
110 27
    }
111
112
    /**
113
     * Adds an ExecutableTest to the tracked results.
114
     *
115
     * @param ExecutableTest $suite
116
     *
117
     * @return $this
118
     */
119 16
    public function addTest(ExecutableTest $suite): self
120
    {
121 16
        $this->suites[] = $suite;
122 16
        $increment = $suite->getTestCount();
123 16
        $this->totalCases = $this->totalCases + $increment;
124
125 16
        return $this;
126
    }
127
128
    /**
129
     * Initializes printing constraints, prints header
130
     * information and starts the test timer.
131
     *
132
     * @param Options $options
133
     */
134 9
    public function start(Options $options)
135
    {
136 9
        $this->numTestsWidth = strlen((string) $this->totalCases);
137 9
        $this->maxColumn = $this->numberOfColumns
138 9
                         + (DIRECTORY_SEPARATOR === '\\' ? -1 : 0) // fix windows blank lines
139 9
                         - strlen($this->getProgress());
140 9
        printf(
141 9
            "\nRunning phpunit in %d process%s with %s%s\n\n",
142 9
            $options->processes,
0 ignored issues
show
Documentation introduced by
The property $processes is declared protected in ParaTest\Runners\PHPUnit\Options. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
143 9
            $options->processes > 1 ? 'es' : '',
0 ignored issues
show
Documentation introduced by
The property $processes is declared protected in ParaTest\Runners\PHPUnit\Options. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
144 9
            $options->phpunit,
0 ignored issues
show
Documentation introduced by
The property $phpunit is declared protected in ParaTest\Runners\PHPUnit\Options. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
145 9
            $options->functional ? '. Functional mode is ON.' : ''
0 ignored issues
show
Documentation introduced by
The property $functional is declared protected in ParaTest\Runners\PHPUnit\Options. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
146
        );
147 9
        if (isset($options->filtered['configuration'])) {
0 ignored issues
show
Documentation introduced by
The property $filtered is declared protected in ParaTest\Runners\PHPUnit\Options. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
148 9
            printf("Configuration read from %s\n\n", $options->filtered['configuration']->getPath());
0 ignored issues
show
Documentation introduced by
The property $filtered is declared protected in ParaTest\Runners\PHPUnit\Options. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
149
        }
150 9
        $this->timer->start();
151 9
        $this->colors = $options->colors;
0 ignored issues
show
Documentation introduced by
The property $colors is declared protected in ParaTest\Runners\PHPUnit\Options. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
152 9
        $this->processSkipped = $this->isSkippedIncompleTestCanBeTracked($options);
153 9
    }
154
155
    /**
156
     * @param string $string
157
     */
158 2
    public function println(string $string = '')
159
    {
160 2
        $this->column = 0;
161 2
        echo "$string\n";
162 2
    }
163
164
    /**
165
     * Prints all results and removes any log files
166
     * used for aggregating results.
167
     */
168
    public function flush()
169
    {
170
        $this->printResults();
171
        $this->clearLogs();
172
    }
173
174
    /**
175
     * Print final results.
176
     */
177 2
    public function printResults()
178
    {
179 2
        echo $this->getHeader();
180 2
        echo $this->getErrors();
181 2
        echo $this->getFailures();
182 2
        echo $this->getWarnings();
183 2
        echo $this->getFooter();
184 2
    }
185
186
    /**
187
     * Prints the individual "quick" feedback for run
188
     * tests, that is the ".EF" items.
189
     *
190
     * @param ExecutableTest $test
191
     */
192 11
    public function printFeedback(ExecutableTest $test)
193
    {
194
        try {
195 11
            $reader = new Reader($test->getTempFile());
196
        } catch (\InvalidArgumentException $e) {
197
            throw new \RuntimeException(sprintf(
198
                "%s\n" .
199
                "The process: %s\n" .
200
                "This means a PHPUnit process was unable to run \"%s\"\n",
201
                $e->getMessage(),
202
                $test->getLastCommand(),
203
                $test->getPath()
204
            ));
205
        }
206 11
        $this->results->addReader($reader);
207 11
        $this->processReaderFeedback($reader, $test->getTestCount());
208 11
        $this->printTestWarnings($test);
209 11
    }
210
211
    /**
212
     * Returns the header containing resource usage.
213
     *
214
     * @return string
215
     */
216 3
    public function getHeader(): string
217
    {
218 3
        return "\n\n" . $this->timer->resourceUsage() . "\n\n";
219
    }
220
221
    /**
222
     * Add an array of warning strings. These cause the test run to be shown
223
     * as failed.
224
     */
225
    public function addWarnings(array $warnings)
226
    {
227
        $this->warnings = array_merge($this->warnings, $warnings);
228
    }
229
230
    /**
231
     * Returns warning messages as a string.
232
     */
233 2
    public function getWarnings(): string
234
    {
235 2
        return $this->getDefects($this->warnings, 'warning');
236
    }
237
238
    /**
239
     * Whether the test run is successful and has no warnings.
240
     *
241
     * @return bool
242
     */
243 4
    public function isSuccessful(): bool
244
    {
245 4
        return $this->results->isSuccessful() && count($this->warnings) === 0;
246
    }
247
248
    /**
249
     * Return the footer information reporting success
250
     * or failure.
251
     *
252
     * @return string
253
     */
254 4
    public function getFooter(): string
255
    {
256 4
        return $this->isSuccessful()
257 1
                    ? $this->getSuccessFooter()
258 4
                    : $this->getFailedFooter();
259
    }
260
261
    /**
262
     * Returns the failure messages.
263
     *
264
     * @return string
265
     */
266 3
    public function getFailures(): string
267
    {
268 3
        $failures = $this->results->getFailures();
0 ignored issues
show
Documentation Bug introduced by
The method getFailures does not exist on object<ParaTest\Logging\LogInterpreter>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
269
270 3
        return $this->getDefects($failures, 'failure');
271
    }
272
273
    /**
274
     * Returns error messages.
275
     *
276
     * @return string
277
     */
278 4
    public function getErrors(): string
279
    {
280 4
        $errors = $this->results->getErrors();
0 ignored issues
show
Documentation Bug introduced by
The method getErrors does not exist on object<ParaTest\Logging\LogInterpreter>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
281
282 4
        return $this->getDefects($errors, 'error');
283
    }
284
285
    /**
286
     * Returns the total cases being printed.
287
     *
288
     * @return int
289
     */
290 2
    public function getTotalCases(): int
291
    {
292 2
        return $this->totalCases;
293
    }
294
295
    /**
296
     * Process reader feedback and print it.
297
     *
298
     * @param Reader $reader
299
     * @param int    $expectedTestCount
300
     */
301 11
    protected function processReaderFeedback(Reader $reader, int $expectedTestCount)
302
    {
303 11
        $feedbackItems = $reader->getFeedback();
304
305 11
        $actualTestCount = count($feedbackItems);
306
307 11
        $this->processTestOverhead($actualTestCount, $expectedTestCount);
308
309 11
        foreach ($feedbackItems as $item) {
310 11
            $this->printFeedbackItem($item);
311 11
            if ($item === 'S') {
312 11
                ++$this->totalSkippedOrIncomplete;
313
            }
314
        }
315
316 11
        if ($this->processSkipped) {
317 4
            $this->printSkippedAndIncomplete($actualTestCount, $expectedTestCount);
318
        }
319 11
    }
320
321
    /**
322
     * Prints test warnings.
323
     *
324
     * @param ExecutableTest $test
325
     */
326 11
    protected function printTestWarnings(ExecutableTest $test)
327
    {
328 11
        $warnings = $test->getWarnings();
329 11
        if ($warnings) {
330
            $this->addWarnings($warnings);
331
            foreach ($warnings as $warning) {
332
                $this->printFeedbackItem('W');
333
            }
334
        }
335 11
    }
336
337
    /**
338
     * Is skipped/incomplete amount can be properly processed.
339
     *
340
     * @todo Skipped/Incomplete test tracking available only in functional mode for now
341
     *       or in regular mode but without group/exclude-group filters.
342
     *
343
     * @param mixed $options
344
     *
345
     * @return bool
346
     */
347 9
    protected function isSkippedIncompleTestCanBeTracked(Options $options): bool
348
    {
349 9
        return $options->functional
0 ignored issues
show
Documentation introduced by
The property $functional is declared protected in ParaTest\Runners\PHPUnit\Options. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
350 9
            || (empty($options->groups) && empty($options->excludeGroups));
0 ignored issues
show
Documentation introduced by
The property $groups is declared protected in ParaTest\Runners\PHPUnit\Options. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property $excludeGroups is declared protected in ParaTest\Runners\PHPUnit\Options. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
351
    }
352
353
    /**
354
     * Process test overhead.
355
     *
356
     * In some situations phpunit can return more tests then we expect and in that case
357
     * this method correct total amount of tests so paratest progress will be auto corrected.
358
     *
359
     * @todo May be we need to throw Exception here instead of silent correction.
360
     *
361
     * @param int $actualTestCount
362
     * @param int $expectedTestCount
363
     */
364 11
    protected function processTestOverhead(int $actualTestCount, int $expectedTestCount)
365
    {
366 11
        $overhead = $actualTestCount - $expectedTestCount;
367 11
        if ($this->processSkipped) {
368 4
            if ($overhead > 0) {
369 1
                $this->totalCases += $overhead;
370
            } else {
371 4
                $this->totalSkippedOrIncomplete += -$overhead;
372
            }
373
        } else {
374 7
            $this->totalCases += $overhead;
375
        }
376 11
    }
377
378
    /**
379
     * Prints S for skipped and incomplete tests.
380
     *
381
     * If for some reason process return less tests than expected then we threat all remaining
382
     * as skipped or incomplete and print them as skipped (S letter)
383
     *
384
     * @param int $actualTestCount
385
     * @param int $expectedTestCount
386
     */
387 4
    protected function printSkippedAndIncomplete(int $actualTestCount, int $expectedTestCount)
388
    {
389 4
        $overhead = $expectedTestCount - $actualTestCount;
390 4
        if ($overhead > 0) {
391
            for ($i = 0; $i < $overhead; ++$i) {
392
                $this->printFeedbackItem('S');
393
            }
394
        }
395 4
    }
396
397
    /**
398
     * Prints a single "quick" feedback item and increments
399
     * the total number of processed cases and the column
400
     * position.
401
     *
402
     * @param $item
403
     */
404 11
    protected function printFeedbackItem(string $item)
405
    {
406 11
        echo $item;
407 11
        ++$this->column;
408 11
        ++$this->casesProcessed;
409 11
        if ($this->column === $this->maxColumn) {
410 2
            echo $this->getProgress();
411 2
            $this->println();
412
        }
413 11
    }
414
415
    /**
416
     * Method that returns a formatted string
417
     * for a collection of errors or failures.
418
     *
419
     * @param array $defects
420
     * @param $type
421
     *
422
     * @return string
423
     */
424 5
    protected function getDefects(array $defects, string $type): string
425
    {
426 5
        $count = count($defects);
427 5
        if ($count === 0) {
428 2
            return '';
429
        }
430 5
        $output = sprintf(
431 5
            "There %s %d %s%s:\n",
432 5
            ($count === 1) ? 'was' : 'were',
433 5
            $count,
434 5
            $type,
435 5
            ($count === 1) ? '' : 's'
436
        );
437
438 5
        for ($i = 1; $i <= count($defects); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
439 5
            $output .= sprintf("\n%d) %s\n", $i, $defects[$i - 1]);
440
        }
441
442 5
        return $output;
443
    }
444
445
    /**
446
     * Prints progress for large test collections.
447
     */
448 9
    protected function getProgress(): string
449
    {
450 9
        return sprintf(
451 9
            ' %' . $this->numTestsWidth . 'd / %' . $this->numTestsWidth . 'd (%3s%%)',
452 9
            $this->casesProcessed,
453 9
            $this->totalCases,
454 9
            floor(($this->totalCases ? $this->casesProcessed / $this->totalCases : 0) * 100)
455
        );
456
    }
457
458
    /**
459
     * Get the footer for a test collection that had tests with
460
     * failures or errors.
461
     *
462
     * @return string
463
     */
464 3
    private function getFailedFooter(): string
465
    {
466 3
        $formatString = "FAILURES!\nTests: %d, Assertions: %d, Failures: %d, Errors: %d.\n";
467
468 3
        return "\n" . $this->red(
469 3
            sprintf(
470 3
                $formatString,
471 3
                $this->results->getTotalTests(),
0 ignored issues
show
Documentation Bug introduced by
The method getTotalTests does not exist on object<ParaTest\Logging\LogInterpreter>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
472 3
                $this->results->getTotalAssertions(),
0 ignored issues
show
Documentation Bug introduced by
The method getTotalAssertions does not exist on object<ParaTest\Logging\LogInterpreter>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
473 3
                $this->results->getTotalFailures(),
0 ignored issues
show
Documentation Bug introduced by
The method getTotalFailures does not exist on object<ParaTest\Logging\LogInterpreter>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
474 3
                $this->results->getTotalErrors()
0 ignored issues
show
Documentation Bug introduced by
The method getTotalErrors does not exist on object<ParaTest\Logging\LogInterpreter>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
475
            )
476
        );
477
    }
478
479
    /**
480
     * Get the footer for a test collection containing all successful
481
     * tests.
482
     *
483
     * @return string
484
     */
485 1
    private function getSuccessFooter(): string
486
    {
487 1
        $tests = $this->totalCases;
488 1
        $asserts = $this->results->getTotalAssertions();
0 ignored issues
show
Documentation Bug introduced by
The method getTotalAssertions does not exist on object<ParaTest\Logging\LogInterpreter>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
489
490 1
        if ($this->totalSkippedOrIncomplete > 0) {
491
            // phpunit 4.5 produce NOT plural version for test(s) and assertion(s) in that case
492
            // also it shows result in standard color scheme
493
            return sprintf(
494
                "OK, but incomplete, skipped, or risky tests!\n"
495
                . "Tests: %d, Assertions: %d, Incomplete: %d.\n",
496
                $tests,
497
                $asserts,
498
                $this->totalSkippedOrIncomplete
499
            );
500
        }
501
502
        // phpunit 4.5 produce plural version for test(s) and assertion(s) in that case
503
        // also it shows result as black text on green background
504 1
        return $this->green(sprintf(
505 1
            "OK (%d test%s, %d assertion%s)\n",
506 1
            $tests,
507 1
            ($tests === 1) ? '' : 's',
508 1
            $asserts,
509 1
            ($asserts === 1) ? '' : 's'
510
        ));
511
    }
512
513 1
    private function green(string $text): string
514
    {
515 1
        if ($this->colors) {
516
            return "\x1b[30;42m\x1b[2K"
517
                . $text
518
                . "\x1b[0m\x1b[2K";
519
        }
520
521 1
        return $text;
522
    }
523
524 3
    private function red(string $text): string
525
    {
526 3
        if ($this->colors) {
527
            return "\x1b[37;41m\x1b[2K"
528
                . $text
529
                . "\x1b[0m\x1b[2K";
530
        }
531
532 3
        return $text;
533
    }
534
535
    /**
536
     * Deletes all the temporary log files for ExecutableTest objects
537
     * being printed.
538
     */
539
    private function clearLogs()
540
    {
541
        foreach ($this->suites as $suite) {
542
            $suite->deleteFile();
543
        }
544
    }
545
}
546