tests/testthat/test-get_code.R

warning_message <- "warning('Code was not verified for reproducibility.')"

testthat::test_that("handles empty @code slot", {
  testthat::expect_identical(
    get_code(teal_data(a = 1, code = character(0)), datanames = "a"),
    warning_message
  )
  testthat::expect_identical(
    get_code(teal_data(a = 1, code = ""), datanames = "a"),
    paste0(warning_message, "\n")
  )
})

testthat::test_that("handles the code without symbols on rhs", {
  code <- c(
    "1 + 1",
    "a <- 5",
    "501"
  )

  testthat::expect_identical(
    get_code(teal_data(a = 5, code = code), datanames = "a"),
    paste(warning_message, "a <- 5", sep = "\n")
  )
})

testthat::test_that("extracts the code of a binding from character vector containing simple code", {
  code <- c(
    "a <- 1",
    "b <- 2"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "a"),
    "a <- 1"
  )
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    "b <- 2"
  )
})

testthat::test_that("extracts the code without downstream usage", {
  code <- c(
    "a <- 1",
    "head(a)"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "a"),
    "a <- 1"
  )
})

testthat::test_that("works for datanames of length > 1", {
  code <- c(
    "a <- 1",
    "b <- 2"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = c("a", "b")),
    paste(code, collapse = "\n")
  )
})

testthat::test_that("warns if binding doesn't exist in code", {
  code <- c("a <- 1")
  tdata <- eval_code(teal_data(), code)
  testthat::expect_warning(
    get_code(tdata, datanames = "c"),
    "Object\\(s\\) not found in code: c"
  )
})

testthat::test_that("does not fall into a loop", {
  code <- c(
    "a <- 1",
    "b <- a",
    "c <- b",
    "a <- c"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "a"),
    paste(code, collapse = "\n")
  )
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    paste(code[1:2], collapse = "\n")
  )
  testthat::expect_identical(
    get_code(tdata, datanames = "c"),
    paste(code[1:3], collapse = "\n")
  )
})


testthat::test_that("extracts code of a parent binding but only those evaluated before coocurence", {
  code <- c(
    "a <- 1",
    "b <- a",
    "a <- 2"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    paste("a <- 1", "b <- a", sep = "\n")
  )
})

testthat::test_that("extracts the code of a parent binding if used as an arg in a function call", {
  code <- c(
    "a <- 1",
    "b <- identity(x = a)",
    "a <- 2"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    paste("a <- 1", "b <- identity(x = a)", sep = "\n")
  )
})

testthat::test_that("extracts the code when using <<-", {
  code <- c(
    "a <- 1",
    "b <- a",
    "b <<- b + 2"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    paste("a <- 1", "b <- a", "b <<- b + 2", sep = "\n")
  )
})

testthat::test_that("detects every assign calls even if not evaluated, if there is only one assignment in this line", {
  code <- c(
    "a <- 1",
    "b <- 2",
    "eval(expression({b <- b + 2}))"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    paste("b <- 2", "eval(expression({\n    b <- b + 2\n}))", sep = "\n")
  )
})

testthat::test_that("returns result of length 1 for non-empty input", {
  tdata1 <- teal_data()
  tdata1 <- within(tdata1, {
    a <- 1
    b <- a^5
    c <- list(x = 2)
  })

  testthat::expect_length(get_code(tdata1, deparse = FALSE), 1)
  testthat::expect_length(get_code(tdata1, deparse = TRUE), 1)
})

testthat::test_that("does not break if code is separated by ;", {
  code <- c(
    "a <- 1;a <- a + 1"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "a"),
    gsub(";", "\n", code, fixed = TRUE)
  )
})

testthat::test_that("does not break if code uses quote()", {
  code <- c(
    "expr <- quote(x <- x + 1)",
    "x <- 0",
    "eval(expr)"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "x"),
    code[2]
  )
})

testthat::test_that("does not break if object is used in a function on lhs", {
  code <- c(
    "data(iris)",
    "iris2 <- iris",
    "names(iris) <- letters[1:5]"
  )
  tdata <- eval_code(teal_data(), code = code)
  testthat::expect_identical(
    get_code(tdata, datanames = "iris"),
    paste(code[c(1, 3)], collapse = "\n")
  )
})

