From 4e18bcfe73a0ef97999fefa5082b5835b4dcc703 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 16:26:31 +0000 Subject: [PATCH 1/2] refactor: move optional arguments of interface functions behind the ellipsis Insert `...` between the defining arguments and the optional modifiers of 8 functions, following the zoning rules in CONTRIBUTING.md. Legacy positional and abbreviated calls are recovered by the generated ARG_HANDLE blocks (registry: tools/migrations/interface.R) and emit a single soft deprecation for igraph 3.0.0. No defaults change and no arguments are renamed. Functions: E, adjacent_vertices, ends, get_edge_ids, identical_graphs, incident, incident_edges, neighbors Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016M32izVHZPfxAqemAe4BrX --- R/cycles.R | 1 + R/interface.R | 181 +++++++++++++++++++++++++++++++++-- R/iterators.R | 61 +++++++++++- man/E.Rd | 4 +- man/adjacent_vertices.Rd | 4 +- man/ends.Rd | 4 +- man/get_edge_ids.Rd | 4 +- man/identical_graphs.Rd | 4 +- man/incident.Rd | 4 +- man/incident_edges.Rd | 4 +- man/neighbors.Rd | 4 +- tools/migrations/interface.R | 95 ++++++++++++++++++ 12 files changed, 354 insertions(+), 16 deletions(-) create mode 100644 tools/migrations/interface.R diff --git a/R/cycles.R b/R/cycles.R index c3d65b5cae0..25356203038 100644 --- a/R/cycles.R +++ b/R/cycles.R @@ -31,6 +31,7 @@ #' a specific cycle. #' #' @param graph The input graph. +#' @inheritParams rlang::args_dots_empty #' @param mode Character constant specifying how to handle directed graphs. #' `out` follows edge directions, `in` follows edges in the reverse direction, #' and `all` ignores edge directions. Ignored in undirected graphs. diff --git a/R/interface.R b/R/interface.R index c90a238f8a7..bc46be018ee 100644 --- a/R/interface.R +++ b/R/interface.R @@ -335,6 +335,7 @@ ecount <- gsize #' #' @param graph The input graph. #' @param v The vertex of which the adjacent vertices are queried. +#' @inheritParams rlang::args_dots_empty #' @param mode Whether to query outgoing (\sQuote{out}), incoming #' (\sQuote{in}) edges, or both types (\sQuote{all}). This is #' ignored for undirected graphs. @@ -348,7 +349,34 @@ ecount <- gsize #' n1 <- neighbors(g, 1) #' n34 <- neighbors(g, 34) #' intersection(n1, n34) -neighbors <- function(graph, v, mode = c("out", "in", "all", "total")) { +neighbors <- function( + graph, + v, + ..., + mode = c("out", "in", "all", "total") +) { + # BEGIN GENERATED ARG_HANDLE: neighbors, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(mode = mode), + recover_new = c("mode"), + recover_old = c("mode"), + match_names = c("mode"), + match_to = c("mode"), + defaults = list(mode = c("out", "in", "all", "total")), + head_args = c("graph", "v"), + fn_name = "neighbors" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + ensure_igraph(graph) mode <- igraph_match_arg(mode) @@ -369,6 +397,7 @@ neighbors <- function(graph, v, mode = c("out", "in", "all", "total")) { #' @param graph The input graph. #' @param v The vertex of which the incident edges are queried. #' @inheritParams neighbors +#' @inheritParams rlang::args_dots_empty #' @return An edge sequence containing the incident edges of #' the input vertex. #' @@ -379,7 +408,34 @@ neighbors <- function(graph, v, mode = c("out", "in", "all", "total")) { #' g <- make_graph("Zachary") #' incident(g, 1) #' incident(g, 34) -incident <- function(graph, v, mode = c("all", "out", "in", "total")) { +incident <- function( + graph, + v, + ..., + mode = c("all", "out", "in", "total") +) { + # BEGIN GENERATED ARG_HANDLE: incident, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(mode = mode), + recover_new = c("mode"), + recover_old = c("mode"), + match_names = c("mode"), + match_to = c("mode"), + defaults = list(mode = c("all", "out", "in", "total")), + head_args = c("graph", "v"), + fn_name = "incident" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + # For undirected graphs, mode doesn't matter, use "all" if (!is_directed(graph)) { mode <- "all" @@ -418,6 +474,7 @@ is_directed <- function(graph) { #' #' @param graph The input graph #' @param es The sequence of edges to query +#' @inheritParams rlang::args_dots_empty #' @param names Whether to return vertex names or #' numeric vertex IDs. By default vertex names are used. #' @return A two column matrix of vertex names or vertex IDs. @@ -430,7 +487,34 @@ is_directed <- function(graph) { #' @examples #' g <- make_ring(5) #' ends(g, E(g)) -ends <- function(graph, es, names = TRUE) { +ends <- function( + graph, + es, + ..., + names = TRUE +) { + # BEGIN GENERATED ARG_HANDLE: ends, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(names = names), + recover_new = c("names"), + recover_old = c("names"), + match_names = c("names"), + match_to = c("names"), + defaults = list(names = TRUE), + head_args = c("graph", "es"), + fn_name = "ends" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + ensure_igraph(graph) res <- matrix(NA_integer_, ncol = length(es), nrow = 2) @@ -515,6 +599,7 @@ el_to_vec <- function(x, call = rlang::caller_env()) { #' or vector of vertex IDs or symbolic vertex names. #' For a vector, the values are interpreted pairwise, i.e. the first and second are used for #' the first edge, the third and fourth for the second, etc. +#' @inheritParams rlang::args_dots_empty #' @param directed Logical, whether to consider edge directions in #' directed graphs. This argument is ignored for undirected graphs. #' @param error Logical, whether to report an error if an edge is not @@ -543,7 +628,35 @@ el_to_vec <- function(x, call = rlang::caller_env()) { #' eis #' E(g)[eis] #' -get_edge_ids <- function(graph, vp, directed = TRUE, error = FALSE) { +get_edge_ids <- function( + graph, + vp, + ..., + directed = TRUE, + error = FALSE +) { + # BEGIN GENERATED ARG_HANDLE: get_edge_ids, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(directed = directed, error = error), + recover_new = c("directed", "error"), + recover_old = c("directed", "error"), + match_names = c("directed", "error"), + match_to = c("directed", "error"), + defaults = list(directed = TRUE, error = FALSE), + head_args = c("graph", "vp"), + fn_name = "get_edge_ids" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + ensure_igraph(graph) vp <- el_to_vec(vp, call = rlang::caller_env()) @@ -622,6 +735,7 @@ gorder <- vcount #' @param graph Input graph. #' @param v The vertices to query. #' @inheritParams neighbors +#' @inheritParams rlang::args_dots_empty #' @return A list of vertex sequences. #' #' @family structural queries @@ -629,7 +743,34 @@ gorder <- vcount #' @examples #' g <- make_graph("Zachary") #' adjacent_vertices(g, c(1, 34)) -adjacent_vertices <- function(graph, v, mode = c("out", "in", "all", "total")) { +adjacent_vertices <- function( + graph, + v, + ..., + mode = c("out", "in", "all", "total") +) { + # BEGIN GENERATED ARG_HANDLE: adjacent_vertices, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(mode = mode), + recover_new = c("mode"), + recover_old = c("mode"), + match_names = c("mode"), + match_to = c("mode"), + defaults = list(mode = c("out", "in", "all", "total")), + head_args = c("graph", "v"), + fn_name = "adjacent_vertices" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + ensure_igraph(graph) vv <- as_igraph_vs(graph, v) - 1 @@ -659,6 +800,7 @@ adjacent_vertices <- function(graph, v, mode = c("out", "in", "all", "total")) { #' @param graph Input graph. #' @param v The vertices to query #' @inheritParams neighbors +#' @inheritParams rlang::args_dots_empty #' @return A list of edge sequences. #' #' @family structural queries @@ -666,7 +808,34 @@ adjacent_vertices <- function(graph, v, mode = c("out", "in", "all", "total")) { #' @examples #' g <- make_graph("Zachary") #' incident_edges(g, c(1, 34)) -incident_edges <- function(graph, v, mode = c("out", "in", "all", "total")) { +incident_edges <- function( + graph, + v, + ..., + mode = c("out", "in", "all", "total") +) { + # BEGIN GENERATED ARG_HANDLE: incident_edges, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(mode = mode), + recover_new = c("mode"), + recover_old = c("mode"), + match_names = c("mode"), + match_to = c("mode"), + defaults = list(mode = c("out", "in", "all", "total")), + head_args = c("graph", "v"), + fn_name = "incident_edges" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + ensure_igraph(graph) vv <- as_igraph_vs(graph, v) - 1 diff --git a/R/iterators.R b/R/iterators.R index 50356a691f6..6eb1072e228 100644 --- a/R/iterators.R +++ b/R/iterators.R @@ -75,10 +75,38 @@ get_es_graph_id <- get_vs_graph_id <- function(seq) { #' the attributes of the two graphs are allowed to be different. #' #' @param g1,g2 The two graphs +#' @inheritParams rlang::args_dots_empty #' @param attrs Whether to compare the attributes of the graphs #' @return Logical scalar #' @export -identical_graphs <- function(g1, g2, attrs = TRUE) { +identical_graphs <- function( + g1, + g2, + ..., + attrs = TRUE +) { + # BEGIN GENERATED ARG_HANDLE: identical_graphs, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(attrs = attrs), + recover_new = c("attrs"), + recover_old = c("attrs"), + match_names = c("attrs"), + match_to = c("attrs"), + defaults = list(attrs = TRUE), + head_args = c("g1", "g2"), + fn_name = "identical_graphs" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + stopifnot(is_igraph(g1), is_igraph(g2)) .Call(Rx_igraph_identical_graphs, g1, g2, as.logical(attrs)) } @@ -321,6 +349,7 @@ unsafe_create_es <- function(graph, idx, es = NULL) { #' edges in the sequence. See [$.igraph.es()] for details. #' #' @param graph The graph. +#' @inheritParams rlang::args_dots_empty #' @param P A list of vertices to select edges via pairs of vertices. #' The first and second vertices select the first edge, the third #' and fourth the second, etc. @@ -343,7 +372,35 @@ unsafe_create_es <- function(graph, idx, es = NULL) { #' g2 <- make_ring(10) %>% #' set_vertex_attr("name", value = letters[1:10]) #' E(g2) -E <- function(graph, P = NULL, path = NULL, directed = TRUE) { +E <- function( + graph, + ..., + P = NULL, + path = NULL, + directed = TRUE +) { + # BEGIN GENERATED ARG_HANDLE: E, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(P = P, path = path, directed = directed), + recover_new = c("P", "path", "directed"), + recover_old = c("P", "path", "directed"), + match_names = c("P", "path", "directed"), + match_to = c("P", "path", "directed"), + defaults = list(P = NULL, path = NULL, directed = TRUE), + head_args = c("graph"), + fn_name = "E" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + ensure_igraph(graph) update_es_ref(graph) diff --git a/man/E.Rd b/man/E.Rd index fe4b84517c8..8340cee43cc 100644 --- a/man/E.Rd +++ b/man/E.Rd @@ -4,11 +4,13 @@ \alias{E} \title{Edges of a graph} \usage{ -E(graph, P = NULL, path = NULL, directed = TRUE) +E(graph, ..., P = NULL, path = NULL, directed = TRUE) } \arguments{ \item{graph}{The graph.} +\item{...}{These dots are for future extensions and must be empty.} + \item{P}{A list of vertices to select edges via pairs of vertices. The first and second vertices select the first edge, the third and fourth the second, etc.} diff --git a/man/adjacent_vertices.Rd b/man/adjacent_vertices.Rd index ffa379e3479..7cd09d74caa 100644 --- a/man/adjacent_vertices.Rd +++ b/man/adjacent_vertices.Rd @@ -4,13 +4,15 @@ \alias{adjacent_vertices} \title{Adjacent vertices of multiple vertices in a graph} \usage{ -adjacent_vertices(graph, v, mode = c("out", "in", "all", "total")) +adjacent_vertices(graph, v, ..., mode = c("out", "in", "all", "total")) } \arguments{ \item{graph}{Input graph.} \item{v}{The vertices to query.} +\item{...}{These dots are for future extensions and must be empty.} + \item{mode}{Whether to query outgoing (\sQuote{out}), incoming (\sQuote{in}) edges, or both types (\sQuote{all}). This is ignored for undirected graphs.} diff --git a/man/ends.Rd b/man/ends.Rd index abccac64a54..be8c4381989 100644 --- a/man/ends.Rd +++ b/man/ends.Rd @@ -5,13 +5,15 @@ \alias{get.edges} \title{Incident vertices of some graph edges} \usage{ -ends(graph, es, names = TRUE) +ends(graph, es, ..., names = TRUE) } \arguments{ \item{graph}{The input graph} \item{es}{The sequence of edges to query} +\item{...}{These dots are for future extensions and must be empty.} + \item{names}{Whether to return vertex names or numeric vertex IDs. By default vertex names are used.} } diff --git a/man/get_edge_ids.Rd b/man/get_edge_ids.Rd index 07ba9544181..f7f389cd20c 100644 --- a/man/get_edge_ids.Rd +++ b/man/get_edge_ids.Rd @@ -4,7 +4,7 @@ \alias{get_edge_ids} \title{Find the edge IDs based on the incident vertices of the edges} \usage{ -get_edge_ids(graph, vp, directed = TRUE, error = FALSE) +get_edge_ids(graph, vp, ..., directed = TRUE, error = FALSE) } \arguments{ \item{graph}{The input graph.} @@ -14,6 +14,8 @@ or vector of vertex IDs or symbolic vertex names. For a vector, the values are interpreted pairwise, i.e. the first and second are used for the first edge, the third and fourth for the second, etc.} +\item{...}{These dots are for future extensions and must be empty.} + \item{directed}{Logical, whether to consider edge directions in directed graphs. This argument is ignored for undirected graphs.} diff --git a/man/identical_graphs.Rd b/man/identical_graphs.Rd index 0294f53dc92..ed60154f8c6 100644 --- a/man/identical_graphs.Rd +++ b/man/identical_graphs.Rd @@ -4,11 +4,13 @@ \alias{identical_graphs} \title{Decide if two graphs are identical} \usage{ -identical_graphs(g1, g2, attrs = TRUE) +identical_graphs(g1, g2, ..., attrs = TRUE) } \arguments{ \item{g1, g2}{The two graphs} +\item{...}{These dots are for future extensions and must be empty.} + \item{attrs}{Whether to compare the attributes of the graphs} } \value{ diff --git a/man/incident.Rd b/man/incident.Rd index 78a3a90f7d5..c0833e46395 100644 --- a/man/incident.Rd +++ b/man/incident.Rd @@ -4,13 +4,15 @@ \alias{incident} \title{Incident edges of a vertex in a graph} \usage{ -incident(graph, v, mode = c("all", "out", "in", "total")) +incident(graph, v, ..., mode = c("all", "out", "in", "total")) } \arguments{ \item{graph}{The input graph.} \item{v}{The vertex of which the incident edges are queried.} +\item{...}{These dots are for future extensions and must be empty.} + \item{mode}{Whether to query outgoing (\sQuote{out}), incoming (\sQuote{in}) edges, or both types (\sQuote{all}). This is ignored for undirected graphs.} diff --git a/man/incident_edges.Rd b/man/incident_edges.Rd index 273c1ea36e5..325ae62623c 100644 --- a/man/incident_edges.Rd +++ b/man/incident_edges.Rd @@ -4,13 +4,15 @@ \alias{incident_edges} \title{Incident edges of multiple vertices in a graph} \usage{ -incident_edges(graph, v, mode = c("out", "in", "all", "total")) +incident_edges(graph, v, ..., mode = c("out", "in", "all", "total")) } \arguments{ \item{graph}{Input graph.} \item{v}{The vertices to query} +\item{...}{These dots are for future extensions and must be empty.} + \item{mode}{Whether to query outgoing (\sQuote{out}), incoming (\sQuote{in}) edges, or both types (\sQuote{all}). This is ignored for undirected graphs.} diff --git a/man/neighbors.Rd b/man/neighbors.Rd index c5f79860e75..7ddf86de6ce 100644 --- a/man/neighbors.Rd +++ b/man/neighbors.Rd @@ -4,13 +4,15 @@ \alias{neighbors} \title{Neighboring (adjacent) vertices in a graph} \usage{ -neighbors(graph, v, mode = c("out", "in", "all", "total")) +neighbors(graph, v, ..., mode = c("out", "in", "all", "total")) } \arguments{ \item{graph}{The input graph.} \item{v}{The vertex of which the adjacent vertices are queried.} +\item{...}{These dots are for future extensions and must be empty.} + \item{mode}{Whether to query outgoing (\sQuote{out}), incoming (\sQuote{in}) edges, or both types (\sQuote{all}). This is ignored for undirected graphs.} diff --git a/tools/migrations/interface.R b/tools/migrations/interface.R new file mode 100644 index 00000000000..f9af26fef85 --- /dev/null +++ b/tools/migrations/interface.R @@ -0,0 +1,95 @@ +# Argument-signature migrations: interface +# Schema: see tools/migrations/README.md. Regenerate with: +# Rscript tools/generate-migrations.R + +migrations <- list( + adjacent_vertices = list( + old = function(graph, v, mode) {}, + new = function( + graph, + v, + ..., + mode = c("out", "in", "all", "total") + ) {}, + when = "3.0.0" + ), + + ends = list( + old = function(graph, es, names) {}, + new = function( + graph, + es, + ..., + names = TRUE + ) {}, + when = "3.0.0" + ), + + get_edge_ids = list( + old = function(graph, vp, directed, error) {}, + new = function( + graph, + vp, + ..., + directed = TRUE, + error = FALSE + ) {}, + when = "3.0.0" + ), + + incident = list( + old = function(graph, v, mode) {}, + new = function( + graph, + v, + ..., + mode = c("all", "out", "in", "total") + ) {}, + when = "3.0.0" + ), + + incident_edges = list( + old = function(graph, v, mode) {}, + new = function( + graph, + v, + ..., + mode = c("out", "in", "all", "total") + ) {}, + when = "3.0.0" + ), + + neighbors = list( + old = function(graph, v, mode) {}, + new = function( + graph, + v, + ..., + mode = c("out", "in", "all", "total") + ) {}, + when = "3.0.0" + ), + + E = list( + old = function(graph, P, path, directed) {}, + new = function( + graph, + ..., + P = NULL, + path = NULL, + directed = TRUE + ) {}, + when = "3.0.0" + ), + + identical_graphs = list( + old = function(g1, g2, attrs) {}, + new = function( + g1, + g2, + ..., + attrs = TRUE + ) {}, + when = "3.0.0" + ) +) From 831bffb1bc0b00c55dd9b6aa7d13c1b30d45358e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 20:05:49 +0000 Subject: [PATCH 2/2] test: cover migrated interface signatures Add argument-coverage tests for the 8 functions of the interface ellipsis migration (R/interface.R, R/iterators.R): every keyword-only tail argument (mode, names, directed/error, P/path/directed, attrs) is passed by name with a non-default value and asserted on, and the legacy positional form is exercised through lifecycle::expect_deprecated() with an equality check against the equivalent named call. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016M32izVHZPfxAqemAe4BrX --- tests/testthat/test-interface.R | 95 +++++++++++++++++++++++++++++++++ tests/testthat/test-iterators.R | 33 ++++++++++++ 2 files changed, 128 insertions(+) diff --git a/tests/testthat/test-interface.R b/tests/testthat/test-interface.R index 0376f705da8..9b50d63af00 100644 --- a/tests/testthat/test-interface.R +++ b/tests/testthat/test-interface.R @@ -268,3 +268,98 @@ test_that("get_edge_ids() returns 0 for missing edges when error=FALSE", { expect_equal(result, 0) expect_true(is.numeric(result)) }) + +# ---- ellipsis migration: argument coverage ---------------------------- + +test_that("neighbors() tail arguments and legacy positional recovery", { + g <- make_ring(10, directed = TRUE) + + # In a directed ring the in- and out-neighborhoods of a vertex differ. + expect_equal(as.numeric(neighbors(g, 1, mode = "in")), 10) + expect_equal(as.numeric(neighbors(g, 1, mode = "out")), 2) + + lifecycle::expect_deprecated( + res <- neighbors(g, 1, "in") + ) + expect_equal(res, neighbors(g, 1, mode = "in")) +}) + +test_that("incident() tail arguments and legacy positional recovery", { + g <- make_ring(10, directed = TRUE) + + # Vertex 1 of a directed ring has one incoming and one outgoing edge. + res_in <- incident(g, 1, mode = "in") + expect_s3_class(res_in, "igraph.es") + expect_equal(as.numeric(res_in), 10) + expect_equal(as.numeric(incident(g, 1, mode = "out")), 1) + + lifecycle::expect_deprecated( + res <- incident(g, 1, "in") + ) + expect_equal(res, incident(g, 1, mode = "in")) +}) + +test_that("adjacent_vertices() tail arguments and legacy positional recovery", { + g <- make_ring(10, directed = TRUE) + + # The in-neighbor of each ring vertex is its predecessor. + res_in <- adjacent_vertices(g, c(1, 2), mode = "in") + expect_length(res_in, 2) + expect_equal(lapply(res_in, as.numeric), list(10, 1)) + + lifecycle::expect_deprecated( + res <- adjacent_vertices(g, c(1, 2), "in") + ) + expect_equal(res, adjacent_vertices(g, c(1, 2), mode = "in")) +}) + +test_that("incident_edges() tail arguments and legacy positional recovery", { + g <- make_ring(10, directed = TRUE) + + # Edge i points from vertex i to vertex i + 1, + # so the incoming edge of vertex 1 is edge 10. + res_in <- incident_edges(g, c(1, 2), mode = "in") + expect_length(res_in, 2) + expect_equal(lapply(res_in, as.numeric), list(10, 1)) + + lifecycle::expect_deprecated( + res <- incident_edges(g, c(1, 2), "in") + ) + expect_equal(res, incident_edges(g, c(1, 2), mode = "in")) +}) + +test_that("ends() tail arguments and legacy positional recovery", { + g <- make_ring(10, directed = TRUE) + V(g)$name <- letters[1:10] + + # The endpoint matrix contains vertex names with names = TRUE + # and vertex IDs with names = FALSE. + expect_equal( + ends(g, es = 1:2), + matrix(c("a", "b", "b", "c"), nrow = 2) + ) + expect_equal( + ends(g, es = 1:2, names = FALSE), + matrix(c(1, 2, 2, 3), nrow = 2) + ) + + lifecycle::expect_deprecated( + res <- ends(g, 1:2, FALSE) + ) + expect_identical(res, ends(g, 1:2, names = FALSE)) +}) + +test_that("get_edge_ids() tail arguments and legacy positional recovery", { + g <- make_ring(10, directed = TRUE) + + # The reversed pair is only found when direction is ignored. + expect_equal(get_edge_ids(g, c(2, 1)), 0) + expect_equal(get_edge_ids(g, c(2, 1), directed = FALSE, error = TRUE), 1) + # With error = TRUE a missing edge is an error instead of a zero ID. + expect_error(get_edge_ids(g, c(1, 3), error = TRUE), "no such edge") + + lifecycle::expect_deprecated( + res <- get_edge_ids(g, c(2, 1), FALSE) + ) + expect_identical(res, get_edge_ids(g, c(2, 1), directed = FALSE)) +}) diff --git a/tests/testthat/test-iterators.R b/tests/testthat/test-iterators.R index 75971bdd9ca..59a6a274fee 100644 --- a/tests/testthat/test-iterators.R +++ b/tests/testthat/test-iterators.R @@ -443,3 +443,36 @@ test_that("logical indices are not recycled", { expect_snapshot_igraph_error(V(g)[c(TRUE, FALSE)]) expect_snapshot_igraph_error(E(g)[c(TRUE, FALSE)]) }) + +# ---- ellipsis migration: argument coverage ---------------------------- + +test_that("E() tail arguments and legacy positional recovery", { + g <- make_ring(10, directed = TRUE) + + # The reversed pair is only matched when direction is ignored. + expect_equal(as.numeric(E(g, P = c(1, 2))), 1) + expect_equal(as.numeric(E(g, P = c(2, 1), directed = FALSE)), 1) + # Path mode selects the consecutive edges along the given vertex path. + expect_equal(as.numeric(E(g, path = c(1, 2, 3))), c(1, 2)) + expect_equal(as.numeric(E(g, path = c(3, 2, 1), directed = FALSE)), c(2, 1)) + + lifecycle::expect_deprecated( + res <- E(g, c(1, 2)) + ) + expect_equal(res, E(g, P = c(1, 2))) +}) + +test_that("identical_graphs() tail arguments and legacy positional recovery", { + g1 <- make_ring(5) + g2 <- make_ring(5) + g2$foo <- 1 + + # The graph attribute only breaks identity when attributes are compared. + expect_false(identical_graphs(g1, g2)) + expect_true(identical_graphs(g1, g2, attrs = FALSE)) + + lifecycle::expect_deprecated( + res <- identical_graphs(g1, g2, FALSE) + ) + expect_identical(res, identical_graphs(g1, g2, attrs = FALSE)) +})