// Copyright (c) 2010 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. // static_map_unittest.cc: Unit tests for StaticMap. // // Author: Siyang Xie (lambxsy@google.com) #include <climits> #include <map> #include "breakpad_googletest_includes.h" #include "processor/static_map-inl.h" typedef int ValueType; typedef int KeyType; typedef google_breakpad::StaticMap< KeyType, ValueType > TestMap; typedef std::map< KeyType, ValueType > StdMap; template<typename Key, typename Value> class SimpleMapSerializer { public: static char* Serialize(const std::map<Key, Value> &stdmap, unsigned int* size = NULL) { unsigned int size_per_node = sizeof(uint32_t) + sizeof(Key) + sizeof(Value); unsigned int memsize = sizeof(int32_t) + size_per_node * stdmap.size(); if (size) *size = memsize; // Allocate memory for serialized data: char* mem = reinterpret_cast<char*>(operator new(memsize)); char* address = mem; // Writer the number of nodes: new (address) uint32_t(static_cast<uint32_t>(stdmap.size())); address += sizeof(uint32_t); // Nodes' offset: uint32_t* offsets = reinterpret_cast<uint32_t*>(address); address += sizeof(uint32_t) * stdmap.size(); // Keys: Key* keys = reinterpret_cast<Key*>(address); address += sizeof(Key) * stdmap.size(); // Traversing map: typename std::map<Key, Value>::const_iterator iter = stdmap.begin(); for (int index = 0; iter != stdmap.end(); ++iter, ++index) { offsets[index] = static_cast<unsigned int>(address - mem); keys[index] = iter->first; new (address) Value(iter->second); address += sizeof(Value); } return mem; } }; class TestInvalidMap : public ::testing::Test { protected: void SetUp() { memset(data, 0, kMemorySize); } // 40 Bytes memory can hold a StaticMap with up to 3 nodes. static const int kMemorySize = 40; char data[kMemorySize]; TestMap test_map; }; TEST_F(TestInvalidMap, TestNegativeNumberNodes) { memset(data, 0xff, sizeof(uint32_t)); // Set the number of nodes = -1 test_map = TestMap(data); ASSERT_FALSE(test_map.ValidateInMemoryStructure()); } TEST_F(TestInvalidMap, TestWrongOffsets) { uint32_t* header = reinterpret_cast<uint32_t*>(data); const uint32_t kNumNodes = 2; const uint32_t kHeaderOffset = sizeof(uint32_t) + kNumNodes * (sizeof(uint32_t) + sizeof(KeyType)); header[0] = kNumNodes; header[1] = kHeaderOffset + 3; // Wrong offset for first node test_map = TestMap(data); ASSERT_FALSE(test_map.ValidateInMemoryStructure()); header[1] = kHeaderOffset; // Correct offset for first node header[2] = kHeaderOffset - 1; // Wrong offset for second node test_map = TestMap(data); ASSERT_FALSE(test_map.ValidateInMemoryStructure()); } TEST_F(TestInvalidMap, TestUnSortedKeys) { uint32_t* header = reinterpret_cast<uint32_t*>(data); const uint32_t kNumNodes = 2; const uint32_t kHeaderOffset = sizeof(uint32_t) + kNumNodes * (sizeof(uint32_t) + sizeof(KeyType)); header[0] = kNumNodes; header[1] = kHeaderOffset; header[2] = kHeaderOffset + sizeof(ValueType); KeyType* keys = reinterpret_cast<KeyType*>( data + (kNumNodes + 1) * sizeof(uint32_t)); // Set keys in non-increasing order. keys[0] = 10; keys[1] = 7; test_map = TestMap(data); ASSERT_FALSE(test_map.ValidateInMemoryStructure()); } class TestValidMap : public ::testing::Test { protected: void SetUp() { int testcase = 0; // Empty map. map_data[testcase] = serializer.Serialize(std_map[testcase], &size[testcase]); test_map[testcase] = TestMap(map_data[testcase]); ++testcase; // Single element. std_map[testcase].insert(std::make_pair(2, 8)); map_data[testcase] = serializer.Serialize(std_map[testcase], &size[testcase]); test_map[testcase] = TestMap(map_data[testcase]); ++testcase; // 100 elements. for (int i = 0; i < 100; ++i) std_map[testcase].insert(std::make_pair(i, 2 * i)); map_data[testcase] = serializer.Serialize(std_map[testcase], &size[testcase]); test_map[testcase] = TestMap(map_data[testcase]); ++testcase; // 1000 random elements. for (int i = 0; i < 1000; ++i) std_map[testcase].insert(std::make_pair(rand(), rand())); map_data[testcase] = serializer.Serialize(std_map[testcase], &size[testcase]); test_map[testcase] = TestMap(map_data[testcase]); // Set correct size of memory allocation for each test case. unsigned int size_per_node = sizeof(uint32_t) + sizeof(KeyType) + sizeof(ValueType); for (testcase = 0; testcase < kNumberTestCases; ++testcase) { correct_size[testcase] = sizeof(uint32_t) + std_map[testcase].size() * size_per_node; } } void TearDown() { for (int i = 0;i < kNumberTestCases; ++i) delete map_data[i]; } void IteratorTester(int test_case) { // scan through: iter_test = test_map[test_case].begin(); iter_std = std_map[test_case].begin(); for (; iter_test != test_map[test_case].end() && iter_std != std_map[test_case].end(); ++iter_test, ++iter_std) { ASSERT_EQ(iter_test.GetKey(), iter_std->first); ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second); } ASSERT_TRUE(iter_test == test_map[test_case].end() && iter_std == std_map[test_case].end()); // Boundary testcase. if (!std_map[test_case].empty()) { // rear boundary case: iter_test = test_map[test_case].end(); iter_std = std_map[test_case].end(); --iter_std; --iter_test; ASSERT_EQ(iter_test.GetKey(), iter_std->first); ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second); ++iter_test; ++iter_std; ASSERT_TRUE(iter_test == test_map[test_case].end()); --iter_test; --iter_std; ASSERT_TRUE(iter_test != test_map[test_case].end()); ASSERT_TRUE(iter_test == test_map[test_case].last()); ASSERT_EQ(iter_test.GetKey(), iter_std->first); ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second); // front boundary case: iter_test = test_map[test_case].begin(); --iter_test; ASSERT_TRUE(iter_test == test_map[test_case].begin()); } } void CompareLookupResult(int test_case) { bool found1 = (iter_test != test_map[test_case].end()); bool found2 = (iter_std != std_map[test_case].end()); ASSERT_EQ(found1, found2); if (found1 && found2) { ASSERT_EQ(iter_test.GetKey(), iter_std->first); ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second); } } void FindTester(int test_case, const KeyType &key) { iter_test = test_map[test_case].find(key); iter_std = std_map[test_case].find(key); CompareLookupResult(test_case); } void LowerBoundTester(int test_case, const KeyType &key) { iter_test = test_map[test_case].lower_bound(key); iter_std = std_map[test_case].lower_bound(key); CompareLookupResult(test_case); } void UpperBoundTester(int test_case, const KeyType &key) { iter_test = test_map[test_case].upper_bound(key); iter_std = std_map[test_case].upper_bound(key); CompareLookupResult(test_case); } void LookupTester(int test_case) { StdMap::const_iterator iter; // Test find(): for (iter = std_map[test_case].begin(); iter != std_map[test_case].end(); ++iter) { FindTester(test_case, iter->first); FindTester(test_case, iter->first + 1); FindTester(test_case, iter->first - 1); } FindTester(test_case, INT_MIN); FindTester(test_case, INT_MAX); // random test: for (int i = 0; i < rand()%5000 + 5000; ++i) FindTester(test_case, rand()); // Test lower_bound(): for (iter = std_map[test_case].begin(); iter != std_map[test_case].end(); ++iter) { LowerBoundTester(test_case, iter->first); LowerBoundTester(test_case, iter->first + 1); LowerBoundTester(test_case, iter->first - 1); } LowerBoundTester(test_case, INT_MIN); LowerBoundTester(test_case, INT_MAX); // random test: for (int i = 0; i < rand()%5000 + 5000; ++i) LowerBoundTester(test_case, rand()); // Test upper_bound(): for (iter = std_map[test_case].begin(); iter != std_map[test_case].end(); ++iter) { UpperBoundTester(test_case, iter->first); UpperBoundTester(test_case, iter->first + 1); UpperBoundTester(test_case, iter->first - 1); } UpperBoundTester(test_case, INT_MIN); UpperBoundTester(test_case, INT_MAX); // random test: for (int i = 0; i < rand()%5000 + 5000; ++i) UpperBoundTester(test_case, rand()); } static const int kNumberTestCases = 4; StdMap std_map[kNumberTestCases]; TestMap test_map[kNumberTestCases]; TestMap::const_iterator iter_test; StdMap::const_iterator iter_std; char* map_data[kNumberTestCases]; unsigned int size[kNumberTestCases]; unsigned int correct_size[kNumberTestCases]; SimpleMapSerializer<KeyType, ValueType> serializer; }; TEST_F(TestValidMap, TestEmptyMap) { int test_case = 0; // Assert memory size allocated during serialization is correct. ASSERT_EQ(correct_size[test_case], size[test_case]); // Sanity check of serialized data: ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure()); ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty()); ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size()); // Test Iterator. IteratorTester(test_case); // Test lookup operations. LookupTester(test_case); } TEST_F(TestValidMap, TestSingleElement) { int test_case = 1; // Assert memory size allocated during serialization is correct. ASSERT_EQ(correct_size[test_case], size[test_case]); // Sanity check of serialized data: ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure()); ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty()); ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size()); // Test Iterator. IteratorTester(test_case); // Test lookup operations. LookupTester(test_case); } TEST_F(TestValidMap, Test100Elements) { int test_case = 2; // Assert memory size allocated during serialization is correct. ASSERT_EQ(correct_size[test_case], size[test_case]); // Sanity check of serialized data: ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure()); ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty()); ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size()); // Test Iterator. IteratorTester(test_case); // Test lookup operations. LookupTester(test_case); } TEST_F(TestValidMap, Test1000RandomElements) { int test_case = 3; // Assert memory size allocated during serialization is correct. ASSERT_EQ(correct_size[test_case], size[test_case]); // Sanity check of serialized data: ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure()); ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty()); ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size()); // Test Iterator. IteratorTester(test_case); // Test lookup operations. LookupTester(test_case); } int main(int argc, char *argv[]) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }