diff --git a/CHANGELOG.md b/CHANGELOG.md index 894ee810..ecb4e181 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 8a060dcc..dedc5bb5 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/bindings/r/tests/testthat/test-golden.R b/bindings/r/tests/testthat/test-golden.R index 055d5b7c..1ccb45d5 100644 --- a/bindings/r/tests/testthat/test-golden.R +++ b/bindings/r/tests/testthat/test-golden.R @@ -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))) {