compare_images() gains max_diff_percent and max_diff_pixels, an
opt-in image-level tolerance: a pixel difference up to the limit passes,
with match = TRUE and the new reason = "within-tolerance", while
diff_count and diff_percentage still record it. Images of different
sizes never pass. expect_images_match(), expect_snapshot_image() and
compare_file_odiff() have the same arguments, and the batch, directory,
PDF and snapshot_report() functions accept them through ....
Within-tolerance snapshots keep their original baseline. summary() and
batch_markdown() report how many passes were within tolerance,
approve_changes() skips them unless selected, and audit_record() has
columns for both parameters and warns when passes within tolerance are
recorded without the limits used.reason = "layout-diff"
on every odiff version, also with fail_on_layout = FALSE. With odiff >=
4.3.5 they were reported as "pixel-diff"; they keep their pixel counts,
which failure messages, JUnit output and print(summary()) now show.diff_percentage is now computed from diff_count rather than taken from
odiff's output, which is rounded to 2 decimal places. A handful of
differing pixels in a large image previously gave a failing comparison with
diff_percentage = 0.fail_on_layout = FALSE (the default for compare_images() and the
batch, directory and PDF functions), odiff < 4.3.5 only compares the area
that differently sized images share, so a larger new image with a small
content change was reported as a tiny pixel difference. Such comparisons
are now reported as reason = "layout-diff", as a size change with no
content change already was. odiff >= 4.3.5 counts the pixels outside the
shared area as different and is unaffected (previously odiffr applied its
size check up to odiff 4.5.0).<0.01% and >99.9%.compare_image_dirs() now reports files missing from current_dir as
failing rows with reason = "missing" instead of silently dropping them
(the warning is still emitted), so a disappearing screenshot now fails CI.compare_images_batch() (and therefore compare_image_dirs()) no longer
stops when a single pair fails, including a nonexistent path or a parallel
worker crash; that pair is reported as a reason = "error" row with the
message in the new error column. Empty input returns an empty
odiffr_batch, and malformed list input gives a clear error.diff_count and diff_percentage are now 0 (rather than NA) for
matching images. NA now means "unknown" (layout difference or error).stdout element of odiff_run() results now holds odiff's
machine-readable output (e.g. "126;1.26") rather than the human-readable
message, and stderr is now populated.expect_images_differ() now fails, rather than passes, when the images
cannot be compared.compare_image_dirs() pattern now matches .bmp (supported by
odiff) and no longer matches .tif (rejected by odiff; use .tiff).threshold, diff_color and diff_overlay are validated up front, so
invalid values (e.g. diff_color = "red", which odiff rejects) now error
in R. Small thresholds are no longer passed in scientific notation.install_odiff() downloads the odiff binary for your platform to the user
cache, so odiff can be installed from R without Node.js or npm. When odiff is
not found, functions that need it (e.g. compare_images()) now offer to run
install_odiff() in interactive sessions, once per session; nothing is ever
downloaded without asking, and the offer is never made in non-interactive
sessions, testthat runs, while knitting or during R CMD check. Set
options(odiffr.ask_install = FALSE) to turn it off. odiff_available()
remains a silent check. The startup message and the "odiff binary not found"
error now recommend install_odiff().use_odiffr_ci() writes a ready-to-use GitHub Actions workflow for visual
tests: it installs odiff with install_odiff(), runs the testthat tests,
adds a snapshot_report() summary of changed image snapshots to the job
summary and uploads new snapshots and diff images when tests fail.find_odiff() now bypasses the Node.js launcher script installed by
npm install -g odiff-bin (odiff >= 4.4) and calls the native odiff binary
directly, making each comparison around 5x faster. A path set with
options(odiffr.path) is used as-is, and options(odiffr.resolve_npm = FALSE)
disables the lookup. odiff_info() gains a shim field.compare_file_odiff() and expect_snapshot_image() now write a diff image
when a snapshot comparison fails (outside _snaps/, in tests/testthat/_odiffr/
by default) and report its location. Both gain a preset argument, and the
new odiff_preset() provides calibrated settings: "strict", "default",
"screenshot" (ignores anti-aliasing noise) and "cross_platform".
compare_file_odiff() works as the compare function of
shinytest2::AppDriver$expect_screenshot(); see
vignette("shinytest2", package = "odiffr").snapshot_report() builds an HTML, Markdown or JUnit report of changed image
snapshots (*.new.png files under _snaps/), for reviewing snapshot
failures in CI.compare_pdfs() compares two PDF files page by page, and compare_pdf_dirs()
compares directories of PDFs, returning batch results that work with
summary(), batch_report(), batch_markdown() and batch_junit().
Requires the pdftools package. See vignette("pdf-outputs", package = "odiffr").audit_record() writes a JSON or CSV record of comparisons, including
input and output file hashes, the odiff version and binary hash, parameters,
timestamp and platform. odiff_run() results gain a params element with
the effective comparison parameters.expect_snapshot_image() is a testthat snapshot expectation that compares
images with odiff, so baselines are managed with testthat::snapshot_review()
and testthat::snapshot_accept(). compare_file_odiff() returns the
underlying compare function for use with testthat::expect_snapshot_file().compare_images(), the testthat expectations and expect_snapshot_image()
accept plots as inputs: ggplot objects, functions that draw a plot, and
recorded plots. Plots are rendered to PNG (with ragg when installed);
rendering is controlled with the new plot_options().approve_changes() accepts current images as the new baselines for the
directory/batch workflow, with dry_run, backup_dir and optional removal
of baselines whose current image no longer exists.plot() method for odiff_result objects shows the baseline, current and
diff images side by side; diff_image() returns the diff image as a
magick image or raster.batch_report() and compare_dirs_report() gain images = "all" to show
baseline, current and diff thumbnails side by side.batch_markdown() writes a Markdown summary of batch results (appending to
the GitHub Actions job summary by default when run in GitHub Actions), and
batch_junit() writes JUnit XML for CI test reporting.odiff_run() gains an enable_asm parameter to enable AVX-512 optimised
assembly for ~12% faster comparisons on supported x86_64 CPUs. Requires
odiff >= 4.1.1.odiff_run() gains a diff_cols parameter (--output-diff-cols) that
returns the column numbers containing differences. Requires odiff >= 4.5.0.odiff_run() results gain an error element, and compare_images(),
compare_images_batch() and compare_image_dirs() results gain an error
column (last), holding odiff's error message when reason == "error".
Error messages are also shown by print(), in testthat failure messages and
in batch_report().reason = "error".odiff_run(diff_lines = TRUE) now returns correct diff_count,
diff_percentage and diff_lines. Previously the count and percentage were
NA and the line numbers included unrelated digits.--parsable-stdout and its machine-readable
output is parsed strictly. stdout and stderr are captured separately, so the
stderr element is now populated._R_CHECK_LIMIT_CORES_=false no longer limits parallel workers to 2.expect_images_match() uses deterministic diff file names, so re-runs
overwrite the previous diff instead of accumulating files in _odiffr/, and
a stale diff is removed once the expectation passes.diff_output path without an extension now gets .png appended
(previously odiff failed to write the diff).timeout values below one second are rounded up to one second instead of
silently disabling the timeout; 0 or Inf means no timeout. Timeouts are
reported via error.odiff_version() is cached per binary, so enable_asm/diff_cols no
longer spawn odiff --version on every comparison.batch_report(): image links are proper file:/// URIs (or
percent-encoded relative URLs), fixing broken images on Windows and for
paths containing spaces, #, ? or %. Rows without pixel statistics show
"-" instead of NA%, empty batches produce a valid report, reports are
written as UTF-8, the output directory is created if needed, and embedding
images is much faster.summary() of an empty batch returns pass_rate = NA instead of NaN.odiffr_update() accepts versions with or without the v prefix, uses
GITHUB_PAT/GITHUB_TOKEN for the GitHub API, never leaves a partial
binary behind after a failed download, gives a clearer error for releases
without binaries (e.g. v4.3.8, v4.4.0), and allows at least 300 seconds for
the download.odiff_version() now correctly parses the version from odiff --version
output instead of --help, which did not contain version information.batch_report() with relative_paths = TRUE now produces correct relative
paths on Windows by normalizing path separators before computing relative
paths.batch_report() gains a relative_paths parameter. When TRUE, image
paths in HTML reports are relative to the report location, making reports
portable without embedding images.compare_image_dirs() now emits a message when files in current_dir
have no corresponding baseline, helping catch missing or extra images early.failed_pairs() and passed_pairs() make it
easy to filter batch comparison results.embed = TRUE,
show_all = TRUE, and relative_paths = TRUE.compare_dirs_report() convenience function combines compare_image_dirs()
and batch_report() into a single call for the common QA workflow of
comparing two directories and generating an HTML report.batch_report(): Generate standalone HTML reports from batch comparison
results. Reports include pass/fail statistics, failure reason breakdown,
diff statistics, and thumbnails of worst offenders.embed = TRUE for self-contained reports
with base64-encoded images, or embed = FALSE (default) to link to files.show_all = TRUE.compare_image_dirs(): Compare all images in two directories by matching
relative paths. Baseline directory is source of truth; missing files in
current directory trigger warnings and are excluded from results.summary() method for batch results: Get aggregate statistics including
pass/fail counts, failure reason breakdown, diff statistics (min, median,
mean, max), and worst offenders ranked by diff percentage.compare_images_batch() and compare_image_dirs() now return objects with
class odiffr_batch for S3 method dispatch.parallel parameter for compare_images_batch() and compare_image_dirs():
Set parallel = TRUE to compare images using multiple CPU cores.parallel::mclapply on Unix systems (macOS, Linux) for faster batch
comparisons.expect_images_match(): Assert two images are visually identicalexpect_images_differ(): Assert two images are visually differenttests/testthat/_odiffr/ on failureoptions(odiffr.save_diff) and options(odiffr.diff_dir)Initial release.
compare_images(): High-level image comparison returning tibble/data.framecompare_images_batch(): Batch comparison of multiple image pairsodiff_run(): Low-level CLI wrapper with full option controlignore_region(): Helper for creating ignore region specificationsfind_odiff(): Locate Odiff binary with priority-based searchodiff_available(): Check if Odiff is availableodiff_version(): Get Odiff version stringodiff_info(): Display full configuration informationodiffr_update(): Download Odiff binary to user cache (fallback option)odiffr_cache_path(): Get cache directory pathodiffr_clear_cache(): Remove cached binariesRequires Odiff (>= 3.0.0) to be installed. Install via:
npm install -g odiff-binAlternatively, use odiffr_update() to download to user cache.
Works on any platform where Odiff is available: