blob: ae92cee84c8ed32b3c8ae25d4a1a4f78d48604a7 [file] [log] [blame]
## simple call, only field names
fg <- setRefClass("foo", c("bar", "flag"))
f0 <- new("foo") # deprecated, but should still work
f1 <- fg(flag = "testing")
f1$bar <- 1
stopifnot(identical(f1$bar, 1))
## add method
fg$methods(showAll = function() c(bar, flag))
stopifnot(all.equal(f1$showAll(), c(1, "testing")))
str(f1)
fg <- setRefClass("foo", list(bar = "numeric", flag = "character",
tag = "ANY"),
methods = list(addToBar = function(incr) {
b <- bar + incr
bar <<- b
b
} )
)
fg$lock("flag")
stopifnot(identical(fg$lock(), "flag"))
ff <- new("foo", bar = 1.5)
stopifnot(identical(ff$bar, 1.5))
ff$bar <- pi
stopifnot(identical(ff$bar, pi))
## flag has not yet been set
ff$flag <- "flag test"
stopifnot(identical(ff$flag, "flag test"))
## but no second assign
stopifnot(is(tryCatch(ff$flag <- "new", error = function(e)e), "error"))
## test against generator
f2 <- fg(bar = pi, flag = "flag test")
## identical does not return TRUE if *contents* of env are identical
stopifnot(identical(ff$bar, f2$bar), identical(ff$flag, f2$flag))
## but flag was now assigned once
stopifnot(is(tryCatch(f2$flag <- "new", error = function(e)e), "error"))
str(f2)
## add some accessor methods
fg$accessors("bar")
ff$setBar(1:3)
stopifnot(identical(ff$getBar(), 1:3))
ff$getBar()
stopifnot(all.equal(ff$addToBar(1), 2:4))
## Add a method
fg$methods(barTimes = function(x) {
"This method multiples field bar by argument x
and this string is self-documentation"
setBar(getBar() * x)})
ffbar <- ff$getBar()
ff$barTimes(10)
stopifnot(all.equal(ffbar * 10, ff$getBar()))
ff$barTimes(.1)
## inheritance. redefines flag so should fail:
stopifnot(is(tryCatch(setRefClass("foo2", list(b2 = "numeric",
flag = "complex"),
contains = "foo",
refMethods = list(addBoth = function(incr) {
addToBar(incr) #uses inherited class method
setB2(getB2() + incr)
})),
error = function(e)e), "error"))
## but with flag as a subclass of "characters", should work
## Also subclasses "tag" which had class "ANY before
setClass("ratedChar", contains = "character",
representation(score = "numeric"))
foo2 <- setRefClass("foo2", list(b2 = "numeric", flag = "ratedChar",
tag = "numeric"),
contains = "foo",
methods = list(addBoth = function(incr) {
addToBar(incr) #uses inherited class method
b2 <<- b2 + incr
}))
## now lock the flag field; should still allow one write
foo2$lock("flag")
f2 <- foo2(bar = -3, flag = as("ANY", "ratedChar"),
b2 = ff$bar, tag = 1.5)
## but not a second one
stopifnot(is(tryCatch(f2$flag <- "Try again",
error = function(e)e), "error"))
str(f2)
f22 <- foo2(bar = f2$bar)
## same story if assignment follows the initialization
f22$flag <- f2$flag
stopifnot(is(tryCatch(f22$flag <- "Try again",
error = function(e)e), "error"))
## Exporting superclass object
f22 <- fg(bar = f2$bar, flag = f2$flag)
f2e <- f2$export("foo")
stopifnot(identical(f2e$bar, f22$bar), identical(f2e$flag, f22$flag),
identical(class(f2e), class(f22)))
stopifnot(identical(f2$flag, as("ANY", "ratedChar")),
identical(f2$bar, -3),
all.equal(f2$b2, 2:4+0))
f2$addBoth(-1)
stopifnot(all.equal(f2$bar, -4), all.equal(f2$b2, 1:3+0))
## test callSuper()
foo3 <- setRefClass("foo3", fields = list(flag2 = "ratedChar"),
contains = "foo2",
methods = list(addBoth = function(incr) {
callSuper(incr)
flag2 <<- as(paste(flag, paste(incr, collapse = ", "),
sep = "; "),
"ratedChar")
incr
}))
f2 <- foo2(bar = -3, flag = as("ANY", "ratedChar"), b2 = 1:3)
f3 <- foo3()
f3$import(f2)
stopifnot(all.equal(f3$b2, f2$b2), all.equal(f3$bar, f2$bar),
all.equal(f3$flag, f2$flag))
f3$addBoth(1)
stopifnot(all.equal(f3$bar, -2), all.equal(f3$b2, 2:4+0),
all.equal(f3$flag2, as("ANY; 1", "ratedChar")))
## but the import should have used up the one write for $flag
stopifnot(is(tryCatch(f3$flag <- "Try again",
error = function(e)e), "error"))
str(f3)
## importing the same class (not very useful but documented to work)
f3 <- foo3()
f4 <- foo3(bar = -3, flag = as("More", "ratedChar"), b2 = 1:3, flag2 = f2$flag)
f3$import(f4)
stopifnot(identical(f3$bar, f4$bar),
identical(f3$flag, f4$flag),
identical(f3$b2, f4$b2),
identical(f3$flag2, f4$flag2))
## similar to $import() but using superclass object in the generator call
## The explicitly supplied flag= should override and be allowed
## by the default $initialize()
f3b <- foo3(f2, flag = as("Other", "ratedChar"),
flag2 = as("More", "ratedChar"))
## check that inherited and direct field assignments worked
stopifnot(identical(f3b$tag, f2$tag),
identical(f3b$flag, as("Other", "ratedChar")),
identical(f3b$flag2, as("More", "ratedChar")))
## the $new() method should match the generator function
f3b <- foo3$new(f2, flag = as("Other", "ratedChar"),
flag2 = as("More", "ratedChar"))
stopifnot(identical(f3b$tag, f2$tag),
identical(f3b$flag, as("Other", "ratedChar")),
identical(f3b$flag2, as("More", "ratedChar")))
## a class with an initialize method, and an extra slot (legal, not a good idea)
setOldClass(c("simple.list", "list"))
fg4 <- setRefClass("foo4",
contains = "foo2",
methods = list(
initialize = function(...) {
.self$initFields(...)
.self@made <<- R.version
.self
}),
representation = list(made = "simple.list")
)
f4 <- new("foo4", flag = as("another test", "ratedChar"), bar = 1:3)
stopifnot(identical(f4@made, R.version))
## a trivial class with no fields, using fields = list(), failed up to rev 56035
foo5 <- setRefClass("foo5", fields = list(),
methods = list(bar = function(test)
paste("*",test,"*")))
f5 <- foo5()
stopifnot(identical( f5$bar("xxx"), paste("*","xxx", "*")))
## simple active binding test
abGen <- setRefClass("ab",
fields = list(a = "ANY",
b = function(x) if(missing(x)) a else {a <<- x; x}))
ab1 <- abGen(a = 1)
stopifnot(identical(ab1$a, 1), identical(ab1$b, 1))
ab1$b <- 2
stopifnot(identical(ab1$a, 2), identical(ab1$b, 2))
## a simple editor for matrix objects. Method $edit() changes some
## range of values; method $undo() undoes the last edit.
mEditor <- setRefClass("matrixEditor",
fields = list(data = "matrix",
edits = "list"),
methods = list(
edit = function(i, j, value) {
## the following string documents the edit method
'Replaces the range [i, j] of the
object by value.
'
backup <-
list(i, j, data[i,j])
data[i,j] <<- value
edits <<- c(list(backup),
edits)
invisible(value)
},
undo = function() {
'Undoes the last edit() operation
and update the edits field accordingly.
'
prev <- edits
if(length(prev)) prev <- prev[[1]]
else stop("No more edits to undo")
edit(prev[[1]], prev[[2]], prev[[3]])
## trim the edits list
length(edits) <<- length(edits) - 2
invisible(prev)
}
))
xMat <- matrix(1:12,4,3)
xx <- mEditor(data = xMat)
xx$edit(2, 2, 0)
xx$data
xx$undo()
mEditor$help("undo")
stopifnot(all.equal(xx$data, xMat))
## add a method to save the object
mEditor$methods(
save = function(file) {
'Save the current object on the file
in R external object format.
'
base::save(.self, file = file)
},
counter = function(i) {
'The number of items in the i-th edit.
(Used to test usingMethods())
'
if(i > 0 && i <= length(edits))
length(edits[[i]][[3]])
else
0L
}
)
tf <- tempfile()
xx$save(tf) #$
load(tf)
unlink(tf)
stopifnot(identical(xx$data, .self$data))
## tests of $trace() methods
## debugging an object
xx$trace(edit, quote(xxTrace <<- TRUE))
## debugging all objects from class mEditor in method $undo()
mEditor$trace(undo, quote(mETrace <<- TRUE))
xxTrace <- mETrace <- FALSE
xx$edit(2,3,100)
xx$undo()
## will not have changed the xx$undo() method (already used)
stopifnot(identical(xxTrace, TRUE), identical(mETrace, FALSE))
## but a new object works the other way around
xxTrace <- mETrace <- FALSE
xx <- mEditor(data = xMat)
xx$edit(2,3,100)
xx$undo()
stopifnot(identical(xxTrace, FALSE), identical(mETrace, TRUE))
markViewer <- ""
setMarkViewer <- function(what)
markViewer <<- what
## Inheriting a reference class: a matrix viewer
mv <- setRefClass("matrixViewer",
fields = c("viewerDevice", "viewerFile"),
contains = "matrixEditor",
methods = list( view = function() {
dd <- dev.cur(); dev.set(viewerDevice)
devAskNewPage(FALSE)
matplot(data, main = paste("After",length(edits),"edits"))
dev.set(dd)},
edit = # invoke previous method, then replot
function(i, j, value) {
callSuper(i, j, value)
view()
}))
## initialize and finalize methods
mv$methods( initialize = function(file = "./matrixView.pdf", ...) {
viewerFile <<- file
pdf(viewerFile)
viewerDevice <<- dev.cur()
message("Plotting to ", viewerFile)
dev.set(dev.prev())
setMarkViewer("ON")
initFields(...)
},
finalize = function() {
dev.off(viewerDevice)
setMarkViewer("OFF")
})
## a counts method to test usingMethods()
mv$methods( counts = function() {
usingMethods("counter")
sapply(seq_along(edits), "counter")
})
ff <- mv( data = xMat)
stopifnot(identical(markViewer, "ON")) # check initialize
ff$edit(2,2,0)
ff$data
if(methods:::.hasCodeTools()) # otherwise 'counter' is not visible
stopifnot(identical(ff$counts(), length(ff$edits[[1]][[3]])))
ff$undo()
stopifnot(all.equal(ff$data, xMat))
rm(ff)
gc()
stopifnot(identical(markViewer, "OFF")) #check finalize
## tests of copying
viewerPlus <- setRefClass("viewerPlus",
fields = list( text = "character",
viewer = "matrixViewer"))
ff <- mv( data = xMat)
v1 <- viewerPlus(text = letters, viewer = ff)
v2 <- v1$copy()
v3 <- v1$copy(TRUE)
v2$text <- "Hello, world"
v2$viewer$data <- t(xMat) # change a field in v2$viewer
v3$text <- LETTERS
v3$viewer <- mv( data = matrix(nrow=1,ncol=1))
## with a deep copy all is protected, with a shallow copy
## the environment of a copied field remains the same,
## but replacing the whole field should be local
stopifnot(identical(v1$text, letters),
identical(v1$viewer, ff),
identical(v2$text, "Hello, world"))
v3 <- v1$copy(TRUE)
v3$viewer$data <- t(xMat) # should modify v1$viewer as well
stopifnot(identical(v1$viewer$data, t(xMat)))
## the field() method
stopifnot(identical(v1$text, v1$field("text")))
v1$field("text", "Now is the time")
stopifnot(identical(v1$field("text"), "Now is the time"))
## setting a non-existent field, or a method, should throw an error
stopifnot(is(tryCatch(v1$field("foobar", 0), error = function(e)e), "error"),
is(tryCatch(v1$field("copy", 0), error = function(e)e), "error") )
## the methods to extract class definition and generator
stopifnot(identical(v3$getRefClass()$def, getRefClass("viewerPlus")$def),
identical(v3$getClass(), getClass("viewerPlus")))
## deal correctly with inherited methods and overriding existing
## methods from $methods(...)
refClassA <- setRefClass("refClassA", methods=list(foo=function() "A"))
refClassB <- setRefClass("refClassB", contains="refClassA")
mnames <- objects(getClass("refClassB")@refMethods)
refClassB$methods(foo=function() callSuper())
stopifnot(identical(refClassB()$foo(), "A"))
mnames2 <- objects(getClass("refClassB")@refMethods)
stopifnot(identical(mnames2[is.na(match(mnames2,mnames))], "foo#refClassA"))
refClassB$methods(foo=function() paste(callSuper(), "Version 2"))
stopifnot(identical(refClassB()$foo(), "A Version 2"))
stopifnot(identical(mnames2, objects(getClass("refClassB")@refMethods)))
if(methods:::.hasCodeTools()) {
## code warnings assigning locally to field names
stopifnot(is(tryCatch(mv$methods(test = function(x)
{ data <- x[!is.na(x)]; mean(data)}),
warning = function(e)e), "warning"))
## warnings for nonlocal assignment that is not a field
stopifnot(is(tryCatch(mv$methods(test2 = function(x) {something <<- data[!is.na(x)]}), warning = function(e)e), "warning"))
## error for trying to assign to a method name
stopifnot(is(tryCatch(mv$methods(test3 = function(x) {edit <<- data[!is.na(x)]}), error = function(e)e), "error"))
} else
warning("Can't run some tests: recommended package codetools is not available")
## tests (fragmentary by necessity) of promptClass for reference class
ccon <- textConnection("ctxt", "w")
suppressMessages(promptClass("refClassB", filename = ccon))
## look for a method, inheritance, inherited method
stopifnot(length(c(grep("foo.*refClassA", ctxt),
grep("code{foo()}", ctxt, fixed = TRUE),
grep("linkS4class{refClassA", ctxt, fixed = TRUE))) >= 3)
close(ccon)
rm(ctxt)
## tests related to subclassing environments. These really test code in the core, viz. builtin.c
a <- refClassA()
ev <- new.env(parent = a) # parent= arg
stopifnot(is.environment(ev))
foo <- function()"A"; environment(foo) <- a # environment of function
stopifnot(identical(as.environment(a), environment(foo)))
xx <- 1:10; environment(xx) <- a # environment attribute
stopifnot(identical(as.environment(a), environment(xx)))
## tests of [[<- and $<- for subclasses of environment. At one point
## methods for these assignments were defined and caused
## inf. recursion when the arguments to the [[<- case were changed in base.
setClass("myEnv", contains = "environment")
m <- new("myEnv", a="test")
m2 <- new("myEnv"); m3 <- new("myEnv")
## test that new.env() is called for each new object
stopifnot(!identical(as.environment(m), as.environment(m2)),
!identical(as.environment(m3), as.environment(m2)))
m[["x"]] <- 1; m$y <- 2
stopifnot(identical(c(m[["x"]], m$y), c(1,2)), is(m, "myEnv"))
rm(x, envir = m) # check rm() works, does not clobber class
stopifnot(identical(sort(objects(m)), sort(c("a", "y"))),
is(m, "myEnv"))
## tests of binding & environment tools with subclases of environment
lockBinding("y", m)
stopifnot(bindingIsLocked("y", m))
unlockBinding("y", m)
stopifnot(!bindingIsLocked("y", m))
makeActiveBinding("z", function(value) {
if(missing(value))
"dummy"
else
"dummy assignment"
}, m)
stopifnot(identical(get("z", m),"dummy"))
## assignment will return the value but do nothing
stopifnot(identical(assign("z","other", m), "other"),
identical(get("z", m),"dummy"))
## this has to be last--Seems no way to unlock an environment!
lockEnvironment(m)
stopifnot(environmentIsLocked(m))
rm(m)
m <- new("myEnv")
stopifnot(length(ls(m)) == 0)
## used to contain the previous content
## test of callSuper() to a hidden default method for initialize() (== initFields)
TestClass <- setRefClass ("TestClass",
fields = list (text = "character"),
methods = list(
print = function () {cat(text)},
initialize = function(text = "", ...) callSuper(text = paste(text, ":", sep=""),...)
))
tt <- TestClass("hello world")
stopifnot(identical(tt$text, "hello world:"))
## now a subclass with another field & another layer of callSuper()
TestClass2 <- setRefClass("TestClass2",
contains = "TestClass",
fields = list( version = "integer"),
methods = list(
initialize = function(..., version = 0L)
callSuper(..., version = version+1L))
)
tt <- TestClass2("test", version = 1L)
stopifnot(identical(tt$text, "test:"), identical(tt$version, as.integer(2)))
tt <- TestClass2(version=3L) # default text
stopifnot(identical(tt$text, ":"), identical(tt$version, as.integer(4)))
## test some capabilities but read-only for .self
.changeAllFields <- function(replacement) {
fields <- names(.refClassDef@fieldClasses)
for(field in fields)
eval(substitute(.self$FIELD <- replacement$FIELD,
list(FIELD = field)))
}
mEditor$methods(change = .changeAllFields)
xx <- mEditor(data = xMat)
xx$edit(2, 2, 0)
yy <- mEditor(data = xMat+1)
yy$change(xx)
stopifnot(identical(yy$data, xx$data), identical(yy$edits, xx$edits))
## but don't allow assigment
if(methods:::.hasCodeTools())
stopifnot(is(tryCatch(yy$.self$data <- xMat, error = function(e)e), "error"))
## the locked binding of refGeneratorSlot class should prevent modifying
## methods, locking fields or setting accessor methods
## Nothing special about refGeneratorSlot in this test -- the point is just
## to use a standard reference class known to be defined in a package
evr <- getRefClass("refGeneratorSlot") # in methods
stopifnot(is(tryCatch(evr$methods(foo = function()"..."), error = function(e)e), "error"),
is(tryCatch(evr$lock("def"), error = function(e)e), "error"),
is(tryCatch(evr$accessors("def"), error = function(e)e), "error"))
##getRefClass() method and function should work with either
## a class name or a class representation (bug report 14600)
tg <- setRefClass("tg", fields = "a")
t1 <- tg(a=1)
tgg <- t1$getRefClass()
tggg <- getRefClass("tg")
stopifnot(identical(tgg$def, tggg$def),
identical(tg$def, tgg$def))
## this used to fail in initFieldArgs() from partial matching "self"
selfClass <- setRefClass("selfClass",
fields=list(
self="character", super="character", sub="character"
)
)
stopifnot(identical(selfClass(self="B", super="A", sub="C")$self, "B"))