Skip to content

Commit 20a6907

Browse files
hjmjohnsonclaude
andcommitted
ENH: Auto-cleanup BigIO test outputs and enforce serial execution
Add ITK_REMOVE_BIGIO_FILES_ON_SUCCESS (advanced, default ON) that automatically removes multi-gigabyte BigIO test output files after successful completion using CTest fixtures. When a BigIO write-read test passes, a cleanup test runs to delete its output files. When a test fails, files are retained for debugging. Set the option to OFF to always retain output files. Additionally ensure all BigIO tests (>1 GB output) run serially via RUN_SERIAL to prevent concurrent disk exhaustion. New CMake function itk_add_bigio_test_cleanup() uses CTest FIXTURES to wire up SETUP (main test) and CLEANUP (file removal) phases. For .mhd files, companion .raw/.zraw files are also removed. Affected modules: - IO/Meta: LargeMetaImageWriteReadTest 1-4 (.mhd + .raw) - IO/TIFF: LargeTIFFImageWriteReadTest 1-4 (.tif) - IO/ImageBase: LargeImageWriteReadTest 2D/3D, WriteConvertRead (.mha) These 11 tests produce up to 47 GB of temporary files total. Without cleanup, they can exhaust disk space on CI runners (GitHub Actions ubuntu-22.04 has 146 GB total). An alternative approach (in-test C++ self-cleanup) was considered and rejected. See Documentation/docs/design/BigIO_test_cleanup_design.md for the full design rationale. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b94cca5 commit 20a6907

5 files changed

Lines changed: 199 additions & 56 deletions

File tree

