Home | History | Annotate | Download | only in unit
      1 //Has to be first for StackAllocator swap overload to be taken
      2 //into account (at least using GCC 4.0.1)
      3 #include "stack_allocator.h"
      4 
      5 #include <vector>
      6 #include <algorithm>
      7 #include <map>
      8 #include <set>
      9 
     10 #if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
     11 #  include <hash_map>
     12 #  include <hash_set>
     13 #  include <rope>
     14 #endif
     15 
     16 #include <string>
     17 
     18 #include "cppunit/cppunit_proxy.h"
     19 
     20 #if defined (__MVS__)
     21 const char star = 92;
     22 #else
     23 const char star = 42;
     24 #endif
     25 
     26 #if !defined (STLPORT) || defined (_STLP_USE_NAMESPACES)
     27 using namespace std;
     28 #endif
     29 
     30 //
     31 // TestCase class
     32 //
     33 class HashTest : public CPPUNIT_NS::TestCase
     34 {
     35   CPPUNIT_TEST_SUITE(HashTest);
     36 #if !defined (STLPORT) || defined (_STLP_NO_EXTENSIONS)
     37   CPPUNIT_IGNORE;
     38 #endif
     39   CPPUNIT_TEST(hmap1);
     40   CPPUNIT_TEST(hmmap1);
     41   CPPUNIT_TEST(hmmap2);
     42   CPPUNIT_TEST(hmset1);
     43   CPPUNIT_TEST(hset2);
     44   CPPUNIT_TEST(insert_erase);
     45   CPPUNIT_TEST(allocator_with_state);
     46   //CPPUNIT_TEST(equality);
     47   CPPUNIT_TEST_SUITE_END();
     48 
     49 #if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
     50   typedef hash_multiset<char, hash<char>, equal_to<char> > hmset;
     51 #endif
     52 
     53 protected:
     54   void hmap1();
     55   void hmmap1();
     56   void hmmap2();
     57   void hmset1();
     58   void hset2();
     59   void insert_erase();
     60   //void equality();
     61   void allocator_with_state();
     62 
     63 #if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
     64   typedef hash_multimap<int, int> hashType;
     65   typedef multimap<int, int> mapType;
     66 
     67   void check_keys( hashType& h, mapType& m );
     68 #endif
     69 };
     70 
     71 CPPUNIT_TEST_SUITE_REGISTRATION(HashTest);
     72 
     73 //
     74 // tests implementation
     75 //
     76 void HashTest::hmap1()
     77 {
     78 #if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
     79   typedef hash_map<char, crope, hash<char>, equal_to<char> > maptype;
     80   maptype m;
     81   // Store mappings between roman numerals and decimals.
     82   m['l'] = "50";
     83   m['x'] = "20"; // Deliberate mistake.
     84   m['v'] = "5";
     85   m['i'] = "1";
     86   CPPUNIT_ASSERT( !strcmp(m['x'].c_str(),"20") );
     87   m['x'] = "10"; // Correct mistake.
     88   CPPUNIT_ASSERT( !strcmp(m['x'].c_str(),"10") );
     89 
     90   CPPUNIT_ASSERT( !strcmp(m['z'].c_str(),"") );
     91 
     92   CPPUNIT_ASSERT( m.count('z')==1 );
     93   pair<maptype::iterator, bool> p = m.insert(pair<const char, crope>('c', crope("100")));
     94 
     95   CPPUNIT_ASSERT(p.second);
     96 
     97   p = m.insert(pair<const char, crope>('c', crope("100")));
     98   CPPUNIT_ASSERT(!p.second);
     99 
    100   //Some iterators compare check, really compile time checks
    101   maptype::iterator ite(m.begin());
    102   maptype::const_iterator cite(m.begin());
    103   cite = m.begin();
    104   maptype const& cm = m;
    105   cite = cm.begin();
    106   CPPUNIT_ASSERT( ite == cite );
    107   CPPUNIT_ASSERT( !(ite != cite) );
    108   CPPUNIT_ASSERT( cite == ite );
    109   CPPUNIT_ASSERT( !(cite != ite) );
    110 #endif
    111 }
    112 
    113 void HashTest::hmmap1()
    114 {
    115 #if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
    116   typedef hash_multimap<char, int, hash<char>,equal_to<char> > mmap;
    117   mmap m;
    118   CPPUNIT_ASSERT(m.count('X')==0);
    119   m.insert(pair<const char,int>('X', 10)); // Standard way.
    120   CPPUNIT_ASSERT(m.count('X')==1);
    121 //  m.insert('X', 20); // Non-standard, but very convenient!
    122   m.insert(pair<const char,int>('X', 20));  // jbuck: standard way
    123   CPPUNIT_ASSERT(m.count('X')==2);
    124 //  m.insert('Y', 32);
    125   m.insert(pair<const char,int>('Y', 32));  // jbuck: standard way
    126   mmap::iterator i = m.find('X'); // Find first match.
    127 
    128   CPPUNIT_ASSERT((*i).first=='X');
    129   CPPUNIT_ASSERT((*i).second==10);
    130   i++;
    131   CPPUNIT_ASSERT((*i).first=='X');
    132   CPPUNIT_ASSERT((*i).second==20);
    133   i++;
    134   CPPUNIT_ASSERT((*i).first=='Y');
    135   CPPUNIT_ASSERT((*i).second==32);
    136   i++;
    137   CPPUNIT_ASSERT(i==m.end());
    138 
    139   size_t count = m.erase('X');
    140   CPPUNIT_ASSERT(count==2);
    141 
    142   //Some iterators compare check, really compile time checks
    143   mmap::iterator ite(m.begin());
    144   mmap::const_iterator cite(m.begin());
    145   CPPUNIT_ASSERT( ite == cite );
    146   CPPUNIT_ASSERT( !(ite != cite) );
    147   CPPUNIT_ASSERT( cite == ite );
    148   CPPUNIT_ASSERT( !(cite != ite) );
    149 
    150   typedef hash_multimap<size_t, size_t> HMapType;
    151   HMapType hmap;
    152 
    153   //We fill the map to implicitely start a rehash.
    154   for (size_t counter = 0; counter < 3077; ++counter)
    155     hmap.insert(HMapType::value_type(1, counter));
    156 
    157   hmap.insert(HMapType::value_type(12325, 1));
    158   hmap.insert(HMapType::value_type(12325, 2));
    159 
    160   CPPUNIT_ASSERT( hmap.count(12325) == 2 );
    161 
    162   //At this point 23 goes to the same bucket as 12325, it used to reveal a bug.
    163   hmap.insert(HMapType::value_type(23, 0));
    164 
    165   CPPUNIT_ASSERT( hmap.count(12325) == 2 );
    166 #endif
    167 }
    168 
    169 #if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
    170 // Short demonstrator that helps reproducing a bug in the hash-table implementation
    171 // of STLPort 5.0.1/5.0.2.
    172 //
    173 // Problem: Fill a hash_multimap with entries of which many have the same key
    174 //          Internally, entries with the same key are kept as one block within the same bucket.
    175 //          Thus, when calling equal_range(key) the begin/end of that block is returned.
    176 //          However, this code shows that for key =3, that block is destroyed after inserting the 194th element.
    177 //          According to _hashtable.c we will have a rehash from size 193 to size 389 in that situation.
    178 //          After that rehash,  equal_range only returns 2 elements with key = 3 whereas there are 65 in it.
    179 // Reproduction:
    180 //          In the main()-method we fill a hash_multimap as well as a multi_map with the same <key, data> pairs
    181 //          After each insertion we call check_keys(...) to assure validity of these two containers.
    182 //          This works fine up to the 193th insertion. Insertion 194 generates the bug.
    183 //
    184 //          check_keys() works as follows:
    185 //          (a) we check whether both containers contain the same number of elements.
    186 //          (b) Assuming that the multi_map works correctly, we iterate over all its elements and check
    187 //              whether we can find that key also in the hash_multimap. We collect all data for that specific
    188 //              key in in a set ("collection"). Notice that data is unique by construction in main(), thus the
    189 //              number of elements in the set must equal the number of entries in the hash_multimap and in the multimap
    190 //          (c) We check if we have seen as many data elements in collection as we have seen in the multimap.
    191 //              if so, we print "OK", otherwise we print a detailed key/data overview and assert.
    192 // Caution:
    193 //        There are several configurations of the program that will NOT fail. (see comment in code below)
    194 //        E.g. it seems that whenever the keys are more or less sorted, the problem does not occur.
    195 //        Also, using numbers from 200 downto 1 or from 300 downto 1 cannot generate the problem,
    196 //        whereas using 400 downto 1 will fail.
    197 //        Finally, if we use key 1 (rather than key 3) we cannot generate a problem.
    198 
    199 void HashTest::check_keys( HashTest::hashType& h, HashTest::mapType& m )
    200 {
    201   set<int> collection;
    202 
    203   // (a) check sizes
    204   CPPUNIT_CHECK( h.size() == m.size() );
    205 
    206   // (b) iterate over multi_map
    207   for ( mapType::iterator i = m.begin(); i != m.end(); ++i ) {
    208     // look up that key in hash-table and keep all data in the set
    209     pair<hashType::iterator,hashType::iterator> range = h.equal_range( i->first );
    210     for ( hashType::iterator j = range.first; j != range.second; ++j ) {
    211       collection.insert( j->second );
    212     }
    213   }
    214   // (c) we should have seen as many elements as there are in the hash-table
    215 #if 0
    216   if (collection.size() == h.size()) cout << " OK" << endl;
    217   else {
    218     // if not, please report
    219     cout << " FAILED: " << endl;
    220     int lastKey  = -1;
    221     // iterate over all elements in multi_map
    222     for (mapType::iterator mIter = m.begin(); mIter != m.end(); mIter++) {
    223       // new key? print a new status line
    224       if (mIter->first != lastKey) {
    225         cout << endl << "Key : " << mIter->first << endl;
    226         lastKey = mIter->first;
    227 
    228         // print all hashed values for that key
    229         cout << " data in hash: ";
    230         pair<hashType::iterator,hashType::iterator> range = h.equal_range(mIter->first);
    231 
    232         for (hashType::iterator h = range.first; h != range.second; h++) {
    233           assert (h->first == lastKey);
    234           cerr << h->second << ", "; // print all data for that key in Hash-Table
    235         }
    236         cout << endl << " data in map:  ";
    237       }
    238       // and print all member in multi-map until the next key occurs
    239       cout << mIter->second << ", " ;  // print all data for that key in Map
    240     }
    241   }
    242 #endif
    243   CPPUNIT_CHECK( collection.size() == h.size() );
    244 }
    245 
    246 #endif
    247 
    248 void HashTest::hmmap2()
    249 {
    250 #if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
    251   hashType h;
    252   mapType m;
    253 
    254   // CAUTION the following configurations WORKS in our setting
    255   //      for (int id = 1; id != 400; id ++)   and int key = (id %3 == 0 ? 3 : id)
    256   //      for (int id = 200; id != 1; id --)   and int key = (id %3 == 0 ? 3 : id)
    257   //      for (int id = 300; id != 1; id --)   and int key = (id %3 == 0 ? 3 : id)
    258   //      for (int id = 400; id != 1; id --)   and int key = (id %3 == 0 ? 1 : id)
    259   //      for (int id = 4000; id != 1; id --)  and int key = (id %3 == 0 ? 1 : id)
    260   //
    261   // whereas these will FAIL
    262   //      for (int id = 400; id != 1; id --)   and int key = (id %3 == 0 ? 3 : id)
    263   //      for (int id = 4000; id != 1; id --)  and int key = (id %3 == 0 ? 3 : id)
    264   //
    265 
    266   for ( int id = 400; id != 1; id-- ) {
    267     // generate many entries with key 3, fill up with unique keys. Data is unique (needed in check_keys())
    268     int key = (id % 3 == 0 ? 3 : id);
    269 
    270     // keep hash_multi_map and multimap in sync
    271     h.insert(make_pair(key, id));
    272     m.insert(make_pair(key, id));
    273 
    274     // check whether both contain the same elements
    275     check_keys( h, m );
    276   }
    277 #endif
    278 }
    279 
    280 void HashTest::hmset1()
    281 {
    282 #if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
    283   hmset s;
    284   CPPUNIT_ASSERT( s.count(star) == 0 );
    285   s.insert(star);
    286   CPPUNIT_ASSERT( s.count(star) == 1 );
    287   s.insert(star);
    288   CPPUNIT_ASSERT( s.count(star) == 2 );
    289   hmset::iterator i = s.find(char(40));
    290   CPPUNIT_ASSERT( i == s.end() );
    291 
    292   i = s.find(star);
    293   CPPUNIT_ASSERT( i != s.end() )
    294   CPPUNIT_ASSERT( *i == '*' );
    295   CPPUNIT_ASSERT( s.erase(star) == 2 );
    296 #endif
    297 }
    298 void HashTest::hset2()
    299 {
    300 #if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
    301   hash_set<int, hash<int>, equal_to<int> > s;
    302   pair<hash_set<int, hash<int>, equal_to<int> >::iterator, bool> p = s.insert(42);
    303   CPPUNIT_ASSERT( p.second );
    304   CPPUNIT_ASSERT( *(p.first) == 42 );
    305 
    306   p = s.insert(42);
    307   CPPUNIT_ASSERT( !p.second );
    308 #endif
    309 }
    310 
    311 void HashTest::insert_erase()
    312 {
    313 #if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
    314   typedef hash_map<string, size_t, hash<string>, equal_to<string> > hmap;
    315   typedef hmap::value_type val_type;
    316   {
    317     hmap values;
    318 #  if !defined (__BORLANDC__) || (__BORLANDC__ >= 0x564)
    319     CPPUNIT_ASSERT( values.insert(val_type("foo", 0)).second );
    320     CPPUNIT_ASSERT( values.insert(val_type("bar", 0)).second );
    321     CPPUNIT_ASSERT( values.insert(val_type("abc", 0)).second );
    322 #  else
    323     CPPUNIT_ASSERT( values.insert(hmap::value_type("foo", 0)).second );
    324     CPPUNIT_ASSERT( values.insert(hmap::value_type("bar", 0)).second );
    325     CPPUNIT_ASSERT( values.insert(hmap::value_type("abc", 0)).second );
    326 #  endif
    327 
    328     CPPUNIT_ASSERT( values.erase("foo") == 1 );
    329     CPPUNIT_ASSERT( values.erase("bar") == 1 );
    330     CPPUNIT_ASSERT( values.erase("abc") == 1 );
    331   }
    332 
    333   {
    334     hmap values;
    335 #  if !defined (__BORLANDC__) || (__BORLANDC__ >= 0x564)
    336     CPPUNIT_ASSERT( values.insert(val_type("foo", 0)).second );
    337     CPPUNIT_ASSERT( values.insert(val_type("bar", 0)).second );
    338     CPPUNIT_ASSERT( values.insert(val_type("abc", 0)).second );
    339 #  else
    340     CPPUNIT_ASSERT( values.insert(hmap::value_type("foo", 0)).second );
    341     CPPUNIT_ASSERT( values.insert(hmap::value_type("bar", 0)).second );
    342     CPPUNIT_ASSERT( values.insert(hmap::value_type("abc", 0)).second );
    343 #  endif
    344 
    345     CPPUNIT_ASSERT( values.erase("abc") == 1 );
    346     CPPUNIT_ASSERT( values.erase("bar") == 1 );
    347     CPPUNIT_ASSERT( values.erase("foo") == 1 );
    348   }
    349 #endif
    350 }
    351 
    352 /*
    353  * Here is the test showing why equality operator on hash containers
    354  * has no meaning:
    355 
    356 struct equality_hash_func {
    357   size_t operator () (size_t val) const {
    358     return val % 10;
    359   }
    360 };
    361 
    362 void HashTest::equality()
    363 {
    364   hash_set<size_t, equality_hash_func, equal_to<size_t> > s1, s2;
    365 
    366   s1.insert(10);
    367   s1.insert(20);
    368 
    369   s2.insert(20);
    370   s2.insert(10);
    371 
    372   //s1 and s2 contains both 10 and 20:
    373   CPPUNIT_ASSERT( s1 == s2 );
    374 }
    375 */
    376 
    377 void HashTest::allocator_with_state()
    378 {
    379 #if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS)
    380   char buf1[2048];
    381   StackAllocator<int> stack1(buf1, buf1 + sizeof(buf1));
    382 
    383   char buf2[2048];
    384   StackAllocator<int> stack2(buf2, buf2 + sizeof(buf2));
    385 
    386   {
    387     typedef hash_set<int, hash<int>, equal_to<int>, StackAllocator<int> > HashSetInt;
    388     HashSetInt hint1(10, hash<int>(), equal_to<int>(), stack1);
    389 
    390     int i;
    391     for (i = 0; i < 5; ++i)
    392       hint1.insert(i);
    393     HashSetInt hint1Cpy(hint1);
    394 
    395     HashSetInt hint2(10, hash<int>(), equal_to<int>(), stack2);
    396     for (; i < 10; ++i)
    397       hint2.insert(i);
    398     HashSetInt hint2Cpy(hint2);
    399 
    400     hint1.swap(hint2);
    401 
    402     CPPUNIT_ASSERT( hint1.get_allocator().swaped() );
    403     CPPUNIT_ASSERT( hint2.get_allocator().swaped() );
    404 
    405     CPPUNIT_ASSERT( hint1.get_allocator() == stack2 );
    406     CPPUNIT_ASSERT( hint2.get_allocator() == stack1 );
    407   }
    408   CPPUNIT_ASSERT( stack1.ok() );
    409   CPPUNIT_ASSERT( stack2.ok() );
    410 #endif
    411 }
    412 
    413 #if defined (STLPORT) && !defined (_STLP_NO_EXTENSIONS) && \
    414    (!defined (_STLP_USE_PTR_SPECIALIZATIONS) || defined (_STLP_CLASS_PARTIAL_SPECIALIZATION))
    415 #  if !defined (__DMC__)
    416 
    417 /* Simple compilation test: Check that nested types like iterator
    418  * can be access even if type used to instanciate container is not
    419  * yet completely defined.
    420  */
    421 class IncompleteClass
    422 {
    423   hash_set<IncompleteClass> hsinstances;
    424   typedef hash_set<IncompleteClass>::iterator hsit;
    425   hash_multiset<IncompleteClass> hsminstances;
    426   typedef hash_multiset<IncompleteClass>::iterator hsmit;
    427 
    428   hash_map<IncompleteClass, IncompleteClass> hminstances;
    429   typedef hash_map<IncompleteClass, IncompleteClass>::iterator hmit;
    430   hash_multimap<IncompleteClass, IncompleteClass> hmminstances;
    431   typedef hash_multimap<IncompleteClass, IncompleteClass>::iterator hmmit;
    432 };
    433 #  endif
    434 #endif
    435