Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions .github/workflows/Test-coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ permissions:
contents: read
id-token: write

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
test-coverage:
if: "! contains(github.event.head_commit.message, '[ci skip]')"
Expand All @@ -37,12 +41,6 @@ jobs:
echo "CMDSTAN_PATH=${HOME}/.cmdstan" >> $GITHUB_ENV
shell: bash

- uses: n1hility/cancel-previous-runs@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
workflow: Test-coverage.yaml
if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/master'"

- uses: actions/checkout@v7

- uses: r-lib/actions/setup-r@v2
Expand Down
25 changes: 4 additions & 21 deletions tests/testthat/test-install.R
Original file line number Diff line number Diff line change
Expand Up @@ -533,11 +533,12 @@ test_that("install_cmdstan() rejects a non-logical copy_make_local", {
# Windows toolchain discovery tests ----------------------------------------

test_that("toolchain_PATH_env_var() returns NULL on non-Windows", {
skip_if(os_is_windows())

old_cache <- .cmdstanr$TOOLCHAIN_PATH
on.exit(.cmdstanr$TOOLCHAIN_PATH <- old_cache)

.cmdstanr$TOOLCHAIN_PATH <- NULL
local_mocked_bindings(os_is_windows = function() FALSE)
expect_null(toolchain_PATH_env_var())
})

Expand Down Expand Up @@ -577,7 +578,6 @@ test_that("toolchain_PATH_env_var() uses RTOOLS40_HOME for R < 4.2", {
.cmdstanr$TOOLCHAIN_PATH <- NULL
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.1.0"),
short_path = function(path) path,
repair_path = function(path) path
Expand All @@ -603,7 +603,6 @@ test_that("toolchain_PATH_env_var() uses RTOOLS40_HOME for R < 4.2", {
.cmdstanr$TOOLCHAIN_PATH <- NULL
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.1.0"),
short_path = function(path) path,
repair_path = function(path) path
Expand All @@ -624,14 +623,15 @@ test_that("toolchain_PATH_env_var() uses RTOOLS40_HOME for R < 4.2", {
})

test_that("toolchain_PATH_env_var() compares R versions numerically", {
skip_if(!os_is_windows())

old_cache <- .cmdstanr$TOOLCHAIN_PATH
on.exit(.cmdstanr$TOOLCHAIN_PATH <- old_cache)

fake_home <- withr::local_tempdir(pattern = "rtools-home-")
rcmd_calls <- 0L
.cmdstanr$TOOLCHAIN_PATH <- NULL
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.10.0"),
.cmdstanr_rcmd = function(...) {
rcmd_calls <<- rcmd_calls + 1L
Expand Down Expand Up @@ -665,7 +665,6 @@ test_that("toolchain_PATH_env_var() uses configured R_TOOLS_SOFT", {
.cmdstanr$TOOLCHAIN_PATH <- NULL
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.2.0"),
.cmdstanr_rcmd = function(..., stdout = FALSE) fake_soft,
short_path = function(path) path,
Expand Down Expand Up @@ -706,7 +705,6 @@ test_that("toolchain_PATH_env_var() falls back to Sys.which() when Rcmd fails",
.cmdstanr$TOOLCHAIN_PATH <- NULL
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.2.0"),
short_path = function(path) path,
repair_path = function(path) path
Expand Down Expand Up @@ -743,7 +741,6 @@ test_that("toolchain_PATH_env_var() searches PATH when R_TOOLS_SOFT is empty", {
.cmdstanr$TOOLCHAIN_PATH <- NULL
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.2.0"),
.cmdstanr_rcmd = function(..., stdout = FALSE) "",
short_path = function(path) path,
Expand Down Expand Up @@ -780,7 +777,6 @@ test_that("toolchain_PATH_env_var() returns NULL when both approaches fail", {
.cmdstanr$TOOLCHAIN_PATH <- NULL
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.2.0"),
short_path = function(path) path,
repair_path = function(path) path
Expand All @@ -807,7 +803,6 @@ test_that("toolchain_PATH_env_var() returns NULL when only one tool in PATH", {
.cmdstanr$TOOLCHAIN_PATH <- NULL
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.2.0"),
short_path = function(path) path,
repair_path = function(path) path
Expand Down Expand Up @@ -840,7 +835,6 @@ test_that("toolchain_PATH_env_var() falls back to PATH when executables missing
.cmdstanr$TOOLCHAIN_PATH <- NULL
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.2.0"),
short_path = function(path) path,
repair_path = function(path) path
Expand Down Expand Up @@ -876,7 +870,6 @@ test_that("toolchain_PATH_env_var() preserves configured compiler", {
.cmdstanr$TOOLCHAIN_PATH <- NULL
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.2.0"),
.cmdstanr_rcmd = function(..., stdout = FALSE) fake_soft,
short_path = function(path) path,
Expand Down Expand Up @@ -921,7 +914,6 @@ test_that("toolchain_PATH_env_var() preserves configured make", {
.cmdstanr$TOOLCHAIN_PATH <- NULL
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.2.0"),
.cmdstanr_rcmd = function(..., stdout = FALSE) fake_soft,
short_path = function(path) path,
Expand Down Expand Up @@ -967,7 +959,6 @@ test_that("toolchain_PATH_env_var() rejects unsafe toolchain paths", {
.cmdstanr$TOOLCHAIN_PATH <- NULL
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.2.0"),
.cmdstanr_rcmd = function(..., stdout = FALSE) fake_soft,
short_path = function(path) path,
Expand All @@ -994,34 +985,26 @@ test_that("is_ucrt_toolchain() returns correct values for R versions", {
# is_ucrt_toolchain() is TRUE for R 4.2.x – 4.x.x on Windows
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.2.0")
)
expect_true(is_ucrt_toolchain())
})
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.4.0")
)
expect_true(is_ucrt_toolchain())
})
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("4.1.0")
)
expect_false(is_ucrt_toolchain())
})
local({
local_mocked_bindings(
os_is_windows = function() TRUE,
current_r_version = function() numeric_version("5.0.0")
)
expect_false(is_ucrt_toolchain())
})
local({
local_mocked_bindings(os_is_windows = function() FALSE)
expect_false(is_ucrt_toolchain())
})
})
93 changes: 0 additions & 93 deletions tests/testthat/test-model-output_dir.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,98 +4,6 @@ local_output_sandbox <- function(pattern = "sandbox", .local_envir = parent.fram
withr::local_tempdir(pattern = pattern, .local_envir = .local_envir)
}

test_that("WSL output paths stay host-native until command composition", {
# Use minimal method arguments so this test exercises path handling without
# launching CmdStan.
method_args <- list(
method = "sample",
save_metric = NULL,
validate = function(num_procs) invisible(),
compose = function(idx, args) args
)
# Cover system and non-system Windows drives as well as a WSL UNC path.
host_dirs <- c(
"C:/output",
"D:/output",
"//wsl$/Ubuntu/home/user/output"
)
wsl_dirs <- c(
"/mnt/c/output",
"/mnt/d/output",
"/home/user/output"
)
as_wsl_path <- function(path = NULL, revert = FALSE) {
if (is.null(path) || revert) {
return(path)
}
path <- sub("//wsl$/Ubuntu", "", path, fixed = TRUE)
for (i in seq_along(host_dirs)) {
path <- sub(host_dirs[i], wsl_dirs[i], path, fixed = TRUE)
}
path
}
# Simulate Windows R using WSL so this boundary test runs on every platform.
with_mocked_bindings(
{
args <- lapply(host_dirs, function(output_dir) {
CmdStanArgs$new(
model_name = "model",
exe_file = "model",
proc_ids = 1,
method_args = method_args,
output_dir = output_dir,
output_basename = "model"
)
})
output_files <- file.path(host_dirs, "model-01.csv")
expect_equal(
vapply(args, function(x) x$output_dir, character(1)),
host_dirs
)
expect_equal(
vapply(args, function(x) x$new_files("output"), character(1)),
output_files
)
cmdstan_output_files <- vapply(seq_along(args), function(i) {
command_args <- args[[i]]$compose_all_args(
output_file = output_files[i]
)
sub("file=", "", command_args[grepl("^file=", command_args)], fixed = TRUE)
}, character(1))
expect_equal(cmdstan_output_files, file.path(wsl_dirs, "model-01.csv"))

command_args <- args[[1]]$compose_all_args(
output_file = output_files[1],
profile_file = file.path(host_dirs[1], "model-profile-01.csv"),
latent_dynamics_file = file.path(host_dirs[1], "model-diagnostic-01.csv")
)
expect_in("diagnostic_file=/mnt/c/output/model-diagnostic-01.csv", command_args)
expect_in("profile_file=/mnt/c/output/model-profile-01.csv", command_args)

# Omitting output_dir must still use the faster WSL-native temp directory.
default_args <- CmdStanArgs$new(
model_name = "model",
exe_file = "model",
proc_ids = 1,
method_args = method_args,
output_basename = "model"
)
expect_equal(default_args$output_dir, "//wsl$/Ubuntu/tmp/cmdstanr")
expect_in(
"file=/tmp/cmdstanr/model-01.csv",
default_args$compose_all_args(
output_file = default_args$new_files("output")
)
)
},
os_is_wsl = function() TRUE,
wsl_safe_path = as_wsl_path,
wsl_dir_prefix = function(...) "//wsl$/Ubuntu",
wsl_tempdir = function() "/tmp/cmdstanr",
validate_cmdstan_args = function(self) invisible()
)
})

test_that("all fitting methods work with output_dir", {
sandbox <- local_output_sandbox()
for (method in c("sample", "optimize", "variational")) {
Expand Down Expand Up @@ -171,7 +79,6 @@ test_that("all fitting methods work with output_dir", {

test_that("explicit WSL output paths are usable by Windows R", {
skip_if_not(os_is_wsl())
# Unlike the mocked test above, this exercises the full Windows/WSL workflow.
output_dir <- local_output_sandbox("wsl-output-dir")
mod <- testing_model("logistic_profiling")
utils::capture.output(
Expand Down
50 changes: 21 additions & 29 deletions tests/testthat/test-utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -427,35 +427,27 @@ test_that("repair_path works with multiple paths", {
})

test_that("wsl_safe_path() works with multiple paths", {
with_mocked_bindings(
{
expect_equal(
wsl_safe_path(
c(
"/mnt/c/project/init-1.json",
"/mnt/d/project/init-2.json",
"relative/init-3.json"
),
revert = TRUE
),
c(
"C:/project/init-1.json",
"D:/project/init-2.json",
"relative/init-3.json"
)
)
expect_equal(
wsl_safe_path(
c(
"//wsl$/Ubuntu/tmp/init-1.json",
"//wsl$/Ubuntu/tmp/init-2.json"
)
),
c("/tmp/init-1.json", "/tmp/init-2.json")
)
},
os_is_wsl = function() TRUE,
wsl_dir_prefix = function(...) "//wsl$/Ubuntu"
skip_if_not(os_is_wsl())
expect_equal(
wsl_safe_path(
c(
"/mnt/c/project/init-1.json",
"/mnt/d/project/init-2.json",
"relative/init-3.json"
),
revert = TRUE
),
c(
"C:/project/init-1.json",
"D:/project/init-2.json",
"relative/init-3.json"
)
)
expect_equal(
wsl_safe_path(
paste0(wsl_dir_prefix(), c("/tmp/init-1.json", "/tmp/init-2.json"))
),
c("/tmp/init-1.json", "/tmp/init-2.json")
)
})

Expand Down
Loading