Skip to content

Commit b2a774f

Browse files
hjmjohnsonblowekampdzenanz
committed
COMP: Auto-remove transient single-owner test outputs in itk_add_test
Wire transient-output cleanup into every test whose ${ITK_TEST_OUTPUT_DIR}/ ${TEMP} outputs are owned by a single test. Each such test lists its outputs with an ITK_REMOVE_TEMPORARY_TEST_FILES <file>... argument to itk_add_test, which registers a FIXTURES_CLEANUP companion that removes them. Gated by the ITK_REMOVE_TEMPORARY_TEST_FILES option (ON by default); set OFF to retain outputs for debugging. The argument keeps each test's cleanup file list co-located with the itk_add_test call that produces them, rather than a separate itk_add_file_test_cleanup() companion call. It is the only parsed keyword, so it must appear last. The gersemi stub declares it so call sites format correctly. Detached-header outputs (.mhd, .nhdr) also remove their companion raw data files, and the cleanup command uses rm -rf so directory outputs (DICOM series, FileTools dirs) are removed as well as plain files. 1355 tests instrumented across 126 test/CMakeLists.txt. 42 multi-consumer shared outputs are excluded so the file persists for the consuming test. Co-authored-by: Bradley Lowekamp <blowekamp@mail.nih.gov> Co-authored-by: Dženan Zukić <dzenan.zukic@kitware.com>
1 parent a449d57 commit b2a774f

129 files changed

Lines changed: 3152 additions & 58 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CMake/ITKModuleTest.cmake

Lines changed: 52 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -101,21 +101,29 @@ endfunction()
101101
# to the value of its containing module.
102102
#
103103
function(itk_add_test)
104+
# Optional ITK_REMOVE_TEMPORARY_TEST_FILES <file>... lists the transient
105+
# outputs to remove after the test. It is the only parsed keyword, so it
106+
# must appear LAST: every argument following it is consumed as a file to
107+
# remove. Arguments preceding it form the add_test specification.
108+
cmake_parse_arguments(_iat "" "" "ITK_REMOVE_TEMPORARY_TEST_FILES" ${ARGN})
109+
set(_iat_args ${_iat_UNPARSED_ARGUMENTS})
110+
104111
# Add tests with data in the ITKData group.
105112
ExternalData_Add_Test(
106113
ITKData
107-
${ARGN}
114+
${_iat_args}
108115
)
109116

110-
if("NAME" STREQUAL "${ARGV0}")
111-
set(_iat_testname ${ARGV1})
117+
list(GET _iat_args 0 _iat_arg0)
118+
if("NAME" STREQUAL "${_iat_arg0}")
119+
list(GET _iat_args 1 _iat_testname)
112120
else()
113-
set(_iat_testname ${ARGV0})
121+
set(_iat_testname ${_iat_arg0})
114122
endif()
115123

116124
if(itk-module)
117125
set(_label ${itk-module})
118-
if(TARGET ${itk-module}-all AND "${ARGN}" MATCHES "DATA{")
126+
if(TARGET ${itk-module}-all AND "${_iat_args}" MATCHES "DATA{")
119127
add_dependencies(${itk-module}-all ITKData)
120128
endif()
121129
else()
@@ -129,6 +137,13 @@ function(itk_add_test)
129137
LABELS
130138
${_label}
131139
)
140+
141+
if(_iat_ITK_REMOVE_TEMPORARY_TEST_FILES)
142+
itk_add_file_test_cleanup(
143+
${_iat_testname}
144+
${_iat_ITK_REMOVE_TEMPORARY_TEST_FILES}
145+
)
146+
endif()
132147
endfunction()
133148

134149
#-----------------------------------------------------------------------------
@@ -343,15 +358,19 @@ function(itk_memcheck_ignore)
343358
endfunction()
344359

