//============================================================================= /** * @file Array_Map_Test.cpp * * Regression test for ACE_Array_Map. * * @author Ossama Othman */ //============================================================================= #include "test_config.h" #include "ace/SString.h" #include "ace/Array_Map.h" #include //#include /* For STL portability testing. */ static char const letters[] = { 'A', 'C', 'E', ' ', 'r', 'u', 'l', 'e', 'z', '!' }; static ACE_TString const words[] = { ACE_TEXT ("alpha"), // A ACE_TEXT ("charlie"), // C ACE_TEXT ("echo"), // E ACE_TEXT (" "), // ACE_TEXT ("romeo"), // r ACE_TEXT ("uniform"), // u ACE_TEXT ("lima"), // l ACE_TEXT ("echo"), // e ACE_TEXT ("zulu"), // z ACE_TEXT ("!") // ! }; static size_t const letters_len = sizeof (letters) / sizeof (letters[0]); static size_t const words_len = sizeof (words) / sizeof (words[0]); // -------------------------------------------------------------- bool insertion_removal_test () { // Instantiate the map. using Map = ACE_Array_Map; Map phonetic[2]; ACE_TEST_ASSERT (phonetic[0] == phonetic[1]); // Sanity check. static size_t const phonetic_len = sizeof (phonetic) / sizeof (phonetic[0]); unsigned int count = 1; // Test map insertion. for (Map * m = phonetic; m != phonetic + phonetic_len; ++m, ++count) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("------------- Populating map %u -------------\n"), count)); ACE_TString const * word = words; for (char const * i = letters; i != letters + letters_len; ++i, ++word) { std::pair const result = m->insert (std::make_pair (*i, *word)); if (result.second) ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Inserted \"%c | %s\"\n"), *i, word->c_str ())); else ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Insertion of \"%c | %s\" failed.\n"), *i, word->c_str ()), false); } ACE_TEST_ASSERT (m->size () == letters_len); } // Test equality of identically populated maps. if (!(phonetic[0] == phonetic[1])) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Equality comparison of two identical ") ACE_TEXT ("maps failed.\n")), false); } else if (phonetic[0] < phonetic[1]) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Relational comparison of two identical ") ACE_TEXT ("maps incorrectly passed.\n")), false); } ACE_TEST_ASSERT (!phonetic[0].is_empty ()); // Sanity check. Map foo (phonetic[0]); // Copy construction Map bar = foo; // Assignment if (!(foo == bar)) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Second equality comparison of two ") ACE_TEXT ("identical maps failed.\n")), false); } Map::value_type values[letters_len]; size_t const values_len = sizeof (values) / sizeof (values[0]); ACE_TString const * word = words; char const * letter = letters; for (Map::value_type * v = values; v != values + values_len; ++v, ++letter, ++word) { *v = std::make_pair (*letter, *word); } Map A (values, values + values_len); if (!(A == bar)) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Third equality comparison of two ") ACE_TEXT ("identical maps failed.\n")), false); } Map B; B.insert (values, values + values_len); if (!(A == B)) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Fourth equality comparison of two ") ACE_TEXT ("identical maps failed.\n")), false); } // ==== Removal tests ==== // Remove two elements from map. Map::iterator letter_A = A.find ('A'); ACE_TEST_ASSERT (letter_A == A.begin ()); // Should be first map element. ACE_TEST_ASSERT (A.count ('A') == 1); // Should only be one letter 'A'. A.erase (letter_A); if (A.find ('A') != A.end () || A.count ('A') != 0) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Letter '%c' not removed from map\n"), (*letter_A).first), false); } static char const z = 'z'; if (A.erase (z) != 1 || A.count (z) != 0 || A.find (z) != A.end ()) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Letter '%c' not removed from map\n"), z), false); } // Remove range of letters from map. static size_t const removed_len = 3; Map::iterator const first = B.begin () + 2; Map::iterator const last = first + removed_len; ACE_TEST_ASSERT (static_cast (last - first) < B.size ()); ACE_TEST_ASSERT (last < B.end ()); Map::value_type removed[removed_len]; size_t rcount = 0; for (Map::iterator x = first; x != last; ++x) { removed[rcount++] = *x; } B.erase (first, last); for (size_t s = 0; s < removed_len ; ++s) { Map::key_type const key = removed[s].first; if (B.count (key) != 0 || B.find (key) != B.end ()) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Letter '%c' from range of letters ") ACE_TEXT ("not removed from map\n"), key), false); } } ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Insertion/removal test passed.\n"))); return true; } // -------------------------------------------------------------- bool index_operator_test () { // Instantiate the map. using Map = ACE_Array_Map; Map phonetic; ACE_TEST_ASSERT (phonetic.size () == 0 && phonetic.is_empty ()); ACE_TEST_ASSERT (phonetic.max_size () > 1); // Run the same test twice, clearing the contents of the map between // the iterations. The goal is to verify that the constant time // clear() method performs as advertised. for (unsigned int count = 1; count < 3; ++count) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("----- Index operator test ") ACE_TEXT ("iteration %u -----\n"), count)); // Test map insertion through the index operator. ACE_TString const * word = words; for (char const * i = letters; i != letters + letters_len; ++i, ++word) { phonetic[*i] = *word; } ACE_TEST_ASSERT (phonetic.size () == letters_len); using const_iterator = Map::const_iterator; // Access the elements that were inserted into the map. char const * letter = letters; word = words; const_iterator const last = phonetic.end (); for (const_iterator n = phonetic.begin (); n != last; ++n, ++letter, ++word) { if ((*n).first != *letter || (*n).second != *word) ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Key/Datum mismatch:\n") ACE_TEXT (" key \"%c\" should be \"%c\"\n") ACE_TEXT (" datum \"%s\" should be \"%s\"\n"), (*n).first, *letter, (*n).second.c_str (), word->c_str ()), false); ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\t%c\t%s\n"), (*n).first, (*n).second.c_str ())); } // Now run the same test in reverse. ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("... in reverse ...\n"))); using const_reverse_iterator = Map::const_reverse_iterator; letter = letters + letters_len - 1; word = words + words_len - 1; // Work around compiler / STL implementations that cannot // handle implicit conversions from iterator to const_iterator // (e.g. due to missing template constructor.) // // We don't strictly need a const Map for this test but having // one allows us to exercise const iterators. Map const & const_phonetic = phonetic; const_reverse_iterator const rlast = const_phonetic.rend (); for (const_reverse_iterator r = const_phonetic.rbegin (); (r != rlast); ++r, --letter, --word) { if ((*r).first != *letter || (*r).second != *word) ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("Key/Datum mismatch:\n") ACE_TEXT (" key \"%c\" should be \"%c\"\n") ACE_TEXT (" datum \"%s\" should be \"%s\"\n"), (*r).first, *letter, (*r).second.c_str (), word->c_str ()), false); ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("\t%c\t%s\n"), (*r).first, (*r).second.c_str ())); } // The size should not have changed. ACE_TEST_ASSERT (phonetic.size () == letters_len); // Empty the map of its contents wholesale. phonetic.clear (); ACE_TEST_ASSERT (phonetic.size () == 0 && phonetic.is_empty ()); } ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Index operator test passed.\n"))); return true; } // -------------------------------------------------------------- class RefCounted { public: RefCounted () : refcount_ (0) { } RefCounted (unsigned int * count) : refcount_ (count) { } ~RefCounted () { if (this->refcount_) --(*this->refcount_); } RefCounted (RefCounted const & r) : refcount_ (r.refcount_ptr ()) { if (this->refcount_) ++(*this->refcount_); } RefCounted & operator= (RefCounted const & r) { RefCounted tmp (r); std::swap (this->refcount_, tmp.refcount_); return *this; } unsigned int * refcount_ptr () const { return this->refcount_; } unsigned int refcount () const { return *this->refcount_; } private: unsigned int * refcount_; }; // -------- bool reference_count_test () { using Map = ACE_Array_Map; static Map::size_type const CAPACITY = 30; unsigned int ref_count = 1; RefCounted counted (&ref_count); ACE_TEST_ASSERT (counted.refcount () == 1); { Map map (CAPACITY); // Preallocate storage for a number of // elements even if they are not used to test // some internals. map[ACE_TEXT("One")] = counted; ACE_TEST_ASSERT (counted.refcount () == 2); std::pair result; { result = map.insert (std::make_pair (ACE_TString (ACE_TEXT ("Two")), counted)); ACE_TEST_ASSERT (result.second); } ACE_TEST_ASSERT (counted.refcount () == 3); { result = map.insert (std::make_pair (ACE_TString (ACE_TEXT ("Three")), counted)); ACE_TEST_ASSERT (result.second); } ACE_TEST_ASSERT (counted.refcount () == 4); Map::size_type const erased = map.erase (ACE_TEXT ("One")); ACE_TEST_ASSERT (erased == 1); ACE_TEST_ASSERT (counted.refcount () == 3); } // Map instance no longer contains any references to the "counted" // object so the reference count should be back to one. ACE_TEST_ASSERT (counted.refcount () == 1); ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Reference count test passed.\n"))); return true; } // -------------------------------------------------------------- int run_main (int, ACE_TCHAR *[]) { ACE_START_TEST (ACE_TEXT ("Array_Map_Test")); ACE_TEST_ASSERT (::letters_len == ::words_len); bool const success = ::insertion_removal_test () && ::index_operator_test () && ::reference_count_test (); ACE_END_TEST; return (success ? 0 : -1); }