# ============================================================================== # Tests for morph utility functions (morph_utils.R) # ============================================================================== # ------------------------------------------------------------------------------ # .has_namespace tests # ------------------------------------------------------------------------------ test_that(".has_namespace returns TRUE for installed packages", { expect_true(couplr:::.has_namespace("stats")) expect_true(couplr:::.has_namespace("utils")) }) test_that(".has_namespace returns FALSE for non-existent packages", { expect_false(couplr:::.has_namespace("nonexistent_package_xyz123")) }) # ------------------------------------------------------------------------------ # .gif_delay_from_fps tests # ------------------------------------------------------------------------------ test_that(".gif_delay_from_fps computes correct delays", { # 10 fps = 100/10 = 10 centiseconds per frame expect_equal(couplr:::.gif_delay_from_fps(10), 10L) # 20 fps = 100/20 = 5 centiseconds per frame expect_equal(couplr:::.gif_delay_from_fps(20), 5L) # 1 fps = 100/1 = 100 centiseconds per frame expect_equal(couplr:::.gif_delay_from_fps(1), 100L) }) test_that(".gif_delay_from_fps handles edge cases", { # Invalid fps defaults to 10 expect_equal(couplr:::.gif_delay_from_fps(0), 10L) expect_equal(couplr:::.gif_delay_from_fps(-1), 10L) expect_equal(couplr:::.gif_delay_from_fps(NA), 10L) }) # ------------------------------------------------------------------------------ # .to_planar_rgb and .from_planar_rgb tests # ------------------------------------------------------------------------------ test_that(".to_planar_rgb converts array to planar format", { # Create a simple 2x3 RGB array H <- 2 W <- 3 arr <- array(0L, dim = c(H, W, 3)) arr[,,1] <- matrix(c(1, 2, 3, 4, 5, 6), nrow = H, ncol = W) # R arr[,,2] <- matrix(c(10, 20, 30, 40, 50, 60), nrow = H, ncol = W) # G arr[,,3] <- matrix(c(100, 200, 101, 201, 102, 202), nrow = H, ncol = W) # B planar <- couplr:::.to_planar_rgb(arr) expect_equal(length(planar), H * W * 3) # First H*W elements are R channel expect_equal(planar[1:(H*W)], as.vector(arr[,,1])) # Next H*W elements are G channel expect_equal(planar[(H*W + 1):(2*H*W)], as.vector(arr[,,2])) # Last H*W elements are B channel expect_equal(planar[(2*H*W + 1):(3*H*W)], as.vector(arr[,,3])) }) test_that(".from_planar_rgb converts back to array", { H <- 2 W <- 3 arr <- array(0, dim = c(H, W, 3)) arr[,,1] <- matrix(1:6, nrow = H, ncol = W) arr[,,2] <- matrix(11:16, nrow = H, ncol = W) arr[,,3] <- matrix(21:26, nrow = H, ncol = W) planar <- couplr:::.to_planar_rgb(arr) restored <- couplr:::.from_planar_rgb(planar, H, W) expect_equal(dim(restored), c(H, W, 3)) expect_equal(restored[,,1], arr[,,1]) expect_equal(restored[,,2], arr[,,2]) expect_equal(restored[,,3], arr[,,3]) }) test_that(".from_planar_rgb validates input length", { expect_error( couplr:::.from_planar_rgb(1:10, H = 2, W = 3), # Expected 18, got 10 "wrong length" ) }) # ------------------------------------------------------------------------------ # .clamp_rgb tests # ------------------------------------------------------------------------------ test_that(".clamp_rgb clamps values to 0-255", { x <- c(-10, 0, 128, 255, 300) result <- couplr:::.clamp_rgb(x) expect_equal(result, c(0L, 0L, 128L, 255L, 255L)) }) test_that(".clamp_rgb preserves dimensions", { x <- matrix(c(-10, 0, 128, 300), nrow = 2, ncol = 2) result <- couplr:::.clamp_rgb(x) expect_equal(dim(result), c(2, 2)) }) test_that(".clamp_rgb rounds values", { x <- c(127.4, 127.6) result <- couplr:::.clamp_rgb(x) expect_equal(result, c(127L, 128L)) }) # ------------------------------------------------------------------------------ # .call_or tests # ------------------------------------------------------------------------------ test_that(".call_or calls primary function if it exists", { # stats::mean exists result <- couplr:::.call_or("mean", "nonexistent", 1:10) expect_equal(result, 5.5) }) test_that(".call_or calls fallback if primary doesn't exist", { # First arg doesn't exist, but sum does result <- couplr:::.call_or("nonexistent_func_xyz", "sum", 1:10) expect_equal(result, 55) }) test_that(".call_or errors if neither exists", { expect_error( couplr:::.call_or("nonexistent1_xyz", "nonexistent2_xyz"), "Neither" ) }) # ------------------------------------------------------------------------------ # .downscale_both tests # ------------------------------------------------------------------------------ test_that(".downscale_both returns originals when steps is 0", { H <- 4 W <- 4 N <- H * W A <- rep(1.0, 3 * N) B <- rep(2.0, 3 * N) result <- couplr:::.downscale_both(A, B, H, W, steps = 0) expect_equal(result$Hs, H) expect_equal(result$Ws, W) expect_equal(result$A_s, A) expect_equal(result$B_s, B) }) test_that(".downscale_both returns originals when steps is NULL", { H <- 4 W <- 4 N <- H * W A <- rep(1.0, 3 * N) B <- rep(2.0, 3 * N) result <- couplr:::.downscale_both(A, B, H, W, steps = NULL) expect_equal(result$Hs, H) expect_equal(result$Ws, W) }) # ------------------------------------------------------------------------------ # .lap_assign tests # ------------------------------------------------------------------------------ test_that(".lap_assign returns 0-based assignment", { cost <- matrix(c(1, 5, 5, 1), 2, 2) result <- couplr:::.lap_assign(cost, method = "hungarian") # Should return 0-based indices expect_true(all(result >= 0)) expect_true(all(result <= 1)) expect_equal(length(result), 2) }) test_that(".lap_assign handles tibble result from lap_solve", { cost <- matrix(c(1, 5, 5, 1), 2, 2) result <- couplr:::.lap_assign(cost, method = "jv") expect_equal(length(result), 2) expect_true(all(result %in% c(0, 1))) }) # ------------------------------------------------------------------------------ # .fill_unassigned_identity tests # ------------------------------------------------------------------------------ test_that(".fill_unassigned_identity fills negative values", { assign <- c(2L, -1L, 1L, -1L) result <- couplr:::.fill_unassigned_identity(assign) expect_equal(result[2], 2L) # Filled with own index expect_equal(result[4], 4L) # Filled with own index expect_equal(result[1], 2L) # Unchanged expect_equal(result[3], 1L) # Unchanged }) test_that(".fill_unassigned_identity handles all assigned", { assign <- c(2L, 1L, 4L, 3L) result <- couplr:::.fill_unassigned_identity(assign) expect_equal(result, assign) }) # ------------------------------------------------------------------------------ # .assemble_assignment tests # ------------------------------------------------------------------------------ test_that(".assemble_assignment builds assignment vector", { N <- 4 i_idx <- c(1L, 3L) j_idx <- c(2L, 4L) result <- couplr:::.assemble_assignment(N, i_idx, j_idx) expect_equal(result[1], 2L) expect_equal(result[3], 4L) expect_equal(result[2], -1L) # Unassigned expect_equal(result[4], -1L) # Unassigned }) test_that(".assemble_assignment handles empty input", { N <- 4 result <- couplr:::.assemble_assignment(N, integer(0), integer(0)) expect_equal(result, rep(-1L, N)) }) # ------------------------------------------------------------------------------ # %||% operator tests # ------------------------------------------------------------------------------ test_that("%||% returns first argument if not NULL", { expect_equal(couplr:::`%||%`(5, 10), 5) expect_equal(couplr:::`%||%`("a", "b"), "a") }) test_that("%||% returns second argument if first is NULL", { expect_equal(couplr:::`%||%`(NULL, 10), 10) expect_equal(couplr:::`%||%`(NULL, "default"), "default") }) # ------------------------------------------------------------------------------ # Image conversion tests (require magick) # ------------------------------------------------------------------------------ test_that(".to_array_rgb works with magick images", { skip_if_not_installed("magick") # Create a simple 2x2 red image img <- magick::image_blank(2, 2, color = "red") result <- couplr:::.to_array_rgb(img) expect_equal(dim(result), c(2, 2, 3)) # Red channel should be 255 expect_true(all(result[,,1] == 255)) # Green and blue should be 0 expect_true(all(result[,,2] == 0)) expect_true(all(result[,,3] == 0)) }) test_that(".to_array_rgb handles different color spaces", { skip_if_not_installed("magick") # Create a gradient image img <- magick::image_blank(3, 3, color = "#808080") # Gray result <- couplr:::.to_array_rgb(img) expect_equal(dim(result), c(3, 3, 3)) # All channels should be equal for gray expect_equal(result[,,1], result[,,2]) expect_equal(result[,,2], result[,,3]) }) # ------------------------------------------------------------------------------ # Palette pipeline tests # ------------------------------------------------------------------------------ test_that(".palette_pairs_identity finds matching colors", { # This tests the internal palette matching logic # We need to mock the info structure info <- list( colorsA_rgb = matrix(c(255, 0, 0, 0, 255, 0), nrow = 2, ncol = 3, byrow = TRUE), colorsB_rgb = matrix(c(255, 0, 0, 0, 0, 255), nrow = 2, ncol = 3, byrow = TRUE), countsA = c(10, 5), countsB = c(8, 7) ) result <- couplr:::.palette_pairs_identity(info) # Only red (255,0,0) matches expect_equal(nrow(result), 1) expect_equal(result$ia, 1) expect_equal(result$ib, 1) expect_equal(result$k, 8) # min(10, 8) }) test_that(".palette_pairs_identity handles no matches", { info <- list( colorsA_rgb = matrix(c(255, 0, 0), nrow = 1, ncol = 3), colorsB_rgb = matrix(c(0, 255, 0), nrow = 1, ncol = 3), countsA = c(10), countsB = c(10) ) result <- couplr:::.palette_pairs_identity(info) expect_equal(nrow(result), 0) })