Skip to content
Merged
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
67 changes: 22 additions & 45 deletions R/test-that.R
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,6 @@ test_code <- function(test, code, env, reporter, skip_on_empty = TRUE) {
reporter$add_result(context = reporter$.context, test = test, result = e)
}

# Any error will be assigned to this variable first
# In case of stack overflow, no further processing (not even a call to
# signalCondition() ) might be possible
test_error <- NULL

expressions_opt <- getOption("expressions")
expressions_opt_new <- min(expressions_opt + 500L, 500000L)

Expand All @@ -104,39 +99,21 @@ test_code <- function(test, code, env, reporter, skip_on_empty = TRUE) {

handle_error <- function(e) {
handled <<- TRUE
# First thing: Collect test error
test_error <<- e

# Increase option(expressions) to handle errors here if possible, even in
# case of a stack overflow. This is important for the DebugReporter.
# Call options() manually, avoid withr overhead.
options(expressions = expressions_opt_new)
withr::defer(options(expressions = expressions_opt))
# case of a stack overflow. This is important for the DebugReporter.
local_options(expressions = expressions_opt_new)

# Add structured backtrace to the expectation
if (can_entrace(e)) {
e <- cnd_entrace(e)
}

test_error <<- e

# Error will be handled by handle_fatal() if this fails; need to do it here
# to be able to debug with the DebugReporter
register_expectation(e, 2)

e[["handled"]] <- TRUE
test_error <<- e
invokeRestart("end_test")
}
handle_fatal <- function(e) {
handled <<- TRUE
# Error caught in handle_error() has precedence
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer needed because we jump out of both the tryCatch + withCallingHandlers stack with a restart.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm... According to the comment on line 123, register_expectation() might fail, and one of the reasons the test_error setup was done is so the original error, rather than the internal one, would be registered here.

But we call register_expectation() again below (albeit with a different debug_end) so I'm not sure why this really matters.

Perhaps just remove the comment on line 123?

if (!is.null(test_error)) {
e <- test_error
if (isTRUE(e[["handled"]])) {
return()
}
}

register_expectation(e, 0)
}
handle_expectation <- function(e) {
Expand All @@ -162,7 +139,6 @@ test_code <- function(test, code, env, reporter, skip_on_empty = TRUE) {
}

register_expectation(e, 5)

tryInvokeRestart("muffleWarning")
}
handle_message <- function(e) {
Expand All @@ -175,7 +151,7 @@ test_code <- function(test, code, env, reporter, skip_on_empty = TRUE) {

debug_end <- if (inherits(e, "skip_empty")) -1 else 2
register_expectation(e, debug_end)
signalCondition(e)
invokeRestart("end_test")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now seems like a clear bug — resignalling a condition inside a calling handler would have just duplicated the condition.

}

test_env <- new.env(parent = env)
Expand All @@ -185,24 +161,25 @@ test_code <- function(test, code, env, reporter, skip_on_empty = TRUE) {
withr::local_options(testthat_topenv = test_env)

before <- inspect_state()
tryCatch(
withCallingHandlers(
{
eval(code, test_env)
if (!handled && !is.null(test)) {
skip_empty()
}
},
expectation = handle_expectation,
skip = handle_skip,
warning = handle_warning,
message = handle_message,
error = handle_error
withRestarts(
tryCatch(
withCallingHandlers(
{
eval(code, test_env)
if (!handled && !is.null(test)) {
skip_empty()
}
},
expectation = handle_expectation,
skip = handle_skip,
warning = handle_warning,
message = handle_message,
error = handle_error
),
# some errors may need handling here, e.g., stack overflow
error = handle_fatal
),
# some errors may need handling here, e.g., stack overflow
error = handle_fatal,
# skip silently terminate code
skip = function(e) {}
end_test = function() {}
)
after <- inspect_state()

Expand Down
Loading