Nothing
# ==================================================================== #
# TITLE: #
# AMR: An R Package for Working with Antimicrobial Resistance Data #
# #
# SOURCE CODE: #
# https://github.com/msberends/AMR #
# #
# PLEASE CITE THIS SOFTWARE AS: #
# Berends MS, Luz CF, Friedrich AW, et al. (2022). #
# AMR: An R Package for Working with Antimicrobial Resistance Data. #
# Journal of Statistical Software, 104(3), 1-31. #
# https://doi.org/10.18637/jss.v104.i03 #
# #
# Developed at the University of Groningen and the University Medical #
# Center Groningen in the Netherlands, in collaboration with many #
# colleagues from around the world, see our website. #
# #
# This R package is free software; you can freely use and distribute #
# it for both personal and commercial purposes under the terms of the #
# GNU General Public License version 2.0 (GNU GPL-2), as published by #
# the Free Software Foundation. #
# We created this package for both routine data analysis and academic #
# research and it was publicly released in the hope that it will be #
# useful, but it comes WITHOUT ANY WARRANTY OR LIABILITY. #
# #
# Visit our website for the full manual and a complete tutorial about #
# how to conduct AMR data analysis: https://amr-for-r.org #
# ==================================================================== #
# Tests of the `microorganisms` data set and the stability of MO codes.
# The rules themselves are in helper-microorganisms.R, which is also used by the taxonomy build.
#
# KNOWN DEFECTS: the data set on the main branch of October 2026 contains defects that will be solved by the
# next taxonomy rebuild (e.g. Trichophyton as a bacterium, issue #309). These are listed in
# data-raw/microorganisms_files/microorganisms_known_defects.csv, so that these tests fail on every NEW defect,
# while the current ones are visible. This list may only become shorter, and must be empty after the rebuild (then
# delete the file). As data-raw/ does not ship with the package, the tests that use this list only run in the
# source repository.
known_defects <- function() {
read_mo_known_defects()
}
new_defects <- function(rule, records) {
setdiff(records, known_defects()$record[known_defects()$rule == rule])
}
# Sentinel organisms: clinically important taxa with properties that must not change unnoticed.
# NA means: not checked.
sentinels <- data.frame(
fullname = c(
"Escherichia coli", "Klebsiella pneumoniae", "Pseudomonas aeruginosa", "Acinetobacter baumannii",
"Haemophilus influenzae", "Neisseria gonorrhoeae", "Staphylococcus aureus", "Streptococcus pneumoniae",
"Enterococcus faecium", "Clostridioides difficile", "Mycobacterium tuberculosis", "Listeria monocytogenes",
"Tropheryma whipplei", "Candida albicans", "Candidozyma auris", "Nakaseomyces glabratus",
"Aspergillus fumigatus", "Cryptococcus neoformans", "Pneumocystis jirovecii", "Trichophyton rubrum",
"Mucor", "Necator americanus", "Ascaris lumbricoides", "Echinococcus granulosus",
"Capillaria", "Plasmodium falciparum", "Leishmania donovani", "Trypanosoma cruzi",
"Giardia", "Toxoplasma gondii"
),
domain = c(
rep("Bacteria", 13), rep("Fungi", 8), rep("Animalia", 4),
"Protozoa", "Protozoa", "Protozoa", "Protozoa", NA
),
gramstain = c(
rep("Gram-negative", 6), rep("Gram-positive", 7), rep(NA, 17)
),
family = c(
"Enterobacteriaceae", "Enterobacteriaceae", "Pseudomonadaceae", "Moraxellaceae", "Pasteurellaceae",
"Neisseriaceae", "Staphylococcaceae", "Streptococcaceae", "Enterococcaceae", "Peptostreptococcaceae",
"Mycobacteriaceae", "Listeriaceae", NA, NA, NA, NA,
"Aspergillaceae", NA, NA, "Arthrodermataceae", "Mucoraceae", NA, NA, NA,
NA, NA, NA, NA, NA, NA
),
stringsAsFactors = FALSE
)
sentinel_defects <- function() {
i <- match(sentinels$fullname, microorganisms$fullname)
ok <- !is.na(i)
# (paste() of an empty vector would return one element, hence the use of sprintf())
out <- sprintf("%s is missing", sentinels$fullname[!ok])
check <- function(property, actual) {
expected <- sentinels[[property]]
wrong <- which(ok & !is.na(expected) & actual != expected)
sprintf("%s %s is %s instead of %s", sentinels$fullname[wrong], rep(property, length(wrong)), actual[wrong], expected[wrong])
}
out <- c(out, check("domain", microorganisms$domain[i]))
out <- c(out, check("family", microorganisms$family[i]))
gramstain <- rep(NA_character_, length(i))
gramstain[ok] <- suppressWarnings(mo_gramstain(microorganisms$mo[i[ok]], language = NULL))
out <- c(out, check("gramstain", gramstain))
# the code prefix must always match the domain
prefix_ok <- startsWith(as.character(microorganisms$mo[i]), paste0(MO_DOMAIN_PREFIX[sentinels$domain], "_"))
c(out, sprintf("%s has a code with the wrong prefix", sentinels$fullname[which(ok & !is.na(sentinels$domain) & !prefix_ok)]))
}
test_that("microorganisms: integrity rules", {
skip_on_cran()
skip_if_no_mo_repository()
issues <- mo_integrity_issues(microorganisms, registry = read_mo_registry(), renames = read_mo_renames(), retirements = read_mo_retirements())
for (rule in names(issues)) {
defects <- new_defects(rule, issues[[rule]])
expect_true(length(defects) == 0,
info = paste0("Rule '", rule, "' is broken by: ", paste(utils::head(defects, 25), collapse = ", "))
)
}
})
test_that("microorganisms: known defects are no longer than needed", {
skip_on_cran()
skip_if_no_mo_repository()
# a known defect that is solved must be removed from the list, so that it cannot come back unnoticed
kd <- known_defects()
skip_if(nrow(kd) == 0)
issues <- mo_integrity_issues(microorganisms, registry = read_mo_registry(), renames = read_mo_renames(), retirements = read_mo_retirements())
current <- c(
unlist(lapply(names(issues), function(rule) paste(rule, issues[[rule]], sep = "|"))),
paste("sentinel", sentinel_defects(), sep = "|")
)
solved <- setdiff(paste(kd$rule, kd$record, sep = "|"), current)
expect_true(length(solved) == 0,
info = paste0("Solved, remove from microorganisms_known_defects.csv: ", paste(utils::head(solved, 25), collapse = ", "))
)
})
test_that("microorganisms: sentinel organisms", {
skip_on_cran()
skip_if_no_mo_repository()
defects <- new_defects("sentinel", sentinel_defects())
expect_true(length(defects) == 0, info = paste(defects, collapse = "; "))
})
test_that("MO codes: every code of every release since v2.0.0 still means the same taxon", {
skip_on_cran()
registry <- read_mo_registry()
skip_if(is.null(registry), "MO code registry not available (only in the source repository)")
renames <- read_mo_renames()
# the registry itself
expect_false(anyDuplicated(registry$mo) > 0)
expect_true(all(numeric_version(sub("^v", "", registry$first_release)) >= "2.0.0"))
expect_true(all(numeric_version(sub("^v", "", registry$first_release)) <= numeric_version(sub("^v", "", registry$last_release))))
# every registered code must lead to its own taxon (or to NA if the taxon is not in the data anymore)
expected_name <- registry$fullname
renamed <- match(registry$mo, renames$mo)
expected_name[!is.na(renamed)] <- renames$new_name[renamed[!is.na(renamed)]]
# deliberate in as.mo() since v2.0.0: the kingdom/domain Fungi returns the code for an unknown fungus
expected_name[registry$mo == "F_[KNG]_FUNGI"] <- "(unknown fungus)"
# codes retired on purpose (their taxon was another organism) must lead to NA
retirements <- read_mo_retirements()
retired_on_purpose <- registry$mo %in% retirements$mo
expect_true(all(is.na(suppressWarnings(as.mo(registry$mo[retired_on_purpose], info = FALSE)))))
registry <- registry[!retired_on_purpose, , drop = FALSE]
expected_name <- expected_name[!retired_on_purpose]
result <- suppressWarnings(as.mo(registry$mo, keep_synonyms = TRUE, info = FALSE))
result_name <- microorganisms$fullname[match(result, microorganisms$mo)]
wrong <- !is.na(result) & mo_name_without_suffix(result_name) != mo_name_without_suffix(expected_name)
# (paste() of empty vectors would return one element, hence the use of sprintf())
defects <- new_defects("registered_code_with_other_taxon", sprintf("%s (%s)", registry$mo[wrong], result_name[wrong]))
expect_true(length(defects) == 0,
info = paste0("Registered codes leading to another taxon: ", paste(utils::head(defects, 25), collapse = ", "))
)
# the internal lookup table must be up to date with the registry
full_registry <- read_mo_registry()
expect_setequal(AMR:::MO_RETIRED_CODES$old_mo, full_registry$mo[!full_registry$mo %in% microorganisms$mo])
})
test_that("MO codes: codes of earlier releases are translated, never guessed", {
skip_on_cran()
retired <- AMR:::MO_RETIRED_CODES
expect_false(any(retired$old_mo %in% microorganisms$mo))
expect_true(all(retired$mo[!is.na(retired$mo)] %in% microorganisms$mo))
# every translated code leads to a record with the registered name
translated <- retired[!is.na(retired$mo), , drop = FALSE]
expect_identical(
as.character(suppressWarnings(as.mo(translated$old_mo, keep_synonyms = TRUE, info = FALSE))),
translated$mo
)
expect_identical(
mo_name_without_suffix(microorganisms$fullname[match(translated$mo, microorganisms$mo)]),
mo_name_without_suffix(translated$fullname)
)
# warnings are wrapped to the console width, so the patterns allow a line break between words
# regression: in October 2026, the code of Trichophyton (F_TRCHP, v3.0.1) was guessed as Talaromyces; since the
# taxonomy update of October 2026, F_TRCHP_RBRM is the current code of Trichophyton rubrum again
x <- suppressWarnings(as.mo("F_TRCHP_RBRM", info = FALSE))
expect_identical(mo_name(x, language = NULL), "Trichophyton rubrum")
expect_identical(suppressWarnings(mo_genus("F_TRCHP", language = NULL)), "Trichophyton")
# codes from before v2.0.0 and unknown codes are never guessed (decision by Matthijs S. Berends, 3 October 2026)
expect_warning(x <- as.mo("B_ESCH_COL", info = FALSE), "not\\s+supported")
expect_true(is.na(x))
expect_warning(x <- as.mo(c("B_ESCHR_COL", "B_ESCHR_COLI"), info = FALSE), "unknown\\s+MO\\s+code")
expect_identical(as.character(x), c(NA, "B_ESCHR_COLI"))
# regression: until October 2026, a MycoBank target that was not in the data set replaced a valid GBIF target by NA
expect_identical(mo_current("Chaetomium abuense"), "Amesia nigricolor")
# and a synonym without any known current name is not 'renamed' to itself
no_target <- mo_synonyms_without_current_name(microorganisms)
expect_true(all(is.na(AMR:::synonym_mo_to_accepted_mo(no_target$mo[is.na(no_target$chain_ends_at)], fill_in_accepted = FALSE))))
# current codes are returned as they are, without warnings
expect_silent(x <- as.mo(c("B_ESCHR_COLI", "B_STPHY_AURS", NA), info = FALSE))
expect_identical(as.character(x), c("B_ESCHR_COLI", "B_STPHY_AURS", NA))
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.