CMake/ITKModuleTest.cmake

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,70 @@ function(itk_memcheck_ignore)
337337
${ARGN}
338338
)
339339
endfunction()
340+
341+
#-----------------------------------------------------------------------------
342+
# Option to automatically remove BigIO test output files on success.
343+
# BigIO write-read tests can produce multi-gigabyte temporary files
344+
# that exhaust disk space on CI runners and local builds.
345+
option(
346+
ITK_REMOVE_BIGIO_FILES_ON_SUCCESS
347+
"Remove BigIO test output files after successful completion"
348+
ON
349+
)
350+
mark_as_advanced(ITK_REMOVE_BIGIO_FILES_ON_SUCCESS)
351+
352+
#-----------------------------------------------------------------------------
353+
# itk_add_bigio_test_cleanup(<test_name> <output_file> [<output_file2> ...])
354+
#
355+
# Adds a cleanup test that removes the given output files after <test_name>
356+
# passes. Uses CTest fixtures: the main test is the SETUP, the cleanup
357+
# test is the CLEANUP. The cleanup test only runs after the main test
358+
# succeeds (CTest fixture semantics: CLEANUP runs after all REQUIRED tests,
359+
# but only if the fixture's SETUP passed).
360+
#
361+
# When ITK_REMOVE_BIGIO_FILES_ON_SUCCESS is OFF, no cleanup test is added
362+
# and the output files are retained for manual inspection.
363+
#
364+
function(itk_add_bigio_test_cleanup TEST_NAME)
365+
if(NOT ITK_REMOVE_BIGIO_FILES_ON_SUCCESS)
366+
return()
367+
endif()
368+
369+
set(FIXTURE_NAME "BigIO_${TEST_NAME}")
370+
371+
# Make the main test the SETUP of this fixture
372+
set_tests_properties(
373+
${TEST_NAME}
374+
PROPERTIES
375+
FIXTURES_SETUP
376+
${FIXTURE_NAME}
377+
)
378+
379+
# Build the removal command for all output files
380+
set(RM_ARGS "")
381+
foreach(OUTPUT_FILE ${ARGN})
382+
# For .mhd files, also remove the companion .raw/.zraw
383+
get_filename_component(EXT "${OUTPUT_FILE}" LAST_EXT)
384+
get_filename_component(NAME_WE "${OUTPUT_FILE}" NAME_WLE)
385+
get_filename_component(DIR "${OUTPUT_FILE}" DIRECTORY)
386+
list(APPEND RM_ARGS "${OUTPUT_FILE}")
387+
if("${EXT}" STREQUAL ".mhd")
388+
list(APPEND RM_ARGS "${DIR}/${NAME_WE}.raw")
389+
list(APPEND RM_ARGS "${DIR}/${NAME_WE}.zraw")
390+
endif()
391+
endforeach()
392+
393+
add_test(
394+
NAME ${TEST_NAME}_cleanup
395+
COMMAND
396+
${CMAKE_COMMAND} -E rm -f ${RM_ARGS}
397+
)
398+
set_tests_properties(
399+
${TEST_NAME}_cleanup
400+
PROPERTIES
401+
FIXTURES_CLEANUP
402+
${FIXTURE_NAME}
403+
LABELS
404+
"BigIO;CLEANUP"
405+
)
406+
endfunction()
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
BigIO Test Output Cleanup: Design Alternatives
2+
===============================================
3+
4+
## Problem
5+
6+
The 11 BigIO write-read tests across IO/Meta, IO/TIFF, and IO/ImageBase
7+
produce up to 47 GB of temporary files. On CI runners with limited disk
8+
(GitHub Actions ubuntu-22.04 has 146 GB total, ~89 GB available after
9+
build), these files can cause disk exhaustion and job cancellation.
10+
11+
## Chosen approach: CTest fixture cleanup (CMake-side)
12+
13+
A new CMake function `itk_add_bigio_test_cleanup()` uses CTest fixture
14+
semantics to wire up automatic file removal after successful test
15+
completion:
16+
17+
- The main test is registered as `FIXTURES_SETUP`
18+
- A cleanup test (`cmake -E rm -f`) is registered as `FIXTURES_CLEANUP`
19+
- CTest only runs CLEANUP when the SETUP test passes
20+
- On test failure, output files are retained for debugging
21+
- Controlled by advanced option `ITK_REMOVE_BIGIO_FILES_ON_SUCCESS`
22+
(default ON; set OFF to retain files for inspection)
23+
24+
For `.mhd` files, companion `.raw`/`.zraw` data files are also removed.
25+
26+
## Alternative considered: in-test self-cleanup (C++ side)
27+
28+
All 4 test source files follow the same pattern: write a large image,
29+
read it back, compare pixels, return EXIT_SUCCESS or EXIT_FAILURE.
30+
Adding `std::remove(filename.c_str())` before `return EXIT_SUCCESS`
31+
would be straightforward.
32+
33+
### Why this was rejected
34+
35+
1. **Multiple exit paths.** Each test has 2-4 `return EXIT_FAILURE`
36+
paths (catch blocks, pixel comparison failures). Cleanup must be
37+
placed only before success returns; an accidental placement before
38+
a failure return would destroy evidence needed for debugging.
39+
40+
2. **Format-dependent companion files.** `.mhd` writes produce two
41+
files (`.mhd` header + `.raw` data). Each test would need
42+
format-aware cleanup logic duplicated in C++. The CMake function
43+
handles this centrally.
44+
45+
3. **Reader lifetime concerns.** After `reader->Update()`, the
46+
reader and its IO object are still alive. On Windows, IO objects
47+
may hold file handles. Deleting files before these objects are
48+
destroyed could be platform-problematic.
49+
50+
4. **Separation of concerns.** The tests exist to verify IO
51+
correctness, not manage disk space. Mixing infrastructure cleanup
52+
into test logic makes test code harder to review and reason about.
53+
54+
5. **No runtime configurability.** Developers inspecting output files
55+
for debugging would need to modify and recompile the test source.
56+
The CMake option `ITK_REMOVE_BIGIO_FILES_ON_SUCCESS=OFF` provides
57+
that control without touching code.
58+
59+
6. **No C++17 filesystem dependency.** The CMake approach uses
60+
`cmake -E rm -f`, avoiding any need for `<filesystem>` headers
61+
or platform-specific removal APIs in test code.
62+
63+
### When in-test cleanup would be appropriate
64+
65+
If tests were run outside CTest (e.g., invoking the test driver binary
66+
directly), CTest fixtures would not execute and files would remain.
67+
An RAII guard pattern could handle this:
68+
69+
```cpp
70+
struct FileCleanupGuard {
71+
std::string path;
72+
bool disarm = false;
73+
~FileCleanupGuard() {
74+
if (!disarm) std::remove(path.c_str());
75+
}
76+
};
77+
```
78+
79+
However, ITK tests are designed to be run via CTest, and direct binary
80+
invocation is not the expected workflow for CI or automated testing.
81+
82+
## Serial execution requirement
83+
84+
All BigIO tests now set `RUN_SERIAL True` to prevent CTest from running
85+
multiple multi-gigabyte tests concurrently. Without this, `ctest -j3`
86+
could launch three 9 GB tests simultaneously, requiring ~27 GB of
87+
temporary disk space on top of regular test output.
88+
89+
The `RESOURCE_LOCK MEMORY_SIZE` property was already present but only
90+
serializes tests within the same resource group. `RUN_SERIAL` ensures
91+
no other test runs concurrently regardless of resource groups.

