diff --git a/tests/testthat/_snaps/other.md b/tests/testthat/_snaps/other.md index 44bab0e8ee5..2f5090f3c93 100644 --- a/tests/testthat/_snaps/other.md +++ b/tests/testthat/_snaps/other.md @@ -26,7 +26,7 @@ # VS/ES require explicit conversion Code - V(karate_oldstyle) + V(karate_oldstyle_graph()) Condition Error in `warn_version()`: ! This graph was created by a now unsupported old igraph version. diff --git a/tests/testthat/helper-test-functions.R b/tests/testthat/helper-test-functions.R index 692f0d1a345..ed96615d253 100644 --- a/tests/testthat/helper-test-functions.R +++ b/tests/testthat/helper-test-functions.R @@ -17,43 +17,78 @@ standardize_eigen_signs <- function(x) { }) } -order_by_magnitude <- function(x) { +order_eigenvalues_by_magnitude <- function(x) { order(abs(x), sign(x), decreasing = TRUE) } -sort_by_magnitude <- function(x) { - x[order_by_magnitude(x)] +sort_eigenvalues_by_magnitude <- function(x) { + x[order_eigenvalues_by_magnitude(x)] } # ---- test-operators.R ------------------------------------------------------- -order_by_two_first_columns <- function(x) x[order(x[, 1], x[, 2]), ] +sort_edgelist_rows <- function(x) x[order(x[, 1], x[, 2]), ] -rn <- function(D) { +name_rows_by_edge_endpoints <- function(D) { rownames(D) <- paste(D[, 1], D[, 2], sep = "-") D } -# Input/expected pairs for the unique()-on-vertex-sequence tests. -unique_tests <- list( - list(1:5, 1:5), - list(c(1, 1, 2:5), 1:5), - list(c(1, 1, 1, 1), 1), - list(c(1, 2, 2, 2), 1:2), - list(c(2, 2, 1, 1), 2:1), - list(c(1, 2, 1, 2), 1:2), - list(c(), c()) -) +# Input/expected vertex-index pairs for the unique()-on-vertex-sequence tests: +# each element is `list(, )`. +unique_vertex_seq_cases <- function() { + list( + list(1:5, 1:5), + list(c(1, 1, 2:5), 1:5), + list(c(1, 1, 1, 1), 1), + list(c(1, 2, 2, 2), 1:2), + list(c(2, 2, 1, 1), 2:1), + list(c(1, 2, 1, 2), 1:2), + list(c(), c()) + ) +} + +# A fresh already-simple graph with two edge attributes, one of which the +# default combination list keeps and one of which it drops. Each caller needs +# its own: the C core's property cache lives in the graph object, and merely +# asking whether the graph is simple changes what simplifying it does. +simple_graph_with_edge_attrs <- function() { + g <- make_graph(c(1, 2, 2, 3), directed = FALSE) + E(g)$weight <- c(1, 2) + E(g)$foo <- c("a", "b") + g +} # ---- test-glet.R ------------------------------------------------------------ -sortgl <- function(x) { +# Shared fixture: the weighted overlapping-groups graph from the examples. +# Its `weight` edge attribute has four distinct threshold levels. +make_weighted_graphlet_graph <- function() { + D1 <- matrix(0, 5, 5) + D2 <- matrix(0, 5, 5) + D3 <- matrix(0, 5, 5) + D1[1:3, 1:3] <- 2 + D2[3:5, 3:5] <- 3 + D3[2:5, 2:5] <- 1 + simplify(graph_from_adjacency_matrix( + D1 + D2 + D3, + mode = "undirected", + weighted = TRUE + )) +} + +# Canonicalise a graphlet_basis() result for comparison: sort the vertices +# within each clique, then the cliques by size (carrying their thresholds). +sort_graphlets_by_size <- function(x) { cl <- lapply(x$cliques, sort) n <- lengths(cl) list(cliques = cl[order(n)], thresholds = x$thresholds[order(n)]) } -threshold.net <- function(graph, level) { +# The maximal cliques of the subgraph left after dropping every edge whose +# weight is below `level`, largest first. One thresholding step of +# graphlet_basis_reference() below. +max_cliques_above_weight <- function(graph, level) { graph.t <- delete_edges(graph, which(E(graph)$weight < level)) clqt <- unvs(max_cliques(graph.t)) @@ -61,7 +96,9 @@ threshold.net <- function(graph, level) { clqt[order(lengths(clqt), decreasing = TRUE)] } -graphlets.old <- function(graph) { +# Pure-R reference implementations of graphlet_basis() and graphlet_proj(), +# kept to cross-check the C results. +graphlet_basis_reference <- function(graph) { if (!is_weighted(graph)) { cli::cli_abort("Graph not weighted") } @@ -71,7 +108,7 @@ graphlets.old <- function(graph) { ## Do all thresholds cl <- lapply(sort(unique(E(graph)$weight)), function(w) { - threshold.net(graph, w) + max_cliques_above_weight(graph, w) }) ## Put the cliques in one long list @@ -89,7 +126,7 @@ graphlets.old <- function(graph) { clf } -graphlets.project.old <- function(graph, cliques, iter, Mu = NULL) { +graphlet_proj_reference <- function(graph, cliques, iter, Mu = NULL) { if (!is_weighted(graph)) { cli::cli_abort("Graph not weighted") } @@ -161,94 +198,150 @@ graphlets.project.old <- function(graph, cliques, iter, Mu = NULL) { # that VS/ES require (and survive) an explicit upgrade. Renamed from the # file-local `names`/`karate` to avoid shadowing base `names()` once sourced # into the shared testthat environment. -karate_oldstyle_names <- c( - "Mr Hi", "Actor 2", "Actor 3", "Actor 4", - "Actor 5", "Actor 6", "Actor 7", "Actor 8", "Actor 9", "Actor 10", - "Actor 11", "Actor 12", "Actor 13", "Actor 14", "Actor 15", "Actor 16", - "Actor 17", "Actor 18", "Actor 19", "Actor 20", "Actor 21", "Actor 22", - "Actor 23", "Actor 24", "Actor 25", "Actor 26", "Actor 27", "Actor 28", - "Actor 29", "Actor 30", "Actor 31", "Actor 32", "Actor 33", "John A" -) - -karate_oldstyle <- structure( - list( - 34, - FALSE, - c( - 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, - 13, 17, 19, 21, 31, 2, 3, 7, 13, 17, 19, 21, 30, 3, 7, 8, 9, - 13, 27, 28, 32, 7, 12, 13, 6, 10, 6, 10, 16, 16, 30, 32, 33, - 33, 33, 32, 33, 32, 33, 32, 33, 33, 32, 33, 32, 33, 25, 27, 29, - 32, 33, 25, 27, 31, 31, 29, 33, 33, 31, 33, 32, 33, 32, 33, 32, - 33, 33 - ), - c( - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, - 5, 5, 6, 8, 8, 8, 9, 13, 14, 14, 15, 15, 18, 18, 19, 20, 20, - 22, 22, 23, 23, 23, 23, 23, 24, 24, 24, 25, 26, 26, 27, 28, 28, - 29, 29, 30, 30, 31, 31, 32 - ), - c( - 0, 1, 16, 2, 17, 24, 3, 4, 5, - 35, 37, 6, 18, 25, 32, 7, 26, 27, 8, 36, 38, 9, 10, 33, 11, 19, - 28, 34, 39, 40, 12, 20, 13, 21, 14, 22, 57, 62, 29, 58, 63, 30, - 59, 66, 23, 41, 15, 64, 65, 69, 31, 42, 46, 48, 50, 53, 55, 60, - 71, 73, 75, 43, 44, 45, 47, 49, 51, 52, 54, 56, 61, 67, 68, 70, - 72, 74, 76, 77 - ), - c( - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, - 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, - 77 - ), - c( - 0, 0, 1, 3, 6, 7, 8, 11, 15, 17, 18, 21, 22, 24, 28, 28, - 28, 30, 32, 32, 34, 34, 36, 36, 36, 36, 38, 38, 41, 42, 44, 46, - 50, 61, 78 - ), - c( - 0, 16, 24, 32, 35, 37, 40, 41, 41, 44, 45, 45, - 45, 45, 46, 48, 50, 50, 50, 52, 53, 55, 55, 57, 62, 65, 66, 68, - 69, 71, 73, 75, 77, 78, 78 - ), +karate_oldstyle_vertex_names <- function() { + c( + "Mr Hi", "Actor 2", "Actor 3", "Actor 4", + "Actor 5", "Actor 6", "Actor 7", "Actor 8", "Actor 9", "Actor 10", + "Actor 11", "Actor 12", "Actor 13", "Actor 14", "Actor 15", "Actor 16", + "Actor 17", "Actor 18", "Actor 19", "Actor 20", "Actor 21", "Actor 22", + "Actor 23", "Actor 24", "Actor 25", "Actor 26", "Actor 27", "Actor 28", + "Actor 29", "Actor 30", "Actor 31", "Actor 32", "Actor 33", "John A" + ) +} + +karate_oldstyle_graph <- function() { + structure( list( - c(1, 0, 1), - structure( - list( - name = "Zachary's karate club network", - Citation = "Wayne W. Zachary. An Information Flow Model for Conflict and Fission in Small Groups. Journal of Anthropological Research Vol. 33, No. 4 452-473", - Author = "Wayne W. Zachary" - ), - names = c("name", "Citation", "Author") + 34, + FALSE, + c( + 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, + 13, 17, 19, 21, 31, 2, 3, 7, 13, 17, 19, 21, 30, 3, 7, 8, 9, + 13, 27, 28, 32, 7, 12, 13, 6, 10, 6, 10, 16, 16, 30, 32, 33, + 33, 33, 32, 33, 32, 33, 32, 33, 33, 32, 33, 32, 33, 25, 27, 29, + 32, 33, 25, 27, 31, 31, 29, 33, 33, 31, 33, 32, 33, 32, 33, 32, + 33, 33 + ), + c( + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, + 5, 5, 6, 8, 8, 8, 9, 13, 14, 14, 15, 15, 18, 18, 19, 20, 20, + 22, 22, 23, 23, 23, 23, 23, 24, 24, 24, 25, 26, 26, 27, 28, 28, + 29, 29, 30, 30, 31, 31, 32 + ), + c( + 0, 1, 16, 2, 17, 24, 3, 4, 5, + 35, 37, 6, 18, 25, 32, 7, 26, 27, 8, 36, 38, 9, 10, 33, 11, 19, + 28, 34, 39, 40, 12, 20, 13, 21, 14, 22, 57, 62, 29, 58, 63, 30, + 59, 66, 23, 41, 15, 64, 65, 69, 31, 42, 46, 48, 50, 53, 55, 60, + 71, 73, 75, 43, 44, 45, 47, 49, 51, 52, 54, 56, 61, 67, 68, 70, + 72, 74, 76, 77 + ), + c( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77 + ), + c( + 0, 0, 1, 3, 6, 7, 8, 11, 15, 17, 18, 21, 22, 24, 28, 28, + 28, 30, 32, 32, 34, 34, 36, 36, 36, 36, 38, 38, 41, 42, 44, 46, + 50, 61, 78 ), - structure( - list( - Faction = c( - 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 2, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2 + c( + 0, 16, 24, 32, 35, 37, 40, 41, 41, 44, 45, 45, + 45, 45, 46, 48, 50, 50, 50, 52, 53, 55, 55, 57, 62, 65, 66, 68, + 69, 71, 73, 75, 77, 78, 78 + ), + list( + c(1, 0, 1), + structure( + list( + name = "Zachary's karate club network", + Citation = "Wayne W. Zachary. An Information Flow Model for Conflict and Fission in Small Groups. Journal of Anthropological Research Vol. 33, No. 4 452-473", + Author = "Wayne W. Zachary" ), - name = karate_oldstyle_names + names = c("name", "Citation", "Author") ), - names = c("Faction", "name") - ), - structure( - list( - weight = c( - 4, - 5, 3, 3, 3, 3, 2, 2, 2, 3, 1, 3, 2, 2, 2, 2, 6, 3, 4, 5, 1, 2, - 2, 2, 3, 4, 5, 1, 3, 2, 2, 2, 3, 3, 3, 2, 3, 5, 3, 3, 3, 3, 3, - 4, 2, 3, 3, 2, 3, 4, 1, 2, 1, 3, 1, 2, 3, 5, 4, 3, 5, 4, 2, 3, - 2, 7, 4, 2, 4, 2, 2, 4, 2, 3, 3, 4, 4, 5 - ) + structure( + list( + Faction = c( + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 2, 1, 2, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2 + ), + name = karate_oldstyle_vertex_names() + ), + names = c("Faction", "name") ), - names = "weight" + structure( + list( + weight = c( + 4, + 5, 3, 3, 3, 3, 2, 2, 2, 3, 1, 3, 2, 2, 2, 2, 6, 3, 4, 5, 1, 2, + 2, 2, 3, 4, 5, 1, 3, 2, 2, 2, 3, 3, 3, 2, 3, 5, 3, 3, 3, 3, 3, + 4, 2, 3, 3, 2, 3, 4, 1, 2, 1, 3, 1, 2, 3, 5, 4, 3, 5, 4, 2, 3, + 2, 7, 4, 2, 4, 2, 2, 4, 2, 3, 3, 4, 4, 5 + ) + ), + names = "weight" + ) ) - ) - ), - class = "igraph" -) + ), + class = "igraph" + ) +} + +# ---- test-cliques.R --------------------------------------------------------- + +# Shared fixture: a triangle plus a disjoint edge. +# The `weight` vertex attribute favors the edge, +# an explicit weight vector can flip the result to the triangle. +make_weighted_clique_graph <- function() { + g <- make_full_graph(3) %du% make_full_graph(2) + V(g)$weight <- c(1, 1, 1, 4, 4) + g +} + +# ---- test-decomposition.R --------------------------------------------------- + +# The first example graph from the Tarjan-Yannakakis paper. +tarjan_yannakakis_graph <- function() { + graph_from_literal( + A - B:C:I, B - A:C:D, C - A:B:E:H, D - B:E:F, + E - C:D:F:H, F - D:E:G, G - F:H, H - C:E:G:I, + I - A:H + ) +} + +# ---- test-generate-migrations.R --------------------------------------------- + +# Source the ellipsis-migration generator into a fresh environment and return +# it. The generator only exists in a source checkout (tools/ is +# .Rbuildignore'd), so the caller is skipped when it is missing. +load_migration_generator <- function() { + generator <- testthat::test_path("..", "..", "tools", "generate-migrations.R") + skip_if_not(file.exists(generator), "tools/generate-migrations.R not found") + gen_env <- new.env() + sys.source(generator, envir = gen_env) + gen_env +} + +# Write a throw-away registry file and return its path. +write_migration_registry <- function(dir, name, code) { + path <- file.path(dir, name) + writeLines(code, path) + path +} + +# ---- test-hrg.R ------------------------------------------------------------- + +# A compact shared fixture: a tiny two-clique graph and one HRG fitted to it, +# reused by the consensus_tree() and predict_edges() blocks. +# Fitting a real HRG is the awkward part of constructing valid inputs there. +hrg_graph_and_fit <- function() { + g <- make_full_graph(4) + make_full_graph(4) + igraph_with_seed(1, list(graph = g, hrg = fit_hrg(g))) +} diff --git a/tests/testthat/test-cliques.R b/tests/testthat/test-cliques.R index 9d14e3d84a2..54a3beb4015 100644 --- a/tests/testthat/test-cliques.R +++ b/tests/testthat/test-cliques.R @@ -462,14 +462,8 @@ test_that("max_cliques handles errors in callback", { # ---- ellipsis migration: argument coverage ---------------------------- -# Shared fixture: a triangle plus a disjoint edge. -# The `weight` vertex attribute favors the edge, -# an explicit weight vector can flip the result to the triangle. -make_weighted_clique_graph <- function() { - g <- make_full_graph(3) %du% make_full_graph(2) - V(g)$weight <- c(1, 1, 1, 4, 4) - g -} +# The `make_weighted_clique_graph()` fixture used below lives in +# helper-test-functions.R. test_that("clique_size_counts() recovers legacy positional arguments", { # `min`, `max` and `maximal` are exercised by name above. diff --git a/tests/testthat/test-decomposition.R b/tests/testthat/test-decomposition.R index a79f095fd0d..9c5fd13bddb 100644 --- a/tests/testthat/test-decomposition.R +++ b/tests/testthat/test-decomposition.R @@ -43,14 +43,9 @@ test_that("is_chordal works", { # ---- ellipsis migration: argument coverage ---------------------------- -# The first example graph from the Tarjan-Yannakakis paper, as in the test above. -tarjan_yannakakis_graph <- function() { - graph_from_literal( - A - B:C:I, B - A:C:D, C - A:B:E:H, D - B:E:F, - E - C:D:F:H, F - D:E:G, G - F:H, H - C:E:G:I, - I - A:H - ) -} +# The `tarjan_yannakakis_graph()` fixture used below -- the first example graph +# from the Tarjan-Yannakakis paper, as in the test above -- lives in +# helper-test-functions.R. test_that("is_chordal accepts alpha, alpham1, fillin, and newgraph by name", { g <- tarjan_yannakakis_graph() diff --git a/tests/testthat/test-embedding.R b/tests/testthat/test-embedding.R index 16be97fb8ad..739c2a06c45 100644 --- a/tests/testthat/test-embedding.R +++ b/tests/testthat/test-embedding.R @@ -51,15 +51,17 @@ test_that("embed_adjacency_matrix -- Undirected, unweighted case works", { scaled = FALSE ) - expect_equal(as_lm$D, sort_by_magnitude(ss$values)[1:no]) - expect_equal(au_lm$D, sort_by_magnitude(ss$values)[1:no]) + expect_equal(as_lm$D, sort_eigenvalues_by_magnitude(ss$values)[1:no]) + expect_equal(au_lm$D, sort_eigenvalues_by_magnitude(ss$values)[1:no]) expect_equal( standardize_eigen_signs(as_lm$X), - standardize_eigen_signs(U[, order_by_magnitude(ss$values)][, 1:no]) + standardize_eigen_signs(U[, order_eigenvalues_by_magnitude(ss$values)][, + 1:no + ]) ) expect_equal( standardize_eigen_signs(au_lm$X), - X[, order_by_magnitude(ss$values)][, 1:no] + X[, order_eigenvalues_by_magnitude(ss$values)][, 1:no] ) au_sa <- embed_adjacency_matrix( @@ -138,15 +140,17 @@ test_that("embed_adjacency_matrix -- Undirected, weighted case works", { scaled = FALSE ) - expect_equal(as_lm$D, sort_by_magnitude(ss$values)[1:no]) - expect_equal(au_lm$D, sort_by_magnitude(ss$values)[1:no]) + expect_equal(as_lm$D, sort_eigenvalues_by_magnitude(ss$values)[1:no]) + expect_equal(au_lm$D, sort_eigenvalues_by_magnitude(ss$values)[1:no]) expect_equal( standardize_eigen_signs(as_lm$X), - standardize_eigen_signs(U[, order_by_magnitude(ss$values)][, 1:no]) + standardize_eigen_signs(U[, order_eigenvalues_by_magnitude(ss$values)][, + 1:no + ]) ) expect_equal( standardize_eigen_signs(au_lm$X), - X[, order_by_magnitude(ss$values)][, 1:no] + X[, order_eigenvalues_by_magnitude(ss$values)][, 1:no] ) au_sa <- embed_adjacency_matrix( @@ -452,15 +456,15 @@ test_that("embed_laplacian_matrix -- Undirected, unweighted, D-A case works", { scaled = FALSE ) - expect_equal(au_lm$D, sort_by_magnitude(D)[1:no]) + expect_equal(au_lm$D, sort_eigenvalues_by_magnitude(D)[1:no]) expect_equal( standardize_eigen_signs(au_lm$X), - standardize_eigen_signs(X[, order_by_magnitude(D)][, 1:no]) + standardize_eigen_signs(X[, order_eigenvalues_by_magnitude(D)][, 1:no]) ) - expect_equal(as_lm$D, sort_by_magnitude(D)[1:no]) + expect_equal(as_lm$D, sort_eigenvalues_by_magnitude(D)[1:no]) expect_equal( standardize_eigen_signs(as_lm$X), - standardize_eigen_signs(U[, order_by_magnitude(D)][, 1:no]) + standardize_eigen_signs(U[, order_eigenvalues_by_magnitude(D)][, 1:no]) ) ## SA @@ -551,15 +555,15 @@ test_that("embed_laplacian_matrix -- Undirected, unweighted, DAD case works", { scaled = FALSE ) - expect_equal(au_lm$D, sort_by_magnitude(D)[1:no]) + expect_equal(au_lm$D, sort_eigenvalues_by_magnitude(D)[1:no]) expect_equal( standardize_eigen_signs(au_lm$X), - standardize_eigen_signs(X[, order_by_magnitude(D)][, 1:no]) + standardize_eigen_signs(X[, order_eigenvalues_by_magnitude(D)][, 1:no]) ) - expect_equal(as_lm$D, sort_by_magnitude(D)[1:no]) + expect_equal(as_lm$D, sort_eigenvalues_by_magnitude(D)[1:no]) expect_equal( standardize_eigen_signs(as_lm$X), - standardize_eigen_signs(U[, order_by_magnitude(D)][, 1:no]) + standardize_eigen_signs(U[, order_eigenvalues_by_magnitude(D)][, 1:no]) ) ## SA @@ -650,15 +654,15 @@ test_that("embed_laplacian_matrix -- Undirected, unweighted, I-DAD case works", scaled = FALSE ) - expect_equal(au_lm$D, sort_by_magnitude(D)[1:no]) + expect_equal(au_lm$D, sort_eigenvalues_by_magnitude(D)[1:no]) expect_equal( standardize_eigen_signs(au_lm$X), - standardize_eigen_signs(X[, order_by_magnitude(D)][, 1:no]) + standardize_eigen_signs(X[, order_eigenvalues_by_magnitude(D)][, 1:no]) ) - expect_equal(as_lm$D, sort_by_magnitude(D)[1:no]) + expect_equal(as_lm$D, sort_eigenvalues_by_magnitude(D)[1:no]) expect_equal( standardize_eigen_signs(as_lm$X), - standardize_eigen_signs(U[, order_by_magnitude(D)][, 1:no]) + standardize_eigen_signs(U[, order_eigenvalues_by_magnitude(D)][, 1:no]) ) ## SA @@ -750,15 +754,15 @@ test_that("embed_laplacian_matrix -- Undirected, weighted, D-A case works", { scaled = FALSE ) - expect_equal(au_lm$D, sort_by_magnitude(D)[1:no]) + expect_equal(au_lm$D, sort_eigenvalues_by_magnitude(D)[1:no]) expect_equal( standardize_eigen_signs(au_lm$X), - standardize_eigen_signs(X[, order_by_magnitude(D)][, 1:no]) + standardize_eigen_signs(X[, order_eigenvalues_by_magnitude(D)][, 1:no]) ) - expect_equal(as_lm$D, sort_by_magnitude(D)[1:no]) + expect_equal(as_lm$D, sort_eigenvalues_by_magnitude(D)[1:no]) expect_equal( standardize_eigen_signs(as_lm$X), - standardize_eigen_signs(U[, order_by_magnitude(D)][, 1:no]) + standardize_eigen_signs(U[, order_eigenvalues_by_magnitude(D)][, 1:no]) ) ## SA @@ -847,15 +851,15 @@ test_that("embed_laplacian_matrix -- Undirected, unweighted, DAD case works", { scaled = FALSE ) - expect_equal(au_lm$D, sort_by_magnitude(D)[1:no]) + expect_equal(au_lm$D, sort_eigenvalues_by_magnitude(D)[1:no]) expect_equal( standardize_eigen_signs(au_lm$X), - standardize_eigen_signs(X[, order_by_magnitude(D)][, 1:no]) + standardize_eigen_signs(X[, order_eigenvalues_by_magnitude(D)][, 1:no]) ) - expect_equal(as_lm$D, sort_by_magnitude(D)[1:no]) + expect_equal(as_lm$D, sort_eigenvalues_by_magnitude(D)[1:no]) expect_equal( standardize_eigen_signs(as_lm$X), - standardize_eigen_signs(U[, order_by_magnitude(D)][, 1:no]) + standardize_eigen_signs(U[, order_eigenvalues_by_magnitude(D)][, 1:no]) ) ## SA @@ -947,15 +951,15 @@ test_that("embed_laplacian_matrix -- Undirected, unweighted, I-DAD case works", scaled = FALSE ) - expect_equal(au_lm$D, sort_by_magnitude(D)[1:no]) + expect_equal(au_lm$D, sort_eigenvalues_by_magnitude(D)[1:no]) expect_equal( standardize_eigen_signs(au_lm$X), - standardize_eigen_signs(X[, order_by_magnitude(D)][, 1:no]) + standardize_eigen_signs(X[, order_eigenvalues_by_magnitude(D)][, 1:no]) ) - expect_equal(as_lm$D, sort_by_magnitude(D)[1:no]) + expect_equal(as_lm$D, sort_eigenvalues_by_magnitude(D)[1:no]) expect_equal( standardize_eigen_signs(as_lm$X), - standardize_eigen_signs(U[, order_by_magnitude(D)][, 1:no]) + standardize_eigen_signs(U[, order_eigenvalues_by_magnitude(D)][, 1:no]) ) ## SA diff --git a/tests/testthat/test-generate-migrations.R b/tests/testthat/test-generate-migrations.R index c1146679b21..a661c1e33a3 100644 --- a/tests/testthat/test-generate-migrations.R +++ b/tests/testthat/test-generate-migrations.R @@ -2,38 +2,27 @@ # The generator is plain base R and only exists in a source checkout # (tools/ is .Rbuildignore'd), so these tests are skipped in a built package. -local_generator <- function(env = parent.frame()) { - generator <- testthat::test_path("..", "..", "tools", "generate-migrations.R") - skip_if_not(file.exists(generator), "tools/generate-migrations.R not found") - gen_env <- new.env() - sys.source(generator, envir = gen_env) - gen_env -} - -write_registry <- function(dir, name, code) { - path <- file.path(dir, name) - writeLines(code, path) - path -} +# The `load_migration_generator()` and `write_migration_registry()` helpers +# used below live in helper-test-functions.R. test_that("migration_registry_files() finds the legacy file and topic files", { - gen <- local_generator() + gen <- load_migration_generator() root <- withr::local_tempdir() dir.create(file.path(root, "tools", "migrations"), recursive = TRUE) expect_identical(gen$migration_registry_files(root), character(0)) - legacy <- write_registry( + legacy <- write_migration_registry( file.path(root, "tools"), "migrations.R", "migrations <- list()" ) - b <- write_registry( + b <- write_migration_registry( file.path(root, "tools", "migrations"), "b-topic.R", "migrations <- list()" ) - a <- write_registry( + a <- write_migration_registry( file.path(root, "tools", "migrations"), "a-topic.R", "migrations <- list()" @@ -44,10 +33,10 @@ test_that("migration_registry_files() finds the legacy file and topic files", { }) test_that("load_migrations() merges entries across registry files", { - gen <- local_generator() + gen <- load_migration_generator() dir <- withr::local_tempdir() - one <- write_registry( + one <- write_migration_registry( dir, "one.R", c( @@ -60,7 +49,7 @@ test_that("load_migrations() merges entries across registry files", { ")" ) ) - two <- write_registry( + two <- write_migration_registry( dir, "two.R", c( @@ -80,7 +69,7 @@ test_that("load_migrations() merges entries across registry files", { }) test_that("load_migrations() rejects duplicate entries across files", { - gen <- local_generator() + gen <- load_migration_generator() dir <- withr::local_tempdir() entry <- c( @@ -92,8 +81,8 @@ test_that("load_migrations() rejects duplicate entries across files", { " )", ")" ) - one <- write_registry(dir, "one.R", entry) - two <- write_registry(dir, "two.R", entry) + one <- write_migration_registry(dir, "one.R", entry) + two <- write_migration_registry(dir, "two.R", entry) expect_error( gen$load_migrations(c(one, two)), @@ -102,10 +91,10 @@ test_that("load_migrations() rejects duplicate entries across files", { }) test_that("load_migrations() rejects files without a migrations list", { - gen <- local_generator() + gen <- load_migration_generator() dir <- withr::local_tempdir() - bad <- write_registry(dir, "bad.R", "not_migrations <- list()") + bad <- write_migration_registry(dir, "bad.R", "not_migrations <- list()") expect_error( gen$load_migrations(bad), "must define a `migrations` list" @@ -113,10 +102,10 @@ test_that("load_migrations() rejects files without a migrations list", { }) test_that("load_migrations() tolerates empty registries", { - gen <- local_generator() + gen <- load_migration_generator() dir <- withr::local_tempdir() - empty <- write_registry(dir, "empty.R", "migrations <- list()") + empty <- write_migration_registry(dir, "empty.R", "migrations <- list()") expect_identical(gen$load_migrations(empty), list()) expect_identical(gen$load_migrations(character(0)), list()) }) @@ -125,7 +114,7 @@ test_that("default expressions keep air's spacing around binary `/`", { # The constant-defaults rule keeps arithmetic like `n / 7100` out of real # registries, but the renderer must stay air-clean for any deparsed # expression, so exercise the helpers directly. - gen <- local_generator() + gen <- load_migration_generator() fmls <- formals(function(agebins = n / 7100, base = "http://a/b") {}) expect_identical(gen$default_expr(fmls, "agebins"), "n / 7100") # slashes inside string literals stay untouched @@ -133,7 +122,7 @@ test_that("default expressions keep air's spacing around binary `/`", { }) test_that("is_constant_default() classifies expressions", { - gen <- local_generator() + gen <- load_migration_generator() const <- alist( NULL, TRUE, @@ -184,7 +173,7 @@ test_that("is_constant_default() classifies expressions", { }) test_that("non-constant defaults are rejected, with no escape hatch", { - gen <- local_generator() + gen <- load_migration_generator() entry <- list( old = function(graph, vids) {}, new = function(graph, ..., vids = V(graph)) {}, diff --git a/tests/testthat/test-glet.R b/tests/testthat/test-glet.R index af039389da0..09dace2b8a7 100644 --- a/tests/testthat/test-glet.R +++ b/tests/testthat/test-glet.R @@ -11,7 +11,7 @@ test_that("Graphlets work for some simple graphs", { expect_equal(full_glet$thresholds, 1) E(full)[1 %--% 2]$weight <- 2 - full_glet2 <- sortgl(graphlet_basis(full)) + full_glet2 <- sort_graphlets_by_size(graphlet_basis(full)) expect_equal( full_glet2, @@ -39,7 +39,7 @@ test_that("Graphlets filtering works", { directed = FALSE, vertices = data.frame(LETTERS[1:5]) ) - glet <- sortgl(graphlet_basis(g)) + glet <- sort_graphlets_by_size(graphlet_basis(g)) expect_equal(unvs(glet$cliques), list(1:3, 2:5)) expect_equal(glet$thresholds, c(8, 5)) @@ -51,7 +51,7 @@ test_that("Graphlets work for a bigger graph", { E(g)$weight <- sample(1:5, ecount(g), replace = TRUE) gl <- graphlet_basis(g) - gl2 <- graphlets.old(g) + gl2 <- graphlet_basis_reference(g) glo <- sort(sapply(unvs(gl$cliques), paste, collapse = "-")) gl2o <- sort(sapply(gl2, paste, collapse = "-")) @@ -75,31 +75,18 @@ test_that("Graphlet projection works", { gl <- graphlet_basis(g) glp <- graphlets(g) - glp2 <- graphlets.project.old(g, cliques = gl$cliques, iter = 1000) + glp2 <- graphlet_proj_reference(g, cliques = gl$cliques, iter = 1000) expect_equal(glp, glp2) }) # ---- ellipsis migration: argument coverage ---------------------------- -# Shared fixture: the weighted overlapping-groups graph from the examples. -# Its `weight` edge attribute has four distinct threshold levels. -make_graphlet_graph <- function() { - D1 <- matrix(0, 5, 5) - D2 <- matrix(0, 5, 5) - D3 <- matrix(0, 5, 5) - D1[1:3, 1:3] <- 2 - D2[3:5, 3:5] <- 3 - D3[2:5, 2:5] <- 1 - simplify(graph_from_adjacency_matrix( - D1 + D2 + D3, - mode = "undirected", - weighted = TRUE - )) -} +# The `make_weighted_graphlet_graph()` fixture used below lives in +# helper-test-functions.R. test_that("graphlet_basis() covers weights", { - g <- make_graphlet_graph() + g <- make_weighted_graphlet_graph() # The attribute weights yield a four-clique candidate basis, # uniform explicit weights override them and collapse the basis @@ -118,7 +105,7 @@ test_that("graphlet_basis() covers weights", { }) test_that("graphlet_proj() covers all tail arguments", { - g <- make_graphlet_graph() + g <- make_weighted_graphlet_graph() cl <- graphlet_basis(g)$cliques # The projection returns one non-negative weight per basis clique. @@ -144,7 +131,7 @@ test_that("graphlet_proj() covers all tail arguments", { }) test_that("graphlets() covers all tail arguments", { - g <- make_graphlet_graph() + g <- make_weighted_graphlet_graph() w <- rep(1, ecount(g)) # Uniform weights collapse the basis to the two maximal cliques. diff --git a/tests/testthat/test-hrg.R b/tests/testthat/test-hrg.R index 215a0e7cb50..eaf8b8478aa 100644 --- a/tests/testthat/test-hrg.R +++ b/tests/testthat/test-hrg.R @@ -141,17 +141,11 @@ test_that("print.igrapHRG() works", { # ---- ellipsis migration: argument coverage ---------------------------- -# A compact shared fixture: a tiny two-clique graph and one HRG fitted to it, -# reused by the consensus_tree() and predict_edges() blocks below. -# Fitting a real HRG is the awkward part of constructing valid inputs there. -hrg_fixture <- function() { - g <- make_full_graph(4) + make_full_graph(4) - igraph_with_seed(1, list(graph = g, hrg = fit_hrg(g))) -} +# The `hrg_graph_and_fit()` fixture used below lives in helper-test-functions.R. test_that("fit_hrg() recovers a legacy positional argument", { rlang::local_options(lifecycle_verbosity = "warning") - fx <- hrg_fixture() + fx <- hrg_graph_and_fit() igraph_local_seed(2) lifecycle::expect_deprecated( @@ -162,7 +156,7 @@ test_that("fit_hrg() recovers a legacy positional argument", { }) test_that("consensus_tree() covers keyword-only tail arguments", { - fx <- hrg_fixture() + fx <- hrg_graph_and_fit() igraph_local_seed(3) res <- consensus_tree(fx$graph, hrg = fx$hrg, start = TRUE, num.samples = 100) @@ -173,7 +167,7 @@ test_that("consensus_tree() covers keyword-only tail arguments", { test_that("consensus_tree() recovers a legacy positional argument", { rlang::local_options(lifecycle_verbosity = "warning") - fx <- hrg_fixture() + fx <- hrg_graph_and_fit() # `num.samples` is pinned small (by name) to keep the two runs fast; `start` # is the recovered positional argument. @@ -189,7 +183,7 @@ test_that("consensus_tree() recovers a legacy positional argument", { }) test_that("predict_edges() covers keyword-only tail arguments", { - fx <- hrg_fixture() + fx <- hrg_graph_and_fit() igraph_local_seed(5) res <- predict_edges( @@ -206,7 +200,7 @@ test_that("predict_edges() covers keyword-only tail arguments", { test_that("predict_edges() recovers a legacy positional argument", { rlang::local_options(lifecycle_verbosity = "warning") - fx <- hrg_fixture() + fx <- hrg_graph_and_fit() igraph_local_seed(6) lifecycle::expect_deprecated( diff --git a/tests/testthat/test-operators.R b/tests/testthat/test-operators.R index d818c2089d5..557a7005b69 100644 --- a/tests/testthat/test-operators.R +++ b/tests/testthat/test-operators.R @@ -6,8 +6,8 @@ test_that("union() works", { expect_vcount(gu, 11) expect_ecount(gu, 20) expect_equal( - order_by_two_first_columns(rbind(as_edgelist(g1), as_edgelist(g2))), - order_by_two_first_columns(as_edgelist(gu)) + sort_edgelist_rows(rbind(as_edgelist(g1), as_edgelist(g2))), + sort_edgelist_rows(as_edgelist(gu)) ) expect_isomorphic(difference(gu, g1), g2) expect_isomorphic(intersection(gu, g2), g2) @@ -18,8 +18,8 @@ test_that("disjoint_union() works", { g2 <- make_star(11, center = 11, mode = "undirected") gdu <- disjoint_union(g1, g2) expect_equal( - order_by_two_first_columns(as_edgelist(gdu)), - order_by_two_first_columns(rbind( + sort_edgelist_rows(as_edgelist(gdu)), + sort_edgelist_rows(rbind( as_edgelist(g1), as_edgelist(g2) + vcount(g1) )) @@ -664,9 +664,9 @@ test_that("intersection of non-named graphs keeps attributes properly", { gi <- intersection(g, g2) - df <- rn(as_data_frame(g)) - df2 <- rn(as_data_frame(g2)) - dfi <- rn(as_data_frame(gi)) + df <- name_rows_by_edge_endpoints(as_data_frame(g)) + df2 <- name_rows_by_edge_endpoints(as_data_frame(g2)) + dfi <- name_rows_by_edge_endpoints(as_data_frame(gi)) expect_equal(df[rownames(dfi), ], dfi[, 1:3], ignore_attr = TRUE) expect_equal(df2[rownames(dfi), ], dfi[, c(1, 2, 4)], ignore_attr = TRUE) @@ -682,9 +682,9 @@ test_that("union of non-named graphs keeps attributes properly", { gu <- union.igraph(g, g2) - df <- rn(as_data_frame(g)) - df2 <- rn(as_data_frame(g2)) - dfu <- rn(as_data_frame(gu)) + df <- name_rows_by_edge_endpoints(as_data_frame(g)) + df2 <- name_rows_by_edge_endpoints(as_data_frame(g2)) + dfu <- name_rows_by_edge_endpoints(as_data_frame(gu)) expect_equal(dfu[rownames(df), 1:3], df, ignore_attr = TRUE) expect_equal(dfu[rownames(df2), c(1, 2, 4)], df2, ignore_attr = TRUE) @@ -1248,11 +1248,11 @@ test_that("rev on detached vs, names", { } }) -# `unique_tests` (the input/expected pairs used below) lives in +# `unique_vertex_seq_cases()` (the input/expected pairs used below) lives in # helper-test-functions.R. test_that("unique on attached vs", { - sapply(unique_tests, function(d) { + sapply(unique_vertex_seq_cases(), function(d) { g <- make_ring(10) vg <- unique(V(g)[d[[1]]]) vr <- V(g)[d[[2]]] @@ -1261,7 +1261,7 @@ test_that("unique on attached vs", { }) test_that("unique on detached vs", { - sapply(unique_tests, function(d) { + sapply(unique_vertex_seq_cases(), function(d) { g <- make_ring(10) vg <- V(g)[d[[1]]] vr <- V(g)[d[[2]]] @@ -1273,7 +1273,7 @@ test_that("unique on detached vs", { }) test_that("unique on attached vs, names", { - sapply(unique_tests, function(d) { + sapply(unique_vertex_seq_cases(), function(d) { g <- make_ring(10) V(g)$name <- letters[1:10] vg <- unique(V(g)[d[[1]]]) @@ -1283,7 +1283,7 @@ test_that("unique on attached vs, names", { }) test_that("unique on detached vs, names", { - sapply(unique_tests, function(d) { + sapply(unique_vertex_seq_cases(), function(d) { g <- make_ring(10) V(g)$name <- letters[1:10] vg <- V(g)[d[[1]]] @@ -1405,17 +1405,8 @@ test_that("graph.attr.comb defaults to the graph.attr.comb igraph option", { expect_length(graph_attr_names(compose(g1, g2)), 0) }) -# A fresh already-simple graph with two edge attributes, one of which the -# default combination list keeps and one of which it drops. Each caller needs -# its own: the C core's property cache lives in the graph object, and merely -# asking whether the graph is simple changes what simplifying it does -- see -# the second test below. -simple_graph_with_attrs <- function() { - g <- make_graph(c(1, 2, 2, 3), directed = FALSE) - E(g)$weight <- c(1, 2) - E(g)$foo <- c("a", "b") - g -} +# The `simple_graph_with_edge_attrs()` fixture used below lives in +# helper-test-functions.R. test_that("simplify() applies edge.attr.comb to an already-simple graph", { # `edge.attr.comb` does not only combine attributes across merged edges, it @@ -1424,24 +1415,27 @@ test_that("simplify() applies edge.attr.comb to an already-simple graph", { # simple still has to go through the combination. # The default list ends in "ignore", so `weight` survives and `foo` does not. - expect_equal(edge_attr_names(simplify(simple_graph_with_attrs())), "weight") + expect_equal( + edge_attr_names(simplify(simple_graph_with_edge_attrs())), + "weight" + ) expect_length( edge_attr_names(simplify( - simple_graph_with_attrs(), + simple_graph_with_edge_attrs(), edge.attr.comb = "ignore" )), 0 ) expect_setequal( edge_attr_names(simplify( - simple_graph_with_attrs(), + simple_graph_with_edge_attrs(), edge.attr.comb = list(weight = "sum", foo = "first") )), c("weight", "foo") ) # Values are untouched -- each edge is its own group. - expect_equal(E(simplify(simple_graph_with_attrs()))$weight, c(1, 2)) + expect_equal(E(simplify(simple_graph_with_edge_attrs()))$weight, c(1, 2)) }) test_that("simplify() is still cache-sensitive, which is a C-core issue", { @@ -1455,8 +1449,8 @@ test_that("simplify() is still cache-sensitive, which is a C-core issue", { # `is_simple(graph)`, which warmed the cache on every call and made the # cache-warm answer the only answer. It cannot cure it either -- # that fix belongs in src/vendor/cigraph/src/operators/simplify.c. - cold <- simple_graph_with_attrs() - warm <- simple_graph_with_attrs() + cold <- simple_graph_with_edge_attrs() + warm <- simple_graph_with_edge_attrs() invisible(is_simple(warm)) expect_length(edge_attr_names(simplify(cold, edge.attr.comb = "ignore")), 0) diff --git a/tests/testthat/test-other.R b/tests/testthat/test-other.R index 8e6111d5123..bd9cf14ecde 100644 --- a/tests/testthat/test-other.R +++ b/tests/testthat/test-other.R @@ -63,19 +63,20 @@ test_that("serialization works", { }) }) -# The old-format `karate_oldstyle` fixture (and its `karate_oldstyle_names`) -# used below live in helper-test-functions.R. +# The old-format `karate_oldstyle_graph()` fixture, +# and its `karate_oldstyle_vertex_names()`, +# live in helper-test-functions.R. test_that("VS/ES require explicit conversion", { expect_snapshot_igraph_error({ - V(karate_oldstyle) + V(karate_oldstyle_graph()) }) }) test_that("VS/ES work with old data type", { - karate2 <- upgrade_graph(karate_oldstyle) + karate2 <- upgrade_graph(karate_oldstyle_graph()) vs2 <- V(karate2) expect_length(vs2, 34) - expect_equal(vs2$name, karate_oldstyle_names) + expect_equal(vs2$name, karate_oldstyle_vertex_names()) })