8000 Run dciodvfy conformance tests on macOS and Windows CI by fedorov · Pull Request #559 · QIICR/dcmqi · GitHub
[go: up one dir, main page]

Skip to content

Run dciodvfy conformance tests on macOS and Windows CI - #559

Merged
fedorov merged 2 commits into
masterfrom
ci/enable-dciodvfy-tests
Sep 2, 2026
Merged

fedorov merged 2 commits into
masterfrom
ci/enable-dciodvfy-tests

Conversation

@fedorov
@fedorov fedorov commented Sep 2, 2026
Copy link
Copy Markdown
Member

Problem

The dciodvfy conformance tests in apps/{seg,sr,paramaps}/Testing/CMakeLists.txt are
registered only if find_program(DCIODVFY_EXECUTABLE dciodvfy) succeeds. No CI job has ever
provided dciodvfy, so all seven have been silently skipped everywhere
, on every platform,
since they were added.

The one place dicom3tools was nominally provisioned — prereq.build_dicom3tools in
docker/Makefile — does not work:

  • the URL fetches dicom3tools_winexe_*.zip, i.e. prebuilt Cygwin Windows binaries, not source;
  • it extracts that .zip with tar xjf, which GNU tar rejects (macOS bsdtar happens to accept
    it, which is why this went unnoticed);
  • it then cds into a versioned directory the flat archive does not contain.

The whole && chain falls through to || echo "Reusing ", so the step reports success while
producing nothing.

These tests are a genuine gate, not a formality: dciodvfy exits non-zero whenever it prints an
Error. Checked against 19 DICOM files under data/, exit status tracked error count exactly.

Change

New composite action .github/actions/install-dicom3tools installs a prebuilt dicom3tools
distribution and prepends it to PATH. It is used from the macOS and Windows workflows, which
enables the seven tests on three jobs: macos-15-intel, macos-15, and windows-2022.

Design notes:

  • Digest pinning. The archive's SHA-256 is verified, not just the release tag. A GitHub
    release asset can be replaced in place under an unchanged tag, so pinning the tag alone does
    not pin the bytes CI executes.
  • Loud failure. The action smoke-tests that the binary can actually be loaded (exit >= 126
    means it could not be executed at all). Without this, an incompatible download would degrade
    straight back into the silent-skip behaviour this PR exists to fix.
  • Step placement. The install runs early because PATH must be set before the inner dcmqi
    project is configured, which happens during the build step, not the configure step, of the
    superbuild.
  • Registration guard. Each workflow asserts ctest -N -R dciodvfy finds a non-zero count
    before running the suite, so this cannot rot back into silence unnoticed.
  • Extraction uses 7z on Windows, as unzip is not present on the hosted Windows images.

Linux is not covered, deliberately

The published Linux binaries are built on ubuntu-22.04 and reference GLIBC_2.34 /
GLIBC_2.29:

libc.so.6 -> GLIBC_2.2.5, GLIBC_2.4, GLIBC_2.14, GLIBC_2.34
libm.so.6 -> GLIBC_2.2.5, GLIBC_2.29

dcmqi's Linux tests run inside deliberately old-glibc containers, to keep the glibc floor of the
shipped binaries low:

job environment glibc usable?
build-linux dockcross/manylinux2014-x64 2.17 no
build-linux-arm64 quay.io/pypa/manylinux_2_28_aarch64 2.28 no, and no aarch64 asset exists

Both are below the floor, so the binary cannot be loaded. Requested upstream in
QIICR/dicom3tools equivalent repo: ImagingDataCommons/dicom3tools#19. Once
manylinux-built Linux assets exist, enabling Linux is one added step per job plus a digest bump.

Both Linux jobs are commented with this reasoning, replacing a comment that understated the
situation, and the broken docker/Makefile target is documented in place.

