Skip to content

Commit c65cf21

Browse files
Merge pull request liip#476 from alexislefebvre/2.x-add-tests-with-mysql-database-cache
2.x: Fix issues with MySQL database backup
2 parents 35cf946 + 92b616a commit c65cf21

14 files changed

Lines changed: 233 additions & 75 deletions

.travis.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,7 @@ install:
4141

4242
# In phpunit.xml.dist, tests annotated with "@group mysql" are excluded,
4343
# revert this.
44-
script: php ./vendor/bin/phpunit --exclude-group ""
44+
# Run tests twice to ensure that tests are idempotent even if database caching is enabled
45+
script:
46+
- php ./vendor/bin/phpunit --exclude-group ""
47+
- php ./vendor/bin/phpunit --exclude-group ""

src/Services/DatabaseBackup/AbstractDatabaseBackup.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,23 @@ abstract class AbstractDatabaseBackup implements DatabaseBackupInterface
3333
*
3434
* @var array
3535
*/
36-
protected $classNames;
36+
protected $classNames = [];
3737

3838
public function __construct(ContainerInterface $container, FixturesLoaderFactory $fixturesLoaderFactory)
3939
{
4040
$this->container = $container;
4141
$this->fixturesLoaderFactory = $fixturesLoaderFactory;
4242
}
4343

44-
public function init(array $metadatas, array $classNames): void
44+
public function init(array $metadatas, array $classNames, bool $append = false): void
4545
{
4646
$this->metadatas = $metadatas;
47-
$this->classNames = $classNames;
47+
48+
if ($append) {
49+
$this->classNames = array_merge($this->classNames, $classNames);
50+
} else {
51+
$this->classNames = $classNames;
52+
}
4853
}
4954

5055
/**

src/Services/DatabaseBackup/DatabaseBackupInterface.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
*/
1919
interface DatabaseBackupInterface
2020
{
21-
public function init(array $metadatas, array $classNames): void;
21+
public function init(array $metadatas, array $classNames, bool $append = false): void;
2222

2323
public function getBackupFilePath(): string;
2424

2525
public function isBackupActual(): bool;
2626

2727
public function backup(AbstractExecutor $executor): void;
2828

29-
public function restore(AbstractExecutor $executor): void;
29+
public function restore(AbstractExecutor $executor, array $excludedTables = []): void;
3030
}

src/Services/DatabaseBackup/MongodbDatabaseBackup.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,6 @@ final class MongodbDatabaseBackup extends AbstractDatabaseBackup implements Data
2525

2626
protected static $databases;
2727

28-
public function init(array $metadatas, array $classNames): void
29-
{
30-
$this->metadatas = $metadatas;
31-
$this->classNames = $classNames;
32-
}
33-
3428
public function getBackupFilePath(): string
3529
{
3630
return $this->container->getParameter('kernel.cache_dir').'/test_mongodb_'.md5(serialize($this->metadatas).serialize($this->classNames));
@@ -90,7 +84,7 @@ public function backup(AbstractExecutor $executor): void
9084
self::$metadata = $dm->getMetadataFactory()->getLoadedMetadata();
9185
}
9286

93-
public function restore(AbstractExecutor $executor): void
87+
public function restore(AbstractExecutor $executor, array $excludedTables = []): void
9488
{
9589
/** @var DocumentManager $dm */
9690
$dm = $executor->getReferenceRepository()->getManager();

src/Services/DatabaseBackup/MysqlDatabaseBackup.php

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ final class MysqlDatabaseBackup extends AbstractDatabaseBackup implements Databa
2222
{
2323
protected static $referenceData;
2424

25-
protected static $sql;
26-
2725
protected static $metadata;
2826

2927
protected static $schemaUpdatedFlag = false;
@@ -40,11 +38,7 @@ public function getReferenceBackupFilePath(): string
4038

4139
protected function getBackup()
4240
{
43-
if (empty(self::$sql)) {
44-
self::$sql = file_get_contents($this->getBackupFilePath());
45-
}
46-
47-
return self::$sql;
41+
return file_get_contents($this->getBackupFilePath());
4842
}
4943

5044
protected function getReferenceBackup(): string
@@ -84,7 +78,7 @@ public function backup(AbstractExecutor $executor): void
8478
$executor->getReferenceRepository()->save($this->getBackupFilePath());
8579
self::$metadata = $em->getMetadataFactory()->getLoadedMetadata();
8680

87-
exec("mysqldump -h $dbHost -u $dbUser -p$dbPass --no-create-info --skip-triggers --no-create-db --no-tablespaces --compact $dbName > {$this->getBackupFilePath()}");
81+
exec("MYSQL_PWD=$dbPass mysqldump --host $dbHost --port=$dbPort --user $dbUser --no-create-info --skip-triggers --no-create-db --no-tablespaces --compact $dbName > {$this->getBackupFilePath()}");
8882
}
8983

9084
protected function updateSchemaIfNeed(EntityManager $em)
@@ -100,7 +94,7 @@ protected function updateSchemaIfNeed(EntityManager $em)
10094
}
10195
}
10296

