tests/testthat/test-robustness.R

# Tests for robustness.R - comprehensive coverage tests
# Testing all robustness analysis functions, attack simulations, and helper functions

# ==============================================================================
# Test Setup
# ==============================================================================

# Create test networks
skip_on_cran()

set.seed(42)

# Create a simple connected network
mat_simple <- matrix(c(
  0, 1, 1, 0, 0,
  1, 0, 1, 1, 0,
  1, 1, 0, 1, 1,
  0, 1, 1, 0, 1,
  0, 0, 1, 1, 0
), 5, 5, byrow = TRUE)
rownames(mat_simple) <- colnames(mat_simple) <- LETTERS[1:5]

# Create a weighted network
mat_weighted <- matrix(c(
  0.0, 0.8, 0.5, 0.0, 0.0,
  0.8, 0.0, 0.7, 0.3, 0.0,
  0.5, 0.7, 0.0, 0.6, 0.4,
  0.0, 0.3, 0.6, 0.0, 0.9,
  0.0, 0.0, 0.4, 0.9, 0.0
), 5, 5, byrow = TRUE)
rownames(mat_weighted) <- colnames(mat_weighted) <- LETTERS[1:5]

# ==============================================================================
# Test robustness() function - basic functionality
# ==============================================================================

test_that("robustness() works with default parameters (vertex, betweenness)", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_simple)
  expect_s3_class(rob, "cograph_robustness")
  expect_s3_class(rob, "data.frame")
  expect_true("removed_pct" %in% names(rob))
  expect_true("comp_size" %in% names(rob))
  expect_true("comp_pct" %in% names(rob))
  expect_true("measure" %in% names(rob))
  expect_true("type" %in% names(rob))
})

test_that("robustness() returns correct structure", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_simple)

  # Number of rows should be n_vertices + 1 (0 to n removed)
  expect_equal(nrow(rob), 6)

  # removed_pct should range from 0 to 1
  expect_equal(min(rob$removed_pct), 0)
  expect_equal(max(rob$removed_pct), 1)

  # comp_pct should start at 1 and end at 0
  expect_equal(rob$comp_pct[1], 1)
  expect_equal(rob$comp_pct[nrow(rob)], 0)
})

test_that("robustness() has correct attributes", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_simple)

  expect_true(!is.null(attr(rob, "n_original")))
  expect_true(!is.null(attr(rob, "orig_max")))
  expect_equal(attr(rob, "n_original"), 5)
  expect_equal(attr(rob, "orig_max"), 5)
})

# ==============================================================================
# Test robustness() with different measures
# ==============================================================================

test_that("robustness() works with betweenness measure", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_simple, measure = "betweenness")
  expect_s3_class(rob, "cograph_robustness")
  expect_equal(unique(rob$measure), "betweenness")
  expect_true(grepl("Targeted vertex attack", rob$type[1]))
})

test_that("robustness() works with degree measure", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_simple, measure = "degree")
  expect_s3_class(rob, "cograph_robustness")
  expect_equal(unique(rob$measure), "degree")
  expect_true(grepl("Targeted vertex attack", rob$type[1]))
})

test_that("robustness() works with random measure", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_simple, measure = "random", n_iter = 10)
  expect_s3_class(rob, "cograph_robustness")
  expect_equal(unique(rob$measure), "random")
  expect_true(grepl("Random vertex removal", rob$type[1]))
})

# ==============================================================================
# Test robustness() with different types
# ==============================================================================

test_that("robustness() works with vertex type", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_simple, type = "vertex")
  expect_s3_class(rob, "cograph_robustness")
  expect_true(grepl("vertex", rob$type[1]))
})

test_that("robustness() works with edge type and betweenness", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_simple, type = "edge", measure = "betweenness")
  expect_s3_class(rob, "cograph_robustness")
  expect_true(grepl("edge", rob$type[1]))
})

test_that("robustness() works with edge type and random", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_simple, type = "edge", measure = "random", n_iter = 10)
  expect_s3_class(rob, "cograph_robustness")
  expect_true(grepl("Random edge removal", rob$type[1]))
})

