test_that("library permissions are validated before restore", { skip_on_os("windows") inaccessible <- renv_scope_tempfile() dir.create(inaccessible, mode = "0100") expect_false(renv_install_preflight_permissions(inaccessible)) }) test_that("restore() gives an error if no lockfile exists", { renv_tests_scope() expect_false(file.exists("renv.lock")) expect_error(restore()) }) test_that("we can restore packages after init", { skip_on_cran() renv_tests_scope("breakfast") init() libpath <- renv_paths_library() before <- list.files(libpath) unlink(renv_paths_library(), recursive = TRUE) restore() after <- list.files(libpath) expect_setequal(before, after) }) test_that("restore can recover when required packages are missing", { skip_on_cran() renv_tests_scope("breakfast") init() remove("oatmeal") snapshot(force = TRUE) unlink(renv_paths_library(), recursive = TRUE) restore() expect_true(renv_package_installed("oatmeal")) }) test_that("restore(clean = TRUE) removes packages not in the lockfile", { renv_tests_scope("oatmeal") init() renv_scope_options(renv.config.auto.snapshot = FALSE) install("bread") expect_true(renv_package_installed("bread")) restore(clean = TRUE) expect_false(renv_package_installed("bread")) }) test_that("renv.records can be used to override records during restore", { renv_tests_scope("bread") init() install("bread@0.1.0") snapshot() expect_equal(renv_package_version("bread"), "0.1.0") bread <- list(Package = "bread", Version = "1.0.0", Source = "CRAN") overrides <- list(bread = bread) renv_scope_options(renv.records = overrides) restore() expect_equal(renv_package_version("bread"), "1.0.0") }) test_that("install.staged works as expected", { renv_tests_scope("breakfast") init() library <- renv_paths_library(project = getwd()) install.opts <- list(breakfast = "--version") local({ renv_scope_options( renv.config.install.staged = TRUE, renv.config.install.transactional = TRUE, install.opts = install.opts ) renv_scope_envvars(RENV_PATHS_CACHE = renv_scope_tempfile()) unlink(renv_paths_library(), recursive = TRUE) expect_error(restore()) files <- list.files(library) expect_true(length(files) == 0L) }) local({ renv_scope_options( renv.config.install.staged = FALSE, renv.config.install.transactional = FALSE, install.opts = install.opts ) renv_scope_envvars(RENV_PATHS_CACHE = renv_scope_tempfile()) unlink(renv_paths_library(), recursive = TRUE) expect_error(restore()) files <- list.files(library) expect_true(length(files) != 0L) }) }) test_that("restore(lockfile = '/path/to/lockfile') works", { renv_tests_scope("bread") init() unlink(paths$library(), recursive = TRUE) restore(lockfile = "renv.lock") expect_true(renv_package_installed("bread")) unlink(paths$library(), recursive = TRUE) lockfile <- renv_lockfile_load(project = getwd()) restore(lockfile = "renv.lock") expect_true(renv_package_installed("bread")) }) test_that("restore(packages = <...>) works", { renv_tests_scope("breakfast") init() unlink(paths$library(), recursive = TRUE) restore(packages = "toast") expect_length(list.files(paths$library()), 2L) expect_true(renv_package_installed("bread")) expect_true(renv_package_installed("toast")) }) test_that("restore ignores packages of incompatible architecture", { renv_tests_scope(c("unixonly", "windowsonly")) init() if (renv_platform_unix()) { expect_true(renv_package_installed("unixonly")) expect_false(renv_package_installed("windowsonly")) lockfile <- renv_lockfile_read("renv.lock") package <- lockfile$Packages$unixonly expect_identical(package$OS_type, "unix") remove("unixonly") restore() expect_true(renv_package_installed("unixonly")) } else { expect_true(renv_package_installed("windowsonly")) expect_false(renv_package_installed("unixonly")) lockfile <- renv_lockfile_read("renv.lock") package <- lockfile$Packages$windowsonly expect_identical(package$OS_type, "windows") remove("windowsonly") restore() expect_true(renv_package_installed("windowsonly")) } }) test_that("restore handled records without version set", { renv_tests_scope() # create dummy lockfile snapshot() # read lockfile and add record without version lockfile <- renv_lockfile_load(project = getwd()) lockfile$Packages$bread <- list(Package = "bread", Source = "Repository") renv_lockfile_save(lockfile, project = getwd()) # try to restore restore() # check for success expect_true(renv_package_installed("bread")) expect_equal(renv_package_version("bread"), "1.0.0") }) test_that("restore doesn't re-use active library paths", { renv_tests_scope() renv_scope_options(renv.settings.snapshot.type = "all") lib1 <- renv_scope_tempfile("lib1") lib2 <- renv_scope_tempfile("lib2") ensure_directory(c(lib1, lib2)) .libPaths(c(lib2, .libPaths())) install("bread", library = lib2) expect_true(renv_package_installed("bread", lib.loc = lib2)) lockfile <- snapshot(library = lib2, lockfile = NULL) restore(library = lib1, lockfile = lockfile) expect_true(renv_package_installed("bread", lib.loc = lib1)) }) test_that("restore(exclude = <...>) excludes as expected", { renv_tests_scope("breakfast") init() remove(c("bread", "breakfast", "oatmeal", "toast")) restore(exclude = "breakfast") expect_false(renv_package_installed("breakfast")) }) test_that("restore works with explicit Source", { renv_tests_scope("breakfast") init() renv_scope_envvars( RENV_PATHS_LOCAL = NULL, RENV_PATHS_CACHE = NULL ) record <- list( Package = "skeleton", Version = "1.0.0", Source = renv_tests_path("local/skeleton/skeleton_1.0.0.tar.gz") ) renv_test_retrieve(record) lockfile <- renv_lockfile_init(project = getwd()) lockfile$Packages <- list(skeleton = record) renv_lockfile_write(lockfile, file = "renv.lock") remove("skeleton") restore() expect_true(renv_package_installed("skeleton")) expect_true(renv_package_version("skeleton") == "1.0.0") }) test_that("restore() restores packages with broken symlinks", { skip_on_cran() renv_scope_options(renv.settings.cache.enabled = TRUE) renv_tests_scope("breakfast") init() # check it's installed pkgpath <- renv_package_find("breakfast") expect_true(renv_file_exists(pkgpath)) # break the cache record <- list(Package = "breakfast", Version = "1.0.0") cachepath <- renv_cache_find(record) unlink(cachepath, recursive = TRUE) # check that it's broken expect_true(renv_file_broken(pkgpath)) # try to restore restore() # check that we're happy again expect_false(renv_file_broken(pkgpath)) expect_true(renv_file_exists(pkgpath)) expect_true(renv_package_installed("breakfast")) }) test_that("restore() also installs packages with broken symlinks", { skip_on_cran() skip_on_os("windows") renv_scope_options(renv.settings.cache.enabled = TRUE) project <- renv_tests_scope("breakfast") init() pkgpaths <- list.files( path = renv_paths_library(project = project), full.names = TRUE ) links <- Sys.readlink(pkgpaths) expect_true(all(nzchar(links))) unlink(links, recursive = TRUE) expect_false(renv_package_installed("breakfast")) restore() expect_true(renv_package_installed("breakfast")) }) test_that("we can restore a package installed with a custom repository", { project <- renv_tests_scope(isolated = TRUE) init() url <- unname(getOption("repos")) local({ renv_scope_options(repos = character()) install("bread", repos = c(TEST = url), rebuild = TRUE) }) dcf <- renv_description_read(package = "bread") expect_equal(!!dcf$RemoteRepos, !!url) expect_equal(!!dcf$RemoteReposName, "TEST") writeLines("library(bread)", con = "_deps.R") local({ renv_scope_options(repos = character()) snapshot() }) suppressMessages(remove.packages("bread")) local({ renv_scope_options(repos = character()) restore(rebuild = TRUE) }) expect_true(renv_package_installed("bread")) }) test_that("restore uses custom repository URL from lockfile", { project <- renv_tests_scope(isolated = TRUE) init() # install bread from a named custom repo, with no global repos set url <- unname(getOption("repos")) local({ renv_scope_options(repos = character()) install("bread", repos = c(TEST = url)) }) # snapshot with no global repos writeLines("library(bread)", con = "_deps.R") local({ renv_scope_options(repos = character()) snapshot() }) # check lockfile: no global repos, but package record has the custom URL lockfile <- renv_lockfile_read("renv.lock") expect_length(lockfile$R$Repositories, 0L) expect_equal(lockfile$Packages$bread$Repository, url) # remove the package and restore with no global repos suppressMessages(remove.packages("bread")) expect_false(renv_package_installed("bread")) local({ renv_scope_options(repos = character()) restore() }) expect_true(renv_package_installed("bread")) }) test_that("restore(strict = TRUE) uses only the recorded repository URL", { project <- renv_tests_scope(isolated = TRUE) init() # install bread from a named custom repo, with no global repos set url <- unname(getOption("repos")) local({ renv_scope_options(repos = character()) install("bread", repos = c(TEST = url)) }) # snapshot with no global repos writeLines("library(bread)", con = "_deps.R") local({ renv_scope_options(repos = character()) snapshot() }) # verify lockfile has the custom URL lockfile <- renv_lockfile_read("renv.lock") expect_equal(lockfile$Packages$bread$Repository, url) # remove the package suppressMessages(remove.packages("bread")) expect_false(renv_package_installed("bread")) # strict restore with the correct URL should succeed local({ renv_scope_options(repos = character()) restore(strict = TRUE) }) expect_true(renv_package_installed("bread")) # now simulate a bad repository URL in the lockfile; # disable the cache so we can verify the download path fails suppressMessages(remove.packages("bread")) lockfile$Packages$bread$Repository <- "file:///nonexistent/repo" renv_lockfile_write(lockfile, "renv.lock") # strict restore with a bad URL should fail local({ renv_scope_options(repos = character(), renv.config.cache.enabled = FALSE) expect_error(restore(strict = TRUE)) }) expect_false(renv_package_installed("bread")) }) test_that("restore(strict = FALSE) falls back when the recorded URL is bad", { renv_tests_scope("bread") init() # doctor the lockfile with a bogus URL-valued Repository lockfile <- renv_lockfile_read("renv.lock") lockfile$Packages$bread$Repository <- "file:///nonexistent/repo" renv_lockfile_write(lockfile, "renv.lock") # remove the package and restore with the real repo still available; # strict = FALSE (default) should fall back and succeed remove("bread") expect_false(renv_package_installed("bread")) restore() expect_true(renv_package_installed("bread")) }) test_that("restore(strict = TRUE) does not affect name-valued Repository fields", { renv_tests_scope("bread") init() # bread was installed from the test repo; its lockfile record should have # Repository = "CRAN" (a name, not a URL) lockfile <- renv_lockfile_read("renv.lock") expect_equal(lockfile$Packages$bread$Repository, "CRAN") # remove and strict-restore; name-valued Repository should resolve # through the configured repos as usual remove("bread") expect_false(renv_package_installed("bread")) restore(strict = TRUE) expect_true(renv_package_installed("bread")) }) test_that("restore(strict = TRUE) resolves dependencies from standard repos", { project <- renv_tests_scope(isolated = TRUE) init() # install toast (depends on bread) from a custom-named repo url <- unname(getOption("repos")) local({ renv_scope_options(repos = character()) install("toast", repos = c(TEST = url)) }) # snapshot with no global repos writeLines("library(toast)", con = "_deps.R") local({ renv_scope_options(repos = character()) snapshot() }) # verify toast has a URL-valued Repository; bread should too lockfile <- renv_lockfile_read("renv.lock") expect_equal(lockfile$Packages$toast$Repository, url) expect_equal(lockfile$Packages$bread$Repository, url) # remove both packages and strict-restore; # both should be retrieved from the recorded URL remove(c("toast", "bread")) expect_false(renv_package_installed("toast")) expect_false(renv_package_installed("bread")) local({ renv_scope_options(repos = character()) restore(strict = TRUE) }) expect_true(renv_package_installed("toast")) expect_true(renv_package_installed("bread")) }) test_that("restore(strict = TRUE) still works when repos.override is set", { skip_on_cran() skip_on_windows() renv_tests_scope("bread") init() # doctor the lockfile with a bogus URL-valued Repository lockfile <- renv_lockfile_read("renv.lock") lockfile$Packages$bread$Repository <- "file:///nonexistent/repo" renv_lockfile_write(lockfile, "renv.lock") # remove the package remove("bread") expect_false(renv_package_installed("bread")) # validate current behavior: repos.override nullifies the Repository # field before strict sees it, so restore succeeds renv_scope_options(renv.config.repos.override = getOption("repos")) restore(packages = "bread", strict = TRUE, rebuild = TRUE) expect_true(renv_package_installed("bread")) }) test_that("the Repository field in a lockfile can be overridden", { skip_on_cran() skip_on_windows() # initialize project project <- renv_tests_scope("bread") init() # simulate bread having an external / unexpected repository url lockfile <- renv_lockfile_read("renv.lock") lockfile$Packages$bread$Repository <- path.expand("~") renv_lockfile_write(lockfile, "renv.lock") # try to force a specific repository URL via override renv_scope_options(renv.config.repos.override = getOption("repos")) remove("bread") restore(packages = "bread", rebuild = TRUE) expect_true(renv_package_installed("bread")) })