]> git.mxchange.org Git - friendica.git/commitdiff
Introduce test optimization
authorPhilipp Holzer <admin+github@philipp.info>
Sat, 27 Jul 2019 12:37:24 +0000 (14:37 +0200)
committerPhilipp Holzer <admin+github@philipp.info>
Sat, 27 Jul 2019 21:54:13 +0000 (23:54 +0200)
- Add static connection for whole tests
- Introduce ExtendedPDO class to enable nested transactions
- Add rollback logic for tests to ensure reliability and increase speed

src/Database/Database.php
tests/DatabaseTest.php
tests/Util/Database/ExtendedPDO.php [new file with mode: 0644]
tests/Util/Database/StaticDatabase.php [new file with mode: 0644]
tests/include/ApiTest.php

index 1533b7674abc98819c31b382eb77a131665285d2..501d65fe76e660491cc14a872517d909dafe21fd 100644 (file)
@@ -21,29 +21,29 @@ use Psr\Log\LoggerInterface;
  */
 class Database
 {
-       private $connected = false;
+       protected $connected = false;
 
        /**
         * @var ConfigCache
         */
-       private $configCache;
+       protected $configCache;
        /**
         * @var Profiler
         */
-       private $profiler;
+       protected $profiler;
        /**
         * @var LoggerInterface
         */
-       private $logger;
-       private $server_info    = '';
+       protected $logger;
+       protected $server_info    = '';
        /** @var PDO|mysqli */
-       private $connection;
-       private $driver;
+       protected $connection;
+       protected $driver;
        private $error          = false;
        private $errorno        = 0;
        private $affected_rows  = 0;
-       private $in_transaction = false;
-       private $in_retrial     = false;
+       protected $in_transaction = false;
+       protected $in_retrial     = false;
        private $relation       = [];
 
        public function __construct(ConfigCache $configCache, Profiler $profiler, LoggerInterface $logger, array $server = [])
@@ -1070,7 +1070,7 @@ class Database
                return true;
        }
 
