diff --git a/.gitignore b/.gitignore index 965adb6e8..2ca3ad28a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,6 @@ revdep inst/doc renv/cellar +/*.Rprof /*.tar.gz /*.backup diff --git a/DESCRIPTION b/DESCRIPTION index b1b0f9f36..aea725876 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -18,8 +18,8 @@ URL: https://rstudio.github.io/renv/, https://github.com/rstudio/renv BugReports: https://github.com/rstudio/renv/issues Imports: utils Suggests: BiocManager, cli, covr, cpp11, devtools, gitcreds, jsonlite, jsonvalidate, knitr, - miniUI, packrat, pak, R6, remotes, reticulate, rmarkdown, rstudioapi, shiny, testthat, - uuid, waldo, yaml, webfakes + miniUI, modules, packrat, pak, R6, remotes, reticulate, rmarkdown, rstudioapi, shiny, + testthat, uuid, waldo, yaml, webfakes Encoding: UTF-8 RoxygenNote: 7.3.2 Roxygen: list(markdown = TRUE) diff --git a/NEWS.md b/NEWS.md index 488ae758a..308f1c1a7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,8 @@ # renv (development version) +* Fixed a performance regression in `renv::dependencies()`. (#1999) + * Fixed an issue where `renv` tests could fail if the `parallel` package was loaded during test execution. diff --git a/R/aaa.R b/R/aaa.R index 28d9814ce..d2eb09848 100644 --- a/R/aaa.R +++ b/R/aaa.R @@ -1,6 +1,7 @@ # global variables the <- new.env(parent = emptyenv()) +the$paths <- new.env(parent = emptyenv()) # detect if we're running on CI ci <- function() { diff --git a/R/call.R b/R/call.R index c3f89174e..5475c830f 100644 --- a/R/call.R +++ b/R/call.R @@ -1,49 +1,35 @@ # given a call of the form e.g. 'pkg::foo()' or 'foo()', # check that method 'foo()' is truly being called and -# strip off the 'pkg::' part for easier parsing +# strip off the 'pkg::' part for easier parsing. +# +# this gets called very often when parsing dependencies, +# so optimizations are welcome here renv_call_expect <- function(node, package, methods) { - if (!is.call(node)) - return(NULL) - + result <- NULL + # check for call of the form 'pkg::foo(a, b, c)' - colon <- renv_call_matches( - call = node[[1L]], - name = c("::", ":::"), - nargs = 2L - ) - - if (colon) { - - # validate the package name - lhs <- node[[1L]][[2L]] - if (as.character(lhs) != package) - return(NULL) - - # extract the inner call - rhs <- node[[1L]][[3L]] - node[[1L]] <- rhs - } - - # check for method match - match <- - is.name(node[[1L]]) && - as.character(node[[1L]]) %in% methods - - if (!match) - return(NULL) - - node + if (is.call(callnode <- node[[1L]])) + if (is.symbol(sym <- callnode[[1L]])) + if (sym == renv_syms_ns_get || sym == renv_syms_ns_get_int) + if (callnode[[2L]] == substitute(package)) + node[[1L]] <- callnode[[3L]] + + # check for any method match + if (is.symbol(sym <- node[[1L]])) + if (any(sym == methods)) + result <- node + + result } -renv_call_normalize <- function(node, stack) { +renv_call_normalize <- function(node) { # check for magrittr pipe -- if this part of the expression is # being piped into, then we need to munge the call - ispipe <- renv_call_matches(node, name = c("%>%", "%T>%", "%<>%")) - + ispipe <- renv_call_matches(node, names = c("%>%", "%T>%", "%<>%")) if (!ispipe) return(node) @@ -76,24 +62,15 @@ renv_call_normalize <- function(node, stack) { } -renv_call_matches <- function(call, name = NULL, nargs = NULL) { - - if (!is.call(call)) - return(FALSE) - - if (!is.null(name)) { - - if (!is.name(call[[1]])) - return(FALSE) - - if (!as.character(call[[1]]) %in% name) - return(FALSE) - - } - - if (!is.null(nargs) && length(call) != nargs + 1L) - return(FALSE) - - TRUE +renv_call_matches <- function(call, names, nargs = NULL) { + ok <- FALSE + + if (is.call(call) && + is.symbol(sym <- call[[1L]]) && + any(names == as.character(sym))) + ok <- is.null(nargs) || length(call) == nargs + 1L + + ok + } diff --git a/R/cli.R b/R/cli.R index ac2a85033..34800340d 100644 --- a/R/cli.R +++ b/R/cli.R @@ -45,7 +45,7 @@ renv_cli_exec_impl <- function(clargs) { return(renv_cli_unknown(method, exports)) # begin building call - args <- list(call("::", as.name("renv"), as.name(method))) + args <- list(call("::", as.symbol("renv"), as.symbol(method))) for (clarg in clargs[-1L]) { diff --git a/R/config.R b/R/config.R index ab378a51b..c4682d055 100644 --- a/R/config.R +++ b/R/config.R @@ -102,7 +102,7 @@ renv_config_get <- function(name, return(renv_config_validate(name, optval, type, default, args)) # check for environment variable - envname <- gsub(".", "_", toupper(name), fixed = TRUE) + envname <- chartr(".", "_", toupper(name)) envkey <- paste("RENV", toupper(scope), envname, sep = "_") envval <- Sys.getenv(envkey, unset = NA) if (!is.na(envval) && nzchar(envval)) { diff --git a/R/dependencies.R b/R/dependencies.R index a5c01a66c..d8fb8ab6a 100644 --- a/R/dependencies.R +++ b/R/dependencies.R @@ -231,6 +231,14 @@ renv_dependencies_impl <- function( # resolve errors errors <- match.arg(errors) + + # the path to the user .Rprofile is used when discovering dependencies, + # so resolve that eagerly now + renv_scope_binding( + envir = the$paths, + symbol = "r_profile_user", + replacement = Sys.getenv("R_PROFILE_USER", unset = path.expand("~/.Rprofile")) + ) before <- Sys.time() renv_dependencies_scope(root = root) @@ -286,11 +294,6 @@ renv_dependencies_root_impl <- function(path) { renv_dependencies_callback <- function(path) { - # user .Rprofile - if (renv_path_same(path, Sys.getenv("R_PROFILE_USER", unset = "~/.Rprofile"))) { - return(function(path) renv_dependencies_discover_r(path, dev = TRUE)) - } - cbname <- list( ".Rprofile" = function(path) renv_dependencies_discover_r(path), "DESCRIPTION" = function(path) renv_dependencies_discover_description(path), @@ -355,9 +358,9 @@ renv_dependencies_find <- function(path = getwd(), root = getwd()) { extra <- renv_dependencies_find_extra(root) if (config$user.profile()) { - rprofile_path <- Sys.getenv("R_PROFILE_USER", unset = "~/.Rprofile") - if (file.exists(rprofile_path)) { - extra <- c(extra, rprofile_path) + profile <- the$paths$r_profile_user + if (file.exists(profile)) { + extra <- c(extra, profile) } } @@ -493,13 +496,13 @@ renv_dependencies_discover_impl <- function(path) { return(NULL) } - tryCatch( - filebacked("dependencies", path, callback), - error = function(cnd) { - warning(cnd) - NULL - } - ) + status <- catch(filebacked("dependencies", path, callback)) + if (inherits(status, "error")) { + signalCondition(warnify(status)) + NULL + } + + status } @@ -513,7 +516,7 @@ renv_dependencies_discover_preflight <- function(paths, errors) { lines <- c( "A large number of files (%i in total) have been discovered.", - "It may take renv a long time to crawl these files for dependencies.", + "It may take renv a long time to scan these files for dependencies.", "Consider using .renvignore to ignore irrelevant files.", "See `?renv::dependencies` for more information.", "Set `options(renv.config.dependencies.limit = Inf)` to disable this warning.", @@ -755,12 +758,12 @@ renv_dependencies_discover_rmd_yaml_header <- function(path, mode) { pattern <- renv_regexps_package_name() # check recursively for package usages of the form 'package::method' - recurse(yaml, function(node, stack) { + recurse(yaml, function(node) { # look for keys of the form 'package::method' values <- c(names(node), if (pstring(node)) node) for (value in values) { call <- tryCatch(parse(text = value)[[1]], error = function(err) NULL) - if (renv_call_matches(call, name = c("::", ":::"), nargs = 2L)) { + if (renv_call_matches(call, names = c("::", ":::"), nargs = 2L)) { deps$push(as.character(call[[2L]])) } } @@ -1026,11 +1029,11 @@ renv_dependencies_discover_rproj <- function(path) { } -renv_dependencies_discover_r <- function(path = NULL, - text = NULL, - expr = NULL, +renv_dependencies_discover_r <- function(path = NULL, + text = NULL, + expr = NULL, envir = NULL, - dev = FALSE) + dev = NULL) { expr <- case( is.function(expr) ~ body(expr), @@ -1043,7 +1046,10 @@ renv_dependencies_discover_r <- function(path = NULL, if (inherits(expr, "error")) return(renv_dependencies_error(path, error = expr)) - + + # resolve dev + dev <- dev %||% path == the$paths$r_profile_user + # update current path state <- renv_dependencies_state() if (!is.null(state)) @@ -1069,26 +1075,27 @@ renv_dependencies_discover_r <- function(path = NULL, ) envir <- envir %||% new.env(parent = emptyenv()) - recurse(expr, function(node, stack) { - + renv_dependencies_recurse(expr, function(node) { + # normalize calls (handle magrittr pipes) - if (is.call(node)) - node <- renv_call_normalize(node, stack) - - # invoke methods on call objects + node <- renv_call_normalize(node) for (method in methods) - method(node, stack, envir) - - # return node - node - + method(node, envir) + + # update node in parent environment -- this is a hack + # that allows our recurse implementation to remain + # performant without needing to care about the return + # values of these functions in general + assign("object", node, envir = parent.frame()) + invisible(node) + }) packages <- ls(envir = envir, all.names = TRUE) renv_dependencies_list(path, packages, dev = dev) } -renv_dependencies_discover_r_methods <- function(node, stack, envir) { +renv_dependencies_discover_r_methods <- function(node, envir) { node <- renv_call_expect(node, "methods", c("setClass", "setGeneric")) if (is.null(node)) @@ -1099,7 +1106,7 @@ renv_dependencies_discover_r_methods <- function(node, stack, envir) { } -renv_dependencies_discover_r_xfun <- function(node, stack, envir) { +renv_dependencies_discover_r_xfun <- function(node, envir) { node <- renv_call_expect(node, "xfun", c("pkg_attach", "pkg_attach2")) if (is.null(node)) @@ -1113,7 +1120,7 @@ renv_dependencies_discover_r_xfun <- function(node, stack, envir) { # extract character vectors from `...` strings <- stack() - recurse(matched[["..."]], function(node, stack) { + recurse(matched[["..."]], function(node) { if (is.character(node)) strings$push(node) }) @@ -1129,7 +1136,7 @@ renv_dependencies_discover_r_xfun <- function(node, stack, envir) { TRUE } -renv_dependencies_discover_r_library_require <- function(node, stack, envir) { +renv_dependencies_discover_r_library_require <- function(node, envir) { node <- renv_call_expect(node, "base", c("library", "require")) if (is.null(node)) @@ -1160,7 +1167,7 @@ renv_dependencies_discover_r_library_require <- function(node, stack, envir) { } -renv_dependencies_discover_r_require_namespace <- function(node, stack, envir) { +renv_dependencies_discover_r_require_namespace <- function(node, envir) { node <- renv_call_expect(node, "base", c("requireNamespace", "loadNamespace")) if (is.null(node)) @@ -1182,9 +1189,9 @@ renv_dependencies_discover_r_require_namespace <- function(node, stack, envir) { } -renv_dependencies_discover_r_colon <- function(node, stack, envir) { +renv_dependencies_discover_r_colon <- function(node, envir) { - ok <- renv_call_matches(node, name = c("::", ":::"), nargs = 2L) + ok <- renv_call_matches(node, names = c("::", ":::"), nargs = 2L) if (!ok) return(FALSE) @@ -1200,7 +1207,7 @@ renv_dependencies_discover_r_colon <- function(node, stack, envir) { } -renv_dependencies_discover_r_pacman <- function(node, stack, envir) { +renv_dependencies_discover_r_pacman <- function(node, envir) { node <- renv_call_expect(node, "pacman", "p_load") if (is.null(node) || length(node) < 2) @@ -1216,7 +1223,7 @@ renv_dependencies_discover_r_pacman <- function(node, stack, envir) { char <- node[["char"]] # detect vector of packages passed as vector - if (renv_call_matches(char, name = "c")) + if (renv_call_matches(char, "c")) parts <- c(parts, as.list(char[-1L])) # detect plain old package name @@ -1252,61 +1259,54 @@ renv_dependencies_discover_r_pacman <- function(node, stack, envir) { } -renv_dependencies_discover_r_modules <- function(node, stack, envir) { +renv_dependencies_discover_r_modules <- function(node, envir) { - if (!is.call(node)) - return(FALSE) + # check for an explicit call to 'modules::import()' + if (identical(node[[1L]], quote(modules::import))) { + renv_dependencies_discover_r_modules_impl(node, envir) + } + + # check for 'import' usages with a module block + node <- renv_call_expect(node, "modules", "module") + if (identical(node[[1L]], quote(module)) && + is.call(node[[2L]]) && + identical(node[[2L]][[1L]], as.symbol("{"))) + { + renv_dependencies_recurse(node[[2L]], function(node) { + renv_dependencies_discover_r_modules_impl(node, envir) + }) + } + +} - # check for call of the form 'pkg::foo(a, b, c)' - colon <- renv_call_matches(node[[1L]], name = c("::", ":::"), nargs = 2L) +renv_dependencies_discover_r_modules_impl <- function(node, envir) { + node <- renv_call_expect(node, "modules", c("import")) if (is.null(node)) return(FALSE) - - ok <- FALSE - if (colon) { - # include if fully qualified call to modules::import - ok <- TRUE - } else { - # otherwise only consider calls within a 'module' block - # (to reduce confusion with reticulate::import) - for (parent in stack) { - parent <- renv_call_expect(parent, "modules", c("amodule", "module")) - if (!is.null(parent)) { - ok <- TRUE - break - } - } - } - - if (!ok) - return(FALSE) - + # attempt to match the call prototype <- function(from, ..., attach = TRUE, where = parent.frame()) {} matched <- catch(match.call(prototype, node, expand.dots = FALSE)) if (inherits(matched, "error")) return(FALSE) - + # extract character vector or symbol from `from` package <- matched[["from"]] if (empty(package)) return(FALSE) - + # package could be symbols or character so call as.character # to be safe then mark packages as known envir[[as.character(package)]] <- TRUE - TRUE + } -renv_dependencies_discover_r_import <- function(node, stack, envir) { - - if (!is.call(node)) - return(FALSE) +renv_dependencies_discover_r_import <- function(node, envir) { # require that usages are colon-prefixed - colon <- renv_call_matches(node[[1L]], name = c("::", ":::"), nargs = 2L) + colon <- renv_call_matches(node[[1L]], names = c("::", ":::"), nargs = 2L) if (!colon) return(FALSE) @@ -1348,23 +1348,23 @@ renv_dependencies_discover_r_import <- function(node, stack, envir) { } -renv_dependencies_discover_r_box <- function(node, stack, envir) { +renv_dependencies_discover_r_box <- function(node, envir) { node <- renv_call_expect(node, "box", "use") if (is.null(node)) return(FALSE) for (i in seq.int(2L, length.out = length(node) - 1L)) - renv_dependencies_discover_r_box_impl(node[[i]], stack, envir) + renv_dependencies_discover_r_box_impl(node[[i]], envir) TRUE } -renv_dependencies_discover_r_box_impl <- function(node, stack, envir) { +renv_dependencies_discover_r_box_impl <- function(node, envir) { # if the call uses /, it's a path, not a package - while (renv_call_matches(node, name = "/")) + if (renv_call_matches(node, "/")) return(FALSE) # if the node is just a symbol, then it's the name of a package @@ -1372,7 +1372,7 @@ renv_dependencies_discover_r_box_impl <- function(node, stack, envir) { name <- if (is.symbol(node) && !identical(node, quote(expr = ))) { as.character(node) } else if ( - renv_call_matches(node, name = "[") && + renv_call_matches(node, "[") && length(node) > 1L && is.symbol(node[[2L]])) { as.character(node[[2L]]) @@ -1387,7 +1387,7 @@ renv_dependencies_discover_r_box_impl <- function(node, stack, envir) { } -renv_dependencies_discover_r_targets <- function(node, stack, envir) { +renv_dependencies_discover_r_targets <- function(node, envir) { node <- renv_call_expect(node, "targets", "tar_option_set") if (is.null(node)) @@ -1413,7 +1413,7 @@ renv_dependencies_discover_r_targets <- function(node, stack, envir) { } -renv_dependencies_discover_r_glue <- function(node, stack, envir) { +renv_dependencies_discover_r_glue <- function(node, envir) { node <- renv_call_expect(node, "glue", "glue") if (is.null(node)) @@ -1432,7 +1432,7 @@ renv_dependencies_discover_r_glue <- function(node, stack, envir) { } -renv_dependencies_discover_r_ggplot2 <- function(node, stack, envir) { +renv_dependencies_discover_r_ggplot2 <- function(node, envir) { node <- renv_call_expect(node, "ggplot2", "ggsave") if (is.null(node)) @@ -1456,10 +1456,17 @@ renv_dependencies_discover_r_ggplot2 <- function(node, stack, envir) { } -renv_dependencies_discover_r_testthat <- function(node, stack, envir) { +renv_dependencies_discover_r_testthat <- function(node, envir) { # check for construction of JunitReporter - if (identical(node, call("$", as.symbol("JunitReporter"), as.symbol("new")))) { + if (identical(node, quote(JunitReporter$new))) { + envir[["xml2"]] <- TRUE + return(TRUE) + } + + # check for an R6 class inheriting from a JunitReporter + class <- renv_call_expect(node, "R6", "R6Class") + if (!is.null(class) && identical(class$inherit, quote(JunitReporter))) { envir[["xml2"]] <- TRUE return(TRUE) } @@ -1468,12 +1475,12 @@ renv_dependencies_discover_r_testthat <- function(node, stack, envir) { node <- renv_call_expect(node, "testthat", c("test_package", "test_dir", "test_file")) if (is.null(node)) return(FALSE) - + candidates <- list( "Junit", "junit", - call("::", "testthat", "JunitReporter"), - as.symbol("JunitReporter") + quote(JunitReporter), + quote(testthat::JunitReporter) ) reporter <- node$reporter @@ -1490,7 +1497,7 @@ renv_dependencies_discover_r_testthat <- function(node, stack, envir) { } -renv_dependencies_discover_r_knitr <- function(node, stack, envir) { +renv_dependencies_discover_r_knitr <- function(node, envir) { matched <- is.call(node) && ( identical(node[[1L]], quote(knitr::opts_chunk$set)) || @@ -1634,7 +1641,7 @@ renv_dependencies_discover_r_glue_impl <- function(string, node, envir) { } -renv_dependencies_discover_r_parsnip <- function(node, stack, envir) { +renv_dependencies_discover_r_parsnip <- function(node, envir) { node <- renv_call_expect(node, "parsnip", "set_engine") if (is.null(node)) @@ -1677,7 +1684,7 @@ renv_dependencies_discover_r_parsnip <- function(node, stack, envir) { } -renv_dependencies_discover_r_database <- function(node, stack, envir) { +renv_dependencies_discover_r_database <- function(node, envir) { found <- FALSE @@ -1711,17 +1718,12 @@ renv_dependencies_discover_r_database <- function(node, stack, envir) { } renv_dependencies_database <- function() { - dynamic( - key = list(), - value = renv_dependencies_database_impl() - ) -} - -renv_dependencies_database_impl <- function() { - db <- getOption("renv.dependencies.database", default = list()) - db$ggplot2$geom_hex <- "hexbin" - db$testthat$JunitReporter <- "xml2" - db + the$dependencies_database <- the$dependencies_database %||% { + db <- getOption("renv.dependencies.database", default = list()) + db$ggplot2$geom_hex <- "hexbin" + db$testthat$JunitReporter <- "xml2" + db + } } renv_dependencies_list <- function(source, @@ -1886,3 +1888,23 @@ renv_dependencies_eval <- function(expr) { eval(expr, envir = envir) } + +renv_dependencies_recurse <- function(object, callback, ...) { + + if (is.call(object)) + callback(object, ...) + + if (is.recursive(object)) + for (i in seq_along(object)) + if (is.call(object[[i]])) + renv_dependencies_recurse_impl(object[[i]], callback, ...) + +} + +renv_dependencies_recurse_impl <- function(object, callback, ...) { + callback(object, ...) + if (is.recursive(object)) + for (i in seq_along(object)) + if (is.call(object[[i]])) + Recall(object[[i]], callback, ...) +} diff --git a/R/dynamic.R b/R/dynamic.R index 2f83480c8..792b472fb 100644 --- a/R/dynamic.R +++ b/R/dynamic.R @@ -23,7 +23,7 @@ dynamic <- function(key, value, envir = NULL, force = FALSE) { # get a unique id for the scope where this function was invoked caller <- sys.call(sys.parent())[[1L]] - if (renv_call_matches(caller, name = ":::")) + if (renv_call_matches(caller, ":::")) caller <- caller[[3L]] # handle cases like FUN diff --git a/R/errors.R b/R/errors.R index d4e87bec7..83c113236 100644 --- a/R/errors.R +++ b/R/errors.R @@ -49,7 +49,7 @@ renv_error_simplify_function <- function(object) { renv_error_simplify_recursive <- function(object) { - longcall <- renv_call_matches(object, name = "{") && length(object) >= 8 + longcall <- renv_call_matches(object, "{") && length(object) >= 8 if (longcall) return(quote(...)) diff --git a/R/load.R b/R/load.R index 8be93e1fd..223e83bce 100644 --- a/R/load.R +++ b/R/load.R @@ -936,7 +936,7 @@ renv_load_base <- function(call, envir) { return(NULL) # if the call was namespace-qualified, assume we should handle it - if (renv_call_matches(call[[1L]], c("::", ":::"))) + if (renv_call_matches(call[[1L]], names = c("::", ":::"))) return(NULL) # if any of the formals normally associated with base::load diff --git a/R/new.R b/R/new.R index 1e185bab0..a360e639b 100644 --- a/R/new.R +++ b/R/new.R @@ -6,8 +6,7 @@ new <- function(expr) { for (expr in as.list(substitute(expr))[-1L]) { - assigning <- renv_call_matches(expr, name = c("=", "<-")) - + assigning <- renv_call_matches(expr, names = c("=", "<-")) if (!assigning) return(eval(expr, envir = public)) diff --git a/R/recurse.R b/R/recurse.R index a384d6e74..3b325ad06 100644 --- a/R/recurse.R +++ b/R/recurse.R @@ -1,27 +1,7 @@ recurse <- function(object, callback, ...) { - renv_recurse_impl(list(), object, callback, ...) -} - -renv_recurse_impl <- function(stack, object, callback, ...) { - - # ignore missing values - if (missing(object) || identical(object, quote(expr = ))) - return(FALSE) - - # push node on to stack - stack[[length(stack) + 1]] <- object - - # invoke callback - result <- callback(object, stack, ...) - if (is.call(result)) - object <- result - else if (identical(result, FALSE)) - return(FALSE) - - # recurse + callback(object, ...) if (is.recursive(object)) for (i in seq_along(object)) - renv_recurse_impl(stack, object[[i]], callback, ...) - + recurse(object[[i]], callback, ...) } diff --git a/R/syms.R b/R/syms.R new file mode 100644 index 000000000..a10ca49cf --- /dev/null +++ b/R/syms.R @@ -0,0 +1,3 @@ + +renv_syms_ns_get <- as.symbol("::") +renv_syms_ns_get_int <- as.symbol(":::") diff --git a/tests/testthat/_snaps/dependencies.md b/tests/testthat/_snaps/dependencies.md index 56b5797ab..0dabddd9d 100644 --- a/tests/testthat/_snaps/dependencies.md +++ b/tests/testthat/_snaps/dependencies.md @@ -4,7 +4,7 @@ . <- dependencies() Output A large number of files (7 in total) have been discovered. - It may take renv a long time to crawl these files for dependencies. + It may take renv a long time to scan these files for dependencies. Consider using .renvignore to ignore irrelevant files. See `?renv::dependencies` for more information. Set `options(renv.config.dependencies.limit = Inf)` to disable this warning. @@ -17,7 +17,7 @@ . <- dependencies() Output A large number of files (11 in total) have been discovered. - It may take renv a long time to crawl these files for dependencies. + It may take renv a long time to scan these files for dependencies. Consider using .renvignore to ignore irrelevant files. See `?renv::dependencies` for more information. Set `options(renv.config.dependencies.limit = Inf)` to disable this warning. diff --git a/tests/testthat/test-bootstrap.R b/tests/testthat/test-bootstrap.R index d1d8eae84..6569e3846 100644 --- a/tests/testthat/test-bootstrap.R +++ b/tests/testthat/test-bootstrap.R @@ -58,7 +58,7 @@ test_that("bootstrap functions don't depend on non-bootstrap APIs", { # iterate over those functions and look for the called functions calls <- stack(mode = "character") - recurse(bodies, function(node, stack) { + recurse(bodies, function(node) { if (is.call(node) && is.symbol(node[[1L]])) calls$push(as.character(node[[1L]])) }) diff --git a/tests/testthat/test-call.R b/tests/testthat/test-call.R index a0d69665e..7a4807066 100644 --- a/tests/testthat/test-call.R +++ b/tests/testthat/test-call.R @@ -1,16 +1,12 @@ test_that("renv_call_matches() works as expected", { + call <- quote(foo(1, 2)) - - expect_true(renv_call_matches(call)) - expect_true(renv_call_matches(call, name = "foo")) - expect_true(renv_call_matches(call, name = c("foo", "bar"))) - expect_true(renv_call_matches(call, nargs = 2L)) - + expect_true(renv_call_matches(call, "foo")) + expect_true(renv_call_matches(call, "foo", nargs = 2L)) expect_false(renv_call_matches(call, "bar")) - expect_false(renv_call_matches(call, nargs = 1L)) + expect_false(renv_call_matches(call, "bar", nargs = 1L)) + expect_true(renv_call_matches(call, names = c("foo", "bar"))) + expect_true(renv_call_matches(call, names = c("foo", "bar"), nargs = 2L)) - call <- quote(foo()(1, 2)) - expect_true(renv_call_matches(call)) - expect_false(renv_call_matches(call, "foo")) }) diff --git a/tests/testthat/test-dependencies.R b/tests/testthat/test-dependencies.R index 78f91a708..28df2d7fe 100644 --- a/tests/testthat/test-dependencies.R +++ b/tests/testthat/test-dependencies.R @@ -427,9 +427,9 @@ test_that("dependencies ignore pseudo-code in YAML metadata", { }) test_that("~/.Rprofile included in dev dependencies when config$user.profile()", { - path <- renv_scope_tempfile("renv-profile") + path <- renv_scope_tempfile("renv-profile", fileext = ".R") writeLines("library(utils)", path) - renv_scope_envvars(R_PROFILE_USER = path) + renv_scope_envvars(R_PROFILE_USER = normalizePath(path, winslash = "/")) renv_scope_options(renv.config.user.profile = TRUE) renv_tests_scope() diff --git a/tests/testthat/test-json.R b/tests/testthat/test-json.R index 59ba2536f..833b18b58 100644 --- a/tests/testthat/test-json.R +++ b/tests/testthat/test-json.R @@ -153,7 +153,7 @@ test_that("json-read.R can function standalone", { # for each function, check that it only uses functions from base ok <- list() for (val in vals) { - recurse(body(val), function(node, stack) { + recurse(body(val), function(node) { if (is.call(node) && is.symbol(node[[1L]])) { lhs <- as.character(node[[1L]]) ok[[lhs]] <<- exists(lhs, envir = envir) diff --git a/tests/testthat/test-recurse.R b/tests/testthat/test-recurse.R new file mode 100644 index 000000000..fa28b1a14 --- /dev/null +++ b/tests/testthat/test-recurse.R @@ -0,0 +1,5 @@ + +test_that("recurse() can handle missing objects", { + data <- substitute(list(a = A), list(A = quote(expr = ))) + expect_no_error(recurse(data, function(node) print(node))) +})