#------------------------------------------------------------------------------
# Test suite. Dependency-free: each test is a small C program whose exit code
# is the pass/fail signal, registered with CTest.
#------------------------------------------------------------------------------

set(TIFA_TEST_INCLUDE_DIRS
    "${PROJECT_BINARY_DIR}"                     # generated tifa_config.h
    "${PROJECT_SOURCE_DIR}/lib/algo/include"
    "${PROJECT_SOURCE_DIR}/lib/data/include"
    "${PROJECT_SOURCE_DIR}/lib/utils/include"
    "${CMAKE_CURRENT_SOURCE_DIR}"               # tifa_test.h
)

function(tifa_add_test name)
    add_executable(${name} ${name}.c)
    target_include_directories(${name} PRIVATE ${TIFA_TEST_INCLUDE_DIRS})
    target_link_libraries(${name} PRIVATE tifa_static)
    add_test(NAME ${name} COMMAND ${name})
    # Cap runtime so a regression that hangs (e.g. a bad loop) is reported as a
    # failure rather than stalling the run indefinitely.
    set_tests_properties(${name} PROPERTIES TIMEOUT 120)
endfunction()

tifa_add_test(test_funcs)
tifa_add_test(test_matrix)
tifa_add_test(test_containers)
tifa_add_test(test_tifa_factor)
tifa_add_test(test_algorithms)
tifa_add_test(test_siqs)
tifa_add_test(test_factor_u64)
tifa_add_test(test_mont_ecm)
tifa_add_test(test_mont_pm1)
tifa_add_test(test_cofactor)
tifa_add_test(test_inputs)

# The tuning-loader test reads fixture files from the source tree; hand it their
# location as a compile definition (no env var needed: the test unsets
# $TIFA_TUNING_DIR and never falls back to the installed tuning directory).
tifa_add_test(test_tuning)
target_compile_definitions(test_tuning PRIVATE
    TIFA_TEST_FIXTURE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/fixtures")

tifa_add_test(test_threads)
target_link_libraries(test_threads PRIVATE Threads::Threads)
target_compile_definitions(test_threads PRIVATE
    TIFA_TEST_FIXTURE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/fixtures")

# The tifa-factor command-line program: a 60-bit semiprime, and
# 2^3 . 3^2 . 1000000007^2 . 1000000009 (repeated factors, several sizes). The
# large factors' order depends on the build (with or without __int128).
add_test(NAME tifa_factor_program_semiprime
         COMMAND tifa_factor 1000000016000000063)
set_tests_properties(tifa_factor_program_semiprime PROPERTIES
    TIMEOUT 60
    PASS_REGULAR_EXPRESSION "(1000000007[^0-9].*1000000009|1000000009[^0-9].*1000000007)[^0-9].*Factorization is complete")
add_test(NAME tifa_factor_program_powers
         COMMAND tifa_factor 72000001656000012600000031752)
set_tests_properties(tifa_factor_program_powers PROPERTIES
    TIMEOUT 60
    PASS_REGULAR_EXPRESSION "2 \\^ 3[^0-9].*3 \\^ 2[^0-9].*(1000000009 \\^ 1[^0-9].*1000000007 \\^ 2|1000000007 \\^ 2[^0-9].*1000000009 \\^ 1)[^0-9].*Factorization is complete")
# A square of a composite: the program factors its root, then squares.
add_test(NAME tifa_factor_program_square
         COMMAND tifa_factor 1000000032000000382000002016000003969)
set_tests_properties(tifa_factor_program_square PROPERTIES
    TIMEOUT 60
    PASS_REGULAR_EXPRESSION "(1000000007 \\^ 2[^0-9].*1000000009 \\^ 2|1000000009 \\^ 2[^0-9].*1000000007 \\^ 2)[^0-9].*Factorization is complete")
# A hint: 340730379077795843 . 604614875560711445197 (128 bits).
add_test(NAME tifa_factor_program_hint
         COMMAND tifa_factor balanced 206010655745875572166197184221848916071)
set_tests_properties(tifa_factor_program_hint PROPERTIES
    TIMEOUT 60
    PASS_REGULAR_EXPRESSION "hint: balanced.*(340730379077795843[^0-9].*604614875560711445197|604614875560711445197[^0-9].*340730379077795843)[^0-9].*Factorization is complete")
# A pass regex alone ignores the exit status: fail on sanitizer reports too.
set_tests_properties(tifa_factor_program_semiprime tifa_factor_program_powers
                     tifa_factor_program_square tifa_factor_program_hint
                     PROPERTIES
    FAIL_REGULAR_EXPRESSION "ERROR|Sanitizer|runtime error")

# The programs' exit statuses (tool_utils.h): 0 complete (or a prime, or 1),
# 1 partial, 2 no factor found, 3 invalid input.
function(tifa_add_exit_test name expected program)
    add_test(NAME ${name}
             COMMAND ${CMAKE_COMMAND} "-DPROGRAM=$<TARGET_FILE:${program}>"
                     "-DARGS=${ARGN}" -DEXPECTED=${expected}
                     -P "${CMAKE_CURRENT_SOURCE_DIR}/check_exit.cmake")
    set_tests_properties(${name} PROPERTIES TIMEOUT 60)
endfunction()
tifa_add_exit_test(exit_complete  0 tifa_factor 1000000016000000063)
tifa_add_exit_test(exit_prime     0 tifa_factor 1000003)
tifa_add_exit_test(exit_partial   1 tdiv 10 3000000048000000189)
tifa_add_exit_test(exit_no_factor 2 tdiv 10 1000000016000000063)
tifa_add_exit_test(exit_zero      3 tifa_factor 0)
tifa_add_exit_test(exit_not_digit 3 tifa_factor 12x)
tifa_add_exit_test(exit_bad_argc  3 tifa_factor random 12 34)
tifa_add_exit_test(exit_bad_hint  3 tifa_factor 12 34)
tifa_add_exit_test(exit_no_input  3 tifa_factor)
tifa_add_exit_test(exit_help      0 tifa_factor --help)
tifa_add_exit_test(exit_help_h    0 siqs -h)
