tests/testthat/test-linkage.R

# ========= has_linkages() =========
test_that("has_linkages() works for glycans with intact linkages", {
  glycan <- o_glycan_core_1(linkage = TRUE)
  expect_true(has_linkages(glycan))
})

test_that("has_linkages() works for glycan with partial linkages", {
  glycan_vec <- o_glycan_core_2(linkage = TRUE)
  glycan <- get_structure_graphs(glycan_vec, return_list = FALSE) # Extract the igraph
  glycan <- igraph::set_edge_attr(glycan, "linkage", value = c("??-?", "b1-6"))

  # Create a new vectorized structure with the modified graph
  modified_glycan_vec <- glycan_structure(glycan)
  expect_true(has_linkages(modified_glycan_vec))
})

test_that("has_linkages() works for glycans without linkages", {
  glycan <- o_glycan_core_1(linkage = FALSE)
  expect_false(has_linkages(glycan))
})

test_that("has_linkages() works in strict mode", {
  glycan <- as_glycan_structure("Gal(b1-?)GalNAc(a1-")
  expect_false(has_linkages(glycan, strict = TRUE))
  glycan <- as_glycan_structure("Gal(b1-3)GalNAc(a1-")
  expect_true(has_linkages(glycan, strict = TRUE))
})

test_that("has_linkages() considers anomers", {
  glycans <- as_glycan_structure(c(
    "Gal(b1-3)GalNAc(a1-",
    "Gal(a1-3)GalNAc(?1-"
  ))
  expect_equal(has_linkages(glycans, strict = TRUE), c(TRUE, FALSE))

  glycan <- as_glycan_structure("Gal(??-?)GalNAc(a1-")
  expect_true(has_linkages(glycan))
})

test_that("has_linkages() considers floating attachment linkages", {
  known <- as_glycan_structure(
    "{Neu5Ac(a2-6)}Gal(b1-3)GalNAc(a1-"
  )
  unknown <- as_glycan_structure(
    "{Neu5Ac(??-?)}Gal(??-?)GalNAc(??-"
  )
  partial <- as_glycan_structure(
    "{Neu5Ac(a2-?)}Gal(a1-?)GalNAc(a1-"
  )

  expect_true(has_linkages(known))
  expect_true(has_linkages(known, strict = TRUE))
  expect_false(has_linkages(unknown))
  expect_false(has_linkages(unknown, strict = TRUE))
  expect_true(has_linkages(partial))
  expect_false(has_linkages(partial, strict = TRUE))
})

test_that("has_linkages() works with glycan graphs", {
  intact <- get_structure_graphs(
    as_glycan_structure("Gal(b1-3)GalNAc(a1-")
  )
  partial <- get_structure_graphs(
    as_glycan_structure("Gal(b1-?)GalNAc(a1-")
  )

  expect_identical(has_linkages(intact), TRUE)
  expect_identical(has_linkages(intact, strict = TRUE), TRUE)
  expect_identical(has_linkages(partial), TRUE)
  expect_identical(has_linkages(partial, strict = TRUE), FALSE)
})

# ========= possible_linkages() =========
# Tests for possible_linkages() function
test_that("possible_linkages() works for a?-2", {
  result <- possible_linkages("a?-2")
  expected <- c("a1-2", "a2-2")
  expect_identical(result, expected)
})

test_that("possible_linkages() works for a1-?", {
  result <- possible_linkages("a1-?")
  expected <- paste0("a1-", 1:9)
  expect_identical(result, expected)
})

test_that("possible_linkages() works for ?1-3", {
  result <- possible_linkages("?1-3")
  expected <- c("a1-3", "b1-3")
  expect_identical(result, expected)
})

test_that("possible_linkages() works for b?-?", {
  result <- possible_linkages("b?-?")
  df <- expand.grid(1:2, 1:9)
  expected <- apply(df, 1, function(x) paste0("b", x[1], "-", x[2]))
  expect_identical(result, expected)
})

test_that("possible_linkages() works for a2-3|6", {
  result <- possible_linkages("a2-3/6")
  expected <- c("a2-3", "a2-6")
  expect_identical(result, expected)
})

test_that("possible_linkages() works for a1-3", {
  expect_identical(possible_linkages("a1-3"), "a1-3")
})

