-
Notifications
You must be signed in to change notification settings - Fork 323
/
Copy pathauto-test.R
132 lines (116 loc) Β· 4.86 KB
/
auto-test.R
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. Everytime 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
}