test_that("robustness() errors on edge type with degree measure", {
  skip_if_not_installed("igraph")

  expect_error(
    robustness(mat_simple, type = "edge", measure = "degree"),
    "edge attacks"
  )
})

# ==============================================================================
# Test robustness() with seed for reproducibility
# ==============================================================================

test_that("robustness() is reproducible with seed for random", {
  skip_if_not_installed("igraph")

  rob1 <- robustness(mat_simple, measure = "random", n_iter = 20, seed = 123)
  rob2 <- robustness(mat_simple, measure = "random", n_iter = 20, seed = 123)

  expect_equal(rob1$comp_pct, rob2$comp_pct)
})

test_that("robustness() gives different results with different seeds", {
  skip_if_not_installed("igraph")

  rob1 <- robustness(mat_simple, measure = "random", n_iter = 20, seed = 123)
  rob2 <- robustness(mat_simple, measure = "random", n_iter = 20, seed = 456)

  # Results should be different (not all equal)
  expect_false(all(rob1$comp_pct == rob2$comp_pct))
})

# ==============================================================================
# Test robustness() with different input types
# ==============================================================================

test_that("robustness() works with igraph input", {
  skip_if_not_installed("igraph")

  g <- igraph::graph_from_adjacency_matrix(mat_simple, mode = "undirected")
  rob <- robustness(g)

  expect_s3_class(rob, "cograph_robustness")
  expect_equal(nrow(rob), 6)
})

test_that("robustness() works with weighted network", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_weighted)
  expect_s3_class(rob, "cograph_robustness")
  expect_equal(nrow(rob), 6)
})

test_that("robustness() works with cograph_network input", {
  skip_if_not_installed("igraph")

  net <- as_cograph(mat_simple)
  rob <- robustness(net)

  expect_s3_class(rob, "cograph_robustness")
})

# ==============================================================================
# Test robustness() with directed networks
# ==============================================================================

test_that("robustness() works with directed network", {
  skip_if_not_installed("igraph")

  mat_dir <- matrix(c(
    0, 1, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1,
    1, 0, 0, 0
  ), 4, 4, byrow = TRUE)

  g <- igraph::graph_from_adjacency_matrix(mat_dir, mode = "directed")
  rob <- robustness(g, measure = "betweenness")

  expect_s3_class(rob, "cograph_robustness")
})

test_that("robustness() respects mode parameter for degree", {
  skip_if_not_installed("igraph")

  mat_dir <- matrix(c(
    0, 1, 1, 0,
    0, 0, 1, 1,
    0, 0, 0, 1,
    0, 0, 0, 0
  ), 4, 4, byrow = TRUE)

  g <- igraph::graph_from_adjacency_matrix(mat_dir, mode = "directed")
  rob_in <- robustness(g, measure = "degree", mode = "in")
  rob_out <- robustness(g, measure = "degree", mode = "out")

  expect_s3_class(rob_in, "cograph_robustness")
  expect_s3_class(rob_out, "cograph_robustness")
})

# ==============================================================================
# Test robustness_auc() function
# ==============================================================================

test_that("robustness_auc() returns numeric value", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_simple, measure = "betweenness")
  auc <- robustness_auc(rob)

  expect_true(is.numeric(auc))
  expect_length(auc, 1)
})

test_that("robustness_auc() returns value between 0 and 1", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_simple, measure = "betweenness")
  auc <- robustness_auc(rob)

  expect_true(auc >= 0)
  expect_true(auc <= 1)
})

test_that("robustness_auc() gives higher value for random vs targeted attack", {
  skip_if_not_installed("igraph")

  # Scale-free networks are typically more robust to random failures
  g <- igraph::sample_pa(50, m = 2, directed = FALSE)

  rob_btw <- robustness(g, measure = "betweenness")
  rob_rnd <- robustness(g, measure = "random", n_iter = 20, seed = 42)

  auc_btw <- robustness_auc(rob_btw)
  auc_rnd <- robustness_auc(rob_rnd)

  # Random failure should have higher AUC than targeted attack for scale-free
  expect_true(auc_rnd >= auc_btw - 0.1)  # Allow some tolerance
})

test_that("robustness_auc() errors with invalid input", {
  df <- data.frame(x = 1:5, y = 1:5)
  expect_error(robustness_auc(df), "removed_pct")
})