Modules/IO/ImageBase/test/CMakeLists.txt

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,6 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 16)
5252
${ITK_TEST_OUTPUT_DIR}/itkLargeImageWriteConvertReadTest.mha
5353
30000L
5454
)
55-
set_tests_properties(
56-
itkLargeImageWriteConvertReadTest
57-
PROPERTIES
58-
RESOURCE_LOCK
59-
MEMORY_SIZE
60-
)
6155

6256
itk_add_test(
6357
NAME itkLargeImageWriteReadTest_2D
@@ -67,12 +61,6 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 16)
6761
${ITK_TEST_OUTPUT_DIR}/itkLargeImageWriteReadTest_2D.mha
6862
30000L
6963
)
70-
set_tests_properties(
71-
itkLargeImageWriteReadTest_2D
72-
PROPERTIES
73-
RESOURCE_LOCK
74-
MEMORY_SIZE
75-
)
7664

7765
itk_add_test(
7866
NAME itkLargeImageWriteReadTest_3D
@@ -83,11 +71,30 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 16)
8371
30000L
8472
4L
8573
)
74+
75+
# BigIO tests produce multi-gigabyte files and must run serially
8676
set_tests_properties(
77+
itkLargeImageWriteConvertReadTest
78+
itkLargeImageWriteReadTest_2D
8779
itkLargeImageWriteReadTest_3D
8880
PROPERTIES
8981
RESOURCE_LOCK
9082
MEMORY_SIZE
83+
LABELS
84+
"BigIO;RUNS_LONG"
85+
RUN_SERIAL
86+
True
87+
)
88+
89+
# Clean up BigIO output files after successful completion
90+
itk_add_bigio_test_cleanup(
91+
itkLargeImageWriteConvertReadTest ${ITK_TEST_OUTPUT_DIR}/itkLargeImageWriteConvertReadTest.mha
92+
)
93+
itk_add_bigio_test_cleanup(
94+
itkLargeImageWriteReadTest_2D ${ITK_TEST_OUTPUT_DIR}/itkLargeImageWriteReadTest_2D.mha
95+
)
96+
itk_add_bigio_test_cleanup(
97+
itkLargeImageWriteReadTest_3D ${ITK_TEST_OUTPUT_DIR}/itkLargeImageWriteReadTest_3D.mha
9198
)
9299
endif()
93100

Modules/IO/Meta/test/CMakeLists.txt

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 5)
482482
50000L
483483
)
484484

485-
# Due to the large memory requirements this tests must be run one by one
485+
# Due to the large memory requirements these tests must be run one by one
486486
set_tests_properties(
487487
itkLargeMetaImageWriteReadTest1
488488
itkLargeMetaImageWriteReadTest2
@@ -494,6 +494,8 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 5)
494494
MEMORY_SIZE
495495
COST
496496
10
497+
RUN_SERIAL
498+
True
497499
)
498500
set_property(
499501
TEST
@@ -519,6 +521,10 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 5)
519521
LABELS
520522
RUNS_LONG
521523
)
524+
# Clean up BigIO output files after successful completion
525+
itk_add_bigio_test_cleanup(itkLargeMetaImageWriteReadTest1 ${ITK_TEST_OUTPUT_DIR}/LargeImage01.mhd)
526+
itk_add_bigio_test_cleanup(itkLargeMetaImageWriteReadTest2 ${ITK_TEST_OUTPUT_DIR}/LargeImage02.mhd)
527+
itk_add_bigio_test_cleanup(itkLargeMetaImageWriteReadTest3 ${ITK_TEST_OUTPUT_DIR}/LargeImage03.mhd)
522528
endif()
523529

524530
if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 12)
@@ -532,7 +538,7 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 12)
532538
70000L
533539
)
534540

