Skip to content

Commit

Permalink
feat(MysqlServerController) add
Browse files Browse the repository at this point in the history
Add the MysqlServerController extension, and related support classes to
download, initialize and start a local MySQL Community Server on the
machine.

Together with the work in there, improve the `UopzFunctions` trait to
return Closure to unset the mocks.
  • Loading branch information
lucatume committed Aug 15, 2024
1 parent bfc179b commit e095d47
Show file tree
Hide file tree
Showing 22 changed files with 3,094 additions and 24 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ wordpress_install:

clean_procs:
pgrep -f 'php -S' | xargs kill
pgrep chromedriver | xargs kill
-pkill -9 -f chromedriver
-pkill -9 -f mysqld
rm -f var/_output/*.pid var/_output/*.running
set -o allexport && source tests/.env && set +o allexport && docker compose down
.PHONY: clean_procs
Expand Down
17 changes: 11 additions & 6 deletions codeception.dist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@ coverage:
- src/*
wpFolder: '%WORDPRESS_ROOT_DIR%'
extensions:
enabled:
- "lucatume\\WPBrowser\\Extension\\EventDispatcherBridge"
- "lucatume\\WPBrowser\\Extension\\BuiltInServerController"
- "lucatume\\WPBrowser\\Extension\\ChromeDriverController"
- "lucatume\\WPBrowser\\Extension\\DockerComposeController"
- "lucatume\\WPBrowser\\Extension\\IsolationSupport"
config:
"lucatume\\WPBrowser\\Extension\\BuiltInServerController":
docroot: '%WORDPRESS_ROOT_DIR%'
Expand All @@ -35,6 +29,17 @@ extensions:
"lucatume\\WPBrowser\\Extension\\DockerComposeController":
compose-file: docker-compose.yml
env-file: tests/.env
"lucatume\\WPBrowser\\Extension\\MysqlServerController":
port: '%WORDPRESS_DB_LOCALHOST_PORT%'
database: '%WORDPRESS_DB_NAME%'
user: '%WORDPRESS_DB_USER%'
password: '%WORDPRESS_DB_PASSWORD%'
enabled:
- "lucatume\\WPBrowser\\Extension\\EventDispatcherBridge"
- "lucatume\\WPBrowser\\Extension\\BuiltInServerController"
- "lucatume\\WPBrowser\\Extension\\ChromeDriverController"
- "lucatume\\WPBrowser\\Extension\\MysqlServerController"
- "lucatume\\WPBrowser\\Extension\\IsolationSupport"
commands:
- "lucatume\\WPBrowser\\Command\\RunOriginal"
- "lucatume\\WPBrowser\\Command\\RunAll"
Expand Down
3 changes: 3 additions & 0 deletions src/Extension/ChromeDriverController.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ private function getPort(): int
return (int)($config['port'] ?? 4444);
}

/**
* @throws ExtensionException
*/
private function getBinary(): ?string
{
$config = $this->config;
Expand Down
209 changes: 209 additions & 0 deletions src/Extension/MysqlServerController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
<?php

declare(strict_types=1);

namespace lucatume\WPBrowser\Extension;

use Codeception\Exception\ExtensionException;
use lucatume\WPBrowser\ManagedProcess\MysqlServer;
use lucatume\WPBrowser\Utils\Filesystem;
use Symfony\Component\Console\Output\OutputInterface;

class MysqlServerController extends ServiceExtension
{
use PidBasedController;

public const PID_FILE_NAME = 'mysql-server.pid';
private MysqlServer $mysqlServer;

public function start(OutputInterface $output): void
{
$pidFile = $this->getPidFile();

if (is_file($pidFile)) {
$output->writeln('MySQL server already running.');

return;
}

$port = $this->getPort();
$database = $this->getDatabase();
$user = $this->getUser();
$password = $this->getPassword();
$binary = $this->getBinary();

$output->write("Starting MySQL server on port $port ...");
try {
$this->mysqlServer = new MysqlServer(
codecept_output_dir('_mysql_server'),
$port,
$database,
$user,
$password,
$binary
);
$this->mysqlServer->setOutput($output);
$this->mysqlServer->start();
} catch (\Exception $e) {
throw new ExtensionException($this, "Error while starting MySQL server. {$e->getMessage()}", $e);
}
$output->write(' ok', true);
}

public function getPidFile(): string
{
return codecept_output_dir(self::PID_FILE_NAME);
}

private function getDatabase(): string
{
/** @var array{database?: string} $config */
$config = $this->config;

if (isset($config['database']) && !(is_string($config['database']) && !empty($config['database']))) {
throw new ExtensionException(
$this,
'The "database" configuration option must be a string.'
);
}

return $config['database'] ?? 'wordpress';
}

private function getUser(): string
{
/** @var array{user?: string} $config */
$config = $this->config;

if (isset($config['user']) && !(is_string($config['user']) && !empty($config['user']))) {
throw new ExtensionException(
$this,
'The "user" configuration option must be a string.'
);
}

return $config['user'] ?? 'wordpress';
}

private function getPassword(): string
{
/** @var array{password?: string} $config */
$config = $this->config;

if (isset($config['password']) && !is_string($config['password'])) {
throw new ExtensionException(
$this,
'The "password" configuration option must be a string.'
);
}

return $config['password'] ?? 'wordpress';
}

/**
* @throws ExtensionException
*/
public function getPort(): int
{
$config = $this->config;
if (isset($config['port'])
&& !(
is_numeric($config['port'])
&& (int)$config['port'] == $config['port']
&& $config['port'] > 0
)) {
throw new ExtensionException(
$this,
'The "port" configuration option must be an integer greater than 0.'
);
}

/** @var array{port?: number} $config */
return (int)($config['port'] ?? 8906);
}

public function stop(OutputInterface $output): void
{
$pidFile = $this->getPidFile();
$mysqlServerPid = (int)file_get_contents($pidFile);

if (!$mysqlServerPid) {
$output->writeln('MySQL server not running.');
return;
}

$output->write("Stopping MySQL server with PID $mysqlServerPid ...", false);
$this->kill($mysqlServerPid);
$this->removePidFile($pidFile);
$output->write(' ok', true);
}

public function getPrettyName(): string
{
return 'MySQL Community Server';
}

/**
* @return array{
* running: string,
* pidFile: string,
* port: int
* }
* @throws ExtensionException
*/
public function getInfo(): array
{
$isRunning = is_file($this->getPidFile());

$info = [
'running' => $isRunning ? 'yes' : 'no',
'pidFile' => Filesystem::relativePath(codecept_root_dir(), $this->getPidFile()),
'host' => '127.0.0.1',
'port' => $this->getPort(),
'user' => $this->getUser(),
'password' => $this->getPassword(),
'root user' => 'root',
'root password' => $this->getUser() === 'root' ? $this->getPassword() : ''
];

if ($isRunning) {
$info['mysql command'] = $this->getCliConnectionCommandline();
$info['mysql root command'] = $this->getRootCliConnectionCommandline();
}

return $info;
}

private function getCliConnectionCommandline(): string
{
if ($this->getPassword() === '') {
return "mysql -h 127.0.0.1 -P {$this->getPort()} -u {$this->getUser()}";
}

return "mysql -h 127.0.0.1 -P {$this->getPort()} -u {$this->getUser()} -p '{$this->getPassword()}'";
}

private function getRootCliConnectionCommandline(): string
{
$rootPassword = $this->getUser() === 'root' ? $this->getPassword() : '';
if ($rootPassword === '') {
return "mysql -h 127.0.0.1 -P {$this->getPort()} -u root";
}

return "mysql -h 127.0.0.1 -P {$this->getPort()} -u root -p '{$rootPassword}'";
}

private function getBinary(): ?string
{
$config = $this->config;
if (isset($config['binary']) && !(is_string($config['binary']) && is_executable($config['binary']))) {
throw new ExtensionException(
$this,
'The "binary" configuration option must be an executable file.'
);
}

/** @var array{binary?: string} $config */
return ($config['binary'] ?? null);
}
}
2 changes: 1 addition & 1 deletion src/ManagedProcess/ChromeDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function __construct(
/**
* @throws RuntimeException
*/
public function doStart(): void
private function doStart(): void
{
$command = [$this->chromeDriverBinary, '--port=' . $this->port, ...$this->arguments];
$process = new Process($command);
Expand Down
3 changes: 2 additions & 1 deletion src/ManagedProcess/ManagedProcessTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ public function stop(): ?int
$exitCode = $process->stop();

if (is_file(static::getPidFile()) && !unlink(static::getPidFile())) {
$pidFile = static::getPidFile();
throw new RuntimeException(
"Could not remove PID file '{static::getPidFile(}'.",
"Could not remove PID file {$pidFile}.",
ManagedProcessInterface::ERR_PID_FILE_DELETE
);
}
Expand Down
Loading

0 comments on commit e095d47

Please sign in to comment.