Nothing
# Test: Closures (evaluated functions) ------------------------------------------
if (utils::packageVersion("base") < "4.2.0") {
# Tree characters are ASCII on old Windows R
skip_on_os("windows")
}
test_that("src() shows closure with srcref and wholeSrcref", {
expect_snapshot(transform = scrub_src_transform, {
f <- simple_function_with_srcref()
src(f)
})
})
test_that("src() shows multi-statement function", {
expect_snapshot(transform = scrub_src_transform, {
f <- multi_statement_function_with_srcref()
src(f)
})
})
# Test: Quoted functions --------------------------------------------------------
test_that("src() shows quoted function with nested body", {
expect_snapshot(transform = scrub_src_transform, {
with_srcref("x <- quote(function() {})")
src(x)
})
})
test_that("src() shows quoted function body directly", {
expect_snapshot(transform = scrub_src_transform, {
with_srcref("x <- quote(function() {})")
src(x[[3]])
})
})
test_that("src() shows quoted function with arguments", {
expect_snapshot(transform = scrub_src_transform, {
with_srcref("x <- quote(function(a, b) {})")
src(x)
})
})
test_that("src() shows srcref with parsed field when positions differ", {
expect_snapshot(transform = scrub_src_transform, {
# Create a synthetic 8-element srcref where parsed positions differ
# Format: c(first_line, first_byte, last_line, last_byte,
# first_col, last_col, first_parsed, last_parsed)
# This simulates a case where R's parser reports different positions
# than the actual source locations (e.g., due to string continuations)
srcfile <- srcfilecopy(
"test.R",
c(
"x <- function() {",
" # A long comment that spans",
" # multiple lines",
" y <- 1",
"}"
)
)
synthetic_srcref <- structure(
c(2L, 3L, 4L, 8L, 3L, 8L, 1L, 5L),
class = "srcref",
srcfile = srcfile
)
src(synthetic_srcref)
})
})
# Test: Expression objects ------------------------------------------------------
test_that("src() shows expression with single element", {
expect_snapshot(transform = scrub_src_transform, {
x <- parse(text = "x + 1", keep.source = TRUE)
src(x)
})
})
test_that("src() shows expression with multiple elements", {
expect_snapshot(transform = scrub_src_transform, {
x <- parse(text = c("x + 1", "y + 2", "z + 3"), keep.source = TRUE)
src(x)
})
})
test_that("src() shows expression with nested block and wholeSrcref", {
expect_snapshot(transform = scrub_src_transform, {
x <- parse(text = "{\n 1\n}", keep.source = TRUE)
src(x)
})
})
test_that("src() shows nested block element directly", {
expect_snapshot(transform = scrub_src_transform, {
x <- parse(text = "{\n 1\n}", keep.source = TRUE)
src(x[[1]])
})
})
# Test: Blocks with wholeSrcref -------------------------------------------------
test_that("src() shows block with srcref list and wholeSrcref", {
expect_snapshot(transform = scrub_src_transform, {
x <- parse(text = "{\n a <- 1\n b <- 2\n}", keep.source = TRUE)
src(x[[1]])
})
})
# Test: Single srcref objects ---------------------------------------------------
test_that("src() shows single srcref", {
expect_snapshot(transform = scrub_src_transform, {
x <- parse(text = "x + 1", keep.source = TRUE)
sr <- attr(x, "srcref")[[1]]
src(sr)
})
})
# Test: List of srcrefs ---------------------------------------------------------
test_that("src() shows list of srcrefs with count", {
expect_snapshot(transform = scrub_src_transform, {
x <- parse(text = c("x + 1", "y + 2"), keep.source = TRUE)
sr_list <- attr(x, "srcref")
src(sr_list)
})
})
# Test: Srcref lists shown as <list> with [[1]], [[2]] notation ----------------
test_that("src() reveals srcref list structure with index notation", {
expect_snapshot(transform = scrub_src_transform, {
with_srcref("x <- quote(function() { 1 })")
src(x[[3]])
})
})
test_that("src() handles srcrefs nested in language calls", {
expect_snapshot(transform = scrub_src_transform, {
x <- parse(text = "foo({ if (1) bar({ 2 }) })", keep.source = TRUE)
src(x, max_depth = 10)
})
})
test_that("src() handles srcrefs nested in function bodies", {
expect_snapshot(transform = scrub_src_transform, {
with_srcref("f <- function() foo({ if (1) bar({ 2 }) })")
src(f, max_depth = 10)
})
})
# Test: Type labels -------------------------------------------------------------
test_that("src() uses correct type labels", {
# Closure
f <- simple_function_with_srcref()
result_closure <- src(f)
expect_equal(attr(result_closure, "srcref_type"), "closure")
# Quoted function
with_srcref("x <- quote(function() {})")
result_quoted <- src(x)
expect_equal(attr(result_quoted, "srcref_type"), "quoted_function")
# Expression
expr <- parse(text = "1 + 1", keep.source = TRUE)
result_expr <- src(expr)
expect_equal(attr(result_expr, "srcref_type"), "expression")
# Block
block <- parse(text = "{1}", keep.source = TRUE)[[1]]
result_block <- src(block)
expect_equal(attr(result_block, "srcref_type"), "block")
})
# Test: Srcfile duplication (current behavior - will change in Phase 1) --------
test_that("src() currently shows duplicate srcfile objects", {
expect_snapshot(transform = scrub_src_transform, {
# Current behavior: srcfile appears twice (in srcref and wholeSrcref)
# After Phase 1: should use reference notation like @abc123
f <- simple_function_with_srcref()
src(f)
})
})
test_that("src() shows many duplicate srcfiles in nested expression", {
expect_snapshot(transform = scrub_src_transform, {
# Current behavior: same srcfile appears many times
# After Phase 1: these should be deduplicated
x <- parse(text = "{\n 1\n 2\n}", keep.source = TRUE)
src(x)
})
})
# Test: Edge cases --------------------------------------------------------------
test_that("src() handles empty block", {
expect_snapshot(transform = scrub_src_transform, {
x <- parse(text = "{}", keep.source = TRUE)
src(x[[1]])
})
})
test_that("src() handles function without arguments", {
expect_snapshot(transform = scrub_src_transform, {
with_srcref("f <- function() { NULL }")
src(f)
})
})
test_that("src() handles if statement with blocks", {
expect_snapshot(transform = scrub_src_transform, {
x <- parse(text = "if (TRUE) { 1 } else { 2 }", keep.source = TRUE)
src(x)
})
})
# Test: Parameters --------------------------------------------------------------
test_that("src() respects show_source_lines parameter", {
expect_snapshot(transform = scrub_src_transform, {
f <- simple_function_with_srcref()
src(f)
})
})
# Test: Complex nested structures -----------------------------------------------
test_that("src() shows expression with multiple nested blocks", {
expect_snapshot(transform = scrub_src_transform, {
x <- parse(
text = "{\n {\n 1\n }\n {\n 2\n }\n}",
keep.source = TRUE
)
src(x)
})
})
test_that("src() shows function with nested block in body", {
expect_snapshot(transform = scrub_src_transform, {
with_srcref("f <- function(x) {\n if (x) {\n 1\n }\n}")
src(f)
})
})
# Tests for src() function and helpers
# Helper function tests --------------------------------------------------------
test_that("extract_srcref_info handles 4-element srcrefs", {
# Create a simple expression with srcref
expr <- parse(text = "x + 1", keep.source = TRUE)
srcref <- attr(expr, "srcref")[[1]]
# Manually create a 4-element srcref for testing
# Note: In practice, 4-element srcrefs are rare in modern R
srcref_4 <- structure(
c(1L, 1L, 1L, 5L),
class = "srcref",
srcfile = attr(srcref, "srcfile")
)
info <- srcref_info(srcref_4)
expect_s3_class(info$location, "lobstr_srcref_location")
expect_equal(as.character(info$location), "1:1 - 1:5")
})
test_that("extract_srcref_info handles 6-element srcrefs", {
# Create a 6-element srcref
expr <- parse(text = "x + 1", keep.source = TRUE)
srcref_base <- attr(expr, "srcref")[[1]]
srcref_6 <- structure(
c(1L, 1L, 1L, 5L, 1L, 5L),
class = "srcref",
srcfile = attr(srcref_base, "srcfile")
)
info <- srcref_info(srcref_6)
expect_s3_class(info$location, "lobstr_srcref_location")
expect_equal(as.character(info$location), "1:1 - 1:5")
})
test_that("extract_srcref_info handles 8-element srcrefs", {
expr <- parse(text = "x + 1", keep.source = TRUE)
srcref <- attr(expr, "srcref")[[1]]
# Most modern srcrefs are 8-element
info <- srcref_info(srcref)
expect_s3_class(info$location, "lobstr_srcref_location")
expect_match(as.character(info$location), "\\d+:\\d+ - \\d+:\\d+")
})
test_that("extract_srcref_info shows encoding details when requested", {
expr <- parse(text = "x + 1", keep.source = TRUE)
srcref <- attr(expr, "srcref")[[1]]
info <- srcref_info(srcref)
expect_true("location" %in% names(info))
})
test_that("extract_srcref_info errors on invalid srcref length", {
# Create an invalid srcref with wrong number of elements
bad_srcref <- structure(c(1L, 2L, 3L), class = "srcref")
expect_error(
srcref_info(bad_srcref),
"Unexpected srcref length"
)
})
test_that("srcfile_node handles srcfilecopy", {
expr <- parse(text = "x + 1", keep.source = TRUE)
srcfile <- attr(attr(expr, "srcref")[[1]], "srcfile")
srcref <- attr(expr, "srcref")[[1]]
seen_srcfiles <- new.env(parent = emptyenv())
info <- srcfile_node(srcfile, seen_srcfiles)
expect_equal(attr(info, "srcfile_class"), class(srcfile)[1])
expect_type(info$filename, "character")
expect_type(info$Enc, "character")
})
test_that("srcfile_node handles NULL gracefully", {
seen_srcfiles <- new.env(parent = emptyenv())
info <- srcfile_node(NULL, seen_srcfiles)
expect_null(info)
})
test_that("srcfile_lines extracts from srcfilecopy", {
code <- c("x <- 1", "y <- 2", "z <- 3")
expr <- parse(text = code, keep.source = TRUE)
srcref <- attr(expr, "srcref")[[1]]
srcfile <- attr(srcref, "srcfile")
snippet <- srcfile_lines(srcfile, srcref)
expect_type(snippet, "character")
expect_true(length(snippet) >= 1)
})
test_that("srcfile_lines respects max_lines", {
code <- c("x <- 1", "y <- 2", "z <- 3", "a <- 4", "b <- 5")
expr <- parse(text = paste(code, collapse = "\n"), keep.source = TRUE)
srcfile <- attr(attr(expr, "srcref")[[1]], "srcfile")
srcref <- structure(
c(1L, 1L, 5L, 10L, 1L, 10L, 1L, 5L),
class = "srcref",
srcfile = srcfile
)
snippet <- srcfile_lines(srcfile, srcref)
expect_type(snippet, "character")
expect_lte(length(snippet), 3)
})
test_that("srcref_location works correctly", {
srcref <- structure(
c(1L, 5L, 3L, 20L, 5L, 20L, 1L, 3L),
class = "srcref"
)
loc <- srcref_location(srcref)
expect_equal(loc, "1:5 - 3:20")
})
# Integration tests for src() --------------------------------------------------
test_that("src works with functions with source references", {
fun <- simple_function_with_srcref()
result <- src(fun)
expect_type(result, "list")
expect_equal(attr(result, "srcref_type"), "closure")
})
test_that("src works with single srcref objects", {
expr <- parse(text = "x + 1", keep.source = TRUE)
srcref <- attr(expr, "srcref")[[1]]
result <- src(srcref)
expect_type(result, "list")
expect_equal(attr(result, "srcref_type"), "srcref")
expect_true("location" %in% names(result))
})
test_that("src works with list of srcrefs", {
expr <- parse(text = c("x + 1", "y + 2"), keep.source = TRUE)
srcref_list <- attr(expr, "srcref")
result <- src(srcref_list)
expect_type(result, "list")
expect_equal(attr(result, "srcref_type"), "list")
})
test_that("src works with expressions", {
expr <- parse(text = "x + 1", keep.source = TRUE)
result <- src(expr)
expect_type(result, "list")
})
test_that("src works for objects without srcrefs", {
fun <- function(x) x + 1
attr(fun, "srcref") <- NULL
expect_null(src(fun))
expect_null(src(new.env()))
expect_null(src(list()))
expect_null(src(sum))
})
test_that("src respects max_lines_preview parameter", {
fun <- multi_statement_function_with_srcref()
result <- src(fun, max_lines_preview = 1)
expect_type(result, "list")
expect_equal(attr(result, "srcref_type"), "closure")
})
test_that("src returns structure and print method works", {
fun <- simple_function_with_srcref()
# src() returns visibly (with S3 class)
result <- src(fun)
expect_s3_class(result, "lobstr_srcref")
# print method returns invisibly and outputs to console
expect_output(
expect_invisible(print(result)),
"<closure>"
)
})
# S3 method tests --------------------------------------------------------------
test_that("tree_label.srcref formats correctly", {
expr <- parse(text = "x + 1", keep.source = TRUE)
srcref <- attr(expr, "srcref")[[1]]
# Call the method directly since srcref has proper class
label <- tree_label.srcref(srcref, list())
expect_type(label, "character")
expect_match(label, "<srcref:")
expect_match(label, "\\d+:\\d+ - \\d+:\\d+")
})
test_that("tree_label.srcfile formats correctly", {
expr <- parse(text = "x + 1", keep.source = TRUE)
srcfile <- attr(attr(expr, "srcref")[[1]], "srcfile")
# Call the method directly since srcfile is an environment with class attribute
label <- tree_label.srcfile(srcfile, list())
expect_type(label, "character")
expect_match(label, "<srcfile")
})
# Edge case tests --------------------------------------------------------------
test_that("src handles functions with wholeSrcref on body", {
fun <- simple_function_with_srcref()
# Verify wholeSrcref exists on body
expect_false(is.null(attr(body(fun), "wholeSrcref")))
result <- src(fun)
expect_type(result, "list")
# Should have body() node in the result
expect_true("body()" %in% names(result))
expect_s3_class(result$`body()`, "lobstr_srcref")
# wholeSrcref should be nested under body()
expect_true("attr(\"wholeSrcref\")" %in% names(result$`body()`))
expect_s3_class(result$`body()`$`attr("wholeSrcref")`, "lobstr_srcref")
})
test_that("src handles functions with only wholeSrcref (no srcref attr)", {
fun <- simple_function_with_srcref()
# Remove srcref attribute from function, keep only wholeSrcref on body
attr(fun, "srcref") <- NULL
result <- src(fun)
expect_type(result, "list")
# Should still have body() node with wholeSrcref from body
expect_true("body()" %in% names(result))
expect_true("attr(\"wholeSrcref\")" %in% names(result$`body()`))
})
test_that("srcfile_lines handles missing files gracefully", {
expr <- parse(text = "x + 1", keep.source = TRUE)
srcref <- attr(expr, "srcref")[[1]]
srcfile <- attr(srcref, "srcfile")
# Point to a non-existent file
srcfile$filename <- "nonexistent_file.R"
snippet <- srcfile_lines(srcfile, srcref)
expect_type(snippet, "character")
})
# Srcfile deduplication tests --------------------------------------------------
test_that("srcfile deduplication - basic case with srcref and wholeSrcref", {
# Parse code that creates a function with both srcref and wholeSrcref
code <- parse(text = "f <- function(x) { x + 1 }", keep.source = TRUE)
f <- eval(code[[1]])
result <- src(f)
# Extract the srcfile from the first occurrence
first_srcfile <- result$`attr("srcref")`$`attr("srcfile")`
# Extract the ID from the first occurrence
id <- attr(first_srcfile, "srcfile_id")
expect_type(id, "character")
expect_true(nchar(id) >= 1) # Should be hex ID (up to 6 chars)
expect_true(nchar(id) <= 6)
# Check that wholeSrcref uses a reference
whole_srcfile <- result$`body()`$`attr("wholeSrcref")`$`attr("srcfile")`
expect_s3_class(whole_srcfile, "lobstr_srcfile_ref")
expect_equal(as.character(whole_srcfile), id)
})
test_that("srcfile deduplication - multiple statement srcrefs share one srcfile", {
# Create a function with multiple statements
code <- parse(
text = "f <- function(x) { a <- x + 1; b <- a * 2; b }",
keep.source = TRUE
)
f <- eval(code[[1]])
result <- src(f)
# Get the ID from the first occurrence
first_srcfile <- result$`attr("srcref")`$`attr("srcfile")`
id <- attr(first_srcfile, "srcfile_id")
# Check that all statement srcrefs use references
stmt_list <- result$`body()`$`attr("srcref")`
for (i in seq_along(stmt_list)) {
stmt_name <- paste0("[[", i, "]]")
stmt_srcfile <- stmt_list[[stmt_name]]$`attr("srcfile")`
# Should be a reference
expect_s3_class(stmt_srcfile, "lobstr_srcfile_ref")
expect_equal(as.character(stmt_srcfile), id)
}
})
test_that("srcfile deduplication - IDs are stable within a single src() call", {
# Parse the same code twice to get two different srcfile objects
code1 <- parse(text = "f <- function(x) { x + 1 }", keep.source = TRUE)
code2 <- parse(text = "g <- function(y) { y * 2 }", keep.source = TRUE)
f <- eval(code1[[1]])
g <- eval(code2[[1]])
# Call src() on first function
result_f <- src(f)
id_f <- attr(result_f$`attr("srcref")`$`attr("srcfile")`, "srcfile_id")
# Call src() on second function (different call, different seen_srcfiles)
result_g <- src(g)
id_g <- attr(result_g$`attr("srcref")`$`attr("srcfile")`, "srcfile_id")
# IDs are sequential and start fresh for each src() call
expect_type(id_f, "character")
expect_type(id_g, "character")
# Both should be 3-digit sequential IDs starting at "001"
expect_equal(id_f, "001")
expect_equal(id_g, "001")
})
test_that("srcfile deduplication - multiple files means no cross-file deduplication", {
# Parse two separate code snippets (different srcfile objects)
code1 <- parse(text = "f <- function(x) { x + 1 }", keep.source = TRUE)
code2 <- parse(text = "g <- function(y) { y * 2 }", keep.source = TRUE)
f <- eval(code1[[1]])
g <- eval(code2[[1]])
# Get the srcfile addresses
srcfile_f <- attr(attr(f, "srcref"), "srcfile")
srcfile_g <- attr(attr(g, "srcref"), "srcfile")
addr_f <- lobstr::obj_addr(srcfile_f)
addr_g <- lobstr::obj_addr(srcfile_g)
# Different srcfiles should have different addresses
expect_false(addr_f == addr_g)
# If we call src() on each separately, they each get their own ID
result_f <- src(f)
result_g <- src(g)
id_f <- attr(result_f$`attr("srcref")`$`attr("srcfile")`, "srcfile_id")
id_g <- attr(result_g$`attr("srcref")`$`attr("srcfile")`, "srcfile_id")
# IDs are sequential and start fresh for each src() call, so both get "001"
# This ensures deterministic snapshots
expect_equal(id_f, "001")
expect_equal(id_g, "001")
})
test_that("srcfile deduplication - nested functions from same file", {
# Create code with a nested function
code <- "
outer <- function() {
inner <- function(x) { x + 1 }
inner(5)
}
"
parsed <- parse(text = code, keep.source = TRUE)
eval(parsed[[1]])
result <- src(outer)
# The outer function should have a srcfile with an ID
outer_srcfile <- result$`attr("srcref")`$`attr("srcfile")`
expect_true(!is.null(attr(outer_srcfile, "srcfile_id")))
# All other references should use the same ID
id <- attr(outer_srcfile, "srcfile_id")
# Check wholeSrcref reference
whole_srcfile <- result$`body()`$`attr("wholeSrcref")`$`attr("srcfile")`
expect_s3_class(whole_srcfile, "lobstr_srcfile_ref")
expect_equal(as.character(whole_srcfile), id)
})
test_that("srcfile deduplication - reference notation displays correctly", {
code <- parse(text = "f <- function(x) { x + 1 }", keep.source = TRUE)
f <- eval(code[[1]])
# Capture the output
output <- capture.output(print(src(f)))
# Should see the full srcfile once with @id notation
full_srcfile_lines <- grep("<srcfilecopy> @[0-9a-f]+", output)
expect_true(length(full_srcfile_lines) >= 1)
# Should see reference notation (just @id without class)
ref_lines <- grep("^[^<]*@[0-9a-f]+\\s*$", output, perl = TRUE)
expect_true(length(ref_lines) >= 1)
# Extract the ID from both to verify they match
full_line <- output[full_srcfile_lines[1]]
id_from_full <- regmatches(full_line, regexpr("@[0-9a-f]+", full_line))
ref_line <- output[ref_lines[1]]
id_from_ref <- regmatches(ref_line, regexpr("@[0-9a-f]+", ref_line))
expect_equal(id_from_full, id_from_ref)
})
test_that("lobstr_srcfile_ref class has correct structure", {
# Create a reference object directly
ref <- new_srcfile_ref("abc123", "srcfilecopy")
expect_s3_class(ref, "lobstr_srcfile_ref")
expect_equal(as.character(ref), "abc123")
expect_equal(attr(ref, "srcfile_class"), "srcfilecopy")
# Tree label should show just @id
label <- tree_label.lobstr_srcfile_ref(ref, list())
expect_equal(label, "@abc123")
})
test_that("srcfile deduplication - expression with multiple elements", {
# Parse an expression with multiple top-level elements
code <- parse(text = c("x <- 1", "y <- 2", "z <- 3"), keep.source = TRUE)
result <- src(code)
# The expression should have an srcref list
srcref_list <- result$`attr("srcref")`
# Get the first srcfile
first_srcfile <- srcref_list$`[[1]]`$`attr("srcfile")`
id <- attr(first_srcfile, "srcfile_id")
expect_type(id, "character")
# Other elements should reference the same srcfile
second_srcfile <- srcref_list$`[[2]]`$`attr("srcfile")`
expect_s3_class(second_srcfile, "lobstr_srcfile_ref")
expect_equal(as.character(second_srcfile), id)
third_srcfile <- srcref_list$`[[3]]`$`attr("srcfile")`
expect_s3_class(third_srcfile, "lobstr_srcfile_ref")
expect_equal(as.character(third_srcfile), id)
})
# Deep nesting tests ----------------------------------------------------------
test_that("deep nesting - for loop with nested block", {
code <- parse(
text = "
f <- function(x) {
for (i in 1:x) {
print(i)
}
}
",
keep.source = TRUE
)
eval(code[[1]])
result <- src(f)
# Should have body with nested block
expect_true("body()" %in% names(result))
# Should show nested block with path notation [[2]][[4]]
# (element 2 of body is the for loop, element 4 is the body block)
nested_block_path <- grep(
"^\\[\\[2\\]\\]\\[\\[4\\]\\]",
names(result$`body()`),
value = TRUE
)
expect_true(length(nested_block_path) >= 1)
# The nested block should have srcref attributes
nested_block <- result$`body()`[[nested_block_path[1]]]
expect_s3_class(nested_block, "lobstr_srcref")
expect_true(
!is.null(nested_block$`attr("srcref")`) ||
!is.null(nested_block$`attr("wholeSrcref")`)
)
})
test_that("deep nesting - if/else with blocks", {
code <- parse(
text = "
g <- function(x) {
if (x > 0) {
y <- x + 1
} else {
y <- x - 1
}
y
}
",
keep.source = TRUE
)
eval(code[[1]])
result <- src(g)
# Should show both if and else blocks with path notation
body_names <- names(result$`body()`)
# Look for nested blocks (should be [[2]][[3]] and [[2]][[4]])
if_block_paths <- grep(
"^\\[\\[2\\]\\]\\[\\[3\\]\\]",
body_names,
value = TRUE
)
else_block_paths <- grep(
"^\\[\\[2\\]\\]\\[\\[4\\]\\]",
body_names,
value = TRUE
)
expect_true(length(if_block_paths) >= 1)
expect_true(length(else_block_paths) >= 1)
# Both blocks should be srcref objects
if_block <- result$`body()`[[if_block_paths[1]]]
expect_s3_class(if_block, "lobstr_srcref")
else_block <- result$`body()`[[else_block_paths[1]]]
expect_s3_class(else_block, "lobstr_srcref")
})
test_that("deep nesting - nested blocks { { { } } }", {
code <- parse(
text = "
h <- function() {
{
{
x <- 1
}
}
}
",
keep.source = TRUE
)
eval(code[[1]])
result <- src(h)
# Should have nested blocks
expect_true("body()" %in% names(result))
# Should have at least one nested [[2]] block
expect_true("[[2]]" %in% names(result$`body()`))
# That nested block should have further nesting
nested_block <- result$`body()`$`[[2]]`
expect_s3_class(nested_block, "lobstr_srcref")
expect_true("[[2]]" %in% names(nested_block))
})
test_that("deep nesting - multiple top-level statements", {
code <- parse(
text = "
f <- function(x) {
a <- x + 1
b <- a * 2
for (i in 1:b) {
print(i)
}
b
}
",
keep.source = TRUE
)
eval(code[[1]])
result <- src(f)
# Should have body with statement srcrefs
expect_true("body()" %in% names(result))
expect_true("attr(\"srcref\")" %in% names(result$`body()`))
# Should show the nested for loop block
body_names <- names(result$`body()`)
for_block_paths <- grep(
"\\[\\[4\\]\\]\\[\\[4\\]\\]",
body_names,
value = TRUE
)
expect_true(length(for_block_paths) >= 1)
})
test_that("deep nesting - empty function", {
code <- parse(text = "f <- function() {}", keep.source = TRUE)
eval(code[[1]])
result <- src(f)
# Should still have structure
expect_type(result, "list")
expect_s3_class(result, "lobstr_srcref")
expect_true("attr(\"srcref\")" %in% names(result))
})
test_that("deep nesting - very deep structure respects max_depth", {
# Create deeply nested structure
code <- parse(
text = "
f <- function(x) {
for (i in 1:x) {
if (i > 0) {
while (i < 10) {
for (j in 1:i) {
print(j)
}
break
}
}
}
}
",
keep.source = TRUE
)
eval(code[[1]])
# With low max_depth, should truncate
result_shallow <- src(f, max_depth = 2)
expect_type(result_shallow, "list")
# With high max_depth, should show more nesting
result_deep <- src(f, max_depth = 10)
expect_type(result_deep, "list")
# Deep version should have more nested paths
shallow_paths <- names(result_shallow)
deep_paths <- names(result_deep)
# Both should be valid results
expect_true(length(shallow_paths) > 0)
expect_true(length(deep_paths) > 0)
})
test_that("deep nesting - intermediate calls without srcrefs are omitted", {
code <- parse(
text = "
f <- function(x) {
for (i in 1:x) {
print(i)
}
}
",
keep.source = TRUE
)
eval(code[[1]])
# Capture output
output <- capture.output(print(src(f)))
# Should NOT see intermediate <language> nodes
language_nodes <- grep("<language>", output, value = TRUE)
expect_equal(length(language_nodes), 0)
# Should see collapsed path notation
collapsed_paths <- grep(
"\\[\\[\\d+\\]\\]\\[\\[\\d+\\]\\]",
output,
value = TRUE
)
expect_true(length(collapsed_paths) > 0)
})
test_that("deep nesting - while loop with nested block", {
code <- parse(
text = "
f <- function(x) {
i <- 0
while (i < x) {
print(i)
i <- i + 1
}
}
",
keep.source = TRUE
)
eval(code[[1]])
result <- src(f)
# Should have nested block for while body
body_names <- names(result$`body()`)
while_block_paths <- grep(
"\\[\\[3\\]\\]\\[\\[3\\]\\]",
body_names,
value = TRUE
)
expect_true(length(while_block_paths) >= 1)
})
test_that("deep nesting - repeat loop with nested block", {
code <- parse(
text = "
f <- function(x) {
repeat {
print(x)
break
}
}
",
keep.source = TRUE
)
eval(code[[1]])
result <- src(f)
# Should have nested block for repeat body
expect_true("body()" %in% names(result))
body_names <- names(result$`body()`)
# Look for nested block path
nested_paths <- grep("^\\[\\[2\\]\\]\\[\\[2\\]\\]", body_names, value = TRUE)
expect_true(length(nested_paths) >= 1)
})
test_that("deep nesting - switch statement with blocks", {
code <- parse(
text = '
f <- function(x) {
switch(x,
a = {
print("a")
},
b = {
print("b")
}
)
}
',
keep.source = TRUE
)
eval(code[[1]])
result <- src(f)
# Should have structure
expect_type(result, "list")
expect_s3_class(result, "lobstr_srcref")
})
test_that("deep nesting - quoted expressions with nested blocks", {
code <- parse(
text = "
x <- quote({
for (i in 1:3) {
print(i)
}
})
",
keep.source = TRUE
)
eval(code[[1]])
result <- src(x)
# Should show nested structure
expect_type(result, "list")
# Should have nested block paths
if (!is.null(result) && length(result) > 0) {
all_names <- names(unlist(result, recursive = TRUE))
nested_paths <- grep(
"\\[\\[\\d+\\]\\]\\[\\[\\d+\\]\\]",
all_names,
value = TRUE
)
# Might or might not have nested paths depending on how quote() preserves srcrefs
expect_true(length(nested_paths) >= 0)
}
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.