From 4ec81813701c7041d59ffd41970b9c43128fb7ca Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Fri, 24 Jul 2026 16:17:21 -0700 Subject: [PATCH 1/4] add tests for isProcessForkedChild() and archSystemFile() cover two 6.0.0 additions that previously had no automated tests: - isProcessForkedChild(): FALSE in the parent, TRUE in parallel::mclapply workers (skipped on Windows, which has no fork()) - archSystemFile(): delegates to system.file() with no arch subdir, and composes the arch-specific subdirectory when .Platform$r_arch is set --- tests/test-arch-system-file.R | 37 +++++++++++++++++++++++++++++++++++ tests/test-fork.R | 26 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 tests/test-arch-system-file.R create mode 100644 tests/test-fork.R diff --git a/tests/test-arch-system-file.R b/tests/test-arch-system-file.R new file mode 100644 index 00000000..065bca29 --- /dev/null +++ b/tests/test-arch-system-file.R @@ -0,0 +1,37 @@ +# Unit tests for archSystemFile(), the arch-aware wrapper around system.file() +# introduced when systemFile() was renamed (see #251, #252). It injects the +# architecture-specific subdirectory (.Platform$r_arch) used on Windows, e.g. +# archSystemFile("lib") -> 'lib/x64', while behaving like system.file() +# elsewhere. + +RcppParallel:::test_init() + +arch <- .Platform$r_arch + +# with no arch subdirectory (the usual case off Windows), archSystemFile() +# should be equivalent to a plain system.file() call +if (!nzchar(arch)) { + assert(identical( + archSystemFile("include"), + system.file("include", package = "RcppParallel") + )) + assert(identical( + archSystemFile("include", "RcppParallel.h"), + system.file("include/RcppParallel.h", package = "RcppParallel") + )) +} + +# the composed relative path must match what system.file() is handed: dir, +# then the arch subdir when set, then the optional name -- joined with '/' +expected <- function(dir, name = NULL) { + parts <- c(dir, if (nzchar(arch)) arch, name) + system.file(paste(parts, collapse = "/"), package = "RcppParallel") +} + +assert(identical(archSystemFile("lib"), expected("lib"))) +assert(identical(archSystemFile("lib", "libtbb.so"), expected("lib", "libtbb.so"))) + +# a real, shipped resource resolves to an existing path +header <- archSystemFile("include", "RcppParallel.h") +assert(nzchar(header)) +assert(file.exists(header)) diff --git a/tests/test-fork.R b/tests/test-fork.R new file mode 100644 index 00000000..08186544 --- /dev/null +++ b/tests/test-fork.R @@ -0,0 +1,26 @@ +# Unit tests for isProcessForkedChild(). +# +# The contract: it returns FALSE in the process where RcppParallel was loaded, +# and TRUE in a fork() of that process (e.g. a parallel::mclapply worker). On +# Windows there is no fork(), so it always returns FALSE. + +RcppParallel:::test_init() + +# in the parent process, we are never a forked child +assert(isProcessForkedChild() == FALSE) + +# parallel::mclapply uses fork() on unix, so its workers must report TRUE. +# on Windows there is no fork() (mclapply falls back to serial), so skip: the +# workers there run in the parent process and would correctly report FALSE. +if (!is_windows()) { + + results <- parallel::mclapply(1:2, function(i) { + RcppParallel::isProcessForkedChild() + }, mc.cores = 2L) + + assert(all(vapply(results, isTRUE, logical(1L)))) + +} + +# forking must not disturb the parent's own view of itself +assert(isProcessForkedChild() == FALSE) From 3a8d05020654639c5c25efc6c900115005818606 Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Fri, 24 Jul 2026 16:19:19 -0700 Subject: [PATCH 2/4] compare file paths with normalizePath() rather than identical() identical() is a byte-for-byte string comparison, but two paths can denote the same file while differing in separators, case, or symlink resolution. normalize both sides before comparing, preserving system.file()'s "" not-found sentinel (normalizePath("") would resolve to the working dir). --- tests/test-arch-system-file.R | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/test-arch-system-file.R b/tests/test-arch-system-file.R index 065bca29..e36c7057 100644 --- a/tests/test-arch-system-file.R +++ b/tests/test-arch-system-file.R @@ -8,14 +8,25 @@ RcppParallel:::test_init() arch <- .Platform$r_arch +# compare two file paths for equality. system.file() returns "" when a path is +# not found, so preserve that sentinel; otherwise normalize both sides, since +# strings that differ only in separators, case, or symlinks can still denote +# the same file (and normalizePath("") would resolve to the working directory). +samePath <- function(a, b) { + norm <- function(p) { + if (nzchar(p)) normalizePath(p, winslash = "/", mustWork = FALSE) else p + } + identical(norm(a), norm(b)) +} + # with no arch subdirectory (the usual case off Windows), archSystemFile() # should be equivalent to a plain system.file() call if (!nzchar(arch)) { - assert(identical( + assert(samePath( archSystemFile("include"), system.file("include", package = "RcppParallel") )) - assert(identical( + assert(samePath( archSystemFile("include", "RcppParallel.h"), system.file("include/RcppParallel.h", package = "RcppParallel") )) @@ -28,8 +39,8 @@ expected <- function(dir, name = NULL) { system.file(paste(parts, collapse = "/"), package = "RcppParallel") } -assert(identical(archSystemFile("lib"), expected("lib"))) -assert(identical(archSystemFile("lib", "libtbb.so"), expected("lib", "libtbb.so"))) +assert(samePath(archSystemFile("lib"), expected("lib"))) +assert(samePath(archSystemFile("lib", "libtbb.so"), expected("lib", "libtbb.so"))) # a real, shipped resource resolves to an existing path header <- archSystemFile("include", "RcppParallel.h") From 0e721e66972a48e7c448287fa48c1bacc38c37d9 Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Fri, 24 Jul 2026 16:25:39 -0700 Subject: [PATCH 3/4] test archSystemFile() against 'libs', which actually uses the arch subdir the previous existence check used archSystemFile("include", ...), but the arch-specific subdirectory only ever holds compiled libraries -- headers are never installed under it. on Windows (r_arch = "x64") this looked for 'include/x64/RcppParallel.h', which does not exist; on unix (r_arch = "") the injection was a no-op, so the mistake was invisible locally. use 'libs', where the package's compiled code lives on every platform. --- tests/test-arch-system-file.R | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/test-arch-system-file.R b/tests/test-arch-system-file.R index e36c7057..48c2e6c6 100644 --- a/tests/test-arch-system-file.R +++ b/tests/test-arch-system-file.R @@ -2,7 +2,8 @@ # introduced when systemFile() was renamed (see #251, #252). It injects the # architecture-specific subdirectory (.Platform$r_arch) used on Windows, e.g. # archSystemFile("lib") -> 'lib/x64', while behaving like system.file() -# elsewhere. +# elsewhere. Note that this subdirectory only ever holds compiled libraries +# ('lib', 'libs'); headers and other resources are never installed under it. RcppParallel:::test_init() @@ -23,12 +24,8 @@ samePath <- function(a, b) { # should be equivalent to a plain system.file() call if (!nzchar(arch)) { assert(samePath( - archSystemFile("include"), - system.file("include", package = "RcppParallel") - )) - assert(samePath( - archSystemFile("include", "RcppParallel.h"), - system.file("include/RcppParallel.h", package = "RcppParallel") + archSystemFile("libs"), + system.file("libs", package = "RcppParallel") )) } @@ -42,7 +39,9 @@ expected <- function(dir, name = NULL) { assert(samePath(archSystemFile("lib"), expected("lib"))) assert(samePath(archSystemFile("lib", "libtbb.so"), expected("lib", "libtbb.so"))) -# a real, shipped resource resolves to an existing path -header <- archSystemFile("include", "RcppParallel.h") -assert(nzchar(header)) -assert(file.exists(header)) +# the package's own compiled code is installed under 'libs' (or 'libs/' +# on Windows) -- exactly the arch-aware lookup archSystemFile() exists for -- +# so it must resolve to an existing directory on every platform +libs <- archSystemFile("libs") +assert(nzchar(libs)) +assert(dir.exists(libs)) From 6cf07e2fb9e3c278a0a3a2d673da9c7013f5844a Mon Sep 17 00:00:00 2001 From: Kevin Ushey Date: Fri, 24 Jul 2026 16:27:17 -0700 Subject: [PATCH 4/4] clarify that arch subdirs are not Windows-specific in test comments --- tests/test-arch-system-file.R | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/test-arch-system-file.R b/tests/test-arch-system-file.R index 48c2e6c6..915f8109 100644 --- a/tests/test-arch-system-file.R +++ b/tests/test-arch-system-file.R @@ -1,9 +1,11 @@ # Unit tests for archSystemFile(), the arch-aware wrapper around system.file() -# introduced when systemFile() was renamed (see #251, #252). It injects the -# architecture-specific subdirectory (.Platform$r_arch) used on Windows, e.g. -# archSystemFile("lib") -> 'lib/x64', while behaving like system.file() -# elsewhere. Note that this subdirectory only ever holds compiled libraries -# ('lib', 'libs'); headers and other resources are never installed under it. +# introduced when systemFile() was renamed (see #251, #252). When R is built +# multi-arch -- typically Windows, but also possible on Linux and macOS -- it +# installs compiled libraries under an architecture-specific subdirectory named +# by .Platform$r_arch, e.g. 'libs/x64'. archSystemFile() injects that subdir, +# and behaves like system.file() when r_arch is unset. Note the subdir only +# ever holds compiled libraries ('lib', 'libs'); headers and other resources +# are never installed under it. RcppParallel:::test_init() @@ -40,8 +42,8 @@ assert(samePath(archSystemFile("lib"), expected("lib"))) assert(samePath(archSystemFile("lib", "libtbb.so"), expected("lib", "libtbb.so"))) # the package's own compiled code is installed under 'libs' (or 'libs/' -# on Windows) -- exactly the arch-aware lookup archSystemFile() exists for -- -# so it must resolve to an existing directory on every platform +# under a multi-arch R) -- exactly the arch-aware lookup archSystemFile() +# exists for -- so it must resolve to an existing directory on every platform libs <- archSystemFile("libs") assert(nzchar(libs)) assert(dir.exists(libs))