test_that("can return from frame", { fn <- function() { val <- g() paste(val, "to fn()") } g <- function(env) { h(environment()) stop("g!\n") } h <- function(env) { return_from(env, "returned from h()") stop("h!\n") } expect_equal(fn(), "returned from h() to fn()") }) test_that("current_env() and current_fn() return current frame props", { fn <- function() { list( rlang = list(identity(current_env()), current_fn()), base = list(environment(), sys.function()) ) } out <- fn() expect_identical(out$rlang[[1]], out$base[[1]]) expect_identical(out$rlang[[2]], out$base[[2]]) }) test_that("sys_parents() removes infloop values", { f <- function() g() g <- function() sys_parents() parents <- do.call("f", list(), envir = env()) n <- length(parents) # No loop when called in non-frame env expect_false(any(parents == seq_len(n))) # g() is called by f() which is called by global because the calling # env is not on the stack expect_equal(parents[[n - 1]], 0) # f() expect_equal(parents[[n]], n - 1) # g() }) test_that("current_fn() and caller_fn() work", { f <- function(n) identity(g(n)) g <- function(n) identity(h(n)) h <- function(n) identity(caller_fn(n)) expect_equal(f(1), g) expect_equal(f(2), f) # Need to break the chain of callers to get `NULL` at `n = 3`. # Otherwise we get the `eval()` frame from testthat expect_null(eval_bare(quote(f(3)), env())) f <- function() current_fn() expect_equal(f(), f) }) test_that("Parents are matched to youngest duplicate frames", { skip_on_cran() out <- env() f <- function() { invisible(g(environment(), report("f"))) } g <- function(env, arg) { fn <- function() h(env, arg) eval(as.call(list(fn)), env) } h <- function(env, arg) { fn <- function() list(arg, report("h")) eval(as.call(list(fn)), env) } report <- function(what) { parents <- sys_parents(match_oldest = FALSE) env_poke(out, what, parents) } f() f_parents <- tail(out[["f"]], 10) - length(out[["f"]]) + 10 h_parents <- tail(out[["h"]], 10) - length(out[["h"]]) + 10 expect_equal(f_parents, c(0:8, 1L)) expect_equal(h_parents, 0:9) }) test_that("frame_fn() returns the function of the supplied frame", { f <- function() { identity(g(current_env())) } g <- function(frame) { identity(h(frame)) } h <- function(frame) { tryCatch(frame_fn(frame)) } expect_equal(f(), f) f <- function() { evalq(g(current_env())) } expect_equal(f(), f) f <- function() { evalq(g(current_env()), env()) } eval_prim <- eval(call2(sys.function)) expect_equal(f(), eval_prim) f <- function() { eval_bare(quote(g(current_env())), env()) } expect_null(f()) }) test_that("current_call(), caller_call() and frame_call() work", { expect_null(eval_bare(call2(current_call), global_env())) expect_null(eval_bare(call2(caller_call), global_env())) expect_null(eval_bare(call2(frame_call), global_env())) f <- function() g() g <- function() { direct <- frame_call() indirect <- evalq(frame_call()) expect_equal(direct, indirect) } f() f <- function() { this <- current_call() that <- g() expect_equal(this, quote(f())) expect_equal(this, that) } g <- function() caller_call() f() return("Don't make this guarantee to stay consistent with `caller_env()`") f <- function() g() g <- function() { direct <- caller_call() indirect <- h(current_env()) expect_equal(indirect, direct) } h <- function(env) evalq(caller_call(), env) f() }) test_that("caller_env2() respects invariant", { f <- function() h() h <- function() { indirect <- evalq(caller_env2()) direct <- caller_env2() expect_equal(indirect, direct) } f() f <- function() g() g <- function() inject(caller_env2(), env()) expect_equal(f(), global_env()) })