Title: Add Tests to Examples
Version: 0.2.0
Description: Add tests in-line in examples. Provides standalone functions for facilitating easier test writing in Rd files. However, a more familiar interface is provided using 'roxygen2' tags. Tools are also provided for facilitating package configuration and use with 'testthat'.
URL: https://github.com/dgkf/testex, https://dgkf.github.io/testex/
License: MIT + file LICENSE
Depends: R (≥ 3.2.0)
Imports: utils
Suggests: testthat, withr, callr, roxygen2, spelling, knitr, rmarkdown
Encoding: UTF-8
RoxygenNote: 7.3.1
Language: en-US
VignetteBuilder: knitr
Config/testex/options: list(version = "0.2.0")
NeedsCompilation: no
Packaged: 2024-04-14 23:16:45 UTC; root
Author: Doug Kelkhoff [aut, cre]
Maintainer: Doug Kelkhoff <doug.kelkhoff@gmail.com>
Repository: CRAN
Date/Publication: 2024-04-14 23:30:02 UTC

Convert to srcref

Description

Convert to srcref

Usage

as.srcref(x)

## S3 method for class 'character'
as.srcref(x)

Arguments

x

an object to coerce

Value

A srcref

Methods (by class)


Deparse an expression and indent for pretty-printing

Description

Deparse an expression and indent for pretty-printing

Usage

deparse_indent(x, indent = 0L)

Arguments

x

A code object

indent

An integer number of spaces or a string to prefix each line of the deparsed output.

Value

An indented version of the deparsed string from x.


Deparse pretty

Description

Deparse to a single string with two spaces of indentation

Usage

deparse_pretty(expr)

Arguments

expr

An expression to deparse

Value

A pretty-formatted string representation of expr.


Expect no Error

Description

Expect no Error

Usage

fallback_expect_no_error(object, ...)

Arguments

object

An expression to evaluate

...

Additional arguments unused

Value

The value produced by the expectation code

Note

This is a stop-gap implementation, and will only be used for legacy versions of testthat before this was properly supported.

A testthat expectation that the provided code can be evaluated without producing an error. This is the most basic expectation one should expect of any example code. Further expectations are provided in subsequent testthat code.


Return the number of characters in a line of a file

Description

Return the number of characters in a line of a file

Usage

file_line_nchar(file, line)

Arguments

file

A file to use as reference

line

A line number to retrieve the length of

Value

The number of characters in line line.


Determine which symbol to use by default when testing examples

Description

Determine which symbol to use by default when testing examples

Usage

get_example_value()

Value

The value of the last test expression


Test whether currently executing R checks

Description

Test whether currently executing R checks

Usage

is_r_cmd_check()

Value

A logical indicating whether ⁠R CMD check⁠ is currently running


Package source file helpers

Description

Discover specific package related file paths

Usage

find_package_root(path = ".", quiet = FALSE)

find_package_rds(package, path = getwd())

package_desc(path = getwd())

Arguments

path

A file path within a package's source code or installation directory. Only considered if package is missing.

quiet

Whether to suppress output

package

A package name

Value

NULL, invisibly

A list of package Rd objects, as returned by tools::Rd_db()


Register a method for a suggested dependency

Description

Generally, the recommend way to register an S3 method is to use the S3Method() namespace directive (often generated automatically by the ⁠@export⁠ roxygen2 tag). However, this technique requires that the generic be in an imported package, and sometimes you want to suggest a package, and only provide a method when that package is loaded. s3_register() can be called from your package's .onLoad() to dynamically register a method only if the generic's package is loaded.

Arguments

generic

Name of the generic in the form pkg::generic.

class

Name of the class

method

Optionally, the implementation of the method. By default, this will be found by looking for a function called generic.class in the package environment.

Note that providing method can be dangerous if you use devtools. When the namespace of the method is reloaded by devtools::load_all(), the function will keep inheriting from the old namespace. This might cause crashes because of dangling .Call() pointers.

Details

For R 3.5.0 and later, s3_register() is also useful when demonstrating class creation in a vignette, since method lookup no longer always involves the lexical scope. For R 3.6.0 and later, you can achieve a similar effect by using "delayed method registration", i.e. placing the following in your NAMESPACE file:

