Refactored test system to be object-oriented

This commit is contained in:
Marcin Kurczewski 2014-05-06 18:09:23 +02:00
parent c005da2e6d
commit 2e6687329b
2 changed files with 216 additions and 154 deletions

View file

@ -8,6 +8,14 @@ class AbstractTest
$this->assert = new Assert();
}
public function setup()
{
}
public function teardown()
{
}
protected function mockUser()
{
$user = UserModel::spawn();

View file

@ -3,180 +3,234 @@ require_once __DIR__ . '/../src/core.php';
require_once __DIR__ . '/../src/upgrade.php';
\Chibi\Autoloader::registerFileSystem(__DIR__);
$options = getopt('cf:', ['clean', 'filter:']);
$cleanDatabase = (isset($options['c']) or isset($options['clean']));
if (isset($options['f']))
$filter = $options['f'];
elseif (isset($options['filter']))
$filter = $options['filter'];
else
$filter = null;
$dbPath = __DIR__ . '/db.sqlite';
if (file_exists($dbPath) and $cleanDatabase)
unlink($dbPath);
try
class TestRunner
{
resetEnvironment();
upgradeDatabase();
protected $dbPath;
runAll($filter);
}
finally
{
removeTestFolders();
}
function resetEnvironment()
{
global $dbPath;
prepareConfig(true);
getConfig()->main->dbDriver = 'sqlite';
getConfig()->main->dbLocation = $dbPath;
removeTestFolders();
prepareEnvironment(true);
}
function removeTestFolders()
{
$folders =
[
realpath(getConfig()->main->filesPath),
realpath(getConfig()->main->thumbsPath),
realpath(dirname(getConfig()->main->logsPath)),
];
foreach ($folders as $folder)
public function __construct()
{
if (!file_exists($folder))
continue;
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
$folder,
FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST);
foreach ($it as $path)
{
$path->isFile()
? unlink($path->getPathname())
: rmdir($path->getPathname());
}
rmdir($folder);
}
}
function getTestMethods($filter)
{
$testFiles = [];
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__)) as $fileName)
{
$path = $fileName->getPathname();
if (preg_match('/.*Test.php$/', $path))
$testFiles []= $path;
$this->dbPath = __DIR__ . '/db.sqlite';
}
$testClasses = \Chibi\Util\Reflection::loadClasses($testFiles);
if ($filter !== null)
public function run($options)
{
$testClasses = array_filter($testClasses, function($className) use ($filter)
{
return stripos($className, $filter) !== false;
});
}
$cleanDatabase = (isset($options['c']) or isset($options['clean']));
$testMethods = [];
if (isset($options['f']))
$filter = $options['f'];
elseif (isset($options['filter']))
$filter = $options['filter'];
else
$filter = null;
foreach ($testClasses as $class)
{
$reflectionClass = new ReflectionClass($class);
foreach ($reflectionClass->getMethods() as $method)
{
if (preg_match('/test/i', $method->name) and $method->isPublic())
{
$testMethods []= $method;
}
}
}
if ($cleanDatabase)
$this->cleanDatabase();
return $testMethods;
}
function runAll($filter)
{
$startTime = microtime(true);
$testMethods = getTestMethods($filter);
echo 'Starting tests' . PHP_EOL;
//get display names of the methods
$labels = [];
foreach ($testMethods as $key => $method)
$labels[$key] = $method->class . '::' . $method->name;
//ensure every label has the same length
$maxLabelLength = count($testMethods) > 0 ? max(array_map('strlen', $labels)) : 0;
foreach ($labels as &$label)
$label = str_pad($label, $maxLabelLength + 1, ' ');
$pad = count($testMethods) ? ceil(log10(1 + count($testMethods))) : 0;
//run all the methods
$success = true;
foreach ($testMethods as $key => $method)
{
$instance = new $method->class();
$testStartTime = microtime(true);
echo str_pad($key + 1, $pad, ' ', STR_PAD_LEFT) . ' ';
echo $labels[$key] . '... ';
unset($e);
try
{
runSingle(function() use ($method, $instance)
$this->resetEnvironment();
upgradeDatabase();
$testFixtures = $this->getTestFixtures($filter);
$this->runAll($testFixtures);
}
finally
{
$this->removeTestFolders();
}
}
protected function cleanDatabase()
{
if (file_exists($this->dbPath))
unlink($this->dbPath);
}
protected function resetEnvironment()
{
prepareConfig(true);
getConfig()->main->dbDriver = 'sqlite';
getConfig()->main->dbLocation = $this->dbPath;
$this->removeTestFolders();
prepareEnvironment(true);
}
protected function removeTestFolders()
{
$folders =
[
realpath(getConfig()->main->filesPath),
realpath(getConfig()->main->thumbsPath),
realpath(dirname(getConfig()->main->logsPath)),
];
foreach ($folders as $folder)
{
if (!file_exists($folder))
continue;
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
$folder,
FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST);
foreach ($it as $path)
{
$path->isFile()
? unlink($path->getPathname())
: rmdir($path->getPathname());
}
rmdir($folder);
}
}
protected function getTestFixtures($filter)
{
$testFiles = [];
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__)) as $fileName)
{
$path = $fileName->getPathname();
if (preg_match('/.*Test.php$/', $path))
$testFiles []= $path;
}
$testClasses = \Chibi\Util\Reflection::loadClasses($testFiles);
if ($filter !== null)
{
$testClasses = array_filter($testClasses, function($className) use ($filter)
{
return stripos($className, $filter) !== false;
});
}
$testFixtures = [];
foreach ($testClasses as $class)
{
$reflectionClass = new ReflectionClass($class);
if ($reflectionClass->isAbstract())
continue;
$testFixture = new StdClass;
$testFixture->class = $reflectionClass;
$testFixture->methods = [];
foreach ($reflectionClass->getMethods() as $method)
{
if (preg_match('/test/i', $method->name)
and $method->isPublic()
and $method->getNumberOfParameters() == 0)
{
$testFixture->methods []= $method;
}
}
$testFixtures []= $testFixture;
}
return $testFixtures;
}
protected function runAll($testFixtures)
{
$startTime = microtime(true);
$testNumber = 0;
$resultPrinter = function($result) use (&$testNumber)
{
printf('%3d %-65s ', ++ $testNumber, $result->origin->class . '::' . $result->origin->name);
if ($result->success)
echo 'OK';
else
echo 'FAIL';
printf(' [%.03fs]' . PHP_EOL, $result->duration);
if ($result->exception)
{
echo '---' . PHP_EOL;
echo $result->exception->getMessage() . PHP_EOL;
echo $result->exception->getTraceAsString() . PHP_EOL;
echo '---' . PHP_EOL . PHP_EOL;
}
};
//run all the methods
echo 'Starting tests' . PHP_EOL;
$success = true;
foreach ($testFixtures as $className => $testFixture)
{
$results = $this->runTestFixture($testFixture, $resultPrinter);
foreach ($results as $result)
$success &= $result->success;
}
printf('%3s %-65s %s [%.03fs]' . PHP_EOL,
' ',
'All tests',
$success ? 'OK' : 'FAIL',
microtime(true) - $startTime);
return $success;
}
protected function runTestFixture($testFixture, $resultPrinter)
{
$instance = $testFixture->class->newInstance();
$instance->setup();
$results = [];
foreach ($testFixture->methods as $method)
{
$result = $this->runTest(function() use ($method, $instance)
{
$method->invoke($instance);
});
echo 'OK';
$result->origin = $method;
if ($resultPrinter !== null)
$resultPrinter($result);
$results []= $result;
}
$instance->teardown();
return $results;
}
protected function runTest($callback)
{
$this->resetEnvironment();
$startTime = microtime(true);
$result = new TestResult();
try
{
\Chibi\Database::rollback(function() use ($callback)
{
$callback();
});
$result->success = true;
}
catch (Exception $e)
{
$success = false;
echo 'FAIL';
}
printf(' [%.03fs]' . PHP_EOL, microtime(true) - $testStartTime);
if (isset($e))
{
echo '---' . PHP_EOL;
echo $e->getMessage() . PHP_EOL;
echo $e->getTraceAsString() . PHP_EOL;
echo '---' . PHP_EOL . PHP_EOL;
$result->exception = $e;
$result->success = false;
}
$endTime = microtime(true);
$result->duration = $endTime - $startTime;
return $result;
}
printf('%s %s... %s [%.03fs]' . PHP_EOL,
str_pad('', $pad, ' '),
str_pad('All tests', $maxLabelLength + 1, ' '),
$success ? 'OK' : 'FAIL',
microtime(true) - $startTime);
return $success;
}
function runSingle($callback)
class TestResult
{
resetEnvironment();
resetEnvironment(true);
\Chibi\Database::rollback(function() use ($callback)
{
$callback();
});
public $duration;
public $success;
public $exception;
public $origin;
}
$options = getopt('cf:', ['clean', 'filter:']);
(new TestRunner)->run($options);