library(testthat) ## Helper functions to create a new package, with some ## R code, and install it temporarily install_quietly <- TRUE with_wd <- function(dir, expr) { wd <- getwd() on.exit(setwd(wd)) setwd(dir) eval(substitute(expr), envir = parent.frame()) } build_pkg <- function(path, pkg_file = NULL) { if (!file.exists(path)) stop("path does not exist") pkg_name <- basename(path) if (is.null(pkg_file)) { pkg_file <- file.path(dirname(path), paste0(pkg_name, "_1.0.tar.gz")) } with_wd(dirname(path), tar(basename(pkg_file), pkg_name, compression = "gzip")) pkg_file } install_tmp_pkg <- function(..., pkg_name, lib_dir, imports = "R6") { if (!file.exists(lib_dir)) stop("lib_dir does not exist") if (!is.character(pkg_name) || length(pkg_name) != 1) { stop("pkg_name is not a string") } ## Create a directory that will contain the source package src_dir <- tempfile() on.exit(try(unlink(src_dir, recursive = TRUE), silent = TRUE), add = TRUE) dir.create(src_dir) ## Create source package, need a non-empty environment, ## otherwise package.skeleton fails tmp_env <- new.env() assign("f", function(x) x, envir = tmp_env) suppressMessages(package.skeleton(pkg_name, path = src_dir, envir = tmp_env)) pkg_dir <- file.path(src_dir, pkg_name) ## Make it installable: remove man, add R6 dependency unlink(file.path(pkg_dir, "man"), recursive = TRUE) cat("Imports: ", paste(imports, collapse = ", "), "\n", file = file.path(pkg_dir, "DESCRIPTION"), append = TRUE) cat(paste0("import(", imports, ")"), sep="\n", file = file.path(pkg_dir, "NAMESPACE"), append = TRUE) ## Put the code in it, dput is noisy, so we need to redirect it to ## temporary file exprs <- list(...) unlink(file.path(pkg_dir, "R"), recursive = TRUE) dir.create(file.path(pkg_dir, "R")) code_file <- file.path(pkg_dir, "R", "code.R") tmp_file <- tempfile() on.exit(try(unlink(tmp_file), silent = TRUE), add = TRUE) sapply(exprs, function(x) cat(deparse(dput(x, file = tmp_file)), file = code_file, append = TRUE, "\n", sep="\n")) ## Build it pkg_file <- build_pkg(pkg_dir) ## Install it into the supplied lib_dir install.packages(pkg_file, lib = lib_dir, repos = NULL, type = "source", quiet = install_quietly) } with_libpath <- function(lib_path, ...) { cur_lib_path <- .libPaths() on.exit(.libPaths(cur_lib_path), add = TRUE) .libPaths(c(lib_path, cur_lib_path)) exprs <- c(as.list(match.call(expand.dots = FALSE)$...)) sapply(exprs, eval, envir = parent.frame()) } ## Each expression in ... is put in a package, that ## is installed and loaded. The package name is given by ## argument name. The packages will be installed in lib_dir, load_tmp_pkgs <- function(..., lib_dir = tempfile(), imports = "R6") { if (!file.exists(lib_dir)) dir.create(lib_dir) exprs <- c(as.list(match.call(expand.dots = FALSE)$...)) for (i in seq_along(exprs)) { expr <- exprs[[i]] name <- names(exprs)[i] install_tmp_pkg(expr, pkg_name = name, lib_dir = lib_dir, imports = imports) ## Unload everything if an error happens on.exit(try(unloadNamespace(name), silent = TRUE), add = TRUE) with_libpath(lib_dir, suppressMessages(library(name, quietly = TRUE, character.only = TRUE))) on.exit() } invisible(NULL) } test_that("inheritance works across packages", { ## Temporary lib_dir lib_dir <- tempfile() on.exit(try(unlink(lib_dir, recursive = TRUE), silent = TRUE), add = TRUE) on.exit(unloadNamespace("R6testB"), add = TRUE) on.exit(unloadNamespace("R6testA"), add = TRUE) ## Make sure that we get the latest versions of them try(unloadNamespace("R6testB"), silent = TRUE) try(unloadNamespace("R6testA"), silent = TRUE) load_tmp_pkgs(lib_dir = lib_dir, ## Code to put in package 'R6testA' R6testA = { AC <- R6Class( public = list( x = 1 ) ) }, ## Code to put in package 'R6testB' R6testB = { BC <- R6Class( inherit = R6testA::AC, public = list( y = 2 ) ) } ) ## Now ready for the tests B <- BC$new() expect_equal(B$x, 1) expect_equal(B$y, 2) }) test_that("more inheritance", { ## Temporary lib_dir lib_dir <- tempfile() on.exit(try(unlink(lib_dir, recursive = TRUE), silent = TRUE), add = TRUE) on.exit(unloadNamespace("pkgB"), add = TRUE) on.exit(unloadNamespace("pkgA"), add = TRUE) ## Make sure that we get the latest versions of them try(unloadNamespace("pkgB"), silent = TRUE) try(unloadNamespace("pkgA"), silent = TRUE) load_tmp_pkgs(lib_dir = lib_dir, pkgA = { funA <- function() { message("Called funA in pkgA 1.0") } AC <- R6Class("AC", public = list( versionString = "pkgA 1.0", fun = function() { message("This object was created in pkgA 1.0") message(paste0("The object has versionString ", self$versionString)) funA() } ) ) } ) load_tmp_pkgs(lib_dir = lib_dir, imports = "pkgA", pkgB = { B <- pkgA::AC$new() } ) expect_message(B$fun(), "created in pkgA 1.0") expect_message(B$fun(), "versionString pkgA 1.0") expect_message(B$fun(), "Called funA in pkgA 1.0") unloadNamespace("pkgB") unloadNamespace("pkgA") load_tmp_pkgs(lib_dir = lib_dir, pkgA = { funA <- function() { message("Called funA in pkgA 2.0") } AC <- R6Class("AC", public = list( versionString = "pkgA 2.0", fun = function() { message("This object was created in pkgA 2.0") message(paste0("The object has versionString ", self$versionString)) funA() } ) ) } ) with_libpath(lib_dir, library(pkgB)) expect_message(B$fun(), "created in pkgA 1.0") expect_message(B$fun(), "versionString pkgA 1.0") expect_message(B$fun(), "Called funA in pkgA 2.0") })