if (getRversion() >= "3.6.0") {
  S3method(package::generic, class)
}

Value

No return value, called for side-effect of registering an S3 generic.

Usage in other packages

To avoid taking a dependency on vctrs, you copy the source of s3_register() into your own package. It is licensed under the permissive unlicense to make it crystal clear that we're happy for you to do this. There's no need to include the license or even credit us when using this function.

Examples

# A typical use case is to dynamically register tibble/pillar methods
# for your class. That way you avoid creating a hard dependency on packages
# that are not essential, while still providing finer control over
# printing when they are used.

.onLoad <- function(...) {
  s3_register("pillar::pillar_shaft", "vctrs_vctr")
  s3_register("tibble::type_sum", "vctrs_vctr")
}


Split a Source Reference at specific lines

Description

Split a Source Reference at specific lines

Usage

split_srcref(sr, where)

Arguments

sr

An original srcref object

where

A numeric vector of line numbers where the srcref should be split

Value

A list of srcref


Build a source location from a minimal numeric vector

Description

Build a length four source location from a length two source location. The starting column on the first line is assumed to be 1, and the final column is taken to be the length of the line if the source file exists, or 1 as a fallback.

Usage

srclocs(x, file)

Arguments

x

A numeric vector of at least length 2

file

A file to use to determine the length of the final line

Value

A numeric vector similar to a utils::getSrcLocation object


Convert a srcref to a character representation

Description

Convert a srcref to a character representation

Usage

srcref_key(x, nloc = 2, path = c("base", "root", "full"))

Arguments

x

A srcref object

nloc

The number of locations (utils::getSrcLocation) to use. Defaults to 2, indicating starting and ending line number.

path

A form of file path to use for the key. One of "base" for only the basename of the source file path, "root" for a path relative to a package root directory if found, or "full" for the full file path.

Value

A string hash of a srcref


Determine the number of source code lines of a given srcref

Description

Determine the number of source code lines of a given srcref

Usage

srcref_nlines(x)

Arguments

x

A srcref object

Value

The number of lines in the original source of a srcref


Get String Line Count

Description

Get String Line Count

Usage

string_newline_count(x)

Arguments

x

A character value

Value

The number of newline characters in a multiline string


Execute examples from Rd files as testthat tests

Description

Reads examples from Rd files and constructs testthat-style tests. testthat expectations are built such that

Usage

test_examples_as_testthat(
  package,
  path,
  ...,
  test_dir = file.path(tempdir(), "testex-tests"),
  clean = TRUE,
  overwrite = TRUE,
  roxygenize = !is_r_cmd_check() && uses_roxygen2(path),
  reporter = testthat::get_reporter()
)

Arguments

package

A package name whose examples should be tested

path

Optionally, a path to a source code directory to use. Will only have an effect if parameter package is missing.

...

Additional argument unused

test_dir

An option directory where test files should be written. Defaults to a temporary directory.

clean

Whether the test_dir should be removed upon completion of test execution. Defaults to TRUE.

overwrite

Whether files should be overwritten if test_dir already exists. Defaults to TRUE.

roxygenize

Whether R documentation files should be re-written using roxygen2 prior to testing. When not FALSE, tests written in roxygen2 tags will be used to update R documentation files prior to testing to use the most up-to-date example tests. May be TRUE, or a list of arguments passed to roxygen2::roxygenize. By default, only enabled when running outside of ⁠R CMD check⁠ and while taking roxygen2 as a dependency.

reporter

A testthat reporter to use. Defaults to the active reporter in the testthat environment or default reporter.

Details

  1. Each example expression is expected to run without error

  2. Any testex expectations are expected to pass

Generally, you won't need to use this function directly. Instead, it is called by a file generated by use_testex_as_testthat() which will add any testex example tests to your existing testthat testing suite.

Value

The result of testthat::source_file(), after iterating over generated test files.

Note

It is assumed that this function is used within a testthat run, when the necessary packages are already installed and loaded.

Examples



