local_name_repair_quiet()
test_that("zero length input returns NULL", {
expect_equal(vec_c(), NULL)
expect_equal(vec_c(NULL), NULL)
expect_equal(vec_c(NULL,), NULL)
expect_equal(vec_c(NULL, NULL), NULL)
})
test_that("NULL is idempotent", {
expect_equal(vec_c(NULL, 1L), 1L)
expect_equal(vec_c(1L, NULL), 1L)
})
test_that("NA is idempotent", {
expect_equal(vec_c(NA, 1L), c(NA, 1L))
expect_equal(vec_c(NA, "x"), c(NA, "x"))
expect_equal(vec_c(NA, factor("x")), factor(c(NA, "x")))
expect_equal(vec_c(NA, new_date(0)), new_date(c(NA, 0)))
expect_equal(vec_c(NA, new_datetime(0)), new_datetime(c(NA, 0)))
expect_equal(vec_c(NA, new_duration(0)), new_duration(c(NA, 0)))
})
test_that("NA is logical if no other types intervene", {
expect_equal(vec_c(logical()), logical())
expect_equal(vec_c(NA), NA)
expect_equal(vec_c(NA, NA), c(NA, NA))
})
test_that("different types are coerced to common", {
expect_equal(vec_c(TRUE, 1L, 1), c(1, 1, 1))
expect_equal(vec_c(TRUE, 2:4), 1:4)
})
test_that("specified .ptypes do not allow more casts", {
expect_error(
vec_c(TRUE, .ptype = character()),
class = "vctrs_error_incompatible_type"
)
})
test_that("common type failure uses error call and error arg (#1641, #1692)", {
expect_snapshot(error = TRUE, {
vec_c("x", 1, .error_call = call("foo"), .error_arg = "arg")
})
expect_snapshot(error = TRUE, {
vec_c("x", .ptype = integer(), .error_call = call("foo"), .error_arg = "arg")
})
})
test_that("common type failure uses positional errors", {
expect_snapshot({
# Looking for `..1` and `a`
(expect_error(vec_c(1, a = "x", 2)))
# Directed cast should also produce positional errors (#1690)
(expect_error(vec_c(1, a = "x", 2, .ptype = double(), .error_arg = "arg")))
# Lossy cast
(expect_error(vec_c(1, a = 2.5, .ptype = integer())))
})
})
test_that("combines outer an inner names", {
expect_equal(vec_c(x = 1), c(x = 1))
expect_equal(vec_c(c(x = 1)), c(x = 1))
expect_equal(vec_c(c(x = 1:2)), c(x1 = 1, x2 = 2))
expect_error(vec_c(y = c(x = 1)), "Please supply")
})
test_that("can bind data.frame columns", {
df <- data.frame(x = NA, y = 1:2)
df$x <- data.frame(a = 1:2)
expected <- data.frame(x = NA, y = c(1:2, 1:2))
expected$x <- data.frame(a = c(1:2, 1:2))
expect_equal(vec_c(df, df), expected)
})
test_that("vec_c() handles matrices", {
m <- matrix(1:4, nrow = 2)
dimnames(m) <- list(c("foo", "bar"), c("baz", "quux"))
# FIXME: `vec_ptype_common(m, m)` doesn't return dimension names
exp <- matrix(c(1:2, 1:2, 3:4, 3:4), nrow = 4)
rownames(exp) <- c("foo", "bar", "foo", "bar")
expect_identical(vec_c(m, m), exp)
expect_error(vec_c(outer = m), "Please supply")
})
test_that("vec_c() includes index in argument tag", {
df1 <- tibble(x = tibble(y = tibble(z = 1)))
df2 <- tibble(x = tibble(y = tibble(z = "a")))
expect_snapshot(error = TRUE, vec_c(df1, df2))
expect_snapshot(error = TRUE, vec_c(df1, df1, df2))
expect_snapshot(error = TRUE, vec_c(foo = df1, bar = df2))
})
test_that("vec_c() handles record classes", {
local_rational_class()
out <- vec_c(rational(1, 2), 1L, NA)
expect_true(vec_is(out, rational(1, 2)))
expect_size(out, 3)
expect_identical(vec_proxy(out), data.frame(n = c(1L, 1L, NA), d = c(2L, 1L, NA)))
})
test_that("can mix named and unnamed vectors (#271)", {
expect_identical(vec_c(c(a = 1), 2), c(a = 1, 2))
expect_identical(vec_c(0, c(a = 1), 2, b = 3), c(0, a = 1, 2, b =3))
})
test_that("preserves names when inputs are cast to a common type (#1690)", {
expect_named(vec_c(c(a = 1), .ptype = integer()), "a")
expect_named(vec_c(foo = c(a = 1), .ptype = integer(), .name_spec = "{outer}_{inner}"), "foo_a")
})
test_that("vec_c() repairs names", {
local_name_repair_quiet()
# Default minimal repair
expect_named(vec_c(a = 1, a = 2, `_` = 3), c("a", "a", "_"))
out <- vec_c(!!!set_names(1, NA))
expect_named(out, "")
expect_named(vec_c(a = 1, a = 2, `_` = 3, .name_repair = "unique"), c("a...1", "a...2", "_"))
expect_error(vec_c(a = 1, a = 2, `_` = 3, .name_repair = "check_unique"), class = "vctrs_error_names_must_be_unique")
expect_named(vec_c(a = 1, a = 2, `_` = 3, .name_repair = "universal"), c("a...1", "a...2", "._"))
expect_named(vec_c(a = 1, a = 2, .name_repair = ~ toupper(.)), c("A", "A"))
})
test_that("vec_c() can repair names quietly", {
local_name_repair_verbose()
expect_snapshot({
res_unique <- vec_c(x = TRUE, x = 0, .name_repair = "unique_quiet")
res_universal <- vec_c("if" = TRUE, "in" = 0, .name_repair = "universal_quiet")
})
expect_named(res_unique, c("x...1", "x...2"))
expect_named(res_universal, c(".if", ".in"))
})
test_that("vec_c() doesn't use outer names for data frames (#524)", {
x <- data.frame(inner = 1)
expect_equal(vec_c(outer = x), x)
a <- data.frame(x = 1L)
b <- data.frame(x = 2L)
expect_equal(vec_c(foo = a, bar = b), data.frame(x = 1:2))
})
test_that("vec_c() preserves row names and inner names", {
x <- data.frame(a = 1, row.names = "r1")
y <- data.frame(a = 2, row.names = "r2")
expect_equal(rownames(vec_c(x, y)), c("r1", "r2"))
expect_equal(rownames(vec_c(x, x)), c("r1...1", "r1...2"))
vec_x <- set_names(1:3, letters[1:3])
vec_y <- c(FOO = 4L)
oo_x <- set_names(as.POSIXlt(c("2020-01-01", "2020-01-02", "2020-01-03")), letters[1:3])
oo_y <- as.POSIXlt(c(FOO = "2020-01-04"))
df_x <- new_data_frame(list(x = 1:3), row.names = letters[1:3])
df_y <- new_data_frame(list(x = 4L), row.names = "d")
mat_x <- matrix(1:3, 3, dimnames = list(letters[1:3]))
mat_y <- matrix(4L, 1, dimnames = list("d"))
nested_x <- new_data_frame(
list(df = df_x, mat = mat_x, vec = vec_x, oo = oo_x),
row.names = c("foo", "bar", "baz")
)
nested_y <- new_data_frame(
list(df = df_y, mat = mat_y, vec = vec_y, oo = oo_y),
row.names = c("quux")
)
nested_out <- vec_c(nested_x, nested_y)
expect_identical(row.names(nested_out), c("foo", "bar", "baz", "quux"))
expect_identical(row.names(nested_out$df), c("a", "b", "c", "d"))
expect_identical(row.names(nested_out$mat), c("a", "b", "c", "d"))
expect_identical(names(nested_out$vec), c("a", "b", "c", "FOO"))
expect_identical(names(nested_out$oo), c("a", "b", "c", "FOO"))
})
test_that("vec_c() outer names work with proxied objects", {
x <- as.POSIXlt(new_datetime(0))
exp <- set_names(x, "outer")
expect_equal(vec_c(outer = x), exp)
named_x <- set_names(x, "inner")
exp <- set_names(named_x, "outer_inner")
expect_error(vec_c(outer = named_x), "Please supply")
expect_equal(vec_c(outer = named_x, .name_spec = "{outer}_{inner}"), exp)
xs <- as.POSIXlt(new_datetime(c(0, 1)))
exp <- set_names(xs, c("outer_1", "outer_2"))
expect_error(vec_c(outer = xs), "Please supply")
expect_equal(vec_c(outer = xs, .name_spec = "{outer}_{inner}"), exp)
})
test_that("vec_c() works with simple homogeneous foreign S3 classes", {
expect_identical(vec_c(foobar(1), foobar(2)), vec_c(foobar(c(1, 2))))
expect_identical(vec_c(NULL, foobar(1), foobar(2)), vec_c(foobar(c(1, 2))))
})
test_that("vec_c() works with simple homogeneous foreign S4 classes", {
joe1 <- .Counts(c(1L, 2L), name = "Joe")
joe2 <- .Counts(3L, name = "Joe")
expect_identical(vec_c(joe1, joe2), .Counts(1:3, name = "Joe"))
})
test_that("vec_c() fails with complex foreign S3 classes", {
expect_snapshot({
x <- structure(foobar(1), attr_foo = "foo")
y <- structure(foobar(2), attr_bar = "bar")
(expect_error(vec_c(x, y), class = "vctrs_error_incompatible_type"))
(expect_error(vec_c(x, y, .error_call = call("foo"), .error_arg = "arg"), class = "vctrs_error_incompatible_type"))
})
})
test_that("vec_c() fails with complex foreign S4 classes", {
expect_snapshot({
joe <- .Counts(c(1L, 2L), name = "Joe")
jane <- .Counts(3L, name = "Jane")
(expect_error(vec_c(joe, jane), class = "vctrs_error_incompatible_type"))
(expect_error(vec_c(joe, jane, .error_call = call("foo"), .error_arg = "arg"), class = "vctrs_error_incompatible_type"))
})
})
test_that("vec_c() falls back to c() if S3 method is available", {
# Check off-by-one error
expect_error(
vec_c(foobar(1), "", foobar(2)),
class = "vctrs_error_incompatible_type"
)
# Fallback when the class implements `c()`
method <- function(...) rep_along(list(...), "dispatched")
local_methods(
c.vctrs_foobar = method
)
expect_identical(
vec_c(foobar(1), foobar(2, class = "foo")),
c("dispatched", "dispatched")
)
expect_identical(
vec_c(NULL, foobar(1), NULL, foobar(2, class = "foo")),
c("dispatched", "dispatched")
)
# Registered fallback
s3_register("base::c", "vctrs_c_fallback", method)
expect_identical(
vec_c(
structure(1, class = "vctrs_c_fallback"),
structure(2, class = "vctrs_c_fallback")
),
c("dispatched", "dispatched")
)
# Don't fallback for S3 lists which are treated as scalars by default
expect_error(
vec_c(foobar(list(1)), foobar(list(2))),
class = "vctrs_error_scalar_type"
)
})
test_that("c() fallback is consistent", {
dispatched <- function(x) structure(x, class = "dispatched")
c_method <- function(...) dispatched(NextMethod())
out <- with_methods(
vec_ptype2.vctrs_foobaz.vctrs_foobaz = function(...) foobaz(df_ptype2(...)),
vec_cast.vctrs_foobaz.vctrs_foobaz = function(...) foobaz(df_cast(...)),
c.vctrs_foobar = c_method,
list(
direct = vec_c(foobar(1L), foobar(2L)),
df = vec_c(data_frame(x = foobar(1L)), data_frame(x = foobar(2L))),
tib = vec_c(tibble(x = foobar(1L)), tibble(x = foobar(2L))),
foreign_df = vec_c(foobaz(data_frame(x = foobar(1L))), foobaz(data_frame(x = foobar(2L))))
)
)
expect_equal(out$direct, dispatched(1:2))
expect_equal(out$df$x, dispatched(1:2))
expect_equal(out$tib$x, dispatched(1:2))
expect_equal(out$foreign_df$x, dispatched(1:2))
# Hard case: generic record vectors
my_rec_record <- function(x) {
new_rcrd(list(x = x), class = "my_rec_record")
}
out <- with_methods(
c.vctrs_foobar = c_method,
vec_ptype2.my_rec_record.my_rec_record = function(x, y, ...) {
my_rec_record(vec_ptype2(field(x, "x"), field(y, "x"), ...))
},
vec_cast.my_rec_record.my_rec_record = function(x, to, ...) {
x
},
vec_c(
data_frame(x = my_rec_record(foobar(1L))),
data_frame(x = my_rec_record(foobar(2L)))
)
)
expect_equal(field(out$x, "x"), dispatched(1:2))
})
test_that("vec_c() falls back to c() if S4 method is available", {
joe1 <- .Counts(c(1L, 2L), name = "Joe")
joe2 <- .Counts(3L, name = "Joe")
c_counts <- function(x, ...) {
xs <- list(x, ...)
xs_data <- lapply(xs, function(x) x@.Data)
new_data <- do.call(c, xs_data)
.Counts(new_data, name = x@name)
}
local_s4_method("c", methods::signature(x = "vctrs_Counts"), c_counts)
expect_identical(
vec_c(joe1, joe2),
.Counts(c(1L, 2L, 3L), name = "Joe")
)
expect_identical(
vec_c(NULL, joe1, joe2),
.Counts(c(1L, 2L, 3L), name = "Joe")
)
})
test_that("vec_c() fallback doesn't support `name_spec` or `ptype`", {
expect_snapshot({
(expect_error(
with_c_foobar(vec_c(foobar(1), foobar(2), .name_spec = "{outer}_{inner}")),
"name specification"
))
# Used to be an error about `ptype`
(expect_error(
with_c_foobar(vec_c(foobar(1), foobar(2), .ptype = "")),
class = "vctrs_error_incompatible_type"
))
# Uses error call (#1641)
(expect_error(
with_c_foobar(vec_c(
foobar(1), foobar(2),
.error_call = call("foo"),
.name_spec = "{outer}_{inner}"
))
))
})
})
test_that("vec_c() doesn't fall back when ptype2 is implemented", {
new_quux <- function(x) structure(x, class = "vctrs_quux")
with_methods(
vec_ptype2.vctrs_foobar.vctrs_foobar = function(x, y, ...) new_quux(int()),
vec_cast.vctrs_quux.vctrs_foobar = function(x, to, ...) new_quux(x),
vec_restore.vctrs_quux = function(x, ...) new_quux(x),
c.vctrs_foobar = function(...) foobar(NextMethod()),
{
expect_s3_class(c(foobar(1:3), foobar(4L)), "vctrs_foobar")
expect_s3_class(vec_c(foobar(1:3), foobar(4L)), "vctrs_quux")
}
)
})
test_that("vec_c() falls back even when ptype is supplied", {
expect_foobar(vec_c(foobar(1), foobar(2), .ptype = foobar(dbl())))
with_methods(
c.vctrs_foobar = function(...) quux(NextMethod()),
{
expect_quux(vec_c(foobar(1), foobar(2), .ptype = foobar(dbl())))
expect_quux(vec_c(foobar(1, foo = TRUE), foobar(2, bar = TRUE), .ptype = foobar(dbl())))
}
)
})
test_that("vec_implements_ptype2() is FALSE for scalars", {
expect_false(vec_implements_ptype2(quote(foo)))
})
test_that("vec_implements_ptype2() and vec_c() fallback are compatible with old registration", {
foo <- structure(NA, class = "vctrs_implements_ptype2_false")
expect_false(vec_implements_ptype2(foo))
vec_ptype2.vctrs_implements_ptype2_true <- function(...) NULL
s3_register(
"vctrs::vec_ptype2",
"vctrs_implements_ptype2_true",
vec_ptype2.vctrs_implements_ptype2_true
)
bar <- structure(NA, class = "vctrs_implements_ptype2_true")
expect_true(vec_implements_ptype2(bar))
local_methods(
`c.vctrs_implements_ptype2_true` = function(...) stop("never called")
)
expect_identical(vec_c(bar), bar)
})
test_that("can ignore names in `vec_c()` by providing a `zap()` name-spec (#232)", {
expect_error(vec_c(a = c(b = 1:2)))
expect_identical(vec_c(a = c(b = 1:2), b = 3L, .name_spec = zap()), 1:3)
expect_snapshot({
(expect_error(
vec_c(a = c(b = letters), b = 1, .name_spec = zap()),
class = "vctrs_error_incompatible_type"
))
})
})
test_that("can concatenate subclasses of `vctrs_vctr` which don't have ptype2 methods", {
x <- new_vctr(1, class = "vctrs_foo")
expect_identical(vec_c(x, x), new_vctr(c(1, 1), class = "vctrs_foo"))
})
test_that("base c() fallback handles unspecified chunks", {
local_methods(
c.vctrs_foobar = function(...) {
x <- NextMethod()
# Should not be passed any unspecified chunks
if (anyNA(x)) {
abort("tilt")
}
foobar(x)
},
`[.vctrs_foobar` = function(x, i, ...) {
# Return a quux to detect dispatch
quux(NextMethod())
}
)
out <- vec_c(foobar(1:2), rep(NA, 2))
expect_identical(out, quux(c(1:2, NA, NA)))
out <- vec_c(rep(NA, 2), foobar(1:2), NA)
expect_identical(out, quux(c(NA, NA, 1:2, NA)))
})
test_that("can zap outer names from a name-spec (#1215)", {
zap_outer_spec <- function(outer, inner) if (is_character(inner)) inner
expect_null(
names(vec_c(a = 1:2, .name_spec = zap_outer_spec))
)
expect_identical(
names(vec_c(a = 1:2, c(foo = 3L), .name_spec = zap_outer_spec)),
c("", "", "foo")
)
expect_null(
names(list_unchop(list(a = 1:2), indices = list(1:2), name_spec = zap_outer_spec))
)
expect_identical(
names(list_unchop(list(a = 1:2, c(foo = 3L)), indices = list(1:2, 3), name_spec = zap_outer_spec)),
c("", "", "foo")
)
})
test_that("named empty vectors force named output (#1263)", {
x <- set_names(int(), chr())
expect_named(vec_c(x), chr())
expect_named(vec_c(x, x), chr())
expect_named(vec_c(x, 1L), "")
expect_named(vec_c(x, 1), "")
expect_named(list_unchop(list(x), indices = list(int())), chr())
expect_named(list_unchop(list(x, x), indices = list(int(), int())), chr())
expect_named(list_unchop(list(x, 1L), indices = list(int(), 1)), "")
expect_named(list_unchop(list(x, 1), indices = list(int(), 1)), "")
})
# Golden tests -------------------------------------------------------
test_that("concatenation performs expected allocations", {
vec_c_list <- function(x, ptype = NULL) {
vec_c(!!!x, .ptype = ptype)
}
expect_snapshot({
ints <- rep(list(1L), 1e2)
dbls <- rep(list(1), 1e2)
# Extra allocations from `list2()`, see r-lib/rlang#937
"# `vec_c()` "
"Integers"
with_memory_prof(vec_c_list(ints))
"Doubles"
with_memory_prof(vec_c_list(dbls))
"Integers to integer"
with_memory_prof(vec_c_list(ints, ptype = int()))
"Doubles to integer"
with_memory_prof(vec_c_list(dbls, ptype = int()))
"# `list_unchop()` "
"Integers"
with_memory_prof(list_unchop(ints))
"Doubles"
with_memory_prof(list_unchop(dbls))
"Integers to integer"
with_memory_prof(list_unchop(ints, ptype = int()))
"Doubles to integer"
with_memory_prof(list_unchop(dbls, ptype = int()))
"# Concatenation with names"
"Named integers"
ints <- rep(list(set_names(1:3, letters[1:3])), 1e2)
with_memory_prof(list_unchop(ints))
"Named matrices"
mat <- matrix(1:4, 2, dimnames = list(c("foo", "bar")))
mats <- rep(list(mat), 1e2)
with_memory_prof(list_unchop(mats))
"Data frame with named columns"
df <- data_frame(
x = set_names(as.list(1:2), c("a", "b")),
y = set_names(1:2, c("A", "B")),
z = data_frame(Z = set_names(1:2, c("Za", "Zb")))
)
dfs <- rep(list(df), 1e2)
with_memory_prof(list_unchop(dfs))
"Data frame with rownames (non-repaired, non-recursive case)"
df <- data_frame(x = 1:2)
dfs <- rep(list(df), 1e2)
dfs <- map2(dfs, seq_along(dfs), set_rownames_recursively)
with_memory_prof(list_unchop(dfs))
"Data frame with rownames (repaired, non-recursive case)"
dfs <- map(dfs, set_rownames_recursively)
with_memory_prof(list_unchop(dfs))
"Data frame with rownames (non-repaired, recursive case) (#1217)"
df <- data_frame(
x = 1:2,
y = data_frame(x = 1:2)
)
dfs <- rep(list(df), 1e2)
dfs <- map2(dfs, seq_along(dfs), set_rownames_recursively)
with_memory_prof(list_unchop(dfs))
"Data frame with rownames (repaired, recursive case) (#1217)"
dfs <- map(dfs, set_rownames_recursively)
with_memory_prof(list_unchop(dfs))
"list-ofs (#1496)"
make_list_of <- function(n) {
df <- tibble::tibble(
x = new_list_of(vec_chop(1:n), ptype = integer())
)
vec_chop(df)
}
with_memory_prof(list_unchop(make_list_of(1e3)))
with_memory_prof(list_unchop(make_list_of(2e3)))
with_memory_prof(list_unchop(make_list_of(4e3)))
})
})
test_that("can dispatch many times", {
# This caused a crash when counters were not correctly protected
foo <- structure(
list(x.sorted = numeric(0), tp = numeric(0), fp = numeric(0)),
row.names = integer(0),
class = c("vctrs_foobar", "tbl_df", "tbl", "data.frame")
)
x <- lapply(1:200, function(...) foo)
expect_error(NA, object = vctrs::list_unchop(x))
})
test_that("dots splicing clones as appropriate", {
x <- list(a = 1)
vctrs::vec_cbind(!!!x)
expect_equal(x, list(a = 1))
x <- list(a = 1)
vctrs::vec_rbind(!!!x)
expect_equal(x, list(a = 1))
x <- list(a = 1)
vctrs::vec_c(!!!x)
expect_equal(x, list(a = 1))
x <- list(a = 1)
vctrs::vec_cbind(!!!x, 2)
expect_equal(x, list(a = 1))
x <- list(a = 1)
vctrs::vec_rbind(!!!x, 2)
expect_equal(x, list(a = 1))
x <- list(a = 1)
vctrs::vec_c(!!!x, 2)
expect_equal(x, list(a = 1))
})
test_that("can combine records wrapped in data frames", {
local_methods(
vec_proxy.vctrs_foobar = function(x, ...) {
data_frame(x = unclass(x), y = seq_along(x))
},
vec_restore.vctrs_foobar = function(x, to, ...) {
foobar(x$x)
}
)
x <- foobar(1:2)
y <- foobar(3:4)
expect_equal(
vec_c(x, y),
foobar(1:4)
)
expect_equal(
list_unchop(list(x, y), indices = list(1:2, 3:4)),
foobar(1:4)
)
expect_equal(
vec_rbind(data_frame(x = x), data_frame(x = y)),
data_frame(x = foobar(1:4))
)
})
test_that("fallback works with subclasses of `vctrs_vctr`", {
# Used to fail because of interaction between common class fallback
# for `base::c()` and the `c()` method for `vctrs_vctr` that called
# back into `vec_c()`.
# Reprex for failure in the ricu package
x <- new_rcrd(list(a = 1), class = "vctrs_foobar")
expect_equal(
vec_c(x, x, .name_spec = "{inner}"),
new_rcrd(list(a = c(1, 1)), class = "vctrs_foobar")
)
# Reprex for failure in the groupr package
x <- new_rcrd(list(a = 1), class = "vctrs_foobar")
df <- data_frame(x = x)
expect_equal(
vec_rbind(df, data.frame()),
df
)
expect_equal(
vec_cast_common(df, data.frame()),
list(df, data_frame(x = x[0]))
)
})
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.