Nothing
describe("property retrieval", {
it("retrieves the properties that exist & errors otherwise", {
foo <- new_class("foo", properties = list(xyz = class_double), package = NULL)
obj <- foo(1)
expect_equal(prop(obj, "xyz"), 1)
expect_equal(obj@xyz, 1)
expect_snapshot_error(prop(obj, "x"))
expect_snapshot_error(obj@x)
})
it("evalutes dynamic properties", {
foo <- new_class("foo", properties = list(
x = new_property(getter = function(self) 1)
))
obj <- foo()
expect_equal(prop(obj, "x"), 1)
expect_equal(obj@x, 1)
})
it("falls back to `base::@` for non-S7 objects", {
expect_error("foo"@blah, 'object of.+class.+"character"')
expect_error(NULL@blah, 'object of.+class.+"NULL"')
})
})
describe("prop setting", {
it("can set a property", {
foo <- new_class("foo", properties = list(xyz = class_double))
obj <- foo(1)
prop(obj, "xyz") <- 2
expect_equal(obj@xyz, 2)
obj@xyz <- 3
expect_equal(obj@xyz, 3)
})
it("can set dynamic properties", {
foo <- new_class("foo", properties = list(
x = new_property(setter = function(self, value) {
self@x <- value * 2
self
})
))
obj <- foo()
obj@x <- 1
expect_equal(obj@x, 2)
})
it("can't set read-only properties", {
foo <- new_class("foo", package = NULL, properties = list(
x = new_property(getter = function(self) 1
)))
obj <- foo()
expect_snapshot(obj@x <- 1, error = TRUE)
})
it("errors if the property doesn't exist or is wrong class", {
foo <- new_class("foo", properties = list(x = class_double), package = NULL)
expect_snapshot(error = TRUE, {
obj <- foo(123)
obj@foo <- 10
obj@x <- "x"
})
})
it("validates all attributes if custom setter", {
foo <- new_class("foo", package = NULL, properties = list(
x = new_property(
class_double,
setter = function(self, value) {
self@x <- 123
self@y <- value
self
}
),
y = new_property(class_double)
))
expect_snapshot(error = TRUE, {
obj <- foo(y = 123, x = 123)
obj@x <- "x"
})
})
it("validates once after custom setter", {
times_validated <- 0L; `add<-` <- `+`
custom_setter <- function(self, value) {
self@x <- as.double(value)
self
}
foo2 <- new_class(
"foo2",
properties = list(x = new_property(class_double, setter = custom_setter)),
validator = function(self) {
add(times_validated) <<- 1L
character()
}
)
obj <- foo2("123")
expect_equal(times_validated, 1)
obj@x <- "456"
expect_equal(times_validated, 2)
})
it("validates once with recursive property setters", {
times_validated <- 0L; `add<-` <- `+`
foo <- new_class(
"foo",
properties = list(
x = new_property(setter = function(self, value) {
self@x <- value
self@y <- paste0(value, "_set_by_x_setter")
self
}),
y = new_property(setter = function(self, value) {
self@y <- value
self@z <- paste0(value, "_set_by_y_setter")
self
}),
z = new_property(class_character)
),
validator = function(self) { add(times_validated) <<- 1L; NULL }
)
out <- foo()
expect_equal(times_validated, 1L)
out@x <- "VAL"
expect_equal(times_validated, 2L)
expect_equal(out@z, "VAL_set_by_x_setter_set_by_y_setter")
})
it("does not run the check or validation functions if check = FALSE", {
foo <- new_class("foo", properties = list(x = class_double))
obj <- foo(123)
prop(obj, "x", check = FALSE) <- "foo"
expect_equal(obj@x, "foo")
})
it("falls back to `base::@` for non-S7 objects", {
x <- "foo"
expect_error(x@blah <- "bar", "is not a slot in class")
})
})
describe("props<-", {
it("validates after setting all properties", {
foo <- new_class("foo",
properties = list(x = class_double, y = class_double),
validator = function(self) if (self@x > self@y) "bad"
)
obj <- foo(1, 2)
props(obj) <- list(x = 5, y = 10)
expect_equal(obj@x, 5)
expect_equal(obj@y, 10)
})
it("has ordinary syntax in set_props()", {
foo <- new_class("foo", properties = list(x = class_double))
obj1 <- foo(1)
obj2 <- set_props(obj1, x = 2)
expect_equal(obj1@x, 1)
expect_equal(obj2@x, 2)
})
})
describe("property access", {
it("access en masse", {
foo <- new_class("foo", properties = list(x = class_numeric, y = class_numeric))
x <- foo(x = 1, y = 2)
expect_equal(prop_names(x), c("x", "y"))
expect_equal(props(x), list(x = 1, y = 2))
expect_true(prop_exists(x, "x"))
expect_true(prop_exists(x, "y"))
expect_false(prop_exists(x, "z"))
})
it("can access dynamic properties", {
foo <- new_class("foo", properties = list(
x = new_property(getter = function(self) 10),
y = new_property()
))
x <- foo(y = 2)
expect_equal(props(x), list(x = 10, y = 2))
})
it("can with property-less object", {
x <- new_class("x")()
expect_equal(prop_names(x), character())
expect_equal(props(x), named_list())
expect_equal(prop_exists(x, "y"), FALSE)
})
it("ignore attributes that are not properties", {
x <- new_class("x")()
attr(x, "extra") <- 1
expect_equal(prop_names(x), character())
expect_equal(props(x), named_list())
expect_false(prop_exists(x, "extra"))
})
})
test_that("properties can be NULL", {
foo <- new_class("foo", properties = list(x = class_any))
x <- foo(x = NULL)
expect_equal(x@x, NULL)
x@x <- 1
expect_equal(x@x, 1)
x@x <- NULL
expect_equal(x@x, NULL)
expect_equal(prop_names(x), "x")
expect_equal(props(x), list(x = NULL))
})
describe("new_property()", {
it("validates getter and settor", {
expect_snapshot(error = TRUE, {
new_property(getter = function(x) {})
new_property(setter = function(x, y, z) {})
})
})
it("validates default", {
expect_snapshot(error = TRUE, {
new_property(class_integer, default = "x")
})
})
it("displays nicely", {
x <- new_property(class_integer, name = "foo")
expect_snapshot({
print(x)
str(list(x))
})
})
})
test_that("properties can be base, S3, S4, S7, or S7 union", {
class_S7 <- new_class("class_S7", package = NULL)
class_S4 <- methods::setClass("class_S4", slots = c(x = "numeric"))
my_class <- new_class("my_class",
package = NULL,
properties = list(
anything = class_any,
null = NULL,
base = class_integer,
S3 = class_factor,
S4 = class_S4,
S7 = class_S7,
S7_union = new_union(class_integer, class_logical)
)
)
expect_snapshot(my_class)
my_obj <- my_class(
anything = TRUE,
null = NULL,
base = 1L,
S3 = factor(),
S4 = class_S4(x = 1),
S7 = class_S7(),
S7_union = 1L
)
# First check that we can set with out error
expect_error(my_obj@base <- 2L, NA)
expect_error(my_obj@S3 <- factor("x"), NA)
expect_error(my_obj@S4 <- class_S4(x = 2), NA)
expect_error(my_obj@S7 <- class_S7(), NA)
expect_error(my_obj@S7_union <- 2L, NA)
expect_error(my_obj@S7_union <- TRUE, NA)
# Then capture the error messages for human inspection
expect_snapshot(error = TRUE, {
my_obj@null <- "x"
my_obj@base <- "x"
my_obj@S3 <- "x"
my_obj@S4 <- "x"
my_obj@S7 <- "x"
my_obj@S7_union <- "x"
})
})
test_that("as_properties normalises properties", {
expect_equal(as_properties(NULL), list())
expect_equal(
as_properties(list(x = class_numeric)),
list(x = new_property(class_numeric, name = "x")
))
expect_equal(
as_properties(list(x = new_property(class = class_numeric))),
list(x = new_property(class_numeric, name = "x")
))
expect_equal(
as_properties(list(new_property(name = "y"))),
list(y = new_property(name = "y")
))
# list name wins
expect_equal(
as_properties(list(x = new_property(name = "y"))),
list(x = new_property(name = "x")
))
})
test_that("as_properties() gives useful error messages", {
expect_snapshot(error = TRUE, {
as_properties(1)
as_properties(list(1))
as_properties(list(new_property(class_character)))
as_properties(list(x = 1))
as_properties(list(x = class_character, x = class_character))
})
})
test_that("can validate with custom validator", {
validate_scalar <- function(value) {
if (length(value) != 1) {
"must be length 1"
}
}
prop <- new_property(class_integer, validator = validate_scalar)
foo <- new_class("foo", package = NULL, properties = list(x = prop))
expect_snapshot(error = TRUE, {
f <- foo(x = 1L)
f@x <- 1:2
foo(x = 1:2)
})
})
test_that("prop<- won't infinitly recurse on a custom setter", {
chattily_sync_ab <- function(self, value) {
cat("Starting syncup with value:", value, "\n")
a_value <- paste0("a_", value)
b_value <- paste0("b_", value)
cat(sprintf('setting @a <- "%s"\n', a_value))
self@a <- a_value
cat(sprintf('setting @b <- "%s"\n', b_value))
self@b <- b_value
self
}
foo <- new_class("foo", properties = list(
a = new_property(setter = chattily_sync_ab),
b = new_property(setter = chattily_sync_ab)
))
expect_snapshot({
obj <- foo()
obj@a <- "val"
})
})
test_that("custom setters can invoke setters on non-self objects", {
Transmitter <- new_class("Transmitter", properties = list(
message = new_property(setter = function(self, value) {
cat("[tx] sending: ", value, "\n")
receiver@message <<- value
cat("[tx] saving last sent message.\n")
self@message <- value
cat("[tx] finished transmitting.\n")
self
})
))
Receiver <- new_class("Receiver", properties = list(
message = new_property(setter = function(self, value) {
cat("[rx] receiving: ", value, "\n")
self@message <- value
cat("[rx] finished receiving.\n")
self
})
))
expect_snapshot({
receiver <- Receiver()
transmitter <- Transmitter()
transmitter@message <- "hello"
expect_equal(receiver@message, "hello")
transmitter@message <- "goodbye"
expect_equal(receiver@message, "goodbye")
})
})
test_that("custom getters don't infinitely recurse", {
# https://github.com/RConsortium/S7/issues/403
someclass <- new_class("someclass", properties = list(
someprop = new_property(
class_character,
getter = function(self) self@someprop,
setter = function(self, value) {
self@someprop <- toupper(value)
self
}
)
))
expect_equal(someclass("foo")@someprop, "FOO")
x <- someclass()
expect_equal(x@someprop, character())
x@someprop <- "foo"
expect_equal(x@someprop, "FOO")
})
test_that("custom setters can call custom getters", {
# https://github.com/RConsortium/S7/issues/403
someclass <- new_class("someclass", properties = list(
someprop = new_property(
class_character,
getter = function(self) self@someprop,
setter = function(self, value) {
self@someprop <- paste0(self@someprop, toupper(value))
self
}
)
))
x <- someclass("foo")
expect_equal(x@someprop, "FOO")
x <- someclass()
expect_equal(x@someprop, character())
x@someprop <- "foo"
expect_equal(x@someprop, "FOO")
x@someprop <- "foo"
expect_equal(x@someprop, "FOOFOO")
})
test_that("custom getters don't evaulate call objects", {
QuotedCall := new_class(class_call, properties = list(
name = new_property(getter = function(self) {
stopifnot(is.call(self))
as.character(self[[1]])
}),
args = new_property(getter = function(self) {
stopifnot(is.call(self))
as.list(self)[-1]
})
), constructor = function(x) {
new_object(substitute(x))
})
cl <- QuotedCall(stop("boom"))
expect_equal(cl@name, "stop")
expect_equal(cl@args, list("boom"))
})
test_that("custom setters don't evaulate call objects", {
Call := new_class(class_call, properties = list(
name = new_property(
getter = function(self) {
stopifnot(is.call(self))
as.character(self[[1]])
},
setter = function(self, value) {
stopifnot(is.call(self), is.name(value))
self[[1]] <- value
self
}
),
args = new_property(
getter = function(self) {
stopifnot(is.call(self))
as.list(self)[-1]
},
setter = function(self, value) {
stopifnot(is.call(self), is.list(value) || is.pairlist(value))
# self[seq(2, length.out = length(value))] <- value
# names(self) <- c("", names(value))
# self
out <- as.call(c(self[[1]], value))
attributes(out) <- attributes(self)
out
})
), constructor = function(name, ...) {
new_object(as.call(c(as.name(name), ...)))
})
cl <- Call("stop", "boom")
expect_identical(cl@name, "stop")
expect_identical(cl@args, list("boom"))
abort <- stop
cl@name <- quote(abort)
expect_identical(cl@name, "abort")
expect_identical(cl[[1]], quote(abort))
cl@args <- pairlist("boom2")
expect_identical(cl[[2]], "boom2")
expect_identical(cl@args, list("boom2"))
expect_identical(drop_attributes(cl), quote(abort("boom2")))
cl@args <- alist(msg = "boom3", foo = bar, baz)
expect_identical(cl@args, alist(msg = "boom3", foo = bar, baz))
expect_identical(drop_attributes(cl),
quote(abort(msg = "boom3", foo = bar, baz)))
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.