# ==============================================================================
# Test robustness_summary() function
# ==============================================================================

test_that("robustness_summary() works with robustness results", {
  skip_if_not_installed("igraph")

  rob_btw <- robustness(mat_simple, measure = "betweenness")
  rob_deg <- robustness(mat_simple, measure = "degree")

  summary_df <- robustness_summary(rob_btw, rob_deg)

  expect_true(is.data.frame(summary_df))
  expect_true("measure" %in% names(summary_df))
  expect_true("auc" %in% names(summary_df))
  expect_true("critical_50" %in% names(summary_df))
  expect_true("critical_10" %in% names(summary_df))
})

test_that("robustness_summary() works with network input", {
  skip_if_not_installed("igraph")

  summary_df <- robustness_summary(x = mat_simple, n_iter = 10)

  expect_true(is.data.frame(summary_df))
  expect_equal(nrow(summary_df), 3)  # betweenness, degree, random
})

test_that("robustness_summary() respects measures argument", {
  skip_if_not_installed("igraph")

  summary_df <- robustness_summary(x = mat_simple, measures = c("betweenness", "degree"), n_iter = 10)

  expect_equal(nrow(summary_df), 2)
})

test_that("robustness_summary() errors without inputs", {
  expect_error(robustness_summary(), "Provide robustness results")
})

test_that("robustness_summary() contains correct metrics", {
  skip_if_not_installed("igraph")

  rob <- robustness(mat_simple, measure = "betweenness")
  summary_df <- robustness_summary(rob)

  # AUC should match manual calculation
  manual_auc <- robustness_auc(rob)
  expect_equal(summary_df$auc[1], round(manual_auc, 4))
})

# ==============================================================================
# Test plot_robustness() function
# ==============================================================================

test_that("plot_robustness() works with robustness results", {
  skip_if_not_installed("igraph")

  rob1 <- robustness(mat_simple, measure = "betweenness")
  rob2 <- robustness(mat_simple, measure = "degree")

  pdf(NULL)
  result <- plot_robustness(rob1, rob2)
  dev.off()

  expect_true(is.data.frame(result))
  expect_true(all(c("removed_pct", "comp_pct", "measure") %in% names(result)))
})

test_that("plot_robustness() works with network input", {
  skip_if_not_installed("igraph")

  pdf(NULL)
  result <- plot_robustness(x = mat_simple, measures = c("betweenness", "degree"), n_iter = 10)
  dev.off()

  expect_true(is.data.frame(result))
})

test_that("plot_robustness() respects seed parameter", {
  skip_if_not_installed("igraph")

  pdf(NULL)
  result1 <- plot_robustness(x = mat_simple, measures = c("random"), n_iter = 10, seed = 123)
  dev.off()

  pdf(NULL)
  result2 <- plot_robustness(x = mat_simple, measures = c("random"), n_iter = 10, seed = 123)
  dev.off()

  expect_equal(result1$comp_pct, result2$comp_pct)
})

test_that("plot_robustness() errors without inputs", {
  expect_error(plot_robustness(), "Provide robustness results")
})

test_that("plot_robustness() respects custom colors", {
  skip_if_not_installed("igraph")

  custom_colors <- c(betweenness = "purple", degree = "orange")

  pdf(NULL)
  result <- plot_robustness(x = mat_simple, measures = c("betweenness", "degree"),
                            colors = custom_colors)
  dev.off()

  expect_true(is.data.frame(result))
})

test_that("plot_robustness() handles custom title and labels", {
  skip_if_not_installed("igraph")

  pdf(NULL)
  result <- plot_robustness(
    x = mat_simple,
    measures = c("betweenness"),
    title = "Custom Title",
    xlab = "Nodes Removed",
    ylab = "Component Size"
  )
  dev.off()

  expect_true(is.data.frame(result))
})

# ==============================================================================
# Test ggplot_robustness() function
# ==============================================================================