-       private function performCommit()
+       protected function performCommit()
        {
                switch ($this->driver) {
                        case 'pdo':
index 98f79e351b5b9f008a24c86d2b357a4a7f8d7960..3c3f835eb6a26c95b6351d94b2631175a683027a 100644 (file)
@@ -5,7 +5,7 @@
 
 namespace Friendica\Test;
 
-use PDO;
+use Friendica\Test\Util\Database\StaticDatabase;
 use PHPUnit\DbUnit\DataSet\YamlDataSet;
 use PHPUnit\DbUnit\TestCaseTrait;
 use PHPUnit_Extensions_Database_DB_IDatabaseConnection;
@@ -17,12 +17,6 @@ abstract class DatabaseTest extends MockedTest
 {
        use TestCaseTrait;
 
-       // only instantiate pdo once for test clean-up/fixture load
-       static private $pdo = null;
-
-       // only instantiate PHPUnit_Extensions_Database_DB_IDatabaseConnection once per test
-       private $conn = null;
-
        /**
         * Get database connection.
         *
@@ -36,38 +30,7 @@ abstract class DatabaseTest extends MockedTest
         */
        protected function getConnection()
        {
-               $server = $_SERVER;
-
-               if ($this->conn === null) {
-                       if (self::$pdo == null) {
-
-                               if (!empty($server['MYSQL_HOST'])
-                                   && !empty($server['MYSQL_USERNAME'] || !empty($server['MYSQL_USER']))
-                                   && $server['MYSQL_PASSWORD'] !== false
-                                   && !empty($server['MYSQL_DATABASE'])) {
-
-                                       $connect = "mysql:host=" . $server['MYSQL_HOST'] . ";dbname=" . $server['MYSQL_DATABASE'];
-
-                                       if (!empty($server['MYSQL_PORT'])) {
-                                               $connect .= ";port=" . $server['MYSQL_PORT'];
-                                       }
-
-                                       if (!empty($server['MYSQL_USERNAME'])) {
-                                               $db_user = $server['MYSQL_USERNAME'];
-                                       } else {
-                                               $db_user = $server['MYSQL_USER'];
-                                       }
-
-                                       $db_pass = (string)$server['MYSQL_PASSWORD'];
-
-                                       self::$pdo = @new PDO($connect, $db_user, $db_pass);
-                                       self::$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
-                               }
-                       }
-                       $this->conn = $this->createDefaultDBConnection(self::$pdo, getenv('MYSQL_DATABASE'));
-               }
-
-               return $this->conn;
+               return $this->createDefaultDBConnection(StaticDatabase::getGlobConnection(), getenv('MYSQL_DATABASE'));
        }
 
        /**
diff --git a/tests/Util/Database/ExtendedPDO.php b/tests/Util/Database/ExtendedPDO.php
new file mode 100644 (file)
index 0000000..6bb0251
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+
+namespace Friendica\Test\Util\Database;
+
+use PDO;
+use PDOException;
+
+/**
+ * This class extends native PDO one but allow nested transactions
+ * by using the SQL statements `SAVEPOINT', 'RELEASE SAVEPOINT' AND 'ROLLBACK SAVEPOINT'
+ */
+class ExtendedPDO extends PDO
+{
+       /**
+        * @var array Database drivers that support SAVEPOINT * statements.
+        */
+       protected static $_supportedDrivers = array("pgsql", "mysql");
+
+       /**
+        * @var int the current transaction depth
+        */
+       protected $_transactionDepth = 0;
+
+       /**
+        * @return int
+        */
+       public function getTransactionDepth()
+       {
+               return $this->_transactionDepth;
+       }
+
+       /**
+        * Test if database driver support savepoints
+        *
+        * @return bool
+        */
+       protected function hasSavepoint()
+       {
+               return in_array($this->getAttribute(PDO::ATTR_DRIVER_NAME),
+                       self::$_supportedDrivers);
+       }
+
+
+       /**
+        * Start transaction
+        *
+        * @return bool|void
+        */
+       public function beginTransaction()
+       {
+               if($this->_transactionDepth == 0 || !$this->hasSavepoint()) {
+                       parent::beginTransaction();
+               } else {
+                       $this->exec("SAVEPOINT LEVEL{$this->_transactionDepth}");
+               }
+
+               $this->_transactionDepth++;
+       }
+
+       /**
+        * Commit current transaction
+        *
+        * @return bool|void
+        */
+       public function commit()
+       {
+               $this->_transactionDepth--;
+
+               if($this->_transactionDepth == 0 || !$this->hasSavepoint()) {
+                       parent::commit();
+               } else {
+                       $this->exec("RELEASE SAVEPOINT LEVEL{$this->_transactionDepth}");
+               }
+       }
+
+       /**
+        * Rollback current transaction,
+        *
+        * @throws PDOException if there is no transaction started
+        * @return bool|void
+        */
+       public function rollBack()
+       {
+
+               if ($this->_transactionDepth == 0) {
+                       throw new PDOException('Rollback error : There is no transaction started');
+               }
+
+               $this->_transactionDepth--;
+
+               if($this->_transactionDepth == 0 || !$this->hasSavepoint()) {
+                       parent::rollBack();
+               } else {
+                       $this->exec("ROLLBACK TO SAVEPOINT LEVEL{$this->_transactionDepth}");
+               }
+       }
+}
diff --git a/tests/Util/Database/StaticDatabase.php b/tests/Util/Database/StaticDatabase.php
new file mode 100644 (file)
index 0000000..6f9bdbe
--- /dev/null
@@ -0,0 +1,126 @@
+<?php
+
+namespace Friendica\Test\Util\Database;
+
+use Friendica\Database\Database;
+use PDO;
+use PDOException;
+
+class StaticDatabase extends Database
+{
+       /**
+        * @var ExtendedPDO
+        */
+       private static $staticConnection;
+
+       /**
+        * Override the behaviour of connect, due there is just one, static connection at all
+        *
+        * @return bool|void
+        */
+       public function connect()
+       {
+               if (!is_null($this->connection) && $this->connected()) {
+                       return true;
+               }
+
+               if (!isset(self::$staticConnection)) {
+
+                       $port       = 0;
+                       $serveraddr = trim($this->configCache->get('database', 'hostname'));
+                       $serverdata = explode(':', $serveraddr);
+                       $server     = $serverdata[0];
+                       if (count($serverdata) > 1) {
+                               $port = trim($serverdata[1]);
+                       }
+                       $server  = trim($server);
+                       $user    = trim($this->configCache->get('database', 'username'));
+                       $pass    = trim($this->configCache->get('database', 'password'));
+                       $db      = trim($this->configCache->get('database', 'database'));
+                       $charset = trim($this->configCache->get('database', 'charset'));
+
+                       if (!(strlen($server) && strlen($user))) {
+                               return false;
+                       }
+
+                       $connect = "mysql:host=" . $server . ";dbname=" . $db;
+
+                       if ($port > 0) {
+                               $connect .= ";port=" . $port;
+                       }
+
+                       if ($charset) {
+                               $connect .= ";charset=" . $charset;
+                       }
+
+
+                       try {
+                               self::$staticConnection = @new ExtendedPDO($connect, $user, $pass);
+                               self::$staticConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
+                       } catch (PDOException $e) {
+                               /// @TODO At least log exception, don't ignore it!
+                       }
+               }
+
+               $this->driver = 'pdo';
+               $this->connection = self::$staticConnection;
+               $this->connected = true;
+
+               return $this->connected;
+       }
+
+       /**
+        * Override the transaction since there are now hierachical transactions possible
+        *
+        * @return bool
+        */
+       public function transaction()
+       {
+               if (!$this->connection->inTransaction() && !$this->connection->beginTransaction()) {
+                       return false;
+               }
+
+               $this->in_transaction = true;
+               return true;
+       }
+
+       /**
+        * @brief Does a commit
+        *
+        * @return boolean Was the command executed successfully?
+        */
+       public function commit()
+       {
+               if (!$this->performCommit()) {
+                       return false;
+               }
+               $this->in_transaction = false;
+               return true;
+       }
+
+       /**
+        * @return ExtendedPDO
+        */
+       public static function getGlobConnection()
+       {
+               return self::$staticConnection;
+       }
+
+       public static function statCommit()
+       {
+               if (isset(self::$staticConnection)) {
+                       while (self::$staticConnection->getTransactionDepth() > 0) {
+                               self::$staticConnection->commit();
+                       }
+               }
+       }
+
+       public static function statRollback()
+       {
+               if (isset(self::$staticConnection)) {
+                       while (self::$staticConnection->getTransactionDepth() > 0) {
+                               self::$staticConnection->rollBack();
+                       }
+               }
+       }
+}
index 351973d47968f036eba579a43a07c1f8543ad4f0..15da515277790c482c190690da4aed698a32cdc5 100644 (file)
@@ -12,7 +12,9 @@ use Friendica\Core\Config;
 use Friendica\Core\PConfig;
 use Friendica\Core\Protocol;
 use Friendica\Core\System;
+use Friendica\Database\Database;
 use Friendica\Network\HTTPException;
+use Friendica\Test\Util\Database\StaticDatabase;
 use Monolog\Handler\TestHandler;
 
 require_once __DIR__ . '/../../include/api.php';
@@ -47,13 +49,16 @@ class ApiTest extends DatabaseTest
         */
        public function setUp()
        {
-               parent::setUp();
+               StaticDatabase::statRollback();
 
                $dice = new Dice();
                $dice = $dice->addRules(include __DIR__ . '/../../static/dependencies.config.php');
+               $dice = $dice->addRule(Database::class, ['instanceOf' => StaticDatabase::class, 'shared' => true]);
                BaseObject::setDependencyInjection($dice);
                $this->app = BaseObject::getApp();
 
+               parent::setUp();
+
                $this->app->argc = 1;
                $this->app->argv = ['home'];
 
@@ -99,6 +104,11 @@ class ApiTest extends DatabaseTest
                Config::set('system', 'theme', 'system_theme');
        }
 
+       protected function tearDown()
+       {
+               StaticDatabase::statRollback();
+       }
+
        /**
         * Assert that an user array contains expected keys.
         * @param array $user User array