CRM-17860 - CiviTester - Split into separate class files
authorTim Otten <totten@civicrm.org>
Wed, 10 Feb 2016 00:33:54 +0000 (16:33 -0800)
committerTim Otten <totten@civicrm.org>
Mon, 15 Feb 2016 22:23:50 +0000 (14:23 -0800)
16 files changed:
CRM/Core/ClassLoader.php
Civi/Test.php [new file with mode: 0644]
Civi/Test/CiviEnvBuilder.php [new file with mode: 0644]
Civi/Test/CiviEnvBuilder/CallbackStep.php [new file with mode: 0644]
Civi/Test/CiviEnvBuilder/ExtensionStep.php [new file with mode: 0644]
Civi/Test/CiviEnvBuilder/SqlFileStep.php [new file with mode: 0644]
Civi/Test/CiviEnvBuilder/SqlStep.php [new file with mode: 0644]
Civi/Test/CiviEnvBuilder/StepInterface.php [new file with mode: 0644]
Civi/Test/Data.php [new file with mode: 0644]
Civi/Test/HeadlessInterface.php
Civi/Test/Schema.php [new file with mode: 0644]
tests/phpunit/Civi/Test/ExampleHookTest.php
tests/phpunit/Civi/Test/ExampleTransactionalTest.php
tests/phpunit/CiviTest/CiviTester.php [deleted file]
tests/phpunit/CiviTest/CiviUnitTestCase.php
tests/phpunit/CiviTest/bootstrap.php

