Skip to content

Commit e8b06e1

Browse files
authored
Revert expect_mapequal() changes (#2255)
Reverts #2150 since it does actually break quite a few packages on CRAN. This change reverts the original behaviour (with more tests) making use of more new helpers. Fixes #2245
1 parent 8dcf1e2 commit e8b06e1

File tree

5 files changed

+168
-40
lines changed

5 files changed

+168
-40
lines changed

NEWS.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
* Test filtering now works with `it()`, and the `desc` argument can take a character vector in order to recursively filter subtests (i.e. `it()` nested inside of `describe()`) (#2118).
2929
* New `snapshot_reject()` rejects all modified snapshots by deleting the `.new` variants (#1923).
3030
* New `SlowReporter` makes it easier to find the slowest tests in your package. The easiest way to run it is with `devtools::test(reporter = "slow")` (#1466).
31-
* Power `expect_mapequal()` with `waldo::compare(list_as_map = TRUE)` (#1521).
3231
* On CRAN, `test_that()` now automatically skips if a package is not installed (#1585). Practically, this means that you no longer need to check that suggested packages are installed. (We don't do this in the tidyverse because we think it has limited payoff, but other styles advise differently.)
3332
* `expect_snapshot()` no longer skips on CRAN, as that skips the rest of the test. Instead it just returns, neither succeeding nor failing (#1585).
3433
* Interrupting a test now prints the test name. This makes it easier to tell where a very slow test might be hanging (#1464).

R/expect-setequal.R

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
#' * `expect_in(x, y)` tests every element of `x` is in `y`
88
#' (i.e. `x` is a subset of `y`).
99
#' * `expect_mapequal(x, y)` treats lists as if they are mappings between names
10-
#' and values. Concretely, this drops `NULL`s in both objects and sorts
11-
#' named components.
10+
#' and values. Concretely, checks that `x` and `y` have the same names, then
11+
#' checks that `x[names(y)]` equals `y`.
1212
#'
1313
#' Note that `expect_setequal()` ignores names, and you will be warned if both
1414
#' `object` and `expected` have them.
@@ -89,7 +89,35 @@ expect_mapequal <- function(object, expected) {
8989
act <- quasi_label(enquo(object))
9090
exp <- quasi_label(enquo(expected))
9191

92-
expect_waldo_equal_("equal", act, exp, list_as_map = TRUE)
92+
check_vector(act$val, error_arg = "object")
93+
check_map_names(act$val, error_arg = "object")
94+
check_vector(exp$val, error_arg = "expected")
95+
check_map_names(exp$val, error_arg = "expected")
96+
97+
act_nms <- names(act$val)
98+
exp_nms <- names(exp$val)
99+
100+
# Length-0 vectors are OK whether named or unnamed.
101+
if (length(act$val) == 0 && length(exp$val) == 0) {
102+
testthat_warn("`object` and `expected` are empty vectors.")
103+
pass()
104+
} else {
105+
if (!setequal(act_nms, exp_nms)) {
106+
act_names <- labelled_value(names(act$val), paste0("names of ", act$lab))
107+
exp_names <- labelled_value(names(exp$val), paste0("names of ", exp$lab))
108+
expect_setequal_(act_names, exp_names)
109+
} else {
110+
if (edition_get() >= 3) {
111+
act <- labelled_value(act$val[exp_nms], act$lab)
112+
expect_waldo_equal_("equal", act, exp)
113+
} else {
114+
# Packages depend on 2e behaviour, but the expectation isn't written
115+
# to be reused, and we don't want to bother
116+
expect_equal(act$val[exp_nms], exp$val)
117+
}
118+
}
119+
}
120+
93121
invisible(act$val)
94122
}
95123

@@ -158,3 +186,27 @@ check_vector <- function(x, error_arg, error_call = caller_env()) {
158186
stop_input_type(x, "a vector", arg = error_arg, call = error_call)
159187
}
160188
}
189+
190+
check_map_names <- function(x, error_arg, error_call = caller_env()) {
191+
nms <- names2(x)
192+
if (anyDuplicated(nms)) {
193+
dups <- unique(nms[duplicated(nms)])
194+
cli::cli_abort(
195+
c(
196+
"All elements in {.arg {error_arg}} must have unique names.",
197+
x = "Duplicate names: {.str {dups}}"
198+
),
199+
call = error_call
200+
)
201+
}
202+
if (any(nms == "")) {
203+
empty <- which(nms == "")
204+
cli::cli_abort(
205+
c(
206+
"All elements in {.arg {error_arg}} must have names.",
207+
x = "Empty names at position{?s}: {empty}"
208+
),
209+
call = error_call
210+
)
211+
}
212+
}

man/expect_setequal.Rd

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/expect-setequal.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,84 @@
109109
Expected: 1, 2, 3, 4, 5, 6, 7, 8, 9, ...
110110
Absent: 3, 4, 5, 6, 7, 8, 9, 10, 11, ...
111111

112+
# fails if names don't match
113+
114+
Code
115+
expect_mapequal(x, y)
116+
Condition
117+
Error:
118+
! Expected names of `x` to have the same values as names of `y`.
119+
Actual: "a", "b"
120+
Expected: "a"
121+
Needs: "b"
122+
123+
---
124+
125+
Code
126+
expect_mapequal(y, x)
127+
Condition
128+
Error:
129+
! Expected names of `y` to have the same values as names of `x`.
130+
Actual: "a"
131+
Expected: "a", "b"
132+
Absent: "b"
133+
134+
# fails if values don't match
135+
136+
Code
137+
expect_mapequal(x, y)
138+
Condition
139+
Error:
140+
! Expected `x` to be equal to `y`.
141+
Differences:
142+
`actual$b`: 2.0
143+
`expected$b`: 3.0
144+
145+
# warns if empty vector
146+
147+
Code
148+
expect_success(expect_mapequal(list(), list()))
149+
Condition
150+
Warning:
151+
`object` and `expected` are empty vectors.
152+
153+
# validates its inputs
154+
155+
Code
156+
expect_mapequal(sum, named)
157+
Condition
158+
Error in `expect_mapequal()`:
159+
! `object` must be a vector, not a primitive function.
160+
Code
161+
expect_mapequal(named, sum)
162+
Condition
163+
Error in `expect_mapequal()`:
164+
! `expected` must be a vector, not a primitive function.
165+
Code
166+
expect_mapequal(unnamed, named)
167+
Condition
168+
Error in `expect_mapequal()`:
169+
! All elements in `object` must have names.
170+
x Empty names at position: 1
171+
Code
172+
expect_mapequal(named, unnamed)
173+
Condition
174+
Error in `expect_mapequal()`:
175+
! All elements in `expected` must have names.
176+
x Empty names at position: 1
177+
Code
178+
expect_mapequal(named, duplicated)
179+
Condition
180+
Error in `expect_mapequal()`:
181+
! All elements in `expected` must have unique names.
182+
x Duplicate names: "x"
183+
Code
184+
expect_mapequal(duplicated, named)
185+
Condition
186+
Error in `expect_mapequal()`:
187+
! All elements in `object` must have unique names.
188+
x Duplicate names: "x"
189+
112190
# expect_contains() gives useful message on failure
113191

114192
Code

tests/testthat/test-expect-setequal.R

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -70,51 +70,50 @@ test_that("truncates long vectors", {
7070

7171
test_that("ignores order", {
7272
expect_success(expect_mapequal(list(a = 1, b = 2), list(b = 2, a = 1)))
73+
expect_success(expect_mapequal(c(a = 1, b = 2), c(b = 2, a = 1)))
7374
})
7475

75-
test_that("ignores order recursively", {
76-
x <- list(outer_1 = 1, outer_2 = list(inner_1 = 1, inner_2 = 2))
77-
y <- list(outer_2 = list(inner_2 = 2, inner_1 = 1), outer_1 = 1)
78-
expect_success(expect_mapequal(x, y))
76+
test_that("fails if names don't match", {
77+
x <- list(a = 1, b = 2)
78+
y <- list(a = 1)
79+
expect_snapshot_failure(expect_mapequal(x, y))
80+
expect_snapshot_failure(expect_mapequal(y, x))
7981
})
8082

81-
test_that("fails when any names are duplicated", {
82-
expect_failure(expect_mapequal(
83-
list(a = 1, b = 2, b = 3),
84-
list(b = 2, a = 1)
85-
))
86-
expect_failure(expect_mapequal(
87-
list(a = 1, b = 2),
88-
list(b = 3, b = 2, a = 1)
89-
))
90-
expect_failure(expect_mapequal(
91-
list(a = 1, b = 2, b = 3),
92-
list(b = 3, b = 2, a = 1)
93-
))
83+
test_that("fails if values don't match", {
84+
x <- list(a = 1, b = 2)
85+
y <- list(a = 1, b = 3)
86+
expect_snapshot_failure(expect_mapequal(x, y))
9487
})
9588

96-
test_that("handling NULLs", {
89+
test_that("NULLs are not dropped", {
9790
expect_success(expect_mapequal(list(a = 1, b = NULL), list(b = NULL, a = 1)))
9891
})
9992

100-
test_that("fail if names don't match", {
101-
expect_failure(expect_mapequal(list(a = 1, b = 2), list(a = 1)))
102-
expect_failure(expect_mapequal(list(a = 1), list(a = 1, b = 2)))
93+
test_that("warns if empty vector", {
94+
expect_snapshot(expect_success(expect_mapequal(list(), list())))
10395
})
10496

105-
test_that("fails if values don't match", {
106-
expect_failure(expect_mapequal(
107-
list(a = 1, b = 2),
108-
list(a = 1, b = 3)
109-
))
110-
})
111-
112-
test_that("fails if unnamed values in different location if any unnamed values", {
113-
expect_success(expect_mapequal(list(1, b = 2, c = 3), list(1, c = 3, b = 2)))
114-
expect_failure(expect_mapequal(
115-
list(1, b = 2, c = 3),
116-
list(b = 2, 1, c = 3)
117-
))
97+
test_that("uses equality behaviour of current edition", {
98+
local_edition(2)
99+
expect_success(expect_mapequal(c(a = 1), c(a = 1L)))
100+
})
101+
102+
test_that("validates its inputs", {
103+
unnamed <- list(1)
104+
named <- list(a = 1)
105+
duplicated <- list(x = 1, x = 2)
106+
107+
expect_snapshot(error = TRUE, {
108+
expect_mapequal(sum, named)
109+
expect_mapequal(named, sum)
110+
111+
expect_mapequal(unnamed, named)
112+
expect_mapequal(named, unnamed)
113+
114+
expect_mapequal(named, duplicated)
115+
expect_mapequal(duplicated, named)
116+
})
118117
})
119118

120119
# contains ----------------------------------------------------------------

0 commit comments

Comments
 (0)