fix(otel): Duplicate otel code attribute keys using both deprecated and preferred names (#4325)

This commit is contained in:
Barret Schloerke
2025-12-03 16:37:20 -05:00
committed by GitHub
parent 5a946caf35
commit 63a00f775f
9 changed files with 306 additions and 140 deletions

View File

@@ -1,5 +1,6 @@
# shiny (development version) # shiny (development version)
* OpenTelemetry code attributes now include both preferred (`code.file.path`, `code.line.number`, `code.column.number`) and deprecated (`code.filepath`, `code.lineno`, `code.column`) attribute names to follow OpenTelemetry semantic conventions while maintaining backward compatibility. The deprecated names will be removed in a future release after Logfire supports the preferred names. (#4325)
* Timer tests are now skipped on CRAN. (#4327) * Timer tests are now skipped on CRAN. (#4327)
# shiny 1.12.0 # shiny 1.12.0

View File

@@ -520,7 +520,7 @@ bindCache.reactiveExpr <- function(x, ..., cache = "app") {
local({ local({
impl <- attr(res, "observable", exact = TRUE) impl <- attr(res, "observable", exact = TRUE)
impl$.otelAttrs <- append_otel_srcref_attrs(x_otel_attrs, call_srcref) impl$.otelAttrs <- append_otel_srcref_attrs(x_otel_attrs, call_srcref, fn_name = "bindCache")
}) })
if (has_otel_collect("reactivity")) { if (has_otel_collect("reactivity")) {

View File

@@ -240,7 +240,7 @@ bindEvent.reactiveExpr <- function(x, ..., ignoreNULL = TRUE, ignoreInit = FALSE
local({ local({
impl <- attr(res, "observable", exact = TRUE) impl <- attr(res, "observable", exact = TRUE)
impl$.otelAttrs <- append_otel_srcref_attrs(x_otel_attrs, call_srcref) impl$.otelAttrs <- append_otel_srcref_attrs(x_otel_attrs, call_srcref, fn_name = "bindEvent")
}) })
@@ -341,7 +341,7 @@ bindEvent.Observer <- function(x, ..., ignoreNULL = TRUE, ignoreInit = FALSE,
class(x) <- c("Observer.event", class(x)) class(x) <- c("Observer.event", class(x))
call_srcref <- get_call_srcref(-1) call_srcref <- get_call_srcref(-1)
x$.otelAttrs <- append_otel_srcref_attrs(x$.otelAttrs, call_srcref) x$.otelAttrs <- append_otel_srcref_attrs(x$.otelAttrs, call_srcref, fn_name = "bindEvent")
if (has_otel_collect("reactivity")) { if (has_otel_collect("reactivity")) {
x <- enable_otel_observe(x) x <- enable_otel_observe(x)

View File

@@ -140,7 +140,7 @@ ExtendedTask <- R6Class("ExtendedTask", portable = TRUE, cloneable = FALSE,
private$otel_log_label_add_to_queue <- otel_log_label_extended_task_add_to_queue(label, domain = domain) private$otel_log_label_add_to_queue <- otel_log_label_extended_task_add_to_queue(label, domain = domain)
private$otel_attrs <- c( private$otel_attrs <- c(
otel_srcref_attributes(call_srcref), otel_srcref_attributes(call_srcref, "ExtendedTask"),
otel_session_id_attrs(domain) otel_session_id_attrs(domain)
) %||% list() ) %||% list()
}, },

View File

@@ -2,7 +2,7 @@
# Very similar to srcrefFromShinyCall(), # Very similar to srcrefFromShinyCall(),
# however, this works when the function does not have a srcref attr set # however, this works when the function does not have a srcref attr set
otel_srcref_attributes <- function(srcref) { otel_srcref_attributes <- function(srcref, fn_name = NULL) {
if (is.function(srcref)) { if (is.function(srcref)) {
srcref <- getSrcRefs(srcref)[[1]][[1]] srcref <- getSrcRefs(srcref)[[1]][[1]]
} }
@@ -16,8 +16,16 @@ otel_srcref_attributes <- function(srcref) {
# Semantic conventions for code: https://opentelemetry.io/docs/specs/semconv/registry/attributes/code/ # Semantic conventions for code: https://opentelemetry.io/docs/specs/semconv/registry/attributes/code/
# #
# Inspiration from https://github.com/r-lib/testthat/pull/2087/files#diff-92de3306849d93d6f7e76c5aaa1b0c037e2d716f72848f8a1c70536e0c8a1564R123-R124 # Inspiration from https://github.com/r-lib/testthat/pull/2087/files#diff-92de3306849d93d6f7e76c5aaa1b0c037e2d716f72848f8a1c70536e0c8a1564R123-R124
filename <- attr(srcref, "srcfile")$filename
dropNulls(list( dropNulls(list(
"code.filepath" = attr(srcref, "srcfile")$filename, "code.function.name" = fn_name,
# Location attrs
"code.file.path" = filename,
"code.line.number" = srcref[1],
"code.column.number" = srcref[2],
# Remove these deprecated location names once Logfire supports the preferred names
# https://github.com/pydantic/logfire/issues/1559
"code.filepath" = filename,
"code.lineno" = srcref[1], "code.lineno" = srcref[1],
"code.column" = srcref[2] "code.column" = srcref[2]
)) ))
@@ -41,12 +49,12 @@ get_call_srcref <- function(which_offset = 0) {
} }
append_otel_srcref_attrs <- function(attrs, call_srcref) { append_otel_srcref_attrs <- function(attrs, call_srcref, fn_name) {
if (is.null(call_srcref)) { if (is.null(call_srcref)) {
return(attrs) return(attrs)
} }
srcref_attrs <- otel_srcref_attributes(call_srcref) srcref_attrs <- otel_srcref_attributes(call_srcref, fn_name)
if (is.null(srcref_attrs)) { if (is.null(srcref_attrs)) {
return(attrs) return(attrs)
} }

View File

@@ -231,7 +231,7 @@ reactiveVal <- function(value = NULL, label = NULL) {
rv <- ReactiveVal$new(value, label) rv <- ReactiveVal$new(value, label)
if (!is.null(call_srcref)) { if (!is.null(call_srcref)) {
rv$.otelAttrs <- otel_srcref_attributes(call_srcref) rv$.otelAttrs <- otel_srcref_attributes(call_srcref, fn_name = "reactiveVal")
} }
ret <- structure( ret <- structure(
@@ -646,7 +646,7 @@ reactiveValues <- function(...) {
defaultLabel = impl$.label defaultLabel = impl$.label
) )
impl$.otelAttrs <- otel_srcref_attributes(call_srcref) impl$.otelAttrs <- otel_srcref_attributes(call_srcref, fn_name = "reactiveValues")
} }
impl$mset(args) impl$mset(args)
@@ -1130,7 +1130,7 @@ reactive <- function(
call_srcref <- get_call_srcref() call_srcref <- get_call_srcref()
if (!is.null(call_srcref)) { if (!is.null(call_srcref)) {
o$.otelAttrs <- otel_srcref_attributes(call_srcref) o$.otelAttrs <- otel_srcref_attributes(call_srcref, fn_name = "reactive")
} }
ret <- structure( ret <- structure(
@@ -1587,7 +1587,7 @@ observe <- function(
..stacktraceon = ..stacktraceon ..stacktraceon = ..stacktraceon
) )
if (!is.null(call_srcref)) { if (!is.null(call_srcref)) {
o$.otelAttrs <- otel_srcref_attributes(call_srcref) o$.otelAttrs <- otel_srcref_attributes(call_srcref, fn_name = "observe")
} }
if (has_otel_collect("reactivity")) { if (has_otel_collect("reactivity")) {
@@ -2055,7 +2055,7 @@ reactive_poll_impl <- function(
otel_label_reactive_poll(label, domain = impl$.domain) otel_label_reactive_poll(label, domain = impl$.domain)
else if (fnName == "reactiveFileReader") else if (fnName == "reactiveFileReader")
otel_label_reactive_file_reader(label, domain = impl$.domain) otel_label_reactive_file_reader(label, domain = impl$.domain)
impl$.otelAttrs <- append_otel_srcref_attrs(impl$.otelAttrs, call_srcref) impl$.otelAttrs <- append_otel_srcref_attrs(impl$.otelAttrs, call_srcref, fn_name = fnName)
}) })
return(re) return(re)
@@ -2522,7 +2522,7 @@ observeEvent <- function(eventExpr, handlerExpr,
}) })
if (!is.null(call_srcref)) { if (!is.null(call_srcref)) {
o$.otelAttrs <- otel_srcref_attributes(call_srcref) o$.otelAttrs <- otel_srcref_attributes(call_srcref, fn_name = "observeEvent")
} }
if (has_otel_collect("reactivity")) { if (has_otel_collect("reactivity")) {
o <- enable_otel_observe(o) o <- enable_otel_observe(o)
@@ -2571,7 +2571,7 @@ eventReactive <- function(eventExpr, valueExpr,
if (!is.null(call_srcref)) { if (!is.null(call_srcref)) {
impl <- attr(r, "observable", exact = TRUE) impl <- attr(r, "observable", exact = TRUE)
impl$.otelAttrs <- otel_srcref_attributes(call_srcref) impl$.otelAttrs <- otel_srcref_attributes(call_srcref, fn_name = "eventReactive")
} }
if (has_otel_collect("reactivity")) { if (has_otel_collect("reactivity")) {
r <- enable_otel_reactive_expr(r) r <- enable_otel_reactive_expr(r)
@@ -2778,7 +2778,7 @@ debounce <- function(r, millis, priority = 100, domain = getDefaultReactiveDomai
local({ local({
er_impl <- attr(er, "observable", exact = TRUE) er_impl <- attr(er, "observable", exact = TRUE)
er_impl$.otelLabel <- otel_label_debounce(label, domain = domain) er_impl$.otelLabel <- otel_label_debounce(label, domain = domain)
er_impl$.otelAttrs <- append_otel_srcref_attrs(er_impl$.otelAttrs, call_srcref) er_impl$.otelAttrs <- append_otel_srcref_attrs(er_impl$.otelAttrs, call_srcref, fn_name = "debounce")
}) })
with_no_otel_collect({ with_no_otel_collect({
@@ -2877,7 +2877,7 @@ throttle <- function(r, millis, priority = 100, domain = getDefaultReactiveDomai
local({ local({
er_impl <- attr(er, "observable", exact = TRUE) er_impl <- attr(er, "observable", exact = TRUE)
er_impl$.otelLabel <- otel_label_throttle(label, domain = domain) er_impl$.otelLabel <- otel_label_throttle(label, domain = domain)
er_impl$.otelAttrs <- append_otel_srcref_attrs(er_impl$.otelAttrs, call_srcref) er_impl$.otelAttrs <- append_otel_srcref_attrs(er_impl$.otelAttrs, call_srcref, fn_name = "throttle")
}) })
er er

View File

@@ -136,7 +136,9 @@ markRenderFunction <- function(
otelAttrs <- otelAttrs <-
otel_srcref_attributes( otel_srcref_attributes(
attr(renderFunc, "wrappedFunc", exact = TRUE) attr(renderFunc, "wrappedFunc", exact = TRUE),
# Can't retrieve the render function used at this point, so just use NULL
fn_name = NULL
) )
ret <- structure( ret <- structure(

View File

@@ -101,9 +101,30 @@ test_that("otel_srcref_attributes extracts attributes from srcref object", {
attrs <- otel_srcref_attributes(srcref) attrs <- otel_srcref_attributes(srcref)
# Preferred attribute names
expect_equal(attrs[["code.file.path"]], "/path/to/myfile.R")
expect_equal(attrs[["code.line.number"]], 15)
expect_equal(attrs[["code.column.number"]], 8)
expect_false("code.function.name" %in% names(attrs))
# Deprecated attribute names (for backward compatibility)
expect_equal(attrs[["code.filepath"]], "/path/to/myfile.R") expect_equal(attrs[["code.filepath"]], "/path/to/myfile.R")
expect_equal(attrs[["code.lineno"]], 15) expect_equal(attrs[["code.lineno"]], 15)
expect_equal(attrs[["code.column"]], 8) expect_equal(attrs[["code.column"]], 8)
# Test with function name
attrs_with_fn <- otel_srcref_attributes(srcref, fn_name = "myFunction")
# Preferred names
expect_equal(attrs_with_fn[["code.file.path"]], "/path/to/myfile.R")
expect_equal(attrs_with_fn[["code.line.number"]], 15)
expect_equal(attrs_with_fn[["code.column.number"]], 8)
expect_equal(attrs_with_fn[["code.function.name"]], "myFunction")
# Deprecated names
expect_equal(attrs_with_fn[["code.filepath"]], "/path/to/myfile.R")
expect_equal(attrs_with_fn[["code.lineno"]], 15)
expect_equal(attrs_with_fn[["code.column"]], 8)
}) })
test_that("otel_srcref_attributes handles NULL srcref", { test_that("otel_srcref_attributes handles NULL srcref", {
@@ -127,9 +148,21 @@ test_that("otel_srcref_attributes extracts from function with srcref", {
{ {
attrs <- otel_srcref_attributes(mock_func) attrs <- otel_srcref_attributes(mock_func)
expect_equal(attrs[["code.filepath"]], "function_file.R") expect_equal(attrs[["code.file.path"]], "function_file.R")
expect_equal(attrs[["code.lineno"]], 42) expect_equal(attrs[["code.line.number"]], 42)
expect_equal(attrs[["code.column"]], 12) expect_equal(attrs[["code.column.number"]], 12)
expect_false("code.function.name" %in% names(attrs))
# Test with function name
attrs_with_fn <- otel_srcref_attributes(
mock_func,
fn_name = "testFunction"
)
expect_equal(attrs_with_fn[["code.file.path"]], "function_file.R")
expect_equal(attrs_with_fn[["code.line.number"]], 42)
expect_equal(attrs_with_fn[["code.column.number"]], 12)
expect_equal(attrs_with_fn[["code.function.name"]], "testFunction")
} }
) )
}) })
@@ -186,11 +219,27 @@ test_that("otel_srcref_attributes drops NULL values", {
attrs <- otel_srcref_attributes(srcref) attrs <- otel_srcref_attributes(srcref)
# Should only contain lineno and column, not filepath # Should only contain lineno and column (both preferred and deprecated)
expect_equal(length(attrs), 2) expect_equal(length(attrs), 4) # 2 preferred + 2 deprecated
# Preferred names
expect_equal(attrs[["code.line.number"]], 10)
expect_equal(attrs[["code.column.number"]], 5)
expect_false("code.file.path" %in% names(attrs))
expect_false("code.function.name" %in% names(attrs))
# Deprecated names
expect_equal(attrs[["code.lineno"]], 10) expect_equal(attrs[["code.lineno"]], 10)
expect_equal(attrs[["code.column"]], 5) expect_equal(attrs[["code.column"]], 5)
expect_false("code.filepath" %in% names(attrs)) expect_false("code.filepath" %in% names(attrs))
# Test with function name - NULL fn_name should still be dropped
attrs_with_null_fn <- otel_srcref_attributes(srcref, fn_name = NULL)
expect_equal(length(attrs_with_null_fn), 4)
expect_false("code.function.name" %in% names(attrs_with_null_fn))
# Test with function name provided
attrs_with_fn <- otel_srcref_attributes(srcref, fn_name = "testFunc")
expect_equal(length(attrs_with_fn), 5) # 4 location + 1 function name
expect_equal(attrs_with_fn[["code.function.name"]], "testFunc")
}) })
test_that("otel_srcref_attributes handles missing srcfile", { test_that("otel_srcref_attributes handles missing srcfile", {
@@ -202,8 +251,13 @@ test_that("otel_srcref_attributes handles missing srcfile", {
attrs <- otel_srcref_attributes(srcref) attrs <- otel_srcref_attributes(srcref)
# Should only contain lineno and column # Should only contain lineno and column (both preferred and deprecated)
expect_equal(length(attrs), 2) expect_equal(length(attrs), 4) # 2 preferred + 2 deprecated
# Preferred names
expect_equal(attrs[["code.line.number"]], 10)
expect_equal(attrs[["code.column.number"]], 5)
expect_false("code.file.path" %in% names(attrs))
# Deprecated names
expect_equal(attrs[["code.lineno"]], 10) expect_equal(attrs[["code.lineno"]], 10)
expect_equal(attrs[["code.column"]], 5) expect_equal(attrs[["code.column"]], 5)
expect_false("code.filepath" %in% names(attrs)) expect_false("code.filepath" %in% names(attrs))
@@ -217,9 +271,10 @@ test_that("reactive() captures otel attributes from source reference", {
x <- get_reactive_objects()$reactive x <- get_reactive_objects()$reactive
attrs <- attr(x, "observable")$.otelAttrs attrs <- attr(x, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_equal(attrs[["code.lineno"]], 4) expect_equal(attrs[["code.line.number"]], 4)
expect_equal(attrs[["code.column"]], 3) expect_equal(attrs[["code.column.number"]], 3)
expect_equal(attrs[["code.function.name"]], "reactive")
}) })
test_that("reactiveVal() captures otel attributes from source reference", { test_that("reactiveVal() captures otel attributes from source reference", {
@@ -228,9 +283,10 @@ test_that("reactiveVal() captures otel attributes from source reference", {
# Test the attribute extraction that would be used in reactiveVal # Test the attribute extraction that would be used in reactiveVal
attrs <- attr(x, ".impl")$.otelAttrs attrs <- attr(x, ".impl")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_equal(attrs[["code.lineno"]], 5) expect_equal(attrs[["code.line.number"]], 5)
expect_equal(attrs[["code.column"]], 3) expect_equal(attrs[["code.column.number"]], 3)
expect_equal(attrs[["code.function.name"]], "reactiveVal")
}) })
test_that("reactiveValues() captures otel attributes from source reference", { test_that("reactiveValues() captures otel attributes from source reference", {
@@ -238,36 +294,41 @@ test_that("reactiveValues() captures otel attributes from source reference", {
attrs <- .subset2(x, "impl")$.otelAttrs attrs <- .subset2(x, "impl")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_equal(attrs[["code.lineno"]], 6) expect_equal(attrs[["code.line.number"]], 6)
expect_equal(attrs[["code.column"]], 3) expect_equal(attrs[["code.column.number"]], 3)
expect_equal(attrs[["code.function.name"]], "reactiveValues")
}) })
test_that("observe() captures otel attributes from source reference", { test_that("observe() captures otel attributes from source reference", {
x <- get_reactive_objects()$observe x <- get_reactive_objects()$observe
attrs <- x$.otelAttrs attrs <- x$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_equal(attrs[["code.lineno"]], 7) expect_equal(attrs[["code.line.number"]], 7)
expect_equal(attrs[["code.column"]], 3) expect_equal(attrs[["code.column.number"]], 3)
expect_equal(attrs[["code.function.name"]], "observe")
}) })
test_that("otel attributes integration with render functions", { test_that("otel attributes integration with render functions", {
x <- get_reactive_objects()$renderText x <- get_reactive_objects()$renderText
attrs <- attr(x, "otelAttrs") attrs <- attr(x, "otelAttrs")
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_equal(attrs[["code.lineno"]], 8) expect_equal(attrs[["code.line.number"]], 8)
expect_equal(attrs[["code.column"]], 20) expect_equal(attrs[["code.column.number"]], 20)
# Render functions should NOT have code.function.name
expect_false("code.function.name" %in% names(attrs))
}) })
test_that("observeEvent() captures otel attributes from source reference", { test_that("observeEvent() captures otel attributes from source reference", {
x <- get_reactive_objects()$observeEvent x <- get_reactive_objects()$observeEvent
attrs <- x$.otelAttrs attrs <- x$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_equal(attrs[["code.lineno"]], 9) expect_equal(attrs[["code.line.number"]], 9)
expect_equal(attrs[["code.column"]], 3) expect_equal(attrs[["code.column.number"]], 3)
expect_equal(attrs[["code.function.name"]], "observeEvent")
}) })
test_that("otel attributes follow OpenTelemetry semantic conventions", { test_that("otel attributes follow OpenTelemetry semantic conventions", {
@@ -282,15 +343,33 @@ test_that("otel attributes follow OpenTelemetry semantic conventions", {
attrs <- otel_srcref_attributes(srcref) attrs <- otel_srcref_attributes(srcref)
# Check that attribute names follow the convention # Check that preferred attribute names follow the convention
expect_true("code.file.path" %in% names(attrs))
expect_true("code.line.number" %in% names(attrs))
expect_true("code.column.number" %in% names(attrs))
expect_false("code.function.name" %in% names(attrs))
# Check that deprecated names are also present
expect_true("code.filepath" %in% names(attrs)) expect_true("code.filepath" %in% names(attrs))
expect_true("code.lineno" %in% names(attrs)) expect_true("code.lineno" %in% names(attrs))
expect_true("code.column" %in% names(attrs)) expect_true("code.column" %in% names(attrs))
# Check that values are of correct types # Check that values are of correct types (preferred names)
expect_true(is.character(attrs[["code.filepath"]])) expect_true(is.character(attrs[["code.file.path"]]))
expect_true(is.numeric(attrs[["code.lineno"]])) expect_true(is.numeric(attrs[["code.line.number"]]))
expect_true(is.numeric(attrs[["code.column"]])) expect_true(is.numeric(attrs[["code.column.number"]]))
# Check that deprecated names have same values
expect_equal(attrs[["code.file.path"]], attrs[["code.filepath"]])
expect_equal(attrs[["code.line.number"]], attrs[["code.lineno"]])
expect_equal(attrs[["code.column.number"]], attrs[["code.column"]])
# Test with function name
attrs_with_fn <- otel_srcref_attributes(srcref, fn_name = "myFunc")
expect_true("code.function.name" %in% names(attrs_with_fn))
expect_true(is.character(attrs_with_fn[["code.function.name"]]))
expect_equal(attrs_with_fn[["code.function.name"]], "myFunc")
}) })
test_that("dropNulls helper works correctly in otel_srcref_attributes", { test_that("dropNulls helper works correctly in otel_srcref_attributes", {
@@ -302,7 +381,7 @@ test_that("dropNulls helper works correctly in otel_srcref_attributes", {
) )
attrs <- otel_srcref_attributes(srcref) attrs <- otel_srcref_attributes(srcref)
expect_equal(length(attrs), 3) expect_equal(length(attrs), 6) # 3 preferred + 3 deprecated
# Test with missing filename (NULL) # Test with missing filename (NULL)
srcref_no_file <- structure( srcref_no_file <- structure(
@@ -312,16 +391,17 @@ test_that("dropNulls helper works correctly in otel_srcref_attributes", {
attr(srcref_no_file, "srcfile") <- list(filename = NULL) attr(srcref_no_file, "srcfile") <- list(filename = NULL)
attrs_no_file <- otel_srcref_attributes(srcref_no_file) attrs_no_file <- otel_srcref_attributes(srcref_no_file)
expect_equal(length(attrs_no_file), 2) expect_equal(length(attrs_no_file), 4) # 2 preferred + 2 deprecated
expect_false("code.file.path" %in% names(attrs_no_file))
expect_false("code.filepath" %in% names(attrs_no_file)) expect_false("code.filepath" %in% names(attrs_no_file))
}) })
test_that("otel attributes are used in reactive context execution", { test_that("otel attributes are used in reactive context execution", {
# Test that otel attributes are properly passed through to spans # Test that otel attributes are properly passed through to spans
mock_attrs <- list( mock_attrs <- list(
"code.filepath" = "context_test.R", "code.file.path" = "context_test.R",
"code.lineno" = 42L, "code.line.number" = 42L,
"code.column" = 8L "code.column.number" = 8L
) )
# Test the context info structure used in react.R # Test the context info structure used in react.R
@@ -342,9 +422,9 @@ test_that("otel attributes are combined with session attributes", {
# as happens in the reactive system # as happens in the reactive system
srcref_attrs <- list( srcref_attrs <- list(
"code.filepath" = "session_test.R", "code.file.path" = "session_test.R",
"code.lineno" = 15L, "code.line.number" = 15L,
"code.column" = 5L "code.column.number" = 5L
) )
session_attrs <- list( session_attrs <- list(
@@ -355,8 +435,8 @@ test_that("otel attributes are combined with session attributes", {
combined_attrs <- c(srcref_attrs, session_attrs) combined_attrs <- c(srcref_attrs, session_attrs)
expect_equal(length(combined_attrs), 4) expect_equal(length(combined_attrs), 4)
expect_equal(combined_attrs[["code.filepath"]], "session_test.R") expect_equal(combined_attrs[["code.file.path"]], "session_test.R")
expect_equal(combined_attrs[["code.lineno"]], 15L) expect_equal(combined_attrs[["code.line.number"]], 15L)
expect_equal(combined_attrs[["session.id"]], "test-session-123") expect_equal(combined_attrs[["session.id"]], "test-session-123")
}) })
@@ -364,25 +444,28 @@ test_that("eventReactive() captures otel attributes from source reference", {
x <- get_reactive_objects()$eventReactive x <- get_reactive_objects()$eventReactive
attrs <- attr(x, "observable")$.otelAttrs attrs <- attr(x, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_equal(attrs[["code.lineno"]], 10) expect_equal(attrs[["code.line.number"]], 10)
expect_equal(attrs[["code.column"]], 3) expect_equal(attrs[["code.column.number"]], 3)
expect_equal(attrs[["code.function.name"]], "eventReactive")
}) })
test_that("renderText() with bindCache() captures otel attributes", { test_that("renderText() with bindCache() captures otel attributes", {
x <- get_reactive_objects()$renderCacheA x <- get_reactive_objects()$renderCacheA
attrs <- attr(x, "otelAttrs") attrs <- attr(x, "otelAttrs")
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_false("code.function.name" %in% names(attrs))
}) })
test_that("renderText() with bindEvent() captures otel attributes", { test_that("renderText() with bindEvent() captures otel attributes", {
x <- get_reactive_objects()$renderEventA x <- get_reactive_objects()$renderEventA
attrs <- attr(x, "otelAttrs") attrs <- attr(x, "otelAttrs")
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_false("code.function.name" %in% names(attrs))
}) })
test_that( test_that(
@@ -391,8 +474,9 @@ test_that(
x <- get_reactive_objects()$renderCacheEventA x <- get_reactive_objects()$renderCacheEventA
attrs <- attr(x, "otelAttrs") attrs <- attr(x, "otelAttrs")
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_false("code.function.name" %in% names(attrs))
} }
) )
@@ -400,16 +484,18 @@ test_that("bindCache() wrapping renderText() captures otel attributes", {
x <- get_reactive_objects()$renderCacheB x <- get_reactive_objects()$renderCacheB
attrs <- attr(x, "otelAttrs") attrs <- attr(x, "otelAttrs")
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_false("code.function.name" %in% names(attrs))
}) })
test_that("bindEvent() wrapping renderText() captures otel attributes", { test_that("bindEvent() wrapping renderText() captures otel attributes", {
x <- get_reactive_objects()$renderEventB x <- get_reactive_objects()$renderEventB
attrs <- attr(x, "otelAttrs") attrs <- attr(x, "otelAttrs")
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_false("code.function.name" %in% names(attrs))
}) })
test_that( test_that(
@@ -418,8 +504,9 @@ test_that(
x <- get_reactive_objects()$renderCacheEventB x <- get_reactive_objects()$renderCacheEventB
attrs <- attr(x, "otelAttrs") attrs <- attr(x, "otelAttrs")
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_false("code.function.name" %in% names(attrs))
} }
) )
@@ -427,32 +514,36 @@ test_that("observe() with bindEvent() captures otel attributes", {
x <- get_reactive_objects()$observeEventA x <- get_reactive_objects()$observeEventA
attrs <- x$.otelAttrs attrs <- x$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "bindEvent")
}) })
test_that("bindEvent() wrapping observe() captures otel attributes", { test_that("bindEvent() wrapping observe() captures otel attributes", {
x <- get_reactive_objects()$observeEventB x <- get_reactive_objects()$observeEventB
attrs <- x$.otelAttrs attrs <- x$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "bindEvent")
}) })
test_that("reactive() with bindCache() captures otel attributes", { test_that("reactive() with bindCache() captures otel attributes", {
x <- get_reactive_objects()$reactiveCacheA x <- get_reactive_objects()$reactiveCacheA
attrs <- attr(x, "observable")$.otelAttrs attrs <- attr(x, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "bindCache")
}) })
test_that("reactive() with bindEvent() captures otel attributes", { test_that("reactive() with bindEvent() captures otel attributes", {
x <- get_reactive_objects()$reactiveEventA x <- get_reactive_objects()$reactiveEventA
attrs <- attr(x, "observable")$.otelAttrs attrs <- attr(x, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "bindEvent")
}) })
test_that( test_that(
@@ -461,8 +552,9 @@ test_that(
x <- get_reactive_objects()$reactiveCacheEventA x <- get_reactive_objects()$reactiveCacheEventA
attrs <- attr(x, "observable")$.otelAttrs attrs <- attr(x, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "bindEvent")
} }
) )
@@ -470,16 +562,18 @@ test_that("bindCache() wrapping reactive() captures otel attributes", {
x <- get_reactive_objects()$reactiveCacheB x <- get_reactive_objects()$reactiveCacheB
attrs <- attr(x, "observable")$.otelAttrs attrs <- attr(x, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "bindCache")
}) })
test_that("bindEvent() wrapping reactive() captures otel attributes", { test_that("bindEvent() wrapping reactive() captures otel attributes", {
x <- get_reactive_objects()$reactiveEventB x <- get_reactive_objects()$reactiveEventB
attrs <- attr(x, "observable")$.otelAttrs attrs <- attr(x, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "bindEvent")
}) })
test_that( test_that(
@@ -488,8 +582,9 @@ test_that(
x <- get_reactive_objects()$reactiveCacheEventB x <- get_reactive_objects()$reactiveCacheEventB
attrs <- attr(x, "observable")$.otelAttrs attrs <- attr(x, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "bindEvent")
} }
) )
@@ -498,16 +593,18 @@ test_that("debounce() creates new reactive with otel attributes", {
x <- get_reactive_objects()$debounce x <- get_reactive_objects()$debounce
attrs <- attr(x, "observable")$.otelAttrs attrs <- attr(x, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "debounce")
}) })
test_that("throttle() creates new reactive with otel attributes", { test_that("throttle() creates new reactive with otel attributes", {
x <- get_reactive_objects()$throttle x <- get_reactive_objects()$throttle
attrs <- attr(x, "observable")$.otelAttrs attrs <- attr(x, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "throttle")
}) })
# Tests for ExtendedTask # Tests for ExtendedTask
@@ -518,8 +615,9 @@ test_that("ExtendedTask is created and is an R6 object", {
attrs <- .subset2(x, ".__enclos_env__")$private$otel_attrs attrs <- .subset2(x, ".__enclos_env__")$private$otel_attrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "ExtendedTask")
}) })
# Tests for reactivePoll # Tests for reactivePoll
@@ -531,8 +629,9 @@ test_that("reactivePoll() captures otel attributes from source reference", {
expect_equal(as.character(otelLabel), "reactivePoll r_poll") expect_equal(as.character(otelLabel), "reactivePoll r_poll")
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "reactivePoll")
}) })
# Tests for reactiveFileReader # Tests for reactiveFileReader
@@ -544,8 +643,9 @@ test_that("reactiveFileReader() captures otel attributes from source reference",
expect_equal(as.character(otelLabel), "reactiveFileReader r_file") expect_equal(as.character(otelLabel), "reactiveFileReader r_file")
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_gt(attrs[["code.lineno"]], 12) expect_gt(attrs[["code.line.number"]], 12)
expect_equal(attrs[["code.function.name"]], "reactiveFileReader")
}) })
# Tests for explicit labels # Tests for explicit labels
@@ -553,9 +653,10 @@ test_that("reactive() with explicit label still captures otel attributes", {
x <- get_reactive_objects()$reactiveLabeled x <- get_reactive_objects()$reactiveLabeled
attrs <- attr(x, "observable")$.otelAttrs attrs <- attr(x, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_equal(attrs[["code.lineno"]], 38) expect_equal(attrs[["code.line.number"]], 38)
expect_equal(attrs[["code.column"]], 3) expect_equal(attrs[["code.column.number"]], 3)
expect_equal(attrs[["code.function.name"]], "reactive")
# Verify label is preserved # Verify label is preserved
label <- attr(x, "observable")$.label label <- attr(x, "observable")$.label
@@ -566,9 +667,10 @@ test_that("observe() with explicit label still captures otel attributes", {
x <- get_reactive_objects()$observeLabeled x <- get_reactive_objects()$observeLabeled
attrs <- x$.otelAttrs attrs <- x$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_equal(attrs[["code.lineno"]], 39) expect_equal(attrs[["code.line.number"]], 39)
expect_equal(attrs[["code.column"]], 3) expect_equal(attrs[["code.column.number"]], 3)
expect_equal(attrs[["code.function.name"]], "observe")
# Verify label is preserved # Verify label is preserved
expect_equal(x$.label, "my_observer") expect_equal(x$.label, "my_observer")
@@ -583,10 +685,10 @@ test_that("reactive created inside function captures function srcref", {
r <- create_reactive() r <- create_reactive()
attrs <- attr(r, "observable")$.otelAttrs attrs <- attr(r, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
# Line number should point to where reactive() is called inside the function # Line number should point to where reactive() is called inside the function
expect_true(is.numeric(attrs[["code.lineno"]])) expect_true(is.numeric(attrs[["code.line.number"]]))
expect_true(is.numeric(attrs[["code.column"]])) expect_true(is.numeric(attrs[["code.column.number"]]))
}) })
test_that("observe created inside function captures function srcref", { test_that("observe created inside function captures function srcref", {
@@ -597,9 +699,9 @@ test_that("observe created inside function captures function srcref", {
o <- create_observer() o <- create_observer()
attrs <- o$.otelAttrs attrs <- o$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_true(is.numeric(attrs[["code.lineno"]])) expect_true(is.numeric(attrs[["code.line.number"]]))
expect_true(is.numeric(attrs[["code.column"]])) expect_true(is.numeric(attrs[["code.column.number"]]))
}) })
test_that("reactive returned from function preserves srcref", { test_that("reactive returned from function preserves srcref", {
@@ -610,8 +712,8 @@ test_that("reactive returned from function preserves srcref", {
counter <- make_counter(42) counter <- make_counter(42)
attrs <- attr(counter, "observable")$.otelAttrs attrs <- attr(counter, "observable")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_true(is.numeric(attrs[["code.lineno"]])) expect_true(is.numeric(attrs[["code.line.number"]]))
}) })
test_that("reactiveVal created in function captures srcref", { test_that("reactiveVal created in function captures srcref", {
@@ -622,8 +724,8 @@ test_that("reactiveVal created in function captures srcref", {
rv <- create_val() rv <- create_val()
attrs <- attr(rv, ".impl")$.otelAttrs attrs <- attr(rv, ".impl")$.otelAttrs
expect_equal(attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_true(is.numeric(attrs[["code.lineno"]])) expect_true(is.numeric(attrs[["code.line.number"]]))
}) })
test_that("nested reactive expressions preserve individual srcrefs", { test_that("nested reactive expressions preserve individual srcrefs", {
@@ -633,17 +735,17 @@ test_that("nested reactive expressions preserve individual srcrefs", {
}) })
outer_attrs <- attr(outer_reactive, "observable")$.otelAttrs outer_attrs <- attr(outer_reactive, "observable")$.otelAttrs
expect_equal(outer_attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(outer_attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_true(is.numeric(outer_attrs[["code.lineno"]])) expect_true(is.numeric(outer_attrs[["code.line.number"]]))
# Get the inner reactive by executing outer # Get the inner reactive by executing outer
withReactiveDomain(MockShinySession$new(), { withReactiveDomain(MockShinySession$new(), {
inner_reactive <- isolate(outer_reactive()) inner_reactive <- isolate(outer_reactive())
inner_attrs <- attr(inner_reactive, "observable")$.otelAttrs inner_attrs <- attr(inner_reactive, "observable")$.otelAttrs
expect_equal(inner_attrs[["code.filepath"]], "test-otel-attr-srcref.R") expect_equal(inner_attrs[["code.file.path"]], "test-otel-attr-srcref.R")
expect_true(is.numeric(inner_attrs[["code.lineno"]])) expect_true(is.numeric(inner_attrs[["code.line.number"]]))
# Inner should have different line number than outer # Inner should have different line number than outer
expect_false(inner_attrs[["code.lineno"]] == outer_attrs[["code.lineno"]]) expect_false(inner_attrs[["code.line.number"]] == outer_attrs[["code.line.number"]])
}) })
}) })

View File

@@ -1,14 +1,47 @@
skip_on_cran() skip_on_cran()
skip_if_not_installed("otelsdk") skip_if_not_installed("otelsdk")
expect_code_attrs <- function(trace) { expect_code_attrs <- function(trace, expected_fn_name = NULL) {
testthat::expect_true(!is.null(trace)) testthat::expect_true(!is.null(trace))
testthat::expect_true(is.list(trace$attributes)) testthat::expect_true(is.list(trace$attributes))
# Check preferred attribute names
testthat::expect_true(is.character(trace$attributes[["code.file.path"]]))
testthat::expect_equal(trace$attributes[["code.file.path"]], "test-otel-mock.R")
testthat::expect_true(is.numeric(trace$attributes[["code.line.number"]]))
testthat::expect_true(is.numeric(trace$attributes[["code.column.number"]]))
# Check deprecated attribute names (for backward compatibility)
testthat::expect_true(is.character(trace$attributes[["code.filepath"]])) testthat::expect_true(is.character(trace$attributes[["code.filepath"]]))
testthat::expect_equal(trace$attributes[["code.filepath"]], "test-otel-mock.R") testthat::expect_equal(trace$attributes[["code.filepath"]], "test-otel-mock.R")
testthat::expect_true(is.numeric(trace$attributes[["code.lineno"]])) testthat::expect_true(is.numeric(trace$attributes[["code.lineno"]]))
testthat::expect_true(is.numeric(trace$attributes[["code.column"]])) testthat::expect_true(is.numeric(trace$attributes[["code.column"]]))
# Verify deprecated names match preferred names
testthat::expect_equal(
trace$attributes[["code.file.path"]],
trace$attributes[["code.filepath"]]
)
testthat::expect_equal(
trace$attributes[["code.line.number"]],
trace$attributes[["code.lineno"]]
)
testthat::expect_equal(
trace$attributes[["code.column.number"]],
trace$attributes[["code.column"]]
)
# Check code.function.name if expected
if (!is.null(expected_fn_name)) {
testthat::expect_true(
is.character(trace$attributes[["code.function.name"]])
)
testthat::expect_equal(
trace$attributes[["code.function.name"]],
expected_fn_name
)
}
invisible(trace) invisible(trace)
} }
MOCK_SESSION_TOKEN <- "test-session-token" MOCK_SESSION_TOKEN <- "test-session-token"
@@ -21,7 +54,7 @@ expect_session_id <- function(trace) {
invisible(trace) invisible(trace)
} }
expect_trace <- function(traces, name, pos = 1) { expect_trace <- function(traces, name, pos = 1, expected_fn_name = NULL) {
# Filter to traces with the given name # Filter to traces with the given name
trace_set <- traces[which(names(traces) == name)] trace_set <- traces[which(names(traces) == name)]
testthat::expect_gte(length(trace_set), pos) testthat::expect_gte(length(trace_set), pos)
@@ -30,7 +63,7 @@ expect_trace <- function(traces, name, pos = 1) {
trace <- trace_set[[pos]] trace <- trace_set[[pos]]
testthat::expect_true(is.list(trace)) testthat::expect_true(is.list(trace))
expect_code_attrs(trace) expect_code_attrs(trace, expected_fn_name = expected_fn_name)
expect_session_id(trace) expect_session_id(trace)
trace trace
@@ -78,9 +111,9 @@ for (bind in c("all", "reactivity")) {
session$flushReact() session$flushReact()
}) })
expect_trace(traces, "observe mock-session:<anonymous>") expect_trace(traces, "observe mock-session:<anonymous>", 1, "observe")
expect_trace(traces, "observe mock-session:my_observe") expect_trace(traces, "observe mock-session:my_observe", 1, "observe")
expect_trace(traces, "observe mock-session:labeled observer") expect_trace(traces, "observe mock-session:labeled observer", 1, "observe")
}) })
test_that(paste0("bind='", bind, "' handles reactiveVal / reactiveValues"), { test_that(paste0("bind='", bind, "' handles reactiveVal / reactiveValues"), {
@@ -104,7 +137,7 @@ for (bind in c("all", "reactivity")) {
expect_equal(rv(), 1) expect_equal(rv(), 1)
}) })
expect_trace(traces, "observe mock-session:<anonymous>") expect_trace(traces, "observe mock-session:<anonymous>", 1, "observe")
# TODO-future: Add tests to see the `Set reactiveVal mock-session:rv` logs # TODO-future: Add tests to see the `Set reactiveVal mock-session:rv` logs
# Requires: https://github.com/r-lib/otelsdk/issues/21 # Requires: https://github.com/r-lib/otelsdk/issues/21
@@ -131,10 +164,16 @@ for (bind in c("all", "reactivity")) {
expect_equal(r3(), 42) expect_equal(r3(), 42)
}) })
observe_trace <- expect_trace(traces, "observe mock-session:obs_r3") observe_trace <- expect_trace(
r_trace <- expect_trace(traces, "reactive mock-session:r") traces, "observe mock-session:obs_r3", 1, "observe"
r2_trace <- expect_trace(traces, "reactive mock-session:<anonymous>") )
r3_trace <- expect_trace(traces, "reactive mock-session:labeled_rv") r_trace <- expect_trace(traces, "reactive mock-session:r", 1, "reactive")
r2_trace <- expect_trace(
traces, "reactive mock-session:<anonymous>", 1, "reactive"
)
r3_trace <- expect_trace(
traces, "reactive mock-session:labeled_rv", 1, "reactive"
)
expect_equal(r_trace$parent, r2_trace$span_id) expect_equal(r_trace$parent, r2_trace$span_id)
expect_equal(r2_trace$parent, r3_trace$span_id) expect_equal(r2_trace$parent, r3_trace$span_id)
@@ -157,7 +196,9 @@ for (bind in c("all", "reactivity")) {
expect_equal(output$txt, "Hello, world!") expect_equal(output$txt, "Hello, world!")
}) })
expect_trace(traces, "output mock-session:txt") # Outputs (render functions) should NOT have code.function.name
trace <- expect_trace(traces, "output mock-session:txt", 1, NULL)
expect_false("code.function.name" %in% names(trace$attributes))
}) })
test_that(paste0("bind='", bind, "' extended tasks are supported"), { test_that(paste0("bind='", bind, "' extended tasks are supported"), {
@@ -183,18 +224,28 @@ for (bind in c("all", "reactivity")) {
traces <- test_server_with_otel(session, server, bind = bind, { traces <- test_server_with_otel(session, server, bind = bind, {
session$flushReact() session$flushReact()
while(!later::loop_empty()) { while (!later::loop_empty()) {
later::run_now() later::run_now()
session$flushReact() session$flushReact()
} }
session$flushReact() session$flushReact()
}) })
invoke_obs <- expect_trace(traces, "observe mock-session:invoke task") invoke_obs <- expect_trace(
render1_trace <- expect_trace(traces, "output mock-session:result") traces, "observe mock-session:invoke task", 1, "observe"
ex_task_trace <- expect_trace(traces, "ExtendedTask mock-session:rand_task") )
# Render functions should NOT have code.function.name
render1_trace <- expect_trace(traces, "output mock-session:result", 1, NULL)
expect_false("code.function.name" %in% names(render1_trace$attributes))
render2_trace <- expect_trace(traces, "output mock-session:result", pos = 2) ex_task_trace <- expect_trace(
traces, "ExtendedTask mock-session:rand_task", 1, "ExtendedTask"
)
render2_trace <- expect_trace(
traces, "output mock-session:result", pos = 2, NULL
)
expect_false("code.function.name" %in% names(render2_trace$attributes))
expect_equal(invoke_obs$span_id, ex_task_trace$parent) expect_equal(invoke_obs$span_id, ex_task_trace$parent)
}) })
@@ -222,9 +273,11 @@ test_that("bind = 'reactivity' traces reactive components", {
}) })
# Should trace reactive components (equivalent to "all") # Should trace reactive components (equivalent to "all")
expect_trace(traces, "observe mock-session:test_obs") expect_trace(traces, "observe mock-session:test_obs", 1, "observe")
expect_trace(traces, "reactive mock-session:r") expect_trace(traces, "reactive mock-session:r", 1, "reactive")
expect_trace(traces, "output mock-session:txt") # Render functions should NOT have code.function.name
txt_trace <- expect_trace(traces, "output mock-session:txt", 1, NULL)
expect_false("code.function.name" %in% names(txt_trace$attributes))
}) })