1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/hashes/hashes.cpp
3 // Purpose: wxHashTable, wxHashMap, wxHashSet unit test
4 // Author: Vadim Zeitlin, Mattia Barbon
5 // Modified: Mike Wetherell
8 // Copyright: (c) 2004 Vadim Zeitlin, Mattia Barbon, 2005 M. Wetherell
9 ///////////////////////////////////////////////////////////////////////////////
11 // ----------------------------------------------------------------------------
13 // ----------------------------------------------------------------------------
26 #include "wx/hashmap.h"
27 #include "wx/hashset.h"
29 #if defined wxLongLong_t && !defined wxLongLongIsLong && \
30 (!defined __VISUALC__ || __VISUALC__ > 1100) // doesn't work on VC5
34 // --------------------------------------------------------------------------
35 // helper class for typed/untyped wxHashTable
36 // --------------------------------------------------------------------------
40 Foo(int n_
) { n
= n_
; count
++; }
48 size_t Foo::count
= 0;
50 struct FooObject
: public wxObject
52 FooObject(int n_
) { n
= n_
; count
++; }
53 ~FooObject() { count
--; }
60 size_t FooObject::count
= 0;
62 // --------------------------------------------------------------------------
64 // --------------------------------------------------------------------------
66 class HashesTestCase
: public CppUnit::TestCase
72 CPPUNIT_TEST_SUITE( HashesTestCase
);
73 CPPUNIT_TEST( wxHashTableTest
);
74 CPPUNIT_TEST( wxUntypedHashTableDeleteContents
);
75 CPPUNIT_TEST( wxTypedHashTableTest
);
76 CPPUNIT_TEST( StringHashMapTest
);
77 CPPUNIT_TEST( PtrHashMapTest
);
78 CPPUNIT_TEST( LongHashMapTest
);
79 CPPUNIT_TEST( ULongHashMapTest
);
80 CPPUNIT_TEST( UIntHashMapTest
);
81 CPPUNIT_TEST( IntHashMapTest
);
82 CPPUNIT_TEST( ShortHashMapTest
);
83 CPPUNIT_TEST( UShortHashMapTest
);
85 CPPUNIT_TEST( LLongHashMapTest
);
86 CPPUNIT_TEST( ULLongHashMapTest
);
88 CPPUNIT_TEST( wxHashSetTest
);
89 CPPUNIT_TEST_SUITE_END();
91 void wxHashTableTest();
92 void wxUntypedHashTableDeleteContents();
93 void wxTypedHashTableTest();
94 void StringHashMapTest();
95 void PtrHashMapTest();
96 void LongHashMapTest();
97 void ULongHashMapTest();
98 void UIntHashMapTest();
99 void IntHashMapTest();
100 void ShortHashMapTest();
101 void UShortHashMapTest();
103 void LLongHashMapTest();
104 void ULLongHashMapTest();
106 void wxHashSetTest();
108 DECLARE_NO_COPY_CLASS(HashesTestCase
)
111 // register in the unnamed registry so that these tests are run by default
112 CPPUNIT_TEST_SUITE_REGISTRATION( HashesTestCase
);
114 // also include in it's own registry so that these tests can be run alone
115 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( HashesTestCase
, "HashesTestCase" );
117 void HashesTestCase::wxHashTableTest()
119 const int COUNT
= 100;
122 wxHashTable
hash(wxKEY_INTEGER
, 10), hash2(wxKEY_STRING
);
126 for ( i
= 0; i
< COUNT
; ++i
)
130 wxHashTable::compatibility_iterator it
= hash
.Next();
139 CPPUNIT_ASSERT( i
== COUNT
);
141 for ( i
= 99; i
>= 0; --i
)
142 CPPUNIT_ASSERT( hash
.Get(i
) == &o
+ i
);
144 for ( i
= 0; i
< COUNT
; ++i
)
145 hash
.Put(i
, &o
+ i
+ 20);
147 for ( i
= 99; i
>= 0; --i
)
148 CPPUNIT_ASSERT( hash
.Get(i
) == &o
+ i
);
150 for ( i
= 0; i
< COUNT
/2; ++i
)
151 CPPUNIT_ASSERT( hash
.Delete(i
) == &o
+ i
);
153 for ( i
= COUNT
/2; i
< COUNT
; ++i
)
154 CPPUNIT_ASSERT( hash
.Get(i
) == &o
+ i
);
156 for ( i
= 0; i
< COUNT
/2; ++i
)
157 CPPUNIT_ASSERT( hash
.Get(i
) == &o
+ i
+ 20);
159 for ( i
= 0; i
< COUNT
/2; ++i
)
160 CPPUNIT_ASSERT( hash
.Delete(i
) == &o
+ i
+ 20);
162 for ( i
= 0; i
< COUNT
/2; ++i
)
163 CPPUNIT_ASSERT( hash
.Get(i
) == NULL
);
165 hash2
.Put(_T("foo"), &o
+ 1);
166 hash2
.Put(_T("bar"), &o
+ 2);
167 hash2
.Put(_T("baz"), &o
+ 3);
169 CPPUNIT_ASSERT(hash2
.Get(_T("moo")) == NULL
);
170 CPPUNIT_ASSERT(hash2
.Get(_T("bar")) == &o
+ 2);
172 hash2
.Put(_T("bar"), &o
+ 0);
174 CPPUNIT_ASSERT(hash2
.Get(_T("bar")) == &o
+ 2);
177 // and now some corner-case testing; 3 and 13 hash to the same bucket
179 wxHashTable
hash(wxKEY_INTEGER
, 10);
185 CPPUNIT_ASSERT(hash
.Get(3) == NULL
);
188 hash
.Put(13, &dummy
);
191 CPPUNIT_ASSERT(hash
.Get(3) == NULL
);
195 CPPUNIT_ASSERT(hash
.Get(13) == NULL
);
198 hash
.Put(13, &dummy
);
201 CPPUNIT_ASSERT(hash
.Get(13) == NULL
);
205 CPPUNIT_ASSERT(hash
.Get(3) == NULL
);
208 // test for key + value access (specifically that supplying either
209 // wrong key or wrong value returns NULL)
211 wxHashTable
hash(wxKEY_INTEGER
, 10);
214 hash
.Put(3, 7, &dummy
+ 7);
215 hash
.Put(4, 8, &dummy
+ 8);
217 CPPUNIT_ASSERT(hash
.Get(7) == NULL
);
218 CPPUNIT_ASSERT(hash
.Get(3, 7) == &dummy
+ 7);
219 CPPUNIT_ASSERT(hash
.Get(4) == NULL
);
220 CPPUNIT_ASSERT(hash
.Get(3) == NULL
);
221 CPPUNIT_ASSERT(hash
.Get(8) == NULL
);
222 CPPUNIT_ASSERT(hash
.Get(8, 4) == NULL
);
224 CPPUNIT_ASSERT(hash
.Delete(7) == NULL
);
225 CPPUNIT_ASSERT(hash
.Delete(3) == NULL
);
226 CPPUNIT_ASSERT(hash
.Delete(3, 7) == &dummy
+ 7);
231 void HashesTestCase::wxUntypedHashTableDeleteContents()
233 // need a nested scope for destruction
236 hash
.DeleteContents(true);
238 CPPUNIT_ASSERT( hash
.GetCount() == 0 );
239 CPPUNIT_ASSERT( FooObject::count
== 0 );
241 static const int hashTestData
[] =
243 0, 1, 17, -2, 2, 4, -4, 345, 3, 3, 2, 1,
247 for ( n
= 0; n
< WXSIZEOF(hashTestData
); n
++ )
249 hash
.Put(hashTestData
[n
], n
, new FooObject(n
));
252 CPPUNIT_ASSERT( hash
.GetCount() == WXSIZEOF(hashTestData
) );
253 CPPUNIT_ASSERT( FooObject::count
== WXSIZEOF(hashTestData
) );
255 // delete from hash without deleting object
256 FooObject
* foo
= (FooObject
*)hash
.Delete(0l);
258 CPPUNIT_ASSERT( FooObject::count
== WXSIZEOF(hashTestData
) );
260 CPPUNIT_ASSERT( FooObject::count
== WXSIZEOF(hashTestData
) - 1 );
264 CPPUNIT_ASSERT( FooObject::count
== 0 );
267 #if WXWIN_COMPATIBILITY_2_4
268 WX_DECLARE_LIST(Foo
, wxListFoos
);
271 WX_DECLARE_HASH(Foo
, wxListFoos
, wxHashFoos
);
273 #if WXWIN_COMPATIBILITY_2_4
274 #include "wx/listimpl.cpp"
275 WX_DEFINE_LIST(wxListFoos
)
278 void HashesTestCase::wxTypedHashTableTest()
280 // need a nested scope for destruction
283 hash
.DeleteContents(true);
285 CPPUNIT_ASSERT( hash
.GetCount() == 0 );
286 CPPUNIT_ASSERT( Foo::count
== 0 );
288 static const int hashTestData
[] =
290 0, 1, 17, -2, 2, 4, -4, 345, 3, 3, 2, 1,
294 for ( n
= 0; n
< WXSIZEOF(hashTestData
); n
++ )
296 hash
.Put(hashTestData
[n
], n
, new Foo(n
));
299 CPPUNIT_ASSERT( hash
.GetCount() == WXSIZEOF(hashTestData
) );
300 CPPUNIT_ASSERT( Foo::count
== WXSIZEOF(hashTestData
) );
302 for ( n
= 0; n
< WXSIZEOF(hashTestData
); n
++ )
304 Foo
*foo
= hash
.Get(hashTestData
[n
], n
);
306 CPPUNIT_ASSERT( foo
!= NULL
);
307 CPPUNIT_ASSERT( foo
->n
== (int)n
);
310 // element not in hash
311 CPPUNIT_ASSERT( hash
.Get(1234) == NULL
);
312 CPPUNIT_ASSERT( hash
.Get(1, 0) == NULL
);
314 // delete from hash without deleting object
315 Foo
* foo
= hash
.Delete(0);
317 CPPUNIT_ASSERT( Foo::count
== WXSIZEOF(hashTestData
) );
319 CPPUNIT_ASSERT( Foo::count
== WXSIZEOF(hashTestData
) - 1 );
323 CPPUNIT_ASSERT( Foo::count
== 0 );
326 // test compilation of basic map types
327 WX_DECLARE_HASH_MAP( int*, int*, wxPointerHash
, wxPointerEqual
, myPtrHashMap
);
328 WX_DECLARE_HASH_MAP( long, long, wxIntegerHash
, wxIntegerEqual
, myLongHashMap
);
329 WX_DECLARE_HASH_MAP( unsigned long, unsigned, wxIntegerHash
, wxIntegerEqual
,
331 WX_DECLARE_HASH_MAP( unsigned int, unsigned, wxIntegerHash
, wxIntegerEqual
,
333 WX_DECLARE_HASH_MAP( int, unsigned, wxIntegerHash
, wxIntegerEqual
,
335 WX_DECLARE_HASH_MAP( short, unsigned, wxIntegerHash
, wxIntegerEqual
,
337 WX_DECLARE_HASH_MAP( unsigned short, unsigned, wxIntegerHash
, wxIntegerEqual
,
341 // WX_DECLARE_HASH_MAP( wxString, wxString, wxStringHash, wxStringEqual,
342 // myStringHashMap );
343 WX_DECLARE_STRING_HASH_MAP(wxString
, myStringHashMap
);
346 WX_DECLARE_HASH_MAP( wxLongLong_t
, wxLongLong_t
,
347 wxIntegerHash
, wxIntegerEqual
, myLLongHashMap
);
348 WX_DECLARE_HASH_MAP( wxULongLong_t
, wxULongLong_t
,
349 wxIntegerHash
, wxIntegerEqual
, myULLongHashMap
);
352 // Helpers to generate a key value pair for item 'i', out of a total of 'count'
353 void MakeKeyValuePair(size_t i
, size_t /*count*/, wxString
& key
, wxString
& val
)
357 val
= wxT("A") + key
+ wxT("C");
360 // for integral keys generate a range of keys that will use all the bits of
362 template <class IntT
, class KeyT
>
363 IntT
MakeKey(size_t i
, size_t count
)
366 max
<<= sizeof(KeyT
) * 8 - 2;
367 max
-= count
/ 4 + 1;
369 return max
/ count
* 4 * i
+ i
/ 3;
372 // make key/value pairs for integer types
373 template <class KeyT
, class ValueT
>
374 void MakeKeyValuePair(size_t i
, size_t count
, KeyT
& key
, ValueT
& value
)
376 key
= MakeKey
<KeyT
, KeyT
>(i
, count
);
377 value
= wx_truncate_cast(ValueT
, key
);
380 // make key/values paris for pointer types
381 template <class T
, class ValueT
>
382 void MakeKeyValuePair(size_t i
, size_t count
, T
*& key
, ValueT
& value
)
384 key
= (T
*)wxUIntToPtr(MakeKey
<wxUIntPtr
, T
*>(i
, count
));
385 value
= wx_truncate_cast(ValueT
, key
);
389 template <class HashMapT
>
392 #if wxUSE_STL && defined HAVE_STL_HASH_MAP
393 typedef typename
HashMapT::value_type::second_type value_type
;
395 typedef typename
HashMapT::value_type::t2 value_type
;
397 typedef typename
HashMapT::key_type key_type
;
398 typedef typename
HashMapT::iterator Itor
;
400 HashMapT
sh(0); // as small as possible
404 const size_t count
= 10000;
406 // init with some data
407 for( i
= 0; i
< count
; ++i
)
409 MakeKeyValuePair(i
, count
, buf
, value
);
413 // test that insertion worked
414 CPPUNIT_ASSERT( sh
.size() == count
);
416 for( i
= 0; i
< count
; ++i
)
418 MakeKeyValuePair(i
, count
, buf
, value
);
419 CPPUNIT_ASSERT( sh
[buf
] == value
);
422 // check that iterators work
424 for( i
= 0, it
= sh
.begin(); it
!= sh
.end(); ++it
, ++i
)
426 CPPUNIT_ASSERT( i
!= count
);
427 CPPUNIT_ASSERT( it
->second
== sh
[it
->first
] );
430 CPPUNIT_ASSERT( sh
.size() == i
);
432 // test copy ctor, assignment operator
433 HashMapT
h1( sh
), h2( 0 );
436 for( i
= 0, it
= sh
.begin(); it
!= sh
.end(); ++it
, ++i
)
438 CPPUNIT_ASSERT( h1
[it
->first
] == it
->second
);
439 CPPUNIT_ASSERT( h2
[it
->first
] == it
->second
);
443 for( i
= 0; i
< count
; ++i
)
445 MakeKeyValuePair(i
, count
, buf
, value
);
446 size_t sz
= sh
.size();
448 // test find() and erase(it)
452 CPPUNIT_ASSERT( it
!= sh
.end() );
456 CPPUNIT_ASSERT( sh
.find( buf
) == sh
.end() );
461 size_t c
= sh
.erase( buf
);
462 CPPUNIT_ASSERT( c
== 1 );
463 CPPUNIT_ASSERT( sh
.find( buf
) == sh
.end() );
466 // count should decrease
467 CPPUNIT_ASSERT( sh
.size() == sz
- 1 );
471 void HashesTestCase::StringHashMapTest() { HashMapTest
<myStringHashMap
>(); }
472 void HashesTestCase::PtrHashMapTest() { HashMapTest
<myPtrHashMap
>(); }
473 void HashesTestCase::LongHashMapTest() { HashMapTest
<myLongHashMap
>(); }
474 void HashesTestCase::ULongHashMapTest() { HashMapTest
<myUnsignedHashMap
>(); }
475 void HashesTestCase::UIntHashMapTest() { HashMapTest
<myTestHashMap1
>(); }
476 void HashesTestCase::IntHashMapTest() { HashMapTest
<myTestHashMap2
>(); }
477 void HashesTestCase::ShortHashMapTest() { HashMapTest
<myTestHashMap3
>(); }
478 void HashesTestCase::UShortHashMapTest() { HashMapTest
<myTestHashMap4
>(); }
481 void HashesTestCase::LLongHashMapTest() { HashMapTest
<myLLongHashMap
>(); }
482 void HashesTestCase::ULLongHashMapTest() { HashMapTest
<myULLongHashMap
>(); }
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
,
490 WX_DECLARE_HASH_SET( unsigned int, wxIntegerHash
, wxIntegerEqual
,
492 WX_DECLARE_HASH_SET( int, wxIntegerHash
, wxIntegerEqual
,
494 WX_DECLARE_HASH_SET( short, wxIntegerHash
, wxIntegerEqual
,
496 WX_DECLARE_HASH_SET( unsigned short, wxIntegerHash
, wxIntegerEqual
,
498 WX_DECLARE_HASH_SET( wxString
, wxStringHash
, wxStringEqual
,
510 unsigned long operator()(const MyStruct
& s
) const
511 { return m_dummy(s
.ptr
); }
512 MyHash
& operator=(const MyHash
&) { return *this; }
514 wxPointerHash m_dummy
;
520 bool operator()(const MyStruct
& s1
, const MyStruct
& s2
) const
521 { return s1
.ptr
== s2
.ptr
; }
522 MyEqual
& operator=(const MyEqual
&) { return *this; }
525 WX_DECLARE_HASH_SET( MyStruct
, MyHash
, MyEqual
, mySet
);
527 typedef myTestHashSet5 wxStringHashSet
;
529 void HashesTestCase::wxHashSetTest()
531 wxStringHashSet set1
;
533 set1
.insert( _T("abc") );
535 CPPUNIT_ASSERT( set1
.size() == 1 );
537 set1
.insert( _T("bbc") );
538 set1
.insert( _T("cbc") );
540 CPPUNIT_ASSERT( set1
.size() == 3 );
542 set1
.insert( _T("abc") );
544 CPPUNIT_ASSERT( set1
.size() == 3 );
550 tmp
.ptr
= &dummy
; tmp
.str
= _T("ABC");
552 tmp
.ptr
= &dummy
+ 1;
554 tmp
.ptr
= &dummy
; tmp
.str
= _T("CDE");
557 CPPUNIT_ASSERT( set2
.size() == 2 );
559 mySet::iterator it
= set2
.find( tmp
);
561 CPPUNIT_ASSERT( it
!= set2
.end() );
562 CPPUNIT_ASSERT( it
->ptr
== &dummy
);
563 CPPUNIT_ASSERT( it
->str
== _T("ABC") );