From 22b18bfbc9140027e3adb00e65f0931226141ae9 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Thu, 15 May 2014 09:39:48 +0200 Subject: [PATCH] Refactored TestRunner and core --- src/core.php | 230 +++++++++++++----- src/upgrade.php | 85 ------- tests/ITestRunner.php | 10 + tests/TestResult.php | 8 + tests/TestRunner.php | 200 +++++++++++++++ tests/Tests/MiscTests/TextHelperTest.php | 2 +- tests/run-all.php | 295 +++++------------------ upgrade.php | 3 +- 8 files changed, 450 insertions(+), 383 deletions(-) delete mode 100644 src/upgrade.php create mode 100644 tests/ITestRunner.php create mode 100644 tests/TestResult.php create mode 100644 tests/TestRunner.php diff --git a/src/core.php b/src/core.php index 9abeda3c..8caed07a 100644 --- a/src/core.php +++ b/src/core.php @@ -19,77 +19,175 @@ require_once $rootDir . 'lib' . DS . 'chibi-core' . DS . 'include.php'; require_once $rootDir . 'src' . DS . 'routes.php'; +final class Core +{ + private static $context; + private static $config; + + static function getConfig() + { + return self::$config; + } + + static function getContext() + { + return self::$context; + } + + static function prepareConfig($testEnvironment) + { + //load config manually + global $config; + global $rootDir; + + $configPaths = []; + if (!$testEnvironment) + { + $configPaths []= $rootDir . DS . 'data' . DS . 'config.ini'; + $configPaths []= $rootDir . DS . 'data' . DS . 'local.ini'; + } + else + { + $configPaths []= $rootDir . DS . 'tests' . DS . 'config.ini'; + } + + self::$config = new \Chibi\Config(); + foreach ($configPaths as $path) + if (file_exists($path)) + self::$config->loadIni($path); + self::$config->rootDir = $rootDir; + } + + static function prepareEnvironment($testEnvironment) + { + //prepare context + global $startTime; + self::$context = new StdClass; + self::$context->startTime = $startTime; + + $config = self::getConfig(); + + TransferHelper::createDirectory($config->main->filesPath); + TransferHelper::createDirectory($config->main->thumbsPath); + + //extension sanity checks + $requiredExtensions = ['pdo', 'pdo_' . $config->main->dbDriver, 'gd', 'openssl', 'fileinfo']; + foreach ($requiredExtensions as $ext) + if (!extension_loaded($ext)) + die('PHP extension "' . $ext . '" must be enabled to continue.' . PHP_EOL); + + if (\Chibi\Database::connected()) + \Chibi\Database::disconnect(); + + if ($testEnvironment) + Auth::setCurrentUser(null); + Access::init(); + Logger::init(); + Mailer::init(); + PropertyModel::init(); + + \Chibi\Database::connect( + $config->main->dbDriver, + TextHelper::absolutePath($config->main->dbLocation), + isset($config->main->dbUser) ? $config->main->dbUser : null, + isset($config->main->dbPass) ? $config->main->dbPass : null); + } + + static function getDbVersion() + { + try + { + $dbVersion = PropertyModel::get(PropertyModel::DbVersion); + } + catch (Exception $e) + { + return [null, null]; + } + if (strpos($dbVersion, '.') !== false) + { + list ($dbVersionMajor, $dbVersionMinor) = explode('.', $dbVersion); + } + elseif ($dbVersion) + { + $dbVersionMajor = $dbVersion; + $dbVersionMinor = null; + } + else + { + $dbVersionMajor = 0; + $dbVersionMinor = 0; + } + return [$dbVersionMajor, $dbVersionMinor]; + } + + static function upgradeDatabase() + { + $config = self::getConfig(); + $upgradesPath = TextHelper::absolutePath($config->rootDir + . DS . 'src' . DS . 'Upgrades' . DS . $config->main->dbDriver); + + $upgrades = glob($upgradesPath . DS . '*.sql'); + natcasesort($upgrades); + + foreach ($upgrades as $upgradePath) + { + preg_match('/(\d+)\.sql/', $upgradePath, $matches); + $upgradeVersionMajor = intval($matches[1]); + + list ($dbVersionMajor, $dbVersionMinor) = self::getDbVersion(); + + if (($upgradeVersionMajor > $dbVersionMajor) + or ($upgradeVersionMajor == $dbVersionMajor and $dbVersionMinor !== null)) + { + printf('%s: executing' . PHP_EOL, $upgradePath); + $upgradeSql = file_get_contents($upgradePath); + $upgradeSql = preg_replace('/^[ \t]+(.*);/m', '\0--', $upgradeSql); + $queries = preg_split('/;\s*[\r\n]+/s', $upgradeSql); + $queries = array_map('trim', $queries); + $queries = array_filter($queries); + $upgradeVersionMinor = 0; + foreach ($queries as $query) + { + $query = preg_replace('/\s*--(.*?)$/m', '', $query); + ++ $upgradeVersionMinor; + if ($upgradeVersionMinor > $dbVersionMinor) + { + try + { + \Chibi\Database::execUnprepared(new \Chibi\Sql\RawStatement($query)); + } + catch (Exception $e) + { + echo $e . PHP_EOL; + echo $query . PHP_EOL; + die; + } + PropertyModel::set(PropertyModel::DbVersion, $upgradeVersionMajor . '.' . $upgradeVersionMinor); + } + } + PropertyModel::set(PropertyModel::DbVersion, $upgradeVersionMajor); + } + else + { + printf('%s: no need to execute' . PHP_EOL, $upgradePath); + } + } + + list ($dbVersionMajor, $dbVersionMinor) = self::getDbVersion(); + printf('Database version: %d.%d' . PHP_EOL, $dbVersionMajor, $dbVersionMinor); + } +} + +//legacy function getConfig() { - global $config; - return $config; + return Core::getConfig(); } function getContext() { - global $context; - return $context; + return Core::getContext(); } -function prepareConfig($testEnvironment) -{ - //load config manually - global $config; - global $rootDir; - - $configPaths = []; - if (!$testEnvironment) - { - $configPaths []= $rootDir . DS . 'data' . DS . 'config.ini'; - $configPaths []= $rootDir . DS . 'data' . DS . 'local.ini'; - } - else - { - $configPaths []= $rootDir . DS . 'tests' . DS . 'config.ini'; - } - - $config = new \Chibi\Config(); - foreach ($configPaths as $path) - if (file_exists($path)) - $config->loadIni($path); - $config->rootDir = $rootDir; -} - -function prepareEnvironment($testEnvironment) -{ - //prepare context - global $context; - global $startTime; - $context = new StdClass; - $context->startTime = $startTime; - - $config = getConfig(); - - TransferHelper::createDirectory($config->main->filesPath); - TransferHelper::createDirectory($config->main->thumbsPath); - - //extension sanity checks - $requiredExtensions = ['pdo', 'pdo_' . $config->main->dbDriver, 'gd', 'openssl', 'fileinfo']; - foreach ($requiredExtensions as $ext) - if (!extension_loaded($ext)) - die('PHP extension "' . $ext . '" must be enabled to continue.' . PHP_EOL); - - if (\Chibi\Database::connected()) - \Chibi\Database::disconnect(); - - if ($testEnvironment) - Auth::setCurrentUser(null); - Access::init(); - Logger::init(); - Mailer::init(); - PropertyModel::init(); - - \Chibi\Database::connect( - $config->main->dbDriver, - TextHelper::absolutePath($config->main->dbLocation), - isset($config->main->dbUser) ? $config->main->dbUser : null, - isset($config->main->dbPass) ? $config->main->dbPass : null); -} - -prepareConfig(false); -prepareEnvironment(false); +Core::prepareConfig(false); +Core::prepareEnvironment(false); diff --git a/src/upgrade.php b/src/upgrade.php deleted file mode 100644 index 24e6bd38..00000000 --- a/src/upgrade.php +++ /dev/null @@ -1,85 +0,0 @@ -rootDir - . DS . 'src' . DS . 'Upgrades' . DS . $config->main->dbDriver); - - $upgrades = glob($upgradesPath . DS . '*.sql'); - natcasesort($upgrades); - - foreach ($upgrades as $upgradePath) - { - preg_match('/(\d+)\.sql/', $upgradePath, $matches); - $upgradeVersionMajor = intval($matches[1]); - - list ($dbVersionMajor, $dbVersionMinor) = getDbVersion(); - - if (($upgradeVersionMajor > $dbVersionMajor) - or ($upgradeVersionMajor == $dbVersionMajor and $dbVersionMinor !== null)) - { - printf('%s: executing' . PHP_EOL, $upgradePath); - $upgradeSql = file_get_contents($upgradePath); - $upgradeSql = preg_replace('/^[ \t]+(.*);/m', '\0--', $upgradeSql); - $queries = preg_split('/;\s*[\r\n]+/s', $upgradeSql); - $queries = array_map('trim', $queries); - $queries = array_filter($queries); - $upgradeVersionMinor = 0; - foreach ($queries as $query) - { - $query = preg_replace('/\s*--(.*?)$/m', '', $query); - ++ $upgradeVersionMinor; - if ($upgradeVersionMinor > $dbVersionMinor) - { - try - { - \Chibi\Database::execUnprepared(new \Chibi\Sql\RawStatement($query)); - } - catch (Exception $e) - { - echo $e . PHP_EOL; - echo $query . PHP_EOL; - die; - } - PropertyModel::set(PropertyModel::DbVersion, $upgradeVersionMajor . '.' . $upgradeVersionMinor); - } - } - PropertyModel::set(PropertyModel::DbVersion, $upgradeVersionMajor); - } - else - { - printf('%s: no need to execute' . PHP_EOL, $upgradePath); - } - } - - list ($dbVersionMajor, $dbVersionMinor) = getDbVersion(); - printf('Database version: %d.%d' . PHP_EOL, $dbVersionMajor, $dbVersionMinor); -} - diff --git a/tests/ITestRunner.php b/tests/ITestRunner.php new file mode 100644 index 00000000..9709ac31 --- /dev/null +++ b/tests/ITestRunner.php @@ -0,0 +1,10 @@ +filter = $filter; + } + + public function setTestsPath($testsPath) + { + $this->testsPath = $testsPath; + } + + public function setEnvironmentPrepareAction($callback) + { + $this->environmentPrepareAction = $callback; + } + + public function setEnvironmentCleanAction($callback) + { + $this->environmentCleanAction = $callback; + } + + public function setTestWrapperAction($callback) + { + $this->testWrapperAction = $callback; + } + + public function run() + { + $testFixtures = $this->getTestFixtures($this->filter); + $this->runAll($testFixtures); + } + + protected function getTestFixtures($filter) + { + $testFiles = []; + $path = $this->testsPath; + if (!$path) + $path = __DIR__; + foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $fileName) + { + $path = $fileName->getPathname(); + if (preg_match('/.*Test.php$/', $path)) + $testFiles []= $path; + } + + $testClasses = \Chibi\Util\Reflection::loadClasses($testFiles); + + $classFilter = $filter; + $methodFilter = null; + if ($filter !== null and strpos($filter, '::') !== false) + { + list ($classFilter, $methodFilter) = explode('::', $filter); + } + + if ($classFilter) + $testClasses = array_filter($testClasses, function($className) use ($classFilter) + { + return stripos($className, $classFilter) !== 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 ($methodFilter and stripos($method->name, $methodFilter) === false) + continue; + + if (preg_match('/test/i', $method->name) + and $method->isPublic() + and $method->getNumberOfParameters() == 0) + { + $testFixture->methods []= $method; + } + } + + if (!empty($testFixture->methods)) + $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); + }); + $result->origin = $method; + + if ($resultPrinter !== null) + $resultPrinter($result); + + $results []= $result; + } + $instance->teardown(); + return $results; + } + + protected function runTest($callback) + { + if ($this->environmentPrepareAction !== null) + call_user_func($this->environmentPrepareAction); + + $startTime = microtime(true); + $result = new TestResult(); + + try + { + if ($this->testWrapperAction !== null) + call_user_func($this->testWrapperAction, $callback); + else + $callback(); + $result->success = true; + } + catch (Exception $e) + { + $result->exception = $e; + $result->success = false; + } + finally + { + if ($this->environmentCleanAction !== null) + call_user_func($this->environmentCleanAction); + } + + $endTime = microtime(true); + $result->duration = $endTime - $startTime; + return $result; + } +} diff --git a/tests/Tests/MiscTests/TextHelperTest.php b/tests/Tests/MiscTests/TextHelperTest.php index 492f0f34..b537ae0c 100644 --- a/tests/Tests/MiscTests/TextHelperTest.php +++ b/tests/Tests/MiscTests/TextHelperTest.php @@ -17,7 +17,7 @@ class TextHelperTest extends AbstractTest } } - public function testToIntegerOrNulll() + public function testToIntegerOrNull() { $this->assert->areEqual(1, TextHelper::toIntegerOrNull(1)); $this->assert->areEqual(1, TextHelper::toIntegerOrNull('1')); diff --git a/tests/run-all.php b/tests/run-all.php index 8a7d7667..5f96b9a0 100644 --- a/tests/run-all.php +++ b/tests/run-all.php @@ -1,247 +1,84 @@ main->filesPath), + realpath(Core::getConfig()->main->thumbsPath), + realpath(dirname(Core::getConfig()->main->logsPath)), + ]; + + foreach ($folders as $folder) { - $this->dbPath = __DIR__ . '/db.sqlite'; - } + if (!file_exists($folder)) + continue; - public function run($options) - { - $cleanDatabase = (isset($options['c']) or isset($options['clean'])); + $it = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator( + $folder, + FilesystemIterator::SKIP_DOTS), + RecursiveIteratorIterator::CHILD_FIRST); - if (isset($options['f'])) - $filter = $options['f']; - elseif (isset($options['filter'])) - $filter = $options['filter']; - else - $filter = null; - - if ($cleanDatabase) - $this->cleanDatabase(); - - try + foreach ($it as $path) { - $this->resetEnvironment(); - upgradeDatabase(); - - $testFixtures = $this->getTestFixtures($filter); - $this->runAll($testFixtures); + $path->isFile() + ? unlink($path->getPathname()) + : rmdir($path->getPathname()); } - finally - { - $this->removeTestFolders(); - } - } - - protected function cleanDatabase() - { - if (file_exists($this->dbPath)) - unlink($this->dbPath); - } - - protected function resetEnvironment() - { - $_SESSION = []; - 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); - - $classFilter = $filter; - $methodFilter = null; - if ($filter !== null and strpos($filter, '::') !== false) - { - list ($classFilter, $methodFilter) = explode('::', $filter); - } - - if ($classFilter) - $testClasses = array_filter($testClasses, function($className) use ($classFilter) - { - return stripos($className, $classFilter) !== 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 ($methodFilter and stripos($method->name, $methodFilter) === false) - continue; - - if (preg_match('/test/i', $method->name) - and $method->isPublic() - and $method->getNumberOfParameters() == 0) - { - $testFixture->methods []= $method; - } - } - - if (!empty($testFixture->methods)) - $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); - }); - $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) - { - $result->exception = $e; - $result->success = false; - } - $endTime = microtime(true); - $result->duration = $endTime - $startTime; - return $result; + rmdir($folder); } } -class TestResult +function resetEnvironment() { - public $duration; - public $success; - public $exception; - public $origin; + global $dbPath; + + $_SESSION = []; + Core::prepareConfig(true); + Core::getConfig()->main->dbDriver = 'sqlite'; + Core::getConfig()->main->dbLocation = $dbPath; + removeTestFolders(); + Core::prepareEnvironment(true); } $options = getopt('cf:', ['clean', 'filter:']); -(new TestRunner)->run($options); +$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; + +resetEnvironment(); +if ($cleanDatabase) + cleanDatabase(); +resetEnvironment(); + +Core::upgradeDatabase(); + +$testRunner = new TestRunner; +$testRunner->setFilter($filter); +$testRunner->setEnvironmentPrepareAction(function() { resetEnvironment(); }); +$testRunner->setEnvironmentCleanAction(function() { removeTestFolders(); }); +$testRunner->setTestWrapperAction(function($callback) + { + \Chibi\Database::rollback(function() use ($callback) + { + $callback(); + }); + }); +$testRunner->run($filter); diff --git a/upgrade.php b/upgrade.php index 0776cdb3..c12d26d9 100644 --- a/upgrade.php +++ b/upgrade.php @@ -1,4 +1,3 @@