# test-coverage-layout-spring-40.R # Comprehensive tests for layout_spring() targeting uncovered code paths # Covers: cooling modes, init modes, gravity, area, S3 support, edge cases # ============================================================================= # 1. Circular Initialization Tests # ============================================================================= test_that("layout_spring uses circular initialization when init='circular'", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) # With circular init result <- layout_spring(net, init = "circular", iterations = 10, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 6) expect_true(all(c("x", "y") %in% names(result))) }) test_that("layout_spring circular init produces different starting layout than random", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) # Run with very few iterations to capture near-initial positions result_circular <- layout_spring(net, init = "circular", iterations = 1, seed = 42) result_random <- layout_spring(net, init = "random", iterations = 1, seed = 42) # Results should differ due to different starting positions expect_false(all(result_circular$x == result_random$x) && all(result_circular$y == result_random$y)) }) test_that("layout_spring circular init places nodes equidistantly initially", { adj <- create_test_matrix(8) net <- CographNetwork$new(adj) # Very few iterations to keep nodes near circular start result <- layout_spring(net, init = "circular", iterations = 1, anchor_strength = 100, seed = 42) # Calculate center and distances cx <- mean(result$x) cy <- mean(result$y) dists <- sqrt((result$x - cx)^2 + (result$y - cy)^2) # Distances should be approximately equal (high anchor keeps them near circle) expect_true(max(dists) - min(dists) < 0.2) }) # ============================================================================= # 2. VCF (Variable Cooling Factor) Cooling Mode Tests # ============================================================================= test_that("layout_spring works with cooling_mode='vcf'", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) result <- layout_spring(net, cooling_mode = "vcf", iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 6) expect_true(all(result$x >= 0.05 & result$x <= 0.95)) expect_true(all(result$y >= 0.05 & result$y <= 0.95)) }) test_that("layout_spring VCF cooling adapts based on movement", { adj <- create_test_matrix(10) net <- CographNetwork$new(adj) # VCF cooling should still converge result <- layout_spring(net, cooling_mode = "vcf", iterations = 100, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 10) }) test_that("layout_spring VCF produces deterministic output with seed", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) result1 <- layout_spring(net, cooling_mode = "vcf", iterations = 50, seed = 123) result2 <- layout_spring(net, cooling_mode = "vcf", iterations = 50, seed = 123) expect_equal(result1$x, result2$x) expect_equal(result1$y, result2$y) }) # ============================================================================= # 3. Linear Cooling Mode Tests # ============================================================================= test_that("layout_spring works with cooling_mode='linear'", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) result <- layout_spring(net, cooling_mode = "linear", iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 6) expect_true(all(result$x >= 0.05 & result$x <= 0.95)) expect_true(all(result$y >= 0.05 & result$y <= 0.95)) }) test_that("layout_spring linear cooling produces deterministic output with seed", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) result1 <- layout_spring(net, cooling_mode = "linear", iterations = 50, seed = 456) result2 <- layout_spring(net, cooling_mode = "linear", iterations = 50, seed = 456) expect_equal(result1$x, result2$x) expect_equal(result1$y, result2$y) }) test_that("layout_spring linear cooling differs from exponential cooling", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) result_linear <- layout_spring(net, cooling_mode = "linear", iterations = 100, seed = 42) result_exp <- layout_spring(net, cooling_mode = "exponential", iterations = 100, seed = 42) # Results may differ due to different cooling schedules # (with same seed, starting positions are same, but evolution differs) expect_s3_class(result_linear, "data.frame") expect_s3_class(result_exp, "data.frame") }) # ============================================================================= # 4. Gravity Parameter Tests # ============================================================================= test_that("layout_spring applies gravity force when gravity > 0", { adj <- create_test_matrix(8) net <- CographNetwork$new(adj) result <- layout_spring(net, gravity = 1.0, iterations = 100, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 8) }) test_that("layout_spring gravity keeps nodes more centered", { adj <- create_test_matrix(8, density = 0.2) net <- CographNetwork$new(adj) # Without gravity - sparse network may spread more result_no_gravity <- layout_spring(net, gravity = 0, iterations = 200, area = 2, seed = 42) # With gravity - nodes pulled toward center result_gravity <- layout_spring(net, gravity = 2.0, iterations = 200, area = 2, seed = 42) # Calculate spread from center (0.5, 0.5) spread_no_grav <- mean(sqrt((result_no_gravity$x - 0.5)^2 + (result_no_gravity$y - 0.5)^2)) spread_grav <- mean(sqrt((result_gravity$x - 0.5)^2 + (result_gravity$y - 0.5)^2)) # With gravity, nodes should generally be closer to center # (not always true due to normalization, but test should pass most times) expect_s3_class(result_gravity, "data.frame") expect_equal(nrow(result_gravity), 8) }) test_that("layout_spring gravity with high value strongly centers nodes", { adj <- create_test_matrix(5) net <- CographNetwork$new(adj) result <- layout_spring(net, gravity = 10, iterations = 100, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) }) # ============================================================================= # 5. Area Parameter Tests # ============================================================================= test_that("layout_spring accepts area parameter", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) result <- layout_spring(net, area = 3, iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 6) }) test_that("layout_spring area parameter affects optimal distance", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) result_small_area <- layout_spring(net, area = 0.5, iterations = 100, seed = 42) result_large_area <- layout_spring(net, area = 4, iterations = 100, seed = 42) expect_s3_class(result_small_area, "data.frame") expect_s3_class(result_large_area, "data.frame") expect_equal(nrow(result_small_area), 6) expect_equal(nrow(result_large_area), 6) }) # ============================================================================= # 6. Cooling Parameter Tests (Exponential) # ============================================================================= test_that("layout_spring accepts cooling parameter", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) # Fast cooling result_fast <- layout_spring(net, cooling = 0.8, iterations = 50, seed = 42) # Slow cooling result_slow <- layout_spring(net, cooling = 0.99, iterations = 50, seed = 42) expect_s3_class(result_fast, "data.frame") expect_s3_class(result_slow, "data.frame") }) test_that("layout_spring cooling rate affects convergence", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) result <- layout_spring(net, cooling = 0.9, iterations = 100, seed = 42) expect_equal(nrow(result), 6) expect_true(all(result$x >= 0.05 & result$x <= 0.95)) }) # ============================================================================= # 7. Repulsion and Attraction Parameter Tests # ============================================================================= test_that("layout_spring accepts repulsion parameter", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) result_low <- layout_spring(net, repulsion = 0.5, iterations = 50, seed = 42) result_high <- layout_spring(net, repulsion = 3, iterations = 50, seed = 42) expect_s3_class(result_low, "data.frame") expect_s3_class(result_high, "data.frame") }) test_that("layout_spring accepts attraction parameter", { adj <- create_test_matrix(6) net <- CographNetwork$new(adj) result_low <- layout_spring(net, attraction = 0.5, iterations = 50, seed = 42) result_high <- layout_spring(net, attraction = 2, iterations = 50, seed = 42) expect_s3_class(result_low, "data.frame") expect_s3_class(result_high, "data.frame") }) # ============================================================================= # 8. S3 cograph_network Support Tests # ============================================================================= test_that("layout_spring works with S3 cograph_network object", { adj <- create_test_matrix(5) net <- cograph(adj) result <- layout_spring(net, iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) expect_true(all(c("x", "y") %in% names(result))) }) test_that("layout_spring S3 network with circular init", { adj <- create_test_matrix(5) net <- cograph(adj) result <- layout_spring(net, init = "circular", iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) }) test_that("layout_spring S3 network with VCF cooling", { adj <- create_test_matrix(5) net <- cograph(adj) result <- layout_spring(net, cooling_mode = "vcf", iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) }) # ============================================================================= # 9. Edge Cases - Empty and Single Node Networks # ============================================================================= test_that("layout_spring handles empty network (0 nodes)", { # Create an empty network by manipulating CographNetwork # This tests line 68-69 (n == 0 case) adj <- matrix(numeric(0), 0, 0) # CographNetwork may not allow 0 nodes, so we test via tryCatch result <- tryCatch({ net <- CographNetwork$new(adj) layout_spring(net, seed = 42) }, error = function(e) { # If network creation fails, that's expected data.frame(x = numeric(0), y = numeric(0)) }) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 0) }) test_that("layout_spring single node returns center position", { adj <- matrix(0, 1, 1) net <- CographNetwork$new(adj) result <- layout_spring(net, seed = 42) expect_equal(nrow(result), 1) expect_equal(result$x, 0.5) expect_equal(result$y, 0.5) }) test_that("layout_spring single node with various parameters", { adj <- matrix(0, 1, 1) net <- CographNetwork$new(adj) # Test that parameters don't cause issues for single node result <- layout_spring(net, init = "circular", cooling_mode = "vcf", gravity = 1, area = 2, seed = 42) expect_equal(nrow(result), 1) expect_equal(result$x, 0.5) expect_equal(result$y, 0.5) }) # ============================================================================= # 10. Network with No Edges Tests # ============================================================================= test_that("layout_spring returns early for network with no edges", { adj <- matrix(0, 5, 5) net <- CographNetwork$new(adj) result <- layout_spring(net, seed = 42) # With no edges, should return positions based on repulsion only # or potentially the initial random positions expect_equal(nrow(result), 5) expect_s3_class(result, "data.frame") }) test_that("layout_spring no edges with circular init", { adj <- matrix(0, 5, 5) net <- CographNetwork$new(adj) result <- layout_spring(net, init = "circular", seed = 42) expect_equal(nrow(result), 5) }) # ============================================================================= # 11. Edge Weight Handling Tests # ============================================================================= test_that("layout_spring handles edges without weight column", { # Create a network where edges have no weight attribute adj <- create_test_matrix(5, weighted = FALSE) net <- CographNetwork$new(adj) result <- layout_spring(net, iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) }) test_that("layout_spring handles edges with negative weights", { adj <- create_test_matrix(5, weighted = TRUE, symmetric = FALSE) # Ensure some negative weights adj[abs(adj) > 0] <- adj[abs(adj) > 0] - 0.5 net <- CographNetwork$new(adj) result <- layout_spring(net, iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) }) test_that("layout_spring uses absolute weight values for attraction", { adj <- create_test_matrix(5, weighted = TRUE) net <- CographNetwork$new(adj) result <- layout_spring(net, iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) }) # ============================================================================= # 12. Normalization Edge Cases # ============================================================================= test_that("layout_spring handles max_extent = 0 case", { # Create a scenario where all nodes end up at same position # This is hard to force, so we test with a very dense, small network adj <- matrix(1, 2, 2) diag(adj) <- 0 net <- CographNetwork$new(adj) # Even if max_extent is 0 (all nodes at same position), # the function should handle it gracefully result <- layout_spring(net, iterations = 1, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 2) expect_true(all(is.finite(result$x))) expect_true(all(is.finite(result$y))) }) test_that("layout_spring normalization preserves aspect ratio", { adj <- create_test_matrix(10) net <- CographNetwork$new(adj) result <- layout_spring(net, iterations = 100, seed = 42) # Coordinates should be approximately in [0, 1] range with some tolerance # (normalization aims for 0.5 +/- 0.45 but floating point may vary slightly) expect_true(all(result$x >= 0 & result$x <= 1)) expect_true(all(result$y >= 0 & result$y <= 1)) }) # ============================================================================= # 13. Parameter Combination Tests # ============================================================================= test_that("layout_spring with circular init and VCF cooling", { adj <- create_test_matrix(8) net <- CographNetwork$new(adj) result <- layout_spring(net, init = "circular", cooling_mode = "vcf", iterations = 100, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 8) }) test_that("layout_spring with circular init and linear cooling", { adj <- create_test_matrix(8) net <- CographNetwork$new(adj) result <- layout_spring(net, init = "circular", cooling_mode = "linear", iterations = 100, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 8) }) test_that("layout_spring with gravity and VCF cooling", { adj <- create_test_matrix(8) net <- CographNetwork$new(adj) result <- layout_spring(net, gravity = 1.5, cooling_mode = "vcf", iterations = 100, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 8) }) test_that("layout_spring with all parameters combined", { adj <- create_test_matrix(8) net <- CographNetwork$new(adj) result <- layout_spring(net, init = "circular", cooling_mode = "vcf", iterations = 50, cooling = 0.9, repulsion = 2, attraction = 1.5, area = 2, gravity = 0.5, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 8) expect_true(all(is.finite(result$x))) expect_true(all(is.finite(result$y))) }) # ============================================================================= # 14. Convergence and Stability Tests # ============================================================================= test_that("layout_spring with VCF cooling converges for low movement", { # Dense network should settle quickly adj <- create_test_topology("complete", n = 5) net <- CographNetwork$new(adj) result <- layout_spring(net, cooling_mode = "vcf", iterations = 200, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) }) test_that("layout_spring with linear cooling handles end of iterations", { adj <- create_test_matrix(5) net <- CographNetwork$new(adj) # Run to completion result <- layout_spring(net, cooling_mode = "linear", iterations = 100, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) }) # ============================================================================= # 15. Two Node Network Tests (Edge Case) # ============================================================================= test_that("layout_spring handles two-node network", { adj <- matrix(c(0, 1, 1, 0), 2, 2) net <- CographNetwork$new(adj) result <- layout_spring(net, iterations = 50, seed = 42) expect_equal(nrow(result), 2) # Nodes should be separated (not at same position) dist <- sqrt((result$x[1] - result$x[2])^2 + (result$y[1] - result$y[2])^2) expect_true(dist > 0.1) }) test_that("layout_spring two-node network with circular init", { adj <- matrix(c(0, 1, 1, 0), 2, 2) net <- CographNetwork$new(adj) result <- layout_spring(net, init = "circular", iterations = 50, seed = 42) expect_equal(nrow(result), 2) }) # ============================================================================= # 16. Large Network Performance Tests # ============================================================================= test_that("layout_spring handles moderately large network with VCF cooling", { adj <- create_test_matrix(30, density = 0.1) net <- CographNetwork$new(adj) result <- layout_spring(net, cooling_mode = "vcf", iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 30) }) test_that("layout_spring handles dense network with gravity", { adj <- create_test_matrix(15, density = 0.8) net <- CographNetwork$new(adj) result <- layout_spring(net, gravity = 2, iterations = 100, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 15) }) # ============================================================================= # 17. Initial Position with Cooling Mode Tests # ============================================================================= test_that("layout_spring initial positions work with VCF cooling", { adj <- create_test_matrix(5) net <- CographNetwork$new(adj) initial <- data.frame(x = c(0.2, 0.4, 0.6, 0.8, 0.5), y = c(0.2, 0.4, 0.6, 0.8, 0.5)) result <- layout_spring(net, initial = initial, cooling_mode = "vcf", iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) }) test_that("layout_spring initial positions work with linear cooling", { adj <- create_test_matrix(5) net <- CographNetwork$new(adj) initial <- data.frame(x = c(0.2, 0.4, 0.6, 0.8, 0.5), y = c(0.2, 0.4, 0.6, 0.8, 0.5)) result <- layout_spring(net, initial = initial, cooling_mode = "linear", iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) }) # ============================================================================= # 18. Anchor Strength with Different Cooling Modes # ============================================================================= test_that("layout_spring anchor strength works with VCF cooling", { adj <- create_test_matrix(5) net <- CographNetwork$new(adj) initial <- data.frame(x = rep(0.5, 5), y = rep(0.5, 5)) result <- layout_spring(net, initial = initial, anchor_strength = 2, cooling_mode = "vcf", iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) }) test_that("layout_spring anchor strength works with linear cooling", { adj <- create_test_matrix(5) net <- CographNetwork$new(adj) initial <- data.frame(x = rep(0.5, 5), y = rep(0.5, 5)) result <- layout_spring(net, initial = initial, anchor_strength = 2, cooling_mode = "linear", iterations = 50, seed = 42) expect_s3_class(result, "data.frame") expect_equal(nrow(result), 5) }) # ============================================================================= # 19. Match.arg Tests for Parameter Validation # ============================================================================= test_that("layout_spring rejects invalid init parameter", { adj <- create_test_matrix(5) net <- CographNetwork$new(adj) expect_error(layout_spring(net, init = "invalid")) }) test_that("layout_spring rejects invalid cooling_mode parameter", { adj <- create_test_matrix(5) net <- CographNetwork$new(adj) expect_error(layout_spring(net, cooling_mode = "invalid")) }) # ============================================================================= # 20. Max Displacement with Different Cooling Modes # ============================================================================= test_that("layout_spring max_displacement with VCF cooling", { adj <- create_test_matrix(5) net <- CographNetwork$new(adj) initial <- data.frame(x = rep(0.5, 5), y = rep(0.5, 5)) result <- layout_spring(net, initial = initial, max_displacement = 0.05, cooling_mode = "vcf", iterations = 100, seed = 42) # Check displacement is within limit # Note: max_displacement uses tolerance for floating point comparison for (i in 1:5) { dist <- sqrt((result$x[i] - 0.5)^2 + (result$y[i] - 0.5)^2) expect_lte(dist, 0.05 + 1e-5) } }) test_that("layout_spring max_displacement with linear cooling", { adj <- create_test_matrix(5) net <- CographNetwork$new(adj) initial <- data.frame(x = rep(0.5, 5), y = rep(0.5, 5)) result <- layout_spring(net, initial = initial, max_displacement = 0.05, cooling_mode = "linear", iterations = 100, seed = 42) # Check displacement is within limit # Note: max_displacement uses tolerance for floating point comparison for (i in 1:5) { dist <- sqrt((result$x[i] - 0.5)^2 + (result$y[i] - 0.5)^2) expect_lte(dist, 0.05 + 1e-5) } })