Nothing
# ==============================================================================
# Global Setup for this file
# ==============================================================================
data(iris)
# Split into Matrix (X) and Grouping Factor (Y) for testing matrix interfaces
X <- as.matrix(iris[, 1:4])
Y <- iris$Species
# ==============================================================================
# 1. Testing Model Fitting Interfaces (S3 Dispatch)
# ==============================================================================
test_that("gipsqda formula interface works correctly", {
fit_formula <- gipsqda(Species ~ ., data = iris)
# Check class and type
expect_s3_class(fit_formula, "gipsqda")
expect_type(fit_formula, "list")
# Check if the result contains key components
required_components <- c("prior", "means", "scaling", "counts")
expect_true(all(required_components %in% names(fit_formula)))
# Check dimensions of the means (3 classes x 4 variables)
expect_equal(dim(fit_formula$means), c(3, 4))
})
test_that("gipsqda matrix interface works and matches formula", {
# Re-fit formula for comparison
fit_formula <- gipsqda(Species ~ ., data = iris)
# Action
fit_matrix <- gipsqda(x = X, grouping = Y)
expect_s3_class(fit_matrix, "gipsqda")
# The results between formula and matrix interface should be consistent
expect_equal(fit_matrix$prior, fit_formula$prior)
expect_equal(fit_matrix$counts, fit_formula$counts)
})
test_that("gipsqda data.frame interface works", {
fit_df <- gipsqda(x = iris[, 1:4], grouping = Y)
expect_s3_class(fit_df, "gipsqda")
})
test_that("gipsqda handles custom arguments", {
fit_opts <- gipsqda(X, Y, MAP = FALSE, weighted_avg = TRUE)
expect_s3_class(fit_opts, "gipsqda")
})
# ==============================================================================
# 2. Testing the 'print' method
# ==============================================================================
test_that("print.gipsqda works", {
fit_formula <- gipsqda(Species ~ ., data = iris)
# expect_output asserts that something is printed to the console
expect_output(print(fit_formula))
})
# ==============================================================================
# 3. Testing the 'predict' method
# ==============================================================================
test_that("predict.gipsqda works correctly", {
# Setup
fit_formula <- gipsqda(Species ~ ., data = iris)
# Action
pred <- predict(fit_formula, newdata = iris)
expect_type(pred, "list")
# Check components
expect_equal(names(pred), c("class", "posterior"))
# Check dimensions matching the input data
expect_length(pred$class, nrow(iris))
expect_equal(nrow(pred$posterior), nrow(iris))
expect_equal(ncol(pred$posterior), 3) # 3 classes
# Check if posterior probabilities sum to 1 (row-wise)
row_sums <- rowSums(pred$posterior)
# expect_equal with a vector handles tolerance automatically
expect_equal(unname(row_sums), rep(1, nrow(iris)),
tolerance = 1e-6,
ignore_attr = TRUE
)
})
test_that("predict.gipsqda works on new subset", {
# Setup
fit_formula <- gipsqda(Species ~ ., data = iris)
new_data <- iris[1:5, ]
# Action
pred_small <- predict(fit_formula, newdata = new_data)
# Assert
expect_length(pred_small$class, 5)
})
# ==============================================================================
# 4. Input Validation & Error Handling
# ==============================================================================
test_that("gipsqda correctly validates bad inputs", {
# Infinite or NA values
X_na <- X
X_na[1, 1] <- NA
expect_error(gipsqda(X_na, Y), "infinite, NA or NaN values in 'x'")
X_inf <- X
X_inf[1, 1] <- Inf
expect_error(gipsqda(X_inf, Y), "infinite, NA or NaN values in 'x'")
# Dimension mismatch
expect_error(gipsqda(X[1:10, ], Y), "nrow\\(x\\) and length\\(grouping\\) are different")
# 'x' is not a matrix (vector input)
expect_error(gipsqda.default(1:10, rep(1, 10)), "'x' is not a matrix")
# Small group size (group count < p+1)
# Create subset where groups have < 5 observations (since p=4)
small_X <- X[c(1:4, 51:54, 101:104), ]
small_Y <- factor(c(rep("s", 4), rep("ve", 4), rep("vi", 4)))
expect_no_error(gipsqda(small_X, small_Y))
})
test_that("gipsqda validates prior probabilities", {
# Sum != 1
expect_error(gipsqda(X, Y, prior = c(0.5, 0.5, 0.5)), "invalid 'prior'")
# Negative values
expect_error(gipsqda(X, Y, prior = c(1.2, -0.1, -0.1)), "invalid 'prior'")
# Wrong length
expect_error(gipsqda(X, Y, prior = c(0.5, 0.5)), "'prior' is of incorrect length")
})
test_that("gipsqda handles missing dimnames", {
X_no_names <- X
dimnames(X_no_names) <- NULL
fit <- gipsqda(X_no_names, Y)
expect_s3_class(fit, "gipsqda")
# Should generate default colnames "1", "2", "3", "4"
expect_equal(colnames(fit$means), as.character(1:4))
})
# ==============================================================================
# 5. Matrix Interface: Subset & NA Action
# ==============================================================================
test_that("gipsqda.matrix handles subset and na.action properly", {
# 1. Test subset
# Must pick samples from ALL groups to avoid "group too small" error
subset_idx <- c(1:10, 51:60, 101:110)
fit_sub <- gipsqda(X, Y, subset = subset_idx)
expect_equal(fit_sub$N, 30) # 3 groups * 10 obs
# 2. Test na.action
X_dirty <- X
X_dirty[1, 1] <- NA
# Workaround for missing row.names in the source code
safe_na_omit <- function(obj) {
attr(obj, "row.names") <- seq_along(obj$g)
stats::na.omit(obj)
}
fit_na <- gipsqda(X_dirty, Y, na.action = safe_na_omit)
# Expect 1 row removed (150 -> 149)
expect_equal(fit_na$N, 149)
})
# ==============================================================================
# 6. Optimizer Selection Logic
# ==============================================================================
test_that("optimizer logic and warnings work", {
# Default behavior
fit_def <- gipsqda(X, Y, optimizer = NULL)
expect_s3_class(fit_def, "gipsqda")
# Warning when max_iter is missing for MH
expect_warning(
gipsqda(X, Y, optimizer = "MH"),
"MH optimizer set but 'max_iter' argument is unspecified"
)
# No warning when explicitly set
expect_no_warning(
gipsqda(X, Y, optimizer = "MH", max_iter = 10)
)
})
# ==============================================================================
# 7. Prediction Methods & Edge Cases
# ==============================================================================
test_that("predict supports different methods", {
fit <- gipsqda(X, Y)
# Predictive (Bayesian) - covers the 'else' block
pred_bay <- predict(fit, newdata = X, method = "predictive")
expect_equal(nrow(pred_bay$posterior), 150)
expect_false(any(is.na(pred_bay$posterior)))
# Debiased
pred_deb <- predict(fit, newdata = X, method = "debiased")
expect_equal(nrow(pred_deb$posterior), 150)
# looCV (Leave-One-Out)
pred_loo <- predict(fit, method = "looCV")
expect_equal(nrow(pred_loo$posterior), 150)
})
test_that("predict catches invalid arguments", {
fit <- gipsqda(X, Y)
# Forbidden combination: looCV + newdata
expect_error(
predict(fit, newdata = X, method = "looCV"),
"cannot have leave-one-out CV with 'newdata'"
)
# Wrong dimensions
expect_error(predict(fit, newdata = X[, 1:2]), "wrong number of variables")
# Invalid prior length
expect_error(
predict(fit, newdata = X, prior = c(0.5, 0.5)),
"'prior' is of incorrect length"
)
# Variable name mismatch warning
X_bad <- X
colnames(X_bad) <- c("A", "B", "C", "D")
expect_warning(
predict(fit, newdata = X_bad),
"variable names in 'newdata' do not match"
)
})
test_that("predict reconstructs data from subset when newdata is missing", {
# Covers the complex eval.parent block
# Use indices from all groups
subset_idx <- c(1:10, 51:60, 101:110)
fit_sub <- gipsqda(X, Y, subset = subset_idx)
# Call predict without newdata -> triggers reconstruction
pred <- predict(fit_sub)
expect_equal(nrow(pred$posterior), 30)
expect_length(pred$class, 30)
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.