test_that("possible_linkages() rejects wrong format", {
  expect_error(possible_linkages("a1-3-4"), "Invalid linkage format")
  expect_error(possible_linkages(""), "Invalid linkage format")
})

test_that("possible_linkages() rejects wrong input type", {
  expect_error(possible_linkages(1))
  expect_error(possible_linkages(NA))
  expect_error(possible_linkages(NULL))
})

test_that("possible_linkages() rejects multiple linkages", {
  expect_error(possible_linkages(c("a1-3", "b1-4")))
})

test_that("possible_linkages() works with custom ranges", {
  result <- possible_linkages(
    "??-?",
    anomer_range = "a",
    pos1_range = 2,
    pos2_range = c(3, 6)
  )
  expected <- c("a2-3", "a2-6")
  expect_identical(result, expected)
})

test_that("possible_linkages() rejects custom ranges with wrong types", {
  expect_error(possible_linkages("??-?", anomer_range = 1))
  expect_error(possible_linkages("??-?", pos1_range = "a"))
  expect_error(possible_linkages("??-?", pos2_range = "a"))
})

test_that("possible_linkages() works with 0 range", {
  expect_equal(possible_linkages("??-?", pos1_range = integer(0)), character(0))
})

test_that("possible_linkages() works with include unknown", {
  result <- possible_linkages("?1-6", include_unknown = TRUE)
  expected <- c("a1-6", "b1-6", "?1-6")
  expect_identical(result, expected)
})

# ========= remove_linkages() =========
test_that("remove_linkages() works", {
  glycan <- o_glycan_core_2(linkage = TRUE)
  glycan <- remove_linkages(glycan)
  graph <- get_structure_graphs(glycan, return_list = FALSE)
  expect_equal(igraph::E(graph)$linkage, c("??-?", "??-?"))
  expect_equal(igraph::graph_attr(graph, "anomer"), "??")
})

test_that("remove_linkages skips floating normalization for ordinary trees", {
  glycan <- o_glycan_core_2(linkage = TRUE)
  testthat::local_mocked_bindings(
    normalize_floating_parts = function(...) {
      stop("floating normalization should not run")
    }
  )

  result <- remove_linkages(glycan)

  expect_identical(
    unname(as.character(result)),
    "GlcNAc(??-?)[Gal(??-?)]GalNAc(??-"
  )
})

test_that("remove_linkages keeps ordinary branch order canonical", {
  glycan <- as_glycan_structure(
    "Fuc(b1-2)[Gal(a1-3)]Gal(b1-3)GlcNAc(b1-"
  )

  result <- remove_linkages(glycan)
  graph <- get_structure_graphs(result, return_list = FALSE)

  expect_identical(
    unname(as.character(result)),
    "Gal(??-?)[Fuc(??-?)]Gal(??-?)GlcNAc(??-"
  )
  expect_identical(
    igraph::V(graph)$mono,
    c("Gal", "Fuc", "Gal", "GlcNAc")
  )
})

test_that("remove_linkages() removes floating attachment linkages", {
  glycan <- as_glycan_structure(
    "{Neu5Ac(a2-6)|2,3}Gal(b1-3)GalNAc(a1-"
  )

  result <- remove_linkages(glycan)

  expect_identical(
    as.character(result),
    "{Neu5Ac(??-?)|2,3}Gal(??-?)GalNAc(??-"
  )
  parts <- structure_floating_parts(result)
  expect_identical(parts$linkage, "??-?")
  expect_identical(parts$parents[[1]], c(2L, 3L))
})

test_that("remove_linkages works with glycan graphs without reordering", {
  structure <- as_glycan_structure(
    "{Neu5Ac(a2-6)|2,3}Gal(b1-3)GalNAc(a1-"
  )
  graph <- get_structure_graphs(structure)
  names_before <- igraph::V(graph)$name
  edges_before <- igraph::as_edgelist(graph, names = FALSE)

  result <- remove_linkages(graph)

  expect_s3_class(result, "igraph")
  expect_identical(igraph::V(result)$name, names_before)
  expect_identical(igraph::as_edgelist(result, names = FALSE), edges_before)
  expect_identical(igraph::E(result)$linkage, "??-?")
  expect_identical(result$anomer, "??")
  expect_identical(result$floating_parts[[1]]$linkage, "??-?")
})

Try the glyrepr package in your browser

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

glyrepr documentation built on Sept. 22, 2026, 5:09 p.m.