testthat::test_that(
  "does not break if object is used in a function on lhs and influencers are both on lhs and rhs",
  {
    code <- c(
      "x <- 5",
      "y <- length(x)",
      "names(x)[y] <- y"
    )
    tdata <- eval_code(teal_data(), code = code)
    testthat::expect_identical(
      get_code(tdata, datanames = "x"),
      paste(code, collapse = "\n")
    )
  }
)

# assign ----------------------------------------------------------------------------------------------------------

testthat::test_that("extracts the code for assign() where \"x\" is a literal string", {
  code <- c(
    "a <- 1",
    "assign('b', 5)",
    "assign(value = 7, x = 'c')",
    "assign(value = 15, x = \"d\")",
    "b <- b + 2",
    "c <- b",
    "d <- d * 2"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    paste("assign(\"b\", 5)", "b <- b + 2", sep = "\n")
  )
  testthat::expect_identical(
    get_code(tdata, datanames = "c"),
    paste(
      "assign(\"b\", 5)",
      "assign(value = 7, x = \"c\")",
      "b <- b + 2",
      "c <- b",
      sep = "\n"
    )
  )
  testthat::expect_identical(
    get_code(tdata, datanames = "d"),
    paste("assign(value = 15, x = \"d\")", "d <- d * 2", sep = "\n")
  )
})

testthat::test_that("extracts the code for assign() where \"x\" is variable", {
  testthat::skip("We will not resolve this, as this requires code evaluation.")
  code <- c(
    "x <- \"a\"",
    "assign(x, 5)",
    "b <- a"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    paste(code, collapse = "\n")
  )
})

testthat::test_that("works for assign() detection no matter how many parametrers were provided in assignq()", {
  code <- c(
    "x <- 1",
    "assign(\"x\", 0, envir = environment())",
    "assign(inherits = FALSE, immediate = TRUE, \"z\", 5, envir = environment())",
    "y <- x + z",
    "y <- x"
  )

  tdata <- eval_code(teal_data(), code)

  testthat::expect_identical(
    get_code(tdata, datanames = "y"),
    paste(code, collapse = "\n")
  )
})

testthat::test_that("detects function usage of the assignment operator", {
  code <- c(
    "x <- 1",
    "`<-`(y,x)"
  )
  code2 <- "`<-`(y, `<-`(x, 2))"

  tdata <- eval_code(teal_data(), code)
  tdata2 <- eval_code(teal_data(), code2)

  testthat::expect_identical(
    get_code(tdata, datanames = "y"),
    paste(c(code[1], "y <- x"), collapse = "\n")
  )
  testthat::expect_identical(
    get_code(tdata2, datanames = "y"),
    "y <- x <- 2"
  )
})


# @linksto ---------------------------------------------------------------------------------------------------------

testthat::test_that("get_code does not break if @linksto is put in the last line", {
  # In some cases R parses comment as a separate expression so the comment is not
  # directly associated with this line of code. This situation occurs when `eval` is in the last
  # line of the code. Other cases are not known but are highly probable.
  code <- c(
    "expr <- quote(x <- x + 1)",
    "x <- 0",
    "eval(expr) #@linksto x"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "x"),
    paste(gsub(" #@linksto x", "", code, fixed = TRUE), collapse = "\n")
  )
})

testthat::test_that("@linksto makes a line being returned for an affected binding", {
  code <- "
  a <- 1 # @linksto b
  b <- 2
  "
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    paste("a <- 1", "b <- 2", sep = "\n")
  )
})

testthat::test_that(
  "@linksto returns the line for an affected binding
  even if the object did not exist in the same iteration of eval_code",
  {
    code <- c(
      "a <- 1 # @linksto b",
      "b <- 2"
    )
    tdata <- eval_code(teal_data(), code)
    testthat::expect_identical(
      get_code(tdata, datanames = "b"),
      paste("a <- 1", "b <- 2", sep = "\n")
    )
  }
)

testthat::test_that(
  "lines affecting parent evaluated after co-occurrence are not included in output when using @linksto",
  {
    code <- c(
      "a <- 1 ",
      "b <- 2 # @linksto a",
      "a <- a + 1",
      "b <- b + 1"
    )
    tdata <- eval_code(teal_data(), code)
    testthat::expect_identical(
      get_code(tdata, datanames = "a"),
      paste("a <- 1", "b <- 2", "a <- a + 1", sep = "\n")
    )
    testthat::expect_identical(
      get_code(tdata, datanames = "b"),
      paste("b <- 2", "b <- b + 1", sep = "\n")
    )
  }
)

