diff --git a/.Rbuildignore b/.Rbuildignore index a1c747a..3da6c38 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -11,3 +11,4 @@ ^codecov\.yml$ ^README\.html$ ^README_cache$ +^\.lintr diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index f6fab3e..0df5e77 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -24,11 +24,12 @@ jobs: - {os: windows-latest, r: 'release'} - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - {os: ubuntu-latest, r: 'release'} - - {os: ubuntu-latest, r: 'oldrel-1'} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} R_KEEP_PKG_SOURCE: yes + _R_CHECK_CRAN_INCOMING_: false + _R_CHECK_FORCE_SUGGESTS_: false NOT_CRAN: "true" steps: diff --git a/.lintr b/.lintr new file mode 100644 index 0000000..e1a22e9 --- /dev/null +++ b/.lintr @@ -0,0 +1,14 @@ +linters: + linters_with_defaults( + defaults = default_linters, + object_length_linter = NULL, + object_name_linter = NULL, + cyclocomp_linter = NULL + ) +exclusions: + list( + "tests/testthat/testing_pkgs", + "tests/testthat/fixtures", + "inst/" + ) + diff --git a/DESCRIPTION b/DESCRIPTION index a90b028..c22d221 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: checked Title: Systematically Run R CMD Checks -Version: 0.2.9 +Version: 0.5.0 Authors@R: c( person( @@ -11,8 +11,8 @@ Authors@R: comment = c(ORCID = "0000-0002-3120-1601") ), person( - "Doug", "Kelkhoff", , - "doug.kelkhoff@gmail.com", + "Doug", "Kelkhoff", + email = "doug.kelkhoff@gmail.com", role = c("aut"), comment = c(ORCID = "0009-0003-7845-4061") ), @@ -31,20 +31,27 @@ URL: BugReports: https://github.com/Genentech/checked/issues License: MIT + file LICENSE Encoding: UTF-8 +Depends: + R (>= 3.6.2) Imports: callr, cli, + glue, igraph, jsonlite, + memoise, options, R6, rcmdcheck, + rlang, utils (>= 3.6.2), tools Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.2 -Suggests: +RoxygenNote: 7.3.3 +Suggests: + remotes, testthat (>= 3.0.0), + visNetwork, withr Config/Needs/website: r-lib/asciicast diff --git a/NAMESPACE b/NAMESPACE index 94d0011..838c894 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,79 +1,154 @@ # Generated by roxygen2: do not edit by hand -S3method("[",checked_results) -S3method(check_path,package_spec) -S3method(check_path,package_spec_archive_source) -S3method(check_path,package_spec_source) +S3method("$",enum) +S3method(Ops,enum) +S3method(as_vertex_name,default) +S3method(as_vertex_name,local_check_meta_task) +S3method(as_vertex_name,task) +S3method(check_path,pkg_origin) +S3method(check_path,pkg_origin_archive) +S3method(check_path,pkg_origin_local) +S3method(check_path,pkg_origin_remote) +S3method(check_path,pkg_origin_repo) S3method(count,default) S3method(count,issues) S3method(count,potential_issues) -S3method(format,list_of_task_spec) -S3method(format,task_spec) +S3method(format,lib_path) +S3method(format,local_check_meta_task) +S3method(format,pkg_origin) +S3method(format,pkg_origin_base) +S3method(format,pkg_origin_local) +S3method(format,pkg_origin_remote) +S3method(format,pkg_origin_source) +S3method(format,reporter_cell) +S3method(format,reporter_line) +S3method(format,task) S3method(format_status_line_ansi,check_process) S3method(format_status_line_ansi,default) -S3method(get_package_spec_dependencies,default) -S3method(get_package_spec_dependencies,package_spec) -S3method(get_package_spec_dependencies,package_spec_archive_source) -S3method(get_package_spec_dependencies,package_spec_source) -S3method(install_parameters,package_spec) -S3method(install_parameters,package_spec_archive_source) -S3method(install_parameters,package_spec_source) -S3method(print,check_design) +S3method(format_task_name,check_task) +S3method(format_task_name,default) +S3method(format_task_name,install_task) +S3method(format_task_name,rev_dep_check_meta_task) +S3method(format_task_name,rev_dep_dep_meta_task) +S3method(format_task_name,task) +S3method(format_task_type,local_check_meta_task) +S3method(format_task_type,rev_dep_check_meta_task) +S3method(format_task_type,rev_dep_dep_meta_task) +S3method(format_task_type,task) +S3method(get_remote_tasks,default) +S3method(get_remote_tasks,pkg_origin_local) +S3method(get_remote_tasks,pkg_origin_remote) +S3method(get_remote_tasks,task) +S3method(install_params,pkg_origin) +S3method(install_params,pkg_origin_archive) +S3method(install_params,pkg_origin_base) +S3method(install_params,pkg_origin_local) +S3method(install_params,pkg_origin_remote) +S3method(install_params,pkg_origin_repo) +S3method(install_params,pkg_origin_unknown) +S3method(is_type,default) +S3method(is_type,list) +S3method(is_type,process) +S3method(is_type,task) +S3method(lib,"NULL") +S3method(lib,character) +S3method(lib,check_task) +S3method(lib,install_task) +S3method(lib,lib_path_default) +S3method(lib,lib_path_isolated) +S3method(lib,task) +S3method(lib_path,default) +S3method(lib_path,pkg_origin_local) +S3method(lib_path,pkg_origin_remote) +S3method(lib_path,pkg_origin_repo) +S3method(package,"NULL") +S3method(package,default) +S3method(package,pkg_origin) +S3method(package,task) +S3method(package_install_type,pkg_origin) +S3method(package_install_type,pkg_origin_local) +S3method(pkg_deps,default) +S3method(pkg_deps,pkg_origin) +S3method(pkg_deps,pkg_origin_archive) +S3method(pkg_deps,pkg_origin_local) +S3method(pkg_deps,pkg_origin_remote) +S3method(plot,task_graph) S3method(print,checked_results) -S3method(print,checked_results_check_task_spec) -S3method(print,checked_results_revdep_check_task_spec) +S3method(print,checker) S3method(print,issues) +S3method(print,local_check_results) S3method(print,potential_issues) -S3method(print,rcmdcheck_diff) -S3method(print,task_spec) +S3method(print,rcmdcheck_check_results) +S3method(print,rcmdcheck_results) +S3method(print,rcmdcheck_rev_dep_results) +S3method(print,rev_dep_dep_results) +S3method(print,task) +S3method(remotes_graph,check_task) +S3method(remotes_graph,igraph.vs) +S3method(remotes_graph,install_task) +S3method(remotes_graph,integer) +S3method(remotes_graph,task) +S3method(remotes_graph,task_graph) S3method(report_finalize,"NULL") S3method(report_finalize,reporter_ansi_tty) S3method(report_finalize,reporter_basic_tty) -S3method(report_initialize,"NULL") -S3method(report_initialize,reporter_ansi_tty) -S3method(report_initialize,reporter_basic_tty) +S3method(report_sleep,"NULL") S3method(report_sleep,default) S3method(report_sleep,reporter_ansi_tty) +S3method(report_sleep,reporter_basic_tty) +S3method(report_start_checks,"NULL") +S3method(report_start_checks,reporter_ansi_tty) +S3method(report_start_setup,"NULL") +S3method(report_start_setup,reporter_ansi_tty) +S3method(report_start_setup,reporter_basic_tty) S3method(report_status,"NULL") S3method(report_status,reporter_ansi_tty) S3method(report_status,reporter_basic_tty) -S3method(results,check_design) -S3method(results,check_task_spec) -S3method(results,list_check_task_spec) -S3method(results,list_revdep_check_task_spec) -S3method(results,revdep_check_task_spec) +S3method(report_step,"NULL") +S3method(report_step,reporter_ansi_tty) +S3method(report_step,reporter_basic_tty) +S3method(report_task,reporter_ansi_tty) +S3method(report_task_ansi_tty,check_task) +S3method(report_task_ansi_tty,default) +S3method(report_task_ansi_tty,rev_dep_check_meta_task) +S3method(results,checker) +S3method(results,igraph.vs) +S3method(results,integer) +S3method(results,local_check_meta_task) +S3method(results,rev_dep_check_meta_task) +S3method(results,rev_dep_dep_meta_task) S3method(run,character) -S3method(run,check_design) -S3method(start_task,check_task_spec) -S3method(start_task,custom_install_task_spec) -S3method(start_task,install_task_spec) -S3method(summary,check_design) -S3method(summary,checked_results) -S3method(summary,checked_results_check_task_spec) -S3method(summary,checked_results_revdep_check_task_spec) -export(check_design) -export(check_dev_rev_deps) -export(check_dir) +S3method(run,checker) +S3method(start_task,check_task) +S3method(start_task,igraph.vs) +S3method(start_task,install_task) +S3method(task_graph,task) +S3method(task_graph,task_graph) export(check_pkgs) export(check_rev_deps) -export(check_task_spec) -export(custom_install_task_spec) -export(install_task_spec) -export(new_check_design) -export(new_rev_dep_check_design) -export(package_spec) -export(package_spec_archive_source) -export(package_spec_source) +export(check_task) +export(checker) +export(install_task) +export(meta_task) +export(new_checker) +export(new_rev_dep_checker) +export(pkg_origin) +export(pkg_origin_archive) +export(pkg_origin_base) +export(pkg_origin_is_base) +export(pkg_origin_local) +export(pkg_origin_remote) +export(pkg_origin_repo) +export(pkg_origin_unknown) +export(plan_local_checks) +export(plan_rev_dep_checks) export(reporter_ansi_tty) +export(reporter_ansi_tty2) export(reporter_basic_tty) export(reporter_default) export(results) -export(results_to_file) -export(rev_dep_check_tasks_df) -export(revdep_check_task_spec) export(run) -export(source_check_tasks_df) -export(task_spec) +export(task) import(cli) import(options) importFrom(R6,R6Class) @@ -81,7 +156,6 @@ importFrom(callr,r_process) importFrom(cli,make_spinner) importFrom(igraph,"E<-") importFrom(igraph,"V<-") -importFrom(igraph,.env) importFrom(igraph,E) importFrom(igraph,V) importFrom(igraph,incident_edges) @@ -91,6 +165,8 @@ importFrom(igraph,subgraph.edges) importFrom(igraph,tail_of) importFrom(igraph,topo_sort) importFrom(igraph,vertex_attr) +importFrom(memoise,memoise) importFrom(rcmdcheck,rcmdcheck_process) +importFrom(rlang,hash) importFrom(utils,install.packages) importFrom(utils,packageName) diff --git a/NEWS.md b/NEWS.md index 923bfe6..51f85be 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,35 +1,8 @@ -# checked (0.2.9) +# checked 0.5.0 -* Add `igraph_subgraph_from_edges` wrapper to used work around deprecation - messages and alternate between `igraph::subgraph.edges` and - `igraph::subgraph_from_edges` depending on the `igraph` version. - -* Address CRAN results issues - -* In `install_packages_process` use `withCallingHandlers` instead - of the `tryCatch` to prevent accidental interruptions if `install.packages()` - triggers a benign warning. - -# checked 0.2.8 - -* Unify notes, warnings and errors are internally stored when generating - results (R4.5 compatibility) - -# checked 0.2.7 - -* Fix a bug where wrong lib.loc was used to derive whether a package has already - been satisfied. - -# checked 0.2.6 +* Entire package refactor -* Save minimal version required by each edge and use them to properly identify - whether dependecy is satisfied when calling `task_graph_update_done` - -# checked 0.2.5 - -* Refine reverse suggested dependecy strategy. - -# checked 0.2.4 +# checked 0.2.3 * Fix check processes hanging forever in some system configurations. @@ -46,16 +19,12 @@ * Add tests for the reverse dependency check use case. -* Make `install_packages_process` capture session's `available_packages_filters` +* Make `install_process` capture session's `available_packages_filters` and reuse them when installing packages to ensure consistency with the main session filtering. * Prettify output by stripping excessive new lines. -* `checked` now depends on `options` - -* Expose `...` allowing customization of check subprocesses when creating checks df. - * Force garbage collection before scheduling task, to make sure any already finished processes are removed from the memory. @@ -110,4 +79,4 @@ # checked 0.1.0 -* Package released to CRAN +* Package released to CRAN \ No newline at end of file diff --git a/R/check.R b/R/check.R index 90e5e66..a5a19ab 100644 --- a/R/check.R +++ b/R/check.R @@ -19,10 +19,13 @@ #' to pull sources for reverse dependencies. In some cases, for instance using #' binaries on Linux, we want to use different repositories when pulling #' sources to check and different when installing dependencies. -#' @param ... Additional arguments passed to [`checked-task-df`] and [`run()`] +#' @param restore `logical` indicating whether output directory should be +#' unlinked before running checks. If `FALSE`, an attempt will me made to +#' restore previous progress from the same `output` +#' @param ... Additional arguments passed to [`run()`] #' #' @return -#' [`check_design()`] R6 class storing all the details +#' [`checker()`] R6 class storing all the details #' regarding checks that run. Can be combined with #' [`results`] and [`summary()`] methods to generate results. #' @@ -45,7 +48,6 @@ NULL #' identify changes in reverse dependency behaviors. #' #' @inheritParams check_functions -#' @inheritParams options_params #' @inheritParams run #' #' @inherit check_functions return @@ -53,70 +55,17 @@ NULL #' @family checks #' @export check_rev_deps <- function( - path, - n = 2L, - output = tempfile(paste(utils::packageName(), Sys.Date(), sep = "-")), - lib.loc = .libPaths(), # nolint object_name_linter - repos = getOption("repos"), - reverse_repos = repos, - restore = options::opt("restore"), - reporter = reporter_default(), - ...) { - - checks <- rev_dep_check_tasks_df( - path = path, - repos = reverse_repos, - lib.loc = lib.loc, - ... - ) - - plan <- check_design$new( - checks, - n = n, - output = output, - lib.loc = lib.loc, - repos = repos, - ) - - run(plan, reporter = reporter, ...) - plan -} - -#' Run reverse dependency checks against a development version only -#' -#' [`check_dev_rev_deps()`] works similarly to [`check_rev_deps()`] but it runs -#' R CMD check only once for each package, with the development version of the -#' package installed. It is advantageous to check whether adding a new package -#' into a repository breaks existing packages that possibly take said package -#' as a `Suggests` dependency. -#' -#' @inheritParams check_functions -#' @inheritParams options_params -#' @inheritParams run -#' -#' @inherit check_functions return -#' -#' @family checks -#' @export -check_dev_rev_deps <- function( - path, - n = 2L, - output = tempfile(paste(utils::packageName(), Sys.Date(), sep = "-")), - lib.loc = .libPaths(), # nolint object_name_linter - repos = getOption("repos"), - restore = options::opt("restore"), - reporter = reporter_default(), - ...) { - - checks <- rev_dep_check_tasks_df( - path = path, - repos = repos, - versions = "dev", - ... - ) - - plan <- check_design$new( - checks, + path, + n = 2L, + output = tempfile(paste(utils::packageName(), Sys.Date(), sep = "-")), + lib.loc = .libPaths(), + repos = getOption("repos"), + reverse_repos = repos, + restore = TRUE, + ... +) { + checks <- checker$new( + plan_rev_dep_checks(path = path, repos = reverse_repos), n = n, output = output, lib.loc = lib.loc, @@ -124,38 +73,35 @@ check_dev_rev_deps <- function( restore = restore ) - run(plan, reporter = reporter, ...) - plan + run(checks, ...) + checks } -#' Check one or more package source directories +#' Check packages #' -#' [`check_pkgs()`] Installs all dependencies and runs `R CMD check`s -#' in parallel for all source packages whose source code is found in the -#' `path` directory +#' Runs classical `R CMD check` for the given source package. It +#' first identifies and installs, in parallel, all dependencies required +#' to check the package. Then, it runs `R CMD check` for each specified package. #' #' @inheritParams check_functions -#' @inheritParams options_params #' @inheritParams run +#' @inheritParams plan_local_checks #' #' @inherit check_functions return #' #' @family checks #' @export check_pkgs <- function( - path, - n = 2L, - output = tempfile(paste(utils::packageName(), Sys.Date(), sep = "-")), - lib.loc = .libPaths(), # nolint object_name_linter - repos = getOption("repos"), - restore = options::opt("restore"), - reporter = reporter_default(), - ...) { - - checks <- source_check_tasks_df(path, ...) - - plan <- check_design$new( - checks, + package, + n = 2L, + output = tempfile(paste(utils::packageName(), Sys.Date(), sep = "-")), + lib.loc = .libPaths(), + repos = getOption("repos"), + restore = TRUE, + ... +) { + checks <- checker$new( + plan_local_checks(package = package, repos = repos), n = n, output = output, lib.loc = lib.loc, @@ -163,43 +109,6 @@ check_pkgs <- function( restore = restore ) - run(plan, reporter = reporter, ...) - plan -} - -#' Check all package source directories in current directory -#' -#' [`check_dir()`] Identifies all R packages in the given directory -#' (non-recursively) and passes them to the [`check_pkgs()`] -#' -#' @inheritParams check_functions -#' @inheritParams options_params -#' @inheritParams run -#' -#' @inherit check_functions return -#' -#' @family checks -#' @export -check_dir <- function( - path, - n = 2L, - output = tempfile(paste(utils::packageName(), Sys.Date(), sep = "-")), - lib.loc = .libPaths(), # nolint object_name_linter - repos = getOption("repos"), - restore = options::opt("restore"), - reporter = reporter_default(), - ...) { - dirs <- list.dirs(path, full.names = TRUE, recursive = FALSE) - r_packages <- dirs[vlapply(dirs, path_is_pkg)] - - check_pkgs( - r_packages, - n = n, - output = output, - lib.loc = lib.loc, - repos = repos, - restore = restore, - reporter = reporter, - ... - ) + run(checks, ...) + checks } diff --git a/R/check_process.R b/R/check_process.R index 1eee23b..916306b 100644 --- a/R/check_process.R +++ b/R/check_process.R @@ -1,17 +1,35 @@ # Regular Expression for Parsing R CMD check checks # nolint start, styler: off RE_CHECK <- paste0( - "(?<=^|\n)", # starts on a new line (or start of string) - "\\* checking ", # literal "* checking " - "(?.*?)", # capture any check content as "check" - " \\.\\.\\.", # until literal "..." - "(?:", # ignore additional check details: - "[\\s\\*]{2,}", # any content starting with two or more of spaces or "*" - ".*?(?:\n|$)", # until a newline (or end of string) - ")*", # repeating until - "(?.*?)", # capturing a status as "status" - "(?=\n|$)" # terminated by a new line (or end of string) + "(?<=^|\\n)", # must start at beginning of string OR right after a newline + "\\* checking ", # literal "* checking " + "(?.*?)", # capture check name/content (non-greedy) as "check" + " \\.\\.\\.", # followed by literal " ..." + "(?:", # zero or more "detail" lines that belong to this check + "\\n[ \\t]*(?=\\n)", # a blank line (newline + optional spaces/tabs + next newline) + "|", + "\\n", # or: a normal detail line starting on the next line + "(?:[ \\t]{2,}", # either indented (2+ spaces/tabs) + "|\\*(?! (?:DONE|checking )))", # or a '*' line that is NOT "* DONE" and NOT "* checking ..." + "[^\\n]*(?:\\n|$)", # consume the rest of that detail line (to newline/end) + ")*", + "[ \\t]*", # allow extra spaces/tabs after "..." on the SAME line + "(?:", # position the engine right before a status token if one exists + # Case 1: status token is on the current line (possibly preceded by comment text) + "(?:[^\\n]*[ \\t]+)?(?=(?:[A-Z]{2}[A-Z0-9_-]*)\\s*(?:\\n|$))", + "|", + # Case 2: status token is on the next line: + # consume remainder of current line + newline + optional indent, + # but only if the next thing is a status token at end-of-line + "[^\\n]*\\n[ \\t]*(?=(?:[A-Z]{2}[A-Z0-9_-]*)\\s*(?:\\n|$))", + "|", + # Case 3: no status token (eat remainder/comment and stop here) + "[^\\n]*", + ")", + "(?(?:[A-Z]{2}[A-Z0-9_-]*)|)", # capture status token, or capture empty string if absent + "(?=\\s*(?:\\n|$))" # must end at newline/end (allow trailing whitespace) ) + # nolint end, styler: on #' @importFrom R6 R6Class @@ -23,6 +41,10 @@ check_process <- R6::R6Class( checks = function() { self$poll_output() private$parsed_checks + }, + results = function() { + private$cache_parsed_results() + private$parsed_results } ), public = list( @@ -34,7 +56,9 @@ check_process <- R6::R6Class( private$throttle <- throttle() private$spinners <- list( check = silent_spinner("circleHalves"), - starting = silent_spinner(list(frames = c("\u2834", "\u2826", "\u2816", "\u2832"))) + starting = silent_spinner(list( + frames = c("\u2834", "\u2826", "\u2816", "\u2832") + )) ) super$initialize(...) @@ -44,9 +68,19 @@ check_process <- R6::R6Class( if (!self$is_alive()) callback(self) }, finish = function() { - self$poll_output() - self$save_results() - if (is.function(f <- private$finish_callback)) f(self) + # self$checks active binding calls poll_output so there is not need + # to call it explicitly + checks <- self$checks + # In some cases, check subprocess might suffer from a race condition, when + # process itself finished, but the final results of the last subcheck + # are not yet available to parse. Therefore we allow the process to + # finalize only if the last subcheck has reported status. + if (checks[length(checks)] != "") { + self$save_results() + private$cache_parsed_results() + private$free_file_descriptors() + if (is.function(f <- private$finish_callback)) f(self) + } }, get_time_last_check_start = function() { private$time_last_check_start @@ -122,17 +156,43 @@ check_process <- R6::R6Class( save_results = function() { path <- file.path(private$check_dir, "result.json") try(rcmdcheck_to_json(self$parse_results(), path), silent = TRUE) + }, + safe_parse_results = function() { + r <- try(self$parse_results(), silent = TRUE) + if (!inherits(r, "try-error")) { + r + } else { + NULL + } } ), private = list( args = list(), time_last_check_start = NULL, time_finish = NULL, - parsed_checks = factor(levels = c("", "NONE", "OK", "NOTE", "WARNING", "ERROR")), + parsed_checks = factor(levels = c( + "", + "NONE", + "OK", + "NOTE", + "WARNING", + "ERROR" + )), + parsed_results = NULL, parsed_partial_check_output = "", throttle = NULL, spinners = NULL, - finish_callback = NULL + finish_callback = NULL, + cache_parsed_results = function() { + r <- self$safe_parse_results() + private$parsed_results <- r %||% private$parsed_results + }, + free_file_descriptors = function() { + if (self$has_output_connection()) close(self$get_output_connection()) + if (self$has_error_connection()) close(self$get_error_connection()) + if (self$has_poll_connection()) close(self$get_poll_connection()) + if (self$has_input_connection()) close(self$get_input_connection()) + } ) ) diff --git a/R/check_design.R b/R/checker.R similarity index 54% rename from R/check_design.R rename to R/checker.R index 9932d58..16adfb5 100644 --- a/R/check_design.R +++ b/R/checker.R @@ -2,21 +2,21 @@ #' #' Instantiate a check design from a path or directory. #' -#' @param x A file path, passed to [`rev_dep_check_tasks_df()`] -#' @param ... Additional arguments passed to [`new_check_design()`] +#' @param x A file path, passed to [`plan_rev_dep_checks()`] +#' @param ... Additional arguments passed to [`new_checker()`] #' #' @family checks #' @export -new_check_design <- function(...) { - check_design$new(...) +new_checker <- function(...) { + checker$new(...) } #' @family checks -#' @name new_check_design +#' @name new_checker #' @export -new_rev_dep_check_design <- function(x, ...) { - tasks <- rev_dep_check_tasks_df(x) - new_check_design(tasks, ...) +new_rev_dep_checker <- function(x, ...) { + plan <- plan_rev_dep_checks(x) + new_checker(plan, ...) } #' `R6` Checks Coordinator @@ -28,31 +28,36 @@ new_rev_dep_check_design <- function(x, ...) { #' @examples #' \dontrun{ #' library(checked) -#' df <- source_check_tasks_df(c( +#' plan <- plan_checks(c( #' system.file("example_packages", "exampleBad", package = "checked"), #' system.file("example_packages", "exampleGood", package = "checked") #' )) #' -#' plan <- check_design$new(df, n = 10, repos = "https://cran.r-project.org/") -#' while (!plan$is_done()) { -#' plan$start_next_task() +#' orchestrator <- checker$new( +#' plan, +#' n = 10, +#' repos = "https://cran.r-project.org/" +#' ) +#' +#' while (!orchestrator$is_done()) { +#' orchestrator$start_next_task() #' } #' } #' #' @family checks #' @export -check_design <- R6::R6Class( # nolint cyclocomp_linter - "check_design", +checker <- R6::R6Class( + "checker", public = list( #' @field graph (`igraph::igraph()`)\cr #' A dependency graph, storing information about which dependencies #' are required prior to execution of each check task. - #' Created with [`task_graph_create()`] + #' Created with [`task_graph()`] graph = NULL, - #' @field input (`data.frame()`)\cr + #' @field plan (`data.frame()`)\cr #' Checks task `data.frame` which is the source of all the checks. - input = NULL, + plan = NULL, #' @field output (`character(1)`)\cr #' Output directory where raw results and temporary library will @@ -65,7 +70,7 @@ check_design <- R6::R6Class( # nolint cyclocomp_linter #' Use checks data.frame to generate task graph in which all dependencies #' and installation order are embedded. #' - #' @param df `check_design` data.frame. + #' @param plan `plan` `data.frame`. #' @param n `integer` value indicating maximum number of subprocesses that #' can be simultaneously spawned when executing tasks. #' @param output `character` value specifying path where the output should @@ -79,38 +84,34 @@ check_design <- R6::R6Class( # nolint cyclocomp_linter #' restore previous progress from the same `output`. #' @param ... Additional arguments unused #' - #' @return [check_design]. + #' @return [checker]. initialize = function( - df, + plan, n = 2L, - output = tempfile(paste(packageName(), Sys.Date(), sep = "-")), - lib.loc = .libPaths(), # nolint object_name_linter + output = file.path( + tempdir(), + paste(packageName(), Sys.Date(), sep = "-") + ), + lib.loc = .libPaths(), repos = getOption("repos"), restore = options::opt("restore"), ... ) { - # Make sure all aliases are unique - stopifnot( - "Check task aliases has to be unique" = - !any(duplicated(df$alias)), - "Check task aliases cannot have the same name as any of the available packages" = # nolint - !any(df$alias %in% available.packages(repos = repos)[, "Package"]), - "Custom package aliases cannot be duplicates of check aliases" = - !any(uulist(drlapply(df$custom, `[[`, "alias")) %in% df$alias) - ) - check_past_output(output, restore, ask = interactive()) - + dir_create(output) - - self$input <- df + + self$plan <- plan self$output <- output private$n <- n - private$lib.loc <- lib.loc + private$lib.loc <- c( + # Append checker designated library + path_checker_lib(output), + lib.loc + ) private$repos <- repos - g <- task_graph_create(df, repos) - self$graph <- task_graph_update_done(g, c(path_lib(output), lib.loc)) + self$graph <- task_graph(self$plan, repos) private$restore_complete_checks() }, @@ -150,10 +151,10 @@ check_design <- R6::R6Class( # nolint cyclocomp_linter #' @return A integer value, coercible to logical to indicate whether a new #' process was spawned, or `-1` if all tasks have finished. start_next_task = function() { - # finish any finished processes for (process in private$active) { if (!process$is_alive()) { process$finish() + private$gc_needed <- TRUE } else if (inherits(process, "check_process")) { # NOTE: check process never finishes unless we poll checks process$poll_output() @@ -163,9 +164,11 @@ check_design <- R6::R6Class( # nolint cyclocomp_linter if (self$is_done()) { return(-1L) } - - # force garbage collection to free memory from terminated processes - gc(verbose = FALSE, reset = FALSE, full = TRUE) + + if (options::opt("proactive_gc") && private$gc_needed) { + gc(verbose = FALSE, reset = FALSE, full = TRUE) + private$gc_needed <- FALSE + } # if all available processes are in use, terminate early n_active <- length(private$active) @@ -173,94 +176,161 @@ check_design <- R6::R6Class( # nolint cyclocomp_linter return(0L) } - next_task <- task_graph_which_ready(self$graph) - - # If there are not new tasks cache, update the graph looking for new tasks - if (length(next_task) == 0) { - self$graph <- upadate_next_tasks(self$graph) - next_task <- task_graph_which_ready(self$graph) - } + next_node <- private$get_next_node() - if (length(next_task) > 0) { - next_task <- head(next_task, 1L) + if (length(next_node) != 0) { process <- start_task( - task = next_task, + node = next_node, g = self$graph, output = self$output, lib.loc = private$lib.loc ) - success <- private$push_process(next_task, process) + if (is.null(process)) { + private$finish_node(next_node) + return(1L) + } + + success <- private$push_process(next_node, process) return(as.integer(success)) } + if (length(private$active) == 0 && !self$is_done()) { + stop("some tasks not executed due to unsolved dependency requirements") + } + finished <- (length(private$active) == 0) && self$is_done() - return(-finished) + + -finished }, + #' @description #' Check if checks are done #' #' Checks whether all the scheduled tasks were successfully executed. is_done = function() { - checks <- igraph::V(self$graph)[igraph::V(self$graph)$type == "check"] - all(checks$status == STATUS$done) + is_check_node <- is_check(V(self$graph)$task) + checks <- V(self$graph)[is_check_node] + if (length(checks) > 0) { + all(checks$status == STATUS$done) + } else { + # If there is not a single check tasks, we need to make sure all + # the task finished + all(V(self$graph)$status == STATUS$done) + } + } ), private = list( - # Values - # maximum child process count n = 2L, + # lib.loc of allowed packages, lib.loc = NULL, + # repositories to fetch dependencies from repos = getOption("repos"), + # active processes active = list(), + # failed tasks failed = list(), - # Methods - push_process = function(task, x) { - task_graph_task_process(self$graph, task) <- x - name <- task_graph_task_name(self$graph, task) - task_graph_package_status(self$graph, task) <- STATUS$`in progress` + # task loop counter + gc_needed = FALSE, + + start_node = function(node) { + task_graph_package_status(self$graph, node) <- STATUS$`in progress` + private$start_node_meta_parents(node) + }, + + start_node_meta_parents = function(node) { + # start parent meta tasks recursively once a child starts + parent_nodes <- igraph::adjacent_vertices(self$graph, node, mode = "in") + parent_nodes <- unlist(parent_nodes, use.names = FALSE) + parent_nodes <- V(self$graph)[parent_nodes] + meta_parent_nodes <- parent_nodes[is_meta(parent_nodes$task)] + for (meta in meta_parent_nodes) { + task_graph_package_status(self$graph, meta) <- STATUS$`in progress` + private$start_node_meta_parents(meta) + } + }, + + finish_node = function(task) { + task_graph_package_status(self$graph, task) <- STATUS$`done` + private$finish_node_meta_parents(task) + }, + + finish_node_meta_parents = function(node) { + # parent meta tasks finish once _all_ children are finished + parent_nodes <- igraph::adjacent_vertices(self$graph, node, mode = "in") + parent_nodes <- unlist(parent_nodes, use.names = FALSE) + parent_nodes <- V(self$graph)[parent_nodes] + meta_parent_nodes <- parent_nodes[is_meta(parent_nodes$task)] + for (meta in meta_parent_nodes) { + siblings_by_meta_parent <- + igraph::adjacent_vertices(self$graph, meta, "out") + for (siblings in siblings_by_meta_parent) { + if (!all(siblings$status == STATUS$`done`)) next + task_graph_package_status(self$graph, meta) <- STATUS$`done` + } + } + }, + + push_process = function(node, x) { + task_graph_task_process(self$graph, node) <- x + name <- task_graph_task_name(self$graph, node) + private$start_node(node) x$set_finisher(function(process) { if (process$get_r_exit_status() != 0) { - private$failed[[name]] <- task + private$failed[[name]] <- node } private$pop_process(name) - task_graph_package_status(self$graph, task) <- STATUS$done + private$finish_node(node) }) private$active[[name]] <- x TRUE }, + restore_complete_checks = function() { - checks <- self$input$alias - check_done <- vlapply(checks, function(check) { + checks <- V(self$graph)[is_check(V(self$graph)$task)] + check_done <- vlapply(checks$name, function(check) { file.exists(file.path( path_check_output(self$output, check), "result.json" )) }) - self$graph <- task_graph_set_package_status( - self$graph, - checks[check_done], - STATUS$done - ) + + for (task in checks[check_done]) { + private$finish_node(task) + } }, + pop_process = function(name) { private$active[[name]] <- NULL + }, + # With these approach we deviate slightly from always prioritizing check + # tasks across the entire graph in favor of prioritizing them on the given + # layer. We do that due to significant performance gain in graph searching. + get_next_node = function(force = FALSE) { + ready_nodes <- task_graph_which_ready(self$graph) + if (length(ready_nodes) == 0 || force) { + self$graph <- task_graph_update_check_ready(self$graph) + self$graph <- task_graph_update_install_ready(self$graph) + ready_nodes <- task_graph_which_ready(self$graph) + } + utils::head(ready_nodes, 1L) } ) ) #' @export -print.check_design <- function(x, ...) { +print.checker <- function(x, ...) { if (x$is_done()) { print(results(x, ...), ...) } else { - print(x$input, ...) + print(x$plan, ...) } invisible(x) } @@ -278,7 +348,7 @@ check_past_output <- function(output, restore, ask = interactive()) { FALSE } } - + if (!restore) { unlink(output, recursive = TRUE, force = TRUE) } diff --git a/R/checks_df.R b/R/checks_df.R deleted file mode 100644 index 62b033f..0000000 --- a/R/checks_df.R +++ /dev/null @@ -1,285 +0,0 @@ -empty_checks_df <- data.frame( - alias = character(0), - version = character(0), - package = character(0), - custom = character(0) -) - -#' Check schedule data frame -#' -#' Create data.frame which each row defines a package for which R CMD check -#' should be run. Such data.frame is a prerequisite for generating -#' [`check_design()`] which orchestrates all the processes -#' including dependencies installation. -#' -#' @details -#' -#' `_tasks_df()` functions generate check task `data.frame` for -#' all source packages specified by the `path`. Therefore it accepts it to be -#' a vector of an arbitrary length. -#' -#' @param path path to the package source. Can be either a single source -#' code directory or a directory containing multiple package source code -#' directories. -#' @param ... parameters passed to the task specs allowing to customize -#' subprocesses. -#' -#' @return The check schedule `data.frame` with the following columns: -#' -#' * `alias`: The alias of the check to run. It also serves the purpose of -#' providing a unique identifier and node name in the task graph. -#' * `version`: Version of the package to be checked. -#' * `package`: Object that inherits from [`check_task_spec()`]. -#' Defines how package to be checked can be acquired. -#' * `custom`: Object that inherits from [`custom_install_task_spec()`]. -#' Defines custom package, for instance only available from local source, that -#' should be installed before checking the package. -#' -#' @family tasks -#' @name checked-task-df -NULL - -#' Build Tasks for Reverse Dependency Checks -#" -#' Generates checks schedule data.frame appropriate for running reverse -#' dependency check for certain source package. In such case `path` parameter -#' should point to the source of the development version of the package and -#' `repos` should be a repository for which reverse dependencies should be -#' identified. -#' -#' @inherit checked-task-df -#' @inheritParams checked-task-df -#' @param repos repository used to identify reverse dependencies. -#' @param versions character vector indicating against which versions of the -#' package reverse dependency should be checked. `c("dev", "release")` -#' (default) stands for the classical reverse dependency check. `"dev"` -#' checks only against development version of the package which is applicable -#' mostly when checking whether adding new package would break tests of -#' packages already in the repository and take the package as suggests -#' dependency. -#' @param lib.loc vector of libraries used to check whether reverse dependency -#' check can return accurate results. -#' -#' @family tasks -#' @export -rev_dep_check_tasks_df <- function( - path, - repos = getOption("repos"), - versions = c("dev", "release"), - lib.loc = .libPaths(), - ... -) { - stopifnot( - "rev_dep_check_tasks_df requires path argument of length 1" = - length(path) == 1 - ) - - versions <- match.arg(versions, c("dev", "release"), several.ok = TRUE) - ap <- utils::available.packages(repos = repos) - path <- check_path_is_pkg_source(path) - package <- get_package_name(path) - revdeps <- tools::package_dependencies( - package, - which = "all", - reverse = TRUE, - db = ap - )[[1]] - - if (length(revdeps) == 0) { - return(empty_checks_df) - } - - version <- ap[revdeps, "Version"] - df_dev <- df_rel <- data.frame( - alias = revdeps, - version = version - ) - - reverse_suggests <- if (!package %in% ap[, "Package"] && "release" %in% versions) { - if (is_package_installed(package, lib.loc)) { - stop( - sprintf( - "Package `%s` not found in repositories `%s` and identified in lib.loc. - checked cannot provide accurate reverse dependency check results. - Remove the package from lib.loc and try again.", - package, - paste0(repos, collapse = ", ") - ) - ) - - } - TRUE - } else { - FALSE - } - - task_specs_function <- if (all(c("dev", "release") %in% versions)) { - rev_dep_check_tasks_specs - } else { - check_tasks_specs - } - - if ("dev" %in% versions) { - df_dev$alias <- paste0(df_dev$alias, " (dev)") - df_dev$package <- task_specs_function(revdeps, repos, df_dev$alias, "new", ...) - df_dev$custom <- rep(list(custom_install_task_spec( - alias = paste0(package, " (dev)"), - package_spec = package_spec_source(name = package, path = path), - type = "source" - )), times = NROW(df_dev)) - } - - if ("release" %in% versions) { - package_v <- if (reverse_suggests) "missing" else ap[package, "Version"] - df_rel$alias <- paste0(df_rel$alias, " (v", package_v, ")") - df_rel$package <- task_specs_function(revdeps, repos, df_rel$alias, "old", ...) - df_rel$custom <- if (reverse_suggests) { - rep(list(custom_install_task_spec()), times = NROW(df_dev)) - } else { - rep(list(custom_install_task_spec( - alias = paste0(package, " (release)"), - package_spec = package_spec(name = package, repos = repos), - # make sure to use the release version built against the same system - type = "source" - )), times = NROW(df_dev)) - } - } - - if (identical(versions, "dev")) { - df <- df_dev - } else if (identical(versions, "release")) { - df <- df_rel - } else { - idx <- rep(seq_len(nrow(df_rel)), each = 2) + c(0, nrow(df_rel)) - df <- rbind(df_dev, df_rel)[idx, ] - } - - df$package <- list_of_task_spec(df$package) - df$custom <- list_of_task_spec(df$custom) - df -} - -rev_dep_check_tasks_specs <- function(packages, repos, aliases, revdep, ...) { - list_of_task_spec(mapply( - function( - p, - a, - env = options::opt("check_envvars"), - args = options::opt("check_args"), - build_args = options::opt("check_build_args") - ) { - revdep_check_task_spec( - alias = a, - package_spec = package_spec(name = p, repos = repos), - env = env, - args = args, - build_args = build_args, - revdep = revdep - ) - }, - packages, - aliases, - SIMPLIFY = FALSE, - USE.NAMES = FALSE, - MoreArgs = list(...) - )) -} - -check_tasks_specs <- function( - packages, - repos, - aliases, - revdep, - ... -) { - list_of_task_spec(mapply( - function( - p, - a, - env = options::opt("check_envvars"), - args = options::opt("check_args"), - build_args = options::opt("check_build_args") - ) { - check_task_spec( - alias = a, - package_spec = package_spec(name = p, repos = repos), - env = env, - args = args, - build_args = build_args - ) - }, - packages, - aliases, - SIMPLIFY = FALSE, - USE.NAMES = FALSE, - MoreArgs = list(...) - )) -} - -#' Create a Task to Check a Package from Source -#' -#' @inherit checked-task-df -#' @inheritParams checked-task-df -#' -#' @family tasks -#' @export -source_check_tasks_df <- function(path, ...) { - name <- names(path) - path <- vcapply(path, check_path_is_pkg_source, USE.NAMES = FALSE) - package <- vcapply(path, get_package_name) - alias <- if (is.null(name)) { - unlist(lapply(unique(package), function(p) { - idx <- package == p - suffixes <- if (sum(idx) > 1) { - paste0("_", seq(sum(idx))) - } else { - "" - } - paste0(p, " (source", suffixes, ")") - })) - } else { - name - } - version <- vcapply(path, get_package_version) - - df <- data.frame( - alias = alias, - version = version - ) - - df$package <- list_of_task_spec( - source_check_tasks_specs(package, path, alias, ...) - ) - df$custom <- list_of_task_spec( - rep(list(custom_install_task_spec()), times = NROW(df)) - ) - - df -} - -source_check_tasks_specs <- function(packages, path, aliases, ...) { - list_of_task_spec(mapply( - function( - p, - path, - a, - env = options::opt("check_envvars"), - args = options::opt("check_args"), - build_args = options::opt("check_build_args")) { - - check_task_spec( - alias = a, - package_spec = package_spec_source(name = p, path = path, repos = NULL), - env = env, - args = args, - build_args = build_args - ) - }, - packages, - path, - aliases, - SIMPLIFY = FALSE, - USE.NAMES = FALSE, - MoreArgs = list(...) - )) -} diff --git a/R/cli.R b/R/cli.R index f50c559..ef8ee2e 100644 --- a/R/cli.R +++ b/R/cli.R @@ -1,3 +1,7 @@ +# We need to define it as variable instead of direct default parameter +# to comply with CRAN LaTeX requirements +DEFAULT_ROW_SYMBOL <- list(bar = "\u2502") + #' Internal Utilities for Command-line Output #' #' Various helper functions for consistent cli output, including theming and @@ -16,14 +20,19 @@ NULL #' @name cli cli_table_row <- function( - status, - ok = "OK", - notes = "N", - warnings = "W", - errors = "E", - msg = "", - title = FALSE) { + status = "", + ok = "OK", + notes = "N", + warnings = "W", + errors = "E", + msg = "", + style = c("row", "title", "header"), + symbols = DEFAULT_ROW_SYMBOL +) { + style <- match.arg(style) + cli_theme() + status <- trimws(as.character(status)) status <- switch(status, "1" = , @@ -37,7 +46,11 @@ cli_table_row <- function( "WARNING" = cli::format_inline("{.warn ?}"), "6" = , "ERROR" = cli::format_inline("{.err \u2a2f}"), - if (title) cli::col_none(cli::style_bold(status)) else status + switch(style, + "title" = cli::col_none(cli::style_bold(status)), + "header" = " ", + status + ) ) ok <- str_pad(ok, n = 2) @@ -45,19 +58,23 @@ cli_table_row <- function( warnings <- str_pad(warnings, n = 2) errors <- str_pad(errors, n = 2) - if (title) { - ok <- cli::col_none(cli::style_bold(ok)) - notes <- cli::col_none(cli::style_bold(notes)) - warnings <- cli::col_none(cli::style_bold(warnings)) - errors <- cli::col_none(cli::style_bold(errors)) - } else { - ok <- cli::format_inline("{.ok {ok}}") - notes <- cli::format_inline("{.note {notes}}") - warnings <- cli::format_inline("{.warn {warnings}}") - errors <- cli::format_inline("{.err {errors}}") - } + switch( + style, + "title" = { + ok <- cli::col_none(cli::style_bold(ok)) + notes <- cli::col_none(cli::style_bold(notes)) + warnings <- cli::col_none(cli::style_bold(warnings)) + errors <- cli::col_none(cli::style_bold(errors)) + }, + "row" = { + ok <- cli::format_inline("{.ok {ok}}") + notes <- cli::format_inline("{.note {notes}}") + warnings <- cli::format_inline("{.warn {warnings}}") + errors <- cli::format_inline("{.err {errors}}") + } + ) - fmt <- "\u2502 {status} \u2502 {ok} {notes} {warnings} {errors} \u2502 {msg}" + fmt <- "{symbols$bar} {status} {symbols$bar} {ok} {notes} {warnings} {errors} {symbols$bar} {msg}" # nolint cli::format_inline(fmt) } @@ -76,5 +93,7 @@ cli_theme <- function(..., .envir = parent.frame()) { cli_wrap_lines <- function(text, w = cli::console_width()) { n <- cli::ansi_nchar(text) - cli::ansi_substring(text, seq_len(ceiling(n / w)) * w - w + 1, seq_len(ceiling(n / w)) * w) + cli::ansi_substring( + text, seq_len(ceiling(n / w)) * w - w + 1, seq_len(ceiling(n / w)) * w + ) } diff --git a/R/install.R b/R/install.R index adabe58..e971b00 100644 --- a/R/install.R +++ b/R/install.R @@ -1,19 +1,20 @@ #' @importFrom utils packageName install.packages #' @importFrom R6 R6Class #' @importFrom callr r_process -install_packages_process <- R6::R6Class( - "install_package_process", +install_process <- R6::R6Class( + "install_process", inherit = callr::r_process, public = list( log = NULL, initialize = function( pkgs, ..., - lib = .libPaths(), + lib = .libPaths()[[1]], libpaths = .libPaths(), available_packages_filters = getOption("available_packages_filters"), - log + log = NULL ) { + if (!dir.exists(lib)) dir.create(lib, recursive = TRUE) private$package <- pkgs self$log <- log private$callr_r_bg( @@ -33,14 +34,14 @@ install_packages_process <- R6::R6Class( ) }, args = list( - pkgs, + private$package, ..., lib = lib, escalate_warning = is_install_failure_warning, available_packages_filters = available_packages_filters ), libpath = libpaths, - stdout = log, + stdout = self$log, stderr = "2>&1", system_profile = TRUE ) @@ -57,6 +58,7 @@ install_packages_process <- R6::R6Class( }, finish = function() { private$time_finish <- Sys.time() + private$free_file_descriptors() if (is.function(f <- private$finish_callback)) f(self) }, get_r_exit_status = function() { @@ -85,6 +87,12 @@ install_packages_process <- R6::R6Class( private$options <- options super$initialize(options = options) + }, + free_file_descriptors = function() { + if (self$has_output_connection()) close(self$get_output_connection()) + if (self$has_error_connection()) close(self$get_error_connection()) + if (self$has_poll_connection()) close(self$get_poll_connection()) + if (self$has_input_connection()) close(self$get_input_connection()) } ) ) diff --git a/R/lib.R b/R/lib.R new file mode 100644 index 0000000..5cdb860 --- /dev/null +++ b/R/lib.R @@ -0,0 +1,106 @@ +#' Make a Library Location +#' +#' A description of where packages should be installed. This object provides +#' necessary information to determine where a package should be installed. +#' lib_path method creates default path handlers for given pkg origin while +#' lib_path_x creates an actual object. +#' +#' @param x A [`pkg_origin()`] object used for default dispatch. +#' @param ... Additional values +#' @param .class An optional subclass, used primarily for dispatch. +#' +#' @family specs +lib_path <- function(x, ..., .class = c()) { + UseMethod("lib_path") +} + +#' @export +lib_path.default <- function(x, ..., .class = c()) { + lib_path_default(.class = .class) +} + +#' @export +lib_path.pkg_origin_repo <- function(x, ..., .class = c()) { + lib_path_default(.class = .class) +} + +#' @export +lib_path.pkg_origin_local <- function(x, ..., .class = c()) { + lib_path_isolated(.class = .class) +} + +#' @export +lib_path.pkg_origin_remote <- function(x, ..., .class = c()) { + x <- sanitize_pkg_origin_remote(x) + NextMethod() +} + +#' @rdname lib_path +lib_path_default <- function(.class = c()) { + structure(list(), class = c(.class, "lib_path_default")) +} + +#' @rdname lib_path +lib_path_isolated <- function(.class = c()) { + structure(list(), class = c(.class, "lib_path_isolated")) +} + +#' @export +format.lib_path <- function(x, ...) { + "library" +} + +#' Get Library Location +#' +#' @param x An object describing a library location +#' @param ... additional parameters passed to methods +#' @param lib.loc Library paths, defaulting to [`.libPaths()`]. +#' @param lib.root A root directory for the isolated library. +#' @param dir_hash unique identifier of the isolated library +#' @param name human-readable subname of the isolated library +#' +#' @keywords internal +lib <- function(x, ...) { + UseMethod("lib") +} + +#' @method lib NULL +#' @export +#' @rdname lib +lib.NULL <- function(x, ...) { + character(0L) +} + +#' @export +#' @rdname lib +lib.character <- function(x, ...) { + x +} + +#' @export +#' @rdname lib +lib.lib_path_isolated <- function( + x, + ..., + lib.root = tempdir(), + dir_hash = hash(Sys.time(), n = 8), + name = "" +) { + dirname <- if (nzchar(name)) { + paste0(name, "-", dir_hash) + } else { + dir_hash + } + file.path(lib.root, dirname) +} + +#' @export +#' @rdname lib +lib.lib_path_default <- function( + x, + ..., + lib.loc = .libPaths() +) { + # In most cases this assumes checker library was appended to the lib.loc + lib.loc[[1]] +} diff --git a/R/next_task.R b/R/next_task.R index 6ea2264..b832ba1 100644 --- a/R/next_task.R +++ b/R/next_task.R @@ -1,107 +1,123 @@ -upadate_next_tasks <- function(g) { - g <- task_graph_update_check_ready(g) - g <- task_graph_update_install_ready(g) - - g -} - -#' @importFrom igraph .env -task_get_lib_loc <- function(g, node, output) { - nhood <- task_graph_neighborhoods(g, node)[[1]] - name <- names(node) %||% node # nolint - nhood <- nhood[names(nhood) != .env$name] - - # Custom packages are possible only for the check type nodes which are - # always terminal. Therefore if we sort nhood making custom packages appear - # first, their lib will always be prioritized - attributes <- igraph::vertex.attributes(g, index = nhood) - - paths <- vcapply(nhood, function(v) { - task_get_install_lib(g, v, output) - }) - - unique(paths[order(attributes$custom, decreasing = TRUE)]) -} - -task_get_install_lib <- function(g, node, output) { - attributes <- igraph::vertex.attributes(g, index = node) - if (attributes$type == "check") { - path_check_output(output, attributes$spec[[1]]$alias) - } else if (attributes$custom) { - path_custom_lib(output, attributes$spec[[1]]$alias) +#' Libpaths from task graph +#' +#' Function that traverses over the task dependency task to acquire libpaths +#' for given nodes. It ensures that when runing a node, a libpath is +#' constructed which has all the required packages on it. +#' +#' @param g `task_graph` object +#' @param node Node(s) for which libpath should be constructed based on `g` +#' @param output Path to the checked output directory +#' @inheritParams lib +#' +#' @keywords internal +task_graph_libpaths <- function( + g, + node = NULL, + lib.loc = .libPaths(), + output = tempdir() +) { + # Maintain original vertices order to make sure libpaths are properly + # constructed + vs <- sort(if (is.null(node)) { + igraph::V(g) } else { - path_lib(output) - } + task_graph_neighborhoods(g, node)[[1]] + }, decreasing = TRUE) + + # iterate over tasks and derive a library location + task_lib <- lapply( + vs$task, + lib, + lib.loc = lib.loc, + lib.root = path_libs(output) + ) + unique(unlist(task_lib)) } -start_task <- function(task, g, ...) { - UseMethod("start_task", task_graph_task_spec(g, task)) +#' Start a new task +#' +#' Starts task based on the `task` object encapsulated in the `node` taken +#' from then `task_graph` `g`. It returns an `install_process` or +#' `check_process` `R6` object. +#' +#' @inheritParams task_graph_libpaths +#' @param ... additional params passed to downstream methods +#' +#' @keywords internal +start_task <- function(node, g, ...) { + UseMethod("start_task") } #' @export -start_task.install_task_spec <- function( - task, - g, - output, - lib.loc, # nolint object_name_linter - ...) { - spec <- task_graph_task_spec(g, task) - install_parameters <- install_parameters(spec$package_spec) - libpaths <- c(task_get_lib_loc(g, task, output), lib.loc) - install_packages_process$new( - install_parameters$package, - lib = path_lib(output), - libpaths = libpaths, - repos = install_parameters$repos, - dependencies = FALSE, - type = spec$type, - INSTALL_opts = spec$INSTALL_opts, - log = path_package_install_log(output, spec$alias), - env = spec$env - ) +#' @method start_task igraph.vs +start_task.igraph.vs <- function(node, g, ...) { + stopifnot(length(node) == 1L) + UseMethod("start_task", node$task[[1]]) } #' @export -start_task.custom_install_task_spec <- function( - task, - g, - output, - lib.loc, # nolint object_name_linter - ...) { - spec <- task_graph_task_spec(g, task) - install_parameters <- install_parameters(spec$package_spec) - libpaths <- c(task_get_lib_loc(g, task, output), lib.loc) - install_packages_process$new( +#' @method start_task install_task +start_task.install_task <- function( + node, + g, + output, + lib.loc, + ... +) { + task <- node$task[[1]] + libpaths <- unique(c( + task_graph_libpaths(g, node, lib.loc = lib.loc, output = output), + lib.loc + )) + install_parameters <- install_params(task$origin) + + if (any(inherits(task$origin, c("pkg_origin_base", "pkg_origin_unknown")))) { + return(NULL) + } + + # install_parameters$package is a valid package name only for + # pkg_origin_repo. Otherwise it's a path to the source package in which case + # is_package_installed returns FALSE (as it should) + if (is_package_installed(install_parameters$package, libpaths)) { + return(NULL) + } + + install_process$new( install_parameters$package, - lib = path_custom_lib(output, spec$alias), + lib = lib(task, lib.loc = lib.loc, lib.root = path_libs(output)), libpaths = libpaths, - repos = install_parameters$repos, + repos = task$origin$repos, dependencies = FALSE, - type = spec$type, - INSTALL_opts = spec$INSTALL_opts, - log = path_package_install_log(output, spec$alias), - env = spec$env + type = task$type, + INSTALL_opts = c(), # TODO + log = path_install_log(output, node$name[[1]]), + env = c() # TODO ) } #' @export -start_task.check_task_spec <- function( - task, - g, - output, - lib.loc, # nolint object_name_linter - ...) { - spec <- task_graph_task_spec(g, task) - libpaths <- c(task_get_lib_loc(g, task, output), lib.loc) - path <- check_path(spec$package_spec, output = path_sources()) +#' @method start_task check_task +start_task.check_task <- function( + node, + g, + output, + lib.loc, + ... +) { + task <- node$task[[1]] + libpaths <- unique(c( + task_graph_libpaths(g, node, lib.loc = lib.loc, output = output), + lib.loc + )) + path <- check_path(task$origin, output = path_sources()) check_process$new( path = path, - check_dir = path_check_output(output, spec$alias), + check_dir = path_check_output(output, node$name[[1]]), libpath = libpaths, - repos = spec$package_spec$repos, - args = spec$args, - build_args = spec$build_args, - env = spec$env + repos = task$repos, + args = task$args, + build_args = task$build_args, + env = task$env ) } diff --git a/R/options.R b/R/options.R index 1d41cf5..e5be46d 100644 --- a/R/options.R +++ b/R/options.R @@ -2,30 +2,49 @@ options::define_options( "tty refresh interval when reporting results in milliseconds", tty_tick_interval = 0.1, - + + "deafult tty height used for the ANSI reporter. Used only + if correct values could not be acquired with system('tput lines')", + tty_default_height = 50, + + "`logical`, indicating whether additional garbage collection should be + performed before starting a new task, if at least one process recently + finalized. This can cause the checker to orchestrate tasks slower but + is recommended to be used for designs with many sub-processes required as + native garbage collection can lag leading to memory issues. Disable only + when maximum prefromance is required and memory is not the issue.", + proactive_gc = TRUE, + "character vector indicating whether R error should be thrown when issues are discovered when generating results. \"never\" means that no errors are thrown. If \"issues\" then errors are emitted only on issues, whereas \"potential issues\" stands for error on both issues and potential issues.", results_error_on = "never", - + "character vector indicating which packages should be included in the results. \"all\" means that all packages are kept. If \"issues\" then only packages with issues identified, whereas \"potential_issues\" stands for keeping packages with both \"issues\" and \"potential_issues\".", results_keep = "all", - + "`logical` indicating whether output directory should be unlinked before running checks. If `FALSE`, an attempt will me made to restore previous progress from the same `output`", restore = NA, - - "named `character` vector of environment variables to use during R CMD check.", + + "`logical` indicating whether origins inheriting from `pkg_origin_local`, + should be scanned for packages in the `remotes` field and added while + constrocuting a plan `task_grap`", + add_remotes = TRUE, + + "named `character` vector of environment variables to use during + the R CMD check.", check_envvars = c( - "_R_CHECK_FORCE_SUGGESTS_" = FALSE, - "_R_CHECK_RD_XREFS_" = FALSE, - "_R_CHECK_SYSTEM_CLOCK_" = FALSE, - "_R_CHECK_SUGGESTS_ONLY_" = TRUE + "_R_CHECK_FORCE_SUGGESTS_" = "false", + "_R_CHECK_RD_XREFS_" = "false", + "_R_CHECK_SYSTEM_CLOCK_" = "false", + "_R_CHECK_SUGGESTS_ONLY_" = "true", + "_R_CHECK_CRAN_INCOMING_" = "false" ), "`character` vector of args passed to the R CMD build.", @@ -42,7 +61,8 @@ options::define_options( check_args = c( "--timings", "--ignore-vignettes", - "--no-manual" + "--no-manual", + "--as-cran" ), envvar_fn = structure( function(raw, ...) trimws(strsplit(raw, " ")[[1]]), @@ -51,7 +71,7 @@ options::define_options( ) #' @eval options::as_roxygen_docs() -#' +#' #' @family documentation NULL diff --git a/R/package.R b/R/package.R new file mode 100644 index 0000000..a1307de --- /dev/null +++ b/R/package.R @@ -0,0 +1,5 @@ +#' @importFrom igraph E V +#' @importFrom memoise memoise +#' @importFrom rlang hash +#' @keywords internal +"_PACKAGE" diff --git a/R/package_spec.R b/R/package_spec.R deleted file mode 100644 index 934bcec..0000000 --- a/R/package_spec.R +++ /dev/null @@ -1,121 +0,0 @@ -#' Package specification -#' -#' Create package specification list which consists of all the details required -#' to identify and acquire source of the package. -#' -#' @param name name of the package. -#' @param repos repository where package with given name should identified. -#' @param version package_version object specifying minimal version required -#' by packages depending on this package. -#' @param op operator used with \code{version}. -#' @param path path to the source of the package (either bundled or not). URLs -#' are acceptable. -#' @param ... parameters passed to downstream constructors -#' -#' @family specs -#' @export -package_spec <- function( - name = NULL, - repos = NULL, - version = numeric_version("0.0"), - op = ">") { - structure(list( - name = name, - repos = repos, - version = version, - op = op - ), class = "package_spec") -} - -#' @export -#' @rdname package_spec -package_spec_source <- function(path = NULL, ...) { - package_spec <- package_spec(...) - package_spec["path"] <- list(path) - class(package_spec) <- c("package_spec_source", class(package_spec)) - package_spec -} - -#' @export -#' @rdname package_spec -package_spec_archive_source <- function(path = NULL, ...) { - package_spec <- package_spec_source(path, ...) - - class(package_spec) <- c("package_spec_archive_source", class(package_spec)) - package_spec -} - -get_package_spec_dependencies <- function(package_spec) { - UseMethod("get_package_spec_dependencies") -} - -#' @export -get_package_spec_dependencies.default <- function(package_spec) { - NULL -} - -#' @export -get_package_spec_dependencies.package_spec <- function(package_spec) { - db <- utils::available.packages(repos = package_spec$repos) - row <- db[package_spec$name, , drop = FALSE] - row[, DB_COLNAMES, drop = FALSE] -} - -#' @export -get_package_spec_dependencies.package_spec_source <- function(package_spec) { - row <- as.data.frame(read.dcf(file.path(package_spec$path, "DESCRIPTION"))) - row <- row[, intersect(DB_COLNAMES, colnames(row)), drop = FALSE] - row[setdiff(DB_COLNAMES, colnames(row))] <- NA_character_ - row -} - -#' @export -get_package_spec_dependencies.package_spec_archive_source <- function(package_spec) { - path <- if (!file.exists(package_spec$path)) { - fetch_package_source(package_spec$path, path_sources()) - } else { - package_spec$path - } - utils::untar(path, exdir = dir) - package_spec$path <- file.path(path, package_spec$name) - get_package_spec_dependencies.package_spec_source(package_spec) -} - - -install_parameters <- function(package_spec) { - UseMethod("install_parameters") -} - -#' @export -install_parameters.package_spec <- function(package_spec) { - list(package = package_spec$name, repos = package_spec$repos) -} - -#' @export -install_parameters.package_spec_source <- function(package_spec) { - list(package = package_spec$path, repos = NULL) -} - -#' @export -install_parameters.package_spec_archive_source <- function(package_spec) { - list(package = package_spec$path, repos = NULL) -} - -check_path <- function(package_spec, ...) { - UseMethod("check_path") -} - -#' @export -check_path.package_spec <- function(package_spec, output, ...) { - get_package_source(package_spec$name, package_spec$repos, destdir = output) -} - -#' @export -check_path.package_spec_source <- function(package_spec, ...) { - package_spec$path -} - -#' @export -check_path.package_spec_archive_source <- function(package_spec, ...) { - package_spec$path -} diff --git a/R/pkg_origin.R b/R/pkg_origin.R new file mode 100644 index 0000000..40ebedd --- /dev/null +++ b/R/pkg_origin.R @@ -0,0 +1,331 @@ +#' Package specification +#' +#' Create package specification list which consists of all the details required +#' to identify and acquire source of the package. +#' +#' @param package name of the package. +#' @param repos repository where package with given name should identified. +#' @param path path to the source of the package (either bundled or not). URLs +#' are acceptable. +#' @param remote remote object from the `remotes` package used to identify +#' non-standard packages. +#' @param .class Additional subclasses. +#' @param ... parameters passed to downstream constructors. +#' +#' @family specs +#' @export +pkg_origin <- function(package, ..., .class = c()) { + structure(list(package = package, ...), class = c(.class, "pkg_origin")) +} + +#' @export +format.pkg_origin_source <- function(x, ...) { + if (is.null(x$source)) { + character(0L) + } else if (is.null(names(x$source))) { + format(x$source, ...) + } else { + names(x$source) + } +} + +#' @export +format.pkg_origin_local <- function(x, ...) { + simple_paths <- format_simplify_path(x$source) + names(x$source) <- simple_paths + x$source +} + +#' @export +format.pkg_origin_base <- function(x, ...) { + character(0L) +} + +#' @export +format.pkg_origin <- function(x, ...) { + format(x$source, ...) +} + +#' @export +format.pkg_origin_remote <- function(x, ...) { + format(class(x$remote)[[1]]) +} + +#' @export +#' @rdname pkg_origin +pkg_origin_repo <- function(package, repos, ...) { + ap_pkg <- available_packages(repos = repos)[package, ] + + version <- package_version(ap_pkg["Version"]) + source <- strip_src_contrib(ap_pkg["Repository"]) + if (any(which <- startsWith(repos, source))) { + source <- repos[which][1] + } + + pkg_origin( + package = package, + version = version, + source = source, + repos = repos, + ..., + .class = "pkg_origin_repo" + ) +} + + +try_pkg_origin_repo <- function(package, repos, ...) { + if (isTRUE(pkg_origin_is_base(package))) { + pkg_origin_base(package, ...) + } else if (package %in% available_packages(repos = repos)[, "Package"]) { + pkg_origin_repo(package = package, repos = repos, ...) + } else { + pkg_origin_unknown(package = package, ...) + } +} + + +#' @export +#' @rdname pkg_origin +pkg_origin_is_base <- function(package, ...) { + is_base <- package == "R" + is_inst <- package %in% utils::installed.packages()[, "Package"] + is_base[is_inst] <- utils::installed.packages()[package[is_inst], "Priority"] == "base" # nolint + is_base +} + + +#' @export +#' @rdname pkg_origin +pkg_origin_base <- function(package, ...) { + pkg_origin( + package = package, + .class = "pkg_origin_base" + ) +} + + +#' @export +#' @rdname pkg_origin +pkg_origin_unknown <- function(package, ...) { + pkg_origin( + package = package, + .class = "pkg_origin_unknown" + ) +} + +#' @export +#' @rdname pkg_origin +pkg_origin_local <- function(path = NULL, ...) { + package <- get_package_name(path) + version <- package_version(get_package_version(path)) + source <- filepath(normalizePath(path)) + + pkg_origin( + package = package, + version = version, + source = source, + ..., + .class = "pkg_origin_local" + ) +} + +#' @export +#' @rdname pkg_origin +pkg_origin_remote <- function(remote = NULL, ...) { + source <- get_remote_package_source(remote) + package <- get_package_name(source) + version <- package_version(get_package_version(source)) + + pkg_origin( + package = package, + version = version, + remote = remote, + source = source, + ..., + .class = c("pkg_origin_remote", "pkg_origin_local") + ) +} + +sanitize_pkg_origin_remote <- function(x) { + if (is.null(x$source) || !dir.exists(x$source)) { + x$source <- get_remote_package_source(x$remote) + } + x +} + +#' @export +#' @rdname pkg_origin +pkg_origin_archive <- function(path = NULL, ...) { + pkg_origin(..., path = path, .class = "pkg_origin_archive") +} + +pkg_deps <- function( + x, + repos = getOption("repos"), + dependencies = TRUE, + db = available_packages(repos = repos) +) { + UseMethod("pkg_deps") +} + +#' @export +pkg_deps.default <- function( + x, + repos = getOption("repos"), + dependencies = TRUE, + db = available_packages(repos = repos) +) { + NULL +} + +#' @export +pkg_deps.pkg_origin <- function( + x, + repos = getOption("repos"), + dependencies = TRUE, + db = available_packages(repos = repos) +) { + df <- pkg_dependencies(package(x), db = db, dependencies = dependencies) + # Packages here come from CRAN only hence we can assume that all entries + # with the name the same as the x are direct dependencies + df$depth <- ifelse(df$package == package(x), "direct", "indirect") + df +} + +#' @export +pkg_deps.pkg_origin_local <- function( + x, + repos = getOption("repos"), + dependencies = TRUE, + db = available_packages(repos = repos) +) { + # We need to temporarily switch the object to data.frame, as subsetting + # assignment for matrix does not have drop parameter and always simplifies + # one row matrices to vectors. + row <- read.dcf(file.path(x$source, "DESCRIPTION")) + + rownames(row) <- package(x) + direct_deps <- pkg_dependencies( + packages = package(x), + dependencies = dependencies, + db = row + ) + direct_deps$depth <- rep.int("direct", NROW(direct_deps)) + + indirect_deps <- pkg_dependencies( + packages = direct_deps$name, + dependencies = dependencies, + db = db + ) + indirect_deps$depth <- rep.int("indirect", NROW(indirect_deps)) + + rbind(direct_deps, indirect_deps) +} + +#' @export +pkg_deps.pkg_origin_remote <- function( + x, + repos = getOption("repos"), + dependencies = TRUE, + db = available_packages(repos = repos) +) { + x <- sanitize_pkg_origin_remote(x) + NextMethod() +} + +#' @export +pkg_deps.pkg_origin_archive <- function( + x, + repos = getOption("repos"), + dependencies = TRUE, + db = available_packages(repos = repos) +) { + # TODO: Implement it by fetching tarball, untarring it and dispatching + # TODO: to origin_local + pkg_deps.pkg_origin_local(x = x, repos = repos, dependencies = dependencies) +} + + +install_params <- function(x, ...) { + UseMethod("install_params") +} + +#' @export +install_params.pkg_origin <- function(x, output, ...) { + stop(sprintf("Can't determine origin of package '%s'", x$name)) +} + +#' @export +install_params.pkg_origin_unknown <- function(x, output, ...) { + list() # no source identified, installation shall be skipped +} + +#' @export +install_params.pkg_origin_base <- function(x, ...) { + list() # no installation needed, distributed with R +} + +#' @export +install_params.pkg_origin_repo <- function(x, ...) { + list(package = x$package, repos = x$repos) +} + +#' @export +install_params.pkg_origin_local <- function(x, ...) { + list(package = x$source, repos = NULL) +} + +#' @export +install_params.pkg_origin_remote <- function(x, ...) { + x <- sanitize_pkg_origin_remote(x) + NextMethod() +} + +#' @export +install_params.pkg_origin_archive <- function(x, ...) { + list(package = x$path, repos = NULL) +} + +package_install_type <- function(x, ...) { + UseMethod("package_install_type") +} + +#' @export +package_install_type.pkg_origin <- function(x, output, ...) { + getOption("pkgType") +} + +#' @export +package_install_type.pkg_origin_local <- function(x, ...) { + "source" +} + +check_path <- function(x, ...) { + UseMethod("check_path") +} + +#' @export +check_path.pkg_origin <- function(x, output, ...) { + stop(sprintf("Can't determine origin of package '%s'", x$name)) +} + +#' @export +check_path.pkg_origin_repo <- function(x, output, ...) { + get_package_source(x$package, x$repos, destdir = output) +} + +#' @export +check_path.pkg_origin_local <- function(x, ...) { + x$source +} + +#' @export +check_path.pkg_origin_remote <- function(x, ...) { + x <- sanitize_pkg_origin_remote(x) + NextMethod() +} + +#' @export +check_path.pkg_origin_archive <- function(x, ...) { + x$source +} diff --git a/R/plan.R b/R/plan.R new file mode 100644 index 0000000..eda9c32 --- /dev/null +++ b/R/plan.R @@ -0,0 +1,228 @@ +#' Plan Reverse Dependency Checks +#' +#' Generates a plan for running reverse dependency check for certain +#' source package. In such case `path` should be proivded with a directory +#' path to the development version of the package and `repos` should be a +#' repository for which reverse dependencies should be identified. +#' +#' @param path path to the package source. +#' @param repos repository used to identify reverse dependencies. +#' +#' @family plan +#' @export +plan_rev_dep_checks <- function( + path, + repos = getOption("repos") +) { + path <- check_path_is_pkg_source(path) + ap <- available_packages(repos = repos) + + package <- get_package_name(path) + revdeps <- tools::package_dependencies( + package, + which = "all", + reverse = TRUE, + db = ap + )[[1]] + + if (length(revdeps) == 0) { + return(task_graph_class(igraph::make_empty_graph())) + } + + # root meta task, indicating a reverse-dependency check plan + task <- sequence_graph(task = list(meta_task( + origin = pkg_origin_local(path), + .subclass = "rev_dep_dep" + ))) + + # build individual plans for development version reverse-dependency checks + rev_dep_dev_check_tasks <- lapply( + revdeps, + plan_rev_dep_dev_check, + origin = pkg_origin_local(path), + repos = repos + ) + + # build individual plans for release version reverse-dependency checks + rev_dep_release_check_tasks <- lapply( + revdeps[revdeps %in% ap[, "Package"]], + plan_rev_dep_release_check, + origin = pkg_origin_local(path), + repos = repos + ) + + # store vertex names so that we can build edges after merging + task_id <- V(task)[[1]]$name + rev_dep_meta_task_ids <- unique(c( + vcapply(rev_dep_dev_check_tasks, function(g) V(g)[[1]]$name), + vcapply(rev_dep_release_check_tasks, function(g) V(g)[[1]]$name) + )) + + # combine component plans into an overall plan + g <- graph_dedup_attrs(igraph::union( + task, + rev_dep_dev_check_tasks, + rev_dep_release_check_tasks + )) + + # reconstruct edges from planned root node to individual checks + edges <- as.vector(rbind(task_id, rev_dep_meta_task_ids)) + + # meta tasks take dependency relationship with subtasks + g <- igraph::add_edges(g, edges = edges, attr = list(type = DEP$Depends)) + E(g)$type <- DEP[E(g)$type] + + g <- task_graph_class(g) + + if (remotes_permitted()) { + remotes_graph(g) + } else { + g + } +} + +plan_rev_dep_dev_check <- function(origin, revdep, repos) { + rev_dep_origin <- pkg_origin_repo(package = revdep, repos = repos) + sequence_graph(task = list( + meta_task( + origin = origin, + revdep = revdep, + .subclass = "rev_dep_check" + ), + make_unique_task(seed = "dev", check_task( + origin = rev_dep_origin, + env = options::opt("check_envvars"), + args = options::opt("check_args"), + build_args = options::opt("check_build_args") + )), + install_task(origin = origin) + )) +} + +plan_rev_dep_release_check <- function(origin, revdep, repos) { + rev_dep_origin <- pkg_origin_repo(package = revdep, repos = repos) + repo_root_origin <- try_pkg_origin_repo( + package = package(origin), + repos = repos + ) + sequence_graph(task = list( + meta_task( + origin = origin, + revdep = revdep, + .subclass = "rev_dep_check" + ), + make_unique_task(seed = "release", check_task( + origin = rev_dep_origin, + env = options::opt("check_envvars"), + args = options::opt("check_args"), + build_args = options::opt("check_build_args") + )), + install_task( + origin = repo_root_origin, + lib = lib_path_isolated() + ) + )) +} + +#' Plan R CMD Checks +#' +#' Generates a plan for running R CMD check for a specified set of packages. +#' +#' @param package A path to either package, directory with packages or name +#' of the package (details) +#' @param repos repository used to identify packages when name is provided. +#' +#' @details +#' `package` parameter has two different allowed values: +#' * Package - checked looks for a DESCRIPTION file in the provided path, if +#' found treats it like a source package. +#' * If the specified value does not correspond to a source package, the +#' parameter is treated as the name and `repos` parameter is used to identify +#' the source. +#' +#' +#' @family plan +#' @export +plan_local_checks <- function( + package, + repos = getOption("repos") +) { + + task <- meta_task( + origin = NULL, + .subclass = "local_check" + ) + + # build individual plans for each package value + local_checks_tasks <- lapply( + package, + function(x, repos) { + if (path_is_pkg(x)) { + check_task( + origin = pkg_origin_local(x), + env = options::opt("check_envvars"), + args = options::opt("check_args"), + build_args = options::opt("check_build_args") + ) + } else { + check_task( + origin = pkg_origin_repo(package = x, repos = repos), + env = options::opt("check_envvars"), + args = options::opt("check_args"), + build_args = options::opt("check_build_args") + ) + } + }, + repos = repos + ) + + star_plan_template(c( + list(task), + local_checks_tasks + )) +} + + +#' Plan source package installation +#' +#' Generates a plan for running installing a package from source. +#' +#' @param package A path to package source. +#' @param repos repository used to identify packages when name is provided. +#' +#' @family plan +plan_local_install <- function( + package, + repos = getOption("repos") +) { + + m_task <- meta_task( + origin = NULL, + .subclass = "local_install" + ) + + i_task <- install_task( + origin = pkg_origin_local(package) + ) + + star_plan_template(list( + m_task, + i_task + )) +} + +star_plan_template <- function(tasks) { + g <- star_graph( + task = tasks + ) + + E(g)$type <- rep(DEP$Depends, times = length(E(g))) + + g <- task_graph_class(g) + + if (remotes_permitted()) { + remotes_graph(g) + } else { + g + } +} diff --git a/R/remotes.R b/R/remotes.R new file mode 100644 index 0000000..198dafc --- /dev/null +++ b/R/remotes.R @@ -0,0 +1,128 @@ +remotes_graph <- function(x, ...) { + UseMethod("remotes_graph") +} + +#' @export +remotes_graph.task_graph <- function(x, ...) { + vs <- V(x) + remotes_subgraphs <- lapply(vs, remotes_graph, vs = vs) + + task_graph_class( + suppressWarningsRegex( + graph_dedup_attrs( + igraph::union( + x, + do.call(igraph::union, remotes_subgraphs) + ) + ), + "Some, but not all graphs are named, not using vertex names", + fixed = TRUE + ) + ) +} + +#' @export +remotes_graph.integer <- function(x, ..., vs) { + remotes_graph(vs[[x]]) +} + +#' @export +#' @method remotes_graph igraph.vs +remotes_graph.igraph.vs <- function(x, ...) { + remotes_graph(x$task) +} + +#' @export +remotes_graph.task <- function(x, ...) { + # By default does not allow remotes for a task unless an explicit method + # has been defined for that type of task + igraph::make_empty_graph() +} + +#' @export +remotes_graph.install_task <- function(x, ...) { + remote_tasks <- get_remote_tasks(x) + if (length(remote_tasks) == 0) return(igraph::make_empty_graph()) + remote_tasks_names <- vcapply(remote_tasks, package) + + x_deps <- pkg_deps(x$origin) + x_remote_deps <- x_deps[ + x_deps$package == package(x) & x_deps$name %in% remote_tasks_names, + ] + + # Sort tasks according to same key + remote_tasks <- remote_tasks[order(remote_tasks_names)] + remote_tasks_types <- x_remote_deps[order(x_remote_deps$name), ]$type + + g <- star_graph( + task = c( + list(x), + remote_tasks + ), + edge_attrs = list(type = remote_tasks_types) + ) + + # Recursively get remote_tasks of remote_tasks + remote_subgraphs <- lapply(remote_tasks, remotes_graph) + suppressWarningsRegex( + graph_dedup_attrs( + igraph::union( + g, + do.call(igraph::union, remote_subgraphs) + ) + ), + "Some, but not all graphs are named, not using vertex names", + fixed = TRUE + ) +} + +#' @export +remotes_graph.check_task <- remotes_graph.install_task + +get_remote_tasks <- function(x) { + UseMethod("get_remote_tasks") +} + +#' @export +get_remote_tasks.default <- function(x) { + NULL +} + +#' @export +get_remote_tasks.task <- function(x) { + get_remote_tasks(x$origin) +} + +#' @export +get_remote_tasks.pkg_origin_local <- function(x) { + pkgs <- .remotes()$extra_deps(as.package.remotes(x$source), "remotes") + + lapply(seq_len(NROW(pkgs)), function(i) { + install_task( + pkg_origin_remote(remote = pkgs$remote[[i]]) + ) + }) +} + +#' @export +get_remote_tasks.pkg_origin_remote <- function(x) { + x <- sanitize_pkg_origin_remote(x) + NextMethod() +} + +get_remote_package_source <- function(remote) { + path <- file.path(path_remotes(), hash(remote)) + if (dir.exists(path)) return(path) + dir_create(path) + tmp <- remotes::remote_download(remote, quiet = TRUE) + file.copy( + from = list.files(tmp, full.names = TRUE, recursive = FALSE), + to = path, + recursive = TRUE + ) + path +} + +remotes_permitted <- function() { + options::opt("add_remotes") && requireNamespace("remotes", quietly = TRUE) +} diff --git a/R/reporter.R b/R/reporter.R index 545b5ae..ace72b6 100644 --- a/R/reporter.R +++ b/R/reporter.R @@ -1,7 +1,7 @@ -#' Check Design Runner Reporters +#' Check checker Runner Reporters #' #' Reporters are used to configure how output is communicated while running -#' a [`check_design`]. They range from glossy command-line tools intended for +#' a [`checker`]. They range from glossy command-line tools intended for #' displaying progress in an interactive R session, to line-feed logs which #' may be better suited for automated execution, such as in continuous #' itegration. @@ -36,6 +36,12 @@ reporter_ansi_tty <- function() { reporter("ansi_tty") } +#' @rdname reporters +#' @export +reporter_ansi_tty2 <- function() { + reporter("ansi_tty2") +} + #' @rdname reporters #' @export reporter_basic_tty <- function() { @@ -57,13 +63,13 @@ reporter_default <- function() { #' Reporter Internal Methods #' #' Each of the internal methods for reporters take a reporter, the check -#' design object and a calling environment. +#' checker object and a calling environment. #' #' @param reporter A object produced using [`reporters`]. Each reporter is a #' thin wrapper around an environment with a class name for dispatch. The #' reporter is mutable and captures any necessary state that needs to be #' tracked while reporting. -#' @param design [`check_design`] The check design to report as it evaluates. +#' @param checker [`checker`] The check checker to report as it evaluates. #' @param envir `environment` An environment to attach to, to leverage on-exit #' hooks. #' @param sleep `numeric` An interval to pause between reporter steps. @@ -74,27 +80,47 @@ reporter_default <- function() { NULL #' @rdname reporters-internal -report_sleep <- function(reporter, design, sleep) { +report_sleep <- function(reporter, checker, sleep) { UseMethod("report_sleep") } #' @rdname reporters-internal #' @export -report_sleep.default <- function(reporter, design, sleep = 1) { +report_sleep.default <- function(reporter, checker, sleep = 1) { Sys.sleep(sleep) } #' @rdname reporters-internal -report_initialize <- function(reporter, design, envir = parent.frame()) { - UseMethod("report_initialize") +report_start_setup <- function(reporter, checker, ..., envir = parent.frame()) { + UseMethod("report_start_setup") +} + +#' @rdname reporters-internal +report_start_checks <- function(reporter, checker, ..., envir = parent.frame()) { # nolint + UseMethod("report_start_checks") +} + +#' @rdname reporters-internal +report_start_checks.default <- function(reporter, checker, ..., envir = parent.frame()) { # nolint + NULL } #' @rdname reporters-internal -report_status <- function(reporter, design, envir = parent.frame()) { +report_status <- function(reporter, checker, envir = parent.frame()) { UseMethod("report_status") } #' @rdname reporters-internal -report_finalize <- function(reporter, design) { +report_finalize <- function(reporter, checker) { UseMethod("report_finalize") } + +#' @rdname reporters-internal +report_task <- function(reporter, g, v) { + UseMethod("report_task") +} + +#' @rdname reporters-internal +report_step <- function(reporter, checker) { + UseMethod("report_step") +} diff --git a/R/reporter_ansi_tty.R b/R/reporter_ansi_tty.R index f3a7ac1..b67db82 100644 --- a/R/reporter_ansi_tty.R +++ b/R/reporter_ansi_tty.R @@ -1,3 +1,11 @@ +#' @export +#' @method report_task reporter_ansi_tty +report_task.reporter_ansi_tty <- function(reporter, g, v) { + UseMethod("report_task_ansi_tty", v$task) +} + +report_task_ansi_tty <- report_task.reporter_ansi_tty + format_status_line_ansi <- function(process, ...) { UseMethod("format_status_line_ansi") } @@ -54,35 +62,207 @@ format_status_line_ansi.check_process <- function( format_status_line_ansi.default <- function( process, ..., + status, width = getOption("width", 80L)) { + + if (status == STATUS$done) { + msg <- "restored from system file." + status <- "NONE" + } else { + msg <- "starting ..." + status <- silent_spinner(list( + frames = c("\u2834", "\u2826", "\u2816", "\u2832") + ))$spin() + } + out <- cli_table_row( - status = "NONE", + status = status, ok = 0, notes = 0, warnings = 0, errors = 0, - "restored from system file." + msg ) cli::ansi_substring(out, 1, width) } +reporter_line <- function(label, status, style = NA_character_) { + structure( + list(label = label, status = status, style = style), + class = "reporter_line" + ) +} + +#' @export +format.reporter_line <- function(x, width = cli::console_width(), ...) { + rule <- switch( + x$style, + "h1" = "\u2550", + " " + ) + label <- format(x$label, pad = rule) + + status <- switch( + x$style, + "h1" = gsub(" ", rule, cli_table_row( + ok = "", + notes = "", + warnings = "", + errors = "", + style = "header", + symbols = list(bar = "\u256A") + )), + x$status + ) + + out <- paste0(label, status, strrep(rule, width)) + out <- cli::ansi_substr(out, 1, width) + + out +} + +reporter_cell <- function( + content, + justify = "left", + padding = c(0, 0), + width = Inf +) { + structure( + content, + justify = justify, + padding = padding, + width = width, + class = "reporter_cell" + ) +} + +#' @export +format.reporter_cell <- function( + x, + padding = attr(x, "padding") %||% c(0, 0), + justify = attr(x, "justify") %||% "right", + width = attr(x, "width") %||% cli::console_width(), + pad = " ", + ... +) { + n <- width - sum(padding) - cli::ansi_nchar(x) + paste0( + if (!is.null(justify) && justify == "right") strrep(pad, n), + if (!is.null(justify) && justify == "center") strrep(pad, (n - n %/% 2)), + strrep(pad, padding[[1]]), + x, + strrep(pad, padding[[2]]), + if (!is.null(justify) && justify == "center") strrep(pad, n %/% 2), + if (!is.null(justify) && justify == "left") strrep(pad, n) + ) +} + +#' @export +report_task_ansi_tty.default <- function(reporter, g, v) { + NULL +} + +#' @export +report_task_ansi_tty.rev_dep_check_meta_task <- function(reporter, g, v) { + # package being rev-dep-check'ed + package <- v$task$origin$package + + # get individual rev dep checks, and get their trees + check_nodes <- igraph::neighbors(g, v$name, "out") + + # NOTE: do we only want to check _direct_ reverse dependencies? + check_neighborhoods <- igraph::make_neighborhood_graph( + g, + order = 1, + check_nodes, + mode = "out" + ) + + # TODO: would be nice to reintroduce `package` as a vertex attribute, maybe + # remove from `pkg_origin`? + package_tasks <- lapply( + check_neighborhoods, + function(g) { + is_dep <- vlapply(V(g)$task, function(i) i$origin$package == package) + V(g)[is_dep][[1]]$task + } + ) + + package_task_labels <- lapply( + package_tasks, + function(t) { + reporter_cell( + fmt(task = t, "+{package} {version} {source.type}"), + width = reporter$label_nchar + ) + } + ) + + rev_dep <- paste0(" ", v$task$revdep, " ") + to_report <- mapply( + function(label, i) { + x <- report_task_ansi_tty(reporter = reporter, g = g, v = V(g)[[i]]) + x$label <- label + x + }, + package_task_labels, + check_nodes, + SIMPLIFY = FALSE + ) + + to_report <- append( + to_report, + after = 0L, + list(reporter_line( + style = "h1", + label = reporter_cell( + rev_dep, + width = reporter$label_nchar, + padding = c(2, 2) + ), + status = NA_character_ + )) + ) + + to_report +} + +#' @export +report_task_ansi_tty.check_task <- function(reporter, g, v) { + package <- fmt(task = v$task, "{package}") + reporter_line( + label = reporter_cell( + package, + justify = "right", + width = reporter$label_nchar + ), + status = format_status_line_ansi(v$process, status = v$status) + ) +} + #' @export report_sleep.reporter_ansi_tty <- function( - reporter, - design, - sleep = options::opt("tty_tick_interval")) { + reporter, + checker, + sleep = options::opt("tty_tick_interval") +) { Sys.sleep(sleep) } #' @export -report_initialize.reporter_ansi_tty <- function( - reporter, - design, - envir = parent.frame()) { - # named factor vector, names as task aliases and value of last reported status - reporter$header <- TRUE - reporter$status <- STATUS$done[c()] +report_start_setup.reporter_ansi_tty <- function( + reporter, + checker, + ..., + envir = parent.frame() +) { + if (cli_env_has_pb(reporter$cli)) return() + reporter$cli <- new.env(parent = reporter) + + reporter$tty_height <- ansi_tty_height() + # Buffer is shorter than actual tty by one line due to progress bar existance + reporter$buffer_height <- reporter$tty_height - 1 # hide cursor when initializer enters, ensure its restored even if interrupted cli::ansi_hide_cursor() @@ -94,124 +274,257 @@ report_initialize.reporter_ansi_tty <- function( cli::cli_progress_bar( type = "custom", - extra = list(message = ""), - format = "ETA {cli::pb_eta} ({cli::pb_current}/{cli::pb_total}) [{cli::pb_elapsed}] {cli::pb_extra$message}", # nolint + format = paste( + "ETA {cli::pb_eta}", + "({cli::pb_current}/{cli::pb_total})", + "[{cli::pb_elapsed}]", + "{cli::pb_extra$message}" + ), format_done = "Finished in {cli::pb_elapsed}", - total = sum(igraph::V(design$graph)$type == "check"), + ..., clear = FALSE, - auto_terminate = TRUE, - .envir = reporter, + auto_terminate = FALSE, + .auto_close = FALSE, + .envir = reporter$cli ) + + cli::cli_progress_update(force = TRUE, .envir = reporter$cli) +} + +reporter_ansi_tty_get_label_nchar <- function( + reporter, + checker, + envir = parent.frame() +) { + # pre-calculate the maximum space needed for label column + v <- igraph::V(checker$graph) + v_report <- v[is_meta(v$task)] + + # for each reporter, produce output + output <- unlist(recursive = FALSE, lapply( + v_report, + function(v) report_task(reporter, checker$graph, V(checker$graph)[[v]]) + )) + + labels <- unlist(lapply(output, function(x) format(x$label, justify = NULL))) + max(cli::ansi_nchar(labels)) +} + +#' @export +report_start_checks.reporter_ansi_tty <- function( + reporter, + checker, + ..., + envir = parent.frame() +) { + # store our current console width, used to trigger complete re-draw + reporter$width <- cli::console_width() + + # derive the longest label, used for spacing table + reporter$label_nchar <- + reporter_ansi_tty_get_label_nchar(reporter, checker, envir) + + # helper function for adding new buffer entries + reporter$buffer_proto <- function(node = "") { + df <- data.frame( + # tracking columns + node = node, # reporter node name + new = TRUE, # whether node is newly added (requires newlines) + updated = NA, # whether output needs to be updated + final = FALSE # whether element is no longer a subject to change + ) + + # output columns + df$line <- list(NULL) + df + } + + # initialize our buffer of output + reporter$buffer <- reporter$buffer_proto()[c(), ] + + reporter$buffer_update <- function(node, lines, final = FALSE) { + if (!node %in% reporter$buffer$node) { + proto_df <- reporter$buffer_proto(node)[rep_len(1L, length(lines)), ] + reporter$buffer <- rbind(reporter$buffer, proto_df) + } + + is_node <- which(reporter$buffer$node == node) + reporter$buffer$updated[is_node] <- TRUE + reporter$buffer$final[is_node] <- final + reporter$buffer$line[is_node] <- lines + } + + # helper to update buffer for a specific node + reporter$buffer_report <- function(node) { + node <- V(checker$graph)[node][[1]] + to_report <- report_task(reporter, checker$graph, node) + output <- vcapply(to_report, format) + final <- node$status == STATUS$done + reporter$buffer_update(node, output, final = final) + } + + extra <- list(message = "") + if (cli_env_has_pb(reporter$cli)) { + cli::cli_progress_update(extra = extra, .envir = reporter$cli) + } else { + report_start_setup( + reporter, + checker, + extra = extra, + total = sum(is_check(igraph::V(checker$graph)$task)), + envir = envir + ) + } } #' @importFrom igraph V #' @export -report_status.reporter_ansi_tty <- function(reporter, design, envir) { # nolint - v <- igraph::V(design$graph) - v_checks <- v[v$type == "check"] - n_char_titles <- max(nchar(v_checks$name)) - failed_tasks <- design$failed_tasks() - failed_packages <- failed_tasks[vlapply(failed_tasks, function(x) x$type == "install")] +report_status.reporter_ansi_tty <- function(reporter, checker, envir) { + msg <- c() + v <- igraph::V(checker$graph) # add newly started task status - new_idx <- which(v_checks$status > STATUS$ready) - new_idx <- new_idx[!v_checks$name[new_idx] %in% names(reporter$status)] - if (length(new_idx) > 0) { - # print header if this is the first status line of the reporter - if (reporter$header) { - cat( - ansi_line_erase(), - strrep(" ", n_char_titles + 2), - cli_table_row("S", "OK", "N", "W", "E", title = TRUE), - "\n", - sep = "" - ) - reporter$header <- FALSE + is_running <- v$status > STATUS$ready & v$status < STATUS$done + + is_done <- v$status >= STATUS$done + is_non_final_in_buffer <- vlapply(v, function(n) { + if (n %in% reporter$buffer$node) { + reporter$buffer$final[which(reporter$buffer$node == n)[[1]]] + } else { + FALSE } + }) + is_newly_done <- is_done & !is_non_final_in_buffer + updated <- v[is_running | is_newly_done] - # always start by reporting in progress, even if finished before reporting - new <- rep_len(STATUS$`in progress`, length(new_idx)) - names(new) <- v_checks$name[new_idx] - reporter$status <- c(reporter$status, new) - cat(strrep("\n", length(new_idx))) + # skip if no updates + if (length(updated) <= 0L) { + return() } - if (length(reporter$failed_packaged) != length(failed_packages)) { - # Add failed packages warning to the buffer - failures_buffer <- rev(unlist(lapply(failed_packages, function(x) { - list( - cli_wrap_lines(cli::cli_fmt(cli::cli_alert_danger( - sprintf("%s package installation had non-zero exit status", x$name) - ))), - cli_wrap_lines(as.character(cli::style_dim( - sprintf("log: %s", x$process[[1]]$log) - ))) - ) - }))) - - reporter$failures_buffer <- vcapply(seq_along(failures_buffer), function(i) { - paste0( - ansi_move_line_rel(i), - ansi_line_erase(), - failures_buffer[i], - ansi_move_line_rel(-i), - sep = "" - ) - }) + # print header if this is the first status line of the reporter + if (nrow(reporter$buffer) == 0L) { + reporter$buffer_update( + node = "HEADER", + list(reporter_line( + label = reporter_cell("", width = reporter$label_nchar), + status = cli_table_row("S", "OK", "N", "W", "E", style = "title") + )), + final = TRUE + ) + } - # For performance store these value in the environment to redraw warnings - # buffer only when necessary - reporter$failed_packaged <- failed_packages + # report check tasks in cli output + to_report_main <- is_meta(updated$task) + for (node_name in updated[to_report_main]$name) { + reporter$buffer_report(node_name) } - # for each not-yet finished task, report status - buffer <- "" - for (idx in seq_along(reporter$status)) { - # update reported status - alias <- names(reporter$status)[[idx]] - v_idx <- which(v_checks$name == alias) - reporter$status[[idx]] <- v_checks$status[[v_idx]] + # report non-check tasks in status line + to_report_bar <- is_install(updated$task) & + updated$status > STATUS$ready & + updated$status < STATUS$done + + if (any(to_report_bar)) msg[length(msg) + 1L] <- "installing" + for (node_name in updated[to_report_bar]$name) { + msg[length(msg) + 1L] <- fmt("{package}", task = updated[[node_name]]$task) + } + + # if console width has changed, redraw all + if (identical(reporter$width, cli::console_width)) { + reporter$buffer$updated <- TRUE + } + + # Get number of lines that are final and didn't update since the last pass + finalized_lines <- + cumprod(!reporter$buffer$updated & reporter$buffer$final) == 1 + # Finalized lines as well as lines that are in progress but would exceed + # the maximum height of the tty should be skipped from reporting until + # the number of finished tasks allows to fit new ones. + reportable_lines <- !finalized_lines + # Which max returns the position of first TRUE, then we allow folloiwng + # reporter$buffer_height lines to be reported at the time + if (sum(reportable_lines) > reporter$buffer_height) { + reportable_lines[seq(which.max(reportable_lines) + reporter$buffer_height, + length(reportable_lines))] <- FALSE + } + # introduce newlines for newly added reporter rows + buffer <- strrep( + "\n", sum(reporter$buffer$new & reportable_lines) + ) + reporter$buffer$new[reportable_lines] <- FALSE + + # for each not-yet finished task, report status + for (idx in which(reporter$buffer$updated & reportable_lines)) { # derive reporter information - n_lines <- length(reporter$status) - idx + 1L - width <- cli::console_width() - n_char_titles - 2L - task_name <- v_checks$name[[v_idx]] - process <- task_graph_task_process(design$graph, v_checks[[v_idx]]) + n_lines <- sum(reportable_lines) - idx + 1L + sum(finalized_lines) - # report status line buffer <- paste0( buffer, - ansi_move_line_rel(n_lines + length(reporter$failures_buffer)), + ansi_move_line_rel(n_lines), ansi_line_erase(), - " ", strrep(" ", n_char_titles - nchar(task_name)), task_name, " ", - format_status_line_ansi(process, width = width), - ansi_move_line_rel(-(n_lines + length(reporter$failures_buffer))), + format(reporter$buffer$line[[idx]]), + ansi_move_line_rel(-n_lines), sep = "" ) - } - cat(paste0(buffer, reporter$failures_buffer)) - - is_inst <- vlapply(design$active_processes(), inherits, "install_package_process") # nolint - inst_pkgs <- names(design$active_processes()[is_inst]) - if (length(inst_pkgs)) { - inst_msg <- paste0("installing ", paste0(inst_pkgs, collapse = ", ")) - } else { - inst_msg <- "" + reporter$buffer$updated[idx] <- FALSE } - n_finished <- sum(v$status[v$type == "check"] >= STATUS$done) + reporter$width <- cli::console_width() + cat(buffer) + + n_finished <- sum(v[is_check(v$task)]$status >= STATUS$done) cli::cli_progress_update( set = n_finished, - extra = list( - message = inst_msg - ), - .envir = reporter + extra = list(message = paste(msg, collapse = " ")), + .envir = reporter$cli ) } #' @export -report_finalize.reporter_ansi_tty <- function(reporter, design) { # nolint - report_status(reporter, design) # report completions of final processes +report_finalize.reporter_ansi_tty <- function(reporter, checker) { + # Init buffer + report_status(reporter, checker) + # Iterate until buffer is fully processed from final results + while (!all(!reporter$buffer$updated & reporter$buffer$final)) { + report_status(reporter, checker) + } + cli::cli_progress_done(.envir = reporter$cli) + cat(ansi_line_erase()) # clear lingering progress bar output + + # Display failures + failed_tasks <- checker$failed_tasks() + failed_packages <- + failed_tasks[vlapply(failed_tasks, function(x) is_install(x$task))] + + failures_output <- vcapply(failed_packages, function(x) { + msg <- paste0( + cli_wrap_lines(cli::cli_fmt(cli::cli_alert_danger( + sprintf( + "%s package installation had non-zero exit status", + package(x$task[[1]]) + ) + ))), + collapse = "\n" + ) + log <- paste0( + cli_wrap_lines(cli::cli_fmt(cli::cli_alert_danger( + sprintf("log: %s", x$process[[1]]$log) + ))), + collapse = "\n" + ) + paste0( + msg, "\n", log + ) + }) + + cat(failures_output, "\n") cli::ansi_show_cursor() } + +#' @export +report_step.reporter_ansi_tty <- function(reporter, checker) { + checker$start_next_task() >= 0 +} diff --git a/R/reporter_basic_tty.R b/R/reporter_basic_tty.R index c3284da..946d56e 100644 --- a/R/reporter_basic_tty.R +++ b/R/reporter_basic_tty.R @@ -1,41 +1,46 @@ #' @export -report_initialize.reporter_basic_tty <- function( - # nolint - reporter, - design, - envir = parent.frame()) { - # start with initialized-as-completed tasks - v <- igraph::V(design$graph) - which_done <- v$status == STATUS$done - done <- v[which_done]$status - names(done) <- v$name[which_done] +report_start_setup.reporter_basic_tty <- function( + reporter, + checker, + ..., + envir = parent.frame() +) { + # start with all tasks initialized as pending + v <- igraph::V(checker$graph) + v_actionable <- v[is_actionable_task(v$task)] # named factor vector, names as task aliases and value of last reported status - reporter$status <- done + reporter$status <- rep(STATUS$pending, times = length(v_actionable)) + names(reporter$status) <- v_actionable$name + reporter$time_start <- Sys.time() cli::cli_text("<", utils::packageName(), "> Checks") } #' @export -report_status.reporter_basic_tty <- function(reporter, design, envir) { # nolint +report_status.reporter_basic_tty <- function(reporter, checker, envir) { cli_theme() - g <- design$graph - tasks_names <- names(igraph::V(g)) - # skip if queued, but not started or already finished - reported_statuses <- sapply(reporter$statuses, `[[`, 1) - reported_done <- names(reported_statuses[reported_statuses == STATUS$done]) - tasks_not_started <- names(igraph::V(g)[igraph::V(g)$status <= STATUS$`ready`]) - tasks_to_report <- tasks_names[!tasks_names %in% c(reported_done, tasks_not_started)] + g <- checker$graph + tasks_names <- names(reporter$status) + reported_done <- names(reporter$status[reporter$status == STATUS$done]) + tasks_not_started <- + names(igraph::V(g)[igraph::V(g)$status <= STATUS$`ready`]) + tasks_to_report <- + tasks_names[!tasks_names %in% c(reported_done, tasks_not_started)] for (i in igraph::V(g)[tasks_to_report]) { node <- igraph::V(g)[[i]] + # skip if queued, but not started + if (node$status <= STATUS$`pending`) next + p <- node$process # report stating of new checks - if (!identical(node$status, reporter$statuses[[node$name]])) { - status <- switch(as.character(node$status), # nolint + if (!identical(node$status, reporter$status[[node$name]])) { + status <- switch( # nolint + as.character(node$status), # nolint (used via glue) "pending" = "queued", "in progress" = cli::cli_fmt(cli::cli_text("started")), "done" = { @@ -53,38 +58,60 @@ report_status.reporter_basic_tty <- function(reporter, design, envir) { # nolint dur <- if (!is.null(p$get_duration)) { p$get_duration() } - if (node$type == "check") { + + if (is_check(node$task)) { ewn <- c("ERROR", "WARNING", "NOTE") ewn <- table(p$get_checks())[ewn] } else { ewn <- c(0, 0, 0) } + + fmt_error <- "{.err {ewn[[1]]} ERROR{?/S}}" + fmt_warning <- "{.warn {ewn[[2]]} WARNING{?/S}}" + fmt_note <- "{.note {ewn[[3]]} NOTE{?/S}}" + fmt_duration <- " {.time_taken ({format_time(dur)})}" cli::cli_fmt(cli::cli_text( "finished", if (sum(ewn) > 0) " with ", paste(collapse = ", ", c( - if (ewn[[1]] > 0) cli::format_inline("{.err {ewn[[1]]} ERROR{?/S}}"), - if (ewn[[2]] > 0) cli::format_inline("{.warn {ewn[[2]]} WARNING{?/S}}"), - if (ewn[[3]] > 0) cli::format_inline("{.note {ewn[[3]]} NOTE{?/S}}") + if (ewn[[1]] > 0) cli::format_inline(fmt_error), + if (ewn[[2]] > 0) cli::format_inline(fmt_warning), + if (ewn[[3]] > 0) cli::format_inline(fmt_note) )), - if (!is.null(dur)) cli::format_inline(" {.time_taken ({format_time(dur)})}") + if (!is.null(dur)) cli::format_inline(fmt_duration) )) } } ) - time <- Sys.time() - reporter$time_start # nolint - prefix <- cli::col_cyan("[{format_time(time)}][{node$type}] ") - cli::cli_text(prefix, "{.pkg {node$name}} {status}") - reporter$statuses[[node$name]] <- node$status + time <- Sys.time() - reporter$time_start # nolint (used via glue) + type <- format_task_type(node$task) # nolint (used via glue) + prefix <- cli::col_cyan("[{format_time(time)}][{type}] ") + cli::cli_text(prefix, "{.pkg {package(node$task)}} {status}") + reporter$status[[node$name]] <- node$status } } } #' @export -report_finalize.reporter_basic_tty <- function(reporter, design) { # nolint +report_finalize.reporter_basic_tty <- function(reporter, checker) { cli_theme() - report_status(reporter, design) # report completions of final processes - time <- format_time(Sys.time() - reporter$time_start) # nolint + report_status(reporter, checker) # report completions of final processes + time <- format_time(Sys.time() - reporter$time_start) # nolint (used via glue) cli::cli_text("Finished in {.time_taken {time}}") } + +#' @export +report_sleep.reporter_basic_tty <- function( + reporter, + checker, + sleep = 0 +) { + # Basci tty does not need sleep + NULL +} + +#' @export +report_step.reporter_basic_tty <- function(reporter, checker) { + checker$start_next_task() >= 0 +} diff --git a/R/reporter_null.R b/R/reporter_null.R index 2751c20..347a1d5 100644 --- a/R/reporter_null.R +++ b/R/reporter_null.R @@ -1,8 +1,24 @@ #' @export -report_initialize.NULL <- function(...) {} +report_start_setup.NULL <- function(...) {} + +#' @export +report_start_checks.NULL <- function(...) {} + +#' @export +report_finalize.NULL <- function(...) {} #' @export report_status.NULL <- function(...) {} #' @export report_finalize.NULL <- function(...) {} + +#' @export +report_step.NULL <- function(reporter, checker) { + checker$start_next_task() >= 0 +} + +#' @export +report_sleep.NULL <- function(reporter, checker, sleep = 0) { + Sys.sleep(sleep) +} diff --git a/R/results.R b/R/results.R index 4fefc2c..eb61cc1 100644 --- a/R/results.R +++ b/R/results.R @@ -4,7 +4,8 @@ CHECK_ISSUES_TYPES <- c("notes", "warnings", "errors") #' #' Get R CMD check results #' -#' @param x \code{\link[checked]{check_design}} object. +#' @param x object which results should be presented. +#' @param checker_obj [`checker`] object. #' @eval options::as_params("error_on" = "results_error_on") #' @param ... other parameters. #' @@ -16,31 +17,26 @@ results <- function(x, ...) { #' @export #' @rdname results -results.check_design <- function( +results.checker <- function( x, error_on = options::opt("results_error_on"), ...) { error_on <- match.arg(error_on, c("never", "issues", "potential_issues")) - checks_nodes <- igraph::V(x$graph)[ - igraph::vertex.attributes(x$graph)$type == "check" & igraph::vertex.attributes(x$graph)$status == STATUS$done + + stopifnot( + "checker object does not represnet a completed run" = x$is_done() + ) + + vs <- V(x$graph) + # Find root's of meta nodes + meta_nodes <- vs[is_meta(vs$task)] + root_meta_nodes_idx <- meta_nodes[ + igraph::ego_size(x$graph, nodes = meta_nodes, mode = "in") == 1 ] - checks_classes <- vcapply(checks_nodes$spec, function(x) class(x)[[1]]) - classes <- unique(checks_classes) - res <- lapply(classes, function(x) { - structure( - checks_nodes$spec[checks_classes == x], - class = paste0("list_", x) - ) - }) res <- structure( - lapply(res, function(y, output) { - structure( - results(y, output), - class = paste0("checked_results_", utils::head(class(y[[1]]), 1L)) - ) - }, output = x$output), - names = classes, + lapply(root_meta_nodes_idx, results, checker_obj = x), + names = vs[root_meta_nodes_idx]$name, class = "checked_results" ) @@ -60,71 +56,110 @@ results.check_design <- function( } #' @export -`[.checked_results` <- function(x, ...) { - structure(NextMethod(), class = class(x)) +#' @rdname results +results.integer <- function(x, checker_obj, ...) { + results(V(checker_obj$graph)[[x]], checker_obj = checker_obj) } #' @export -results.list_revdep_check_task_spec <- function(x, output, ...) { - name <- vcapply(x, function(y) y$package_spec$name) - revdep <- vcapply(x, `[[`, "revdep") - count <- table(name, revdep) - is_complete_pair <- vlapply(name, function(y) { - identical(unname(count[y, ]), c(1L, 1L)) - }) - - names_complete <- sort(unique(name[is_complete_pair])) - +#' @method results igraph.vs +#' @rdname results +results.igraph.vs <- function(x, ...) { + UseMethod("results", structure(0L, class = class(x$task))) +} - new <- lapply(names_complete, function(y) { - x[[which(name == y & revdep == "new")]] - }) +#' @export +#' @rdname results +results.rev_dep_dep_meta_task <- function(x, checker_obj, ...) { + # x is a igraph.vs with rev_dep_dep_meta_task task + nh <- igraph::neighbors(checker_obj$graph, x, mode = "out") + structure( + lapply(nh, results, checker_obj = checker_obj), + names = nh$name, + package = package(x$task), + class = "rev_dep_dep_results" + ) +} - old <- lapply(names_complete, function(y) { - x[[which(name == y & revdep == "old")]] +#' @export +#' @rdname results +results.rev_dep_check_meta_task <- function(x, checker_obj, ...) { + # x is a igraph.vs with rev_dep_dep_meta_task task + checks <- igraph::neighbors(checker_obj$graph, x, mode = "out") + seeds <- vcapply(checks, function(v) { + V(checker_obj$graph)[[v]]$task$seed }) + # seeds are dev and release, sort alphanumerical to make sure dev is always + # at the first index + checks <- checks[order(seeds)] + + results_revdep_check( + dev = checks[[1]]$name, + release = checks[[2]]$name, + output = checker_obj$output + ) +} +#' @export +#' @rdname results +results.local_check_meta_task <- function(x, checker_obj, ...) { + # x is a igraph.vs with rev_dep_dep_meta_task task + nh <- igraph::neighbors(checker_obj$graph, x, mode = "out") structure( - mapply(results, x = new, y = old, output = output, SIMPLIFY = FALSE), - names = names_complete + lapply(nh$name, results_check, output = checker_obj$output), + names = nh$name, + class = "local_check_results" ) } -#' @export -results.revdep_check_task_spec <- function(x, y, output, ...) { - new <- rcmdcheck_from_json(file.path(path_check_output(output, x$alias), "result.json")) - old <- rcmdcheck_from_json(file.path(path_check_output(output, y$alias), "result.json")) +results_revdep_check <- function(dev, release, output = NULL, ...) { + # Make it work either with full path or name of the package + dev_path <- if (is.null(output) || file.exists(dev)) { + dev + } else { + file.path(path_check_output(output, dev), "result.json") + } + + release_path <- if (is.null(output) || file.exists(release)) { + release + } else { + file.path(path_check_output(output, release), "result.json") + } + dev_check <- rcmdcheck_from_json(dev_path) + release_check <- rcmdcheck_from_json(release_path) structure( lapply(CHECK_ISSUES_TYPES, function(i) { - new_i <- structure( - # If no issues identified, object is an empty list instead of a character - # vector. Changing it to empty character for consistency. - if (is.list(new[[i]])) character(0) else new[[i]], - names = get_issue_header(new[[i]]) + dev_check_i <- structure( + # If no issues identified, object is an empty list instead of + # a character vector. Changing it to empty character for consistency. + if (is.list(dev_check[[i]])) character(0) else dev_check[[i]], + names = get_issue_header(dev_check[[i]]) ) - old_i <- structure( - if (is.list(old[[i]])) character(0) else old[[i]], - names = get_issue_header(old[[i]]) + release_check_i <- structure( + if (is.list(release_check[[i]])) character(0) else release_check[[i]], + names = get_issue_header(release_check[[i]]) ) - matching_headers_idx <- names(new_i) %in% names(old_i) + matching_headers_idx <- names(dev_check_i) %in% names(release_check_i) # Create temporary object with "See for details" path # stripped out as well as all whitespaces. As they will always emit # potential issues due to the path or screen differences - new_i_tmp <- strip_details_from_issue(new_i) - old_i_tmp <- strip_details_from_issue(old_i) - matching_messages_idx <- new_i_tmp %in% old_i_tmp + dev_check_i_tmp <- strip_details_from_issue(dev_check_i) + release_check_i_tmp <- strip_details_from_issue(release_check_i) + matching_messages_idx <- dev_check_i_tmp %in% release_check_i_tmp new_issues <- structure( - unname(new_i[!matching_headers_idx]), + unname(dev_check_i[!matching_headers_idx]), class = "issues" ) - new_potential_issues <- new_i[matching_headers_idx & !matching_messages_idx] + new_potential_issues <- dev_check_i[ + matching_headers_idx & !matching_messages_idx + ] new_potential_issues <- structure( list( new = unname(new_potential_issues), - old = unname(old_i[names(new_potential_issues)]) + old = unname(release_check_i[names(new_potential_issues)]) ), class = "potential_issues" ) @@ -132,72 +167,38 @@ results.revdep_check_task_spec <- function(x, y, output, ...) { list("issues" = new_issues, "potential_issues" = new_potential_issues) }), names = CHECK_ISSUES_TYPES, - package = new$package, - class = "rcmdcheck_diff" + package = dev_check$package, + class = c("rcmdcheck_rev_dep_results", "rcmdcheck_results") ) } -#' @export -results.list_check_task_spec <- function(x, output, ...) { - alias <- vcapply(x, `[[`, "alias") - structure( - lapply(x, results, output = output), - names = alias - ) -} +results_check <- function(x, output, ...) { + # Make it work either with full path or name of the package + x_path <- if (is.null(output) || file.exists(x)) { + x + } else { + file.path(path_check_output(output, x), "result.json") + } -#' @export -results.check_task_spec <- function(x, output, ...) { - x <- rcmdcheck_from_json(file.path(path_check_output(output, x$alias), "result.json")) + x_check <- rcmdcheck_from_json(x_path) structure( lapply(CHECK_ISSUES_TYPES, function(i) { - x_i <- x[[i]] + x_check_i <- x_check[[i]] new_issues <- structure( - unname(x_i), + unname(x_check_i), class = "issues" ) list("issues" = new_issues) }), names = CHECK_ISSUES_TYPES, - package = x$package, - class = "rcmdcheck_diff" + package = x_check$package, + class = c("rcmdcheck_check_results", "rcmdcheck_results") ) } -#' Results to file -#' -#' Write \code{checked_results} object to the text file. When converting results -#' to text, \code{\link[checked]{print.checked_results}} method is used. -#' -#' -#' @param results \code{\link[checked]{results}} object. -#' @param file A connection or character path. -#' @inheritParams print.checked_results -#' -#' @family results -#' @export -results_to_file <- function(results, file, keep = "all", ...) { - text <- c() - for (i in seq_along(results)) { - df <- results_to_df(results[[i]], issues_type = keep) - if (keep == "all" || any(rowSums(df) > 0)) { - text <- c( - text, - utils::capture.output(print(results[i], keep = keep)) - ) - } - } - - if (!any(nzchar(text))) { - text <- "No issues identified." - } - - writeLines(text, file) -} - results_to_df <- function(results, ...) { if (length(results) == 0) { data.frame( @@ -216,6 +217,18 @@ results_to_df <- function(results, ...) { } } +filter_results <- function(x, keep, ...) { + keep <- match.arg(keep, c("all", "issues", "potential_issues")) + + if (keep != "all") { + df <- results_to_df(x, issues_type = keep) + issues <- rowSums(df) != 0 + x[issues] + } else { + x + } +} + count <- function(d, ...) { UseMethod("count") } @@ -235,56 +248,38 @@ count.potential_issues <- function(d, issues_type = "potential_issues", ...) { if (issues_type == "issues") 0 else length(d$new) } -#' @export -summary.check_design <- function(object, ...) { - summary(results(object), ...) -} - -#' @export -summary.checked_results <- function(object, ...) { - lapply(object, summary, ...) -} - -#' @export -summary.checked_results_revdep_check_task_spec <- function(object, ...) { - summary.checked_results_check_task_spec(object, ...) -} - -#' @export -summary.checked_results_check_task_spec <- function(object, ...) { - results_to_df(object, ...) -} - #' Print checked results #' #' @param x an object to be printed. #' @eval options::as_params("keep" = "results_keep") +#' @param name character name of the `rev_dep_dep` package #' @param ... other parameters. #' #' @family results #' @export print.checked_results <- function(x, ...) { for (i in seq_along(x)) { - cat("#", tools::toTitleCase(strsplit(names(x)[i], "_")[[1]]), "\n\n") - print(x[[i]], ...) - cat("\n") + print(x[[i]], ..., name = names(x)[[i]]) + cat("\n\n\n") } invisible(x) } #' @name print.checked_results #' @export -print.checked_results_check_task_spec <- function( - x, - keep = options::opt("results_keep"), - ...) { - - keep <- match.arg(keep, c("all", "issues", "potential_issues")) - if (keep != "all") { - df <- results_to_df(x, issues_type = keep) - issues <- rowSums(df) != 0 - x <- x[issues] - } +print.rev_dep_dep_results <- function( + x, + ..., + name = NULL, + keep = options::opt("results_keep") +) { + cat(sprintf( + "# %s reverse dependency check results (%s) \n\n", + attr(x, "package"), + name + )) + + x <- filter_results(x, keep = keep) for (i in seq_along(x)) { print(x[[i]], ...) @@ -295,8 +290,24 @@ print.checked_results_check_task_spec <- function( #' @name print.checked_results #' @export -print.checked_results_revdep_check_task_spec <- function(x, ...) { - print.checked_results_check_task_spec(x, ...) +print.local_check_results <- function( + x, + ..., + name = NULL, + keep = options::opt("results_keep") +) { + cat(sprintf( + "# Local check results (%s) \n\n", + name + )) + + x <- filter_results(x, keep = keep) + + for (i in seq_along(x)) { + print(x[[i]], ...) + cat("\n") + } + invisible(x) } get_issue_header <- function(x) { @@ -335,8 +346,19 @@ rcmdcheck_from_json <- function(file) { } #' @export -print.rcmdcheck_diff <- function(x, ...) { +print.rcmdcheck_rev_dep_results <- function(x, ...) { cat(sprintf("%s package R CMD check diff \n", attr(x, "package"))) + NextMethod() +} + +#' @export +print.rcmdcheck_check_results <- function(x, ...) { + cat(sprintf("%s package R CMD check \n", attr(x, "package"))) + NextMethod() +} + +#' @export +print.rcmdcheck_results <- function(x, ...) { for (i in CHECK_ISSUES_TYPES) { status <- if (length(x[[i]]$issues) > 0) { sprintf("NEW ISSUES [%s]", length(x[[i]]$issues)) @@ -392,5 +414,5 @@ collapse_new_lines <- function(x) { x = x, pattern = "(\\n\\s*){2,}", replacement = "\n\n", - ) + ) } diff --git a/R/run.R b/R/run.R index 063be82..b2615ca 100644 --- a/R/run.R +++ b/R/run.R @@ -1,39 +1,47 @@ #' Run a Series of `R CMD check`s #' #' [`run()`] provides a generic, and is the central interface for executing -#' [`check_design`]s. If a path is provided, a new reverse dependency check +#' [`checker`]s. If a path is provided, a new reverse dependency check #' plan is generated from the source code path. Otherwise a plan can be #' built separately and executed using [`run()`]. #' -#' @param design `character` or `check_design` If a `character` value is -#' provided, it is first coerced into a `check_design` using -#' [`new_rev_dep_check_design()`]. -#' @param ... Additional arguments passed to [`new_rev_dep_check_design()`] +#' @param checker `character` or `checker` If a `character` value is +#' provided, it is first coerced into a `checker` using +#' [`new_rev_dep_checker()`]. +#' @param ... Additional arguments passed to [`new_rev_dep_checker()`] #' @param reporter A reporter to provide progress updates. Will default to the #' most expressive command-line reporter given your terminal capabilities. #' #' @export -run <- function(design, ..., reporter = reporter_default()) { +run <- function(checker, ..., reporter = reporter_default()) { UseMethod("run") } #' @export -run.character <- function(design, ..., reporter = reporter_default()) { - run(new_rev_dep_check_design(design, ...), reporter = reporter) +run.character <- function(checker, ..., reporter = reporter_default()) { + checker <- new_rev_dep_checker(checker, ...) + report_start_setup( + reporter, + checker, + extra = list(message = "planning checks ...") + ) + + run(checker, reporter = reporter) } #' @export -run.check_design <- function(design, ..., reporter = reporter_default()) { +run.checker <- function(checker, ..., reporter = reporter_default()) { on.exit(add = TRUE, { - design$terminate() - report_finalize(reporter, design) + checker$terminate() + report_finalize(reporter, checker) }) - report_initialize(reporter, design) - while (design$start_next_task() >= 0) { - report_status(reporter, design) - report_sleep(reporter, design) + report_start_setup(reporter, checker) + report_start_checks(reporter, checker) + while (report_step(reporter, checker)) { + report_status(reporter, checker) + report_sleep(reporter, checker) } - invisible(design) + invisible(checker) } diff --git a/R/task-formatting.R b/R/task-formatting.R new file mode 100644 index 0000000..50d9125 --- /dev/null +++ b/R/task-formatting.R @@ -0,0 +1,75 @@ +#' @export +format.task <- function(x, ..., indent = 0L) { + fmt(task = x, "{action} {package} {version} from {source}", ...) +} + +#' @export +format.local_check_meta_task <- function(x, ..., indent = 0L) { + fmt(task = x, "{action}", ...) +} + +format_task_type <- function(x, ...) { + UseMethod("format_task_type") +} + +#' @export +format_task_type.task <- function(x, ...) { + sub("_task$", "", class(x)[[1]]) +} + +#' @export +format_task_type.rev_dep_dep_meta_task <- function(x, ...) { + "meta revdeps of" +} + +#' @export +format_task_type.rev_dep_check_meta_task <- function(x, ..., g = NULL) { + fmt(task = x, "meta revdep {.pkg {x$revdep}} of") +} + +#' @export +format_task_type.local_check_meta_task <- function(x, ...) { + "meta checks" +} + +format_task_name <- function(x, ...) { + UseMethod("format_task_name") +} + +#' @export +format_task_name.default <- function(x, ...) { + stop( + "Dont' know how to name object with class(es) `", + deparse(class(x)), + "`" + ) +} + +#' @export +format_task_name.task <- function(x, ...) { + "task" +} + +#' @export +format_task_name.install_task <- function(x, ..., short = FALSE) { + paste0(if (!short) "install ", format(x$origin, ..., short = short)) +} + +#' @export +format_task_name.check_task <- function(x, ...) { + paste0("check ", format(x$origin, ...)) +} + +#' @export +format_task_name.rev_dep_dep_meta_task <- function(x, ..., short = FALSE) { + paste0( + "check ", + format(x$origin, ..., short = short), + if (short) " revdeps" else " reverse-dependencies" + ) +} + +#' @export +format_task_name.rev_dep_check_meta_task <- function(x, ..., short = FALSE) { + paste0("check ", if (short) "revdep" else "reverse-dependency") +} diff --git a/R/task.R b/R/task.R new file mode 100644 index 0000000..fd32ff6 --- /dev/null +++ b/R/task.R @@ -0,0 +1,167 @@ +#' Task specification +#' +#' Create task specification list which consists of all the details required +#' to run specific task. +#' +#' Tasks can be nested, representing either a singular task, or a set of +#' related tasks. +#' +#' @param ... parameters passed to downstream constructors. +#' @param .subclass Additional subclasses. +#' +#' @family tasks +#' @export +task <- function(..., .subclass = NULL) { + structure(list(...), class = c(sprintf("%s_task", .subclass), "task")) +} + +#' Construct a 'Meta' Task +#' +#' Meta tasks are tasks which are not intended to perform computation. They +#' exist simply to provide relationships among computational tasks. +#' +#' @param ... Objects passed to specified class functions +#' @param .subclass character name of the subclass. It will be appended with +#' "_meta" suffix. +#' +#' @family tasks +#' @export +meta_task <- function(..., .subclass = NULL) { + task(..., .subclass = c(sprintf("%s_meta", .subclass), "meta")) +} + +make_unique_task <- function(task, seed = stats::runif(1)) { + task$seed <- seed + task +} + +#' @export +#' @rdname lib +lib.task <- function(x, ...) { + character(0L) +} + +#' @family tasks +#' @export +print.task <- function(x, ...) { + cat(format(x, ...), "\n") +} + +#' Create a task to install a package and dependencies +#' +#' @param origin [`pkg_origin()`] object. +#' @param lib Any object that can be passed to [`lib()`] to generate a library +#' path. +#' @inheritParams utils::install.packages +#' +#' @family tasks +#' @export +install_task <- function( + origin, + type = package_install_type(origin), + INSTALL_opts = NULL, + lib = lib_path(origin), + ... +) { + task( + origin = origin, + type = type, + INSTALL_opts = INSTALL_opts, + lib = lib, + ..., + .subclass = "install" + ) +} + +#' @export +#' @rdname lib +lib.install_task <- function(x, ...) { + lib(x$lib, dir_hash = hash(x$origin, n = 8), name = package(x), ...) +} + +is_type <- function(x, type) { + UseMethod("is_type") +} + +#' @export +is_type.default <- function(x, type) { + inherits(x, type) +} + +#' @export +is_type.list <- function(x, type) { + vlapply(x, is_type, type = type) +} + +#' @export +is_type.task <- function(x, type) { + inherits(x, paste0(type, "_task")) +} + +#' @export +is_type.process <- function(x, type) { + inherits(x, paste0(type, "_process")) +} + +is_install <- function(x) { + is_type(x, "install") +} + +is_check <- function(x) { + is_type(x, "check") +} + +is_actionable_task <- function(x) { + is_type(x, "check") | is_type(x, "install") +} + +is_meta <- function(x) { + is_type(x, "meta") +} + +#' Create a task to run `R CMD check` +#' +#' @inheritParams rcmdcheck::rcmdcheck +#' @inheritDotParams task +#' +#' @family tasks +#' @export +check_task <- function(build_args = NULL, args = NULL, env = NULL, ...) { + task( + env = env, + args = args, + build_args = build_args, + ..., + .subclass = "check" + ) +} + +#' @export +#' @rdname lib +lib.check_task <- function(x, ...) { + character(0L) # no additional libraries needed for checking +} + +package <- function(x) { + UseMethod("package") +} + +#' @export +package.default <- function(x) { + stop("Unrecognized type") +} + +#' @export +package.NULL <- function(x) { + "" +} + +#' @export +package.task <- function(x) { + package(x$origin) +} + +#' @export +package.pkg_origin <- function(x) { + x$package +} diff --git a/R/task_graph.R b/R/task_graph.R index 1985a4f..8f79499 100644 --- a/R/task_graph.R +++ b/R/task_graph.R @@ -1,180 +1,166 @@ -#' Create Task Graph +#' Build task graph edges #' -#' @param df data.frame listing -#' @param repos repositories which will be used to identify dependencies chain -#' to run R CMD checks -#' @return A dependency graph with vertex attributes "root" (a logical value -#' indicating whether the package as one of the roots used to create the -#' graph), "status" (installation status) and "order" (installation order). +#' Edges describe relationships between tasks. Often, this is a dependency +#' between packages, requiring that some package be installed before a latter +#' task can be executed. #' +#' [`tools::package_dependencies()`] is used to calculate these relationships. +#' However, the package data returned by [`utils::available.packages()`], +#' that is used internally to determine dependencies does not know about +#' local or remote packages, so those are first appended to this data set +#' prior to calculating edges. The bulk of this function serves to join this +#' data. +#' +#' @param x a `plan` object, containing a list of related steps. +#' @param repos `repos`, as expected by [`tools::package_dependencies()`] to +#' determine package relationships. +#' @param ... params passed to helper methods. +#' @return A `data.frame` that can be used to build +#' [`igraph::make_graph`] edges. +#' +#' @examples +#' \dontrun{ +# # requires that source code directory is for a package with revdeps +#' task_graph(plan_rev_dep_checks(".")) +#' } #' @keywords internal -#' @importFrom igraph V -task_graph_create <- function(df, repos = getOption("repos")) { - edges <- task_edges_df(df, repos) - vertices <- task_vertices_df(df, edges, repos) - - g <- igraph::graph_from_data_frame(edges, vertices = vertices) - igraph::V(g)$status <- STATUS$pending # nolint object_name_linter - igraph::V(g)$process <- rep_len(list(), length(g)) - task_graph_sort(g) +#' +#' @importFrom igraph V E +task_graph <- function(x, repos = getOption("repos"), ...) { + UseMethod("task_graph") } -task_edges_df <- function(df, repos) { +#' @export +task_graph.task <- function(x, repos = getOption("repos"), ...) { + df <- pkg_deps(x$origin, repos = repos, dependencies = TRUE) + # Distinguish direct dependencies of the package form possible indirect + # in the same data.frame which could come from suggested loops. This ensures + # there is a separate node for the root task. + df$package[df$depth == "direct"] <- + paste(df$package[df$depth == "direct"], "root", sep = "-") + colmap <- c("package" = "from", "name" = "to") + rename <- match(names(df), names(colmap)) + to_rename <- !is.na(rename) + names(df)[to_rename] <- colmap[rename[to_rename]] + df <- df[, c(which(to_rename), which(!to_rename)), drop = FALSE] + g_dep <- igraph::graph_from_data_frame(df) + + E(g_dep)$relation <- RELATION$dep + E(g_dep)$type <- DEP[E(g_dep)$type] + V(g_dep)$task <- lapply( + V(g_dep)$name, + function(p) { + if (endsWith(p, "-root")) { + x + } else { + origin <- try_pkg_origin_repo(package = p, repos = repos) + install_task(origin = origin) + } + } + ) - if (NROW(df) == 0) { - return(empty_edge) - } + V(g_dep)$name <- vcapply(V(g_dep)$task, as_vertex_name) - db <- utils::available.packages(repos = repos)[, DB_COLNAMES] + g_dep +} - # For checks alias has to have different name than package name +#' @export +task_graph.task_graph <- function(x, repos = getOption("repos"), ...) { + # only use dependency edges when populating graph + nodes <- V(x)[is_actionable_task(V(x)$task)] - # Add custom packages to db - custom_aliases_idx <- which(vlapply(df$custom, function(x) !is.null(x$alias))) - custom_aliases <- vcapply(df$custom[custom_aliases_idx], `[[`, "alias") - custom_aliases_map <- unique(data.frame( - value = custom_aliases, - hash = vcapply(custom_aliases, hash_alias) - )) + check_task_neighborhoods <- igraph::neighborhood( + x, + order = length(x), + mode = "out", + nodes = nodes + ) - desc <- drlapply(df$custom, function(x) { - row <- get_package_spec_dependencies(x$package_spec) - hash <- custom_aliases_map[custom_aliases_map$value == x$alias, ]$hash - row[, "Package"] <- hash - row - }) - # Drop potential duplicates - desc <- unique(desc) - - # Adding checks to db and custom packages as Depends link - checks <- drlapply(df$package, function(x) { - p <- df[df$alias == x$alias, ] - row <- get_package_spec_dependencies(x$package_spec) - row[, "Package"] <- x$alias - if (!is.null(p$custom[[1]]$alias)) { - row_idx <- custom_aliases_map$value == p$custom[[1]]$alias - hash <- custom_aliases_map[row_idx, ]$hash - row[, "Depends"] <- ifelse( - is.na(row[, "Depends"]), - hash, - paste0(row[, "Depends"], ", ", hash) - ) - } - row - }) + # for each check task in the plan, build a dependency tree and merge it + # into the existing check task subtree + check_task_neighborhoods <- lapply(check_task_neighborhoods, function(nh) { + subtree <- igraph::induced_subgraph(x, nh) - db <- rbind(db, desc, checks) + # build dependency graph, with fallback installation task + deps <- task_graph(nh[[1]]$task, repos = repos) - # Get suggests end enhances dependencies first so we can derive hard - # dependencies for them as well - suggests_dependencies <- uulist(package_deps( - df$alias, - db = db, - which = c("Suggests", "Enhances"), - recursive = FALSE - )) + # merge trees on package names + # NOTE: attributes (tasks) are preserved in the order they appear + subtree <- graph_dedup_attrs(igraph::union(subtree, deps)) - # Get recursively strong dependencies for all packages - core_dependencies <- package_deps( - c(df$alias, custom_aliases_map$hash, suggests_dependencies), - db = db, - which = "strong", - recursive = TRUE - ) + # fill new edges with dependency relation - between check task and installs + is_na <- is.na(E(subtree)$type) + E(subtree)$type[is_na] <- DEP$Depends - dependencies <- uulist(c( - # tools::package_dependencies do not include package itself. - # we add it at this stage - df$alias, - custom_aliases_map$hash, - suggests_dependencies, - core_dependencies - )) + deduplicate_task_graph(subtree) + }) + # then merge all the full check task task trees into a single graph + g <- graph_dedup_attrs(igraph::union(x, check_task_neighborhoods)) - dependencies <- dependencies[!dependencies %in% base_pkgs()] + E(g)$type <- DEP[E(g)$type] + V(g)$status <- STATUS$pending + V(g)$process <- rep_len(list(), length(g)) - edges <- drlapply(dependencies, function(p) { - edges_per_type <- drlapply(uulist(DEP), function(type) { - deps <- try(db[db[, "Package"] == p, type], silent = TRUE) - if (inherits(deps, "try-error") || length(deps) == 0) { - empty_edge - } else { - deps <- split_packages_names(deps) - # Filter out base packages - deps <- deps[deps$dep %in% dependencies, ] - cbind( - deps, - root = rep(p, times = NROW(deps)), - type = rep(type, times = NROW(deps)) - ) - } - }) - }) + g <- task_graph_sort(g) + + # Restore the original (defined by the plan) order of primary tasks + n_g <- length(V(g)) + x_ids <- as.numeric(V(g)[names(V(x))]) + g_ids <- numeric(n_g) + g_ids[x_ids] <- seq(from = n_g, length.out = length(x_ids), by = -1) + g_ids[g_ids == 0] <- setdiff(seq_along(V(g)), g_ids) + g <- igraph::permute(g, g_ids) - edges$dep <- replace_with_map(edges$dep, custom_aliases_map$hash, custom_aliases_map$value) - edges$root <- replace_with_map(edges$root, custom_aliases_map$hash, custom_aliases_map$value) - # reorder columns to the igraph format - edges[, c("dep", "root", "type", "op", "version")] + task_graph_class(g) } -task_vertices_df <- function(df, edges, repos) { - vertices <- unique(c(edges$dep, edges$root)) - custom_pkgs_aliases <- uulist(lapply(df$custom, `[[`, "alias")) - task_type <- ifelse(vertices %in% df$alias, "check", "install") - - spec <- lapply(vertices, function(v) { - if (v %in% df$alias) { - df$package[[which(df$alias == v)]] - } else if (v %in% custom_pkgs_aliases) { - df$custom[[utils::head(which(as.character(lapply(df$custom, `[[`, "alias")) == v), 1)]] - } else { - e <- edges[edges$dep == v, ] - ver_order <- order( - e$version, - # In case of multiple requirements with the same version - # prioritize those using ">" operator - e$op, - na.last = TRUE, - decreasing = c(TRUE, FALSE), - method = "radix" - ) - install_task_spec( - alias = v, - package_spec = package_spec( - name = v, - repos = repos, - # Specify version requirements for dependencies - op = e$op[[ver_order[[1]]]], - version = e$version[[ver_order[[1]]]] - ) + +task_graph_class <- function(g) { + class(g) <- c("task_graph", class(g)) + g +} + + +deduplicate_task_graph <- function(g) { + vs <- V(g) + for (i in vs) { + # Task graph is allowed to have multi-edges in which case neighbors + # would multiply the same nodes. Therefore we call unique. + children <- unique(igraph::neighbors(g, i, "out")) + children_names <- vcapply(children$task, package) + duplicated_names <- children_names[duplicated(children_names)] + for (p in duplicated_names) { + duplicated_nodes <- children[children_names == p] + g <- igraph::delete_edges( + g, + # Always keep lowest ID node + paste(vs$name[[i]], duplicated_nodes[-1]$name, sep = "|") ) } - }) - - out <- data.frame( - name = vertices, - type = task_type, - custom = vertices %in% custom_pkgs_aliases - ) + } + isolated <- which(igraph::degree(g) == 0) + igraph::delete_vertices(g, isolated) +} - out$spec <- spec - out +dep_edges <- function(edges, dependencies = TRUE) { + edges[edges$type %in% dependencies] } #' Find Task Neighborhood #' -#' @param g A task graph, as produced with [task_graph_create()] +#' @param g A task graph, as produced with [task_graph()] #' @param nodes Names or nodes objects of packages whose neighborhoods #' should be calculated. #' #' @importFrom igraph neighborhood #' @keywords internal -task_graph_neighborhoods <- function(g, nodes) { +task_graph_neighborhoods <- function(g, nodes, ...) { igraph::neighborhood( g, order = length(g), nodes = nodes, - mode = "in" + mode = "out", + ... ) } @@ -193,32 +179,42 @@ task_graph_neighborhoods <- function(g, nodes) { #' @return The [igraph::graph] `g`, with vertices sorted in preferred #' installation order. #' -#' @importFrom igraph vertex_attr neighborhood subgraph.edges permute topo_sort E V E<- V<- +#' @importFrom igraph vertex_attr neighborhood subgraph.edges permute topo_sort +#' @importFrom igraph E V E<- V<- #' @keywords internal task_graph_sort <- function(g) { - roots <- which(igraph::vertex_attr(g, "type") == "check") + roots <- which(is_check(V(g)$task)) - # split into neighborhoods by root (revdep) - nhood <- task_graph_neighborhoods(g, roots) + # calculcate check task neighborhood sizes + nh_sizes <- igraph::neighborhood_size( + g, + nodes = roots, + order = length(g), + mode = "out" + ) # prioritize by neighborhood size (small to large) - priority <- length(nhood) + priority <- length(roots) priority_footprint <- integer(length(g)) - for (i in order(-vapply(nhood, length, integer(1L)))) { - priority_footprint[nhood[[i]]] <- priority + for (i in order(-nh_sizes)) { + priority_footprint[roots[[i]]] <- priority priority <- priority - 1 } # use only strong dependencies to prioritize by topology (leafs first) - strong_edges <- igraph::E(g)[igraph::E(g)$type %in% DEP_STRONG] - g_strong <- igraph_subgraph_from_edges(g, strong_edges, delete.vertices = FALSE) - topo <- igraph::topo_sort(g_strong, mode = "in") + strong_edges <- dep_edges(E(g), check_dependencies("strong")) + g_strong <- igraph::subgraph_from_edges( + g, + strong_edges, + delete.vertices = FALSE + ) + topo <- igraph::topo_sort(g_strong, mode = "out") priority_topo <- integer(length(g)) - priority_topo[match(topo$name, igraph::V(g)$name)] <- rev(seq_along(topo)) + priority_topo[match(topo$name, V(g)$name)] <- rev(seq_along(topo)) # combine priorities, prioritize first by total, footprint then topology - priorities <- rbind(priority_footprint, priority_topo) - order <- rank(length(igraph::V(g))^seq(nrow(priorities) - 1, 0) %*% priorities) + priority <- rbind(priority_footprint, priority_topo) + order <- rank(length(V(g))^seq(nrow(priority) - 1, 0) %*% priority) g <- igraph::permute(g, order) g @@ -226,23 +222,21 @@ task_graph_sort <- function(g) { #' Find the Next Packages Not Dependent on an Unavailable Package #' -#' While other packages are in progress, identify tasks with all the -#' dependencies done and mark them as \code{ready} already has its dependencies -#' done. -#' +#' While other packages are in progress, ensure that the next selected package +#' already has its dependencies done. +#' #' @details #' There are helpers defined for particular use cases that strictly rely on the -#' [`task_graph_update_ready()`], they are: +#' [`task_graph_which_ready()`], they are: #' -#' * `task_graph_update_ready_strong()` - List vertices whose strong +#' * `task_graph_update_check_ready()` - Updates check vertices whose all #' dependencies are satisfied. -#' * `task_graph_update_check_ready()` - List root vertices whose all +#' * `task_graph_update_install_ready()` - Update install vertices whose all #' dependencies are satisfied. -#' * `task_graph_update_install_ready()` - List install vertices whose -#' dependencies are all satisfied +#' * `task_graph_which_ready()` - List vertices whose wit ready status. #' -#' @param g A dependency graph, as produced with [task_graph_create()]. -#' @param v Names or nodes objects of packages whose readiness should be +#' @param g A dependency graph, as produced with [task_graph()]. +#' @param v Names or nodes objects of packages whose satisfiability should be #' checked. #' @param dependencies Which dependencies types should be met for a node to be #' considered satisfied. @@ -254,45 +248,42 @@ task_graph_sort <- function(g) { #' @importFrom igraph incident_edges tail_of #' @keywords internal task_graph_update_ready <- function( - g, - v = igraph::V(g), - dependencies = TRUE, - status = STATUS$pending) { - if (is.character(status)) status <- STATUS[[status]] + g, + v = V(g), + dependencies = TRUE, + status = STATUS$pending +) { + if (is.character(status)) { + status <- STATUS[[status]] + } + dependencies <- check_dependencies(dependencies) + if (length(status) > 0) { idx <- v$status %in% status v <- v[idx] } deps_met <- vlapply( - igraph::incident_edges(g, v, mode = "in"), + igraph::incident_edges(g, v, mode = "out"), function(edges) { - edges <- edges[edges$type %in% dependencies] - all(igraph::tail_of(g, edges)$status == STATUS$done) + is_dep <- edges$type %in% dependencies + all(igraph::head_of(g, edges[is_dep])$status == STATUS$done) } ) - task_graph_set_package_status( - g, - names(deps_met[deps_met]), - STATUS$ready - ) -} - -task_graph_update_ready_strong <- function(..., dependencies = "strong") { # nolint - task_graph_update_ready(..., dependencies = dependencies) + task_graph_set_package_status(g, names(deps_met[deps_met]), STATUS$ready) } - task_graph_update_check_ready <- function( - g, - ..., - dependencies = "all", - status = STATUS$pending) { + g, + ..., + dependencies = "all", + status = STATUS$pending +) { task_graph_update_ready( g, - igraph::V(g)[igraph::V(g)$type == "check"], + V(g)[is_check(V(g)$task)], ..., dependencies = dependencies, status = status @@ -300,52 +291,36 @@ task_graph_update_check_ready <- function( } task_graph_update_install_ready <- function( - g, - ..., - dependencies = "strong", - status = STATUS$pending) { + g, + ..., + dependencies = "strong", + status = STATUS$pending +) { task_graph_update_ready( g, - igraph::V(g)[igraph::V(g)$type == "install"], + V(g)[is_install(V(g)$task)], ..., dependencies = dependencies, status = status ) } - -#' Find task with ready state +#' Find nodes with ready state #' -#' List tasks which have ready state prioritizing check tasks over -#' install tasks. +#' List nodes which have ready state prioritizing check task nodes over +#' install task nodes. #' -#' @param g A dependency graph, as produced with [task_graph_create()]. +#' @param g A dependency graph, as produced with [task_graph()]. #' #' @return The names of packages with ready state. #' -#' @importFrom igraph incident_edges tail_of #' @keywords internal task_graph_which_ready <- function(g) { - ready_checks <- task_graph_get_package_with_status( - g, - igraph::V(g)[igraph::V(g)$type == "check"], - "ready" - ) - ready_installs <- task_graph_get_package_with_status( - g, - igraph::V(g)[igraph::V(g)$type == "install"], - "ready" - ) - - c(ready_checks, ready_installs) + nodes <- V(g)[is_actionable_task(V(g)$task)] + statuses <- igraph::vertex.attributes(g, nodes)$status + nodes[statuses == STATUS["ready"]] } -empty_edge <- data.frame( - dep = character(0), - root = character(0), - type = character(0) -) - task_graph_set_package_status <- function(g, v, status) { if (is.character(status)) status <- STATUS[[status]] igraph::set_vertex_attr(g, "status", v, status) @@ -359,23 +334,12 @@ task_graph_package_status <- function(g, v) { task_graph_set_package_status(x, v, value) } -`task_graph_package_status<-` <- function(x, v, value) { - task_graph_set_package_status(x, v, value) -} - -task_graph_get_package_with_status <- function(g, v, status) { - if (is.character(status)) status <- STATUS[[status]] - statuses <- igraph::vertex.attributes(g, v)$status - - v[statuses == .env$status] -} - `task_graph_task_process<-` <- function(x, v, value) { task_graph_set_task_process(x, v, value) } -task_graph_task_spec <- function(g, v) { +task_graph_task <- function(g, v) { igraph::vertex_attr(g, "spec", v)[[1]] } @@ -392,36 +356,128 @@ task_graph_set_task_process <- function(g, v, process) { } task_graph_update_done <- function(g, lib.loc) { - custom_installs <- vlapply( - igraph::V(g)$spec, - inherits, - "custom_install_task_spec" - ) - installs <- igraph::V(g)$type == "install" - # custom install cannot be satisfied - v <- igraph::V(g)[installs & !custom_installs] - which_done <- which(vlapply(v$spec, is_package_satisfied, lib.loc = lib.loc)) + v <- V(g)[V(g)$type == "install"] + which_done <- which(vlapply(v$name, is_package_installed, lib.loc = lib.loc)) task_graph_set_package_status(g, v[which_done], STATUS$done) } -is_package_satisfied <- function(v, lib.loc) { # nolint object_name_linter - if (!is.null(v$package_spec$version)) { - installed_version <- tryCatch( - utils::packageVersion(v$package_spec$name, lib.loc = lib.loc), - error = function(e) { - numeric_version("0") - } - ) - get(v$package_spec$op)(installed_version, v$package_spec$version) - } else { - FALSE +#' @export +plot.task_graph <- function(x, ..., interactive = FALSE) { + style <- function(attr, ...) { + map <- simplify2array(list(...)) + nomatch <- if (names(map[length(map)]) == "") length(map) else NA + map[match(as.character(attr), names(map), nomatch = nomatch)] } -} -igraph_subgraph_from_edges <- function(...) { - if (utils::packageVersion("igraph") < "2.1.0") { - igraph::subgraph.edges(...) + task_type <- vcapply(V(x)$task, function(task) { + if (is_install(task)) return(class(task$origin)[[1]]) + class(task)[[1]] + }) + + vertex <- igraph::as_data_frame(x, what = "vertices") + edge <- igraph::as_data_frame(x, what = "edges") + + vertex$color <- style( + task_type, + "rev_dep_dep_meta_task" = "blueviolet", + "rev_dep_check_meta_task" = "blueviolet", + "check_task" = "lightblue", + "library_task" = "lightgreen", + "install_task" = "cornflowerblue", + "pkg_origin_repo" = "cornflowerblue", + "pkg_origin_base" = "lightgray", + "pkg_origin_unknown" = "red", + "pkg_origin_local" = "blue", + "pkg_origin_remote" = "orange", + "red" + ) + + vertex$label <- cli::ansi_strip(vcapply(V(x)$task, format, g = x)) + + vertex$frame.color <- style( + STATUS[V(x)$status %||% 1], + "done" = "green", + "in progress" = "lightgreen", + "black" + ) + + vertex$frame.width <- style( + STATUS[V(x)$status %||% 1], + "done" = 3L, + 1L + ) + + vertex$size <- style( + task_type, + "install_task" = 5, + "pkg_origin_repo" = 5, + "pkg_origin_unknown" = 5, + "pkg_origin_local" = 5, + "pkg_origin_base" = 5, + 8 + ) + + if (interactive) { + plot_interactive_task_graph(x, vertex = vertex, edge = edge) } else { - igraph::subgraph_from_edges(...) + plot_static_task_graph(x, vertex = vertex, edge = edge) } } + +plot_static_task_graph <- function( + g, + vertex = igraph::as_data_frame(g, what = "vertices"), + edge = igraph::as_data_frame(g, what = "edges") +) { + igraph::plot.igraph( + g, + vertex.label.family = "sans", + vertex.label.color = "gray4", + vertex.label.dist = 1, + vertex.label = vertex$label, + vertex.color = vertex$color, + vertex.size = vertex$size, + vertex.frame.color = vertex$frame.color, + vertex.frame.width = vertex$frame.width, + layout = igraph::layout_with_sugiyama( + g, + hgap = 200, + maxiter = 1000 + ) + ) +} + +plot_interactive_task_graph <- function( + g, + vertex = igraph::as_data_frame(g, what = "vertices"), + edge = igraph::as_data_frame(g, what = "edges") +) { + vertex$title <- gsub("<|>", "", vcapply(vertex$task, format)) + edge_titles <- paste0(edge$type, ifelse( + vlapply(edge$version, is.na), + "", + paste0( + "(", + ifelse(is.na(edge$op), "", paste0(edge$op, " ")), + format(edge$version), ")" + ) + )) + + if (length(edge_titles) > 0) edge$title <- edge_titles + vertex$id <- seq_len(nrow(vertex)) + edge$from <- vertex$id[match(edge$from, vertex$name)] + edge$to <- vertex$id[match(edge$to, vertex$name)] + edge$arrows <- "to" + + # visNetwork hates non-atomic vectors + vertex$task <- NULL + edge$version <- format(edge$version) + + visNetwork::visNetwork( + nodes = vertex, + edges = edge, + width = "100vw", + height = "100vh", + tooltipDelay = 0 + ) +} diff --git a/R/task_spec.R b/R/task_spec.R deleted file mode 100644 index d3f4d10..0000000 --- a/R/task_spec.R +++ /dev/null @@ -1,119 +0,0 @@ -#' Task specification -#' -#' Create task specification list which consists of all the details required -#' to run specific task. -#' -#' @param alias task alias which also serves as unique identifier of the task. -#' @param package_spec \code{\link[checked]{package_spec}} object -#' @param env environmental variables to be set in separate process running -#' specific task. -#' -#' @family tasks -#' @export -task_spec <- function( - alias = NULL, - package_spec = NULL, - env = options::opt("check_envvars")) { - structure( - list( - alias = alias, - package_spec = package_spec, - env = env - ), - class = "task_spec" - ) -} - -list_of_task_spec <- function(x, ...) { - structure(x, class = c("list_of_task_spec", "list")) -} - -#' @family tasks -#' @export -print.task_spec <- function(x, ...) { - cat(format(x, ...), "\n") -} - -#' @family tasks -#' @export -format.task_spec <- function(x, ...) { - paste0("") -} - -#' @family tasks -#' @export -format.list_of_task_spec <- function(x, ...) { - vcapply(x, format) -} - -#' Create a task to install a package and dependencies -#' -#' @param ... Additional parameters passed to [`task_spec()`] -#' @inheritParams utils::install.packages -#' -#' @family tasks -#' @export -install_task_spec <- function(type = getOption("pkgType"), INSTALL_opts = NULL, ...) { - task_spec <- task_spec(...) - install_spec <- list( - type = type, - INSTALL_opts = INSTALL_opts - ) - structure( - c(install_spec, task_spec), - class = c("install_task_spec", class(task_spec)) - ) -} - -#' Create a custom install task -#' -#' @inheritDotParams install_task_spec -#' -#' @family tasks -#' @export -custom_install_task_spec <- function(...) { - task_spec <- install_task_spec(...) - class(task_spec) <- c("custom_install_task_spec", class(task_spec)) - task_spec -} - -#' Create a task to run `R CMD check` -#' -#' @inheritParams rcmdcheck::rcmdcheck -#' @inheritDotParams task_spec -#' -#' @family tasks -#' @export -check_task_spec <- function( - args = options::opt("check_args"), - build_args = options::opt("check_build_args"), - ...) { - - task_spec <- task_spec(...) - check_spec <- list( - args = args, - build_args = build_args - ) - - structure( - c(check_spec, task_spec), - class = c("check_task_spec", class(task_spec)) - ) -} - -#' Create a task to run reverse dependency checks -#' -#' @param revdep character indicating whether the task specification describes -#' check associated with the development (new) or release (old) version of the -#' for which reverse dependency check is run. -#' @param ... Additional parameters passed to [`task_spec()`] -#' -#' @family tasks -#' @export -revdep_check_task_spec <- function(revdep, ...) { - task_spec <- check_task_spec(...) - task_spec["revdep"] <- list(revdep) - class(task_spec) <- c("revdep_check_task_spec", class(task_spec)) - - task_spec -} diff --git a/R/utils-ansi.R b/R/utils-ansi.R index 72ae670..a5e8625 100644 --- a/R/utils-ansi.R +++ b/R/utils-ansi.R @@ -19,3 +19,14 @@ ansi_line_erase <- function(n = "") { ansi_move_line_rel <- function(n) { paste0("\033[", abs(n), if (n > 0L) "F" else "E") } + +#' @describeIn ansi +#' Get the height of the ansi tty using 'tput lines' interface +ansi_tty_height <- function() { + tryCatch( + as.numeric(system("tput lines", intern = TRUE, ignore.stderr = TRUE)), + warning = function(w) { + options::opt("tty_default_height") + } + ) +} diff --git a/R/utils-available-packages.R b/R/utils-available-packages.R new file mode 100644 index 0000000..2427a27 --- /dev/null +++ b/R/utils-available-packages.R @@ -0,0 +1,48 @@ +#' Available Packages +#' +#' Functionally Equivalent to [`utils::available.packages()`], assuming +#' `utils`'s cache doesn't expire in the middle of a top-level call evaluation. +#' Modifications were made so that the results for queries with unique +#' arguments are only called once for each top-level expression. +#' +#' Though [`utils::available.packages()`] will cache the `PACKAGES` index, +#' it must still be parsed with each call. Since this can happen hundreds of +#' times when building a `R CMD check` plan, this can cause a signficiant +#' bottleneck to the responsiveness of this package. +#' +#' system.time({ for (i in 1:10) available.packages() }) +#' #> user system elapsed +#' #> 3.453 0.196 3.655 +#' +#' system.time({ for (i in 1:10) available_packages() }) +#' #> user system elapsed +#' #> 0.325 0.002 0.328 +#' +#' @note This _could_ be removed by propagating the `available.packages()` +#' database matrix through all the calls that need to use it, though this +#' would be a sizable refactor. +#' +#' @keywords internal +available_packages <- local({ + callback_name <- paste0(packageName(), "-top-level-ap") + callback_index <- 0L + + increment_index <- function(...) { + callback_index <<- callback_index + 1L + FALSE + } + + maybe_add_callback <- function() { + if (!callback_name %in% getTaskCallbackNames()) { + addTaskCallback(name = callback_name, increment_index) + } + } + + memoise::memoise( + hash = function(x) rlang::hash(list(x, callback_index)), + function(...) { + maybe_add_callback() + utils::available.packages(...) + } + ) +}) diff --git a/R/utils-cli.R b/R/utils-cli.R index 6102a57..4ca87c3 100644 --- a/R/utils-cli.R +++ b/R/utils-cli.R @@ -1,3 +1,158 @@ +#' Task formatter bindings +#' +#' This bit of code is intended for use with [`fmt()`], and allows for us +#' to layer symbol bindings on top of the environment used for string +#' interpolation which provide syntactic sugar for common formatting components. +#' +#' @param g task_graph object. +#' @param nodes graph nodes to format. +#' @param task task to format. +#' @param tasks currently unused. +#' +#' @keywords internal +task_formats <- function( + g = NULL, + nodes = V(g), + task = NULL, + tasks = list(task) +) { + if (is.null(task)) { + task <- nodes$task + } + + # # NOTE: currently unused, vestigial vectorized alternative # nolint + # if (length(tasks) == 1 && is.null(tasks[[1]])) { # nolint + # tasks <- V(g)$task # nolint + # } # nolint + + makeActiveBinding("source", env = environment(), function() { + src <- task$origin %||% character(0L) + src <- format(src) + if (!is.null(names(src))) src <- names(src) + cli_type("path", src) + }) + + makeActiveBinding("source.full", env = environment(), function() { + src <- task$origin$source %||% character(0L) + cli_type("path", format(src)) + }) + + makeActiveBinding("source.type", env = environment(), function() { + src_type <- class(task$origin)[[1]] + src_type_str <- switch(src_type, + "pkg_origin_remote" = source, + "pkg_origin_local" = "local", + "pkg_origin_repo" = source, + "remote" + ) + + cli_type("path", format(src_type_str)) + }) + + makeActiveBinding("package", env = environment(), function() { + pkg <- task$origin$package %||% character(0L) + cli_type("package", pkg) + }) + + makeActiveBinding("version", env = environment(), function() { + cli_type("version", format(task$origin$version %||% character(0L))) + }) + + makeActiveBinding("action", env = environment(), function() { + cli_type("task_type", format_task_type(task, g = g)) + }) + + makeActiveBinding("dep.sources", env = environment(), function() { + # get nodes dependencies + dep_edges <- E(g)$relation == as.numeric(RELATION$dep) + dep_g <- igraph::subgraph_from_edges(g, which(dep_edges)) + vs <- task_graph_neighborhoods(dep_g, nodes = nodes$name, mindist = 1) + vs <- unlist(vs) + + # get dependency source names + sources <- lapply(V(g)[vs]$task, function(task) format(task$origin)) + sources <- unlist(Filter(length, sources)) + + if (!is.null(names(sources))) { + is_unnamed <- names(sources) == "" + names(sources[is_unnamed]) <- sources[is_unnamed] + } + + # filter for only novel dependencies + sources <- setdiff(names(sources), names(getOption("repos"))) + cli_type("dep_sources", sources) + }) + + environment() +} + +#' Produce cli output for a task +#' +#' Provided a task, allows for use of a handful of shorthand symbols which will +#' use the task as a context for formatting task fields. +#' +#' @param ... params passed to [`cli::format_inline`]. +#' @param .envir output environment. +#' @param ansi logical whether ansi should be stripped. +#' @inheritParams task_formats +#' +#' @examples +#' task <- install_task(origin = pkg_origin( +#' package = "pkg", +#' version = package_version("1.2.3"), +#' source = system.file( +#' "example_packages", +#' "exampleGood", +#' package = "checked" +#' ) +#' )) +#' +#' # Examples for unexported functions are not supported +#' # fmt(task = task, "{action} {package} ({version}) from {source}") +#' +#' @keywords internal +fmt <- function( + ..., + g, + nodes, + task = NULL, + .envir = parent.frame(), + ansi = TRUE +) { + env <- task_formats(g = g, nodes = nodes, task = task, tasks = NULL) + parent.env(env) <- .envir + + cli::cli_div( + theme = list( + div = list( + "class-map" = list( + "cli_path" = "path", + "cli_task_type" = "class", + "cli_package" = "pkg", + "cli_version" = "pkg-version" + ) + ), + ".pkg-version" = list( + "font-style" = "italic", + "transform" = function(x) sprintf("v%s", x) + ) + ) + ) + + out <- cli::format_inline(..., .envir = env) + + # ideally would use `options(cli.ansi)`, but it seems to be unused + if (!ansi) out <- cli::ansi_strip(out) + + out +} + +glu <- function(..., g, nodes, task = NULL, .envir = parent.frame()) { + env <- task_formats(g = g, nodes = nodes, task = task, tasks = NULL) + parent.env(env) <- .envir + glue::glue(..., .envir = env) +} + #' Create a 'cli' Spinner With Suppressed Output #' #' 'cli' will implicitly push spinner output to various output streams, @@ -42,3 +197,12 @@ str_pad <- function(x, n) { x <- format(x) paste0(strrep(" ", n - nchar(x)), x) } + +emoji <- list( + dev = "\U0001F6A7", + release = "\U0001F680" +) + +cli_env_has_pb <- function(env) { + !is.null(attr(env, "withr_handlers")) +} diff --git a/R/utils-deps.R b/R/utils-deps.R new file mode 100644 index 0000000..3122dcd --- /dev/null +++ b/R/utils-deps.R @@ -0,0 +1,150 @@ +#' Convert a value to a set of dependency types +#' +#' Implements conventions established by both [`tools::package_dependencies()`] +#' and [`pkgdepends::as_pkg_dependencies()`], following +#' [`pkgdepends::as_pkg_dependencies()`] return type structure of a list +#' including `$direct` and `$indirect` dependency types. Reimplemented to +#' avoid dependence on `pkgdepends` compilation requirements. +#' +#' @param x A `logical` scalar, `character` string of `"all"`, `"most"`, +#' `"hard"` or `"soft"`, `NA` or a vector of dependency types. +#' +#' @note locally defined and bespoke dispatch system to avoid registering +#' methods when loaded in combination with `pkgdepends` +#' +#' @keywords internal +as_pkg_dependencies <- function(x) { + into_deptype_vec <- function(x) { + if (is.na(x)) { + x <- "hard" + } else if (identical(x, TRUE)) { + x <- "most" + } else if (identical(x, FALSE)) { + x <- character(0L) + } + + deptypes <- c("Depends", "Imports", "LinkingTo", "Suggests", "Enhances") + if (is.character(x)) { + x <- switch(x, + "all" = deptypes, + "most" = c("Depends", "Imports", "LinkingTo", "Suggests"), + "hard" = c("Depends", "Imports", "LinkingTo"), + "soft" = c("Suggests", "Enhances"), + x + ) + } + + stopifnot(all(x %in% deptypes)) + + x + } + + into_deptype_list <- function(direct = "all", indirect = "hard") { + list( + direct = into_deptype_vec(direct), + indirect = into_deptype_vec(indirect) + ) + } + + if (!is.list(x)) x <- list(x) + do.call(into_deptype_list, x) +} + +#' Build Package Dependencies Table +#' +#' Inspired by `tools::package_dependencies`, but with the added benefit +#' of recording the dependency type and relationships throughout the +#' dependency tree. +#' +#' @inheritParams tools::package_dependencies +#' @param dependencies A `logical` scalar, `character` string of +#' `"all"`, `"most"`, `"hard"` or `"soft"`, `NA` or a vector of dependency +#' types compatible with [`as_pkg_dependencies()`] function. +#' +#' @keywords internal +pkg_dependencies <- function( + packages, + dependencies = TRUE, + db = available_packages(), + verbose = FALSE +) { + dependencies <- as_pkg_dependencies(dependencies) + + na_version <- package_version(NA_character_, strict = FALSE) + proto_df <- data.frame( + package = character(0L), + type = character(0L), + name = character(0L), + op = character(0L), + version = na_version[c()] + ) + + depth <- 0L + out <- list(list(proto_df)) + while (length(packages) > 0L) { + depth <- depth + 1L + deptypes <- if (depth == 1L) dependencies$direct else dependencies$indirect + # db does not need to have all the dependencies types. + deptypes <- intersect(colnames(db), deptypes) + depstrs <- safe_db_subset(db, packages, deptypes, drop = FALSE) + + n <- length(out) + 1 + out[[n]] <- Map( + package = rep(packages, times = length(deptypes)), + depstr = as.vector(depstrs), + deptype = rep(deptypes, each = length(packages)), + f = function(package, depstr, deptype) { + if (is.na(depstr)) return() + deps <- .tools$.split_dependencies(depstr) + out <- proto_df[seq_along(deps), , drop = FALSE] + + out$package <- package + out$type <- DEP[deptype] + out$name <- vcapply(deps, `[[`, "name") + out$op <- vcapply(deps, function(i) i$op %||% NA_character_) + out$version <- mapply( + function(i) i$version %||% na_version, + deps, + USE.NAMES = FALSE, + SIMPLIFY = FALSE + ) + + # TODO: Although CRAN does not seem to officially support < and <= + # TODO: requirements for hard dependencies, as of 01.07.2025 + # TODO: there is a singe package MatrixModels which specifies it. + # TODO: For now we decided to skip such requirements to assess + # TODO: the severity of this. + out <- out[!out$op %in% c("<", "<="), ] + rownames(out) <- paste0(out$package, "-", out$name) + out + } + ) + + old_deps <- unlist(lapply(out, names)) + new_deps <- unlist(lapply(out[[n]], `[[`, "name")) + packages <- setdiff(new_deps, c(old_deps, base_pkgs())) + } + + out <- do.call(rbind, unlist(out, recursive = FALSE)) + rownames(out) <- NULL + out +} + +safe_db_subset <- function(db, r, c, drop = FALSE) { + if (is.character(r) && !all(r %in% rownames(db))) { + missing <- r[!r %in% rownames(db)] + missing <- matrix( + NA, + nrow = length(missing), + ncol = NCOL(db), + dimnames = list( + missing, + colnames(db) + ) + ) + db <- rbind(db, missing) + } + + c <- intersect(colnames(db), c) + db[r, c, drop = drop] +} diff --git a/R/utils-enums.R b/R/utils-enums.R index 80f33ed..0852eee 100644 --- a/R/utils-enums.R +++ b/R/utils-enums.R @@ -7,16 +7,15 @@ enum <- function(...) { x <- c(...) f <- factor(x, levels = x) - structure( - lapply(f, identity), - names = levels(f) - ) + names(f) <- as.character(f) + structure(f, class = c("enum", "factor")) } #' Internally provide extended mathematical operators for enums #' @noRd +#' @export #' @keywords internal -Ops.factor <- function(e1, e2) { +Ops.enum <- function(e1, e2) { # nolint start, styler: off switch(.Generic, ">" = , ">=" = , "==" = , "<" = , "<=" = { return(do.call(.Generic, list(as.numeric(e1), as.numeric(e2)))) @@ -25,9 +24,27 @@ Ops.factor <- function(e1, e2) { NextMethod() } +#' Internally provide `$` for enum factors +#' @noRd +#' @keywords internal +#' @export +`$.enum` <- function(x, i) { + x[[i]] +} + +#' Relation types +#' +#' A flag for edges that articulate different relations between nodes. +#' +#' @keywords internal +RELATION <- enum( + "dep", # for task dependencies + "report" # for reporting graph, from task to reporting node +) + #' Check execution status categories #' @keywords internal -STATUS <- enum( # nolint +STATUS <- enum( "pending", "ready", "in progress", @@ -36,7 +53,7 @@ STATUS <- enum( # nolint #' Dependencies categories #' @keywords internal -DEP <- enum( # nolint +DEP <- enum( "Imports", "Depends", "LinkingTo", @@ -46,11 +63,11 @@ DEP <- enum( # nolint #' Strong dependencies categories #' @keywords internal -DEP_STRONG <- unlist(DEP[1:3]) # nolint +DEP_STRONG <- unlist(DEP[1:3]) #' Available packages database dependencies columns #' @keywords internal -DB_COLNAMES <- c( # nolint +DB_COLNAMES <- c( "Package", "Depends", "Imports", diff --git a/R/utils-igraph.R b/R/utils-igraph.R new file mode 100644 index 0000000..3b74be3 --- /dev/null +++ b/R/utils-igraph.R @@ -0,0 +1,91 @@ +vertex_df <- function(name, ...) { + dots <- list(...) + vertices <- data.frame(name = name) + vertices[names(dots)] <- dots + vertices[] <- lapply(vertices, `names<-`, NULL) + vertices +} + +new_graph <- function( + ..., + vertex_attrs = list(...), + edge_attrs = list(), + name_by = vertex_attrs[[1]], + name = vcapply(name_by, as_vertex_name), + type = c("sequence", "star") +) { + type <- match.arg(type) + vertices <- vertex_df(name = name, ...) + edges_orientation <- switch( + type, + sequence = -1L, + star = 1 + ) + edges <- data.frame( + from = utils::head(vertices$name, edges_orientation), + to = utils::tail(vertices$name, -1L) + ) + if (length(edge_attrs) > 0) { + edges <- cbind(edges, edge_attrs) + } + igraph::graph_from_data_frame(edges, vertices = vertices) +} + +sequence_graph <- function(...) { + new_graph(..., type = "sequence") +} + +star_graph <- function(...) { + new_graph(..., type = "star") +} + +#' Deduplicate attributes +#' +#' Primarily intended for cleaning up the result of an [`igraph::union()`], +#' which adds duplicated attributes when attributes of the same name exist in +#' multiple graphs. Searches for suffixes and consolidates attributes, +#' taking the attribute from the first non-NA value observed. +#' +#' @param g task_graph object +#' +#' @keywords internal +graph_dedup_attrs <- function(g) { + # pattern appended to duplicated attributes + re <- "_\\d+$" + + # de-duplicate vertex attributes + v_attrs <- igraph::vertex_attr_names(g) + v_dup_attrs <- grep(re, v_attrs, value = TRUE) + v_dup_group <- sub(re, "", v_dup_attrs) + v_dup_attrs <- split(v_dup_attrs, v_dup_group) + for (i in seq_along(v_dup_attrs)) { + attr_name <- names(v_dup_attrs[i]) + attr_value <- igraph::vertex_attr(g, v_dup_attrs[[i]][[1L]]) + g <- igraph::delete_vertex_attr(g, v_dup_attrs[[i]][[1L]]) + for (attr_dup_name in v_dup_attrs[[i]][-1L]) { + is_na <- is.na(attr_value) + attr_value[is_na] <- igraph::vertex_attr(g, attr_dup_name)[is_na] + g <- igraph::delete_vertex_attr(g, attr_dup_name) + } + g <- igraph::set_vertex_attr(g, attr_name, value = attr_value) + } + + # de-duplicate edge attributes + e_attrs <- igraph::edge_attr_names(g) + e_dup_attrs <- grep(re, e_attrs, value = TRUE) + e_dup_group <- sub(re, "", e_dup_attrs) + e_dup_attrs <- split(e_dup_attrs, e_dup_group) + for (i in seq_along(e_dup_attrs)) { + attr_name <- names(e_dup_attrs[i]) + attr_value <- igraph::edge_attr(g, e_dup_attrs[[i]][[1L]]) + g <- igraph::delete_edge_attr(g, e_dup_attrs[[i]][[1L]]) + for (attr_dup_name in e_dup_attrs[[i]][-1L]) { + is_na <- is.na(attr_value) + attr_value[is_na] <- igraph::edge_attr(g, attr_dup_name)[is_na] + g <- igraph::delete_edge_attr(g, attr_dup_name) + } + g <- igraph::set_edge_attr(g, attr_name, value = attr_value) + } + + g +} diff --git a/R/utils-paths.R b/R/utils-paths.R index ac54073..1ff40a1 100644 --- a/R/utils-paths.R +++ b/R/utils-paths.R @@ -1,3 +1,58 @@ +filepath <- function(x) { + structure(x, class = "filepath") +} + +cli_type <- function(types, x) { + structure(x, class = paste0("cli_", types)) +} + +format_simplify_path <- function(x, ..., full.path = FALSE) { + if (full.path) { + return(normalizePath(x, mustWork = FALSE)) + } + + wp <- path_parts(getwd()) + xp <- path_parts(normalizePath(x, mustWork = FALSE)) + min_len <- min(length(wp), length(xp)) + first_diff <- Position( + identity, + utils::head(wp, min_len) != utils::head(xp, min_len) + ) + + if (is.na(first_diff)) { + parts <- utils::tail(xp, -min_len) + if (length(parts) == 0) { + parts <- "." + } + do.call(file.path, as.list(parts)) + } else if (first_diff > min_len) { + parts <- utils::tail(xp, -first_diff + 1) + do.call(file.path, as.list(parts)) + } else if (first_diff <= min_len) { + parents <- rep_len("..", length(xp) - first_diff + 1) + parts <- c(parents, utils::tail(xp, -first_diff + 1)) + do.call(file.path, as.list(parts)) + } else { + x + } +} + +#' Split a Filepath into Parts +#' +#' @param x A `character(1L)` or `filepath` +#' @return A `character` vector of path parts +#' +#' @keywords internal +path_parts <- function(x) { + parts <- character() + repeat { + parts[[length(parts) + 1L]] <- basename(x) + if (x == dirname(x)) break + x <- dirname(x) + } + rev(parts) +} + path_default <- function() { file.path(tempdir(), utils::packageName()) } @@ -7,18 +62,12 @@ path_libs <- function(path) { normalizePath(p) } -path_lib <- function(path) { - dir_create(p <- file.path(path_libs(path), "lib")) - normalizePath(p) -} - -path_custom_lib <- function(path, custom) { - valid_name <- hash_alias(custom) - dir_create(p <- file.path(path_libs(path), valid_name)) +path_checker_lib <- function(path) { + dir_create(p <- file.path(path_libs(path), "checker_lib")) normalizePath(p) } -path_package_install_log <- function(path, package, name = "lib") { +path_install_log <- function(path, package, name = "lib") { dir_create(p <- file.path(path_logs(path), name)) normalizePath(file.path(p, sprintf("%s.log", package)), mustWork = FALSE) } @@ -33,6 +82,11 @@ path_sources <- function() { normalizePath(p) } +path_remotes <- function() { + dir_create(p <- file.path(tempdir(), "checked_remotes")) + normalizePath(p) +} + path_check_output <- function(path, check) { dir_create(p <- file.path(path, "checks")) normalizePath(file.path(p, check), mustWork = FALSE) diff --git a/R/utils-pkg-source.R b/R/utils-pkg-source.R index b93b769..48719d5 100644 --- a/R/utils-pkg-source.R +++ b/R/utils-pkg-source.R @@ -1,19 +1,5 @@ -split_packages_names <- function(x) { - if (is.na(x)) { - data.frame( - dep = character(0), - op = character(0), - version = character(0) - ) - } else { - drlapply(.tools$.split_dependencies(x), function(x){ - data.frame( - dep = x$name, - op = x$op %||% ">", - version = x$version %||% numeric_version("0") - ) - }) - } +strip_src_contrib <- function(x) { + sub("/src/contrib$", "", x) } check_dependencies <- function(dependencies) { @@ -71,7 +57,7 @@ fetch_package_source <- function(archive_url, destdir) { get_package_source <- function(package, repos, db = NULL, destdir = NULL) { if (is.null(db)) { - db <- utils::available.packages(repos = repos) + db <- available_packages(repos = repos) } pkg <- db[package, ] archive_url <- sprintf( @@ -87,11 +73,3 @@ get_package_source <- function(package, repos, db = NULL, destdir = NULL) { archive_url } } - -package_deps <- function(packages = NULL, ...) { - if (length(packages) == 0) { - NULL - } else { - tools::package_dependencies(packages = packages, ...) - } -} diff --git a/R/utils-remotes.R b/R/utils-remotes.R new file mode 100644 index 0000000..f66ace6 --- /dev/null +++ b/R/utils-remotes.R @@ -0,0 +1,34 @@ +#' A simple alternative to `devtools::as.package` +#' +#' Functionally identical to `devtools`' `as.package`, but without interactive +#' options for package creation. +#' +#' Function required for communicating with the `remotes` package interface +#' +#' @note Code inspired by `devtools` `load_pkg_description` with very minor +#' edits to further reduce `devtools` dependencies. +#' +#' @param x A package object to coerce +#' @keywords internal +as.package.remotes <- function(x) { + if (inherits(x, "package")) { + return(x) + } + info <- read.dcf(file.path(x, "DESCRIPTION"))[1L, ] + Encoding(info) <- "UTF-8" + desc <- as.list(info) + names(desc) <- tolower(names(desc)) + desc$path <- x + structure(desc, class = "package") +} + + +.remotes <- function() { + if (requireNamespace("remotes", quietly = TRUE)) { + as.list(getNamespace("remotes"), all.names = TRUE)[c( + "extra_deps" + )] + } else { + "remotes package not available" + } +} diff --git a/R/utils.R b/R/utils.R index 84fcfae..9f45281 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,10 +1,48 @@ #' @import cli NULL +as_vertex_name <- function(x, ...) { + UseMethod("as_vertex_name") +} + +#' @export +as_vertex_name.default <- function(x, ...) { + hash(x, ...) +} + +#' @export +as_vertex_name.task <- function(x, ...) { + paste0( + hash(x, ...), + gsub("\\s+", "-", fmt(task = x, "-{action}-{package}", ansi = FALSE)) + ) +} + +#' @export +as_vertex_name.local_check_meta_task <- function(x, ...) { + paste0( + hash(x, ...), + gsub("\\s+", "-", fmt(task = x, "-{action}", ansi = FALSE)) + ) +} + +hash <- function(x, n = 12) { + substring(cli::hash_obj_sha256(x), 1, n) +} + +hashes <- function(x, ...) { + vcapply(x, hash, ...) +} + base_pkgs <- function() { c("R", utils::installed.packages(priority = "base")[, "Package"]) } +is_package_installed <- function(pkg, lib.loc = .libPaths()) { + path <- find.package(pkg, lib.loc = lib.loc, quiet = TRUE) + length(path) > 0 +} + .callr <- as.list(getNamespace("callr"), all.names = TRUE)[c( "default_load_hook" )] @@ -13,21 +51,6 @@ base_pkgs <- function() { ".split_dependencies" )] -replace_with_map <- function(x, value, replacement) { - m <- match(x, value) - x[which(!is.na(m))] <- replacement[m[!is.na(m)]] - x -} - -is_package_installed <- function(pkg, lib.loc) { # nolint object_name_linter - path <- find.package(pkg, lib.loc = lib.loc, quiet = TRUE) - length(path) > 0 -} - -hash_alias <- function(x) { - paste0(c("hash", as.character(charToRaw(x))), collapse = "") -} - dir_create <- function(path) { if (!dir.exists(path)) { dir.create(path, showWarnings = FALSE, recursive = TRUE) @@ -36,16 +59,18 @@ dir_create <- function(path) { `%||%` <- function(lhs, rhs) if (is.null(lhs)) rhs else lhs -drlapply <- function(...) { - do.call(rbind, lapply(...)) -} - -drmapply <- function(...) { - do.call(rbind, mapply(..., USE.NAMES = FALSE, SIMPLIFY = FALSE)) -} - -uulist <- function(...) unique(as.character(unlist(...))) vcapply <- function(...) vapply(..., FUN.VALUE = character(1L)) vlapply <- function(...) vapply(..., FUN.VALUE = logical(1L)) viapply <- function(...) vapply(..., FUN.VALUE = integer(1L)) vnapply <- function(...) vapply(..., FUN.VALUE = numeric(1L)) + +suppressWarningsRegex <- function(expr, regex, ...) { + withCallingHandlers( + expr, + warning = function(w) { + if (grepl(pattern = regex, x = conditionMessage(w), ...)) { + invokeRestart("muffleWarning") + } + } + ) +} diff --git a/README.Rmd b/README.Rmd index 4b3eb0c..276ceea 100644 --- a/README.Rmd +++ b/README.Rmd @@ -6,6 +6,12 @@ always_allow_html: yes +```{sh clone-dev-praise, echo = FALSE, results = "hide"} +# praise chosen for having few direct and reverse dependencies +rm -rf /tmp/praise +git clone https://github.com/gaborcsardi/praise /tmp/praise +``` + ```{r, include = FALSE, cache = FALSE} knitr::opts_chunk$set( collapse = TRUE, @@ -22,8 +28,17 @@ knitr::opts_chunk$set( asciicast::init_knitr_engine( startup = bquote({ options(repos = .(getOption("repos"))) + plan <- checked::plan_rev_dep_checks("/tmp/praise") + design <- checked::checker$new( + plan, + n = 10, + lib.loc = .libPaths(), + restore = FALSE + ) }), - echo_input = FALSE + echo_input = FALSE, + timeout = 300, + interactive = FALSE ) ``` @@ -35,12 +50,6 @@ asciicast::init_knitr_engine( [![coverage](https://codecov.io/gh/Genentech/checked/branch/main/graph/badge.svg)](https://app.codecov.io/gh/Genentech/checked/tree/main) -```{sh clone-dev-praise, echo = FALSE, results = "hide"} -# praise chosen for having few direct and reverse dependencies -rm -rf /tmp/praise -git clone https://github.com/gaborcsardi/praise /tmp/praise -``` - # Running Checks Although `checked` is broadly capable of running arbitrary sets of `R CMD check` @@ -51,7 +60,7 @@ Running reverse dependency checks is as easy as ```r library(checked) -x <- run("/home/dev/praise") +x <- run("/home/dev/praise", n = 4) results(x) ``` ```{r, echo = FALSE} @@ -77,10 +86,12 @@ run("/home/dev/praise") ``` ```{asciicast ansi-tty-example, cache = FALSE} #| asciicast_knitr_output = "svg", -#| asciicast_at = "all", #| asciicast_cursor = FALSE, -#| asciicast_speed = 2 -options(checked.tty_tick_interval = 200) -checked::run("/tmp/praise") +#| asciicast_speed = 2, +#| asciicast_timeout = 300, +#| asciicast_at = "all", +#| asciicast_idle_time_limit = 2 +options(checked.tty_tick_interval = 0.01) +checked::run(design) cat() ``` diff --git a/README.md b/README.md index 87a9cf6..5d44fec 100644 --- a/README.md +++ b/README.md @@ -14,26 +14,26 @@ checked # Running Checks -Although `checked` is broadly capable of running arbitrary sets of `R -CMD check` tasks, reverse dependency checking is one of the most common -use cases where batch `R CMD check`s are needed. +Although `checked` is broadly capable of running arbitrary sets of +`R CMD check` tasks, reverse dependency checking is one of the most +common use cases where batch `R CMD check`s are needed. Running reverse dependency checks is as easy as ``` r library(checked) -x <- run("/home/dev/praise") +x <- run("/home/dev/praise", n = 4) results(x) ``` - #> # Revdep Check Task Spec + #> # praise reverse dependency check results (1c999505a831-meta-revdeps-of-praise) #> - #> goodpractice package R CMD check diff + #> testthat package R CMD check diff #> notes: OK #> warnings: OK #> errors: OK #> - #> testthat package R CMD check diff + #> goodpractice package R CMD check diff #> notes: OK #> warnings: OK #> errors: OK @@ -57,5 +57,5 @@ run("/home/dev/praise") - + diff --git a/_pkgdown.yml b/_pkgdown.yml index 0dd304e..6ca5575 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -9,9 +9,13 @@ reference: contents: - run - - title: Designing Check Sets + - title: Designing and Runing Check Sets contents: - has_concept("checks") + + - title: Plan checks + contents: + - has_concept("plan") - title: Building Tasks contents: @@ -28,7 +32,7 @@ reference: - title: Reporters contents: - has_concept("reporters") - + - title: Documentation contents: - has_concept("documentation") diff --git a/cran-comments.md b/cran-comments.md new file mode 100644 index 0000000..c1f7393 --- /dev/null +++ b/cran-comments.md @@ -0,0 +1,3 @@ +The package fixes CRAN checks results failures while also preforming a full +refactor of the logic behind it. Changes are fully tetsed and backwards compatibility +was applied whenever possible. \ No newline at end of file diff --git a/inst/example_packages/exampleGood/DESCRIPTION b/inst/example_packages/exampleGood/DESCRIPTION index dce9fc7..c9b9d5c 100644 --- a/inst/example_packages/exampleGood/DESCRIPTION +++ b/inst/example_packages/exampleGood/DESCRIPTION @@ -10,7 +10,7 @@ Authors@R: Description: What the package does (one paragraph). Imports: methods, - DBI + R6 License: file LICENSE Encoding: UTF-8 LazyData: true diff --git a/inst/example_packages/exampleGood/R/reexport.R b/inst/example_packages/exampleGood/R/reexport.R index d124949..8b3fd37 100644 --- a/inst/example_packages/exampleGood/R/reexport.R +++ b/inst/example_packages/exampleGood/R/reexport.R @@ -1,5 +1,6 @@ #' Reexport utils::help #' #' @importFrom utils help +#' @inheritParams utils::help #' @export reexport_help <- utils::help diff --git a/inst/example_packages/exampleGood/man/reexport_help.Rd b/inst/example_packages/exampleGood/man/reexport_help.Rd index 3607d44..8329f07 100644 --- a/inst/example_packages/exampleGood/man/reexport_help.Rd +++ b/inst/example_packages/exampleGood/man/reexport_help.Rd @@ -13,6 +13,38 @@ reexport_help( help_type = getOption("help_type") ) } +\arguments{ +\item{topic}{usually, a \link{name} or character string specifying the + topic for which help is sought. A character string (enclosed in + explicit single or double quotes) is always taken as naming a topic. + + If the value of \code{topic} is a length-one + character vector the topic is taken to be the value of the only + element. Otherwise \code{topic} must be a name or a \link{reserved} + word (if syntactically valid) or character string. + + See \sQuote{Details} for what happens if this is omitted. + } + +\item{package}{a name or character vector giving the packages to look + into for documentation, or \code{NULL}. By default, all packages + whose namespaces are loaded are used. To avoid a name being deparsed use e.g. + \code{(pkg_ref)} (see the examples).} + +\item{lib.loc}{a character vector of directory names of \R libraries, + or \code{NULL}. The default value of \code{NULL} corresponds to all + libraries currently known. If the default is used, the loaded + packages are searched before the libraries. This is not used for + HTML help (see \sQuote{Details}).} + +\item{verbose}{logical; if \code{TRUE}, the file name is reported.} + +\item{try.all.packages}{logical; see \code{Note}.} + +\item{help_type}{character string: the type of help required. + Possible values are \code{"text"}, \code{"html"} and \code{"pdf"}. + Case is ignored, and partial matching is allowed.} +} \description{ Reexport utils::help } diff --git a/man/DEP.Rd b/man/DEP.Rd index 34df58c..c21dfd5 100644 --- a/man/DEP.Rd +++ b/man/DEP.Rd @@ -5,7 +5,7 @@ \alias{DEP} \title{Dependencies categories} \format{ -An object of class \code{list} of length 5. +An object of class \code{enum} (inherits from \code{factor}) of length 5. } \usage{ DEP diff --git a/man/DEP_STRONG.Rd b/man/DEP_STRONG.Rd index 1ff19ed..bb530c0 100644 --- a/man/DEP_STRONG.Rd +++ b/man/DEP_STRONG.Rd @@ -5,7 +5,7 @@ \alias{DEP_STRONG} \title{Strong dependencies categories} \format{ -An object of class \code{factor} of length 3. +An object of class \code{enum} (inherits from \code{factor}) of length 3. } \usage{ DEP_STRONG diff --git a/man/RELATION.Rd b/man/RELATION.Rd new file mode 100644 index 0000000..95d510e --- /dev/null +++ b/man/RELATION.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils-enums.R +\docType{data} +\name{RELATION} +\alias{RELATION} +\title{Relation types} +\format{ +An object of class \code{enum} (inherits from \code{factor}) of length 2. +} +\usage{ +RELATION +} +\description{ +A flag for edges that articulate different relations between nodes. +} +\keyword{internal} diff --git a/man/STATUS.Rd b/man/STATUS.Rd index ab5ca98..c723d11 100644 --- a/man/STATUS.Rd +++ b/man/STATUS.Rd @@ -5,7 +5,7 @@ \alias{STATUS} \title{Check execution status categories} \format{ -An object of class \code{list} of length 4. +An object of class \code{enum} (inherits from \code{factor}) of length 4. } \usage{ STATUS diff --git a/man/ansi.Rd b/man/ansi.Rd index eaa6993..d12059c 100644 --- a/man/ansi.Rd +++ b/man/ansi.Rd @@ -4,11 +4,14 @@ \alias{ansi} \alias{ansi_line_erase} \alias{ansi_move_line_rel} +\alias{ansi_tty_height} \title{Various utilities for formatting ANSI output} \usage{ ansi_line_erase(n = "") ansi_move_line_rel(n) + +ansi_tty_height() } \arguments{ \item{n}{The number of lines to move. Positive is up, negative is down.} @@ -22,5 +25,7 @@ Various utilities for formatting ANSI output \item \code{ansi_move_line_rel()}: Offset the cursor by a relative number of lines +\item \code{ansi_tty_height()}: Get the height of the ansi tty using 'tput lines' interface + }} \keyword{internal} diff --git a/man/as.package.remotes.Rd b/man/as.package.remotes.Rd new file mode 100644 index 0000000..094994f --- /dev/null +++ b/man/as.package.remotes.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils-remotes.R +\name{as.package.remotes} +\alias{as.package.remotes} +\title{A simple alternative to \code{devtools::as.package}} +\usage{ +as.package.remotes(x) +} +\arguments{ +\item{x}{A package object to coerce} +} +\description{ +Functionally identical to \code{devtools}' \code{as.package}, but without interactive +options for package creation. +} +\details{ +Function required for communicating with the \code{remotes} package interface +} +\note{ +Code inspired by \code{devtools} \code{load_pkg_description} with very minor +edits to further reduce \code{devtools} dependencies. +} +\keyword{internal} diff --git a/man/as_pkg_dependencies.Rd b/man/as_pkg_dependencies.Rd new file mode 100644 index 0000000..ebd4f13 --- /dev/null +++ b/man/as_pkg_dependencies.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils-deps.R +\name{as_pkg_dependencies} +\alias{as_pkg_dependencies} +\title{Convert a value to a set of dependency types} +\usage{ +as_pkg_dependencies(x) +} +\arguments{ +\item{x}{A \code{logical} scalar, \code{character} string of \code{"all"}, \code{"most"}, +\code{"hard"} or \code{"soft"}, \code{NA} or a vector of dependency types.} +} +\description{ +Implements conventions established by both \code{\link[tools:package_dependencies]{tools::package_dependencies()}} +and \code{\link[pkgdepends:as_pkg_dependencies]{pkgdepends::as_pkg_dependencies()}}, following +\code{\link[pkgdepends:as_pkg_dependencies]{pkgdepends::as_pkg_dependencies()}} return type structure of a list +including \verb{$direct} and \verb{$indirect} dependency types. Reimplemented to +avoid dependence on \code{pkgdepends} compilation requirements. +} +\note{ +locally defined and bespoke dispatch system to avoid registering +methods when loaded in combination with \code{pkgdepends} +} +\keyword{internal} diff --git a/man/available_packages.Rd b/man/available_packages.Rd new file mode 100644 index 0000000..c0e619c --- /dev/null +++ b/man/available_packages.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils-available-packages.R +\name{available_packages} +\alias{available_packages} +\title{Available Packages} +\usage{ +available_packages(...) +} +\description{ +Functionally Equivalent to \code{\link[utils:available.packages]{utils::available.packages()}}, assuming +\code{utils}'s cache doesn't expire in the middle of a top-level call evaluation. +Modifications were made so that the results for queries with unique +arguments are only called once for each top-level expression. +} +\details{ +Though \code{\link[utils:available.packages]{utils::available.packages()}} will cache the \code{PACKAGES} index, +it must still be parsed with each call. Since this can happen hundreds of +times when building a \verb{R CMD check} plan, this can cause a signficiant +bottleneck to the responsiveness of this package. + +\if{html}{\out{
}}\preformatted{system.time(\{ for (i in 1:10) available.packages() \}) +#> user system elapsed +#> 3.453 0.196 3.655 + +system.time(\{ for (i in 1:10) available_packages() \}) +#> user system elapsed +#> 0.325 0.002 0.328 +}\if{html}{\out{
}} +} +\note{ +This \emph{could} be removed by propagating the \code{available.packages()} +database matrix through all the calls that need to use it, though this +would be a sizable refactor. +} +\keyword{internal} diff --git a/man/check_dev_rev_deps.Rd b/man/check_dev_rev_deps.Rd deleted file mode 100644 index 8694405..0000000 --- a/man/check_dev_rev_deps.Rd +++ /dev/null @@ -1,62 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/check.R -\name{check_dev_rev_deps} -\alias{check_dev_rev_deps} -\title{Run reverse dependency checks against a development version only} -\usage{ -check_dev_rev_deps( - path, - n = 2L, - output = tempfile(paste(utils::packageName(), Sys.Date(), sep = "-")), - lib.loc = .libPaths(), - repos = getOption("repos"), - restore = options::opt("restore"), - reporter = reporter_default(), - ... -) -} -\arguments{ -\item{path}{file path to the package source directory} - -\item{n}{\code{integer} value indicating maximum number of subprocesses that can -be simultaneously spawned when executing tasks.} - -\item{output}{\code{character} value specifying path where the output should be -stored.} - -\item{lib.loc}{\code{character} vector with libraries allowed to be used when -checking packages, defaults to entire \code{\link[=.libPaths]{.libPaths()}}.} - -\item{repos}{\code{character} vector of repositories which will be used when -generating task graph and later pulling dependencies.} - -\item{restore}{\code{logical} indicating whether output directory should be unlinked before -running checks. If \code{FALSE}, an attempt will me made to restore previous -progress from the same \code{output} (Defaults to \code{NA}, overwritable using option 'checked.restore' or environment variable 'R_CHECKED_RESTORE')} - -\item{reporter}{A reporter to provide progress updates. Will default to the -most expressive command-line reporter given your terminal capabilities.} - -\item{...}{Additional arguments passed to \code{\link{checked-task-df}} and \code{\link[=run]{run()}}} -} -\value{ -\code{\link[=check_design]{check_design()}} R6 class storing all the details -regarding checks that run. Can be combined with -\code{\link{results}} and \code{\link[=summary]{summary()}} methods to generate results. -} -\description{ -\code{\link[=check_dev_rev_deps]{check_dev_rev_deps()}} works similarly to \code{\link[=check_rev_deps]{check_rev_deps()}} but it runs -R CMD check only once for each package, with the development version of the -package installed. It is advantageous to check whether adding a new package -into a repository breaks existing packages that possibly take said package -as a \code{Suggests} dependency. -} -\seealso{ -Other checks: -\code{\link{check_design}}, -\code{\link{check_dir}()}, -\code{\link{check_pkgs}()}, -\code{\link{check_rev_deps}()}, -\code{\link{new_check_design}()} -} -\concept{checks} diff --git a/man/check_dir.Rd b/man/check_dir.Rd deleted file mode 100644 index f5a18ef..0000000 --- a/man/check_dir.Rd +++ /dev/null @@ -1,59 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/check.R -\name{check_dir} -\alias{check_dir} -\title{Check all package source directories in current directory} -\usage{ -check_dir( - path, - n = 2L, - output = tempfile(paste(utils::packageName(), Sys.Date(), sep = "-")), - lib.loc = .libPaths(), - repos = getOption("repos"), - restore = options::opt("restore"), - reporter = reporter_default(), - ... -) -} -\arguments{ -\item{path}{file path to the package source directory} - -\item{n}{\code{integer} value indicating maximum number of subprocesses that can -be simultaneously spawned when executing tasks.} - -\item{output}{\code{character} value specifying path where the output should be -stored.} - -\item{lib.loc}{\code{character} vector with libraries allowed to be used when -checking packages, defaults to entire \code{\link[=.libPaths]{.libPaths()}}.} - -\item{repos}{\code{character} vector of repositories which will be used when -generating task graph and later pulling dependencies.} - -\item{restore}{\code{logical} indicating whether output directory should be unlinked before -running checks. If \code{FALSE}, an attempt will me made to restore previous -progress from the same \code{output} (Defaults to \code{NA}, overwritable using option 'checked.restore' or environment variable 'R_CHECKED_RESTORE')} - -\item{reporter}{A reporter to provide progress updates. Will default to the -most expressive command-line reporter given your terminal capabilities.} - -\item{...}{Additional arguments passed to \code{\link{checked-task-df}} and \code{\link[=run]{run()}}} -} -\value{ -\code{\link[=check_design]{check_design()}} R6 class storing all the details -regarding checks that run. Can be combined with -\code{\link{results}} and \code{\link[=summary]{summary()}} methods to generate results. -} -\description{ -\code{\link[=check_dir]{check_dir()}} Identifies all R packages in the given directory -(non-recursively) and passes them to the \code{\link[=check_pkgs]{check_pkgs()}} -} -\seealso{ -Other checks: -\code{\link{check_design}}, -\code{\link{check_dev_rev_deps}()}, -\code{\link{check_pkgs}()}, -\code{\link{check_rev_deps}()}, -\code{\link{new_check_design}()} -} -\concept{checks} diff --git a/man/check_functions.Rd b/man/check_functions.Rd index 2deee21..2ad0b62 100644 --- a/man/check_functions.Rd +++ b/man/check_functions.Rd @@ -23,10 +23,14 @@ to pull sources for reverse dependencies. In some cases, for instance using binaries on Linux, we want to use different repositories when pulling sources to check and different when installing dependencies.} -\item{...}{Additional arguments passed to \code{\link{checked-task-df}} and \code{\link[=run]{run()}}} +\item{restore}{\code{logical} indicating whether output directory should be +unlinked before running checks. If \code{FALSE}, an attempt will me made to +restore previous progress from the same \code{output}} + +\item{...}{Additional arguments passed to \code{\link[=run]{run()}}} } \value{ -\code{\link[=check_design]{check_design()}} R6 class storing all the details +\code{\link[=checker]{checker()}} R6 class storing all the details regarding checks that run. Can be combined with \code{\link{results}} and \code{\link[=summary]{summary()}} methods to generate results. } diff --git a/man/check_pkgs.Rd b/man/check_pkgs.Rd index e2683cf..3f3fd6c 100644 --- a/man/check_pkgs.Rd +++ b/man/check_pkgs.Rd @@ -2,21 +2,21 @@ % Please edit documentation in R/check.R \name{check_pkgs} \alias{check_pkgs} -\title{Check one or more package source directories} +\title{Check packages} \usage{ check_pkgs( - path, + package, n = 2L, output = tempfile(paste(utils::packageName(), Sys.Date(), sep = "-")), lib.loc = .libPaths(), repos = getOption("repos"), - restore = options::opt("restore"), - reporter = reporter_default(), + restore = TRUE, ... ) } \arguments{ -\item{path}{file path to the package source directory} +\item{package}{A path to either package, directory with packages or name +of the package (details)} \item{n}{\code{integer} value indicating maximum number of subprocesses that can be simultaneously spawned when executing tasks.} @@ -30,31 +30,26 @@ checking packages, defaults to entire \code{\link[=.libPaths]{.libPaths()}}.} \item{repos}{\code{character} vector of repositories which will be used when generating task graph and later pulling dependencies.} -\item{restore}{\code{logical} indicating whether output directory should be unlinked before -running checks. If \code{FALSE}, an attempt will me made to restore previous -progress from the same \code{output} (Defaults to \code{NA}, overwritable using option 'checked.restore' or environment variable 'R_CHECKED_RESTORE')} +\item{restore}{\code{logical} indicating whether output directory should be +unlinked before running checks. If \code{FALSE}, an attempt will me made to +restore previous progress from the same \code{output}} -\item{reporter}{A reporter to provide progress updates. Will default to the -most expressive command-line reporter given your terminal capabilities.} - -\item{...}{Additional arguments passed to \code{\link{checked-task-df}} and \code{\link[=run]{run()}}} +\item{...}{Additional arguments passed to \code{\link[=run]{run()}}} } \value{ -\code{\link[=check_design]{check_design()}} R6 class storing all the details +\code{\link[=checker]{checker()}} R6 class storing all the details regarding checks that run. Can be combined with \code{\link{results}} and \code{\link[=summary]{summary()}} methods to generate results. } \description{ -\code{\link[=check_pkgs]{check_pkgs()}} Installs all dependencies and runs \verb{R CMD check}s -in parallel for all source packages whose source code is found in the -\code{path} directory +Runs classical \verb{R CMD check} for the given source package. It +first identifies and installs, in parallel, all dependencies required +to check the package. Then, it runs \verb{R CMD check} for each specified package. } \seealso{ Other checks: -\code{\link{check_design}}, -\code{\link{check_dev_rev_deps}()}, -\code{\link{check_dir}()}, \code{\link{check_rev_deps}()}, -\code{\link{new_check_design}()} +\code{\link{checker}}, +\code{\link{new_checker}()} } \concept{checks} diff --git a/man/check_rev_deps.Rd b/man/check_rev_deps.Rd index 1a4d892..e729aee 100644 --- a/man/check_rev_deps.Rd +++ b/man/check_rev_deps.Rd @@ -11,8 +11,7 @@ check_rev_deps( lib.loc = .libPaths(), repos = getOption("repos"), reverse_repos = repos, - restore = options::opt("restore"), - reporter = reporter_default(), + restore = TRUE, ... ) } @@ -36,17 +35,14 @@ to pull sources for reverse dependencies. In some cases, for instance using binaries on Linux, we want to use different repositories when pulling sources to check and different when installing dependencies.} -\item{restore}{\code{logical} indicating whether output directory should be unlinked before -running checks. If \code{FALSE}, an attempt will me made to restore previous -progress from the same \code{output} (Defaults to \code{NA}, overwritable using option 'checked.restore' or environment variable 'R_CHECKED_RESTORE')} +\item{restore}{\code{logical} indicating whether output directory should be +unlinked before running checks. If \code{FALSE}, an attempt will me made to +restore previous progress from the same \code{output}} -\item{reporter}{A reporter to provide progress updates. Will default to the -most expressive command-line reporter given your terminal capabilities.} - -\item{...}{Additional arguments passed to \code{\link{checked-task-df}} and \code{\link[=run]{run()}}} +\item{...}{Additional arguments passed to \code{\link[=run]{run()}}} } \value{ -\code{\link[=check_design]{check_design()}} R6 class storing all the details +\code{\link[=checker]{checker()}} R6 class storing all the details regarding checks that run. Can be combined with \code{\link{results}} and \code{\link[=summary]{summary()}} methods to generate results. } @@ -66,10 +62,8 @@ identify changes in reverse dependency behaviors. } \seealso{ Other checks: -\code{\link{check_design}}, -\code{\link{check_dev_rev_deps}()}, -\code{\link{check_dir}()}, \code{\link{check_pkgs}()}, -\code{\link{new_check_design}()} +\code{\link{checker}}, +\code{\link{new_checker}()} } \concept{checks} diff --git a/man/check_task_spec.Rd b/man/check_task.Rd similarity index 59% rename from man/check_task_spec.Rd rename to man/check_task.Rd index 79ab0ef..420d96c 100644 --- a/man/check_task_spec.Rd +++ b/man/check_task.Rd @@ -1,16 +1,18 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/task_spec.R -\name{check_task_spec} -\alias{check_task_spec} +% Please edit documentation in R/task.R +\name{check_task} +\alias{check_task} \title{Create a task to run \verb{R CMD check}} \usage{ -check_task_spec( - args = options::opt("check_args"), - build_args = options::opt("check_build_args"), - ... -) +check_task(build_args = NULL, args = NULL, env = NULL, ...) } \arguments{ +\item{build_args}{Character vector of arguments to pass to \verb{R CMD build}. +Pass each argument as a single element of this character vector (do not use +spaces to delimit arguments like you would in the shell). For example, +\code{build_args = c("--force", "--keep-empty-dirs")} is a correct usage and +\code{build_args = "--force --keep-empty-dirs"} is incorrect.} + \item{args}{Character vector of arguments to pass to \verb{R CMD check}. Pass each argument as a single element of this character vector (do not use spaces to delimit arguments like you would in the shell). For example, to skip @@ -19,19 +21,13 @@ instead of the \code{--output} option you should use the \code{check_dir} argume because \code{--output} cannot deal with spaces and other special characters on Windows.)} -\item{build_args}{Character vector of arguments to pass to \verb{R CMD build}. -Pass each argument as a single element of this character vector (do not use -spaces to delimit arguments like you would in the shell). For example, -\code{build_args = c("--force", "--keep-empty-dirs")} is a correct usage and -\code{build_args = "--force --keep-empty-dirs"} is incorrect.} +\item{env}{A named character vector, extra environment variables to +set in the check process.} \item{...}{ - Arguments passed on to \code{\link[=task_spec]{task_spec}} + Arguments passed on to \code{\link[=task]{task}} \describe{ - \item{\code{alias}}{task alias which also serves as unique identifier of the task.} - \item{\code{package_spec}}{\code{\link[checked]{package_spec}} object} - \item{\code{env}}{environmental variables to be set in separate process running -specific task.} + \item{\code{.subclass}}{Additional subclasses.} }} } \description{ @@ -39,12 +35,8 @@ Create a task to run \verb{R CMD check} } \seealso{ Other tasks: -\code{\link{checked-task-df}}, -\code{\link{custom_install_task_spec}()}, -\code{\link{install_task_spec}()}, -\code{\link{rev_dep_check_tasks_df}()}, -\code{\link{revdep_check_task_spec}()}, -\code{\link{source_check_tasks_df}()}, -\code{\link{task_spec}()} +\code{\link{install_task}()}, +\code{\link{meta_task}()}, +\code{\link{task}()} } \concept{tasks} diff --git a/man/checked-package.Rd b/man/checked-package.Rd new file mode 100644 index 0000000..033a5c5 --- /dev/null +++ b/man/checked-package.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/package.R +\docType{package} +\name{checked-package} +\alias{checked} +\alias{checked-package} +\title{checked: Systematically Run R CMD Checks} +\description{ +Systematically Run R checks against multiple packages. Checks are run in parallel with strategies to minimize dependency installation. Provides out of the box interface for running reverse dependency check. +} +\seealso{ +Useful links: +\itemize{ + \item \url{https://Genentech.github.io/checked/} + \item \url{https://github.com/Genentech/checked} + \item Report bugs at \url{https://github.com/Genentech/checked/issues} +} + +} +\author{ +\strong{Maintainer}: Szymon Maksymiuk \email{sz.maksymiuk@gmail.com} (\href{https://orcid.org/0000-0002-3120-1601}{ORCID}) + +Authors: +\itemize{ + \item Doug Kelkhoff \email{doug.kelkhoff@gmail.com} (\href{https://orcid.org/0009-0003-7845-4061}{ORCID}) +} + +Other contributors: +\itemize{ + \item F. Hoffmann-La Roche AG [copyright holder, funder] +} + +} +\keyword{internal} diff --git a/man/checked-task-df.Rd b/man/checked-task-df.Rd deleted file mode 100644 index 9b11ff5..0000000 --- a/man/checked-task-df.Rd +++ /dev/null @@ -1,48 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/checks_df.R -\name{checked-task-df} -\alias{checked-task-df} -\title{Check schedule data frame} -\arguments{ -\item{path}{path to the package source. Can be either a single source -code directory or a directory containing multiple package source code -directories.} - -\item{...}{parameters passed to the task specs allowing to customize -subprocesses.} -} -\value{ -The check schedule \code{data.frame} with the following columns: -\itemize{ -\item \code{alias}: The alias of the check to run. It also serves the purpose of -providing a unique identifier and node name in the task graph. -\item \code{version}: Version of the package to be checked. -\item \code{package}: Object that inherits from \code{\link[=check_task_spec]{check_task_spec()}}. -Defines how package to be checked can be acquired. -\item \code{custom}: Object that inherits from \code{\link[=custom_install_task_spec]{custom_install_task_spec()}}. -Defines custom package, for instance only available from local source, that -should be installed before checking the package. -} -} -\description{ -Create data.frame which each row defines a package for which R CMD check -should be run. Such data.frame is a prerequisite for generating -\code{\link[=check_design]{check_design()}} which orchestrates all the processes -including dependencies installation. -} -\details{ -\verb{_tasks_df()} functions generate check task \code{data.frame} for -all source packages specified by the \code{path}. Therefore it accepts it to be -a vector of an arbitrary length. -} -\seealso{ -Other tasks: -\code{\link{check_task_spec}()}, -\code{\link{custom_install_task_spec}()}, -\code{\link{install_task_spec}()}, -\code{\link{rev_dep_check_tasks_df}()}, -\code{\link{revdep_check_task_spec}()}, -\code{\link{source_check_tasks_df}()}, -\code{\link{task_spec}()} -} -\concept{tasks} diff --git a/man/check_design.Rd b/man/checker.Rd similarity index 55% rename from man/check_design.Rd rename to man/checker.Rd index f94b3af..1dbab10 100644 --- a/man/check_design.Rd +++ b/man/checker.Rd @@ -1,7 +1,7 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/check_design.R -\name{check_design} -\alias{check_design} +% Please edit documentation in R/checker.R +\name{checker} +\alias{checker} \title{\code{R6} Checks Coordinator} \description{ A stateful object that orchestrates all separate processes required to @@ -10,25 +10,28 @@ manage installation, library setup and run \verb{R CMD check}s in sequence. \examples{ \dontrun{ library(checked) -df <- source_check_tasks_df(c( +plan <- plan_checks(c( system.file("example_packages", "exampleBad", package = "checked"), system.file("example_packages", "exampleGood", package = "checked") )) -plan <- check_design$new(df, n = 10, repos = "https://cran.r-project.org/") -while (!plan$is_done()) { - plan$start_next_task() +orchestrator <- checker$new( + plan, + n = 10, + repos = "https://cran.r-project.org/" +) + +while (!orchestrator$is_done()) { + orchestrator$start_next_task() } } } \seealso{ Other checks: -\code{\link{check_dev_rev_deps}()}, -\code{\link{check_dir}()}, \code{\link{check_pkgs}()}, \code{\link{check_rev_deps}()}, -\code{\link{new_check_design}()} +\code{\link{new_checker}()} } \concept{checks} \section{Public fields}{ @@ -37,9 +40,9 @@ Other checks: \item{\code{graph}}{(\code{igraph::igraph()})\cr A dependency graph, storing information about which dependencies are required prior to execution of each check task. -Created with \code{\link[=task_graph_create]{task_graph_create()}}} +Created with \code{\link[=task_graph]{task_graph()}}} -\item{\code{input}}{(\code{data.frame()})\cr +\item{\code{plan}}{(\code{data.frame()})\cr Checks task \code{data.frame} which is the source of all the checks.} \item{\code{output}}{(\code{character(1)})\cr @@ -51,29 +54,29 @@ be created and stored.} \section{Methods}{ \subsection{Public methods}{ \itemize{ -\item \href{#method-check_design-new}{\code{check_design$new()}} -\item \href{#method-check_design-active_processes}{\code{check_design$active_processes()}} -\item \href{#method-check_design-failed_tasks}{\code{check_design$failed_tasks()}} -\item \href{#method-check_design-terminate}{\code{check_design$terminate()}} -\item \href{#method-check_design-step}{\code{check_design$step()}} -\item \href{#method-check_design-start_next_task}{\code{check_design$start_next_task()}} -\item \href{#method-check_design-is_done}{\code{check_design$is_done()}} -\item \href{#method-check_design-clone}{\code{check_design$clone()}} +\item \href{#method-checker-new}{\code{checker$new()}} +\item \href{#method-checker-active_processes}{\code{checker$active_processes()}} +\item \href{#method-checker-failed_tasks}{\code{checker$failed_tasks()}} +\item \href{#method-checker-terminate}{\code{checker$terminate()}} +\item \href{#method-checker-step}{\code{checker$step()}} +\item \href{#method-checker-start_next_task}{\code{checker$start_next_task()}} +\item \href{#method-checker-is_done}{\code{checker$is_done()}} +\item \href{#method-checker-clone}{\code{checker$clone()}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-check_design-new}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-checker-new}{}}} \subsection{Method \code{new()}}{ Initialize a new check design Use checks data.frame to generate task graph in which all dependencies and installation order are embedded. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{check_design$new( - df, +\if{html}{\out{
}}\preformatted{checker$new( + plan, n = 2L, - output = tempfile(paste(packageName(), Sys.Date(), sep = "-")), + output = file.path(tempdir(), paste(packageName(), Sys.Date(), sep = "-")), lib.loc = .libPaths(), repos = getOption("repos"), restore = options::opt("restore"), @@ -84,7 +87,7 @@ and installation order are embedded. \subsection{Arguments}{ \if{html}{\out{
}} \describe{ -\item{\code{df}}{\code{check_design} data.frame.} +\item{\code{plan}}{\code{plan} \code{data.frame}.} \item{\code{n}}{\code{integer} value indicating maximum number of subprocesses that can be simultaneously spawned when executing tasks.} @@ -107,48 +110,48 @@ restore previous progress from the same \code{output}.} \if{html}{\out{
}} } \subsection{Returns}{ -\link{check_design}. +\link{checker}. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-check_design-active_processes}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-checker-active_processes}{}}} \subsection{Method \code{active_processes()}}{ Get Active Processes list \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{check_design$active_processes()}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{checker$active_processes()}\if{html}{\out{
}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-check_design-failed_tasks}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-checker-failed_tasks}{}}} \subsection{Method \code{failed_tasks()}}{ Get Failed Tasks list \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{check_design$failed_tasks()}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{checker$failed_tasks()}\if{html}{\out{
}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-check_design-terminate}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-checker-terminate}{}}} \subsection{Method \code{terminate()}}{ Kill All Active Design Processes Immediately terminates all the active processes. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{check_design$terminate()}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{checker$terminate()}\if{html}{\out{
}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-check_design-step}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-checker-step}{}}} \subsection{Method \code{step()}}{ Fill Available Processes with Tasks \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{check_design$step()}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{checker$step()}\if{html}{\out{
}} } \subsection{Returns}{ @@ -157,12 +160,12 @@ running. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-check_design-start_next_task}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-checker-start_next_task}{}}} \subsection{Method \code{start_next_task()}}{ Start Next Task \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{check_design$start_next_task()}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{checker$start_next_task()}\if{html}{\out{
}} } \subsection{Returns}{ @@ -171,24 +174,24 @@ process was spawned, or \code{-1} if all tasks have finished. } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-check_design-is_done}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-checker-is_done}{}}} \subsection{Method \code{is_done()}}{ Check if checks are done Checks whether all the scheduled tasks were successfully executed. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{check_design$is_done()}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{checker$is_done()}\if{html}{\out{
}} } } \if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-check_design-clone}{}}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-checker-clone}{}}} \subsection{Method \code{clone()}}{ The objects of this class are cloneable with this method. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{check_design$clone(deep = FALSE)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{checker$clone(deep = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ diff --git a/man/cli.Rd b/man/cli.Rd index ff0e85f..82c8741 100644 --- a/man/cli.Rd +++ b/man/cli.Rd @@ -7,13 +7,14 @@ \title{Internal Utilities for Command-line Output} \usage{ cli_table_row( - status, + status = "", ok = "OK", notes = "N", warnings = "W", errors = "E", msg = "", - title = FALSE + style = c("row", "title", "header"), + symbols = DEFAULT_ROW_SYMBOL ) cli_theme(..., .envir = parent.frame()) diff --git a/man/custom_install_task_spec.Rd b/man/custom_install_task_spec.Rd deleted file mode 100644 index 88a291f..0000000 --- a/man/custom_install_task_spec.Rd +++ /dev/null @@ -1,41 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/task_spec.R -\name{custom_install_task_spec} -\alias{custom_install_task_spec} -\title{Create a custom install task} -\usage{ -custom_install_task_spec(...) -} -\arguments{ -\item{...}{ - Arguments passed on to \code{\link[=install_task_spec]{install_task_spec}} - \describe{ - \item{\code{type}}{character, indicating the type of package to download and - install. Will be \code{"source"} except on Windows and some macOS - builds: see the section on \sQuote{Binary packages} for those. - } - \item{\code{INSTALL_opts}}{ - an optional character vector of additional option(s) to be passed to - \command{R CMD INSTALL} for a source package install. E.g., - \code{c("--html", "--no-multiarch", "--no-test-load")} or, for - macOS, \code{"--dsym"}. - - Can also be a named list of character vectors to be used as - additional options, with names the respective package names. - } - }} -} -\description{ -Create a custom install task -} -\seealso{ -Other tasks: -\code{\link{check_task_spec}()}, -\code{\link{checked-task-df}}, -\code{\link{install_task_spec}()}, -\code{\link{rev_dep_check_tasks_df}()}, -\code{\link{revdep_check_task_spec}()}, -\code{\link{source_check_tasks_df}()}, -\code{\link{task_spec}()} -} -\concept{tasks} diff --git a/man/figures/README/ansi-tty-example-dark.svg b/man/figures/README/ansi-tty-example-dark.svg deleted file mode 100644 index f524fd4..0000000 --- a/man/figures/README/ansi-tty-example-dark.svg +++ /dev/null @@ -1 +0,0 @@ -ETA?(0/4)[1.1s]installingpraise(release),diffviewerETA?(0/4)[1.4s]installingpraise(release),diffviewerETA?(0/4)[1.6s]installingpraise(release),diffviewerETA?(0/4)[1.8s]installingpraise(release),diffviewerETA?(0/4)[2.1s]installingpraise(release),diffviewerETA?(0/4)[2.3s]installingpraise(release),diffviewerETA?(0/4)[2.5s]installingpraise(release),diffviewerETA?(0/4)[2.7s]installingpraise(release),diffviewerETA?(0/4)[3s]installingpraise(release),diffviewerETA?(0/4)[3.2s]installingpraise(release),diffviewerETA?(0/4)[3.4s]installingpraise(release),diffviewerETA?(0/4)[3.6s]installingpraise(release),diffviewerETA?(0/4)[3.9s]installingdiffviewer,praise(dev)ETA?(0/4)[4.2s]installingdiffviewer,praise(dev)SOKNWEtestthat(v1.0.0)00000.9sstarting...ETA?(0/4)[7.3s]installingpraise(dev)testthat(v1.0.0)20002.7scheckingpackagenamespaceinfortestthat(dev)00001.0sstarting...ETA?(0/4)[9.2s]testthat(v1.0.0)50003.1scheckingifthereisanamespacetestthat(dev)00001.4sstarting...ETA?(0/4)[9.7s]testthat(v1.0.0)50003.6scheckingifthereisanamespacetestthat(dev)00001.8sstarting...ETA?(0/4)[10s]testthat(v1.0.0)50003.9scheckingifthereisanamespacetestthat(dev)20002.1scheckingpackagenamespaceinforETA?(0/4)[10.3s]testthat(v1.0.0)50004.3scheckingifthereisanamespacetestthat(dev)20002.5scheckingpackagenamespaceinforETA?(0/4)[10.7s]testthat(v1.0.0)50004.6scheckingifthereisanamespacetestthat(dev)20002.9scheckingpackagenamespaceinforETA?(0/4)[11.1s]testthat(v1.0.0)90005.0scheckingforsufficient/correcttestthat(dev)20003.3scheckingpackagenamespaceinforETA?(0/4)[11.5s]testthat(v1.0.0)90005.4scheckingforsufficient/correcttestthat(dev)50003.6scheckingifthereisanamespaceETA?(0/4)[11.8s]testthat(v1.0.0)90005.7scheckingforsufficient/correcttestthat(dev)50003.9scheckingifthereisanamespaceETA?(0/4)[12.1s]testthat(v1.0.0)90006.1scheckingforsufficient/correcttestthat(dev)50004.3scheckingifthereisanamespaceETA?(0/4)[12.5s]testthat(v1.0.0)90006.4scheckingforsufficient/correcttestthat(dev)50004.7scheckingifthereisanamespaceETA?(0/4)[12.9s]testthat(v1.0.0)90006.8scheckingforsufficient/correcttestthat(dev)50005.0scheckingifthereisanamespaceETA?(0/4)[13.2s]testthat(v1.0.0)90007.1scheckingforsufficient/correcttestthat(dev)90005.3scheckingforsufficient/correctETA?(0/4)[13.5s]testthat(v1.0.0)90007.5scheckingforsufficient/correcttestthat(dev)90005.7scheckingforsufficient/correctETA?(0/4)[14s]testthat(v1.0.0)90007.9scheckingforsufficient/correcttestthat(dev)90006.1scheckingforsufficient/correctETA?(0/4)[14.3s]testthat(v1.0.0)90008.3scheckingforsufficient/correcttestthat(dev)90006.5scheckingforsufficient/correctETA?(0/4)[14.7s]testthat(v1.0.0)90008.6scheckingforsufficient/correcttestthat(dev)90006.8scheckingforsufficient/correctETA?(0/4)[15s]testthat(v1.0.0)90008.9scheckingforsufficient/correcttestthat(dev)90007.1scheckingforsufficient/correctETA?(0/4)[15.3s]testthat(v1.0.0)90009.3scheckingforsufficient/correcttestthat(dev)90007.5scheckingforsufficient/correctETA?(0/4)[15.7s]testthat(v1.0.0)90009.6scheckingforsufficient/correcttestthat(dev)90007.8scheckingforsufficient/correctETA?(0/4)[16s]testthat(v1.0.0)90009.9scheckingforsufficient/correcttestthat(dev)90008.1scheckingforsufficient/correctETA?(0/4)[16.3s]testthat(v1.0.0)900010.3scheckingforsufficient/correcttestthat(dev)90008.5scheckingforsufficient/correctETA?(0/4)[16.7s]testthat(v1.0.0)900010.6scheckingforsufficient/correcttestthat(dev)90008.8scheckingforsufficient/correctETA?(0/4)[17s]testthat(v1.0.0)900011.0scheckingforsufficient/correcttestthat(dev)90009.2scheckingforsufficient/correctETA?(0/4)[17.4s]testthat(v1.0.0)900011.4scheckingforsufficient/correcttestthat(dev)90009.6scheckingforsufficient/correctETA?(0/4)[17.8s]testthat(v1.0.0)900011.8scheckingforsufficient/correcttestthat(dev)900010.0scheckingforsufficient/correctETA?(0/4)[18.2s]testthat(v1.0.0)900012.1scheckingforsufficient/correcttestthat(dev)900010.3scheckingforsufficient/correctETA?(0/4)[18.6s]testthat(v1.0.0)900012.5scheckingforsufficient/correcttestthat(dev)900010.7scheckingforsufficient/correctETA?(0/4)[18.9s]testthat(v1.0.0)900012.8scheckingforsufficient/correcttestthat(dev)900011.0scheckingforsufficient/correctETA?(0/4)[19.2s]testthat(v1.0.0)900013.2scheckingforsufficient/correcttestthat(dev)900011.4scheckingforsufficient/correctETA?(0/4)[19.6s]testthat(v1.0.0)900013.5scheckingforsufficient/correcttestthat(dev)900011.7scheckingforsufficient/correctETA?(0/4)[19.9s]testthat(v1.0.0)900013.9scheckingforsufficient/correcttestthat(dev)900012.0scheckingforsufficient/correctETA?(0/4)[20.2s]testthat(v1.0.0)900014.2scheckingforsufficient/correcttestthat(dev)900012.4scheckingforsufficient/correctETA?(0/4)[20.6s]testthat(v1.0.0)900014.5scheckingforsufficient/correcttestthat(dev)900012.7scheckingforsufficient/correctETA?(0/4)[20.9s]testthat(v1.0.0)900014.8scheckingforsufficient/correcttestthat(dev)900013.0scheckingforsufficient/correctETA?(0/4)[21.2s]testthat(v1.0.0)900015.2scheckingforsufficient/correcttestthat(dev)900013.4scheckingforsufficient/correctETA?(0/4)[21.6s]testthat(v1.0.0)900015.5scheckingforsufficient/correcttestthat(dev)900013.8scheckingforsufficient/correctETA?(0/4)[22s]testthat(v1.0.0)900015.9scheckingforsufficient/correcttestthat(dev)900014.1scheckingforsufficient/correctETA?(0/4)[22.3s]testthat(v1.0.0)900016.2scheckingforsufficient/correcttestthat(dev)900014.4scheckingforsufficient/correctETA?(0/4)[22.6s]testthat(v1.0.0)900016.6scheckingforsufficient/correcttestthat(dev)900014.8scheckingforsufficient/correctETA?(0/4)[23s]testthat(v1.0.0)900016.9scheckingforsufficient/correcttestthat(dev)900015.1scheckingforsufficient/correctETA?(0/4)[23.3s]testthat(v1.0.0)900017.3scheckingforsufficient/correcttestthat(dev)900015.5scheckingforsufficient/correctETA?(0/4)[23.6s]testthat(v1.0.0)900017.6scheckingforsufficient/correcttestthat(dev)900015.8scheckingforsufficient/correctETA?(0/4)[24s]testthat(v1.0.0)900018.0scheckingforsufficient/correcttestthat(dev)900016.1scheckingforsufficient/correctETA?(0/4)[24.3s]testthat(v1.0.0)900018.3scheckingforsufficient/correcttestthat(dev)900016.5scheckingforsufficient/correctETA?(0/4)[24.7s]testthat(v1.0.0)900018.6scheckingforsufficient/correcttestthat(dev)900016.8scheckingforsufficient/correctETA?(0/4)[25s]testthat(v1.0.0)900019.0scheckingforsufficient/correcttestthat(dev)900017.2scheckingforsufficient/correctETA?(0/4)[25.4s]testthat(v1.0.0)900019.3scheckingforsufficient/correcttestthat(dev)900017.5scheckingforsufficient/correctETA?(0/4)[25.8s]testthat(v1.0.0)900019.8scheckingforsufficient/correcttestthat(dev)900018.0scheckingforsufficient/correctETA?(0/4)[26.3s]testthat(v1.0.0)900020.2scheckingforsufficient/correcttestthat(dev)900018.4scheckingforsufficient/correctETA?(0/4)[26.6s]testthat(v1.0.0)900020.6scheckingforsufficient/correcttestthat(dev)900018.8scheckingforsufficient/correctETA?(0/4)[27s]testthat(v1.0.0)900021.0scheckingforsufficient/correcttestthat(dev)900019.2scheckingforsufficient/correctETA?(0/4)[27.5s]testthat(v1.0.0)900021.4scheckingforsufficient/correcttestthat(dev)900019.6scheckingforsufficient/correctETA?(0/4)[27.9s]testthat(v1.0.0)900021.8scheckingforsufficient/correcttestthat(dev)900020.1scheckingforsufficient/correctETA?(0/4)[28.3s]testthat(v1.0.0)900022.2scheckingforsufficient/correcttestthat(dev)900020.5scheckingforsufficient/correctETA?(0/4)[28.8s]testthat(v1.0.0)900022.7scheckingforsufficient/correcttestthat(dev)900020.9scheckingforsufficient/correctETA?(0/4)[29.2s]testthat(v1.0.0)900023.1scheckingforsufficient/correcttestthat(dev)900021.4scheckingforsufficient/correctETA?(0/4)[29.6s]testthat(v1.0.0)900023.6scheckingforsufficient/correcttestthat(dev)900021.8scheckingforsufficient/correctETA?(0/4)[30s]testthat(v1.0.0)900024.0scheckingforsufficient/correcttestthat(dev)900022.2scheckingforsufficient/correctETA?(0/4)[30.4s]testthat(v1.0.0)900024.3scheckingforsufficient/correcttestthat(dev)900022.5scheckingforsufficient/correctETA?(0/4)[30.7s]testthat(v1.0.0)900024.6scheckingforsufficient/correcttestthat(dev)900022.9scheckingforsufficient/correctETA?(0/4)[31.1s]testthat(v1.0.0)900025.0scheckingforsufficient/correcttestthat(dev)900023.2scheckingforsufficient/correctETA?(0/4)[31.4s]testthat(v1.0.0)900025.3scheckingforsufficient/correcttestthat(dev)900023.6scheckingforsufficient/correctETA?(0/4)[31.8s]testthat(v1.0.0)900025.7scheckingforsufficient/correcttestthat(dev)900023.9scheckingforsufficient/correctETA?(0/4)[32.2s]testthat(v1.0.0)900026.1scheckingforsufficient/correcttestthat(dev)900024.3scheckingforsufficient/correctETA?(0/4)[32.5s]testthat(v1.0.0)900026.4scheckingforsufficient/correcttestthat(dev)900024.6scheckingforsufficient/correctETA?(0/4)[32.8s]testthat(v1.0.0)900026.8scheckingforsufficient/correcttestthat(dev)900025.0scheckingforsufficient/correctETA?(0/4)[33.2s]testthat(v1.0.0)900027.1scheckingforsufficient/correcttestthat(dev)900025.3scheckingforsufficient/correctETA?(0/4)[33.5s]testthat(v1.0.0)900027.5scheckingforsufficient/correcttestthat(dev)900025.7scheckingforsufficient/correctETA?(0/4)[33.9s]testthat(v1.0.0)900027.8scheckingforsufficient/correcttestthat(dev)900026.0scheckingforsufficient/correctETA?(0/4)[34.2s]testthat(v1.0.0)900028.1scheckingforsufficient/correcttestthat(dev)900026.3scheckingforsufficient/correctETA?(0/4)[34.5s]testthat(v1.0.0)900028.4scheckingforsufficient/correcttestthat(dev)900026.6scheckingforsufficient/correctETA?(0/4)[34.8s]testthat(v1.0.0)900028.8scheckingforsufficient/correcttestthat(dev)900027.0scheckingforsufficient/correctETA?(0/4)[35.2s]testthat(v1.0.0)900029.2scheckingforsufficient/correcttestthat(dev)900027.4scheckingforsufficient/correctETA?(0/4)[35.6s]testthat(v1.0.0)900029.6scheckingforsufficient/correcttestthat(dev)900027.8scheckingforsufficient/correctETA?(0/4)[36s]testthat(v1.0.0)900030.0scheckingforsufficient/correcttestthat(dev)900028.2scheckingforsufficient/correctETA?(0/4)[36.4s]testthat(v1.0.0)900030.3scheckingforsufficient/correcttestthat(dev)900028.5scheckingforsufficient/correctETA?(0/4)[36.7s]testthat(v1.0.0)900030.7scheckingforsufficient/correcttestthat(dev)900028.9scheckingforsufficient/correctETA?(0/4)[37.1s]testthat(v1.0.0)900031.0scheckingforsufficient/correcttestthat(dev)900029.2scheckingforsufficient/correctETA?(0/4)[37.4s]testthat(v1.0.0)900031.3scheckingforsufficient/correcttestthat(dev)900029.5scheckingforsufficient/correctETA?(0/4)[37.7s]testthat(v1.0.0)900031.7scheckingforsufficient/correcttestthat(dev)900029.9scheckingforsufficient/correctETA?(0/4)[38.2s]testthat(v1.0.0)900032.1scheckingforsufficient/correcttestthat(dev)900030.3scheckingforsufficient/correctETA?(0/4)[38.5s]testthat(v1.0.0)900032.4scheckingforsufficient/correcttestthat(dev)900030.6scheckingforsufficient/correctETA?(0/4)[38.8s]testthat(v1.0.0)900032.7scheckingforsufficient/correcttestthat(dev)900030.9scheckingforsufficient/correctETA?(0/4)[39.1s]testthat(v1.0.0)900033.1scheckingforsufficient/correcttestthat(dev)900031.3scheckingforsufficient/correctETA?(0/4)[39.5s]testthat(v1.0.0)900033.5scheckingforsufficient/correcttestthat(dev)900031.7scheckingforsufficient/correctETA?(0/4)[39.9s]testthat(v1.0.0)900033.8scheckingforsufficient/correcttestthat(dev)900032.0scheckingforsufficient/correctETA?(0/4)[40.2s]testthat(v1.0.0)900034.1scheckingforsufficient/correcttestthat(dev)900032.3scheckingforsufficient/correctETA?(0/4)[40.5s]testthat(v1.0.0)900034.5scheckingforsufficient/correcttestthat(dev)900032.7scheckingforsufficient/correctETA?(0/4)[40.9s]testthat(v1.0.0)900034.8scheckingforsufficient/correcttestthat(dev)900033.0scheckingforsufficient/correctETA?(0/4)[41.2s]testthat(v1.0.0)900035.1s(30.1s)checkingforsufficienttestthat(dev)900033.3scheckingforsufficient/correctETA?(0/4)[41.5s]testthat(v1.0.0)900035.5s(30.4s)checkingforsufficienttestthat(dev)900033.7scheckingforsufficient/correctETA?(0/4)[41.9s]testthat(v1.0.0)900035.8s(30.8s)checkingforsufficienttestthat(dev)900034.0scheckingforsufficient/correctETA?(0/4)[42.2s]testthat(v1.0.0)900036.2s(31.1s)checkingforsufficienttestthat(dev)900034.4scheckingforsufficient/correctETA?(0/4)[42.6s]testthat(v1.0.0)900036.5s(31.5s)checkingforsufficienttestthat(dev)900034.7scheckingforsufficient/correctETA?(0/4)[42.9s]testthat(v1.0.0)900036.9s(31.8s)checkingforsufficienttestthat(dev)900035.1scheckingforsufficient/correctETA?(0/4)[43.3s]testthat(v1.0.0)900037.2s(32.2s)checkingforsufficienttestthat(dev)900035.4s(30.1s)checkingforsufficientETA?(0/4)[43.6s]testthat(v1.0.0)900037.5s(32.5s)checkingforsufficienttestthat(dev)900035.7s(30.5s)checkingforsufficientETA?(0/4)[43.9s]testthat(v1.0.0)900037.9s(32.8s)checkingforsufficienttestthat(dev)900036.1s(30.8s)checkingforsufficientETA?(0/4)[44.3s]testthat(v1.0.0)900038.2s(33.2s)checkingforsufficienttestthat(dev)900036.4s(31.1s)checkingforsufficientETA?(0/4)[44.6s]testthat(v1.0.0)900038.5s(33.5s)checkingforsufficienttestthat(dev)900036.7s(31.4s)checkingforsufficientETA?(0/4)[44.9s]testthat(v1.0.0)900038.8s(33.8s)checkingforsufficienttestthat(dev)900037.0s(31.8s)checkingforsufficientETA?(0/4)[45.2s]testthat(v1.0.0)900039.2s(34.1s)checkingforsufficienttestthat(dev)900037.4s(32.1s)checkingforsufficientETA?(0/4)[45.6s]testthat(v1.0.0)900039.6s(34.5s)checkingforsufficienttestthat(dev)900037.8s(32.5s)checkingforsufficientETA?(0/4)[46s]testthat(v1.0.0)900039.9s(34.9s)checkingforsufficienttestthat(dev)900038.1s(32.8s)checkingforsufficientETA?(0/4)[46.3s]testthat(v1.0.0)900040.2s(35.2s)checkingforsufficienttestthat(dev)900038.4s(33.2s)checkingforsufficientETA?(0/4)[46.6s]testthat(v1.0.0)900040.6s(35.5s)checkingforsufficienttestthat(dev)900038.8s(33.5s)checkingforsufficientETA?(0/4)[47s]testthat(v1.0.0)900040.9s(35.9s)checkingforsufficienttestthat(dev)900039.1s(33.8s)checkingforsufficientETA?(0/4)[47.3s]testthat(v1.0.0)900041.2s(36.2s)checkingforsufficienttestthat(dev)900039.5s(34.2s)checkingforsufficientETA?(0/4)[47.7s]testthat(v1.0.0)900041.7s(36.6s)checkingforsufficienttestthat(dev)900039.9s(34.6s)checkingforsufficientETA?(0/4)[48.1s]testthat(v1.0.0)900042.1s(37.0s)checkingforsufficienttestthat(dev)900040.3s(35.0s)checkingforsufficientETA?(0/4)[48.5s]testthat(v1.0.0)900042.5s(37.4s)checkingforsufficienttestthat(dev)900040.7s(35.4s)checkingforsufficientETA?(0/4)[48.9s]testthat(v1.0.0)900042.8s(37.8s)checkingforsufficienttestthat(dev)900041.0s(35.8s)checkingforsufficientETA?(0/4)[49.2s]testthat(v1.0.0)900043.2s(38.1s)checkingforsufficienttestthat(dev)900041.4s(36.1s)checkingforsufficientETA?(0/4)[49.6s]testthat(v1.0.0)900043.5s(38.5s)checkingforsufficienttestthat(dev)900041.7s(36.4s)checkingforsufficientETA?(0/4)[49.9s]testthat(v1.0.0)900043.8s(38.8s)checkingforsufficienttestthat(dev)900042.0s(36.8s)checkingforsufficientETA?(0/4)[50.2s]testthat(v1.0.0)900044.1s(39.1s)checkingforsufficienttestthat(dev)900042.3s(37.1s)checkingforsufficientETA?(0/4)[50.6s]testthat(v1.0.0)900044.5s(39.5s)checkingforsufficienttestthat(dev)900042.7s(37.4s)checkingforsufficientETA?(0/4)[50.9s]testthat(v1.0.0)1010044.8scheckinginstalledpackagesizetestthat(dev)900043.0s(37.7s)checkingforsufficientETA?(0/4)[51.3s]testthat(v1.0.0)1110045.2scheckingpackagedirectory...testthat(dev)900043.4s(38.2s)checkingforsufficientETA?(0/4)[51.6s]testthat(v1.0.0)1410045.6scheckingforleft-overfiles..testthat(dev)900043.8s(38.6s)checkingforsufficientETA?(0/4)[52s]testthat(v1.0.0)1510046.0scheckingindexinformation...testthat(dev)900044.2s(39.0s)checkingforsufficientETA?(0/4)[52.4s]testthat(v1.0.0)1510046.4scheckingindexinformation...testthat(dev)900044.7s(39.4s)checkingforsufficientETA?(0/4)[52.9s]testthat(v1.0.0)1510046.8scheckingindexinformation...testthat(dev)900045.0s(39.7s)checkingforsufficientETA?(0/4)[53.2s]testthat(v1.0.0)1510047.1scheckingindexinformation...testthat(dev)900045.4s(40.1s)checkingforsufficientETA?(0/4)[53.6s]testthat(v1.0.0)1710047.5scheckingcodefilesfornon-ASCtestthat(dev)1010045.7scheckinginstalledpackagesizeETA?(0/4)[54s]testthat(v1.0.0)1810047.9scheckingRfilesforsyntaxerrtestthat(dev)1110046.2scheckingpackagedirectory...ETA?(0/4)[54.4s]testthat(v1.0.0)1910048.3scheckingwhetherthepackagecatestthat(dev)1510046.6scheckingindexinformation...ETA?(0/4)[54.8s]testthat(v1.0.0)2010048.7scheckingwhetherthepackagecatestthat(dev)1510046.9scheckingindexinformation...ETA?(0/4)[55.1s]testthat(v1.0.0)2110049.1scheckingwhetherthepackagecatestthat(dev)1510047.3scheckingindexinformation...ETA?(0/4)[55.6s]testthat(v1.0.0)2210049.5scheckingwhetherthenamespacetestthat(dev)1510047.8scheckingindexinformation...ETA?(0/4)[56s]testthat(v1.0.0)2210049.9scheckingwhetherthenamespacetestthat(dev)1610048.1scheckingpackagesubdirectoriesETA?(0/4)[56.3s]testthat(v1.0.0)2310050.3scheckingwhetherthenamespacetestthat(dev)1810048.5scheckingRfilesforsyntaxerrETA?(0/4)[56.7s]testthat(v1.0.0)2410050.6scheckingloadingwithoutbeingtestthat(dev)1910048.8scheckingwhetherthepackagecaETA?(0/4)[57s]testthat(v1.0.0)2410051.0scheckingloadingwithoutbeingtestthat(dev)1910049.3scheckingwhetherthepackagecaETA?(0/4)[57.5s]testthat(v1.0.0)2410051.4scheckingloadingwithoutbeingtestthat(dev)2010049.7scheckingwhetherthepackagecaETA?(0/4)[58s]testthat(v1.0.0)2410051.9scheckingloadingwithoutbeingtestthat(dev)2110050.1scheckingwhetherthepackagecaETA?(0/4)[58.3s]testthat(v1.0.0)2410052.3scheckingloadingwithoutbeingtestthat(dev)2210050.5scheckingwhetherthenamespaceETA?(0/4)[58.7s]testthat(v1.0.0)2410052.7scheckingloadingwithoutbeingtestthat(dev)2310050.9scheckingwhetherthenamespaceETA?(0/4)[59.1s]testthat(v1.0.0)2410053.1scheckingloadingwithoutbeingtestthat(dev)2310051.3scheckingwhetherthenamespaceETA?(0/4)[59.5s]testthat(v1.0.0)2410053.4scheckingloadingwithoutbeingtestthat(dev)2410051.7scheckingloadingwithoutbeingETA?(0/4)[59.9s]testthat(v1.0.0)2510053.8scheckingdependenciesinRcodetestthat(dev)2410052.0scheckingloadingwithoutbeingETA?(0/4)[1m0.2s]testthat(v1.0.0)2510054.2scheckingdependenciesinRcodetestthat(dev)2410052.4scheckingloadingwithoutbeingETA?(0/4)[1m0.6s]testthat(v1.0.0)2510054.5scheckingdependenciesinRcodetestthat(dev)2410052.7scheckingloadingwithoutbeingETA?(0/4)[1m1s]testthat(v1.0.0)2610054.9scheckingS3generic/methodconstestthat(dev)2410053.1scheckingloadingwithoutbeingETA?(0/4)[1m1.3s]testthat(v1.0.0)2610055.2scheckingS3generic/methodconstestthat(dev)2410053.4scheckingloadingwithoutbeingETA?(0/4)[1m1.6s]testthat(v1.0.0)2710055.6scheckingreplacementfunctionstestthat(dev)2410053.8scheckingloadingwithoutbeingETA?(0/4)[1m2s]testthat(v1.0.0)2710055.9scheckingreplacementfunctionstestthat(dev)2410054.2scheckingloadingwithoutbeingETA?(0/4)[1m2.4s]testthat(v1.0.0)2710056.3scheckingreplacementfunctionstestthat(dev)2510054.5scheckingdependenciesinRcodeETA?(0/4)[1m2.7s]testthat(v1.0.0)2810056.7scheckingforeignfunctioncallstestthat(dev)2510054.9scheckingdependenciesinRcodeETA?(0/4)[1m3.1s]testthat(v1.0.0)2810057.0scheckingforeignfunctioncallstestthat(dev)2510055.3scheckingdependenciesinRcodeETA?(0/4)[1m3.5s]testthat(v1.0.0)2810057.5scheckingforeignfunctioncallstestthat(dev)2610055.7scheckingS3generic/methodconsETA?(0/4)[1m3.9s]testthat(v1.0.0)2810057.8scheckingforeignfunctioncallstestthat(dev)2710056.1scheckingreplacementfunctionsETA?(0/4)[1m4.3s]testthat(v1.0.0)2810058.2scheckingforeignfunctioncallstestthat(dev)2710056.4scheckingreplacementfunctionsETA?(0/4)[1m4.7s]testthat(v1.0.0)2810058.6scheckingforeignfunctioncallstestthat(dev)2710056.8scheckingreplacementfunctionsETA?(0/4)[1m5s]testthat(v1.0.0)2810058.9scheckingforeignfunctioncallstestthat(dev)2710057.1scheckingreplacementfunctionsETA?(0/4)[1m5.3s]testthat(v1.0.0)2810059.3scheckingforeignfunctioncallstestthat(dev)2810057.5scheckingforeignfunctioncallsETA?(0/4)[1m5.7s]testthat(v1.0.0)2810059.6scheckingforeignfunctioncallstestthat(dev)2810057.8scheckingforeignfunctioncallsETA?(0/4)[1m6s]testthat(v1.0.0)2810060.0scheckingforeignfunctioncallstestthat(dev)2810058.2scheckingforeignfunctioncallsETA?(0/4)[1m6.4s]testthat(v1.0.0)281001.0mcheckingforeignfunctioncallstestthat(dev)2810058.5scheckingforeignfunctioncallsETA?(0/4)[1m6.7s]testthat(v1.0.0)281001.0mcheckingforeignfunctioncallstestthat(dev)2810058.8scheckingforeignfunctioncallsETA?(0/4)[1m7s]testthat(v1.0.0)281001.0mcheckingforeignfunctioncallstestthat(dev)2810059.2scheckingforeignfunctioncallsETA?(0/4)[1m7.4s]testthat(v1.0.0)281001.0mcheckingforeignfunctioncallstestthat(dev)2810059.5scheckingforeignfunctioncallsETA?(0/4)[1m7.7s]testthat(dev)2810059.9scheckingforeignfunctioncallsETA?(0/4)[1m8.1s]testthat(dev)281001.0mcheckingforeignfunctioncallsETA?(0/4)[1m8.5s]testthat(dev)281001.0mcheckingforeignfunctioncallsETA?(0/4)[1m8.9s]testthat(dev)281001.0mcheckingforeignfunctioncallsETA?(0/4)[1m9.2s]testthat(v1.0.0)281001.1mcheckingforeignfunctioncallstestthat(dev)281001.0mcheckingforeignfunctioncallsETA?(0/4)[1m9.7s]testthat(v1.0.0)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m10.2s]testthat(v1.0.0)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m10.5s]testthat(v1.0.0)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m10.8s]testthat(dev)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m11.2s]testthat(dev)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m11.6s]testthat(dev)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m11.9s]testthat(dev)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m12.4s]ETA?(0/4)[1m12.7s]testthat(v1.0.0)291001.1mcheckingRcodeforpossibleproETA?(0/4)[1m13s]testthat(v1.0.0)291001.1mcheckingRcodeforpossibleproETA?(0/4)[1m13.4s]testthat(v1.0.0)311001.1mcheckingRdmetadata...ETA?(0/4)[1m13.8s]testthat(v1.0.0)311001.1mcheckingRdmetadata...ETA?(0/4)[1m14.1s]testthat(v1.0.0)311001.1mcheckingRdmetadata...ETA?(0/4)[1m14.5s]testthat(v1.0.0)321001.1mcheckingformissingdocumentatitestthat(dev)291001.1mcheckingRcodeforpossibleproETA?(0/4)[1m14.8s]testthat(v1.0.0)321001.1mcheckingformissingdocumentatitestthat(dev)291001.1mcheckingRcodeforpossibleproETA?(0/4)[1m15.2s]testthat(v1.0.0)321001.2mcheckingformissingdocumentatitestthat(dev)301001.1mcheckingRdfiles...ETA?(0/4)[1m15.6s]testthat(v1.0.0)321001.2mcheckingformissingdocumentatitestthat(dev)311001.1mcheckingRdmetadata...ETA?(0/4)[1m16s]testthat(v1.0.0)321001.2mcheckingformissingdocumentatitestthat(dev)311001.1mcheckingRdmetadata...ETA?(0/4)[1m16.4s]testthat(v1.0.0)331001.2mcheckingforcode/documentationtestthat(dev)321001.1mcheckingformissingdocumentatiETA?(0/4)[1m16.8s]testthat(v1.0.0)331001.2mcheckingforcode/documentationtestthat(dev)321001.1mcheckingformissingdocumentatiETA?(0/4)[1m17.1s]testthat(v1.0.0)331001.2mcheckingforcode/documentationtestthat(dev)321001.2mcheckingformissingdocumentatiETA?(0/4)[1m17.4s]testthat(v1.0.0)331001.2mcheckingforcode/documentationtestthat(dev)321001.2mcheckingformissingdocumentatiETA?(0/4)[1m17.8s]testthat(dev)321001.2mcheckingformissingdocumentatiETA?(0/4)[1m18.2s]testthat(v1.0.0)341001.2mcheckingRd\usagesections...testthat(dev)321001.2mcheckingformissingdocumentatiETA?(0/4)[1m18.6s]testthat(v1.0.0)381001.2mcheckinglineendingsinMakefiltestthat(dev)331001.2mcheckingforcode/documentationETA?(0/4)[1m19s]testthat(v1.0.0)431001.2mcheckingcompiledcode...testthat(dev)331001.2mcheckingforcode/documentationETA?(0/4)[1m19.4s]testthat(v1.0.0)451001.2mcheckingfilesin‘vignettes’..testthat(dev)331001.2mcheckingforcode/documentationETA?(0/4)[1m19.8s]testthat(v1.0.0)451001.2mcheckingfilesin‘vignettes’..testthat(dev)331001.2mcheckingforcode/documentationETA?(0/4)[1m20.1s]testthat(v1.0.0)451001.2mcheckingfilesin‘vignettes’..testthat(dev)341001.2mcheckingRd\usagesections...ETA?(0/4)[1m20.4s]testthat(v1.0.0)451001.2mcheckingfilesin‘vignettes’..testthat(dev)351001.2mcheckingRdcontents...ETA?(0/4)[1m20.7s]testthat(dev)381001.2mcheckinglineendingsinMakefilETA?(0/4)[1m21.1s]testthat(v1.0.0)451001.3mcheckingfilesin‘vignettes’..testthat(dev)431001.2mcheckingcompiledcode...ETA?(0/4)[1m21.5s]testthat(v1.0.0)451001.3mcheckingfilesin‘vignettes’..testthat(dev)451001.2mcheckingfilesin‘vignettes’..ETA?(0/4)[1m21.8s]testthat(v1.0.0)451001.3mcheckingfilesin‘vignettes’..testthat(dev)451001.2mcheckingfilesin‘vignettes’..ETA?(0/4)[1m22.1s]testthat(v1.0.0)451001.3mcheckingfilesin‘vignettes’..testthat(dev)451001.2mcheckingfilesin‘vignettes’..ETA?(0/4)[1m22.5s]testthat(dev)451001.2mcheckingfilesin‘vignettes’..ETA?(0/4)[1m22.8s]ETA?(0/4)[1m23.2s]testthat(dev)451001.3mcheckingfilesin‘vignettes’..ETA?(0/4)[1m23.6s]testthat(dev)451001.3mcheckingfilesin‘vignettes’..ETA?(0/4)[1m23.9s]testthat(dev)451001.3mcheckingfilesin‘vignettes’..ETA?(0/4)[1m24.3s]testthat(v1.0.0)461001.3mcheckingexamples...testthat(dev)451001.3mcheckingfilesin‘vignettes’..ETA?(0/4)[1m24.7s]testthat(v1.0.0)471001.3mcheckingtests...ETA?(0/4)[1m25.1s]testthat(v1.0.0)471001.3mcheckingtests...ETA?(0/4)[1m25.5s]testthat(v1.0.0)471001.3mcheckingtests...ETA?(0/4)[1m25.9s]testthat(v1.0.0)471001.3mcheckingtests...ETA?(0/4)[1m26.3s]testthat(dev)471001.3mcheckingforunstateddependenciETA?(0/4)[1m26.6s]testthat(dev)471001.3mcheckingtests...ETA?(0/4)[1m27s]testthat(dev)471001.3mcheckingtests...ETA?(0/4)[1m27.5s]testthat(v1.0.0)471001.4mcheckingtests...testthat(dev)471001.3mcheckingtests...ETA?(0/4)[1m27.9s]testthat(v1.0.0)471001.4mcheckingtests...testthat(dev)471001.3mcheckingtests...ETA?(0/4)[1m28.3s]testthat(v1.0.0)471001.4mcheckingtests...ETA?(0/4)[1m28.6s]testthat(v1.0.0)471001.4mcheckingtests...ETA?(0/4)[1m29s]testthat(dev)471001.4mcheckingtests...ETA?(0/4)[1m29.4s]testthat(dev)471001.4mcheckingtests...ETA?(0/4)[1m29.8s]testthat(dev)471001.4mcheckingtests...ETA?(0/4)[1m30.1s]testthat(dev)471001.4mcheckingtests...ETA?(0/4)[1m30.4s]ETA?(0/4)[1m30.8s]ETA?(0/4)[1m31.1s]ETA?(0/4)[1m31.4s]ETA?(0/4)[1m31.8s]ETA?(0/4)[1m32.1s]ETA?(0/4)[1m32.5s]ETA?(0/4)[1m32.8s]ETA?(0/4)[1m33.1s]testthat(v1.0.0)471001.5mcheckingtests...ETA?(0/4)[1m33.5s]testthat(v1.0.0)471001.5mcheckingtests...ETA?(0/4)[1m33.9s]testthat(v1.0.0)471001.5mcheckingtests...ETA?(0/4)[1m34.3s]testthat(v1.0.0)471001.5mcheckingtests...ETA?(0/4)[1m34.7s]ETA?(0/4)[1m35s]testthat(dev)471001.5mcheckingtests...ETA?(0/4)[1m35.3s]testthat(dev)471001.5mcheckingtests...ETA?(0/4)[1m35.6s]testthat(dev)471001.5mcheckingtests...ETA?(0/4)[1m36s]testthat(dev)471001.5mcheckingtests...ETA?(0/4)[1m36.3s]ETA?(0/4)[1m36.7s]ETA?(0/4)[1m37.1s]ETA?(0/4)[1m37.5s]ETA?(0/4)[1m37.8s]ETA?(0/4)[1m38.2s]ETA?(0/4)[1m38.6s]ETA?(0/4)[1m39s]ETA?(0/4)[1m39.3s]testthat(v1.0.0)471001.6mcheckingtests...ETA?(0/4)[1m39.8s]testthat(v1.0.0)471001.6mcheckingtests...ETA?(0/4)[1m40.1s]testthat(v1.0.0)471001.6mcheckingtests...ETA?(0/4)[1m40.5s]testthat(v1.0.0)471001.6mcheckingtests...ETA?(0/4)[1m40.8s]ETA?(0/4)[1m41.2s]testthat(dev)471001.6mcheckingtests...ETA?(0/4)[1m41.5s]testthat(dev)471001.6mcheckingtests...ETA?(0/4)[1m41.8s]testthat(dev)471001.6mcheckingtests...ETA?(0/4)[1m42.2s]testthat(dev)471001.6mcheckingtests...ETA?(0/4)[1m42.5s]ETA?(0/4)[1m42.9s]ETA?(0/4)[1m43.3s]ETA?(0/4)[1m43.7s]ETA?(0/4)[1m44.1s]ETA?(0/4)[1m44.5s]ETA?(0/4)[1m44.9s]ETA?(0/4)[1m45.2s]testthat(v1.0.0)471001.7mcheckingtests...ETA?(0/4)[1m45.5s]testthat(v1.0.0)471001.7mcheckingtests...ETA?(0/4)[1m46s]testthat(v1.0.0)471001.7mcheckingtests...ETA?(0/4)[1m46.3s]testthat(v1.0.0)471001.7mcheckingtests...ETA?(0/4)[1m46.7s]ETA?(0/4)[1m47.1s]testthat(dev)471001.7mcheckingtests...ETA?(0/4)[1m47.4s]testthat(dev)471001.7mcheckingtests...ETA?(0/4)[1m47.8s]testthat(dev)471001.7mcheckingtests...ETA?(0/4)[1m48.2s]testthat(dev)471001.7mcheckingtests...ETA?(0/4)[1m48.5s]ETA?(0/4)[1m49s]ETA?(0/4)[1m49.7s]ETA?(0/4)[1m50.2s]ETA?(0/4)[1m50.7s]ETA?(0/4)[1m51.2s]testthat(v1.0.0)471001.8mcheckingtests...ETA?(0/4)[1m51.7s]testthat(v1.0.0)471001.8mcheckingtests...ETA?(0/4)[1m52.3s]testthat(v1.0.0)471001.8mcheckingtests...ETA?(0/4)[1m52.6s]testthat(v1.0.0)471001.8mcheckingtests...ETA?(0/4)[1m53.1s]testthat(dev)471001.8mcheckingtests...ETA?(0/4)[1m54s]testthat(dev)471001.8mcheckingtests...ETA?(0/4)[1m54.5s]testthat(dev)471001.8mcheckingtests...ETA?(0/4)[1m54.9s]testthat(dev)471001.8mcheckingtests...ETA?(0/4)[1m55.3s]ETA?(0/4)[1m55.8s]ETA?(0/4)[1m56.3s]ETA?(0/4)[1m57.1s]testthat(v1.0.0)471001.9mcheckingtests...ETA?(0/4)[1m58.3s]testthat(v1.0.0)471001.9mcheckingtests...ETA?(0/4)[1m58.9s]testthat(v1.0.0)471001.9mcheckingtests...testthat(dev)471001.9mcheckingtests...ETA?(0/4)[1m59.8s]testthat(v1.0.0)471001.9mcheckingtests...testthat(dev)471001.9mcheckingtests...ETA?(0/4)[2m0.3s]testthat(dev)471001.9mcheckingtests...ETA?(0/4)[2m1s]testthat(dev)471001.9mcheckingtests...ETA?(0/4)[2m1.5s]ETA?(0/4)[2m2.1s]ETA?(0/4)[2m2.6s]ETA?(0/4)[2m3s]ETA?(0/4)[2m3.6s]testthat(v1.0.0)471002.0mcheckingtests...ETA?(0/4)[2m4.5s]testthat(v1.0.0)471002.0mcheckingtests...ETA?(0/4)[2m5s]testthat(v1.0.0)471002.0mcheckingtests...testthat(dev)471002.0mcheckingtests...ETA?(0/4)[2m5.6s]testthat(v1.0.0)471002.0mcheckingtests...testthat(dev)471002.0mcheckingtests...ETA?(0/4)[2m6.3s]testthat(dev)471002.0mcheckingtests...ETA?(0/4)[2m7s]testthat(dev)471002.0mcheckingtests...ETA?(0/4)[2m7.6s]ETA?(0/4)[2m8.1s]ETA?(0/4)[2m8.5s]ETA?(0/4)[2m8.9s]ETA?(0/4)[2m9.5s]testthat(v1.0.0)471002.1mcheckingtests...ETA?(0/4)[2m10.1s]testthat(v1.0.0)471002.1mcheckingtests...ETA?(0/4)[2m10.9s]testthat(v1.0.0)471002.1mcheckingtests...testthat(dev)471002.1mcheckingtests...ETA?(0/4)[2m11.8s]testthat(v1.0.0)471002.1mcheckingtests...testthat(dev)471002.1mcheckingtests...ETA?(0/4)[2m12.3s]testthat(dev)471002.1mcheckingtests...ETA?(0/4)[2m12.6s]testthat(dev)471002.1mcheckingtests...ETA?(0/4)[2m12.9s]ETA?(0/4)[2m13.3s]ETA?(0/4)[2m14.1s]ETA?(0/4)[2m14.6s]ETA?(0/4)[2m15.1s]testthat(v1.0.0)471002.2mcheckingtests...ETA?(0/4)[2m15.9s]testthat(v1.0.0)471002.2mcheckingtests...ETA?(0/4)[2m16.4s]testthat(v1.0.0)471002.2mcheckingtests...ETA?(0/4)[2m17.3s]testthat(v1.0.0)471002.2mcheckingtests...testthat(dev)471002.2mcheckingtests...ETA?(0/4)[2m18s]testthat(dev)471002.2mcheckingtests...ETA?(0/4)[2m18.6s]testthat(dev)471002.2mcheckingtests...ETA?(0/4)[2m19.2s]testthat(dev)471002.2mcheckingtests...ETA?(0/4)[2m19.9s]ETA?(0/4)[2m20.4s]ETA?(0/4)[2m21s]ETA?(0/4)[2m21.6s]testthat(v1.0.0)471002.3mcheckingtests...ETA?(0/4)[2m22.5s]testthat(v1.0.0)471002.3mcheckingtests...ETA?(0/4)[2m23.1s]testthat(v1.0.0)471002.3mcheckingtests...testthat(dev)471002.3mcheckingtests...ETA?(0/4)[2m23.7s]testthat(v1.0.0)471002.3mcheckingtests...testthat(dev)471002.3mcheckingtests...ETA?(0/4)[2m24.2s]testthat(dev)471002.3mcheckingtests...ETA?(0/4)[2m24.7s]testthat(dev)471002.3mcheckingtests...ETA?(0/4)[2m25.4s]ETA?(0/4)[2m25.9s]ETA?(0/4)[2m26.6s]ETA?(0/4)[2m27s]ETA?(0/4)[2m27.6s]testthat(v1.0.0)471002.4mcheckingtests...ETA?(0/4)[2m28.2s]testthat(v1.0.0)471002.4mcheckingtests...ETA?(0/4)[2m28.7s]testthat(v1.0.0)471002.4mcheckingtests...testthat(dev)471002.4mcheckingtests...ETA?(0/4)[2m29.4s]testthat(v1.0.0)471002.4mcheckingtests...testthat(dev)471002.4mcheckingtests...ETA?(0/4)[2m30s]testthat(dev)471002.4mcheckingtests...ETA?(0/4)[2m30.5s]testthat(dev)471002.4mcheckingtests...ETA?(0/4)[2m31.1s]ETA?(0/4)[2m31.6s]ETA?(0/4)[2m32.1s]ETA?(0/4)[2m32.5s]ETA?(0/4)[2m32.9s]ETA?(0/4)[2m33.5s]testthat(v1.0.0)471002.5mcheckingtests...ETA?(0/4)[2m34s]testthat(v1.0.0)471002.5mcheckingtests...ETA?(0/4)[2m34.4s]testthat(v1.0.0)471002.5mcheckingtests...ETA?(0/4)[2m34.8s]testthat(v1.0.0)471002.5mcheckingtests...ETA?(0/4)[2m35.2s]testthat(dev)471002.5mcheckingtests...ETA?(0/4)[2m35.6s]testthat(dev)471002.5mcheckingtests...ETA?(0/4)[2m36s]testthat(dev)471002.5mcheckingtests...ETA?(0/4)[2m36.5s]testthat(dev)471002.5mcheckingtests...ETA?(0/4)[2m36.9s]ETA?(0/4)[2m37.4s]ETA?(0/4)[2m37.9s]ETA?(0/4)[2m38.2s]ETA?(0/4)[2m38.6s]ETA?(0/4)[2m39s]ETA?(0/4)[2m39.6s]testthat(v1.0.0)471002.6mcheckingtests...ETA?(0/4)[2m40.1s]testthat(v1.0.0)471002.6mcheckingtests...ETA?(0/4)[2m40.8s]testthat(v1.0.0)471002.6mcheckingtests...testthat(dev)471002.6mcheckingtests...ETA?(0/4)[2m41.3s]testthat(v1.0.0)471002.6mcheckingtests...testthat(dev)471002.6mcheckingtests...ETA?(0/4)[2m41.7s]testthat(dev)471002.6mcheckingtests...ETA?(0/4)[2m42.3s]testthat(dev)471002.6mcheckingtests...ETA?(0/4)[2m42.8s]ETA?(0/4)[2m43.1s]ETA?(0/4)[2m43.5s]ETA?(0/4)[2m43.8s]ETA?(0/4)[2m44.2s]ETA?(0/4)[2m44.5s]ETA?(0/4)[2m44.8s]ETA?(0/4)[2m45.1s]testthat(v1.0.0)471002.7mcheckingtests...ETA?(0/4)[2m45.5s]testthat(v1.0.0)471002.7mcheckingtests...ETA?(0/4)[2m45.9s]testthat(v1.0.0)481002.7mcheckingtests...ETA?(0/4)[2m46.2s]testthat(v1.0.0)!481002.7mETA8m(1/4)[2m46.8s]installingclisymbolsETA8m(1/4)[2m47.2s]installingclisymbolstestthat(dev)481002.7mcheckingtests...ETA8m(1/4)[2m47.6s]installingclisymbolstestthat(dev)!481002.7mETA3m(2/4)[2m48s]installingclisymbolsETA3m(2/4)[2m48.4s]installingclisymbolsETA3m(2/4)[2m48.7s]installingclisymbolsETA3m(2/4)[2m49.2s]installingclisymbolsETA3m(2/4)[2m49.6s]installingclisymbolsETA3m(2/4)[2m50s]installingclisymbolsETA3m(2/4)[2m50.3s]installingclisymbolsgoodpractice(v1.0.0)00001.1sstarting...ETA3m(2/4)[2m51.9s]goodpractice(v1.0.0)20002.5scheckingpackagenamespaceinforgoodpractice(dev)00001.1sstarting...ETA3m(2/4)[2m53.4s]goodpractice(v1.0.0)20003.0scheckingpackagenamespaceinforgoodpractice(dev)20001.7scheckingpackagenamespaceinforETA3m(2/4)[2m53.9s]goodpractice(v1.0.0)50003.5scheckingifthereisanamespacegoodpractice(dev)20002.1scheckingpackagenamespaceinforETA3m(2/4)[2m54.4s]goodpractice(v1.0.0)90003.9scheckingforsufficient/correctgoodpractice(dev)20002.6scheckingpackagenamespaceinforETA3m(2/4)[2m54.8s]goodpractice(v1.0.0)90004.4scheckingforsufficient/correctgoodpractice(dev)20003.0scheckingpackagenamespaceinforETA3m(2/4)[2m55.3s]goodpractice(v1.0.0)90005.0scheckingforsufficient/correctgoodpractice(dev)50003.7scheckingifthereisanamespaceETA3m(2/4)[2m56s]goodpractice(v1.0.0)90005.6scheckingforsufficient/correctgoodpractice(dev)90004.2scheckingforsufficient/correctETA3m(2/4)[2m56.5s]goodpractice(v1.0.0)90006.1scheckingforsufficient/correctgoodpractice(dev)90004.8scheckingforsufficient/correctETA3m(2/4)[2m57s]goodpractice(v1.0.0)90006.8scheckingforsufficient/correctgoodpractice(dev)90005.5scheckingforsufficient/correctETA3m(2/4)[2m57.8s]goodpractice(v1.0.0)90007.4scheckingforsufficient/correctgoodpractice(dev)90006.0scheckingforsufficient/correctETA3m(2/4)[2m58.2s]goodpractice(v1.0.0)90008.0scheckingforsufficient/correctgoodpractice(dev)90006.6scheckingforsufficient/correctETA3m(2/4)[2m58.8s]goodpractice(v1.0.0)90008.5scheckingforsufficient/correctgoodpractice(dev)90007.1scheckingforsufficient/correctETA3m(2/4)[2m59.3s]goodpractice(v1.0.0)90009.0scheckingforsufficient/correctgoodpractice(dev)90007.6scheckingforsufficient/correctETA3m(2/4)[2m59.8s]goodpractice(v1.0.0)90009.4scheckingforsufficient/correctgoodpractice(dev)90008.1scheckingforsufficient/correctETA3m(2/4)[3m0.3s]goodpractice(v1.0.0)900010.0scheckingforsufficient/correctgoodpractice(dev)90008.6scheckingforsufficient/correctETA3m(2/4)[3m0.9s]goodpractice(v1.0.0)900010.5scheckingforsufficient/correctgoodpractice(dev)90009.1scheckingforsufficient/correctETA3m(2/4)[3m1.3s]goodpractice(v1.0.0)900011.0scheckingforsufficient/correctgoodpractice(dev)90009.6scheckingforsufficient/correctETA3m(2/4)[3m1.9s]goodpractice(v1.0.0)1500011.6scheckingforleft-overfiles..goodpractice(dev)900010.3scheckingforsufficient/correctETA3m(2/4)[3m2.6s]goodpractice(v1.0.0)1600012.3scheckingindexinformation...goodpractice(dev)900010.9scheckingforsufficient/correctETA3m(2/4)[3m3.2s]goodpractice(v1.0.0)1600012.8scheckingindexinformation...goodpractice(dev)900011.4scheckingforsufficient/correctETA3m(2/4)[3m3.6s]goodpractice(v1.0.0)1800013.3scheckingcodefilesfornon-ASCgoodpractice(dev)900011.9scheckingforsufficient/correctETA3m(2/4)[3m4.2s]goodpractice(v1.0.0)1900013.8scheckingRfilesforsyntaxerrgoodpractice(dev)900012.4scheckingforsufficient/correctETA3m(2/4)[3m4.7s]goodpractice(v1.0.0)1900014.4scheckingRfilesforsyntaxerrgoodpractice(dev)1600013.0scheckingindexinformation...ETA3m(2/4)[3m5.3s]goodpractice(v1.0.0)2000015.0scheckingwhetherthepackagecagoodpractice(dev)1600013.6scheckingindexinformation...ETA3m(2/4)[3m5.9s]goodpractice(v1.0.0)2000015.5scheckingwhetherthepackagecagoodpractice(dev)1900014.2scheckingRfilesforsyntaxerrETA3m(2/4)[3m6.5s]goodpractice(v1.0.0)2100016.1scheckingwhetherthepackagecagoodpractice(dev)1900014.7scheckingRfilesforsyntaxerrETA3m(2/4)[3m7s]goodpractice(v1.0.0)2100016.6scheckingwhetherthepackagecagoodpractice(dev)1900015.2scheckingRfilesforsyntaxerrETA3m(2/4)[3m7.4s]goodpractice(v1.0.0)2200017.1scheckingwhetherthepackagecagoodpractice(dev)2000015.7scheckingwhetherthepackagecaETA3m(2/4)[3m8s]goodpractice(v1.0.0)2200017.6scheckingwhetherthepackagecagoodpractice(dev)2100016.3scheckingwhetherthepackagecaETA3m(2/4)[3m8.5s]goodpractice(v1.0.0)2300018.1scheckingwhetherthenamespacegoodpractice(dev)2100016.8scheckingwhetherthepackagecaETA3m(2/4)[3m9s]goodpractice(v1.0.0)2300018.6scheckingwhetherthenamespacegoodpractice(dev)2200017.2scheckingwhetherthepackagecaETA3m(2/4)[3m9.5s]goodpractice(v1.0.0)2400019.2scheckingwhetherthenamespacegoodpractice(dev)2200017.8scheckingwhetherthepackagecaETA3m(2/4)[3m10.1s]goodpractice(v1.0.0)2400019.9scheckingwhetherthenamespacegoodpractice(dev)2300018.5scheckingwhetherthenamespaceETA3m(2/4)[3m10.8s]goodpractice(v1.0.0)2500020.4scheckingloadingwithoutbeinggoodpractice(dev)2300019.0scheckingwhetherthenamespaceETA3m(2/4)[3m11.2s]goodpractice(v1.0.0)2500020.8scheckingloadingwithoutbeinggoodpractice(dev)2300019.4scheckingwhetherthenamespaceETA3m(2/4)[3m11.7s]goodpractice(v1.0.0)2500021.3scheckingloadingwithoutbeinggoodpractice(dev)2400019.9scheckingwhetherthenamespaceETA3m(2/4)[3m12.1s]goodpractice(v1.0.0)2500021.8scheckingloadingwithoutbeinggoodpractice(dev)2400020.4scheckingwhetherthenamespaceETA3m(2/4)[3m12.6s]goodpractice(v1.0.0)2600022.3scheckingdependenciesinRcodegoodpractice(dev)2500020.9scheckingloadingwithoutbeingETA3m(2/4)[3m13.1s]goodpractice(v1.0.0)2600022.7scheckingdependenciesinRcodegoodpractice(dev)2500021.3scheckingloadingwithoutbeingETA3m(2/4)[3m13.5s]goodpractice(v1.0.0)2600023.2scheckingdependenciesinRcodegoodpractice(dev)2500021.9scheckingloadingwithoutbeingETA3m(2/4)[3m14.1s]goodpractice(v1.0.0)2700023.8scheckingS3generic/methodconsgoodpractice(dev)2500022.4scheckingloadingwithoutbeingETA3m(2/4)[3m14.7s]goodpractice(v1.0.0)2700024.4scheckingS3generic/methodconsgoodpractice(dev)2600023.0scheckingdependenciesinRcodeETA3m(2/4)[3m15.2s]goodpractice(v1.0.0)2800024.9scheckingreplacementfunctionsgoodpractice(dev)2600023.5scheckingdependenciesinRcodeETA3m(2/4)[3m15.8s]goodpractice(v1.0.0)2800025.4scheckingreplacementfunctionsgoodpractice(dev)2700024.0scheckingS3generic/methodconsETA3m(2/4)[3m16.4s]goodpractice(v1.0.0)2900026.0scheckingforeignfunctioncallsgoodpractice(dev)2700024.6scheckingS3generic/methodconsETA3m(2/4)[3m16.8s]goodpractice(v1.0.0)2900026.5scheckingforeignfunctioncallsgoodpractice(dev)2800025.1scheckingreplacementfunctionsETA3m(2/4)[3m17.4s]goodpractice(v1.0.0)2900027.0scheckingforeignfunctioncallsgoodpractice(dev)2800025.6scheckingreplacementfunctionsETA3m(2/4)[3m17.8s]goodpractice(v1.0.0)2900027.4scheckingforeignfunctioncallsgoodpractice(dev)2900026.1scheckingforeignfunctioncallsETA3m(2/4)[3m18.3s]goodpractice(v1.0.0)2900028.0scheckingforeignfunctioncallsgoodpractice(dev)2900026.6scheckingforeignfunctioncallsETA3m(2/4)[3m18.8s]goodpractice(v1.0.0)2900028.5scheckingforeignfunctioncallsgoodpractice(dev)2900027.1scheckingforeignfunctioncallsETA3m(2/4)[3m19.4s]goodpractice(v1.0.0)2900029.0scheckingforeignfunctioncallsgoodpractice(dev)2900027.7scheckingforeignfunctioncallsETA3m(2/4)[3m19.9s]goodpractice(v1.0.0)2900029.5scheckingforeignfunctioncallsgoodpractice(dev)2900028.1scheckingforeignfunctioncallsETA3m(2/4)[3m20.4s]goodpractice(v1.0.0)3200030.1scheckingRdmetadata...goodpractice(dev)2900028.7scheckingforeignfunctioncallsETA3m(2/4)[3m20.9s]goodpractice(v1.0.0)3200030.5scheckingRdmetadata...goodpractice(dev)2900029.1scheckingforeignfunctioncallsETA3m(2/4)[3m21.4s]goodpractice(v1.0.0)3200031.0scheckingRdmetadata...goodpractice(dev)2900029.6scheckingforeignfunctioncallsETA3m(2/4)[3m21.9s]goodpractice(v1.0.0)3300031.5scheckingformissingdocumentatgoodpractice(dev)3000030.1scheckingRcodeforpossibleprETA3m(2/4)[3m22.4s]goodpractice(v1.0.0)3300032.0scheckingformissingdocumentatgoodpractice(dev)3200030.6scheckingRdmetadata...ETA3m(2/4)[3m22.9s]goodpractice(v1.0.0)3300032.5scheckingformissingdocumentatgoodpractice(dev)3200031.1scheckingRdmetadata...ETA3m(2/4)[3m23.3s]goodpractice(v1.0.0)3300032.9scheckingformissingdocumentatgoodpractice(dev)3300031.6scheckingformissingdocumentatETA3m(2/4)[3m23.8s]goodpractice(v1.0.0)3300033.5scheckingformissingdocumentatgoodpractice(dev)3300032.1scheckingformissingdocumentatETA3m(2/4)[3m24.3s]goodpractice(v1.0.0)3400034.0scheckingformissingdocumentatgoodpractice(dev)3300032.6scheckingformissingdocumentatETA3m(2/4)[3m24.9s]goodpractice(v1.0.0)3400034.6scheckingforcode/documentationgoodpractice(dev)3300033.3scheckingformissingdocumentatETA3m(2/4)[3m25.6s]goodpractice(v1.0.0)3400035.3scheckingforcode/documentationgoodpractice(dev)3300033.9scheckingformissingdocumentatETA3m(2/4)[3m26.2s]goodpractice(v1.0.0)3500035.8scheckingRd\usagesections...goodpractice(dev)3400034.4scheckingforcode/documentationETA3m(2/4)[3m26.7s]goodpractice(v1.0.0)3700036.3scheckingforunstateddependencgoodpractice(dev)3400034.9scheckingforcode/documentationETA3m(2/4)[3m27.2s]goodpractice(v1.0.0)3900036.8scheckingfilesin‘vignettes’.goodpractice(dev)3400035.4scheckingforcode/documentationETA3m(2/4)[3m27.7s]goodpractice(v1.0.0)3900037.4scheckingfilesin‘vignettes’.goodpractice(dev)3500036.0scheckingRd\usagesections...ETA3m(2/4)[3m28.3s]goodpractice(v1.0.0)3900038.1scheckingfilesin‘vignettes’.goodpractice(dev)3700036.7scheckingforunstateddependencETA3m(2/4)[3m29s]goodpractice(v1.0.0)3900038.7scheckingfilesin‘vignettes’.goodpractice(dev)3900037.4scheckingfilesin‘vignettes’.ETA3m(2/4)[3m29.7s]goodpractice(v1.0.0)4100039.3scheckingforunstateddependencgoodpractice(dev)3900037.9scheckingfilesin‘vignettes’.ETA4m(2/4)[3m30.2s]goodpractice(v1.0.0)4100039.8scheckingtests...goodpractice(dev)3900038.4scheckingfilesin‘vignettes’.ETA4m(2/4)[3m30.7s]goodpractice(v1.0.0)4100040.3scheckingtests...goodpractice(dev)4000038.9scheckingfilesin‘vignettes’.ETA4m(2/4)[3m31.1s]goodpractice(v1.0.0)4100040.8scheckingtests...goodpractice(dev)4100039.4scheckingtests...ETA4m(2/4)[3m31.7s]goodpractice(v1.0.0)4100041.4scheckingtests...goodpractice(dev)4100040.0scheckingtests...ETA4m(2/4)[3m32.2s]goodpractice(v1.0.0)4100041.9scheckingtests...goodpractice(dev)4100040.5scheckingtests...ETA4m(2/4)[3m32.7s]goodpractice(v1.0.0)4100042.5scheckingtests...goodpractice(dev)4100041.3scheckingtests...ETA4m(2/4)[3m33.5s]goodpractice(v1.0.0)4100043.2scheckingtests...goodpractice(dev)4100041.8scheckingtests...ETA4m(2/4)[3m34.1s]goodpractice(v1.0.0)4100043.7scheckingtests...goodpractice(dev)4100042.4scheckingtests...ETA4m(2/4)[3m34.6s]goodpractice(v1.0.0)4100044.2scheckingtests...goodpractice(dev)4100042.8scheckingtests...ETA4m(2/4)[3m35.1s]goodpractice(v1.0.0)4100044.8scheckingtests...goodpractice(dev)4100043.4scheckingtests...ETA4m(2/4)[3m35.6s]goodpractice(v1.0.0)4100045.2scheckingtests...goodpractice(dev)4100043.8scheckingtests...ETA4m(2/4)[3m36.1s]goodpractice(v1.0.0)4100045.7scheckingtests...goodpractice(dev)4100044.3scheckingtests...ETA4m(2/4)[3m36.5s]goodpractice(v1.0.0)4100046.1scheckingtests...goodpractice(dev)4100044.8scheckingtests...ETA4m(2/4)[3m37s]goodpractice(v1.0.0)4100046.6scheckingtests...goodpractice(dev)4100045.3scheckingtests...ETA4m(2/4)[3m37.5s]goodpractice(v1.0.0)4100047.2scheckingtests...goodpractice(dev)4100045.8scheckingtests...ETA4m(2/4)[3m38s]goodpractice(v1.0.0)4100047.6scheckingtests...goodpractice(dev)4100046.3scheckingtests...ETA4m(2/4)[3m38.5s]goodpractice(v1.0.0)4100048.1scheckingtests...goodpractice(dev)4100046.8scheckingtests...ETA4m(2/4)[3m39s]goodpractice(v1.0.0)4100048.6scheckingtests...goodpractice(dev)4100047.3scheckingtests...ETA4m(2/4)[3m39.5s]goodpractice(v1.0.0)4100049.2scheckingtests...goodpractice(dev)4100047.8scheckingtests...ETA4m(2/4)[3m40.1s]goodpractice(v1.0.0)4200049.7sgoodpractice(dev)4100048.3scheckingtests...ETA4m(2/4)[3m40.6s]goodpractice(v1.0.0)4200050.0sgoodpractice(dev)4200048.8sETA1m(3/4)[3m41s]goodpractice(dev)4200049.0sFinishedin3m41.5sETA?(0/4)[4.4s]installingdiffviewer,praise(dev) \ No newline at end of file diff --git a/man/figures/README/ansi-tty-example.svg b/man/figures/README/ansi-tty-example.svg deleted file mode 100644 index 8a3162b..0000000 --- a/man/figures/README/ansi-tty-example.svg +++ /dev/null @@ -1 +0,0 @@ -ETA?(0/4)[1.1s]installingpraise(release),diffviewerETA?(0/4)[1.4s]installingpraise(release),diffviewerETA?(0/4)[1.6s]installingpraise(release),diffviewerETA?(0/4)[1.8s]installingpraise(release),diffviewerETA?(0/4)[2.1s]installingpraise(release),diffviewerETA?(0/4)[2.3s]installingpraise(release),diffviewerETA?(0/4)[2.5s]installingpraise(release),diffviewerETA?(0/4)[2.7s]installingpraise(release),diffviewerETA?(0/4)[3s]installingpraise(release),diffviewerETA?(0/4)[3.2s]installingpraise(release),diffviewerETA?(0/4)[3.4s]installingpraise(release),diffviewerETA?(0/4)[3.6s]installingpraise(release),diffviewerETA?(0/4)[3.9s]installingdiffviewer,praise(dev)ETA?(0/4)[4.2s]installingdiffviewer,praise(dev)SOKNWEtestthat(v1.0.0)00000.9sstarting...ETA?(0/4)[7.3s]installingpraise(dev)testthat(v1.0.0)20002.7scheckingpackagenamespaceinfortestthat(dev)00001.0sstarting...ETA?(0/4)[9.2s]testthat(v1.0.0)50003.1scheckingifthereisanamespacetestthat(dev)00001.4sstarting...ETA?(0/4)[9.7s]testthat(v1.0.0)50003.6scheckingifthereisanamespacetestthat(dev)00001.8sstarting...ETA?(0/4)[10s]testthat(v1.0.0)50003.9scheckingifthereisanamespacetestthat(dev)20002.1scheckingpackagenamespaceinforETA?(0/4)[10.3s]testthat(v1.0.0)50004.3scheckingifthereisanamespacetestthat(dev)20002.5scheckingpackagenamespaceinforETA?(0/4)[10.7s]testthat(v1.0.0)50004.6scheckingifthereisanamespacetestthat(dev)20002.9scheckingpackagenamespaceinforETA?(0/4)[11.1s]testthat(v1.0.0)90005.0scheckingforsufficient/correcttestthat(dev)20003.3scheckingpackagenamespaceinforETA?(0/4)[11.5s]testthat(v1.0.0)90005.4scheckingforsufficient/correcttestthat(dev)50003.6scheckingifthereisanamespaceETA?(0/4)[11.8s]testthat(v1.0.0)90005.7scheckingforsufficient/correcttestthat(dev)50003.9scheckingifthereisanamespaceETA?(0/4)[12.1s]testthat(v1.0.0)90006.1scheckingforsufficient/correcttestthat(dev)50004.3scheckingifthereisanamespaceETA?(0/4)[12.5s]testthat(v1.0.0)90006.4scheckingforsufficient/correcttestthat(dev)50004.7scheckingifthereisanamespaceETA?(0/4)[12.9s]testthat(v1.0.0)90006.8scheckingforsufficient/correcttestthat(dev)50005.0scheckingifthereisanamespaceETA?(0/4)[13.2s]testthat(v1.0.0)90007.1scheckingforsufficient/correcttestthat(dev)90005.3scheckingforsufficient/correctETA?(0/4)[13.5s]testthat(v1.0.0)90007.5scheckingforsufficient/correcttestthat(dev)90005.7scheckingforsufficient/correctETA?(0/4)[14s]testthat(v1.0.0)90007.9scheckingforsufficient/correcttestthat(dev)90006.1scheckingforsufficient/correctETA?(0/4)[14.3s]testthat(v1.0.0)90008.3scheckingforsufficient/correcttestthat(dev)90006.5scheckingforsufficient/correctETA?(0/4)[14.7s]testthat(v1.0.0)90008.6scheckingforsufficient/correcttestthat(dev)90006.8scheckingforsufficient/correctETA?(0/4)[15s]testthat(v1.0.0)90008.9scheckingforsufficient/correcttestthat(dev)90007.1scheckingforsufficient/correctETA?(0/4)[15.3s]testthat(v1.0.0)90009.3scheckingforsufficient/correcttestthat(dev)90007.5scheckingforsufficient/correctETA?(0/4)[15.7s]testthat(v1.0.0)90009.6scheckingforsufficient/correcttestthat(dev)90007.8scheckingforsufficient/correctETA?(0/4)[16s]testthat(v1.0.0)90009.9scheckingforsufficient/correcttestthat(dev)90008.1scheckingforsufficient/correctETA?(0/4)[16.3s]testthat(v1.0.0)900010.3scheckingforsufficient/correcttestthat(dev)90008.5scheckingforsufficient/correctETA?(0/4)[16.7s]testthat(v1.0.0)900010.6scheckingforsufficient/correcttestthat(dev)90008.8scheckingforsufficient/correctETA?(0/4)[17s]testthat(v1.0.0)900011.0scheckingforsufficient/correcttestthat(dev)90009.2scheckingforsufficient/correctETA?(0/4)[17.4s]testthat(v1.0.0)900011.4scheckingforsufficient/correcttestthat(dev)90009.6scheckingforsufficient/correctETA?(0/4)[17.8s]testthat(v1.0.0)900011.8scheckingforsufficient/correcttestthat(dev)900010.0scheckingforsufficient/correctETA?(0/4)[18.2s]testthat(v1.0.0)900012.1scheckingforsufficient/correcttestthat(dev)900010.3scheckingforsufficient/correctETA?(0/4)[18.6s]testthat(v1.0.0)900012.5scheckingforsufficient/correcttestthat(dev)900010.7scheckingforsufficient/correctETA?(0/4)[18.9s]testthat(v1.0.0)900012.8scheckingforsufficient/correcttestthat(dev)900011.0scheckingforsufficient/correctETA?(0/4)[19.2s]testthat(v1.0.0)900013.2scheckingforsufficient/correcttestthat(dev)900011.4scheckingforsufficient/correctETA?(0/4)[19.6s]testthat(v1.0.0)900013.5scheckingforsufficient/correcttestthat(dev)900011.7scheckingforsufficient/correctETA?(0/4)[19.9s]testthat(v1.0.0)900013.9scheckingforsufficient/correcttestthat(dev)900012.0scheckingforsufficient/correctETA?(0/4)[20.2s]testthat(v1.0.0)900014.2scheckingforsufficient/correcttestthat(dev)900012.4scheckingforsufficient/correctETA?(0/4)[20.6s]testthat(v1.0.0)900014.5scheckingforsufficient/correcttestthat(dev)900012.7scheckingforsufficient/correctETA?(0/4)[20.9s]testthat(v1.0.0)900014.8scheckingforsufficient/correcttestthat(dev)900013.0scheckingforsufficient/correctETA?(0/4)[21.2s]testthat(v1.0.0)900015.2scheckingforsufficient/correcttestthat(dev)900013.4scheckingforsufficient/correctETA?(0/4)[21.6s]testthat(v1.0.0)900015.5scheckingforsufficient/correcttestthat(dev)900013.8scheckingforsufficient/correctETA?(0/4)[22s]testthat(v1.0.0)900015.9scheckingforsufficient/correcttestthat(dev)900014.1scheckingforsufficient/correctETA?(0/4)[22.3s]testthat(v1.0.0)900016.2scheckingforsufficient/correcttestthat(dev)900014.4scheckingforsufficient/correctETA?(0/4)[22.6s]testthat(v1.0.0)900016.6scheckingforsufficient/correcttestthat(dev)900014.8scheckingforsufficient/correctETA?(0/4)[23s]testthat(v1.0.0)900016.9scheckingforsufficient/correcttestthat(dev)900015.1scheckingforsufficient/correctETA?(0/4)[23.3s]testthat(v1.0.0)900017.3scheckingforsufficient/correcttestthat(dev)900015.5scheckingforsufficient/correctETA?(0/4)[23.6s]testthat(v1.0.0)900017.6scheckingforsufficient/correcttestthat(dev)900015.8scheckingforsufficient/correctETA?(0/4)[24s]testthat(v1.0.0)900018.0scheckingforsufficient/correcttestthat(dev)900016.1scheckingforsufficient/correctETA?(0/4)[24.3s]testthat(v1.0.0)900018.3scheckingforsufficient/correcttestthat(dev)900016.5scheckingforsufficient/correctETA?(0/4)[24.7s]testthat(v1.0.0)900018.6scheckingforsufficient/correcttestthat(dev)900016.8scheckingforsufficient/correctETA?(0/4)[25s]testthat(v1.0.0)900019.0scheckingforsufficient/correcttestthat(dev)900017.2scheckingforsufficient/correctETA?(0/4)[25.4s]testthat(v1.0.0)900019.3scheckingforsufficient/correcttestthat(dev)900017.5scheckingforsufficient/correctETA?(0/4)[25.8s]testthat(v1.0.0)900019.8scheckingforsufficient/correcttestthat(dev)900018.0scheckingforsufficient/correctETA?(0/4)[26.3s]testthat(v1.0.0)900020.2scheckingforsufficient/correcttestthat(dev)900018.4scheckingforsufficient/correctETA?(0/4)[26.6s]testthat(v1.0.0)900020.6scheckingforsufficient/correcttestthat(dev)900018.8scheckingforsufficient/correctETA?(0/4)[27s]testthat(v1.0.0)900021.0scheckingforsufficient/correcttestthat(dev)900019.2scheckingforsufficient/correctETA?(0/4)[27.5s]testthat(v1.0.0)900021.4scheckingforsufficient/correcttestthat(dev)900019.6scheckingforsufficient/correctETA?(0/4)[27.9s]testthat(v1.0.0)900021.8scheckingforsufficient/correcttestthat(dev)900020.1scheckingforsufficient/correctETA?(0/4)[28.3s]testthat(v1.0.0)900022.2scheckingforsufficient/correcttestthat(dev)900020.5scheckingforsufficient/correctETA?(0/4)[28.8s]testthat(v1.0.0)900022.7scheckingforsufficient/correcttestthat(dev)900020.9scheckingforsufficient/correctETA?(0/4)[29.2s]testthat(v1.0.0)900023.1scheckingforsufficient/correcttestthat(dev)900021.4scheckingforsufficient/correctETA?(0/4)[29.6s]testthat(v1.0.0)900023.6scheckingforsufficient/correcttestthat(dev)900021.8scheckingforsufficient/correctETA?(0/4)[30s]testthat(v1.0.0)900024.0scheckingforsufficient/correcttestthat(dev)900022.2scheckingforsufficient/correctETA?(0/4)[30.4s]testthat(v1.0.0)900024.3scheckingforsufficient/correcttestthat(dev)900022.5scheckingforsufficient/correctETA?(0/4)[30.7s]testthat(v1.0.0)900024.6scheckingforsufficient/correcttestthat(dev)900022.9scheckingforsufficient/correctETA?(0/4)[31.1s]testthat(v1.0.0)900025.0scheckingforsufficient/correcttestthat(dev)900023.2scheckingforsufficient/correctETA?(0/4)[31.4s]testthat(v1.0.0)900025.3scheckingforsufficient/correcttestthat(dev)900023.6scheckingforsufficient/correctETA?(0/4)[31.8s]testthat(v1.0.0)900025.7scheckingforsufficient/correcttestthat(dev)900023.9scheckingforsufficient/correctETA?(0/4)[32.2s]testthat(v1.0.0)900026.1scheckingforsufficient/correcttestthat(dev)900024.3scheckingforsufficient/correctETA?(0/4)[32.5s]testthat(v1.0.0)900026.4scheckingforsufficient/correcttestthat(dev)900024.6scheckingforsufficient/correctETA?(0/4)[32.8s]testthat(v1.0.0)900026.8scheckingforsufficient/correcttestthat(dev)900025.0scheckingforsufficient/correctETA?(0/4)[33.2s]testthat(v1.0.0)900027.1scheckingforsufficient/correcttestthat(dev)900025.3scheckingforsufficient/correctETA?(0/4)[33.5s]testthat(v1.0.0)900027.5scheckingforsufficient/correcttestthat(dev)900025.7scheckingforsufficient/correctETA?(0/4)[33.9s]testthat(v1.0.0)900027.8scheckingforsufficient/correcttestthat(dev)900026.0scheckingforsufficient/correctETA?(0/4)[34.2s]testthat(v1.0.0)900028.1scheckingforsufficient/correcttestthat(dev)900026.3scheckingforsufficient/correctETA?(0/4)[34.5s]testthat(v1.0.0)900028.4scheckingforsufficient/correcttestthat(dev)900026.6scheckingforsufficient/correctETA?(0/4)[34.8s]testthat(v1.0.0)900028.8scheckingforsufficient/correcttestthat(dev)900027.0scheckingforsufficient/correctETA?(0/4)[35.2s]testthat(v1.0.0)900029.2scheckingforsufficient/correcttestthat(dev)900027.4scheckingforsufficient/correctETA?(0/4)[35.6s]testthat(v1.0.0)900029.6scheckingforsufficient/correcttestthat(dev)900027.8scheckingforsufficient/correctETA?(0/4)[36s]testthat(v1.0.0)900030.0scheckingforsufficient/correcttestthat(dev)900028.2scheckingforsufficient/correctETA?(0/4)[36.4s]testthat(v1.0.0)900030.3scheckingforsufficient/correcttestthat(dev)900028.5scheckingforsufficient/correctETA?(0/4)[36.7s]testthat(v1.0.0)900030.7scheckingforsufficient/correcttestthat(dev)900028.9scheckingforsufficient/correctETA?(0/4)[37.1s]testthat(v1.0.0)900031.0scheckingforsufficient/correcttestthat(dev)900029.2scheckingforsufficient/correctETA?(0/4)[37.4s]testthat(v1.0.0)900031.3scheckingforsufficient/correcttestthat(dev)900029.5scheckingforsufficient/correctETA?(0/4)[37.7s]testthat(v1.0.0)900031.7scheckingforsufficient/correcttestthat(dev)900029.9scheckingforsufficient/correctETA?(0/4)[38.2s]testthat(v1.0.0)900032.1scheckingforsufficient/correcttestthat(dev)900030.3scheckingforsufficient/correctETA?(0/4)[38.5s]testthat(v1.0.0)900032.4scheckingforsufficient/correcttestthat(dev)900030.6scheckingforsufficient/correctETA?(0/4)[38.8s]testthat(v1.0.0)900032.7scheckingforsufficient/correcttestthat(dev)900030.9scheckingforsufficient/correctETA?(0/4)[39.1s]testthat(v1.0.0)900033.1scheckingforsufficient/correcttestthat(dev)900031.3scheckingforsufficient/correctETA?(0/4)[39.5s]testthat(v1.0.0)900033.5scheckingforsufficient/correcttestthat(dev)900031.7scheckingforsufficient/correctETA?(0/4)[39.9s]testthat(v1.0.0)900033.8scheckingforsufficient/correcttestthat(dev)900032.0scheckingforsufficient/correctETA?(0/4)[40.2s]testthat(v1.0.0)900034.1scheckingforsufficient/correcttestthat(dev)900032.3scheckingforsufficient/correctETA?(0/4)[40.5s]testthat(v1.0.0)900034.5scheckingforsufficient/correcttestthat(dev)900032.7scheckingforsufficient/correctETA?(0/4)[40.9s]testthat(v1.0.0)900034.8scheckingforsufficient/correcttestthat(dev)900033.0scheckingforsufficient/correctETA?(0/4)[41.2s]testthat(v1.0.0)900035.1s(30.1s)checkingforsufficienttestthat(dev)900033.3scheckingforsufficient/correctETA?(0/4)[41.5s]testthat(v1.0.0)900035.5s(30.4s)checkingforsufficienttestthat(dev)900033.7scheckingforsufficient/correctETA?(0/4)[41.9s]testthat(v1.0.0)900035.8s(30.8s)checkingforsufficienttestthat(dev)900034.0scheckingforsufficient/correctETA?(0/4)[42.2s]testthat(v1.0.0)900036.2s(31.1s)checkingforsufficienttestthat(dev)900034.4scheckingforsufficient/correctETA?(0/4)[42.6s]testthat(v1.0.0)900036.5s(31.5s)checkingforsufficienttestthat(dev)900034.7scheckingforsufficient/correctETA?(0/4)[42.9s]testthat(v1.0.0)900036.9s(31.8s)checkingforsufficienttestthat(dev)900035.1scheckingforsufficient/correctETA?(0/4)[43.3s]testthat(v1.0.0)900037.2s(32.2s)checkingforsufficienttestthat(dev)900035.4s(30.1s)checkingforsufficientETA?(0/4)[43.6s]testthat(v1.0.0)900037.5s(32.5s)checkingforsufficienttestthat(dev)900035.7s(30.5s)checkingforsufficientETA?(0/4)[43.9s]testthat(v1.0.0)900037.9s(32.8s)checkingforsufficienttestthat(dev)900036.1s(30.8s)checkingforsufficientETA?(0/4)[44.3s]testthat(v1.0.0)900038.2s(33.2s)checkingforsufficienttestthat(dev)900036.4s(31.1s)checkingforsufficientETA?(0/4)[44.6s]testthat(v1.0.0)900038.5s(33.5s)checkingforsufficienttestthat(dev)900036.7s(31.4s)checkingforsufficientETA?(0/4)[44.9s]testthat(v1.0.0)900038.8s(33.8s)checkingforsufficienttestthat(dev)900037.0s(31.8s)checkingforsufficientETA?(0/4)[45.2s]testthat(v1.0.0)900039.2s(34.1s)checkingforsufficienttestthat(dev)900037.4s(32.1s)checkingforsufficientETA?(0/4)[45.6s]testthat(v1.0.0)900039.6s(34.5s)checkingforsufficienttestthat(dev)900037.8s(32.5s)checkingforsufficientETA?(0/4)[46s]testthat(v1.0.0)900039.9s(34.9s)checkingforsufficienttestthat(dev)900038.1s(32.8s)checkingforsufficientETA?(0/4)[46.3s]testthat(v1.0.0)900040.2s(35.2s)checkingforsufficienttestthat(dev)900038.4s(33.2s)checkingforsufficientETA?(0/4)[46.6s]testthat(v1.0.0)900040.6s(35.5s)checkingforsufficienttestthat(dev)900038.8s(33.5s)checkingforsufficientETA?(0/4)[47s]testthat(v1.0.0)900040.9s(35.9s)checkingforsufficienttestthat(dev)900039.1s(33.8s)checkingforsufficientETA?(0/4)[47.3s]testthat(v1.0.0)900041.2s(36.2s)checkingforsufficienttestthat(dev)900039.5s(34.2s)checkingforsufficientETA?(0/4)[47.7s]testthat(v1.0.0)900041.7s(36.6s)checkingforsufficienttestthat(dev)900039.9s(34.6s)checkingforsufficientETA?(0/4)[48.1s]testthat(v1.0.0)900042.1s(37.0s)checkingforsufficienttestthat(dev)900040.3s(35.0s)checkingforsufficientETA?(0/4)[48.5s]testthat(v1.0.0)900042.5s(37.4s)checkingforsufficienttestthat(dev)900040.7s(35.4s)checkingforsufficientETA?(0/4)[48.9s]testthat(v1.0.0)900042.8s(37.8s)checkingforsufficienttestthat(dev)900041.0s(35.8s)checkingforsufficientETA?(0/4)[49.2s]testthat(v1.0.0)900043.2s(38.1s)checkingforsufficienttestthat(dev)900041.4s(36.1s)checkingforsufficientETA?(0/4)[49.6s]testthat(v1.0.0)900043.5s(38.5s)checkingforsufficienttestthat(dev)900041.7s(36.4s)checkingforsufficientETA?(0/4)[49.9s]testthat(v1.0.0)900043.8s(38.8s)checkingforsufficienttestthat(dev)900042.0s(36.8s)checkingforsufficientETA?(0/4)[50.2s]testthat(v1.0.0)900044.1s(39.1s)checkingforsufficienttestthat(dev)900042.3s(37.1s)checkingforsufficientETA?(0/4)[50.6s]testthat(v1.0.0)900044.5s(39.5s)checkingforsufficienttestthat(dev)900042.7s(37.4s)checkingforsufficientETA?(0/4)[50.9s]testthat(v1.0.0)1010044.8scheckinginstalledpackagesizetestthat(dev)900043.0s(37.7s)checkingforsufficientETA?(0/4)[51.3s]testthat(v1.0.0)1110045.2scheckingpackagedirectory...testthat(dev)900043.4s(38.2s)checkingforsufficientETA?(0/4)[51.6s]testthat(v1.0.0)1410045.6scheckingforleft-overfiles..testthat(dev)900043.8s(38.6s)checkingforsufficientETA?(0/4)[52s]testthat(v1.0.0)1510046.0scheckingindexinformation...testthat(dev)900044.2s(39.0s)checkingforsufficientETA?(0/4)[52.4s]testthat(v1.0.0)1510046.4scheckingindexinformation...testthat(dev)900044.7s(39.4s)checkingforsufficientETA?(0/4)[52.9s]testthat(v1.0.0)1510046.8scheckingindexinformation...testthat(dev)900045.0s(39.7s)checkingforsufficientETA?(0/4)[53.2s]testthat(v1.0.0)1510047.1scheckingindexinformation...testthat(dev)900045.4s(40.1s)checkingforsufficientETA?(0/4)[53.6s]testthat(v1.0.0)1710047.5scheckingcodefilesfornon-ASCtestthat(dev)1010045.7scheckinginstalledpackagesizeETA?(0/4)[54s]testthat(v1.0.0)1810047.9scheckingRfilesforsyntaxerrtestthat(dev)1110046.2scheckingpackagedirectory...ETA?(0/4)[54.4s]testthat(v1.0.0)1910048.3scheckingwhetherthepackagecatestthat(dev)1510046.6scheckingindexinformation...ETA?(0/4)[54.8s]testthat(v1.0.0)2010048.7scheckingwhetherthepackagecatestthat(dev)1510046.9scheckingindexinformation...ETA?(0/4)[55.1s]testthat(v1.0.0)2110049.1scheckingwhetherthepackagecatestthat(dev)1510047.3scheckingindexinformation...ETA?(0/4)[55.6s]testthat(v1.0.0)2210049.5scheckingwhetherthenamespacetestthat(dev)1510047.8scheckingindexinformation...ETA?(0/4)[56s]testthat(v1.0.0)2210049.9scheckingwhetherthenamespacetestthat(dev)1610048.1scheckingpackagesubdirectoriesETA?(0/4)[56.3s]testthat(v1.0.0)2310050.3scheckingwhetherthenamespacetestthat(dev)1810048.5scheckingRfilesforsyntaxerrETA?(0/4)[56.7s]testthat(v1.0.0)2410050.6scheckingloadingwithoutbeingtestthat(dev)1910048.8scheckingwhetherthepackagecaETA?(0/4)[57s]testthat(v1.0.0)2410051.0scheckingloadingwithoutbeingtestthat(dev)1910049.3scheckingwhetherthepackagecaETA?(0/4)[57.5s]testthat(v1.0.0)2410051.4scheckingloadingwithoutbeingtestthat(dev)2010049.7scheckingwhetherthepackagecaETA?(0/4)[58s]testthat(v1.0.0)2410051.9scheckingloadingwithoutbeingtestthat(dev)2110050.1scheckingwhetherthepackagecaETA?(0/4)[58.3s]testthat(v1.0.0)2410052.3scheckingloadingwithoutbeingtestthat(dev)2210050.5scheckingwhetherthenamespaceETA?(0/4)[58.7s]testthat(v1.0.0)2410052.7scheckingloadingwithoutbeingtestthat(dev)2310050.9scheckingwhetherthenamespaceETA?(0/4)[59.1s]testthat(v1.0.0)2410053.1scheckingloadingwithoutbeingtestthat(dev)2310051.3scheckingwhetherthenamespaceETA?(0/4)[59.5s]testthat(v1.0.0)2410053.4scheckingloadingwithoutbeingtestthat(dev)2410051.7scheckingloadingwithoutbeingETA?(0/4)[59.9s]testthat(v1.0.0)2510053.8scheckingdependenciesinRcodetestthat(dev)2410052.0scheckingloadingwithoutbeingETA?(0/4)[1m0.2s]testthat(v1.0.0)2510054.2scheckingdependenciesinRcodetestthat(dev)2410052.4scheckingloadingwithoutbeingETA?(0/4)[1m0.6s]testthat(v1.0.0)2510054.5scheckingdependenciesinRcodetestthat(dev)2410052.7scheckingloadingwithoutbeingETA?(0/4)[1m1s]testthat(v1.0.0)2610054.9scheckingS3generic/methodconstestthat(dev)2410053.1scheckingloadingwithoutbeingETA?(0/4)[1m1.3s]testthat(v1.0.0)2610055.2scheckingS3generic/methodconstestthat(dev)2410053.4scheckingloadingwithoutbeingETA?(0/4)[1m1.6s]testthat(v1.0.0)2710055.6scheckingreplacementfunctionstestthat(dev)2410053.8scheckingloadingwithoutbeingETA?(0/4)[1m2s]testthat(v1.0.0)2710055.9scheckingreplacementfunctionstestthat(dev)2410054.2scheckingloadingwithoutbeingETA?(0/4)[1m2.4s]testthat(v1.0.0)2710056.3scheckingreplacementfunctionstestthat(dev)2510054.5scheckingdependenciesinRcodeETA?(0/4)[1m2.7s]testthat(v1.0.0)2810056.7scheckingforeignfunctioncallstestthat(dev)2510054.9scheckingdependenciesinRcodeETA?(0/4)[1m3.1s]testthat(v1.0.0)2810057.0scheckingforeignfunctioncallstestthat(dev)2510055.3scheckingdependenciesinRcodeETA?(0/4)[1m3.5s]testthat(v1.0.0)2810057.5scheckingforeignfunctioncallstestthat(dev)2610055.7scheckingS3generic/methodconsETA?(0/4)[1m3.9s]testthat(v1.0.0)2810057.8scheckingforeignfunctioncallstestthat(dev)2710056.1scheckingreplacementfunctionsETA?(0/4)[1m4.3s]testthat(v1.0.0)2810058.2scheckingforeignfunctioncallstestthat(dev)2710056.4scheckingreplacementfunctionsETA?(0/4)[1m4.7s]testthat(v1.0.0)2810058.6scheckingforeignfunctioncallstestthat(dev)2710056.8scheckingreplacementfunctionsETA?(0/4)[1m5s]testthat(v1.0.0)2810058.9scheckingforeignfunctioncallstestthat(dev)2710057.1scheckingreplacementfunctionsETA?(0/4)[1m5.3s]testthat(v1.0.0)2810059.3scheckingforeignfunctioncallstestthat(dev)2810057.5scheckingforeignfunctioncallsETA?(0/4)[1m5.7s]testthat(v1.0.0)2810059.6scheckingforeignfunctioncallstestthat(dev)2810057.8scheckingforeignfunctioncallsETA?(0/4)[1m6s]testthat(v1.0.0)2810060.0scheckingforeignfunctioncallstestthat(dev)2810058.2scheckingforeignfunctioncallsETA?(0/4)[1m6.4s]testthat(v1.0.0)281001.0mcheckingforeignfunctioncallstestthat(dev)2810058.5scheckingforeignfunctioncallsETA?(0/4)[1m6.7s]testthat(v1.0.0)281001.0mcheckingforeignfunctioncallstestthat(dev)2810058.8scheckingforeignfunctioncallsETA?(0/4)[1m7s]testthat(v1.0.0)281001.0mcheckingforeignfunctioncallstestthat(dev)2810059.2scheckingforeignfunctioncallsETA?(0/4)[1m7.4s]testthat(v1.0.0)281001.0mcheckingforeignfunctioncallstestthat(dev)2810059.5scheckingforeignfunctioncallsETA?(0/4)[1m7.7s]testthat(dev)2810059.9scheckingforeignfunctioncallsETA?(0/4)[1m8.1s]testthat(dev)281001.0mcheckingforeignfunctioncallsETA?(0/4)[1m8.5s]testthat(dev)281001.0mcheckingforeignfunctioncallsETA?(0/4)[1m8.9s]testthat(dev)281001.0mcheckingforeignfunctioncallsETA?(0/4)[1m9.2s]testthat(v1.0.0)281001.1mcheckingforeignfunctioncallstestthat(dev)281001.0mcheckingforeignfunctioncallsETA?(0/4)[1m9.7s]testthat(v1.0.0)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m10.2s]testthat(v1.0.0)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m10.5s]testthat(v1.0.0)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m10.8s]testthat(dev)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m11.2s]testthat(dev)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m11.6s]testthat(dev)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m11.9s]testthat(dev)281001.1mcheckingforeignfunctioncallsETA?(0/4)[1m12.4s]ETA?(0/4)[1m12.7s]testthat(v1.0.0)291001.1mcheckingRcodeforpossibleproETA?(0/4)[1m13s]testthat(v1.0.0)291001.1mcheckingRcodeforpossibleproETA?(0/4)[1m13.4s]testthat(v1.0.0)311001.1mcheckingRdmetadata...ETA?(0/4)[1m13.8s]testthat(v1.0.0)311001.1mcheckingRdmetadata...ETA?(0/4)[1m14.1s]testthat(v1.0.0)311001.1mcheckingRdmetadata...ETA?(0/4)[1m14.5s]testthat(v1.0.0)321001.1mcheckingformissingdocumentatitestthat(dev)291001.1mcheckingRcodeforpossibleproETA?(0/4)[1m14.8s]testthat(v1.0.0)321001.1mcheckingformissingdocumentatitestthat(dev)291001.1mcheckingRcodeforpossibleproETA?(0/4)[1m15.2s]testthat(v1.0.0)321001.2mcheckingformissingdocumentatitestthat(dev)301001.1mcheckingRdfiles...ETA?(0/4)[1m15.6s]testthat(v1.0.0)321001.2mcheckingformissingdocumentatitestthat(dev)311001.1mcheckingRdmetadata...ETA?(0/4)[1m16s]testthat(v1.0.0)321001.2mcheckingformissingdocumentatitestthat(dev)311001.1mcheckingRdmetadata...ETA?(0/4)[1m16.4s]testthat(v1.0.0)331001.2mcheckingforcode/documentationtestthat(dev)321001.1mcheckingformissingdocumentatiETA?(0/4)[1m16.8s]testthat(v1.0.0)331001.2mcheckingforcode/documentationtestthat(dev)321001.1mcheckingformissingdocumentatiETA?(0/4)[1m17.1s]testthat(v1.0.0)331001.2mcheckingforcode/documentationtestthat(dev)321001.2mcheckingformissingdocumentatiETA?(0/4)[1m17.4s]testthat(v1.0.0)331001.2mcheckingforcode/documentationtestthat(dev)321001.2mcheckingformissingdocumentatiETA?(0/4)[1m17.8s]testthat(dev)321001.2mcheckingformissingdocumentatiETA?(0/4)[1m18.2s]testthat(v1.0.0)341001.2mcheckingRd\usagesections...testthat(dev)321001.2mcheckingformissingdocumentatiETA?(0/4)[1m18.6s]testthat(v1.0.0)381001.2mcheckinglineendingsinMakefiltestthat(dev)331001.2mcheckingforcode/documentationETA?(0/4)[1m19s]testthat(v1.0.0)431001.2mcheckingcompiledcode...testthat(dev)331001.2mcheckingforcode/documentationETA?(0/4)[1m19.4s]testthat(v1.0.0)451001.2mcheckingfilesin‘vignettes’..testthat(dev)331001.2mcheckingforcode/documentationETA?(0/4)[1m19.8s]testthat(v1.0.0)451001.2mcheckingfilesin‘vignettes’..testthat(dev)331001.2mcheckingforcode/documentationETA?(0/4)[1m20.1s]testthat(v1.0.0)451001.2mcheckingfilesin‘vignettes’..testthat(dev)341001.2mcheckingRd\usagesections...ETA?(0/4)[1m20.4s]testthat(v1.0.0)451001.2mcheckingfilesin‘vignettes’..testthat(dev)351001.2mcheckingRdcontents...ETA?(0/4)[1m20.7s]testthat(dev)381001.2mcheckinglineendingsinMakefilETA?(0/4)[1m21.1s]testthat(v1.0.0)451001.3mcheckingfilesin‘vignettes’..testthat(dev)431001.2mcheckingcompiledcode...ETA?(0/4)[1m21.5s]testthat(v1.0.0)451001.3mcheckingfilesin‘vignettes’..testthat(dev)451001.2mcheckingfilesin‘vignettes’..ETA?(0/4)[1m21.8s]testthat(v1.0.0)451001.3mcheckingfilesin‘vignettes’..testthat(dev)451001.2mcheckingfilesin‘vignettes’..ETA?(0/4)[1m22.1s]testthat(v1.0.0)451001.3mcheckingfilesin‘vignettes’..testthat(dev)451001.2mcheckingfilesin‘vignettes’..ETA?(0/4)[1m22.5s]testthat(dev)451001.2mcheckingfilesin‘vignettes’..ETA?(0/4)[1m22.8s]ETA?(0/4)[1m23.2s]testthat(dev)451001.3mcheckingfilesin‘vignettes’..ETA?(0/4)[1m23.6s]testthat(dev)451001.3mcheckingfilesin‘vignettes’..ETA?(0/4)[1m23.9s]testthat(dev)451001.3mcheckingfilesin‘vignettes’..ETA?(0/4)[1m24.3s]testthat(v1.0.0)461001.3mcheckingexamples...testthat(dev)451001.3mcheckingfilesin‘vignettes’..ETA?(0/4)[1m24.7s]testthat(v1.0.0)471001.3mcheckingtests...ETA?(0/4)[1m25.1s]testthat(v1.0.0)471001.3mcheckingtests...ETA?(0/4)[1m25.5s]testthat(v1.0.0)471001.3mcheckingtests...ETA?(0/4)[1m25.9s]testthat(v1.0.0)471001.3mcheckingtests...ETA?(0/4)[1m26.3s]testthat(dev)471001.3mcheckingforunstateddependenciETA?(0/4)[1m26.6s]testthat(dev)471001.3mcheckingtests...ETA?(0/4)[1m27s]testthat(dev)471001.3mcheckingtests...ETA?(0/4)[1m27.5s]testthat(v1.0.0)471001.4mcheckingtests...testthat(dev)471001.3mcheckingtests...ETA?(0/4)[1m27.9s]testthat(v1.0.0)471001.4mcheckingtests...testthat(dev)471001.3mcheckingtests...ETA?(0/4)[1m28.3s]testthat(v1.0.0)471001.4mcheckingtests...ETA?(0/4)[1m28.6s]testthat(v1.0.0)471001.4mcheckingtests...ETA?(0/4)[1m29s]testthat(dev)471001.4mcheckingtests...ETA?(0/4)[1m29.4s]testthat(dev)471001.4mcheckingtests...ETA?(0/4)[1m29.8s]testthat(dev)471001.4mcheckingtests...ETA?(0/4)[1m30.1s]testthat(dev)471001.4mcheckingtests...ETA?(0/4)[1m30.4s]ETA?(0/4)[1m30.8s]ETA?(0/4)[1m31.1s]ETA?(0/4)[1m31.4s]ETA?(0/4)[1m31.8s]ETA?(0/4)[1m32.1s]ETA?(0/4)[1m32.5s]ETA?(0/4)[1m32.8s]ETA?(0/4)[1m33.1s]testthat(v1.0.0)471001.5mcheckingtests...ETA?(0/4)[1m33.5s]testthat(v1.0.0)471001.5mcheckingtests...ETA?(0/4)[1m33.9s]testthat(v1.0.0)471001.5mcheckingtests...ETA?(0/4)[1m34.3s]testthat(v1.0.0)471001.5mcheckingtests...ETA?(0/4)[1m34.7s]ETA?(0/4)[1m35s]testthat(dev)471001.5mcheckingtests...ETA?(0/4)[1m35.3s]testthat(dev)471001.5mcheckingtests...ETA?(0/4)[1m35.6s]testthat(dev)471001.5mcheckingtests...ETA?(0/4)[1m36s]testthat(dev)471001.5mcheckingtests...ETA?(0/4)[1m36.3s]ETA?(0/4)[1m36.7s]ETA?(0/4)[1m37.1s]ETA?(0/4)[1m37.5s]ETA?(0/4)[1m37.8s]ETA?(0/4)[1m38.2s]ETA?(0/4)[1m38.6s]ETA?(0/4)[1m39s]ETA?(0/4)[1m39.3s]testthat(v1.0.0)471001.6mcheckingtests...ETA?(0/4)[1m39.8s]testthat(v1.0.0)471001.6mcheckingtests...ETA?(0/4)[1m40.1s]testthat(v1.0.0)471001.6mcheckingtests...ETA?(0/4)[1m40.5s]testthat(v1.0.0)471001.6mcheckingtests...ETA?(0/4)[1m40.8s]ETA?(0/4)[1m41.2s]testthat(dev)471001.6mcheckingtests...ETA?(0/4)[1m41.5s]testthat(dev)471001.6mcheckingtests...ETA?(0/4)[1m41.8s]testthat(dev)471001.6mcheckingtests...ETA?(0/4)[1m42.2s]testthat(dev)471001.6mcheckingtests...ETA?(0/4)[1m42.5s]ETA?(0/4)[1m42.9s]ETA?(0/4)[1m43.3s]ETA?(0/4)[1m43.7s]ETA?(0/4)[1m44.1s]ETA?(0/4)[1m44.5s]ETA?(0/4)[1m44.9s]ETA?(0/4)[1m45.2s]testthat(v1.0.0)471001.7mcheckingtests...ETA?(0/4)[1m45.5s]testthat(v1.0.0)471001.7mcheckingtests...ETA?(0/4)[1m46s]testthat(v1.0.0)471001.7mcheckingtests...ETA?(0/4)[1m46.3s]testthat(v1.0.0)471001.7mcheckingtests...ETA?(0/4)[1m46.7s]ETA?(0/4)[1m47.1s]testthat(dev)471001.7mcheckingtests...ETA?(0/4)[1m47.4s]testthat(dev)471001.7mcheckingtests...ETA?(0/4)[1m47.8s]testthat(dev)471001.7mcheckingtests...ETA?(0/4)[1m48.2s]testthat(dev)471001.7mcheckingtests...ETA?(0/4)[1m48.5s]ETA?(0/4)[1m49s]ETA?(0/4)[1m49.7s]ETA?(0/4)[1m50.2s]ETA?(0/4)[1m50.7s]ETA?(0/4)[1m51.2s]testthat(v1.0.0)471001.8mcheckingtests...ETA?(0/4)[1m51.7s]testthat(v1.0.0)471001.8mcheckingtests...ETA?(0/4)[1m52.3s]testthat(v1.0.0)471001.8mcheckingtests...ETA?(0/4)[1m52.6s]testthat(v1.0.0)471001.8mcheckingtests...ETA?(0/4)[1m53.1s]testthat(dev)471001.8mcheckingtests...ETA?(0/4)[1m54s]testthat(dev)471001.8mcheckingtests...ETA?(0/4)[1m54.5s]testthat(dev)471001.8mcheckingtests...ETA?(0/4)[1m54.9s]testthat(dev)471001.8mcheckingtests...ETA?(0/4)[1m55.3s]ETA?(0/4)[1m55.8s]ETA?(0/4)[1m56.3s]ETA?(0/4)[1m57.1s]testthat(v1.0.0)471001.9mcheckingtests...ETA?(0/4)[1m58.3s]testthat(v1.0.0)471001.9mcheckingtests...ETA?(0/4)[1m58.9s]testthat(v1.0.0)471001.9mcheckingtests...testthat(dev)471001.9mcheckingtests...ETA?(0/4)[1m59.8s]testthat(v1.0.0)471001.9mcheckingtests...testthat(dev)471001.9mcheckingtests...ETA?(0/4)[2m0.3s]testthat(dev)471001.9mcheckingtests...ETA?(0/4)[2m1s]testthat(dev)471001.9mcheckingtests...ETA?(0/4)[2m1.5s]ETA?(0/4)[2m2.1s]ETA?(0/4)[2m2.6s]ETA?(0/4)[2m3s]ETA?(0/4)[2m3.6s]testthat(v1.0.0)471002.0mcheckingtests...ETA?(0/4)[2m4.5s]testthat(v1.0.0)471002.0mcheckingtests...ETA?(0/4)[2m5s]testthat(v1.0.0)471002.0mcheckingtests...testthat(dev)471002.0mcheckingtests...ETA?(0/4)[2m5.6s]testthat(v1.0.0)471002.0mcheckingtests...testthat(dev)471002.0mcheckingtests...ETA?(0/4)[2m6.3s]testthat(dev)471002.0mcheckingtests...ETA?(0/4)[2m7s]testthat(dev)471002.0mcheckingtests...ETA?(0/4)[2m7.6s]ETA?(0/4)[2m8.1s]ETA?(0/4)[2m8.5s]ETA?(0/4)[2m8.9s]ETA?(0/4)[2m9.5s]testthat(v1.0.0)471002.1mcheckingtests...ETA?(0/4)[2m10.1s]testthat(v1.0.0)471002.1mcheckingtests...ETA?(0/4)[2m10.9s]testthat(v1.0.0)471002.1mcheckingtests...testthat(dev)471002.1mcheckingtests...ETA?(0/4)[2m11.8s]testthat(v1.0.0)471002.1mcheckingtests...testthat(dev)471002.1mcheckingtests...ETA?(0/4)[2m12.3s]testthat(dev)471002.1mcheckingtests...ETA?(0/4)[2m12.6s]testthat(dev)471002.1mcheckingtests...ETA?(0/4)[2m12.9s]ETA?(0/4)[2m13.3s]ETA?(0/4)[2m14.1s]ETA?(0/4)[2m14.6s]ETA?(0/4)[2m15.1s]testthat(v1.0.0)471002.2mcheckingtests...ETA?(0/4)[2m15.9s]testthat(v1.0.0)471002.2mcheckingtests...ETA?(0/4)[2m16.4s]testthat(v1.0.0)471002.2mcheckingtests...ETA?(0/4)[2m17.3s]testthat(v1.0.0)471002.2mcheckingtests...testthat(dev)471002.2mcheckingtests...ETA?(0/4)[2m18s]testthat(dev)471002.2mcheckingtests...ETA?(0/4)[2m18.6s]testthat(dev)471002.2mcheckingtests...ETA?(0/4)[2m19.2s]testthat(dev)471002.2mcheckingtests...ETA?(0/4)[2m19.9s]ETA?(0/4)[2m20.4s]ETA?(0/4)[2m21s]ETA?(0/4)[2m21.6s]testthat(v1.0.0)471002.3mcheckingtests...ETA?(0/4)[2m22.5s]testthat(v1.0.0)471002.3mcheckingtests...ETA?(0/4)[2m23.1s]testthat(v1.0.0)471002.3mcheckingtests...testthat(dev)471002.3mcheckingtests...ETA?(0/4)[2m23.7s]testthat(v1.0.0)471002.3mcheckingtests...testthat(dev)471002.3mcheckingtests...ETA?(0/4)[2m24.2s]testthat(dev)471002.3mcheckingtests...ETA?(0/4)[2m24.7s]testthat(dev)471002.3mcheckingtests...ETA?(0/4)[2m25.4s]ETA?(0/4)[2m25.9s]ETA?(0/4)[2m26.6s]ETA?(0/4)[2m27s]ETA?(0/4)[2m27.6s]testthat(v1.0.0)471002.4mcheckingtests...ETA?(0/4)[2m28.2s]testthat(v1.0.0)471002.4mcheckingtests...ETA?(0/4)[2m28.7s]testthat(v1.0.0)471002.4mcheckingtests...testthat(dev)471002.4mcheckingtests...ETA?(0/4)[2m29.4s]testthat(v1.0.0)471002.4mcheckingtests...testthat(dev)471002.4mcheckingtests...ETA?(0/4)[2m30s]testthat(dev)471002.4mcheckingtests...ETA?(0/4)[2m30.5s]testthat(dev)471002.4mcheckingtests...ETA?(0/4)[2m31.1s]ETA?(0/4)[2m31.6s]ETA?(0/4)[2m32.1s]ETA?(0/4)[2m32.5s]ETA?(0/4)[2m32.9s]ETA?(0/4)[2m33.5s]testthat(v1.0.0)471002.5mcheckingtests...ETA?(0/4)[2m34s]testthat(v1.0.0)471002.5mcheckingtests...ETA?(0/4)[2m34.4s]testthat(v1.0.0)471002.5mcheckingtests...ETA?(0/4)[2m34.8s]testthat(v1.0.0)471002.5mcheckingtests...ETA?(0/4)[2m35.2s]testthat(dev)471002.5mcheckingtests...ETA?(0/4)[2m35.6s]testthat(dev)471002.5mcheckingtests...ETA?(0/4)[2m36s]testthat(dev)471002.5mcheckingtests...ETA?(0/4)[2m36.5s]testthat(dev)471002.5mcheckingtests...ETA?(0/4)[2m36.9s]ETA?(0/4)[2m37.4s]ETA?(0/4)[2m37.9s]ETA?(0/4)[2m38.2s]ETA?(0/4)[2m38.6s]ETA?(0/4)[2m39s]ETA?(0/4)[2m39.6s]testthat(v1.0.0)471002.6mcheckingtests...ETA?(0/4)[2m40.1s]testthat(v1.0.0)471002.6mcheckingtests...ETA?(0/4)[2m40.8s]testthat(v1.0.0)471002.6mcheckingtests...testthat(dev)471002.6mcheckingtests...ETA?(0/4)[2m41.3s]testthat(v1.0.0)471002.6mcheckingtests...testthat(dev)471002.6mcheckingtests...ETA?(0/4)[2m41.7s]testthat(dev)471002.6mcheckingtests...ETA?(0/4)[2m42.3s]testthat(dev)471002.6mcheckingtests...ETA?(0/4)[2m42.8s]ETA?(0/4)[2m43.1s]ETA?(0/4)[2m43.5s]ETA?(0/4)[2m43.8s]ETA?(0/4)[2m44.2s]ETA?(0/4)[2m44.5s]ETA?(0/4)[2m44.8s]ETA?(0/4)[2m45.1s]testthat(v1.0.0)471002.7mcheckingtests...ETA?(0/4)[2m45.5s]testthat(v1.0.0)471002.7mcheckingtests...ETA?(0/4)[2m45.9s]testthat(v1.0.0)481002.7mcheckingtests...ETA?(0/4)[2m46.2s]testthat(v1.0.0)!481002.7mETA8m(1/4)[2m46.8s]installingclisymbolsETA8m(1/4)[2m47.2s]installingclisymbolstestthat(dev)481002.7mcheckingtests...ETA8m(1/4)[2m47.6s]installingclisymbolstestthat(dev)!481002.7mETA3m(2/4)[2m48s]installingclisymbolsETA3m(2/4)[2m48.4s]installingclisymbolsETA3m(2/4)[2m48.7s]installingclisymbolsETA3m(2/4)[2m49.2s]installingclisymbolsETA3m(2/4)[2m49.6s]installingclisymbolsETA3m(2/4)[2m50s]installingclisymbolsETA3m(2/4)[2m50.3s]installingclisymbolsgoodpractice(v1.0.0)00001.1sstarting...ETA3m(2/4)[2m51.9s]goodpractice(v1.0.0)20002.5scheckingpackagenamespaceinforgoodpractice(dev)00001.1sstarting...ETA3m(2/4)[2m53.4s]goodpractice(v1.0.0)20003.0scheckingpackagenamespaceinforgoodpractice(dev)20001.7scheckingpackagenamespaceinforETA3m(2/4)[2m53.9s]goodpractice(v1.0.0)50003.5scheckingifthereisanamespacegoodpractice(dev)20002.1scheckingpackagenamespaceinforETA3m(2/4)[2m54.4s]goodpractice(v1.0.0)90003.9scheckingforsufficient/correctgoodpractice(dev)20002.6scheckingpackagenamespaceinforETA3m(2/4)[2m54.8s]goodpractice(v1.0.0)90004.4scheckingforsufficient/correctgoodpractice(dev)20003.0scheckingpackagenamespaceinforETA3m(2/4)[2m55.3s]goodpractice(v1.0.0)90005.0scheckingforsufficient/correctgoodpractice(dev)50003.7scheckingifthereisanamespaceETA3m(2/4)[2m56s]goodpractice(v1.0.0)90005.6scheckingforsufficient/correctgoodpractice(dev)90004.2scheckingforsufficient/correctETA3m(2/4)[2m56.5s]goodpractice(v1.0.0)90006.1scheckingforsufficient/correctgoodpractice(dev)90004.8scheckingforsufficient/correctETA3m(2/4)[2m57s]goodpractice(v1.0.0)90006.8scheckingforsufficient/correctgoodpractice(dev)90005.5scheckingforsufficient/correctETA3m(2/4)[2m57.8s]goodpractice(v1.0.0)90007.4scheckingforsufficient/correctgoodpractice(dev)90006.0scheckingforsufficient/correctETA3m(2/4)[2m58.2s]goodpractice(v1.0.0)90008.0scheckingforsufficient/correctgoodpractice(dev)90006.6scheckingforsufficient/correctETA3m(2/4)[2m58.8s]goodpractice(v1.0.0)90008.5scheckingforsufficient/correctgoodpractice(dev)90007.1scheckingforsufficient/correctETA3m(2/4)[2m59.3s]goodpractice(v1.0.0)90009.0scheckingforsufficient/correctgoodpractice(dev)90007.6scheckingforsufficient/correctETA3m(2/4)[2m59.8s]goodpractice(v1.0.0)90009.4scheckingforsufficient/correctgoodpractice(dev)90008.1scheckingforsufficient/correctETA3m(2/4)[3m0.3s]goodpractice(v1.0.0)900010.0scheckingforsufficient/correctgoodpractice(dev)90008.6scheckingforsufficient/correctETA3m(2/4)[3m0.9s]goodpractice(v1.0.0)900010.5scheckingforsufficient/correctgoodpractice(dev)90009.1scheckingforsufficient/correctETA3m(2/4)[3m1.3s]goodpractice(v1.0.0)900011.0scheckingforsufficient/correctgoodpractice(dev)90009.6scheckingforsufficient/correctETA3m(2/4)[3m1.9s]goodpractice(v1.0.0)1500011.6scheckingforleft-overfiles..goodpractice(dev)900010.3scheckingforsufficient/correctETA3m(2/4)[3m2.6s]goodpractice(v1.0.0)1600012.3scheckingindexinformation...goodpractice(dev)900010.9scheckingforsufficient/correctETA3m(2/4)[3m3.2s]goodpractice(v1.0.0)1600012.8scheckingindexinformation...goodpractice(dev)900011.4scheckingforsufficient/correctETA3m(2/4)[3m3.6s]goodpractice(v1.0.0)1800013.3scheckingcodefilesfornon-ASCgoodpractice(dev)900011.9scheckingforsufficient/correctETA3m(2/4)[3m4.2s]goodpractice(v1.0.0)1900013.8scheckingRfilesforsyntaxerrgoodpractice(dev)900012.4scheckingforsufficient/correctETA3m(2/4)[3m4.7s]goodpractice(v1.0.0)1900014.4scheckingRfilesforsyntaxerrgoodpractice(dev)1600013.0scheckingindexinformation...ETA3m(2/4)[3m5.3s]goodpractice(v1.0.0)2000015.0scheckingwhetherthepackagecagoodpractice(dev)1600013.6scheckingindexinformation...ETA3m(2/4)[3m5.9s]goodpractice(v1.0.0)2000015.5scheckingwhetherthepackagecagoodpractice(dev)1900014.2scheckingRfilesforsyntaxerrETA3m(2/4)[3m6.5s]goodpractice(v1.0.0)2100016.1scheckingwhetherthepackagecagoodpractice(dev)1900014.7scheckingRfilesforsyntaxerrETA3m(2/4)[3m7s]goodpractice(v1.0.0)2100016.6scheckingwhetherthepackagecagoodpractice(dev)1900015.2scheckingRfilesforsyntaxerrETA3m(2/4)[3m7.4s]goodpractice(v1.0.0)2200017.1scheckingwhetherthepackagecagoodpractice(dev)2000015.7scheckingwhetherthepackagecaETA3m(2/4)[3m8s]goodpractice(v1.0.0)2200017.6scheckingwhetherthepackagecagoodpractice(dev)2100016.3scheckingwhetherthepackagecaETA3m(2/4)[3m8.5s]goodpractice(v1.0.0)2300018.1scheckingwhetherthenamespacegoodpractice(dev)2100016.8scheckingwhetherthepackagecaETA3m(2/4)[3m9s]goodpractice(v1.0.0)2300018.6scheckingwhetherthenamespacegoodpractice(dev)2200017.2scheckingwhetherthepackagecaETA3m(2/4)[3m9.5s]goodpractice(v1.0.0)2400019.2scheckingwhetherthenamespacegoodpractice(dev)2200017.8scheckingwhetherthepackagecaETA3m(2/4)[3m10.1s]goodpractice(v1.0.0)2400019.9scheckingwhetherthenamespacegoodpractice(dev)2300018.5scheckingwhetherthenamespaceETA3m(2/4)[3m10.8s]goodpractice(v1.0.0)2500020.4scheckingloadingwithoutbeinggoodpractice(dev)2300019.0scheckingwhetherthenamespaceETA3m(2/4)[3m11.2s]goodpractice(v1.0.0)2500020.8scheckingloadingwithoutbeinggoodpractice(dev)2300019.4scheckingwhetherthenamespaceETA3m(2/4)[3m11.7s]goodpractice(v1.0.0)2500021.3scheckingloadingwithoutbeinggoodpractice(dev)2400019.9scheckingwhetherthenamespaceETA3m(2/4)[3m12.1s]goodpractice(v1.0.0)2500021.8scheckingloadingwithoutbeinggoodpractice(dev)2400020.4scheckingwhetherthenamespaceETA3m(2/4)[3m12.6s]goodpractice(v1.0.0)2600022.3scheckingdependenciesinRcodegoodpractice(dev)2500020.9scheckingloadingwithoutbeingETA3m(2/4)[3m13.1s]goodpractice(v1.0.0)2600022.7scheckingdependenciesinRcodegoodpractice(dev)2500021.3scheckingloadingwithoutbeingETA3m(2/4)[3m13.5s]goodpractice(v1.0.0)2600023.2scheckingdependenciesinRcodegoodpractice(dev)2500021.9scheckingloadingwithoutbeingETA3m(2/4)[3m14.1s]goodpractice(v1.0.0)2700023.8scheckingS3generic/methodconsgoodpractice(dev)2500022.4scheckingloadingwithoutbeingETA3m(2/4)[3m14.7s]goodpractice(v1.0.0)2700024.4scheckingS3generic/methodconsgoodpractice(dev)2600023.0scheckingdependenciesinRcodeETA3m(2/4)[3m15.2s]goodpractice(v1.0.0)2800024.9scheckingreplacementfunctionsgoodpractice(dev)2600023.5scheckingdependenciesinRcodeETA3m(2/4)[3m15.8s]goodpractice(v1.0.0)2800025.4scheckingreplacementfunctionsgoodpractice(dev)2700024.0scheckingS3generic/methodconsETA3m(2/4)[3m16.4s]goodpractice(v1.0.0)2900026.0scheckingforeignfunctioncallsgoodpractice(dev)2700024.6scheckingS3generic/methodconsETA3m(2/4)[3m16.8s]goodpractice(v1.0.0)2900026.5scheckingforeignfunctioncallsgoodpractice(dev)2800025.1scheckingreplacementfunctionsETA3m(2/4)[3m17.4s]goodpractice(v1.0.0)2900027.0scheckingforeignfunctioncallsgoodpractice(dev)2800025.6scheckingreplacementfunctionsETA3m(2/4)[3m17.8s]goodpractice(v1.0.0)2900027.4scheckingforeignfunctioncallsgoodpractice(dev)2900026.1scheckingforeignfunctioncallsETA3m(2/4)[3m18.3s]goodpractice(v1.0.0)2900028.0scheckingforeignfunctioncallsgoodpractice(dev)2900026.6scheckingforeignfunctioncallsETA3m(2/4)[3m18.8s]goodpractice(v1.0.0)2900028.5scheckingforeignfunctioncallsgoodpractice(dev)2900027.1scheckingforeignfunctioncallsETA3m(2/4)[3m19.4s]goodpractice(v1.0.0)2900029.0scheckingforeignfunctioncallsgoodpractice(dev)2900027.7scheckingforeignfunctioncallsETA3m(2/4)[3m19.9s]goodpractice(v1.0.0)2900029.5scheckingforeignfunctioncallsgoodpractice(dev)2900028.1scheckingforeignfunctioncallsETA3m(2/4)[3m20.4s]goodpractice(v1.0.0)3200030.1scheckingRdmetadata...goodpractice(dev)2900028.7scheckingforeignfunctioncallsETA3m(2/4)[3m20.9s]goodpractice(v1.0.0)3200030.5scheckingRdmetadata...goodpractice(dev)2900029.1scheckingforeignfunctioncallsETA3m(2/4)[3m21.4s]goodpractice(v1.0.0)3200031.0scheckingRdmetadata...goodpractice(dev)2900029.6scheckingforeignfunctioncallsETA3m(2/4)[3m21.9s]goodpractice(v1.0.0)3300031.5scheckingformissingdocumentatgoodpractice(dev)3000030.1scheckingRcodeforpossibleprETA3m(2/4)[3m22.4s]goodpractice(v1.0.0)3300032.0scheckingformissingdocumentatgoodpractice(dev)3200030.6scheckingRdmetadata...ETA3m(2/4)[3m22.9s]goodpractice(v1.0.0)3300032.5scheckingformissingdocumentatgoodpractice(dev)3200031.1scheckingRdmetadata...ETA3m(2/4)[3m23.3s]goodpractice(v1.0.0)3300032.9scheckingformissingdocumentatgoodpractice(dev)3300031.6scheckingformissingdocumentatETA3m(2/4)[3m23.8s]goodpractice(v1.0.0)3300033.5scheckingformissingdocumentatgoodpractice(dev)3300032.1scheckingformissingdocumentatETA3m(2/4)[3m24.3s]goodpractice(v1.0.0)3400034.0scheckingformissingdocumentatgoodpractice(dev)3300032.6scheckingformissingdocumentatETA3m(2/4)[3m24.9s]goodpractice(v1.0.0)3400034.6scheckingforcode/documentationgoodpractice(dev)3300033.3scheckingformissingdocumentatETA3m(2/4)[3m25.6s]goodpractice(v1.0.0)3400035.3scheckingforcode/documentationgoodpractice(dev)3300033.9scheckingformissingdocumentatETA3m(2/4)[3m26.2s]goodpractice(v1.0.0)3500035.8scheckingRd\usagesections...goodpractice(dev)3400034.4scheckingforcode/documentationETA3m(2/4)[3m26.7s]goodpractice(v1.0.0)3700036.3scheckingforunstateddependencgoodpractice(dev)3400034.9scheckingforcode/documentationETA3m(2/4)[3m27.2s]goodpractice(v1.0.0)3900036.8scheckingfilesin‘vignettes’.goodpractice(dev)3400035.4scheckingforcode/documentationETA3m(2/4)[3m27.7s]goodpractice(v1.0.0)3900037.4scheckingfilesin‘vignettes’.goodpractice(dev)3500036.0scheckingRd\usagesections...ETA3m(2/4)[3m28.3s]goodpractice(v1.0.0)3900038.1scheckingfilesin‘vignettes’.goodpractice(dev)3700036.7scheckingforunstateddependencETA3m(2/4)[3m29s]goodpractice(v1.0.0)3900038.7scheckingfilesin‘vignettes’.goodpractice(dev)3900037.4scheckingfilesin‘vignettes’.ETA3m(2/4)[3m29.7s]goodpractice(v1.0.0)4100039.3scheckingforunstateddependencgoodpractice(dev)3900037.9scheckingfilesin‘vignettes’.ETA4m(2/4)[3m30.2s]goodpractice(v1.0.0)4100039.8scheckingtests...goodpractice(dev)3900038.4scheckingfilesin‘vignettes’.ETA4m(2/4)[3m30.7s]goodpractice(v1.0.0)4100040.3scheckingtests...goodpractice(dev)4000038.9scheckingfilesin‘vignettes’.ETA4m(2/4)[3m31.1s]goodpractice(v1.0.0)4100040.8scheckingtests...goodpractice(dev)4100039.4scheckingtests...ETA4m(2/4)[3m31.7s]goodpractice(v1.0.0)4100041.4scheckingtests...goodpractice(dev)4100040.0scheckingtests...ETA4m(2/4)[3m32.2s]goodpractice(v1.0.0)4100041.9scheckingtests...goodpractice(dev)4100040.5scheckingtests...ETA4m(2/4)[3m32.7s]goodpractice(v1.0.0)4100042.5scheckingtests...goodpractice(dev)4100041.3scheckingtests...ETA4m(2/4)[3m33.5s]goodpractice(v1.0.0)4100043.2scheckingtests...goodpractice(dev)4100041.8scheckingtests...ETA4m(2/4)[3m34.1s]goodpractice(v1.0.0)4100043.7scheckingtests...goodpractice(dev)4100042.4scheckingtests...ETA4m(2/4)[3m34.6s]goodpractice(v1.0.0)4100044.2scheckingtests...goodpractice(dev)4100042.8scheckingtests...ETA4m(2/4)[3m35.1s]goodpractice(v1.0.0)4100044.8scheckingtests...goodpractice(dev)4100043.4scheckingtests...ETA4m(2/4)[3m35.6s]goodpractice(v1.0.0)4100045.2scheckingtests...goodpractice(dev)4100043.8scheckingtests...ETA4m(2/4)[3m36.1s]goodpractice(v1.0.0)4100045.7scheckingtests...goodpractice(dev)4100044.3scheckingtests...ETA4m(2/4)[3m36.5s]goodpractice(v1.0.0)4100046.1scheckingtests...goodpractice(dev)4100044.8scheckingtests...ETA4m(2/4)[3m37s]goodpractice(v1.0.0)4100046.6scheckingtests...goodpractice(dev)4100045.3scheckingtests...ETA4m(2/4)[3m37.5s]goodpractice(v1.0.0)4100047.2scheckingtests...goodpractice(dev)4100045.8scheckingtests...ETA4m(2/4)[3m38s]goodpractice(v1.0.0)4100047.6scheckingtests...goodpractice(dev)4100046.3scheckingtests...ETA4m(2/4)[3m38.5s]goodpractice(v1.0.0)4100048.1scheckingtests...goodpractice(dev)4100046.8scheckingtests...ETA4m(2/4)[3m39s]goodpractice(v1.0.0)4100048.6scheckingtests...goodpractice(dev)4100047.3scheckingtests...ETA4m(2/4)[3m39.5s]goodpractice(v1.0.0)4100049.2scheckingtests...goodpractice(dev)4100047.8scheckingtests...ETA4m(2/4)[3m40.1s]goodpractice(v1.0.0)4200049.7sgoodpractice(dev)4100048.3scheckingtests...ETA4m(2/4)[3m40.6s]goodpractice(v1.0.0)4200050.0sgoodpractice(dev)4200048.8sETA1m(3/4)[3m41s]goodpractice(dev)4200049.0sFinishedin3m41.5sETA?(0/4)[4.4s]installingdiffviewer,praise(dev) \ No newline at end of file diff --git a/man/fmt.Rd b/man/fmt.Rd new file mode 100644 index 0000000..8413048 --- /dev/null +++ b/man/fmt.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils-cli.R +\name{fmt} +\alias{fmt} +\title{Produce cli output for a task} +\usage{ +fmt(..., g, nodes, task = NULL, .envir = parent.frame(), ansi = TRUE) +} +\arguments{ +\item{...}{params passed to \code{\link[cli:format_inline]{cli::format_inline}}.} + +\item{g}{task_graph object.} + +\item{nodes}{graph nodes to format.} + +\item{task}{task to format.} + +\item{.envir}{output environment.} + +\item{ansi}{logical whether ansi should be stripped.} +} +\description{ +Provided a task, allows for use of a handful of shorthand symbols which will +use the task as a context for formatting task fields. +} +\keyword{internal} diff --git a/man/graph_dedup_attrs.Rd b/man/graph_dedup_attrs.Rd new file mode 100644 index 0000000..5c3112e --- /dev/null +++ b/man/graph_dedup_attrs.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils-igraph.R +\name{graph_dedup_attrs} +\alias{graph_dedup_attrs} +\title{Deduplicate attributes} +\usage{ +graph_dedup_attrs(g) +} +\arguments{ +\item{g}{task_graph object} +} +\description{ +Primarily intended for cleaning up the result of an \code{\link[igraph:union]{igraph::union()}}, +which adds duplicated attributes when attributes of the same name exist in +multiple graphs. Searches for suffixes and consolidates attributes, +taking the attribute from the first non-NA value observed. +} +\keyword{internal} diff --git a/man/install_task_spec.Rd b/man/install_task.Rd similarity index 53% rename from man/install_task_spec.Rd rename to man/install_task.Rd index f439c20..0663971 100644 --- a/man/install_task_spec.Rd +++ b/man/install_task.Rd @@ -1,12 +1,20 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/task_spec.R -\name{install_task_spec} -\alias{install_task_spec} +% Please edit documentation in R/task.R +\name{install_task} +\alias{install_task} \title{Create a task to install a package and dependencies} \usage{ -install_task_spec(type = getOption("pkgType"), INSTALL_opts = NULL, ...) +install_task( + origin, + type = package_install_type(origin), + INSTALL_opts = NULL, + lib = lib_path(origin), + ... +) } \arguments{ +\item{origin}{\code{\link[=pkg_origin]{pkg_origin()}} object.} + \item{type}{character, indicating the type of package to download and install. Will be \code{"source"} except on Windows and some macOS builds: see the section on \sQuote{Binary packages} for those. @@ -22,19 +30,23 @@ install_task_spec(type = getOption("pkgType"), INSTALL_opts = NULL, ...) additional options, with names the respective package names. } -\item{...}{Additional parameters passed to \code{\link[=task_spec]{task_spec()}}} +\item{lib}{Any object that can be passed to \code{\link[=lib]{lib()}} to generate a library +path.} + +\item{...}{ + further arguments to be passed to \code{\link[utils]{download.file}}, + \code{\link[utils]{available.packages}}, or to the functions for binary + installs on macOS and Windows (which accept an argument \code{"lock"}: + see the section on \sQuote{Locking}). + } } \description{ Create a task to install a package and dependencies } \seealso{ Other tasks: -\code{\link{check_task_spec}()}, -\code{\link{checked-task-df}}, -\code{\link{custom_install_task_spec}()}, -\code{\link{rev_dep_check_tasks_df}()}, -\code{\link{revdep_check_task_spec}()}, -\code{\link{source_check_tasks_df}()}, -\code{\link{task_spec}()} +\code{\link{check_task}()}, +\code{\link{meta_task}()}, +\code{\link{task}()} } \concept{tasks} diff --git a/man/lib.Rd b/man/lib.Rd new file mode 100644 index 0000000..f317aef --- /dev/null +++ b/man/lib.Rd @@ -0,0 +1,52 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/lib.R, R/task.R +\name{lib} +\alias{lib} +\alias{lib.NULL} +\alias{lib.character} +\alias{lib.lib_path_isolated} +\alias{lib.lib_path_default} +\alias{lib.task} +\alias{lib.install_task} +\alias{lib.check_task} +\title{Get Library Location} +\usage{ +lib(x, ...) + +\method{lib}{`NULL`}(x, ...) + +\method{lib}{character}(x, ...) + +\method{lib}{lib_path_isolated}( + x, + ..., + lib.root = tempdir(), + dir_hash = hash(Sys.time(), n = 8), + name = "" +) + +\method{lib}{lib_path_default}(x, ..., lib.loc = .libPaths()) + +\method{lib}{task}(x, ...) + +\method{lib}{install_task}(x, ...) + +\method{lib}{check_task}(x, ...) +} +\arguments{ +\item{x}{An object describing a library location} + +\item{...}{additional parameters passed to methods} + +\item{lib.root}{A root directory for the isolated library.} + +\item{dir_hash}{unique identifier of the isolated library} + +\item{name}{human-readable subname of the isolated library} + +\item{lib.loc}{Library paths, defaulting to \code{\link[=.libPaths]{.libPaths()}}.} +} +\description{ +Get Library Location +} +\keyword{internal} diff --git a/man/lib_path.Rd b/man/lib_path.Rd new file mode 100644 index 0000000..0d5ddbb --- /dev/null +++ b/man/lib_path.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/lib.R +\name{lib_path} +\alias{lib_path} +\alias{lib_path_default} +\alias{lib_path_isolated} +\title{Make a Library Location} +\usage{ +lib_path(x, ..., .class = c()) + +lib_path_default(.class = c()) + +lib_path_isolated(.class = c()) +} +\arguments{ +\item{x}{A \code{\link[=pkg_origin]{pkg_origin()}} object used for default dispatch.} + +\item{...}{Additional values} + +\item{.class}{An optional subclass, used primarily for dispatch.} +} +\description{ +A description of where packages should be installed. This object provides +necessary information to determine where a package should be installed. +lib_path method creates default path handlers for given pkg origin while +lib_path_x creates an actual object. +} +\seealso{ +Other specs: +\code{\link{pkg_origin}()} +} +\concept{specs} diff --git a/man/meta_task.Rd b/man/meta_task.Rd new file mode 100644 index 0000000..0662a62 --- /dev/null +++ b/man/meta_task.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/task.R +\name{meta_task} +\alias{meta_task} +\title{Construct a 'Meta' Task} +\usage{ +meta_task(..., .subclass = NULL) +} +\arguments{ +\item{...}{Objects passed to specified class functions} + +\item{.subclass}{character name of the subclass. It will be appended with +"_meta" suffix.} +} +\description{ +Meta tasks are tasks which are not intended to perform computation. They +exist simply to provide relationships among computational tasks. +} +\seealso{ +Other tasks: +\code{\link{check_task}()}, +\code{\link{install_task}()}, +\code{\link{task}()} +} +\concept{tasks} diff --git a/man/new_check_design.Rd b/man/new_check_design.Rd deleted file mode 100644 index 0e061d2..0000000 --- a/man/new_check_design.Rd +++ /dev/null @@ -1,35 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/check_design.R -\name{new_check_design} -\alias{new_check_design} -\alias{new_rev_dep_check_design} -\title{Creating new Check Design Objects} -\usage{ -new_check_design(...) - -new_rev_dep_check_design(x, ...) -} -\arguments{ -\item{...}{Additional arguments passed to \code{\link[=new_check_design]{new_check_design()}}} - -\item{x}{A file path, passed to \code{\link[=rev_dep_check_tasks_df]{rev_dep_check_tasks_df()}}} -} -\description{ -Instantiate a check design from a path or directory. -} -\seealso{ -Other checks: -\code{\link{check_design}}, -\code{\link{check_dev_rev_deps}()}, -\code{\link{check_dir}()}, -\code{\link{check_pkgs}()}, -\code{\link{check_rev_deps}()} - -Other checks: -\code{\link{check_design}}, -\code{\link{check_dev_rev_deps}()}, -\code{\link{check_dir}()}, -\code{\link{check_pkgs}()}, -\code{\link{check_rev_deps}()} -} -\concept{checks} diff --git a/man/new_checker.Rd b/man/new_checker.Rd new file mode 100644 index 0000000..6e8c94d --- /dev/null +++ b/man/new_checker.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/checker.R +\name{new_checker} +\alias{new_checker} +\alias{new_rev_dep_checker} +\title{Creating new Check Design Objects} +\usage{ +new_checker(...) + +new_rev_dep_checker(x, ...) +} +\arguments{ +\item{...}{Additional arguments passed to \code{\link[=new_checker]{new_checker()}}} + +\item{x}{A file path, passed to \code{\link[=plan_rev_dep_checks]{plan_rev_dep_checks()}}} +} +\description{ +Instantiate a check design from a path or directory. +} +\seealso{ +Other checks: +\code{\link{check_pkgs}()}, +\code{\link{check_rev_deps}()}, +\code{\link{checker}} + +Other checks: +\code{\link{check_pkgs}()}, +\code{\link{check_rev_deps}()}, +\code{\link{checker}} +} +\concept{checks} diff --git a/man/options.Rd b/man/options.Rd index 07d12d3..c00ce40 100644 --- a/man/options.Rd +++ b/man/options.Rd @@ -26,6 +26,24 @@ tty refresh interval when reporting results in milliseconds\item{default: }{\pre \item{envvar: }{R_CHECKED_TTY_TICK_INTERVAL (evaluated if possible, raw string otherwise)} }} +\item{tty_default_height}{\describe{ +deafult tty height used for the ANSI reporter. Used only +if correct values could not be acquired with system('tput lines')\item{default: }{\preformatted{50}} +\item{option: }{checked.tty_default_height} +\item{envvar: }{R_CHECKED_TTY_DEFAULT_HEIGHT (evaluated if possible, raw string otherwise)} +}} + +\item{proactive_gc}{\describe{ +\code{logical}, indicating whether additional garbage collection should be +performed before starting a new task, if at least one process recently +finalized. This can cause the checker to orchestrate tasks slower but +is recommended to be used for designs with many sub-processes required as +native garbage collection can lag leading to memory issues. Disable only +when maximum prefromance is required and memory is not the issue.\item{default: }{\preformatted{TRUE}} +\item{option: }{checked.proactive_gc} +\item{envvar: }{R_CHECKED_PROACTIVE_GC (evaluated if possible, raw string otherwise)} +}} + \item{results_error_on}{\describe{ character vector indicating whether R error should be thrown when issues are discovered when generating results. "never" means that no errors @@ -52,9 +70,19 @@ progress from the same \code{output}\item{default: }{\preformatted{NA}} \item{envvar: }{R_CHECKED_RESTORE (evaluated if possible, raw string otherwise)} }} +\item{add_remotes}{\describe{ +\code{logical} indicating whether origins inheriting from \code{pkg_origin_local}, +should be scanned for packages in the \code{remotes} field and added while +constrocuting a plan \code{task_grap}\item{default: }{\preformatted{TRUE}} +\item{option: }{checked.add_remotes} +\item{envvar: }{R_CHECKED_ADD_REMOTES (evaluated if possible, raw string otherwise)} +}} + \item{check_envvars}{\describe{ -named \code{character} vector of environment variables to use during R CMD check.\item{default: }{\preformatted{c(`_R_CHECK_FORCE_SUGGESTS_` = FALSE, `_R_CHECK_RD_XREFS_` = FALSE, - `_R_CHECK_SYSTEM_CLOCK_` = FALSE, `_R_CHECK_SUGGESTS_ONLY_` = TRUE)}} +named \code{character} vector of environment variables to use during +the R CMD check.\item{default: }{\preformatted{c(`_R_CHECK_FORCE_SUGGESTS_` = "false", `_R_CHECK_RD_XREFS_` = "false", + `_R_CHECK_SYSTEM_CLOCK_` = "false", `_R_CHECK_SUGGESTS_ONLY_` = "true", + `_R_CHECK_CRAN_INCOMING_` = "false")}} \item{option: }{checked.check_envvars} \item{envvar: }{R_CHECKED_CHECK_ENVVARS (evaluated if possible, raw string otherwise)} }} @@ -66,7 +94,7 @@ named \code{character} vector of environment variables to use during R CMD check }} \item{check_args}{\describe{ -\code{character} vector of args passed to the R CMD check.\item{default: }{\preformatted{c("--timings", "--ignore-vignettes", "--no-manual")}} +\code{character} vector of args passed to the R CMD check.\item{default: }{\preformatted{c("--timings", "--ignore-vignettes", "--no-manual", "--as-cran")}} \item{option: }{checked.check_args} \item{envvar: }{R_CHECKED_CHECK_ARGS (space-separated R CMD check flags)} }} diff --git a/man/options_params.Rd b/man/options_params.Rd index e9f6d16..4e68647 100644 --- a/man/options_params.Rd +++ b/man/options_params.Rd @@ -4,19 +4,31 @@ \alias{options_params} \title{Checked Options} \arguments{ +\item{proactive_gc}{\code{logical}, indicating whether additional garbage collection should be +performed before starting a new task, if at least one process recently +finalized. This can cause the checker to orchestrate tasks slower but +is recommended to be used for designs with many sub-processes required as +native garbage collection can lag leading to memory issues. Disable only +when maximum prefromance is required and memory is not the issue. (Defaults to \code{TRUE}, overwritable using option 'checked.proactive_gc' or environment variable 'R_CHECKED_PROACTIVE_GC')} + \item{results_error_on}{character vector indicating whether R error should be thrown when issues are discovered when generating results. "never" means that no errors are thrown. If "issues" then errors are emitted only on issues, whereas "potential issues" stands for error on both issues and potential issues. (Defaults to \code{"never"}, overwritable using option 'checked.results_error_on' or environment variable 'R_CHECKED_RESULTS_ERROR_ON')} -\item{check_args}{\code{character} vector of args passed to the R CMD check. (Defaults to \code{c("--timings", "--ignore-vignettes", "--no-manual")}, overwritable using option 'checked.check_args' or environment variable 'R_CHECKED_CHECK_ARGS')} +\item{check_args}{\code{character} vector of args passed to the R CMD check. (Defaults to \code{c("--timings", "--ignore-vignettes", "--no-manual", "--as-cran")}, overwritable using option 'checked.check_args' or environment variable 'R_CHECKED_CHECK_ARGS')} \item{results_keep}{character vector indicating which packages should be included in the results. "all" means that all packages are kept. If "issues" then only packages with issues identified, whereas "potential_issues" stands for keeping packages with both "issues" and "potential_issues". (Defaults to \code{"all"}, overwritable using option 'checked.results_keep' or environment variable 'R_CHECKED_RESULTS_KEEP')} -\item{check_envvars}{named \code{character} vector of environment variables to use during R CMD check. (Defaults to \verb{c(}\emph{R_CHECK_FORCE_SUGGESTS}\verb{= FALSE,}\emph{R_CHECK_RD_XREFS}\verb{= FALSE, ; }\emph{R_CHECK_SYSTEM_CLOCK}\verb{= FALSE,}\emph{R_CHECK_SUGGESTS_ONLY}\verb{ = TRUE)}, overwritable using option 'checked.check_envvars' or environment variable 'R_CHECKED_CHECK_ENVVARS')} +\item{add_remotes}{\code{logical} indicating whether origins inheriting from \code{pkg_origin_local}, +should be scanned for packages in the \code{remotes} field and added while +constrocuting a plan \code{task_grap} (Defaults to \code{TRUE}, overwritable using option 'checked.add_remotes' or environment variable 'R_CHECKED_ADD_REMOTES')} + +\item{check_envvars}{named \code{character} vector of environment variables to use during +the R CMD check. (Defaults to \verb{c(}\emph{R_CHECK_FORCE_SUGGESTS}\verb{= "false",}\emph{R_CHECK_RD_XREFS}\verb{= "false", ; }\emph{R_CHECK_SYSTEM_CLOCK}\verb{= "false",}\emph{R_CHECK_SUGGESTS_ONLY}\verb{= "true", ; }\emph{R_CHECK_CRAN_INCOMING}\verb{ = "false")}, overwritable using option 'checked.check_envvars' or environment variable 'R_CHECKED_CHECK_ENVVARS')} \item{tty_tick_interval}{tty refresh interval when reporting results in milliseconds (Defaults to \code{0.1}, overwritable using option 'checked.tty_tick_interval' or environment variable 'R_CHECKED_TTY_TICK_INTERVAL')} @@ -25,6 +37,9 @@ packages with both "issues" and "potential_issues". (Defaults to \code{"all"}, o \item{restore}{\code{logical} indicating whether output directory should be unlinked before running checks. If \code{FALSE}, an attempt will me made to restore previous progress from the same \code{output} (Defaults to \code{NA}, overwritable using option 'checked.restore' or environment variable 'R_CHECKED_RESTORE')} + +\item{tty_default_height}{deafult tty height used for the ANSI reporter. Used only +if correct values could not be acquired with system('tput lines') (Defaults to \code{50}, overwritable using option 'checked.tty_default_height' or environment variable 'R_CHECKED_TTY_DEFAULT_HEIGHT')} } \description{ Checked Options diff --git a/man/package_spec.Rd b/man/package_spec.Rd deleted file mode 100644 index d2a4fe4..0000000 --- a/man/package_spec.Rd +++ /dev/null @@ -1,39 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/package_spec.R -\name{package_spec} -\alias{package_spec} -\alias{package_spec_source} -\alias{package_spec_archive_source} -\title{Package specification} -\usage{ -package_spec( - name = NULL, - repos = NULL, - version = numeric_version("0.0"), - op = ">" -) - -package_spec_source(path = NULL, ...) - -package_spec_archive_source(path = NULL, ...) -} -\arguments{ -\item{name}{name of the package.} - -\item{repos}{repository where package with given name should identified.} - -\item{version}{package_version object specifying minimal version required -by packages depending on this package.} - -\item{op}{operator used with \code{version}.} - -\item{path}{path to the source of the package (either bundled or not). URLs -are acceptable.} - -\item{...}{parameters passed to downstream constructors} -} -\description{ -Create package specification list which consists of all the details required -to identify and acquire source of the package. -} -\concept{specs} diff --git a/man/path_parts.Rd b/man/path_parts.Rd new file mode 100644 index 0000000..93aa803 --- /dev/null +++ b/man/path_parts.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils-paths.R +\name{path_parts} +\alias{path_parts} +\title{Split a Filepath into Parts} +\usage{ +path_parts(x) +} +\arguments{ +\item{x}{A \code{character(1L)} or \code{filepath}} +} +\value{ +A \code{character} vector of path parts +} +\description{ +Split a Filepath into Parts +} +\keyword{internal} diff --git a/man/pkg_dependencies.Rd b/man/pkg_dependencies.Rd new file mode 100644 index 0000000..b1404d4 --- /dev/null +++ b/man/pkg_dependencies.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils-deps.R +\name{pkg_dependencies} +\alias{pkg_dependencies} +\title{Build Package Dependencies Table} +\usage{ +pkg_dependencies( + packages, + dependencies = TRUE, + db = available_packages(), + verbose = FALSE +) +} +\arguments{ +\item{packages}{a character vector of package names.} + +\item{dependencies}{A \code{logical} scalar, \code{character} string of +\code{"all"}, \code{"most"}, \code{"hard"} or \code{"soft"}, \code{NA} or a vector of dependency +types compatible with \code{\link[=as_pkg_dependencies]{as_pkg_dependencies()}} function.} + +\item{db}{character matrix as from \code{\link{available.packages}()} + (with the default \code{NULL} the results of this call) + or data frame variants thereof. Alternatively, a package database + like the one available from + \url{https://cran.r-project.org/web/packages/packages.rds}. + } + +\item{verbose}{logical indicating if output should monitor the package + search cycles.} +} +\description{ +Inspired by \code{tools::package_dependencies}, but with the added benefit +of recording the dependency type and relationships throughout the +dependency tree. +} +\keyword{internal} diff --git a/man/pkg_origin.Rd b/man/pkg_origin.Rd new file mode 100644 index 0000000..333ced9 --- /dev/null +++ b/man/pkg_origin.Rd @@ -0,0 +1,53 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/pkg_origin.R +\name{pkg_origin} +\alias{pkg_origin} +\alias{pkg_origin_repo} +\alias{pkg_origin_is_base} +\alias{pkg_origin_base} +\alias{pkg_origin_unknown} +\alias{pkg_origin_local} +\alias{pkg_origin_remote} +\alias{pkg_origin_archive} +\title{Package specification} +\usage{ +pkg_origin(package, ..., .class = c()) + +pkg_origin_repo(package, repos, ...) + +pkg_origin_is_base(package, ...) + +pkg_origin_base(package, ...) + +pkg_origin_unknown(package, ...) + +pkg_origin_local(path = NULL, ...) + +pkg_origin_remote(remote = NULL, ...) + +pkg_origin_archive(path = NULL, ...) +} +\arguments{ +\item{package}{name of the package.} + +\item{...}{parameters passed to downstream constructors.} + +\item{.class}{Additional subclasses.} + +\item{repos}{repository where package with given name should identified.} + +\item{path}{path to the source of the package (either bundled or not). URLs +are acceptable.} + +\item{remote}{remote object from the \code{remotes} package used to identify +non-standard packages.} +} +\description{ +Create package specification list which consists of all the details required +to identify and acquire source of the package. +} +\seealso{ +Other specs: +\code{\link{lib_path}()} +} +\concept{specs} diff --git a/man/plan_local_checks.Rd b/man/plan_local_checks.Rd new file mode 100644 index 0000000..34f3e14 --- /dev/null +++ b/man/plan_local_checks.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/plan.R +\name{plan_local_checks} +\alias{plan_local_checks} +\title{Plan R CMD Checks} +\usage{ +plan_local_checks(package, repos = getOption("repos")) +} +\arguments{ +\item{package}{A path to either package, directory with packages or name +of the package (details)} + +\item{repos}{repository used to identify packages when name is provided.} +} +\description{ +Generates a plan for running R CMD check for a specified set of packages. +} +\details{ +\code{package} parameter has two different allowed values: +\itemize{ +\item Package - checked looks for a DESCRIPTION file in the provided path, if +found treats it like a source package. +\item If the specified value does not correspond to a source package, the +parameter is treated as the name and \code{repos} parameter is used to identify +the source. +} +} +\seealso{ +Other plan: +\code{\link{plan_local_install}()}, +\code{\link{plan_rev_dep_checks}()} +} +\concept{plan} diff --git a/man/plan_local_install.Rd b/man/plan_local_install.Rd new file mode 100644 index 0000000..8346cb5 --- /dev/null +++ b/man/plan_local_install.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/plan.R +\name{plan_local_install} +\alias{plan_local_install} +\title{Plan source package installation} +\usage{ +plan_local_install(package, repos = getOption("repos")) +} +\arguments{ +\item{package}{A path to package source.} + +\item{repos}{repository used to identify packages when name is provided.} +} +\description{ +Generates a plan for running installing a package from source. +} +\seealso{ +Other plan: +\code{\link{plan_local_checks}()}, +\code{\link{plan_rev_dep_checks}()} +} +\concept{plan} diff --git a/man/plan_rev_dep_checks.Rd b/man/plan_rev_dep_checks.Rd new file mode 100644 index 0000000..a61a5a6 --- /dev/null +++ b/man/plan_rev_dep_checks.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/plan.R +\name{plan_rev_dep_checks} +\alias{plan_rev_dep_checks} +\title{Plan Reverse Dependency Checks} +\usage{ +plan_rev_dep_checks(path, repos = getOption("repos")) +} +\arguments{ +\item{path}{path to the package source.} + +\item{repos}{repository used to identify reverse dependencies.} +} +\description{ +Generates a plan for running reverse dependency check for certain +source package. In such case \code{path} should be proivded with a directory +path to the development version of the package and \code{repos} should be a +repository for which reverse dependencies should be identified. +} +\seealso{ +Other plan: +\code{\link{plan_local_checks}()}, +\code{\link{plan_local_install}()} +} +\concept{plan} diff --git a/man/print.checked_results.Rd b/man/print.checked_results.Rd index 03f2d40..bc7cf46 100644 --- a/man/print.checked_results.Rd +++ b/man/print.checked_results.Rd @@ -2,21 +2,23 @@ % Please edit documentation in R/results.R \name{print.checked_results} \alias{print.checked_results} -\alias{print.checked_results_check_task_spec} -\alias{print.checked_results_revdep_check_task_spec} +\alias{print.rev_dep_dep_results} +\alias{print.local_check_results} \title{Print checked results} \usage{ \method{print}{checked_results}(x, ...) -\method{print}{checked_results_check_task_spec}(x, keep = options::opt("results_keep"), ...) +\method{print}{rev_dep_dep_results}(x, ..., name = NULL, keep = options::opt("results_keep")) -\method{print}{checked_results_revdep_check_task_spec}(x, ...) +\method{print}{local_check_results}(x, ..., name = NULL, keep = options::opt("results_keep")) } \arguments{ \item{x}{an object to be printed.} \item{...}{other parameters.} +\item{name}{character name of the \code{rev_dep_dep} package} + \item{keep}{character vector indicating which packages should be included in the results. "all" means that all packages are kept. If "issues" then only packages with issues identified, whereas "potential_issues" stands for keeping @@ -27,7 +29,6 @@ Print checked results } \seealso{ Other results: -\code{\link{results}()}, -\code{\link{results_to_file}()} +\code{\link{results}()} } \concept{results} diff --git a/man/reporters-internal.Rd b/man/reporters-internal.Rd index a00d14d..ce4e7ab 100644 --- a/man/reporters-internal.Rd +++ b/man/reporters-internal.Rd @@ -4,20 +4,32 @@ \alias{reporters-internal} \alias{report_sleep} \alias{report_sleep.default} -\alias{report_initialize} +\alias{report_start_setup} +\alias{report_start_checks} +\alias{report_start_checks.default} \alias{report_status} \alias{report_finalize} +\alias{report_task} +\alias{report_step} \title{Reporter Internal Methods} \usage{ -report_sleep(reporter, design, sleep) +report_sleep(reporter, checker, sleep) -\method{report_sleep}{default}(reporter, design, sleep = 1) +\method{report_sleep}{default}(reporter, checker, sleep = 1) -report_initialize(reporter, design, envir = parent.frame()) +report_start_setup(reporter, checker, ..., envir = parent.frame()) -report_status(reporter, design, envir = parent.frame()) +report_start_checks(reporter, checker, ..., envir = parent.frame()) -report_finalize(reporter, design) +\method{report_start_checks}{default}(reporter, checker, ..., envir = parent.frame()) + +report_status(reporter, checker, envir = parent.frame()) + +report_finalize(reporter, checker) + +report_task(reporter, g, v) + +report_step(reporter, checker) } \arguments{ \item{reporter}{A object produced using \code{\link{reporters}}. Each reporter is a @@ -25,7 +37,7 @@ thin wrapper around an environment with a class name for dispatch. The reporter is mutable and captures any necessary state that needs to be tracked while reporting.} -\item{design}{\code{\link{check_design}} The check design to report as it evaluates.} +\item{checker}{\code{\link{checker}} The check checker to report as it evaluates.} \item{sleep}{\code{numeric} An interval to pause between reporter steps.} @@ -34,6 +46,6 @@ hooks.} } \description{ Each of the internal methods for reporters take a reporter, the check -design object and a calling environment. +checker object and a calling environment. } \keyword{internal} diff --git a/man/reporters.Rd b/man/reporters.Rd index e008911..b2f8c13 100644 --- a/man/reporters.Rd +++ b/man/reporters.Rd @@ -3,19 +3,22 @@ \name{reporters} \alias{reporters} \alias{reporter_ansi_tty} +\alias{reporter_ansi_tty2} \alias{reporter_basic_tty} \alias{reporter_default} -\title{Check Design Runner Reporters} +\title{Check checker Runner Reporters} \usage{ reporter_ansi_tty() +reporter_ansi_tty2() + reporter_basic_tty() reporter_default() } \description{ Reporters are used to configure how output is communicated while running -a \code{\link{check_design}}. They range from glossy command-line tools intended for +a \code{\link{checker}}. They range from glossy command-line tools intended for displaying progress in an interactive R session, to line-feed logs which may be better suited for automated execution, such as in continuous itegration. diff --git a/man/results.Rd b/man/results.Rd index bd88643..b888004 100644 --- a/man/results.Rd +++ b/man/results.Rd @@ -2,15 +2,30 @@ % Please edit documentation in R/results.R \name{results} \alias{results} -\alias{results.check_design} +\alias{results.checker} +\alias{results.integer} +\alias{results.igraph.vs} +\alias{results.rev_dep_dep_meta_task} +\alias{results.rev_dep_check_meta_task} +\alias{results.local_check_meta_task} \title{Check results} \usage{ results(x, ...) -\method{results}{check_design}(x, error_on = options::opt("results_error_on"), ...) +\method{results}{checker}(x, error_on = options::opt("results_error_on"), ...) + +\method{results}{integer}(x, checker_obj, ...) + +\method{results}{igraph.vs}(x, ...) + +\method{results}{rev_dep_dep_meta_task}(x, checker_obj, ...) + +\method{results}{rev_dep_check_meta_task}(x, checker_obj, ...) + +\method{results}{local_check_meta_task}(x, checker_obj, ...) } \arguments{ -\item{x}{\code{\link[checked]{check_design}} object.} +\item{x}{object which results should be presented.} \item{...}{other parameters.} @@ -18,13 +33,14 @@ results(x, ...) are discovered when generating results. "never" means that no errors are thrown. If "issues" then errors are emitted only on issues, whereas "potential issues" stands for error on both issues and potential issues. (Defaults to \code{"never"}, overwritable using option 'checked.results_error_on' or environment variable 'R_CHECKED_RESULTS_ERROR_ON')} + +\item{checker_obj}{\code{\link{checker}} object.} } \description{ Get R CMD check results } \seealso{ Other results: -\code{\link{print.checked_results}()}, -\code{\link{results_to_file}()} +\code{\link{print.checked_results}()} } \concept{results} diff --git a/man/results_to_file.Rd b/man/results_to_file.Rd deleted file mode 100644 index ea36e8d..0000000 --- a/man/results_to_file.Rd +++ /dev/null @@ -1,30 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/results.R -\name{results_to_file} -\alias{results_to_file} -\title{Results to file} -\usage{ -results_to_file(results, file, keep = "all", ...) -} -\arguments{ -\item{results}{\code{\link[checked]{results}} object.} - -\item{file}{A connection or character path.} - -\item{keep}{character vector indicating which packages should be included in the results. -"all" means that all packages are kept. If "issues" then only packages -with issues identified, whereas "potential_issues" stands for keeping -packages with both "issues" and "potential_issues". (Defaults to \code{"all"}, overwritable using option 'checked.results_keep' or environment variable 'R_CHECKED_RESULTS_KEEP')} - -\item{...}{other parameters.} -} -\description{ -Write \code{checked_results} object to the text file. When converting results -to text, \code{\link[checked]{print.checked_results}} method is used. -} -\seealso{ -Other results: -\code{\link{print.checked_results}()}, -\code{\link{results}()} -} -\concept{results} diff --git a/man/rev_dep_check_tasks_df.Rd b/man/rev_dep_check_tasks_df.Rd deleted file mode 100644 index d62613b..0000000 --- a/man/rev_dep_check_tasks_df.Rd +++ /dev/null @@ -1,75 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/checks_df.R -\name{rev_dep_check_tasks_df} -\alias{rev_dep_check_tasks_df} -\title{Build Tasks for Reverse Dependency Checks -Generates checks schedule data.frame appropriate for running reverse -dependency check for certain source package. In such case \code{path} parameter -should point to the source of the development version of the package and -\code{repos} should be a repository for which reverse dependencies should be -identified.} -\usage{ -rev_dep_check_tasks_df( - path, - repos = getOption("repos"), - versions = c("dev", "release"), - lib.loc = .libPaths(), - ... -) -} -\arguments{ -\item{path}{path to the package source. Can be either a single source -code directory or a directory containing multiple package source code -directories.} - -\item{repos}{repository used to identify reverse dependencies.} - -\item{versions}{character vector indicating against which versions of the -package reverse dependency should be checked. \code{c("dev", "release")} -(default) stands for the classical reverse dependency check. \code{"dev"} -checks only against development version of the package which is applicable -mostly when checking whether adding new package would break tests of -packages already in the repository and take the package as suggests -dependency.} - -\item{lib.loc}{vector of libraries used to check whether reverse dependency -check can return accurate results.} - -\item{...}{parameters passed to the task specs allowing to customize -subprocesses.} -} -\value{ -The check schedule \code{data.frame} with the following columns: -\itemize{ -\item \code{alias}: The alias of the check to run. It also serves the purpose of -providing a unique identifier and node name in the task graph. -\item \code{version}: Version of the package to be checked. -\item \code{package}: Object that inherits from \code{\link[=check_task_spec]{check_task_spec()}}. -Defines how package to be checked can be acquired. -\item \code{custom}: Object that inherits from \code{\link[=custom_install_task_spec]{custom_install_task_spec()}}. -Defines custom package, for instance only available from local source, that -should be installed before checking the package. -} -} -\description{ -Create data.frame which each row defines a package for which R CMD check -should be run. Such data.frame is a prerequisite for generating -\code{\link[=check_design]{check_design()}} which orchestrates all the processes -including dependencies installation. -} -\details{ -\verb{_tasks_df()} functions generate check task \code{data.frame} for -all source packages specified by the \code{path}. Therefore it accepts it to be -a vector of an arbitrary length. -} -\seealso{ -Other tasks: -\code{\link{check_task_spec}()}, -\code{\link{checked-task-df}}, -\code{\link{custom_install_task_spec}()}, -\code{\link{install_task_spec}()}, -\code{\link{revdep_check_task_spec}()}, -\code{\link{source_check_tasks_df}()}, -\code{\link{task_spec}()} -} -\concept{tasks} diff --git a/man/revdep_check_task_spec.Rd b/man/revdep_check_task_spec.Rd deleted file mode 100644 index 96420ec..0000000 --- a/man/revdep_check_task_spec.Rd +++ /dev/null @@ -1,29 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/task_spec.R -\name{revdep_check_task_spec} -\alias{revdep_check_task_spec} -\title{Create a task to run reverse dependency checks} -\usage{ -revdep_check_task_spec(revdep, ...) -} -\arguments{ -\item{revdep}{character indicating whether the task specification describes -check associated with the development (new) or release (old) version of the -for which reverse dependency check is run.} - -\item{...}{Additional parameters passed to \code{\link[=task_spec]{task_spec()}}} -} -\description{ -Create a task to run reverse dependency checks -} -\seealso{ -Other tasks: -\code{\link{check_task_spec}()}, -\code{\link{checked-task-df}}, -\code{\link{custom_install_task_spec}()}, -\code{\link{install_task_spec}()}, -\code{\link{rev_dep_check_tasks_df}()}, -\code{\link{source_check_tasks_df}()}, -\code{\link{task_spec}()} -} -\concept{tasks} diff --git a/man/run.Rd b/man/run.Rd index 5a0cd26..f1597a2 100644 --- a/man/run.Rd +++ b/man/run.Rd @@ -4,21 +4,21 @@ \alias{run} \title{Run a Series of \verb{R CMD check}s} \usage{ -run(design, ..., reporter = reporter_default()) +run(checker, ..., reporter = reporter_default()) } \arguments{ -\item{design}{\code{character} or \code{check_design} If a \code{character} value is -provided, it is first coerced into a \code{check_design} using -\code{\link[=new_rev_dep_check_design]{new_rev_dep_check_design()}}.} +\item{checker}{\code{character} or \code{checker} If a \code{character} value is +provided, it is first coerced into a \code{checker} using +\code{\link[=new_rev_dep_checker]{new_rev_dep_checker()}}.} -\item{...}{Additional arguments passed to \code{\link[=new_rev_dep_check_design]{new_rev_dep_check_design()}}} +\item{...}{Additional arguments passed to \code{\link[=new_rev_dep_checker]{new_rev_dep_checker()}}} \item{reporter}{A reporter to provide progress updates. Will default to the most expressive command-line reporter given your terminal capabilities.} } \description{ \code{\link[=run]{run()}} provides a generic, and is the central interface for executing -\code{\link{check_design}}s. If a path is provided, a new reverse dependency check +\code{\link{checker}}s. If a path is provided, a new reverse dependency check plan is generated from the source code path. Otherwise a plan can be built separately and executed using \code{\link[=run]{run()}}. } diff --git a/man/source_check_tasks_df.Rd b/man/source_check_tasks_df.Rd deleted file mode 100644 index 2d07d39..0000000 --- a/man/source_check_tasks_df.Rd +++ /dev/null @@ -1,51 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/checks_df.R -\name{source_check_tasks_df} -\alias{source_check_tasks_df} -\title{Create a Task to Check a Package from Source} -\usage{ -source_check_tasks_df(path, ...) -} -\arguments{ -\item{path}{path to the package source. Can be either a single source -code directory or a directory containing multiple package source code -directories.} - -\item{...}{parameters passed to the task specs allowing to customize -subprocesses.} -} -\value{ -The check schedule \code{data.frame} with the following columns: -\itemize{ -\item \code{alias}: The alias of the check to run. It also serves the purpose of -providing a unique identifier and node name in the task graph. -\item \code{version}: Version of the package to be checked. -\item \code{package}: Object that inherits from \code{\link[=check_task_spec]{check_task_spec()}}. -Defines how package to be checked can be acquired. -\item \code{custom}: Object that inherits from \code{\link[=custom_install_task_spec]{custom_install_task_spec()}}. -Defines custom package, for instance only available from local source, that -should be installed before checking the package. -} -} -\description{ -Create data.frame which each row defines a package for which R CMD check -should be run. Such data.frame is a prerequisite for generating -\code{\link[=check_design]{check_design()}} which orchestrates all the processes -including dependencies installation. -} -\details{ -\verb{_tasks_df()} functions generate check task \code{data.frame} for -all source packages specified by the \code{path}. Therefore it accepts it to be -a vector of an arbitrary length. -} -\seealso{ -Other tasks: -\code{\link{check_task_spec}()}, -\code{\link{checked-task-df}}, -\code{\link{custom_install_task_spec}()}, -\code{\link{install_task_spec}()}, -\code{\link{rev_dep_check_tasks_df}()}, -\code{\link{revdep_check_task_spec}()}, -\code{\link{task_spec}()} -} -\concept{tasks} diff --git a/man/start_task.Rd b/man/start_task.Rd new file mode 100644 index 0000000..5501243 --- /dev/null +++ b/man/start_task.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/next_task.R +\name{start_task} +\alias{start_task} +\title{Start a new task} +\usage{ +start_task(node, g, ...) +} +\arguments{ +\item{node}{Node(s) for which libpath should be constructed based on \code{g}} + +\item{g}{\code{task_graph} object} + +\item{...}{additional params passed to downstream methods} +} +\description{ +Starts task based on the \code{task} object encapsulated in the \code{node} taken +from then \code{task_graph} \code{g}. It returns an \code{install_process} or +\code{check_process} \code{R6} object. +} +\keyword{internal} diff --git a/man/task.Rd b/man/task.Rd new file mode 100644 index 0000000..564921a --- /dev/null +++ b/man/task.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/task.R +\name{task} +\alias{task} +\title{Task specification} +\usage{ +task(..., .subclass = NULL) +} +\arguments{ +\item{...}{parameters passed to downstream constructors.} + +\item{.subclass}{Additional subclasses.} +} +\description{ +Create task specification list which consists of all the details required +to run specific task. +} +\details{ +Tasks can be nested, representing either a singular task, or a set of +related tasks. +} +\seealso{ +Other tasks: +\code{\link{check_task}()}, +\code{\link{install_task}()}, +\code{\link{meta_task}()} +} +\concept{tasks} diff --git a/man/task_formats.Rd b/man/task_formats.Rd new file mode 100644 index 0000000..b9eab99 --- /dev/null +++ b/man/task_formats.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils-cli.R +\name{task_formats} +\alias{task_formats} +\title{Task formatter bindings} +\usage{ +task_formats(g = NULL, nodes = V(g), task = NULL, tasks = list(task)) +} +\arguments{ +\item{g}{task_graph object.} + +\item{nodes}{graph nodes to format.} + +\item{task}{task to format.} + +\item{tasks}{currently unused.} +} +\description{ +This bit of code is intended for use with \code{\link[=fmt]{fmt()}}, and allows for us +to layer symbol bindings on top of the environment used for string +interpolation which provide syntactic sugar for common formatting components. +} +\keyword{internal} diff --git a/man/task_graph.Rd b/man/task_graph.Rd new file mode 100644 index 0000000..e01a62f --- /dev/null +++ b/man/task_graph.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/task_graph.R +\name{task_graph} +\alias{task_graph} +\title{Build task graph edges} +\usage{ +task_graph(x, repos = getOption("repos"), ...) +} +\arguments{ +\item{x}{a \code{plan} object, containing a list of related steps.} + +\item{repos}{\code{repos}, as expected by \code{\link[tools:package_dependencies]{tools::package_dependencies()}} to +determine package relationships.} + +\item{...}{params passed to helper methods.} +} +\value{ +A \code{data.frame} that can be used to build +\code{\link[igraph:make_graph]{igraph::make_graph}} edges. +} +\description{ +Edges describe relationships between tasks. Often, this is a dependency +between packages, requiring that some package be installed before a latter +task can be executed. +} +\details{ +\code{\link[tools:package_dependencies]{tools::package_dependencies()}} is used to calculate these relationships. +However, the package data returned by \code{\link[utils:available.packages]{utils::available.packages()}}, +that is used internally to determine dependencies does not know about +local or remote packages, so those are first appended to this data set +prior to calculating edges. The bulk of this function serves to join this +data. +} +\examples{ +\dontrun{ +task_graph(plan_rev_dep_checks(".")) +} +} +\keyword{internal} diff --git a/man/task_graph_create.Rd b/man/task_graph_create.Rd deleted file mode 100644 index aed0a69..0000000 --- a/man/task_graph_create.Rd +++ /dev/null @@ -1,23 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/task_graph.R -\name{task_graph_create} -\alias{task_graph_create} -\title{Create Task Graph} -\usage{ -task_graph_create(df, repos = getOption("repos")) -} -\arguments{ -\item{df}{data.frame listing} - -\item{repos}{repositories which will be used to identify dependencies chain -to run R CMD checks} -} -\value{ -A dependency graph with vertex attributes "root" (a logical value -indicating whether the package as one of the roots used to create the -graph), "status" (installation status) and "order" (installation order). -} -\description{ -Create Task Graph -} -\keyword{internal} diff --git a/man/task_graph_libpaths.Rd b/man/task_graph_libpaths.Rd new file mode 100644 index 0000000..9e8c836 --- /dev/null +++ b/man/task_graph_libpaths.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/next_task.R +\name{task_graph_libpaths} +\alias{task_graph_libpaths} +\title{Libpaths from task graph} +\usage{ +task_graph_libpaths(g, node = NULL, lib.loc = .libPaths(), output = tempdir()) +} +\arguments{ +\item{g}{\code{task_graph} object} + +\item{node}{Node(s) for which libpath should be constructed based on \code{g}} + +\item{lib.loc}{Library paths, defaulting to \code{\link[=.libPaths]{.libPaths()}}.} + +\item{output}{Path to the checked output directory} +} +\description{ +Function that traverses over the task dependency task to acquire libpaths +for given nodes. It ensures that when runing a node, a libpath is +constructed which has all the required packages on it. +} +\keyword{internal} diff --git a/man/task_graph_neighborhoods.Rd b/man/task_graph_neighborhoods.Rd index 0b56d8d..5bd9d8b 100644 --- a/man/task_graph_neighborhoods.Rd +++ b/man/task_graph_neighborhoods.Rd @@ -4,10 +4,10 @@ \alias{task_graph_neighborhoods} \title{Find Task Neighborhood} \usage{ -task_graph_neighborhoods(g, nodes) +task_graph_neighborhoods(g, nodes, ...) } \arguments{ -\item{g}{A task graph, as produced with \code{\link[=task_graph_create]{task_graph_create()}}} +\item{g}{A task graph, as produced with \code{\link[=task_graph]{task_graph()}}} \item{nodes}{Names or nodes objects of packages whose neighborhoods should be calculated.} diff --git a/man/task_graph_update_ready.Rd b/man/task_graph_update_ready.Rd index 4fd81dd..6671fd2 100644 --- a/man/task_graph_update_ready.Rd +++ b/man/task_graph_update_ready.Rd @@ -6,15 +6,15 @@ \usage{ task_graph_update_ready( g, - v = igraph::V(g), + v = V(g), dependencies = TRUE, status = STATUS$pending ) } \arguments{ -\item{g}{A dependency graph, as produced with \code{\link[=task_graph_create]{task_graph_create()}}.} +\item{g}{A dependency graph, as produced with \code{\link[=task_graph]{task_graph()}}.} -\item{v}{Names or nodes objects of packages whose readiness should be +\item{v}{Names or nodes objects of packages whose satisfiability should be checked.} \item{dependencies}{Which dependencies types should be met for a node to be @@ -27,20 +27,18 @@ with that status.} The name of the next package to prioritize } \description{ -While other packages are in progress, identify tasks with all the -dependencies done and mark them as \code{ready} already has its dependencies -done. +While other packages are in progress, ensure that the next selected package +already has its dependencies done. } \details{ There are helpers defined for particular use cases that strictly rely on the -\code{\link[=task_graph_update_ready]{task_graph_update_ready()}}, they are: +\code{\link[=task_graph_which_ready]{task_graph_which_ready()}}, they are: \itemize{ -\item \code{task_graph_update_ready_strong()} - List vertices whose strong +\item \code{task_graph_update_check_ready()} - Updates check vertices whose all dependencies are satisfied. -\item \code{task_graph_update_check_ready()} - List root vertices whose all +\item \code{task_graph_update_install_ready()} - Update install vertices whose all dependencies are satisfied. -\item \code{task_graph_update_install_ready()} - List install vertices whose -dependencies are all satisfied +\item \code{task_graph_which_ready()} - List vertices whose wit ready status. } } \keyword{internal} diff --git a/man/task_graph_which_ready.Rd b/man/task_graph_which_ready.Rd index 1f7379b..985eec1 100644 --- a/man/task_graph_which_ready.Rd +++ b/man/task_graph_which_ready.Rd @@ -2,18 +2,18 @@ % Please edit documentation in R/task_graph.R \name{task_graph_which_ready} \alias{task_graph_which_ready} -\title{Find task with ready state} +\title{Find nodes with ready state} \usage{ task_graph_which_ready(g) } \arguments{ -\item{g}{A dependency graph, as produced with \code{\link[=task_graph_create]{task_graph_create()}}.} +\item{g}{A dependency graph, as produced with \code{\link[=task_graph]{task_graph()}}.} } \value{ The names of packages with ready state. } \description{ -List tasks which have ready state prioritizing check tasks over -install tasks. +List nodes which have ready state prioritizing check task nodes over +install task nodes. } \keyword{internal} diff --git a/man/task_spec.Rd b/man/task_spec.Rd deleted file mode 100644 index deb6d33..0000000 --- a/man/task_spec.Rd +++ /dev/null @@ -1,35 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/task_spec.R -\name{task_spec} -\alias{task_spec} -\title{Task specification} -\usage{ -task_spec( - alias = NULL, - package_spec = NULL, - env = options::opt("check_envvars") -) -} -\arguments{ -\item{alias}{task alias which also serves as unique identifier of the task.} - -\item{package_spec}{\code{\link[checked]{package_spec}} object} - -\item{env}{environmental variables to be set in separate process running -specific task.} -} -\description{ -Create task specification list which consists of all the details required -to run specific task. -} -\seealso{ -Other tasks: -\code{\link{check_task_spec}()}, -\code{\link{checked-task-df}}, -\code{\link{custom_install_task_spec}()}, -\code{\link{install_task_spec}()}, -\code{\link{rev_dep_check_tasks_df}()}, -\code{\link{revdep_check_task_spec}()}, -\code{\link{source_check_tasks_df}()} -} -\concept{tasks} diff --git a/tests/testthat.R b/tests/testthat.R index 78ec056..2b27e56 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -1,12 +1,5 @@ -# This file is part of the standard setup for testthat. -# It is recommended that you do not modify it. -# -# Where should you do additional test configuration? -# Learn more about the roles of various files in: -# * https://r-pkgs.org/testing-design.html#sec-tests-files-overview -# * https://testthat.r-lib.org/articles/special-files.html - library(testthat) library(checked) +# Meet CRAN multiple core usage requirement test_check("checked") diff --git a/tests/testthat/_snaps/deps.md b/tests/testthat/_snaps/deps.md new file mode 100644 index 0000000..1f4f886 --- /dev/null +++ b/tests/testthat/_snaps/deps.md @@ -0,0 +1,187 @@ +# pkg_dependencies works as expected for local package + + Code + local_deps + Output + package type name op version + 1 checked Depends R >= 3, 6, 2 + 2 checked Imports callr + 3 checked Imports cli + 4 checked Imports glue + 5 checked Imports igraph + 6 checked Imports jsonlite + 7 checked Imports memoise + 8 checked Imports options + 9 checked Imports R6 + 10 checked Imports rcmdcheck + 11 checked Imports rlang + 12 checked Imports utils >= 3, 6, 2 + 13 checked Imports tools + 14 checked Suggests remotes + 15 checked Suggests testthat >= 3, 0, 0 + 16 checked Suggests visNetwork + 17 checked Suggests withr + +# pkg_dependencies works as expected for cran package + + Code + df + Output + package type name op version + 1 checked Imports callr + 2 checked Imports cli + 3 checked Imports igraph + 4 checked Imports jsonlite + 5 checked Imports options + 6 checked Imports R6 + 7 checked Imports rcmdcheck + 8 checked Imports utils >= 3, 6, 2 + 9 checked Imports tools + 10 checked Suggests testthat >= 3, 0, 0 + 11 checked Suggests withr + 12 callr Depends R >= 3, 4 + 13 cli Depends R >= 3, 4 + 14 igraph Depends methods + 15 igraph Depends R >= 3, 5, 0 + 16 jsonlite Depends methods + 17 R6 Depends R >= 3, 6 + 18 testthat Depends R >= 4, 1, 0 + 19 withr Depends R >= 3, 6, 0 + 20 callr Imports processx >= 3, 6, 1 + 21 callr Imports R6 + 22 callr Imports utils + 23 cli Imports utils + 24 igraph Imports cli + 25 igraph Imports graphics + 26 igraph Imports grDevices + 27 igraph Imports lifecycle + 28 igraph Imports magrittr + 29 igraph Imports Matrix + 30 igraph Imports pkgconfig >= 2, 0, 0 + 31 igraph Imports rlang >= 1, 1, 0 + 32 igraph Imports stats + 33 igraph Imports utils + 34 igraph Imports vctrs + 35 options Imports utils + 36 rcmdcheck Imports callr >= 3, 1, 1, 9000 + 37 rcmdcheck Imports cli >= 3, 0, 0 + 38 rcmdcheck Imports curl + 39 rcmdcheck Imports desc >= 1, 2, 0 + 40 rcmdcheck Imports digest + 41 rcmdcheck Imports pkgbuild + 42 rcmdcheck Imports prettyunits + 43 rcmdcheck Imports R6 + 44 rcmdcheck Imports rprojroot + 45 rcmdcheck Imports sessioninfo >= 1, 1, 1 + 46 rcmdcheck Imports utils + 47 rcmdcheck Imports withr + 48 rcmdcheck Imports xopen + 49 testthat Imports brio >= 1, 1, 5 + 50 testthat Imports callr >= 3, 7, 6 + 51 testthat Imports cli >= 3, 6, 5 + 52 testthat Imports desc >= 1, 4, 3 + 53 testthat Imports evaluate >= 1, 0, 4 + 54 testthat Imports jsonlite >= 2, 0, 0 + 55 testthat Imports lifecycle >= 1, 0, 4 + 56 testthat Imports magrittr >= 2, 0, 3 + 57 testthat Imports methods + 58 testthat Imports pkgload >= 1, 4, 0 + 59 testthat Imports praise >= 1, 0, 0 + 60 testthat Imports processx >= 3, 8, 6 + 61 testthat Imports ps >= 1, 9, 1 + 62 testthat Imports R6 >= 2, 6, 1 + 63 testthat Imports rlang >= 1, 1, 6 + 64 testthat Imports utils + 65 testthat Imports waldo >= 0, 6, 2 + 66 testthat Imports withr >= 3, 0, 2 + 67 withr Imports graphics + 68 withr Imports grDevices + 69 igraph LinkingTo cpp11 >= 0, 5, 0 + 70 processx Depends R >= 3, 4, 0 + 71 lifecycle Depends R >= 3, 6 + 72 magrittr Depends R >= 3, 4, 0 + 73 Matrix Depends R >= + 74 Matrix Depends methods + 75 rlang Depends R >= 4, 0, 0 + 76 vctrs Depends R >= 4, 0, 0 + 77 curl Depends R >= 3, 0, 0 + 78 desc Depends R >= 3, 4 + 79 digest Depends R >= 3, 3, 0 + 80 pkgbuild Depends R >= 3, 5 + 81 prettyunits Depends R >= 2, 10 + 82 rprojroot Depends R >= 3, 0, 0 + 83 sessioninfo Depends R >= 3, 4 + 84 xopen Depends R >= 3, 1 + 85 brio Depends R >= 3, 6 + 86 evaluate Depends R >= 3, 6, 0 + 87 pkgload Depends R >= 3, 4, 0 + 88 ps Depends R >= 3, 4 + 89 waldo Depends R >= 4, 0 + 90 cpp11 Depends R >= 4, 0, 0 + 91 processx Imports ps >= 1, 2, 0 + 92 processx Imports R6 + 93 processx Imports utils + 94 lifecycle Imports cli >= 3, 4, 0 + 95 lifecycle Imports rlang >= 1, 1, 0 + 96 Matrix Imports grDevices + 97 Matrix Imports graphics + 98 Matrix Imports grid + 99 Matrix Imports lattice + 100 Matrix Imports stats + 101 Matrix Imports utils + 102 pkgconfig Imports utils + 103 rlang Imports utils + 104 vctrs Imports cli >= 3, 4, 0 + 105 vctrs Imports glue + 106 vctrs Imports lifecycle >= 1, 0, 3 + 107 vctrs Imports rlang >= 1, 1, 7 + 108 desc Imports cli + 109 desc Imports R6 + 110 desc Imports utils + 111 digest Imports utils + 112 pkgbuild Imports callr >= 3, 2, 0 + 113 pkgbuild Imports cli >= 3, 4, 0 + 114 pkgbuild Imports desc + 115 pkgbuild Imports processx + 116 pkgbuild Imports R6 + 117 sessioninfo Imports cli >= 3, 1, 0 + 118 sessioninfo Imports tools + 119 sessioninfo Imports utils + 120 xopen Imports processx + 121 pkgload Imports cli >= 3, 3, 0 + 122 pkgload Imports desc + 123 pkgload Imports fs + 124 pkgload Imports glue + 125 pkgload Imports lifecycle + 126 pkgload Imports methods + 127 pkgload Imports pkgbuild + 128 pkgload Imports processx + 129 pkgload Imports rlang >= 1, 1, 1 + 130 pkgload Imports rprojroot + 131 pkgload Imports utils + 132 ps Imports utils + 133 waldo Imports cli + 134 waldo Imports diffobj >= 0, 3, 4 + 135 waldo Imports glue + 136 waldo Imports methods + 137 waldo Imports rlang >= 1, 1, 0 + 138 lattice Depends R >= + 139 glue Depends R >= 3, 6 + 140 fs Depends R >= 3, 6 + 141 diffobj Depends R >= 3, 1, 0 + 142 lattice Imports grid + 143 lattice Imports grDevices + 144 lattice Imports graphics + 145 lattice Imports stats + 146 lattice Imports utils + 147 glue Imports methods + 148 fs Imports methods + 149 diffobj Imports crayon >= 1, 3, 2 + 150 diffobj Imports tools + 151 diffobj Imports methods + 152 diffobj Imports utils + 153 diffobj Imports stats + 154 crayon Imports grDevices + 155 crayon Imports methods + 156 crayon Imports utils + diff --git a/tests/testthat/_snaps/reporters.md b/tests/testthat/_snaps/reporters.md new file mode 100644 index 0000000..6806e4f --- /dev/null +++ b/tests/testthat/_snaps/reporters.md @@ -0,0 +1,28 @@ +# reporter_basic_tty works as expected for pkg.none + + Code + run(design, reporter = reporter) + Message + +# reporter_basic_tty works as expected for pkg.ok.error + + Code + run(design, reporter = reporter) + Message + Checks + [][install] rev.both.dependency started + [][install] rev.both.dependency finished () + [][install] pkg.ok.error started + [][install] pkg.ok.error finished () + [][install] pkg.ok.error started + [][check] rev.both.ok started + [][install] pkg.ok.error finished () + [][check] rev.both.ok finished with 1 NOTE () + [][check] rev.both.error started + [][check] rev.both.error finished with 1 NOTE () + [][check] rev.both.ok started + [][check] rev.both.ok finished with 1 NOTE () + [][check] rev.both.error started + [][check] rev.both.error finished with 1 ERROR, 1 WARNING () + Finished in + diff --git a/tests/testthat/testing_pkgs/DALEXtra/DESCRIPTION b/tests/testthat/fixtures/DALEXtra/DESCRIPTION similarity index 100% rename from tests/testthat/testing_pkgs/DALEXtra/DESCRIPTION rename to tests/testthat/fixtures/DALEXtra/DESCRIPTION diff --git a/tests/testthat/testing_pkgs/rd2markdown/DESCRIPTION b/tests/testthat/fixtures/rd2markdown/DESCRIPTION similarity index 100% rename from tests/testthat/testing_pkgs/rd2markdown/DESCRIPTION rename to tests/testthat/fixtures/rd2markdown/DESCRIPTION diff --git a/tests/testthat/testing_pkgs/revdeps/v1/pkg.none.broken_1.0.0.tar.gz b/tests/testthat/fixtures/revdeps/v1/pkg.none.broken_1.0.0.tar.gz similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v1/pkg.none.broken_1.0.0.tar.gz rename to tests/testthat/fixtures/revdeps/v1/pkg.none.broken_1.0.0.tar.gz diff --git a/tests/testthat/fixtures/revdeps/v1/pkg.none_1.0.0.tar.gz b/tests/testthat/fixtures/revdeps/v1/pkg.none_1.0.0.tar.gz new file mode 100644 index 0000000..6956555 Binary files /dev/null and b/tests/testthat/fixtures/revdeps/v1/pkg.none_1.0.0.tar.gz differ diff --git a/tests/testthat/testing_pkgs/revdeps/v1/pkg.ok.error_1.0.0.tar.gz b/tests/testthat/fixtures/revdeps/v1/pkg.ok.error_1.0.0.tar.gz similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v1/pkg.ok.error_1.0.0.tar.gz rename to tests/testthat/fixtures/revdeps/v1/pkg.ok.error_1.0.0.tar.gz diff --git a/tests/testthat/testing_pkgs/revdeps/v1/rev.both.dependency_1.0.0.tar.gz b/tests/testthat/fixtures/revdeps/v1/rev.both.dependency_1.0.0.tar.gz similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v1/rev.both.dependency_1.0.0.tar.gz rename to tests/testthat/fixtures/revdeps/v1/rev.both.dependency_1.0.0.tar.gz diff --git a/tests/testthat/testing_pkgs/revdeps/v1/rev.both.error_1.0.0.tar.gz b/tests/testthat/fixtures/revdeps/v1/rev.both.error_1.0.0.tar.gz similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v1/rev.both.error_1.0.0.tar.gz rename to tests/testthat/fixtures/revdeps/v1/rev.both.error_1.0.0.tar.gz diff --git a/tests/testthat/testing_pkgs/revdeps/v1/rev.both.ok_1.0.0.tar.gz b/tests/testthat/fixtures/revdeps/v1/rev.both.ok_1.0.0.tar.gz similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v1/rev.both.ok_1.0.0.tar.gz rename to tests/testthat/fixtures/revdeps/v1/rev.both.ok_1.0.0.tar.gz diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.none/DESCRIPTION b/tests/testthat/fixtures/revdeps/v2/pkg.none/DESCRIPTION similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.none/DESCRIPTION rename to tests/testthat/fixtures/revdeps/v2/pkg.none/DESCRIPTION diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.none/LICENSE b/tests/testthat/fixtures/revdeps/v2/pkg.none/LICENSE similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.none/LICENSE rename to tests/testthat/fixtures/revdeps/v2/pkg.none/LICENSE diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.none/NAMESPACE b/tests/testthat/fixtures/revdeps/v2/pkg.none/NAMESPACE similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.none/NAMESPACE rename to tests/testthat/fixtures/revdeps/v2/pkg.none/NAMESPACE diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.none/R/hw.R b/tests/testthat/fixtures/revdeps/v2/pkg.none/R/hw.R similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.none/R/hw.R rename to tests/testthat/fixtures/revdeps/v2/pkg.none/R/hw.R diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.none/man/hello_world.Rd b/tests/testthat/fixtures/revdeps/v2/pkg.none/man/hello_world.Rd similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.none/man/hello_world.Rd rename to tests/testthat/fixtures/revdeps/v2/pkg.none/man/hello_world.Rd diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.none/tests/test-hw.R b/tests/testthat/fixtures/revdeps/v2/pkg.none/tests/test-hw.R similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.none/tests/test-hw.R rename to tests/testthat/fixtures/revdeps/v2/pkg.none/tests/test-hw.R diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/DESCRIPTION b/tests/testthat/fixtures/revdeps/v2/pkg.ok.error/DESCRIPTION similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/DESCRIPTION rename to tests/testthat/fixtures/revdeps/v2/pkg.ok.error/DESCRIPTION diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/LICENSE b/tests/testthat/fixtures/revdeps/v2/pkg.ok.error/LICENSE similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/LICENSE rename to tests/testthat/fixtures/revdeps/v2/pkg.ok.error/LICENSE diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/NAMESPACE b/tests/testthat/fixtures/revdeps/v2/pkg.ok.error/NAMESPACE similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/NAMESPACE rename to tests/testthat/fixtures/revdeps/v2/pkg.ok.error/NAMESPACE diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/R/hw.R b/tests/testthat/fixtures/revdeps/v2/pkg.ok.error/R/hw.R similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/R/hw.R rename to tests/testthat/fixtures/revdeps/v2/pkg.ok.error/R/hw.R diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/man/hello_world.Rd b/tests/testthat/fixtures/revdeps/v2/pkg.ok.error/man/hello_world.Rd similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/man/hello_world.Rd rename to tests/testthat/fixtures/revdeps/v2/pkg.ok.error/man/hello_world.Rd diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/man/hello_world_loud.Rd b/tests/testthat/fixtures/revdeps/v2/pkg.ok.error/man/hello_world_loud.Rd similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/man/hello_world_loud.Rd rename to tests/testthat/fixtures/revdeps/v2/pkg.ok.error/man/hello_world_loud.Rd diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/tests/test-hw.R b/tests/testthat/fixtures/revdeps/v2/pkg.ok.error/tests/test-hw.R similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.ok.error/tests/test-hw.R rename to tests/testthat/fixtures/revdeps/v2/pkg.ok.error/tests/test-hw.R diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.suggests/DESCRIPTION b/tests/testthat/fixtures/revdeps/v2/pkg.suggests/DESCRIPTION similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.suggests/DESCRIPTION rename to tests/testthat/fixtures/revdeps/v2/pkg.suggests/DESCRIPTION diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.suggests/LICENSE b/tests/testthat/fixtures/revdeps/v2/pkg.suggests/LICENSE similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.suggests/LICENSE rename to tests/testthat/fixtures/revdeps/v2/pkg.suggests/LICENSE diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.suggests/NAMESPACE b/tests/testthat/fixtures/revdeps/v2/pkg.suggests/NAMESPACE similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.suggests/NAMESPACE rename to tests/testthat/fixtures/revdeps/v2/pkg.suggests/NAMESPACE diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.suggests/R/hw.R b/tests/testthat/fixtures/revdeps/v2/pkg.suggests/R/hw.R similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.suggests/R/hw.R rename to tests/testthat/fixtures/revdeps/v2/pkg.suggests/R/hw.R diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.suggests/man/hello_world.Rd b/tests/testthat/fixtures/revdeps/v2/pkg.suggests/man/hello_world.Rd similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.suggests/man/hello_world.Rd rename to tests/testthat/fixtures/revdeps/v2/pkg.suggests/man/hello_world.Rd diff --git a/tests/testthat/testing_pkgs/revdeps/v2/pkg.suggests/tests/test-hw.R b/tests/testthat/fixtures/revdeps/v2/pkg.suggests/tests/test-hw.R similarity index 100% rename from tests/testthat/testing_pkgs/revdeps/v2/pkg.suggests/tests/test-hw.R rename to tests/testthat/fixtures/revdeps/v2/pkg.suggests/tests/test-hw.R diff --git a/tests/testthat/test-check-reverse.R b/tests/testthat/test-check-reverse.R index 1167c0a..b79fdf1 100644 --- a/tests/testthat/test-check-reverse.R +++ b/tests/testthat/test-check-reverse.R @@ -1,16 +1,13 @@ -# CRAN does not like out subprocesses tests resulting in false positive tests -# results -testthat::skip_on_cran() - create_temp_repo <- function(dir, repo_path) { - dir.create(ctburl <- utils::contrib.url(repo_path, type = "source"), recursive = TRUE) + contrib_url <- utils::contrib.url(repo_path, type = "source") + dir.create(contrib_url, recursive = TRUE) sources <- list.files(dir, pattern = "^.*\\.tar\\.gz$", full.names = TRUE) - vapply(sources, file.copy, FUN.VALUE = logical(1), to = ctburl) - tools::write_PACKAGES(ctburl, type = "source") + vapply(sources, file.copy, FUN.VALUE = logical(1), to = contrib_url) + tools::write_PACKAGES(contrib_url, type = "source") } -sources_old <- test_path("testing_pkgs", "revdeps", "v1") -sources_new <- test_path("testing_pkgs", "revdeps", "v2") +sources_old <- test_path("fixtures", "revdeps", "v1") +sources_new <- test_path("fixtures", "revdeps", "v2") dir.create(repo_dir <- tempfile("repo")) repo <- paste0("file:///", repo_dir) @@ -19,12 +16,17 @@ create_temp_repo(sources_old, repo_dir) test_that("check_rev_deps works for package with no revdeps", { # Ensure source installation to make sure test works also on mac and windows withr::with_options(list(pkgType = "source"), { - expect_no_error(design <- check_rev_deps( - file.path(sources_new, "pkg.none"), - n = 2L, repos = repo)) + expect_no_error( + checks <- check_rev_deps( + file.path(sources_new, "pkg.none"), + n = 1L, + repos = repo, + reporter = NULL + ) + ) }) - r <- results(design) + r <- results(checks) expect_s3_class(r, "checked_results") expect_true(is.list(r)) expect_length(r, 0L) @@ -35,7 +37,10 @@ test_that("check_rev_deps works for package with one breaking change", { withr::with_options(list(pkgType = "source"), { design <- check_rev_deps( file.path(sources_new, "pkg.ok.error"), - n = 2L, repos = repo) + n = 1L, + repos = repo, + reporter = NULL + ) }) r <- results(design) @@ -43,162 +48,131 @@ test_that("check_rev_deps works for package with one breaking change", { expect_true(is.list(r)) expect_named(r) expect_length(r, 1L) - expect_length(r$revdep_check_task_spec, 2L) + expect_length(r[[1]], 2L) + expect_s3_class(r[[1]], "rev_dep_dep_results") # rev.both.error - expect_length(r$revdep_check_task_spec$rev.both.error$notes$issues, 0L) - expect_length(r$revdep_check_task_spec$rev.both.error$notes$potential_issues$new, 0L) - expect_length(r$revdep_check_task_spec$rev.both.error$notes$potential_issues$old, 0L) + both_error_i <- which(grepl("rev.both.error", names(r[[1]]), fixed = TRUE)) + expect_true(length(both_error_i) == 1) + r_both_error <- r[[1]][[both_error_i]] + expect_s3_class(r_both_error, "rcmdcheck_rev_dep_results") - expect_length(r$revdep_check_task_spec$rev.both.error$warnings$issues, 1L) + expect_length(r_both_error$notes$issues, 0L) + expect_length(r_both_error$notes$potential_issues$new, 0L) + expect_length(r_both_error$notes$potential_issues$old, 0L) + + expect_length(r_both_error$warnings$issues, 1L) expect_true( - grepl("Namespace in Imports field not imported from", - r$revdep_check_task_spec$rev.both.error$warnings$issues), - grepl("Missing or unexported object", - r$revdep_check_task_spec$rev.both.error$warnings$issues) + grepl("Missing or unexported object", r_both_error$warnings$issues) ) - expect_length(r$revdep_check_task_spec$rev.both.error$warnings$potential_issues$new, 0L) - expect_length(r$revdep_check_task_spec$rev.both.error$warnings$potential_issues$old, 0L) + expect_length(r_both_error$warnings$potential_issues$new, 0L) + expect_length(r_both_error$warnings$potential_issues$old, 0L) - expect_length(r$revdep_check_task_spec$rev.both.error$errors$issues, 1L) + expect_length(r_both_error$errors$issues, 1L) expect_true( - grepl("Running the tests in", - r$revdep_check_task_spec$rev.both.error$errors$issues), - grepl("is not an exported object from", - r$revdep_check_task_spec$rev.both.error$errors$issues) + grepl("Running the tests in", r_both_error$errors$issues) ) - expect_length(r$revdep_check_task_spec$rev.both.error$errors$potential_issues$new, 0L) - expect_length(r$revdep_check_task_spec$rev.both.error$errors$potential_issues$old, 0L) - + expect_true( + grepl("is not an exported object from", r_both_error$errors$issues) + ) + expect_length(r_both_error$errors$potential_issues$new, 0L) + expect_length(r_both_error$errors$potential_issues$old, 0L) # rev.both.ok - - expect_length(r$revdep_check_task_spec$rev.both.ok$notes$issues, 0L) - expect_length(r$revdep_check_task_spec$rev.both.ok$notes$potential_issues$new, 0L) - expect_length(r$revdep_check_task_spec$rev.both.ok$notes$potential_issues$old, 0L) - - expect_length(r$revdep_check_task_spec$rev.both.ok$warnings$issues, 0L) - expect_length(r$revdep_check_task_spec$rev.both.ok$warnings$potential_issues$new, 0L) - expect_length(r$revdep_check_task_spec$rev.both.ok$warnings$potential_issues$old, 0L) - - expect_length(r$revdep_check_task_spec$rev.both.ok$errors$issues, 0L) - expect_length(r$revdep_check_task_spec$rev.both.ok$errors$potential_issues$new, 0L) - expect_length(r$revdep_check_task_spec$rev.both.ok$errors$potential_issues$old, 0L) + both_ok_i <- which(grepl("rev.both.ok", names(r[[1]]), fixed = TRUE)) + expect_true(length(both_ok_i) == 1) + r_both_ok <- r[[1]][[both_ok_i]] + expect_s3_class(r_both_ok, "rcmdcheck_rev_dep_results") + + expect_length(r_both_ok$notes$issues, 0L) + expect_length(r_both_ok$notes$potential_issues$new, 0L) + expect_length(r_both_ok$notes$potential_issues$old, 0L) + + expect_length(r_both_ok$warnings$issues, 0L) + expect_length(r_both_ok$warnings$potential_issues$new, 0L) + expect_length(r_both_ok$warnings$potential_issues$old, 0L) + + expect_length(r_both_ok$errors$issues, 0L) + expect_length(r_both_ok$errors$potential_issues$new, 0L) + expect_length(r_both_ok$errors$potential_issues$old, 0L) }) -test_that("check_rev_deps works for a package without release version", { - +test_that("check_rev_deps works for a package without a version in repos", { # Ensure source installation to make sure test works also on mac and windows withr::with_options(list(pkgType = "source"), { - expect_no_warning(design <- check_rev_deps( + expect_no_error(design <- check_rev_deps( file.path(sources_new, "pkg.suggests"), - n = 2L, repos = repo, env = c("NOT_CRAN" = "false", options::opt("check_envvars")))) + n = 1L, + repos = repo, + reporter = NULL + )) }) - expect_identical( - design$input$package[[1]]$env, - c("NOT_CRAN" = "false", options::opt("check_envvars")) - ) - r <- results(design) expect_s3_class(r, "checked_results") expect_true(is.list(r)) + expect_length( + list.dirs(file.path(design$output, "checks"), recursive = FALSE), + 4 + ) expect_named(r) expect_length(r, 1L) - expect_length(r$revdep_check_task_spec, 2L) + expect_length(r[[1]], 2L) + expect_s3_class(r[[1]], "rev_dep_dep_results") - # pkg.none - expect_length(r$revdep_check_task_spec$pkg.none$notes$issues, 0L) - expect_length(r$revdep_check_task_spec$pkg.none$notes$potential_issues$new, 0L) - expect_length(r$revdep_check_task_spec$pkg.none$notes$potential_issues$old, 0L) - - expect_length(r$revdep_check_task_spec$pkg.none$warnings$issues, 0L) - expect_length(r$revdep_check_task_spec$pkg.none$warnings$potential_issues$new, 0L) - expect_length(r$revdep_check_task_spec$pkg.none$warnings$potential_issues$old, 0L) - - - expect_length(r$revdep_check_task_spec$pkg.none$errors$issues, 0L) - expect_length(r$revdep_check_task_spec$pkg.none$errors$potential_issues$new, 0L) - expect_length(r$revdep_check_task_spec$pkg.none$errors$potential_issues$old, 0L) - - # pkg.none.broken - expect_length(r$revdep_check_task_spec$pkg.none.broken$notes$issues, 0L) - expect_length(r$revdep_check_task_spec$pkg.none.broken$notes$potential_issues$new, 0L) - expect_length(r$revdep_check_task_spec$pkg.none.broken$notes$potential_issues$old, 0L) - - expect_length(r$revdep_check_task_spec$pkg.none.broken$warnings$issues, 0L) - expect_length(r$revdep_check_task_spec$pkg.none.broken$warnings$potential_issues$new, 0L) - expect_length(r$revdep_check_task_spec$pkg.none.broken$warnings$potential_issues$old, 0L) - - - expect_length(r$revdep_check_task_spec$pkg.none.broken$errors$issues, 1L) - expect_true( - grepl("Running the tests in", - r$revdep_check_task_spec$pkg.none.broken$errors$issues), - grepl("\"hello world\" is not TRUE", - r$revdep_check_task_spec$pkg.none.broken$errors$issues) - ) - expect_length(r$revdep_check_task_spec$pkg.none.broken$errors$potential_issues$new, 0L) - expect_length(r$revdep_check_task_spec$pkg.none.broken$errors$potential_issues$old, 0L) - - # Error testing - dir.create(temp_lib <- tempfile("testing_lib")) - install.packages( - file.path(sources_new, "pkg.suggests"), - lib = temp_lib, - type = "source", - repos = NULL - ) - - withr::with_options(list(pkgType = "source"), { - expect_error(design <- check_rev_deps( - file.path(sources_new, "pkg.suggests"), - lib.loc = temp_lib, - n = 2L, repos = repo, env = c("NOT_CRAN" = "false", options::opt("check_envvars"))), - "cannot provide accurate reverse dependency check results") - }) -}) + none_broken_i <- which(grepl("pkg.none.broken-", names(r[[1]]), fixed = TRUE)) + expect_true(length(none_broken_i) == 1) + r_none_broken <- r[[1]][[none_broken_i]] + expect_s3_class(r_none_broken, "rcmdcheck_rev_dep_results") -test_that("check_dev_rev_deps works as expected", { - withr::with_options(list(pkgType = "source"), { - design <- check_dev_rev_deps( - file.path(sources_new, "pkg.ok.error"), - n = 2L, repos = repo) - }) + expect_length(r_none_broken$notes$issues, 0L) + expect_length(r_none_broken$notes$potential_issues$new, 0L) + expect_length(r_none_broken$notes$potential_issues$old, 0L) - r <- results(design) - expect_s3_class(r, "checked_results") - expect_true(is.list(r)) - expect_named(r) - expect_length(r, 1L) - expect_length(r$check_task_spec, 2L) + expect_length(r_none_broken$warnings$issues, 0L) + expect_length(r_none_broken$warnings$potential_issues$new, 0L) + expect_length(r_none_broken$warnings$potential_issues$old, 0L) - # rev.both.error - expect_length(r$check_task_spec$`rev.both.error (dev)`$notes$issues, 0L) - expect_length(r$check_task_spec$`rev.both.error (dev)`$warnings$issues, 1L) + expect_length(r_none_broken$errors$issues, 1L) expect_true( - grepl("Namespace in Imports field not imported from", - r$check_task_spec$`rev.both.error (dev)`$warnings$issues), - grepl("Missing or unexported object", - r$check_task_spec$`rev.both.error (dev)`$warnings$issues) + grepl("Running the tests in", r_none_broken$errors$issues) ) - - expect_length(r$check_task_spec$`rev.both.error (dev)`$errors$issues, 1L) expect_true( - grepl("Running the tests in", - r$check_task_spec$`rev.both.error (dev)`$errors$issues), - grepl("is not an exported object from", - r$check_task_spec$`rev.both.error (dev)`$errors$issues) + grepl("there is no package called", r_none_broken$errors$issues) ) + expect_length(r_none_broken$errors$potential_issues$new, 0L) + expect_length(r_none_broken$errors$potential_issues$old, 0L) - # rev.both.ok - expect_length(r$check_task_spec$`rev.both.ok (dev)`$notes$issues, 1L) + + # pkg.none + none_i <- which(grepl("pkg.none-", names(r[[1]]))) + expect_true(length(none_i) == 1) + r_none <- r[[1]][[none_i]] + expect_s3_class(r_none, "rcmdcheck_rev_dep_results") + + expect_length(r_none$notes$issues, 0L) + expect_length(r_none$notes$potential_issues$new, 0L) + expect_length(r_none$notes$potential_issues$old, 0L) + + expect_length(r_none$warnings$issues, 0L) + expect_length(r_none$warnings$potential_issues$new, 0L) + expect_length(r_none$warnings$potential_issues$old, 0L) + + expect_length(r_none$errors$issues, 1L) + expect_true( + grepl("Running the tests in", r_none$errors$issues) + ) expect_true( - grepl("Namespace in Imports field not imported from", - r$check_task_spec$`rev.both.ok (dev)`$notes$issues) + grepl("Reverse suggested deps detected", r_none$errors$issues) ) + expect_length(r_none$errors$potential_issues$new, 0L) + expect_length(r_none$errors$potential_issues$old, 0L) - expect_length(r$check_task_spec$`rev.both.ok (dev)`$warnings$issues, 0L) - expect_length(r$check_task_spec$`rev.both.ok (dev)`$errors$issues, 0L) + # Plot works + expect_no_error(plot(design$graph)) + expect_no_error( + g_i <- plot(design$graph, interactive = TRUE) + ) + expect_s3_class(g_i, "visNetwork") }) diff --git a/tests/testthat/test-check.R b/tests/testthat/test-check.R index ac5b918..7e3c721 100644 --- a/tests/testthat/test-check.R +++ b/tests/testthat/test-check.R @@ -1,58 +1,63 @@ -# CRAN does not like out subprocesses tests resulting in false positive tests -# results -testthat::skip_on_cran() - test_that("check_pkgs works as expected", { examples_path <- system.file("example_packages", package = "checked") - # WIP - expect_no_error(design <- check_pkgs(c( - file.path(examples_path, "exampleGood"), - file.path(examples_path, "exampleBad") - ), n = 2L, repos = "https://cran.r-project.org/", - env = c(NOT_CRAN = "false", options::opt("check_envvars")))) - - expect_identical( - design$input$package[[1]]$env, - c(NOT_CRAN = "false", options::opt("check_envvars")) + + expect_no_error( + plan <- check_pkgs( + file.path(examples_path, c("exampleGood", "exampleBad")), + n = 1L, + repos = "https://cran.r-project.org/", + reporter = NULL, + lib.loc = .libPaths() + ) ) -}) -test_that("check_pkgs works as expected", { - examples_path <- system.file("example_packages", package = "checked") - # WIP - expect_no_error(check_pkgs( - c( - file.path(examples_path, "exampleGood"), - file.path(examples_path, "exampleBad") - ), - n = 2L, repos = "https://cran.r-project.org/", - reporter = checked:::reporter_ansi_tty() - )) -}) + r <- results(plan) + expect_s3_class(r, "checked_results") + expect_true(is.list(r)) + expect_length( + list.dirs(file.path(plan$output, "checks"), recursive = FALSE), + 2 + ) + expect_named(r) + expect_length(r, 1L) + expect_length(r[[1]], 2L) + + expect_s3_class(r[[1]], "local_check_results") + + # exampleBad + example_bad_i <- which(grepl("check-exampleBad", names(r[[1]]), fixed = TRUE)) + expect_true(length(example_bad_i) == 1) + r_example_bad <- r[[1]][[example_bad_i]] + expect_s3_class(r_example_bad, "rcmdcheck_check_results") + + expect_length(r_example_bad$notes$issues, 2L) + expect_length(r_example_bad$notes$potential_issues$new, 0L) + expect_length(r_example_bad$notes$potential_issues$old, 0L) + + expect_length(r_example_bad$warnings$issues, 3L) + expect_length(r_example_bad$warnings$potential_issues$new, 0L) + expect_length(r_example_bad$warnings$potential_issues$old, 0L) + + expect_length(r_example_bad$errors$issues, 0L) + expect_length(r_example_bad$errors$potential_issues$new, 0L) + expect_length(r_example_bad$errors$potential_issues$old, 0L) + + # exampleGood + example_good_i <- + which(grepl("check-exampleGood", names(r[[1]]), fixed = TRUE)) + expect_true(length(example_good_i) == 1) + r_example_good <- r[[1]][[example_good_i]] + expect_s3_class(r_example_good, "rcmdcheck_check_results") + + expect_length(r_example_good$notes$issues, 0L) + expect_length(r_example_good$notes$potential_issues$new, 0L) + expect_length(r_example_good$notes$potential_issues$old, 0L) + + expect_length(r_example_good$warnings$issues, 0L) + expect_length(r_example_good$warnings$potential_issues$new, 0L) + expect_length(r_example_good$warnings$potential_issues$old, 0L) -test_that("check design restore dialog test", { - - dir_create(output <- tempfile()) - expect_true(dir.exists(output)) - with_mocked_bindings({ - check_past_output(output, options::opt("restore"), ask = TRUE) - }, restore_menu = function(...) "1") # Yes - expect_true(dir.exists(output)) - with_mocked_bindings({ - check_past_output(output, options::opt("restore"), ask = TRUE) - }, restore_menu = function(...) "2") # No - expect_true(!dir.exists(output)) - - dir_create(output <- tempfile()) - expect_true(dir.exists(output)) - check_past_output(output, options::opt("restore"), ask = FALSE) - expect_true(!dir.exists(output)) - - dir_create(output <- tempfile()) - expect_true(dir.exists(output)) - check_past_output(output, TRUE, ask = TRUE) - expect_true(dir.exists(output)) - check_past_output(output, FALSE, ask = TRUE) - expect_true(!dir.exists(output)) - + expect_length(r_example_good$errors$issues, 0L) + expect_length(r_example_good$errors$potential_issues$new, 0L) + expect_length(r_example_good$errors$potential_issues$old, 0L) }) diff --git a/tests/testthat/test-checks_df.R b/tests/testthat/test-checks_df.R deleted file mode 100644 index 3d67f7a..0000000 --- a/tests/testthat/test-checks_df.R +++ /dev/null @@ -1,184 +0,0 @@ -examples_path <- system.file("example_packages", package = "checked") -path <- c( - test_path("testing_pkgs", "DALEXtra"), - test_path("testing_pkgs", "rd2markdown"), - file.path(examples_path, "exampleGood"), - file.path(examples_path, "exampleOkay"), - file.path(examples_path, "exampleBad") -) -expected_revdeps <- tools::package_dependencies( - "DALEXtra", - db = utils::available.packages(repos = "https://cran.r-project.org/"), - reverse = TRUE, - which = "all" -)[[1]] - -test_that("rev_dep_check_tasks_df works with deafult params", { - expect_silent( - df <- rev_dep_check_tasks_df( - test_path("testing_pkgs", "DALEXtra"), - repos = "https://cran.r-project.org/" - ) - ) - expect_s3_class(df, "data.frame") - expect_true(NROW(df) >= 2*length(expected_revdeps)) - expect_named(df, c("alias", "version", "package", "custom")) - - expect_s3_class(df$package, "list_of_task_spec") - expect_equal(unique(vcapply(df$package, function(x) class(x)[[1]])), "revdep_check_task_spec") - expect_equal(unique(vcapply(df$package, function(x) class(x$package_spec)[[1]])), "package_spec") - - expect_s3_class(df$custom, "list_of_task_spec") - expect_equal(unique(vcapply(df$custom, function(x) class(x)[[1]])), "custom_install_task_spec") - expect_equal(unique(vcapply(df$custom, function(x) class(x$package_spec)[[1]])), c("package_spec_source", "package_spec")) - - expect_true(all(endsWith(df$alias[seq(1, NROW(df), by = 2)], "(dev)"))) - expect_true(all(endsWith(df$alias[seq(2, NROW(df), by = 2)], "(v2.3.0)"))) - - # Test displayes - expect_no_error(print(df)) - expect_no_error(print(df$package)) - expect_no_error(print(df$custom)) -}) - -test_that("task_df functions can specify subprocesses configuration", { - expect_silent( - df <- rev_dep_check_tasks_df( - test_path("testing_pkgs", "DALEXtra"), - repos = "https://cran.r-project.org/", - env = c("NOT_CRAN" = "false", options::opt("check_envvars")), - args = c("--some-option", "--other-option", options::opt("check_args")), - build_args = c("--yet-another-option", options::opt("check_build_args")) - ) - ) - - expect_identical( - df$package[[1]]$env, - c("NOT_CRAN" = "false", options::opt("check_envvars")) - ) - expect_identical( - df$package[[1]]$args, - c("--some-option", "--other-option", options::opt("check_args")) - ) - expect_identical( - df$package[[1]]$build_args, - c("--yet-another-option", options::opt("check_build_args")) - ) - - expect_silent( - df <- rev_dep_check_tasks_df( - test_path("testing_pkgs", "DALEXtra"), - repos = "https://cran.r-project.org/", - env = c("NOT_CRAN" = "false"), - args = c("--some-option", "--other-option"), - build_args = c("--yet-another-option") - ) - ) - - expect_identical( - df$package[[1]]$env, - c("NOT_CRAN" = "false") - ) - expect_identical( - df$package[[1]]$args, - c("--some-option", "--other-option") - ) - expect_identical( - df$package[[1]]$build_args, - "--yet-another-option" - ) - - withr::with_envvar( - c(R_CHECKED_CHECK_ARGS = "--some-option --other-option --another-option", - R_CHECKED_CHECK_BUILD_ARGS = "--yet-another-option"), { - expect_silent( - df <- source_check_tasks_df( - path, - build_args = c("--another-option", options::opt("check_build_args")) - ) - ) - } - ) - - expect_identical( - df$package[[1]]$env, - options::opt("check_envvars") - ) - expect_identical( - df$package[[1]]$args, - c("--some-option", "--other-option", "--another-option") - ) - expect_identical( - df$package[[1]]$build_args, - c("--another-option", "--yet-another-option") - ) - -}) - -test_that("rev_dep_check_tasks_df development_only = TRUE", { - expect_silent( - df <- rev_dep_check_tasks_df( - test_path("testing_pkgs", "DALEXtra"), - repos = "https://cran.r-project.org/", - versions = "dev" - ) - ) - expect_s3_class(df, "data.frame") - expect_true(NROW(df) >= length(expected_revdeps)) - expect_named(df, c("alias", "version", "package", "custom")) - - expect_s3_class(df$package, "list_of_task_spec") - expect_equal(unique(vcapply(df$package, function(x) class(x)[[1]])), "check_task_spec") - expect_equal(unique(vcapply(df$package, function(x) class(x$package_spec)[[1]])), "package_spec") - - expect_s3_class(df$custom, "list_of_task_spec") - expect_equal(unique(vcapply(df$custom, function(x) class(x)[[1]])), "custom_install_task_spec") - expect_equal(unique(vcapply(df$custom, function(x) class(x$package_spec)[[1]])), "package_spec_source") - - expect_true(all(endsWith(df$alias, "(dev)"))) - expect_true(all(!endsWith(df$alias, "(v2.3.0)"))) -}) - -test_that("source_check_tasks_df works as expected", { - expect_silent( - df <- source_check_tasks_df(path) - ) - expect_s3_class(df, "data.frame") - expect_equal(NROW(df), 5) - expect_named(df, c("alias", "version", "package", "custom")) - - expect_s3_class(df$package, "list_of_task_spec") - expect_equal(unique(vcapply(df$package, function(x) class(x)[[1]])), "check_task_spec") - expect_equal(unique(vcapply(df$package, function(x) class(x$package_spec)[[1]])), "package_spec_source") - - expect_s3_class(df$custom, "list_of_task_spec") - expect_equal(unique(vcapply(df$custom, function(x) class(x)[[1]])), "custom_install_task_spec") - expect_equal(unique(vcapply(df$custom, function(x) class(x$package_spec)[[1]])), "NULL") - - expect_true(all(endsWith(df$alias, "(source)"))) -}) - -test_that("source_check_tasks_df aliases are properly handled", { - broken_names <- c("DALEXtra_new", "rd2markdown_new", "exampleGood_new", "exampleOkay_new", "exampleBad_new") - path_broken <- path - names(path_broken) <- broken_names - - expect_silent( - df <- source_check_tasks_df(path_broken) - ) - - expect_true(all(endsWith(df$alias, "_new"))) - expect_equal(df$alias, broken_names) - - expect_silent( - df <- source_check_tasks_df(c( - file.path(examples_path, "exampleGood"), - file.path(examples_path, "exampleGood"), - file.path(examples_path, "exampleGood") - )) - ) - - expect_equal( - df$alias, c("exampleGood (source_1)", "exampleGood (source_2)", "exampleGood (source_3)") - ) -}) diff --git a/tests/testthat/test-dep-graph-next-package.R b/tests/testthat/test-dep-graph-next-package.R index 3a95b6a..a1263b1 100644 --- a/tests/testthat/test-dep-graph-next-package.R +++ b/tests/testthat/test-dep-graph-next-package.R @@ -1,10 +1,10 @@ test_that("dep_graph_next_package finds next installable package", { # nolint start, styler: off g <- igraph::make_graph(~ - A +- B +- C, - A +------ D, - A +- E +- D, - A +- F +- D + A -+ B -+ C, + A ------+ D, + A -+ E -+ D, + A -+ F -+ D ) # nolint end, styler: on @@ -21,12 +21,14 @@ test_that("dep_graph_next_package finds next installable package", { V(g)["D"]$status <- STATUS[["in progress"]] V(g)["C"]$status <- STATUS[["done"]] g <- task_graph_update_ready(g) - expect_equal(names(task_graph_which_ready(g)), "B") + ready_nodes <- V(g)[V(g)$status == STATUS$ready] + expect_equal(names(ready_nodes), "B") # if the order is reversed, now "F" and "E" should be next V(g)$status <- STATUS[["pending"]] V(g)["D"]$status <- STATUS[["done"]] V(g)["C"]$status <- STATUS[["in progress"]] g <- task_graph_update_ready(g) - expect_equal(names(task_graph_which_ready(g)), c("F", "E")) + ready_nodes <- V(g)[V(g)$status == STATUS$ready] + expect_equal(names(ready_nodes), c("F", "E")) }) diff --git a/tests/testthat/test-deps.R b/tests/testthat/test-deps.R new file mode 100644 index 0000000..ef46b33 --- /dev/null +++ b/tests/testthat/test-deps.R @@ -0,0 +1,33 @@ +test_that("pkg_dependencies works as expected for local package", { + desc <- read.dcf(system.file("DESCRIPTION", package = "checked")) + rownames(desc) <- "checked" + + local_deps <- pkg_dependencies( + packages = "checked", + dependencies = TRUE, + db = desc + ) + expect_snapshot(local_deps) +}) + + +test_that("pkg_dependencies works as expected for cran package", { + skip_on_cran() + df <- pkg_dependencies( + "checked", + db = available_packages( + repos = "https://packagemanager.posit.co/cran/2026-02-01" + ) + ) + + ap <- available_packages( + repos = "https://packagemanager.posit.co/cran/2026-02-01" + ) + core_pkgs <- ap[!is.na(ap[, "Priority"]), "Package"] + df[df$package %in% core_pkgs & df$name == "R", ]$version <- + list(package_version(NA_character_, strict = FALSE)) + # We need to exclude dependencies lines stating minimal R + # version required for base and recommended packages as these will fail + # for different version of R regardless of used snapshot + expect_snapshot(df) +}) diff --git a/tests/testthat/test-plan.R b/tests/testthat/test-plan.R new file mode 100644 index 0000000..f7ea84f --- /dev/null +++ b/tests/testthat/test-plan.R @@ -0,0 +1,93 @@ +default_env <- options::opt("check_envvars", env = "checked") +default_args <- options::opt("check_args", env = "checked") +default_build_args <- options::opt("check_build_args", env = "checked") + +test_that("rev_dep_check_tasks_df works with deafult params", { + expect_silent( + plan <- plan_rev_dep_checks( + test_path("fixtures", "DALEXtra"), + repos = "https://packagemanager.posit.co/cran/2026-02-01" + ) + ) + + expect_s3_class(plan, "task_graph") + expect_true(length(plan) >= 15) + + expect_all_true( + c( + "rev_dep_dep_meta_task", + "rev_dep_check_meta_task", + "check_task", + "install_task" + ) %in% vcapply(V(plan)$task, function(x) class(x)[[1]]) + ) + expect_s3_class(V(plan)$task[[1]], "rev_dep_dep_meta_task") + expect_s3_class(V(plan)$task[[1]]$origin, "pkg_origin_local") + expect_true(V(plan)$task[[1]]$origin$package == "DALEXtra") + expect_s3_class(V(plan)$task[[1]]$origin$version, "package_version") + expect_true(V(plan)$task[[1]]$origin$version == "2.3.0") + + expect_s3_class(V(plan)$task[[3]], "check_task") + expect_equal( + names(V(plan)$task[[3]]), + c("env", "args", "build_args", "origin", "seed") + ) + expect_s3_class(V(plan)$task[[3]]$origin, "pkg_origin_repo") + expect_equal(V(plan)$task[[3]]$env, default_env) + expect_equal(V(plan)$task[[3]]$args, default_args) + expect_equal(V(plan)$task[[3]]$build_args, default_build_args) + expect_true(V(plan)$task[[3]]$origin$package == "marginaleffects") + expect_s3_class(V(plan)$task[[3]]$origin$version, "package_version") + expect_true(V(plan)$task[[3]]$origin$version == "0.31.0") + + # Test displayes + expect_no_error(expect_output(print(plan))) + expect_no_error(expect_output(print(V(plan)$task[[1]]))) + expect_no_error(expect_output(print(V(plan)$task[[3]]))) + expect_no_error(expect_output(print(V(plan)$task[[3]]$origin))) +}) + +test_that("source_check_tasks_df works as expected", { + examples_path <- system.file("example_packages", package = "checked") + expect_silent( + plan <- plan_local_checks( + c( + test_path("fixtures", "DALEXtra"), + test_path("fixtures", "rd2markdown"), + file.path(examples_path, "exampleGood"), + file.path(examples_path, "exampleOkay"), + file.path(examples_path, "exampleBad") + ) + ) + ) + expect_s3_class(plan, "task_graph") + expect_true(length(plan) == 6) + + expect_all_true( + c( + "local_check_meta_task", + "check_task" + ) %in% vcapply(V(plan)$task, function(x) class(x)[[1]]) + ) + expect_s3_class(V(plan)$task[[1]], "local_check_meta_task") + expect_null(V(plan)$task[[1]]$origin) + + expect_s3_class(V(plan)$task[[2]], "check_task") + expect_equal( + names(V(plan)$task[[3]]), + c("env", "args", "build_args", "origin") + ) + expect_s3_class(V(plan)$task[[3]]$origin, "pkg_origin_local") + expect_equal(V(plan)$task[[3]]$env, default_env) + expect_equal(V(plan)$task[[3]]$args, default_args) + expect_equal(V(plan)$task[[3]]$build_args, default_build_args) + expect_true(V(plan)$task[[3]]$origin$package == "rd2markdown") + expect_s3_class(V(plan)$task[[3]]$origin$version, "package_version") + expect_true(V(plan)$task[[3]]$origin$version == "0.0.8") + + # Test displayes + expect_no_error(expect_output(print(plan))) + expect_no_error(expect_output(print(V(plan)$task[[1]]))) + expect_no_error(expect_output(print(V(plan)$task[[3]]))) + expect_no_error(expect_output(print(V(plan)$task[[3]]$origin))) +}) diff --git a/tests/testthat/test-reporters.R b/tests/testthat/test-reporters.R new file mode 100644 index 0000000..7f46465 --- /dev/null +++ b/tests/testthat/test-reporters.R @@ -0,0 +1,103 @@ +create_temp_repo <- function(dir, repo_path) { + contrib_url <- utils::contrib.url(repo_path, type = "source") + dir.create(contrib_url, recursive = TRUE) + sources <- list.files(dir, pattern = "^.*\\.tar\\.gz$", full.names = TRUE) + vapply(sources, file.copy, FUN.VALUE = logical(1), to = contrib_url) + tools::write_PACKAGES(contrib_url, type = "source") +} + +sources_old <- test_path("fixtures", "revdeps", "v1") +sources_new <- test_path("fixtures", "revdeps", "v2") + +dir.create(repo_dir <- tempfile("repo")) +repo <- paste0("file:///", repo_dir) +create_temp_repo(sources_old, repo_dir) +old <- getOption("pkgType") +options(pkgType = "source") + +test_that("reporter_basic_tty works as expected for pkg.none", { + # Entire testing suite takes too long on CRAN, we need to trim it + skip_on_cran() + + plan <- plan_rev_dep_checks( + file.path(sources_new, "pkg.none"), + repos = repo + ) + + design <- checker$new( + plan, + n = 1L, + repos = repo, + restore = FALSE + ) + + reporter <- reporter_basic_tty() + + expect_snapshot( + run(design, reporter = reporter), + # We remove the last line as it reports the time which can change + transform = function(lines) { + lines[-length(lines)] + } + ) +}) + +test_that("reporter_basic_tty works as expected for pkg.ok.error", { + # Entire testing suite takes too long on CRAN, we need to trim it + skip_on_cran() + + plan <- plan_rev_dep_checks( + file.path(sources_new, "pkg.ok.error"), + repos = repo + ) + + design <- checker$new( + plan, + n = 1L, + repos = repo, + restore = FALSE + ) + + reporter <- reporter_basic_tty() + + expect_snapshot( + run(design, reporter = reporter), + # We remove the last line as it reports the time which can change + transform = function(lines) { + lines <- gsub("\\d+(?:\\.\\d+)?(?:ms|s|m)", "", lines) + lines[!startsWith(lines, "ETA")] + } + ) +}) + +test_that("reporter_ansi_tty works as expected for pkg.ok.error", { + # Entire testing suite takes too long on CRAN, we need to trim it + skip_on_cran() + + plan <- plan_rev_dep_checks( + file.path(sources_new, "pkg.ok.error"), + repos = repo + ) + + design <- checker$new( + plan, + n = 1L, + repos = repo, + restore = FALSE + ) + reporter <- reporter_ansi_tty() + + expect_no_error(suppressMessages( + capture.output( + run(design, reporter = reporter) + ) + )) + + expect_s3_class(reporter$buffer, "data.frame") + expect_equal(NROW(reporter$buffer), 7) + expect_all_true(reporter$buffer$final) + expect_all_false(reporter$buffer$updated) + expect_all_false(reporter$buffer$new) +}) + +options(pkgType = old) diff --git a/tests/testthat/test-results-utils.R b/tests/testthat/test-results-utils.R new file mode 100644 index 0000000..36280b0 --- /dev/null +++ b/tests/testthat/test-results-utils.R @@ -0,0 +1,29 @@ +test_that("results_to_df works as expected", { + # Entire testing suite takes too long on CRAN, we need to trim it + skip_on_cran() + + examples_path <- system.file("example_packages", package = "checked") + + expect_no_error( + plan <- check_pkgs( + file.path(examples_path, c("exampleGood", "exampleBad")), + n = 1L, + repos = "https://cran.r-project.org/", + reporter = NULL + ) + ) + + r <- results(plan) + df <- results_to_df(r[[1]]) + expect_equal(NROW(df), 2) + expect_equal(names(df), c("notes", "warnings", "errors")) + expect_equal(df$notes, c(2, 0)) + expect_equal(df$warnings, c(3, 0)) + expect_equal(df$errors, c(0, 0)) + expect_true( + all( + endsWith(row.names(df)[[1]], "check-exampleBad"), + endsWith(row.names(df)[[2]], "check-exampleGood") + ) + ) +}) diff --git a/tests/testthat/test-results.R b/tests/testthat/test-results.R deleted file mode 100644 index 1d21734..0000000 --- a/tests/testthat/test-results.R +++ /dev/null @@ -1,26 +0,0 @@ -# CRAN does not like out subprocesses tests resulting in false positive tests -# results -testthat::skip_on_cran() - -test_that("results_to_file works as expected", { - examples_path <- system.file("example_packages", package = "checked") - # WIP - expect_no_error(plan <- check_pkgs(c( - file.path(examples_path, "exampleGood"), - file.path(examples_path, "exampleBad") - ), n = 2L, repos = "https://cran.r-project.org/")) - - r <- results(plan) - r_file <- tempfile() - expect_no_error(results_to_file(r, r_file)) - expect_true(!identical(readLines(r_file), "No issues identified.")) - - expect_no_error(plan <- check_rev_deps(c( - file.path(examples_path, "exampleBad") - ), n = 2L, repos = "https://cran.r-project.org/")) - - r <- results(plan) - r_file <- tempfile() - expect_no_error(results_to_file(r, r_file)) - expect_true(identical(readLines(r_file), "No issues identified.")) -}) diff --git a/tests/testthat/testing_pkgs/revdeps/v1/pkg.none_1.0.0.tar.gz b/tests/testthat/testing_pkgs/revdeps/v1/pkg.none_1.0.0.tar.gz deleted file mode 100644 index 1822907..0000000 Binary files a/tests/testthat/testing_pkgs/revdeps/v1/pkg.none_1.0.0.tar.gz and /dev/null differ