cmake_minimum_required(VERSION 3.21)

# ── Catch2 ────────────────────────────────────────────────────────────────────
find_package(Catch2 3 QUIET)
if(NOT Catch2_FOUND)
    include(FetchContent)
    FetchContent_Declare(
        Catch2
        GIT_REPOSITORY https://github.com/catchorg/Catch2.git
        GIT_TAG        v3.5.3
    )
    FetchContent_MakeAvailable(Catch2)
endif()

# ── Google Test ───────────────────────────────────────────────────────────────
find_package(GTest QUIET)
if(NOT GTest_FOUND)
    include(FetchContent)
    FetchContent_Declare(
        googletest
        GIT_REPOSITORY https://github.com/google/googletest.git
        GIT_TAG        v1.14.0
    )
    FetchContent_MakeAvailable(googletest)
endif()

# ── Test executable ───────────────────────────────────────────────────────────
add_executable(kpn_tests
    test_fixed_string.cpp
    test_traits.cpp
    test_channel.cpp
    test_node.cpp
    test_network.cpp
    test_static_network.cpp
    test_shared_resource.cpp
    test_pool_node.cpp
    test_backpressure_deadlock.cpp
    test_scheduler.cpp
)

target_link_libraries(kpn_tests PRIVATE
    kpn
    Catch2::Catch2WithMain
    GTest::gtest
)

# ── Channel stress suite (separate executable) ────────────────────────────────
# Contended SPSC tests for the lock-free Channel<T>. Kept out of kpn_tests
# because each case runs many reps / tens of thousands of items and is slow.
# Most valuable under -DKPN_SANITIZER=thread, but correct (and run) without it.
add_executable(kpn_tests_stress test_channel_stress.cpp)
target_link_libraries(kpn_tests_stress PRIVATE kpn Catch2::Catch2WithMain)

# ── Sanitizer flags ───────────────────────────────────────────────────────────
# kpn_sanitizer_flags() is defined in the top-level CMakeLists and is a no-op
# unless -DKPN_SANITIZER=... is set. Sanitizer must be on both compile and link.
kpn_sanitizer_flags(_kpn_san)
if(_kpn_san)
    foreach(_t kpn_tests kpn_tests_stress)
        target_compile_options(${_t} PRIVATE ${_kpn_san})
        target_link_options(${_t}    PRIVATE ${_kpn_san})
    endforeach()
endif()

include(CTest)
include(Catch)

# DISCOVERY_MODE PRE_TEST defers test enumeration to `ctest` run time. The
# default (POST_BUILD) runs each test binary during the build to list its
# cases — which fails a sanitizer build: a TSan/ASan binary needs a fixed
# address-space layout and aborts on startup ("unexpected memory mapping")
# under the container's ASLR, breaking the build before any test runs. The
# tsan.yaml job invokes the binaries directly (not via ctest), so deferring
# discovery costs nothing there and keeps `ctest` working for normal builds.
catch_discover_tests(kpn_tests DISCOVERY_MODE PRE_TEST)
# Register the stress suite under its own label so CI can run / time it
# separately from the fast unit tests.
catch_discover_tests(kpn_tests_stress DISCOVERY_MODE PRE_TEST PROPERTIES LABELS "stress")
