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:
@@ -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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [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
|
## [0.8.4] - 2026-06-11
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -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
|
- `bindings/java`: JUnit cases covering one indicator per FFI archetype
|
||||||
(scalar/batch, multi-output, bars, profile, array input) plus batch equivalence.
|
(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
|
## Contributing
|
||||||
|
|
||||||
Contributions are very welcome — issues, bug reports, ideas, and pull requests
|
Contributions are very welcome — issues, bug reports, ideas, and pull requests
|
||||||
|
|||||||
@@ -3,23 +3,36 @@
|
|||||||
# archetype test only checks finiteness, this pins exact values, catching wiring
|
# archetype test only checks finiteness, this pins exact values, catching wiring
|
||||||
# bugs (swapped params, wrong multi-output field). Fixtures are generated by
|
# bugs (swapped params, wrong multi-output field). Fixtures are generated by
|
||||||
# `cargo run -p wickra-examples --bin gen_golden`.
|
# `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)
|
d <- normalizePath(getwd(), winslash = "/", mustWork = FALSE)
|
||||||
repeat {
|
repeat {
|
||||||
g <- file.path(d, "testdata", "golden")
|
g <- file.path(d, "testdata", "golden")
|
||||||
if (dir.exists(g)) return(g)
|
if (dir.exists(g)) return(g)
|
||||||
parent <- dirname(d)
|
parent <- dirname(d)
|
||||||
if (identical(parent, d)) stop("testdata/golden not found from ", getwd())
|
if (identical(parent, d)) return(NULL)
|
||||||
d <- parent
|
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_golden <- function(name) {
|
||||||
read.csv(file.path(golden_dir, paste0(name, ".csv")),
|
read.csv(file.path(golden_dir, paste0(name, ".csv")),
|
||||||
colClasses = "character", check.names = FALSE)
|
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)
|
gcell <- function(s) if (identical(s, "nan")) NA_real_ else as.numeric(s)
|
||||||
|
|
||||||
expect_close <- function(got, want, row, field) {
|
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", {
|
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))
|
specs <- list(c("sma", 14), c("ema", 14), c("rsi", 14))
|
||||||
for (spec in specs) {
|
for (spec in specs) {
|
||||||
name <- spec[[1]]
|
name <- spec[[1]]
|
||||||
@@ -47,6 +60,8 @@ test_that("scalar indicators match golden", {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test_that("candle Atr matches golden", {
|
test_that("candle Atr matches golden", {
|
||||||
|
skip_if_no_golden()
|
||||||
|
golden_input <- read_golden_input()
|
||||||
atr <- Atr(14)
|
atr <- Atr(14)
|
||||||
exp <- read_golden("atr")
|
exp <- read_golden("atr")
|
||||||
for (i in seq_len(nrow(golden_input))) {
|
for (i in seq_len(nrow(golden_input))) {
|
||||||
@@ -57,6 +72,8 @@ test_that("candle Atr matches golden", {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test_that("pairwise Beta matches golden", {
|
test_that("pairwise Beta matches golden", {
|
||||||
|
skip_if_no_golden()
|
||||||
|
golden_input <- read_golden_input()
|
||||||
beta <- Beta(20)
|
beta <- Beta(20)
|
||||||
exp <- read_golden("beta")
|
exp <- read_golden("beta")
|
||||||
for (i in seq_len(nrow(golden_input))) {
|
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", {
|
test_that("multi-output MACD matches golden", {
|
||||||
|
skip_if_no_golden()
|
||||||
|
golden_input <- read_golden_input()
|
||||||
macd <- MacdIndicator(12, 26, 9)
|
macd <- MacdIndicator(12, 26, 9)
|
||||||
exp <- read_golden("macd")
|
exp <- read_golden("macd")
|
||||||
for (i in seq_len(nrow(golden_input))) {
|
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", {
|
test_that("multi-output ADX matches golden", {
|
||||||
|
skip_if_no_golden()
|
||||||
|
golden_input <- read_golden_input()
|
||||||
adx <- Adx(14)
|
adx <- Adx(14)
|
||||||
exp <- read_golden("adx")
|
exp <- read_golden("adx")
|
||||||
for (i in seq_len(nrow(golden_input))) {
|
for (i in seq_len(nrow(golden_input))) {
|
||||||
|
|||||||
Reference in New Issue
Block a user