Issues (9)

src/Job.php (4 issues)

Severity
1
<?php
2
declare(strict_types=1);
3
4
namespace MyTester;
5
6
use Ayesh\PHP_Timer\Timer;
7
use Psr\EventDispatcher\EventDispatcherInterface;
8
use Throwable;
9
use TypeError;
10
11
/**
12
 * One job of a test suite
13
 *
14
 * @author Jakub Konečný
15
 * @property-read callable $callback
16
 * @property-read bool|string $skip
17
 * @property-read JobResult $result
18
 * @property-read string $output @internal
19
 * @property-read int $totalTime Total elapsed time in milliseconds
20
 * @property-read Throwable|null $exception
21
 * @property-read string $nameWithDataSet Job's name + data set (or its custom name)
22
 */
23
final class Job
24
{
25
    use \Nette\SmartObject;
26
27
    /** @var callable Task */
28
    private $callback;
29
    private JobResult $result = JobResult::PASSED;
30
    private string $output = "";
31
    /** @var int Total elapsed time in milliseconds */
32
    private int $totalTime = 0;
33
    /**
34
     * @internal
35
     */
36
    public int $totalAssertions = 0;
37
    private Throwable|null $exception = null;
38
    private EventDispatcherInterface $eventDispatcher;
39
40
    /**
41
     * @param mixed[] $params
42
     * @param callable[] $onAfterExecute
43
     */
44 1
    public function __construct(
45
        public readonly string $name,
46
        callable $callback,
47
        public readonly array $params = [],
48
        private bool|string $skip = false,
49
        public array $onAfterExecute = [],
50
        public readonly string $dataSetName = "",
51
        public readonly bool $reportDeprecations = true,
52
        public readonly int $maxRetries = 0
53
    ) {
54 1
        if (count($this->onAfterExecute) > 0) {
55
            trigger_error("Using " . self::class . "::]\$onAfterExecute is deprecated, add a listener for event " . Events\TestJobFinished::class . " instead", E_USER_DEPRECATED);
0 ignored issues
show
Line exceeds 120 characters; contains 179 characters
Loading history...
56
        }
57 1
        $this->callback = $callback;
58 1
    }
59
60
    protected function getCallback(): callable
61
    {
62 1
        return $this->callback;
63
    }
64
65
    protected function getSkip(): bool|string
66
    {
67 1
        return $this->skip;
68
    }
69
70
    protected function getResult(): JobResult
71
    {
72 1
        return $this->result;
73
    }
74
75
    protected function getOutput(): string
76
    {
77 1
        return $this->output;
78
    }
79
80
    protected function getTotalTime(): int
81
    {
82 1
        return $this->totalTime;
83
    }
84
85
    protected function getException(): ?\Throwable
86
    {
87 1
        return $this->exception;
88
    }
89
90
    protected function getNameWithDataSet(): string
91
    {
92 1
        $jobName = $this->name;
93 1
        if (count($this->params) > 0) {
94 1
            $jobName .= " with data set ";
95 1
            if ($this->dataSetName !== "") {
96 1
                $jobName .= $this->dataSetName;
97
            } else {
98 1
                $jobName .= "(" . implode(", ", $this->params) . ")";
99
            }
100
        }
101 1
        return $jobName;
102
    }
103
104
    /**
105
     * @internal
106
     */
107
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): void
108
    {
109 1
        $this->eventDispatcher = $eventDispatcher;
110 1
    }
111
112
    /**
113
     * @deprecated
114
     */
115
    private function onAfterExecute(): void
116
    {
117 1
        foreach ($this->onAfterExecute as $callback) {
118
            $callback($this);
119
        }
120 1
    }
121
122
    /**
123
     * Executes the task
124
     */
125
    public function execute(): void
0 ignored issues
show
Function's cyclomatic complexity (16) exceeds 10; consider refactoring the function
Loading history...
126
    {
127 1
        for ($attemptNumber = 0; $attemptNumber <= $this->maxRetries; $attemptNumber++) {
128 1
            if ($this->skip === false) {
129 1
                $previousAttemptsAssertions = 0;
130 1
                if (is_array($this->callback) && isset($this->callback[0]) && $this->callback[0] instanceof TestCase) {
131 1
                    $previousAttemptsAssertions = $this->callback[0]->getCounter();
132
                }
133 1
                $timerName = $this->name . time();
134 1
                Timer::start($timerName);
135 1
                ob_start();
136 1
                set_error_handler(
137 1
                    function (int $errno, string $errstr, string $errfile, int $errline): bool {
138 1
                        if ($this->reportDeprecations) {
139 1
                            $this->eventDispatcher->dispatch(new Events\DeprecationTriggered($errstr, $errfile, $errline));
0 ignored issues
show
Line exceeds 120 characters; contains 123 characters
Loading history...
140
                        }
141 1
                        return true;
142 1
                    },
143 1
                    E_USER_DEPRECATED
144
                );
145
                try {
146
                    try {
147 1
                        call_user_func_array($this->callback, $this->params);
148 1
                    } catch (TypeError $e) {
149
                        if (
150
                            isset($e->getTrace()[0]) &&
151
                            isset($e->getTrace()[0]["class"]) && $e->getTrace()[0]["class"] === TestCase::class &&
152
                            isset($e->getTrace()[0]["function"]) && str_starts_with($e->getTrace()[0]["function"], "assert")
0 ignored issues
show
Line exceeds 120 characters; contains 124 characters
Loading history...
153
                        ) {
154
                            /** @var array{0: TestCase, 1: string}&callable $callback */
155
                            $callback = $this->callback;
156
                            throw new AssertionFailedException(
157
                                "Invalid value passed to an assertion.",
158
                                $callback[0]->getCounter() + 1,
159
                                $e
160
                            );
161
                        }
162 1
                        throw $e;
163
                    }
164 1
                } catch (SkippedTestException $e) {
165 1
                    $this->skip = ($e->getMessage() !== "") ? $e->getMessage() : true;
166 1
                } catch (IncompleteTestException $e) {
167 1
                    $message = $e->getMessage() !== "" ? $e->getMessage() : "incomplete";
168 1
                    echo "Warning: $message\n";
169 1
                } catch (AssertionFailedException $e) {
170 1
                    echo $e->getMessage();
171 1
                    $this->exception = $e;
172 1
                } catch (Throwable $e) {
173 1
                    echo "Error: " . ($e->getMessage() !== "" ? $e->getMessage() : $e::class) . "\n";
174 1
                    echo "Trace:\n" . $e->getTraceAsString() . "\n";
175 1
                    $this->exception = $e;
176
                }
177 1
                if (is_array($this->callback) && isset($this->callback[0]) && $this->callback[0] instanceof TestCase) {
178 1
                    $this->totalAssertions = $this->callback[0]->getCounter() - $previousAttemptsAssertions;
179
                }
180 1
                $this->eventDispatcher->dispatch(new Events\TestJobFinished($this));
181 1
                $this->onAfterExecute(); // @phpstan-ignore method.deprecated
182 1
                restore_error_handler();
183 1
                $this->output = (string) ob_get_clean();
184 1
                Timer::stop($timerName);
185
                // @phpstan-ignore argument.type, cast.int
186 1
                $this->totalTime = (int) Timer::read($timerName, Timer::FORMAT_PRECISE);
187
            }
188 1
            $this->result = JobResult::fromJob($this);
189 1
            if ($this->result !== JobResult::FAILED) {
190 1
                break;
191
            }
192
        }
193 1
    }
194
}
195