Showing preview only (393K chars total). Download the full file or copy to clipboard to get everything.
Repository: jimhester/covr
Branch: main
Commit: f1866d296c00
Files: 256
Total size: 336.2 KB
Directory structure:
gitextract_nxw_371p/
├── .Rbuildignore
├── .gitattributes
├── .github/
│ ├── .gitignore
│ └── workflows/
│ ├── R-CMD-check.yaml
│ ├── pkgdown.yaml
│ ├── pr-commands.yaml
│ └── test-coverage.yaml
├── .gitignore
├── .lintr
├── CODE_OF_CONDUCT.md
├── DESCRIPTION
├── LICENSE
├── LICENSE.md
├── Makefile
├── NAMESPACE
├── NEWS.md
├── R/
│ ├── R6.R
│ ├── RC.R
│ ├── S4.R
│ ├── S7.R
│ ├── azure.R
│ ├── box.R
│ ├── cobertura.R
│ ├── codecov.R
│ ├── compiled.R
│ ├── coveralls.R
│ ├── covr.R
│ ├── data_frame.R
│ ├── display_name.R
│ ├── exclusions.R
│ ├── gitlab.R
│ ├── icc.R
│ ├── parallel.R
│ ├── parse_data.R
│ ├── replace.R
│ ├── report.R
│ ├── sonarqube.R
│ ├── summary_functions.R
│ ├── system.R
│ ├── trace_calls.R
│ ├── trace_tests.R
│ ├── utils.R
│ ├── value.R
│ ├── vectorized.R
│ └── zzz.R
├── README.md
├── SECURITY.md
├── _pkgdown.yml
├── codecov.yml
├── cran-comments.md
├── docker_checker/
│ └── Dockerfile
├── inst/
│ ├── rstudio/
│ │ └── addins.dcf
│ └── www/
│ ├── report.css
│ └── shared/
│ └── highlight.js/
│ ├── LICENSE
│ ├── highlight.pack.js
│ └── rstudio.css
├── man/
│ ├── as_coverage.Rd
│ ├── as_coverage_with_tests.Rd
│ ├── azure.Rd
│ ├── clear_counters.Rd
│ ├── code_coverage.Rd
│ ├── codecov.Rd
│ ├── count.Rd
│ ├── count_test.Rd
│ ├── coverage_to_list.Rd
│ ├── coveralls.Rd
│ ├── covr-package.Rd
│ ├── covr.record_tests.Rd
│ ├── current_test_call_count.Rd
│ ├── current_test_index.Rd
│ ├── current_test_key.Rd
│ ├── display_name.Rd
│ ├── environment_coverage.Rd
│ ├── exclusions.Rd
│ ├── file_coverage.Rd
│ ├── file_report.Rd
│ ├── function_coverage.Rd
│ ├── gitlab.Rd
│ ├── has_srcref.Rd
│ ├── in_covr.Rd
│ ├── is_covr_count_call.Rd
│ ├── is_current_test_finished.Rd
│ ├── key.Rd
│ ├── new_counter.Rd
│ ├── new_test_counter.Rd
│ ├── package_coverage.Rd
│ ├── percent_coverage.Rd
│ ├── print.coverage.Rd
│ ├── report.Rd
│ ├── system_check.Rd
│ ├── system_output.Rd
│ ├── tally_coverage.Rd
│ ├── to_cobertura.Rd
│ ├── to_sonarqube.Rd
│ ├── trace_calls.Rd
│ ├── truncate_call.Rd
│ ├── update_current_test.Rd
│ ├── value.Rd
│ └── zero_coverage.Rd
├── shim_package.sh
├── src/
│ └── reassign.c
├── tests/
│ ├── testthat/
│ │ ├── Test+Char/
│ │ │ └── TestCompiled/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestCompiled.R
│ │ │ ├── man/
│ │ │ │ └── simple.Rd
│ │ │ ├── src/
│ │ │ │ └── simple.cc
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestCompiled.R
│ │ │ └── testthat.R
│ │ ├── TestCompiled/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestCompiled.R
│ │ │ ├── man/
│ │ │ │ └── simple.Rd
│ │ │ ├── src/
│ │ │ │ ├── simple-header.h
│ │ │ │ ├── simple.cc
│ │ │ │ └── simple4.cc
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestCompiled.R
│ │ │ └── testthat.R
│ │ ├── TestCompiledSubdir/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestCompiledSubdir.R
│ │ │ ├── man/
│ │ │ │ └── simple.Rd
│ │ │ ├── src/
│ │ │ │ ├── Makevars
│ │ │ │ └── lib/
│ │ │ │ └── simple.c
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestCompiledSubdir.R
│ │ │ └── testthat.R
│ │ ├── TestExclusion/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestExclusion.R
│ │ │ ├── man/
│ │ │ │ └── test_me.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestExclusion.R
│ │ │ └── testthat.R
│ │ ├── TestFunctional/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── a.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-a.R
│ │ │ └── testthat.R
│ │ ├── TestNestedTestDirs/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── a.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ ├── nested_tests/
│ │ │ │ │ └── test-a.R
│ │ │ │ ├── test-a.R
│ │ │ │ └── test-nested-dir.R
│ │ │ └── testthat.R
│ │ ├── TestParallel/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestParallel.R
│ │ │ ├── man/
│ │ │ │ └── test_me.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestParallel.R
│ │ │ └── testthat.R
│ │ ├── TestPrint/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestPrint.R
│ │ │ ├── man/
│ │ │ │ └── test_me.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestSummary.R
│ │ │ └── testthat.R
│ │ ├── TestR6/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestR6.R
│ │ │ ├── man/
│ │ │ │ └── a.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestR6.R
│ │ │ └── testthat.R
│ │ ├── TestRC/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestRC.R
│ │ │ ├── man/
│ │ │ │ └── a.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestRC.R
│ │ │ └── testthat.R
│ │ ├── TestS4/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestS4.R
│ │ │ ├── codecov.yml
│ │ │ ├── man/
│ │ │ │ └── a.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestS4.R
│ │ │ └── testthat.R
│ │ ├── TestS7/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── foo.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-foo.R
│ │ │ └── testthat.R
│ │ ├── TestSummary/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestSummary.R
│ │ │ ├── man/
│ │ │ │ └── test_me.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestSummary.R
│ │ │ └── testthat.R
│ │ ├── TestUseTry/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── notry.R
│ │ │ └── tests/
│ │ │ ├── tests.R
│ │ │ └── testthat/
│ │ │ └── test-notry.R
│ │ ├── Testbox/
│ │ │ ├── app/
│ │ │ │ ├── app.R
│ │ │ │ └── modules/
│ │ │ │ └── module.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-module.R
│ │ │ └── testthat.R
│ │ ├── Testbox_R6/
│ │ │ ├── app/
│ │ │ │ ├── app.R
│ │ │ │ └── modules/
│ │ │ │ └── moduleR6.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-moduleR6.R
│ │ │ └── testthat.R
│ │ ├── Testbox_attached_modules_functions/
│ │ │ ├── app/
│ │ │ │ ├── app.R
│ │ │ │ └── modules/
│ │ │ │ └── module.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ ├── test-aliased_functions.R
│ │ │ │ ├── test-aliased_modules.R
│ │ │ │ ├── test-attached_functions.R
│ │ │ │ └── test-three_dots.R
│ │ │ └── testthat.R
│ │ ├── Testbox_attached_modules_functions_R6/
│ │ │ ├── app/
│ │ │ │ ├── app.R
│ │ │ │ └── modules/
│ │ │ │ └── moduleR6.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-attached_R6.R
│ │ │ └── testthat.R
│ │ ├── _snaps/
│ │ │ ├── Compiled.md
│ │ │ └── S7.md
│ │ ├── a
│ │ ├── b
│ │ ├── cobertura.xml
│ │ ├── corner-cases-test.R
│ │ ├── corner-cases.R
│ │ ├── corner-cases.Rds
│ │ ├── helper.R
│ │ ├── sonarqube.xml
│ │ ├── test-Compiled.R
│ │ ├── test-R6.R
│ │ ├── test-RC.R
│ │ ├── test-S4.R
│ │ ├── test-S7.R
│ │ ├── test-azure.R
│ │ ├── test-box-R6.R
│ │ ├── test-box.R
│ │ ├── test-box_attached_modules_functions-R6.R
│ │ ├── test-box_attached_modules_functions.R
│ │ ├── test-braceless.R
│ │ ├── test-cobertura.R
│ │ ├── test-codecov.R
│ │ ├── test-corner-cases.R
│ │ ├── test-coveralls.R
│ │ ├── test-covr.R
│ │ ├── test-exclusions.R
│ │ ├── test-file_coverage.R
│ │ ├── test-functions.R
│ │ ├── test-gcov.R
│ │ ├── test-gitlab.R
│ │ ├── test-memoised.R
│ │ ├── test-null.R
│ │ ├── test-package_coverage.R
│ │ ├── test-parallel.R
│ │ ├── test-print.R
│ │ ├── test-record_tests.R
│ │ ├── test-report.R
│ │ ├── test-report.htm
│ │ ├── test-sonarqube.R
│ │ ├── test-summary.R
│ │ ├── test-trace_calls.R
│ │ ├── test-utils.R
│ │ └── test-vectorized.R
│ └── testthat.R
├── unshim_package.sh
└── vignettes/
└── how_it_works.Rmd
================================================
FILE CONTENTS
================================================
================================================
FILE: .Rbuildignore
================================================
^covr\.Rproj$
\.tar\.gz$
^travis-tool\.sh$
^LICENSE\.md$
^covr\.Rcheck$
^\.travis\.yml$
^shim_package\.sh$
^unshim_package\.sh$
Makefile
docker_checker
_dev\.R$
^\.lintr$
^appveyor\.yml$
^wercker\.yml$
^\.Rproj\.user$
^tests/testthat/.*/.*(o|sl|so|dylib|a|dll|def)$
^covr.*$
^cran_comments\.md$
^revdep/
^cran-comments\.md$
^cache$
^data.Rmd$
^covr_performance.Rmd$
^revdep$
^CRAN-RELEASE$
^docs$
^_pkgdown\.yml$
^pkgdown$
^script.R$
^azure-pipelines\.yml$
^[.]github$
^codecov\.yml$
^CODE_OF_CONDUCT\.md$
^\.github$
^SECURITY\.md$
^CRAN-SUBMISSION$
================================================
FILE: .gitattributes
================================================
/NEWS.md merge=union
================================================
FILE: .github/.gitignore
================================================
*.html
================================================
FILE: .github/workflows/R-CMD-check.yaml
================================================
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
#
# NOTE: This workflow is overkill for most R packages and
# check-standard.yaml is likely a better choice.
# usethis::use_github_action("check-standard") will install it.
on:
workflow_dispatch:
push:
branches: [main, master]
pull_request:
branches: [main, master]
name: R-CMD-check
jobs:
R-CMD-check:
runs-on: ${{ matrix.config.os }}
name: ${{ matrix.config.os }} (${{ matrix.config.r }})
strategy:
fail-fast: false
matrix:
config:
- {os: macos-latest, r: 'release'}
- {os: windows-latest, r: 'release'}
# Use 3.6 to trigger usage of RTools35
- {os: windows-latest, r: '3.6'}
# use 4.1 to check with rtools40's older compiler
- {os: windows-latest, r: '4.1'}
- {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
- {os: ubuntu-latest, r: 'release'}
- {os: ubuntu-latest, r: 'oldrel-1'}
- {os: ubuntu-latest, r: 'oldrel-2'}
- {os: ubuntu-latest, r: 'oldrel-3'}
- {os: ubuntu-latest, r: 'oldrel-4'}
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
R_KEEP_PKG_SOURCE: yes
steps:
- uses: actions/checkout@v3
- uses: r-lib/actions/setup-pandoc@v2
- uses: r-lib/actions/setup-r@v2
with:
r-version: ${{ matrix.config.r }}
http-user-agent: ${{ matrix.config.http-user-agent }}
use-public-rspm: true
- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::rcmdcheck
needs: check
- uses: r-lib/actions/check-r-package@v2
with:
upload-snapshots: true
================================================
FILE: .github/workflows/pkgdown.yaml
================================================
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
release:
types: [published]
workflow_dispatch:
name: pkgdown
jobs:
pkgdown:
runs-on: ubuntu-latest
# Only restrict concurrency for non-PR jobs
concurrency:
group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
permissions:
contents: write
steps:
- uses: actions/checkout@v3
- uses: r-lib/actions/setup-pandoc@v2
- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true
- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::pkgdown, local::.
needs: website
- name: Build site
run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
shell: Rscript {0}
- name: Deploy to GitHub pages 🚀
if: github.event_name != 'pull_request'
uses: JamesIves/github-pages-deploy-action@v4.4.1
with:
clean: false
branch: gh-pages
folder: docs
================================================
FILE: .github/workflows/pr-commands.yaml
================================================
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
issue_comment:
types: [created]
name: Commands
jobs:
document:
if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/document') }}
name: document
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v3
- uses: r-lib/actions/pr-fetch@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true
- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::roxygen2
needs: pr-document
- name: Document
run: roxygen2::roxygenise()
shell: Rscript {0}
- name: commit
run: |
git config --local user.name "$GITHUB_ACTOR"
git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
git add man/\* NAMESPACE
git commit -m 'Document'
- uses: r-lib/actions/pr-push@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
style:
if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') && startsWith(github.event.comment.body, '/style') }}
name: style
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v3
- uses: r-lib/actions/pr-fetch@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- uses: r-lib/actions/setup-r@v2
- name: Install dependencies
run: install.packages("styler")
shell: Rscript {0}
- name: Style
run: styler::style_pkg()
shell: Rscript {0}
- name: commit
run: |
git config --local user.name "$GITHUB_ACTOR"
git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
git add \*.R
git commit -m 'Style'
- uses: r-lib/actions/pr-push@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/test-coverage.yaml
================================================
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
name: test-coverage
jobs:
test-coverage:
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v3
- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true
- uses: r-lib/actions/setup-r-dependencies@v2
with:
needs: coverage
- name: Setup
run: |
R CMD INSTALL .
source shim_package.sh
- name: Test coverage
run: |
covr::codecov(
quiet = FALSE,
clean = FALSE,
install_path = file.path(Sys.getenv("RUNNER_TEMP"), "package")
)
shell: Rscript {0}
- name: Show testthat output
if: always()
run: |
## --------------------------------------------------------------------
find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true
shell: bash
- name: Upload test results
if: failure()
uses: actions/upload-artifact@v4
with:
name: coverage-test-failures
path: ${{ runner.temp }}/package
================================================
FILE: .gitignore
================================================
*.o
*.so
inst/doc
.Rproj.user
covr.Rproj
cache/
data.Rmd
vignettes/covr_performance.Rmd
revdep/checks
revdep/library
docs/
script.R
.Rhistory
================================================
FILE: .lintr
================================================
linters: linters_with_defaults(line_length_linter(120))
exclusions: list("inst/doc/how_it_works.R")
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at james.f.hester@gmail.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
<https://www.contributor-covenant.org/version/2/1/code_of_conduct.html>.
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][https://github.com/mozilla/inclusion].
For answers to common questions about this code of conduct, see the FAQ at
<https://www.contributor-covenant.org/faq>. Translations are available at <https://www.contributor-covenant.org/translations>.
[homepage]: https://www.contributor-covenant.org
================================================
FILE: DESCRIPTION
================================================
Encoding: UTF-8
Package: covr
Title: Test Coverage for Packages
Version: 3.6.5.9001
Authors@R: c(
person("Jim", "Hester", email = "james.f.hester@gmail.com", role = c("aut", "cre")),
person("Willem", "Ligtenberg", role = "ctb"),
person("Kirill", "Müller", role = "ctb"),
person("Henrik", "Bengtsson", role = "ctb"),
person("Steve", "Peak", role = "ctb"),
person("Kirill", "Sevastyanenko", role = "ctb"),
person("Jon", "Clayden", role = "ctb"),
person("Robert", "Flight", role = "ctb"),
person("Eric", "Brown", role = "ctb"),
person("Brodie", "Gaslam", role = "ctb"),
person("Will", "Beasley", role = "ctb"),
person("Robert", "Krzyzanowski", role = "ctb"),
person("Markus", "Wamser", role = "ctb"),
person("Karl", "Forner", role = "ctb"),
person("Gergely", "Daróczi", role = "ctb"),
person("Jouni", "Helske", role = "ctb"),
person("Kun", "Ren", role = "ctb"),
person("Jeroen", "Ooms", role = "ctb"),
person("Ken", "Williams", role = "ctb"),
person("Chris", "Campbell", role = "ctb"),
person("David", "Hugh-Jones", role = "ctb"),
person("Qin", "Wang", role = "ctb"),
person("Doug", "Kelkhoff", role = "ctb"),
person("Ivan", "Sagalaev", role = c("ctb", "cph"), comment = "highlight.js library"),
person("Mark", "Otto", role = "ctb", comment = "Bootstrap library"),
person("Jacob", "Thornton", role = "ctb", comment = "Bootstrap library"),
person(family = "Bootstrap contributors", role = "ctb", comment = "Bootstrap library"),
person(family = "Twitter, Inc", role = "cph", comment = "Bootstrap library")
)
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 code.
URL: https://covr.r-lib.org, https://github.com/r-lib/covr
BugReports: https://github.com/r-lib/covr/issues
Depends:
R (>= 3.1.0),
methods
Imports:
digest,
stats,
utils,
jsonlite,
rex,
httr,
cli,
withr (>= 1.0.2),
yaml
Suggests:
R6,
S7 (>= 0.2.0),
curl,
knitr,
rmarkdown,
htmltools,
DT (>= 0.2),
testthat (>= 3.0.0),
rlang,
rstudioapi (>= 0.2),
xml2 (>= 1.0.0),
parallel,
memoise,
covr,
box (>= 1.2.0)
License: MIT + file LICENSE
VignetteBuilder: knitr
RoxygenNote: 7.3.3
Roxygen: list(markdown = TRUE)
Config/testthat/edition: 3
Config/testthat/parallel: TRUE
================================================
FILE: LICENSE
================================================
YEAR: 2022
COPYRIGHT HOLDER: covr authors
================================================
FILE: LICENSE.md
================================================
# MIT License
Copyright (c) 2022 covr authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
RCHECKER=docker_checker
FILTER=
# to install test packages, code must be compiled inside src/ dir,
# that may cause problems for docker user docker
# so we grant all permissions
fix-permission-tests:
chmod -R a+rwx tests
build-docker-checker:
docker build -t $(RCHECKER) docker_checker
run-rocker: build-docker-checker
-docker rm $(RCHECKER)
docker run --rm -ti -v $(PWD)/..:/home/docker $(RCHECKER) bash
test: build-docker-checker fix-permission-tests
docker run --rm -ti -v $(PWD)/..:/home/docker $(RCHECKER) Rscript -e 'library(devtools);install("covr");test("covr", "$(FILTER)")'
check: build-docker-checker fix-permission-tests
docker run --rm -ti -v $(PWD)/..:/home/docker $(RCHECKER) Rscript -e 'library(devtools);install("covr");devtools::check("covr")'
rox: build-docker-checker
docker run --rm -ti -v $(PWD)/..:/home/docker $(RCHECKER) Rscript -e 'devtools::document("covr")'
================================================
FILE: NAMESPACE
================================================
# Generated by roxygen2: do not edit by hand
S3method("[",coverage)
S3method(as.data.frame,coverage)
S3method(markers,coverage)
S3method(markers,coverages)
S3method(markers,data.frame)
S3method(merge_coverage,character)
S3method(merge_coverage,list)
S3method(print,coverage)
S3method(print,coverages)
S3method(value,coverage)
S3method(value,expression_coverage)
S3method(value,expression_coverages)
S3method(value,line_coverage)
S3method(value,line_coverages)
export(azure)
export(code_coverage)
export(codecov)
export(coverage_to_list)
export(coveralls)
export(display_name)
export(environment_coverage)
export(file_coverage)
export(file_report)
export(function_coverage)
export(gitlab)
export(in_covr)
export(package_coverage)
export(percent_coverage)
export(report)
export(tally_coverage)
export(to_cobertura)
export(to_sonarqube)
export(value)
export(zero_coverage)
import(methods)
importFrom(httr,RETRY)
importFrom(httr,content)
importFrom(httr,upload_file)
importFrom(stats,aggregate)
importFrom(stats,na.omit)
importFrom(stats,na.pass)
importFrom(stats,setNames)
importFrom(utils,capture.output)
importFrom(utils,getParseData)
importFrom(utils,getSrcDirectory)
importFrom(utils,getSrcFilename)
importFrom(utils,getSrcref)
importFrom(utils,head)
importFrom(utils,relist)
importFrom(utils,str)
importFrom(utils,tail)
useDynLib(covr, .registration = TRUE)
================================================
FILE: NEWS.md
================================================
# covr (development version)
* Fix a rare edge case where `count_test` was called before `.current_test` has
been initialized leading to crash (@maksymiuks, #631).
* Fix rare error in `clean_coverage_tests` where `NA` were being compared
in `if` condition (@maksymiuks, #631).
# covr 3.6.5
## New Features and improvements
* Added support for `klmr/box` modules. This works best with `file_coverage()`. (@radbasa, #491)
* Performance improvement for compiled code with a lot of compilation units (@krlmlr, #611)
* Fix R CMD check NOTE for upcoming R 4.6: non-API calls to SET_BODY, SET_CLOENV, SET_FORMALS (@t-kalinowski, #587)
## Fixes and minor improvements
* Messages are now displayed using cli instead of crayon (@olivroy, #591).
* covr now uses `testthat::with_mocked_bindings()` for its internal testing (@olivroy, #595).
* Fix a bug preventing `package_coverage()` from running tests when `install_path` is set to a relative path (@gergness, #517, #548).
* Fixed a performance regression and an error triggered by a change in R
4.4.0. (@kyleam, #588)
* Fixed an issue where attempting to generate code coverage on an already-loaded
package could fail on Windows. (@kevinushey, #574)
* Prevent `covr.record_tests` option from logging duplicate tests when the same
line of testing code is hit repeatedly, as in a loop. (@dgkf, #528)
* Normalize `install_path` path before creating directory to prevent
failures when running covr in a subprocess using a path with Windows
`\\` path separators. (@maksymiuks, #592)
# covr 3.6.4
* Fix for a failing test on CRAN
# covr 3.6.3
* Updates to internal usage of `is.atomic()` to work with upcoming R release (@mmaechler , #542)
* `package_coverage()` now works correctly with ignore files when it is not run in the package root directory (@mpadge, #538)
# covr 3.6.2
# covr 3.6.1
* `to_cobertura()` is now explicit about the doctype of the resulting XML. It also sets a source path if recorded. (@mmyrte, #524)
* The internal generic `merge_coverage()` now correctly registers the S3 methods.
* The internal test for recording large calls no longer assumes R is on the system PATH.
# covr 3.6.0
* Added `covr.record_tests` option. When `TRUE`, this enables the recording of the trace of the tests being executed and adds an itemization of which tests result in the execution of each trace.
For more details see `?covr.record_tests` (@dgkf, #463, #485, #503)
* `as.data.frame()` now returns an 0 row data frame when there are no functions in a package (#427)
* `codecov()` is now more robust when `coverage` is not the output from `package_coverage()` and `token` is not provided (#456)
* `package_coverage(code = )` now accepts character vectors of length greater than 1 (@bastistician, #481)
* `package_coverage()` now handles packages with install or render time examples (#488)
* `package_coverage()` now sets the environment variable `R_TESTS` to the tests-startup.R file like R CMD check does (#420)
* `report()` now provides a more detailed error message if the `DT` and `htmltools` dependencies are not installed (#500).
* Fix `parse_gcov` bug when package is stored in directory with regex special characters, see #459
* Error/warning thrown for, respectively, missing gcov or empty parsed gcov output (@stephematician, #448)
* Support Google Cloud Build uploading reports to Codecov.io (@MarkEdmondson1234 #469)
* covr is now licensed as MIT (#454)
# covr 3.5.1
* Generated files from [cpp11](https://cpp11.r-lib.org/) are now ignored (#437)
* `codecov()` and `coveralls()` now retry failed requests before raising an error (#428, @jameslamb)
# covr 3.5.0
* `codecov()` now supports GitHub Actions for public repositories without having to specify a token.
* New `to_sonarqube()` function added to support SonarQube generic XML format (@nibant, @Delfic, #413).
# covr 3.4.0
* `codecov()` now supports GitHub Actions.
* New `in_covr()` function added to return true if code is being run by covr (#407).
* `file_coverage()`, `environment_coverage()` and `function_coverage()` now set
`R_COVR=true`, to be consistent with `package_coverage()` (#407)
# covr 3.3.2
* Fix test failures in the development version of R (4.0.0) (#400)
# covr 3.3.1
* Fix inadvertent regression in return visibility when functions are covered.
covr versions prior to 3.3.0 surrounded each statement in `{` blocks. covr
3.3.0 switched to using `({`, but this caused an inadvertent regression, as
`(` will make the result visible it is the last expression in a function.
Using `if (TRUE) {` restores the previous behavior. (#391, #392)
# covr 3.3.0
## New Features
* New `azure()` function added to make it easy to use covr on [Azure
Pipelines](https://azure.microsoft.com/en-us/products/devops/pipelines/)
(#370)
* Work around issues related to the new curly curly syntax in rlang (#379, #377, rlang#813)
* Compiled code coverage has been improved, in particular C++ templates now
contain the merged coverage of all template instances, even if the instances
were defined in separate compilation units. (#390)
## Bugfixes and minor improvements
* `codecov()` now includes support for the flags field (#365)
* `codecov` now looks `codecov.yml` for token if `CODECOV_TOKEN` envvar is not
set (@MishaCivey #349).
* `per_line()` now does not track lines with only punctuation such as `}` or `{` (#387)
* `tally_coverage()` now includes compiled code, like it did previously (#384)
* Define the necessary coverage flags for C++14, C++17 and C++20 (#369).
* `to_cobertura()` now works with Cobertura coverage-04.dtd (@samssann, #337).
* [R6](https://github.com/r-lib/R6) class generators prefixed with `.` are now
included in coverage results (@jameslamb, #356).
* `package_coverage()` gains option `pre_clean`, set to `FALSE` to disable
cleaning of existing objects before running `package_coverage()` (@jpritikin, #375)
# 3.2.1
* Fix for regression when testing coverage of packages using mclapply (#335).
# 3.2.0
## Breaking changes
* Previously deprecated `shine()` has been removed. Instead use `report()`.
## New Features
* `file_report()` added when viewing coverage for a single file (#308).
* `display_name()` is now exported, which can be useful to filter the coverage
object by filename.
* `environment_coverage()` added, mainly so it can be used for `devtools::test_coverage_file()`.
* `gitlab()` function added to create a coverage report for GitLab using
GitLab's internal pages (@surmann, #327, #331).
* The (optional) dependency on shiny has been removed. `report()` can now be
built with only DT and htmltools installed.
## Bugfixes and minor improvements
* Fix for gcc-8 gcov output producing lines with no coverage counts in them (#328)
* `impute_srcref()` now handles `...` and drop through arguments in switch
statements (#325).
* `tally_coverage()` now avoids an error when there are NA values in the source
references (#322).
* `covr(clean = TRUE)` now cleans the temporary library as well (#144)
* `package_coverage()` now returns the end of the file if there is a test error (#319)
* `report()` now handles reports in relative paths with subdirectories correctly (#329)
* `report()` reworked to look more like codecov.io and to display the overall
coverage (#302, #307).
* DT explicitly loaded early in `report()` so that failures will occur fast if
it is not installed. (#321, @renkun-ken).
# 3.1.0 #
## Breaking changes
* `shine()` has been deprecated in favor of `report()`.
## New Features
* Add support for `.covrignore` files (#238), to exclude files from the coverage.
* Support future versions of R which do not use parse data by default (#309).
* Allow using `trace_calls()` for manually adding functions to package trace
that are not found automatically (#295, @mb706).
## Bugfixes
* Fix errors when R is not in the `PATH` (#291)
* Fix line computations when relative paths are being used (#242).
* Fix for Coveralls `Build processing error.` (#285) on pro accounts from
Travis CI (#306, @kiwiroy).
* Keep attributes of function bodies (#311, @gaborcsardi)
# 3.0.1 #
* Add an RStudio Addin for running a coverage report.
* Never use mcexit fix on windows (#223).
* Fix for a performance regression in parsing and reading parse data (#274).
* Fix `switch` support for packages, which was broken due to a bug in
how parse data is stored in packages.
* Improve behavior of `switch` coverage, it now supports default values and
fall through properly.
* Add `-p` flag to gcov command to preserve file paths. Fixes a bug where
gcov output didn't get reported when multiple compiled source files had
the same name (#271, @patperry)
# 3.0.0 #
* The covr license has been changed to GPL-3.
* Set environment variable `R_COVR=true` when covr is running (#236, #268).
* Made the gather-and-merge-results step at the end of package_coverage() more memory efficient (#226, @HenrikBengtsson).
* Support code coverage with icc (#247, @QinWang).
# 2.2.2 #
* `filter_not_package_files()` now works if a source reference does not have a filename (#254, @hughjonesd).
* Fix test broken with xml2 v1.1.0
* Filter out non-local filenames from results (#237).
* Vignette rewrite / improvements (#229, @CSJCampbell).
* Fix code that returns `structure(NULL, *)` which is deprecated in R 3.4.0 (#260, #261, @renkun-ken).
# 2.2.1 #
* Fix test broken with DT 0.2
# 2.2.0 #
* Fix tests broken with updated htmlwidgets
* Change report tab title based on filename (Chen Liang).
* Add support for cobertura XML output (@wligtenberg).
* Add mcparallel support by patching `mcparallel:::mcexit()`
automatically for packages using parallel (#195, @kforner).
# 2.1.0 #
* Add support for GitLab CI (#190, @enbrown).
* Update exclusion documentation to include line_exclusions and function
exclusions (#191).
* Support coverage of R6 methods (#174).
* Explicitly set default packages (including methods) (#183, #180)
* Set R_LIBS and R_LIBS_SITE as well as R_LIBS_USER (#188).
* Automatically exclude RcppExport files (#170).
* Memoised and Vectorized functions now able to be tracked.
# 2.0.1 #
* Support for filtering by function as well as line.
* Now tracks coverage for RC methods
* Rewrote loading and saving to support parallel code and tests including
`quit()` calls.
* Made passing code to `function_coverage()` and `package_coverage()` _not_ use
non-standard evaluation.
* `NULL` statements are analyzed for coverage (#156, @krlmlr).
* Finer coverage analysis for brace-less `if`, `while` and `for` statements (#154, @krlmlr).
* Run any combination of coverage types (#104, #133)
* Remove inconsistencies in line counts between shiny app and services (#129)
* Include header files in gcov output (#112)
* Add support for C++11 (#131)
* Always clean gcov files even on failure (#108)
* zero_coverage works with RStudio markers (#119)
* Remove the devtools dependency
# 1.3.0 #
* Set `.libPaths()` in subprocess to match those in calling process (#140, #147).
* Move devtools dependency to suggests, only needed on windows
* move htmltools to suggests
# 1.0.0 #
* Initial Release
================================================
FILE: R/R6.R
================================================
replacements_R6 <- function(env) {
unlist(recursive = FALSE, eapply(env, all.names = TRUE,
function(obj) {
if (inherits(obj, "R6ClassGenerator")) {
traverse_R6(obj, env)
}
}))
}
traverse_R6 <- function(obj, env) {
unlist(recursive = FALSE, eapply(obj,
function(o) {
if (inherits(o, "list")) {
lapply(names(o),
function(f_name) {
f <- get(f_name, o)
if (inherits(f, "function")) {
replacement(f_name, env = env, target_value = f)
}
})
}
}))
}
================================================
FILE: R/RC.R
================================================
replacements_RC <- function(env) {
pat <- paste0("^", classMetaName(""))
unlist(recursive = FALSE, lapply(ls(env, pattern = pat, all.names = TRUE),
function(name) {
class <- get(name, env)
if (extends(class, "envRefClass")) {
lapply(ls(class@refMethods, all.names = TRUE), replacement, env = class@refMethods)
}
}))
}
================================================
FILE: R/S4.R
================================================
replacements_S4 <- function(env) {
generics <- getGenerics(env)
unlist(recursive = FALSE,
Map(generics@.Data, generics@package, USE.NAMES = FALSE,
f = function(name, package) {
what <- methodsPackageMetaName("T", paste(name, package, sep = ":"))
table <- get(what, envir = env)
lapply(ls(table, all.names = TRUE), replacement, env = table)
})
)
}
================================================
FILE: R/S7.R
================================================
replacements_S7 <- function(env) {
bindings <- unlist(recursive = FALSE, use.names = FALSE, eapply(env, all.names = TRUE,
function(obj) {
if (inherits(obj, "S7_generic")) {
traverse_S7_generic(obj)
} else if (inherits(obj, "S7_class")) {
traverse_S7_class(obj)
}
}))
S7_methods_tbl <- attr(env[[".__S3MethodsTable__."]], "S7methods", exact = TRUE)
external_methods <- lapply(seq_along(S7_methods_tbl), function(i) {
entry <- S7_methods_tbl[[i]]
name <- external_generic_method_signature(entry$generic, entry$signature)
replacement(
# `name` is for informative printouts only.
# It is not used by covr, and does not need to be unique,
name = name,
env = entry,
target_value = entry$method)
})
c(bindings, external_methods)
}
traverse_S7_generic <- function(x) {
# Each binding in the environment at x@methods is either a function or, for
# generics that dispatch on multiple arguments, another environment.
get_replacements <- function(env) {
replacements <- lapply(names(env), function(name) {
target_value <- get(name, envir = env)
if (is.environment(target_value)) {
# Recurse for nested environments
get_replacements(target_value)
} else {
name <- as.character(attr(target_value, "name", exact = TRUE) %||% name)
list(replacement(name, env, target_value))
}
})
unlist(replacements, recursive = FALSE, use.names = FALSE)
}
get_replacements(S7::prop(x, "methods"))
}
traverse_S7_class <- function(x) {
class_name <- S7::prop(x, "name")
prop_fun_replacements <-
lapply(S7::prop(x, "properties"), function(p) {
lapply(c("getter", "setter", "validator"), function(prop_fun) {
if (!is.null(p[[prop_fun]])) {
replacement(
sprintf("%s@properties$%s$%s", class_name, p$name, prop_fun),
env = p,
target_value = p[[prop_fun]])
}
})
})
prop_fun_replacements <- unlist(prop_fun_replacements, recursive = FALSE, use.names = FALSE)
c(
list(
replacement(paste0(class_name, "@constructor"), env = x, target_value = S7::prop(x, "constructor")),
replacement(paste0(class_name, "@validator") , env = x, target_value = S7::prop(x, "validator"))
),
prop_fun_replacements
)
}
external_generic_method_signature <- function(generic, signature) {
# This function is a lightly modified copy of S7:::method_signature() for external generics
display_generic <- paste0(c(generic$package, generic$name), collapse = "::")
class_deparse <- asNamespace("S7")$class_deparse # not exported from S7 :/
single <- length(generic$dispatch_args) == 1
if (single) {
signature <- class_deparse(signature[[1]])
} else {
classes <- vapply(signature, class_deparse, "", USE.NAMES = FALSE)
signature <- paste0("list(", paste0(classes, collapse = ", "), ")")
}
sprintf("method(%s, %s)", display_generic, signature)
}
================================================
FILE: R/azure.R
================================================
#' Run covr on a package and output the result so it is available on Azure Pipelines
#' @inheritParams codecov
#' @inheritParams to_cobertura
#' @export
azure <- function(
...,
coverage = package_coverage(..., quiet = quiet), filename = "coverage.xml", quiet = TRUE) {
to_cobertura(coverage, filename = filename)
}
================================================
FILE: R/box.R
================================================
replacements_box <- function(env) {
unlist(recursive = FALSE, eapply(env, all.names = TRUE,
function(obj) {
if (inherits(attr(obj, "spec"), "box$mod_spec")) {
obj_impl <- attr(obj, "namespace")
compact(
c(
lapply(ls(obj_impl),
function(f_name) {
f <- get(f_name, obj_impl)
if (inherits(f, "function")) {
replacement(f_name, env = obj, target_value = f)
}
}
),
unlist(recursive = FALSE,
lapply(ls(obj_impl),
function(f_name) {
f <- get(f_name, obj_impl)
if (inherits(f, "R6ClassGenerator")) {
traverse_R6(f, obj)
}
}
)
)
)
)
}
}
)
)
}
================================================
FILE: R/cobertura.R
================================================
#' Create a Cobertura XML file
#'
#' Create a
#' cobertura-compliant XML report following [this
#' DTD](https://github.com/cobertura/cobertura/blob/master/cobertura/src/site/htdocs/xml/coverage-04.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](https://github.com/cobertura/cobertura/issues/425) for
#' context. Where `covr` doesn't provide a coverage metric (branch coverage,
#' complexity), a zero is reported.
#'
#' *Note*: This functionality requires the xml2 package be installed.
#'
#' @param cov the coverage object returned from [package_coverage()]
#' @param filename the name of the Cobertura XML file
#' @export
to_cobertura <- function(cov, filename = "cobertura.xml") {
loadNamespace("xml2")
df <- tally_coverage(cov, by = "line")
percent_overall <- percent_coverage(df, by = "line") / 100
percent_per_file <- tapply(df$value, df$filename, FUN = function(x) (sum(x > 0) / length(x)))
percent_per_function <- tapply(df$value, df$functions, FUN = function(x) (sum(x > 0) / length(x)))
lines_valid <- nrow(df)
lines_covered <- sum(df$value > 0)
d <- xml2::xml_new_document()
xml2::xml_add_child(d, xml2::xml_dtd(
name = "coverage",
system_id = "https://raw.githubusercontent.com/cobertura/cobertura/master/cobertura/src/site/htdocs/xml/coverage-04.dtd"
))
top <- xml2::xml_add_child(d,
"coverage",
"line-rate" = as.character(percent_overall),
"branch-rate" = "0",
`lines-covered` = as.character(lines_covered),
`lines-valid` = as.character(lines_valid),
`branches-covered` = "0",
`branches-valid` = "0",
complexity = 0,
version = as.character(utils::packageVersion("covr")),
timestamp = as.character(Sys.time()))
# Add sources
sources <- xml2::xml_add_child(top, "sources")
source_pth <- attr(cov, "package")$path %||% attr(cov, "root")
if (!is.null(source_pth)) {
xml2::xml_add_child(sources, "source", xml2::xml_cdata(source_pth))
}
files <- unique(df$filename)
#for (f in files){
#xml2::xml_add_child(sources, "source", f)
#}
# Add packages
packages <- xml2::xml_add_child(top, "packages")
package <- xml2::xml_add_child(packages, "package",
name = attr(cov, "package")$package,
"line-rate" = as.character(percent_overall),
"branch-rate" = "0",
complexity = "0")
classes <- xml2::xml_add_child(package, "classes")
# Add classes (for which we will use files for now)
for (f in files){
class <- xml2::xml_add_child(classes, "class",
name = basename(f),
filename = f,
"line-rate" = as.character(percent_per_file[f]),
"branch-rate" = "0",
complexity = "0")
# Add methods for all lines with functions
methods <- xml2::xml_add_child(class, "methods")
for (fun_name in unique(na.omit(df[df$filename == f, "functions"]))) {
fun <- xml2::xml_add_child(methods, "method",
name = fun_name,
signature = "",
"line-rate" = as.character(percent_per_function[fun_name]),
"branch-rate" = "0",
"complexity" = "0")
# Add lines
lines <- xml2::xml_add_child(fun, "lines")
fun_lines <- which(df$functions == fun_name)
for (i in fun_lines){
line <- df[i, ]
xml2::xml_add_child(lines, "line",
number = as.character(line$line),
hits = as.character(line$value),
branch = "false")
}
}
# Add lines for "class"
class_lines <- xml2::xml_add_child(class, "lines")
file_lines <- which(df$filename == f)
for (i in file_lines) {
line <- df[i, ]
xml2::xml_add_child(class_lines, "line",
number = as.character(line$line),
hits = as.character(line$value),
branch = "false")
}
}
xml2::write_xml(d, file = filename)
invisible(d)
}
================================================
FILE: R/codecov.R
================================================
#' Run covr on a package and upload the result to codecov.io
#' @param coverage an existing coverage object to submit, if `NULL`,
#' [package_coverage()] will be called with the arguments from
#' `...`
#' @param ... arguments passed to [package_coverage()]
#' @param base_url Codecov url (change for Enterprise)
#' @param quiet if `FALSE`, print the coverage before submission.
#' @param token a codecov upload token, if `NULL` then following external
#' sources will be checked in this order:
#' 1. the environment variable \sQuote{CODECOV_TOKEN}. If it is empty, then
#' 1. package will look at directory of the package for a file `codecov.yml`.
#' File must have `codecov` section where field `token` is set to a token that
#' will be used.
#' @param commit explicitly set the commit this coverage result object
#' corresponds to. Is looked up from the service or locally if it is
#' `NULL`.
#' @param branch explicitly set the branch this coverage result object
#' corresponds to, this is looked up from the service or locally if it is
#' `NULL`.
#' @param pr explicitly set the pr this coverage result object corresponds to,
#' this is looked up from the service if it is `NULL`.
#' @param flags A flag to use for this coverage upload see
#' <https://docs.codecov.com/docs/flags> for details.
#' @export
#' @examples
#' \dontrun{
#' codecov(path = "test")
#' }
codecov <- function(...,
coverage = NULL,
base_url = "https://codecov.io",
token = NULL,
commit = NULL,
branch = NULL,
pr = NULL,
flags = NULL,
quiet = TRUE) {
if (is.null(coverage)) {
coverage <- package_coverage(quiet = quiet, ...)
}
if (!quiet) {
print(coverage)
}
# -------
# Jenkins
# -------
if (Sys.getenv("JENKINS_URL") != "") {
# https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project
codecov_url <- paste0(base_url, "/upload/v2") # nolint
codecov_query <- list(service = "jenkins",
branch = branch %||% Sys.getenv("GIT_BRANCH"),
commit = commit %||% Sys.getenv("GIT_COMMIT"),
build = Sys.getenv("BUILD_NUMBER"),
build_url = utils::URLencode(Sys.getenv("BUILD_URL")))
# ---------
# Travis CI
# ---------
} else if (Sys.getenv("CI") == "true" && Sys.getenv("TRAVIS") == "true") {
# https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables
codecov_url <- paste0(base_url, "/upload/v2") # nolint
codecov_query <- list(branch = branch %||% Sys.getenv("TRAVIS_BRANCH"),
service = "travis",
build = Sys.getenv("TRAVIS_JOB_NUMBER"),
pr = pr %||% Sys.getenv("TRAVIS_PULL_REQUEST"),
job = Sys.getenv("TRAVIS_JOB_ID"),
slug = Sys.getenv("TRAVIS_REPO_SLUG"),
root = Sys.getenv("TRAVIS_BUILD_DIR"),
commit = commit %||% Sys.getenv("TRAVIS_COMMIT"))
# --------
# Codeship
# --------
} else if (Sys.getenv("CI") == "true" && Sys.getenv("CI_NAME") == "codeship") {
# https://www.codeship.io/documentation/continuous-integration/set-environment-variables/
codecov_url <- paste0(base_url, "/upload/v2") # nolint
codecov_query <- list(service = "codeship",
branch = branch %||% Sys.getenv("CI_BRANCH"),
build = Sys.getenv("CI_BUILD_NUMBER"),
build_url = utils::URLencode(Sys.getenv("CI_BUILD_URL")),
commit = commit %||% Sys.getenv("CI_COMMIT_ID"))
# ---------
# Circle CI
# ---------
} else if (Sys.getenv("CI") == "true" && Sys.getenv("CIRCLECI") == "true") {
# https://circleci.com/docs/environment-variables
codecov_url <- paste0(base_url, "/upload/v2") # nolint
codecov_query <- list(service = "circleci",
branch = branch %||% Sys.getenv("CIRCLE_BRANCH"),
build = Sys.getenv("CIRCLE_BUILD_NUM"),
owner = Sys.getenv("CIRCLE_PROJECT_USERNAME"),
repo = Sys.getenv("CIRCLE_PROJECT_REPONAME"),
pr = pr %||% Sys.getenv("CIRCLE_PR_NUMBER"),
commit = commit %||% Sys.getenv("CIRCLE_SHA1"))
# ---------
# Semaphore
# ---------
} else if (Sys.getenv("CI") == "true" && Sys.getenv("SEMAPHORE") == "true") {
# https://semaphoreapp.com/docs/available-environment-variables.html
codecov_url <- paste0(base_url, "/upload/v2") # nolint
slug_info <- strsplit(Sys.getenv("SEMAPHORE_REPO_SLUG"), "/")[[1]]
codecov_query <- list(service = "semaphore",
branch = branch %||% Sys.getenv("BRANCH_NAME"),
build = Sys.getenv("SEMAPHORE_BUILD_NUMBER"),
owner = slug_info[1],
repo = slug_info[2],
commit = commit %||% Sys.getenv("REVISION"))
# --------
# drone.io
# --------
} else if (Sys.getenv("CI") == "true" && Sys.getenv("DRONE") == "true") {
# http://docs.drone.io/env.html
codecov_url <- paste0(base_url, "/upload/v2") # nolint
codecov_query <- list(service = "drone.io",
branch = branch %||% Sys.getenv("DRONE_BRANCH"),
build = Sys.getenv("DRONE_BUILD_NUMBER"),
build_url = utils::URLencode(Sys.getenv("DRONE_BUILD_URL")),
pr = pr %||% Sys.getenv("DRONE_PULL_REQUEST"),
commit = commit %||% Sys.getenv("DRONE_COMMIT"))
# --------
# AppVeyor
# --------
} else if (Sys.getenv("CI") == "True" && Sys.getenv("APPVEYOR") == "True") {
# http://www.appveyor.com/docs/environment-variables
codecov_url <- paste0(base_url, "/upload/v2") # nolint
codecov_query <- list(service = "appveyor",
branch = branch %||% Sys.getenv("APPVEYOR_REPO_BRANCH"),
job = paste(Sys.getenv("APPVEYOR_ACCOUNT_NAME"), Sys.getenv("APPVEYOR_PROJECT_SLUG"), Sys.getenv("APPVEYOR_BUILD_VERSION"), sep = "/"),
build = Sys.getenv("APPVEYOR_JOB_ID"),
pr = pr %||% Sys.getenv("APPVEYOR_PULL_REQUEST_NUMBER"),
slug = Sys.getenv("APPVEYOR_REPO_NAME"),
commit = commit %||% Sys.getenv("APPVEYOR_REPO_COMMIT"))
# -------
# Wercker
# -------
} else if (Sys.getenv("CI") == "true" && Sys.getenv("WERCKER_GIT_BRANCH") != "") {
# http://devcenter.wercker.com/articles/steps/variables.html
codecov_url <- paste0(base_url, "/upload/v2") # nolint
codecov_query <- list(service = "wercker",
branch = branch %||% Sys.getenv("WERCKER_GIT_BRANCH"),
build = Sys.getenv("WERCKER_MAIN_PIPELINE_STARTED"),
owner = Sys.getenv("WERCKER_GIT_OWNER"),
repo = Sys.getenv("WERCKER_GIT_REPOSITORY"),
commit = commit %||% Sys.getenv("WERCKER_GIT_COMMIT"))
# ---------
# GitLab-CI
# ---------
} else if (Sys.getenv("CI") == "true" && Sys.getenv("CI_SERVER_NAME") == "GitLab CI") {
# http://docs.gitlab.com/ce/ci/variables/README.html
slug <- sub(".*/([^/]+/[^/]+)[.]git", "\\1", Sys.getenv("CI_BUILD_REPO"))
codecov_url <- paste0(base_url, "/upload/v2") # nolint
codecov_query <- list(service = "gitlab",
branch = branch %||% Sys.getenv("CI_BUILD_REF_NAME"),
build = Sys.getenv("CI_BUILD_ID"),
slug = slug,
commit = commit %||% Sys.getenv("CI_BUILD_REF"))
# ---------
# GitHub Actions
# ---------
} else if (nzchar(Sys.getenv("GITHUB_ACTION"))) {
# Adapted from
# https://github.com/codecov/codecov-bash/blob/3316b21c8fe0ca7ada543fb8473ac616822ce27a/codecov#L763-L783
slug <- Sys.getenv("GITHUB_REPOSITORY")
github_ref <- Sys.getenv("GITHUB_REF")
github_head_ref <- Sys.getenv("GITHUB_HEAD_REF")
github_run_id <- Sys.getenv("GITHUB_RUN_ID")
is_fork_pr <- nzchar(github_head_ref)
if (is_fork_pr) {
pr <- pr %||% sub("^refs/pull/(.*)/merge", "\\1", github_ref)
branch <- branch %||% github_head_ref
} else {
branch <- branch %||% sub("^refs/heads/", "", github_ref)
}
codecov_url <- paste0(base_url, "/upload/v2") # nolint
codecov_query <- list(service = "github-actions",
branch = branch,
build = github_run_id,
build_url = utils::URLencode(sprintf("https://github.com/%s/actions/runs/%s", slug, github_run_id)),
pr = pr,
slug = slug,
commit = commit %||% Sys.getenv("GITHUB_SHA"))
# ---------
# Google Cloud Build
# ---------
} else if (nzchar(Sys.getenv("GCB_PROJECT_ID"))) {
# https://cloud.google.com/build/docs/configuring-builds/substitute-variable-values
codecov_url <- paste0(base_url, "/upload/v2") # nolint
build_url <- sprintf("https://console.cloud.google.com/cloud-build/builds/%s?project=%s",
Sys.getenv("GCB_BUILD_ID"), Sys.getenv("GCB_PROJECT_ID"))
name <- NULL
pr <- NULL
if(nzchar(Sys.getenv("GCB_TAG_NAME"))) name <- Sys.getenv("GCB_TAG_NAME")
if(nzchar(Sys.getenv("GCB_PR_NUMBER"))) pr <- Sys.getenv("GCB_PR_NUMBER")
codecov_query <- list(
branch = branch %||% Sys.getenv("GCB_BRANCH_NAME"),
service = "custom",
build = Sys.getenv("GCB_BUILD_ID"),
build_url = build_url,
name = name,
pr = pr,
commit = commit %||% Sys.getenv("GCB_COMMIT_SHA")
)
# ---------
# Local GIT
# ---------
} else {
codecov_url <- paste0(base_url, "/upload/v2") # nolint
codecov_query <- list(branch = branch %||% local_branch(),
commit = commit %||% current_commit())
}
# Add flags parameter
codecov_query$flags <- flags
token <- token %||% Sys.getenv("CODECOV_TOKEN", extract_from_yaml(attr(coverage, "package")$path))
if (nzchar(token)) {
codecov_query$token <- token
}
coverage_json <- to_codecov(coverage)
content(RETRY("POST",
url = codecov_url,
query = codecov_query,
body = coverage_json,
encode = "json",
httr::config(http_version = curl_http_1_1())))
}
curl_http_1_1 <- function() {
symbols <- curl::curl_symbols()
symbols$value[symbols$name == "CURL_HTTP_VERSION_1_1"]
}
extract_from_yaml <- function(path){
if (is.null(path)) {
return("")
}
path_to_yaml <- file.path(path, "codecov.yml")
if (file.exists(path_to_yaml)) {
yaml::read_yaml(path_to_yaml)[["codecov"]][["token"]] %||% ""
} else {
""
}
}
to_codecov <- function(x) {
coverages <- lapply(per_line(x),
function(xx) {
xx$coverage <- c(NA, xx$coverage)
xx
})
res <- Map(function(coverage, name) {
list(
"name" = jsonlite::unbox(name),
"coverage" = coverage$coverage
)
}, coverages, names(coverages), USE.NAMES = FALSE)
jsonlite::toJSON(na = "null", list("files" = res, "uploader" = jsonlite::unbox("R")))
}
================================================
FILE: R/compiled.R
================================================
# this does not handle LCOV_EXCL_START ect.
parse_gcov <- function(file, package_path = "") {
if (!file.exists(file)) {
return(NULL)
}
lines <- readLines(file)
source_file <- rex::re_matches(lines[1], rex::rex("Source:", capture(name = "source", anything)))$source
# retrieve full path to the source files
source_file <- normalize_path(source_file)
# If the source file does not start with the package path or does not exist ignore it.
if (!file.exists(source_file) || !grepl(rex::rex(start, rex::regex(paste0(rex::escape(package_path), collapse = "|"))), source_file)) {
return(NULL)
}
re <- rex::rex(any_spaces,
capture(name = "coverage", some_of(digit, "-", "#", "=")),
":", any_spaces,
capture(name = "line", digits),
":"
)
matches <- rex::re_matches(lines, re)
# Exclude lines with no match to the pattern
lines <- lines[!is.na(matches$coverage)]
matches <- na.omit(matches)
# gcov lines which have no coverage
matches$coverage[matches$coverage == "#####"] <- 0 # nolint
# gcov lines which have parse error, so make untracked
matches$coverage[matches$coverage == "====="] <- "-"
coverage_lines <- matches$line != "0" & matches$coverage != "-"
matches <- matches[coverage_lines, ]
values <- as.numeric(matches$coverage)
if (any(is.na(values))) {
stop("values could not be coerced to numeric ", matches$coverage)
}
# There are no functions for gcov, so we set everything to NA
functions <- rep(NA_character_, length(values))
line_coverages(source_file, matches, values, functions)
}
# for mocking
readLines <- NULL
file.exists <- NULL
clean_gcov <- function(path) {
src_dir <- file.path(path, "src")
gcov_files <- list.files(src_dir,
pattern = rex::rex(or(".gcda", ".gcno", ".gcov"), end),
full.names = TRUE,
recursive = TRUE)
unlink(gcov_files)
}
run_gcov <- function(path, quiet = TRUE, clean = TRUE,
gcov_path = getOption("covr.gcov", ""),
gcov_args = getOption("covr.gcov_args", NULL)) {
src_path <- normalize_path(file.path(path, "src"))
if (!file.exists(src_path)) {
return()
}
withr::local_dir(src_path)
gcov_inputs <- list.files(".", pattern = rex::rex(".gcno", end), recursive = TRUE, full.names = TRUE)
if (!nzchar(gcov_path)) {
if (length(gcov_inputs)) stop('gcov not found')
return()
}
run_gcov_one <- function(src) {
system_check(gcov_path,
args = c(gcov_args, src, "-p", "-o", dirname(src)),
quiet = quiet, echo = !quiet)
gcov_outputs <- list.files(".", pattern = rex::rex(".gcov", end), recursive = TRUE, full.names = TRUE)
if (!quiet) {
message("gcov output for ", src, ":")
message(paste(gcov_outputs, collapse = "\n"))
}
if (clean) {
on.exit(unlink(gcov_outputs))
} else {
gcov_output_base <- file.path("..", "covr", src)
gcov_output_targets <- sub(".", gcov_output_base, gcov_outputs)
if (!quiet) {
message("gcov output targets for ", src, ":")
message(paste(gcov_output_targets, collapse = "\n"))
}
lapply(
unique(dirname(gcov_output_targets)),
function(.x) dir.create(.x, recursive = TRUE, showWarnings = FALSE)
)
on.exit({
if (!quiet) {
message("Moving gcov outputs to covr directory.\n")
}
file.rename(gcov_outputs, gcov_output_targets)
})
}
unlist(lapply(gcov_outputs, parse_gcov, package_path = c(path, getOption("covr.gcov_additional_paths", NULL))), recursive = FALSE)
}
res <- compact(unlist(lapply(gcov_inputs, run_gcov_one), recursive = FALSE))
if (!length(res) && length(gcov_inputs))
warning('parsed gcov output was empty')
res
}
line_coverages <- function(source_file, matches, values, functions) {
# create srcfile reference from the source file
src_file <- srcfilecopy(source_file, readLines(source_file))
line_lengths <- vapply(src_file$lines[as.numeric(matches$line)], nchar, numeric(1))
res <- Map(function(line, length, value, func) {
src_ref <- srcref(src_file, c(line, 1, line, length))
res <- list(srcref = src_ref, value = value, functions = func)
class(res) <- "line_coverage"
res
},
matches$line, line_lengths, values, functions)
if (!length(res)) {
return(NULL)
}
names(res) <- lapply(res, function(x) key(x$srcref))
class(res) <- "line_coverages"
res
}
================================================
FILE: R/coveralls.R
================================================
#' Run covr on a package and upload the result to coveralls
#' @param coverage an existing coverage object to submit, if `NULL`,
#' [package_coverage()] will be called with the arguments from
#' `...`
#' @param ... arguments passed to [package_coverage()]
#' @param repo_token The secret repo token for your repository,
#' found at the bottom of your repository's page on Coveralls. This is useful
#' if your job is running on a service Coveralls doesn't support out-of-the-box.
#' If set to NULL, it is assumed that the job is running on travis-ci
#' @param service_name the CI service to use, if environment variable
#' \sQuote{CI_NAME} is set that is used, otherwise \sQuote{travis-ci} is used.
#' @param quiet if `FALSE`, print the coverage before submission.
#' @export
coveralls <- function(..., coverage = NULL,
repo_token = Sys.getenv("COVERALLS_TOKEN"),
service_name = Sys.getenv("CI_NAME", "travis-ci"),
quiet = TRUE) {
if (is.null(coverage)) {
coverage <- package_coverage(..., quiet = quiet)
}
if (!quiet) {
print(coverage)
}
service <- tolower(service_name)
coveralls_url <- "https://coveralls.io/api/v1/jobs"
coverage_json <- to_coveralls(coverage,
repo_token = repo_token, service_name = service)
result <- RETRY("POST", url = coveralls_url,
body = list(json_file = upload_file(to_file(coverage_json))))
content <- content(result)
if (isTRUE(content$error)) {
stop("Failed to upload coverage data. Reply by Coveralls: ", content$message)
}
content
}
to_file <- function(x) {
name <- temp_file()
con <- file(name)
writeChar(con = con, x, eos = NULL)
close(con)
name
}
to_coveralls <- function(x, service_job_id = Sys.getenv("TRAVIS_JOB_ID"),
service_name, repo_token = "") {
coverages <- per_line(x)
res <- Map(function(coverage, name) {
source_code <- paste(collapse = "\n", coverage$file$file_lines)
list(
"name" = jsonlite::unbox(name),
"source" = jsonlite::unbox(source_code),
"source_digest" = jsonlite::unbox(digest::digest(source_code, algo = "md5", serialize = FALSE)),
"coverage" = coverage$coverage)
}, coverages, names(coverages), USE.NAMES = FALSE)
git_info <- switch(service_name,
drone = jenkins_git_info(), # drone has the same env vars as jenkins
jenkins = jenkins_git_info(),
'travis-pro' = jenkins_git_info(),
list(NULL)
)
payload <- if (!nzchar(repo_token)) {
list(
"service_job_id" = jsonlite::unbox(service_job_id),
"service_name" = jsonlite::unbox(service_name),
"source_files" = res)
} else {
tmp <- list(
"repo_token" = jsonlite::unbox(repo_token),
"service_name" = jsonlite::unbox(service_name),
"source_files" = res)
tmp$git <- git_info
tmp
}
jsonlite::toJSON(na = "null", payload)
}
jenkins_git_info <- function() {
# check https://coveralls.zendesk.com/hc/en-us/articles/201350799-API-Reference
# for why and how we are doing this
formats <- c(
id = "%H",
author_name = "%an",
author_email = "%ae",
commiter_name = "%cn",
commiter_email = "%ce",
message = "%s"
)
head <- lapply(structure(
scan(
sep = "\n",
what = "character",
text = system_output("git", c("log", "-n", "1",
paste0("--pretty=format:", paste(collapse = "%n", formats)))
),
quiet = TRUE
),
names = names(formats)
), jsonlite::unbox)
remotes <- list(list(
name = jsonlite::unbox("origin"),
url = jsonlite::unbox(Sys.getenv("CI_REMOTE"))
))
c(list(branch = jsonlite::unbox(Sys.getenv("CI_BRANCH"))),
head = list(head),
remotes = list(remotes))
}
================================================
FILE: R/covr.R
================================================
#' covr: Test coverage for packages
#'
#' 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 code.
#'
#' A coverage report can be used to inspect coverage for each line in your
#' package. Using `report()` requires the additional dependencies `DT` and `htmltools`.
#'
#' ```r
#' # If run with no arguments `report()` implicitly calls `package_coverage()`
#' report()
#' ```
#'
#' @section Package options:
#'
#' `covr` uses the following [options()] to configure behaviour:
#'
#' \itemize{
#' \item `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.
#'
#' \item `covr.exclude_end`: Used along with `covr.exclude_start`, an optional
#' regular expression which ends a line-exclusion region. For more
#' details, see `?exclusions`.
#'
#' \item `covr.exclude_pattern`: An optional line-exclusion pattern. Lines
#' which match the pattern will be excluded from coverage. For more details,
#' see `?exclusions`.
#'
#' \item `covr.exclude_start`: Used along with `covr.exclude_end`, an optional
#' regular expression which starts a line-exclusion region. For more
#' details, see `?exclusions`.
#'
#' \item `covr.filter_non_package`: If `TRUE` (the default behavior), coverage
#' of files outside the target package are filtered from coverage output.
#'
#' \item `covr.fix_parallel_mcexit`:
#'
#' \item `covr.flags`:
#'
#' \item `covr.gcov`: 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.
#'
#' \item `covr.gcov_additional_paths`:
#'
#' \item `covr.gcov_args`:
#'
#' \item `covr.icov`:
#'
#' \item `covr.icov_args`:
#'
#' \item `covr.icov_flags`:
#'
#' \item `covr.icov_prof`:
#'
#' \item `covr.rstudio_source_markers`: A logical value. If `TRUE` (the
#' default behavior), source markers are displayed within the RStudio IDE
#' when using `zero_coverage`.
#'
#' \item `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`.
#'
#' \item `covr.showCfunctions`:
#' }
#'
#'
"_PACKAGE"
#' @import methods
#' @importFrom stats aggregate na.omit na.pass setNames
#' @importFrom utils capture.output getSrcFilename relist str head
#' @importFrom httr content RETRY upload_file
NULL
the <- new.env(parent = emptyenv())
the$replacements <- list()
trace_environment <- function(env) {
clear_counters()
the$replacements <- compact(c(
replacements_S4(env),
replacements_RC(env),
replacements_R6(env),
replacements_S7(env),
replacements_box(env),
lapply(ls(env, all.names = TRUE), replacement, env = env)))
lapply(the$replacements, replace)
}
reset_traces <- function() {
lapply(the$replacements, reset)
}
save_trace <- function(directory) {
tmp_file <- temp_file("covr_trace_", tmpdir = directory)
saveRDS(.counters, file = tmp_file)
}
#' Convert a counters object to a coverage object
#'
#' @param counters An environment of covr trace results to convert to a coverage
#' object. If `counters` is not provided, the `covr` namespace value
#' `.counters` is used.
#' @param ... Additional attributes to include with the coverage object.
#'
as_coverage <- function(counters = NULL, ...) {
if (missing(counters))
counters <- .counters
counters <- as.list(counters)
counters <- as_coverage_with_tests(counters)
structure(counters, ..., class = "coverage")
}
#' Clean and restructure counter tests for a 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)
#'
#' @inheritParams as_coverage
#'
as_coverage_with_tests <- function(counters) {
clean_coverage_tests(counters)
# unnest environment-wrapped $tests$tally as more accessible $tests
for (i in seq_along(counters)) {
if (!is.environment(counters[[i]]$tests)) next
counters[[i]]$tests <- counters[[i]]$tests$tally
}
tests <- counters$tests
counters$tests <- NULL
structure(counters, tests = tests, class = "coverage")
}
#' Calculate test coverage for a specific function.
#'
#' @param fun name of the function.
#' @param code expressions to run.
#' @param env environment the function is defined in.
#' @param enc the enclosing environment which to run the expressions.
#' @examples
#' add <- function(x, y) { x + y }
#' function_coverage(fun = add, code = NULL) # 0% coverage
#' function_coverage(fun = add, code = add(1, 2) == 3) # 100% coverage
#' @export
function_coverage <- function(fun, code = NULL, env = NULL, enc = parent.frame()) {
if (is.function(fun)) {
env <- environment(fun)
# get name of function, stripping preceding blah:: if needed
fun <- rex::re_substitutes(deparse(substitute(fun)), rex::regex(".*:::?"), "")
}
clear_counters()
replacement <- if (!is.null(env)) {
replacement(fun, env)
} else {
replacement(fun)
}
on.exit({
reset(replacement)
clear_counters()
})
replace(replacement)
withr::with_envvar(c("R_COVR" = "true"),
eval(code, enc)
)
as_coverage(as.list(.counters))
}
#' Calculate test coverage for sets of files
#'
#' 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 sourced.
#' @param source_files Character vector of source files with function
#' definitions to measure coverage
#' @param test_files Character vector of test files with code to test the
#' functions
#' @param parent_env The parent environment to use when sourcing the files.
#' @inheritParams package_coverage
#' @examples
#' # 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"))
#' @export
file_coverage <- function(
source_files,
test_files,
line_exclusions = NULL,
function_exclusions = NULL,
parent_env = parent.frame()) {
env <- new.env(parent = parent_env)
withr::with_options(c("keep.parse.data.pkgs" = TRUE), {
lapply(source_files,
sys.source, keep.source = TRUE, envir = env)
})
trace_environment(env)
on.exit({
reset_traces()
clear_counters()
})
withr::with_envvar(c("R_COVR" = "true"),
lapply(test_files,
sys.source, keep.source = TRUE, envir = env)
)
coverage <- as_coverage(.counters)
exclude(coverage,
line_exclusions = line_exclusions,
function_exclusions = function_exclusions,
path = NULL)
}
#' Calculate coverage of code directly
#'
#' 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.
#' @param source_code A character vector of source code
#' @param test_code A character vector of test code
#' @inheritParams file_coverage
#' @param ... Additional arguments passed to [file_coverage()]
#' @examples
#' source <- "add <- function(x, y) { x + y }"
#' test <- "add(1, 2) == 3"
#' code_coverage(source, test)
#' @export
code_coverage <- function(
source_code,
test_code,
line_exclusions = NULL,
function_exclusions = NULL,
...) {
src <- tempfile("source.R")
test <- tempfile("test.R")
on.exit(file.remove(src, test))
cat(source_code, file = src)
cat(test_code, file = test)
file_coverage(src, test, line_exclusions = line_exclusions,
function_exclusions = function_exclusions, ...)
}
#' Calculate coverage of an environment
#'
#' @param env The environment to be instrumented.
#' @inheritParams file_coverage
#' @export
environment_coverage <- function(
env = parent.frame(),
test_files,
line_exclusions = NULL,
function_exclusions = NULL) {
exec_env <- new.env(parent = env)
trace_environment(env)
on.exit({
reset_traces()
clear_counters()
})
withr::with_envvar(c("R_COVR" = "true"),
lapply(test_files,
sys.source, keep.source = TRUE, envir = exec_env)
)
coverage <- as_coverage(.counters)
exclude(coverage,
line_exclusions = line_exclusions,
function_exclusions = function_exclusions,
path = NULL)
}
#' Calculate test coverage for a package
#'
#' 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.
#'
#' @details
#' 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.
#'
#' #ifdef unix
#' Parallelized code using \pkg{parallel}'s [mcparallel()] needs to
#' use a patched `parallel:::mcexit`. This is done automatically if the
#' package depends on \pkg{parallel}, but can also be explicitly set using the
#' environment variable `COVR_FIX_PARALLEL_MCEXIT` or the global option
#' `covr.fix_parallel_mcexit`.
#' #endif
#'
#' @param path file path to the package.
#' @param type run the package \sQuote{tests}, \sQuote{vignettes},
#' \sQuote{examples}, \sQuote{all}, or \sQuote{none}. The default is
#' \sQuote{tests}.
#' @param combine_types If `TRUE` (the default) the coverage for all types
#' is simply summed into one coverage object. If `FALSE` separate objects
#' are used for each type of coverage.
#' @param 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.
#' @param quiet whether to load and compile the package quietly, useful for
#' debugging errors.
#' @param clean whether to clean temporary output files after running, mainly
#' useful for debugging errors.
#' @param line_exclusions a named list of files with the lines to exclude from
#' each file.
#' @param function_exclusions a vector of regular expressions matching function
#' names to exclude. Example `print\\\.` to match print methods.
#' @param code A character vector of additional test code to run.
#' @param ... Additional arguments passed to [tools::testInstalledPackage()].
#' @param exclusions \sQuote{Deprecated}, please use \sQuote{line_exclusions} instead.
#' @param pre_clean whether to delete all objects present in the src directory before recompiling
#' @param 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 `clean =
#' FALSE`) to help debug test failures.
#' @seealso [exclusions()] For details on excluding parts of the
#' package from the coverage calculations.
#' @export
package_coverage <- function(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"),
...,
exclusions, pre_clean = TRUE) {
if (!missing(exclusions)) {
warning(
"`exclusions` is deprecated and will be removed in an upcoming release. Please use `line_exclusions` instead.",
call. = FALSE, domain = NA
)
line_exclusions <- exclusions
}
pkg <- as_package(path)
if (missing(type)) {
type <- "tests"
}
type <- parse_type(type)
run_separately <- !isTRUE(combine_types) && length(type) > 1
if (run_separately) {
# store the args that were called
called_args <- as.list(match.call())[-1]
# remove the type
called_args$type <- NULL
res <- list()
for (t in type) {
res[[t]] <- do.call(Recall, c(called_args, type = t))
attr(res[[t]], "type") <- t
}
attr(res, "package") <- pkg
class(res) <- "coverages"
return(res)
}
if (is.character(relative_path)) {
stopifnot(length(relative_path) == 1)
root <- normalize_path(relative_path)
} else if (isTRUE(relative_path)) {
root <- pkg$path
} else {
root <- NULL
}
# tools::testInstalledPackage requires normalized install_path (#517)
install_path <- normalize_path(install_path)
dir.create(install_path)
# check for compiler
if (!uses_icc()) {
flags <- getOption("covr.flags")
} else if (length(getOption("covr.icov")) > 0L) {
flags <- getOption("covr.icov_flags")
# clean up old icov files
unlink(file.path(pkg$path, "src", "*.dyn"))
unlink(file.path(pkg$path, "src", "pgopti.*"))
} else {
stop("icc is not available")
}
if (isTRUE(clean)) {
on.exit({
clean_objects(pkg$path)
clean_gcov(pkg$path)
clean_parse_data()
unlink(install_path, recursive = TRUE)
}, add = TRUE)
}
# clean any dlls prior to trying to install
if (isTRUE(pre_clean)) clean_objects(pkg$path)
# install the package in a temporary directory
withr::with_envvar(
list(R_LIBS = paste(.libPaths(), collapse = .Platform$path.sep)),
withr::with_makevars(flags, assignment = "+=", {
args <- c(
"--vanilla", "CMD", "INSTALL",
"-l", shQuote(install_path),
"--example",
"--install-tests",
"--with-keep.source",
"--with-keep.parse.data",
"--no-staged-install",
"--no-multiarch",
shQuote(pkg$path)
)
name <- if (.Platform$OS.type == "windows") "R.exe" else "R"
path <- file.path(R.home("bin"), name)
res <- system2(
path,
args,
stdout = if (quiet) NULL else "",
stderr = if (quiet) NULL else ""
)
})
)
if (res != 0) {
stop("Package installation did not succeed.")
}
# add hooks to the package startup
add_hooks(pkg$package, install_path,
fix_mcexit = should_enable_parallel_mcexit_fix(pkg))
libs <- env_path(install_path, .libPaths())
# We need to set the libpaths in the current R session for examples with
# install or runtime Sexpr blocks, which may implicitly load the package in
# the current R session.
withr::with_libpaths(install_path, action = "prefix", {
withr::with_envvar(
c(R_DEFAULT_PACKAGES = "datasets,utils,grDevices,graphics,stats,methods",
R_LIBS = libs,
R_LIBS_USER = libs,
R_LIBS_SITE = libs,
R_COVR = "true",
R_TESTS = file.path(R.home("share"), "R", "tests-startup.R")), {
withCallingHandlers({
if ("vignettes" %in% type) {
type <- type[type != "vignettes"]
run_vignettes(pkg, install_path)
}
out_dir <- file.path(install_path, pkg$package)
if ("examples" %in% type) {
type <- type[type != "examples"]
# testInstalledPackage explicitly sets R_LIBS="" on windows, and does
# not restore it after, so we need to reset it ourselves.
withr::with_envvar(c(R_LIBS = Sys.getenv("R_LIBS")), {
result <- tools::testInstalledPackage(pkg$package, outDir = out_dir, types = "examples", lib.loc = install_path, ...)
if (result != 0L) {
show_failures(out_dir)
}
})
}
if ("tests" %in% type) {
result <- tools::testInstalledPackage(pkg$package, outDir = out_dir, types = "tests", lib.loc = install_path, ...)
if (result != 0L) {
show_failures(out_dir)
}
}
# We always run the commands file (even if empty) to load the package and
# initialize all the counters to 0.
run_commands(pkg, install_path, code)
},
message = function(e) if (quiet) invokeRestart("muffleMessage") else e,
warning = function(e) if (quiet) invokeRestart("muffleWarning") else e)
})
})
# read tracing files
trace_files <- list.files(path = install_path, pattern = "^covr_trace_[^/]+$", full.names = TRUE)
coverage <- merge_coverage(trace_files)
if (!uses_icc()) {
res <- run_gcov(pkg$path, quiet = quiet, clean = clean)
} else {
res <- run_icov(pkg$path, quiet = quiet)
}
coverage <- as_coverage(
c(coverage, res),
package = pkg,
root = root
)
if (!clean) {
attr(coverage, "library") <- install_path
}
if (getOption("covr.filter_non_package", TRUE)) {
coverage <- filter_non_package_files(coverage)
}
# Exclude generated files from Rcpp and cpp11 to avoid redundant coverage information
line_exclusions <- c(
"src/RcppExports.cpp",
"R/RcppExports.R",
"src/cpp11.cpp",
"R/cpp11.R",
line_exclusions,
withr::with_dir(root, parse_covr_ignore())
)
exclude(coverage,
line_exclusions = line_exclusions,
function_exclusions = function_exclusions,
path = root)
}
#' Convert a coverage dataset to a list
#'
#' @param x a coverage dataset, defaults to running `package_coverage()`.
#' @return A list containing coverage result for each individual file and the whole package
#' @export
coverage_to_list <- function(x = package_coverage()){
covr_df <- tally_coverage(x)
file_result <- tapply(covr_df$value, covr_df$filename,
FUN = function(x) round(sum(x > 0) / length(x) * 100, digits = 2))
total_result <- round(sum(covr_df$value > 0) / nrow(covr_df) * 100, digits = 2)
return(list(filecoverage = file_result, totalcoverage = total_result))
}
show_failures <- function(dir) {
fail_files <- list.files(dir, pattern = "fail$", recursive = TRUE, full.names = TRUE)
for (file in fail_files) {
lines <- readLines(file)
# Skip header lines (until first >)
lines <- lines[seq(which.min(grepl("^>", lines)), length(lines))]
# R will only show options("warning.length") number of characters in an
# error, so show the last characters of that number
error_header <- paste0("Failure in `", file, "`\n")
# 9 is the length of `Error: ` + newline + NUL maybe?
error_length <- getOption("warning.length") - 9
error_body <- paste(lines, collapse = "\n")
header_len <- nchar(error_header, "bytes")
body_len <- nchar(error_body, "bytes")
error_body <- substr(error_body, body_len - (error_length - header_len), body_len)
cnd <- structure(list(message = paste0(error_header, error_body)), class = c("covr_error", "error", "condition"))
stop(cnd)
}
}
# merge multiple coverage files together. Assumes the order of coverage lines
# is the same in each object, this should always be the case if the objects are
# from the same initial library.
merge_coverage <- function(x) {
UseMethod("merge_coverage")
}
#' @export
merge_coverage.character <- function(x) {
coverage_objs <- lapply(x, function(f) {
as.list(suppressWarnings(readRDS(f)))
})
merge_coverage(coverage_objs)
}
#' @export
merge_coverage.list <- function(x) {
coverage_objs <- x
if (length(coverage_objs) == 0) {
return()
}
x <- coverage_objs[[1]]
names <- names(x)
clean_coverage_tests(x) # x[[key]]$tests environments modified in-place
for (y in tail(coverage_objs, -1L)) {
# only affects coverage produced with options(covr.record_tests = TRUE)
clean_coverage_tests(y)
x <- merge_coverage_tests(from = y, into = x)
for (name in intersect(names, names(y))) {
if (name == "tests") next
x[[name]]$value <- x[[name]]$value + y[[name]]$value
}
for (name in setdiff(names(y), names)) {
x[[name]] <- y[[name]]
}
names <- union(names, names(y))
}
x
}
# Strip allocated, but unused test records from coverage test matrix
#
# The tally of tests that hit each trace is held in a pre-allocated matrix
# which may be padded with unused rows. Start by stripping unused rows:
#
# If tests were not recorded (that is, if `options(covr.record_tests)` was not
# `TRUE` when the coverage was calculated, this function will have no effect.
#
# @param obj A coverage counter environment, within which a $tests$tally matrix
# may have been allocated, but not entirely populated.
#
clean_coverage_tests <- function(obj) {
counter_has_tests_tally <- function(counter) !is.null(counter$tests)
if (is.na(Position(counter_has_tests_tally, obj))) return()
for (i in seq_along(obj)) {
val <- obj[[i]]$value
if (is.null(val) || is.na(val)) next
n <- nrow(obj[[i]]$tests$tally)
if (is.null(n) || is.na(n) || n < val) next
obj[[i]]$tests$tally <- obj[[i]]$tests$tally[seq_len(val),,drop = FALSE]
}
}
# Merge recorded tests from one coverage object into another. Because coverage
# objects are environments, these environments will be modified by-reference as
# a side-effect of calling this function.
#
# If tests were not recorded (that is, if `options(covr.record_tests)` was not
# `TRUE` when the coverage was calculated, this function will have no effect.
#
# @param from A coverage counter environment whose tests should be merged into
# \code{into}
# @param into A coverage counter environment to add tests into
#
merge_coverage_tests <- function(from, into = NULL) {
if (is.null(from$tests)) return(into)
# TODO: The x[[name]]$tests$tally matrices are re-allocated with each rbind of
# additional test hits as each object is merged. This could be avoided by
# first calculating the total rows needed to store all the merged tests and
# then allocating a matrix of the appropriate size from the start. In most
# cases, this amounts to neglegable overhead but is an opportunity for
# improvement.
# align tests from coverage objects
test_idx <- match(names(from$tests), Filter(nchar, names(into$tests)))
new_test_idx <- if (!length(test_idx)) seq_along(from$tests) else which(is.na(test_idx))
test_idx[new_test_idx] <- length(into$tests) + seq_along(new_test_idx)
# append any tests that we haven't encountered in previous objects
into$tests <- append(into$tests, from$tests[new_test_idx])
from$tests <- NULL
# modify trace test tallies
for (name in intersect(names(into), names(from))) {
if (name == "tests") next
from[[name]]$tests$tally[, 1L] <- test_idx[from[[name]]$tests$tally[, 1L]]
into[[name]]$tests$tally <- rbind(into[[name]]$tests$tally, from[[name]]$tests$tally)
}
into
}
parse_type <- function(type) {
type <- match_arg(type, choices = c("tests", "vignettes", "examples", "all", "none"), several.ok = TRUE)
if (type %==% "all") {
type <- c("tests", "vignettes", "examples")
}
if (length(type) > 1L) {
if ("all" %in% type) {
stop(sQuote("all"), " must be the only type specified", call. = FALSE)
}
if ("none" %in% type) {
stop(sQuote("none"), " must be the only type specified", call. = FALSE)
}
}
type
}
# Run vignettes for a package. This is done in a new process as otherwise the
# finalizer is not called to dump the results. The namespace is first
# explicitly loaded to ensure output even if no vignettes exist.
# @param pkg Package object (from as_package) to run
# @param lib the library path to look in
run_vignettes <- function(pkg, lib) {
outfile <- file.path(lib, paste0(pkg$package, "-Vignette.Rout"))
failfile <- paste(outfile, "fail", sep = "." )
cat("tools::buildVignettes(dir = '", pkg$path, "')\n", file = outfile, sep = "")
cmd <- paste(shQuote(file.path(R.home("bin"), "R")),
"CMD BATCH --vanilla --no-timing",
shQuote(outfile), shQuote(failfile))
res <- system(cmd)
if (res != 0) {
show_failures(dirname(failfile))
} else {
file.rename(failfile, outfile)
}
}
run_commands <- function(pkg, lib, commands) {
outfile <- file.path(lib, paste0(pkg$package, "-commands.Rout"))
failfile <- paste(outfile, "fail", sep = "." )
writeLines(c(
paste0("library('", pkg$package, "')"),
commands), con = outfile)
cmd <- paste(shQuote(file.path(R.home("bin"), "R")),
"CMD BATCH --vanilla --no-timing",
shQuote(outfile), shQuote(failfile))
res <- system(cmd)
if (res != 0L) {
show_failures(dirname(failfile))
} else {
file.rename(failfile, outfile)
}
}
# Add hooks to the installed package
# Installed packages have lazy loading code to setup the lazy load database at
# pkg_name/R/pkg_name. This function adds a user level onLoad Hook to the
# package which calls `covr::trace_environment`, so the package environment is
# traced when the package is loaded.
# It also adds a finalizer that saves the tracing information to the package
# namespace environment which is run when the ns is garbage collected or the
# process ends. This ensures the tracing count information will be written
# regardless of how the process terminates.
# @param pkg_name name of the package to add hooks to
# @param lib the library path to look in
# @param fix_mcexit whether to add the fix for mcparallel:::mcexit
add_hooks <- function(pkg_name, lib, fix_mcexit = FALSE,
record_tests = isTRUE(getOption("covr.record_tests", FALSE))) {
trace_dir <- paste0("Sys.getenv(\"COVERAGE_DIR\", \"", lib, "\")")
load_script <- file.path(lib, pkg_name, "R", pkg_name)
lines <- readLines(load_script)
lines <- append(lines,
c(paste0("setHook(packageEvent(pkg, \"onLoad\"), function(...) options(covr.record_tests = ", record_tests, "))"),
"setHook(packageEvent(pkg, \"onLoad\"), function(...) covr:::trace_environment(ns))",
paste0("reg.finalizer(ns, function(...) { covr:::save_trace(", trace_dir, ") }, onexit = TRUE)")),
length(lines) - 1L)
if (fix_mcexit) {
lines <- append(lines, sprintf("covr:::fix_mcexit('%s')", trace_dir))
}
writeLines(text = lines, con = load_script)
}
#' @export
`[.coverage` <- function(x, ...) {
structure(NextMethod(), class = "coverage")
}
#' Determine if code is being run in covr
#'
#' 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.
#' @export
#' @examples
#' if (require(testthat)) {
#' testthat::skip_if(in_covr())
#' }
in_covr <- function() {
identical(Sys.getenv("R_COVR"), "true")
}
================================================
FILE: R/data_frame.R
================================================
#' @export
as.data.frame.coverage <- function(x, row.names = NULL, optional = FALSE, sort = TRUE, ...) {
column_names <- c("filename", "functions", "first_line", "first_byte", "last_line", "last_byte",
"first_column", "last_column", "first_parsed",
"last_parsed", "value")
res <- setNames(c(list(character(0)), rep(list(numeric(0)), times = length(column_names) - 1)),
column_names)
if (length(x)) {
res$filename <- display_name(x)
res$functions <- vcapply(x, function(xx) xx$functions[1])
vals <- t(vapply(x,
function(xx) c(xx$srcref, xx$value),
numeric(9), USE.NAMES = FALSE))
for (i in seq_len(NCOL(vals))) {
res[[i + 2]] <- vals[, i]
}
}
df <- data.frame(res, stringsAsFactors = FALSE, check.names = FALSE)
if (sort) {
# if we are sorting we no longer need to preserve the order of the input and can merge values together
df <- merge_values(df)
df <- df[order(df$filename, df$first_line, df$first_byte, df$last_line, df$last_byte), ]
}
rownames(df) <- NULL
df
}
merge_values <- function(x, sentinel = "___NA___") {
if (NROW(x) == 0) {
return(x)
}
# We can't use aggregate directly, because it doesn't allow missing values in
# grouping variables...
x$functions[is.na(x$functions)] <- sentinel
res <- aggregate(value ~ ., x, sum)
res$functions[res$functions == sentinel] <- NA_character_
res
}
================================================
FILE: R/display_name.R
================================================
#' Retrieve the path name (filename) for each coverage object
#'
#' @param x A coverage object
#' @keywords internal
#' @export
display_name <- function(x) {
stopifnot(inherits(x, "coverage"))
if (length(x) == 0) {
return()
}
filenames <- vcapply(x, function(x) get_source_filename(x$srcref, full.names = TRUE))
to_relative_path(filenames, attr(x, "root"))
}
to_relative_path <- function(path, base) {
if (is.null(base)) {
return(path)
}
rex::re_substitutes(path, rex::rex(base, "/"), "")
}
filter_non_package_files <- function(x) {
filenames <- vcapply(x, function(x) get_source_filename(x$srcref, full.names = TRUE))
x[rex::re_matches(filenames, rex::rex(attr(x, "package")$path, "/"), "")]
}
================================================
FILE: R/exclusions.R
================================================
#' Exclusions
#'
#' covr supports a couple of different ways of excluding some or all of a file.
#'
#' @section Line Exclusions:
#'
#' 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.
#'
#' @section Function Exclusions:
#'
#' 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.
#'
#' @section Exclusion Comments:
#'
#' 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 the `exclude_pattern`, `exclude_start`,
#' `exclude_end` arguments to `package_coverage()` or by setting the global
#' options `covr.exclude_pattern`, `covr.exclude_start`, `covr.exclude_end`.
#' @examples
#' \dontrun{
#' # exclude whole file of R/test.R
#' package_coverage(exclusions = "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
#' }
#' @name exclusions
NULL
exclude <- function(coverage,
line_exclusions = NULL,
function_exclusions = NULL,
exclude_pattern = getOption("covr.exclude_pattern"),
exclude_start = getOption("covr.exclude_start"),
exclude_end = getOption("covr.exclude_end"),
path = NULL) {
sources <- traced_files(coverage)
source_exclusions <- lapply(sources,
function(x) {
parse_exclusions(x$file_lines, exclude_pattern, exclude_start, exclude_end)
})
excl <- normalize_exclusions(c(source_exclusions, line_exclusions), path)
df <- as.data.frame(coverage, sort = FALSE)
to_exclude <- rep(FALSE, length(coverage))
if (!is.null(function_exclusions)) {
to_exclude <- Reduce(`|`, init = to_exclude,
Map(rex::re_matches, function_exclusions, MoreArgs = list(data = df$functions)))
to_exclude[is.na(to_exclude)] <- FALSE
}
df$full_name <- vcapply(coverage,
function(x) {
normalize_path(get_source_filename(x$srcref, full.names = TRUE))
})
to_exclude <- to_exclude | vlapply(seq_len(NROW(df)),
function(i) {
file <- df[i, "full_name"]
which_exclusion <- match(file, names(excl))
!is.na(which_exclusion) &&
(
identical(excl[[which_exclusion]], Inf) ||
all(seq(df[i, "first_line"], df[i, "last_line"]) %in% excl[[file]])
)
})
if (any(to_exclude)) {
coverage <- coverage[!to_exclude]
}
coverage
}
parse_exclusions <- function(lines,
exclude_pattern = getOption("covr.exclude"),
exclude_start = getOption("covr.exclude_start"),
exclude_end = getOption("covr.exclude_end")) {
exclusions <- numeric(0)
starts <- which(rex::re_matches(lines, exclude_start))
ends <- which(rex::re_matches(lines, exclude_end))
if (length(starts) > 0) {
if (length(starts) != length(ends)) {
starts_msg <- sprintf(
ngettext(length(starts), "%d range start (%s)", "%d range starts (%s)"),
length(starts), toString(starts)
)
ends_msg <- sprintf(
ngettext(length(ends), "%d range end (%s)", "%d range ends (%s)"),
length(ends), toString(ends)
)
stop(starts_msg, " but only ", ends_msg, " for exclusion from code coverage!")
}
for (i in seq_along(starts)) {
exclusions <- c(exclusions, seq(starts[i], ends[i]))
}
}
exclusions <- c(exclusions, which(rex::re_matches(lines, exclude_pattern)))
sort(unique(exclusions))
}
file_exclusions <- function(x, path = NULL) {
excl <- normalize_exclusions(x, path)
full_files <- vlapply(excl, function(x1) length(x1) == 1 && x1 == Inf)
if (any(full_files)) {
names(excl)[full_files]
} else {
NULL
}
}
normalize_exclusions <- function(x, path = NULL) {
if (is.null(x) || length(x) <= 0) {
return(list())
}
# no named parameters at all
if (is.null(names(x))) {
x <- structure(relist(rep(Inf, length(x)), x), names = x)
} else {
unnamed <- names(x) == ""
if (any(unnamed)) {
# must be character vectors of length 1
bad <- vlapply(seq_along(x),
function(i) {
unnamed[i] & (!is.character(x[[i]]) | length(x[[i]]) != 1)
})
if (any(bad)) {
stop("Full file exclusions must be character vectors of length 1. items: ",
paste(collapse = ", ", which(bad)),
" are not!",
call. = FALSE)
}
names(x)[unnamed] <- x[unnamed]
x[unnamed] <- Inf
}
}
if (!is.null(path)) {
names(x) <- file.path(path, names(x))
}
names(x) <- normalize_path(names(x))
remove_line_duplicates(
remove_file_duplicates(
compact(x)
)
)
}
remove_file_duplicates <- function(x) {
unique_names <- unique(names(x))
## check for duplicate files
if (length(unique_names) < length(names(x))) {
x <- lapply(unique_names,
function(name) {
vals <- unname(unlist(x[names(x) == name]))
if (any(vals == Inf)) {
Inf
} else {
vals
}
})
names(x) <- unique_names
}
x
}
remove_line_duplicates <- function(x) {
x[] <- lapply(x, unique)
x
}
parse_covr_ignore <- function(file = getOption("covr.covrignore", Sys.getenv("COVR_COVRIGNORE", ".covrignore"))) {
if (!file.exists(file)) {
return(NULL)
}
lines <- readLines(file)
paths <- Sys.glob(lines, dirmark = TRUE)
files <- unlist(
lapply(paths, function(x) {
if (dir.exists(x)) {
list.files(recursive = TRUE, all.files = TRUE, path = x, full.names = TRUE)
} else {
x
}
}))
}
================================================
FILE: R/gitlab.R
================================================
#' Run covr on package and create report for GitLab
#'
#' Utilize internal GitLab static pages to publish package coverage.
#' Creates local covr report in a package subdirectory.
#' Uses the [pages](https://docs.gitlab.com/user/project/pages/)
#' GitLab job to publish the report.
#' @inheritParams codecov
#' @inheritParams report
#' @export
gitlab <- function(..., coverage = NULL, file = "public/coverage.html", quiet = TRUE) {
if (is.null(coverage)) {
coverage <- package_coverage(quiet = quiet, ...)
}
if (!quiet) {
print(coverage)
}
out_file <- file.path(tempfile(), file)
on.exit(unlink(out_dir, recursive = TRUE), add = TRUE)
out_dir <- dirname(out_file)
pkg_path <- attributes(coverage)$package$path
report(coverage, file = out_file, browse = FALSE)
file.copy(out_dir, pkg_path, recursive = TRUE)
}
================================================
FILE: R/icc.R
================================================
parse_icov <- function(lines, package_path = "") {
source_file <- trim_ws(lines[1L])
# If the source file does not start with the package path ignore it.
if (!grepl(rex::rex(start, package_path), source_file)) {
return(NULL)
}
# remove source file lines and empty/white space lines
lines <- trim_ws(lines[-1L])
lines <- lines[lines != ""]
# get line, values, and functions
r1 <- rex::re_matches(lines, rex::rex("function - ",
capture(name = "source", anything),
"\t", digits, "\t", digits, "\t", digits, anything))$source
idx1 <- which(!is.na(r1))
re <- rex::rex(
capture(name = "instance", digits), "\t",
capture(name = "line", digits), "\t",
capture(name = "idcol", digits), "\t", any_spaces,
capture(name = "coverage", digits))
show_C_functions <- getOption("covr.showCfunctions", FALSE)
if (length(idx1) == 0L) {
m1 <- rex::re_matches(lines, re)
m1$functions <- rep(NA_character_, nrow(m1))
} else {
m1 <- rex::re_matches(lines[-idx1], re)
if (isTRUE(show_C_functions)) {
# get function names
if (length(idx1) == 1L) {
m1$functions <- rep(r1[idx1], nrow(m1))
} else {
stopifnot(idx1[1L] == 1L)
nums <- c(idx1[2L:length(idx1)]-1L, length(r1)) - idx1
stopifnot(sum(nums) == nrow(m1))
m1$functions <- unlist(mapply(rep, r1[idx1], nums,
SIMPLIFY=FALSE, USE.NAMES=FALSE))
}
}
}
# remove invalid rows if exists
m1 <- na.omit(m1)
m1$line <- as.numeric(m1$line)
m1$coverage <- as.numeric(m1$coverage)
if (is.null(m1$functions)) {
m2 <- aggregate(m1$coverage, by = list(line=m1$line), sum)
names(m2) <- c("line", "coverage")
m2$functions <- NA_character_
} else {
m2 <- aggregate(m1$coverage,
by = list(line=m1$line, functions=m1$functions), sum)
names(m2) <- c("line", "functions", "coverage")
}
matches <- m2[order(m2$line), ]
values <- as.numeric(matches$coverage > 0L)
functions <- matches$functions
line_coverages(source_file, matches, values, functions)
}
run_icov <- function(path, quiet = TRUE,
icov_path = getOption("covr.icov", ""),
icov_args = getOption("covr.icov_args", NULL)) {
src_path <- normalize_path(file.path(path, "src"))
if (!file.exists(src_path)) {
return()
}
if (!nzchar(icov_path)) {
warning("icc codecov not available")
return()
}
icov_profmerge <- getOption("covr.icov_prof", "")
if (!nzchar(icov_profmerge)) {
warning("icc profmerge not available")
return()
}
icov_inputs <- list.files(path, pattern = rex::rex(".dyn", end),
recursive = TRUE, full.names = TRUE)
if (length(icov_inputs) == 0L) {
warning("no icc .dyn files are generated")
return()
}
system_check(icov_profmerge,
args = c("-prof_dir", src_path),
quiet = quiet, echo = !quiet)
withr::with_dir(src_path, {
system_check(icov_path,
args = c("-prj", "tmp", "-spi", file.path(src_path, "pgopti.spi"),
"-dpi", file.path(src_path, "pgopti.dpi"),
"-include-nonexec",
"-txtbcvrg", "bcovg.log"),
quiet = quiet, echo = !quiet)
})
lines <- readLines(file.path(src_path, "bcovg.log"))
# generate line coverage
re <- rex::re_matches(lines, rex::rex("Covered Functions in File: \"",
capture(name = "source", anything), "\""))$source
idx1 <- which(!is.na(re))
idx2 <- c(idx1[2:length(idx1)]-1, length(re))
srcfilenms <- re[idx1]
lines[idx1] <- srcfilenms
icov_outputs <- lapply(seq_along(idx1), function(i) lines[idx1[i]:idx2[i]])
structure(
unlist(recursive = FALSE,
lapply(icov_outputs, parse_icov, package_path = path)),
class = "coverage")
}
# check if icc is used
uses_icc <- function() {
compiler <- tryCatch(
{
system2(file.path(R.home("bin"), "R"),
args = c("--vanilla", "CMD", "config", "CC"),
stdout = TRUE)
},
warning = function(e) NA_character_)
isTRUE(any(grepl("\\bicc\\b", compiler)))
}
================================================
FILE: R/parallel.R
================================================
# utility function to replace a symbol in a locked loaded package/namespace
replace_binding <- function(package, name, value) {
ns <- getNamespace(package)
unlock <- get('unlockBinding') # to fool r CMD check
lock <- get('lockBinding')
unlock(name, ns)
assign(name, value, ns)
lock(name, ns)
}
# patch parallel:::mcexit to force it to save the covr trace on exit
fix_mcexit <- function(trace_dir) {
get_from_ns <- `:::` # trick to fool R CMD check
mcexit <- get_from_ns('parallel', 'mcexit')
trace_dir <- parse(text = trace_dir)[[1]]
# directly patch mcexit
body(mcexit) <- as.call(append(after = 1, as.list(body(mcexit)),
as.call(list(call(":::", as.symbol("covr"), as.symbol("save_trace")), trace_dir))))
replace_binding('parallel', 'mcexit', mcexit)
}
uses_parallel <- function(pkg) {
any(grepl("\\bparallel\\b",
pkg[c("depends", "imports", "suggests", "enhances", "linkingto")]))
}
on_windows <- function() {
"windows" %in% tolower(Sys.info()[["sysname"]])
}
# consider in that order: the environment variable COVR_FIX_PARALLEL_MCEXIT,
# the option covr.fix_parallel_mcexit, or auto-detection of the usage of
# parallel by the package (cf uses_parallel()).
should_enable_parallel_mcexit_fix <- function(pkg) {
isTRUE(!on_windows() &&
as.logical(Sys.getenv("COVR_FIX_PARALLEL_MCEXIT",
getOption("covr.fix_parallel_mcexit",
uses_parallel(pkg)))))
}
================================================
FILE: R/parse_data.R
================================================
#' @importFrom utils getParseData getSrcref tail
impute_srcref <- function(x, parent_ref) {
if (!is_conditional_or_loop(x)) return(NULL)
if (is.null(parent_ref)) return(NULL)
pd <- get_tokens(parent_ref)
pd_expr <-
(
(pd$line1 == parent_ref[[1L]] & pd$line2 == parent_ref[[3L]]) |
(pd$line1 == parent_ref[[7L]] & pd$line2 == parent_ref[[8L]])
) &
pd$col1 == parent_ref[[2L]] &
pd$col2 == parent_ref[[4L]] &
pd$token == "expr"
pd_expr_idx <- which(pd_expr)
if (length(pd_expr_idx) == 0L) return(NULL) # srcref not found in parse data
if (length(pd_expr_idx) > 1) pd_expr_idx <- pd_expr_idx[[1]]
expr_id <- pd$id[pd_expr_idx]
pd_child <- pd[pd$parent == expr_id, ]
pd_child <- pd_child[order(pd_child$line1, pd_child$col1), ]
# exclude comments
pd_child <- pd_child[pd_child$token != "COMMENT", ]
if (pd$line1[pd_expr_idx] == parent_ref[[7L]] & pd$line2[pd_expr_idx] == parent_ref[[8L]]) {
line_offset <- parent_ref[[7L]] - parent_ref[[1L]]
} else {
line_offset <- 0
}
make_srcref <- function(from, to = from) {
if (length(from) == 0) {
return(NULL)
}
srcref(
attr(parent_ref, "srcfile"),
c(pd_child$line1[from] - line_offset,
pd_child$col1[from],
pd_child$line2[to] - line_offset,
pd_child$col2[to],
pd_child$col1[from],
pd_child$col2[to],
pd_child$line1[from],
pd_child$line2[to]
)
)
}
switch(
as.character(x[[1L]]),
"if" = {
src_ref <- list(
NULL,
make_srcref(3),
make_srcref(5),
make_srcref(7)
)
# the fourth component isn't used for an "if" without "else"
src_ref[seq_along(x)]
},
"for" = {
list(
NULL,
NULL,
make_srcref(2),
make_srcref(3)
)
},
"while" = {
list(
NULL,
make_srcref(3),
make_srcref(5)
)
},
"switch" = {
exprs <- tail(which(pd_child$token == "expr"), n = -1)
# Add NULLs for drop through conditions
token <- pd_child$token
next_token <- c(tail(token, n = -1), NA_character_)
drops <- which(token == "EQ_SUB" & next_token != "expr")
exprs <- sort(c(exprs, drops))
ignore_drop_through <- function(x) {
if (x %in% drops) {
return(NULL)
}
x
}
exprs <- lapply(exprs, ignore_drop_through)
# Don't create srcrefs for ... conditions
ignore_dots <- function(x) {
if (identical("...", pd$text[pd$parent == pd_child$id[x]])) {
return(NULL)
}
x
}
exprs <- lapply(exprs, ignore_dots)
c(list(NULL), lapply(exprs, make_srcref))
},
NULL
)
}
is_conditional_or_loop <- function(x) is.symbol(x[[1L]]) && as.character(x[[1L]]) %in% c("if", "for", "else", "switch")
package_parse_data <- new.env()
get_parse_data <- function(srcfile) {
if (length(package_parse_data) == 0) {
lines <- getSrcLines(srcfile, 1L, Inf)
lines_split <- split_on_line_directives(lines)
if (!length(lines_split)) {
return(NULL)
}
res <- lapply(lines_split,
function(x) getParseData(parse(text = x, keep.source = TRUE), includeText = TRUE))
for (i in seq_along(res)) {
package_parse_data[[names(res)[[i]]]] <- res[[i]]
}
}
package_parse_data[[srcfile[["filename"]]]]
}
clean_parse_data <- function() {
rm(list = ls(package_parse_data), envir = package_parse_data)
}
get_tokens <- function(srcref) {
# Before R 4.4.0, covr's custom get_parse_data is necessary because
# utils::getParseData returns parse data for only the last file in the
# package. That issue (bug#16756) is fixed in R 4.4.0 (r84538).
#
# On R 4.4.0, continue to use get_parse_data because covr's code expects the
# result to be limited to the srcref file. getParseData will return parse data
# for all of the package's files.
get_parse_data(attr(getSrcref(srcref), "srcfile")) %||%
# This covers the non-installed file case where the source file isn't a
# concatenated file with "line N" directives.
getParseData(srcref)
}
================================================
FILE: R/replace.R
================================================
#' @useDynLib covr, .registration = TRUE
replacement <- function(name, env = as.environment(-1), target_value = get(name, envir = env)) {
if (is.function(target_value) && !is.primitive(target_value)) {
if (is_vectorized(target_value)) {
new_value <- target_value
environment(new_value)$FUN <- trace_calls(environment(new_value)$FUN, name)
} else if (is.function(target_value) && inherits(target_value, "memoised")) {
new_value <- target_value
environment(new_value)$`_f` <- trace_calls(environment(new_value)$`_f`, name)
} else {
new_value <- trace_calls(target_value, name)
attributes(body(new_value)) <- attributes(body(target_value))
}
attributes(new_value) <- attributes(target_value)
if (isS4(target_value)) {
new_value <- asS4(new_value)
}
list(
env = env,
name = as.name(name),
orig_value = .Call(covr_duplicate_, target_value),
target_value = target_value,
new_value = new_value
)
}
}
replace <- function(replacement) {
.Call(covr_reassign_function, replacement$target_value, replacement$new_value)
}
reset <- function(replacement) {
.Call(covr_reassign_function, replacement$target_value, replacement$orig_value)
}
================================================
FILE: R/report.R
================================================
#' Display covr results using a standalone report
#'
#' @param x a coverage dataset, defaults to running `package_coverage()`.
#' @param file The report filename.
#' @param browse whether to open a browser to view the report.
#' @examples
#' \dontrun{
#' x <- package_coverage()
#' report(x)
#' }
#' @export
# This function was originally a shiny application, but has now been converted into
# a normal static document and no longer depends on shiny.
report <- function(x = package_coverage(),
file = file.path(tempdir(), paste0(get_package_name(x), "-report.html")),
browse = interactive()) {
# Create any directories as needed
dir.create(dirname(file), recursive = TRUE, showWarnings = FALSE)
# Paths need to be absolute for save_html to work properly
file <- file.path(normalizePath(dirname(file), mustWork = TRUE), basename(file))
if (!(requireNamespace("htmltools", quietly = TRUE) && requireNamespace("DT", quietly = TRUE))) {
stop("The `DT` and `htmltools` packages must be installed to use `covr::report()`", call. = FALSE)
}
data <- to_report_data(x)
# Color the td cells by coverage amount, like codecov.io does
color_coverage_callback <- DT::JS(
'function(td, cellData, rowData, row, col) {
var percent = cellData.replace("%", "");
if (percent > 90) {
var grad = "linear-gradient(90deg, #edfde7 " + cellData + ", white " + cellData + ")";
} else if (percent > 75) {
var grad = "linear-gradient(90deg, #f9ffe5 " + cellData + ", white " + cellData + ")";
} else {
var grad = "linear-gradient(90deg, #fcece9 " + cellData + ", white " + cellData + ")";
}
$(td).css("background", grad);
}
')
# Open a new file in the source tab and switch to it
file_choice_callback <- DT::JS(
"table.on('click.dt', 'a', function() {
files = $('div#files div');
files.not('div.hidden').addClass('hidden');
id = $(this).text();
files.filter('div[id=\\'' + id + '\\']').removeClass('hidden');
$('ul.nav a[data-value=Source]').text(id).tab('show');
});")
package_name <- attr(x, "package")$package
percentage <- sprintf("%02.2f%%", data$overall)
table <- DT::datatable(
data$file_stats,
escape = FALSE,
fillContainer = FALSE,
options = list(
searching = FALSE,
dom = "t",
paging = FALSE,
columnDefs = list(
list(targets = 6, createdCell = color_coverage_callback))),
rownames = FALSE,
class = "row-border",
callback = file_choice_callback
)
table$sizingPolicy$defaultWidth <- "100%"
table$sizingPolicy$defaultHeight <- NULL
ui <- fluid_page(
htmltools::includeCSS(system.file("www/report.css", package = "covr")),
column(8, offset = 2, size = "md",
htmltools::HTML(paste0("<h2>", package_name, " coverage - ", percentage, "</h2>")),
tabset_panel(
tab_panel("Files",
table
),
tab_panel("Source", addHighlight(renderSourceTable(data$full)))
)
)
)
htmltools::save_html(ui, file)
viewer <- getOption("viewer", utils::browseURL)
if (browse) {
viewer(file)
}
invisible(file)
}
#' A coverage report for a specific file
#'
#' @inheritParams report
#' @param file The file to report on, if `NULL`, use the first file in the
#' coverage output.
#' @param out_file The output file
#' @export
file_report <- function(x = package_coverage(), file = NULL, out_file = file.path(tempdir(), paste0(get_package_name(x), "-file-report.html")), browse = interactive()) {
loadNamespace("htmltools")
loadNamespace("DT")
files <- display_name(x)
if (is.null(file)) {
file <- files[[1]]
}
stopifnot(length(file) == 1)
x <- x[files %in% file]
data <- to_report_data(x)
percentage <- data$file_stats$Coverage
ui <- fluid_page(
htmltools::includeCSS(system.file("www/report.css", package = "covr")),
column(8, offset = 2, size = "md",
htmltools::HTML(paste0("<h2>", file, " - ", percentage, "</h2>")),
addHighlight(
renderSourceTable(data$full, "")
)
)
)
htmltools::save_html(ui, out_file)
viewer <- getOption("viewer", utils::browseURL)
if (browse) {
viewer(out_file)
}
invisible(out_file)
}
to_report_data <- function(x) {
coverages <- per_line(x)
res <- list()
res$overall <- percent_coverage(x)
res$full <- lapply(coverages,
function(coverage) {
lines <- coverage$file$file_lines
values <- coverage$coverage
values[is.na(values)] <- ""
data.frame(
line = seq_along(lines),
source = lines,
coverage = values,
stringsAsFactors = FALSE)
})
nms <- names(coverages)
# set a temp name if it doesn't exist
nms[nms == ""] <- "<text>"
names(res$full) <- nms
res$file_stats <- compute_file_stats(res$full)
res$file_stats$File <- add_link(names(res$full))
res$file_stats <- sort_file_stats(res$file_stats)
res$file_stats$Coverage <- res$file_stats$Coverage
res
}
compute_file_stats <- function(files) {
do.call("rbind",
lapply(files,
function(file) {
data.frame(
Coverage = sprintf("%.2f%%", sum(file$coverage > 0) / sum(file$coverage != "") * 100),
Lines = NROW(file),
Relevant = sum(file$coverage != ""),
Covered = sum(file$coverage > 0),
Missed = sum(file$coverage == 0),
`Hits / Line` = sprintf("%.0f", sum(as.numeric(file$coverage), na.rm = TRUE) / sum(file$coverage != "")),
stringsAsFactors = FALSE,
check.names = FALSE)
}
)
)
}
sort_file_stats <- function(stats) {
stats[order(as.numeric(sub("%", "", stats$Coverage)), -stats$Relevant),
c("File", "Lines", "Relevant", "Covered", "Missed", "Hits / Line", "Coverage")]
}
add_link <- function(files) {
vcapply(files, function(file) { as.character(htmltools::a(href = "#", file)) })
}
renderSourceTable <- function(data, class = "hidden") {
htmltools::div(id = "files",
Map(function(lines, file) {
htmltools::div(id = file, class=class,
htmltools::tags$table(class = "table-condensed",
htmltools::tags$tbody(
lapply(seq_len(NROW(lines)),
function(row_num) {
coverage <- lines[row_num, "coverage"]
cov_type <- NULL
if (coverage == 0) {
cov_value <- "!"
cov_type <- "missed"
} else if (coverage > 0) {
cov_value <- htmltools::HTML(paste0(lines[row_num, "coverage"], "<em>x</em>", collapse = ""))
cov_type <- "covered"
} else {
cov_type <- "never"
cov_value <- ""
}
htmltools::tags$tr(class = cov_type,
htmltools::tags$td(class = "num", lines[row_num, "line"]),
htmltools::tags$td(class = "coverage", cov_value),
htmltools::tags$td(class = "col-sm-12", htmltools::pre(class = "language-r", lines[row_num, "source"]))
)
})
)
))
}, lines = data, file = names(data)),
htmltools::tags$script(
"$('div#files pre').each(function(i, block) {
hljs.highlightBlock(block);
});"))
}
addHighlight <- function(x = list()) {
highlight <- htmltools::htmlDependency("highlight.js", "6.2",
system.file(package = "covr",
"www/shared/highlight.js"),
script = "highlight.pack.js",
stylesheet = "rstudio.css")
htmltools::attachDependencies(x, c(htmltools::htmlDependencies(x), list(highlight)))
}
addin_report <- function() {
loadNamespace("rstudioapi")
project <- rstudioapi::getActiveProject()
covr::report(covr::package_coverage(project %||% getwd()))
}
# These are all adapted from functions in shiny
column <- function(width, ..., offset = 0, size = c("xs", "sm", "md", "lg")) {
size <- match.arg(size)
col_class <- paste0("col-", size, "-", width)
if (offset > 0) {
col_class <- paste0(col_class, " ", "col-", size, "-offset-", offset)
}
htmltools::div(class = col_class, ...)
}
tab_panel <- function(title, ..., value = title) {
htmltools::div(class = "tab-pane", title = title, `data-value` = value, ...)
}
fluid_page <- function(...) {
bootstrap_page(
htmltools::div(class = "container-fluid", ...)
)
}
bootstrap_page <- function(...) {
htmltools::attachDependencies(htmltools::tagList(list(...)), html_dependency_bootstrap())
}
# from htmldeps::html_dependency_bootstrap (not yet on CRAN)
html_dependency_bootstrap <- function () {
htmltools::htmlDependency(name = "bootstrap", version = "3.3.5",
src = system.file(file = "www/shared/bootstrap", package = "covr"),
meta = list(viewport = "width=device-width, initial-scale=1"),
script = c("js/bootstrap.min.js", "shim/html5shiv.min.js", "shim/respond.min.js"),
stylesheet = c("css/bootstrap.min.css", "css/bootstrap-theme.min.css")
)
}
tabset_panel <- function(...) {
tabset <- build_tabset(list(...))
htmltools::div(class = "tabbable",
tabset$nav_list,
tabset$content)
}
build_tabset <- function(tabs) {
tabset_id <- "covr"
tabs <- lapply(seq_len(length(tabs)), build_tab_item, tabs = tabs, tabset_id = tabset_id)
list(nav_list = ul(class = "nav nav-tabs", `data-tabsetid` = tabset_id, lapply(tabs, "[[", 1)),
content = htmltools::div(class = "tab-content", `data-tabsetid` = tabset_id, lapply(tabs, "[[", 2))
)
}
build_tab_item <- function(i, tabs, tabset_id) {
div_tag <- tabs[[i]]
tab_id <- paste("tab", tabset_id, i, sep = "-")
li_tag <- li(
htmltools::a(href = paste0("#", tab_id),
`data-toggle` = "tab",
`data-value` = div_tag$attribs$`data-value`,
div_tag$attribs$title
)
)
if (i == 1) {
li_tag$attribs$class <- "active"
div_tag$attribs$class <- paste(div_tag$attribs$class, "active")
}
div_tag$attribs$id <- tab_id
list(li_tag = li_tag, div_tag = div_tag)
}
li <- function(...) htmltools::tag("li", list(...))
ul <- function(...) htmltools::tag("ul", list(...))
================================================
FILE: R/sonarqube.R
================================================
#' Create a SonarQube Generic XML file for test coverage according to
#' https://docs.sonarqube.org/latest/analysis/generic-test/
#' Based on cobertura.R
#'
#' This functionality requires the xml2 package be installed.
#' @param cov the coverage object returned from [package_coverage()]
#' @param filename the name of the SonarQube Generic XML file
#' @author Talkdesk Inc.
#' @export
to_sonarqube <- function(cov, filename = "sonarqube.xml"){
loadNamespace("xml2")
df <- tally_coverage(cov, by = "line")
d <- xml2::xml_new_document()
top <- xml2::xml_add_child(d, "coverage", version = "1")
files <- unique(df$filename)
for (f in files){
file <- xml2::xml_add_child(top, "file", path = paste(attr(cov, "package")$package, "/", as.character(f), sep=""))
for (fun_name in unique(na.omit(df[df$filename == f, "functions"]))) {
fun_lines <- which(df$functions == fun_name & df$filename == f)
for (i in fun_lines){
line <- df[i, ]
xml2::xml_add_child(file, "lineToCover", lineNumber = as.character(line$line),
covered = tolower(as.character(line$value>0)))
}
}
}
xml2::write_xml(d, file = filename)
invisible(d)
}
================================================
FILE: R/summary_functions.R
================================================
#' Provide percent coverage of package
#'
#' Calculate the total percent coverage from a coverage result object.
#' @param x the coverage object returned from [package_coverage()]
#' @param ... additional arguments passed to [tally_coverage()]
#' @return The total percentage as a `numeric(1)`.
#' @export
percent_coverage <- function(x, ...) {
res <- tally_coverage(x, ...)
(sum(res$value > 0) / length(res$value)) * 100
}
#' Tally coverage by line or expression
#'
#' @inheritParams percent_coverage
#' @param by whether to tally coverage by line or expression
#' @return a `data.frame` of coverage tallied by line or expression.
#' @export
tally_coverage <- function(x, by = c("line", "expression")) {
# Rarely something goes wrong with the source references and we get all NAs
# for them, so we omit them here
df <- as.data.frame(x)
all_na_rows <- rowSums(is.na(df)) == ncol(df)
df <- df[!all_na_rows, ]
if (NROW(df) == 0) {
return(df)
}
by <- match.arg(by)
switch(by,
"line" = {
# if it already has a line column it has already been tallied.
if (!is.null(df$line)) {
return(df)
}
# aggregate() can't cope with zero-length data frames anyway.
if (nrow(df) == 0L) {
return(NULL)
}
# results with NA functions (such as from compiled code) are dropped
# unless NA is a level.
df$functions <- addNA(df$functions)
res <- expand_lines(df)
res <- aggregate(value ~ filename + functions + line,
data = res, FUN = min, na.action = na.pass)
res$functions <- as.character(res$functions)
# exclude blank lines from results
if (inherits(x, "coverage")) {
srcfiles <- unique(lapply(x, function(x) attr(x$srcref, "srcfile")))
srcfile_names <- vcapply(srcfiles, `[[`, "filename")
srcfile_names <- to_relative_path(srcfile_names, attr(x, "root"))
blank_lines <- compact(
setNames(lapply(srcfiles, function(srcfile) attr(srcfile_lines(srcfile), "blanks")),
srcfile_names))
if (length(blank_lines)) {
blank_lines <- utils::stack(blank_lines)
non_blanks <- setdiff.data.frame(
res,
blank_lines,
by.x = c("filename", "line"),
by.y = c("ind", "values"))
res <- res[non_blanks, ]
}
res
}
res[order(res$filename, res$line), ]
},
"expression" = df
)
}
#' Provide locations of zero coverage
#'
#' When examining the test coverage of a package, it is useful to know if there are
#' any locations where there is **0** test coverage.
#'
#' @param x a coverage object returned [package_coverage()]
#' @param ... additional arguments passed to
#' [tally_coverage()]
#' @return A `data.frame` with coverage data where the coverage is 0.
#' @details if used within RStudio this function outputs the results using the
#' Marker API.
#' @export
zero_coverage <- function(x, ...) {
coverage_data <- tally_coverage(x, ...)
coverage_data <- coverage_data[coverage_data$value == 0, , drop = FALSE]
res <- coverage_data[
# need to use %in% rather than explicit indexing because
# tally_coverage returns a df without the columns if
# by is equal to "line"
colnames(coverage_data) %in%
c("filename",
"functions",
"line",
"first_line",
"last_line",
"first_column",
"last_column",
"value")]
if (getOption("covr.rstudio_source_markers", TRUE) &&
rstudioapi::hasFun("sourceMarkers")) {
markers <- markers(coverage_data)
rstudioapi::callFun("sourceMarkers",
name = "covr",
markers = markers,
basePath = attr(x, "package")$path,
autoSelect = "first")
invisible(res)
} else {
res
}
}
#' Print a coverage object
#'
#' @param x the coverage object to be printed
#' @param group whether to group coverage by filename or function
#' @param by whether to count coverage by line or expression
#' @param ... additional arguments ignored
#' @return The coverage object (invisibly).
#' @export
print.coverage <- function(x, group = c("filename", "functions"), by = "line", ...) {
if (length(x) == 0) {
return()
}
group <- match.arg(group)
type <- attr(x, "type")
if (is.null(type) || type == "none") {
type <- NULL
}
df <- tally_coverage(x, by = by)
if (!NROW(df)) {
return(invisible())
}
percents <- tapply(df$value, df[[group]], FUN = function(x) (sum(x > 0) / length(x)) * 100)
overall_percentage <- percent_coverage(df, by = by)
msg <- cli::format_message(paste0(
cli::style_bold(
"{attr(x, 'package')$package} {to_title(type)} Coverage: "
),
format_percentage(overall_percentage)
))
message(msg)
by_coverage <- percents[order(percents,
names(percents))]
for (i in seq_along(by_coverage)) {
msg <- cli::format_message(
paste0(
cli::style_bold(names(by_coverage)[i], ": "),
format_percentage(by_coverage[i])
)
)
message(msg)
}
invisible(x)
}
#' @export
print.coverages <- function(x, ...) {
for (i in seq_along(x)) {
# Add a blank line between consecutive coverage items
if (i != 1) {
message()
}
print(x[[i]], ...)
}
invisible(x)
}
format_percentage <- function(x) {
color <- if (x >= 90) cli::col_green
else if (x >= 75) cli::col_yellow
else cli::col_red
color(sprintf("%02.2f%%", x))
}
markers <- function(x, ...) UseMethod("markers")
#' @export
markers.coverages <- function(x, ...) {
mrks <- unlist(lapply(unname(x), markers), recursive = FALSE)
mrks <- mrks[order(
vcapply(mrks, `[[`, "file"),
viapply(mrks, `[[`, "line"),
vcapply(mrks, `[[`, "message")
)]
# request source markers
rstudioapi::callFun("sourceMarkers",
name = "covr",
markers = mrks,
basePath = NULL,
autoSelect = "first")
invisible()
}
#' @export
markers.coverage <- function(x, ...) {
# generate the markers
markers <- lapply(unname(x), function(xx) {
filename <- get_source_filename(xx$srcref, full.names = TRUE)
list(
type = "warning",
file = filename,
line = xx$srcref[1],
column = xx$srcref[2],
message = sprintf("No %s Coverage!", to_title(attr(x, "type")))
)
})
}
#' @export
markers.data.frame <- function(x, ..., type = "test") { # nolint
# generate the markers
markers <- Map(function(filename, line, column) {
list(
type = "warning",
file = filename,
line = line,
column = column %||% 1,
message = sprintf("No %s Coverage!", to_title(type))
)},
x$filename,
x$first_line %||% x$line,
x$first_column %||% rep(list(NULL), NROW(x)),
USE.NAMES = FALSE)
}
# Expand lines given as start and end ranges to enumerate each line
expand_lines <- function(x) {
repeats <- (x$last_line - x$first_line) + 1L
lines <- unlist(Map(seq, x$first_line, x$last_line)) %||% integer()
res <- x[rep(seq_len(NROW(x)), repeats), c("filename", "functions", "value")]
res$line <- lines
rownames(res) <- NULL
res
}
================================================
FILE: R/system.R
================================================
#' Run a system command and check if it succeeds.
#'
#' This function automatically quotes both the command and each
#' argument so they are properly protected from shell expansion.
#' @param cmd the command to run.
#' @param args a vector of command arguments.
#' @param env a named character vector of environment variables. Will be quoted
#' @param quiet if `TRUE`, the command output will be echoed.
#' @param echo if `TRUE`, the command to run will be echoed.
#' @param ... additional arguments passed to [base::system()]
#' @return `TRUE` if the command succeeds, an error will be thrown if the
#' command fails.
#' @keywords internal
system_check <- function(cmd, args = character(), env = character(),
quiet = FALSE, echo = FALSE, ...) {
full <- paste(c(shQuote(cmd), lapply(args, shQuote)), collapse = " ")
if (echo) {
message(wrap_command(full), "\n")
}
status <- withr::with_envvar(env,
system(full, intern = FALSE, ignore.stderr = quiet, ignore.stdout = quiet, ...)
)
if (!identical(as.character(status), "0")) {
stop("Command ", sQuote(full), " failed (", status, ")", call. = FALSE)
}
invisible(TRUE)
}
#' Run a system command and capture the output.
#'
#' This function automatically quotes both the command and each
#' argument so they are properly protected from shell expansion.
#' @inheritParams system_check
#' @return command output if the command succeeds, an error will be thrown if
#' the command fails.
#' @keywords internal
system_output <- function(cmd, args = character(), env = character(),
quiet = FALSE, echo = FALSE, ...) {
full <- paste(c(shQuote(cmd), lapply(args, shQuote)), collapse = " ")
if (echo) {
message(wrap_command(full), "\n")
}
result <- withCallingHandlers(withr::with_envvar(env,
system(full, intern = TRUE, ignore.stderr = quiet, ...)
), warning = function(w) stop(w))
result
}
wrap_command <- function(x) {
lines <- strwrap(x, getOption("width") - 2, exdent = 2)
continue <- c(rep(" \\", length(lines) - 1), "")
paste(lines, continue, collapse = "\n")
}
================================================
FILE: R/trace_calls.R
================================================
#' trace each call with a srcref attribute
#'
#' This function calls itself recursively so it can properly traverse the AST.
#' @param x the call
#' @param parent_functions the functions which this call is a child of.
#' @param parent_ref argument used to set the srcref of the current call during
#' the recursion.
#' @seealso <http://adv-r.had.co.nz/Expressions.html>
#' @return a modified expression with count calls inserted before each previous
#' call.
#' @keywords internal
trace_calls <- function (x, parent_functions = NULL, parent_ref = NULL) {
# Construct the calls by hand to avoid a NOTE from R CMD check
count <- function(key, val) {
call("if", TRUE,
call("{",
as.call(list(call(":::", as.symbol("covr"), as.symbol("count")), key)),
val
)
)
}
if (is.null(parent_functions)) {
parent_functions <- deparse(substitute(x))
}
recurse <- function(y) {
lapply(y, trace_calls, parent_functions = parent_functions)
}
if (is.atomic(x) || is.name(x) || is.null(x)) {
if (is.null(parent_ref)) {
x
} else {
if (is_na(x) || is_brace(x)) {
x
} else {
key <- new_counter(parent_ref, parent_functions) # nolint
count(key, x)
}
}
} else if (is.call(x)) {
src_ref <- attr(x, "srcref") %||% impute_srcref(x, parent_ref)
if ((identical(x[[1]], as.name("<-")) || identical(x[[1]], as.name("="))) && # nolint
(is.call(x[[3]]) && identical(x[[3]][[1]], as.name("function")))) {
parent_functions <- c(parent_functions, as.character(x[[2]]))
}
# do not try to trace curly curly
if (identical(x[[1]], as.name("{")) && length(x) == 2 && is.call(x[[2]]) && identical(x[[2]][[1]], as.name("{"))) {
as.call(x)
} else if (!is.null(src_ref)) {
as.call(Map(trace_calls, x, src_ref, MoreArgs = list(parent_functions = parent_functions)))
} else if (!is.null(parent_ref)) {
key <- new_counter(parent_ref, parent_functions)
count(key, as.call(recurse(x)))
} else {
as.call(recurse(x))
}
} else if (is.function(x)) {
# We cannot trace primitive functions
if (is.primitive(x)) {
return(x)
}
fun_body <- body(x)
if (!is.null(attr(x, "srcref")) &&
(is.symbol(fun_body) || !identical(fun_body[[1]], as.name("{")))) {
src_ref <- attr(x, "srcref")
key <- new_counter(src_ref, parent_functions)
fun_body <- count(key, trace_calls(fun_body, parent_functions))
} else {
fun_body <- trace_calls(fun_body, parent_functions)
}
new_formals <- trace_calls(formals(x), parent_functions)
if (is.null(new_formals)) new_formals <- list()
formals(x) <- new_formals
body(x) <- fun_body
x
} else if (is.pairlist(x)) {
as.pairlist(recurse(x))
} else if (is.expression(x)) {
as.expression(recurse(x))
} else if (is.list(x)) {
recurse(x)
} else {
message("Unknown language class: ", paste(class(x), collapse = "/"))
x
}
}
.counters <- new.env(parent = emptyenv())
.current_test <- new.env(parent = emptyenv())
#' initialize a new counter
#'
#' @param src_ref a [base::srcref()]
#' @param parent_functions the functions that this srcref is contained in.
#' @keywords internal
new_counter <- function(src_ref, parent_functions) {
key <- key(src_ref)
.counters[[key]]$value <- 0
.counters[[key]]$srcref <- src_ref
.counters[[key]]$functions <- parent_functions
if (isTRUE(getOption("covr.record_tests", FALSE))) new_test_counter(key)
key
}
#' increment a given counter
#'
#' @param key generated with [key()]
#' @keywords internal
count <- function(key) {
.counters[[key]]$value <- .counters[[key]]$value + 1L
if (isTRUE(.current_test$record)) count_test(key)
}
#' clear all previous counters
#'
#' @keywords internal
clear_counters <- function() {
rm(envir = .counters, list = ls(envir = .counters))
rm(envir = .current_test, list = ls(envir = .current_test))
.current_test$record <- isTRUE(getOption("covr.record_tests", FALSE))
}
#' Generate a key for a call
#'
#' @param x the srcref of the call to create a key for
#' @keywords internal
key <- function(x) {
paste(collapse = ":", c(get_source_filename(x), x))
}
================================================
FILE: R/trace_tests.R
================================================
#' Record Test Traces During Coverage Execution
#'
#' 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`.
#'
#' @section Additional fields:
#'
#' Within the `covr` result, you can explore this information in two places:
#'
#' \itemize{
#' \item `attr(,"tests")`: A list of call stacks, which results in target code
#' execution.
#'
#' \item `$<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.
#' }
#'
#' @section Test traces:
#'
#' 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:
#'
#' 1. 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`.
#' 1. 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`.
#'
#' @examples
#' 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 4
#'
#' @name covr.record_tests
NULL
#' Append a test trace to a counter, updating global current test
#'
#' @param key generated with [key()]
#' @keywords internal
#'
count_test <- function(key) {
n_calls_into_covr <- 2L
if (is_current_test_finished()) {
update_current_test()
}
# ignore if .counter was not created with record_tests (nested coverage calls)
if (is.null(.counters[[key]]$tests)) return()
.current_test$i <- .current_test$i + 1L
# expand infrequently as new tests are added, doubling matrix size as needed
tests <- .counters[[key]]$tests
n <- NROW(tests$tally)
if (.counters[[key]]$value > n) {
tests$tally <- rbind(tests$tally, matrix(NA_integer_, ncol = 4L, nrow = n))
}
# ignore if .current_test was not initialized properly yet
if (length(.current_test$index) == 0) {
return()
}
# test number
tests$.data[[1L]] <- .current_test$index
# test call number (for test expressions that are called multiple times)
tests$.data[[2L]] <- .current_test$call_count
# call stack depth when trace is hit
tests$.data[[3L]] <- sys.nframe() - length(.current_test$frames) - n_calls_into_covr + 1L
# number of traces hit by the test so far
tests$.data[[4L]] <- .current_test$i
tests$.value <- .counters[[key]]$value
with(tests, tally[.value, ] <- .data)
}
#' Initialize a new test counter for a coverage trace
#'
#' Initialize a test counter, a matrix used to tally tests, their stack depth
#' and the execution order as the trace associated with \code{key} is hit. Each
#' test trace is an environment, which allows assignment into a pre-allocated
#' \code{tests} matrix with minimall reallocation.
#'
#' The \code{tests} matrix has columns \code{tests}, \code{depth} and \code{i},
#' corresponding to the test index (the index of the associated test in
#' \code{.counters$tests}), the stack depth when the trace is evaluated and the
#' number of traces that have been hit so far during test evaluation.
#'
#' @inheritParams count
#'
new_test_counter <- function(key) {
.counters[[key]]$tests <- new.env(parent = baseenv())
.counters[[key]]$tests$.data <- vector("integer", 4L)
.counters[[key]]$tests$.value <- integer(1L)
.counters[[key]]$tests$tally <- matrix(
NA_integer_,
ncol = 4L,
# initialize with 4 empty rows, only expanded once populated
nrow = 4L,
# cols: test index; call index; call stack depth of covr:::count; execution order index
dimnames = list(c(), c("test", "call", "depth", "i"))
)
}
#' Update current test if unit test expression has progressed
#'
#' Updating a test logs some metadata regarding the current call stack, noteably
#' trying to capture information about the call stack prior to the covr::count
#' call being traced.
#'
#' There are a couple patterns of behavior, which try to accommodate a variety
#' of testing suites:
#'
#' \itemize{
#' \item `testthat`: During execution of `testthat`'s `test_*` functions,
#' files are sourced and the working directory is temporarily changed to the
#' package `/tests` directory. Knowing this, calls in the call stack which
#' are relative to this directory are extracted and recorded.
#' \item `RUnit`:
#' \item `custom`: Any other custom test suites may not have source kept with
#' their execution, in which case the entire test call stack is kept.
#' }
#'
#' checks to see if the current call stack has the same
#' `srcref` (or expression, if no source is available) at the same frame prior
#' to entering into a package where `covr:::count` is called.
#'
#' @keywords internal
#'
#' @importFrom utils getSrcDirectory
#'
update_current_test <- function() {
syscalls <- sys.calls()
syscall_first_count <- Position(is_covr_count_call, syscalls, nomatch = -1L)
if (syscall_first_count < 2L) return() # skip if nothing before covr::count
syscall_srcfile <- vcapply(syscalls, get_source_filename, normalize = TRUE)
has_srcfile <- viapply(syscall_srcfile, length) > 0L
srcfile_tmp <- logical(length(has_srcfile))
srcfile_tmp[has_srcfile] <- startsWith(
syscall_srcfile[has_srcfile],
normalizePath(.libPaths()[[1]], mustWork = FALSE)
)
test_frames <- if (any(srcfile_tmp)) {
# if possible, try to take any frames within the temporary library
which(srcfile_tmp)
} else {
# otherwise, default to taking all syscalls up until covr:::count
seq_len(syscall_first_count - 1L)
}
# add in outer frame, which may call intermediate .Internal or .External
exec_frames <- unique(c(test_frames, syscall_first_count - 1L))
# build updated current test data, isolating relevant frames
.current_test$trace <- syscalls[exec_frames]
.current_test$i <- 0L
.current_test$frames <- exec_frames
.current_test$last_frame <- exec_frames[[Position(
has_srcref,
.current_test$trace,
right = TRUE,
nomatch = length(exec_frames)
)]]
# might be NULL if srcrefs aren't kept during building / sourcing
.current_test$src_env <- sys.frame(which = .current_test$last_frame - 1L)
.current_test$src_call <- syscalls[[.current_test$last_frame]]
.current_test$srcref <- getSrcref(.current_test$src_call)
.current_test$src <- .current_test$srcref %||% .current_test$src_call
.current_test$key <- current_test_key()
.current_test$index <- current_test_index()
.current_test$call_count <- current_test_call_count()
# NOTE: r-bugs 18348
# restrict test call lengths to avoid R Rds deserialization limit
# https://bugs.r-project.org/show_bug.cgi?id=18348
max_call_len <- 1e4
call_lengths <- vapply(.current_test$trace, length, numeric(1L))
if (any(call_lengths > max_call_len)) {
.current_test$trace <- lapply(
.current_test$trace,
truncate_call,
limit = max_call_len
)
warning("A large call was captured as part of a test and will be truncated.")
}
.counters$tests[[.current_test$index]] <- .current_test$trace
attr(.counters$tests[[.current_test$index]], "call_count") <- .current_test$call_count
names(.counters$tests)[[.current_test$index]] <- .current_test$key
}
#' Build key for the current test
#'
#' If the current test has a srcref, a unique character key is built from its
#' srcref. Otherwise, an empty string is returned.
#'
#' @return A unique character string if the test call has a srcref, or an empty
#' string otherwise.
#'
#' @keywords internal
current_test_key <- function() {
if (!inherits(.current_test$src, "srcref")) return("")
file.path(
dirname(get_source_filename(.current_test$src, normalize = TRUE)),
key(.current_test$src)
)
}
#' Retrieve the index for the test in `.counters$tests`
#'
#' If the test was encountered before, the index will be the index of the test
#' in the logged tests list. Otherwise, the index will be the next index beyond
#' the length of the tests list.
#'
#' @return An integer index for the test call
#'
#' @keywords internal
current_test_index <- function() {
# check if test has already been encountered and reuse test index
if (inherits(.current_test$src, "srcref")) {
# when tests have srcrefs, we can quickly compare test keys
match(
.current_test$key,
names(.counters$tests),
nomatch = length(.counters$tests) + 1L
)
} else {
# otherwise we compare call stacks
Position(
function(t) identical(t[], .current_test$trace), # t[] to ignore attr
.counters$tests,
right = TRUE,
nomatch = length(.counters$tests) + 1L
)
}
}
#' Retrieve the number of times the test call was called
#'
#' A single test expression might be evaluated many times. Each time the same
#' expression is called, the call count is incremented.
#'
#' @return An integer value representing the number of calls of the current
#' call into the package from the testing suite.
#'
current_test_call_count <- function() {
if (.current_test$index <= length(.counters$tests)) {
attr(.counters$tests[[.current_test$index]], "call_count") + 1L
} else {
1L
}
}
#' Truncate call objects to limit the number of arguments
#'
#' 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.
#'
#' @param call_obj A (possibly large) \code{call} object
#' @param limit A \code{call} length limit to impose
#' @return The \code{call_obj} with arguments trimmed
#'
truncate_call <- function(call_obj, limit = 1e4) {
if (length(call_obj) < limit) return(call_obj)
call_obj <- head(call_obj, limit)
call_obj[[length(call_obj)]] <- quote(`<truncated>`)
call_obj
}
#' Returns TRUE if we've moved on from test reflected in .current_test
#'
#' 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 <- function() {
is.null(.current_test$src) ||
.current_test$last_frame > sys.nframe() ||
!identical(.current_test$src_call, sys.call(which = .current_test$last_frame)) ||
!identical(.current_test$src_env, sys.frame(which = .current_test$last_frame - 1L))
}
#' Is the source bound to the expression
#'
#' @param expr A language object which may have a `srcref` attribute
#' @return A logical value indicating whether the language object has source
#'
has_srcref <- function(expr) {
!is.null(getSrcref(expr))
}
#' Is the expression a call to covr:::count
#'
#' @param expr A language object
#' @return A logical value indicating whether the object is a call to
#' `covr:::count`.
#'
is_covr_count_call <- function(expr) {
count_call <- call(":::", as.symbol("covr"), as.symbol("count"))
identical(expr[[1]], count_call)
}
================================================
FILE: R/utils.R
================================================
`%||%` <- function(x, y) {
if (!is.null(x)) {
x
} else {
y
}
}
compact <- function(x) {
x[viapply(x, length) != 0]
}
trim <- function(x) {
rex::re_substitutes(x, rex::rex(list(start, spaces) %or% list(spaces, end)), "", global = TRUE)
}
local_branch <- function(dir = ".") {
withr::with_dir(dir,
branch <- system_output("git", c("rev-parse", "--abbrev-ref", "HEAD"))
)
trim(branch)
}
current_commit <- function(dir = ".") {
withr::with_dir(dir,
commit <- system_output("git", c("rev-parse", "HEAD"))
)
trim(commit)
}
`[.coverage` <- function(x, i, ...) {
attrs <- attributes(x)
attrs$names <- attrs$names[i]
res <- unclass(x)
res <- res[i]
attributes(res) <- attrs
res
}
to_title <- function(x) {
rex::re_substitutes(x,
rex::rex(rex::regex("\\b"), capture(any)),
"\\U\\1",
global = TRUE)
}
srcfile_lines <- function(srcfile) {
lines <- getSrcLines(srcfile, 1, Inf)
matches <- rex::re_matches(lines,
rex::rex(start, any_spaces, "#line", spaces,
capture(name = "line_number", digit), spaces,
quotes, capture(name = "filename", anything), quotes))
matches <- na.omit(matches)
filename_match <- which(matches$filename == srcfile$filename)
if (length(filename_match) == 1) {
# rownames(matches) is the line number of lines
start <- as.numeric(rownames(matches)[filename_match]) + 1
# If there is another directive we want to stop at that, otherwise stop at
# the end
end <- if (!is.na(rownames(matches)[filename_match + 1])) {
as.numeric(rownames(matches)[filename_match + 1]) - 1
} else {
length(lines)
}
# If there are no line directives for the file just use the entire file
} else {
start <- 1
end <- length(lines)
}
res <- lines[seq(start, end)]
# Track blank or comment lines so they can be excluded from the result calculations, but only for R files
if (rex::re_matches(srcfile$filename, rex::rex(".", one_of("r", "R"), end))) {
attr(res, "blanks") <- which(rex::re_matches(res, rex::rex(start, any_spaces, maybe("#", anything), end)))
}
res
}
# Split lines into a list based on the line directives in the file.
split_on_line_directives <- function(lines) {
matches <- rex::re_matches(lines,
rex::rex(start, any_spaces, "#line", spaces,
capture(name = "line_number", digit), spaces,
quotes, capture(name = "filename", anything), quotes))
directive_lines <- which(!is.na(matches$line_number))
if (!length(directive_lines)) {
return(NULL)
}
file_starts <- directive_lines + 1
file_ends <- c(directive_lines[-1] - 1, length(lines))
res <- mapply(
function(start, end) lines[start:end],
file_starts,
file_ends,
SIMPLIFY = FALSE
)
names(res) <- na.omit(matches$filename)
res
}
traced_files <- function(x) {
res <- list()
filenames <- display_name(x)
for (i in seq_along(x)) {
src_file <- attr(x[[i]]$srcref, "srcfile")
filename <- filenames[[i]]
if (filename == "") next
if (!is.null(res[[filename]])) next
lines <- getSrcLines(src_file, 1, Inf)
matches <- rex::re_matches(lines,
rex::rex(start, any_spaces, "#line", spaces,
capture(name = "line_number", digit), spaces,
quotes, capture(name = "filename", anything), quotes))
matches <- na.omit(matches)
filename_match <- which(matches$filename == src_file$filename)
if (length(filename_match) == 1) {
start <- as.numeric(rownames(matches)[filename_match]) + 1
end <- if (!is.na(rownames(matches)[filename_match + 1])) {
as.numeric(rownames(matches)[filename_match + 1]) - 1
} else {
length(lines)
}
} else {
start <- 1
end <- length(lines)
}
src_file$file_lines <- lines[seq(start, end)]
res[[filename]] <- src_file
}
res
}
per_line <- function(coverage) {
df <- as.data.frame(coverage)
# In rare cases the source reference such as generated code onload the source
# reference will not exists, so the first_line will be NA
df <- df[!is.na(df$first_line), ]
files <- traced_files(coverage)
# Lines with only spaces or only comments
blank_lines <- lapply(files, function(file) {
which(rex::re_matches(file$file_lines, rex::rex(start, any_spaces, maybe("#", anything), end)))
})
# lines with only })], or an else block
empty_lines <- lapply(files, function(file) {
which(rex::re_matches(file$file_lines, "^(?:[[:punct:][:space:]]|else)*$"))
})
file_lengths <- lapply(files, function(file) {
length(file$file_lines)
})
res <- lapply(file_lengths,
function(x) {
rep(NA_real_, length.out = x)
})
# df is sorted by file and first line ascending, so we store the maximum
# last_line seen to detect if the previous expression contains the current
# expression.
max_last <- 0
prev_filename <- ""
for (i in seq_len(NROW(df))) {
filename <- df[i, "filename"]
for (line in seq(df[i, "first_line"], df[i, "last_line"])) {
# if it is not a blank line or empty line
if (!line %in% c(blank_lines[[filename]], empty_lines[[filename]])) {
value <- df[i, "value"]
# if current coverage is NA or last line < max last line
if (is.na(res[[filename]][line]) || line < max_last || (line == max_last && res[[filename]][line] > value)) {
res[[filename]][line] <- value
}
if (df[i, "filename"] != prev_filename) {
prev_filename <- df[i, "filename"]
max_last <- 0
}
if (df[i, "last_line"] > max_last) {
max_last <- df[i, "last_line"]
}
}
}
}
structure(
Map(function(file, coverage) {
structure(list(file = file, coverage = coverage), class = "line_coverage")
},
files, res),
class = "line_coverages")
}
if (getRversion() < "3.2.0") {
isNamespaceLoaded <- function(x) x %in% loadedNamespaces()
}
is_windows <- function() {
.Platform$OS.type == "windows"
}
as_package <- function(path) {
path <- normalize_path(path)
if (!file.exists(path)) {
stop("`path` is invalid: ", path, call. = FALSE)
}
root <- package_root(path)
if (is.null(root)) {
stop(sQuote(path), " does not contain a package!", call. = FALSE)
}
res <- read_description(file.path(root, "DESCRIPTION"))
res$path <- root
res
}
package_root <- function(path) {
stopifnot(is.character(path))
has_description <- function(path) {
file.exists(file.path(path, "DESCRIPTION"))
}
is_root <- function(path) {
identical(path, dirname(path))
}
path <- normalize_path(path)
while (!is_root(path) && !has_description(path)) {
path <- dirname(path)
}
if (is_root(path)) {
NULL
} else {
path
}
}
read_description <- function(path) {
if (!length(path) || !file.exists(path)) {
stop("DESCRIPTION file not found at ", sQuote(path), call. = FALSE)
}
res <- as.list(read.dcf(path)[1, ])
names(res) <- tolower(names(res))
res
}
clean_objects <- function(path) {
files <- list.files(file.path(path, "src"),
pattern = rex::rex(".",
or("o", "sl", "so", "dylib",
"a", "dll"), end),
full.names = TRUE, recursive = TRUE)
unlink(files)
invisible(files)
}
# This is not actually an S3 method
# From http://stackoverflow.com/a/34639237/2055486
setdiff.data.frame <- function(x, y,
by = intersect(names(x), names(y)),
by.x = by, by.y = by) {
stopifnot(
is.data.frame(x),
is.data.frame(y),
length(by.x) == length(by.y))
!do.call(paste, c(x[by.x], sep = "\30")) %in% do.call(paste, c(y[by.y], sep = "\30"))
}
`%==%` <- function(x, y) identical(x, y)
`%!=%` <- function(x, y) !identical(x, y)
is_na <- function(x) {
!is.null(x) && !is.symbol(x) && is.na(x)
}
is_brace <- function(x) {
is.symbol(x) && as.character(x) == "{"
}
modify_name <- function(expr, old, new) {
replace <- function(e)
if (is.name(e) && identical(e, as.name(old))) e <- as.name(new)
else if (length(e) <= 1L) e
else as.call(lapply(e, replace))
replace(expr)
}
# This is the fix for https://bugs.r-project.org/bugzilla3/show_bug.cgi?id=16659
match_arg <- base::match.arg
body(match_arg) <- modify_name(body(match_arg), "all", "any")
# from https://github.com/wch/r-source/blob/2065bd3c09813949e9fa7236d167f1b7ed5c8ba3/src/library/tools/R/check.R#L4134-L4137
env_path <- function(...) {
paths <- c(...)
paste(paths[nzchar(paths)], collapse = .Platform$path.sep)
}
normalize_path <- function(x) {
path <- normalizePath(x, winslash = "/", mustWork = FALSE)
# Strip any trailing slashes as they are invalid on windows
sub("/*$", "", path)
}
temp_dir <- function() {
normalize_path(tempdir())
}
temp_file <- function(pattern = "file", tmpdir = temp_dir(), fileext = "") {
normalize_path(tempfile(pattern, tmpdir, fileext))
}
get_package_name <- function(x) {
attr(x, "package")$package %||% "coverage"
}
get_source_filename <- function(x, full.names = FALSE, unique = TRUE, normalize = FALSE) {
res <- getSrcFilename(x, full.names, unique)
if (length(res) == 0) {
return("")
}
if (normalize) {
return(normalize_path(res))
}
res
}
vcapply <- function(X, FUN, ...) vapply(X, FUN, ..., FUN.VALUE = character(1))
vdapply <- function(X, FUN, ...) vapply(X, FUN, ..., FUN.VALUE = numeric(1))
viapply <- function(X, FUN, ...) vapply(X, FUN, ..., FUN.VALUE = integer(1))
vlapply <- function(X, FUN, ...) vapply(X, FUN, ..., FUN.VALUE = logical(1))
trim_ws <- function(x) {
x <- sub("^[ \t\r\n]+", "", x, perl = TRUE)
sub("[ \t\r\n]+$", "", x, perl = TRUE)
}
================================================
FILE: R/value.R
================================================
#' Retrieve the value from an object
#' @export
#' @param x object from which to retrieve the value
#' @param ... additional arguments passed to methods
value <- function(x, ...) UseMethod("value")
#' @export
value.coverage <- function(x, ...) {
vdapply(x, value, ...)
}
#' @export
value.expression_coverage <- function(x, ...) {
x$value
}
#' @export
value.expression_coverages <- value.coverage
#' @export
value.line_coverage <- value.expression_coverage
#' @export
value.line_coverages <- value.expression_coverages
================================================
FILE: R/vectorized.R
================================================
# simple function to test if a function is Vectorized
is_vectorized <- function(x) {
is.function(x) && exists("FUN", environment(x), mode = "function") && exists("vectorize.args", environment(x))
}
================================================
FILE: R/zzz.R
================================================
.onLoad <- function(libname, pkgname) { # nolint
rex::register_shortcuts("covr")
op <- options()
op_covr <- list(
covr.covrignore = Sys.getenv("COVR_COVRIGNORE", ".covrignore"),
covr.gcov = Sys.which("gcov"),
covr.gcov_args = NULL,
covr.gcov_additional_paths = NULL,
covr.exclude_pattern = rex::rex("#", any_spaces, "nocov"),
covr.exclude_start = rex::rex("#", any_spaces, "nocov", any_spaces, "start"),
covr.exclude_end = rex::rex("#", any_spaces, "nocov", any_spaces, "end"),
covr.flags = c(CFLAGS = "-O0 --coverage",
CXXFLAGS = "-O0 --coverage",
CXX1XFLAGS = "-O0 --coverage",
CXX11FLAGS = "-O0 --coverage",
CXX14FLAGS = "-O0 --coverage",
CXX17FLAGS = "-O0 --coverage",
CXX20FLAGS = "-O0 --coverage",
FFLAGS = "-O0 --coverage",
FCFLAGS = "-O0 --coverage",
FLIBS = "-lgcov",
# LDFLAGS is ignored on windows and visa versa
LDFLAGS = if (!is_windows()) "--coverage" else NULL,
SHLIB_LIBADD = if (is_windows()) "--coverage" else NULL)
)
# add icc code coverage settings
icov_flag <- "-O0 -prof-gen=srcpos"
op_covr <- c(op_covr, list(
covr.icov = Sys.which("codecov"),
covr.icov_args = NULL,
covr.icov_prof = Sys.which("profmerge"),
covr.icov_flags = c(CFLAGS = icov_flag,
CXXFLAGS = icov_flag,
CXX1XFLAGS = icov_flag,
CXX11FLAGS = icov_flag,
CXX14FLAGS = icov_flag,
CXX17FLAGS = icov_flag,
CXX20FLAGS = icov_flag,
FFLAGS = icov_flag,
FCFLAGS = icov_flag,
# LDFLAGS is ignored on windows and visa versa
LDFLAGS = icov_flag,
SHLIB_LIBADD = icov_flag)
))
toset <- !(names(op_covr) %in% names(op))
if (any(toset)) options(op_covr[toset])
invisible()
}
================================================
FILE: README.md
================================================
# covr <img src="man/figures/logo.png" align="right" />
<!-- badges: start -->
[](https://github.com/r-lib/covr/actions/workflows/R-CMD-check.yaml)
[](https://app.codecov.io/gh/r-lib/covr?branch=master)
[](https://cran.r-project.org/package=covr)
<!-- badges: end -->
Track test coverage for your R package and view reports locally or (optionally)
upload the results to [codecov](https://about.codecov.io/) or [coveralls](https://coveralls.io/).
# Installation #
```r
install.packages("covr")
# For devel version
devtools::install_github("r-lib/covr")
```
The easiest way to setup covr on [Github Actions](https://github.com/r-lib/actions/tree/v2-branch/examples#test-coverage-workflow)
is with [usethis](https://github.com/r-lib/usethis).
```r
usethis::use_github_action("test-coverage")
```
# Usage #
For local development a coverage report can be used to inspect coverage for
each line in your package. *Note* requires the
[DT](https://github.com/rstudio/DT) package to be installed.
```r
library(covr)
# If run with no arguments implicitly calls `package_coverage()`
report()
```
covr also defines an [RStudio Addin](https://rstudio.github.io/rstudioaddins/),
which runs `report()` on the active project. This can be used via the Addin
menu or by binding the action to a
[shortcut](https://rstudio.github.io/rstudioaddins/#keyboard-shorcuts), e.g.
*Ctrl-Shift-C*.
## Interactively ##
```r
# If run with the working directory within the package source.
package_coverage()
# or a package in another directory
cov <- package_coverage("/dir/lintr")
# view results as a data.frame
as.data.frame(cov)
# zero_coverage() shows only uncovered lines.
# If run within RStudio, `zero_coverage()` will open a marker pane with the
# uncovered lines.
zero_coverage(cov)
```
# Exclusions #
`covr` supports a few of different ways of excluding some or all of a file.
## .covrignore file ##
A `.covrignore` file located in your package's root directory can be used to
exclude files or directories.
The lines in the `.covrignore` file are interpreted as a list of file globs to
ignore. It uses the globbing rules in `Sys.glob()`. Any directories listed will
ignore all the files in the directory.
Alternative locations for the file can be set by the environment variable
`COVR_COVRIGNORE` or the R option `covr.covrignore`.
The `.covrignore` file should be added to your `.RBuildignore` file unless you
want to distribute it with your package. If so it can be added to
`inst/.covrignore` instead.
## Function Exclusions ##
The `function_exclusions` argument to `package_coverage()` can be used to
exclude functions by name. This argument takes a vector of regular expressions
matching functions to exclude.
```r
# exclude print functions
package_coverage(function_exclusions = "print\\.")
# exclude `.onLoad` function
package_coverage(function_exclusions = "\\.onLoad")
```
## Line Exclusions ##
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.
```r
# exclude whole file of R/test.R
package_coverage(line_exclusions = "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" = c(1, 10), "R/test2.R"))
```
## Exclusion Comments ##
In addition you can exclude lines from the coverage by putting special comments
in your source code.
This can be done per line.
```r
f1 <- function(x) {
x + 1 # nocov
}
```
Or by specifying a range with a start and end.
```r
f2 <- function(x) { # nocov start
x + 2
} # nocov end
```
The patterns used can be specified by setting the global options
`covr.exclude_pattern`, `covr.exclude_start`, `covr.exclude_end`.
NB: The same pattern applies to exclusions in the `src` folder, so skipped lines in, e.g., C code (where comments can start with `//`) should look like `// # nocov`.
# FAQ #
## Will covr work with testthat, RUnit, etc... ##
Covr should be compatible with any testing package, it uses
`tools::testInstalledPackage()` to run your packages tests.
## Will covr work with alternative compilers such as ICC ##
Covr now supports Intel's `icc` compiler, thanks to work contributed by Qin
Wang at Oracle.
Covr is known to work with clang versions `3.5+` and gcc version `4.2+`.
If the appropriate gcov version is not on your path you can set the appropriate
location with the `covr.gcov` options. If you set this path to "" it will turn
_off_ coverage of compiled code.
```r
options(covr.gcov = "path/to/gcov")
```
## How does covr work? ##
`covr` tracks test coverage by modifying a package's code to add tracking calls
to each call.
The vignette
[vignettes/how_it_works.Rmd](https://github.com/r-lib/covr/blob/master/vignettes/how_it_works.Rmd)
contains a detailed explanation of the technique and the rationale behind it.
You can view the vignette from within `R` using
```r
vignette("how_it_works", package = "covr")
```
## Why can't covr run during R CMD check ##
Because covr modifies the package code it is possible there are unknown edge
cases where that modification affects the output. In addition when tracking
coverage for compiled code covr compiles the package without optimization,
which _can_ modify behavior (usually due to package bugs which are masked with
higher optimization levels).
# Alternative Coverage Tools #
- <https://github.com/MangoTheCat/testCoverage> (no longer supported)
- [**R-coverage**](https://web.archive.org/web/20160611114452/http://r2d2.quartzbio.com/posts/r-coverage-docker.html) (no longer supported)
## Code of Conduct
Please note that the covr project is released with a [Contributor Code of Conduct](https://github.com/r-lib/covr/blob/main/CODE_OF_CONDUCT.md). By contributing to this project, you agree to abide by its terms.
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Reporting a Vulnerability
Send an email to james.f.hester@gmail.com to report a vulnerability.
================================================
FILE: _pkgdown.yml
================================================
url: https://covr.r-lib.org
authors:
"Jim Hester":
href: https://www.jimhester.com/
destination: docs
development:
mode: auto
template:
bootstrap: 5
params:
ganalytics: UA-115082821-1
================================================
FILE: codecov.yml
================================================
comment: false
coverage:
status:
project:
default:
target: auto
threshold: 1%
informational: true
patch:
default:
target: auto
threshold: 1%
informational: true
================================================
FILE: cran-comments.md
================================================
This is a patch release to fix a change made in R-devel
## R CMD check results
There were no NOTEs, ERRORs or WARNINGs.
================================================
FILE: docker_checker/Dockerfile
================================================
FROM rocker/hadleyverse
MAINTAINER james.f.hester@gmail.com
# install deps from current github master
RUN Rscript -e 'devtools::install_github("jimhester/covr", dependencies = TRUE)'
# remove installed covr to be sure not to conflict with current source version
RUN Rscript -e 'remove.packages("covr")'
# docker user setup
RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo && \
chmod -R a+rwx /usr/local/lib/R/site-library
USER docker
WORKDIR /home/docker
================================================
FILE: inst/rstudio/addins.dcf
================================================
Name: Calculate package test coverage
Description: Calculates the package test coverage and opens a report, using `covr::report()`
Binding: addin_report
Interactive: false
================================================
FILE: inst/www/report.css
================================================
table tr:hover td {
font-weight:bold;text-decoration:none
}
table tr.covered td{
background-color:rgba(95,151,68,0.3)
}
table tr:hover.covered .num{
background-color:rgba(95,151,68,0.7)
}
table tr.missed td{
background-color:rgba(185,73,71,0.3)
}
table tr:hover.missed .num{
background-color:rgba(185,73,71,0.7)
}
table tr.missed:hover td{
-webkit-box-shadow:0 -2px 0 0 #b94947 inset;
-moz-box-shadow:0 -2px 0 0 #b94947 inset;
box-shadow:0 -2px 0 0 #b94947 inset
}
table tr.covered:hover td{
-webkit-box-shadow:0 -2px 0 0 #5f9744 inset;
-moz-box-shadow:0 -2px 0 0 #5f9744 inset;
box-shadow:0 -2px 0 0 #5f9744 inset
}
table tr.never td{
background-color:transparent
}
table tbody {
border-style: solid;
border: 1px solid rgba(0,0,0,0.1)
}
table .num {
border-right: 1px solid rgba(0,0,0,0.1)
}
td.coverage em {
opacity: 0.5;
}
table td.coverage {
border-right: 1px solid rgba(0,0,0,0.1);
font-weight: bold;
text-align: center;
}
table.table-condensed pre {
background-color: transparent;
margin: 0;
padding: 0;
border: 0;
font-size: 11px;
}
div#files td {
padding: 0;
padding-left: 5px;
}
div#files td.num {
padding-right: 5px;
}
table.table-condensed {
font-size: 11px;
}
================================================
FILE: inst/www/shared/highlight.js/LICENSE
================================================
Copyright (c) 2006, Ivan Sagalaev
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of highlight.js nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: inst/www/shared/highlight.js/highlight.pack.js
================================================
var hljs=new function(){function m(p){return p.replace(/&/gm,"&").replace(/</gm,"<")}function f(r,q,p){return RegExp(q,"m"+(r.cI?"i":"")+(p?"g":""))}function b(r){for(var p=0;p<r.childNodes.length;p++){var q=r.childNodes[p];if(q.nodeName=="CODE"){return q}if(!(q.nodeType==3&&q.nodeValue.match(/\s+/))){break}}}function h(t,s){var p="";for(var r=0;r<t.childNodes.length;r++){if(t.childNodes[r].nodeType==3){var q=t.childNodes[r].nodeValue;if(s){q=q.replace(/\n/g,"")}p+=q}else{if(t.childNodes[r].nodeName=="BR"){p+="\n"}else{p+=h(t.childNodes[r])}}}if(/MSIE [678]/.test(navigator.userAgent)){p=p.replace(/\r/g,"\n")}return p}function a(s){var r=s.className.split(/\s+/);r=r.concat(s.parentNode.className.split(/\s+/));for(var q=0;q<r.length;q++){var p=r[q].replace(/^language-/,"");if(e[p]){return p}}}function c(q){var p=[];(function(s,t){for(var r=0;r<s.childNodes.length;r++){if(s.childNodes[r].nodeType==3){t+=s.childNodes[r].nodeValue.length}else{if(s.childNodes[r].nodeName=="BR"){t+=1}else{if(s.childNodes[r].nodeType==1){p.push({event:"start",offset:t,node:s.childNodes[r]});t=arguments.callee(s.childNodes[r],t);p.push({event:"stop",offset:t,node:s.childNodes[r]})}}}}return t})(q,0);return p}function k(y,w,x){var q=0;var z="";var s=[];function u(){if(y.length&&w.length){if(y[0].offset!=w[0].offset){return(y[0].offset<w[0].offset)?y:w}else{return w[0].event=="start"?y:w}}else{return y.length?y:w}}function t(D){var A="<"+D.nodeName.toLowerCase();for(var B=0;B<D.attributes.length;B++){var C=D.attributes[B];A+=" "+C.nodeName.toLowerCase();if(C.value!==undefined&&C.value!==false&&C.value!==null){A+='="'+m(C.value)+'"'}}return A+">"}while(y.length||w.length){var v=u().splice(0,1)[0];z+=m(x.substr(q,v.offset-q));q=v.offset;if(v.event=="start"){z+=t(v.node);s.push(v.node)}else{if(v.event=="stop"){var p,r=s.length;do{r--;p=s[r];z+=("</"+p.nodeName.toLowerCase()+">")}while(p!=v.node);s.splice(r,1);while(r<s.length){z+=t(s[r]);r++}}}}return z+m(x.substr(q))}function j(){function q(x,y,v){if(x.compiled){return}var u;var s=[];if(x.k){x.lR=f(y,x.l||hljs.IR,true);for(var w in x.k){if(!x.k.hasOwnProperty(w)){continue}if(x.k[w] instanceof Object){u=x.k[w]}else{u=x.k;w="keyword"}for(var r in u){if(!u.hasOwnProperty(r)){continue}x.k[r]=[w,u[r]];s.push(r)}}}if(!v){if(x.bWK){x.b="\\b("+s.join("|")+")\\s"}x.bR=f(y,x.b?x.b:"\\B|\\b");if(!x.e&&!x.eW){x.e="\\B|\\b"}if(x.e){x.eR=f(y,x.e)}}if(x.i){x.iR=f(y,x.i)}if(x.r===undefined){x.r=1}if(!x.c){x.c=[]}x.compiled=true;for(var t=0;t<x.c.length;t++){if(x.c[t]=="self"){x.c[t]=x}q(x.c[t],y,false)}if(x.starts){q(x.starts,y,false)}}for(var p in e){if(!e.hasOwnProperty(p)){continue}q(e[p].dM,e[p],true)}}function d(B,C){if(!j.called){j();j.called=true}function q(r,M){for(var L=0;L<M.c.length;L++){if((M.c[L].bR.exec(r)||[null])[0]==r){return M.c[L]}}}function v(L,r){if(D[L].e&&D[L].eR.test(r)){return 1}if(D[L].eW){var M=v(L-1,r);return M?M+1:0}return 0}function w(r,L){return L.i&&L.iR.test(r)}function K(N,O){var M=[];for(var L=0;L<N.c.length;L++){M.push(N.c[L].b)}var r=D.length-1;do{if(D[r].e){M.push(D[r].e)}r--}while(D[r+1].eW);if(N.i){M.push(N.i)}return f(O,M.join("|"),true)}function p(M,L){var N=D[D.length-1];if(!N.t){N.t=K(N,E)}N.t.lastIndex=L;var r=N.t.exec(M);return r?[M.substr(L,r.index-L),r[0],false]:[M.substr(L),"",true]}function z(N,r){var L=E.cI?r[0].toLowerCase():r[0];var M=N.k[L];if(M&&M instanceof Array){return M}return false}function F(L,P){L=m(L);if(!P.k){return L}var r="";var O=0;P.lR.lastIndex=0;var M=P.lR.exec(L);while(M){r+=L.substr(O,M.index-O);var N=z(P,M);if(N){x+=N[1];r+='<span class="'+N[0]+'">'+M[0]+"</span>"}else{r+=M[0]}O=P.lR.lastIndex;M=P.lR.exec(L)}return r+L.substr(O,L.length-O)}function J(L,M){if(M.sL&&e[M.sL]){var r=d(M.sL,L);x+=r.keyword_count;return r.value}else{return F(L,M)}}function I(M,r){var L=M.cN?'<span class="'+M.cN+'">':"";if(M.rB){y+=L;M.buffer=""}else{if(M.eB){y+=m(r)+L;M.buffer=""}else{y+=L;M.buffer=r}}D.push(M);A+=M.r}function G(N,M,Q){var R=D[D.length-1];if(Q){y+=J(R.buffer+N,R);return false}var P=q(M,R);if(P){y+=J(R.buffer+N,R);I(P,M);return P.rB}var L=v(D.length-1,M);if(L){var O=R.cN?"</span>":"";if(R.rE){y+=J(R.buffer+N,R)+O}else{if(R.eE){y+=J(R.buffer+N,R)+O+m(M)}else{y+=J(R.buffer+N+M,R)+O}}while(L>1){O=D[D.length-2].cN?"</span>":"";y+=O;L--;D.length--}var r=D[D.length-1];D.length--;D[D.length-1].buffer="";if(r.starts){I(r.starts,"")}return R.rE}if(w(M,R)){throw"Illegal"}}var E=e[B];var D=[E.dM];var A=0;var x=0;var y="";try{var s,u=0;E.dM.buffer="";do{s=p(C,u);var t=G(s[0],s[1],s[2]);u+=s[0].length;if(!t){u+=s[1].length}}while(!s[2]);if(D.length>1){throw"Illegal"}return{r:A,keyword_count:x,value:y}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:m(C)}}else{throw H}}}function g(t){var p={keyword_count:0,r:0,value:m(t)};var r=p;for(var q in e){if(!e.hasOwnProperty(q)){continue}var s=d(q,t);s.language=q;if(s.keyword_count+s.r>r.keyword_count+r.r){r=s}if(s.keyword_count+s.r>p.keyword_count+p.r){r=p;p=s}}if(r.language){p.second_best=r}return p}function i(r,q,p){if(q){r=r.replace(/^((<[^>]+>|\t)+)/gm,function(t,w,v,u){return w.replace(/\t/g,q)})}if(p){r=r.replace(/\n/g,"<br>")}return r}function n(t,w,r){var x=h(t,r);var v=a(t);var y,s;if(v){y=d(v,x)}else{return}var q=c(t);if(q.length){s=document.createElement("pre");s.innerHTML=y.value;y.value=k(q,c(s),x)}y.value=i(y.value,w,r);var u=t.className;if(!u.match("(\\s|^)(language-)?"+v+"(\\s|$)")){u=u?(u+" "+v):v}if(/MSIE [678]/.test(navigator.userAgent)&&t.tagName=="CODE"&&t.parentNode.tagName=="PRE"){s=t.parentNode;var p=document.createElement("div");p.innerHTML="<pre><code>"+y.value+"</code></pre>";t=p.firstChild.firstChild;p.firstChild.cN=s.cN;s.parentNode.replaceChild(p.firstChild,s)}else{t.innerHTML=y.value}t.className=u;t.result={language:v,kw:y.keyword_count,re:y.r};if(y.second_best){t.second_best={language:y.second_best.language,kw:y.second_best.keyword_count,re:y.second_best.r}}}function o(){if(o.called){return}o.called=true;var r=document.getElementsByTagName("pre");for(var p=0;p<r.length;p++){var q=b(r[p]);if(q){n(q,hljs.tabReplace)}}}function l(){if(window.addEventListener){window.addEventListener("DOMContentLoaded",o,false);window.addEventListener("load",o,false)}else{if(window.attachEvent){window.attachEvent("onload",o)}else{window.onload=o}}}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=n;this.initHighlighting=o;this.initHighlightingOnLoad=l;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="\\b(0[xX][a-fA-F0-9]+|(\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.ER="(?![\\s\\S])";this.BE={b:"\\\\.",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(r,s){var p={};for(var q in r){p[q]=r[q]}if(s){for(var q in s){p[q]=s[q]}}return p}}();hljs.LANGUAGES.css=function(){var a={cN:"function",b:hljs.IR+"\\(",e:"\\)",c:[{eW:true,eE:true,c:[hljs.NM,hljs.ASM,hljs.QSM]}]};return{cI:true,dM:{i:"[=/|']",c:[hljs.CBLCLM,{cN:"id",b:"\\#[A-Za-z0-9_-]+"},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"pseudo",b:":(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\\"\\']+"},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:{"font-face":1,page:1}},{cN:"at_rule",b:"@",e:"[{;]",eE:true,k:{"import":1,page:1,media:1,charset:1},c:[a,hljs.ASM,hljs.QSM,hljs.NM]},{cN:"tag",b:hljs.IR,r:0},{cN:"rules",b:"{",e:"}",i:"[^\\s]",r:0,c:[hljs.CBLCLM,{cN:"rule",b:"[^\\s]",rB:true,e:";",eW:true,c:[{cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:true,i:"[^\\s]",starts:{cN:"value",eW:true,eE:true,c:[a,hljs.NM,hljs.QSM,hljs.ASM,hljs.CBLCLM,{cN:"hexcolor",b:"\\#[0-9A-F]+"},{cN:"important",b:"!important"}]}}]}]}]}}}();hljs.LANGUAGES.javascript={dM:{k:{keyword:{"in":1,"if":1,"for":1,"while":1,"finally":1,"var":1,"new":1,"function":1,"do":1,"return":1,"void":1,"else":1,"break":1,"catch":1,"instanceof":1,"with":1,"throw":1,"case":1,"default":1,"try":1,"this":1,"switch":1,"continue":1,"typeof":1,"delete":1},literal:{"true":1,"false":1,"null":1}},c:[hljs.ASM,hljs.QSM,hljs.CLCM,hljs.CBLCLM,hljs.CNM,{b:"("+hljs.RSR+"|\\b(case|return|throw)\\b)\\s*",k:{"return":1,"throw":1,"case":1},c:[hljs.CLCM,hljs.CBLCLM,{cN:"regexp",b:"/",e:"/[gim]*",c:[{b:"\\\\/"}]}],r:0},{cN:"function",bWK:true,e:"{",k:{"function":1},c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[hljs.ASM,hljs.QSM,hljs.CLCM,hljs.CBLCLM]}]}]}};hljs.LANGUAGES.r={dM:{c:[hljs.HCM,{cN:"number",b:"\\b0[xX][0-9a-fA-F]+[Li]?\\b",e:hljs.IMMEDIATE_RE,r:0},{cN:"number",b:"\\b\\d+(?:[eE][+\\-]?\\d*)?L\\b",e:hljs.IMMEDIATE_RE,r:0},{cN:"number",b:"\\b\\d+\\.(?!\\d)(?:i\\b)?",e:hljs.IMMEDIATE_RE,r:1},{cN:"number",b:"\\b\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b",e:hljs.IMMEDIATE_RE,r:0},{cN:"number",b:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b",e:hljs.IMMEDIATE_RE,r:1},{cN:"keyword",b:"(?:tryCatch|library|setGeneric|setGroupGeneric)\\b",e:hljs.IMMEDIATE_RE,r:10},{cN:"keyword",b:"\\.\\.\\.",e:hljs.IMMEDIATE_RE,r:10},{cN:"keyword",b:"\\.\\.\\d+(?![\\w.])",e:hljs.IMMEDIATE_RE,r:10},{cN:"keyword",b:"\\b(?:function)",e:hljs.IMMEDIATE_RE,r:2},{cN:"keyword",b:"(?:if|in|break|next|repeat|else|for|return|switch|while|try|stop|warning|require|attach|detach|source|setMethod|setClass)\\b",e:hljs.IMMEDIATE_RE,r:1},{cN:"literal",b:"(?:NA|NA_integer_|NA_real_|NA_character_|NA_complex_)\\b",e:hljs.IMMEDIATE_RE,r:10},{cN:"literal",b:"(?:NULL|TRUE|FALSE|T|F|Inf|NaN)\\b",e:hljs.IMMEDIATE_RE,r:1},{cN:"identifier",b:"[a-zA-Z.][a-zA-Z0-9._]*\\b",e:hljs.IMMEDIATE_RE,r:0},{cN:"operator",b:"<\\-(?!\\s*\\d)",e:hljs.IMMEDIATE_RE,r:2},{cN:"operator",b:"\\->|<\\-",e:hljs.IMMEDIATE_RE,r:1},{cN:"operator",b:"%%|~",e:hljs.IMMEDIATE_RE},{cN:"operator",b:">=|<=|==|!=|\\|\\||&&|=|\\+|\\-|\\*|/|\\^|>|<|!|&|\\||\\$|:",e:hljs.IMMEDIATE_RE,r:0},{cN:"operator",b:"%",e:"%",i:"\\n",r:1},{cN:"identifier",b:"`",e:"`",r:0},{cN:"string",b:'"',e:'"',c:[hljs.BE],r:0},{cN:"string",b:"'",e:"'",c:[hljs.BE],r:0},{cN:"paren",b:"[[({\\])}]",e:hljs.IMMEDIATE_RE,r:0}]}};hljs.LANGUAGES.xml=function(){var b="[A-Za-z0-9\\._:-]+";var a={eW:true,c:[{cN:"attribute",b:b,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,dM:{c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"<!--",e:"-->",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{title:{style:1}},c:[a],starts:{cN:"css",e:"</style>",rE:true,sL:"css"}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{title:{script:1}},c:[a],starts:{cN:"javascript",e:"<\/script>",rE:true,sL:"javascript"}},{cN:"vbscript",b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"title",b:"[^ />]+"},a]}]}}}();
================================================
FILE: inst/www/shared/highlight.js/rstudio.css
================================================
code {
line-height: 150%;
}
pre .operator,
pre .paren {
color: rgb(104, 118, 135)
}
pre .literal {
color: rgb(88, 72, 246)
}
pre .number {
color: rgb(0, 0, 205);
}
pre .comment {
color: rgb(76, 136, 107);
font-style: italic;
}
pre .keyword,
pre .id {
color: rgb(0, 0, 255);
}
pre .identifier {
color: rgb(0, 0, 0);
}
pre .string,
pre .attribute {
color: rgb(3, 106, 7);
}
pre .doctype {
color: rgb(104, 104, 92);
}
pre .tag,
pre .title {
color: rgb(4, 29, 140);
}
pre .value {
color: rgb(13, 105, 18);
}
.language-xml .attribute {
color: rgb(0, 0, 0);
}
.language-css .attribute {
color: rgb(110, 124, 219);
}
.language-css .value {
color: rgb(23, 149, 30);
}
.language-css .number,
.language-css .hexcolor {
color: rgb(7, 27, 201);
}
.language-css .function {
color: rgb(61, 77, 113);
}
.language-css .tag {
color: rgb(195, 13, 25);
}
.language-css .class {
color: rgb(53, 132, 148);
}
.language-css .pseudo {
color: rgb(13, 105, 18);
}
================================================
FILE: man/as_coverage.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/covr.R
\name{as_coverage}
\alias{as_coverage}
\title{Convert a counters object to a coverage object}
\usage{
as_coverage(counters = NULL, ...)
}
\arguments{
\item{counters}{An environment of covr trace results to convert to a coverage
object. If \code{counters} is not provided, the \code{covr} namespace value
\code{.counters} is used.}
\item{...}{Additional attributes to include with the coverage object.}
}
\description{
Convert a counters object to a coverage object
}
================================================
FILE: man/as_coverage_with_tests.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/covr.R
\name{as_coverage_with_tests}
\alias{as_coverage_with_tests}
\title{Clean and restructure counter tests for a coverage object}
\usage{
as_coverage_with_tests(counters)
}
\arguments{
\item{counters}{An environment of covr trace results to convert to a coverage
object. If \code{counters} is not provided, the \code{covr} namespace value
\code{.counters} is used.}
}
\description{
For tests produced with \code{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)
}
================================================
FILE: man/azure.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/azure.R
\name{azure}
\alias{azure}
\title{Run covr on a package and output the result so it is available on Azure Pipelines}
\usage{
azure(
...,
coverage = package_coverage(..., quiet = quiet),
filename = "coverage.xml",
quiet = TRUE
)
}
\arguments{
\item{...}{arguments passed to \code{\link[=package_coverage]{package_coverage()}}}
\item{coverage}{an existing coverage object to submit, if \code{NULL},
\code{\link[=package_coverage]{package_coverage()}} will be called with the arguments from
\code{...}}
\item{filename}{the name of the Cobertura XML file}
\item{quiet}{if \code{FALSE}, print the coverage before submission.}
}
\description{
Run covr on a package and output the result so it is available on Azure Pipelines
}
================================================
FILE: man/clear_counters.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/trace_calls.R
\name{clear_counters}
\alias{clear_counters}
\title{clear all previous counters}
\usage{
clear_counters()
}
\description{
clear all previous counters
}
\keyword{internal}
================================================
FILE: man/code_coverage.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/covr.R
\name{code_coverage}
\alias{code_coverage}
\title{Calculate coverage of code directly}
\usage{
code_coverage(
source_code,
test_code,
line_exclusions = NULL,
function_exclusions = NULL,
...
)
}
\arguments{
\item{source_code}{A character vector of source code}
\item{test_code}{A character vector of test code}
\item{line_exclusions}{a named list of files with the lines to exclude from
each file.}
\item{function_exclusions}{a vector of regular expressions matching function
names to exclude. Example \verb{print\\\\\\.} to match print methods.}
\item{...}{Additional arguments passed to \code{\link[=file_coverage]{file_coverage()}}}
}
\description{
This function is useful for testing, and is a thin wrapper around
\code{\link[=file_coverage]{file_coverage()}} because parseData is not populated properly
unless the functions are defined in a file.
}
\examples{
source <- "add <- function(x, y) { x + y }"
test <- "add(1, 2) == 3"
code_coverage(source, test)
}
================================================
FILE: man/codecov.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/codecov.R
\name{codecov}
\alias{codecov}
\title{Run covr on a package and upload the result to codecov.io}
\usage{
codecov(
...,
coverage = NULL,
base_url = "https://codecov.io",
token = NULL,
commit = NULL,
branch = NULL,
pr = NULL,
flags = NULL,
quiet = TRUE
)
}
\arguments{
\item{...}{arguments passed to \code{\link[=package_coverage]{package_coverage()}}}
\item{coverage}{an existing coverage object to submit, if \code{NULL},
\code{\link[=package_coverage]{package_coverage()}} will be called with the arguments from
\code{...}}
\item{base_url}{Codecov url (change for Enterprise)}
\item{token}{a codecov upload token, if \code{NULL} then following external
sources will be checked in this order:
\enumerate{
\item the environment variable \sQuote{CODECOV_TOKEN}. If it is empty, then
\item package will look at directory of the package for a file \code{codecov.yml}.
File must have \code{codecov} section where field \code{token} is set to a token that
will be used.
}}
\item{commit}{explicitly set the commit this coverage result object
corresponds to. Is looked up from the service or locally if it is
\code{NULL}.}
\item{branch}{explicitly set the branch this coverage result object
corresponds to, this is looked up from the service or locally if it is
\code{NULL}.}
\item{pr}{explicitly set the pr this coverage result object corresponds to,
this is looked up from the service if it is \code{NULL}.}
\item{flags}{A flag to use for this coverage upload see
\url{https://docs.codecov.com/docs/flags} for details.}
\item{quiet}{if \code{FALSE}, print the coverage before submission.}
}
\description{
Run covr on a package and upload the result to codecov.io
}
\examples{
\dontrun{
codecov(path = "test")
}
}
================================================
FILE: man/count.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/trace_calls.R
\name{count}
\alias{count}
\title{increment a given counter}
\usage{
count(key)
}
\arguments{
\item{key}{generated with \code{\link[=key]{key()}}}
}
\description{
increment a given counter
}
\keyword{internal}
================================================
FILE: man/count_test.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/trace_tests.R
\name{count_test}
\alias{count_test}
\title{Append a test trace to a counter, updating global current test}
\usage{
count_test(key)
}
\arguments{
\item{key}{generated with \code{\link[=key]{key()}}}
}
\description{
Append a test trace to a counter, updating global current test
}
\keyword{internal}
================================================
FILE: man/coverage_to_list.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/covr.R
\name{coverage_to_list}
\alias{coverage_to_list}
\title{Convert a coverage dataset to a list}
\usage{
coverage_to_list(x = package_coverage())
}
\arguments{
\item{x}{a coverage dataset, defaults to running \code{package_coverage()}.}
}
\value{
A list containing coverage result for each individual file and the whole package
}
\description{
Convert a coverage dataset to a list
}
================================================
FILE: man/coveralls.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/coveralls.R
\name{coveralls}
\alias{coveralls}
\title{Run covr on a package and upload the result to coveralls}
\usage{
coveralls(
...,
coverage = NULL,
repo_token = Sys.getenv("COVERALLS_TOKEN"),
service_name = Sys.getenv("CI_NAME", "travis-ci"),
quiet = TRUE
)
}
\arguments{
\item{...}{arguments passed to \code{\link[=package_coverage]{package_coverage()}}}
\item{coverage}{an existing coverage object to submit, if \code{NULL},
\code{\link[=package_coverage]{package_coverage()}} will be called with the arguments from
\code{...}}
\item{repo_token}{The secret repo token for your repository,
found at the bottom of your repository's page on Coveralls. This is useful
if your job is running on a service Coveralls doesn't support out-of-the-box.
If set to NULL, it is assumed that the job is running on travis-ci}
\item{service_name}{the CI service to use, if environment variable
\sQuote{CI_NAME} is set that is used, otherwise \sQuote{travis-ci} is used.}
\item{quiet}{if \code{FALSE}, print the coverage before submission.}
}
\description{
Run covr on a package and upload the result to coveralls
}
================================================
FILE: man/covr-package.Rd
================================================
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/covr.R
\docType{package}
\name{covr-package}
\alias{covr}
\alias{covr-package}
\title{covr: Test coverage for packages}
\description{
covr tracks and reports code coverage for your package and (optionally)
upload the results to a coverage service like 'Codecov' \url{https://about.codecov.io} or
'Coveralls' \url{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 code.
}
\details{
A coverage report can be used to inspect coverage for each line in your
package. Using \code{report()} requires the additional dependencies \code{DT} and \code{htmltools}.
\if{html}{\out{<div class="sourceCode r">}}\preformatted{# If run with no arguments `report()` implicitly calls `package_coverage()`
report()
}\if{html}{\out{</div>}}
}
\section{Package options}{
\code{covr} uses the following \code{\link[=options]{options()}} to configure behaviour:
\itemize{
\item \code{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
\code{COVR_COVRIGNORE}, or \code{".covrignore"} if the neither the option nor the
environment variable are set.
\item \code{covr.exclude_end}: Used along with \code{covr.exclude_start}, an optional
regular expression which ends a line-exclusion region. For more
details, see \code{?exclusions}.
\item \code{covr.exclude_pattern}: An optional line-exclusion pattern. Lines
which match the pattern will be excluded from coverage. For more details,
see \code{?exclusions}.
\item \code{covr.exclude_start}: Used along with \code{covr.exclude_end}, an optional
regular expression which starts a line-exclusion region. For more
details, see \code{?exclusions}.
\item \code{covr.filter_non_package}: If \code{TRUE} (the default behavior), coverage
of files outside the target package are filtered from coverage output.
\item \code{covr.fix_parallel_mcexit}:
\item \code{covr.flags}:
\item \code{covr.gcov}: 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.
\item \code{covr.gcov_additional_paths}:
\item \code{covr.gcov_args}:
\item \code{covr.icov}:
\item \code{covr.icov_args}:
\item \code{covr.icov_flags}:
\item \code{covr.icov_prof}:
\item \code{covr.rstudio_source_markers}: A logical value. If \code{TRUE} (the
default behavior), source markers are displayed within the RStudio IDE
when using \code{zero_coverage}.
\item \code{covr.record_tests}: If \code{TRUE} (default \code{NULL}), record a listing of
top level test expressions and associate tests with \code{covr} traces
evaluated during the test's execution. For more details, see
\code{?covr.record_tests}.
\item \code{covr.showCfunctions}:
}
}
\seealso{
Useful links:
\itemize{
\item \url{https://covr.r-lib.org}
\item \url{https://github.com/r-lib/covr}
\item Report bugs at \url{https://github.com/r-lib/covr/issues}
}
}
\author{
\strong{Maintainer}: Jim Hester \email{james.f.hester@gmail.com}
Oth
gitextract_nxw_371p/
├── .Rbuildignore
├── .gitattributes
├── .github/
│ ├── .gitignore
│ └── workflows/
│ ├── R-CMD-check.yaml
│ ├── pkgdown.yaml
│ ├── pr-commands.yaml
│ └── test-coverage.yaml
├── .gitignore
├── .lintr
├── CODE_OF_CONDUCT.md
├── DESCRIPTION
├── LICENSE
├── LICENSE.md
├── Makefile
├── NAMESPACE
├── NEWS.md
├── R/
│ ├── R6.R
│ ├── RC.R
│ ├── S4.R
│ ├── S7.R
│ ├── azure.R
│ ├── box.R
│ ├── cobertura.R
│ ├── codecov.R
│ ├── compiled.R
│ ├── coveralls.R
│ ├── covr.R
│ ├── data_frame.R
│ ├── display_name.R
│ ├── exclusions.R
│ ├── gitlab.R
│ ├── icc.R
│ ├── parallel.R
│ ├── parse_data.R
│ ├── replace.R
│ ├── report.R
│ ├── sonarqube.R
│ ├── summary_functions.R
│ ├── system.R
│ ├── trace_calls.R
│ ├── trace_tests.R
│ ├── utils.R
│ ├── value.R
│ ├── vectorized.R
│ └── zzz.R
├── README.md
├── SECURITY.md
├── _pkgdown.yml
├── codecov.yml
├── cran-comments.md
├── docker_checker/
│ └── Dockerfile
├── inst/
│ ├── rstudio/
│ │ └── addins.dcf
│ └── www/
│ ├── report.css
│ └── shared/
│ └── highlight.js/
│ ├── LICENSE
│ ├── highlight.pack.js
│ └── rstudio.css
├── man/
│ ├── as_coverage.Rd
│ ├── as_coverage_with_tests.Rd
│ ├── azure.Rd
│ ├── clear_counters.Rd
│ ├── code_coverage.Rd
│ ├── codecov.Rd
│ ├── count.Rd
│ ├── count_test.Rd
│ ├── coverage_to_list.Rd
│ ├── coveralls.Rd
│ ├── covr-package.Rd
│ ├── covr.record_tests.Rd
│ ├── current_test_call_count.Rd
│ ├── current_test_index.Rd
│ ├── current_test_key.Rd
│ ├── display_name.Rd
│ ├── environment_coverage.Rd
│ ├── exclusions.Rd
│ ├── file_coverage.Rd
│ ├── file_report.Rd
│ ├── function_coverage.Rd
│ ├── gitlab.Rd
│ ├── has_srcref.Rd
│ ├── in_covr.Rd
│ ├── is_covr_count_call.Rd
│ ├── is_current_test_finished.Rd
│ ├── key.Rd
│ ├── new_counter.Rd
│ ├── new_test_counter.Rd
│ ├── package_coverage.Rd
│ ├── percent_coverage.Rd
│ ├── print.coverage.Rd
│ ├── report.Rd
│ ├── system_check.Rd
│ ├── system_output.Rd
│ ├── tally_coverage.Rd
│ ├── to_cobertura.Rd
│ ├── to_sonarqube.Rd
│ ├── trace_calls.Rd
│ ├── truncate_call.Rd
│ ├── update_current_test.Rd
│ ├── value.Rd
│ └── zero_coverage.Rd
├── shim_package.sh
├── src/
│ └── reassign.c
├── tests/
│ ├── testthat/
│ │ ├── Test+Char/
│ │ │ └── TestCompiled/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestCompiled.R
│ │ │ ├── man/
│ │ │ │ └── simple.Rd
│ │ │ ├── src/
│ │ │ │ └── simple.cc
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestCompiled.R
│ │ │ └── testthat.R
│ │ ├── TestCompiled/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestCompiled.R
│ │ │ ├── man/
│ │ │ │ └── simple.Rd
│ │ │ ├── src/
│ │ │ │ ├── simple-header.h
│ │ │ │ ├── simple.cc
│ │ │ │ └── simple4.cc
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestCompiled.R
│ │ │ └── testthat.R
│ │ ├── TestCompiledSubdir/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestCompiledSubdir.R
│ │ │ ├── man/
│ │ │ │ └── simple.Rd
│ │ │ ├── src/
│ │ │ │ ├── Makevars
│ │ │ │ └── lib/
│ │ │ │ └── simple.c
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestCompiledSubdir.R
│ │ │ └── testthat.R
│ │ ├── TestExclusion/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestExclusion.R
│ │ │ ├── man/
│ │ │ │ └── test_me.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestExclusion.R
│ │ │ └── testthat.R
│ │ ├── TestFunctional/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── a.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-a.R
│ │ │ └── testthat.R
│ │ ├── TestNestedTestDirs/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── a.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ ├── nested_tests/
│ │ │ │ │ └── test-a.R
│ │ │ │ ├── test-a.R
│ │ │ │ └── test-nested-dir.R
│ │ │ └── testthat.R
│ │ ├── TestParallel/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestParallel.R
│ │ │ ├── man/
│ │ │ │ └── test_me.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestParallel.R
│ │ │ └── testthat.R
│ │ ├── TestPrint/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestPrint.R
│ │ │ ├── man/
│ │ │ │ └── test_me.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestSummary.R
│ │ │ └── testthat.R
│ │ ├── TestR6/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestR6.R
│ │ │ ├── man/
│ │ │ │ └── a.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestR6.R
│ │ │ └── testthat.R
│ │ ├── TestRC/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestRC.R
│ │ │ ├── man/
│ │ │ │ └── a.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestRC.R
│ │ │ └── testthat.R
│ │ ├── TestS4/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestS4.R
│ │ │ ├── codecov.yml
│ │ │ ├── man/
│ │ │ │ └── a.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestS4.R
│ │ │ └── testthat.R
│ │ ├── TestS7/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── foo.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-foo.R
│ │ │ └── testthat.R
│ │ ├── TestSummary/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── TestSummary.R
│ │ │ ├── man/
│ │ │ │ └── test_me.Rd
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-TestSummary.R
│ │ │ └── testthat.R
│ │ ├── TestUseTry/
│ │ │ ├── DESCRIPTION
│ │ │ ├── NAMESPACE
│ │ │ ├── R/
│ │ │ │ └── notry.R
│ │ │ └── tests/
│ │ │ ├── tests.R
│ │ │ └── testthat/
│ │ │ └── test-notry.R
│ │ ├── Testbox/
│ │ │ ├── app/
│ │ │ │ ├── app.R
│ │ │ │ └── modules/
│ │ │ │ └── module.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-module.R
│ │ │ └── testthat.R
│ │ ├── Testbox_R6/
│ │ │ ├── app/
│ │ │ │ ├── app.R
│ │ │ │ └── modules/
│ │ │ │ └── moduleR6.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-moduleR6.R
│ │ │ └── testthat.R
│ │ ├── Testbox_attached_modules_functions/
│ │ │ ├── app/
│ │ │ │ ├── app.R
│ │ │ │ └── modules/
│ │ │ │ └── module.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ ├── test-aliased_functions.R
│ │ │ │ ├── test-aliased_modules.R
│ │ │ │ ├── test-attached_functions.R
│ │ │ │ └── test-three_dots.R
│ │ │ └── testthat.R
│ │ ├── Testbox_attached_modules_functions_R6/
│ │ │ ├── app/
│ │ │ │ ├── app.R
│ │ │ │ └── modules/
│ │ │ │ └── moduleR6.R
│ │ │ └── tests/
│ │ │ ├── testthat/
│ │ │ │ └── test-attached_R6.R
│ │ │ └── testthat.R
│ │ ├── _snaps/
│ │ │ ├── Compiled.md
│ │ │ └── S7.md
│ │ ├── a
│ │ ├── b
│ │ ├── cobertura.xml
│ │ ├── corner-cases-test.R
│ │ ├── corner-cases.R
│ │ ├── corner-cases.Rds
│ │ ├── helper.R
│ │ ├── sonarqube.xml
│ │ ├── test-Compiled.R
│ │ ├── test-R6.R
│ │ ├── test-RC.R
│ │ ├── test-S4.R
│ │ ├── test-S7.R
│ │ ├── test-azure.R
│ │ ├── test-box-R6.R
│ │ ├── test-box.R
│ │ ├── test-box_attached_modules_functions-R6.R
│ │ ├── test-box_attached_modules_functions.R
│ │ ├── test-braceless.R
│ │ ├── test-cobertura.R
│ │ ├── test-codecov.R
│ │ ├── test-corner-cases.R
│ │ ├── test-coveralls.R
│ │ ├── test-covr.R
│ │ ├── test-exclusions.R
│ │ ├── test-file_coverage.R
│ │ ├── test-functions.R
│ │ ├── test-gcov.R
│ │ ├── test-gitlab.R
│ │ ├── test-memoised.R
│ │ ├── test-null.R
│ │ ├── test-package_coverage.R
│ │ ├── test-parallel.R
│ │ ├── test-print.R
│ │ ├── test-record_tests.R
│ │ ├── test-report.R
│ │ ├── test-report.htm
│ │ ├── test-sonarqube.R
│ │ ├── test-summary.R
│ │ ├── test-trace_calls.R
│ │ ├── test-utils.R
│ │ └── test-vectorized.R
│ └── testthat.R
├── unshim_package.sh
└── vignettes/
└── how_it_works.Rmd
SYMBOL INDEX (25 symbols across 6 files)
FILE: inst/www/shared/highlight.js/highlight.pack.js
function m (line 1) | function m(p){return p.replace(/&/gm,"&").replace(/</gm,"<")}
function f (line 1) | function f(r,q,p){return RegExp(q,"m"+(r.cI?"i":"")+(p?"g":""))}
function b (line 1) | function b(r){for(var p=0;p<r.childNodes.length;p++){var q=r.childNodes[...
function h (line 1) | function h(t,s){var p="";for(var r=0;r<t.childNodes.length;r++){if(t.chi...
function a (line 1) | function a(s){var r=s.className.split(/\s+/);r=r.concat(s.parentNode.cla...
function c (line 1) | function c(q){var p=[];(function(s,t){for(var r=0;r<s.childNodes.length;...
function k (line 1) | function k(y,w,x){var q=0;var z="";var s=[];function u(){if(y.length&&w....
function j (line 1) | function j(){function q(x,y,v){if(x.compiled){return}var u;var s=[];if(x...
function d (line 1) | function d(B,C){if(!j.called){j();j.called=true}function q(r,M){for(var ...
function g (line 1) | function g(t){var p={keyword_count:0,r:0,value:m(t)};var r=p;for(var q i...
function i (line 1) | function i(r,q,p){if(q){r=r.replace(/^((<[^>]+>|\t)+)/gm,function(t,w,v,...
function n (line 1) | function n(t,w,r){var x=h(t,r);var v=a(t);var y,s;if(v){y=d(v,x)}else{re...
function o (line 1) | function o(){if(o.called){return}o.called=true;var r=document.getElement...
function l (line 1) | function l(){if(window.addEventListener){window.addEventListener("DOMCon...
FILE: src/reassign.c
function CheckBody (line 11) | inline static
function CheckEnvironment (line 45) | inline static
function CheckFormals (line 51) | inline static
function SEXP (line 65) | SEXP covr_reassign_function(SEXP old_fun, SEXP new_fun) {
function SEXP (line 138) | SEXP covr_duplicate_(SEXP x) { return duplicate(x); }
function R_init_covr (line 149) | void R_init_covr(DllInfo *dll) {
FILE: tests/testthat/Test+Char/TestCompiled/src/simple.cc
function SEXP (line 6) | SEXP simple_(SEXP x) {
FILE: tests/testthat/TestCompiled/src/simple.cc
function SEXP (line 7) | SEXP simple_(SEXP x) {
function SEXP (line 28) | SEXP simple3_(SEXP x) {
FILE: tests/testthat/TestCompiled/src/simple4.cc
function SEXP (line 7) | SEXP simple4_(SEXP x) {
FILE: tests/testthat/TestCompiledSubdir/src/lib/simple.c
function SEXP (line 6) | SEXP simple_(SEXP x) {
Condensed preview — 256 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (378K chars).
[
{
"path": ".Rbuildignore",
"chars": 550,
"preview": "^covr\\.Rproj$\n\\.tar\\.gz$\n^travis-tool\\.sh$\n^LICENSE\\.md$\n^covr\\.Rcheck$\n^\\.travis\\.yml$\n^shim_package\\.sh$\n^unshim_packa"
},
{
"path": ".gitattributes",
"chars": 21,
"preview": "/NEWS.md merge=union\n"
},
{
"path": ".github/.gitignore",
"chars": 7,
"preview": "*.html\n"
},
{
"path": ".github/workflows/R-CMD-check.yaml",
"chars": 1864,
"preview": "# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples\n# Need help debugging build failures? Start at"
},
{
"path": ".github/workflows/pkgdown.yaml",
"chars": 1301,
"preview": "# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples\n# Need help debugging build failures? Start at"
},
{
"path": ".github/workflows/pr-commands.yaml",
"chars": 2392,
"preview": "# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples\n# Need help debugging build failures? Start at"
},
{
"path": ".github/workflows/test-coverage.yaml",
"chars": 1402,
"preview": "# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples\n# Need help debugging build failures? Start at"
},
{
"path": ".gitignore",
"chars": 142,
"preview": "*.o\n*.so\ninst/doc\n.Rproj.user\ncovr.Rproj\ncache/\ndata.Rmd\nvignettes/covr_performance.Rmd\nrevdep/checks\nrevdep/library\ndoc"
},
{
"path": ".lintr",
"chars": 100,
"preview": "linters: linters_with_defaults(line_length_linter(120))\nexclusions: list(\"inst/doc/how_it_works.R\")\n"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5246,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "DESCRIPTION",
"chars": 2789,
"preview": "Encoding: UTF-8\nPackage: covr\nTitle: Test Coverage for Packages\nVersion: 3.6.5.9001\nAuthors@R: c(\n person(\"Jim\", \"Hes"
},
{
"path": "LICENSE",
"chars": 42,
"preview": "YEAR: 2022\nCOPYRIGHT HOLDER: covr authors\n"
},
{
"path": "LICENSE.md",
"chars": 1071,
"preview": "# MIT License\n\nCopyright (c) 2022 covr authors\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "Makefile",
"chars": 904,
"preview": "RCHECKER=docker_checker\nFILTER=\n\n# to install test packages, code must be compiled inside src/ dir, \n# that may cause pr"
},
{
"path": "NAMESPACE",
"chars": 1361,
"preview": "# Generated by roxygen2: do not edit by hand\n\nS3method(\"[\",coverage)\nS3method(as.data.frame,coverage)\nS3method(markers,c"
},
{
"path": "NEWS.md",
"chars": 11192,
"preview": "# covr (development version)\n\n* Fix a rare edge case where `count_test` was called before `.current_test` has\n been ini"
},
{
"path": "R/R6.R",
"chars": 638,
"preview": "replacements_R6 <- function(env) {\n unlist(recursive = FALSE, eapply(env, all.names = TRUE,\n function(obj) {\n i"
},
{
"path": "R/RC.R",
"chars": 355,
"preview": "replacements_RC <- function(env) {\n pat <- paste0(\"^\", classMetaName(\"\"))\n unlist(recursive = FALSE, lapply(ls(env, pa"
},
{
"path": "R/S4.R",
"chars": 388,
"preview": "replacements_S4 <- function(env) {\n generics <- getGenerics(env)\n\n unlist(recursive = FALSE,\n Map(generics@.Data, g"
},
{
"path": "R/S7.R",
"chars": 2996,
"preview": "replacements_S7 <- function(env) {\n bindings <- unlist(recursive = FALSE, use.names = FALSE, eapply(env, all.names = TR"
},
{
"path": "R/azure.R",
"chars": 322,
"preview": "#' Run covr on a package and output the result so it is available on Azure Pipelines\n#' @inheritParams codecov\n#' @inher"
},
{
"path": "R/box.R",
"chars": 936,
"preview": "replacements_box <- function(env) {\n unlist(recursive = FALSE, eapply(env, all.names = TRUE,\n function(obj) {\n "
},
{
"path": "R/cobertura.R",
"chars": 3935,
"preview": "#' Create a Cobertura XML file\n#'\n#' Create a\n#' cobertura-compliant XML report following [this\n#' DTD](https://github.c"
},
{
"path": "R/codecov.R",
"chars": 11552,
"preview": "#' Run covr on a package and upload the result to codecov.io\n#' @param coverage an existing coverage object to submit, i"
},
{
"path": "R/compiled.R",
"chars": 4471,
"preview": "# this does not handle LCOV_EXCL_START ect.\nparse_gcov <- function(file, package_path = \"\") {\n if (!file.exists(file)) "
},
{
"path": "R/coveralls.R",
"chars": 3756,
"preview": "#' Run covr on a package and upload the result to coveralls\n#' @param coverage an existing coverage object to submit, if"
},
{
"path": "R/covr.R",
"chars": 27354,
"preview": "#' covr: Test coverage for packages\n#'\n#' covr tracks and reports code coverage for your package and (optionally)\n#' upl"
},
{
"path": "R/data_frame.R",
"chars": 1471,
"preview": "#' @export\nas.data.frame.coverage <- function(x, row.names = NULL, optional = FALSE, sort = TRUE, ...) {\n column_names "
},
{
"path": "R/display_name.R",
"chars": 726,
"preview": "#' Retrieve the path name (filename) for each coverage object\n#'\n#' @param x A coverage object\n#' @keywords internal\n#' "
},
{
"path": "R/exclusions.R",
"chars": 6385,
"preview": "#' Exclusions\n#'\n#' covr supports a couple of different ways of excluding some or all of a file.\n#'\n#' @section Line Exc"
},
{
"path": "R/gitlab.R",
"chars": 842,
"preview": "#' Run covr on package and create report for GitLab\n#'\n#' Utilize internal GitLab static pages to publish package covera"
},
{
"path": "R/icc.R",
"chars": 4167,
"preview": "parse_icov <- function(lines, package_path = \"\") {\n\n source_file <- trim_ws(lines[1L])\n # If the source file does not "
},
{
"path": "R/parallel.R",
"chars": 1421,
"preview": "# utility function to replace a symbol in a locked loaded package/namespace\nreplace_binding <- function(package, name, v"
},
{
"path": "R/parse_data.R",
"chars": 4169,
"preview": "#' @importFrom utils getParseData getSrcref tail\nimpute_srcref <- function(x, parent_ref) {\n if (!is_conditional_or_loo"
},
{
"path": "R/replace.R",
"chars": 1244,
"preview": "#' @useDynLib covr, .registration = TRUE\nreplacement <- function(name, env = as.environment(-1), target_value = get(name"
},
{
"path": "R/report.R",
"chars": 10188,
"preview": "#' Display covr results using a standalone report\n#'\n#' @param x a coverage dataset, defaults to running `package_covera"
},
{
"path": "R/sonarqube.R",
"chars": 1194,
"preview": "#' Create a SonarQube Generic XML file for test coverage according to\n#' https://docs.sonarqube.org/latest/analysis/gene"
},
{
"path": "R/summary_functions.R",
"chars": 7490,
"preview": "#' Provide percent coverage of package\n#'\n#' Calculate the total percent coverage from a coverage result object.\n#' @par"
},
{
"path": "R/system.R",
"chars": 2122,
"preview": "#' Run a system command and check if it succeeds.\n#'\n#' This function automatically quotes both the command and each\n#' "
},
{
"path": "R/trace_calls.R",
"chars": 4223,
"preview": "#' trace each call with a srcref attribute\n#'\n#' This function calls itself recursively so it can properly traverse the "
},
{
"path": "R/trace_tests.R",
"chars": 13151,
"preview": "#' Record Test Traces During Coverage Execution\n#'\n#' By setting `options(covr.record_tests = TRUE)`, the result of covr"
},
{
"path": "R/utils.R",
"chars": 9732,
"preview": "`%||%` <- function(x, y) {\n if (!is.null(x)) {\n x\n } else {\n y\n }\n}\n\ncompact <- function(x) {\n x[viapply(x, le"
},
{
"path": "R/value.R",
"chars": 527,
"preview": "#' Retrieve the value from an object\n#' @export\n#' @param x object from which to retrieve the value\n#' @param ... additi"
},
{
"path": "R/vectorized.R",
"chars": 200,
"preview": "# simple function to test if a function is Vectorized\nis_vectorized <- function(x) {\n is.function(x) && exists(\"FUN\", e"
},
{
"path": "R/zzz.R",
"chars": 2013,
"preview": ".onLoad <- function(libname, pkgname) { # nolint\n rex::register_shortcuts(\"covr\")\n op <- options()\n op_covr <- list(\n"
},
{
"path": "README.md",
"chars": 6180,
"preview": "# covr <img src=\"man/figures/logo.png\" align=\"right\" />\n\n<!-- badges: start -->\n[ 2006, Ivan Sagalaev\nAll rights reserved.\nRedistribution and use in source and binary forms, with or withou"
},
{
"path": "inst/www/shared/highlight.js/highlight.pack.js",
"chars": 11276,
"preview": "var hljs=new function(){function m(p){return p.replace(/&/gm,\"&\").replace(/</gm,\"<\")}function f(r,q,p){return Reg"
},
{
"path": "inst/www/shared/highlight.js/rstudio.css",
"chars": 996,
"preview": "code {\n line-height: 150%;\n}\n\npre .operator,\npre .paren {\n color: rgb(104, 118, 135)\n}\n\npre .literal {\n color: rgb(88"
},
{
"path": "man/as_coverage.Rd",
"chars": 553,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/covr.R\n\\name{as_coverage}\n\\alias{as_covera"
},
{
"path": "man/as_coverage_with_tests.Rd",
"chars": 681,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/covr.R\n\\name{as_coverage_with_tests}\n\\alia"
},
{
"path": "man/azure.Rd",
"chars": 818,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/azure.R\n\\name{azure}\n\\alias{azure}\n\\title{"
},
{
"path": "man/clear_counters.Rd",
"chars": 263,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_calls.R\n\\name{clear_counters}\n\\alias"
},
{
"path": "man/code_coverage.Rd",
"chars": 1062,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/covr.R\n\\name{code_coverage}\n\\alias{code_co"
},
{
"path": "man/codecov.Rd",
"chars": 1821,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/codecov.R\n\\name{codecov}\n\\alias{codecov}\n\\"
},
{
"path": "man/count.Rd",
"chars": 302,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_calls.R\n\\name{count}\n\\alias{count}\n\\"
},
{
"path": "man/count_test.Rd",
"chars": 391,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_tests.R\n\\name{count_test}\n\\alias{cou"
},
{
"path": "man/coverage_to_list.Rd",
"chars": 465,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/covr.R\n\\name{coverage_to_list}\n\\alias{cove"
},
{
"path": "man/coveralls.Rd",
"chars": 1197,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/coveralls.R\n\\name{coveralls}\n\\alias{covera"
},
{
"path": "man/covr-package.Rd",
"chars": 4510,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/covr.R\n\\docType{package}\n\\name{covr-packag"
},
{
"path": "man/covr.record_tests.Rd",
"chars": 3175,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_tests.R\n\\name{covr.record_tests}\n\\al"
},
{
"path": "man/current_test_call_count.Rd",
"chars": 518,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_tests.R\n\\name{current_test_call_coun"
},
{
"path": "man/current_test_index.Rd",
"chars": 505,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_tests.R\n\\name{current_test_index}\n\\a"
},
{
"path": "man/current_test_key.Rd",
"chars": 464,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_tests.R\n\\name{current_test_key}\n\\ali"
},
{
"path": "man/display_name.Rd",
"chars": 363,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/display_name.R\n\\name{display_name}\n\\alias{"
},
{
"path": "man/environment_coverage.Rd",
"chars": 750,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/covr.R\n\\name{environment_coverage}\n\\alias{"
},
{
"path": "man/exclusions.Rd",
"chars": 1771,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/exclusions.R\n\\name{exclusions}\n\\alias{excl"
},
{
"path": "man/file_coverage.Rd",
"chars": 1400,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/covr.R\n\\name{file_coverage}\n\\alias{file_co"
},
{
"path": "man/file_report.Rd",
"chars": 685,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/report.R\n\\name{file_report}\n\\alias{file_re"
},
{
"path": "man/function_coverage.Rd",
"chars": 713,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/covr.R\n\\name{function_coverage}\n\\alias{fun"
},
{
"path": "man/gitlab.Rd",
"chars": 871,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/gitlab.R\n\\name{gitlab}\n\\alias{gitlab}\n\\tit"
},
{
"path": "man/has_srcref.Rd",
"chars": 418,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_tests.R\n\\name{has_srcref}\n\\alias{has"
},
{
"path": "man/in_covr.Rd",
"chars": 469,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/covr.R\n\\name{in_covr}\n\\alias{in_covr}\n\\tit"
},
{
"path": "man/is_covr_count_call.Rd",
"chars": 421,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_tests.R\n\\name{is_covr_count_call}\n\\a"
},
{
"path": "man/is_current_test_finished.Rd",
"chars": 660,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_tests.R\n\\name{is_current_test_finish"
},
{
"path": "man/key.Rd",
"chars": 296,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_calls.R\n\\name{key}\n\\alias{key}\n\\titl"
},
{
"path": "man/new_counter.Rd",
"chars": 422,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_calls.R\n\\name{new_counter}\n\\alias{ne"
},
{
"path": "man/new_test_counter.Rd",
"chars": 892,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_tests.R\n\\name{new_test_counter}\n\\ali"
},
{
"path": "man/package_coverage.Rd",
"chars": 3124,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/covr.R\n\\name{package_coverage}\n\\alias{pack"
},
{
"path": "man/percent_coverage.Rd",
"chars": 567,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/summary_functions.R\n\\name{percent_coverage"
},
{
"path": "man/print.coverage.Rd",
"chars": 575,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/summary_functions.R\n\\name{print.coverage}\n"
},
{
"path": "man/report.Rd",
"chars": 628,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/report.R\n\\name{report}\n\\alias{report}\n\\tit"
},
{
"path": "man/system_check.Rd",
"chars": 937,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/system.R\n\\name{system_check}\n\\alias{system"
},
{
"path": "man/system_output.Rd",
"chars": 941,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/system.R\n\\name{system_output}\n\\alias{syste"
},
{
"path": "man/tally_coverage.Rd",
"chars": 542,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/summary_functions.R\n\\name{tally_coverage}\n"
},
{
"path": "man/to_cobertura.Rd",
"chars": 1027,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/cobertura.R\n\\name{to_cobertura}\n\\alias{to_"
},
{
"path": "man/to_sonarqube.Rd",
"chars": 613,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/sonarqube.R\n\\name{to_sonarqube}\n\\alias{to_"
},
{
"path": "man/trace_calls.Rd",
"chars": 700,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_calls.R\n\\name{trace_calls}\n\\alias{tr"
},
{
"path": "man/truncate_call.Rd",
"chars": 630,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_tests.R\n\\name{truncate_call}\n\\alias{"
},
{
"path": "man/update_current_test.Rd",
"chars": 1236,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/trace_tests.R\n\\name{update_current_test}\n\\"
},
{
"path": "man/value.Rd",
"chars": 345,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/value.R\n\\name{value}\n\\alias{value}\n\\title{"
},
{
"path": "man/zero_coverage.Rd",
"chars": 728,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/summary_functions.R\n\\name{zero_coverage}\n\\"
},
{
"path": "shim_package.sh",
"chars": 157,
"preview": "#!/usr/bin/env sh\n\nperl -i -pe 's/\\bcovr\\b/covrShim/g;s/\\bcovr_/covrShim_/g;s/_covr/_covrShim/g;' DESCRIPTION NAMESPACE "
},
{
"path": "src/reassign.c",
"chars": 4196,
"preview": "#define USE_RINTERNALS\n#include <R.h>\n#include <Rinternals.h>\n#include <R_ext/Error.h>\n#include <R_ext/Rdynload.h>\n#incl"
},
{
"path": "tests/testthat/Test+Char/TestCompiled/DESCRIPTION",
"chars": 323,
"preview": "Package: TestCompiled\nTitle: What the Package Does (one line, title case)\nVersion: 0.0.0.9000\nAuthors@R: \"First Last <fi"
},
{
"path": "tests/testthat/Test+Char/TestCompiled/NAMESPACE",
"chars": 78,
"preview": "# Generated by roxygen2: do not edit by hand\n\nuseDynLib(TestCompiled,simple_)\n"
},
{
"path": "tests/testthat/Test+Char/TestCompiled/R/TestCompiled.R",
"chars": 116,
"preview": "#' an example function\n#'\n#' @useDynLib TestCompiled simple_\nsimple <- function(x) {\n .Call(simple_, x) # nolint\n}\n"
},
{
"path": "tests/testthat/Test+Char/TestCompiled/man/simple.Rd",
"chars": 206,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/TestCompiled.R\n\\name{simple}\n\\alias{simple"
},
{
"path": "tests/testthat/Test+Char/TestCompiled/src/simple.cc",
"chars": 379,
"preview": "#define USE_RINTERNALS\n#include <R.h>\n#include <Rdefines.h>\n#include <R_ext/Error.h>\n\nextern \"C\" SEXP simple_(SEXP x) {\n"
},
{
"path": "tests/testthat/Test+Char/TestCompiled/tests/testthat/test-TestCompiled.R",
"chars": 167,
"preview": "test_that(\"compiled function simple works\", {\n expect_equal(simple(1), 1)\n expect_equal(simple(2), 1)\n expect_equal(s"
},
{
"path": "tests/testthat/Test+Char/TestCompiled/tests/testthat.R",
"chars": 70,
"preview": "library(testthat)\nlibrary(\"TestCompiled\")\n\ntest_check(\"TestCompiled\")\n"
},
{
"path": "tests/testthat/TestCompiled/DESCRIPTION",
"chars": 323,
"preview": "Package: TestCompiled\nTitle: What the Package Does (one line, title case)\nVersion: 0.0.0.9000\nAuthors@R: \"First Last <fi"
},
{
"path": "tests/testthat/TestCompiled/NAMESPACE",
"chars": 144,
"preview": "# Generated by roxygen2: do not edit by hand\n\nuseDynLib(TestCompiled,simple_)\nuseDynLib(TestCompiled,simple3_)\nuseDynLib"
},
{
"path": "tests/testthat/TestCompiled/R/TestCompiled.R",
"chars": 232,
"preview": "#' an example function\n#'\n#' @useDynLib TestCompiled simple_\nsimple <- function(x) {\n .Call(simple_, x) # nolint\n}\n\nsim"
},
{
"path": "tests/testthat/TestCompiled/man/simple.Rd",
"chars": 207,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/TestCompiled.R\n\\name{simple}\n\\alias{simple"
},
{
"path": "tests/testthat/TestCompiled/src/simple-header.h",
"chars": 427,
"preview": "#pragma once\n\n#define USE_RINTERNALS\n#include <R.h>\n#include <Rdefines.h>\n#include <R_ext/Error.h>\n\ntemplate <typename R"
},
{
"path": "tests/testthat/TestCompiled/src/simple.cc",
"chars": 483,
"preview": "#define USE_RINTERNALS\n#include <R.h>\n#include <Rdefines.h>\n#include <R_ext/Error.h>\n#include \"simple-header.h\"\n\nextern "
},
{
"path": "tests/testthat/TestCompiled/src/simple4.cc",
"chars": 185,
"preview": "#define USE_RINTERNALS\n#include <R.h>\n#include <Rdefines.h>\n#include <R_ext/Error.h>\n#include \"simple-header.h\"\n\nextern "
},
{
"path": "tests/testthat/TestCompiled/tests/testthat/test-TestCompiled.R",
"chars": 395,
"preview": "test_that(\"compiled function simple works\", {\n expect_equal(simple(1), 1)\n expect_equal(simple(2), 1)\n expect_equal(s"
},
{
"path": "tests/testthat/TestCompiled/tests/testthat.R",
"chars": 70,
"preview": "library(testthat)\nlibrary(\"TestCompiled\")\n\ntest_check(\"TestCompiled\")\n"
},
{
"path": "tests/testthat/TestCompiledSubdir/DESCRIPTION",
"chars": 310,
"preview": "Package: TestCompiledSubdir\nTitle: What the Package Does (one line, title case)\nVersion: 0.0.0.9000\nAuthors@R: \"First La"
},
{
"path": "tests/testthat/TestCompiledSubdir/NAMESPACE",
"chars": 92,
"preview": "# Generated by roxygen2 (4.1.1): do not edit by hand\n\nuseDynLib(TestCompiledSubdir,simple_)\n"
},
{
"path": "tests/testthat/TestCompiledSubdir/R/TestCompiledSubdir.R",
"chars": 122,
"preview": "#' an example function\n#'\n#' @useDynLib TestCompiledSubdir simple_\nsimple <- function(x) {\n .Call(simple_, x) # nolint\n"
},
{
"path": "tests/testthat/TestCompiledSubdir/man/simple.Rd",
"chars": 221,
"preview": "% Generated by roxygen2 (4.1.1): do not edit by hand\n% Please edit documentation in R/TestCompiledSubdir.R\n\\name{simple}"
},
{
"path": "tests/testthat/TestCompiledSubdir/src/Makevars",
"chars": 23,
"preview": "OBJECTS = lib/simple.o\n"
},
{
"path": "tests/testthat/TestCompiledSubdir/src/lib/simple.c",
"chars": 368,
"preview": "#define USE_RINTERNALS\n#include <R.h>\n#include <Rdefines.h>\n#include <R_ext/Error.h>\n\nSEXP simple_(SEXP x) {\n double *p"
},
{
"path": "tests/testthat/TestCompiledSubdir/tests/testthat/test-TestCompiledSubdir.R",
"chars": 167,
"preview": "test_that(\"compiled function simple works\", {\n expect_equal(simple(1), 1)\n expect_equal(simple(2), 1)\n expect_equal(s"
},
{
"path": "tests/testthat/TestCompiledSubdir/tests/testthat.R",
"chars": 82,
"preview": "library(testthat)\nlibrary(\"TestCompiledSubdir\")\n\ntest_check(\"TestCompiledSubdir\")\n"
},
{
"path": "tests/testthat/TestExclusion/DESCRIPTION",
"chars": 282,
"preview": "Package: TestExclusion\nTitle: What the Package Does (one line, title case)\nVersion: 0.0.0.9000\nAuthors@R: \"First Last <f"
},
{
"path": "tests/testthat/TestExclusion/NAMESPACE",
"chars": 114,
"preview": "# Generated by roxygen2 (4.1.1): do not edit by hand\n\nexport(dont_test_me)\nexport(test_exclusion)\nexport(test_me)\n"
},
{
"path": "tests/testthat/TestExclusion/R/TestExclusion.R",
"chars": 250,
"preview": "#' an example function\n#'\n#' @export\ntest_me <- function(x, y){\n x + y\n}\n\n# nocov start\n#' @export\ndont_test_me <- func"
},
{
"path": "tests/testthat/TestExclusion/man/test_me.Rd",
"chars": 222,
"preview": "% Generated by roxygen2 (4.1.1): do not edit by hand\n% Please edit documentation in R/TestExclusion.R\n\\name{test_me}\n\\al"
},
{
"path": "tests/testthat/TestExclusion/tests/testthat/test-TestExclusion.R",
"chars": 102,
"preview": "test_that(\"test_me works\", {\n expect_equal(test_me(2, 2), 4)\n expect_equal(test_exclusion(1), 2)\n})\n"
},
{
"path": "tests/testthat/TestExclusion/tests/testthat.R",
"chars": 72,
"preview": "library(testthat)\nlibrary(\"TestExclusion\")\n\ntest_check(\"TestExclusion\")\n"
},
{
"path": "tests/testthat/TestFunctional/DESCRIPTION",
"chars": 325,
"preview": "Package: TestFunctional\nTitle: What the Package Does (one line, title case)\nVersion: 0.0.0.9000\nAuthors@R: \"First Last <"
},
{
"path": "tests/testthat/TestFunctional/NAMESPACE",
"chars": 66,
"preview": "# Generated by roxygen2: do not edit by hand\n\nexport(a)\nexport(b)\n"
},
{
"path": "tests/testthat/TestFunctional/R/a.R",
"chars": 180,
"preview": "foo <- function(x) {\n force(x)\n function() {\n if (x < 1)\n {\n return(TRUE)\n } else {\n return(FALSE)\n"
},
{
"path": "tests/testthat/TestFunctional/tests/testthat/test-a.R",
"chars": 110,
"preview": "test_that(\"regular function `a` works as expected\", {\n expect_equal(a(), TRUE)\n expect_equal(b(), FALSE)\n})\n"
},
{
"path": "tests/testthat/TestFunctional/tests/testthat.R",
"chars": 74,
"preview": "library(testthat)\nlibrary(\"TestFunctional\")\n\ntest_check(\"TestFunctional\")\n"
},
{
"path": "tests/testthat/TestNestedTestDirs/DESCRIPTION",
"chars": 329,
"preview": "Package: TestNestedTestDirs\nTitle: What the Package Does (one line, title case)\nVersion: 0.0.0.9000\nAuthors@R: \"First La"
},
{
"path": "tests/testthat/TestNestedTestDirs/NAMESPACE",
"chars": 66,
"preview": "# Generated by roxygen2: do not edit by hand\n\nexport(a)\nexport(b)\n"
},
{
"path": "tests/testthat/TestNestedTestDirs/R/a.R",
"chars": 180,
"preview": "foo <- function(x) {\n force(x)\n function() {\n if (x < 1)\n {\n return(TRUE)\n } else {\n return(FALSE)\n"
},
{
"path": "tests/testthat/TestNestedTestDirs/tests/testthat/nested_tests/test-a.R",
"chars": 110,
"preview": "test_that(\"regular function `a` works as expected\", {\n expect_equal(a(), TRUE)\n expect_equal(b(), FALSE)\n})\n"
},
{
"path": "tests/testthat/TestNestedTestDirs/tests/testthat/test-a.R",
"chars": 110,
"preview": "test_that(\"regular function `a` works as expected\", {\n expect_equal(a(), TRUE)\n expect_equal(b(), FALSE)\n})\n"
},
{
"path": "tests/testthat/TestNestedTestDirs/tests/testthat/test-nested-dir.R",
"chars": 301,
"preview": "# used for testing the extraction of srcrefs pertaining to tests, which assumes \n# srcrefs within working directory\n\nif "
},
{
"path": "tests/testthat/TestNestedTestDirs/tests/testthat.R",
"chars": 82,
"preview": "library(testthat)\nlibrary(\"TestNestedTestDirs\")\n\ntest_check(\"TestNestedTestDirs\")\n"
},
{
"path": "tests/testthat/TestParallel/DESCRIPTION",
"chars": 300,
"preview": "Package: TestParallel\nTitle: What the Package Does (one line, title case)\nVersion: 0.0.0.9000\nAuthors@R: \"First Last <fi"
},
{
"path": "tests/testthat/TestParallel/NAMESPACE",
"chars": 95,
"preview": "# Generated by roxygen2 (4.1.1): do not edit by hand\n\nexport(test1)\nexport(test2)\nexport(test3)"
},
{
"path": "tests/testthat/TestParallel/R/TestParallel.R",
"chars": 165,
"preview": "#' an example function\n#'\n#' @export\ntest1 <- function(x, y){\n x + y\n}\n\n#' @export\ntest2 <- function(x, y){\n x * y\n}\n\n"
},
{
"path": "tests/testthat/TestParallel/man/test_me.Rd",
"chars": 220,
"preview": "% Generated by roxygen2 (4.1.1): do not edit by hand\n% Please edit documentation in R/TestSummary.R\n\\name{test_me}\n\\alia"
},
{
"path": "tests/testthat/TestParallel/tests/testthat/test-TestParallel.R",
"chars": 212,
"preview": "test_that(\"test_me works\", {\n library(parallel)\n\n mccollect(mcparallel(\n expect_equal(test1(2, 2), 4)\n ))\n\n mccol"
},
{
"path": "tests/testthat/TestParallel/tests/testthat.R",
"chars": 70,
"preview": "library(testthat)\nlibrary(\"TestParallel\")\n\ntest_check(\"TestParallel\")\n"
},
{
"path": "tests/testthat/TestPrint/DESCRIPTION",
"chars": 278,
"preview": "Package: TestPrint\nTitle: What the Package Does (one line, title case)\nVersion: 0.0.0.9000\nAuthors@R: \"First Last <first"
},
{
"path": "tests/testthat/TestPrint/NAMESPACE",
"chars": 70,
"preview": "# Generated by roxygen2 (4.1.1): do not edit by hand\n\nexport(test_me)\n"
},
{
"path": "tests/testthat/TestPrint/R/TestPrint.R",
"chars": 109,
"preview": "#' an example function\n#'\n#' @export\ntest_me <- function(x, y) {\n if (TRUE) { x + y } else { 0 } # nolint\n}\n"
},
{
"path": "tests/testthat/TestPrint/man/test_me.Rd",
"chars": 218,
"preview": "% Generated by roxygen2 (4.1.1): do not edit by hand\n% Please edit documentation in R/TestPrint.R\n\\name{test_me}\n\\alias{"
},
{
"path": "tests/testthat/TestPrint/tests/testthat/test-TestSummary.R",
"chars": 65,
"preview": "test_that(\"test_me works\", {\n expect_equal(test_me(2, 2), 4)\n})\n"
},
{
"path": "tests/testthat/TestPrint/tests/testthat.R",
"chars": 64,
"preview": "library(testthat)\nlibrary(\"TestPrint\")\n\ntest_check(\"TestPrint\")\n"
},
{
"path": "tests/testthat/TestR6/DESCRIPTION",
"chars": 314,
"preview": "Package: TestR6\nTitle: What the Package Does (one line, title case)\nVersion: 0.0.0.9000\nAuthors@R: \"First Last <first.la"
},
{
"path": "tests/testthat/TestR6/NAMESPACE",
"chars": 79,
"preview": "# Generated by roxygen2 (4.1.1): do not edit by hand\n\nexport(TestR6)\nexport(a)\n"
},
{
"path": "tests/testthat/TestR6/R/TestR6.R",
"chars": 415,
"preview": "#' an example function\n#'\n#' @export\na <- function(x) {\n if (x <= 1) {\n 1\n } else {\n 2\n }\n}\n\n#' @export\nTestR6 "
},
{
"path": "tests/testthat/TestR6/man/a.Rd",
"chars": 194,
"preview": "% Generated by roxygen2 (4.1.1): do not edit by hand\n% Please edit documentation in R/TestR6.R\n\\name{a}\n\\alias{a}\n\\title"
},
{
"path": "tests/testthat/TestR6/tests/testthat/test-TestR6.R",
"chars": 374,
"preview": "test_that(\"regular function `a` works as expected\", {\n expect_equal(a(1), 1)\n expect_equal(a(2), 2)\n expect_equal(a(3"
},
{
"path": "tests/testthat/TestR6/tests/testthat.R",
"chars": 58,
"preview": "library(testthat)\nlibrary(\"TestR6\")\n\ntest_check(\"TestR6\")\n"
},
{
"path": "tests/testthat/TestRC/DESCRIPTION",
"chars": 298,
"preview": "Package: TestRC\nTitle: What the Package Does (one line, title case)\nVersion: 0.0.0.9000\nAuthors@R: \"First Last <first.la"
},
{
"path": "tests/testthat/TestRC/NAMESPACE",
"chars": 86,
"preview": "# Generated by roxygen2 (4.1.1): do not edit by hand\n\nexport(a)\nexportClasses(TestRC)\n"
},
{
"path": "tests/testthat/TestRC/R/TestRC.R",
"chars": 325,
"preview": "#' an example function\n#'\n#' @export\na <- function(x) {\n if (x <= 1) {\n 1\n } else {\n 2\n }\n}\n\n#' @export\nTestRC "
},
{
"path": "tests/testthat/TestRC/man/a.Rd",
"chars": 194,
"preview": "% Generated by roxygen2 (4.1.1): do not edit by hand\n% Please edit documentation in R/TestRC.R\n\\name{a}\n\\alias{a}\n\\title"
},
{
"path": "tests/testthat/TestRC/tests/testthat/test-TestRC.R",
"chars": 366,
"preview": "test_that(\"regular function `a` works as expected\", {\n expect_equal(a(1), 1)\n expect_equal(a(2), 2)\n expect_equal(a(3"
},
{
"path": "tests/testthat/TestRC/tests/testthat.R",
"chars": 58,
"preview": "library(testthat)\nlibrary(\"TestRC\")\n\ntest_check(\"TestRC\")\n"
},
{
"path": "tests/testthat/TestS4/DESCRIPTION",
"chars": 294,
"preview": "Package: TestS4\nTitle: What the Package Does (one line, title case)\nVersion: 0.0.0.9000\nAuthors@R: \"First Last <first.la"
},
{
"path": "tests/testthat/TestS4/NAMESPACE",
"chars": 93,
"preview": "# Generated by roxygen2: do not edit by hand\n\nexport(a)\nexport(print2)\nexportClasses(TestS4)\n"
},
{
"path": "tests/testthat/TestS4/R/TestS4.R",
"chars": 523,
"preview": "#' an example function\n#'\n#' @export\n#' @examples\n#' a(1)\na <- function(x) {\n if (x <= 1) {\n 1\n } else {\n 2\n }\n"
},
{
"path": "tests/testthat/TestS4/codecov.yml",
"chars": 42,
"preview": "codecov:\n token: codecov_token_from_yaml\n"
},
{
"path": "tests/testthat/TestS4/man/a.Rd",
"chars": 204,
"preview": "% Generated by roxygen2: do not edit by hand\n% Please edit documentation in R/TestS4.R\n\\name{a}\n\\alias{a}\n\\title{an exam"
},
{
"path": "tests/testthat/TestS4/tests/testthat/test-TestS4.R",
"chars": 391,
"preview": "test_that(\"regular function `a` works as expected\", {\n expect_equal(a(1), 1)\n expect_equal(a(2), 2)\n expect_equal(a(3"
},
{
"path": "tests/testthat/TestS4/tests/testthat.R",
"chars": 72,
"preview": "library(testthat)\n\nsuppressPackageStartupMessages(test_check(\"TestS4\"))\n"
},
{
"path": "tests/testthat/TestS7/DESCRIPTION",
"chars": 510,
"preview": "Package: TestS7\nTitle: What the Package Does (One Line, Title Case)\nVersion: 0.0.0.9000\nAuthors@R: c(\n person(\"Jim\", "
},
{
"path": "tests/testthat/TestS7/NAMESPACE",
"chars": 137,
"preview": "# Generated by roxygen2: do not edit by hand\n\nexport(Range)\nexport(inside)\nif (getRversion() < \"4.3.0\") importFrom(\"S7\","
},
{
"path": "tests/testthat/TestS7/R/foo.R",
"chars": 1347,
"preview": "#' @import S7\n#' @export\nRange <- new_class(\"Range\",\n properties = list(\n start = class_double,\n end = class_doub"
},
{
"path": "tests/testthat/TestS7/tests/testthat/test-foo.R",
"chars": 555,
"preview": "test_that(\"Range works\", {\n x <- Range(1:10)\n\n x@end <- 20\n\n expect_error(x@end <- \"x\", \"must be <double>\")\n\n expect"
},
{
"path": "tests/testthat/TestS7/tests/testthat.R",
"chars": 392,
"preview": "# This file is part of the standard setup for testthat.\n# It is recommended that you do not modify it.\n#\n# Where should "
},
{
"path": "tests/testthat/TestSummary/DESCRIPTION",
"chars": 280,
"preview": "Package: TestSummary\nTitle: What the Package Does (one line, title case)\nVersion: 0.0.0.9000\nAuthors@R: \"First Last <fir"
},
{
"path": "tests/testthat/TestSummary/NAMESPACE",
"chars": 91,
"preview": "# Generated by roxygen2 (4.1.1): do not edit by hand\n\nexport(dont_test_me)\nexport(test_me)\n"
},
{
"path": "tests/testthat/TestSummary/R/TestSummary.R",
"chars": 159,
"preview": "#' an example function\n#'\n#' @export\ntest_me <- function(x, y){\n if (TRUE)\n x + y\n else\n x - y\n}\n\n#' @export\ndon"
},
{
"path": "tests/testthat/TestSummary/man/test_me.Rd",
"chars": 220,
"preview": "% Generated by roxygen2 (4.1.1): do not edit by hand\n% Please edit documentation in R/TestSummary.R\n\\name{test_me}\n\\alia"
},
{
"path": "tests/testthat/TestSummary/tests/testthat/test-TestSummary.R",
"chars": 65,
"preview": "test_that(\"test_me works\", {\n expect_equal(test_me(2, 2), 4)\n})\n"
},
{
"path": "tests/testthat/TestSummary/tests/testthat.R",
"chars": 68,
"preview": "library(testthat)\nlibrary(\"TestSummary\")\n\ntest_check(\"TestSummary\")\n"
},
{
"path": "tests/testthat/TestUseTry/DESCRIPTION",
"chars": 293,
"preview": "Package: TestUseTry\nTitle: Test That `use_try` Parameter works\nVersion: 0.0.0.9000\nAuthors@R: \"First Last <first.last@ex"
},
{
"path": "tests/testthat/TestUseTry/NAMESPACE",
"chars": 66,
"preview": "# Generated by roxygen2 (4.1.1): do not edit by hand\n\nexport(fun)\n"
},
{
"path": "tests/testthat/TestUseTry/R/notry.R",
"chars": 209,
"preview": "#' @export\n\nfun <- function() {\n withCallingHandlers(\n signalCondition(simpleError(\"This Will Exit if `!isTRUE(use_t"
},
{
"path": "tests/testthat/TestUseTry/tests/tests.R",
"chars": 18,
"preview": "TestUseTry::fun()\n"
},
{
"path": "tests/testthat/TestUseTry/tests/testthat/test-notry.R",
"chars": 31,
"preview": "expect_true(TestUseTry::fun())\n"
},
{
"path": "tests/testthat/Testbox/app/app.R",
"chars": 188,
"preview": "options(box.path = file.path(getwd()))\n# remove box cache\nloaded_mods <- loadNamespace(\"box\")$loaded_mods\nrm(list = ls(l"
},
{
"path": "tests/testthat/Testbox/app/modules/module.R",
"chars": 146,
"preview": "#' an example function\n#'\n#' @export\na <- function(x) {\n if (x <= 1) {\n 1\n } else {\n 2\n }\n}\n\nprivate_function <"
},
{
"path": "tests/testthat/Testbox/tests/testthat/test-module.R",
"chars": 517,
"preview": "box::use(\n testthat[test_that, expect_equal]\n)\n\nbox::use(\n app/modules/module\n)\n\nimpl <- attr(module, \"namespace\")\n\nte"
},
{
"path": "tests/testthat/Testbox/tests/testthat.R",
"chars": 201,
"preview": "options(box.path = file.path(getwd()))\n# remove box cache\nloaded_mods <- loadNamespace(\"box\")$loaded_mods\nrm(list = ls(l"
},
{
"path": "tests/testthat/Testbox_R6/app/app.R",
"chars": 190,
"preview": "options(box.path = file.path(getwd()))\n# remove box cache\nloaded_mods <- loadNamespace(\"box\")$loaded_mods\nrm(list = ls(l"
},
{
"path": "tests/testthat/Testbox_R6/app/modules/moduleR6.R",
"chars": 324,
"preview": "#' @export\nTestR6 <- R6::R6Class(\"TestR6\", # nolint\n public = list(\n show = "
},
{
"path": "tests/testthat/Testbox_R6/tests/testthat/test-moduleR6.R",
"chars": 461,
"preview": "box::use(\n testthat[test_that, expect_equal, expect_s3_class]\n)\n\nbox::use(\n app/modules/moduleR6\n)\n\ntest_that(\"TestR6 "
},
{
"path": "tests/testthat/Testbox_R6/tests/testthat.R",
"chars": 201,
"preview": "options(box.path = file.path(getwd()))\n# remove box cache\nloaded_mods <- loadNamespace(\"box\")$loaded_mods\nrm(list = ls(l"
},
{
"path": "tests/testthat/Testbox_attached_modules_functions/app/app.R",
"chars": 188,
"preview": "options(box.path = file.path(getwd()))\n# remove box cache\nloaded_mods <- loadNamespace(\"box\")$loaded_mods\nrm(list = ls(l"
},
{
"path": "tests/testthat/Testbox_attached_modules_functions/app/modules/module.R",
"chars": 195,
"preview": "#' an example function\n#'\n#' @export\na <- function(x) {\n if (x <= 1) {\n 1\n } else {\n 2\n }\n}\n\n#' @export\nb <- fu"
}
]
// ... and 56 more files (download for full content)
About this extraction
This page contains the full source code of the jimhester/covr GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 256 files (336.2 KB), approximately 105.1k tokens, and a symbol index with 25 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.