michael@0: // Copyright (c) 2008, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: #import "SimpleStringDictionaryTest.h" michael@0: #import "SimpleStringDictionary.h" michael@0: michael@0: using google_breakpad::KeyValueEntry; michael@0: using google_breakpad::SimpleStringDictionary; michael@0: using google_breakpad::SimpleStringDictionaryIterator; michael@0: michael@0: @implementation SimpleStringDictionaryTest michael@0: michael@0: //============================================================================== michael@0: - (void)testKeyValueEntry { michael@0: KeyValueEntry entry; michael@0: michael@0: // Verify that initial state is correct michael@0: STAssertFalse(entry.IsActive(), @"Initial key value entry is active!"); michael@0: STAssertEquals(strlen(entry.GetKey()), (size_t)0, @"Empty key value did not " michael@0: @"have length 0"); michael@0: STAssertEquals(strlen(entry.GetValue()), (size_t)0, @"Empty key value did not " michael@0: @"have length 0"); michael@0: michael@0: // Try setting a key/value and then verify michael@0: entry.SetKeyValue("key1", "value1"); michael@0: STAssertEqualCStrings(entry.GetKey(), "key1", @"key was not equal to key1"); michael@0: STAssertEqualCStrings(entry.GetValue(), "value1", @"value was not equal"); michael@0: michael@0: // Try setting a new value michael@0: entry.SetValue("value3"); michael@0: michael@0: // Make sure the new value took michael@0: STAssertEqualCStrings(entry.GetValue(), "value3", @"value was not equal"); michael@0: michael@0: // Make sure the key didn't change michael@0: STAssertEqualCStrings(entry.GetKey(), "key1", @"key changed after setting " michael@0: @"value!"); michael@0: michael@0: // Try setting a new key/value and then verify michael@0: entry.SetKeyValue("key2", "value2"); michael@0: STAssertEqualCStrings(entry.GetKey(), "key2", @"New key was not equal to " michael@0: @"key2"); michael@0: STAssertEqualCStrings(entry.GetValue(), "value2", @"New value was not equal " michael@0: @"to value2"); michael@0: michael@0: // Clear the entry and verify the key and value are empty strings michael@0: entry.Clear(); michael@0: STAssertFalse(entry.IsActive(), @"Key value clear did not clear object"); michael@0: STAssertEquals(strlen(entry.GetKey()), (size_t)0, @"Length of cleared key " michael@0: @"was not 0"); michael@0: STAssertEquals(strlen(entry.GetValue()), (size_t)0, @"Length of cleared " michael@0: @"value was not 0!"); michael@0: } michael@0: michael@0: - (void)testEmptyKeyValueCombos { michael@0: KeyValueEntry entry; michael@0: entry.SetKeyValue(NULL, NULL); michael@0: STAssertEqualCStrings(entry.GetKey(), "", @"Setting NULL key did not return " michael@0: @"empty key!"); michael@0: STAssertEqualCStrings(entry.GetValue(), "", @"Setting NULL value did not " michael@0: @"set empty string value!"); michael@0: } michael@0: michael@0: michael@0: //============================================================================== michael@0: - (void)testSimpleStringDictionary { michael@0: // Make a new dictionary michael@0: SimpleStringDictionary *dict = new SimpleStringDictionary(); michael@0: STAssertTrue(dict != NULL, nil); michael@0: michael@0: // try passing in NULL for key michael@0: //dict->SetKeyValue(NULL, "bad"); // causes assert() to fire michael@0: michael@0: // Set three distinct values on three keys michael@0: dict->SetKeyValue("key1", "value1"); michael@0: dict->SetKeyValue("key2", "value2"); michael@0: dict->SetKeyValue("key3", "value3"); michael@0: michael@0: STAssertTrue(!strcmp(dict->GetValueForKey("key1"), "value1"), nil); michael@0: STAssertTrue(!strcmp(dict->GetValueForKey("key2"), "value2"), nil); michael@0: STAssertTrue(!strcmp(dict->GetValueForKey("key3"), "value3"), nil); michael@0: STAssertEquals(dict->GetCount(), 3, @"GetCount did not return 3"); michael@0: // try an unknown key michael@0: STAssertTrue(dict->GetValueForKey("key4") == NULL, nil); michael@0: michael@0: // try a NULL key michael@0: //STAssertTrue(dict->GetValueForKey(NULL) == NULL, nil); // asserts michael@0: michael@0: // Remove a key michael@0: dict->RemoveKey("key3"); michael@0: michael@0: // Now make sure it's not there anymore michael@0: STAssertTrue(dict->GetValueForKey("key3") == NULL, nil); michael@0: michael@0: // Remove a NULL key michael@0: //dict->RemoveKey(NULL); // will cause assert() to fire michael@0: michael@0: // Remove by setting value to NULL michael@0: dict->SetKeyValue("key2", NULL); michael@0: michael@0: // Now make sure it's not there anymore michael@0: STAssertTrue(dict->GetValueForKey("key2") == NULL, nil); michael@0: } michael@0: michael@0: //============================================================================== michael@0: // The idea behind this test is to add a bunch of values to the dictionary, michael@0: // remove some in the middle, then add a few more in. We then create a michael@0: // SimpleStringDictionaryIterator and iterate through the dictionary, taking michael@0: // note of the key/value pairs we see. We then verify that it iterates michael@0: // through exactly the number of key/value pairs we expect, and that they michael@0: // match one-for-one with what we would expect. In all cases we're setting michael@0: // key value pairs of the form: michael@0: // michael@0: // key/value (like key0/value0, key17,value17, etc.) michael@0: // michael@0: - (void)testSimpleStringDictionaryIterator { michael@0: SimpleStringDictionary *dict = new SimpleStringDictionary(); michael@0: STAssertTrue(dict != NULL, nil); michael@0: michael@0: char key[KeyValueEntry::MAX_STRING_STORAGE_SIZE]; michael@0: char value[KeyValueEntry::MAX_STRING_STORAGE_SIZE]; michael@0: michael@0: const int kDictionaryCapacity = SimpleStringDictionary::MAX_NUM_ENTRIES; michael@0: const int kPartitionIndex = kDictionaryCapacity - 5; michael@0: michael@0: // We assume at least this size in the tests below michael@0: STAssertTrue(kDictionaryCapacity >= 64, nil); michael@0: michael@0: // We'll keep track of the number of key/value pairs we think should michael@0: // be in the dictionary michael@0: int expectedDictionarySize = 0; michael@0: michael@0: // Set a bunch of key/value pairs like key0/value0, key1/value1, ... michael@0: for (int i = 0; i < kPartitionIndex; ++i) { michael@0: sprintf(key, "key%d", i); michael@0: sprintf(value, "value%d", i); michael@0: dict->SetKeyValue(key, value); michael@0: } michael@0: expectedDictionarySize = kPartitionIndex; michael@0: michael@0: // set a couple of the keys twice (with the same value) - should be nop michael@0: dict->SetKeyValue("key2", "value2"); michael@0: dict->SetKeyValue("key4", "value4"); michael@0: dict->SetKeyValue("key15", "value15"); michael@0: michael@0: // Remove some random elements in the middle michael@0: dict->RemoveKey("key7"); michael@0: dict->RemoveKey("key18"); michael@0: dict->RemoveKey("key23"); michael@0: dict->RemoveKey("key31"); michael@0: expectedDictionarySize -= 4; // we just removed four key/value pairs michael@0: michael@0: // Set some more key/value pairs like key59/value59, key60/value60, ... michael@0: for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) { michael@0: sprintf(key, "key%d", i); michael@0: sprintf(value, "value%d", i); michael@0: dict->SetKeyValue(key, value); michael@0: } michael@0: expectedDictionarySize += kDictionaryCapacity - kPartitionIndex; michael@0: michael@0: // Now create an iterator on the dictionary michael@0: SimpleStringDictionaryIterator iter(*dict); michael@0: michael@0: // We then verify that it iterates through exactly the number of michael@0: // key/value pairs we expect, and that they match one-for-one with what we michael@0: // would expect. The ordering of the iteration does not matter... michael@0: michael@0: // used to keep track of number of occurrences found for key/value pairs michael@0: int count[kDictionaryCapacity]; michael@0: memset(count, 0, sizeof(count)); michael@0: michael@0: int totalCount = 0; michael@0: michael@0: const KeyValueEntry *entry; michael@0: michael@0: while ((entry = iter.Next())) { michael@0: totalCount++; michael@0: michael@0: // Extract keyNumber from a string of the form key michael@0: int keyNumber; michael@0: sscanf(entry->GetKey(), "key%d", &keyNumber); michael@0: michael@0: // Extract valueNumber from a string of the form value michael@0: int valueNumber; michael@0: sscanf(entry->GetValue(), "value%d", &valueNumber); michael@0: michael@0: // The value number should equal the key number since that's how we set them michael@0: STAssertTrue(keyNumber == valueNumber, nil); michael@0: michael@0: // Key and value numbers should be in proper range: michael@0: // 0 <= keyNumber < kDictionaryCapacity michael@0: bool isKeyInGoodRange = michael@0: (keyNumber >= 0 && keyNumber < kDictionaryCapacity); michael@0: bool isValueInGoodRange = michael@0: (valueNumber >= 0 && valueNumber < kDictionaryCapacity); michael@0: STAssertTrue(isKeyInGoodRange, nil); michael@0: STAssertTrue(isValueInGoodRange, nil); michael@0: michael@0: if (isKeyInGoodRange && isValueInGoodRange) { michael@0: ++count[keyNumber]; michael@0: } michael@0: } michael@0: michael@0: // Make sure each of the key/value pairs showed up exactly one time, except michael@0: // for the ones which we removed. michael@0: for (int i = 0; i < kDictionaryCapacity; ++i) { michael@0: // Skip over key7, key18, key23, and key31, since we removed them michael@0: if (!(i == 7 || i == 18 || i == 23 || i == 31)) { michael@0: STAssertTrue(count[i] == 1, nil); michael@0: } michael@0: } michael@0: michael@0: // Make sure the number of iterations matches the expected dictionary size. michael@0: STAssertTrue(totalCount == expectedDictionarySize, nil); michael@0: } michael@0: michael@0: @end