Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ Collate:
'generic_pkg_data_derive.R'
'impl_data.R'
'data_archive_md5.R'
'data_bioc_reverse_dependencies.R'
'data_coverage.R'
'data_desc.R'
'data_documentation_examples.R'
Expand Down
107 changes: 107 additions & 0 deletions R/data_bioc_reverse_dependencies.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#' @include impl_data.R

# BioConductor reverse dependencies metrics implementation
# Reuses the viable_revdep_packages() and get_reverse_deps() helpers defined
# in data_reverse_dependencies.R (both are called at runtime, so no load-time
# ordering dependency exists between the two files).

#' Known BioConductor repository URLs
#'
#' Returns the standard BioConductor repository URLs used to build the package
#' universe for reverse dependency analysis. Unlike \acronym{CRAN}, the
#' BioConductor package universe is spread across several repositories
#' (software, annotation, experiment data and workflows), so all are queried.
#'
#' The `"release"` alias always resolves to the current BioConductor release, so
#' the metric does not need to track the BioConductor version explicitly. A
#' different release (or an internal mirror) can be supplied through the
#' `val.meter.bioc_repos` option.
#'
#' @param version BioConductor release to target. Defaults to `"release"`.
#' @return Character vector of repository URLs.
#' @noRd
get_bioc_repos <- function(version = "release") {
repos <- getOption("val.meter.bioc_repos")
if (!is.null(repos)) {
return(repos)
}

file.path(
"https://bioconductor.org/packages",
version,
c("bioc", "data/annotation", "data/experiment", "workflows")
)
}

impl_data(
"bioc_reverse_dependencies",
class = class_character,
metric = FALSE,
tags = c("adoption", "transient"),
permissions = c("network"),
title = "BioConductor Reverse Dependencies",
description = paste(
"The names of packages on \\acronym{BioConductor} that directly depend on",
"this package through \\code{Depends}, \\code{Imports}, or",
"\\code{LinkingTo} fields."
)
)

impl_data(
"bioc_reverse_dependencies",
for_resource = cran_repo_resource,
function(pkg, resource, field, ...) {
# the BioConductor package universe is independent of the package's own
# resource, so it is queried from the known BioConductor repositories
# rather than from resource@repo.
bioc_matrix <- viable_revdep_packages(repos = get_bioc_repos())

get_reverse_deps(
pkg$name,
bioc_matrix,
dependencies = c("Depends", "Imports", "LinkingTo")
)
}
)

impl_data(
"bioc_reverse_dependencies",
for_resource = mock_resource,
function(pkg, resource, field, ...) {
# Simulate reverse dependencies with a random sample of package names
sample(
paste0("mockbiocpkg", seq_len(10)),
size = min(rpois(1, 2), 10),
replace = FALSE
)
}
)


impl_data(
"bioc_reverse_dependencies_count",
class = class_integer,
metric = TRUE,
tags = c("adoption", "transient"),
permissions = c(),
title = "BioConductor Reverse Dependencies Count",

description = paste(
"The number of packages on \\acronym{BioConductor} that directly depend",
"on this package through \\code{Depends}, \\code{Imports}, or",
"\\code{LinkingTo} fields. This metric reflects adoption within the",
"BioConductor ecosystem and indicates how many packages would be affected",
"by breaking changes. Higher counts suggest wider usage and community",
"trust, but also greater responsibility for maintaining backward",
"compatibility."
)
)

impl_data(
"bioc_reverse_dependencies_count",
for_resource = new_union(cran_repo_resource, mock_resource),
function(pkg, resource, field, ...) {
# Just count the length of rev dep vector
length(pkg$bioc_reverse_dependencies)
}
)
138 changes: 138 additions & 0 deletions tests/testthat/test-data_bioc_reverse_dependencies.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Helper to create a mock BioConductor package matrix for testing
create_mock_bioc_matrix <- function() {
matrix(
c(
# Package, Version, Priority, Depends, Imports, LinkingTo, Suggests
"biocA", "1.0.0", NA, "R (>= 3.5.0), targetpkg", NA, NA, NA,
"biocB", "2.0.0", NA, "R (>= 4.0.0)", "targetpkg, utils", NA, NA,
"biocC", "1.5.0", NA, NA, NA, NA, "targetpkg, testthat",
"biocD", "3.0.0", NA, NA, "targetpkg", NA, NA,
"biocE", "1.2.0", NA, NA, "otherpkg", NA, NA
),
nrow = 5,
ncol = 7,
byrow = TRUE,
dimnames = list(
c("1", "2", "3", "4", "5"),
c(
"Package", "Version", "Priority", "Depends",
"Imports", "LinkingTo", "Suggests"
)
)
)
}

describe("get_bioc_repos", {
it("returns the standard BioConductor repository URLs", {
repos <- get_bioc_repos()

expect_type(repos, "character")
expect_true(length(repos) >= 1)
expect_true(all(grepl("^https://bioconductor.org/packages/", repos)))
expect_true(any(grepl("/bioc$", repos)))
})

it("targets a specific release when asked", {
expect_true(all(grepl("/3.18/", get_bioc_repos(version = "3.18"))))
})

it("honours the val.meter.bioc_repos option override", {
withr::with_options(
list(val.meter.bioc_repos = "file:///tmp/fake-bioc"),
expect_equal(get_bioc_repos(), "file:///tmp/fake-bioc")
)
})
})

describe("bioc_reverse_dependencies metric behavior with mocked data", {
it("returns dependent package names when the BioC universe is mocked", {
mock_matrix <- create_mock_bioc_matrix()

with_mocked_bindings(
viable_revdep_packages = function(repos) mock_matrix,
.package = "val.meter",
{
p <- pkg(
cran_repo_resource(
package = "targetpkg",
repo = "https://cloud.r-project.org/"
),
permissions = permissions("network")
)

deps <- p$bioc_reverse_dependencies

expect_type(deps, "character")
# strong deps only (Depends/Imports/LinkingTo), not Suggests
expect_true(all(c("biocA", "biocB", "biocD") %in% deps))
expect_false("biocC" %in% deps)
}
)
})

it("returns an empty character vector when no reverse deps exist", {
mock_matrix <- create_mock_bioc_matrix()

with_mocked_bindings(
viable_revdep_packages = function(repos) mock_matrix,
.package = "val.meter",
{
p <- pkg(
cran_repo_resource(
package = "lonelypkg",
repo = "https://cloud.r-project.org/"
),
permissions = permissions("network")
)

deps <- p$bioc_reverse_dependencies

expect_type(deps, "character")
expect_length(deps, 0)
}
)
})

it("generates realistic mock data for a mock resource (offline)", {
p <- pkg(
mock_resource(package = "fakepkg", version = "1.2.3"),
permissions("network")
)

deps <- p$bioc_reverse_dependencies

expect_type(deps, "character")
expect_true(length(deps) >= 0 && length(deps) <= 10)
})
})

describe("bioc_reverse_dependencies_count metric behavior", {
it("returns integer count matching dependency vector length", {
mock_matrix <- create_mock_bioc_matrix()

with_mocked_bindings(
viable_revdep_packages = function(repos) mock_matrix,
.package = "val.meter",
{
p <- pkg(
cran_repo_resource(
package = "targetpkg",
repo = "https://cloud.r-project.org/"
),
permissions = permissions("network")
)

count <- p$bioc_reverse_dependencies_count
deps <- p$bioc_reverse_dependencies

expect_type(count, "integer")
expect_length(count, 1)
expect_equal(count, length(deps))
}
)
})

it("is registered as a metric", {
expect_true("bioc_reverse_dependencies_count" %in% names(metrics()))
})
})
Loading