diff --git a/R/BooleDS.R b/R/BooleDS.R index 7081e32a..ee967f7f 100644 --- a/R/BooleDS.R +++ b/R/BooleDS.R @@ -53,9 +53,13 @@ thr <- dsBase::listDisclosureSettingsDS() # V1<-.loadServersideObject(V1.name) -# V2 may be either a server-side object name or a literal scalar (e.g. "3", "-1") +# V2 may be either a server-side object name or a literal scalar (e.g. "3", "-1", "TRUE", "NA") V2.numeric <- suppressWarnings(as.numeric(V2.name)) -if(!is.na(V2.numeric)){ +if(V2.name %in% c("TRUE", "FALSE")){ + V2 <- as.logical(V2.name) +}else if(V2.name == "NA"){ + V2 <- NA +}else if(!is.na(V2.numeric)){ V2 <- V2.numeric }else{ V2 <- .loadServersideObject(V2.name) diff --git a/R/asListDS.R b/R/asListDS.R index f912594e..b6ffb829 100644 --- a/R/asListDS.R +++ b/R/asListDS.R @@ -1,32 +1,21 @@ -#' @title asListDS a serverside aggregate function called by ds.asList +#' @title asListDS a serverside assign function called by ds.asList #' @description Coerces an R object into a list -#' @details Unlike most other class coercing functions this is -#' an aggregate function rather than an assign function. This -#' is because the \code{datashield.assign} function in the data repository deals specially with -#' a created object (newobj) if it is of class list. Reconfiguring the -#' function as an aggregate function works around this problem. -#' This aggregate function is based on the native R function \code{as.list} +#' @details This assign function is based on the native R function \code{as.list} #' and so additional information can be found in the help for \code{as.list} #' @param x.name the name of the input object to be coerced to class #' data.matrix. Must be specified in inverted commas. But this argument is #' usually specified directly by argument of the clientside function #' \code{ds.asList} -#' @param newobj is the object hard assigned '<<-' to be the output of the -#' function written to the serverside -#' @return the object specified by the argument (or its default name -#' .mat) which is written to the serverside. -#' In addition, two validity messages are returned. The first confirms an output -#' object has been created, the second states its class. The way that \code{as.list} -#' coerces objects to list depends on the class of the object, but in general -#' the class of the output object should usually be 'list' +#' @return the input object coerced to a list, which \code{ds.asList} writes to the +#' serverside. The way that \code{as.list} coerces objects to list depends on the class +#' of the object #' @author Amadou Gaye, Paul Burton for DataSHIELD Development Team #' @author Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands #' @export -asListDS <- function (x.name, newobj){ +asListDS <- function (x.name){ x <- .loadServersideObject(x.name) - result <- as.list(x) - assign(newobj, result, envir = parent.frame()) + as.list(x) } -# AGGREGATE FUNCTION +# ASSIGN FUNCTION # asListDS diff --git a/R/aucDS.R b/R/aucDS.R index 6afda830..7229212c 100644 --- a/R/aucDS.R +++ b/R/aucDS.R @@ -8,16 +8,13 @@ #' the complete cases that are used in the regression model. #' @return returns the AUC and its standard error #' @author Demetris Avraam for DataSHIELD Development Team +#' @author Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands #' @export #' aucDS <- function(pred=pred, y=y){ - if(is.character(pred)){ - pred <- eval(parse(text = pred), envir = parent.frame()) - } - if(is.character(y)){ - y <- eval(parse(text = y), envir = parent.frame()) - } + pred <- .loadServersideObject(pred) + y <- .loadServersideObject(y) y <- as.numeric(as.character(y)) diff --git a/R/bp_standardsDS.R b/R/bp_standardsDS.R index f90e683e..8d6baec7 100644 --- a/R/bp_standardsDS.R +++ b/R/bp_standardsDS.R @@ -20,23 +20,16 @@ #' @note The z-scores of height based on CDC growth charts are calculated #' by the sds function from the childsds R package. #' @author Demetris Avraam for DataSHIELD Development Team +#' @author Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands #' @import childsds #' @export #' bp_standardsDS <- function(sex=sex, age=age, height=height, bp=bp, systolic=systolic){ - if(is.character(sex)){ - sex <- eval(parse(text = sex), envir = parent.frame()) - } - if(is.character(age)){ - age <- eval(parse(text = age), envir = parent.frame()) - } - if(is.character(height)){ - height <- eval(parse(text = height), envir = parent.frame()) - } - if(is.character(bp)){ - bp <- eval(parse(text = bp), envir = parent.frame()) - } + sex <- .loadServersideObject(sex) + age <- .loadServersideObject(age) + height <- .loadServersideObject(height) + bp <- .loadServersideObject(bp) # convert height to a Z-score relative to age and sex based on CDC growth charts Zht <- sds(value=height, age=age, sex=sex, male="1", female="2", diff --git a/R/corDS.R b/R/corDS.R index 6133b4e0..9653900a 100644 --- a/R/corDS.R +++ b/R/corDS.R @@ -32,7 +32,7 @@ corDS <- function(x=NULL, y=NULL){ x.val <- .loadServersideObject(x) .checkClass(obj = x.val, obj_name = x, permitted_classes = c("numeric", "integer", "matrix", "data.frame")) - if (!is.null(y)){ + if (!is.null(y) && !any(class(x.val) %in% c("matrix", "data.frame"))){ y.val <- .loadServersideObject(y) .checkClass(obj = y.val, obj_name = y, permitted_classes = c("numeric", "integer", "matrix", "data.frame")) } diff --git a/R/covDS.R b/R/covDS.R index fe25eabd..84f8f66b 100644 --- a/R/covDS.R +++ b/R/covDS.R @@ -41,7 +41,7 @@ covDS <- function(x=NULL, y=NULL, use=NULL){ x.val <- .loadServersideObject(x) .checkClass(obj = x.val, obj_name = x, permitted_classes = c("numeric", "integer", "matrix", "data.frame")) - if (!is.null(y)){ + if (!is.null(y) && !any(class(x.val) %in% c("matrix", "data.frame"))){ y.val <- .loadServersideObject(y) .checkClass(obj = y.val, obj_name = y, permitted_classes = c("numeric", "integer", "matrix", "data.frame")) } diff --git a/R/dataFrameSortDS.R b/R/dataFrameSortDS.R index 9801d804..c51b33e0 100644 --- a/R/dataFrameSortDS.R +++ b/R/dataFrameSortDS.R @@ -30,8 +30,8 @@ #' R environment as a data.frame named according to the argument(or with #' default name 'dataframesort.newobj') if no name is specified #' @author Paul Burton, with critical error identification by -#' @author Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands #' Leire Abarrategui-Martinez, for DataSHIELD Development Team, 2/4/2020 +#' @author Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands #' @export #' dataFrameSortDS <- function(df.name=NULL,sort.key.name=NULL,sort.descending,sort.method){ diff --git a/R/dimDS.R b/R/dimDS.R index c27db5b5..6a1e9e1f 100644 --- a/R/dimDS.R +++ b/R/dimDS.R @@ -11,7 +11,6 @@ #' dimDS <- function(x){ x.val <- .loadServersideObject(x) - .checkClass(obj = x.val, obj_name = x, permitted_classes = c("data.frame", "matrix")) list(dim = dim(x.val), class = class(x.val)) } #AGGREGATE FUNCTION diff --git a/R/gamlssDS.R b/R/gamlssDS.R index 1789b5af..7f7ae0e0 100644 --- a/R/gamlssDS.R +++ b/R/gamlssDS.R @@ -83,8 +83,9 @@ gamlssDS <- function(formula=formula, sigma.formula=sigma.formula, nu.formula=nu thr <- dsBase::listDisclosureSettingsDS() nfilter.glm <- as.numeric(thr$nfilter.glm) - data <- .loadServersideObject(data) - .checkClass(obj = data, obj_name = "data", permitted_classes = c("data.frame", "matrix")) + data.name <- data + data <- .loadServersideObject(data.name) + .checkClass(obj = data, obj_name = data.name, permitted_classes = c("data.frame", "list")) family <- gsub("left_parenthesis", "(", family, fixed = TRUE) family <- gsub("right_parenthesis", ")", family, fixed = TRUE) diff --git a/R/glmDS1.R b/R/glmDS1.R index 568e3070..3fc2ab3c 100644 --- a/R/glmDS1.R +++ b/R/glmDS1.R @@ -43,7 +43,7 @@ nfilter.glm <- as.numeric(thr$nfilter.glm) dataTable <- NULL }else{ dataTable <- .loadServersideObject(data) - .checkClass(obj = dataTable, obj_name = data, permitted_classes = c("data.frame", "matrix")) + .checkClass(obj = dataTable, obj_name = data, permitted_classes = c("data.frame", "list")) } formulatext <- Reduce(paste, deparse(formula)) diff --git a/R/glmDS2.R b/R/glmDS2.R index 93e8edda..caff48f0 100644 --- a/R/glmDS2.R +++ b/R/glmDS2.R @@ -41,7 +41,7 @@ glmDS2 <- function (formula, family, beta.vect, offset, weights, dataName) { if(!is.null(dataName)){ dataDF <- .loadServersideObject(dataName) - .checkClass(obj = dataDF, obj_name = dataName, permitted_classes = c("data.frame", "matrix")) + .checkClass(obj = dataDF, obj_name = dataName, permitted_classes = c("data.frame", "list")) }else{ dataDF <- NULL } diff --git a/R/glmSLMADS1.R b/R/glmSLMADS1.R index 4cddefdb..5e594bf4 100644 --- a/R/glmSLMADS1.R +++ b/R/glmSLMADS1.R @@ -53,7 +53,7 @@ final.family.object<-eval(parse(text=family)) dataTable <- NULL }else{ dataTable <- .loadServersideObject(data) - .checkClass(obj = dataTable, obj_name = data, permitted_classes = c("data.frame", "matrix")) + .checkClass(obj = dataTable, obj_name = data, permitted_classes = c("data.frame", "list")) } diff --git a/R/glmSLMADS2.R b/R/glmSLMADS2.R index 58d1b374..20d698ae 100644 --- a/R/glmSLMADS2.R +++ b/R/glmSLMADS2.R @@ -54,7 +54,7 @@ errorMessage2<-"No errors" if(!is.null(dataName)){ dataDF <- .loadServersideObject(dataName) - .checkClass(obj = dataDF, obj_name = dataName, permitted_classes = c("data.frame", "matrix")) + .checkClass(obj = dataDF, obj_name = dataName, permitted_classes = c("data.frame", "list")) }else{ dataDF<-NULL } diff --git a/R/glmerSLMADS.assign.R b/R/glmerSLMADS.assign.R index 2105fea7..36c7159c 100644 --- a/R/glmerSLMADS.assign.R +++ b/R/glmerSLMADS.assign.R @@ -48,7 +48,7 @@ glmerSLMADS.assign <- function(formula, offset, weights, dataName, family, if(!is.null(dataName)){ dataDF <- .loadServersideObject(dataName) - .checkClass(obj = dataDF, obj_name = dataName, permitted_classes = c("data.frame", "matrix")) + .checkClass(obj = dataDF, obj_name = dataName, permitted_classes = c("data.frame", "list")) }else{ dataDF <- NULL } diff --git a/R/glmerSLMADS2.R b/R/glmerSLMADS2.R index d4b15f8c..480db660 100644 --- a/R/glmerSLMADS2.R +++ b/R/glmerSLMADS2.R @@ -55,7 +55,7 @@ glmerSLMADS2 <- function(formula, offset, weights, dataName, family, if(!is.null(dataName)){ dataDF <- .loadServersideObject(dataName) - .checkClass(obj = dataDF, obj_name = dataName, permitted_classes = c("data.frame", "matrix")) + .checkClass(obj = dataDF, obj_name = dataName, permitted_classes = c("data.frame", "list")) }else{ dataDF <- NULL } diff --git a/R/igb_standardsDS.R b/R/igb_standardsDS.R index e30247cb..13a19afc 100644 --- a/R/igb_standardsDS.R +++ b/R/igb_standardsDS.R @@ -17,6 +17,7 @@ #' standard is used. #' @return assigns the converted measurement as a new object on the server-side #' @author Demetris Avraam for DataSHIELD Development Team +#' @author Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands #' @import dplyr #' @importFrom gamlss.dist qST3 pST3 #' @export @@ -228,15 +229,11 @@ igb_standardsDS <- function(gagebrth=gagebrth, z=z, p=p, val=val, var=var, sex=s # Start the analysis - if(is.character(gagebrth)){ - gagebrth <- eval(parse(text = gagebrth), envir = parent.frame()) - } - if(is.character(val)){ - val <- eval(parse(text = val), envir = parent.frame()) - } - if(is.character(sex)){ - sex <- eval(parse(text = sex), envir = parent.frame()) + gagebrth <- .loadServersideObject(gagebrth) + if(!is.null(val)){ + val <- .loadServersideObject(val) } + sex <- .loadServersideObject(sex) x.out <- rep(NA, t=length(sex)) idx.males <- which(sex=="Male") diff --git a/R/lengthDS.R b/R/lengthDS.R index 0441e67c..70165d99 100644 --- a/R/lengthDS.R +++ b/R/lengthDS.R @@ -12,7 +12,6 @@ #' lengthDS <- function(x){ x.val <- .loadServersideObject(x) - .checkClass(obj = x.val, obj_name = x, permitted_classes = c("character", "factor", "integer", "logical", "numeric", "list", "data.frame", "array", "matrix")) list(length = length(x.val), class = class(x.val)) } #AGGREGATE FUNCTION diff --git a/R/levelsDS.R b/R/levelsDS.R index 33c33ec6..d253bb99 100644 --- a/R/levelsDS.R +++ b/R/levelsDS.R @@ -2,7 +2,7 @@ #' @title Returns the levels of a factor vector #' @description This function is similar to R function \code{levels}. #' @details The function returns the levels of the input vector or list. -#' @param x a factor vector +#' @param x a character string, the name of a factor vector #' @return a list with one element: \code{Levels} (the factor levels present #' in the vector) #' @author Alex Westerberg, for DataSHIELD Development Team @@ -11,12 +11,12 @@ #' levelsDS <- function(x){ - x.val <- .loadServersideObject(x) - .checkClass(obj = x.val, obj_name = x, permitted_classes = "factor") - # Check Permissive Privacy Control Level. dsBase::checkPermissivePrivacyControlLevel(c('permissive', 'banana', 'carrot')) + x.val <- .loadServersideObject(x) + .checkClass(obj = x.val, obj_name = x, permitted_classes = "factor") + ################################################################## #MODULE 1: CAPTURE THE nfilter SETTINGS # thr <- dsBase::listDisclosureSettingsDS() # diff --git a/R/lmerSLMADS.assign.R b/R/lmerSLMADS.assign.R index 0aabe6b6..7731c552 100644 --- a/R/lmerSLMADS.assign.R +++ b/R/lmerSLMADS.assign.R @@ -39,7 +39,7 @@ lmerSLMADS.assign <- function(formula, offset, weights, dataName, REML = TRUE, if(!is.null(dataName)){ dataDF <- .loadServersideObject(dataName) - .checkClass(obj = dataDF, obj_name = dataName, permitted_classes = c("data.frame", "matrix")) + .checkClass(obj = dataDF, obj_name = dataName, permitted_classes = c("data.frame", "list")) }else{ dataDF <- NULL } @@ -126,13 +126,9 @@ lmerSLMADS.assign <- function(formula, offset, weights, dataName, REML = TRUE, ################################################################## #sort out offset and weights - # - # offset.to.use/weights.to.use are needed in two different environments: - # stats::glm() below resolves them via environment(formula2use), which is - # this function's *caller* (matching where the other formula variables are - # assigned, see the model.variables loop above); lme4::lmer() further down - # resolves them relative to its own call frame, i.e. this function's *own* - # frame. Assign to both so each modelling call finds them. + # stats::glm() finds offset.to.use/weights.to.use in the caller's frame + # (environment(formula2use)) but lme4::lmer() in this function's own frame, + # so they are assigned in both. if(is.null(offset)) { varname.offset<-NULL @@ -141,14 +137,14 @@ lmerSLMADS.assign <- function(formula, offset, weights, dataName, REML = TRUE, }else{ varname.offset <- paste0(offset) } - + if(!(is.null(offset))) { cbindtext.offset <- paste0("cbind(", offset,")") offset.to.use <- eval(parse(text=cbindtext.offset), envir = parent.frame()) assign("offset.to.use", offset.to.use, envir = parent.frame()) } - + if(is.null(weights)) { varname.weights<-NULL @@ -157,8 +153,8 @@ lmerSLMADS.assign <- function(formula, offset, weights, dataName, REML = TRUE, }else{ varname.weights <- paste0(weights) } - - + + if(!(is.null(weights))) { cbindtext.weights <- paste0("cbind(", weights,")") @@ -367,7 +363,7 @@ if(!is.null(optimizer)&&optimizer!="nloptwrap") mg <- lme4::lmer(formula2use, offset=offset.to.use, weights=weights.to.use, data=dataDF, REML = REML, verbose = verbose, control = control.obj) outlist <- mg - + } #tidy up in parent.frame() eval(quote(rm(offset.to.use)), envir = parent.frame()) diff --git a/R/lmerSLMADS2.R b/R/lmerSLMADS2.R index 68bcb465..7a1142d1 100644 --- a/R/lmerSLMADS2.R +++ b/R/lmerSLMADS2.R @@ -48,7 +48,7 @@ lmerSLMADS2 <- function(formula, offset, weights, dataName, REML = TRUE, if(!is.null(dataName)){ dataDF <- .loadServersideObject(dataName) - .checkClass(obj = dataDF, obj_name = dataName, permitted_classes = c("data.frame", "matrix")) + .checkClass(obj = dataDF, obj_name = dataName, permitted_classes = c("data.frame", "list")) }else{ dataDF <- NULL } @@ -135,13 +135,9 @@ lmerSLMADS2 <- function(formula, offset, weights, dataName, REML = TRUE, ################################################################## #sort out offset and weights - # - # offset.to.use/weights.to.use are needed in two different environments: - # stats::glm() below resolves them via environment(formula2use), which is - # this function's *caller* (matching where the other formula variables are - # assigned, see the model.variables loop above); lme4::lmer() further down - # resolves them relative to its own call frame, i.e. this function's *own* - # frame. Assign to both so each modelling call finds them. + # stats::glm() finds offset.to.use/weights.to.use in the caller's frame + # (environment(formula2use)) but lme4::lmer() in this function's own frame, + # so they are assigned in both. if(is.null(offset)) { varname.offset<-NULL @@ -150,14 +146,14 @@ lmerSLMADS2 <- function(formula, offset, weights, dataName, REML = TRUE, }else{ varname.offset <- paste0(offset) } - + if(!(is.null(offset))) { cbindtext.offset <- paste0("cbind(", offset,")") offset.to.use <- eval(parse(text=cbindtext.offset), envir = parent.frame()) assign("offset.to.use", offset.to.use, envir = parent.frame()) } - + if(is.null(weights)) { varname.weights<-NULL @@ -166,8 +162,8 @@ lmerSLMADS2 <- function(formula, offset, weights, dataName, REML = TRUE, }else{ varname.weights <- paste0(weights) } - - + + if(!(is.null(weights))) { cbindtext.weights <- paste0("cbind(", weights,")") diff --git a/R/mdPatternDS.R b/R/mdPatternDS.R index 0f817034..66dbaaba 100644 --- a/R/mdPatternDS.R +++ b/R/mdPatternDS.R @@ -36,7 +36,9 @@ #' \item{pattern}{The missing data pattern matrix with disclosure control applied} #' \item{valid}{Logical indicating if all patterns meet disclosure requirements} #' \item{message}{A message describing the validity status} +#' \item{class}{The class of the input object, for client-side consistency checking} #' @author Xavier EscribĂ  montagut for DataSHIELD Development Team +#' @author Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands #' @import mice #' @export #' @@ -48,24 +50,8 @@ mdPatternDS <- function(x){ nfilter.tab <- as.numeric(thr$nfilter.tab) ############################################################# - # Parse the input data name with error handling - x.val <- tryCatch( - { - eval(parse(text=x), envir = parent.frame()) - }, - error = function(e) { - stop(paste0("Object '", x, "' does not exist on the server"), call. = FALSE) - } - ) - - # Check object class - typ <- class(x.val) - - # Check that input is a data frame or matrix - if(!("data.frame" %in% typ || "matrix" %in% typ)){ - stop(paste0("The input object must be of type 'data.frame' or 'matrix'. Current type: ", - paste(typ, collapse = ", ")), call. = FALSE) - } + x.val <- .loadServersideObject(x) + .checkClass(obj = x.val, obj_name = x, permitted_classes = c("data.frame", "matrix")) # Use x.val for further processing x <- x.val @@ -113,7 +99,8 @@ mdPatternDS <- function(x){ message = ifelse(validity == "valid", "Valid: all pattern counts meet disclosure requirements", paste0("Invalid: some pattern counts below threshold (", - nfilter.tab, ") have been suppressed")) + nfilter.tab, ") have been suppressed")), + class = class(x.val) )) } diff --git a/R/meanDS.R b/R/meanDS.R index 576ec1b6..49260dba 100644 --- a/R/meanDS.R +++ b/R/meanDS.R @@ -23,7 +23,7 @@ meanDS <- function(x){ ############################################################# xvect <- .loadServersideObject(x) - .checkClass(obj = xvect, obj_name = x, permitted_classes = c("numeric", "integer")) + .checkClass(obj = xvect, obj_name = x, permitted_classes = c("numeric", "integer", "logical")) out.mean <- mean(xvect, na.rm=TRUE) out.numNa <- length(which(is.na(xvect))) diff --git a/R/meanSdGpDS.R b/R/meanSdGpDS.R index eecf0dde..30b9e178 100644 --- a/R/meanSdGpDS.R +++ b/R/meanSdGpDS.R @@ -27,7 +27,7 @@ meanSdGpDS <- function (x, index){ X <- .loadServersideObject(x) .checkClass(obj = X, obj_name = x, permitted_classes = c("numeric", "integer")) INDEX <- .loadServersideObject(index) - .checkClass(obj = INDEX, obj_name = index, permitted_classes = c("factor", "character", "integer")) + .checkClass(obj = INDEX, obj_name = index, permitted_classes = c("factor", "character", "integer", "numeric")) x.class <- class(X) index.class <- class(INDEX) diff --git a/R/miceDS.R b/R/miceDS.R index 7bf0bccd..c7d68ec8 100644 --- a/R/miceDS.R +++ b/R/miceDS.R @@ -58,8 +58,9 @@ miceDS <- function(data=data, m=m, maxit=maxit, method=method, post=post, seed=s seed <- getOption("datashield.seed") } - data <- .loadServersideObject(data) - .checkClass(obj = data, obj_name = "data", permitted_classes = c("data.frame", "matrix")) + data.name <- data + data <- .loadServersideObject(data.name) + .checkClass(obj = data, obj_name = data.name, permitted_classes = c("data.frame", "matrix")) if(!is.null(method)){ method <- unlist(stringr::str_split(method, pattern=",")) diff --git a/R/seqDS.R b/R/seqDS.R index 56113e4a..d6ff81bc 100644 --- a/R/seqDS.R +++ b/R/seqDS.R @@ -66,8 +66,8 @@ nfilter.subset<-as.numeric(thr$nfilter.subset) # ######################################################################### - FROM.eval <- tryCatch(.loadServersideObject(FROM.value.char), error = function(e) as.numeric(FROM.value.char)) - if(is.character(FROM.value.char)&&is.numeric(FROM.eval)){ + FROM.eval <- suppressWarnings(as.numeric(FROM.value.char)) + if(is.character(FROM.value.char)&&!anyNA(FROM.eval)){ FROM<-FROM.eval }else{ studysideMessage<-"ERROR: FROM.value.char must be specified as a real number in inverted commas eg '-3.74' or '0'" @@ -76,8 +76,8 @@ nfilter.subset<-as.numeric(thr$nfilter.subset) # if(!is.null(TO.value.char)) { - TO.eval <- tryCatch(.loadServersideObject(TO.value.char), error = function(e) as.numeric(TO.value.char)) - if(is.character(TO.value.char)&&is.numeric(TO.eval)) + TO.eval <- suppressWarnings(as.numeric(TO.value.char)) + if(is.character(TO.value.char)&&!anyNA(TO.eval)) { TO<-TO.eval }else{ @@ -91,8 +91,8 @@ nfilter.subset<-as.numeric(thr$nfilter.subset) # TO<-NULL } - BY.eval <- tryCatch(.loadServersideObject(BY.value.char), error = function(e) as.numeric(BY.value.char)) - if(is.character(BY.value.char)&&is.numeric(BY.eval)){ + BY.eval <- suppressWarnings(as.numeric(BY.value.char)) + if(is.character(BY.value.char)&&!anyNA(BY.eval)){ BY<-BY.eval }else{ studysideMessage<-"ERROR: BY.value.char must be specified as a real number in inverted commas eg '-3.74' or '0'" @@ -100,8 +100,8 @@ nfilter.subset<-as.numeric(thr$nfilter.subset) # } if(!is.null(LENGTH.OUT.value.char)){ - LENGTH.OUT.eval <- tryCatch(.loadServersideObject(LENGTH.OUT.value.char), error = function(e) as.numeric(LENGTH.OUT.value.char)) - if(is.character(LENGTH.OUT.value.char)&&is.numeric(LENGTH.OUT.eval)){ + LENGTH.OUT.eval <- suppressWarnings(as.numeric(LENGTH.OUT.value.char)) + if(is.character(LENGTH.OUT.value.char)&&!anyNA(LENGTH.OUT.eval)){ LENGTH.OUT<-LENGTH.OUT.eval }else{ studysideMessage<-"ERROR: If LENGTH.OUT.value.char is non-NULL, it must specify a positive integer in inverted commas eg '14'" diff --git a/R/utils.R b/R/utils.R index b96d8735..30e2fa30 100644 --- a/R/utils.R +++ b/R/utils.R @@ -7,6 +7,7 @@ #' #' @param x A character string naming the object, optionally with "$column" syntax. #' @return The retrieved R object, or the specified column if `$` syntax is used. +#' @author Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands #' @noRd .loadServersideObject <- function(x) { if (!is.character(x) || length(x) != 1) { @@ -27,9 +28,13 @@ obj <- tryCatch( get(obj_name, envir = env), - error = function(e) stop("The server-side object '", x, "' does not exist") + error = function(e) stop("The server-side object '", x, "' does not exist", call. = FALSE) ) + if (is.null(obj)) { + stop("The server-side object '", x, "' is NULL", call. = FALSE) + } + if (hasColumn) { obj <- obj[[col_name]] if (is.null(obj)) { diff --git a/R/varDS.R b/R/varDS.R index eb9fcf0e..eef07147 100644 --- a/R/varDS.R +++ b/R/varDS.R @@ -24,7 +24,7 @@ varDS <- function(x){ ############################################################# xvect <- .loadServersideObject(x) - .checkClass(obj = xvect, obj_name = x, permitted_classes = c("numeric", "integer")) + .checkClass(obj = xvect, obj_name = x, permitted_classes = c("numeric", "integer", "logical")) out.sum <- sum(xvect, na.rm=TRUE) out.sumSquares <- sum(xvect^2, na.rm=TRUE) diff --git a/man/asListDS.Rd b/man/asListDS.Rd index a2fc5a1c..407c75df 100644 --- a/man/asListDS.Rd +++ b/man/asListDS.Rd @@ -2,37 +2,26 @@ % Please edit documentation in R/asListDS.R \name{asListDS} \alias{asListDS} -\title{asListDS a serverside aggregate function called by ds.asList} +\title{asListDS a serverside assign function called by ds.asList} \usage{ -asListDS(x.name, newobj) +asListDS(x.name) } \arguments{ \item{x.name}{the name of the input object to be coerced to class data.matrix. Must be specified in inverted commas. But this argument is usually specified directly by argument of the clientside function \code{ds.asList}} - -\item{newobj}{is the object hard assigned '<<-' to be the output of the -function written to the serverside} } \value{ -the object specified by the argument (or its default name -.mat) which is written to the serverside. -In addition, two validity messages are returned. The first confirms an output -object has been created, the second states its class. The way that \code{as.list} -coerces objects to list depends on the class of the object, but in general -the class of the output object should usually be 'list' +the input object coerced to a list, which \code{ds.asList} writes to the +serverside. The way that \code{as.list} coerces objects to list depends on the class +of the object } \description{ Coerces an R object into a list } \details{ -Unlike most other class coercing functions this is -an aggregate function rather than an assign function. This -is because the \code{datashield.assign} function in the data repository deals specially with -a created object (newobj) if it is of class list. Reconfiguring the -function as an aggregate function works around this problem. -This aggregate function is based on the native R function \code{as.list} +This assign function is based on the native R function \code{as.list} and so additional information can be found in the help for \code{as.list} } \author{ diff --git a/man/aucDS.Rd b/man/aucDS.Rd index 567f2e38..b1d4cb30 100644 --- a/man/aucDS.Rd +++ b/man/aucDS.Rd @@ -24,4 +24,6 @@ The AUC determines the discriminative ability of a model. } \author{ Demetris Avraam for DataSHIELD Development Team + +Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands } diff --git a/man/bp_standardsDS.Rd b/man/bp_standardsDS.Rd index 1b26b9f5..cfafb344 100644 --- a/man/bp_standardsDS.Rd +++ b/man/bp_standardsDS.Rd @@ -45,4 +45,6 @@ by the sds function from the childsds R package. } \author{ Demetris Avraam for DataSHIELD Development Team + +Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands } diff --git a/man/dataFrameSortDS.Rd b/man/dataFrameSortDS.Rd index 9aa9ea5f..a3dfd8dd 100644 --- a/man/dataFrameSortDS.Rd +++ b/man/dataFrameSortDS.Rd @@ -53,7 +53,7 @@ For more details see help for the clientside function: [ds.dataFrameShort] } \author{ Paul Burton, with critical error identification by +Leire Abarrategui-Martinez, for DataSHIELD Development Team, 2/4/2020 Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands -Leire Abarrategui-Martinez, for DataSHIELD Development Team, 2/4/2020 } diff --git a/man/igb_standardsDS.Rd b/man/igb_standardsDS.Rd index 99fd30e0..4eed445d 100644 --- a/man/igb_standardsDS.Rd +++ b/man/igb_standardsDS.Rd @@ -46,4 +46,6 @@ standard is used. } \author{ Demetris Avraam for DataSHIELD Development Team + +Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands } diff --git a/man/levelsDS.Rd b/man/levelsDS.Rd index 4002c73c..778f8a59 100644 --- a/man/levelsDS.Rd +++ b/man/levelsDS.Rd @@ -7,7 +7,7 @@ levelsDS(x) } \arguments{ -\item{x}{a factor vector} +\item{x}{a character string, the name of a factor vector} } \value{ a list with one element: \code{Levels} (the factor levels present diff --git a/man/mdPatternDS.Rd b/man/mdPatternDS.Rd index 1084565e..6295c024 100644 --- a/man/mdPatternDS.Rd +++ b/man/mdPatternDS.Rd @@ -15,6 +15,7 @@ A list containing: \item{pattern}{The missing data pattern matrix with disclosure control applied} \item{valid}{Logical indicating if all patterns meet disclosure requirements} \item{message}{A message describing the validity status} +\item{class}{The class of the input object, for client-side consistency checking} } \description{ This function is a serverside aggregate function that computes the @@ -51,4 +52,6 @@ suppressed in some studies. } \author{ Xavier EscribĂ  montagut for DataSHIELD Development Team + +Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands } diff --git a/tests/testthat/test-arg-asLogicalDS.R b/tests/testthat/test-arg-asLogicalDS.R index d778e010..9d16e8a5 100644 --- a/tests/testthat/test-arg-asLogicalDS.R +++ b/tests/testthat/test-arg-asLogicalDS.R @@ -28,7 +28,7 @@ test_that("simple asLogicalDS non-input", { test_that("simple asLogicalDS NULL", { input <- NULL - expect_error(asLogicalDS("input"), "The server-side object must be of type numeric, integer, character or matrix. 'input' is type NULL.", fixed = TRUE) + expect_error(asLogicalDS("input"), "The server-side object 'input' is NULL", fixed = TRUE) }) # context("asLogicalDS::arg::input NA") diff --git a/tests/testthat/test-arg-mdPatternDS.R b/tests/testthat/test-arg-mdPatternDS.R index 989deb76..e2ce112d 100644 --- a/tests/testthat/test-arg-mdPatternDS.R +++ b/tests/testthat/test-arg-mdPatternDS.R @@ -24,14 +24,22 @@ set.standard.disclosure.settings() test_that("mdPatternDS x NULL", { x <- NULL - expect_error(mdPatternDS(x), "The input object must be of type 'data.frame' or 'matrix'. Current type: NULL") + expect_error(mdPatternDS(x), "The input must be a single character string") }) # context("mdPatternDS::arg::x not valid variable") test_that("mdPatternDS x not variable", { x <- "not a variable" - expect_error(mdPatternDS(x), "Object 'not a variable' does not exist on the server") + expect_error(mdPatternDS(x), "does not exist") +}) + +# context("mdPatternDS::arg::x wrong type") +test_that("mdPatternDS x wrong type", { + x_val <- c(1.0, 2.0, 3.0) + x <- "x_val" + + expect_error(mdPatternDS(x), "must be of type data.frame or matrix") }) # diff --git a/tests/testthat/test-disc-mdPatternDS.R b/tests/testthat/test-disc-mdPatternDS.R index d10b004a..6f6a7c01 100644 --- a/tests/testthat/test-disc-mdPatternDS.R +++ b/tests/testthat/test-disc-mdPatternDS.R @@ -27,7 +27,7 @@ test_that("mdPatternDS: sample incomplete data.frame", { res <- mdPatternDS(x) - expect_length(res, 3) + expect_length(res, 4) expect_length(class(res), 1) expect_true(all(class(res) %in% c("list"))) expect_length(class(res$pattern), 2) diff --git a/tests/testthat/test-smk-BooleDS.R b/tests/testthat/test-smk-BooleDS.R index 685dc0fa..e517e6f1 100644 --- a/tests/testthat/test-smk-BooleDS.R +++ b/tests/testthat/test-smk-BooleDS.R @@ -299,6 +299,22 @@ test_that("BooleDS errors when V2 object does not exist", { ) }) +test_that("BooleDS accepts a literal TRUE for V2", { + input <- data.frame(flag = c(TRUE, FALSE, TRUE, FALSE)) + + res <- BooleDS("input$flag", "TRUE", 1, "NA", TRUE) + + expect_equal(res, c(1, 0, 1, 0)) +}) + +test_that("BooleDS accepts a literal FALSE for V2", { + input <- data.frame(flag = c(TRUE, FALSE, TRUE, FALSE)) + + res <- BooleDS("input$flag", "FALSE", 1, "NA", TRUE) + + expect_equal(res, c(0, 1, 0, 1)) +}) + # # Done # diff --git a/tests/testthat/test-smk-asListDS.R b/tests/testthat/test-smk-asListDS.R index 1ac8ac68..534017c6 100644 --- a/tests/testthat/test-smk-asListDS.R +++ b/tests/testthat/test-smk-asListDS.R @@ -19,19 +19,59 @@ # Tests # -# context("asListDS::smk::simple") -test_that("simple asListDS", { - input <- list(v1 = c(1, 2, 3), v2 = c(4, 5, 6)) - newobj.name <- 'newobj' +# context("asListDS::smk::list") +test_that("asListDS returns a list unchanged", { + input <- list(v1 = c(1, 2, 3), v2 = c(4, 5, 6)) - expect_false(exists("newobj")) + res <- asListDS("input") - res <- asListDS("input", newobj.name) + expect_equal(class(res), "list") + expect_identical(res, input) +}) + +# context("asListDS::smk::numeric") +test_that("asListDS converts a numeric vector to a list of its elements", { + input <- c(1.5, 2.5, 3.5) + + res <- asListDS("input") - expect_true(exists("newobj")) - expect_equal(class(newobj), "list") - expect_length(newobj, 2) + expect_equal(class(res), "list") + expect_length(res, 3) + expect_equal(unlist(res), input) }) + +# context("asListDS::smk::data.frame") +test_that("asListDS converts a data.frame to a named list of its columns", { + input <- data.frame(a = c(1, 2, 3), b = c("x", "y", "z")) + + res <- asListDS("input") + + expect_equal(class(res), "list") + expect_named(res, c("a", "b")) + expect_equal(res$a, input$a) + expect_equal(res$b, input$b) +}) + +# context("asListDS::smk::column") +test_that("asListDS converts a data.frame column given with $", { + input <- data.frame(a = c(1, 2, 3)) + + res <- asListDS("input$a") + + expect_equal(class(res), "list") + expect_equal(unlist(res), c(1, 2, 3)) +}) + +test_that("asListDS throws error when object does not exist", { + expect_error(asListDS("nonexistent_object"), regexp = "does not exist") +}) + +test_that("asListDS throws error when object is NULL", { + input <- NULL + + expect_error(asListDS("input"), "The server-side object 'input' is NULL", fixed = TRUE) +}) + # # Done # diff --git a/tests/testthat/test-smk-aucDS.R b/tests/testthat/test-smk-aucDS.R index bcbbada1..c33d2eee 100644 --- a/tests/testthat/test-smk-aucDS.R +++ b/tests/testthat/test-smk-aucDS.R @@ -25,7 +25,7 @@ test_that("aucDS", { model <- glm(formula = D$DIS_DIAB~D$GENDER+D$PM_BMI_CONTINUOUS, family = 'binomial') pred <- predict(object = model, type = "link") - res <- aucDS(pred = pred, y = model$y) + res <- aucDS(pred = "pred", y = "model$y") expect_equal(class(res), "list") expect_length(res, 2) @@ -36,6 +36,18 @@ test_that("aucDS", { }) +test_that("aucDS throws error when pred does not exist", { + y <- c(0, 1, 0, 1) + + expect_error(aucDS(pred = "nonexistent_object", y = "y"), regexp = "does not exist") +}) + +test_that("aucDS throws error when y does not exist", { + pred <- c(0.1, 0.8, 0.3, 0.9) + + expect_error(aucDS(pred = "pred", y = "nonexistent_object"), regexp = "does not exist") +}) + # # Done # diff --git a/tests/testthat/test-smk-bp_standardsDS.R b/tests/testthat/test-smk-bp_standardsDS.R index fe8b9bc1..06fa5519 100644 --- a/tests/testthat/test-smk-bp_standardsDS.R +++ b/tests/testthat/test-smk-bp_standardsDS.R @@ -28,7 +28,7 @@ test_that("systolic bp_standardsDS", { bp <- c(118.54, 87.62, 72.13, 91.18, 88.47, 73.28, 100.27, 86.84, 103.51, 115.28) systolic <- TRUE - res <- bp_standardsDS(sex=sex, age=age, height=height, bp=bp, systolic=systolic) + res <- bp_standardsDS(sex="sex", age="age", height="height", bp="bp", systolic=systolic) expect_equal(class(res), "list") expect_length(res, 2) @@ -56,7 +56,7 @@ test_that("diastolic bp_standardsDS", { bp <- c(118.54, 87.62, 72.13, 91.18, 88.47, 73.28, 100.27, 86.84, 103.51, 115.28) systolic <- FALSE - res <- bp_standardsDS(sex=sex, age=age, height=height, bp=bp, systolic=systolic) + res <- bp_standardsDS(sex="sex", age="age", height="height", bp="bp", systolic=systolic) expect_equal(class(res), "list") expect_length(res, 2) @@ -76,6 +76,14 @@ test_that("diastolic bp_standardsDS", { }) +test_that("bp_standardsDS throws error when an input does not exist", { + sex <- c(2, 1) + age <- c(5.26, 7.73) + height <- c(113.57, 92.30) + + expect_error(bp_standardsDS(sex="sex", age="age", height="height", bp="nonexistent_object", systolic=TRUE), regexp = "does not exist") +}) + # # Done # diff --git a/tests/testthat/test-smk-classDS.R b/tests/testthat/test-smk-classDS.R index a3eb79d3..9d6104d8 100644 --- a/tests/testthat/test-smk-classDS.R +++ b/tests/testthat/test-smk-classDS.R @@ -223,11 +223,7 @@ test_that("special classDS, NA", { test_that("special classDS, NULL", { input <- NULL - res <- classDS("input") - - expect_equal(class(res), "character") - expect_length(res, 1) - expect_equal(res, "NULL") + expect_error(classDS("input"), "The server-side object 'input' is NULL", fixed = TRUE) }) test_that("classDS throws error when object does not exist", { diff --git a/tests/testthat/test-smk-corDS.R b/tests/testthat/test-smk-corDS.R index e86b6e2c..1c4af5e5 100644 --- a/tests/testthat/test-smk-corDS.R +++ b/tests/testthat/test-smk-corDS.R @@ -728,6 +728,17 @@ test_that("corDS throws error when object is of invalid type", { expect_error(corDS("bad_input", "y"), regexp = "must be of type") }) +test_that("corDS ignores y when x is a data.frame", { + input <- data.frame(v1 = c(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0), + v2 = c(2.0, 1.0, 4.0, 3.0, 6.0, 5.0, 8.0, 7.0, 10.0, 9.0)) + other <- c(10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0) + + res.x <- corDS("input", NULL) + res.xy <- corDS("input", "other") + + expect_equal(res.xy, res.x) +}) + # # Done # diff --git a/tests/testthat/test-smk-covDS.R b/tests/testthat/test-smk-covDS.R index 24352ac7..d899c7c2 100644 --- a/tests/testthat/test-smk-covDS.R +++ b/tests/testthat/test-smk-covDS.R @@ -242,6 +242,17 @@ test_that("covDS throws error when object is of invalid type", { expect_error(covDS("bad_input", "y", "pairwise.complete"), regexp = "must be of type") }) +test_that("covDS ignores y when x is a data.frame", { + input <- data.frame(v1 = c(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0), + v2 = c(2.0, 1.0, 4.0, 3.0, 6.0, 5.0, 8.0, 7.0, 10.0, 9.0)) + other <- c(10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0) + + res.x <- covDS("input", NULL, "pairwise.complete") + res.xy <- covDS("input", "other", "pairwise.complete") + + expect_equal(res.xy, res.x) +}) + # # Done # diff --git a/tests/testthat/test-smk-dimDS.R b/tests/testthat/test-smk-dimDS.R index c45d07fb..e53908de 100644 --- a/tests/testthat/test-smk-dimDS.R +++ b/tests/testthat/test-smk-dimDS.R @@ -60,12 +60,12 @@ test_that("dimDS throws error when object does not exist", { ) }) -test_that("dimDS throws error when object is not data.frame or matrix", { - bad_input <- c(1, 2, 3) - expect_error( - dimDS("bad_input"), - regexp = "must be of type data.frame or matrix" - ) +test_that("dimDS accepts any class, e.g. a 3-D array or a table", { + arr <- array(1:24, dim = c(2, 3, 4)) + tab <- table(c("a", "b", "a")) + + expect_equal(dimDS("arr")$dim, c(2L, 3L, 4L)) + expect_equal(dimDS("tab")$dim, 2L) }) # diff --git a/tests/testthat/test-smk-glmDS1.R b/tests/testthat/test-smk-glmDS1.R index 69aa22fe..5ccb00a0 100644 --- a/tests/testthat/test-smk-glmDS1.R +++ b/tests/testthat/test-smk-glmDS1.R @@ -45,7 +45,7 @@ test_that("glmDS1 throws error when data does not exist", { ) }) -test_that("glmDS1 throws error when data is not a data.frame or matrix", { +test_that("glmDS1 throws error when data is not a data.frame or list", { bad_input <- c(1, 2, 3) expect_error( glmDS1(formula = stats::as.formula("y ~ x"), family = "gaussian", @@ -54,6 +54,29 @@ test_that("glmDS1 throws error when data is not a data.frame or matrix", { ) }) +test_that("glmDS1 gives the same result for data as a list as for a data.frame", { + D <- data.frame( + y = c(4.1, 4.2, 3.9, 2.9, 3.6, 3.8, 4.2, 4.2, 2.5, 3.5, + 2.8, 3.7, 3.3, 3.3, 2.4, 3.5, 3.0, 3.1, 3.5, 3.4), + x = c(41.0, 41.0, 39.0, 38.9, 40.6, 41.0, 40.7, 41.4, 36.0, 39.0, + 37.6, 40.0, 40.3, 40.7, 37.0, 40.0, 41.6, 36.3, 39.3, 40.0) + ) + D.list <- as.list(D) + + res.df <- suppressWarnings(glmDS1(formula = stats::as.formula("y ~ x"), family = "gaussian", weights = NULL, offset = NULL, data = "D")) + res.list <- suppressWarnings(glmDS1(formula = stats::as.formula("y ~ x"), family = "gaussian", weights = NULL, offset = NULL, data = "D.list")) + + expect_equal(res.list, res.df) +}) + +test_that("glmDS1 throws error when data is a matrix", { + D.matrix <- cbind(y = c(1, 2, 3), x = c(4, 5, 6)) + expect_error( + glmDS1(formula = stats::as.formula("y ~ x"), family = "gaussian", weights = NULL, offset = NULL, data = "D.matrix"), + regexp = "must be of type data.frame or list" + ) +}) + # # Done # diff --git a/tests/testthat/test-smk-glmDS2.R b/tests/testthat/test-smk-glmDS2.R index ddf4f284..01d52c09 100644 --- a/tests/testthat/test-smk-glmDS2.R +++ b/tests/testthat/test-smk-glmDS2.R @@ -50,7 +50,7 @@ test_that("glmDS2 throws error when dataName does not exist", { ) }) -test_that("glmDS2 throws error when dataName is not a data.frame or matrix", { +test_that("glmDS2 throws error when dataName is not a data.frame or list", { bad_input <- c(1, 2, 3) expect_error( glmDS2(formula = stats::as.formula("y ~ x"), family = "gaussian", @@ -59,6 +59,29 @@ test_that("glmDS2 throws error when dataName is not a data.frame or matrix", { ) }) +test_that("glmDS2 gives the same result for data as a list as for a data.frame", { + D <- data.frame( + y = c(4.1, 4.2, 3.9, 2.9, 3.6, 3.8, 4.2, 4.2, 2.5, 3.5, + 2.8, 3.7, 3.3, 3.3, 2.4, 3.5, 3.0, 3.1, 3.5, 3.4), + x = c(41.0, 41.0, 39.0, 38.9, 40.6, 41.0, 40.7, 41.4, 36.0, 39.0, + 37.6, 40.0, 40.3, 40.7, 37.0, 40.0, 41.6, 36.3, 39.3, 40.0) + ) + D.list <- as.list(D) + + res.df <- suppressWarnings(glmDS2(formula = stats::as.formula("y ~ x"), family = "gaussian", beta.vect = "0,0", offset = NULL, weights = NULL, dataName = "D")) + res.list <- suppressWarnings(glmDS2(formula = stats::as.formula("y ~ x"), family = "gaussian", beta.vect = "0,0", offset = NULL, weights = NULL, dataName = "D.list")) + + expect_equal(res.list, res.df) +}) + +test_that("glmDS2 throws error when data is a matrix", { + D.matrix <- cbind(y = c(1, 2, 3), x = c(4, 5, 6)) + expect_error( + glmDS2(formula = stats::as.formula("y ~ x"), family = "gaussian", beta.vect = "0,0", offset = NULL, weights = NULL, dataName = "D.matrix"), + regexp = "must be of type data.frame or list" + ) +}) + # # Done # diff --git a/tests/testthat/test-smk-glmSLMADS1.R b/tests/testthat/test-smk-glmSLMADS1.R index 7496a5f8..e5f941e0 100644 --- a/tests/testthat/test-smk-glmSLMADS1.R +++ b/tests/testthat/test-smk-glmSLMADS1.R @@ -45,7 +45,7 @@ test_that("glmSLMADS1 throws error when data does not exist", { ) }) -test_that("glmSLMADS1 throws error when data is not a data.frame or matrix", { +test_that("glmSLMADS1 throws error when data is not a data.frame or list", { bad_input <- c(1, 2, 3) expect_error( glmSLMADS1(formula = stats::as.formula("y ~ x"), family = "gaussian", diff --git a/tests/testthat/test-smk-glmSLMADS2.R b/tests/testthat/test-smk-glmSLMADS2.R index 74c26fea..15b2a144 100644 --- a/tests/testthat/test-smk-glmSLMADS2.R +++ b/tests/testthat/test-smk-glmSLMADS2.R @@ -49,7 +49,7 @@ test_that("glmSLMADS2 throws error when dataName does not exist", { ) }) -test_that("glmSLMADS2 throws error when dataName is not a data.frame or matrix", { +test_that("glmSLMADS2 throws error when dataName is not a data.frame or list", { bad_input <- c(1, 2, 3) expect_error( diff --git a/tests/testthat/test-smk-glmerSLMADS.assign.R b/tests/testthat/test-smk-glmerSLMADS.assign.R index abe56803..3c7ce4e4 100644 --- a/tests/testthat/test-smk-glmerSLMADS.assign.R +++ b/tests/testthat/test-smk-glmerSLMADS.assign.R @@ -45,7 +45,7 @@ test_that("glmerSLMADS.assign throws error when dataName does not exist", { ) }) -test_that("glmerSLMADS.assign throws error when dataName is not a data.frame or matrix", { +test_that("glmerSLMADS.assign throws error when dataName is not a data.frame or list", { bad_input <- c(1, 2, 3) expect_error( glmerSLMADS.assign(formula = stats::as.formula("y ~ x + (1|group)"), diff --git a/tests/testthat/test-smk-glmerSLMADS2.R b/tests/testthat/test-smk-glmerSLMADS2.R index 81b26fcb..a40dd97a 100644 --- a/tests/testthat/test-smk-glmerSLMADS2.R +++ b/tests/testthat/test-smk-glmerSLMADS2.R @@ -49,7 +49,7 @@ test_that("glmerSLMADS2 throws error when dataName does not exist", { ) }) -test_that("glmerSLMADS2 throws error when dataName is not a data.frame or matrix", { +test_that("glmerSLMADS2 throws error when dataName is not a data.frame or list", { bad_input <- c(1, 2, 3) expect_error( glmerSLMADS2(formula = stats::as.formula("y ~ x + (1|group)"), diff --git a/tests/testthat/test-smk-igb_standardsDS.R b/tests/testthat/test-smk-igb_standardsDS.R index bd3a8f79..f3f11a16 100644 --- a/tests/testthat/test-smk-igb_standardsDS.R +++ b/tests/testthat/test-smk-igb_standardsDS.R @@ -154,6 +154,18 @@ test_that("igb_standardsDS - igb_centile2value", { }) +test_that("igb_standardsDS throws error when gagebrth does not exist", { + data <- data.frame(sex = c("Male", "Female")) + + expect_error(igb_standardsDS(gagebrth="nonexistent_object", z=0, p=50, val=NULL, var="lencm", sex="data$sex", fun="igb_centile2value"), regexp = "does not exist") +}) + +test_that("igb_standardsDS throws error when val does not exist", { + data <- data.frame(gagebrth = c(250, 270), sex = c("Male", "Female")) + + expect_error(igb_standardsDS(gagebrth="data$gagebrth", z=0, p=50, val="nonexistent_object", var="lencm", sex="data$sex", fun="igb_value2zscore"), regexp = "does not exist") +}) + # # Done # diff --git a/tests/testthat/test-smk-isValidDS.R b/tests/testthat/test-smk-isValidDS.R index 83937374..bedab4c7 100644 --- a/tests/testthat/test-smk-isValidDS.R +++ b/tests/testthat/test-smk-isValidDS.R @@ -215,5 +215,5 @@ test_that("special isValidDS, NA", { test_that("special isValidDS, NULL", { input <- NULL - expect_error(isValidDS("input"), regexp = "must be of type") + expect_error(isValidDS("input"), "The server-side object 'input' is NULL", fixed = TRUE) }) diff --git a/tests/testthat/test-smk-lengthDS.R b/tests/testthat/test-smk-lengthDS.R index d91fafd9..f3216719 100644 --- a/tests/testthat/test-smk-lengthDS.R +++ b/tests/testthat/test-smk-lengthDS.R @@ -96,6 +96,15 @@ test_that("simple lengthDS, array", { expect_equal(res$class, "array") }) +test_that("lengthDS accepts any class, e.g. a Date vector", { + input <- as.Date(c("2020-01-01", "2020-06-01")) + + res <- lengthDS("input") + + expect_equal(res$length, 2) + expect_equal(res$class, "Date") +}) + # # Done # diff --git a/tests/testthat/test-smk-levelsDS.R b/tests/testthat/test-smk-levelsDS.R index 43fd7658..0a498229 100644 --- a/tests/testthat/test-smk-levelsDS.R +++ b/tests/testthat/test-smk-levelsDS.R @@ -61,6 +61,13 @@ test_that("levelsDS blocks when levels density exceeds threshold", { ) }) +test_that("levelsDS is blocked in non-permissive mode before any object lookup", { + options(datashield.privacyControlLevel = "non-permissive") + on.exit(options(datashield.privacyControlLevel = NULL), add = TRUE) + + expect_error(levelsDS("nonexistent_object"), regexp = "non-permissive") +}) + # # Done # diff --git a/tests/testthat/test-smk-lmerSLMADS.assign.R b/tests/testthat/test-smk-lmerSLMADS.assign.R index 6ef1a9c1..a5f8237a 100644 --- a/tests/testthat/test-smk-lmerSLMADS.assign.R +++ b/tests/testthat/test-smk-lmerSLMADS.assign.R @@ -71,7 +71,7 @@ test_that("lmerSLMADS.assign throws error when dataName does not exist", { ) }) -test_that("lmerSLMADS.assign throws error when dataName is not a data.frame or matrix", { +test_that("lmerSLMADS.assign throws error when dataName is not a data.frame or list", { bad_input <- c(1, 2, 3) expect_error( lmerSLMADS.assign(formula = stats::as.formula("y ~ x + (1|group)"), diff --git a/tests/testthat/test-smk-lmerSLMADS2.R b/tests/testthat/test-smk-lmerSLMADS2.R index 4e09bbe3..93d93000 100644 --- a/tests/testthat/test-smk-lmerSLMADS2.R +++ b/tests/testthat/test-smk-lmerSLMADS2.R @@ -48,7 +48,7 @@ test_that("lmerSLMADS2 throws error when dataName does not exist", { ) }) -test_that("lmerSLMADS2 throws error when dataName is not a data.frame or matrix", { +test_that("lmerSLMADS2 throws error when dataName is not a data.frame or list", { bad_input <- c(1, 2, 3) expect_error( lmerSLMADS2(formula = stats::as.formula("y ~ x + (1|group)"), diff --git a/tests/testthat/test-smk-mdPatternDS.R b/tests/testthat/test-smk-mdPatternDS.R index ba682b1e..3e11a024 100644 --- a/tests/testthat/test-smk-mdPatternDS.R +++ b/tests/testthat/test-smk-mdPatternDS.R @@ -27,7 +27,7 @@ test_that("mdPatternDS: sample 1 complete data.frame", { res <- mdPatternDS(x) - expect_length(res, 3) + expect_length(res, 4) expect_length(class(res), 1) expect_true(all(class(res) %in% c("list"))) expect_length(class(res$pattern), 2) @@ -64,7 +64,7 @@ test_that("mdPatternDS: sample 2 complete data.frame", { res <- mdPatternDS(x) - expect_length(res, 3) + expect_length(res, 4) expect_length(class(res), 1) expect_true(all(class(res) %in% c("list"))) expect_length(class(res$pattern), 2) diff --git a/tests/testthat/test-smk-meanDS.R b/tests/testthat/test-smk-meanDS.R index 8bb47c69..d15aa516 100644 --- a/tests/testthat/test-smk-meanDS.R +++ b/tests/testthat/test-smk-meanDS.R @@ -78,13 +78,37 @@ test_that("numeric meanDS, with all NA", { expect_equal(res$class, "numeric") }) +# context("meanDS::smk::logical with all NA") +test_that("logical meanDS, with all NA", { + input <- c(NA, NA, NA, NA, NA) + + res <- meanDS("input") + + expect_equal(res$EstimatedMean, NaN) + expect_equal(res$Nmissing, 5) + expect_equal(res$Nvalid, 0) + expect_equal(res$Ntotal, 5) + expect_equal(res$class, "logical") +}) + +# context("meanDS::smk::logical") +test_that("logical meanDS is the proportion TRUE", { + input <- c(TRUE, FALSE, TRUE, TRUE, NA) + + res <- meanDS("input") + + expect_equal(res$EstimatedMean, 0.75) + expect_equal(res$Nvalid, 4) + expect_equal(res$class, "logical") +}) + test_that("meanDS throws error when object does not exist", { expect_error(meanDS("nonexistent_object"), regexp = "does not exist") }) test_that("meanDS throws error when object is not numeric or integer", { bad_input <- c("a", "b", "c") - expect_error(meanDS("bad_input"), regexp = "must be of type numeric or integer") + expect_error(meanDS("bad_input"), regexp = "must be of type numeric, integer or logical") }) # diff --git a/tests/testthat/test-smk-meanSdGpDS.R b/tests/testthat/test-smk-meanSdGpDS.R index de3d4ecb..ed8ef814 100644 --- a/tests/testthat/test-smk-meanSdGpDS.R +++ b/tests/testthat/test-smk-meanSdGpDS.R @@ -37,6 +37,18 @@ test_that("simple meanSdGpDS, numeric by factor", { expect_equal(as.numeric(res$Mean_gp)[2], 8.0) }) +# context("meanSdGpDS::smk::numeric by double 0/1") +test_that("simple meanSdGpDS, numeric by a double 0/1 grouping variable", { + x_var <- c(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0) + index_var <- c(0, 0, 0, 0, 0, 1, 1, 1, 1, 1) + + res <- meanSdGpDS("x_var", "index_var") + + expect_equal(res$class.index, "numeric") + expect_equal(as.numeric(res$Mean_gp)[1], 3.0) + expect_equal(as.numeric(res$Mean_gp)[2], 8.0) +}) + test_that("meanSdGpDS throws error when X does not exist", { index_var <- as.factor(c("A", "A", "B", "B")) expect_error(meanSdGpDS("nonexistent_x", "index_var"), regexp = "does not exist") diff --git a/tests/testthat/test-smk-seqDS.R b/tests/testthat/test-smk-seqDS.R index 3a9edfb6..c5ed9370 100644 --- a/tests/testthat/test-smk-seqDS.R +++ b/tests/testthat/test-smk-seqDS.R @@ -135,6 +135,13 @@ test_that("seqDS errors when ALONG.WITH object does not exist", { ) }) +test_that("seqDS rejects a FROM value that is not a number", { + expect_error( + seqDS("not_a_number", "12", "2", NULL, NULL), + regexp = "FROM.value.char must be specified as a real number" + ) +}) + # # Done # diff --git a/tests/testthat/test-smk-utils.R b/tests/testthat/test-smk-utils.R index 131454e1..e3b3452d 100644 --- a/tests/testthat/test-smk-utils.R +++ b/tests/testthat/test-smk-utils.R @@ -48,6 +48,14 @@ test_that(".loadServersideObject() throws error for missing object", { ) }) +test_that(".loadServersideObject() throws error for a NULL object", { + null_obj <- NULL + expect_error( + .dsFunctionWrapper("null_obj"), + regexp = "The server-side object 'null_obj' is NULL" + ) +}) + test_that(".checkClass() passes for correct class", { df <- data.frame(a = 1) expect_invisible( diff --git a/tests/testthat/test-smk-varDS.R b/tests/testthat/test-smk-varDS.R index 51eac8e2..f3da7671 100644 --- a/tests/testthat/test-smk-varDS.R +++ b/tests/testthat/test-smk-varDS.R @@ -84,13 +84,28 @@ test_that("numeric varDS, with all NA", { expect_equal(res$class, "numeric") }) +# context("varDS::smk::logical with all NA") +test_that("logical varDS, with all NA", { + input <- c(NA, NA, NA, NA, NA) + + res <- varDS("input") + + expect_equal(class(res$Sum), "integer") + expect_equal(res$Sum, 0) + expect_equal(res$SumOfSquares, 0) + expect_equal(res$Nmissing, 5) + expect_equal(res$Nvalid, 0) + expect_equal(res$Ntotal, 5) + expect_equal(res$class, "logical") +}) + test_that("varDS throws error when object does not exist", { expect_error(varDS("nonexistent_object"), regexp = "does not exist") }) test_that("varDS throws error when object is not numeric or integer", { bad_input <- c("a", "b", "c") - expect_error(varDS("bad_input"), regexp = "must be of type numeric or integer") + expect_error(varDS("bad_input"), regexp = "must be of type numeric, integer or logical") }) #