added musrview png ref test via ctest
All checks were successful
Build and Deploy Documentation / build-and-deploy (push) Successful in 27s

This commit is contained in:
2026-02-18 17:07:20 +01:00
parent c50e4a3a06
commit ba939574a4
75 changed files with 269 additions and 0 deletions

View File

@@ -1,2 +1,3 @@
#--- musrfit integration tests ------------------------------------------------
add_subdirectory(maxLH_check)
add_subdirectory(musrview_check)

View File

@@ -0,0 +1,124 @@
#--- musrview PNG integration tests -------------------------------------------
find_package(Python3 REQUIRED COMPONENTS Interpreter)
#--- helper macro -------------------------------------------------------------
# add_musrview_test(test_name msr_file [extra_musrview_args...])
# msr_file is relative to ${CMAKE_SOURCE_DIR}/doc/examples.
# extra args (e.g. -f, -a) are forwarded directly to musrview.
macro(add_musrview_test test_name msr_file)
add_test(
NAME ${test_name}
COMMAND Python3::Interpreter
${CMAKE_CURRENT_SOURCE_DIR}/musrview_check.py
$<TARGET_FILE:musrview>
${CMAKE_SOURCE_DIR}/doc/examples/${msr_file}
${CMAKE_CURRENT_SOURCE_DIR}/ref
${test_name}
--tol 0.01
${ARGN}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/doc/examples
)
endmacro()
#--- doc/examples tests -------------------------------------------------------
# single histogram fits
add_musrview_test(musrview-histo-MusrRoot test-histo-MusrRoot.msr)
add_musrview_test(musrview-histo-MusrRoot-fourier test-histo-MusrRoot.msr -f)
add_musrview_test(musrview-histo-MusrRoot-fourier-avg test-histo-MusrRoot.msr -f -a)
add_musrview_test(musrview-histo-PSI-BIN test-histo-PSI-BIN.msr)
add_musrview_test(musrview-histo-PSI-BIN-fourier test-histo-PSI-BIN.msr -f)
add_musrview_test(musrview-histo-PSI-BIN-fourier-avg test-histo-PSI-BIN.msr -f -a)
add_musrview_test(musrview-histo-ROOT-NPP test-histo-ROOT-NPP.msr)
add_musrview_test(musrview-histo-ROOT-NPP-fourier test-histo-ROOT-NPP.msr -f)
add_musrview_test(musrview-histo-ROOT-NPP-fourier-avg test-histo-ROOT-NPP.msr -f -a)
add_musrview_test(musrview-histo-HAL9500 test-histo-HAL9500.msr)
add_musrview_test(musrview-histo-HAL9500-fourier test-histo-HAL9500.msr -f)
add_musrview_test(musrview-histo-HAL9500-fourier-avg test-histo-HAL9500.msr -f -a)
add_musrview_test(musrview-histo-HAL9500-RRF test-histo-HAL9500-RRF.msr)
add_musrview_test(musrview-histo-HAL9500-RRF-fourier test-histo-HAL9500-RRF.msr -f)
add_musrview_test(musrview-histo-HAL9500-RRF-fourier-avg test-histo-HAL9500-RRF.msr -f -a)
add_musrview_test(musrview-histo-muMinus test-histo-muMinus.msr)
add_musrview_test(musrview-histo-muMinus-fourier test-histo-muMinus.msr -f)
add_musrview_test(musrview-histo-muMinus-fourier-avg test-histo-muMinus.msr -f -a)
add_musrview_test(musrview-histo-NeXus test-histo-NeXus.msr)
add_musrview_test(musrview-histo-NeXus-fourier test-histo-NeXus.msr -f)
add_musrview_test(musrview-histo-NeXus-fourier-avg test-histo-NeXus.msr -f -a)
add_musrview_test(musrview-histo-NeXus2 test-histo-NeXus2.msr)
add_musrview_test(musrview-histo-NeXus2-fourier test-histo-NeXus2.msr -f)
add_musrview_test(musrview-histo-NeXus2-fourier-avg test-histo-NeXus2.msr -f -a)
# asymmetry fits
add_musrview_test(musrview-asy-PSI-BIN test-asy-PSI-BIN.msr)
add_musrview_test(musrview-asy-PSI-BIN-fourier test-asy-PSI-BIN.msr -f)
add_musrview_test(musrview-asy-PSI-BIN-fourier-avg test-asy-PSI-BIN.msr -f -a)
add_musrview_test(musrview-asy-MUD test-asy-MUD.msr)
add_musrview_test(musrview-asy-MUD-fourier test-asy-MUD.msr -f)
add_musrview_test(musrview-asy-MUD-fourier-avg test-asy-MUD.msr -f -a)
add_musrview_test(musrview-asy-NeXus2 test-asy-NeXus2.msr)
add_musrview_test(musrview-asy-NeXus2-fourier test-asy-NeXus2.msr -f)
add_musrview_test(musrview-asy-NeXus2-fourier-avg test-asy-NeXus2.msr -f -a)
add_musrview_test(musrview-asy-HAL9500-RRF test-asy-HAL9500-RRF.msr)
add_musrview_test(musrview-asy-HAL9500-RRF-fourier test-asy-HAL9500-RRF.msr -f)
add_musrview_test(musrview-asy-HAL9500-RRF-fourier-avg test-asy-HAL9500-RRF.msr -f -a)
add_musrview_test(musrview-asy-LF-BaB6 test-asy-LF-BaB6.msr)
add_musrview_test(musrview-asy-LF-BaB6-fourier test-asy-LF-BaB6.msr -f)
add_musrview_test(musrview-asy-LF-BaB6-fourier-avg test-asy-LF-BaB6.msr -f -a)
#--- doc/examples/ViewOpts tests ----------------------------------------------
add_musrview_test(musrview-asy-PSI-BIN-range ViewOpts/test-asy-PSI-BIN-range.msr)
add_musrview_test(musrview-asy-PSI-BIN-range-fourier ViewOpts/test-asy-PSI-BIN-range.msr -f)
add_musrview_test(musrview-asy-PSI-BIN-range-fourier-avg ViewOpts/test-asy-PSI-BIN-range.msr -f -a)
add_musrview_test(musrview-asy-PSI-BIN-use_fit_ranges ViewOpts/test-asy-PSI-BIN-use_fit_ranges.msr)
add_musrview_test(musrview-asy-PSI-BIN-use_fit_ranges-fourier ViewOpts/test-asy-PSI-BIN-use_fit_ranges.msr -f)
add_musrview_test(musrview-asy-PSI-BIN-use_fit_ranges-fourier-avg ViewOpts/test-asy-PSI-BIN-use_fit_ranges.msr -f -a)
add_musrview_test(musrview-histo-PSI-BIN-no-scale-range-lc ViewOpts/test-histo-PSI-BIN-no-scale-range-lifetimecorrection.msr)
add_musrview_test(musrview-histo-PSI-BIN-no-scale-range-lc-fourier ViewOpts/test-histo-PSI-BIN-no-scale-range-lifetimecorrection.msr -f)
add_musrview_test(musrview-histo-PSI-BIN-no-scale-range-lc-fourier-avg ViewOpts/test-histo-PSI-BIN-no-scale-range-lifetimecorrection.msr -f -a)
add_musrview_test(musrview-histo-PSI-BIN-no-scale-range-no-lc ViewOpts/test-histo-PSI-BIN-no-scale-range-no-lifetimecorrection.msr)
add_musrview_test(musrview-histo-PSI-BIN-no-scale-range-no-lc-fourier ViewOpts/test-histo-PSI-BIN-no-scale-range-no-lifetimecorrection.msr -f)
add_musrview_test(musrview-histo-PSI-BIN-no-scale-range-no-lc-fourier-avg ViewOpts/test-histo-PSI-BIN-no-scale-range-no-lifetimecorrection.msr -f -a)
add_musrview_test(musrview-histo-PSI-BIN-scale-fitRangeBin-range-lc ViewOpts/test-histo-PSI-BIN-scale-fitRangeBin-range-lifetimecorrection.msr)
add_musrview_test(musrview-histo-PSI-BIN-scale-fitRangeBin-range-lc-fourier ViewOpts/test-histo-PSI-BIN-scale-fitRangeBin-range-lifetimecorrection.msr -f)
add_musrview_test(musrview-histo-PSI-BIN-scale-fitRangeBin-range-lc-fourier-avg ViewOpts/test-histo-PSI-BIN-scale-fitRangeBin-range-lifetimecorrection.msr -f -a)
add_musrview_test(musrview-histo-PSI-BIN-scale-fitRangeBin-ufr-lc ViewOpts/test-histo-PSI-BIN-scale-fitRangeBin-use_fit_ranges-lifetimecorrection.msr)
add_musrview_test(musrview-histo-PSI-BIN-scale-fitRangeBin-ufr-lc-fourier ViewOpts/test-histo-PSI-BIN-scale-fitRangeBin-use_fit_ranges-lifetimecorrection.msr -f)
add_musrview_test(musrview-histo-PSI-BIN-scale-fitRangeBin-ufr-lc-fourier-avg ViewOpts/test-histo-PSI-BIN-scale-fitRangeBin-use_fit_ranges-lifetimecorrection.msr -f -a)
add_musrview_test(musrview-histo-PSI-BIN-scale-range-lc ViewOpts/test-histo-PSI-BIN-scale-range-lifetimecorrection.msr)
add_musrview_test(musrview-histo-PSI-BIN-scale-range-lc-fourier ViewOpts/test-histo-PSI-BIN-scale-range-lifetimecorrection.msr -f)
add_musrview_test(musrview-histo-PSI-BIN-scale-range-lc-fourier-avg ViewOpts/test-histo-PSI-BIN-scale-range-lifetimecorrection.msr -f -a)
add_musrview_test(musrview-histo-PSI-BIN-scale-range-no-lc ViewOpts/test-histo-PSI-BIN-scale-range-no-lifetimecorrection.msr)
add_musrview_test(musrview-histo-PSI-BIN-scale-range-no-lc-fourier ViewOpts/test-histo-PSI-BIN-scale-range-no-lifetimecorrection.msr -f)
add_musrview_test(musrview-histo-PSI-BIN-scale-range-no-lc-fourier-avg ViewOpts/test-histo-PSI-BIN-scale-range-no-lifetimecorrection.msr -f -a)
add_musrview_test(musrview-histo-PSI-BIN-scale-sub_ranges-lc ViewOpts/test-histo-PSI-BIN-scale-sub_ranges-lifetimecorrection.msr)
add_musrview_test(musrview-histo-PSI-BIN-scale-sub_ranges-lc-fourier ViewOpts/test-histo-PSI-BIN-scale-sub_ranges-lifetimecorrection.msr -f)
add_musrview_test(musrview-histo-PSI-BIN-scale-sub_ranges-lc-fourier-avg ViewOpts/test-histo-PSI-BIN-scale-sub_ranges-lifetimecorrection.msr -f -a)
add_musrview_test(musrview-histo-PSI-BIN-scale-ufr-lc ViewOpts/test-histo-PSI-BIN-scale-use_fit_ranges-lifetimecorrection.msr)
add_musrview_test(musrview-histo-PSI-BIN-scale-ufr-lc-fourier ViewOpts/test-histo-PSI-BIN-scale-use_fit_ranges-lifetimecorrection.msr -f)
add_musrview_test(musrview-histo-PSI-BIN-scale-ufr-lc-fourier-avg ViewOpts/test-histo-PSI-BIN-scale-use_fit_ranges-lifetimecorrection.msr -f -a)
add_musrview_test(musrview-histo-PSI-BIN-scale-ufr-no-lc ViewOpts/test-histo-PSI-BIN-scale-use_fit_ranges-no-lifetimecorrection.msr)
add_musrview_test(musrview-histo-PSI-BIN-scale-ufr-no-lc-fourier ViewOpts/test-histo-PSI-BIN-scale-use_fit_ranges-no-lifetimecorrection.msr -f)
add_musrview_test(musrview-histo-PSI-BIN-scale-ufr-no-lc-fourier-avg ViewOpts/test-histo-PSI-BIN-scale-use_fit_ranges-no-lifetimecorrection.msr -f -a)

