diff --git a/DESCRIPTION b/DESCRIPTION index 37f0f627..b1e8d294 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -26,7 +26,6 @@ Description: High level functions for parallel programming with 'Rcpp'. Depends: R (>= 3.6.0) Suggests: Rcpp, - RUnit, knitr, rmarkdown Roxygen: list(markdown = TRUE) diff --git a/R/test.R b/R/test.R new file mode 100644 index 00000000..56446a0a --- /dev/null +++ b/R/test.R @@ -0,0 +1,71 @@ +# Minimal test helpers used by the package's own test scripts in tests/. +# +# The test files (tests/test-*.R) are plain R scripts run by 'R CMD check'. +# Each one begins with a call to test_init(), which performs the shared setup +# those scripts rely on: choosing a default parallel backend, seeding the RNG +# for reproducibility, and working around environment quirks that would +# otherwise break R sub-processes spawned during testing. + +# Copy every object in the RcppParallel namespace -- including internal, +# non-exported functions and constants -- into 'envir', so test scripts can +# reference them directly without the 'RcppParallel:::' prefix. Modelled on +# renv:::summon(). test_init() calls this for the test script's environment. +summon <- function(envir = parent.frame()) { + namespace <- asNamespace("RcppParallel") + contents <- as.list.environment(namespace, all.names = TRUE) + list2env(contents, envir = envir) + invisible(envir) +} + +# Assert that 'cond' is true. Like a single-condition stopifnot(), but it also +# understands the all.equal() idiom: all.equal() returns TRUE when its +# arguments match and a character description of the differences when they do +# not, so a character result is reported as a failure. A logical 'cond' passes +# only if every element is TRUE (NAs count as failures), which makes it safe to +# pass a logical vector directly, e.g. assert(x > 0). +assert <- function(cond) { + + label <- paste(deparse(substitute(cond)), collapse = " ") + + # a character result (e.g. from all.equal()) describes a mismatch + if (is.character(cond)) + stop(sprintf("assertion failed: %s\n%s", label, paste(cond, collapse = "\n")), + call. = FALSE) + + if (!is.logical(cond) || anyNA(cond) || !all(cond)) + stop(sprintf("assertion failed: %s", label), call. = FALSE) + + invisible(TRUE) + +} + +test_init <- function() { + + # R CMD check sets R_TESTS=startup.Rs, which breaks R sub-processes run + # with a different working directory (e.g. the Rscript invocations in a + # test package's Makevars). Clearing it avoids those failures. + # (based on practice in Rcpp) + Sys.setenv(R_TESTS = "") + + # make tests that use random data deterministic + set.seed(42L) + + # choose a default backend unless one was requested explicitly; tinythread + # is always available, so tests can run even where TBB is not enabled + backend <- Sys.getenv("RCPP_PARALLEL_BACKEND", unset = NA) + if (is.na(backend)) + Sys.setenv(RCPP_PARALLEL_BACKEND = "tinythread") + + writeLines(paste("Using backend:", Sys.getenv("RCPP_PARALLEL_BACKEND"))) + + # ensure the packages used by the C++ tests are available + requireNamespace("Rcpp", quietly = TRUE) + requireNamespace("RcppParallel", quietly = TRUE) + + # make all RcppParallel functions (including internal ones) directly + # referenceable in the calling test script, without the ':::' prefix + summon(parent.frame()) + + invisible(TRUE) + +} diff --git a/inst/tests/runit.distance.R b/inst/tests/runit.distance.R deleted file mode 100644 index f85a5802..00000000 --- a/inst/tests/runit.distance.R +++ /dev/null @@ -1,18 +0,0 @@ - -library(Rcpp) -library(RUnit) - -sourceCpp(system.file("tests/cpp/distance.cpp", package = "RcppParallel")) - -test.distance <- function() { - - n <- 1000 - m <- matrix(runif(n*10), ncol = 10) - m <- m/rowSums(m) - - checkEquals( - rcpp_js_distance(m), - rcpp_parallel_js_distance(m) - ) - -} diff --git a/inst/tests/runit.innerproduct.R b/inst/tests/runit.innerproduct.R deleted file mode 100644 index 2f9260cb..00000000 --- a/inst/tests/runit.innerproduct.R +++ /dev/null @@ -1,17 +0,0 @@ - -library(Rcpp) -library(RUnit) - -sourceCpp(system.file("tests/cpp/innerproduct.cpp", package = "RcppParallel")) - -test.innerproduct <- function() { - - x <- runif(1000000) - y <- runif(1000000) - - checkEquals( - innerProduct(x, y), - parallelInnerProduct(x, y) - ) - -} diff --git a/inst/tests/runit.sum.R b/inst/tests/runit.sum.R deleted file mode 100644 index 4792b1cd..00000000 --- a/inst/tests/runit.sum.R +++ /dev/null @@ -1,15 +0,0 @@ - -library(Rcpp) -library(RUnit) - -sourceCpp(system.file("tests/cpp/sum.cpp", package = "RcppParallel")) - -test.sum <- function() { - - v <- as.numeric(c(1:10000000)) - - checkEquals( - vectorSum(v), - parallelVectorSum(v) - ) -} diff --git a/inst/tests/runit.transform.R b/inst/tests/runit.transform.R deleted file mode 100644 index d056dbdb..00000000 --- a/inst/tests/runit.transform.R +++ /dev/null @@ -1,15 +0,0 @@ - -library(Rcpp) -library(RUnit) - -sourceCpp(system.file("tests/cpp/transform.cpp", package = "RcppParallel")) - -test.transform <- function() { - - m <- matrix(as.numeric(c(1:1000000)), nrow = 1000, ncol = 1000) - - checkEquals( - matrixSqrt(m), - parallelMatrixSqrt(m) - ) -} diff --git a/inst/tests/runit.truefalse_macros.R b/inst/tests/runit.truefalse_macros.R deleted file mode 100644 index 8e403e36..00000000 --- a/inst/tests/runit.truefalse_macros.R +++ /dev/null @@ -1,6 +0,0 @@ - -library(Rcpp) -library(RUnit) - -sourceCpp(system.file("tests/cpp/truefalse_macros.cpp", package = "RcppParallel")) - diff --git a/tests/doRUnit.R b/tests/doRUnit.R deleted file mode 100644 index 34d49eb7..00000000 --- a/tests/doRUnit.R +++ /dev/null @@ -1,41 +0,0 @@ -stopifnot(require(RUnit, quietly = TRUE)) -stopifnot(require(Rcpp, quietly = TRUE)) -stopifnot(require(RcppParallel, quietly = TRUE)) - -## Set a seed to make the test deterministic -set.seed(42) - -## Set a default backend -backend <- Sys.getenv("RCPP_PARALLEL_BACKEND", unset = NA) -if (is.na(backend)) - Sys.setenv(RCPP_PARALLEL_BACKEND = "tinythread") - -writeLines(paste("Using backend:", Sys.getenv("RCPP_PARALLEL_BACKEND"))) - -## Define tests -suite <- defineTestSuite( - name = "RcppParallel Unit Tests", - dirs = system.file("tests", package = "RcppParallel") -) - -## Based on practice in Rcpp to avoid some test failures -Sys.setenv("R_TESTS" = "") - -## Run tests -tests <- runTestSuite(suite) - -## Print results -printTextProtocol(tests) - -## Return success or failure to R CMD CHECK -if (getErrors(tests)$nFail > 0) { - stop("TEST FAILED!") -} - -if (getErrors(tests)$nErr > 0) { - stop("TEST HAD ERRORS!") -} - -if (getErrors(tests)$nTestFunc < 1) { - stop("NO TEST FUNCTIONS RUN!") -} diff --git a/tests/test-distance.R b/tests/test-distance.R new file mode 100644 index 00000000..7e66bed5 --- /dev/null +++ b/tests/test-distance.R @@ -0,0 +1,12 @@ +RcppParallel:::test_init() + +Rcpp::sourceCpp(system.file("tests/cpp/distance.cpp", package = "RcppParallel")) + +n <- 1000 +m <- matrix(runif(n * 10), ncol = 10) +m <- m / rowSums(m) + +assert(all.equal( + rcpp_js_distance(m), + rcpp_parallel_js_distance(m) +)) diff --git a/tests/test-innerproduct.R b/tests/test-innerproduct.R new file mode 100644 index 00000000..c1374fb0 --- /dev/null +++ b/tests/test-innerproduct.R @@ -0,0 +1,11 @@ +RcppParallel:::test_init() + +Rcpp::sourceCpp(system.file("tests/cpp/innerproduct.cpp", package = "RcppParallel")) + +x <- runif(1000000) +y <- runif(1000000) + +assert(all.equal( + innerProduct(x, y), + parallelInnerProduct(x, y) +)) diff --git a/tests/testInstallLibs.R b/tests/test-install-libs.R similarity index 99% rename from tests/testInstallLibs.R rename to tests/test-install-libs.R index 387381b1..3b98806b 100644 --- a/tests/testInstallLibs.R +++ b/tests/test-install-libs.R @@ -6,6 +6,8 @@ # helper definitions that precede its '# Main' section (evaluating the whole # file would trigger an actual install), and exercise them directly. +RcppParallel:::test_init() + # locate src/install.libs.R by walking up from the working directory; this is # reachable when tests are run from the package sources. If it cannot be found # (e.g. only the installed package is available), skip rather than fail. diff --git a/tests/scalable-allocator.R b/tests/test-scalable-allocator.R similarity index 92% rename from tests/scalable-allocator.R rename to tests/test-scalable-allocator.R index c2944193..0957178c 100644 --- a/tests/scalable-allocator.R +++ b/tests/test-scalable-allocator.R @@ -9,7 +9,7 @@ # # https://github.com/RcppCore/RcppParallel/pull/262 -library(RcppParallel) +RcppParallel:::test_init() # building the test package requires a toolchain, so keep this test # off of CRAN's machines @@ -20,7 +20,7 @@ if (!ci) { } # the scalable allocator is only available with the TBB backend -if (!RcppParallel:::TBB_ENABLED) { +if (!TBB_ENABLED) { writeLines("TBB is not enabled; skipping scalable allocator test.") quit(save = "no") } @@ -89,11 +89,6 @@ libDir <- file.path(tempdir(), "library") dir.create(libDir, recursive = TRUE, showWarnings = FALSE) Sys.setenv(R_LIBS = paste(.libPaths(), collapse = .Platform$path.sep)) -# R CMD check sets R_TESTS=startup.Rs, which breaks R sub-processes run -# with a different working directory -- like the Rscript invocations in -# the test package's Makevars (see also tests/doRUnit.R) -Sys.setenv(R_TESTS = "") - rExe <- file.path(R.home("bin"), if (.Platform$OS.type == "windows") "R.exe" else "R") args <- c("CMD", "INSTALL", "--no-multiarch", paste0("--library=", shQuote(libDir)), shQuote(pkgRoot)) @@ -108,4 +103,4 @@ if (is.numeric(status) && status != 0L) invisible(loadNamespace("scalabletest", lib.loc = libDir)) ok <- .Call("scalable_roundtrip", 1000L, PACKAGE = "scalabletest") writeLines(paste("scalable allocator round trip:", if (isTRUE(ok)) "OK" else "FAILED")) -stopifnot(isTRUE(ok)) +assert(isTRUE(ok)) diff --git a/tests/test-sum.R b/tests/test-sum.R new file mode 100644 index 00000000..3133d05a --- /dev/null +++ b/tests/test-sum.R @@ -0,0 +1,10 @@ +RcppParallel:::test_init() + +Rcpp::sourceCpp(system.file("tests/cpp/sum.cpp", package = "RcppParallel")) + +v <- as.numeric(1:10000000) + +assert(all.equal( + vectorSum(v), + parallelVectorSum(v) +)) diff --git a/tests/test-transform.R b/tests/test-transform.R new file mode 100644 index 00000000..25edc985 --- /dev/null +++ b/tests/test-transform.R @@ -0,0 +1,10 @@ +RcppParallel:::test_init() + +Rcpp::sourceCpp(system.file("tests/cpp/transform.cpp", package = "RcppParallel")) + +m <- matrix(as.numeric(1:1000000), nrow = 1000, ncol = 1000) + +assert(all.equal( + matrixSqrt(m), + parallelMatrixSqrt(m) +)) diff --git a/tests/test-truefalse-macros.R b/tests/test-truefalse-macros.R new file mode 100644 index 00000000..3259d7a8 --- /dev/null +++ b/tests/test-truefalse-macros.R @@ -0,0 +1,5 @@ +RcppParallel:::test_init() + +# this file has no runtime assertions: the TRUE/FALSE macro checks are +# static_assert()s in the C++ source, so a successful compile is the test +Rcpp::sourceCpp(system.file("tests/cpp/truefalse_macros.cpp", package = "RcppParallel"))