# run_and_diff.cmake — shared ctest helper for the tests/ and examples/ smoke tests.
#
# (From cparse, MIT.) Runs one binary and checks its output against a fixture, without
# depending on GTest (these are plain add_test() entries so they register
# whenever EXAMPLE=ON, regardless of whether TESTING=ON/GTest was found —
# see the root CMakeLists.txt comment on enable_testing()).
#
# Required:
# EXE path to the built executable ($<TARGET_FILE:...>)
# EXPECTED file holding the expected text
# Optional:
# ARG single command-line argument (e.g. an input file path). Omit
# entirely (do not pass -DARG=) to run EXE with no arguments at
# all — e.g. to check a "missing argument" usage message.
# ARGS several command-line arguments, separated by '|' (a ';' would
# be split by add_test() itself). Appended after ARG.
# INPUT file piped to the executable's stdin
# MATCH if set, EXPECTED only needs to appear as a SUBSTRING of
# stdout+stderr (for a diagnostic message that also carries a
# machine-specific absolute path); otherwise stdout must equal
# EXPECTED exactly (leading/trailing whitespace ignored) and
# stderr must be empty.
# RC if set, the exit code must equal this value; otherwise the
# exit code is not checked.
if(NOT EXE)
message(FATAL_ERROR "run_and_diff.cmake: EXE not set")
endif()
if(NOT EXPECTED)
message(FATAL_ERROR "run_and_diff.cmake: EXPECTED not set")
endif()
set(_cmd "${EXE}")
if(ARG)
list(APPEND _cmd "${ARG}")
endif()
if(ARGS)
string(REPLACE "|" ";" _args "${ARGS}")
list(APPEND _cmd ${_args})
endif()
if(INPUT)
execute_process(
COMMAND ${_cmd}
INPUT_FILE "${INPUT}"
OUTPUT_VARIABLE actual_out
ERROR_VARIABLE actual_err
RESULT_VARIABLE rc
)
else()
execute_process(
COMMAND ${_cmd}
OUTPUT_VARIABLE actual_out
ERROR_VARIABLE actual_err
RESULT_VARIABLE rc
)
endif()
file(READ "${EXPECTED}" expected)
if(DEFINED RC AND NOT "${rc}" STREQUAL "${RC}")
message(FATAL_ERROR
"run_and_diff: exit code mismatch — expected ${RC}, got ${rc}.\n"
"--- actual stdout ---\n${actual_out}\n"
"--- actual stderr ---\n${actual_err}")
endif()
if(MATCH)
string(FIND "${actual_out}${actual_err}" "${expected}" pos)
if(pos EQUAL -1)
message(FATAL_ERROR
"run_and_diff: expected substring not found.\n"
"--- expected (substring) ---\n${expected}\n"
"--- actual stdout ---\n${actual_out}\n"
"--- actual stderr ---\n${actual_err}\n"
"--- exit code ---\n${rc}")
endif()
else()
string(STRIP "${actual_out}" actual_stripped)
string(STRIP "${expected}" expected_stripped)
string(STRIP "${actual_err}" err_stripped)
if(NOT actual_stripped STREQUAL expected_stripped)
message(FATAL_ERROR
"run_and_diff: stdout mismatch.\n"
"--- expected ---\n${expected}\n"
"--- actual ---\n${actual_out}\n"
"--- stderr ---\n${actual_err}\n"
"--- exit code ---\n${rc}")
endif()
if(NOT err_stripped STREQUAL "")
message(FATAL_ERROR "run_and_diff: expected no stderr, got:\n${actual_err}")
endif()
endif()