From 7c80b0c787ea98b4d1ddfd3c573d7db5e275218a Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 16 Mar 2026 22:31:00 -0400 Subject: [PATCH] Add regression tests for interval container lookup operations Add tests to pin down the expected results of lower_bound, upper_bound, equal_range, and find on the various interval containers. These tests aim to trigger failures on some standard libraries (in particular newer versions of libc++) since Boost.ICL uses associative containers with a comparator that is not a proper strict weak ordering. Note that many tests already fail without these reproducers when upgrading to a newer libc++, but these tests provide additional coverage. Related to #51 --- test/fix_tickets_/fix_tickets.cpp | 378 ++++++++++++++++++++++++++++++ 1 file changed, 378 insertions(+) diff --git a/test/fix_tickets_/fix_tickets.cpp b/test/fix_tickets_/fix_tickets.cpp index cb6524c..a25c391 100644 --- a/test/fix_tickets_/fix_tickets.cpp +++ b/test/fix_tickets_/fix_tickets.cpp @@ -249,3 +249,381 @@ BOOST_AUTO_TEST_CASE(github_25_intersects_for_empty_interval_throws) BOOST_CHECK_EQUAL(false, intersects(mt_set, Itv::right_open(-1,-1))); } +//------------------------------------------------------------------------------ +// GitHub issue #51: exclusive_less_than strict weak ordering violation +// +// The following tests verify the expected semantics of lower_bound, +// upper_bound, equal_range, and find on interval containers. +//------------------------------------------------------------------------------ + +// interval_set tests +BOOST_AUTO_TEST_CASE(github_51_interval_set_upper_bound) +{ + typedef boost::icl::interval_set ItvSet; + typedef ItvSet::interval_type Itv; + + ItvSet s; + s.add(Itv::right_open(0, 10)); + s.add(Itv::right_open(20, 30)); + s.add(Itv::right_open(40, 50)); + + // upper_bound with query overlapping multiple stored intervals + { + ItvSet::iterator it = s.upper_bound(Itv::right_open(5, 25)); + BOOST_CHECK(it != s.end()); + BOOST_CHECK_EQUAL(*it, Itv::right_open(40, 50)); + } + + // upper_bound past all intervals + { + ItvSet::iterator it = s.upper_bound(Itv::right_open(5, 55)); + BOOST_CHECK(it == s.end()); + } +} + +BOOST_AUTO_TEST_CASE(github_51_interval_set_lower_bound) +{ + typedef boost::icl::interval_set ItvSet; + typedef ItvSet::interval_type Itv; + + ItvSet s; + s.add(Itv::right_open(0, 10)); + s.add(Itv::right_open(20, 30)); + s.add(Itv::right_open(40, 50)); + + // lower_bound with query overlapping first and second intervals + { + ItvSet::iterator it = s.lower_bound(Itv::right_open(5, 25)); + BOOST_CHECK(it != s.end()); + BOOST_CHECK_EQUAL(*it, Itv::right_open(0, 10)); + } + + // lower_bound for interval before all stored + { + ItvSet::iterator it = s.lower_bound(Itv::right_open(-5, -1)); + BOOST_CHECK(it != s.end()); + BOOST_CHECK_EQUAL(*it, Itv::right_open(0, 10)); + } +} + +BOOST_AUTO_TEST_CASE(github_51_interval_set_equal_range) +{ + typedef boost::icl::interval_set ItvSet; + typedef ItvSet::interval_type Itv; + + ItvSet s; + s.add(Itv::right_open(0, 10)); + s.add(Itv::right_open(20, 30)); + s.add(Itv::right_open(40, 50)); + + // equal_range covering first two intervals + { + std::pair range = + s.equal_range(Itv::right_open(5, 25)); + int count = 0; + for (ItvSet::iterator it = range.first; it != range.second; ++it) + ++count; + BOOST_CHECK_EQUAL(count, 2); + } + + // equal_range covering all intervals + { + std::pair range = + s.equal_range(Itv::right_open(-5, 55)); + int count = 0; + for (ItvSet::iterator it = range.first; it != range.second; ++it) + ++count; + BOOST_CHECK_EQUAL(count, 3); + } +} + +BOOST_AUTO_TEST_CASE(github_51_interval_set_find) +{ + typedef boost::icl::interval_set ItvSet; + typedef ItvSet::interval_type Itv; + + ItvSet s; + s.add(Itv::right_open(0, 10)); + s.add(Itv::right_open(20, 30)); + s.add(Itv::right_open(40, 50)); + + // find for overlapping interval returns first overlapping + { + ItvSet::const_iterator it = s.find(Itv::right_open(5, 25)); + BOOST_CHECK(it != s.end()); + BOOST_CHECK_EQUAL(*it, Itv::right_open(0, 10)); + } + + // find for non-overlapping interval returns end + { + ItvSet::const_iterator it = s.find(Itv::right_open(10, 20)); + BOOST_CHECK(it == s.end()); + } +} + +// split_interval_set tests +BOOST_AUTO_TEST_CASE(github_51_split_interval_set_upper_bound) +{ + typedef boost::icl::split_interval_set ItvSet; + typedef ItvSet::interval_type Itv; + + ItvSet s; + s.add(Itv::right_open(0, 10)); + s.add(Itv::right_open(20, 30)); + s.add(Itv::right_open(40, 50)); + + { + ItvSet::iterator it = s.upper_bound(Itv::right_open(5, 25)); + BOOST_CHECK(it != s.end()); + BOOST_CHECK_EQUAL(*it, Itv::right_open(40, 50)); + } +} + +BOOST_AUTO_TEST_CASE(github_51_split_interval_set_lower_bound) +{ + typedef boost::icl::split_interval_set ItvSet; + typedef ItvSet::interval_type Itv; + + ItvSet s; + s.add(Itv::right_open(0, 10)); + s.add(Itv::right_open(20, 30)); + s.add(Itv::right_open(40, 50)); + + { + ItvSet::iterator it = s.lower_bound(Itv::right_open(5, 25)); + BOOST_CHECK(it != s.end()); + BOOST_CHECK_EQUAL(*it, Itv::right_open(0, 10)); + } +} + +BOOST_AUTO_TEST_CASE(github_51_split_interval_set_equal_range) +{ + typedef boost::icl::split_interval_set ItvSet; + typedef ItvSet::interval_type Itv; + + ItvSet s; + s.add(Itv::right_open(0, 10)); + s.add(Itv::right_open(20, 30)); + s.add(Itv::right_open(40, 50)); + + { + std::pair range = + s.equal_range(Itv::right_open(5, 25)); + int count = 0; + for (ItvSet::iterator it = range.first; it != range.second; ++it) + ++count; + BOOST_CHECK_EQUAL(count, 2); + } +} + +BOOST_AUTO_TEST_CASE(github_51_split_interval_set_find) +{ + typedef boost::icl::split_interval_set ItvSet; + typedef ItvSet::interval_type Itv; + + ItvSet s; + s.add(Itv::right_open(0, 10)); + s.add(Itv::right_open(20, 30)); + s.add(Itv::right_open(40, 50)); + + { + ItvSet::const_iterator it = s.find(Itv::right_open(5, 25)); + BOOST_CHECK(it != s.end()); + BOOST_CHECK_EQUAL(*it, Itv::right_open(0, 10)); + } + + { + ItvSet::const_iterator it = s.find(Itv::right_open(10, 20)); + BOOST_CHECK(it == s.end()); + } +} + +// separate_interval_set tests +BOOST_AUTO_TEST_CASE(github_51_separate_interval_set_upper_bound) +{ + typedef boost::icl::separate_interval_set ItvSet; + typedef ItvSet::interval_type Itv; + + ItvSet s; + s.add(Itv::right_open(0, 10)); + s.add(Itv::right_open(20, 30)); + s.add(Itv::right_open(40, 50)); + + { + ItvSet::iterator it = s.upper_bound(Itv::right_open(5, 25)); + BOOST_CHECK(it != s.end()); + BOOST_CHECK_EQUAL(*it, Itv::right_open(40, 50)); + } +} + +BOOST_AUTO_TEST_CASE(github_51_separate_interval_set_lower_bound) +{ + typedef boost::icl::separate_interval_set ItvSet; + typedef ItvSet::interval_type Itv; + + ItvSet s; + s.add(Itv::right_open(0, 10)); + s.add(Itv::right_open(20, 30)); + s.add(Itv::right_open(40, 50)); + + { + ItvSet::iterator it = s.lower_bound(Itv::right_open(5, 25)); + BOOST_CHECK(it != s.end()); + BOOST_CHECK_EQUAL(*it, Itv::right_open(0, 10)); + } +} + +// interval_map tests +BOOST_AUTO_TEST_CASE(github_51_interval_map_upper_bound) +{ + typedef boost::icl::interval_map ItvMap; + typedef ItvMap::interval_type Itv; + + ItvMap m; + m.add(make_pair(Itv::right_open(0, 10), 1)); + m.add(make_pair(Itv::right_open(20, 30), 2)); + m.add(make_pair(Itv::right_open(40, 50), 3)); + + { + ItvMap::iterator it = m.upper_bound(Itv::right_open(5, 25)); + BOOST_CHECK(it != m.end()); + BOOST_CHECK_EQUAL(it->first, Itv::right_open(40, 50)); + } +} + +BOOST_AUTO_TEST_CASE(github_51_interval_map_lower_bound) +{ + typedef boost::icl::interval_map ItvMap; + typedef ItvMap::interval_type Itv; + + ItvMap m; + m.add(make_pair(Itv::right_open(0, 10), 1)); + m.add(make_pair(Itv::right_open(20, 30), 2)); + m.add(make_pair(Itv::right_open(40, 50), 3)); + + { + ItvMap::iterator it = m.lower_bound(Itv::right_open(5, 25)); + BOOST_CHECK(it != m.end()); + BOOST_CHECK_EQUAL(it->first, Itv::right_open(0, 10)); + } +} + +BOOST_AUTO_TEST_CASE(github_51_interval_map_equal_range) +{ + typedef boost::icl::interval_map ItvMap; + typedef ItvMap::interval_type Itv; + + ItvMap m; + m.add(make_pair(Itv::right_open(0, 10), 1)); + m.add(make_pair(Itv::right_open(20, 30), 2)); + m.add(make_pair(Itv::right_open(40, 50), 3)); + + { + std::pair range = + m.equal_range(Itv::right_open(5, 25)); + int count = 0; + for (ItvMap::iterator it = range.first; it != range.second; ++it) + ++count; + BOOST_CHECK_EQUAL(count, 2); + } +} + +BOOST_AUTO_TEST_CASE(github_51_interval_map_find) +{ + typedef boost::icl::interval_map ItvMap; + typedef ItvMap::interval_type Itv; + + ItvMap m; + m.add(make_pair(Itv::right_open(0, 10), 1)); + m.add(make_pair(Itv::right_open(20, 30), 2)); + m.add(make_pair(Itv::right_open(40, 50), 3)); + + { + ItvMap::const_iterator it = m.find(Itv::right_open(5, 25)); + BOOST_CHECK(it != m.end()); + BOOST_CHECK_EQUAL(it->first, Itv::right_open(0, 10)); + } + + { + ItvMap::const_iterator it = m.find(Itv::right_open(10, 20)); + BOOST_CHECK(it == m.end()); + } +} + +// split_interval_map tests +BOOST_AUTO_TEST_CASE(github_51_split_interval_map_upper_bound) +{ + typedef boost::icl::split_interval_map ItvMap; + typedef ItvMap::interval_type Itv; + + ItvMap m; + m.add(make_pair(Itv::right_open(0, 10), 1)); + m.add(make_pair(Itv::right_open(20, 30), 2)); + m.add(make_pair(Itv::right_open(40, 50), 3)); + + { + ItvMap::iterator it = m.upper_bound(Itv::right_open(5, 25)); + BOOST_CHECK(it != m.end()); + BOOST_CHECK_EQUAL(it->first, Itv::right_open(40, 50)); + } +} + +BOOST_AUTO_TEST_CASE(github_51_split_interval_map_lower_bound) +{ + typedef boost::icl::split_interval_map ItvMap; + typedef ItvMap::interval_type Itv; + + ItvMap m; + m.add(make_pair(Itv::right_open(0, 10), 1)); + m.add(make_pair(Itv::right_open(20, 30), 2)); + m.add(make_pair(Itv::right_open(40, 50), 3)); + + { + ItvMap::iterator it = m.lower_bound(Itv::right_open(5, 25)); + BOOST_CHECK(it != m.end()); + BOOST_CHECK_EQUAL(it->first, Itv::right_open(0, 10)); + } +} + +BOOST_AUTO_TEST_CASE(github_51_split_interval_map_equal_range) +{ + typedef boost::icl::split_interval_map ItvMap; + typedef ItvMap::interval_type Itv; + + ItvMap m; + m.add(make_pair(Itv::right_open(0, 10), 1)); + m.add(make_pair(Itv::right_open(20, 30), 2)); + m.add(make_pair(Itv::right_open(40, 50), 3)); + + { + std::pair range = + m.equal_range(Itv::right_open(5, 25)); + int count = 0; + for (ItvMap::iterator it = range.first; it != range.second; ++it) + ++count; + BOOST_CHECK_EQUAL(count, 2); + } +} + +BOOST_AUTO_TEST_CASE(github_51_split_interval_map_find) +{ + typedef boost::icl::split_interval_map ItvMap; + typedef ItvMap::interval_type Itv; + + ItvMap m; + m.add(make_pair(Itv::right_open(0, 10), 1)); + m.add(make_pair(Itv::right_open(20, 30), 2)); + m.add(make_pair(Itv::right_open(40, 50), 3)); + + { + ItvMap::const_iterator it = m.find(Itv::right_open(5, 25)); + BOOST_CHECK(it != m.end()); + BOOST_CHECK_EQUAL(it->first, Itv::right_open(0, 10)); + } + + { + ItvMap::const_iterator it = m.find(Itv::right_open(10, 20)); + BOOST_CHECK(it == m.end()); + } +} +