103-
public function restore(AbstractExecutor $executor): void
97+
public function restore(AbstractExecutor $executor, array $excludedTables = []): void
10498
{
10599
/** @var EntityManager $em */
106100
$em = $executor->getReferenceRepository()->getManager();
@@ -110,10 +104,23 @@ public function restore(AbstractExecutor $executor): void
110104
$this->updateSchemaIfNeed($em);
111105
$truncateSql = [];
112106
foreach ($this->metadatas as $classMetadata) {
113-
$truncateSql[] = 'DELETE FROM '.$classMetadata->table['name']; // in small tables it's really faster than truncate
107+
$tableName = $classMetadata->table['name'];
108+
109+
if (!in_array($tableName, $excludedTables)) {
110+
$truncateSql[] = 'DELETE FROM '.$tableName; // in small tables it's really faster than truncate
111+
}
114112
}
115-
$connection->query(implode(';', $truncateSql));
116-
$connection->query($this->getBackup());
113+
if (!empty($truncateSql)) {
114+
$connection->query(implode(';', $truncateSql));
115+
}
116+
117+
// Only run query if it exists, to avoid the following exception:
118+
// SQLSTATE[42000]: Syntax error or access violation: 1065 Query was empty
119+
$backup = $this->getBackup();
120+
if (!empty($backup)) {
121+
$connection->query($backup);
122+
}
123+
117124
$connection->query('SET FOREIGN_KEY_CHECKS = 1;');
118125

119126
if (self::$metadata) {

src/Services/DatabaseBackup/SqliteDatabaseBackup.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public function backup(AbstractExecutor $executor): void
5858
copy($this->getDatabaseName($connection), $this->getBackupFilePath());
5959
}
6060

61-
public function restore(AbstractExecutor $executor): void
61+
public function restore(AbstractExecutor $executor, array $excludedTables = []): void
6262
{
6363
/** @var EntityManager $em */
6464
$em = $executor->getReferenceRepository()->getManager();

src/Services/DatabaseTools/ORMDatabaseTool.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,9 @@ public function loadFixtures(array $classNames = [], bool $append = false): Abst
9696
$this->createDatabaseIfNotExists();
9797

9898
$backupService = $this->getBackupService();
99+
99100
if ($backupService) {
100-
$backupService->init($this->getMetadatas(), $classNames);
101+
$backupService->init($this->getMetadatas(), $classNames, $append);
101102

102103
if ($backupService->isBackupActual()) {
103104
if (null !== $this->connection) {
@@ -110,7 +111,7 @@ public function loadFixtures(array $classNames = [], bool $append = false): Abst
110111
$this->webTestCase->preFixtureBackupRestore($this->om, $referenceRepository, $backupService->getBackupFilePath());
111112
$executor = $this->getExecutor($this->getPurger());
112113
$executor->setReferenceRepository($referenceRepository);
113-
$backupService->restore($executor);
114+
$backupService->restore($executor, $this->excludedDoctrineTables);
114115
$this->webTestCase->postFixtureBackupRestore($backupService->getBackupFilePath());
115116

116117
return $executor;

tests/AppConfigLeanFramework/AppConfigLeanFrameworkKernel.php

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,6 @@
1313

1414
namespace Liip\FunctionalTestBundle\Tests\AppConfigLeanFramework;
1515

16-
/*
17-
* This file is part of the Liip/FunctionalTestBundle
18-
*
19-
* (c) Lukas Kahwe Smith <smith@pooteeweet.org>
20-
*
21-
* This source file is subject to the MIT license that is bundled
22-
* with this source code in the file LICENSE.
23-
*/
24-
2516
use Liip\FunctionalTestBundle\Tests\App\AppKernel;
2617
use Symfony\Component\Config\Loader\LoaderInterface;
2718

tests/AppConfigMysql/AppConfigMysqlKernel.php

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,6 @@
1313

1414
namespace Liip\FunctionalTestBundle\Tests\AppConfigMysql;
1515

16-
/*
17-
* This file is part of the Liip/FunctionalTestBundle
18-
*
19-
* (c) Lukas Kahwe Smith <smith@pooteeweet.org>
20-
*
21-
* This source file is subject to the MIT license that is bundled
22-
* with this source code in the file LICENSE.
23-
*/
24-
2516
use Liip\FunctionalTestBundle\Tests\App\AppKernel;
2617
use Symfony\Component\Config\Loader\LoaderInterface;
2718

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the Liip/FunctionalTestBundle
7+
*
8+
* (c) Lukas Kahwe Smith <smith@pooteeweet.org>
9+
*
10+
* This source file is subject to the MIT license that is bundled
11+
* with this source code in the file LICENSE.
12+
*/
13+
14+
namespace Liip\FunctionalTestBundle\Tests\AppConfigMysqlCacheDb;
15+
16+
use Liip\FunctionalTestBundle\Tests\AppConfigMysql\AppConfigMysqlKernel;
17+
use Symfony\Component\Config\Loader\LoaderInterface;
18+
19+
class AppConfigMysqlKernelCacheDb extends AppConfigMysqlKernel
20+
{
21+
/**
22+
* Load the config.yml from the current directory.
23+
*/
24+
public function registerContainerConfiguration(LoaderInterface $loader): void
25+
{
26+
// Load the default file.
27+
parent::registerContainerConfiguration($loader);
28+
29+
// Load the file with MySQL configuration
30+
$loader->load(__DIR__.'/config.yml');
31+
}
32+
}

0 commit comments

Comments
 (0)