diff --git a/DESCRIPTION b/DESCRIPTION index 07fc5ee0..d086e5c0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Encoding: UTF-8 Package: covr Title: Test Coverage for Packages -Version: 3.6.4.9000 +Version: 3.6.4.9001 Authors@R: c( person("Jim", "Hester", email = "james.f.hester@gmail.com", role = c("aut", "cre")), person("Willem", "Ligtenberg", role = "ctb"), @@ -68,7 +68,8 @@ Suggests: parallel, memoise, mockery, - covr + covr, + box (>= 1.2.0) License: MIT + file LICENSE VignetteBuilder: knitr RoxygenNote: 7.2.3 diff --git a/NEWS.md b/NEWS.md index 82315a7d..1d6f97a1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # covr (development version) +* Added support for `klmr/box` modules. This works best with `file_coverage()`. (@radbasa, #491) + # covr 3.6.4 * Fix for a failing test on CRAN diff --git a/R/R6.R b/R/R6.R index 91ce1ace..d2b6e914 100644 --- a/R/R6.R +++ b/R/R6.R @@ -2,18 +2,22 @@ replacements_R6 <- function(env) { unlist(recursive = FALSE, eapply(env, all.names = TRUE, function(obj) { if (inherits(obj, "R6ClassGenerator")) { - unlist(recursive = FALSE, eapply(obj, - function(o) { - if (inherits(o, "list")) { - lapply(names(o), - function(f_name) { - f <- get(f_name, o) - if (inherits(f, "function")) { - replacement(f_name, env = env, target_value = f) - } -}) + traverse_R6(obj, env) } })) } - })) + +traverse_R6 <- function(obj, env) { + unlist(recursive = FALSE, eapply(obj, + function(o) { + if (inherits(o, "list")) { + lapply(names(o), + function(f_name) { + f <- get(f_name, o) + if (inherits(f, "function")) { + replacement(f_name, env = env, target_value = f) + } + }) + } + })) } diff --git a/R/box.R b/R/box.R new file mode 100644 index 00000000..da4c6cda --- /dev/null +++ b/R/box.R @@ -0,0 +1,32 @@ +replacements_box <- function(env) { + unlist(recursive = FALSE, eapply(env, all.names = TRUE, + function(obj) { + if (inherits(attr(obj, "spec"), "box$mod_spec")) { + obj_impl <- attr(obj, "namespace") + compact( + c( + lapply(ls(obj_impl), + function(f_name) { + f <- get(f_name, obj_impl) + if (inherits(f, "function")) { + replacement(f_name, env = obj, target_value = f) + } + } + ), + unlist(recursive = FALSE, + lapply(ls(obj_impl), + function(f_name) { + f <- get(f_name, obj_impl) + if (inherits(f, "R6ClassGenerator")) { + traverse_R6(f, obj) + } + } + ) + ) + ) + ) + } + } + ) + ) +} diff --git a/R/covr.R b/R/covr.R index 93a09b5c..106d8f03 100644 --- a/R/covr.R +++ b/R/covr.R @@ -93,6 +93,7 @@ trace_environment <- function(env) { replacements_S4(env), replacements_RC(env), replacements_R6(env), + replacements_box(env), lapply(ls(env, all.names = TRUE), replacement, env = env))) lapply(the$replacements, replace) diff --git a/tests/testthat/Testbox/app/app.R b/tests/testthat/Testbox/app/app.R new file mode 100644 index 00000000..a03a3dfa --- /dev/null +++ b/tests/testthat/Testbox/app/app.R @@ -0,0 +1,8 @@ +options(box.path = file.path(getwd())) +# remove box cache +loaded_mods <- loadNamespace("box")$loaded_mods +rm(list = ls(loaded_mods), envir = loaded_mods) + +box::use( + app/modules/module +) diff --git a/tests/testthat/Testbox/app/modules/module.R b/tests/testthat/Testbox/app/modules/module.R new file mode 100644 index 00000000..2fa4982f --- /dev/null +++ b/tests/testthat/Testbox/app/modules/module.R @@ -0,0 +1,14 @@ +#' an example function +#' +#' @export +a <- function(x) { + if (x <= 1) { + 1 + } else { + 2 + } +} + +private_function <- function(x) { + x ^ 2 +} diff --git a/tests/testthat/Testbox/tests/testthat.R b/tests/testthat/Testbox/tests/testthat.R new file mode 100644 index 00000000..59bf8cce --- /dev/null +++ b/tests/testthat/Testbox/tests/testthat.R @@ -0,0 +1,8 @@ +options(box.path = file.path(getwd())) +# remove box cache +loaded_mods <- loadNamespace("box")$loaded_mods +rm(list = ls(loaded_mods), envir = loaded_mods) + +library(testthat) + +test_dir("tests/testthat") diff --git a/tests/testthat/Testbox/tests/testthat/test-module.R b/tests/testthat/Testbox/tests/testthat/test-module.R new file mode 100644 index 00000000..1a8b092c --- /dev/null +++ b/tests/testthat/Testbox/tests/testthat/test-module.R @@ -0,0 +1,23 @@ +box::use( + testthat[test_that, expect_equal] +) + +box::use( + app/modules/module +) + +impl <- attr(module, "namespace") + +test_that("regular function `a` works as expected", { + expect_equal(module$a(1), 1) + expect_equal(module$a(2), 2) + expect_equal(module$a(3), 2) + expect_equal(module$a(4), 2) + expect_equal(module$a(0), 1) +}) + +test_that("private function works as expected", { + expect_equal(impl$private_function(2), 4) + expect_equal(impl$private_function(3), 9) + expect_equal(impl$private_function(4), 16) +}) diff --git a/tests/testthat/Testbox_R6/app/app.R b/tests/testthat/Testbox_R6/app/app.R new file mode 100644 index 00000000..1930366b --- /dev/null +++ b/tests/testthat/Testbox_R6/app/app.R @@ -0,0 +1,8 @@ +options(box.path = file.path(getwd())) +# remove box cache +loaded_mods <- loadNamespace("box")$loaded_mods +rm(list = ls(loaded_mods), envir = loaded_mods) + +box::use( + app/modules/moduleR6 +) diff --git a/tests/testthat/Testbox_R6/app/modules/moduleR6.R b/tests/testthat/Testbox_R6/app/modules/moduleR6.R new file mode 100644 index 00000000..9c2876c3 --- /dev/null +++ b/tests/testthat/Testbox_R6/app/modules/moduleR6.R @@ -0,0 +1,11 @@ +#' @export +TestR6 <- R6::R6Class("TestR6", # nolint + public = list( + show = function(x) { + 1 + 3 + }, + print2 = function(x) { + 1 + 2 + } + ) +) diff --git a/tests/testthat/Testbox_R6/tests/testthat.R b/tests/testthat/Testbox_R6/tests/testthat.R new file mode 100644 index 00000000..59bf8cce --- /dev/null +++ b/tests/testthat/Testbox_R6/tests/testthat.R @@ -0,0 +1,8 @@ +options(box.path = file.path(getwd())) +# remove box cache +loaded_mods <- loadNamespace("box")$loaded_mods +rm(list = ls(loaded_mods), envir = loaded_mods) + +library(testthat) + +test_dir("tests/testthat") diff --git a/tests/testthat/Testbox_R6/tests/testthat/test-moduleR6.R b/tests/testthat/Testbox_R6/tests/testthat/test-moduleR6.R new file mode 100644 index 00000000..f36608aa --- /dev/null +++ b/tests/testthat/Testbox_R6/tests/testthat/test-moduleR6.R @@ -0,0 +1,23 @@ +box::use( + testthat[test_that, expect_equal, expect_s3_class] +) + +box::use( + app/modules/moduleR6 +) + +test_that("TestR6 class can be instantiated", { + skip_if(is_r_devel()) + t1 <- moduleR6$TestR6$new() # nolint + + expect_s3_class(t1, "R6") + expect_s3_class(t1, "TestR6") + }) + +test_that("TestR6 Methods can be evaluated", { + skip_if(is_r_devel()) + t1 <- moduleR6$TestR6$new() # nolint + + expect_equal(t1$show(), 4) + expect_equal(print(t1$print2()), 3) +}) diff --git a/tests/testthat/Testbox_attached_modules_functions/app/app.R b/tests/testthat/Testbox_attached_modules_functions/app/app.R new file mode 100644 index 00000000..a03a3dfa --- /dev/null +++ b/tests/testthat/Testbox_attached_modules_functions/app/app.R @@ -0,0 +1,8 @@ +options(box.path = file.path(getwd())) +# remove box cache +loaded_mods <- loadNamespace("box")$loaded_mods +rm(list = ls(loaded_mods), envir = loaded_mods) + +box::use( + app/modules/module +) diff --git a/tests/testthat/Testbox_attached_modules_functions/app/modules/module.R b/tests/testthat/Testbox_attached_modules_functions/app/modules/module.R new file mode 100644 index 00000000..33cc2c07 --- /dev/null +++ b/tests/testthat/Testbox_attached_modules_functions/app/modules/module.R @@ -0,0 +1,19 @@ +#' an example function +#' +#' @export +a <- function(x) { + if (x <= 1) { + 1 + } else { + 2 + } +} + +#' @export +b <- function(x) { + return(x * 2) +} + +private_function <- function(x) { + x ^ 2 +} diff --git a/tests/testthat/Testbox_attached_modules_functions/tests/testthat.R b/tests/testthat/Testbox_attached_modules_functions/tests/testthat.R new file mode 100644 index 00000000..59bf8cce --- /dev/null +++ b/tests/testthat/Testbox_attached_modules_functions/tests/testthat.R @@ -0,0 +1,8 @@ +options(box.path = file.path(getwd())) +# remove box cache +loaded_mods <- loadNamespace("box")$loaded_mods +rm(list = ls(loaded_mods), envir = loaded_mods) + +library(testthat) + +test_dir("tests/testthat") diff --git a/tests/testthat/Testbox_attached_modules_functions/tests/testthat/test-aliased_functions.R b/tests/testthat/Testbox_attached_modules_functions/tests/testthat/test-aliased_functions.R new file mode 100644 index 00000000..f5865063 --- /dev/null +++ b/tests/testthat/Testbox_attached_modules_functions/tests/testthat/test-aliased_functions.R @@ -0,0 +1,15 @@ +box::use( + testthat[test_that, expect_equal] +) + +box::use( + app/modules/module[x = a] +) + +test_that("attached regular function `a` works as expected", { + expect_equal(x(1), 1) + expect_equal(x(2), 2) + expect_equal(x(3), 2) + expect_equal(x(4), 2) + expect_equal(x(0), 1) +}) diff --git a/tests/testthat/Testbox_attached_modules_functions/tests/testthat/test-aliased_modules.R b/tests/testthat/Testbox_attached_modules_functions/tests/testthat/test-aliased_modules.R new file mode 100644 index 00000000..4c84d7e9 --- /dev/null +++ b/tests/testthat/Testbox_attached_modules_functions/tests/testthat/test-aliased_modules.R @@ -0,0 +1,15 @@ +box::use( + testthat[test_that, expect_equal] +) + +box::use( + x = app/modules/module +) + +test_that("attached regular function `a` works as expected", { + expect_equal(x$a(1), 1) + expect_equal(x$a(2), 2) + expect_equal(x$a(3), 2) + expect_equal(x$a(4), 2) + expect_equal(x$a(0), 1) +}) diff --git a/tests/testthat/Testbox_attached_modules_functions/tests/testthat/test-attached_functions.R b/tests/testthat/Testbox_attached_modules_functions/tests/testthat/test-attached_functions.R new file mode 100644 index 00000000..034e823a --- /dev/null +++ b/tests/testthat/Testbox_attached_modules_functions/tests/testthat/test-attached_functions.R @@ -0,0 +1,15 @@ +box::use( + testthat[test_that, expect_equal] +) + +box::use( + app/modules/module[a] +) + +test_that("attached regular function `a` works as expected", { + expect_equal(a(1), 1) + expect_equal(a(2), 2) + expect_equal(a(3), 2) + expect_equal(a(4), 2) + expect_equal(a(0), 1) +}) diff --git a/tests/testthat/Testbox_attached_modules_functions/tests/testthat/test-three_dots.R b/tests/testthat/Testbox_attached_modules_functions/tests/testthat/test-three_dots.R new file mode 100644 index 00000000..5f3ace81 --- /dev/null +++ b/tests/testthat/Testbox_attached_modules_functions/tests/testthat/test-three_dots.R @@ -0,0 +1,21 @@ +box::use( + testthat[test_that, expect_equal] +) + +box::use( + app/modules/module[...] +) + +test_that("attached regular function `a` works as expected", { + expect_equal(a(1), 1) + expect_equal(a(2), 2) + expect_equal(a(3), 2) + expect_equal(a(4), 2) + expect_equal(a(0), 1) +}) + +test_that("attached regular function `b` works as expected", { + expect_equal(b(1), 2) + expect_equal(b(2), 4) + expect_equal(b(3), 6) +}) diff --git a/tests/testthat/Testbox_attached_modules_functions_R6/app/app.R b/tests/testthat/Testbox_attached_modules_functions_R6/app/app.R new file mode 100644 index 00000000..1930366b --- /dev/null +++ b/tests/testthat/Testbox_attached_modules_functions_R6/app/app.R @@ -0,0 +1,8 @@ +options(box.path = file.path(getwd())) +# remove box cache +loaded_mods <- loadNamespace("box")$loaded_mods +rm(list = ls(loaded_mods), envir = loaded_mods) + +box::use( + app/modules/moduleR6 +) diff --git a/tests/testthat/Testbox_attached_modules_functions_R6/app/modules/moduleR6.R b/tests/testthat/Testbox_attached_modules_functions_R6/app/modules/moduleR6.R new file mode 100644 index 00000000..9c2876c3 --- /dev/null +++ b/tests/testthat/Testbox_attached_modules_functions_R6/app/modules/moduleR6.R @@ -0,0 +1,11 @@ +#' @export +TestR6 <- R6::R6Class("TestR6", # nolint + public = list( + show = function(x) { + 1 + 3 + }, + print2 = function(x) { + 1 + 2 + } + ) +) diff --git a/tests/testthat/Testbox_attached_modules_functions_R6/tests/testthat.R b/tests/testthat/Testbox_attached_modules_functions_R6/tests/testthat.R new file mode 100644 index 00000000..59bf8cce --- /dev/null +++ b/tests/testthat/Testbox_attached_modules_functions_R6/tests/testthat.R @@ -0,0 +1,8 @@ +options(box.path = file.path(getwd())) +# remove box cache +loaded_mods <- loadNamespace("box")$loaded_mods +rm(list = ls(loaded_mods), envir = loaded_mods) + +library(testthat) + +test_dir("tests/testthat") diff --git a/tests/testthat/Testbox_attached_modules_functions_R6/tests/testthat/test-attached_R6.R b/tests/testthat/Testbox_attached_modules_functions_R6/tests/testthat/test-attached_R6.R new file mode 100644 index 00000000..2c147730 --- /dev/null +++ b/tests/testthat/Testbox_attached_modules_functions_R6/tests/testthat/test-attached_R6.R @@ -0,0 +1,23 @@ +box::use( + testthat[test_that, expect_equal, expect_s3_class] +) + +box::use( + app/modules/moduleR6[TestR6] +) + +test_that("TestR6 class can be instantiated", { + skip_if(is_r_devel()) + t1 <- TestR6$new() # nolint + + expect_s3_class(t1, "R6") + expect_s3_class(t1, "TestR6") +}) + +test_that("TestR6 Methods can be evaluated", { + skip_if(is_r_devel()) + t1 <- TestR6$new() # nolint + + expect_equal(t1$show(), 4) + expect_equal(t1$print2(), 3) +}) diff --git a/tests/testthat/test-box-R6.R b/tests/testthat/test-box-R6.R new file mode 100644 index 00000000..b50b8408 --- /dev/null +++ b/tests/testthat/test-box-R6.R @@ -0,0 +1,21 @@ +context("box-R6") + +loaded_mods <- loadNamespace("box")$loaded_mods +rm(list = ls(loaded_mods), envir = loaded_mods) + +test_that("R6 box module coverage is reported", { + # Similar to test-R6.R, there is some sort of bug that causes this test + # to fail during R CMD check in R-devel, not sure why, and can't reproduce + # it interactively + skip_if(is_r_devel()) + withr::with_dir("Testbox_R6", { + cov <- as.data.frame(file_coverage( + source_files = "app/app.R", + test_files = list.files("tests/testthat", full.names = TRUE))) + + expect_equal(cov$value, c(1, 1)) + expect_equal(cov$first_line, c(5, 8)) + expect_equal(cov$last_line, c(5, 8)) + expect_true("show" %in% cov$functions) + }) +}) diff --git a/tests/testthat/test-box.R b/tests/testthat/test-box.R new file mode 100644 index 00000000..9ee174a0 --- /dev/null +++ b/tests/testthat/test-box.R @@ -0,0 +1,18 @@ +context("box") + +loaded_mods <- loadNamespace("box")$loaded_mods +rm(list = ls(loaded_mods), envir = loaded_mods) + +test_that("box module coverage is reported", { + withr::with_dir("Testbox", { + cov <- as.data.frame(file_coverage( + source_files = "app/app.R", + test_files = list.files("tests/testthat", full.names = TRUE))) + + expect_equal(cov$value, c(5, 2, 3, 3)) + expect_equal(cov$first_line, c(5, 6, 8, 13)) + expect_equal(cov$last_line, c(5, 6, 8, 13)) + expect_true("a" %in% cov$functions) + expect_true("private_function" %in% cov$functions) + }) +}) diff --git a/tests/testthat/test-box_attached_modules_functions-R6.R b/tests/testthat/test-box_attached_modules_functions-R6.R new file mode 100644 index 00000000..ba491dc8 --- /dev/null +++ b/tests/testthat/test-box_attached_modules_functions-R6.R @@ -0,0 +1,22 @@ +context("box-attached-modules-functions-R6") + +loaded_mods <- loadNamespace("box")$loaded_mods +rm(list = ls(loaded_mods), envir = loaded_mods) + +test_that("R6 box attached module coverage is reported", { + # Similar to test-R6.R, there is some sort of bug that causes this test + # to fail during R CMD check in R-devel, not sure why, and can't reproduce + # it interactively + skip_if(is_r_devel()) + withr::with_dir("Testbox_attached_modules_functions_R6", { + cov <- as.data.frame(file_coverage( + source_files = "app/app.R", + test_files = list.files("tests/testthat", full.names = TRUE))) + + expect_equal(cov$value, c(1, 1)) + expect_equal(cov$first_line, c(5, 8)) + expect_equal(cov$last_line, c(5, 8)) + expect_true("show" %in% cov$functions) + }) + +}) diff --git a/tests/testthat/test-box_attached_modules_functions.R b/tests/testthat/test-box_attached_modules_functions.R new file mode 100644 index 00000000..06064083 --- /dev/null +++ b/tests/testthat/test-box_attached_modules_functions.R @@ -0,0 +1,19 @@ +context("box-attached-modules-functions") + +loaded_mods <- loadNamespace("box")$loaded_mods +rm(list = ls(loaded_mods), envir = loaded_mods) + +test_that("box attached module coverage is reported", { + withr::with_dir("Testbox_attached_modules_functions", { + cov <- as.data.frame(file_coverage( + source_files = "app/app.R", + test_files = list.files("tests/testthat", full.names = TRUE))) + + expect_equal(cov$value, c(20, 8, 12, 3, 0)) + expect_equal(cov$first_line, c(5, 6, 8, 14, 18)) + expect_equal(cov$last_line, c(5, 6, 8, 14, 18)) + expect_true("a" %in% cov$functions) + expect_true("private_function" %in% cov$functions) + }) + +})