testthat::test_that(
  "@linksto gets extracted if it's a side-effect on a dependent object (even of a dependent object)",
  {
    code <- "
      iris[1:5, ] -> iris2
      iris_head <- head(iris) # @linksto iris3
      iris3 <- iris_head[1, ] # @linksto iris2
      classes <- lapply(iris2, class)
    "
    tdata <- eval_code(teal_data(), code)
    testthat::expect_identical(
      get_code(tdata, datanames = "classes"),
      paste("iris2 <- iris[1:5, ]",
        "iris_head <- head(iris)",
        "iris3 <- iris_head[1, ]",
        "classes <- lapply(iris2, class)",
        sep = "\n"
      )
    )
  }
)

# functions -------------------------------------------------------------------------------------------------------

testthat::test_that("ignores occurrence in a function definition", {
  code <- c(
    "b <- 2",
    "foo <- function(b) { b <- b + 2 }"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    "b <- 2"
  )
  testthat::expect_identical(
    get_code(tdata, datanames = "foo"),
    "foo <- function(b) {\n    b <- b + 2\n}"
  )
})

testthat::test_that("ignores occurrence in a function definition that has function in it", {
  code <- c(
    "b <- 2",
    "foo <- function(b) { function(c) {b <- c + 2 }}"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    "b <- 2"
  )
  testthat::expect_identical(
    get_code(tdata, datanames = "foo"),
    "foo <- function(b) {\n    function(c) {\n        b <- c + 2\n    }\n}"
  )
})

testthat::test_that("ignores occurrence in a function definition in lapply", {
  code <- c(
    "a <- list(a = 1, b = 2, c = 3)",
    "b <- lapply(a, FUN = function(x) { x <- x + 1 })",
    "b <- Filter(function(x) x > 2, b)",
    "x <- 1",
    "identity(x)"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "x"),
    "x <- 1"
  )
})

testthat::test_that("does not ignore occurrence in function body if object exsits in env", {
  skip("This is not urgent and can be ommitted with @linksto tag.")
  code <- c(
    "a <- list(a = 1, b = 2, c = 3)",
    "p <- 5", # This is not extracted, even though is used in the next line.
    "b <- lapply(a, FUN = function(x) { x <- x + p })",
    "b <- Filter(function(x) x > 2, b)"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    paste(code, sep = "\n")
  )
})

testthat::test_that("ignores occurrence in function definition without { curly brackets", {
  code <- c(
    "b <- 2",
    "foo <- function(b) b <- b + 2 "
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "foo"),
    "foo <- function(b) b <- b + 2"
  )
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    "b <- 2"
  )
})

testthat::test_that("detects occurrence of the function object", {
  code <- c(
    "a <- 1",
    "b <- 2",
    "foo <- function(b) { b <- b + 2 }",
    "b <- foo(a)"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    paste("a <- 1", "b <- 2", "foo <- function(b) {\n    b <- b + 2\n}", "b <- foo(a)", sep = "\n")
  )
})

testthat::test_that("detects occurrence of a function definition when a formal is named the same as a function", {
  code <- c(
    "x <- 1",
    "foo <- function(foo = 1) 'text'",
    "a <- foo(x)"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "a"),
    paste("x <- 1", "foo <- function(foo = 1) \"text\"", "a <- foo(x)", sep = "\n")
  )
})

testthat::test_that("detects occurrence of a function definition with a @linksto usage", {
  code <- c(
    "
        foo <- function() {
          env <- parent.frame()
          env$x <- 0
        }",
    "foo() # @linksto x",
    "y <- x"
  )
  tdata <- teal_data(code = code)
  testthat::expect_identical(
    get_code(tdata, datanames = "x"),
    paste(
      warning_message,
      "foo <- function() {\n    env <- parent.frame()\n    env$x <- 0\n}\nfoo()",
      sep = "\n"
    )
  )
})
# $ ---------------------------------------------------------------------------------------------------------------

