diff --git a/sources/Data/Collections/HashTable.uc b/sources/Data/Collections/HashTable.uc index aee142d..a783b44 100644 --- a/sources/Data/Collections/HashTable.uc +++ b/sources/Data/Collections/HashTable.uc @@ -480,7 +480,7 @@ public function Empty(optional bool deprecated) * * Collecting all keys from the `HashTable` is O(). * - * See also `CopyTextKeys()` methods. + * See also `GetTextKeys()` methods. * * @return Array of all the caller `HashTable`'s keys. * This method does not return copies of keys, but actual keys instead - diff --git a/sources/Data/Collections/Tests/TEST_HashTable.uc b/sources/Data/Collections/Tests/TEST_HashTable.uc new file mode 100644 index 0000000..3ec2ccc --- /dev/null +++ b/sources/Data/Collections/Tests/TEST_HashTable.uc @@ -0,0 +1,561 @@ +/** + * Set of tests for `HashTable` class. + * Copyright 2022 Anton Tarasenko + *------------------------------------------------------------------------------ + * This file is part of Acedia. + * + * Acedia is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * Acedia is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Acedia. If not, see . + */ +class TEST_HashTable extends TestCase + abstract; + +var private array keys; +var private array mockedItems; + +protected static function TESTS() +{ + Test_GetSet(); + Test_HasKey(); + Test_GetKeys(); + Test_GetTextKeys(); + Test_Remove(); + Test_CreateItem(); + Test_Empty(); + Test_Length(); + Test_ReferenceManagement(); + Test_Take(); + Test_LargeArray(); +} + +protected static function AcediaObject NewKey(int value) +{ + local IntBox newBox; + + newBox = __().box.int(value); + default.keys[default.keys.length] = newBox; + return newBox; +} + +protected static function MockItem NewMockItem() +{ + local MockItem newItem; + + newItem = MockItem(__().memory.Allocate(class'MockItem')); + default.mockedItems[default.mockedItems.length] = newItem; + return newItem; +} + +protected static function Test_GetSet() +{ + local Text textObject; + local HashTable array; + + array = HashTable(__().memory.Allocate(class'HashTable')); + Context("Testing getters and setters for items of `HashTable`."); + Issue("`SetItem()` does not correctly set new items."); + textObject = __().text.FromString("value"); + array.SetItem(__().text.FromString("key"), textObject); + array.SetItem(__().box.int(13), __().text.FromString("value #2")); + array.SetItem(__().box.float(345.2), __().box.bool(true)); + TEST_ExpectTrue( Text(array.GetItem(__().box.int(13))).ToString() + == "value #2"); + TEST_ExpectTrue(array.GetItem(__().text.FromString("key")) == textObject); + TEST_ExpectTrue(BoolBox(array.GetItem(__().box.float(345.2))).Get()); + + Issue("`SetItem()` does not correctly overwrite new items."); + array.SetItem(__().text.FromString("key"), __().text.FromString("value")); + array.SetItem(__().box.int(13), __().box.int(11)); + TEST_ExpectFalse(array.GetItem(__().text.FromString("key")) == textObject); + TEST_ExpectTrue( Text(array.GetItem(__().text.FromString("key"))) + .ToString() == "value"); + TEST_ExpectTrue( IntBox(array.GetItem(__().box.int(13))).Get() + == 11); + + Issue("`GetItem()` does not return `none` for non-existing keys."); + TEST_ExpectNone(array.GetItem(__().box.int(12))); + TEST_ExpectNone(array.GetItem(__().box.byte(67))); + TEST_ExpectNone(array.GetItem(__().box.float(43.1234))); + TEST_ExpectNone(array.GetItem(__().text.FromString("Some random stuff"))); +} + +protected static function Test_HasKey() +{ + local HashTable array; + + array = HashTable(__().memory.Allocate(class'HashTable')); + Context("Testing `HasKey()` method for `HashTable`."); + array.SetItem(__().text.FromString("key"), __().text.FromString("value")); + array.SetItem(__().box.int(13), __().text.FromString("value #2")); + array.SetItem(__().box.float(345.2), __().box.bool(true)); + Issue("`HasKey()` reports that added keys do not exist in" + @ "`HashTable`."); + TEST_ExpectTrue(array.HasKey(__().text.FromString("key"))); + TEST_ExpectTrue(array.HasKey(__().box.int(13))); + TEST_ExpectTrue(array.HasKey(__().box.float(345.2))); + + Issue("`HasKey()` reports that `HashTable` contains keys that" + @ "were never added."); + TEST_ExpectFalse(array.HasKey(none)); + TEST_ExpectFalse(array.HasKey(__().box.float(13))); + TEST_ExpectFalse(array.HasKey(__().box.byte(139))); +} + +protected static function Test_GetKeys() +{ + local int i; + local AcediaObject key1, key2, key3; + local array keys; + local HashTable array; + + array = HashTable(__().memory.Allocate(class'HashTable')); + Context("Testing `GetKeys()` method for `HashTable`."); + key1 = __().text.FromString("key"); + key2 = __().box.int(13); + key3 = __().box.float(345.2); + array.SetItem(key1, __().text.FromString("value")); + array.SetItem(key2, __().text.FromString("value #2")); + array.SetItem(key3, __().box.bool(true)); + keys = array.GetKeys(); + Issue("`GetKeys()` returns array with wrong amount of elements."); + TEST_ExpectTrue(keys.length == 3); + + Issue("`GetKeys()` returns array with duplicate keys."); + TEST_ExpectTrue(keys[0] != keys[1]); + TEST_ExpectTrue(keys[0] != keys[2]); + TEST_ExpectTrue(keys[1] != keys[2]); + + Issue("`GetKeys()` returns array with incorrect keys."); + for (i = 0; i < 3; i += 1) { + TEST_ExpectTrue(keys[i] == key1 || keys[i] == key2 || keys[i] == key3); + } + + keys = array.RemoveItem(key1).GetKeys(); + Issue("`GetKeys()` returns array with incorrect keys after removing" + @ "an element."); + TEST_ExpectTrue(keys.length == 2); + TEST_ExpectTrue(keys[0] != keys[1]); + for (i = 0; i < 2; i += 1) { + TEST_ExpectTrue(keys[i] == key2 || keys[i] == key3); + } +} + +protected static function Test_GetTextKeys() +{ + local array allKeys; + local array keys; + local HashTable array; + + array = HashTable(__().memory.Allocate(class'HashTable')); + Context("Testing `GetTextKeys()` method for `HashTable`."); + array.SetItem(__().text.FromString("key"), __().text.FromString("value")); + array.SetItem(__().box.int(13), __().text.FromString("value #2")); + array.SetItem(__().box.float(-925.274), __().text.FromString("value #2")); + array.SetItem(__().text.FromString("second key"), __().box.bool(true)); + + Issue("`GetTextKeys()` does not return correct set of keys."); + keys = array.GetTextKeys(); + TEST_ExpectTrue(keys.length == 2); + TEST_ExpectTrue( + (keys[0].ToString() == "key" + && keys[1].ToString() == "second key") + || (keys[0].ToString() == "second key" + && keys[1].ToString() == "key")); + + Issue("Deallocating keys returned by `GetTextKeys()` affects their" + @ "source collection."); + allKeys = array.GetKeys(); + TEST_ExpectTrue(allKeys.length == 4); + TEST_ExpectNotNone(array.GetItem(__().text.FromString("key"))); + TEST_ExpectNotNone(array.GetItem(__().text.FromString("second key"))); + TEST_ExpectTrue( + array.GetText(__().text.FromString("key")).ToString() == "value"); +} + +protected static function Test_Remove() +{ + local AcediaObject key1, key2, key3; + local array keys; + local HashTable array; + + Context("Testing removing elements from `HashTable`."); + array = HashTable(__().memory.Allocate(class'HashTable')); + key1 = __().text.FromString("some key"); + key2 = __().box.int(25); + key3 = __().box.float(0.07); + array.SetItem(key1, __().text.FromString("value")); + array.SetItem(key2, __().text.FromString("value #2")); + array.SetItem(key3, __().box.bool(true)); + + Issue("Elements are not properly removed from `HashTable`."); + array.RemoveItem(key1) + .RemoveItem(__().box.int(25)) + .RemoveItem(__().box.float(0.06)); + keys = array.GetKeys(); + TEST_ExpectTrue(array.GetLength() == 1); + TEST_ExpectTrue(keys.length == 1); + TEST_ExpectTrue(keys[0] == key3); +} + +protected static function Test_CreateItem() +{ + local HashTable array; + + array = HashTable(__().memory.Allocate(class'HashTable')); + Context("Testing creating brand new items for `HashTable`."); + Issue("`CreateItem()` incorrectly adds new values to the" + @ "`HashTable`."); + array.CreateItem(__().text.FromString("key"), class'Text'); + array.CreateItem(__().box.float(17.895), class'IntRef'); + array.CreateItem(__().text.FromString("key #2"), class'BoolBox'); + TEST_ExpectTrue(Text(array.GetItem(__().text.FromString("key"))) + .ToString() == ""); + TEST_ExpectTrue( IntRef(array.GetItem(__().box.float(17.895))).Get() + == 0); + TEST_ExpectFalse(BoolBox(array.GetItem(__().text.FromString("key #2"))) + .Get()); + + Issue("`CreateItem()` incorrectly overrides existing values in the" + @ "`HashTable`."); + array.SetItem(__().box.int(13), __().ref.int(7)); + array.CreateItem(__().box.int(13), class'StringRef'); + TEST_ExpectTrue( StringRef(array.GetItem(__().box.int(13))).Get() + == ""); + + class'MockItem'.default.objectCount = 0; + Issue("`CreateItem()` creates new object even if it cannot be recorded with" + @ "a given key."); + array.CreateItem(none, class'MockItem'); + TEST_ExpectTrue(class'MockItem'.default.objectCount == 0); +} + +protected static function Test_Empty() +{ + local AcediaObject key1, key2, key3; + local HashTable array; + + key1 = __().text.FromString("key"); + key2 = __().box.int(13); + key3 = __().box.float(345.2); + array = HashTable(__().memory.Allocate(class'HashTable')); + array.SetItem(key1, __().text.FromString("value")); + array.SetItem(key2, __().text.FromString("value #2")); + array.SetItem(key3, __().box.bool(true)); + + Context("Testing `Empty()` method for `HashTable`."); + Issue("`HashTable` still contains elements after being emptied."); + array.Empty(); + TEST_ExpectTrue(array.GetKeys().length == 0); + TEST_ExpectTrue(array.GetLength() == 0); + + Issue("`HashTable` still contains elements after being emptied."); + array.SetItem(key1, __().text.FromString("value")); + array.SetItem(key2, __().text.FromString("value #2")); + array.SetItem(key3, __().box.bool(true)); + array.Empty(); + TEST_ExpectTrue(array.GetKeys().length == 0); + TEST_ExpectTrue(array.GetLength() == 0); +} + +protected static function Test_Length() +{ + local HashTable array; + + array = HashTable(__().memory.Allocate(class'HashTable')); + Context("Testing computing length of `HashTable`."); + Issue("Length is not zero for newly created `HashTable`."); + TEST_ExpectTrue(array.GetLength() == 0); + + Issue("Length is incorrectly computed after adding elements to" + @ "`HashTable`."); + array.SetItem(__().text.FromString("key"), __().text.FromString("value")); + array.SetItem(__().box.int(4563), __().text.FromString("value #2")); + array.SetItem(__().box.float(3425.243), __().box.byte(23)); + TEST_ExpectTrue(array.GetLength() == 3); + + Issue("Length is incorrectly computed after removing elements from" + @ "`HashTable`."); + array.RemoveItem(__().box.int(4563)); + TEST_ExpectTrue(array.GetLength() == 2); +} + +protected static function HashTable RecreateTestHashTable() +{ + local HashTable table; + + class'MockItem'.default.objectCount = 0; + default.mockedItems.length = 0; + default.keys.length = 0; + table = HashTable(__().memory.Allocate(class'HashTable')); + table.SetItem(NewKey(0), NewMockItem()); + table.SetItem(NewKey(1), NewMockItem()); + table.SetItem(NewKey(2), NewMockItem()); + table.SetItem(NewKey(3), NewMockItem()); + table.SetItem(NewKey(4), NewMockItem()); + table.SetItem(NewKey(5), NewMockItem()); + table.CreateItem(NewKey(6), class'MockItem'); + table.CreateItem(NewKey(7), class'MockItem'); + table.CreateItem(NewKey(8), class'MockItem'); + return table; +} + +protected static function Test_ReferenceManagement() +{ + Context("Testing how `HashTable` handles reference counting."); + SubTest_IncorrectGetRefCount(RecreateTestHashTable()); + SubTest_IncorrectTakeRefCount(RecreateTestHashTable()); + SubTest_IncorrectGetEntryRefCount(RecreateTestHashTable()); + SubTest_IncorrectTakeEntryRefCount(RecreateTestHashTable()); + SubTest_IncorrectEmptyRefCount(RecreateTestHashTable()); + SubTest_IncorrectGetKeysRefCount(RecreateTestHashTable()); +} + +protected static function SubTest_IncorrectGetRefCount(HashTable table) +{ + Issue("`HashTable` incorrectly handles reference count when storing" @ + "an item."); + // 1 ref after creation + 1 ref in `HastTable` => 2 + TEST_ExpectTrue(default.mockedItems[0]._getRefCount() == 2); + TEST_ExpectTrue(default.mockedItems[1]._getRefCount() == 2); + TEST_ExpectTrue(default.mockedItems[2]._getRefCount() == 2); + TEST_ExpectTrue(default.mockedItems[3]._getRefCount() == 2); + TEST_ExpectTrue(default.mockedItems[4]._getRefCount() == 2); + TEST_ExpectTrue(default.mockedItems[5]._getRefCount() == 2); + + Issue("`HashTable` incorrectly handles reference count when returning" + @ "an item with `GetItem()`."); + // 1 ref after creation + 1 ref in `HastTable` + 1 after `GetItem()` => 3 + TEST_ExpectTrue(table.GetItem(__().box.int(0))._getRefCount() == 3); + TEST_ExpectTrue(table.GetItem(__().box.int(1))._getRefCount() == 3); + TEST_ExpectTrue(table.GetItem(__().box.int(2))._getRefCount() == 3); + TEST_ExpectTrue(table.GetItem(__().box.int(3))._getRefCount() == 3); + TEST_ExpectTrue(table.GetItem(__().box.int(4))._getRefCount() == 3); + TEST_ExpectTrue(table.GetItem(__().box.int(5))._getRefCount() == 3); + // 1 ref in `HastTable` + 1 after `GetItem()` => 2 + TEST_ExpectTrue(table.GetItem(__().box.int(6))._getRefCount() == 2); + TEST_ExpectTrue(table.GetItem(__().box.int(7))._getRefCount() == 2); + TEST_ExpectTrue(table.GetItem(__().box.int(8))._getRefCount() == 2); + // 1 ref after creation + 1 ref in `HastTable` => 2 + Issue("`HashTable` incorrectly handles reference count for keys when" + @ "using `GetItem()."); + TEST_ExpectTrue(default.keys[0]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[1]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[2]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[3]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[4]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[5]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[6]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[7]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[8]._getRefCount() == 2); +} + +protected static function SubTest_IncorrectTakeRefCount(HashTable table) +{ + Issue("`HashTable` incorrectly handles reference count when returning" + @ "an item with `TakeItem()`."); + // 1 ref after creation + 1 ref in `HastTable` => 2 + TEST_ExpectTrue(table.TakeItem(__().box.int(0))._getRefCount() == 2); + TEST_ExpectTrue(table.TakeItem(__().box.int(1))._getRefCount() == 2); + TEST_ExpectTrue(table.TakeItem(__().box.int(2))._getRefCount() == 2); + TEST_ExpectTrue(table.TakeItem(__().box.int(3))._getRefCount() == 2); + TEST_ExpectTrue(table.TakeItem(__().box.int(4))._getRefCount() == 2); + TEST_ExpectTrue(table.TakeItem(__().box.int(5))._getRefCount() == 2); + // 1 ref in `HastTable` => 1 + TEST_ExpectTrue(table.TakeItem(__().box.int(6))._getRefCount() == 1); + TEST_ExpectTrue(table.TakeItem(__().box.int(7))._getRefCount() == 1); + TEST_ExpectTrue(table.TakeItem(__().box.int(8))._getRefCount() == 1); + // Keys should get their refs decreased after the `Take()` command + TEST_ExpectTrue(default.keys[0]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[1]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[2]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[3]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[4]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[5]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[6]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[7]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[8]._getRefCount() == 1); +} + +protected static function SubTest_IncorrectGetEntryRefCount(HashTable table) +{ + Issue("`HashTable` incorrectly handles reference count when storing" @ + "an item."); + Issue("`HashTable` incorrectly handles reference count when returning" + @ "an item with `GetEntry()`."); + // 1 ref after creation + 1 ref in `HastTable` + 1 from `GetEntry()` => 3 + TEST_ExpectTrue(table.GetEntry(__().box.int(0)).key._getRefCount() == 3); + TEST_ExpectTrue(table.GetEntry(__().box.int(1)).key._getRefCount() == 3); + TEST_ExpectTrue(table.GetEntry(__().box.int(2)).key._getRefCount() == 3); + TEST_ExpectTrue(table.GetEntry(__().box.int(3)).key._getRefCount() == 3); + TEST_ExpectTrue(table.GetEntry(__().box.int(4)).key._getRefCount() == 3); + TEST_ExpectTrue(table.GetEntry(__().box.int(5)).key._getRefCount() == 3); + // 1ref after creation + 1 ref in `HastTable` + 1 after `GetEntry()` => 3 + TEST_ExpectTrue(table.GetEntry(__().box.int(6)).key._getRefCount() == 3); + TEST_ExpectTrue(table.GetEntry(__().box.int(7)).key._getRefCount() == 3); + TEST_ExpectTrue(table.GetEntry(__().box.int(8)).key._getRefCount() == 3); + + // 1 ref after creation + 1 ref in `HastTable` + // + 2 from two `GetEntry()`s => 4 + TEST_ExpectTrue(table.GetEntry(__().box.int(0)).value._getRefCount() == 4); + TEST_ExpectTrue(table.GetEntry(__().box.int(1)).value._getRefCount() == 4); + TEST_ExpectTrue(table.GetEntry(__().box.int(2)).value._getRefCount() == 4); + TEST_ExpectTrue(table.GetEntry(__().box.int(3)).value._getRefCount() == 4); + TEST_ExpectTrue(table.GetEntry(__().box.int(4)).value._getRefCount() == 4); + TEST_ExpectTrue(table.GetEntry(__().box.int(5)).value._getRefCount() == 4); + // 1 ref in `HastTable` + 2 from two `GetEntry()`s => 3 + TEST_ExpectTrue(table.GetEntry(__().box.int(6)).value._getRefCount() == 3); + TEST_ExpectTrue(table.GetEntry(__().box.int(7)).value._getRefCount() == 3); + TEST_ExpectTrue(table.GetEntry(__().box.int(8)).value._getRefCount() == 3); +} + +protected static function SubTest_IncorrectTakeEntryRefCount(HashTable table) +{ + Issue("`HashTable` incorrectly handles reference count when returning" + @ "an item with `TakeEntry()`."); + // 1 ref after creation + 1 ref in `HastTable` => 2 + TEST_ExpectTrue(table.TakeEntry(__().box.int(0)).value._getRefCount() == 2); + TEST_ExpectTrue(table.TakeEntry(__().box.int(1)).value._getRefCount() == 2); + TEST_ExpectTrue(table.TakeEntry(__().box.int(2)).value._getRefCount() == 2); + TEST_ExpectTrue(table.TakeEntry(__().box.int(3)).value._getRefCount() == 2); + TEST_ExpectTrue(table.TakeEntry(__().box.int(4)).value._getRefCount() == 2); + TEST_ExpectTrue(table.TakeEntry(__().box.int(5)).value._getRefCount() == 2); + // 1 ref in `HastTable` => 1 + TEST_ExpectTrue(table.TakeEntry(__().box.int(6)).value._getRefCount() == 1); + TEST_ExpectTrue(table.TakeEntry(__().box.int(7)).value._getRefCount() == 1); + TEST_ExpectTrue(table.TakeEntry(__().box.int(8)).value._getRefCount() == 1); + // Keys should get their refs unchanged after the `TakeEntry()` command + TEST_ExpectTrue(default.keys[0]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[1]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[2]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[3]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[4]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[5]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[6]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[7]._getRefCount() == 2); + TEST_ExpectTrue(default.keys[8]._getRefCount() == 2); +} + +protected static function SubTest_IncorrectEmptyRefCount(HashTable table) +{ + Issue("`HashTable` incorrectly handles reference count when emptying" + @ "`HashTable` with `Empty()`."); + table.Empty(); + // 1 ref after creation + now 0 ref in `HastTable` => 1 + TEST_ExpectTrue(default.mockedItems[0]._getRefCount() == 1); + TEST_ExpectTrue(default.mockedItems[1]._getRefCount() == 1); + TEST_ExpectTrue(default.mockedItems[2]._getRefCount() == 1); + TEST_ExpectTrue(default.mockedItems[3]._getRefCount() == 1); + TEST_ExpectTrue(default.mockedItems[4]._getRefCount() == 1); + TEST_ExpectTrue(default.mockedItems[5]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[0]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[1]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[2]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[3]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[4]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[5]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[6]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[7]._getRefCount() == 1); + TEST_ExpectTrue(default.keys[8]._getRefCount() == 1); +} + +protected static function SubTest_IncorrectGetKeysRefCount(HashTable table) +{ + Issue("`HashTable` incorrectly handles reference count when returning" + @ "keys with `GetKeys()`."); + table.GetKeys(); + // 1 ref after creation + 1 ref in `HastTable` + 1 after `GetKeys()` => 3 + TEST_ExpectTrue(default.keys[0]._getRefCount() == 3); + TEST_ExpectTrue(default.keys[1]._getRefCount() == 3); + TEST_ExpectTrue(default.keys[2]._getRefCount() == 3); + TEST_ExpectTrue(default.keys[3]._getRefCount() == 3); + TEST_ExpectTrue(default.keys[4]._getRefCount() == 3); + TEST_ExpectTrue(default.keys[5]._getRefCount() == 3); + TEST_ExpectTrue(default.keys[6]._getRefCount() == 3); + TEST_ExpectTrue(default.keys[7]._getRefCount() == 3); + TEST_ExpectTrue(default.keys[8]._getRefCount() == 3); +} + +protected static function Test_Take() +{ + local HashTable.Entry entry; + local HashTable array; + local array keys; + + class'MockItem'.default.objectCount = 0; + array = HashTable(__().memory.Allocate(class'HashTable')); + array.SetItem(__().box.int(0), NewMockItem()); + array.SetItem(__().box.int(1), NewMockItem()); + array.SetItem(__().box.int(2), NewMockItem()); + array.CreateItem(__().box.int(3), class'MockItem'); + array.CreateItem(__().box.int(4), class'MockItem'); + Context("Testing `TakeItem()`and `TakeEntry()` methods of `HashTable`."); + Issue("`TakeItem()` does not return stored value."); + TEST_ExpectTrue(array.TakeItem(__().box.int(0)).class == class'MockItem'); + TEST_ExpectTrue(array.TakeItem(__().box.int(3)).class == class'MockItem'); + + Issue("`TakeItem()` does not remove value from the hash table."); + TEST_ExpectNone(array.GetItem(__().box.int(0))); + TEST_ExpectNone(array.GetItem(__().box.int(3))); + + Issue("`TakeEntry()` does not return stored key / value."); + entry = array.TakeEntry(__().box.int(1)); + TEST_ExpectTrue(IntBox(entry.key).Get() == 1); + TEST_ExpectTrue(entry.value.class == class'MockItem'); + entry = array.TakeEntry(__().box.int(4)); + TEST_ExpectTrue(IntBox(entry.key).Get() == 4); + TEST_ExpectTrue(entry.value.class == class'MockItem'); + + Issue("`TakeEntry()` does not remove value from the hash table."); + TEST_ExpectNone(array.GetItem(__().box.int(1))); + TEST_ExpectNone(array.GetItem(__().box.int(4))); + + Issue("Keys are not removed by `Take()` / `TakeEntry()` commands."); + keys = array.GetKeys(); + TEST_ExpectTrue(keys.length == 1); + TEST_ExpectTrue(IntBox(keys[0]).Get() == 2); +} + +protected static function Test_LargeArray() +{ + local int i; + local AcediaObject nextKey; + local HashTable array; + + Context("Testing storing large amount of elements in `HashTable`."); + Issue("`HashTable` cannot handle large amount of elements."); + array = HashTable(__().memory.Allocate(class'HashTable')); + for (i = 0; i < 5000; i += 1) { + if (i % 2 == 0) { + nextKey = __().text.FromString("var" @ i); + } + else { + nextKey = __().box.int(i * 56 - 435632); + } + array.SetItem(nextKey, __().ref.int(i)); + } + for (i = 0; i < 5000; i += 1) { + if (i % 2 == 0) { + nextKey = __().text.FromString("var" @ i); + } + else { + nextKey = __().box.int(i * 56 - 435632); + } + TEST_ExpectTrue(IntRef(array.GetItem(nextKey)).Get() == i); + } +} + +defaultproperties +{ + caseGroup = "Collections" + caseName = "HashTable" +} \ No newline at end of file diff --git a/sources/Manifest.uc b/sources/Manifest.uc index a269e87..ec74b1b 100644 --- a/sources/Manifest.uc +++ b/sources/Manifest.uc @@ -46,7 +46,7 @@ defaultproperties testCases(15) = class'TEST_User' testCases(16) = class'TEST_Memory' testCases(17) = class'TEST_ArrayList' - testCases(18) = class'TEST_AssociativeArray' + testCases(18) = class'TEST_HashTable' testCases(19) = class'TEST_CollectionsMixed' testCases(20) = class'TEST_Iterator' testCases(21) = class'TEST_Command'