From d975648a978863df9718712804833078d7aa2710 Mon Sep 17 00:00:00 2001 From: Pieter Svenson Date: Wed, 13 May 2026 18:25:25 +0000 Subject: [PATCH 1/5] test overlap overflow --- include/interval-tree/interval_types.hpp | 6 ++-- tests/interval_tests.hpp | 35 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/include/interval-tree/interval_types.hpp b/include/interval-tree/interval_types.hpp index 10bbefa..7e1238b 100644 --- a/include/interval-tree/interval_types.hpp +++ b/include/interval-tree/interval_types.hpp @@ -414,13 +414,15 @@ namespace lib_interval_tree closed::overlaps(closedEquiv1.low(), closedEquiv1.high(), closedEquiv2.low(), closedEquiv2.high()); if (!closedOverlap) { - if (closedEquiv1.high() + 1 == closedEquiv2.low() && + if (/*closedEquiv1.high() != std::numeric_limits::max() && */ + closedEquiv1.high() + 1 == closedEquiv2.low() && (ival1.right_border() == interval_border::closed_adjacent || ival2.left_border() == interval_border::closed_adjacent)) { return true; } - if (closedEquiv2.high() + 1 == closedEquiv1.low() && + if (/*closedEquiv2.high() != std::numeric_limits::max() && */ + closedEquiv2.high() + 1 == closedEquiv1.low() && (ival2.right_border() == interval_border::closed_adjacent || ival1.left_border() == interval_border::closed_adjacent)) { diff --git a/tests/interval_tests.hpp b/tests/interval_tests.hpp index f0c94aa..f0e8821 100644 --- a/tests/interval_tests.hpp +++ b/tests/interval_tests.hpp @@ -291,6 +291,41 @@ TEST_F(OverlapTests, LeftOverlapTests) EXPECT_FALSE(i(0, 5).overlaps({-6, -2})); } +TEST_F(OverlapTests, LimitOverlapTests) +{ + using lib_interval_tree::open; + const MIN = std::numeric_limits::min(); + const MAX = std::numeric_limits::max(); + + // one min + EXPECT_TRUE(i(MIN, 5).overlaps({3, 16})); + EXPECT_TRUE(i(MIN, 5).overlaps({3, 16})); + EXPECT_TRUE(i(MIN, 5).overlaps({3, 16})); + EXPECT_TRUE(i(MIN, 5).overlaps({3, 16})); + EXPECT_TRUE(i(MIN, 5).overlaps({3, 16})); + + // one max + EXPECT_TRUE(i(0, 5).overlaps({3, MAX})); + EXPECT_TRUE(i(0, 5).overlaps({3, MAX})); + EXPECT_TRUE(i(0, 5).overlaps({3, MAX})); + EXPECT_TRUE(i(0, 5).overlaps({3, MAX})); + EXPECT_TRUE(i(0, 5).overlaps({3, MAX})); + + // both min + EXPECT_TRUE(i(MIN, 5).overlaps({MIN, 16})); + EXPECT_TRUE(i(MIN, 5).overlaps({MIN, 16})); + EXPECT_TRUE(i(MIN, 5).overlaps({MIN, 16})); + EXPECT_TRUE(i(MIN, 5).overlaps({MIN, 16})); + EXPECT_TRUE(i(MIN, 5).overlaps({MIN, 16})); + + // both max + EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); + EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); + EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); + EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); + EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); +} + TEST_F(OverlapTests, ShallEncompassCompletely) { using lib_interval_tree::open; From 4a7476f7c33b3aba0bdc45e07f975147f3a07f7e Mon Sep 17 00:00:00 2001 From: Pieter Svenson Date: Wed, 13 May 2026 18:29:36 +0000 Subject: [PATCH 2/5] more test --- tests/interval_tests.hpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/interval_tests.hpp b/tests/interval_tests.hpp index f0e8821..b3b5829 100644 --- a/tests/interval_tests.hpp +++ b/tests/interval_tests.hpp @@ -324,6 +324,27 @@ TEST_F(OverlapTests, LimitOverlapTests) EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); + + // min-max overlap + EXPECT_TRUE(i(0, MAX).overlaps({MIN, 3})); + EXPECT_TRUE(i(0, MAX).overlaps({MIN, 3})); + EXPECT_TRUE(i(0, MAX).overlaps({MIN, 3})); + EXPECT_TRUE(i(0, MAX).overlaps({MIN, 3})); + EXPECT_TRUE(i(0, MAX).overlaps({MIN, 3})); + + // min-max closed overlap + EXPECT_TRUE(i(0, MAX).overlaps({MIN, 0})); + EXPECT_FALSE(i(0, MAX).overlaps({MIN, 0})); + EXPECT_FALSE(i(0, MAX).overlaps({MIN, 0})); + EXPECT_FALSE(i(0, MAX).overlaps({MIN, 0})); + EXPECT_TRUE(i(0, MAX).overlaps({MIN, 0})); + + // min-max touching + EXPECT_FALSE(i(1, MAX).overlaps({MIN, 0})); + EXPECT_FALSE(i(1, MAX).overlaps({MIN, 0})); + EXPECT_FALSE(i(1, MAX).overlaps({MIN, 0})); + EXPECT_FALSE(i(1, MAX).overlaps({MIN, 0})); + EXPECT_TRUE(i(1, MAX).overlaps({MIN, 0})); } TEST_F(OverlapTests, ShallEncompassCompletely) From 634e1f28eafc3577fffaaf022ef34edfb5f5cd77 Mon Sep 17 00:00:00 2001 From: Pieter Svenson Date: Wed, 24 Jun 2026 18:26:06 +0000 Subject: [PATCH 3/5] fetch google testing during build, better tests, ensure no overflow --- .github/workflows/build_and_test.yml | 2 +- README.md | 10 +- include/interval-tree/interval_types.hpp | 37 +++---- tests/CMakeLists.txt | 14 ++- tests/interval_tests.hpp | 120 ++++++++++++----------- 5 files changed, 105 insertions(+), 78 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 469f19b..845623b 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -42,7 +42,7 @@ jobs: run: sudo apt-get update - name: Do install - run: sudo apt-get install -y ${{ matrix.compiler.pkg }} cmake ninja-build libgtest-dev libgmock-dev + run: sudo apt-get install -y ${{ matrix.compiler.pkg }} cmake ninja-build - name: Checkout uses: actions/checkout@v2 diff --git a/README.md b/README.md index 56ed9a1..633ed2d 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,15 @@ int main() ``` ## Compile & Run Testing -Having googletest (find here on github) installed / built is a requirement to run the tests. Create a build folder, navigate there, run cmake and build the tree-tests target. -You might have to adapt the linker line for gtest, if you built it yourself and didn't install it into your system. + +```bash +mkdir build +cd build +cmake --build . +./tests/tree-tests +``` + If you want to generate the pretty drawings, install cairo, pull the submodule and pass INT_TREE_DRAW_EXAMPLES=on to the cmake command line to generate a drawings/make_drawings executeable. Some features of this library require the presence of an optional type. diff --git a/include/interval-tree/interval_types.hpp b/include/interval-tree/interval_types.hpp index 7e1238b..f40045c 100644 --- a/include/interval-tree/interval_types.hpp +++ b/include/interval-tree/interval_types.hpp @@ -220,7 +220,8 @@ namespace lib_interval_tree #endif overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2) { - return (l1 <= (h2 + 1)) && (l2 <= (h1 + 1)); + return (h2 == std::numeric_limits::max() || l1 <= (h2 + 1)) && + (h1 == std::numeric_limits::max() || l2 <= (h1 + 1)); } template @@ -412,25 +413,25 @@ namespace lib_interval_tree auto closedOverlap = closed::overlaps(closedEquiv1.low(), closedEquiv1.high(), closedEquiv2.low(), closedEquiv2.high()); - if (!closedOverlap) + if (closedOverlap) { - if (/*closedEquiv1.high() != std::numeric_limits::max() && */ - closedEquiv1.high() + 1 == closedEquiv2.low() && - (ival1.right_border() == interval_border::closed_adjacent || - ival2.left_border() == interval_border::closed_adjacent)) - { - return true; - } - if (/*closedEquiv2.high() != std::numeric_limits::max() && */ - closedEquiv2.high() + 1 == closedEquiv1.low() && - (ival2.right_border() == interval_border::closed_adjacent || - ival1.left_border() == interval_border::closed_adjacent)) - { - return true; - } - return false; + return true; } - return true; + if (closedEquiv2.low() > closedEquiv1.high() && + closedEquiv2.low() - closedEquiv1.high() == 1 && + (ival1.right_border() == interval_border::closed_adjacent || + ival2.left_border() == interval_border::closed_adjacent)) + { + return true; + } + if (closedEquiv1.low() > closedEquiv2.high() && + closedEquiv1.low() - closedEquiv2.high() == 1 && + (ival2.right_border() == interval_border::closed_adjacent || + ival1.left_border() == interval_border::closed_adjacent)) + { + return true; + } + return false; } template diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6b1b227..0e12d84 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,7 +19,14 @@ file(GLOB sources "*.cpp") # Add Executable add_executable(tree-tests ${sources}) -find_package(GTest REQUIRED) +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.15.2 +) +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) target_link_libraries(tree-tests PRIVATE interval-tree GTest::gtest GTest::gmock GTest::gmock_main) @@ -47,4 +54,9 @@ if (DEFINED ENV{MSYSTEM}) COMMAND bash -c "ldd $" | "grep" "clang" | awk "NF == 4 { system(\"${CMAKE_COMMAND} -E copy \" \$3 \" $\") }" VERBATIM ) +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU") + target_compile_options(tree-tests PRIVATE -fsanitize=signed-integer-overflow) + target_link_options(tree-tests PRIVATE -fsanitize=signed-integer-overflow) endif() \ No newline at end of file diff --git a/tests/interval_tests.hpp b/tests/interval_tests.hpp index b3b5829..d267fe1 100644 --- a/tests/interval_tests.hpp +++ b/tests/interval_tests.hpp @@ -291,62 +291,6 @@ TEST_F(OverlapTests, LeftOverlapTests) EXPECT_FALSE(i(0, 5).overlaps({-6, -2})); } -TEST_F(OverlapTests, LimitOverlapTests) -{ - using lib_interval_tree::open; - const MIN = std::numeric_limits::min(); - const MAX = std::numeric_limits::max(); - - // one min - EXPECT_TRUE(i(MIN, 5).overlaps({3, 16})); - EXPECT_TRUE(i(MIN, 5).overlaps({3, 16})); - EXPECT_TRUE(i(MIN, 5).overlaps({3, 16})); - EXPECT_TRUE(i(MIN, 5).overlaps({3, 16})); - EXPECT_TRUE(i(MIN, 5).overlaps({3, 16})); - - // one max - EXPECT_TRUE(i(0, 5).overlaps({3, MAX})); - EXPECT_TRUE(i(0, 5).overlaps({3, MAX})); - EXPECT_TRUE(i(0, 5).overlaps({3, MAX})); - EXPECT_TRUE(i(0, 5).overlaps({3, MAX})); - EXPECT_TRUE(i(0, 5).overlaps({3, MAX})); - - // both min - EXPECT_TRUE(i(MIN, 5).overlaps({MIN, 16})); - EXPECT_TRUE(i(MIN, 5).overlaps({MIN, 16})); - EXPECT_TRUE(i(MIN, 5).overlaps({MIN, 16})); - EXPECT_TRUE(i(MIN, 5).overlaps({MIN, 16})); - EXPECT_TRUE(i(MIN, 5).overlaps({MIN, 16})); - - // both max - EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); - EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); - EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); - EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); - EXPECT_TRUE(i(0, MAX).overlaps({3, MAX})); - - // min-max overlap - EXPECT_TRUE(i(0, MAX).overlaps({MIN, 3})); - EXPECT_TRUE(i(0, MAX).overlaps({MIN, 3})); - EXPECT_TRUE(i(0, MAX).overlaps({MIN, 3})); - EXPECT_TRUE(i(0, MAX).overlaps({MIN, 3})); - EXPECT_TRUE(i(0, MAX).overlaps({MIN, 3})); - - // min-max closed overlap - EXPECT_TRUE(i(0, MAX).overlaps({MIN, 0})); - EXPECT_FALSE(i(0, MAX).overlaps({MIN, 0})); - EXPECT_FALSE(i(0, MAX).overlaps({MIN, 0})); - EXPECT_FALSE(i(0, MAX).overlaps({MIN, 0})); - EXPECT_TRUE(i(0, MAX).overlaps({MIN, 0})); - - // min-max touching - EXPECT_FALSE(i(1, MAX).overlaps({MIN, 0})); - EXPECT_FALSE(i(1, MAX).overlaps({MIN, 0})); - EXPECT_FALSE(i(1, MAX).overlaps({MIN, 0})); - EXPECT_FALSE(i(1, MAX).overlaps({MIN, 0})); - EXPECT_TRUE(i(1, MAX).overlaps({MIN, 0})); -} - TEST_F(OverlapTests, ShallEncompassCompletely) { using lib_interval_tree::open; @@ -696,6 +640,70 @@ TEST_F(OverlapTests, DynamicBorderTests) // one side open or closed follows from other tests } +TEST_F(OverlapTests, DynamicBorderTestsLimits) +{ + const auto MIN = std::numeric_limits::min(); + const auto MAX = std::numeric_limits::max(); + + constexpr auto c = interval_border::closed; + constexpr auto o = interval_border::open; + constexpr auto ca = interval_border::closed_adjacent; + + // one min + EXPECT_TRUE(i(MIN, 5, c, c).overlaps({3, 16, c, c})); + EXPECT_TRUE(i(MIN, 5, o, c).overlaps({3, 16, c, c})); + EXPECT_TRUE(i(MIN, 5, ca, c).overlaps({3, 16, c, c})); + + // one max + EXPECT_TRUE(i(0, 5, c, c).overlaps({3, MAX, c, c})); + EXPECT_TRUE(i(0, 5, c, c).overlaps({3, MAX, c, o})); + EXPECT_TRUE(i(0, 5, c, c).overlaps({3, MAX, c, ca})); + + // both min + EXPECT_TRUE(i(MIN, 5, c, c).overlaps({MIN, 16, c, c})); + EXPECT_TRUE(i(MIN, 5, o, c).overlaps({MIN, 16, c, c})); + EXPECT_TRUE(i(MIN, 5, ca, c).overlaps({MIN, 16, c, c})); + EXPECT_TRUE(i(MIN, 5, c, c).overlaps({MIN, 16, o, c})); + EXPECT_TRUE(i(MIN, 5, o, c).overlaps({MIN, 16, o, c})); + EXPECT_TRUE(i(MIN, 5, ca, c).overlaps({MIN, 16, o, c})); + EXPECT_TRUE(i(MIN, 5, c, c).overlaps({MIN, 16, ca, c})); + EXPECT_TRUE(i(MIN, 5, o, c).overlaps({MIN, 16, ca, c})); + EXPECT_TRUE(i(MIN, 5, ca, c).overlaps({MIN, 16, ca, c})); + + // both max + EXPECT_TRUE(i(0, MAX, c, c).overlaps({3, MAX, c, c})); + EXPECT_TRUE(i(0, MAX, c, o).overlaps({3, MAX, c, c})); + EXPECT_TRUE(i(0, MAX, c, ca).overlaps({3, MAX, c, c})); + EXPECT_TRUE(i(0, MAX, c, c).overlaps({3, MAX, c, o})); + EXPECT_TRUE(i(0, MAX, c, c).overlaps({3, MAX, c, c})); + EXPECT_TRUE(i(0, MAX, c, c).overlaps({3, MAX, c, ca})); + EXPECT_TRUE(i(0, MAX, c, ca).overlaps({3, MAX, c, c})); + EXPECT_TRUE(i(0, MAX, c, ca).overlaps({3, MAX, c, o})); + EXPECT_TRUE(i(0, MAX, c, ca).overlaps({3, MAX, c, ca})); + + // min-max overlap + EXPECT_TRUE(i(0, MAX, c, c).overlaps({MIN, 3, c, c})); + EXPECT_TRUE(i(0, MAX, c, o).overlaps({MIN, 3, c, c})); + EXPECT_TRUE(i(0, MAX, c, ca).overlaps({MIN, 3, c, c})); + EXPECT_TRUE(i(0, MAX, c, c).overlaps({MIN, 3, o, c})); + EXPECT_TRUE(i(0, MAX, c, o).overlaps({MIN, 3, o, c})); + EXPECT_TRUE(i(0, MAX, c, ca).overlaps({MIN, 3, o, c})); + EXPECT_TRUE(i(0, MAX, c, c).overlaps({MIN, 3, ca, c})); + EXPECT_TRUE(i(0, MAX, c, o).overlaps({MIN, 3, ca, c})); + EXPECT_TRUE(i(0, MAX, c, ca).overlaps({MIN, 3, ca, c})); + + // min-max not overlap + EXPECT_FALSE(i(1, MAX, c, c).overlaps({MIN, 0, c, c})); + EXPECT_FALSE(i(1, MAX, c, o).overlaps({MIN, 0, c, c})); + EXPECT_FALSE(i(1, MAX, c, ca).overlaps({MIN, 0, c, c})); + EXPECT_FALSE(i(1, MAX, c, c).overlaps({MIN, 0, o, c})); + EXPECT_FALSE(i(1, MAX, c, o).overlaps({MIN, 0, o, c})); + EXPECT_FALSE(i(1, MAX, c, ca).overlaps({MIN, 0, o, c})); + EXPECT_FALSE(i(1, MAX, c, c).overlaps({MIN, 0, ca, c})); + EXPECT_FALSE(i(1, MAX, c, o).overlaps({MIN, 0, ca, c})); + EXPECT_FALSE(i(1, MAX, c, ca).overlaps({MIN, 0, ca, c})); +} + TEST_F(IntervalTests, DynamicWithinTest) { auto base = i(-100, 100, interval_border::closed, interval_border::closed); From 5997c050b9a9d9898788984036f58e6b8cfbbd3b Mon Sep 17 00:00:00 2001 From: Pieter Svenson Date: Wed, 24 Jun 2026 18:34:52 +0000 Subject: [PATCH 4/5] include limits header --- include/interval-tree/interval_types.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/interval-tree/interval_types.hpp b/include/interval-tree/interval_types.hpp index f40045c..9595fc5 100644 --- a/include/interval-tree/interval_types.hpp +++ b/include/interval-tree/interval_types.hpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace lib_interval_tree { From 9a9e05c8f7ffae57955bb159b17fcb30f33601f3 Mon Sep 17 00:00:00 2001 From: Pieter Svenson Date: Wed, 24 Jun 2026 19:02:57 +0000 Subject: [PATCH 5/5] switch to ilammy/msvc-dev-cmd --- .github/workflows/build_and_test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 845623b..c47dd11 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -64,7 +64,7 @@ jobs: uses: actions/checkout@v2 - name: Setup MSVC - uses: microsoft/setup-msbuild@v1.0.2 + uses: ilammy/msvc-dev-cmd@v1 - name: Install vcpkg and gtest run: | @@ -76,12 +76,12 @@ jobs: VCPKG_DEFAULT_TRIPLET: x64-windows - name: CMake Configure - run: cmake -B ${{github.workspace}}/build -G"Visual Studio 17 2022" -A x64 -DCMAKE_TOOLCHAIN_FILE=${{github.workspace}}/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_STANDARD=20 -DINT_TREE_USE_OPTIONAL_POLYFILL=on -DINT_TREE_BUILD_EXAMPLES=on -DINT_TREE_ENABLE_TESTS=on -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + run: cmake -B ${{github.workspace}}/build -G"Ninja" -DCMAKE_TOOLCHAIN_FILE=${{github.workspace}}/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_STANDARD=20 -DINT_TREE_USE_OPTIONAL_POLYFILL=on -DINT_TREE_BUILD_EXAMPLES=on -DINT_TREE_ENABLE_TESTS=on -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - name: Test working-directory: ${{github.workspace}}/build - run: .\tests\Release\tree-tests.exe + run: .\tests\tree-tests.exe shell: cmd \ No newline at end of file