Passed
Push — master ( d6f237...737295 )
by Jakub
01:43
created

TestCase::getJobs()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 33
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 9
Bugs 0 Features 0
Metric Value
eloc 25
dl 0
loc 33
ccs 0
cts 24
cp 0
rs 9.2088
c 9
b 0
f 0
cc 5
nc 4
nop 0
crap 30
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MyTester;
6
7
use MyTester\Annotations\PhpAttributesEngine;
8
use MyTester\Annotations\Reader;
9
use ReflectionClass;
10
use ReflectionMethod;
11
12
/**
13
 * One test suite
14
 *
15
 * @author Jakub Konečný
16
 * @property-read Job[] $jobs @internal
17
 */
18
abstract class TestCase
19
{
20
    use \Nette\SmartObject;
21
    use TAssertions;
22
23
    public const RESULT_PASSED = ".";
24
    public const RESULT_SKIPPED = "s";
25
    public const RESULT_FAILED = "F";
26
27
    public const METHOD_PATTERN = '#^test[A-Z0-9_]#';
28
29
    /** @internal */
30
    public const ANNOTATION_TEST = "test";
31
    /** @internal */
32
    public const ANNOTATION_TEST_SUITE = "testSuite";
33
34
    protected SkipChecker $skipChecker;
35
    protected ShouldFailChecker $shouldFailChecker;
36
    protected DataProvider $dataProvider;
37
    protected Reader $annotationsReader;
38
39
    public function __construct()
40
    {
41
        $this->annotationsReader = new Reader();
42
        $this->annotationsReader->registerEngine(new PhpAttributesEngine());
43
        $this->skipChecker = new SkipChecker($this->annotationsReader);
44
        $this->shouldFailChecker = new ShouldFailChecker($this->annotationsReader);
45
        $this->dataProvider = new DataProvider($this->annotationsReader);
46
    }
47
48
    /**
49
     * Get list of jobs with parameters for current test suite
50
     *
51
     * @return Job[]
52
     */
53
    protected function getJobs(): array
54
    {
55
        $jobs = [];
56
        $r = new ReflectionClass(static::class);
57
        $methods = array_values((array) preg_grep(static::METHOD_PATTERN, array_map(function (ReflectionMethod $rm) {
58
            return $rm->getName();
59
        }, $r->getMethods())));
60
        foreach ($methods as $method) {
61
            $reflection = new ReflectionMethod(static::class, $method);
62
            if (!$reflection->isPublic()) {
63
                continue;
64
            }
65
            /** @var callable $callback */
66
            $callback = [$this, $method];
67
            $job = [
68
                "name" => $this->getJobName(static::class, $method),
69
                "callback" => $callback,
70
                "params" => [],
71
                "skip" => $this->skipChecker->shouldSkip(static::class, $method),
72
                "shouldFail" => $this->shouldFailChecker->shouldFail(static::class, $method),
73
            ];
74
            $data = $this->dataProvider->getData($this, $method);
75
            if (count($data) > 0) {
76
                foreach ($data as $value) {
77
                    $job["params"][0] = $value;
78
                    $jobs[] = new Job(... $job);
79
                    $job["params"] = [];
80
                }
81
            } else {
82
                $jobs[] = new Job($job["name"], $job["callback"], $job["params"], $job["skip"], $job["shouldFail"]);
83
            }
84
        }
85
        return $jobs;
86
    }
87
88
    /**
89
     * Get name of current test suite
90
     */
91
    protected function getSuiteName(): string
92
    {
93
        $annotation = $this->annotationsReader->getAnnotation(static::ANNOTATION_TEST_SUITE, static::class);
94
        if ($annotation !== null) {
95
            return $annotation;
96
        }
97
        return static::class;
98
    }
99
100
    /**
101
     * Get name for a job
102
     */
103
    protected function getJobName(string|object $class, string $method): string
104
    {
105
        $annotation = $this->annotationsReader->getAnnotation(static::ANNOTATION_TEST, $class, $method);
106
        if ($annotation !== null) {
107
            return $annotation;
108
        }
109
        return $this->getSuiteName() . "::" . $method;
110
    }
111
112
    /**
113
     * Called at start of the suite
114
     */
115
    public function startUp(): void
116
    {
117
    }
118
119
    /**
120
     * Called at end of the suite
121
     */
122
    public function shutDown(): void
123
    {
124
    }
125
126
    /**
127
     * Called before each job
128
     */
129
    public function setUp(): void
130
    {
131
    }
132
133
    /**
134
     * Called after each job
135
     */
136
    public function tearDown(): void
137
    {
138
    }
139
140
    protected function runJob(Job $job): string
141
    {
142
        $this->resetCounter();
143
        if (!$job->skip) {
144
            $this->setUp();
145
        }
146
        $this->shouldFail = $job->shouldFail;
147
        $job->execute();
148
        if (!$job->skip) {
149
            $this->tearDown();
150
        }
151
        $this->shouldFail = false;
152
        $this->resetCounter();
153
        return match ($job->result) {
154
            Job::RESULT_PASSED => static::RESULT_PASSED,
155
            Job::RESULT_SKIPPED => static::RESULT_SKIPPED,
156
            Job::RESULT_FAILED => static::RESULT_FAILED,
157
            default => "",
158
        };
159
    }
160
161
    /**
162
     * Runs the test suite
163
     */
164
    public function run(): bool
165
    {
166
        $this->startUp();
167
        $jobs = $this->getJobs();
168
        $passed = true;
169
        foreach ($jobs as $job) {
170
            $this->runJob($job);
171
            if ($job->result === Job::RESULT_FAILED) {
172
                $passed = false;
173
            }
174
        }
175
        $this->shutDown();
176
        return $passed;
177
    }
178
}
179