|
10 | 10 |
|
11 | 11 | use OC\App\AppStore\Fetcher\AppFetcher; |
12 | 12 | use OC\Command\QueueBus; |
| 13 | +use OC\DB\Connection; |
13 | 14 | use OC\Files\AppData\Factory; |
14 | 15 | use OC\Files\Cache\Storage; |
15 | 16 | use OC\Files\Config\MountProviderCollection; |
|
42 | 43 | abstract class TestCase extends \PHPUnit\Framework\TestCase { |
43 | 44 | private QueueBus $commandBus; |
44 | 45 |
|
45 | | - /** @psalm-suppress ImpureStaticProperty For tests it's not an issue */ |
46 | | - protected static ?IDBConnection $realDatabase = null; |
47 | 46 | /** @psalm-suppress ImpureStaticProperty */ |
48 | | - private static bool $wasDatabaseAllowed = false; |
49 | 47 | protected array $services = []; |
50 | 48 |
|
51 | 49 | #[\Override] |
52 | 50 | protected function onNotSuccessfulTest(\Throwable $t): never { |
53 | 51 | $this->restoreAllServices(); |
54 | 52 |
|
55 | | - // restore database connection |
56 | | - if (!$this->IsDatabaseAccessAllowed()) { |
57 | | - \OC::$server->registerService(IDBConnection::class, function () { |
58 | | - return self::$realDatabase; |
59 | | - }); |
60 | | - } |
61 | | - |
62 | 53 | parent::onNotSuccessfulTest($t); |
63 | 54 | } |
64 | 55 |
|
@@ -134,38 +125,37 @@ protected function setUp(): void { |
134 | 125 | $this->overwriteService('AsyncCommandBus', $this->commandBus); |
135 | 126 | $this->overwriteService(IBus::class, $this->commandBus); |
136 | 127 |
|
137 | | - // detect database access |
138 | | - self::$wasDatabaseAllowed = true; |
139 | | - if (!$this->IsDatabaseAccessAllowed()) { |
140 | | - self::$wasDatabaseAllowed = false; |
141 | | - if (is_null(self::$realDatabase)) { |
142 | | - self::$realDatabase = Server::get(IDBConnection::class); |
143 | | - } |
144 | | - /** @psalm-suppress InternalMethod */ |
145 | | - \OC::$server->registerService(IDBConnection::class, function (): void { |
146 | | - $this->fail('Your test case is not allowed to access the database.'); |
147 | | - }); |
148 | | - } |
149 | | - |
150 | 128 | $traits = $this->getTestTraits(); |
151 | 129 | foreach ($traits as $trait) { |
152 | 130 | $methodName = 'setUp' . basename(str_replace('\\', '/', $trait)); |
153 | 131 | if (method_exists($this, $methodName)) { |
154 | 132 | call_user_func([$this, $methodName]); |
155 | 133 | } |
156 | 134 | } |
| 135 | + |
| 136 | + // Something outside of our test might have done something with the database already, but we ignore it. |
| 137 | + $this->resetDatabaseStats(); |
157 | 138 | } |
158 | 139 |
|
159 | 140 | #[\Override] |
160 | 141 | protected function tearDown(): void { |
161 | 142 | $this->restoreAllServices(); |
162 | 143 |
|
163 | | - // restore database connection |
164 | | - if (!$this->IsDatabaseAccessAllowed()) { |
165 | | - /** @psalm-suppress InternalMethod */ |
166 | | - \OC::$server->registerService(IDBConnection::class, function () { |
167 | | - return self::$realDatabase; |
168 | | - }); |
| 144 | + $databaseAllowed = $this->IsDatabaseAccessAllowed(); |
| 145 | + $databaseUsed = $this->wasDatabaseUsed(); |
| 146 | + $this->resetDatabaseStats(); |
| 147 | + if ($databaseUsed && !$databaseAllowed) { |
| 148 | + $this->fail('The database was used, but it was not allowed'); |
| 149 | + } |
| 150 | + // Some test methods using a data provider generate cases that both use and not use the DB |
| 151 | + // Therefore we can't enable this check all the time. |
| 152 | + // It's useful for manually checking if all tests are using the correct group though. |
| 153 | + // 1. DB_UNUSED=1 ./autotest.sh sqlite | grep -B 1 "The database was not used, but it was allowed" | grep "::" | cut -d ":" -f 1 | cut -d " " -f 2 | sort -u |
| 154 | + // 2. Remove the Group attribute from all classes from the output |
| 155 | + // 3. ./autotest.sh sqlite | grep -B 1 "The database was used, but it was not allowed" | grep "::" | cut -d " " -f 2 | sort -u |
| 156 | + // 4. Add the Group attribute to all methods from the output |
| 157 | + if (!$databaseUsed && $databaseAllowed && getenv('DB_UNUSED') !== false) { |
| 158 | + $this->fail('The database was not used, but it was allowed'); |
169 | 159 | } |
170 | 160 |
|
171 | 161 | // further cleanup |
@@ -298,26 +288,17 @@ public function filterClassMethods(string $className, array $filterMethods): arr |
298 | 288 |
|
299 | 289 | #[\Override] |
300 | 290 | public static function tearDownAfterClass(): void { |
301 | | - if (!self::$wasDatabaseAllowed && self::$realDatabase !== null) { |
302 | | - // in case an error is thrown in a test, PHPUnit jumps straight to tearDownAfterClass, |
303 | | - // so we need the database again |
304 | | - \OC::$server->registerService(IDBConnection::class, function () { |
305 | | - return self::$realDatabase; |
306 | | - }); |
307 | | - } |
308 | 291 | $dataDir = Server::get(IConfig::class)->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data-autotest'); |
309 | | - if (self::$wasDatabaseAllowed && Server::get(IDBConnection::class)) { |
310 | | - $db = Server::get(IDBConnection::class); |
311 | | - if ($db->inTransaction()) { |
312 | | - $db->rollBack(); |
313 | | - throw new \Exception('There was a transaction still in progress and needed to be rolled back. Please fix this in your test.'); |
314 | | - } |
315 | | - $queryBuilder = $db->getQueryBuilder(); |
316 | | - |
317 | | - self::tearDownAfterClassCleanShares($queryBuilder); |
318 | | - self::tearDownAfterClassCleanStorages($queryBuilder); |
319 | | - self::tearDownAfterClassCleanFileCache($queryBuilder); |
| 292 | + $db = Server::get(IDBConnection::class); |
| 293 | + if ($db->inTransaction()) { |
| 294 | + $db->rollBack(); |
| 295 | + self::fail('There was a transaction still in progress and needed to be rolled back. Please fix this in your test.'); |
320 | 296 | } |
| 297 | + |
| 298 | + $queryBuilder = $db->getQueryBuilder(); |
| 299 | + self::tearDownAfterClassCleanShares($queryBuilder); |
| 300 | + self::tearDownAfterClassCleanStorages($queryBuilder); |
| 301 | + self::tearDownAfterClassCleanFileCache($queryBuilder); |
321 | 302 | self::tearDownAfterClassCleanStrayDataFiles($dataDir); |
322 | 303 | self::tearDownAfterClassCleanStrayHooks(); |
323 | 304 | self::tearDownAfterClassCleanStrayLocks(); |
@@ -556,4 +537,16 @@ protected function IsDatabaseAccessAllowed(): bool { |
556 | 537 | $annotations = $this->getGroupAnnotations(); |
557 | 538 | return in_array('DB', $annotations) || in_array('SLOWDB', $annotations); |
558 | 539 | } |
| 540 | + |
| 541 | + private function wasDatabaseUsed(): bool { |
| 542 | + $connection = Server::get(Connection::class); |
| 543 | + $databaseStats = $connection->getStats(); |
| 544 | + return $databaseStats['built'] > 0 || $databaseStats['executed'] > 0; |
| 545 | + } |
| 546 | + |
| 547 | + private function resetDatabaseStats(): void { |
| 548 | + $connection = Server::get(Connection::class); |
| 549 | + self::invokePrivate($connection, 'queriesBuilt', [0]); |
| 550 | + self::invokePrivate($connection, 'queriesExecuted', [0]); |
| 551 | + } |
559 | 552 | } |
0 commit comments