345360
#-----------------------------------------------------------------------------
346-
# Option to automatically remove large test output files after completion.
347-
# BigIO write-read tests can produce multi-gigabyte temporary files
348-
# that exhaust disk space on CI runners and local builds.
361+
# Removing transient test outputs is the default behavior: each wired test
362+
# gets a companion cleanup test that deletes its ${ITK_TEST_OUTPUT_DIR}
363+
# outputs, which otherwise accumulate (BigIO write-read tests alone produce
364+
# multi-gigabyte files that exhaust disk on CI runners and local builds).
365+
# Cleanup runs unconditionally after the test (pass, fail, or skip); a
366+
# developer who needs to inspect outputs must opt out by configuring with
367+
# -DITK_REMOVE_TEMPORARY_TEST_FILES=OFF, which retains all test outputs.
349368
option(
350-
ITK_REMOVE_TEST_FILES_ON_SUCCESS
351-
"Remove large test output files after test completion. Set OFF to retain files for debugging."
369+
ITK_REMOVE_TEMPORARY_TEST_FILES
370+
"Remove transient test outputs after each test (default ON; removed even on failure). Set OFF to retain all outputs for debugging."
352371
ON
353372
)
354-
mark_as_advanced(ITK_REMOVE_TEST_FILES_ON_SUCCESS)
373+
mark_as_advanced(ITK_REMOVE_TEMPORARY_TEST_FILES)
355374

356375
#-----------------------------------------------------------------------------
357376
# itk_add_file_test_cleanup(<test_name> <output_file> [<output_file2> ...])
@@ -363,19 +382,19 @@ mark_as_advanced(ITK_REMOVE_TEST_FILES_ON_SUCCESS)
363382
# NOTE: Per CMake documentation, FIXTURES_CLEANUP tests execute
364383
# unconditionally after their fixture — they run regardless of whether the
365384
# SETUP test passed, failed, or was skipped. To retain output files for
366-
# debugging after a failure, set ITK_REMOVE_TEST_FILES_ON_SUCCESS=OFF.
385+
# debugging after a failure, set ITK_REMOVE_TEMPORARY_TEST_FILES=OFF.
367386
#
368387
# NOTE: In a full unfiltered run all tests (including *_cleanup variants)
369388
# are in the active set and fixture ordering is respected. When rerunning
370389
# a single test with a strict filter (e.g. ctest -R "^<test_name>$"),
371390
# the *_cleanup test does not match; omit the anchors (ctest -R <test_name>)
372391
# so both the main test and its cleanup are selected.
373392
#
374-
# When ITK_REMOVE_TEST_FILES_ON_SUCCESS is OFF, no cleanup test is added
393+
# When ITK_REMOVE_TEMPORARY_TEST_FILES is OFF, no cleanup test is added
375394
# and the output files are retained for manual inspection.
376395
#
377396
function(itk_add_file_test_cleanup TEST_NAME)
378-
if(NOT ITK_REMOVE_TEST_FILES_ON_SUCCESS)
397+
if(NOT ITK_REMOVE_TEMPORARY_TEST_FILES)
379398
return()
380399
endif()
381400

@@ -389,31 +408,24 @@ function(itk_add_file_test_cleanup TEST_NAME)
389408
${FIXTURE_NAME}
390409
)
391410

392-
# Build the removal command for all output files
393-
set(RM_ARGS "")
394-
foreach(OUTPUT_FILE ${ARGN})
395-
# For .mhd files, also remove the companion .raw/.zraw
396-
get_filename_component(EXT "${OUTPUT_FILE}" LAST_EXT)
397-
get_filename_component(BASE_NAME "${OUTPUT_FILE}" NAME_WLE)
398-
get_filename_component(DIR "${OUTPUT_FILE}" DIRECTORY)
399-
list(APPEND RM_ARGS "${OUTPUT_FILE}")
400-
if("${EXT}" STREQUAL ".mhd")
401-
list(APPEND RM_ARGS "${DIR}/${BASE_NAME}.raw")
402-
list(APPEND RM_ARGS "${DIR}/${BASE_NAME}.zraw")
403-
endif()
404-
endforeach()
411+
# The developer supplies the exact files and glob patterns to remove. Each
412+
# entry is forwarded verbatim; itkRemoveTestFiles.cmake expands patterns at
413+
# test time and removes only the matching files (never directories).
414+
set(RM_ARGS ${ARGN})
405415

