Added unit test system

This commit is contained in:
Marcin Kurczewski 2014-05-04 21:23:12 +02:00
parent b885411b2e
commit 505d08bb08
12 changed files with 490 additions and 121 deletions

@ -1 +1 @@
Subproject commit 22910a186efbcb9bc86a3ae3eb6f4aff34096406 Subproject commit cf0b4bd2253d0d3240a87ce31f77ee587b306638

View file

@ -1,13 +1,8 @@
<?php <?php
$startTime = microtime(true);
require_once '../src/core.php'; require_once '../src/core.php';
$query = rtrim($_SERVER['REQUEST_URI'], '/'); $query = rtrim($_SERVER['REQUEST_URI'], '/');
$context = getContext();
//prepare context
$context = new StdClass;
$context->startTime = $startTime;
$context->query = $query; $context->query = $query;
function renderView() function renderView()
@ -16,12 +11,6 @@ function renderView()
\Chibi\View::render($context->layoutName, $context); \Chibi\View::render($context->layoutName, $context);
} }
function getContext()
{
global $context;
return $context;
}
$context->simpleControllerName = null; $context->simpleControllerName = null;
$context->simpleActionName = null; $context->simpleActionName = null;

View file

@ -101,5 +101,3 @@ class Access
}); });
} }
} }
Access::init();

67
src/Assert.php Normal file
View file

@ -0,0 +1,67 @@
<?php
class Assert
{
public function throws($callback, $expectedMessage)
{
$success = false;
try
{
$callback();
$success = true;
}
catch (Exception $e)
{
if (stripos($e->getMessage(), $expectedMessage) === false)
$this->fail('Assertion failed. Expected: "' . $expectedMessage . '", got: "' . $e->getMessage() . '"');
}
if ($success)
$this->fail('Assertion failed. Expected exception, got nothing');
}
public function doesNotThrow($callback)
{
try
{
$ret = $callback();
}
catch (Exception $e)
{
$this->fail('Assertion failed. Expected nothing, got exception: "' . $e->getMessage() . '"');
}
return $ret;
}
public function isNotNull($actual)
{
if ($actual === null or $actual === false)
$this->fail('Assertion failed. Expected: NULL, got: "' . $actual . '"');
}
public function isTrue($actual)
{
return $this->areEqual(1, intval(boolval($actual)));
}
public function isFalse($actual)
{
return $this->areEqual(0, intval(boolval($actual)));
}
public function areEqual($expected, $actual)
{
if ($expected != $actual)
$this->fail('Assertion failed. Expected: "' . $this->dumpVar($expected) . '", got: "' . $this->dumpVar($actual) . '"');
}
public function dumpVar($var)
{
ob_start();
var_dump($var);
return trim(ob_get_clean());
}
public function fail($message)
{
throw new SimpleException($message);
}
}

View file

@ -57,5 +57,3 @@ class Logger
self::$buffer = $buffer; self::$buffer = $buffer;
} }
} }
Logger::init();

View file

