1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        tests/hashes/hashes.cpp
3 // Purpose:     wxHashTable, wxHashMap, wxHashSet unit test
4 // Author:      Vadim Zeitlin, Mattia Barbon
5 // Modified:    Mike Wetherell
6 // Created:     2004-05-16
7 // Copyright:   (c) 2004 Vadim Zeitlin, Mattia Barbon, 2005 M. Wetherell
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 // ----------------------------------------------------------------------------
11 // headers
12 // ----------------------------------------------------------------------------
13 
14 #include "testprec.h"
15 
16 #ifdef __BORLANDC__
17     #pragma hdrstop
18 #endif
19 
20 #ifndef WX_PRECOMP
21     #include "wx/wx.h"
22 #endif // WX_PRECOMP
23 
24 #include "wx/hash.h"
25 #include "wx/hashmap.h"
26 #include "wx/hashset.h"
27 
28 #if defined wxLongLong_t && !defined wxLongLongIsLong && \
29         (!defined __VISUALC__ || __VISUALC__ > 1100)    // doesn't work on VC5
30     #define TEST_LONGLONG
31 #endif
32 
33 // --------------------------------------------------------------------------
34 // helper class for typed/untyped wxHashTable
35 // --------------------------------------------------------------------------
36 
37 struct Foo
38 {
FooFoo39     Foo(int n_) { n = n_; count++; }
~FooFoo40     ~Foo() { count--; }
41 
42     int n;
43 
44     static size_t count;
45 };
46 
47 size_t Foo::count = 0;
48 
49 struct FooObject : public wxObject
50 {
FooObjectFooObject51     FooObject(int n_) { n = n_; count++; }
~FooObjectFooObject52     ~FooObject() { count--; }
53 
54     int n;
55 
56     static size_t count;
57 };
58 
59 size_t FooObject::count = 0;
60 
61 // --------------------------------------------------------------------------
62 // test class
63 // --------------------------------------------------------------------------
64 
65 class HashesTestCase : public CppUnit::TestCase
66 {
67 public:
HashesTestCase()68     HashesTestCase() { }
69 
70 private:
71     CPPUNIT_TEST_SUITE( HashesTestCase );
72         CPPUNIT_TEST( wxHashTableTest );
73         CPPUNIT_TEST( wxUntypedHashTableDeleteContents );
74         CPPUNIT_TEST( wxTypedHashTableTest );
75         CPPUNIT_TEST( StringHashMapTest );
76         CPPUNIT_TEST( PtrHashMapTest );
77         CPPUNIT_TEST( LongHashMapTest );
78         CPPUNIT_TEST( ULongHashMapTest );
79         CPPUNIT_TEST( UIntHashMapTest );
80         CPPUNIT_TEST( IntHashMapTest );
81         CPPUNIT_TEST( ShortHashMapTest );
82         CPPUNIT_TEST( UShortHashMapTest );
83 #ifdef TEST_LONGLONG
84         CPPUNIT_TEST( LLongHashMapTest );
85         CPPUNIT_TEST( ULLongHashMapTest );
86 #endif
87         CPPUNIT_TEST( wxHashSetTest );
88     CPPUNIT_TEST_SUITE_END();
89 
90     void wxHashTableTest();
91     void wxUntypedHashTableDeleteContents();
92     void wxTypedHashTableTest();
93     void StringHashMapTest();
94     void PtrHashMapTest();
95     void LongHashMapTest();
96     void ULongHashMapTest();
97     void UIntHashMapTest();
98     void IntHashMapTest();
99     void ShortHashMapTest();
100     void UShortHashMapTest();
101 #ifdef TEST_LONGLONG
102     void LLongHashMapTest();
103     void ULLongHashMapTest();
104 #endif
105     void wxHashSetTest();
106 
107     DECLARE_NO_COPY_CLASS(HashesTestCase)
108 };
109 
110 // register in the unnamed registry so that these tests are run by default
111 CPPUNIT_TEST_SUITE_REGISTRATION( HashesTestCase );
112 
113 // also include in its own registry so that these tests can be run alone
114 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( HashesTestCase, "HashesTestCase" );
115 
wxHashTableTest()116 void HashesTestCase::wxHashTableTest()
117 {
118     const int COUNT = 100;
119 
120     {
121         wxHashTable hash(wxKEY_INTEGER, 10), hash2(wxKEY_STRING);
122         wxObject o;
123         int i;
124 
125         for ( i = 0; i < COUNT; ++i )
126             hash.Put(i, &o + i);
127 
128         hash.BeginFind();
129         wxHashTable::compatibility_iterator it = hash.Next();
130         i = 0;
131 
132         while (it)
133         {
134             ++i;
135             it = hash.Next();
136         }
137 
138         CPPUNIT_ASSERT( i == COUNT );
139 
140         for ( i = 99; i >= 0; --i )
141             CPPUNIT_ASSERT( hash.Get(i) == &o + i );
142 
143         for ( i = 0; i < COUNT; ++i )
144             hash.Put(i, &o + i + 20);
145 
146         for ( i = 99; i >= 0; --i )
147             CPPUNIT_ASSERT( hash.Get(i) == &o + i);
148 
149         for ( i = 0; i < COUNT/2; ++i )
150             CPPUNIT_ASSERT( hash.Delete(i) == &o + i);
151 
152         for ( i = COUNT/2; i < COUNT; ++i )
153             CPPUNIT_ASSERT( hash.Get(i) == &o + i);
154 
155         for ( i = 0; i < COUNT/2; ++i )
156             CPPUNIT_ASSERT( hash.Get(i) == &o + i + 20);
157 
158         for ( i = 0; i < COUNT/2; ++i )
159             CPPUNIT_ASSERT( hash.Delete(i) == &o + i + 20);
160 
161         for ( i = 0; i < COUNT/2; ++i )
162             CPPUNIT_ASSERT( hash.Get(i) == NULL);
163 
164         hash2.Put(wxT("foo"), &o + 1);
165         hash2.Put(wxT("bar"), &o + 2);
166         hash2.Put(wxT("baz"), &o + 3);
167 
168         CPPUNIT_ASSERT(hash2.Get(wxT("moo")) == NULL);
169         CPPUNIT_ASSERT(hash2.Get(wxT("bar")) == &o + 2);
170 
171         hash2.Put(wxT("bar"), &o + 0);
172 
173         CPPUNIT_ASSERT(hash2.Get(wxT("bar")) == &o + 2);
174     }
175 
176     // and now some corner-case testing; 3 and 13 hash to the same bucket
177     {
178         wxHashTable hash(wxKEY_INTEGER, 10);
179         wxObject dummy;
180 
181         hash.Put(3, &dummy);
182         hash.Delete(3);
183 
184         CPPUNIT_ASSERT(hash.Get(3) == NULL);
185 
186         hash.Put(3, &dummy);
187         hash.Put(13, &dummy);
188         hash.Delete(3);
189 
190         CPPUNIT_ASSERT(hash.Get(3) == NULL);
191 
192         hash.Delete(13);
193 
194         CPPUNIT_ASSERT(hash.Get(13) == NULL);
195 
196         hash.Put(3, &dummy);
197         hash.Put(13, &dummy);
198         hash.Delete(13);
199 
200         CPPUNIT_ASSERT(hash.Get(13) == NULL);
201 
202         hash.Delete(3);
203 
204         CPPUNIT_ASSERT(hash.Get(3) == NULL);
205     }
206 
207     // test for key + value access (specifically that supplying either
208     // wrong key or wrong value returns NULL)
209     {
210         wxHashTable hash(wxKEY_INTEGER, 10);
211         wxObject dummy;
212 
213         hash.Put(3, 7, &dummy + 7);
214         hash.Put(4, 8, &dummy + 8);
215 
216         CPPUNIT_ASSERT(hash.Get(7) == NULL);
217         CPPUNIT_ASSERT(hash.Get(3, 7) == &dummy + 7);
218         CPPUNIT_ASSERT(hash.Get(4) == NULL);
219         CPPUNIT_ASSERT(hash.Get(3) == NULL);
220         CPPUNIT_ASSERT(hash.Get(8) == NULL);
221         CPPUNIT_ASSERT(hash.Get(8, 4) == NULL);
222 
223         CPPUNIT_ASSERT(hash.Delete(7) == NULL);
224         CPPUNIT_ASSERT(hash.Delete(3) == NULL);
225         CPPUNIT_ASSERT(hash.Delete(3, 7) == &dummy + 7);
226     }
227 
228 }
229 
wxUntypedHashTableDeleteContents()230 void HashesTestCase::wxUntypedHashTableDeleteContents()
231 {
232     // need a nested scope for destruction
233     {
234         wxHashTable hash;
235         hash.DeleteContents(true);
236 
237         CPPUNIT_ASSERT( hash.GetCount() == 0 );
238         CPPUNIT_ASSERT( FooObject::count == 0 );
239 
240         static const int hashTestData[] =
241         {
242             0, 1, 17, -2, 2, 4, -4, 345, 3, 3, 2, 1,
243         };
244 
245         size_t n;
246         for ( n = 0; n < WXSIZEOF(hashTestData); n++ )
247         {
248             hash.Put(hashTestData[n], n, new FooObject(n));
249         }
250 
251         CPPUNIT_ASSERT( hash.GetCount() == WXSIZEOF(hashTestData) );
252         CPPUNIT_ASSERT( FooObject::count == WXSIZEOF(hashTestData) );
253 
254         // delete from hash without deleting object
255         FooObject* foo = (FooObject*)hash.Delete(0l);
256 
257         CPPUNIT_ASSERT( FooObject::count == WXSIZEOF(hashTestData) );
258         delete foo;
259         CPPUNIT_ASSERT( FooObject::count == WXSIZEOF(hashTestData) - 1 );
260     }
261 
262     // hash destroyed
263     CPPUNIT_ASSERT( FooObject::count == 0 );
264 }
265 
266 WX_DECLARE_HASH(Foo, wxListFoos, wxHashFoos);
267 
wxTypedHashTableTest()268 void HashesTestCase::wxTypedHashTableTest()
269 {
270     // need a nested scope for destruction
271     {
272         wxHashFoos hash;
273         hash.DeleteContents(true);
274 
275         CPPUNIT_ASSERT( hash.GetCount() == 0 );
276         CPPUNIT_ASSERT( Foo::count == 0 );
277 
278         static const int hashTestData[] =
279         {
280             0, 1, 17, -2, 2, 4, -4, 345, 3, 3, 2, 1,
281         };
282 
283         size_t n;
284         for ( n = 0; n < WXSIZEOF(hashTestData); n++ )
285         {
286             hash.Put(hashTestData[n], n, new Foo(n));
287         }
288 
289         CPPUNIT_ASSERT( hash.GetCount() == WXSIZEOF(hashTestData) );
290         CPPUNIT_ASSERT( Foo::count == WXSIZEOF(hashTestData) );
291 
292         for ( n = 0; n < WXSIZEOF(hashTestData); n++ )
293         {
294             Foo *foo = hash.Get(hashTestData[n], n);
295 
296             CPPUNIT_ASSERT( foo != NULL );
297             CPPUNIT_ASSERT( foo->n == (int)n );
298         }
299 
300         // element not in hash
301         CPPUNIT_ASSERT( hash.Get(1234) == NULL );
302         CPPUNIT_ASSERT( hash.Get(1, 0) == NULL );
303 
304         // delete from hash without deleting object
305         Foo* foo = hash.Delete(0);
306 
307         CPPUNIT_ASSERT( Foo::count == WXSIZEOF(hashTestData) );
308         delete foo;
309         CPPUNIT_ASSERT( Foo::count == WXSIZEOF(hashTestData) - 1 );
310     }
311 
312     // hash destroyed
313     CPPUNIT_ASSERT( Foo::count == 0 );
314 }
315 
316 // test compilation of basic map types
317 WX_DECLARE_HASH_MAP( int*, int*, wxPointerHash, wxPointerEqual, myPtrHashMap );
318 WX_DECLARE_HASH_MAP( long, long, wxIntegerHash, wxIntegerEqual, myLongHashMap );
319 WX_DECLARE_HASH_MAP( unsigned long, unsigned, wxIntegerHash, wxIntegerEqual,
320                      myUnsignedHashMap );
321 WX_DECLARE_HASH_MAP( unsigned int, unsigned, wxIntegerHash, wxIntegerEqual,
322                      myTestHashMap1 );
323 WX_DECLARE_HASH_MAP( int, unsigned, wxIntegerHash, wxIntegerEqual,
324                      myTestHashMap2 );
325 WX_DECLARE_HASH_MAP( short, unsigned, wxIntegerHash, wxIntegerEqual,
326                      myTestHashMap3 );
327 WX_DECLARE_HASH_MAP( unsigned short, unsigned, wxIntegerHash, wxIntegerEqual,
328                      myTestHashMap4 );
329 
330 // same as:
331 // WX_DECLARE_HASH_MAP( wxString, wxString, wxStringHash, wxStringEqual,
332 //                      myStringHashMap );
333 WX_DECLARE_STRING_HASH_MAP(wxString, myStringHashMap);
334 
335 #ifdef TEST_LONGLONG
336     WX_DECLARE_HASH_MAP( wxLongLong_t, wxLongLong_t,
337                          wxIntegerHash, wxIntegerEqual, myLLongHashMap );
338     WX_DECLARE_HASH_MAP( wxULongLong_t, wxULongLong_t,
339                          wxIntegerHash, wxIntegerEqual, myULLongHashMap );
340 #endif
341 
342 // Helpers to generate a key value pair for item 'i', out of a total of 'count'
MakeKeyValuePair(size_t i,size_t,wxString & key,wxString & val)343 void MakeKeyValuePair(size_t i, size_t /*count*/, wxString& key, wxString& val)
344 {
345     key.clear();
346     key << i;
347     val = wxT("A") + key + wxT("C");
348 }
349 
350 // for integral keys generate a range of keys that will use all the bits of
351 // the key type
352 template <class IntT, class KeyT>
353 IntT MakeKey(size_t i, size_t count)
354 {
355     IntT max = 1;
356     max <<= sizeof(KeyT) * 8 - 2;
357     max -= count / 4 + 1;
358 
359     return max / count * 4 * i + i / 3;
360 }
361 
362 // make key/value pairs for integer types
363 template <class KeyT, class ValueT>
MakeKeyValuePair(size_t i,size_t count,KeyT & key,ValueT & value)364 void MakeKeyValuePair(size_t i, size_t count, KeyT& key, ValueT& value)
365 {
366     key = MakeKey<KeyT, KeyT>(i, count);
367     value = wx_truncate_cast(ValueT, key);
368 }
369 
370 // make key/values paris for pointer types
371 template <class T, class ValueT>
MakeKeyValuePair(size_t i,size_t count,T * & key,ValueT & value)372 void MakeKeyValuePair(size_t i, size_t count, T*& key, ValueT& value)
373 {
374     key = (T*)wxUIntToPtr(MakeKey<wxUIntPtr, T*>(i, count));
375     value = wx_truncate_cast(ValueT, key);
376 }
377 
378 // the test
379 template <class HashMapT>
380 void
381 #if defined(__GNUC__) && !defined(__clang__)
382 // At least g++ 4.8.2 (included in Ubuntu 14.04) is known to miscompile the
383 // code in this function and make all the loops below infinite when using -O2,
384 // so we need to turn off optimizations for it to allow the tests to run at
385 // all.
386 __attribute__((optimize("O0")))
387 #endif // g++
HashMapTest()388 HashMapTest()
389 {
390     typedef typename HashMapT::value_type::second_type value_type;
391     typedef typename HashMapT::key_type key_type;
392     typedef typename HashMapT::iterator Itor;
393 
394     HashMapT sh(0); // as small as possible
395     key_type buf;
396     value_type value;
397     size_t i;
398     const size_t count = 10000;
399 
400     // init with some data
401     for( i = 0; i < count; ++i )
402     {
403         MakeKeyValuePair(i, count, buf, value);
404         sh[buf] = value;
405     }
406 
407     // test that insertion worked
408     CPPUNIT_ASSERT( sh.size() == count );
409 
410     for( i = 0; i < count; ++i )
411     {
412         MakeKeyValuePair(i, count, buf, value);
413         CPPUNIT_ASSERT( sh[buf] == value );
414     }
415 
416     // check that iterators work
417     Itor it;
418     for( i = 0, it = sh.begin(); it != sh.end(); ++it, ++i )
419     {
420         CPPUNIT_ASSERT( i != count );
421         CPPUNIT_ASSERT( it->second == sh[it->first] );
422     }
423 
424     CPPUNIT_ASSERT( sh.size() == i );
425 
426     // test copy ctor, assignment operator
427     HashMapT h1( sh ), h2( 0 );
428     h2 = sh;
429 
430     for( i = 0, it = sh.begin(); it != sh.end(); ++it, ++i )
431     {
432         CPPUNIT_ASSERT( h1[it->first] == it->second );
433         CPPUNIT_ASSERT( h2[it->first] == it->second );
434     }
435 
436     // other tests
437     for( i = 0; i < count; ++i )
438     {
439         MakeKeyValuePair(i, count, buf, value);
440         size_t sz = sh.size();
441 
442         // test find() and erase(it)
443         if( i < 100 )
444         {
445             it = sh.find( buf );
446             CPPUNIT_ASSERT( it != sh.end() );
447 
448             sh.erase( it );
449 
450             CPPUNIT_ASSERT( sh.find( buf ) == sh.end() );
451         }
452         else
453         // test erase(key)
454         {
455             size_t c = sh.erase( buf );
456             CPPUNIT_ASSERT( c == 1 );
457             CPPUNIT_ASSERT( sh.find( buf ) == sh.end() );
458         }
459 
460         // count should decrease
461         CPPUNIT_ASSERT( sh.size() == sz - 1 );
462     }
463 }
464 
StringHashMapTest()465 void HashesTestCase::StringHashMapTest() { HashMapTest<myStringHashMap>();   }
PtrHashMapTest()466 void HashesTestCase::PtrHashMapTest()    { HashMapTest<myPtrHashMap>();      }
LongHashMapTest()467 void HashesTestCase::LongHashMapTest()   { HashMapTest<myLongHashMap>();     }
ULongHashMapTest()468 void HashesTestCase::ULongHashMapTest()  { HashMapTest<myUnsignedHashMap>(); }
UIntHashMapTest()469 void HashesTestCase::UIntHashMapTest()   { HashMapTest<myTestHashMap1>();    }
IntHashMapTest()470 void HashesTestCase::IntHashMapTest()    { HashMapTest<myTestHashMap2>();    }
ShortHashMapTest()471 void HashesTestCase::ShortHashMapTest()  { HashMapTest<myTestHashMap3>();    }
UShortHashMapTest()472 void HashesTestCase::UShortHashMapTest() { HashMapTest<myTestHashMap4>();    }
473 
474 #ifdef TEST_LONGLONG
LLongHashMapTest()475 void HashesTestCase::LLongHashMapTest()  { HashMapTest<myLLongHashMap>();    }
ULLongHashMapTest()476 void HashesTestCase::ULLongHashMapTest() { HashMapTest<myULLongHashMap>();   }
477 #endif
478 
479 #ifdef __VISUALC__
480     #if __VISUALC__ <= 1200
481         #pragma warning(disable:4284) // operator->() returns a non-UDT
482     #endif
483 #endif // __VISUALC__
484 
485 // test compilation of basic set types
486 WX_DECLARE_HASH_SET( int*, wxPointerHash, wxPointerEqual, myPtrHashSet );
487 WX_DECLARE_HASH_SET( long, wxIntegerHash, wxIntegerEqual, myLongHashSet );
488 WX_DECLARE_HASH_SET( unsigned long, wxIntegerHash, wxIntegerEqual,
489                      myUnsignedHashSet );
490 WX_DECLARE_HASH_SET( unsigned int, wxIntegerHash, wxIntegerEqual,
491                      myTestHashSet1 );
492 WX_DECLARE_HASH_SET( int, wxIntegerHash, wxIntegerEqual,
493                      myTestHashSet2 );
494 WX_DECLARE_HASH_SET( short, wxIntegerHash, wxIntegerEqual,
495                      myTestHashSet3 );
496 WX_DECLARE_HASH_SET( unsigned short, wxIntegerHash, wxIntegerEqual,
497                      myTestHashSet4 );
498 WX_DECLARE_HASH_SET( wxString, wxStringHash, wxStringEqual,
499                      myTestHashSet5 );
500 
501 struct MyStruct
502 {
503     int* ptr;
504     wxString str;
505 };
506 
507 class MyHash
508 {
509 public:
operator ()(const MyStruct & s) const510     unsigned long operator()(const MyStruct& s) const
511         { return m_dummy(s.ptr); }
operator =(const MyHash &)512     MyHash& operator=(const MyHash&) { return *this; }
513 private:
514     wxPointerHash m_dummy;
515 };
516 
517 class MyEqual
518 {
519 public:
operator ()(const MyStruct & s1,const MyStruct & s2) const520     bool operator()(const MyStruct& s1, const MyStruct& s2) const
521         { return s1.ptr == s2.ptr; }
operator =(const MyEqual &)522     MyEqual& operator=(const MyEqual&) { return *this; }
523 };
524 
525 WX_DECLARE_HASH_SET( MyStruct, MyHash, MyEqual, mySet );
526 
527 typedef myTestHashSet5 wxStringHashSet;
528 
wxHashSetTest()529 void HashesTestCase::wxHashSetTest()
530 {
531     wxStringHashSet set1;
532 
533     set1.insert( wxT("abc") );
534 
535     CPPUNIT_ASSERT( set1.size() == 1 );
536 
537     set1.insert( wxT("bbc") );
538     set1.insert( wxT("cbc") );
539 
540     CPPUNIT_ASSERT( set1.size() == 3 );
541 
542     set1.insert( wxT("abc") );
543 
544     CPPUNIT_ASSERT( set1.size() == 3 );
545 
546     mySet set2;
547     int dummy;
548     MyStruct tmp;
549 
550     tmp.ptr = &dummy; tmp.str = wxT("ABC");
551     set2.insert( tmp );
552     tmp.ptr = &dummy + 1;
553     set2.insert( tmp );
554     tmp.ptr = &dummy; tmp.str = wxT("CDE");
555     set2.insert( tmp );
556 
557     CPPUNIT_ASSERT( set2.size() == 2 );
558 
559     mySet::iterator it = set2.find( tmp );
560 
561     CPPUNIT_ASSERT( it != set2.end() );
562     CPPUNIT_ASSERT( it->ptr == &dummy );
563     CPPUNIT_ASSERT( it->str == wxT("ABC") );
564 }
565