test_that("ggplot_robustness() works with named networks", {
  skip_if_not_installed("igraph")
  skip_if_not_installed("ggplot2")

  g1 <- igraph::sample_pa(30, m = 2, directed = FALSE)
  g2 <- igraph::sample_gnp(30, 0.15)

  p <- ggplot_robustness("Network A" = g1, "Network B" = g2, n_iter = 10)

  expect_s3_class(p, "ggplot")
})

test_that("ggplot_robustness() works with networks list", {
  skip_if_not_installed("igraph")
  skip_if_not_installed("ggplot2")

  g1 <- igraph::sample_pa(30, m = 2, directed = FALSE)
  g2 <- igraph::sample_gnp(30, 0.15)

  p <- ggplot_robustness(networks = list("A" = g1, "B" = g2), n_iter = 10)

  expect_s3_class(p, "ggplot")
})

test_that("ggplot_robustness() works with single network", {
  skip_if_not_installed("igraph")
  skip_if_not_installed("ggplot2")

  g <- igraph::sample_pa(30, m = 2, directed = FALSE)

  p <- ggplot_robustness("Test Network" = g, n_iter = 10)

  expect_s3_class(p, "ggplot")
})

test_that("ggplot_robustness() respects ncol parameter", {
  skip_if_not_installed("igraph")
  skip_if_not_installed("ggplot2")

  g1 <- igraph::sample_pa(20, m = 2, directed = FALSE)
  g2 <- igraph::sample_gnp(20, 0.15)
  g3 <- igraph::sample_smallworld(1, 20, 3, 0.1)

  p <- ggplot_robustness("A" = g1, "B" = g2, "C" = g3, n_iter = 10, ncol = 3)

  expect_s3_class(p, "ggplot")
})

test_that("ggplot_robustness() respects free_y parameter", {
  skip_if_not_installed("igraph")
  skip_if_not_installed("ggplot2")

  g1 <- igraph::sample_pa(30, m = 2, directed = FALSE)
  g2 <- igraph::sample_gnp(30, 0.15)

  p <- ggplot_robustness("A" = g1, "B" = g2, n_iter = 10, free_y = TRUE)

  expect_s3_class(p, "ggplot")
})

test_that("ggplot_robustness() respects custom colors", {
  skip_if_not_installed("igraph")
  skip_if_not_installed("ggplot2")

  g <- igraph::sample_pa(30, m = 2, directed = FALSE)
  custom_colors <- c(Betweenness = "purple", Degree = "orange", Random = "cyan")

  p <- ggplot_robustness("Test" = g, colors = custom_colors, n_iter = 10)

  expect_s3_class(p, "ggplot")
})

test_that("ggplot_robustness() respects title parameter", {
  skip_if_not_installed("igraph")
  skip_if_not_installed("ggplot2")

  g <- igraph::sample_pa(30, m = 2, directed = FALSE)

  p <- ggplot_robustness("Test" = g, title = "Custom Title", n_iter = 10)

  expect_s3_class(p, "ggplot")
})

test_that("ggplot_robustness() respects measures parameter", {
  skip_if_not_installed("igraph")
  skip_if_not_installed("ggplot2")

  g <- igraph::sample_pa(30, m = 2, directed = FALSE)

  p <- ggplot_robustness("Test" = g, measures = c("betweenness"), n_iter = 10)

  expect_s3_class(p, "ggplot")
})

test_that("ggplot_robustness() errors without networks", {
  skip_if_not_installed("ggplot2")

  expect_error(ggplot_robustness(), "Provide at least one network")
})

test_that("ggplot_robustness() requires ggplot2", {
  # This is hard to test directly since we can't unload packages,

  # but we can verify the check exists in the code
  expect_true(TRUE)
})

# ==============================================================================
# Test internal helper functions
# ==============================================================================

test_that("robustness_vertex_attack handles targeted attack correctly", {
  skip_if_not_installed("igraph")

  g <- igraph::graph_from_adjacency_matrix(mat_simple, mode = "undirected")
  orig_max <- max(igraph::components(g)$csize)
  n <- igraph::vcount(g)

  result <- cograph:::robustness_vertex_attack(g, "betweenness", "all", 10, orig_max, n)

  expect_true(is.numeric(result))
  expect_length(result, n)
  expect_equal(result[1], orig_max)  # First value should be original max
})

