| // Copyright (c) 2017, 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 <algorithm> |
| #include <string> |
| |
| #include "breakpad_googletest_includes.h" |
| #include "common/long_string_dictionary.h" |
| |
| namespace google_breakpad { |
| |
| using std::string; |
| |
| TEST(LongStringDictionary, LongStringDictionary) { |
| // Make a new dictionary |
| LongStringDictionary dict; |
| |
| // Set three distinct values on three keys |
| dict.SetKeyValue("key1", "value1"); |
| dict.SetKeyValue("key2", "value2"); |
| dict.SetKeyValue("key3", "value3"); |
| |
| EXPECT_EQ("value1", dict.GetValueForKey("key1")); |
| EXPECT_EQ("value2", dict.GetValueForKey("key2")); |
| EXPECT_EQ("value3", dict.GetValueForKey("key3")); |
| EXPECT_EQ(3u, dict.GetCount()); |
| // try an unknown key |
| EXPECT_EQ("", dict.GetValueForKey("key4")); |
| |
| // Remove a key |
| dict.RemoveKey("key3"); |
| |
| // Now make sure it's not there anymore |
| EXPECT_EQ("", dict.GetValueForKey("key3")); |
| |
| // Remove by setting value to NULL |
| dict.SetKeyValue("key2", NULL); |
| |
| // Now make sure it's not there anymore |
| EXPECT_EQ("", dict.GetValueForKey("key2")); |
| } |
| |
| // Add a bunch of values to the dictionary, remove some entries in the middle, |
| // and then add more. |
| TEST(LongStringDictionary, Iterator) { |
| LongStringDictionary* dict = new LongStringDictionary(); |
| ASSERT_TRUE(dict); |
| |
| char key[LongStringDictionary::key_size]; |
| char value[LongStringDictionary::value_size]; |
| |
| const int kDictionaryCapacity = LongStringDictionary::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 |
| SimpleStringDictionary::Iterator 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 SimpleStringDictionary::Entry* entry; |
| while ((entry = iter.Next())) { |
| totalCount++; |
| |
| // Extract keyNumber from a string of the form key<keyNumber> |
| int keyNumber; |
| sscanf(entry->key, "key%d", &keyNumber); |
| |
| // Extract valueNumber from a string of the form value<valueNumber> |
| int valueNumber; |
| sscanf(entry->value, "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 (size_t 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); |
| } |
| |
| TEST(LongStringDictionary, AddRemove) { |
| LongStringDictionary dict; |
| dict.SetKeyValue("rob", "ert"); |
| dict.SetKeyValue("mike", "pink"); |
| dict.SetKeyValue("mark", "allays"); |
| |
| EXPECT_EQ(3u, dict.GetCount()); |
| EXPECT_EQ("ert", dict.GetValueForKey("rob")); |
| EXPECT_EQ("pink", dict.GetValueForKey("mike")); |
| EXPECT_EQ("allays", dict.GetValueForKey("mark")); |
| |
| dict.RemoveKey("mike"); |
| |
| EXPECT_EQ(2u, dict.GetCount()); |
| EXPECT_EQ("", dict.GetValueForKey("mike")); |
| |
| dict.SetKeyValue("mark", "mal"); |
| EXPECT_EQ(2u, dict.GetCount()); |
| EXPECT_EQ("mal", dict.GetValueForKey("mark")); |
| |
| dict.RemoveKey("mark"); |
| EXPECT_EQ(1u, dict.GetCount()); |
| EXPECT_EQ("", dict.GetValueForKey("mark")); |
| } |
| |
| TEST(LongStringDictionary, AddRemoveLongValue) { |
| LongStringDictionary dict; |
| |
| string long_value = string(256, 'x'); |
| dict.SetKeyValue("rob", long_value.c_str()); |
| |
| EXPECT_EQ(2u, dict.GetCount()); |
| |
| string long_value_part_1 = string(255, 'x'); |
| |
| EXPECT_EQ(long_value_part_1, dict.GetValueForKey("rob__1")); |
| EXPECT_EQ("x", dict.GetValueForKey("rob__2")); |
| |
| EXPECT_EQ(long_value, dict.GetValueForKey("rob")); |
| |
| dict.RemoveKey("rob"); |
| EXPECT_EQ(0u, dict.GetCount()); |
| } |
| |
| TEST(LongStringDictionary, AddRemoveSuperLongValue) { |
| LongStringDictionary dict; |
| |
| string long_value = string(255 * 10, 'x'); |
| dict.SetKeyValue("rob", long_value.c_str()); |
| |
| EXPECT_EQ(10u, dict.GetCount()); |
| |
| string long_value_part = string(255, 'x'); |
| |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__1")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__2")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__3")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__4")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__5")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__6")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__7")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__8")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__9")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__10")); |
| EXPECT_EQ(10u, dict.GetCount()); |
| |
| EXPECT_EQ(long_value, dict.GetValueForKey("rob")); |
| |
| dict.RemoveKey("rob"); |
| EXPECT_EQ(0u, dict.GetCount()); |
| } |
| |
| TEST(LongStringDictionary, TruncateSuperLongValue) { |
| LongStringDictionary dict; |
| |
| string long_value = string(255 * 11, 'x'); |
| dict.SetKeyValue("rob", long_value.c_str()); |
| |
| EXPECT_EQ(10u, dict.GetCount()); |
| |
| string long_value_part = string(255, 'x'); |
| |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__1")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__2")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__3")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__4")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__5")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__6")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__7")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__8")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__9")); |
| EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__10")); |
| EXPECT_EQ(10u, dict.GetCount()); |
| |
| string expected_long_value = string(255 * 10, 'x'); |
| EXPECT_EQ(expected_long_value, dict.GetValueForKey("rob")); |
| |
| dict.RemoveKey("rob"); |
| EXPECT_EQ(0u, dict.GetCount()); |
| } |
| |
| TEST(LongStringDictionary, OverrideLongValue) { |
| LongStringDictionary dict; |
| |
| string long_value = string(255 * 10, 'x'); |
| dict.SetKeyValue("rob", long_value.c_str()); |
| |
| EXPECT_EQ(10u, dict.GetCount()); |
| EXPECT_EQ(long_value, dict.GetValueForKey("rob")); |
| |
| dict.SetKeyValue("rob", "short_value"); |
| |
| EXPECT_EQ(1u, dict.GetCount()); |
| EXPECT_EQ("short_value", dict.GetValueForKey("rob")); |
| } |
| |
| TEST(LongStringDictionary, OverrideShortValue) { |
| LongStringDictionary dict; |
| |
| dict.SetKeyValue("rob", "short_value"); |
| |
| EXPECT_EQ(1u, dict.GetCount()); |
| EXPECT_EQ("short_value", dict.GetValueForKey("rob")); |
| |
| string long_value = string(255 * 10, 'x'); |
| dict.SetKeyValue("rob", long_value.c_str()); |
| |
| EXPECT_EQ(10u, dict.GetCount()); |
| EXPECT_EQ(long_value, dict.GetValueForKey("rob")); |
| } |
| |
| } // namespace google_breakpad |