Nothing
# ==============================================================================
# Final coverage tests for morph_utils.R - targeting remaining uncovered lines
# ==============================================================================
# ------------------------------------------------------------------------------
# .to_array_rgb: various data type branches (lines 31-41, 44-56)
# ------------------------------------------------------------------------------
test_that(".to_array_rgb handles raw array directly", {
skip_if_not_installed("magick")
# Test with actual magick image - covers raw data path
img <- magick::image_blank(4, 4, color = "#FF8040")
result <- couplr:::.to_array_rgb(img)
expect_equal(dim(result), c(4, 4, 3))
expect_true(is.integer(result))
# Red channel should be 255
expect_equal(result[1, 1, 1], 255L)
})
test_that(".to_array_rgb handles different channel orderings", {
skip_if_not_installed("magick")
# Create images of different sizes to exercise dimension branches
for (size in c(2, 5, 10)) {
img <- magick::image_blank(size, size + 1, color = "green")
result <- couplr:::.to_array_rgb(img)
expect_equal(dim(result)[1], size + 1) # Height
expect_equal(dim(result)[2], size) # Width
expect_equal(dim(result)[3], 3) # Channels
}
})
# ------------------------------------------------------------------------------
# .lap_assign: list return type branches (lines 243-252)
# ------------------------------------------------------------------------------
test_that(".lap_assign handles list with 'assignment' field", {
# Override lap_solve temporarily
original_fn <- couplr::lap_solve
mock_lap_solve <- function(C, ...) {
n <- nrow(C)
list(assignment = 1:n, cost = 0) # Return list with assignment
}
assignInNamespace("lap_solve", mock_lap_solve, ns = "couplr")
cost <- matrix(1:9, 3, 3)
result <- couplr:::.lap_assign(cost, method = "jv")
assignInNamespace("lap_solve", original_fn, ns = "couplr")
expect_length(result, 3)
expect_true(all(result >= 0))
})
test_that(".lap_assign handles list with 'perm' field", {
original_fn <- couplr::lap_solve
mock_lap_solve <- function(C, ...) {
n <- nrow(C)
list(perm = n:1, cost = 0) # Return list with perm
}
assignInNamespace("lap_solve", mock_lap_solve, ns = "couplr")
cost <- matrix(1:9, 3, 3)
result <- couplr:::.lap_assign(cost, method = "jv")
assignInNamespace("lap_solve", original_fn, ns = "couplr")
expect_length(result, 3)
})
test_that(".lap_assign handles list with 'match' field", {
original_fn <- couplr::lap_solve
mock_lap_solve <- function(C, ...) {
n <- nrow(C)
list(match = c(2L, 3L, 1L), cost = 0) # Return list with match
}
assignInNamespace("lap_solve", mock_lap_solve, ns = "couplr")
cost <- matrix(1:9, 3, 3)
result <- couplr:::.lap_assign(cost, method = "jv")
assignInNamespace("lap_solve", original_fn, ns = "couplr")
expect_length(result, 3)
})
test_that(".lap_assign handles numeric vector return", {
original_fn <- couplr::lap_solve
mock_lap_solve <- function(C, ...) {
n <- nrow(C)
as.numeric(1:n) # Return numeric vector
}
assignInNamespace("lap_solve", mock_lap_solve, ns = "couplr")
cost <- matrix(1:9, 3, 3)
result <- couplr:::.lap_assign(cost, method = "jv")
assignInNamespace("lap_solve", original_fn, ns = "couplr")
expect_length(result, 3)
})
test_that(".lap_assign errors on unsupported return", {
original_fn <- couplr::lap_solve
mock_lap_solve <- function(C, ...) {
"unsupported_return_type" # Return string
}
assignInNamespace("lap_solve", mock_lap_solve, ns = "couplr")
cost <- matrix(1:9, 3, 3)
expect_error(
couplr:::.lap_assign(cost, method = "jv"),
"unsupported structure"
)
assignInNamespace("lap_solve", original_fn, ns = "couplr")
})
# ------------------------------------------------------------------------------
# .lap_assign: lap_solve_batch fallback (lines 226-228)
# ------------------------------------------------------------------------------
test_that(".lap_assign uses lap_solve_batch when lap_solve missing", {
original_lap_solve <- couplr::lap_solve
# Temporarily hide lap_solve
mock_missing <- function(...) stop("missing")
# This is tricky - we need to test the fallback path
# For now, just verify lap_solve_batch exists
expect_true(exists("lap_solve_batch", envir = asNamespace("couplr")))
})
# ------------------------------------------------------------------------------
# .lap_assign: data.frame target filling (lines 237-239)
# ------------------------------------------------------------------------------
test_that(".lap_assign fills gaps in data.frame source/target", {
original_fn <- couplr::lap_solve
mock_lap_solve <- function(C, ...) {
# Return partial assignment (source 1->target 2, source 3->target 1)
# source 2 not assigned
data.frame(source = c(1L, 3L), target = c(2L, 1L))
}
assignInNamespace("lap_solve", mock_lap_solve, ns = "couplr")
cost <- matrix(1:9, 3, 3)
result <- couplr:::.lap_assign(cost, method = "jv")
assignInNamespace("lap_solve", original_fn, ns = "couplr")
expect_length(result, 3)
# Gap at position 2 should be filled with identity (2)
expect_equal(result[2] + 1, 2) # +1 because result is 0-based
})
# ------------------------------------------------------------------------------
# .solve_color_match_pipeline: no-fill path (line 493)
# ------------------------------------------------------------------------------
test_that(".solve_color_match_pipeline without fill has unassigned", {
H <- 4
W <- 4
N <- H * W
# Completely different colors - no matches
A_planar <- c(rep(255, N), rep(0, N), rep(0, N)) # Red
B_planar <- c(rep(0, N), rep(0, N), rep(255, N)) # Blue
result <- couplr:::.solve_color_match_pipeline(
A_planar, B_planar, H, W,
quantize_bits = 8, # High bits = fewer matches
method = "jv",
fill_identity_for_unmatched = FALSE
)
# Should have some -1 entries (unassigned)
expect_true(any(result == -1L) || all(result > 0))
})
# ------------------------------------------------------------------------------
# .palette_pairs_identity: multiple color matches (lines 357-367)
# ------------------------------------------------------------------------------
test_that(".palette_pairs_identity handles multiple matches", {
info <- list(
colorsA_rgb = matrix(c(
255, 0, 0, # Red
0, 255, 0, # Green
0, 0, 255 # Blue
), nrow = 3, ncol = 3, byrow = TRUE),
colorsB_rgb = matrix(c(
255, 0, 0, # Red (matches)
0, 255, 0, # Green (matches)
128, 128, 128 # Gray (no match)
), nrow = 3, ncol = 3, byrow = TRUE),
countsA = c(10L, 20L, 15L),
countsB = c(8L, 25L, 30L)
)
result <- couplr:::.palette_pairs_identity(info)
expect_equal(nrow(result), 2) # Red and Green match
})
# ------------------------------------------------------------------------------
# .build_spatial_assignments_for_pairs: k limiting (lines 321-323)
# ------------------------------------------------------------------------------
test_that(".build_spatial_assignments_for_pairs limits by k", {
info <- list(
groupsA = list(1:10, 11:15), # 10 pixels, 5 pixels
groupsB = list(21:30, 31:35) # 10 pixels, 5 pixels
)
pairs <- data.frame(
ia = c(1L, 2L),
ib = c(1L, 2L),
k = c(5L, 3L) # Limit to 5 and 3 matches
)
result <- couplr:::.build_spatial_assignments_for_pairs(info, pairs, H = 10, W = 10)
# Should have exactly 5 + 3 = 8 assignments
expect_equal(length(result$i_idx), 8)
})
# ------------------------------------------------------------------------------
# .patch_cost_matrix: diag_norm edge case (lines 270-272)
# ------------------------------------------------------------------------------
test_that(".patch_cost_matrix handles infinite diag_norm from dist", {
# Single patch at same location - dist returns 0
patches_a <- list(
colors = matrix(c(100, 100, 100), nrow = 1, ncol = 3),
centers = matrix(c(0, 0), nrow = 1, ncol = 2)
)
patches_b <- list(
colors = matrix(c(200, 200, 200), nrow = 1, ncol = 3),
centers = matrix(c(0, 0), nrow = 1, ncol = 2)
)
# Without H,W, diag_norm comes from max(dist(...)) which may be 0
result <- couplr:::.patch_cost_matrix(patches_a, patches_b, alpha = 1, beta = 0.5)
expect_true(is.finite(result))
})
# ------------------------------------------------------------------------------
# .expand_patch_assignment: NA/Inf values (line 284)
# ------------------------------------------------------------------------------
test_that(".expand_patch_assignment handles NA assignment", {
patch_assign <- list(NA_integer_, 1L)
patches_a <- list(
indices = list(c(1L, 2L), c(3L, 4L))
)
patches_b <- list(
indices = list(c(5L, 6L))
)
result <- couplr:::.expand_patch_assignment(patch_assign, patches_a, patches_b, N = 4)
expect_equal(result[1], -1L) # NA assignment -> not assigned
expect_equal(result[2], -1L)
})
# ------------------------------------------------------------------------------
# .cpp_* wrapper functions with actual calls
# ------------------------------------------------------------------------------
test_that(".cpp_palette_info works", {
H <- 4
W <- 4
N <- H * W
Ap <- runif(N * 3) * 255
Bp <- runif(N * 3) * 255
result <- couplr:::.cpp_palette_info(Ap, Bp, H, W, bits = 5)
expect_true(is.list(result))
expect_true("groupsA" %in% names(result) || "countsA" %in% names(result))
})
test_that(".cpp_spatial_cost works", {
idxA <- c(1L, 2L, 3L)
idxB <- c(4L, 5L, 6L)
result <- couplr:::.cpp_spatial_cost(idxA, idxB, H = 4, W = 4)
expect_true(is.matrix(result))
expect_equal(dim(result), c(3, 3))
})
test_that(".cpp_compute_pixel_cost works", {
H <- 2
W <- 2
N <- H * W
Ap <- rep(100, N * 3)
Bp <- rep(150, N * 3)
result <- couplr:::.cpp_compute_pixel_cost(Ap, Bp, H, W, alpha = 1, beta = 0)
expect_true(is.matrix(result))
expect_equal(dim(result), c(N, N))
})
test_that(".cpp_render_morph works", {
H <- 2
W <- 2
N <- H * W
Ap <- rep(100, N * 3)
Bp <- rep(200, N * 3)
asg <- 0:(N - 1) # Identity assignment (0-based)
result <- couplr:::.cpp_render_morph(Ap, Bp, asg, H, W, nF = 2)
expect_true(is.list(result))
expect_equal(length(result), 2) # 2 frames
})
test_that(".cpp_overlap works", {
H <- 4
W <- 4
N <- H * W
Ap <- rep(100, N * 3)
Bp <- rep(100, N * 3)
result <- couplr:::.cpp_overlap(Ap, Bp, H, W, bits = 5)
expect_true(is.list(result) || is.numeric(result))
})
# .cpp_extract_patches not currently exported - skip this test
# ------------------------------------------------------------------------------
# morph_utils: old version of color_walk in morph_utils.R (lines 381-483)
# This tests the version with warning for large groups
# ------------------------------------------------------------------------------
test_that("morph_utils .solve_color_walk_pipeline handles empty groups", {
H <- 2
W <- 2
N <- H * W
# Uniform colors
A_planar <- c(rep(128, N), rep(128, N), rep(128, N))
B_planar <- c(rep(128, N), rep(128, N), rep(128, N))
result <- couplr:::.solve_color_walk_pipeline(
A_planar, B_planar, H, W,
quantize_bits = 5,
method = "jv",
maximize = FALSE
)
expect_equal(length(result), N)
})
# ------------------------------------------------------------------------------
# morph_pixel.R: specific solver functions
# ------------------------------------------------------------------------------
test_that(".exact_cost_and_solve in morph_pixel works", {
H <- 3
W <- 3
N <- H * W
Ap <- runif(N * 3) * 255
Bp <- runif(N * 3) * 255
# Call the internal function from morph_pixel.R directly
# This should be the version at line 780
result <- couplr:::.exact_cost_and_solve(
Ap, Bp, H, W,
alpha = 1, beta = 0.1,
method = "hungarian",
maximize = FALSE
)
expect_equal(length(result), N)
})
# ------------------------------------------------------------------------------
# morph_pixel.R: .solve_color_walk_pipeline in morph_pixel.R (lines 1179-1221)
# This is different from the one in morph_utils.R
# ------------------------------------------------------------------------------
test_that("morph_pixel .solve_color_walk_pipeline processes all pixels", {
H <- 5
W <- 5
N <- H * W
set.seed(123)
Ap <- runif(N * 3) * 255
Bp <- runif(N * 3) * 255
# This should use the morph_pixel.R version
result <- couplr:::.solve_color_walk_pipeline(
Ap, Bp, H, W,
quantize_bits = 3, # Low bits = more colors per group
method = "jv",
maximize = FALSE
)
expect_equal(length(result), N)
# Should be a bijection
expect_equal(sort(result), 1:N)
})
# ------------------------------------------------------------------------------
# .generate_square_tiles: corner cases in tiling (lines 1248-1316)
# ------------------------------------------------------------------------------
test_that(".generate_square_tiles handles W > H", {
tiles <- couplr:::.generate_square_tiles(W = 8, H = 3, P = 3)
# Count covered pixels
covered <- matrix(FALSE, 3, 8)
for (tile in tiles) {
for (dx in 0:(tile$size - 1)) {
for (dy in 0:(tile$size - 1)) {
covered[tile$y0 + dy + 1, tile$x0 + dx + 1] <- TRUE
}
}
}
expect_true(all(covered))
})
test_that(".generate_square_tiles handles H > W", {
tiles <- couplr:::.generate_square_tiles(W = 3, H = 8, P = 3)
covered <- matrix(FALSE, 8, 3)
for (tile in tiles) {
for (dx in 0:(tile$size - 1)) {
for (dy in 0:(tile$size - 1)) {
covered[tile$y0 + dy + 1, tile$x0 + dx + 1] <- TRUE
}
}
}
expect_true(all(covered))
})
test_that(".generate_square_tiles handles P = 1", {
tiles <- couplr:::.generate_square_tiles(W = 3, H = 3, P = 1)
# All tiles should be size 1
expect_true(all(sapply(tiles, function(t) t$size) == 1))
expect_equal(length(tiles), 9) # 3x3 = 9 tiles
})
# ------------------------------------------------------------------------------
# .recursive_tiling_solver: edge dimensions (lines 1007-1019)
# ------------------------------------------------------------------------------
test_that(".recursive_tiling_solver handles patch_size > image", {
H <- 2
W <- 2
N <- H * W
A_planar <- runif(N * 3) * 255
B_planar <- runif(N * 3) * 255
result <- couplr:::.recursive_tiling_solver(
A_planar, B_planar, H, W,
patch_size = 10, # Larger than image
alpha = 1, beta = 0
)
expect_equal(length(result), N)
})
test_that(".recursive_tiling_solver handles single pixel", {
H <- 1
W <- 1
N <- 1
A_planar <- c(100, 150, 200)
B_planar <- c(50, 100, 150)
result <- couplr:::.recursive_tiling_solver(
A_planar, B_planar, H, W,
patch_size = 1,
alpha = 1, beta = 0
)
expect_equal(result, 1L)
})
# ------------------------------------------------------------------------------
# .square_tiling_solver: mean_dc/mean_ds = 0 handling (lines 874-879)
# ------------------------------------------------------------------------------
test_that(".square_tiling_solver handles zero color distance", {
H <- 4
W <- 4
N <- H * W
# All pixels same color -> mean_dc = 0
A_planar <- c(rep(100, N), rep(100, N), rep(100, N))
B_planar <- c(rep(100, N), rep(100, N), rep(100, N))
result <- couplr:::.square_tiling_solver(
A_planar, B_planar, H, W,
max_tile_size = 2,
alpha = 1, beta = 0
)
expect_equal(length(result), N)
})
test_that(".square_tiling_solver handles different tile sizes", {
H <- 4
W <- 4
N <- H * W
A_planar <- runif(N * 3) * 255
B_planar <- runif(N * 3) * 255
# Test with tile size 1 (pixel-level)
result <- couplr:::.square_tiling_solver(
A_planar, B_planar, H, W,
max_tile_size = 1,
alpha = 1, beta = 0
)
expect_equal(length(result), N)
})
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.