Index: util/adt/Stack.class.php =================================================================== --- util/adt/Stack.class.php (revision 6673) +++ util/adt/Stack.class.php (working copy) @@ -6,7 +6,8 @@ uses( 'lang.IndexOutOfBoundsException', - 'util.NoSuchElementException' + 'util.NoSuchElementException', + 'util.adt.HashProvider' ); /** @@ -37,7 +38,8 @@ */ class Stack extends Object { var - $_elements= array(); + $_elements = array(), + $_hash = 0; /** * Pushes an item onto the top of the stack. Returns the element that @@ -49,6 +51,7 @@ */ function &push(&$object) { array_unshift($this->_elements, $object); + $this->_hash+= HashProvider::hashOf($object->hashCode()); return $object; } @@ -63,7 +66,9 @@ if (empty($this->_elements)) { return throw(new NoSuchElementException('Stack is empty')); } - return array_shift($this->_elements); + $element= array_shift($this->_elements); + $this->_hash+= HashProvider::hashOf($element->hashCode()); + return $element; } /** @@ -126,5 +131,29 @@ } return $this->_elements[$index]; } + + /** + * Returns a hashcode for this queue + * + * @access public + * @return string + */ + function hashCode() { + return $this->_hash; + } + + /** + * Returns true if this queue equals another queue. + * + * @access public + * @param &lang.Object cmp + * @return bool + */ + function equals(&$cmp) { + return ( + is('util.adt.Stack', $cmp) && + ($this->hashCode() === $cmp->hashCode()) + ); + } } ?> Index: util/adt/DJBX33AHashImplementation.class.php =================================================================== --- util/adt/DJBX33AHashImplementation.class.php (revision 0) +++ util/adt/DJBX33AHashImplementation.class.php (revision 0) @@ -0,0 +1,77 @@ += 8; $len-= 8) { + $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + } + + switch ($len) { + case 7: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + case 6: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + case 5: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + case 4: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + case 3: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + case 2: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + case 1: $hash= (($hash << 5) + $hash) + ord($str{$offset++}); + } + return $hash; + } + + } implements(__FILE__, 'util.adt.HashImplementation'); +?> Index: util/adt/HashImplementation.class.php =================================================================== --- util/adt/HashImplementation.class.php (revision 0) +++ util/adt/HashImplementation.class.php (revision 0) @@ -0,0 +1,25 @@ + Index: util/adt/Set.class.php =================================================================== --- util/adt/Set.class.php (revision 6673) +++ util/adt/Set.class.php (working copy) @@ -77,5 +77,22 @@ * @return lang.Object[] objects */ function toArray() { } + + /** + * Returns a hashcode for this set + * + * @access public + * @return string + */ + function hashCode() { } + + /** + * Returns true if this set equals another set. + * + * @access public + * @param &lang.Object cmp + * @return bool + */ + function equals(&$cmp) { } } ?> Index: util/adt/HashTable.class.php =================================================================== --- util/adt/HashTable.class.php (revision 0) +++ util/adt/HashTable.class.php (revision 0) @@ -0,0 +1,175 @@ +hashCode(); + if (!isset($this->_buckets[$h])) { + $previous= NULL; + } else { + $previous= &$this->_buckets[$h][1]; + } + + $this->_buckets[$h]= array(&$key, &$value); + $this->_hash+= HashProvider::hashOf($h.$value->hashCode()); + return $previous; + } + + /** + * Returns the value to which this map maps the specified key. + * Returns NULL if the map contains no mapping for this key. + * + * @access public + * @param &lang.Object key + * @return &lang.Object the value associated with the key + */ + function &get(&$key) { + $h= $key->hashCode(); + if (!isset($this->_buckets[$h])) return NULL; + + return $this->_buckets[$h][1]; + } + + /** + * Removes the mapping for this key from this map if it is present. + * Returns the value to which the map previously associated the key, + * or null if the map contained no mapping for this key. + * + * @access public + * @param &lang.Object key + * @return &lang.Object the previous value associated with the key + */ + function &remove(&$key) { + $h= $key->hashCode(); + if (!isset($this->_buckets[$h])) { + $previous= NULL; + } else { + $previous= &$this->_buckets[$h][1]; + $this->_hash-= HashProvider::hashOf($h.$previous->hashCode()); + unset($this->_buckets[$h]); + } + + return $previous; + } + + /** + * Removes all mappings from this map. + * + * @access public + */ + function clear() { + $this->_buckets= array(); + $this->_hash= 0; + } + + /** + * Returns the number of key-value mappings in this map + * + * @access public + */ + function size() { + return sizeof($this->_buckets); + } + + /** + * Returns true if this map contains no key-value mappings. + * + * @access public + */ + function isEmpty() { + return empty($this->_buckets); + } + + /** + * Returns true if this map contains a mapping for the specified key. + * + * @access public + * @param &lang.Object key + * @return bool + */ + function containsKey(&$key) { + return isset($this->_buckets[$key->hashCode()]); + } + + /** + * Returns true if this map maps one or more keys to the specified value. + * + * @access public + * @param &lang.Object value + * @return bool + */ + function containsValue(&$value) { + foreach (array_keys($this->_buckets) as $key) { + if ($this->_buckets[$key][1]->equals($value)) return TRUE; + } + return FALSE; + } + + /** + * Returns a hashcode for this map + * + * @access public + * @return string + */ + function hashCode() { + return $this->_hash; + } + + /** + * Returns true if this map equals another map. + * + * @access public + * @param &lang.Object cmp + * @return bool + */ + function equals(&$cmp) { + return ( + is('util.adt.Map', $cmp) && + ($this->hashCode() === $cmp->hashCode()) + ); + } + + /** + * Returns a string representation of this map + * + * @access public + * @return string + */ + function toString() { + $s= $this->getClassName().'['.sizeof($this->_buckets).'] {'; + if (0 == sizeof($this->_buckets)) return $s.' }'; + + $s.= "\n"; + foreach (array_keys($this->_buckets) as $key) { + $s.= ' '.$this->_buckets[$key][0]->toString().' => '.$this->_buckets[$key][1]->toString().",\n"; + } + return substr($s, 0, -2)."\n}"; + } + + } implements(__FILE__, 'util.adt.Map'); +?> Index: util/adt/LRUBuffer.class.php =================================================================== --- util/adt/LRUBuffer.class.php (revision 6673) +++ util/adt/LRUBuffer.class.php (working copy) @@ -17,16 +17,18 @@ $size = 0; var - $_buf = array(); + $_access = array(), + $_elements = array(); /** * Constructor * * @access public * @param int size + * @throws lang.IllegalArgumentException is size is not greater than zero */ function __construct($size) { - $this->size= $size; + $this->setSize($size); } /** @@ -47,48 +49,68 @@ * buffer's size has not yet been exceeded). * * - * $deleted= $buf->add($key); + * $deleted= &$buf->add($key); * * * @access public - * @param string key - * @return int + * @param &lang.Object element + * @return &lang.Object victim */ - function add($id) { - $this->_buf[$id]= $this->microtime(); - if (sizeof($this->_buf) > $this->size) { + function &add(&$element) { + $h= $element->hashCode(); + $this->_access[$h]= $this->microtime(); + $this->_elements[$h]= &$element; + + // Check if this buffer's size has been exceeded + if (sizeof($this->_access) <= $this->size) return NULL; - // Find the position of the smallest value and delete it - $p= array_search(min($this->_buf), $this->_buf, TRUE); - unset($this->_buf[$p]); - return $p; - } + // Find the position of the smallest value and delete it + $p= array_search(min($this->_access), $this->_access, TRUE); + $victim= &$this->_elements[$p]; - return NULL; + unset($this->_access[$p]); + unset($this->_elements[$p]); + + return $victim; } /** * Update an element * * @access public - * @param string id + * @param &lang.Object element */ - function update($id) { - $this->_buf[$id]= $this->microtime(); + function update(&$element) { + $this->_access[$element->hashCode()]= $this->microtime(); } /** - * Set Size + * Get number of elements currently contained in this buffer * * @access public + * @return int + */ + function numElements() { + return sizeof($this->_access); + } + + /** + * Set size + * + * @access public * @param int size + * @throws lang.IllegalArgumentException is size is not greater than zero */ function setSize($size) { + if ($size <= 0) return throw(new IllegalArgumentException( + 'Size must be greater than zero, '.$size.' given' + )); + $this->size= $size; } /** - * Get Size + * Get size * * @access public * @return int Index: util/adt/Map.class.php =================================================================== --- util/adt/Map.class.php (revision 0) +++ util/adt/Map.class.php (revision 0) @@ -0,0 +1,110 @@ + Index: util/adt/HashProvider.class.php =================================================================== --- util/adt/HashProvider.class.php (revision 0) +++ util/adt/HashProvider.class.php (revision 0) @@ -0,0 +1,102 @@ + + * $hashCode= HashProvider::hashOf($string); + * + * + * Uses DJBX33A as default hashing implementation. To change the hashing + * implementation to be used, use the following: + * + * $provider= &HashProvider::getInstance(); + * $provider->setImplementation(new MyHashImplementation()); + * + * + * @see xp://util.adt.DJBX33AHashImplementation + * @see xp://util.adt.Map + * @purpose Hashing + */ + class HashProvider extends Object { + var + $impl= NULL; + + /** + * Static initializer. Sets DJBX33A as default hashing implementation. + * + * @model static + * @access public + */ + function __static() { + $self= &HashProvider::getInstance(); + $self->setImplementation(new DJBX33AHashImplementation()); + } + + /** + * Retrieve sole instance of this object + * + * @model static + * @access public + * @return &util.adt.HashProvider + */ + function &getInstance() { + static $instance= NULL; + + if (!isset($instance)) { + $instance= new HashProvider(); + + // No need to set the impl member, this block is only ever + // executed from the static initializer, which sets this + // member to the default hashing implementation. + } + return $instance; + } + + /** + * Returns hash for a given string + * + * @model static + * @access public + * @param string str + * @return int + */ + function hashOf($str) { + $self= &HashProvider::getInstance(); + return $self->impl->hashOf($str); + } + + /** + * Set hashing implementation + * + * @access public + * @param &util.adt.HashImplementation impl + * @throws lang.IllegalArgumentException when impl is not a HashImplementation + */ + function setImplementation(&$impl) { + if (!is('util.adt.HashImplementation', $impl)) { + return throw(new IllegalArgumentException( + 'Implementation is not a HashImplementation, '.xp::typeOf($impl).' given' + )); + } + $this->impl= &$impl; + } + + /** + * Get hashing implementation + * + * @access public + * @return &util.adt.HashImplementation + */ + function &getImplementation() { + return $this->impl; + } + } +?> Index: util/adt/HashSet.class.php =================================================================== --- util/adt/HashSet.class.php (revision 6673) +++ util/adt/HashSet.class.php (working copy) @@ -4,6 +4,8 @@ * $Id$ */ + uses('util.adt.HashProvider'); + /** * A set of objects * @@ -11,7 +13,8 @@ */ class HashSet extends Object { var - $_elements= array(); + $_elements = array(), + $_hash = 0; /** * Adds an object @@ -24,6 +27,7 @@ $h= $object->hashCode(); if (isset($this->_elements[$h])) return FALSE; + $this->_hash+= HashProvider::hashOf($h); $this->_elements[$h]= &$object; return TRUE; } @@ -39,6 +43,7 @@ $h= $object->hashCode(); if (!isset($this->_elements[$h])) return FALSE; + $this->_hash-= HashProvider::hashOf($h); unset($this->_elements[$h]); return TRUE; } @@ -51,8 +56,7 @@ * @return bool TRUE if the set contains the specified element. */ function contains(&$object) { - $h= $object->hashCode(); - return isset($this->_elements[$h]); + return isset($this->_elements[$object->hashCode()]); } /** @@ -72,6 +76,7 @@ */ function clear() { $this->_elements= array(); + $this->_hash= 0; } /** @@ -98,6 +103,7 @@ if (isset($this->_elements[$h])) continue; $result= TRUE; + $this->_hash+= HashProvider::hashOf($h); $this->_elements[$h]= &$objects[$i]; } return $result; @@ -113,5 +119,46 @@ return array_values($this->_elements); } + /** + * Returns a hashcode for this set + * + * @access public + * @return string + */ + function hashCode() { + return $this->_hash; + } + + /** + * Returns true if this set equals another set. + * + * @access public + * @param &lang.Object cmp + * @return bool + */ + function equals(&$cmp) { + return ( + is('util.adt.Set', $cmp) && + ($this->hashCode() === $cmp->hashCode()) + ); + } + + /** + * Returns a string representation of this set + * + * @access public + * @return string + */ + function toString() { + $s= $this->getClassName().'['.sizeof($this->_elements).'] {'; + if (0 == sizeof($this->_elements)) return $s.' }'; + + $s.= "\n"; + foreach (array_keys($this->_elements) as $key) { + $s.= ' '.$this->_elements[$key]->toString().",\n"; + } + return substr($s, 0, -2)."\n}"; + } + } implements(__FILE__, 'util.adt.Set'); ?> Index: util/adt/Queue.class.php =================================================================== --- util/adt/Queue.class.php (revision 6673) +++ util/adt/Queue.class.php (working copy) @@ -6,7 +6,8 @@ uses( 'lang.IndexOutOfBoundsException', - 'util.NoSuchElementException' + 'util.NoSuchElementException', + 'util.adt.HashProvider' ); /** @@ -36,7 +37,8 @@ */ class Queue extends Object { var - $_elements= array(); + $_elements = array(), + $_hash = 0; /** * Puts an item into the queue. Returns the element that was added. @@ -47,6 +49,7 @@ */ function &put(&$object) { $this->_elements[]= &$object; + $this->_hash+= HashProvider::hashOf($object->hashCode()); return $object; } @@ -61,7 +64,11 @@ if (empty($this->_elements)) { return throw(new NoSuchElementException('Queue is empty')); } - return array_shift($this->_elements); + + $e= &$this->_elements[0]; + $this->_hash-= HashProvider::hashOf($e->hashCode()); + $this->_elements= array_slice($this->_elements, 1); + return $e; } /** @@ -121,7 +128,9 @@ function remove(&$object) { if (-1 == ($pos= $this->search($object))) return FALSE; + $this->_hash-= HashProvider::hashOf($this->_elements[$pos]->hashCode()); unset($this->_elements[$pos]); + $this->_elements= array_values($this->_elements); // Re-index return TRUE; } @@ -139,5 +148,29 @@ } return $this->_elements[$index]; } + + /** + * Returns a hashcode for this queue + * + * @access public + * @return string + */ + function hashCode() { + return $this->_hash; + } + + /** + * Returns true if this queue equals another queue. + * + * @access public + * @param &lang.Object cmp + * @return bool + */ + function equals(&$cmp) { + return ( + is('util.adt.Queue', $cmp) && + ($this->hashCode() === $cmp->hashCode()) + ); + } } ?> Index: net/xp_framework/unittest/util/adt/StackTest.class.php =================================================================== --- net/xp_framework/unittest/util/adt/StackTest.class.php (revision 0) +++ net/xp_framework/unittest/util/adt/StackTest.class.php (revision 0) @@ -0,0 +1,130 @@ +stack= &new Stack(); + } + + /** + * Tests the Stack is initially empty + * + * @access public + */ + #[@test] + function initiallyEmpty() { + $this->assertTrue($this->stack->isEmpty()); + } + + /** + * Tests Stack equals its clone + * + * @access public + */ + #[@test] + function equalsClone() { + $this->stack->push(new String('green')); + $this->assertTrue($this->stack->equals(clone($this->stack))); + } + + /** + * Tests push() + * + * @access public + */ + #[@test] + function push() { + $this->stack->push(new String('green')); + $this->assertFalse($this->stack->isEmpty()); + $this->assertEquals(1, $this->stack->size()); + } + + /** + * Tests pop() + * + * @access public + */ + #[@test] + function pop() { + $color= &new String('green'); + $this->stack->push($color); + $this->assertEquals($color, $this->stack->pop()); + $this->assertTrue($this->stack->isEmpty()); + } + + /** + * Tests peek() + * + * @access public + */ + #[@test] + function peek() { + $color= &new String('green'); + $this->stack->push($color); + $this->assertEquals($color, $this->stack->peek()); + $this->assertFalse($this->stack->isEmpty()); + } + + /** + * Tests search() + * + * @access public + */ + #[@test] + function search() { + $color= &new String('green'); + $this->stack->push($color); + $this->assertEquals(0, $this->stack->search($color)); + $this->assertEquals(-1, $this->stack->search(new String('non-existant'))); + } + + /** + * Tests elementAt() + * + * @access public + */ + #[@test] + function elementAt() { + $this->stack->push(new String('red')); + $this->stack->push(new String('green')); + $this->stack->push(new String('blue')); + + $this->assertEquals(new String('blue'), $this->stack->elementAt(0)); + $this->assertEquals(new String('green'), $this->stack->elementAt(1)); + $this->assertEquals(new String('red'), $this->stack->elementAt(2)); + } + + /** + * Tests elementAt() when given an illegal offset + * + * @access public + */ + #[@test, @expect('lang.IndexOutOfBoundsException')] + function elementAtIllegalOffset() { + $this->stack->elementAt(-1); + } + } +?> Index: net/xp_framework/unittest/util/adt/HashTableTest.class.php =================================================================== --- net/xp_framework/unittest/util/adt/HashTableTest.class.php (revision 0) +++ net/xp_framework/unittest/util/adt/HashTableTest.class.php (revision 0) @@ -0,0 +1,213 @@ +map= &new HashTable(); + } + + /** + * Tests the map is initially empty + * + * @access public + */ + #[@test] + function initiallyEmpty() { + $this->assertTrue($this->map->isEmpty()); + } + + /** + * Tests map equals its clone + * + * @access public + */ + #[@test] + function equalsClone() { + $this->map->put(new String('color'), new String('green')); + $this->assertTrue($this->map->equals(clone($this->map))); + } + + /** + * Tests map equals another map with the same contents + * + * @access public + */ + #[@test] + function equalsOtherMapWithSameContents() { + $other= &new HashTable(); + $this->map->put(new String('color'), new String('green')); + $other->put(new String('color'), new String('green')); + $this->assertTrue($this->map->equals($other)); + } + + /** + * Tests map does not equal map with different contents + * + * @access public + */ + #[@test] + function doesNotEqualMapWithDifferentContents() { + $other= &new HashTable(); + $this->map->put(new String('color'), new String('blue')); + $other->put(new String('color'), new String('yellow')); + $this->assertFalse($this->map->equals($other)); + } + + /** + * Tests put() + * + * @access public + */ + #[@test] + function put() { + $this->map->put(new String('color'), new String('green')); + $this->assertFalse($this->map->isEmpty()); + $this->assertEquals(1, $this->map->size()); + } + + /** + * Tests put() returns previous value + * + * @access public + */ + #[@test] + function putReturnsPreviousValue() { + $color= &new String('color'); + $this->assertNull($this->map->put($color, new String('green'))); + $this->assertEquals(new String('green'), $this->map->put($color, new String('red'))); + $this->assertEquals(new String('red'), $this->map->get($color)); + } + + /** + * Tests get() + * + * @access public + */ + #[@test] + function get() { + $this->map->put(new String('key'), new String('value')); + $this->assertEquals(new String('value'), $this->map->get(new String('key'))); + } + + /** + * Tests get() returns NULL if the list is empty + * + * @access public + */ + #[@test] + function getReturnsNullOnEmptyList() { + $this->assertTrue($this->map->isEmpty()); + $this->assertNull($this->map->get(new String('key'))); + } + + /** + * Tests remove() + * + * @access public + */ + #[@test] + function remove() { + $this->map->put(new String('key'), new String('value')); + $this->map->remove(new String('key')); + $this->assertTrue($this->map->isEmpty()); + } + + /** + * Tests remove() returns previous value + * + * @access public + */ + #[@test] + function removeReturnsPreviousValue() { + $this->map->put(new String('key'), new String('value')); + $this->assertEquals(new String('value'), $this->map->remove(new String('key'))); + } + + /** + * Tests containsKey() method + * + * @access public + */ + #[@test] + function containsKey() { + $this->map->put(new String('key'), new String('value')); + $this->assertTrue($this->map->containsKey(new String('key'))); + $this->assertFalse($this->map->containsKey(new String('non-existant-key'))); + } + + /** + * Tests clear() method + * + * @access public + */ + #[@test] + function clear() { + $this->map->put(new String('key'), new String('value')); + $this->map->clear(); + $this->assertTrue($this->map->isEmpty()); + } + + /** + * Tests containsValue() method + * + * @access public + */ + #[@test] + function containsValue() { + $this->map->put(new String('key'), new String('value')); + $this->assertTrue($this->map->containsValue(new String('value'))); + $this->assertFalse($this->map->containsValue(new String('non-existant-value'))); + } + + /** + * Tests toString() method + * + * @access public + */ + #[@test] + function stringRepresentation() { + $this->map->put(new String('color'), new String('purple')); + $this->map->put(new String('price'), new String('25 USD')); + $this->assertEquals( + "util.adt.HashTable[2] {\n color => purple,\n price => 25 USD\n}", + $this->map->toString() + ); + } + + /** + * Tests toString() method on an empty map + * + * @access public + */ + #[@test] + function stringRepresentationOfEmptyMap() { + $this->assertEquals( + 'util.adt.HashTable[0] { }', + $this->map->toString() + ); + } + } +?> Index: net/xp_framework/unittest/util/adt/LRUBufferTest.class.php =================================================================== --- net/xp_framework/unittest/util/adt/LRUBufferTest.class.php (revision 0) +++ net/xp_framework/unittest/util/adt/LRUBufferTest.class.php (revision 0) @@ -0,0 +1,160 @@ +buffer= &new LRUBuffer(LRUTEST_BUFFER_DEAULT_SIZE); + } + + /** + * Tests the buffer is initially empty + * + * @access public + */ + #[@test] + function initiallyEmpty() { + $this->assertEquals(0, $this->buffer->numElements()); + } + + /** + * Tests the getSize() method + * + * @access public + */ + #[@test] + function getSize() { + $this->assertEquals(LRUTEST_BUFFER_DEAULT_SIZE, $this->buffer->getSize()); + } + + /** + * Tests the add() method + * + * @access public + */ + #[@test] + function add() { + $this->buffer->add(new String('one')); + $this->assertEquals(1, $this->buffer->numElements()); + } + + /** + * Tests the add() method returns the victim + * + * @access public + */ + #[@test] + function addReturnsVictim() { + + // We should be able to add at least as many as the buffer's size + // elements to the LRUBuffer. Nothing should be deleted from it + // during this loop. + for ($i= 0, $s= $this->buffer->getSize(); $i < $s; $i++) { + if (NULL === ($victim= &$this->buffer->add(new String('item #'.$i)))) continue; + + return $this->fail( + 'Victim '.xp::stringOf($victim).' when inserting item #'.($i + 1).'/'.$s, + $victim, + NULL + ); + } + + // The LRUBuffer is now "full". Next time we add something, the + // element last recently used should be returned. + $this->assertEquals( + new String('item #0'), + $this->buffer->add(new String('last item')) + ); + } + + /** + * Add a specified number of strings to the buffer. + * + * @access protected + * @param int num + */ + function addElements($num) { + for ($i= 0; $i < $num; $i++) { + $this->buffer->add(new String('item #'.$i)); + } + } + + /** + * Tests the buffer does not grow beyond the set limit + * + * @access public + */ + #[@test] + function bufferDoesNotGrowBeyondSize() { + $this->addElements($this->buffer->getSize()+ 1); + $this->assertEquals($this->buffer->getSize(), $this->buffer->numElements()); + } + + /** + * Tests the update() method + * + * @access public + */ + #[@test] + function update() { + + // Fill the LRUBuffer until its size is reached + $this->addElements($this->buffer->getSize()); + + // Update the first item + $this->buffer->update(new String('item #0')); + + // Now the second item should be chosen the victim when adding + // another element + $this->assertEquals( + new String('item #1'), + $this->buffer->add(new String('last item')) + ); + } + + /** + * Tests the setSize() method + * + * @access public + */ + #[@test] + function setSize() { + $this->buffer->setSize(10); + $this->assertEquals(10, $this->buffer->getSize()); + } + + /** + * Tests the setSize() method when passed an argument <= zero + * + * @access public + */ + #[@test, @expect('lang.IllegalArgumentException')] + function illegalSize() { + $this->buffer->setSize(0); + } + } +?> Index: net/xp_framework/unittest/util/adt/HashSetTest.class.php =================================================================== --- net/xp_framework/unittest/util/adt/HashSetTest.class.php (revision 0) +++ net/xp_framework/unittest/util/adt/HashSetTest.class.php (revision 0) @@ -0,0 +1,254 @@ +set= &new HashSet(); + } + + /** + * Tests the set is initially empty + * + * @access public + */ + #[@test] + function initiallyEmpty() { + $this->assertTrue($this->set->isEmpty()); + } + + /** + * Tests set equals its clone + * + * @access public + */ + #[@test] + function equalsClone() { + $this->set->add(new String('green')); + $this->assertTrue($this->set->equals(clone($this->set))); + } + + /** + * Tests set equals another set with the same contents + * + * @access public + */ + #[@test] + function equalsOtherSetWithSameContents() { + $other= &new HashSet(); + $this->set->add(new String('color')); + $other->add(new String('color')); + $this->assertTrue($this->set->equals($other)); + } + + /** + * Tests set does not equal set with different contents + * + * @access public + */ + #[@test] + function doesNotEqualSetWithDifferentContents() { + $other= &new HashSet(); + $this->set->add(new String('blue')); + $other->add(new String('yellow')); + $this->assertFalse($this->set->equals($other)); + } + + /** + * Tests add() + * + * @access public + */ + #[@test] + function add() { + $this->set->add(new String('green')); + $this->assertFalse($this->set->isEmpty()); + $this->assertEquals(1, $this->set->size()); + } + + /** + * Tests addAll() + * + * @access public + */ + #[@test] + function addAll() { + $array= array(new String('one'), new String('two'), new String('three')); + $this->set->addAll($array); + $this->assertFalse($this->set->isEmpty()); + $this->assertEquals(3, $this->set->size()); + } + + /** + * Tests addAll() uniques the array given + * + * @access public + */ + #[@test] + function addAllUniques() { + $array= array(new String('one'), new String('one'), new String('two')); + $this->set->addAll($array); + $this->assertFalse($this->set->isEmpty()); + $this->assertEquals(2, $this->set->size()); // String{"one"} and String{"two"} + } + + /** + * Tests addAll() returns TRUE if the set changed as a result if the + * call, FALSE otherwise. + * + * @access public + */ + #[@test] + function addAllReturnsWhetherSetHasChanged() { + $array= array(new String('caffeine'), new String('nicotine')); + $this->assertTrue($this->set->addAll($array)); + $this->assertFalse($this->set->addAll($array)); + $this->assertFalse($this->set->addAll(array(new String('caffeine')))); + $this->assertFalse($this->set->addAll(array())); + } + + /** + * Tests contains() method + * + * @access public + */ + #[@test] + function contains() { + $this->set->add(new String('key')); + $this->assertTrue($this->set->contains(new String('key'))); + $this->assertFalse($this->set->contains(new String('non-existant-key'))); + } + + /** + * Tests add() returns TRUE if the set did not already contain the + * given element, FALSE otherwise + * + * @access public + */ + #[@test] + function addSameValueTwice() { + $color= &new String('green'); + $this->assertTrue($this->set->add($color)); + $this->assertFalse($this->set->add($color)); + } + + /** + * Tests remove() + * + * @access public + */ + #[@test] + function remove() { + $this->set->add(new String('key')); + $this->assertTrue($this->set->remove(new String('key'))); + $this->assertTrue($this->set->isEmpty()); + } + + /** + * Tests remove() returns FALSE when given object cannot be + * contained in the set (because the set is empty) + * + * @access public + */ + #[@test] + function removeOnEmptySet() { + $this->assertFalse($this->set->remove(new String('irrelevant-set-is-empty-anyway'))); + } + + /** + * Tests remove() returns FALSE when given object is not contained + * in the set. + * + * @access public + */ + #[@test] + function removeNonExistantObject() { + $this->set->add(new String('key')); + $this->assertFalse($this->set->remove(new String('non-existant-key'))); + } + + /** + * Tests clear() method + * + * @access public + */ + #[@test] + function clear() { + $this->set->add(new String('key')); + $this->set->clear(); + $this->assertTrue($this->set->isEmpty()); + } + + /** + * Tests toArray() method + * + * @access public + */ + #[@test] + function toArray() { + $color= &new String('red'); + $this->set->add($color); + $this->assertEquals(array(&$color), $this->set->toArray()); + } + + /** + * Tests toArray() method + * + * @access public + */ + #[@test] + function toArrayOnEmptySet() { + $this->assertEquals(array(), $this->set->toArray()); + } + + /** + * Tests toString() method + * + * @access public + */ + #[@test] + function stringRepresentation() { + $this->set->add(new String('color')); + $this->set->add(new String('price')); + $this->assertEquals( + "util.adt.HashSet[2] {\n color,\n price\n}", + $this->set->toString() + ); + } + + /** + * Tests toString() method on an empty set + * + * @access public + */ + #[@test] + function stringRepresentationOfEmptySet() { + $this->assertEquals( + 'util.adt.HashSet[0] { }', + $this->set->toString() + ); + } + } +?> Index: net/xp_framework/unittest/util/adt/QueueTest.class.php =================================================================== --- net/xp_framework/unittest/util/adt/QueueTest.class.php (revision 0) +++ net/xp_framework/unittest/util/adt/QueueTest.class.php (revision 0) @@ -0,0 +1,235 @@ +queue= &new Queue(); + } + + /** + * Tests the queue is initially empty + * + * @access public + */ + #[@test] + function initiallyEmpty() { + $this->assertTrue($this->queue->isEmpty()); + } + + /** + * Tests queue equals its clone + * + * @access public + */ + #[@test] + function equalsClone() { + $this->queue->put(new String('green')); + $this->assertTrue($this->queue->equals(clone($this->queue))); + } + + /** + * Tests put() + * + * @access public + */ + #[@test] + function put() { + $this->queue->put(new String('green')); + $this->assertFalse($this->queue->isEmpty()); + $this->assertEquals(1, $this->queue->size()); + } + + /** + * Tests get() + * + * @access public + */ + #[@test] + function get() { + $color= &new String('red'); + $this->queue->put($color); + $this->assertEquals($color, $this->queue->get()); + $this->assertTrue($this->queue->isEmpty()); + } + + /** + * Tests get() throws an exception when there are no more elements + * in the queue, + * + * @access public + */ + #[@test, @expect('util.NoSuchElementException')] + function exceptionOnNoMoreElements() { + $this->queue->get(); + } + + /** + * Tests peek() + * + * @access public + */ + #[@test] + function peek() { + $color= &new String('blue'); + $this->queue->put($color); + $this->assertEquals($color, $this->queue->peek()); + $this->assertFalse($this->queue->isEmpty()); + } + + /** + * Tests peek() returns NULL when there are no more elements + * in the queue. + * + * @access public + */ + #[@test] + function peekReturnsNullOnNoMoreElements() { + $this->assertNull($this->queue->peek()); + } + + /** + * Tests remove() + * + * @access public + */ + #[@test] + function remove() { + $color= &new String('blue'); + $this->queue->put($color); + $this->queue->remove($color); + $this->assertTrue($this->queue->isEmpty()); + } + + /** + * Tests remove() returns TRUE when the element was deleted, FALSE otherwise + * + * @access public + */ + #[@test] + function removeReturnsWhetherDeleted() { + $color= &new String('pink'); + $this->queue->put($color); + $this->assertTrue($this->queue->remove($color)); + $this->assertFalse($this->queue->remove(new String('purple'))); + $this->assertTrue($this->queue->isEmpty()); + $this->assertFalse($this->queue->remove($color)); + $this->assertFalse($this->queue->remove(new String('purple'))); + } + + /** + * Tests elementAt() + * + * @access public + */ + #[@test] + function elementAt() { + $this->queue->put(new String('red')); + $this->queue->put(new String('green')); + $this->queue->put(new String('blue')); + $this->assertEquals(new String('red'), $this->queue->elementAt(0)); + $this->assertEquals(new String('green'), $this->queue->elementAt(1)); + $this->assertEquals(new String('blue'), $this->queue->elementAt(2)); + } + + /** + * Tests iterative use + * + * Example: + * + * + * // Fill queue + * with ($q= &new Queue()); { + * $q->put(new String('One')); + * $q->put(new String('Two')); + * $q->put(new String('Three')); + * $q->put(new String('Four')); + * } + * + * // Empty queue + * while (!$q->isEmpty()) { + * var_dump($q->get()); + * } + * + * + * @access public + */ + #[@test] + function iterativeUse() { + $input= array(new String('red'), new String('green'), new String('blue')); + + // Add + for ($i= 0, $s= sizeof($input); $i < sizeof($input); $i++) { + $this->queue->put($input[$i]); + } + + // Retrieve + $i= 0; + while (!$this->queue->isEmpty()) { + $element= &$this->queue->get(); + + if (!$input[$i]->equals($element)) { + $this->fail('Not equal at offset #'.$i, $element, $input[$i]); + break; + } + $i++; + } + } + + /** + * Tests elementAt() throws an exception in case an illegal offset + * is specified. + * + * @access public + */ + #[@test, @expect('util.IndexOutOfBoundsException')] + function elementAtIllegalOffset() { + $this->queue->elementAt(-1); + } + + /** + * Tests elementAt() throws an exception in case an out-of-bound + * offset is specified. + * + * @access public + */ + #[@test, @expect('util.IndexOutOfBoundsException')] + function elementAtOffsetOutOfBounds() { + $this->queue->put(new String('one')); + $this->queue->elementAt($this->queue->size() + 1); + } + + /** + * Tests elementAt() throws an exception in case the list is + * empty. + * + * @access public + */ + #[@test, @expect('util.IndexOutOfBoundsException')] + function elementAtEmptyList() { + $this->queue->elementAt(0); + } + } +?>