From 1e105113fa345329c3fded6dcad1d49d7ab22d22 Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Tue, 24 Mar 2026 10:04:14 -0700 Subject: [PATCH 01/27] Moved to 3e and removed context calls --- DESCRIPTION | 3 ++- tests/testthat/test-csv.R | 2 -- tests/testthat/test-data.R | 2 -- tests/testthat/test-example.R | 2 -- tests/testthat/test-failed-chains.R | 1 - tests/testthat/test-fit-gq.R | 2 -- tests/testthat/test-fit-init.R | 1 - tests/testthat/test-fit-laplace.R | 2 -- tests/testthat/test-fit-mcmc.R | 2 -- tests/testthat/test-fit-mle.R | 2 -- tests/testthat/test-fit-shared.R | 2 -- tests/testthat/test-fit-vb.R | 2 -- tests/testthat/test-install.R | 2 -- tests/testthat/test-json.R | 2 -- tests/testthat/test-knitr.R | 2 -- tests/testthat/test-model-code-print.R | 2 -- tests/testthat/test-model-compile.R | 2 -- tests/testthat/test-model-data.R | 1 - tests/testthat/test-model-diagnose.R | 2 -- tests/testthat/test-model-expose-functions.R | 2 -- tests/testthat/test-model-generate_quantities.R | 2 -- tests/testthat/test-model-init.R | 2 -- tests/testthat/test-model-laplace.R | 2 -- tests/testthat/test-model-methods.R | 1 - tests/testthat/test-model-optimize.R | 2 -- tests/testthat/test-model-output_dir.R | 2 -- tests/testthat/test-model-pathfinder.R | 3 --- tests/testthat/test-model-sample-metric.R | 2 -- tests/testthat/test-model-sample.R | 2 -- tests/testthat/test-model-sample_mpi.R | 2 -- tests/testthat/test-model-variables.R | 2 -- tests/testthat/test-model-variational.R | 2 -- tests/testthat/test-opencl.R | 3 --- tests/testthat/test-path.R | 2 -- tests/testthat/test-profiling.R | 2 -- tests/testthat/test-threads.R | 2 -- tests/testthat/test-utils.R | 2 -- 37 files changed, 2 insertions(+), 71 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index a569f8fc7..f0e727a32 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -55,6 +55,7 @@ Suggests: loo (>= 2.0.0), qs2, rmarkdown, - testthat (>= 2.1.0), + testthat (>= 3.0.0), Rcpp VignetteBuilder: knitr +Config/testthat/edition: 3 diff --git a/tests/testthat/test-csv.R b/tests/testthat/test-csv.R index 89c0faf36..46540dafb 100644 --- a/tests/testthat/test-csv.R +++ b/tests/testthat/test-csv.R @@ -1,5 +1,3 @@ -context("read_cmdstan_csv") - set_cmdstan_path() fit_bernoulli_optimize <- testing_fit("bernoulli", method = "optimize", seed = 1234) fit_bernoulli_variational <- testing_fit("bernoulli", method = "variational", seed = 123) diff --git a/tests/testthat/test-data.R b/tests/testthat/test-data.R index b024f5e6d..8fb9f63f9 100644 --- a/tests/testthat/test-data.R +++ b/tests/testthat/test-data.R @@ -1,5 +1,3 @@ -context("data-utils") - set_cmdstan_path() fit <- testing_fit("bernoulli", method = "sample", seed = 123) fit_vb <- testing_fit("bernoulli", method = "variational", seed = 123) diff --git a/tests/testthat/test-example.R b/tests/testthat/test-example.R index d8c919100..638e245a8 100644 --- a/tests/testthat/test-example.R +++ b/tests/testthat/test-example.R @@ -1,5 +1,3 @@ -context("cmdstanr_example") - test_that("cmdstanr_example works", { fit_mcmc <- cmdstanr_example("logistic", chains = 2, force_recompile = TRUE) checkmate::expect_r6(fit_mcmc, "CmdStanMCMC") diff --git a/tests/testthat/test-failed-chains.R b/tests/testthat/test-failed-chains.R index 7a67cf2c9..b76e87001 100644 --- a/tests/testthat/test-failed-chains.R +++ b/tests/testthat/test-failed-chains.R @@ -1,4 +1,3 @@ -context("failed chains") set_cmdstan_path() stan_program <- testing_stan_file("chain_fails") stan_program_init_warnings <- testing_stan_file("init_warnings") diff --git a/tests/testthat/test-fit-gq.R b/tests/testthat/test-fit-gq.R index 33cd62bed..951e86239 100644 --- a/tests/testthat/test-fit-gq.R +++ b/tests/testthat/test-fit-gq.R @@ -1,5 +1,3 @@ -context("fitted-gq") - set_cmdstan_path() fit <- testing_fit("bernoulli", method = "sample", seed = 123) fit_gq <- testing_fit("bernoulli_ppc", method = "generate_quantities", seed = 123, fitted_params = fit) diff --git a/tests/testthat/test-fit-init.R b/tests/testthat/test-fit-init.R index 7b78c73a2..e4aca7599 100644 --- a/tests/testthat/test-fit-init.R +++ b/tests/testthat/test-fit-init.R @@ -1,4 +1,3 @@ -context("fitted-inits") set_cmdstan_path() data_list_schools <- testing_data("schools") diff --git a/tests/testthat/test-fit-laplace.R b/tests/testthat/test-fit-laplace.R index 8991a9e12..90415e935 100644 --- a/tests/testthat/test-fit-laplace.R +++ b/tests/testthat/test-fit-laplace.R @@ -1,5 +1,3 @@ -context("fitted-laplace") - set_cmdstan_path() fit_laplace <- testing_fit("logistic", method = "laplace", seed = 100) PARAM_NAMES <- c("alpha", "beta[1]", "beta[2]", "beta[3]") diff --git a/tests/testthat/test-fit-mcmc.R b/tests/testthat/test-fit-mcmc.R index 6876fd5d4..657345d49 100644 --- a/tests/testthat/test-fit-mcmc.R +++ b/tests/testthat/test-fit-mcmc.R @@ -1,5 +1,3 @@ -context("fitted-mcmc") - set_cmdstan_path() fit_mcmc <- testing_fit("logistic", method = "sample", seed = 123, chains = 2) diff --git a/tests/testthat/test-fit-mle.R b/tests/testthat/test-fit-mle.R index cd87a214e..c868a37c2 100644 --- a/tests/testthat/test-fit-mle.R +++ b/tests/testthat/test-fit-mle.R @@ -1,5 +1,3 @@ -context("fitted-mle") - set_cmdstan_path() fit_mle <- testing_fit("logistic", method = "optimize", seed = 123) mod <- testing_model("bernoulli") diff --git a/tests/testthat/test-fit-shared.R b/tests/testthat/test-fit-shared.R index d422c47c3..8f83be319 100644 --- a/tests/testthat/test-fit-shared.R +++ b/tests/testthat/test-fit-shared.R @@ -1,5 +1,3 @@ -context("fitted-shared-methods") - set_cmdstan_path() fits <- list() fits[["sample"]] <- testing_fit("logistic", method = "sample", diff --git a/tests/testthat/test-fit-vb.R b/tests/testthat/test-fit-vb.R index e701e951a..f5d2f2c96 100644 --- a/tests/testthat/test-fit-vb.R +++ b/tests/testthat/test-fit-vb.R @@ -1,5 +1,3 @@ -context("fitted-vb") - set_cmdstan_path() fit_vb <- testing_fit("logistic", method = "variational", seed = 123) fit_vb_sci_not <- testing_fit("logistic", method = "variational", seed = 123, iter = 200000, adapt_iter = 100000) diff --git a/tests/testthat/test-install.R b/tests/testthat/test-install.R index 0f2b99659..f3d8f0691 100644 --- a/tests/testthat/test-install.R +++ b/tests/testthat/test-install.R @@ -1,5 +1,3 @@ -context("install") - # avoid parallel on Mac due to strange intermittent TBB errors on Github Actions CORES <- if (os_is_macos()) 1 else 2 diff --git a/tests/testthat/test-json.R b/tests/testthat/test-json.R index c2c2657cf..c86232da7 100644 --- a/tests/testthat/test-json.R +++ b/tests/testthat/test-json.R @@ -1,5 +1,3 @@ -context("json") - test_that("JSON output unboxing works", { temp_file <- tempfile() N <- 10 diff --git a/tests/testthat/test-knitr.R b/tests/testthat/test-knitr.R index 7e4400c51..bea503b43 100644 --- a/tests/testthat/test-knitr.R +++ b/tests/testthat/test-knitr.R @@ -1,5 +1,3 @@ -context("knitr engine") - test_that("eng_cmdstan throws correct errors", { skip_if_not_installed("knitr") expect_error(eng_cmdstan(list(output.var = 1)), "must be a character string") diff --git a/tests/testthat/test-model-code-print.R b/tests/testthat/test-model-code-print.R index e3409ed97..fcebcd46c 100644 --- a/tests/testthat/test-model-code-print.R +++ b/tests/testthat/test-model-code-print.R @@ -1,5 +1,3 @@ -context("model-code-print") - set_cmdstan_path() stan_program <- testing_stan_file("bernoulli") mod <- testing_model("bernoulli") diff --git a/tests/testthat/test-model-compile.R b/tests/testthat/test-model-compile.R index 4bc60a953..55b98979e 100644 --- a/tests/testthat/test-model-compile.R +++ b/tests/testthat/test-model-compile.R @@ -1,5 +1,3 @@ -context("model-compile") - set_cmdstan_path() stan_program <- cmdstan_example_file() mod <- cmdstan_model(stan_file = stan_program, compile = FALSE) diff --git a/tests/testthat/test-model-data.R b/tests/testthat/test-model-data.R index 2f91e5759..34a427e93 100644 --- a/tests/testthat/test-model-data.R +++ b/tests/testthat/test-model-data.R @@ -1,4 +1,3 @@ -context("model-data") # see separate test-json for testing writing data to JSON set_cmdstan_path() diff --git a/tests/testthat/test-model-diagnose.R b/tests/testthat/test-model-diagnose.R index 76ab4a476..ad252612e 100644 --- a/tests/testthat/test-model-diagnose.R +++ b/tests/testthat/test-model-diagnose.R @@ -1,5 +1,3 @@ -context("model-diagnose") - set_cmdstan_path() mod <- testing_model("bernoulli") data_list <- testing_data("bernoulli") diff --git a/tests/testthat/test-model-expose-functions.R b/tests/testthat/test-model-expose-functions.R index 9bcf13824..0cbc4f8ba 100644 --- a/tests/testthat/test-model-expose-functions.R +++ b/tests/testthat/test-model-expose-functions.R @@ -1,5 +1,3 @@ -context("model-expose-functions") - # Standalone functions not expected to work on WSL yet skip_if(os_is_wsl()) diff --git a/tests/testthat/test-model-generate_quantities.R b/tests/testthat/test-model-generate_quantities.R index 7c6418157..130ad2839 100644 --- a/tests/testthat/test-model-generate_quantities.R +++ b/tests/testthat/test-model-generate_quantities.R @@ -1,5 +1,3 @@ -context("model-generate-quantities") - set_cmdstan_path() fit <- testing_fit("bernoulli", method = "sample", seed = 123) mod_gq <- testing_model("bernoulli_ppc") diff --git a/tests/testthat/test-model-init.R b/tests/testthat/test-model-init.R index ebe546777..92e952d64 100644 --- a/tests/testthat/test-model-init.R +++ b/tests/testthat/test-model-init.R @@ -1,5 +1,3 @@ -context("model-init") - set_cmdstan_path() mod <- testing_model("bernoulli") data_list <- testing_data("bernoulli") diff --git a/tests/testthat/test-model-laplace.R b/tests/testthat/test-model-laplace.R index 00dc78bbf..01dc84f75 100644 --- a/tests/testthat/test-model-laplace.R +++ b/tests/testthat/test-model-laplace.R @@ -1,5 +1,3 @@ -context("model-laplace") - set_cmdstan_path() mod <- testing_model("logistic") data_list <- testing_data("logistic") diff --git a/tests/testthat/test-model-methods.R b/tests/testthat/test-model-methods.R index 1e38ad25f..c502af15c 100644 --- a/tests/testthat/test-model-methods.R +++ b/tests/testthat/test-model-methods.R @@ -1,4 +1,3 @@ -context("model-methods") skip_if(os_is_wsl()) set_cmdstan_path() diff --git a/tests/testthat/test-model-optimize.R b/tests/testthat/test-model-optimize.R index a3bd89c64..a06b8f374 100644 --- a/tests/testthat/test-model-optimize.R +++ b/tests/testthat/test-model-optimize.R @@ -1,5 +1,3 @@ -context("model-optimize") - set_cmdstan_path() mod <- testing_model("bernoulli") data_list <- testing_data("bernoulli") diff --git a/tests/testthat/test-model-output_dir.R b/tests/testthat/test-model-output_dir.R index f52848276..0fceac4ed 100644 --- a/tests/testthat/test-model-output_dir.R +++ b/tests/testthat/test-model-output_dir.R @@ -1,5 +1,3 @@ -context("model-output_dir-output-basename") - set_cmdstan_path() sandbox <- file.path(tempdir(check = TRUE), "sandbox") if (!dir.exists(sandbox)) { diff --git a/tests/testthat/test-model-pathfinder.R b/tests/testthat/test-model-pathfinder.R index 580580109..7f71cb758 100644 --- a/tests/testthat/test-model-pathfinder.R +++ b/tests/testthat/test-model-pathfinder.R @@ -1,5 +1,3 @@ -context("model-pathfinder") - set_cmdstan_path() stan_program <- testing_stan_file("bernoulli") mod <- testing_model("bernoulli") @@ -156,4 +154,3 @@ test_that("no output with show_messages = FALSE", { ) expect_equal(length(output), 0) }) - diff --git a/tests/testthat/test-model-sample-metric.R b/tests/testthat/test-model-sample-metric.R index 422442fac..5789e5834 100644 --- a/tests/testthat/test-model-sample-metric.R +++ b/tests/testthat/test-model-sample-metric.R @@ -1,5 +1,3 @@ -context("model-sample-metric") - set_cmdstan_path() mod <- testing_model("bernoulli") data_list <- testing_data("bernoulli") diff --git a/tests/testthat/test-model-sample.R b/tests/testthat/test-model-sample.R index f9ce48b4d..b7892fe52 100644 --- a/tests/testthat/test-model-sample.R +++ b/tests/testthat/test-model-sample.R @@ -1,5 +1,3 @@ -context("model-sample") - set_cmdstan_path() stan_program <- testing_stan_file("bernoulli") mod <- testing_model("bernoulli") diff --git a/tests/testthat/test-model-sample_mpi.R b/tests/testthat/test-model-sample_mpi.R index 1ea0f9a68..0a1cef82d 100644 --- a/tests/testthat/test-model-sample_mpi.R +++ b/tests/testthat/test-model-sample_mpi.R @@ -1,5 +1,3 @@ -context("model-sample_mpi") - test_that("sample_mpi() works", { skip_if(!mpi_toolchain_present()) mpi_file <- write_stan_file(" diff --git a/tests/testthat/test-model-variables.R b/tests/testthat/test-model-variables.R index 5ca43ef28..ae71d29f0 100644 --- a/tests/testthat/test-model-variables.R +++ b/tests/testthat/test-model-variables.R @@ -1,5 +1,3 @@ -context("model-variables") - set_cmdstan_path() test_that("$variables() work correctly with example models", { diff --git a/tests/testthat/test-model-variational.R b/tests/testthat/test-model-variational.R index 6c917d1e5..6248d01c0 100644 --- a/tests/testthat/test-model-variational.R +++ b/tests/testthat/test-model-variational.R @@ -1,5 +1,3 @@ -context("model-variational") - set_cmdstan_path() mod <- testing_model("bernoulli") data_list <- testing_data("bernoulli") diff --git a/tests/testthat/test-opencl.R b/tests/testthat/test-opencl.R index 928581414..44f5fc77d 100644 --- a/tests/testthat/test-opencl.R +++ b/tests/testthat/test-opencl.R @@ -1,5 +1,3 @@ -context("opencl") - set_cmdstan_path() fit <- testing_fit("bernoulli", method = "sample", seed = 123, chains = 1) @@ -128,4 +126,3 @@ test_that("all methods run with valid opencl_ids", { expect_false(is.null(fit$metadata()$device)) expect_false(is.null(fit$metadata()$platform)) }) - diff --git a/tests/testthat/test-path.R b/tests/testthat/test-path.R index e9e7eda05..21fbbfff0 100644 --- a/tests/testthat/test-path.R +++ b/tests/testthat/test-path.R @@ -1,5 +1,3 @@ -context("paths") - Sys.unsetenv("CMDSTAN") PATH <- absolute_path(set_cmdstan_path()) VERSION <- cmdstan_version() diff --git a/tests/testthat/test-profiling.R b/tests/testthat/test-profiling.R index 638f1fdc8..dd45c45c2 100644 --- a/tests/testthat/test-profiling.R +++ b/tests/testthat/test-profiling.R @@ -1,5 +1,3 @@ -context("profiling") - set_cmdstan_path() diff --git a/tests/testthat/test-threads.R b/tests/testthat/test-threads.R index fb5eec615..5e9f04f7f 100644 --- a/tests/testthat/test-threads.R +++ b/tests/testthat/test-threads.R @@ -1,5 +1,3 @@ -context("threading") - set_cmdstan_path() stan_program <- testing_stan_file("bernoulli") stan_gq_program <- testing_stan_file("bernoulli_ppc") diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index a2c39f157..b27628c5b 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -1,5 +1,3 @@ -context("utils") - set_cmdstan_path() fit_mcmc <- testing_fit("logistic", method = "sample", seed = 123, chains = 2) From 5115c3496307a0464ceeceae0d93b8387ca282fe Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Tue, 24 Mar 2026 10:35:58 -0700 Subject: [PATCH 02/27] LLMd testthat 3e syntax changes --- tests/testthat/helper-custom-expectations.R | 6 ++-- tests/testthat/test-csv.R | 5 +-- tests/testthat/test-json.R | 34 ++++++++----------- tests/testthat/test-model-code-print.R | 10 ++++-- tests/testthat/test-model-diagnose.R | 6 ++-- .../testthat/test-model-generate_quantities.R | 4 +-- tests/testthat/test-model-laplace.R | 16 ++++----- tests/testthat/test-model-optimize.R | 6 ++-- tests/testthat/test-model-pathfinder.R | 8 ++--- tests/testthat/test-model-sample.R | 16 ++++----- tests/testthat/test-model-variational.R | 4 +-- 11 files changed, 58 insertions(+), 57 deletions(-) diff --git a/tests/testthat/helper-custom-expectations.R b/tests/testthat/helper-custom-expectations.R index 47a6e38bd..ebff4f65a 100644 --- a/tests/testthat/helper-custom-expectations.R +++ b/tests/testthat/helper-custom-expectations.R @@ -11,7 +11,7 @@ expect_compilation <- function(mod, ...) { } if(!is.null(before_mtime)) { after_mtime <- file.mtime(mod$exe_file()) - expect(before_mtime != after_mtime, sprintf("Exe file '%s' has NOT changed, despite expecting (re)compilation", mod$exe_file())) + expect_true(before_mtime != after_mtime, sprintf("Exe file '%s' has NOT changed, despite expecting (re)compilation", mod$exe_file())) } invisible(mod) } @@ -26,7 +26,7 @@ expect_call_compilation <- function(constructor_call) { fail(sprint("Model executable '%s' does not exist after compilation.", mod$exe_file())) } after_mtime <- file.mtime(mod$exe_file()) - expect(before_time <= after_mtime, sprintf("Exe file '%s' has old timestamp, despite expecting (re)compilation", mod$exe_file())) + expect_true(before_time <= after_mtime, sprintf("Exe file '%s' has old timestamp, despite expecting (re)compilation", mod$exe_file())) invisible(mod) } @@ -40,7 +40,7 @@ expect_no_recompilation <- function(mod, ...) { before_mtime <- file.mtime(mod$exe_file()) expect_interactive_message(mod$compile(...), "Model executable is up to date!") after_mtime <- file.mtime(mod$exe_file()) - expect(before_mtime == after_mtime, sprintf("Model executable '%s' has changed, despite expecting no recompilation", mod$exe_file())) + expect_true(before_mtime == after_mtime, sprintf("Model executable '%s' has changed, despite expecting no recompilation", mod$exe_file())) invisible(mod) } diff --git a/tests/testthat/test-csv.R b/tests/testthat/test-csv.R index 46540dafb..0922c0f05 100644 --- a/tests/testthat/test-csv.R +++ b/tests/testthat/test-csv.R @@ -517,9 +517,10 @@ test_that("returning time works for read_cmdstan_csv", { test_that("time from read_cmdstan_csv matches time from fit$time()", { fit <- fit_bernoulli_thin_1 - expect_equivalent( + expect_equal( read_cmdstan_csv(fit$output_files())$time$chains, - fit$time()$chains + fit$time()$chains, + ignore_attr = TRUE ) }) diff --git a/tests/testthat/test-json.R b/tests/testthat/test-json.R index c86232da7..b0c782085 100644 --- a/tests/testthat/test-json.R +++ b/tests/testthat/test-json.R @@ -1,10 +1,13 @@ +expect_json_output <- function(json_output, file) { + expect_identical(json_output, readLines(file)) +} + test_that("JSON output unboxing works", { temp_file <- tempfile() N <- 10 write_stan_json(list(N = N), file = temp_file) json_output <- readLines(temp_file) - expect_known_output(cat(json_output, sep = "\n"), - file = test_path("answers", "json-unboxing.json")) + expect_json_output(json_output, test_path("answers", "json-unboxing.json")) }) test_that("JSON output for boolean is correct", { @@ -12,8 +15,7 @@ test_that("JSON output for boolean is correct", { N <- c(TRUE, FALSE, TRUE) write_stan_json(list(N = N), file = temp_file) json_output <- readLines(temp_file) - expect_known_output(cat(json_output, sep = "\n"), - file = test_path("answers", "json-boolean.json")) + expect_json_output(json_output, test_path("answers", "json-boolean.json")) }) test_that("JSON output for factors is correct", { @@ -21,8 +23,7 @@ test_that("JSON output for factors is correct", { N <- factor(c(0,1,2,2,1,0), labels = c("c1", "c2", "c3")) write_stan_json(list(N = N), file = temp_file) json_output <- readLines(temp_file) - expect_known_output(cat(json_output, sep = "\n"), - file = test_path("answers", "json-factor.json")) + expect_json_output(json_output, test_path("answers", "json-factor.json")) }) test_that("JSON output for integer vector is correct", { @@ -31,8 +32,7 @@ test_that("JSON output for integer vector is correct", { write_stan_json(list(N = N), file = temp_file) json_output <- readLines(temp_file) - expect_known_output(cat(json_output, sep = "\n"), - file = test_path("answers", "json-integer.json")) + expect_json_output(json_output, test_path("answers", "json-integer.json")) }) test_that("JSON output for data frame and matrix is correct", { @@ -52,8 +52,7 @@ test_that("JSON output for data frame and matrix is correct", { # Floating-point error introduced in jsonlite 1.8.5 # https://github.com/jeroen/jsonlite/issues/420 if (packageVersion("jsonlite") != "1.8.5") { - expect_known_output(cat(json_output_df, sep = "\n"), - file = test_path("answers", "json-df-matrix.json")) + expect_json_output(json_output_df, test_path("answers", "json-df-matrix.json")) } }) @@ -63,8 +62,7 @@ test_that("JSON output for list of vectors is correct", { write_stan_json(list(N = N), file = temp_file) json_output <- readLines(temp_file) - expect_known_output(cat(json_output, sep = "\n"), - file = test_path("answers", "json-vector-lists.json")) + expect_json_output(json_output, test_path("answers", "json-vector-lists.json")) }) test_that("JSON output for list of matrices is correct", { @@ -75,8 +73,7 @@ test_that("JSON output for list of matrices is correct", { ) write_stan_json(list(M = matrices), file = temp_file) json_output <- readLines(temp_file) - expect_known_output(cat(json_output, sep = "\n"), - file = test_path("answers", "json-matrix-lists.json")) + expect_json_output(json_output, test_path("answers", "json-matrix-lists.json")) }) test_that("JSON output for table is correct", { @@ -85,18 +82,15 @@ test_that("JSON output for table is correct", { write_stan_json(list(x = table(f)), file = temp_file) json_output <- readLines(temp_file) - expect_known_output(cat(json_output, sep = "\n"), - file = test_path("answers", "json-table-vector.json")) + expect_json_output(json_output, test_path("answers", "json-table-vector.json")) write_stan_json(list(x = table(f, f)), file = temp_file) json_output <- readLines(temp_file) - expect_known_output(cat(json_output, sep = "\n"), - file = test_path("answers", "json-table-matrix.json")) + expect_json_output(json_output, test_path("answers", "json-table-matrix.json")) write_stan_json(list(x = table(f, f, f)), file = temp_file) json_output <- readLines(temp_file) - expect_known_output(cat(json_output, sep = "\n"), - file = test_path("answers", "json-table-array.json")) + expect_json_output(json_output, test_path("answers", "json-table-array.json")) }) test_that("write_stan_json errors if NAs", { diff --git a/tests/testthat/test-model-code-print.R b/tests/testthat/test-model-code-print.R index fcebcd46c..6659202ef 100644 --- a/tests/testthat/test-model-code-print.R +++ b/tests/testthat/test-model-code-print.R @@ -4,8 +4,14 @@ mod <- testing_model("bernoulli") test_that("code() and print() methods work", { - expect_known_output(mod$print(), file = test_path("answers", "model-print-output.stan")) - expect_known_value(mod$code(), file = test_path("answers", "model-code-output.rds")) + expect_identical( + utils::capture.output(mod$print()), + readLines(test_path("answers", "model-print-output.stan")) + ) + expect_identical( + mod$code(), + readRDS(test_path("answers", "model-code-output.rds")) + ) }) test_that("code() and print() still work if file is removed", { diff --git a/tests/testthat/test-model-diagnose.R b/tests/testthat/test-model-diagnose.R index ad252612e..0ef2db687 100644 --- a/tests/testthat/test-model-diagnose.R +++ b/tests/testthat/test-model-diagnose.R @@ -32,11 +32,11 @@ ok_arg_sci_nota_values <- list( test_that("diagnose() method runs when all arguments specified validly", { # specifying all arguments validly fit1 <- do.call(mod$diagnose, ok_arg_values) - expect_is(fit1, "CmdStanDiagnose") + expect_s3_class(fit1, "CmdStanDiagnose") # leaving all at default (except 'data' and 'seed') fit2 <- mod$diagnose(data = data_list, seed = 123) - expect_is(fit2, "CmdStanDiagnose") + expect_s3_class(fit2, "CmdStanDiagnose") }) test_that("diagnose() method runs when arguments are specified in scientific notation", { @@ -44,7 +44,7 @@ test_that("diagnose() method runs when arguments are specified in scientific not # specifying all arguments validly fit1 <- do.call(mod$diagnose, ok_arg_sci_nota_values) - expect_is(fit1, "CmdStanDiagnose") + expect_s3_class(fit1, "CmdStanDiagnose") }) test_that("diagnose() method errors for any invalid argument before calling cmdstan", { diff --git a/tests/testthat/test-model-generate_quantities.R b/tests/testthat/test-model-generate_quantities.R index 130ad2839..ec1924aa7 100644 --- a/tests/testthat/test-model-generate_quantities.R +++ b/tests/testthat/test-model-generate_quantities.R @@ -23,11 +23,11 @@ bad_arg_values <- list( test_that("generate_quantities() method runs when all arguments specified validly", { # specifying all arguments validly expect_gq_output(fit1 <- do.call(mod_gq$generate_quantities, ok_arg_values)) - expect_is(fit1, "CmdStanGQ") + expect_s3_class(fit1, "CmdStanGQ") # leaving all at default (except 'data') expect_gq_output(fit2 <- mod_gq$generate_quantities(fitted_params = fit, data = data_list)) - expect_is(fit2, "CmdStanGQ") + expect_s3_class(fit2, "CmdStanGQ") }) test_that("generate_quantities() method errors for any invalid argument before calling cmdstan", { diff --git a/tests/testthat/test-model-laplace.R b/tests/testthat/test-model-laplace.R index 01dc84f75..961883c8f 100644 --- a/tests/testthat/test-model-laplace.R +++ b/tests/testthat/test-model-laplace.R @@ -46,7 +46,7 @@ test_that("laplace() method errors for any invalid argument before calling cmdst test_that("laplace() runs when all arguments specified validly", { # specifying all arguments validly expect_laplace_output(fit1 <- do.call(mod$laplace, ok_arg_values)) - expect_is(fit1, "CmdStanLaplace") + expect_s3_class(fit1, "CmdStanLaplace") # check that correct arguments were indeed passed to CmdStan expect_equal(fit1$metadata()$refresh, ok_arg_values$refresh) @@ -59,7 +59,7 @@ test_that("laplace() runs when all arguments specified validly", { # leaving all at default (except 'data') expect_laplace_output(fit2 <- mod$laplace(data = data_list, seed = 123)) - expect_is(fit2, "CmdStanLaplace") + expect_s3_class(fit2, "CmdStanLaplace") }) test_that("laplace() all valid 'mode' inputs give same results", { @@ -70,12 +70,12 @@ test_that("laplace() all valid 'mode' inputs give same results", { fit3 <- mod$laplace(data = data_list, mode = NULL, seed = 100, refresh = 0) }) - expect_is(fit1, "CmdStanLaplace") - expect_is(fit2, "CmdStanLaplace") - expect_is(fit3, "CmdStanLaplace") - expect_is(fit1$mode(), "CmdStanMLE") - expect_is(fit2$mode(), "CmdStanMLE") - expect_is(fit3$mode(), "CmdStanMLE") + expect_s3_class(fit1, "CmdStanLaplace") + expect_s3_class(fit2, "CmdStanLaplace") + expect_s3_class(fit3, "CmdStanLaplace") + expect_s3_class(fit1$mode(), "CmdStanMLE") + expect_s3_class(fit2$mode(), "CmdStanMLE") + expect_s3_class(fit3$mode(), "CmdStanMLE") expect_equal(fit1$mode()$mle(), fit2$mode()$mle()) expect_equal(fit1$mode()$mle(), fit3$mode()$mle()) expect_equal(fit1$lp(), fit2$lp()) diff --git a/tests/testthat/test-model-optimize.R b/tests/testthat/test-model-optimize.R index a06b8f374..204a5b73a 100644 --- a/tests/testthat/test-model-optimize.R +++ b/tests/testthat/test-model-optimize.R @@ -43,17 +43,17 @@ ok_arg_sci_nota_values <- list( test_that("optimize() method runs when all arguments specified validly", { # specifying all arguments validly expect_optim_output(fit1 <- do.call(mod$optimize, ok_arg_values)) - expect_is(fit1, "CmdStanMLE") + expect_s3_class(fit1, "CmdStanMLE") # leaving all at default (except 'data') expect_optim_output(fit2 <- mod$optimize(data = data_list, seed = 123)) - expect_is(fit2, "CmdStanMLE") + expect_s3_class(fit2, "CmdStanMLE") }) test_that("optimize() method runs when arguments are specified in scientific notation", { # specifying all arguments validly expect_optim_output(fit1 <- do.call(mod$optimize, ok_arg_sci_nota_values)) - expect_is(fit1, "CmdStanMLE") + expect_s3_class(fit1, "CmdStanMLE") }) test_that("optimize() warns if threads specified but not enabled", { diff --git a/tests/testthat/test-model-pathfinder.R b/tests/testthat/test-model-pathfinder.R index 7f71cb758..c3a55b52e 100644 --- a/tests/testthat/test-model-pathfinder.R +++ b/tests/testthat/test-model-pathfinder.R @@ -101,15 +101,15 @@ expect_pathfinder_output <- function(object, num_chains = NULL) { test_that("Pathfinder Runs", { expect_pathfinder_output(fit <- mod$pathfinder(data=data_list, seed=1234, refresh = 0)) - expect_is(fit, "CmdStanPathfinder") + expect_s3_class(fit, "CmdStanPathfinder") }) test_that("pathfinder() method works with data files", { expect_pathfinder_output(fit_r <- mod$pathfinder(data = data_file_r)) - expect_is(fit_r, "CmdStanPathfinder") + expect_s3_class(fit_r, "CmdStanPathfinder") expect_pathfinder_output(fit_json <- mod$pathfinder(data = data_file_json)) - expect_is(fit_json, "CmdStanPathfinder") + expect_s3_class(fit_json, "CmdStanPathfinder") }) test_that("pathfinder() method works with init file", { @@ -130,7 +130,7 @@ test_that("pathfinder() method works with init function and default paths", { test_that("pathfinder() method runs when all arguments specified", { expect_pathfinder_output(fit <- do.call(mod$pathfinder, ok_arg_values)) - expect_is(fit, "CmdStanPathfinder") + expect_s3_class(fit, "CmdStanPathfinder") }) test_that("pathfinder() method runs when the stan file is removed", { diff --git a/tests/testthat/test-model-sample.R b/tests/testthat/test-model-sample.R index b7892fe52..360270cbe 100644 --- a/tests/testthat/test-model-sample.R +++ b/tests/testthat/test-model-sample.R @@ -83,15 +83,15 @@ bad_arg_values_3 <- list( test_that("sample() method works with data list", { expect_sample_output(fit <- mod$sample(data = data_list, chains = 1), 1) - expect_is(fit, "CmdStanMCMC") + expect_s3_class(fit, "CmdStanMCMC") }) test_that("sample() method works with data files", { expect_sample_output(fit_r <- mod$sample(data = data_file_r, chains = 1), 1) - expect_is(fit_r, "CmdStanMCMC") + expect_s3_class(fit_r, "CmdStanMCMC") expect_sample_output(fit_json <- mod$sample(data = data_file_json, chains = 1), 1) - expect_is(fit_json, "CmdStanMCMC") + expect_s3_class(fit_json, "CmdStanMCMC") }) test_that("sample() method works with init file", { @@ -107,7 +107,7 @@ test_that("sample() method works with init file", { test_that("sample() method runs when all arguments specified", { expect_sample_output(fit <- do.call(mod$sample, ok_arg_values), 2) - expect_is(fit, "CmdStanMCMC") + expect_s3_class(fit, "CmdStanMCMC") }) test_that("sample() method runs when the stan file is removed", { @@ -196,7 +196,7 @@ test_that("sample() method runs when fixed_param = TRUE", { mod_fp$compile() expect_sample_output(fit_1000 <- mod_fp$sample(fixed_param = TRUE, iter_sampling = 1000), 4) - expect_is(fit_1000, "CmdStanMCMC") + expect_s3_class(fit_1000, "CmdStanMCMC") expect_equal(dim(fit_1000$draws()), c(1000,4,10)) expect_sample_output(fit_500 <- mod_fp$sample(fixed_param = TRUE, iter_sampling = 500), 4) @@ -219,15 +219,15 @@ test_that("sample() method runs when adapt_engaged = FALSE", { test_that("chain_ids work with sample()", { mod$compile() expect_sample_output(fit12 <- mod$sample(data = data_list, chains = 2, chain_ids = c(10,12))) - expect_is(fit12, "CmdStanMCMC") + expect_s3_class(fit12, "CmdStanMCMC") expect_equal(fit12$metadata()$id, c(10,12)) expect_sample_output(fit12 <- mod$sample(data = data_list, chains = 2, chain_ids = c(100,7))) - expect_is(fit12, "CmdStanMCMC") + expect_s3_class(fit12, "CmdStanMCMC") expect_equal(fit12$metadata()$id, c(100,7)) expect_sample_output(fit12 <- mod$sample(data = data_list, chains = 1, chain_ids = c(6))) - expect_is(fit12, "CmdStanMCMC") + expect_s3_class(fit12, "CmdStanMCMC") expect_equal(fit12$metadata()$id, c(6)) expect_error(mod$sample(data = data_list, chains = 1, chain_ids = c(0)), diff --git a/tests/testthat/test-model-variational.R b/tests/testthat/test-model-variational.R index 6248d01c0..25b1fc775 100644 --- a/tests/testthat/test-model-variational.R +++ b/tests/testthat/test-model-variational.R @@ -44,11 +44,11 @@ bad_arg_values <- list( test_that("variational() method runs when all arguments specified validly", { # specifying all arguments validly expect_vb_output(fit1 <- do.call(mod$variational, ok_arg_values)) - expect_is(fit1, "CmdStanVB") + expect_s3_class(fit1, "CmdStanVB") # leaving all at default (except data and seed) expect_vb_output(fit2 <- mod$variational(data = data_list, seed = 123)) - expect_is(fit2, "CmdStanVB") + expect_s3_class(fit2, "CmdStanVB") }) test_that("variational() warns if threads specified but not enabled", { From a91cff8fee9d74757b2868b61c2e67fee17e8fd7 Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Tue, 24 Mar 2026 12:52:38 -0700 Subject: [PATCH 03/27] Tweak some minor testing things --- tests/testthat/helper-custom-expectations.R | 16 +++++++-- tests/testthat/helper-mock-cli.R | 40 ++++++++++++--------- tests/testthat/test-model-compile.R | 8 ++--- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/tests/testthat/helper-custom-expectations.R b/tests/testthat/helper-custom-expectations.R index ebff4f65a..2bda8e329 100644 --- a/tests/testthat/helper-custom-expectations.R +++ b/tests/testthat/helper-custom-expectations.R @@ -5,7 +5,9 @@ expect_compilation <- function(mod, ...) { } else { before_mtime <- NULL } - expect_interactive_message(mod$compile(...), "Compiling Stan program...") + rlang::with_interactive(value = TRUE, { + expect_message(mod$compile(...), "Compiling Stan program...") + }) if(length(mod$exe_file()) == 0 || !file.exists(mod$exe_file())) { fail(sprint("Model executable '%s' does not exist after compilation.", mod$exe_file())) } @@ -20,8 +22,14 @@ expect_compilation <- function(mod, ...) { #' @param constructor_call a call returning a CmdStanModel object that should have been compiled #' @return the newly created model expect_call_compilation <- function(constructor_call) { + constructor_call <- substitute(constructor_call) before_time <- Sys.time() - mod <- expect_interactive_message(constructor_call, "Compiling Stan program...") + rlang::with_interactive(value = TRUE, { + expect_message( + mod <- rlang::eval_bare(constructor_call, parent.frame()), + "Compiling Stan program..." + ) + }) if(length(mod$exe_file()) == 0 || !file.exists(mod$exe_file())) { fail(sprint("Model executable '%s' does not exist after compilation.", mod$exe_file())) } @@ -38,7 +46,9 @@ expect_no_recompilation <- function(mod, ...) { } before_mtime <- file.mtime(mod$exe_file()) - expect_interactive_message(mod$compile(...), "Model executable is up to date!") + rlang::with_interactive(value = TRUE, { + expect_message(mod$compile(...), "Model executable is up to date!") + }) after_mtime <- file.mtime(mod$exe_file()) expect_true(before_mtime == after_mtime, sprintf("Model executable '%s' has changed, despite expecting no recompilation", mod$exe_file())) invisible(mod) diff --git a/tests/testthat/helper-mock-cli.R b/tests/testthat/helper-mock-cli.R index 60a9e52db..649aa6853 100644 --- a/tests/testthat/helper-mock-cli.R +++ b/tests/testthat/helper-mock-cli.R @@ -1,23 +1,29 @@ real_wcr <- wsl_compatible_run with_mocked_cli <- function(code, compile_ret, info_ret) { - with_mocked_bindings( - code, - wsl_compatible_run = function(command, args, ...) { - if ( - !is.null(command) - && command == "make" - && !is.null(args) - && startsWith(basename(args[1]), "model-") - ) { - message("mock-compile-was-called") - compile_ret - } else if (!is.null(args) && args[1] == "info") { - info_ret - } else { - real_wcr(command = command, args = args, ...) - } - } + code <- substitute(code) + rlang::eval_bare( + rlang::expr( + with_mocked_bindings( + !!code, + wsl_compatible_run = !!function(command, args, ...) { + if ( + !is.null(command) + && command == "make" + && !is.null(args) + && startsWith(basename(args[1]), "model-") + ) { + message("mock-compile-was-called") + compile_ret + } else if (!is.null(args) && args[1] == "info") { + info_ret + } else { + real_wcr(command = command, args = args, ...) + } + } + ) + ), + env = parent.frame() ) } diff --git a/tests/testthat/test-model-compile.R b/tests/testthat/test-model-compile.R index 55b98979e..cc7156fc9 100644 --- a/tests/testthat/test-model-compile.R +++ b/tests/testthat/test-model-compile.R @@ -297,11 +297,9 @@ test_that("check_syntax() works", { stan_file <- testing_stan_file("bernoulli") mod_ok <- cmdstan_model(stan_file, compile = FALSE) - expect_true( - expect_message( - mod_ok$check_syntax(), - "Stan program is syntactically correct" - ) + expect_message( + mod_ok$check_syntax(), + "Stan program is syntactically correct" ) expect_message( mod_ok$check_syntax(quiet = TRUE), From c98740e8f423c439e6b91dc7f34d783a348fdb5f Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:01:44 -0700 Subject: [PATCH 04/27] Fixed tests --- tests/testthat/_snaps/json/json-boolean.json | 3 ++ .../testthat/_snaps/json/json-df-matrix.json | 7 ++++ tests/testthat/_snaps/json/json-factor.json | 3 ++ tests/testthat/_snaps/json/json-integer.json | 3 ++ .../_snaps/json/json-matrix-lists.json | 12 ++++++ .../_snaps/json/json-table-array.json | 28 ++++++++++++++ .../_snaps/json/json-table-matrix.json | 8 ++++ .../_snaps/json/json-table-vector.json | 3 ++ tests/testthat/_snaps/json/json-unboxing.json | 3 ++ .../_snaps/json/json-vector-lists.json | 6 +++ .../model-code-print/model-code-output.stan | 11 ++++++ .../model-code-print/model-print-output.stan | 11 ++++++ tests/testthat/test-json.R | 37 ++++++------------- tests/testthat/test-model-code-print.R | 16 ++++---- 14 files changed, 117 insertions(+), 34 deletions(-) create mode 100644 tests/testthat/_snaps/json/json-boolean.json create mode 100644 tests/testthat/_snaps/json/json-df-matrix.json create mode 100644 tests/testthat/_snaps/json/json-factor.json create mode 100644 tests/testthat/_snaps/json/json-integer.json create mode 100644 tests/testthat/_snaps/json/json-matrix-lists.json create mode 100644 tests/testthat/_snaps/json/json-table-array.json create mode 100644 tests/testthat/_snaps/json/json-table-matrix.json create mode 100644 tests/testthat/_snaps/json/json-table-vector.json create mode 100644 tests/testthat/_snaps/json/json-unboxing.json create mode 100644 tests/testthat/_snaps/json/json-vector-lists.json create mode 100644 tests/testthat/_snaps/model-code-print/model-code-output.stan create mode 100644 tests/testthat/_snaps/model-code-print/model-print-output.stan diff --git a/tests/testthat/_snaps/json/json-boolean.json b/tests/testthat/_snaps/json/json-boolean.json new file mode 100644 index 000000000..0d8a3bf3f --- /dev/null +++ b/tests/testthat/_snaps/json/json-boolean.json @@ -0,0 +1,3 @@ +{ + "N": [1, 0, 1] +} diff --git a/tests/testthat/_snaps/json/json-df-matrix.json b/tests/testthat/_snaps/json/json-df-matrix.json new file mode 100644 index 000000000..a5132dcfd --- /dev/null +++ b/tests/testthat/_snaps/json/json-df-matrix.json @@ -0,0 +1,7 @@ +{ + "X": [ + [1, 0.2], + [2, 0.3], + [3, 0.4] + ] +} diff --git a/tests/testthat/_snaps/json/json-factor.json b/tests/testthat/_snaps/json/json-factor.json new file mode 100644 index 000000000..cf77867f1 --- /dev/null +++ b/tests/testthat/_snaps/json/json-factor.json @@ -0,0 +1,3 @@ +{ + "N": [1, 2, 3, 3, 2, 1] +} diff --git a/tests/testthat/_snaps/json/json-integer.json b/tests/testthat/_snaps/json/json-integer.json new file mode 100644 index 000000000..872c8b0a0 --- /dev/null +++ b/tests/testthat/_snaps/json/json-integer.json @@ -0,0 +1,3 @@ +{ + "N": [1, 2, 3, 4] +} diff --git a/tests/testthat/_snaps/json/json-matrix-lists.json b/tests/testthat/_snaps/json/json-matrix-lists.json new file mode 100644 index 000000000..224562681 --- /dev/null +++ b/tests/testthat/_snaps/json/json-matrix-lists.json @@ -0,0 +1,12 @@ +{ + "M": [ + [ + [1, 3], + [2, 4] + ], + [ + [5, 6], + [7, 8] + ] + ] +} diff --git a/tests/testthat/_snaps/json/json-table-array.json b/tests/testthat/_snaps/json/json-table-array.json new file mode 100644 index 000000000..e8e864e1e --- /dev/null +++ b/tests/testthat/_snaps/json/json-table-array.json @@ -0,0 +1,28 @@ +{ + "x": [ + [ + [5, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0] + ], + [ + [0, 0, 0, 0], + [0, 5, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0] + ], + [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 5, 0], + [0, 0, 0, 0] + ], + [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 5] + ] + ] +} diff --git a/tests/testthat/_snaps/json/json-table-matrix.json b/tests/testthat/_snaps/json/json-table-matrix.json new file mode 100644 index 000000000..b10267e3a --- /dev/null +++ b/tests/testthat/_snaps/json/json-table-matrix.json @@ -0,0 +1,8 @@ +{ + "x": [ + [5, 0, 0, 0], + [0, 5, 0, 0], + [0, 0, 5, 0], + [0, 0, 0, 5] + ] +} diff --git a/tests/testthat/_snaps/json/json-table-vector.json b/tests/testthat/_snaps/json/json-table-vector.json new file mode 100644 index 000000000..63e4c2b4c --- /dev/null +++ b/tests/testthat/_snaps/json/json-table-vector.json @@ -0,0 +1,3 @@ +{ + "x": [5, 5, 5, 5] +} diff --git a/tests/testthat/_snaps/json/json-unboxing.json b/tests/testthat/_snaps/json/json-unboxing.json new file mode 100644 index 000000000..e7dc50604 --- /dev/null +++ b/tests/testthat/_snaps/json/json-unboxing.json @@ -0,0 +1,3 @@ +{ + "N": 10 +} diff --git a/tests/testthat/_snaps/json/json-vector-lists.json b/tests/testthat/_snaps/json/json-vector-lists.json new file mode 100644 index 000000000..9f9bb3e3b --- /dev/null +++ b/tests/testthat/_snaps/json/json-vector-lists.json @@ -0,0 +1,6 @@ +{ + "N": [ + [1, 2, 3], + [4, 5, 6] + ] +} diff --git a/tests/testthat/_snaps/model-code-print/model-code-output.stan b/tests/testthat/_snaps/model-code-print/model-code-output.stan new file mode 100644 index 000000000..3b6099fcc --- /dev/null +++ b/tests/testthat/_snaps/model-code-print/model-code-output.stan @@ -0,0 +1,11 @@ +data { + int N; + array[N] int y; +} +parameters { + real theta; +} +model { + theta ~ beta(1, 1); // uniform prior on interval 0,1 + y ~ bernoulli(theta); +} diff --git a/tests/testthat/_snaps/model-code-print/model-print-output.stan b/tests/testthat/_snaps/model-code-print/model-print-output.stan new file mode 100644 index 000000000..3b6099fcc --- /dev/null +++ b/tests/testthat/_snaps/model-code-print/model-print-output.stan @@ -0,0 +1,11 @@ +data { + int N; + array[N] int y; +} +parameters { + real theta; +} +model { + theta ~ beta(1, 1); // uniform prior on interval 0,1 + y ~ bernoulli(theta); +} diff --git a/tests/testthat/test-json.R b/tests/testthat/test-json.R index b0c782085..a413ec1c2 100644 --- a/tests/testthat/test-json.R +++ b/tests/testthat/test-json.R @@ -1,29 +1,22 @@ -expect_json_output <- function(json_output, file) { - expect_identical(json_output, readLines(file)) -} - test_that("JSON output unboxing works", { temp_file <- tempfile() N <- 10 write_stan_json(list(N = N), file = temp_file) - json_output <- readLines(temp_file) - expect_json_output(json_output, test_path("answers", "json-unboxing.json")) + expect_snapshot_file(temp_file, "json-unboxing.json") }) test_that("JSON output for boolean is correct", { temp_file <- tempfile() N <- c(TRUE, FALSE, TRUE) write_stan_json(list(N = N), file = temp_file) - json_output <- readLines(temp_file) - expect_json_output(json_output, test_path("answers", "json-boolean.json")) + expect_snapshot_file(temp_file, "json-boolean.json") }) test_that("JSON output for factors is correct", { temp_file <- tempfile() N <- factor(c(0,1,2,2,1,0), labels = c("c1", "c2", "c3")) write_stan_json(list(N = N), file = temp_file) - json_output <- readLines(temp_file) - expect_json_output(json_output, test_path("answers", "json-factor.json")) + expect_snapshot_file(temp_file, "json-factor.json") }) test_that("JSON output for integer vector is correct", { @@ -31,8 +24,7 @@ test_that("JSON output for integer vector is correct", { N <- c(1.0, 2.0, 3, 4) write_stan_json(list(N = N), file = temp_file) - json_output <- readLines(temp_file) - expect_json_output(json_output, test_path("answers", "json-integer.json")) + expect_snapshot_file(temp_file, "json-integer.json") }) test_that("JSON output for data frame and matrix is correct", { @@ -45,14 +37,12 @@ test_that("JSON output for data frame and matrix is correct", { write_stan_json(list(X = df), file = temp_file_df) write_stan_json(list(X = mat), file = temp_file_mat) - json_output_mat <- readLines(temp_file_df) - json_output_df <- readLines(temp_file_mat) - expect_identical(json_output_df, json_output_mat) + expect_identical(readLines(temp_file_df), readLines(temp_file_mat)) # Floating-point error introduced in jsonlite 1.8.5 # https://github.com/jeroen/jsonlite/issues/420 if (packageVersion("jsonlite") != "1.8.5") { - expect_json_output(json_output_df, test_path("answers", "json-df-matrix.json")) + expect_snapshot_file(temp_file_df, "json-df-matrix.json") } }) @@ -61,8 +51,7 @@ test_that("JSON output for list of vectors is correct", { N <- list(c(1,2,3), c(4,5,6)) write_stan_json(list(N = N), file = temp_file) - json_output <- readLines(temp_file) - expect_json_output(json_output, test_path("answers", "json-vector-lists.json")) + expect_snapshot_file(temp_file, "json-vector-lists.json") }) test_that("JSON output for list of matrices is correct", { @@ -72,8 +61,7 @@ test_that("JSON output for list of matrices is correct", { matrix(5:8, nrow = 2, byrow = TRUE) ) write_stan_json(list(M = matrices), file = temp_file) - json_output <- readLines(temp_file) - expect_json_output(json_output, test_path("answers", "json-matrix-lists.json")) + expect_snapshot_file(temp_file, "json-matrix-lists.json") }) test_that("JSON output for table is correct", { @@ -81,16 +69,13 @@ test_that("JSON output for table is correct", { f <- factor(rep(1:4, each = 5)) write_stan_json(list(x = table(f)), file = temp_file) - json_output <- readLines(temp_file) - expect_json_output(json_output, test_path("answers", "json-table-vector.json")) + expect_snapshot_file(temp_file, "json-table-vector.json") write_stan_json(list(x = table(f, f)), file = temp_file) - json_output <- readLines(temp_file) - expect_json_output(json_output, test_path("answers", "json-table-matrix.json")) + expect_snapshot_file(temp_file, "json-table-matrix.json") write_stan_json(list(x = table(f, f, f)), file = temp_file) - json_output <- readLines(temp_file) - expect_json_output(json_output, test_path("answers", "json-table-array.json")) + expect_snapshot_file(temp_file, "json-table-array.json") }) test_that("write_stan_json errors if NAs", { diff --git a/tests/testthat/test-model-code-print.R b/tests/testthat/test-model-code-print.R index 6659202ef..6851eacdc 100644 --- a/tests/testthat/test-model-code-print.R +++ b/tests/testthat/test-model-code-print.R @@ -4,14 +4,14 @@ mod <- testing_model("bernoulli") test_that("code() and print() methods work", { - expect_identical( - utils::capture.output(mod$print()), - readLines(test_path("answers", "model-print-output.stan")) - ) - expect_identical( - mod$code(), - readRDS(test_path("answers", "model-code-output.rds")) - ) + print_file <- tempfile(fileext = ".stan") + code_file <- tempfile(fileext = ".stan") + + writeLines(utils::capture.output(mod$print()), print_file) + writeLines(mod$code(), code_file) + + expect_snapshot_file(print_file, "model-print-output.stan") + expect_snapshot_file(code_file, "model-code-output.stan") }) test_that("code() and print() still work if file is removed", { From e1399babf86e5b5e58880e21149b8f17c434417d Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:40:31 -0700 Subject: [PATCH 05/27] Refresh test binaries --- tests/testthat/helper-models.R | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/testthat/helper-models.R b/tests/testthat/helper-models.R index f1e248b87..759d72efa 100644 --- a/tests/testthat/helper-models.R +++ b/tests/testthat/helper-models.R @@ -1,3 +1,12 @@ +local({ + stan_files <- dir(test_path("resources", "stan"), pattern = "\\.stan$", full.names = TRUE) + exe_files <- cmdstanr:::cmdstan_ext(cmdstanr:::strip_ext(stan_files)) + existing_exe_files <- exe_files[file.exists(exe_files)] + if (length(existing_exe_files) > 0) { + unlink(existing_exe_files, force = TRUE) + } +}) + testing_data <- function(name) { if (file.exists(test_path("resources", "data", paste0(name, ".data.rds")))) { readRDS(test_path("resources", "data", paste0(name, ".data.rds"))) From ce5f2892e224880a27bbfe361ee043b3ae245ab7 Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Tue, 24 Mar 2026 15:35:34 -0700 Subject: [PATCH 06/27] Use withr for tests --- tests/testthat/test-example.R | 9 +++++---- tests/testthat/test-fit-mcmc.R | 13 +++++-------- tests/testthat/test-install.R | 4 +--- tests/testthat/test-model-init.R | 5 ++--- tests/testthat/test-model-sample.R | 6 +++--- tests/testthat/test-model-sample_mpi.R | 8 +++++--- tests/testthat/test-path.R | 12 ++++-------- 7 files changed, 25 insertions(+), 32 deletions(-) diff --git a/tests/testthat/test-example.R b/tests/testthat/test-example.R index 638e245a8..0f950e2cc 100644 --- a/tests/testthat/test-example.R +++ b/tests/testthat/test-example.R @@ -104,10 +104,11 @@ test_that("cmdstanr_write_stan_file_dir option works", { if (!dir.exists(test_dir)) { dir.create(test_dir) } - options("cmdstanr_write_stan_file_dir" = test_dir) - file <- write_stan_file(stan_program) - expect_equal(repair_path(dirname(file)), repair_path(test_dir)) - options("cmdstanr_write_stan_file_dir" = NULL) + local({ + withr::local_options(list("cmdstanr_write_stan_file_dir" = test_dir)) + file <- write_stan_file(stan_program) + expect_equal(repair_path(dirname(file)), repair_path(test_dir)) + }) file <- write_stan_file(stan_program) expect_equal(repair_path(dirname(file)), repair_path(base_dir)) if (!dir.exists(test_dir)) { diff --git a/tests/testthat/test-fit-mcmc.R b/tests/testthat/test-fit-mcmc.R index 657345d49..8af8a7168 100644 --- a/tests/testthat/test-fit-mcmc.R +++ b/tests/testthat/test-fit-mcmc.R @@ -329,23 +329,20 @@ test_that("loo works for all draws storage formats", { skip_if_not_installed("loo") fit <- testing_fit("bernoulli_log_lik") - options(cmdstanr_draws_format = "draws_array") + withr::local_options(list(cmdstanr_draws_format = "draws_array")) expect_s3_class(suppressWarnings(fit$loo()), "loo") - options(cmdstanr_draws_format = "draws_df") + withr::local_options(list(cmdstanr_draws_format = "draws_df")) expect_s3_class(suppressWarnings(fit$loo()), "loo") - options(cmdstanr_draws_format = "draws_matrix") + withr::local_options(list(cmdstanr_draws_format = "draws_matrix")) expect_s3_class(suppressWarnings(fit$loo()), "loo") - options(cmdstanr_draws_format = "draws_list") + withr::local_options(list(cmdstanr_draws_format = "draws_list")) expect_s3_class(suppressWarnings(fit$loo()), "loo") - options(cmdstanr_draws_format = "draws_rvars") + withr::local_options(list(cmdstanr_draws_format = "draws_rvars")) expect_s3_class(suppressWarnings(fit$loo()), "loo") - - # reset option - options(cmdstanr_draws_format = NULL) }) test_that("draws() works for different formats", { diff --git a/tests/testthat/test-install.R b/tests/testthat/test-install.R index f3d8f0691..c0253ae93 100644 --- a/tests/testthat/test-install.R +++ b/tests/testthat/test-install.R @@ -122,8 +122,7 @@ test_that("install_cmdstan() works with version and release_url", { test_that("toolchain checks on Unix work", { skip_if(os_is_windows()) - path_backup <- Sys.getenv("PATH") - Sys.setenv("PATH" = "") + withr::local_envvar(c("PATH" = "")) if (os_is_macos()) { err_msg_cpp <- "A suitable C++ compiler was not found. Please install the command line tools for Mac with 'xcode-select --install' or install Xcode from the app store. Then restart R and run cmdstanr::check_cmdstan_toolchain()." err_msg_make <- "The 'make' tool was not found. Please install the command line tools for Mac with 'xcode-select --install' or install Xcode from the app store. Then restart R and run cmdstanr::check_cmdstan_toolchain()." @@ -141,7 +140,6 @@ test_that("toolchain checks on Unix work", { err_msg_make, fixed = TRUE ) - Sys.setenv("PATH" = path_backup) }) test_that("clean and rebuild works", { diff --git a/tests/testthat/test-model-init.R b/tests/testthat/test-model-init.R index 92e952d64..e2e30dfc2 100644 --- a/tests/testthat/test-model-init.R +++ b/tests/testthat/test-model-init.R @@ -238,7 +238,7 @@ test_that("error if init function specified incorrectly", { }) test_that("print message if not all parameters are initialized", { - options(cmdstanr_warn_inits = NULL) # should default to TRUE + withr::local_options(list(cmdstanr_warn_inits = NULL)) # should default to TRUE init_list <- list( list( alpha = 1 @@ -271,7 +271,7 @@ test_that("print message if not all parameters are initialized", { }) test_that("No message printed if options(cmdstanr_warn_inits=FALSE)", { - options(cmdstanr_warn_inits = FALSE) + withr::local_options(list(cmdstanr_warn_inits = FALSE)) expect_message( utils::capture.output(mod_logistic$optimize(data = data_list_logistic, init = list(list(a = 0)), seed = 123)), regexp = NA @@ -284,7 +284,6 @@ test_that("No message printed if options(cmdstanr_warn_inits=FALSE)", { utils::capture.output(mod_logistic$sample(data = data_list_logistic, init = list(list(alpha = 1),list(alpha = 1)), chains = 2, seed = 123)), regexp = NA ) - options(cmdstanr_warn_inits = TRUE) }) test_that("Initial values for single-element containers treated correctly", { diff --git a/tests/testthat/test-model-sample.R b/tests/testthat/test-model-sample.R index 360270cbe..85c5dbb85 100644 --- a/tests/testthat/test-model-sample.R +++ b/tests/testthat/test-model-sample.R @@ -177,14 +177,14 @@ test_that("sampling in parallel works", { }) test_that("mc.cores option detected", { - options(mc.cores = 3) + withr::local_options(list(mc.cores = 3)) expect_output( mod$sample(data = data_list, chains = 3), "Running MCMC with 3 parallel chains", fixed = TRUE ) - options(mc.cores = NULL) + withr::local_options(list(mc.cores = NULL)) expect_output( mod$sample(data = data_list, chains = 2), "Running MCMC with 2 sequential chains", @@ -367,7 +367,7 @@ test_that("All output can be suppressed by show_messages", { stan_program <- testing_stan_file("bernoulli") data_list <- testing_data("bernoulli") mod <- cmdstan_model(stan_program, force_recompile = TRUE) - options("cmdstanr_verbose" = FALSE) + withr::local_options(list("cmdstanr_verbose" = FALSE)) output <- capture.output( fit <- mod$sample(data = data_list, show_messages = FALSE) ) diff --git a/tests/testthat/test-model-sample_mpi.R b/tests/testthat/test-model-sample_mpi.R index 0a1cef82d..9463cbbbc 100644 --- a/tests/testthat/test-model-sample_mpi.R +++ b/tests/testthat/test-model-sample_mpi.R @@ -33,9 +33,11 @@ test_that("sample_mpi() works", { if (os_is_wsl()) { # Default GHA WSL install runs as root, which MPI discourages # Specify that this is safe to ignore for this test - Sys.setenv("OMPI_ALLOW_RUN_AS_ROOT"=1) - Sys.setenv("OMPI_ALLOW_RUN_AS_ROOT_CONFIRM"=1) - Sys.setenv("WSLENV"="OMPI_ALLOW_RUN_AS_ROOT/u:OMPI_ALLOW_RUN_AS_ROOT_CONFIRM/u") + withr::local_envvar(c( + "OMPI_ALLOW_RUN_AS_ROOT" = "1", + "OMPI_ALLOW_RUN_AS_ROOT_CONFIRM" = "1", + "WSLENV" = "OMPI_ALLOW_RUN_AS_ROOT/u:OMPI_ALLOW_RUN_AS_ROOT_CONFIRM/u" + )) } utils::capture.output( diff --git a/tests/testthat/test-path.R b/tests/testthat/test-path.R index 21fbbfff0..9758a9cd4 100644 --- a/tests/testthat/test-path.R +++ b/tests/testthat/test-path.R @@ -26,7 +26,7 @@ test_that("Setting bad path leads to warning (can't find directory)", { test_that("Setting bad path from env leads to warning (can't find directory)", { unset_cmdstan_path() .cmdstanr$WSL <- TRUE - Sys.setenv(CMDSTAN = "BAD_PATH") + withr::local_envvar(c(CMDSTAN = "BAD_PATH")) expect_warning( cmdstanr_initialize(), "Can't find directory specified by environment variable" @@ -34,17 +34,15 @@ test_that("Setting bad path from env leads to warning (can't find directory)", { expect_null(.cmdstanr$PATH) expect_null(.cmdstanr$VERSION) expect_false(isTRUE(.cmdstanr$WSL)) - Sys.unsetenv("CMDSTAN") }) test_that("Setting path from env var is detected", { unset_cmdstan_path() expect_true(is.null(.cmdstanr$VERSION)) - Sys.setenv(CMDSTAN = PATH) + withr::local_envvar(c(CMDSTAN = PATH)) expect_silent(cmdstanr_initialize()) expect_equal(cmdstan_path(), PATH) expect_false(is.null(.cmdstanr$VERSION)) - Sys.unsetenv("CMDSTAN") }) test_that("Unsupported CmdStan path from env var is rejected", { @@ -54,10 +52,9 @@ test_that("Unsupported CmdStan path from env var is rejected", { old_install <- file.path(parent_dir, "cmdstan-2.34.0") dir.create(old_install, recursive = TRUE, showWarnings = FALSE) on.exit(unlink(parent_dir, recursive = TRUE), add = TRUE) - on.exit(Sys.unsetenv("CMDSTAN"), add = TRUE) writeLines("CMDSTAN_VERSION := 2.34.0", con = file.path(old_install, "makefile")) - Sys.setenv(CMDSTAN = parent_dir) + withr::local_envvar(c(CMDSTAN = parent_dir)) suppressWarnings(cmdstanr_initialize()) expect_false(identical(.cmdstanr$PATH, absolute_path(old_install))) expect_false(identical(.cmdstanr$VERSION, "2.34.0")) @@ -75,9 +72,8 @@ test_that("Existing CMDSTAN env path with no install resets cached state", { empty_parent <- file.path(tempdir(check = TRUE), "cmdstan-empty-parent") dir.create(empty_parent, recursive = TRUE, showWarnings = FALSE) on.exit(unlink(empty_parent, recursive = TRUE), add = TRUE) - on.exit(Sys.unsetenv("CMDSTAN"), add = TRUE) - Sys.setenv(CMDSTAN = empty_parent) + withr::local_envvar(c(CMDSTAN = empty_parent)) expect_warning( cmdstanr_initialize(), "No CmdStan installation found in the path specified by the environment variable 'CMDSTAN'.", From 2970907e0548a4a0c392ec928add3f5e3eeffff7 Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Tue, 24 Mar 2026 17:21:27 -0700 Subject: [PATCH 07/27] Fix LF issue in tests --- tests/testthat/test-model-code-print.R | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-model-code-print.R b/tests/testthat/test-model-code-print.R index 6851eacdc..71de1a631 100644 --- a/tests/testthat/test-model-code-print.R +++ b/tests/testthat/test-model-code-print.R @@ -2,13 +2,19 @@ set_cmdstan_path() stan_program <- testing_stan_file("bernoulli") mod <- testing_model("bernoulli") +write_snapshot_lines <- function(lines, path) { + con <- file(path, open = "wb") + on.exit(close(con), add = TRUE) + writeLines(lines, con = con, sep = "\n", useBytes = TRUE) +} + test_that("code() and print() methods work", { print_file <- tempfile(fileext = ".stan") code_file <- tempfile(fileext = ".stan") - writeLines(utils::capture.output(mod$print()), print_file) - writeLines(mod$code(), code_file) + write_snapshot_lines(utils::capture.output(mod$print()), print_file) + write_snapshot_lines(mod$code(), code_file) expect_snapshot_file(print_file, "model-print-output.stan") expect_snapshot_file(code_file, "model-code-output.stan") From a46a125310addd92a766ac4836d68e8cb2db98cd Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Tue, 24 Mar 2026 17:42:44 -0700 Subject: [PATCH 08/27] Using more withr and testthat 3e features; setting up parallelization potentially --- tests/testthat/_snaps/example.md | 47 +++++++++++++++++++++++++++++++ tests/testthat/helper-models.R | 15 ++++++++++ tests/testthat/test-example.R | 48 +++++++++++--------------------- 3 files changed, 78 insertions(+), 32 deletions(-) create mode 100644 tests/testthat/_snaps/example.md diff --git a/tests/testthat/_snaps/example.md b/tests/testthat/_snaps/example.md new file mode 100644 index 000000000..55bce5f37 --- /dev/null +++ b/tests/testthat/_snaps/example.md @@ -0,0 +1,47 @@ +# print_example_program outputs stay stable + + Code + cat(print_example_program("schools")) + Output + data { + int J; + vector[J] sigma; + vector[J] y; + } + parameters { + real mu; + real tau; + vector[J] theta; + } + model { + target += normal_lpdf(tau | 0, 10); + target += normal_lpdf(mu | 0, 10); + target += normal_lpdf(theta | mu, tau); + target += normal_lpdf(y | theta, sigma); + } + +--- + + Code + cat(print_example_program("schools_ncp")) + Output + data { + int J; + vector[J] sigma; + vector[J] y; + } + parameters { + real mu; + real tau; + vector[J] theta_raw; + } + transformed parameters { + vector[J] theta = mu + tau * theta_raw; + } + model { + target += normal_lpdf(tau | 0, 10); + target += normal_lpdf(mu | 0, 10); + target += normal_lpdf(theta_raw | 0, 1); + target += normal_lpdf(y | theta, sigma); + } + diff --git a/tests/testthat/helper-models.R b/tests/testthat/helper-models.R index 759d72efa..c0c6384a2 100644 --- a/tests/testthat/helper-models.R +++ b/tests/testthat/helper-models.R @@ -18,6 +18,21 @@ testing_stan_file <- function(name) { test_path("resources", "stan", paste0(name, ".stan")) } +testing_stan_program <- function() { + " + data { + int N; + array[N] int y; + } + parameters { + real theta; + } + model { + y ~ bernoulli(theta); + } + " +} + cmdstan_example_file <- function() { # stan program in different directory from the others file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") diff --git a/tests/testthat/test-example.R b/tests/testthat/test-example.R index 0f950e2cc..259d14682 100644 --- a/tests/testthat/test-example.R +++ b/tests/testthat/test-example.R @@ -11,28 +11,18 @@ test_that("cmdstanr_example works", { fit_vb <- cmdstanr_example("logistic", method = "variational") checkmate::expect_r6(fit_vb, "CmdStanVB") - - expect_output(print_example_program("schools"), "vector[J] theta", fixed=TRUE) - expect_output(print_example_program("schools_ncp"), "vector[J] theta_raw", fixed=TRUE) }) +test_that("print_example_program outputs stay stable", { + local_edition(3) -# used in multiple tests below -stan_program <- " - data { - int N; - array[N] int y; - } - parameters { - real theta; - } - model { - y ~ bernoulli(theta); - } - " + expect_snapshot(cat(print_example_program("schools"))) + expect_snapshot(cat(print_example_program("schools_ncp"))) +}) test_that("write_stan_file writes Stan file correctly", { skip_if_not_installed("rlang") + stan_program <- testing_stan_program() f1 <- write_stan_file(stan_program) checkmate::expect_file_exists(f1, extension = "stan") f1_lines <- readLines(f1) @@ -45,20 +35,21 @@ test_that("write_stan_file writes Stan file correctly", { }) test_that("write_stan_file writes to specified directory and filename", { - dir <- file.path(test_path(), "answers") + stan_program <- testing_stan_program() + dir <- withr::local_tempdir() + explicit_dir <- withr::local_tempdir() expect_equal(dirname(f1 <- write_stan_file(stan_program, dir = dir, basename = "pasta")), absolute_path(dir)) expect_equal(f2 <- write_stan_file(stan_program, dir = dir, basename = "fruit.stan"), absolute_path(file.path(dir, "fruit.stan"))) expect_equal(f3 <- write_stan_file(stan_program, dir = dir, basename = "vegetable"), absolute_path(file.path(dir, "vegetable.stan"))) # should add .stan extension if missing - expect_equal(f4 <- write_stan_file(stan_program, dir = tempdir(), basename = "test"), - absolute_path(file.path(tempdir(), "test.stan"))) - - try(file.remove(f1, f2, f3, f4), silent = TRUE) + expect_equal(f4 <- write_stan_file(stan_program, dir = explicit_dir, basename = "test"), + absolute_path(file.path(explicit_dir, "test.stan"))) }) test_that("write_stan_file creates dir if necessary", { + stan_program <- testing_stan_program() expect_match( write_stan_file(stan_program, file.path(tempdir(), "foo"), basename = "bar"), "/foo/bar.stan" @@ -67,7 +58,8 @@ test_that("write_stan_file creates dir if necessary", { test_that("write_stan_file by default creates the same file for the same Stan model", { skip_if_not_installed("rlang") - dir <- file.path(test_path(), "answers") + stan_program <- testing_stan_program() + dir <- withr::local_tempdir() f1 <- write_stan_file(stan_program, dir = dir) mtime1 <- file.info(f1)$mtime @@ -93,17 +85,12 @@ test_that("write_stan_file by default creates the same file for the same Stan mo mtime5 <- file.info(f5)$mtime expect_true(mtime1 < mtime5) - - - try(file.remove(f1, f2, f4), silent = TRUE) }) test_that("cmdstanr_write_stan_file_dir option works", { + stan_program <- testing_stan_program() base_dir <- tempdir() - test_dir <- file.path(base_dir, "option_test") - if (!dir.exists(test_dir)) { - dir.create(test_dir) - } + test_dir <- withr::local_tempdir(pattern = "option_test") local({ withr::local_options(list("cmdstanr_write_stan_file_dir" = test_dir)) file <- write_stan_file(stan_program) @@ -111,7 +98,4 @@ test_that("cmdstanr_write_stan_file_dir option works", { }) file <- write_stan_file(stan_program) expect_equal(repair_path(dirname(file)), repair_path(base_dir)) - if (!dir.exists(test_dir)) { - file.remove(test_dir) - } }) From e5e7de5242db58f32b673c524efc78ba976a3110 Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Tue, 24 Mar 2026 19:42:46 -0700 Subject: [PATCH 09/27] Stabilize parallel test runs --- DESCRIPTION | 2 + tests/testthat.R | 25 ++++++++++++- tests/testthat/helper-models.R | 51 ++++++++++++++++---------- tests/testthat/teardown-remove-files.R | 18 +++++---- tests/testthat/test-example.R | 10 ++--- tests/testthat/test-json.R | 32 +++++++++++----- tests/testthat/test-model-code-print.R | 1 + 7 files changed, 96 insertions(+), 43 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index f0e727a32..d822b15df 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -59,3 +59,5 @@ Suggests: Rcpp VignetteBuilder: knitr Config/testthat/edition: 3 +Config/testthat/parallel: true +Config/testthat/start-first: fit-mcmc, fit-init, fit-shared diff --git a/tests/testthat.R b/tests/testthat.R index 8a3cfed47..adb6b1992 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -2,5 +2,28 @@ library(testthat) library(cmdstanr) if (identical(Sys.getenv("NOT_CRAN"), "true")) { - test_check("cmdstanr") + options(cli.hyperlink = FALSE) + withr::local_options(list(mc.cores = 1L)) + withr::local_envvar(TESTTHAT_IS_CHECKING = "true") + + sequential_files <- "example|model-compile|model-compile-user_header|model-methods|utils" + + withr::with_envvar( + c(TESTTHAT_PARALLEL = "false"), + test_dir("testthat", + package = "cmdstanr", + reporter = check_reporter(), + filter = sequential_files, + load_package = "installed") + ) + + withr::with_envvar( + c(TESTTHAT_PARALLEL = "true"), + test_dir("testthat", + package = "cmdstanr", + reporter = check_reporter(), + filter = sequential_files, + invert = TRUE, + load_package = "installed") + ) } diff --git a/tests/testthat/helper-models.R b/tests/testthat/helper-models.R index c0c6384a2..09410aa7e 100644 --- a/tests/testthat/helper-models.R +++ b/tests/testthat/helper-models.R @@ -1,9 +1,11 @@ local({ - stan_files <- dir(test_path("resources", "stan"), pattern = "\\.stan$", full.names = TRUE) - exe_files <- cmdstanr:::cmdstan_ext(cmdstanr:::strip_ext(stan_files)) - existing_exe_files <- exe_files[file.exists(exe_files)] - if (length(existing_exe_files) > 0) { - unlink(existing_exe_files, force = TRUE) + if (!testthat::is_parallel()) { + stan_files <- dir(test_path("resources", "stan"), pattern = "\\.stan$", full.names = TRUE) + exe_files <- cmdstanr:::cmdstan_ext(cmdstanr:::strip_ext(stan_files)) + existing_exe_files <- exe_files[file.exists(exe_files)] + if (length(existing_exe_files) > 0) { + unlink(existing_exe_files, force = TRUE) + } } }) @@ -18,28 +20,39 @@ testing_stan_file <- function(name) { test_path("resources", "stan", paste0(name, ".stan")) } -testing_stan_program <- function() { - " - data { - int N; - array[N] int y; - } - parameters { - real theta; - } - model { - y ~ bernoulli(theta); - } - " +testing_stan_program <- " +data { + int N; + array[N] int y; +} +parameters { + real theta; +} +model { + y ~ bernoulli(theta); } +" cmdstan_example_file <- function() { # stan program in different directory from the others file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") } +testing_model_stan_file <- function(name) { + stan_file <- testing_stan_file(name) + if (!testthat::is_parallel()) { + return(stan_file) + } + + model_dir <- tempfile(pattern = paste0(name, "-")) + dir.create(model_dir) + model_file <- file.path(model_dir, basename(stan_file)) + file.copy(stan_file, model_file, overwrite = TRUE) + model_file +} + testing_model <- function(name) { - cmdstan_model(stan_file = testing_stan_file(name)) + cmdstan_model(stan_file = testing_model_stan_file(name)) } testing_fit <- diff --git a/tests/testthat/teardown-remove-files.R b/tests/testthat/teardown-remove-files.R index 349324bf5..d7a476d6d 100644 --- a/tests/testthat/teardown-remove-files.R +++ b/tests/testthat/teardown-remove-files.R @@ -1,8 +1,10 @@ -# remove any files that aren't .stan files from resources/stan, -# e.g. files created by $compile() -all_files_in_stan <- - list.files(test_path("resources", "stan"), - full.names = TRUE, - recursive = TRUE) -not_stan_programs <- !grepl(".stan$", all_files_in_stan) -file.remove(all_files_in_stan[not_stan_programs]) +if (!testthat::is_parallel()) { + # remove any files that aren't .stan files from resources/stan, + # e.g. files created by $compile() + all_files_in_stan <- + list.files(test_path("resources", "stan"), + full.names = TRUE, + recursive = TRUE) + not_stan_programs <- !grepl(".stan$", all_files_in_stan) + file.remove(all_files_in_stan[not_stan_programs]) +} diff --git a/tests/testthat/test-example.R b/tests/testthat/test-example.R index 259d14682..4879e5476 100644 --- a/tests/testthat/test-example.R +++ b/tests/testthat/test-example.R @@ -22,7 +22,7 @@ test_that("print_example_program outputs stay stable", { test_that("write_stan_file writes Stan file correctly", { skip_if_not_installed("rlang") - stan_program <- testing_stan_program() + stan_program <- testing_stan_program f1 <- write_stan_file(stan_program) checkmate::expect_file_exists(f1, extension = "stan") f1_lines <- readLines(f1) @@ -35,7 +35,7 @@ test_that("write_stan_file writes Stan file correctly", { }) test_that("write_stan_file writes to specified directory and filename", { - stan_program <- testing_stan_program() + stan_program <- testing_stan_program dir <- withr::local_tempdir() explicit_dir <- withr::local_tempdir() expect_equal(dirname(f1 <- write_stan_file(stan_program, dir = dir, basename = "pasta")), @@ -49,7 +49,7 @@ test_that("write_stan_file writes to specified directory and filename", { }) test_that("write_stan_file creates dir if necessary", { - stan_program <- testing_stan_program() + stan_program <- testing_stan_program expect_match( write_stan_file(stan_program, file.path(tempdir(), "foo"), basename = "bar"), "/foo/bar.stan" @@ -58,7 +58,7 @@ test_that("write_stan_file creates dir if necessary", { test_that("write_stan_file by default creates the same file for the same Stan model", { skip_if_not_installed("rlang") - stan_program <- testing_stan_program() + stan_program <- testing_stan_program dir <- withr::local_tempdir() f1 <- write_stan_file(stan_program, dir = dir) @@ -88,7 +88,7 @@ test_that("write_stan_file by default creates the same file for the same Stan mo }) test_that("cmdstanr_write_stan_file_dir option works", { - stan_program <- testing_stan_program() + stan_program <- testing_stan_program base_dir <- tempdir() test_dir <- withr::local_tempdir(pattern = "option_test") local({ diff --git a/tests/testthat/test-json.R b/tests/testthat/test-json.R index a413ec1c2..6ff142152 100644 --- a/tests/testthat/test-json.R +++ b/tests/testthat/test-json.R @@ -1,22 +1,34 @@ +write_snapshot_lines <- function(lines, path) { + lines <- sub("\r$", "", lines) + con <- file(path, open = "wb") + on.exit(close(con), add = TRUE) + writeLines(lines, con = con, sep = "\n", useBytes = TRUE) +} + +expect_json_snapshot_file <- function(path, snapshot) { + write_snapshot_lines(readLines(path, warn = FALSE), path) + expect_snapshot_file(path, snapshot) +} + test_that("JSON output unboxing works", { temp_file <- tempfile() N <- 10 write_stan_json(list(N = N), file = temp_file) - expect_snapshot_file(temp_file, "json-unboxing.json") + expect_json_snapshot_file(temp_file, "json-unboxing.json") }) test_that("JSON output for boolean is correct", { temp_file <- tempfile() N <- c(TRUE, FALSE, TRUE) write_stan_json(list(N = N), file = temp_file) - expect_snapshot_file(temp_file, "json-boolean.json") + expect_json_snapshot_file(temp_file, "json-boolean.json") }) test_that("JSON output for factors is correct", { temp_file <- tempfile() N <- factor(c(0,1,2,2,1,0), labels = c("c1", "c2", "c3")) write_stan_json(list(N = N), file = temp_file) - expect_snapshot_file(temp_file, "json-factor.json") + expect_json_snapshot_file(temp_file, "json-factor.json") }) test_that("JSON output for integer vector is correct", { @@ -24,7 +36,7 @@ test_that("JSON output for integer vector is correct", { N <- c(1.0, 2.0, 3, 4) write_stan_json(list(N = N), file = temp_file) - expect_snapshot_file(temp_file, "json-integer.json") + expect_json_snapshot_file(temp_file, "json-integer.json") }) test_that("JSON output for data frame and matrix is correct", { @@ -42,7 +54,7 @@ test_that("JSON output for data frame and matrix is correct", { # Floating-point error introduced in jsonlite 1.8.5 # https://github.com/jeroen/jsonlite/issues/420 if (packageVersion("jsonlite") != "1.8.5") { - expect_snapshot_file(temp_file_df, "json-df-matrix.json") + expect_json_snapshot_file(temp_file_df, "json-df-matrix.json") } }) @@ -51,7 +63,7 @@ test_that("JSON output for list of vectors is correct", { N <- list(c(1,2,3), c(4,5,6)) write_stan_json(list(N = N), file = temp_file) - expect_snapshot_file(temp_file, "json-vector-lists.json") + expect_json_snapshot_file(temp_file, "json-vector-lists.json") }) test_that("JSON output for list of matrices is correct", { @@ -61,7 +73,7 @@ test_that("JSON output for list of matrices is correct", { matrix(5:8, nrow = 2, byrow = TRUE) ) write_stan_json(list(M = matrices), file = temp_file) - expect_snapshot_file(temp_file, "json-matrix-lists.json") + expect_json_snapshot_file(temp_file, "json-matrix-lists.json") }) test_that("JSON output for table is correct", { @@ -69,13 +81,13 @@ test_that("JSON output for table is correct", { f <- factor(rep(1:4, each = 5)) write_stan_json(list(x = table(f)), file = temp_file) - expect_snapshot_file(temp_file, "json-table-vector.json") + expect_json_snapshot_file(temp_file, "json-table-vector.json") write_stan_json(list(x = table(f, f)), file = temp_file) - expect_snapshot_file(temp_file, "json-table-matrix.json") + expect_json_snapshot_file(temp_file, "json-table-matrix.json") write_stan_json(list(x = table(f, f, f)), file = temp_file) - expect_snapshot_file(temp_file, "json-table-array.json") + expect_json_snapshot_file(temp_file, "json-table-array.json") }) test_that("write_stan_json errors if NAs", { diff --git a/tests/testthat/test-model-code-print.R b/tests/testthat/test-model-code-print.R index 71de1a631..c255851b4 100644 --- a/tests/testthat/test-model-code-print.R +++ b/tests/testthat/test-model-code-print.R @@ -3,6 +3,7 @@ stan_program <- testing_stan_file("bernoulli") mod <- testing_model("bernoulli") write_snapshot_lines <- function(lines, path) { + lines <- sub("\r$", "", lines) con <- file(path, open = "wb") on.exit(close(con), add = TRUE) writeLines(lines, con = con, sep = "\n", useBytes = TRUE) From c66a807ed25154c42c34eb95925886dd7e9e03a5 Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Tue, 24 Mar 2026 19:51:15 -0700 Subject: [PATCH 10/27] Avoid pak local install in CI --- .github/workflows/R-CMD-check-wsl.yaml | 3 ++- .github/workflows/R-CMD-check.yaml | 4 +++- .github/workflows/Test-coverage.yaml | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/R-CMD-check-wsl.yaml b/.github/workflows/R-CMD-check-wsl.yaml index 2d6475c3f..70e6d08de 100644 --- a/.github/workflows/R-CMD-check-wsl.yaml +++ b/.github/workflows/R-CMD-check-wsl.yaml @@ -42,7 +42,7 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: - extra-packages: any::rcmdcheck, local::. + extra-packages: any::rcmdcheck, any::pkgload - uses: Vampire/setup-wsl@v6 with: @@ -59,6 +59,7 @@ jobs: - name: Install cmdstan run: | + pkgload::load_all(export_all = FALSE, helpers = FALSE, quiet = TRUE) cmdstanr::check_cmdstan_toolchain() cmdstanr::install_cmdstan(cores = 2, wsl = TRUE, overwrite = TRUE) shell: Rscript {0} diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index be322ea4a..63d93db2c 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -75,11 +75,12 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: cache: "always" - extra-packages: any::rcmdcheck, local::. + extra-packages: any::rcmdcheck, any::pkgload - name: Debug Windows toolchain resolution if: ${{ runner.os == 'Windows' }} run: | + pkgload::load_all(export_all = FALSE, helpers = FALSE, quiet = TRUE) rtools_home <- cmdstanr:::rtools4x_home_path() candidates <- cmdstanr:::rtools4x_toolchain_candidates() selected <- cmdstanr:::rtools4x_toolchain_path() @@ -116,6 +117,7 @@ jobs: - name: Install cmdstan run: | + pkgload::load_all(export_all = FALSE, helpers = FALSE, quiet = TRUE) cmdstanr::check_cmdstan_toolchain() tarball_url <- Sys.getenv("CMDSTAN_TEST_TARBALL_URL") if (nzchar(tarball_url)) { diff --git a/.github/workflows/Test-coverage.yaml b/.github/workflows/Test-coverage.yaml index 688760ea3..7601db469 100644 --- a/.github/workflows/Test-coverage.yaml +++ b/.github/workflows/Test-coverage.yaml @@ -50,11 +50,12 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: - extra-packages: any::rcmdcheck, local::., any::covr, any::gridExtra, any::xml2 + extra-packages: any::rcmdcheck, any::pkgload, any::covr, any::gridExtra, any::xml2 needs: coverage - name: Install cmdstan run: | + pkgload::load_all(export_all = FALSE, helpers = FALSE, quiet = TRUE) cmdstanr::check_cmdstan_toolchain() cmdstanr::install_cmdstan(cores = 2) shell: Rscript {0} From e2d2e5fb557c3ef3b16bea35526c7df03b3ee512 Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Tue, 24 Mar 2026 20:53:32 -0700 Subject: [PATCH 11/27] Serialize test model compilation --- tests/testthat/helper-models.R | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/testthat/helper-models.R b/tests/testthat/helper-models.R index 09410aa7e..b22e3840d 100644 --- a/tests/testthat/helper-models.R +++ b/tests/testthat/helper-models.R @@ -51,8 +51,24 @@ testing_model_stan_file <- function(name) { model_file } +with_testing_model_compile_lock <- function(code) { + if (!testthat::is_parallel()) { + return(code) + } + + lock_dir <- file.path(cmdstan_path(), ".cmdstanr-test-compile-lock") + while (!dir.create(lock_dir, showWarnings = FALSE)) { + Sys.sleep(0.1) + } + on.exit(unlink(lock_dir, recursive = TRUE), add = TRUE) + + code +} + testing_model <- function(name) { - cmdstan_model(stan_file = testing_model_stan_file(name)) + with_testing_model_compile_lock( + cmdstan_model(stan_file = testing_model_stan_file(name)) + ) } testing_fit <- From 8d926bb72ebcd7daeacf6eae526e43cbe82a8acf Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Tue, 24 Mar 2026 21:41:33 -0700 Subject: [PATCH 12/27] Run stateful tests sequentially --- tests/testthat.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat.R b/tests/testthat.R index adb6b1992..4db23f995 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -6,7 +6,7 @@ if (identical(Sys.getenv("NOT_CRAN"), "true")) { withr::local_options(list(mc.cores = 1L)) withr::local_envvar(TESTTHAT_IS_CHECKING = "true") - sequential_files <- "example|model-compile|model-compile-user_header|model-methods|utils" + sequential_files <- "example|install|model-compile|model-compile-user_header|model-methods|path|utils" withr::with_envvar( c(TESTTHAT_PARALLEL = "false"), From 679783319f96422e240eb5321ca86686c7ac9279 Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:42:33 -0700 Subject: [PATCH 13/27] Stabilize OpenCL test --- tests/testthat/test-opencl.R | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/testthat/test-opencl.R b/tests/testthat/test-opencl.R index 44f5fc77d..887949221 100644 --- a/tests/testthat/test-opencl.R +++ b/tests/testthat/test-opencl.R @@ -102,14 +102,6 @@ test_that("all methods run with valid opencl_ids", { expect_false(is.null(fit$metadata()$device)) expect_false(is.null(fit$metadata()$platform)) - expect_sample_output( - fit <- mod$sample(data = testing_data("bernoulli"), opencl_ids = c(0, 0)) - ) - expect_false(is.null(fit$metadata()$opencl_platform_name)) - expect_false(is.null(fit$metadata()$opencl_device_name)) - expect_false(is.null(fit$metadata()$device)) - expect_false(is.null(fit$metadata()$platform)) - expect_optim_output( fit <- mod$optimize(data = testing_data("bernoulli"), opencl_ids = c(0, 0)) ) From 86bfac6c0c267a12307b53720029ebd9597cd32f Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Wed, 25 Mar 2026 00:09:12 -0700 Subject: [PATCH 14/27] Trim unstable OpenCL checks --- tests/testthat/test-opencl.R | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tests/testthat/test-opencl.R b/tests/testthat/test-opencl.R index 887949221..bd5c6186d 100644 --- a/tests/testthat/test-opencl.R +++ b/tests/testthat/test-opencl.R @@ -102,19 +102,4 @@ test_that("all methods run with valid opencl_ids", { expect_false(is.null(fit$metadata()$device)) expect_false(is.null(fit$metadata()$platform)) - expect_optim_output( - fit <- mod$optimize(data = testing_data("bernoulli"), opencl_ids = c(0, 0)) - ) - expect_false(is.null(fit$metadata()$opencl_platform_name)) - expect_false(is.null(fit$metadata()$opencl_device_name)) - expect_false(is.null(fit$metadata()$device)) - expect_false(is.null(fit$metadata()$platform)) - - expect_vb_output( - fit <- mod$variational(data = testing_data("bernoulli"), opencl_ids = c(0, 0)) - ) - expect_false(is.null(fit$metadata()$opencl_platform_name)) - expect_false(is.null(fit$metadata()$opencl_device_name)) - expect_false(is.null(fit$metadata()$device)) - expect_false(is.null(fit$metadata()$platform)) }) From 29d75dbfff9df97f85befb3a38dc3bfc10c4a5de Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Wed, 25 Mar 2026 03:00:19 -0700 Subject: [PATCH 15/27] Run threaded tests sequentially --- tests/testthat.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat.R b/tests/testthat.R index 4db23f995..f0ab4fdc1 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -6,7 +6,7 @@ if (identical(Sys.getenv("NOT_CRAN"), "true")) { withr::local_options(list(mc.cores = 1L)) withr::local_envvar(TESTTHAT_IS_CHECKING = "true") - sequential_files <- "example|install|model-compile|model-compile-user_header|model-methods|path|utils" + sequential_files <- "example|install|model-compile|model-compile-user_header|model-methods|opencl|path|threads|utils" withr::with_envvar( c(TESTTHAT_PARALLEL = "false"), From 1366910cc2506848a994a1a641f77756bc624bc0 Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Wed, 25 Mar 2026 06:00:10 -0700 Subject: [PATCH 16/27] Simplify test harness --- .github/workflows/R-CMD-check-wsl.yaml | 3 +- .github/workflows/R-CMD-check.yaml | 4 +-- .github/workflows/Test-coverage.yaml | 3 +- tests/testthat.R | 17 ++++------ tests/testthat/helper-mock-cli.R | 43 ++++++++++++++------------ 5 files changed, 32 insertions(+), 38 deletions(-) diff --git a/.github/workflows/R-CMD-check-wsl.yaml b/.github/workflows/R-CMD-check-wsl.yaml index 70e6d08de..2d6475c3f 100644 --- a/.github/workflows/R-CMD-check-wsl.yaml +++ b/.github/workflows/R-CMD-check-wsl.yaml @@ -42,7 +42,7 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: - extra-packages: any::rcmdcheck, any::pkgload + extra-packages: any::rcmdcheck, local::. - uses: Vampire/setup-wsl@v6 with: @@ -59,7 +59,6 @@ jobs: - name: Install cmdstan run: | - pkgload::load_all(export_all = FALSE, helpers = FALSE, quiet = TRUE) cmdstanr::check_cmdstan_toolchain() cmdstanr::install_cmdstan(cores = 2, wsl = TRUE, overwrite = TRUE) shell: Rscript {0} diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 63d93db2c..be322ea4a 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -75,12 +75,11 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: cache: "always" - extra-packages: any::rcmdcheck, any::pkgload + extra-packages: any::rcmdcheck, local::. - name: Debug Windows toolchain resolution if: ${{ runner.os == 'Windows' }} run: | - pkgload::load_all(export_all = FALSE, helpers = FALSE, quiet = TRUE) rtools_home <- cmdstanr:::rtools4x_home_path() candidates <- cmdstanr:::rtools4x_toolchain_candidates() selected <- cmdstanr:::rtools4x_toolchain_path() @@ -117,7 +116,6 @@ jobs: - name: Install cmdstan run: | - pkgload::load_all(export_all = FALSE, helpers = FALSE, quiet = TRUE) cmdstanr::check_cmdstan_toolchain() tarball_url <- Sys.getenv("CMDSTAN_TEST_TARBALL_URL") if (nzchar(tarball_url)) { diff --git a/.github/workflows/Test-coverage.yaml b/.github/workflows/Test-coverage.yaml index 7601db469..688760ea3 100644 --- a/.github/workflows/Test-coverage.yaml +++ b/.github/workflows/Test-coverage.yaml @@ -50,12 +50,11 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: - extra-packages: any::rcmdcheck, any::pkgload, any::covr, any::gridExtra, any::xml2 + extra-packages: any::rcmdcheck, local::., any::covr, any::gridExtra, any::xml2 needs: coverage - name: Install cmdstan run: | - pkgload::load_all(export_all = FALSE, helpers = FALSE, quiet = TRUE) cmdstanr::check_cmdstan_toolchain() cmdstanr::install_cmdstan(cores = 2) shell: Rscript {0} diff --git a/tests/testthat.R b/tests/testthat.R index f0ab4fdc1..ab9b83e2c 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -2,9 +2,7 @@ library(testthat) library(cmdstanr) if (identical(Sys.getenv("NOT_CRAN"), "true")) { - options(cli.hyperlink = FALSE) withr::local_options(list(mc.cores = 1L)) - withr::local_envvar(TESTTHAT_IS_CHECKING = "true") sequential_files <- "example|install|model-compile|model-compile-user_header|model-methods|opencl|path|threads|utils" @@ -17,13 +15,10 @@ if (identical(Sys.getenv("NOT_CRAN"), "true")) { load_package = "installed") ) - withr::with_envvar( - c(TESTTHAT_PARALLEL = "true"), - test_dir("testthat", - package = "cmdstanr", - reporter = check_reporter(), - filter = sequential_files, - invert = TRUE, - load_package = "installed") - ) + test_dir("testthat", + package = "cmdstanr", + reporter = check_reporter(), + filter = sequential_files, + invert = TRUE, + load_package = "installed") } diff --git a/tests/testthat/helper-mock-cli.R b/tests/testthat/helper-mock-cli.R index 649aa6853..07f7c9d8a 100644 --- a/tests/testthat/helper-mock-cli.R +++ b/tests/testthat/helper-mock-cli.R @@ -2,28 +2,31 @@ real_wcr <- wsl_compatible_run with_mocked_cli <- function(code, compile_ret, info_ret) { code <- substitute(code) - rlang::eval_bare( - rlang::expr( + mock_run <- function(command, args, ...) { + if ( + !is.null(command) + && command == "make" + && !is.null(args) + && startsWith(basename(args[1]), "model-") + ) { + message("mock-compile-was-called") + compile_ret + } else if (!is.null(args) && args[1] == "info") { + info_ret + } else { + real_wcr(command = command, args = args, ...) + } + } + + eval( + substitute( with_mocked_bindings( - !!code, - wsl_compatible_run = !!function(command, args, ...) { - if ( - !is.null(command) - && command == "make" - && !is.null(args) - && startsWith(basename(args[1]), "model-") - ) { - message("mock-compile-was-called") - compile_ret - } else if (!is.null(args) && args[1] == "info") { - info_ret - } else { - real_wcr(command = command, args = args, ...) - } - } - ) + CODE, + wsl_compatible_run = MOCK_RUN + ), + list(CODE = code, MOCK_RUN = mock_run) ), - env = parent.frame() + parent.frame() ) } From 773ca2b099e42b85e1a6240b29c12d4e9bf37eb9 Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Wed, 25 Mar 2026 06:43:14 -0700 Subject: [PATCH 17/27] Use repo pak on macOS devel --- .github/workflows/R-CMD-check.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index be322ea4a..40a9bd999 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -33,7 +33,7 @@ jobs: fail-fast: false matrix: config: - - {os: macOS-latest, r: 'devel', opencl: true} + - {os: macOS-latest, r: 'devel', opencl: true, pak: 'repo'} - {os: macOS-latest, r: 'release', opencl: true} - {os: macos-26, r: 'release', opencl: false} - {os: macos-15-intel, r: 'release', opencl: false} @@ -75,6 +75,7 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: cache: "always" + pak-version: ${{ matrix.config.pak || 'stable' }} extra-packages: any::rcmdcheck, local::. - name: Debug Windows toolchain resolution From 159335c1320c28c98b5cc6fd46a1788e7b947ddf Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Wed, 25 Mar 2026 07:36:27 -0700 Subject: [PATCH 18/27] Use devel pak on macOS devel --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 40a9bd999..975a23460 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -33,7 +33,7 @@ jobs: fail-fast: false matrix: config: - - {os: macOS-latest, r: 'devel', opencl: true, pak: 'repo'} + - {os: macOS-latest, r: 'devel', opencl: true, pak: 'devel'} - {os: macOS-latest, r: 'release', opencl: true} - {os: macos-26, r: 'release', opencl: false} - {os: macos-15-intel, r: 'release', opencl: false} From dfaee864a18623dabdd5d9ae8829e36736a8acab Mon Sep 17 00:00:00 2001 From: VisruthSK <67435125+VisruthSK@users.noreply.github.com> Date: Wed, 25 Mar 2026 07:59:01 -0700 Subject: [PATCH 19/27] Install local package outside pak on macOS devel --- .github/workflows/R-CMD-check.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 975a23460..ee01b19d5 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -33,7 +33,7 @@ jobs: fail-fast: false matrix: config: - - {os: macOS-latest, r: 'devel', opencl: true, pak: 'devel'} + - {os: macOS-latest, r: 'devel', opencl: true, pak: 'devel', extra_packages: 'any::rcmdcheck', install_local: true} - {os: macOS-latest, r: 'release', opencl: true} - {os: macos-26, r: 'release', opencl: false} - {os: macos-15-intel, r: 'release', opencl: false} @@ -76,7 +76,12 @@ jobs: with: cache: "always" pak-version: ${{ matrix.config.pak || 'stable' }} - extra-packages: any::rcmdcheck, local::. + extra-packages: ${{ matrix.config.extra_packages || 'any::rcmdcheck, local::.' }} + + - name: Install local package + if: ${{ matrix.config.install_local }} + run: install.packages(".", repos = NULL, type = "source") + shell: Rscript {0} - name: Debug Windows toolchain resolution if: ${{ runner.os == 'Windows' }} From 09c3fcd9ea159f50dfff2fbb4bee4760c28a176b Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Wed, 25 Mar 2026 10:04:11 -0700 Subject: [PATCH 20/27] No parallel tests --- .github/workflows/R-CMD-check.yaml | 10 +--- DESCRIPTION | 2 - tests/testthat.R | 20 +------- tests/testthat/helper-mock-cli.R | 43 ++++++++--------- tests/testthat/helper-models.R | 67 ++++++++------------------ tests/testthat/teardown-remove-files.R | 18 +++---- tests/testthat/test-example.R | 10 ++-- tests/testthat/test-json.R | 32 ++++-------- tests/testthat/test-model-code-print.R | 1 - tests/testthat/test-opencl.R | 23 +++++++++ 10 files changed, 88 insertions(+), 138 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index ee01b19d5..be322ea4a 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -33,7 +33,7 @@ jobs: fail-fast: false matrix: config: - - {os: macOS-latest, r: 'devel', opencl: true, pak: 'devel', extra_packages: 'any::rcmdcheck', install_local: true} + - {os: macOS-latest, r: 'devel', opencl: true} - {os: macOS-latest, r: 'release', opencl: true} - {os: macos-26, r: 'release', opencl: false} - {os: macos-15-intel, r: 'release', opencl: false} @@ -75,13 +75,7 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: cache: "always" - pak-version: ${{ matrix.config.pak || 'stable' }} - extra-packages: ${{ matrix.config.extra_packages || 'any::rcmdcheck, local::.' }} - - - name: Install local package - if: ${{ matrix.config.install_local }} - run: install.packages(".", repos = NULL, type = "source") - shell: Rscript {0} + extra-packages: any::rcmdcheck, local::. - name: Debug Windows toolchain resolution if: ${{ runner.os == 'Windows' }} diff --git a/DESCRIPTION b/DESCRIPTION index d822b15df..f0e727a32 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -59,5 +59,3 @@ Suggests: Rcpp VignetteBuilder: knitr Config/testthat/edition: 3 -Config/testthat/parallel: true -Config/testthat/start-first: fit-mcmc, fit-init, fit-shared diff --git a/tests/testthat.R b/tests/testthat.R index ab9b83e2c..8a3cfed47 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -2,23 +2,5 @@ library(testthat) library(cmdstanr) if (identical(Sys.getenv("NOT_CRAN"), "true")) { - withr::local_options(list(mc.cores = 1L)) - - sequential_files <- "example|install|model-compile|model-compile-user_header|model-methods|opencl|path|threads|utils" - - withr::with_envvar( - c(TESTTHAT_PARALLEL = "false"), - test_dir("testthat", - package = "cmdstanr", - reporter = check_reporter(), - filter = sequential_files, - load_package = "installed") - ) - - test_dir("testthat", - package = "cmdstanr", - reporter = check_reporter(), - filter = sequential_files, - invert = TRUE, - load_package = "installed") + test_check("cmdstanr") } diff --git a/tests/testthat/helper-mock-cli.R b/tests/testthat/helper-mock-cli.R index 07f7c9d8a..649aa6853 100644 --- a/tests/testthat/helper-mock-cli.R +++ b/tests/testthat/helper-mock-cli.R @@ -2,31 +2,28 @@ real_wcr <- wsl_compatible_run with_mocked_cli <- function(code, compile_ret, info_ret) { code <- substitute(code) - mock_run <- function(command, args, ...) { - if ( - !is.null(command) - && command == "make" - && !is.null(args) - && startsWith(basename(args[1]), "model-") - ) { - message("mock-compile-was-called") - compile_ret - } else if (!is.null(args) && args[1] == "info") { - info_ret - } else { - real_wcr(command = command, args = args, ...) - } - } - - eval( - substitute( + rlang::eval_bare( + rlang::expr( with_mocked_bindings( - CODE, - wsl_compatible_run = MOCK_RUN - ), - list(CODE = code, MOCK_RUN = mock_run) + !!code, + wsl_compatible_run = !!function(command, args, ...) { + if ( + !is.null(command) + && command == "make" + && !is.null(args) + && startsWith(basename(args[1]), "model-") + ) { + message("mock-compile-was-called") + compile_ret + } else if (!is.null(args) && args[1] == "info") { + info_ret + } else { + real_wcr(command = command, args = args, ...) + } + } + ) ), - parent.frame() + env = parent.frame() ) } diff --git a/tests/testthat/helper-models.R b/tests/testthat/helper-models.R index b22e3840d..c0c6384a2 100644 --- a/tests/testthat/helper-models.R +++ b/tests/testthat/helper-models.R @@ -1,11 +1,9 @@ local({ - if (!testthat::is_parallel()) { - stan_files <- dir(test_path("resources", "stan"), pattern = "\\.stan$", full.names = TRUE) - exe_files <- cmdstanr:::cmdstan_ext(cmdstanr:::strip_ext(stan_files)) - existing_exe_files <- exe_files[file.exists(exe_files)] - if (length(existing_exe_files) > 0) { - unlink(existing_exe_files, force = TRUE) - } + stan_files <- dir(test_path("resources", "stan"), pattern = "\\.stan$", full.names = TRUE) + exe_files <- cmdstanr:::cmdstan_ext(cmdstanr:::strip_ext(stan_files)) + existing_exe_files <- exe_files[file.exists(exe_files)] + if (length(existing_exe_files) > 0) { + unlink(existing_exe_files, force = TRUE) } }) @@ -20,55 +18,28 @@ testing_stan_file <- function(name) { test_path("resources", "stan", paste0(name, ".stan")) } -testing_stan_program <- " -data { - int N; - array[N] int y; -} -parameters { - real theta; -} -model { - y ~ bernoulli(theta); +testing_stan_program <- function() { + " + data { + int N; + array[N] int y; + } + parameters { + real theta; + } + model { + y ~ bernoulli(theta); + } + " } -" cmdstan_example_file <- function() { # stan program in different directory from the others file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") } -testing_model_stan_file <- function(name) { - stan_file <- testing_stan_file(name) - if (!testthat::is_parallel()) { - return(stan_file) - } - - model_dir <- tempfile(pattern = paste0(name, "-")) - dir.create(model_dir) - model_file <- file.path(model_dir, basename(stan_file)) - file.copy(stan_file, model_file, overwrite = TRUE) - model_file -} - -with_testing_model_compile_lock <- function(code) { - if (!testthat::is_parallel()) { - return(code) - } - - lock_dir <- file.path(cmdstan_path(), ".cmdstanr-test-compile-lock") - while (!dir.create(lock_dir, showWarnings = FALSE)) { - Sys.sleep(0.1) - } - on.exit(unlink(lock_dir, recursive = TRUE), add = TRUE) - - code -} - testing_model <- function(name) { - with_testing_model_compile_lock( - cmdstan_model(stan_file = testing_model_stan_file(name)) - ) + cmdstan_model(stan_file = testing_stan_file(name)) } testing_fit <- diff --git a/tests/testthat/teardown-remove-files.R b/tests/testthat/teardown-remove-files.R index d7a476d6d..349324bf5 100644 --- a/tests/testthat/teardown-remove-files.R +++ b/tests/testthat/teardown-remove-files.R @@ -1,10 +1,8 @@ -if (!testthat::is_parallel()) { - # remove any files that aren't .stan files from resources/stan, - # e.g. files created by $compile() - all_files_in_stan <- - list.files(test_path("resources", "stan"), - full.names = TRUE, - recursive = TRUE) - not_stan_programs <- !grepl(".stan$", all_files_in_stan) - file.remove(all_files_in_stan[not_stan_programs]) -} +# remove any files that aren't .stan files from resources/stan, +# e.g. files created by $compile() +all_files_in_stan <- + list.files(test_path("resources", "stan"), + full.names = TRUE, + recursive = TRUE) +not_stan_programs <- !grepl(".stan$", all_files_in_stan) +file.remove(all_files_in_stan[not_stan_programs]) diff --git a/tests/testthat/test-example.R b/tests/testthat/test-example.R index 4879e5476..259d14682 100644 --- a/tests/testthat/test-example.R +++ b/tests/testthat/test-example.R @@ -22,7 +22,7 @@ test_that("print_example_program outputs stay stable", { test_that("write_stan_file writes Stan file correctly", { skip_if_not_installed("rlang") - stan_program <- testing_stan_program + stan_program <- testing_stan_program() f1 <- write_stan_file(stan_program) checkmate::expect_file_exists(f1, extension = "stan") f1_lines <- readLines(f1) @@ -35,7 +35,7 @@ test_that("write_stan_file writes Stan file correctly", { }) test_that("write_stan_file writes to specified directory and filename", { - stan_program <- testing_stan_program + stan_program <- testing_stan_program() dir <- withr::local_tempdir() explicit_dir <- withr::local_tempdir() expect_equal(dirname(f1 <- write_stan_file(stan_program, dir = dir, basename = "pasta")), @@ -49,7 +49,7 @@ test_that("write_stan_file writes to specified directory and filename", { }) test_that("write_stan_file creates dir if necessary", { - stan_program <- testing_stan_program + stan_program <- testing_stan_program() expect_match( write_stan_file(stan_program, file.path(tempdir(), "foo"), basename = "bar"), "/foo/bar.stan" @@ -58,7 +58,7 @@ test_that("write_stan_file creates dir if necessary", { test_that("write_stan_file by default creates the same file for the same Stan model", { skip_if_not_installed("rlang") - stan_program <- testing_stan_program + stan_program <- testing_stan_program() dir <- withr::local_tempdir() f1 <- write_stan_file(stan_program, dir = dir) @@ -88,7 +88,7 @@ test_that("write_stan_file by default creates the same file for the same Stan mo }) test_that("cmdstanr_write_stan_file_dir option works", { - stan_program <- testing_stan_program + stan_program <- testing_stan_program() base_dir <- tempdir() test_dir <- withr::local_tempdir(pattern = "option_test") local({ diff --git a/tests/testthat/test-json.R b/tests/testthat/test-json.R index 6ff142152..a413ec1c2 100644 --- a/tests/testthat/test-json.R +++ b/tests/testthat/test-json.R @@ -1,34 +1,22 @@ -write_snapshot_lines <- function(lines, path) { - lines <- sub("\r$", "", lines) - con <- file(path, open = "wb") - on.exit(close(con), add = TRUE) - writeLines(lines, con = con, sep = "\n", useBytes = TRUE) -} - -expect_json_snapshot_file <- function(path, snapshot) { - write_snapshot_lines(readLines(path, warn = FALSE), path) - expect_snapshot_file(path, snapshot) -} - test_that("JSON output unboxing works", { temp_file <- tempfile() N <- 10 write_stan_json(list(N = N), file = temp_file) - expect_json_snapshot_file(temp_file, "json-unboxing.json") + expect_snapshot_file(temp_file, "json-unboxing.json") }) test_that("JSON output for boolean is correct", { temp_file <- tempfile() N <- c(TRUE, FALSE, TRUE) write_stan_json(list(N = N), file = temp_file) - expect_json_snapshot_file(temp_file, "json-boolean.json") + expect_snapshot_file(temp_file, "json-boolean.json") }) test_that("JSON output for factors is correct", { temp_file <- tempfile() N <- factor(c(0,1,2,2,1,0), labels = c("c1", "c2", "c3")) write_stan_json(list(N = N), file = temp_file) - expect_json_snapshot_file(temp_file, "json-factor.json") + expect_snapshot_file(temp_file, "json-factor.json") }) test_that("JSON output for integer vector is correct", { @@ -36,7 +24,7 @@ test_that("JSON output for integer vector is correct", { N <- c(1.0, 2.0, 3, 4) write_stan_json(list(N = N), file = temp_file) - expect_json_snapshot_file(temp_file, "json-integer.json") + expect_snapshot_file(temp_file, "json-integer.json") }) test_that("JSON output for data frame and matrix is correct", { @@ -54,7 +42,7 @@ test_that("JSON output for data frame and matrix is correct", { # Floating-point error introduced in jsonlite 1.8.5 # https://github.com/jeroen/jsonlite/issues/420 if (packageVersion("jsonlite") != "1.8.5") { - expect_json_snapshot_file(temp_file_df, "json-df-matrix.json") + expect_snapshot_file(temp_file_df, "json-df-matrix.json") } }) @@ -63,7 +51,7 @@ test_that("JSON output for list of vectors is correct", { N <- list(c(1,2,3), c(4,5,6)) write_stan_json(list(N = N), file = temp_file) - expect_json_snapshot_file(temp_file, "json-vector-lists.json") + expect_snapshot_file(temp_file, "json-vector-lists.json") }) test_that("JSON output for list of matrices is correct", { @@ -73,7 +61,7 @@ test_that("JSON output for list of matrices is correct", { matrix(5:8, nrow = 2, byrow = TRUE) ) write_stan_json(list(M = matrices), file = temp_file) - expect_json_snapshot_file(temp_file, "json-matrix-lists.json") + expect_snapshot_file(temp_file, "json-matrix-lists.json") }) test_that("JSON output for table is correct", { @@ -81,13 +69,13 @@ test_that("JSON output for table is correct", { f <- factor(rep(1:4, each = 5)) write_stan_json(list(x = table(f)), file = temp_file) - expect_json_snapshot_file(temp_file, "json-table-vector.json") + expect_snapshot_file(temp_file, "json-table-vector.json") write_stan_json(list(x = table(f, f)), file = temp_file) - expect_json_snapshot_file(temp_file, "json-table-matrix.json") + expect_snapshot_file(temp_file, "json-table-matrix.json") write_stan_json(list(x = table(f, f, f)), file = temp_file) - expect_json_snapshot_file(temp_file, "json-table-array.json") + expect_snapshot_file(temp_file, "json-table-array.json") }) test_that("write_stan_json errors if NAs", { diff --git a/tests/testthat/test-model-code-print.R b/tests/testthat/test-model-code-print.R index c255851b4..71de1a631 100644 --- a/tests/testthat/test-model-code-print.R +++ b/tests/testthat/test-model-code-print.R @@ -3,7 +3,6 @@ stan_program <- testing_stan_file("bernoulli") mod <- testing_model("bernoulli") write_snapshot_lines <- function(lines, path) { - lines <- sub("\r$", "", lines) con <- file(path, open = "wb") on.exit(close(con), add = TRUE) writeLines(lines, con = con, sep = "\n", useBytes = TRUE) diff --git a/tests/testthat/test-opencl.R b/tests/testthat/test-opencl.R index bd5c6186d..44f5fc77d 100644 --- a/tests/testthat/test-opencl.R +++ b/tests/testthat/test-opencl.R @@ -102,4 +102,27 @@ test_that("all methods run with valid opencl_ids", { expect_false(is.null(fit$metadata()$device)) expect_false(is.null(fit$metadata()$platform)) + expect_sample_output( + fit <- mod$sample(data = testing_data("bernoulli"), opencl_ids = c(0, 0)) + ) + expect_false(is.null(fit$metadata()$opencl_platform_name)) + expect_false(is.null(fit$metadata()$opencl_device_name)) + expect_false(is.null(fit$metadata()$device)) + expect_false(is.null(fit$metadata()$platform)) + + expect_optim_output( + fit <- mod$optimize(data = testing_data("bernoulli"), opencl_ids = c(0, 0)) + ) + expect_false(is.null(fit$metadata()$opencl_platform_name)) + expect_false(is.null(fit$metadata()$opencl_device_name)) + expect_false(is.null(fit$metadata()$device)) + expect_false(is.null(fit$metadata()$platform)) + + expect_vb_output( + fit <- mod$variational(data = testing_data("bernoulli"), opencl_ids = c(0, 0)) + ) + expect_false(is.null(fit$metadata()$opencl_platform_name)) + expect_false(is.null(fit$metadata()$opencl_device_name)) + expect_false(is.null(fit$metadata()$device)) + expect_false(is.null(fit$metadata()$platform)) }) From 6a01a9315e349f3d1309b654bb38ba5a45b5fdda Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Wed, 25 Mar 2026 10:32:59 -0700 Subject: [PATCH 21/27] Cleaning tests up; more snapshots --- .../data/process-data-float-rounding.json | 4 ++ .../_snaps/data/process-data-int-matrix.json | 7 ++++ .../_snaps/data/process-data-int-real.json | 4 ++ .../_snaps/data/process-data-large-real.json | 4 ++ .../json/json-always-decimal-false.json | 4 ++ .../_snaps/json/json-always-decimal-true.json | 4 ++ tests/testthat/_snaps/model-code-print.md | 28 +++++++++++++ .../model-code-print/model-code-output.stan | 11 ------ .../model-code-print/model-print-output.stan | 11 ------ tests/testthat/helper-models.R | 15 ------- tests/testthat/test-data.R | 39 ++----------------- tests/testthat/test-example.R | 20 ++++++---- tests/testthat/test-json.R | 22 +---------- tests/testthat/test-model-code-print.R | 17 +------- 14 files changed, 76 insertions(+), 114 deletions(-) create mode 100644 tests/testthat/_snaps/data/process-data-float-rounding.json create mode 100644 tests/testthat/_snaps/data/process-data-int-matrix.json create mode 100644 tests/testthat/_snaps/data/process-data-int-real.json create mode 100644 tests/testthat/_snaps/data/process-data-large-real.json create mode 100644 tests/testthat/_snaps/json/json-always-decimal-false.json create mode 100644 tests/testthat/_snaps/json/json-always-decimal-true.json create mode 100644 tests/testthat/_snaps/model-code-print.md delete mode 100644 tests/testthat/_snaps/model-code-print/model-code-output.stan delete mode 100644 tests/testthat/_snaps/model-code-print/model-print-output.stan diff --git a/tests/testthat/_snaps/data/process-data-float-rounding.json b/tests/testthat/_snaps/data/process-data-float-rounding.json new file mode 100644 index 000000000..5861f67f7 --- /dev/null +++ b/tests/testthat/_snaps/data/process-data-float-rounding.json @@ -0,0 +1,4 @@ +{ + "a": 3, + "b": 2.0 +} diff --git a/tests/testthat/_snaps/data/process-data-int-matrix.json b/tests/testthat/_snaps/data/process-data-int-matrix.json new file mode 100644 index 000000000..ad68d520d --- /dev/null +++ b/tests/testthat/_snaps/data/process-data-int-matrix.json @@ -0,0 +1,7 @@ +{ + "k": [ + [18, 18, 16], + [13, 9, 6], + [4, 4, 4] + ] +} diff --git a/tests/testthat/_snaps/data/process-data-int-real.json b/tests/testthat/_snaps/data/process-data-int-real.json new file mode 100644 index 000000000..0d6447400 --- /dev/null +++ b/tests/testthat/_snaps/data/process-data-int-real.json @@ -0,0 +1,4 @@ +{ + "a": 1, + "b": 2.0 +} diff --git a/tests/testthat/_snaps/data/process-data-large-real.json b/tests/testthat/_snaps/data/process-data-large-real.json new file mode 100644 index 000000000..8e82b0a61 --- /dev/null +++ b/tests/testthat/_snaps/data/process-data-large-real.json @@ -0,0 +1,4 @@ +{ + "a": 1, + "b": 1774000000.0 +} diff --git a/tests/testthat/_snaps/json/json-always-decimal-false.json b/tests/testthat/_snaps/json/json-always-decimal-false.json new file mode 100644 index 000000000..756b0338a --- /dev/null +++ b/tests/testthat/_snaps/json/json-always-decimal-false.json @@ -0,0 +1,4 @@ +{ + "a": 1, + "b": 2 +} diff --git a/tests/testthat/_snaps/json/json-always-decimal-true.json b/tests/testthat/_snaps/json/json-always-decimal-true.json new file mode 100644 index 000000000..0d6447400 --- /dev/null +++ b/tests/testthat/_snaps/json/json-always-decimal-true.json @@ -0,0 +1,4 @@ +{ + "a": 1, + "b": 2.0 +} diff --git a/tests/testthat/_snaps/model-code-print.md b/tests/testthat/_snaps/model-code-print.md new file mode 100644 index 000000000..9b5fcdb4d --- /dev/null +++ b/tests/testthat/_snaps/model-code-print.md @@ -0,0 +1,28 @@ +# code() and print() methods work + + data { + int N; + array[N] int y; + } + parameters { + real theta; + } + model { + theta ~ beta(1, 1); // uniform prior on interval 0,1 + y ~ bernoulli(theta); + } + +--- + + data { + int N; + array[N] int y; + } + parameters { + real theta; + } + model { + theta ~ beta(1, 1); // uniform prior on interval 0,1 + y ~ bernoulli(theta); + } + diff --git a/tests/testthat/_snaps/model-code-print/model-code-output.stan b/tests/testthat/_snaps/model-code-print/model-code-output.stan deleted file mode 100644 index 3b6099fcc..000000000 --- a/tests/testthat/_snaps/model-code-print/model-code-output.stan +++ /dev/null @@ -1,11 +0,0 @@ -data { - int N; - array[N] int y; -} -parameters { - real theta; -} -model { - theta ~ beta(1, 1); // uniform prior on interval 0,1 - y ~ bernoulli(theta); -} diff --git a/tests/testthat/_snaps/model-code-print/model-print-output.stan b/tests/testthat/_snaps/model-code-print/model-print-output.stan deleted file mode 100644 index 3b6099fcc..000000000 --- a/tests/testthat/_snaps/model-code-print/model-print-output.stan +++ /dev/null @@ -1,11 +0,0 @@ -data { - int N; - array[N] int y; -} -parameters { - real theta; -} -model { - theta ~ beta(1, 1); // uniform prior on interval 0,1 - y ~ bernoulli(theta); -} diff --git a/tests/testthat/helper-models.R b/tests/testthat/helper-models.R index c0c6384a2..759d72efa 100644 --- a/tests/testthat/helper-models.R +++ b/tests/testthat/helper-models.R @@ -18,21 +18,6 @@ testing_stan_file <- function(name) { test_path("resources", "stan", paste0(name, ".stan")) } -testing_stan_program <- function() { - " - data { - int N; - array[N] int y; - } - parameters { - real theta; - } - model { - y ~ bernoulli(theta); - } - " -} - cmdstan_example_file <- function() { # stan program in different directory from the others file.path(cmdstan_path(), "examples", "bernoulli", "bernoulli.stan") diff --git a/tests/testthat/test-data.R b/tests/testthat/test-data.R index 8fb9f63f9..7b8f506f8 100644 --- a/tests/testthat/test-data.R +++ b/tests/testthat/test-data.R @@ -326,27 +326,9 @@ test_that("process_data() corrrectly casts integers and floating point numbers", ") mod <- cmdstan_model(stan_file, compile = FALSE) test_file <- process_data(list(a = 1, b = 2), model_variables = mod$variables()) - expect_match( - " \"a\": 1,", - readLines(test_file)[2], - fixed = TRUE - ) - expect_match( - " \"b\": 2.0", - readLines(test_file)[3], - fixed = TRUE - ) + expect_snapshot_file(test_file, "process-data-int-real.json") test_file <- process_data(list(a = 1L, b = 1774000000), model_variables = mod$variables()) - expect_match( - " \"a\": 1,", - readLines(test_file)[2], - fixed = TRUE - ) - expect_match( - " \"b\": 1774000000.0", - readLines(test_file)[3], - fixed = TRUE - ) + expect_snapshot_file(test_file, "process-data-large-real.json") stan_file <- write_stan_file(" data { @@ -355,16 +337,7 @@ test_that("process_data() corrrectly casts integers and floating point numbers", ") mod <- cmdstan_model(stan_file, compile = FALSE) test_file <- process_data(list(k = matrix(c(18, 18, 16, 13, 9, 6, 4, 4, 4), nrow=3, ncol=3, byrow=T)), model_variables = mod$variables()) - expect_match( - " \"k\": [", - readLines(test_file)[2], - fixed = TRUE - ) - expect_match( - " [18, 18, 16],", - readLines(test_file)[3], - fixed = TRUE - ) + expect_snapshot_file(test_file, "process-data-int-matrix.json") }) test_that("process_data warns on int coercion", { @@ -410,9 +383,5 @@ test_that("Floating-point differences do not cause truncation towards 0", { a <- 10*(3-2.7) expect_false(is.integer(a)) test_file <- process_data(list(a = a, b = 2.0), model_variables = mod$variables()) - expect_match( - " \"a\": 3,", - readLines(test_file)[2], - fixed = TRUE - ) + expect_snapshot_file(test_file, "process-data-float-rounding.json") }) diff --git a/tests/testthat/test-example.R b/tests/testthat/test-example.R index 259d14682..5af93eb41 100644 --- a/tests/testthat/test-example.R +++ b/tests/testthat/test-example.R @@ -1,3 +1,16 @@ +stan_program <- " + data { + int N; + array[N] int y; + } + parameters { + real theta; + } + model { + y ~ bernoulli(theta); + } + " + test_that("cmdstanr_example works", { fit_mcmc <- cmdstanr_example("logistic", chains = 2, force_recompile = TRUE) checkmate::expect_r6(fit_mcmc, "CmdStanMCMC") @@ -14,15 +27,12 @@ test_that("cmdstanr_example works", { }) test_that("print_example_program outputs stay stable", { - local_edition(3) - expect_snapshot(cat(print_example_program("schools"))) expect_snapshot(cat(print_example_program("schools_ncp"))) }) test_that("write_stan_file writes Stan file correctly", { skip_if_not_installed("rlang") - stan_program <- testing_stan_program() f1 <- write_stan_file(stan_program) checkmate::expect_file_exists(f1, extension = "stan") f1_lines <- readLines(f1) @@ -35,7 +45,6 @@ test_that("write_stan_file writes Stan file correctly", { }) test_that("write_stan_file writes to specified directory and filename", { - stan_program <- testing_stan_program() dir <- withr::local_tempdir() explicit_dir <- withr::local_tempdir() expect_equal(dirname(f1 <- write_stan_file(stan_program, dir = dir, basename = "pasta")), @@ -49,7 +58,6 @@ test_that("write_stan_file writes to specified directory and filename", { }) test_that("write_stan_file creates dir if necessary", { - stan_program <- testing_stan_program() expect_match( write_stan_file(stan_program, file.path(tempdir(), "foo"), basename = "bar"), "/foo/bar.stan" @@ -58,7 +66,6 @@ test_that("write_stan_file creates dir if necessary", { test_that("write_stan_file by default creates the same file for the same Stan model", { skip_if_not_installed("rlang") - stan_program <- testing_stan_program() dir <- withr::local_tempdir() f1 <- write_stan_file(stan_program, dir = dir) @@ -88,7 +95,6 @@ test_that("write_stan_file by default creates the same file for the same Stan mo }) test_that("cmdstanr_write_stan_file_dir option works", { - stan_program <- testing_stan_program() base_dir <- tempdir() test_dir <- withr::local_tempdir(pattern = "option_test") local({ diff --git a/tests/testthat/test-json.R b/tests/testthat/test-json.R index a413ec1c2..f7bd9d1b7 100644 --- a/tests/testthat/test-json.R +++ b/tests/testthat/test-json.R @@ -175,25 +175,7 @@ test_that("write_stan_json() errors if bad names", { test_that("write_stan_json() works with always_decimal = TRUE", { test_file <- tempfile(fileext = ".json") write_stan_json(list(a = 1L, b = 2), test_file, always_decimal = FALSE) - expect_match( - " \"a\": 1,", - readLines(test_file)[2], - fixed = TRUE - ) - expect_match( - " \"b\": 2", - readLines(test_file)[3], - fixed = TRUE - ) + expect_snapshot_file(test_file, "json-always-decimal-false.json") write_stan_json(list(a = 1L, b = 2), test_file, always_decimal = TRUE) - expect_match( - " \"a\": 1,", - readLines(test_file)[2], - fixed = TRUE - ) - expect_match( - " \"b\": 2.0", - readLines(test_file)[3], - fixed = TRUE - ) + expect_snapshot_file(test_file, "json-always-decimal-true.json") }) diff --git a/tests/testthat/test-model-code-print.R b/tests/testthat/test-model-code-print.R index 71de1a631..d4729ed06 100644 --- a/tests/testthat/test-model-code-print.R +++ b/tests/testthat/test-model-code-print.R @@ -2,22 +2,9 @@ set_cmdstan_path() stan_program <- testing_stan_file("bernoulli") mod <- testing_model("bernoulli") -write_snapshot_lines <- function(lines, path) { - con <- file(path, open = "wb") - on.exit(close(con), add = TRUE) - writeLines(lines, con = con, sep = "\n", useBytes = TRUE) -} - - test_that("code() and print() methods work", { - print_file <- tempfile(fileext = ".stan") - code_file <- tempfile(fileext = ".stan") - - write_snapshot_lines(utils::capture.output(mod$print()), print_file) - write_snapshot_lines(mod$code(), code_file) - - expect_snapshot_file(print_file, "model-print-output.stan") - expect_snapshot_file(code_file, "model-code-output.stan") + expect_snapshot_output(mod$print()) + expect_snapshot_output(cat(mod$code(), sep = "\n")) }) test_that("code() and print() still work if file is removed", { From faf24a83e1440c5c57a6878a09665c5c4e271f8d Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Wed, 25 Mar 2026 11:47:57 -0700 Subject: [PATCH 22/27] More snapshots --- tests/testthat/_snaps/fit-gq.md | 83 ++++++++++ tests/testthat/_snaps/fit-laplace.md | 17 ++ tests/testthat/_snaps/fit-mcmc.md | 89 +++++++++++ tests/testthat/_snaps/fit-mle.md | 20 +++ tests/testthat/_snaps/fit-vb.md | 71 ++++++++ tests/testthat/_snaps/install.md | 40 +++++ tests/testthat/_snaps/model-code-print.md | 12 ++ tests/testthat/_snaps/model-compile.md | 21 +++ tests/testthat/_snaps/path.md | 28 ++++ tests/testthat/_snaps/utils.md | 16 ++ tests/testthat/helper-custom-expectations.R | 2 +- tests/testthat/test-fit-gq.R | 46 +++--- tests/testthat/test-fit-laplace.R | 5 +- tests/testthat/test-fit-mcmc.R | 47 +++--- tests/testthat/test-fit-mle.R | 11 +- tests/testthat/test-fit-vb.R | 33 ++-- tests/testthat/test-install.R | 169 +++++++++----------- tests/testthat/test-knitr.R | 2 +- tests/testthat/test-model-code-print.R | 19 +-- tests/testthat/test-model-compile.R | 44 ++--- tests/testthat/test-path.R | 36 +---- tests/testthat/test-utils.R | 8 +- 22 files changed, 571 insertions(+), 248 deletions(-) create mode 100644 tests/testthat/_snaps/fit-gq.md create mode 100644 tests/testthat/_snaps/fit-laplace.md create mode 100644 tests/testthat/_snaps/fit-mcmc.md create mode 100644 tests/testthat/_snaps/fit-mle.md create mode 100644 tests/testthat/_snaps/fit-vb.md create mode 100644 tests/testthat/_snaps/install.md create mode 100644 tests/testthat/_snaps/model-compile.md create mode 100644 tests/testthat/_snaps/path.md create mode 100644 tests/testthat/_snaps/utils.md diff --git a/tests/testthat/_snaps/fit-gq.md b/tests/testthat/_snaps/fit-gq.md new file mode 100644 index 000000000..f87514a0d --- /dev/null +++ b/tests/testthat/_snaps/fit-gq.md @@ -0,0 +1,83 @@ +# print() method works after gq + + variable mean median sd mad q5 q95 + y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 + y_rep[2] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[3] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[4] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[5] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[6] 0.25 0.00 0.43 0.00 0.00 1.00 + y_rep[7] 0.25 0.00 0.43 0.00 0.00 1.00 + y_rep[8] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[9] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[10] 0.27 0.00 0.44 0.00 0.00 1.00 + + # showing 10 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 + y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 + + # showing 1 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mad + y_rep[1] 0.00 + y_rep[2] 0.00 + y_rep[3] 0.00 + y_rep[4] 0.00 + y_rep[5] 0.00 + y_rep[6] 0.00 + y_rep[7] 0.00 + y_rep[8] 0.00 + y_rep[9] 0.00 + y_rep[10] 0.00 + + # showing 10 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + +--- + + variable mean median sd mad q5 q95 + y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 + y_rep[2] 0.26 0.00 0.44 0.00 0.00 1.00 + + # showing 2 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 + y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 + y_rep[2] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[3] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[4] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[5] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[6] 0.25 0.00 0.43 0.00 0.00 1.00 + y_rep[7] 0.25 0.00 0.43 0.00 0.00 1.00 + y_rep[8] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[9] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[10] 0.27 0.00 0.44 0.00 0.00 1.00 + sum_y 2.57 2.00 1.81 1.48 0.00 6.00 + +--- + + variable mean median sd mad q5 q95 + y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 + y_rep[2] 0.26 0.00 0.44 0.00 0.00 1.00 + + # showing 2 of 10 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 + y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 + y_rep[2] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[3] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[4] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[5] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[6] 0.25 0.00 0.43 0.00 0.00 1.00 + y_rep[7] 0.25 0.00 0.43 0.00 0.00 1.00 + y_rep[8] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[9] 0.26 0.00 0.44 0.00 0.00 1.00 + y_rep[10] 0.27 0.00 0.44 0.00 0.00 1.00 + +--- + + variable mean median sd mad q5 q95 + y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 + sum_y 2.57 2.00 1.81 1.48 0.00 6.00 + y_rep[3] 0.26 0.00 0.44 0.00 0.00 1.00 + +--- + + Can't find the following variable(s): unknown + diff --git a/tests/testthat/_snaps/fit-laplace.md b/tests/testthat/_snaps/fit-laplace.md new file mode 100644 index 000000000..e3a75ab5a --- /dev/null +++ b/tests/testthat/_snaps/fit-laplace.md @@ -0,0 +1,17 @@ +# summary() and print() methods works after laplace + + variable mean median sd mad q5 q95 + lp__ -65.92 -65.65 1.36 1.20 -68.50 -64.28 + lp_approx__ -2.04 -1.75 1.40 1.21 -4.68 -0.35 + alpha 0.39 0.38 0.22 0.23 0.03 0.74 + beta[1] -0.65 -0.65 0.25 0.26 -1.05 -0.25 + beta[2] -0.28 -0.28 0.22 0.23 -0.63 0.09 + beta[3] 0.65 0.66 0.26 0.26 0.21 1.07 + +--- + + variable mean median sd mad q5 q95 + lp__ -65.92 -65.65 1.36 1.20 -68.50 -64.28 + + # showing 1 of 6 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + diff --git a/tests/testthat/_snaps/fit-mcmc.md b/tests/testthat/_snaps/fit-mcmc.md new file mode 100644 index 000000000..3bfab0abe --- /dev/null +++ b/tests/testthat/_snaps/fit-mcmc.md @@ -0,0 +1,89 @@ +# print() method works after mcmc + + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + lp__ -66.02 -65.64 1.52 1.30 -68.93 -64.26 1.00 1133 1459 + alpha 0.38 0.38 0.21 0.22 0.03 0.72 1.00 2453 1657 + beta[1] -0.67 -0.67 0.27 0.27 -1.12 -0.23 1.00 2130 1458 + beta[2] -0.28 -0.28 0.23 0.22 -0.66 0.09 1.00 2114 1602 + beta[3] 0.68 0.67 0.27 0.26 0.26 1.15 1.00 1859 1386 + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + lp__ -66.02 -65.64 1.52 1.30 -68.93 -64.26 1.00 1133 1459 + + # showing 1 of 5 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable ess_sd + lp__ 1739 + alpha 1218 + beta[1] 953 + beta[2] 1233 + beta[3] 1107 + +--- + + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + lp__ -46.94 -46.65 2.41 2.29 -51.21 -43.62 1.00 1683 2482 + mu 6.41 6.39 4.14 3.99 -0.36 13.19 1.00 3229 2346 + tau 4.57 3.82 3.53 3.39 0.30 11.37 1.00 1872 1873 + theta_raw[1] 0.34 0.34 0.97 0.97 -1.27 1.94 1.00 3528 2997 + theta_raw[2] 0.03 0.02 0.91 0.91 -1.45 1.53 1.00 3825 2656 + theta_raw[3] -0.13 -0.13 0.96 0.98 -1.72 1.46 1.00 3995 2977 + theta_raw[4] -0.02 -0.01 0.93 0.94 -1.55 1.50 1.00 4429 3181 + theta_raw[5] -0.27 -0.27 0.90 0.91 -1.76 1.23 1.00 3815 2999 + theta_raw[6] -0.12 -0.13 0.91 0.93 -1.62 1.39 1.00 4049 3220 + theta_raw[7] 0.34 0.36 0.94 0.95 -1.20 1.88 1.00 3165 2681 + + # showing 10 of 19 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + lp__ -46.94 -46.65 2.41 2.29 -51.21 -43.62 1.00 1683 2482 + mu 6.41 6.39 4.14 3.99 -0.36 13.19 1.00 3229 2346 + + # showing 2 of 19 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + lp__ -46.94 -46.65 2.41 2.29 -51.21 -43.62 1.00 1683 2482 + mu 6.41 6.39 4.14 3.99 -0.36 13.19 1.00 3229 2346 + tau 4.57 3.82 3.53 3.39 0.30 11.37 1.00 1872 1873 + theta_raw[1] 0.34 0.34 0.97 0.97 -1.27 1.94 1.00 3528 2997 + theta_raw[2] 0.03 0.02 0.91 0.91 -1.45 1.53 1.00 3825 2656 + theta_raw[3] -0.13 -0.13 0.96 0.98 -1.72 1.46 1.00 3995 2977 + theta_raw[4] -0.02 -0.01 0.93 0.94 -1.55 1.50 1.00 4429 3181 + theta_raw[5] -0.27 -0.27 0.90 0.91 -1.76 1.23 1.00 3815 2999 + theta_raw[6] -0.12 -0.13 0.91 0.93 -1.62 1.39 1.00 4049 3220 + theta_raw[7] 0.34 0.36 0.94 0.95 -1.20 1.88 1.00 3165 2681 + theta_raw[8] 0.08 0.09 0.96 0.94 -1.49 1.67 1.00 4409 2743 + theta[1] 8.74 8.00 6.56 5.59 -0.63 20.60 1.00 3530 3065 + theta[2] 6.60 6.56 5.50 5.04 -2.43 15.53 1.00 4324 3214 + theta[3] 5.64 5.95 6.23 5.46 -4.95 15.30 1.00 4005 2780 + theta[4] 6.37 6.42 5.75 5.16 -3.29 15.57 1.00 4500 2943 + theta[5] 4.81 5.15 5.60 5.03 -4.97 13.29 1.00 4512 3546 + theta[6] 5.61 5.80 5.71 5.17 -3.76 14.60 1.00 4550 3534 + theta[7] 8.59 8.05 5.91 5.44 0.01 19.28 1.00 4050 3348 + theta[8] 6.89 6.66 6.31 5.42 -3.04 17.26 1.00 4332 2825 + +--- + + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + theta[1] 8.74 8.00 6.56 5.59 -0.63 20.60 1.00 3530 3065 + theta[2] 6.60 6.56 5.50 5.04 -2.43 15.53 1.00 4324 3214 + + # showing 2 of 8 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + theta[1] 8.74 8.00 6.56 5.59 -0.63 20.60 1.00 3530 3065 + theta[2] 6.60 6.56 5.50 5.04 -2.43 15.53 1.00 4324 3214 + theta[3] 5.64 5.95 6.23 5.46 -4.95 15.30 1.00 4005 2780 + theta[4] 6.37 6.42 5.75 5.16 -3.29 15.57 1.00 4500 2943 + theta[5] 4.81 5.15 5.60 5.03 -4.97 13.29 1.00 4512 3546 + theta[6] 5.61 5.80 5.71 5.17 -3.76 14.60 1.00 4550 3534 + theta[7] 8.59 8.05 5.91 5.44 0.01 19.28 1.00 4050 3348 + theta[8] 6.89 6.66 6.31 5.42 -3.04 17.26 1.00 4332 2825 + +--- + + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + theta[1] 8.74 8.00 6.56 5.59 -0.63 20.60 1.00 3530 3065 + tau 4.57 3.82 3.53 3.39 0.30 11.37 1.00 1872 1873 + mu 6.41 6.39 4.14 3.99 -0.36 13.19 1.00 3229 2346 + theta_raw[3] -0.13 -0.13 0.96 0.98 -1.72 1.46 1.00 3995 2977 + +--- + + Can't find the following variable(s): unknown + diff --git a/tests/testthat/_snaps/fit-mle.md b/tests/testthat/_snaps/fit-mle.md new file mode 100644 index 000000000..9eb3a1b31 --- /dev/null +++ b/tests/testthat/_snaps/fit-mle.md @@ -0,0 +1,20 @@ +# print() method works after optimization + + variable estimate + lp__ -63.92 + alpha 0.36 + beta[1] -0.63 + beta[2] -0.26 + beta[3] 0.65 + +--- + + variable estimate + lp__ -63.92 + + # showing 1 of 5 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + +--- + + Can't find the following variable(s): unknown + diff --git a/tests/testthat/_snaps/fit-vb.md b/tests/testthat/_snaps/fit-vb.md new file mode 100644 index 000000000..f66d66cae --- /dev/null +++ b/tests/testthat/_snaps/fit-vb.md @@ -0,0 +1,71 @@ +# print() method works after vb + + variable mean median sd mad q5 q95 + lp__ -66.75 -66.18 2.16 1.79 -70.59 -64.40 + lp_approx__ -2.04 -1.74 1.39 1.26 -4.91 -0.40 + alpha 0.30 0.29 0.21 0.20 -0.03 0.64 + beta[1] -0.59 -0.58 0.37 0.37 -1.18 0.02 + beta[2] -0.25 -0.25 0.22 0.23 -0.64 0.09 + beta[3] 0.60 0.61 0.27 0.28 0.13 1.02 + variable mean median sd mad q5 q95 + lp__ -66.75 -66.18 2.16 1.79 -70.59 -64.40 + + # showing 1 of 6 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + +--- + + variable mean median sd mad q5 q95 + lp__ -47.27 -46.75 2.69 2.15 -51.93 -43.98 + lp_approx__ -5.05 -4.68 2.27 2.08 -9.18 -1.98 + mu 6.55 6.52 3.82 3.78 0.24 12.67 + tau 3.87 3.05 2.81 2.00 1.01 9.44 + theta_raw[1] 0.44 0.44 0.98 0.97 -1.15 1.97 + theta_raw[2] 0.14 0.12 0.92 0.89 -1.39 1.63 + theta_raw[3] -0.21 -0.18 1.00 1.01 -1.86 1.38 + theta_raw[4] 0.11 0.12 0.88 0.88 -1.35 1.55 + theta_raw[5] -0.11 -0.12 1.00 1.03 -1.66 1.57 + theta_raw[6] 0.04 0.03 0.78 0.81 -1.26 1.31 + + # showing 10 of 20 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 + lp__ -47.27 -46.75 2.69 2.15 -51.93 -43.98 + lp_approx__ -5.05 -4.68 2.27 2.08 -9.18 -1.98 + mu 6.55 6.52 3.82 3.78 0.24 12.67 + tau 3.87 3.05 2.81 2.00 1.01 9.44 + theta_raw[1] 0.44 0.44 0.98 0.97 -1.15 1.97 + theta_raw[2] 0.14 0.12 0.92 0.89 -1.39 1.63 + theta_raw[3] -0.21 -0.18 1.00 1.01 -1.86 1.38 + theta_raw[4] 0.11 0.12 0.88 0.88 -1.35 1.55 + theta_raw[5] -0.11 -0.12 1.00 1.03 -1.66 1.57 + theta_raw[6] 0.04 0.03 0.78 0.81 -1.26 1.31 + theta_raw[7] 0.34 0.33 0.98 0.99 -1.24 1.96 + theta_raw[8] 0.10 0.10 0.97 0.97 -1.47 1.68 + theta[1] 8.21 7.82 6.46 5.22 -1.00 17.39 + theta[2] 7.23 7.00 6.04 4.97 -2.01 16.92 + theta[3] 5.75 5.92 6.03 5.26 -3.41 14.73 + theta[4] 6.90 6.98 5.90 5.03 -2.08 16.23 + theta[5] 6.03 5.78 6.50 5.13 -3.18 16.17 + theta[6] 6.74 6.84 5.36 4.86 -1.99 15.91 + theta[7] 7.80 7.57 6.21 5.41 -1.44 18.38 + theta[8] 7.07 6.85 5.94 5.30 -2.19 17.13 + +--- + + variable mean median sd mad q5 q95 + theta[1] 8.21 7.82 6.46 5.22 -1.00 17.39 + theta[2] 7.23 7.00 6.04 4.97 -2.01 16.92 + theta[3] 5.75 5.92 6.03 5.26 -3.41 14.73 + theta[4] 6.90 6.98 5.90 5.03 -2.08 16.23 + theta[5] 6.03 5.78 6.50 5.13 -3.18 16.17 + theta[6] 6.74 6.84 5.36 4.86 -1.99 15.91 + theta[7] 7.80 7.57 6.21 5.41 -1.44 18.38 + theta[8] 7.07 6.85 5.94 5.30 -2.19 17.13 + tau 3.87 3.05 2.81 2.00 1.01 9.44 + lp__ -47.27 -46.75 2.69 2.15 -51.93 -43.98 + + # showing 10 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + +--- + + Can't find the following variable(s): unknown + diff --git a/tests/testthat/_snaps/install.md b/tests/testthat/_snaps/install.md new file mode 100644 index 000000000..b00f15a20 --- /dev/null +++ b/tests/testthat/_snaps/install.md @@ -0,0 +1,40 @@ +# install_cmdstan() errors if invalid version or URL + + Download of CmdStan failed with error: cannot open URL 'https://github.com/stan-dev/cmdstan/releases/download/v2.35.5/cmdstan-2.35.5.tar.gz' + Please check if the supplied version number is valid. + +--- + + Download of CmdStan failed with error: cannot open URL 'https://github.com/stan-dev/cmdstan/releases/download/v2.35.5/cmdstan-2.35.5.tar.gz' + Please check if the supplied release URL is valid. + +--- + + https://github.com/stan-dev/cmdstan/releases/tag/v2.24.0 is not a .tar.gz archive!cmdstanr supports installing from .tar.gz archives only. + +# toolchain checks on Unix work + + A C++ compiler was not found. Please install the 'clang++' or 'g++' compiler, restart R, and run cmdstanr::check_cmdstan_toolchain(). + +--- + + The 'make' tool was not found. Please install 'make', restart R, and then run cmdstanr::check_cmdstan_toolchain(). + +# check_rtools4x_windows_toolchain reports missing Rtools and make + + + Rtools44 was not found but is required to run CmdStan with R version 4.5.2. + Please install or reinstall the appropriate Rtools version for this R installation, + restart R, and then run cmdstanr::check_cmdstan_toolchain(). + +# check_rtools4x_windows_toolchain validates install path and empty candidates + + + Rtools44 is installed in a path with spaces or brackets, which is not supported. + Please reinstall the appropriate Rtools version for this R installation to a valid path, + restart R, and then run cmdstanr::check_cmdstan_toolchain(). + +# check_cmdstan_toolchain(fix = TRUE) is deprecated + + The 'fix' argument is deprecated and will be removed in a future release. + diff --git a/tests/testthat/_snaps/model-code-print.md b/tests/testthat/_snaps/model-code-print.md index 9b5fcdb4d..0bc92b13e 100644 --- a/tests/testthat/_snaps/model-code-print.md +++ b/tests/testthat/_snaps/model-code-print.md @@ -26,3 +26,15 @@ y ~ bernoulli(theta); } +# code() warns and print() errors if only exe and no Stan file + + '$code()' will return NULL because the 'CmdStanModel' was not created with a Stan file. + +--- + + '$print()' cannot be used because the 'CmdStanModel' was not created with a Stan file. + +# check_syntax() errors if only exe and no Stan file + + '$check_syntax()' cannot be used because the 'CmdStanModel' was not created with a Stan file. + diff --git a/tests/testthat/_snaps/model-compile.md b/tests/testthat/_snaps/model-compile.md new file mode 100644 index 000000000..7f71e3150 --- /dev/null +++ b/tests/testthat/_snaps/model-compile.md @@ -0,0 +1,21 @@ +# name in STANCFLAGS is set correctly + + Code + cat(trim_stanc_invocations(out), sep = "\n") + Output + bin/stanc --name='bernoulli_model' --o + +--- + + Code + cat(trim_stanc_invocations(out), sep = "\n") + Output + bin/stanc --name='bernoulli2_model' --o + +# STANCFLAGS from get_cmdstan_flags() are included in compile output + + Code + cat(trim_stanc_invocations(out), sep = "\n") + Output + bin/stanc --name='bernoulli_model' --O1 --warn-pedantic --o + diff --git a/tests/testthat/_snaps/path.md b/tests/testthat/_snaps/path.md new file mode 100644 index 000000000..9b05ce9ef --- /dev/null +++ b/tests/testthat/_snaps/path.md @@ -0,0 +1,28 @@ +# Setting bad path leads to warning (can't find directory) + + Path not set. Can't find directory: BAD_PATH + +# Setting bad path from env leads to warning (can't find directory) + + Can't find directory specified by environment variable 'CMDSTAN'. Path not set. + +# Existing CMDSTAN env path with no install resets cached state + + No CmdStan installation found in the path specified by the environment variable 'CMDSTAN'. + +# Getting missing path leads to error (path not set) + + CmdStan path has not been set yet. See ?set_cmdstan_path. + +# cmdstan_version() behaves correctly when version is not set + + CmdStan path has not been set yet. See ?set_cmdstan_path. + +# Warning message is thrown if can't detect version number + + Can't find CmdStan makefile to detect version number. Path may not point to valid installation. + +# Setting path rejects unsupported CmdStan versions + + CmdStan path not set. CmdStan v2.34.0 is no longer supported. cmdstanr now requires CmdStan v2.35.0 or newer. + diff --git a/tests/testthat/_snaps/utils.md b/tests/testthat/_snaps/utils.md new file mode 100644 index 000000000..862b6a063 --- /dev/null +++ b/tests/testthat/_snaps/utils.md @@ -0,0 +1,16 @@ +# check_ebfmi and computing ebfmi works + + E-BFMI not computed because it is undefined for posterior chains of length less than 3. + +--- + + E-BFMI not computed because it is undefined for posterior chains of length less than 3. + +--- + + E-BFMI not computed because the 'energy__' diagnostic could not be located. + +--- + + E-BFMI not computed because the 'energy__' diagnostic could not be located. + diff --git a/tests/testthat/helper-custom-expectations.R b/tests/testthat/helper-custom-expectations.R index 2bda8e329..242dc8380 100644 --- a/tests/testthat/helper-custom-expectations.R +++ b/tests/testthat/helper-custom-expectations.R @@ -108,7 +108,7 @@ expect_interactive_message <- function(object, regexp = NULL) { expect_noninteractive_silent <- function(object) { rlang::with_interactive(value = FALSE, - expect_silent(object)) + expect_no_condition(object)) } expect_equal_ignore_order <- function(object, expected, ...) { diff --git a/tests/testthat/test-fit-gq.R b/tests/testthat/test-fit-gq.R index 951e86239..846fbe912 100644 --- a/tests/testthat/test-fit-gq.R +++ b/tests/testthat/test-fit-gq.R @@ -64,34 +64,24 @@ test_that("summary() method works after gq", { }) test_that("print() method works after gq", { - expect_output(expect_s3_class(fit_gq$print(), "CmdStanGQ"), "variable") - expect_output(fit_gq$print(max_rows = 1), "# showing 1 of 11 rows") - expect_output(fit_gq$print(NULL, c("mad")), "mad") - - expect_output(fit_gq$print(), "showing 10 of 11 rows") - expect_output(fit_gq$print(max_rows = 2), "showing 2 of 11 rows") - expect_output(fit_gq$print(max_rows = 11), "sum_y", fixed=TRUE) # last parameter - expect_output(fit_gq$print("y_rep", max_rows = 2), "showing 2 of 10 rows") - expect_error( - fit_gq$print(variable = "unknown", max_rows = 20), - "Can't find the following variable(s): unknown", - fixed = TRUE - ) # unknown parameter - - out <- capture.output(fit_gq$print("y_rep")) - expect_length(out, 11) # columns names + 1 y_rep - expect_match(out[1], "variable") - expect_match(out[2], "y_rep[1]", fixed = TRUE) - expect_match(out[9], "y_rep[8]", fixed = TRUE) - expect_false(any(grepl("sum_y|theta", out))) - - # make sure the row order is correct - out <- capture.output(fit_gq$print(c("y_rep[1]", "sum_y", "y_rep[3]"))) - expect_length(out, 4) - expect_match(out[1], " variable") - expect_match(out[2], " y_rep[1]", fixed = TRUE) - expect_match(out[3], " sum_y") - expect_match(out[4], " y_rep[3]", fixed = TRUE) + expect_s3_class(fit_gq$print(), "CmdStanGQ") + expect_snapshot_output({ + fit_gq$print() + fit_gq$print(max_rows = 1) + fit_gq$print(NULL, c("mad")) + }) + expect_snapshot_output({ + fit_gq$print(max_rows = 2) + fit_gq$print(max_rows = 11) + }) + expect_snapshot_output({ + fit_gq$print("y_rep", max_rows = 2) + fit_gq$print("y_rep") + }) + expect_snapshot_output({ + fit_gq$print(c("y_rep[1]", "sum_y", "y_rep[3]")) + }) + expect_snapshot_error(fit_gq$print(variable = "unknown", max_rows = 20)) }) test_that("output() method works after gq", { diff --git a/tests/testthat/test-fit-laplace.R b/tests/testthat/test-fit-laplace.R index 90415e935..1acb8f8bf 100644 --- a/tests/testthat/test-fit-laplace.R +++ b/tests/testthat/test-fit-laplace.R @@ -13,8 +13,9 @@ test_that("summary() and print() methods works after laplace", { expect_equal(x$variable, c("lp__", "lp_approx__", PARAM_NAMES)) expect_equal(colnames(x), c("variable", "mean", "sd")) - expect_output(expect_s3_class(fit_laplace$print(), "CmdStanLaplace"), "variable") - expect_output(fit_laplace$print(max_rows = 1), "# showing 1 of 6 rows") + expect_s3_class(fit_laplace$print(), "CmdStanLaplace") + expect_snapshot_output(fit_laplace$print()) + expect_snapshot_output(fit_laplace$print(max_rows = 1)) }) test_that("draws() method returns posterior sample (reading csv works)", { diff --git a/tests/testthat/test-fit-mcmc.R b/tests/testthat/test-fit-mcmc.R index 8af8a7168..0224c2fb7 100644 --- a/tests/testthat/test-fit-mcmc.R +++ b/tests/testthat/test-fit-mcmc.R @@ -123,37 +123,28 @@ test_that("summary() method works after mcmc", { }) test_that("print() method works after mcmc", { - expect_output(expect_s3_class(fit_mcmc$print(), "CmdStanMCMC"), "variable") - expect_output(fit_mcmc$print(max_rows = 1), "# showing 1 of 5 rows") - expect_output(fit_mcmc$print(NULL, c("ess_sd")), "ess_sd") + expect_s3_class(fit_mcmc$print(), "CmdStanMCMC") # test on model with more parameters fit <- cmdstanr_example("schools_ncp") - expect_output(fit$print(), "showing 10 of 19 rows") - expect_output(fit$print(max_rows = 2), "showing 2 of 19 rows") - expect_output(fit$print(max_rows = 19), "theta[8]", fixed=TRUE) # last parameter - expect_output(fit$print("theta", max_rows = 2), "showing 2 of 8 rows") - expect_error( - fit$print(variable = "unknown", max_rows = 20), - "Can't find the following variable(s): unknown", - fixed = TRUE - ) # unknown parameter - - out <- capture.output(fit$print("theta")) - expect_length(out, 9) # columns names + 8 thetas - expect_match(out[1], "variable") - expect_match(out[2], "theta[1]", fixed = TRUE) - expect_match(out[9], "theta[8]", fixed = TRUE) - expect_false(any(grepl("mu|tau|theta_raw", out))) - - # make sure the row order is correct - out <- capture.output(fit$print(c("theta[1]", "tau", "mu", "theta_raw[3]"))) - expect_length(out, 5) - expect_match(out[1], " variable") - expect_match(out[2], " theta[1]", fixed = TRUE) - expect_match(out[3], " tau") - expect_match(out[4], " mu") - expect_match(out[5], " theta_raw[3]", fixed = TRUE) + expect_snapshot_output({ + fit_mcmc$print() + fit_mcmc$print(max_rows = 1) + fit_mcmc$print(NULL, c("ess_sd")) + }) + expect_snapshot_output({ + fit$print() + fit$print(max_rows = 2) + fit$print(max_rows = 19) + }) + expect_snapshot_output({ + fit$print("theta", max_rows = 2) + fit$print("theta") + }) + expect_snapshot_output({ + fit$print(c("theta[1]", "tau", "mu", "theta_raw[3]")) + }) + expect_snapshot_error(fit$print(variable = "unknown", max_rows = 20)) }) test_that("output() method works after mcmc", { diff --git a/tests/testthat/test-fit-mle.R b/tests/testthat/test-fit-mle.R index c868a37c2..3f1d5de2d 100644 --- a/tests/testthat/test-fit-mle.R +++ b/tests/testthat/test-fit-mle.R @@ -22,13 +22,10 @@ test_that("summary method works after optimization", { }) test_that("print() method works after optimization", { - expect_output(expect_s3_class(fit_mle$print(), "CmdStanMLE"), "estimate") - expect_output(fit_mle$print(max_rows = 1), "# showing 1 of 5 rows") - expect_error( - fit_mle$print(variable = "unknown", max_rows = 20), - "Can't find the following variable(s): unknown", - fixed = TRUE - ) # unknown parameter + expect_s3_class(fit_mle$print(), "CmdStanMLE") + expect_snapshot_output(fit_mle$print()) + expect_snapshot_output(fit_mle$print(max_rows = 1)) + expect_snapshot_error(fit_mle$print(variable = "unknown", max_rows = 20)) }) test_that("time() method works after optimization", { diff --git a/tests/testthat/test-fit-vb.R b/tests/testthat/test-fit-vb.R index f5d2f2c96..1763a6297 100644 --- a/tests/testthat/test-fit-vb.R +++ b/tests/testthat/test-fit-vb.R @@ -18,29 +18,22 @@ test_that("summary() method works after vb", { }) test_that("print() method works after vb", { - expect_output(expect_s3_class(fit_vb$print(), "CmdStanVB"), "variable") - expect_output(fit_vb$print(max_rows = 1), "# showing 1 of 6 rows") + expect_s3_class(fit_vb$print(), "CmdStanVB") # test on model with more parameters fit <- cmdstanr_example("schools_ncp", method = "variational", seed = 123) - expect_output(fit$print(), "lp_approx__") - expect_output(fit$print(), "showing 10 of 20 rows") - expect_output(fit$print(max_rows = 20), "theta[8]", fixed = TRUE) # last parameter - expect_error( - fit$print(variable = "unknown", max_rows = 20), - "Can't find the following variable(s): unknown", - fixed = TRUE - ) # unknown parameter - - out <- capture.output(fit$print(c("theta", "tau", "lp__", "lp_approx__"))) - expect_length(out, 13) # columns names + 8 thetas + tau + lp__ + lp_approx__ + empty + message - expect_match(out[1], " variable") - expect_match(out[2], " theta[1]", fixed = TRUE) - expect_match(out[9], " theta[8]", fixed = TRUE) - expect_match(out[10], " tau") - expect_match(out[11], " lp__") - expect_false(nzchar(out[12])) # empty line - expect_match(out[13], "10 of 11 rows") + expect_snapshot_output({ + fit_vb$print() + fit_vb$print(max_rows = 1) + }) + expect_snapshot_output({ + fit$print() + fit$print(max_rows = 20) + }) + expect_snapshot_output({ + fit$print(c("theta", "tau", "lp__", "lp_approx__")) + }) + expect_snapshot_error(fit$print(variable = "unknown", max_rows = 20)) }) test_that("draws() method returns posterior sample (reading csv works)", { diff --git a/tests/testthat/test-install.R b/tests/testthat/test-install.R index c0253ae93..72b0ee856 100644 --- a/tests/testthat/test-install.R +++ b/tests/testthat/test-install.R @@ -67,18 +67,13 @@ test_that("install_cmdstan() errors if it times out", { }) test_that("install_cmdstan() errors if invalid version or URL", { - expect_error( - install_cmdstan(version = "2.35.5", wsl = os_is_wsl()), - "Download of CmdStan failed with error: cannot open URL 'https://github.com/stan-dev/cmdstan/releases/download/v2.35.5/cmdstan-2.35.5.tar.gz'\nPlease check if the supplied version number is valid." - ) - expect_error( + expect_snapshot_error(install_cmdstan(version = "2.35.5", wsl = os_is_wsl())) + expect_snapshot_error( install_cmdstan(release_url = "https://github.com/stan-dev/cmdstan/releases/download/v2.35.5/cmdstan-2.35.5.tar.gz", - wsl = os_is_wsl()), - "Download of CmdStan failed with error: cannot open URL 'https://github.com/stan-dev/cmdstan/releases/download/v2.35.5/cmdstan-2.35.5.tar.gz'\nPlease check if the supplied release URL is valid." + wsl = os_is_wsl()) ) - expect_error( - install_cmdstan(release_url = "https://github.com/stan-dev/cmdstan/releases/tag/v2.24.0", wsl = os_is_wsl()), - "cmdstanr supports installing from .tar.gz archives only" + expect_snapshot_error( + install_cmdstan(release_url = "https://github.com/stan-dev/cmdstan/releases/tag/v2.24.0", wsl = os_is_wsl()) ) }) @@ -123,23 +118,9 @@ test_that("install_cmdstan() works with version and release_url", { test_that("toolchain checks on Unix work", { skip_if(os_is_windows()) withr::local_envvar(c("PATH" = "")) - if (os_is_macos()) { - err_msg_cpp <- "A suitable C++ compiler was not found. Please install the command line tools for Mac with 'xcode-select --install' or install Xcode from the app store. Then restart R and run cmdstanr::check_cmdstan_toolchain()." - err_msg_make <- "The 'make' tool was not found. Please install the command line tools for Mac with 'xcode-select --install' or install Xcode from the app store. Then restart R and run cmdstanr::check_cmdstan_toolchain()." - } else { - err_msg_cpp <- "A C++ compiler was not found. Please install the 'clang++' or 'g++' compiler, restart R, and run cmdstanr::check_cmdstan_toolchain()." - err_msg_make <- "The 'make' tool was not found. Please install 'make', restart R, and then run cmdstanr::check_cmdstan_toolchain()." - } - expect_error( - check_unix_cpp_compiler(), - err_msg_cpp, - fixed = TRUE - ) - expect_error( - check_unix_make(), - err_msg_make, - fixed = TRUE - ) + variant <- if (os_is_macos()) "macos" else NULL + expect_snapshot_error(check_unix_cpp_compiler(), variant = variant) + expect_snapshot_error(check_unix_make(), variant = variant) }) test_that("clean and rebuild works", { @@ -299,7 +280,7 @@ test_that("deprecated CMDSTANR_USE_MSYS_TOOLCHAIN is ignored with warning", { "CMDSTANR_USE_MSYS_TOOLCHAIN", fixed = TRUE ) - expect_silent(make_cmd()) + expect_no_condition(make_cmd()) }) }) @@ -365,24 +346,20 @@ test_that("rtools4x_toolchain_path prefers ABI-compatible legacy fallback", { file.create(file.path(fake_rtools_home, "ucrt64", "bin", "g++.exe")) withr::with_envvar(setNames(fake_rtools_home, env_var), { - with_mocked_bindings( - { - expect_equal( - rtools4x_toolchain_path(), - repair_path(file.path(fake_rtools_home, "mingw64", "bin")) - ) - }, - is_ucrt_toolchain = function() FALSE - ) - with_mocked_bindings( - { - expect_equal( - rtools4x_toolchain_path(), - repair_path(file.path(fake_rtools_home, "ucrt64", "bin")) - ) - }, - is_ucrt_toolchain = function() TRUE - ) + local({ + local_mocked_bindings(is_ucrt_toolchain = function() FALSE) + expect_equal( + rtools4x_toolchain_path(), + repair_path(file.path(fake_rtools_home, "mingw64", "bin")) + ) + }) + local({ + local_mocked_bindings(is_ucrt_toolchain = function() TRUE) + expect_equal( + rtools4x_toolchain_path(), + repair_path(file.path(fake_rtools_home, "ucrt64", "bin")) + ) + }) }) }) @@ -419,64 +396,66 @@ test_that("check_rtools4x_windows_toolchain reports checked toolchain paths", { }) test_that("toolchain_PATH_env_var() handles missing and configured Rtools homes", { - with_mocked_bindings( - expect_null(toolchain_PATH_env_var()), - os_is_windows = function() FALSE - ) - with_mocked_bindings( - expect_null(toolchain_PATH_env_var()), - os_is_windows = function() TRUE, - rtools4x_home_path = function() "" - ) - with_mocked_bindings( + local({ + local_mocked_bindings(os_is_windows = function() FALSE) + expect_null(toolchain_PATH_env_var()) + }) + local({ + local_mocked_bindings( + os_is_windows = function() TRUE, + rtools4x_home_path = function() "" + ) + expect_null(toolchain_PATH_env_var()) + }) + local({ + local_mocked_bindings( + os_is_windows = function() TRUE, + rtools4x_home_path = function() "C:/rtools", + rtools4x_toolchain_path = function() "C:/rtools/ucrt64/bin", + repair_path = function(path) path + ) expect_equal( toolchain_PATH_env_var(), "C:/rtools/usr/bin;C:/rtools/ucrt64/bin" - ), - os_is_windows = function() TRUE, - rtools4x_home_path = function() "C:/rtools", - rtools4x_toolchain_path = function() "C:/rtools/ucrt64/bin", - repair_path = function(path) path - ) + ) + }) }) test_that("check_rtools4x_windows_toolchain reports missing Rtools and make", { fake_rtools_home <- tempfile(pattern = "rtools-home-missing-", tmpdir = tempdir(check = TRUE)) on.exit(unlink(fake_rtools_home, recursive = TRUE), add = TRUE) - with_mocked_bindings( - expect_error( - check_rtools4x_windows_toolchain(), - "restart R, and then run cmdstanr::check_cmdstan_toolchain()", - fixed = TRUE - ), - rtools4x_home_path = function() "", - rtools4x_version = function() "44" - ) + local({ + local_mocked_bindings( + rtools4x_home_path = function() "", + rtools4x_version = function() "44" + ) + expect_snapshot_error(check_rtools4x_windows_toolchain()) + }) dir.create(file.path(fake_rtools_home, "usr", "bin"), recursive = TRUE, showWarnings = FALSE) - with_mocked_bindings( + local({ + local_mocked_bindings( + rtools4x_home_path = function() fake_rtools_home, + rtools4x_version = function() "44" + ) expect_error( check_rtools4x_windows_toolchain(), "restart R, and then run cmdstanr::check_cmdstan_toolchain()", fixed = TRUE - ), - rtools4x_home_path = function() fake_rtools_home, - rtools4x_version = function() "44" - ) + ) + }) }) test_that("check_rtools4x_windows_toolchain validates install path and empty candidates", { - with_mocked_bindings( - expect_error( - check_rtools4x_windows_toolchain(), - "Please reinstall the appropriate Rtools version for this R installation to a valid path", - fixed = TRUE - ), - rtools4x_home_path = function() "C:/Program Files/Rtools44", - rtools4x_version = function() "44" - ) + local({ + local_mocked_bindings( + rtools4x_home_path = function() "C:/Program Files/Rtools44", + rtools4x_version = function() "44" + ) + expect_snapshot_error(check_rtools4x_windows_toolchain()) + }) fake_rtools_home <- tempfile(pattern = "rtools-home-empty-", tmpdir = tempdir(check = TRUE)) on.exit(unlink(fake_rtools_home, recursive = TRUE), add = TRUE) @@ -484,22 +463,20 @@ test_that("check_rtools4x_windows_toolchain validates install path and empty can recursive = TRUE, showWarnings = FALSE) file.create(file.path(fake_rtools_home, "usr", "bin", "make.exe")) - with_mocked_bindings( + local({ + local_mocked_bindings( + rtools4x_home_path = function() fake_rtools_home, + rtools4x_version = function() "44", + rtools4x_toolchain_candidates = function() character() + ) expect_error( check_rtools4x_windows_toolchain(), "restart R, and then run cmdstanr::check_cmdstan_toolchain()", fixed = TRUE - ), - rtools4x_home_path = function() fake_rtools_home, - rtools4x_version = function() "44", - rtools4x_toolchain_candidates = function() character() - ) + ) + }) }) test_that("check_cmdstan_toolchain(fix = TRUE) is deprecated", { - expect_warning( - check_cmdstan_toolchain(fix = TRUE), - "The 'fix' argument is deprecated and will be removed in a future release", - fixed = TRUE - ) + expect_snapshot_warning(check_cmdstan_toolchain(fix = TRUE)) }) diff --git a/tests/testthat/test-knitr.R b/tests/testthat/test-knitr.R index bea503b43..6141ac9c4 100644 --- a/tests/testthat/test-knitr.R +++ b/tests/testthat/test-knitr.R @@ -22,7 +22,7 @@ test_that("eng_cmdstan works", { )) expect_interactive_message(eng_cmdstan(opts), "Compiling Stan program") opts$eval <- FALSE - expect_silent(eng_cmdstan(opts)) + expect_noninteractive_silent(eng_cmdstan(opts)) }) test_that("register_knitr_engine works with and without override", { diff --git a/tests/testthat/test-model-code-print.R b/tests/testthat/test-model-code-print.R index d4729ed06..2a7ce9635 100644 --- a/tests/testthat/test-model-code-print.R +++ b/tests/testthat/test-model-code-print.R @@ -61,23 +61,12 @@ test_that("code() doesn't change when file changes (unless model is recreated)", test_that("code() warns and print() errors if only exe and no Stan file", { mod_exe <- cmdstan_model(exe_file = mod$exe_file()) - expect_warning( - expect_null(mod_exe$code()), - "'$code()' will return NULL because the 'CmdStanModel' was not created with a Stan file", - fixed = TRUE - ) - expect_error( - mod_exe$print(), - "'$print()' cannot be used because the 'CmdStanModel' was not created with a Stan file.", - fixed = TRUE - ) + expect_snapshot_warning(code <- mod_exe$code()) + expect_null(code) + expect_snapshot_error(mod_exe$print()) }) test_that("check_syntax() errors if only exe and no Stan file", { mod_exe <- cmdstan_model(exe_file = mod$exe_file()) - expect_error( - mod_exe$check_syntax(), - "'$check_syntax()' cannot be used because the 'CmdStanModel' was not created with a Stan file.", - fixed = TRUE - ) + expect_snapshot_error(mod_exe$check_syntax()) }) diff --git a/tests/testthat/test-model-compile.R b/tests/testthat/test-model-compile.R index cc7156fc9..81a40e066 100644 --- a/tests/testthat/test-model-compile.R +++ b/tests/testthat/test-model-compile.R @@ -3,6 +3,15 @@ stan_program <- cmdstan_example_file() mod <- cmdstan_model(stan_file = stan_program, compile = FALSE) cmdstan_make_local(cpp_options = list("PRECOMPILED_HEADERS"="false")) +stanc_snapshot_variant <- function() { + if (os_is_windows() && !os_is_wsl()) "windows" else NULL +} + +trim_stanc_invocations <- function(output) { + out <- grep("bin/stanc", output, value = TRUE, fixed = TRUE) + sub("( --o).*", "\\1", out) +} + test_that("object initialized correctly", { expect_equal(mod$stan_file(), stan_program) expect_equal(mod$exe_file(), character(0)) @@ -111,17 +120,19 @@ test_that("compilation works with include_paths", { }) test_that("name in STANCFLAGS is set correctly", { + local_reproducible_output() + variant <- stanc_snapshot_variant() out <- utils::capture.output(mod$compile(quiet = FALSE, force_recompile = TRUE)) - if(os_is_windows() && !os_is_wsl()) { - out_no_name <- "bin/stanc.exe --name='bernoulli_model' --o" - out_name <- "bin/stanc.exe --name='bernoulli2_model' --o" - } else { - out_no_name <- "bin/stanc --name='bernoulli_model' --o" - out_name <- "bin/stanc --name='bernoulli2_model' --o" - } - expect_output(print(out), out_no_name) - out <- utils::capture.output(mod$compile(quiet = FALSE, force_recompile = TRUE, stanc_options = list(name = "bernoulli2_model"))) - expect_output(print(out), out_name) + expect_snapshot(cat(trim_stanc_invocations(out), sep = "\n"), variant = variant) + + out <- utils::capture.output( + mod$compile( + quiet = FALSE, + force_recompile = TRUE, + stanc_options = list(name = "bernoulli2_model") + ) + ) + expect_snapshot(cat(trim_stanc_invocations(out), sep = "\n"), variant = variant) }) @@ -828,9 +839,10 @@ test_that("dirname of stan_file is used as include path if no other paths suppli }) test_that("STANCFLAGS from get_cmdstan_flags() are included in compile output", { + local_reproducible_output() + variant <- stanc_snapshot_variant() real_get_cmdstan_flags <- get_cmdstan_flags - out <- with_mocked_bindings( - utils::capture.output(mod$compile(quiet = FALSE, force_recompile = TRUE)), + local_mocked_bindings( get_cmdstan_flags = function(flag_name) { if (identical(flag_name, "STANCFLAGS")) { c("--O1", "--warn-pedantic") @@ -839,12 +851,8 @@ test_that("STANCFLAGS from get_cmdstan_flags() are included in compile output", } } ) - if(os_is_windows() && !os_is_wsl()) { - out_w_flags <- "bin/stanc.exe --name='bernoulli_model'[[:space:]]+--O1[[:space:]]+--warn-pedantic[[:space:]]+--o" - } else { - out_w_flags <- "bin/stanc --name='bernoulli_model'[[:space:]]+--O1[[:space:]]+--warn-pedantic[[:space:]]+--o" - } - expect_output(print(out), out_w_flags) + out <- utils::capture.output(mod$compile(quiet = FALSE, force_recompile = TRUE)) + expect_snapshot(cat(trim_stanc_invocations(out), sep = "\n"), variant = variant) }) test_that("compile() ignores directory chatter from MAKEFLAGS when reading STANCFLAGS", { diff --git a/tests/testthat/test-path.R b/tests/testthat/test-path.R index 9758a9cd4..16c737a64 100644 --- a/tests/testthat/test-path.R +++ b/tests/testthat/test-path.R @@ -17,20 +17,14 @@ test_that("Setting path works and confirms with message", { test_that("Setting bad path leads to warning (can't find directory)", { unset_cmdstan_path() expect_null(.cmdstanr$PATH) - expect_warning( - set_cmdstan_path("BAD_PATH"), - "Can't find directory" - ) + expect_snapshot_warning(set_cmdstan_path("BAD_PATH")) }) test_that("Setting bad path from env leads to warning (can't find directory)", { unset_cmdstan_path() .cmdstanr$WSL <- TRUE withr::local_envvar(c(CMDSTAN = "BAD_PATH")) - expect_warning( - cmdstanr_initialize(), - "Can't find directory specified by environment variable" - ) + expect_snapshot_warning(cmdstanr_initialize()) expect_null(.cmdstanr$PATH) expect_null(.cmdstanr$VERSION) expect_false(isTRUE(.cmdstanr$WSL)) @@ -40,7 +34,7 @@ test_that("Setting path from env var is detected", { unset_cmdstan_path() expect_true(is.null(.cmdstanr$VERSION)) withr::local_envvar(c(CMDSTAN = PATH)) - expect_silent(cmdstanr_initialize()) + expect_no_condition(cmdstanr_initialize()) expect_equal(cmdstan_path(), PATH) expect_false(is.null(.cmdstanr$VERSION)) }) @@ -74,11 +68,7 @@ test_that("Existing CMDSTAN env path with no install resets cached state", { on.exit(unlink(empty_parent, recursive = TRUE), add = TRUE) withr::local_envvar(c(CMDSTAN = empty_parent)) - expect_warning( - cmdstanr_initialize(), - "No CmdStan installation found in the path specified by the environment variable 'CMDSTAN'.", - fixed = TRUE - ) + expect_snapshot_warning(cmdstanr_initialize()) expect_null(.cmdstanr$PATH) expect_null(.cmdstanr$VERSION) expect_false(isTRUE(.cmdstanr$WSL)) @@ -100,10 +90,7 @@ test_that("Getting a valid path works", { test_that("Getting missing path leads to error (path not set)", { unset_cmdstan_path() - expect_error( - cmdstan_path(), - "CmdStan path has not been set yet" - ) + expect_snapshot_error(cmdstan_path()) expect_null(.cmdstanr$PATH) }) @@ -118,16 +105,13 @@ test_that("cmdstan_version() behaves correctly when version is not set", { version <- .cmdstanr$VERSION on.exit(.cmdstanr$VERSION <- version) .cmdstanr$VERSION <- NULL - expect_error(cmdstan_version()) + expect_snapshot_error(cmdstan_version()) expect_null(cmdstan_version(error_on_NA = FALSE)) }) test_that("Warning message is thrown if can't detect version number", { path <- testthat::test_path("answers") # valid path but not cmdstan - expect_warning( - set_cmdstan_path(path), - "Can't find CmdStan makefile to detect version number" - ) + expect_snapshot_warning(set_cmdstan_path(path)) }) test_that("Setting path rejects unsupported CmdStan versions", { @@ -145,11 +129,7 @@ test_that("Setting path rejects unsupported CmdStan versions", { on.exit(unlink(path, recursive = TRUE), add = TRUE) writeLines("CMDSTAN_VERSION := 2.34.0", con = file.path(path, "makefile")) - expect_warning( - set_cmdstan_path(path), - "cmdstanr now requires CmdStan v2.35.0 or newer", - fixed = TRUE - ) + expect_snapshot_warning(set_cmdstan_path(path)) expect_null(.cmdstanr$PATH) expect_null(.cmdstanr$VERSION) expect_false(isTRUE(.cmdstanr$WSL)) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index b27628c5b..56797f7f3 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -108,12 +108,12 @@ test_that("check_ebfmi and computing ebfmi works", { expect_equal(as.numeric(ebfmi(posterior::as_draws_list(energy_df))), check_val) expect_equal(as.numeric(ebfmi(posterior::as_draws_matrix(energy_df))), check_val) energy_df <- posterior::as_draws(data.frame("energy__" = 0)) - expect_warning(check_ebfmi(energy_df), "E-BFMI not computed because it is undefined for posterior chains of length less than 3.") - expect_warning(ebfmi(energy_df), "E-BFMI not computed because it is undefined for posterior chains of length less than 3.") + expect_snapshot_warning(check_ebfmi(energy_df)) + expect_snapshot_warning(ebfmi(energy_df)) energy_df <- posterior::as_draws(data.frame("somethingelse" = 0)) - expect_warning(check_ebfmi(energy_df), "E-BFMI not computed because the 'energy__' diagnostic could not be located.") - expect_warning(ebfmi(energy_df), "E-BFMI not computed because the 'energy__' diagnostic could not be located.") + expect_snapshot_warning(check_ebfmi(energy_df)) + expect_snapshot_warning(ebfmi(energy_df)) }) From 08ef444d378effcae6db2f8b6b93d722a40dffb8 Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Wed, 25 Mar 2026 12:07:18 -0700 Subject: [PATCH 23/27] Small changes --- tests/testthat/_snaps/cpp_opts.md | 8 ++++ tests/testthat/helper-custom-expectations.R | 2 +- tests/testthat/helper-mock-cli.R | 42 ++++++++++----------- tests/testthat/test-cpp_opts.R | 7 +--- tests/testthat/test-install.R | 2 +- tests/testthat/test-model-data.R | 2 +- tests/testthat/test-model-pathfinder.R | 2 +- tests/testthat/test-path.R | 2 +- 8 files changed, 35 insertions(+), 32 deletions(-) create mode 100644 tests/testthat/_snaps/cpp_opts.md diff --git a/tests/testthat/_snaps/cpp_opts.md b/tests/testthat/_snaps/cpp_opts.md new file mode 100644 index 000000000..7d166fa18 --- /dev/null +++ b/tests/testthat/_snaps/cpp_opts.md @@ -0,0 +1,8 @@ +# validate_cpp_options works + + STAN_OPENCL set to FALSE Since this is a non-empty value, it will result in the corresponding ccp option being turned ON. To turn this option off, use cpp_options = list(stan_opencl = NULL). + +# exe_info cpp_options comparison works + + Recompiling is recommended due to missing exe_info. + diff --git a/tests/testthat/helper-custom-expectations.R b/tests/testthat/helper-custom-expectations.R index 242dc8380..2bda8e329 100644 --- a/tests/testthat/helper-custom-expectations.R +++ b/tests/testthat/helper-custom-expectations.R @@ -108,7 +108,7 @@ expect_interactive_message <- function(object, regexp = NULL) { expect_noninteractive_silent <- function(object) { rlang::with_interactive(value = FALSE, - expect_no_condition(object)) + expect_silent(object)) } expect_equal_ignore_order <- function(object, expected, ...) { diff --git a/tests/testthat/helper-mock-cli.R b/tests/testthat/helper-mock-cli.R index 649aa6853..799e8d1d0 100644 --- a/tests/testthat/helper-mock-cli.R +++ b/tests/testthat/helper-mock-cli.R @@ -2,29 +2,27 @@ real_wcr <- wsl_compatible_run with_mocked_cli <- function(code, compile_ret, info_ret) { code <- substitute(code) - rlang::eval_bare( - rlang::expr( - with_mocked_bindings( - !!code, - wsl_compatible_run = !!function(command, args, ...) { - if ( - !is.null(command) - && command == "make" - && !is.null(args) - && startsWith(basename(args[1]), "model-") - ) { - message("mock-compile-was-called") - compile_ret - } else if (!is.null(args) && args[1] == "info") { - info_ret - } else { - real_wcr(command = command, args = args, ...) - } - } - ) - ), - env = parent.frame() + caller <- parent.frame() + local_mocked_bindings( + wsl_compatible_run = function(command, args, ...) { + if ( + !is.null(command) + && command == "make" + && !is.null(args) + && startsWith(basename(args[1]), "model-") + ) { + message("mock-compile-was-called") + compile_ret + } else if (!is.null(args) && args[1] == "info") { + info_ret + } else { + real_wcr(command = command, args = args, ...) + } + }, + .package = "cmdstanr", + .env = caller ) + rlang::eval_bare(code, env = caller) } ######## Mock Compile Expectations ####### diff --git a/tests/testthat/test-cpp_opts.R b/tests/testthat/test-cpp_opts.R index e02d1c9de..aea550c45 100644 --- a/tests/testthat/test-cpp_opts.R +++ b/tests/testthat/test-cpp_opts.R @@ -34,7 +34,7 @@ test_that("validate_cpp_options works", { abc = FALSE ) ) - expect_warning(validate_cpp_options(list(STAN_OPENCL = FALSE))) + expect_snapshot_warning(validate_cpp_options(list(STAN_OPENCL = FALSE))) }) @@ -64,8 +64,5 @@ test_that("exe_info cpp_options comparison works", { )) # no exe_info -> no recompile based on cpp info - expect_warning( - expect_true(exe_info_reflects_cpp_options(list(), list())), - "Recompiling is recommended" - ) + expect_snapshot_warning(expect_true(exe_info_reflects_cpp_options(list(), list()))) }) diff --git a/tests/testthat/test-install.R b/tests/testthat/test-install.R index 72b0ee856..33fe60659 100644 --- a/tests/testthat/test-install.R +++ b/tests/testthat/test-install.R @@ -280,7 +280,7 @@ test_that("deprecated CMDSTANR_USE_MSYS_TOOLCHAIN is ignored with warning", { "CMDSTANR_USE_MSYS_TOOLCHAIN", fixed = TRUE ) - expect_no_condition(make_cmd()) + expect_silent(make_cmd()) }) }) diff --git a/tests/testthat/test-model-data.R b/tests/testthat/test-model-data.R index 34a427e93..1e1c2ed67 100644 --- a/tests/testthat/test-model-data.R +++ b/tests/testthat/test-model-data.R @@ -32,5 +32,5 @@ test_that("empty data list doesn't error if no data block", { ) # would error if fitting failed - expect_silent(fit$draws()) + expect_no_error(fit$draws()) }) diff --git a/tests/testthat/test-model-pathfinder.R b/tests/testthat/test-model-pathfinder.R index c3a55b52e..7d4747f94 100644 --- a/tests/testthat/test-model-pathfinder.R +++ b/tests/testthat/test-model-pathfinder.R @@ -145,7 +145,7 @@ test_that("pathfinder() method runs when the stan file is removed", { test_that("no error when checking estimates after failure", { fit <- cmdstanr_example("schools", method = "pathfinder", seed = 123) # optim always fails for this - expect_silent(fit$summary()) # no error + expect_no_error(fit$summary()) }) test_that("no output with show_messages = FALSE", { diff --git a/tests/testthat/test-path.R b/tests/testthat/test-path.R index 16c737a64..cc01acc5b 100644 --- a/tests/testthat/test-path.R +++ b/tests/testthat/test-path.R @@ -34,7 +34,7 @@ test_that("Setting path from env var is detected", { unset_cmdstan_path() expect_true(is.null(.cmdstanr$VERSION)) withr::local_envvar(c(CMDSTAN = PATH)) - expect_no_condition(cmdstanr_initialize()) + expect_silent(cmdstanr_initialize()) expect_equal(cmdstan_path(), PATH) expect_false(is.null(.cmdstanr$VERSION)) }) From b7cb68751114b1a00c3f96cec9cb0fe43750ed60 Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Wed, 25 Mar 2026 12:25:36 -0700 Subject: [PATCH 24/27] Bump testthat requirement --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index f0e727a32..9b8a93e37 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -55,7 +55,7 @@ Suggests: loo (>= 2.0.0), qs2, rmarkdown, - testthat (>= 3.0.0), + testthat (>= 3.3.0), Rcpp VignetteBuilder: knitr Config/testthat/edition: 3 From e55042f1bb77229e815de4322d816b160419bfa7 Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Wed, 25 Mar 2026 14:02:22 -0700 Subject: [PATCH 25/27] Removed brittle snapshots --- tests/testthat/test-fit-gq.R | 46 ++++++++++++++++++++------------- tests/testthat/test-fit-mcmc.R | 47 ++++++++++++++++++++-------------- tests/testthat/test-install.R | 26 ++++++++++++++++--- 3 files changed, 78 insertions(+), 41 deletions(-) diff --git a/tests/testthat/test-fit-gq.R b/tests/testthat/test-fit-gq.R index 846fbe912..951e86239 100644 --- a/tests/testthat/test-fit-gq.R +++ b/tests/testthat/test-fit-gq.R @@ -64,24 +64,34 @@ test_that("summary() method works after gq", { }) test_that("print() method works after gq", { - expect_s3_class(fit_gq$print(), "CmdStanGQ") - expect_snapshot_output({ - fit_gq$print() - fit_gq$print(max_rows = 1) - fit_gq$print(NULL, c("mad")) - }) - expect_snapshot_output({ - fit_gq$print(max_rows = 2) - fit_gq$print(max_rows = 11) - }) - expect_snapshot_output({ - fit_gq$print("y_rep", max_rows = 2) - fit_gq$print("y_rep") - }) - expect_snapshot_output({ - fit_gq$print(c("y_rep[1]", "sum_y", "y_rep[3]")) - }) - expect_snapshot_error(fit_gq$print(variable = "unknown", max_rows = 20)) + expect_output(expect_s3_class(fit_gq$print(), "CmdStanGQ"), "variable") + expect_output(fit_gq$print(max_rows = 1), "# showing 1 of 11 rows") + expect_output(fit_gq$print(NULL, c("mad")), "mad") + + expect_output(fit_gq$print(), "showing 10 of 11 rows") + expect_output(fit_gq$print(max_rows = 2), "showing 2 of 11 rows") + expect_output(fit_gq$print(max_rows = 11), "sum_y", fixed=TRUE) # last parameter + expect_output(fit_gq$print("y_rep", max_rows = 2), "showing 2 of 10 rows") + expect_error( + fit_gq$print(variable = "unknown", max_rows = 20), + "Can't find the following variable(s): unknown", + fixed = TRUE + ) # unknown parameter + + out <- capture.output(fit_gq$print("y_rep")) + expect_length(out, 11) # columns names + 1 y_rep + expect_match(out[1], "variable") + expect_match(out[2], "y_rep[1]", fixed = TRUE) + expect_match(out[9], "y_rep[8]", fixed = TRUE) + expect_false(any(grepl("sum_y|theta", out))) + + # make sure the row order is correct + out <- capture.output(fit_gq$print(c("y_rep[1]", "sum_y", "y_rep[3]"))) + expect_length(out, 4) + expect_match(out[1], " variable") + expect_match(out[2], " y_rep[1]", fixed = TRUE) + expect_match(out[3], " sum_y") + expect_match(out[4], " y_rep[3]", fixed = TRUE) }) test_that("output() method works after gq", { diff --git a/tests/testthat/test-fit-mcmc.R b/tests/testthat/test-fit-mcmc.R index 0224c2fb7..8af8a7168 100644 --- a/tests/testthat/test-fit-mcmc.R +++ b/tests/testthat/test-fit-mcmc.R @@ -123,28 +123,37 @@ test_that("summary() method works after mcmc", { }) test_that("print() method works after mcmc", { - expect_s3_class(fit_mcmc$print(), "CmdStanMCMC") + expect_output(expect_s3_class(fit_mcmc$print(), "CmdStanMCMC"), "variable") + expect_output(fit_mcmc$print(max_rows = 1), "# showing 1 of 5 rows") + expect_output(fit_mcmc$print(NULL, c("ess_sd")), "ess_sd") # test on model with more parameters fit <- cmdstanr_example("schools_ncp") - expect_snapshot_output({ - fit_mcmc$print() - fit_mcmc$print(max_rows = 1) - fit_mcmc$print(NULL, c("ess_sd")) - }) - expect_snapshot_output({ - fit$print() - fit$print(max_rows = 2) - fit$print(max_rows = 19) - }) - expect_snapshot_output({ - fit$print("theta", max_rows = 2) - fit$print("theta") - }) - expect_snapshot_output({ - fit$print(c("theta[1]", "tau", "mu", "theta_raw[3]")) - }) - expect_snapshot_error(fit$print(variable = "unknown", max_rows = 20)) + expect_output(fit$print(), "showing 10 of 19 rows") + expect_output(fit$print(max_rows = 2), "showing 2 of 19 rows") + expect_output(fit$print(max_rows = 19), "theta[8]", fixed=TRUE) # last parameter + expect_output(fit$print("theta", max_rows = 2), "showing 2 of 8 rows") + expect_error( + fit$print(variable = "unknown", max_rows = 20), + "Can't find the following variable(s): unknown", + fixed = TRUE + ) # unknown parameter + + out <- capture.output(fit$print("theta")) + expect_length(out, 9) # columns names + 8 thetas + expect_match(out[1], "variable") + expect_match(out[2], "theta[1]", fixed = TRUE) + expect_match(out[9], "theta[8]", fixed = TRUE) + expect_false(any(grepl("mu|tau|theta_raw", out))) + + # make sure the row order is correct + out <- capture.output(fit$print(c("theta[1]", "tau", "mu", "theta_raw[3]"))) + expect_length(out, 5) + expect_match(out[1], " variable") + expect_match(out[2], " theta[1]", fixed = TRUE) + expect_match(out[3], " tau") + expect_match(out[4], " mu") + expect_match(out[5], " theta_raw[3]", fixed = TRUE) }) test_that("output() method works after mcmc", { diff --git a/tests/testthat/test-install.R b/tests/testthat/test-install.R index 33fe60659..e803345d4 100644 --- a/tests/testthat/test-install.R +++ b/tests/testthat/test-install.R @@ -118,9 +118,23 @@ test_that("install_cmdstan() works with version and release_url", { test_that("toolchain checks on Unix work", { skip_if(os_is_windows()) withr::local_envvar(c("PATH" = "")) - variant <- if (os_is_macos()) "macos" else NULL - expect_snapshot_error(check_unix_cpp_compiler(), variant = variant) - expect_snapshot_error(check_unix_make(), variant = variant) + if (os_is_macos()) { + err_msg_cpp <- "A suitable C++ compiler was not found. Please install the command line tools for Mac with 'xcode-select --install' or install Xcode from the app store. Then restart R and run cmdstanr::check_cmdstan_toolchain()." + err_msg_make <- "The 'make' tool was not found. Please install the command line tools for Mac with 'xcode-select --install' or install Xcode from the app store. Then restart R and run cmdstanr::check_cmdstan_toolchain()." + } else { + err_msg_cpp <- "A C++ compiler was not found. Please install the 'clang++' or 'g++' compiler, restart R, and run cmdstanr::check_cmdstan_toolchain()." + err_msg_make <- "The 'make' tool was not found. Please install 'make', restart R, and then run cmdstanr::check_cmdstan_toolchain()." + } + expect_error( + check_unix_cpp_compiler(), + err_msg_cpp, + fixed = TRUE + ) + expect_error( + check_unix_make(), + err_msg_make, + fixed = TRUE + ) }) test_that("clean and rebuild works", { @@ -430,7 +444,11 @@ test_that("check_rtools4x_windows_toolchain reports missing Rtools and make", { rtools4x_home_path = function() "", rtools4x_version = function() "44" ) - expect_snapshot_error(check_rtools4x_windows_toolchain()) + expect_error( + check_rtools4x_windows_toolchain(), + "restart R, and then run cmdstanr::check_cmdstan_toolchain()", + fixed = TRUE + ) }) dir.create(file.path(fake_rtools_home, "usr", "bin"), From d684bc33a3135a350ba0d9aa36ad658a62678e8e Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Wed, 25 Mar 2026 18:47:09 -0700 Subject: [PATCH 26/27] Transform windows snapshots to remove .exe --- tests/testthat/test-model-compile.R | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/tests/testthat/test-model-compile.R b/tests/testthat/test-model-compile.R index 81a40e066..808142ae2 100644 --- a/tests/testthat/test-model-compile.R +++ b/tests/testthat/test-model-compile.R @@ -3,15 +3,15 @@ stan_program <- cmdstan_example_file() mod <- cmdstan_model(stan_file = stan_program, compile = FALSE) cmdstan_make_local(cpp_options = list("PRECOMPILED_HEADERS"="false")) -stanc_snapshot_variant <- function() { - if (os_is_windows() && !os_is_wsl()) "windows" else NULL -} - trim_stanc_invocations <- function(output) { out <- grep("bin/stanc", output, value = TRUE, fixed = TRUE) sub("( --o).*", "\\1", out) } +stanc_snapshot_transform <- function(lines) { + sub("bin/stanc\\.exe", "bin/stanc", lines) +} + test_that("object initialized correctly", { expect_equal(mod$stan_file(), stan_program) expect_equal(mod$exe_file(), character(0)) @@ -121,9 +121,11 @@ test_that("compilation works with include_paths", { test_that("name in STANCFLAGS is set correctly", { local_reproducible_output() - variant <- stanc_snapshot_variant() out <- utils::capture.output(mod$compile(quiet = FALSE, force_recompile = TRUE)) - expect_snapshot(cat(trim_stanc_invocations(out), sep = "\n"), variant = variant) + expect_snapshot( + cat(trim_stanc_invocations(out), sep = "\n"), + transform = stanc_snapshot_transform + ) out <- utils::capture.output( mod$compile( @@ -132,7 +134,10 @@ test_that("name in STANCFLAGS is set correctly", { stanc_options = list(name = "bernoulli2_model") ) ) - expect_snapshot(cat(trim_stanc_invocations(out), sep = "\n"), variant = variant) + expect_snapshot( + cat(trim_stanc_invocations(out), sep = "\n"), + transform = stanc_snapshot_transform + ) }) @@ -840,7 +845,6 @@ test_that("dirname of stan_file is used as include path if no other paths suppli test_that("STANCFLAGS from get_cmdstan_flags() are included in compile output", { local_reproducible_output() - variant <- stanc_snapshot_variant() real_get_cmdstan_flags <- get_cmdstan_flags local_mocked_bindings( get_cmdstan_flags = function(flag_name) { @@ -852,7 +856,10 @@ test_that("STANCFLAGS from get_cmdstan_flags() are included in compile output", } ) out <- utils::capture.output(mod$compile(quiet = FALSE, force_recompile = TRUE)) - expect_snapshot(cat(trim_stanc_invocations(out), sep = "\n"), variant = variant) + expect_snapshot( + cat(trim_stanc_invocations(out), sep = "\n"), + transform = stanc_snapshot_transform + ) }) test_that("compile() ignores directory chatter from MAKEFLAGS when reading STANCFLAGS", { From d0348c66a84fced5c9dbc353a2f857213c04f2ef Mon Sep 17 00:00:00 2001 From: VisruthSK Date: Wed, 25 Mar 2026 21:47:45 -0700 Subject: [PATCH 27/27] More snapshots --- tests/testthat/_snaps/fit-gq.md | 128 +++++++++--------- tests/testthat/_snaps/fit-mcmc.md | 142 ++++++++++---------- tests/testthat/_snaps/install.md | 24 +++- tests/testthat/helper-custom-expectations.R | 54 +++++++- tests/testthat/test-fit-gq.R | 55 ++++---- tests/testthat/test-fit-mcmc.R | 58 ++++---- tests/testthat/test-install.R | 25 ++-- tests/testthat/test-model-compile.R | 11 +- 8 files changed, 281 insertions(+), 216 deletions(-) diff --git a/tests/testthat/_snaps/fit-gq.md b/tests/testthat/_snaps/fit-gq.md index f87514a0d..28199208a 100644 --- a/tests/testthat/_snaps/fit-gq.md +++ b/tests/testthat/_snaps/fit-gq.md @@ -1,83 +1,79 @@ # print() method works after gq - variable mean median sd mad q5 q95 - y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 - y_rep[2] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[3] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[4] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[5] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[6] 0.25 0.00 0.43 0.00 0.00 1.00 - y_rep[7] 0.25 0.00 0.43 0.00 0.00 1.00 - y_rep[8] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[9] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[10] 0.27 0.00 0.44 0.00 0.00 1.00 + variable mean median sd mad q5 q95 + y_rep[1] + y_rep[2] + y_rep[3] + y_rep[4] + y_rep[5] + y_rep[6] + y_rep[7] + y_rep[8] + y_rep[9] + y_rep[10] - # showing 10 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) - variable mean median sd mad q5 q95 - y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 + # showing 10 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 + y_rep[1] - # showing 1 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) - variable mad - y_rep[1] 0.00 - y_rep[2] 0.00 - y_rep[3] 0.00 - y_rep[4] 0.00 - y_rep[5] 0.00 - y_rep[6] 0.00 - y_rep[7] 0.00 - y_rep[8] 0.00 - y_rep[9] 0.00 - y_rep[10] 0.00 + # showing 1 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mad + y_rep[1] + y_rep[2] + y_rep[3] + y_rep[4] + y_rep[5] + y_rep[6] + y_rep[7] + y_rep[8] + y_rep[9] + y_rep[10] - # showing 10 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + # showing 10 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) --- - variable mean median sd mad q5 q95 - y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 - y_rep[2] 0.26 0.00 0.44 0.00 0.00 1.00 + variable mean median sd mad q5 q95 + y_rep[1] + y_rep[2] - # showing 2 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) - variable mean median sd mad q5 q95 - y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 - y_rep[2] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[3] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[4] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[5] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[6] 0.25 0.00 0.43 0.00 0.00 1.00 - y_rep[7] 0.25 0.00 0.43 0.00 0.00 1.00 - y_rep[8] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[9] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[10] 0.27 0.00 0.44 0.00 0.00 1.00 - sum_y 2.57 2.00 1.81 1.48 0.00 6.00 + # showing 2 of 11 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 + y_rep[1] + y_rep[2] + y_rep[3] + y_rep[4] + y_rep[5] + y_rep[6] + y_rep[7] + y_rep[8] + y_rep[9] + y_rep[10] + sum_y --- - variable mean median sd mad q5 q95 - y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 - y_rep[2] 0.26 0.00 0.44 0.00 0.00 1.00 + variable mean median sd mad q5 q95 + y_rep[1] + y_rep[2] - # showing 2 of 10 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) - variable mean median sd mad q5 q95 - y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 - y_rep[2] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[3] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[4] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[5] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[6] 0.25 0.00 0.43 0.00 0.00 1.00 - y_rep[7] 0.25 0.00 0.43 0.00 0.00 1.00 - y_rep[8] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[9] 0.26 0.00 0.44 0.00 0.00 1.00 - y_rep[10] 0.27 0.00 0.44 0.00 0.00 1.00 + # showing 2 of 10 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 + y_rep[1] + y_rep[2] + y_rep[3] + y_rep[4] + y_rep[5] + y_rep[6] + y_rep[7] + y_rep[8] + y_rep[9] + y_rep[10] --- - variable mean median sd mad q5 q95 - y_rep[1] 0.24 0.00 0.43 0.00 0.00 1.00 - sum_y 2.57 2.00 1.81 1.48 0.00 6.00 - y_rep[3] 0.26 0.00 0.44 0.00 0.00 1.00 - ---- - - Can't find the following variable(s): unknown + variable mean median sd mad q5 q95 + y_rep[1] + sum_y + y_rep[3] diff --git a/tests/testthat/_snaps/fit-mcmc.md b/tests/testthat/_snaps/fit-mcmc.md index 3bfab0abe..19b6d705c 100644 --- a/tests/testthat/_snaps/fit-mcmc.md +++ b/tests/testthat/_snaps/fit-mcmc.md @@ -1,89 +1,85 @@ # print() method works after mcmc - variable mean median sd mad q5 q95 rhat ess_bulk ess_tail - lp__ -66.02 -65.64 1.52 1.30 -68.93 -64.26 1.00 1133 1459 - alpha 0.38 0.38 0.21 0.22 0.03 0.72 1.00 2453 1657 - beta[1] -0.67 -0.67 0.27 0.27 -1.12 -0.23 1.00 2130 1458 - beta[2] -0.28 -0.28 0.23 0.22 -0.66 0.09 1.00 2114 1602 - beta[3] 0.68 0.67 0.27 0.26 0.26 1.15 1.00 1859 1386 - variable mean median sd mad q5 q95 rhat ess_bulk ess_tail - lp__ -66.02 -65.64 1.52 1.30 -68.93 -64.26 1.00 1133 1459 + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + lp__ + alpha + beta[1] + beta[2] + beta[3] + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + lp__ - # showing 1 of 5 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) - variable ess_sd - lp__ 1739 - alpha 1218 - beta[1] 953 - beta[2] 1233 - beta[3] 1107 + # showing 1 of 5 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable ess_sd + lp__ + alpha + beta[1] + beta[2] + beta[3] --- - variable mean median sd mad q5 q95 rhat ess_bulk ess_tail - lp__ -46.94 -46.65 2.41 2.29 -51.21 -43.62 1.00 1683 2482 - mu 6.41 6.39 4.14 3.99 -0.36 13.19 1.00 3229 2346 - tau 4.57 3.82 3.53 3.39 0.30 11.37 1.00 1872 1873 - theta_raw[1] 0.34 0.34 0.97 0.97 -1.27 1.94 1.00 3528 2997 - theta_raw[2] 0.03 0.02 0.91 0.91 -1.45 1.53 1.00 3825 2656 - theta_raw[3] -0.13 -0.13 0.96 0.98 -1.72 1.46 1.00 3995 2977 - theta_raw[4] -0.02 -0.01 0.93 0.94 -1.55 1.50 1.00 4429 3181 - theta_raw[5] -0.27 -0.27 0.90 0.91 -1.76 1.23 1.00 3815 2999 - theta_raw[6] -0.12 -0.13 0.91 0.93 -1.62 1.39 1.00 4049 3220 - theta_raw[7] 0.34 0.36 0.94 0.95 -1.20 1.88 1.00 3165 2681 + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + lp__ + mu + tau + theta_raw[1] + theta_raw[2] + theta_raw[3] + theta_raw[4] + theta_raw[5] + theta_raw[6] + theta_raw[7] - # showing 10 of 19 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) - variable mean median sd mad q5 q95 rhat ess_bulk ess_tail - lp__ -46.94 -46.65 2.41 2.29 -51.21 -43.62 1.00 1683 2482 - mu 6.41 6.39 4.14 3.99 -0.36 13.19 1.00 3229 2346 + # showing 10 of 19 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + lp__ + mu - # showing 2 of 19 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) - variable mean median sd mad q5 q95 rhat ess_bulk ess_tail - lp__ -46.94 -46.65 2.41 2.29 -51.21 -43.62 1.00 1683 2482 - mu 6.41 6.39 4.14 3.99 -0.36 13.19 1.00 3229 2346 - tau 4.57 3.82 3.53 3.39 0.30 11.37 1.00 1872 1873 - theta_raw[1] 0.34 0.34 0.97 0.97 -1.27 1.94 1.00 3528 2997 - theta_raw[2] 0.03 0.02 0.91 0.91 -1.45 1.53 1.00 3825 2656 - theta_raw[3] -0.13 -0.13 0.96 0.98 -1.72 1.46 1.00 3995 2977 - theta_raw[4] -0.02 -0.01 0.93 0.94 -1.55 1.50 1.00 4429 3181 - theta_raw[5] -0.27 -0.27 0.90 0.91 -1.76 1.23 1.00 3815 2999 - theta_raw[6] -0.12 -0.13 0.91 0.93 -1.62 1.39 1.00 4049 3220 - theta_raw[7] 0.34 0.36 0.94 0.95 -1.20 1.88 1.00 3165 2681 - theta_raw[8] 0.08 0.09 0.96 0.94 -1.49 1.67 1.00 4409 2743 - theta[1] 8.74 8.00 6.56 5.59 -0.63 20.60 1.00 3530 3065 - theta[2] 6.60 6.56 5.50 5.04 -2.43 15.53 1.00 4324 3214 - theta[3] 5.64 5.95 6.23 5.46 -4.95 15.30 1.00 4005 2780 - theta[4] 6.37 6.42 5.75 5.16 -3.29 15.57 1.00 4500 2943 - theta[5] 4.81 5.15 5.60 5.03 -4.97 13.29 1.00 4512 3546 - theta[6] 5.61 5.80 5.71 5.17 -3.76 14.60 1.00 4550 3534 - theta[7] 8.59 8.05 5.91 5.44 0.01 19.28 1.00 4050 3348 - theta[8] 6.89 6.66 6.31 5.42 -3.04 17.26 1.00 4332 2825 + # showing 2 of 19 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + lp__ + mu + tau + theta_raw[1] + theta_raw[2] + theta_raw[3] + theta_raw[4] + theta_raw[5] + theta_raw[6] + theta_raw[7] + theta_raw[8] + theta[1] + theta[2] + theta[3] + theta[4] + theta[5] + theta[6] + theta[7] + theta[8] --- - variable mean median sd mad q5 q95 rhat ess_bulk ess_tail - theta[1] 8.74 8.00 6.56 5.59 -0.63 20.60 1.00 3530 3065 - theta[2] 6.60 6.56 5.50 5.04 -2.43 15.53 1.00 4324 3214 + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + theta[1] + theta[2] - # showing 2 of 8 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) - variable mean median sd mad q5 q95 rhat ess_bulk ess_tail - theta[1] 8.74 8.00 6.56 5.59 -0.63 20.60 1.00 3530 3065 - theta[2] 6.60 6.56 5.50 5.04 -2.43 15.53 1.00 4324 3214 - theta[3] 5.64 5.95 6.23 5.46 -4.95 15.30 1.00 4005 2780 - theta[4] 6.37 6.42 5.75 5.16 -3.29 15.57 1.00 4500 2943 - theta[5] 4.81 5.15 5.60 5.03 -4.97 13.29 1.00 4512 3546 - theta[6] 5.61 5.80 5.71 5.17 -3.76 14.60 1.00 4550 3534 - theta[7] 8.59 8.05 5.91 5.44 0.01 19.28 1.00 4050 3348 - theta[8] 6.89 6.66 6.31 5.42 -3.04 17.26 1.00 4332 2825 + # showing 2 of 8 rows (change via 'max_rows' argument or 'cmdstanr_max_rows' option) + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + theta[1] + theta[2] + theta[3] + theta[4] + theta[5] + theta[6] + theta[7] + theta[8] --- - variable mean median sd mad q5 q95 rhat ess_bulk ess_tail - theta[1] 8.74 8.00 6.56 5.59 -0.63 20.60 1.00 3530 3065 - tau 4.57 3.82 3.53 3.39 0.30 11.37 1.00 1872 1873 - mu 6.41 6.39 4.14 3.99 -0.36 13.19 1.00 3229 2346 - theta_raw[3] -0.13 -0.13 0.96 0.98 -1.72 1.46 1.00 3995 2977 - ---- - - Can't find the following variable(s): unknown + variable mean median sd mad q5 q95 rhat ess_bulk ess_tail + theta[1] + tau + mu + theta_raw[3] diff --git a/tests/testthat/_snaps/install.md b/tests/testthat/_snaps/install.md index b00f15a20..a0fcc57a4 100644 --- a/tests/testthat/_snaps/install.md +++ b/tests/testthat/_snaps/install.md @@ -14,18 +14,30 @@ # toolchain checks on Unix work - A C++ compiler was not found. Please install the 'clang++' or 'g++' compiler, restart R, and run cmdstanr::check_cmdstan_toolchain(). + Code + check_unix_cpp_compiler() + Condition + Error: + ! C++ compiler missing. --- - The 'make' tool was not found. Please install 'make', restart R, and then run cmdstanr::check_cmdstan_toolchain(). + Code + check_unix_make() + Condition + Error: + ! make missing. # check_rtools4x_windows_toolchain reports missing Rtools and make - - Rtools44 was not found but is required to run CmdStan with R version 4.5.2. - Please install or reinstall the appropriate Rtools version for this R installation, - restart R, and then run cmdstanr::check_cmdstan_toolchain(). + Code + check_rtools4x_windows_toolchain() + Condition + Error: + ! + Rtools was not found but is required to run CmdStan with R version . + Please install or reinstall the appropriate Rtools version for this R installation, + restart R, and then run cmdstanr::check_cmdstan_toolchain(). # check_rtools4x_windows_toolchain validates install path and empty candidates diff --git a/tests/testthat/helper-custom-expectations.R b/tests/testthat/helper-custom-expectations.R index 2bda8e329..731bd13d7 100644 --- a/tests/testthat/helper-custom-expectations.R +++ b/tests/testthat/helper-custom-expectations.R @@ -1,7 +1,11 @@ #' @param ... arguments passed to mod$compile() expect_compilation <- function(mod, ...) { + mtime_check_enabled <- FALSE if(length(mod$exe_file()) > 0 && file.exists(mod$exe_file())) { + original_mtime <- file.mtime(mod$exe_file()) + suppressWarnings(Sys.setFileTime(mod$exe_file(), original_mtime - 10)) before_mtime <- file.mtime(mod$exe_file()) + mtime_check_enabled <- before_mtime < original_mtime } else { before_mtime <- NULL } @@ -11,9 +15,13 @@ expect_compilation <- function(mod, ...) { if(length(mod$exe_file()) == 0 || !file.exists(mod$exe_file())) { fail(sprint("Model executable '%s' does not exist after compilation.", mod$exe_file())) } - if(!is.null(before_mtime)) { + if(!is.null(before_mtime) && mtime_check_enabled) { after_mtime <- file.mtime(mod$exe_file()) - expect_true(before_mtime != after_mtime, sprintf("Exe file '%s' has NOT changed, despite expecting (re)compilation", mod$exe_file())) + expect_gt( + after_mtime, + before_mtime, + sprintf("Exe file '%s' has NOT changed, despite expecting (re)compilation", mod$exe_file()) + ) } invisible(mod) } @@ -23,7 +31,7 @@ expect_compilation <- function(mod, ...) { #' @return the newly created model expect_call_compilation <- function(constructor_call) { constructor_call <- substitute(constructor_call) - before_time <- Sys.time() + before_time <- Sys.time() - 10 rlang::with_interactive(value = TRUE, { expect_message( mod <- rlang::eval_bare(constructor_call, parent.frame()), @@ -118,3 +126,43 @@ expect_equal_ignore_order <- function(object, expected, ...) { } expect_not_true <- function(...) expect_false(isTRUE(...)) + +transform_print_snapshot <- function(x) { + vapply(x, function(line) { + line <- trimws(line) + if (!nzchar(line)) { + return(line) + } + if (grepl("^variable\\b", line)) { + return(gsub("\\s+", " ", line)) + } + if (grepl("^# showing", line) || + grepl("^Can't find the following variable\\(s\\):", line)) { + return(line) + } + sub("\\s+.*$", "", line) + }, character(1)) +} + +transform_unix_toolchain_snapshot <- function(x) { + x <- gsub("A suitable C\\+\\+ compiler was not found\\..*", "C++ compiler missing.", x) + x <- gsub("A C\\+\\+ compiler was not found\\..*", "C++ compiler missing.", x) + gsub("The 'make' tool was not found\\..*", "make missing.", x) +} + +transform_r_version_snapshot <- function(x) { + x <- gsub("Rtools[0-9]+", "Rtools", x) + gsub("R version [0-9]+\\.[0-9]+\\.[0-9]+", "R version ", x) +} + +capture_print_snapshot <- function(code) { + code <- substitute(code) + value <- NULL + lines <- utils::capture.output( + value <- rlang::eval_bare(code, parent.frame()) + ) + list( + value = value, + lines = transform_print_snapshot(lines) + ) +} diff --git a/tests/testthat/test-fit-gq.R b/tests/testthat/test-fit-gq.R index 951e86239..6a47c3c30 100644 --- a/tests/testthat/test-fit-gq.R +++ b/tests/testthat/test-fit-gq.R @@ -64,34 +64,41 @@ test_that("summary() method works after gq", { }) test_that("print() method works after gq", { - expect_output(expect_s3_class(fit_gq$print(), "CmdStanGQ"), "variable") - expect_output(fit_gq$print(max_rows = 1), "# showing 1 of 11 rows") - expect_output(fit_gq$print(NULL, c("mad")), "mad") - - expect_output(fit_gq$print(), "showing 10 of 11 rows") - expect_output(fit_gq$print(max_rows = 2), "showing 2 of 11 rows") - expect_output(fit_gq$print(max_rows = 11), "sum_y", fixed=TRUE) # last parameter - expect_output(fit_gq$print("y_rep", max_rows = 2), "showing 2 of 10 rows") + printed_fit_gq <- capture_print_snapshot(fit_gq$print()) + expect_s3_class(printed_fit_gq$value, "CmdStanGQ") + expect_snapshot_output(cat( + paste(printed_fit_gq$lines, collapse = "\n"), + "\n", + paste(capture_print_snapshot(fit_gq$print(max_rows = 1))$lines, collapse = "\n"), + "\n", + paste(capture_print_snapshot(fit_gq$print(NULL, c("mad")))$lines, collapse = "\n"), + "\n", + sep = "" + )) + expect_snapshot_output(cat( + paste(capture_print_snapshot(fit_gq$print(max_rows = 2))$lines, collapse = "\n"), + "\n", + paste(capture_print_snapshot(fit_gq$print(max_rows = 11))$lines, collapse = "\n"), + "\n", + sep = "" + )) + expect_snapshot_output(cat( + paste(capture_print_snapshot(fit_gq$print("y_rep", max_rows = 2))$lines, collapse = "\n"), + "\n", + paste(capture_print_snapshot(fit_gq$print("y_rep"))$lines, collapse = "\n"), + "\n", + sep = "" + )) + expect_snapshot_output(cat( + paste(capture_print_snapshot(fit_gq$print(c("y_rep[1]", "sum_y", "y_rep[3]")))$lines, collapse = "\n"), + "\n", + sep = "" + )) expect_error( fit_gq$print(variable = "unknown", max_rows = 20), "Can't find the following variable(s): unknown", fixed = TRUE - ) # unknown parameter - - out <- capture.output(fit_gq$print("y_rep")) - expect_length(out, 11) # columns names + 1 y_rep - expect_match(out[1], "variable") - expect_match(out[2], "y_rep[1]", fixed = TRUE) - expect_match(out[9], "y_rep[8]", fixed = TRUE) - expect_false(any(grepl("sum_y|theta", out))) - - # make sure the row order is correct - out <- capture.output(fit_gq$print(c("y_rep[1]", "sum_y", "y_rep[3]"))) - expect_length(out, 4) - expect_match(out[1], " variable") - expect_match(out[2], " y_rep[1]", fixed = TRUE) - expect_match(out[3], " sum_y") - expect_match(out[4], " y_rep[3]", fixed = TRUE) + ) }) test_that("output() method works after gq", { diff --git a/tests/testthat/test-fit-mcmc.R b/tests/testthat/test-fit-mcmc.R index 8af8a7168..45fb7ee26 100644 --- a/tests/testthat/test-fit-mcmc.R +++ b/tests/testthat/test-fit-mcmc.R @@ -123,37 +123,45 @@ test_that("summary() method works after mcmc", { }) test_that("print() method works after mcmc", { - expect_output(expect_s3_class(fit_mcmc$print(), "CmdStanMCMC"), "variable") - expect_output(fit_mcmc$print(max_rows = 1), "# showing 1 of 5 rows") - expect_output(fit_mcmc$print(NULL, c("ess_sd")), "ess_sd") - + printed_fit_mcmc <- capture_print_snapshot(fit_mcmc$print()) + expect_s3_class(printed_fit_mcmc$value, "CmdStanMCMC") + expect_snapshot_output(cat( + paste(printed_fit_mcmc$lines, collapse = "\n"), + "\n", + paste(capture_print_snapshot(fit_mcmc$print(max_rows = 1))$lines, collapse = "\n"), + "\n", + paste(capture_print_snapshot(fit_mcmc$print(NULL, c("ess_sd")))$lines, collapse = "\n"), + "\n", + sep = "" + )) # test on model with more parameters fit <- cmdstanr_example("schools_ncp") - expect_output(fit$print(), "showing 10 of 19 rows") - expect_output(fit$print(max_rows = 2), "showing 2 of 19 rows") - expect_output(fit$print(max_rows = 19), "theta[8]", fixed=TRUE) # last parameter - expect_output(fit$print("theta", max_rows = 2), "showing 2 of 8 rows") + expect_snapshot_output(cat( + paste(capture_print_snapshot(fit$print())$lines, collapse = "\n"), + "\n", + paste(capture_print_snapshot(fit$print(max_rows = 2))$lines, collapse = "\n"), + "\n", + paste(capture_print_snapshot(fit$print(max_rows = 19))$lines, collapse = "\n"), + "\n", + sep = "" + )) + expect_snapshot_output(cat( + paste(capture_print_snapshot(fit$print("theta", max_rows = 2))$lines, collapse = "\n"), + "\n", + paste(capture_print_snapshot(fit$print("theta"))$lines, collapse = "\n"), + "\n", + sep = "" + )) + expect_snapshot_output(cat( + paste(capture_print_snapshot(fit$print(c("theta[1]", "tau", "mu", "theta_raw[3]")))$lines, collapse = "\n"), + "\n", + sep = "" + )) expect_error( fit$print(variable = "unknown", max_rows = 20), "Can't find the following variable(s): unknown", fixed = TRUE - ) # unknown parameter - - out <- capture.output(fit$print("theta")) - expect_length(out, 9) # columns names + 8 thetas - expect_match(out[1], "variable") - expect_match(out[2], "theta[1]", fixed = TRUE) - expect_match(out[9], "theta[8]", fixed = TRUE) - expect_false(any(grepl("mu|tau|theta_raw", out))) - - # make sure the row order is correct - out <- capture.output(fit$print(c("theta[1]", "tau", "mu", "theta_raw[3]"))) - expect_length(out, 5) - expect_match(out[1], " variable") - expect_match(out[2], " theta[1]", fixed = TRUE) - expect_match(out[3], " tau") - expect_match(out[4], " mu") - expect_match(out[5], " theta_raw[3]", fixed = TRUE) + ) }) test_that("output() method works after mcmc", { diff --git a/tests/testthat/test-install.R b/tests/testthat/test-install.R index e803345d4..86011d5e6 100644 --- a/tests/testthat/test-install.R +++ b/tests/testthat/test-install.R @@ -118,22 +118,15 @@ test_that("install_cmdstan() works with version and release_url", { test_that("toolchain checks on Unix work", { skip_if(os_is_windows()) withr::local_envvar(c("PATH" = "")) - if (os_is_macos()) { - err_msg_cpp <- "A suitable C++ compiler was not found. Please install the command line tools for Mac with 'xcode-select --install' or install Xcode from the app store. Then restart R and run cmdstanr::check_cmdstan_toolchain()." - err_msg_make <- "The 'make' tool was not found. Please install the command line tools for Mac with 'xcode-select --install' or install Xcode from the app store. Then restart R and run cmdstanr::check_cmdstan_toolchain()." - } else { - err_msg_cpp <- "A C++ compiler was not found. Please install the 'clang++' or 'g++' compiler, restart R, and run cmdstanr::check_cmdstan_toolchain()." - err_msg_make <- "The 'make' tool was not found. Please install 'make', restart R, and then run cmdstanr::check_cmdstan_toolchain()." - } - expect_error( + expect_snapshot( check_unix_cpp_compiler(), - err_msg_cpp, - fixed = TRUE + error = TRUE, + transform = transform_unix_toolchain_snapshot ) - expect_error( + expect_snapshot( check_unix_make(), - err_msg_make, - fixed = TRUE + error = TRUE, + transform = transform_unix_toolchain_snapshot ) }) @@ -444,10 +437,10 @@ test_that("check_rtools4x_windows_toolchain reports missing Rtools and make", { rtools4x_home_path = function() "", rtools4x_version = function() "44" ) - expect_error( + expect_snapshot( check_rtools4x_windows_toolchain(), - "restart R, and then run cmdstanr::check_cmdstan_toolchain()", - fixed = TRUE + error = TRUE, + transform = transform_r_version_snapshot ) }) diff --git a/tests/testthat/test-model-compile.R b/tests/testthat/test-model-compile.R index 808142ae2..2e20f7ee9 100644 --- a/tests/testthat/test-model-compile.R +++ b/tests/testthat/test-model-compile.R @@ -4,12 +4,16 @@ mod <- cmdstan_model(stan_file = stan_program, compile = FALSE) cmdstan_make_local(cpp_options = list("PRECOMPILED_HEADERS"="false")) trim_stanc_invocations <- function(output) { + output <- gsub("\\\\", "/", output) out <- grep("bin/stanc", output, value = TRUE, fixed = TRUE) + out <- sub("^.*?(bin/stanc(?:\\.exe)?)\\b", "\\1", out, perl = TRUE) sub("( --o).*", "\\1", out) } stanc_snapshot_transform <- function(lines) { - sub("bin/stanc\\.exe", "bin/stanc", lines) + lines <- gsub("\\\\", "/", lines) + lines <- sub("^.*?(bin/stanc)(?:\\.exe)?\\b", "\\1", lines, perl = TRUE) + gsub("--name=(['\"])?([^ '\"=]+)\\1", "--name='\\2'", lines, perl = TRUE) } test_that("object initialized correctly", { @@ -83,9 +87,10 @@ test_that("compile() method works with spaces in path", { test_that("compile() method overwrites binaries", { mod$compile(quiet = TRUE) - old_time = file.mtime(mod$exe_file()) + old_time <- file.mtime(mod$exe_file()) - 10 + Sys.setFileTime(mod$exe_file(), old_time) + old_time <- file.mtime(mod$exe_file()) mod$compile(quiet = TRUE, force_recompile = TRUE) - new_time = expect_gt(file.mtime(mod$exe_file()), old_time) })