0

I'm running my package through R CMD check and the only (remaining) warning is the following:

W  checking for unstated dependencies in 'tests' (4.4s)
   '::' or ':::' import not declared from: 'devtools'

After getting confused for a long time about this seemingly nonsensical warning, I realized it's coming from my "test manager" script (see reason for its need below). This is file pkg/tests/testthat.R, while the tests themselves are in pkg/tests/testthat/.

# testthat.R
sink(stderr(), type = "output")

x <- tryCatch(
  {
    x <- data.frame(devtools::test())  # here's the problem!

    as.numeric(sum(x$failed) > 0)
  },
  error = function(e) {
    1
  }
)

sink(NULL, type = "output")
cat(1)

If I comment out this entire file, the R CMD check warning vanishes.

And then the weird part: if I replace devtools::test() with just test(), the R CMD check warning vanishes.

However, the purpose of this "manager" script is to be to be called (via Rscript) by a git pre-commit hook. This way, I can run all my tests to ensure the commit is stable. Due to this, I can't use test(), since devtools isn't loaded when the script is run via Rscript.

I tried a few things to satisfy both R CMD check and being called by Rscript:

  • Using library(devtools) doesn't work (throws a package not found error);
  • Moving testthat.R out of the /tests/ folder and into the top-level. This kills the R CMD check warning, but it now instead throws a note: Non-standard file/directory found at top level: 'testthat.R', so not exactly satisfactory (especially since keeping it in the /tests/ directory seems more logically consistent);
  • Testing for a function which has been apparently loaded by R CMD check to determine behavior. Since using a naked test() works, I assumed devtools was loaded, so prepended the following to the file (and used runTests on the problematic line). The logic being, if we can find test(), use it. If we can't, then this probably isn't R CMD check, so we can use the full name.
if (length(find("test")) == 0) {
  runTests <- devtools::test()
} else {
  runTests <- test()
}

Unfortunately, this just made things worse: the warning remains and we also get an error on the if-else block:

> if (length(find("test")) == 0) {
+   runTests <- devtools::test()
+ } else {
+   runTests <- test()
+ }
Error in loadNamespace(name) : there is no package called 'devtools'
Calls: :: ... loadNamespace -> withRestarts -> withOneRestart -> doWithOneRestart

Why devtools::test() throws an error here and just a warning on the problematic line is beyond me.

  • Similarly, using testthat::skip(). Also doesn't work.

So, what can I do to satisfy both R CMD check and being called by Rscript? Is there a way to tell R CMD check to ignore this file?


For the record, this is my git pre-commit hook, in case it can be reformulated to solve this problem some other way

#!/bin/sh

R_USER="D:/Users/wasabi/Documents"
export R_USER

# check that Rscript is accessible via PATH; fail otherwise
command -v Rscript >/dev/null || {
    echo "Rscript must be accessible via PATH. Commit aborted.";
    exit 1;
};

# check whether there are unstaged changes. If so, stash them.
# This allows the tests to run only on previously committed or
# indexed (added on this commit) changes.
hasChanges=$(git diff)
if [ -n "$hasChanges" ]; then
    git stash push --keep-index
fi

exitCode=$(Rscript tests/testthat.R)

# remember to unstash any unstaged changes
if [ -n "$hasChanges" ]; then
    git stash pop
fi

exit $exitCode
Wasabi
  • 2,879
  • 3
  • 26
  • 48

1 Answers1

1

The solution is to simply add tests/testthat.R to .Rbuildignore (either by hand in the form of a regular expression or using usethis::use_build_ignore("tests/testthat.R")).

If you actually run R CMD check, the warning will still appear (since it runs on the source files, and therefore ignores .Rbuildignore, unless you run it on the binary itself).

But the "Check Package" command in RStudio relies on devtools::check(), which builds the package first and then checks the binary, therefore not getting the error. And since that's how my team and I will actually be running the checks, it's sufficient.

Solution inspired by this question.

Wasabi
  • 2,879
  • 3
  • 26
  • 48