testthat::test_that("understands $ usage and do not treat rhs of $ as objects (only lhs)", {
  code <- c(
    "x <- data.frame(a = 1:3)",
    "a <- data.frame(y = 1:3)",
    "a$x <- a$y",
    "a$x <- a$x + 2",
    "a$x <- x$a"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "x"),
    "x <- data.frame(a = 1:3)"
  )
  testthat::expect_identical(
    get_code(tdata, datanames = "a"),
    paste("x <- data.frame(a = 1:3)",
      "a <- data.frame(y = 1:3)",
      "a$x <- a$y",
      "a$x <- a$x + 2",
      "a$x <- x$a",
      sep = "\n"
    )
  )
})

testthat::test_that("detects cooccurrence properly even if all objects are on lhs", {
  code <- c(
    "a <- 1",
    "b <- list(c = 2)",
    "b[[a]] <- 3"
  )
  tdata <- eval_code(teal_data(), code)
  testthat::expect_identical(
    get_code(tdata, datanames = "b"),
    paste(code, collapse = "\n")
  )
})


# @ ---------------------------------------------------------------------------------------------------------------

testthat::test_that("understands @ usage and do not treat rhs of @ as objects (only lhs)", {
  code <- c(
    "setClass('aclass', slots = c(a = 'numeric', x = 'numeric', y = 'numeric')) # @linksto a x",
    "x <- new('aclass', a = 1:3, x = 1:3, y = 1:3)",
    "a <- new('aclass', a = 1:3, x = 1:3, y = 1:3)",
    "a@x <- a@y",
    "a@x <- a@x + 2",
    "a@x <- x@a"
  )
  tdata <- teal_data(x = 1, a = 1, code = code)
  testthat::expect_identical(
    get_code(tdata, datanames = "x"),
    paste(
      warning_message,
      'setClass("aclass", slots = c(a = "numeric", x = "numeric", y = "numeric"))',
      'x <- new("aclass", a = 1:3, x = 1:3, y = 1:3)',
      sep = "\n"
    )
  )
  testthat::expect_identical(
    get_code(tdata, datanames = "a"),
    paste(
      warning_message,
      'setClass("aclass", slots = c(a = "numeric", x = "numeric", y = "numeric"))',
      'x <- new("aclass", a = 1:3, x = 1:3, y = 1:3)',
      'a <- new("aclass", a = 1:3, x = 1:3, y = 1:3)',
      "a@x <- a@y",
      "a@x <- a@x + 2",
      "a@x <- x@a",
      sep = "\n"
    )
  )
})



# libraries -------------------------------------------------------------------------------------------------------

testthat::test_that("library() and require() are always returned", {
  code <- c(
    "set.seed(1)",
    "library(scda)",
    "require(dplyr)",
    "library(MultiAssayExperiment)",
    "x <- 5",
    "y <- 6"
  )
  tdata <- teal_data(x = 5, y = 6, code = code)
  testthat::expect_identical(
    get_code(tdata, datanames = "x"),
    paste(
      warning_message,
      "library(scda)",
      "require(dplyr)",
      "library(MultiAssayExperiment)",
      "x <- 5",
      sep = "\n"
    )
  )
})


# data() ----------------------------------------------------------------------------------------------------------

testthat::test_that("data() call is returned when data name is provided as is", {
  code <- c(
    "set.seed(1)",
    "library(scda)",
    "require(dplyr)",
    "library(MultiAssayExperiment)",
    "data(miniACC, envir = environment())",
    "x <- miniACC"
  )
  tdata <- teal_data(x = 1, code = code)
  testthat::expect_identical(
    get_code(tdata, datanames = "x"),
    paste(
      warning_message,
      "library(scda)",
      "require(dplyr)",
      "library(MultiAssayExperiment)",
      "data(miniACC, envir = environment())",
      "x <- miniACC",
      sep = "\n"
    )
  )
})

testthat::test_that("data() call is returned when data name is provided as a character", {
  code <- c(
    "set.seed(1)",
    "library(scda)",
    "require(dplyr)",
    "library(MultiAssayExperiment)",
    "data('mtcars')",
    "z <- mtcars"
  )
  tdata <- teal_data(z = 1, code = code)
  testthat::expect_identical(
    get_code(tdata, datanames = "z"),
    paste(
      warning_message,
      "library(scda)",
      "require(dplyr)",
      "library(MultiAssayExperiment)",
      "data(\"mtcars\")",
      "z <- mtcars",
      sep = "\n"
    )
  )
})

Try the teal.data package in your browser

Any scripts or data that you put into this service are public.

teal.data documentation built on May 29, 2024, 8:03 a.m.