| Title: | Test Coverage for Packages |
|---|---|
| Description: | Track and report code coverage for your package and (optionally) upload the results to a coverage service like 'Codecov' <https://about.codecov.io> or 'Coveralls' <https://coveralls.io>. Code coverage is a measure of the amount of code being exercised by a set of tests. It is an indirect measure of test quality and completeness. This package is compatible with any testing methodology or framework and tracks coverage of both R code and compiled C, C++, Fortran and Rust code. |
| Authors: | Jim Hester [aut, cre], Willem Ligtenberg [ctb], Kirill Müller [ctb], Henrik Bengtsson [ctb], Steve Peak [ctb], Kirill Sevastyanenko [ctb], Jon Clayden [ctb], Robert Flight [ctb], Eric Brown [ctb], Brodie Gaslam [ctb], Will Beasley [ctb], Robert Krzyzanowski [ctb], Markus Wamser [ctb], Karl Forner [ctb], Gergely Daróczi [ctb], Jouni Helske [ctb], Kun Ren [ctb], Jeroen Ooms [ctb], Ken Williams [ctb], Chris Campbell [ctb], David Hugh-Jones [ctb], Qin Wang [ctb], Doug Kelkhoff [ctb], Ivan Sagalaev [ctb, cph] (highlight.js library) |
| Maintainer: | Jim Hester <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 3.6.5.9002 |
| Built: | 2026-11-11 22:11:22 UTC |
| Source: | https://github.com/r-lib/covr |
covr tracks and reports code coverage for your package and (optionally) upload the results to a coverage service like 'Codecov' https://about.codecov.io or 'Coveralls' https://coveralls.io. Code coverage is a measure of the amount of code being exercised by a set of tests. It is an indirect measure of test quality and completeness. This package is compatible with any testing methodology or framework and tracks coverage of both R code and compiled C, C++, Fortran and Rust code.
A coverage report can be used to inspect coverage for each line in your
package. Using report() requires the additional dependency htmltools.
# If run with no arguments `report()` implicitly calls `package_coverage()` report()
covr uses the following options() to configure behaviour:
covr.branch_coverage: If TRUE (default FALSE), also record
which branches of each if and switch() in R code were taken. This
is experimental. It must be set before the code is instrumented, e.g.
before calling package_coverage(). For more details, see
?covr.branch_coverage.
covr.covrignore: A filename to use as an ignore file,
listing glob-style wildcarded paths of files to ignore for coverage
calculations. Defaults to the value of environment variable
COVR_COVRIGNORE, or ".covrignore" if the neither the option nor the
environment variable are set.
covr.exclude_end: Used along with covr.exclude_start, an optional
regular expression which ends a line-exclusion region. For more
details, see ?exclusions.
covr.exclude_pattern: An optional line-exclusion pattern. Lines
which match the pattern will be excluded from coverage. For more details,
see ?exclusions.
covr.exclude_start: Used along with covr.exclude_end, an optional
regular expression which starts a line-exclusion region. For more
details, see ?exclusions.
covr.filter_non_package: If TRUE (the default behavior), coverage
of files outside the target package are filtered from coverage output.
covr.failure_lines: The number of trailing lines of test output
shown in the message and print() method of a covr_error when tests,
vignettes, examples or code fail (default 50). The full output is
always available in the condition's $output field.
covr.annotate_max_lines: The maximum number of source lines shown
by annotate_coverage() (default 200), the default of its max_lines
argument. Inf shows everything.
covr.print_max_ranges: The maximum number of missed ranges, and of
missed branches, listed per file or function by print(x, missing = TRUE) (default 10). More are summarised as ... (<k> more). Inf
lists them all.
covr.fix_parallel_mcexit: Whether to include code run in processes
forked by parallel's mclapply(), mcparallel() and related
functions, by patching parallel:::mcexit. By default
package_coverage() does this if the package uses parallel, and
the functions that measure coverage in the current session, such as
file_coverage(), always do. The environment variable
COVR_FIX_PARALLEL_MCEXIT takes precedence over this option. Not
supported on Windows.
covr.flags:
covr.gcov: Path to the gcov executable. Defaults to the gcov found
on your PATH, or NA if there is none. If the appropriate gcov version
is not on your path you can use this option to set the appropriate
location. If set to "" it will turn off coverage of compiled code. If
NA, covr throws an error if any compiled coverage files (.gcno)
exist.
covr.gcov_additional_paths:
covr.gcov_args:
covr.gcov_partial: How to count compiled code lines that gcov
reports as only partially executed (e.g. if (x) f(); when f() never
runs). "covered" (the default) counts them as covered, "uncovered"
counts them as not covered, for requiring every part of a line to run.
This works with both gcc's gcov and llvm-cov (the gcov on macOS).
llvm-cov can't tell apart code that only runs when a C++ exception is
thrown, so lines that create objects with destructors may also count as
partially executed.
covr.llvm_cov: Path to the llvm-cov used to read the coverage
of Rust code. Defaults to the one of the Rust toolchain, installed with
rustup component add llvm-tools, or for Rust installed without rustup
(e.g. with apt-get install cargo rustc), to the one on the PATH with
the same LLVM version as rustc, e.g. llvm-cov-18. If set to "" the
Rust code isn't instrumented, which turns off its coverage.
covr.llvm_profdata: Path to the llvm-profdata used to merge the
coverage counts of Rust code. Found like covr.llvm_cov.
covr.report_colors: A named character vector with the colors of
covered and missed lines in report() and file_report(), and of the
coverage percentages printed in the console, e.g.
c(covered = "#0072B2", missed = "#D55E00"). colorblind_palette has
colors that are easier to tell apart than the default green and red
with color vision deficiency. Either element can be left out to keep
its default.
An optional partial element sets the color of partial coverage (at
least 75% but less than 90%), which otherwise follows covered, or is
yellow in the console if covered isn't set either, and of lines with
a branch that wasn't taken in reports with branch coverage, which are
otherwise yellow. Transparency is ignored. In terminals with only 8
colors, the console uses the closest of those colors.
covr.rstudio_source_markers: A logical value. If TRUE (the
default behavior), source markers are displayed within the RStudio IDE
when using zero_coverage.
covr.record_tests: If TRUE (default NULL), record a listing of
top level test expressions and associate tests with covr traces
evaluated during the test's execution. For more details, see
?covr.record_tests.
covr.full_traversal: If TRUE (default FALSE), also trace
functions nested inside lists and environments, such as
fns <- list(a = function(x) x) or functions defined inside local().
This walks every object in the package, so it is off by default. For
package_coverage() it can also be enabled for a package by adding
Config/covr/full_traversal: true to its DESCRIPTION; the option takes
precedence when set.
covr.install_instrument: If FALSE (default TRUE, or the value
of the environment variable COVR_INSTALL_INSTRUMENT),
package_coverage() installs a package as usual and instruments it each
time a process loads it, rather than once after installing it.
covr.showCfunctions:
Maintainer: Jim Hester [email protected]
Authors:
Jim Hester [email protected]
Other contributors:
Willem Ligtenberg [contributor]
Kirill Müller [contributor]
Henrik Bengtsson [contributor]
Steve Peak [contributor]
Kirill Sevastyanenko [contributor]
Jon Clayden [contributor]
Robert Flight [contributor]
Eric Brown [contributor]
Brodie Gaslam [contributor]
Will Beasley [contributor]
Robert Krzyzanowski [contributor]
Markus Wamser [contributor]
Karl Forner [contributor]
Gergely Daróczi [contributor]
Jouni Helske [contributor]
Kun Ren [contributor]
Jeroen Ooms [contributor]
Ken Williams [contributor]
Chris Campbell [contributor]
David Hugh-Jones [contributor]
Qin Wang [contributor]
Doug Kelkhoff [contributor]
Ivan Sagalaev (highlight.js library) [contributor, copyright holder]
Useful links:
Report bugs at https://github.com/r-lib/covr/issues
Produces plain text showing source lines together with their coverage
counts, in a form that is easy to read in a terminal or to paste into a
prompt. By default the missed lines are shown with context surrounding
lines.
annotate_coverage( x, lines = NULL, context = 2, files = NULL, max_lines = getOption("covr.annotate_max_lines", 200) )annotate_coverage( x, lines = NULL, context = 2, files = NULL, max_lines = getOption("covr.annotate_max_lines", 200) )
x |
A coverage object, e.g. from |
lines |
A data frame with |
context |
Number of lines to show before and after each line in
|
files |
Optional character vector of file names (as they appear in the
coverage, e.g. |
max_lines |
The maximum number of source lines to show, 200 by default
(option |
Each shown line has the line number, a count column and the source. Missed
lines are marked !!0, covered code lines show their hit count, and lines
without coverage data (blank lines, comments, ...) have an empty count.
Non-adjacent chunks are separated by ....
If branch coverage was measured (see covr.branch_coverage), lines with a
branch that was not taken are also shown by default, followed by
<- not taken: and the labels of their missed branches. Only if and
switch() calls that were evaluated count; the others are covered by the
missed lines. Such a line keeps
its count if it was run.
A character vector of class covr_annotation, with one element per
output line. If there is nothing to show it is "No missed lines.".
source <- " f <- function(x) { if (x) { 'yes' } else { 'no' } } " cov <- code_coverage(source, "f(TRUE)") annotate_coverage(cov) annotate_coverage(cov, context = Inf)source <- " f <- function(x) { if (x) { 'yes' } else { 'no' } } " cov <- code_coverage(source, "f(TRUE)") annotate_coverage(cov) annotate_coverage(cov, context = Inf)
Convert a counters object to a coverage object
as_coverage(counters, ...)as_coverage(counters, ...)
counters |
An environment of covr trace results to convert to a coverage object. |
... |
Additional attributes to include with the coverage object. |
For tests produced with options(covr.record_tests), prune any unused
records in the $tests$tally matrices of each trace and get rid of the
wrapping $tests environment (reassigning with value of $tests$tally)
as_coverage_with_tests(counters)as_coverage_with_tests(counters)
counters |
An environment of covr trace results to convert to a coverage object. |
Convert a coverage object to a data frame
## S3 method for class 'coverage' as.data.frame(x, row.names = NULL, optional = FALSE, sort = TRUE, ...)## S3 method for class 'coverage' as.data.frame(x, row.names = NULL, optional = FALSE, sort = TRUE, ...)
x |
a coverage object. |
row.names, optional
|
ignored, for compatibility with
|
sort |
if |
... |
ignored. |
A data frame with one row for each traced expression of R code and each line of compiled code, and the columns:
filename: the source file, relative to the root attribute of x
(the package directory, for package_coverage()) if there is one.
functions: the name of the function containing the expression, or
NA for compiled code.
first_line, last_line: the lines the expression starts and ends on.
first_byte, last_byte: the bytes of those lines the expression
starts and ends at.
first_column, last_column: the columns the expression starts and
ends at. These differ from the bytes when the line contains multibyte
characters.
first_parsed, last_parsed: the lines as seen by the parser. These
differ from first_line and last_line when the source uses #line
directives.
value: the number of times the expression was executed.
Use tally_coverage() for one row for each line instead.
cov <- code_coverage("add <- function(x, y) { x + y }", "add(1, 2)") as.data.frame(cov)cov <- code_coverage("add <- function(x, y) { x + y }", "add(1, 2)") as.data.frame(cov)
Deprecated. Use cobertura(), which this function calls, instead.
azure( ..., coverage = package_coverage(..., quiet = quiet), filename = "coverage.xml", quiet = TRUE )azure( ..., coverage = package_coverage(..., quiet = quiet), filename = "coverage.xml", quiet = TRUE )
... |
arguments passed to |
coverage |
an existing coverage object to submit, if |
filename |
the name of the Cobertura XML file |
quiet |
if |
Write an SVG badge showing the percent coverage, in the style of the badges made by Codecov and shields.io, so a README can show the coverage of a package without uploading it to a coverage service. The badge is made locally and doesn't need a network connection.
badge(cov, filename = "coverage.svg", label = "coverage")badge(cov, filename = "coverage.svg", label = "coverage")
cov |
the coverage object returned from |
filename |
the name of the SVG file |
label |
the text on the left side of the badge |
The coverage is that of percent_coverage(), rounded down to a whole
percent so that only fully covered code shows 100%. The badge is green for
coverage of 90% or more, yellow for 75% or more and red below that, the
same thresholds used by report(). Coverage without any lines of code,
such as that of a package with no R code, shows as unknown on a gray
badge.
The badge is a plain file, so on a continuous integration service it needs
to be stored somewhere that the README can link to, e.g. committed to a
separate branch of the repository.
system.file("workflows", "test-coverage-badge.yaml", package = "covr") is
a GitHub Actions workflow that does this.
The lines of the SVG file, invisibly.
cov <- code_coverage( "f <- function(x) {\n y <- x + 1\n if (y > 0) {\n 'yes'\n } else {\n 'no'\n }\n}", "f(1)" ) tmp <- tempfile(fileext = ".svg") badge(cov, tmp) cat(readLines(tmp), sep = "\n")cov <- code_coverage( "f <- function(x) {\n y <- x + 1\n if (y > 0) {\n 'yes'\n } else {\n 'no'\n }\n}", "f(1)" ) tmp <- tempfile(fileext = ".svg") badge(cov, tmp) cat(readLines(tmp), sep = "\n")
Combine the coverage of several runs, e.g. of tests in different
directories, into a single coverage object. The counts of traces present in
more than one object are added, and traces only in some of the objects are
appended. Tests recorded with options(covr.record_tests = TRUE) are
combined as well.
## S3 method for class 'coverage' c(...)## S3 method for class 'coverage' c(...)
... |
Coverage objects, e.g. from |
The result keeps the attributes of the first object, such as the package
and root used for relative paths, except that the lines excluded from line
coverage are the union of the excluded lines of all objects. As with
package_coverage(type = c("tests", "examples")), the result only has a
type if all objects have the same type.
As with any S3 method for c(), this method is only used when the first
argument is a coverage object.
A coverage object.
source_file <- tempfile(fileext = ".R") writeLines( c( "add <- function(x, y) {", " x + y", "}", "subtract <- function(x, y) {", " x - y", "}" ), source_file ) add_test <- tempfile(fileext = ".R") writeLines("add(1, 2)", add_test) subtract_test <- tempfile(fileext = ".R") writeLines("subtract(2, 1)", subtract_test) add_cov <- file_coverage(source_file, add_test) subtract_cov <- file_coverage(source_file, subtract_test) percent_coverage(add_cov) percent_coverage(c(add_cov, subtract_cov))source_file <- tempfile(fileext = ".R") writeLines( c( "add <- function(x, y) {", " x + y", "}", "subtract <- function(x, y) {", " x - y", "}" ), source_file ) add_test <- tempfile(fileext = ".R") writeLines("add(1, 2)", add_test) subtract_test <- tempfile(fileext = ".R") writeLines("subtract(2, 1)", subtract_test) add_cov <- file_coverage(source_file, add_test) subtract_cov <- file_coverage(source_file, subtract_test) percent_coverage(add_cov) percent_coverage(c(add_cov, subtract_cov))
Create a
cobertura-compliant XML report following this DTD.
Because there are two DTDs called coverage-04.dtd and some tools do not seem to
adhere to either of them, the parser you're using may balk at the file. Please see
this github discussion for
context. Where covr doesn't provide a coverage metric (complexity), a zero
is reported.
cobertura(cov, filename = "cobertura.xml")cobertura(cov, filename = "cobertura.xml")
cov |
the coverage object returned from |
filename |
the name of the Cobertura XML file |
Branch rates are only reported if branches were measured with
options(covr.branch_coverage = TRUE) (see covr.branch_coverage),
otherwise they are zero. Each line where an if or switch() starts then
has branch="true" and a condition-coverage such as "50% (1/2)", the
percentage and number of the branches of the if and switch() calls on
the line that were taken. The branch rate of code without branches is one.
Note: This functionality requires the xml2 package be installed.
This function is useful for testing, and is a thin wrapper around
file_coverage() because parseData is not populated properly
unless the functions are defined in a file.
code_coverage( source_code, test_code, line_exclusions = NULL, function_exclusions = NULL, ... )code_coverage( source_code, test_code, line_exclusions = NULL, function_exclusions = NULL, ... )
source_code |
A character vector of source code |
test_code |
A character vector of test code |
line_exclusions |
a named list of files with the lines to exclude from each file. |
function_exclusions |
a vector of regular expressions matching function
names to exclude. Example |
... |
Additional arguments passed to |
A coverage object.
Code run in processes forked by parallel::mclapply(),
parallel::mcparallel() and related functions (not available on Windows)
is included in the coverage. While the code runs, parallel:::mcfork and
parallel:::mcexit are patched so that the forked processes save their
counts when they exit. To turn this off, set the environment variable
COVR_FIX_PARALLEL_MCEXIT or the option covr.fix_parallel_mcexit to
FALSE.
source <- "add <- function(x, y) { x + y }" test <- "add(1, 2) == 3" code_coverage(source, test)source <- "add <- function(x, y) { x + y }" test <- "add(1, 2) == 3" code_coverage(source, test)
Uploads coverage to Codecov with the current upload protocol, the one used
by the Codecov CLI and the
codecov/codecov-action GitHub Action. This takes two requests:
codecov( ..., coverage = NULL, base_url = "https://ingest.codecov.io", token = NULL, commit = NULL, branch = NULL, pr = NULL, flags = NULL, quiet = TRUE )codecov( ..., coverage = NULL, base_url = "https://ingest.codecov.io", token = NULL, commit = NULL, branch = NULL, pr = NULL, flags = NULL, quiet = TRUE )
... |
arguments passed to |
coverage |
an existing coverage object to submit, if |
base_url |
the url coverage is uploaded to. The default is Codecov's
upload API; change it for Codecov Enterprise. The former default
|
token |
a codecov upload token, if
If there is no token, the upload is sent without one, see the section “Tokens”. |
commit |
explicitly set the commit this coverage result object
corresponds to. Is looked up from the service or locally if it is
|
branch |
explicitly set the branch this coverage result object
corresponds to, this is looked up from the service or locally if it is
|
pr |
explicitly set the pr this coverage result object corresponds to,
this is looked up from the service if it is |
flags |
A flag to use for this coverage upload see https://docs.codecov.com/docs/flags for details. |
quiet |
if |
POST <base_url>/upload/<git service>/<owner>::::<repo>/upload-coverage
(for a repository in nested groups, owner/group/repo is
owner:::group::::repo) tells Codecov about the commit, branch, pull request and CI job. The
response contains a url for the upload on Codecov and a pre-signed
location to send the coverage report to.
PUT sends the report, covr's JSON coverage format (processed by
Codecov as an R report), to that location. With branch coverage (see
covr.branch_coverage), the report uses Codecov's generic JSON format
instead, as the R format only takes line hits. A line with branches
then has the number of its branches taken and the number of branches,
e.g. "1/2", so Codecov shows it as partly covered unless all were
taken.
If quiet is FALSE, the url of the upload on Codecov is printed after a
successful upload.
The commit, branch, pull request, job, build URL and repository are looked up
from the environment variables of the CI service, which is one of Jenkins,
Travis, CircleCI, Drone, AppVeyor, GitLab CI,
GitHub Actions or Google Cloud Build. Anywhere else, or for values the
service does not provide, they are looked up from the local git repository
in the working directory. The repository (owner/repo) and the git service
(github, gitlab, bitbucket, ...) come from the CI service when it
provides them, otherwise from the origin remote of the local git
repository, otherwise the git service is assumed to be github.
The response of Codecov to the request for an upload, invisibly: a
list that includes url, the url of the upload on Codecov. If Codecov
rejects either request, codecov() throws an error of class
covr_codecov_error with the response of Codecov in its message, except
when Codecov is rate limiting requests (HTTP 429) or is unavailable (HTTP
5xx), when it gives a warning of class covr_codecov_warning and returns
NULL instead, so CI does not fail because of a transient Codecov problem.
Codecov authenticates uploads with an upload token, passed in the
Authorization header. Both the token of a repository and an organization
(global) upload token, which is shared by all of an organization's
repositories, can be used. The token is looked up as described for token.
See https://docs.codecov.com/docs/codecov-tokens.
Without a token, Codecov only accepts uploads for public repositories, and
only for an unprotected branch, which is a branch name with an owner prefix
such as owner:branch. Pull requests from forks do not have access to the
token as a CI secret, so on GitHub Actions, when there is no token, branch
is NULL and the head repository of the pull request (the
pull_request.head.repo.full_name of the event in GITHUB_EVENT_PATH)
differs from GITHUB_REPOSITORY, codecov() uses the label of the head of
the pull request, owner:branch, as the branch name, as
codecov/codecov-action does. Pull requests from the same repository, other
CI services and uploads from other branches use the plain branch name, so
without a token Codecov rejects them unless the branch is already
unprotected.
Instead of uploading from R, write the coverage to a file and upload it with
the codecov/codecov-action GitHub Action, or any other Codecov uploader.
lcov() has no dependencies and writes lcov.info, which the action
uploads without further configuration:
- name: Test coverage
run: covr::lcov(covr::package_coverage())
shell: Rscript {0}
- uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
cobertura() writes the Cobertura XML format, for which the xml2 package
is needed.
## Not run: codecov(path = "test") ## End(Not run)## Not run: codecov(path = "test") ## End(Not run)
colorblind_palette has colors for the covr.report_colors option that
are easier to tell apart with color vision deficiency than the default
green and red: blue for covered code, yellow for partial coverage and
vermillion for missed code, from the Okabe-Ito palette. They are used in
report(), file_report() and the coverage percentages printed in the
console.
colorblind_palettecolorblind_palette
A named character vector with elements covered, partial and
missed.
To set the colors in your .Rprofile without loading covr, copy the
values.
colorblind_palette ## Not run: options(covr.report_colors = covr::colorblind_palette) report() ## End(Not run)colorblind_palette ## Not run: options(covr.report_colors = covr::colorblind_palette) report() ## End(Not run)
package_coverage(), file_coverage(), code_coverage(),
function_coverage() and environment_coverage() return a coverage
object, which records how many times each part of the code was executed.
Rather than using the list directly, you will usually want to:
Get a data frame with one row for each expression with as.data.frame()
(as.data.frame.coverage()), or one row for each line or function with
tally_coverage().
Get the overall percentage with percent_coverage(), or the percentage
for each file with coverage_to_list().
Find the lines or functions which were never run with zero_coverage(),
list them as ranges with print(x, missing = TRUE), or show their source
with annotate_coverage().
Get the execution counts as a named numeric vector with value().
Browse the coverage with report() or file_report().
A coverage object is a named list with one element for each traced
expression of R code and each line of compiled code. Each element is a list
with:
value: the number of times the expression or line was executed.
srcref: a srcref with its location. Its srcfile
attribute holds the name and lines of the source file.
functions: the name of the function containing it, or NA for compiled
code.
tests: only present with options(covr.record_tests = TRUE), see
covr.record_tests.
The names of the elements are the base name of the file followed by the
eight numbers of the srcref, separated by :. For example
foo.R:10:3:12:4:3:4:10:12 is an expression from line 10 column 3 to line
12 column 4 of foo.R (see as.data.frame.coverage() for the meaning of
each number). The srcfile of the srcref, or the filename column of
as.data.frame(), gives the path of the file.
Coverage objects from package_coverage() also have the attributes
package (information about the package, such as its package name and
path), root (the directory file names are relative to, or NULL) and,
if clean = FALSE, library (the install_path).
With options(covr.record_tests = TRUE), coverage objects also have a
tests attribute with the test calls, see covr.record_tests.
With options(covr.branch_coverage = TRUE), coverage objects also have a
branches attribute with the branches of each if and switch(), see
covr.branch_coverage.
cov <- code_coverage("add <- function(x, y) { x + y }", "add(1, 2)") cov as.data.frame(cov) names(cov) cov[[1]]$valuecov <- code_coverage("add <- function(x, y) { x + y }", "add(1, 2)") cov as.data.frame(cov) names(cov) cov[[1]]$value
Convert a coverage dataset to a list
coverage_to_list(x = package_coverage())coverage_to_list(x = package_coverage())
x |
a coverage dataset, defaults to running |
A list containing coverage result for each individual file and the whole package
Creates a markdown table with the total coverage and the coverage of each
file, suitable for a GitHub Actions job summary or a pull request comment.
If base is given, the table also includes the coverage of base and the
change from it.
coverage_to_markdown(x = package_coverage(), base = NULL)coverage_to_markdown(x = package_coverage(), base = NULL)
x |
a coverage dataset, defaults to running |
base |
an optional coverage dataset to compare against, e.g. the coverage of the main branch. |
A character vector of markdown lines, with class "covr_markdown".
## Not run: # In a GitHub Actions step md <- coverage_to_markdown(package_coverage()) cat(md, file = Sys.getenv("GITHUB_STEP_SUMMARY"), sep = "\n", append = TRUE) ## End(Not run)## Not run: # In a GitHub Actions step md <- coverage_to_markdown(package_coverage()) cat(md, file = Sys.getenv("GITHUB_STEP_SUMMARY"), sep = "\n", append = TRUE) ## End(Not run)
On GitHub Actions, use the official
Coveralls action instead of
this function: write the coverage with cobertura() and upload
cobertura.xml with coverallsapp/github-action@v2. See the README for an
example workflow. With branch coverage, upload the LCOV file written by
lcov() instead, as the action reads which branches were taken from it.
coveralls( ..., coverage = NULL, repo_token = Sys.getenv("COVERALLS_TOKEN"), service_name = NULL, quiet = TRUE )coveralls( ..., coverage = NULL, repo_token = Sys.getenv("COVERALLS_TOKEN"), service_name = NULL, quiet = TRUE )
... |
arguments passed to |
coverage |
an existing coverage object to submit, if |
repo_token |
The secret repo token for your repository, found on your
repository's page on Coveralls. Without a token ( |
service_name |
The name of the CI service. If |
quiet |
if |
With branch coverage (see covr.branch_coverage), each source file sent to
Coveralls also has its branches: the line, block, branch and count of
each branch, as in lcov(). A branch that was never taken has a count of
zero.
The CI service, job, pull request, branch and commit are detected from the
environment variables of the services codecov() supports. Anywhere else,
the branch, commit and remote are looked up from the local git repository.
The environment variables CI_BRANCH and CI_REMOTE override the detected
branch and remote.
By setting options(covr.branch_coverage = TRUE) before measuring
coverage, covr also records which branches of each if and switch() in R
code were taken. Line and expression coverage are the same as without it.
Branch coverage is experimental: what counts as a branch, and how branches
are stored and reported, may change.
The option is read when the code is instrumented, so it has no cost when it
is not set. When it is set, every if and switch() that runs calls a
function of covr, which makes the code slower.
An if has two branches, "TRUE" and "FALSE". The "FALSE" branch
of an if without an else is taken when the condition is FALSE.
Nothing is recorded if the condition is NA or otherwise signals an
error.
A switch() has a branch for each of its arms, labelled with the name of
the arm. Arms that fall through to the next arm (switch(x, a = , b = 1))
share its branch, labelled "a, b". An unnamed arm is labelled
"<default>", or with its position if all arms are unnamed. A switch()
without a default arm has another branch, "<no match>", taken when no
arm matches, including a number that selects no arm. A switch() with
arms in ... has no branches.
&&, || and loops have no branches, nor does compiled code.
If the line where an if or switch() starts is excluded, e.g. with
# nocov (see exclusions), all its branches are excluded too.
The branches are in the branches attribute of the coverage object, a list
with an element for each if and switch(), named like the elements of
the coverage object (see coverage). Each is a list with the srcref of
the if or switch(), its functions, the labels of its branches, and
value, the number of times each branch was taken. Use
tally_coverage(by = "branch") for a data frame with one row for each
branch, and percent_coverage(by = "branch") for the percentage of
branches taken.
report(), file_report(), lcov(), cobertura(), codecov() and
coveralls() include the branches. The reports show the number of branches
taken on each line and the percentage of branches taken in each file, and
show a line with branches that weren't all taken as partly covered. Codecov
shows such lines as partly covered too, and counts them as not covered in
the coverage percentage, so the percentage on Codecov is lower with branch
coverage. sonarqube() doesn't include the branches yet.
withr::with_options(list(covr.branch_coverage = TRUE), { cov <- code_coverage( "f <- function(x) {\n if (x > 0) 'positive'\n 'done'\n}", "f(1)" ) }) tally_coverage(cov, by = "branch") percent_coverage(cov, by = "branch")withr::with_options(list(covr.branch_coverage = TRUE), { cov <- code_coverage( "f <- function(x) {\n if (x > 0) 'positive'\n 'done'\n}", "f(1)" ) }) tally_coverage(cov, by = "branch") percent_coverage(cov, by = "branch")
By setting options(covr.record_tests = TRUE), the result of covr coverage
collection functions will include additional data pertaining to the tests
which are executed and an index of which tests, at what stack depth, trigger
the execution of each trace.
This functionality requires that the package code and tests are installed and
sourced with the source. For more details, refer to R options, keep.source,
keep.source.pkgs and keep.parse.data.pkgs.
Within the covr result, you can explore this information in two places:
attr(,"tests"): A list of call stacks, which results in target code
execution.
$<srcref>$tests: For each srcref count in the coverage object, a
$tests field is now included which contains a matrix with three columns,
"test", "call", "depth" and "i" which specify the test number
(corresponding to the index of the test in attr(,"tests"), the number
of times the test expression was evaluated to produce the trace hit, the
stack depth into the target code where the trace was executed, and the
order of execution for each test.
The content of test traces are dependent on the unit testing framework that is used by the target package. The behavior is contingent on the available information in the sources kept for the testing files.
Test traces are extracted by the following criteria:
If any srcref files are are provided by a file within covr's temporary
library, all calls from those files are kept as a test trace. This will
collect traces from tests run with common testing frameworks such as
testthat and RUnit.
Otherwise, as a conservative fallback in situations where no source references are found, or when none are from within the temporary directory, the entire call stack is collected.
These calls are subsequently subset for only those up until the call to
covr's internal count function, and will always include the last call in
the call stack prior to a call to count.
Functions in the recorded calls, such as the function of an R6 active
binding or a function passed to do.call(), have their environment replaced
with emptyenv(), so that the objects they enclose aren't saved with the
coverage.
fcode <- ' f <- function(x) { if (x) f(!x) else FALSE }' options(covr.record_tests = TRUE) cov <- code_coverage(fcode, "f(TRUE)") # extract executed test code for the first test tail(attr(cov, "tests")[[1L]], 1L) # [[1]] # f(TRUE) # extract test itemization per trace cov[[3]][c("srcref", "tests")] # $srcref # f(!x) # # $tests # test call depth i # [1,] 1 1 2 4 # reconstruct the code path of a test by ordering test traces by [,"i"] lapply(cov, `[[`, "tests") # $`source.Ref2326138c55:4:6:4:10:6:10:4:4` # test call depth i # [1,] 1 1 1 2 # # $`source.Ref2326138c55:3:8:3:8:8:8:3:3` # test call depth i # [1,] 1 1 1 1 # [2,] 1 1 2 3 # # $`source.Ref2326138c55:6:6:6:10:6:10:6:6` # test call depth i # [1,] 1 1 2 4fcode <- ' f <- function(x) { if (x) f(!x) else FALSE }' options(covr.record_tests = TRUE) cov <- code_coverage(fcode, "f(TRUE)") # extract executed test code for the first test tail(attr(cov, "tests")[[1L]], 1L) # [[1]] # f(TRUE) # extract test itemization per trace cov[[3]][c("srcref", "tests")] # $srcref # f(!x) # # $tests # test call depth i # [1,] 1 1 2 4 # reconstruct the code path of a test by ordering test traces by [,"i"] lapply(cov, `[[`, "tests") # $`source.Ref2326138c55:4:6:4:10:6:10:4:4` # test call depth i # [1,] 1 1 1 2 # # $`source.Ref2326138c55:3:8:3:8:8:8:3:3` # test call depth i # [1,] 1 1 1 1 # [2,] 1 1 2 3 # # $`source.Ref2326138c55:6:6:6:10:6:10:6:6` # test call depth i # [1,] 1 1 2 4
A single test expression might be evaluated many times. Each time the same expression is called, the call count is incremented.
current_test_call_count()current_test_call_count()
An integer value representing the number of calls of the current call into the package from the testing suite.
The functions in env are instrumented, then the files in test_files are
run with sys.source() in a child environment of env, from the current
working directory. Nothing else is set up, so testthat tests that rely on
its test runner, e.g. for setup files, snapshots, or the test directory as
the working directory (which testthat::test_path() uses to find fixtures
while testing), should instead be run with testthat::test_file() inside
with_coverage().
environment_coverage( env = parent.frame(), test_files, line_exclusions = NULL, function_exclusions = NULL )environment_coverage( env = parent.frame(), test_files, line_exclusions = NULL, function_exclusions = NULL )
env |
The environment to be instrumented. |
test_files |
Character vector of test files with code to test the functions |
line_exclusions |
a named list of files with the lines to exclude from each file. |
function_exclusions |
a vector of regular expressions matching function
names to exclude. Example |
A coverage object.
Code run in processes forked by parallel::mclapply(),
parallel::mcparallel() and related functions (not available on Windows)
is included in the coverage. While the code runs, parallel:::mcfork and
parallel:::mcexit are patched so that the forked processes save their
counts when they exit. To turn this off, set the environment variable
COVR_FIX_PARALLEL_MCEXIT or the option covr.fix_parallel_mcexit to
FALSE.
environment_coverage() and with_coverage() include the compiled code in
the coverage of the namespace of a package loaded with pkgload::load_all()
(e.g. by devtools::load_all()), so devtools::test_coverage_active_file()
also works for files in src/. With compiled files in source_files,
file_coverage() loads their package with pkgload::load_all() and reports
the coverage of those files, without needing devtools:
file_coverage("src/foo.c", "tests/testthat/test-foo.R")
file_coverage("src/rust/src/lib.rs", "tests/testthat/test-foo.R")
The compiled code is measured in the current session, which is much faster
than package_coverage(): C, C++, Fortran and Objective-C code with gcov,
and Rust code (e.g. of packages using extendr or savvy) with LLVM's
source-based coverage, which needs the llvm tools described in
package_coverage(). An instrumented copy of the package is built in the
session's temporary directory, leaving the package's own build untouched,
and later runs only recompile the files that changed, with cargo's
incremental build in the copy's own target directory for Rust code.
While the code runs, the namespace's native routines use the instrumented
DLL instead of the package's own. Routines that were looked up some other
way, e.g. stored by .onLoad() or found by other packages with
R_GetCCallable(), keep using the package's DLL, so their code isn't
counted. Set options(covr.gcov = "") to leave out C, C++, Fortran and
Objective-C code, and options(covr.llvm_cov = "") to leave out Rust code.
covr supports a couple of different ways of excluding some or all of a file.
The line_exclusions argument to package_coverage() can be used
to exclude some or all of a file. This argument takes a list of filenames
or named ranges to exclude.
Alternatively function_exclusions can be used to exclude R functions
based on regular expression(s). For example print\\\.* can be used to
exclude all the print methods defined in a package from coverage.
In addition you can exclude lines from the coverage by putting special comments
in your source code. This can be done per line or by specifying a range.
The patterns used can be specified by setting the global options
covr.exclude_pattern, covr.exclude_start, covr.exclude_end.
## Not run: # exclude whole file of R/test.R package_coverage(line_exclusions = list("R/test.R")) # exclude lines 1 to 10 and 15 from R/test.R package_coverage(line_exclusions = list("R/test.R" = c(1:10, 15))) # exclude lines 1 to 10 from R/test.R, all of R/test2.R package_coverage(line_exclusions = list("R/test.R" = 1:10, "R/test2.R")) # exclude all print and format methods from the package. package_coverage(function_exclusions = c("print\\.", "format\\.")) # single line exclusions f1 <- function(x) { x + 1 # nocov } # ranged exclusions f2 <- function(x) { # nocov start x + 2 } # nocov end ## End(Not run)## Not run: # exclude whole file of R/test.R package_coverage(line_exclusions = list("R/test.R")) # exclude lines 1 to 10 and 15 from R/test.R package_coverage(line_exclusions = list("R/test.R" = c(1:10, 15))) # exclude lines 1 to 10 from R/test.R, all of R/test2.R package_coverage(line_exclusions = list("R/test.R" = 1:10, "R/test2.R")) # exclude all print and format methods from the package. package_coverage(function_exclusions = c("print\\.", "format\\.")) # single line exclusions f1 <- function(x) { x + 1 # nocov } # ranged exclusions f2 <- function(x) { # nocov start x + 2 } # nocov end ## End(Not run)
The files in source_files are first sourced into a new environment
to define functions to be checked. Then they are instrumented to track
coverage and the files in test_files are run with runner.
file_coverage( source_files, test_files, line_exclusions = NULL, function_exclusions = NULL, parent_env = parent.frame(), runner = source_test_file )file_coverage( source_files, test_files, line_exclusions = NULL, function_exclusions = NULL, parent_env = parent.frame(), runner = source_test_file )
source_files |
Character vector of source files with function
definitions to measure coverage. Can also include compiled files from a
package's |
test_files |
Character vector of test files with code to test the functions |
line_exclusions |
a named list of files with the lines to exclude from each file. |
function_exclusions |
a vector of regular expressions matching function
names to exclude. Example |
parent_env |
The parent environment to use when sourcing the files.
If |
runner |
A function used to run each test file, called as
|
A coverage object.
Code run in processes forked by parallel::mclapply(),
parallel::mcparallel() and related functions (not available on Windows)
is included in the coverage. While the code runs, parallel:::mcfork and
parallel:::mcexit are patched so that the forked processes save their
counts when they exit. To turn this off, set the environment variable
COVR_FIX_PARALLEL_MCEXIT or the option covr.fix_parallel_mcexit to
FALSE.
By default the test files are run with sys.source(), not with testthat's
test runner, so features that need the runner don't work. In particular,
testthat::expect_snapshot() is skipped ("Can't compare snapshot to
reference when testing interactively").
To use these features, run the test files with testthat::test_file():
file_coverage( "R/my_functions.R", "tests/test-my_functions.R", runner = testthat::test_file )
The tests then run in a child of the environment the source files were
sourced into, and snapshots are saved in a _snaps directory next to the
test file. expect_snapshot() also needs testthat's 3rd edition (e.g.
testthat::local_edition(3) in the tests), and is skipped unless the
NOT_CRAN environment variable is "true".
For more control, source the files yourself and use with_coverage().
To make the package's other functions available, load the package (e.g.
with pkgload::load_all()) and use parent_env = asNamespace("pkg").
Calls from other package files use the namespace's untraced functions, so
don't count. devtools::test_coverage_active_file() measures the coverage
of one file of a package without this limitation.
environment_coverage() and with_coverage() include the compiled code in
the coverage of the namespace of a package loaded with pkgload::load_all()
(e.g. by devtools::load_all()), so devtools::test_coverage_active_file()
also works for files in src/. With compiled files in source_files,
file_coverage() loads their package with pkgload::load_all() and reports
the coverage of those files, without needing devtools:
file_coverage("src/foo.c", "tests/testthat/test-foo.R")
file_coverage("src/rust/src/lib.rs", "tests/testthat/test-foo.R")
The compiled code is measured in the current session, which is much faster
than package_coverage(): C, C++, Fortran and Objective-C code with gcov,
and Rust code (e.g. of packages using extendr or savvy) with LLVM's
source-based coverage, which needs the llvm tools described in
package_coverage(). An instrumented copy of the package is built in the
session's temporary directory, leaving the package's own build untouched,
and later runs only recompile the files that changed, with cargo's
incremental build in the copy's own target directory for Rust code.
While the code runs, the namespace's native routines use the instrumented
DLL instead of the package's own. Routines that were looked up some other
way, e.g. stored by .onLoad() or found by other packages with
R_GetCCallable(), keep using the package's DLL, so their code isn't
counted. Set options(covr.gcov = "") to leave out C, C++, Fortran and
Objective-C code, and options(covr.llvm_cov = "") to leave out Rust code.
# For the purpose of this example, save code containing code and tests to files cat("add <- function(x, y) { x + y }", file="add.R") cat("add(1, 2) == 3", file="add_test.R") # Use file_coverage() to calculate test coverage file_coverage(source_files = "add.R", test_files = "add_test.R") # cleanup file.remove(c("add.R", "add_test.R"))# For the purpose of this example, save code containing code and tests to files cat("add <- function(x, y) { x + y }", file="add.R") cat("add(1, 2) == 3", file="add_test.R") # Use file_coverage() to calculate test coverage file_coverage(source_files = "add.R", test_files = "add_test.R") # cleanup file.remove(c("add.R", "add_test.R"))
file_report() saves the coverage of one file as a standalone HTML file and
opens it. file_report_html() creates the same report as HTML that can be
included in a larger document, like report_html().
file_report( x = package_coverage(), file = NULL, out_file = file.path(tempdir(), paste0(get_package_name(x), "-file-report.html")), browse = interactive() ) file_report_html(x = package_coverage(), file = NULL)file_report( x = package_coverage(), file = NULL, out_file = file.path(tempdir(), paste0(get_package_name(x), "-file-report.html")), browse = interactive() ) file_report_html(x = package_coverage(), file = NULL)
x |
a coverage dataset, defaults to running |
file |
The file to report on, if |
out_file |
The output file |
browse |
whether to open a browser to view the report. |
With branch coverage, the report shows the branches of the file as described
in the "Branches" section of report().
file_report_html() returns an htmltools::tagList(), with the
dependencies of the report attached.
Calculate test coverage for a specific function.
function_coverage(fun, code = NULL, env = NULL, enc = parent.frame())function_coverage(fun, code = NULL, env = NULL, enc = parent.frame())
fun |
name of the function. |
code |
expressions to run. |
env |
environment the function is defined in. |
enc |
the enclosing environment which to run the expressions. |
A coverage object.
Code run in processes forked by parallel::mclapply(),
parallel::mcparallel() and related functions (not available on Windows)
is included in the coverage. While the code runs, parallel:::mcfork and
parallel:::mcexit are patched so that the forked processes save their
counts when they exit. To turn this off, set the environment variable
COVR_FIX_PARALLEL_MCEXIT or the option covr.fix_parallel_mcexit to
FALSE.
add <- function(x, y) { x + y } function_coverage(fun = add, code = NULL) # 0% coverage function_coverage(fun = add, code = add(1, 2) == 3) # 100% coverageadd <- function(x, y) { x + y } function_coverage(fun = add, code = NULL) # 0% coverage function_coverage(fun = add, code = add(1, 2) == 3) # 100% coverage
Utilize internal GitLab static pages to publish package coverage. Creates local covr report in a package subdirectory. Uses the pages GitLab job to publish the report.
gitlab(..., coverage = NULL, file = "public/coverage.html", quiet = TRUE)gitlab(..., coverage = NULL, file = "public/coverage.html", quiet = TRUE)
... |
arguments passed to |
coverage |
an existing coverage object to submit, if |
file |
The report filename. |
quiet |
if |
Is the source bound to the expression
has_srcref(expr)has_srcref(expr)
expr |
A language object which may have a |
A logical value indicating whether the language object has source
covr functions set the environment variable R_COVR when they are running.
in_covr() returns TRUE if this environment variable is set and FALSE
otherwise.
in_covr()in_covr()
if (require(testthat)) { testthat::skip_if(in_covr()) }if (require(testthat)) { testthat::skip_if(in_covr()) }
Is the expression a call to covr:::count
is_covr_count_call(expr)is_covr_count_call(expr)
expr |
A language object |
A logical value indicating whether the object is a call to
covr:::count or covr:::count_and_record.
Quickly dismiss the need to update the current test if we can. To test if we're still in the last test, check if the same srcref (or call, if source is not kept) exists at the last recorded calling frame prior to entering a covr trace. If this has changed, do a more comprehensive test to see if any of the test call stack has changed, in which case we are onto a new test.
is_current_test_finished()is_current_test_finished()
Write coverage in the LCOV tracefile format described in
geninfo(1),
which is read by genhtml, Codecov, Coveralls, SonarQube and editor
plugins such as Coverage Gutters.
lcov(cov, filename = "lcov.info")lcov(cov, filename = "lcov.info")
cov |
the coverage object returned from |
filename |
the name of the LCOV file |
Each source file gets a record with its functions (FN, FNDA, FNF,
FNH) and lines (DA, LF, LH). Line counts are those of
tally_coverage() with by = "line", combined so that each source line
has a single DA record: when several functions share a line, the line's
count is the smallest of theirs, so a line only counts as covered if all its
code was run, as in report(), percent_coverage() and codecov(). A
function's count is the largest count of any of its expressions.
Branches are only recorded if they were measured with
options(covr.branch_coverage = TRUE) (see covr.branch_coverage). Each
branch gets a BRDA record on the line where its if or switch()
starts, numbered from zero within each if or switch(). Its count is -
if the if or switch() never ran. BRF and BRH give the number of
branches and the number of branches taken.
Source file paths (SF) are written as covr reports them, for a package
relative to the package root, so tools such as genhtml should be run from
that directory.
The lines of the LCOV file, invisibly.
cov <- code_coverage( "f <- function(x) {\n y <- x + 1\n y * 2\n}\ng <- function() 1", "f(1)" ) tmp <- tempfile(fileext = ".info") lcov(cov, tmp) cat(readLines(tmp), sep = "\n")cov <- code_coverage( "f <- function(x) {\n y <- x + 1\n y * 2\n}\ng <- function() 1", "f(1)" ) tmp <- tempfile(fileext = ".info") lcov(cov, tmp) cat(readLines(tmp), sep = "\n")
Initialize a test counter, a matrix used to tally tests, their stack depth
and the execution order as the trace associated with key is hit. Each
test trace is an environment, which allows assignment into a pre-allocated
tests matrix with minimall reallocation.
new_test_counter(key)new_test_counter(key)
key |
generated with |
The tests matrix has columns tests, depth and i,
corresponding to the test index (the index of the associated test in
.counters$tests), the stack depth when the trace is evaluated and the
number of traces that have been hit so far during test evaluation.
This function calculates the test coverage for a development package on the
path. By default it runs only the package tests, but it can also run
vignette and example code.
package_coverage( path = ".", type = c("tests", "vignettes", "examples", "all", "none"), combine_types = TRUE, relative_path = TRUE, quiet = TRUE, clean = TRUE, line_exclusions = NULL, function_exclusions = NULL, code = character(), install_path = temp_file("R_LIBS"), ..., pre_clean, allow_failures = getOption("covr.allow_failures", FALSE), show_code_output = FALSE )package_coverage( path = ".", type = c("tests", "vignettes", "examples", "all", "none"), combine_types = TRUE, relative_path = TRUE, quiet = TRUE, clean = TRUE, line_exclusions = NULL, function_exclusions = NULL, code = character(), install_path = temp_file("R_LIBS"), ..., pre_clean, allow_failures = getOption("covr.allow_failures", FALSE), show_code_output = FALSE )
path |
file path to the package, or to an installed package. |
type |
run the package ‘tests’, ‘vignettes’, ‘examples’, ‘all’, or ‘none’. The default is ‘tests’. |
combine_types |
If |
relative_path |
whether to output the paths as relative or absolute paths. If a string, it is interpreted as a root path and all paths will be relative to that root. |
quiet |
whether to load and compile the package quietly, useful for debugging errors. |
clean |
whether to clean temporary output files after running, mainly
useful for debugging errors. If coverage fails, or failures are reported as
warnings with |
line_exclusions |
a named list of files with the lines to exclude from each file. |
function_exclusions |
a vector of regular expressions matching function
names to exclude. Example |
code |
A character vector of additional test code to run. |
install_path |
The path the instrumented package will be installed to
and tests run in. By default it is a path in the R sessions temporary
directory. It can sometimes be useful to set this (along with |
... |
Additional arguments passed to |
pre_clean |
Deprecated and ignored. Objects present in the src directory are always deleted before recompiling. |
allow_failures |
If |
show_code_output |
If |
This function uses tools::testInstalledPackage() to run the
code, if you would like to test your package in another way you can set
type = "none" and pass the code to run as a character vector to the
code parameter.
Coverage is also recorded in other R processes that load the package, such
as callr sessions, future or parallel cluster workers, or
Shiny apps run by shinytest2. Each process saves its coverage when it
exits normally, so the coverage is lost if a process is killed (e.g. with
SIGKILL or SIGTERM) or is still running when the tests finish, and
package_coverage() warns (with class covr_unsaved_trace). Stop these
processes gracefully and wait for them to exit, e.g. call $stop() on a
shinytest2 AppDriver, which waits longer for the app to exit when
run by covr. See vignette("shiny", package = "covr") for measuring the
coverage of Shiny apps.
path can also be an installed package, e.g.
system.file(package = "pkg"), to compute coverage without the package
source. The package must have been installed with its tests and source
references, e.g. with
remotes::install_github("user/pkg", INSTALL_opts = c("--install-tests", "--with-keep.source")). File names are those of the source it was
installed from, and compiled code isn't covered.
The coverage of Rust code, built by cargo into a static library that is
linked into the package, is measured with LLVM's source-based coverage.
This needs the llvm tools of the Rust toolchain
(rustup component add llvm-tools), or for Rust installed by a Linux
distribution, those of the LLVM version rustc uses (e.g.
apt-get install llvm-18); without them package_coverage()
warns and reports no coverage for the Rust code. covr instruments the
crates of the package's cargo workspace by having cargo run rustc through a
wrapper (with RUSTC_WORKSPACE_WRAPPER) that adds -C instrument-coverage,
so it works however the package sets RUSTFLAGS, and dependencies are
built as usual. Vendored crates, including the C code they compile, and
code generated in cargo's target directory aren't included. The code that
macros such as #[extendr] and #[savvy] generate is counted on the
macro's line, so the line isn't covered if parts of the generated code
never run, e.g. the handling of arguments that can't be converted. The
coverage of Rust code isn't measured on Windows, as rustup's Rust for the
x86_64-pc-windows-gnu target that Rtools uses has no profiler runtime.
Parallelized code using parallel's mcparallel() needs to
use a patched parallel:::mcexit. This is done automatically if the
package depends on parallel, but can also be explicitly set using the
environment variable COVR_FIX_PARALLEL_MCEXIT or the global option
covr.fix_parallel_mcexit.
A coverage object. If combine_types = FALSE and more than one
type is run, a list of class coverages with one coverage object for
each type, named by type.
exclusions() For details on excluding parts of the
package from the coverage calculations.
Calculate the total percent coverage from a coverage result object.
percent_coverage(x, ...)percent_coverage(x, ...)
x |
the coverage object returned from |
... |
additional arguments passed to |
The total percentage as a numeric(1).
Print a coverage object
## S3 method for class 'coverage' print( x, group = c("filename", "functions"), by = "line", n = Inf, missing = FALSE, ... )## S3 method for class 'coverage' print( x, group = c("filename", "functions"), by = "line", n = Inf, missing = FALSE, ... )
x |
the coverage object to be printed |
group |
whether to group coverage by filename or function |
by |
whether to count coverage by line, expression or function |
n |
the maximum number of files or functions to print. They are sorted from least to most covered, so the least covered are shown first. |
missing |
whether to list the lines that were not run after each file
or function that has any, as ranges such as If branch coverage was measured (see covr.branch_coverage), the missed
branches of each file or function are also listed, as |
... |
additional arguments ignored |
The coverage object (invisibly).
If branch coverage was measured (see covr.branch_coverage), the
percentage of branches taken is also shown after the overall coverage, e.g.
Coverage: 87.40% (branches 72.00%).
cov <- code_coverage( "f <- function(x) x + 1; g <- function(x) x - 1; h <- function(x) 0", "f(1)" ) print(cov, group = "functions", n = 2) cov <- code_coverage( "f <- function(x) {\n if (x) {\n 1\n\n # no\n 2\n }\n}", "f(FALSE)" ) print(cov, missing = TRUE)cov <- code_coverage( "f <- function(x) x + 1; g <- function(x) x - 1; h <- function(x) 0", "f(1)" ) print(cov, group = "functions", n = 2) cov <- code_coverage( "f <- function(x) {\n if (x) {\n 1\n\n # no\n 2\n }\n}", "f(FALSE)" ) print(cov, missing = TRUE)
report() saves the coverage report as a standalone HTML file and opens it.
To include the report in a larger document, such as an R Markdown or Quarto
document, use report_html() instead.
report( x = package_coverage(), file = file.path(tempdir(), paste0(get_package_name(x), "-report.html")), browse = interactive() )report( x = package_coverage(), file = file.path(tempdir(), paste0(get_package_name(x), "-report.html")), browse = interactive() )
x |
a coverage dataset, defaults to running |
file |
The report filename. |
browse |
whether to open a browser to view the report. |
If branch coverage was measured with options(covr.branch_coverage = TRUE)
(see covr.branch_coverage), the report also shows the branches of each
if and switch(). The title has the percentage of branches taken after
the line coverage, and the list of files has a "Branches" column with the
percentage of the branches of each file that were taken. In the source, a
line with branches has the number of its branches taken out of the total,
e.g. 1/2; hovering over it or focusing it shows the label and count of
each branch. A line with a branch that wasn't taken is shown as partly
covered, unless its if or switch() never ran, which leaves it missed.
Covered lines are shown in green and missed lines in red. To use other
colors, set the covr.report_colors option to a named character vector of
R colors (anything accepted by grDevices::col2rgb(); transparency is
ignored) with elements covered, missed and/or partial. The colors are
used for the highlighted lines and the coverage bars in the file list;
partial is used for the bars of files with at least 75% but less than 90%
coverage, which otherwise follow covered, and for the partly covered lines
of reports with branches, which are otherwise yellow.
colorblind_palette has colors that are easier to tell apart with color vision deficiency:
options(covr.report_colors = covr::colorblind_palette)
The report uses a dark theme if the browser or operating system is set to prefer one. The moon (or sun) button at the top right switches between the light and dark themes; the browser remembers the choice for other reports until it is switched back.
## Not run: x <- package_coverage() report(x) # Colors that are distinguishable with most kinds of color blindness old <- options(covr.report_colors = colorblind_palette) report(x) options(old) ## End(Not run)## Not run: x <- package_coverage() report(x) # Colors that are distinguishable with most kinds of color blindness old <- options(covr.report_colors = colorblind_palette) report(x) options(old) ## End(Not run)
report_html() creates the coverage report shown by report() as HTML that
can be included in a larger document, such as an R Markdown or Quarto
document or a page made with htmltools. Printing it in an R Markdown or
Quarto chunk includes the report in the document; printing it in the console
shows it in the viewer.
report_html(x = package_coverage())report_html(x = package_coverage())
x |
a coverage dataset, defaults to running |
The report takes the width of the element it is in, and its styles and scripts only apply to the report, so they don't change the rest of the document. A document can include several reports.
The colors and the dark theme can be changed as described in report().
An htmltools::tagList(), with the dependencies of the report
attached.
## Not run: # In an R Markdown or Quarto chunk report_html(package_coverage()) # Save it with other content htmltools::save_html( htmltools::tagList( htmltools::h1("Package review"), report_html(package_coverage()) ), "review.html" ) ## End(Not run)## Not run: # In an R Markdown or Quarto chunk report_html(package_coverage()) # Save it with other content htmltools::save_html( htmltools::tagList( htmltools::h1("Package review"), report_html(package_coverage()) ), "review.html" ) ## End(Not run)
Measures the coverage of the functions of a Shiny app that is not part of a
package, while code runs its tests, by default with
shinytest2::test_app(). This includes the code run by the app in the R
processes that shinytest2 starts to run it. For apps in a package, use
package_coverage() instead.
shiny_app_coverage( app_dir = ".", code = shinytest2::test_app(app_dir), line_exclusions = NULL, function_exclusions = NULL )shiny_app_coverage( app_dir = ".", code = shinytest2::test_app(app_dir), line_exclusions = NULL, function_exclusions = NULL )
app_dir |
The directory of the Shiny app. |
code |
The code to run the app's tests, evaluated in the calling environment. |
line_exclusions |
a named list of files with the lines to exclude from each file. |
function_exclusions |
a vector of regular expressions matching function
names to exclude. Example |
The functions defined by the app's files (app.R, ui.R, server.R,
global.R and the files in R/) are instrumented when Shiny loads them,
in this R process and in the R processes started while code runs. This
includes a server function that isn't assigned to a name, such as the one
in server.R or one defined inline, e.g. shinyApp(ui, function(input, output) ...). Code outside functions, such as building the UI, is not
counted, as in package_coverage().
This works by adding covr to the default packages of R processes started
while code runs (the R_DEFAULT_PACKAGES environment variable), and
relies on internal hooks of Shiny which may change in future versions.
An app process saves its coverage when it exits normally, so stop it
gracefully, e.g. with $stop() on a shinytest2 AppDriver, which
waits longer for the app to exit when run by covr. If an app process is
killed or is still running when code finishes, its coverage is lost and
shiny_app_coverage() warns (with class covr_unsaved_trace).
AppDriver$new() skips tests on CRAN unless the NOT_CRAN environment
variable is "true".
A coverage object.
vignette("shiny", package = "covr")
## Not run: Sys.setenv(NOT_CRAN = "true") shiny_app_coverage("path/to/app") ## End(Not run)## Not run: Sys.setenv(NOT_CRAN = "true") shiny_app_coverage("path/to/app") ## End(Not run)
This functionality requires the xml2 package be installed.
sonarqube(cov, filename = "sonarqube.xml", path_prefix = NULL)sonarqube(cov, filename = "sonarqube.xml", path_prefix = NULL)
cov |
the coverage object returned from |
filename |
the name of the SonarQube Generic XML file |
path_prefix |
SonarQube expects file paths relative to the project's
base directory. By default the paths are relative to the package root
(e.g. |
Talkdesk Inc.
Tally coverage by line, expression, function or branch
tally_coverage(x, by = c("line", "expression", "function", "branch"))tally_coverage(x, by = c("line", "expression", "function", "branch"))
x |
the coverage object returned from |
by |
whether to tally coverage by line, expression, function or branch |
A data.frame. With by = "expression" it is the same as
as.data.frame.coverage(). With by = "line" it has one row for each
function on each line of code (excluding blank lines, comments and lines
with only punctuation such as }) and the columns filename,
functions, line and value, where value is the smallest execution
count of the expressions on that line, so a line only counts as covered
if all its expressions were run. An expression spanning several lines,
such as a loop, only counts for the lines that none of the expressions
inside it are on. percent_coverage(), report() and the other line
based outputs count each line once, with the smallest count of its
functions.
With by = "function" it has one row for each top level R function and
the columns filename, functions, first_line, last_line and
value, where first_line and last_line span the function's traced
code and value is the largest execution count of its expressions, so a
function counts as covered if any of its code was run. Compiled code has
no function names, so it is left out.
With by = "branch" it has one row for each branch measured with
options(covr.branch_coverage = TRUE) (see covr.branch_coverage), and
the columns filename, functions, line and column (where the if
or switch() starts), branch (the index of the branch in its if or
switch()), label and value, the number of times the branch was
taken. It has no rows if branch coverage wasn't measured.
cov <- code_coverage( "f <- function(x) {\n if (x) 'yes' else 'no'\n}\ng <- function() 1", "f(TRUE)" ) tally_coverage(cov) tally_coverage(cov, by = "expression") tally_coverage(cov, by = "function")cov <- code_coverage( "f <- function(x) {\n if (x) 'yes' else 'no'\n}\ng <- function() 1", "f(TRUE)" ) tally_coverage(cov) tally_coverage(cov, by = "expression") tally_coverage(cov, by = "function")
Builds a traceability matrix from coverage recorded with
options(covr.record_tests = TRUE): which tests ran each line of code. Use
it to find the tests to rerun after changing some lines, or the tests
exercising a function.
tally_tests(x)tally_tests(x)
x |
a coverage object computed with
|
Lines are those of tally_coverage() with by = "line", so blank lines,
comments, lines with only punctuation and excluded lines are left out. A
test is a top level call in a test file that ran package code, such as an
expectation; see covr.record_tests.
A data.frame with one row for each line and each test that ran
it, and the columns:
filename, functions, line: the line of code, as in
tally_coverage().
test_file, test_line: where the test call is. For
package_coverage(), test files are relative to the package directory,
e.g. tests/testthat/test-foo.R. NA if the test has no source
references.
test: the description of the enclosing test_that() or it() call,
or NA.
value: the number of times the test ran the line.
cov <- withr::with_options( list(covr.record_tests = TRUE), code_coverage( "f <- function(x) {\n if (x) 'yes' else 'no'\n}", "f(TRUE)\nf(FALSE)" ) ) tests <- tally_tests(cov) tests # The tests that ran lines 1 to 2 unique(tests[tests$line %in% 1:2, c("test_file", "test_line")])cov <- withr::with_options( list(covr.record_tests = TRUE), code_coverage( "f <- function(x) {\n if (x) 'yes' else 'no'\n}", "f(TRUE)\nf(FALSE)" ) ) tests <- tally_tests(cov) tests # The tests that ran lines 1 to 2 unique(tests[tests$line %in% 1:2, c("test_file", "test_line")])
A helper to circumvent R errors when deserializing large call objects from
Rds. Trims the number of arguments in a call object, and replaces the last
argument with a <truncated> symbol.
truncate_call(call_obj, limit = 10000)truncate_call(call_obj, limit = 10000)
call_obj |
A (possibly large) |
limit |
A |
The call_obj with arguments trimmed
Retrieve the execution counts from a coverage object
value(x, ...)value(x, ...)
x |
a coverage object, or one of its elements. |
... |
additional arguments passed to methods. |
For a coverage object, a named numeric vector with the number of
times each traced expression or line was executed, named like the
elements of x (see coverage). For a single element, its count.
cov <- code_coverage("add <- function(x, y) { x + y }", "add(1, 2)") value(cov)cov <- code_coverage("add <- function(x, y) { x + y }", "add(1, 2)") value(cov)
with_coverage() instruments the functions in env, evaluates code, and
returns the coverage that code produced. Unlike environment_coverage(),
which sys.source()s a set of files, code can be any expression. This
makes it possible to measure coverage while running tests with a test runner
such as testthat::test_file(), without having to recreate the runner's
infrastructure (reporters, snapshot handling, test environment, ...).
with_coverage( code, env = parent.frame(), line_exclusions = NULL, function_exclusions = NULL )with_coverage( code, env = parent.frame(), line_exclusions = NULL, function_exclusions = NULL )
code |
The code to evaluate while measuring coverage. It is evaluated
lazily, in the calling environment, after |
env |
The environment to be instrumented, e.g. a package namespace. |
line_exclusions |
a named list of files with the lines to exclude from each file. |
function_exclusions |
a vector of regular expressions matching function
names to exclude. Example |
Functions are instrumented in place, so every existing reference to them,
including ones captured by an environment created by code (e.g. the child
environment of the namespace that testthat::test_file() evaluates tests
in), runs the instrumented version. The only requirement is that code must
not re-create the functions in env, e.g. when using
testthat::test_file() with package set the package must not be reloaded
(use load_package = "none"), as reloading would replace the instrumented
functions with fresh, uninstrumented ones.
The instrumentation is removed and the counters are cleared when
with_coverage() exits, even if code fails.
A coverage object.
Code run in processes forked by parallel::mclapply(),
parallel::mcparallel() and related functions (not available on Windows)
is included in the coverage. While the code runs, parallel:::mcfork and
parallel:::mcexit are patched so that the forked processes save their
counts when they exit. To turn this off, set the environment variable
COVR_FIX_PARALLEL_MCEXIT or the option covr.fix_parallel_mcexit to
FALSE.
environment_coverage() and with_coverage() include the compiled code in
the coverage of the namespace of a package loaded with pkgload::load_all()
(e.g. by devtools::load_all()), so devtools::test_coverage_active_file()
also works for files in src/. With compiled files in source_files,
file_coverage() loads their package with pkgload::load_all() and reports
the coverage of those files, without needing devtools:
file_coverage("src/foo.c", "tests/testthat/test-foo.R")
file_coverage("src/rust/src/lib.rs", "tests/testthat/test-foo.R")
The compiled code is measured in the current session, which is much faster
than package_coverage(): C, C++, Fortran and Objective-C code with gcov,
and Rust code (e.g. of packages using extendr or savvy) with LLVM's
source-based coverage, which needs the llvm tools described in
package_coverage(). An instrumented copy of the package is built in the
session's temporary directory, leaving the package's own build untouched,
and later runs only recompile the files that changed, with cargo's
incremental build in the copy's own target directory for Rust code.
While the code runs, the namespace's native routines use the instrumented
DLL instead of the package's own. Routines that were looked up some other
way, e.g. stored by .onLoad() or found by other packages with
R_GetCCallable(), keep using the package's DLL, so their code isn't
counted. Set options(covr.gcov = "") to leave out C, C++, Fortran and
Objective-C code, and options(covr.llvm_cov = "") to leave out Rust code.
## Not run: # With a package loaded by pkgload::load_all() ns <- asNamespace("mypkg") with_coverage( testthat::test_file( "tests/testthat/test-foo.R", package = "mypkg", load_package = "none" ), env = ns ) ## End(Not run)## Not run: # With a package loaded by pkgload::load_all() ns <- asNamespace("mypkg") with_coverage( testthat::test_file( "tests/testthat/test-foo.R", package = "mypkg", load_package = "none" ), env = ns ) ## End(Not run)
When examining the test coverage of a package, it is useful to know if there are any locations where there is 0 test coverage.
zero_coverage(x, ...)zero_coverage(x, ...)
x |
a coverage object returned |
... |
additional arguments passed to
|
if used within RStudio this function outputs the results using the Marker API.
A data.frame with coverage data where the coverage is 0.
cov <- code_coverage( "f <- function(x) {\n if (x) 'yes' else 'no'\n}\ng <- function() 1", "f(TRUE)" ) zero_coverage(cov) zero_coverage(cov, by = "function")cov <- code_coverage( "f <- function(x) {\n if (x) 'yes' else 'no'\n}\ng <- function() 1", "f(TRUE)" ) zero_coverage(cov) zero_coverage(cov, by = "function")