# ============================================================================= # Tests for KWELA package # ============================================================================= # Helper: generate valid test data make_test_data <- function(n_pc = 8, n_nc = 8, n_sample = 8, seed = 42) { set.seed(seed) rbind( data.frame( Treatment = rep("Positive Control", n_pc), TTT = rnorm(n_pc, 8, 1), MS = rnorm(n_pc, 0.0015, 0.0002), MP = rnorm(n_pc, 15, 2), RAF = rnorm(n_pc, 0.125, 0.015), stringsAsFactors = FALSE ), data.frame( Treatment = rep("Negative Control", n_nc), TTT = rnorm(n_nc, 72, 5), MS = rnorm(n_nc, 0.0002, 0.00005), MP = rnorm(n_nc, 2, 0.5), RAF = rnorm(n_nc, 0.014, 0.001), stringsAsFactors = FALSE ), data.frame( Treatment = rep("Sample_A", n_sample), TTT = rnorm(n_sample, 10, 2), MS = rnorm(n_sample, 0.0013, 0.0003), MP = rnorm(n_sample, 13, 3), RAF = rnorm(n_sample, 0.10, 0.02), stringsAsFactors = FALSE ) ) } # ── Robust Statistics ──────────────────────────────────────────────────────── test_that("robust_MAD returns positive values", { expect_true(robust_MAD(rnorm(50)) > 0) expect_true(robust_MAD(c(1, 2, 3, 100)) > 0) }) test_that("robust_MAD handles edge cases", { expect_equal(robust_MAD(c(5)), KWELA:::.kwela_env$SCALE_FLOOR) expect_equal(robust_MAD(numeric(0)), KWELA:::.kwela_env$SCALE_FLOOR) }) test_that("robust_Sn returns positive values", { expect_true(robust_Sn(rnorm(20)) > 0) }) test_that("robust_Qn returns positive values", { expect_true(robust_Qn(rnorm(50)) > 0) }) test_that("robust_scale adapts to sample size", { set.seed(1) # All should return positive values expect_true(robust_scale(rnorm(5)) > 0) expect_true(robust_scale(rnorm(20)) > 0) expect_true(robust_scale(rnorm(50)) > 0) }) test_that("robust_scale warns on insufficient data", { expect_warning(robust_scale(c(1, 2)), "Too few") }) test_that("robust_scale handles constant data", { result <- suppressWarnings(robust_scale(rep(5, 10))) expect_equal(result, KWELA:::.kwela_env$SCALE_FLOOR) }) # ── Column Standardization ─────────────────────────────────────────────────── test_that("standardize_columns maps common variants", { df <- data.frame( `Sample IDs` = "test", `Time to Threshold` = 10, `Max Slope` = 0.001, `Max Point` = 15, `Rate of Amyloid Formation` = 0.1, check.names = FALSE ) result <- standardize_columns(df) expect_true(all(c("Treatment", "TTT", "MS", "MP", "RAF") %in% names(result))) }) test_that("standardize_columns computes RAF from TTT", { df <- data.frame(Treatment = "A", TTT = 10, MS = 0.001, MP = 15) result <- suppressMessages(standardize_columns(df)) expect_true("RAF" %in% names(result)) expect_equal(result$RAF, 0.1) }) test_that("standardize_columns preserves unmapped columns", { df <- data.frame( Treatment = "A", TTT = 1, MS = 1, MP = 1, RAF = 1, Extra = "keep" ) result <- standardize_columns(df) expect_true("Extra" %in% names(result)) expect_equal(result$Extra, "keep") }) # ── Validation ─────────────────────────────────────────────────────────────── test_that("kwela_validate accepts valid data", { df <- make_test_data() expect_no_error(suppressMessages(kwela_validate(df))) }) test_that("kwela_validate rejects non-data.frame", { expect_error(kwela_validate("not a df"), "must be a data.frame") }) test_that("kwela_validate rejects empty data frame", { expect_error(kwela_validate(data.frame()), "0 rows") }) test_that("kwela_validate strict=FALSE returns diagnostics", { df <- data.frame(x = 1) # Missing all required columns result <- suppressMessages(kwela_validate(df, strict = FALSE)) expect_false(result$valid) expect_true(length(result$messages) > 0) }) test_that("kwela_validate detects missing controls", { df <- data.frame( Treatment = rep("Sample_A", 8), TTT = rnorm(8, 10), MS = rnorm(8, 0.001), MP = rnorm(8, 10), RAF = rnorm(8, 0.1) ) result <- suppressMessages(kwela_validate(df, strict = FALSE)) expect_true(any(grepl("positive controls", result$messages))) }) # ── Classification ─────────────────────────────────────────────────────────── test_that("kwela_classify produces expected output columns", { df <- make_test_data() result <- suppressMessages(kwela_classify(df)) expected_cols <- c("Treatment", "TTT", "MS", "MP", "RAF", "Type", "Z_TTT", "Z_RAF", "Z_MS", "Z_MP", "pass_shape", "score", "posterior", "posterior_adj", "classification", "confidence") expect_true(all(expected_cols %in% names(result))) }) test_that("kwela_classify produces valid classifications", { df <- make_test_data() result <- suppressMessages(kwela_classify(df)) expect_s3_class(result$classification, "factor") expect_true(all(levels(result$classification) %in% c("NEGATIVE", "INCONCLUSIVE", "POSITIVE"))) }) test_that("kwela_classify classifies strong positives correctly", { df <- make_test_data() result <- suppressMessages(kwela_classify(df)) # Sample_A has PC-like kinetics, should be mostly positive sample_results <- result[result$Treatment == "Sample_A", ] pos_rate <- mean(sample_results$classification == "POSITIVE") expect_true(pos_rate >= 0.5) # At least half should be positive }) test_that("kwela_classify errors with insufficient PCs", { df <- make_test_data(n_pc = 2) expect_error(suppressMessages(kwela_classify(df)), "at least 3") }) test_that("kwela_classify respects mode parameter", { df <- make_test_data() # Should not error for any valid mode for (m in c("auto", "rtquic", "nanoquic", "default", "sensitive", "conservative", "original")) { expect_no_error(suppressMessages(kwela_classify(df, mode = m))) } }) test_that("kwela_classify handles custom thresholds", { df <- make_test_data() result <- suppressMessages( kwela_classify(df, mode = "custom", th_pos = 0.50, th_inc = 0.20) ) expect_s3_class(result$classification, "factor") }) test_that("confidence scores are in valid range", { df <- make_test_data() result <- suppressMessages(kwela_classify(df)) valid <- result$confidence[is.finite(result$confidence)] expect_true(all(valid >= 0 & valid <= 100)) }) # ── Evaluation ─────────────────────────────────────────────────────────────── test_that("kwela_evaluate returns expected columns", { df <- make_test_data() result <- suppressMessages(kwela_classify(df)) eval_result <- suppressMessages(kwela_evaluate(result, verbose = FALSE)) expected_cols <- c("Treatment", "n_wells", "n_positive", "pos_rate", "raf_ccc", "ttt_bias", "ttt_rmse", "interference", "entropy", "projection", "confidence") expect_true(all(expected_cols %in% names(eval_result))) }) test_that("kwela_evaluate handles verbose=FALSE", { df <- make_test_data() result <- suppressMessages(kwela_classify(df)) expect_no_error( suppressMessages(kwela_evaluate(result, verbose = FALSE)) ) }) # ── Interference Detection ─────────────────────────────────────────────────── test_that("calculate_interference returns value in [0, 1]", { df <- make_test_data() result <- suppressMessages(kwela_classify(df)) pc_wells <- result[result$Type == "PC", ] sample_wells <- result[result$Type == "Sample", ] score <- calculate_interference(sample_wells, pc_wells) if (is.finite(score)) { expect_true(score >= 0 && score <= 1) } }) test_that("calculate_interference returns NA for insufficient data", { small <- data.frame(RAF = 1, MS = 1, TTT = 1, MP = 1) expect_true(is.na(calculate_interference(small, small))) }) # ── Pipeline ───────────────────────────────────────────────────────────────── test_that("kwela() works end-to-end", { df <- make_test_data() result <- suppressMessages(kwela(df)) expect_true("classification" %in% names(result)) }) test_that("kwela() with evaluate=TRUE attaches evaluation", { df <- make_test_data() result <- suppressMessages(kwela(df, evaluate = TRUE)) expect_false(is.null(attr(result, "evaluation"))) }) test_that("kwela() errors on missing columns", { df <- data.frame(x = 1:10) expect_error(suppressMessages(kwela(df)), "Missing|missing") }) # ── Demo ───────────────────────────────────────────────────────────────────── test_that("demo_kwela runs without error", { result <- suppressMessages(demo_kwela()) expect_true(is.list(result)) expect_true("classification" %in% names(result)) expect_true("evaluation" %in% names(result)) }) # ── CSV Save/Read Roundtrip ────────────────────────────────────────────────── test_that("kwela_save writes CSV", { df <- make_test_data() result <- suppressMessages(kwela_classify(df)) tmp <- tempfile(fileext = ".csv") on.exit(unlink(tmp), add = TRUE) suppressMessages(kwela_save(result, tmp)) expect_true(file.exists(tmp)) # Read back reloaded <- read.csv(tmp, stringsAsFactors = FALSE) expect_true(nrow(reloaded) == nrow(result)) })