diff --git a/include/etl/basic_string.h b/include/etl/basic_string.h index 7bc73534..59db587a 100644 --- a/include/etl/basic_string.h +++ b/include/etl/basic_string.h @@ -1466,9 +1466,7 @@ namespace etl //********************************************************************* size_type find(const_pointer s, size_type pos, size_type n) const { - size_t sz = etl::strlen(s); - - return find_impl(s, s + n, sz, pos); + return find_impl(s, s + n, n, pos); } //********************************************************************* diff --git a/include/etl/binary.h b/include/etl/binary.h index e7d9eec0..230fbf4a 100644 --- a/include/etl/binary.h +++ b/include/etl/binary.h @@ -1742,7 +1742,7 @@ namespace etl { count = 1U; - if ((value & 0xFFFFFFFFF0000000ULL) == 0U) + if ((value & 0xFFFFFFFF00000000ULL) == 0U) { value <<= 32U; count += 32U; @@ -1795,7 +1795,7 @@ namespace etl { typedef typename etl::make_unsigned::type unsigned_t; - return static_cast(count_trailing_ones(static_cast(value))); + return static_cast(count_leading_zeros(static_cast(value))); } #if ETL_USING_8BIT_TYPES @@ -1927,8 +1927,8 @@ namespace etl if ((value & 0xFFFF0000UL) == 0xFFFF0000UL) { - value <<= 8U; - count += 8U; + value <<= 16U; + count += 16U; } if ((value & 0xFF000000UL) == 0xFF000000UL) @@ -1988,14 +1988,14 @@ namespace etl if ((value & 0xFFFFFFFF00000000ULL) == 0xFFFFFFFF00000000ULL) { - value <<= 8U; - count += 8U; + value <<= 32U; + count += 32U; } if ((value & 0xFFFF000000000000ULL) == 0xFFFF000000000000ULL) { - value <<= 8U; - count += 8U; + value <<= 16U; + count += 16U; } if ((value & 0xFF00000000000000ULL) == 0xFF00000000000000ULL) diff --git a/include/etl/circular_buffer.h b/include/etl/circular_buffer.h index da6877e3..4c8290c4 100644 --- a/include/etl/circular_buffer.h +++ b/include/etl/circular_buffer.h @@ -1206,7 +1206,7 @@ namespace etl { this->clear(); - for (typename etl::icircular_buffer::const_iterator itr = other.begin(); itr != other.end(); ++itr) + for (typename etl::icircular_buffer::iterator itr = other.begin(); itr != other.end(); ++itr) { this->push(etl::move(*itr)); } diff --git a/include/etl/deque.h b/include/etl/deque.h index e88a0fd3..bd9afa46 100644 --- a/include/etl/deque.h +++ b/include/etl/deque.h @@ -605,10 +605,10 @@ namespace etl } //*************************************************** - reference operator[](size_t i) + const_reference operator[](size_t i) const { - iterator result(*this); - result += i; + const_iterator result(*this); + result += static_cast(i); return *result; } diff --git a/include/etl/expected.h b/include/etl/expected.h index 829c4247..655be8b1 100644 --- a/include/etl/expected.h +++ b/include/etl/expected.h @@ -206,7 +206,7 @@ namespace etl //******************************************* /// Get the error. //******************************************* - ETL_CONSTEXPR14 TError&& error() const&& ETL_NOEXCEPT + ETL_CONSTEXPR14 const TError&& error() const&& ETL_NOEXCEPT { return etl::move(error_value); } @@ -1309,7 +1309,7 @@ namespace etl template < typename F, typename U = typename etl::remove_cvref< typename etl::invoke_result::type>::type> auto transform_error(F&& f) const&& -> expected { - return transform_error_impl(etl::forward(f), *this); + return transform_error_impl(etl::forward(f), etl::move(*this)); } #endif diff --git a/include/etl/forward_list.h b/include/etl/forward_list.h index 4518be05..cfba7bf9 100644 --- a/include/etl/forward_list.h +++ b/include/etl/forward_list.h @@ -116,15 +116,15 @@ namespace etl }; //*************************************************************************** - /// Unsorted exception for the list. - ///\ingroup list + /// Unsorted exception for the forward_list. + ///\ingroup forward_list //*************************************************************************** class forward_list_no_pool : public forward_list_exception { public: forward_list_no_pool(string_type file_name_, numeric_type line_number_) - : forward_list_exception(ETL_ERROR_TEXT("list:no pool", ETL_FORWARD_LIST_FILE_ID"D"), file_name_, line_number_) + : forward_list_exception(ETL_ERROR_TEXT("forward_list:no pool", ETL_FORWARD_LIST_FILE_ID"D"), file_name_, line_number_) { } }; diff --git a/include/etl/histogram.h b/include/etl/histogram.h index 195076dd..95943b76 100644 --- a/include/etl/histogram.h +++ b/include/etl/histogram.h @@ -479,7 +479,7 @@ namespace etl //********************************* const_iterator end() const { - return accumulator.begin(); + return accumulator.end(); } //********************************* @@ -487,7 +487,7 @@ namespace etl //********************************* const_iterator cend() const { - return accumulator.cbegin(); + return accumulator.cend(); } //********************************* diff --git a/include/etl/intrusive_links.h b/include/etl/intrusive_links.h index 30966714..a1a9e653 100644 --- a/include/etl/intrusive_links.h +++ b/include/etl/intrusive_links.h @@ -890,7 +890,10 @@ namespace etl template typename etl::enable_if< etl::is_same >::value, void>::type link_clear_range(TLink* start) { - etl::link_clear_range(*start); + if (start != ETL_NULLPTR) + { + etl::link_clear_range(*start); + } } #if ETL_USING_CPP17 diff --git a/include/etl/string_view.h b/include/etl/string_view.h index 7f0162ec..1178e4c4 100644 --- a/include/etl/string_view.h +++ b/include/etl/string_view.h @@ -589,9 +589,9 @@ namespace etl return npos; } - position = etl::min(position, size()); + position = etl::min(position, size() - view.size()); - const_iterator iposition = etl::find_end(begin(), begin() + position, view.begin(), view.end()); + const_iterator iposition = etl::find_end(begin(), begin() + position + view.size(), view.begin(), view.end()); if (iposition == end()) { diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index 2b4372af..6f3b21bd 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -499,19 +499,19 @@ namespace etl }; #if ETL_HAS_NATIVE_CHAR8_T template <> - struct is_signed : true_type + struct is_signed : false_type { }; #endif #if ETL_HAS_NATIVE_CHAR16_T template <> - struct is_signed : true_type + struct is_signed : false_type { }; #endif #if ETL_HAS_NATIVE_CHAR32_T template <> - struct is_signed : true_type + struct is_signed : false_type { }; #endif @@ -569,6 +569,24 @@ namespace etl struct is_unsigned : true_type { }; + #if ETL_HAS_NATIVE_CHAR8_T + template <> + struct is_unsigned : true_type + { + }; + #endif + #if ETL_HAS_NATIVE_CHAR16_T + template <> + struct is_unsigned : true_type + { + }; + #endif + #if ETL_HAS_NATIVE_CHAR32_T + template <> + struct is_unsigned : true_type + { + }; + #endif template struct is_unsigned : is_unsigned { diff --git a/include/etl/utility.h b/include/etl/utility.h index 5a77b5b5..4c7e4719 100644 --- a/include/etl/utility.h +++ b/include/etl/utility.h @@ -377,7 +377,7 @@ namespace etl inline bool operator==(const pair& a, const pair& b) { #include "private/diagnostic_float_equal_push.h" - return (a.first == b.first) && !(a.second < b.second) && !(a.second > b.second); + return (a.first == b.first) && (a.second == b.second); #include "private/diagnostic_pop.h" } diff --git a/test/test_binary.cpp b/test/test_binary.cpp index d2c772e6..acbdd7fa 100644 --- a/test/test_binary.cpp +++ b/test/test_binary.cpp @@ -3040,6 +3040,108 @@ namespace CHECK_ARRAY_EQUAL(expected.data(), output.data(), expected.size()); } + + //************************************************************************* + TEST(test_count_leading_zeros_signed_32) + { + // int32_t(-1) = 0xFFFFFFFF → 0 leading zeros + CHECK_EQUAL(0, int(etl::count_leading_zeros(int32_t(-1)))); + + // int32_t(1) = 0x00000001 → 31 leading zeros + CHECK_EQUAL(31, int(etl::count_leading_zeros(int32_t(1)))); + + // int32_t(0) = 0x00000000 → 32 leading zeros + CHECK_EQUAL(32, int(etl::count_leading_zeros(int32_t(0)))); + + // int32_t(256) = 0x00000100 → 23 leading zeros + CHECK_EQUAL(23, int(etl::count_leading_zeros(int32_t(256)))); + + // Verify against unsigned version + for (int i = 0; i < 32; ++i) + { + int32_t value = int32_t(1) << i; + CHECK_EQUAL(int(etl::count_leading_zeros(uint32_t(value))), int(etl::count_leading_zeros(value))); + } + } + + //************************************************************************* + TEST(test_count_leading_zeros_signed_64) + { + CHECK_EQUAL(0, int(etl::count_leading_zeros(int64_t(-1)))); + CHECK_EQUAL(63, int(etl::count_leading_zeros(int64_t(1)))); + CHECK_EQUAL(64, int(etl::count_leading_zeros(int64_t(0)))); + + for (int i = 0; i < 64; ++i) + { + int64_t value = int64_t(1) << i; + CHECK_EQUAL(int(etl::count_leading_zeros(uint64_t(value))), int(etl::count_leading_zeros(value))); + } + } + + //************************************************************************* + TEST(test_count_leading_zeros_64_specific_values) + { + // Value that specifically triggers the upper-32-bit mask path + // Bit 35 set: 0x0000000800000000 → 28 leading zeros + CHECK_EQUAL(28, int(etl::count_leading_zeros(uint64_t(0x0000000800000000ULL)))); + // Bit 31 set: 0x0000000080000000 → 32 leading zeros + CHECK_EQUAL(32, int(etl::count_leading_zeros(uint64_t(0x0000000080000000ULL)))); + // All zeros in upper 32: 0x00000000FFFFFFFF → 32 leading zeros + CHECK_EQUAL(32, int(etl::count_leading_zeros(uint64_t(0x00000000FFFFFFFFULL)))); + // Upper half has bit: 0x0000000100000000 → 31 leading zeros + CHECK_EQUAL(31, int(etl::count_leading_zeros(uint64_t(0x0000000100000000ULL)))); + } + + //************************************************************************* + TEST(test_count_leading_ones_32_specific_values) + { + // 0xFFFF0000 → upper 16 bits set → exactly 16 leading ones + CHECK_EQUAL(16, int(etl::count_leading_ones(uint32_t(0xFFFF0000UL)))); + // 0xFFFF8000 → 17 leading ones + CHECK_EQUAL(17, int(etl::count_leading_ones(uint32_t(0xFFFF8000UL)))); + // 0xFFFFF000 → 20 leading ones + CHECK_EQUAL(20, int(etl::count_leading_ones(uint32_t(0xFFFFF000UL)))); + // 0xFFFFFF00 → 24 leading ones + CHECK_EQUAL(24, int(etl::count_leading_ones(uint32_t(0xFFFFFF00UL)))); + // 0xFFFFFFFE → 31 leading ones + CHECK_EQUAL(31, int(etl::count_leading_ones(uint32_t(0xFFFFFFFEUL)))); + // 0xFFFFFFFF → 32 leading ones + CHECK_EQUAL(32, int(etl::count_leading_ones(uint32_t(0xFFFFFFFFUL)))); + // 0x80000000 → 1 leading one + CHECK_EQUAL(1, int(etl::count_leading_ones(uint32_t(0x80000000UL)))); + + // Verify against reference for boundary values + for (uint32_t i = 0; i < 33; ++i) + { + // Create value with exactly 'i' leading ones + uint32_t value = (i == 0) ? 0U : (i == 32) ? 0xFFFFFFFFUL : ~((1UL << (32U - i)) - 1UL); + CHECK_EQUAL(int(test_leading_ones(value)), int(etl::count_leading_ones(value))); + } + } + + //************************************************************************* + TEST(test_count_leading_ones_64_specific_values) + { + // 0xFFFFFFFF00000000 → 32 leading ones + CHECK_EQUAL(32, int(etl::count_leading_ones(uint64_t(0xFFFFFFFF00000000ULL)))); + // 0xFFFFFFFFFFFF0000 → 48 leading ones + CHECK_EQUAL(48, int(etl::count_leading_ones(uint64_t(0xFFFFFFFFFFFF0000ULL)))); + // 0xFFFF000000000000 → 16 leading ones + CHECK_EQUAL(16, int(etl::count_leading_ones(uint64_t(0xFFFF000000000000ULL)))); + // 0xFFFFFFFFFFFFFF00 → 56 leading ones + CHECK_EQUAL(56, int(etl::count_leading_ones(uint64_t(0xFFFFFFFFFFFFFF00ULL)))); + // 0xFFFFFFFFFFFFFFFE → 63 leading ones + CHECK_EQUAL(63, int(etl::count_leading_ones(uint64_t(0xFFFFFFFFFFFFFFFEULL)))); + // 0xFFFFFFFFFFFFFFFF → 64 leading ones + CHECK_EQUAL(64, int(etl::count_leading_ones(uint64_t(0xFFFFFFFFFFFFFFFFULL)))); + + // Verify against reference for boundary values + for (uint64_t i = 0; i < 65; ++i) + { + uint64_t value = (i == 0) ? 0ULL : (i == 64) ? 0xFFFFFFFFFFFFFFFFULL : ~((1ULL << (64ULL - i)) - 1ULL); + CHECK_EQUAL(int(test_leading_ones(value)), int(etl::count_leading_ones(value))); + } + } } } // namespace diff --git a/test/test_circular_buffer.cpp b/test/test_circular_buffer.cpp index a9897e43..34452537 100644 --- a/test/test_circular_buffer.cpp +++ b/test/test_circular_buffer.cpp @@ -1072,5 +1072,30 @@ namespace CHECK(!is_equal); } + + //************************************************************************* + TEST(test_move_assignment_actually_moves) + { + DataM data; + data.push(ItemM("A")); + data.push(ItemM("B")); + data.push(ItemM("C")); + + DataM data2; + data2 = std::move(data); + + CHECK_EQUAL(3U, data2.size()); + + // If the move was correct, data2 should hold the moved-to values + CHECK_EQUAL("A", data2[0].value); + CHECK_EQUAL("B", data2[1].value); + CHECK_EQUAL("C", data2[2].value); + + // Original should be moved-from (empty strings with move-only type) + for (size_t i = 0; i < data.size(); ++i) + { + CHECK(data[i].value.empty()); + } + } } } // namespace diff --git a/test/test_deque.cpp b/test/test_deque.cpp index 22abd04a..d26096d1 100644 --- a/test/test_deque.cpp +++ b/test/test_deque.cpp @@ -2272,6 +2272,29 @@ namespace CHECK(std::equal(blank_data.begin(), blank_data.end(), data.begin())); } + + //************************************************************************* + TEST(test_const_iterator_subscript) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + const DataNDC& cdata = data; + + // Access via const_iterator operator[] + DataNDC::const_iterator cit = cdata.begin(); + + // Verify operator[] returns values matching sequential access + for (size_t i = 0; i < cdata.size(); ++i) + { + CHECK(cit[i] == cdata[i]); + } + + // Verify const_iterator operator[] returns const_reference + // (This is a compile-time check - if the fix is reverted, + // the type would be non-const reference which is incorrect for const_iterator) + const NDC& ref = cit[0]; + CHECK(ref == cdata[0]); + } } } // namespace diff --git a/test/test_expected.cpp b/test/test_expected.cpp index a28e2f54..621727b2 100644 --- a/test/test_expected.cpp +++ b/test/test_expected.cpp @@ -1616,5 +1616,26 @@ namespace Error result = exp.error_or(Error("default")); CHECK_EQUAL("real_error", result.e); } + + //************************************************************************* + TEST(test_unexpected_error_const_rvalue_ref) + { + const etl::unexpected ue(Error("test_error")); + + // Move from const rvalue — should get const Error&& + const Error&& ref = std::move(ue).error(); + CHECK_EQUAL("test_error", ref.e); + } + + //************************************************************************* + TEST(test_transform_error_void_const_rvalue) + { + const etl::expected exp(etl::unexpected(Error("original"))); + + auto result = std::move(exp).transform_error([](const Error& e) { return Error(e.e + "_transformed"); }); + + CHECK_FALSE(result.has_value()); + CHECK_EQUAL("original_transformed", result.error().e); + } } } // namespace diff --git a/test/test_forward_list.cpp b/test/test_forward_list.cpp index 02d928de..3f805b7f 100644 --- a/test/test_forward_list.cpp +++ b/test/test_forward_list.cpp @@ -1444,6 +1444,28 @@ namespace CHECK_EQUAL(ItemNDC("F"), *itr++); } #endif + + //************************************************************************* + TEST(test_forward_list_exception_types) + { + // Verify exception class hierarchy is correct + CHECK(true == (std::is_base_of::value)); + CHECK(true == (std::is_base_of::value)); + CHECK(true == (std::is_base_of::value)); + CHECK(true == (std::is_base_of::value)); + +#if defined(ETL_VERBOSE_ERRORS) + // When verbose errors are enabled, check the error text contains "forward_list" + etl::forward_list_full ex_full(__FILE__, __LINE__); + etl::forward_list_empty ex_empty(__FILE__, __LINE__); + + std::string full_msg(ex_full.what()); + std::string empty_msg(ex_empty.what()); + + CHECK(full_msg.find("forward_list") != std::string::npos); + CHECK(empty_msg.find("forward_list") != std::string::npos); +#endif + } } #include "etl/private/diagnostic_pop.h" } // namespace diff --git a/test/test_histogram.cpp b/test/test_histogram.cpp index a68970f9..624c0e68 100644 --- a/test/test_histogram.cpp +++ b/test/test_histogram.cpp @@ -332,5 +332,59 @@ namespace isEqual = std::equal(output2.begin(), output2.end(), histogram.begin()); CHECK(isEqual); } + + //************************************************************************* + TEST(test_sparse_histogram_iteration) + { + StringHistogram histogram; + + // Empty histogram: begin == end + CHECK(histogram.begin() == histogram.end()); + CHECK(histogram.cbegin() == histogram.cend()); + + // Add items + histogram.add(std::string("apple")); + histogram.add(std::string("banana")); + histogram.add(std::string("apple")); + + // Non-empty: begin != end + CHECK(histogram.begin() != histogram.end()); + CHECK(histogram.cbegin() != histogram.cend()); + + // Count elements by iterating + size_t count = 0; + for (auto it = histogram.begin(); it != histogram.end(); ++it) + { + ++count; + } + CHECK_EQUAL(2U, count); // "apple" and "banana" + + // Same with cbegin/cend + count = 0; + for (auto it = histogram.cbegin(); it != histogram.cend(); ++it) + { + ++count; + } + CHECK_EQUAL(2U, count); + + // Verify we can find the expected values + bool found_apple = false; + bool found_banana = false; + for (auto it = histogram.begin(); it != histogram.end(); ++it) + { + if (it->first == "apple") + { + CHECK_EQUAL(2, it->second); + found_apple = true; + } + if (it->first == "banana") + { + CHECK_EQUAL(1, it->second); + found_banana = true; + } + } + CHECK(found_apple); + CHECK(found_banana); + } } } // namespace diff --git a/test/test_intrusive_links.cpp b/test/test_intrusive_links.cpp index c90a56c3..7bdbeb1b 100644 --- a/test/test_intrusive_links.cpp +++ b/test/test_intrusive_links.cpp @@ -1845,5 +1845,15 @@ namespace CHECK(c.etl_left == nullptr); CHECK(c.etl_right == nullptr); } + + //************************************************************************* + TEST(test_link_clear_range_bidirectional_nullptr) + { + // Passing nullptr should be a no-op, not a crash + BData* null_ptr = nullptr; + etl::link_clear_range(null_ptr); + // If we get here without crashing, the test passes + CHECK(true); + } } } // namespace diff --git a/test/test_string_char.cpp b/test/test_string_char.cpp index 63b84400..b8271af4 100644 --- a/test/test_string_char.cpp +++ b/test/test_string_char.cpp @@ -5475,5 +5475,32 @@ namespace CHECK(text1 == sstream_view); } #endif + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_find_char_pointer_n_shorter_than_strlen) + { + const value_t haystack_str[] = STR("Hello World"); + + TextSTD compare(haystack_str); + TextL text(haystack_str); + + // Haystack is "Hello World" (size 11). + // search_str is "Worldly" (strlen 7). + // We search for first 5 chars ("World") starting at pos=6. + // Old code: (6 + 7 = 13) > 11 → premature npos (BUG) + // Fixed: (6 + 5 = 11) <= 11 → proceeds to search → finds at 6 + const value_t search_str[] = STR("Worldly"); + + size_t pos_std = compare.find(search_str, 6, 5); + size_t pos_etl = text.find(search_str, 6, 5); + CHECK_EQUAL(6U, pos_std); + CHECK_EQUAL(pos_std, pos_etl); + + // pos=5 also triggers: (5 + 7 = 12) > 11 with old code + pos_std = compare.find(search_str, 5, 5); + pos_etl = text.find(search_str, 5, 5); + CHECK_EQUAL(6U, pos_std); + CHECK_EQUAL(pos_std, pos_etl); + } } } // namespace diff --git a/test/test_string_view.cpp b/test/test_string_view.cpp index 658a6fea..a9314dd2 100644 --- a/test/test_string_view.cpp +++ b/test/test_string_view.cpp @@ -877,7 +877,7 @@ namespace CHECK(2U == view.rfind(s2, 5)); CHECK(View::npos == view.rfind(s4)); - CHECK(1U == view.rfind(s3, 5, 2)); + CHECK(5U == view.rfind(s3, 5, 2)); CHECK(View::npos == view.rfind(s4, 0, 11)); } @@ -1183,5 +1183,40 @@ namespace CHECK_TRUE(u32view == u32sstream_view); } #endif + + //************************************************************************* + TEST(test_rfind_boundary_positions) + { + etl::string_view sv("abcabc"); + + // rfind "abc" with position=3: should find the second "abc" at position 3 + size_t pos = sv.rfind(etl::string_view("abc"), 3); + CHECK_EQUAL(3U, pos); + + // rfind "abc" with position=2: should find only the first "abc" at position 0 + pos = sv.rfind(etl::string_view("abc"), 2); + CHECK_EQUAL(0U, pos); + + // rfind "abc" with position=0: should find at position 0 + pos = sv.rfind(etl::string_view("abc"), 0); + CHECK_EQUAL(0U, pos); + + // rfind with npos (search entire string) + pos = sv.rfind(etl::string_view("abc")); + CHECK_EQUAL(3U, pos); + + // rfind something not found + pos = sv.rfind(etl::string_view("xyz")); + CHECK_EQUAL(etl::string_view::npos, pos); + + // Compare with std::string to verify exact behavior + std::string std_sv("abcabc"); + for (size_t p = 0; p <= std_sv.size(); ++p) + { + size_t std_pos = std_sv.rfind("abc", p); + size_t etl_pos = sv.rfind(etl::string_view("abc"), p); + CHECK_EQUAL(std_pos, etl_pos); + } + } } } // namespace diff --git a/test/test_type_traits.cpp b/test/test_type_traits.cpp index bb4d73f1..6756d6d9 100644 --- a/test/test_type_traits.cpp +++ b/test/test_type_traits.cpp @@ -2247,6 +2247,25 @@ namespace CHECK_FALSE((etl::is_object_v)); CHECK_FALSE((etl::is_object_v)); CHECK_FALSE((etl::is_object_v)); +#endif + } + + //************************************************************************* + TEST(test_is_signed_unsigned_char_types) + { +#if ETL_HAS_NATIVE_CHAR8_T + CHECK_FALSE(etl::is_signed::value); + CHECK_TRUE(etl::is_unsigned::value); +#endif + +#if ETL_HAS_NATIVE_CHAR16_T + CHECK_FALSE(etl::is_signed::value); + CHECK_TRUE(etl::is_unsigned::value); +#endif + +#if ETL_HAS_NATIVE_CHAR32_T + CHECK_FALSE(etl::is_signed::value); + CHECK_TRUE(etl::is_unsigned::value); #endif } } diff --git a/test/test_utility.cpp b/test/test_utility.cpp index 06d0d325..08cf2e75 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -1047,5 +1047,51 @@ namespace CHECK_EQUAL(expect1c, result1g); #endif } + + //************************************************************************* + TEST(test_pair_equality_uses_equality_operator) + { + // Basic equality + etl::pair p1(1, 2); + etl::pair p2(1, 2); + etl::pair p3(1, 3); + etl::pair p4(2, 2); + + CHECK_TRUE(p1 == p2); + CHECK_FALSE(p1 == p3); // different second + CHECK_FALSE(p1 == p4); // different first + + // Custom type where operator== and operator< can disagree + // The old code used !(ab), which is NOT equivalent to a==b + // for types that don't define a total order consistent with equality. + struct WeirdType + { + int value; + bool equal_flag; + + bool operator==(const WeirdType& other) const + { + return equal_flag && other.equal_flag; + } + bool operator<(const WeirdType& other) const + { + return value < other.value; + } + bool operator>(const WeirdType& other) const + { + return value > other.value; + } + }; + + WeirdType w1{1, false}; + WeirdType w2{1, false}; // same value, but equal_flag is false + + // With proper ==: w1 == w2 should be false (both equal_flags are false) + // With old !(w1w2): would be true (same value) + etl::pair pw1(0, w1); + etl::pair pw2(0, w2); + + CHECK_FALSE(pw1 == pw2); // This would FAIL with the old < > based comparison + } } } // namespace