#--------------------------------------------*-cmake-*---------------------------------------------#
# file   config/ApplicationUnitTest.cmake
# author Kelly Thompson <kgt@lanl.gov>
# date   Monday, Nov 19, 2012, 16:21 pm
# brief  Provide macros that aid in creating unit tests that run
#        interactive user codes (i.e.: run a binary that reads an
#        input file and diff the resulting output file).
# note   Copyright (C) 2016, Triad National Security, LLC.
#        All rights reserved.
#--------------------------------------------------------------------------------------------------#

# Reference: https://rtt.lanl.gov/redmine/projects/draco/wiki/CMake-based_ApplicationUnitTest
# Example: draco/src/diagnostics/test/tDracoInfo.cmake (and associated CMakeLists.txt).

#--------------------------------------------------------------------------------------------------#
## Example from draco/src/diagnostics/test/tDracoInfo.cmake
#--------------------------------------------------------------------------------------------------#

# Use config/ApplicationUnitTest.cmake test registration:
#
# include( ApplicationUnitTest )
# add_app_unit_test(
#   DRIVER ${CMAKE_CURRENT_SOURCE_DIR}/tDracoInfo.cmake
#   APP    $<TARGET_FILE_DIR:Exe_draco_info>/$<TARGET_FILE_NAME:Exe_draco_info>
#   LABELS nomemcheck )
#
# Optional Parameters:
#   GOLDFILE      - Compare the output from APP against this file.
#   STDINFILE     - APP expects interactive input, use data from this file.
#   WORKDIR       - APP must be run from this directory.
#   BUILDENV
#   FAIL_REGEX
#   LABELS        - E.g.: nomemcheck, nr, perfbench
#   PASS_REGEX
#   PE_LIST       - How may mpi ranks to use "1;2;4"
#   RESOURCE_LOCK - Prevent tests with the same string from running concurrently.
#   RUN_AFTER     - Run the named tests before this test is started.
#   TEST_ARGS     - optional papmeters that will be given to APP.

# The above will generate a test with data similar to this:
#
# add_test(
#   NAME ${ctestname_base}${argname}
#   COMMAND ${CMAKE_COMMAND}
#   -D APP=${aut_APP}
#   -D ARGVALUE=${argvalue}
#   -D WORKDIR=${aut_WORKDIR}
#   -D TESTNAME=${ctestname_base}${argname}
#   -D DRACO_CONFIG_DIR=${DRACO_CONFIG_DIR}
#   -D DRACO_INFO=$<TARGET_FILE_DIR:Exe_draco_info>/$<TARGET_FILE_NAME:Exe_draco_info>
#   -D STDINFILE=${aut_STDINFILE}
#   -D GOLDFILE=${aut_GOLDFILE}
#   -D RUN_CMD=${RUN_CMD}
#   -D numPE=${numPE}
#   -D PROJECT_BINARY_DIR=${PROJECT_BINARY_DIR}
#   -D PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
#   ${BUILDENV}
#   -P ${aut_DRIVER}
#   )
#    )
# set_tests_properties( diagnostics_draco_info
#    PROPERTIES
#      PASS_REGULAR_EXPRESSION Passes
#      FAIL_REGULAR_EXPRESSION Fails
#      LABELS nomemcheck
#    )

# Variables defined above can be used in this script.

#--------------------------------------------------------------------------------------------------#

include_guard(GLOBAL)
set( VERBOSE_DEBUG OFF )

function(JOIN VALUES GLUE OUTPUT)
  string (REGEX REPLACE "([^\\]|^);" "\\1${GLUE}" _TMP_STR "${VALUES}")
  string (REGEX REPLACE "[\\](.)" "\\1" _TMP_STR "${_TMP_STR}") #fixes escaping
  set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE)
endfunction()