@ -1,4 +1,6 @@
<?php <?php
$startTime = microtime(true);
define('SZURU_VERSION', '0.7.1'); define('SZURU_VERSION', '0.7.1');
define('SZURU_LINK', 'http://github.com/rr-/szurubooru'); define('SZURU_LINK', 'http://github.com/rr-/szurubooru');
@ -17,38 +19,60 @@ require_once $rootDir . 'lib' . DS . 'chibi-core' . DS . 'include.php';
\Chibi\AutoLoader::registerFilesystem($rootDir . 'lib' . DS . 'chibi-sql'); \Chibi\AutoLoader::registerFilesystem($rootDir . 'lib' . DS . 'chibi-sql');
\Chibi\AutoLoader::registerFilesystem(__DIR__); \Chibi\AutoLoader::registerFilesystem(__DIR__);
//load config manually
$configPaths =
[
$rootDir . DS . 'data' . DS . 'config.ini',
$rootDir . DS . 'data' . DS . 'local.ini',
];
$config = new \Chibi\Config();
foreach ($configPaths as $path)
if (file_exists($path))
$config->loadIni($path);
$config->rootDir = $rootDir;
function getConfig() function getConfig()
{ {
global $config; global $config;
return $config; return $config;
} }
function getContext()
{
global $context;
return $context;
}
//extension sanity checks function resetEnvironment()
$requiredExtensions = ['pdo', 'pdo_' . $config->main->dbDriver, 'gd', 'openssl', 'fileinfo']; {
foreach ($requiredExtensions as $ext) //load config manually
if (!extension_loaded($ext)) global $config;
die('PHP extension "' . $ext . '" must be enabled to continue.' . PHP_EOL); global $rootDir;
global $startTime;
\Chibi\Database::connect( $configPaths =
$config->main->dbDriver, [
TextHelper::absolutePath($config->main->dbLocation), $rootDir . DS . 'data' . DS . 'config.ini',
$config->main->dbUser, $rootDir . DS . 'data' . DS . 'local.ini',
$config->main->dbPass); $rootDir . DS . 'tests' . DS . 'test.ini',
];
$config = new \Chibi\Config();
foreach ($configPaths as $path)
if (file_exists($path))
$config->loadIni($path);
$config->rootDir = $rootDir;
//wire models //prepare context
foreach (\Chibi\AutoLoader::getAllIncludablePaths() as $path) global $context;
if (preg_match('/Model/', $path)) $context = new StdClass;
\Chibi\AutoLoader::safeInclude($path); $context->startTime = $startTime;
//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();
Auth::setCurrentUser(null);
Access::init();
Logger::init();
\Chibi\Database::connect(
$config->main->dbDriver,
TextHelper::absolutePath($config->main->dbLocation),
$config->main->dbUser,
$config->main->dbPass);
}
resetEnvironment();

85
src/upgrade.php Normal file
View file

@ -0,0 +1,85 @@
<?php
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];
}
function upgradeDatabase()
{
$config = 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) = 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);
}

1
tests/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
db.sqlite

10
tests/AbstractTest.php Normal file
View file

@ -0,0 +1,10 @@
<?php
class AbstractTest
{
public $assert;
public function __construct()
{
$this->assert = new Assert();
}
}

138
tests/BasicAuthTest.php Normal file
View file

