Merge branch 'develop-olympus' into develop

* develop-olympus:
  [task/refactor-db-testcase] Further improve error messages.
  [task/refactor-db-testcase] Do not show db password on connect error
  [task/refactor-db-testcase] Improve error message of db tests
  [task/refactor-db-testcase] Refactor phpbb_database_test_case
This commit is contained in:
Nils Adermann 2011-02-16 11:55:43 +01:00
commit bd51bbda4e
3 changed files with 359 additions and 273 deletions

View file

@ -34,3 +34,4 @@ $class_loader->register();
require_once 'test_framework/phpbb_test_case_helpers.php';
require_once 'test_framework/phpbb_test_case.php';
require_once 'test_framework/phpbb_database_test_case.php';
require_once 'test_framework/phpbb_database_test_connection_manager.php';

View file

@ -9,7 +9,7 @@
abstract class phpbb_database_test_case extends PHPUnit_Extensions_Database_TestCase
{
private static $already_connected;
static private $already_connected;
protected $test_case_helpers;
@ -38,66 +38,6 @@ abstract class phpbb_database_test_case extends PHPUnit_Extensions_Database_Test
return $this->test_case_helpers;
}
public function get_dbms_data($dbms)
{
$available_dbms = array(
'firebird' => array(
'SCHEMA' => 'firebird',
'DELIM' => ';;',
'PDO' => 'firebird',
),
'mysqli' => array(
'SCHEMA' => 'mysql_41',
'DELIM' => ';',
'PDO' => 'mysql',
),
'mysql' => array(
'SCHEMA' => 'mysql',
'DELIM' => ';',
'PDO' => 'mysql',
),
'mssql' => array(
'SCHEMA' => 'mssql',
'DELIM' => 'GO',
'PDO' => 'odbc',
),
'mssql_odbc'=> array(
'SCHEMA' => 'mssql',
'DELIM' => 'GO',
'PDO' => 'odbc',
),
'mssqlnative' => array(
'SCHEMA' => 'mssql',
'DELIM' => 'GO',
'PDO' => 'sqlsrv',
),
'oracle' => array(
'SCHEMA' => 'oracle',
'DELIM' => '/',
'PDO' => 'oci',
),
'postgres' => array(
'SCHEMA' => 'postgres',
'DELIM' => ';',
'PDO' => 'pgsql',
),
'sqlite' => array(
'SCHEMA' => 'sqlite',
'DELIM' => ';',
'PDO' => 'sqlite2',
),
);
if (isset($available_dbms[$dbms]))
{
return $available_dbms[$dbms];
}
else
{
trigger_error('Database unsupported', E_USER_ERROR);
}
}
public function get_database_config()
{
if (isset($_SERVER['PHPBB_TEST_DBMS']))
@ -142,232 +82,26 @@ abstract class phpbb_database_test_case extends PHPUnit_Extensions_Database_Test
}
}
// NOTE: This function is not the same as split_sql_file from functions_install
public function split_sql_file($sql, $dbms)
{
$dbms_data = $this->get_dbms_data($dbms);
$sql = str_replace("\r" , '', $sql);
$data = preg_split('/' . preg_quote($dbms_data['DELIM'], '/') . '$/m', $sql);
$data = array_map('trim', $data);
// The empty case
$end_data = end($data);
if (empty($end_data))
{
unset($data[key($data)]);
}
if ($dbms == 'sqlite')
{
// remove comment lines starting with # - they are not proper sqlite
// syntax and break sqlite2
foreach ($data as $i => $query)
{
$data[$i] = preg_replace('/^#.*$/m', "\n", $query);
}
}
return $data;
}
/**
* Retrieves a list of all tables from the database.
*
* @param PDO $pdo
* @param string $dbms
* @return array(string)
*/
function get_tables($pdo, $dbms)
{
switch ($pdo)
{
case 'mysql':
case 'mysql4':
case 'mysqli':
$sql = 'SHOW TABLES';
break;
case 'sqlite':
$sql = 'SELECT name
FROM sqlite_master
WHERE type = "table"';
break;
case 'mssql':
case 'mssql_odbc':
case 'mssqlnative':
$sql = "SELECT name
FROM sysobjects
WHERE type='U'";
break;
case 'postgres':
$sql = 'SELECT relname
FROM pg_stat_user_tables';
break;
case 'firebird':
$sql = 'SELECT rdb$relation_name
FROM rdb$relations
WHERE rdb$view_source is null
AND rdb$system_flag = 0';
break;
case 'oracle':
$sql = 'SELECT table_name
FROM USER_TABLES';
break;
}
$result = $pdo->query($sql);
$tables = array();
while ($row = $result->fetch(PDO::FETCH_NUM))
{
$tables[] = current($row);
}
return $tables;
}
/**
* Returns a PDO connection for the configured database.
*
* @param array $config The database configuration
* @param array $dbms Information on the used DBMS.
* @param bool $use_db Whether the DSN should be tied to a
* particular database making it impossible
* to delete that database.
* @return PDO The PDO database connection.
*/
public function new_pdo($config, $dbms, $use_db)
{
$dsn = $dbms['PDO'] . ':';
switch ($dbms['PDO'])
{
case 'sqlite2':
$dsn .= $config['dbhost'];
break;
case 'sqlsrv':
// prefix the hostname (or DSN) with Server= so using just (local)\SQLExpress
// works for example, further parameters can still be appended using ;x=y
$dsn .= 'Server=';
// no break -> rest like ODBC
case 'odbc':
// for ODBC assume dbhost is a suitable DSN
// e.g. Driver={SQL Server Native Client 10.0};Server=(local)\SQLExpress;
$dsn .= $config['dbhost'];
if ($use_db)
{
$dsn .= ';Database=' . $config['dbname'];
}
break;
default:
$dsn .= 'host=' . $config['dbhost'];
if ($use_db)
{
$dsn .= ';dbname=' . $config['dbname'];
}
break;
}
$pdo = new PDO($dsn, $config['dbuser'], $config['dbpasswd']);;
// good for debug
// $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
}
private function recreate_db($config, $dbms)
{
switch ($config['dbms'])
{
case 'sqlite':
if (file_exists($config['dbhost']))
{
unlink($config['dbhost']);
}
break;
default:
$pdo = $this->new_pdo($config, $dbms, false);
try
{
$pdo->exec('DROP DATABASE ' . $config['dbname']);
}
catch (PDOException $e)
{
// try to delete all tables if dropping the database was not possible.
foreach ($this->get_tables() as $table)
{
try
{
$pdo->exec('DROP TABLE ' . $table);
}
catch (PDOException $e){} // ignore non-existent tables
}
}
$pdo->exec('CREATE DATABASE ' . $config['dbname']);
break;
}
}
private function load_schema($pdo, $config, $dbms)
{
if ($config['dbms'] == 'mysql')
{
$sth = $pdo->query('SELECT VERSION() AS version');
$row = $sth->fetch(PDO::FETCH_ASSOC);
if (version_compare($row['version'], '4.1.3', '>='))
{
$dbms['SCHEMA'] .= '_41';
}
else
{
$dbms['SCHEMA'] .= '_40';
}
}
$sql = $this->split_sql_file(file_get_contents(dirname(__FILE__) . "/../../phpBB/install/schemas/{$dbms['SCHEMA']}_schema.sql"), $config['dbms']);
foreach ($sql as $query)
{
$pdo->exec($query);
}
}
public function getConnection()
{
$config = $this->get_database_config();
$dbms = $this->get_dbms_data($config['dbms']);
$manager = $this->create_connection_manager($config);
if (!self::$already_connected)
{
$this->recreate_db($config, $dbms);
$manager->recreate_db();
}
$pdo = $this->new_pdo($config, $dbms, true);
$manager->connect();
if (!self::$already_connected)
{
$this->load_schema($pdo, $config, $dbms);
$manager->load_schema();
self::$already_connected = true;
}
return $this->createDefaultDBConnection($pdo, 'testdb');
return $this->createDefaultDBConnection($manager->get_pdo(), 'testdb');
}
public function new_dbal()
@ -399,4 +133,9 @@ abstract class phpbb_database_test_case extends PHPUnit_Extensions_Database_Test
{
$this->get_test_case_helpers()->setExpectedTriggerError($errno, $message);
}
protected function create_connection_manager($config)
{
return new phpbb_database_test_connection_manager($config);
}
}

