mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-10 07:28:01 -05:00
* Limit deep stack growth * Improvements to deep stack trace culling - Keep around the first deep stack trace; it may have useful information. (We may want to change this in the future to keep the first two stack traces, or even make it an option) - Print out an indicator that we've elided stack traces, and how many * Add comments * Add NEWS item * Add test for unlimited deep stacks * Code review feedback * Code review feedback Co-authored-by: Carson Sievert <cpsievert1@gmail.com> * Use head() over indexing Co-authored-by: Carson Sievert <cpsievert1@gmail.com> * Improve unit test robustness * Remove vector indices from snapshot * Make stack trace stripping work across deep stacks * Pass tests * Try passing tests again * Rename keep_head to retain_first_n * Remove misleading variable assignment * Add more comments, refine dropTrivialTestFrames * Don't call stripStackTraces if we're not stripping * Use deep stack deduplication instead of elision This hopefully will avoid any potential ..stacktraceon../off.. scoring issues, and will be more useful for users. The downside is that it's still possible to have uselessly large deep stack traces, but at least that will only happen now if you have manually written gigantic async/promise chains by hand or maybe did some clever metaprogramming. The coro case should be fine. * Add coro-based unit test * Use rlang::hash, it's much faster * typo Co-authored-by: Carson Sievert <cpsievert1@gmail.com> * Remove unnecessary logic * Simplify/robustify reactlog version checking test * Warn only once on call stack digest cache miss * Super conservatively wrap appendCallStackWithDupe in try/catch * Use more specific attribute name Co-authored-by: Carson Sievert <cpsievert1@gmail.com> * Remove excessively cautious try/catch --------- Co-authored-by: Carson Sievert <cpsievert1@gmail.com>
138 lines
4.1 KiB
R
138 lines
4.1 KiB
R
keyValList <- function(key, value) {
|
|
ret <- list()
|
|
ret[[key]] <- value
|
|
ret
|
|
}
|
|
withOption <- function(key, value, oldVal = NULL, expr) {
|
|
oldVal <- getOption(key, oldVal)
|
|
do.call("options", keyValList(key, value))
|
|
on.exit({
|
|
do.call("options", keyValList(key, oldVal))
|
|
})
|
|
force(expr)
|
|
}
|
|
|
|
withLogging <- function(expr) {
|
|
|
|
rLog$reset()
|
|
|
|
# reset ctx counter
|
|
reactiveEnvr <- .getReactiveEnvironment()
|
|
reactiveEnvr$.nextId <- 0L
|
|
|
|
withOption("shiny.reactlog", TRUE, FALSE, {
|
|
withOption("shiny.reactlog.console", TRUE, FALSE, {
|
|
withOption("shiny.suppressMissingContextError", TRUE, FALSE, {
|
|
force(expr)
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
expect_logs <- function(expr, ...) {
|
|
expected_messages <- unlist(list(...))
|
|
captured_messages <- capture_messages(expr)
|
|
captured_messages <- sub("\n$", "", captured_messages)
|
|
if (length(captured_messages) != length(expected_messages)) {
|
|
cat("\nCaptured: \n"); print(captured_messages)
|
|
cat("Expected: \n"); print(expected_messages)
|
|
}
|
|
expect_equal(
|
|
captured_messages,
|
|
expected_messages
|
|
)
|
|
}
|
|
|
|
test_that("rLog resets when options are FALSE", {
|
|
|
|
withOption("shiny.reactlog", FALSE, FALSE, {
|
|
withOption("shiny.reactlog.console", FALSE, FALSE, {
|
|
rLog$reset()
|
|
|
|
# check for dummy and no reactid information
|
|
expect_true(!is.null(rLog$noReactId))
|
|
expect_true(!is.null(rLog$dummyReactId))
|
|
expect_equal(rLog$msg$getReact(rLog$noReactId, force = TRUE)$reactId, rLog$noReactId)
|
|
expect_equal(rLog$msg$getReact(rLog$dummyReactId, force = TRUE)$reactId, rLog$dummyReactId)
|
|
expect_equal(length(rLog$msg$reactCache), 2)
|
|
})
|
|
})
|
|
|
|
})
|
|
|
|
test_that("message logger appears", {
|
|
|
|
withLogging({
|
|
|
|
expect_logs(
|
|
{
|
|
val <- reactiveVal(1, label = "val")
|
|
},
|
|
"- define: r1:'val' - reactiveVal ' num 1'"
|
|
)
|
|
expect_silent(
|
|
{
|
|
values <- reactiveValues(a = 2, b = 3)
|
|
local({
|
|
values_obj <- .subset2(values, 'impl')
|
|
values_obj$.label <- "values"
|
|
})
|
|
}
|
|
)
|
|
expect_logs(
|
|
{
|
|
react <- reactive(val() + values$a)
|
|
},
|
|
"- define: r3:'reactive({\\n val() + values$a\\n})' - observable ' NULL'"
|
|
)
|
|
|
|
expect_logs(
|
|
{
|
|
react()
|
|
},
|
|
"- createContext: ctxDummy - isolate",
|
|
"- dependsOn: rDummyReactId:'DummyReactId' on r3:'reactive({\\n val() + values$a\\n})' in ctxDummy",
|
|
"- createContext: ctx1 - observable",
|
|
"- enter: r3:'reactive({\\n val() + values$a\\n})' in ctx1 - observable",
|
|
"= - dependsOn: r3:'reactive({\\n val() + values$a\\n})' on r1:'val' in ctx1",
|
|
"= - define: r2$a:'values$a' - reactiveValuesKey ' num 2'",
|
|
"= - dependsOn: r3:'reactive({\\n val() + values$a\\n})' on r2$a:'values$a' in ctx1",
|
|
"- exit: r3:'reactive({\\n val() + values$a\\n})' in ctx1 - observable"
|
|
)
|
|
|
|
expect_logs(
|
|
{
|
|
val(4)
|
|
},
|
|
"- valueChange: r1:'val' ' num 4'",
|
|
"- invalidateStart: r1:'val'",
|
|
"= - invalidateStart: r3:'reactive({\\n val() + values$a\\n})' in ctx1 - observable",
|
|
"= = - isolateInvalidateStart: rDummyReactId:'DummyReactId' in ctxDummy",
|
|
"= = = - dependsOnRemove: rDummyReactId:'DummyReactId' on r3:'reactive({\\n val() + values$a\\n})' in ctxDummy",
|
|
"= = - isolateInvalidateEnd: rDummyReactId:'DummyReactId' in ctxDummy",
|
|
"= = - dependsOnRemove: r3:'reactive({\\n val() + values$a\\n})' on r1:'val' in ctx1",
|
|
"= = - dependsOnRemove: r3:'reactive({\\n val() + values$a\\n})' on r2$a:'values$a' in ctx1",
|
|
"= - invalidateEnd: r3:'reactive({\\n val() + values$a\\n})' in ctx1 - observable",
|
|
"- invalidateEnd: r1:'val'"
|
|
)
|
|
|
|
expect_logs(
|
|
{values$a <- 5},
|
|
"- valueChange: r2$a:'values$a' ' num 5'",
|
|
"- invalidateStart: r2$a:'values$a'",
|
|
"- invalidateEnd: r2$a:'values$a'"
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
test_that("reactlog_version is as expected", {
|
|
expect_match(
|
|
packageDescription("shiny")$Suggests,
|
|
# The space between reactlog and the version number can include \n
|
|
sprintf("\\breactlog\\s+\\Q(>= %s)\\E", reactlog_min_version)
|
|
)
|
|
})
|