From be7094326d065aad9f6291a9c8e4159e5f51e994 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 29 Jan 2016 01:13:09 -0800 Subject: [PATCH] CRM-17860 - CiviUnitTestCase - Maintain headless schema automatically --- tests/phpunit/CiviTest/CiviTestDB.php | 166 +++++++++++++++++--- tests/phpunit/CiviTest/CiviTestPdoUtils.php | 3 + tests/phpunit/CiviTest/CiviUnitTestCase.php | 26 ++- 3 files changed, 168 insertions(+), 27 deletions(-) diff --git a/tests/phpunit/CiviTest/CiviTestDB.php b/tests/phpunit/CiviTest/CiviTestDB.php index 2bcf83b76f..5b688c9a7a 100644 --- a/tests/phpunit/CiviTest/CiviTestDB.php +++ b/tests/phpunit/CiviTest/CiviTestDB.php @@ -3,37 +3,129 @@ class CiviTestDB { /** - * @param string $dbName + * @var CiviTestPdoUtils + */ + protected $pdoUtils; + + /** + * CiviTestDB constructor. * @param CiviTestPdoUtils $pdoUtils + */ + public function __construct($pdoUtils) { + $this->pdoUtils = $pdoUtils; + } + + /** + * Autocreate the schema. + * + * Check civitest_schema_rev. If it's out-of-date, drop the old schema and load the + * new one. + * + * @return mixed + */ + public function updateSchema() { + $schemaFile = dirname(dirname(dirname(dirname(__FILE__)))) . "/sql/civicrm.mysql"; + if (!is_file($schemaFile)) { + return $this->fatal("Failed to find schema: $schemaFile"); + } + + // $schemaFileRev = md5(@file_get_contents($schemaFile)); + $schemaFileRev = filemtime($schemaFile) . ' ' . filectime($schemaFile); + + $tables = $this->getCurrentTables('BASE TABLE'); + $liveSchemaRev = ''; + if (in_array('civitest_schema_rev', $tables)) { + $pdoStmt = $this->pdoUtils->pdo->query("SELECT schema_rev FROM {$this->pdoUtils->dbName}.civitest_schema_rev"); + foreach ($pdoStmt as $row) { + $liveSchemaRev = $row['schema_rev']; + } + } + + if ($liveSchemaRev === $schemaFileRev) { + return; + } + + echo "Installing {$this->pdoUtils->dbName} schema\n"; + $this->dropSchema(); + if ($this->pdoUtils->do_query(@file_get_contents($schemaFile)) === FALSE) { + return $this->fatal("Cannot load $schemaFile. Aborting."); + } + $query = sprintf( + "USE {$this->pdoUtils->dbName};" + . "DROP TABLE IF EXISTS civitest_schema_rev;" + . "CREATE TABLE civitest_schema_rev (schema_rev VARCHAR(64));" + . "INSERT INTO civitest_schema_rev (schema_rev) VALUES (%s);", + $this->pdoUtils->pdo->quote($schemaFileRev) + ); + + if ($this->pdoUtils->do_query($query) === FALSE) { + return $this->fatal("Failed to flag schema version: $query"); + } + } + + public function dropSchema() { + $queries = array( + "USE {$this->pdoUtils->dbName};", + "SET foreign_key_checks = 0", + // SQL mode needs to be strict, that's our standard + "SET SQL_MODE='STRICT_ALL_TABLES';", + "SET global innodb_flush_log_at_trx_commit = 2;", + ); + + foreach ($this->getCurrentTables('VIEW') as $table) { + if (preg_match('/^(civicrm_|log_)/', $table)) { + $queries[] = "DROP VIEW $table"; + } + } + + foreach ($this->getCurrentTables('BASE TABLE') as $table) { + if (preg_match('/^(civicrm_|log_)/', $table)) { + $queries[] = "DROP TABLE $table"; + } + } + + $queries[] = "set global innodb_flush_log_at_trx_commit = 1;"; + $queries[] = "SET foreign_key_checks = 1"; + + foreach ($queries as $query) { + if ($this->pdoUtils->do_query($query) === FALSE) { + return $this->fatal("dropSchema: Query failed: $query"); + } + } + } + + /** * @return bool */ - public static function realPopulateDB($dbName, $pdoUtils) { - $pdo = $pdoUtils->pdo; - // only consider real tables and not views - $tables = $pdo->query("SELECT table_name FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = '{$dbName}' AND TABLE_TYPE = 'BASE TABLE'"); + public function populate() { + $pdoUtils = $this->pdoUtils; + $tables = $this->getCurrentTables('BASE TABLE'); $truncates = array(); $drops = array(); foreach ($tables as $table) { // skip log tables - if (substr($table['table_name'], 0, 4) == 'log_') { + if (substr($table, 0, 4) == 'log_') { continue; } // don't change list of installed extensions - if ($table['table_name'] == 'civicrm_extension') { + if ($table == 'civicrm_extension') { continue; } - if (substr($table['table_name'], 0, 14) == 'civicrm_value_') { - $drops[] = 'DROP TABLE ' . $table['table_name'] . ';'; + if (substr($table, 0, 14) == 'civicrm_value_') { + $drops[] = 'DROP TABLE ' . $table . ';'; + } + elseif (substr($table, 0, 9) == 'civitest_') { + // ignore } else { - $truncates[] = 'TRUNCATE ' . $table['table_name'] . ';'; + $truncates[] = 'TRUNCATE ' . $table . ';'; } } + $dbName = $pdoUtils->dbName; $queries = array( "USE {$dbName};", "SET foreign_key_checks = 0", @@ -45,9 +137,7 @@ class CiviTestDB { $queries = array_merge($queries, $drops); foreach ($queries as $query) { if ($pdoUtils->do_query($query) === FALSE) { - // failed to create test database - echo "failed to create test db."; - exit; + return $this->fatal("Query failed: $query"); } } @@ -60,27 +150,22 @@ class CiviTestDB { $query3 = file_get_contents($sql_file3); $query4 = file_get_contents($sql_file4); if ($pdoUtils->do_query($query2) === FALSE) { - echo "Cannot load civicrm_data.mysql. Aborting."; - exit; + return $this->fatal("Cannot load civicrm_data.mysql. Aborting."); } if ($pdoUtils->do_query($query3) === FALSE) { - echo "Cannot load test_data.mysql. Aborting."; - exit; + return $this->fatal("Cannot load test_data.mysql. Aborting."); } if ($pdoUtils->do_query($query4) === FALSE) { - echo "Cannot load test_data.mysql. Aborting."; - exit; + return $this->fatal("Cannot load test_data.mysql. Aborting."); } // done with all the loading, get transactions back if ($pdoUtils->do_query("set global innodb_flush_log_at_trx_commit = 1;") === FALSE) { - echo "Cannot set global? Huh?"; - exit; + return $this->fatal("Cannot set global? Huh?"); } if ($pdoUtils->do_query("SET foreign_key_checks = 1") === FALSE) { - echo "Cannot get foreign keys back? Huh?"; - exit; + return $this->fatal("Cannot get foreign keys back? Huh?"); } unset($query, $query2, $query3); @@ -100,4 +185,35 @@ class CiviTestDB { return TRUE; } -} \ No newline at end of file + /** + * @param $message + * @return mixed + */ + protected function fatal($message) { + echo "$message\n"; + exit(1); + } + + /** + * @param string $type + * 'BASE TABLE' or 'VIEW'. + * @return array + */ + protected function getCurrentTables($type) { + $pdo = $this->pdoUtils->pdo; + // only consider real tables and not views + $query = sprintf( + "SELECT table_name FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = %s AND TABLE_TYPE = %s", + $pdo->quote($this->pdoUtils->dbName), + $pdo->quote($type) + ); + $tables = $pdo->query($query); + $result = array(); + foreach ($tables as $table) { + $result[] = $table['table_name']; + } + return $result; + } + +} diff --git a/tests/phpunit/CiviTest/CiviTestPdoUtils.php b/tests/phpunit/CiviTest/CiviTestPdoUtils.php index eaccb7a8d5..9dcbcbd48b 100644 --- a/tests/phpunit/CiviTest/CiviTestPdoUtils.php +++ b/tests/phpunit/CiviTest/CiviTestPdoUtils.php @@ -42,6 +42,8 @@ class CiviTestPdoUtils { */ public $pdo; + public $dbName; + /** * Construct an object for this database. */ @@ -52,6 +54,7 @@ class CiviTestPdoUtils { $port = @$dsninfo['port']; $user = $dsninfo['username']; $pass = $dsninfo['password']; + $this->dbName = $dsninfo['database']; try { $this->pdo = new PDO("mysql:host={$host}" . ($port ? ";port=$port" : ""), diff --git a/tests/phpunit/CiviTest/CiviUnitTestCase.php b/tests/phpunit/CiviTest/CiviUnitTestCase.php index 745756927c..365c9c7e9c 100755 --- a/tests/phpunit/CiviTest/CiviUnitTestCase.php +++ b/tests/phpunit/CiviTest/CiviUnitTestCase.php @@ -90,7 +90,7 @@ class CiviUnitTestCase extends PHPUnit_Extensions_Database_TestCase { protected $tempDirs; /** - * @var Utils instance + * @var CiviTestPdoUtils */ public static $utils; @@ -260,7 +260,29 @@ class CiviUnitTestCase extends PHPUnit_Extensions_Database_TestCase { } self::$populateOnce = NULL; - return CiviTestDB::realPopulateDB(self::getDBName(), self::$utils); + $builder = new CiviTestDB(self::$utils); + + static $isSchemaUpdated = FALSE; + if (!$isSchemaUpdated) { + $builder->updateSchema(); + $isSchemaUpdated = TRUE; + } + + $builder->populate(); + + // Rebuild triggers + civicrm_api('system', 'flush', array('version' => 3, 'triggers' => 1)); + + CRM_Core_BAO_ConfigSetting::setEnabledComponents(array( + 'CiviEvent', + 'CiviContribute', + 'CiviMember', + 'CiviMail', + 'CiviReport', + 'CiviPledge', + )); + + return TRUE; } public static function setUpBeforeClass() { -- 2.25.1