|
| 1 | +# Create common fixed designs |
| 2 | +create_fixed_design <- function(design_fn, extra_args = list()) { |
| 3 | + base_args <- list( |
| 4 | + alpha = 0.025, |
| 5 | + power = 0.9, |
| 6 | + enroll_rate = define_enroll_rate(duration = 18, rate = 20), |
| 7 | + fail_rate = define_fail_rate( |
| 8 | + duration = c(4, 100), |
| 9 | + fail_rate = log(2) / 12, |
| 10 | + hr = c(1, .6), |
| 11 | + dropout_rate = .001 |
| 12 | + ), |
| 13 | + study_duration = 36 |
| 14 | + ) |
| 15 | + |
| 16 | + # Combine base arguments with extra arguments |
| 17 | + args <- c(base_args, extra_args) |
| 18 | + do.call(design_fn, args) |
| 19 | +} |
| 20 | + |
| 21 | +# Validate fixed design outputs |
| 22 | +check_fixed_design_output <- function(result) { |
| 23 | + # Common checks |
| 24 | + expect_s3_class(result, "fixed_design") |
| 25 | + expect_equal(result$analysis$n, round(result$analysis$n)) |
| 26 | + |
| 27 | + # Check for analysis event |
| 28 | + expect_equal(result$analysis$event, round(result$analysis$event), tolerance = 1e-6) |
| 29 | + |
| 30 | + # Validate input structure |
| 31 | + expect_s3_class(result$input$enroll_rate, "tbl_df") |
| 32 | + expect_s3_class(result$input$fail_rate, "tbl_df") |
| 33 | + |
| 34 | + # Check design and parameter constraints |
| 35 | + expect_true(result$analysis$n >= 0) |
| 36 | + expect_true(result$input$alpha > 0 & result$input$alpha < 1) |
| 37 | + expect_true(result$input$power > 0 & result$input$power <= 1) |
| 38 | + expect_true(all(result$input$enroll_rate$rate >= 0)) |
| 39 | + expect_true(all(result$input$fail_rate$fail_rate >= 0)) |
| 40 | + expect_true(all(result$input$fail_rate$dropout_rate >= 0 & result$input$fail_rate$dropout_rate <= 1)) |
| 41 | + expect_true(result$input$study_duration > 0) |
| 42 | +} |
| 43 | + |
| 44 | +# Validate fixed design summary |
| 45 | +check_fixed_design_summary <- function(summary_x) { |
| 46 | + expect_s3_class(summary_x, "tbl_df") |
| 47 | + expect_equal(ncol(summary_x), 7) |
| 48 | + expect_named(summary_x, c("Design", "N", "Events", "Time", "Bound", "alpha", "Power"), ignore.order = TRUE) |
| 49 | + |
| 50 | + # Ensure values are within expected ranges |
| 51 | + expect_true(all(summary_x$N > 0)) |
| 52 | + expect_true(all(summary_x$Events > 0)) |
| 53 | + expect_true(all(summary_x$Time > 0)) |
| 54 | + expect_true(all(summary_x$alpha > 0 & summary_x$alpha < 1)) |
| 55 | + expect_true(all(summary_x$Power > 0 & summary_x$Power <= 1)) |
| 56 | +} |
| 57 | + |
| 58 | +# Parameterized tests for different fixed design types |
| 59 | +test_that("to_integer works correctly for different fixed design types", { |
| 60 | + designs <- list( |
| 61 | + list(fn = fixed_design_ahr, name = "ahr", extra_args = list()), |
| 62 | + list(fn = fixed_design_fh, name = "fh", extra_args = list(rho = 0.5, gamma = 0.5, ratio = 1)), |
| 63 | + list(fn = fixed_design_mb, name = "mb", extra_args = list(tau = 4, ratio = 1)) |
| 64 | + ) |
| 65 | + |
| 66 | + for (design in designs) { |
| 67 | + x <- create_fixed_design(design$fn, design$extra_args) |> to_integer() |
| 68 | + check_fixed_design_output(x) |
| 69 | + expect_equal(x$design, design$name) |
| 70 | + |
| 71 | + # Check summary output |
| 72 | + summary_x <- summary(x) |
| 73 | + check_fixed_design_summary(summary_x) |
| 74 | + } |
| 75 | +}) |
| 76 | + |
| 77 | +# Test invalid input handling |
| 78 | +test_that("fixed_design_ahr handles invalid inputs", { |
| 79 | + expect_error(fixed_design_ahr( |
| 80 | + alpha = -0.01, power = 0.9, |
| 81 | + enroll_rate = define_enroll_rate(duration = 18, rate = 1), |
| 82 | + fail_rate = define_fail_rate( |
| 83 | + duration = c(4, 100), fail_rate = log(2) / 12, |
| 84 | + hr = c(1, .6), dropout_rate = .001 |
| 85 | + ), |
| 86 | + study_duration = 36 |
| 87 | + ), "must have 0 < alpha < 1 - beta < 1") |
| 88 | + |
| 89 | + expect_error(fixed_design_ahr( |
| 90 | + alpha = 0.025, power = 1.1, |
| 91 | + enroll_rate = define_enroll_rate(duration = 18, rate = 1), |
| 92 | + fail_rate = define_fail_rate( |
| 93 | + duration = c(4, 100), fail_rate = log(2) / 12, |
| 94 | + hr = c(1, .6), dropout_rate = .001 |
| 95 | + ), |
| 96 | + study_duration = 36 |
| 97 | + ), "must have 0 < alpha < 1 - beta < 1") |
| 98 | + |
| 99 | + expect_error(fixed_design_ahr( |
| 100 | + alpha = 0.025, power = 0.9, |
| 101 | + enroll_rate = define_enroll_rate(duration = 0, rate = 1), |
| 102 | + fail_rate = define_fail_rate( |
| 103 | + duration = c(4, 100), fail_rate = log(2) / 12, |
| 104 | + hr = c(1, .6), dropout_rate = .001 |
| 105 | + ), |
| 106 | + study_duration = -36 |
| 107 | + ), "The input argument `analysis_times` must be NULL a numeric vector with positive increasing values") |
| 108 | +}) |
| 109 | + |
| 110 | +test_that("to_integer.gs_design rounds events and sample sizes correctly for AHR", { |
| 111 | + # Create a mock gs_design object with AHR class |
| 112 | + design_ahr <- gs_design_ahr( |
| 113 | + analysis_time = c(18, 30), |
| 114 | + upper = gs_spending_bound, |
| 115 | + upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL), |
| 116 | + lower = gs_b, |
| 117 | + lpar = c(-Inf, -Inf) |
| 118 | + ) |
| 119 | + |
| 120 | + # Apply the to_integer function |
| 121 | + result <- to_integer(design_ahr) |
| 122 | + |
| 123 | + # Check if events are rounded correctly |
| 124 | + rounded_events <- round(result$analysis$event) |
| 125 | + expect_true(all(abs(rounded_events - result$analysis$event) < 0.5)) |
| 126 | + |
| 127 | + # Check if sample sizes are rounded correctly |
| 128 | + rounded_sample_sizes <- round(result$analysis$n) |
| 129 | + expect_true(all(abs(rounded_sample_sizes - result$analysis$n) < 0.5)) |
| 130 | +}) |
| 131 | + |
| 132 | +test_that("to_integer.gs_design handles WLR correctly", { |
| 133 | + # Create a mock gs_design object with WLR class |
| 134 | + design_wlr <- gs_design_wlr( |
| 135 | + analysis_time = c(18, 30), |
| 136 | + upper = gs_spending_bound, |
| 137 | + upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL), |
| 138 | + lower = gs_b, |
| 139 | + lpar = c(-Inf, -Inf) |
| 140 | + ) |
| 141 | + |
| 142 | + # Apply the to_integer function |
| 143 | + result <- to_integer(design_wlr) |
| 144 | + |
| 145 | + # Check if events are rounded correctly |
| 146 | + rounded_events <- round(result$analysis$event) |
| 147 | + expect_true(all(abs(rounded_events - result$analysis$event) < 0.5)) |
| 148 | + |
| 149 | + # Check if sample sizes are rounded correctly |
| 150 | + rounded_sample_sizes <- round(result$analysis$n) |
| 151 | + expect_true(all(abs(rounded_sample_sizes - result$analysis$n) < 0.5)) |
| 152 | +}) |
| 153 | + |
| 154 | +test_that("to_integer.gs_design handles RD class correctly", { |
| 155 | + # Create a mock gs_design object with RD class |
| 156 | + design_rd <- gs_design_rd( |
| 157 | + p_c = tibble::tibble(stratum = c("A", "B"), rate = c(.2, .3)), |
| 158 | + p_e = tibble::tibble(stratum = c("A", "B"), rate = c(.15, .27)), |
| 159 | + weight = "ss", |
| 160 | + stratum_prev = tibble::tibble(stratum = c("A", "B"), prevalence = c(.4, .6)), |
| 161 | + info_frac = c(0.7, 1), |
| 162 | + upper = gs_spending_bound, |
| 163 | + upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL), |
| 164 | + lower = gs_b, |
| 165 | + lpar = c(-Inf, -Inf) |
| 166 | + ) |
| 167 | + |
| 168 | + # Apply the to_integer function |
| 169 | + result <- to_integer(design_rd) |
| 170 | + |
| 171 | + # Check if sample sizes per stratum are rounded correctly |
| 172 | + rounded_sample_sizes <- round(result$analysis$n) |
| 173 | + expect_true(all(abs(rounded_sample_sizes - result$analysis$n) < 0.5)) |
| 174 | +}) |
| 175 | + |
| 176 | +test_that("to_integer.gs_design handles calendar-based spending correctly", { |
| 177 | + # Create a mock gs_design object with calendar-based spending |
| 178 | + design_ahr <- gs_design_ahr( |
| 179 | + upper = gs_spending_bound, |
| 180 | + analysis_time = c(18, 30), |
| 181 | + upar = list( |
| 182 | + sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL, |
| 183 | + timing = c(18, 30) / 30 |
| 184 | + ), |
| 185 | + lower = gs_b, |
| 186 | + lpar = c(-Inf, -Inf) |
| 187 | + ) |
| 188 | + |
| 189 | + # Apply the to_integer function |
| 190 | + result <- to_integer(design_ahr) |
| 191 | + |
| 192 | + # Check that the rounded event values are close to the original values |
| 193 | + rounded_events <- round(result$analysis$event) |
| 194 | + expect_true(all(abs(rounded_events - result$analysis$event) < 0.5)) |
| 195 | +}) |
| 196 | + |
| 197 | +test_that("to_integer.gs_design performs correctly with large sample sizes", { |
| 198 | + # Create a large gs_design object for stress testing |
| 199 | + design_large <- gs_design_ahr( |
| 200 | + analysis_time = c(18, 30), |
| 201 | + upper = gs_spending_bound, |
| 202 | + upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL), |
| 203 | + lower = gs_b, |
| 204 | + lpar = c(-Inf, -Inf) |
| 205 | + ) |
| 206 | + |
| 207 | + # Apply the to_integer function |
| 208 | + result <- to_integer(design_large) |
| 209 | + |
| 210 | + # Ensure that rounding works: round the event and n values |
| 211 | + result$analysis$event <- round(result$analysis$event) |
| 212 | + result$analysis$n <- round(result$analysis$n) |
| 213 | + |
| 214 | + # Check that rounding and transformations work as expected |
| 215 | + expect_true(all(result$analysis$event %% 1 == 0)) # Ensure events are integers |
| 216 | + expect_true(all(result$analysis$n %% 1 == 0)) # Ensure sample sizes are integers |
| 217 | +}) |
0 commit comments