From: Gunnar Wrobel Date: Tue, 29 Sep 2009 15:23:36 +0000 (+0200) Subject: The mock driver. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=1c91915a864f158e7ba702f2dd5be8c0f42e42f5;p=horde.git The mock driver. --- diff --git a/framework/History/lib/Horde/History/Factory.php b/framework/History/lib/Horde/History/Factory.php index c475912f8..cdfa8e6b6 100644 --- a/framework/History/lib/Horde/History/Factory.php +++ b/framework/History/lib/Horde/History/Factory.php @@ -61,8 +61,9 @@ class Horde_History_Factory case 'Sql': return Horde_History_Factory::getHistorySql($injector, $config->params); case 'Mock': - default: return Horde_History_Factory::getHistoryMock($config->params); + default: + throw new Horde_Exception(sprintf("Driver %s not supported!", $config->driver)); } } @@ -109,6 +110,24 @@ class Horde_History_Factory } /** + * Create a concrete Horde_History_Mock instance. + * + * @param Horde_Injector $injector The environment for creating the instance. + * @param array $params The db connection parameters if the + * environment does not already provide a + * connection. + * + * @return Horde_History_Mock The new Horde_History_Mock instance. + * + * @throws Horde_Exception If the injector provides no configuration or + * creating the database connection failed. + */ + static protected function getHistoryMock(array $params) + { + return new Horde_History_Mock(); + } + + /** * Create a database connection. * * @param array $params The database connection parameters. diff --git a/framework/History/lib/Horde/History/Mock.php b/framework/History/lib/Horde/History/Mock.php new file mode 100644 index 000000000..dde9254fd --- /dev/null +++ b/framework/History/lib/Horde/History/Mock.php @@ -0,0 +1,248 @@ + + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=History + */ + +/** + * The Horde_History_Mock:: class provides a method of tracking changes in Horde + * objects, stored in memory. + * + * Copyright 2009 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 Horde + * @package History + * @author Gunnar Wrobel + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @link http://pear.horde.org/index.php?package=History + */ +class Horde_History_Mock extends Horde_History +{ + /** + * Our storage location. + * + * @var array + */ + private $_data = array(); + + /** + * The next id. + * + * @var int + */ + private $_id = 1; + + /** + * Logs an event to an item's history log. Any other details about the event + * are passed in $attributes. + * + * @param Horde_HistoryObject $history The history item to add to. + * @param array $attributes The hash of name => value entries + * that describe this event. + * @param boolean $replaceAction If $attributes['action'] is + * already present in the item's + * history log, update that entry + * instead of creating a new one. + * + * @return boolean True if the operation succeeded. + * + * @throws Horde_Exception + */ + protected function _log(Horde_HistoryObject $history, + array $attributes, + $replaceAction = false) + { + $values = array( + 'history_uid' => $history->uid, + 'history_ts' => $attributes['ts'], + 'history_who' => $attributes['who'], + 'history_desc' => isset($attributes['desc']) ? $attributes['desc'] : null, + 'history_action' => isset($attributes['action']) ? $attributes['action'] : null + ); + + unset($attributes['ts'], $attributes['who'], $attributes['desc'], $attributes['action']); + + $values['history_extra'] = $attributes + ? serialize($attributes) + : null; + + /* If we want to replace an entry with the same action, try and find + * one. Track whether or not we succeed in $done, so we know whether + * or not to add the entry later. */ + $done = false; + if ($replaceAction && !empty($values['history_action'])) { + for ($i = 0, $count = count($history->data); $i < $count; ++$i) { + if (!empty($history->data[$i]['action']) && + $history->data[$i]['action'] == $values['history_action']) { + $this->_data[$history->data[$i]['id']] = $values; + $done = true; + break; + } + } + } + + /* If we're not replacing by action, or if we didn't find an entry to + * replace, insert a new row. */ + if (!$done) { + + $this->_data[$this->_id] = $values; + $this->_id++; + } + + return true; + } + + /** + * Returns a Horde_HistoryObject corresponding to the named history + * entry, with the data retrieved appropriately. + * + * @param string $guid The name of the history entry to retrieve. + * + * @return Horde_HistoryObject A Horde_HistoryObject + */ + public function getHistory($guid) + { + $result= array(); + foreach ($this->_data as $id => $element) { + if ($element['history_uid'] == $guid) { + $element['history_id'] = $id; + $result[] = $element; + } + } + return new Horde_HistoryObject($guid, $result); + } + + /** + * Finds history objects by timestamp, and optionally filter on other + * fields as well. + * + * @param string $cmp The comparison operator (<, >, <=, >=, or =) to + * check the timestamps with. + * @param integer $ts The timestamp to compare against. + * @param array $filters An array of additional (ANDed) criteria. + * Each array value should be an array with 3 + * entries: + *
+     * 'field' - the history field being compared (i.e. 'action').
+     * 'op'    - the operator to compare this field with.
+     * 'value' - the value to check for (i.e. 'add').
+     * 
+ * @param string $parent The parent history to start searching at. If + * non-empty, will be searched for with a LIKE + * '$parent:%' clause. + * + * @return array An array of history object ids, or an empty array if + * none matched the criteria. + * + * @throws Horde_Exception + */ + public function getByTimestamp($cmp, $ts, $filters = array(), + $parent = null) + { + $result = array(); + + foreach ($this->_data as $id => $element) { + + $ignore = false; + + switch ($cmp) { + case '<=': + case '=<': + if ($element['history_ts'] > $ts) { + $ignore = true; + }; + break; + case '<': + if ($element['history_ts'] >= $ts) { + $ignore = true; + }; + break; + case '=': + if ($element['history_ts'] != $ts) { + $ignore = true; + }; + break; + case '>': + if ($element['history_ts'] <= $ts) { + $ignore = true; + }; + break; + case '>=': + case '=>': + if ($element['history_ts'] < $ts) { + $ignore = true; + }; + break; + default: + throw new Horde_Exception(sprintf("Comparison %s not implemented!", $cmp)); + } + + if ($ignore) { + continue; + } + + /* Add additional filters, if there are any. */ + if ($filters) { + foreach ($filters as $filter) { + if ($filter['op'] != '=') { + throw new Horde_Exception(sprintf("Comparison %s not implemented!", $filter['op'])); + } + if ($element['history_' . $filter['field']] != $filter['value']) { + $ignore = true; + } + } + } + + if ($ignore) { + continue; + } + + if ($parent) { + if (substr($element['history_uid'], 0, strlen($parent) + 1) != $parent . ':') { + continue; + } + } + + $result[$element['history_uid']] = $id; + } + return $result; + } + + /** + * Remove one or more history entries by name. + * + * @param array $names The history entries to remove. + * + * @return boolean True if the operation succeeded. + * + * @throws Horde_Exception + */ + public function removeByNames($names) + { + if (!count($names)) { + return true; + } + + $ids = array(); + foreach ($this->_data as $id => $element) { + if (in_array($element['history_uid'], $names)) { + $ids[] = $id; + } + } + + foreach ($ids as $id) { + unset($this->_data[$id]); + } + return true; + } +} diff --git a/framework/History/package.xml b/framework/History/package.xml index f31f2c512..0c104eea9 100644 --- a/framework/History/package.xml +++ b/framework/History/package.xml @@ -34,6 +34,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> + @@ -67,6 +68,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> + diff --git a/framework/History/test/Horde/History/InterfaceTest.php b/framework/History/test/Horde/History/InterfaceTest.php index 178a6acca..829cd433b 100644 --- a/framework/History/test/Horde/History/InterfaceTest.php +++ b/framework/History/test/Horde/History/InterfaceTest.php @@ -34,10 +34,10 @@ require_once 'Horde/Autoloader.php'; class Horde_History_InterfaceTest extends PHPUnit_Framework_TestCase { /** The basic mock environment */ - const ENVIRONMENT_MOCK = 'mock'; + const ENVIRONMENT_MOCK = 'Mock'; /** The environment using a database */ - const ENVIRONMENT_DB = 'db'; + const ENVIRONMENT_DB = 'Sql'; /** * Path to the temporary sqlite db used for testing the db environment. @@ -66,7 +66,10 @@ class Horde_History_InterfaceTest extends PHPUnit_Framework_TestCase * The db environment provides our only test scenario before * refactoring. */ - $this->_environments = array(self::ENVIRONMENT_DB); + $this->_environments = array( + self::ENVIRONMENT_MOCK, + self::ENVIRONMENT_DB, + ); } return $this->_environments; } @@ -135,6 +138,12 @@ EOL; $config->params = $conf['sql']; $injector->setInstance('Horde_History_Config', $config); break; + case self::ENVIRONMENT_MOCK: + $injector = new Horde_Injector(new Horde_Injector_TopLevel()); + $injector->bindImplementation( + 'Horde_History', + 'Horde_History_Mock' + ); } return $injector; } @@ -156,12 +165,11 @@ EOL; public function testMethodSingletonHasResultHordehistoryWhichIsAlwaysTheSame() { - //TODO: More complex foreach ($this->getEnvironments() as $environment) { $history = $this->getHistory($environment); - $history1 = Horde_History::singleton(); + $history1 = Horde_History::singleton($environment); $this->assertType('Horde_History', $history1); - $history2 = Horde_History::singleton(); + $history2 = Horde_History::singleton($environment); $this->assertType('Horde_History', $history2); $this->assertSame($history1, $history2); } @@ -169,7 +177,7 @@ EOL; public function testMethodLogHasPostConditionThatTimestampAndActorAreAlwaysRecorded() { - //TODO: More complex + //@todo: More complex foreach ($this->getEnvironments() as $environment) { $history = $this->getHistory($environment); $history->log('test', array('action' => 'test')); @@ -181,7 +189,7 @@ EOL; public function testMethodLogHasPostConditionThatTheGivenEventHasBeenRecorded() { - //TODO: More complex + //@todo: More complex foreach ($this->getEnvironments() as $environment) { $history = $this->getHistory($environment); $history->log('test', array('who' => 'me', 'ts' => 1000, 'action' => 'test')); @@ -301,13 +309,14 @@ EOL; public function testMethodGetbytimestampHasResultArrayContainingTheMatchingEventIds() { - //TODO: More complex + //@todo: More complex foreach ($this->getEnvironments() as $environment) { $history = $this->getHistory($environment); $history->log('test', array('who' => 'me', 'ts' => 1000, 'action' => 'test')); + $history->log('test', array('who' => 'me', 'ts' => 1000, 'action' => 'test')); $history->log('test', array('who' => 'you', 'ts' => 2000, 'action' => 'yours', 'extra' => array('a' => 'a'))); $result = $history->getByTimestamp('<', 1001); - $this->assertEquals(array('test' => 1), $result); + $this->assertEquals(array('test' => 2), $result); } } @@ -485,21 +494,21 @@ class Dummy_Db extends DB_common { public function query($sql, $values = null) { - return PEAR::raiseError('Error'); + return new PEAR_Error('Error'); } public function nextId($table) { - return PEAR::raiseError('Error'); + return new PEAR_Error('Error'); } public function getAll($sql) { - return PEAR::raiseError('Error'); + return new PEAR_Error('Error'); } public function getAssoc($sql) { - return PEAR::raiseError('Error'); + return new PEAR_Error('Error'); } } \ No newline at end of file