# library(pkg.example)
path <- system.file("pkg.example", package = "testex")
test_examples_as_testthat(path = path)



Test a list of files

Description

Test a list of files

Usage

test_files(files, context, ...)

Arguments

files

A collection of file paths to test

context

An optional context message to display in testthat reporters

...

Additional arguments passed to testhat::source_file

Value

The result of testthat::source_file(), after iterating over generated test files.


A syntactic helper for writing quick and easy example tests

Description

A wrapper around stopifnot that allows you to use . to refer to .Last.value and preserve the last non-test output from an example.

Usage

testex(
  ...,
  srcref = NULL,
  example_srcref = NULL,
  value = get_example_value(),
  envir = parent.frame(),
  style = "standalone"
)

Arguments

...

Expressions to evaluated. . will be replaced with the expression passed to val, and may be used as a shorthand for the last example result.

srcref

An option srcref_key string used to indicate where the relevant test code originated from.

example_srcref

An option srcref_key string used to indicate where the relevant example code originated from.

value

A value to test against. By default, this will use the example's .Last.value.

envir

An environment in which tests should be evaluated. By default the parent environment where tests are evaluated.

style

A syntactic style used by the test. Defaults to "standalone", which expects TRUE and uses a .-notation. Accepts one of "standalone" or "testthat". By default, styles will be implicitly converted to accommodate known testing frameworks, though this can be disabled by passing the style "AsIs" with I().

Value

Invisibly returns the .Last.value as it existed prior to evaluating the test.

Documenting with testex

testex is a simple wrapper around execution that propagates the .Last.value returned before running, allowing you to chain tests more easily.

Use in Rd files:

\examples{
  f <- function(a, b) a + b
  f(3, 4)
  \testonly{
    testex::testex(
      is.numeric(.),
      identical(., 7)
    )
  }
}

But Rd files are generally regarded as being a bit cumbersome to author directly. Instead, testex provide helpers that generate this style of documentation, which use this function internally.

Use with roxygen2

Within a roxygen2 ⁠@examples⁠ block you can instead use the ⁠@test⁠ tag which will generate Rd code as shown above.

#' @examples
#' f <- function(a, b) a + b
#' f(3, 4)
#' @test is.numeric(.)
#' @test identical(., 7)

Cached retrieval of testex options from package DESCRIPTION

Description

As long as the fingerprint has not changed, the package DESCRIPTION will be read only once to parse and retrieve configuration options. If the DESCRIPTION file is modified or if run from a separate process, the configured settings will be refreshed based on the most recent version of the file.

Usage

memoise_testex_desc(path, fingerprint, ...)

testex_options(path = package_desc(), ...)

Arguments

path

A path in which to search for a package DESCRIPTION

fingerprint

An object used to indicate when the cached values have been invalidated

Value

The test options environment, invisibly.

The test options environment as a list

Functions


Rd Example Parsing Helpers

Description

Rd Example Parsing Helpers

Usage

rd_extract_examples(rd)

rd_code_as_string(rd)

split_testonly_as_expr(rd)

split_testonly(rd)

Arguments

rd

An Rd object

Value

The examples section of an Rd object

A formatted Rd example

An interlaced list of expressions, either representing example code or tests. The names of the list are either ⁠\testonly⁠ or RDCODE depending on the originating source of the expression.

A list of Rd tag contents

Functions


testex roxygen2 tags

Description

testex provides two new roxygen2 tags, ⁠@test⁠ and ⁠@testthat⁠.

tags

testex tags are all sub-tags meant to be used within an ⁠@examples⁠ block. They should be considered as tags within the ⁠@examples⁠ block and used to construct blocks of testing code within example code.

⁠@test⁠:

In-line expectations to test the output of the previous command within an example. If . is used within the test expression, it will be used to refer to the output of the previous example command. Otherwise, the result of the expression is expected to be identical to the previous output.

#' @examples
#' 1 + 2
#' @test 3
#' @test . == 3
#'
#' @examples
#' 3 + 4
#' @test identical(., 7)
⁠@testthat⁠:

Similar to ⁠@test⁠, ⁠@testthat⁠ can be used to make in-line assertions using testthat expectations. testthat expectations follow a convention where the first argument is an object to compare against an expected value or characteristic. Since the value will always be the result of the previous example, this part of the code is implicitly constructed for you.

If you want to use the example result elsewhere in your expectation, you can refer to it with a .. When used in this way, testex will not do any further implicit modification of your expectation.

#' @examples
#' 1 + 2
#' @testthat expect_equal(3)
#' @testthat expect_gt(0)
#'
#' @examples
#' 3 + 4
#' @testthat expect_equal(., 7)

Support for testthat Expectations

Description

testthat support is managed through a "style" provided to testex. When using the testthat style (automatically when using the ⁠@testthat⁠ tag), expectations are processed such that they always refer to the previous example. Special care is taken to manage propagation of this value through your test code, regardless of how testthat is executed.

Examples


# example code
1 + 2

# within `testex` block, test code refers to previous result with `.`
testex(style = "testthat", srcref = "abc.R:1:3", { 
  test_that("addition holds up", {
    expect_equal(., 3)
  })
})


Add testex tags and configure package to fully use testex features

Description

Add testex tags and configure package to fully use testex features

Usage

use_testex(path = getwd(), check = TRUE, quiet = FALSE)

Arguments

path

A package source code working directory

check

A logical value indicating whether tests should be executing during ⁠R CMD check⁠.

quiet

Whether output should be suppressed

Value

The result of write.dcf() upon modifying the package DESCRIPTION file.

Note

The testex roxygen2 tags behave similarly to roxygen2 ⁠@examples⁠ tags, with the minor addition of some wrapping code to manage the tests. This means that they will be integrated into your ⁠@examples⁠ and can be intermixed between ⁠@examples⁠ tags


Run examples as testthat expectations

Description

Run examples as testthat expectations

Usage

use_testex_as_testthat(path = getwd(), context = "testex", quiet = FALSE)

Arguments

path

A package source code working directory

context

A testthat test context to use as the basis for a new test filename.

quiet

Whether to emit output messages.

Value

The result of writeLines() after writing a new testthat file.


Checks for use of roxygen2

Description

Checks for use of roxygen2

Usage

uses_roxygen2(path)

Arguments

path

A file path to a package source code directory

Value

A logical value indicating whether a package takes roxygen2 as a dependency.


vapply shorthand alternatives

Description

Simple wrappers around vapply for common data types

Usage

vlapply(..., FUN.VALUE = logical(1L))

vcapply(..., FUN.VALUE = character(1L))

vnapply(..., FUN.VALUE = numeric(1L))

Arguments

...

Arguments passed to vapply

FUN.VALUE

A preset signature for the flavor of vapply. This is exposed for transparency, but modifying it would break the implicit contract in the function name about the return type.

Value

The result of vapply


Temporarily attach a namespace

Description

This function is primarily for managing attaching of namespaces needed for testing internally. It is exported only because it is needed in code generated within Rd files, but is otherwise unlikely to be needed.

Usage

with_attached(ns, expr)

Arguments

ns

A namespace or namespace name to attach

expr

An expression to evaluate while the namespace is attached

Value

The result of evaluating expr


Raise testthat Expectations With A Known Source Reference

Description

Retroactively assigns a source file and location to a expectation. This allows testthat to report an origin for any code that raised an example test failure from the source roxygen2 code, even though the test code is reconstructed from package documentation files.

Usage

with_srcref(src, expr, envir = parent.frame())

Arguments

src

A srcref_key which is parsed to produce an artificial srcref for the expectation signaled messages.

expr

An expression to be evaluated. If an expectation condition is raised during its evaluation, its srcref is converted to src.

envir

An environment in which to evaluate expr.

Value

The result of evaluating expr, or an expectation with appended srcref information if an expectation is raised.


Wraps an example expression in a testthat expectation to not error

Description

Wraps an example expression in a testthat expectation to not error

Usage

wrap_expect_no_error(expr, value)

Arguments

expr

An expression to wrap in a expect_no_error() expectation. Uses testthats version if recent enough version is available, or provides a fallback otherwise.

value

A symbol to use to store the result of expr

Value

A testthat::test_that() call