Review notes

  • These jobs may go red on first run, and that is the point. I could not verify locally that
    all seven tests pass on current master — reproducing needs a DCMTK rebuild, since dcmqi now
    uses setBackgroundPixelValue / getBackgroundSegmentNumber. Given dciodvfy has never
    gated anything here, and known conformance gaps already exist in dcmqi output (see the
    labelmap exclusion note in apps/seg/Testing/CMakeLists.txt), a latent error in a
    non-labelmap path is plausible. Worth watching this PR's own CI run.
  • Untested edge: the Windows dicom3tools build is Cygwin-based, and it will receive
    Windows-style paths from ctest. Cygwin normally handles these, but it is not verified here.
  • docker/Makefile is left alone. Its broken dicom3tools target is an independent bug;
    repairing or removing it is worth a separate change, since even a fixed version cannot use the
    current prebuilt Linux release.
  • Supply chain: the action points at fedorov/dicom3tools, a personal repository. Digest
    pinning limits the blast radius, but if these binaries become a load-bearing CI dependency,
    moving the release to QIICR/dicom3tools — where the 2020 snapshot already lives — would be
    the more durable home.

🤖 Generated with Claude Code

The optional dciodvfy tests in apps/{seg,sr,paramaps}/Testing/CMakeLists.txt are
registered only when find_program(DCIODVFY_EXECUTABLE dciodvfy) succeeds, and no
CI job has ever provided dciodvfy, so all seven have been silently skipped
everywhere. They are a real gate: dciodvfy exits non-zero whenever it reports an
Error, so registering them turns DICOM conformance regressions into build
failures.

Add a composite action that installs a prebuilt dicom3tools distribution and
prepends it to PATH, and use it from the macOS and Windows workflows. The action
verifies the archive's SHA-256 rather than trusting the release tag alone, since
a release asset can be replaced in place under an unchanged tag, and smoke-tests
that the binary can actually be loaded so an incompatible download fails loudly
instead of degrading back into a silent skip. Each workflow also asserts that
ctest sees the dciodvfy tests before running the suite.

The install step is placed early because PATH must be set before the inner dcmqi
project is configured, which happens during the build step rather than the
configure step of the superbuild.

Both Linux jobs remain uncovered and are now commented with the reason: the
prebuilt Linux binaries are built on ubuntu-22.04 and reference GLIBC_2.34 /
GLIBC_2.29, while the Linux tests run in dockcross/manylinux2014-x64 (glibc 2.17)
and manylinux_2_28_aarch64 (glibc 2.28), and no aarch64 asset is published.
Tracked upstream in ImagingDataCommons/dicom3tools#19.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

url="https://github.com/$RELEASE_REPO/releases/download/$RELEASE_TAG/$asset"
echo "Downloading $url"
curl -fsSL --retry 3 --retry-delay 5 -o "$archive" "$url"
The Multi-frame Dimension module declared DimensionIndexPointer =
ImagePositionPatient (0020,0032) with FunctionalGroupPointer =
RealWorldValueMappingSequence (0040,9096). Per PS3.3 C.7.6.17.1 the functional
group pointer names the sequence that contains the attribute the dimension index
points at, and ImagePositionPatient lives in PlanePositionSequence (0020,9113),
which ParaMapConverter already writes per frame. RealWorldValueMappingSequence
does not contain it, so the reference resolved to nothing.

The SEG converter already gets this right (Itk2DicomConverter.cpp), which is why
only the parametric map outputs were affected.

Caught by the dciodvfy tests enabled in the preceding commit, which had never
actually run in CI:

  Warning - DimensionIndexPointer Attribute not present in any Shared or
            Per-Frame Functional Group - (0x0020,0x0032)

Despite the "Warning" wording this fails validation: the emitting check in
dciodvfy sets success = false, and dciodvfy exits 1 when success is false. Most
warnings do not affect its exit status, so the wording is misleading here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@fedorov
fedorov commented Sep 2, 2026
Copy link
Copy Markdown
Member Author

