testit/ 0000755 0001762 0000144 00000000000 13562562422 011575 5 ustar ligges users testit/NAMESPACE 0000644 0001762 0000144 00000000221 13544732632 013011 0 ustar ligges users # Generated by roxygen2: do not edit by hand
export("%==%")
export(assert)
export(has_error)
export(has_warning)
export(test_pkg)
import(utils)
testit/README.md 0000644 0001762 0000144 00000006427 13545011015 013051 0 ustar ligges users # testit
[](https://travis-ci.org/yihui/testit)
[](https://coveralls.io/github/yihui/testit?branch=master)
[](https://cran.r-project.org/package=testit)
This package provides two simple functions (30 lines of code in total):
- `assert(fact, ...)`: think of it as `message(fact)` + `stopifnot(...)`
- `test_pkg(package)`: runs tests with all objects (exported or
non-exported) in the package namespace directly available, so no need to
use the triple-colon `package:::name` for non-exported objects
## Why?
Because it is tedious to type these commands repeatedly in
tests:
```r
message('checking if these numbers are equal...')
stopifnot(all.equal(1, 1+1e-10), 10*.1 == 1)
message('checking if a non-exported function works...')
stopifnot(is.character(package:::utility_foo(x = 'abcd', y = 1:100)))
```
With the two simple functions above, we type six letters (`assert`) instead
of sixteen (`message` + `stopifnot`), and `assert` is also a more intuitive
function name for testing purposes (you _assert_ a fact followed by evidence):
```r
assert('These numbers are equal', {
(all.equal(1, 1 + 1e-10))
(10 * .1 == 1)
})
assert('A non-exported function works', {
res = utility_foo(x = 'abcd', y = 1:100)
(is.character(res))
})
assert('T is TRUE and F is FALSE by default, but can be changed', {
(T == TRUE )
(F == FALSE)
T = FALSE
(T == FALSE)
})
```
## R CMD check
Put the tests under the directory `pkg_name/tests/testit/` (where `pkg_name`
is the root directory of your package), and write a `test-all.R` under
`pkg_name/tests/`:
```r
library(testit)
test_pkg('pkg_name')
```
That is all for `R CMD check`. For package development, it is recommended to
use [**devtools**](https://cran.r-project.org/package=devtools). In
particular, `Ctrl/Cmd + Shift + L` in RStudio makes all objects in a package
visible to you, and you can run tests interactively.
## Installation
Stable version on CRAN:
```r
install.packages('testit')
```
Development version:
```r
devtools::install_github('yihui/testit')
```
## More
How about [**testthat**](https://CRAN.R-project.org/package=testthat)? Well,
this package is far less sophisticated than **testthat**. There is nothing
fancy in this package. Please do consider **testthat** if your tests require
more granularity. I myself do not use **testthat** because I'm too lazy to learn
the new vocabulary (`testthat::expect_xxx`). For **testit**, I do not need to
think if I should use `expect_equal`, `expect_equivalent`, or
`expect_identical`; I just write test conditions in parentheses that are expected to return `TRUE`. That
is the only single rule to remember.
There is no plan to add new features or reinvent anything in this package.
It is an intentionally tiny package.
Although he did not really mean it,
[Xunzi](http://en.wikipedia.org/wiki/Xunzi) said something that happens to
apply well to unit testing:
> 不积跬步,无以至千里;不积小流,无以成江海。
This package is free and open source software, licensed under GPL-3.
testit/man/ 0000755 0001762 0000144 00000000000 13357673757 012370 5 ustar ligges users testit/man/has_message.Rd 0000644 0001762 0000144 00000001270 13544732632 015120 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/testit.R
\name{has_warning}
\alias{has_warning}
\alias{has_error}
\title{Check if an R expression produces warnings or errors}
\usage{
has_warning(expr)
has_error(expr, silent = !interactive())
}
\arguments{
\item{expr}{an R expression}
\item{silent}{logical: should the report of error messages be suppressed?}
}
\value{
A logical value.
}
\description{
The two functions \code{has_warning()} and \code{has_error()} check if an
expression produces warnings and errors, respectively.
}
\examples{
has_warning(1 + 1)
has_warning(1:2 + 1:3)
has_error(2 - 3)
has_error(1 + "a")
has_error(stop("err"), silent = TRUE)
}
testit/man/assert.Rd 0000644 0001762 0000144 00000007572 13544732632 014155 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/testit.R
\name{assert}
\alias{assert}
\alias{\%==\%}
\title{Assertions with an optional message}
\usage{
assert(fact, ...)
x \%==\% y
}
\arguments{
\item{fact}{a message for the assertions when any of them fails; treated the
same way as expressions in \code{...} if it is not a character string,
which means you do not have to provide a message to this function}
\item{...}{an R expression; see Details}
\item{x, y}{two R objects to be compared}
}
\value{
For \code{assert()}, invisible \code{NULL} if all expressions
returned \code{TRUE}, otherwise an error is signalled and the user-provided
message is emitted. For \code{\%==\%}, \code{TRUE} or \code{FALSE}.
}
\description{
The function \code{assert()} was inspired by \code{\link{stopifnot}()}. It
emits a message in case of errors, which can be a helpful hint for diagnosing
the errors (\code{stopifnot()} only prints the possibly truncated source code
of the expressions).
The infix operator \code{\%==\%} is simply an alias of the
\code{\link{identical}()} function to make it slightly easier and intuitive
to write test conditions. \code{x \%==\% y} is the same as
\code{identical(x, y)}. When it is used inside \code{assert()}, a message
will be printed if the returned value is not \code{TRUE}, to show the
values of the LHS (\code{x}) and RHS (\code{y}) via \code{\link{str}()},
which can be helpful for you to check why the assertion failed.
}
\details{
For the \code{...} argument, it should be a single R expression wrapped in
\code{{}}. This expression may contain multiple sub-expressions. A
sub-expression is treated as a test condition if it is wrapped in \code{()}
(meaning its value will be checked to see if it is a logical vector
containing any \code{FALSE} values) , otherwise it is evaluated in the normal
way and its value will not be checked. If the value of the last
sub-expression is logical, it will also be treated as a test condition.
}
\note{
The internal implementation of \code{assert()} is different with the
\code{stopifnot()} function in R \pkg{base}: (1) the custom message
\code{fact} is emitted if an error occurs; (2) \code{assert()} requires the
logical values to be non-empty (\code{logical(0)} will trigger an error);
(3) if \code{...} contains a compound expression in \code{{}} that returns
\code{FALSE} (e.g., \code{if (TRUE) {1+1; FALSE}}), the first and the last
but one line of the source code from \code{\link{deparse}()} are printed in
the error message, otherwise the first line is printed; (4) the arguments
in \code{...} are evaluated sequentially, and \code{assert()} will signal
an error upon the first failed assertion, and will ignore the rest of
assertions.
}
\examples{
## The first way to write assertions -------------------
assert("T is bad for TRUE, and so is F for FALSE", {
T = FALSE
F = TRUE
(T != TRUE) # note the parentheses
(F != FALSE)
})
assert("A Poisson random number is non-negative", {
x = rpois(1, 10)
(x >= 0)
(x > -1) # () is optional because it's the last expression
})
## The second way to write assertions --------------------
assert("one equals one", 1 == 1)
assert("seq and : produce equal sequences", seq(1L, 10L) == 1L:10L)
assert("seq and : produce identical sequences", identical(seq(1L, 10L), 1L:10L))
# multiple tests
T = FALSE
F = TRUE
assert("T is bad for TRUE, and so is F for FALSE", T != TRUE, F != FALSE)
# a mixture of tests
assert("Let's pray all of them will pass", 1 == 1, 1 != 2, letters[4] == "d",
rev(rev(letters)) == letters)
# logical(0) cannot pass assert(), although stopifnot() does not care
try(assert("logical(0) cannot pass", 1 == integer(0)))
stopifnot(1 == integer(0)) # it's OK!
# a compound expression
try(assert("this if statement returns TRUE", if (TRUE) {
x = 1
x == 2
}))
# no message
assert(!FALSE, TRUE, is.na(NA))
}
testit/man/test_pkg.Rd 0000644 0001762 0000144 00000003630 13544732632 014463 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/testit.R
\name{test_pkg}
\alias{test_pkg}
\title{Run the tests of a package in its namespace}
\usage{
test_pkg(package, dir = c("testit", "tests/testit"))
}
\arguments{
\item{package}{the package name}
\item{dir}{the directory of the test files; by default, it is the directory
\file{testit/} or \file{tests/testit/} under the current working directory}
}
\value{
\code{NULL}. All test files are executed, unless an error occurs.
}
\description{
The main purpose of this function is to expose the namespace of a package
when running tests, which allows one to use non-exported objects in the
package without having to resort to the triple colon \code{\link{:::}} trick.
}
\details{
The tests are assumed to be under the \file{testit/} or \file{tests/testit/}
directory by default (depending on your working directory is the package root
directory or the \file{tests/} directory). This function also looks for the
\file{tests/testit/} directory under the package installation directory when
the user-provided \code{dir} does not exist. The test scripts must be named
of the form \samp{test-*.R}; other R scripts will not be treated as test
files (but may also be useful, e.g. you can \code{\link{source}()} them in
tests).
For \command{R CMD check}, this means the test R scripts (\file{test-*.R} are
under \file{pkg_root/tests/testit/}. The R scripts are executed with
\code{\link{sys.source}} in the namespace of the package to be tested; when
an R script is executed, the working directory is the same as the directory
containing this script, and all existing objects in the test environment will
be removed before the code is executed.
}
\note{
All test scripts (\samp{test-*.R}) must be encoded in UTF-8 if they
contain any multibyte characters.
}
\examples{
\dontrun{
test_pkg("testit")
}
}
\seealso{
The \pkg{testthat} package (much more sophisticated).
}
testit/DESCRIPTION 0000644 0001762 0000144 00000001476 13562562422 013313 0 ustar ligges users Package: testit
Type: Package
Title: A Simple Package for Testing R Packages
Version: 0.11
Authors@R: c(
person("Yihui", "Xie", role = c("aut", "cre"), email = "xie@yihui.name", comment = c(ORCID = "0000-0003-0645-5666")),
person("Steven", "Mortimer", role = "ctb", email="reportmort@gmail.com")
)
Description: Provides two convenience functions assert() and test_pkg() to
facilitate testing R packages.
License: GPL-3
URL: https://github.com/yihui/testit
BugReports: https://github.com/yihui/testit/issues
Suggests: rstudioapi
RoxygenNote: 6.1.1
Encoding: UTF-8
NeedsCompilation: no
Packaged: 2019-11-12 15:21:17 UTC; yihui
Author: Yihui Xie [aut, cre] (),
Steven Mortimer [ctb]
Maintainer: Yihui Xie
Repository: CRAN
Date/Publication: 2019-11-12 17:00:02 UTC
testit/tests/ 0000755 0001762 0000144 00000000000 13357673757 012757 5 ustar ligges users testit/tests/test-all.R 0000644 0001762 0000144 00000000111 12661420115 014567 0 ustar ligges users library(testit)
test_pkg('testit')
try(test_pkg('testit', 'test-error'))
testit/tests/test-error/ 0000755 0001762 0000144 00000000000 13357673757 015065 5 ustar ligges users testit/tests/test-error/test-error.R 0000644 0001762 0000144 00000000036 12661414653 017276 0 ustar ligges users stop('An intentional error!')
testit/tests/testit/ 0000755 0001762 0000144 00000000000 13357673757 014273 5 ustar ligges users testit/tests/testit/test-assert.R 0000644 0001762 0000144 00000002171 13326651121 016646 0 ustar ligges users library(testit)
assert('assert works', 1 == 1)
# Okay, that is kind of cheating
assert(
'assert() should signal an error if a condition does not hold',
has_error(assert('this should produce an error', 1 == 2))
)
# a meaningless test in terms of R (failure is irrelevant to Frequentist or Bayesian)
try(assert(
'Frequentists must be correct (http://xkcd.com/1132/)',
'The sun has exploded!', sample(6, 2) == c(6, 6)
), silent = !interactive())
# fail logical(0)
assert(
'assert() should stop on logical(0)',
has_error(assert('1 equals integer(0)', 1 == integer(0)))
)
assert(
'the infix operator %==% works',
1 %==% 1, !(1 %==% 1L)
)
assert(
'has_warning() works',
has_warning(warning('An intentional warning')),
has_warning((function() {1:2 + 1:3})())
)
assert(
'has_error() works',
has_error(stop('An intentional error')),
has_error(1 + 'a')
)
assert(
'has_error() can suppress error message',
has_error(stop('An intentional error'), silent = TRUE),
has_error(1 + 'a', silent = FALSE)
)
assert('tests can be written in () in a single {}', {
(1 == 1L)
z = 1:10
(rev(z) %==% 10:1)
!!TRUE
})
testit/tests/testit/test-utils.R 0000644 0001762 0000144 00000001456 13326651315 016517 0 ustar ligges users library(testit)
# no need to use testit:::available_dir()
assert(
'available_dir() should find an existing directory',
file.exists(
available_dir(c('foobar', 'whatever', '~', system.file('man', package = 'testit')))
),
has_error(available_dir('asdfasdf'))
)
exprs = parse(text = 'if (TRUE) {T&F}\n1+1')
assert(
'deparse_key() fetches the n-1 element if code is in {}',
deparse_key(exprs[[1]]) == 'if (TRUE) { .... T & F'
)
assert(
'deparse_key() returns the parsed code if length == 1',
deparse_key(exprs[[2]]) == '1 + 1'
)
assert('insert_identical() should not work in a non-interactive R session', {
if (!interactive()) has_error(insert_identical())
})
assert('sys.source2() works on empty files', {
f = tempfile()
writeLines(' ', f)
(sys.source2(f, environment()) %==% NULL)
})
testit/NEWS 0000644 0001762 0000144 00000005140 13545010765 012273 0 ustar ligges users CHANGES IN testit VERSION 0.11
MAJOR CHANGES
o The package license was changed from GPL to GPL-3 (@jayvdb #8).
CHANGES IN testit VERSION 0.10
NEW FEATURES
o `test_pkg()` also looks for tests under the `tests/testit/` directory. This
makes it easier for you to run `test_pkg()` under the root directory of the
package, and you don't need to change the working directory to `tests/`.
CHANGES IN testit VERSION 0.9
NEW FEATURES
o Added a new argument `silent` to has_error() (thanks, @StevenMMortimer, #6).
CHANGES IN testit VERSION 0.8
NEW FEATURES
o When `%==%` is used inside assert(), a message will be printed if the value
is not TRUE, to show the values of the LHS and RHS, respectively.
CHANGES IN testit VERSION 0.7
NEW FEATURES
o provided an alternative way to write assertions of the form
assert('fact', {(condition_2); (condition_2)}); see ?testit::assert for more
information
CHANGES IN testit VERSION 0.6
NEW FEATURES
o test_pkg() runs package tests with top-level environment being set to the
namespace of the tested package (thanks, @kalibera, #3)
MAJOR CHANGES
o all test scripts (test-*.R) are assumed to be encoded in UTF-8 if they
contain multibyte characters
CHANGES IN testit VERSION 0.5
NEW FEATURES
o added an infix operator `%==%` as an alias of identical() (in RStudio, you
can use an add-in to insert the text `%==%`)
MINOR CHANGES
o test_pkg() will print out the filename of the R script that errored
CHANGES IN testit VERSION 0.4
MAJOR CHANGES
o the `fact` argument of `assert()` is optional now: all arguments of
`assert()` can be test conditions
CHANGES IN testit VERSION 0.3
MAJOR CHANGES
o the test files have to be named of the form test-*.R (or test-*.r), i.e.
they have to use the prefix test-
o the test environment is always cleaned (all objects removed) before the
next test is run
CHANGES IN testit VERSION 0.2.1
MINOR CHANGES
o fixed a test that failed under R 2.15.x because the argument keep.source
did not exist in parse()
CHANGES IN testit VERSION 0.2
MAJOR CHANGES
o assert() does not use base::stopifnot() any more; a tailored version of
stopifnot() is used now; see ?assert for the differences between this
version and base::stopifnot(); in particular, assert(fact, logical(0))
will fail but stopifnot(logical(0)) will not
CHANGES IN testit VERSION 0.1
NEW FEATURES
o this is the first version of testit; the source code is hosted on
Github: https://github.com/yihui/testit
o added functions assert(), test_pkg(), has_error() and has_warning()
testit/R/ 0000755 0001762 0000144 00000000000 13357673757 012016 5 ustar ligges users testit/R/utils.R 0000644 0001762 0000144 00000003225 13326647504 013266 0 ustar ligges users # find an available dir
available_dir = function(dirs) {
for (i in dirs) {
if (utils::file_test('-d', i)) return(i)
}
stop('none of the directories exists:\n', paste(utils::formatUL(dirs), collapse = '\n'))
}
# tailored for assert(): extract the expression that is likely to be useful for
# diagnostics if possible
deparse_key = function(expr) {
x = deparse(expr, width.cutoff = 100L)
if ((n <- length(x)) <= 1) return(x)
# if expression is in {}, fetch the line n-1, otherwise use the first line
paste(x[1], '....', if (x[n] == '}') sub('^\\s*', '', x[n - 1L]))
}
# whether every element of x is strictly TRUE
all_true = function(x) {
is.logical(x) && length(x) && !any(is.na(x)) && all(x)
}
insert_identical = function() {
rstudioapi::insertText(text = ' %==% ')
}
# This function is a modification of base::sys.source. It allows to specify
# the top-level environment, which is by default "envir" (the same as in
# base::sys.source), but for package testing it is desirable to use the
# package namespace to mimick the environment structure used when packages
# are running. This function assumes that chdir = FALSE and keep.source = TRUE.
sys.source2 = function(file, envir, top.env = as.environment(envir)) {
oop = options(keep.source = TRUE, topLevelEnvironment = top.env)
on.exit(options(oop))
lines = readLines(file, warn = FALSE, encoding = 'UTF-8')
srcfile = srcfilecopy(file, lines, file.mtime(file), isFile = TRUE)
exprs = parse(text = lines, srcfile = srcfile, encoding = 'UTF-8')
if (length(exprs) == 0L) return()
owd = setwd(dirname(file)); on.exit(setwd(owd), add = TRUE)
for (i in seq_along(exprs)) eval(exprs[i], envir)
}
testit/R/testit.R 0000644 0001762 0000144 00000022027 13544732603 013440 0 ustar ligges users #' Assertions with an optional message
#'
#' The function \code{assert()} was inspired by \code{\link{stopifnot}()}. It
#' emits a message in case of errors, which can be a helpful hint for diagnosing
#' the errors (\code{stopifnot()} only prints the possibly truncated source code
#' of the expressions).
#'
#' For the \code{...} argument, it should be a single R expression wrapped in
#' \code{{}}. This expression may contain multiple sub-expressions. A
#' sub-expression is treated as a test condition if it is wrapped in \code{()}
#' (meaning its value will be checked to see if it is a logical vector
#' containing any \code{FALSE} values) , otherwise it is evaluated in the normal
#' way and its value will not be checked. If the value of the last
#' sub-expression is logical, it will also be treated as a test condition.
#' @param fact a message for the assertions when any of them fails; treated the
#' same way as expressions in \code{...} if it is not a character string,
#' which means you do not have to provide a message to this function
#' @param ... an R expression; see Details
#' @return For \code{assert()}, invisible \code{NULL} if all expressions
#' returned \code{TRUE}, otherwise an error is signalled and the user-provided
#' message is emitted. For \code{\%==\%}, \code{TRUE} or \code{FALSE}.
#' @note The internal implementation of \code{assert()} is different with the
#' \code{stopifnot()} function in R \pkg{base}: (1) the custom message
#' \code{fact} is emitted if an error occurs; (2) \code{assert()} requires the
#' logical values to be non-empty (\code{logical(0)} will trigger an error);
#' (3) if \code{...} contains a compound expression in \code{{}} that returns
#' \code{FALSE} (e.g., \code{if (TRUE) {1+1; FALSE}}), the first and the last
#' but one line of the source code from \code{\link{deparse}()} are printed in
#' the error message, otherwise the first line is printed; (4) the arguments
#' in \code{...} are evaluated sequentially, and \code{assert()} will signal
#' an error upon the first failed assertion, and will ignore the rest of
#' assertions.
#' @export
#' @examples
#' ## The first way to write assertions -------------------
#'
#' assert('T is bad for TRUE, and so is F for FALSE', {T=FALSE;F=TRUE
#' (T!=TRUE) # note the parentheses
#' (F!=FALSE)})
#'
#' assert('A Poisson random number is non-negative', {
#' x = rpois(1, 10)
#' (x >= 0)
#' (x > -1) # () is optional because it's the last expression
#' })
#'
#'
#' ## The second way to write assertions --------------------
#'
#' assert('one equals one', 1==1)
#' assert('seq and : produce equal sequences', seq(1L, 10L) == 1L:10L)
#' assert('seq and : produce identical sequences', identical(seq(1L, 10L), 1L:10L))
#'
#' # multiple tests
#' T=FALSE; F=TRUE
#' assert('T is bad for TRUE, and so is F for FALSE', T!=TRUE, F!=FALSE)
#'
#' # a mixture of tests
#' assert("Let's pray all of them will pass", 1==1, 1!=2, letters[4]=='d', rev(rev(letters))==letters)
#'
#' # logical(0) cannot pass assert(), although stopifnot() does not care
#' try(assert('logical(0) cannot pass', 1==integer(0)))
#' stopifnot(1==integer(0)) # it's OK!
#'
#' # a compound expression
#' try(assert('this if statement returns TRUE', if(TRUE){x=1;x==2}))
#'
#' # no message
#' assert(!FALSE, TRUE, is.na(NA))
assert = function(fact, ...) {
opt = options(testit.asserting = TRUE); on.exit(options(opt), add = TRUE)
mc = match.call()
# match.call() uses the arg order in the func def, so fact is always 1st arg
fact = NULL
if (is.character(mc[[2]])) {
fact = mc[[2]]; mc = mc[-2]
}
one = one_expression(mc)
assert2(fact, if (one) mc[[2]][-1] else mc[-1], parent.frame(), !one)
}
# whether the argument of a function call is a single expression in {}
one_expression = function(call) {
length(call) == 2 && length(call[[2]]) >= 1 && identical(call[[c(2, 1)]], as.symbol('{'))
}
assert2 = function(fact, exprs, envir, all = TRUE) {
n = length(exprs)
for (i in seq_len(n)) {
expr = exprs[[i]]
val = eval(expr, envir = envir, enclos = NULL)
# special case: fact is an expression instead of a string constant in assert()
if (is.null(fact) && all && i == 1 && is.character(val)) {
fact = val; next
}
# check all values in case of multiple arguments, o/w only check values in ()
if (all || (i == n && is.logical(val)) ||
(length(expr) >= 1 && identical(expr[[1]], as.symbol('(')))) {
if (all_true(val)) next
if (!is.null(fact)) message('assertion failed: ', fact)
stop(sprintf(
ngettext(length(val), '%s is not TRUE', '%s are not all TRUE'),
deparse_key(expr)
), call. = FALSE, domain = NA)
}
}
}
#' @description The infix operator \code{\%==\%} is simply an alias of the
#' \code{\link{identical}()} function to make it slightly easier and intuitive
#' to write test conditions. \code{x \%==\% y} is the same as
#' \code{identical(x, y)}. When it is used inside \code{assert()}, a message
#' will be printed if the returned value is not \code{TRUE}, to show the
#' values of the LHS (\code{x}) and RHS (\code{y}) via \code{\link{str}()},
#' which can be helpful for you to check why the assertion failed.
#' @param x,y two R objects to be compared
#' @rdname assert
#' @import utils
#' @export
`%==%` = function(x, y) {
res = identical(x, y)
if (!res && isTRUE(getOption('testit.asserting', FALSE))) {
mc = match.call()
info = paste(capture.output({
cat(deparse_key(mc[[2]]), '(LHS) ==>\n')
str(x)
cat('----------\n')
str(y)
cat('<== (RHS)', deparse_key(mc[[3]]), '\n')
}), collapse = '\n')
message(info)
}
res
}
#' Run the tests of a package in its namespace
#'
#' The main purpose of this function is to expose the namespace of a package
#' when running tests, which allows one to use non-exported objects in the
#' package without having to resort to the triple colon \code{\link{:::}} trick.
#'
#' The tests are assumed to be under the \file{testit/} or \file{tests/testit/}
#' directory by default (depending on your working directory is the package root
#' directory or the \file{tests/} directory). This function also looks for the
#' \file{tests/testit/} directory under the package installation directory when
#' the user-provided \code{dir} does not exist. The test scripts must be named
#' of the form \samp{test-*.R}; other R scripts will not be treated as test
#' files (but may also be useful, e.g. you can \code{\link{source}()} them in
#' tests).
#'
#' For \command{R CMD check}, this means the test R scripts (\file{test-*.R} are
#' under \file{pkg_root/tests/testit/}. The R scripts are executed with
#' \code{\link{sys.source}} in the namespace of the package to be tested; when
#' an R script is executed, the working directory is the same as the directory
#' containing this script, and all existing objects in the test environment will
#' be removed before the code is executed.
#' @param package the package name
#' @param dir the directory of the test files; by default, it is the directory
#' \file{testit/} or \file{tests/testit/} under the current working directory
#' @return \code{NULL}. All test files are executed, unless an error occurs.
#' @note All test scripts (\samp{test-*.R}) must be encoded in UTF-8 if they
#' contain any multibyte characters.
#' @seealso The \pkg{testthat} package (much more sophisticated).
#' @export
#' @examples \dontrun{test_pkg('testit')}
test_pkg = function(package, dir = c('testit', 'tests/testit')) {
library(package, character.only = TRUE)
path = available_dir(c(dir, system.file('tests', 'testit', package = package)))
rs = list.files(path, '^test-.+[.][rR]$', full.names = TRUE)
# make all objects in the package visible to tests
env = new.env(parent = getNamespace(package))
for (r in rs) {
rm(list = ls(env, all.names = TRUE), envir = env)
withCallingHandlers(
sys.source2(r, envir = env, top.env = getNamespace(package)),
error = function(e) {
z = .traceback(5)
if (length(z) == 0) return()
z = z[[1]]
n = length(z)
s = if (!is.null(srcref <- attr(z, 'srcref'))) {
paste0(' at ', basename(attr(srcref, 'srcfile')$filename), '#', srcref[1])
}
cat('Error from', z[1], if (n > 1) '...', s, '\n')
}
)
}
}
#' Check if an R expression produces warnings or errors
#'
#' The two functions \code{has_warning()} and \code{has_error()} check if an
#' expression produces warnings and errors, respectively.
#' @param expr an R expression
#' @param silent logical: should the report of error messages be suppressed?
#' @return A logical value.
#' @export
#' @rdname has_message
#' @examples has_warning(1+1); has_warning(1:2+1:3)
#'
#' has_error(2-3); has_error(1+'a'); has_error(stop("err"), silent = TRUE)
has_warning = function(expr) {
warn = FALSE
op = options(warn = -1); on.exit(options(op))
withCallingHandlers(expr, warning = function(w) {
warn <<- TRUE
invokeRestart('muffleWarning')
})
warn
}
#' @export
#' @rdname has_message
has_error = function(expr, silent = !interactive()) {
inherits(try(force(expr), silent = silent), 'try-error')
}
testit/MD5 0000644 0001762 0000144 00000001303 13562562422 012102 0 ustar ligges users 52384a66501aa0f0ba0dd9e8c416465d *DESCRIPTION
78c5938d8bfd6df28866607550a87a6b *NAMESPACE
dff261865366549d04a95b1ba33ffbb8 *NEWS
48fa6de6702d202fab334564d6100a18 *R/testit.R
943b8431c9ca795587afcd52ebc210d3 *R/utils.R
ee681d9380f6cbce1f70681e310c2113 *README.md
4aae9b69bfa4f9baa95bd50f98274ba3 *inst/rstudio/addins.dcf
61838cbc08881d522d3dcf406b971403 *man/assert.Rd
31e04410a6e767062af385680c9d9b6c *man/has_message.Rd
768b639bfcf9290b21550404635fbfae *man/test_pkg.Rd
fc95de596ff143707ce5da2874086a92 *tests/test-all.R
5330c047ff926dcaf08d9266db9a71f8 *tests/test-error/test-error.R
19586fa6d7b802908a529efc2d81a8f0 *tests/testit/test-assert.R
d0e552278f4254984e245eae8b556f76 *tests/testit/test-utils.R
testit/inst/ 0000755 0001762 0000144 00000000000 12660727755 012564 5 ustar ligges users testit/inst/rstudio/ 0000755 0001762 0000144 00000000000 12660730017 014236 5 ustar ligges users testit/inst/rstudio/addins.dcf 0000644 0001762 0000144 00000000170 12660730065 016157 0 ustar ligges users Name: Insert %==%
Description: Insert the infix operator %==% from testit.
Binding: insert_identical
Interactive: false