From 7c5a6e49a13db1ed93fe4ecdc5d024e628e8efe7 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Fri, 2 Sep 2022 20:40:17 +0100 Subject: [PATCH] Fixed unordered container equality tests to match STL --- include/etl/unordered_map.h | 39 +++++++++-- include/etl/unordered_multimap.h | 41 ++++++++++-- include/etl/unordered_multiset.h | 37 +++++++++-- include/etl/unordered_set.h | 109 +++++++++++++++++++------------ test/test_unordered_map.cpp | 99 +++++++++++++++++++++++----- test/test_unordered_multimap.cpp | 99 +++++++++++++++++++++++----- test/test_unordered_multiset.cpp | 54 +++++++++++++++ test/test_unordered_set.cpp | 21 +++--- 8 files changed, 396 insertions(+), 103 deletions(-) diff --git a/include/etl/unordered_map.h b/include/etl/unordered_map.h index cf52b692..8420d46d 100644 --- a/include/etl/unordered_map.h +++ b/include/etl/unordered_map.h @@ -142,11 +142,11 @@ namespace etl typedef const value_type* const_pointer; typedef size_t size_type; - typedef const TKey& key_parameter_t; typedef etl::forward_link<0> link_t; // Default link. + // The nodes that store the elements. // The nodes that store the elements. struct node_t : public link_t { @@ -158,6 +158,17 @@ namespace etl value_type key_value_pair; }; + friend bool operator ==(const node_t& lhs, const node_t& rhs) + { + return (lhs.key_value_pair.first == rhs.key_value_pair.first) && + (lhs.key_value_pair.second == rhs.key_value_pair.second); + } + + friend bool operator !=(const node_t& lhs, const node_t& rhs) + { + return !(lhs == rhs); + } + protected: typedef etl::intrusive_forward_list bucket_t; @@ -167,7 +178,7 @@ namespace etl // Local iterators iterate over one bucket. typedef typename bucket_t::iterator local_iterator; - typedef typename bucket_t::const_iterator local_const_iterator; + typedef typename bucket_t::const_iterator const_local_iterator; //********************************************************************* class iterator : public etl::iterator @@ -505,7 +516,7 @@ namespace etl /// Returns a const_iterator to the beginning of the unordered_map bucket. ///\return A const iterator to the beginning of the unordered_map bucket. //********************************************************************* - local_const_iterator begin(size_t i) const + const_local_iterator begin(size_t i) const { return pbuckets[i].cbegin(); } @@ -514,7 +525,7 @@ namespace etl /// Returns a const_iterator to the beginning of the unordered_map bucket. ///\return A const iterator to the beginning of the unordered_map bucket. //********************************************************************* - local_const_iterator cbegin(size_t i) const + const_local_iterator cbegin(size_t i) const { return pbuckets[i].cbegin(); } @@ -559,7 +570,7 @@ namespace etl /// Returns a const_iterator to the end of the unordered_map bucket. ///\return A const iterator to the end of the unordered_map bucket. //********************************************************************* - local_const_iterator end(size_t i) const + const_local_iterator end(size_t i) const { return pbuckets[i].cend(); } @@ -568,7 +579,7 @@ namespace etl /// Returns a const_iterator to the end of the unordered_map bucket. ///\return A const iterator to the end of the unordered_map bucket. //********************************************************************* - local_const_iterator cend(size_t i) const + const_local_iterator cend(size_t i) const { return pbuckets[i].cend(); } @@ -1501,7 +1512,21 @@ namespace etl template bool operator ==(const etl::iunordered_map& lhs, const etl::iunordered_map& rhs) { - return (lhs.size() == rhs.size()) && etl::equal(lhs.begin(), lhs.end(), rhs.begin()); + const bool sizes_match = (lhs.size() == rhs.size()); + bool elements_match = true; + + if (sizes_match) + { + for (size_t i = 0; (i < lhs.bucket_count()) && elements_match; ++i) + { + if (!etl::is_permutation(lhs.begin(i), lhs.end(i), rhs.begin(i))) + { + elements_match = false; + } + } + } + + return (sizes_match && elements_match); } //*************************************************************************** diff --git a/include/etl/unordered_multimap.h b/include/etl/unordered_multimap.h index 69aba453..96793f99 100644 --- a/include/etl/unordered_multimap.h +++ b/include/etl/unordered_multimap.h @@ -146,7 +146,9 @@ namespace etl typedef etl::forward_link<0> link_t; // Default link. - struct node_t : public link_t // The nodes that store the elements. + //********************************************************************* + // The nodes that store the elements. + struct node_t : public link_t { node_t(const_reference key_value_pair_) : key_value_pair(key_value_pair_) @@ -156,6 +158,17 @@ namespace etl value_type key_value_pair; }; + friend bool operator ==(const node_t& lhs, const node_t& rhs) + { + return (lhs.key_value_pair.first == rhs.key_value_pair.first) && + (lhs.key_value_pair.second == rhs.key_value_pair.second); + } + + friend bool operator !=(const node_t& lhs, const node_t& rhs) + { + return !(lhs == rhs); + } + protected: typedef etl::intrusive_forward_list bucket_t; @@ -165,7 +178,7 @@ namespace etl // Local iterators iterate over one bucket. typedef typename bucket_t::iterator local_iterator; - typedef typename bucket_t::const_iterator local_const_iterator; + typedef typename bucket_t::const_iterator const_local_iterator; //********************************************************************* class iterator : public etl::iterator @@ -504,7 +517,7 @@ namespace etl /// Returns a const_iterator to the beginning of the unordered_multimap bucket. ///\return A const iterator to the beginning of the unordered_multimap bucket. //********************************************************************* - local_const_iterator begin(size_t i) const + const_local_iterator begin(size_t i) const { return pbuckets[i].cbegin(); } @@ -513,7 +526,7 @@ namespace etl /// Returns a const_iterator to the beginning of the unordered_multimap bucket. ///\return A const iterator to the beginning of the unordered_multimap bucket. //********************************************************************* - local_const_iterator cbegin(size_t i) const + const_local_iterator cbegin(size_t i) const { return pbuckets[i].cbegin(); } @@ -558,7 +571,7 @@ namespace etl /// Returns a const_iterator to the end of the unordered_multimap bucket. ///\return A const iterator to the end of the unordered_multimap bucket. //********************************************************************* - local_const_iterator end(size_t i) const + const_local_iterator end(size_t i) const { return pbuckets[i].cend(); } @@ -567,7 +580,7 @@ namespace etl /// Returns a const_iterator to the end of the unordered_multimap bucket. ///\return A const iterator to the end of the unordered_multimap bucket. //********************************************************************* - local_const_iterator cend(size_t i) const + const_local_iterator cend(size_t i) const { return pbuckets[i].cend(); } @@ -1407,7 +1420,21 @@ namespace etl template bool operator ==(const etl::iunordered_multimap& lhs, const etl::iunordered_multimap& rhs) { - return (lhs.size() == rhs.size()) && etl::equal(lhs.begin(), lhs.end(), rhs.begin()); + const bool sizes_match = (lhs.size() == rhs.size()); + bool elements_match = true; + + if (sizes_match) + { + for (size_t i = 0; (i < lhs.bucket_count()) && elements_match; ++i) + { + if (!etl::is_permutation(lhs.begin(i), lhs.end(i), rhs.begin(i))) + { + elements_match = false; + } + } + } + + return (sizes_match && elements_match); } //*************************************************************************** diff --git a/include/etl/unordered_multiset.h b/include/etl/unordered_multiset.h index 260151c7..7a1835a0 100644 --- a/include/etl/unordered_multiset.h +++ b/include/etl/unordered_multiset.h @@ -143,6 +143,7 @@ namespace etl typedef etl::forward_link<0> link_t; + //********************************************************************* // The nodes that store the elements. struct node_t : public link_t { @@ -154,6 +155,16 @@ namespace etl value_type key; }; + friend bool operator ==(const node_t& lhs, const node_t& rhs) + { + return (lhs.key == rhs.key); + } + + friend bool operator !=(const node_t& lhs, const node_t& rhs) + { + return !(lhs == rhs); + } + protected: typedef etl::intrusive_forward_list bucket_t; @@ -163,7 +174,7 @@ namespace etl // Local iterators iterate over one bucket. typedef typename bucket_t::iterator local_iterator; - typedef typename bucket_t::const_iterator local_const_iterator; + typedef typename bucket_t::const_iterator const_local_iterator; //********************************************************************* class iterator : public etl::iterator @@ -500,7 +511,7 @@ namespace etl /// Returns a const_iterator to the beginning of the unordered_multiset bucket. ///\return A const iterator to the beginning of the unordered_multiset bucket. //********************************************************************* - local_const_iterator begin(size_t i) const + const_local_iterator begin(size_t i) const { return pbuckets[i].cbegin(); } @@ -509,7 +520,7 @@ namespace etl /// Returns a const_iterator to the beginning of the unordered_multiset bucket. ///\return A const iterator to the beginning of the unordered_multiset bucket. //********************************************************************* - local_const_iterator cbegin(size_t i) const + const_local_iterator cbegin(size_t i) const { return pbuckets[i].cbegin(); } @@ -554,7 +565,7 @@ namespace etl /// Returns a const_iterator to the end of the unordered_multiset bucket. ///\return A const iterator to the end of the unordered_multiset bucket. //********************************************************************* - local_const_iterator end(size_t i) const + const_local_iterator end(size_t i) const { return pbuckets[i].cend(); } @@ -563,7 +574,7 @@ namespace etl /// Returns a const_iterator to the end of the unordered_multiset bucket. ///\return A const iterator to the end of the unordered_multiset bucket. //********************************************************************* - local_const_iterator cend(size_t i) const + const_local_iterator cend(size_t i) const { return pbuckets[i].cend(); } @@ -1390,7 +1401,21 @@ namespace etl template bool operator ==(const etl::iunordered_multiset& lhs, const etl::iunordered_multiset& rhs) { - return (lhs.size() == rhs.size()) && etl::equal(lhs.begin(), lhs.end(), rhs.begin()); + const bool sizes_match = (lhs.size() == rhs.size()); + bool elements_match = true; + + if (sizes_match) + { + for (size_t i = 0; (i < lhs.bucket_count()) && elements_match; ++i) + { + if (!etl::is_permutation(lhs.begin(i), lhs.end(i), rhs.begin(i))) + { + elements_match = false; + } + } + } + + return (sizes_match && elements_match); } //*************************************************************************** diff --git a/include/etl/unordered_set.h b/include/etl/unordered_set.h index a3b4203a..8691acb1 100644 --- a/include/etl/unordered_set.h +++ b/include/etl/unordered_set.h @@ -144,6 +144,7 @@ namespace etl typedef etl::forward_link<0> link_t; + //********************************************************************* // The nodes that store the elements. struct node_t : public link_t { @@ -155,6 +156,16 @@ namespace etl value_type key; }; + friend bool operator ==(const node_t& lhs, const node_t& rhs) + { + return (lhs.key == rhs.key); + } + + friend bool operator !=(const node_t& lhs, const node_t& rhs) + { + return !(lhs == rhs); + } + protected: typedef etl::intrusive_forward_list bucket_t; @@ -164,7 +175,7 @@ namespace etl // Local iterators iterate over one bucket. typedef typename bucket_t::iterator local_iterator; - typedef typename bucket_t::const_iterator local_const_iterator; + typedef typename bucket_t::const_iterator const_local_iterator; //********************************************************************* class iterator : public etl::iterator @@ -501,7 +512,7 @@ namespace etl /// Returns a const_iterator to the beginning of the unordered_set bucket. ///\return A const iterator to the beginning of the unordered_set bucket. //********************************************************************* - local_const_iterator begin(size_t i) const + const_local_iterator begin(size_t i) const { return pbuckets[i].cbegin(); } @@ -510,7 +521,7 @@ namespace etl /// Returns a const_iterator to the beginning of the unordered_set bucket. ///\return A const iterator to the beginning of the unordered_set bucket. //********************************************************************* - local_const_iterator cbegin(size_t i) const + const_local_iterator cbegin(size_t i) const { return pbuckets[i].cbegin(); } @@ -555,7 +566,7 @@ namespace etl /// Returns a const_iterator to the end of the unordered_set bucket. ///\return A const iterator to the end of the unordered_set bucket. //********************************************************************* - local_const_iterator end(size_t i) const + const_local_iterator end(size_t i) const { return pbuckets[i].cend(); } @@ -564,7 +575,7 @@ namespace etl /// Returns a const_iterator to the end of the unordered_set bucket. ///\return A const iterator to the end of the unordered_set bucket. //********************************************************************* - local_const_iterator cend(size_t i) const + const_local_iterator cend(size_t i) const { return pbuckets[i].cend(); } @@ -1303,40 +1314,40 @@ namespace etl //********************************************************************* void adjust_first_last_markers_after_erase(bucket_t* pcurrent) { - if (empty()) +if (empty()) +{ + first = pbuckets; + last = pbuckets; +} +else +{ + if (pcurrent == first) + { + // We erased the first so, we need to search again from where we erased. + while (first->empty()) + { + ++first; + } + } + else if (pcurrent == last) + { + // We erased the last, so we need to search again. Start from the first, go no further than the current last. + bucket_t* pcurrent = first; + bucket_t* pend = last; + + last = first; + + while (pcurrent != pend) + { + if (!pcurrent->empty()) { - first = pbuckets; - last = pbuckets; + last = pcurrent; } - else - { - if (pcurrent == first) - { - // We erased the first so, we need to search again from where we erased. - while (first->empty()) - { - ++first; - } - } - else if (pcurrent == last) - { - // We erased the last, so we need to search again. Start from the first, go no further than the current last. - bucket_t* pcurrent = first; - bucket_t* pend = last; - last = first; - - while (pcurrent != pend) - { - if (!pcurrent->empty()) - { - last = pcurrent; - } - - ++pcurrent; - } - } - } + ++pcurrent; + } + } +} } // Disable copy construction. @@ -1364,9 +1375,9 @@ namespace etl /// For library debugging purposes only. ETL_DECLARE_DEBUG_COUNT - //************************************************************************* - /// Destructor. - //************************************************************************* + //************************************************************************* + /// Destructor. + //************************************************************************* #if defined(ETL_POLYMORPHIC_UNORDERED_SET) || defined(ETL_POLYMORPHIC_CONTAINERS) public: virtual ~iunordered_set() @@ -1384,20 +1395,34 @@ namespace etl /// Equal operator. ///\param lhs Reference to the first unordered_set. ///\param rhs Reference to the second unordered_set. - ///\return true if the arrays are equal, otherwise false + ///\return true if the sets are equal, otherwise false ///\ingroup unordered_set //*************************************************************************** template bool operator ==(const etl::iunordered_set& lhs, const etl::iunordered_set& rhs) { - return (lhs.size() == rhs.size()) && etl::equal(lhs.begin(), lhs.end(), rhs.begin()); + const bool sizes_match = (lhs.size() == rhs.size()); + bool elements_match = true; + + if (sizes_match) + { + for (size_t i = 0; (i < lhs.bucket_count()) && elements_match; ++i) + { + if (!etl::is_permutation(lhs.begin(i), lhs.end(i), rhs.begin(i))) + { + elements_match = false; + } + } + } + + return (sizes_match && elements_match); } //*************************************************************************** /// Not equal operator. ///\param lhs Reference to the first unordered_set. ///\param rhs Reference to the second unordered_set. - ///\return true if the arrays are not equal, otherwise false + ///\return true if the sets are not equal, otherwise false ///\ingroup unordered_set //*************************************************************************** template diff --git a/test/test_unordered_map.cpp b/test/test_unordered_map.cpp index 9efba58b..4cb084a0 100644 --- a/test/test_unordered_map.cpp +++ b/test/test_unordered_map.cpp @@ -960,6 +960,75 @@ namespace CHECK_CLOSE(2.0, data.load_factor(), 0.01); } + //************************************************************************* + TEST(test_equality_comparison_fails_when_hash_collisions_occur_582) + { + struct bad_hash + { + // Force hash collisions + size_t operator()(int key) const + { + return key % 4; + } + }; + + using etl_map = etl::unordered_map; + using stl_map = std::unordered_map; + + std::vector random_keys1 = + { + {17, "17"}, {14, "14"}, { 3, "3"}, { 7, "7"}, { 2, "2"}, + { 6, "6"}, { 9, "9"}, { 3, "3"}, {18, "18"}, {10, "10"}, + { 8, "8"}, {11, "11"}, { 4, "4"}, { 1, "1"}, {12, "12"}, + {15, "15"}, {16, "16"}, { 0, "0"}, { 5, "5"}, {19, "19"} + }; + + std::vector random_keys2 = + { + { 3, "3"}, { 6, "6"}, { 5, "5"}, {17, "17"}, { 2, "2"}, + { 7, "7"}, { 3, "3"}, {19, "19"}, { 8, "8"}, {15, "15"}, + {14, "14"}, { 0, "0"}, {18, "18"}, { 4, "4"}, {10, "10"}, + { 9, "9"}, {16, "16"}, {11, "11"}, {12, "12"}, { 1, "1"} + }; + + // Check that the input data is valid. + CHECK_EQUAL(random_keys1.size(), random_keys2.size()); + CHECK(std::is_permutation(random_keys1.begin(), random_keys1.end(), random_keys2.begin())); + + //*************************************************** + // Fill ETL + etl_map etlmap1; + etl_map etlmap2; + + for (auto i : random_keys1) + { + etlmap1.insert(i); + } + + for (auto i : random_keys2) + { + etlmap2.insert(i); + } + + //*************************************************** + // Fill STD + stl_map stdmap1; + stl_map stdmap2; + + for (auto i : random_keys1) + { + stdmap1.insert(i); + } + + for (auto i : random_keys2) + { + stdmap2.insert(i); + } + + //*************************************************** + CHECK_EQUAL((stdmap1 == stdmap2), (etlmap1 == etlmap2)); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_and_erase_bug) { @@ -1065,11 +1134,11 @@ namespace CustomHashFunction chf(1); CustomKeyEq ceq(2); - etl::unordered_map set1(chf, ceq); - etl::unordered_map set2(set1); + etl::unordered_map map1(chf, ceq); + etl::unordered_map map2(map1); - CHECK_EQUAL(chf.id, set2.hash_function().id); - CHECK_EQUAL(ceq.id, set2.key_eq().id); + CHECK_EQUAL(chf.id, map2.hash_function().id); + CHECK_EQUAL(ceq.id, map2.key_eq().id); } //************************************************************************* @@ -1081,13 +1150,13 @@ namespace CustomHashFunction chf3(3); CustomKeyEq ceq4(4); - etl::unordered_map set1(chf1, ceq2); - etl::unordered_map set2(chf3, ceq4); + etl::unordered_map map1(chf1, ceq2); + etl::unordered_map map2(chf3, ceq4); - set2.operator=(set1); + map2.operator=(map1); - CHECK_EQUAL(chf1.id, set2.hash_function().id); - CHECK_EQUAL(ceq2.id, set2.key_eq().id); + CHECK_EQUAL(chf1.id, map2.hash_function().id); + CHECK_EQUAL(ceq2.id, map2.key_eq().id); } //************************************************************************* @@ -1106,10 +1175,10 @@ namespace value_type{5, 55} }; - etl::unordered_map set1(data.begin(), data.end(), chf1, ceq2); + etl::unordered_map map1(data.begin(), data.end(), chf1, ceq2); - CHECK_EQUAL(chf1.id, set1.hash_function().id); - CHECK_EQUAL(ceq2.id, set1.key_eq().id); + CHECK_EQUAL(chf1.id, map1.hash_function().id); + CHECK_EQUAL(ceq2.id, map1.key_eq().id); } //************************************************************************* @@ -1120,10 +1189,10 @@ namespace using value_type = etl::unordered_map::value_type; - etl::unordered_map set1({ value_type{1, 11}, value_type{2, 22}, value_type{3, 33}, value_type{4, 44}, value_type{5, 55} }, chf1, ceq2); + etl::unordered_map map1({ value_type{1, 11}, value_type{2, 22}, value_type{3, 33}, value_type{4, 44}, value_type{5, 55} }, chf1, ceq2); - CHECK_EQUAL(chf1.id, set1.hash_function().id); - CHECK_EQUAL(ceq2.id, set1.key_eq().id); + CHECK_EQUAL(chf1.id, map1.hash_function().id); + CHECK_EQUAL(ceq2.id, map1.key_eq().id); } }; } diff --git a/test/test_unordered_multimap.cpp b/test/test_unordered_multimap.cpp index 863a14e2..ec431d60 100644 --- a/test/test_unordered_multimap.cpp +++ b/test/test_unordered_multimap.cpp @@ -839,6 +839,75 @@ namespace CHECK_CLOSE(2.0, data.load_factor(), 0.01); } + //************************************************************************* + TEST(test_equality_comparison_fails_when_hash_collisions_occur_582) + { + struct bad_hash + { + // Force hash collisions + size_t operator()(int key) const + { + return key % 4; + } + }; + + using etl_map = etl::unordered_multimap; + using stl_map = std::unordered_multimap; + + std::vector random_keys1 = + { + {17, "17"}, {14, "14"}, { 3, "3"}, { 7, "7"}, { 2, "2"}, + { 6, "6"}, { 9, "9"}, { 3, "3"}, {18, "18"}, {10, "10"}, + { 8, "8"}, {11, "11"}, { 4, "4"}, { 1, "1"}, {12, "12"}, + {15, "15"}, {16, "16"}, { 0, "0"}, { 5, "5"}, {19, "19"} + }; + + std::vector random_keys2 = + { + { 3, "3"}, { 6, "6"}, { 5, "5"}, {17, "17"}, { 2, "2"}, + { 7, "7"}, { 3, "3"}, {19, "19"}, { 8, "8"}, {15, "15"}, + {14, "14"}, { 0, "0"}, {18, "18"}, { 4, "4"}, {10, "10"}, + { 9, "9"}, {16, "16"}, {11, "11"}, {12, "12"}, { 1, "1"} + }; + + // Check that the input data is valid. + CHECK_EQUAL(random_keys1.size(), random_keys2.size()); + CHECK(std::is_permutation(random_keys1.begin(), random_keys1.end(), random_keys2.begin())); + + //*************************************************** + // Fill ETL + etl_map etlmap1; + etl_map etlmap2; + + for (auto i : random_keys1) + { + etlmap1.insert(i); + } + + for (auto i : random_keys2) + { + etlmap2.insert(i); + } + + //*************************************************** + // Fill STD + stl_map stdmap1; + stl_map stdmap2; + + for (auto i : random_keys1) + { + stdmap1.insert(i); + } + + for (auto i : random_keys2) + { + stdmap2.insert(i); + } + + //*************************************************** + CHECK_EQUAL((stdmap1 == stdmap2), (etlmap1 == etlmap2)); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_and_erase_bug) { @@ -875,11 +944,11 @@ namespace CustomHashFunction chf(1); CustomKeyEq ceq(2); - etl::unordered_multimap set1(chf, ceq); - etl::unordered_multimap set2(set1); + etl::unordered_multimap map1(chf, ceq); + etl::unordered_multimap map2(map1); - CHECK_EQUAL(chf.id, set2.hash_function().id); - CHECK_EQUAL(ceq.id, set2.key_eq().id); + CHECK_EQUAL(chf.id, map2.hash_function().id); + CHECK_EQUAL(ceq.id, map2.key_eq().id); } //************************************************************************* @@ -891,13 +960,13 @@ namespace CustomHashFunction chf3(3); CustomKeyEq ceq4(4); - etl::unordered_multimap set1(chf1, ceq2); - etl::unordered_multimap set2(chf3, ceq4); + etl::unordered_multimap map1(chf1, ceq2); + etl::unordered_multimap map2(chf3, ceq4); - set2.operator=(set1); + map2.operator=(map1); - CHECK_EQUAL(chf1.id, set2.hash_function().id); - CHECK_EQUAL(ceq2.id, set2.key_eq().id); + CHECK_EQUAL(chf1.id, map2.hash_function().id); + CHECK_EQUAL(ceq2.id, map2.key_eq().id); } //************************************************************************* @@ -916,10 +985,10 @@ namespace value_type{5, 55} }; - etl::unordered_multimap set1(data.begin(), data.end(), chf1, ceq2); + etl::unordered_multimap map1(data.begin(), data.end(), chf1, ceq2); - CHECK_EQUAL(chf1.id, set1.hash_function().id); - CHECK_EQUAL(ceq2.id, set1.key_eq().id); + CHECK_EQUAL(chf1.id, map1.hash_function().id); + CHECK_EQUAL(ceq2.id, map1.key_eq().id); } //************************************************************************* @@ -930,10 +999,10 @@ namespace using value_type = etl::unordered_multimap::value_type; - etl::unordered_multimap set1({ value_type{1, 11}, value_type{2, 22}, value_type{3, 33}, value_type{4, 44}, value_type{5, 55} }, chf1, ceq2); + etl::unordered_multimap map1({ value_type{1, 11}, value_type{2, 22}, value_type{3, 33}, value_type{4, 44}, value_type{5, 55} }, chf1, ceq2); - CHECK_EQUAL(chf1.id, set1.hash_function().id); - CHECK_EQUAL(ceq2.id, set1.key_eq().id); + CHECK_EQUAL(chf1.id, map1.hash_function().id); + CHECK_EQUAL(ceq2.id, map1.key_eq().id); } }; } diff --git a/test/test_unordered_multiset.cpp b/test/test_unordered_multiset.cpp index 4e532cf6..a0ef0ceb 100644 --- a/test/test_unordered_multiset.cpp +++ b/test/test_unordered_multiset.cpp @@ -36,6 +36,7 @@ SOFTWARE. #include #include #include +#include #include "data.h" @@ -770,6 +771,59 @@ namespace CHECK_CLOSE(2.0, data.load_factor(), 0.01); } + //************************************************************************* + TEST(test_equality_comparison_fails_when_hash_collisions_occur_582) + { + struct bad_hash + { + // Force hash collisions + size_t operator()(int key) const + { + return key % 4; + } + }; + + std::vector random_keys1 = { 17, 14, 3, 7, 2, 6, 9, 3, 18, 10, 8, 11, 4, 1, 12, 15, 16, 0, 5, 19 }; + std::vector random_keys2 = { 3, 6, 5, 17, 2, 7, 3, 19, 8, 15, 14, 0, 18, 4, 10, 9, 16, 11, 12, 1 }; + + // Check that the input data is valid. + CHECK_EQUAL(random_keys1.size(), random_keys2.size()); + CHECK(std::is_permutation(random_keys1.begin(), random_keys1.end(), random_keys2.begin())); + + //*************************************************** + // Fill ETL + etl::unordered_multiset etlset1; + etl::unordered_multiset etlset2; + + for (auto i : random_keys1) + { + etlset1.insert(i); + } + + for (auto i : random_keys2) + { + etlset2.insert(i); + } + + //*************************************************** + // Fill STD + std::unordered_multiset stdset1; + std::unordered_multiset stdset2; + + for (auto i : random_keys1) + { + stdset1.insert(i); + } + + for (auto i : random_keys2) + { + stdset2.insert(i); + } + + //*************************************************** + CHECK_EQUAL((stdset1 == stdset2), (etlset1 == etlset2)); + } + //************************************************************************* TEST(test_copying_of_hash_and_key_compare_with_copy_construct) { diff --git a/test/test_unordered_set.cpp b/test/test_unordered_set.cpp index ab458e33..cd13e7ca 100644 --- a/test/test_unordered_set.cpp +++ b/test/test_unordered_set.cpp @@ -713,31 +713,31 @@ namespace { struct bad_hash { - // Often has hash collisions + // Force hash collisions size_t operator()(int key) const { return key % 4; } }; - std::vector keys1 = { 17, 14, 3, 7, 2, 6, 9, 3, 18, 10, 8, 11, 4, 1, 12, 15, 16, 0, 5, 19 }; - std::vector keys2 = { 3, 6, 5, 17, 2, 7, 3, 19, 8, 15, 14, 0, 18, 4, 10, 9, 16, 11, 12, 1 }; + std::vector random_keys1 = { 17, 14, 3, 7, 2, 6, 9, 3, 18, 10, 8, 11, 4, 1, 12, 15, 16, 0, 5, 19 }; + std::vector random_keys2 = { 3, 6, 5, 17, 2, 7, 3, 19, 8, 15, 14, 0, 18, 4, 10, 9, 16, 11, 12, 1 }; // Check that the input data is valid. - CHECK_EQUAL(keys1.size(), keys2.size()); - CHECK(std::is_permutation(keys1.begin(), keys1.end(), keys2.begin())); + CHECK_EQUAL(random_keys1.size(), random_keys2.size()); + CHECK(std::is_permutation(random_keys1.begin(), random_keys1.end(), random_keys2.begin())); //*************************************************** // Fill ETL etl::unordered_set etlset1; etl::unordered_set etlset2; - for (auto i : keys1) + for (auto i : random_keys1) { etlset1.insert(i); } - for (auto i : keys2) + for (auto i : random_keys2) { etlset2.insert(i); } @@ -747,19 +747,18 @@ namespace std::unordered_set stdset1; std::unordered_set stdset2; - for (auto i : keys1) + for (auto i : random_keys1) { stdset1.insert(i); } - for (auto i : keys2) + for (auto i : random_keys2) { stdset2.insert(i); } //*************************************************** - CHECK(etlset1 == etlset2); - CHECK(stdset1 == stdset2); + CHECK_EQUAL((stdset1 == stdset2), (etlset1 == etlset2)); } //*************************************************************************