1
|
|
|
<?php |
|
|
|
|
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Hgraca\DoctrineTestDbRegenerationBundle\EventSubscriber; |
6
|
|
|
|
7
|
|
|
use Exception; |
8
|
|
|
use Hgraca\DoctrineTestDbRegenerationBundle\Doctrine\SchemaManager; |
9
|
|
|
use Hgraca\DoctrineTestDbRegenerationBundle\Doctrine\SchemaManagerInterface; |
10
|
|
|
use PHPUnit\Framework\TestListener; |
11
|
|
|
use PHPUnit\Framework\TestListenerDefaultImplementation; |
12
|
|
|
|
13
|
|
|
if ( |
14
|
|
|
class_exists('PHPUnit_Runner_Version') |
15
|
|
|
&& version_compare(PHPUnit_Runner_Version::id(), '5', '>=') |
16
|
|
|
&& version_compare(PHPUnit_Runner_Version::id(), '6', '<') |
17
|
|
|
) { |
18
|
|
|
// PHPUnit 5 |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @codeCoverageIgnore We can never cover both of these classes, although we do cover the trait |
22
|
|
|
*/ |
23
|
|
|
class DbRegenerationPHPUnitEventSubscriber extends \PHPUnit_Framework_BaseTestListener |
|
|
|
|
24
|
|
|
{ |
25
|
|
|
use DbRegenerationPHPUnitEventSubscriberTrait; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @throws \Exception |
29
|
|
|
*/ |
30
|
|
|
public function startTest(\PHPUnit_Framework_Test $test): void |
|
|
|
|
31
|
|
|
{ |
32
|
|
|
$this->onTestStart($test); |
33
|
|
|
} |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @throws \Exception |
37
|
|
|
*/ |
38
|
|
|
public function endTest(\PHPUnit_Framework_Test $test, $time): void |
39
|
|
|
{ |
40
|
|
|
$this->onEndTest($test); |
41
|
|
|
} |
42
|
|
|
} |
43
|
|
|
} elseif (class_exists('\PHPUnit\Runner\Version') && version_compare(\PHPUnit\Runner\Version::id(), '7', '<')) { |
44
|
|
|
// PHPUnit 6+ |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @codeCoverageIgnore We can never cover both of these classes, although we do cover the trait |
48
|
|
|
*/ |
49
|
|
|
class DbRegenerationPHPUnitEventSubscriber implements TestListener |
|
|
|
|
50
|
|
|
{ |
51
|
|
|
use TestListenerDefaultImplementation, |
52
|
|
|
DbRegenerationPHPUnitEventSubscriberTrait; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* @throws \Exception |
56
|
|
|
*/ |
57
|
|
|
public function startTest(\PHPUnit\Framework\Test $test): void |
58
|
|
|
{ |
59
|
|
|
$this->onTestStart($test); |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @throws \Exception |
64
|
|
|
*/ |
65
|
|
|
public function endTest(\PHPUnit\Framework\Test $test, $time): void |
66
|
|
|
{ |
67
|
|
|
$this->onEndTest($test); |
68
|
|
|
} |
69
|
|
|
} |
70
|
|
|
} elseif (class_exists('\PHPUnit\Runner\Version') && version_compare(\PHPUnit\Runner\Version::id(), '7', '>=')) { |
71
|
|
|
// PHPUnit 7+ |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @codeCoverageIgnore We can never cover both of these classes, although we do cover the trait |
75
|
|
|
*/ |
76
|
|
|
class DbRegenerationPHPUnitEventSubscriber implements TestListener |
|
|
|
|
77
|
|
|
{ |
78
|
|
|
use TestListenerDefaultImplementation, |
79
|
|
|
DbRegenerationPHPUnitEventSubscriberTrait; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @throws \Exception |
83
|
|
|
*/ |
84
|
|
|
public function startTest(\PHPUnit\Framework\Test $test): void |
85
|
|
|
{ |
86
|
|
|
$this->onTestStart($test); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @throws \Exception |
91
|
|
|
*/ |
92
|
|
|
public function endTest(\PHPUnit\Framework\Test $test, float $time): void |
93
|
|
|
{ |
94
|
|
|
$this->onEndTest($test); |
95
|
|
|
} |
96
|
|
|
} |
97
|
|
|
} else { |
98
|
|
|
throw new Exception('DoctrineTestDbRegenerationBundle doesn\'t work with such an old version of PHPUnit.'); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
trait DbRegenerationPHPUnitEventSubscriberTrait |
|
|
|
|
102
|
|
|
{ |
103
|
|
|
/** |
104
|
|
|
* @var bool |
105
|
|
|
*/ |
106
|
|
|
private $hasCreatedTestDatabaseBackup = false; |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* @var bool |
110
|
|
|
*/ |
111
|
|
|
private $hasRestoredTestDatabase = false; |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* @var SchemaManagerInterface |
115
|
|
|
*/ |
116
|
|
|
private static $schemaManager; |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* @var bool |
120
|
|
|
*/ |
121
|
|
|
private $shouldRegenerateDbOnEveryTest; |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* @var bool |
125
|
|
|
*/ |
126
|
|
|
private $shouldRemoveDbAfterEveryTest; |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* @var int |
130
|
|
|
*/ |
131
|
|
|
private $shouldReuseExistingDbBkp; |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @var array |
135
|
|
|
*/ |
136
|
|
|
private $executeMigrations; |
137
|
|
|
|
138
|
16 |
|
public function __construct( |
139
|
|
|
int $shouldRemoveDbAfterEveryTest = 1, |
140
|
|
|
int $shouldRegenerateDbOnEveryTest = 1, |
141
|
|
|
int $shouldReuseExistingDbBkp = 0, |
142
|
|
|
array $executeMigrations = [], |
143
|
|
|
SchemaManagerInterface $schemaManager = null |
144
|
|
|
) { |
145
|
16 |
|
$this->shouldRemoveDbAfterEveryTest = (bool) $shouldRemoveDbAfterEveryTest; |
146
|
16 |
|
$this->shouldRegenerateDbOnEveryTest = (bool) $shouldRegenerateDbOnEveryTest; |
147
|
16 |
|
$this->shouldReuseExistingDbBkp = (bool) $shouldReuseExistingDbBkp; |
|
|
|
|
148
|
16 |
|
$this->executeMigrations = $executeMigrations; |
149
|
16 |
|
self::$schemaManager = $schemaManager; |
150
|
16 |
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* @throws \Exception |
154
|
|
|
*/ |
155
|
8 |
|
private function onTestStart($test): void |
156
|
|
|
{ |
157
|
8 |
|
if (!$test instanceof DatabaseAwareTestInterface) { |
158
|
4 |
|
return; |
159
|
|
|
} |
160
|
|
|
|
161
|
4 |
|
if (!$this->hasCreatedTestDatabaseBackup()) { |
162
|
4 |
|
$this->createTestDatabaseBackup($this->shouldReuseExistingDbBkp); |
|
|
|
|
163
|
|
|
} |
164
|
|
|
|
165
|
4 |
|
if (!$this->hasRestoredTestDatabase() || $this->shouldRegenerateDbOnEveryTest) { |
166
|
4 |
|
$this->restoreTestDatabase(); |
167
|
|
|
} |
168
|
4 |
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* At the end of each test that used the DB, |
172
|
|
|
* we remove the dirty DB just in case we forget |
173
|
|
|
* to add the interface to another test and it |
174
|
|
|
* ends up using a dirty DB, yielding unreliable results. |
175
|
|
|
* Its safer but slower though, so we can turn it off if we want to. |
176
|
|
|
* |
177
|
|
|
* @throws \ErrorException |
178
|
|
|
*/ |
179
|
8 |
|
public function onEndTest($test): void |
180
|
|
|
{ |
181
|
8 |
|
if (!$this->shouldRemoveDbAfterEveryTest || !$test instanceof DatabaseAwareTestInterface) { |
182
|
4 |
|
return; |
183
|
|
|
} |
184
|
|
|
|
185
|
4 |
|
if ($this->shouldRegenerateDbOnEveryTest) { |
186
|
2 |
|
$this->getSchemaManager()->removeTestDatabase(); |
187
|
|
|
} |
188
|
4 |
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* @throws \ErrorException |
192
|
|
|
*/ |
193
|
6 |
|
public static function getSchemaManager(): SchemaManagerInterface |
194
|
|
|
{ |
195
|
6 |
|
return self::$schemaManager ?? self::$schemaManager = SchemaManager::constructUsingTestContainer(); |
196
|
|
|
} |
197
|
|
|
|
198
|
4 |
|
private function hasCreatedTestDatabaseBackup(): bool |
199
|
|
|
{ |
200
|
4 |
|
return $this->hasCreatedTestDatabaseBackup; |
201
|
|
|
} |
202
|
|
|
|
203
|
4 |
|
private function createdTestDatabaseBackup(): void |
204
|
|
|
{ |
205
|
4 |
|
$this->hasCreatedTestDatabaseBackup = true; |
206
|
4 |
|
} |
207
|
|
|
|
208
|
4 |
|
private function hasRestoredTestDatabase(): bool |
209
|
|
|
{ |
210
|
4 |
|
return $this->hasRestoredTestDatabase; |
211
|
|
|
} |
212
|
|
|
|
213
|
4 |
|
private function restoredTestDatabase(): void |
214
|
|
|
{ |
215
|
4 |
|
$this->hasRestoredTestDatabase = true; |
216
|
4 |
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* @throws \ErrorException |
220
|
|
|
*/ |
221
|
4 |
|
private function createTestDatabaseBackup(bool $shouldReuseExistingDbBkp = false): void |
222
|
|
|
{ |
223
|
4 |
|
$this->switchOffDoctrineTestBundleStaticDriver(); |
224
|
4 |
|
$this->getSchemaManager()->createTestDatabaseBackup($shouldReuseExistingDbBkp, $this->executeMigrations); |
225
|
4 |
|
$this->createdTestDatabaseBackup(); |
226
|
4 |
|
$this->switchOnDoctrineTestBundleStaticDriver(); |
227
|
4 |
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* @throws \ErrorException |
231
|
|
|
*/ |
232
|
4 |
|
private function restoreTestDatabase(): void |
233
|
|
|
{ |
234
|
4 |
|
$this->getSchemaManager()->restoreTestDatabase(); |
235
|
4 |
|
$this->restoredTestDatabase(); |
236
|
4 |
|
} |
237
|
|
|
|
238
|
4 |
|
private function switchOffDoctrineTestBundleStaticDriver(): void |
239
|
|
|
{ |
240
|
4 |
|
if (\class_exists('DAMA\DoctrineTestBundle\Doctrine\DBAL\StaticDriver')) { |
241
|
|
|
\DAMA\DoctrineTestBundle\Doctrine\DBAL\StaticDriver::setKeepStaticConnections(false); |
|
|
|
|
242
|
|
|
} |
243
|
4 |
|
} |
244
|
|
|
|
245
|
4 |
|
private function switchOnDoctrineTestBundleStaticDriver(): void |
246
|
|
|
{ |
247
|
4 |
|
if (\class_exists('DAMA\DoctrineTestBundle\Doctrine\DBAL\StaticDriver')) { |
248
|
|
|
\DAMA\DoctrineTestBundle\Doctrine\DBAL\StaticDriver::setKeepStaticConnections(true); |
249
|
|
|
} |
250
|
4 |
|
} |
251
|
|
|
} |
252
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.