Skip to content

Commit

Permalink
Core Function Changes to Accommodate Empty Lists in Recursion, Covr B…
Browse files Browse the repository at this point in the history
…ehavior on Windows & Vignette Path Warnings (#322)
  • Loading branch information
bburns632 authored May 3, 2024
1 parent d44b789 commit b3a95de
Show file tree
Hide file tree
Showing 18 changed files with 319 additions and 114 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
with:
fetch-depth: 1
- name: If local, apt update
if: ${ (env.ACT || false)}
if: ${{ (env.ACT || false)}}
run: sudo apt update
- name: Install Tidy Ubuntu
run: sudo apt install -y tidy
Expand Down
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@

## CHANGES
* Updated `pkgnet-intro` vignette to include information on the Class Inheritance Reporter and other minor edits.
* Recursive functions `.parse_function` and `.parse_R6_expression` made tolerant to control statemets like `break` or `next` that would break the recursion. (#322)
* Excessive warnings removed for custom `vignette_path` param in `CreatePackageVignette()` (#322)

## BUGFIXES
* `CreatePackageReporter()` failing on Windows to build package coverage when `report_path` specified. (#322)

# pkgnet 0.4.2
## NEW FEATURES
Expand Down
36 changes: 0 additions & 36 deletions R/CreatePackageVignette.R
Original file line number Diff line number Diff line change
Expand Up @@ -85,42 +85,6 @@ CreatePackageVignette <- function(pkg = "."
, dirname(vignette_path)))
}

# Check if vignette_path matches the right package
# if the path is to a file in a directory named vignettes
vignetteDirAbsPath <- normalizePath(dirname(vignette_path))
# If path is a vignettes directory
if (grepl('/vignettes$', vignetteDirAbsPath)) {
# Get path for expected DESCRIPTION file for package
expectedDescriptionPath <- gsub(
pattern = "vignettes$"
, replacement = "DESCRIPTION"
, x = vignetteDirAbsPath
)

# If DESCRIPTION file exists check the name
if (file.exists(expectedDescriptionPath)) {
foundPkgName <- read.dcf(expectedDescriptionPath)[1,][["Package"]]

# If it doesn't match pkg_name, give warning
if (!identical(foundPkgName, pkg_name)) {
log_warn(glue::glue(
"You are writing a report for {pkg_name} to the vignettes "
, "directory for {foundPkgName}"
, pkg_name = pkg_name
, foundPkgName = foundPkgName))
}

# Otherwise, warn that we're writing to a vignettes folder inside
# a directory that is not a package root
} else {
log_warn(paste(
"You specified a path to a vignettes directory"
, vignetteDirAbsPath
, "that is not inside a package root directory."
))
}
}

log_info(sprintf(
"Creating pkgnet package report as vignette for %s..."
, pkg_name
Expand Down
72 changes: 45 additions & 27 deletions R/FunctionReporter.R
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,22 @@ FunctionReporter <- R6::R6Class(

log_info(sprintf("Calculating test coverage for %s...", self$pkg_name))

# workaround for covr conflict with loaded packages on windows
if(.Platform$OS.type == "windows") {
detach(paste0('package:',self$pkg_name), unload = TRUE, character.only = TRUE)
}

pkgCovDT <- data.table::as.data.table(covr::package_coverage(
path = private$pkg_path
, type = "tests"
, combine_types = FALSE
))

# workaround for covr conflict with loaded packages on windows
if(.Platform$OS.type == "windows") {
attachNamespace(self$pkg_name)
}

pkgCovDT <- pkgCovDT[, .(coveredLines = sum(value > 0)
, totalLines = .N
, coverageRatio = sum(value > 0)/.N
Expand Down Expand Up @@ -395,12 +405,17 @@ FunctionReporter <- R6::R6Class(
if (!is.list(x) && listable) {
x <- as.list(x)

# Check for expression of the form foo$bar
# We still want to split it up because foo might be a function
# but we want to get rid of bar, because it's a symbol in foo's namespace
# and not a symbol that could be reliably matched to the package namespace
if (identical(x[[1]], quote(`$`))) {
x <- x[1:2]
if (length(x) > 0){
# Check for expression of the form foo$bar
# We still want to split it up because foo might be a function
# but we want to get rid of bar, because it's a symbol in foo's namespace
# and not a symbol that could be reliably matched to the package namespace
if (identical(x[[1]], quote(`$`))) {
x <- x[1:2]
}
} else {
# make empty lists "not listable" so recursion stops
listable <- FALSE
}
}

Expand Down Expand Up @@ -640,35 +655,38 @@ FunctionReporter <- R6::R6Class(
# an environment pointer then we can break x up into list of components
listable <- (!is.atomic(x) && !is.symbol(x) && !is.environment(x))

# If it is not a list but listable...
if (!is.list(x) && listable) {
# Convert to list
xList <- as.list(x)

# Check if expression x is from _$_
if (identical(xList[[1]], quote(`$`))) {

# Check if expression x is of form self$foo, private$foo, or super$foo
# We want to keep those together because they could refer to the class'
# methods. So expression is not listable
if (identical(xList[[2]], quote(self))
|| identical(xList[[2]], quote(private))
|| identical(xList[[2]], quote(super))) {
listable <- FALSE

# If expression lefthand side is not keyword, we still want to split
# it up because left might be a function
# but we want to get rid of right, because it's a symbol in left's namespace
# and not a symbol that could be reliably matched to the package namespace
if (length(xList) > 0){
# Check if expression x is from _$_
if (identical(xList[[1]], quote(`$`))) {
# Check if expression x is of form self$foo, private$foo, or super$foo
if (identical(xList[[2]], quote(self)) || identical(xList[[2]], quote(private)) || identical(xList[[2]], quote(super))) {
# We want to keep those together because they could refer to the class'
# methods. So expression is not listable
listable <- FALSE
} else {
# If expression lefthand side is not keyword, we still want to split
# it up because left might be a function
# but we want to get rid of right, because it's a symbol in left's namespace
# and not a symbol that could be reliably matched to the package namespace
x <- xList[1:2]
}
} else {
# Left Hand is not a _$_. Proceed as normal list.
x <- xList
x <- x[1:2]
}

# Otherwise list as usual
} else {
x <- xList
}
# List is zero length. This might occur when encountering a "break" command.
# Make empty list "non-listable" so recursion stops in following step.
listable <- FALSE
}
}



if (listable){
# Filter out atomic values because we don't care about them
x <- Filter(f = Negate(is.atomic), x = x)
Expand Down
1 change: 1 addition & 0 deletions R/testing_utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
, sartre = file.path(pkgnetSourcePath, testPkgSourceDir, "sartre")
, milne = file.path(pkgnetSourcePath, testPkgSourceDir, "milne")
, silverstein = file.path(pkgnetSourcePath, testPkgSourceDir, "silverstein")
, control = file.path(pkgnetSourcePath, testPkgSourceDir, "control")
, pkgnet = pkgnetSourcePath
)

Expand Down
12 changes: 12 additions & 0 deletions inst/control/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Package: control
Type: Package
Title: Have self control, don't break!
Version: 0.1
Author: Brian Burns
Maintainer: Brian Burns <[email protected]>
Description: This package is used to test that functions in pkgnet don't break on control statements.
Imports:
R6
License: file LICENSE
LazyData: TRUE
RoxygenNote: 7.3.1
1 change: 1 addition & 0 deletions inst/control/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this is a test package
5 changes: 5 additions & 0 deletions inst/control/NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Generated by roxygen2: do not edit by hand

export(next_up)
export(take_a_break)
export(testClass)
23 changes: 23 additions & 0 deletions inst/control/R/self_control.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#' @title Take a Break
#' @name take_a_break
#' @description Test if .parse_function() breaks with control function 'break'
#' @export
take_a_break <- function() {
for (i in 1:10){
if (i==5){
break
}
}
}

#' @title Next Up
#' @name next_up
#' @description Test if .parse_function() breaks with control function 'next'
#' @export
next_up <- function() {
for (i in 1:10){
if (i==5){
next
}
}
}
29 changes: 29 additions & 0 deletions inst/control/R/self_control_R6.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#' Test Class for Control Statements
#'
#' @description A Test Class for Control Statements handling in R6
#' @export
testClass <- R6::R6Class(
classname = "testClass",
public = list(
#' @description
#' Test if .parse_R6_expression () breaks with control function 'break'
#' @return Nothing
take_a_break = function() {
for (i in 1:10){
if (i==5){
break
}
}
},
#' @description
#' Test if .parse_R6_expression () breaks with control function 'next'
#' @return Nothing
next_up = function() {
for (i in 1:10){
if (i==5){
next
}
}
}
)
)
11 changes: 11 additions & 0 deletions inst/control/man/next_up.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions inst/control/man/take_a_break.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions inst/control/man/testClass.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion inst/package_report/package_vignette_template.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ output:
rmarkdown::html_vignette:
toc: true
vignette: >
%\VignetteIndexEntry{Package Report, by pkgnet}
%\VignetteIndexEntry{ {{pkg_name}} Package Report, by pkgnet }
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
Expand Down
2 changes: 1 addition & 1 deletion tests/testthat/test-0-test-package-installation.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# correctly and being available.

test_that('Test packages installed correctly',{
testPkgNames <- c("baseballstats", "sartre", "milne", "silverstein")
testPkgNames <- c("baseballstats", "sartre", "milne", "silverstein","control")
for (thisTestPkg in testPkgNames) {
expect_true(
object = require(thisTestPkg
Expand Down
34 changes: 34 additions & 0 deletions tests/testthat/test-CreatePackageReport.R
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,40 @@ test_that("CreatePackageReport respects report_path when explicitly given", {
expect_true(any(grepl("Dependency Network", readLines(testing_file))))
})

test_that("Test that CreatePackageReport runs with control statements", {

testReportPath <- tempfile(
pattern = "control"
, fileext = ".html"
)

createdReport <- CreatePackageReport(
pkg_name = "control"
, report_path = testReportPath
)

testthat::expect_true({
reporters <- grep("Reporter$", names(createdReport), value = TRUE)
all(vapply(
X = reporters
, FUN = function(x) {
is.null(createdReport[[x]]) | inherits(createdReport[[x]], "AbstractPackageReporter")
}
, FUN.VALUE = logical(1)
))
})
testthat::expect_true(file.exists(testReportPath) && file.size(testReportPath) > 0)
testthat::expect_true(inherits(createdReport, "PackageReport"))
testthat::expect_true(
all(
vapply(DefaultReporters(), function(x){class(x)[1]}, FUN.VALUE = character(1))
%in% names(createdReport)
)
, info = "Returned report object doesn't have reporters accessible")
file.remove(testReportPath)
})


##### TEST TEAR DOWN #####

Sys.unsetenv("PKGNET_SUPPRESS_BROWSER")
Loading

0 comments on commit b3a95de

Please sign in to comment.