test_that("robustness_vertex_attack handles random attack correctly", {
  skip_if_not_installed("igraph")

  g <- igraph::graph_from_adjacency_matrix(mat_simple, mode = "undirected")
  orig_max <- max(igraph::components(g)$csize)
  n <- igraph::vcount(g)

  set.seed(42)
  result <- cograph:::robustness_vertex_attack(g, "random", "all", 20, orig_max, n)

  expect_true(is.numeric(result))
  expect_length(result, n)
})

test_that("robustness_edge_attack handles targeted attack correctly", {
  skip_if_not_installed("igraph")

  g <- igraph::graph_from_adjacency_matrix(mat_simple, mode = "undirected")
  orig_max <- max(igraph::components(g)$csize)
  n <- igraph::ecount(g)

  result <- cograph:::robustness_edge_attack(g, "betweenness", 10, orig_max, n)

  expect_true(is.numeric(result))
  expect_length(result, n)
})

test_that("robustness_edge_attack handles random attack correctly", {
  skip_if_not_installed("igraph")

  g <- igraph::graph_from_adjacency_matrix(mat_simple, mode = "undirected")
  orig_max <- max(igraph::components(g)$csize)
  n <- igraph::ecount(g)

  set.seed(42)
  result <- cograph:::robustness_edge_attack(g, "random", 20, orig_max, n)

  expect_true(is.numeric(result))
  expect_length(result, n)
})

# ==============================================================================
# Test edge cases and special scenarios
# ==============================================================================

test_that("robustness() handles small network", {
  skip_if_not_installed("igraph")

  mat_tiny <- matrix(c(0, 1, 1, 0), 2, 2)
  rob <- robustness(mat_tiny)

  expect_s3_class(rob, "cograph_robustness")
  expect_equal(nrow(rob), 3)  # 0, 1, 2 vertices removed
})

test_that("robustness() handles sparse network", {
  skip_if_not_installed("igraph")

  # Create a sparse network (tree-like structure)
  mat_sparse <- matrix(0, 5, 5)
  mat_sparse[1, 2] <- mat_sparse[2, 1] <- 1
  mat_sparse[2, 3] <- mat_sparse[3, 2] <- 1
  mat_sparse[3, 4] <- mat_sparse[4, 3] <- 1
  mat_sparse[4, 5] <- mat_sparse[5, 4] <- 1

  rob <- robustness(mat_sparse)

  expect_s3_class(rob, "cograph_robustness")
})

test_that("robustness() handles dense network", {
  skip_if_not_installed("igraph")

  # Create a fully connected network
  mat_dense <- matrix(1, 5, 5)
  diag(mat_dense) <- 0

  rob <- robustness(mat_dense)

  expect_s3_class(rob, "cograph_robustness")
})

test_that("robustness() handles network with single edge", {
  skip_if_not_installed("igraph")

  mat_single <- matrix(c(0, 1, 1, 0), 2, 2)
  rob <- robustness(mat_single, type = "edge")

  expect_s3_class(rob, "cograph_robustness")
  expect_equal(nrow(rob), 2)  # 0, 1 edges removed
})

test_that("robustness() handles network with hub node", {
  skip_if_not_installed("igraph")

  # Star network - node 1 is hub
  mat_star <- matrix(0, 5, 5)
  mat_star[1, 2:5] <- 1
  mat_star[2:5, 1] <- 1

  rob_btw <- robustness(mat_star, measure = "betweenness")
  rob_deg <- robustness(mat_star, measure = "degree")

  expect_s3_class(rob_btw, "cograph_robustness")
  expect_s3_class(rob_deg, "cograph_robustness")

  # Hub removal should cause rapid fragmentation
  expect_true(rob_btw$comp_pct[2] < rob_btw$comp_pct[1])
})

# ==============================================================================
# Test integration with scale-free and random networks
# ==============================================================================

test_that("scale-free networks are vulnerable to targeted attacks", {
  skip_if_not_installed("igraph")

  # Scale-free network
  g <- igraph::sample_pa(50, m = 2, directed = FALSE)

  rob_btw <- robustness(g, measure = "betweenness")
  rob_rnd <- robustness(g, measure = "random", n_iter = 20, seed = 42)

  auc_btw <- robustness_auc(rob_btw)
  auc_rnd <- robustness_auc(rob_rnd)

  # Scale-free should typically show: random AUC > targeted AUC
  # Due to hub vulnerability
  expect_true(auc_rnd > auc_btw * 0.8)  # Allow some tolerance
})

