From b8cc02b683daebe1e0d425389750843a5491e11b Mon Sep 17 00:00:00 2001 From: Doug Kelkhoff <18220321+dgkf@users.noreply.github.com> Date: Wed, 10 Jul 2024 14:50:24 -0400 Subject: [PATCH] v0.0.1 Release Final Touches (#77) * chore: add @returns, cran-comments.md, remove remotes suggested dependency * fix: initial CRAN notes * fix: add new cran-comments to buildignore * skip reexport.srcrefs package tests on cran * more updates for CRAN submission --- .Rbuildignore | 1 + .gitignore | 1 + DESCRIPTION | 45 ++++-- LICENSE | 4 +- NEWS.md | 6 +- R/Rd_df.R | 2 +- R/getSrcFilepath.R | 4 +- R/list_of_srcref.R | 15 +- R/obj_namespace_name.R | 9 +- R/pseudo_srcref.R | 4 +- R/srcref_df.R | 42 +++-- R/srcrefs.R | 37 +++-- R/test_description.R | 37 +++-- R/test_trace_df.R | 38 +++-- R/utils.R | 17 +- README.Rmd | 27 ++-- README.md | 147 +++++++++--------- cran-comments.md | 9 ++ inst/examplepkg/DESCRIPTION | 2 +- inst/examplepkg/R/rd_sampler.R | 1 - inst/examplepkg/man/reexport_example.Rd | 1 + man/as.data.frame.list_of_srcref.Rd | 5 +- man/as.package.Rd | 3 + man/as_list_of_srcref.Rd | 5 +- man/coverage_check_has_recorded_tests.Rd | 5 + man/coverage_get_tests.Rd | 3 + man/coverage_has_recorded_tests.Rd | 5 + man/dot-tools.Rd | 16 -- man/expr_str.Rd | 4 + man/flat_map_srcrefs.Rd | 3 + man/format.list_of_srcref.Rd | 3 + man/get_namespace_object_names.Rd | 4 + man/is_srcref.Rd | 3 + man/join_on_containing_srcrefs.Rd | 3 + man/match_containing_srcrefs.Rd | 4 + man/new_empty_test_trace_tally.Rd | 3 + man/obj_namespace_name.Rd | 3 + man/package_check_has_keep_source.Rd | 4 + man/pkg_srcrefs.Rd | 8 +- man/pkg_srcrefs_df.Rd | 5 +- man/srcref_expr.Rd | 3 + man/srcref_str.Rd | 7 +- man/test_description_test_that.Rd | 3 + man/test_description_test_that_describe.Rd | 5 +- man/test_description_test_that_describe_it.Rd | 5 +- man/test_srcrefs.Rd | 3 + man/test_trace_df.Rd | 3 + man/trace_srcrefs.Rd | 3 + man/with_pseudo_srcref.Rd | 3 + .../testthat/packages/no.exports/R/no-code.R | 1 - tests/testthat/setup_test_packages.R | 14 +- tests/testthat/test_case_list_obj.R | 1 - .../testthat/test_match_containing_srcrefs.R | 18 +-- tests/testthat/test_pkg_srcrefs.R | 1 + tests/testthat/test_srcrefs.R | 4 +- tests/testthat/test_test_description.R | 14 +- vignettes/combining_srcref_data.Rmd | 8 +- vignettes/plotting_test_paths.Rmd | 8 +- vignettes/working_with_srcrefs.Rmd | 27 ++-- 59 files changed, 425 insertions(+), 244 deletions(-) create mode 100644 cran-comments.md delete mode 100644 man/dot-tools.Rd diff --git a/.Rbuildignore b/.Rbuildignore index 8d5064d..c5a1552 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -7,3 +7,4 @@ ^\.github$ ^doc$ ^Meta$ +^cran-comments\.md$ diff --git a/.gitignore b/.gitignore index a389081..cb24861 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ inst/doc README.html /doc/ /Meta/ +*\.tar\.gz diff --git a/DESCRIPTION b/DESCRIPTION index d4ee4bc..f6d400d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,24 +1,38 @@ Package: covtracer -Title: Tools for contextualizing tests -Version: 0.0.0.9016 -Authors@R: c( +Title: Contextualizing Tests +Version: 0.0.1 +Authors@R: + c( person( - given = "Doug", - family = "Kelkhoff", - role = c("aut", "cre"), - email = "kelkhofd@gene.com"), + "Doug", "Kelkhoff", , + "doug.kelkhoff@gmail.com", + role = c("cre", "aut"), + comment = c(ORCID = "0009-0003-7845-4061") + ), + person( + given = "Szymon", + family = "Maksymiuk", + role = c("aut"), + email = "sz.maksymiuk@gmail.com", + comment = c(ORCID = "0000-0002-3120-1601") + ), person( given = "Andrew", family = "McNeil", role = c("aut"), - email = "andrew.richard.mcneil@gmail.com") + email = "andrew.richard.mcneil@gmail.com" + ), + person( + "F. Hoffmann-La Roche AG", + role = c("cph", "fnd") + ) ) Description: - Provides tools for dissecting a package namespace or "covr" coverage object - in order to cross reference tested code with the lines that are evaluated, as - well as linking those evaluated lines to the documentation that they are - described within. Connecting these three pieces of information provides a - mechanism of linking tests to documented behaviors. + Dissects a package environment or 'covr' coverage object in order to cross + reference tested code with the lines that are evaluated, as well as linking + those evaluated lines to the documentation that they are described within. + Connecting these three pieces of information provides a mechanism of + linking tests to documented behaviors. URL: https://github.com/genentech/covtracer BugReports: https://github.com/genentech/covtracer/issues Depends: @@ -29,7 +43,6 @@ Imports: methods Suggests: testthat, - remotes, covr (>= 3.5.2), withr, R6, @@ -38,11 +51,9 @@ Suggests: igraph, knitr, rmarkdown -Remotes: - github::r-lib/covr License: MIT + file LICENSE Encoding: UTF-8 LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 VignetteBuilder: knitr diff --git a/LICENSE b/LICENSE index 7e1d467..a426624 100644 --- a/LICENSE +++ b/LICENSE @@ -1,2 +1,2 @@ -YEAR: 2022 -COPYRIGHT HOLDER: Genentech +YEAR: 2024 +COPYRIGHT HOLDER: F. Hoffmann-La Roche AG diff --git a/NEWS.md b/NEWS.md index cc37b8a..dab610e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,8 @@ -# Unreleased (tentative 0.0.1) +# covtracer 0.0.1 + +* initial CRAN release + +# covtracer 0.0.0.9000 * Fix handling for packages with no objects (#73, @dgkf) diff --git a/R/Rd_df.R b/R/Rd_df.R index adba535..79fa18f 100644 --- a/R/Rd_df.R +++ b/R/Rd_df.R @@ -37,7 +37,7 @@ Rd_df <- function(x) { if (length(doctype)) doctype else NA_character_ }, character(1L)) - aliases <- aliases[sort(names(aliases))] # avoid OS-specific file sorting + aliases <- aliases[sort(names(aliases))] # avoid OS-specific file sorting naliases <- vapply(aliases, length, integer(1L)) files <- rep(names(db), times = naliases) doctype <- rep(doctype, times = naliases) diff --git a/R/getSrcFilepath.R b/R/getSrcFilepath.R index 1eefc6e..92d090e 100644 --- a/R/getSrcFilepath.R +++ b/R/getSrcFilepath.R @@ -12,7 +12,9 @@ getSrcFilepath.default <- function(x) { path <- getSrcFilename(x, full.names = TRUE) wd <- attr(getSrcref(x), "srcfile")$wd if (!is.null(wd)) path <- file.path(wd, path) - if (!length(path)) return(NA_character_) + if (!length(path)) { + return(NA_character_) + } normalizePath(path, winslash = "/", mustWork = FALSE) } diff --git a/R/list_of_srcref.R b/R/list_of_srcref.R index 9361eca..8287fa8 100644 --- a/R/list_of_srcref.R +++ b/R/list_of_srcref.R @@ -1,6 +1,7 @@ #' Create an S3 list of srcref objects #' -#' @param x A list or single srcref to coerce to a list_of_srcref +#' @param x A list or single srcref to coerce to a `list_of_srcref` +#' @return A `list_of_srcref` class object #' #' @rdname as_list_of_srcref as_list_of_srcref <- function(x) { @@ -41,15 +42,20 @@ as_list_of_srcref.list <- function(x) { #' paths when formatting `srcref`s. #' @param full.num A \code{logical} value indicating whether to use all numeric #' `srcref` components when formatting `srcref`s. +#' @return A `character` vector of formatted strings #' #' @export format.list_of_srcref <- function(x, ..., full.names = FALSE, full.num = FALSE) { out <- rep_len(NA_character_, length(x)) - if (!length(x)) return(out) + if (!length(x)) { + return(out) + } xnull <- vapply(x, is.null, logical(1L)) srcnull <- vapply(x, function(i) is.null(getSrcref(i)), logical(1L)) isnull <- xnull | srcnull - if (all(isnull)) return(out) + if (all(isnull)) { + return(out) + } fps <- if (full.names) getSrcFilepath(x[!isnull]) else vapply(x[!isnull], getSrcFilename, character(1L)) srcref_num_rep_len <- length(as.numeric(x[!isnull][[1]])) nums <- t(vapply(x[!isnull], as.numeric, numeric(srcref_num_rep_len))) @@ -71,7 +77,8 @@ print.list_of_srcref <- function(x, ...) { sprintf("$%s", ifelse( grepl("^[a-zA-Z0-9_.]*$", names(x)), names(x), - sprintf("`%s`", names(x)))) + sprintf("`%s`", names(x)) + )) ) xfmt <- sprintf( "%s\n%s\n\n", diff --git a/R/obj_namespace_name.R b/R/obj_namespace_name.R index 8b0f300..5171e45 100644 --- a/R/obj_namespace_name.R +++ b/R/obj_namespace_name.R @@ -1,5 +1,7 @@ env_ns_name <- function(e) { - if (is.null(e) || !isNamespace(e)) return(NA_character_) + if (is.null(e) || !isNamespace(e)) { + return(NA_character_) + } unname(getNamespaceName(e)) } @@ -24,6 +26,7 @@ env_name <- function(e) { #' #' @param x A value to find within namespace `ns` #' @param ns A package namespace +#' @return A `character` string representing a namespace or similar #' obj_namespace_name <- function(x, ns) { UseMethod("obj_namespace_name") @@ -37,7 +40,9 @@ obj_namespace_name.default <- function(x, ns) { #' @exportS3Method obj_namespace_name.character <- function(x, ns) { is_exported <- x %in% getNamespaceExports(ns) - if (!is_exported) return(NA_character_) + if (!is_exported) { + return(NA_character_) + } env_ns_name(environment(getExportedValue(ns, x))) } diff --git a/R/pseudo_srcref.R b/R/pseudo_srcref.R index dcb3db6..1cd9048 100644 --- a/R/pseudo_srcref.R +++ b/R/pseudo_srcref.R @@ -12,10 +12,12 @@ #' @param call Any code object, most often a `call` object #' @param file A filepath to bind as a `srcfile` object #' @param lloc A `srcef`-like `lloc` numeric vector +#' @return A `with_pseudo_srcref` object, mimicking the structure of `srcref` #' with_pseudo_srcref <- function(call, file, lloc) { - if (!is.null(srcfile) && !is.null(lloc)) + if (!is.null(srcfile) && !is.null(lloc)) { attr(call, "srcref") <- structure(lloc, srcfile = srcfile(file), class = "pseudo_srcref") + } structure(call, class = c("with_pseudo_srcref", class(call))) } diff --git a/R/srcref_df.R b/R/srcref_df.R index a7e9b61..cb141c1 100644 --- a/R/srcref_df.R +++ b/R/srcref_df.R @@ -21,34 +21,40 @@ #' #' @examples #' pkg <- system.file("examplepkg", package = "covtracer") -#' remotes::install_local( +#' install.packages( #' pkg, -#' force = TRUE, +#' type = "source", +#' repos = NULL, #' quiet = TRUE, #' INSTALL_opts = "--with-keep.source" #' ) #' as.data.frame(pkg_srcrefs("examplepkg")) #' @export -as.data.frame.list_of_srcref <- function(x, ..., use.names = TRUE, +as.data.frame.list_of_srcref <- function( + x, ..., use.names = TRUE, expand.srcref = FALSE, row.names = NULL) { - if (expand.srcref) { df <- data.frame( srcfile = getSrcFilepath(x), matrix( t(vapply(lapply(x, as.numeric), `[`, numeric(8L), 1:8)), ncol = 8L, - dimnames = list(c(), c("line1", "byte1", "line2", - "byte2", "col1", "col2", "parsed1", "parsed2"))), + dimnames = list(c(), c( + "line1", "byte1", "line2", + "byte2", "col1", "col2", "parsed1", "parsed2" + )) + ), stringsAsFactors = FALSE, row.names = row.names, - ...) + ... + ) } else { df <- data.frame( srcref = I(x), stringsAsFactors = FALSE, row.names = row.names, - ...) + ... + ) class(df$srcref) <- setdiff(class(df$srcref), "AsIs") } @@ -74,9 +80,10 @@ as.data.frame.list_of_srcref <- function(x, ..., use.names = TRUE, #' #' @examples #' pkg <- system.file("examplepkg", package = "covtracer") -#' remotes::install_local( +#' install.packages( #' pkg, -#' force = TRUE, +#' type = "source", +#' repos = NULL, #' quiet = TRUE, #' INSTALL_opts = "--with-keep.source" #' ) @@ -246,8 +253,9 @@ test_trace_mapping <- function(x) { logical(1L) ) - if (!any(has_tests)) + if (!any(has_tests)) { return(new_empty_test_trace_tally()) + } mat <- do.call( rbind, @@ -259,7 +267,7 @@ test_trace_mapping <- function(x) { ) ) - mat <- mat[order(mat[, "test"], mat[, "i"]),, drop = FALSE] + mat <- mat[order(mat[, "test"], mat[, "i"]), , drop = FALSE] mat } @@ -273,6 +281,8 @@ test_trace_mapping <- function(x) { #' #' @param l A `list_of_srcref` object #' @param r A `list_of_srcref` object +#' @return A `integer` vector of the first index in `r` that fully encapsulate +#' the respective element in `l` #' match_containing_srcrefs <- function(l, r) { # NOTE: @@ -311,11 +321,11 @@ match_containing_srcrefs <- function(l, r) { l_start_gte_r_start <- (ldf[[li, "line1"]] > rdf[[ri, "line1"]]) || (ldf[[li, "line1"]] == rdf[[ri, "line1"]] && - ldf[[li, "col1"]] >= rdf[[ri, "col1"]]) + ldf[[li, "col1"]] >= rdf[[ri, "col1"]]) l_end_lte_r_end <- (ldf[[li, "line2"]] < rdf[[ri, "line2"]]) || (ldf[[li, "line2"]] == rdf[[ri, "line2"]] && - ldf[[li, "col2"]] <= rdf[[ri, "col2"]]) + ldf[[li, "col2"]] <= rdf[[ri, "col2"]]) if (!is.na(l_start_gte_r_start) && l_start_gte_r_start) { if (!is.na(l_end_lte_r_end) && l_end_lte_r_end) { @@ -358,12 +368,14 @@ match_containing_srcrefs <- function(l, r) { #' The name should be the name of the column from the left `data.frame` #' containing a `list_of_srcref` column, and the value should be the name of a #' column from the right `data.frame` containing a `list_of_srcref` column. +#' @return A `data.frame` of `x` joined on `y` by spanning `srcref` #' #' @export join_on_containing_srcrefs <- function(x, y, by = c("srcref" = "srcref")) { by <- by[1L] - if (!names(by) %in% names(x) || !by %in% names(y)) + if (!names(by) %in% names(x) || !by %in% names(y)) { stop("joining columns defined in `by` not found in provided data.frames") + } idx <- match_containing_srcrefs(x[[names(by)[[1L]]]], y[[by[[1L]]]]) if (any(names(y) %in% names(x))) { diff --git a/R/srcrefs.R b/R/srcrefs.R index 3cf5ccf..58f877c 100644 --- a/R/srcrefs.R +++ b/R/srcrefs.R @@ -1,6 +1,7 @@ #' Test whether an object is a \code{srcref} object #' #' @param x Any object +#' @return A `logical` indicating whether object is a `srcref` #' is_srcref <- function(x) { inherits(x, "srcref") @@ -96,8 +97,9 @@ srcrefs.list <- function(x, ..., srcref_names = NULL, breadcrumbs = character()) #' @rdname srcrefs srcrefs.namespace <- function(x, ..., breadcrumbs = character()) { # short circuit on recursive environment traversal - if (env_name(x) %in% breadcrumbs) + if (env_name(x) %in% breadcrumbs) { return(NULL) + } flat_map_srcrefs( as.list(x, all.names = TRUE), @@ -109,15 +111,17 @@ srcrefs.namespace <- function(x, ..., breadcrumbs = character()) { #' @exportS3Method #' @rdname srcrefs srcrefs.environment <- function(x, ..., breadcrumbs = character()) { - if (isNamespace(x)) + if (isNamespace(x)) { return(srcrefs.namespace(x, ..., breadcrumbs = breadcrumbs)) + } # short circuit on recursive environment traversal - if (env_name(x) %in% breadcrumbs) + if (env_name(x) %in% breadcrumbs) { return(NULL) + } objs <- as.list(x, all.names = TRUE) - objs <- Filter(function(i) !identical(i, x), objs) # prevent direct recursion + objs <- Filter(function(i) !identical(i, x), objs) # prevent direct recursion flat_map_srcrefs( objs, ns = env_ns_name(x), @@ -129,9 +133,9 @@ srcrefs.environment <- function(x, ..., breadcrumbs = character()) { #' @exportS3Method #' @rdname srcrefs -srcrefs.R6ClassGenerator <- function(x, ..., srcref_names = NULL, - breadcrumbs = character()) { - +srcrefs.R6ClassGenerator <- function( + x, ..., srcref_names = NULL, + breadcrumbs = character()) { objs <- c(list(x$new), x$public_methods, x$private_methods, x$active) names(objs) <- rep_len(srcref_names, length(objs)) flat_map_srcrefs( @@ -147,7 +151,9 @@ srcrefs.R6ClassGenerator <- function(x, ..., srcref_names = NULL, #' @importFrom utils getSrcref #' @rdname srcrefs srcrefs.standardGeneric <- function(x, ..., srcref_names = NULL) { - if (is.null(sr <- getSrcref(x))) return(sr) + if (is.null(sr <- getSrcref(x))) { + return(sr) + } attr(sr, "namespace") <- obj_namespace_name(x) @@ -167,7 +173,9 @@ srcrefs.nonstandardGenericFunction <- srcrefs.standardGeneric #' @rdname srcrefs srcrefs.MethodDefinition <- function(x, ..., srcref_names = NULL) { # catch methods in methods tables from packages without srcref data - if (is.null(sr <- getSrcref(x))) return(sr) + if (is.null(sr <- getSrcref(x))) { + return(sr) + } # generic source package generic_origin_ns <- attr(x@generic, "package") @@ -199,6 +207,7 @@ srcrefs.MethodDefinition <- function(x, ..., srcref_names = NULL) { #' `xs` objects themselves have namespaces attributed already to them, the #' namespace will not be replaced. #' @inheritParams srcrefs +#' @return A `list` of `srcref`s #' flat_map_srcrefs <- function(xs, ns = NULL, breadcrumbs = character()) { srcs <- mapply( @@ -241,12 +250,14 @@ flat_map_srcrefs <- function(xs, ns = NULL, breadcrumbs = character()) { #' a namespace environment, or a \code{link[covr]{package_coverage}} result #' from which a package name is discovered. When package names are provided, a #' namespace available in the current environment is used. +#' @return A `list_of_srcref` #' #' @examples #' pkg <- system.file("examplepkg", package = "covtracer") -#' remotes::install_local( +#' install.packages( #' pkg, -#' force = TRUE, +#' type = "source", +#' repos = NULL, #' quiet = TRUE, #' INSTALL_opts = "--with-keep.source" #' ) @@ -304,6 +315,8 @@ pkg_srcrefs.coverage <- function(x) { #' #' @param env A package namespace environment or iterable collection of package #' objects +#' @return Used for side effect of throwing an error when a package was not +#' installed with `srcref`s. #' package_check_has_keep_source <- function(env) { has_srcref <- function(x) !is.null(getSrcref(x)) @@ -323,6 +336,7 @@ package_check_has_keep_source <- function(env) { #' Extract test srcref objects #' #' @param x A package coverage object or similar. +#' @return A `list_of_srcref` #' #' @examples #' options(covr.record_tests = TRUE) @@ -368,6 +382,7 @@ test_srcrefs.coverage <- function(x) { #' #' @param x (\code{link[covr]{package_coverage}}) A \code{\link[covr]{covr}} #' coverage object produced with \code{options(covr.record_tests = TRUE)}. +#' @return A `list_of_srcref` #' #' @family srcrefs #' @seealso as.data.frame.list_of_srcref diff --git a/R/test_description.R b/R/test_description.R index 42b5954..58061a4 100644 --- a/R/test_description.R +++ b/R/test_description.R @@ -42,8 +42,11 @@ test_description.expression <- function(x) { #' @importFrom utils getSrcref test_description.call <- function(x) { src <- getSrcref(x) - if (is.null(src)) test_description(expr_str(x)) - else test_description(src) + if (is.null(src)) { + test_description(expr_str(x)) + } else { + test_description(src) + } } #' @exportS3Method @@ -57,12 +60,12 @@ test_description.list <- function(x) { test_calls <- lapply(x, `[[`, 1L) is_test_that_call <- vapply(test_calls, function(test_call) { identical(test_call, quote(test_that)) || - identical(test_call, quote(testthat::test_that)) + identical(test_call, quote(testthat::test_that)) }, logical(1L)) is_describe_call <- vapply(test_calls, function(test_call) { identical(test_call, quote(describe)) || - identical(test_call, quote(testthat::describe)) + identical(test_call, quote(testthat::describe)) }, logical(1L)) # only consider `it` calls within a `describe` call @@ -118,8 +121,11 @@ as_test_desc <- function(x, type = "call") { as_testthat_desc <- function(x) { if (!is.character(x)) { try_result <- try(eval(x, envir = baseenv()), silent = TRUE) - if (!inherits(try_result, "try-error")) x <- try_result - else return(as_test_desc(expr_str(x))) + if (!inherits(try_result, "try-error")) { + x <- try_result + } else { + return(as_test_desc(expr_str(x))) + } } as_test_desc(x, type = "testthat") @@ -131,6 +137,7 @@ as_testthat_desc <- function(x) { #' #' @param x A test_that call object #' @param ... Additional arguments unused +#' @return A `character` description, parsed from a `test_that::test_that` call #' test_description_test_that <- function(x, ...) { as_testthat_desc(match.call(testthat::test_that, x)$desc) @@ -140,8 +147,9 @@ test_description_test_that <- function(x, ...) { #' Parse the test description from a `describe` call #' -#' @param x A test_that::describe call object +#' @param x A `test_that::describe` call object #' @param ... Additional arguments unused +#' @return A `character` description, parsed from a `test_that::describe` call #' test_description_test_that_describe <- function(x, ...) { as_testthat_desc(match.call(testthat::describe, x)$description) @@ -151,8 +159,9 @@ test_description_test_that_describe <- function(x, ...) { #' Parse the test description from a `it` call #' -#' @param x A test_that::describe call object +#' @param x A `test_that::it` call object #' @param ... Additional arguments unused +#' @return A `character` description, parsed from a `test_that::it` call #' test_description_test_that_describe_it <- function(x, ...) { # mock `it` function, defined in testthat::describe @@ -164,7 +173,8 @@ test_description_test_that_describe_it <- function(x, ...) { #' Parse the expression associated with a srcref #' -#' @param ref a \code{srcref} +#' @param ref a `srcref` +#' @return A parsed `srcref` object #' srcref_expr <- function(ref) { parse(text = srcref_str(ref)) @@ -172,9 +182,10 @@ srcref_expr <- function(ref) { -#' Parse the expression associated with a srcref +#' Convert a srcref into a string #' -#' @param ref a \code{srcref} +#' @param ref a `srcref` +#' @return A string representing the `srcref` #' srcref_str <- function(ref) { paste0(as.character(ref), collapse = "\n") @@ -185,9 +196,11 @@ srcref_str <- function(ref) { #' Convert an expression, call or symbol to a single-line string #' #' @param ref a \code{srcref} +#' @return The given expression, formatted as a string with prefixes for +#' symbols and generics. #' expr_str <- function(ref) { - # used when description of the test is given by a variable + # used when description of the test is given by a variable # (see: inst/examplepkg/tests/testthat/test-complex-calls.R) if (is.symbol(ref)) { return(paste0("symbol: ", as.character(ref))) diff --git a/R/test_trace_df.R b/R/test_trace_df.R index 4ea3702..65a044f 100644 --- a/R/test_trace_df.R +++ b/R/test_trace_df.R @@ -10,6 +10,7 @@ #' as the bases of tracing tests. Coverage results must have been produced #' using `options(covr.record_tests = TRUE)`. #' @param ... Additional arguments unused +#' @return A `data.frame` of tests and corresponding traces #' #' @export #' @rdname test_trace_df @@ -26,10 +27,10 @@ test_trace_df <- function(x, ...) { #' @importFrom stats aggregate #' @export #' @rdname test_trace_df -test_trace_df.coverage <- function(x, ..., - pkg = as.package(attr(x, "package")$path), - aggregate_by = sum) { - +test_trace_df.coverage <- function( + x, ..., + pkg = as.package(attr(x, "package")$path), + aggregate_by = sum) { coverage_check_has_recorded_tests(x) pkgname <- pkg$package @@ -111,8 +112,10 @@ test_trace_df.coverage <- function(x, ..., # Reorder columns df$is_reexported <- !(is.na(df$namespace) | df$namespace == pkgname) cols <- setdiff(names(df), c("trace_name", "trace", "test", "depth")) - col_order <- c("alias", "srcref", "test_name", "test_srcref", "trace_srcref", - "count", "direct", "is_exported", "is_reexported") + col_order <- c( + "alias", "srcref", "test_name", "test_srcref", "trace_srcref", + "count", "direct", "is_exported", "is_reexported" + ) col_order <- c(col_order, setdiff(cols, col_order)) df <- df[, col_order] @@ -127,8 +130,9 @@ test_trace_df.coverage <- function(x, ..., #' \code{option(covr.record_tests = TRUE)}. #' #' @param coverage a \code{\link[covr]{covr}} coverage object -#' @family coverage_tests +#' @return A `list` of tests evaluated when using `covr` #' +#' @family coverage_tests coverage_get_tests <- function(coverage) { attr(coverage, "tests") } @@ -141,10 +145,12 @@ coverage_get_tests <- function(coverage) { #' was captured with \code{option(covr.record_tests = TRUE)}. #' #' @param coverage a \code{\link[covr]{covr}} coverage object -#' @family coverage_tests +#' @return A `logical` value, indicating whether the coverage object has +#' recorded tests, or `NA` when it does not appear to have traced any test +#' code. #' +#' @family coverage_tests #' @importFrom utils getSrcDirectory -#' coverage_has_recorded_tests <- function(coverage) { has_tests_attr <- !is.null(attr(coverage, "tests")) @@ -158,9 +164,13 @@ coverage_has_recorded_tests <- function(coverage) { coverage )) - if (has_tests_attr || has_trace_tests) return(TRUE) - else if (!has_r_dir_traces) return(NA) - else return(FALSE) + if (has_tests_attr || has_trace_tests) { + return(TRUE) + } else if (!has_r_dir_traces) { + return(NA) + } else { + return(FALSE) + } } @@ -175,9 +185,11 @@ coverage_has_recorded_tests <- function(coverage) { #' @param warn Whether to warn when it is uncertain whether the tests were #' recorded. It may be uncertain if tests were recorded if there are no tested #' R code traces. +#' @return Used for side-effects of emitting an error when a coverage object +#' does not contain recorded traces, or a warning when a coverage object +#' appears to have no tests. #' #' @family coverage_tests -#' coverage_check_has_recorded_tests <- function(coverage, warn = TRUE) { has_recorded_tests <- coverage_has_recorded_tests(coverage) diff --git a/R/utils.R b/R/utils.R index e8f50aa..3dc584b 100644 --- a/R/utils.R +++ b/R/utils.R @@ -11,6 +11,8 @@ #' Get namespace exports, filtering methods tables and definitions #' #' @param ns A namespace object +#' @return The names of exported objects, filtering internal method tables and +#' metadata. #' get_namespace_object_names <- function(ns) { out <- getNamespaceExports(ns) @@ -22,6 +24,8 @@ get_namespace_object_names <- function(ns) { #' Build an empty covr-style test trace mapping #' +#' @return An empty test-trace matrix, as provided by `covr` +#' #' @importFrom utils packageVersion new_empty_test_trace_tally <- function() { # version needs to be updated until changes are merged @@ -51,9 +55,12 @@ new_empty_test_trace_tally <- function() { #' edits to further reduce `devtools` dependencies. #' #' @param x A package object to coerce +#' @return A `package` object #' as.package <- function(x) { - if (inherits(x, "package")) return(x) + if (inherits(x, "package")) { + return(x) + } info <- read.dcf(file.path(x, "DESCRIPTION"))[1L, ] Encoding(info) <- "UTF-8" desc <- as.list(info) @@ -64,13 +71,9 @@ as.package <- function(x) { -#' Loading unexported helpers from tools -#' +#' Loading select unexported helpers from tools +#' @noRd #' @import tools -#' .tools <- as.list(getNamespace("tools"), all.names = TRUE)[c( - "compareDependsPkgVersion", - "processRdSexprs", - "initialRdMacros", ".Rd_get_metadata" )] diff --git a/README.Rmd b/README.Rmd index 74ef7f7..4ddaa57 100644 --- a/README.Rmd +++ b/README.Rmd @@ -14,7 +14,7 @@ knitr::opts_chunk$set( ) options( - tibble.print_min = 5, + tibble.print_min = 5, tibble.print_max = 5, width = 120L ) @@ -27,7 +27,7 @@ suppressPackageStartupMessages(library(dplyr)) [![CRAN](https://img.shields.io/cran/v/covtracer.svg)](https://cran.r-project.org/package=covtracer) [![R-CMD-check](https://github.com/Genentech/covtracer/workflows/R-CMD-check/badge.svg)](https://github.com/Genentech/covtracer/actions) -[![Code Coverage](https://img.shields.io/codecov/c/github/genentech/covtracer/main.svg)](https://codecov.io/gh/genentech/covtracer) +[![Code Coverage](https://img.shields.io/codecov/c/github/genentech/covtracer/main.svg)](https://app.codecov.io/gh/genentech/covtracer) Tools for contextualizing tests, built using `covr` test traces. This @@ -43,7 +43,7 @@ flowchart LR ## Installation To install, use `remotes` to install directly from -[GitHub](https://www.github.com/Genentech/covtracer) +[GitHub](https://github.com/Genentech/covtracer) Functionality hinges heavily on coverage objects prepared using `covr` (≥ 3.5.1.9003). To ensure suggested dependency requirements are met, @@ -80,15 +80,16 @@ library(covtracer) library(dplyr) library(withr) library(covr) -library(remotes) withr::with_temp_libpaths({ pkg <- system.file("examplepkg", package = "covtracer") - remotes::install_local( - pkg, - force = TRUE, - quiet = TRUE, - INSTALL_opts = "--with-keep.source" + + install.packages( + pkg, + type = "source", + repos = NULL, + quiet = TRUE, + INSTALL_opts = c("--with-keep.source", "--install-tests") ) options(covr.record_tests = TRUE) @@ -109,7 +110,7 @@ objects. ```{r} traceability_matrix <- ttdf %>% - filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code + filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code select(test_name, file) %>% filter(!duplicated(.)) %>% arrange(file) @@ -132,7 +133,7 @@ behaviors. ```{r} ttdf %>% - filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code + filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code select(test_name, file) %>% filter(!duplicated(.)) %>% arrange(file) @@ -145,7 +146,7 @@ documentation that is not covered by any test. ```{r} ttdf %>% - filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code + filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code select(test_name, count, alias, file) %>% filter(is.na(count)) %>% arrange(alias) @@ -162,7 +163,7 @@ consider only the coverage of directly tested functions. ```{r} ttdf %>% - filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code + filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code select(direct, alias) %>% group_by(alias) %>% summarize(any_direct_tests = any(direct, na.rm = TRUE)) %>% diff --git a/README.md b/README.md index 0da3936..11c4ee1 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![CRAN](https://img.shields.io/cran/v/covtracer.svg)](https://cran.r-project.org/package=covtracer) [![R-CMD-check](https://github.com/Genentech/covtracer/workflows/R-CMD-check/badge.svg)](https://github.com/Genentech/covtracer/actions) [![Code -Coverage](https://img.shields.io/codecov/c/github/genentech/covtracer/main.svg)](https://codecov.io/gh/genentech/covtracer) +Coverage](https://img.shields.io/codecov/c/github/genentech/covtracer/main.svg)](https://app.codecov.io/gh/genentech/covtracer) Tools for contextualizing tests, built using `covr` test traces. This @@ -24,7 +24,7 @@ flowchart LR ## Installation To install, use `remotes` to install directly from -[GitHub](https://www.github.com/Genentech/covtracer) +[GitHub](https://github.com/Genentech/covtracer) Functionality hinges heavily on coverage objects prepared using `covr` (≥ 3.5.1.9003). To ensure suggested dependency requirements are met, @@ -63,15 +63,16 @@ library(covtracer) library(dplyr) library(withr) library(covr) -library(remotes) withr::with_temp_libpaths({ pkg <- system.file("examplepkg", package = "covtracer") - remotes::install_local( - pkg, - force = TRUE, - quiet = TRUE, - INSTALL_opts = "--with-keep.source" + + install.packages( + pkg, + type = "source", + repos = NULL, + quiet = TRUE, + INSTALL_opts = c("--with-keep.source", "--install-tests") ) options(covr.record_tests = TRUE) @@ -93,37 +94,34 @@ to evaluated, documented objects. ``` r traceability_matrix <- ttdf %>% - filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code + filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code select(test_name, file) %>% filter(!duplicated(.)) %>% arrange(file) traceability_matrix -#> test_name file -#> 1 Example R6 Accumulator class methods are traced Accumulator.Rd -#> 2 Example R6 Accumulator class constructor is traced Accumulator.Rd -#> 3 adder.Rd -#> 4 Example R6 Accumulator class methods are traced adder.Rd -#> 5 Calling a deeply nested series of functions. complex_call_stack.Rd -#> 6 Calling a function halfway through call stack. deeper_nested_function.Rd -#> 7 Calling a deeply nested series of functions. deeper_nested_function.Rd -#> 8 hypotenuse is calculated correctly; with negative lengths hypotenuse.Rd -#> 9 hypotenuse is calculated correctly hypotenuse.Rd -#> 10 S4Example increment generic method works increment.Rd -#> 11 S4Example names method works names-S4Example-method.Rd -#> 12 names-S4Example2-method.Rd -#> 13 Calling a deeply nested series of functions. nested_function.Rd -#> 14 Example R6 Person class public methods are traced Person.Rd -#> 15 Rando.Rd -#> 16 Example R6 Rando class active field functions are traced Rando.Rd -#> 17 rd_sampler.Rd -#> 18 Calling a function halfway through call stack. recursive_function.Rd -#> 19 Calling a deeply nested series of functions. recursive_function.Rd -#> 20 reexport_example.Rd -#> 21 reexports.Rd -#> 22 s3_example_func works using list dispatch s3_example_func.Rd -#> 23 s3_example_func works using default dispatch s3_example_func.Rd -#> 24 +#> test_name file +#> 1 Example R6 Person class public methods are traced Accumulator.Rd +#> 2 S4Example increment generic method works Person.Rd +#> 3 Example R6 Person class public methods are traced Person.Rd +#> 4 S4Example increment generic method works Rando.Rd +#> 5 Rando.Rd +#> 6 adder.Rd +#> 7 S4Example increment generic method works adder.Rd +#> 8 S4Example increment generic method works complex_call_stack.Rd +#> 9 S4Example increment generic method works deeper_nested_function.Rd +#> 10 S4Example increment generic method works hypotenuse.Rd +#> 11 S4Example increment generic method works increment.Rd +#> 12 S4Example names method works names-S4Example-method.Rd +#> 13 names-S4Example2-method.Rd +#> 14 S4Example increment generic method works nested_function.Rd +#> 15 rd_sampler.Rd +#> 16 S4Example increment generic method works recursive_function.Rd +#> 17 reexport_example.Rd +#> 18 reexports.Rd +#> 19 S4Example increment generic method works s3_example_func.Rd +#> 20 S4 Generic Call: show() show-S4Example-method.Rd +#> 21 ``` We can quickly see which functions or methods are entirely untested. @@ -141,35 +139,32 @@ behaviors. ``` r ttdf %>% - filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code + filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code select(test_name, file) %>% filter(!duplicated(.)) %>% arrange(file) -#> test_name file -#> 1 Example R6 Accumulator class methods are traced Accumulator.Rd -#> 2 Example R6 Accumulator class constructor is traced Accumulator.Rd -#> 3 adder.Rd -#> 4 Example R6 Accumulator class methods are traced adder.Rd -#> 5 Calling a deeply nested series of functions. complex_call_stack.Rd -#> 6 Calling a function halfway through call stack. deeper_nested_function.Rd -#> 7 Calling a deeply nested series of functions. deeper_nested_function.Rd -#> 8 hypotenuse is calculated correctly; with negative lengths hypotenuse.Rd -#> 9 hypotenuse is calculated correctly hypotenuse.Rd -#> 10 S4Example increment generic method works increment.Rd -#> 11 S4Example names method works names-S4Example-method.Rd -#> 12 names-S4Example2-method.Rd -#> 13 Calling a deeply nested series of functions. nested_function.Rd -#> 14 Example R6 Person class public methods are traced Person.Rd -#> 15 Rando.Rd -#> 16 Example R6 Rando class active field functions are traced Rando.Rd -#> 17 rd_sampler.Rd -#> 18 Calling a function halfway through call stack. recursive_function.Rd -#> 19 Calling a deeply nested series of functions. recursive_function.Rd -#> 20 reexport_example.Rd -#> 21 reexports.Rd -#> 22 s3_example_func works using list dispatch s3_example_func.Rd -#> 23 s3_example_func works using default dispatch s3_example_func.Rd -#> 24 +#> test_name file +#> 1 Example R6 Person class public methods are traced Accumulator.Rd +#> 2 S4Example increment generic method works Person.Rd +#> 3 Example R6 Person class public methods are traced Person.Rd +#> 4 S4Example increment generic method works Rando.Rd +#> 5 Rando.Rd +#> 6 adder.Rd +#> 7 S4Example increment generic method works adder.Rd +#> 8 S4Example increment generic method works complex_call_stack.Rd +#> 9 S4Example increment generic method works deeper_nested_function.Rd +#> 10 S4Example increment generic method works hypotenuse.Rd +#> 11 S4Example increment generic method works increment.Rd +#> 12 S4Example names method works names-S4Example-method.Rd +#> 13 names-S4Example2-method.Rd +#> 14 S4Example increment generic method works nested_function.Rd +#> 15 rd_sampler.Rd +#> 16 S4Example increment generic method works recursive_function.Rd +#> 17 reexport_example.Rd +#> 18 reexports.Rd +#> 19 S4Example increment generic method works s3_example_func.Rd +#> 20 S4 Generic Call: show() show-S4Example-method.Rd +#> 21 ``` ### Finding Untested Behaviors @@ -179,16 +174,16 @@ only documentation that is not covered by any test. ``` r ttdf %>% - filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code + filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code select(test_name, count, alias, file) %>% filter(is.na(count)) %>% arrange(alias) #> test_name count alias file -#> 1 NA adder adder.Rd -#> 2 NA help reexports.Rd -#> 3 NA names,S4Example2-method names-S4Example2-method.Rd -#> 4 NA person -#> 5 NA Rando Rando.Rd +#> 1 NA Rando Rando.Rd +#> 2 NA adder adder.Rd +#> 3 NA help reexports.Rd +#> 4 NA names,S4Example2-method names-S4Example2-method.Rd +#> 5 NA person #> 6 NA rd_sampler rd_sampler.Rd #> 7 NA reexport_example reexport_example.Rd #> 8 NA reexports reexports.Rd @@ -206,18 +201,18 @@ functions. ``` r ttdf %>% - filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code + filter(!doctype %in% c("data", "class")) %>% # ignore objects without testable code select(direct, alias) %>% group_by(alias) %>% summarize(any_direct_tests = any(direct, na.rm = TRUE)) %>% arrange(alias) -#> # A tibble: 20 × 2 -#> alias any_direct_tests -#> -#> 1 Accumulator TRUE -#> 2 adder TRUE -#> 3 complex_call_stack TRUE -#> 4 deeper_nested_function TRUE -#> 5 help FALSE -#> # … with 15 more rows +#> # A tibble: 21 × 2 +#> alias any_direct_tests +#> +#> 1 Accumulator TRUE +#> 2 Person TRUE +#> 3 Rando TRUE +#> 4 adder TRUE +#> 5 complex_call_stack TRUE +#> # ℹ 16 more rows ``` diff --git a/cran-comments.md b/cran-comments.md new file mode 100644 index 0000000..2eaa5d4 --- /dev/null +++ b/cran-comments.md @@ -0,0 +1,9 @@ +> Examples with CPU (user + system) or elapsed time > 5 + +Some examples (roughly 4, depending on CPU speed) take just slightly over 5s to +run. We've taken a lot of care to make them run as fast as possible, but due to +the nature of the package, having meaningful examples that showcase the breadth +of edge cases requires running `covr::package_coverage` against an embedded +example package. This is inevitably a bottleneck for any sizable package. +The slow example times are surely tolerable to any users of the package as +real-world use cases will be far longer test suites. diff --git a/inst/examplepkg/DESCRIPTION b/inst/examplepkg/DESCRIPTION index 8c0bd99..22c7b88 100644 --- a/inst/examplepkg/DESCRIPTION +++ b/inst/examplepkg/DESCRIPTION @@ -16,7 +16,7 @@ License: Encoding: UTF-8 LazyData: true Roxygen: list(markdown = TRUE, r6 = FALSE) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 Suggests: testthat (>= 3.0.0) Config/testthat/edition: 3 diff --git a/inst/examplepkg/R/rd_sampler.R b/inst/examplepkg/R/rd_sampler.R index 831a613..3144a9f 100644 --- a/inst/examplepkg/R/rd_sampler.R +++ b/inst/examplepkg/R/rd_sampler.R @@ -52,7 +52,6 @@ #' @author R.D. Sampler. #' #' @export -#' rd_sampler <- function(x, y = TRUE, ...) { TRUE } diff --git a/inst/examplepkg/man/reexport_example.Rd b/inst/examplepkg/man/reexport_example.Rd index 667aeec..b9b1a74 100644 --- a/inst/examplepkg/man/reexport_example.Rd +++ b/inst/examplepkg/man/reexport_example.Rd @@ -17,6 +17,7 @@ reexport_example( setRNG = FALSE, ask = getOption("example.ask"), prompt.prefix = abbreviate(topic, 6), + catch.aborts = FALSE, run.dontrun = FALSE, run.donttest = interactive() ) diff --git a/man/as.data.frame.list_of_srcref.Rd b/man/as.data.frame.list_of_srcref.Rd index f5aef0b..6bd99c6 100644 --- a/man/as.data.frame.list_of_srcref.Rd +++ b/man/as.data.frame.list_of_srcref.Rd @@ -45,9 +45,10 @@ Coerce a list_of_srcref object to a data.frame } \examples{ pkg <- system.file("examplepkg", package = "covtracer") -remotes::install_local( +install.packages( pkg, - force = TRUE, + type = "source", + repos = NULL, quiet = TRUE, INSTALL_opts = "--with-keep.source" ) diff --git a/man/as.package.Rd b/man/as.package.Rd index 27faacc..2b8cf05 100644 --- a/man/as.package.Rd +++ b/man/as.package.Rd @@ -9,6 +9,9 @@ as.package(x) \arguments{ \item{x}{A package object to coerce} } +\value{ +A \code{package} object +} \description{ Functionally identical to \code{devtools}' \code{as.package}, but without interactive options for package creation. diff --git a/man/as_list_of_srcref.Rd b/man/as_list_of_srcref.Rd index 3cd6abb..22d2597 100644 --- a/man/as_list_of_srcref.Rd +++ b/man/as_list_of_srcref.Rd @@ -13,7 +13,10 @@ as_list_of_srcref(x) \method{as_list_of_srcref}{list}(x) } \arguments{ -\item{x}{A list or single srcref to coerce to a list_of_srcref} +\item{x}{A list or single srcref to coerce to a \code{list_of_srcref}} +} +\value{ +A \code{list_of_srcref} class object } \description{ Create an S3 list of srcref objects diff --git a/man/coverage_check_has_recorded_tests.Rd b/man/coverage_check_has_recorded_tests.Rd index eb5ddec..e432791 100644 --- a/man/coverage_check_has_recorded_tests.Rd +++ b/man/coverage_check_has_recorded_tests.Rd @@ -13,6 +13,11 @@ coverage_check_has_recorded_tests(coverage, warn = TRUE) recorded. It may be uncertain if tests were recorded if there are no tested R code traces.} } +\value{ +Used for side-effects of emitting an error when a coverage object +does not contain recorded traces, or a warning when a coverage object +appears to have no tests. +} \description{ Check whether the coverage object has expected fields produced when coverage was captured with \code{option(covr.record_tests = TRUE)}, throwing an error diff --git a/man/coverage_get_tests.Rd b/man/coverage_get_tests.Rd index c778323..befb57a 100644 --- a/man/coverage_get_tests.Rd +++ b/man/coverage_get_tests.Rd @@ -9,6 +9,9 @@ coverage_get_tests(coverage) \arguments{ \item{coverage}{a \code{\link[covr]{covr}} coverage object} } +\value{ +A \code{list} of tests evaluated when using \code{covr} +} \description{ Assumes the coverage object was produced while \code{option(covr.record_tests = TRUE)}. diff --git a/man/coverage_has_recorded_tests.Rd b/man/coverage_has_recorded_tests.Rd index 01a4da2..7c2850c 100644 --- a/man/coverage_has_recorded_tests.Rd +++ b/man/coverage_has_recorded_tests.Rd @@ -9,6 +9,11 @@ coverage_has_recorded_tests(coverage) \arguments{ \item{coverage}{a \code{\link[covr]{covr}} coverage object} } +\value{ +A \code{logical} value, indicating whether the coverage object has +recorded tests, or \code{NA} when it does not appear to have traced any test +code. +} \description{ Test whether the coverage object has expected fields produced when coverage was captured with \code{option(covr.record_tests = TRUE)}. diff --git a/man/dot-tools.Rd b/man/dot-tools.Rd deleted file mode 100644 index 6d50682..0000000 --- a/man/dot-tools.Rd +++ /dev/null @@ -1,16 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/utils.R -\docType{data} -\name{.tools} -\alias{.tools} -\title{Loading unexported helpers from tools} -\format{ -An object of class \code{list} of length 4. -} -\usage{ -.tools -} -\description{ -Loading unexported helpers from tools -} -\keyword{datasets} diff --git a/man/expr_str.Rd b/man/expr_str.Rd index 67f8a3d..1ad180e 100644 --- a/man/expr_str.Rd +++ b/man/expr_str.Rd @@ -9,6 +9,10 @@ expr_str(ref) \arguments{ \item{ref}{a \code{srcref}} } +\value{ +The given expression, formatted as a string with prefixes for +symbols and generics. +} \description{ Convert an expression, call or symbol to a single-line string } diff --git a/man/flat_map_srcrefs.Rd b/man/flat_map_srcrefs.Rd index 166001f..80d4ea6 100644 --- a/man/flat_map_srcrefs.Rd +++ b/man/flat_map_srcrefs.Rd @@ -18,6 +18,9 @@ namespace will not be replaced.} traversing the namespace used as a memory of what we've seen already), which is used for short-circuiting recursive environment traversal.} } +\value{ +A \code{list} of \code{srcref}s +} \description{ Map \code{srcrefs} over an iterable object, Filtering non-srcref results } diff --git a/man/format.list_of_srcref.Rd b/man/format.list_of_srcref.Rd index 2c173be..e51b094 100644 --- a/man/format.list_of_srcref.Rd +++ b/man/format.list_of_srcref.Rd @@ -17,6 +17,9 @@ paths when formatting \code{srcref}s.} \item{full.num}{A \code{logical} value indicating whether to use all numeric \code{srcref} components when formatting \code{srcref}s.} } +\value{ +A \code{character} vector of formatted strings +} \description{ Format list_of_srcref as character } diff --git a/man/get_namespace_object_names.Rd b/man/get_namespace_object_names.Rd index 29f754e..a4a762f 100644 --- a/man/get_namespace_object_names.Rd +++ b/man/get_namespace_object_names.Rd @@ -9,6 +9,10 @@ get_namespace_object_names(ns) \arguments{ \item{ns}{A namespace object} } +\value{ +The names of exported objects, filtering internal method tables and +metadata. +} \description{ Get namespace exports, filtering methods tables and definitions } diff --git a/man/is_srcref.Rd b/man/is_srcref.Rd index d450bbe..41ffc8e 100644 --- a/man/is_srcref.Rd +++ b/man/is_srcref.Rd @@ -9,6 +9,9 @@ is_srcref(x) \arguments{ \item{x}{Any object} } +\value{ +A \code{logical} indicating whether object is a \code{srcref} +} \description{ Test whether an object is a \code{srcref} object } diff --git a/man/join_on_containing_srcrefs.Rd b/man/join_on_containing_srcrefs.Rd index 0119d83..6e168b6 100644 --- a/man/join_on_containing_srcrefs.Rd +++ b/man/join_on_containing_srcrefs.Rd @@ -19,6 +19,9 @@ The name should be the name of the column from the left \code{data.frame} containing a \code{list_of_srcref} column, and the value should be the name of a column from the right \code{data.frame} containing a \code{list_of_srcref} column.} } +\value{ +A \code{data.frame} of \code{x} joined on \code{y} by spanning \code{srcref} +} \description{ References to source code are defined by the source code line and column span of the relevant source code. This function takes data frames containing that diff --git a/man/match_containing_srcrefs.Rd b/man/match_containing_srcrefs.Rd index 48e56fc..340df12 100644 --- a/man/match_containing_srcrefs.Rd +++ b/man/match_containing_srcrefs.Rd @@ -11,6 +11,10 @@ match_containing_srcrefs(l, r) \item{r}{A \code{list_of_srcref} object} } +\value{ +A \code{integer} vector of the first index in \code{r} that fully encapsulate +the respective element in \code{l} +} \description{ Provided two lists of \code{srcref} objects, find the first \code{srcrefs} in \code{r} that entirely encapsulate each respective \code{srcref} in \code{l}, returning a list of diff --git a/man/new_empty_test_trace_tally.Rd b/man/new_empty_test_trace_tally.Rd index f0f12b9..7f41617 100644 --- a/man/new_empty_test_trace_tally.Rd +++ b/man/new_empty_test_trace_tally.Rd @@ -6,6 +6,9 @@ \usage{ new_empty_test_trace_tally() } +\value{ +An empty test-trace matrix, as provided by \code{covr} +} \description{ Build an empty covr-style test trace mapping } diff --git a/man/obj_namespace_name.Rd b/man/obj_namespace_name.Rd index 667434a..b74cc02 100644 --- a/man/obj_namespace_name.Rd +++ b/man/obj_namespace_name.Rd @@ -11,6 +11,9 @@ obj_namespace_name(x, ns) \item{ns}{A package namespace} } +\value{ +A \code{character} string representing a namespace or similar +} \description{ For most objects, this will be identical to the namespace name provided, but reexports will retain their originating package's namespace name. This helper diff --git a/man/package_check_has_keep_source.Rd b/man/package_check_has_keep_source.Rd index ce5c919..80b4c12 100644 --- a/man/package_check_has_keep_source.Rd +++ b/man/package_check_has_keep_source.Rd @@ -10,6 +10,10 @@ package_check_has_keep_source(env) \item{env}{A package namespace environment or iterable collection of package objects} } +\value{ +Used for side effect of throwing an error when a package was not +installed with \code{srcref}s. +} \description{ Test whether the package object collection contains srcref attributes. } diff --git a/man/pkg_srcrefs.Rd b/man/pkg_srcrefs.Rd index fca581a..377a6ca 100644 --- a/man/pkg_srcrefs.Rd +++ b/man/pkg_srcrefs.Rd @@ -19,14 +19,18 @@ pkg_srcrefs(x) \item{x}{A \code{\link[covr]{package_coverage}} coverage object, from which the name of the package used is extracted.} } +\value{ +A \code{list_of_srcref} +} \description{ Extract all the srcref objects of objects within a package namespace } \examples{ pkg <- system.file("examplepkg", package = "covtracer") -remotes::install_local( +install.packages( pkg, - force = TRUE, + type = "source", + repos = NULL, quiet = TRUE, INSTALL_opts = "--with-keep.source" ) diff --git a/man/pkg_srcrefs_df.Rd b/man/pkg_srcrefs_df.Rd index db0fbbb..963e8a1 100644 --- a/man/pkg_srcrefs_df.Rd +++ b/man/pkg_srcrefs_df.Rd @@ -24,9 +24,10 @@ Create a data.frame of package srcref objects } \examples{ pkg <- system.file("examplepkg", package = "covtracer") -remotes::install_local( +install.packages( pkg, - force = TRUE, + type = "source", + repos = NULL, quiet = TRUE, INSTALL_opts = "--with-keep.source" ) diff --git a/man/srcref_expr.Rd b/man/srcref_expr.Rd index fd0e97a..bfd759e 100644 --- a/man/srcref_expr.Rd +++ b/man/srcref_expr.Rd @@ -9,6 +9,9 @@ srcref_expr(ref) \arguments{ \item{ref}{a \code{srcref}} } +\value{ +A parsed \code{srcref} object +} \description{ Parse the expression associated with a srcref } diff --git a/man/srcref_str.Rd b/man/srcref_str.Rd index 389e144..1922777 100644 --- a/man/srcref_str.Rd +++ b/man/srcref_str.Rd @@ -2,13 +2,16 @@ % Please edit documentation in R/test_description.R \name{srcref_str} \alias{srcref_str} -\title{Parse the expression associated with a srcref} +\title{Convert a srcref into a string} \usage{ srcref_str(ref) } \arguments{ \item{ref}{a \code{srcref}} } +\value{ +A string representing the \code{srcref} +} \description{ -Parse the expression associated with a srcref +Convert a srcref into a string } diff --git a/man/test_description_test_that.Rd b/man/test_description_test_that.Rd index 68884c5..abe2b80 100644 --- a/man/test_description_test_that.Rd +++ b/man/test_description_test_that.Rd @@ -11,6 +11,9 @@ test_description_test_that(x, ...) \item{...}{Additional arguments unused} } +\value{ +A \code{character} description, parsed from a \code{test_that::test_that} call +} \description{ Parse the test description from a \code{test_that} call } diff --git a/man/test_description_test_that_describe.Rd b/man/test_description_test_that_describe.Rd index eb31f06..a0a2a59 100644 --- a/man/test_description_test_that_describe.Rd +++ b/man/test_description_test_that_describe.Rd @@ -7,10 +7,13 @@ test_description_test_that_describe(x, ...) } \arguments{ -\item{x}{A test_that::describe call object} +\item{x}{A \code{test_that::describe} call object} \item{...}{Additional arguments unused} } +\value{ +A \code{character} description, parsed from a \code{test_that::describe} call +} \description{ Parse the test description from a \code{describe} call } diff --git a/man/test_description_test_that_describe_it.Rd b/man/test_description_test_that_describe_it.Rd index 4273fd6..4e52f77 100644 --- a/man/test_description_test_that_describe_it.Rd +++ b/man/test_description_test_that_describe_it.Rd @@ -7,10 +7,13 @@ test_description_test_that_describe_it(x, ...) } \arguments{ -\item{x}{A test_that::describe call object} +\item{x}{A \code{test_that::it} call object} \item{...}{Additional arguments unused} } +\value{ +A \code{character} description, parsed from a \code{test_that::it} call +} \description{ Parse the test description from a \code{it} call } diff --git a/man/test_srcrefs.Rd b/man/test_srcrefs.Rd index bcbbf12..83c0aff 100644 --- a/man/test_srcrefs.Rd +++ b/man/test_srcrefs.Rd @@ -13,6 +13,9 @@ test_srcrefs(x) \item{x}{A \code{\link[covr]{package_coverage}} coverage object, from which the test \code{srcref}s are extracted.} } +\value{ +A \code{list_of_srcref} +} \description{ Extract test srcref objects } diff --git a/man/test_trace_df.Rd b/man/test_trace_df.Rd index f59f062..e30c942 100644 --- a/man/test_trace_df.Rd +++ b/man/test_trace_df.Rd @@ -28,6 +28,9 @@ package object is to be used for inspecting the package namespace.} \code{counts} and \code{direct} columns from a test to a trace. If \code{NULL}, no aggregation will be applied. (Default \code{sum})} } +\value{ +A \code{data.frame} of tests and corresponding traces +} \description{ Intercept unit test coverage reports and process results to link evaluated functions to the unit tests which trigger their evaluation. In doing so, diff --git a/man/trace_srcrefs.Rd b/man/trace_srcrefs.Rd index d0f2113..f0a0570 100644 --- a/man/trace_srcrefs.Rd +++ b/man/trace_srcrefs.Rd @@ -13,6 +13,9 @@ trace_srcrefs(x) \item{x}{(\code{link[covr]{package_coverage}}) A \code{\link[covr]{covr}} coverage object produced with \code{options(covr.record_tests = TRUE)}.} } +\value{ +A \code{list_of_srcref} +} \description{ Extract srcref objects from coverage object traces } diff --git a/man/with_pseudo_srcref.Rd b/man/with_pseudo_srcref.Rd index b485f8f..98542bc 100644 --- a/man/with_pseudo_srcref.Rd +++ b/man/with_pseudo_srcref.Rd @@ -13,6 +13,9 @@ with_pseudo_srcref(call, file, lloc) \item{lloc}{A \code{srcef}-like \code{lloc} numeric vector} } +\value{ +A \code{with_pseudo_srcref} object, mimicking the structure of \code{srcref} +} \description{ Most relevant data can be traced to an existing srcref. However, some data, such as test traces from coverage objects, are likely cleaned up and their diff --git a/tests/testthat/packages/no.exports/R/no-code.R b/tests/testthat/packages/no.exports/R/no-code.R index 8b13789..e69de29 100644 --- a/tests/testthat/packages/no.exports/R/no-code.R +++ b/tests/testthat/packages/no.exports/R/no-code.R @@ -1 +0,0 @@ - diff --git a/tests/testthat/setup_test_packages.R b/tests/testthat/setup_test_packages.R index 00a6251..a0cf1bb 100644 --- a/tests/testthat/setup_test_packages.R +++ b/tests/testthat/setup_test_packages.R @@ -2,6 +2,14 @@ cli::cli_alert_info("Installing test packages") cli::cli_ul() cli::cli_ul() +on_cran <- tryCatch( + { + testthat::skip_on_cran() + FALSE + }, + condition = function(c) TRUE +) + opts <- list( "keep.source" = TRUE, "keep.source.pkgs" = TRUE, @@ -17,13 +25,13 @@ dir.create(lib <- tempfile("ct_"), recursive = TRUE) .libPaths(c(lib, .libPaths())) tests <- normalizePath(testthat::test_path()) -pkg_dirs <- list( +pkg_dirs <- Filter(Negate(is.null), list( system.file("examplepkg", package = "covtracer"), file.path(tests, "packages", "list.obj"), file.path(tests, "packages", "no.evaluable.code"), file.path(tests, "packages", "no.exports"), - file.path(tests, "packages", "reexport.srcref") -) + if (!on_cran) file.path(tests, "packages", "reexport.srcref") +)) # install our testing packages into a temp directory install.packages( diff --git a/tests/testthat/test_case_list_obj.R b/tests/testthat/test_case_list_obj.R index 8b13789..e69de29 100644 --- a/tests/testthat/test_case_list_obj.R +++ b/tests/testthat/test_case_list_obj.R @@ -1 +0,0 @@ - diff --git a/tests/testthat/test_match_containing_srcrefs.R b/tests/testthat/test_match_containing_srcrefs.R index c02abca..fe45aee 100644 --- a/tests/testthat/test_match_containing_srcrefs.R +++ b/tests/testthat/test_match_containing_srcrefs.R @@ -24,9 +24,9 @@ test_that("match_containing_srcrefs matches tests to appropriate code by file", (trace_beg_line > pkgsrc_beg_line || trace_beg_line == pkgsrc_beg_line && trace_beg_byte >= pkgsrc_beg_byte) && - # and trace ends at or before end of srcref region - (trace_end_line < pkgsrc_end_line || - trace_end_line == pkgsrc_end_line && trace_end_byte <= pkgsrc_end_byte) + # and trace ends at or before end of srcref region + (trace_end_line < pkgsrc_end_line || + trace_end_line == pkgsrc_end_line && trace_end_byte <= pkgsrc_end_byte) } ))) }) @@ -35,16 +35,16 @@ test_that("match_containing_srcrefs matches tests even when some objects have no # may be the case for reexports from packages built --without-keep.source" traces <- as_list_of_srcref.list(list( - "a" = srcref(srcfile = srcfile("a"), c(1,0,1,10,0,10,0,0)), # from a file not in the package - "c" = srcref(srcfile = srcfile("c"), c(2,0,2,10,0,10,0,0)) + "a" = srcref(srcfile = srcfile("a"), c(1, 0, 1, 10, 0, 10, 0, 0)), # from a file not in the package + "c" = srcref(srcfile = srcfile("c"), c(2, 0, 2, 10, 0, 10, 0, 0)) )) # spoof a package with missing srcrefs pkgsrcs <- as_list_of_srcref.list(list( - NA, - "b" = srcref(srcfile = srcfile("b"), c(1,0,1,10,0,10,0,0)), - "c" = srcref(srcfile = srcfile("c"), c(1,0,3, 1,0, 1,0,0)), - NA + NA, + "b" = srcref(srcfile = srcfile("b"), c(1, 0, 1, 10, 0, 10, 0, 0)), + "c" = srcref(srcfile = srcfile("c"), c(1, 0, 3, 1, 0, 1, 0, 0)), + NA )) expect_silent(m <- match_containing_srcrefs(traces, pkgsrcs)) diff --git a/tests/testthat/test_pkg_srcrefs.R b/tests/testthat/test_pkg_srcrefs.R index 91bfce6..5c1ece0 100644 --- a/tests/testthat/test_pkg_srcrefs.R +++ b/tests/testthat/test_pkg_srcrefs.R @@ -36,6 +36,7 @@ test_that("pkg_srcrefs discovers namespace of package objects", { }) test_that("pkg_srcrefs distinguish namespaces of reexported object srcrefs", { + skip_on_cran() expect_s3_class(srcs <- pkg_srcrefs(reexport.srcref_ns), "list_of_srcref") expect_true("examplepkg" %in% lapply(srcs, attr, "namespace")) }) diff --git a/tests/testthat/test_srcrefs.R b/tests/testthat/test_srcrefs.R index 2c6ccf6..430699d 100644 --- a/tests/testthat/test_srcrefs.R +++ b/tests/testthat/test_srcrefs.R @@ -100,6 +100,8 @@ test_that("srcrefs return lists uses names which can be linked to object docs", test_that("srcrefs does not recursve into self-referential envs", { env <- new.env() env$self <- env - env$fn <- function(a = 1) { print(a) } + env$fn <- function(a = 1) { + print(a) + } expect_length(names(srcrefs(env)), 1L) }) diff --git a/tests/testthat/test_test_description.R b/tests/testthat/test_test_description.R index b09a130..42d1491 100644 --- a/tests/testthat/test_test_description.R +++ b/tests/testthat/test_test_description.R @@ -1,18 +1,18 @@ test_that("test_description can build a test descriptions a call with a srcref", { # parsing from an expression with src file parse_content <- parse(file = file.path(test_path(), "test_test_description.R"), keep.source = TRUE) - x_list <- getSrcref(parse_content) # list of file expressions + x_list <- getSrcref(parse_content) # list of file expressions expect_equal(class(x_list), "list") - x_srcref <- x_list[[1L]] # first expression (this test) + x_srcref <- x_list[[1L]] # first expression (this test) expect_s3_class(x_srcref, "srcref") - expect_match(test_description(parse_content), "test_description can build") # full expression - expect_match(test_description(x_srcref), "\\[test_test_description.R#L\\d+\\]") # a single srcref - expect_match(test_description(x_srcref), "test_that\\(\"test_description can build") # a single srcref + expect_match(test_description(parse_content), "test_description can build") # full expression + expect_match(test_description(x_srcref), "\\[test_test_description.R#L\\d+\\]") # a single srcref + expect_match(test_description(x_srcref), "test_that\\(\"test_description can build") # a single srcref # parsing from an expression without src file parse_content <- parse(file = file.path(test_path(), "test_test_description.R"), keep.source = FALSE) - expect_equal(getSrcref(parse_content), NULL) # content without srcref - expect_match(test_description(parse_content), "^[^[]") # does not start with file name + expect_equal(getSrcref(parse_content), NULL) # content without srcref + expect_match(test_description(parse_content), "^[^[]") # does not start with file name expect_match(test_description(parse_content), "test_description can build") }) diff --git a/vignettes/combining_srcref_data.Rmd b/vignettes/combining_srcref_data.Rmd index a7fe1e1..12a9115 100644 --- a/vignettes/combining_srcref_data.Rmd +++ b/vignettes/combining_srcref_data.Rmd @@ -32,14 +32,14 @@ we can use to showcase these relationships: ```{r, message = FALSE, warning = FALSE} library(withr) library(covr) -library(remotes) withr::with_temp_libpaths({ options(keep.source = TRUE, keep.source.pkg = TRUE, covr.record_tests = TRUE) examplepkg_source_path <- system.file("examplepkg", package = "covtracer") - remotes::install_local( - examplepkg_source_path, - quiet = TRUE, + install.packages( + examplepkg_source_path, + type = "source", + repos = NULL, INSTALL_opts = c("--with-keep.source", "--install-tests") ) examplepkg_cov <- covr::package_coverage(examplepkg_source_path) diff --git a/vignettes/plotting_test_paths.Rmd b/vignettes/plotting_test_paths.Rmd index 9732cf7..ea94080 100644 --- a/vignettes/plotting_test_paths.Rmd +++ b/vignettes/plotting_test_paths.Rmd @@ -34,7 +34,6 @@ library(covtracer) library(withr) library(covr) -library(remotes) ``` ```{r libpaths, include = FALSE} @@ -47,10 +46,11 @@ dir.create(lib <- tempfile("covtracer_pkgs_")) options(keep.source = TRUE, keep.source.pkg = TRUE, covr.record_tests = TRUE) examplepkg_source_path <- system.file("examplepkg", package = "covtracer") -remotes::install_local( +install.packages( examplepkg_source_path, + type = "source", + repos = NULL, quiet = TRUE, - force = TRUE, INSTALL_opts = c("--with-keep.source", "--install-tests") ) @@ -156,7 +156,7 @@ plot.igraph(g, vertex.label.family = "sans", vertex.label.color = "black", vertex.label.dist = 1, - vertex.label.degree = - pi / 2, + vertex.label.degree = -pi / 2, vertex.label.cex = 0.8, mark.border = NA, margin = c(0, 0.2, 0, 0.2) diff --git a/vignettes/working_with_srcrefs.Rmd b/vignettes/working_with_srcrefs.Rmd index 55755a1..2dd3955 100644 --- a/vignettes/working_with_srcrefs.Rmd +++ b/vignettes/working_with_srcrefs.Rmd @@ -13,7 +13,15 @@ knitr::opts_chunk$set( comment = "#>" ) -options(width = 120L) +local({ + source_hook <- knitr::knit_hooks$get("source") + knitr::knit_hooks$set(source = function(x, options) { + old_opts <- options(width = 120L) + on.exit(options(old_opts)) + + source_hook(x, options) + }) +}) ``` ```{r setup} @@ -23,13 +31,14 @@ library(covtracer) ```{r, include = FALSE} library(withr) library(covr) -library(remotes) withr::with_temp_libpaths({ options(keep.source = TRUE, keep.source.pkg = TRUE, covr.record_tests = TRUE) examplepkg_source_path <- system.file("examplepkg", package = "covtracer") - remotes::install_local( + install.packages( examplepkg_source_path, + type = "source", + repos = NULL, quiet = TRUE, INSTALL_opts = c("--with-keep.source", "--install-tests") ) @@ -38,12 +47,12 @@ withr::with_temp_libpaths({ }) ``` +# What are `srcref` objects? + ```{r} print(examplepkg_ns$hypotenuse) ``` -# What are `srcref` objects? - `srcref`s are a base R data type that is used frequently for working with package code. When you install a package using the `--with-keep.source` flag, data about the package's source code representation is bound to the objects that @@ -89,14 +98,14 @@ into our working library. ```{r, eval = FALSE} library(withr) library(covr) -library(remotes) withr::with_temp_libpaths({ options(keep.source = TRUE, keep.source.pkg = TRUE, covr.record_tests = TRUE) examplepkg_source_path <- system.file("examplepkg", package = "covtracer") - remotes::install_local( + install.packages( examplepkg_source_path, - quiet = TRUE, + type = "source", + repos = NULL, INSTALL_opts = c("--with-keep.source", "--install-tests") ) examplepkg_cov <- covr::package_coverage(examplepkg_source_path) @@ -122,7 +131,7 @@ and readability. Getting a `list` of `srcref`s ```{r, include = FALSE} -pkg_srcrefs(examplepkg_ns)[1] # for brevity, only showing first srcref +pkg_srcrefs(examplepkg_ns)[1] # for brevity, only showing first srcref ``` ```{r, eval = FALSE}