fix(r): skip golden-fixture test in standalone package builds + document parity (#257)

## Problem
The r-universe build of `wickra` 0.8.4 fails (`R CMD check` ERROR): the golden-fixture parity test added in #255 walks up from the working directory looking for `testdata/golden` and `stop()`s when it cannot find it. Standalone package builds (r-universe / CRAN) package only `bindings/r`, so the repo-root `testdata/golden` fixtures are unreachable there — whereas the monorepo CI checks out the full repo, so the walk-up succeeds and the test passes.

Run: https://github.com/r-universe/wickra-lib/actions/runs/27354800361

## Fix
- **`bindings/r/tests/testthat/test-golden.R`** — the fixture-directory lookup now returns `NULL` instead of erroring when the fixtures are absent, and each test starts with `skip_if(is.null(golden_dir), ...)`. The repository CI (full repo present) still runs the parity checks; standalone builds skip them. The per-test `golden_input` read moved inside the (post-skip) test bodies so nothing runs at source time when the fixtures are missing.
- **`CHANGELOG.md`** — `[Unreleased]` Fixed entry.

## Docs (B6)
- **`README.md` `## Testing`** — the four C-ABI bindings (C#, Go, Java, R) were described as covering one indicator per FFI archetype; document that they additionally replay the shared golden fixture and assert exact parity with the Rust reference outputs.
This commit is contained in:
kingchenc
2026-06-11 17:08:33 +02:00
committed by GitHub
parent 8bfe24ac1d
commit 3e5a19c94a
3 changed files with 38 additions and 5 deletions
+6
View File
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Fixed
- The R binding's golden-fixture parity test now skips gracefully when the shared
`testdata/golden` fixtures are not bundled with the package — standalone
r-universe / CRAN builds package only `bindings/r`, so the repo-root fixtures
are unreachable there. The parity stays enforced by the repository CI, where
the fixtures are present.
## [0.8.4] - 2026-06-11
### Fixed
+6
View File
@@ -358,6 +358,12 @@ Every layer is covered; run the suites with the commands in
- `bindings/java`: JUnit cases covering one indicator per FFI archetype
(scalar/batch, multi-output, bars, profile, array input) plus batch equivalence.
The four C-ABI bindings (C#, Go, Java, R) additionally replay a shared,
language-neutral golden fixture (`testdata/golden/*.csv`, generated by
`cargo run -p wickra-examples --bin gen_golden`) and assert exact parity with the
Rust reference outputs across every archetype (SMA, EMA, RSI, ATR, MACD, ADX,
Beta), catching FFI wiring bugs the math-only core tests cannot see.
## Contributing
Contributions are very welcome — issues, bug reports, ideas, and pull requests
+26 -5
View File
@@ -3,23 +3,36 @@
# archetype test only checks finiteness, this pins exact values, catching wiring
# bugs (swapped params, wrong multi-output field). Fixtures are generated by
# `cargo run -p wickra-examples --bin gen_golden`.
#
# The fixtures live at the repository root (testdata/golden) and are present
# during the monorepo test run, but they are NOT bundled into the standalone R
# package. A packaged check (r-universe / CRAN) therefore cannot find them, so
# these tests skip there; the parity is already enforced by the repository CI.
golden_dir <- local({
find_golden_dir <- function() {
d <- normalizePath(getwd(), winslash = "/", mustWork = FALSE)
repeat {
g <- file.path(d, "testdata", "golden")
if (dir.exists(g)) return(g)
parent <- dirname(d)
if (identical(parent, d)) stop("testdata/golden not found from ", getwd())
if (identical(parent, d)) return(NULL)
d <- parent
}
})
}
golden_dir <- find_golden_dir()
skip_if_no_golden <- function() {
skip_if(is.null(golden_dir), "golden fixtures not bundled with the package")
}
read_golden <- function(name) {
read.csv(file.path(golden_dir, paste0(name, ".csv")),
colClasses = "character", check.names = FALSE)
}
read_golden_input <- function() read.csv(file.path(golden_dir, "input.csv"))
gcell <- function(s) if (identical(s, "nan")) NA_real_ else as.numeric(s)
expect_close <- function(got, want, row, field) {
@@ -31,9 +44,9 @@ expect_close <- function(got, want, row, field) {
}
}
golden_input <- read.csv(file.path(golden_dir, "input.csv"))
test_that("scalar indicators match golden", {
skip_if_no_golden()
golden_input <- read_golden_input()
specs <- list(c("sma", 14), c("ema", 14), c("rsi", 14))
for (spec in specs) {
name <- spec[[1]]
@@ -47,6 +60,8 @@ test_that("scalar indicators match golden", {
})
test_that("candle Atr matches golden", {
skip_if_no_golden()
golden_input <- read_golden_input()
atr <- Atr(14)
exp <- read_golden("atr")
for (i in seq_len(nrow(golden_input))) {
@@ -57,6 +72,8 @@ test_that("candle Atr matches golden", {
})
test_that("pairwise Beta matches golden", {
skip_if_no_golden()
golden_input <- read_golden_input()
beta <- Beta(20)
exp <- read_golden("beta")
for (i in seq_len(nrow(golden_input))) {
@@ -67,6 +84,8 @@ test_that("pairwise Beta matches golden", {
})
test_that("multi-output MACD matches golden", {
skip_if_no_golden()
golden_input <- read_golden_input()
macd <- MacdIndicator(12, 26, 9)
exp <- read_golden("macd")
for (i in seq_len(nrow(golden_input))) {
@@ -82,6 +101,8 @@ test_that("multi-output MACD matches golden", {
})
test_that("multi-output ADX matches golden", {
skip_if_no_golden()
golden_input <- read_golden_input()
adx <- Adx(14)
exp <- read_golden("adx")
for (i in seq_len(nrow(golden_input))) {