diff options
Diffstat (limited to '')
-rw-r--r-- | library/Icinga/Test/BaseTestCase.php | 361 | ||||
-rw-r--r-- | library/Icinga/Test/ClassLoader.php | 113 | ||||
-rw-r--r-- | library/Icinga/Test/DbTest.php | 47 |
3 files changed, 521 insertions, 0 deletions
diff --git a/library/Icinga/Test/BaseTestCase.php b/library/Icinga/Test/BaseTestCase.php new file mode 100644 index 0000000..6702df0 --- /dev/null +++ b/library/Icinga/Test/BaseTestCase.php @@ -0,0 +1,361 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +namespace { + + if (!function_exists('t')) { + function t() + { + return func_get_arg(0); + } + } + + if (!function_exists('mt')) { + function mt() + { + return func_get_arg(0); + } + } +} + +namespace Icinga\Test { + + use Exception; + use ipl\I18n\NoopTranslator; + use ipl\I18n\StaticTranslator; + use RuntimeException; + use Mockery; + use Icinga\Application\Icinga; + use Icinga\Data\ConfigObject; + use Icinga\Data\ResourceFactory; + use Icinga\Data\Db\DbConnection; + + /** + * Class BaseTestCase + */ + abstract class BaseTestCase extends Mockery\Adapter\Phpunit\MockeryTestCase implements DbTest + { + /** + * Path to application/ + * + * @var string + */ + public static $appDir; + + /** + * Path to library/Icinga + * + * @var string + */ + public static $libDir; + + /** + * Path to etc/ + * + * @var + */ + public static $etcDir; + + /** + * Path to test/php/ + * + * @var string + */ + public static $testDir; + + /** + * Path to share/icinga2-web + * + * @var string + */ + public static $shareDir; + + /** + * Path to modules/ + * + * @var string + */ + public static $moduleDir; + + /** + * Resource configuration for different database types + * + * @var array + */ + protected static $dbConfiguration = array( + 'mysql' => array( + 'type' => 'db', + 'db' => 'mysql', + 'host' => '127.0.0.1', + 'port' => 3306, + 'dbname' => 'icinga_unittest', + 'username' => 'icinga_unittest', + 'password' => 'icinga_unittest' + ), + 'pgsql' => array( + 'type' => 'db', + 'db' => 'pgsql', + 'host' => '127.0.0.1', + 'port' => 5432, + 'dbname' => 'icinga_unittest', + 'username' => 'icinga_unittest', + 'password' => 'icinga_unittest' + ), + ); + + /** + * Setup the default timezone + */ + public static function setupTimezone() + { + date_default_timezone_set('UTC'); + } + + /** + * Setup test path environment + * + * @throws RuntimeException + */ + public static function setupDirectories() + { + $baseDir = getenv('ICINGAWEB_BASEDIR') ?: realpath(__DIR__ . '/../../../'); + if ($baseDir === false) { + throw new RuntimeException('Application base dir not found'); + } + + $libDir = getenv('ICINGAWEB_ICINGA_LIB') ?: realpath($baseDir . '/library/Icinga'); + if ($libDir === false) { + throw new RuntimeException('Icinga library dir not found'); + } + + self::$appDir = $baseDir . '/application'; + self::$libDir = $libDir; + self::$etcDir = $baseDir . '/etc'; + self::$testDir = $baseDir . '/test/php'; + self::$shareDir = $baseDir . '/share/icinga2-web'; + self::$moduleDir = getenv('ICINGAWEB_MODULES_DIR') ?: $baseDir . '/modules'; + } + + /** + * Setup MVC bootstrapping and ensure that the Icinga-Mock gets reinitialized + */ + public function setUp(): void + { + parent::setUp(); + + StaticTranslator::$instance = new NoopTranslator(); + $this->setupIcingaMock(); + } + + /** + * Setup mock object for the application's bootstrap + * + * @return Mockery\Mock + */ + protected function setupIcingaMock() + { + $requestMock = Mockery::mock('Icinga\Web\Request')->shouldDeferMissing(); + $requestMock->shouldReceive('getPathInfo')->andReturn('')->byDefault() + ->shouldReceive('getBaseUrl')->andReturn('/')->byDefault() + ->shouldReceive('getQuery')->andReturn(array())->byDefault() + ->shouldReceive('getParam')->with(Mockery::type('string'), Mockery::type('string')) + ->andReturnUsing(function ($name, $default) { + return $default; + })->byDefault(); + + $responseMock = Mockery::mock('Icinga\Web\Response')->shouldDeferMissing(); + // Can't express this as demeter chains. See: https://github.com/padraic/mockery/issues/59 + $bootstrapMock = Mockery::mock('Icinga\Application\ApplicationBootstrap')->shouldDeferMissing(); + $libDir = dirname(self::$libDir); + $bootstrapMock->shouldReceive('getFrontController')->andReturn($bootstrapMock) + ->shouldReceive('getApplicationDir')->andReturn(self::$appDir) + ->shouldReceive('getLibraryDir')->andReturnUsing(function ($subdir = null) use ($libDir) { + if ($subdir !== null) { + $libDir .= '/' . ltrim($subdir, '/'); + } + return $libDir; + }) + ->shouldReceive('getRequest')->andReturn($requestMock) + ->shouldReceive('getResponse')->andReturn($responseMock); + + Icinga::setApp($bootstrapMock, true); + return $bootstrapMock; + } + + /** + * Return the currently active request mock object + * + * @return Icinga\Web\Request + */ + public function getRequestMock() + { + return Icinga::app()->getRequest(); + } + + /** + * Return the currently active response mock object + * + * @return Icinga\Web\Response + */ + public function getResponseMock() + { + return Icinga::app()->getFrontController()->getResponse(); + } + + /** + * Create Config for database configuration + * + * @param string $name + * + * @return ConfigObject + * @throws RuntimeException + */ + protected function createDbConfigFor($name) + { + if (array_key_exists($name, self::$dbConfiguration)) { + $config = new ConfigObject(self::$dbConfiguration[$name]); + + $host = getenv(sprintf('ICINGAWEB_TEST_%s_HOST', strtoupper($name))); + if ($host) { + $config['host'] = $host; + } + + $port = getenv(sprintf('ICINGAWEB_TEST_%s_PORT', strtoupper($name))); + if ($port) { + $config['port'] = $port; + } + + return $config; + } + + throw new RuntimeException('Configuration for database type not available: ' . $name); + } + + /** + * Creates an array of Icinga\Data\Db\DbConnection + * + * @param string $name + * + * @return array + */ + protected function createDbConnectionFor($name) + { + try { + $conn = ResourceFactory::createResource($this->createDbConfigFor($name)); + } catch (Exception $e) { + $conn = $e->getMessage(); + } + + return array( + array($conn) + ); + } + + /** + * PHPUnit provider for mysql + * + * @return DbConnection + */ + public function mysqlDb() + { + return $this->createDbConnectionFor('mysql'); + } + + /** + * PHPUnit provider for pgsql + * + * @return DbConnection + */ + public function pgsqlDb() + { + return $this->createDbConnectionFor('pgsql'); + } + + /** + * PHPUnit provider for oracle + * + * @return DbConnection + */ + public function oracleDb() + { + return $this->createDbConnectionFor('oracle'); + } + + /** + * Executes sql file by using the database connection + * + * @param DbConnection $resource + * @param string $filename + * + * @throws RuntimeException + */ + public function loadSql(DbConnection $resource, $filename) + { + if (!is_file($filename)) { + throw new RuntimeException( + 'Sql file not found: ' . $filename . ' (test=' . $this->getName() . ')' + ); + } + + $sqlData = file_get_contents($filename); + + if (!$sqlData) { + throw new RuntimeException( + 'Sql file is empty: ' . $filename . ' (test=' . $this->getName() . ')' + ); + } + + $resource->getDbAdapter()->exec($sqlData); + } + + /** + * Setup provider for testcase + * + * @param string|DbConnection|null $resource + */ + public function setupDbProvider($resource) + { + if (!$resource instanceof DbConnection) { + if (is_string($resource)) { + $this->markTestSkipped('Could not initialize provider: ' . $resource); + } else { + $this->markTestSkipped('Could not initialize provider'); + } + return; + } + + $adapter = $resource->getDbAdapter(); + + try { + $adapter->getConnection(); + } catch (Exception $e) { + $this->markTestSkipped('Could not connect to provider: '. $e->getMessage()); + } + + $tables = $adapter->listTables(); + foreach ($tables as $table) { + $adapter->exec('DROP TABLE ' . $table . ';'); + } + } + + /** + * Add assertMatchesRegularExpression() method for phpunit >= 8.0 < 9.0 for compatibility with PHP 7.2. + * + * @TODO Remove once PHP 7.2 support is not needed for testing anymore. + */ + public static function assertMatchesRegularExpression( + string $pattern, + string $string, + string $message = '' + ): void { + if (method_exists(parent::class, 'assertMatchesRegularExpression')) { + parent::assertMatchesRegularExpression($pattern, $string, $message); + } else { + static::assertRegExp($pattern, $string, $message); + } + } + } + + BaseTestCase::setupTimezone(); + BaseTestCase::setupDirectories(); +} diff --git a/library/Icinga/Test/ClassLoader.php b/library/Icinga/Test/ClassLoader.php new file mode 100644 index 0000000..af90a7e --- /dev/null +++ b/library/Icinga/Test/ClassLoader.php @@ -0,0 +1,113 @@ +<?php +/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Test; + +/** + * PSR-4 class loader + */ +class ClassLoader +{ + /** + * Namespace separator + */ + const NAMESPACE_SEPARATOR = '\\'; + + /** + * Namespaces + * + * @var array + */ + private $namespaces = array(); + + /** + * Register a base directory for a namespace prefix + * + * @param string $namespace + * @param string $directory + * + * @return $this + */ + public function registerNamespace($namespace, $directory) + { + $this->namespaces[$namespace] = $directory; + + return $this; + } + + /** + * Test whether a namespace exists + * + * @param string $namespace + * + * @return bool + */ + public function hasNamespace($namespace) + { + return array_key_exists($namespace, $this->namespaces); + } + + /** + * Get the source file of the given class or interface + * + * @param string $class Name of the class or interface + * + * @return string|null + */ + public function getSourceFile($class) + { + foreach ($this->namespaces as $namespace => $dir) { + if ($class === strstr($class, $namespace)) { + $classPath = str_replace( + self::NAMESPACE_SEPARATOR, + DIRECTORY_SEPARATOR, + substr($class, strlen($namespace)) + ) . '.php'; + if (file_exists($file = $dir . $classPath)) { + return $file; + } + } + } + return null; + } + + /** + * Load the given class or interface + * + * @param string $class Name of the class or interface + * + * @return bool Whether the class or interface has been loaded + */ + public function loadClass($class) + { + if ($file = $this->getSourceFile($class)) { + require $file; + return true; + } + return false; + } + + /** + * Register {@link loadClass()} as an autoloader + */ + public function register() + { + spl_autoload_register(array($this, 'loadClass')); + } + + /** + * Unregister {@link loadClass()} as an autoloader + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Unregister this as an autoloader + */ + public function __destruct() + { + $this->unregister(); + } +} diff --git a/library/Icinga/Test/DbTest.php b/library/Icinga/Test/DbTest.php new file mode 100644 index 0000000..d1b1ff0 --- /dev/null +++ b/library/Icinga/Test/DbTest.php @@ -0,0 +1,47 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +namespace Icinga\Test; + +use Icinga\Data\Db\DbConnection; + +interface DbTest +{ + /** + * PHPUnit provider for mysql + * + * @return DbConnection + */ + public function mysqlDb(); + + /** + * PHPUnit provider for pgsql + * + * @return DbConnection + */ + public function pgsqlDb(); + + /** + * PHPUnit provider for oracle + * + * @return DbConnection + */ + public function oracleDb(); + + /** + * Executes sql file on PDO object + * + * @param DbConnection $resource + * @param string $filename + * + * @return boolean Operational success flag + */ + public function loadSql(DbConnection $resource, $filename); + + /** + * Setup provider for testcase + * + * @param string|DbConnection|null $resource + */ + public function setupDbProvider($resource); +} |