535-
# Due to the large memory requirements this tests must be run one by one
541+
# Due to the large memory requirements this test must be run serially
536542
set_tests_properties(
537543
itkLargeMetaImageWriteReadTest4
538544
PROPERTIES
@@ -542,6 +548,8 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 12)
542548
MEMORY_SIZE
543549
COST
544550
30
551+
RUN_SERIAL
552+
True
545553
)
546554
set_property(
547555
TEST
@@ -551,12 +559,5 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 12)
551559
LABELS
552560
RUNS_LONG
553561
)
554-
set_property(
555-
TEST
556-
itkLargeMetaImageWriteReadTest4
557-
APPEND
558-
PROPERTY
559-
RUN_SERIAL
560-
True
561-
)
562+
itk_add_bigio_test_cleanup(itkLargeMetaImageWriteReadTest4 ${ITK_TEST_OUTPUT_DIR}/LargeImage04.mhd)
562563
endif()

Modules/IO/TIFF/test/CMakeLists.txt

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 5)
486486
50000L
487487
)
488488

489-
# Due to the large memory requirements this tests must be run one by one
489+
# Due to the large memory requirements these tests must be run one by one
490490
set_tests_properties(
491491
itkLargeTIFFImageWriteReadTest1
492492
itkLargeTIFFImageWriteReadTest2
@@ -496,6 +496,8 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 5)
496496
BigIO
497497
RESOURCE_LOCK
498498
MEMORY_SIZE
499+
RUN_SERIAL
500+
True
499501
)
500502
set_property(
501503
TEST
@@ -505,14 +507,6 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 5)
505507
LABELS
506508
RUNS_LONG
507509
)
508-
set_property(
509-
TEST
510-
itkLargeTIFFImageWriteReadTest1
511-
APPEND
512-
PROPERTY
513-
RUN_SERIAL
514-
True
515-
)
516510
set_property(
517511
TEST
518512
itkLargeTIFFImageWriteReadTest2
@@ -521,14 +515,6 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 5)
521515
LABELS
522516
RUNS_LONG
523517
)
524-
set_property(
525-
TEST
526-
itkLargeTIFFImageWriteReadTest2
527-
APPEND
528-
PROPERTY
529-
RUN_SERIAL
530-
True
531-
)
532518
set_property(
533519
TEST
534520
itkLargeTIFFImageWriteReadTest3
@@ -537,14 +523,10 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 5)
537523
LABELS
538524
RUNS_LONG
539525
)
540-
set_property(
541-
TEST
542-
itkLargeTIFFImageWriteReadTest3
543-
APPEND
544-
PROPERTY
545-
RUN_SERIAL
546-
True
547-
)
526+
# Clean up BigIO output files after successful completion
527+
itk_add_bigio_test_cleanup(itkLargeTIFFImageWriteReadTest1 ${ITK_TEST_OUTPUT_DIR}/LargeImage01.tif)
528+
itk_add_bigio_test_cleanup(itkLargeTIFFImageWriteReadTest2 ${ITK_TEST_OUTPUT_DIR}/LargeImage02.tif)
529+
itk_add_bigio_test_cleanup(itkLargeTIFFImageWriteReadTest3 ${ITK_TEST_OUTPUT_DIR}/LargeImage03.tif)
548530
endif()
549531

550532
if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 12)
@@ -558,7 +540,7 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 12)
558540
70000L
559541
)
560542

561-
# Due to the large memory requirements this tests must lock the memory size resource
543+
# Due to the large memory requirements this test must be run serially
562544
set_tests_properties(
563545
itkLargeTIFFImageWriteReadTest4
564546
PROPERTIES
@@ -568,6 +550,8 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 12)
568550
MEMORY_SIZE
569551
COST
570552
30
553+
RUN_SERIAL
554+
True
571555
)
572556
set_property(
573557
TEST
@@ -577,14 +561,7 @@ if("${ITK_COMPUTER_MEMORY_SIZE}" GREATER 12)
577561
LABELS
578562
RUNS_LONG
579563
)
580-
set_property(
581-
TEST
582-
itkLargeTIFFImageWriteReadTest4
583-
APPEND
584-
PROPERTY
585-
RUN_SERIAL
586-
True
587-
)
564+
itk_add_bigio_test_cleanup(itkLargeTIFFImageWriteReadTest4 ${ITK_TEST_OUTPUT_DIR}/LargeImage04.tif)
588565
endif()
589566

590567
# expand + RGB image

0 commit comments

Comments
 (0)