context('Caching') test_that('caching a variable that doesnt exist fails with correct message', { test_project <- tempfile('test_project') suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) var_to_cache <- "xxxx" # make sure it doesn't exist if (exists(var_to_cache, envir = .TargetEnv )) { rm(list=var_to_cache, envir = .TargetEnv) } # try to cache it expect_message(cache(var_to_cache, CODE = NULL, depends = NULL), "Does not exist in global environment and no code to create it") tidy_up() }) test_that('caching a variable not already in cache caches correctly', { test_project <- tempfile('test_project') suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) var_to_cache <- "xxxx" test_data <- data.frame(Names=c("a", "b", "c"), Ages=c(20,30,40)) assign(var_to_cache, test_data, envir = .TargetEnv) # Create a new cached version expect_message(cache(var_to_cache, CODE = NULL, depends = NULL), "Creating cache entry from global environment") # Remove it from Global Environment rm(list=var_to_cache, envir = .TargetEnv) # Load up from cache and check it's the same as what was originally created suppressMessages(load.project()) expect_equal(get(var_to_cache, envir = .TargetEnv) , test_data) tidy_up() }) test_that('caching a variable created from CODE caches correctly', { # CODE caching requires suggested package formatR skip_if_not_installed("formatR") test_project <- tempfile('test_project') suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) var_to_cache <- "xxxx" test_data <- data.frame(Names=c("a", "b", "c"), Ages=c(200,300,400)) # Create a cached version created from CODE expect_message(cache(var_to_cache, depends = NULL, CODE = { data.frame(Names=c("a", "b", "c"), Ages=c(200,300,400)) }), "Creating cache entry from CODE") # Remove it from Global Environment (rm will fail if it's not created from CODE) expect_error(rm(list=var_to_cache, envir = .TargetEnv), NA) # Load up from cache and check it's the same as what was originally created suppressMessages(load.project()) expect_equal(get(var_to_cache, envir = .TargetEnv) , test_data) tidy_up() }) test_that("caching a variable created from CODE using 'data.table' along with non-standard evaluation caches correctly", { # The 'data.table' package has a mechanism to check if the calling # environment is 'data.table' aware # (https://github.com/Rdatatable/data.table/blob/master/R/cedta.R) # and otherwise calls the corresponding 'data.frame' method for at least # the '[' operator instead. This is why the parent environment of the # environment where 'CODE' is evaluated in has to be the global # environment ('.TargetEnv'). skip_if_not_installed("data.table") # CODE caching requires suggested package formatR skip_if_not_installed("formatR") test_project <- tempfile("test_project") suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) var_to_cache <- "foo" expect_error( { suppressMessages(cache(var_to_cache, CODE = { bar <- data.table::data.table(baz = 1:12) bar[baz <= 6, ] })) }, NA ) expect_identical( get(var_to_cache, envir = .TargetEnv), data.table::data.table(baz = 1:6) ) tidy_up() }) test_that('re-caching is skipped when a cached variable hasnt changed', { test_project <- tempfile('test_project') suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) var_to_cache <- "xxxx" test_data <- data.frame(Names=c("a", "b", "c"), Ages=c(20,30,40)) assign(var_to_cache, test_data, envir = .TargetEnv) # Create initial cached version cache(var_to_cache, CODE = NULL, depends = NULL) initial_mtime <- file.info(file.path('cache', paste0(var_to_cache, ".RData")))$mtime # wait two seconds Sys.sleep(2) # Remove it from Global Environment rm(list=var_to_cache, envir = .TargetEnv) # Load up from cache and attempt to re-cache suppressMessages(load.project()) expect_message(cache(var_to_cache, CODE = NULL, depends = NULL), "Skipping cache update for") # Check that modification time hasn't changed new_mtime <- file.info(file.path('cache', paste0(var_to_cache, ".RData")))$mtime expect_equal(initial_mtime, new_mtime) tidy_up() }) test_that('re-caching is done again when a cached variable has changed', { test_project <- tempfile('test_project') suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) var_to_cache <- "xxxx" test_data <- data.frame(Names=c("a", "b", "c"), Ages=c(20,30,40)) assign(var_to_cache, test_data, envir = .TargetEnv) # Create initial cached version cache(var_to_cache, CODE = NULL, depends = NULL) initial_mtime <- file.info(file.path('cache', paste0(var_to_cache, ".RData")))$mtime # wait two seconds Sys.sleep(2) # Remove it from Global Environment rm(list=var_to_cache, envir = .TargetEnv) # Load up from cache suppressMessages(load.project()) # change the variable and attempt to re-cache test_data2 <- data.frame(Names=c("aaa", "b", "c"), Ages=c(20,30,40)) assign(var_to_cache, test_data2, envir = .TargetEnv) expect_message(cache(var_to_cache, CODE = NULL, depends = NULL), "Updating existing cache entry from global environment") # Check that modification time has changed new_mtime <- file.info(file.path('cache', paste0(var_to_cache, ".RData")))$mtime expect_false(isTRUE(all.equal(initial_mtime, new_mtime))) # Reload and check that re-cached value is different rm(list=var_to_cache, envir = .TargetEnv) suppressMessages(load.project()) expect_false(isTRUE(all.equal(get(var_to_cache, envir = .TargetEnv) , test_data))) tidy_up() }) test_that('re-caching fails with correct message if cached variable is not in global env', { test_project <- tempfile('test_project') suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) var_to_cache <- "xxxx" test_data <- data.frame(Names=c("a", "b", "c"), Ages=c(20,30,40)) assign(var_to_cache, test_data, envir = .TargetEnv) # Create initial cached version cache(var_to_cache, CODE = NULL, depends = NULL) initial_mtime <- file.info(file.path('cache', paste0(var_to_cache, ".RData")))$mtime # wait two seconds Sys.sleep(2) # Remove it from Global Environment rm(list=var_to_cache, envir = .TargetEnv) # Load up from cache suppressMessages(load.project()) # And remove it from Global Environment again rm(list=var_to_cache, envir = .TargetEnv) expect_message(cache(var_to_cache, CODE = NULL, depends = NULL), "Unable to update cache for") # Check that modification time hasn't changed new_mtime <- file.info(file.path('cache', paste0(var_to_cache, ".RData")))$mtime expect_equal(initial_mtime, new_mtime) tidy_up() }) test_that('re-caching a variable created from CODE only happens if code changes, not comments or white space', { # CODE caching requires suggested package formatR skip_if_not_installed("formatR") test_project <- tempfile('test_project') suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) var_to_cache <- "xxxx" test_data <- data.frame(Names=c("a", "b", "c"), Ages=c(20000000,300,400)) # Create a cached version created from CODE expect_message(cache(var_to_cache, depends = NULL, CODE = { data.frame(Names=c("a", "b", "c"), Ages=c(200,300,400)) }), "Creating cache entry from CODE") initial_mtime <- file.info(file.path('cache', paste0(var_to_cache, ".RData")))$mtime # wait two seconds Sys.sleep(2) # Remove it from Global Environment (rm will fail if it's not created from CODE) expect_error(rm(list=var_to_cache, envir = .TargetEnv), NA) # Load up from cache again suppressMessages(load.project()) # re-cache, this time adding some comments and whitespace, but not changing code # should skip re-caching expect_message(cache(var_to_cache, depends = NULL, CODE = { # New comments add in data.frame(Names=c("a", "b", "c"), Ages=c(200,300,400)) # but code remains the same # extra new lines added for good measure }), "Skipping cache update for ") # Check that modification time hasn't changed new_mtime <- file.info(file.path('cache', paste0(var_to_cache, ".RData")))$mtime expect_equal(initial_mtime, new_mtime) # re-cache agin, keeping the comments and whitespace, but changing the code expect_message(cache(var_to_cache, depends = NULL, CODE = { # New comments add in data.frame(Names=c("a", "b", "c"), Ages=c(20000000,300,400)) # but code remains the same # extra new lines added for good measure }), "Updating existing cache entry from CODE") # Check that modification time has also changed new_mtime <- file.info(file.path('cache', paste0(var_to_cache, ".RData")))$mtime expect_false(isTRUE(all.equal(initial_mtime, new_mtime))) # Finally check that the new code evaluated correctly expect_equal(get(var_to_cache, envir = .TargetEnv) , test_data) tidy_up() }) test_that('caching a variable with an underscore is not unnecessarily loaded next load.project()', { test_project <- tempfile('test_project') suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) var_to_cache <- "xx_xx" test_data <- data.frame(Names=c("a", "b", "c"), Ages=c(20,30,40)) assign(var_to_cache, test_data, envir = .TargetEnv) # Create a new cached version expect_message(cache(var_to_cache, CODE = NULL, depends = NULL), "Creating cache entry from global environment") # Load up from cache and check no message contains an x expect_message(load.project(), "[^[^x]+$") tidy_up() }) test_that('cache and memory is cleared correctly', { test_project <- tempfile('test_project') suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) var_to_cache <- "xxxx" test_data <- data.frame(Names=c("a", "b", "c"), Ages=c(20,30,40)) assign(var_to_cache, test_data, envir = .TargetEnv) # Create a new cached version expect_message(cache("xxxx"), "Creating cache entry from global environment") # clear from memory clear("xxxx", force = TRUE) # Read the config and set config$sticky_variables cfg <- .read.config() cfg$sticky_variables <- var_to_cache .save.config(cfg) # variable is loaded into memory when load.project is run expect_message(load.project(), "Loading cached data set: xxxx") # variable exists in cache expect_message(cache(), "Variable: xxxx") # variable should still be in memory expect_message(clear(), "not cleared: config xxxx") # variable exists in memory expect_true(exists("xxxx")) # delete variable from cache expect_message(clear.cache(), "Removed successfully") # variable does not exist in memory, should have been forced cleared expect_true(!exists("xxxx")) # shouldn't be anything in the cache expect_message(cache(), "No variables in cache") tidy_up() }) test_that('multiple items are cleared correctly from the cache', { test_project <- tempfile('test_project') suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) assign("xxx", 10, envir = .TargetEnv) assign("yyy", 20, envir = .TargetEnv) assign("zzz", 30, envir = .TargetEnv) # Create cached version of each of these expect_message(cache("xxx"), "Creating cache entry from global environment") expect_message(cache("yyy"), "Creating cache entry from global environment") expect_message(cache("zzz"), "Creating cache entry from global environment") # clear two variables from cache expect_message(clear.cache("yyy", "zzz"), "Removed successfully ") # Check variables not in global env expect_true(!exists("yyy")) expect_true(!exists("zzz")) # clear everything and reload project clear(force = TRUE) # variable is loaded into memory when load.project is run expect_message(load.project(), "Loading cached data set: xxx") expect_equal(xxx, 10) # variable exists in memory expect_true(exists("xxx")) # Check variables still not in global env expect_true(!exists("yyy")) expect_true(!exists("zzz")) tidy_up() }) test_that('caching a variable using CODE doesnt leave variables in globalenv', { # CODE caching requires suggested package formatR skip_if_not_installed("formatR") test_project <- tempfile('test_project') suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) var_to_cache <- "xxxx" # make sure it doesn't exist if (exists(var_to_cache, envir = .TargetEnv )) { rm(list=var_to_cache, envir = .TargetEnv) } # set an environment variable in global env assign("yyy", 10, envir = .TargetEnv) # create a cached variable cache(var_to_cache, CODE = { aaa <- 10 bbb <- 10 aaa*bbb*yyy }) # check variable calculates correctly expect_equal(get(var_to_cache), 10*10*10) # Make sure local variables don't exist in global env expect_true(!exists("aaa")) expect_true(!exists("bbb")) tidy_up() }) test_that('caching a variable already in cache with no hash file re-caches correctly', { test_project <- tempfile('test_project') suppressMessages(create.project(test_project)) on.exit(unlink(test_project, recursive = TRUE), add = TRUE) oldwd <- setwd(test_project) on.exit(setwd(oldwd), add = TRUE) var_to_cache <- "xxxx" test_data <- data.frame(Names=c("a", "b", "c"), Ages=c(20,30,40)) assign(var_to_cache, test_data, envir = .TargetEnv) # Create a new cached version expect_message(cache(var_to_cache, CODE = NULL, depends = NULL), "Creating cache entry from global environment") # delete the hash file unlink(file.path("cache", paste0(var_to_cache, ".hash"))) # Create another cached version: should be created from new again expect_message(cache(var_to_cache, CODE = NULL, depends = NULL), "Creating cache entry from global environment") # Check that the hash file exists expect_true(file.exists(file.path("cache", paste0(var_to_cache, ".hash")))) # Load up from cache and check it's the same as what was originally created suppressMessages(load.project()) expect_equal(get(var_to_cache, envir = .TargetEnv) , test_data) tidy_up() })