Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: tests

on:
push:
branches: [ main, dev ]
pull_request:
branches: [ main, dev ]

jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Parse environment.json and set Docker image
id: docker_image
run: |
IMAGE=$(jq -r '.base_image' .codeocean/environment.json)
IMAGE=${IMAGE//codeocean/nciccbr}
echo "image=$IMAGE" >> $GITHUB_OUTPUT
echo "Using Docker image: $IMAGE"

- name: Run tests in Docker
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
${{ steps.docker_image.outputs.image }} \
Rscript tests/testthat.R
Comment on lines +11 to +29

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium test

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
110 changes: 81 additions & 29 deletions code/main.R
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/usr/bin/env Rscript
rlang::global_entrace()
library(argparse)
library(glue)
library(MOSuite)
Expand All @@ -13,17 +12,67 @@ setup_capsule_environment()
# parse CLI arguments
parser <- ArgumentParser()

parser$add_argument("--count_type", type="character", default="filt")
parser$add_argument("--sub_count_type", type="character", default=NULL, help="Sub count type if count_type is a list")
parser$add_argument("--sample_id_colname", type="character", default=NULL, help="Column name for sample IDs")
parser$add_argument("--feature_id_colname", type="character", default=NULL, help="Column name for feature IDs")
parser$add_argument("--samples_to_include", type="character", default="", help="Comma-separated list of samples to include")
parser$add_argument("--covariates_colnames", type="character", default="Group", help="Comma-separated list of covariate column names")
parser$add_argument("--contrast_colname", type="character", default="Group", help="Column containing group variables for DE")
parser$add_argument("--contrasts", type="character", default="", help="Contrasts in format group1-group2,group1-group3")
parser$add_argument("--input_in_log_counts", type="logical", default=FALSE, help="Counts are already log2-transformed")
parser$add_argument("--return_mean_and_sd", type="logical", default=FALSE, help="Return mean and SD in addition to DE estimates")
parser$add_argument("--voom_normalization_method", type="character", default="quantile", help="Normalization method for limma::voom")
parser$add_argument("--count_type", type = "character", default = "filt")
parser$add_argument(
"--sub_count_type",
type = "character",
default = NULL,
help = "Sub count type if count_type is a list"
)
parser$add_argument(
"--sample_id_colname",
type = "character",
default = NULL,
help = "Column name for sample IDs"
)
parser$add_argument(
"--feature_id_colname",
type = "character",
default = NULL,
help = "Column name for feature IDs"
)
parser$add_argument(
"--samples_to_include",
type = "character",
default = "",
help = "Comma-separated list of samples to include"
)
parser$add_argument(
"--covariates_colnames",
type = "character",
default = "Group",
help = "Comma-separated list of covariate column names"
)
parser$add_argument(
"--contrast_colname",
type = "character",
default = "Group",
help = "Column containing group variables for DE"
)
parser$add_argument(
"--contrasts",
type = "character",
default = "",
help = "Contrasts in format group1-group2,group1-group3"
)
parser$add_argument(
"--input_in_log_counts",
type = "logical",
default = FALSE,
help = "Counts are already log2-transformed"
)
parser$add_argument(
"--return_mean_and_sd",
type = "logical",
default = FALSE,
help = "Return mean and SD in addition to DE estimates"
)
parser$add_argument(
"--voom_normalization_method",
type = "character",
default = "quantile",
help = "Normalization method for limma::voom"
)

args <- parser$parse_args()

Expand All @@ -32,25 +81,28 @@ moo <- load_moo_from_data_dir()

# validate required parameters
if (identical(args$covariates_colnames, "")) {
stop("covariates_colnames is required and cannot be empty")
stop("covariates_colnames is required and cannot be empty")
}
if (identical(args$contrasts, "")) {
stop("contrasts is required and cannot be empty")
stop("contrasts is required and cannot be empty")
}

# run MOSuite
moo |>
diff_counts(
count_type = args$count_type,
sub_count_type = args$sub_count_type,
sample_id_colname = args$sample_id_colname,
feature_id_colname = args$feature_id_colname,
samples_to_include = parse_optional_vector(args$samples_to_include),
covariates_colnames = parse_vector_with_default(args$covariates_colnames, "Group"),
contrast_colname = args$contrast_colname,
contrasts = parse_optional_vector(args$contrasts),
input_in_log_counts = args$input_in_log_counts,
return_mean_and_sd = args$return_mean_and_sd,
voom_normalization_method = args$voom_normalization_method
) |>
write_rds(file.path(getOption("moo_plots_dir"), "..", "moo", "moo.rds"))
moo |>
diff_counts(
count_type = args$count_type,
sub_count_type = args$sub_count_type,
sample_id_colname = args$sample_id_colname,
feature_id_colname = args$feature_id_colname,
samples_to_include = parse_optional_vector(args$samples_to_include),
covariates_colnames = parse_vector_with_default(
args$covariates_colnames,
"Group"
),
contrast_colname = args$contrast_colname,
contrasts = parse_optional_vector(args$contrasts),
input_in_log_counts = args$input_in_log_counts,
return_mean_and_sd = args$return_mean_and_sd,
voom_normalization_method = args$voom_normalization_method
) |>
write_rds(file.path(getOption("moo_plots_dir"), "..", "moo", "moo-diff.rds"))
Binary file added tests/data/moo-filt.rds
Binary file not shown.
2 changes: 2 additions & 0 deletions tests/testthat.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
library(testthat)
test_dir(file.path('tests', 'testthat'), stop_on_failure = TRUE)
146 changes: 146 additions & 0 deletions tests/testthat/test-main.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
test_that("code/run executes successfully with default CLI arguments", {
# Create temporary workspace
workspace <- tempfile("mosuite_diff_counts_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-filt.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",
"--covariates_colnames=Group",
"--contrast_colname=Group",
"--contrasts=B-A"
)
)

# 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.rds")),
info = "Output file moo-diff.rds should be created"
)

# Validate output is a valid MOO object
moo <- readr::read_rds(file.path(results_dir, "moo", "moo-diff.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_diff_counts_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-filt.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",
"--count_type=filt",
"--covariates_colnames=Group",
"--contrast_colname=Group",
"--contrasts=C-A",
"--input_in_log_counts=FALSE",
"--return_mean_and_sd=TRUE",
"--voom_normalization_method=quantile"
)
)

# 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.rds")),
info = "Output file moo-diff.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.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"
)
})