177 lines
5.8 KiB
PHP
177 lines
5.8 KiB
PHP
<?php
|
|
require_once implode(DIRECTORY_SEPARATOR, [
|
|
__DIR__,
|
|
'vendor',
|
|
'autoload.php'
|
|
]);
|
|
|
|
class Benchmark
|
|
{
|
|
protected static array $queue = [];
|
|
public static function print(): void
|
|
{
|
|
if (empty(self::$queue)) {
|
|
return;
|
|
}
|
|
echo implode(PHP_EOL, self::$queue), PHP_EOL;
|
|
self::$queue = [];
|
|
}
|
|
public static function execute(callable $callback, ?array $args = null, ?string $description = null): mixed
|
|
{
|
|
if (null === $description) {
|
|
$description = self::getCallableName($callback);
|
|
}
|
|
$i = count(self::$queue);
|
|
self::$queue[$i] = "Executing $description";
|
|
$start = microtime(true);
|
|
|
|
if (null === $args) {
|
|
$result = call_user_func($callback);
|
|
} else {
|
|
$result = call_user_func_array($callback, $args);
|
|
}
|
|
|
|
$end = microtime(true);
|
|
self::$queue[$i] .= sprintf(" in %.2f seconds", $end - $start);
|
|
return $result;
|
|
}
|
|
public static function getCallableName(callable $callable): string
|
|
{
|
|
return match (true) {
|
|
is_string($callable) && strpos($callable, '::') => '[static] ' . $callable,
|
|
is_string($callable) => '[function] ' . $callable,
|
|
is_array($callable) && is_object($callable[0]) => '[method] ' . get_class($callable[0]) . '->' . $callable[1],
|
|
is_array($callable) => '[static] ' . $callable[0] . '::' . $callable[1],
|
|
$callable instanceof Closure => '[closure]',
|
|
is_object($callable) => '[invokable] ' . get_class($callable),
|
|
default => '[unknown]',
|
|
};
|
|
}
|
|
}
|
|
class TestBootstrap
|
|
{
|
|
public function __construct(protected array $configuration) {}
|
|
|
|
public function run(bool $resetDatabase = false): void
|
|
{
|
|
if ($resetDatabase) {
|
|
if (Benchmark::execute([$this, 'isMigrated'])) {
|
|
Benchmark::execute([$this, 'resetDatabase']);
|
|
}
|
|
}
|
|
if (!Benchmark::execute([$this, 'isMigrated'])) {
|
|
Benchmark::execute([$this, 'migrate']);
|
|
}
|
|
}
|
|
|
|
protected string $baseCommand = "bin/phinx";
|
|
public function isMigrated(): bool
|
|
{
|
|
$cmd = "{$this->baseCommand} status -e testing -f json --no-info";
|
|
$output = shell_exec($cmd);
|
|
$status = json_decode($output, true);
|
|
|
|
return $status['missing_count'] === 0;
|
|
}
|
|
public function migrate(): void
|
|
{
|
|
$cmd = "{$this->baseCommand} migrate -e testing";
|
|
$status = shell_exec($cmd);
|
|
if ($status !== false and $status !== null) {
|
|
$cmd = "{$this->baseCommand} seed:run -e testing";
|
|
shell_exec($cmd);
|
|
}
|
|
}
|
|
|
|
public function resetDatabase(): void
|
|
{
|
|
$tables = $this->getTables();
|
|
if ($this->connect()->beginTransaction()) {
|
|
try {
|
|
$this->connect()->query("SET FOREIGN_KEY_CHECKS=0;");
|
|
foreach ($tables as $table) {
|
|
$this->connect()->query("DROP TABLE IF EXISTS `{$table}`");
|
|
}
|
|
$this->connect()->query("SET FOREIGN_KEY_CHECKS=1;");
|
|
if ($this->connect()->inTransaction()) {
|
|
$this->connect()->commit();
|
|
}
|
|
} catch (PDOException $exception) {
|
|
if ($this->connect()->inTransaction()) {
|
|
$this->connect()->rollBack();
|
|
}
|
|
throw $exception;
|
|
}
|
|
}
|
|
}
|
|
public function truncateTables(): void
|
|
{
|
|
$tables = $this->getTables();
|
|
if ($this->connect()->beginTransaction()) {
|
|
try {
|
|
$this->connect()->query("SET FOREIGN_KEY_CHECKS=0;");
|
|
foreach ($tables as $table) {
|
|
$this->connect()->query("TRUNCATE TABLE `{$table}`");
|
|
}
|
|
$this->connect()->query("SET FOREIGN_KEY_CHECKS=1;");
|
|
if ($this->connect()->inTransaction()) {
|
|
$this->connect()->commit();
|
|
}
|
|
} catch (PDOException $exception) {
|
|
if ($this->connect()->inTransaction()) {
|
|
$this->connect()->rollBack();
|
|
}
|
|
throw $exception;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected PDO $connection;
|
|
protected function connect(): PDO
|
|
{
|
|
if (!isset($this->connection)) {
|
|
$dsn = "mysql:host={$this->configuration['DB_HOST']};dbname={$this->configuration['DB_DATABASE']}";
|
|
$this->connection = new PDO(
|
|
$dsn,
|
|
$this->configuration['DB_USER'],
|
|
$this->configuration['DB_PASSWORD']
|
|
);
|
|
}
|
|
return $this->connection;
|
|
}
|
|
protected array $tables;
|
|
protected function getTables(): array
|
|
{
|
|
if (!isset($this->tables)) {
|
|
$this->tables = $this->connect()->query("SHOW TABLES")->fetchAll(PDO::FETCH_COLUMN);
|
|
}
|
|
return $this->tables;
|
|
}
|
|
}
|
|
|
|
spl_autoload_register(function($className) {
|
|
$baseTestPath = __DIR__ . "/tests";
|
|
$namespaceMap = [
|
|
"Tests\\Extension\\" => "{$baseTestPath}/extension",
|
|
"Tests\\Integration\\" => "{$baseTestPath}/integration",
|
|
"Tests\\Unit\\" => "{$baseTestPath}/unit/src",
|
|
"Tests\\Performance\\" => "{$baseTestPath}/performance",
|
|
];
|
|
foreach ($namespaceMap as $namespace => $path) {
|
|
if (str_starts_with($className, $namespace)) {
|
|
require str_replace($namespace, "{$path}/", $className) . ".php";
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
|
|
$bootstrap = new TestBootstrap($_ENV);
|
|
$resetDatabase = $_ENV['RESET_DATABASE'] ?? false;
|
|
Benchmark::execute([$bootstrap, 'run'], ['resetDatabase' => $resetDatabase]);
|
|
Benchmark::print();
|
|
|
|
register_shutdown_function(function() use ($bootstrap) {
|
|
Benchmark::execute([$bootstrap, 'truncateTables']);
|
|
Benchmark::print();
|
|
});
|