local_options(
rlang_trace_use_winch = FALSE
)
# These tests must come first because print method includes srcrefs
test_that("tree printing only changes deliberately", {
# Because of srcrefs
skip_on_cran()
skip_if_not_installed("testthat", "2.99.0")
local_options(
rlang_trace_format_srcrefs = TRUE
)
dir <- normalizePath(test_path(".."))
e <- environment()
i <- function(i) j(i)
j <- function(i) { k(i) }
k <- function(i) {
NULL
l(i)
}
l <- function(i) trace_back(e)
trace <- i()
expect_snapshot({
print(trace, dir = dir)
cat("\n")
print(trace_slice(trace, 0L), dir = dir)
})
})
test_that("can print tree with collapsed branches", {
# Because of srcrefs
skip_on_cran()
skip_if_not_installed("testthat", "2.99.0")
# Fake eval() call does not have same signature on old R
skip_if(getRversion() < "3.4")
local_options(
rlang_trace_format_srcrefs = TRUE
)
dir <- normalizePath(test_path(".."))
e <- environment()
f <- function() { g() }
g <- function() { tryCatch(h(), foo = identity, bar = identity) }
h <- function() { tryCatch(i(), baz = identity) }
i <- function() { tryCatch(trace_back(e, bottom = 0)) }
trace <- eval(quote(f()))
expect_snapshot_trace(trace,
dir = dir,
srcrefs = TRUE
)
# With multiple siblings
f <- function() eval(quote(eval(quote(g()))))
g <- function() tryCatch(eval(quote(h())), foo = identity, bar = identity)
h <- function() trace_back(e)
trace <- eval(quote(f()))
expect_snapshot_trace(trace,
dir = dir,
srcrefs = TRUE
)
})
test_that("trace_simplify_branch() extracts last branch", {
e <- environment()
j <- function(i) k(i)
k <- function(i) l(i)
l <- function(i) eval(quote(m()), parent.frame(i))
m <- function() trace_back(e)
x1 <- j(1)
expect_equal(sum(x1$visible), 6)
expect_equal(sum(trace_simplify_branch(x1)$visible), 3)
x2 <- j(2)
expect_equal(sum(x2$visible), 6)
expect_equal(sum(trace_simplify_branch(x2)$visible), 2)
x3 <- j(3)
expect_equal(sum(x3$visible), 1)
expect_equal(sum(trace_simplify_branch(x3)$visible), 1)
})
test_that("integerish indices are allowed", {
trace <- trace_back()
expect_identical(trace_slice(trace, 0), trace_slice(trace, 0L))
})
test_that("cli_branch() handles edge case", {
e <- environment()
f <- function() trace_back(e)
trace <- f()
tree <- trace_as_tree(trace, srcrefs = FALSE)
expect_snapshot(cli_branch(tree[-1, ]))
})
test_that("trace formatting picks up `rlang_trace_format_srcrefs`", {
e <- environment()
f <- function() trace_back(e)
trace <- f()
with_options(
rlang_trace_format_srcrefs = FALSE,
expect_false(any(grepl("testthat", format(trace))))
)
with_options(
rlang_trace_format_srcrefs = TRUE,
expect_true(any(!!grepl("test-trace\\.R", format(trace))))
)
})
test_that("trace picks up option `rlang_trace_top_env` for trimming trace", {
e <- current_env()
f1 <- function() trace_back()
f2 <- function() trace_back(e)
with_options(rlang_trace_top_env = current_env(),
expect_identical(trace_length(f1()), trace_length(f2()))
)
})
# This test used to be about `simplify = "collapse"`
test_that("collapsed formatting doesn't collapse single frame siblings", {
e <- current_env()
f <- function() eval_bare(quote(g()))
g <- function() trace_back(e)
trace <- f()
expect_snapshot({
print(trace, simplify = "none", drop = TRUE, srcrefs = FALSE)
print(trace, simplify = "none", drop = FALSE, srcrefs = FALSE)
})
})
test_that("recursive frames are rewired to the global env", {
dir <- normalizePath(test_path(".."))
e <- environment()
f <- function() g()
g <- function() trace_back(e)
trace <- eval_tidy(quo(f()))
expect_snapshot_trace(trace)
})
test_that("long backtrace branches are truncated", {
e <- current_env()
f <- function(n) {
if (n) {
return(f(n - 1))
}
trace_back(e)
}
trace <- f(10)
expect_snapshot({
cat("Full:\n")
print(trace, simplify = "branch", srcrefs = FALSE)
cat("\n5 frames:\n")
print(trace, simplify = "branch", max_frames = 5, srcrefs = FALSE)
cat("\n2 frames:\n")
print(trace, simplify = "branch", max_frames = 2, srcrefs = FALSE)
cat("\n1 frame:\n")
print(trace, simplify = "branch", max_frames = 1, srcrefs = FALSE)
})
expect_error(print(trace, simplify = "none", max_frames = 5), "currently only supported with")
})
test_that("eval() frames are collapsed", {
# Fake eval() call does not have same signature on old R
skip_if(getRversion() < "3.4")
e <- current_env()
f <- function() base::eval(quote(g()))
g <- function() eval(quote(trace_back(e, bottom = 0)))
trace <- f()
expect_snapshot_trace(trace)
f <- function() base::evalq(g())
g <- function() evalq(trace_back(e, bottom = 0))
trace <- f()
expect_snapshot_trace(trace)
})
test_that("children of collapsed frames are rechained to correct parent", {
# Fake eval() call does not have same signature on old R
skip_if(getRversion() < "3.4")
e <- current_env()
f <- function() eval(quote(g()), env())
g <- function() trace_back(e)
trace <- f()
expect_snapshot({
cat("Full + drop:\n")
print(trace, simplify = "none", drop = TRUE, srcrefs = FALSE)
cat("Full - drop:\n")
print(trace, simplify = "none", drop = FALSE, srcrefs = FALSE)
cat("\nBranch:\n")
print(trace, simplify = "branch", srcrefs = FALSE)
})
})
test_that("combinations of incomplete and leading pipes collapse properly", {
skip_if_not_installed("magrittr", "1.5.0.9000")
skip_on_cran()
# Fake eval() call does not have same signature on old R
skip_if(getRversion() < "3.4")
`%>%` <- magrittr::`%>%`
e <- current_env()
F <- function(x, ...) x
T <- function(x) trace_back(e)
trace <- NA %>% F() %>% T() %>% F() %>% F()
expect_snapshot_trace(trace)
trace <- T(NA) %>% F()
expect_snapshot_trace(trace)
trace <- F(NA) %>% F() %>% T() %>% F() %>% F()
expect_snapshot_trace(trace)
trace <- NA %>% T()
expect_snapshot_trace(trace)
trace <- NA %>% F() %>% T()
expect_snapshot_trace(trace)
trace <- F(NA) %>% T()
expect_snapshot_trace(trace)
trace <- F(NA) %>% F() %>% T()
expect_snapshot_trace(trace)
})
test_that("calls before and after pipe are preserved", {
skip_if_not_installed("magrittr", "1.5.0.9000")
skip_on_cran()
# Fake eval() call does not have same signature on old R
skip_if(getRversion() < "3.4")
`%>%` <- magrittr::`%>%`
e <- current_env()
F <- function(x, ...) x
T <- function(x) trace_back(e)
C <- function(x) f()
f <- function() trace_back(e)
trace <- F(NA %>% T())
expect_snapshot_trace(trace)
trace <- NA %>% C()
expect_snapshot_trace(trace)
trace <- F(NA %>% C())
expect_snapshot_trace(trace)
})
test_that("always keep very first frame as part of backtrace branch", {
# Fake eval() call does not have same signature on old R
skip_if(getRversion() < "3.4")
e <- current_env()
gen <- function(x) UseMethod("gen")
gen.default <- function(x) trace_back(e)
trace <- gen()
expect_snapshot_trace(trace)
})
test_that("can take the str() of a trace (#615)", {
e <- current_env()
f <- function(n) if (n < 10) f(n - 1) else trace_back(e)
expect_output(expect_no_error(str(f(10))))
})
test_that("anonymous calls are stripped from backtraces", {
e <- current_env()
trace <- (function() {
"foo"
"bar"
trace_back(e)
})()
expect_identical(format(trace, simplify = "branch"), chr())
expect_snapshot_trace(trace)
})
test_that("collapsing of eval() frames detects when error occurs within eval()", {
e <- NULL
trace <- NULL
fn <- function() {
local_options(
rlang_trace_format_srcrefs = FALSE
)
e <<- current_env()
eval()
}
catch_cnd(withCallingHandlers(
fn(),
error = function(err) trace <<- trace_back(e)
))
expect_snapshot_trace(trace)
})
test_that("can print degenerate backtraces", {
trace_sym <- new_trace(list(quote(foo)), int(0))
expect_snapshot_trace(trace_sym)
trace_null <- new_trace(list(NULL), int(0))
expect_snapshot_trace(trace_null)
trace_scalar <- new_trace(list(1L), int(0))
expect_snapshot_trace(trace_scalar)
})
test_that("check for dangling promise in call CAR (#492)", {
expect_snapshot_trace(local({
e <- current_env()
print.foo <- function(x) {
rlang::trace_back(e)
}
foo <- structure(list(), class = "foo")
print(foo)
}))
})
test_that("dangling srcrefs are not printed", {
from <- test_path("fixtures", "trace-srcref.R")
to <- test_path("fixtures", "trace-srcref2.R")
file.copy(from, to)
on.exit(unlink(to))
source(to, local = TRUE, keep.source = TRUE)
unlink(to)
expect_snapshot_trace(
local(f(current_env())),
srcrefs = TRUE
)
})
test_that("summary.rlang_trace() prints the full tree", {
e <- current_env()
f <- function() g()
g <- function() h()
h <- function() trace_back(e)
trace <- f()
expect_snapshot(summary(trace, srcrefs = FALSE))
})
test_that("unexported functions have `:::` prefix", {
expect_true(TRUE)
return("no longer using the rlanglibtest")
# Should be installed as part of the C API tests
skip_if_not_installed("rlanglibtest")
test_trace_unexported_child <- env_get(ns_env("rlanglibtest"), "test_trace_unexported_child")
e <- current_env()
f <- function() test_trace_unexported_child(e)
trace <- f()
expect_snapshot_trace(trace)
})
test_that("global functions have `global::` prefix", {
f <- eval_bare(expr(function(e) rlang::trace_back(e)), global_env())
g <- function(e) f(e)
trace <- g(current_env())
expect_snapshot_trace(trace)
})
test_that("local functions inheriting from global do not have `global::` prefix", {
f <- eval_bare(expr(function(e) rlang::trace_back(e)), env(global_env()))
g <- function(e) f(e)
trace <- g(current_env())
expect_snapshot_trace(trace)
})
test_that("can trim layers of backtraces", {
e <- current_env()
f <- function(n) identity(identity(g(n)))
g <- function(n) identity(identity(h(n)))
h <- function(n) identity(identity(trace_back(e, bottom = n)))
trace0 <- f(0)
trace1 <- f(1)
trace2 <- f(2)
trace3 <- f(3)
expect_snapshot({
local_options(rlang_trace_format_srcrefs = FALSE)
cat_line("No trimming:")
summary(trace0)
cat_line("", "", "One layer (the default):")
summary(trace1)
cat_line("", "", "Two layers:")
summary(trace2)
cat_line("", "", "Three layers:")
summary(trace3)
})
# Test that trimming with frame environment is equivalent
e <- current_env()
f <- function(n) identity(identity(g(n)))
g <- function(n) identity(identity(h(n)))
h <- function(n) identity(identity(trace_back(e, bottom = caller_env(n - 1L))))
trace1_env <- f(1)
trace2_env <- f(2)
trace3_env <- f(3)
expect_equal_trace(trace1, trace1_env)
expect_equal_trace(trace2, trace2_env)
expect_equal_trace(trace3, trace3_env)
})
test_that("fails when `bottom` is not on the stack", {
expect_error(trace_back(bottom = env()), "Can't find `bottom`")
})
test_that("caught error does not display backtrace in knitted files", {
skip_if_not_installed("knitr")
skip_if_not_installed("rmarkdown")
skip_if(!rmarkdown::pandoc_available())
local_options(
rlang_backtrace_on_error = NULL,
rlang_backtrace_on_error_report = NULL,
rlang_interactive = FALSE
)
lines <- render_md("test-trace.Rmd")
error_line <- lines[[length(lines)]]
expect_match(error_line, "foo$")
expect_snapshot({
cat_line(render_md("test-trace-full.Rmd"))
})
})
test_that("empty backtraces are dealt with", {
foo <- NULL
local({
env <- new.env()
local_options(rlang_trace_top_env = env)
tryCatch(
error = identity,
withCallingHandlers(
error = function(cnd) foo <<- cnd_entrace(cnd),
eval(quote(stop("stop")), env)
)
)
})
expect_identical(trace_length(foo$trace), 0L)
})
test_that("can trace back with quosured symbol", {
e <- current_env()
f <- function(foo = g()) {
# This will create a call in the call stack that isn't really a call
quo <- quo(foo)
# Quosure must be nested otherwise `eval_tidy()` unwraps it
eval_tidy(expr(identity(!!quo)))
}
g <- function() trace_back(e)
# FIXME: Weird trace structure
trace <- f()
expect_s3_class(trace, "rlang_trace")
})
test_that("can slice backtrace", {
trace <- new_trace(alist(a(), b(), c()), 0:2)
expect_identical(
trace_slice(trace, 2:3),
new_trace(alist(b(), c()), 0:1)
)
exp <- new_trace(alist(a(), c()), c(0L, 0L))
expect_identical(
trace_slice(trace, c(1, 3)),
exp
)
expect_identical(
trace_slice(trace, -2),
exp
)
})
test_that("backtraces carry `version` attribute", {
expect_identical(attr(trace_back(), "version"), 2L)
})
test_that("can bind backtraces", {
trace1 <- new_trace(alist(a(), b(), c()), 0:2)
expect_equal(trace_bind(), new_trace(list(), int()))
expect_equal(trace_bind(trace1), trace1)
trace2 <- new_trace(alist(foo(), bar(), baz()), c(0L, 1L, 1L))
out <- trace_bind(trace1, trace2)
expect_equal(
out$call,
alist(a(), b(), c(), foo(), bar(), baz())
)
expect_equal(
out$parent,
c(0:3, c(4L, 4L))
)
})
test_that("backtraces don't contain inlined objects (#1069, r-lib/testthat#1223)", {
# !! deparsing in older R
skip_if_not_installed("base", "3.5.0")
local_options(
rlang_trace_format_srcrefs = FALSE
)
e <- environment()
f <- function(...) do.call("g", list(runif(1e6) + 0))
g <- function(...) h()
h <- function() trace_back(e)
trace <- inject(f(!!list()))
expect_snapshot(summary(trace))
expect_lt(object.size(trace$call), 50000)
})
test_that("runs of namespaces are embolden (#946)", {
local_options(
rlang_trace_format_srcrefs = FALSE,
rlang_trace_top_env = current_env()
)
f <- function() g()
g <- function() h()
h <- function() identity(1 + "")
err <- catch_cnd(withCallingHandlers(f(), error = entrace), "error")
testthat::local_reproducible_output(crayon = TRUE)
expect_snapshot({
print(err)
summary(err)
})
})
test_that("`bottom` must be a positive integer", {
expect_snapshot((expect_error(trace_back(bottom = -1))))
})
test_that("collapsed case in branch formatting", {
trace <- new_trace(alist(f(), g(), h(), evalq(), evalq()), 0:4)
expect_snapshot_output(print(trace, simplify = "branch"))
})
test_that("can detect namespace and scope from call", {
fn <- set_env(function() NULL, empty_env())
expect_equal(
call_trace_context(quote(bar()), fn),
trace_context()
)
expect_equal(
call_trace_context(quote(foo::bar()), fn),
trace_context("foo", "::")
)
expect_equal(
call_trace_context(quote(foo:::bar()), fn),
trace_context("foo", ":::")
)
})
test_that("trailing `FALSE` visibility is handled", {
trace <- new_trace(
alist(f(), g(), h(), foo(), bar()),
parent = 0:4,
visible = c(TRUE, TRUE, TRUE, FALSE, FALSE)
)
expect_snapshot_trace(trace)
})
test_that("can create empty trace with trace_back()", {
expect_equal(
trace_back(top = environment()),
new_trace(list(), int())
)
})
test_that("can format empty traces", {
trace <- new_trace(list(), int())
expect_snapshot_trace(trace)
})
test_that("backtrace is formatted with sources (#1396)", {
file <- tempfile("my_source", fileext = ".R")
with_srcref(file = file, "
f <- function() g()
g <- function() abort('foo')
")
err <- catch_cnd(f(), "error")
rlang_cli_local_hyperlinks()
lines <- format(err$trace)
n_links <- sum(grepl("\033]8;.*my_source.*\\.R:", lines))
expect_true(n_links > 0)
})
test_that("sibling streaks in tree backtraces", {
f <- function(x) identity(identity(x))
g <- function() f(f(h()))
h <- function() abort("foo")
err <- catch_cnd(f(g()), "error")
expect_snapshot_trace(err)
})
test_that("parallel '|' branches are correctly emphasised", {
f <- function(n) g(n)
g <- function(n) h(n)
h <- function(n) if (n) parallel(f(n - 1)) else abort("foo")
parallel <- function(x) p1(identity(x))
p1 <- function(x) p2(x)
p2 <- function(x) p3(x)
p3 <- function(x) x
err <- expect_error(parallel(f(0)))
expect_snapshot_trace(err)
deep <- function(n) parallel(f(n))
err <- expect_error(deep(1))
expect_snapshot_trace(err)
})
test_that("error calls and args are highlighted", {
f <- function(x) g(x)
g <- function(x) h(x)
h <- function(x) check_string(x)
wrapper <- function() {
try_fetch(f(1), error = function(cnd) abort("Tilt.", parent = cnd))
}
parent <- catch_error(f(1))
child <- catch_error(wrapper())
expect_snapshot({
print_highlighted_trace(parent)
print_highlighted_trace(child)
})
})
test_that("error calls and args are highlighted (no highlighted arg)", {
f <- function() g()
g <- function() h()
h <- function() abort("foo")
argless <- catch_error(f())
expect_snapshot({
print_highlighted_trace(argless)
})
})
test_that("frame is detected from the left", {
f <- function() g()
g <- function() h()
h <- function() identity(evalq(identity(abort("foo"))))
err <- catch_error(f())
expect_snapshot({
"If detected from the right, `evalq()`is highlighted instead of `h()`"
print_highlighted_trace(err)
})
})
test_that("arg is defensively checked", {
f <- function() g()
g <- function() h()
h <- function() abort("foo", arg = env())
err <- catch_error(f())
expect_snapshot({
print_highlighted_trace(err)
})
})
test_that("namespaced calls are highlighted", {
f <- function() g()
g <- function() h()
h <- function() rlang:::as_string(1)
err <- catch_error(f())
expect_snapshot({
print_highlighted_trace(err)
})
})
test_that("can highlight long lists of arguments in backtrace (#1456)", {
f <- function(...) g(
aaaaaaaaaaaa = aaaaaaaaaaaa,
bbbbbbbbbbbb = bbbbbbbbbbbb,
cccccccccccc = cccccccccccc,
dddddddddddd = dddddddddddd,
eeeeeeeeeeee = eeeeeeeeeeee,
...
)
g <- function(aaaaaaaaaaaa,
bbbbbbbbbbbb,
cccccccccccc,
dddddddddddd,
eeeeeeeeeeee, ...) {
rlang::abort("foo", ...)
}
err <- catch_error(f())
expect_snapshot({
print_highlighted_trace(err)
})
err <- catch_error(f(arg = "bbbbbbbbbbbb"))
expect_snapshot({
print_highlighted_trace(err)
})
})
test_that("can highlight multi-line arguments in backtrace (#1456)", {
f <- function(...) g(x = {
a
b
}, ...)
g <- function(x, ...) {
rlang::abort("foo", ...)
}
err <- catch_error(f())
expect_snapshot({
print_highlighted_trace(err)
})
err <- catch_error(f(arg = "x"))
expect_snapshot({
print_highlighted_trace(err)
})
})
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.