View File

@@ -0,0 +1,144 @@
#!/usr/bin/env python3
"""
Runs musrview --png on a given msr-file, then compares the generated PNGs
against reference images using pixel-level comparison (Pillow).
Usage:
musrview_check.py <musrview> <msr-file> <ref-dir> <test-name>
[--tol T] [--generate] [musrview-opts...]
Modes:
default Compare generated PNGs against references in <ref-dir>/<test-name>/
--generate Generate reference PNGs into <ref-dir>/<test-name>/ (no comparison)
Tolerance metric: mean absolute pixel difference normalised to [0, 1].
0.0 = identical, 1.0 = maximally different. Default tolerance: 0.01 (~1%).
"""
import argparse
import glob
import os
import shutil
import subprocess
import sys
import tempfile
def pixel_diff(img_a_path, img_b_path):
"""Return the mean absolute pixel difference normalised to [0, 1]."""
from PIL import Image
import numpy as np
a = np.asarray(Image.open(img_a_path).convert("RGBA"), dtype=np.float64)
b = np.asarray(Image.open(img_b_path).convert("RGBA"), dtype=np.float64)
if a.shape != b.shape:
return 1.0 # completely different dimensions
return np.mean(np.abs(a - b)) / 255.0
def main():
# ---- argument parsing ----------------------------------------------------
parser = argparse.ArgumentParser(description="musrview PNG integration test")
parser.add_argument("musrview", help="path to musrview executable")
parser.add_argument("msr_file", help="path to msr input file")
parser.add_argument("ref_dir", help="root reference directory")
parser.add_argument("test_name", help="test name (subdirectory in ref_dir)")
parser.add_argument("--tol", type=float, default=0.01,
help="tolerance for pixel comparison (default 0.01)")
parser.add_argument("--generate", action="store_true",
help="generate reference PNGs instead of comparing")
# everything after the known args is forwarded to musrview
args, musrview_opts = parser.parse_known_args()
msr_basename = os.path.splitext(os.path.basename(args.msr_file))[0]
ref_subdir = os.path.join(args.ref_dir, args.test_name)
# ---- run musrview in a temporary directory -------------------------------
work_dir = os.path.dirname(os.path.abspath(args.msr_file))
with tempfile.TemporaryDirectory() as tmp_dir:
# snapshot existing PNGs so we only pick up newly created ones
pre_existing = set(glob.glob(os.path.join(work_dir, f"{msr_basename}_*.png")))
cmd = [args.musrview, args.msr_file, "--png"] + musrview_opts
print(f"running: {' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True,
cwd=work_dir,
env={**os.environ, "MUSRVIEW_PNG_DIR": tmp_dir})
if result.returncode != 0:
print(f"**ERROR** musrview returned exit code {result.returncode}")
print(result.stdout + result.stderr)
return 1
# collect only newly created PNGs (exclude pre-existing ones)
generated = sorted(
p for p in glob.glob(os.path.join(work_dir, f"{msr_basename}_*.png"))
if p not in pre_existing
)
if not generated:
generated = sorted(glob.glob(os.path.join(tmp_dir, f"{msr_basename}_*.png")))
if not generated:
print(f"**ERROR** no PNGs matching '{msr_basename}_*.png' found")
print(f" checked: {work_dir}")
if result.stdout:
print(result.stdout)
return 1
# ---- generate mode ---------------------------------------------------
if args.generate:
os.makedirs(ref_subdir, exist_ok=True)
for png in generated:
dst = os.path.join(ref_subdir, os.path.basename(png))
shutil.copy2(png, dst)
print(f" saved reference: {dst}")
# clean up generated PNGs from work_dir
for png in generated:
if os.path.dirname(os.path.abspath(png)) == os.path.abspath(work_dir):
os.remove(png)
print(f"GENERATE: {len(generated)} reference PNG(s) written to {ref_subdir}")
return 0
# ---- compare mode ----------------------------------------------------
if not os.path.isdir(ref_subdir):
print(f"**ERROR** reference directory not found: {ref_subdir}")
return 1
failures = 0
compared = 0
for png_path in generated:
name = os.path.basename(png_path)
ref_path = os.path.join(ref_subdir, name)
if not os.path.isfile(ref_path):
print(f"FAIL: no reference PNG for {name}")
failures += 1
continue
diff = pixel_diff(png_path, ref_path)
compared += 1
if diff > args.tol:
print(f"FAIL: {name} diff={diff:.6f} > tol={args.tol:.6f}")
failures += 1
else:
print(f"PASS: {name} diff={diff:.6f} <= tol={args.tol:.6f}")
# clean up generated PNGs from work_dir
for png in generated:
if os.path.dirname(os.path.abspath(png)) == os.path.abspath(work_dir):
os.remove(png)
if compared == 0:
print("**ERROR** no PNGs were compared")
return 1
if failures:
print(f"\n{failures} of {len(generated)} PNG(s) FAILED")
return 1
print(f"\nAll {compared} PNG(s) PASSED (tol={args.tol})")
return 0
if __name__ == "__main__":
sys.exit(main())

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB