hashes.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  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. // headers
  11. // ----------------------------------------------------------------------------
  12. #include "testprec.h"
  13. #ifdef __BORLANDC__
  14. #pragma hdrstop
  15. #endif
  16. #ifndef WX_PRECOMP
  17. #include "wx/wx.h"
  18. #endif // WX_PRECOMP
  19. #include "wx/hash.h"
  20. #include "wx/hashmap.h"
  21. #include "wx/hashset.h"
  22. #if defined wxLongLong_t && !defined wxLongLongIsLong && \
  23. (!defined __VISUALC__ || __VISUALC__ > 1100) // doesn't work on VC5
  24. #define TEST_LONGLONG
  25. #endif
  26. // --------------------------------------------------------------------------
  27. // helper class for typed/untyped wxHashTable
  28. // --------------------------------------------------------------------------
  29. struct Foo
  30. {
  31. Foo(int n_) { n = n_; count++; }
  32. ~Foo() { count--; }
  33. int n;
  34. static size_t count;
  35. };
  36. size_t Foo::count = 0;
  37. struct FooObject : public wxObject
  38. {
  39. FooObject(int n_) { n = n_; count++; }
  40. ~FooObject() { count--; }
  41. int n;
  42. static size_t count;
  43. };
  44. size_t FooObject::count = 0;
  45. // --------------------------------------------------------------------------
  46. // test class
  47. // --------------------------------------------------------------------------
  48. class HashesTestCase : public CppUnit::TestCase
  49. {
  50. public:
  51. HashesTestCase() { }
  52. private:
  53. CPPUNIT_TEST_SUITE( HashesTestCase );
  54. CPPUNIT_TEST( wxHashTableTest );
  55. CPPUNIT_TEST( wxUntypedHashTableDeleteContents );
  56. CPPUNIT_TEST( wxTypedHashTableTest );
  57. CPPUNIT_TEST( StringHashMapTest );
  58. CPPUNIT_TEST( PtrHashMapTest );
  59. CPPUNIT_TEST( LongHashMapTest );
  60. CPPUNIT_TEST( ULongHashMapTest );
  61. CPPUNIT_TEST( UIntHashMapTest );
  62. CPPUNIT_TEST( IntHashMapTest );
  63. CPPUNIT_TEST( ShortHashMapTest );
  64. CPPUNIT_TEST( UShortHashMapTest );
  65. #ifdef TEST_LONGLONG
  66. CPPUNIT_TEST( LLongHashMapTest );
  67. CPPUNIT_TEST( ULLongHashMapTest );
  68. #endif
  69. CPPUNIT_TEST( wxHashSetTest );
  70. CPPUNIT_TEST_SUITE_END();
  71. void wxHashTableTest();
  72. void wxUntypedHashTableDeleteContents();
  73. void wxTypedHashTableTest();
  74. void StringHashMapTest();
  75. void PtrHashMapTest();
  76. void LongHashMapTest();
  77. void ULongHashMapTest();
  78. void UIntHashMapTest();
  79. void IntHashMapTest();
  80. void ShortHashMapTest();
  81. void UShortHashMapTest();
  82. #ifdef TEST_LONGLONG
  83. void LLongHashMapTest();
  84. void ULLongHashMapTest();
  85. #endif
  86. void wxHashSetTest();
  87. DECLARE_NO_COPY_CLASS(HashesTestCase)
  88. };
  89. // register in the unnamed registry so that these tests are run by default
  90. CPPUNIT_TEST_SUITE_REGISTRATION( HashesTestCase );
  91. // also include in its own registry so that these tests can be run alone
  92. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( HashesTestCase, "HashesTestCase" );
  93. void HashesTestCase::wxHashTableTest()
  94. {
  95. const int COUNT = 100;
  96. {
  97. wxHashTable hash(wxKEY_INTEGER, 10), hash2(wxKEY_STRING);
  98. wxObject o;
  99. int i;
  100. for ( i = 0; i < COUNT; ++i )
  101. hash.Put(i, &o + i);
  102. hash.BeginFind();
  103. wxHashTable::compatibility_iterator it = hash.Next();
  104. i = 0;
  105. while (it)
  106. {
  107. ++i;
  108. it = hash.Next();
  109. }
  110. CPPUNIT_ASSERT( i == COUNT );
  111. for ( i = 99; i >= 0; --i )
  112. CPPUNIT_ASSERT( hash.Get(i) == &o + i );
  113. for ( i = 0; i < COUNT; ++i )
  114. hash.Put(i, &o + i + 20);
  115. for ( i = 99; i >= 0; --i )
  116. CPPUNIT_ASSERT( hash.Get(i) == &o + i);
  117. for ( i = 0; i < COUNT/2; ++i )
  118. CPPUNIT_ASSERT( hash.Delete(i) == &o + i);
  119. for ( i = COUNT/2; i < COUNT; ++i )
  120. CPPUNIT_ASSERT( hash.Get(i) == &o + i);
  121. for ( i = 0; i < COUNT/2; ++i )
  122. CPPUNIT_ASSERT( hash.Get(i) == &o + i + 20);
  123. for ( i = 0; i < COUNT/2; ++i )
  124. CPPUNIT_ASSERT( hash.Delete(i) == &o + i + 20);
  125. for ( i = 0; i < COUNT/2; ++i )
  126. CPPUNIT_ASSERT( hash.Get(i) == NULL);
  127. hash2.Put(wxT("foo"), &o + 1);
  128. hash2.Put(wxT("bar"), &o + 2);
  129. hash2.Put(wxT("baz"), &o + 3);
  130. CPPUNIT_ASSERT(hash2.Get(wxT("moo")) == NULL);
  131. CPPUNIT_ASSERT(hash2.Get(wxT("bar")) == &o + 2);
  132. hash2.Put(wxT("bar"), &o + 0);
  133. CPPUNIT_ASSERT(hash2.Get(wxT("bar")) == &o + 2);
  134. }
  135. // and now some corner-case testing; 3 and 13 hash to the same bucket
  136. {
  137. wxHashTable hash(wxKEY_INTEGER, 10);
  138. wxObject dummy;
  139. hash.Put(3, &dummy);
  140. hash.Delete(3);
  141. CPPUNIT_ASSERT(hash.Get(3) == NULL);
  142. hash.Put(3, &dummy);
  143. hash.Put(13, &dummy);
  144. hash.Delete(3);
  145. CPPUNIT_ASSERT(hash.Get(3) == NULL);
  146. hash.Delete(13);
  147. CPPUNIT_ASSERT(hash.Get(13) == NULL);
  148. hash.Put(3, &dummy);
  149. hash.Put(13, &dummy);
  150. hash.Delete(13);
  151. CPPUNIT_ASSERT(hash.Get(13) == NULL);
  152. hash.Delete(3);
  153. CPPUNIT_ASSERT(hash.Get(3) == NULL);
  154. }
  155. // test for key + value access (specifically that supplying either
  156. // wrong key or wrong value returns NULL)
  157. {
  158. wxHashTable hash(wxKEY_INTEGER, 10);
  159. wxObject dummy;
  160. hash.Put(3, 7, &dummy + 7);
  161. hash.Put(4, 8, &dummy + 8);
  162. CPPUNIT_ASSERT(hash.Get(7) == NULL);
  163. CPPUNIT_ASSERT(hash.Get(3, 7) == &dummy + 7);
  164. CPPUNIT_ASSERT(hash.Get(4) == NULL);
  165. CPPUNIT_ASSERT(hash.Get(3) == NULL);
  166. CPPUNIT_ASSERT(hash.Get(8) == NULL);
  167. CPPUNIT_ASSERT(hash.Get(8, 4) == NULL);
  168. CPPUNIT_ASSERT(hash.Delete(7) == NULL);
  169. CPPUNIT_ASSERT(hash.Delete(3) == NULL);
  170. CPPUNIT_ASSERT(hash.Delete(3, 7) == &dummy + 7);
  171. }
  172. }
  173. void HashesTestCase::wxUntypedHashTableDeleteContents()
  174. {
  175. // need a nested scope for destruction
  176. {
  177. wxHashTable hash;
  178. hash.DeleteContents(true);
  179. CPPUNIT_ASSERT( hash.GetCount() == 0 );
  180. CPPUNIT_ASSERT( FooObject::count == 0 );
  181. static const int hashTestData[] =
  182. {
  183. 0, 1, 17, -2, 2, 4, -4, 345, 3, 3, 2, 1,
  184. };
  185. size_t n;
  186. for ( n = 0; n < WXSIZEOF(hashTestData); n++ )
  187. {
  188. hash.Put(hashTestData[n], n, new FooObject(n));
  189. }
  190. CPPUNIT_ASSERT( hash.GetCount() == WXSIZEOF(hashTestData) );
  191. CPPUNIT_ASSERT( FooObject::count == WXSIZEOF(hashTestData) );
  192. // delete from hash without deleting object
  193. FooObject* foo = (FooObject*)hash.Delete(0l);
  194. CPPUNIT_ASSERT( FooObject::count == WXSIZEOF(hashTestData) );
  195. delete foo;
  196. CPPUNIT_ASSERT( FooObject::count == WXSIZEOF(hashTestData) - 1 );
  197. }
  198. // hash destroyed
  199. CPPUNIT_ASSERT( FooObject::count == 0 );
  200. }
  201. WX_DECLARE_HASH(Foo, wxListFoos, wxHashFoos);
  202. void HashesTestCase::wxTypedHashTableTest()
  203. {
  204. // need a nested scope for destruction
  205. {
  206. wxHashFoos hash;
  207. hash.DeleteContents(true);
  208. CPPUNIT_ASSERT( hash.GetCount() == 0 );
  209. CPPUNIT_ASSERT( Foo::count == 0 );
  210. static const int hashTestData[] =
  211. {
  212. 0, 1, 17, -2, 2, 4, -4, 345, 3, 3, 2, 1,
  213. };
  214. size_t n;
  215. for ( n = 0; n < WXSIZEOF(hashTestData); n++ )
  216. {
  217. hash.Put(hashTestData[n], n, new Foo(n));
  218. }
  219. CPPUNIT_ASSERT( hash.GetCount() == WXSIZEOF(hashTestData) );
  220. CPPUNIT_ASSERT( Foo::count == WXSIZEOF(hashTestData) );
  221. for ( n = 0; n < WXSIZEOF(hashTestData); n++ )
  222. {
  223. Foo *foo = hash.Get(hashTestData[n], n);
  224. CPPUNIT_ASSERT( foo != NULL );
  225. CPPUNIT_ASSERT( foo->n == (int)n );
  226. }
  227. // element not in hash
  228. CPPUNIT_ASSERT( hash.Get(1234) == NULL );
  229. CPPUNIT_ASSERT( hash.Get(1, 0) == NULL );
  230. // delete from hash without deleting object
  231. Foo* foo = hash.Delete(0);
  232. CPPUNIT_ASSERT( Foo::count == WXSIZEOF(hashTestData) );
  233. delete foo;
  234. CPPUNIT_ASSERT( Foo::count == WXSIZEOF(hashTestData) - 1 );
  235. }
  236. // hash destroyed
  237. CPPUNIT_ASSERT( Foo::count == 0 );
  238. }
  239. // test compilation of basic map types
  240. WX_DECLARE_HASH_MAP( int*, int*, wxPointerHash, wxPointerEqual, myPtrHashMap );
  241. WX_DECLARE_HASH_MAP( long, long, wxIntegerHash, wxIntegerEqual, myLongHashMap );
  242. WX_DECLARE_HASH_MAP( unsigned long, unsigned, wxIntegerHash, wxIntegerEqual,
  243. myUnsignedHashMap );
  244. WX_DECLARE_HASH_MAP( unsigned int, unsigned, wxIntegerHash, wxIntegerEqual,
  245. myTestHashMap1 );
  246. WX_DECLARE_HASH_MAP( int, unsigned, wxIntegerHash, wxIntegerEqual,
  247. myTestHashMap2 );
  248. WX_DECLARE_HASH_MAP( short, unsigned, wxIntegerHash, wxIntegerEqual,
  249. myTestHashMap3 );
  250. WX_DECLARE_HASH_MAP( unsigned short, unsigned, wxIntegerHash, wxIntegerEqual,
  251. myTestHashMap4 );
  252. // same as:
  253. // WX_DECLARE_HASH_MAP( wxString, wxString, wxStringHash, wxStringEqual,
  254. // myStringHashMap );
  255. WX_DECLARE_STRING_HASH_MAP(wxString, myStringHashMap);
  256. #ifdef TEST_LONGLONG
  257. WX_DECLARE_HASH_MAP( wxLongLong_t, wxLongLong_t,
  258. wxIntegerHash, wxIntegerEqual, myLLongHashMap );
  259. WX_DECLARE_HASH_MAP( wxULongLong_t, wxULongLong_t,
  260. wxIntegerHash, wxIntegerEqual, myULLongHashMap );
  261. #endif
  262. // Helpers to generate a key value pair for item 'i', out of a total of 'count'
  263. void MakeKeyValuePair(size_t i, size_t /*count*/, wxString& key, wxString& val)
  264. {
  265. key.clear();
  266. key << i;
  267. val = wxT("A") + key + wxT("C");
  268. }
  269. // for integral keys generate a range of keys that will use all the bits of
  270. // the key type
  271. template <class IntT, class KeyT>
  272. IntT MakeKey(size_t i, size_t count)
  273. {
  274. IntT max = 1;
  275. max <<= sizeof(KeyT) * 8 - 2;
  276. max -= count / 4 + 1;
  277. return max / count * 4 * i + i / 3;
  278. }
  279. // make key/value pairs for integer types
  280. template <class KeyT, class ValueT>
  281. void MakeKeyValuePair(size_t i, size_t count, KeyT& key, ValueT& value)
  282. {
  283. key = MakeKey<KeyT, KeyT>(i, count);
  284. value = wx_truncate_cast(ValueT, key);
  285. }
  286. // make key/values paris for pointer types
  287. template <class T, class ValueT>
  288. void MakeKeyValuePair(size_t i, size_t count, T*& key, ValueT& value)
  289. {
  290. key = (T*)wxUIntToPtr(MakeKey<wxUIntPtr, T*>(i, count));
  291. value = wx_truncate_cast(ValueT, key);
  292. }
  293. // the test
  294. template <class HashMapT>
  295. void HashMapTest()
  296. {
  297. typedef typename HashMapT::value_type::second_type value_type;
  298. typedef typename HashMapT::key_type key_type;
  299. typedef typename HashMapT::iterator Itor;
  300. HashMapT sh(0); // as small as possible
  301. key_type buf;
  302. value_type value;
  303. size_t i;
  304. const size_t count = 10000;
  305. // init with some data
  306. for( i = 0; i < count; ++i )
  307. {
  308. MakeKeyValuePair(i, count, buf, value);
  309. sh[buf] = value;
  310. }
  311. // test that insertion worked
  312. CPPUNIT_ASSERT( sh.size() == count );
  313. for( i = 0; i < count; ++i )
  314. {
  315. MakeKeyValuePair(i, count, buf, value);
  316. CPPUNIT_ASSERT( sh[buf] == value );
  317. }
  318. // check that iterators work
  319. Itor it;
  320. for( i = 0, it = sh.begin(); it != sh.end(); ++it, ++i )
  321. {
  322. CPPUNIT_ASSERT( i != count );
  323. CPPUNIT_ASSERT( it->second == sh[it->first] );
  324. }
  325. CPPUNIT_ASSERT( sh.size() == i );
  326. // test copy ctor, assignment operator
  327. HashMapT h1( sh ), h2( 0 );
  328. h2 = sh;
  329. for( i = 0, it = sh.begin(); it != sh.end(); ++it, ++i )
  330. {
  331. CPPUNIT_ASSERT( h1[it->first] == it->second );
  332. CPPUNIT_ASSERT( h2[it->first] == it->second );
  333. }
  334. // other tests
  335. for( i = 0; i < count; ++i )
  336. {
  337. MakeKeyValuePair(i, count, buf, value);
  338. size_t sz = sh.size();
  339. // test find() and erase(it)
  340. if( i < 100 )
  341. {
  342. it = sh.find( buf );
  343. CPPUNIT_ASSERT( it != sh.end() );
  344. sh.erase( it );
  345. CPPUNIT_ASSERT( sh.find( buf ) == sh.end() );
  346. }
  347. else
  348. // test erase(key)
  349. {
  350. size_t c = sh.erase( buf );
  351. CPPUNIT_ASSERT( c == 1 );
  352. CPPUNIT_ASSERT( sh.find( buf ) == sh.end() );
  353. }
  354. // count should decrease
  355. CPPUNIT_ASSERT( sh.size() == sz - 1 );
  356. }
  357. }
  358. void HashesTestCase::StringHashMapTest() { HashMapTest<myStringHashMap>(); }
  359. void HashesTestCase::PtrHashMapTest() { HashMapTest<myPtrHashMap>(); }
  360. void HashesTestCase::LongHashMapTest() { HashMapTest<myLongHashMap>(); }
  361. void HashesTestCase::ULongHashMapTest() { HashMapTest<myUnsignedHashMap>(); }
  362. void HashesTestCase::UIntHashMapTest() { HashMapTest<myTestHashMap1>(); }
  363. void HashesTestCase::IntHashMapTest() { HashMapTest<myTestHashMap2>(); }
  364. void HashesTestCase::ShortHashMapTest() { HashMapTest<myTestHashMap3>(); }
  365. void HashesTestCase::UShortHashMapTest() { HashMapTest<myTestHashMap4>(); }
  366. #ifdef TEST_LONGLONG
  367. void HashesTestCase::LLongHashMapTest() { HashMapTest<myLLongHashMap>(); }
  368. void HashesTestCase::ULLongHashMapTest() { HashMapTest<myULLongHashMap>(); }
  369. #endif
  370. #ifdef __VISUALC__
  371. #if __VISUALC__ <= 1200
  372. #pragma warning(disable:4284) // operator->() returns a non-UDT
  373. #endif
  374. #endif // __VISUALC__
  375. // test compilation of basic set types
  376. WX_DECLARE_HASH_SET( int*, wxPointerHash, wxPointerEqual, myPtrHashSet );
  377. WX_DECLARE_HASH_SET( long, wxIntegerHash, wxIntegerEqual, myLongHashSet );
  378. WX_DECLARE_HASH_SET( unsigned long, wxIntegerHash, wxIntegerEqual,
  379. myUnsignedHashSet );
  380. WX_DECLARE_HASH_SET( unsigned int, wxIntegerHash, wxIntegerEqual,
  381. myTestHashSet1 );
  382. WX_DECLARE_HASH_SET( int, wxIntegerHash, wxIntegerEqual,
  383. myTestHashSet2 );
  384. WX_DECLARE_HASH_SET( short, wxIntegerHash, wxIntegerEqual,
  385. myTestHashSet3 );
  386. WX_DECLARE_HASH_SET( unsigned short, wxIntegerHash, wxIntegerEqual,
  387. myTestHashSet4 );
  388. WX_DECLARE_HASH_SET( wxString, wxStringHash, wxStringEqual,
  389. myTestHashSet5 );
  390. struct MyStruct
  391. {
  392. int* ptr;
  393. wxString str;
  394. };
  395. class MyHash
  396. {
  397. public:
  398. unsigned long operator()(const MyStruct& s) const
  399. { return m_dummy(s.ptr); }
  400. MyHash& operator=(const MyHash&) { return *this; }
  401. private:
  402. wxPointerHash m_dummy;
  403. };
  404. class MyEqual
  405. {
  406. public:
  407. bool operator()(const MyStruct& s1, const MyStruct& s2) const
  408. { return s1.ptr == s2.ptr; }
  409. MyEqual& operator=(const MyEqual&) { return *this; }
  410. };
  411. WX_DECLARE_HASH_SET( MyStruct, MyHash, MyEqual, mySet );
  412. typedef myTestHashSet5 wxStringHashSet;
  413. void HashesTestCase::wxHashSetTest()
  414. {
  415. wxStringHashSet set1;
  416. set1.insert( wxT("abc") );
  417. CPPUNIT_ASSERT( set1.size() == 1 );
  418. set1.insert( wxT("bbc") );
  419. set1.insert( wxT("cbc") );
  420. CPPUNIT_ASSERT( set1.size() == 3 );
  421. set1.insert( wxT("abc") );
  422. CPPUNIT_ASSERT( set1.size() == 3 );
  423. mySet set2;
  424. int dummy;
  425. MyStruct tmp;
  426. tmp.ptr = &dummy; tmp.str = wxT("ABC");
  427. set2.insert( tmp );
  428. tmp.ptr = &dummy + 1;
  429. set2.insert( tmp );
  430. tmp.ptr = &dummy; tmp.str = wxT("CDE");
  431. set2.insert( tmp );
  432. CPPUNIT_ASSERT( set2.size() == 2 );
  433. mySet::iterator it = set2.find( tmp );
  434. CPPUNIT_ASSERT( it != set2.end() );
  435. CPPUNIT_ASSERT( it->ptr == &dummy );
  436. CPPUNIT_ASSERT( it->str == wxT("ABC") );
  437. }