Issues (4)

src/Job.php (1 issue)

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