1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/hashes/hashes.cpp
3 // Purpose: wxHashTable, wxHashMap, wxHashSet unit test
4 // Author: Vadim Zeitlin, Mattia Barbon
7 // Copyright: (c) 2004 Vadim Zeitlin, Mattia Barbon
8 ///////////////////////////////////////////////////////////////////////////////
10 // ----------------------------------------------------------------------------
12 // ----------------------------------------------------------------------------
25 #include "wx/hashmap.h"
26 #include "wx/hashset.h"
28 // --------------------------------------------------------------------------
29 // helper class for typed/untyped wxHashTable
30 // --------------------------------------------------------------------------
34 Foo(int n_
) { n
= n_
; count
++; }
42 size_t Foo::count
= 0;
44 struct FooObject
: public wxObject
46 FooObject(int n_
) { n
= n_
; count
++; }
47 ~FooObject() { count
--; }
54 size_t FooObject::count
= 0;
56 // --------------------------------------------------------------------------
58 // --------------------------------------------------------------------------
60 class HashesTestCase
: public CppUnit::TestCase
66 CPPUNIT_TEST_SUITE( HashesTestCase
);
67 CPPUNIT_TEST( wxHashTableTest
);
68 CPPUNIT_TEST( wxUntypedHashTableDeleteContents
);
69 CPPUNIT_TEST( wxTypedHashTableTest
);
70 CPPUNIT_TEST( wxHashMapTest
);
71 CPPUNIT_TEST( wxHashSetTest
);
72 CPPUNIT_TEST_SUITE_END();
74 void wxHashTableTest();
75 void wxUntypedHashTableDeleteContents();
76 void wxTypedHashTableTest();
80 DECLARE_NO_COPY_CLASS(HashesTestCase
)
83 // register in the unnamed registry so that these tests are run by default
84 CPPUNIT_TEST_SUITE_REGISTRATION( HashesTestCase
);
86 // also include in it's own registry so that these tests can be run alone
87 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( HashesTestCase
, "HashesTestCase" );
89 void HashesTestCase::wxHashTableTest()
91 const int COUNT
= 100;
94 wxHashTable
hash(wxKEY_INTEGER
, 10), hash2(wxKEY_STRING
);
98 for ( i
= 0; i
< COUNT
; ++i
)
102 wxHashTable::compatibility_iterator it
= hash
.Next();
111 CPPUNIT_ASSERT( i
== COUNT
);
113 for ( i
= 99; i
>= 0; --i
)
114 CPPUNIT_ASSERT( hash
.Get(i
) == &o
+ i
);
116 for ( i
= 0; i
< COUNT
; ++i
)
117 hash
.Put(i
, &o
+ i
+ 20);
119 for ( i
= 99; i
>= 0; --i
)
120 CPPUNIT_ASSERT( hash
.Get(i
) == &o
+ i
);
122 for ( i
= 0; i
< COUNT
/2; ++i
)
123 CPPUNIT_ASSERT( hash
.Delete(i
) == &o
+ i
);
125 for ( i
= COUNT
/2; i
< COUNT
; ++i
)
126 CPPUNIT_ASSERT( hash
.Get(i
) == &o
+ i
);
128 for ( i
= 0; i
< COUNT
/2; ++i
)
129 CPPUNIT_ASSERT( hash
.Get(i
) == &o
+ i
+ 20);
131 for ( i
= 0; i
< COUNT
/2; ++i
)
132 CPPUNIT_ASSERT( hash
.Delete(i
) == &o
+ i
+ 20);
134 for ( i
= 0; i
< COUNT
/2; ++i
)
135 CPPUNIT_ASSERT( hash
.Get(i
) == NULL
);
137 hash2
.Put(_T("foo"), &o
+ 1);
138 hash2
.Put(_T("bar"), &o
+ 2);
139 hash2
.Put(_T("baz"), &o
+ 3);
141 CPPUNIT_ASSERT(hash2
.Get(_T("moo")) == NULL
);
142 CPPUNIT_ASSERT(hash2
.Get(_T("bar")) == &o
+ 2);
144 hash2
.Put(_T("bar"), &o
+ 0);
146 CPPUNIT_ASSERT(hash2
.Get(_T("bar")) == &o
+ 2);
149 // and now some corner-case testing; 3 and 13 hash to the same bucket
151 wxHashTable
hash(wxKEY_INTEGER
, 10);
157 CPPUNIT_ASSERT(hash
.Get(3) == NULL
);
160 hash
.Put(13, &dummy
);
163 CPPUNIT_ASSERT(hash
.Get(3) == NULL
);
167 CPPUNIT_ASSERT(hash
.Get(13) == NULL
);
170 hash
.Put(13, &dummy
);
173 CPPUNIT_ASSERT(hash
.Get(13) == NULL
);
177 CPPUNIT_ASSERT(hash
.Get(3) == NULL
);
180 // test for key + value access (specifically that supplying either
181 // wrong key or wrong value returns NULL)
183 wxHashTable
hash(wxKEY_INTEGER
, 10);
186 hash
.Put(3, 7, &dummy
+ 7);
187 hash
.Put(4, 8, &dummy
+ 8);
189 CPPUNIT_ASSERT(hash
.Get(7) == NULL
);
190 CPPUNIT_ASSERT(hash
.Get(3, 7) == &dummy
+ 7);
191 CPPUNIT_ASSERT(hash
.Get(4) == NULL
);
192 CPPUNIT_ASSERT(hash
.Get(3) == NULL
);
193 CPPUNIT_ASSERT(hash
.Get(8) == NULL
);
194 CPPUNIT_ASSERT(hash
.Get(8, 4) == NULL
);
196 CPPUNIT_ASSERT(hash
.Delete(7) == NULL
);
197 CPPUNIT_ASSERT(hash
.Delete(3) == NULL
);
198 CPPUNIT_ASSERT(hash
.Delete(3, 7) == &dummy
+ 7);
203 void HashesTestCase::wxUntypedHashTableDeleteContents()
205 // need a nested scope for destruction
208 hash
.DeleteContents(true);
210 CPPUNIT_ASSERT( hash
.GetCount() == 0 );
211 CPPUNIT_ASSERT( FooObject::count
== 0 );
213 static const int hashTestData
[] =
215 0, 1, 17, -2, 2, 4, -4, 345, 3, 3, 2, 1,
219 for ( n
= 0; n
< WXSIZEOF(hashTestData
); n
++ )
221 hash
.Put(hashTestData
[n
], n
, new FooObject(n
));
224 CPPUNIT_ASSERT( hash
.GetCount() == WXSIZEOF(hashTestData
) );
225 CPPUNIT_ASSERT( FooObject::count
== WXSIZEOF(hashTestData
) );
227 // delete from hash without deleting object
228 FooObject
* foo
= (FooObject
*)hash
.Delete(0l);
230 CPPUNIT_ASSERT( FooObject::count
== WXSIZEOF(hashTestData
) );
232 CPPUNIT_ASSERT( FooObject::count
== WXSIZEOF(hashTestData
) - 1 );
236 CPPUNIT_ASSERT( FooObject::count
== 0 );
239 #if WXWIN_COMPATIBILITY_2_4
240 WX_DECLARE_LIST(Foo
, wxListFoos
);
243 WX_DECLARE_HASH(Foo
, wxListFoos
, wxHashFoos
);
245 #if WXWIN_COMPATIBILITY_2_4
246 #include "wx/listimpl.cpp"
247 WX_DEFINE_LIST(wxListFoos
);
250 void HashesTestCase::wxTypedHashTableTest()
252 // need a nested scope for destruction
255 hash
.DeleteContents(true);
257 CPPUNIT_ASSERT( hash
.GetCount() == 0 );
258 CPPUNIT_ASSERT( Foo::count
== 0 );
260 static const int hashTestData
[] =
262 0, 1, 17, -2, 2, 4, -4, 345, 3, 3, 2, 1,
266 for ( n
= 0; n
< WXSIZEOF(hashTestData
); n
++ )
268 hash
.Put(hashTestData
[n
], n
, new Foo(n
));
271 CPPUNIT_ASSERT( hash
.GetCount() == WXSIZEOF(hashTestData
) );
272 CPPUNIT_ASSERT( Foo::count
== WXSIZEOF(hashTestData
) );
274 for ( n
= 0; n
< WXSIZEOF(hashTestData
); n
++ )
276 Foo
*foo
= hash
.Get(hashTestData
[n
], n
);
278 CPPUNIT_ASSERT( foo
!= NULL
);
279 CPPUNIT_ASSERT( foo
->n
== (int)n
);
282 // element not in hash
283 CPPUNIT_ASSERT( hash
.Get(1234) == NULL
);
284 CPPUNIT_ASSERT( hash
.Get(1, 0) == NULL
);
286 // delete from hash without deleting object
287 Foo
* foo
= hash
.Delete(0);
289 CPPUNIT_ASSERT( Foo::count
== WXSIZEOF(hashTestData
) );
291 CPPUNIT_ASSERT( Foo::count
== WXSIZEOF(hashTestData
) - 1 );
295 CPPUNIT_ASSERT( Foo::count
== 0 );
298 // test compilation of basic map types
299 WX_DECLARE_HASH_MAP( int*, int*, wxPointerHash
, wxPointerEqual
, myPtrHashMap
);
300 WX_DECLARE_HASH_MAP( long, long, wxIntegerHash
, wxIntegerEqual
, myLongHashMap
);
301 WX_DECLARE_HASH_MAP( unsigned long, unsigned, wxIntegerHash
, wxIntegerEqual
,
303 WX_DECLARE_HASH_MAP( unsigned int, unsigned, wxIntegerHash
, wxIntegerEqual
,
305 WX_DECLARE_HASH_MAP( int, unsigned, wxIntegerHash
, wxIntegerEqual
,
307 WX_DECLARE_HASH_MAP( short, unsigned, wxIntegerHash
, wxIntegerEqual
,
309 WX_DECLARE_HASH_MAP( unsigned short, unsigned, wxIntegerHash
, wxIntegerEqual
,
313 // WX_DECLARE_HASH_MAP( wxString, wxString, wxStringHash, wxStringEqual,
314 // myStringHashMap );
315 WX_DECLARE_STRING_HASH_MAP(wxString
, myStringHashMap
);
317 typedef myStringHashMap::iterator Itor
;
319 void HashesTestCase::wxHashMapTest()
321 myStringHashMap
sh(0); // as small as possible
324 const size_t count
= 10000;
326 // init with some data
327 for( i
= 0; i
< count
; ++i
)
329 buf
.Printf(wxT("%d"), i
);
330 sh
[buf
] = wxT("A") + buf
+ wxT("C");
333 // test that insertion worked
334 CPPUNIT_ASSERT( sh
.size() == count
);
336 for( i
= 0; i
< count
; ++i
)
338 buf
.Printf(wxT("%d"), i
);
339 CPPUNIT_ASSERT( sh
[buf
] == wxT("A") + buf
+ wxT("C") );
342 // check that iterators work
344 for( i
= 0, it
= sh
.begin(); it
!= sh
.end(); ++it
, ++i
)
346 CPPUNIT_ASSERT( i
!= count
);
347 CPPUNIT_ASSERT( it
->second
== sh
[it
->first
] );
350 CPPUNIT_ASSERT( sh
.size() == i
);
352 // test copy ctor, assignment operator
353 myStringHashMap
h1( sh
), h2( 0 );
356 for( i
= 0, it
= sh
.begin(); it
!= sh
.end(); ++it
, ++i
)
358 CPPUNIT_ASSERT( h1
[it
->first
] == it
->second
);
359 CPPUNIT_ASSERT( h2
[it
->first
] == it
->second
);
363 for( i
= 0; i
< count
; ++i
)
365 buf
.Printf(wxT("%d"), i
);
366 size_t sz
= sh
.size();
368 // test find() and erase(it)
372 CPPUNIT_ASSERT( it
!= sh
.end() );
376 CPPUNIT_ASSERT( sh
.find( buf
) == sh
.end() );
381 size_t c
= sh
.erase( buf
);
382 CPPUNIT_ASSERT( c
== 1 );
383 CPPUNIT_ASSERT( sh
.find( buf
) == sh
.end() );
386 // count should decrease
387 CPPUNIT_ASSERT( sh
.size() == sz
- 1 );
391 // test compilation of basic set types
392 WX_DECLARE_HASH_SET( int*, wxPointerHash
, wxPointerEqual
, myPtrHashSet
);
393 WX_DECLARE_HASH_SET( long, wxIntegerHash
, wxIntegerEqual
, myLongHashSet
);
394 WX_DECLARE_HASH_SET( unsigned long, wxIntegerHash
, wxIntegerEqual
,
396 WX_DECLARE_HASH_SET( unsigned int, wxIntegerHash
, wxIntegerEqual
,
398 WX_DECLARE_HASH_SET( int, wxIntegerHash
, wxIntegerEqual
,
400 WX_DECLARE_HASH_SET( short, wxIntegerHash
, wxIntegerEqual
,
402 WX_DECLARE_HASH_SET( unsigned short, wxIntegerHash
, wxIntegerEqual
,
404 WX_DECLARE_HASH_SET( wxString
, wxStringHash
, wxStringEqual
,
416 unsigned long operator()(const MyStruct
& s
) const
417 { return m_dummy(s
.ptr
); }
418 MyHash
& operator=(const MyHash
&) { return *this; }
420 wxPointerHash m_dummy
;
426 bool operator()(const MyStruct
& s1
, const MyStruct
& s2
) const
427 { return s1
.ptr
== s2
.ptr
; }
428 MyEqual
& operator=(const MyEqual
&) { return *this; }
431 WX_DECLARE_HASH_SET( MyStruct
, MyHash
, MyEqual
, mySet
);
433 typedef myTestHashSet5 wxStringHashSet
;
435 void HashesTestCase::wxHashSetTest()
437 wxStringHashSet set1
;
439 set1
.insert( _T("abc") );
441 CPPUNIT_ASSERT( set1
.size() == 1 );
443 set1
.insert( _T("bbc") );
444 set1
.insert( _T("cbc") );
446 CPPUNIT_ASSERT( set1
.size() == 3 );
448 set1
.insert( _T("abc") );
450 CPPUNIT_ASSERT( set1
.size() == 3 );
456 tmp
.ptr
= &dummy
; tmp
.str
= _T("ABC");
458 tmp
.ptr
= &dummy
+ 1;
460 tmp
.ptr
= &dummy
; tmp
.str
= _T("CDE");
463 CPPUNIT_ASSERT( set2
.size() == 2 );
465 mySet::iterator it
= set2
.find( tmp
);
467 CPPUNIT_ASSERT( it
!= set2
.end() );
468 CPPUNIT_ASSERT( it
->ptr
== &dummy
);
469 CPPUNIT_ASSERT( it
->str
== _T("ABC") );