Skip to content

Commit ffde38b

Browse files
Rearrange to desired signature, adjust tests
1 parent 1a020dc commit ffde38b

File tree

2 files changed

+42
-72
lines changed

2 files changed

+42
-72
lines changed

R/expect-shape.R

Lines changed: 27 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,54 @@
11
#' Does code return an object with the specified shape?
22
#'
3-
#' By "shape", we mean an object's [dim()], or, for one-dimensional objects,
4-
#' it's [length()]. Thus this is an extension of [expect_length()] to more
5-
#' general objects like [data.frame()], [matrix()], and [array()].
6-
#' To wit, first, the object's `dim()` is checked. If non-`NULL`, it is compared
7-
#' to `shape` (or one/both of `nrow`, `ncol`, if they are supplied, in which
8-
#' case they take precedence). If `dim(object)` is `NULL`, `length(object)`
9-
#' is compared to `shape`.
10-
#'
113
#' @seealso [expect_length()] to specifically make assertions about the
124
#' [length()] of a vector.
135
#' @inheritParams expect_that
14-
#' @param shape Expected shape, a numeric vector.
15-
#' @param nrow Expected number of rows, numeric.
16-
#' @param ncol Expected number of columns, numeric.
6+
#' @param ... Ignored.
7+
#' @param length Expected [length()] of `object`.
8+
#' @param nrow Expected [nrow()] of `object`.
9+
#' @param ncol Expected [ncol()] of `object`.
10+
#' @param dim Expected [dim()] of `object`.
1711
#' @family expectations
1812
#' @export
1913
#' @examples
20-
expect_shape = function(object, shape, nrow, ncol) {
21-
stopifnot(
22-
missing(shape) || is.numeric(shape),
23-
missing(nrow) || is.numeric(nrow),
24-
missing(ncol) || is.numeric(ncol)
25-
)
14+
expect_shape = function(object, ..., length, nrow, ncol, dim) {
15+
# absent, not present, due to '!' operator precedence
16+
n_absent <- missing(length) + missing(nrow) + missing(ncol) + missing(dim)
17+
if (n_absent != 3L) {
18+
cli::cli_abort(
19+
"Exactly one of {.arg length}, {.arg nrow}, {.arg ncol}, or {.arg dim} must be provided."
20+
)
21+
}
2622

27-
dim_object <- dim(object)
28-
if (is.null(dim_object)) {
29-
if (missing(shape)) {
30-
stop("`shape` must be provided for one-dimensional inputs")
31-
}
32-
return(expect_length(object, shape))
23+
# Re-use expect_length() to ensure they stay in sync.
24+
if (!missing(length)) {
25+
return(expect_length(object, length))
3326
}
3427

3528
act <- quasi_label(enquo(object), arg = "object")
29+
# need base:: qualification or we might trigger an error for missing(dim)
30+
dim_object <- base::dim(object)
3631

37-
if (missing(nrow) && missing(ncol)) {
38-
# testing dim
39-
if (missing(shape)) {
40-
stop("`shape` must be provided if `nrow` and `ncol` are not")
41-
}
42-
act$shape <- dim_object
32+
if (!missing(nrow)) {
33+
act$nrow <- dim_object[1L]
4334

4435
expect(
45-
isTRUE(all.equal(act$shape, shape)),
46-
sprintf("%s has shape (%s), not (%s).", act$lab, toString(act$shape), toString(shape))
36+
act$nrow == nrow,
37+
sprintf("%s has %i rows, not %i.", act$lab, act$nrow, nrow)
4738
)
48-
} else if (missing(nrow) && !missing(ncol)) {
49-
# testing only ncol
39+
} else if (!missing(ncol)) {
5040
act$ncol <- dim_object[2L]
5141

5242
expect(
5343
act$ncol == ncol,
5444
sprintf("%s has %i columns, not %i.", act$lab, act$ncol, ncol)
5545
)
56-
} else if (!missing(nrow) && missing(ncol)) {
57-
# testing only nrow
58-
act$nrow <- dim_object[1L]
46+
} else { # !missing(dim)
47+
act$dim <- dim_object
5948

6049
expect(
61-
act$nrow == nrow,
62-
sprintf("%s has %i rows, not %i.", act$lab, act$nrow, nrow)
63-
)
64-
} else { # !missing(nrow) && !missing(ncol)
65-
# testing both nrow & ncol (useful, e.g., for testing dim(.)[1:2] for arrays
66-
act$nrow <- dim_object[1L]
67-
act$ncol <- dim_object[2L]
68-
69-
expect(
70-
act$nrow == nrow && act$ncol == ncol,
71-
sprintf("%s has %i rows and %i columns, not %i rows and %i columns", act$lab, act$nrow, act$ncol, nrow, ncol)
50+
isTRUE(all.equal(act$dim, dim)),
51+
sprintf("%s has shape (%s), not (%s).", act$lab, toString(act$dim), toString(dim))
7252
)
7353
}
74-
75-
return(act$val)
7654
}

tests/testthat/test-expect-shape.R

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
test_that("shape computed correctly", {
22
# equivalent to expect_length
3-
expect_success(expect_shape(1, 1))
4-
expect_failure(expect_shape(1, 2), "has length 1, not length 2.")
5-
expect_success(expect_shape(1:10, 10))
6-
expect_success(expect_shape(letters[1:5], 5))
3+
expect_success(expect_shape(1, length = 1))
4+
expect_failure(expect_shape(1, length = 2), "has length 1, not length 2.")
5+
expect_success(expect_shape(1:10, length = 10))
6+
expect_success(expect_shape(letters[1:5], length = 5))
77

88
# testing dim()
9-
expect_success(expect_shape(matrix(nrow = 5, ncol = 4), c(5L, 4L)))
10-
expect_failure(expect_shape(matrix(nrow = 6, ncol = 3), c(6L, 2L)))
11-
expect_failure(expect_shape(matrix(nrow = 6, ncol = 3), c(7L, 3L)))
12-
expect_success(expect_shape(data.frame(1:10, 11:20), c(10, 2)))
13-
expect_success(expect_shape(array(dim = 1:3), 1:3))
9+
expect_success(expect_shape(matrix(nrow = 5, ncol = 4), dim = c(5L, 4L)))
10+
expect_failure(expect_shape(matrix(nrow = 6, ncol = 3), dim = c(6L, 2L)))
11+
expect_failure(expect_shape(matrix(nrow = 6, ncol = 3), dim = c(7L, 3L)))
12+
expect_success(expect_shape(data.frame(1:10, 11:20), dim = c(10, 2)))
13+
expect_success(expect_shape(array(dim = 1:3), dim = 1:3))
1414

1515
# testing nrow=
1616
expect_success(expect_shape(matrix(nrow = 5, ncol = 4), nrow = 5L))
@@ -21,31 +21,23 @@ test_that("shape computed correctly", {
2121
expect_success(expect_shape(matrix(nrow = 5, ncol = 4), ncol = 4L))
2222
expect_failure(expect_shape(matrix(nrow = 5, ncol = 5), ncol = 7L))
2323
expect_success(expect_shape(data.frame(1:10, 11:20), ncol = 2L))
24-
25-
# testing nrow= and ncol=
26-
expect_success(expect_shape(matrix(nrow = 5, ncol = 4), nrow = 5L, ncol = 4L))
27-
expect_failure(expect_shape(matrix(nrow = 5, ncol = 5), nrow = 6L, ncol = 5L))
28-
expect_success(expect_shape(data.frame(1:10, 11:20), nrow = 10L, ncol = 2L))
29-
expect_success(expect_shape(array(dim = 5:7), nrow = 5L, ncol = 6L))
30-
31-
# precedence of manual nrow/ncol over shape
32-
expect_success(expect_shape(matrix(nrow = 7, ncol = 10), 1:2, nrow = 7L))
33-
expect_success(expect_shape(matrix(nrow = 7, ncol = 10), 1:2, ncol = 10L))
3424
})
3525

3626
test_that("uses S4 dim method", {
3727
A <- setClass("ExpectShapeA", slots = c(x = "numeric", y = "numeric"))
3828
setMethod("dim", "ExpectShapeA", function(x) 8:10)
39-
expect_success(expect_shape(A(x = 1:9, y = 3), 8:10))
29+
expect_success(expect_shape(A(x = 1:9, y = 3), dim = 8:10))
4030
})
4131

4232
test_that("returns input", {
4333
x <- list(1:10, letters)
44-
out <- expect_shape(x, 2)
34+
out <- expect_shape(x, length = 2)
4535
expect_identical(out, x)
4636
})
4737

4838
test_that("at least one argument is required", {
49-
expect_error(expect_shape(1:10), "`shape` must be provided for one-dimensional inputs", fixed = TRUE)
50-
expect_error(expect_shape(cbind(1:2)), "`shape` must be provided if `nrow` and `ncol` are not")
39+
err_msg <- "Exactly one of `length`, `nrow`, `ncol`, or `dim` must be provided."
40+
expect_error(expect_shape(1:10), err_msg, fixed = TRUE) # no args
41+
expect_error(expect_shape(1:10, 2), err_msg, fixed = TRUE) # no named args
42+
expect_error(expect_shape(1:10, nrow = 1L, ncol = 2L), err_msg, fixed = TRUE) # multiple named args
5143
})

0 commit comments

Comments
 (0)