##
MACRO(FRENSIE_INITIALIZE_PACKAGE_TESTS PACKAGE_NAME)
  SET(options)
  SET(oneValueArgs BOOST_TEST_LIB)
  SET(multiValueArgs)
  CMAKE_PARSE_ARGUMENTS(FRENSIE_INITIALIZE_PACKAGE_TESTS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

  # Check for unused parameters
  IF(NOT "${FRENSIE_INITIALIZE_PACKAGE_TESTS_UNPARSED_ARGUMENTS}" STREQUAL "")
    MESSAGE(WARNING "${PACKAGE_NAME} tests initialization unused parameters = ${FRENSIE_INITIALIZE_PACKAGE_TESTS_UNPARSED_ARGUMENTS}")
  ENDIF()

  # Determine if boost tests can be created
  IF(NOT "${FRENSIE_INITIALIZE_PACKAGE_TESTS_BOOST_TEST_LIB}" STREQUAL "")
    SET(BOOST_TESTING_ENABLED TRUE)
    SET(BOOST_TEST_LIB ${FRENSIE_INITIALIZE_PACKAGE_TESTS_BOOST_TEST_LIB})
  ELSE()
    SET(BOOST_TESTING_ENABLED FALSE)
  ENDIF()

  # Set variables required by the other test macros
  SET(PACKAGE_LIBRARY ${PACKAGE_NAME})
  SET(PACKAGE_TEST_EXECS)
ENDMACRO(FRENSIE_INITIALIZE_PACKAGE_TESTS)

##
MACRO(FRENSIE_ADD_TEST_EXECUTABLE TEST_NAME_ROOT)
  SET(options BOOST_TEST)
  SET(oneValueArgs)
  SET(multiValueArgs DEPENDS LIB_DEPENDS TARGET_DEPENDS)
  CMAKE_PARSE_ARGUMENTS(FRENSIE_ADD_TEST_EXECUTABLE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

  # Check for unused parameters
  IF(NOT "${FRENSIE_ADD_TEST_EXECUTABLE_UNPARSED_ARGUMENTS}" STREQUAL "")
    MESSAGE(WARNING "FRENSIE_ADD_TEST_EXECUTABLE ${TEST_NAME_ROOT} unused parameters = ${FRENSIE_ADD_TEST_EXECUTABLE_UNPARSED_ARGUMENTS}")
  ENDIF()

  ADD_EXECUTABLE(tst${TEST_NAME_ROOT} ${FRENSIE_ADD_TEST_EXECUTABLE_DEPENDS})
  TARGET_LINK_LIBRARIES(tst${TEST_NAME_ROOT} ${PACKAGE_LIBRARY})

  # Check if this is a boost test, link against the boost test library
  IF(${FRENSIE_ADD_TEST_EXECUTABLE_BOOST_TEST})
    IF(${BOOST_TESTING_ENABLED})
      TARGET_LINK_LIBRARIES(tst${TEST_NAME_ROOT} ${BOOST_TEST_LIB})
    ELSE()
      MESSAGE(FATAL "Cannot create ${TEST_NAME_ROOT} because boost tests have not been enabled for this package")
    ENDIF()
  ENDIF()

  # Link against the extra library dependencies
  IF(NOT "${FRENSIE_ADD_TEST_EXECUTABLE_LIB_DEPENDS}" STREQUAL "")
    TARGET_LINK_LIBRARIES(tst${TEST_NAME_ROOT} ${FRENSIE_ADD_TEST_EXECUTABLE_LIB_DEPENDS})
  ENDIF()

  # Add custom dependencies
  IF(NOT "${FRENSIE_ADD_TEST_EXECUTABLE_TARGET_DEPENDS}" STREQUAL "")
    ADD_DEPENDENCIES(tst${TEST_NAME_ROOT} ${FRENSIE_ADD_TEST_EXECUTABLE_TARGET_DEPENDS})
  ENDIF()

  # Add this test exec to the list of package test execs
  LIST(APPEND PACKAGE_TEST_EXECS tst${TEST_NAME_ROOT})
ENDMACRO(FRENSIE_ADD_TEST_EXECUTABLE)

##
MACRO(FRENSIE_ADD_TEST TEST_NAME_ROOT)
  SET(options VERBOSE_TEST_OUTPUT OPENMP_TEST)
  SET(oneValueArgs TEST_EXEC_NAME_ROOT MPI_PROCS)
  SET(multiValueArgs EXTRA_ARGS EXTRA_LABELS ACE_LIB_DEPENDS)
  CMAKE_PARSE_ARGUMENTS(FRENSIE_ADD_TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

  # Check for unused parameters
  IF(NOT "${FRENSIE_ADD_TEST_UNPARSED_ARGUMENTS}" STREQUAL "")
    MESSAGE(WARNING "FRENSIE_ADD_TEST ${TEST_NAME_ROOT} unused parameters = ${FRENSIE_ADD_TEST_UNPARSED_ARGUMENTS}")
  ENDIF()

  # If verbose test output has been requested, add the command line option for
  # maximum verbosity (works with both boost and FRENSIE tests)
  IF(${FRENSIE_ADD_TEST_VERBOSE_TEST_OUTPUT})
    LIST(APPEND FRENSIE_ADD_TEST_EXTRA_ARGS "--report_level=detailed")
  ENDIF()

  # Set the test labels
  SET(TEST_LABELS ${PACKAGE_LIBRARY})
  LIST(APPEND TEST_LABELS ${FRENSIE_ADD_TEST_EXTRA_LABELS})

  # Add the openmp label if this test has been specified as an openmp test
  IF(${FRENSIE_ADD_TEST_OPENMP_TEST})
    LIST(APPEND TEST_LABELS openmp)
  ENDIF()

  # Set the test name and preamble
  IF("${FRENSIE_ADD_TEST_MPI_PROCS}" STREQUAL "" OR
     "${FRENSIE_ADD_TEST_MPI_PROCS}" STREQUAL "1" )
    SET(TEST_NAME ${TEST_NAME_ROOT}_test)
    SET(TEST_PREAMBLE "")
  ELSE()
    SET(TEST_NAME ${TEST_NAME_ROOT}_${FRENSIE_ADD_TEST_MPI_PROCS}_test)
    SET(TEST_PREAMBLE ${MPIEXEC} ${MPIEXEC_FLAGS} ${MPIEXEC_NUMPROC_FLAG} ${FRENSIE_ADD_TEST_MPI_PROCS})
    LIST(APPEND TEST_LABELS mpi)
  ENDIF()

  # Set the test exec name
  IF(NOT "${FRENSIE_ADD_TEST_TEST_EXEC_NAME_ROOT}" STREQUAL "")
    SET(TEST_EXEC_NAME tst${FRENSIE_ADD_TEST_TEST_EXEC_NAME_ROOT})
  ELSE()
    SET(TEST_EXEC_NAME tst${TEST_NAME_ROOT})
  ENDIF()

  SET(UNAVAILABLE_ACE_LIB_DEPENDS ${FRENSIE_ADD_TEST_ACE_LIB_DEPENDS})

  IF(NOT "${FRENSIE_ADD_TEST_ACE_LIB_DEPENDS}" STREQUAL "")
    FOREACH(ACE_LIB_DEPEND ${FRENSIE_ADD_TEST_ACE_LIB_DEPENDS})
      IS_ACE_LIBRARY_AVAILABLE(${ACE_LIB_DEPEND} IS_LIB_AVAILABLE)

      IF(${IS_LIB_AVAILABLE})
        LIST(REMOVE_ITEM UNAVAILABLE_ACE_LIB_DEPENDS ${ACE_LIB_DEPEND})
      ENDIF()
    ENDFOREACH()
  ENDIF()

  LIST(LENGTH UNAVAILABLE_ACE_LIB_DEPENDS NUM_UNAVAILABLE_ACE_LIB_DEPENDS)

  IF(NUM_UNAVAILABLE_ACE_LIB_DEPENDS EQUAL "0")
    # Replace ACE placeholder arguments in the extra args with the
    # corresponding values
    IF(NOT "${FRENSIE_ADD_TEST_ACE_LIB_DEPENDS}" STREQUAL "")
      REPLACE_ACE_PLACEHOLDER_ARGS(FRENSIE_ADD_TEST_EXTRA_ARGS
        LIBRARY_NAMES ${FRENSIE_ADD_TEST_ACE_LIB_DEPENDS})
    ENDIF()
    
    # Add the test
    ADD_TEST(${TEST_NAME} ${TEST_PREAMBLE} ${TEST_EXEC_NAME} ${FRENSIE_ADD_TEST_EXTRA_ARGS})

    # Assign the test labels
    SET_PROPERTY(TEST ${TEST_NAME} PROPERTY LABELS ${TEST_LABELS})
  ELSE()
    STRING(REPLACE ";" ", " UNAVAILABLE_ACE_LIB_DEPENDS "${UNAVAILABLE_ACE_LIB_DEPENDS}")
    
    MESSAGE(WARNING "Test suite ${TEST_NAME} will be disabled because it requires the following unavailable ACE libraries: ${UNAVAILABLE_ACE_LIB_DEPENDS}.")
  ENDIF()

  # Cleanup
  UNSET(TEST_LABELS)
  UNSET(TEST_NAME)
  UNSET(TEST_PREAMBLE)
  UNSET(TEST_EXEC_NAME)
ENDMACRO(FRENSIE_ADD_TEST)

##
MACRO(FRENSIE_FINALIZE_PACKAGE_TESTS PACKAGE_NAME)
  # Add a custom build target for all package test execs
  ADD_CUSTOM_TARGET(${PACKAGE_NAME}_tests
    DEPENDS ${PACKAGE_TEST_EXECS})

  # Add a custom test target for all package tests
  ADD_CUSTOM_TARGET(test-${PACKAGE_NAME}
    COMMAND ctest -L ${PACKAGE_NAME}
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

  ADD_CUSTOM_TARGET(test-${PACKAGE_NAME}2
    COMMAND ctest -L ${PACKAGE_NAME} -j2
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

  ADD_CUSTOM_TARGET(test-${PACKAGE_NAME}4
    COMMAND ctest -L ${PACKAGE_NAME} -j4
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

  ADD_CUSTOM_TARGET(test-${PACKAGE_NAME}8
    COMMAND ctest -L ${PACKAGE_NAME} -j8
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

  ADD_CUSTOM_TARGET(test-${PACKAGE_NAME}12
    COMMAND ctest -L ${PACKAGE_NAME} -j12
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

  IF(${BOOST_TESTING_ENABLED})
    UNSET(BOOST_TEST_LIB)
  ENDIF()

  UNSET(BOOST_TESTING_ENABLED)
  UNSET(PACKAGE_LIBRARY)
  UNSET(PACKAGE_TEST_EXECS)
ENDMACRO(FRENSIE_FINALIZE_PACKAGE_TESTS)