test_that("random networks degrade more uniformly", {
  skip_if_not_installed("igraph")

  # Erdos-Renyi random network
  g <- igraph::sample_gnp(50, 0.15)

  rob_btw <- robustness(g, measure = "betweenness")
  rob_rnd <- robustness(g, measure = "random", n_iter = 20, seed = 42)

  auc_btw <- robustness_auc(rob_btw)
  auc_rnd <- robustness_auc(rob_rnd)

  # Random networks should show more similar AUC for both strategies
  expect_true(abs(auc_btw - auc_rnd) < 0.3)
})

test_that("small-world networks show moderate robustness", {
  skip_if_not_installed("igraph")

  # Small-world network
  g <- igraph::sample_smallworld(dim = 1, size = 50, nei = 3, p = 0.1)

  rob <- robustness(g, measure = "betweenness")
  auc <- robustness_auc(rob)

  expect_true(is.numeric(auc))
  expect_true(auc > 0)
  expect_true(auc < 1)
})

# ==============================================================================
# Test match.arg behavior
# ==============================================================================

test_that("robustness() validates type argument", {
  skip_if_not_installed("igraph")

  expect_error(robustness(mat_simple, type = "invalid"))
})

test_that("robustness() validates measure argument", {
  skip_if_not_installed("igraph")

  expect_error(robustness(mat_simple, measure = "invalid"))
})

# ==============================================================================
# Coverage: break guards, gray50 fallback, unnamed networks, seed in ggplot
# ==============================================================================

test_that("robustness vertex break guards fire on 2-node network", {
  skip_if_not_installed("igraph")

  # 2-node graph: seq_len(n-1) = seq_len(1), so loop runs once and

  # the break condition is checked on next iteration
  mat2 <- matrix(c(0, 1, 1, 0), 2, 2)
  rob_btw <- robustness(mat2, measure = "betweenness")
  expect_s3_class(rob_btw, "cograph_robustness")

  rob_rnd <- robustness(mat2, measure = "random", n_iter = 5, seed = 1)
  expect_s3_class(rob_rnd, "cograph_robustness")
})

test_that("robustness edge break guards fire on single-edge network", {
  skip_if_not_installed("igraph")

  mat2 <- matrix(c(0, 1, 1, 0), 2, 2)
  rob_edge_btw <- robustness(mat2, type = "edge", measure = "betweenness")
  expect_s3_class(rob_edge_btw, "cograph_robustness")

  rob_edge_rnd <- robustness(mat2, type = "edge", measure = "random",
                              n_iter = 5, seed = 1)
  expect_s3_class(rob_edge_rnd, "cograph_robustness")
})

test_that("plot_robustness gray50 fallback for unknown measure", {
  skip_if_not_installed("igraph")

  # Create a result with a non-standard measure name
  rob <- robustness(mat_simple, measure = "betweenness")
  rob$measure <- "custom_measure"

  pdf(NULL)
  result <- plot_robustness(rob)
  dev.off()

  expect_true(is.data.frame(result))
})

test_that("ggplot_robustness with unnamed networks", {
  skip_if_not_installed("igraph")
  skip_if_not_installed("ggplot2")

  g <- igraph::sample_pa(20, m = 2, directed = FALSE)
  p <- ggplot_robustness(networks = list(g), n_iter = 5)

  expect_s3_class(p, "ggplot")
})

test_that("ggplot_robustness with seed parameter", {
  skip_if_not_installed("igraph")
  skip_if_not_installed("ggplot2")

  g <- igraph::sample_pa(20, m = 2, directed = FALSE)
  p <- ggplot_robustness("Test" = g, measures = c("random"),
                          n_iter = 5, seed = 42)

  expect_s3_class(p, "ggplot")
})

# ==============================================================================
# Test strategy parameter (sequential vs static)
# ==============================================================================

