Skip to content

Commit e435b17

Browse files
committed
Revert expect_mapequal() changes
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 dba0ef1 commit e435b17

File tree

4 files changed

+166
-38
lines changed

4 files changed

+166
-38
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

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

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+
# check inputs
146+
147+
Code
148+
expect_mapequal(sum, named)
149+
Condition
150+
Error in `expect_mapequal()`:
151+
! `object` must be a vector, not a primitive function.
152+
Code
153+
expect_mapequal(named, sum)
154+
Condition
155+
Error in `expect_mapequal()`:
156+
! `expected` must be a vector, not a primitive function.
157+
Code
158+
expect_mapequal(unnamed, named)
159+
Condition
160+
Error in `expect_mapequal()`:
161+
! All elements in `object` must have names.
162+
x Empty names at position: 1
163+
Code
164+
expect_mapequal(named, unnamed)
165+
Condition
166+
Error in `expect_mapequal()`:
167+
! All elements in `expected` must have names.
168+
x Empty names at position: 1
169+
Code
170+
expect_mapequal(named, duplicated)
171+
Condition
172+
Error in `expect_mapequal()`:
173+
! All elements in `expected` must have unique names.
174+
x Duplicate names: "x"
175+
Code
176+
expect_mapequal(duplicated, named)
177+
Condition
178+
Error in `expect_mapequal()`:
179+
! All elements in `object` must have unique names.
180+
x Duplicate names: "x"
181+
182+
# warns if empty vector
183+
184+
Code
185+
expect_success(expect_mapequal(list(), list()))
186+
Condition
187+
Warning:
188+
`object` and `expected` are empty vectors.
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)