# Helper for setting the depth (-d) option for aprun.  We want
# n*d==mpi_cores_per_cpu. For example:
# Trinity: 32 = 4 x 8
#             = 2 x 16, etc.
function(set_aprun_depth_flags numPE aprun_depth_options)
  math( EXPR depth     "${MPI_CORES_PER_CPU} / ${numPE}")
  math( EXPR remainder "${MPI_CORES_PER_CPU} % ${numPE}" )
  if( ${remainder} GREATER "0" )
    message(FATAL_ERROR
      "Expecting the requested number of ranks (${numPE}) to be a factor of the ranks/node (${MPI_CORES_PER_CPU})" )
  endif()
  set( aprun_depth_options "-d ${depth}" PARENT_SCOPE )
endfunction()

##---------------------------------------------------------------------------##
## Check values for $APP
##
## Requires:
##  APP        - name of executable that will be run
##
## Returns:
##   APP       - cleaned up path to executable
##   STDINFILE - cleaned up path to intput file.
##   BINDIR    - Directory location of binary file
##   PROJECT_BINARY_DIR - Parent directory of BINDIR
##   GOLDFILE  - cleaned up path to gold standard file.
##   OUTFILE   - Output filename derived from GOLDFILE.
##   ERRFILE   - Output(error) filename derived from GOLDFILE.
##
## if VERBOSE is set, also echo input values.
##   APP       - path name for executable.
##   STDINFILE - optional input file
##   GOLDFILE  - optional gold standard file.
##---------------------------------------------------------------------------##
macro( aut_setup )

  # Setup and sanity check
  if( "${APP}x" STREQUAL "x" )
    message( FATAL_ERROR "You must provide a value for APP." )
  endif()

  # Set paths...
  get_filename_component( APP ${APP} ABSOLUTE )
  if( STDINFILE )
    get_filename_component( STDINFILE ${STDINFILE} ABSOLUTE )
  endif()
  get_filename_component( BINDIR ${APP} PATH )
  # get_filename_component( PROJECT_BINARY_DIR ${BINDIR} PATH )
  if( GOLDFILE )
    get_filename_component( OUTFILE ${GOLDFILE} NAME_WE )
  else()
    get_filename_component( OUTFILE ${APP} NAME_WE )
  endif()
  set( ERRFILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTFILE}.err")
  set( OUTFILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTFILE}.out")

  if( NOT EXISTS "${APP}" )
    message( FATAL_ERROR "Cannot find ${APP}")
  else()
    message("Testing ${APP}")
  endif()

  set( numpasses 0 )
  set( numfails  0 )

  if( VERBOSE_DEBUG )
    message("Running with the following parameters:")
    message("   APP       = ${APP}
   BINDIR    = ${BINDIR}
   PROJECT_BINARY_DIR = ${PROJECT_BINARY_DIR}
   OUTFILE   = ${OUTFILE}
   ERRFILE   = ${ERRFILE}")
    if( STDINFILE )
      message("   STDINFILE = ${STDINFILE}")
    endif()
    if( GOLDFILE )
      message("   GOLDFILE = ${GOLDFILE}" )
    endif()
  endif()

  # Look for auxillary applications that are often used by
  # ApplicationUnitTest.cmake.
  find_program( exenumdiff numdiff )
  if( NOT EXISTS ${exenumdiff} )
    message( FATAL_ERROR "Numdiff not found in PATH")
  endif()
  if( VERBOSE_DEBUG )
    message("   exenumdiff = ${exenumdiff}" )
  endif()

endmacro()

##---------------------------------------------------------------------------##
## PASSMSG/FAILMSG
##---------------------------------------------------------------------------##

macro(PASSMSG msg)
  math( EXPR numpasses "${numpasses} + 1" )
  message( "Test Passes: ${msg}")
endmacro()

macro(FAILMSG msg)
  math( EXPR numfails "${numfails} + 1" )
  message( "Test Fails: ${msg}")
endmacro()

macro(ITFAILS)
  math( EXPR numfails "${numfails} + 1" )
endmacro()

##---------------------------------------------------------------------------##
## REGISTRATION
##---------------------------------------------------------------------------##

macro( aut_register_test )
  # Register the test...

  if( EXISTS ${Draco_SOURCE_DIR}/config/ApplicationUnitTest.cmake )
    # this is draco
    set( DRACO_CONFIG_DIR ${Draco_SOURCE_DIR}/config )
  endif()

  set(num_procs 1)
  if( numPE )
    set(num_procs ${numPE} )
  endif()
  string(REPLACE ";" " " RUN_CMD "${RUN_CMD}")

  if( VERBOSE_DEBUG )
    message("
  add_test(
    NAME ${ctestname_base}${argname}
    COMMAND ${CMAKE_COMMAND}
    -D APP=${aut_APP}
    -D ARGVALUE=${argvalue}
    -D WORKDIR=${aut_WORKDIR}
    -D TESTNAME=${ctestname_base}${argname}
    -D DRACO_CONFIG_DIR=${DRACO_CONFIG_DIR}
    -D DRACO_INFO=$<TARGET_FILE_DIR:Exe_draco_info>/$<TARGET_FILE_NAME:Exe_draco_info>
    -D STDINFILE=${aut_STDINFILE}
    -D GOLDFILE=${aut_GOLDFILE}
    -D RUN_CMD=${RUN_CMD}
    -D numPE=${numPE}
    -D MPIEXEC_EXECUTABLE=${MPIEXEC_EXECUTABLE}
    -D MPI_CORES_PER_CPU=${MPI_CORES_PER_CPU}
    -D SITENAME=${SITENAME}
    -D PROJECT_BINARY_DIR=${PROJECT_BINARY_DIR}
    -D PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
    ${BUILDENV}
    -P ${aut_DRIVER}
    )
  set_tests_properties( ${ctestname_base}${argname}
    PROPERTIES
      PASS_REGULAR_EXPRESSION \"${aut_PASS_REGEX}\"
      FAIL_REGULAR_EXPRESSION \"${aut_FAIL_REGEX}\"
      PROCESSORS              \"${num_procs}\"
    )")
    if( DEFINED aut_RESOURCE_LOCK )
      message("  set_tests_properties( ${ctestname_base}${argname}
      PROPERTIES RESOURCE_LOCK \"${aut_RESOURCE_LOCK}\" )" )
    endif()
    if( DEFINED aut_RUN_AFTER )
      message("  set_tests_properties( ${ctestname_base}${argname}
      PROPERTIES DEPENDS \"${aut_RUN_AFTER}\" )" )
    endif()
    if( DEFINED aut_LABELS )
      message("  set_tests_properties( ${ctestname_base}${argname}
      PROPERTIES LABELS \"${aut_LABELS}\" )" )
    endif()
  endif(VERBOSE_DEBUG)

  # Look for python, which is used to drive application unit tests
  if( NOT Python_Interpreter_FOUND )
     # python should have been found when vendor_libraries.cmake was run.
    message( FATAL_ERROR "Draco requires python. Python not found in PATH.")
  endif()

  # Check to see if driver file is python or CMake
  set(PYTHON_TEST TRUE)
  string(FIND ${aut_DRIVER} ".py" PYTHON_TEST)
  if (${PYTHON_TEST} EQUAL -1)
    set(PYTHON_TEST FALSE)
  endif ()

  # Set arguments that don't change between python and CMake driver
  set (SHARED_ARGUMENTS
      -DAPP=${aut_APP}
      -DARGVALUE=_av_${argvalue}_av_
      -DWORKDIR=${aut_WORKDIR}
      -DTESTNAME=${ctestname_base}${argname}
      -DDRACO_CONFIG_DIR=${DRACO_CONFIG_DIR}
      -DSTDINFILE=${aut_STDINFILE}
      -DGOLDFILE=${aut_GOLDFILE}
      -DRUN_CMD=${RUN_CMD}
      -DnumPE=${numPE}
      -D MPIEXEC_EXECUTABLE=${MPIEXEC_EXECUTABLE}
      -DMPI_CORES_PER_CPU=${MPI_CORES_PER_CPU}
      -DSITENAME=${SITENAME}
      -DPROJECT_BINARY_DIR=${PROJECT_BINARY_DIR}
      -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
      ${BUILDENV}
      )
  if( TARGET Exe_draco_info )
    list(APPEND SHARED_ARGUMENTS
      -DDRACO_INFO=$<TARGET_FILE_DIR:Exe_draco_info>/$<TARGET_FILE_NAME:Exe_draco_info> )
  endif()

  # Add python version if python driver file is specified

  if (${PYTHON_TEST})
    add_test(
      NAME ${ctestname_base}${argname}
      COMMAND "${Python_EXECUTABLE}"
      ${aut_DRIVER}
      ${SHARED_ARGUMENTS}
      )
  else ()
    add_test(
      NAME ${ctestname_base}${argname}
      COMMAND ${CMAKE_COMMAND}
      ${SHARED_ARGUMENTS}
      -P ${aut_DRIVER}
      )
  endif()

  set_tests_properties( ${ctestname_base}${argname}
    PROPERTIES
      PASS_REGULAR_EXPRESSION "${aut_PASS_REGEX}"
      FAIL_REGULAR_EXPRESSION "${aut_FAIL_REGEX}"
      PROCESSORS              "${num_procs}"
    )
  if( DEFINED aut_RESOURCE_LOCK )
    set_tests_properties( ${ctestname_base}${argname}
      PROPERTIES RESOURCE_LOCK "${aut_RESOURCE_LOCK}" )
  endif()
  if( DEFINED aut_RUN_AFTER )
    set_tests_properties( ${ctestname_base}${argname}
      PROPERTIES DEPENDS "${aut_RUN_AFTER}" )
  endif()
  if( DEFINED aut_LABELS )
    set_tests_properties( ${ctestname_base}${argname}
      PROPERTIES LABELS "${aut_LABELS}" )
  endif()

  unset(num_procs)
endmacro()

#--------------------------------------------------------------------------------------------------#
# See documentation at the top of this file.
macro( add_app_unit_test )

  # These become variables of the form ${aut_APP}, etc.
  cmake_parse_arguments(
    aut
    "NONE"
    "APP;DRIVER;GOLDFILE;STDINFILE;WORKDIR"
    "BUILDENV;FAIL_REGEX;LABELS;PASS_REGEX;PE_LIST;RESOURCE_LOCK;RUN_AFTER;TEST_ARGS"
    ${ARGV}
    )

  #
  # Check required intputs and set default vaules:
  #
  if( NOT EXISTS ${aut_DRIVER} )
    message( FATAL_ERROR "Could not find the cmake driver script = ${aut_DRIVER}." )
  endif()
  if( NOT DEFINED aut_APP )
    message( FATAL_ERROR "You must provide a value for APP." )
  endif()
  if( NOT DEFINED aut_WORKDIR )
    set( aut_WORKDIR ${CMAKE_CURRENT_BINARY_DIR} )
  endif()
  if( NOT DEFINED aut_PASS_REGEX )
    set( aut_PASS_REGEX "PASSED" )
  endif()
  if( NOT DEFINED aut_FAIL_REGEX )
    set( aut_FAIL_REGEX "FAILED;fails" )
  endif()
  if( DEFINED aut_GOLDFILE )
    if( NOT EXISTS ${aut_GOLDFILE} )
      message( FATAL_ERROR "File not found, GOLDFILE=${aut_GOLDFILE}.")
    endif()
  endif()
  if( DEFINED aut_STDINFILE )
    if( NOT EXISTS ${aut_STDINFILE} )
      message( FATAL_ERROR "File not found, STDINFILE=${aut_STDINFILE}.")
    endif()
  endif()

  # Load some information from the build environment:
  unset( RUN_CMD )

  if( DEFINED aut_PE_LIST AND ${DRACO_C4} MATCHES "MPI" )

    # Parallel tests
    if( "${MPIEXEC_EXECUTABLE}" MATCHES "aprun" )
      set( RUN_CMD "aprun -n" )
    else()
      set( RUN_CMD "${MPIEXEC_EXECUTABLE} ${MPIEXEC_PREFLAGS} ${MPIEXEC_NUMPROC_FLAG}")
    endif()

  else()

    # Scalar tests
    if( "${MPIEXEC_EXECUTABLE}" MATCHES "aprun" OR
        "${MPIEXEC_EXECUTABLE}" MATCHES "jsrun" OR
        "${MPIEXEC_EXECUTABLE}" MATCHES "srun" )
      set( RUN_CMD "${MPIEXEC_EXECUTABLE} ${MPIEXEC_PREFLAGS} ${MPIEXEC_NUMPROC_FLAG} 1" )
    endif()
  endif()

  # Prove extra build environment to the cmake-scripted unit test
  unset( BUILDENV )
  if( DEFINED aut_BUILDENV )
    # expect a semicolon delimited list of parameters.
    # FOO=myvalue1;BAR=myvalue2
    # translate this to -DF00=myvalue1 -DBAR=myvalue2
    foreach(item ${aut_BUILDENV} )
      list(APPEND BUILDENV "-D${item}" )
    endforeach()
  endif()

  # Create a name for the test
  get_filename_component( drivername   ${aut_DRIVER} NAME_WE )
  get_filename_component( package_name ${aut_DRIVER} PATH )
  get_filename_component( package_name ${package_name} PATH )
  get_filename_component( package_name ${package_name} NAME )
  set( ctestname_base ${package_name}_${drivername} )
  # Make the test name safe for regex
  string( REGEX REPLACE "[+]" "x" ctestname_base ${ctestname_base} )

  unset( argvalue )
  unset( argname  )
  if( DEFINED aut_TEST_ARGS )

    # Create a suffix for the testname and generate a string that can
    # be provided to the runTests macro.
    if( ${DRACO_C4} MATCHES "MPI" AND DEFINED aut_PE_LIST )
      foreach( numPE ${aut_PE_LIST} )
        foreach( argvalue ${aut_TEST_ARGS} )
          if( ${argvalue} STREQUAL "none" )
            set( argvalue "_${numPE}" )
          else()
            get_filename_component( safe_argvalue "${argvalue}" NAME )
            string( REGEX REPLACE "[-]" "" safe_argvalue ${safe_argvalue} )
            string( REGEX REPLACE "[ +.]" "_" safe_argvalue ${safe_argvalue} )
            set( argname "_${numPE}_${safe_argvalue}" )
          endif()
          # Register the test...
          if( VERBOSE_DEBUG )
            message("aut_register_test(${ctestname_base}${argname})")
          endif()
          aut_register_test()
        endforeach()
      endforeach()
    else()
      foreach( argvalue ${aut_TEST_ARGS} )
        if( ${argvalue} STREQUAL "none" )
          set( argvalue "" )
        else()
          get_filename_component( safe_argvalue "${argvalue}" NAME )
          string( REGEX REPLACE "[-]" "" safe_argvalue ${safe_argvalue} )
          string( REGEX REPLACE "[ +.]" "_" safe_argvalue ${safe_argvalue} )
          set( argname "_${safe_argvalue}" )
        endif()
        # Register the test...
        if( VERBOSE_DEBUG )
          message("aut_register_test(${ctestname_base}${argname})")
        endif()
        aut_register_test()
      endforeach()
    endif()

  else( DEFINED aut_TEST_ARGS )

    # Register the test...
    if( ${DRACO_C4} MATCHES "MPI" AND DEFINED aut_PE_LIST )
      foreach( numPE ${aut_PE_LIST} )
        set( argname "_${numPE}" )
        if( VERBOSE_DEBUG )
          message("aut_register_test(${ctestname_base}${argname})")
        endif()
        aut_register_test()
      endforeach()
    else()
      if( VERBOSE_DEBUG )
        message("aut_register_test(${ctestname_base})")
      endif()
      aut_register_test()
    endif()

  endif( DEFINED aut_TEST_ARGS )

  # cleanup
  unset( DRIVER )
  unset( APP )
  unset( STDINFILE )
  unset( GOLDFILE  )
  unset( TEST_ARGS )
  unset( numPE )

endmacro()

##---------------------------------------------------------------------------##
## Run the tests
##---------------------------------------------------------------------------##
macro( aut_runTests )

  # Run the application and capture the output.
  message("
=============================================
=== ${TESTNAME}
=============================================
")

  # Print version information
  separate_arguments(RUN_CMD)
  set( runcmd ${RUN_CMD} ) # plain string with spaces (used in Capsaicin)

  if( numPE )
    # Use 1 proc to run draco_info
    set( draco_info_numPE 1 )
  endif()
  if( EXISTS ${DRACO_INFO} )
    execute_process(
      COMMAND ${RUN_CMD} ${draco_info_numPE} ${DRACO_INFO} --version
      RESULT_VARIABLE testres
      OUTPUT_VARIABLE testout
      ERROR_VARIABLE  testerror
      )
    if( NOT ${testres} STREQUAL "0" )
      FAILMSG("Unable to run '${runcmd} ${draco_info_numPE} ${DRACO_INFO} --version'")
    else()
      message("${testout}")
    endif()
  endif()
  unset( draco_info_numPE )

  if( numPE )
    string( REPLACE ".out" "-${numPE}.out" OUTFILE ${OUTFILE} )
    string( REPLACE ".err" "-${numPE}.err" ERRFILE ${ERRFILE} )
  endif()
  string(REGEX REPLACE "_av_([A-z-]*)_av_" "\\1" ARGVALUE "${ARGVALUE}" )
  if( ARGVALUE )
    string( REGEX REPLACE "[-]" "" safe_argvalue ${ARGVALUE} )
    string( REPLACE ".out" "-${safe_argvalue}.out" OUTFILE ${OUTFILE} )
    string( REPLACE ".err" "-${safe_argvalue}.err" ERRFILE ${ERRFILE} )
  endif()

  if( DEFINED RUN_CMD )
    string( REPLACE ";" " " run_cmd_string "${RUN_CMD}" )
    message(">>> Running: ${run_cmd_string} ${numPE}")
    message(">>>          ${APP}")
    if( DEFINED ARGVALUE)
      message(">>>          ${ARGVALUE}")
    endif()
  else()
    message(">>> Running: ${APP} ${ARGVALUE}" )
  endif()
  if( EXISTS ${STDINFILE} )
    set( INPUT_FILE "INPUT_FILE ${STDINFILE}")
    message(">>>          < ${STDINFILE}")
  endif()
  message(">>>          > ${OUTFILE}
")

  # Run the application capturing all output.

  separate_arguments(INPUT_FILE)
  separate_arguments(ARGVALUE)

  execute_process(
    COMMAND ${RUN_CMD} ${numPE} ${APP} ${ARGVALUE}
    WORKING_DIRECTORY ${WORKDIR}
    ${INPUT_FILE}
    RESULT_VARIABLE testres
    OUTPUT_VARIABLE testout
    ERROR_VARIABLE  testerror
    )
#  unset(aprun_depth_options)

  # Convert the ARGVALUE from a list back into a space separated string.
  if( ARGVALUE )
    string( REGEX REPLACE ";" " " ARGVALUE ${ARGVALUE} )
  endif()

  if (FALSE)
  # This block was too slow for some Capsaicin tests

  # Capture all the output to log files:
  # - before we create the file, extract some lines that we want to
  #   exclude:
  #   1. Aprun inserts this line:
  #      "Application 12227386 resources: utime ~0s, stime ~0s, ..."

  # Preserve blank lines by settting the variable "Esc" to the ASCII
  # value 27 - basically something which is unlikely to conflict with
  # anything in the file contents.
  string(ASCII 27 Esc)
  string( REGEX REPLACE "\n" "${Esc};" testout "${testout}" )
  # string( REGEX REPLACE "\n" ";" testout ${testout} )
  unset( newout )
  foreach( line ${testout} )
    if( NOT line MATCHES "Application [0-9]* resources: utime" )
      # list( APPEND newout ${line} )
      string( REGEX REPLACE "${Esc}" "\n" line ${line} )
      set( newout "${newout}${line}" )
    endif()
  endforeach()
  # unset(testout)
  set( testout ${newout} )
  # join( "${newout}" "\n" testout )
  # string( REGEX REPLACE "${Esc};" "\n" testout ${testout} )
  endif()


  # now write the cleaned up file
  file( WRITE ${OUTFILE} "${testout}" )
  # [2015-07-28 KT] not sure we need to dump stderr values right now
  # file( WRITE ${ERRFILE} ${testerr} )

  # Ensure there are no errors
  if( NOT "${testres}" STREQUAL "0" )
    message( FATAL_ERROR "Test FAILED:"
      "last message written to stderr: '${testerror}"
      "See ${testout} for full details.")
  else()
    message("${testout}")
    PASSMSG("Application ran to completion.")
  endif()

endmacro()

##---------------------------------------------------------------------------##
## Set numdiff run command
##---------------------------------------------------------------------------##
function(set_numdiff_run_cmd RUN_CMD numdiff_run_cmd)
  if( DEFINED RUN_CMD )
    set(numdiff_run_cmd ${RUN_CMD})
    separate_arguments(numdiff_run_cmd)
    if( "${MPIEXEC_EXECUTABLE}" MATCHES "aprun" OR
        "${MPIEXEC_EXECUTABLE}" MATCHES "mpiexec" )
      # For Cray environments, let numdiff run on the login node.
      set(numdiff_run_cmd "")
    elseif( numPE )
      # Use 1 processor for srun, ssh, etc.
      set( numdiff_run_cmd "${numdiff_run_cmd};1" )
    endif()
  endif()
  set( numdiff_run_cmd "${numdiff_run_cmd}" PARENT_SCOPE )
endfunction()

##---------------------------------------------------------------------------##
## Run numdiff
##---------------------------------------------------------------------------##
macro( aut_numdiff )

  set_numdiff_run_cmd("${RUN_CMD}" numdiff_run_cmd)
  string( REPLACE ";" " " pretty_run_cmd "${numdiff_run_cmd}" )
  message("Comparing output to goldfile:
${pretty_run_cmd} ${exenumdiff} ${ARGV2} ${ARGV3} ${ARGV4} ${ARGV5} ${ARGV6} \\
   ${OUTFILE} \\
   ${GOLDFILE}")
  execute_process(
    COMMAND ${numdiff_run_cmd} ${exenumdiff} ${ARGV2} ${ARGV3} ${ARGV4} ${ARGV5} ${ARGV6} ${OUTFILE} ${GOLDFILE}
    RESULT_VARIABLE numdiffres
    OUTPUT_VARIABLE numdiffout
    ERROR_VARIABLE numdifferror
    )
  if( ${numdiffres} STREQUAL 0 )
    PASSMSG("gold matches out.
")
  else()
    FAILMSG("gold does not match out.
numdiff output = ${numdiffout}" )
  endif()
  unset( numdiff_run_cmd )

endmacro()

##---------------------------------------------------------------------------##
## Run numdiff given 2 files
##---------------------------------------------------------------------------##
macro( aut_numdiff_2files file1 file2 )

  # Sometimes we are looking at a shared filesystem from different nodes.  If a
  # file doesn't exist, touch the file.  If the touch creates the file, then
  # running numdiff should fail.
  if( NOT EXISTS ${file1} )
    execute_process( COMMAND "${CMAKE_COMMAND}" -E touch ${file1} )
    #FAILMSG( "Specified file1 = ${file1} does not exist." )
  endif()
  if( NOT EXISTS ${file2} )
    execute_process( COMMAND "${CMAKE_COMMAND}" -E touch ${file2} )
    # FAILMSG( "Specified file2 = ${file2} does not exist." )
  endif()

  set_numdiff_run_cmd("${RUN_CMD}" numdiff_run_cmd)
  string( REPLACE ";" " " pretty_run_cmd "${numdiff_run_cmd}" )
  message("
Comparing files:
${pretty_run_cmd} ${exenumdiff} ${ARGV2} ${ARGV3} ${ARGV4} ${ARGV5} ${ARGV6} \\
   ${file1} \\
   ${file2}")

  execute_process(
    COMMAND ${numdiff_run_cmd} ${exenumdiff} ${ARGV2} ${ARGV3} ${ARGV4} ${ARGV5} ${ARGV6} ${file1} ${file2}
    RESULT_VARIABLE numdiffres
    OUTPUT_VARIABLE numdiffout
    ERROR_VARIABLE  numdifferror
    )
  if( ${numdiffres} STREQUAL 0 )
    PASSMSG("files match!
")
  else()
    FAILMSG("files do not match.
numdiff output = ${numdiffout}" )
  endif()
  unset( numdiff_run_cmd )

endmacro()

##---------------------------------------------------------------------------##
## Run gdiff (Capsaicin)
##
## Usage:
##    aut_gdiff(input_file)
##
## GDIFF and possibly PGDIFF must be provided when registering the test:
##
## add_app_unit_test(
##  DRIVER    ${CMAKE_CURRENT_SOURCE_DIR}/tstAnaheim.cmake
##  APP       $<TARGET_FILE_DIR:Exe_anaheim>/$<TARGET_FILE_NAME:Exe_anaheim>
##  BUILDENV  "GDIFF=$<TARGET_FILE_DIR:Exe_gdiff>/$<TARGET_FILE_NAME:Exe_gdiff>;PGDIFF=$<TARGET_FILE_DIR:Exe_pgdiff>/$<TARGET_FILE_NAME:Exe_pgdiff>"
##
##---------------------------------------------------------------------------##
macro( aut_gdiff infile )

  # Sanity checks
  # 1. Must be able to find gdiff.  Location is specified via
  #    BUILDENV  "GDIFF=$<TARGET_FILE_DIR:Exe_gdiff>/$<TARGET_FILE_NAME:Exe_gdiff>"
  # 2. Input file ($1) must exist

  if( NOT EXISTS ${GDIFF} )
    FAILMSG( "Could not find gdiff!  Did you list it when registering this test?
GDIFF = ${GDIFF}" )
  endif()
  if( NOT EXISTS ${infile} )
    FAILMSG( "Could not find specified intput file (${infile})!
Did you list it when registering this test?" )
  endif()
  if( numPE )
    if( "${numPE}" GREATER "1" AND NOT EXISTS ${PGDIFF})
      FAILMSG( "If numPE > 1, you must provide the path to PGDIFF!")
    endif()
  endif()

  #----------------------------------------
  # Choose pgdiff or gdiff

  if( numPE AND "${numPE}" GREATER "1" )
    set( pgdiff_gdiff  ${RUN_CMD} ${numPE} ${PGDIFF} )
  else()
    # Use 1 proc to run gdiff
    set( pgdiff_gdiff ${RUN_CMD} 1 ${GDIFF} )
  endif()

  #----------------------------------------
  # Run GDIFF or PGDIFF

  separate_arguments(pgdiff_gdiff)
  # pretty print string
  string( REPLACE ";" " " pgdiff_gdiff_string "${pgdiff_gdiff}" )
  message("
Comparing output to goldfile via gdiff:
   ${pgdiff_gdiff_string} ${infile}
")
  execute_process(
    COMMAND ${pgdiff_gdiff} ${infile}
    RESULT_VARIABLE gdiffres
    OUTPUT_VARIABLE gdiffout
    ERROR_VARIABLE  gdifferror
    )
  if( ${gdiffres} STREQUAL 0 )
    PASSMSG("gdiff returned 0.")
  else()
    FAILMSG("gdiff returned non-zero.")
    message( "gdiff messages =
${gdiffout}" )
  endif()

  # should be no occurance of "FAILED"
  string(FIND "${gdiffout}" "FAILED" POS1)
  # should be  at least one occurance of "passed"
  string(FIND "${gdiffout}" "passed" POS2)

  if( ${POS1} GREATER 0 OR ${POS2} EQUAL 0 )
    # found failures or no passes.
    FAILMSG( "Failed identical file check ( ${POS1}, ${POS2} )." )
  else()
    PASSMSG( "Passed identical file check ( ${POS1}, ${POS2} )." )
  endif()

  unset( pgdiff_gdiff )

endmacro()

##---------------------------------------------------------------------------##
## Run the tests
##---------------------------------------------------------------------------##
macro( aut_report )

  message("
*****************************************************************")
  if( ${numpasses} GREATER 0 AND ${numfails} STREQUAL 0 )
    message("**** ${TESTNAME}: PASSED.")
  else()
    message("**** ${TESTNAME}: FAILED.")
  endif()
  message("*****************************************************************
")

endmacro()

##---------------------------------------------------------------------------##
## end
##---------------------------------------------------------------------------##
