diff --git a/R/model.R b/R/model.R index 5414b7bb..69a37bc5 100644 --- a/R/model.R +++ b/R/model.R @@ -1,4 +1,3 @@ - #' Create a new CmdStanModel object #' #' @description \if{html}{\figure{logo.png}{options: width=25}} @@ -252,7 +251,7 @@ CmdStanModel <- R6::R6Class( private$stan_file_ <- absolute_path(stan_file) private$stan_code_ <- readLines(stan_file) private$model_name_ <- sub(" ", "_", strip_ext(basename(private$stan_file_))) - private$precompile_cpp_options_ <- validate_precompile_cpp_options(args$cpp_options) %||% list() + private$precompile_cpp_options_ <- validate_cpp_options(args$cpp_options) %||% list() private$precompile_stanc_options_ <- assert_valid_stanc_options(args$stanc_options) %||% list() if (!is.null(args$user_header) || !is.null(args$cpp_options[["USER_HEADER"]]) || !is.null(args$cpp_options[["user_header"]])) { @@ -350,7 +349,7 @@ CmdStanModel <- R6::R6Class( info <- if (cli_info_success) parse_exe_info_string(ret$stdout) else list() cpp_options <- exe_info_style_cpp_options(private$precompile_cpp_options_) compiled_with_cpp_options <- !is.null(private$cmdstan_version_) - + private$exe_info_ <- if (compiled_with_cpp_options) { # recompile has occurred since the CmdStanModel was created # cpp_options as were used as configured @@ -367,8 +366,9 @@ CmdStanModel <- R6::R6Class( } else { # info cli failure + no compile/recompile has occurred warning( - 'Retrieving exe_file info failed. ', - 'This may be due to running a model that was compiled with pre-2.26.1 cmdstan.' + "Retrieving exe_file info failed. ", + "This may be due to running a model ", + "that was compiled with pre-2.26.1 cmdstan." ) NULL } @@ -389,18 +389,20 @@ CmdStanModel <- R6::R6Class( # because that value is only set if model has been recomplied # since CmdStanModel instantiation if (!fallback) { - return(self$exe_info()[['stan_version']]) + return(self$exe_info()[["stan_version"]]) } for (candidate in c( - self$exe_info()[['stan_version']], - self$exe_info_fallback()[['stan_version']] - )) if (!is.null(candidate)) return (candidate) + self$exe_info()[["stan_version"]], + self$exe_info_fallback()[["stan_version"]] + )) if (!is.null(candidate)) return(candidate) }, cpp_options = function() { warning( - 'mod$cpp_options() will be deprecated in the next major version of cmdstanr. ', - 'Use mod$exe_info() to see options from last compilation. ', - 'Use mod$precompile_cpp_options() to see default options for next compilation.' + "mod$cpp_options() will be deprecated ", + "in the next major version of cmdstanr. ", + "Use mod$exe_info() to see options from last compilation. ", + "Use mod$precompile_cpp_options() ", + "to see default options for next compilation." ) private$cpp_options_ }, @@ -562,12 +564,15 @@ compile <- function(quiet = TRUE, if (!is.null(user_header) && ( !is.null(cpp_options[["USER_HEADER"]]) || !is.null(cpp_options[["user_header"]]) - )) warning("User header specified both via user_header argument and via cpp_options arguments") + )) warning( + "User header specified both via user_header argument ", + "and via cpp_options arguments" + ) if (length(cpp_options) == 0 && !is.null(private$precompile_cpp_options_)) { cpp_options <- private$precompile_cpp_options_ } - cpp_options <- validate_precompile_cpp_options(cpp_options) + cpp_options <- validate_cpp_options(cpp_options) if (length(stanc_options) == 0 && !is.null(private$precompile_stanc_options_)) { stanc_options <- private$precompile_stanc_options_ @@ -794,7 +799,10 @@ compile <- function(quiet = TRUE, private$precompile_stanc_options_ <- NULL private$precompile_include_paths_ <- NULL - # Must be run after private$cmdstan_version_, private$exe_file_, and private$precompiled_cpp_options_ + # Must be run after + # - private$cmdstan_version_ + # - private$exe_file_ + # - private$precompiled_cpp_options_ # are all up to date self$exe_info(update=TRUE) @@ -1321,7 +1329,11 @@ sample <- function(data = NULL, output_dir = output_dir, output_basename = output_basename, sig_figs = sig_figs, - opencl_ids = assert_valid_opencl(opencl_ids, self$exe_info(), self$exe_info_fallback()), + opencl_ids = assert_valid_opencl( + opencl_ids, + self$exe_info(), + self$exe_info_fallback() + ), model_variables = model_variables, save_cmdstan_config = save_cmdstan_config ) @@ -1571,7 +1583,11 @@ optimize <- function(data = NULL, num_procs = 1, show_stderr_messages = show_exceptions, show_stdout_messages = show_messages, - threads_per_proc = assert_valid_threads(threads, self$exe_info(), self$exe_info_fallback()) + threads_per_proc = assert_valid_threads( + threads, + self$exe_info(), + self$exe_info_fallback() + ) ) model_variables <- NULL if (is_variables_method_supported(self)) { @@ -1606,7 +1622,11 @@ optimize <- function(data = NULL, output_dir = output_dir, output_basename = output_basename, sig_figs = sig_figs, - opencl_ids = assert_valid_opencl(opencl_ids, self$exe_info(), self$exe_info_fallback()), + opencl_ids = assert_valid_opencl( + opencl_ids, + self$exe_info(), + self$exe_info_fallback() + ), model_variables = model_variables, save_cmdstan_config = save_cmdstan_config ) @@ -1711,7 +1731,11 @@ laplace <- function(data = NULL, num_procs = 1, show_stderr_messages = show_exceptions, show_stdout_messages = show_messages, - threads_per_proc = assert_valid_threads(threads, self$exe_info(), self$exe_info_fallback()) + threads_per_proc = assert_valid_threads( + threads, + self$exe_info(), + self$exe_info_fallback() + ) ) model_variables <- NULL if (is_variables_method_supported(self)) { @@ -1773,7 +1797,11 @@ laplace <- function(data = NULL, output_dir = output_dir, output_basename = output_basename, sig_figs = sig_figs, - opencl_ids = assert_valid_opencl(opencl_ids, self$exe_info(), self$exe_info_fallback()), + opencl_ids = assert_valid_opencl( + opencl_ids, + self$exe_info(), + self$exe_info_fallback() + ), model_variables = model_variables, save_cmdstan_config = save_cmdstan_config ) @@ -1861,7 +1889,11 @@ variational <- function(data = NULL, num_procs = 1, show_stderr_messages = show_exceptions, show_stdout_messages = show_messages, - threads_per_proc = assert_valid_threads(threads, self$exe_info(), self$exe_info_fallback()) + threads_per_proc = assert_valid_threads( + threads, + self$exe_info(), + self$exe_info_fallback() + ) ) model_variables <- NULL if (is_variables_method_supported(self)) { @@ -1896,7 +1928,11 @@ variational <- function(data = NULL, output_dir = output_dir, output_basename = output_basename, sig_figs = sig_figs, - opencl_ids = assert_valid_opencl(opencl_ids, self$exe_info(), self$exe_info_fallback()), + opencl_ids = assert_valid_opencl( + opencl_ids, + self$exe_info(), + self$exe_info_fallback() + ), model_variables = model_variables, save_cmdstan_config = save_cmdstan_config ) @@ -2006,7 +2042,11 @@ pathfinder <- function(data = NULL, num_procs = 1, show_stderr_messages = show_exceptions, show_stdout_messages = show_messages, - threads_per_proc = assert_valid_threads(num_threads, self$exe_info(), self$exe_info_fallback()) + threads_per_proc = assert_valid_threads( + num_threads, + self$exe_info(), + self$exe_info_fallback() + ) ) model_variables <- NULL if (is_variables_method_supported(self)) { @@ -2046,7 +2086,11 @@ pathfinder <- function(data = NULL, output_dir = output_dir, output_basename = output_basename, sig_figs = sig_figs, - opencl_ids = assert_valid_opencl(opencl_ids, self$exe_info(), self$exe_info_fallback()), + opencl_ids = assert_valid_opencl( + opencl_ids, + self$exe_info(), + self$exe_info_fallback() + ), model_variables = model_variables, num_threads = num_threads, save_cmdstan_config = save_cmdstan_config @@ -2143,7 +2187,12 @@ generate_quantities <- function(fitted_params, procs <- CmdStanGQProcs$new( num_procs = length(fitted_params_files), parallel_procs = checkmate::assert_integerish(parallel_chains, lower = 1, null.ok = TRUE), - threads_per_proc = assert_valid_threads(threads_per_chain, self$exe_info(), self$exe_info_fallback(), multiple_chains = TRUE) + threads_per_proc = assert_valid_threads( + threads_per_chain, + self$exe_info(), + self$exe_info_fallback(), + multiple_chains = TRUE + ) ) model_variables <- NULL if (is_variables_method_supported(self)) { @@ -2164,7 +2213,11 @@ generate_quantities <- function(fitted_params, output_dir = output_dir, output_basename = output_basename, sig_figs = sig_figs, - opencl_ids = assert_valid_opencl(opencl_ids, self$exe_info(), self$exe_info_fallback()), + opencl_ids = assert_valid_opencl( + opencl_ids, + self$exe_info(), + self$exe_info_fallback() + ), model_variables = model_variables ) runset <- CmdStanRun$new(args, procs) @@ -2302,18 +2355,24 @@ CmdStanModel$set("public", name = "expose_functions", value = expose_functions) assert_valid_opencl <- function( opencl_ids, exe_info, - fallback_exe_info = list('stan_version' = '2.0.0', 'stan_opencl' = FALSE) + fallback_exe_info = list("stan_version" = "2.0.0", "stan_opencl" = FALSE) ) { if (is.null(opencl_ids)) return(invisible(opencl_ids)) - - fallback <- length(exe_info) == 0 - if(fallback) exe_info <- fallback_exe_info - # If we're unsure if this info is accurate, we shouldn't stop the user from attempting on that basis - # the user should have been warned about this in initialize(), so no need to re-warn here. - if(fallback) stop <- warning + + fallback <- length(exe_info) == 0 + if (fallback) exe_info <- fallback_exe_info + # If we're unsure if this info is accurate, + # we shouldn't stop the user from attempting on that basis + # the user should have been warned about this in initialize(), + # so no need to re-warn here. + if (fallback) stop <- warning if (exe_info[['stan_version']] < "2.26.0") { - stop("Runtime selection of OpenCL devices is only supported with CmdStan version 2.26 or newer.", call. = FALSE) + stop( + "Runtime selection of OpenCL devices is only supported ", + "with CmdStan version 2.26 or newer.", + call. = FALSE + ) } if (isFALSE(exe_info[["stan_opencl"]])) { @@ -2325,12 +2384,19 @@ assert_valid_opencl <- function( invisible(opencl_ids) } -assert_valid_threads <- function(threads, exe_info, fallback_exe_info, multiple_chains = FALSE) { - fallback <- length(exe_info) == 0 - if(fallback) exe_info <- fallback_exe_info - # If we're unsure if this info is accurate, we shouldn't stop the user from attempting on that basis - # the user should have been warned about this in initialize(), so no need to re-warn here. - if(fallback) stop <- warning +assert_valid_threads <- function( + threads, + exe_info, + fallback_exe_info, + multiple_chains = FALSE +) { + fallback <- length(exe_info) == 0 + if (fallback) exe_info <- fallback_exe_info + # If we're unsure if this info is accurate, + # we shouldn't stop the user from attempting on that basis + # the user should have been warned about this in initialize(), + # so no need to re-warn here. + if (fallback) stop <- warning threads_arg <- if (multiple_chains) "threads_per_chain" else "threads" checkmate::assert_integerish(threads, .var.name = threads_arg, @@ -2353,23 +2419,32 @@ assert_valid_threads <- function(threads, exe_info, fallback_exe_info, multiple_ invisible(threads) } -validate_precompile_cpp_options <- function(cpp_options) { - if(is.null(cpp_options) || length(cpp_options) == 0) return(list()) +validate_cpp_options <- function(cpp_options) { + if (is.null(cpp_options) || length(cpp_options) == 0) return(list()) - if (!is.null(cpp_options[["user_header"]]) && !is.null(cpp_options[['USER_HEADER']])) { - warning('User header specified both via cpp_options[["USER_HEADER"]] and cpp_options[["user_header"]].', call. = FALSE) + if ( + !is.null(cpp_options[["user_header"]]) && + !is.null(cpp_options[["USER_HEADER"]]) + ) { + warning( + "User header specified both via cpp_options[[\"USER_HEADER\"]] ", + "and cpp_options[[\"user_header\"]].", + call. = FALSE + ) } names(cpp_options) <- tolower(names(cpp_options)) flags_set_if_defined <- c( # cmdstan - "stan_threads", "stan_mpi", "stan_opencl", "stan_no_range_checks", "stan_cpp_optims", + "stan_threads", "stan_mpi", "stan_opencl", + "stan_no_range_checks", "stan_cpp_optims", # stan math - "integrated_opencl", "tbb_lib", "tbb_inc", "tbb_interface_new" + "integrated_opencl", "tbb_lib", "tbb_inc", "tbb_interface_new" ) for (flag in flags_set_if_defined) { if (isFALSE(cpp_options[[flag]])) warning( - toupper(flag), " set to ", cpp_options[flag], " Since this is a non-empty value, ", + toupper(flag), " set to ", cpp_options[flag], + " 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(", flag, " = NULL)." ) @@ -2381,25 +2456,28 @@ exe_info_style_cpp_options <- function(cpp_options) { if(is.null(cpp_options)) cpp_options <- list() names(cpp_options) <- tolower(names(cpp_options)) flags_reported_in_exe_info <- c( - "stan_threads", "stan_mpi", "stan_opencl", "stan_no_range_checks", "stan_cpp_optims" + "stan_threads", "stan_mpi", "stan_opencl", + "stan_no_range_checks", "stan_cpp_optims" ) for (flag in flags_reported_in_exe_info) { - cpp_options[[flag]] <- !(is.null(cpp_options[[flag]]) || cpp_options[[flag]] == '') + cpp_options[[flag]] <- !( + is.null(cpp_options[[flag]]) || cpp_options[[flag]] == "" + ) } cpp_options } exe_info_reflects_cpp_options <- function(exe_info, cpp_options) { - if(length(exe_info) == 0) { - warning('Recompiling is recommended due to missing exe_info.') + if (length(exe_info) == 0) { + warning("Recompiling is recommended due to missing exe_info.") return(TRUE) } - if(is.null(cpp_options)) return(TRUE) + if (is.null(cpp_options)) return(TRUE) cpp_options <- exe_info_style_cpp_options(cpp_options)[tolower(names(cpp_options))] overlap <- names(cpp_options)[names(cpp_options) %in% names(exe_info)] - if(length(overlap) == 0) TRUE else all.equal( + if (length(overlap) == 0) TRUE else all.equal( exe_info[overlap], cpp_options[overlap] ) @@ -2518,7 +2596,12 @@ parse_exe_info_string <- function(ret_stdout) { } } - info[["stan_version"]] <- paste0(info[["stan_version_major"]], ".", info[["stan_version_minor"]], ".", info[["stan_version_patch"]]) + info[["stan_version"]] <- paste0( + info[["stan_version_major"]], + ".", + info[["stan_version_minor"]], + ".", info[["stan_version_patch"]] + ) info[["stan_version_major"]] <- NULL info[["stan_version_minor"]] <- NULL info[["stan_version_patch"]] <- NULL @@ -2545,7 +2628,9 @@ run_info_cli <- function(exe_file) { is_variables_method_supported <- function(mod) { - cmdstan_version() >= "2.27.0" && mod$has_stan_file() && file.exists(mod$stan_file()) + cmdstan_version() >= "2.27.0" && + mod$has_stan_file() && + file.exists(mod$stan_file()) } model_compile_info_legacy <- function(exe_file) { @@ -2572,7 +2657,7 @@ model_compile_info_legacy <- function(exe_file) { if (!is.na(as.logical(val))) { val <- as.logical(val) } - if(!is.logical(val) || isTRUE(val)) { + if (!is.logical(val) || isTRUE(val)) { info[[tolower(key_val[1])]] <- val } } @@ -2582,7 +2667,9 @@ model_compile_info_legacy <- function(exe_file) { info } -resolve_exe_path <- function(dir = NULL, private_dir = NULL, self_exe_file = NULL, self_stan_file = NULL) { +resolve_exe_path <- function( + dir = NULL, private_dir = NULL, self_exe_file = NULL, self_stan_file = NULL +) { if (is.null(dir) && !is.null(private_dir)) { dir <- absolute_path(private_dir) } else if (!is.null(dir)) { @@ -2603,7 +2690,12 @@ resolve_exe_path <- function(dir = NULL, private_dir = NULL, self_exe_file = NUL } exe <- cmdstan_ext(strip_ext(exe_base)) if (dir.exists(exe)) { - stop("There is a subfolder matching the model name in the same folder as the model! Please remove or rename the subfolder and try again.", call. = FALSE) + stop( + "There is a subfolder matching the model name ", + "in the same folder as the model! ", + "Please remove or rename the subfolder and try again.", + call. = FALSE + ) } } else { exe <- self_exe_file diff --git a/R/path.R b/R/path.R index 4172c86d..275e5abc 100644 --- a/R/path.R +++ b/R/path.R @@ -234,9 +234,9 @@ unset_cmdstan_path <- function() { } # fake a cmdstan version (only used in tests) -fake_cmdstan_version <- function(version, mod=NULL) { +fake_cmdstan_version <- function(version, mod = NULL) { .cmdstanr$VERSION <- version - if(!is.null(mod)) { + if (!is.null(mod)) { if (!is.null(mod$.__enclos_env__$private$exe_info_)) { mod$.__enclos_env__$private$exe_info_$stan_version <- version } diff --git a/tests/testthat/helper-custom-expectations.R b/tests/testthat/helper-custom-expectations.R index 86244d1e..b0d22a96 100644 --- a/tests/testthat/helper-custom-expectations.R +++ b/tests/testthat/helper-custom-expectations.R @@ -101,10 +101,10 @@ expect_noninteractive_silent <- function(object) { expect_silent(object)) } -expect_equal_ignore_order <- function(object, expected, ...){ +expect_equal_ignore_order <- function(object, expected, ...) { object <- expected[sort(names(object))] expected <- expected[sort(names(expected))] expect_equal(object, expected, ...) } -expect_not_true <- function(...) expect_false(isTRUE(...)) +expect_not_true <- function(...) expect_false(isTRUE(...)) \ No newline at end of file diff --git a/tests/testthat/helper-mock-cli.R b/tests/testthat/helper-mock-cli.R index 0d2a2d49..60a9e52d 100644 --- a/tests/testthat/helper-mock-cli.R +++ b/tests/testthat/helper-mock-cli.R @@ -1,19 +1,22 @@ real_wcr <- wsl_compatible_run -with_mocked_cli <- function(code, compile_ret, info_ret){ +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' + && command == "make" && !is.null(args) - && startsWith(basename(args[1]), 'model-') + && 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, ...) + } else if (!is.null(args) && args[1] == "info") { + info_ret + } else { + real_wcr(command = command, args = args, ...) + } } ) } @@ -31,15 +34,21 @@ with_mocked_cli <- function(code, compile_ret, info_ret){ # fails if mock_compile is called (even once) # # Implementation: -# `with_mocked_cli` emits a message with the contents `mock-compile-was-called` if a compile is triggered +# `with_mocked_cli` +# if a compile is triggered +# emits a message with the contents `mock-compile-was-called` # (defined as wsl_compatible_run being called with make model-*) # `expect_mock_compile` checks for this message: # passes if it detects such a message # fails if it does not # `expect_no_mock_compile` -# fails if a message with exactly this text is detected +# fails if a message with exactly this text is detected # passes if no such message is detected # messages with any other text does not impact `expect_no_mock_compile` -expect_mock_compile <- function(object, ...) expect_message(object, regexp = 'mock-compile-was-called', ...) -expect_no_mock_compile <- function(object, ...) expect_no_message(object, message = 'mock-compile-was-called' , ...) +expect_mock_compile <- function(object, ...) { + expect_message(object, regexp = "mock-compile-was-called", ...) +} +expect_no_mock_compile <- function(object, ...) { + expect_no_message(object, message = "mock-compile-was-called", ...) +} diff --git a/tests/testthat/test-model-compile-user_header.R b/tests/testthat/test-model-compile-user_header.R index b1492f49..1341c9b0 100644 --- a/tests/testthat/test-model-compile-user_header.R +++ b/tests/testthat/test-model-compile-user_header.R @@ -1,14 +1,21 @@ -file_that_exists <- 'placeholder_exists' -file_that_doesnt_exist <- 'placeholder_doesnt_exist' +file_that_exists <- "placeholder_exists" +file_that_doesnt_exist <- "placeholder_doesnt_exist" file.create(file_that_exists) -on.exit(if(file.exists(file_that_exists)) file.remove(file_that_exists), add=TRUE, after=FALSE) +on.exit( + if (file.exists(file_that_exists)) file.remove(file_that_exists), + add = TRUE, + after = FALSE +) make_local_orig <- cmdstan_make_local() -cmdstan_make_local(cpp_options = list("PRECOMPILED_HEADERS"="false")) -on.exit(cmdstan_make_local(cpp_options = make_local_orig, append = FALSE), add = TRUE, after = FALSE) -hpp <- -" +cmdstan_make_local(cpp_options = list("PRECOMPILED_HEADERS" = "false")) +on.exit( + cmdstan_make_local(cpp_options = make_local_orig, append = FALSE), + add = TRUE, + after = FALSE +) +hpp <- " #include #include #include @@ -17,9 +24,10 @@ namespace bernoulli_external_model_namespace { template >* = nullptr> - inline typename boost::math::tools::promote_args::type make_odds(const T0__ & - theta, - std::ostream *pstream__) + inline typename boost::math::tools::promote_args::type make_odds( + const T0__ & theta, + std::ostream *pstream__ + ) { return theta / (1 - theta); } @@ -30,77 +38,112 @@ test_that("cmdstan_model works with user_header with mock", { tmpfile <- tempfile(fileext = ".hpp") cat(hpp, file = tmpfile, sep = "\n") - with_mocked_cli(compile_ret = list(status = 0), info_ret = list(), code = expect_mock_compile( - expect_warning( - expect_no_warning({ - mod <- cmdstan_model( - stan_file = testing_stan_file("bernoulli_external"), - exe_file = file_that_exists, - user_header = tmpfile - ) - }, message = 'Recompiling is recommended'), # this warning should not occur because recompile happens automatically - 'Retrieving exe_file info failed' # this warning should occur - ) - )) - - with_mocked_cli(compile_ret = list(status = 0), info_ret = list(), code = expect_mock_compile({ - mod_2 <- cmdstan_model( - stan_file = testing_stan_file("bernoulli_external"), - exe_file = file_that_doesnt_exist, - cpp_options=list(USER_HEADER=tmpfile), - stanc_options = list("allow-undefined") + with_mocked_cli( + compile_ret = list(status = 0), + info_ret = list(), + code = expect_mock_compile( + expect_warning( + expect_no_warning({ + mod <- cmdstan_model( + stan_file = testing_stan_file("bernoulli_external"), + exe_file = file_that_exists, + user_header = tmpfile + ) + }, message = "Recompiling is recommended"), + # ^ this warning should not occur because recompile happens automatically + "Retrieving exe_file info failed" + # ^ this warning should occur + ) ) - })) + ) + + with_mocked_cli( + compile_ret = list(status = 0), + info_ret = list(), + code = expect_mock_compile({ + mod_2 <- cmdstan_model( + stan_file = testing_stan_file("bernoulli_external"), + exe_file = file_that_doesnt_exist, + cpp_options = list(USER_HEADER = tmpfile), + stanc_options = list("allow-undefined") + ) + }) + ) # Check recompilation upon changing header file.create(file_that_exists) - with_mocked_cli(compile_ret = list(status = 0), info_ret = list(), code = expect_no_mock_compile({ - mod$compile(quiet = TRUE, user_header = tmpfile) - })) + with_mocked_cli( + compile_ret = list(status = 0), + info_ret = list(), + code = expect_no_mock_compile({ + mod$compile(quiet = TRUE, user_header = tmpfile) + }) + ) Sys.setFileTime(tmpfile, Sys.time() + 1) # touch file to trigger recompile - with_mocked_cli(compile_ret = list(status = 0), info_ret = list(), code = expect_mock_compile({ - mod$compile(quiet = TRUE, user_header = tmpfile) - })) + with_mocked_cli( + compile_ret = list(status = 0), + info_ret = list(), + code = expect_mock_compile({ + mod$compile(quiet = TRUE, user_header = tmpfile) + }) + ) # mock does not automatically update file mtime Sys.setFileTime(mod$exe_file(), Sys.time() + 1) # touch file to trigger recompile # Alternative spec of user header - with_mocked_cli(compile_ret = list(status = 0), info_ret = list(), code = expect_no_mock_compile({ - mod$compile( - quiet = TRUE, - cpp_options = list(user_header = tmpfile), - dry_run = TRUE - )})) + with_mocked_cli( + compile_ret = list(status = 0), + info_ret = list(), + code = expect_no_mock_compile({ + mod$compile( + quiet = TRUE, + cpp_options = list(user_header = tmpfile), + dry_run = TRUE + ) + }) + ) # Error/warning messages - with_mocked_cli(compile_ret = list(status = 1), info_ret = list(), code = expect_error( - cmdstan_model( - stan_file = testing_stan_file("bernoulli_external"), - cpp_options = list(USER_HEADER = "non_existent.hpp"), - stanc_options = list("allow-undefined") - ), - "header file '[^']*' does not exist" - )) - - with_mocked_cli(compile_ret = list(status = 1), info_ret = list(), code = expect_warning( - cmdstan_model( - stan_file = testing_stan_file("bernoulli_external"), - cpp_options = list(USER_HEADER = tmpfile, user_header = tmpfile), - dry_run = TRUE - ), - "User header specified both" - )) - with_mocked_cli(compile_ret = list(status = 1), info_ret = list(), code = expect_warning( - cmdstan_model( - stan_file = testing_stan_file("bernoulli_external"), - user_header = tmpfile, - cpp_options = list(USER_HEADER = tmpfile), - dry_run = TRUE - ), - "User header specified both" - )) + with_mocked_cli( + compile_ret = list(status = 1), + info_ret = list(), + code = expect_error( + cmdstan_model( + stan_file = testing_stan_file("bernoulli_external"), + cpp_options = list(USER_HEADER = "non_existent.hpp"), + stanc_options = list("allow-undefined") + ), + "header file '[^']*' does not exist" + ) + ) + + with_mocked_cli( + compile_ret = list(status = 1), + info_ret = list(), + code = expect_warning( + cmdstan_model( + stan_file = testing_stan_file("bernoulli_external"), + cpp_options = list(USER_HEADER = tmpfile, user_header = tmpfile), + dry_run = TRUE + ), + "User header specified both" + ) + ) + with_mocked_cli( + compile_ret = list(status = 1), + info_ret = list(), + code = expect_warning( + cmdstan_model( + stan_file = testing_stan_file("bernoulli_external"), + user_header = tmpfile, + cpp_options = list(USER_HEADER = tmpfile), + dry_run = TRUE + ), + "User header specified both" + ) + ) }) test_that("user_header precedence order is correct", { @@ -112,35 +155,53 @@ test_that("user_header precedence order is correct", { add = TRUE ) - with_mocked_cli(compile_ret = list(status = 1), info_ret = list(), code = expect_warning( - {mod <- cmdstan_model( - stan_file = testing_stan_file("bernoulli_external"), - user_header = tmp_files[[1]], - cpp_options = list(USER_HEADER = tmp_files[[2]], user_header = tmp_files[[3]]), - dry_run = TRUE - )}, - "User header specified both" - )) + with_mocked_cli( + compile_ret = list(status = 1), + info_ret = list(), + code = expect_warning({ + mod <- cmdstan_model( + stan_file = testing_stan_file("bernoulli_external"), + user_header = tmp_files[[1]], + cpp_options = list( + USER_HEADER = tmp_files[[2]], + user_header = tmp_files[[3]] + ), + dry_run = TRUE + ) + }, "User header specified both") + ) expect_equal(mod$precompile_cpp_options()$user_header, tmp_files[[1]]) - with_mocked_cli(compile_ret = list(status = 1), info_ret = list(), code = expect_warning( - {mod <- cmdstan_model( - stan_file = testing_stan_file("bernoulli_external"), - cpp_options = list(USER_HEADER = tmp_files[[2]], user_header = tmp_files[[3]]), - dry_run = TRUE - )}, - "User header specified both" - )) + with_mocked_cli( + compile_ret = list(status = 1), + info_ret = list(), + code = expect_warning({ + mod <- cmdstan_model( + stan_file = testing_stan_file("bernoulli_external"), + cpp_options = list( + USER_HEADER = tmp_files[[2]], + user_header = tmp_files[[3]] + ), + dry_run = TRUE + ) + }, "User header specified both") + ) expect_equal(mod$precompile_cpp_options()$user_header, tmp_files[[2]]) - with_mocked_cli(compile_ret = list(status = 1), info_ret = list(), code = expect_warning( - {mod <- cmdstan_model( - stan_file = testing_stan_file("bernoulli_external"), - cpp_options = list(user_header = tmp_files[[3]], USER_HEADER = tmp_files[[2]] ), - dry_run = TRUE - )}, - "User header specified both" - )) + with_mocked_cli( + compile_ret = list(status = 1), + info_ret = list(), + code = expect_warning({ + mod <- cmdstan_model( + stan_file = testing_stan_file("bernoulli_external"), + cpp_options = list( + user_header = tmp_files[[3]], + USER_HEADER = tmp_files[[2]] + ), + dry_run = TRUE + ) + }, "User header specified both") + ) expect_equal(mod$precompile_cpp_options()$user_header, tmp_files[[3]]) }) \ No newline at end of file diff --git a/tests/testthat/test-model-compile.R b/tests/testthat/test-model-compile.R index c5ce0d21..79cefa1c 100644 --- a/tests/testthat/test-model-compile.R +++ b/tests/testthat/test-model-compile.R @@ -9,7 +9,11 @@ mod <- cmdstan_model(stan_file = stan_program, compile = FALSE) make_local_orig <- cmdstan_make_local() cmdstan_make_local(cpp_options = list("PRECOMPILED_HEADERS"="false")) -on.exit(cmdstan_make_local(cpp_options = make_local_orig, append = FALSE), add = TRUE, after = FALSE) +on.exit( + cmdstan_make_local(cpp_options = make_local_orig, append = FALSE), + add = TRUE, + after = FALSE +) test_that("object initialized correctly", { expect_equal(mod$stan_file(), stan_program) @@ -21,6 +25,7 @@ test_that("object initialized correctly", { fixed = TRUE ) }) + test_that("error if no compile() before model fitting", { expect_error( mod$sample(), @@ -395,9 +400,6 @@ test_that("check_syntax() works with include_paths", { }) - -# Test Failing Due to Side effect ----- - test_that("check_syntax() works with include_paths on compiled model", { stan_program_w_include <- testing_stan_file("bernoulli_include") @@ -408,7 +410,6 @@ test_that("check_syntax() works with include_paths on compiled model", { }) - test_that("check_syntax() works with pedantic=TRUE", { model_code <- " transformed data { diff --git a/tests/testthat/test-model-generate_quantities.R b/tests/testthat/test-model-generate_quantities.R index 9f5ec1fb..9db95e9b 100644 --- a/tests/testthat/test-model-generate_quantities.R +++ b/tests/testthat/test-model-generate_quantities.R @@ -53,7 +53,10 @@ test_that("generate_quantities work for different chains and parallel_chains", { ) expect_call_compilation({ - mod_gq <- cmdstan_model(testing_stan_file("bernoulli_ppc"), cpp_options = list(stan_threads = TRUE)) + mod_gq <- cmdstan_model( + testing_stan_file("bernoulli_ppc"), + cpp_options = list(stan_threads = TRUE) + ) }) expect_gq_output( diff --git a/tests/testthat/test-model-internal.R b/tests/testthat/test-model-internal.R index 24c42fad..cf50954a 100644 --- a/tests/testthat/test-model-internal.R +++ b/tests/testthat/test-model-internal.R @@ -11,7 +11,7 @@ test_that("parse_exe_info_string works", { STAN_CPP_OPTIMS=false "), list( - stan_version = '2.38.0', + stan_version = "2.38.0", stan_threads = FALSE, stan_mpi = FALSE, stan_opencl = TRUE, @@ -20,36 +20,52 @@ test_that("parse_exe_info_string works", { ) ) }) - + test_that("validate_precompile_cpp_options works", { expect_equal_ignore_order( - validate_precompile_cpp_options(list(Stan_Threads = TRUE, STAN_OPENCL = NULL, aBc = FALSE)), + validate_precompile_cpp_options(list( + Stan_Threads = TRUE, + STAN_OPENCL = NULL, + aBc = FALSE + )), list( stan_threads = TRUE, - stan_opencl = NULL, + stan_opencl = NULL, abc = FALSE ) ) - expect_warning(validate_precompile_cpp_options(list(STAN_OPENCL= FALSE))) + expect_warning(validate_precompile_cpp_options(list(STAN_OPENCL = FALSE))) }) -test_that('exe_info cpp_options comparison works', { +test_that("exe_info cpp_options comparison works", { exe_info_all_flags_off <- exe_info_style_cpp_options(list()) - exe_info_all_flags_off[['stan_version']] <- '35.0.0' + exe_info_all_flags_off[["stan_version"]] <- "35.0.0" - expect_true(exe_info_reflects_cpp_options(exe_info_all_flags_off, list())) - expect_true(exe_info_reflects_cpp_options(list(stan_opencl = FALSE), list(stan_opencl = NULL))) - expect_not_true(exe_info_reflects_cpp_options(list(stan_opencl = FALSE), list(stan_opencl = FALSE))) - expect_not_true(exe_info_reflects_cpp_options(list(stan_opencl = FALSE, stan_threads = FALSE), list(stan_opencl = NULL, stan_threads = TRUE))) + expect_true(exe_info_reflects_cpp_options( + exe_info_all_flags_off, + list() + )) + expect_true(exe_info_reflects_cpp_options( + list(stan_opencl = FALSE), + list(stan_opencl = NULL) + )) + expect_not_true(exe_info_reflects_cpp_options( + list(stan_opencl = FALSE), + list(stan_opencl = FALSE) + )) + expect_not_true(exe_info_reflects_cpp_options( + list(stan_opencl = FALSE, stan_threads = FALSE), + list(stan_opencl = NULL, stan_threads = TRUE) + )) expect_not_true(exe_info_reflects_cpp_options( - list(stan_opencl = FALSE, stan_threads = FALSE), - list(stan_opencl = NULL, stan_threads = TRUE, EXTRA_ARG = TRUE) + list(stan_opencl = FALSE, stan_threads = FALSE), + list(stan_opencl = NULL, stan_threads = TRUE, EXTRA_ARG = TRUE) )) # no exe_info -> no recompile based on cpp info expect_warning( expect_true(exe_info_reflects_cpp_options(list(), list())), - 'Recompiling is recommended' + "Recompiling is recommended" ) }) \ No newline at end of file diff --git a/tests/testthat/test-model-recompile-logic.R b/tests/testthat/test-model-recompile-logic.R index 652cccb8..b34aadcd 100644 --- a/tests/testthat/test-model-recompile-logic.R +++ b/tests/testthat/test-model-recompile-logic.R @@ -1,19 +1,31 @@ stan_program <- cmdstan_example_file() -file_that_doesnt_exist <- 'placeholder_doesnt_exist' -file_that_exists <- 'placeholder_exists' +file_that_doesnt_exist <- "placeholder_doesnt_exist" +file_that_exists <- "placeholder_exists" file.create(file_that_exists) -on.exit(if(file.exists(file_that_exists)) file.remove(file_that_exists)) +on.exit(if (file.exists(file_that_exists)) file.remove(file_that_exists)) test_that("warning when no recompile and no info", - with_mocked_cli(compile_ret = list(), info_ret = list(status = 1), code = expect_warning({ - mod <- cmdstan_model(stan_file = stan_program, exe_file = file_that_exists, compile = FALSE) - }, "Recompiling is recommended.")) + with_mocked_cli( + compile_ret = list(), + info_ret = list(status = 1), + code = expect_warning({ + mod <- cmdstan_model( + stan_file = stan_program, + exe_file = file_that_exists, + compile = FALSE + ) + }, "Recompiling is recommended.") + ) ) -test_that("recompiles when force_recompile flag set", - with_mocked_cli(compile_ret = list(status=0), info_ret = list(), code = expect_mock_compile({ - mod <- cmdstan_model(stan_file = stan_program, force_recompile = TRUE) - })) +test_that("recompiles when force_recompile flag set", + with_mocked_cli( + compile_ret = list(status = 0), + info_ret = list(), + code = expect_mock_compile({ + mod <- cmdstan_model(stan_file = stan_program, force_recompile = TRUE) + }) + ) ) test_that("No mismatch results in no recompile.", with_mocked_cli( @@ -37,10 +49,10 @@ test_that("No mismatch results in no recompile.", with_mocked_cli( )) test_that("Mismatch results in recompile.", with_mocked_cli( - compile_ret = list(status=0), + compile_ret = list(status = 0), info_ret = list( - status=0, - stdout= " + status = 0, + stdout = " stan_version_major = 2 stan_version_minor = 35 stan_version_patch = 0 @@ -52,15 +64,20 @@ test_that("Mismatch results in recompile.", with_mocked_cli( " ), code = expect_mock_compile({ - mod <- cmdstan_model(stan_file = stan_program, exe_file = file_that_exists, cpp_options = list(stan_threads = TRUE)) + mod <- cmdstan_model( + stan_file = stan_program, + exe_file = file_that_exists, + cpp_options = list(stan_threads = TRUE) + ) }) )) -test_that("$exe_info(), $precompile_cpp_options() return expected data without recompile", +test_that( + "$exe_info(), $precompile_cpp_options() return expected data without recompile", with_mocked_cli( - compile_ret = list(status=0), + compile_ret = list(status = 0), info_ret = list( - status=0, - stdout= " + status = 0, + stdout = " stan_version_major = 2 stan_version_minor = 38 stan_version_patch = 0 @@ -84,7 +101,7 @@ test_that("$exe_info(), $precompile_cpp_options() return expected data without r expect_equal_ignore_order( mod$exe_info(), list( - stan_version = '2.38.0', + stan_version = "2.38.0", stan_threads = FALSE, stan_mpi = FALSE, stan_opencl = TRUE, @@ -96,7 +113,7 @@ test_that("$exe_info(), $precompile_cpp_options() return expected data without r mod$precompile_cpp_options(), list( stan_threads = TRUE, - stan_opencl = NULL, + stan_opencl = NULL, abc = FALSE ) ) @@ -106,10 +123,10 @@ test_that("$exe_info(), $precompile_cpp_options() return expected data without r test_that("$exe_info_fallback() logic works as expected with cpp_options", with_mocked_cli( - compile_ret = list(status=0), + compile_ret = list(status = 0), info_ret = list( status = 1, - stdout = '' + stdout = "" ), code = { expect_warning( @@ -118,10 +135,15 @@ test_that("$exe_info_fallback() logic works as expected with cpp_options", stan_file = stan_program, exe_file = file_that_exists, compile = FALSE, - cpp_options = list(Stan_Threads = TRUE, stan_Opencl = NULL, aBc = FALSE, dEf = NULL) + cpp_options = list( + Stan_Threads = TRUE, + stan_Opencl = NULL, + aBc = FALSE, + dEf = NULL + ) ) }), - 'Retrieving exe_file info failed' + "Retrieving exe_file info failed" ) # cmdstan_model call same as above # Because we use testthat 3e, cannot nest expect_warning() with itself @@ -131,10 +153,15 @@ test_that("$exe_info_fallback() logic works as expected with cpp_options", stan_file = stan_program, exe_file = file_that_exists, compile = FALSE, - cpp_options = list(Stan_Threads = TRUE, stan_Opencl = NULL, aBc = FALSE, dEf = NULL) + cpp_options = list( + Stan_Threads = TRUE, + stan_Opencl = NULL, + aBc = FALSE, + dEf = NULL + ) ) }), - 'Recompiling is recommended' + "Recompiling is recommended" ) expect_equal( mod$exe_info(), @@ -168,7 +195,7 @@ test_that("$exe_info_fallback() logic works as expected with cpp_options", test_that("$exe_info_fallback() logic works as expected without cpp_options", with_mocked_cli( - compile_ret = list(status=0), + compile_ret = list(status = 0), info_ret = list( status = 1, stdout = "" @@ -180,7 +207,7 @@ test_that("$exe_info_fallback() logic works as expected without cpp_options", exe_file = file_that_exists ) }), - 'Retrieving exe_file info failed' + "Retrieving exe_file info failed" ) # cmdstan_model call same as above # Because we use testthat 3e, cannot nest expect_warning() with itself @@ -217,10 +244,10 @@ test_that("$exe_info_fallback() logic works as expected without cpp_options", test_that("Recompile when cpp args don't match binary", { with_mocked_cli( - compile_ret = list(status=0), + compile_ret = list(status = 0), info_ret = list( - status=0, - stdout= " + status = 0, + stdout = " stan_version_major = 2 stan_version_minor = 38 stan_version_patch = 0 @@ -232,7 +259,11 @@ test_that("Recompile when cpp args don't match binary", { " ), expect_mock_compile({ - mod_gq <- cmdstan_model(testing_stan_file("bernoulli_ppc"), exe_file = file_that_exists, cpp_options = list(stan_threads = TRUE)) + mod_gq <- cmdstan_model( + testing_stan_file("bernoulli_ppc"), + exe_file = file_that_exists, + cpp_options = list(stan_threads = TRUE) + ) }) ) }) \ No newline at end of file diff --git a/tests/testthat/test-opencl.R b/tests/testthat/test-opencl.R index cc87184c..d9d1b4bc 100644 --- a/tests/testthat/test-opencl.R +++ b/tests/testthat/test-opencl.R @@ -33,7 +33,11 @@ test_that("all methods error when opencl_ids is used with non OpenCL model", { test_that("all methods error on invalid opencl_ids", { skip_if_not(Sys.getenv("CMDSTANR_OPENCL_TESTS") %in% c("1", "true")) stan_file <- testing_stan_file("bernoulli") - mod <- cmdstan_model(stan_file = stan_file, force_recompile = TRUE, cpp_options = list(stan_opencl = TRUE)) + mod <- cmdstan_model( + stan_file = stan_file, + force_recompile = TRUE, + cpp_options = list(stan_opencl = TRUE) + ) utils::capture.output( expect_warning( mod$sample(data = testing_data("bernoulli"), opencl_ids = c(1000, 1000), chains = 1), @@ -56,7 +60,11 @@ test_that("all methods error on invalid opencl_ids", { ) ) stan_file_gq <- testing_stan_file("bernoulli_ppc") - mod_gq <- cmdstan_model(stan_file = stan_file_gq, force_recompile = TRUE, cpp_options = list(stan_opencl = TRUE)) + mod_gq <- cmdstan_model( + stan_file = stan_file_gq, + force_recompile = TRUE, + cpp_options = list(stan_opencl = TRUE) + ) utils::capture.output( expect_warning( mod_gq$generate_quantities(fitted_params = fit, data = testing_data("bernoulli"), opencl_ids = c(1000, 1000)), @@ -69,7 +77,11 @@ test_that("all methods error on invalid opencl_ids", { test_that("all methods run with valid opencl_ids", { skip_if_not(Sys.getenv("CMDSTANR_OPENCL_TESTS") %in% c("1", "true")) stan_file <- testing_stan_file("bernoulli") - mod <- cmdstan_model(stan_file = stan_file, force_recompile = TRUE, cpp_options = list(stan_opencl = TRUE)) + mod <- cmdstan_model( + stan_file = stan_file, + force_recompile = TRUE, + cpp_options = list(stan_opencl = TRUE) + ) expect_sample_output( fit <- mod$sample(data = testing_data("bernoulli"), opencl_ids = c(0, 0), chains = 1) ) @@ -79,7 +91,11 @@ test_that("all methods run with valid opencl_ids", { expect_false(is.null(fit$metadata()$platform)) stan_file_gq <- testing_stan_file("bernoulli_ppc") - mod_gq <- cmdstan_model(stan_file = stan_file_gq, force_recompile = TRUE, cpp_options = list(stan_opencl = TRUE)) + mod_gq <- cmdstan_model( + stan_file = stan_file_gq, + force_recompile = TRUE, + cpp_options = list(stan_opencl = TRUE) + ) expect_gq_output( fit <- mod_gq$generate_quantities(fitted_params = fit, data = testing_data("bernoulli"), opencl_ids = c(0, 0)), ) @@ -121,8 +137,16 @@ test_that("error for runtime selection of OpenCL devices if version less than 2. force_recompile = TRUE) fake_cmdstan_version("2.25.0", mod) expect_error( - mod$sample(data = testing_data("bernoulli"), chains = 1, refresh = 0, opencl_ids = c(0,0)), - "Runtime selection of OpenCL devices is only supported with CmdStan version 2.26 or newer", + mod$sample( + data = testing_data("bernoulli"), + chains = 1, + refresh = 0, + opencl_ids = c(0, 0) + ), + paste0( + "Runtime selection of OpenCL devices ", + "is only supported with CmdStan version 2.26 or newer" + ), fixed = TRUE ) reset_cmdstan_version() @@ -134,6 +158,10 @@ test_that("model from exe_file retains open_cl option", { mod <- cmdstan_model(stan_file = stan_file, cpp_options = list(stan_opencl = TRUE)) mod_from_exe <- cmdstan_model(exe_file = mod$exe_file()) expect_sample_output( - fit <- mod_from_exe$sample(data = testing_data("bernoulli"), opencl_ids = c(0, 0), chains = 1) + fit <- mod_from_exe$sample( + data = testing_data("bernoulli"), + opencl_ids = c(0, 0), + chains = 1 + ) ) }) \ No newline at end of file