# ============================================================================== # Tests for utility functions (utils.R) # ============================================================================== # ------------------------------------------------------------------------------ # validate_cost_data tests # ------------------------------------------------------------------------------ test_that("validate_cost_data accepts valid numeric matrix", { cost <- matrix(c(1, 2, 3, 4), 2, 2) result <- couplr:::validate_cost_data(cost) expect_equal(result, cost) }) test_that("validate_cost_data converts numeric vector to matrix", { cost <- c(1, 2, 3, 4) result <- couplr:::validate_cost_data(cost) expect_true(is.matrix(result)) }) test_that("validate_cost_data errors on data frame", { cost <- data.frame(x = 1:3, y = 1:3, z = 1:3) expect_error( couplr:::validate_cost_data(cost), "Data frame input" ) }) test_that("validate_cost_data errors on empty matrix", { cost <- matrix(nrow = 0, ncol = 0) expect_error( couplr:::validate_cost_data(cost), "at least one row" ) }) test_that("validate_cost_data errors on non-numeric matrix", { cost <- matrix(c("a", "b", "c", "d"), 2, 2) expect_error( couplr:::validate_cost_data(cost), "must be numeric" ) }) test_that("validate_cost_data errors on NaN values", { cost <- matrix(c(1, NaN, 3, 4), 2, 2) expect_error( couplr:::validate_cost_data(cost), "NaN" ) }) test_that("validate_cost_data allows NA values", { cost <- matrix(c(1, NA, 3, 4), 2, 2) # NA is at [2,1] in column-major order result <- couplr:::validate_cost_data(cost) expect_true(is.na(result[2, 1])) }) test_that("validate_cost_data allows Inf values", { cost <- matrix(c(1, Inf, 3, 4), 2, 2) # Inf is at [2,1] in column-major order result <- couplr:::validate_cost_data(cost) expect_true(is.infinite(result[2, 1])) }) # ------------------------------------------------------------------------------ # is_* predicate function tests # ------------------------------------------------------------------------------ test_that("is_lap_solve_result returns TRUE for lap_solve result", { cost <- matrix(c(1, 2, 3, 4), 2, 2) result <- lap_solve(cost) expect_true(is_lap_solve_result(result)) }) test_that("is_lap_solve_result returns FALSE for other objects", { expect_false(is_lap_solve_result(list())) expect_false(is_lap_solve_result(data.frame())) expect_false(is_lap_solve_result(NULL)) expect_false(is_lap_solve_result(1:5)) }) test_that("is_lap_solve_batch_result returns TRUE for batch result", { costs <- list(matrix(c(1, 2, 3, 4), 2, 2)) result <- lap_solve_batch(costs) expect_true(is_lap_solve_batch_result(result)) }) test_that("is_lap_solve_batch_result returns FALSE for other objects", { expect_false(is_lap_solve_batch_result(list())) expect_false(is_lap_solve_batch_result(data.frame())) expect_false(is_lap_solve_batch_result(NULL)) }) test_that("is_lap_solve_kbest_result returns TRUE for kbest result", { cost <- matrix(c(1, 5, 5, 1), 2, 2) result <- lap_solve_kbest(cost, k = 2) expect_true(is_lap_solve_kbest_result(result)) }) test_that("is_lap_solve_kbest_result returns FALSE for other objects", { expect_false(is_lap_solve_kbest_result(list())) expect_false(is_lap_solve_kbest_result(data.frame())) }) # ------------------------------------------------------------------------------ # get_total_cost tests # ------------------------------------------------------------------------------ test_that("get_total_cost works for lap_solve_result", { cost <- matrix(c(1, 2, 3, 4), 2, 2) result <- lap_solve(cost) tc <- get_total_cost(result) expect_type(tc, "double") expect_true(tc > 0) }) test_that("get_total_cost works for lap_solve_batch_result", { costs <- list( matrix(c(1, 2, 3, 4), 2, 2), matrix(c(5, 6, 7, 8), 2, 2) ) result <- lap_solve_batch(costs) tc <- get_total_cost(result) expect_type(tc, "double") expect_equal(length(tc), 2) # Two problems }) test_that("get_total_cost works for lap_solve_kbest_result", { cost <- matrix(c(1, 5, 5, 1), 2, 2) result <- lap_solve_kbest(cost, k = 2) tc <- get_total_cost(result) expect_type(tc, "double") expect_true(length(tc) >= 1) }) test_that("get_total_cost errors on invalid object", { expect_error( get_total_cost(list()), "not a valid assignment result" ) }) test_that("get_total_cost errors on lap_solve_result without attribute", { result <- tibble::tibble(source = 1:2, target = 2:1, cost = c(1, 2)) class(result) <- c("lap_solve_result", class(result)) # No total_cost attribute expect_error( get_total_cost(result), "total_cost attribute not found" ) }) # ------------------------------------------------------------------------------ # get_method_used tests # ------------------------------------------------------------------------------ test_that("get_method_used works for lap_solve_result", { cost <- matrix(c(1, 2, 3, 4), 2, 2) result <- lap_solve(cost, method = "hungarian") method <- get_method_used(result) expect_type(method, "character") expect_equal(method, "hungarian") }) test_that("get_method_used works for lap_solve_batch_result", { costs <- list(matrix(c(1, 2, 3, 4), 2, 2)) result <- lap_solve_batch(costs, method = "hungarian") method <- get_method_used(result) expect_type(method, "character") expect_equal(method, "hungarian") }) test_that("get_method_used errors on invalid object", { expect_error( get_method_used(list()), "not a valid assignment result" ) }) test_that("get_method_used errors on lap_solve_result without attribute", { result <- tibble::tibble(source = 1:2, target = 2:1, cost = c(1, 2)) class(result) <- c("lap_solve_result", class(result)) # No method_used attribute expect_error( get_method_used(result), "method_used attribute not found" ) }) # ------------------------------------------------------------------------------ # as_assignment_matrix tests # ------------------------------------------------------------------------------ test_that("as_assignment_matrix creates binary matrix", { cost <- matrix(c(1, 5, 5, 1), 2, 2) result <- lap_solve(cost) mat <- as_assignment_matrix(result) expect_true(is.matrix(mat)) expect_true(all(mat %in% c(0L, 1L))) expect_equal(sum(mat), 2) # Two assignments }) test_that("as_assignment_matrix handles custom dimensions", { cost <- matrix(c(1, 5, 5, 1), 2, 2) result <- lap_solve(cost) mat <- as_assignment_matrix(result, n_sources = 5, n_targets = 5) expect_equal(nrow(mat), 5) expect_equal(ncol(mat), 5) expect_equal(sum(mat), 2) }) test_that("as_assignment_matrix errors on non-lap_solve_result", { expect_error( as_assignment_matrix(list()), "must be a lap_solve_result" ) }) test_that("as_assignment_matrix handles empty result", { result <- tibble::tibble( source = integer(0), target = integer(0), cost = numeric(0) ) class(result) <- c("lap_solve_result", class(result)) attr(result, "total_cost") <- 0 attr(result, "method_used") <- "test" mat <- as_assignment_matrix(result, n_sources = 3, n_targets = 3) expect_equal(dim(mat), c(3, 3)) expect_equal(sum(mat), 0) }) test_that("as_assignment_matrix errors on missing columns", { result <- tibble::tibble(x = 1:2, y = 1:2) class(result) <- c("lap_solve_result", class(result)) attr(result, "total_cost") <- 0 attr(result, "method_used") <- "test" expect_error( as_assignment_matrix(result), "source.*target" ) }) test_that("as_assignment_matrix errors on negative dimensions", { cost <- matrix(c(1, 5, 5, 1), 2, 2) result <- lap_solve(cost) expect_error( as_assignment_matrix(result, n_sources = -1), "non-negative" ) }) # ------------------------------------------------------------------------------ # %||% operator tests # ------------------------------------------------------------------------------ test_that("null coalescing operator returns first if not NULL", { result <- couplr:::`%||%`(5, 10) expect_equal(result, 5) }) test_that("null coalescing operator returns second if first is NULL", { result <- couplr:::`%||%`(NULL, 10) expect_equal(result, 10) }) test_that("null coalescing operator works with NA", { # NA is not NULL, so should return NA result <- couplr:::`%||%`(NA, 10) expect_true(is.na(result)) }) # ------------------------------------------------------------------------------ # is_distance_object tests # ------------------------------------------------------------------------------ test_that("is_distance_object returns TRUE for distance_object", { left <- data.frame(id = 1:5, x = 1:5) right <- data.frame(id = 6:10, x = 6:10) dist_obj <- compute_distances(left, right, vars = "x") expect_true(is_distance_object(dist_obj)) }) test_that("is_distance_object returns FALSE for other objects", { expect_false(is_distance_object(list())) expect_false(is_distance_object(data.frame())) expect_false(is_distance_object(NULL)) expect_false(is_distance_object(matrix(1:4, 2, 2))) })