# Each of these source test files is instantiated in single and double precision
set(TESTS
    basicpassfail
    dumbinputs
    finufft1d_test
    finufft1dmany_test
    finufft2d_test
    finufft2dmany_test
    finufft3d_test
    finufft3dmany_test
    spreadinterp1d_test
    adjointness
    tolsweep
)

foreach(TEST ${TESTS})
    add_executable(${TEST} ${TEST}.cpp)
    target_compile_features(${TEST} PRIVATE cxx_std_17)
    finufft_link_test(${TEST})
    add_executable(${TEST}f ${TEST}.cpp)
    target_compile_definitions(${TEST}f PRIVATE -DSINGLE)
    target_compile_features(${TEST}f PRIVATE cxx_std_17)
    finufft_link_test(${TEST}f)
endforeach()

# copy the DLLs to the build directory so that the tests can find them there fix
# for windows
copy_dll(finufft basicpassfail)

# Small C error-handling test for the public C API.
include(CheckCSourceCompiles)
# gersemi: off
check_c_source_compiles("
        #include <complex.h>
        int main() {
            float complex z = 1.0f + 2.0f*I;
            float x = crealf(z);
            return 0;
        }
    " HAS_C_COMPLEX)
# gersemi: on

if(HAS_C_COMPLEX)
    add_executable(error_handling error_handling.c)
    target_compile_features(error_handling PRIVATE c_std_99)
    finufft_link_test(error_handling)
    add_test(NAME run_error_handling COMMAND error_handling WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
        # the deprecated-opts case must warn, not error (non-C++ wrappers rely on it)
    set_tests_properties(
        run_error_handling
        PROPERTIES PASS_REGULAR_EXPRESSION "opts.spread_thread is deprecated and ignored"
    )
endif()

# Add ctest definitions not for both precisions...
# testutils also unit-tests the complexity-based upsampfac picker, so it includes
# the internal heuristics.hpp and needs xsimd + poet (the dispatcher) on top of
# finufft_link_test.
add_executable(testutils testutils.cpp)
if(FINUFFT_USE_DUCC0)
    target_compile_definitions(testutils PRIVATE -DFINUFFT_USE_DUCC0)
endif()
target_compile_features(testutils PRIVATE cxx_std_17)
finufft_link_test(testutils)
target_link_libraries(testutils PRIVATE xsimd poet::poet)
add_test(NAME run_testutils COMMAND testutils WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

add_executable(threadsafe_execute threadsafe_execute.cpp)
finufft_link_test(threadsafe_execute)
if(NOT (MINGW OR MSYS OR CYGWIN)) # crashes during std::thread teardown on MinGW (libgomp TLS).
    add_test(NAME run_threadsafe_execute COMMAND threadsafe_execute WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
endif()

if(NOT FINUFFT_USE_DUCC0 AND FINUFFT_USE_OPENMP)
    find_package(OpenMP COMPONENTS CXX REQUIRED)
    add_executable(fftw_lock_test fftw_lock_test.cpp)
    target_compile_features(fftw_lock_test PRIVATE cxx_std_17)
    finufft_link_test(fftw_lock_test)
    target_link_libraries(fftw_lock_test PRIVATE OpenMP::OpenMP_CXX)
    add_test(NAME run_fftw_lock_test COMMAND fftw_lock_test WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
endif()

# Add ctest definitions that run at both precisions...
function(add_tests_with_prec PREC REQ_TOL CHECK_TOL SUFFIX)
    # All of the following should be run at OMP_NUM_THREADS=4 or something small,
    # as in makefile. This prevents them taking a huge time on a, say, 128-core
    # Rome node. ... but I don't know platform-indep way to do that! Does anyone?

    add_test(NAME run_basic_pass_fail_${PREC} COMMAND basicpassfail${SUFFIX} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

    add_test(
        NAME run_finufft1d_test_${PREC}
        COMMAND finufft1d_test${SUFFIX} 1e2 2e2 ${REQ_TOL} 0 2 0.0 ${CHECK_TOL}
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )

    # default opts must stay silent: a deprecation warning here would hit every user
    set_tests_properties(run_finufft1d_test_${PREC} PROPERTIES FAIL_REGULAR_EXPRESSION "deprecated and ignored")

    add_test(
        NAME run_finufft1dmany_test_${PREC}
                COMMAND finufft1dmany_test${SUFFIX} 3 1e2 2e2 ${REQ_TOL} 0 0 2 0.0 ${CHECK_TOL}
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )

    # maxbatchsize=1 forces nbatch=ntrans: the batch loop in execute runs once per
    # transform with batchSize=1. All other many-tests leave maxbatchsize=0, which
    # gives nbatch=1 on >=3 threads, so this path is otherwise never exercised.
    add_test(
        NAME run_finufft1dmany_test_nbatch_${PREC}
        COMMAND finufft1dmany_test${SUFFIX} 8 1e2 2e2 ${REQ_TOL} 0 1 2 0.0 ${CHECK_TOL}
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )

    add_test(
        NAME run_finufft2d_test_${PREC}
        COMMAND finufft2d_test${SUFFIX} 1e2 1e1 1e3 ${REQ_TOL} 0 2 0.0 ${CHECK_TOL}
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )

    add_test(
        NAME run_finufft2dmany_test_${PREC}
        COMMAND finufft2dmany_test${SUFFIX} 3 1e2 1e1 1e3 ${REQ_TOL} 0 0 2 0.0 ${CHECK_TOL}
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )

    add_test(
        NAME run_finufft2dmany_test_nbatch_${PREC}
        COMMAND finufft2dmany_test${SUFFIX} 8 1e2 1e1 1e3 ${REQ_TOL} 0 1 2 0.0 ${CHECK_TOL}
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )

    add_test(
        NAME run_finufft3d_test_${PREC}
        COMMAND finufft3d_test${SUFFIX} 5 10 20 1e2 ${REQ_TOL} 0 2 0.0 ${CHECK_TOL}
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )

    add_test(
        NAME run_finufft3dmany_test_${PREC}
        COMMAND finufft3dmany_test${SUFFIX} 2 5 10 20 1e2 ${REQ_TOL} 0 0 2 0.0 ${CHECK_TOL}
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )

    add_test(NAME run_dumbinputs_${PREC} COMMAND dumbinputs${SUFFIX} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

    add_test(
        NAME spreadinterp1d_${PREC}
        COMMAND spreadinterp1d_test${SUFFIX} 1e3 1e3 ${REQ_TOL} 0 2 2.0 ${CHECK_TOL}
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )

    add_test(NAME run_tolsweep_${PREC} COMMAND tolsweep${SUFFIX} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
    # under valgrind tolsweep needs more than ctest's 1500 s default
    set_tests_properties(run_tolsweep_${PREC} PROPERTIES TIMEOUT 3000)

    add_test(NAME run_adjointness_${PREC} COMMAND adjointness${SUFFIX} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
endfunction()

# use above function to actually add the tests, with certain requested and check
# tols; these are for classical tests that test one tol at fp32 and one at fp64.
# (The middle two tol arguments are not used by some of the tests, eg, by
# tolsweep)...
# Float tol loosened from 1e-5 pending auto-mode sigma coupling (see setpts.hpp TODO).
add_tests_with_prec(float 1e-3 1e-2 f)
add_tests_with_prec(double 1e-12 1e-11 "")