406-
add_test(
407-
NAME ${TEST_NAME}_cleanup
408-
COMMAND
409-
${CMAKE_COMMAND} -E rm -f ${RM_ARGS}
410-
)
411-
set_tests_properties(
412-
${TEST_NAME}_cleanup
413-
PROPERTIES
414-
FIXTURES_CLEANUP
415-
${FIXTURE_NAME}
416-
LABELS
417-
"CLEANUP"
418-
)
416+
if(RM_ARGS)
417+
add_test(
418+
NAME ${TEST_NAME}_cleanup
419+
COMMAND
420+
${CMAKE_COMMAND} -P ${ITK_CMAKE_DIR}/itkRemoveTestFiles.cmake ${RM_ARGS}
421+
)
422+
set_tests_properties(
423+
${TEST_NAME}_cleanup
424+
PROPERTIES
425+
FIXTURES_CLEANUP
426+
${FIXTURE_NAME}
427+
LABELS
428+
"CLEANUP"
429+
)
430+
endif()
419431
endfunction()

CMake/itkRemoveTestFiles.cmake

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Remove transient test output files matching the given path patterns.
2+
#
3+
# Invoked as a fixture cleanup command:
4+
# cmake -P itkRemoveTestFiles.cmake <pattern> [<pattern> ...]
5+
#
6+
# Each pattern is expanded with file(GLOB) at test time and only the matching
7+
# regular files are removed. Directories are never removed, so an unintended
8+
# pattern can delete individual files but can never recurse into a tree.
9+
#
10+
# A pattern may contain a single brace set "{a,b,c}" listing the extensions a
11+
# test actually writes for a shared stem, e.g. "foo.{mhd,raw}" expands to
12+
# "foo.mhd" and "foo.raw" before globbing.
13+
if(CMAKE_ARGC LESS 4)
14+
return()
15+
endif()
16+
17+
# Expand one "{a,b,c}" brace set in PATTERN into OUT_VAR (a list).
18+
function(_itk_expand_braces PATTERN OUT_VAR)
19+
string(FIND "${PATTERN}" "{" _open)
20+
string(FIND "${PATTERN}" "}" _close)
21+
if(_open EQUAL -1 OR _close EQUAL -1 OR _close LESS _open)
22+
set(${OUT_VAR} "${PATTERN}" PARENT_SCOPE)
23+
return()
24+
endif()
25+
string(SUBSTRING "${PATTERN}" 0 ${_open} _prefix)
26+
math(EXPR _glen "${_close} - ${_open} - 1")
27+
math(EXPR _gstart "${_open} + 1")
28+
string(SUBSTRING "${PATTERN}" ${_gstart} ${_glen} _group)
29+
math(EXPR _sstart "${_close} + 1")
30+
string(SUBSTRING "${PATTERN}" ${_sstart} -1 _suffix)
31+
string(REPLACE "," ";" _opts "${_group}")
32+
set(_result "")
33+
foreach(_o IN LISTS _opts)
34+
list(APPEND _result "${_prefix}${_o}${_suffix}")
35+
endforeach()
36+
set(${OUT_VAR} "${_result}" PARENT_SCOPE)
37+
endfunction()
38+
39+
math(EXPR _last "${CMAKE_ARGC} - 1")
40+
foreach(_i RANGE 3 ${_last})
41+
_itk_expand_braces("${CMAKE_ARGV${_i}}" _patterns)
42+
foreach(_pat IN LISTS _patterns)
43+
file(GLOB _matches "${_pat}")
44+
foreach(_match IN LISTS _matches)
45+
if(NOT IS_DIRECTORY "${_match}")
46+
file(REMOVE "${_match}")
47+
endif()
48+
endforeach()
49+
endforeach()
50+
endforeach()

CMake/stubs/itk_add_test.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ function(itk_add_test)
1212
ARG
1313
"COMMAND_EXPAND_LISTS"
1414
"NAME;WORKING_DIRECTORY"
15-
"COMMAND;CONFIGURATIONS"
15+
"COMMAND;CONFIGURATIONS;ITK_REMOVE_TEMPORARY_TEST_FILES"
1616
)
1717

1818
# This is a stub function to assist code formatting

Modules/Bridge/VtkGlue/test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ if(NOT VTK_RENDERING_BACKEND STREQUAL "None")
8282
QuickViewTest
8383
DATA{${ITK_DATA_ROOT}/Input/peppers.png}
8484
${ITK_TEST_OUTPUT_DIR}
85+
ITK_REMOVE_TEMPORARY_TEST_FILES
86+
${ITK_TEST_OUTPUT_DIR}/QuickViewTest0.png
8587
)
8688
set_property(
8789
TEST

0 commit comments

Comments
 (0)