View file

@ -0,0 +1,346 @@
<?php
/**
*
* @package testing
* @copyright (c) 2011 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/
class phpbb_database_test_connection_manager
{
private $config;
private $dbms;
private $pdo;
/**
* Constructor
*
* @param array $config Tests database configuration as returned by
* phpbb_database_test_case::get_database_config()
*/
public function __construct($config)
{
$this->config = $config;
$this->dbms = $this->get_dbms_data($this->config['dbms']);
}
/**
* Return the current PDO instance
*/
public function get_pdo()
{
return $this->pdo;
}
/**
* Creates a PDO connection for the configured database.
*
* @param bool $use_db Whether the DSN should be tied to a
* particular database making it impossible
* to delete that database.
*/
public function connect($use_db = true)
{
$dsn = $this->dbms['PDO'] . ':';
switch ($this->dbms['PDO'])
{
case 'sqlite2':
$dsn .= $this->config['dbhost'];
break;
case 'sqlsrv':
// prefix the hostname (or DSN) with Server= so using just (local)\SQLExpress
// works for example, further parameters can still be appended using ;x=y
$dsn .= 'Server=';
// no break -> rest like ODBC
case 'odbc':
// for ODBC assume dbhost is a suitable DSN
// e.g. Driver={SQL Server Native Client 10.0};Server=(local)\SQLExpress;
$dsn .= $this->config['dbhost'];
if ($use_db)
{
$dsn .= ';Database=' . $this->config['dbname'];
}
break;
default:
$dsn .= 'host=' . $this->config['dbhost'];
if ($use_db)
{
$dsn .= ';dbname=' . $this->config['dbname'];
}
break;
}
try
{
$this->pdo = new PDO($dsn, $this->config['dbuser'], $this->config['dbpasswd']);
}
catch (PDOException $e)
{
$cleaned_dsn = str_replace($this->config['dbpasswd'], '*password*', $dsn);
throw new Exception("Unable do connect to $cleaned_dsn using PDO with error: {$e->getMessage()}");
}
// good for debug
// $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
/**
* Load the phpBB database schema into the database
*/
public function load_schema()
{
$this->ensure_connected(__METHOD__);
$directory = dirname(__FILE__) . '/../../phpBB/install/schemas/';
$this->load_schema_from_file($directory);
}
/**
* Drop the database if it exists and re-create it
*
* Note: This does not load the schema, and it is suggested
* to re-connect after calling to get use_db isolation.
*/
public function recreate_db()
{
switch ($this->config['dbms'])
{
case 'sqlite':
if (file_exists($this->config['dbhost']))
{
unlink($this->config['dbhost']);
}
break;
default:
$this->connect(false);
try
{
$this->pdo->exec('DROP DATABASE ' . $config['dbname']);
}
catch (PDOException $e)
{
// try to delete all tables if dropping the database was not possible.
foreach ($this->get_tables() as $table)
{
$this->pdo->exec('DROP TABLE ' . $table);
}
}
$this->pdo->exec('CREATE DATABASE ' . $this->config['dbname']);
break;
}
}
/**
* Retrieves a list of all tables from the database.
*
* @return array(string)
*/
public function get_tables()
{
$this->ensure_connected(__METHOD__);
switch ($this->config['dbms'])
{
case 'mysql':
case 'mysql4':
case 'mysqli':
$sql = 'SHOW TABLES';
break;
case 'sqlite':
$sql = 'SELECT name
FROM sqlite_master
WHERE type = "table"';
break;
case 'mssql':
case 'mssql_odbc':
case 'mssqlnative':
$sql = "SELECT name
FROM sysobjects
WHERE type='U'";
break;
case 'postgres':
$sql = 'SELECT relname
FROM pg_stat_user_tables';
break;
case 'firebird':
$sql = 'SELECT rdb$relation_name
FROM rdb$relations
WHERE rdb$view_source is null
AND rdb$system_flag = 0';
break;
case 'oracle':
$sql = 'SELECT table_name
FROM USER_TABLES';
break;
}
$result = $this->pdo->query($sql);
$tables = array();
while ($row = $result->fetch(PDO::FETCH_NUM))
{
$tables[] = current($row);
}
return $tables;
}
/**
* Throw an exception if not connected
*/
protected function ensure_connected($method_name)
{
if (null === $this->pdo)
{
throw new Exception(sprintf('You must connect before calling %s', $method_name));
}
}
/**
* Compile the correct schema filename (as per create_schema_files) and
* load it into the database.
*/
protected function load_schema_from_file($directory)
{
$schema = $this->dbms['SCHEMA'];
if ($this->config['dbms'] == 'mysql')
{
$sth = $this->pdo->query('SELECT VERSION() AS version');
$row = $sth->fetch(PDO::FETCH_ASSOC);
if (version_compare($row['version'], '4.1.3', '>='))
{
$schema .= '_41';
}
else
{
$schema .= '_40';
}
}
$filename = $directory . $schema . '_schema.sql';
$sql = $this->split_sql(file_get_contents($filename));
foreach ($sql as $query)
{
$this->pdo->exec($query);
}
}
/**
* Split contents of an SQL file into an array of SQL statements
*
* Note: This method is not the same as split_sql_file from functions_install.
*
* @param string $sql Raw contents of an SQL file
*
* @return Array of runnable SQL statements
*/
protected function split_sql($sql)
{
$sql = str_replace("\r" , '', $sql);
$data = preg_split('/' . preg_quote($this->dbms['DELIM'], '/') . '$/m', $sql);
$data = array_map('trim', $data);
// The empty case
$end_data = end($data);
if (empty($end_data))
{
unset($data[key($data)]);
}
if ($this->config['dbms'] == 'sqlite')
{
// remove comment lines starting with # - they are not proper sqlite
// syntax and break sqlite2
foreach ($data as $i => $query)
{
$data[$i] = preg_replace('/^#.*$/m', "\n", $query);
}
}
return $data;
}
/**
* Map a phpBB dbms driver name to dbms data array
*/
protected function get_dbms_data($dbms)
{
$available_dbms = array(
'firebird' => array(
'SCHEMA' => 'firebird',
'DELIM' => ';;',
'PDO' => 'firebird',
),
'mysqli' => array(
'SCHEMA' => 'mysql_41',
'DELIM' => ';',
'PDO' => 'mysql',
),
'mysql' => array(
'SCHEMA' => 'mysql',
'DELIM' => ';',
'PDO' => 'mysql',
),
'mssql' => array(
'SCHEMA' => 'mssql',
'DELIM' => 'GO',
'PDO' => 'odbc',
),
'mssql_odbc'=> array(
'SCHEMA' => 'mssql',
'DELIM' => 'GO',
'PDO' => 'odbc',
),
'mssqlnative' => array(
'SCHEMA' => 'mssql',
'DELIM' => 'GO',
'PDO' => 'sqlsrv',
),
'oracle' => array(
'SCHEMA' => 'oracle',
'DELIM' => '/',
'PDO' => 'oci',
),
'postgres' => array(
'SCHEMA' => 'postgres',
'DELIM' => ';',
'PDO' => 'pgsql',
),
'sqlite' => array(
'SCHEMA' => 'sqlite',
'DELIM' => ';',
'PDO' => 'sqlite2',
),
);
if (isset($available_dbms[$dbms]))
{
return $available_dbms[$dbms];
}
else
{
$message = "Supplied dbms \"$dbms\" is not a valid phpBB dbms, must be one of: ";
$message .= implode(', ', array_keys($available_dbms));
throw new Exception($message);
}
}
}