diff --git a/README.md b/README.md index 2a708d8..ef30d97 100644 --- a/README.md +++ b/README.md @@ -2,5 +2,7 @@ Code Ocean capsule: MOSuite - filter differential expression analysis results +[![tests](https://github.com/NIDAP-Community/MOSuite-filter-diff/actions/workflows/tests.yml/badge.svg)](https://github.com/NIDAP-Community/MOSuite-filter-diff/actions/workflows/tests.yml) + - [Code Ocean Capsule](https://poc-nci.codeocean.io/capsule/8221739/tree) | [Latest Release](https://poc-nci.codeocean.io/capsule/0936928/tree/latest) - [MOSuite R package docs](https://ccbr.github.io/MOSuite/) \ No newline at end of file diff --git a/code/main.R b/code/main.R index 50e7042..2e8249a 100644 --- a/code/main.R +++ b/code/main.R @@ -1,5 +1,4 @@ #!/usr/bin/env Rscript -rlang::global_entrace() library(argparse) library(glue) library(MOSuite) @@ -13,28 +12,138 @@ setup_capsule_environment() # parse CLI arguments parser <- ArgumentParser() -parser$add_argument("--feature_id_colname", type="character", default=NULL, help="Column name for feature IDs") -parser$add_argument("--significance_column", type="character", default="adjpval", help="Column name for significance") -parser$add_argument("--significance_cutoff", type="double", default=0.05, help="Significance cutoff threshold") -parser$add_argument("--change_column", type="character", default="logFC", help="Column name for change") -parser$add_argument("--change_cutoff", type="double", default=1, help="Change cutoff threshold") -parser$add_argument("--filtering_mode", type="character", default="any", help="Filter mode: any or all") -parser$add_argument("--include_estimates", type="character", default="FC,logFC,tstat,pval,adjpval", help="Comma-separated list of estimates to include") -parser$add_argument("--round_estimates", type="logical", default=TRUE, help="Round estimates") -parser$add_argument("--rounding_decimal_for_percent_cells", type="integer", default=0, help="Decimal places for percent labels") -parser$add_argument("--contrast_filter", type="character", default="none", help="Filter contrasts: keep, remove, or none") -parser$add_argument("--contrasts", type="character", default="", help="Comma-separated list of contrasts") -parser$add_argument("--groups", type="character", default="", help="Comma-separated list of groups") -parser$add_argument("--groups_filter", type="character", default="none", help="Filter groups: keep, remove, or none") -parser$add_argument("--label_font_size", type="double", default=6, help="Font size for labels") -parser$add_argument("--label_distance", type="double", default=1, help="Distance of labels from bars") -parser$add_argument("--y_axis_expansion", type="double", default=0.08, help="Y-axis expansion") -parser$add_argument("--fill_colors", type="character", default="steelblue1,whitesmoke", help="Comma-separated fill colors") -parser$add_argument("--pie_chart_in_3d", type="logical", default=TRUE, help="Draw pie charts in 3D") -parser$add_argument("--bar_width", type="double", default=0.4, help="Bar width") -parser$add_argument("--draw_bar_border", type="logical", default=TRUE, help="Draw bar borders") -parser$add_argument("--plot_type", type="character", default="bar", help="Plot type: bar or pie") -parser$add_argument("--plot_titles_fontsize", type="integer", default=12, help="Font size for plot titles") +parser$add_argument( + "--feature_id_colname", + type = "character", + default = NULL, + help = "Column name for feature IDs" +) +parser$add_argument( + "--significance_column", + type = "character", + default = "adjpval", + help = "Column name for significance" +) +parser$add_argument( + "--significance_cutoff", + type = "double", + default = 0.05, + help = "Significance cutoff threshold" +) +parser$add_argument( + "--change_column", + type = "character", + default = "logFC", + help = "Column name for change" +) +parser$add_argument( + "--change_cutoff", + type = "double", + default = 1, + help = "Change cutoff threshold" +) +parser$add_argument( + "--filtering_mode", + type = "character", + default = "any", + help = "Filter mode: any or all" +) +parser$add_argument( + "--include_estimates", + type = "character", + default = "FC,logFC,tstat,pval,adjpval", + help = "Comma-separated list of estimates to include" +) +parser$add_argument( + "--round_estimates", + type = "logical", + default = TRUE, + help = "Round estimates" +) +parser$add_argument( + "--rounding_decimal_for_percent_cells", + type = "integer", + default = 0, + help = "Decimal places for percent labels" +) +parser$add_argument( + "--contrast_filter", + type = "character", + default = "none", + help = "Filter contrasts: keep, remove, or none" +) +parser$add_argument( + "--contrasts", + type = "character", + default = "", + help = "Comma-separated list of contrasts" +) +parser$add_argument( + "--groups", + type = "character", + default = "", + help = "Comma-separated list of groups" +) +parser$add_argument( + "--groups_filter", + type = "character", + default = "none", + help = "Filter groups: keep, remove, or none" +) +parser$add_argument( + "--label_font_size", + type = "double", + default = 6, + help = "Font size for labels" +) +parser$add_argument( + "--label_distance", + type = "double", + default = 1, + help = "Distance of labels from bars" +) +parser$add_argument( + "--y_axis_expansion", + type = "double", + default = 0.08, + help = "Y-axis expansion" +) +parser$add_argument( + "--fill_colors", + type = "character", + default = "steelblue1,whitesmoke", + help = "Comma-separated fill colors" +) +parser$add_argument( + "--pie_chart_in_3d", + type = "logical", + default = TRUE, + help = "Draw pie charts in 3D" +) +parser$add_argument( + "--bar_width", + type = "double", + default = 0.4, + help = "Bar width" +) +parser$add_argument( + "--draw_bar_border", + type = "logical", + default = TRUE, + help = "Draw bar borders" +) +parser$add_argument( + "--plot_type", + type = "character", + default = "bar", + help = "Plot type: bar or pie" +) +parser$add_argument( + "--plot_titles_fontsize", + type = "integer", + default = 12, + help = "Font size for plot titles" +) args <- parser$parse_args() @@ -42,29 +151,34 @@ args <- parser$parse_args() moo <- load_moo_from_data_dir() # run MOSuite -moo |> - filter_diff( - feature_id_colname = args$feature_id_colname, - significance_column = args$significance_column, - significance_cutoff = args$significance_cutoff, - change_column = args$change_column, - change_cutoff = args$change_cutoff, - filtering_mode = args$filtering_mode, - include_estimates = parse_optional_vector(args$include_estimates), - round_estimates = args$round_estimates, - rounding_decimal_for_percent_cells = args$rounding_decimal_for_percent_cells, - contrast_filter = args$contrast_filter, - contrasts = parse_optional_vector(args$contrasts), - groups = parse_optional_vector(args$groups), - groups_filter = args$groups_filter, - label_font_size = args$label_font_size, - label_distance = args$label_distance, - y_axis_expansion = args$y_axis_expansion, - fill_colors = parse_optional_vector(args$fill_colors), - pie_chart_in_3d = args$pie_chart_in_3d, - bar_width = args$bar_width, - draw_bar_border = args$draw_bar_border, - plot_type = args$plot_type, - plot_titles_fontsize = args$plot_titles_fontsize - ) |> - write_rds(file.path(getOption("moo_plots_dir"), "..", "moo", "moo.rds")) +moo |> + filter_diff( + feature_id_colname = args$feature_id_colname, + significance_column = args$significance_column, + significance_cutoff = args$significance_cutoff, + change_column = args$change_column, + change_cutoff = args$change_cutoff, + filtering_mode = args$filtering_mode, + include_estimates = parse_optional_vector(args$include_estimates), + round_estimates = args$round_estimates, + rounding_decimal_for_percent_cells = args$rounding_decimal_for_percent_cells, + contrast_filter = args$contrast_filter, + contrasts = parse_optional_vector(args$contrasts), + groups = parse_optional_vector(args$groups), + groups_filter = args$groups_filter, + label_font_size = args$label_font_size, + label_distance = args$label_distance, + y_axis_expansion = args$y_axis_expansion, + fill_colors = parse_optional_vector(args$fill_colors), + pie_chart_in_3d = args$pie_chart_in_3d, + bar_width = args$bar_width, + draw_bar_border = args$draw_bar_border, + plot_type = args$plot_type, + plot_titles_fontsize = args$plot_titles_fontsize + ) |> + write_rds(file.path( + getOption("moo_plots_dir"), + "..", + "moo", + "moo-diff-filt.rds" + )) diff --git a/tests/data/moo-diff.rds b/tests/data/moo-diff.rds new file mode 100644 index 0000000..330b3e6 Binary files /dev/null and b/tests/data/moo-diff.rds differ diff --git a/tests/testthat.R b/tests/testthat.R new file mode 100644 index 0000000..ca2904a --- /dev/null +++ b/tests/testthat.R @@ -0,0 +1,2 @@ +library(testthat) +test_dir(file.path('tests', 'testthat'), stop_on_failure = TRUE) diff --git a/tests/testthat/test-main.R b/tests/testthat/test-main.R new file mode 100644 index 0000000..4853e1a --- /dev/null +++ b/tests/testthat/test-main.R @@ -0,0 +1,148 @@ +test_that("code/run executes successfully with default CLI arguments", { + # Create temporary workspace + workspace <- tempfile("mosuite_filter_diff_test_") + dir.create(workspace) + on.exit(unlink(workspace, recursive = TRUE), add = TRUE) + + # Set up directory structure + code_dir <- file.path(workspace, "code") + data_dir <- file.path(workspace, "data") + results_dir <- file.path(code_dir, "..", "results") + dir.create(code_dir) + dir.create(data_dir) + dir.create(results_dir) + + # Get test data from package tests directory + repo_root <- normalizePath(file.path(dirname(getwd()), "..")) + test_data_file <- file.path(repo_root, "tests", "data", "moo-diff.rds") + + expect_true( + file.exists(test_data_file), + info = paste("Test data file should exist at", test_data_file) + ) + file.copy(test_data_file, file.path(data_dir, "moo.rds")) + + # Copy main.R and run script to workspace + file.copy( + file.path(repo_root, "code", "main.R"), + file.path(code_dir, "main.R") + ) + file.copy( + file.path(repo_root, "code", "run"), + file.path(code_dir, "run") + ) + + # Run the script from code directory + old_wd <- getwd() + setwd(code_dir) + on.exit(setwd(old_wd), add = TRUE) + + # Execute run script with default CLI arguments + exit_code <- system2( + "bash", + args = c( + "run", + "--significance_column=adjpval", + "--significance_cutoff=0.05", + "--change_column=logFC", + "--change_cutoff=1", + "--filtering_mode=any" + ) + ) + + # Check for successful execution + expect_equal(exit_code, 0, info = "run script should execute without error") + expect_true( + file.exists(file.path(results_dir, "moo", "moo-diff-filt.rds")), + info = "Output file moo-diff-filt.rds should be created" + ) + + # Validate output is a valid MOO object + moo <- readr::read_rds(file.path(results_dir, "moo", "moo-diff-filt.rds")) + expect_true( + S7::S7_inherits(moo, MOSuite::multiOmicDataSet), + info = "Output should be an S7 multiOmicDataSet object" + ) + + # Validate diff results exist + expect_true( + "diff" %in% names(moo@analyses), + info = "Output should have diff results in moo@analyses" + ) +}) + +test_that("code/run executes with custom CLI arguments", { + # Create temporary workspace + workspace <- tempfile("mosuite_filter_diff_custom_test_") + dir.create(workspace) + on.exit(unlink(workspace, recursive = TRUE), add = TRUE) + + # Set up directory structure + code_dir <- file.path(workspace, "code") + data_dir <- file.path(workspace, "data") + results_dir <- file.path(code_dir, "..", "results") + dir.create(code_dir) + dir.create(data_dir) + dir.create(results_dir) + + # Get test data from package tests directory + repo_root <- normalizePath(file.path(dirname(getwd()), "..")) + test_data_file <- file.path(repo_root, "tests", "data", "moo-diff.rds") + + # Copy test data to workspace + file.copy(test_data_file, file.path(data_dir, "moo.rds")) + + # Copy main.R and run script to workspace + file.copy( + file.path(repo_root, "code", "main.R"), + file.path(code_dir, "main.R") + ) + file.copy( + file.path(repo_root, "code", "run"), + file.path(code_dir, "run") + ) + + # Run the script from code directory + old_wd <- getwd() + setwd(code_dir) + on.exit(setwd(old_wd), add = TRUE) + + # Execute run script with custom CLI arguments + exit_code <- system2( + "bash", + args = c( + "run", + "--significance_column=pval", + "--significance_cutoff=0.01", + "--change_column=logFC", + "--change_cutoff=2", + "--filtering_mode=all", + "--round_estimates=TRUE", + "--plot_type=bar" + ) + ) + + # Check for successful execution + expect_equal( + exit_code, + 0, + info = "run script with custom args should execute without error" + ) + expect_true( + file.exists(file.path(results_dir, "moo", "moo-diff-filt.rds")), + info = "Output file moo-diff-filt.rds should be created with custom args" + ) + + # Validate output is a valid MOO object + moo <- readr::read_rds(file.path(results_dir, "moo", "moo-diff-filt.rds")) + expect_true( + S7::S7_inherits(moo, MOSuite::multiOmicDataSet), + info = "Output should be an S7 multiOmicDataSet object" + ) + + # Validate diff results exist + expect_true( + "diff" %in% names(moo@analyses), + info = "Output should have diff results in moo@analyses" + ) +})