From 8db04d5dbc71180908279b3b701b770e5e5eb1a5 Mon Sep 17 00:00:00 2001 From: Gunnar Wrobel Date: Mon, 28 Jun 2010 09:05:00 +0200 Subject: [PATCH] Initial implementation of the Kolab_Config package. --- framework/Kolab_Config/lib/Horde/Kolab/Config.php | 214 +++++++++++++++++++++ .../lib/Horde/Kolab/Config/Exception.php | 31 +++ framework/Kolab_Config/package.xml | 32 ++- .../test/Horde/Kolab/Config/AllTests.php | 47 +++++ .../test/Horde/Kolab/Config/Autoload.php | 21 +- .../Horde/Kolab/Config/ConfigStoryTestCase.php | 133 +++++++++++++ .../Horde/Kolab/Config/Integration/ConfigTest.php | 107 +++++++++-- .../test/Horde/Kolab/Config/conf.php.dist | 2 + .../Kolab/Config/fixture/empty/no_config_file | 1 + .../Horde/Kolab/Config/fixture/global/globals.conf | 1 + .../Horde/Kolab/Config/fixture/local/kolab.conf | 2 + .../Horde/Kolab/Config/fixture/local/kolab.globals | 3 + 12 files changed, 568 insertions(+), 26 deletions(-) create mode 100644 framework/Kolab_Config/lib/Horde/Kolab/Config.php create mode 100644 framework/Kolab_Config/lib/Horde/Kolab/Config/Exception.php create mode 100644 framework/Kolab_Config/test/Horde/Kolab/Config/ConfigStoryTestCase.php create mode 100644 framework/Kolab_Config/test/Horde/Kolab/Config/conf.php.dist create mode 100644 framework/Kolab_Config/test/Horde/Kolab/Config/fixture/empty/no_config_file create mode 100644 framework/Kolab_Config/test/Horde/Kolab/Config/fixture/global/globals.conf create mode 100644 framework/Kolab_Config/test/Horde/Kolab/Config/fixture/local/kolab.conf create mode 100644 framework/Kolab_Config/test/Horde/Kolab/Config/fixture/local/kolab.globals diff --git a/framework/Kolab_Config/lib/Horde/Kolab/Config.php b/framework/Kolab_Config/lib/Horde/Kolab/Config.php new file mode 100644 index 000000000..85c75fb1c --- /dev/null +++ b/framework/Kolab_Config/lib/Horde/Kolab/Config.php @@ -0,0 +1,214 @@ + + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Config + */ + +/** + * The Kolab Server configuration handler. + * + * Copyright 2010 Klarälvdalens Datakonsult AB + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @category Kolab + * @package Kolab_Config + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Config + */ +class Horde_Kolab_Config +implements ArrayAccess +{ + /** + * The path to the directory holding the configuration files. + * + * @var string + */ + private $_directory; + + /** + * Name of the global configuration file. + * + * @var string + */ + private $_global; + + /** + * Name of the local configuration file. + * + * @var string + */ + private $_local; + + /** + * The configuration data. + * + * @var array + */ + private $_data; + + /** + * Constructor. + * + * @param string $directory The path to the directory holding the + * configuration files. + * @param string $global Name of the global configuration file. + * @param string $local Name of the local configuration file. + */ + public function __construct( + $directory = '@kolab_server_dir@/etc/kolab', + $global = 'kolab.globals', + $local = 'kolab.conf' + ) { + if ($directory === '@kolab_server_dir@/etc/kolab') { + $this->_directory = '/kolab/etc/kolab'; + } else { + $this->_directory = realpath($directory); + } + $this->_global = $global; + $this->_local = $local; + } + + /** + * Read the configuration files. + * + * @return NULL + */ + public function read() + { + if (!file_exists($this->_getGlobalConfigFilePath()) + && !file_exists($this->_getLocalConfigFilePath())) { + throw new Horde_Kolab_Config_Exception( + 'No configuration files found in ' . $this->_directory . '.' + ); + } + + if (file_exists($this->_getGlobalConfigFilePath())) { + $this->_loadConfigurationFile( + $this->_getGlobalConfigFilePath() + ); + } + + if (file_exists($this->_getLocalConfigFilePath())) { + $this->_loadConfigurationFile( + $this->_getLocalConfigFilePath() + ); + } + } + + private function _loadConfigurationFile($path) + { + if ($this->_data === null) { + $this->_data = array(); + } + + $fh = fopen($path, 'r'); + + while (!feof($fh)) { + $line = trim(fgets($fh)); + if ($line && preg_match('/^([^:#]+):(.*)/', $line, $matches)) { + $this->_data[trim($matches[1])] = trim($matches[2]); + } + } + fclose($fh); + } + + /** + * Return the path to the global configuration file. + * + * @return NULL + */ + private function _getGlobalConfigFilePath() + { + return $this->_directory . DIRECTORY_SEPARATOR . $this->_global; + } + + /** + * Return the path to the local configuration file. + * + * @return NULL + */ + private function _getLocalConfigFilePath() + { + return $this->_directory . DIRECTORY_SEPARATOR . $this->_local; + } + + /** + * Initialize this object if this has not happened yet. + * + * @return NULL + */ + private function _init() + { + if ($this->_data === null) { + $this->read(); + } + } + + /** + * Return the value for the given array key. + * + * @param string $key The key. + * + * @return mixed The value for the given key. + */ + public function offsetGet($key) + { + if (!$this->offsetExists($key)) { + throw new Horde_Kolab_Config_Exception( + 'Parameter "' . $key . '" has no value!' + ); + } + return $this->_data[$key]; + } + + /** + * Does the requested array value exist in the configuration? + * + * @param string $key The key. + * + * @return boolean True if the configuration value exists. + */ + public function offsetExists($key) + { + $this->_init(); + if (!is_string($key) || empty($key)) { + throw new InvalidArgumentException( + 'The key must be a non-empty string!' + ); + } + return isset($this->_data[$key]); + } + + /** + * Set the given key to the provided value. + * + * @param string $key The key. + * @param mixed $value The value that should be stored. + * + * @return NULL + */ + public function offsetSet($key, $value) + { + } + + /** + * Delete the value identified by the given key. + * + * @param string $key The key. + * + * @return NULL + */ + public function offsetUnset($key) + { + } +} \ No newline at end of file diff --git a/framework/Kolab_Config/lib/Horde/Kolab/Config/Exception.php b/framework/Kolab_Config/lib/Horde/Kolab/Config/Exception.php new file mode 100644 index 000000000..8847f4d34 --- /dev/null +++ b/framework/Kolab_Config/lib/Horde/Kolab/Config/Exception.php @@ -0,0 +1,31 @@ + + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Config + */ + +/** + * This class provides the standard error class for the Kolab_Config package. + * + * Copyright 2010 Klarälvdalens Datakonsult AB + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @category Kolab + * @package Kolab_Config + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Config + */ +class Horde_Kolab_Config_Exception +extends Exception +{ +} diff --git a/framework/Kolab_Config/package.xml b/framework/Kolab_Config/package.xml index 408f33266..4ca386141 100644 --- a/framework/Kolab_Config/package.xml +++ b/framework/Kolab_Config/package.xml @@ -25,7 +25,7 @@ yes 2010-06-27 - + 0.1.0 0.1.0 @@ -40,20 +40,36 @@ + + + + + + + + + + + + + + + - + + - - - - + + + + @@ -69,9 +85,13 @@ + + + + diff --git a/framework/Kolab_Config/test/Horde/Kolab/Config/AllTests.php b/framework/Kolab_Config/test/Horde/Kolab/Config/AllTests.php index 231c1b853..92c822590 100644 --- a/framework/Kolab_Config/test/Horde/Kolab/Config/AllTests.php +++ b/framework/Kolab_Config/test/Horde/Kolab/Config/AllTests.php @@ -41,6 +41,53 @@ require_once 'Horde/Test/AllTests.php'; */ class Horde_Kolab_Config_AllTests extends Horde_Test_AllTests { + /** + * Main entry point for running the suite. + */ + public static function main($package = null, $file = null) + { + if ($package) { + self::$_package = $package; + } + if ($file) { + self::$_file = $file; + } + + PHPUnit_TextUI_TestRunner::run(self::suite()); + } + + /** + * Collect the unit tests of this directory into a new suite. + * + * @return PHPUnit_Framework_TestSuite The test suite. + */ + public static function suite() + { + return self::detectTestFixture(Horde_Test_AllTests::suite()); + } + + /** + * Detect if test configuration is available for the server integration + * tests. + * + * @param PHPUnit_Framework_TestSuite $suite The current test suite. + */ + public static function detectTestFixture(PHPUnit_Framework_TestSuite $suite) + { + $config = getenv('KOLAB_CONFIG_TEST_CONFIG'); + if ($config === false) { + $config = dirname(__FILE__) . '/conf.php'; + } + if (file_exists($config)) { + require $config; + if (isset($conf['kolab']['config']['test'])) { + $fixture = new stdClass; + $fixture->conf = $conf['kolab']['config']['test']; + $suite->setSharedFixture($fixture); + } + } + return $suite; + } } Horde_Kolab_Config_AllTests::init('Horde_Kolab_Config', __FILE__); diff --git a/framework/Kolab_Config/test/Horde/Kolab/Config/Autoload.php b/framework/Kolab_Config/test/Horde/Kolab/Config/Autoload.php index a82afdaff..0e91eaa6b 100644 --- a/framework/Kolab_Config/test/Horde/Kolab/Config/Autoload.php +++ b/framework/Kolab_Config/test/Horde/Kolab/Config/Autoload.php @@ -1,15 +1,20 @@ - - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Config + * Copyright 2009-2010 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @category Kolab + * @package Kolab_Config + * @subpackage UnitTests + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Config */ if (!spl_autoload_functions()) { @@ -27,3 +32,7 @@ if (!spl_autoload_functions()) { /** Catch strict standards */ error_reporting(E_ALL | E_STRICT); + + +/** Load the basic test definition */ +require_once dirname(__FILE__) . '/ConfigStoryTestCase.php'; diff --git a/framework/Kolab_Config/test/Horde/Kolab/Config/ConfigStoryTestCase.php b/framework/Kolab_Config/test/Horde/Kolab/Config/ConfigStoryTestCase.php new file mode 100644 index 000000000..6fc7150cd --- /dev/null +++ b/framework/Kolab_Config/test/Horde/Kolab/Config/ConfigStoryTestCase.php @@ -0,0 +1,133 @@ + + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Config + */ + +/** + * Base for session testing. + * + * Copyright 2010 Klarälvdalens Datakonsult AB + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @category Kolab + * @package Kolab_Config + * @subpackage UnitTests + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Config + */ +class Horde_Kolab_Config_ConfigStoryTestCase +extends PHPUnit_Extensions_Story_TestCase +{ + /** + * Handle a "given" step. + * + * @param array &$world Joined "world" of variables. + * @param string $action The description of the step. + * @param array $arguments Additional arguments to the step. + * + * @return mixed The outcome of the step. + */ + public function runGiven(&$world, $action, $arguments) + { + switch($action) { + case 'that no Kolab server configuration file can be found': + $world['config'] = new Horde_Kolab_Config( + dirname(__FILE__) . '/fixture/empty' + ); + break; + case 'that a global configuration file was specified as a combination of a directory path and a file name': + $world['config'] = new Horde_Kolab_Config( + dirname(__FILE__) . '/fixture/global', + 'globals.conf' + ); + break; + case 'that the location of the configuration files were specified with a directory path': + $world['config'] = new Horde_Kolab_Config( + dirname(__FILE__) . '/fixture/local' + ); + break; + default: + return $this->notImplemented($action); + } + } + + /** + * Handle a "when" step. + * + * @param array &$world Joined "world" of variables. + * @param string $action The description of the step. + * @param array $arguments Additional arguments to the step. + * + * @return mixed The outcome of the step. + */ + public function runWhen(&$world, $action, $arguments) + { + switch($action) { + case 'reading the configuration': + try { + $world['config']->read(); + } catch (Horde_Kolab_Config_Exception $e) { + $world['result'] = $e; + } + break; + case 'reading the parameter': + try { + $world['result'] = $world['config'][$arguments[0]]; + } catch (Exception $e) { + $world['result'] = $e; + } + break; + default: + return $this->notImplemented($action); + } + } + + /** + * Handle a "then" step. + * + * @param array &$world Joined "world" of variables. + * @param string $action The description of the step. + * @param array $arguments Additional arguments to the step. + * + * @return mixed The outcome of the step. + */ + public function runThen(&$world, $action, $arguments) + { + switch($action) { + case 'the Config Object will throw an exception of type': + $this->assertType( + $arguments[0], $world['result'] + ); + break; + case 'the exception has the message': + $this->assertEquals( + $arguments[0], $world['result']->getMessage() + ); + break; + case 'the result will be': + if ($world['result'] instanceOf Exception) { + $this->assertEquals( + '', $world['result']->getMessage() + ); + } else { + $this->assertEquals($arguments[0], $world['result']); + } + break; + default: + return $this->notImplemented($action); + } + } + +} \ No newline at end of file diff --git a/framework/Kolab_Config/test/Horde/Kolab/Config/Integration/ConfigTest.php b/framework/Kolab_Config/test/Horde/Kolab/Config/Integration/ConfigTest.php index a57e334ed..01d81b140 100644 --- a/framework/Kolab_Config/test/Horde/Kolab/Config/Integration/ConfigTest.php +++ b/framework/Kolab_Config/test/Horde/Kolab/Config/Integration/ConfigTest.php @@ -4,11 +4,12 @@ * * PHP version 5 * - * @category Kolab - * @package Kolab_Config - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Config + * @category Kolab + * @package Kolab_Config + * @subpackage UnitTests + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Config */ /** @@ -17,24 +18,102 @@ require_once dirname(__FILE__) . '/../Autoload.php'; /** - * Test the Kolab session handler base implementation. + * Test the Kolab configuration handler. * - * Copyright 2009-2010 The Horde Project (http://www.horde.org/) + * Copyright 2010 Klarälvdalens Datakonsult AB * * See the enclosed file COPYING for license information (LGPL). If you * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. * - * @category Kolab - * @package Kolab_Config - * @author Gunnar Wrobel - * @license http://www.fsf.org/copyleft/lgpl.html LGPL - * @link http://pear.horde.org/index.php?package=Kolab_Config + * @category Kolab + * @package Kolab_Config + * @subpackage UnitTests + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=Kolab_Config */ class Horde_Kolab_Config_Integration_ConfigTest -extends PHPUnit_Framework_TestCase +extends Horde_Kolab_Config_ConfigStoryTestCase { + /** + * @scenario + */ + public function aMissingGlobalConfigurationFileThrowsAnException() + { + $this->given('that no Kolab server configuration file can be found') + ->when('reading the configuration') + ->then('the Config Object will throw an exception of type', 'Horde_Kolab_Config_Exception') + ->and('the exception has the message', + 'No configuration files found in ' + . realpath(dirname(__FILE__) . '/../fixture/empty') . '.' + ); + } + + /** + * @scenario + */ + public function theNameOfTheGlobalConfigurationDirectoryAndFileCanBeSpecified() + { + $this->given('that a global configuration file was specified as a combination of a directory path and a file name') + ->when('reading the parameter', 'global') + ->then('the result will be', 'global'); + } + + /** + * @scenario + */ + public function theNameOfTheConfigurationDirectoryCanBeSpecified() + { + $this->given('that the location of the configuration files were specified with a directory path') + ->when('reading the parameter', 'local') + ->then('the result will be', 'local'); + } + + /** + * @scenario + */ + public function readingAConfigurationYieldsTheGlobalConfigurationValueIfTheLocalOneIsMissing() + { + $this->given('that the location of the configuration files were specified with a directory path') + ->when('reading the parameter', 'only_global') + ->then('the result will be', 'global'); + } + + /** + * @scenario + */ + public function readingAConfigurationYieldsTheLocalConfigurationValueIfItIsSetInBothConfigurationFiles() + { + $this->given('that the location of the configuration files were specified with a directory path') + ->when('reading the parameter', 'both') + ->then('the result will be', 'local'); + } + + /** + * @scenario + */ + public function readingAConfigurationWithAnInvalidKeyThrowsAnException() + { + $this->given('that the location of the configuration files were specified with a directory path') + ->when('reading the parameter', array()) + ->then('the Config Object will throw an exception of type', 'InvalidArgumentException') + ->and( + 'the exception has the message', + 'The key must be a non-empty string!' + ); + } - public function testSomething() + /** + * @scenario + */ + public function tryingToReadAMissingConfigurationValueThrowsAnException() { + $this->given('that the location of the configuration files were specified with a directory path') + ->when('reading the parameter', 'unknown') + ->then('the Config Object will throw an exception of type', 'Horde_Kolab_Config_Exception') + ->and( + 'the exception has the message', + 'Parameter "unknown" has no value!' + ); } } \ No newline at end of file diff --git a/framework/Kolab_Config/test/Horde/Kolab/Config/conf.php.dist b/framework/Kolab_Config/test/Horde/Kolab/Config/conf.php.dist new file mode 100644 index 000000000..16efc5334 --- /dev/null +++ b/framework/Kolab_Config/test/Horde/Kolab/Config/conf.php.dist @@ -0,0 +1,2 @@ +