First CI run: 5 of 7 tests pass, 2 caught a real bug

macOS arm64 result: 97% tests passed, 2 tests failed out of 77. The three SEG and two SR
dciodvfy tests pass. Both failures are the parametric map ones, and they are a genuine
conformance defect, now fixed in 70589c3.

"Warning" here really does fail validation

The output contains no Error line, only warnings, which makes the failure look spurious:

7: Warning - DimensionIndexPointer Attribute not present in any Shared or Per-Frame Functional Group - (0x0020,0x0032)
7: Warning - Missing attribute or value that would be needed to build DICOMDIR - Study ID
7: Warning - Value dubious for this VR - (0x0070,0x0084) PN Content Creator's Name  PN [1] = <dcmqi> - Retired Person Name form
7: ParametricMap
7: Warning - Unrecognized defined term <GEIIS> for value 1 of attribute <Coding Scheme Designator>

It is not the case that dciodvfy treats warnings as errors in general. main ends with
return success ? 0 : 1, and most warning sites deliberately leave success alone:

  • the DICOMDIR warnings are called out in the source as "always only warnings ... do not
    affect success"
    ;
  • the unrecognized-defined-term warning is commented "does NOT cause failure (cf.
    EnumValues)"
    .

The first warning is the exception. Its emitting site in appsrc/dcfile/dciodvfy.cc
(checkDimensionIndexValuesArePresentInFunctionalGroupsSequence) prints a W-prefixed
message and then sets success = false immediately afterwards. So exactly one of these four
lines is fatal, and it is the one describing a real problem. The misleading severity wording
is a dicom3tools presentation issue, not a dcmqi one.

The underlying defect

ParaMapConverter.cpp declared the dimension index as:

mfdim.addDimensionIndex(DCM_ImagePositionPatient, dimUID,
                        DCM_RealWorldValueMappingSequence, "Frame position");

FunctionalGroupPointer must name the sequence that actually contains the attribute
DimensionIndexPointer refers to (PS3.3 C.7.6.17.1). ImagePositionPatient (0020,0032) is in
PlanePositionSequence (0020,9113), which ParaMapConverter already writes per frame via
FGPlanePosPatient. RealWorldValueMappingSequence (0040,9096) does not contain it, so
dciodvfy looked inside that sequence, found nothing, and correctly reported the dimension
index as dangling.

Itk2DicomConverter.cpp already passes DCM_PlanePositionSequence for the same attribute,
which is why only parametric map output was affected. Fixed by matching it.

No baseline or schema references the tag, so metadata round-trip comparisons are unaffected.

Note for reviewers

This is the payoff from these tests never having run: the defect has presumably been shipping
in every parametric map dcmqi has produced. Worth deciding whether it deserves a CHANGELOG
entry under Fixed when the release notes are next generated.

@sonarqubecloud
sonarqubecloud Bot commented Sep 2, 2026
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@fedorov
fedorov merged commit 5bacafe into master Sep 2, 2026
8 of 9 checks passed
@fedorov
fedorov deleted the ci/enable-dciodvfy-tests branch September 2, 2026 20:51
fedorov added a commit that referenced this pull request Sep 11, 2026
The dciodvfy tests for the three multiframe source variants derived both
the name of the test they depend on and the file they verify from one
loop variable, but the producing tests and their output files used
different suffixes: the tests are named after the full variant
(_2frames_per_plane, _shared_plane_position) while the files were
abbreviated (_2frames, _shared). Two of the three TEST_DEPENDS therefore
named tests that do not exist. CTest ignores an unknown DEPENDS silently,
so nothing failed - but under ctest -j the dciodvfy test could run before
the conversion that writes its input.

This stayed invisible because CI had no dciodvfy until #559/#561, which
install dicom3tools from PyPI on all platforms; the tests are created and
run there now.

Name the output files after the variants as well, so a single identifier
drives the test name, the file and the dependency.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0