Skip to content

Commit 49cc7ea

Browse files
committed
Merged origin/main into slow-reporter
2 parents 719e3f8 + 6099802 commit 49cc7ea

34 files changed

+453
-240
lines changed

.claude/settings.local.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
{
2+
"$schema": "https://json.schemastore.org/claude-code-settings.json",
23
"permissions": {
34
"allow": [
45
"Bash(find:*)",
5-
"Bash(R:*)"
6+
"Bash(R:*)",
7+
"Bash(rm:*)",
8+
"Bash(air format:*)"
69
],
710
"deny": []
8-
},
9-
"$schema": "https://json.schemastore.org/claude-code-settings.json"
10-
}
11+
}
12+
}

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ General advice:
1616

1717
- `devtools::test()` - Run all tests
1818
- `devtools::test_file("tests/testthat/test-filename.R")` - Run tests in a specific file
19+
- DO NOT USE `devtools::test_active_file()`
1920
- `devtools::load_all()` - Load package for development
2021
- `devtools::check()` - Run R CMD check
2122
- `devtools::install()` - Install package locally

NEWS.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
# testthat (development version)
22

3+
* Parallel testthat now does not ignore test files with syntax errors (#1360).
4+
* `expect_lt()`, `expect_gt()`, and friends have a refined display that is more likely to display the correct number of digits and shows you the actual values compared.
5+
* `describe()`, `it()`, and `test_that()` now have a shared stack of descriptions so that if you nest any inside of each other, any resulting failures will show you the full path.
6+
* `describe()` now correctly scopes `skip()` (#2007).
7+
* `ParallelProgressReporter` now respect `max_failures` (#1162).
8+
* The last snapshot is no longer lost if the snapshot file is missing the final newline (#2092). It's easy to accidentally remove this because there are two trailing new lines in snapshot files and many editors will automatically remove if you touch the file.
39
* New `expect_r6_class()` (#2030).
4-
* `expect_*()` functions consistently and rigorously check their inputs (#1754).
10+
* `expect_*()` functions consistently and rigorously check their inputs (#1754).
511
* `JunitReporter()` no longer fails with `"no applicable method for xml_add_child"` for warnings outside of tests (#1913). Additionally, warnings now save their backtraces.
612
* `JunitReporter()` strips ANSI escapes in more placese (#1852, #2032).
713
* `try_again()` is now publicised. The first argument is now the number of retries, not tries (#2050).

R/describe.R

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -56,43 +56,17 @@
5656
#' it("can handle division by 0") #not yet implemented
5757
#' })
5858
#' })
59-
6059
describe <- function(description, code) {
61-
check_string(description, allow_empty = FALSE)
62-
describe_description <- description
63-
64-
# prepares a new environment for each it-block
65-
describe_environment <- new.env(parent = parent.frame())
66-
describe_environment$it <- function(description, code = NULL) {
67-
check_string(description, allow_empty = FALSE)
68-
code <- substitute(code)
69-
70-
description <- paste0(describe_description, ": ", description)
71-
describe_it(description, code, describe_environment)
72-
}
73-
74-
eval(substitute(code), describe_environment)
75-
invisible()
76-
}
77-
78-
describe_it <- function(description, code, env = parent.frame()) {
79-
reporter <- get_reporter() %||% local_interactive_reporter()
80-
local_test_context()
60+
local_description_push(description)
8161

82-
test_code(
83-
description,
84-
code,
85-
env = env,
86-
reporter = reporter,
87-
skip_on_empty = FALSE
88-
)
62+
test_code(code, parent.frame(), skip_on_empty = FALSE)
8963
}
9064

9165
#' @export
9266
#' @rdname describe
9367
it <- function(description, code = NULL) {
94-
check_string(description, allow_empty = FALSE)
68+
local_description_push(description)
9569

9670
code <- substitute(code)
97-
describe_it(description, code, env = parent.frame())
71+
test_code(code, env = parent.frame(), skip_on_empty = FALSE)
9872
}

R/expect-comparison.R

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,31 @@ expect_compare_ <- function(
3131
msg <- c(
3232
"<" = "not strictly less than",
3333
"<=" = "not less than",
34-
">" = "not strictly more than",
35-
">=" = "not more than"
34+
">" = "not strictly greater than",
35+
">=" = "not greater than"
3636
)[[operator]]
3737

38+
negated_op <- switch(operator, "<" = ">=", "<=" = ">", ">" = "<=", ">=" = "<")
39+
3840
cmp <- op(act$val, exp$val)
3941
if (length(cmp) != 1 || !is.logical(cmp)) {
4042
abort("Result of comparison must be a single logical value")
4143
}
4244
if (!isTRUE(cmp)) {
45+
digits <- max(
46+
digits(act$val),
47+
digits(exp$val),
48+
min_digits(act$val, exp$val)
49+
)
4350
msg <- sprintf(
44-
"%s is %s %s. Difference: %.3g",
51+
"%s is %s %s.\n%s - %s = %s %s 0",
4552
act$lab,
4653
msg,
4754
exp$lab,
48-
act$val - exp$val
55+
num_exact(act$val, digits),
56+
num_exact(exp$val, digits),
57+
num_exact(act$val - exp$val, digits),
58+
negated_op
4959
)
5060
return(fail(msg, trace_env = trace_env))
5161
}
@@ -109,3 +119,39 @@ expect_more_than <- function(...) {
109119
warning("Deprecated: please use `expect_gt()` instead", call. = FALSE)
110120
expect_gt(...)
111121
}
122+
123+
124+
# Helpers -----------------------------------------------------------------
125+
126+
num_exact <- function(x, digits = 6) {
127+
sprintf(paste0("%0.", digits, "f"), x)
128+
}
129+
130+
min_digits <- function(x, y, tolerance = testthat_tolerance()) {
131+
if (is.integer(x) && is.integer(y)) {
132+
return(0L)
133+
}
134+
135+
attributes(x) <- NULL
136+
attributes(y) <- NULL
137+
138+
n <- digits(abs(x - y))
139+
if (!is.null(tolerance)) {
140+
n <- min(n, digits(tolerance))
141+
}
142+
143+
as.integer(n) + 1L
144+
}
145+
146+
digits <- function(x) {
147+
x <- x[!is.na(x) & x != 0]
148+
if (length(x) == 0) {
149+
return(0)
150+
}
151+
scale <- -log10(min(x))
152+
if (scale <= 0) {
153+
0L
154+
} else {
155+
ceiling(round(scale, digits = 2))
156+
}
157+
}

R/expect-inheritance.R

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,17 @@
2020
#' See [expect_vector()] for testing properties of objects created by vctrs.
2121
#'
2222
#' @param type String giving base type (as returned by [typeof()]).
23-
#' @param class
24-
#' * `expect_type()`: a single string giving an R base type.
25-
#' * `expect_s3_class()`: a character vector of class names or `NA` to assert
26-
#' that `object` isn't an S3 object. If you provide multiple class names,
27-
#' the test will pass if `object` inherits from any of them, unless
28-
#' `exact = TRUE`.
29-
#' * `expect_s4_class()`: a character vector of class names or `NA` to assert
30-
#' that `object` isn't an S4 object.
31-
#' * `expect_r6_class()`: a string.
32-
#' * `expect_s7_class()`: an [S7::S7_class()] object.
23+
#' @param class The required type varies depending on the function:
24+
#' * `expect_type()`: a string.
25+
#' * `expect_s3_class()`: a string or character vector. The behaviour of
26+
#' multiple values (i.e. a character vector) is controlled by the
27+
#' `exact` argument.
28+
#' * `expect_s4_class()`: a string.
29+
#' * `expect_r6_class()`: a string.
30+
#' * `expect_s7_class()`: an [S7::S7_class()] object.
31+
#'
32+
#' For historical reasons, `expect_s3_class()` and `expect_s4_class()` also
33+
#' take `NA` to assert that the `object` is not an S3 or S4 object.
3334
#' @inheritParams expect_that
3435
#' @family expectations
3536
#' @examples
@@ -85,7 +86,7 @@ expect_type <- function(object, type) {
8586
#' @rdname inheritance-expectations
8687
#' @param exact If `FALSE`, the default, checks that `object` inherits
8788
#' from any element of `class`. If `TRUE`, checks that object has a class
88-
#' that's identical to `class`.
89+
#' that exactly matches `class`.
8990
expect_s3_class <- function(object, class, exact = FALSE) {
9091
check_bool(exact)
9192

R/parallel-taskq.R

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ task_q <- R6::R6Class(
7878
private$tasks$state[[i]] <- "ready"
7979
msg <- NULL
8080
} else if (msg$code == PROCESS_DONE) {
81+
if (!is.null(msg$error)) {
82+
private$handle_error(msg, i)
83+
}
8184
private$tasks$state[[i]] <- "ready"
8285
} else if (msg$code %in% PROCESS_FAILURES) {
8386
private$handle_error(msg, i)

R/reporter-progress.R

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -221,16 +221,20 @@ ProgressReporter <- R6::R6Class(
221221
self$report_issues(self$ctxt_issues)
222222

223223
if (self$is_full()) {
224-
snapshotter <- get_snapshotter()
225-
if (!is.null(snapshotter)) {
226-
snapshotter$end_file()
227-
}
224+
self$report_full()
225+
}
226+
},
228227

229-
stop_reporter(c(
230-
"Maximum number of failures exceeded; quitting at end of file.",
231-
i = "Increase this number with (e.g.) {.run testthat::set_max_fails(Inf)}"
232-
))
228+
report_full = function() {
229+
snapshotter <- get_snapshotter()
230+
if (!is.null(snapshotter)) {
231+
snapshotter$end_file()
233232
}
233+
234+
stop_reporter(c(
235+
"Maximum number of failures exceeded; quitting at end of file.",
236+
i = "Increase this number with (e.g.) {.run testthat::set_max_fails(Inf)}"
237+
))
234238
},
235239

236240
add_result = function(context, test, result) {
@@ -258,6 +262,10 @@ ProgressReporter <- R6::R6Class(
258262
},
259263

260264
end_reporter = function() {
265+
if (self$is_full()) {
266+
return()
267+
}
268+
261269
self$cat_line()
262270

263271
colour_if <- function(n, type) {
@@ -465,7 +473,13 @@ ParallelProgressReporter <- R6::R6Class(
465473
self$report_issues(fsts$issues)
466474

467475
self$files[[self$file_name]] <- NULL
468-
if (length(self$files)) self$update(force = TRUE)
476+
if (length(self$files)) {
477+
self$update(force = TRUE)
478+
}
479+
480+
if (self$is_full()) {
481+
self$report_full()
482+
}
469483
},
470484

471485
end_reporter = function() {

R/reporter-silent.R

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,11 @@ SilentReporter <- R6::R6Class(
2828
}
2929
)
3030
)
31+
32+
# Useful for testing test_that() and friends which otherwise swallow
33+
# all expectations by design
34+
capture_expectations <- function(code) {
35+
reporter <- SilentReporter$new()
36+
with_reporter(reporter, code)
37+
reporter$expectations()
38+
}

R/reporter-zzz.R

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ get_reporter <- function() {
3636
#' @rdname reporter-accessors
3737
#' @export
3838
with_reporter <- function(reporter, code, start_end_reporter = TRUE) {
39+
# Ensure we don't propagate the local description to the new reporter
40+
local_description_set()
3941
reporter <- find_reporter(reporter)
4042

4143
old <- set_reporter(reporter)

0 commit comments

Comments
 (0)