Add unit tests for the plotting functions; fix scales >= 1.3.0 compatibility - #49
Conversation
…ibility None of the package's plot functions render to a graphics device -- they build and return a ggplot object (or, for overlay_peaklist(), a list of ggplot2 layers) via + composition, so they can be unit-tested like any other data structure by inspecting the returned object's data, labels, scales and layer classes, without snapshot/visual-regression tooling. Covers: mat_to_nativeRaster() (pure matrix-to-raster encoding, including the zero-length edge case), plot_interactive(), cubic_root_trans(), as.data.frame.GCIMSSample(), plot() for GCIMSSample/GCIMSChromatogram/ GCIMSSpectrum (labels, data, axis ranges, the single/multi/empty-value subtitle branches), and overlay_peaklist() across its color_by, apex, palette-recycling and pdata-merging branches, plus its two validation errors. Brings plot-GCIMSSample.R, plot-GCIMSChromatogram.R, plot-GCIMSSpectrum.R and utils-plot.R to 100% line coverage. While writing these tests, found that mat_to_gplot() checked inherits(trans, "trans") to validate a transform object passed directly to plot()'s trans= argument (as opposed to a string). scales >= 1.3.0 renamed this S3 class from "trans" to "transform", so passing any transform object -- including the package's own cubic_root_trans() -- this way always failed with "unknown trans value", even though the roxygen docs explicitly point to this as valid usage. Fixed the check to match the class scales has used since 1.3.0 (already over a year old), and bumped the scales dependency in DESCRIPTION to >= 1.4.0 accordingly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019V3CHRGSzcEu3k6tpUFv56
|
On this sprint we've gone from testing 23% of the code lines to 86% of the code lines. A higher test coverage helps to understand if something is breaking because of a dependency upgrade or because an unintended regression in a recent code change. It also gives confidence when making a change to see that the change we are making doesn't break the code we have so far. It also helps detecting issues in less visited code paths, such as error handling or corner features. During this process we have fixed a dozen bugs. One of these bugs prevents spurious peaks from appearing in the peak table, before I remember we were filtering them out manually. Another bug fixed the saturation column in the peak lists. This column was not used in downstream analysis, but still it is a useful QC metric. The other 10 fixed bugs would not be triggered usually, but still it's worth having them fixed. |
Summary
None of this package's plot functions render to a graphics device — they build and return a
ggplotobject (or, foroverlay_peaklist(), a list ofggplot2layers) via+composition. That means they can be unit-tested like any other data structure, by inspecting the returned object's data/labels/scales/layer classes, without snapshot/visual-regression tooling (e.g.vdiffr).mat_to_nativeRaster()(utils-plot.R): pure matrix→raster encoding — shape/class/channels attributes, determinism, character vs. pre-encoded colormap, and the zero-length edge caseplot_interactive(): wraps a ggplot inplotly::ggplotly(), checked it returns a"plotly"objectcubic_root_trans(): transform/inverse round-trip (including negative values, since it's a signed cube root) and thebreaks()empty/non-finite edge caseas.data.frame.GCIMSSample(): melts the intensity matrix, checked columns/values,dt_range/row.namesplot()onGCIMSSample/GCIMSChromatogram/GCIMSSpectrum: labels, data, axis-range restriction, theremove_baselinebranch, and the single-value/multi-value/empty-value subtitle branchesoverlay_peaklist(): thecolor_by(none/literal color/column), apex, >10-groups-hides-legend, palette-recycling, non-data.frameinput, andpdata-merging branches, plus its two validation errorsBrings
plot-GCIMSSample.R,plot-GCIMSChromatogram.R,plot-GCIMSSpectrum.R, andutils-plot.Rto 100% line coverage. Overall package coverage: 78.34% → 86.83%.Bug fix
While testing
plot(sample, trans = ...), found thatmat_to_gplot()checkedinherits(trans, "trans")to validate a transform object passed directly (not as a string).scales >= 1.3.0renamed this S3 class from"trans"to"transform", so passing any transform object this way — including the package's owncubic_root_trans()— always failed with"unknown trans value", even though the roxygen docs explicitly point to this as valid usage (plot(s, trans = scales::identity_trans()),plot(s, trans = cubic_root_trans())).Fixed the check to use the class name
scaleshas used since 1.3.0 (already over a year old), and bumpedDESCRIPTION'sscalesdependency to>= 1.4.0accordingly. The string-basedtrans = "log10"path was unaffected (it goes through a different code path using the still-preserved legacy*_trans()function names).Test plan
testthat::test_local(".")— 672 passing, 1 pre-existing skip, 0 failurescovr::package_coverage()confirms all four files at 100%; overall package coverage 78.34% → 86.83%trans=bug end-to-end before and after the fix (string values, transform objects, and the two error paths)Generated by Claude Code