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
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# testthat (development version)

* `local_mock()` and `with_mock()` have been deprecated because they are no longer permitted in R 4.5.
* `snapshot_review()` now passes `...` on to `shiny::runApp()` (#1928).
* `expect_named()` now gives more informative errors (#2091).
* `expect_*()` functions consistently and rigorously check their inputs (#1754).
Expand Down
134 changes: 6 additions & 128 deletions R/mock.R
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
#' Mock functions in a package.
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#' `r lifecycle::badge("defunct")`
#'
#' `with_mock()` and `local_mock()` are deprecated in favour of
#' [with_mocked_bindings()] and [local_mocked_bindings()].
#'
#' These functions worked by using some C code to temporarily modify the mocked
#' function _in place_. This was an abuse of R's internals and it is no longer
#' permitted.
#' `with_mock()` and `local_mock()` are now defunct, and can be replaced by
#' [with_mocked_bindings()] and [local_mocked_bindings()]. These functions only
#' worked by abusing of R's internals.
#'
#' @param ... named parameters redefine mocked functions, unnamed parameters
#' will be evaluated after mocking the functions
Expand All @@ -18,132 +15,13 @@
#' @param .local_envir Environment in which to add exit handler.
#' For expert use only.
#' @keywords internal
#' @return The result of the last unnamed parameter
#' @export
with_mock <- function(..., .env = topenv()) {
lifecycle::deprecate_warn("3.3.0", "with_mock()", "with_mocked_bindings()")

dots <- eval(substitute(alist(...)))
mock_qual_names <- names(dots)

if (all(mock_qual_names == "")) {
cli::cli_warn(
c(
"Not mocking anything.",
"i" = "Please use named parameters to specify the functions you want to mock."
)
)
code_pos <- rep(TRUE, length(dots))
} else {
code_pos <- (mock_qual_names == "")
}
code <- dots[code_pos]

mock_funs <- lapply(dots[!code_pos], eval, parent.frame())
mocks <- extract_mocks(mock_funs, .env = .env)

withr::defer(lapply(mocks, reset_mock))
lapply(mocks, set_mock)

# Evaluate the code
if (length(code) > 0) {
for (expression in code[-length(code)]) {
eval(expression, parent.frame())
}
# Isolate last item for visibility
eval(code[[length(code)]], parent.frame())
}
lifecycle::deprecate_stop("3.2.0", "with_mock()", "with_mocked_bindings()")
}

#' @export
#' @rdname with_mock
local_mock <- function(..., .env = topenv(), .local_envir = parent.frame()) {
lifecycle::deprecate_warn("3.3.0", "local_mock()", "local_mocked_bindings()")

mocks <- extract_mocks(list(...), .env = .env)
on_exit <- bquote(
withr::defer(lapply(.(mocks), .(reset_mock))),
)

lapply(mocks, set_mock)
eval_bare(on_exit, .local_envir)
invisible()
}

pkg_rx <- ".*[^:]"
colons_rx <- "::(?:[:]?)"
name_rx <- ".*"
pkg_and_name_rx <- sprintf("^(?:(%s)%s)?(%s)$", pkg_rx, colons_rx, name_rx)

extract_mocks <- function(funs, .env) {
if (is.environment(.env)) {
.env <- environmentName(.env)
}
mock_qual_names <- names(funs)

lapply(
stats::setNames(nm = mock_qual_names),
function(qual_name) {
pkg_name <- gsub(pkg_and_name_rx, "\\1", qual_name)

if (is_base_pkg(pkg_name)) {
cli::cli_abort(
"Can't mock functions in base package {.pkg {pkg_name}}."
)
}

name <- gsub(pkg_and_name_rx, "\\2", qual_name)

if (pkg_name == "") {
pkg_name <- .env
}

env <- asNamespace(pkg_name)

if (!exists(name, envir = env, mode = "function")) {
cli::cli_abort(
"Function {.fn {name}} not found in environment {environmentName(env)}."
)
}
mock(name = name, env = env, new = funs[[qual_name]])
}
)
}

mock <- function(name, env, new) {
target_value <- get(name, envir = env, mode = "function")
structure(
list(
env = env,
name = as.name(name),
orig_value = .Call(duplicate_, target_value),
target_value = target_value,
new_value = new
),
class = "mock"
)
}

set_mock <- function(mock) {
.Call(
reassign_function,
mock$name,
mock$env,
mock$target_value,
mock$new_value
)
}

reset_mock <- function(mock) {
.Call(
reassign_function,
mock$name,
mock$env,
mock$target_value,
mock$orig_value
)
}

is_base_pkg <- function(x) {
x %in% rownames(utils::installed.packages(priority = "base"))
lifecycle::deprecate_stop("3.2.0", "local_mock()", "local_mocked_bindings()")
}
14 changes: 4 additions & 10 deletions man/with_mock.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

109 changes: 99 additions & 10 deletions revdep/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,102 @@
# Revdeps

## New problems (6)

|package |version |error |warning |note |
|:-------|:--------|:------|:-------|:----|
|[arrow](problems.md#arrow)|17.0.0.1 |__+1__ | |2 |
|[epiCo](problems.md#epico)|1.0.0 |__+1__ | |1 |
|[ieegio](problems.md#ieegio)|0.0.2 |__+1__ | | |
|[madrat](problems.md#madrat)|3.6.4 |__+1__ | | |
|[vines](problems.md#vines)|1.1.5 |__+1__ | | |
|[xpose](problems.md#xpose)|0.4.18 |__+1__ | | |
## Failed to check (34)

|package |version |error |warning |note |
|:-------------------|:---------|:-----|:-------|:----|
|arealDB |0.9.4 |1 | | |
|arules |? | | | |
|atom4R |0.3-3 |1 | | |
|bayesdfa |1.3.4 |1 | | |
|ctsem |3.10.4 |1 | |1 |
|dataone |2.2.2 |1 | | |
|datapack |1.4.1 |1 | | |
|DSMolgenisArmadillo |? | | | |
|dsTidyverse |? | | | |
|dsTidyverseClient |? | | | |
|EcoEnsemble |1.1.2 |1 | | |
|FAfA |0.3 |1 | | |
|FAIRmaterials |0.4.2.1 |1 | | |
|fdaPDE |1.1-21 |1 | | |
|fio |0.1.6 |1 | | |
|geess |? | | | |
|gllvm |2.0.5 |1 | | |
|gpboost |1.6.1 |1 | | |
|gpuR |2.0.6 |1 | | |
|loon.shiny |? | | | |
|loon.tourr |? | | | |
|metajam |0.3.1 |1 | | |
|multinma |0.8.1 |1 | | |
|OpenMx |? | | | |
|rdflib |0.2.9 |1 | | |
|recommenderlab |? | | | |
|redland |1.0.17-18 |1 | | |
|rstanarm |2.32.1 |1 | | |
|SQLFormatteR |0.0.2 |1 | | |
|string2path |0.2.2 |1 | | |
|TestAnaAPP |1.1.2 |1 | | |
|TriDimRegression |1.0.2 |1 | | |
|xactonomial |1.0.3 |1 | | |
|zen4R |0.10.2 |1 | | |

## New problems (56)

|package |version |error |warning |note |
|:------------------|:-------|:--------|:-------|:------|
|[aws.comprehend](problems.md#awscomprehend)|0.2.1 |__+1__ | | |
|[bcRP](problems.md#bcrp)|1.0.1 |__+1__ | | |
|[bindr](problems.md#bindr)|0.1.2 |__+1__ | | |
|[conflr](problems.md#conflr)|0.1.1 |__+1__ | |2 |
|[countdown](problems.md#countdown)|0.4.0 |__+1__ | |1 |
|[covr](problems.md#covr)|3.6.4 |__+1__ | | |
|[datarobot](problems.md#datarobot)|2.18.6 |__+1__ | | |
|[digitize](problems.md#digitize)|0.0.4 |__+1__ | | |
|[distro](problems.md#distro)|0.1.0 |__+1__ | |1 |
|[esci](problems.md#esci)|1.0.7 | | |__+1__ |
|[gen3sis](problems.md#gen3sis)|1.5.11 |__+1__ | |1 |
|[geomorph](problems.md#geomorph)|4.0.10 | | |__+1__ |
|[graphhopper](problems.md#graphhopper)|0.1.2 |__+1__ | |1 |
|[handwriterRF](problems.md#handwriterrf)|1.1.1 |__+1__ | | |
|[humanize](problems.md#humanize)|0.2.0 |__+1__ | |1 |
|[ipaddress](problems.md#ipaddress)|1.0.2 |__+1__ | |1 |
|[leaflet.minicharts](problems.md#leafletminicharts)|0.6.2 |__+1__ | | |
|[learnr](problems.md#learnr)|0.11.5 |__+1__ | | |
|[MakefileR](problems.md#makefiler)|1.0 |__+1__ | |1 |
|[manipulateWidget](problems.md#manipulatewidget)|0.11.1 |__+1__ | |2 |
|[mbbe](problems.md#mbbe)|0.1.0 |__+1__ | | |
|[metaDigitise](problems.md#metadigitise)|1.0.1 |__+1__ | |1 |
|[mknapsack](problems.md#mknapsack)|0.1.0 |__+1__ | | |
|[mockery](problems.md#mockery)|0.4.4 |__+3__ | | |
|[moexer](problems.md#moexer)|0.3.0 |__+1__ | | |
|[MolgenisArmadillo](problems.md#molgenisarmadillo)|2.9.1 |__+1__ | | |
|[NasdaqDataLink](problems.md#nasdaqdatalink)|1.0.0 |__+1__ | | |
|[nhlapi](problems.md#nhlapi)|0.1.4 |__+1__ | |1 |
|[owmr](problems.md#owmr)|0.8.2 |__+1__ | | |
|[oxcAAR](problems.md#oxcaar)|1.1.1 |1 __+1__ | | |
|[parameters](problems.md#parameters)|0.27.0 | | |__+1__ |
|[passport](problems.md#passport)|0.3.0 |__+1__ | | |
|[pocketapi](problems.md#pocketapi)|0.1 |__+1__ | |2 |
|[projmgr](problems.md#projmgr)|0.1.1 |__+1__ | | |
|[PubChemR](problems.md#pubchemr)|2.1.4 |1 __+1__ | |1 |
|[Quandl](problems.md#quandl)|2.11.0 |__+1__ | | |
|[REddyProc](problems.md#reddyproc)|1.3.3 | | |__+1__ |
|[regmedint](problems.md#regmedint)|1.0.1 |__+1__ | |1 |
|[Rexperigen](problems.md#rexperigen)|0.2.1 |__+1__ | |1 |
|[rosetteApi](problems.md#rosetteapi)|1.14.4 |__+1__ | | |
|[Rpolyhedra](problems.md#rpolyhedra)|0.5.6 |__+1__ | | |
|[RPresto](problems.md#rpresto)|1.4.7 |__+1__ | | |
|[RTD](problems.md#rtd)|0.4.1 |__+1__ | |1 |
|[Ryacas0](problems.md#ryacas0)|0.4.4 |__+1__ | |2 |
|[shiny.benchmark](problems.md#shinybenchmark)|0.1.1 |__+1__ | | |
|[shinyShortcut](problems.md#shinyshortcut)|0.1.0 |__+1__ | |1 |
|[skimr](problems.md#skimr)|2.1.5 |__+1__ | | |
|[spaero](problems.md#spaero)|0.6.0 |__+1__ | |4 |
|[starwarsdb](problems.md#starwarsdb)|0.1.2 |__+1__ | |1 |
|[tangles](problems.md#tangles)|2.0.1 |__+1__ | | |
|[texreg](problems.md#texreg)|1.39.4 |__+1__ |1 |2 |
|[ThankYouStars](problems.md#thankyoustars)|0.2.0 |__+1__ | |1 |
|[tinyProject](problems.md#tinyproject)|0.6.1 |__+1__ | | |
|[tryCatchLog](problems.md#trycatchlog)|1.3.1 |__+1__ | |1 |
|[WhatIf](problems.md#whatif)|1.5-10 |__+1__ | | |
|[ZillowR](problems.md#zillowr)|1.0.0 |__+1__ | | |

Loading
Loading