test_that("static strategy produces different results from sequential", {
  set.seed(42)
  g <- igraph::sample_pa(30, m = 2, directed = FALSE)

  rob_seq <- robustness(g, measure = "betweenness", strategy = "sequential")
  rob_sta <- robustness(g, measure = "betweenness", strategy = "static")

  # Both should have same structure
  expect_equal(nrow(rob_seq), nrow(rob_sta))
  expect_equal(rob_seq$removed_pct, rob_sta$removed_pct)

  # But different comp_pct (sequential is a stronger attack)
  expect_false(identical(rob_seq$comp_pct, rob_sta$comp_pct))

  # Sequential AUC should be <= static AUC (stronger attack = lower AUC)
  expect_true(robustness_auc(rob_seq) <= robustness_auc(rob_sta) + 1e-10)
})

test_that("static strategy works with degree measure", {
  set.seed(42)
  g <- igraph::sample_pa(20, m = 2, directed = FALSE)

  rob <- robustness(g, measure = "degree", strategy = "static")

  expect_s3_class(rob, "cograph_robustness")
  expect_equal(rob$comp_pct[1], 1.0)
  expect_equal(rob$comp_pct[nrow(rob)], 0.0)
})

test_that("static strategy matches brainGraph output", {
  skip_if_not_installed("brainGraph")

  set.seed(99)
  g <- igraph::sample_gnm(25, 50, directed = FALSE)
  igraph::V(g)$name <- paste0("V", seq_len(igraph::vcount(g)))

  cg <- robustness(g, measure = "betweenness", strategy = "static")
  bg <- brainGraph::robustness(g, type = "vertex", measure = "btwn.cent")

  expect_equal(cg$comp_pct, bg$comp.pct)
})

test_that("static strategy with degree matches brainGraph", {
  skip_if_not_installed("brainGraph")

  set.seed(99)
  g <- igraph::sample_gnm(25, 50, directed = FALSE)
  igraph::V(g)$name <- paste0("V", seq_len(igraph::vcount(g)))

  cg <- robustness(g, measure = "degree", strategy = "static")
  bg <- brainGraph::robustness(g, type = "vertex", measure = "degree")

  expect_equal(cg$comp_pct, bg$comp.pct)
})

test_that("strategy does not affect random measure", {
  set.seed(42)
  g <- igraph::sample_pa(15, m = 2, directed = FALSE)

  rob_seq <- robustness(g, measure = "random", strategy = "sequential",
                         n_iter = 20, seed = 123)
  rob_sta <- robustness(g, measure = "random", strategy = "static",
                         n_iter = 20, seed = 123)

  expect_equal(rob_seq$comp_pct, rob_sta$comp_pct)
})

test_that("static edge attack works", {
  set.seed(42)
  g <- igraph::sample_pa(15, m = 2, directed = FALSE)

  rob <- robustness(g, type = "edge", measure = "betweenness", strategy = "static")

  expect_s3_class(rob, "cograph_robustness")
  expect_equal(rob$comp_pct[1], 1.0)
  expect_equal(rob$comp_pct[nrow(rob)], 0.0)
})

test_that("ggplot_robustness forwards strategy parameter", {
  set.seed(42)
  g <- igraph::sample_pa(15, m = 2, directed = FALSE)

  p <- ggplot_robustness("Test" = g, strategy = "static",
                          measures = c("betweenness"), n_iter = 5)
  expect_s3_class(p, "ggplot")
})

test_that("static works on matrix input", {
  mat <- matrix(c(
    0.0, 0.5, 0.1,
    0.3, 0.0, 0.4,
    0.1, 0.2, 0.0
  ), nrow = 3, byrow = TRUE)
  rownames(mat) <- colnames(mat) <- c("A", "B", "C")

  rob <- robustness(mat, measure = "betweenness", strategy = "static")

  expect_s3_class(rob, "cograph_robustness")
  expect_equal(nrow(rob), 4)  # 3 nodes + 1
})

# ==============================================================================
# Summary
# ==============================================================================

cat("\n=== All Robustness Tests Passed ===\n")

Try the cograph package in your browser

Any scripts or data that you put into this service are public.

cograph documentation built on April 1, 2026, 1:07 a.m.