tests/testthat/test-rscript-partial-arg-matching.R

# R MATCHES A NAMED ARGUMENT BY PREFIX, NOT BY STRING EQUALITY.
#
# For a named argument that is not an exact formal, R looks for a prefix that is
# unambiguous among the formals the call has not matched yet. Neither `library()`
# nor `require()` has a `...`, and only `character.only` starts with "char", so
# `library(pkgvar, char = TRUE)` runs with character.only effectively TRUE.
#
# Matching the flag by string equality therefore misses the abbreviated spelling
# and reads the variable's own symbol as the package name, which is the very
# defect of #143. And matching the `package` argument by string equality misses
# `library(pack = "jsonlite")`, a real dependency, the same cause turned the other
# way round.

pkgs_of <- function(lines) {
  script <- tempfile(fileext = ".R")
  writeLines(lines, script)

  return(att_from_rscript(path = script))
}

test_that("an abbreviated character.only is honoured, whatever the abbreviation", {
  for (flag in c("character.only", "character.onl", "character", "char", "c")) {
    res <- pkgs_of(sprintf("library(pkgvar, %s = TRUE)", flag))
    expect_false("pkgvar" %in% res, info = flag)
  }
})

test_that("an abbreviated character.only is honoured on require() too", {
  for (flag in c("character.only", "character.o", "char", "c")) {
    res <- pkgs_of(sprintf("require(pkgvar, %s = TRUE)", flag))
    expect_false("pkgvar" %in% res, info = flag)
  }
})

test_that("an abbreviated character.only set to FALSE still reads the symbol", {
  res <- pkgs_of("library(findme1, char = FALSE)")

  expect_true("findme1" %in% res)
})

test_that("an abbreviated package argument is matched", {
  expect_true("jsonlite" %in% pkgs_of('library(pack = "jsonlite")'))
  expect_true("jsonlite" %in% pkgs_of('require(packa = "jsonlite")'))
  expect_true("jsonlite" %in% pkgs_of('requireNamespace(pack = "jsonlite", quietly = TRUE)'))
  expect_true("jsonlite" %in% pkgs_of('loadNamespace(pac = "jsonlite")'))
})

test_that("an abbreviated ns argument of getFromNamespace is matched", {
  expect_true("jsonlite" %in% pkgs_of('getFromNamespace("f", n = "jsonlite")'))
})

test_that("an ambiguous prefix is not matched, and nothing crashes", {
  # `library()` has both `package` and `pos`, so "p" names neither. R would stop
  # on this call; the parser must simply decline to guess.
  expect_silent(res <- pkgs_of('library(pkgvar, p = TRUE)'))

  expect_true(is.character(res))
})

test_that("the exact spellings keep working", {
  expect_false("pkgvar" %in% pkgs_of("library(pkgvar, character.only = TRUE)"))
  expect_true("jsonlite" %in% pkgs_of('library(package = "jsonlite")'))
  expect_true("jsonlite" %in% pkgs_of('requireNamespace(package = "jsonlite", quietly = TRUE)'))
  expect_true("findme1" %in% pkgs_of("library(findme1)"))
  expect_true("findme2" %in% pkgs_of("require(findme2)"))
  expect_false("pkg" %in% pkgs_of("requireNamespace(pkg, quietly = TRUE)"))
})

test_that("the formals come from the running R, not from a table written here", {
  f <- pkg_intro_formals("library")

  expect_identical(f, names(formals(base::library)))
  expect_true("character.only" %in% f)
})

test_that("an unknown call yields no formals, so no partial matching is attempted", {
  expect_identical(pkg_intro_formals("il.n.existe.pas"), character(0))
  expect_true(is.na(match_named_arg(
    c("", "cha"), arg_name = "character.only", formals_names = character(0)
  )))
})

test_that("a formal placed after ... is matched by its exact name only", {
  # R's own rule, not a precaution: anything shorter falls into the dots.
  # `requireNamespace()` is `(package, ..., quietly)` on current R.
  f <- pkg_intro_formals("requireNamespace")
  skip_if_not("..." %in% f, "this R has no ... in requireNamespace()")

  expect_gt(match("quietly", table = f), match("...", table = f))
  expect_true(is.na(match_named_arg(
    c("", "quiet"), arg_name = "quietly", formals_names = f
  )))
  expect_equal(
    match_named_arg(c("", "quietly"), arg_name = "quietly", formals_names = f),
    2L
  )
  # `package` sits before the dots, so it does abbreviate.
  expect_equal(
    match_named_arg(c("pack"), arg_name = "package", formals_names = f), 1L
  )
})

Try the attachment package in your browser

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

attachment documentation built on Oct. 1, 2026, 1:08 a.m.