@ -0,0 +1,138 @@
<?php
class BasicAuthTest extends AbstractTest
{
public function testValidPassword()
{
$user = $this->prepareValidUser();
UserModel::save($user);
$this->assert->doesNotThrow(function()
{
Auth::login('existing', 'ble', false);
});
}
public function testLogout()
{
$this->assert->isFalse(Auth::isLoggedIn());
$this->testValidPassword();
$this->assert->isTrue(Auth::isLoggedIn());
Auth::setCurrentUser(null);
$this->assert->isFalse(Auth::isLoggedIn());
}
public function testInvalidUser()
{
$this->assert->throws(function()
{
Auth::login('non-existing', 'wrong-password', false);
}, 'invalid username');
}
public function testInvalidPassword()
{
$user = $this->prepareValidUser();
$user->passHash = UserModel::hashPassword('ble2', $user->passSalt);
UserModel::save($user);
$this->assert->throws(function()
{
Auth::login('existing', 'wrong-password', false);
}, 'invalid password');
}
public function testBanned()
{
$user = $this->prepareValidUser();
$user->ban();
UserModel::save($user);
$this->assert->throws(function()
{
Auth::login('existing', 'ble', false);
}, 'You are banned');
}
public function testStaffConfirmationEnabled()
{
getConfig()->registration->staffActivation = true;
$user = $this->prepareValidUser();
$user->staffConfirmed = false;
UserModel::save($user);
$this->assert->throws(function()
{
Auth::login('existing', 'ble', false);
}, 'staff hasn\'t confirmed');
}
public function testStaffConfirmationDisabled()
{
getConfig()->registration->staffActivation = false;
$user = $this->prepareValidUser();
$user->staffConfirmed = false;
UserModel::save($user);
$this->assert->doesNotThrow(function()
{
Auth::login('existing', 'ble', false);
});
}
public function testMailConfirmationEnabledFail1()
{
getConfig()->registration->needEmailForRegistering = true;
$user = $this->prepareValidUser();
$user->staffConfirmed = false;
UserModel::save($user);
$this->assert->throws(function()
{
Auth::login('existing', 'ble', false);
}, 'need e-mail address confirmation');
}
public function testMailConfirmationEnabledFail2()
{
getConfig()->registration->needEmailForRegistering = true;
$user = $this->prepareValidUser();
$user->staffConfirmed = false;
$user->emailUnconfirmed = 'test@example.com';
UserModel::save($user);
$this->assert->throws(function()
{
Auth::login('existing', 'ble', false);
}, 'need e-mail address confirmation');
}
public function testMailConfirmationEnabledPass()
{
getConfig()->registration->needEmailForRegistering = true;
$user = $this->prepareValidUser();
$user->staffConfirmed = false;
$user->emailConfirmed = 'test@example.com';
UserModel::save($user);
$this->assert->doesNotThrow(function()
{
Auth::login('existing', 'ble', false);
});
}
protected function prepareValidUser()
{
$user = UserModel::spawn();
$user->setAccessRank(new AccessRank(AccessRank::Registered));
$user->setName('existing');
$user->passHash = UserModel::hashPassword('ble', $user->passSalt);
return $user;
}
}

135
tests/run-all.php Normal file
View file

@ -0,0 +1,135 @@
<?php
$configPath = __DIR__ . '/test.ini';
if (isset($_SERVER['argv']))
$args = $_SERVER['argv'];
else
$args = [];
try
{
$dbPath = __DIR__ . '/db.sqlite';
if (file_exists($dbPath) and in_array('-c', $args))
unlink($dbPath);
$configIni =
[
'[main]',
'dbDriver = "sqlite"',
'dbLocation = "' . $dbPath . '"',
'filesPath = "' . __DIR__ . 'files',
'thumbsPath = "' . __DIR__ . 'thumbs',
'logsPath = "/dev/null"',
'[registration]',
'needEmailForRegistering = 0',
'needEmailForCommenting = 0',
'needEmailForUploading = 0'
];
file_put_contents($configPath, implode(PHP_EOL, $configIni));
require_once __DIR__ . '/../src/core.php';
require_once __DIR__ . '/../src/upgrade.php';
upgradeDatabase();
runAll();
}
finally
{
unlink($configPath);
}
function getTestMethods()
{
//get all test methods
$testClasses = \Chibi\Util\Reflection::loadClasses(glob(__DIR__ . '/*Test.php'));
$testMethods = [];
foreach ($testClasses as $class)
{
$reflectionClass = new ReflectionClass($class);
foreach ($reflectionClass->getMethods() as $method)
{
if (preg_match('/test/i', $method->name))
{
$testMethods []= $method;
}
}
}
return $testMethods;
}
function runAll()
{
$startTime = microtime(true);
$testMethods = getTestMethods();
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 = max(array_map('strlen', $labels));
foreach ($labels as &$label)
$label = str_pad($label, $maxLabelLength + 1, ' ');
$pad = count($testMethods) ? ceil(log10(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)
{
$method->invoke($instance);
});
echo 'OK';
}
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;
}
}
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)
{
resetEnvironment();
\Chibi\Database::rollback(function() use ($callback)
{
$callback();
});
}

View file

@ -1,80 +1,4 @@
<?php <?php
require_once 'src/core.php'; require_once 'src/core.php';
$config = getConfig(); require_once 'src/upgrade.php';
upgradeDatabase();
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];
}
$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) = 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);