################################################################################
# Copyright 2018-2025, Barcelona Supercomputing Center (BSC), Spain            #
# Copyright 2015-2025, Johannes Gutenberg Universitaet Mainz, Germany          #
#                                                                              #
# This software was partially supported by the                                 #
# EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).    #
#                                                                              #
# This software was partially supported by the                                 #
# ADA-FS project under the SPPEXA project funded by the DFG.                   #
#                                                                              #
# This file is part of GekkoFS.                                                #
#                                                                              #
# GekkoFS is free software: you can redistribute it and/or modify              #
# it under the terms of the GNU General Public License as published by         #
# the Free Software Foundation, either version 3 of the License, or            #
# (at your option) any later version.                                          #
#                                                                              #
# GekkoFS is distributed in the hope that it will be useful,                   #
# but WITHOUT ANY WARRANTY; without even the implied warranty of               #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                #
# GNU General Public License for more details.                                 #
#                                                                              #
# You should have received a copy of the GNU General Public License            #
# along with GekkoFS.  If not, see <https://www.gnu.org/licenses/>.            #
#                                                                              #
# SPDX-License-Identifier: GPL-3.0-or-later                                    #
################################################################################

include(FetchContent)

# get Catch2
set(FETCHCONTENT_QUIET OFF)
FetchContent_Declare(catch2
    GIT_REPOSITORY https://github.com/catchorg/Catch2.git
    GIT_TAG v3.16.0
    GIT_SHALLOW ON
    GIT_PROGRESS ON
)

FetchContent_MakeAvailable(catch2)

set(GKFS_CATCH_TEST_SPEC "" CACHE STRING
    "Catch2 test specification passed to unit_tests")
set(GKFS_CATCH_DISCOVERY_MODE "PRE_TEST" CACHE STRING
    "Catch2 discovery mode: POST_BUILD or PRE_TEST")
set_property(CACHE GKFS_CATCH_DISCOVERY_MODE PROPERTY STRINGS POST_BUILD PRE_TEST)
set(GKFS_CATCH_ADD_TAGS_AS_LABELS ON CACHE BOOL
    "Expose Catch2 tags as CTest labels")
set(GKFS_CATCH_EXTRA_ARGS "" CACHE STRING
    "Extra arguments passed to each Catch2 unit test")

if (NOT GKFS_CATCH_DISCOVERY_MODE STREQUAL "POST_BUILD" AND
    NOT GKFS_CATCH_DISCOVERY_MODE STREQUAL "PRE_TEST")
    message(FATAL_ERROR
        "GKFS_CATCH_DISCOVERY_MODE must be POST_BUILD or PRE_TEST")
endif ()

add_subdirectory(helpers)

# create a convenience library with Catch2's main
# to speed up test compilation
#add_library(catch2_main STATIC)
#target_sources(catch2_main PRIVATE catch_main.cpp)
#target_link_libraries(catch2_main
#    Catch2::Catch2
#)

# define executables for tests and make them depend on the convenience
# library (and Catch2 transitively) and fmt
add_executable(unit_tests)
target_sources(unit_tests
    PRIVATE
    ${CMAKE_CURRENT_LIST_DIR}/test_utils_arithmetic.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_path.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_common_path.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_distributor.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_replica_placement.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_replica_migrator.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_replica_migration_adapter.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_repair_queue.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_helpers.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_random_slicing_distributor.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_distributor_factory_and_migrator.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_random_slicing_pipeline.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_env_util.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_stats.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_unique_fd.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_repair_journal_lock.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_fs_data_faults.cpp
    ${CMAKE_CURRENT_LIST_DIR}/test_migration_checkpoint.cpp
    ${CMAKE_SOURCE_DIR}/src/common/hostfile_management.cpp
    ${CMAKE_SOURCE_DIR}/src/daemon/classes/fs_data.cpp)

if (GKFS_TESTS_GUIDED_DISTRIBUTION)
    target_sources(unit_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR}/test_guided_distributor.cpp)
endif ()

if (GKFS_BUILD_USER_LIB)
target_link_libraries(unit_tests
    PRIVATE
    Catch2WithMain
    fmt::fmt
    spdlog::spdlog
    path_util
    helpers
    arithmetic
    env_util
    distributor
    statistics
    gkfs_user_lib
)
else ()
target_link_libraries(unit_tests
    PRIVATE
    Catch2WithMain
    fmt::fmt
    spdlog::spdlog
    path_util
    helpers
    arithmetic
    env_util
    distributor
    statistics
)
endif()

# ThreadSanitizer may fail to reserve its shadow memory when ASLR chooses an
# incompatible address layout. Run TSAN tests with ASLR disabled so Catch2 test
# discovery and CTest use the same stable execution environment.
if (CMAKE_CXX_FLAGS MATCHES "(^|[ ;])-fsanitize=thread([ ;]|$)")
    set_property(TARGET unit_tests PROPERTY CROSSCOMPILING_EMULATOR
                 "setarch;x86_64;-R")
endif ()

# Catch2's extras folder includes helper functions
# to auto-discover Catch tests and register them in CTest
set(CMAKE_MODULE_PATH "${catch2_SOURCE_DIR}/extras" ${CMAKE_MODULE_PATH})
include(Catch)
set(_gkfs_catch_discover_args)
if (GKFS_CATCH_TEST_SPEC)
    list(APPEND _gkfs_catch_discover_args TEST_SPEC ${GKFS_CATCH_TEST_SPEC})
endif ()
if (GKFS_CATCH_EXTRA_ARGS)
    list(APPEND _gkfs_catch_discover_args EXTRA_ARGS ${GKFS_CATCH_EXTRA_ARGS})
endif ()
list(APPEND _gkfs_catch_discover_args
    DISCOVERY_MODE ${GKFS_CATCH_DISCOVERY_MODE}
    PROPERTIES LABELS "unit::all")
if (GKFS_CATCH_ADD_TAGS_AS_LABELS)
    list(APPEND _gkfs_catch_discover_args ADD_TAGS_AS_LABELS)
endif ()
catch_discover_tests(unit_tests ${_gkfs_catch_discover_args})

if (GKFS_INSTALL_TESTS)
    install(TARGETS unit_tests
        DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/unit
    )
endif ()
