From c6c3e1d91784260c336c96c3a10ffc6787815a64 Mon Sep 17 00:00:00 2001 From: Utkarsh Date: Sat, 28 Mar 2026 16:47:59 +0530 Subject: [PATCH 1/6] Add dedicated edge-case tests for exported _data() functions --- .../testthat/test-data-functions-edge-cases.R | 505 ++++++++++++++++++ 1 file changed, 505 insertions(+) create mode 100644 tests/testthat/test-data-functions-edge-cases.R diff --git a/tests/testthat/test-data-functions-edge-cases.R b/tests/testthat/test-data-functions-edge-cases.R new file mode 100644 index 00000000..cce97fc2 --- /dev/null +++ b/tests/testthat/test-data-functions-edge-cases.R @@ -0,0 +1,505 @@ +source(test_path("data-for-ppc-tests.R")) +load(test_path("data-for-ordinal.rda")) + +# ppc_bars_data edge cases ------------------------------------------------- + +test_that("ppc_bars_data returns correct structure and types", { + d <- ppc_bars_data(y_ord, yrep_ord) + expect_s3_class(d, "data.frame") + expect_named(d, c("x", "y_obs", "l", "m", "h")) + expect_type(d$x, "double") + expect_type(d$y_obs, "integer") + expect_type(d$l, "double") + expect_type(d$m, "double") + expect_type(d$h, "double") +}) + +test_that("ppc_bars_data works with single observation", { + y1 <- 2L + yrep1 <- matrix(c(1L, 2L, 3L, 2L, 2L), ncol = 1) + d <- ppc_bars_data(y1, yrep1) + expect_s3_class(d, "data.frame") + expect_true(all(c("x", "y_obs") %in% names(d))) + expect_equal(d$y_obs[d$x == 2], 1) +}) + +test_that("ppc_bars_data works with single draw", { + y_single <- c(1L, 2L, 3L, 2L) + yrep_single <- matrix(c(1L, 2L, 2L, 3L), nrow = 1) + d <- ppc_bars_data(y_single, yrep_single) + expect_s3_class(d, "data.frame") + # with single draw, l == m == h + expect_equal(d$l, d$m, ignore_attr = TRUE) + expect_equal(d$m, d$h, ignore_attr = TRUE) +}) + +test_that("ppc_bars_data freq = FALSE returns proportions", { + d <- ppc_bars_data(y_ord, yrep_ord, freq = FALSE) + expect_true(all(d$y_obs <= 1)) + expect_true(all(d$y_obs >= 0)) +}) + +test_that("ppc_bars_data with group adds group column", { + d <- ppc_bars_data(y_ord, yrep_ord, group = group_ord) + expect_true("group" %in% names(d)) + expect_s3_class(d$group, "factor") +}) + +test_that("ppc_bars_data prob = 0 collapses interval to median", { + d <- ppc_bars_data(y_ord, yrep_ord, prob = 0) + expect_equal(d$l, d$m, ignore_attr = TRUE) + expect_equal(d$m, d$h, ignore_attr = TRUE) +}) + +test_that("ppc_bars_data errors on NA in y", { + y_na <- y_ord + y_na[1] <- NA + expect_error(ppc_bars_data(y_na, yrep_ord)) +}) + +test_that("ppc_bars_data errors on NA in yrep", { + yrep_na <- yrep_ord + yrep_na[1, 1] <- NA + expect_error(ppc_bars_data(y_ord, yrep_na)) +}) + +test_that("ppc_bars_data errors on non-discrete inputs", { + expect_error(ppc_bars_data(y_ord + 0.5, yrep_ord), + "ppc_bars expects 'y' to be discrete") + expect_error(ppc_bars_data(y_ord, yrep_ord + 0.5), + "ppc_bars expects 'yrep' to be discrete") +}) + +test_that("ppc_bars_data errors on zero-length input", { + expect_error(ppc_bars_data(integer(0), matrix(integer(0), nrow = 5, ncol = 0))) +}) + + +# ppc_error_data edge cases ------------------------------------------------ + +test_that("ppc_error_data works with single observation", { + skip_if_not_installed("rstantools") + y1 <- 5 + yrep1 <- matrix(c(4, 6, 5), ncol = 1) + d <- ppc_error_data(y1, yrep1) + expect_named(d, c("y_id", "y_name", "y_obs", "rep_id", "rep_label", "value")) + expect_equal(nrow(d), 3) + expect_equal(d$value, y1 - yrep1[, 1]) + expect_true(all(d$y_obs == 5)) +}) + +test_that("ppc_error_data works with single draw", { + skip_if_not_installed("rstantools") + d <- ppc_error_data(y, yrep[1, , drop = FALSE]) + expect_equal(nrow(d), length(y)) + expect_true(all(d$rep_id == 1)) + expect_equal(d$value, y - yrep[1, ]) +}) + +test_that("ppc_error_data preserves observation names", { + skip_if_not_installed("rstantools") + y_named <- c(a = 1, b = 2, c = 3) + yrep_named <- matrix(c(1.1, 2.1, 3.1), ncol = 3) + colnames(yrep_named) <- c("a", "b", "c") + d <- ppc_error_data(y_named, yrep_named) + expect_equal(as.character(d$y_name), c("a", "b", "c")) +}) + +test_that("ppc_error_data errors on NA in y", { + skip_if_not_installed("rstantools") + y_na <- y + y_na[1] <- NA + expect_error(ppc_error_data(y_na, yrep)) +}) + +test_that("ppc_error_data errors on NA in yrep", { + skip_if_not_installed("rstantools") + yrep_na <- yrep + yrep_na[1, 1] <- NA + expect_error(ppc_error_data(y, yrep_na)) +}) + +test_that("ppc_error_data errors on dimension mismatch", { + skip_if_not_installed("rstantools") + expect_error(ppc_error_data(y, yrep[, 1:5]), + "ncol(yrep) must be equal to length(y)", fixed = TRUE) +}) + +test_that("ppc_error_data returns zero-row data frame for zero-length input", { + skip_if_not_installed("rstantools") + d <- ppc_error_data(numeric(0), matrix(numeric(0), nrow = 1, ncol = 0)) + expect_s3_class(d, "data.frame") + expect_equal(nrow(d), 0) + expect_named(d, c("y_id", "y_name", "y_obs", "rep_id", "rep_label", "value")) +}) + + +# ppc_scatter_data edge cases ---------------------------------------------- + +test_that("ppc_scatter_data returns correct structure", { + d <- ppc_scatter_data(y, yrep) + expect_s3_class(d, "data.frame") + expect_named(d, c("y_id", "y_name", "y_obs", "rep_id", "rep_label", "value")) + expect_equal(nrow(d), length(y) * nrow(yrep)) +}) + +test_that("ppc_scatter_data works with single observation", { + y1 <- 5 + yrep1 <- matrix(c(4, 6, 5), ncol = 1) + d <- ppc_scatter_data(y1, yrep1) + expect_equal(nrow(d), 3) + expect_true(all(d$y_obs == 5)) + expect_equal(d$value, c(4, 6, 5)) +}) + +test_that("ppc_scatter_data works with single draw", { + d <- ppc_scatter_data(y, yrep[1, , drop = FALSE]) + expect_equal(nrow(d), length(y)) + expect_true(all(d$rep_id == 1)) + expect_equal(d$value, yrep[1, ]) + expect_equal(d$y_obs, y) +}) + +test_that("ppc_scatter_data preserves observation names", { + y_named <- c(a = 1, b = 2) + yrep_named <- matrix(c(1.1, 2.1), ncol = 2) + colnames(yrep_named) <- c("a", "b") + d <- ppc_scatter_data(y_named, yrep_named) + expect_equal(as.character(d$y_name), c("a", "b")) +}) + +test_that("ppc_scatter_data errors on NA in y", { + y_na <- y + y_na[1] <- NA + expect_error(ppc_scatter_data(y_na, yrep)) +}) + +test_that("ppc_scatter_data errors on NA in yrep", { + yrep_na <- yrep + yrep_na[1, 1] <- NA + expect_error(ppc_scatter_data(y, yrep_na)) +}) + +test_that("ppc_scatter_data returns zero-row data frame for zero-length input", { + d <- ppc_scatter_data(numeric(0), matrix(numeric(0), nrow = 1, ncol = 0)) + expect_s3_class(d, "data.frame") + expect_equal(nrow(d), 0) + expect_named(d, c("y_id", "y_name", "y_obs", "rep_id", "rep_label", "value")) +}) + + +# ppc_scatter_avg_data edge cases ------------------------------------------ + +test_that("ppc_scatter_avg_data returns correct structure", { + d <- ppc_scatter_avg_data(y, yrep) + expect_s3_class(d, "data.frame") + expect_named(d, c("y_id", "y_name", "y_obs", "rep_id", "rep_label", "value")) + expect_equal(nrow(d), length(y)) + expect_true(all(is.na(d$rep_id))) +}) + +test_that("ppc_scatter_avg_data with group adds group column", { + d <- ppc_scatter_avg_data(y, yrep, group = group) + expect_true("group" %in% names(d)) + expect_s3_class(d$group, "factor") + expect_equal(nrow(d), length(y)) + expect_equal(as.character(d$group), as.character(group)) +}) + +test_that("ppc_scatter_avg_data works with single observation", { + y1 <- 5 + yrep1 <- matrix(c(4, 6, 5), ncol = 1) + d <- ppc_scatter_avg_data(y1, yrep1) + expect_equal(nrow(d), 1) + expect_equal(d$value, mean(c(4, 6, 5))) + expect_equal(d$y_obs, 5) +}) + +test_that("ppc_scatter_avg_data with custom stat function", { + d <- ppc_scatter_avg_data(y, yrep, stat = "median") + expected <- apply(yrep, 2, median) + expect_equal(d$value, expected) +}) + +test_that("ppc_scatter_avg_data errors on NA in y", { + y_na <- y + y_na[1] <- NA + expect_error(ppc_scatter_avg_data(y_na, yrep)) +}) + +test_that("ppc_scatter_avg_data returns zero-row data frame for zero-length input", { + d <- ppc_scatter_avg_data(numeric(0), matrix(numeric(0), nrow = 1, ncol = 0)) + expect_s3_class(d, "data.frame") + expect_equal(nrow(d), 0) +}) + + +# ppc_loo_pit_data edge cases ---------------------------------------------- + +test_that("ppc_loo_pit_data with pre-computed pit and boundary_correction = FALSE", { + set.seed(42) + pit_vals <- runif(30) + expect_message( + d <- ppc_loo_pit_data(pit = pit_vals, boundary_correction = FALSE, samples = 5), + "pit" + ) + expect_s3_class(d, "data.frame") + y_rows <- d[d$is_y, ] + yrep_rows <- d[!d$is_y, ] + expect_equal(nrow(y_rows), length(pit_vals)) + expect_equal(nrow(yrep_rows), length(pit_vals) * 5) + expect_equal(y_rows$value, pit_vals) + expect_true(all(d$value >= 0 & d$value <= 1)) +}) + +test_that("ppc_loo_pit_data with pre-computed pit and boundary_correction = TRUE", { + set.seed(42) + pit_vals <- runif(30) + grid_len <- 128 + expect_message( + d <- ppc_loo_pit_data( + pit = pit_vals, + boundary_correction = TRUE, + samples = 5, + grid_len = grid_len + ), + "pit" + ) + expect_true("x" %in% names(d)) + y_rows <- d[d$is_y, ] + yrep_rows <- d[!d$is_y, ] + expect_equal(nrow(y_rows), grid_len) + expect_equal(nrow(yrep_rows), grid_len * 5) + expect_false(anyNA(d$x)) +}) + +test_that("ppc_loo_pit_data with single pit value and no boundary correction", { + expect_message( + d <- ppc_loo_pit_data(pit = 0.5, boundary_correction = FALSE, samples = 3), + "pit" + ) + expect_s3_class(d, "data.frame") + y_rows <- d[d$is_y, ] + expect_equal(nrow(y_rows), 1) + expect_equal(y_rows$value, 0.5) +}) + +test_that("ppc_loo_pit_data with custom bw parameter", { + set.seed(42) + pit_vals <- runif(50) + expect_message( + d <- ppc_loo_pit_data( + pit = pit_vals, + boundary_correction = TRUE, + bw = "SJ", + samples = 3, + grid_len = 128 + ), + "pit" + ) + expect_s3_class(d, "data.frame") + expect_true("x" %in% names(d)) +}) + +test_that("ppc_loo_pit_data returns zero-row data frame for zero-length pit", { + expect_message( + d <- ppc_loo_pit_data(pit = numeric(0), boundary_correction = FALSE, samples = 2), + "pit" + ) + expect_s3_class(d, "data.frame") + expect_equal(nrow(d), 0) +}) + +test_that("ppc_loo_pit_data is_y and is_y_label columns are consistent", { + set.seed(42) + pit_vals <- runif(10) + expect_message( + d <- ppc_loo_pit_data(pit = pit_vals, boundary_correction = FALSE, samples = 2), + "pit" + ) + expect_true(all(d$is_y_label[d$is_y] == levels(d$is_y_label)[1])) + expect_true(all(d$is_y_label[!d$is_y] == levels(d$is_y_label)[2])) +}) + + +# ppd_data edge cases ------------------------------------------------------ + +test_that("ppd_data works with single observation (single column)", { + ypred <- matrix(c(1, 2, 3), ncol = 1) + d <- ppd_data(ypred) + expect_s3_class(d, "data.frame") + expect_named(d, c("y_id", "y_name", "rep_id", "rep_label", "value")) + expect_equal(nrow(d), 3) + expect_true(all(d$y_id == 1)) + expect_equal(d$value, c(1, 2, 3)) +}) + +test_that("ppd_data rep_label uses 'pred' not 'rep'", { + ypred <- matrix(1:4, nrow = 2, ncol = 2) + d <- ppd_data(ypred) + expect_true(all(grepl("pred", levels(d$rep_label), fixed = TRUE))) + expect_false(any(grepl("rep", levels(d$rep_label), fixed = TRUE))) +}) + +test_that("ppd_data errors on NA in ypred", { + ypred_na <- matrix(c(1, NA, 3, 4), nrow = 2) + expect_error(ppd_data(ypred_na)) +}) + +test_that("ppd_data errors on non-matrix input", { + expect_error(ppd_data(c(1, 2, 3))) +}) + +test_that("ppd_data with group errors on length mismatch", { + ypred <- matrix(1:4, nrow = 2, ncol = 2) + expect_error(ppd_data(ypred, group = factor(c("a", "b", "c")))) +}) + +test_that("ppd_data returns zero-row data frame for zero-length input", { + d <- ppd_data(matrix(numeric(0), nrow = 1, ncol = 0)) + expect_s3_class(d, "data.frame") + expect_equal(nrow(d), 0) + expect_named(d, c("y_id", "y_name", "rep_id", "rep_label", "value")) +}) + + +# ppd_stat_data edge cases ------------------------------------------------- + +test_that("ppd_stat_data returns correct structure with single stat", { + d <- ppd_stat_data(yrep, stat = "mean") + expect_s3_class(d, "data.frame") + expect_true("value" %in% names(d)) + expect_true("variable" %in% names(d)) + expect_false("group" %in% names(d)) +}) + +test_that("ppd_stat_data returns correct structure with two stats", { + d <- ppd_stat_data(yrep, stat = c("mean", "sd")) + expect_true("value" %in% names(d)) + expect_true("value2" %in% names(d)) +}) + +test_that("ppd_stat_data with group adds group column", { + d <- ppd_stat_data(yrep, group = group, stat = "mean") + expect_true("group" %in% names(d)) + expect_s3_class(d$group, "factor") +}) + +test_that("ppd_stat_data works with single draw", { + yrep_single <- matrix(rnorm(10), nrow = 1) + d <- ppd_stat_data(yrep_single, stat = "mean") + expect_s3_class(d, "data.frame") + expect_equal(nrow(d), 1) +}) + +test_that("ppd_stat_data works with single observation", { + yrep_1obs <- matrix(rnorm(5), ncol = 1) + d <- ppd_stat_data(yrep_1obs, stat = "mean") + expect_s3_class(d, "data.frame") +}) + +test_that("ppd_stat_data errors on stat length > 2", { + expect_error(ppd_stat_data(yrep, stat = c("mean", "sd", "var")), + "'stat' must have length 1 or 2") +}) + +test_that("ppd_stat_data errors on invalid stat function name", { + expect_error(ppd_stat_data(yrep, stat = "not_a_known_function")) +}) + +test_that("ppd_stat_data errors on NA in ypred", { + yrep_na <- yrep + yrep_na[1, 1] <- NA + expect_error(ppd_stat_data(yrep_na, stat = "mean")) +}) + +test_that("ppd_stat_data errors on zero-length input", { + expect_error(ppd_stat_data(matrix(numeric(0), nrow = 1, ncol = 0), stat = "mean")) +}) + + +# ppd_intervals_data edge cases -------------------------------------------- + +test_that("ppd_intervals_data returns correct structure", { + d <- ppd_intervals_data(yrep) + expect_s3_class(d, "data.frame") + expected_cols <- c("y_id", "x", "inner_width", "outer_width", + "ll", "l", "m", "h", "hh") + expect_true(all(expected_cols %in% names(d))) + expect_equal(nrow(d), ncol(yrep)) +}) + +test_that("ppd_intervals_data works with single observation", { + yrep_1obs <- matrix(rnorm(25), ncol = 1) + d <- ppd_intervals_data(yrep_1obs) + expect_equal(nrow(d), 1) + expect_true(d$ll <= d$l) + expect_true(d$l <= d$m) + expect_true(d$m <= d$h) + expect_true(d$h <= d$hh) +}) + +test_that("ppd_intervals_data works with single draw", { + yrep_1draw <- matrix(rnorm(10), nrow = 1) + d <- ppd_intervals_data(yrep_1draw) + expect_equal(nrow(d), 10) + # single draw: all quantiles equal the value + expect_equal(d$ll, d$m) + expect_equal(d$hh, d$m) +}) + +test_that("ppd_intervals_data uses custom x values", { + x_vals <- seq(10, 100, length.out = ncol(yrep)) + d <- ppd_intervals_data(yrep, x = x_vals) + expect_equal(d$x, x_vals) +}) + +test_that("ppd_intervals_data with group adds group column", { + d <- ppd_intervals_data(yrep, group = group) + expect_true("group" %in% names(d)) + expect_s3_class(d$group, "factor") +}) + +test_that("ppd_intervals_data respects prob and prob_outer", { + d <- ppd_intervals_data(yrep, prob = 0.5, prob_outer = 0.9) + expect_equal(unique(d$inner_width), 0.5) + expect_equal(unique(d$outer_width), 0.9) +}) + +test_that("ppd_intervals_data quantile ordering holds", { + d <- ppd_intervals_data(yrep, prob = 0.3, prob_outer = 0.8) + expect_true(all(d$ll <= d$l)) + expect_true(all(d$l <= d$m)) + expect_true(all(d$m <= d$h)) + expect_true(all(d$h <= d$hh)) +}) + +test_that("ppd_intervals_data errors on NA in ypred", { + yrep_na <- yrep + yrep_na[1, 1] <- NA + expect_error(ppd_intervals_data(yrep_na)) +}) + +test_that("ppd_intervals_data errors on invalid prob_outer", { + expect_error(ppd_intervals_data(yrep, prob_outer = 0)) + expect_error(ppd_intervals_data(yrep, prob_outer = 1.01)) +}) + +test_that("ppd_intervals_data returns zero-row data frame for zero-length input", { + d <- ppd_intervals_data(matrix(numeric(0), nrow = 1, ncol = 0)) + expect_s3_class(d, "data.frame") + expect_equal(nrow(d), 0) +}) + + +# ppd_ribbon_data edge cases ----------------------------------------------- + +test_that("ppd_ribbon_data is identical to ppd_intervals_data", { + expect_identical(ppd_ribbon_data, ppd_intervals_data) +}) + +test_that("ppd_ribbon_data returns same result as ppd_intervals_data", { + d1 <- ppd_ribbon_data(yrep, prob = 0.5, prob_outer = 0.9) + d2 <- ppd_intervals_data(yrep, prob = 0.5, prob_outer = 0.9) + expect_identical(d1, d2) +}) From 085bbe7e746a57a1dedfb3c618940c4f1a2fccea Mon Sep 17 00:00:00 2001 From: Utkarsh Date: Sat, 28 Mar 2026 16:53:58 +0530 Subject: [PATCH 2/6] Update NEWS.md with _data() edge-case test entry --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index e7fc9138..6a957b5a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,7 @@ # bayesplot (development version) * Added unit tests for `mcmc_areas_ridges_data()`, `mcmc_parcoord_data()`, and `mcmc_trace_data()`. -* Added unit tests for `ppc_error_data()` and `ppc_loo_pit_data()` covering output structure, argument handling, and edge cases. +* Added dedicated edge-case tests for all exported `_data()` functions: `ppc_bars_data()`, `ppc_error_data()`, `ppc_scatter_data()`, `ppc_scatter_avg_data()`, `ppc_loo_pit_data()`, `ppd_data()`, `ppd_stat_data()`, `ppd_intervals_data()`, `ppd_ribbon_data()`. * Added vignette sections demonstrating `*_data()` companion functions for building custom ggplot2 visualizations (#435) * Extract `drop_singleton_values()` helper in `mcmc_nuts_treedepth()` to remove duplicated filtering logic. * Eliminate redundant data processing in `mcmc_areas_data()` by reusing the prepared MCMC array for both interval and density computation. From 014e16924922cf79d01b29ca0672a728597f8e8a Mon Sep 17 00:00:00 2001 From: Utkarsh Date: Sat, 28 Mar 2026 16:57:14 +0530 Subject: [PATCH 3/6] update news.md --- NEWS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 6a957b5a..c91a2152 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,8 @@ # bayesplot (development version) +* Added dedicated edge-case tests for all exported `_data()` functions. * Added unit tests for `mcmc_areas_ridges_data()`, `mcmc_parcoord_data()`, and `mcmc_trace_data()`. -* Added dedicated edge-case tests for all exported `_data()` functions: `ppc_bars_data()`, `ppc_error_data()`, `ppc_scatter_data()`, `ppc_scatter_avg_data()`, `ppc_loo_pit_data()`, `ppd_data()`, `ppd_stat_data()`, `ppd_intervals_data()`, `ppd_ribbon_data()`. +* Added unit tests for `ppc_error_data()` and `ppc_loo_pit_data()` covering output structure, argument handling, and edge cases. * Added vignette sections demonstrating `*_data()` companion functions for building custom ggplot2 visualizations (#435) * Extract `drop_singleton_values()` helper in `mcmc_nuts_treedepth()` to remove duplicated filtering logic. * Eliminate redundant data processing in `mcmc_areas_data()` by reusing the prepared MCMC array for both interval and density computation. From ed8b058ae7b7082d98eacca652cfb89ac6adea92 Mon Sep 17 00:00:00 2001 From: Utkarsh Date: Sat, 28 Mar 2026 17:16:46 +0530 Subject: [PATCH 4/6] Add edge-case tests for exported _data() functions --- .../testthat/test-data-functions-edge-cases.R | 380 ++---------------- 1 file changed, 36 insertions(+), 344 deletions(-) diff --git a/tests/testthat/test-data-functions-edge-cases.R b/tests/testthat/test-data-functions-edge-cases.R index cce97fc2..c1154e38 100644 --- a/tests/testthat/test-data-functions-edge-cases.R +++ b/tests/testthat/test-data-functions-edge-cases.R @@ -1,48 +1,21 @@ source(test_path("data-for-ppc-tests.R")) load(test_path("data-for-ordinal.rda")) -# ppc_bars_data edge cases ------------------------------------------------- +# ppc_bars_data ------------------------------------------------------------ -test_that("ppc_bars_data returns correct structure and types", { - d <- ppc_bars_data(y_ord, yrep_ord) - expect_s3_class(d, "data.frame") - expect_named(d, c("x", "y_obs", "l", "m", "h")) - expect_type(d$x, "double") - expect_type(d$y_obs, "integer") - expect_type(d$l, "double") - expect_type(d$m, "double") - expect_type(d$h, "double") -}) - -test_that("ppc_bars_data works with single observation", { +test_that("ppc_bars_data handles single observation and single draw", { y1 <- 2L yrep1 <- matrix(c(1L, 2L, 3L, 2L, 2L), ncol = 1) d <- ppc_bars_data(y1, yrep1) expect_s3_class(d, "data.frame") - expect_true(all(c("x", "y_obs") %in% names(d))) expect_equal(d$y_obs[d$x == 2], 1) -}) - -test_that("ppc_bars_data works with single draw", { - y_single <- c(1L, 2L, 3L, 2L) - yrep_single <- matrix(c(1L, 2L, 2L, 3L), nrow = 1) - d <- ppc_bars_data(y_single, yrep_single) - expect_s3_class(d, "data.frame") - # with single draw, l == m == h - expect_equal(d$l, d$m, ignore_attr = TRUE) - expect_equal(d$m, d$h, ignore_attr = TRUE) -}) -test_that("ppc_bars_data freq = FALSE returns proportions", { - d <- ppc_bars_data(y_ord, yrep_ord, freq = FALSE) - expect_true(all(d$y_obs <= 1)) - expect_true(all(d$y_obs >= 0)) -}) - -test_that("ppc_bars_data with group adds group column", { - d <- ppc_bars_data(y_ord, yrep_ord, group = group_ord) - expect_true("group" %in% names(d)) - expect_s3_class(d$group, "factor") + # single draw: interval collapses to a point + y_s <- c(1L, 2L, 3L, 2L) + yrep_s <- matrix(c(1L, 2L, 2L, 3L), nrow = 1) + d2 <- ppc_bars_data(y_s, yrep_s) + expect_equal(d2$l, d2$m, ignore_attr = TRUE) + expect_equal(d2$m, d2$h, ignore_attr = TRUE) }) test_that("ppc_bars_data prob = 0 collapses interval to median", { @@ -51,162 +24,57 @@ test_that("ppc_bars_data prob = 0 collapses interval to median", { expect_equal(d$m, d$h, ignore_attr = TRUE) }) -test_that("ppc_bars_data errors on NA in y", { - y_na <- y_ord - y_na[1] <- NA - expect_error(ppc_bars_data(y_na, yrep_ord)) -}) - -test_that("ppc_bars_data errors on NA in yrep", { - yrep_na <- yrep_ord - yrep_na[1, 1] <- NA - expect_error(ppc_bars_data(y_ord, yrep_na)) -}) - -test_that("ppc_bars_data errors on non-discrete inputs", { - expect_error(ppc_bars_data(y_ord + 0.5, yrep_ord), - "ppc_bars expects 'y' to be discrete") - expect_error(ppc_bars_data(y_ord, yrep_ord + 0.5), - "ppc_bars expects 'yrep' to be discrete") -}) - test_that("ppc_bars_data errors on zero-length input", { expect_error(ppc_bars_data(integer(0), matrix(integer(0), nrow = 5, ncol = 0))) }) -# ppc_error_data edge cases ------------------------------------------------ +# ppc_error_data ----------------------------------------------------------- -test_that("ppc_error_data works with single observation", { +test_that("ppc_error_data handles single observation", { skip_if_not_installed("rstantools") y1 <- 5 yrep1 <- matrix(c(4, 6, 5), ncol = 1) d <- ppc_error_data(y1, yrep1) - expect_named(d, c("y_id", "y_name", "y_obs", "rep_id", "rep_label", "value")) expect_equal(nrow(d), 3) expect_equal(d$value, y1 - yrep1[, 1]) expect_true(all(d$y_obs == 5)) }) -test_that("ppc_error_data works with single draw", { - skip_if_not_installed("rstantools") - d <- ppc_error_data(y, yrep[1, , drop = FALSE]) - expect_equal(nrow(d), length(y)) - expect_true(all(d$rep_id == 1)) - expect_equal(d$value, y - yrep[1, ]) -}) - -test_that("ppc_error_data preserves observation names", { - skip_if_not_installed("rstantools") - y_named <- c(a = 1, b = 2, c = 3) - yrep_named <- matrix(c(1.1, 2.1, 3.1), ncol = 3) - colnames(yrep_named) <- c("a", "b", "c") - d <- ppc_error_data(y_named, yrep_named) - expect_equal(as.character(d$y_name), c("a", "b", "c")) -}) - -test_that("ppc_error_data errors on NA in y", { - skip_if_not_installed("rstantools") - y_na <- y - y_na[1] <- NA - expect_error(ppc_error_data(y_na, yrep)) -}) - -test_that("ppc_error_data errors on NA in yrep", { - skip_if_not_installed("rstantools") - yrep_na <- yrep - yrep_na[1, 1] <- NA - expect_error(ppc_error_data(y, yrep_na)) -}) - -test_that("ppc_error_data errors on dimension mismatch", { - skip_if_not_installed("rstantools") - expect_error(ppc_error_data(y, yrep[, 1:5]), - "ncol(yrep) must be equal to length(y)", fixed = TRUE) -}) - test_that("ppc_error_data returns zero-row data frame for zero-length input", { skip_if_not_installed("rstantools") d <- ppc_error_data(numeric(0), matrix(numeric(0), nrow = 1, ncol = 0)) - expect_s3_class(d, "data.frame") expect_equal(nrow(d), 0) expect_named(d, c("y_id", "y_name", "y_obs", "rep_id", "rep_label", "value")) }) -# ppc_scatter_data edge cases ---------------------------------------------- - -test_that("ppc_scatter_data returns correct structure", { - d <- ppc_scatter_data(y, yrep) - expect_s3_class(d, "data.frame") - expect_named(d, c("y_id", "y_name", "y_obs", "rep_id", "rep_label", "value")) - expect_equal(nrow(d), length(y) * nrow(yrep)) -}) +# ppc_scatter_data --------------------------------------------------------- -test_that("ppc_scatter_data works with single observation", { +test_that("ppc_scatter_data handles single observation and single draw", { y1 <- 5 yrep1 <- matrix(c(4, 6, 5), ncol = 1) d <- ppc_scatter_data(y1, yrep1) expect_equal(nrow(d), 3) expect_true(all(d$y_obs == 5)) expect_equal(d$value, c(4, 6, 5)) -}) - -test_that("ppc_scatter_data works with single draw", { - d <- ppc_scatter_data(y, yrep[1, , drop = FALSE]) - expect_equal(nrow(d), length(y)) - expect_true(all(d$rep_id == 1)) - expect_equal(d$value, yrep[1, ]) - expect_equal(d$y_obs, y) -}) -test_that("ppc_scatter_data preserves observation names", { - y_named <- c(a = 1, b = 2) - yrep_named <- matrix(c(1.1, 2.1), ncol = 2) - colnames(yrep_named) <- c("a", "b") - d <- ppc_scatter_data(y_named, yrep_named) - expect_equal(as.character(d$y_name), c("a", "b")) -}) - -test_that("ppc_scatter_data errors on NA in y", { - y_na <- y - y_na[1] <- NA - expect_error(ppc_scatter_data(y_na, yrep)) -}) - -test_that("ppc_scatter_data errors on NA in yrep", { - yrep_na <- yrep - yrep_na[1, 1] <- NA - expect_error(ppc_scatter_data(y, yrep_na)) + # single draw + d2 <- ppc_scatter_data(y, yrep[1, , drop = FALSE]) + expect_equal(nrow(d2), length(y)) + expect_equal(d2$value, yrep[1, ]) + expect_equal(d2$y_obs, y) }) test_that("ppc_scatter_data returns zero-row data frame for zero-length input", { d <- ppc_scatter_data(numeric(0), matrix(numeric(0), nrow = 1, ncol = 0)) - expect_s3_class(d, "data.frame") expect_equal(nrow(d), 0) - expect_named(d, c("y_id", "y_name", "y_obs", "rep_id", "rep_label", "value")) }) -# ppc_scatter_avg_data edge cases ------------------------------------------ - -test_that("ppc_scatter_avg_data returns correct structure", { - d <- ppc_scatter_avg_data(y, yrep) - expect_s3_class(d, "data.frame") - expect_named(d, c("y_id", "y_name", "y_obs", "rep_id", "rep_label", "value")) - expect_equal(nrow(d), length(y)) - expect_true(all(is.na(d$rep_id))) -}) - -test_that("ppc_scatter_avg_data with group adds group column", { - d <- ppc_scatter_avg_data(y, yrep, group = group) - expect_true("group" %in% names(d)) - expect_s3_class(d$group, "factor") - expect_equal(nrow(d), length(y)) - expect_equal(as.character(d$group), as.character(group)) -}) +# ppc_scatter_avg_data ----------------------------------------------------- -test_that("ppc_scatter_avg_data works with single observation", { +test_that("ppc_scatter_avg_data handles single observation", { y1 <- 5 yrep1 <- matrix(c(4, 6, 5), ncol = 1) d <- ppc_scatter_avg_data(y1, yrep1) @@ -215,76 +83,25 @@ test_that("ppc_scatter_avg_data works with single observation", { expect_equal(d$y_obs, 5) }) -test_that("ppc_scatter_avg_data with custom stat function", { - d <- ppc_scatter_avg_data(y, yrep, stat = "median") - expected <- apply(yrep, 2, median) - expect_equal(d$value, expected) -}) - -test_that("ppc_scatter_avg_data errors on NA in y", { - y_na <- y - y_na[1] <- NA - expect_error(ppc_scatter_avg_data(y_na, yrep)) -}) - test_that("ppc_scatter_avg_data returns zero-row data frame for zero-length input", { d <- ppc_scatter_avg_data(numeric(0), matrix(numeric(0), nrow = 1, ncol = 0)) - expect_s3_class(d, "data.frame") expect_equal(nrow(d), 0) }) -# ppc_loo_pit_data edge cases ---------------------------------------------- +# ppc_loo_pit_data --------------------------------------------------------- -test_that("ppc_loo_pit_data with pre-computed pit and boundary_correction = FALSE", { - set.seed(42) - pit_vals <- runif(30) - expect_message( - d <- ppc_loo_pit_data(pit = pit_vals, boundary_correction = FALSE, samples = 5), - "pit" - ) - expect_s3_class(d, "data.frame") - y_rows <- d[d$is_y, ] - yrep_rows <- d[!d$is_y, ] - expect_equal(nrow(y_rows), length(pit_vals)) - expect_equal(nrow(yrep_rows), length(pit_vals) * 5) - expect_equal(y_rows$value, pit_vals) - expect_true(all(d$value >= 0 & d$value <= 1)) -}) - -test_that("ppc_loo_pit_data with pre-computed pit and boundary_correction = TRUE", { - set.seed(42) - pit_vals <- runif(30) - grid_len <- 128 - expect_message( - d <- ppc_loo_pit_data( - pit = pit_vals, - boundary_correction = TRUE, - samples = 5, - grid_len = grid_len - ), - "pit" - ) - expect_true("x" %in% names(d)) - y_rows <- d[d$is_y, ] - yrep_rows <- d[!d$is_y, ] - expect_equal(nrow(y_rows), grid_len) - expect_equal(nrow(yrep_rows), grid_len * 5) - expect_false(anyNA(d$x)) -}) - -test_that("ppc_loo_pit_data with single pit value and no boundary correction", { +test_that("ppc_loo_pit_data handles single pit value", { expect_message( d <- ppc_loo_pit_data(pit = 0.5, boundary_correction = FALSE, samples = 3), "pit" ) - expect_s3_class(d, "data.frame") y_rows <- d[d$is_y, ] expect_equal(nrow(y_rows), 1) expect_equal(y_rows$value, 0.5) }) -test_that("ppc_loo_pit_data with custom bw parameter", { +test_that("ppc_loo_pit_data works with custom bw parameter", { set.seed(42) pit_vals <- runif(50) expect_message( @@ -297,16 +114,14 @@ test_that("ppc_loo_pit_data with custom bw parameter", { ), "pit" ) - expect_s3_class(d, "data.frame") expect_true("x" %in% names(d)) }) -test_that("ppc_loo_pit_data returns zero-row data frame for zero-length pit", { +test_that("ppc_loo_pit_data handles zero-length pit", { expect_message( d <- ppc_loo_pit_data(pit = numeric(0), boundary_correction = FALSE, samples = 2), "pit" ) - expect_s3_class(d, "data.frame") expect_equal(nrow(d), 0) }) @@ -322,95 +137,32 @@ test_that("ppc_loo_pit_data is_y and is_y_label columns are consistent", { }) -# ppd_data edge cases ------------------------------------------------------ +# ppd_data ----------------------------------------------------------------- -test_that("ppd_data works with single observation (single column)", { +test_that("ppd_data handles single observation (single column)", { ypred <- matrix(c(1, 2, 3), ncol = 1) d <- ppd_data(ypred) - expect_s3_class(d, "data.frame") - expect_named(d, c("y_id", "y_name", "rep_id", "rep_label", "value")) expect_equal(nrow(d), 3) expect_true(all(d$y_id == 1)) expect_equal(d$value, c(1, 2, 3)) }) -test_that("ppd_data rep_label uses 'pred' not 'rep'", { - ypred <- matrix(1:4, nrow = 2, ncol = 2) - d <- ppd_data(ypred) - expect_true(all(grepl("pred", levels(d$rep_label), fixed = TRUE))) - expect_false(any(grepl("rep", levels(d$rep_label), fixed = TRUE))) -}) - -test_that("ppd_data errors on NA in ypred", { - ypred_na <- matrix(c(1, NA, 3, 4), nrow = 2) - expect_error(ppd_data(ypred_na)) -}) - -test_that("ppd_data errors on non-matrix input", { - expect_error(ppd_data(c(1, 2, 3))) -}) - -test_that("ppd_data with group errors on length mismatch", { - ypred <- matrix(1:4, nrow = 2, ncol = 2) - expect_error(ppd_data(ypred, group = factor(c("a", "b", "c")))) -}) - test_that("ppd_data returns zero-row data frame for zero-length input", { d <- ppd_data(matrix(numeric(0), nrow = 1, ncol = 0)) - expect_s3_class(d, "data.frame") expect_equal(nrow(d), 0) - expect_named(d, c("y_id", "y_name", "rep_id", "rep_label", "value")) -}) - - -# ppd_stat_data edge cases ------------------------------------------------- - -test_that("ppd_stat_data returns correct structure with single stat", { - d <- ppd_stat_data(yrep, stat = "mean") - expect_s3_class(d, "data.frame") - expect_true("value" %in% names(d)) - expect_true("variable" %in% names(d)) - expect_false("group" %in% names(d)) }) -test_that("ppd_stat_data returns correct structure with two stats", { - d <- ppd_stat_data(yrep, stat = c("mean", "sd")) - expect_true("value" %in% names(d)) - expect_true("value2" %in% names(d)) -}) -test_that("ppd_stat_data with group adds group column", { - d <- ppd_stat_data(yrep, group = group, stat = "mean") - expect_true("group" %in% names(d)) - expect_s3_class(d$group, "factor") -}) +# ppd_stat_data ------------------------------------------------------------ -test_that("ppd_stat_data works with single draw", { +test_that("ppd_stat_data handles single draw and single observation", { yrep_single <- matrix(rnorm(10), nrow = 1) d <- ppd_stat_data(yrep_single, stat = "mean") - expect_s3_class(d, "data.frame") expect_equal(nrow(d), 1) -}) -test_that("ppd_stat_data works with single observation", { yrep_1obs <- matrix(rnorm(5), ncol = 1) - d <- ppd_stat_data(yrep_1obs, stat = "mean") - expect_s3_class(d, "data.frame") -}) - -test_that("ppd_stat_data errors on stat length > 2", { - expect_error(ppd_stat_data(yrep, stat = c("mean", "sd", "var")), - "'stat' must have length 1 or 2") -}) - -test_that("ppd_stat_data errors on invalid stat function name", { - expect_error(ppd_stat_data(yrep, stat = "not_a_known_function")) -}) - -test_that("ppd_stat_data errors on NA in ypred", { - yrep_na <- yrep - yrep_na[1, 1] <- NA - expect_error(ppd_stat_data(yrep_na, stat = "mean")) + d2 <- ppd_stat_data(yrep_1obs, stat = "mean") + expect_s3_class(d2, "data.frame") }) test_that("ppd_stat_data errors on zero-length input", { @@ -418,34 +170,19 @@ test_that("ppd_stat_data errors on zero-length input", { }) -# ppd_intervals_data edge cases -------------------------------------------- - -test_that("ppd_intervals_data returns correct structure", { - d <- ppd_intervals_data(yrep) - expect_s3_class(d, "data.frame") - expected_cols <- c("y_id", "x", "inner_width", "outer_width", - "ll", "l", "m", "h", "hh") - expect_true(all(expected_cols %in% names(d))) - expect_equal(nrow(d), ncol(yrep)) -}) +# ppd_intervals_data ------------------------------------------------------- -test_that("ppd_intervals_data works with single observation", { +test_that("ppd_intervals_data handles single observation and single draw", { yrep_1obs <- matrix(rnorm(25), ncol = 1) d <- ppd_intervals_data(yrep_1obs) expect_equal(nrow(d), 1) - expect_true(d$ll <= d$l) - expect_true(d$l <= d$m) - expect_true(d$m <= d$h) - expect_true(d$h <= d$hh) -}) + expect_true(d$ll <= d$l && d$l <= d$m && d$m <= d$h && d$h <= d$hh) -test_that("ppd_intervals_data works with single draw", { + # single draw: all quantiles collapse to the value yrep_1draw <- matrix(rnorm(10), nrow = 1) - d <- ppd_intervals_data(yrep_1draw) - expect_equal(nrow(d), 10) - # single draw: all quantiles equal the value - expect_equal(d$ll, d$m) - expect_equal(d$hh, d$m) + d2 <- ppd_intervals_data(yrep_1draw) + expect_equal(d2$ll, d2$m) + expect_equal(d2$hh, d2$m) }) test_that("ppd_intervals_data uses custom x values", { @@ -454,52 +191,7 @@ test_that("ppd_intervals_data uses custom x values", { expect_equal(d$x, x_vals) }) -test_that("ppd_intervals_data with group adds group column", { - d <- ppd_intervals_data(yrep, group = group) - expect_true("group" %in% names(d)) - expect_s3_class(d$group, "factor") -}) - -test_that("ppd_intervals_data respects prob and prob_outer", { - d <- ppd_intervals_data(yrep, prob = 0.5, prob_outer = 0.9) - expect_equal(unique(d$inner_width), 0.5) - expect_equal(unique(d$outer_width), 0.9) -}) - -test_that("ppd_intervals_data quantile ordering holds", { - d <- ppd_intervals_data(yrep, prob = 0.3, prob_outer = 0.8) - expect_true(all(d$ll <= d$l)) - expect_true(all(d$l <= d$m)) - expect_true(all(d$m <= d$h)) - expect_true(all(d$h <= d$hh)) -}) - -test_that("ppd_intervals_data errors on NA in ypred", { - yrep_na <- yrep - yrep_na[1, 1] <- NA - expect_error(ppd_intervals_data(yrep_na)) -}) - -test_that("ppd_intervals_data errors on invalid prob_outer", { - expect_error(ppd_intervals_data(yrep, prob_outer = 0)) - expect_error(ppd_intervals_data(yrep, prob_outer = 1.01)) -}) - test_that("ppd_intervals_data returns zero-row data frame for zero-length input", { d <- ppd_intervals_data(matrix(numeric(0), nrow = 1, ncol = 0)) - expect_s3_class(d, "data.frame") expect_equal(nrow(d), 0) }) - - -# ppd_ribbon_data edge cases ----------------------------------------------- - -test_that("ppd_ribbon_data is identical to ppd_intervals_data", { - expect_identical(ppd_ribbon_data, ppd_intervals_data) -}) - -test_that("ppd_ribbon_data returns same result as ppd_intervals_data", { - d1 <- ppd_ribbon_data(yrep, prob = 0.5, prob_outer = 0.9) - d2 <- ppd_intervals_data(yrep, prob = 0.5, prob_outer = 0.9) - expect_identical(d1, d2) -}) From 5a05326cbf3b5f8a45c75b30ce7d549a9a551680 Mon Sep 17 00:00:00 2001 From: Utkarsh Date: Tue, 31 Mar 2026 11:16:06 +0530 Subject: [PATCH 5/6] Move edge-case tests to existing test files --- NEWS.md | 2 +- .../testthat/test-data-functions-edge-cases.R | 197 ------------------ tests/testthat/test-ppc-discrete.R | 21 ++ tests/testthat/test-ppc-distributions.R | 8 + tests/testthat/test-ppc-errors.R | 9 + tests/testthat/test-ppc-intervals.R | 13 ++ tests/testthat/test-ppc-loo.R | 7 + tests/testthat/test-ppc-scatterplots.R | 23 ++ tests/testthat/test-ppc-test-statistics.R | 10 + 9 files changed, 92 insertions(+), 198 deletions(-) delete mode 100644 tests/testthat/test-data-functions-edge-cases.R diff --git a/NEWS.md b/NEWS.md index c91a2152..9a73a9db 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # bayesplot (development version) -* Added dedicated edge-case tests for all exported `_data()` functions. +* Added singleton-dimension edge-case tests for exported `_data()` functions. * Added unit tests for `mcmc_areas_ridges_data()`, `mcmc_parcoord_data()`, and `mcmc_trace_data()`. * Added unit tests for `ppc_error_data()` and `ppc_loo_pit_data()` covering output structure, argument handling, and edge cases. * Added vignette sections demonstrating `*_data()` companion functions for building custom ggplot2 visualizations (#435) diff --git a/tests/testthat/test-data-functions-edge-cases.R b/tests/testthat/test-data-functions-edge-cases.R deleted file mode 100644 index c1154e38..00000000 --- a/tests/testthat/test-data-functions-edge-cases.R +++ /dev/null @@ -1,197 +0,0 @@ -source(test_path("data-for-ppc-tests.R")) -load(test_path("data-for-ordinal.rda")) - -# ppc_bars_data ------------------------------------------------------------ - -test_that("ppc_bars_data handles single observation and single draw", { - y1 <- 2L - yrep1 <- matrix(c(1L, 2L, 3L, 2L, 2L), ncol = 1) - d <- ppc_bars_data(y1, yrep1) - expect_s3_class(d, "data.frame") - expect_equal(d$y_obs[d$x == 2], 1) - - # single draw: interval collapses to a point - y_s <- c(1L, 2L, 3L, 2L) - yrep_s <- matrix(c(1L, 2L, 2L, 3L), nrow = 1) - d2 <- ppc_bars_data(y_s, yrep_s) - expect_equal(d2$l, d2$m, ignore_attr = TRUE) - expect_equal(d2$m, d2$h, ignore_attr = TRUE) -}) - -test_that("ppc_bars_data prob = 0 collapses interval to median", { - d <- ppc_bars_data(y_ord, yrep_ord, prob = 0) - expect_equal(d$l, d$m, ignore_attr = TRUE) - expect_equal(d$m, d$h, ignore_attr = TRUE) -}) - -test_that("ppc_bars_data errors on zero-length input", { - expect_error(ppc_bars_data(integer(0), matrix(integer(0), nrow = 5, ncol = 0))) -}) - - -# ppc_error_data ----------------------------------------------------------- - -test_that("ppc_error_data handles single observation", { - skip_if_not_installed("rstantools") - y1 <- 5 - yrep1 <- matrix(c(4, 6, 5), ncol = 1) - d <- ppc_error_data(y1, yrep1) - expect_equal(nrow(d), 3) - expect_equal(d$value, y1 - yrep1[, 1]) - expect_true(all(d$y_obs == 5)) -}) - -test_that("ppc_error_data returns zero-row data frame for zero-length input", { - skip_if_not_installed("rstantools") - d <- ppc_error_data(numeric(0), matrix(numeric(0), nrow = 1, ncol = 0)) - expect_equal(nrow(d), 0) - expect_named(d, c("y_id", "y_name", "y_obs", "rep_id", "rep_label", "value")) -}) - - -# ppc_scatter_data --------------------------------------------------------- - -test_that("ppc_scatter_data handles single observation and single draw", { - y1 <- 5 - yrep1 <- matrix(c(4, 6, 5), ncol = 1) - d <- ppc_scatter_data(y1, yrep1) - expect_equal(nrow(d), 3) - expect_true(all(d$y_obs == 5)) - expect_equal(d$value, c(4, 6, 5)) - - # single draw - d2 <- ppc_scatter_data(y, yrep[1, , drop = FALSE]) - expect_equal(nrow(d2), length(y)) - expect_equal(d2$value, yrep[1, ]) - expect_equal(d2$y_obs, y) -}) - -test_that("ppc_scatter_data returns zero-row data frame for zero-length input", { - d <- ppc_scatter_data(numeric(0), matrix(numeric(0), nrow = 1, ncol = 0)) - expect_equal(nrow(d), 0) -}) - - -# ppc_scatter_avg_data ----------------------------------------------------- - -test_that("ppc_scatter_avg_data handles single observation", { - y1 <- 5 - yrep1 <- matrix(c(4, 6, 5), ncol = 1) - d <- ppc_scatter_avg_data(y1, yrep1) - expect_equal(nrow(d), 1) - expect_equal(d$value, mean(c(4, 6, 5))) - expect_equal(d$y_obs, 5) -}) - -test_that("ppc_scatter_avg_data returns zero-row data frame for zero-length input", { - d <- ppc_scatter_avg_data(numeric(0), matrix(numeric(0), nrow = 1, ncol = 0)) - expect_equal(nrow(d), 0) -}) - - -# ppc_loo_pit_data --------------------------------------------------------- - -test_that("ppc_loo_pit_data handles single pit value", { - expect_message( - d <- ppc_loo_pit_data(pit = 0.5, boundary_correction = FALSE, samples = 3), - "pit" - ) - y_rows <- d[d$is_y, ] - expect_equal(nrow(y_rows), 1) - expect_equal(y_rows$value, 0.5) -}) - -test_that("ppc_loo_pit_data works with custom bw parameter", { - set.seed(42) - pit_vals <- runif(50) - expect_message( - d <- ppc_loo_pit_data( - pit = pit_vals, - boundary_correction = TRUE, - bw = "SJ", - samples = 3, - grid_len = 128 - ), - "pit" - ) - expect_true("x" %in% names(d)) -}) - -test_that("ppc_loo_pit_data handles zero-length pit", { - expect_message( - d <- ppc_loo_pit_data(pit = numeric(0), boundary_correction = FALSE, samples = 2), - "pit" - ) - expect_equal(nrow(d), 0) -}) - -test_that("ppc_loo_pit_data is_y and is_y_label columns are consistent", { - set.seed(42) - pit_vals <- runif(10) - expect_message( - d <- ppc_loo_pit_data(pit = pit_vals, boundary_correction = FALSE, samples = 2), - "pit" - ) - expect_true(all(d$is_y_label[d$is_y] == levels(d$is_y_label)[1])) - expect_true(all(d$is_y_label[!d$is_y] == levels(d$is_y_label)[2])) -}) - - -# ppd_data ----------------------------------------------------------------- - -test_that("ppd_data handles single observation (single column)", { - ypred <- matrix(c(1, 2, 3), ncol = 1) - d <- ppd_data(ypred) - expect_equal(nrow(d), 3) - expect_true(all(d$y_id == 1)) - expect_equal(d$value, c(1, 2, 3)) -}) - -test_that("ppd_data returns zero-row data frame for zero-length input", { - d <- ppd_data(matrix(numeric(0), nrow = 1, ncol = 0)) - expect_equal(nrow(d), 0) -}) - - -# ppd_stat_data ------------------------------------------------------------ - -test_that("ppd_stat_data handles single draw and single observation", { - yrep_single <- matrix(rnorm(10), nrow = 1) - d <- ppd_stat_data(yrep_single, stat = "mean") - expect_equal(nrow(d), 1) - - yrep_1obs <- matrix(rnorm(5), ncol = 1) - d2 <- ppd_stat_data(yrep_1obs, stat = "mean") - expect_s3_class(d2, "data.frame") -}) - -test_that("ppd_stat_data errors on zero-length input", { - expect_error(ppd_stat_data(matrix(numeric(0), nrow = 1, ncol = 0), stat = "mean")) -}) - - -# ppd_intervals_data ------------------------------------------------------- - -test_that("ppd_intervals_data handles single observation and single draw", { - yrep_1obs <- matrix(rnorm(25), ncol = 1) - d <- ppd_intervals_data(yrep_1obs) - expect_equal(nrow(d), 1) - expect_true(d$ll <= d$l && d$l <= d$m && d$m <= d$h && d$h <= d$hh) - - # single draw: all quantiles collapse to the value - yrep_1draw <- matrix(rnorm(10), nrow = 1) - d2 <- ppd_intervals_data(yrep_1draw) - expect_equal(d2$ll, d2$m) - expect_equal(d2$hh, d2$m) -}) - -test_that("ppd_intervals_data uses custom x values", { - x_vals <- seq(10, 100, length.out = ncol(yrep)) - d <- ppd_intervals_data(yrep, x = x_vals) - expect_equal(d$x, x_vals) -}) - -test_that("ppd_intervals_data returns zero-row data frame for zero-length input", { - d <- ppd_intervals_data(matrix(numeric(0), nrow = 1, ncol = 0)) - expect_equal(nrow(d), 0) -}) diff --git a/tests/testthat/test-ppc-discrete.R b/tests/testthat/test-ppc-discrete.R index 7b0a6471..8cd20f57 100644 --- a/tests/testthat/test-ppc-discrete.R +++ b/tests/testthat/test-ppc-discrete.R @@ -77,6 +77,27 @@ test_that("ppc_bars_data includes all levels", { expect_equal(d3$h[2], 0, ignore_attr = TRUE) }) +test_that("ppc_bars_data handles single observation and single draw", { + y1 <- 2L + yrep1 <- matrix(c(1L, 2L, 3L, 2L, 2L), ncol = 1) + d <- ppc_bars_data(y1, yrep1) + expect_s3_class(d, "data.frame") + expect_equal(d$y_obs[d$x == 2], 1) + + # single draw: interval collapses to a point + y_s <- c(1L, 2L, 3L, 2L) + yrep_s <- matrix(c(1L, 2L, 2L, 3L), nrow = 1) + d2 <- ppc_bars_data(y_s, yrep_s) + expect_equal(d2$l, d2$m, ignore_attr = TRUE) + expect_equal(d2$m, d2$h, ignore_attr = TRUE) +}) + +test_that("ppc_bars_data prob = 0 collapses interval to median", { + d <- ppc_bars_data(y_ord, yrep_ord, prob = 0) + expect_equal(d$l, d$m, ignore_attr = TRUE) + expect_equal(d$m, d$h, ignore_attr = TRUE) +}) + # rootograms ----------------------------------------------------------- yrep3 <- matrix(yrep2, nrow = 5, ncol = ncol(yrep2), byrow = TRUE) diff --git a/tests/testthat/test-ppc-distributions.R b/tests/testthat/test-ppc-distributions.R index 34e1c82f..c6072af2 100644 --- a/tests/testthat/test-ppc-distributions.R +++ b/tests/testthat/test-ppc-distributions.R @@ -221,6 +221,14 @@ test_that("ppd_data handles a single replicate matrix", { expect_equal(d$value, c(11, 21)) }) +test_that("ppd_data handles single observation (single column)", { + ypred <- matrix(c(1, 2, 3), ncol = 1) + d <- ppd_data(ypred) + expect_equal(nrow(d), 3) + expect_true(all(d$y_id == 1)) + expect_equal(d$value, c(1, 2, 3)) +}) + # Visual tests ----------------------------------------------------------------- diff --git a/tests/testthat/test-ppc-errors.R b/tests/testthat/test-ppc-errors.R index e88c65b5..e50829b1 100644 --- a/tests/testthat/test-ppc-errors.R +++ b/tests/testthat/test-ppc-errors.R @@ -85,6 +85,15 @@ test_that("ppc_error_data with group returns exact structure", { expect_equal(d$group[d$rep_id == 1], group) }) +test_that("ppc_error_data handles single observation", { + y1 <- 5 + yrep1 <- matrix(c(4, 6, 5), ncol = 1) + d <- ppc_error_data(y1, yrep1) + expect_equal(nrow(d), 3) + expect_equal(d$value, y1 - yrep1[, 1]) + expect_true(all(d$y_obs == 5)) +}) + # Visual tests ----------------------------------------------------------------- diff --git a/tests/testthat/test-ppc-intervals.R b/tests/testthat/test-ppc-intervals.R index a1499303..e40b66fc 100644 --- a/tests/testthat/test-ppc-intervals.R +++ b/tests/testthat/test-ppc-intervals.R @@ -72,6 +72,19 @@ test_that("ppd_intervals_data + y_obs column same as ppc_intervals_data", { expect_equal(tibble::add_column(d_group2, y_obs = d_group$y_obs, .after = "y_id"), d_group) }) +test_that("ppd_intervals_data handles single observation and single draw", { + yrep_1obs <- matrix(rnorm(25), ncol = 1) + d <- ppd_intervals_data(yrep_1obs) + expect_equal(nrow(d), 1) + expect_true(d$ll <= d$l && d$l <= d$m && d$m <= d$h && d$h <= d$hh) + + # single draw: all quantiles collapse to the value + yrep_1draw <- matrix(rnorm(10), nrow = 1) + d2 <- ppd_intervals_data(yrep_1draw) + expect_equal(d2$ll, d2$m) + expect_equal(d2$hh, d2$m) +}) + test_that("ppc_intervals_data does math correctly", { d <- ppc_intervals_data(y, yrep, prob = .4, prob_outer = .8) qs <- unname(quantile(yrep[, 1], c(.1, .3, .5, .7, .9))) diff --git a/tests/testthat/test-ppc-loo.R b/tests/testthat/test-ppc-loo.R index a722ad46..b52c6f9e 100644 --- a/tests/testthat/test-ppc-loo.R +++ b/tests/testthat/test-ppc-loo.R @@ -399,3 +399,10 @@ test_that("ppc_loo_pit_data returns the expected structure for both boundary mod expect_equal(nrow(yrep_rows), grid_len * n_samples) expect_false(anyNA(d_bc$x)) }) + +test_that("ppc_loo_pit_data works with a single pit value", { + d <- suppressMessages(ppc_loo_pit_data(pit = 0.5, boundary_correction = FALSE, samples = 3)) + y_rows <- d[d$is_y, ] + expect_equal(nrow(y_rows), 1) + expect_equal(y_rows$value, 0.5) +}) diff --git a/tests/testthat/test-ppc-scatterplots.R b/tests/testthat/test-ppc-scatterplots.R index 02494796..e829c003 100644 --- a/tests/testthat/test-ppc-scatterplots.R +++ b/tests/testthat/test-ppc-scatterplots.R @@ -34,6 +34,29 @@ test_that("ppc_scatter_avg_data can take a custom fun_avg", { expect_equal(sums$value, colSums(yrep)) }) +test_that("ppc_scatter_data handles single observation and single draw", { + y1 <- 5 + yrep1 <- matrix(c(4, 6, 5), ncol = 1) + d <- ppc_scatter_data(y1, yrep1) + expect_equal(nrow(d), 3) + expect_true(all(d$y_obs == 5)) + expect_equal(d$value, c(4, 6, 5)) + + # single draw + d2 <- ppc_scatter_data(y, yrep[1, , drop = FALSE]) + expect_equal(nrow(d2), length(y)) + expect_equal(d2$value, yrep[1, ]) + expect_equal(d2$y_obs, y) +}) + +test_that("ppc_scatter_avg_data handles single observation", { + y1 <- 5 + yrep1 <- matrix(c(4, 6, 5), ncol = 1) + d <- ppc_scatter_avg_data(y1, yrep1) + expect_equal(nrow(d), 1) + expect_equal(d$value, mean(c(4, 6, 5))) + expect_equal(d$y_obs, 5) +}) # Visual tests ------------------------------------------------------------ diff --git a/tests/testthat/test-ppc-test-statistics.R b/tests/testthat/test-ppc-test-statistics.R index bc95ccc3..75a6dc75 100644 --- a/tests/testthat/test-ppc-test-statistics.R +++ b/tests/testthat/test-ppc-test-statistics.R @@ -129,6 +129,16 @@ test_that("ppc_stat_data and ppd_stat_data throw correct errors", { "object 'not_a_known_function' of mode 'function' was not found") }) +test_that("ppd_stat_data handles single draw and single observation", { + yrep_single <- matrix(rnorm(10), nrow = 1) + d <- ppd_stat_data(yrep_single, stat = "mean") + expect_equal(nrow(d), 1) + + yrep_1obs <- matrix(rnorm(5), ncol = 1) + d2 <- ppd_stat_data(yrep_1obs, stat = "mean") + expect_s3_class(d2, "data.frame") +}) + # Visual tests ------------------------------------------------------------ From cd0dfeefdfd551c3ab0fe523c5720eb6c3b35b58 Mon Sep 17 00:00:00 2001 From: Utkarsh Date: Wed, 1 Apr 2026 09:05:28 +0530 Subject: [PATCH 6/6] add nrow check for single-observation ppd_stat_data test --- tests/testthat/test-ppc-test-statistics.R | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/testthat/test-ppc-test-statistics.R b/tests/testthat/test-ppc-test-statistics.R index 75a6dc75..d227c30a 100644 --- a/tests/testthat/test-ppc-test-statistics.R +++ b/tests/testthat/test-ppc-test-statistics.R @@ -137,6 +137,7 @@ test_that("ppd_stat_data handles single draw and single observation", { yrep_1obs <- matrix(rnorm(5), ncol = 1) d2 <- ppd_stat_data(yrep_1obs, stat = "mean") expect_s3_class(d2, "data.frame") + expect_equal(nrow(d2), 5) })