Description
I have been playing around with extending tinytest, and I noticed that the "call" argument in tinytest()
gets overwritten by the function constructed by capture()
. Additionally format.tinytest()
has specific logic to reconstruct the call from the trace if needed. I assume that having capture()
control the "call" is due to sys.call(sys.parent(1))
, returning fun(...)
for captured functions.
I was thinking of a solution around these issues, and decided to make capturing functions pass their call down to the captured functions. This allows expectations or anything that calls tinytest()
to control what call
looks like, making it easier to nest expectations.
An minimal example is below:
# Simple tinytest constructor
tinytest <- function(result, call = sys.call(sys.parent(1)), ...) {
force(call)
trace <- sys.calls()
structure(result, class = "tinytest", call = call, trace = trace, ...)
}
# Expectation that will be captured
expect_example <- function(...) {
tinytest(TRUE, ...)
}
# Simple capture function
capture <- function(fun) {
force(fun)
function(...) {
# Create a local variable that matches the name of the function that was called.
# Assign the captured function to that name.
# Evaluate the original call to pass it into the captured function.
call <- sys.call()
assign(as.character(call[[1]]), fun)
out <- eval(call)
## do stuff to out here.
attr(out, "captured") <- TRUE
out
}
}
So now when a captured function is called, sys.call(sys.parent(1))
behaves as expected. This allows the call attribute to be the same for captured and uncaptured functions.
expect_example(a=1, b=2) # call is `expect_example(a=1,b=2)`
expect_example <- capture(expect_example)
expect_example(a=1, b=2) # call is `expect_example(a=1,b=2)`
expect_example_alt <- expect_example
expect_example_alt(a=1, b=2) # call is `expect_example_alt(a=1,b=2)`