diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index fa0c818..9448fb2 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -67,4 +67,4 @@ jobs: if [ ${{ matrix.db-type }} == 'mysql' ]; then sudo service mysql start && mysql -h 127.0.0.1 -u root -proot -e 'CREATE DATABASE IF NOT EXISTS test_suite_light;'; fi - composer run-tests-${{ matrix.db-type }} \ No newline at end of file + composer ${{ matrix.db-type }} \ No newline at end of file diff --git a/composer.json b/composer.json index e777eab..01fc309 100644 --- a/composer.json +++ b/composer.json @@ -38,9 +38,9 @@ } }, "scripts": { - "run-tests-mysql": "bash run_tests.sh Mysql", - "run-tests-pgsql": "bash run_tests.sh Postgres", - "run-tests-sqlite": "bash run_tests.sh Sqlite" + "mysql": "bash run_tests.sh Mysql", + "pgsql": "bash run_tests.sh Postgres", + "sqlite": "bash run_tests.sh Sqlite" }, "config": { "sort-packages": true diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f74fb12..f245feb 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -15,15 +15,6 @@ ./tests/TestCase/ - - ./tests/DropTablesTestCase/TableSnifferDropCitiesTest.php - - - ./tests/DropTablesTestCase/TableSnifferDropCountriesTest.php - - - ./tests/DropTablesTestCase/TableSnifferDropTablesTest.php - diff --git a/run_tests.sh b/run_tests.sh index 4c0fccb..6780cd6 100644 --- a/run_tests.sh +++ b/run_tests.sh @@ -5,19 +5,22 @@ DRIVER=$1; echo "Starting PHPUNIT tests" export DB_DRIVER=$DRIVER -# Test Cases where tables get dropped are put separately, -# since they are giving a hard time to the fixtures -# These can be put all together again once the migrations -# get required in the dependencies -./vendor/bin/phpunit --testsuite Default --stop-on-fail -./vendor/bin/phpunit --testsuite DropCities --stop-on-fail -./vendor/bin/phpunit --testsuite DropCountries --stop-on-fail -./vendor/bin/phpunit --testsuite DropTables --stop-on-fail +####################### +#### Tests with temporary sniffers +####################### +./vendor/bin/phpunit -# Run the tests again using non-triggered based sniffers +####################### +#### Tests with non temporary sniffers +####################### +export SNIFFERS_IN_MAIN_MODE="true" +./vendor/bin/phpunit + +#### DEPRECATED ##### +# Run the tests using +# non-triggered based sniffers +##################### export TABLE_SNIFFER="CakephpTestSuiteLight\Sniffer\\${DRIVER}TableSniffer" +export USE_NON_TRIGGERED_BASED_SNIFFERS="true" +./vendor/bin/phpunit -./vendor/bin/phpunit --testsuite Default --stop-on-fail -./vendor/bin/phpunit --testsuite DropCities --stop-on-fail -./vendor/bin/phpunit --testsuite DropCountries --stop-on-fail -./vendor/bin/phpunit --testsuite DropTables --stop-on-fail diff --git a/src/FixtureInjector.php b/src/FixtureInjector.php index d6bf7a1..c41f765 100644 --- a/src/FixtureInjector.php +++ b/src/FixtureInjector.php @@ -61,13 +61,17 @@ public function startTestSuite(TestSuite $suite) */ public function startTest(Test $test) { + $this->statisticTool->startsTestTime(); + // Truncation can be skipped if no DB interaction are expected if (!$this->skipTablesTruncation($test)) { $this->_fixtureManager->truncateDirtyTables(); } + $this->statisticTool->startsLoadingFixturesTime(); // Load CakePHP fixtures parent::startTest($test); + $this->statisticTool->stopsLoadingFixturesTime(); // Run the seeds of your DB // $this->rollbackAndMigrateIfRequired(); @@ -82,7 +86,8 @@ public function startTest(Test $test) */ public function endTest(Test $test, $time) { - $this->statisticTool->collectTestStatistics($test, $time); + $this->statisticTool->stopsTestTime(); + $this->statisticTool->collectTestStatistics($test); } /** diff --git a/src/FixtureManager.php b/src/FixtureManager.php index 3262806..5eaf1df 100644 --- a/src/FixtureManager.php +++ b/src/FixtureManager.php @@ -17,10 +17,7 @@ use Cake\Datasource\ConnectionInterface; use Cake\Datasource\ConnectionManager; use Cake\TestSuite\Fixture\FixtureManager as BaseFixtureManager; -use CakephpTestSuiteLight\Sniffer\BaseTableSniffer; -use CakephpTestSuiteLight\Sniffer\MysqlTriggerBasedTableSniffer; -use CakephpTestSuiteLight\Sniffer\PostgresTriggerBasedTableSniffer; -use CakephpTestSuiteLight\Sniffer\SqliteTriggerBasedTableSniffer; +use CakephpTestSuiteLight\Sniffer\SnifferRegistry; use Exception; use function strpos; @@ -35,11 +32,6 @@ class FixtureManager extends BaseFixtureManager */ private static $_configIsLoaded = false; - /** - * @var array - */ - private $sniffers = []; - /** * @var array|null */ @@ -81,58 +73,6 @@ public function aliasConnections() $this->_aliasConnections(); } - /** - * Each connection has it's own sniffer - * - * @param string $connectionName - * @return BaseTableSniffer - */ - public function getSniffer(string $connectionName): BaseTableSniffer - { - return $this->sniffers[$connectionName] ?? $this->addSniffer($connectionName); - } - - /** - * @param string $connectionName - * @return BaseTableSniffer - */ - public function addSniffer(string $connectionName): BaseTableSniffer - { - $snifferName = $this->getConnectionSnifferName($connectionName); - - /** @var BaseTableSniffer $sniffer */ - $sniffer = new $snifferName($this->getConnection($connectionName)); - return $this->sniffers[$connectionName] = $sniffer; - } - - /** - * Read in the config the sniffer to use - * @param string $connectionName - * @return string - */ - public function getConnectionSnifferName(string $connectionName): string - { - $config = ConnectionManager::getConfig($connectionName); - $driver = ''; - - if (isset($config['tableSniffer'])) { - $snifferName = $config['tableSniffer']; - } else { - try { - $driver = $this->getConnection($connectionName)->config()['driver']; - $snifferName = $this->getDefaultTableSniffers()[$driver] ?? null; - if (is_null($snifferName)) { - throw new \RuntimeException(); - } - } catch (\RuntimeException $e) { - $msg = "Testsuite light error for connection {$connectionName}. "; - $msg .= "The DB driver {$driver} is not supported or was not found"; - throw new \PHPUnit\Framework\Exception($msg); - } - } - return $snifferName; - } - /** * Scan all test connections and truncate the dirty tables * @return void @@ -140,7 +80,7 @@ public function getConnectionSnifferName(string $connectionName): string public function truncateDirtyTables() { foreach ($this->getActiveConnections() as $connection) { - $this->getSniffer($connection)->truncateDirtyTables(); + SnifferRegistry::get($connection)->truncateDirtyTables(); } } @@ -198,27 +138,14 @@ public function loadConfig(): FixtureManager } /** - * Table sniffers provided by the package - * @return array - */ - public function getDefaultTableSniffers(): array - { - return [ - \Cake\Database\Driver\Mysql::class => MysqlTriggerBasedTableSniffer::class, - \Cake\Database\Driver\Sqlite::class => SqliteTriggerBasedTableSniffer::class, - \Cake\Database\Driver\Postgres::class => PostgresTriggerBasedTableSniffer::class, - ]; - } - - /** - * Get the appropriate truncator and drop all tables + * Get the appropriate sniffer and drop all tables * @param string $connectionName * @return void */ public function dropTables(string $connectionName) { - $this->getSniffer($connectionName)->dropTables( - $this->getSniffer($connectionName)->fetchAllTables() + SnifferRegistry::get($connectionName)->dropTables( + SnifferRegistry::get($connectionName)->fetchAllTables() ); } diff --git a/src/Sniffer/BaseTableSniffer.php b/src/Sniffer/BaseTableSniffer.php index 5833a51..920a113 100644 --- a/src/Sniffer/BaseTableSniffer.php +++ b/src/Sniffer/BaseTableSniffer.php @@ -30,11 +30,17 @@ abstract class BaseTableSniffer protected $allTables; /** - * Truncate all the tables found in the dirty table collector + * Truncate all the dirty tables * @return void */ abstract public function truncateDirtyTables(); + /** + * Get all the dirty tables + * @return array + */ + abstract public function getDirtyTables(): array; + /** * List all tables * @return array @@ -54,17 +60,8 @@ abstract public function dropTables(array $tables); */ public function __construct(ConnectionInterface $connection) { - $this->connection = $connection; - $this->setup(); - } - - /** - * Setup method - * @return void - */ - public function setup() - { - $this->getAllTables(true); + $this->setConnection($connection); + $this->start(); } /** @@ -84,14 +81,33 @@ public function setConnection(ConnectionInterface $connection) } /** - * Find all tables where an insert happened - * This also includes empty tables, where a delete - * was performed after an insert - * @return array + * Get the sniffer started + * Typically create the dirty table collector + * Truncate all tables + * Create the spying triggers + * @return void + */ + public function start() + { + $this->getAllTables(true); + } + + /** + * Stop spying + * @return void + */ + public function shutdown() + {} + + /** + * Stop spying and restart + * Useful if the schema or the + * dirty table collector changed */ - public function getDirtyTables(): array + public function restart() { - return $this->fetchQuery("SELECT table_name FROM " . TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR); + $this->shutdown(); + $this->start(); } /** @@ -136,19 +152,6 @@ public function implodeSpecial(string $glueBefore, array $array, string $glueAft return $glueBefore . implode($glueAfter.$glueBefore, $array) . $glueAfter; } - /** - * The dirty table collector should never be dropped - * This method helps removing it from a list of tables - * @param array $tables - * @return void - */ - public function removeDirtyTableCollectorFromArray(array &$tables) - { - if (($key = array_search(TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR, $tables)) !== false) { - unset($tables[$key]); - } - } - /** * Get all tables except the phinx tables * * @param bool $forceFetch @@ -177,27 +180,12 @@ public function getAllTables(bool $forceFetch = false): array return $this->allTables; } - /** - * Create the table gathering the dirty tables - * @return void - */ - public function createDirtyTableCollector() - { - $dirtyTable = TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR; - $this->getConnection()->execute(" - CREATE TABLE IF NOT EXISTS {$dirtyTable} ( - table_name VARCHAR(128) PRIMARY KEY - ); - "); - } - /** * Checks if the present class implements triggers * @return bool */ public function implementsTriggers(): bool { - $class = new \ReflectionClass($this); - return $class->implementsInterface(TriggerBasedTableSnifferInterface::class); + return $this instanceof BaseTriggerBasedTableSniffer; } } \ No newline at end of file diff --git a/src/Sniffer/BaseTriggerBasedTableSniffer.php b/src/Sniffer/BaseTriggerBasedTableSniffer.php new file mode 100644 index 0000000..f1fa0ab --- /dev/null +++ b/src/Sniffer/BaseTriggerBasedTableSniffer.php @@ -0,0 +1,201 @@ +mode = self::TEMP_MODE; + parent::__construct($connection); + } + + /** + * @return ConnectionInterface + */ + public function getConnection(): ConnectionInterface + { + return $this->connection; + } + + /** + * @param ConnectionInterface $connection + */ + public function setConnection(ConnectionInterface $connection) + { + $this->connection = $connection; + } + + /** + * Find all tables where an insert happened + * This also includes empty tables, where a delete + * was performed after an insert + * @return array + */ + public function getDirtyTables(): array + { + try { + return $this->fetchQuery("SELECT table_name FROM " . self::DIRTY_TABLE_COLLECTOR); + } catch (\Exception $e) { + $this->restart(); + return $this->getAllTablesExceptPhinxlogs(true); + } + } + + /** + * Create the table gathering the dirty tables + * @return void + */ + public function createDirtyTableCollector() + { + $temporary = $this->isInTempMode() ? 'TEMPORARY' : ''; + $dirtyTable = self::DIRTY_TABLE_COLLECTOR; + + $this->getConnection()->execute(" + CREATE {$temporary} TABLE IF NOT EXISTS {$dirtyTable} ( + table_name VARCHAR(128) PRIMARY KEY + ); + "); + } + + /** + * Drop the table gathering the dirty tables + * @return void + */ + public function dropDirtyTableCollector() + { + $dirtyTable = self::DIRTY_TABLE_COLLECTOR; + $this->getConnection()->execute("DROP TABLE IF EXISTS {$dirtyTable}"); + } + + /** + * The dirty table collector being temporary, + * ensure that all tables are clean when starting the suite + * @return void + */ + public function cleanAllTables() + { + $this->markAllTablesAsDirty(); + $this->truncateDirtyTables(); + } + + /** + * The dirty table collector is not temporary + * @return void + */ + public function activateMainMode() + { + $this->setMode(self::MAIN_MODE); + } + + /** + * The dirty table collector is temporary + * @return void + */ + public function activateTempMode() + { + $this->setMode(self::TEMP_MODE); + } + + /** + * @param string $mode + * @return void + */ + public function setMode(string $mode) + { + if ($this->mode === $mode) { + return; + } + $this->mode = $mode; + $this->restart(); + } + + /** + * Get the mode on which the sniffer is running + * This defines if the collector table is + * temporary or not + * @return string + */ + public function getMode(): string + { + if (!$this->implementsTriggers()) { + return ''; + } + return $this->mode; + } + + /** + * @return bool + */ + public function isInTempMode(): bool + { + return ($this->getMode() === self::TEMP_MODE); + } + + /** + * @return bool + */ + public function isInMainMode(): bool + { + return ($this->getMode() === self::MAIN_MODE); + } +} \ No newline at end of file diff --git a/src/Sniffer/DriverTraits/MysqlSnifferTrait.php b/src/Sniffer/DriverTraits/MysqlSnifferTrait.php new file mode 100644 index 0000000..8b700ea --- /dev/null +++ b/src/Sniffer/DriverTraits/MysqlSnifferTrait.php @@ -0,0 +1,82 @@ +fetchQuery("SHOW triggers"); + + foreach ($triggers as $k => $trigger) { + if (strpos($trigger, BaseTriggerBasedTableSniffer::TRIGGER_PREFIX) !== 0) { + unset($triggers[$k]); + } + } + + return (array)$triggers; + } + + /** + * @inheritDoc + */ + public function dropTriggers() + { + $triggers = $this->getTriggers(); + if (empty($triggers)) { + return; + } + + $stmts = $this->implodeSpecial("DROP TRIGGER ", $triggers, ";"); + $this->getConnection()->execute($stmts); + } + + /** + * @inheritDoc + */ + public function fetchAllTables(): array + { + return $this->fetchQuery(" + SELECT table_name + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = DATABASE(); + "); + } + + /** + * @inheritDoc + */ + public function dropTables(array $tables) + { + if (empty($tables)) { + return; + } + + $this->getConnection()->disableConstraints(function (Connection $connection) use ($tables) { + $connection->transactional(function(Connection $connection) use ($tables) { + $connection->execute( + $this->implodeSpecial( + 'DROP TABLE IF EXISTS `', $tables, '`;') + ); + }); + }); + } +} \ No newline at end of file diff --git a/src/Sniffer/DriverTraits/PostgresSnifferTrait.php b/src/Sniffer/DriverTraits/PostgresSnifferTrait.php new file mode 100644 index 0000000..ccb8460 --- /dev/null +++ b/src/Sniffer/DriverTraits/PostgresSnifferTrait.php @@ -0,0 +1,88 @@ +fetchQuery(" + SELECT tgname + FROM pg_trigger + WHERE tgname LIKE '{$triggerPrefix}%' + "); + + foreach ($triggers as $k => $trigger) { + if (strpos($trigger, $triggerPrefix) !== 0) { + unset($triggers[$k]); + } + } + + return (array)$triggers; + } + + /** + * @inheritDoc + */ + public function dropTriggers() + { + $triggers = $this->getTriggers(); + if (empty($triggers)) { + return; + } + + foreach ($triggers as $trigger) { + $table = substr($trigger, strlen(BaseTriggerBasedTableSniffer::TRIGGER_PREFIX)); + $this->getConnection()->execute("DROP TRIGGER {$trigger} ON {$table};"); + } + } + + /** + * @inheritDoc + */ + public function fetchAllTables(): array + { + return $this->fetchQuery(" + SELECT table_name + FROM information_schema.tables + WHERE table_schema = 'public' + "); + } + + /** + * @inheritDoc + */ + public function dropTables(array $tables) + { + if (empty($tables)) { + return; + } + + $this->getConnection()->disableConstraints(function (Connection $connection) use ($tables) { + $tables[] = BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR; + foreach ($tables as $table) { + $connection->execute( + 'DROP TABLE IF EXISTS "' . $table . '" CASCADE;' + ); + } + }); + } +} \ No newline at end of file diff --git a/src/Sniffer/DriverTraits/SqliteSnifferTrait.php b/src/Sniffer/DriverTraits/SqliteSnifferTrait.php new file mode 100644 index 0000000..8d26b0f --- /dev/null +++ b/src/Sniffer/DriverTraits/SqliteSnifferTrait.php @@ -0,0 +1,87 @@ +fetchQuery(" + SELECT name FROM sqlite_master WHERE type = 'trigger' AND name LIKE '{$triggerPrefix}%' + UNION + SELECT name FROM sqlite_temp_master WHERE type = 'trigger' AND name LIKE '{$triggerPrefix}%' + "); + + foreach ($triggers as $k => $trigger) { + if (strpos($trigger, $triggerPrefix) !== 0) { + unset($triggers[$k]); + } + } + + return (array)$triggers; + } + + /** + * @inheritDoc + */ + public function dropTriggers() + { + $triggers = $this->getTriggers(); + + if (empty($triggers)) { + return; + } + + foreach ($triggers as $trigger) { + $this->getConnection()->execute("DROP TRIGGER {$trigger};"); + } + } + + /** + * @inheritDoc + */ + public function fetchAllTables(): array + { + return $this->fetchQuery(" + SELECT name FROM sqlite_master WHERE type='table' AND name != 'sqlite_sequence'; + "); + } + + /** + * @inheritDoc + */ + public function dropTables(array $tables) + { + if (empty($tables)) { + return; + } + + $this->getConnection()->disableConstraints(function (Connection $connection) use ($tables) { + $tables[] = BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR; + $connection->transactional(function(Connection $connection) use ($tables) { + foreach ($tables as $table) { + $connection->execute("DROP TABLE IF EXISTS $table;"); + } + }); + }); + } +} \ No newline at end of file diff --git a/src/Sniffer/MysqlTableSniffer.php b/src/Sniffer/MysqlTableSniffer.php index 5b51c5b..d030e73 100644 --- a/src/Sniffer/MysqlTableSniffer.php +++ b/src/Sniffer/MysqlTableSniffer.php @@ -15,6 +15,7 @@ use Cake\Database\Connection; +use CakephpTestSuiteLight\Sniffer\DriverTraits\MysqlSnifferTrait; /** * Class MysqlTableSniffer @@ -22,6 +23,8 @@ */ class MysqlTableSniffer extends BaseTableSniffer { + use MysqlSnifferTrait; + /** * @inheritDoc */ @@ -51,45 +54,7 @@ public function truncateDirtyTables() $this->getConnection()->disableConstraints(function (Connection $connection) use ($tables) { $connection->transactional(function(Connection $connection) use ($tables) { $connection->execute( - $this->implodeSpecial( - "TRUNCATE TABLE `", - $tables, - "`;" - ) - ); - }); - }); - } - - /** - * @inheritDoc - */ - public function fetchAllTables(): array - { - return $this->fetchQuery(" - SELECT table_name - FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE(); - "); - } - - /** - * @inheritDoc - */ - public function dropTables(array $tables) - { - if (empty($tables)) { - return; - } - - $this->getConnection()->disableConstraints(function (Connection $connection) use ($tables) { - $connection->transactional(function(Connection $connection) use ($tables) { - $connection->execute( - $this->implodeSpecial( - 'DROP TABLE IF EXISTS `', - $tables, - '`;' - ) + $this->implodeSpecial("TRUNCATE TABLE `", $tables, "`;") ); }); }); diff --git a/src/Sniffer/MysqlTriggerBasedTableSniffer.php b/src/Sniffer/MysqlTriggerBasedTableSniffer.php index 1b269eb..e0fed4f 100644 --- a/src/Sniffer/MysqlTriggerBasedTableSniffer.php +++ b/src/Sniffer/MysqlTriggerBasedTableSniffer.php @@ -14,57 +14,24 @@ namespace CakephpTestSuiteLight\Sniffer; -use Cake\Database\Connection; +use CakephpTestSuiteLight\Sniffer\DriverTraits\MysqlSnifferTrait; -class MysqlTriggerBasedTableSniffer extends BaseTableSniffer implements TriggerBasedTableSnifferInterface +class MysqlTriggerBasedTableSniffer extends BaseTriggerBasedTableSniffer { - /** - * @inheritDoc - */ - public function truncateDirtyTables() - { - $this->getConnection()->execute('CALL TruncateDirtyTables();'); - } + use MysqlSnifferTrait; /** * @inheritDoc */ - public function fetchAllTables(): array - { - return $this->fetchQuery(" - SELECT table_name - FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = DATABASE(); - "); - } - - /** - * @inheritDoc - */ - public function dropTables(array $tables) + public function truncateDirtyTables() { - $this->removeDirtyTableCollectorFromArray($tables); - - if (empty($tables)) { - return; + try { + $this->getConnection()->execute('CALL TruncateDirtyTables();'); + } catch (\Exception $e) { + // The dirty table collector might not be found because the session + // was interrupted. + $this->restart(); } - - $this->getConnection()->disableConstraints(function (Connection $connection) use ($tables) { - $connection->transactional(function(Connection $connection) use ($tables) { - $connection->execute( - $this->implodeSpecial( - 'DROP TABLE IF EXISTS `', - $tables, - '`;' - ) - ); - // Truncate dirty table collector - $connection - ->newQuery() - ->delete(self::DIRTY_TABLE_COLLECTOR) - ->execute(); - }); - }); } /** @@ -83,7 +50,7 @@ public function createTriggers() if ($table === $dirtyTable) { continue; } - $stmts .= " + $stmts .= " CREATE TRIGGER {$triggerPrefix}{$table} AFTER INSERT ON `{$table}` FOR EACH ROW INSERT IGNORE INTO {$dirtyTable} (table_name) VALUES ('{$table}'), ('{$dirtyTable}'); @@ -98,31 +65,39 @@ public function createTriggers() /** * @inheritDoc */ - public function setup() + public function start() { - parent::setup(); + parent::start(); // create dirty tables collector $this->createDirtyTableCollector(); - - // create triggers $this->createTriggers(); + $this->createTruncateDirtyTablesProcedure(); + $this->cleanAllTables(); + } - $dirtyTable = self::DIRTY_TABLE_COLLECTOR; - // Collect all statements and run them in one transaction - $stmts = []; + /** + * @inheritDoc + */ + public function shutdown() + { + parent::shutdown(); + + $this->dropTriggers(); + $this->dropDirtyTableCollector(); + } - // create truncate procedure - $createTruncateProcedureStmt = " + public function createTruncateDirtyTablesProcedure() + { + $dirtyTable = self::DIRTY_TABLE_COLLECTOR; + $this->getConnection()->execute(" DROP PROCEDURE IF EXISTS TruncateDirtyTables; CREATE PROCEDURE TruncateDirtyTables() BEGIN DECLARE current_table_name VARCHAR(128); DECLARE finished INTEGER DEFAULT 0; DECLARE dirty_table_cursor CURSOR FOR - SELECT dt.table_name FROM {$dirtyTable} dt - INNER JOIN information_schema.TABLES info_schema on dt.table_name = info_schema.TABLE_NAME - WHERE info_schema.table_schema = DATABASE(); + SELECT dt.table_name FROM {$dirtyTable} dt; DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1; SET FOREIGN_KEY_CHECKS=0; @@ -141,50 +116,19 @@ public function setup() SET FOREIGN_KEY_CHECKS=1; END - "; - $stmts[] = $createTruncateProcedureStmt; - - // Run all statements in one transaction - $this->getConnection()->transactional(function(Connection $connection) use ($stmts) { - foreach ($stmts as $stmt) { - $connection->execute($stmt); - } - }); - } - - /** - * @inheritDoc - */ - public function getTriggers(): array - { - $triggers = $this->fetchQuery(" - SHOW triggers "); - - foreach ($triggers as $k => $trigger) { - if (strpos($trigger, self::TRIGGER_PREFIX) !== 0) { - unset($triggers[$k]); - } - } - - return (array)$triggers; } /** * @inheritDoc */ - public function dropTriggers() + public function markAllTablesAsDirty() { - $triggers = $this->getTriggers(); - if (empty($triggers)) { - return; - } + $tables = $this->getAllTablesExceptPhinxlogs(); + $dirtyTable = self::DIRTY_TABLE_COLLECTOR; + $tables[] = $dirtyTable; - $stmts = $this->implodeSpecial( - "DROP TRIGGER ", - $triggers, - ";" - ); - $this->getConnection()->execute($stmts); + $stmt = "INSERT IGNORE INTO {$dirtyTable} VALUES ('" . implode("'), ('", $tables) . "')"; + $this->getConnection()->execute($stmt); } } \ No newline at end of file diff --git a/src/Sniffer/PostgresTableSniffer.php b/src/Sniffer/PostgresTableSniffer.php index 1f8186e..4c48e37 100644 --- a/src/Sniffer/PostgresTableSniffer.php +++ b/src/Sniffer/PostgresTableSniffer.php @@ -15,6 +15,7 @@ use Cake\Database\Connection; +use CakephpTestSuiteLight\Sniffer\DriverTraits\PostgresSnifferTrait; /** * Class PostgresTableSniffer @@ -22,6 +23,8 @@ */ class PostgresTableSniffer extends BaseTableSniffer { + use PostgresSnifferTrait; + /** * @inheritDoc */ @@ -53,36 +56,4 @@ public function truncateDirtyTables() }); }); } - - /** - * @inheritDoc - */ - public function fetchAllTables(): array - { - return $this->fetchQuery(" - SELECT table_name - FROM information_schema.tables - WHERE table_schema = 'public' - "); - } - - /** - * @inheritDoc - */ - public function dropTables(array $tables) - { - if (empty($tables)) { - return; - } - - $this->getConnection()->disableConstraints(function (Connection $connection) use ($tables) { - $connection->transactional(function(Connection $connection) use ($tables) { - foreach ($tables as $table) { - $connection->execute( - 'DROP TABLE IF EXISTS "' . $table . '" CASCADE;' - ); - } - }); - }); - } } \ No newline at end of file diff --git a/src/Sniffer/PostgresTriggerBasedTableSniffer.php b/src/Sniffer/PostgresTriggerBasedTableSniffer.php index 6f27286..a57b6a7 100644 --- a/src/Sniffer/PostgresTriggerBasedTableSniffer.php +++ b/src/Sniffer/PostgresTriggerBasedTableSniffer.php @@ -15,57 +15,27 @@ use Cake\Database\Connection; +use CakephpTestSuiteLight\Sniffer\DriverTraits\PostgresSnifferTrait; -class PostgresTriggerBasedTableSniffer extends BaseTableSniffer implements TriggerBasedTableSnifferInterface +class PostgresTriggerBasedTableSniffer extends BaseTriggerBasedTableSniffer { - /** - * @inheritDoc - */ - public function truncateDirtyTables() - { - $this->getConnection()->transactional(function (Connection $connection) { - $connection->execute('CALL TruncateDirtyTables();'); - $connection->execute('TRUNCATE TABLE ' . self::DIRTY_TABLE_COLLECTOR . ' RESTART IDENTITY CASCADE;'); - }); - } - - /** - * @inheritDoc - */ - public function fetchAllTables(): array - { - return $this->fetchQuery(" - SELECT table_name - FROM information_schema.tables - WHERE table_schema = 'public' - "); - } + use PostgresSnifferTrait; /** * @inheritDoc */ - public function dropTables(array $tables) + public function truncateDirtyTables() { - $this->removeDirtyTableCollectorFromArray($tables); - - if (empty($tables)) { - return; - } - - $this->getConnection()->disableConstraints(function (Connection $connection) use ($tables) { - $connection->transactional(function(Connection $connection) use ($tables) { - foreach ($tables as $table) { - $connection->execute( - 'DROP TABLE IF EXISTS "' . $table . '" CASCADE;' - ); - } - // Truncate dirty table collector - $connection - ->newQuery() - ->delete(self::DIRTY_TABLE_COLLECTOR) - ->execute(); + try { + $this->getConnection()->transactional(function (Connection $connection) { + $connection->execute('CALL TruncateDirtyTables();'); + $connection->execute('TRUNCATE TABLE ' . self::DIRTY_TABLE_COLLECTOR . ' RESTART IDENTITY CASCADE;'); }); - }); + } catch (\Exception $e) { + // The dirty table collector might not be found because the session + // was interrupted. + $this->restart(); + } } /** @@ -81,15 +51,18 @@ public function createTriggers() $stmts = []; foreach ($this->getAllTablesExceptPhinxlogs() as $table) { + if ($table === $dirtyTable) { + continue; + } $stmts[] = " CREATE OR REPLACE FUNCTION mark_table_{$table}_as_dirty() RETURNS TRIGGER LANGUAGE PLPGSQL AS $$ DECLARE spy_is_inactive {$dirtyTable}%ROWTYPE; BEGIN SELECT * FROM {$dirtyTable} WHERE table_name = '{$table}' LIMIT 1 INTO spy_is_inactive; - IF NOT FOUND THEN - INSERT INTO {$dirtyTable} (table_name) VALUES ('{$table}'); - END IF; + IF NOT FOUND THEN + INSERT INTO {$dirtyTable} (table_name) VALUES ('{$table}'), ('{$dirtyTable}') ON CONFLICT DO NOTHING; + END IF; RETURN NEW; END; $$ @@ -109,19 +82,30 @@ public function createTriggers() /** * @inheritDoc */ - public function setup() + public function start() { - parent::setup(); - - $dirtyTable = self::DIRTY_TABLE_COLLECTOR; + parent::start(); - // create dirty tables collector $this->createDirtyTableCollector(); - - // create triggers $this->createTriggers(); + $this->createTruncateDirtyTablesProcedure(); + $this->cleanAllTables(); + } + + /** + * @inheritDoc + */ + public function shutdown() + { + parent::shutdown(); + + $this->dropTriggers(); + $this->dropDirtyTableCollector(); + } - // create truncate procedure + public function createTruncateDirtyTablesProcedure() + { + $dirtyTable = self::DIRTY_TABLE_COLLECTOR; $this->getConnection()->execute(" CREATE OR REPLACE PROCEDURE TruncateDirtyTables() AS $$ DECLARE @@ -145,37 +129,13 @@ public function setup() /** * @inheritDoc */ - public function getTriggers(): array - { - $triggerPrefix = self::TRIGGER_PREFIX; - $triggers = $this->fetchQuery(" - SELECT tgname - FROM pg_trigger - WHERE tgname LIKE '{$triggerPrefix}%' - "); - - foreach ($triggers as $k => $trigger) { - if (strpos($trigger, self::TRIGGER_PREFIX) !== 0) { - unset($triggers[$k]); - } - } - - return (array)$triggers; - } - - /** - * @inheritDoc - */ - public function dropTriggers() + public function markAllTablesAsDirty() { - $triggers = $this->getTriggers(); - if (empty($triggers)) { - return; - } + $tables = $this->getAllTablesExceptPhinxlogs(); + $dirtyTable = self::DIRTY_TABLE_COLLECTOR; + $tables[] = $dirtyTable; - foreach ($triggers as $trigger) { - $table = substr($trigger, strlen(self::TRIGGER_PREFIX)); - $this->getConnection()->execute("DROP TRIGGER {$trigger} ON {$table};"); - } + $stmt = "INSERT INTO {$dirtyTable} VALUES ('" . implode("'), ('", $tables) . "') ON CONFLICT DO NOTHING"; + $this->getConnection()->execute($stmt); } } \ No newline at end of file diff --git a/src/Sniffer/SnifferRegistry.php b/src/Sniffer/SnifferRegistry.php new file mode 100644 index 0000000..07bca18 --- /dev/null +++ b/src/Sniffer/SnifferRegistry.php @@ -0,0 +1,115 @@ + $sniffer) { + $sniffer->shutdown(); + unset(self::$sniffers[$conn]); + } + } + + /** + * @param string $name + * @return ConnectionInterface + */ + public static function getConnection($name = 'test'): ConnectionInterface + { + return ConnectionManager::get($name); + } + + /** + * Read in the config the sniffer to use + * @param string $connectionName + * @return string + */ + public static function getConnectionSnifferName(string $connectionName): string + { + $config = ConnectionManager::getConfig($connectionName); + $driver = ''; + + if (isset($config['tableSniffer'])) { + $snifferName = $config['tableSniffer']; + } else { + try { + $driver = self::getConnection($connectionName)->config()['driver']; + $snifferName = self::getDefaultTableSniffers()[$driver] ?? null; + if (is_null($snifferName)) { + throw new \RuntimeException(); + } + } catch (\RuntimeException $e) { + $msg = "Testsuite light error for connection {$connectionName}. "; + $msg .= "The DB driver {$driver} is not supported or was not found"; + throw new \PHPUnit\Framework\Exception($msg); + } + } + return $snifferName; + } + + /** + * Table sniffers provided by the package + * @return array + */ + public static function getDefaultTableSniffers(): array + { + return [ + \Cake\Database\Driver\Mysql::class => MysqlTriggerBasedTableSniffer::class, + \Cake\Database\Driver\Sqlite::class => SqliteTriggerBasedTableSniffer::class, + \Cake\Database\Driver\Postgres::class => PostgresTriggerBasedTableSniffer::class, + ]; + } +} \ No newline at end of file diff --git a/src/Sniffer/SqliteTableSniffer.php b/src/Sniffer/SqliteTableSniffer.php index d231ed5..15ac6a3 100644 --- a/src/Sniffer/SqliteTableSniffer.php +++ b/src/Sniffer/SqliteTableSniffer.php @@ -16,6 +16,7 @@ use Cake\Database\Connection; use Cake\Database\Exception; +use CakephpTestSuiteLight\Sniffer\DriverTraits\SqliteSnifferTrait; /** * Class SqliteTableSniffer @@ -23,6 +24,8 @@ */ class SqliteTableSniffer extends BaseTableSniffer { + use SqliteSnifferTrait; + /** * @inheritDoc */ @@ -65,32 +68,4 @@ public function truncateDirtyTables() }); }); } - - /** - * @inheritDoc - */ - public function fetchAllTables(): array - { - return $this->fetchQuery(" - SELECT name FROM sqlite_master WHERE type='table' AND name != 'sqlite_sequence'; - "); - } - - /** - * @inheritDoc - */ - public function dropTables(array $tables) - { - if (empty($tables)) { - return; - } - - $this->getConnection()->disableConstraints(function (Connection $connection) use ($tables) { - $connection->transactional(function(Connection $connection) use ($tables) { - foreach ($tables as $table) { - $connection->execute("DROP TABLE IF EXISTS $table;"); - } - }); - }); - } } \ No newline at end of file diff --git a/src/Sniffer/SqliteTriggerBasedTableSniffer.php b/src/Sniffer/SqliteTriggerBasedTableSniffer.php index cc0223a..1c9a409 100644 --- a/src/Sniffer/SqliteTriggerBasedTableSniffer.php +++ b/src/Sniffer/SqliteTriggerBasedTableSniffer.php @@ -15,9 +15,17 @@ use Cake\Database\Connection; +use CakephpTestSuiteLight\Sniffer\DriverTraits\SqliteSnifferTrait; -class SqliteTriggerBasedTableSniffer extends BaseTableSniffer implements TriggerBasedTableSnifferInterface +class SqliteTriggerBasedTableSniffer extends BaseTriggerBasedTableSniffer { + use SqliteSnifferTrait; + + private function getDirtyTableCollectorName(): string + { + return ($this->isInTempMode() ? 'temp.' : '') . self::DIRTY_TABLE_COLLECTOR; + } + /** * @inheritDoc */ @@ -33,55 +41,22 @@ public function truncateDirtyTables() } $this->getConnection()->disableConstraints(function (Connection $connection) use ($tables) { - $connection->transactional(function(Connection $connection) use ($tables) { - foreach ($tables as $table) { - $connection - ->newQuery() - ->delete($table) - ->execute(); - $connection - ->newQuery() - ->delete('sqlite_sequence') - ->where(['name' => $table]) - ->execute(); - } - }); + foreach ($tables as $table) { + $connection->execute("DELETE FROM {$table}"); + try { + $connection->execute("DELETE FROM sqlite_sequence WHERE name = '{$table}'"); + } catch (\PDOException $e) {} + } }); - } - - /** - * @inheritDoc - */ - public function fetchAllTables(): array - { - return $this->fetchQuery(" - SELECT name FROM sqlite_master WHERE type='table' AND name != 'sqlite_sequence'; - "); - } - - /** - * @inheritDoc - */ - public function dropTables(array $tables) - { - $this->removeDirtyTableCollectorFromArray($tables); - if (empty($tables)) { - return; + $dirtyTable = $this->getDirtyTableCollectorName(); + try { + $this->getConnection()->execute("DELETE FROM {$dirtyTable}"); + } catch (\Exception $e) { + // The dirty table collector might not be found because the session + // was interrupted. + $this->restart(); } - - $this->getConnection()->disableConstraints(function (Connection $connection) use ($tables) { - $connection->transactional(function(Connection $connection) use ($tables) { - foreach ($tables as $table) { - $connection->execute("DROP TABLE IF EXISTS $table;"); - } - }); - // Truncate dirty table collector - $connection - ->newQuery() - ->delete(self::DIRTY_TABLE_COLLECTOR) - ->execute(); - }); } /** @@ -94,13 +69,18 @@ public function createTriggers() $dirtyTable = self::DIRTY_TABLE_COLLECTOR; $triggerPrefix = self::TRIGGER_PREFIX; + $temporary = $this->isInTempMode() ? 'TEMPORARY' : ''; + $schemaName = $this->isInTempMode() ? 'temp.' : ''; $stmts = []; foreach ($this->getAllTablesExceptPhinxlogs() as $table) { + if ($table === $dirtyTable) { + continue; + } $stmts[] = " - CREATE TRIGGER {$triggerPrefix}{$table} AFTER INSERT ON `$table` + CREATE {$temporary} TRIGGER {$triggerPrefix}{$table} AFTER INSERT ON `$table` BEGIN - INSERT OR IGNORE INTO {$dirtyTable} VALUES ('$table'); + INSERT OR IGNORE INTO {$dirtyTable} VALUES ('{$table}'), ('{$schemaName}{$dirtyTable}'); END; "; } @@ -112,50 +92,36 @@ public function createTriggers() /** * @inheritDoc */ - public function setup() + public function start() { - parent::setup(); + parent::start(); $this->createDirtyTableCollector(); $this->createTriggers(); + $this->cleanAllTables(); } /** * @inheritDoc */ - public function getTriggers(): array + public function shutdown() { - $triggerPrefix = self::TRIGGER_PREFIX; - $triggers = $this->fetchQuery(" - SELECT name - FROM sqlite_master - WHERE type = 'trigger' - AND name LIKE '{$triggerPrefix}%' - "); - - foreach ($triggers as $k => $trigger) { - if (strpos($trigger, self::TRIGGER_PREFIX) !== 0) { - unset($triggers[$k]); - } - } + parent::shutdown(); - return (array)$triggers; + $this->dropTriggers(); + $this->dropDirtyTableCollector(); } /** * @inheritDoc */ - public function dropTriggers() + public function markAllTablesAsDirty() { - $triggers = $this->getTriggers(); - - if (empty($triggers)) { - return; - } + $tables = $this->getAllTablesExceptPhinxlogs(); + $dirtyTable = self::DIRTY_TABLE_COLLECTOR; + $tables[] = $dirtyTable; - foreach ($triggers as $trigger) { - $this->getConnection()->execute(" - DROP TRIGGER $trigger;"); - } + $stmt = "INSERT OR IGNORE INTO {$dirtyTable} VALUES ('" . implode("'), ('", $tables) . "')"; + $this->getConnection()->execute($stmt); } } \ No newline at end of file diff --git a/src/Sniffer/TriggerBasedTableSnifferInterface.php b/src/Sniffer/TriggerBasedTableSnifferInterface.php index 5ba6366..e69de29 100644 --- a/src/Sniffer/TriggerBasedTableSnifferInterface.php +++ b/src/Sniffer/TriggerBasedTableSnifferInterface.php @@ -1,52 +0,0 @@ -getFixtureManager()->getActiveConnections() as $connectionName) { - $this->dirtyTables[$connectionName] = $this->getFixtureManager()->getSniffer($connectionName)->getDirtyTables(); + $this->dirtyTables[$connectionName] = SnifferRegistry::get($connectionName)->getDirtyTables(); } } @@ -93,10 +114,9 @@ public function getDirtyTables(): array /** * @param Test $test - * @param float $time * @return void */ - public function collectTestStatistics(Test $test, float $time) + public function collectTestStatistics(Test $test) { if ($this->isNotActivated()) { return; @@ -108,11 +128,13 @@ public function collectTestStatistics(Test $test, float $time) $testName = method_exists($test, 'getName') ? $test->getName() : 'Test name undefined'; $this->statistics[] = [ - round($time * 1000) / 1000, // Time in seconds - get_class($test), // Test Class name - $testName, // Test method name - count($dirtyTables), // Number of dirty tables - implode(', ', $dirtyTables), // Dirty tables + round($this->testDuration * 1000) / 1000, // Test duration in seconds + get_class($test), // Test Class name + $testName, // Test method name + count($dirtyTables), // Number of dirty tables + implode(', ', $dirtyTables), // Dirty tables + $this->fixturesLoadingTime, // Time taken for the fixtures to load + $this->testDuration > 0 ? round($this->fixturesLoadingTime * 100 / $this->testDuration) : 0, // Time taken for the fixtures to load in % ]; } @@ -136,7 +158,7 @@ private function castDirtyTables(): array foreach ($this->getDirtyTables() as $connection => $dirtyTables) { $db = ConnectionManager::get($connection)->config()['database']; foreach ($dirtyTables as $i => $dirtyTable) { - if ($dirtyTable !== TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR) { + if (strpos($dirtyTable, BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR) === false) { $dirtyTables[$i] = "$db.$dirtyTable"; } else { unset($dirtyTables[$i]); @@ -172,6 +194,8 @@ public function writeStatsInCsv() 'Test Method', '# Dirty Tables', 'Dirty Tables', + 'Static Fixtures Processing Time', + 'Static Fixtures Processing (%)', ]); foreach ($this->statistics as $stat) { @@ -221,4 +245,46 @@ public function getFixtureManager(): FixtureManager { return $this->fixtureManager; } + + /** + * @return void + */ + public function startsTestTime() + { + if ($this->isActivated) { + $this->startTestTime = \microtime(true); + } + } + + /** + * @return void + */ + public function startsLoadingFixturesTime() + { + if ($this->isActivated) { + $this->startLoadingFixtureTime = \microtime(true); + } + } + + /** + * @return void + */ + public function stopsTestTime() + { + if ($this->isActivated && $this->startTestTime !== null) { + $this->testDuration = \microtime(true) - $this->startTestTime; + $this->startTestTime = null; + } + } + + /** + * @return void + */ + public function stopsLoadingFixturesTime() + { + if ($this->isActivated && $this->startLoadingFixtureTime !== null) { + $this->fixturesLoadingTime = \microtime(true) - $this->startLoadingFixtureTime; + $this->startLoadingFixtureTime = null; + } + } } \ No newline at end of file diff --git a/tests/DropTablesTestCase/TableSnifferDropCitiesTest.php b/tests/DropTablesTestCase/TableSnifferDropCitiesTest.php index b4b3882..e69de29 100644 --- a/tests/DropTablesTestCase/TableSnifferDropCitiesTest.php +++ b/tests/DropTablesTestCase/TableSnifferDropCitiesTest.php @@ -1,113 +0,0 @@ -FixtureManager = new FixtureManager(); - $this->TableSniffer = $this->FixtureManager->getSniffer('test'); - $this->Countries = TableRegistry::getTableLocator()->get('Countries'); - $this->Cities = TableRegistry::getTableLocator()->get('Cities'); - } - - private function activateForeignKeysOnSqlite() { - $connection = ConnectionManager::get('test'); - if ($connection->config()['driver'] === Sqlite::class) { - $connection->execute('PRAGMA foreign_keys = ON;' ); - } - } - - private function createCountry(): Country - { - $country = $this->Countries->newEntity([ - 'name' => 'Foo', - ]); - return $this->Countries->saveOrFail($country); - } - - private function createCity(): City - { - $city = $this->Cities->newEntity([ - 'uuid_primary_key' => TestUtil::makeUuid(), - 'id_primary_key' => rand(1, 99999999), - 'name' => 'Foo', - 'country_id' => $this->createCountry()->id - ]); - return $this->Cities->saveOrFail($city); - } - - public function tearDown() - { - unset($this->TableSniffer); - unset($this->FixtureManager); - unset($this->Countries); - unset($this->Cities); - - parent::tearDown(); - } - - public function testDropWithForeignKeyCheckCities() - { - $this->activateForeignKeysOnSqlite(); - $this->createCity(); - $this->TableSniffer->dropTables($this->TableSniffer->fetchAllTables()); - - $this->expectException(\PDOException::class); - $this->Cities->find()->first(); - } -} \ No newline at end of file diff --git a/tests/DropTablesTestCase/TableSnifferDropTablesTest.php b/tests/DropTablesTestCase/TableSnifferDropTablesTest.php index 41387ed..e69de29 100644 --- a/tests/DropTablesTestCase/TableSnifferDropTablesTest.php +++ b/tests/DropTablesTestCase/TableSnifferDropTablesTest.php @@ -1,102 +0,0 @@ -FixtureManager = new FixtureManager(); - $this->TableSniffer = $this->FixtureManager->getSniffer('test'); - $this->Countries = TableRegistry::getTableLocator()->get('Countries'); - $this->Cities = TableRegistry::getTableLocator()->get('Cities'); - } - - public function tearDown() - { - unset($this->TableSniffer); - unset($this->FixtureManager); - unset($this->Countries); - unset($this->Cities); - ConnectionManager::drop('test_dummy_connection'); - - parent::tearDown(); - } - - /** - * After dropping all tables, only the dirty table collecting table should remain - * This should never be dropped - */ - public function testGetAllTablesAfterDroppingAll() - { - $this->assertSame( - 1, - $this->Countries->find()->count() - ); - $this->assertSame( - 1, - $this->Cities->find()->count() - ); - - $this->FixtureManager->dropTables('test'); - - if ($this->TableSniffer->implementsTriggers()) { - $expected = [TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR]; - } else { - $expected = []; - } - $this->assertSame($expected, $this->TableSniffer->fetchAllTables()); - - $this->FixtureManager->unload($this); - } -} \ No newline at end of file diff --git a/tests/TestApp/config/Migrations/20200208100000_initial_migration.php b/tests/TestApp/config/Migrations/20200208100000_initial_migration.php index a12a572..9a661a0 100644 --- a/tests/TestApp/config/Migrations/20200208100000_initial_migration.php +++ b/tests/TestApp/config/Migrations/20200208100000_initial_migration.php @@ -13,7 +13,7 @@ */ use Cake\Datasource\ConnectionManager; -use CakephpTestSuiteLight\FixtureManager; +use CakephpTestSuiteLight\Sniffer\SnifferRegistry; use Migrations\AbstractMigration; class InitialMigration extends AbstractMigration @@ -23,8 +23,7 @@ class InitialMigration extends AbstractMigration */ public function up() { - $manager = new FixtureManager(); - $supportsUuid = $manager->getSniffer('test')->implementsTriggers(); + $supportsUuid = SnifferRegistry::get('test')->implementsTriggers(); // Sqlite is not happy with the composite and/or uuid concept if (!$supportsUuid || ConnectionManager::getConfig('test')['driver'] === 'Cake\Database\Driver\Sqlite') { diff --git a/tests/TestCase/FixtureManagerTest.php b/tests/TestCase/FixtureManagerTest.php index 6865e81..837fb5e 100644 --- a/tests/TestCase/FixtureManagerTest.php +++ b/tests/TestCase/FixtureManagerTest.php @@ -15,16 +15,10 @@ use Cake\Core\Configure; -use Cake\Database\Driver\Mysql; -use Cake\Database\Driver\Postgres; -use Cake\Database\Driver\Sqlite; use Cake\Datasource\ConnectionManager; use Cake\ORM\TableRegistry; use Cake\TestSuite\TestCase; use CakephpTestSuiteLight\FixtureManager; -use CakephpTestSuiteLight\Sniffer\MysqlTriggerBasedTableSniffer; -use CakephpTestSuiteLight\Sniffer\PostgresTriggerBasedTableSniffer; -use CakephpTestSuiteLight\Sniffer\SqliteTriggerBasedTableSniffer; use TestApp\Model\Table\CountriesTable; use TestApp\Test\Fixture\CitiesFixture; use TestApp\Test\Fixture\CountriesFixture; @@ -97,26 +91,6 @@ public function testConnectionIsTest() ); } - public function dataProviderTestLoadDefaultSniffer() - { - return [ - [Mysql::class, MysqlTriggerBasedTableSniffer::class], - [Sqlite::class, SqliteTriggerBasedTableSniffer::class], - [Postgres::class, PostgresTriggerBasedTableSniffer::class], - ]; - } - - /** - * @param $driver - * @param $sniffer - * @dataProvider dataProviderTestLoadDefaultSniffer - */ - public function testGetDefaultTableSniffers($driver, $sniffer) - { - $act = $this->FixtureManager->getDefaultTableSniffers()[$driver]; - $this->assertEquals($sniffer, $act); - } - public function testLoadSnifferFromConfigFile() { $expected = '\testTableSniffer'; @@ -125,24 +99,6 @@ public function testLoadSnifferFromConfigFile() $this->assertEquals($expected, $conf); } - public function testGetConnectionSnifferNameOnNonExistingConnection() - { - $this->expectException(\PHPUnit\Framework\Exception::class); - $this->FixtureManager->getConnectionSnifferName('dummy'); - } - - public function testGetConnectionSnifferNameOnConnection() - { - $sniffer = 'FooSniffer'; - $connectionName = 'testGetConnectionSnifferNameOnConnection'; - $testConfig = ConnectionManager::getConfig('test'); - $testConfig['tableSniffer'] = $sniffer; - ConnectionManager::setConfig($connectionName, $testConfig); - $act = $this->FixtureManager->getConnectionSnifferName($connectionName); - $this->assertSame($sniffer, $act); - ConnectionManager::drop($connectionName); - } - public function testFetchActiveConnections() { $this->FixtureManager->fetchActiveConnections(); diff --git a/tests/TestCase/Sniffer/SnifferRegistryTest.php b/tests/TestCase/Sniffer/SnifferRegistryTest.php new file mode 100644 index 0000000..391daf7 --- /dev/null +++ b/tests/TestCase/Sniffer/SnifferRegistryTest.php @@ -0,0 +1,67 @@ +assertEquals($sniffer, $act); + } + + public function testGetConnectionSnifferNameOnNonExistingConnection() + { + $this->expectException(Exception::class); + SnifferRegistry::getConnectionSnifferName('dummy'); + } + + public function testGetConnectionSnifferNameOnConnection() + { + $sniffer = 'FooSniffer'; + $connectionName = 'testGetConnectionSnifferNameOnConnection'; + $testConfig = ConnectionManager::getConfig('test'); + $testConfig['tableSniffer'] = $sniffer; + ConnectionManager::setConfig($connectionName, $testConfig); + $act = SnifferRegistry::getConnectionSnifferName($connectionName); + $this->assertSame($sniffer, $act); + ConnectionManager::drop($connectionName); + } +} \ No newline at end of file diff --git a/tests/DropTablesTestCase/TableSnifferDropCountriesTest.php b/tests/TestCase/Sniffer/TableSnifferDropTablesTest.php similarity index 64% rename from tests/DropTablesTestCase/TableSnifferDropCountriesTest.php rename to tests/TestCase/Sniffer/TableSnifferDropTablesTest.php index 37d41f0..feb157c 100644 --- a/tests/DropTablesTestCase/TableSnifferDropCountriesTest.php +++ b/tests/TestCase/Sniffer/TableSnifferDropTablesTest.php @@ -11,24 +11,25 @@ * @since 1.0.0 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -namespace CakephpTestSuiteLight\Test\DropTablesTestCase; +namespace CakephpTestSuiteLight\Test\TestCase\Sniffer; use Cake\Database\Driver\Sqlite; use Cake\Datasource\ConnectionManager; +use Cake\Datasource\EntityInterface; use Cake\ORM\TableRegistry; use Cake\TestSuite\TestCase; use CakephpTestSuiteLight\FixtureManager; use CakephpTestSuiteLight\Sniffer\BaseTableSniffer; +use CakephpTestSuiteLight\Sniffer\SnifferRegistry; use CakephpTestSuiteLight\Test\TestUtil; -use TestApp\Model\Entity\City; -use TestApp\Model\Entity\Country; +use Migrations\Migrations; use TestApp\Model\Table\CitiesTable; use TestApp\Model\Table\CountriesTable; use TestApp\Test\Fixture\CitiesFixture; use TestApp\Test\Fixture\CountriesFixture; -class TableSnifferDropCountriesTest extends TestCase +class TableSnifferDropTablesTest extends TestCase { public $fixtures = [ // The order here is important @@ -60,21 +61,76 @@ class TableSnifferDropCountriesTest extends TestCase public function setUp() { $this->FixtureManager = new FixtureManager(); - $this->TableSniffer = $this->FixtureManager->getSniffer('test'); + $this->TableSniffer = SnifferRegistry::get('test'); $this->Countries = TableRegistry::getTableLocator()->get('Countries'); $this->Cities = TableRegistry::getTableLocator()->get('Cities'); } public function tearDown() { + $this->runMigrations(); + + $this->TableSniffer->start(); + unset($this->TableSniffer); unset($this->FixtureManager); unset($this->Countries); unset($this->Cities); + ConnectionManager::drop('test_dummy_connection'); parent::tearDown(); } + /** + * After dropping all tables, only the dirty table collecting table should remain + * This should never be dropped + */ + public function testGetAllTablesAfterDroppingAll() + { + $this->assertSame( + 1, + $this->Countries->find()->count() + ); + $this->assertSame( + 1, + $this->Cities->find()->count() + ); + + $this->FixtureManager->dropTables('test'); + + $this->assertSame([], $this->TableSniffer->fetchAllTables()); + + $this->FixtureManager->unload($this); + } + + public function testDropWithForeignKeyCheckCities() + { + $this->activateForeignKeysOnSqlite(); + $this->createCity(); + $this->TableSniffer->dropTables($this->TableSniffer->fetchAllTables()); + + $this->expectException(\PDOException::class); + $this->Cities->find()->first(); + } + + public function testDropWithForeignKeyCheckCountries() + { + $this->activateForeignKeysOnSqlite(); + $this->createCity(); // This will create a country too + $this->TableSniffer->dropTables($this->TableSniffer->fetchAllTables()); + + $this->expectException(\PDOException::class); + $this->Countries->find()->first(); + } + + private function runMigrations() + { + $migrations = new Migrations(); + $migrations->migrate([ + 'connection' => 'test', + ]); + } + private function activateForeignKeysOnSqlite() { $connection = ConnectionManager::get('test'); if ($connection->config()['driver'] === Sqlite::class) { @@ -82,7 +138,7 @@ private function activateForeignKeysOnSqlite() { } } - private function createCountry(): Country + private function createCountry(): EntityInterface { $country = $this->Countries->newEntity([ 'name' => 'Foo', @@ -90,7 +146,7 @@ private function createCountry(): Country return $this->Countries->saveOrFail($country); } - private function createCity(): City + private function createCity(): EntityInterface { $city = $this->Cities->newEntity([ 'uuid_primary_key' => TestUtil::makeUuid(), @@ -100,14 +156,4 @@ private function createCity(): City ]); return $this->Cities->saveOrFail($city); } - - public function testDropWithForeignKeyCheckCities() - { - $this->activateForeignKeysOnSqlite(); - $this->createCity(); // This will create a country too - $this->TableSniffer->dropTables($this->TableSniffer->fetchAllTables()); - - $this->expectException(\PDOException::class); - $this->Countries->find()->first(); - } } \ No newline at end of file diff --git a/tests/TestCase/Sniffer/TableSnifferTest.php b/tests/TestCase/Sniffer/TableSnifferTest.php index 7085a6c..b6c1873 100644 --- a/tests/TestCase/Sniffer/TableSnifferTest.php +++ b/tests/TestCase/Sniffer/TableSnifferTest.php @@ -18,14 +18,17 @@ use Cake\TestSuite\TestCase; use CakephpTestSuiteLight\FixtureManager; use CakephpTestSuiteLight\Sniffer\BaseTableSniffer; -use CakephpTestSuiteLight\Sniffer\TriggerBasedTableSnifferInterface; +use CakephpTestSuiteLight\Sniffer\BaseTriggerBasedTableSniffer; +use CakephpTestSuiteLight\Sniffer\SnifferRegistry; use CakephpTestSuiteLight\Test\Traits\ArrayComparerTrait; +use CakephpTestSuiteLight\Test\Traits\SnifferHelperTrait; use TestApp\Test\Fixture\CitiesFixture; use TestApp\Test\Fixture\CountriesFixture; class TableSnifferTest extends TestCase { use ArrayComparerTrait; + use SnifferHelperTrait; public $fixtures = [ // The order here is important @@ -48,14 +51,12 @@ class TableSnifferTest extends TestCase public function setUp() { - $this->FixtureManager = new FixtureManager(); - $this->TableSniffer = $this->FixtureManager->getSniffer('test'); + $this->TableSniffer = SnifferRegistry::get('test'); } public function tearDown() { unset($this->TableSniffer); - unset($this->FixtureManager); ConnectionManager::drop('test_dummy_connection'); @@ -69,11 +70,6 @@ private function createNonExistentConnection() ConnectionManager::setConfig('test_dummy_connection', $config); } - private function driverIs(string $driver): bool - { - return ConnectionManager::getConfig('test')['driver'] === 'Cake\Database\Driver\\' . $driver; - } - public function dataProviderOfDirtyTables() { return [ @@ -85,6 +81,7 @@ public function dataProviderOfDirtyTables() /** * All tables should be clean before every test * @dataProvider dataProviderOfDirtyTables + * @param bool $loadFixtures */ public function testGetDirtyTablesWithLoadFixtures(bool $loadFixtures) { @@ -100,6 +97,7 @@ public function testGetDirtyTablesWithLoadFixtures(bool $loadFixtures) /** * All tables should be clean before every test * @dataProvider dataProviderOfDirtyTables + * @param bool $loadFixtures */ public function testGetDirtyTablesWithLoadOneCity(bool $loadFixtures) { @@ -119,17 +117,27 @@ public function testGetDirtyTablesWithLoadOneCity(bool $loadFixtures) /** * All tables should be clean before every test * @dataProvider dataProviderOfDirtyTables + * @param bool $loadFixtures */ public function testGetDirtyTablesWithLoadOneCountry(bool $loadFixtures) { if ($loadFixtures) { + $expected = [ + 'countries', + ]; $this->loadFixtures('Countries'); - $expected = $this->TableSniffer->implementsTriggers() ? 2 : 1; // Expect cities, but also the dirty_table table + if ($this->TableSniffer->implementsTriggers()) { + if ($this->driverIs('Sqlite') && $this->TableSniffer->isInTempMode()) { + $expected[] = 'temp.' . BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR; + } else { + $expected[] = BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR; + } + } } else { - $expected = 0; + $expected = []; } - $this->assertSame($expected, count($this->TableSniffer->getDirtyTables())); + $this->assertArraysHaveSameContent($expected, $this->TableSniffer->getDirtyTables()); } /** @@ -138,13 +146,12 @@ public function testGetDirtyTablesWithLoadOneCountry(bool $loadFixtures) public function testGetSnifferOnNonExistentDB() { $this->createNonExistentConnection(); - if ($this->driverIs('Sqlite')) { $this->assertTrue(true); } else { $this->expectException(\Exception::class); } - $this->FixtureManager->getSniffer('test_dummy_connection'); + SnifferRegistry::get('test_dummy_connection'); } public function testImplodeSpecial() @@ -156,9 +163,9 @@ public function testImplodeSpecial() $this->assertSame($expect, $this->TableSniffer->implodeSpecial($glueBefore, $array, $glueAfter)); } - public function testCheckTriggersAfterSetup() + public function testCheckTriggersAfterStart() { - $this->skipIf(!$this->TableSniffer->implementsTriggers()); + $this->skipUnless($this->TableSniffer->implementsTriggers()); $expected = [ 'dirty_table_spy_cities', @@ -168,10 +175,13 @@ public function testCheckTriggersAfterSetup() $found = $this->TableSniffer->fetchQuery('SHOW TRIGGERS'); } elseif ($this->driverIs('Postgres')) { $found = $this->TableSniffer->fetchQuery('SELECT tgname FROM pg_trigger'); - $expected[] = 'dirty_table_spy_' . TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR; } elseif ($this->driverIs('Sqlite')) { - $found = $this->TableSniffer->fetchQuery('SELECT name FROM sqlite_master WHERE type = "trigger"'); - $expected[] = 'dirty_table_spy_' . TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR; + if ($this->TableSniffer->implementsTriggers() && $this->TableSniffer->isInTempMode()) { + $found = $this->TableSniffer->fetchQuery('SELECT name FROM sqlite_temp_master WHERE type = "trigger"'); + } else { + $found = $this->TableSniffer->fetchQuery('SELECT name FROM sqlite_master WHERE type = "trigger"'); + } + } foreach ($expected as $trigger) { @@ -181,18 +191,84 @@ public function testCheckTriggersAfterSetup() public function testGetAllTablesExceptPhinxlogs() { - $found = $this->TableSniffer->getAllTablesExceptPhinxlogs(); + $found = $this->TableSniffer->getAllTablesExceptPhinxlogs(true); $expected = [ 'cities', 'countries', ]; + if ($this->TableSniffer->implementsTriggers() && $this->TableSniffer->isInMainMode()) { + $expected[] = BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR; + } + $this->assertArraysHaveSameContent($expected, $found); + } + + public function testMarkAllTablesAsDirty() + { + $this->skipUnless($this->TableSniffer->implementsTriggers()); + + $dirtyTables = $this->TableSniffer->getDirtyTables(); + $this->assertSame([], $dirtyTables); + + $this->TableSniffer->markAllTablesAsDirty(); + + $dirtyTables = $this->TableSniffer->getDirtyTables(); + $this->assertArraysHaveSameContent([ + 'cities', + 'countries', + BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR, + ], $dirtyTables); + } + + public function testGetTriggers() + { if ($this->TableSniffer->implementsTriggers()) { - $expected[] = TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR; + $expect = [ + 'dirty_table_spy_cities', + 'dirty_table_spy_countries', + ]; } else { - $this->TableSniffer->removeDirtyTableCollectorFromArray($found); + $expect = []; } - $this->assertArraysHaveSameContent($expected, $found); + $this->assertArraysHaveSameContent($expect, $this->TableSniffer->getTriggers()); + } + + public function testCreateTriggers() + { + $this->skipUnless($this->TableSniffer->implementsTriggers()); + + $this->TableSniffer->createTriggers(); + + $triggers = $this->TableSniffer->getTriggers(); + $this->assertArraysHaveSameContent([ + 'dirty_table_spy_cities', + 'dirty_table_spy_countries', + ], $triggers); + } + + public function testDropTriggers() + { + $this->TableSniffer->dropTriggers(); + $this->assertArraysHaveSameContent([], $this->TableSniffer->getTriggers()); + if ($this->TableSniffer->implementsTriggers()) { + $this->TableSniffer->createTriggers(); + } + } + + public function testSwitchMode() + { + $this->skipUnless($this->TableSniffer->implementsTriggers()); + $mode = $this->TableSniffer->getMode(); + + foreach ([1, 2, 3] as $i) { + $this->TableSniffer->activateTempMode(); + $this->assertSame(false, in_array(BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR, $this->TableSniffer->getAllTables(true))); + + $this->TableSniffer->activateMainMode(); + $this->assertSame(true, in_array(BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR, $this->TableSniffer->getAllTables(true))); + } + + $this->TableSniffer->setMode($mode); } } \ No newline at end of file diff --git a/tests/TestCase/Sniffer/TableSnifferWithFixturesTest.php b/tests/TestCase/Sniffer/TableSnifferWithFixturesTest.php index 95c114d..8d2075e 100644 --- a/tests/TestCase/Sniffer/TableSnifferWithFixturesTest.php +++ b/tests/TestCase/Sniffer/TableSnifferWithFixturesTest.php @@ -16,16 +16,15 @@ use Cake\Database\Driver\Sqlite; use Cake\Datasource\ConnectionManager; +use Cake\Datasource\EntityInterface; use Cake\ORM\TableRegistry; use Cake\TestSuite\TestCase; -use CakephpTestSuiteLight\FixtureManager; use CakephpTestSuiteLight\Sniffer\BaseTableSniffer; -use CakephpTestSuiteLight\Sniffer\MysqlTriggerBasedTableSniffer; -use CakephpTestSuiteLight\Sniffer\TriggerBasedTableSnifferInterface; +use CakephpTestSuiteLight\Sniffer\BaseTriggerBasedTableSniffer; +use CakephpTestSuiteLight\Sniffer\SnifferRegistry; use CakephpTestSuiteLight\Test\TestUtil; use CakephpTestSuiteLight\Test\Traits\ArrayComparerTrait; -use TestApp\Model\Entity\City; -use TestApp\Model\Entity\Country; +use CakephpTestSuiteLight\Test\Traits\SnifferHelperTrait; use TestApp\Model\Table\CitiesTable; use TestApp\Model\Table\CountriesTable; use TestApp\Test\Fixture\CitiesFixture; @@ -34,6 +33,7 @@ class TableSnifferWithFixturesTest extends TestCase { use ArrayComparerTrait; + use SnifferHelperTrait; public $fixtures = [ // The order here is important @@ -46,11 +46,6 @@ class TableSnifferWithFixturesTest extends TestCase */ public $TableSniffer; - /** - * @var FixtureManager - */ - public $FixtureManager; - /** * @var CountriesTable */ @@ -63,8 +58,7 @@ class TableSnifferWithFixturesTest extends TestCase public function setUp() { - $this->FixtureManager = new FixtureManager(); - $this->TableSniffer = $this->FixtureManager->getSniffer('test'); + $this->TableSniffer = SnifferRegistry::get('test'); $this->Countries = TableRegistry::getTableLocator()->get('Countries'); $this->Cities = TableRegistry::getTableLocator()->get('Cities'); @@ -74,7 +68,6 @@ public function setUp() public function tearDown() { unset($this->TableSniffer); - unset($this->FixtureManager); unset($this->Countries); unset($this->Cities); ConnectionManager::drop('test_dummy_connection'); @@ -93,11 +86,14 @@ public function testGetDirtyTables() 'cities', ]; if ($this->TableSniffer->implementsTriggers()) { - $expected[] = TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR; + if ($this->driverIs('Sqlite') && $this->TableSniffer->isInTempMode()) { + $expected[] = 'temp.' . BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR; + } else { + $expected[] = BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR; + } } - $country = $this->Countries->newEntity(['name' => 'foo']); - $this->Countries->saveOrFail($country); + $this->createCountry(); $found = $this->TableSniffer->getDirtyTables(); $this->assertArraysHaveSameContent($expected, $found); } @@ -113,11 +109,8 @@ public function testGetAllTables() 'countries', 'phinxlog', ]; - - if ($this->TableSniffer->implementsTriggers()) { - $expected[] = TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR; - } else { - $this->TableSniffer->removeDirtyTableCollectorFromArray($found); + if ($this->TableSniffer->implementsTriggers() && $this->TableSniffer->isInMainMode()) { + $expected[] = BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR; } $this->assertArraysHaveSameContent($expected, $found); @@ -125,16 +118,14 @@ public function testGetAllTables() public function testGetAllTablesExceptPhinxlogs() { - $found = $this->TableSniffer->getAllTablesExceptPhinxlogs(); + $found = $this->TableSniffer->getAllTablesExceptPhinxlogs(true); $expected = [ 'cities', 'countries', ]; - if ($this->TableSniffer->implementsTriggers()) { - $expected[] = TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR; - } else { - $this->TableSniffer->removeDirtyTableCollectorFromArray($found); + if ($this->TableSniffer->implementsTriggers() && $this->TableSniffer->isInMainMode()) { + $expected[] = BaseTriggerBasedTableSniffer::DIRTY_TABLE_COLLECTOR; } $this->assertArraysHaveSameContent($expected, $found); @@ -147,7 +138,7 @@ private function activateForeignKeysOnSqlite() { } } - private function createCountry(): Country + private function createCountry(): EntityInterface { $country = $this->Countries->newEntity([ 'name' => 'Foo', @@ -155,7 +146,7 @@ private function createCountry(): Country return $this->Countries->saveOrFail($country); } - private function createCity(): City + private function createCity(): EntityInterface { $city = $this->Cities->newEntity([ 'uuid_primary_key' => TestUtil::makeUuid(), @@ -184,7 +175,7 @@ public function testTruncateWithForeignKey() { $this->createCity(); - $this->TableSniffer->truncateDirtyTables($this->TableSniffer->getDirtyTables()); + $this->TableSniffer->truncateDirtyTables(); $this->assertSame( 0, @@ -201,9 +192,6 @@ public function testGetAndDropTriggers() 'dirty_table_spy_countries', 'dirty_table_spy_cities', ]; - if (!($this->TableSniffer instanceof MysqlTriggerBasedTableSniffer)) { - $expected[] = 'dirty_table_spy_' . TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR; - } $this->assertArraysHaveSameContent($expected, $found); @@ -212,6 +200,6 @@ public function testGetAndDropTriggers() $found = $this->TableSniffer->getTriggers(); $this->assertArraysHaveSameContent($expected, $found); - $this->TableSniffer->setup(); + $this->TableSniffer->start(); } } \ No newline at end of file diff --git a/tests/TestCase/Sniffer/TableSnifferWithMigrationTest.php b/tests/TestCase/Sniffer/TableSnifferWithMigrationTest.php index 7903cb0..5b7eb69 100644 --- a/tests/TestCase/Sniffer/TableSnifferWithMigrationTest.php +++ b/tests/TestCase/Sniffer/TableSnifferWithMigrationTest.php @@ -15,10 +15,8 @@ use Cake\TestSuite\TestCase; -use CakephpTestSuiteLight\FixtureManager; use CakephpTestSuiteLight\Sniffer\BaseTableSniffer; -use CakephpTestSuiteLight\Sniffer\MysqlTriggerBasedTableSniffer; -use CakephpTestSuiteLight\Sniffer\TriggerBasedTableSnifferInterface; +use CakephpTestSuiteLight\Sniffer\SnifferRegistry; use CakephpTestSuiteLight\Test\Traits\ArrayComparerTrait; use Migrations\Migrations; @@ -36,27 +34,51 @@ class TableSnifferWithMigrationTest extends TestCase */ public $TableSniffer; + /** + * @var bool + */ + public static $snifferWasInTempMod; + + public static function setUpBeforeClass() + { + if (SnifferRegistry::get('test')->implementsTriggers() && SnifferRegistry::get('test')->isInTempMode()) { + SnifferRegistry::get('test')->activateMainMode(); + self::$snifferWasInTempMod = true; + } + } + + public static function tearDownAfterClass() + { + if (SnifferRegistry::get('test')->implementsTriggers() && self::$snifferWasInTempMod) { + SnifferRegistry::get('test')->activateTempMode(); + } + } - public function setUp(): void + public function setUp() { - $fixtureManager = new FixtureManager(); - $this->TableSniffer = $fixtureManager->getSniffer('test'); + $this->TableSniffer = SnifferRegistry::get('test'); $config = [ 'connection' => 'test', 'source' => 'TestMigrations', ]; - $this->migrations = new Migrations($config); + $this->migrations = new Migrations(); $this->migrations->migrate($config); } - public function tearDown(): void + public function tearDown() { unset($this->TableSniffer); - $this->migrations->rollback(); - $this->migrations->rollback(); + $this->migrations->rollback([ + 'connection' => 'test', + 'source' => 'TestMigrations', + ]); + $this->migrations->rollback([ + 'connection' => 'test', + 'source' => 'TestMigrations', + ]); } protected function countProducts(): int @@ -72,7 +94,7 @@ protected function countProducts(): int * after the setup of the sniffer triggers, * it is not marked as dirty */ - public function testPopulateWithMigrationsWithoutSetup() + public function testPopulateWithMigrationsWithoutRestart() { $tables = $this->TableSniffer->fetchAllTables(); $this->assertTrue(in_array('products', $tables)); @@ -84,38 +106,41 @@ public function testPopulateWithMigrationsWithoutSetup() } } - public function testPopulateWithMigrationsWithSetup() + public function testPopulateWithMigrationsWithRestart() { $tables = $this->TableSniffer->fetchAllTables(); $this->assertTrue(in_array('products', $tables)); // Rollback the table products population migration - $this->migrations->rollback(); + $this->migrations->rollback([ + 'connection' => 'test', + 'source' => 'TestMigrations', + ]); $expected = [ 'dirty_table_spy_countries', 'dirty_table_spy_cities', ]; - if (!($this->TableSniffer instanceof MysqlTriggerBasedTableSniffer)) { - $expected[] = 'dirty_table_spy_' . TriggerBasedTableSnifferInterface::DIRTY_TABLE_COLLECTOR; - } if ($this->TableSniffer->implementsTriggers()) { $this->assertArraysHaveSameContent($expected, $this->TableSniffer->getTriggers()); } // Reset the triggers - $this->TableSniffer->setup(); + $this->TableSniffer->restart(); if ($this->TableSniffer->implementsTriggers()) { $expected[] = 'dirty_table_spy_products'; - $this->assertArraysHaveSameContent( $expected, $this->TableSniffer->getTriggers()); + $this->assertArraysHaveSameContent($expected, $this->TableSniffer->getTriggers()); } $nProducts = $this->countProducts(); // Populate the products table - $this->migrations->migrate(); + $this->migrations->migrate([ + 'connection' => 'test', + 'source' => 'TestMigrations', + ]); if ($this->TableSniffer->implementsTriggers()) { $this->assertArraysHaveSameContent($expected, $this->TableSniffer->getTriggers()); diff --git a/tests/TestCase/StatisticToolTest.php b/tests/TestCase/StatisticToolTest.php index 883aaef..51e93cc 100644 --- a/tests/TestCase/StatisticToolTest.php +++ b/tests/TestCase/StatisticToolTest.php @@ -58,9 +58,13 @@ public function tearDown() public function testCollectTestStatistics() { // Arrange + $this->StatisticTool->startsTestTime(); + $this->StatisticTool->startsLoadingFixturesTime(); $this->loadFixtures(); - $time = 0.1239999; - $this->StatisticTool->collectTestStatistics($this, $time); + $this->StatisticTool->stopsLoadingFixturesTime(); + $this->StatisticTool->stopsTestTime(); + + $this->StatisticTool->collectTestStatistics($this); $db = ConnectionManager::get('test')->config()['database']; // Act @@ -70,10 +74,15 @@ public function testCollectTestStatistics() $this->assertSame(1, count($stats)); $stats = $stats[0]; - $this->assertSame(0.124, $stats[0]); + // Duration of the test + $this->assertSame(true, $stats[0] > 0); + // Test class name $this->assertSame(self::class, $stats[1]); + // Test method name $this->assertSame(__FUNCTION__, $stats[2]); + // Number of dirty tables $this->assertSame(2, $stats[3]); + // List of dirty tables $this->assertSame("$db.cities, $db.countries", $stats[4]); } diff --git a/tests/Traits/SnifferHelperTrait.php b/tests/Traits/SnifferHelperTrait.php new file mode 100644 index 0000000..e427b46 --- /dev/null +++ b/tests/Traits/SnifferHelperTrait.php @@ -0,0 +1,15 @@ + '\1']); -$migrations = new \Migrations\Migrations(['connection' => 'test']); -$migrations->migrate(); \ No newline at end of file + +// Prepare the DB +SnifferRegistry::clear(); + +if (getenv('SNIFFERS_IN_MAIN_MODE') && SnifferRegistry::get('test')->implementsTriggers()) { + SnifferRegistry::get('test')->activateMainMode(); +} + +SnifferRegistry::get('test')->dropTriggers(); + +if (getenv('USE_NON_TRIGGERED_BASED_SNIFFERS') && !SnifferRegistry::get('test')->implementsTriggers()) { + SnifferRegistry::get('test')->dropTables( + SnifferRegistry::get('test')->getAllTables(true) + ); +} + +// Run migrations +$migrations = new Migrations(); +$migrations->migrate(); + +// Clear the Sniffers, ready to start the tests +SnifferRegistry::clear();