index c32ef990009c0ad34b9e27b7f2dd4af59e461ffe..23117ef69e84d552f4519f2f84cf6ea6cd22d9a4 100644 (file)
@@ -77,7 +77,6 @@ class CRM_Core_ClassLoader {
       'CiviMailUtils',
       'CiviReportTestCase',
       'CiviSeleniumTestCase',
-      'CiviTester',
       'CiviTestSuite',
       'CiviUnitTestCase',
       'Contact',
diff --git a/Civi/Test.php b/Civi/Test.php
new file mode 100644 (file)
index 0000000..e59c1e5
--- /dev/null
@@ -0,0 +1,150 @@
+<?php
+namespace Civi;
+
+use PDO;
+use PDOException;
+
+/**
+ * Class Test
+ *
+ * A facade for managing the test environment.
+ */
+class Test {
+
+  /**
+   * @var array
+   */
+  private static $singletons = array();
+
+  /**
+   * Get the data source used for testing.
+   *
+   * @param string|NULL $part
+   *   One of NULL, 'hostspec', 'port', 'username', 'password', 'database'.
+   * @return string|array|NULL
+   *   If $part is omitted, return full DSN array.
+   *   If $part is a string, return that part of the DSN.
+   */
+  public static function dsn($part = NULL) {
+    if (!isset(self::$singletons['dsn'])) {
+      require_once "DB.php";
+      self::$singletons['dsn'] = \DB::parseDSN(CIVICRM_DSN);
+    }
+
+    if ($part === NULL) {
+      return self::$singletons['dsn'];
+    }
+
+    if (isset(self::$singletons['dsn'][$part])) {
+      return self::$singletons['dsn'][$part];
+    }
+
+    return NULL;
+  }
+
+  /**
+   * Get a connection to the test database.
+   *
+   * @return PDO
+   */
+  public static function pdo() {
+    if (!isset(self::$singletons['pdo'])) {
+      $dsninfo = self::dsn();
+      $host = $dsninfo['hostspec'];
+      $port = @$dsninfo['port'];
+      try {
+        self::$singletons['pdo'] = new PDO("mysql:host={$host}" . ($port ? ";port=$port" : ""),
+          $dsninfo['username'], $dsninfo['password'],
+          array(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE)
+        );
+      }
+      catch (PDOException $e) {
+        echo "Can't connect to MySQL server:" . PHP_EOL . $e->getMessage() . PHP_EOL;
+        exit(1);
+      }
+    }
+    return self::$singletons['pdo'];
+  }
+
+  /**
+   * Get the schema manager.
+   *
+   * @return \Civi\Test\CiviEnvBuilder
+   *
+   * @code
+   * \Civi\Test::headless()->apply();
+   * \Civi\Test::headless()->sqlFile('ex.sql')->apply();
+   * @endCode
+   */
+  public static function headless() {
+    $civiRoot = dirname(__DIR__);
+    $builder = new \Civi\Test\CiviEnvBuilder('CiviTesterSchema');
+    $builder
+      ->callback(function ($ctx) {
+        if (CIVICRM_UF !== 'UnitTests') {
+          throw new \RuntimeException("\\Civi\\Test::headless() requires CIVICRM_UF=UnitTests");
+        }
+        $dbName = \Civi\Test::dsn('database');
+        echo "Installing {$dbName} schema\n";
+        \Civi\Test::schema()->dropAll();
+      }, 'msg-drop')
+      ->sqlFile($civiRoot . "/sql/civicrm.mysql")
+      ->sql("DELETE FROM civicrm_extension")
+      ->callback(function ($ctx) {
+        \Civi\Test::data()->populate();
+      }, 'populate');
+    return $builder;
+  }
+
+  /**
+   * @return \Civi\Test\Schema
+   */
+  public static function schema() {
+    if (!isset(self::$singletons['schema'])) {
+      self::$singletons['schema'] = new \Civi\Test\Schema();
+    }
+    return self::$singletons['schema'];
+  }
+
+
+  /**
+   * @return \Civi\Test\Data
+   */
+  public static function data() {
+    if (!isset(self::$singletons['data'])) {
+      self::$singletons['data'] = new \Civi\Test\Data('CiviTesterData');
+    }
+    return self::$singletons['data'];
+  }
+
+  /**
+   * Prepare and execute a batch of SQL statements.
+   *
+   * @param string $query
+   * @return bool
+   */
+  public static function execute($query) {
+    $pdo = \Civi\Test::pdo();
+
+    $string = preg_replace("/^#[^\n]*$/m", "\n", $query);
+    $string = preg_replace("/^(--[^-]).*/m", "\n", $string);
+
+    $queries = preg_split('/;\s*$/m', $string);
+    foreach ($queries as $query) {
+      $query = trim($query);
+      if (!empty($query)) {
+        $result = $pdo->query($query);
+        if ($pdo->errorCode() == 0) {
+          continue;
+        }
+        else {
+          var_dump($result);
+          var_dump($pdo->errorInfo());
+          // die( "Cannot execute $query: " . $pdo->errorInfo() );
+        }
+      }
+    }
+    return TRUE;
+  }
+
+}
diff --git a/Civi/Test/CiviEnvBuilder.php b/Civi/Test/CiviEnvBuilder.php
new file mode 100644 (file)
index 0000000..ce2d496
--- /dev/null
@@ -0,0 +1,166 @@
+<?php
+namespace Civi\Test;
+
+use Civi\Test\CiviEnvBuilder\CallbackStep;
+use Civi\Test\CiviEnvBuilder\ExtensionStep;
+use Civi\Test\CiviEnvBuilder\SqlFileStep;
+use Civi\Test\CiviEnvBuilder\SqlStep;
+use Civi\Test\CiviEnvBuilder\StepInterface;
+use RuntimeException;
+
+/**
+ * Class CiviEnvBuilder
+ *
+ * Provides a fluent interface for tracking a set of steps.
+ * By computing and storing a signature for the list steps, we can
+ * determine whether to (a) do nothing with the list or (b)
+ * reapply all the steps.
+ */
+class CiviEnvBuilder {
+  protected $name;
+
+  private $steps = array();
+
+  /**
+   * @var string|NULL
+   *   A digest of the values in $steps.
+   */
+  private $targetSignature = NULL;
+
+  public function __construct($name) {
+    $this->name = $name;
+  }
+
+  public function addStep(StepInterface $step) {
+    $this->targetSignature = NULL;
+    $this->steps[] = $step;
+    return $this;
+  }
+
+  public function callback($callback, $signature = NULL) {
+    return $this->addStep(new CallbackStep($callback, $signature));
+  }
+
+  public function sql($sql) {
+    return $this->addStep(new SqlStep($sql));
+  }
+
+  public function sqlFile($file) {
+    return $this->addStep(new SqlFileStep($file));
+  }
+
+  /**
+   * Require an extension (based on its name).
+   *
+   * @param string $name
+   * @return \CiviEnvBuilder
+   */
+  public function ext($name) {
+    return $this->addStep(new ExtensionStep($name));
+  }
+
+  /**
+   * Require an extension (based on its directory).
+   *
+   * @param $dir
+   * @return \CiviEnvBuilder
+   * @throws \CRM_Extension_Exception_ParseException
+   */
+  public function extDir($dir) {
+    while ($dir && dirname($dir) !== $dir && !file_exists("$dir/info.xml")) {
+      $dir = dirname($dir);
+    }
+    if (file_exists("$dir/info.xml")) {
+      $info = \CRM_Extension_Info::loadFromFile("$dir/info.xml");
+      $name = $info->key;
+    }
+    return $this->addStep(new ExtensionStep($name));
+  }
+
+  protected function assertValid() {
+    foreach ($this->steps as $step) {
+      if (!$step->isValid()) {
+        throw new RuntimeException("Found invalid step: " . var_dump($step, 1));
+      }
+    }
+  }
+
+  /**
+   * @return string
+   */
+  protected function getTargetSignature() {
+    if ($this->targetSignature === NULL) {
+      $buf = '';
+      foreach ($this->steps as $step) {
+        $buf .= $step->getSig();
+      }
+      $this->targetSignature = md5($buf);
+    }
+
+    return $this->targetSignature;
+  }
+
+  /**
+   * @return string
+   */
+  protected function getSavedSignature() {
+    $liveSchemaRev = NULL;
+    $pdo = \Civi\Test::pdo();
+    $pdoStmt = $pdo->query(sprintf(
+      "SELECT rev FROM %s.civitest_revs WHERE name = %s",
+      \Civi\Test::dsn('database'),
+      $pdo->quote($this->name)
+    ));
+    foreach ($pdoStmt as $row) {
+      $liveSchemaRev = $row['rev'];
+    }
+    return $liveSchemaRev;
+  }
+
+  /**
+   * @param $newSignature
+   */
+  protected function setSavedSignature($newSignature) {
+    $pdo = \Civi\Test::pdo();
+    $query = sprintf(
+      'INSERT INTO %s.civitest_revs (name,rev) VALUES (%s,%s) '
+      . 'ON DUPLICATE KEY UPDATE rev = %s;',
+      \Civi\Test::dsn('database'),
+      $pdo->quote($this->name),
+      $pdo->quote($newSignature),
+      $pdo->quote($newSignature)
+    );
+
+    if (\Civi\Test::execute($query) === FALSE) {
+      throw new RuntimeException("Failed to flag schema version: $query");
+    }
+  }
+
+  /**
+   * Determine if the schema is correct. If necessary, destroy and recreate.
+   *
+   * @param bool $force
+   * @return $this
+   */
+  public function apply($force = FALSE) {
+    $dbName = \Civi\Test::dsn('database');
+    $query = "USE {$dbName};"
+      . "CREATE TABLE IF NOT EXISTS civitest_revs (name VARCHAR(64) PRIMARY KEY, rev VARCHAR(64));";
+
+    if (\Civi\Test::execute($query) === FALSE) {
+      throw new \RuntimeException("Failed to flag schema version: $query");
+    }
+
+    $this->assertValid();
+
+    if (!$force && $this->getSavedSignature() === $this->getTargetSignature()) {
+      return $this;
+    }
+    foreach ($this->steps as $step) {
+      $step->run($this);
+    }
+    $this->setSavedSignature($this->getTargetSignature());
+    return $this;
+  }
+
+}
diff --git a/Civi/Test/CiviEnvBuilder/CallbackStep.php b/Civi/Test/CiviEnvBuilder/CallbackStep.php
new file mode 100644 (file)
index 0000000..6d0c9f0
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+namespace Civi\Test\CiviEnvBuilder;
+class CallbackStep implements StepInterface {
+  private $callback;
+  private $sig;
+
+  /**
+   * CallbackStep constructor.
+   * @param $callback
+   * @param $sig
+   */
+  public function __construct($callback, $sig = NULL) {
+    $this->callback = $callback;
+    $this->sig = $sig === NULL ? md5(var_export($callback, 1)) : $sig;
+  }
+
+  public function getSig() {
+    return $this->sig;
+  }
+
+  public function isValid() {
+    return is_callable($this->callback);
+  }
+
+  public function run($ctx) {
+    call_user_func($this->callback, $ctx);
+  }
+
+}
diff --git a/Civi/Test/CiviEnvBuilder/ExtensionStep.php b/Civi/Test/CiviEnvBuilder/ExtensionStep.php
new file mode 100644 (file)
index 0000000..afc4d6c
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+namespace Civi\Test\CiviEnvBuilder;
+class ExtensionStep implements StepInterface {
+  private $name;
+
+  /**
+   * ExtensionStep constructor.
+   * @param $name
+   */
+  public function __construct($name) {
+    $this->name = $name;
+  }
+
+  public function getSig() {
+    return 'ext:' . $this->name;
+  }
+
+  public function isValid() {
+    return is_string($this->name);
+  }
+
+  public function run($ctx) {
+    \CRM_Extension_System::singleton()->getManager()->install(array(
+      $this->name,
+    ));
+  }
+
+}
diff --git a/Civi/Test/CiviEnvBuilder/SqlFileStep.php b/Civi/Test/CiviEnvBuilder/SqlFileStep.php
new file mode 100644 (file)
index 0000000..c901931
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+namespace Civi\Test\CiviEnvBuilder;
+
+class SqlFileStep implements StepInterface {
+  private $file;
+
+  /**
+   * SqlFileStep constructor.
+   * @param string $file
+   */
+  public function __construct($file) {
+    $this->file = $file;
+  }
+
+
+  public function getSig() {
+    return implode(' ', array(
+      $this->file,
+      filemtime($this->file),
+      filectime($this->file),
+    ));
+  }
+
+  public function isValid() {
+    return is_file($this->file) && is_readable($this->file);
+  }
+
+  public function run($ctx) {
+    /** @var $ctx \CiviEnvBuilder */
+    if (\Civi\Test::execute(@file_get_contents($this->file)) === FALSE) {
+      throw new \RuntimeException("Cannot load {$this->file}. Aborting.");
+    }
+  }
+
+}
diff --git a/Civi/Test/CiviEnvBuilder/SqlStep.php b/Civi/Test/CiviEnvBuilder/SqlStep.php
new file mode 100644 (file)
index 0000000..7a2736b
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+namespace Civi\Test\CiviEnvBuilder;
+class SqlStep implements StepInterface {
+  private $sql;
+
+  /**
+   * SqlFileStep constructor.
+   * @param string $sql
+   */
+  public function __construct($sql) {
+    $this->sql = $sql;
+  }
+
+
+  public function getSig() {
+    return md5($this->sql);
+  }
+
+  public function isValid() {
+    return TRUE;
+  }
+
+  public function run($ctx) {
+    /** @var $ctx \CiviEnvBuilder */
+    if (\Civi\Test::execute($this->sql) === FALSE) {
+      throw new \RuntimeException("Cannot execute: {$this->sql}");
+    }
+  }
+
+}
diff --git a/Civi/Test/CiviEnvBuilder/StepInterface.php b/Civi/Test/CiviEnvBuilder/StepInterface.php
new file mode 100644 (file)
index 0000000..3d6dc95
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+namespace Civi\Test\CiviEnvBuilder;
+
+interface StepInterface {
+  public function getSig();
+
+  public function isValid();
+
+  public function run($ctx);
+
+}
diff --git a/Civi/Test/Data.php b/Civi/Test/Data.php
new file mode 100644 (file)
index 0000000..d1e62e5
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+namespace Civi\Test;
+
+use RuntimeException;
+
+/**
+ * Class Data
+ */
+class Data {
+
+  /**
+   * @return bool
+   */
+  public function populate() {
+    \Civi\Test::schema()->truncateAll();
+
+    \Civi\Test::schema()->setStrict(FALSE);
+    $sqlDir = dirname(dirname(__DIR__)) . "/sql";
+
+    $query2 = file_get_contents("$sqlDir/civicrm_data.mysql");
+    $query3 = file_get_contents("$sqlDir/test_data.mysql");
+    $query4 = file_get_contents("$sqlDir/test_data_second_domain.mysql");
+    if (\Civi\Test::execute($query2) === FALSE) {
+      throw new RuntimeException("Cannot load civicrm_data.mysql. Aborting.");
+    }
+    if (\Civi\Test::execute($query3) === FALSE) {
+      throw new RuntimeException("Cannot load test_data.mysql. Aborting.");
+    }
+    if (\Civi\Test::execute($query4) === FALSE) {
+      throw new RuntimeException("Cannot load test_data.mysql. Aborting.");
+    }
+
+    unset($query, $query2, $query3);
+
+    \Civi\Test::schema()->setStrict(TRUE);
+
+    // 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;
+  }
+
+}
index fb1cbf4f541645bc5b684b105b4053717a296661..d9e2c6332e5212cc5555f7efff9284538596abc0 100644 (file)
@@ -27,11 +27,11 @@ interface HeadlessInterface {
    * It should perform any necessary steps required for putting the database
    * in a consistent baseline -- such as loading schema and extensions.
    *
-   * The utility class `CiviTester` provides a number of helper functions
+   * The utility class `\Civi\Test` provides a number of helper functions
    * for managing this setup, and it includes optimizations to avoid redundant
    * setup work.
    *
-   * @see CiviTester
+   * @see \Civi\Test
    */
   public function setUpHeadless();
 
diff --git a/Civi/Test/Schema.php b/Civi/Test/Schema.php
new file mode 100644 (file)
index 0000000..706f848
--- /dev/null
@@ -0,0 +1,128 @@
+<?php
+namespace Civi\Test;
+
+use RuntimeException;
+
+/**
+ * Class Schema
+ *
+ * Manage the entire database. This is useful for destroying or loading the schema.
+ */
+class Schema {
+
+  /**
+   * @param string $type
+   *   'BASE TABLE' or 'VIEW'.
+   * @return array
+   */
+  public function getTables($type) {
+    $pdo = \Civi\Test::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(\Civi\Test::dsn('database')),
+      $pdo->quote($type)
+    );
+    $tables = $pdo->query($query);
+    $result = array();
+    foreach ($tables as $table) {
+      $result[] = $table['table_name'];
+    }
+    return $result;
+  }
+
+  public function setStrict($checks) {
+    $dbName = \Civi\Test::dsn('database');
+    if ($checks) {
+      $queries = array(
+        "USE {$dbName};",
+        "SET global innodb_flush_log_at_trx_commit = 1;",
+        "SET SQL_MODE='STRICT_ALL_TABLES';",
+        "SET foreign_key_checks = 1;",
+      );
+    }
+    else {
+      $queries = array(
+        "USE {$dbName};",
+        "SET foreign_key_checks = 0",
+        "SET SQL_MODE='STRICT_ALL_TABLES';",
+        "SET global innodb_flush_log_at_trx_commit = 2;",
+      );
+    }
+    foreach ($queries as $query) {
+      if (\Civi\Test::execute($query) === FALSE) {
+        throw new RuntimeException("Query failed: $query");
+      }
+    }
+    return $this;
+  }
+
+  public function dropAll() {
+    $queries = array();
+    foreach ($this->getTables('VIEW') as $table) {
+      if (preg_match('/^(civicrm_|log_)/', $table)) {
+        $queries[] = "DROP VIEW $table";
+      }
+    }
+
+    foreach ($this->getTables('BASE TABLE') as $table) {
+      if (preg_match('/^(civicrm_|log_)/', $table)) {
+        $queries[] = "DROP TABLE $table";
+      }
+    }
+
+    $this->setStrict(FALSE);
+    foreach ($queries as $query) {
+      if (\Civi\Test::execute($query) === FALSE) {
+        throw new RuntimeException("dropSchema: Query failed: $query");
+      }
+    }
+    $this->setStrict(TRUE);
+
+    return $this;
+  }
+
+  /**
+   * @return array
+   */
+  public function truncateAll() {
+    $tables = \Civi\Test::schema()->getTables('BASE TABLE');
+
+    $truncates = array();
+    $drops = array();
+    foreach ($tables as $table) {
+      // skip log tables
+      if (substr($table, 0, 4) == 'log_') {
+        continue;
+      }
+
+      // don't change list of installed extensions
+      if ($table == 'civicrm_extension') {
+        continue;
+      }
+
+      if (substr($table, 0, 14) == 'civicrm_value_') {
+        $drops[] = 'DROP TABLE ' . $table . ';';
+      }
+      elseif (substr($table, 0, 9) == 'civitest_') {
+        // ignore
+      }
+      else {
+        $truncates[] = 'TRUNCATE ' . $table . ';';
+      }
+    }
+
+    \Civi\Test::schema()->setStrict(FALSE);
+    $queries = array_merge($truncates, $drops);
+    foreach ($queries as $query) {
+      if (\Civi\Test::execute($query) === FALSE) {
+        throw new RuntimeException("Query failed: $query");
+      }
+    }
+    \Civi\Test::schema()->setStrict(TRUE);
+
+    return $this;
+  }
+
+}
index 0d17eed68d48b80cfbe86c38f63101206fcc6059..d7e4ebbb9b571465e1879a50803870226b3b5284 100644 (file)
@@ -16,7 +16,7 @@ class ExampleHookTest extends \PHPUnit_Framework_TestCase implements HeadlessInt
   protected $contact;
 
   public function setUpHeadless() {
-    return \CiviTester::headless()->apply();
+    return \Civi\Test::headless()->apply();
   }
 
   protected function setUp() {
index dba12993813de341b845353c1404fcc7debbdb02..7c54db29d10e2390d6c96839c30ee9b05cd7bcbb 100644 (file)
@@ -18,7 +18,7 @@ class ExampleTransactionalTest extends \PHPUnit_Framework_TestCase implements He
   static $contactIds = array();
 
   public function setUpHeadless() {
-    return \CiviTester::headless()->apply();
+    return \Civi\Test::headless()->apply();
   }
 
   protected function setUp() {
diff --git a/tests/phpunit/CiviTest/CiviTester.php b/tests/phpunit/CiviTest/CiviTester.php
deleted file mode 100644 (file)
index 89e68ac..0000000
+++ /dev/null
@@ -1,599 +0,0 @@
-<?php
-
-class CiviTester {
-
-  /**
-   * @var array
-   */
-  private static $singletons = array();
-
-  /**
-   * Get the data source used for testing.
-   *
-   * @param string|NULL $part
-   *   One of NULL, 'hostspec', 'port', 'username', 'password', 'database'.
-   * @return string|array|NULL
-   *   If $part is omitted, return full DSN array.
-   *   If $part is a string, return that part of the DSN.
-   */
-  public static function dsn($part = NULL) {
-    if (!isset(self::$singletons['dsn'])) {
-      require_once "DB.php";
-      self::$singletons['dsn'] = DB::parseDSN(CIVICRM_DSN);
-    }
-
-    if ($part === NULL) {
-      return self::$singletons['dsn'];
-    }
-
-    if (isset(self::$singletons['dsn'][$part])) {
-      return self::$singletons['dsn'][$part];
-    }
-
-    return NULL;
-  }
-
-  /**
-   * Get a connection to the test database.
-   *
-   * @return PDO
-   */
-  public static function pdo() {
-    if (!isset(self::$singletons['pdo'])) {
-      $dsninfo = self::dsn();
-      $host = $dsninfo['hostspec'];
-      $port = @$dsninfo['port'];
-      try {
-        self::$singletons['pdo'] = new PDO("mysql:host={$host}" . ($port ? ";port=$port" : ""),
-          $dsninfo['username'], $dsninfo['password'],
-          array(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE)
-        );
-      }
-      catch (PDOException$e) {
-        echo "Can't connect to MySQL server:" . PHP_EOL . $e->getMessage() . PHP_EOL;
-        exit(1);
-      }
-    }
-    return self::$singletons['pdo'];
-  }
-
-  /**
-   * Get the schema manager.
-   *
-   * @return \CiviTesterBuilder
-   *
-   * @code
-   * CiviTester::headless()->apply();
-   * CiviTester::headless()->sqlFile('ex.sql')->apply();
-   * @endCode
-   */
-  public static function headless() {
-    $civiRoot = dirname(dirname(dirname(dirname(__FILE__))));
-    $builder = new CiviTesterBuilder('CiviTesterSchema');
-    $builder
-      ->callback(function ($ctx) {
-        if (CIVICRM_UF !== 'UnitTests') {
-          throw new \RuntimeException("CiviTester::headless() requires CIVICRM_UF=UnitTests");
-        }
-        $dbName = CiviTester::dsn('database');
-        echo "Installing {$dbName} schema\n";
-        CiviTester::schema()->dropAll();
-      }, 'msg-drop')
-      ->sqlFile($civiRoot . "/sql/civicrm.mysql")
-      ->sql("DELETE FROM civicrm_extension")
-      ->callback(function ($ctx) {
-        CiviTester::data()->populate();
-      }, 'populate');
-    return $builder;
-  }
-
-  /**
-   * @return \CiviTesterSchema
-   */
-  public static function schema() {
-    if (!isset(self::$singletons['schema'])) {
-      self::$singletons['schema'] = new CiviTesterSchema();
-    }
-    return self::$singletons['schema'];
-  }
-
-
-  /**
-   * @return \CiviTesterData
-   */
-  public static function data() {
-    if (!isset(self::$singletons['data'])) {
-      self::$singletons['data'] = new CiviTesterData('CiviTesterData');
-    }
-    return self::$singletons['data'];
-  }
-
-  /**
-   * Prepare and execute a batch of SQL statements.
-   *
-   * @param string $query
-   * @return bool
-   */
-  public static function execute($query) {
-    $pdo = CiviTester::pdo();
-
-    $string = preg_replace("/^#[^\n]*$/m", "\n", $query);
-    $string = preg_replace("/^(--[^-]).*/m", "\n", $string);
-
-    $queries = preg_split('/;\s*$/m', $string);
-    foreach ($queries as $query) {
-      $query = trim($query);
-      if (!empty($query)) {
-        $result = $pdo->query($query);
-        if ($pdo->errorCode() == 0) {
-          continue;
-        }
-        else {
-          var_dump($result);
-          var_dump($pdo->errorInfo());
-          // die( "Cannot execute $query: " . $pdo->errorInfo() );
-        }
-      }
-    }
-    return TRUE;
-  }
-
-}
-
-/**
- * Class CiviTesterSchema
- *
- * Manage the entire database. This is useful for destroying or loading the schema.
- */
-class CiviTesterSchema {
-
-  /**
-   * @param string $type
-   *   'BASE TABLE' or 'VIEW'.
-   * @return array
-   */
-  public function getTables($type) {
-    $pdo = CiviTester::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(CiviTester::dsn('database')),
-      $pdo->quote($type)
-    );
-    $tables = $pdo->query($query);
-    $result = array();
-    foreach ($tables as $table) {
-      $result[] = $table['table_name'];
-    }
-    return $result;
-  }
-
-  public function setStrict($checks) {
-    $dbName = CiviTester::dsn('database');
-    if ($checks) {
-      $queries = array(
-        "USE {$dbName};",
-        "SET global innodb_flush_log_at_trx_commit = 1;",
-        "SET SQL_MODE='STRICT_ALL_TABLES';",
-        "SET foreign_key_checks = 1;",
-      );
-    }
-    else {
-      $queries = array(
-        "USE {$dbName};",
-        "SET foreign_key_checks = 0",
-        "SET SQL_MODE='STRICT_ALL_TABLES';",
-        "SET global innodb_flush_log_at_trx_commit = 2;",
-      );
-    }
-    foreach ($queries as $query) {
-      if (CiviTester::execute($query) === FALSE) {
-        throw new RuntimeException("Query failed: $query");
-      }
-    }
-    return $this;
-  }
-
-  public function dropAll() {
-    $queries = array();
-    foreach ($this->getTables('VIEW') as $table) {
-      if (preg_match('/^(civicrm_|log_)/', $table)) {
-        $queries[] = "DROP VIEW $table";
-      }
-    }
-
-    foreach ($this->getTables('BASE TABLE') as $table) {
-      if (preg_match('/^(civicrm_|log_)/', $table)) {
-        $queries[] = "DROP TABLE $table";
-      }
-    }
-
-    $this->setStrict(FALSE);
-    foreach ($queries as $query) {
-      if (CiviTester::execute($query) === FALSE) {
-        throw new RuntimeException("dropSchema: Query failed: $query");
-      }
-    }
-    $this->setStrict(TRUE);
-
-    return $this;
-  }
-
-  /**
-   * @return array
-   */
-  public function truncateAll() {
-    $tables = CiviTester::schema()->getTables('BASE TABLE');
-
-    $truncates = array();
-    $drops = array();
-    foreach ($tables as $table) {
-      // skip log tables
-      if (substr($table, 0, 4) == 'log_') {
-        continue;
-      }
-
-      // don't change list of installed extensions
-      if ($table == 'civicrm_extension') {
-        continue;
-      }
-
-      if (substr($table, 0, 14) == 'civicrm_value_') {
-        $drops[] = 'DROP TABLE ' . $table . ';';
-      }
-      elseif (substr($table, 0, 9) == 'civitest_') {
-        // ignore
-      }
-      else {
-        $truncates[] = 'TRUNCATE ' . $table . ';';
-      }
-    }
-
-    CiviTester::schema()->setStrict(FALSE);
-    $queries = array_merge($truncates, $drops);
-    foreach ($queries as $query) {
-      if (CiviTester::execute($query) === FALSE) {
-        throw new RuntimeException("Query failed: $query");
-      }
-    }
-    CiviTester::schema()->setStrict(TRUE);
-
-    return $this;
-  }
-
-}
-
-/**
- * Class CiviTesterData
- */
-class CiviTesterData {
-
-  /**
-   * @return bool
-   */
-  public function populate() {
-    CiviTester::schema()->truncateAll();
-
-    CiviTester::schema()->setStrict(FALSE);
-    //  initialize test database
-    $sql_file2 = dirname(dirname(dirname(dirname(__FILE__)))) . "/sql/civicrm_data.mysql";
-    $sql_file3 = dirname(dirname(dirname(dirname(__FILE__)))) . "/sql/test_data.mysql";
-    $sql_file4 = dirname(dirname(dirname(dirname(__FILE__)))) . "/sql/test_data_second_domain.mysql";
-
-    $query2 = file_get_contents($sql_file2);
-    $query3 = file_get_contents($sql_file3);
-    $query4 = file_get_contents($sql_file4);
-    if (CiviTester::execute($query2) === FALSE) {
-      throw new RuntimeException("Cannot load civicrm_data.mysql. Aborting.");
-    }
-    if (CiviTester::execute($query3) === FALSE) {
-      throw new RuntimeException("Cannot load test_data.mysql. Aborting.");
-    }
-    if (CiviTester::execute($query4) === FALSE) {
-      throw new RuntimeException("Cannot load test_data.mysql. Aborting.");
-    }
-
-    unset($query, $query2, $query3);
-
-    CiviTester::schema()->setStrict(TRUE);
-
-    // 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;
-  }
-
-}
-
-/**
- * Class CiviTesterBuilder
- *
- * Provides a fluent interface for tracking a set of steps.
- * By computing and storing a signature for the list steps, we can
- * determine whether to (a) do nothing with the list or (b)
- * reapply all the steps.
- */
-class CiviTesterBuilder {
-  protected $name;
-
-  private $steps = array();
-
-  /**
-   * @var string|NULL
-   *   A digest of the values in $steps.
-   */
-  private $targetSignature = NULL;
-
-  public function __construct($name) {
-    $this->name = $name;
-  }
-
-  public function addStep(CiviTesterStep $step) {
-    $this->targetSignature = NULL;
-    $this->steps[] = $step;
-    return $this;
-  }
-
-  public function callback($callback, $signature = NULL) {
-    return $this->addStep(new CiviTesterCallbackStep($callback, $signature));
-  }
-
-  public function sql($sql) {
-    return $this->addStep(new CiviTesterSqlStep($sql));
-  }
-
-  public function sqlFile($file) {
-    return $this->addStep(new CiviTesterSqlFileStep($file));
-  }
-
-  /**
-   * Require an extension (based on its name).
-   *
-   * @param string $name
-   * @return \CiviTesterBuilder
-   */
-  public function ext($name) {
-    return $this->addStep(new CiviTesterExtensionStep($name));
-  }
-
-  /**
-   * Require an extension (based on its directory).
-   *
-   * @param $dir
-   * @return \CiviTesterBuilder
-   * @throws \CRM_Extension_Exception_ParseException
-   */
-  public function extDir($dir) {
-    while ($dir && dirname($dir) !== $dir && !file_exists("$dir/info.xml")) {
-      $dir = dirname($dir);
-    }
-    if (file_exists("$dir/info.xml")) {
-      $info = CRM_Extension_Info::loadFromFile("$dir/info.xml");
-      $name = $info->key;
-    }
-    return $this->addStep(new CiviTesterExtensionStep($name));
-  }
-
-  protected function assertValid() {
-    foreach ($this->steps as $step) {
-      if (!$step->isValid()) {
-        throw new RuntimeException("Found invalid step: " . var_dump($step, 1));
-      }
-    }
-  }
-
-  /**
-   * @return string
-   */
-  protected function getTargetSignature() {
-    if ($this->targetSignature === NULL) {
-      $buf = '';
-      foreach ($this->steps as $step) {
-        $buf .= $step->getSig();
-      }
-      $this->targetSignature = md5($buf);
-    }
-
-    return $this->targetSignature;
-  }
-
-  /**
-   * @return string
-   */
-  protected function getSavedSignature() {
-    $liveSchemaRev = NULL;
-    $pdo = CiviTester::pdo();
-    $pdoStmt = $pdo->query(sprintf(
-      "SELECT rev FROM %s.civitest_revs WHERE name = %s",
-      CiviTester::dsn('database'),
-      $pdo->quote($this->name)
-    ));
-    foreach ($pdoStmt as $row) {
-      $liveSchemaRev = $row['rev'];
-    }
-    return $liveSchemaRev;
-  }
-
-  /**
-   * @param $newSignature
-   */
-  protected function setSavedSignature($newSignature) {
-    $pdo = CiviTester::pdo();
-    $query = sprintf(
-      'INSERT INTO %s.civitest_revs (name,rev) VALUES (%s,%s) '
-      . 'ON DUPLICATE KEY UPDATE rev = %s;',
-      CiviTester::dsn('database'),
-      $pdo->quote($this->name),
-      $pdo->quote($newSignature),
-      $pdo->quote($newSignature)
-    );
-
-    if (CiviTester::execute($query) === FALSE) {
-      throw new RuntimeException("Failed to flag schema version: $query");
-    }
-  }
-
-  /**
-   * Determine if the schema is correct. If necessary, destroy and recreate.
-   *
-   * @param bool $force
-   * @return $this
-   */
-  public function apply($force = FALSE) {
-    $dbName = CiviTester::dsn('database');
-    $query = "USE {$dbName};"
-      . "CREATE TABLE IF NOT EXISTS civitest_revs (name VARCHAR(64) PRIMARY KEY, rev VARCHAR(64));";
-
-    if (CiviTester::execute($query) === FALSE) {
-      throw new RuntimeException("Failed to flag schema version: $query");
-    }
-
-    $this->assertValid();
-
-    if (!$force && $this->getSavedSignature() === $this->getTargetSignature()) {
-      return $this;
-    }
-    foreach ($this->steps as $step) {
-      $step->run($this);
-    }
-    $this->setSavedSignature($this->getTargetSignature());
-    return $this;
-  }
-
-}
-
-interface CiviTesterStep {
-  public function getSig();
-
-  public function isValid();
-
-  public function run($ctx);
-
-}
-
-class CiviTesterSqlFileStep implements CiviTesterStep {
-  private $file;
-
-  /**
-   * CiviTesterSqlFileStep constructor.
-   * @param $file
-   */
-  public function __construct($file) {
-    $this->file = $file;
-  }
-
-
-  public function getSig() {
-    return implode(' ', array(
-      $this->file,
-      filemtime($this->file),
-      filectime($this->file),
-    ));
-  }
-
-  public function isValid() {
-    return is_file($this->file) && is_readable($this->file);
-  }
-
-  public function run($ctx) {
-    /** @var $ctx CiviTesterBuilder */
-    if (CiviTester::execute(@file_get_contents($this->file)) === FALSE) {
-      throw new RuntimeException("Cannot load {$this->file}. Aborting.");
-    }
-  }
-
-}
-
-class CiviTesterSqlStep implements CiviTesterStep {
-  private $sql;
-
-  /**
-   * CiviTesterSqlFileStep constructor.
-   * @param $sql
-   */
-  public function __construct($sql) {
-    $this->sql = $sql;
-  }
-
-
-  public function getSig() {
-    return md5($this->sql);
-  }
-
-  public function isValid() {
-    return TRUE;
-  }
-
-  public function run($ctx) {
-    /** @var $ctx CiviTesterBuilder */
-    if (CiviTester::execute($this->sql) === FALSE) {
-      throw new RuntimeException("Cannot execute: {$this->sql}");
-    }
-  }
-
-}
-
-class CiviTesterCallbackStep implements CiviTesterStep {
-  private $callback;
-  private $sig;
-
-  /**
-   * CiviTesterCallbackStep constructor.
-   * @param $callback
-   * @param $sig
-   */
-  public function __construct($callback, $sig = NULL) {
-    $this->callback = $callback;
-    $this->sig = $sig === NULL ? md5(var_export($callback, 1)) : $sig;
-  }
-
-  public function getSig() {
-    return $this->sig;
-  }
-
-  public function isValid() {
-    return is_callable($this->callback);
-  }
-
-  public function run($ctx) {
-    call_user_func($this->callback, $ctx);
-  }
-
-}
-
-class CiviTesterExtensionStep implements CiviTesterStep {
-  private $name;
-
-  /**
-   * CiviTesterExtensionStep constructor.
-   * @param $name
-   */
-  public function __construct($name) {
-    $this->name = $name;
-  }
-
-  public function getSig() {
-    return 'ext:' . $this->name;
-  }
-
-  public function isValid() {
-    return is_string($this->name);
-  }
-
-  public function run($ctx) {
-    CRM_Extension_System::singleton()->getManager()->install(array(
-      $this->name,
-    ));
-  }
-
-}
index 630ff41c3b7ba2822b9b34aa5165989432ee3a61..c92568aeb02d973ef74e2d373c846947c44e706b 100644 (file)
@@ -221,7 +221,7 @@ class CiviUnitTestCase extends PHPUnit_Extensions_Database_TestCase {
       self::$dbInit = TRUE;
     }
 
-    return $this->createDefaultDBConnection(CiviTester::pdo(), $dbName);
+    return $this->createDefaultDBConnection(Civi\Test::pdo(), $dbName);
   }
 
   /**
@@ -253,7 +253,7 @@ class CiviUnitTestCase extends PHPUnit_Extensions_Database_TestCase {
     }
     self::$populateOnce = NULL;
 
-    CiviTester::data()->populate();
+    Civi\Test::data()->populate();
 
     return TRUE;
   }
index e351917383430beb499265da086b940467ef9983..d50ff31fadaef4550c8714ec856223e24013d9a5 100644 (file)
@@ -19,7 +19,7 @@ define('CIVICRM_TEST', 1);
 eval(cv('php:boot --level=settings', 'phpcode'));
 
 if (CIVICRM_UF === 'UnitTests') {
-  CiviTester::headless()->apply();
+  Civi\Test::headless()->apply();
 }
 
 // ------------------------------------------------------------------------------