CRM-17860 - CiviUnitTestCase - Maintain headless schema automatically
authorTim Otten <totten@civicrm.org>
Fri, 29 Jan 2016 09:13:09 +0000 (01:13 -0800)
committerTim Otten <totten@civicrm.org>
Tue, 2 Feb 2016 05:31:19 +0000 (22:31 -0700)
tests/phpunit/CiviTest/CiviTestDB.php
tests/phpunit/CiviTest/CiviTestPdoUtils.php
tests/phpunit/CiviTest/CiviUnitTestCase.php

index 2bcf83b76f8c0d5c142eea9a2c287d0381f50ce0..5b688c9a7a88f567eb6c8e14ce500e5de4e99b2d 100644 (file)
 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;
+  }
+
+}
index eaccb7a8d5e8a0bcb29b4fd2f415d667c96e1ec3..9dcbcbd48b120868c6dfe2dd5a06fe55e3351005 100644 (file)
@@ -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" : ""),
index 745756927c3a443e3b851bb39a2bbdbe7e07e511..365c9c7e9c64b83e7f1c37fd79496157ffab6e51 100755 (executable)
@@ -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() {