From df10cdfba90f393dcfabee88cbb8a6db8915938f Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 17 Dec 2024 12:01:08 +0100 Subject: [PATCH 01/16] Don't use a restart to handle `stop_on_error = 2L` In this case, the handler could simply return NULL --- R/conditions.R | 3 ++- R/evaluate.R | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/R/conditions.R b/R/conditions.R index 98255536..099536e4 100644 --- a/R/conditions.R +++ b/R/conditions.R @@ -34,7 +34,8 @@ condition_handlers <- function(watcher, on_error, on_warning, on_message) { switch(on_error, continue = invokeRestart("eval_continue"), stop = invokeRestart("eval_stop"), - error = invokeRestart("eval_error", cnd) + # No need to invoke a restart as we want the error to be thrown in this case. + error = NULL ) } ) diff --git a/R/evaluate.R b/R/evaluate.R index 4444c63b..6e56dcb0 100644 --- a/R/evaluate.R +++ b/R/evaluate.R @@ -151,8 +151,7 @@ evaluate <- function(input, handlers ), eval_continue = function() TRUE, - eval_stop = function() FALSE, - eval_error = function(cnd) {signalCondition(cnd); stop(cnd)} + eval_stop = function() FALSE ) watcher$check_devices() From 86325bb3b301dcda702f2a2ba5f61d0cca2391a8 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 17 Dec 2024 15:10:59 +0100 Subject: [PATCH 02/16] stop_on_error = 2L make evaluate error with output captured --- tests/testthat/_snaps/conditions.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/testthat/_snaps/conditions.md b/tests/testthat/_snaps/conditions.md index 1bad37f4..44f97f78 100644 --- a/tests/testthat/_snaps/conditions.md +++ b/tests/testthat/_snaps/conditions.md @@ -13,4 +13,15 @@ Condition Error: ! 1 + Output + + Source code: + stop("1") + Condition: + Error: + 1 + Source code: + 2 + Text output: + [1] 2 From 24e30b68341afca85c365876ee57d4afcefe018e Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 18 Dec 2024 12:28:39 +0100 Subject: [PATCH 03/16] assign result to value for cleaner snapshot --- tests/testthat/_snaps/conditions.md | 15 ++------------- tests/testthat/test-conditions.R | 4 ++-- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/tests/testthat/_snaps/conditions.md b/tests/testthat/_snaps/conditions.md index 44f97f78..aaa62a6e 100644 --- a/tests/testthat/_snaps/conditions.md +++ b/tests/testthat/_snaps/conditions.md @@ -6,22 +6,11 @@ Warning in `f()`: Hi! -# all three starts of stop_on_error work as expected +# all three values of stop_on_error work as expected Code - evaluate("stop(\"1\")\n2", stop_on_error = 2L) + ev <- evaluate("stop(\"1\")\n2", stop_on_error = 2L) Condition Error: ! 1 - Output - - Source code: - stop("1") - Condition: - Error: - 1 - Source code: - 2 - Text output: - [1] 2 diff --git a/tests/testthat/test-conditions.R b/tests/testthat/test-conditions.R index 218cd096..f47737df 100644 --- a/tests/testthat/test-conditions.R +++ b/tests/testthat/test-conditions.R @@ -118,14 +118,14 @@ test_that("an error terminates evaluation of multi-expression input", { expect_output_types(ev, c("source", "error")) }) -test_that("all three starts of stop_on_error work as expected", { +test_that("all three values of stop_on_error work as expected", { ev <- evaluate('stop("1")\n2', stop_on_error = 0L) expect_output_types(ev, c("source", "error", "source", "text")) ev <- evaluate('stop("1")\n2', stop_on_error = 1L) expect_output_types(ev, c("source", "error")) - expect_snapshot(evaluate('stop("1")\n2', stop_on_error = 2L), error = TRUE) + expect_snapshot(ev <- evaluate("stop(\"1\")\n2", stop_on_error = 2L), error = TRUE) }) test_that("errors during printing are captured", { From 8774fe287643e56fd2b0f850cf375a78dccc2d4f Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Thu, 19 Dec 2024 15:30:01 +0100 Subject: [PATCH 04/16] Add some test about render error and backtrace showing --- .../_snaps/conditions/abort-error.txt | 12 +++++++++++ .../testthat/_snaps/conditions/stop-error.txt | 12 +++++++++++ tests/testthat/helper.R | 17 +++++++++++++++ .../testthat/ressources/with-abort-error.Rmd | 15 +++++++++++++ tests/testthat/ressources/with-stop-error.Rmd | 15 +++++++++++++ tests/testthat/test-conditions.R | 21 +++++++++++++++++++ 6 files changed, 92 insertions(+) create mode 100644 tests/testthat/_snaps/conditions/abort-error.txt create mode 100644 tests/testthat/_snaps/conditions/stop-error.txt create mode 100644 tests/testthat/ressources/with-abort-error.Rmd create mode 100644 tests/testthat/ressources/with-stop-error.Rmd diff --git a/tests/testthat/_snaps/conditions/abort-error.txt b/tests/testthat/_snaps/conditions/abort-error.txt new file mode 100644 index 00000000..a64af295 --- /dev/null +++ b/tests/testthat/_snaps/conditions/abort-error.txt @@ -0,0 +1,12 @@ + + +processing file: with-abort-error.Rmd +Error in `h()`: +! ! +Backtrace: + 1. global f() + 2. global g() + 3. global h() + +Quitting from lines 11-15 [unnamed-chunk-2] (with-abort-error.Rmd) +Execution halted diff --git a/tests/testthat/_snaps/conditions/stop-error.txt b/tests/testthat/_snaps/conditions/stop-error.txt new file mode 100644 index 00000000..6947c891 --- /dev/null +++ b/tests/testthat/_snaps/conditions/stop-error.txt @@ -0,0 +1,12 @@ + + +processing file: with-stop-error.Rmd +Error in `h()`: +! ! +Backtrace: + 1. global f() + 2. global g() + 3. global h() + +Quitting from lines 11-15 [unnamed-chunk-2] (with-stop-error.Rmd) +Execution halted diff --git a/tests/testthat/helper.R b/tests/testthat/helper.R index 7a222667..3ded4e2d 100644 --- a/tests/testthat/helper.R +++ b/tests/testthat/helper.R @@ -2,3 +2,20 @@ expect_output_types <- function(x, types) { output_types <- vapply(x, output_type, character(1)) expect_equal(output_types, types) } + +quick_install <- function(package, lib, quiet = TRUE) { + opts <- c( + "--data-compress=none", + "--no-byte-compile", + "--no-data", + "--no-demo", + "--no-docs", + "--no-help", + "--no-html", + "--no-libs", + "--use-vanilla", + sprintf("--library=%s", lib), + package + ) + invisible(callr::rcmd("INSTALL", opts, show = !quiet, fail_on_status = TRUE)) +} diff --git a/tests/testthat/ressources/with-abort-error.Rmd b/tests/testthat/ressources/with-abort-error.Rmd new file mode 100644 index 00000000..3ed32cd6 --- /dev/null +++ b/tests/testthat/ressources/with-abort-error.Rmd @@ -0,0 +1,15 @@ +--- +title: document with error +--- + +```{r} +rlang::global_entrace() +options(rlang_backtrace_on_error_report = "full") +``` + +```{r} +f <- function() g() +g <- function() h() +h <- function() rlang::abort("!") +f() +``` diff --git a/tests/testthat/ressources/with-stop-error.Rmd b/tests/testthat/ressources/with-stop-error.Rmd new file mode 100644 index 00000000..2d9b679b --- /dev/null +++ b/tests/testthat/ressources/with-stop-error.Rmd @@ -0,0 +1,15 @@ +--- +title: document with error +--- + +```{r} +rlang::global_entrace() +options(rlang_backtrace_on_error_report = "full") +``` + +```{r} +f <- function() g() +g <- function() h() +h <- function() stop("!") +f() +``` diff --git a/tests/testthat/test-conditions.R b/tests/testthat/test-conditions.R index f47737df..553515a5 100644 --- a/tests/testthat/test-conditions.R +++ b/tests/testthat/test-conditions.R @@ -136,3 +136,24 @@ test_that("errors during printing are captured", { ev <- evaluate("a") expect_output_types(ev, c("source", "error")) }) + +test_that("Entraced error does not loose their backtrace when render errors", { + skip_if_not_installed("rlang") + skip_if_not_installed("rmarkdown") + skip_if_not_installed("callr") + skip_on_cran() + # install dev version of package in temp directory + withr::local_temp_libpaths() + quick_install(pkgload::pkg_path("."), lib = .libPaths()[1]) + + out <- withr::local_tempfile(fileext = "txt") + + rscript <- withr::local_tempfile(fileext = "R") + writeLines(sprintf("rmarkdown::render(%s)", dQuote(test_path("ressources/with-stop-error.Rmd"), FALSE)), con = rscript) + callr::rscript(rscript, fail_on_status = FALSE, show = FALSE, stderr = out) + expect_snapshot_file(out, name = 'stop-error.txt') + + writeLines(sprintf("rmarkdown::render(%s)", dQuote(test_path("ressources/with-abort-error.Rmd"), FALSE)), con = rscript) + callr::rscript(rscript, fail_on_status = FALSE, show = FALSE, stderr = out) + expect_snapshot_file(out, name = 'abort-error.txt') +}) From 385365f05267ec56d3035679f27534b9ac99a19a Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Thu, 19 Dec 2024 18:15:30 +0100 Subject: [PATCH 05/16] Add missing Suggests packages --- DESCRIPTION | 3 +++ 1 file changed, 3 insertions(+) diff --git a/DESCRIPTION b/DESCRIPTION index 1e5ce4ca..a398b03a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -25,11 +25,14 @@ BugReports: https://github.com/r-lib/evaluate/issues Depends: R (>= 3.6.0) Suggests: + callr, covr, ggplot2 (>= 3.3.6), lattice, methods, + pkgload, rlang, + rmarkdown, testthat (>= 3.0.0), withr Config/Needs/website: tidyverse/tidytemplate From 26c3a685174f8709f78fc925adb1e3c448fdec93 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Thu, 19 Dec 2024 18:58:41 +0100 Subject: [PATCH 06/16] when testing rlang::abort(), `rlang::global_entrace()` is not needed --- tests/testthat/_snaps/conditions/abort-error.txt | 2 +- tests/testthat/ressources/with-abort-error.Rmd | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/testthat/_snaps/conditions/abort-error.txt b/tests/testthat/_snaps/conditions/abort-error.txt index a64af295..fd12b07b 100644 --- a/tests/testthat/_snaps/conditions/abort-error.txt +++ b/tests/testthat/_snaps/conditions/abort-error.txt @@ -8,5 +8,5 @@ Backtrace: 2. global g() 3. global h() -Quitting from lines 11-15 [unnamed-chunk-2] (with-abort-error.Rmd) +Quitting from lines 6-10 [unnamed-chunk-1] (with-abort-error.Rmd) Execution halted diff --git a/tests/testthat/ressources/with-abort-error.Rmd b/tests/testthat/ressources/with-abort-error.Rmd index 3ed32cd6..b7262fbf 100644 --- a/tests/testthat/ressources/with-abort-error.Rmd +++ b/tests/testthat/ressources/with-abort-error.Rmd @@ -2,11 +2,6 @@ title: document with error --- -```{r} -rlang::global_entrace() -options(rlang_backtrace_on_error_report = "full") -``` - ```{r} f <- function() g() g <- function() h() From 7bf2d80f9bd03e2d9b1ae8a238c5b610b4a8e22b Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Thu, 19 Dec 2024 19:00:32 +0100 Subject: [PATCH 07/16] Remove unneeded option to clarify the test case `options(rlang_backtrace_on_error_report = "full")` is only useful when error is not thrown by render() and kept as cell output (e.g. error = TRUE) --- tests/testthat/_snaps/conditions/stop-error.txt | 2 +- tests/testthat/ressources/with-stop-error.Rmd | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/testthat/_snaps/conditions/stop-error.txt b/tests/testthat/_snaps/conditions/stop-error.txt index 6947c891..dae4297e 100644 --- a/tests/testthat/_snaps/conditions/stop-error.txt +++ b/tests/testthat/_snaps/conditions/stop-error.txt @@ -8,5 +8,5 @@ Backtrace: 2. global g() 3. global h() -Quitting from lines 11-15 [unnamed-chunk-2] (with-stop-error.Rmd) +Quitting from lines 10-14 [unnamed-chunk-2] (with-stop-error.Rmd) Execution halted diff --git a/tests/testthat/ressources/with-stop-error.Rmd b/tests/testthat/ressources/with-stop-error.Rmd index 2d9b679b..1a5572ec 100644 --- a/tests/testthat/ressources/with-stop-error.Rmd +++ b/tests/testthat/ressources/with-stop-error.Rmd @@ -4,7 +4,6 @@ title: document with error ```{r} rlang::global_entrace() -options(rlang_backtrace_on_error_report = "full") ``` ```{r} From 6b3108490f8a6f136b7e0d1e9a402967dd2eaf12 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Fri, 20 Dec 2024 14:01:52 +0100 Subject: [PATCH 08/16] Add more test cases for complete coverage and example of stop_on_error = 2L behavior - Using evaluate only - In rmarkdown + knitr context --- .../conditions/stop-error-auto-entrace.txt | 12 +++++++++ .../_snaps/conditions/stop-error-no-trace.txt | 3 +++ .../stop-error-trace-calling-handler.txt | 23 +++++++++++++++++ .../conditions/stop-error-trace-trimmed.txt | 8 ++++++ .../conditions/stop-error-trace-wch.txt | 22 ++++++++++++++++ .../testthat/_snaps/conditions/stop-error.txt | 12 --------- tests/testthat/ressources/with-abort-error.R | 6 +++++ ...r.Rmd => with-stop-error-auto-entrace.Rmd} | 0 .../ressources/with-stop-error-no-trace.R | 6 +++++ .../with-stop-error-trace-trimmed.R | 10 ++++++++ .../ressources/with-stop-error-trace.R | 9 +++++++ .../testthat/ressources/with-stop-error-wch.R | 8 ++++++ tests/testthat/test-conditions.R | 25 ++++++++++++++++--- 13 files changed, 128 insertions(+), 16 deletions(-) create mode 100644 tests/testthat/_snaps/conditions/stop-error-auto-entrace.txt create mode 100644 tests/testthat/_snaps/conditions/stop-error-no-trace.txt create mode 100644 tests/testthat/_snaps/conditions/stop-error-trace-calling-handler.txt create mode 100644 tests/testthat/_snaps/conditions/stop-error-trace-trimmed.txt create mode 100644 tests/testthat/_snaps/conditions/stop-error-trace-wch.txt delete mode 100644 tests/testthat/_snaps/conditions/stop-error.txt create mode 100644 tests/testthat/ressources/with-abort-error.R rename tests/testthat/ressources/{with-stop-error.Rmd => with-stop-error-auto-entrace.Rmd} (100%) create mode 100644 tests/testthat/ressources/with-stop-error-no-trace.R create mode 100644 tests/testthat/ressources/with-stop-error-trace-trimmed.R create mode 100644 tests/testthat/ressources/with-stop-error-trace.R create mode 100644 tests/testthat/ressources/with-stop-error-wch.R diff --git a/tests/testthat/_snaps/conditions/stop-error-auto-entrace.txt b/tests/testthat/_snaps/conditions/stop-error-auto-entrace.txt new file mode 100644 index 00000000..4572c1cf --- /dev/null +++ b/tests/testthat/_snaps/conditions/stop-error-auto-entrace.txt @@ -0,0 +1,12 @@ + + +processing file: with-stop-error-auto-entrace.Rmd +Error in `h()`: +! ! +Backtrace: + 1. global f() + 2. global g() + 3. global h() + +Quitting from lines 10-14 [unnamed-chunk-2] (with-stop-error-auto-entrace.Rmd) +Execution halted diff --git a/tests/testthat/_snaps/conditions/stop-error-no-trace.txt b/tests/testthat/_snaps/conditions/stop-error-no-trace.txt new file mode 100644 index 00000000..7b09931c --- /dev/null +++ b/tests/testthat/_snaps/conditions/stop-error-no-trace.txt @@ -0,0 +1,3 @@ +Error in h() : ! +Calls: ... withCallingHandlers -> withVisible -> eval -> eval -> f -> g -> h +Execution halted diff --git a/tests/testthat/_snaps/conditions/stop-error-trace-calling-handler.txt b/tests/testthat/_snaps/conditions/stop-error-trace-calling-handler.txt new file mode 100644 index 00000000..7ef2b6c9 --- /dev/null +++ b/tests/testthat/_snaps/conditions/stop-error-trace-calling-handler.txt @@ -0,0 +1,23 @@ +Error in `h()`: +! ! +Backtrace: + ▆ + 1. ├─evaluate::evaluate(...) + 2. │ ├─base::withRestarts(...) + 3. │ │ └─base (local) withRestartList(expr, restarts) + 4. │ │ ├─base (local) withOneRestart(withRestartList(expr, restarts[-nr]), restarts[[nr]]) + 5. │ │ │ └─base (local) doWithOneRestart(return(expr), restart) + 6. │ │ └─base (local) withRestartList(expr, restarts[-nr]) + 7. │ │ └─base (local) withOneRestart(expr, restarts[[1L]]) + 8. │ │ └─base (local) doWithOneRestart(return(expr), restart) + 9. │ ├─evaluate:::with_handlers(...) + 10. │ │ ├─base::eval(call) + 11. │ │ │ └─base::eval(call) + 12. │ │ └─base::withCallingHandlers(...) + 13. │ ├─base::withVisible(eval(expr, envir)) + 14. │ └─base::eval(expr, envir) + 15. │ └─base::eval(expr, envir) + 16. └─global f() + 17. └─global g() + 18. └─global h() +Execution halted diff --git a/tests/testthat/_snaps/conditions/stop-error-trace-trimmed.txt b/tests/testthat/_snaps/conditions/stop-error-trace-trimmed.txt new file mode 100644 index 00000000..0389c681 --- /dev/null +++ b/tests/testthat/_snaps/conditions/stop-error-trace-trimmed.txt @@ -0,0 +1,8 @@ +Error in `h()`: +! ! +Backtrace: + ▆ + 1. └─global f() + 2. └─global g() + 3. └─global h() +Execution halted diff --git a/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt b/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt new file mode 100644 index 00000000..b27ea1d7 --- /dev/null +++ b/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt @@ -0,0 +1,22 @@ +Error in `g()`: +! could not find function "g" +Backtrace: + ▆ + 1. ├─base::withCallingHandlers(...) + 2. ├─evaluate::evaluate(...) + 3. │ ├─base::withRestarts(...) + 4. │ │ └─base (local) withRestartList(expr, restarts) + 5. │ │ ├─base (local) withOneRestart(withRestartList(expr, restarts[-nr]), restarts[[nr]]) + 6. │ │ │ └─base (local) doWithOneRestart(return(expr), restart) + 7. │ │ └─base (local) withRestartList(expr, restarts[-nr]) + 8. │ │ └─base (local) withOneRestart(expr, restarts[[1L]]) + 9. │ │ └─base (local) doWithOneRestart(return(expr), restart) + 10. │ ├─evaluate:::with_handlers(...) + 11. │ │ ├─base::eval(call) + 12. │ │ │ └─base::eval(call) + 13. │ │ └─base::withCallingHandlers(...) + 14. │ ├─base::withVisible(eval(expr, envir)) + 15. │ └─base::eval(expr, envir) + 16. │ └─base::eval(expr, envir) + 17. └─global f() +Execution halted diff --git a/tests/testthat/_snaps/conditions/stop-error.txt b/tests/testthat/_snaps/conditions/stop-error.txt deleted file mode 100644 index dae4297e..00000000 --- a/tests/testthat/_snaps/conditions/stop-error.txt +++ /dev/null @@ -1,12 +0,0 @@ - - -processing file: with-stop-error.Rmd -Error in `h()`: -! ! -Backtrace: - 1. global f() - 2. global g() - 3. global h() - -Quitting from lines 10-14 [unnamed-chunk-2] (with-stop-error.Rmd) -Execution halted diff --git a/tests/testthat/ressources/with-abort-error.R b/tests/testthat/ressources/with-abort-error.R new file mode 100644 index 00000000..362d4cd8 --- /dev/null +++ b/tests/testthat/ressources/with-abort-error.R @@ -0,0 +1,6 @@ +evaluate::evaluate(function() { + f <- function() g() + g <- function() h() + h <- function() rlang::abort("!") + f() +}, stop_on_error = 2L) diff --git a/tests/testthat/ressources/with-stop-error.Rmd b/tests/testthat/ressources/with-stop-error-auto-entrace.Rmd similarity index 100% rename from tests/testthat/ressources/with-stop-error.Rmd rename to tests/testthat/ressources/with-stop-error-auto-entrace.Rmd diff --git a/tests/testthat/ressources/with-stop-error-no-trace.R b/tests/testthat/ressources/with-stop-error-no-trace.R new file mode 100644 index 00000000..9fa53c6f --- /dev/null +++ b/tests/testthat/ressources/with-stop-error-no-trace.R @@ -0,0 +1,6 @@ +evaluate::evaluate(function() { + f <- function() g() + g <- function() h() + h <- function() stop("!") + f() +}, stop_on_error = 2L) diff --git a/tests/testthat/ressources/with-stop-error-trace-trimmed.R b/tests/testthat/ressources/with-stop-error-trace-trimmed.R new file mode 100644 index 00000000..6b2f42be --- /dev/null +++ b/tests/testthat/ressources/with-stop-error-trace-trimmed.R @@ -0,0 +1,10 @@ +handlers <- evaluate::new_output_handler( + calling_handlers = list(error = function(cnd) rlang::entrace(cnd)) +) +options(rlang_trace_top_env = parent.frame()) +evaluate::evaluate(function() { + f <- function() g() + g <- function() h() + h <- function() stop("!") + f() +}, stop_on_error = 2L, output_handler = handlers) diff --git a/tests/testthat/ressources/with-stop-error-trace.R b/tests/testthat/ressources/with-stop-error-trace.R new file mode 100644 index 00000000..78c5149f --- /dev/null +++ b/tests/testthat/ressources/with-stop-error-trace.R @@ -0,0 +1,9 @@ +handlers <- evaluate::new_output_handler( + calling_handlers = list(error = function(cnd) rlang::entrace(cnd)) +) +evaluate::evaluate(function() { + f <- function() g() + g <- function() h() + h <- function() stop("!") + f() +}, stop_on_error = 2L, output_handler = handlers) diff --git a/tests/testthat/ressources/with-stop-error-wch.R b/tests/testthat/ressources/with-stop-error-wch.R new file mode 100644 index 00000000..db3194b5 --- /dev/null +++ b/tests/testthat/ressources/with-stop-error-wch.R @@ -0,0 +1,8 @@ +withCallingHandlers( + error = function(cnd) rlang::entrace(cnd), + evaluate::evaluate(function() { + f <- function() g() + h <- function() stop("!") + f() + }, stop_on_error = 2L) +) diff --git a/tests/testthat/test-conditions.R b/tests/testthat/test-conditions.R index 553515a5..ca04fc45 100644 --- a/tests/testthat/test-conditions.R +++ b/tests/testthat/test-conditions.R @@ -137,7 +137,7 @@ test_that("errors during printing are captured", { expect_output_types(ev, c("source", "error")) }) -test_that("Entraced error does not loose their backtrace when render errors", { +test_that("Error can be entraced and are shown correctly when stop_on_error = 2L.", { skip_if_not_installed("rlang") skip_if_not_installed("rmarkdown") skip_if_not_installed("callr") @@ -147,11 +147,28 @@ test_that("Entraced error does not loose their backtrace when render errors", { quick_install(pkgload::pkg_path("."), lib = .libPaths()[1]) out <- withr::local_tempfile(fileext = "txt") - + + # Checking different way to entrace with evaluate + callr::rscript(test_path("ressources/with-stop-error-no-trace.R"), fail_on_status = FALSE, show = FALSE, stderr = out) + expect_snapshot_file(out, name = 'stop-error-no-trace.txt') + + callr::rscript(test_path("ressources/with-stop-error-trace.R"), fail_on_status = FALSE, show = FALSE, stderr = out) + expect_snapshot_file(out, name = 'stop-error-trace-calling-handler.txt') + + callr::rscript(test_path("ressources/with-stop-error-wch.R"), fail_on_status = FALSE, show = FALSE, stderr = out) + expect_snapshot_file(out, name = 'stop-error-trace-wch.txt') + + callr::rscript(test_path("ressources/with-stop-error-trace-trimmed.R"), fail_on_status = FALSE, show = FALSE, stderr = out) + expect_snapshot_file(out, name = 'stop-error-trace-trimmed.txt') + + callr::rscript(test_path("ressources/with-abort-error.R"), fail_on_status = FALSE, show = FALSE, stderr = out) + expect_snapshot_file(out, name = 'abort-error.txt') + + # Checking error in rmarkdown and knitr context rscript <- withr::local_tempfile(fileext = "R") - writeLines(sprintf("rmarkdown::render(%s)", dQuote(test_path("ressources/with-stop-error.Rmd"), FALSE)), con = rscript) + writeLines(sprintf("rmarkdown::render(%s)", dQuote(test_path("ressources/with-stop-error-auto-entrace.Rmd"), FALSE)), con = rscript) callr::rscript(rscript, fail_on_status = FALSE, show = FALSE, stderr = out) - expect_snapshot_file(out, name = 'stop-error.txt') + expect_snapshot_file(out, name = 'stop-error-auto-entrace.txt') writeLines(sprintf("rmarkdown::render(%s)", dQuote(test_path("ressources/with-abort-error.Rmd"), FALSE)), con = rscript) callr::rscript(rscript, fail_on_status = FALSE, show = FALSE, stderr = out) From 880b664fb75e16e795ed5ec188d7501ae16a0d59 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Fri, 20 Dec 2024 14:32:55 +0100 Subject: [PATCH 09/16] Use knitr and test sewed output - Use knitr instead of rmarkdown - Test error thrown and capture in cell output --- DESCRIPTION | 2 +- .../_snaps/conditions/abort-error.txt | 28 +++++++++---- .../_snaps/conditions/rmd-abort-error.md | 22 ++++++++++ .../_snaps/conditions/rmd-abort-error.txt | 12 ++++++ .../rmd-stop-error-auto-entrace.txt | 12 ++++++ .../rmd-stop-error-entrace-sewed.md | 27 ++++++++++++ .../_snaps/conditions/rmd-stop-error.md | 15 +++++++ .../conditions/stop-error-auto-entrace.txt | 12 ------ .../with-stop-error-auto-entrace.Rmd | 4 -- .../ressources/with-stop-error-sewed.Rmd | 15 +++++++ tests/testthat/test-conditions.R | 41 +++++++++++++++---- 11 files changed, 156 insertions(+), 34 deletions(-) create mode 100644 tests/testthat/_snaps/conditions/rmd-abort-error.md create mode 100644 tests/testthat/_snaps/conditions/rmd-abort-error.txt create mode 100644 tests/testthat/_snaps/conditions/rmd-stop-error-auto-entrace.txt create mode 100644 tests/testthat/_snaps/conditions/rmd-stop-error-entrace-sewed.md create mode 100644 tests/testthat/_snaps/conditions/rmd-stop-error.md delete mode 100644 tests/testthat/_snaps/conditions/stop-error-auto-entrace.txt create mode 100644 tests/testthat/ressources/with-stop-error-sewed.Rmd diff --git a/DESCRIPTION b/DESCRIPTION index a398b03a..114dff11 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -32,7 +32,7 @@ Suggests: methods, pkgload, rlang, - rmarkdown, + knitr, testthat (>= 3.0.0), withr Config/Needs/website: tidyverse/tidytemplate diff --git a/tests/testthat/_snaps/conditions/abort-error.txt b/tests/testthat/_snaps/conditions/abort-error.txt index fd12b07b..3cfb05da 100644 --- a/tests/testthat/_snaps/conditions/abort-error.txt +++ b/tests/testthat/_snaps/conditions/abort-error.txt @@ -1,12 +1,24 @@ - - -processing file: with-abort-error.Rmd Error in `h()`: ! ! Backtrace: - 1. global f() - 2. global g() - 3. global h() - -Quitting from lines 6-10 [unnamed-chunk-1] (with-abort-error.Rmd) + ▆ + 1. ├─evaluate::evaluate(...) + 2. │ ├─base::withRestarts(...) + 3. │ │ └─base (local) withRestartList(expr, restarts) + 4. │ │ ├─base (local) withOneRestart(withRestartList(expr, restarts[-nr]), restarts[[nr]]) + 5. │ │ │ └─base (local) doWithOneRestart(return(expr), restart) + 6. │ │ └─base (local) withRestartList(expr, restarts[-nr]) + 7. │ │ └─base (local) withOneRestart(expr, restarts[[1L]]) + 8. │ │ └─base (local) doWithOneRestart(return(expr), restart) + 9. │ ├─evaluate:::with_handlers(...) + 10. │ │ ├─base::eval(call) + 11. │ │ │ └─base::eval(call) + 12. │ │ └─base::withCallingHandlers(...) + 13. │ ├─base::withVisible(eval(expr, envir)) + 14. │ └─base::eval(expr, envir) + 15. │ └─base::eval(expr, envir) + 16. └─global f() + 17. └─global g() + 18. └─global h() + 19. └─rlang::abort("!") Execution halted diff --git a/tests/testthat/_snaps/conditions/rmd-abort-error.md b/tests/testthat/_snaps/conditions/rmd-abort-error.md new file mode 100644 index 00000000..0fa32f41 --- /dev/null +++ b/tests/testthat/_snaps/conditions/rmd-abort-error.md @@ -0,0 +1,22 @@ +--- +title: document with error +--- + + +``` r +f <- function() g() +g <- function() h() +h <- function() rlang::abort("!") +f() +``` + +``` +## Error in `h()`: +## ! ! +## Backtrace: +## x +## 1. \-evaluate (local) f() +## 2. \-evaluate (local) g() +## 3. \-evaluate (local) h() +## 4. \-rlang::abort("!") +``` diff --git a/tests/testthat/_snaps/conditions/rmd-abort-error.txt b/tests/testthat/_snaps/conditions/rmd-abort-error.txt new file mode 100644 index 00000000..114acb66 --- /dev/null +++ b/tests/testthat/_snaps/conditions/rmd-abort-error.txt @@ -0,0 +1,12 @@ + + +processing file: ressources/with-abort-error.Rmd +Error in `h()`: +! ! +Backtrace: + 1. global f() + 2. global g() + 3. global h() + +Quitting from lines 6-10 [unnamed-chunk-1] (ressources/with-abort-error.Rmd) +Execution halted diff --git a/tests/testthat/_snaps/conditions/rmd-stop-error-auto-entrace.txt b/tests/testthat/_snaps/conditions/rmd-stop-error-auto-entrace.txt new file mode 100644 index 00000000..c2ea7b05 --- /dev/null +++ b/tests/testthat/_snaps/conditions/rmd-stop-error-auto-entrace.txt @@ -0,0 +1,12 @@ + + +processing file: ressources/with-stop-error-auto-entrace.Rmd +Error in `h()`: +! ! +Backtrace: + 1. global f() + 2. global g() + 3. global h() + +Quitting from lines 6-10 [unnamed-chunk-1] (ressources/with-stop-error-auto-entrace.Rmd) +Execution halted diff --git a/tests/testthat/_snaps/conditions/rmd-stop-error-entrace-sewed.md b/tests/testthat/_snaps/conditions/rmd-stop-error-entrace-sewed.md new file mode 100644 index 00000000..a7f1e5ec --- /dev/null +++ b/tests/testthat/_snaps/conditions/rmd-stop-error-entrace-sewed.md @@ -0,0 +1,27 @@ +--- +title: document with error +--- + + +``` r +rlang::global_entrace() +options(rlang_backtrace_on_error_report = "full") +``` + + +``` r +f <- function() g() +g <- function() h() +h <- function() stop("!") +f() +``` + +``` +## Error in `h()`: +## ! ! +## Backtrace: +## x +## 1. \-evaluate (local) f() +## 2. \-evaluate (local) g() +## 3. \-evaluate (local) h() +``` diff --git a/tests/testthat/_snaps/conditions/rmd-stop-error.md b/tests/testthat/_snaps/conditions/rmd-stop-error.md new file mode 100644 index 00000000..78ea2154 --- /dev/null +++ b/tests/testthat/_snaps/conditions/rmd-stop-error.md @@ -0,0 +1,15 @@ +--- +title: document with error +--- + + +``` r +f <- function() g() +g <- function() h() +h <- function() stop("!") +f() +``` + +``` +## Error in h(): ! +``` diff --git a/tests/testthat/_snaps/conditions/stop-error-auto-entrace.txt b/tests/testthat/_snaps/conditions/stop-error-auto-entrace.txt deleted file mode 100644 index 4572c1cf..00000000 --- a/tests/testthat/_snaps/conditions/stop-error-auto-entrace.txt +++ /dev/null @@ -1,12 +0,0 @@ - - -processing file: with-stop-error-auto-entrace.Rmd -Error in `h()`: -! ! -Backtrace: - 1. global f() - 2. global g() - 3. global h() - -Quitting from lines 10-14 [unnamed-chunk-2] (with-stop-error-auto-entrace.Rmd) -Execution halted diff --git a/tests/testthat/ressources/with-stop-error-auto-entrace.Rmd b/tests/testthat/ressources/with-stop-error-auto-entrace.Rmd index 1a5572ec..ae2d7174 100644 --- a/tests/testthat/ressources/with-stop-error-auto-entrace.Rmd +++ b/tests/testthat/ressources/with-stop-error-auto-entrace.Rmd @@ -2,10 +2,6 @@ title: document with error --- -```{r} -rlang::global_entrace() -``` - ```{r} f <- function() g() g <- function() h() diff --git a/tests/testthat/ressources/with-stop-error-sewed.Rmd b/tests/testthat/ressources/with-stop-error-sewed.Rmd new file mode 100644 index 00000000..2d9b679b --- /dev/null +++ b/tests/testthat/ressources/with-stop-error-sewed.Rmd @@ -0,0 +1,15 @@ +--- +title: document with error +--- + +```{r} +rlang::global_entrace() +options(rlang_backtrace_on_error_report = "full") +``` + +```{r} +f <- function() g() +g <- function() h() +h <- function() stop("!") +f() +``` diff --git a/tests/testthat/test-conditions.R b/tests/testthat/test-conditions.R index ca04fc45..4beb6f2b 100644 --- a/tests/testthat/test-conditions.R +++ b/tests/testthat/test-conditions.R @@ -137,16 +137,16 @@ test_that("errors during printing are captured", { expect_output_types(ev, c("source", "error")) }) -test_that("Error can be entraced and are shown correctly when stop_on_error = 2L.", { +test_that("Error can be entraced and correctly handled in outputs", { skip_if_not_installed("rlang") - skip_if_not_installed("rmarkdown") + skip_if_not_installed("knitr") skip_if_not_installed("callr") skip_on_cran() # install dev version of package in temp directory withr::local_temp_libpaths() quick_install(pkgload::pkg_path("."), lib = .libPaths()[1]) - out <- withr::local_tempfile(fileext = "txt") + out <- withr::local_tempfile(fileext = ".txt") # Checking different way to entrace with evaluate callr::rscript(test_path("ressources/with-stop-error-no-trace.R"), fail_on_status = FALSE, show = FALSE, stderr = out) @@ -164,13 +164,36 @@ test_that("Error can be entraced and are shown correctly when stop_on_error = 2L callr::rscript(test_path("ressources/with-abort-error.R"), fail_on_status = FALSE, show = FALSE, stderr = out) expect_snapshot_file(out, name = 'abort-error.txt') - # Checking error in rmarkdown and knitr context - rscript <- withr::local_tempfile(fileext = "R") - writeLines(sprintf("rmarkdown::render(%s)", dQuote(test_path("ressources/with-stop-error-auto-entrace.Rmd"), FALSE)), con = rscript) + # Checking error thrown when in rmarkdown and knitr context + rscript <- withr::local_tempfile(fileext = ".R") + out2 <- normalizePath(withr::local_tempfile(fileext = ".md"), winslash = "/", mustWork = FALSE) + writeLines(c( + "options(knitr.chunk.error = FALSE)", + sprintf('knitr::knit("%s", output = "%s")', test_path("ressources/with-stop-error-auto-entrace.Rmd"), out2) + ), con = rscript) callr::rscript(rscript, fail_on_status = FALSE, show = FALSE, stderr = out) - expect_snapshot_file(out, name = 'stop-error-auto-entrace.txt') + expect_snapshot_file(out, name = 'rmd-stop-error-auto-entrace.txt') - writeLines(sprintf("rmarkdown::render(%s)", dQuote(test_path("ressources/with-abort-error.Rmd"), FALSE)), con = rscript) + writeLines(c( + "options(knitr.chunk.error = FALSE)", + sprintf('res <- knitr::knit("%s", output = "%s")', test_path("ressources/with-abort-error.Rmd"), out2) + ), con = rscript) callr::rscript(rscript, fail_on_status = FALSE, show = FALSE, stderr = out) - expect_snapshot_file(out, name = 'abort-error.txt') + expect_snapshot_file(out, name = 'rmd-abort-error.txt') + + # Checking error captured in cell output in rmarkdown and knitr context + withr::with_options(list(options(knitr.chunk.error = TRUE)), { + expect_snapshot_file( + knitr::knit(test_path("ressources/with-stop-error-auto-entrace.Rmd"), output = out, quiet = TRUE), + name = "rmd-stop-error.md" + ) + expect_snapshot_file( + knitr::knit(test_path("ressources/with-stop-error-sewed.Rmd"), output = out, quiet = TRUE), + name = "rmd-stop-error-entrace-sewed.md" + ) + expect_snapshot_file( + knitr::knit(test_path("ressources/with-abort-error.Rmd"), output = out, quiet = TRUE), + name = "rmd-abort-error.md" + ) + }) }) From c9d7a6966f6bc00fcc272bf2388c0f188ae987bf Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Fri, 20 Dec 2024 15:21:42 +0100 Subject: [PATCH 10/16] indicate to rlang top env for trimmed traceback --- R/evaluate.R | 5 ++++ .../_snaps/conditions/abort-error.txt | 25 ++++--------------- .../stop-error-trace-calling-handler.txt | 23 +++-------------- .../conditions/stop-error-trace-wch.txt | 20 ++------------- 4 files changed, 16 insertions(+), 57 deletions(-) diff --git a/R/evaluate.R b/R/evaluate.R index 6e56dcb0..c23817ee 100644 --- a/R/evaluate.R +++ b/R/evaluate.R @@ -121,6 +121,11 @@ evaluate <- function(input, } local_inject_funs(envir) + if (is.null(getOption("rlang_trace_top_env"))) { + # If not already set, indicate the top environment to trim traceback + options(rlang_trace_top_env = envir) + } + # Handlers for warnings, errors and messages user_handlers <- output_handler$calling_handlers evaluate_handlers <- condition_handlers( diff --git a/tests/testthat/_snaps/conditions/abort-error.txt b/tests/testthat/_snaps/conditions/abort-error.txt index 3cfb05da..9f55bb7d 100644 --- a/tests/testthat/_snaps/conditions/abort-error.txt +++ b/tests/testthat/_snaps/conditions/abort-error.txt @@ -1,24 +1,9 @@ Error in `h()`: ! ! Backtrace: - ▆ - 1. ├─evaluate::evaluate(...) - 2. │ ├─base::withRestarts(...) - 3. │ │ └─base (local) withRestartList(expr, restarts) - 4. │ │ ├─base (local) withOneRestart(withRestartList(expr, restarts[-nr]), restarts[[nr]]) - 5. │ │ │ └─base (local) doWithOneRestart(return(expr), restart) - 6. │ │ └─base (local) withRestartList(expr, restarts[-nr]) - 7. │ │ └─base (local) withOneRestart(expr, restarts[[1L]]) - 8. │ │ └─base (local) doWithOneRestart(return(expr), restart) - 9. │ ├─evaluate:::with_handlers(...) - 10. │ │ ├─base::eval(call) - 11. │ │ │ └─base::eval(call) - 12. │ │ └─base::withCallingHandlers(...) - 13. │ ├─base::withVisible(eval(expr, envir)) - 14. │ └─base::eval(expr, envir) - 15. │ └─base::eval(expr, envir) - 16. └─global f() - 17. └─global g() - 18. └─global h() - 19. └─rlang::abort("!") + ▆ + 1. └─global f() + 2. └─global g() + 3. └─global h() + 4. └─rlang::abort("!") Execution halted diff --git a/tests/testthat/_snaps/conditions/stop-error-trace-calling-handler.txt b/tests/testthat/_snaps/conditions/stop-error-trace-calling-handler.txt index 7ef2b6c9..0389c681 100644 --- a/tests/testthat/_snaps/conditions/stop-error-trace-calling-handler.txt +++ b/tests/testthat/_snaps/conditions/stop-error-trace-calling-handler.txt @@ -1,23 +1,8 @@ Error in `h()`: ! ! Backtrace: - ▆ - 1. ├─evaluate::evaluate(...) - 2. │ ├─base::withRestarts(...) - 3. │ │ └─base (local) withRestartList(expr, restarts) - 4. │ │ ├─base (local) withOneRestart(withRestartList(expr, restarts[-nr]), restarts[[nr]]) - 5. │ │ │ └─base (local) doWithOneRestart(return(expr), restart) - 6. │ │ └─base (local) withRestartList(expr, restarts[-nr]) - 7. │ │ └─base (local) withOneRestart(expr, restarts[[1L]]) - 8. │ │ └─base (local) doWithOneRestart(return(expr), restart) - 9. │ ├─evaluate:::with_handlers(...) - 10. │ │ ├─base::eval(call) - 11. │ │ │ └─base::eval(call) - 12. │ │ └─base::withCallingHandlers(...) - 13. │ ├─base::withVisible(eval(expr, envir)) - 14. │ └─base::eval(expr, envir) - 15. │ └─base::eval(expr, envir) - 16. └─global f() - 17. └─global g() - 18. └─global h() + ▆ + 1. └─global f() + 2. └─global g() + 3. └─global h() Execution halted diff --git a/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt b/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt index b27ea1d7..530ff5e8 100644 --- a/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt +++ b/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt @@ -1,22 +1,6 @@ Error in `g()`: ! could not find function "g" Backtrace: - ▆ - 1. ├─base::withCallingHandlers(...) - 2. ├─evaluate::evaluate(...) - 3. │ ├─base::withRestarts(...) - 4. │ │ └─base (local) withRestartList(expr, restarts) - 5. │ │ ├─base (local) withOneRestart(withRestartList(expr, restarts[-nr]), restarts[[nr]]) - 6. │ │ │ └─base (local) doWithOneRestart(return(expr), restart) - 7. │ │ └─base (local) withRestartList(expr, restarts[-nr]) - 8. │ │ └─base (local) withOneRestart(expr, restarts[[1L]]) - 9. │ │ └─base (local) doWithOneRestart(return(expr), restart) - 10. │ ├─evaluate:::with_handlers(...) - 11. │ │ ├─base::eval(call) - 12. │ │ │ └─base::eval(call) - 13. │ │ └─base::withCallingHandlers(...) - 14. │ ├─base::withVisible(eval(expr, envir)) - 15. │ └─base::eval(expr, envir) - 16. │ └─base::eval(expr, envir) - 17. └─global f() + ▆ + 1. └─global f() Execution halted From 9b2a12bcc8cc0e5596bc4a372f9a5caa9bc15f82 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Fri, 20 Dec 2024 15:36:06 +0100 Subject: [PATCH 11/16] Ensure reproducible output in snapshot testthat::local_reproducible_output() needs to be called in the background Rscript process too --- tests/testthat/_snaps/conditions/abort-error.txt | 11 ++++++----- .../_snaps/conditions/stop-error-no-trace.txt | 1 + .../conditions/stop-error-trace-calling-handler.txt | 9 +++++---- .../_snaps/conditions/stop-error-trace-trimmed.txt | 9 +++++---- .../_snaps/conditions/stop-error-trace-wch.txt | 5 +++-- tests/testthat/ressources/with-abort-error.R | 1 + tests/testthat/ressources/with-stop-error-no-trace.R | 1 + .../ressources/with-stop-error-trace-trimmed.R | 1 + tests/testthat/ressources/with-stop-error-trace.R | 1 + tests/testthat/ressources/with-stop-error-wch.R | 1 + 10 files changed, 25 insertions(+), 15 deletions(-) diff --git a/tests/testthat/_snaps/conditions/abort-error.txt b/tests/testthat/_snaps/conditions/abort-error.txt index 9f55bb7d..97966625 100644 --- a/tests/testthat/_snaps/conditions/abort-error.txt +++ b/tests/testthat/_snaps/conditions/abort-error.txt @@ -1,9 +1,10 @@ Error in `h()`: ! ! Backtrace: - ▆ - 1. └─global f() - 2. └─global g() - 3. └─global h() - 4. └─rlang::abort("!") + x + 1. \-global f() + 2. \-global g() + 3. \-global h() + 4. \-rlang::abort("!") Execution halted +Ran 8/8 deferred expressions diff --git a/tests/testthat/_snaps/conditions/stop-error-no-trace.txt b/tests/testthat/_snaps/conditions/stop-error-no-trace.txt index 7b09931c..66f5c0ea 100644 --- a/tests/testthat/_snaps/conditions/stop-error-no-trace.txt +++ b/tests/testthat/_snaps/conditions/stop-error-no-trace.txt @@ -1,3 +1,4 @@ Error in h() : ! Calls: ... withCallingHandlers -> withVisible -> eval -> eval -> f -> g -> h Execution halted +Ran 8/8 deferred expressions diff --git a/tests/testthat/_snaps/conditions/stop-error-trace-calling-handler.txt b/tests/testthat/_snaps/conditions/stop-error-trace-calling-handler.txt index 0389c681..fd142af4 100644 --- a/tests/testthat/_snaps/conditions/stop-error-trace-calling-handler.txt +++ b/tests/testthat/_snaps/conditions/stop-error-trace-calling-handler.txt @@ -1,8 +1,9 @@ Error in `h()`: ! ! Backtrace: - ▆ - 1. └─global f() - 2. └─global g() - 3. └─global h() + x + 1. \-global f() + 2. \-global g() + 3. \-global h() Execution halted +Ran 8/8 deferred expressions diff --git a/tests/testthat/_snaps/conditions/stop-error-trace-trimmed.txt b/tests/testthat/_snaps/conditions/stop-error-trace-trimmed.txt index 0389c681..fd142af4 100644 --- a/tests/testthat/_snaps/conditions/stop-error-trace-trimmed.txt +++ b/tests/testthat/_snaps/conditions/stop-error-trace-trimmed.txt @@ -1,8 +1,9 @@ Error in `h()`: ! ! Backtrace: - ▆ - 1. └─global f() - 2. └─global g() - 3. └─global h() + x + 1. \-global f() + 2. \-global g() + 3. \-global h() Execution halted +Ran 8/8 deferred expressions diff --git a/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt b/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt index 530ff5e8..55d0d0eb 100644 --- a/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt +++ b/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt @@ -1,6 +1,7 @@ Error in `g()`: ! could not find function "g" Backtrace: - ▆ - 1. └─global f() + x + 1. \-global f() Execution halted +Ran 8/8 deferred expressions diff --git a/tests/testthat/ressources/with-abort-error.R b/tests/testthat/ressources/with-abort-error.R index 362d4cd8..fe6a15a0 100644 --- a/tests/testthat/ressources/with-abort-error.R +++ b/tests/testthat/ressources/with-abort-error.R @@ -1,3 +1,4 @@ +testthat::local_reproducible_output() evaluate::evaluate(function() { f <- function() g() g <- function() h() diff --git a/tests/testthat/ressources/with-stop-error-no-trace.R b/tests/testthat/ressources/with-stop-error-no-trace.R index 9fa53c6f..2dc76d53 100644 --- a/tests/testthat/ressources/with-stop-error-no-trace.R +++ b/tests/testthat/ressources/with-stop-error-no-trace.R @@ -1,3 +1,4 @@ +testthat::local_reproducible_output() evaluate::evaluate(function() { f <- function() g() g <- function() h() diff --git a/tests/testthat/ressources/with-stop-error-trace-trimmed.R b/tests/testthat/ressources/with-stop-error-trace-trimmed.R index 6b2f42be..05fc9e7a 100644 --- a/tests/testthat/ressources/with-stop-error-trace-trimmed.R +++ b/tests/testthat/ressources/with-stop-error-trace-trimmed.R @@ -1,3 +1,4 @@ +testthat::local_reproducible_output() handlers <- evaluate::new_output_handler( calling_handlers = list(error = function(cnd) rlang::entrace(cnd)) ) diff --git a/tests/testthat/ressources/with-stop-error-trace.R b/tests/testthat/ressources/with-stop-error-trace.R index 78c5149f..32a69b20 100644 --- a/tests/testthat/ressources/with-stop-error-trace.R +++ b/tests/testthat/ressources/with-stop-error-trace.R @@ -1,3 +1,4 @@ +testthat::local_reproducible_output() handlers <- evaluate::new_output_handler( calling_handlers = list(error = function(cnd) rlang::entrace(cnd)) ) diff --git a/tests/testthat/ressources/with-stop-error-wch.R b/tests/testthat/ressources/with-stop-error-wch.R index db3194b5..2a287533 100644 --- a/tests/testthat/ressources/with-stop-error-wch.R +++ b/tests/testthat/ressources/with-stop-error-wch.R @@ -1,3 +1,4 @@ +testthat::local_reproducible_output() withCallingHandlers( error = function(cnd) rlang::entrace(cnd), evaluate::evaluate(function() { From c6ce2c963f6c08c2bc777fbadaf0c5857c0ab24b Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Fri, 20 Dec 2024 15:50:34 +0100 Subject: [PATCH 12/16] Fix wrong error in test file because g() was missing --- tests/testthat/_snaps/conditions/stop-error-trace-wch.txt | 6 ++++-- tests/testthat/ressources/with-stop-error-wch.R | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt b/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt index 55d0d0eb..fd142af4 100644 --- a/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt +++ b/tests/testthat/_snaps/conditions/stop-error-trace-wch.txt @@ -1,7 +1,9 @@ -Error in `g()`: -! could not find function "g" +Error in `h()`: +! ! Backtrace: x 1. \-global f() + 2. \-global g() + 3. \-global h() Execution halted Ran 8/8 deferred expressions diff --git a/tests/testthat/ressources/with-stop-error-wch.R b/tests/testthat/ressources/with-stop-error-wch.R index 2a287533..1d7aee68 100644 --- a/tests/testthat/ressources/with-stop-error-wch.R +++ b/tests/testthat/ressources/with-stop-error-wch.R @@ -3,6 +3,7 @@ withCallingHandlers( error = function(cnd) rlang::entrace(cnd), evaluate::evaluate(function() { f <- function() g() + g <- function() h() h <- function() stop("!") f() }, stop_on_error = 2L) From 3a955424a2ed219281d09f2efdac87f2d28f5cd9 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 8 Jan 2025 12:23:42 +0100 Subject: [PATCH 13/16] Evaluate trim by default so test opting out for different rlang top env --- .../conditions/stop-error-trace-trim.txt | 24 +++++++++++++++++++ ...trimmed.R => with-stop-error-trace-trim.R} | 5 ++-- tests/testthat/test-conditions.R | 11 ++++++--- 3 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 tests/testthat/_snaps/conditions/stop-error-trace-trim.txt rename tests/testthat/ressources/{with-stop-error-trace-trimmed.R => with-stop-error-trace-trim.R} (74%) diff --git a/tests/testthat/_snaps/conditions/stop-error-trace-trim.txt b/tests/testthat/_snaps/conditions/stop-error-trace-trim.txt new file mode 100644 index 00000000..6d7c3275 --- /dev/null +++ b/tests/testthat/_snaps/conditions/stop-error-trace-trim.txt @@ -0,0 +1,24 @@ +Error in `h()`: +! ! +Backtrace: + x + 1. +-evaluate::evaluate(...) + 2. | +-base::withRestarts(...) + 3. | | \-base (local) withRestartList(expr, restarts) + 4. | | +-base (local) withOneRestart(withRestartList(expr, restarts[-nr]), restarts[[nr]]) + 5. | | | \-base (local) doWithOneRestart(return(expr), restart) + 6. | | \-base (local) withRestartList(expr, restarts[-nr]) + 7. | | \-base (local) withOneRestart(expr, restarts[[1L]]) + 8. | | \-base (local) doWithOneRestart(return(expr), restart) + 9. | +-evaluate:::with_handlers(...) + 10. | | +-base::eval(call) + 11. | | | \-base::eval(call) + 12. | | \-base::withCallingHandlers(...) + 13. | +-base::withVisible(eval(expr, envir)) + 14. | \-base::eval(expr, envir) + 15. | \-base::eval(expr, envir) + 16. \-global f() + 17. \-global g() + 18. \-global h() +Execution halted +Ran 8/8 deferred expressions diff --git a/tests/testthat/ressources/with-stop-error-trace-trimmed.R b/tests/testthat/ressources/with-stop-error-trace-trim.R similarity index 74% rename from tests/testthat/ressources/with-stop-error-trace-trimmed.R rename to tests/testthat/ressources/with-stop-error-trace-trim.R index 05fc9e7a..3c16eed1 100644 --- a/tests/testthat/ressources/with-stop-error-trace-trimmed.R +++ b/tests/testthat/ressources/with-stop-error-trace-trim.R @@ -2,8 +2,9 @@ testthat::local_reproducible_output() handlers <- evaluate::new_output_handler( calling_handlers = list(error = function(cnd) rlang::entrace(cnd)) ) -options(rlang_trace_top_env = parent.frame()) -evaluate::evaluate(function() { +library(evaluate) +options(rlang_trace_top_env = rlang::pkg_env("evaluate")) +evaluate(function() { f <- function() g() g <- function() h() h <- function() stop("!") diff --git a/tests/testthat/test-conditions.R b/tests/testthat/test-conditions.R index 4beb6f2b..eb45a839 100644 --- a/tests/testthat/test-conditions.R +++ b/tests/testthat/test-conditions.R @@ -149,21 +149,26 @@ test_that("Error can be entraced and correctly handled in outputs", { out <- withr::local_tempfile(fileext = ".txt") # Checking different way to entrace with evaluate + ## No trace callr::rscript(test_path("ressources/with-stop-error-no-trace.R"), fail_on_status = FALSE, show = FALSE, stderr = out) expect_snapshot_file(out, name = 'stop-error-no-trace.txt') + ## Using calling.handler in evaluate's output handler callr::rscript(test_path("ressources/with-stop-error-trace.R"), fail_on_status = FALSE, show = FALSE, stderr = out) expect_snapshot_file(out, name = 'stop-error-trace-calling-handler.txt') + ## Using withCallingHandler() callr::rscript(test_path("ressources/with-stop-error-wch.R"), fail_on_status = FALSE, show = FALSE, stderr = out) expect_snapshot_file(out, name = 'stop-error-trace-wch.txt') - callr::rscript(test_path("ressources/with-stop-error-trace-trimmed.R"), fail_on_status = FALSE, show = FALSE, stderr = out) - expect_snapshot_file(out, name = 'stop-error-trace-trimmed.txt') - + ## Using abort() in evaluated code callr::rscript(test_path("ressources/with-abort-error.R"), fail_on_status = FALSE, show = FALSE, stderr = out) expect_snapshot_file(out, name = 'abort-error.txt') + # setting option rlang_trace_top_env modified opt-out default evaluate trace trimming + callr::rscript(test_path("ressources/with-stop-error-trace-trim.R"), fail_on_status = FALSE, show = FALSE, stderr = out) + expect_snapshot_file(out, name = 'stop-error-trace-trim.txt') + # Checking error thrown when in rmarkdown and knitr context rscript <- withr::local_tempfile(fileext = ".R") out2 <- normalizePath(withr::local_tempfile(fileext = ".md"), winslash = "/", mustWork = FALSE) From 82557b4cef9b6529da37bd26e933628f9934163f Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 8 Jan 2025 15:21:32 +0100 Subject: [PATCH 14/16] scrub traceback source file data To allow reproducibility on CI --- tests/testthat/test-conditions.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/testthat/test-conditions.R b/tests/testthat/test-conditions.R index eb45a839..ffbe6656 100644 --- a/tests/testthat/test-conditions.R +++ b/tests/testthat/test-conditions.R @@ -167,7 +167,8 @@ test_that("Error can be entraced and correctly handled in outputs", { # setting option rlang_trace_top_env modified opt-out default evaluate trace trimming callr::rscript(test_path("ressources/with-stop-error-trace-trim.R"), fail_on_status = FALSE, show = FALSE, stderr = out) - expect_snapshot_file(out, name = 'stop-error-trace-trim.txt') + expect_snapshot_file(out, name = 'stop-error-trace-trim.txt', + transform = function(lines) gsub("\\s*at evaluate/R/evaluate.R(:\\d+)*", "", lines)) # Checking error thrown when in rmarkdown and knitr context rscript <- withr::local_tempfile(fileext = ".R") From d7e3fa819a0a1e618ffdeda1b9bc590fd652973a Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 8 Jan 2025 15:21:46 +0100 Subject: [PATCH 15/16] remove previous snapshot --- .../_snaps/conditions/stop-error-trace-trimmed.txt | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 tests/testthat/_snaps/conditions/stop-error-trace-trimmed.txt diff --git a/tests/testthat/_snaps/conditions/stop-error-trace-trimmed.txt b/tests/testthat/_snaps/conditions/stop-error-trace-trimmed.txt deleted file mode 100644 index fd142af4..00000000 --- a/tests/testthat/_snaps/conditions/stop-error-trace-trimmed.txt +++ /dev/null @@ -1,9 +0,0 @@ -Error in `h()`: -! ! -Backtrace: - x - 1. \-global f() - 2. \-global g() - 3. \-global h() -Execution halted -Ran 8/8 deferred expressions From 98ff231b52c379898b30ca3db2e70750a75f93dc Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Wed, 8 Jan 2025 15:28:22 +0100 Subject: [PATCH 16/16] correctly scrub possible source file path --- tests/testthat/test-conditions.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-conditions.R b/tests/testthat/test-conditions.R index ffbe6656..8a79e7ae 100644 --- a/tests/testthat/test-conditions.R +++ b/tests/testthat/test-conditions.R @@ -168,7 +168,7 @@ test_that("Error can be entraced and correctly handled in outputs", { # setting option rlang_trace_top_env modified opt-out default evaluate trace trimming callr::rscript(test_path("ressources/with-stop-error-trace-trim.R"), fail_on_status = FALSE, show = FALSE, stderr = out) expect_snapshot_file(out, name = 'stop-error-trace-trim.txt', - transform = function(lines) gsub("\\s*at evaluate/R/evaluate.R(:\\d+)*", "", lines)) + transform = function(lines) gsub("\\s*at evaluate/R/.*\\.R(:\\d+)*", "", lines)) # Checking error thrown when in rmarkdown and knitr context rscript <- withr::local_tempfile(fileext = ".R")