include(CTest)

# prepare Catch library
set(CATCH_INCLUDE_DIR ${HIGHS_SOURCE_DIR}/src/extern/catch)
add_library(Catch INTERFACE)
target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
target_include_directories(Catch INTERFACE ${HIGHS_SOURCE_DIR}/src)

configure_file(${HIGHS_SOURCE_DIR}/check/HCheckConfig.h.in ${HIGHS_BINARY_DIR}/HCheckConfig.h)

FILE(WRITE ${CMAKE_BINARY_DIR}/testoptions.txt
"mip_rel_gap=0.0
mip_abs_gap=0.0")

# Make test executable
set(TEST_SOURCES
    TestHighsVersion.cpp
    TestAlienBasis.cpp
    TestDualize.cpp
    TestCallbacks.cpp
    TestCheckSolution.cpp
    TestEkk.cpp
    TestFactor.cpp
    TestFreezeBasis.cpp
    TestHotStart.cpp
    TestMain.cpp
    TestNames.cpp
    TestOptions.cpp
    TestIO.cpp
    TestSort.cpp
    TestSetup.cpp
    TestFilereader.cpp
    TestHighsGFkSolve.cpp
    TestInfo.cpp
    TestBasis.cpp
    TestBasisSolves.cpp
    TestCrossover.cpp
    TestHighsHash.cpp
    TestHighsIntegers.cpp
    TestHighsParallel.cpp
    TestHighsRbTree.cpp
    TestHighsHessian.cpp
    TestHighsModel.cpp
    TestHighsSparseMatrix.cpp
    TestHSet.cpp
    TestICrash.cpp
    TestLogging.cpp
    TestLPFileFormat.cpp
    TestLpValidation.cpp
    TestLpModification.cpp
    TestLpOrientation.cpp
    TestPresolve.cpp
    TestQpSolver.cpp
    TestRays.cpp
    TestRanging.cpp
    TestSemiVariables.cpp
    TestThrow.cpp
    Avgas.cpp)

if (NOT APPLE)
    # Bug with updated IPX code and gas11. Maybe somehow related to the rpath on 
    # macOS (Lukas). Only triggered by gas11 with no presolve which is strange. 
    # may be an interface related issue which will pop up soon. 
    # works OK on linux. The test was added to doctest for macOS but still hanging.
    set(TEST_SOURCES ${TEST_SOURCES} TestSpecialLps.cpp TestLpSolvers.cpp TestMipSolver.cpp)
endif()

set (TEST_SOURCES ${TEST_SOURCES} TestIpx.cpp)

add_executable(unit_tests ${TEST_SOURCES})
if (UNIX)
    target_compile_options(unit_tests PRIVATE "-Wno-unused-variable")
    target_compile_options(unit_tests PRIVATE "-Wno-unused-const-variable")
endif()
target_link_libraries(unit_tests libhighs Catch)

if(FORTRAN_FOUND)
   set(CMAKE_Fortran_MODULE_DIRECTORY ${HIGHS_BINARY_DIR}/modules)
   include_directories(${HIGHS_SOURCE_DIR}/src)
   add_executable(fortrantest TestFortranAPI.f90)
   if (NOT FAST_BUILD)
       target_link_libraries(fortrantest libhighs FortranHighs)
    else()
       target_link_libraries(fortrantest highs FortranHighs)
    endif()
   target_include_directories(fortrantest PUBLIC ${HIGHS_SOURCE_DIR}/src/interfaces)
 endif(FORTRAN_FOUND)

# check the C API
add_executable(capi_unit_tests TestCAPI.c)
target_link_libraries(capi_unit_tests libhighs)
add_test(NAME capi_unit_tests COMMAND capi_unit_tests)

# Check whether test executable builds OK.
add_test(NAME unit-test-build
         COMMAND ${CMAKE_COMMAND}
                 --build ${HIGHS_BINARY_DIR}
                 --target unit_tests
                 --config ${CMAKE_BUILD_TYPE}
         )


# Avoid that several build jobs try to concurretly build.
set_tests_properties(unit-test-build
                     PROPERTIES
                     RESOURCE_LOCK unittestbin)

# create a binary running all the tests in the executable
add_test(NAME unit_tests_all COMMAND unit_tests --success)
set_tests_properties(unit_tests_all
                    PROPERTIES
                    DEPENDS unit-test-build)
set_tests_properties(unit_tests_all PROPERTIES TIMEOUT 10000) 

# An individual test can be added with the command below but the approach
# above with a single add_test for all the unit tests automatically detects all
# TEST_CASEs in the source files specified in TEST_SOURCES. Do not define any
# tests in TestMain.cpp and do not define CATCH_CONFIG_MAIN anywhere else.
# add_test(NAME correct-print-test COMMAND unit_tests correct-print)

# --------------------------------------
# Another way of adding the tests. Needs a script from github repo and a
# Catch2 installation. So add tests manually if there is no build issues.
# catch_discover_tests(unit_test)

# --------------------------------------
# Run instance tests.
#
# define the set of feasible instances
set(successInstances
    "25fv47\;3149\; 5.5018458883\;"
    "80bau3b\;3686\; 9.8722419241\;"
    "adlittle\;74\; 2.2549496316\;"
    "afiro\;22\;-4.6475314286\;"
    "etamacro\;532\;-7.5571523330\;"
    "greenbea\;5109\;-7.2555248130\;"
    "shell\;623\; 1.2088253460\;"
    "stair\;529\;-2.5126695119\;"
    "standata\;72\; 1.2576995000\;"
    "standgub\;68\; 1.2576995000\;"
    "standmps\;218\; 1.4060175000\;"
    )

set(infeasibleInstances
    "bgetam\;        infeasible"
    "box1\;          infeasible"
    "ex72a\;         infeasible"
    "forest6\;       infeasible"
    "galenet\;       infeasible"
    "gams10am\;      infeasible"
#    "klein1\;        infeasible"
    "refinery\;      infeasible"
    "woodinfe\;      infeasible"
    )

set(unboundedInstances
     "gas11\;         unbounded"
    )

set(failInstances
    )
 
set(mipInstances 
    "small_mip\;3.2368421\;"
    "flugpl\;1201500\;"
    "lseu\;1120|1119.9999999\;"
    "egout\;(568.1007|568.1006999)\;"
    "gt2\;21166\;"
    "rgn\;82.1999992\;"
    "bell5\;(8966406.49152|8966406.491519|8966406.49151)\;"
    "sp150x300d\;(69|68.9999999)\;"
    "p0548\;(8691|8690.9999999)\;"
    "dcmulti\;188182\;"
    )

# define settings
set(settings
    "--presolve=off"
    "--presolve=on"
    "--random_seed=1"
    "--random_seed=2"
    "--random_seed=3"
#   "--random_seed=4"
#   "--random_seed=5"
#   "--parallel=on"
    )

# define a macro to add tests
#
# add_instancetests takes an instance group and a status
# that the solver should report as arguments
macro(add_instancetests instances solutionstatus)
# loop over the instances
foreach(instance ${${instances}})
    # add default tests
    # treat the instance as a tuple (list) of two values
    list(GET instance 0 name)
    list(GET instance 1 iter)

    if(${solutionstatus} STREQUAL "Optimal")
        list(GET instance 2 optval)
    endif()

    # specify the instance and the settings load command
    if(ZLIB AND ZLIB_FOUND AND EXISTS "${HIGHS_SOURCE_DIR}/check/instances/${name}.mps.gz")
      set(inst "${HIGHS_SOURCE_DIR}/check/instances/${name}.mps.gz")
    else()
      set(inst "${HIGHS_SOURCE_DIR}/check/instances/${name}.mps")
    endif()

    # loop over all settings
    foreach(setting ${settings})
        add_test(NAME ${name}${setting} COMMAND $<TARGET_FILE:highs> ${setting}
              ${inst})

        set_tests_properties (${name}${setting} PROPERTIES
                DEPENDS unit_tests_all)
        set_tests_properties (${name}${setting} PROPERTIES
                PASS_REGULAR_EXPRESSION
                "Model   status      : ${solutionstatus}")

        if(${solutionstatus} STREQUAL "Optimal")
            if(${setting} STREQUAL "--presolve=off")
                set_tests_properties (${name}${setting} PROPERTIES
                        PASS_REGULAR_EXPRESSION
                        "Simplex   iterations: ${iter}\nObjective value     : ${optval}")
            else()
                set_tests_properties (${name}${setting} PROPERTIES
                        PASS_REGULAR_EXPRESSION
                        "Objective value     : ${optval}")
            endif()
        endif()
    endforeach(setting)
endforeach(instance)
endmacro(add_instancetests)

# add tests for success and fail instances
add_instancetests(successInstances "Optimal")
add_instancetests(failInstances "Fail")
add_instancetests(infeasibleInstances "Infeasible")
#add_instancetests(unboundedInstances "Unbounded")

foreach(instance ${mipInstances})
    list(GET instance 0 name)
    list(GET instance 1 optval)
    # specify the instance and the settings load command
    set(inst "${HIGHS_SOURCE_DIR}/check/instances/${name}.mps")

    foreach(setting ${settings})
        add_test(NAME ${name}${setting} COMMAND $<TARGET_FILE:highs> ${setting} --options_file ${CMAKE_BINARY_DIR}/testoptions.txt
              ${inst})

        set_tests_properties (${name}${setting} PROPERTIES
                DEPENDS unit_tests_all)

        set_tests_properties (${name}${setting} PROPERTIES
                PASS_REGULAR_EXPRESSION
                "Status            Optimal\n  Primal bound      ${optval}.*\n  Dual bound        ${optval}.*\n  Solution status   feasible\n                    ${optval}.* \\(objective\\)"
                FAIL_REGULAR_EXPRESSION
                "Solution status   infeasible")

    endforeach(setting)
endforeach()
