diff --git a/DESCRIPTION b/DESCRIPTION index 4b25807..e1ab099 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: CancerEvolutionVisualization Title: Publication Quality Phylogenetic Tree Plots Version: 2.0.1 -Date: 2023-11-17 +Date: 2023-12-15 Authors@R: c( person("Paul Boutros", role = "cre", email = "PBoutros@mednet.ucla.edu"), person("Adriana Salcedo", role = "aut"), @@ -22,6 +22,7 @@ Imports: grDevices, utils, stringr, + rmarkdown, BoutrosLab.plotting.general Suggests: testthat, diff --git a/NAMESPACE b/NAMESPACE index 46e3517..fd9d4b0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,7 +8,7 @@ importFrom("graphics", "par", "strheight", "strwidth") importFrom("grDevices", "dev.list", "rainbow") importFrom("utils", "read.table", "vi", "head") importFrom("stringr", "str_replace_all") -importFrom("stats", "setNames", "aggregate", "reshape") +importFrom("stats", "setNames", "median", "aggregate", "reshape") importFrom("grDevices", "col2rgb") export(SRCGrob) diff --git a/NEWS b/NEWS index b004a80..0b3c512 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,11 @@ +CancerEvolutionVisualization 2.1.0 2023-12-15 (Helena Winata, Dan Knight) + +ADDED +* CEV report template and public function +* Generic functions to generate accompanying heatmaps for summarizing + CCF and clustering datas + +-------------------------------------------------------------------------- CancerEvolutionVisualization 2.0.1 2023-11-17 (Helena Winata, Dan Knight) ADDED @@ -207,4 +215,4 @@ CancerEvolutionVisualization 0.0.0 2021-09-13 (Adriana Salcedo) INITIAL FEATURES -- Tree, CNA, and SNV input sampling +- Tree, CNA, and SNV input sampling \ No newline at end of file diff --git a/R/CEV.report.R b/R/CEV.report.R new file mode 100644 index 0000000..4da3a0b --- /dev/null +++ b/R/CEV.report.R @@ -0,0 +1,36 @@ +CEV.report <- function( + phylogeny, + SNV.assignment, + SNV.counts, + CCF.values, + output.filename, + title, + author, + date = NULL + ) { + inputs <- prep.report( + phylogeny, + SNV.assignment, + SNV.counts, + CCF.values + ); + + report.params <- list( + title = title, + author = author, + date = if (!is.null(date)) date else Sys.Date(), + summary.tree.data = inputs$summary.tree.input, + heatmap.data = inputs$heatmap.input + ); + + template.path <- system.file( + 'CEV.report.Rmd', + package = 'CancerEvolutionVisualization' + ); + + rmarkdown::render( + template.path, + output_file = output.filename, + params = report.params + ); + } diff --git a/R/heatmap.R b/R/heatmap.R new file mode 100644 index 0000000..a07e723 --- /dev/null +++ b/R/heatmap.R @@ -0,0 +1,296 @@ +plot.ccf.hm <- function( + CCF.arr, + CCF.threshold = NULL, + cluster.dim = 'both', + cluster.method = 'complete', + dist.method = 'euclidean', + colour.scheme = NULL, + xaxis.lab = NULL, + xlab.label = 'Mutations', + filename = NULL, + ... + ) { + + if (!is.null(CCF.threshold)) { + CCF.arr[CCF.arr <= CCF.threshold] <- 0; + } + col.labels <- seq(0, 1, .2); + sample.names <- colnames(CCF.arr); + + heatmap.colours <- if (!is.null(colour.scheme)) { + colour.scheme; + } else { + default.heatmap.colours(); + } + + hm <- BoutrosLab.plotting.general::create.heatmap( + filename = filename, + x = CCF.arr, + force.clustering = TRUE, + cluster.dimensions = cluster.dim, + clustering.method = cluster.method, + rows.distance.method = dist.method, + cols.distance.method = dist.method, + xaxis.lab = xaxis.lab, + xlab.label = xlab.label, + xlab.cex = 1, + xaxis.cex = 0.6, + xaxis.fontface = 1, + xaxis.rot = 90, + yaxis.lab = sample.names, + ylab.cex = 1, + yaxis.cex = 0.6, + yaxis.fontface = 1, + colourkey.cex = 0.6, + colour.scheme = heatmap.colours, + left.padding = 1, + right.padding = 1, + resolution = 3000, + width = 9, + height = 5, + colourkey.labels.at = col.labels, + ... + ); + + return(hm); + } + +plot.cluster.hm <- function( + cluster.df, + plt.height = 6, + plt.width = 11, + colour.scheme = NULL, + xaxis.col = NULL, + filename = NULL, + ... + ) { + + if (is.null(levels(cluster.df$ID))) { + cluster.df$ID <- factor( + cluster.df$ID, + levels = sort(unique(cluster.df$ID)) + ); + } + + cluster.df <- droplevels(cluster.df)[order(cluster.df$clone.id, -abs(cluster.df$CCF)), ]; + arr <- data.frame.to.array(cluster.df); + snv.order <- unique(cluster.df[, c('SNV.id', 'clone.id')]); + cluster.colours <- get.colours(cluster.df$clone.id, return.names = TRUE); + arr <- arr[snv.order$SNV.id, levels(cluster.df$ID)]; + + heatmap.colours <- if (!is.null(colour.scheme)) { + colour.scheme; + } else { + default.heatmap.colours(); + } + + if (!is.null(xaxis.col)) { + xaxis.label <- unique(cluster.df[cluster.df$SNV.id %in% rownames(arr), xaxis.col]); + } + + hm <- plot.ccf.hm( + CCF.arr = arr, + cluster.dim = 'none', + colour.scheme = heatmap.colours, + ... + ); + + # Suppress "three-colour scheme" warning with 3 clones. + cov <- suppressWarnings(BoutrosLab.plotting.general::create.heatmap( + x = t(cluster.colours[snv.order$clone.id]), + input.colours = TRUE, + clustering.method = 'none', + grid.col = FALSE, + print.colour.key = FALSE, + resolution = 5000 + )); + + legend.clone <- BoutrosLab.plotting.general::legend.grob( + list( + legend = list( + title = 'Clones', + labels = names(cluster.colours), + colours = cluster.colours, + border = 'black' + ), + legend = list( + title = 'CCF', + labels = c(min(arr), max(arr)), + colours = heatmap.colours, + border = 'black', + continuous = TRUE, + size = 0.6 + ) + ), + size = 1, + title.cex = 0.75, + label.cex = 0.6 + ); + + plt <- BoutrosLab.plotting.general::create.multiplot( + filename = filename, + plot.objects = list(cov, hm), + plot.layout = c(1, 2), + panel.heights = c(1, 0.05), + xaxis.lab = if (!is.null(xaxis.col)) xaxis.label else NULL, + xaxis.cex = 0.6, + xaxis.rot = 90, + xaxis.fontface = 1, + xaxis.tck = 0, + yaxis.lab = list(NULL, colnames(arr)), + yaxis.cex = 0.6, + yaxis.tck = 0, + yaxis.fontface = 1, + y.spacing = 0.5, + left.padding = 17, + print.new.legend = TRUE, + legend = list(right = list( + fun = legend.clone + )), + height = plt.height, + width = plt.width + ); + return(plt); + } + +plot.summary.ccf.hm <- function( + mutation.df, + CCF.threshold = 0, + filename = NULL + ) { + + median.ccf <- aggregate( + mutation.df$CCF, + by = list(mutation.df$ID, mutation.df$clone.id), + FUN = median + ); + + colnames(median.ccf) <- c('ID', 'clone.id', 'median.CCF'); + + arr <- data.frame.to.array( + median.ccf, + value = 'median.CCF', + x.axis = 'clone.id', + y.axis = 'ID' + ); + arr[arr <= CCF.threshold] <- 0; + + filtered.CCFs <- mutation.df$CCF > 0; + SNV.per.clone <- aggregate(SNV.id ~ clone.id, mutation.df[filtered.CCFs, ], FUN = length); + colnames(SNV.per.clone) <- c('clone.id', 'num.SNV'); + + SNV.per.sample <- aggregate(SNV.id ~ ID, mutation.df[filtered.CCFs, ], FUN = length); + colnames(SNV.per.sample) <- c('ID', 'num.SNV'); + + heatmap.colours <- default.heatmap.colours(); + barplot.padding.percentage <- 0.05; + + max.clone.SNV <- max(SNV.per.clone$num.SNV); + + clone.bar <- BoutrosLab.plotting.general::create.barplot( + formula = num.SNV ~ clone.id, + data = SNV.per.clone, + yaxis.cex = 0, + xaxis.lab = rep('', nrow(arr)), + xaxis.cex = 0, + ylimits = c( + -(max.clone.SNV * barplot.padding.percentage), + max.clone.SNV * (1 + barplot.padding.percentage) + ), + resolution = 50 + ); + + max.sample.SNV <- max(SNV.per.sample$num.SNV); + + sample.bar <- BoutrosLab.plotting.general::create.barplot( + formula = ID ~ num.SNV, + data = SNV.per.sample, + xlab.label = 'SNV per sample', + xlimits = c( + -(max.sample.SNV * barplot.padding.percentage), + max.sample.SNV * (1 + barplot.padding.percentage) + ), + ylab.label = NULL, + yaxis.lab = rep('', length(arr)), + yaxis.cex = 0, + resolution = 50, + plot.horizontal = TRUE + ); + + hm <- BoutrosLab.plotting.general::create.heatmap( + x = arr, + cluster.dimensions = 'none', + xlab.cex = 1, + xlab.label = 'Clone ID', + xaxis.lab = rownames(arr), + xaxis.cex = 0.6, + xaxis.fontface = 1, + xaxis.rot = 90, + ylab.cex = 1, + ylab.label = 'Sample ID', + yaxis.lab = colnames(arr), + yaxis.cex = 0.6, + yaxis.fontface = 1, + print.colour.key = FALSE, + colour.scheme = heatmap.colours, + left.padding = 1, + right.padding = 1, + width = 9, + height = 5 + ); + + legend.ccf <- BoutrosLab.plotting.general::legend.grob( + list( + legend = list( + title = 'CCF', + labels = c(min(arr), max(arr)), + colours = heatmap.colours, + border = 'black', + continuous = TRUE, + size = 0.6 + ) + ), + size = 1, + title.cex = 0.75, + label.cex = 0.6 + ); + + plt <- BoutrosLab.plotting.general::create.multiplot( + filename = filename, + plot.objects = list(hm, sample.bar, clone.bar), + plot.layout = c(2, 2), + layout.skip = c(FALSE, FALSE, FALSE, TRUE), + panel.heights = c(0.3, 1), + panel.widths = c(1, 0.2), + plot.labels.to.retrieve = 1:3, + xlab.label = c('\t', 'Clone ID', '\t', '\t', 'SNV per sample'), + xlab.cex = 0.7, + xaxis.cex = 0.6, + xaxis.tck = 0.4, + xaxis.rot = 90, + xaxis.fontface = 1, + xlab.to.xaxis.padding = - 0.5, + ylab.label = c( 'SNV per clone', '\t', '\t', 'Sample ID', '\t'), + ylab.padding = 8, + ylab.cex = 0.7, + yaxis.cex = 0.6, + yaxis.tck = 0.4, + yaxis.fontface = 1, + x.spacing = c(0), + y.spacing = c(-0.5), + left.padding = 10, + bottom.padding = 3, + merge.legends = FALSE, + print.new.legend = TRUE, + legend = list(right = list( + fun = legend.ccf + )), + height = 6, + width = 11 + ) + return(plt); + } + +default.heatmap.colours <- function() { + return(c('white', 'blue')) + } diff --git a/R/prep.report.R b/R/prep.report.R new file mode 100644 index 0000000..fb5fbd7 --- /dev/null +++ b/R/prep.report.R @@ -0,0 +1,117 @@ +prep.report <- function( + phylogeny, + SNV.assignment, + SNV.counts, + CCF.values + ) { + phylogeny <- prep.phylogeny(phylogeny); + SNV.assignment <- prep.SNV.assignment(SNV.assignment); + SNV.counts <- prep.SNV.counts(SNV.counts); + CCF.values <- prep.CCF.values(CCF.values); + + validate.clone.ids(phylogeny, SNV.assignment, SNV.counts); + + summary.tree.input <- create.report.summary.tree.input(phylogeny, SNV.counts); + heatmap.input <- create.report.heatmap.input(SNV.assignment, CCF.values); + + return(list( + summary.tree.input = summary.tree.input, + heatmap.input = heatmap.input + )); + } + +prep.phylogeny <- function(phylogeny) { + phylogeny.data.name <- 'Phylogeny'; + + if (!is.data.frame(phylogeny)) { + stop(paste(phylogeny.data.name, 'input is not a data.frame.')); + } + + check.column.exists(phylogeny, 'clone.id', phylogeny.data.name); + check.column.exists(phylogeny, 'parent', phylogeny.data.name); + + return(phylogeny); + } + +prep.SNV.assignment <- function(SNV.assignment) { + SNV.assignment.data.name <- 'SNV Assignment'; + + if (!is.data.frame(SNV.assignment)) { + stop(paste(SNV.assignment.data.name, 'input is not a data.frame.')); + } + + check.column.exists(SNV.assignment, 'SNV.id', SNV.assignment.data.name); + check.column.exists(SNV.assignment, 'clone.id', SNV.assignment.data.name); + + return(SNV.assignment); + }; + +prep.SNV.counts <- function(SNV.counts) { + SNV.count.data.name <- 'SNV Count'; + if (!is.data.frame(SNV.counts)) { + stop(paste(SNV.count.data.name, 'input is not a data.frame.')); + } + + check.column.exists(SNV.counts, 'clone.id', SNV.count.data.name); + check.column.exists(SNV.counts, 'num.snv', SNV.count.data.name); + check.column.exists(SNV.counts, 'CP', SNV.count.data.name); + + return(SNV.counts); + }; + +prep.CCF.values <- function(CCF.values) { + CCF.value.data.name <- 'CCF'; + + if (!is.data.frame(CCF.values)) { + stop(paste(CCF.value.data.name, 'input is not a data.frame.')); + } + + check.column.exists(CCF.values, 'sample.id', CCF.value.data.name); + check.column.exists(CCF.values, 'SNV.id', CCF.value.data.name); + check.column.exists(CCF.values, 'CCF', CCF.value.data.name); + + return(CCF.values); + }; + +validate.clone.ids <- function( + phylogeny, + SNV.assignment, + SNV.counts + ) { + + reference.clone.ids <- unique(phylogeny$clone.id); + + get.clone.error.message <- function(input.name) { + return(paste(input.name, 'clone IDs do not match phylogeny clone IDs.')) + } + + if (!column.contains.all(reference.clone.ids, SNV.assignment$clone.id)) { + stop(get.clone.error.message('SNV Assignment')); + } + + if (!column.contains.all(reference.clone.ids, SNV.counts$clone.id)) { + stop(get.clone.error.message('SNV Count')) + } + } + +create.report.summary.tree.input <- function(phylogeny, SNV.counts) { + clone.ids <- phylogeny$clone.id; + rownames(SNV.counts) <- SNV.counts$clone.id; + + return(data.frame( + node.id = clone.ids, + parent = phylogeny$parent, + length1 = SNV.counts[clone.ids, 'num.snv'] + )); + } + +create.report.heatmap.input <- function(SNV.assignment, CCF.values) { + rownames(SNV.assignment) <- SNV.assignment$SNV.id; + + return(data.frame( + ID = CCF.values$sample.id, + SNV.id = CCF.values$SNV.id, + CCF = CCF.values$CCF, + clone.id = SNV.assignment[CCF.values$SNV.id, 'clone.id'] + )); + } diff --git a/R/utility.R b/R/utility.R index ec2b060..72189c7 100644 --- a/R/utility.R +++ b/R/utility.R @@ -19,10 +19,61 @@ reindex.column <- function(column.values, new.value.index) { )); } +check.column.exists <- function( + df, + column.name, + data.name = NULL + ) { + + result <- column.name %in% colnames(df); + + if (!result) { + message <- paste( + 'No column', + paste0('"', column.name, '"'), + 'found in', + if (!is.null(data.name)) data.name else 'data' + ); + + stop(message); + } + } + +column.contains.all <- function(reference.column, checked.column) { + vector.error.message <- function(column.type) { + return(paste(column.type, 'must be a vector.')) + } + + if (!is.vector(reference.column)) { + stop(vector.error.message('Reference')); + } + + if (!is.vector(checked.column)) { + stop(vector.error.message('Checked')); + } + + if (is.list(reference.column)) { + reference.column <- unlist(reference.column); + } + + reference.values <- sapply( + reference.column, + FUN = function(column.name) TRUE, + USE.NAMES = TRUE + ); + + values.in.reference <- all(sapply( + checked.column, + FUN = function(column.name) !is.na(reference.values[column.name]) + )); + + return(values.in.reference); + } + data.frame.to.array <- function( DF, value = 'CCF', - x.axis = 'snv.id', + x.axis = 'SNV.id', y.axis = 'ID' ) { diff --git a/inst/CEV.pdf b/inst/CEV.pdf new file mode 100644 index 0000000..6097fa2 Binary files /dev/null and b/inst/CEV.pdf differ diff --git a/inst/CEV.report.Rmd b/inst/CEV.report.Rmd new file mode 100644 index 0000000..64df7b8 --- /dev/null +++ b/inst/CEV.report.Rmd @@ -0,0 +1,28 @@ +--- +params: + title: + author: + date: + summary.tree.data: + heatmap.data: +title: '`r params$title`' +author: '`r params$author`' +date: '`r params$date`' +output: pdf_document +--- + +# Phylogenetic Tree +```{r summary-tree, echo=F, warning=F} +summary.tree <- SRCGrob(params$summary.tree.data); +grid.draw(summary.tree); +``` + +# Mutation Assignment Heatmap +```{r snv-heatmap, echo=F, warning=F} +plot.cluster.hm(params$heatmap.data); +``` + +# Clone Summary Heatmap +```{r summary-heatmap, echo=F, warning=F} +plot.summary.ccf.hm(params$heatmap.data); +``` diff --git a/tests/testthat/data/multisample.test.data.Rda b/tests/testthat/data/multisample.test.data.Rda new file mode 100644 index 0000000..db939f8 Binary files /dev/null and b/tests/testthat/data/multisample.test.data.Rda differ diff --git a/tests/testthat/data/report.data.Rda b/tests/testthat/data/report.data.Rda new file mode 100644 index 0000000..233e2a7 Binary files /dev/null and b/tests/testthat/data/report.data.Rda differ diff --git a/tests/testthat/test-heatmap.R b/tests/testthat/test-heatmap.R new file mode 100644 index 0000000..2577791 --- /dev/null +++ b/tests/testthat/test-heatmap.R @@ -0,0 +1,18 @@ +load('data/multisample.test.data.Rda'); + +test_that('plot.ccf.hm runs with valid data input', { + mutation.CCF.data <- data.frame.to.array(multisample.result.data$mutation); + + CCF.heatmap <- plot.ccf.hm(mutation.CCF.data); + expect_is(CCF.heatmap, 'trellis'); + }); + +test_that('plot.cluster.hm runs with valid data input', { + CCF.heatmap <- plot.cluster.hm(multisample.result.data$mutation); + expect_is(CCF.heatmap, 'trellis'); + }); + +test_that('plot.summary.ccf.hm runs with valid data input', { + CCF.heatmap <- plot.summary.ccf.hm(multisample.result.data$mutation); + expect_is(CCF.heatmap, 'trellis'); + }); diff --git a/tests/testthat/test-prep.report.R b/tests/testthat/test-prep.report.R new file mode 100644 index 0000000..f17fc69 --- /dev/null +++ b/tests/testthat/test-prep.report.R @@ -0,0 +1,150 @@ +load('data/report.data.Rda'); + +test_that('prep.phylogeny errors on missing clone.id column', { + invalid.phylogeny <- data.frame(clone.id = c(1)); + + expect_error( + prep.phylogeny(invalid.phylogeny), + regexp = 'parent' + ); + }); + +test_that('prep.phylogeny errors on missing parent column', { + invalid.phylogeny <- data.frame(parent = c(1)); + + expect_error( + prep.phylogeny(invalid.phylogeny), + regexp = 'clone.id' + ); + }); + + +test_that('prep.SNV.assignment errors on missing SNV.id column', { + invalid.SNV.assignment <- data.frame(clone.id = c(1)); + + expect_error( + prep.SNV.assignment(invalid.SNV.assignment), + regexp = 'SNV.id' + ); + }); + +test_that('prep.SNV.assignment errors on missing clone.id column', { + invalid.SNV.assignment <- data.frame(SNV.id = c(1)); + + expect_error( + prep.SNV.assignment(invalid.SNV.assignment), + regexp = 'clone.id' + ); + }); + +test_that('prep.SNV.counts errors on missing clone.id column', { + invalid.SNV.counts <- data.frame(num.snv = c(1), CP = c(1)); + + expect_error( + prep.SNV.counts(invalid.SNV.counts), + regexp = 'clone.id' + ); + }); + +test_that('prep.SNV.counts errors on missing num.snv column', { + invalid.SNV.counts <- data.frame(clone.id = c(1), CP = c(1)); + + expect_error( + prep.SNV.counts(invalid.SNV.counts), + regexp = 'num.snv' + ); + }); + +test_that('prep.SNV.counts errors on missing CP column', { + invalid.SNV.counts <- data.frame(clone.id = c(1), num.snv = c(1)); + + expect_error( + prep.SNV.counts(invalid.SNV.counts), + regexp = 'CP' + ); + }); + +test_that('prep.CCF.values errors on missing sample.id column', { + invalid.CCF.values <- data.frame(SNV.id = c(1), CCF = c(1)); + + expect_error( + prep.CCF.values(invalid.CCF.values), + regexp = 'sample.id' + ); + }); + +test_that('prep.CCF.values errors on missing SNV.id column', { + invalid.CCF.values <- data.frame(sample.id = c(1), CCF = c(1)); + + expect_error( + prep.CCF.values(invalid.CCF.values), + regexp = 'SNV.id' + ); + }); + +test_that('prep.CCF.values errors on missing CCF column', { + invalid.CCF.values <- data.frame(sample.id = c(1), SNV.id = c(1)); + + expect_error( + prep.CCF.values(invalid.CCF.values), + regexp = 'CCF' + ); + }); + +test_that('validate.clone.ids handles valid clone IDs', { + input <- data.frame(clone.ids = c('A', 'B', 'C')); + expect_silent(validate.clone.ids(input, input, input)); + }); + +test_that('validate.clone.ids handles invalid SNV assignment clone IDs', { + reference.clone.ids <- c('B', 'A', 'B', 'C'); + invalid.clone.ids <- sapply(reference.clone.ids, function(x) paste0(x, 'B')); + + phylogeny <- SNV.counts <- data.frame(clone.ids = reference.clone.ids); + SNV.assignment <- data.frame(clone.ids = invalid.clone.ids); + + expect_error( + validate.clone.ids(phylogeny, SNV.assignment, SNV.counts), 'clone'); + }); + +test_that('validate.clone.ids handles invalid SNV count clone IDs', { + reference.clone.ids <- c('1', '3', 'B', '4'); + invalid.clone.ids <- sapply(reference.clone.ids, function(x) paste0(x, 'A')); + + phylogeny <- SNV.assignment <- data.frame(clone.ids = reference.clone.ids); + SNV.counts <- data.frame(clone.ids = invalid.clone.ids); + + expect_error( + validate.clone.ids(phylogeny, SNV.assignment, SNV.counts), 'clone'); + }); + +test_that('create.report.tree.input result can create SRCGrob', { + input <- create.report.summary.tree.input(phylogeny, SNV.counts); + tree <- SRCGrob(input); + + expect_is(tree, 'SRCGrob'); + }); + +test_that('create.report.heatmap.input result can create CCF heatmap', { + input <- data.frame.to.array( + create.report.heatmap.input(SNV.assignment, CCF.values) + ); + + heatmap <- plot.ccf.hm(input); + + expect_is(heatmap, 'trellis'); + }); + +test_that('create.report.heatmap.input result can create summary heatmap', { + input <- create.report.heatmap.input(SNV.assignment, CCF.values); + heatmap <- plot.summary.ccf.hm(input); + + expect_is(heatmap, 'trellis'); + }); + +test_that('create.report.heatmap.input result can create cluster heatmap', { + input <- create.report.heatmap.input(SNV.assignment, CCF.values); + heatmap <- plot.cluster.hm(input); + + expect_is(heatmap, 'trellis'); + }); diff --git a/tests/testthat/test-utility.R b/tests/testthat/test-utility.R index 940d2f8..253691d 100644 --- a/tests/testthat/test-utility.R +++ b/tests/testthat/test-utility.R @@ -32,3 +32,22 @@ test_that( expect_equal(as.numeric(reindexed), expected.values); }); + +test_that('column.contains.all handles valid case', { + reference <- c('1', '2'); + expect_true(column.contains.all(reference, reference)); + }); + +test_that('column.contains.all errors on non-vector reference input', { + reference <- NULL; + checked <- 1:10; + + expect_error(column.contains.all(reference, checked), 'vector'); + }); + +test_that('column.contains.all errors on non-vector checked input', { + reference <- c('A', 'C', 'D'); + other <- NULL; + + expect_error(column.contains.all(reference, other), 'vector'); + });