Skip to content

Commit

Permalink
Merge branch 'master' of github.com:BR0kEN-/TqExtension
Browse files Browse the repository at this point in the history
  • Loading branch information
BR0kEN- committed Jan 12, 2016
2 parents 721b79e + 80127da commit 61ac216
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 18 deletions.
12 changes: 12 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@
- To see all, available in your system, steps execute the `bin/behat -dl`.
- Some examples can be found [here](examples).

## Work with copy of DB on the fly

If your steps makes an irreversible changes in database and you want to be sure that works with clean, initial database, then **TqExtension** can give such possibility for you.

Note, database will be copied before feature started and will be restored after completion. This means that changes, made by scenarios in feature, will be available until next feature started. This gives a possibility to operate changes per scenarios in one feature.

Also you able to adjust settings for every feature. By default you WILL NOT WORK WITH A COPY OF DB and, to enable this, you MUST ADD `@cloneDB` tag to FEATURE DEFINITION. **Its important**: not to scenario definition, to feature.

And even you want to work with specific connection (key in `$databases` array from `settings.php`) - it's possible. Just add it name after the `@cloneDB` tag, separated by `:` (e.g. `@cloneDB:default`).

All this sounds great, but there are drawbacks. **You will lost execution time before and after every feature.** The value of this time depends on your hardware and size of database. So try to not use large databases for testing.

## Table of contents

- [TqContext](#tqcontext)
Expand Down
99 changes: 82 additions & 17 deletions src/Utils/DatabaseManager.php
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
<?php
/**
* @author Sergey Bondarenko, <sb@firstvector.org>
* @author Sergii Bondarenko, <sb@firstvector.org>
*/
namespace Drupal\TqExtension\Utils;

class DatabaseManager
{
/**
* MySQL and MySQLDump login arguments.
*
* @var string
*/
private $credentials = '-u%s -p%s';
private $credentials = '-u%s -p%s -h%s -P%s';
/**
* Name of original database.
*
* @var string
*/
private $originalName = '';
private $source = '';
/**
* Name of temporary database that will store data from original.
*
* @var string
*/
private $newName = '';
private $temporary = '';
/**
* Name of an object where this class is called.
*
Expand All @@ -45,48 +47,111 @@ public function __construct($connection, $callee)
throw new \InvalidArgumentException(sprintf('An object of "%s" type does not exist.', $callee));
}

/** @var array $databases */
$databases = [];

require sprintf('%s/%s/settings.php', DRUPAL_ROOT, conf_path());

if (empty($databases[$connection])) {
throw new \InvalidArgumentException(sprintf('The "%s" database connection does not exist.', $connection));
}

$info = $databases[$connection]['default'];

$this->callee = $callee;
$this->originalName = $info['database'];
$this->newName = "tqextension_$this->originalName";
$this->credentials = sprintf($this->credentials, $info['username'], $info['password']);
$db = $databases[$connection]['default'];

foreach (['drop', 'create'] as $action) {
$this->exec("mysql $this->credentials -e '$action database $this->newName;'");
foreach (['port' => 3306, 'host' => '127.0.0.1'] as $option => $default) {
if (empty($db[$option])) {
$db[$option] = $default;
}
}

$this->exec("mysqldump $this->credentials $this->originalName | mysql $this->credentials $this->newName");
$this->callee = $callee;
$this->source = $db['database'];
$this->temporary = "tqextension_$this->source";
$this->credentials = sprintf($this->credentials, $db['username'], $db['password'], $db['host'], $db['port']);

// Drop and create temporary DB and copy source into it.
$this->copy($this->source, $this->temporary);
}

/**
* Restore original database.
*/
public function __destruct()
{
$this->exec("mysqldump $this->credentials $this->newName | mysql $this->credentials $this->originalName");
// Drop and create source DB and copy temporary into it.
$this->copy($this->temporary, $this->source);
// Kill temporary DB.
$this->drop($this->temporary);
}

/**
* @param string $name
* Name of the database to check.
*
* @return bool
* Checking state.
*/
public function exist($name)
{
return !empty($this->exec("mysql -e 'show databases' | grep '^$name$'"));
}

/**
* @param string $name
* Name of the database to drop.
*/
public function drop($name)
{
if ($this->exist($name)) {
$this->exec("mysql -e '%s database $name;'", __FUNCTION__);
}
}

/**
* @param string $name
* Name of the database to create.
*/
public function create($name)
{
if (!$this->exist($name)) {
$this->exec("mysql -e '%s database $name;'", __FUNCTION__);
}
}

/**
* @param string $source
* Source DB name.
* @param string $destination
* Name of the new DB.
*/
public function copy($source, $destination)
{
$this->drop($destination);
$this->create($destination);
$this->exec("mysqldump $source | mysql $destination");
}

/**
* Executes a shell command.
*
* @param string $command
* Command to execute.
*
* @return string
* Result of a shell command.
*/
private function exec($command)
{
$command = vsprintf($command, array_slice(func_get_args(), 1));
// Adding credentials after "mysql" and "mysqldump" commands.
$command = preg_replace(
'/(mysql(?:dump)?)/',
"\\1 $this->credentials",
vsprintf($command, array_slice(func_get_args(), 1))
);

if (method_exists($this->callee, 'debug')) {
call_user_func([$this->callee, 'debug'], [$command]);
}

shell_exec($command);
return trim(shell_exec($command));
}
}
2 changes: 1 addition & 1 deletion src/Utils/FormValueAssertion.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public function checkable()
}

/**
* @param string[] $allowedElements
* @param array[] $allowedElements
* Element machine names.
*/
private function restrictElements(array $allowedElements)
Expand Down

0 comments on commit 61ac216

Please sign in to comment.