Passed
Push — master ( 492f4c...0725f5 )
by Jakub
02:01
created

TestCase::run()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.0123

Importance

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