|
15 | 15 | #' # If run with no arguments `report()` implicitly calls `package_coverage()` |
16 | 16 | #' report() |
17 | 17 | #' ``` |
| 18 | +#' |
| 19 | +#' @section Package options: |
| 20 | +#' |
| 21 | +#' `covr` uses the following [options()] to configure behaviour: |
| 22 | +#' |
| 23 | +#' \itemize{ |
| 24 | +#' \item `covr.covrignore`: A filename to use as an ignore file, |
| 25 | +#' listing glob-style wildcarded paths of files to ignore for coverage |
| 26 | +#' calculations. Defaults to the value of environment variable |
| 27 | +#' `COVR_COVRIGNORE`, or `".covrignore"` if the neither the option nor the |
| 28 | +#' environment variable are set. |
| 29 | +#' |
| 30 | +#' \item `covr.exclude_end`: Used along with `covr.exclude_start`, an optional |
| 31 | +#' regular expression which ends a line-exclusion region. For more |
| 32 | +#' details, see `?exclusions`. |
| 33 | +#' |
| 34 | +#' \item `covr.exclude_pattern`: An optional line-exclusion pattern. Lines |
| 35 | +#' which match the pattern will be excluded from coverage. For more details, |
| 36 | +#' see `?exclusions`. |
| 37 | +#' |
| 38 | +#' \item `covr.exclude_start`: Used along with `covr.exclude_end`, an optional |
| 39 | +#' regular expression which starts a line-exclusion region. For more |
| 40 | +#' details, see `?exclusions`. |
| 41 | +#' |
| 42 | +#' \item `covr.filter_non_package`: If `TRUE` (the default behavior), coverage |
| 43 | +#' of files outside the target package are filtered from coverage output. |
| 44 | +#' |
| 45 | +#' \item `covr.fix_parallel_mcexit`: |
| 46 | +#' |
| 47 | +#' \item `covr.flags`: |
| 48 | +#' |
| 49 | +#' \item `covr.gcov`: If the appropriate gcov version is not on your path you |
| 50 | +#' can use this option to set the appropriate location. If set to "" it will |
| 51 | +#' turn off coverage of compiled code. |
| 52 | +#' |
| 53 | +#' \item `covr.gcov_additional_paths`: |
| 54 | +#' |
| 55 | +#' \item `covr.gcov_args`: |
| 56 | +#' |
| 57 | +#' \item `covr.icov`: |
| 58 | +#' |
| 59 | +#' \item `covr.icov_args`: |
| 60 | +#' |
| 61 | +#' \item `covr.icov_flags`: |
| 62 | +#' |
| 63 | +#' \item `covr.icov_prof`: |
| 64 | +#' |
| 65 | +#' \item `covr.rstudio_source_markers`: A logical value. If `TRUE` (the |
| 66 | +#' default behavior), source markers are displayed within the RStudio IDE |
| 67 | +#' when using `zero_coverage`. |
| 68 | +#' |
| 69 | +#' \item `covr.record_tests`: If `TRUE` (default `NULL`), record a listing of |
| 70 | +#' top level test expressions and associate tests with `covr` traces |
| 71 | +#' evaluated during the test's execution. For more details, see |
| 72 | +#' `?covr.record_tests`. |
| 73 | +#' |
| 74 | +#' \item `covr.showCfunctions`: |
| 75 | +#' } |
| 76 | +#' |
| 77 | +#' |
18 | 78 | "_PACKAGE" |
19 | 79 |
|
20 | 80 | #' @import methods |
@@ -47,6 +107,26 @@ save_trace <- function(directory) { |
47 | 107 | saveRDS(.counters, file = tmp_file) |
48 | 108 | } |
49 | 109 |
|
| 110 | +#' Convert a counters object to a coverage object |
| 111 | +#' |
| 112 | +#' @param counters An environment of covr trace results to convert to a coverage |
| 113 | +#' object. If `counters` is not provided, the `covr` namespace value |
| 114 | +#' `.counters` is used. |
| 115 | +#' @param ... Additional attributes to include with the coverage object. |
| 116 | +#' |
| 117 | +as_coverage <- function(counters = NULL, ...) { |
| 118 | + if (missing(counters)) |
| 119 | + counters <- .counters |
| 120 | + |
| 121 | + counters <- as.list(counters) |
| 122 | + |
| 123 | + # extract optional tests |
| 124 | + tests <- counters$tests |
| 125 | + counters$tests <- NULL |
| 126 | + |
| 127 | + structure(counters, tests = tests, ..., class = "coverage") |
| 128 | +} |
| 129 | + |
50 | 130 | #' Calculate test coverage for a specific function. |
51 | 131 | #' |
52 | 132 | #' @param fun name of the function. |
@@ -81,7 +161,7 @@ function_coverage <- function(fun, code = NULL, env = NULL, enc = parent.frame() |
81 | 161 | eval(code, enc) |
82 | 162 | ) |
83 | 163 |
|
84 | | - structure(as.list(.counters), class = "coverage") |
| 164 | + as_coverage(as.list(.counters)) |
85 | 165 | } |
86 | 166 |
|
87 | 167 | #' Calculate test coverage for sets of files |
@@ -121,7 +201,7 @@ file_coverage <- function( |
121 | 201 | sys.source, keep.source = TRUE, envir = env) |
122 | 202 | ) |
123 | 203 |
|
124 | | - coverage <- structure(as.list(.counters), class = "coverage") |
| 204 | + coverage <- as_coverage(.counters) |
125 | 205 |
|
126 | 206 | exclude(coverage, |
127 | 207 | line_exclusions = line_exclusions, |
@@ -178,7 +258,7 @@ environment_coverage <- function( |
178 | 258 | sys.source, keep.source = TRUE, envir = exec_env) |
179 | 259 | ) |
180 | 260 |
|
181 | | - coverage <- structure(as.list(.counters), class = "coverage") |
| 261 | + coverage <- as_coverage(.counters) |
182 | 262 |
|
183 | 263 | exclude(coverage, |
184 | 264 | line_exclusions = line_exclusions, |
@@ -382,10 +462,11 @@ package_coverage <- function(path = ".", |
382 | 462 | res <- run_icov(pkg$path, quiet = quiet) |
383 | 463 | } |
384 | 464 |
|
385 | | - coverage <- structure(c(coverage, res), |
386 | | - class = "coverage", |
387 | | - package = pkg, |
388 | | - relative = relative_path) |
| 465 | + coverage <- as_coverage( |
| 466 | + c(coverage, res), |
| 467 | + package = pkg, |
| 468 | + relative = relative_path |
| 469 | + ) |
389 | 470 |
|
390 | 471 | if (!clean) { |
391 | 472 | attr(coverage, "library") <- install_path |
@@ -454,29 +535,46 @@ show_failures <- function(dir) { |
454 | 535 | # merge multiple coverage files together. Assumes the order of coverage lines |
455 | 536 | # is the same in each object, this should always be the case if the objects are |
456 | 537 | # from the same initial library. |
457 | | -merge_coverage <- function(files) { |
458 | | - nfiles <- length(files) |
459 | | - if (nfiles == 0) { |
460 | | - return() |
461 | | - } |
| 538 | +merge_coverage <- function(x) { |
| 539 | + UseMethod("merge_coverage") |
| 540 | +} |
462 | 541 |
|
463 | | - x <- suppressWarnings(readRDS(files[1])) |
464 | | - x <- as.list(x) |
465 | | - if (nfiles == 1) { |
466 | | - return(x) |
| 542 | +merge_coverage.character <- function(files) { |
| 543 | + coverage_objs <- lapply(files, function(f) { |
| 544 | + as.list(suppressWarnings(readRDS(f))) |
| 545 | + }) |
| 546 | + merge_coverage(coverage_objs) |
| 547 | +} |
| 548 | + |
| 549 | +merge_coverage.list <- function(coverage_objs) { |
| 550 | + if (length(coverage_objs) == 0) { |
| 551 | + return() |
467 | 552 | } |
468 | 553 |
|
| 554 | + x <- coverage_objs[[1]] |
469 | 555 | names <- names(x) |
470 | | - for (i in 2:nfiles) { |
471 | | - y <- suppressWarnings(readRDS(files[i])) |
| 556 | + |
| 557 | + for (y in tail(coverage_objs, -1L)) { |
| 558 | + # align tests from coverage objects |
| 559 | + test_idx <- match(names(y$tests), Filter(nchar, names(x$tests))) |
| 560 | + new_test_idx <- if (!length(test_idx)) seq_along(y$tests) else which(is.na(test_idx)) |
| 561 | + test_idx[new_test_idx] <- length(x$tests) + seq_along(new_test_idx) |
| 562 | + |
| 563 | + # append any tests that we haven't encountered in previous objects |
| 564 | + x$tests <- append(x$tests, y$tests[new_test_idx]) |
| 565 | + y$tests <- NULL |
| 566 | + |
472 | 567 | for (name in intersect(names, names(y))) { |
473 | 568 | x[[name]]$value <- x[[name]]$value + y[[name]]$value |
| 569 | + y[[name]]$tests[,1] <- test_idx[y[[name]]$tests[,1]] |
| 570 | + x[[name]]$tests <- rbind(x[[name]]$tests, y[[name]]$tests) |
474 | 571 | } |
| 572 | + |
475 | 573 | for (name in setdiff(names(y), names)) { |
476 | 574 | x[[name]] <- y[[name]] |
477 | 575 | } |
| 576 | + |
478 | 577 | names <- union(names, names(y)) |
479 | | - y <- NULL |
480 | 578 | } |
481 | 579 |
|
482 | 580 | x |
@@ -550,13 +648,16 @@ run_commands <- function(pkg, lib, commands) { |
550 | 648 | # @param pkg_name name of the package to add hooks to |
551 | 649 | # @param lib the library path to look in |
552 | 650 | # @param fix_mcexit whether to add the fix for mcparallel:::mcexit |
553 | | -add_hooks <- function(pkg_name, lib, fix_mcexit = FALSE) { |
| 651 | +add_hooks <- function(pkg_name, lib, fix_mcexit = FALSE, |
| 652 | + record_tests = isTRUE(getOption("covr.record_tests", FALSE))) { |
| 653 | + |
554 | 654 | trace_dir <- paste0("Sys.getenv(\"COVERAGE_DIR\", \"", lib, "\")") |
555 | 655 |
|
556 | 656 | load_script <- file.path(lib, pkg_name, "R", pkg_name) |
557 | 657 | lines <- readLines(file.path(lib, pkg_name, "R", pkg_name)) |
558 | 658 | lines <- append(lines, |
559 | | - c("setHook(packageEvent(pkg, \"onLoad\"), function(...) covr:::trace_environment(ns))", |
| 659 | + c(paste0("setHook(packageEvent(pkg, \"onLoad\"), function(...) options(covr.record_tests = ", record_tests, "))"), |
| 660 | + "setHook(packageEvent(pkg, \"onLoad\"), function(...) covr:::trace_environment(ns))", |
560 | 661 | paste0("reg.finalizer(ns, function(...) { covr:::save_trace(", trace_dir, ") }, onexit = TRUE)")), |
561 | 662 | length(lines) - 1L) |
562 | 663 |
|
|
0 commit comments