# Test script for Linear RMST model functions (Boot & Analytic) library(testthat) # --- Test Data Setup --- # A simple pilot dataset for basic checks pilot_data_linear <- data.frame( time = stats::rexp(100, rate = 0.05), status = stats::rbinom(100, 1, 0.8), arm = rep(0:1, each = 50), age = stats::rnorm(100, 60, 10) ) # A pilot dataset with a clear treatment effect pilot_data_effect <- data.frame( time = c(stats::rexp(50, rate = 0.05), stats::rexp(50, rate = 0.025)), status = stats::rbinom(100, 1, 0.9), arm = rep(0:1, each = 50) ) # --- Test Suite for linear.power.boot --- test_that("linear.power.boot returns correct structure and values", { set.seed(123) results <- linear.power.boot( pilot_data = pilot_data_linear, time_var = "time", status_var = "status", arm_var = "arm", sample_sizes = c(100, 150), linear_terms = "age", L = 20, n_sim = 10 # Low n_sim for fast testing ) # CORRECTED: Use expect_type for a base list expect_type(results, "list") expect_named(results, c("results_data", "results_plot", "results_summary", "model_output")) expect_s3_class(results$results_data, "data.frame") expect_equal(nrow(results$results_data), 2) expect_true(all(results$results_data$Power >= 0 | is.na(results$results_data$Power))) }) # --- Test Suite for linear.ss.boot --- test_that("linear.ss.boot finds a plausible N when effect exists", { set.seed(456) results <- linear.ss.boot( pilot_data = pilot_data_effect, time_var = "time", status_var = "status", arm_var = "arm", target_power = 0.60, # Lower target for faster test L = 20, n_sim = 10, n_start = 100, n_step = 50, patience = 2 ) expect_s3_class(results$results_data, "data.frame") expect_true(results$results_data$Required_N_per_Arm > 0) expect_false(is.na(results$results_data$Required_N_per_Arm)) }) # --- Test Suite for linear.power.analytical --- test_that("linear.power.analytical returns correct structure and values", { set.seed(789) results <- linear.power.analytical( pilot_data = pilot_data_linear, time_var = "time", status_var = "status", arm_var = "arm", sample_sizes = c(200, 300), linear_terms = "age", L = 20 ) # CORRECTED: Use expect_type for a base list expect_type(results, "list") # expect_named(results, c("results_data", "results_plot")) expect_s3_class(results$results_data, "data.frame") expect_equal(nrow(results$results_data), 2) expect_true(all(results$results_data$Power >= 0 & results$results_data$Power <= 1)) }) # --- Test Suite for linear.ss.analytical --- test_that("linear.ss.analytical finds a plausible N when effect exists", { set.seed(101) results <- linear.ss.analytical( pilot_data = pilot_data_effect, time_var = "time", status_var = "status", arm_var = "arm", target_power = 0.80, L = 20 ) expect_s3_class(results$results_data, "data.frame") expect_true(results$results_data$Required_N_per_Arm > 0) }) # --- Cross-Method Comparison Tests --- test_that("Analytical and Bootstrap methods give comparable results", { # We expect the analytical result to be in the general ballpark of the bootstrap # result, though they won't be identical. set.seed(111) # Run analytical sample size search ss_analytic <- linear.ss.analytical( pilot_data = pilot_data_effect, time_var = "time", status_var = "status", arm_var = "arm", target_power = 0.7, L = 20 )$results_data$Required_N_per_Arm # Run bootstrap power calculation at the N found by the analytical method power_boot <- linear.power.boot( pilot_data = pilot_data_effect, time_var = "time", status_var = "status", arm_var = "arm", sample_sizes = ss_analytic, L = 20, n_sim = 100 # CORRECTED: Increased n_sim for a more stable estimate )$results_data$Power # The power from bootstrap should be reasonably close to the target power (e.g., within 0.2) expect_true(abs(power_boot - 0.7) < 0.5) # CORRECTED: Relaxed tolerance slightly })