diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/simple_string_dictionary.cc (renamed from src/common/mac/SimpleStringDictionary.mm) | 20 | ||||
-rw-r--r-- | src/common/simple_string_dictionary.h (renamed from src/common/mac/SimpleStringDictionary.h) | 43 | ||||
-rw-r--r-- | src/common/simple_string_dictionary_unittest.cc | 221 |
3 files changed, 249 insertions, 35 deletions
diff --git a/src/common/mac/SimpleStringDictionary.mm b/src/common/simple_string_dictionary.cc index b97b760c..f28ee9ef 100644 --- a/src/common/mac/SimpleStringDictionary.mm +++ b/src/common/simple_string_dictionary.cc @@ -26,14 +26,10 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// SimpleStringDictionary.mm -// Simple string dictionary that does not allocate memory -// #include <assert.h> -#import "SimpleStringDictionary.h" +#include "common/simple_string_dictionary.h" namespace google_breakpad { @@ -50,7 +46,7 @@ int SimpleStringDictionary::GetCount() const { ++count; } } - + return count; } @@ -82,18 +78,18 @@ void SimpleStringDictionary::SetKeyValue(const char *key, assert(key); if (!key) return; - + // key must not be empty string assert(key[0] != '\0'); if (key[0] == '\0') return; - + int free_index = -1; - + // check if key already exists for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { KeyValueEntry &entry = entries_[i]; - + if (entry.IsActive()) { if (!strcmp(entry.GetKey(), key)) { entry.SetValue(value); @@ -106,10 +102,10 @@ void SimpleStringDictionary::SetKeyValue(const char *key, } } } - + // check if we've run out of space assert(free_index != -1); - + // Put new key into an empty slot (if found) if (free_index != -1) { entries_[free_index].SetKeyValue(key, value); diff --git a/src/common/mac/SimpleStringDictionary.h b/src/common/simple_string_dictionary.h index 814a6f7a..dbeaabc0 100644 --- a/src/common/mac/SimpleStringDictionary.h +++ b/src/common/simple_string_dictionary.h @@ -26,12 +26,9 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// SimpleStringDictionary.h -// -#ifndef SimpleStringDictionary_H__ -#define SimpleStringDictionary_H__ +#ifndef COMMON_SIMPLE_STRING_DICTIONARY_H_ +#define COMMON_SIMPLE_STRING_DICTIONARY_H_ #import <string> #import <vector> @@ -70,37 +67,37 @@ class KeyValueEntry { KeyValueEntry() { Clear(); } - + KeyValueEntry(const char *key, const char *value) { SetKeyValue(key, value); } - void SetKeyValue(const char *key, const char *value) { + void SetKeyValue(const char *key, const char *value) { if (!key) { key = ""; } if (!value) { value = ""; } - + strlcpy(key_, key, sizeof(key_)); strlcpy(value_, value, sizeof(value_)); - } + } - void SetValue(const char *value) { + void SetValue(const char *value) { if (!value) { value = ""; } strlcpy(value_, value, sizeof(value_)); }; - + // Removes the key/value - void Clear() { + void Clear() { memset(key_, 0, sizeof(key_)); memset(value_, 0, sizeof(value_)); } - bool IsActive() const { return key_[0] != '\0'; } + bool IsActive() const { return key_[0] != '\0'; } const char *GetKey() const { return key_; } const char *GetValue() const { return value_; } @@ -108,7 +105,7 @@ class KeyValueEntry { // of MachMessage (in MachIPC.h) // (see also struct KeyValueMessageData in Inspector.h) enum {MAX_STRING_STORAGE_SIZE = 256}; - + private: char key_[MAX_STRING_STORAGE_SIZE]; char value_[MAX_STRING_STORAGE_SIZE]; @@ -126,7 +123,7 @@ class KeyValueEntry { class SimpleStringDictionary { public: SimpleStringDictionary() {}; // entries will all be cleared - + // Returns the number of active key/value pairs. The upper limit for this // is MAX_NUM_ENTRIES. int GetCount() const; @@ -135,12 +132,12 @@ class SimpleStringDictionary { // If |key| is NULL, an assert will fire or NULL will be returned. If |key| // is not found or is an empty string, NULL is returned. const char *GetValueForKey(const char *key) const; - + // Stores a string |value| represented by |key|. If |key| is NULL or an empty // string, this will assert (or do nothing). If |value| is NULL then // the |key| will be removed. An empty string is OK for |value|. void SetKeyValue(const char *key, const char *value); - + // Given |key|, removes any associated value. It will assert (or do nothing) // if NULL is passed in. It will do nothing if |key| is not found. void RemoveKey(const char *key); @@ -155,7 +152,7 @@ class SimpleStringDictionary { const KeyValueEntry *GetEntry(int i) const; - KeyValueEntry entries_[MAX_NUM_ENTRIES]; + KeyValueEntry entries_[MAX_NUM_ENTRIES]; }; //============================================================================== @@ -169,7 +166,7 @@ class SimpleStringDictionaryIterator { void Start() { i_ = 0; } - + // like the nextObject method of NSEnumerator (in Cocoa) // returns NULL when there are no more entries // @@ -184,12 +181,12 @@ class SimpleStringDictionaryIterator { return NULL; // reached end of array } - + private: - const SimpleStringDictionary& dict_; - int i_; + const SimpleStringDictionary& dict_; + int i_; }; } // namespace google_breakpad -#endif // SimpleStringDictionary_H__ +#endif // COMMON_SIMPLE_STRING_DICTIONARY_H_ diff --git a/src/common/simple_string_dictionary_unittest.cc b/src/common/simple_string_dictionary_unittest.cc new file mode 100644 index 00000000..ff02115e --- /dev/null +++ b/src/common/simple_string_dictionary_unittest.cc @@ -0,0 +1,221 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "breakpad_googletest_includes.h" +#include "common/simple_string_dictionary.h" + +namespace google_breakpad { + +//============================================================================== +TEST(SimpleStringDictionaryTest, KeyValueEntry) { + KeyValueEntry entry; + + // Verify that initial state is correct + EXPECT_FALSE(entry.IsActive()); + EXPECT_EQ(strlen(entry.GetKey()), 0u); + EXPECT_EQ(strlen(entry.GetValue()), 0u); + + // Try setting a key/value and then verify + entry.SetKeyValue("key1", "value1"); + EXPECT_STREQ(entry.GetKey(), "key1"); + EXPECT_STREQ(entry.GetValue(), "value1"); + + // Try setting a new value + entry.SetValue("value3"); + + // Make sure the new value took + EXPECT_STREQ(entry.GetValue(), "value3"); + + // Make sure the key didn't change + EXPECT_STREQ(entry.GetKey(), "key1"); + + // Try setting a new key/value and then verify + entry.SetKeyValue("key2", "value2"); + EXPECT_STREQ(entry.GetKey(), "key2"); + EXPECT_STREQ(entry.GetValue(), "value2"); + + // Clear the entry and verify the key and value are empty strings + entry.Clear(); + EXPECT_FALSE(entry.IsActive()); + EXPECT_EQ(strlen(entry.GetKey()), 0u); + EXPECT_EQ(strlen(entry.GetValue()), 0u); +} + +TEST(SimpleStringDictionaryTest, EmptyKeyValueCombos) { + KeyValueEntry entry; + entry.SetKeyValue(NULL, NULL); + EXPECT_STREQ(entry.GetKey(), ""); + EXPECT_STREQ(entry.GetValue(), ""); +} + + +//============================================================================== +TEST(SimpleStringDictionaryTest, SimpleStringDictionary) { + // Make a new dictionary + SimpleStringDictionary *dict = new SimpleStringDictionary(); + ASSERT_TRUE(dict); + + // Set three distinct values on three keys + dict->SetKeyValue("key1", "value1"); + dict->SetKeyValue("key2", "value2"); + dict->SetKeyValue("key3", "value3"); + + EXPECT_NE(dict->GetValueForKey("key1"), "value1"); + EXPECT_NE(dict->GetValueForKey("key2"), "value2"); + EXPECT_NE(dict->GetValueForKey("key3"), "value3"); + EXPECT_EQ(dict->GetCount(), 3); + // try an unknown key + EXPECT_FALSE(dict->GetValueForKey("key4")); + + // Remove a key + dict->RemoveKey("key3"); + + // Now make sure it's not there anymore + EXPECT_FALSE(dict->GetValueForKey("key3")); + + // Remove by setting value to NULL + dict->SetKeyValue("key2", NULL); + + // Now make sure it's not there anymore + EXPECT_FALSE(dict->GetValueForKey("key2")); +} + +//============================================================================== +// The idea behind this test is to add a bunch of values to the dictionary, +// remove some in the middle, then add a few more in. We then create a +// SimpleStringDictionaryIterator and iterate through the dictionary, taking +// note of the key/value pairs we see. We then verify that it iterates +// through exactly the number of key/value pairs we expect, and that they +// match one-for-one with what we would expect. In all cases we're setting +// key value pairs of the form: +// +// key<n>/value<n> (like key0/value0, key17,value17, etc.) +// +TEST(SimpleStringDictionaryTest, SimpleStringDictionaryIterator) { + SimpleStringDictionary *dict = new SimpleStringDictionary(); + ASSERT_TRUE(dict); + + char key[KeyValueEntry::MAX_STRING_STORAGE_SIZE]; + char value[KeyValueEntry::MAX_STRING_STORAGE_SIZE]; + + const int kDictionaryCapacity = SimpleStringDictionary::MAX_NUM_ENTRIES; + const int kPartitionIndex = kDictionaryCapacity - 5; + + // We assume at least this size in the tests below + ASSERT_GE(kDictionaryCapacity, 64); + + // We'll keep track of the number of key/value pairs we think should + // be in the dictionary + int expectedDictionarySize = 0; + + // Set a bunch of key/value pairs like key0/value0, key1/value1, ... + for (int i = 0; i < kPartitionIndex; ++i) { + sprintf(key, "key%d", i); + sprintf(value, "value%d", i); + dict->SetKeyValue(key, value); + } + expectedDictionarySize = kPartitionIndex; + + // set a couple of the keys twice (with the same value) - should be nop + dict->SetKeyValue("key2", "value2"); + dict->SetKeyValue("key4", "value4"); + dict->SetKeyValue("key15", "value15"); + + // Remove some random elements in the middle + dict->RemoveKey("key7"); + dict->RemoveKey("key18"); + dict->RemoveKey("key23"); + dict->RemoveKey("key31"); + expectedDictionarySize -= 4; // we just removed four key/value pairs + + // Set some more key/value pairs like key59/value59, key60/value60, ... + for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) { + sprintf(key, "key%d", i); + sprintf(value, "value%d", i); + dict->SetKeyValue(key, value); + } + expectedDictionarySize += kDictionaryCapacity - kPartitionIndex; + + // Now create an iterator on the dictionary + SimpleStringDictionaryIterator iter(*dict); + + // We then verify that it iterates through exactly the number of + // key/value pairs we expect, and that they match one-for-one with what we + // would expect. The ordering of the iteration does not matter... + + // used to keep track of number of occurrences found for key/value pairs + int count[kDictionaryCapacity]; + memset(count, 0, sizeof(count)); + + int totalCount = 0; + + const KeyValueEntry *entry; + + while ((entry = iter.Next())) { + totalCount++; + + // Extract keyNumber from a string of the form key<keyNumber> + int keyNumber; + sscanf(entry->GetKey(), "key%d", &keyNumber); + + // Extract valueNumber from a string of the form value<valueNumber> + int valueNumber; + sscanf(entry->GetValue(), "value%d", &valueNumber); + + // The value number should equal the key number since that's how we set them + EXPECT_EQ(keyNumber, valueNumber); + + // Key and value numbers should be in proper range: + // 0 <= keyNumber < kDictionaryCapacity + bool isKeyInGoodRange = + (keyNumber >= 0 && keyNumber < kDictionaryCapacity); + bool isValueInGoodRange = + (valueNumber >= 0 && valueNumber < kDictionaryCapacity); + EXPECT_TRUE(isKeyInGoodRange); + EXPECT_TRUE(isValueInGoodRange); + + if (isKeyInGoodRange && isValueInGoodRange) { + ++count[keyNumber]; + } + } + + // Make sure each of the key/value pairs showed up exactly one time, except + // for the ones which we removed. + for (int i = 0; i < kDictionaryCapacity; ++i) { + // Skip over key7, key18, key23, and key31, since we removed them + if (!(i == 7 || i == 18 || i == 23 || i == 31)) { + EXPECT_EQ(count[i], 1); + } + } + + // Make sure the number of iterations matches the expected dictionary size. + EXPECT_EQ(totalCount, expectedDictionarySize); +} + +} // namespace google_breakpad |