1 // Copyright (c) 2010 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 // static_range_map_unittest.cc: Unit tests for StaticRangeMap.
31 //
32 // Author: Siyang Xie (lambxsy@google.com)
33 
34 #include "breakpad_googletest_includes.h"
35 #include "common/scoped_ptr.h"
36 #include "processor/range_map-inl.h"
37 #include "processor/static_range_map-inl.h"
38 #include "processor/simple_serializer-inl.h"
39 #include "processor/map_serializers-inl.h"
40 #include "processor/logging.h"
41 
42 
43 namespace {
44 // Types used for testing.
45 typedef int AddressType;
46 typedef int EntryType;
47 typedef google_breakpad::StaticRangeMap< AddressType, EntryType > TestMap;
48 typedef google_breakpad::RangeMap< AddressType, EntryType > RMap;
49 
50 // RangeTest contains data to use for store and retrieve tests.  See
51 // RunTests for descriptions of the tests.
52 struct RangeTest {
53   // Base address to use for test
54   AddressType address;
55 
56   // Size of range to use for test
57   AddressType size;
58 
59   // Unique ID of range - unstorable ranges must have unique IDs too
60   EntryType id;
61 
62   // Whether this range is expected to be stored successfully or not
63   bool expect_storable;
64 };
65 
66 // A RangeTestSet encompasses multiple RangeTests, which are run in
67 // sequence on the same RangeMap.
68 struct RangeTestSet {
69   // An array of RangeTests
70   const RangeTest* range_tests;
71 
72   // The number of tests in the set
73   unsigned int range_test_count;
74 };
75 
76 // These tests will be run sequentially.  The first set of tests exercises
77 // most functions of RangeTest, and verifies all of the bounds-checking.
78 const RangeTest range_tests_0[] = {
79   { INT_MIN,     16,      1,  true },   // lowest possible range
80   { -2,          5,       2,  true },   // a range through zero
81   { INT_MAX - 9, 11,      3,  false },  // tests anti-overflow
82   { INT_MAX - 9, 10,      4,  true },   // highest possible range
83   { 5,           0,       5,  false },  // tests anti-zero-size
84   { 5,           1,       6,  true },   // smallest possible range
85   { -20,         15,      7,  true },   // entirely negative
86 
87   { 10,          10,      10, true },   // causes the following tests to fail
88   { 9,           10,      11, false },  // one-less base, one-less high
89   { 9,           11,      12, false },  // one-less base, identical high
90   { 9,           12,      13, false },  // completely contains existing
91   { 10,          9,       14, false },  // identical base, one-less high
92   { 10,          10,      15, false },  // exactly identical to existing range
93   { 10,          11,      16, false },  // identical base, one-greater high
94   { 11,          8,       17, false },  // contained completely within
95   { 11,          9,       18, false },  // one-greater base, identical high
96   { 11,          10,      19, false },  // one-greater base, one-greater high
97   { 9,           2,       20, false },  // overlaps bottom by one
98   { 10,          1,       21, false },  // overlaps bottom by one, contained
99   { 19,          1,       22, false },  // overlaps top by one, contained
100   { 19,          2,       23, false },  // overlaps top by one
101 
102   { 9,           1,       24, true },   // directly below without overlap
103   { 20,          1,       25, true },   // directly above without overlap
104 
105   { 6,           3,       26, true },   // exactly between two ranges, gapless
106   { 7,           3,       27, false },  // tries to span two ranges
107   { 7,           5,       28, false },  // tries to span three ranges
108   { 4,           20,      29, false },  // tries to contain several ranges
109 
110   { 30,          50,      30, true },
111   { 90,          25,      31, true },
112   { 35,          65,      32, false },  // tries to span two noncontiguous
113   { 120,         10000,   33, true },   // > 8-bit
114   { 20000,       20000,   34, true },   // > 8-bit
115   { 0x10001,     0x10001, 35, true },   // > 16-bit
116 
117   { 27,          -1,      36, false }   // tests high < base
118 };
119 
120 // Attempt to fill the entire space.  The entire space must be filled with
121 // three stores because AddressType is signed for these tests, so RangeMap
122 // treats the size as signed and rejects sizes that appear to be negative.
123 // Even if these tests were run as unsigned, two stores would be needed
124 // to fill the space because the entire size of the space could only be
125 // described by using one more bit than would be present in AddressType.
126 const RangeTest range_tests_1[] = {
127   { INT_MIN, INT_MAX, 50, true },   // From INT_MIN to -2, inclusive
128   { -1,      2,       51, true },   // From -1 to 0, inclusive
129   { 1,       INT_MAX, 52, true },   // From 1 to INT_MAX, inclusive
130   { INT_MIN, INT_MAX, 53, false },  // Can't fill the space twice
131   { -1,      2,       54, false },
132   { 1,       INT_MAX, 55, false },
133   { -3,      6,       56, false },  // -3 to 2, inclusive - spans 3 ranges
134 };
135 
136 // A light round of testing to verify that RetrieveRange does the right
137 // the right thing at the extremities of the range when nothing is stored
138 // there.  Checks are forced without storing anything at the extremities
139 // by setting size = 0.
140 const RangeTest range_tests_2[] = {
141   { INT_MIN, 0, 100, false },  // makes RetrieveRange check low end
142   { -1,      3, 101, true },
143   { INT_MAX, 0, 102, false },  // makes RetrieveRange check high end
144 };
145 
146 // Similar to the previous test set, but with a couple of ranges closer
147 // to the extremities.
148 const RangeTest range_tests_3[] = {
149   { INT_MIN + 1, 1, 110, true },
150   { INT_MAX - 1, 1, 111, true },
151   { INT_MIN,     0, 112, false },  // makes RetrieveRange check low end
152   { INT_MAX,     0, 113, false }   // makes RetrieveRange check high end
153 };
154 
155 // The range map is cleared between sets of tests listed here.
156 const RangeTestSet range_test_sets[] = {
157   { range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) },
158   { range_tests_1, sizeof(range_tests_1) / sizeof(RangeTest) },
159   { range_tests_2, sizeof(range_tests_2) / sizeof(RangeTest) },
160   { range_tests_3, sizeof(range_tests_3) / sizeof(RangeTest) },
161   { range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) }   // Run again
162 };
163 
164 }  // namespace
165 
166 namespace google_breakpad {
167 class TestStaticRangeMap : public ::testing::Test {
168  protected:
SetUp()169   void SetUp() {
170     kTestCasesCount_ = sizeof(range_test_sets) / sizeof(RangeTestSet);
171   }
172 
173   // StoreTest uses the data in a RangeTest and calls StoreRange on the
174   // test RangeMap.  It returns true if the expected result occurred, and
175   // false if something else happened.
176   void StoreTest(RMap* range_map, const RangeTest* range_test);
177 
178   // RetrieveTest uses the data in RangeTest and calls RetrieveRange on the
179   // test RangeMap.  If it retrieves the expected value (which can be no
180   // map entry at the specified range,) it returns true, otherwise, it returns
181   // false.  RetrieveTest will check the values around the base address and
182   // the high address of a range to guard against off-by-one errors.
183   void RetrieveTest(TestMap* range_map, const RangeTest* range_test);
184 
185   // Test RetrieveRangeAtIndex, which is supposed to return objects in order
186   // according to their addresses.  This test is performed by looping through
187   // the map, calling RetrieveRangeAtIndex for all possible indices in sequence,
188   // and verifying that each call returns a different object than the previous
189   // call, and that ranges are returned with increasing base addresses.  Returns
190   // false if the test fails.
191   void RetrieveIndexTest(const TestMap* range_map, int set);
192 
193   void RunTestCase(int test_case);
194 
195   unsigned int kTestCasesCount_;
196   RangeMapSerializer<AddressType, EntryType> serializer_;
197 };
198 
StoreTest(RMap * range_map,const RangeTest * range_test)199 void TestStaticRangeMap::StoreTest(RMap* range_map,
200                                    const RangeTest* range_test) {
201   bool stored = range_map->StoreRange(range_test->address,
202                                       range_test->size,
203                                       range_test->id);
204   EXPECT_EQ(stored, range_test->expect_storable)
205       << "StoreRange id " << range_test->id << "FAILED";
206 }
207 
RetrieveTest(TestMap * range_map,const RangeTest * range_test)208 void TestStaticRangeMap::RetrieveTest(TestMap* range_map,
209                                       const RangeTest* range_test) {
210   for (unsigned int side = 0; side <= 1; ++side) {
211     // When side == 0, check the low side (base address) of each range.
212     // When side == 1, check the high side (base + size) of each range.
213 
214     // Check one-less and one-greater than the target address in addition
215     // to the target address itself.
216 
217     // If the size of the range is only 1, don't check one greater than
218     // the base or one less than the high - for a successfully stored
219     // range, these tests would erroneously fail because the range is too
220     // small.
221     AddressType low_offset = -1;
222     AddressType high_offset = 1;
223     if (range_test->size == 1) {
224       if (!side)          // When checking the low side,
225         high_offset = 0;  // don't check one over the target.
226       else                // When checking the high side,
227         low_offset = 0;   // don't check one under the target.
228     }
229 
230     for (AddressType offset = low_offset; offset <= high_offset; ++offset) {
231       AddressType address =
232           offset +
233           (!side ? range_test->address :
234                    range_test->address + range_test->size - 1);
235 
236       bool expected_result = false;  // This is correct for tests not stored.
237       if (range_test->expect_storable) {
238         if (offset == 0)             // When checking the target address,
239           expected_result = true;    // test should always succeed.
240         else if (offset == -1)       // When checking one below the target,
241           expected_result = side;    // should fail low and succeed high.
242         else                         // When checking one above the target,
243           expected_result = !side;   // should succeed low and fail high.
244       }
245 
246       const EntryType* id;
247       AddressType retrieved_base;
248       AddressType retrieved_size;
249       bool retrieved = range_map->RetrieveRange(address, id,
250                                                 &retrieved_base,
251                                                 &retrieved_size);
252 
253       bool observed_result = retrieved && *id == range_test->id;
254       EXPECT_EQ(observed_result, expected_result)
255           << "RetrieveRange id " << range_test->id
256           << ", side " << side << ", offset " << offset << " FAILED.";
257 
258       // If a range was successfully retrieved, check that the returned
259       // bounds match the range as stored.
260       if (observed_result == true) {
261         EXPECT_EQ(retrieved_base, range_test->address)
262             << "RetrieveRange id " << range_test->id
263             << ", side " << side << ", offset " << offset << " FAILED.";
264         EXPECT_EQ(retrieved_size, range_test->size)
265             << "RetrieveRange id " << range_test->id
266             << ", side " << side << ", offset " << offset << " FAILED.";
267       }
268 
269       // Now, check RetrieveNearestRange.  The nearest range is always
270       // expected to be different from the test range when checking one
271       // less than the low side.
272       bool expected_nearest = range_test->expect_storable;
273       if (!side && offset < 0)
274         expected_nearest = false;
275 
276       AddressType nearest_base;
277       AddressType nearest_size;
278       bool retrieved_nearest = range_map->RetrieveNearestRange(address,
279                                                                id,
280                                                                &nearest_base,
281                                                                &nearest_size);
282 
283       // When checking one greater than the high side, RetrieveNearestRange
284       // should usually return the test range.  When a different range begins
285       // at that address, though, then RetrieveNearestRange should return the
286       // range at the address instead of the test range.
287       if (side && offset > 0 && nearest_base == address) {
288         expected_nearest = false;
289       }
290 
291       bool observed_nearest = retrieved_nearest &&
292                               *id == range_test->id;
293 
294       EXPECT_EQ(observed_nearest, expected_nearest)
295           << "RetrieveRange id " << range_test->id
296           << ", side " << side << ", offset " << offset << " FAILED.";
297 
298       // If a range was successfully retrieved, check that the returned
299       // bounds match the range as stored.
300       if (expected_nearest ==true) {
301         EXPECT_EQ(nearest_base, range_test->address)
302             << "RetrieveRange id " << range_test->id
303             << ", side " << side << ", offset " << offset << " FAILED.";
304         EXPECT_EQ(nearest_size, range_test->size)
305             << "RetrieveRange id " << range_test->id
306             << ", side " << side << ", offset " << offset << " FAILED.";
307       }
308     }
309   }
310 }
311 
RetrieveIndexTest(const TestMap * range_map,int set)312 void TestStaticRangeMap::RetrieveIndexTest(const TestMap* range_map, int set) {
313   AddressType last_base = 0;
314   const EntryType* last_entry = 0;
315   const EntryType* entry;
316   int object_count = range_map->GetCount();
317   for (int object_index = 0; object_index < object_count; ++object_index) {
318     AddressType base;
319     ASSERT_TRUE(range_map->RetrieveRangeAtIndex(object_index,
320                                                 entry,
321                                                 &base,
322                                                 NULL))
323         << "FAILED: RetrieveRangeAtIndex set " << set
324         << " index " << object_index;
325 
326     ASSERT_TRUE(entry) << "FAILED: RetrieveRangeAtIndex set " << set
327                            << " index " << object_index;
328 
329     // It's impossible to do these comparisons unless there's a previous
330     // object to compare against.
331     if (last_entry) {
332       // The object must be different from the last_entry one.
333       EXPECT_NE(*entry, *last_entry) << "FAILED: RetrieveRangeAtIndex set "
334                                      << set << " index " << object_index;
335       // Each object must have a base greater than the previous object's base.
336       EXPECT_GT(base, last_base) << "FAILED: RetrieveRangeAtIndex set " << set
337                                  << " index " << object_index;
338     }
339     last_entry = entry;
340     last_base = base;
341   }
342 
343   // Make sure that RetrieveRangeAtIndex doesn't allow lookups at indices that
344   // are too high.
345   ASSERT_FALSE(range_map->RetrieveRangeAtIndex(
346       object_count, entry, NULL, NULL)) << "FAILED: RetrieveRangeAtIndex set "
347                                         << set << " index " << object_count
348                                         << " (too large)";
349 }
350 
351 // RunTests runs a series of test sets.
RunTestCase(int test_case)352 void TestStaticRangeMap::RunTestCase(int test_case) {
353   // Maintain the range map in a pointer so that deletion can be meaningfully
354   // tested.
355   scoped_ptr<RMap> rmap(new RMap());
356 
357   const RangeTest* range_tests = range_test_sets[test_case].range_tests;
358   unsigned int range_test_count = range_test_sets[test_case].range_test_count;
359 
360   // Run the StoreRange test, which validates StoreRange and initializes
361   // the RangeMap with data for the RetrieveRange test.
362   int stored_count = 0;  // The number of ranges successfully stored
363   for (unsigned int range_test_index = 0;
364        range_test_index < range_test_count;
365        ++range_test_index) {
366     const RangeTest* range_test = &range_tests[range_test_index];
367     StoreTest(rmap.get(), range_test);
368 
369     if (range_test->expect_storable)
370       ++stored_count;
371   }
372 
373   scoped_array<char> memaddr(serializer_.Serialize(*rmap, NULL));
374   scoped_ptr<TestMap> static_range_map(new TestMap(memaddr.get()));
375 
376   // The RangeMap's own count of objects should also match.
377   EXPECT_EQ(static_range_map->GetCount(), stored_count);
378 
379   // Run the RetrieveRange test
380   for (unsigned int range_test_index = 0;
381        range_test_index < range_test_count;
382        ++range_test_index) {
383     const RangeTest* range_test = &range_tests[range_test_index];
384     RetrieveTest(static_range_map.get(), range_test);
385   }
386 
387   RetrieveIndexTest(static_range_map.get(), test_case);
388 }
389 
TEST_F(TestStaticRangeMap,TestCase0)390 TEST_F(TestStaticRangeMap, TestCase0) {
391   int test_case = 0;
392   RunTestCase(test_case);
393 }
394 
TEST_F(TestStaticRangeMap,TestCase1)395 TEST_F(TestStaticRangeMap, TestCase1) {
396   int test_case = 1;
397   RunTestCase(test_case);
398 }
399 
TEST_F(TestStaticRangeMap,TestCase2)400 TEST_F(TestStaticRangeMap, TestCase2) {
401   int test_case = 2;
402   RunTestCase(test_case);
403 }
404 
TEST_F(TestStaticRangeMap,TestCase3)405 TEST_F(TestStaticRangeMap, TestCase3) {
406   int test_case = 3;
407   RunTestCase(test_case);
408 }
409 
TEST_F(TestStaticRangeMap,RunTestCase0Again)410 TEST_F(TestStaticRangeMap, RunTestCase0Again) {
411   int test_case = 0;
412   RunTestCase(test_case);
413 }
414 
415 }  // namespace google_breakpad
416 
main(int argc,char * argv[])417 int main(int argc, char *argv[]) {
418   ::testing::InitGoogleTest(&argc, argv);
419 
420   return RUN_ALL_TESTS();
421 }
422