1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
#' Watches code and tests for changes, rerunning tests as appropriate.
#'
#' The idea behind `auto_test()` is that you just leave it running while
#' you develop your code. Every time you save a file it will be automatically
#' tested and you can easily see if your changes have caused any test
#' failures.
#'
#' The current strategy for rerunning tests is as follows:
#'
#' - if any code has changed, then those files are reloaded and all tests
#' rerun
#' - otherwise, each new or modified test is run
#'
#' In the future, `auto_test()` might implement one of the following more
#' intelligent alternatives:
#'
#' - Use codetools to build up dependency tree and then rerun tests only
#' when a dependency changes.
#' - Mimic ruby's autotest and rerun only failing tests until they pass,
#' and then rerun all tests.
#
#' @seealso [auto_test_package()]
#' @export
#' @param code_path path to directory containing code
#' @param test_path path to directory containing tests
#' @param reporter test reporter to use
#' @param env environment in which to execute test suite.
#' @param hash Passed on to [watch()]. When FALSE, uses less accurate
#' modification time stamps, but those are faster for large files.
#' @keywords debugging
auto_test <- function(code_path, test_path, reporter = default_reporter(),
env = test_env(),
hash = TRUE) {
reporter <- find_reporter(reporter)
code_path <- normalizePath(code_path)
test_path <- normalizePath(test_path)
# Start by loading all code and running all tests
source_dir(code_path, env = env)
test_dir(test_path, env = env, reporter = reporter$clone(deep = TRUE))
# Next set up watcher to monitor changes
watcher <- function(added, deleted, modified) {
changed <- normalizePath(c(added, modified))
tests <- changed[starts_with(changed, test_path)]
code <- changed[starts_with(changed, code_path)]
if (length(code) > 0) {
# Reload code and rerun all tests
cat("Changed code: ", paste0(basename(code), collapse = ", "), "\n")
cat("Rerunning all tests\n")
source_dir(code_path, env = env)
test_dir(test_path, env = env, reporter = reporter$clone(deep = TRUE))
} else if (length(tests) > 0) {
# If test changes, rerun just that test
cat("Rerunning tests: ", paste0(basename(tests), collapse = ", "), "\n")
test_files(tests, env = env, reporter = reporter$clone(deep = TRUE))
}
TRUE
}
watch(c(code_path, test_path), watcher, hash = hash)
}
#' Watches a package for changes, rerunning tests as appropriate.
#'
#' @param pkg path to package
#' @export
#' @param reporter test reporter to use
#' @param hash Passed on to [watch()]. When FALSE, uses less accurate
#' modification time stamps, but those are faster for large files.
#' @keywords debugging
#' @seealso [auto_test()] for details on how method works
auto_test_package <- function(pkg = ".", reporter = default_reporter(), hash = TRUE) {
reporter <- find_reporter(reporter)
path <- pkgload::pkg_path(pkg)
package <- pkgload::pkg_name(path)
code_path <- file.path(path, c("R", "src"))
code_path <- code_path[file.exists(code_path)]
code_path <- normalizePath(code_path)
test_path <- normalizePath(file.path(path, "tests", "testthat"))
# Start by loading all code and running all tests
withr::local_envvar("NOT_CRAN" = "true")
pkgload::load_all(path)
test_dir(test_path, package = package, reporter = reporter$clone(deep = TRUE), stop_on_failure = FALSE)
# Next set up watcher to monitor changes
watcher <- function(added, deleted, modified) {
changed <- normalizePath(c(added, modified))
tests <- changed[starts_with(changed, test_path)]
code <- changed[starts_with(changed, code_path)]
# Remove helper from test and add it to code (if a helper changed,
# like for code, reload all and rerun all tests)
helper <- tests[starts_with(basename(tests), "helper-")]
tests <- setdiff(tests, helper)
code <- c(code, helper)
if (length(code) > 0) {
# Reload code and rerun all tests
cat("Changed code: ", paste0(basename(code), collapse = ", "), "\n")
cat("Rerunning all tests\n")
pkgload::load_all(path, quiet = TRUE)
test_dir(test_path, package = package, reporter = reporter$clone(deep = TRUE))
} else if (length(tests) > 0) {
# If test changes, rerun just that test
cat("Rerunning tests: ", paste0(basename(tests), collapse = ", "), "\n")
env <- env_clone(asNamespace(package))
test_files(
test_dir = test_path,
test_package = package,
test_paths = tests,
env = env,
reporter = reporter$clone(deep = TRUE)
)
}
TRUE
}
watch(c(code_path, test_path), watcher, hash = hash)
}
# Helpers -----------------------------------------------------------------
starts_with <- function(string, prefix) {
substr(string, 1, nchar(prefix)) == prefix
}
|